4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
7 ** May you do good and not evil.
8 ** May you find forgiveness for yourself and forgive others.
9 ** May you share freely, never taking more than you give.
11 *************************************************************************
15 ** This file contains experimental code used to record data from live
16 ** SQLite applications that may be useful for offline analysis.
17 ** Specifically, this module can be used to capture the following
20 ** 1) The initial contents of all database files opened by the
23 ** 2) All SQL statements executed by the application.
25 ** The captured information can then be used to run (for example)
26 ** performance analysis looking for slow queries or to look for
27 ** optimization opportunities in either the application or in SQLite
32 ** To use this module, SQLite must be compiled with the SQLITE_ENABLE_SQLLOG
33 ** pre-processor symbol defined and this file linked into the application.
34 ** One way to link this file into the application is to append the content
35 ** of this file onto the end of the "sqlite3.c" amalgamation and then
36 ** recompile the application as normal except with the addition of the
37 ** -DSQLITE_ENABLE_SQLLOG option.
39 ** At runtime, logging is enabled by setting environment variable
40 ** SQLITE_SQLLOG_DIR to the name of a directory in which to store logged
41 ** data. The logging directory must already exist.
43 ** Usually, if the application opens the same database file more than once
44 ** (either by attaching it or by using more than one database handle), only
45 ** a single copy is made. This behavior may be overridden (so that a
46 ** separate copy is taken each time the database file is opened or attached)
47 ** by setting the environment variable SQLITE_SQLLOG_REUSE_FILES to 0.
51 ** The SQLITE_SQLLOG_DIR is populated with three types of files:
53 ** sqllog_N.db - Copies of database files. N may be any integer.
55 ** sqllog_N.sql - A list of SQL statements executed by a single
56 ** connection. N may be any integer.
58 ** sqllog.idx - An index mapping from integer N to a database
59 ** file name - indicating the full path of the
60 ** database from which sqllog_N.db was copied.
64 ** This module attempts to make a best effort to continue logging if an
65 ** IO or other error is encountered. For example, if a log file cannot
66 ** be opened logs are not collected for that connection, but other
67 ** logging proceeds as expected. Errors are logged by calling sqlite3_log().
78 #include <sys/types.h>
80 static int getProcessId(void){
82 return (int)_getpid();
88 /* Names of environment variables to be used */
89 #define ENVIRONMENT_VARIABLE1_NAME "SQLITE_SQLLOG_DIR"
90 #define ENVIRONMENT_VARIABLE2_NAME "SQLITE_SQLLOG_REUSE_FILES"
92 /* Assume that all database and database file names are shorted than this. */
93 #define SQLLOG_NAMESZ 512
95 /* Maximum number of simultaneous database connections the process may
96 ** open (if any more are opened an error is logged using sqlite3_log()
97 ** and processing is halted).
99 #define MAX_CONNECTIONS 256
101 /* There is one instance of this object for each SQLite database connection
102 ** that is being logged.
105 int isErr
; /* True if an error has occurred */
106 sqlite3
*db
; /* Connection handle */
107 int iLog
; /* First integer value used in file names */
108 FILE *fd
; /* File descriptor for log file */
111 /* This object is a singleton that keeps track of all data loggers.
113 static struct SLGlobal
{
114 /* Protected by MUTEX_STATIC_MASTER */
115 sqlite3_mutex
*mutex
; /* Recursive mutex */
116 int nConn
; /* Size of aConn[] array */
118 /* Protected by SLGlobal.mutex */
119 int bReuse
; /* True to avoid extra copies of db files */
120 char zPrefix
[SQLLOG_NAMESZ
]; /* Prefix for all created files */
121 char zIdx
[SQLLOG_NAMESZ
]; /* Full path to *.idx file */
122 int iNextLog
; /* Used to allocate file names */
123 int iNextDb
; /* Used to allocate database file names */
124 int bRec
; /* True if testSqllog() is called rec. */
125 int iClock
; /* Clock value */
126 struct SLConn aConn
[MAX_CONNECTIONS
];
130 ** Return true if c is an ASCII whitespace character.
132 static int sqllog_isspace(char c
){
133 return (c
==' ' || c
=='\t' || c
=='\n' || c
=='\v' || c
=='\f' || c
=='\r');
137 ** The first argument points to a nul-terminated string containing an SQL
138 ** command. Before returning, this function sets *pz to point to the start
139 ** of the first token in this command, and *pn to the number of bytes in
140 ** the token. This is used to check if the SQL command is an "ATTACH" or
143 static void sqllogTokenize(const char *z
, const char **pz
, int *pn
){
147 /* Skip past any whitespace */
148 while( sqllog_isspace(*p
) ){
152 /* Figure out how long the first token is */
155 while( (p
[n
]>='a' && p
[n
]<='z') || (p
[n
]>='A' && p
[n
]<='Z') ) n
++;
160 ** Check if the logs directory already contains a copy of database file
161 ** zFile. If so, return a pointer to the full path of the copy. Otherwise,
164 ** If a non-NULL value is returned, then the caller must arrange to
165 ** eventually free it using sqlite3_free().
167 static char *sqllogFindFile(const char *zFile
){
171 /* Open the index file for reading */
172 fd
= fopen(sqllogglobal
.zIdx
, "r");
174 sqlite3_log(SQLITE_IOERR
, "sqllogFindFile(): error in fopen()");
178 /* Loop through each entry in the index file. If zFile is not NULL and the
179 ** entry is a match, then set zRet to point to the filename of the existing
180 ** copy and break out of the loop. */
181 while( feof(fd
)==0 ){
182 char zLine
[SQLLOG_NAMESZ
*2+5];
183 if( fgets(zLine
, sizeof(zLine
), fd
) ){
187 zLine
[sizeof(zLine
)-1] = '\0';
189 while( *z
>='0' && *z
<='9' ) z
++;
190 while( *z
==' ' ) z
++;
193 while( n
>0 && sqllog_isspace(z
[n
-1]) ) n
--;
195 if( n
==strlen(zFile
) && 0==memcmp(zFile
, z
, n
) ){
197 memset(zBuf
, 0, sizeof(zBuf
));
199 while( *z
>='0' && *z
<='9' ){
203 zRet
= sqlite3_mprintf("%s_%s.db", sqllogglobal
.zPrefix
, zBuf
);
210 sqlite3_log(SQLITE_IOERR
, "sqllogFindFile(): error reading index file");
217 static int sqllogFindAttached(
218 struct SLConn
*p
, /* Database connection */
219 const char *zSearch
, /* Name to search for (or NULL) */
220 char *zName
, /* OUT: Name of attached database */
221 char *zFile
/* OUT: Name of attached file */
226 /* The "PRAGMA database_list" command returns a list of databases in the
227 ** order that they were attached. So a newly attached database is
228 ** described by the last row returned. */
229 assert( sqllogglobal
.bRec
==0 );
230 sqllogglobal
.bRec
= 1;
231 rc
= sqlite3_prepare_v2(p
->db
, "PRAGMA database_list", -1, &pStmt
, 0);
233 while( SQLITE_ROW
==sqlite3_step(pStmt
) ){
234 const char *zVal1
; int nVal1
;
235 const char *zVal2
; int nVal2
;
237 zVal1
= (const char*)sqlite3_column_text(pStmt
, 1);
238 nVal1
= sqlite3_column_bytes(pStmt
, 1);
239 memcpy(zName
, zVal1
, nVal1
+1);
241 zVal2
= (const char*)sqlite3_column_text(pStmt
, 2);
242 nVal2
= sqlite3_column_bytes(pStmt
, 2);
243 memcpy(zFile
, zVal2
, nVal2
+1);
245 if( zSearch
&& strlen(zSearch
)==nVal1
246 && 0==sqlite3_strnicmp(zSearch
, zVal1
, nVal1
)
251 rc
= sqlite3_finalize(pStmt
);
253 sqllogglobal
.bRec
= 0;
256 sqlite3_log(rc
, "sqllogFindAttached(): error in \"PRAGMA database_list\"");
263 ** Parameter zSearch is the name of a database attached to the database
264 ** connection associated with the first argument. This function creates
265 ** a backup of this database in the logs directory.
267 ** The name used for the backup file is automatically generated. Call
270 ** If the bLog parameter is true, then a statement of the following form
271 ** is written to the log file associated with *p:
273 ** ATTACH 'zFile' AS 'zName';
275 ** Otherwise, if bLog is false, a comment is added to the log file:
277 ** -- Main database file is 'zFile'
279 ** The SLGlobal.mutex mutex is always held when this function is called.
281 static void sqllogCopydb(struct SLConn
*p
, const char *zSearch
, int bLog
){
282 char zName
[SQLLOG_NAMESZ
]; /* Attached database name */
283 char zFile
[SQLLOG_NAMESZ
]; /* Database file name */
288 rc
= sqllogFindAttached(p
, zSearch
, zName
, zFile
);
289 if( rc
!=SQLITE_OK
) return;
291 if( zFile
[0]=='\0' ){
292 zInit
= sqlite3_mprintf("");
294 if( sqllogglobal
.bReuse
){
295 zInit
= sqllogFindFile(zFile
);
304 /* Generate a file-name to use for the copy of this database */
305 iDb
= sqllogglobal
.iNextDb
++;
306 zInit
= sqlite3_mprintf("%s_%d.db", sqllogglobal
.zPrefix
, iDb
);
308 /* Create the backup */
309 assert( sqllogglobal
.bRec
==0 );
310 sqllogglobal
.bRec
= 1;
311 rc
= sqlite3_open(zInit
, ©
);
313 sqlite3_backup
*pBak
;
314 sqlite3_exec(copy
, "PRAGMA synchronous = 0", 0, 0, 0);
315 pBak
= sqlite3_backup_init(copy
, "main", p
->db
, zName
);
317 sqlite3_backup_step(pBak
, -1);
318 rc
= sqlite3_backup_finish(pBak
);
320 rc
= sqlite3_errcode(copy
);
324 sqllogglobal
.bRec
= 0;
327 /* Write an entry into the database index file */
328 FILE *fd
= fopen(sqllogglobal
.zIdx
, "a");
330 fprintf(fd
, "%d %s\n", iDb
, zFile
);
334 sqlite3_log(rc
, "sqllogCopydb(): error backing up database");
340 zFree
= sqlite3_mprintf("ATTACH '%q' AS '%q'; -- clock=%d\n",
341 zInit
, zName
, sqllogglobal
.iClock
++
344 zFree
= sqlite3_mprintf("-- Main database is '%q'\n", zInit
);
346 fprintf(p
->fd
, "%s", zFree
);
353 ** If it is not already open, open the log file for connection *p.
355 ** The SLGlobal.mutex mutex is always held when this function is called.
357 static void sqllogOpenlog(struct SLConn
*p
){
358 /* If the log file has not yet been opened, open it now. */
362 /* If it is still NULL, have global.zPrefix point to a copy of
363 ** environment variable $ENVIRONMENT_VARIABLE1_NAME. */
364 if( sqllogglobal
.zPrefix
[0]==0 ){
366 char *zVar
= getenv(ENVIRONMENT_VARIABLE1_NAME
);
367 if( zVar
==0 || strlen(zVar
)+10>=(sizeof(sqllogglobal
.zPrefix
)) ) return;
368 sprintf(sqllogglobal
.zPrefix
, "%s/sqllog_%d", zVar
, getProcessId());
369 sprintf(sqllogglobal
.zIdx
, "%s.idx", sqllogglobal
.zPrefix
);
370 if( getenv(ENVIRONMENT_VARIABLE2_NAME
) ){
371 sqllogglobal
.bReuse
= atoi(getenv(ENVIRONMENT_VARIABLE2_NAME
));
373 fd
= fopen(sqllogglobal
.zIdx
, "w");
377 /* Open the log file */
378 zLog
= sqlite3_mprintf("%s_%d.sql", sqllogglobal
.zPrefix
, p
->iLog
);
379 p
->fd
= fopen(zLog
, "w");
382 sqlite3_log(SQLITE_IOERR
, "sqllogOpenlog(): Failed to open log file");
388 ** This function is called if the SQLLOG callback is invoked to report
389 ** execution of an SQL statement. Parameter p is the connection the statement
390 ** was executed by and parameter zSql is the text of the statement itself.
392 static void testSqllogStmt(struct SLConn
*p
, const char *zSql
){
393 const char *zFirst
; /* Pointer to first token in zSql */
394 int nFirst
; /* Size of token zFirst in bytes */
396 sqllogTokenize(zSql
, &zFirst
, &nFirst
);
397 if( nFirst
!=6 || 0!=sqlite3_strnicmp("ATTACH", zFirst
, 6) ){
398 /* Not an ATTACH statement. Write this directly to the log. */
399 fprintf(p
->fd
, "%s; -- clock=%d\n", zSql
, sqllogglobal
.iClock
++);
401 /* This is an ATTACH statement. Copy the database. */
402 sqllogCopydb(p
, 0, 1);
407 ** The SQLITE_CONFIG_SQLLOG callback registered by sqlite3_init_sqllog().
409 ** The eType parameter has the following values:
411 ** 0: Opening a new database connection. zSql is the name of the
412 ** file being opened. db is a pointer to the newly created database
415 ** 1: An SQL statement has run to completion. zSql is the text of the
416 ** SQL statement with all parameters expanded to their actual values.
418 ** 2: Closing a database connection. zSql is NULL. The db pointer to
419 ** the database connection being closed has already been shut down
420 ** and cannot be used for any further SQL.
422 ** The pCtx parameter is a copy of the pointer that was originally passed
423 ** into the sqlite3_config(SQLITE_CONFIG_SQLLOG) statement. In this
424 ** particular implementation, pCtx is always a pointer to the
425 ** sqllogglobal global variable define above.
427 static void testSqllog(void *pCtx
, sqlite3
*db
, const char *zSql
, int eType
){
428 struct SLConn
*p
= 0;
429 sqlite3_mutex
*master
= sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER
);
431 assert( eType
==0 || eType
==1 || eType
==2 );
432 assert( (eType
==2)==(zSql
==0) );
434 /* This is a database open command. */
436 sqlite3_mutex_enter(master
);
437 if( sqllogglobal
.mutex
==0 ){
438 sqllogglobal
.mutex
= sqlite3_mutex_alloc(SQLITE_MUTEX_RECURSIVE
);
440 p
= &sqllogglobal
.aConn
[sqllogglobal
.nConn
++];
443 p
->iLog
= sqllogglobal
.iNextLog
++;
444 sqlite3_mutex_leave(master
);
446 /* Open the log and take a copy of the main database file */
447 sqlite3_mutex_enter(sqllogglobal
.mutex
);
448 if( sqllogglobal
.bRec
==0 ){
450 if( p
->fd
) sqllogCopydb(p
, "main", 0);
452 sqlite3_mutex_leave(sqllogglobal
.mutex
);
458 for(i
=0; i
<sqllogglobal
.nConn
; i
++){
459 p
= &sqllogglobal
.aConn
[i
];
460 if( p
->db
==db
) break;
462 if( i
==sqllogglobal
.nConn
) return;
464 /* A database handle close command */
466 sqlite3_mutex_enter(master
);
467 if( p
->fd
) fclose(p
->fd
);
471 sqllogglobal
.nConn
--;
472 if( sqllogglobal
.nConn
==0 ){
473 sqlite3_mutex_free(sqllogglobal
.mutex
);
474 sqllogglobal
.mutex
= 0;
476 int nShift
= &sqllogglobal
.aConn
[sqllogglobal
.nConn
] - p
;
478 memmove(p
, &p
[1], nShift
*sizeof(struct SLConn
));
481 sqlite3_mutex_leave(master
);
483 /* An ordinary SQL command. */
485 sqlite3_mutex_enter(sqllogglobal
.mutex
);
486 if( sqllogglobal
.bRec
==0 ){
487 testSqllogStmt(p
, zSql
);
489 sqlite3_mutex_leave(sqllogglobal
.mutex
);
495 ** This function is called either before sqlite3_initialized() or by it.
496 ** It checks if the SQLITE_SQLLOG_DIR variable is defined, and if so
497 ** registers an SQLITE_CONFIG_SQLLOG callback to record the applications
498 ** database activity.
500 void sqlite3_init_sqllog(void){
501 if( getenv(ENVIRONMENT_VARIABLE1_NAME
) ){
502 if( SQLITE_OK
==sqlite3_config(SQLITE_CONFIG_SQLLOG
, testSqllog
, 0) ){
503 memset(&sqllogglobal
, 0, sizeof(sqllogglobal
));
504 sqllogglobal
.bReuse
= 1;