144918fa0Sdanielk1977 /*
244918fa0Sdanielk1977 ** 2007 September 9
344918fa0Sdanielk1977 **
444918fa0Sdanielk1977 ** The author disclaims copyright to this source code. In place of
544918fa0Sdanielk1977 ** a legal notice, here is a blessing:
644918fa0Sdanielk1977 **
744918fa0Sdanielk1977 ** May you do good and not evil.
844918fa0Sdanielk1977 ** May you find forgiveness for yourself and forgive others.
944918fa0Sdanielk1977 ** May you share freely, never taking more than you give.
1044918fa0Sdanielk1977 **
1144918fa0Sdanielk1977 *************************************************************************
1244918fa0Sdanielk1977 **
1344918fa0Sdanielk1977 ** This file contains the implementation of some Tcl commands used to
1444918fa0Sdanielk1977 ** test that sqlite3 database handles may be concurrently accessed by
1544918fa0Sdanielk1977 ** multiple threads. Right now this only works on unix.
1644918fa0Sdanielk1977 */
1744918fa0Sdanielk1977
1844918fa0Sdanielk1977 #include "sqliteInt.h"
1952b1dbb5Smistachkin #if defined(INCLUDE_SQLITE_TCL_H)
2052b1dbb5Smistachkin # include "sqlite_tcl.h"
2152b1dbb5Smistachkin #else
2252b1dbb5Smistachkin # include "tcl.h"
2352b1dbb5Smistachkin #endif
24570f7e25Sdanielk1977
25b8613ab1Sdrh #if SQLITE_THREADSAFE
2644918fa0Sdanielk1977
2744918fa0Sdanielk1977 #include <errno.h>
2839070edbSshane
2939070edbSshane #if !defined(_MSC_VER)
3044918fa0Sdanielk1977 #include <unistd.h>
3139070edbSshane #endif
3244918fa0Sdanielk1977
3344918fa0Sdanielk1977 /*
3444918fa0Sdanielk1977 ** One of these is allocated for each thread created by [sqlthread spawn].
3544918fa0Sdanielk1977 */
3644918fa0Sdanielk1977 typedef struct SqlThread SqlThread;
3744918fa0Sdanielk1977 struct SqlThread {
38570f7e25Sdanielk1977 Tcl_ThreadId parent; /* Thread id of parent thread */
39570f7e25Sdanielk1977 Tcl_Interp *interp; /* Parent interpreter */
4044918fa0Sdanielk1977 char *zScript; /* The script to execute. */
4144918fa0Sdanielk1977 char *zVarname; /* Varname in parent script */
4244918fa0Sdanielk1977 };
4344918fa0Sdanielk1977
44570f7e25Sdanielk1977 /*
45570f7e25Sdanielk1977 ** A custom Tcl_Event type used by this module. When the event is
46570f7e25Sdanielk1977 ** handled, script zScript is evaluated in interpreter interp. If
47570f7e25Sdanielk1977 ** the evaluation throws an exception (returns TCL_ERROR), then the
48570f7e25Sdanielk1977 ** error is handled by Tcl_BackgroundError(). If no error occurs,
49570f7e25Sdanielk1977 ** the result is simply discarded.
50570f7e25Sdanielk1977 */
51570f7e25Sdanielk1977 typedef struct EvalEvent EvalEvent;
52570f7e25Sdanielk1977 struct EvalEvent {
53570f7e25Sdanielk1977 Tcl_Event base; /* Base class of type Tcl_Event */
54570f7e25Sdanielk1977 char *zScript; /* The script to execute. */
55570f7e25Sdanielk1977 Tcl_Interp *interp; /* The interpreter to execute it in. */
5644918fa0Sdanielk1977 };
5744918fa0Sdanielk1977
5844918fa0Sdanielk1977 static Tcl_ObjCmdProc sqlthread_proc;
5981fa193aSdanielk1977 static Tcl_ObjCmdProc clock_seconds_proc;
603a2d29f8Sshaneh #if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
61404ca075Sdanielk1977 static Tcl_ObjCmdProc blocking_step_proc;
6265a2ea11Sdanielk1977 static Tcl_ObjCmdProc blocking_prepare_v2_proc;
6369910da9Sdrh #endif
64570f7e25Sdanielk1977 int Sqlitetest1_Init(Tcl_Interp *);
655e0ce87aSdan int Sqlite3_Init(Tcl_Interp *);
6644918fa0Sdanielk1977
67e84d8d32Smistachkin /* Functions from main.c */
68e84d8d32Smistachkin extern const char *sqlite3ErrName(int);
69e84d8d32Smistachkin
7065a2ea11Sdanielk1977 /* Functions from test1.c */
71e84d8d32Smistachkin extern void *sqlite3TestTextToPtr(const char *);
72e84d8d32Smistachkin extern int getDbPointer(Tcl_Interp *, const char *, sqlite3 **);
73e84d8d32Smistachkin extern int sqlite3TestMakePointerStr(Tcl_Interp *, char *, void *);
74e84d8d32Smistachkin extern int sqlite3TestErrCode(Tcl_Interp *, sqlite3 *, int);
7565a2ea11Sdanielk1977
76570f7e25Sdanielk1977 /*
77570f7e25Sdanielk1977 ** Handler for events of type EvalEvent.
78570f7e25Sdanielk1977 */
tclScriptEvent(Tcl_Event * evPtr,int flags)797617e4a8Smistachkin static int SQLITE_TCLAPI tclScriptEvent(Tcl_Event *evPtr, int flags){
80570f7e25Sdanielk1977 int rc;
81570f7e25Sdanielk1977 EvalEvent *p = (EvalEvent *)evPtr;
82570f7e25Sdanielk1977 rc = Tcl_Eval(p->interp, p->zScript);
83570f7e25Sdanielk1977 if( rc!=TCL_OK ){
84570f7e25Sdanielk1977 Tcl_BackgroundError(p->interp);
85570f7e25Sdanielk1977 }
8639070edbSshane UNUSED_PARAMETER(flags);
87570f7e25Sdanielk1977 return 1;
88570f7e25Sdanielk1977 }
89570f7e25Sdanielk1977
90570f7e25Sdanielk1977 /*
91570f7e25Sdanielk1977 ** Register an EvalEvent to evaluate the script pScript in the
92570f7e25Sdanielk1977 ** parent interpreter/thread of SqlThread p.
93570f7e25Sdanielk1977 */
postToParent(SqlThread * p,Tcl_Obj * pScript)94570f7e25Sdanielk1977 static void postToParent(SqlThread *p, Tcl_Obj *pScript){
95570f7e25Sdanielk1977 EvalEvent *pEvent;
96570f7e25Sdanielk1977 char *zMsg;
97570f7e25Sdanielk1977 int nMsg;
98570f7e25Sdanielk1977
99570f7e25Sdanielk1977 zMsg = Tcl_GetStringFromObj(pScript, &nMsg);
100570f7e25Sdanielk1977 pEvent = (EvalEvent *)ckalloc(sizeof(EvalEvent)+nMsg+1);
101570f7e25Sdanielk1977 pEvent->base.nextPtr = 0;
102570f7e25Sdanielk1977 pEvent->base.proc = tclScriptEvent;
103570f7e25Sdanielk1977 pEvent->zScript = (char *)&pEvent[1];
104570f7e25Sdanielk1977 memcpy(pEvent->zScript, zMsg, nMsg+1);
105570f7e25Sdanielk1977 pEvent->interp = p->interp;
106570f7e25Sdanielk1977
107570f7e25Sdanielk1977 Tcl_ThreadQueueEvent(p->parent, (Tcl_Event *)pEvent, TCL_QUEUE_TAIL);
108570f7e25Sdanielk1977 Tcl_ThreadAlert(p->parent);
109570f7e25Sdanielk1977 }
110570f7e25Sdanielk1977
111570f7e25Sdanielk1977 /*
112570f7e25Sdanielk1977 ** The main function for threads created with [sqlthread spawn].
113570f7e25Sdanielk1977 */
tclScriptThread(ClientData pSqlThread)114570f7e25Sdanielk1977 static Tcl_ThreadCreateType tclScriptThread(ClientData pSqlThread){
11544918fa0Sdanielk1977 Tcl_Interp *interp;
11644918fa0Sdanielk1977 Tcl_Obj *pRes;
11744918fa0Sdanielk1977 Tcl_Obj *pList;
11844918fa0Sdanielk1977 int rc;
11944918fa0Sdanielk1977 SqlThread *p = (SqlThread *)pSqlThread;
120b8613ab1Sdrh extern int Sqlitetest_mutex_Init(Tcl_Interp*);
12144918fa0Sdanielk1977
12244918fa0Sdanielk1977 interp = Tcl_CreateInterp();
12381fa193aSdanielk1977 Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0);
12444918fa0Sdanielk1977 Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, pSqlThread, 0);
1253a2d29f8Sshaneh #if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
126404ca075Sdanielk1977 Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0);
127a8bbef84Sdanielk1977 Tcl_CreateObjCommand(interp,
128a8bbef84Sdanielk1977 "sqlite3_blocking_prepare_v2", blocking_prepare_v2_proc, (void *)1, 0);
129a8bbef84Sdanielk1977 Tcl_CreateObjCommand(interp,
130a8bbef84Sdanielk1977 "sqlite3_nonblocking_prepare_v2", blocking_prepare_v2_proc, 0, 0);
131404ca075Sdanielk1977 #endif
13244918fa0Sdanielk1977 Sqlitetest1_Init(interp);
133b8613ab1Sdrh Sqlitetest_mutex_Init(interp);
1345e0ce87aSdan Sqlite3_Init(interp);
13544918fa0Sdanielk1977
13644918fa0Sdanielk1977 rc = Tcl_Eval(interp, p->zScript);
13744918fa0Sdanielk1977 pRes = Tcl_GetObjResult(interp);
13844918fa0Sdanielk1977 pList = Tcl_NewObj();
13944918fa0Sdanielk1977 Tcl_IncrRefCount(pList);
140570f7e25Sdanielk1977 Tcl_IncrRefCount(pRes);
14144918fa0Sdanielk1977
142d9b5b117Sdanielk1977 if( rc!=TCL_OK ){
143d9b5b117Sdanielk1977 Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj("error", -1));
144d9b5b117Sdanielk1977 Tcl_ListObjAppendElement(interp, pList, pRes);
145d9b5b117Sdanielk1977 postToParent(p, pList);
146d9b5b117Sdanielk1977 Tcl_DecrRefCount(pList);
147d9b5b117Sdanielk1977 pList = Tcl_NewObj();
148d9b5b117Sdanielk1977 }
149d9b5b117Sdanielk1977
15044918fa0Sdanielk1977 Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj("set", -1));
15144918fa0Sdanielk1977 Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj(p->zVarname, -1));
15244918fa0Sdanielk1977 Tcl_ListObjAppendElement(interp, pList, pRes);
153570f7e25Sdanielk1977 postToParent(p, pList);
154570f7e25Sdanielk1977
155570f7e25Sdanielk1977 ckfree((void *)p);
15644918fa0Sdanielk1977 Tcl_DecrRefCount(pList);
157570f7e25Sdanielk1977 Tcl_DecrRefCount(pRes);
15844918fa0Sdanielk1977 Tcl_DeleteInterp(interp);
159e1a2a990Sdan while( Tcl_DoOneEvent(TCL_ALL_EVENTS|TCL_DONT_WAIT) );
1603374f8aeSdan Tcl_ExitThread(0);
16139070edbSshane TCL_THREAD_CREATE_RETURN;
16244918fa0Sdanielk1977 }
16344918fa0Sdanielk1977
16444918fa0Sdanielk1977 /*
16544918fa0Sdanielk1977 ** sqlthread spawn VARNAME SCRIPT
16644918fa0Sdanielk1977 **
16785b623f2Sdrh ** Spawn a new thread with its own Tcl interpreter and run the
16844918fa0Sdanielk1977 ** specified SCRIPT(s) in it. The thread terminates after running
16944918fa0Sdanielk1977 ** the script. The result of the script is stored in the variable
17044918fa0Sdanielk1977 ** VARNAME.
17144918fa0Sdanielk1977 **
17244918fa0Sdanielk1977 ** The caller can wait for the script to terminate using [vwait VARNAME].
17344918fa0Sdanielk1977 */
sqlthread_spawn(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])1747617e4a8Smistachkin static int SQLITE_TCLAPI sqlthread_spawn(
17544918fa0Sdanielk1977 ClientData clientData,
17644918fa0Sdanielk1977 Tcl_Interp *interp,
17744918fa0Sdanielk1977 int objc,
17844918fa0Sdanielk1977 Tcl_Obj *CONST objv[]
17944918fa0Sdanielk1977 ){
180570f7e25Sdanielk1977 Tcl_ThreadId x;
18144918fa0Sdanielk1977 SqlThread *pNew;
18244918fa0Sdanielk1977 int rc;
18344918fa0Sdanielk1977
18444918fa0Sdanielk1977 int nVarname; char *zVarname;
18544918fa0Sdanielk1977 int nScript; char *zScript;
18644918fa0Sdanielk1977
187570f7e25Sdanielk1977 /* Parameters for thread creation */
188570f7e25Sdanielk1977 const int nStack = TCL_THREAD_STACK_DEFAULT;
189570f7e25Sdanielk1977 const int flags = TCL_THREAD_NOFLAGS;
190570f7e25Sdanielk1977
19144918fa0Sdanielk1977 assert(objc==4);
19239070edbSshane UNUSED_PARAMETER(clientData);
19339070edbSshane UNUSED_PARAMETER(objc);
19444918fa0Sdanielk1977
19544918fa0Sdanielk1977 zVarname = Tcl_GetStringFromObj(objv[2], &nVarname);
19644918fa0Sdanielk1977 zScript = Tcl_GetStringFromObj(objv[3], &nScript);
197570f7e25Sdanielk1977
198570f7e25Sdanielk1977 pNew = (SqlThread *)ckalloc(sizeof(SqlThread)+nVarname+nScript+2);
19944918fa0Sdanielk1977 pNew->zVarname = (char *)&pNew[1];
20044918fa0Sdanielk1977 pNew->zScript = (char *)&pNew->zVarname[nVarname+1];
20144918fa0Sdanielk1977 memcpy(pNew->zVarname, zVarname, nVarname+1);
20244918fa0Sdanielk1977 memcpy(pNew->zScript, zScript, nScript+1);
203570f7e25Sdanielk1977 pNew->parent = Tcl_GetCurrentThread();
204570f7e25Sdanielk1977 pNew->interp = interp;
20544918fa0Sdanielk1977
206570f7e25Sdanielk1977 rc = Tcl_CreateThread(&x, tclScriptThread, (void *)pNew, nStack, flags);
207570f7e25Sdanielk1977 if( rc!=TCL_OK ){
208570f7e25Sdanielk1977 Tcl_AppendResult(interp, "Error in Tcl_CreateThread()", 0);
20981fa193aSdanielk1977 ckfree((char *)pNew);
21044918fa0Sdanielk1977 return TCL_ERROR;
21144918fa0Sdanielk1977 }
21244918fa0Sdanielk1977
21344918fa0Sdanielk1977 return TCL_OK;
21444918fa0Sdanielk1977 }
21544918fa0Sdanielk1977
21644918fa0Sdanielk1977 /*
21744918fa0Sdanielk1977 ** sqlthread parent SCRIPT
21844918fa0Sdanielk1977 **
21944918fa0Sdanielk1977 ** This can be called by spawned threads only. It sends the specified
22044918fa0Sdanielk1977 ** script back to the parent thread for execution. The result of
22144918fa0Sdanielk1977 ** evaluating the SCRIPT is returned. The parent thread must enter
22244918fa0Sdanielk1977 ** the event loop for this to work - otherwise the caller will
22344918fa0Sdanielk1977 ** block indefinitely.
22444918fa0Sdanielk1977 **
22544918fa0Sdanielk1977 ** NOTE: At the moment, this doesn't work. FIXME.
22644918fa0Sdanielk1977 */
sqlthread_parent(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])2277617e4a8Smistachkin static int SQLITE_TCLAPI sqlthread_parent(
22844918fa0Sdanielk1977 ClientData clientData,
22944918fa0Sdanielk1977 Tcl_Interp *interp,
23044918fa0Sdanielk1977 int objc,
23144918fa0Sdanielk1977 Tcl_Obj *CONST objv[]
23244918fa0Sdanielk1977 ){
233570f7e25Sdanielk1977 EvalEvent *pEvent;
23444918fa0Sdanielk1977 char *zMsg;
23544918fa0Sdanielk1977 int nMsg;
23644918fa0Sdanielk1977 SqlThread *p = (SqlThread *)clientData;
23744918fa0Sdanielk1977
23844918fa0Sdanielk1977 assert(objc==3);
23939070edbSshane UNUSED_PARAMETER(objc);
24039070edbSshane
24144918fa0Sdanielk1977 if( p==0 ){
24244918fa0Sdanielk1977 Tcl_AppendResult(interp, "no parent thread", 0);
24344918fa0Sdanielk1977 return TCL_ERROR;
24444918fa0Sdanielk1977 }
24544918fa0Sdanielk1977
24644918fa0Sdanielk1977 zMsg = Tcl_GetStringFromObj(objv[2], &nMsg);
247570f7e25Sdanielk1977 pEvent = (EvalEvent *)ckalloc(sizeof(EvalEvent)+nMsg+1);
248570f7e25Sdanielk1977 pEvent->base.nextPtr = 0;
249570f7e25Sdanielk1977 pEvent->base.proc = tclScriptEvent;
250570f7e25Sdanielk1977 pEvent->zScript = (char *)&pEvent[1];
251570f7e25Sdanielk1977 memcpy(pEvent->zScript, zMsg, nMsg+1);
252570f7e25Sdanielk1977 pEvent->interp = p->interp;
253570f7e25Sdanielk1977 Tcl_ThreadQueueEvent(p->parent, (Tcl_Event *)pEvent, TCL_QUEUE_TAIL);
254570f7e25Sdanielk1977 Tcl_ThreadAlert(p->parent);
25544918fa0Sdanielk1977
25644918fa0Sdanielk1977 return TCL_OK;
25744918fa0Sdanielk1977 }
25844918fa0Sdanielk1977
xBusy(void * pArg,int nBusy)259d9b5b117Sdanielk1977 static int xBusy(void *pArg, int nBusy){
26039070edbSshane UNUSED_PARAMETER(pArg);
26139070edbSshane UNUSED_PARAMETER(nBusy);
262d9b5b117Sdanielk1977 sqlite3_sleep(50);
263d9b5b117Sdanielk1977 return 1; /* Try again... */
264d9b5b117Sdanielk1977 }
265d9b5b117Sdanielk1977
266e9dcd5e6Sdanielk1977 /*
267e9dcd5e6Sdanielk1977 ** sqlthread open
268e9dcd5e6Sdanielk1977 **
269e9dcd5e6Sdanielk1977 ** Open a database handle and return the string representation of
270e9dcd5e6Sdanielk1977 ** the pointer value.
271e9dcd5e6Sdanielk1977 */
sqlthread_open(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])2727617e4a8Smistachkin static int SQLITE_TCLAPI sqlthread_open(
273d9b5b117Sdanielk1977 ClientData clientData,
274d9b5b117Sdanielk1977 Tcl_Interp *interp,
275d9b5b117Sdanielk1977 int objc,
276d9b5b117Sdanielk1977 Tcl_Obj *CONST objv[]
277d9b5b117Sdanielk1977 ){
278d9b5b117Sdanielk1977 int sqlite3TestMakePointerStr(Tcl_Interp *interp, char *zPtr, void *p);
279d9b5b117Sdanielk1977
280d9b5b117Sdanielk1977 const char *zFilename;
281d9b5b117Sdanielk1977 sqlite3 *db;
282d9b5b117Sdanielk1977 char zBuf[100];
283*85bd982bSmistachkin extern int Md5_Register(sqlite3*,char**,const sqlite3_api_routines*);
284d9b5b117Sdanielk1977
28539070edbSshane UNUSED_PARAMETER(clientData);
28639070edbSshane UNUSED_PARAMETER(objc);
28739070edbSshane
288d9b5b117Sdanielk1977 zFilename = Tcl_GetString(objv[2]);
289caffb1a5Sdrh sqlite3_open(zFilename, &db);
29044e95d4fSmistachkin Md5_Register(db, 0, 0);
291d9b5b117Sdanielk1977 sqlite3_busy_handler(db, xBusy, 0);
292d9b5b117Sdanielk1977
293d9b5b117Sdanielk1977 if( sqlite3TestMakePointerStr(interp, zBuf, db) ) return TCL_ERROR;
294d9b5b117Sdanielk1977 Tcl_AppendResult(interp, zBuf, 0);
295d9b5b117Sdanielk1977
296d9b5b117Sdanielk1977 return TCL_OK;
297d9b5b117Sdanielk1977 }
298d9b5b117Sdanielk1977
299d9b5b117Sdanielk1977
30044918fa0Sdanielk1977 /*
301e9dcd5e6Sdanielk1977 ** sqlthread open
302e9dcd5e6Sdanielk1977 **
303e9dcd5e6Sdanielk1977 ** Return the current thread-id (Tcl_GetCurrentThread()) cast to
304e9dcd5e6Sdanielk1977 ** an integer.
305e9dcd5e6Sdanielk1977 */
sqlthread_id(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])3067617e4a8Smistachkin static int SQLITE_TCLAPI sqlthread_id(
307e9dcd5e6Sdanielk1977 ClientData clientData,
308e9dcd5e6Sdanielk1977 Tcl_Interp *interp,
309e9dcd5e6Sdanielk1977 int objc,
310e9dcd5e6Sdanielk1977 Tcl_Obj *CONST objv[]
311e9dcd5e6Sdanielk1977 ){
312e9dcd5e6Sdanielk1977 Tcl_ThreadId id = Tcl_GetCurrentThread();
313860e332cSdrh Tcl_SetObjResult(interp, Tcl_NewIntObj(SQLITE_PTR_TO_INT(id)));
31439070edbSshane UNUSED_PARAMETER(clientData);
31539070edbSshane UNUSED_PARAMETER(objc);
31639070edbSshane UNUSED_PARAMETER(objv);
317e9dcd5e6Sdanielk1977 return TCL_OK;
318e9dcd5e6Sdanielk1977 }
319e9dcd5e6Sdanielk1977
320e9dcd5e6Sdanielk1977
321e9dcd5e6Sdanielk1977 /*
32244918fa0Sdanielk1977 ** Dispatch routine for the sub-commands of [sqlthread].
32344918fa0Sdanielk1977 */
sqlthread_proc(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])3247617e4a8Smistachkin static int SQLITE_TCLAPI sqlthread_proc(
32544918fa0Sdanielk1977 ClientData clientData,
32644918fa0Sdanielk1977 Tcl_Interp *interp,
32744918fa0Sdanielk1977 int objc,
32844918fa0Sdanielk1977 Tcl_Obj *CONST objv[]
32944918fa0Sdanielk1977 ){
33044918fa0Sdanielk1977 struct SubCommand {
33144918fa0Sdanielk1977 char *zName;
33244918fa0Sdanielk1977 Tcl_ObjCmdProc *xProc;
33344918fa0Sdanielk1977 int nArg;
33444918fa0Sdanielk1977 char *zUsage;
33544918fa0Sdanielk1977 } aSub[] = {
33644918fa0Sdanielk1977 {"parent", sqlthread_parent, 1, "SCRIPT"},
33744918fa0Sdanielk1977 {"spawn", sqlthread_spawn, 2, "VARNAME SCRIPT"},
338d9b5b117Sdanielk1977 {"open", sqlthread_open, 1, "DBNAME"},
339e9dcd5e6Sdanielk1977 {"id", sqlthread_id, 0, ""},
34044918fa0Sdanielk1977 {0, 0, 0}
34144918fa0Sdanielk1977 };
34244918fa0Sdanielk1977 struct SubCommand *pSub;
34344918fa0Sdanielk1977 int rc;
34444918fa0Sdanielk1977 int iIndex;
34544918fa0Sdanielk1977
34644918fa0Sdanielk1977 if( objc<2 ){
34744918fa0Sdanielk1977 Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND");
34844918fa0Sdanielk1977 return TCL_ERROR;
34944918fa0Sdanielk1977 }
35044918fa0Sdanielk1977
35144918fa0Sdanielk1977 rc = Tcl_GetIndexFromObjStruct(
35244918fa0Sdanielk1977 interp, objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iIndex
35344918fa0Sdanielk1977 );
35444918fa0Sdanielk1977 if( rc!=TCL_OK ) return rc;
35544918fa0Sdanielk1977 pSub = &aSub[iIndex];
35644918fa0Sdanielk1977
3570ee469c9Sdrh if( objc<(pSub->nArg+2) ){
35844918fa0Sdanielk1977 Tcl_WrongNumArgs(interp, 2, objv, pSub->zUsage);
35944918fa0Sdanielk1977 return TCL_ERROR;
36044918fa0Sdanielk1977 }
36144918fa0Sdanielk1977
36244918fa0Sdanielk1977 return pSub->xProc(clientData, interp, objc, objv);
36344918fa0Sdanielk1977 }
36444918fa0Sdanielk1977
36544918fa0Sdanielk1977 /*
36681fa193aSdanielk1977 ** The [clock_seconds] command. This is more or less the same as the
36781fa193aSdanielk1977 ** regular tcl [clock seconds], except that it is available in testfixture
36881fa193aSdanielk1977 ** when linked against both Tcl 8.4 and 8.5. Because [clock seconds] is
36981fa193aSdanielk1977 ** implemented as a script in Tcl 8.5, it is not usually available to
37081fa193aSdanielk1977 ** testfixture.
37181fa193aSdanielk1977 */
clock_seconds_proc(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])3727617e4a8Smistachkin static int SQLITE_TCLAPI clock_seconds_proc(
37381fa193aSdanielk1977 ClientData clientData,
37481fa193aSdanielk1977 Tcl_Interp *interp,
37581fa193aSdanielk1977 int objc,
37681fa193aSdanielk1977 Tcl_Obj *CONST objv[]
37781fa193aSdanielk1977 ){
37881fa193aSdanielk1977 Tcl_Time now;
37981fa193aSdanielk1977 Tcl_GetTime(&now);
38081fa193aSdanielk1977 Tcl_SetObjResult(interp, Tcl_NewIntObj(now.sec));
38139070edbSshane UNUSED_PARAMETER(clientData);
38239070edbSshane UNUSED_PARAMETER(objc);
38339070edbSshane UNUSED_PARAMETER(objv);
38481fa193aSdanielk1977 return TCL_OK;
38581fa193aSdanielk1977 }
38681fa193aSdanielk1977
387404ca075Sdanielk1977 /*************************************************************************
388404ca075Sdanielk1977 ** This block contains the implementation of the [sqlite3_blocking_step]
389404ca075Sdanielk1977 ** command available to threads created by [sqlthread spawn] commands. It
390404ca075Sdanielk1977 ** is only available on UNIX for now. This is because pthread condition
391404ca075Sdanielk1977 ** variables are used.
392404ca075Sdanielk1977 **
393404ca075Sdanielk1977 ** The source code for the C functions sqlite3_blocking_step(),
394404ca075Sdanielk1977 ** blocking_step_notify() and the structure UnlockNotification is
395404ca075Sdanielk1977 ** automatically extracted from this file and used as part of the
396404ca075Sdanielk1977 ** documentation for the sqlite3_unlock_notify() API function. This
397404ca075Sdanielk1977 ** should be considered if these functions are to be extended (i.e. to
398404ca075Sdanielk1977 ** support windows) in the future.
399404ca075Sdanielk1977 */
4003a2d29f8Sshaneh #if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
401404ca075Sdanielk1977
402404ca075Sdanielk1977 /* BEGIN_SQLITE_BLOCKING_STEP */
403404ca075Sdanielk1977 /* This example uses the pthreads API */
404404ca075Sdanielk1977 #include <pthread.h>
405404ca075Sdanielk1977
406404ca075Sdanielk1977 /*
407404ca075Sdanielk1977 ** A pointer to an instance of this structure is passed as the user-context
408404ca075Sdanielk1977 ** pointer when registering for an unlock-notify callback.
409404ca075Sdanielk1977 */
410404ca075Sdanielk1977 typedef struct UnlockNotification UnlockNotification;
411404ca075Sdanielk1977 struct UnlockNotification {
412ef8662bbSdrh int fired; /* True after unlock event has occurred */
413404ca075Sdanielk1977 pthread_cond_t cond; /* Condition variable to wait on */
414404ca075Sdanielk1977 pthread_mutex_t mutex; /* Mutex to protect structure */
415404ca075Sdanielk1977 };
416404ca075Sdanielk1977
417404ca075Sdanielk1977 /*
418404ca075Sdanielk1977 ** This function is an unlock-notify callback registered with SQLite.
419404ca075Sdanielk1977 */
unlock_notify_cb(void ** apArg,int nArg)42065a2ea11Sdanielk1977 static void unlock_notify_cb(void **apArg, int nArg){
421404ca075Sdanielk1977 int i;
422404ca075Sdanielk1977 for(i=0; i<nArg; i++){
423404ca075Sdanielk1977 UnlockNotification *p = (UnlockNotification *)apArg[i];
424404ca075Sdanielk1977 pthread_mutex_lock(&p->mutex);
425404ca075Sdanielk1977 p->fired = 1;
426404ca075Sdanielk1977 pthread_cond_signal(&p->cond);
427404ca075Sdanielk1977 pthread_mutex_unlock(&p->mutex);
428404ca075Sdanielk1977 }
429404ca075Sdanielk1977 }
430404ca075Sdanielk1977
431404ca075Sdanielk1977 /*
43265a2ea11Sdanielk1977 ** This function assumes that an SQLite API call (either sqlite3_prepare_v2()
43365a2ea11Sdanielk1977 ** or sqlite3_step()) has just returned SQLITE_LOCKED. The argument is the
43465a2ea11Sdanielk1977 ** associated database connection.
43565a2ea11Sdanielk1977 **
43665a2ea11Sdanielk1977 ** This function calls sqlite3_unlock_notify() to register for an
43765a2ea11Sdanielk1977 ** unlock-notify callback, then blocks until that callback is delivered
43865a2ea11Sdanielk1977 ** and returns SQLITE_OK. The caller should then retry the failed operation.
43965a2ea11Sdanielk1977 **
44065a2ea11Sdanielk1977 ** Or, if sqlite3_unlock_notify() indicates that to block would deadlock
44165a2ea11Sdanielk1977 ** the system, then this function returns SQLITE_LOCKED immediately. In
44265a2ea11Sdanielk1977 ** this case the caller should not retry the operation and should roll
44365a2ea11Sdanielk1977 ** back the current transaction (if any).
44465a2ea11Sdanielk1977 */
wait_for_unlock_notify(sqlite3 * db)44565a2ea11Sdanielk1977 static int wait_for_unlock_notify(sqlite3 *db){
44665a2ea11Sdanielk1977 int rc;
44765a2ea11Sdanielk1977 UnlockNotification un;
44865a2ea11Sdanielk1977
44965a2ea11Sdanielk1977 /* Initialize the UnlockNotification structure. */
45065a2ea11Sdanielk1977 un.fired = 0;
45165a2ea11Sdanielk1977 pthread_mutex_init(&un.mutex, 0);
45265a2ea11Sdanielk1977 pthread_cond_init(&un.cond, 0);
45365a2ea11Sdanielk1977
45465a2ea11Sdanielk1977 /* Register for an unlock-notify callback. */
45565a2ea11Sdanielk1977 rc = sqlite3_unlock_notify(db, unlock_notify_cb, (void *)&un);
45665a2ea11Sdanielk1977 assert( rc==SQLITE_LOCKED || rc==SQLITE_OK );
45765a2ea11Sdanielk1977
45865a2ea11Sdanielk1977 /* The call to sqlite3_unlock_notify() always returns either SQLITE_LOCKED
45965a2ea11Sdanielk1977 ** or SQLITE_OK.
46065a2ea11Sdanielk1977 **
46165a2ea11Sdanielk1977 ** If SQLITE_LOCKED was returned, then the system is deadlocked. In this
46265a2ea11Sdanielk1977 ** case this function needs to return SQLITE_LOCKED to the caller so
46365a2ea11Sdanielk1977 ** that the current transaction can be rolled back. Otherwise, block
46465a2ea11Sdanielk1977 ** until the unlock-notify callback is invoked, then return SQLITE_OK.
46565a2ea11Sdanielk1977 */
46665a2ea11Sdanielk1977 if( rc==SQLITE_OK ){
46765a2ea11Sdanielk1977 pthread_mutex_lock(&un.mutex);
46865a2ea11Sdanielk1977 if( !un.fired ){
46965a2ea11Sdanielk1977 pthread_cond_wait(&un.cond, &un.mutex);
47065a2ea11Sdanielk1977 }
47165a2ea11Sdanielk1977 pthread_mutex_unlock(&un.mutex);
47265a2ea11Sdanielk1977 }
47365a2ea11Sdanielk1977
47465a2ea11Sdanielk1977 /* Destroy the mutex and condition variables. */
47565a2ea11Sdanielk1977 pthread_cond_destroy(&un.cond);
47665a2ea11Sdanielk1977 pthread_mutex_destroy(&un.mutex);
47765a2ea11Sdanielk1977
47865a2ea11Sdanielk1977 return rc;
47965a2ea11Sdanielk1977 }
48065a2ea11Sdanielk1977
48165a2ea11Sdanielk1977 /*
482404ca075Sdanielk1977 ** This function is a wrapper around the SQLite function sqlite3_step().
483404ca075Sdanielk1977 ** It functions in the same way as step(), except that if a required
484404ca075Sdanielk1977 ** shared-cache lock cannot be obtained, this function may block waiting for
485404ca075Sdanielk1977 ** the lock to become available. In this scenario the normal API step()
486404ca075Sdanielk1977 ** function always returns SQLITE_LOCKED.
487404ca075Sdanielk1977 **
488404ca075Sdanielk1977 ** If this function returns SQLITE_LOCKED, the caller should rollback
489404ca075Sdanielk1977 ** the current transaction (if any) and try again later. Otherwise, the
490404ca075Sdanielk1977 ** system may become deadlocked.
491404ca075Sdanielk1977 */
sqlite3_blocking_step(sqlite3_stmt * pStmt)492404ca075Sdanielk1977 int sqlite3_blocking_step(sqlite3_stmt *pStmt){
49365a2ea11Sdanielk1977 int rc;
49465a2ea11Sdanielk1977 while( SQLITE_LOCKED==(rc = sqlite3_step(pStmt)) ){
49565a2ea11Sdanielk1977 rc = wait_for_unlock_notify(sqlite3_db_handle(pStmt));
49665a2ea11Sdanielk1977 if( rc!=SQLITE_OK ) break;
497404ca075Sdanielk1977 sqlite3_reset(pStmt);
498404ca075Sdanielk1977 }
49965a2ea11Sdanielk1977 return rc;
500404ca075Sdanielk1977 }
501404ca075Sdanielk1977
50265a2ea11Sdanielk1977 /*
50365a2ea11Sdanielk1977 ** This function is a wrapper around the SQLite function sqlite3_prepare_v2().
50465a2ea11Sdanielk1977 ** It functions in the same way as prepare_v2(), except that if a required
50565a2ea11Sdanielk1977 ** shared-cache lock cannot be obtained, this function may block waiting for
50665a2ea11Sdanielk1977 ** the lock to become available. In this scenario the normal API prepare_v2()
50765a2ea11Sdanielk1977 ** function always returns SQLITE_LOCKED.
50865a2ea11Sdanielk1977 **
50965a2ea11Sdanielk1977 ** If this function returns SQLITE_LOCKED, the caller should rollback
51065a2ea11Sdanielk1977 ** the current transaction (if any) and try again later. Otherwise, the
51165a2ea11Sdanielk1977 ** system may become deadlocked.
51265a2ea11Sdanielk1977 */
sqlite3_blocking_prepare_v2(sqlite3 * db,const char * zSql,int nSql,sqlite3_stmt ** ppStmt,const char ** pz)51365a2ea11Sdanielk1977 int sqlite3_blocking_prepare_v2(
51465a2ea11Sdanielk1977 sqlite3 *db, /* Database handle. */
51565a2ea11Sdanielk1977 const char *zSql, /* UTF-8 encoded SQL statement. */
51665a2ea11Sdanielk1977 int nSql, /* Length of zSql in bytes. */
51765a2ea11Sdanielk1977 sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
51865a2ea11Sdanielk1977 const char **pz /* OUT: End of parsed string */
51965a2ea11Sdanielk1977 ){
52065a2ea11Sdanielk1977 int rc;
52165a2ea11Sdanielk1977 while( SQLITE_LOCKED==(rc = sqlite3_prepare_v2(db, zSql, nSql, ppStmt, pz)) ){
52265a2ea11Sdanielk1977 rc = wait_for_unlock_notify(db);
52365a2ea11Sdanielk1977 if( rc!=SQLITE_OK ) break;
524404ca075Sdanielk1977 }
525404ca075Sdanielk1977 return rc;
526404ca075Sdanielk1977 }
527404ca075Sdanielk1977 /* END_SQLITE_BLOCKING_STEP */
528404ca075Sdanielk1977
529404ca075Sdanielk1977 /*
530404ca075Sdanielk1977 ** Usage: sqlite3_blocking_step STMT
531404ca075Sdanielk1977 **
532404ca075Sdanielk1977 ** Advance the statement to the next row.
533404ca075Sdanielk1977 */
blocking_step_proc(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])5347617e4a8Smistachkin static int SQLITE_TCLAPI blocking_step_proc(
535404ca075Sdanielk1977 void * clientData,
536404ca075Sdanielk1977 Tcl_Interp *interp,
537404ca075Sdanielk1977 int objc,
538404ca075Sdanielk1977 Tcl_Obj *CONST objv[]
539404ca075Sdanielk1977 ){
540404ca075Sdanielk1977
541404ca075Sdanielk1977 sqlite3_stmt *pStmt;
542404ca075Sdanielk1977 int rc;
543404ca075Sdanielk1977
544404ca075Sdanielk1977 if( objc!=2 ){
545404ca075Sdanielk1977 Tcl_WrongNumArgs(interp, 1, objv, "STMT");
546404ca075Sdanielk1977 return TCL_ERROR;
547404ca075Sdanielk1977 }
548404ca075Sdanielk1977
549404ca075Sdanielk1977 pStmt = (sqlite3_stmt*)sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
550404ca075Sdanielk1977 rc = sqlite3_blocking_step(pStmt);
551404ca075Sdanielk1977
552e84d8d32Smistachkin Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), 0);
553404ca075Sdanielk1977 return TCL_OK;
554404ca075Sdanielk1977 }
555404ca075Sdanielk1977
55665a2ea11Sdanielk1977 /*
55765a2ea11Sdanielk1977 ** Usage: sqlite3_blocking_prepare_v2 DB sql bytes ?tailvar?
558a8bbef84Sdanielk1977 ** Usage: sqlite3_nonblocking_prepare_v2 DB sql bytes ?tailvar?
55965a2ea11Sdanielk1977 */
blocking_prepare_v2_proc(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])5607617e4a8Smistachkin static int SQLITE_TCLAPI blocking_prepare_v2_proc(
56165a2ea11Sdanielk1977 void * clientData,
56265a2ea11Sdanielk1977 Tcl_Interp *interp,
56365a2ea11Sdanielk1977 int objc,
56465a2ea11Sdanielk1977 Tcl_Obj *CONST objv[]
56565a2ea11Sdanielk1977 ){
56665a2ea11Sdanielk1977 sqlite3 *db;
56765a2ea11Sdanielk1977 const char *zSql;
56865a2ea11Sdanielk1977 int bytes;
56965a2ea11Sdanielk1977 const char *zTail = 0;
57065a2ea11Sdanielk1977 sqlite3_stmt *pStmt = 0;
57165a2ea11Sdanielk1977 char zBuf[50];
57265a2ea11Sdanielk1977 int rc;
573a8bbef84Sdanielk1977 int isBlocking = !(clientData==0);
57465a2ea11Sdanielk1977
57565a2ea11Sdanielk1977 if( objc!=5 && objc!=4 ){
57665a2ea11Sdanielk1977 Tcl_AppendResult(interp, "wrong # args: should be \"",
57765a2ea11Sdanielk1977 Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0);
57865a2ea11Sdanielk1977 return TCL_ERROR;
57965a2ea11Sdanielk1977 }
58065a2ea11Sdanielk1977 if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
58165a2ea11Sdanielk1977 zSql = Tcl_GetString(objv[2]);
58265a2ea11Sdanielk1977 if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR;
58365a2ea11Sdanielk1977
584a8bbef84Sdanielk1977 if( isBlocking ){
585a8bbef84Sdanielk1977 rc = sqlite3_blocking_prepare_v2(db, zSql, bytes, &pStmt, &zTail);
586a8bbef84Sdanielk1977 }else{
587a8bbef84Sdanielk1977 rc = sqlite3_prepare_v2(db, zSql, bytes, &pStmt, &zTail);
588a8bbef84Sdanielk1977 }
589a8bbef84Sdanielk1977
59065a2ea11Sdanielk1977 assert(rc==SQLITE_OK || pStmt==0);
59165a2ea11Sdanielk1977 if( zTail && objc>=5 ){
59265a2ea11Sdanielk1977 if( bytes>=0 ){
59365a2ea11Sdanielk1977 bytes = bytes - (zTail-zSql);
59465a2ea11Sdanielk1977 }
59565a2ea11Sdanielk1977 Tcl_ObjSetVar2(interp, objv[4], 0, Tcl_NewStringObj(zTail, bytes), 0);
59665a2ea11Sdanielk1977 }
59765a2ea11Sdanielk1977 if( rc!=SQLITE_OK ){
59865a2ea11Sdanielk1977 assert( pStmt==0 );
59965545b59Sdrh sqlite3_snprintf(sizeof(zBuf), zBuf, "%s ", (char *)sqlite3ErrName(rc));
60065a2ea11Sdanielk1977 Tcl_AppendResult(interp, zBuf, sqlite3_errmsg(db), 0);
60165a2ea11Sdanielk1977 return TCL_ERROR;
60265a2ea11Sdanielk1977 }
60365a2ea11Sdanielk1977
60465a2ea11Sdanielk1977 if( pStmt ){
60565a2ea11Sdanielk1977 if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR;
60665a2ea11Sdanielk1977 Tcl_AppendResult(interp, zBuf, 0);
60765a2ea11Sdanielk1977 }
60865a2ea11Sdanielk1977 return TCL_OK;
60965a2ea11Sdanielk1977 }
61065a2ea11Sdanielk1977
61169910da9Sdrh #endif /* SQLITE_OS_UNIX && SQLITE_ENABLE_UNLOCK_NOTIFY */
612404ca075Sdanielk1977 /*
613404ca075Sdanielk1977 ** End of implementation of [sqlite3_blocking_step].
614404ca075Sdanielk1977 ************************************************************************/
615404ca075Sdanielk1977
61681fa193aSdanielk1977 /*
61744918fa0Sdanielk1977 ** Register commands with the TCL interpreter.
61844918fa0Sdanielk1977 */
SqlitetestThread_Init(Tcl_Interp * interp)61944918fa0Sdanielk1977 int SqlitetestThread_Init(Tcl_Interp *interp){
62044918fa0Sdanielk1977 Tcl_CreateObjCommand(interp, "sqlthread", sqlthread_proc, 0, 0);
62181fa193aSdanielk1977 Tcl_CreateObjCommand(interp, "clock_seconds", clock_seconds_proc, 0, 0);
6223a2d29f8Sshaneh #if SQLITE_OS_UNIX && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
623404ca075Sdanielk1977 Tcl_CreateObjCommand(interp, "sqlite3_blocking_step", blocking_step_proc,0,0);
624a8bbef84Sdanielk1977 Tcl_CreateObjCommand(interp,
625a8bbef84Sdanielk1977 "sqlite3_blocking_prepare_v2", blocking_prepare_v2_proc, (void *)1, 0);
626a8bbef84Sdanielk1977 Tcl_CreateObjCommand(interp,
627a8bbef84Sdanielk1977 "sqlite3_nonblocking_prepare_v2", blocking_prepare_v2_proc, 0, 0);
628404ca075Sdanielk1977 #endif
62944918fa0Sdanielk1977 return TCL_OK;
63044918fa0Sdanielk1977 }
63144918fa0Sdanielk1977 #else
SqlitetestThread_Init(Tcl_Interp * interp)63244918fa0Sdanielk1977 int SqlitetestThread_Init(Tcl_Interp *interp){
63344918fa0Sdanielk1977 return TCL_OK;
63444918fa0Sdanielk1977 }
63544918fa0Sdanielk1977 #endif
636