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