/* ** This routine installs a default busy handler that waits for the ** specified number of milliseconds before returning 0. */ void sqlite_busy_timeout(sqlite *db, int ms){ if( ms>0 ){ sqlite_busy_handler(db, sqliteDefaultBusyCallback, (void*)(long)ms); }else{ sqlite_busy_handler(db, 0, 0); } }
IoObject *IoSQLite_open(IoSQLite *self, IoObject *locals, IoMessage *m) { /*doc SQLite open(optionalPathString) Opens the database.Returns self on success or nil upon failure. If the databse is locked, "yield" will be called until it is accessable or timeoutSeconds has expired. */ char *zErrMsg; if (DATA(self)->debugOn) { IoState_print_(IOSTATE, "IoSQLite opening '%s'\n", CSTRING(DATA(self)->path)); } DATA(self)->db = sqlite_open(CSTRING(DATA(self)->path), 0, &zErrMsg); if (!DATA(self)->db) { IoSQLite_error_(self, zErrMsg); } else { IoSQLite_error_(self, ""); } sqlite_busy_handler(DATA(self)->db, IoSQLite_busyHandler, self); sqlite_busy_timeout(DATA(self)->db, DATA(self)->timeoutSeconds*1000); return self; }
static void *worker_bee(void *pArg){ const char *zFilename = (char*)pArg; char *azErr; int i, cnt; int t = atoi(zFilename); char **az; sqlite *db; pthread_mutex_lock(&lock); thread_cnt++; pthread_mutex_unlock(&lock); printf("%s: START\n", zFilename); fflush(stdout); for(cnt=0; cnt<10; cnt++){ db = sqlite_open(&zFilename[2], 0, &azErr); if( db==0 ){ fprintf(stdout,"%s: can't open\n", zFilename); Exit(1); } sqlite_busy_handler(db, db_is_locked, zFilename); db_execute(db, zFilename, "CREATE TABLE t%d(a,b,c);", t); for(i=1; i<=100; i++){ db_execute(db, zFilename, "INSERT INTO t%d VALUES(%d,%d,%d);", t, i, i*2, i*i); } az = db_query(db, zFilename, "SELECT count(*) FROM t%d", t); db_check(zFilename, "tX size", az, "100", 0); az = db_query(db, zFilename, "SELECT avg(b) FROM t%d", t); db_check(zFilename, "tX avg", az, "101", 0); db_execute(db, zFilename, "DELETE FROM t%d WHERE a>50", t); az = db_query(db, zFilename, "SELECT avg(b) FROM t%d", t); db_check(zFilename, "tX avg2", az, "51", 0); for(i=1; i<=50; i++){ char z1[30], z2[30]; az = db_query(db, zFilename, "SELECT b, c FROM t%d WHERE a=%d", t, i); sprintf(z1, "%d", i*2); sprintf(z2, "%d", i*i); db_check(zFilename, "readback", az, z1, z2, 0); } db_execute(db, zFilename, "DROP TABLE t%d;", t); sqlite_close(db); } printf("%s: END\n", zFilename); /* unlink(zFilename); */ fflush(stdout); pthread_mutex_lock(&lock); thread_cnt--; if( thread_cnt<=0 ){ pthread_cond_signal(&sig); } pthread_mutex_unlock(&lock); return 0; }
/* ** The "sqlite" command below creates a new Tcl command for each ** connection it opens to an SQLite database. This routine is invoked ** whenever one of those connection-specific commands is executed ** in Tcl. For example, if you run Tcl code like this: ** ** sqlite db1 "my_database" ** db1 close ** ** The first command opens a connection to the "my_database" database ** and calls that connection "db1". The second command causes this ** subroutine to be invoked. */ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ SqliteDb *pDb = (SqliteDb*)cd; int choice; int rc = TCL_OK; static const char *DB_strs[] = { "authorizer", "busy", "changes", "close", "commit_hook", "complete", "errorcode", "eval", "function", "last_insert_rowid", "last_statement_changes", "onecolumn", "progress", "rekey", "timeout", "trace", 0 }; enum DB_enum { DB_AUTHORIZER, DB_BUSY, DB_CHANGES, DB_CLOSE, DB_COMMIT_HOOK, DB_COMPLETE, DB_ERRORCODE, DB_EVAL, DB_FUNCTION, DB_LAST_INSERT_ROWID, DB_LAST_STATEMENT_CHANGES, DB_ONECOLUMN, DB_PROGRESS, DB_REKEY, DB_TIMEOUT, DB_TRACE }; if( objc<2 ){ Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ..."); return TCL_ERROR; } if( Tcl_GetIndexFromObj(interp, objv[1], DB_strs, "option", 0, &choice) ){ return TCL_ERROR; } switch( (enum DB_enum)choice ){ /* $db authorizer ?CALLBACK? ** ** Invoke the given callback to authorize each SQL operation as it is ** compiled. 5 arguments are appended to the callback before it is ** invoked: ** ** (1) The authorization type (ex: SQLITE_CREATE_TABLE, SQLITE_INSERT, ...) ** (2) First descriptive name (depends on authorization type) ** (3) Second descriptive name ** (4) Name of the database (ex: "main", "temp") ** (5) Name of trigger that is doing the access ** ** The callback should return on of the following strings: SQLITE_OK, ** SQLITE_IGNORE, or SQLITE_DENY. Any other return value is an error. ** ** If this method is invoked with no arguments, the current authorization ** callback string is returned. */ case DB_AUTHORIZER: { if( objc>3 ){ Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?"); }else if( objc==2 ){ if( pDb->zAuth ){ Tcl_AppendResult(interp, pDb->zAuth, 0); } }else{ char *zAuth; int len; if( pDb->zAuth ){ Tcl_Free(pDb->zAuth); } zAuth = Tcl_GetStringFromObj(objv[2], &len); if( zAuth && len>0 ){ pDb->zAuth = Tcl_Alloc( len + 1 ); strcpy(pDb->zAuth, zAuth); }else{ pDb->zAuth = 0; } #ifndef SQLITE_OMIT_AUTHORIZATION if( pDb->zAuth ){ pDb->interp = interp; sqlite_set_authorizer(pDb->db, auth_callback, pDb); }else{ sqlite_set_authorizer(pDb->db, 0, 0); } #endif } break; } /* $db busy ?CALLBACK? ** ** Invoke the given callback if an SQL statement attempts to open ** a locked database file. */ case DB_BUSY: { if( objc>3 ){ Tcl_WrongNumArgs(interp, 2, objv, "CALLBACK"); return TCL_ERROR; }else if( objc==2 ){ if( pDb->zBusy ){ Tcl_AppendResult(interp, pDb->zBusy, 0); } }else{ char *zBusy; int len; if( pDb->zBusy ){ Tcl_Free(pDb->zBusy); } zBusy = Tcl_GetStringFromObj(objv[2], &len); if( zBusy && len>0 ){ pDb->zBusy = Tcl_Alloc( len + 1 ); strcpy(pDb->zBusy, zBusy); }else{ pDb->zBusy = 0; } if( pDb->zBusy ){ pDb->interp = interp; sqlite_busy_handler(pDb->db, DbBusyHandler, pDb); }else{ sqlite_busy_handler(pDb->db, 0, 0); } } break; } /* $db progress ?N CALLBACK? ** ** Invoke the given callback every N virtual machine opcodes while executing ** queries. */ case DB_PROGRESS: { if( objc==2 ){ if( pDb->zProgress ){ Tcl_AppendResult(interp, pDb->zProgress, 0); } }else if( objc==4 ){ char *zProgress; int len; int N; if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &N) ){ return TCL_ERROR; }; if( pDb->zProgress ){ Tcl_Free(pDb->zProgress); } zProgress = Tcl_GetStringFromObj(objv[3], &len); if( zProgress && len>0 ){ pDb->zProgress = Tcl_Alloc( len + 1 ); strcpy(pDb->zProgress, zProgress); }else{ pDb->zProgress = 0; } #ifndef SQLITE_OMIT_PROGRESS_CALLBACK if( pDb->zProgress ){ pDb->interp = interp; sqlite_progress_handler(pDb->db, N, DbProgressHandler, pDb); }else{ sqlite_progress_handler(pDb->db, 0, 0, 0); } #endif }else{ Tcl_WrongNumArgs(interp, 2, objv, "N CALLBACK"); return TCL_ERROR; } break; } /* ** $db changes ** ** Return the number of rows that were modified, inserted, or deleted by ** the most recent "eval". */ case DB_CHANGES: { Tcl_Obj *pResult; int nChange; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 2, objv, ""); return TCL_ERROR; } nChange = sqlite_changes(pDb->db); pResult = Tcl_GetObjResult(interp); Tcl_SetIntObj(pResult, nChange); break; } /* ** $db last_statement_changes ** ** Return the number of rows that were modified, inserted, or deleted by ** the last statment to complete execution (excluding changes due to ** triggers) */ case DB_LAST_STATEMENT_CHANGES: { Tcl_Obj *pResult; int lsChange; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 2, objv, ""); return TCL_ERROR; } lsChange = sqlite_last_statement_changes(pDb->db); pResult = Tcl_GetObjResult(interp); Tcl_SetIntObj(pResult, lsChange); break; } /* $db close ** ** Shutdown the database */ case DB_CLOSE: { Tcl_DeleteCommand(interp, Tcl_GetStringFromObj(objv[0], 0)); 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?"); }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; sqlite_commit_hook(pDb->db, DbCommitHandler, pDb); }else{ sqlite_commit_hook(pDb->db, 0, 0); } } break; } /* $db complete SQL ** ** Return TRUE if SQL is a complete SQL statement. Return FALSE if ** additional lines of input are needed. This is similar to the ** built-in "info complete" command of Tcl. */ case DB_COMPLETE: { Tcl_Obj *pResult; int isComplete; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "SQL"); return TCL_ERROR; } isComplete = sqlite_complete( Tcl_GetStringFromObj(objv[2], 0) ); pResult = Tcl_GetObjResult(interp); Tcl_SetBooleanObj(pResult, isComplete); break; } /* ** $db errorcode ** ** Return the numeric error code that was returned by the most recent ** call to sqlite_exec(). */ case DB_ERRORCODE: { Tcl_SetObjResult(interp, Tcl_NewIntObj(pDb->rc)); break; } /* ** $db eval $sql ?array { ...code... }? ** ** The SQL statement in $sql is evaluated. For each row, the values are ** placed in elements of the array named "array" and ...code... is executed. ** If "array" and "code" are omitted, then no callback is every invoked. ** If "array" is an empty string, then the values are placed in variables ** that have the same name as the fields extracted by the query. */ case DB_EVAL: { CallbackData cbData; char *zErrMsg; char *zSql; #ifdef UTF_TRANSLATION_NEEDED Tcl_DString dSql; int i; #endif if( objc!=5 && objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "SQL ?ARRAY-NAME CODE?"); return TCL_ERROR; } pDb->interp = interp; zSql = Tcl_GetStringFromObj(objv[2], 0); #ifdef UTF_TRANSLATION_NEEDED Tcl_DStringInit(&dSql); Tcl_UtfToExternalDString(NULL, zSql, -1, &dSql); zSql = Tcl_DStringValue(&dSql); #endif Tcl_IncrRefCount(objv[2]); if( objc==5 ){ cbData.interp = interp; cbData.once = 1; cbData.zArray = Tcl_GetStringFromObj(objv[3], 0); cbData.pCode = objv[4]; cbData.tcl_rc = TCL_OK; cbData.nColName = 0; cbData.azColName = 0; zErrMsg = 0; Tcl_IncrRefCount(objv[3]); Tcl_IncrRefCount(objv[4]); rc = sqlite_exec(pDb->db, zSql, DbEvalCallback, &cbData, &zErrMsg); Tcl_DecrRefCount(objv[4]); Tcl_DecrRefCount(objv[3]); if( cbData.tcl_rc==TCL_BREAK ){ cbData.tcl_rc = TCL_OK; } }else{ Tcl_Obj *pList = Tcl_NewObj(); cbData.tcl_rc = TCL_OK; rc = sqlite_exec(pDb->db, zSql, DbEvalCallback2, pList, &zErrMsg); Tcl_SetObjResult(interp, pList); } pDb->rc = rc; if( rc==SQLITE_ABORT ){ if( zErrMsg ) free(zErrMsg); rc = cbData.tcl_rc; }else if( zErrMsg ){ Tcl_SetResult(interp, zErrMsg, TCL_VOLATILE); free(zErrMsg); rc = TCL_ERROR; }else if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, sqlite_error_string(rc), 0); rc = TCL_ERROR; }else{ } Tcl_DecrRefCount(objv[2]); #ifdef UTF_TRANSLATION_NEEDED Tcl_DStringFree(&dSql); if( objc==5 && cbData.azColName ){ for(i=0; i<cbData.nColName; i++){ if( cbData.azColName[i] ) free(cbData.azColName[i]); } free(cbData.azColName); cbData.azColName = 0; } #endif return rc; } /* ** $db function NAME SCRIPT ** ** Create a new SQL function called NAME. Whenever that function is ** called, invoke SCRIPT to evaluate the function. */ case DB_FUNCTION: { SqlFunc *pFunc; char *zName; char *zScript; int nScript; if( objc!=4 ){ Tcl_WrongNumArgs(interp, 2, objv, "NAME SCRIPT"); return TCL_ERROR; } zName = Tcl_GetStringFromObj(objv[2], 0); zScript = Tcl_GetStringFromObj(objv[3], &nScript); pFunc = (SqlFunc*)Tcl_Alloc( sizeof(*pFunc) + nScript + 1 ); if( pFunc==0 ) return TCL_ERROR; pFunc->interp = interp; pFunc->pNext = pDb->pFunc; pFunc->zScript = (char*)&pFunc[1]; strcpy(pFunc->zScript, zScript); sqlite_create_function(pDb->db, zName, -1, tclSqlFunc, pFunc); sqlite_function_type(pDb->db, zName, SQLITE_NUMERIC); break; } /* ** $db last_insert_rowid ** ** Return an integer which is the ROWID for the most recent insert. */ case DB_LAST_INSERT_ROWID: { Tcl_Obj *pResult; int rowid; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 2, objv, ""); return TCL_ERROR; } rowid = sqlite_last_insert_rowid(pDb->db); pResult = Tcl_GetObjResult(interp); Tcl_SetIntObj(pResult, rowid); break; } /* ** $db onecolumn SQL ** ** Return a single column from a single row of the given SQL query. */ case DB_ONECOLUMN: { char *zSql; char *zErrMsg = 0; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "SQL"); return TCL_ERROR; } zSql = Tcl_GetStringFromObj(objv[2], 0); rc = sqlite_exec(pDb->db, zSql, DbEvalCallback3, interp, &zErrMsg); if( rc==SQLITE_ABORT ){ rc = SQLITE_OK; }else if( zErrMsg ){ Tcl_SetResult(interp, zErrMsg, TCL_VOLATILE); free(zErrMsg); rc = TCL_ERROR; }else if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, sqlite_error_string(rc), 0); rc = TCL_ERROR; } break; } /* ** $db rekey KEY ** ** Change the encryption key on the currently open database. */ case DB_REKEY: { int nKey; void *pKey; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "KEY"); return TCL_ERROR; } pKey = Tcl_GetByteArrayFromObj(objv[2], &nKey); #ifdef SQLITE_HAS_CODEC rc = sqlite_rekey(pDb->db, pKey, nKey); if( rc ){ Tcl_AppendResult(interp, sqlite_error_string(rc), 0); rc = TCL_ERROR; } #endif break; } /* ** $db timeout MILLESECONDS ** ** Delay for the number of milliseconds specified when a file is locked. */ case DB_TIMEOUT: { int ms; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "MILLISECONDS"); return TCL_ERROR; } if( Tcl_GetIntFromObj(interp, objv[2], &ms) ) return TCL_ERROR; sqlite_busy_timeout(pDb->db, ms); break; } /* $db trace ?CALLBACK? ** ** Make arrangements to invoke the CALLBACK routine for each SQL statement ** that is executed. The text of the SQL is appended to CALLBACK before ** it is executed. */ case DB_TRACE: { if( objc>3 ){ Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?"); }else if( objc==2 ){ if( pDb->zTrace ){ Tcl_AppendResult(interp, pDb->zTrace, 0); } }else{ char *zTrace; int len; if( pDb->zTrace ){ Tcl_Free(pDb->zTrace); } zTrace = Tcl_GetStringFromObj(objv[2], &len); if( zTrace && len>0 ){ pDb->zTrace = Tcl_Alloc( len + 1 ); strcpy(pDb->zTrace, zTrace); }else{ pDb->zTrace = 0; } if( pDb->zTrace ){ pDb->interp = interp; sqlite_trace(pDb->db, DbTraceHandler, pDb); }else{ sqlite_trace(pDb->db, 0, 0); } } break; } } /* End of the SWITCH statement */ return rc; }