diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/build.c | 4 | ||||
-rw-r--r-- | src/main.c | 24 | ||||
-rw-r--r-- | src/os_unix.c | 8 | ||||
-rw-r--r-- | src/sqlite.h.in | 15 | ||||
-rw-r--r-- | src/sqliteInt.h | 8 | ||||
-rw-r--r-- | src/tclsqlite.c | 589 | ||||
-rw-r--r-- | src/test1.c | 8 | ||||
-rw-r--r-- | src/vdbeInt.h | 1 | ||||
-rw-r--r-- | src/vdbeapi.c | 27 |
9 files changed, 410 insertions, 274 deletions
diff --git a/src/build.c b/src/build.c index 50ad5262d..d79db7e8c 100644 --- a/src/build.c +++ b/src/build.c @@ -22,7 +22,7 @@ ** COMMIT ** ROLLBACK ** -** $Id: build.c,v 1.342 2005/08/20 03:03:04 drh Exp $ +** $Id: build.c,v 1.343 2005/08/29 23:00:04 drh Exp $ */ #include "sqliteInt.h" #include <ctype.h> @@ -85,6 +85,7 @@ void sqlite3FinishCoding(Parse *pParse){ sqlite3VdbeAddOp(v, OP_Goto, 0, pParse->cookieGoto); } +#ifndef SQLITE_OMIT_TRACE /* Add a No-op that contains the complete text of the compiled SQL ** statement as its P3 argument. This does not change the functionality ** of the program. @@ -92,6 +93,7 @@ void sqlite3FinishCoding(Parse *pParse){ ** This is used to implement sqlite3_trace(). */ sqlite3VdbeOp3(v, OP_Noop, 0, 0, pParse->zSql, pParse->zTail-pParse->zSql); +#endif /* SQLITE_OMIT_TRACE */ } diff --git a/src/main.c b/src/main.c index 41d1045fc..bfc2bd597 100644 --- a/src/main.c +++ b/src/main.c @@ -14,7 +14,7 @@ ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** -** $Id: main.c,v 1.299 2005/08/28 17:00:23 drh Exp $ +** $Id: main.c,v 1.300 2005/08/29 23:00:04 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -509,13 +509,14 @@ int sqlite3_create_function16( } #endif +#ifndef SQLITE_OMIT_TRACE /* ** Register a trace function. The pArg from the previously registered trace ** is returned. ** ** A NULL trace function means that no tracing is executes. A non-NULL ** trace is a pointer to a function that is invoked at the start of each -** sqlite3_exec(). +** SQL statement. */ void *sqlite3_trace(sqlite3 *db, void (*xTrace)(void*,const char*), void *pArg){ void *pOld = db->pTraceArg; @@ -523,6 +524,25 @@ void *sqlite3_trace(sqlite3 *db, void (*xTrace)(void*,const char*), void *pArg){ db->pTraceArg = pArg; return pOld; } +/* +** Register a profile function. The pArg from the previously registered +** profile function is returned. +** +** A NULL profile function means that no profiling is executes. A non-NULL +** profile is a pointer to a function that is invoked at the conclusion of +** each SQL statement that is run. +*/ +void *sqlite3_profile( + sqlite3 *db, + void (*xProfile)(void*,const char*,sqlite_uint64), + void *pArg +){ + void *pOld = db->pProfileArg; + db->xProfile = xProfile; + db->pProfileArg = pArg; + return pOld; +} +#endif /* SQLITE_OMIT_TRACE */ /*** EXPERIMENTAL *** ** diff --git a/src/os_unix.c b/src/os_unix.c index 2ea9aa8e3..836d01fab 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -18,6 +18,7 @@ #include <time.h> +#include <sys/time.h> #include <errno.h> #include <unistd.h> @@ -1420,9 +1421,16 @@ int sqlite3_current_time = 0; ** return 0. Return 1 if the time and date cannot be found. */ int sqlite3OsCurrentTime(double *prNow){ +#ifdef NO_GETTOD time_t t; time(&t); *prNow = t/86400.0 + 2440587.5; +#else + struct timeval sNow; + struct timezone sTz; /* Not used */ + gettimeofday(&sNow, &sTz); + *prNow = 2440587.5 + sNow.tv_sec/86400.0 + sNow.tv_usec/86400000000.0; +#endif #ifdef SQLITE_TEST if( sqlite3_current_time ){ *prNow = sqlite3_current_time/86400.0 + 2440587.5; diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 3f18f88e9..30533e4ac 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -12,7 +12,7 @@ ** This header file defines the interface that the SQLite library ** presents to client programs. ** -** @(#) $Id: sqlite.h.in,v 1.139 2005/08/11 02:10:19 drh Exp $ +** @(#) $Id: sqlite.h.in,v 1.140 2005/08/29 23:00:04 drh Exp $ */ #ifndef _SQLITE3_H_ #define _SQLITE3_H_ @@ -464,11 +464,18 @@ int sqlite3_set_authorizer( #define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */ /* -** Register a function that is called at every invocation of sqlite3_exec() -** or sqlite3_prepare(). This function can be used (for example) to generate -** a log file of all SQL executed against a database. +** Register a function for tracing SQL command evaluation. The function +** registered by sqlite3_trace() is invoked at the first sqlite3_step() +** for the evaluation of an SQL statement. The function registered by +** sqlite3_profile() runs at the end of each SQL statement and includes +** information on how long that statement ran. +** +** The sqlite3_profile() API is currently considered experimental and +** is subject to change. */ void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); +void *sqlite3_profile(sqlite3*, + void(*xProfile)(void*,const char*,sqlite_uint64), void*); /* ** This routine configures a callback function - the progress callback - that diff --git a/src/sqliteInt.h b/src/sqliteInt.h index dba03e1f1..8042ae047 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.404 2005/08/28 17:00:23 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.405 2005/08/29 23:00:04 drh Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -420,8 +420,10 @@ struct sqlite3 { } init; struct Vdbe *pVdbe; /* List of active virtual machines */ int activeVdbeCnt; /* Number of vdbes currently executing */ - void (*xTrace)(void*,const char*); /* Trace function */ - void *pTraceArg; /* Argument to the trace function */ + void (*xTrace)(void*,const char*); /* Trace function */ + void *pTraceArg; /* Argument to the trace function */ + void (*xProfile)(void*,const char*,u64); /* Profiling function */ + void *pProfileArg; /* Argument to profile function */ void *pCommitArg; /* Argument to xCommitCallback() */ int (*xCommitCallback)(void*);/* Invoked at every commit. */ void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*); diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 6a36437d6..5e5e6268f 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -11,7 +11,7 @@ ************************************************************************* ** A TCL Interface to SQLite ** -** $Id: tclsqlite.c,v 1.131 2005/08/16 11:11:35 drh Exp $ +** $Id: tclsqlite.c,v 1.132 2005/08/29 23:00:04 drh Exp $ */ #ifndef NO_TCL /* Omit this whole file if TCL is unavailable */ @@ -84,6 +84,7 @@ struct SqliteDb { char *zBusy; /* The busy callback routine */ char *zCommit; /* The commit hook callback routine */ char *zTrace; /* The trace callback routine */ + char *zProfile; /* The profile callback routine */ char *zProgress; /* The progress callback routine */ char *zAuth; /* The authorization callback routine */ char *zNull; /* Text to substitute for an SQL NULL value */ @@ -190,6 +191,9 @@ static void DbDeleteCmd(void *db){ if( pDb->zTrace ){ Tcl_Free(pDb->zTrace); } + if( pDb->zProfile ){ + Tcl_Free(pDb->zProfile); + } if( pDb->zAuth ){ Tcl_Free(pDb->zAuth); } @@ -248,6 +252,25 @@ static void DbTraceHandler(void *cd, const char *zSql){ } /* +** This routine is called by the SQLite profile handler after a statement +** SQL has executed. The TCL script in pDb->zProfile is evaluated. +*/ +static void DbProfileHandler(void *cd, const char *zSql, sqlite_uint64 tm){ + SqliteDb *pDb = (SqliteDb*)cd; + Tcl_DString str; + char zTm[100]; + + sqlite3_snprintf(sizeof(zTm)-1, zTm, "%lld", tm); + Tcl_DStringInit(&str); + Tcl_DStringAppend(&str, pDb->zProfile, -1); + Tcl_DStringAppendElement(&str, zSql); + Tcl_DStringAppendElement(&str, zTm); + Tcl_Eval(pDb->interp, Tcl_DStringValue(&str)); + Tcl_DStringFree(&str); + Tcl_ResetResult(pDb->interp); +} + +/* ** This routine is called when a transaction is committed. The ** TCL script in pDb->zCommit is executed. If it returns non-zero or ** if it throws an exception, the transaction is rolled back instead @@ -589,9 +612,10 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ "collation_needed", "commit_hook", "complete", "copy", "errorcode", "eval", "function", "last_insert_rowid", "nullvalue", - "onecolumn", "progress", "rekey", - "timeout", "total_changes", "trace", - "transaction", "version", 0 + "onecolumn", "profile", "progress", + "rekey", "timeout", "total_changes", + "trace", "transaction", "version", + 0 }; enum DB_enum { DB_AUTHORIZER, DB_BUSY, DB_CACHE, @@ -599,9 +623,9 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ DB_COLLATION_NEEDED, DB_COMMIT_HOOK, DB_COMPLETE, DB_COPY, DB_ERRORCODE, DB_EVAL, DB_FUNCTION, DB_LAST_INSERT_ROWID,DB_NULLVALUE, - DB_ONECOLUMN, DB_PROGRESS, DB_REKEY, - DB_TIMEOUT, DB_TOTAL_CHANGES, DB_TRACE, - DB_TRANSACTION, DB_VERSION, + DB_ONECOLUMN, DB_PROFILE, DB_PROGRESS, + DB_REKEY, DB_TIMEOUT, DB_TOTAL_CHANGES, + DB_TRACE, DB_TRANSACTION, DB_VERSION }; /* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */ @@ -780,44 +804,6 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ break; } - /* $db commit_hook ?CALLBACK? - ** - ** Invoke the given callback just before committing every SQL transaction. - ** If the callback throws an exception or returns non-zero, then the - ** transaction is aborted. If CALLBACK is an empty string, the callback - ** is disabled. - */ - case DB_COMMIT_HOOK: { - if( objc>3 ){ - Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?"); - return TCL_ERROR; - }else if( objc==2 ){ - if( pDb->zCommit ){ - Tcl_AppendResult(interp, pDb->zCommit, 0); - } - }else{ - char *zCommit; - int len; - if( pDb->zCommit ){ - Tcl_Free(pDb->zCommit); - } - zCommit = Tcl_GetStringFromObj(objv[2], &len); - if( zCommit && len>0 ){ - pDb->zCommit = Tcl_Alloc( len + 1 ); - strcpy(pDb->zCommit, zCommit); - }else{ - pDb->zCommit = 0; - } - if( pDb->zCommit ){ - pDb->interp = interp; - sqlite3_commit_hook(pDb->db, DbCommitHandler, pDb); - }else{ - sqlite3_commit_hook(pDb->db, 0, 0); - } - } - break; - } - /* ** $db collate NAME SCRIPT ** @@ -870,6 +856,44 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ break; } + /* $db commit_hook ?CALLBACK? + ** + ** Invoke the given callback just before committing every SQL transaction. + ** If the callback throws an exception or returns non-zero, then the + ** transaction is aborted. If CALLBACK is an empty string, the callback + ** is disabled. + */ + case DB_COMMIT_HOOK: { + if( objc>3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?"); + return TCL_ERROR; + }else if( objc==2 ){ + if( pDb->zCommit ){ + Tcl_AppendResult(interp, pDb->zCommit, 0); + } + }else{ + char *zCommit; + int len; + if( pDb->zCommit ){ + Tcl_Free(pDb->zCommit); + } + zCommit = Tcl_GetStringFromObj(objv[2], &len); + if( zCommit && len>0 ){ + pDb->zCommit = Tcl_Alloc( len + 1 ); + strcpy(pDb->zCommit, zCommit); + }else{ + pDb->zCommit = 0; + } + if( pDb->zCommit ){ + pDb->interp = interp; + sqlite3_commit_hook(pDb->db, DbCommitHandler, pDb); + }else{ + sqlite3_commit_hook(pDb->db, 0, 0); + } + } + break; + } + /* $db complete SQL ** ** Return TRUE if SQL is a complete SQL statement. Return FALSE if @@ -891,6 +915,192 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ break; } + /* $db copy conflict-algorithm table filename ?SEPARATOR? ?NULLINDICATOR? + ** + ** Copy data into table from filename, optionally using SEPARATOR + ** as column separators. If a column contains a null string, or the + ** value of NULLINDICATOR, a NULL is inserted for the column. + ** conflict-algorithm is one of the sqlite conflict algorithms: + ** rollback, abort, fail, ignore, replace + ** On success, return the number of lines processed, not necessarily same + ** as 'db changes' due to conflict-algorithm selected. + ** + ** This code is basically an implementation/enhancement of + ** the sqlite3 shell.c ".import" command. + ** + ** This command usage is equivalent to the sqlite2.x COPY statement, + ** which imports file data into a table using the PostgreSQL COPY file format: + ** $db copy $conflit_algo $table_name $filename \t \\N + */ + case DB_COPY: { + char *zTable; /* Insert data into this table */ + char *zFile; /* The file from which to extract data */ + char *zConflict; /* The conflict algorithm to use */ + sqlite3_stmt *pStmt; /* A statement */ + int rc; /* Result code */ + int nCol; /* Number of columns in the table */ + int nByte; /* Number of bytes in an SQL string */ + int i, j; /* Loop counters */ + int nSep; /* Number of bytes in zSep[] */ + int nNull; /* Number of bytes in zNull[] */ + char *zSql; /* An SQL statement */ + char *zLine; /* A single line of input from the file */ + char **azCol; /* zLine[] broken up into columns */ + char *zCommit; /* How to commit changes */ + FILE *in; /* The input file */ + int lineno = 0; /* Line number of input file */ + char zLineNum[80]; /* Line number print buffer */ + Tcl_Obj *pResult; /* interp result */ + + char *zSep; + char *zNull; + if( objc<5 || objc>7 ){ + Tcl_WrongNumArgs(interp, 2, objv, + "CONFLICT-ALGORITHM TABLE FILENAME ?SEPARATOR? ?NULLINDICATOR?"); + return TCL_ERROR; + } + if( objc>=6 ){ + zSep = Tcl_GetStringFromObj(objv[5], 0); + }else{ + zSep = "\t"; + } + if( objc>=7 ){ + zNull = Tcl_GetStringFromObj(objv[6], 0); + }else{ + zNull = ""; + } + zConflict = Tcl_GetStringFromObj(objv[2], 0); + zTable = Tcl_GetStringFromObj(objv[3], 0); + zFile = Tcl_GetStringFromObj(objv[4], 0); + nSep = strlen(zSep); + nNull = strlen(zNull); + if( nSep==0 ){ + Tcl_AppendResult(interp, "Error: non-null separator required for copy", 0); + return TCL_ERROR; + } + if(sqlite3StrICmp(zConflict, "rollback") != 0 && + sqlite3StrICmp(zConflict, "abort" ) != 0 && + sqlite3StrICmp(zConflict, "fail" ) != 0 && + sqlite3StrICmp(zConflict, "ignore" ) != 0 && + sqlite3StrICmp(zConflict, "replace" ) != 0 ) { + Tcl_AppendResult(interp, "Error: \"", zConflict, + "\", conflict-algorithm must be one of: rollback, " + "abort, fail, ignore, or replace", 0); + return TCL_ERROR; + } + zSql = sqlite3_mprintf("SELECT * FROM '%q'", zTable); + if( zSql==0 ){ + Tcl_AppendResult(interp, "Error: no such table: ", zTable, 0); + return TCL_ERROR; + } + nByte = strlen(zSql); + rc = sqlite3_prepare(pDb->db, zSql, 0, &pStmt, 0); + sqlite3_free(zSql); + if( rc ){ + Tcl_AppendResult(interp, "Error: ", sqlite3_errmsg(pDb->db), 0); + nCol = 0; + }else{ + nCol = sqlite3_column_count(pStmt); + } + sqlite3_finalize(pStmt); + if( nCol==0 ) { + return TCL_ERROR; + } + zSql = malloc( nByte + 50 + nCol*2 ); + if( zSql==0 ) { + Tcl_AppendResult(interp, "Error: can't malloc()", 0); + return TCL_ERROR; + } + sqlite3_snprintf(nByte+50, zSql, "INSERT OR %q INTO '%q' VALUES(?", + zConflict, zTable); + j = strlen(zSql); + for(i=1; i<nCol; i++){ + zSql[j++] = ','; + zSql[j++] = '?'; + } + zSql[j++] = ')'; + zSql[j] = 0; + rc = sqlite3_prepare(pDb->db, zSql, 0, &pStmt, 0); + free(zSql); + if( rc ){ + Tcl_AppendResult(interp, "Error: ", sqlite3_errmsg(pDb->db), 0); + sqlite3_finalize(pStmt); + return TCL_ERROR; + } + in = fopen(zFile, "rb"); + if( in==0 ){ + Tcl_AppendResult(interp, "Error: cannot open file: ", zFile, NULL); + sqlite3_finalize(pStmt); + return TCL_ERROR; + } + azCol = malloc( sizeof(azCol[0])*(nCol+1) ); + if( azCol==0 ) { + Tcl_AppendResult(interp, "Error: can't malloc()", 0); + return TCL_ERROR; + } + sqlite3_exec(pDb->db, "BEGIN", 0, 0, 0); + zCommit = "COMMIT"; + while( (zLine = local_getline(0, in))!=0 ){ + char *z; + i = 0; + lineno++; + azCol[0] = zLine; + for(i=0, z=zLine; *z; z++){ + if( *z==zSep[0] && strncmp(z, zSep, nSep)==0 ){ + *z = 0; + i++; + if( i<nCol ){ + azCol[i] = &z[nSep]; + z += nSep-1; + } + } + } + if( i+1!=nCol ){ + char *zErr; + zErr = malloc(200 + strlen(zFile)); + sprintf(zErr,"Error: %s line %d: expected %d columns of data but found %d", + zFile, lineno, nCol, i+1); + Tcl_AppendResult(interp, zErr, 0); + free(zErr); + zCommit = "ROLLBACK"; + break; + } + for(i=0; i<nCol; i++){ + /* check for null data, if so, bind as null */ + if ((nNull>0 && strcmp(azCol[i], zNull)==0) || strlen(azCol[i])==0) { + sqlite3_bind_null(pStmt, i+1); + }else{ + sqlite3_bind_text(pStmt, i+1, azCol[i], -1, SQLITE_STATIC); + } + } + sqlite3_step(pStmt); + rc = sqlite3_reset(pStmt); + free(zLine); + if( rc!=SQLITE_OK ){ + Tcl_AppendResult(interp,"Error: ", sqlite3_errmsg(pDb->db), 0); + zCommit = "ROLLBACK"; + break; + } + } + free(azCol); + fclose(in); + sqlite3_finalize(pStmt); + sqlite3_exec(pDb->db, zCommit, 0, 0, 0); + + if( zCommit[0] == 'C' ){ + /* success, set result as number of lines processed */ + pResult = Tcl_GetObjResult(interp); + Tcl_SetIntObj(pResult, lineno); + rc = TCL_OK; + }else{ + /* failure, append lineno where failed */ + sprintf(zLineNum,"%d",lineno); + Tcl_AppendResult(interp,", failed while processing line: ",zLineNum,0); + rc = TCL_ERROR; + } + break; + } + /* ** $db errorcode ** @@ -1302,6 +1512,37 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ } /* + ** $db nullvalue ?STRING? + ** + ** Change text used when a NULL comes back from the database. If ?STRING? + ** is not present, then the current string used for NULL is returned. + ** If STRING is present, then STRING is returned. + ** + */ + case DB_NULLVALUE: { + if( objc!=2 && objc!=3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "NULLVALUE"); + return TCL_ERROR; + } + if( objc==3 ){ + int len; + char *zNull = Tcl_GetStringFromObj(objv[2], &len); + if( pDb->zNull ){ + Tcl_Free(pDb->zNull); + } + if( zNull && len>0 ){ + pDb->zNull = Tcl_Alloc( len + 1 ); + strncpy(pDb->zNull, zNull, len); + pDb->zNull[len] = '\0'; + }else{ + pDb->zNull = 0; + } + } + Tcl_SetObjResult(interp, dbTextToObj(pDb->zNull)); + break; + } + + /* ** $db last_insert_rowid ** ** Return an integer which is the ROWID for the most recent insert. @@ -1365,6 +1606,45 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ break; } + /* $db profile ?CALLBACK? + ** + ** Make arrangements to invoke the CALLBACK routine after each SQL statement + ** that has run. The text of the SQL and the amount of elapse time are + ** appended to CALLBACK before the script is run. + */ + case DB_PROFILE: { + if( objc>3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?"); + return TCL_ERROR; + }else if( objc==2 ){ + if( pDb->zProfile ){ + Tcl_AppendResult(interp, pDb->zProfile, 0); + } + }else{ + char *zProfile; + int len; + if( pDb->zProfile ){ + Tcl_Free(pDb->zProfile); + } + zProfile = Tcl_GetStringFromObj(objv[2], &len); + if( zProfile && len>0 ){ + pDb->zProfile = Tcl_Alloc( len + 1 ); + strcpy(pDb->zProfile, zProfile); + }else{ + pDb->zProfile = 0; + } +#ifndef SQLITE_OMIT_TRACE + if( pDb->zProfile ){ + pDb->interp = interp; + sqlite3_profile(pDb->db, DbProfileHandler, pDb); + }else{ + sqlite3_profile(pDb->db, 0, 0); + } +#endif + } + break; + } + /* ** $db rekey KEY ** @@ -1403,37 +1683,6 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ sqlite3_busy_timeout(pDb->db, ms); break; } - - /* - ** $db nullvalue ?STRING? - ** - ** Change text used when a NULL comes back from the database. If ?STRING? - ** is not present, then the current string used for NULL is returned. - ** If STRING is present, then STRING is returned. - ** - */ - case DB_NULLVALUE: { - if( objc!=2 && objc!=3 ){ - Tcl_WrongNumArgs(interp, 2, objv, "NULLVALUE"); - return TCL_ERROR; - } - if( objc==3 ){ - int len; - char *zNull = Tcl_GetStringFromObj(objv[2], &len); - if( pDb->zNull ){ - Tcl_Free(pDb->zNull); - } - if( zNull && len>0 ){ - pDb->zNull = Tcl_Alloc( len + 1 ); - strncpy(pDb->zNull, zNull, len); - pDb->zNull[len] = '\0'; - }else{ - pDb->zNull = 0; - } - } - Tcl_SetObjResult(interp, dbTextToObj(pDb->zNull)); - break; - } /* ** $db total_changes @@ -1479,12 +1728,14 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ }else{ pDb->zTrace = 0; } +#ifndef SQLITE_OMIT_TRACE if( pDb->zTrace ){ pDb->interp = interp; sqlite3_trace(pDb->db, DbTraceHandler, pDb); }else{ sqlite3_trace(pDb->db, 0, 0); } +#endif } break; } @@ -1546,192 +1797,6 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ break; } - /* $db copy conflict-algorithm table filename ?SEPARATOR? ?NULLINDICATOR? - ** - ** Copy data into table from filename, optionally using SEPARATOR - ** as column separators. If a column contains a null string, or the - ** value of NULLINDICATOR, a NULL is inserted for the column. - ** conflict-algorithm is one of the sqlite conflict algorithms: - ** rollback, abort, fail, ignore, replace - ** On success, return the number of lines processed, not necessarily same - ** as 'db changes' due to conflict-algorithm selected. - ** - ** This code is basically an implementation/enhancement of - ** the sqlite3 shell.c ".import" command. - ** - ** This command usage is equivalent to the sqlite2.x COPY statement, - ** which imports file data into a table using the PostgreSQL COPY file format: - ** $db copy $conflit_algo $table_name $filename \t \\N - */ - case DB_COPY: { - char *zTable; /* Insert data into this table */ - char *zFile; /* The file from which to extract data */ - char *zConflict; /* The conflict algorithm to use */ - sqlite3_stmt *pStmt; /* A statement */ - int rc; /* Result code */ - int nCol; /* Number of columns in the table */ - int nByte; /* Number of bytes in an SQL string */ - int i, j; /* Loop counters */ - int nSep; /* Number of bytes in zSep[] */ - int nNull; /* Number of bytes in zNull[] */ - char *zSql; /* An SQL statement */ - char *zLine; /* A single line of input from the file */ - char **azCol; /* zLine[] broken up into columns */ - char *zCommit; /* How to commit changes */ - FILE *in; /* The input file */ - int lineno = 0; /* Line number of input file */ - char zLineNum[80]; /* Line number print buffer */ - Tcl_Obj *pResult; /* interp result */ - - char *zSep; - char *zNull; - if( objc<5 || objc>7 ){ - Tcl_WrongNumArgs(interp, 2, objv, - "CONFLICT-ALGORITHM TABLE FILENAME ?SEPARATOR? ?NULLINDICATOR?"); - return TCL_ERROR; - } - if( objc>=6 ){ - zSep = Tcl_GetStringFromObj(objv[5], 0); - }else{ - zSep = "\t"; - } - if( objc>=7 ){ - zNull = Tcl_GetStringFromObj(objv[6], 0); - }else{ - zNull = ""; - } - zConflict = Tcl_GetStringFromObj(objv[2], 0); - zTable = Tcl_GetStringFromObj(objv[3], 0); - zFile = Tcl_GetStringFromObj(objv[4], 0); - nSep = strlen(zSep); - nNull = strlen(zNull); - if( nSep==0 ){ - Tcl_AppendResult(interp, "Error: non-null separator required for copy", 0); - return TCL_ERROR; - } - if(sqlite3StrICmp(zConflict, "rollback") != 0 && - sqlite3StrICmp(zConflict, "abort" ) != 0 && - sqlite3StrICmp(zConflict, "fail" ) != 0 && - sqlite3StrICmp(zConflict, "ignore" ) != 0 && - sqlite3StrICmp(zConflict, "replace" ) != 0 ) { - Tcl_AppendResult(interp, "Error: \"", zConflict, - "\", conflict-algorithm must be one of: rollback, " - "abort, fail, ignore, or replace", 0); - return TCL_ERROR; - } - zSql = sqlite3_mprintf("SELECT * FROM '%q'", zTable); - if( zSql==0 ){ - Tcl_AppendResult(interp, "Error: no such table: ", zTable, 0); - return TCL_ERROR; - } - nByte = strlen(zSql); - rc = sqlite3_prepare(pDb->db, zSql, 0, &pStmt, 0); - sqlite3_free(zSql); - if( rc ){ - Tcl_AppendResult(interp, "Error: ", sqlite3_errmsg(pDb->db), 0); - nCol = 0; - }else{ - nCol = sqlite3_column_count(pStmt); - } - sqlite3_finalize(pStmt); - if( nCol==0 ) { - return TCL_ERROR; - } - zSql = malloc( nByte + 50 + nCol*2 ); - if( zSql==0 ) { - Tcl_AppendResult(interp, "Error: can't malloc()", 0); - return TCL_ERROR; - } - sqlite3_snprintf(nByte+50, zSql, "INSERT OR %q INTO '%q' VALUES(?", - zConflict, zTable); - j = strlen(zSql); - for(i=1; i<nCol; i++){ - zSql[j++] = ','; - zSql[j++] = '?'; - } - zSql[j++] = ')'; - zSql[j] = 0; - rc = sqlite3_prepare(pDb->db, zSql, 0, &pStmt, 0); - free(zSql); - if( rc ){ - Tcl_AppendResult(interp, "Error: ", sqlite3_errmsg(pDb->db), 0); - sqlite3_finalize(pStmt); - return TCL_ERROR; - } - in = fopen(zFile, "rb"); - if( in==0 ){ - Tcl_AppendResult(interp, "Error: cannot open file: ", zFile, NULL); - sqlite3_finalize(pStmt); - return TCL_ERROR; - } - azCol = malloc( sizeof(azCol[0])*(nCol+1) ); - if( azCol==0 ) { - Tcl_AppendResult(interp, "Error: can't malloc()", 0); - return TCL_ERROR; - } - sqlite3_exec(pDb->db, "BEGIN", 0, 0, 0); - zCommit = "COMMIT"; - while( (zLine = local_getline(0, in))!=0 ){ - char *z; - i = 0; - lineno++; - azCol[0] = zLine; - for(i=0, z=zLine; *z; z++){ - if( *z==zSep[0] && strncmp(z, zSep, nSep)==0 ){ - *z = 0; - i++; - if( i<nCol ){ - azCol[i] = &z[nSep]; - z += nSep-1; - } - } - } - if( i+1!=nCol ){ - char *zErr; - zErr = malloc(200 + strlen(zFile)); - sprintf(zErr,"Error: %s line %d: expected %d columns of data but found %d", - zFile, lineno, nCol, i+1); - Tcl_AppendResult(interp, zErr, 0); - free(zErr); - zCommit = "ROLLBACK"; - break; - } - for(i=0; i<nCol; i++){ - /* check for null data, if so, bind as null */ - if ((nNull>0 && strcmp(azCol[i], zNull)==0) || strlen(azCol[i])==0) { - sqlite3_bind_null(pStmt, i+1); - }else{ - sqlite3_bind_text(pStmt, i+1, azCol[i], -1, SQLITE_STATIC); - } - } - sqlite3_step(pStmt); - rc = sqlite3_reset(pStmt); - free(zLine); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp,"Error: ", sqlite3_errmsg(pDb->db), 0); - zCommit = "ROLLBACK"; - break; - } - } - free(azCol); - fclose(in); - sqlite3_finalize(pStmt); - sqlite3_exec(pDb->db, zCommit, 0, 0, 0); - - if( zCommit[0] == 'C' ){ - /* success, set result as number of lines processed */ - pResult = Tcl_GetObjResult(interp); - Tcl_SetIntObj(pResult, lineno); - rc = TCL_OK; - }else{ - /* failure, append lineno where failed */ - sprintf(zLineNum,"%d",lineno); - Tcl_AppendResult(interp,", failed while processing line: ",zLineNum,0); - rc = TCL_ERROR; - } - break; - } - /* $db version ** ** Return the version string for this database. diff --git a/src/test1.c b/src/test1.c index e181f09e1..cd0c65585 100644 --- a/src/test1.c +++ b/src/test1.c @@ -13,7 +13,7 @@ ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test1.c,v 1.155 2005/08/19 00:14:42 drh Exp $ +** $Id: test1.c,v 1.156 2005/08/29 23:00:05 drh Exp $ */ #include "sqliteInt.h" #include "tcl.h" @@ -2954,6 +2954,12 @@ static void set_options(Tcl_Interp *interp){ Tcl_SetVar2(interp, "sqlite_options", "threadsafe", "0", TCL_GLOBAL_ONLY); #endif +#ifdef SQLITE_OMIT_TRACE + Tcl_SetVar2(interp, "sqlite_options", "trace", "0", TCL_GLOBAL_ONLY); +#else + Tcl_SetVar2(interp, "sqlite_options", "trace", "1", TCL_GLOBAL_ONLY); +#endif + #ifdef SQLITE_OMIT_TRIGGER Tcl_SetVar2(interp, "sqlite_options", "trigger", "0", TCL_GLOBAL_ONLY); #else diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 7bf2a0ec5..34e8d230b 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -358,6 +358,7 @@ struct Vdbe { u8 aborted; /* True if ROLLBACK in another VM causes an abort */ u8 expired; /* True if the VM needs to be recompiled */ int nChange; /* Number of db changes made since last reset */ + u64 startTime; /* Time when query started - used for profiling */ }; /* diff --git a/src/vdbeapi.c b/src/vdbeapi.c index dfe8f12c4..75af8a9b6 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -15,6 +15,7 @@ */ #include "sqliteInt.h" #include "vdbeInt.h" +#include "os.h" /* ** Return TRUE (non-zero) of the statement supplied as an argument needs @@ -173,9 +174,10 @@ int sqlite3_step(sqlite3_stmt *pStmt){ return SQLITE_MISUSE; } if( p->pc<0 ){ +#ifndef SQLITE_OMIT_TRACE /* Invoke the trace callback if there is one */ - if( (db = p->db)->xTrace && !db->init.busy ){ + if( db->xTrace && !db->init.busy ){ assert( p->nOp>0 ); assert( p->aOp[p->nOp-1].opcode==OP_Noop ); assert( p->aOp[p->nOp-1].p3!=0 ); @@ -187,6 +189,12 @@ int sqlite3_step(sqlite3_stmt *pStmt){ return SQLITE_MISUSE; } } + if( db->xProfile && !db->init.busy ){ + double rNow; + sqlite3OsCurrentTime(&rNow); + p->startTime = (rNow - (int)rNow)*3600.0*24.0*1000000000.0; + } +#endif /* Print a copy of SQL as it is executed if the SQL_TRACE pragma is turned ** on in debugging mode. @@ -213,6 +221,23 @@ int sqlite3_step(sqlite3_stmt *pStmt){ rc = SQLITE_MISUSE; } +#ifndef SQLITE_OMIT_TRACE + /* Invoke the profile callback if there is one + */ + if( rc!=SQLITE_ROW && db->xProfile && !db->init.busy ){ + double rNow; + u64 elapseTime; + + sqlite3OsCurrentTime(&rNow); + elapseTime = (rNow - (int)rNow)*3600.0*24.0*1000000000.0 - p->startTime; + assert( p->nOp>0 ); + assert( p->aOp[p->nOp-1].opcode==OP_Noop ); + assert( p->aOp[p->nOp-1].p3!=0 ); + assert( p->aOp[p->nOp-1].p3type==P3_DYNAMIC ); + db->xProfile(db->pProfileArg, p->aOp[p->nOp-1].p3, elapseTime); + } +#endif + sqlite3Error(p->db, rc, p->zErrMsg ? "%s" : 0, p->zErrMsg); return rc; } |