Annotation of embedaddon/sqlite3/src/test_thread.c, revision 1.1.1.1

1.1       misho       1: /*
                      2: ** 2007 September 9
                      3: **
                      4: ** The author disclaims copyright to this source code.  In place of
                      5: ** a legal notice, here is a blessing:
                      6: **
                      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.
                     10: **
                     11: *************************************************************************
                     12: **
                     13: ** This file contains the implementation of some Tcl commands used to
                     14: ** test that sqlite3 database handles may be concurrently accessed by 
                     15: ** multiple threads. Right now this only works on unix.
                     16: */
                     17: 
                     18: #include "sqliteInt.h"
                     19: #include <tcl.h>
                     20: 
                     21: #if SQLITE_THREADSAFE
                     22: 
                     23: #include <errno.h>
                     24: 
                     25: #if !defined(_MSC_VER)
                     26: #include <unistd.h>
                     27: #endif
                     28: 
                     29: /*
                     30: ** One of these is allocated for each thread created by [sqlthread spawn].
                     31: */
                     32: typedef struct SqlThread SqlThread;
                     33: struct SqlThread {
                     34:   Tcl_ThreadId parent;     /* Thread id of parent thread */
                     35:   Tcl_Interp *interp;      /* Parent interpreter */
                     36:   char *zScript;           /* The script to execute. */
                     37:   char *zVarname;          /* Varname in parent script */
                     38: };
                     39: 
                     40: /*
                     41: ** A custom Tcl_Event type used by this module. When the event is
                     42: ** handled, script zScript is evaluated in interpreter interp. If
                     43: ** the evaluation throws an exception (returns TCL_ERROR), then the
                     44: ** error is handled by Tcl_BackgroundError(). If no error occurs,
                     45: ** the result is simply discarded.
                     46: */
                     47: typedef struct EvalEvent EvalEvent;
                     48: struct EvalEvent {
                     49:   Tcl_Event base;          /* Base class of type Tcl_Event */
                     50:   char *zScript;           /* The script to execute. */
                     51:   Tcl_Interp *interp;      /* The interpreter to execute it in. */
                     52: };
                     53: 
                     54: static Tcl_ObjCmdProc sqlthread_proc;
                     55: static Tcl_ObjCmdProc clock_seconds_proc;
                     56: #if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
                     57: static Tcl_ObjCmdProc blocking_step_proc;
                     58: static Tcl_ObjCmdProc blocking_prepare_v2_proc;
                     59: #endif
                     60: int Sqlitetest1_Init(Tcl_Interp *);
                     61: int Sqlite3_Init(Tcl_Interp *);
                     62: 
                     63: /* Functions from test1.c */
                     64: void *sqlite3TestTextToPtr(const char *);
                     65: const char *sqlite3TestErrorName(int);
                     66: int getDbPointer(Tcl_Interp *, const char *, sqlite3 **);
                     67: int sqlite3TestMakePointerStr(Tcl_Interp *, char *, void *);
                     68: int sqlite3TestErrCode(Tcl_Interp *, sqlite3 *, int);
                     69: 
                     70: /*
                     71: ** Handler for events of type EvalEvent.
                     72: */
                     73: static int tclScriptEvent(Tcl_Event *evPtr, int flags){
                     74:   int rc;
                     75:   EvalEvent *p = (EvalEvent *)evPtr;
                     76:   rc = Tcl_Eval(p->interp, p->zScript);
                     77:   if( rc!=TCL_OK ){
                     78:     Tcl_BackgroundError(p->interp);
                     79:   }
                     80:   UNUSED_PARAMETER(flags);
                     81:   return 1;
                     82: }
                     83: 
                     84: /*
                     85: ** Register an EvalEvent to evaluate the script pScript in the
                     86: ** parent interpreter/thread of SqlThread p.
                     87: */
                     88: static void postToParent(SqlThread *p, Tcl_Obj *pScript){
                     89:   EvalEvent *pEvent;
                     90:   char *zMsg;
                     91:   int nMsg;
                     92: 
                     93:   zMsg = Tcl_GetStringFromObj(pScript, &nMsg); 
                     94:   pEvent = (EvalEvent *)ckalloc(sizeof(EvalEvent)+nMsg+1);
                     95:   pEvent->base.nextPtr = 0;
                     96:   pEvent->base.proc = tclScriptEvent;
                     97:   pEvent->zScript = (char *)&pEvent[1];
                     98:   memcpy(pEvent->zScript, zMsg, nMsg+1);
                     99:   pEvent->interp = p->interp;
                    100: 
                    101:   Tcl_ThreadQueueEvent(p->parent, (Tcl_Event *)pEvent, TCL_QUEUE_TAIL);
                    102:   Tcl_ThreadAlert(p->parent);
                    103: }
                    104: 
                    105: /*
                    106: ** The main function for threads created with [sqlthread spawn].
                    107: */
                    108: static Tcl_ThreadCreateType tclScriptThread(ClientData pSqlThread){
                    109:   Tcl_Interp *interp;
                    110:   Tcl_Obj *pRes;
                    111:   Tcl_Obj *pList;
                    112:   int rc;
                    113:   SqlThread *p = (SqlThread *)pSqlThread;
                    114:   extern int Sqlitetest_mutex_Init(Tcl_Interp*);
                    115: 
                    116:   interp = Tcl_CreateInterp();
                    117:   Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0);
                    118:   Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, pSqlThread, 0);
                    119: #if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
                    120:   Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0);
                    121:   Tcl_CreateObjCommand(interp, 
                    122:       "sqlite3_blocking_prepare_v2", blocking_prepare_v2_proc, (void *)1, 0);
                    123:   Tcl_CreateObjCommand(interp, 
                    124:       "sqlite3_nonblocking_prepare_v2", blocking_prepare_v2_proc, 0, 0);
                    125: #endif
                    126:   Sqlitetest1_Init(interp);
                    127:   Sqlitetest_mutex_Init(interp);
                    128:   Sqlite3_Init(interp);
                    129: 
                    130:   rc = Tcl_Eval(interp, p->zScript);
                    131:   pRes = Tcl_GetObjResult(interp);
                    132:   pList = Tcl_NewObj();
                    133:   Tcl_IncrRefCount(pList);
                    134:   Tcl_IncrRefCount(pRes);
                    135: 
                    136:   if( rc!=TCL_OK ){
                    137:     Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj("error", -1));
                    138:     Tcl_ListObjAppendElement(interp, pList, pRes);
                    139:     postToParent(p, pList);
                    140:     Tcl_DecrRefCount(pList);
                    141:     pList = Tcl_NewObj();
                    142:   }
                    143: 
                    144:   Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj("set", -1));
                    145:   Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj(p->zVarname, -1));
                    146:   Tcl_ListObjAppendElement(interp, pList, pRes);
                    147:   postToParent(p, pList);
                    148: 
                    149:   ckfree((void *)p);
                    150:   Tcl_DecrRefCount(pList);
                    151:   Tcl_DecrRefCount(pRes);
                    152:   Tcl_DeleteInterp(interp);
                    153:   while( Tcl_DoOneEvent(TCL_ALL_EVENTS|TCL_DONT_WAIT) );
                    154:   Tcl_ExitThread(0);
                    155:   TCL_THREAD_CREATE_RETURN;
                    156: }
                    157: 
                    158: /*
                    159: ** sqlthread spawn VARNAME SCRIPT
                    160: **
                    161: **     Spawn a new thread with its own Tcl interpreter and run the
                    162: **     specified SCRIPT(s) in it. The thread terminates after running
                    163: **     the script. The result of the script is stored in the variable
                    164: **     VARNAME.
                    165: **
                    166: **     The caller can wait for the script to terminate using [vwait VARNAME].
                    167: */
                    168: static int sqlthread_spawn(
                    169:   ClientData clientData,
                    170:   Tcl_Interp *interp,
                    171:   int objc,
                    172:   Tcl_Obj *CONST objv[]
                    173: ){
                    174:   Tcl_ThreadId x;
                    175:   SqlThread *pNew;
                    176:   int rc;
                    177: 
                    178:   int nVarname; char *zVarname;
                    179:   int nScript; char *zScript;
                    180: 
                    181:   /* Parameters for thread creation */
                    182:   const int nStack = TCL_THREAD_STACK_DEFAULT;
                    183:   const int flags = TCL_THREAD_NOFLAGS;
                    184: 
                    185:   assert(objc==4);
                    186:   UNUSED_PARAMETER(clientData);
                    187:   UNUSED_PARAMETER(objc);
                    188: 
                    189:   zVarname = Tcl_GetStringFromObj(objv[2], &nVarname);
                    190:   zScript = Tcl_GetStringFromObj(objv[3], &nScript);
                    191: 
                    192:   pNew = (SqlThread *)ckalloc(sizeof(SqlThread)+nVarname+nScript+2);
                    193:   pNew->zVarname = (char *)&pNew[1];
                    194:   pNew->zScript = (char *)&pNew->zVarname[nVarname+1];
                    195:   memcpy(pNew->zVarname, zVarname, nVarname+1);
                    196:   memcpy(pNew->zScript, zScript, nScript+1);
                    197:   pNew->parent = Tcl_GetCurrentThread();
                    198:   pNew->interp = interp;
                    199: 
                    200:   rc = Tcl_CreateThread(&x, tclScriptThread, (void *)pNew, nStack, flags);
                    201:   if( rc!=TCL_OK ){
                    202:     Tcl_AppendResult(interp, "Error in Tcl_CreateThread()", 0);
                    203:     ckfree((char *)pNew);
                    204:     return TCL_ERROR;
                    205:   }
                    206: 
                    207:   return TCL_OK;
                    208: }
                    209: 
                    210: /*
                    211: ** sqlthread parent SCRIPT
                    212: **
                    213: **     This can be called by spawned threads only. It sends the specified
                    214: **     script back to the parent thread for execution. The result of
                    215: **     evaluating the SCRIPT is returned. The parent thread must enter
                    216: **     the event loop for this to work - otherwise the caller will
                    217: **     block indefinitely.
                    218: **
                    219: **     NOTE: At the moment, this doesn't work. FIXME.
                    220: */
                    221: static int sqlthread_parent(
                    222:   ClientData clientData,
                    223:   Tcl_Interp *interp,
                    224:   int objc,
                    225:   Tcl_Obj *CONST objv[]
                    226: ){
                    227:   EvalEvent *pEvent;
                    228:   char *zMsg;
                    229:   int nMsg;
                    230:   SqlThread *p = (SqlThread *)clientData;
                    231: 
                    232:   assert(objc==3);
                    233:   UNUSED_PARAMETER(objc);
                    234: 
                    235:   if( p==0 ){
                    236:     Tcl_AppendResult(interp, "no parent thread", 0);
                    237:     return TCL_ERROR;
                    238:   }
                    239: 
                    240:   zMsg = Tcl_GetStringFromObj(objv[2], &nMsg);
                    241:   pEvent = (EvalEvent *)ckalloc(sizeof(EvalEvent)+nMsg+1);
                    242:   pEvent->base.nextPtr = 0;
                    243:   pEvent->base.proc = tclScriptEvent;
                    244:   pEvent->zScript = (char *)&pEvent[1];
                    245:   memcpy(pEvent->zScript, zMsg, nMsg+1);
                    246:   pEvent->interp = p->interp;
                    247:   Tcl_ThreadQueueEvent(p->parent, (Tcl_Event *)pEvent, TCL_QUEUE_TAIL);
                    248:   Tcl_ThreadAlert(p->parent);
                    249: 
                    250:   return TCL_OK;
                    251: }
                    252: 
                    253: static int xBusy(void *pArg, int nBusy){
                    254:   UNUSED_PARAMETER(pArg);
                    255:   UNUSED_PARAMETER(nBusy);
                    256:   sqlite3_sleep(50);
                    257:   return 1;             /* Try again... */
                    258: }
                    259: 
                    260: /*
                    261: ** sqlthread open
                    262: **
                    263: **     Open a database handle and return the string representation of
                    264: **     the pointer value.
                    265: */
                    266: static int sqlthread_open(
                    267:   ClientData clientData,
                    268:   Tcl_Interp *interp,
                    269:   int objc,
                    270:   Tcl_Obj *CONST objv[]
                    271: ){
                    272:   int sqlite3TestMakePointerStr(Tcl_Interp *interp, char *zPtr, void *p);
                    273: 
                    274:   const char *zFilename;
                    275:   sqlite3 *db;
                    276:   int rc;
                    277:   char zBuf[100];
                    278:   extern void Md5_Register(sqlite3*);
                    279: 
                    280:   UNUSED_PARAMETER(clientData);
                    281:   UNUSED_PARAMETER(objc);
                    282: 
                    283:   zFilename = Tcl_GetString(objv[2]);
                    284:   rc = sqlite3_open(zFilename, &db);
                    285: #ifdef SQLITE_HAS_CODEC
                    286:   if( db && objc>=4 ){
                    287:     const char *zKey;
                    288:     int nKey;
                    289:     zKey = Tcl_GetStringFromObj(objv[3], &nKey);
                    290:     rc = sqlite3_key(db, zKey, nKey);
                    291:     if( rc!=SQLITE_OK ){
                    292:       char *zErrMsg = sqlite3_mprintf("error %d: %s", rc, sqlite3_errmsg(db));
                    293:       sqlite3_close(db);
                    294:       Tcl_AppendResult(interp, zErrMsg, (char*)0);
                    295:       sqlite3_free(zErrMsg);
                    296:       return TCL_ERROR;
                    297:     }
                    298:   }
                    299: #endif
                    300:   Md5_Register(db);
                    301:   sqlite3_busy_handler(db, xBusy, 0);
                    302:   
                    303:   if( sqlite3TestMakePointerStr(interp, zBuf, db) ) return TCL_ERROR;
                    304:   Tcl_AppendResult(interp, zBuf, 0);
                    305: 
                    306:   return TCL_OK;
                    307: }
                    308: 
                    309: 
                    310: /*
                    311: ** sqlthread open
                    312: **
                    313: **     Return the current thread-id (Tcl_GetCurrentThread()) cast to
                    314: **     an integer.
                    315: */
                    316: static int sqlthread_id(
                    317:   ClientData clientData,
                    318:   Tcl_Interp *interp,
                    319:   int objc,
                    320:   Tcl_Obj *CONST objv[]
                    321: ){
                    322:   Tcl_ThreadId id = Tcl_GetCurrentThread();
                    323:   Tcl_SetObjResult(interp, Tcl_NewIntObj(SQLITE_PTR_TO_INT(id)));
                    324:   UNUSED_PARAMETER(clientData);
                    325:   UNUSED_PARAMETER(objc);
                    326:   UNUSED_PARAMETER(objv);
                    327:   return TCL_OK;
                    328: }
                    329: 
                    330: 
                    331: /*
                    332: ** Dispatch routine for the sub-commands of [sqlthread].
                    333: */
                    334: static int sqlthread_proc(
                    335:   ClientData clientData,
                    336:   Tcl_Interp *interp,
                    337:   int objc,
                    338:   Tcl_Obj *CONST objv[]
                    339: ){
                    340:   struct SubCommand {
                    341:     char *zName;
                    342:     Tcl_ObjCmdProc *xProc;
                    343:     int nArg;
                    344:     char *zUsage;
                    345:   } aSub[] = {
                    346:     {"parent", sqlthread_parent, 1, "SCRIPT"},
                    347:     {"spawn",  sqlthread_spawn,  2, "VARNAME SCRIPT"},
                    348:     {"open",   sqlthread_open,   1, "DBNAME"},
                    349:     {"id",     sqlthread_id,     0, ""},
                    350:     {0, 0, 0}
                    351:   };
                    352:   struct SubCommand *pSub;
                    353:   int rc;
                    354:   int iIndex;
                    355: 
                    356:   if( objc<2 ){
                    357:     Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND");
                    358:     return TCL_ERROR;
                    359:   }
                    360: 
                    361:   rc = Tcl_GetIndexFromObjStruct(
                    362:       interp, objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iIndex
                    363:   );
                    364:   if( rc!=TCL_OK ) return rc;
                    365:   pSub = &aSub[iIndex];
                    366: 
                    367:   if( objc<(pSub->nArg+2) ){
                    368:     Tcl_WrongNumArgs(interp, 2, objv, pSub->zUsage);
                    369:     return TCL_ERROR;
                    370:   }
                    371: 
                    372:   return pSub->xProc(clientData, interp, objc, objv);
                    373: }
                    374: 
                    375: /*
                    376: ** The [clock_seconds] command. This is more or less the same as the
                    377: ** regular tcl [clock seconds], except that it is available in testfixture
                    378: ** when linked against both Tcl 8.4 and 8.5. Because [clock seconds] is
                    379: ** implemented as a script in Tcl 8.5, it is not usually available to
                    380: ** testfixture.
                    381: */ 
                    382: static int clock_seconds_proc(
                    383:   ClientData clientData,
                    384:   Tcl_Interp *interp,
                    385:   int objc,
                    386:   Tcl_Obj *CONST objv[]
                    387: ){
                    388:   Tcl_Time now;
                    389:   Tcl_GetTime(&now);
                    390:   Tcl_SetObjResult(interp, Tcl_NewIntObj(now.sec));
                    391:   UNUSED_PARAMETER(clientData);
                    392:   UNUSED_PARAMETER(objc);
                    393:   UNUSED_PARAMETER(objv);
                    394:   return TCL_OK;
                    395: }
                    396: 
                    397: /*************************************************************************
                    398: ** This block contains the implementation of the [sqlite3_blocking_step]
                    399: ** command available to threads created by [sqlthread spawn] commands. It
                    400: ** is only available on UNIX for now. This is because pthread condition
                    401: ** variables are used.
                    402: **
                    403: ** The source code for the C functions sqlite3_blocking_step(),
                    404: ** blocking_step_notify() and the structure UnlockNotification is
                    405: ** automatically extracted from this file and used as part of the
                    406: ** documentation for the sqlite3_unlock_notify() API function. This
                    407: ** should be considered if these functions are to be extended (i.e. to 
                    408: ** support windows) in the future.
                    409: */ 
                    410: #if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
                    411: 
                    412: /* BEGIN_SQLITE_BLOCKING_STEP */
                    413: /* This example uses the pthreads API */
                    414: #include <pthread.h>
                    415: 
                    416: /*
                    417: ** A pointer to an instance of this structure is passed as the user-context
                    418: ** pointer when registering for an unlock-notify callback.
                    419: */
                    420: typedef struct UnlockNotification UnlockNotification;
                    421: struct UnlockNotification {
                    422:   int fired;                         /* True after unlock event has occurred */
                    423:   pthread_cond_t cond;               /* Condition variable to wait on */
                    424:   pthread_mutex_t mutex;             /* Mutex to protect structure */
                    425: };
                    426: 
                    427: /*
                    428: ** This function is an unlock-notify callback registered with SQLite.
                    429: */
                    430: static void unlock_notify_cb(void **apArg, int nArg){
                    431:   int i;
                    432:   for(i=0; i<nArg; i++){
                    433:     UnlockNotification *p = (UnlockNotification *)apArg[i];
                    434:     pthread_mutex_lock(&p->mutex);
                    435:     p->fired = 1;
                    436:     pthread_cond_signal(&p->cond);
                    437:     pthread_mutex_unlock(&p->mutex);
                    438:   }
                    439: }
                    440: 
                    441: /*
                    442: ** This function assumes that an SQLite API call (either sqlite3_prepare_v2() 
                    443: ** or sqlite3_step()) has just returned SQLITE_LOCKED. The argument is the
                    444: ** associated database connection.
                    445: **
                    446: ** This function calls sqlite3_unlock_notify() to register for an 
                    447: ** unlock-notify callback, then blocks until that callback is delivered 
                    448: ** and returns SQLITE_OK. The caller should then retry the failed operation.
                    449: **
                    450: ** Or, if sqlite3_unlock_notify() indicates that to block would deadlock 
                    451: ** the system, then this function returns SQLITE_LOCKED immediately. In 
                    452: ** this case the caller should not retry the operation and should roll 
                    453: ** back the current transaction (if any).
                    454: */
                    455: static int wait_for_unlock_notify(sqlite3 *db){
                    456:   int rc;
                    457:   UnlockNotification un;
                    458: 
                    459:   /* Initialize the UnlockNotification structure. */
                    460:   un.fired = 0;
                    461:   pthread_mutex_init(&un.mutex, 0);
                    462:   pthread_cond_init(&un.cond, 0);
                    463: 
                    464:   /* Register for an unlock-notify callback. */
                    465:   rc = sqlite3_unlock_notify(db, unlock_notify_cb, (void *)&un);
                    466:   assert( rc==SQLITE_LOCKED || rc==SQLITE_OK );
                    467: 
                    468:   /* The call to sqlite3_unlock_notify() always returns either SQLITE_LOCKED 
                    469:   ** or SQLITE_OK. 
                    470:   **
                    471:   ** If SQLITE_LOCKED was returned, then the system is deadlocked. In this
                    472:   ** case this function needs to return SQLITE_LOCKED to the caller so 
                    473:   ** that the current transaction can be rolled back. Otherwise, block
                    474:   ** until the unlock-notify callback is invoked, then return SQLITE_OK.
                    475:   */
                    476:   if( rc==SQLITE_OK ){
                    477:     pthread_mutex_lock(&un.mutex);
                    478:     if( !un.fired ){
                    479:       pthread_cond_wait(&un.cond, &un.mutex);
                    480:     }
                    481:     pthread_mutex_unlock(&un.mutex);
                    482:   }
                    483: 
                    484:   /* Destroy the mutex and condition variables. */
                    485:   pthread_cond_destroy(&un.cond);
                    486:   pthread_mutex_destroy(&un.mutex);
                    487: 
                    488:   return rc;
                    489: }
                    490: 
                    491: /*
                    492: ** This function is a wrapper around the SQLite function sqlite3_step().
                    493: ** It functions in the same way as step(), except that if a required
                    494: ** shared-cache lock cannot be obtained, this function may block waiting for
                    495: ** the lock to become available. In this scenario the normal API step()
                    496: ** function always returns SQLITE_LOCKED.
                    497: **
                    498: ** If this function returns SQLITE_LOCKED, the caller should rollback
                    499: ** the current transaction (if any) and try again later. Otherwise, the
                    500: ** system may become deadlocked.
                    501: */
                    502: int sqlite3_blocking_step(sqlite3_stmt *pStmt){
                    503:   int rc;
                    504:   while( SQLITE_LOCKED==(rc = sqlite3_step(pStmt)) ){
                    505:     rc = wait_for_unlock_notify(sqlite3_db_handle(pStmt));
                    506:     if( rc!=SQLITE_OK ) break;
                    507:     sqlite3_reset(pStmt);
                    508:   }
                    509:   return rc;
                    510: }
                    511: 
                    512: /*
                    513: ** This function is a wrapper around the SQLite function sqlite3_prepare_v2().
                    514: ** It functions in the same way as prepare_v2(), except that if a required
                    515: ** shared-cache lock cannot be obtained, this function may block waiting for
                    516: ** the lock to become available. In this scenario the normal API prepare_v2()
                    517: ** function always returns SQLITE_LOCKED.
                    518: **
                    519: ** If this function returns SQLITE_LOCKED, the caller should rollback
                    520: ** the current transaction (if any) and try again later. Otherwise, the
                    521: ** system may become deadlocked.
                    522: */
                    523: int sqlite3_blocking_prepare_v2(
                    524:   sqlite3 *db,              /* Database handle. */
                    525:   const char *zSql,         /* UTF-8 encoded SQL statement. */
                    526:   int nSql,                 /* Length of zSql in bytes. */
                    527:   sqlite3_stmt **ppStmt,    /* OUT: A pointer to the prepared statement */
                    528:   const char **pz           /* OUT: End of parsed string */
                    529: ){
                    530:   int rc;
                    531:   while( SQLITE_LOCKED==(rc = sqlite3_prepare_v2(db, zSql, nSql, ppStmt, pz)) ){
                    532:     rc = wait_for_unlock_notify(db);
                    533:     if( rc!=SQLITE_OK ) break;
                    534:   }
                    535:   return rc;
                    536: }
                    537: /* END_SQLITE_BLOCKING_STEP */
                    538: 
                    539: /*
                    540: ** Usage: sqlite3_blocking_step STMT
                    541: **
                    542: ** Advance the statement to the next row.
                    543: */
                    544: static int blocking_step_proc(
                    545:   void * clientData,
                    546:   Tcl_Interp *interp,
                    547:   int objc,
                    548:   Tcl_Obj *CONST objv[]
                    549: ){
                    550: 
                    551:   sqlite3_stmt *pStmt;
                    552:   int rc;
                    553: 
                    554:   if( objc!=2 ){
                    555:     Tcl_WrongNumArgs(interp, 1, objv, "STMT");
                    556:     return TCL_ERROR;
                    557:   }
                    558: 
                    559:   pStmt = (sqlite3_stmt*)sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
                    560:   rc = sqlite3_blocking_step(pStmt);
                    561: 
                    562:   Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), 0);
                    563:   return TCL_OK;
                    564: }
                    565: 
                    566: /*
                    567: ** Usage: sqlite3_blocking_prepare_v2 DB sql bytes ?tailvar?
                    568: ** Usage: sqlite3_nonblocking_prepare_v2 DB sql bytes ?tailvar?
                    569: */
                    570: static int blocking_prepare_v2_proc(
                    571:   void * clientData,
                    572:   Tcl_Interp *interp,
                    573:   int objc,
                    574:   Tcl_Obj *CONST objv[]
                    575: ){
                    576:   sqlite3 *db;
                    577:   const char *zSql;
                    578:   int bytes;
                    579:   const char *zTail = 0;
                    580:   sqlite3_stmt *pStmt = 0;
                    581:   char zBuf[50];
                    582:   int rc;
                    583:   int isBlocking = !(clientData==0);
                    584: 
                    585:   if( objc!=5 && objc!=4 ){
                    586:     Tcl_AppendResult(interp, "wrong # args: should be \"", 
                    587:        Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0);
                    588:     return TCL_ERROR;
                    589:   }
                    590:   if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
                    591:   zSql = Tcl_GetString(objv[2]);
                    592:   if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR;
                    593: 
                    594:   if( isBlocking ){
                    595:     rc = sqlite3_blocking_prepare_v2(db, zSql, bytes, &pStmt, &zTail);
                    596:   }else{
                    597:     rc = sqlite3_prepare_v2(db, zSql, bytes, &pStmt, &zTail);
                    598:   }
                    599: 
                    600:   assert(rc==SQLITE_OK || pStmt==0);
                    601:   if( zTail && objc>=5 ){
                    602:     if( bytes>=0 ){
                    603:       bytes = bytes - (zTail-zSql);
                    604:     }
                    605:     Tcl_ObjSetVar2(interp, objv[4], 0, Tcl_NewStringObj(zTail, bytes), 0);
                    606:   }
                    607:   if( rc!=SQLITE_OK ){
                    608:     assert( pStmt==0 );
                    609:     sprintf(zBuf, "%s ", (char *)sqlite3TestErrorName(rc));
                    610:     Tcl_AppendResult(interp, zBuf, sqlite3_errmsg(db), 0);
                    611:     return TCL_ERROR;
                    612:   }
                    613: 
                    614:   if( pStmt ){
                    615:     if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR;
                    616:     Tcl_AppendResult(interp, zBuf, 0);
                    617:   }
                    618:   return TCL_OK;
                    619: }
                    620: 
                    621: #endif /* SQLITE_OS_UNIX && SQLITE_ENABLE_UNLOCK_NOTIFY */
                    622: /*
                    623: ** End of implementation of [sqlite3_blocking_step].
                    624: ************************************************************************/
                    625: 
                    626: /*
                    627: ** Register commands with the TCL interpreter.
                    628: */
                    629: int SqlitetestThread_Init(Tcl_Interp *interp){
                    630:   Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, 0, 0);
                    631:   Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0);
                    632: #if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
                    633:   Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0);
                    634:   Tcl_CreateObjCommand(interp, 
                    635:       "sqlite3_blocking_prepare_v2", blocking_prepare_v2_proc, (void *)1, 0);
                    636:   Tcl_CreateObjCommand(interp, 
                    637:       "sqlite3_nonblocking_prepare_v2", blocking_prepare_v2_proc, 0, 0);
                    638: #endif
                    639:   return TCL_OK;
                    640: }
                    641: #else
                    642: int SqlitetestThread_Init(Tcl_Interp *interp){
                    643:   return TCL_OK;
                    644: }
                    645: #endif

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>