1ac455939Sdan /* 2ac455939Sdan ** 2012 November 26 3ac455939Sdan ** 4ac455939Sdan ** The author disclaims copyright to this source code. In place of 5ac455939Sdan ** a legal notice, here is a blessing: 6ac455939Sdan ** 7ac455939Sdan ** May you do good and not evil. 8ac455939Sdan ** May you find forgiveness for yourself and forgive others. 9ac455939Sdan ** May you share freely, never taking more than you give. 10ac455939Sdan ** 11ac455939Sdan ************************************************************************* 12ac455939Sdan ** 13ac455939Sdan ** OVERVIEW 14ac455939Sdan ** 15ac455939Sdan ** This file contains experimental code used to record data from live 16ac455939Sdan ** SQLite applications that may be useful for offline analysis. Specifically: 17ac455939Sdan ** 18ac455939Sdan ** 1) The initial contents of all database files opened by the 19ac455939Sdan ** application, and 20ac455939Sdan ** 21ac455939Sdan ** 2) All SQL statements executed by the application. 22ac455939Sdan ** 23ac455939Sdan ** USAGE 24ac455939Sdan ** 25ac455939Sdan ** To use this module, SQLite must be compiled with the SQLITE_ENABLE_SQLLOG 26ac455939Sdan ** pre-processor symbol defined and this file linked into the application 27ac455939Sdan ** somehow. 28ac455939Sdan ** 29ac455939Sdan ** At runtime, logging is enabled by setting environment variable 30ac455939Sdan ** SQLITE_SQLLOG_DIR to the name of a directory in which to store logged 31ac455939Sdan ** data. The directory must already exist. 32ac455939Sdan ** 33ac455939Sdan ** Usually, if the application opens the same database file more than once 34ac455939Sdan ** (either by attaching it or by using more than one database handle), only 35*48864df9Smistachkin ** a single copy is made. This behavior may be overridden (so that a 36ac455939Sdan ** separate copy is taken each time the database file is opened or attached) 37ac455939Sdan ** by setting the environment variable SQLITE_SQLLOG_REUSE_FILES to 0. 38ac455939Sdan ** 39ac455939Sdan ** OUTPUT: 40ac455939Sdan ** 41ac455939Sdan ** The SQLITE_SQLLOG_DIR is populated with three types of files: 42ac455939Sdan ** 43ac455939Sdan ** sqllog_N.db - Copies of database files. N may be any integer. 44ac455939Sdan ** 45ac455939Sdan ** sqllog_N.sql - A list of SQL statements executed by a single 46ac455939Sdan ** connection. N may be any integer. 47ac455939Sdan ** 48ac455939Sdan ** sqllog.idx - An index mapping from integer N to a database 49ac455939Sdan ** file name - indicating the full path of the 50ac455939Sdan ** database from which sqllog_N.db was copied. 5171ba10d3Sdan ** 5271ba10d3Sdan ** ERROR HANDLING: 5371ba10d3Sdan ** 5471ba10d3Sdan ** This module attempts to make a best effort to continue logging if an 5571ba10d3Sdan ** IO or other error is encountered. For example, if a log file cannot 5671ba10d3Sdan ** be opened logs are not collected for that connection, but other 5771ba10d3Sdan ** logging proceeds as expected. Errors are logged by calling sqlite3_log(). 58ac455939Sdan */ 59ac455939Sdan 60ac455939Sdan #include "sqlite3.h" 61ac455939Sdan #include "stdio.h" 62ac455939Sdan #include "stdlib.h" 63ac455939Sdan #include "string.h" 64ac455939Sdan #include "assert.h" 65ac455939Sdan 6671ba10d3Sdan #include "sys/types.h" 6771ba10d3Sdan #include "unistd.h" 6871ba10d3Sdan static int getProcessId(void){ 697d1f0c79Sdrh #if SQLITE_OS_WIN 707d1f0c79Sdrh return (int)_getpid(); 717d1f0c79Sdrh #else 7271ba10d3Sdan return (int)getpid(); 737d1f0c79Sdrh #endif 7471ba10d3Sdan } 7571ba10d3Sdan 7671ba10d3Sdan 77ac455939Sdan #define ENVIRONMENT_VARIABLE1_NAME "SQLITE_SQLLOG_DIR" 78ac455939Sdan #define ENVIRONMENT_VARIABLE2_NAME "SQLITE_SQLLOG_REUSE_FILES" 79ac455939Sdan 80ac455939Sdan /* Assume that all database and database file names are shorted than this. */ 81ac455939Sdan #define SQLLOG_NAMESZ 512 82ac455939Sdan 83ac455939Sdan /* Maximum number of simultaneous database connections the process may 84ac455939Sdan ** open (if any more are opened an error is logged using sqlite3_log() 85ac455939Sdan ** and processing is halted). 86ac455939Sdan */ 87ac455939Sdan #define MAX_CONNECTIONS 256 88ac455939Sdan 89ac455939Sdan struct SLConn { 90ac455939Sdan int isErr; /* True if an error has occurred */ 91ac455939Sdan sqlite3 *db; /* Connection handle */ 92ac455939Sdan int iLog; /* First integer value used in file names */ 93ac455939Sdan FILE *fd; /* File descriptor for log file */ 94ac455939Sdan }; 95ac455939Sdan 96ac455939Sdan struct SLGlobal { 97ac455939Sdan /* Protected by MUTEX_STATIC_MASTER */ 98ac455939Sdan sqlite3_mutex *mutex; /* Recursive mutex */ 99ac455939Sdan int nConn; /* Size of aConn[] array */ 100ac455939Sdan 101ac455939Sdan /* Protected by SLGlobal.mutex */ 102ac455939Sdan int bReuse; /* True to avoid extra copies of db files */ 10371ba10d3Sdan char zPrefix[SQLLOG_NAMESZ]; /* Prefix for all created files */ 104ac455939Sdan char zIdx[SQLLOG_NAMESZ]; /* Full path to *.idx file */ 105ac455939Sdan int iNextLog; /* Used to allocate file names */ 106ac455939Sdan int iNextDb; /* Used to allocate database file names */ 107ac455939Sdan int bRec; /* True if testSqllog() is called rec. */ 108ac455939Sdan int iClock; /* Clock value */ 109ac455939Sdan struct SLConn aConn[MAX_CONNECTIONS]; 110ac455939Sdan } sqllogglobal; 111ac455939Sdan 112ac455939Sdan /* 113ac455939Sdan ** Return true if c is an ASCII whitespace character. 114ac455939Sdan */ 115ac455939Sdan static int sqllog_isspace(char c){ 116ac455939Sdan return (c==' ' || c=='\t' || c=='\n' || c=='\v' || c=='\f' || c=='\r'); 117ac455939Sdan } 118ac455939Sdan 119ac455939Sdan /* 120ac455939Sdan ** The first argument points to a nul-terminated string containing an SQL 121ac455939Sdan ** command. Before returning, this function sets *pz to point to the start 122ac455939Sdan ** of the first token in this command, and *pn to the number of bytes in 123ac455939Sdan ** the token. This is used to check if the SQL command is an "ATTACH" or 124ac455939Sdan ** not. 125ac455939Sdan */ 126ac455939Sdan static void sqllogTokenize(const char *z, const char **pz, int *pn){ 127ac455939Sdan const char *p = z; 128ac455939Sdan int n; 129ac455939Sdan 130ac455939Sdan /* Skip past any whitespace */ 131ac455939Sdan while( sqllog_isspace(*p) ){ 132ac455939Sdan p++; 133ac455939Sdan } 134ac455939Sdan 135ac455939Sdan /* Figure out how long the first token is */ 136ac455939Sdan *pz = p; 137ac455939Sdan n = 0; 138ac455939Sdan while( (p[n]>='a' && p[n]<='z') || (p[n]>='A' && p[n]<='Z') ) n++; 139ac455939Sdan *pn = n; 140ac455939Sdan } 141ac455939Sdan 142ac455939Sdan /* 143ac455939Sdan ** Check if the logs directory already contains a copy of database file 144ac455939Sdan ** zFile. If so, return a pointer to the full path of the copy. Otherwise, 145ac455939Sdan ** return NULL. 146ac455939Sdan ** 147ac455939Sdan ** If a non-NULL value is returned, then the caller must arrange to 148ac455939Sdan ** eventually free it using sqlite3_free(). 149ac455939Sdan */ 150ac455939Sdan static char *sqllogFindFile(const char *zFile){ 151ac455939Sdan char *zRet = 0; 152ac455939Sdan FILE *fd = 0; 153ac455939Sdan 154ac455939Sdan /* Open the index file for reading */ 155ac455939Sdan fd = fopen(sqllogglobal.zIdx, "r"); 15671ba10d3Sdan if( fd==0 ){ 15771ba10d3Sdan sqlite3_log(SQLITE_IOERR, "sqllogFindFile(): error in fopen()"); 15871ba10d3Sdan return 0; 15971ba10d3Sdan } 160ac455939Sdan 16171ba10d3Sdan /* Loop through each entry in the index file. If zFile is not NULL and the 16271ba10d3Sdan ** entry is a match, then set zRet to point to the filename of the existing 16371ba10d3Sdan ** copy and break out of the loop. */ 164ac455939Sdan while( feof(fd)==0 ){ 165ac455939Sdan char zLine[SQLLOG_NAMESZ*2+5]; 16671ba10d3Sdan if( fgets(zLine, sizeof(zLine), fd) ){ 16771ba10d3Sdan int n; 16871ba10d3Sdan char *z; 169ac455939Sdan 17071ba10d3Sdan zLine[sizeof(zLine)-1] = '\0'; 171ac455939Sdan z = zLine; 172ac455939Sdan while( *z>='0' && *z<='9' ) z++; 173ac455939Sdan while( *z==' ' ) z++; 174ac455939Sdan 175ac455939Sdan n = strlen(z); 176ac455939Sdan while( n>0 && sqllog_isspace(z[n-1]) ) n--; 17771ba10d3Sdan 178ac455939Sdan if( n==strlen(zFile) && 0==memcmp(zFile, z, n) ){ 179ac455939Sdan char zBuf[16]; 18071ba10d3Sdan memset(zBuf, 0, sizeof(zBuf)); 181ac455939Sdan z = zLine; 182ac455939Sdan while( *z>='0' && *z<='9' ){ 183ac455939Sdan zBuf[z-zLine] = *z; 184ac455939Sdan z++; 185ac455939Sdan } 18671ba10d3Sdan zRet = sqlite3_mprintf("%s_%s.db", sqllogglobal.zPrefix, zBuf); 187ac455939Sdan break; 188ac455939Sdan } 189ac455939Sdan } 19071ba10d3Sdan } 19171ba10d3Sdan 19271ba10d3Sdan if( ferror(fd) ){ 19371ba10d3Sdan sqlite3_log(SQLITE_IOERR, "sqllogFindFile(): error reading index file"); 19471ba10d3Sdan } 195ac455939Sdan 196ac455939Sdan fclose(fd); 197ac455939Sdan return zRet; 198ac455939Sdan } 199ac455939Sdan 20071ba10d3Sdan static int sqllogFindAttached( 201ac455939Sdan struct SLConn *p, /* Database connection */ 202ac455939Sdan const char *zSearch, /* Name to search for (or NULL) */ 203ac455939Sdan char *zName, /* OUT: Name of attached database */ 204ac455939Sdan char *zFile /* OUT: Name of attached file */ 205ac455939Sdan ){ 206ac455939Sdan sqlite3_stmt *pStmt; 207ac455939Sdan int rc; 208ac455939Sdan 209ac455939Sdan /* The "PRAGMA database_list" command returns a list of databases in the 210ac455939Sdan ** order that they were attached. So a newly attached database is 211ac455939Sdan ** described by the last row returned. */ 212ac455939Sdan assert( sqllogglobal.bRec==0 ); 213ac455939Sdan sqllogglobal.bRec = 1; 214ac455939Sdan rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0); 215ac455939Sdan if( rc==SQLITE_OK ){ 216ac455939Sdan while( SQLITE_ROW==sqlite3_step(pStmt) ){ 2177d1f0c79Sdrh const char *zVal1; int nVal1; 2187d1f0c79Sdrh const char *zVal2; int nVal2; 219ac455939Sdan 2207d1f0c79Sdrh zVal1 = (const char*)sqlite3_column_text(pStmt, 1); 221ac455939Sdan nVal1 = sqlite3_column_bytes(pStmt, 1); 222ac455939Sdan memcpy(zName, zVal1, nVal1+1); 223ac455939Sdan 2247d1f0c79Sdrh zVal2 = (const char*)sqlite3_column_text(pStmt, 2); 225ac455939Sdan nVal2 = sqlite3_column_bytes(pStmt, 2); 226ac455939Sdan memcpy(zFile, zVal2, nVal2+1); 227ac455939Sdan 228ac455939Sdan if( zSearch && strlen(zSearch)==nVal1 229ac455939Sdan && 0==sqlite3_strnicmp(zSearch, zVal1, nVal1) 230ac455939Sdan ){ 231ac455939Sdan break; 232ac455939Sdan } 233ac455939Sdan } 234ac455939Sdan rc = sqlite3_finalize(pStmt); 235ac455939Sdan } 236ac455939Sdan sqllogglobal.bRec = 0; 23771ba10d3Sdan 23871ba10d3Sdan if( rc!=SQLITE_OK ){ 23971ba10d3Sdan sqlite3_log(rc, "sqllogFindAttached(): error in \"PRAGMA database_list\""); 24071ba10d3Sdan } 24171ba10d3Sdan return rc; 242ac455939Sdan } 243ac455939Sdan 244ac455939Sdan 245ac455939Sdan /* 246ac455939Sdan ** Parameter zSearch is the name of a database attached to the database 247ac455939Sdan ** connection associated with the first argument. This function creates 248ac455939Sdan ** a backup of this database in the logs directory. 249ac455939Sdan ** 250ac455939Sdan ** The name used for the backup file is automatically generated. Call 251ac455939Sdan ** it zFile. 252ac455939Sdan ** 253ac455939Sdan ** If the bLog parameter is true, then a statement of the following form 254ac455939Sdan ** is written to the log file associated with *p: 255ac455939Sdan ** 256ac455939Sdan ** ATTACH 'zFile' AS 'zName'; 257ac455939Sdan ** 258ac455939Sdan ** Otherwise, if bLog is false, a comment is added to the log file: 259ac455939Sdan ** 260ac455939Sdan ** -- Main database file is 'zFile' 261ac455939Sdan ** 262ac455939Sdan ** The SLGlobal.mutex mutex is always held when this function is called. 263ac455939Sdan */ 264ac455939Sdan static void sqllogCopydb(struct SLConn *p, const char *zSearch, int bLog){ 265ac455939Sdan char zName[SQLLOG_NAMESZ]; /* Attached database name */ 266ac455939Sdan char zFile[SQLLOG_NAMESZ]; /* Database file name */ 267ac455939Sdan char *zFree; 268ac455939Sdan char *zInit = 0; 26971ba10d3Sdan int rc; 270ac455939Sdan 27171ba10d3Sdan rc = sqllogFindAttached(p, zSearch, zName, zFile); 27271ba10d3Sdan if( rc!=SQLITE_OK ) return; 27371ba10d3Sdan 274ac455939Sdan if( zFile[0]=='\0' ){ 275ac455939Sdan zInit = sqlite3_mprintf(""); 276ac455939Sdan }else{ 277ac455939Sdan if( sqllogglobal.bReuse ){ 278ac455939Sdan zInit = sqllogFindFile(zFile); 279ac455939Sdan }else{ 280ac455939Sdan zInit = 0; 281ac455939Sdan } 282ac455939Sdan if( zInit==0 ){ 283ac455939Sdan int rc; 284ac455939Sdan sqlite3 *copy = 0; 285ac455939Sdan int iDb; 286ac455939Sdan 287ac455939Sdan /* Generate a file-name to use for the copy of this database */ 288ac455939Sdan iDb = sqllogglobal.iNextDb++; 28971ba10d3Sdan zInit = sqlite3_mprintf("%s_%d.db", sqllogglobal.zPrefix, iDb); 290ac455939Sdan 291ac455939Sdan /* Create the backup */ 292ac455939Sdan assert( sqllogglobal.bRec==0 ); 293ac455939Sdan sqllogglobal.bRec = 1; 294ac455939Sdan rc = sqlite3_open(zInit, ©); 295ac455939Sdan if( rc==SQLITE_OK ){ 296ac455939Sdan sqlite3_backup *pBak; 297ac455939Sdan sqlite3_exec(copy, "PRAGMA synchronous = 0", 0, 0, 0); 298ac455939Sdan pBak = sqlite3_backup_init(copy, "main", p->db, zName); 299ac455939Sdan if( pBak ){ 300ac455939Sdan sqlite3_backup_step(pBak, -1); 30171ba10d3Sdan rc = sqlite3_backup_finish(pBak); 30271ba10d3Sdan }else{ 30371ba10d3Sdan rc = sqlite3_errcode(copy); 304ac455939Sdan } 305ac455939Sdan sqlite3_close(copy); 306ac455939Sdan } 307ac455939Sdan sqllogglobal.bRec = 0; 308ac455939Sdan 30971ba10d3Sdan if( rc==SQLITE_OK ){ 310ac455939Sdan /* Write an entry into the database index file */ 31171ba10d3Sdan FILE *fd = fopen(sqllogglobal.zIdx, "a"); 31271ba10d3Sdan if( fd ){ 313ac455939Sdan fprintf(fd, "%d %s\n", iDb, zFile); 314ac455939Sdan fclose(fd); 315ac455939Sdan } 31671ba10d3Sdan }else{ 31771ba10d3Sdan sqlite3_log(rc, "sqllogCopydb(): error backing up database"); 31871ba10d3Sdan } 31971ba10d3Sdan } 320ac455939Sdan } 321ac455939Sdan 322ac455939Sdan if( bLog ){ 32371ba10d3Sdan zFree = sqlite3_mprintf("ATTACH '%q' AS '%q'; -- clock=%d\n", 324ac455939Sdan zInit, zName, sqllogglobal.iClock++ 325ac455939Sdan ); 326ac455939Sdan }else{ 327ac455939Sdan zFree = sqlite3_mprintf("-- Main database is '%q'\n", zInit); 328ac455939Sdan } 329ac455939Sdan fprintf(p->fd, "%s", zFree); 330ac455939Sdan sqlite3_free(zFree); 331ac455939Sdan 332ac455939Sdan sqlite3_free(zInit); 333ac455939Sdan } 334ac455939Sdan 335ac455939Sdan /* 336ac455939Sdan ** If it is not already open, open the log file for connection *p. 337ac455939Sdan ** 338ac455939Sdan ** The SLGlobal.mutex mutex is always held when this function is called. 339ac455939Sdan */ 340ac455939Sdan static void sqllogOpenlog(struct SLConn *p){ 341ac455939Sdan /* If the log file has not yet been opened, open it now. */ 342ac455939Sdan if( p->fd==0 ){ 343ac455939Sdan char *zLog; 344ac455939Sdan 34571ba10d3Sdan /* If it is still NULL, have global.zPrefix point to a copy of 34671ba10d3Sdan ** environment variable $ENVIRONMENT_VARIABLE1_NAME. */ 34771ba10d3Sdan if( sqllogglobal.zPrefix[0]==0 ){ 348ac455939Sdan FILE *fd; 349ac455939Sdan char *zVar = getenv(ENVIRONMENT_VARIABLE1_NAME); 35071ba10d3Sdan if( zVar==0 || strlen(zVar)+10>=(sizeof(sqllogglobal.zPrefix)) ) return; 35171ba10d3Sdan sprintf(sqllogglobal.zPrefix, "%s/sqllog_%d", zVar, getProcessId()); 35271ba10d3Sdan sprintf(sqllogglobal.zIdx, "%s.idx", sqllogglobal.zPrefix); 353ac455939Sdan if( getenv(ENVIRONMENT_VARIABLE2_NAME) ){ 354ac455939Sdan sqllogglobal.bReuse = atoi(getenv(ENVIRONMENT_VARIABLE2_NAME)); 355ac455939Sdan } 356ac455939Sdan fd = fopen(sqllogglobal.zIdx, "w"); 35771ba10d3Sdan if( fd ) fclose(fd); 358ac455939Sdan } 359ac455939Sdan 360ac455939Sdan /* Open the log file */ 36171ba10d3Sdan zLog = sqlite3_mprintf("%s_%d.sql", sqllogglobal.zPrefix, p->iLog); 362ac455939Sdan p->fd = fopen(zLog, "w"); 363ac455939Sdan sqlite3_free(zLog); 36471ba10d3Sdan if( p->fd==0 ){ 36571ba10d3Sdan sqlite3_log(SQLITE_IOERR, "sqllogOpenlog(): Failed to open log file"); 36671ba10d3Sdan } 367ac455939Sdan } 368ac455939Sdan } 369ac455939Sdan 370ac455939Sdan /* 371ac455939Sdan ** This function is called if the SQLLOG callback is invoked to report 372ac455939Sdan ** execution of an SQL statement. Parameter p is the connection the statement 37371ba10d3Sdan ** was executed by and parameter zSql is the text of the statement itself. 374ac455939Sdan */ 37571ba10d3Sdan static void testSqllogStmt(struct SLConn *p, const char *zSql){ 376ac455939Sdan const char *zFirst; /* Pointer to first token in zSql */ 377ac455939Sdan int nFirst; /* Size of token zFirst in bytes */ 378ac455939Sdan 379ac455939Sdan sqllogTokenize(zSql, &zFirst, &nFirst); 380ac455939Sdan if( nFirst!=6 || 0!=sqlite3_strnicmp("ATTACH", zFirst, 6) ){ 381ac455939Sdan /* Not an ATTACH statement. Write this directly to the log. */ 38271ba10d3Sdan fprintf(p->fd, "%s; -- clock=%d\n", zSql, sqllogglobal.iClock++); 383ac455939Sdan }else{ 384ac455939Sdan /* This is an ATTACH statement. Copy the database. */ 385ac455939Sdan sqllogCopydb(p, 0, 1); 386ac455939Sdan } 387ac455939Sdan } 388ac455939Sdan 389ac455939Sdan /* 390ac455939Sdan ** The SQLITE_CONFIG_SQLLOG callback registered by sqlite3_init_sqllog(). 391ac455939Sdan */ 39271ba10d3Sdan static void testSqllog(void *pCtx, sqlite3 *db, const char *zSql, int eType){ 393696b33e6Sdrh struct SLConn *p = 0; 394ac455939Sdan sqlite3_mutex *master = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER); 395ac455939Sdan 39671ba10d3Sdan assert( eType==0 || eType==1 || eType==2 ); 39771ba10d3Sdan assert( (eType==2)==(zSql==0) ); 398ac455939Sdan 399ac455939Sdan /* This is a database open command. */ 40071ba10d3Sdan if( eType==0 ){ 401ac455939Sdan sqlite3_mutex_enter(master); 402ac455939Sdan if( sqllogglobal.mutex==0 ){ 403ac455939Sdan sqllogglobal.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_RECURSIVE); 404ac455939Sdan } 405ac455939Sdan p = &sqllogglobal.aConn[sqllogglobal.nConn++]; 406ac455939Sdan p->fd = 0; 407ac455939Sdan p->db = db; 408ac455939Sdan p->iLog = sqllogglobal.iNextLog++; 409ac455939Sdan sqlite3_mutex_leave(master); 410ac455939Sdan 411ac455939Sdan /* Open the log and take a copy of the main database file */ 412ac455939Sdan sqlite3_mutex_enter(sqllogglobal.mutex); 413ac455939Sdan if( sqllogglobal.bRec==0 ){ 414ac455939Sdan sqllogOpenlog(p); 41571ba10d3Sdan if( p->fd ) sqllogCopydb(p, "main", 0); 416ac455939Sdan } 417ac455939Sdan sqlite3_mutex_leave(sqllogglobal.mutex); 418ac455939Sdan } 419ac455939Sdan 420ac455939Sdan else{ 421ac455939Sdan 422ac455939Sdan int i; 423ac455939Sdan for(i=0; i<sqllogglobal.nConn; i++){ 424ac455939Sdan p = &sqllogglobal.aConn[i]; 425ac455939Sdan if( p->db==db ) break; 426ac455939Sdan } 427ac455939Sdan if( i==sqllogglobal.nConn ) return; 428ac455939Sdan 429ac455939Sdan /* A database handle close command */ 43071ba10d3Sdan if( eType==2 ){ 431ac455939Sdan sqlite3_mutex_enter(master); 432ac455939Sdan if( p->fd ) fclose(p->fd); 433ac455939Sdan p->db = 0; 434ac455939Sdan p->fd = 0; 435ac455939Sdan 436ac455939Sdan sqllogglobal.nConn--; 437ac455939Sdan if( sqllogglobal.nConn==0 ){ 438ac455939Sdan sqlite3_mutex_free(sqllogglobal.mutex); 439ac455939Sdan sqllogglobal.mutex = 0; 440ac455939Sdan }else{ 441ac455939Sdan int nShift = &sqllogglobal.aConn[sqllogglobal.nConn] - p; 442ac455939Sdan if( nShift>0 ){ 443ac455939Sdan memmove(p, &p[1], nShift*sizeof(struct SLConn)); 444ac455939Sdan } 445ac455939Sdan } 446ac455939Sdan sqlite3_mutex_leave(master); 447ac455939Sdan 448ac455939Sdan /* An ordinary SQL command. */ 44971ba10d3Sdan }else if( p->fd ){ 450ac455939Sdan sqlite3_mutex_enter(sqllogglobal.mutex); 451ac455939Sdan if( sqllogglobal.bRec==0 ){ 45271ba10d3Sdan testSqllogStmt(p, zSql); 453ac455939Sdan } 454ac455939Sdan sqlite3_mutex_leave(sqllogglobal.mutex); 455ac455939Sdan } 456ac455939Sdan } 457ac455939Sdan } 458ac455939Sdan 459ac455939Sdan /* 460ac455939Sdan ** This function is called either before sqlite3_initialized() or by it. 461ac455939Sdan ** It checks if the SQLITE_SQLLOG_DIR variable is defined, and if so 462ac455939Sdan ** registers an SQLITE_CONFIG_SQLLOG callback to record the applications 463ac455939Sdan ** database activity. 464ac455939Sdan */ 465ac455939Sdan void sqlite3_init_sqllog(void){ 466ac455939Sdan if( getenv(ENVIRONMENT_VARIABLE1_NAME) ){ 467ac455939Sdan if( SQLITE_OK==sqlite3_config(SQLITE_CONFIG_SQLLOG, testSqllog, 0) ){ 468ac455939Sdan memset(&sqllogglobal, 0, sizeof(sqllogglobal)); 469ac455939Sdan sqllogglobal.bReuse = 1; 470ac455939Sdan } 471ac455939Sdan } 472ac455939Sdan } 473