blob: 6a319fabf4e4e5f5122883a786469528ad58bf3e [file] [log] [blame]
danielk197744918fa2007-09-07 11:29:25 +00001/*
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.
danielk197744918fa2007-09-07 11:29:25 +000016*/
17
18#include "sqliteInt.h"
drhf78fbde2007-12-13 18:29:35 +000019#include <tcl.h>
danielk1977570f7e22007-09-07 18:40:38 +000020
drhb8613ab2009-01-19 17:40:12 +000021#if SQLITE_THREADSAFE
danielk197744918fa2007-09-07 11:29:25 +000022
danielk197744918fa2007-09-07 11:29:25 +000023#include <errno.h>
shane39070ed2009-02-03 19:55:20 +000024
25#if !defined(_MSC_VER)
danielk197744918fa2007-09-07 11:29:25 +000026#include <unistd.h>
shane39070ed2009-02-03 19:55:20 +000027#endif
danielk197744918fa2007-09-07 11:29:25 +000028
29/*
30** One of these is allocated for each thread created by [sqlthread spawn].
31*/
32typedef struct SqlThread SqlThread;
33struct SqlThread {
danielk1977570f7e22007-09-07 18:40:38 +000034 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 */
danielk197744918fa2007-09-07 11:29:25 +000038};
39
danielk1977570f7e22007-09-07 18:40:38 +000040/*
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*/
47typedef struct EvalEvent EvalEvent;
48struct 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. */
danielk197744918fa2007-09-07 11:29:25 +000052};
53
54static Tcl_ObjCmdProc sqlthread_proc;
danielk197781fa1932008-08-28 13:55:10 +000055static Tcl_ObjCmdProc clock_seconds_proc;
drh69910da2009-03-27 12:32:54 +000056#if defined(SQLITE_OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
danielk1977404ca072009-03-16 13:19:36 +000057static Tcl_ObjCmdProc blocking_step_proc;
danielk197765a2ea12009-03-19 07:58:31 +000058static Tcl_ObjCmdProc blocking_prepare_v2_proc;
drh69910da2009-03-27 12:32:54 +000059#endif
danielk1977570f7e22007-09-07 18:40:38 +000060int Sqlitetest1_Init(Tcl_Interp *);
danielk197744918fa2007-09-07 11:29:25 +000061
danielk197765a2ea12009-03-19 07:58:31 +000062/* Functions from test1.c */
63void *sqlite3TestTextToPtr(const char *);
64const char *sqlite3TestErrorName(int);
65int getDbPointer(Tcl_Interp *, const char *, sqlite3 **);
66int sqlite3TestMakePointerStr(Tcl_Interp *, char *, void *);
67int sqlite3TestErrCode(Tcl_Interp *, sqlite3 *, int);
68
danielk1977570f7e22007-09-07 18:40:38 +000069/*
70** Handler for events of type EvalEvent.
71*/
72static 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 }
shane39070ed2009-02-03 19:55:20 +000079 UNUSED_PARAMETER(flags);
danielk1977570f7e22007-09-07 18:40:38 +000080 return 1;
81}
82
83/*
84** Register an EvalEvent to evaluate the script pScript in the
85** parent interpreter/thread of SqlThread p.
86*/
87static 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*/
107static Tcl_ThreadCreateType tclScriptThread(ClientData pSqlThread){
danielk197744918fa2007-09-07 11:29:25 +0000108 Tcl_Interp *interp;
109 Tcl_Obj *pRes;
110 Tcl_Obj *pList;
danielk197744918fa2007-09-07 11:29:25 +0000111 int rc;
danielk197744918fa2007-09-07 11:29:25 +0000112 SqlThread *p = (SqlThread *)pSqlThread;
drhb8613ab2009-01-19 17:40:12 +0000113 extern int Sqlitetest_mutex_Init(Tcl_Interp*);
danielk197744918fa2007-09-07 11:29:25 +0000114
115 interp = Tcl_CreateInterp();
danielk197781fa1932008-08-28 13:55:10 +0000116 Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0);
danielk197744918fa2007-09-07 11:29:25 +0000117 Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, pSqlThread, 0);
drh72bcfa62009-03-24 18:42:16 +0000118#if defined(SQLITE_OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
danielk1977404ca072009-03-16 13:19:36 +0000119 Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0);
danielk1977a8bbef82009-03-23 17:11:26 +0000120 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);
danielk1977404ca072009-03-16 13:19:36 +0000124#endif
danielk197744918fa2007-09-07 11:29:25 +0000125 Sqlitetest1_Init(interp);
drhb8613ab2009-01-19 17:40:12 +0000126 Sqlitetest_mutex_Init(interp);
danielk197744918fa2007-09-07 11:29:25 +0000127
128 rc = Tcl_Eval(interp, p->zScript);
129 pRes = Tcl_GetObjResult(interp);
130 pList = Tcl_NewObj();
131 Tcl_IncrRefCount(pList);
danielk1977570f7e22007-09-07 18:40:38 +0000132 Tcl_IncrRefCount(pRes);
danielk197744918fa2007-09-07 11:29:25 +0000133
danielk1977d9b5b112007-09-10 06:23:53 +0000134 if( rc!=TCL_OK ){
danielk197744918fa2007-09-07 11:29:25 +0000135 Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj("error", -1));
danielk1977d9b5b112007-09-10 06:23:53 +0000136 Tcl_ListObjAppendElement(interp, pList, pRes);
137 postToParent(p, pList);
138 Tcl_DecrRefCount(pList);
139 pList = Tcl_NewObj();
danielk197744918fa2007-09-07 11:29:25 +0000140 }
danielk197744918fa2007-09-07 11:29:25 +0000141
danielk1977d9b5b112007-09-10 06:23:53 +0000142 Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj("set", -1));
143 Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj(p->zVarname, -1));
144 Tcl_ListObjAppendElement(interp, pList, pRes);
danielk1977570f7e22007-09-07 18:40:38 +0000145 postToParent(p, pList);
146
147 ckfree((void *)p);
danielk197744918fa2007-09-07 11:29:25 +0000148 Tcl_DecrRefCount(pList);
danielk1977570f7e22007-09-07 18:40:38 +0000149 Tcl_DecrRefCount(pRes);
danielk197744918fa2007-09-07 11:29:25 +0000150 Tcl_DeleteInterp(interp);
shane39070ed2009-02-03 19:55:20 +0000151 TCL_THREAD_CREATE_RETURN;
danielk197744918fa2007-09-07 11:29:25 +0000152}
153
154/*
155** sqlthread spawn VARNAME SCRIPT
156**
drh85b623f2007-12-13 21:54:09 +0000157** Spawn a new thread with its own Tcl interpreter and run the
danielk197744918fa2007-09-07 11:29:25 +0000158** 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*/
164static int sqlthread_spawn(
165 ClientData clientData,
166 Tcl_Interp *interp,
167 int objc,
168 Tcl_Obj *CONST objv[]
169){
danielk1977570f7e22007-09-07 18:40:38 +0000170 Tcl_ThreadId x;
danielk197744918fa2007-09-07 11:29:25 +0000171 SqlThread *pNew;
danielk197744918fa2007-09-07 11:29:25 +0000172 int rc;
173
174 int nVarname; char *zVarname;
175 int nScript; char *zScript;
176
danielk1977570f7e22007-09-07 18:40:38 +0000177 /* Parameters for thread creation */
178 const int nStack = TCL_THREAD_STACK_DEFAULT;
179 const int flags = TCL_THREAD_NOFLAGS;
180
danielk197744918fa2007-09-07 11:29:25 +0000181 assert(objc==4);
shane39070ed2009-02-03 19:55:20 +0000182 UNUSED_PARAMETER(clientData);
183 UNUSED_PARAMETER(objc);
danielk197744918fa2007-09-07 11:29:25 +0000184
185 zVarname = Tcl_GetStringFromObj(objv[2], &nVarname);
186 zScript = Tcl_GetStringFromObj(objv[3], &nScript);
danielk1977570f7e22007-09-07 18:40:38 +0000187
188 pNew = (SqlThread *)ckalloc(sizeof(SqlThread)+nVarname+nScript+2);
danielk197744918fa2007-09-07 11:29:25 +0000189 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);
danielk1977570f7e22007-09-07 18:40:38 +0000193 pNew->parent = Tcl_GetCurrentThread();
194 pNew->interp = interp;
danielk197744918fa2007-09-07 11:29:25 +0000195
danielk1977570f7e22007-09-07 18:40:38 +0000196 rc = Tcl_CreateThread(&x, tclScriptThread, (void *)pNew, nStack, flags);
197 if( rc!=TCL_OK ){
198 Tcl_AppendResult(interp, "Error in Tcl_CreateThread()", 0);
danielk197781fa1932008-08-28 13:55:10 +0000199 ckfree((char *)pNew);
danielk197744918fa2007-09-07 11:29:25 +0000200 return TCL_ERROR;
201 }
202
danielk197744918fa2007-09-07 11:29:25 +0000203 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*/
danielk197744918fa2007-09-07 11:29:25 +0000217static int sqlthread_parent(
218 ClientData clientData,
219 Tcl_Interp *interp,
220 int objc,
221 Tcl_Obj *CONST objv[]
222){
danielk1977570f7e22007-09-07 18:40:38 +0000223 EvalEvent *pEvent;
danielk197744918fa2007-09-07 11:29:25 +0000224 char *zMsg;
225 int nMsg;
226 SqlThread *p = (SqlThread *)clientData;
227
228 assert(objc==3);
shane39070ed2009-02-03 19:55:20 +0000229 UNUSED_PARAMETER(objc);
230
danielk197744918fa2007-09-07 11:29:25 +0000231 if( p==0 ){
232 Tcl_AppendResult(interp, "no parent thread", 0);
233 return TCL_ERROR;
234 }
235
236 zMsg = Tcl_GetStringFromObj(objv[2], &nMsg);
danielk1977570f7e22007-09-07 18:40:38 +0000237 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);
danielk197744918fa2007-09-07 11:29:25 +0000245
246 return TCL_OK;
247}
danielk197744918fa2007-09-07 11:29:25 +0000248
danielk1977d9b5b112007-09-10 06:23:53 +0000249static int xBusy(void *pArg, int nBusy){
shane39070ed2009-02-03 19:55:20 +0000250 UNUSED_PARAMETER(pArg);
251 UNUSED_PARAMETER(nBusy);
danielk1977d9b5b112007-09-10 06:23:53 +0000252 sqlite3_sleep(50);
253 return 1; /* Try again... */
254}
255
danielk1977e9dcd5e2007-09-10 10:53:01 +0000256/*
257** sqlthread open
258**
259** Open a database handle and return the string representation of
260** the pointer value.
261*/
danielk1977d9b5b112007-09-10 06:23:53 +0000262static 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
shane39070ed2009-02-03 19:55:20 +0000276 UNUSED_PARAMETER(clientData);
277 UNUSED_PARAMETER(objc);
278
danielk1977d9b5b112007-09-10 06:23:53 +0000279 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
danielk197744918fa2007-09-07 11:29:25 +0000291/*
danielk1977e9dcd5e2007-09-10 10:53:01 +0000292** sqlthread open
293**
294** Return the current thread-id (Tcl_GetCurrentThread()) cast to
295** an integer.
296*/
297static 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));
shane39070ed2009-02-03 19:55:20 +0000305 UNUSED_PARAMETER(clientData);
306 UNUSED_PARAMETER(objc);
307 UNUSED_PARAMETER(objv);
danielk1977e9dcd5e2007-09-10 10:53:01 +0000308 return TCL_OK;
309}
310
311
312/*
danielk197744918fa2007-09-07 11:29:25 +0000313** Dispatch routine for the sub-commands of [sqlthread].
314*/
315static 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[] = {
danielk197744918fa2007-09-07 11:29:25 +0000327 {"parent", sqlthread_parent, 1, "SCRIPT"},
danielk197744918fa2007-09-07 11:29:25 +0000328 {"spawn", sqlthread_spawn, 2, "VARNAME SCRIPT"},
danielk1977d9b5b112007-09-10 06:23:53 +0000329 {"open", sqlthread_open, 1, "DBNAME"},
danielk1977e9dcd5e2007-09-10 10:53:01 +0000330 {"id", sqlthread_id, 0, ""},
danielk197744918fa2007-09-07 11:29:25 +0000331 {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(
danielk1977570f7e22007-09-07 18:40:38 +0000343 interp, objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iIndex
danielk197744918fa2007-09-07 11:29:25 +0000344 );
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/*
danielk197781fa1932008-08-28 13:55:10 +0000357** 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*/
363static 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));
shane39070ed2009-02-03 19:55:20 +0000372 UNUSED_PARAMETER(clientData);
373 UNUSED_PARAMETER(objc);
374 UNUSED_PARAMETER(objv);
danielk197781fa1932008-08-28 13:55:10 +0000375 return TCL_OK;
376}
377
danielk1977404ca072009-03-16 13:19:36 +0000378/*************************************************************************
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*/
drh72bcfa62009-03-24 18:42:16 +0000391#if defined(SQLITE_OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
danielk1977404ca072009-03-16 13:19:36 +0000392
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*/
401typedef struct UnlockNotification UnlockNotification;
402struct 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*/
danielk197765a2ea12009-03-19 07:58:31 +0000411static void unlock_notify_cb(void **apArg, int nArg){
danielk1977404ca072009-03-16 13:19:36 +0000412 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/*
danielk197765a2ea12009-03-19 07:58:31 +0000423** 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*/
436static 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/*
danielk1977404ca072009-03-16 13:19:36 +0000473** 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*/
483int sqlite3_blocking_step(sqlite3_stmt *pStmt){
danielk197765a2ea12009-03-19 07:58:31 +0000484 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);
danielk1977404ca072009-03-16 13:19:36 +0000489 }
danielk197765a2ea12009-03-19 07:58:31 +0000490 return rc;
491}
danielk1977404ca072009-03-16 13:19:36 +0000492
danielk197765a2ea12009-03-19 07:58:31 +0000493/*
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*/
504int 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 }
danielk1977404ca072009-03-16 13:19:36 +0000516 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*/
525static int blocking_step_proc(
526 void * clientData,
527 Tcl_Interp *interp,
528 int objc,
529 Tcl_Obj *CONST objv[]
530){
danielk1977404ca072009-03-16 13:19:36 +0000531
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
danielk197765a2ea12009-03-19 07:58:31 +0000547/*
548** Usage: sqlite3_blocking_prepare_v2 DB sql bytes ?tailvar?
danielk1977a8bbef82009-03-23 17:11:26 +0000549** Usage: sqlite3_nonblocking_prepare_v2 DB sql bytes ?tailvar?
danielk197765a2ea12009-03-19 07:58:31 +0000550*/
551static 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;
danielk1977a8bbef82009-03-23 17:11:26 +0000564 int isBlocking = !(clientData==0);
danielk197765a2ea12009-03-19 07:58:31 +0000565
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
danielk1977a8bbef82009-03-23 17:11:26 +0000575 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
danielk197765a2ea12009-03-19 07:58:31 +0000581 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
drh69910da2009-03-27 12:32:54 +0000602#endif /* SQLITE_OS_UNIX && SQLITE_ENABLE_UNLOCK_NOTIFY */
danielk1977404ca072009-03-16 13:19:36 +0000603/*
604** End of implementation of [sqlite3_blocking_step].
605************************************************************************/
606
danielk197781fa1932008-08-28 13:55:10 +0000607/*
danielk197744918fa2007-09-07 11:29:25 +0000608** Register commands with the TCL interpreter.
609*/
610int SqlitetestThread_Init(Tcl_Interp *interp){
611 Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, 0, 0);
danielk197781fa1932008-08-28 13:55:10 +0000612 Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0);
drh72bcfa62009-03-24 18:42:16 +0000613#if defined(SQLITE_OS_UNIX) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
danielk1977404ca072009-03-16 13:19:36 +0000614 Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0);
danielk1977a8bbef82009-03-23 17:11:26 +0000615 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);
danielk1977404ca072009-03-16 13:19:36 +0000619#endif
danielk197744918fa2007-09-07 11:29:25 +0000620 return TCL_OK;
621}
622#else
623int SqlitetestThread_Init(Tcl_Interp *interp){
624 return TCL_OK;
625}
626#endif