Merge branch 'prerelease' of github.com:sqlcipher/sqlcipher into prerelease
[sqlcipher.git] / src / test4.c
bloba6375c7cc403f60caab7e959533e3db1ff750ae4
1 /*
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.
11 *************************************************************************
12 ** Code for testing the SQLite library in a multithreaded environment.
14 #include "sqliteInt.h"
15 #include "tcl.h"
16 #if SQLITE_OS_UNIX && SQLITE_THREADSAFE
17 #include <stdlib.h>
18 #include <string.h>
19 #include <pthread.h>
20 #include <sched.h>
21 #include <ctype.h>
23 extern const char *sqlite3ErrName(int);
26 ** Each thread is controlled by an instance of the following
27 ** structure.
29 typedef struct Thread Thread;
30 struct Thread {
31 /* The first group of fields are writable by the master and read-only
32 ** to the thread. */
33 char *zFilename; /* Name of database file */
34 void (*xOp)(Thread*); /* next operation to do */
35 char *zArg; /* argument usable by xOp */
36 int opnum; /* Operation number */
37 int busy; /* True if this thread is in use */
39 /* The next group of fields are writable by the thread but read-only to the
40 ** master. */
41 int completed; /* Number of operations completed */
42 sqlite3 *db; /* Open database */
43 sqlite3_stmt *pStmt; /* Pending operation */
44 char *zErr; /* operation error */
45 char *zStaticErr; /* Static error message */
46 int rc; /* operation return code */
47 int argc; /* number of columns in result */
48 const char *argv[100]; /* result columns */
49 const char *colv[100]; /* result column names */
53 ** There can be as many as 26 threads running at once. Each is named
54 ** by a capital letter: A, B, C, ..., Y, Z.
56 #define N_THREAD 26
57 static Thread threadset[N_THREAD];
61 ** The main loop for a thread. Threads use busy waiting.
63 static void *thread_main(void *pArg){
64 Thread *p = (Thread*)pArg;
65 if( p->db ){
66 sqlite3_close(p->db);
68 sqlite3_open(p->zFilename, &p->db);
69 if( SQLITE_OK!=sqlite3_errcode(p->db) ){
70 p->zErr = strdup(sqlite3_errmsg(p->db));
71 sqlite3_close(p->db);
72 p->db = 0;
74 p->pStmt = 0;
75 p->completed = 1;
76 while( p->opnum<=p->completed ) sched_yield();
77 while( p->xOp ){
78 if( p->zErr && p->zErr!=p->zStaticErr ){
79 sqlite3_free(p->zErr);
80 p->zErr = 0;
82 (*p->xOp)(p);
83 p->completed++;
84 while( p->opnum<=p->completed ) sched_yield();
86 if( p->pStmt ){
87 sqlite3_finalize(p->pStmt);
88 p->pStmt = 0;
90 if( p->db ){
91 sqlite3_close(p->db);
92 p->db = 0;
94 if( p->zErr && p->zErr!=p->zStaticErr ){
95 sqlite3_free(p->zErr);
96 p->zErr = 0;
98 p->completed++;
99 #ifndef SQLITE_OMIT_DEPRECATED
100 sqlite3_thread_cleanup();
101 #endif
102 return 0;
106 ** Get a thread ID which is an upper case letter. Return the index.
107 ** If the argument is not a valid thread ID put an error message in
108 ** the interpreter and return -1.
110 static int parse_thread_id(Tcl_Interp *interp, const char *zArg){
111 if( zArg==0 || zArg[0]==0 || zArg[1]!=0 || !isupper((unsigned char)zArg[0]) ){
112 Tcl_AppendResult(interp, "thread ID must be an upper case letter", 0);
113 return -1;
115 return zArg[0] - 'A';
119 ** Usage: thread_create NAME FILENAME
121 ** NAME should be an upper case letter. Start the thread running with
122 ** an open connection to the given database.
124 static int tcl_thread_create(
125 void *NotUsed,
126 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
127 int argc, /* Number of arguments */
128 const char **argv /* Text of each argument */
130 int i;
131 pthread_t x;
132 int rc;
134 if( argc!=3 ){
135 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
136 " ID FILENAME", 0);
137 return TCL_ERROR;
139 i = parse_thread_id(interp, argv[1]);
140 if( i<0 ) return TCL_ERROR;
141 if( threadset[i].busy ){
142 Tcl_AppendResult(interp, "thread ", argv[1], " is already running", 0);
143 return TCL_ERROR;
145 threadset[i].busy = 1;
146 sqlite3_free(threadset[i].zFilename);
147 threadset[i].zFilename = sqlite3_mprintf("%s", argv[2]);
148 threadset[i].opnum = 1;
149 threadset[i].completed = 0;
150 rc = pthread_create(&x, 0, thread_main, &threadset[i]);
151 if( rc ){
152 Tcl_AppendResult(interp, "failed to create the thread", 0);
153 sqlite3_free(threadset[i].zFilename);
154 threadset[i].busy = 0;
155 return TCL_ERROR;
157 pthread_detach(x);
158 return TCL_OK;
162 ** Wait for a thread to reach its idle state.
164 static void thread_wait(Thread *p){
165 while( p->opnum>p->completed ) sched_yield();
169 ** Usage: thread_wait ID
171 ** Wait on thread ID to reach its idle state.
173 static int tcl_thread_wait(
174 void *NotUsed,
175 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
176 int argc, /* Number of arguments */
177 const char **argv /* Text of each argument */
179 int i;
181 if( argc!=2 ){
182 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
183 " ID", 0);
184 return TCL_ERROR;
186 i = parse_thread_id(interp, argv[1]);
187 if( i<0 ) return TCL_ERROR;
188 if( !threadset[i].busy ){
189 Tcl_AppendResult(interp, "no such thread", 0);
190 return TCL_ERROR;
192 thread_wait(&threadset[i]);
193 return TCL_OK;
197 ** Stop a thread.
199 static void stop_thread(Thread *p){
200 thread_wait(p);
201 p->xOp = 0;
202 p->opnum++;
203 thread_wait(p);
204 sqlite3_free(p->zArg);
205 p->zArg = 0;
206 sqlite3_free(p->zFilename);
207 p->zFilename = 0;
208 p->busy = 0;
212 ** Usage: thread_halt ID
214 ** Cause a thread to shut itself down. Wait for the shutdown to be
215 ** completed. If ID is "*" then stop all threads.
217 static int tcl_thread_halt(
218 void *NotUsed,
219 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
220 int argc, /* Number of arguments */
221 const char **argv /* Text of each argument */
223 int i;
225 if( argc!=2 ){
226 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
227 " ID", 0);
228 return TCL_ERROR;
230 if( argv[1][0]=='*' && argv[1][1]==0 ){
231 for(i=0; i<N_THREAD; i++){
232 if( threadset[i].busy ) stop_thread(&threadset[i]);
234 }else{
235 i = parse_thread_id(interp, argv[1]);
236 if( i<0 ) return TCL_ERROR;
237 if( !threadset[i].busy ){
238 Tcl_AppendResult(interp, "no such thread", 0);
239 return TCL_ERROR;
241 stop_thread(&threadset[i]);
243 return TCL_OK;
247 ** Usage: thread_argc ID
249 ** Wait on the most recent thread_step to complete, then return the
250 ** number of columns in the result set.
252 static int tcl_thread_argc(
253 void *NotUsed,
254 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
255 int argc, /* Number of arguments */
256 const char **argv /* Text of each argument */
258 int i;
259 char zBuf[100];
261 if( argc!=2 ){
262 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
263 " ID", 0);
264 return TCL_ERROR;
266 i = parse_thread_id(interp, argv[1]);
267 if( i<0 ) return TCL_ERROR;
268 if( !threadset[i].busy ){
269 Tcl_AppendResult(interp, "no such thread", 0);
270 return TCL_ERROR;
272 thread_wait(&threadset[i]);
273 sprintf(zBuf, "%d", threadset[i].argc);
274 Tcl_AppendResult(interp, zBuf, 0);
275 return TCL_OK;
279 ** Usage: thread_argv ID N
281 ** Wait on the most recent thread_step to complete, then return the
282 ** value of the N-th columns in the result set.
284 static int tcl_thread_argv(
285 void *NotUsed,
286 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
287 int argc, /* Number of arguments */
288 const char **argv /* Text of each argument */
290 int i;
291 int n;
293 if( argc!=3 ){
294 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
295 " ID N", 0);
296 return TCL_ERROR;
298 i = parse_thread_id(interp, argv[1]);
299 if( i<0 ) return TCL_ERROR;
300 if( !threadset[i].busy ){
301 Tcl_AppendResult(interp, "no such thread", 0);
302 return TCL_ERROR;
304 if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
305 thread_wait(&threadset[i]);
306 if( n<0 || n>=threadset[i].argc ){
307 Tcl_AppendResult(interp, "column number out of range", 0);
308 return TCL_ERROR;
310 Tcl_AppendResult(interp, threadset[i].argv[n], 0);
311 return TCL_OK;
315 ** Usage: thread_colname ID N
317 ** Wait on the most recent thread_step to complete, then return the
318 ** name of the N-th columns in the result set.
320 static int tcl_thread_colname(
321 void *NotUsed,
322 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
323 int argc, /* Number of arguments */
324 const char **argv /* Text of each argument */
326 int i;
327 int n;
329 if( argc!=3 ){
330 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
331 " ID N", 0);
332 return TCL_ERROR;
334 i = parse_thread_id(interp, argv[1]);
335 if( i<0 ) return TCL_ERROR;
336 if( !threadset[i].busy ){
337 Tcl_AppendResult(interp, "no such thread", 0);
338 return TCL_ERROR;
340 if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
341 thread_wait(&threadset[i]);
342 if( n<0 || n>=threadset[i].argc ){
343 Tcl_AppendResult(interp, "column number out of range", 0);
344 return TCL_ERROR;
346 Tcl_AppendResult(interp, threadset[i].colv[n], 0);
347 return TCL_OK;
351 ** Usage: thread_result ID
353 ** Wait on the most recent operation to complete, then return the
354 ** result code from that operation.
356 static int tcl_thread_result(
357 void *NotUsed,
358 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
359 int argc, /* Number of arguments */
360 const char **argv /* Text of each argument */
362 int i;
363 const char *zName;
365 if( argc!=2 ){
366 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
367 " ID", 0);
368 return TCL_ERROR;
370 i = parse_thread_id(interp, argv[1]);
371 if( i<0 ) return TCL_ERROR;
372 if( !threadset[i].busy ){
373 Tcl_AppendResult(interp, "no such thread", 0);
374 return TCL_ERROR;
376 thread_wait(&threadset[i]);
377 zName = sqlite3ErrName(threadset[i].rc);
378 Tcl_AppendResult(interp, zName, 0);
379 return TCL_OK;
383 ** Usage: thread_error ID
385 ** Wait on the most recent operation to complete, then return the
386 ** error string.
388 static int tcl_thread_error(
389 void *NotUsed,
390 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
391 int argc, /* Number of arguments */
392 const char **argv /* Text of each argument */
394 int i;
396 if( argc!=2 ){
397 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
398 " ID", 0);
399 return TCL_ERROR;
401 i = parse_thread_id(interp, argv[1]);
402 if( i<0 ) return TCL_ERROR;
403 if( !threadset[i].busy ){
404 Tcl_AppendResult(interp, "no such thread", 0);
405 return TCL_ERROR;
407 thread_wait(&threadset[i]);
408 Tcl_AppendResult(interp, threadset[i].zErr, 0);
409 return TCL_OK;
413 ** This procedure runs in the thread to compile an SQL statement.
415 static void do_compile(Thread *p){
416 if( p->db==0 ){
417 p->zErr = p->zStaticErr = "no database is open";
418 p->rc = SQLITE_ERROR;
419 return;
421 if( p->pStmt ){
422 sqlite3_finalize(p->pStmt);
423 p->pStmt = 0;
425 p->rc = sqlite3_prepare(p->db, p->zArg, -1, &p->pStmt, 0);
429 ** Usage: thread_compile ID SQL
431 ** Compile a new virtual machine.
433 static int tcl_thread_compile(
434 void *NotUsed,
435 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
436 int argc, /* Number of arguments */
437 const char **argv /* Text of each argument */
439 int i;
440 if( argc!=3 ){
441 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
442 " ID SQL", 0);
443 return TCL_ERROR;
445 i = parse_thread_id(interp, argv[1]);
446 if( i<0 ) return TCL_ERROR;
447 if( !threadset[i].busy ){
448 Tcl_AppendResult(interp, "no such thread", 0);
449 return TCL_ERROR;
451 thread_wait(&threadset[i]);
452 threadset[i].xOp = do_compile;
453 sqlite3_free(threadset[i].zArg);
454 threadset[i].zArg = sqlite3_mprintf("%s", argv[2]);
455 threadset[i].opnum++;
456 return TCL_OK;
460 ** This procedure runs in the thread to step the virtual machine.
462 static void do_step(Thread *p){
463 int i;
464 if( p->pStmt==0 ){
465 p->zErr = p->zStaticErr = "no virtual machine available";
466 p->rc = SQLITE_ERROR;
467 return;
469 p->rc = sqlite3_step(p->pStmt);
470 if( p->rc==SQLITE_ROW ){
471 p->argc = sqlite3_column_count(p->pStmt);
472 for(i=0; i<sqlite3_data_count(p->pStmt); i++){
473 p->argv[i] = (char*)sqlite3_column_text(p->pStmt, i);
475 for(i=0; i<p->argc; i++){
476 p->colv[i] = sqlite3_column_name(p->pStmt, i);
482 ** Usage: thread_step ID
484 ** Advance the virtual machine by one step
486 static int tcl_thread_step(
487 void *NotUsed,
488 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
489 int argc, /* Number of arguments */
490 const char **argv /* Text of each argument */
492 int i;
493 if( argc!=2 ){
494 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
495 " IDL", 0);
496 return TCL_ERROR;
498 i = parse_thread_id(interp, argv[1]);
499 if( i<0 ) return TCL_ERROR;
500 if( !threadset[i].busy ){
501 Tcl_AppendResult(interp, "no such thread", 0);
502 return TCL_ERROR;
504 thread_wait(&threadset[i]);
505 threadset[i].xOp = do_step;
506 threadset[i].opnum++;
507 return TCL_OK;
511 ** This procedure runs in the thread to finalize a virtual machine.
513 static void do_finalize(Thread *p){
514 if( p->pStmt==0 ){
515 p->zErr = p->zStaticErr = "no virtual machine available";
516 p->rc = SQLITE_ERROR;
517 return;
519 p->rc = sqlite3_finalize(p->pStmt);
520 p->pStmt = 0;
524 ** Usage: thread_finalize ID
526 ** Finalize the virtual machine.
528 static int tcl_thread_finalize(
529 void *NotUsed,
530 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
531 int argc, /* Number of arguments */
532 const char **argv /* Text of each argument */
534 int i;
535 if( argc!=2 ){
536 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
537 " IDL", 0);
538 return TCL_ERROR;
540 i = parse_thread_id(interp, argv[1]);
541 if( i<0 ) return TCL_ERROR;
542 if( !threadset[i].busy ){
543 Tcl_AppendResult(interp, "no such thread", 0);
544 return TCL_ERROR;
546 thread_wait(&threadset[i]);
547 threadset[i].xOp = do_finalize;
548 sqlite3_free(threadset[i].zArg);
549 threadset[i].zArg = 0;
550 threadset[i].opnum++;
551 return TCL_OK;
555 ** Usage: thread_swap ID ID
557 ** Interchange the sqlite* pointer between two threads.
559 static int tcl_thread_swap(
560 void *NotUsed,
561 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
562 int argc, /* Number of arguments */
563 const char **argv /* Text of each argument */
565 int i, j;
566 sqlite3 *temp;
567 if( argc!=3 ){
568 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
569 " ID1 ID2", 0);
570 return TCL_ERROR;
572 i = parse_thread_id(interp, argv[1]);
573 if( i<0 ) return TCL_ERROR;
574 if( !threadset[i].busy ){
575 Tcl_AppendResult(interp, "no such thread", 0);
576 return TCL_ERROR;
578 thread_wait(&threadset[i]);
579 j = parse_thread_id(interp, argv[2]);
580 if( j<0 ) return TCL_ERROR;
581 if( !threadset[j].busy ){
582 Tcl_AppendResult(interp, "no such thread", 0);
583 return TCL_ERROR;
585 thread_wait(&threadset[j]);
586 temp = threadset[i].db;
587 threadset[i].db = threadset[j].db;
588 threadset[j].db = temp;
589 return TCL_OK;
593 ** Usage: thread_db_get ID
595 ** Return the database connection pointer for the given thread. Then
596 ** remove the pointer from the thread itself. Afterwards, the thread
597 ** can be stopped and the connection can be used by the main thread.
599 static int tcl_thread_db_get(
600 void *NotUsed,
601 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
602 int argc, /* Number of arguments */
603 const char **argv /* Text of each argument */
605 int i;
606 char zBuf[100];
607 extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);
608 if( argc!=2 ){
609 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
610 " ID", 0);
611 return TCL_ERROR;
613 i = parse_thread_id(interp, argv[1]);
614 if( i<0 ) return TCL_ERROR;
615 if( !threadset[i].busy ){
616 Tcl_AppendResult(interp, "no such thread", 0);
617 return TCL_ERROR;
619 thread_wait(&threadset[i]);
620 sqlite3TestMakePointerStr(interp, zBuf, threadset[i].db);
621 threadset[i].db = 0;
622 Tcl_AppendResult(interp, zBuf, (char*)0);
623 return TCL_OK;
627 ** Usage: thread_db_put ID DB
630 static int tcl_thread_db_put(
631 void *NotUsed,
632 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
633 int argc, /* Number of arguments */
634 const char **argv /* Text of each argument */
636 int i;
637 extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);
638 extern void *sqlite3TestTextToPtr(const char *);
639 if( argc!=3 ){
640 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
641 " ID DB", 0);
642 return TCL_ERROR;
644 i = parse_thread_id(interp, argv[1]);
645 if( i<0 ) return TCL_ERROR;
646 if( !threadset[i].busy ){
647 Tcl_AppendResult(interp, "no such thread", 0);
648 return TCL_ERROR;
650 thread_wait(&threadset[i]);
651 assert( !threadset[i].db );
652 threadset[i].db = (sqlite3*)sqlite3TestTextToPtr(argv[2]);
653 return TCL_OK;
657 ** Usage: thread_stmt_get ID
659 ** Return the database stmt pointer for the given thread. Then
660 ** remove the pointer from the thread itself.
662 static int tcl_thread_stmt_get(
663 void *NotUsed,
664 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
665 int argc, /* Number of arguments */
666 const char **argv /* Text of each argument */
668 int i;
669 char zBuf[100];
670 extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);
671 if( argc!=2 ){
672 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
673 " ID", 0);
674 return TCL_ERROR;
676 i = parse_thread_id(interp, argv[1]);
677 if( i<0 ) return TCL_ERROR;
678 if( !threadset[i].busy ){
679 Tcl_AppendResult(interp, "no such thread", 0);
680 return TCL_ERROR;
682 thread_wait(&threadset[i]);
683 sqlite3TestMakePointerStr(interp, zBuf, threadset[i].pStmt);
684 threadset[i].pStmt = 0;
685 Tcl_AppendResult(interp, zBuf, (char*)0);
686 return TCL_OK;
690 ** Register commands with the TCL interpreter.
692 int Sqlitetest4_Init(Tcl_Interp *interp){
693 static struct {
694 char *zName;
695 Tcl_CmdProc *xProc;
696 } aCmd[] = {
697 { "thread_create", (Tcl_CmdProc*)tcl_thread_create },
698 { "thread_wait", (Tcl_CmdProc*)tcl_thread_wait },
699 { "thread_halt", (Tcl_CmdProc*)tcl_thread_halt },
700 { "thread_argc", (Tcl_CmdProc*)tcl_thread_argc },
701 { "thread_argv", (Tcl_CmdProc*)tcl_thread_argv },
702 { "thread_colname", (Tcl_CmdProc*)tcl_thread_colname },
703 { "thread_result", (Tcl_CmdProc*)tcl_thread_result },
704 { "thread_error", (Tcl_CmdProc*)tcl_thread_error },
705 { "thread_compile", (Tcl_CmdProc*)tcl_thread_compile },
706 { "thread_step", (Tcl_CmdProc*)tcl_thread_step },
707 { "thread_finalize", (Tcl_CmdProc*)tcl_thread_finalize },
708 { "thread_swap", (Tcl_CmdProc*)tcl_thread_swap },
709 { "thread_db_get", (Tcl_CmdProc*)tcl_thread_db_get },
710 { "thread_db_put", (Tcl_CmdProc*)tcl_thread_db_put },
711 { "thread_stmt_get", (Tcl_CmdProc*)tcl_thread_stmt_get },
713 int i;
715 for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
716 Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
718 return TCL_OK;
720 #else
721 int Sqlitetest4_Init(Tcl_Interp *interp){ return TCL_OK; }
722 #endif /* SQLITE_OS_UNIX */