blob: c5b5ec12cc43d8b43bcf1edd34ca7aa28d9c4db8 [file] [log] [blame]
drha6064dc2003-12-19 02:52:05 +00001/*
2** 2003 December 18
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*************************************************************************
mistachkind5578432012-08-25 10:01:29 +000012** Code for testing the SQLite library in a multithreaded environment.
drha6064dc2003-12-19 02:52:05 +000013*/
14#include "sqliteInt.h"
mistachkin52b1dbb2016-07-28 14:37:04 +000015#if defined(INCLUDE_SQLITE_TCL_H)
16# include "sqlite_tcl.h"
17#else
18# include "tcl.h"
19#endif
shaneh3a2d29f2011-04-04 21:48:01 +000020#if SQLITE_OS_UNIX && SQLITE_THREADSAFE
drha6064dc2003-12-19 02:52:05 +000021#include <stdlib.h>
22#include <string.h>
23#include <pthread.h>
24#include <sched.h>
25#include <ctype.h>
26
mistachkine84d8d32013-04-29 03:09:10 +000027extern const char *sqlite3ErrName(int);
drhd040e762013-04-10 23:48:37 +000028
drha6064dc2003-12-19 02:52:05 +000029/*
30** Each thread is controlled by an instance of the following
31** structure.
32*/
33typedef struct Thread Thread;
34struct Thread {
35 /* The first group of fields are writable by the master and read-only
36 ** to the thread. */
37 char *zFilename; /* Name of database file */
38 void (*xOp)(Thread*); /* next operation to do */
39 char *zArg; /* argument usable by xOp */
40 int opnum; /* Operation number */
41 int busy; /* True if this thread is in use */
42
43 /* The next group of fields are writable by the thread but read-only to the
44 ** master. */
45 int completed; /* Number of operations completed */
drh9bb575f2004-09-06 17:24:11 +000046 sqlite3 *db; /* Open database */
danielk1977fc57d7b2004-05-26 02:04:57 +000047 sqlite3_stmt *pStmt; /* Pending operation */
drha6064dc2003-12-19 02:52:05 +000048 char *zErr; /* operation error */
49 char *zStaticErr; /* Static error message */
50 int rc; /* operation return code */
51 int argc; /* number of columns in result */
danielk1977fc57d7b2004-05-26 02:04:57 +000052 const char *argv[100]; /* result columns */
53 const char *colv[100]; /* result column names */
drha6064dc2003-12-19 02:52:05 +000054};
55
56/*
57** There can be as many as 26 threads running at once. Each is named
58** by a capital letter: A, B, C, ..., Y, Z.
59*/
60#define N_THREAD 26
61static Thread threadset[N_THREAD];
62
63
64/*
65** The main loop for a thread. Threads use busy waiting.
66*/
67static void *thread_main(void *pArg){
68 Thread *p = (Thread*)pArg;
69 if( p->db ){
danielk19776f8a5032004-05-10 10:34:51 +000070 sqlite3_close(p->db);
drha6064dc2003-12-19 02:52:05 +000071 }
danielk19774f057f92004-06-08 00:02:33 +000072 sqlite3_open(p->zFilename, &p->db);
danielk197780290862004-05-22 09:21:21 +000073 if( SQLITE_OK!=sqlite3_errcode(p->db) ){
74 p->zErr = strdup(sqlite3_errmsg(p->db));
75 sqlite3_close(p->db);
76 p->db = 0;
77 }
danielk1977fc57d7b2004-05-26 02:04:57 +000078 p->pStmt = 0;
drha6064dc2003-12-19 02:52:05 +000079 p->completed = 1;
80 while( p->opnum<=p->completed ) sched_yield();
81 while( p->xOp ){
82 if( p->zErr && p->zErr!=p->zStaticErr ){
drh3f4fedb2004-05-31 19:34:33 +000083 sqlite3_free(p->zErr);
drha6064dc2003-12-19 02:52:05 +000084 p->zErr = 0;
85 }
86 (*p->xOp)(p);
87 p->completed++;
88 while( p->opnum<=p->completed ) sched_yield();
89 }
danielk1977fc57d7b2004-05-26 02:04:57 +000090 if( p->pStmt ){
91 sqlite3_finalize(p->pStmt);
92 p->pStmt = 0;
drha6064dc2003-12-19 02:52:05 +000093 }
94 if( p->db ){
danielk19776f8a5032004-05-10 10:34:51 +000095 sqlite3_close(p->db);
drha6064dc2003-12-19 02:52:05 +000096 p->db = 0;
97 }
98 if( p->zErr && p->zErr!=p->zStaticErr ){
drh3f4fedb2004-05-31 19:34:33 +000099 sqlite3_free(p->zErr);
drha6064dc2003-12-19 02:52:05 +0000100 p->zErr = 0;
101 }
102 p->completed++;
shaneeec556d2008-10-12 00:27:53 +0000103#ifndef SQLITE_OMIT_DEPRECATED
drhb4bc7052006-01-11 23:40:33 +0000104 sqlite3_thread_cleanup();
shaneeec556d2008-10-12 00:27:53 +0000105#endif
drha6064dc2003-12-19 02:52:05 +0000106 return 0;
107}
108
109/*
110** Get a thread ID which is an upper case letter. Return the index.
111** If the argument is not a valid thread ID put an error message in
112** the interpreter and return -1.
113*/
114static int parse_thread_id(Tcl_Interp *interp, const char *zArg){
drh4c755c02004-08-08 20:22:17 +0000115 if( zArg==0 || zArg[0]==0 || zArg[1]!=0 || !isupper((unsigned char)zArg[0]) ){
drha6064dc2003-12-19 02:52:05 +0000116 Tcl_AppendResult(interp, "thread ID must be an upper case letter", 0);
117 return -1;
118 }
119 return zArg[0] - 'A';
120}
121
122/*
123** Usage: thread_create NAME FILENAME
124**
125** NAME should be an upper case letter. Start the thread running with
126** an open connection to the given database.
127*/
mistachkin7617e4a2016-07-28 17:11:20 +0000128static int SQLITE_TCLAPI tcl_thread_create(
drha6064dc2003-12-19 02:52:05 +0000129 void *NotUsed,
130 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
131 int argc, /* Number of arguments */
132 const char **argv /* Text of each argument */
133){
134 int i;
135 pthread_t x;
136 int rc;
137
138 if( argc!=3 ){
139 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
140 " ID FILENAME", 0);
141 return TCL_ERROR;
142 }
143 i = parse_thread_id(interp, argv[1]);
144 if( i<0 ) return TCL_ERROR;
145 if( threadset[i].busy ){
146 Tcl_AppendResult(interp, "thread ", argv[1], " is already running", 0);
147 return TCL_ERROR;
148 }
149 threadset[i].busy = 1;
drhcab5ed72007-08-22 11:41:18 +0000150 sqlite3_free(threadset[i].zFilename);
drhb9755982010-07-24 16:34:37 +0000151 threadset[i].zFilename = sqlite3_mprintf("%s", argv[2]);
drha6064dc2003-12-19 02:52:05 +0000152 threadset[i].opnum = 1;
drh2fd2fa02003-12-20 04:00:52 +0000153 threadset[i].completed = 0;
drha6064dc2003-12-19 02:52:05 +0000154 rc = pthread_create(&x, 0, thread_main, &threadset[i]);
155 if( rc ){
156 Tcl_AppendResult(interp, "failed to create the thread", 0);
drhcab5ed72007-08-22 11:41:18 +0000157 sqlite3_free(threadset[i].zFilename);
drha6064dc2003-12-19 02:52:05 +0000158 threadset[i].busy = 0;
159 return TCL_ERROR;
160 }
161 pthread_detach(x);
162 return TCL_OK;
163}
164
165/*
166** Wait for a thread to reach its idle state.
167*/
168static void thread_wait(Thread *p){
169 while( p->opnum>p->completed ) sched_yield();
170}
171
172/*
173** Usage: thread_wait ID
174**
175** Wait on thread ID to reach its idle state.
176*/
mistachkin7617e4a2016-07-28 17:11:20 +0000177static int SQLITE_TCLAPI tcl_thread_wait(
drha6064dc2003-12-19 02:52:05 +0000178 void *NotUsed,
179 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
180 int argc, /* Number of arguments */
181 const char **argv /* Text of each argument */
182){
183 int i;
184
185 if( argc!=2 ){
186 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
187 " ID", 0);
188 return TCL_ERROR;
189 }
190 i = parse_thread_id(interp, argv[1]);
191 if( i<0 ) return TCL_ERROR;
192 if( !threadset[i].busy ){
193 Tcl_AppendResult(interp, "no such thread", 0);
194 return TCL_ERROR;
195 }
196 thread_wait(&threadset[i]);
197 return TCL_OK;
198}
199
200/*
201** Stop a thread.
202*/
203static void stop_thread(Thread *p){
204 thread_wait(p);
205 p->xOp = 0;
206 p->opnum++;
207 thread_wait(p);
drhcab5ed72007-08-22 11:41:18 +0000208 sqlite3_free(p->zArg);
drha6064dc2003-12-19 02:52:05 +0000209 p->zArg = 0;
drhcab5ed72007-08-22 11:41:18 +0000210 sqlite3_free(p->zFilename);
drha6064dc2003-12-19 02:52:05 +0000211 p->zFilename = 0;
212 p->busy = 0;
213}
214
215/*
216** Usage: thread_halt ID
217**
218** Cause a thread to shut itself down. Wait for the shutdown to be
219** completed. If ID is "*" then stop all threads.
220*/
mistachkin7617e4a2016-07-28 17:11:20 +0000221static int SQLITE_TCLAPI tcl_thread_halt(
drha6064dc2003-12-19 02:52:05 +0000222 void *NotUsed,
223 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
224 int argc, /* Number of arguments */
225 const char **argv /* Text of each argument */
226){
227 int i;
228
229 if( argc!=2 ){
230 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
231 " ID", 0);
232 return TCL_ERROR;
233 }
234 if( argv[1][0]=='*' && argv[1][1]==0 ){
235 for(i=0; i<N_THREAD; i++){
236 if( threadset[i].busy ) stop_thread(&threadset[i]);
237 }
238 }else{
239 i = parse_thread_id(interp, argv[1]);
240 if( i<0 ) return TCL_ERROR;
241 if( !threadset[i].busy ){
242 Tcl_AppendResult(interp, "no such thread", 0);
243 return TCL_ERROR;
244 }
245 stop_thread(&threadset[i]);
246 }
247 return TCL_OK;
248}
249
250/*
251** Usage: thread_argc ID
252**
253** Wait on the most recent thread_step to complete, then return the
254** number of columns in the result set.
255*/
mistachkin7617e4a2016-07-28 17:11:20 +0000256static int SQLITE_TCLAPI tcl_thread_argc(
drha6064dc2003-12-19 02:52:05 +0000257 void *NotUsed,
258 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
259 int argc, /* Number of arguments */
260 const char **argv /* Text of each argument */
261){
262 int i;
263 char zBuf[100];
264
265 if( argc!=2 ){
266 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
267 " ID", 0);
268 return TCL_ERROR;
269 }
270 i = parse_thread_id(interp, argv[1]);
271 if( i<0 ) return TCL_ERROR;
272 if( !threadset[i].busy ){
273 Tcl_AppendResult(interp, "no such thread", 0);
274 return TCL_ERROR;
275 }
276 thread_wait(&threadset[i]);
drh65545b52015-01-19 00:35:53 +0000277 sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", threadset[i].argc);
drha6064dc2003-12-19 02:52:05 +0000278 Tcl_AppendResult(interp, zBuf, 0);
279 return TCL_OK;
280}
281
282/*
283** Usage: thread_argv ID N
284**
285** Wait on the most recent thread_step to complete, then return the
286** value of the N-th columns in the result set.
287*/
mistachkin7617e4a2016-07-28 17:11:20 +0000288static int SQLITE_TCLAPI tcl_thread_argv(
drha6064dc2003-12-19 02:52:05 +0000289 void *NotUsed,
290 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
291 int argc, /* Number of arguments */
292 const char **argv /* Text of each argument */
293){
294 int i;
295 int n;
296
297 if( argc!=3 ){
298 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
299 " ID N", 0);
300 return TCL_ERROR;
301 }
302 i = parse_thread_id(interp, argv[1]);
303 if( i<0 ) return TCL_ERROR;
304 if( !threadset[i].busy ){
305 Tcl_AppendResult(interp, "no such thread", 0);
306 return TCL_ERROR;
307 }
308 if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
309 thread_wait(&threadset[i]);
310 if( n<0 || n>=threadset[i].argc ){
311 Tcl_AppendResult(interp, "column number out of range", 0);
312 return TCL_ERROR;
313 }
314 Tcl_AppendResult(interp, threadset[i].argv[n], 0);
315 return TCL_OK;
316}
317
318/*
319** Usage: thread_colname ID N
320**
321** Wait on the most recent thread_step to complete, then return the
322** name of the N-th columns in the result set.
323*/
mistachkin7617e4a2016-07-28 17:11:20 +0000324static int SQLITE_TCLAPI tcl_thread_colname(
drha6064dc2003-12-19 02:52:05 +0000325 void *NotUsed,
326 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
327 int argc, /* Number of arguments */
328 const char **argv /* Text of each argument */
329){
330 int i;
331 int n;
332
333 if( argc!=3 ){
334 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
335 " ID N", 0);
336 return TCL_ERROR;
337 }
338 i = parse_thread_id(interp, argv[1]);
339 if( i<0 ) return TCL_ERROR;
340 if( !threadset[i].busy ){
341 Tcl_AppendResult(interp, "no such thread", 0);
342 return TCL_ERROR;
343 }
344 if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
345 thread_wait(&threadset[i]);
346 if( n<0 || n>=threadset[i].argc ){
347 Tcl_AppendResult(interp, "column number out of range", 0);
348 return TCL_ERROR;
349 }
350 Tcl_AppendResult(interp, threadset[i].colv[n], 0);
351 return TCL_OK;
352}
353
354/*
355** Usage: thread_result ID
356**
357** Wait on the most recent operation to complete, then return the
358** result code from that operation.
359*/
mistachkin7617e4a2016-07-28 17:11:20 +0000360static int SQLITE_TCLAPI tcl_thread_result(
drha6064dc2003-12-19 02:52:05 +0000361 void *NotUsed,
362 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
363 int argc, /* Number of arguments */
364 const char **argv /* Text of each argument */
365){
366 int i;
367 const char *zName;
368
369 if( argc!=2 ){
370 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
371 " ID", 0);
372 return TCL_ERROR;
373 }
374 i = parse_thread_id(interp, argv[1]);
375 if( i<0 ) return TCL_ERROR;
376 if( !threadset[i].busy ){
377 Tcl_AppendResult(interp, "no such thread", 0);
378 return TCL_ERROR;
379 }
380 thread_wait(&threadset[i]);
mistachkine84d8d32013-04-29 03:09:10 +0000381 zName = sqlite3ErrName(threadset[i].rc);
drha6064dc2003-12-19 02:52:05 +0000382 Tcl_AppendResult(interp, zName, 0);
383 return TCL_OK;
384}
385
386/*
387** Usage: thread_error ID
388**
389** Wait on the most recent operation to complete, then return the
390** error string.
391*/
mistachkin7617e4a2016-07-28 17:11:20 +0000392static int SQLITE_TCLAPI tcl_thread_error(
drha6064dc2003-12-19 02:52:05 +0000393 void *NotUsed,
394 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
395 int argc, /* Number of arguments */
396 const char **argv /* Text of each argument */
397){
398 int i;
399
400 if( argc!=2 ){
401 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
402 " ID", 0);
403 return TCL_ERROR;
404 }
405 i = parse_thread_id(interp, argv[1]);
406 if( i<0 ) return TCL_ERROR;
407 if( !threadset[i].busy ){
408 Tcl_AppendResult(interp, "no such thread", 0);
409 return TCL_ERROR;
410 }
411 thread_wait(&threadset[i]);
412 Tcl_AppendResult(interp, threadset[i].zErr, 0);
413 return TCL_OK;
414}
415
416/*
417** This procedure runs in the thread to compile an SQL statement.
418*/
419static void do_compile(Thread *p){
420 if( p->db==0 ){
421 p->zErr = p->zStaticErr = "no database is open";
422 p->rc = SQLITE_ERROR;
423 return;
424 }
danielk1977fc57d7b2004-05-26 02:04:57 +0000425 if( p->pStmt ){
426 sqlite3_finalize(p->pStmt);
427 p->pStmt = 0;
drha6064dc2003-12-19 02:52:05 +0000428 }
danielk1977fc57d7b2004-05-26 02:04:57 +0000429 p->rc = sqlite3_prepare(p->db, p->zArg, -1, &p->pStmt, 0);
drha6064dc2003-12-19 02:52:05 +0000430}
431
432/*
433** Usage: thread_compile ID SQL
434**
435** Compile a new virtual machine.
436*/
mistachkin7617e4a2016-07-28 17:11:20 +0000437static int SQLITE_TCLAPI tcl_thread_compile(
drha6064dc2003-12-19 02:52:05 +0000438 void *NotUsed,
439 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
440 int argc, /* Number of arguments */
441 const char **argv /* Text of each argument */
442){
443 int i;
444 if( argc!=3 ){
445 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
446 " ID SQL", 0);
447 return TCL_ERROR;
448 }
449 i = parse_thread_id(interp, argv[1]);
450 if( i<0 ) return TCL_ERROR;
451 if( !threadset[i].busy ){
452 Tcl_AppendResult(interp, "no such thread", 0);
453 return TCL_ERROR;
454 }
455 thread_wait(&threadset[i]);
456 threadset[i].xOp = do_compile;
drhcab5ed72007-08-22 11:41:18 +0000457 sqlite3_free(threadset[i].zArg);
drhb9755982010-07-24 16:34:37 +0000458 threadset[i].zArg = sqlite3_mprintf("%s", argv[2]);
drha6064dc2003-12-19 02:52:05 +0000459 threadset[i].opnum++;
460 return TCL_OK;
461}
462
463/*
464** This procedure runs in the thread to step the virtual machine.
465*/
466static void do_step(Thread *p){
danielk1977fc57d7b2004-05-26 02:04:57 +0000467 int i;
468 if( p->pStmt==0 ){
drha6064dc2003-12-19 02:52:05 +0000469 p->zErr = p->zStaticErr = "no virtual machine available";
470 p->rc = SQLITE_ERROR;
471 return;
472 }
danielk1977fc57d7b2004-05-26 02:04:57 +0000473 p->rc = sqlite3_step(p->pStmt);
474 if( p->rc==SQLITE_ROW ){
475 p->argc = sqlite3_column_count(p->pStmt);
476 for(i=0; i<sqlite3_data_count(p->pStmt); i++){
drh24bd82c2006-01-20 17:56:32 +0000477 p->argv[i] = (char*)sqlite3_column_text(p->pStmt, i);
danielk1977fc57d7b2004-05-26 02:04:57 +0000478 }
479 for(i=0; i<p->argc; i++){
480 p->colv[i] = sqlite3_column_name(p->pStmt, i);
481 }
482 }
drha6064dc2003-12-19 02:52:05 +0000483}
484
485/*
486** Usage: thread_step ID
487**
488** Advance the virtual machine by one step
489*/
mistachkin7617e4a2016-07-28 17:11:20 +0000490static int SQLITE_TCLAPI tcl_thread_step(
drha6064dc2003-12-19 02:52:05 +0000491 void *NotUsed,
492 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
493 int argc, /* Number of arguments */
494 const char **argv /* Text of each argument */
495){
496 int i;
497 if( argc!=2 ){
498 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
499 " IDL", 0);
500 return TCL_ERROR;
501 }
502 i = parse_thread_id(interp, argv[1]);
503 if( i<0 ) return TCL_ERROR;
504 if( !threadset[i].busy ){
505 Tcl_AppendResult(interp, "no such thread", 0);
506 return TCL_ERROR;
507 }
508 thread_wait(&threadset[i]);
509 threadset[i].xOp = do_step;
510 threadset[i].opnum++;
511 return TCL_OK;
512}
513
514/*
515** This procedure runs in the thread to finalize a virtual machine.
516*/
517static void do_finalize(Thread *p){
danielk1977fc57d7b2004-05-26 02:04:57 +0000518 if( p->pStmt==0 ){
drha6064dc2003-12-19 02:52:05 +0000519 p->zErr = p->zStaticErr = "no virtual machine available";
520 p->rc = SQLITE_ERROR;
521 return;
522 }
danielk1977fc57d7b2004-05-26 02:04:57 +0000523 p->rc = sqlite3_finalize(p->pStmt);
524 p->pStmt = 0;
drha6064dc2003-12-19 02:52:05 +0000525}
526
527/*
528** Usage: thread_finalize ID
529**
530** Finalize the virtual machine.
531*/
mistachkin7617e4a2016-07-28 17:11:20 +0000532static int SQLITE_TCLAPI tcl_thread_finalize(
drha6064dc2003-12-19 02:52:05 +0000533 void *NotUsed,
534 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
535 int argc, /* Number of arguments */
536 const char **argv /* Text of each argument */
537){
538 int i;
539 if( argc!=2 ){
540 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
541 " IDL", 0);
542 return TCL_ERROR;
543 }
544 i = parse_thread_id(interp, argv[1]);
545 if( i<0 ) return TCL_ERROR;
546 if( !threadset[i].busy ){
547 Tcl_AppendResult(interp, "no such thread", 0);
548 return TCL_ERROR;
549 }
550 thread_wait(&threadset[i]);
551 threadset[i].xOp = do_finalize;
drhcab5ed72007-08-22 11:41:18 +0000552 sqlite3_free(threadset[i].zArg);
drha6064dc2003-12-19 02:52:05 +0000553 threadset[i].zArg = 0;
554 threadset[i].opnum++;
555 return TCL_OK;
556}
557
558/*
drh1bdd9b52004-04-23 17:04:44 +0000559** Usage: thread_swap ID ID
560**
561** Interchange the sqlite* pointer between two threads.
562*/
mistachkin7617e4a2016-07-28 17:11:20 +0000563static int SQLITE_TCLAPI tcl_thread_swap(
drh1bdd9b52004-04-23 17:04:44 +0000564 void *NotUsed,
565 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
566 int argc, /* Number of arguments */
567 const char **argv /* Text of each argument */
568){
569 int i, j;
drh9bb575f2004-09-06 17:24:11 +0000570 sqlite3 *temp;
drh1bdd9b52004-04-23 17:04:44 +0000571 if( argc!=3 ){
572 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
573 " ID1 ID2", 0);
574 return TCL_ERROR;
575 }
576 i = parse_thread_id(interp, argv[1]);
577 if( i<0 ) return TCL_ERROR;
578 if( !threadset[i].busy ){
579 Tcl_AppendResult(interp, "no such thread", 0);
580 return TCL_ERROR;
581 }
582 thread_wait(&threadset[i]);
583 j = parse_thread_id(interp, argv[2]);
584 if( j<0 ) return TCL_ERROR;
585 if( !threadset[j].busy ){
586 Tcl_AppendResult(interp, "no such thread", 0);
587 return TCL_ERROR;
588 }
589 thread_wait(&threadset[j]);
590 temp = threadset[i].db;
591 threadset[i].db = threadset[j].db;
592 threadset[j].db = temp;
593 return TCL_OK;
594}
595
596/*
drh64b1bea2006-01-15 02:30:57 +0000597** Usage: thread_db_get ID
598**
599** Return the database connection pointer for the given thread. Then
600** remove the pointer from the thread itself. Afterwards, the thread
601** can be stopped and the connection can be used by the main thread.
602*/
mistachkin7617e4a2016-07-28 17:11:20 +0000603static int SQLITE_TCLAPI tcl_thread_db_get(
drh64b1bea2006-01-15 02:30:57 +0000604 void *NotUsed,
605 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
606 int argc, /* Number of arguments */
607 const char **argv /* Text of each argument */
608){
609 int i;
610 char zBuf[100];
611 extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);
612 if( argc!=2 ){
613 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
614 " ID", 0);
615 return TCL_ERROR;
616 }
617 i = parse_thread_id(interp, argv[1]);
618 if( i<0 ) return TCL_ERROR;
619 if( !threadset[i].busy ){
620 Tcl_AppendResult(interp, "no such thread", 0);
621 return TCL_ERROR;
622 }
623 thread_wait(&threadset[i]);
624 sqlite3TestMakePointerStr(interp, zBuf, threadset[i].db);
625 threadset[i].db = 0;
drh52fc8492006-02-23 21:43:55 +0000626 Tcl_AppendResult(interp, zBuf, (char*)0);
drh64b1bea2006-01-15 02:30:57 +0000627 return TCL_OK;
628}
629
630/*
dan11b38792009-09-09 18:46:52 +0000631** Usage: thread_db_put ID DB
632**
633*/
mistachkin7617e4a2016-07-28 17:11:20 +0000634static int SQLITE_TCLAPI tcl_thread_db_put(
dan11b38792009-09-09 18:46:52 +0000635 void *NotUsed,
636 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
637 int argc, /* Number of arguments */
638 const char **argv /* Text of each argument */
639){
640 int i;
641 extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);
642 extern void *sqlite3TestTextToPtr(const char *);
643 if( argc!=3 ){
644 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
645 " ID DB", 0);
646 return TCL_ERROR;
647 }
648 i = parse_thread_id(interp, argv[1]);
649 if( i<0 ) return TCL_ERROR;
650 if( !threadset[i].busy ){
651 Tcl_AppendResult(interp, "no such thread", 0);
652 return TCL_ERROR;
653 }
654 thread_wait(&threadset[i]);
655 assert( !threadset[i].db );
656 threadset[i].db = (sqlite3*)sqlite3TestTextToPtr(argv[2]);
657 return TCL_OK;
658}
659
660/*
drh64b1bea2006-01-15 02:30:57 +0000661** Usage: thread_stmt_get ID
662**
663** Return the database stmt pointer for the given thread. Then
664** remove the pointer from the thread itself.
665*/
mistachkin7617e4a2016-07-28 17:11:20 +0000666static int SQLITE_TCLAPI tcl_thread_stmt_get(
drh64b1bea2006-01-15 02:30:57 +0000667 void *NotUsed,
668 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
669 int argc, /* Number of arguments */
670 const char **argv /* Text of each argument */
671){
672 int i;
673 char zBuf[100];
674 extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);
675 if( argc!=2 ){
676 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
677 " ID", 0);
678 return TCL_ERROR;
679 }
680 i = parse_thread_id(interp, argv[1]);
681 if( i<0 ) return TCL_ERROR;
682 if( !threadset[i].busy ){
683 Tcl_AppendResult(interp, "no such thread", 0);
684 return TCL_ERROR;
685 }
686 thread_wait(&threadset[i]);
687 sqlite3TestMakePointerStr(interp, zBuf, threadset[i].pStmt);
688 threadset[i].pStmt = 0;
drh52fc8492006-02-23 21:43:55 +0000689 Tcl_AppendResult(interp, zBuf, (char*)0);
drh64b1bea2006-01-15 02:30:57 +0000690 return TCL_OK;
691}
692
693/*
drha6064dc2003-12-19 02:52:05 +0000694** Register commands with the TCL interpreter.
695*/
696int Sqlitetest4_Init(Tcl_Interp *interp){
697 static struct {
698 char *zName;
699 Tcl_CmdProc *xProc;
700 } aCmd[] = {
701 { "thread_create", (Tcl_CmdProc*)tcl_thread_create },
702 { "thread_wait", (Tcl_CmdProc*)tcl_thread_wait },
703 { "thread_halt", (Tcl_CmdProc*)tcl_thread_halt },
704 { "thread_argc", (Tcl_CmdProc*)tcl_thread_argc },
705 { "thread_argv", (Tcl_CmdProc*)tcl_thread_argv },
706 { "thread_colname", (Tcl_CmdProc*)tcl_thread_colname },
707 { "thread_result", (Tcl_CmdProc*)tcl_thread_result },
708 { "thread_error", (Tcl_CmdProc*)tcl_thread_error },
709 { "thread_compile", (Tcl_CmdProc*)tcl_thread_compile },
710 { "thread_step", (Tcl_CmdProc*)tcl_thread_step },
711 { "thread_finalize", (Tcl_CmdProc*)tcl_thread_finalize },
drh1bdd9b52004-04-23 17:04:44 +0000712 { "thread_swap", (Tcl_CmdProc*)tcl_thread_swap },
drh64b1bea2006-01-15 02:30:57 +0000713 { "thread_db_get", (Tcl_CmdProc*)tcl_thread_db_get },
dan11b38792009-09-09 18:46:52 +0000714 { "thread_db_put", (Tcl_CmdProc*)tcl_thread_db_put },
drh64b1bea2006-01-15 02:30:57 +0000715 { "thread_stmt_get", (Tcl_CmdProc*)tcl_thread_stmt_get },
drha6064dc2003-12-19 02:52:05 +0000716 };
717 int i;
718
719 for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
720 Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
721 }
722 return TCL_OK;
723}
724#else
725int Sqlitetest4_Init(Tcl_Interp *interp){ return TCL_OK; }
danielk197729bafea2008-06-26 10:41:19 +0000726#endif /* SQLITE_OS_UNIX */