source: trunk/src/3rdparty/sqlite/test4.c@ 205

Last change on this file since 205 was 205, checked in by rudi, 14 years ago

Added SQLite 2.8.17 sources. This allows to build at least one of the sql drivers / plugins.

File size: 17.7 KB
Line 
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.
10**
11*************************************************************************
12** Code for testing the the SQLite library in a multithreaded environment.
13**
14** $Id: test4.c,v 1.3 2004/04/23 17:04:45 drh Exp $
15*/
16#include "sqliteInt.h"
17#include "tcl.h"
18#if defined(OS_UNIX) && OS_UNIX==1 && defined(THREADSAFE) && THREADSAFE==1
19#include <stdlib.h>
20#include <string.h>
21#include <pthread.h>
22#include <sched.h>
23#include <ctype.h>
24
25/*
26** Each thread is controlled by an instance of the following
27** structure.
28*/
29typedef struct Thread Thread;
30struct 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 */
38
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 sqlite *db; /* Open database */
43 sqlite_vm *vm; /* 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; /* result columns */
49 const char **colv; /* result column names */
50};
51
52/*
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.
55*/
56#define N_THREAD 26
57static Thread threadset[N_THREAD];
58
59
60/*
61** The main loop for a thread. Threads use busy waiting.
62*/
63static void *thread_main(void *pArg){
64 Thread *p = (Thread*)pArg;
65 if( p->db ){
66 sqlite_close(p->db);
67 }
68 p->db = sqlite_open(p->zFilename, 0, &p->zErr);
69 p->vm = 0;
70 p->completed = 1;
71 while( p->opnum<=p->completed ) sched_yield();
72 while( p->xOp ){
73 if( p->zErr && p->zErr!=p->zStaticErr ){
74 sqlite_freemem(p->zErr);
75 p->zErr = 0;
76 }
77 (*p->xOp)(p);
78 p->completed++;
79 while( p->opnum<=p->completed ) sched_yield();
80 }
81 if( p->vm ){
82 sqlite_finalize(p->vm, 0);
83 p->vm = 0;
84 }
85 if( p->db ){
86 sqlite_close(p->db);
87 p->db = 0;
88 }
89 if( p->zErr && p->zErr!=p->zStaticErr ){
90 sqlite_freemem(p->zErr);
91 p->zErr = 0;
92 }
93 p->completed++;
94 return 0;
95}
96
97/*
98** Get a thread ID which is an upper case letter. Return the index.
99** If the argument is not a valid thread ID put an error message in
100** the interpreter and return -1.
101*/
102static int parse_thread_id(Tcl_Interp *interp, const char *zArg){
103 if( zArg==0 || zArg[0]==0 || zArg[1]!=0 || !isupper(zArg[0]) ){
104 Tcl_AppendResult(interp, "thread ID must be an upper case letter", 0);
105 return -1;
106 }
107 return zArg[0] - 'A';
108}
109
110/*
111** Usage: thread_create NAME FILENAME
112**
113** NAME should be an upper case letter. Start the thread running with
114** an open connection to the given database.
115*/
116static int tcl_thread_create(
117 void *NotUsed,
118 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
119 int argc, /* Number of arguments */
120 const char **argv /* Text of each argument */
121){
122 int i;
123 pthread_t x;
124 int rc;
125
126 if( argc!=3 ){
127 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
128 " ID FILENAME", 0);
129 return TCL_ERROR;
130 }
131 i = parse_thread_id(interp, argv[1]);
132 if( i<0 ) return TCL_ERROR;
133 if( threadset[i].busy ){
134 Tcl_AppendResult(interp, "thread ", argv[1], " is already running", 0);
135 return TCL_ERROR;
136 }
137 threadset[i].busy = 1;
138 sqliteFree(threadset[i].zFilename);
139 threadset[i].zFilename = sqliteStrDup(argv[2]);
140 threadset[i].opnum = 1;
141 threadset[i].completed = 0;
142 rc = pthread_create(&x, 0, thread_main, &threadset[i]);
143 if( rc ){
144 Tcl_AppendResult(interp, "failed to create the thread", 0);
145 sqliteFree(threadset[i].zFilename);
146 threadset[i].busy = 0;
147 return TCL_ERROR;
148 }
149 pthread_detach(x);
150 return TCL_OK;
151}
152
153/*
154** Wait for a thread to reach its idle state.
155*/
156static void thread_wait(Thread *p){
157 while( p->opnum>p->completed ) sched_yield();
158}
159
160/*
161** Usage: thread_wait ID
162**
163** Wait on thread ID to reach its idle state.
164*/
165static int tcl_thread_wait(
166 void *NotUsed,
167 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
168 int argc, /* Number of arguments */
169 const char **argv /* Text of each argument */
170){
171 int i;
172
173 if( argc!=2 ){
174 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
175 " ID", 0);
176 return TCL_ERROR;
177 }
178 i = parse_thread_id(interp, argv[1]);
179 if( i<0 ) return TCL_ERROR;
180 if( !threadset[i].busy ){
181 Tcl_AppendResult(interp, "no such thread", 0);
182 return TCL_ERROR;
183 }
184 thread_wait(&threadset[i]);
185 return TCL_OK;
186}
187
188/*
189** Stop a thread.
190*/
191static void stop_thread(Thread *p){
192 thread_wait(p);
193 p->xOp = 0;
194 p->opnum++;
195 thread_wait(p);
196 sqliteFree(p->zArg);
197 p->zArg = 0;
198 sqliteFree(p->zFilename);
199 p->zFilename = 0;
200 p->busy = 0;
201}
202
203/*
204** Usage: thread_halt ID
205**
206** Cause a thread to shut itself down. Wait for the shutdown to be
207** completed. If ID is "*" then stop all threads.
208*/
209static int tcl_thread_halt(
210 void *NotUsed,
211 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
212 int argc, /* Number of arguments */
213 const char **argv /* Text of each argument */
214){
215 int i;
216
217 if( argc!=2 ){
218 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
219 " ID", 0);
220 return TCL_ERROR;
221 }
222 if( argv[1][0]=='*' && argv[1][1]==0 ){
223 for(i=0; i<N_THREAD; i++){
224 if( threadset[i].busy ) stop_thread(&threadset[i]);
225 }
226 }else{
227 i = parse_thread_id(interp, argv[1]);
228 if( i<0 ) return TCL_ERROR;
229 if( !threadset[i].busy ){
230 Tcl_AppendResult(interp, "no such thread", 0);
231 return TCL_ERROR;
232 }
233 stop_thread(&threadset[i]);
234 }
235 return TCL_OK;
236}
237
238/*
239** Usage: thread_argc ID
240**
241** Wait on the most recent thread_step to complete, then return the
242** number of columns in the result set.
243*/
244static int tcl_thread_argc(
245 void *NotUsed,
246 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
247 int argc, /* Number of arguments */
248 const char **argv /* Text of each argument */
249){
250 int i;
251 char zBuf[100];
252
253 if( argc!=2 ){
254 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
255 " ID", 0);
256 return TCL_ERROR;
257 }
258 i = parse_thread_id(interp, argv[1]);
259 if( i<0 ) return TCL_ERROR;
260 if( !threadset[i].busy ){
261 Tcl_AppendResult(interp, "no such thread", 0);
262 return TCL_ERROR;
263 }
264 thread_wait(&threadset[i]);
265 sprintf(zBuf, "%d", threadset[i].argc);
266 Tcl_AppendResult(interp, zBuf, 0);
267 return TCL_OK;
268}
269
270/*
271** Usage: thread_argv ID N
272**
273** Wait on the most recent thread_step to complete, then return the
274** value of the N-th columns in the result set.
275*/
276static int tcl_thread_argv(
277 void *NotUsed,
278 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
279 int argc, /* Number of arguments */
280 const char **argv /* Text of each argument */
281){
282 int i;
283 int n;
284
285 if( argc!=3 ){
286 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
287 " ID N", 0);
288 return TCL_ERROR;
289 }
290 i = parse_thread_id(interp, argv[1]);
291 if( i<0 ) return TCL_ERROR;
292 if( !threadset[i].busy ){
293 Tcl_AppendResult(interp, "no such thread", 0);
294 return TCL_ERROR;
295 }
296 if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
297 thread_wait(&threadset[i]);
298 if( n<0 || n>=threadset[i].argc ){
299 Tcl_AppendResult(interp, "column number out of range", 0);
300 return TCL_ERROR;
301 }
302 Tcl_AppendResult(interp, threadset[i].argv[n], 0);
303 return TCL_OK;
304}
305
306/*
307** Usage: thread_colname ID N
308**
309** Wait on the most recent thread_step to complete, then return the
310** name of the N-th columns in the result set.
311*/
312static int tcl_thread_colname(
313 void *NotUsed,
314 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
315 int argc, /* Number of arguments */
316 const char **argv /* Text of each argument */
317){
318 int i;
319 int n;
320
321 if( argc!=3 ){
322 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
323 " ID N", 0);
324 return TCL_ERROR;
325 }
326 i = parse_thread_id(interp, argv[1]);
327 if( i<0 ) return TCL_ERROR;
328 if( !threadset[i].busy ){
329 Tcl_AppendResult(interp, "no such thread", 0);
330 return TCL_ERROR;
331 }
332 if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
333 thread_wait(&threadset[i]);
334 if( n<0 || n>=threadset[i].argc ){
335 Tcl_AppendResult(interp, "column number out of range", 0);
336 return TCL_ERROR;
337 }
338 Tcl_AppendResult(interp, threadset[i].colv[n], 0);
339 return TCL_OK;
340}
341
342/*
343** Usage: thread_result ID
344**
345** Wait on the most recent operation to complete, then return the
346** result code from that operation.
347*/
348static int tcl_thread_result(
349 void *NotUsed,
350 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
351 int argc, /* Number of arguments */
352 const char **argv /* Text of each argument */
353){
354 int i;
355 const char *zName;
356
357 if( argc!=2 ){
358 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
359 " ID", 0);
360 return TCL_ERROR;
361 }
362 i = parse_thread_id(interp, argv[1]);
363 if( i<0 ) return TCL_ERROR;
364 if( !threadset[i].busy ){
365 Tcl_AppendResult(interp, "no such thread", 0);
366 return TCL_ERROR;
367 }
368 thread_wait(&threadset[i]);
369 switch( threadset[i].rc ){
370 case SQLITE_OK: zName = "SQLITE_OK"; break;
371 case SQLITE_ERROR: zName = "SQLITE_ERROR"; break;
372 case SQLITE_INTERNAL: zName = "SQLITE_INTERNAL"; break;
373 case SQLITE_PERM: zName = "SQLITE_PERM"; break;
374 case SQLITE_ABORT: zName = "SQLITE_ABORT"; break;
375 case SQLITE_BUSY: zName = "SQLITE_BUSY"; break;
376 case SQLITE_LOCKED: zName = "SQLITE_LOCKED"; break;
377 case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break;
378 case SQLITE_READONLY: zName = "SQLITE_READONLY"; break;
379 case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break;
380 case SQLITE_IOERR: zName = "SQLITE_IOERR"; break;
381 case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break;
382 case SQLITE_NOTFOUND: zName = "SQLITE_NOTFOUND"; break;
383 case SQLITE_FULL: zName = "SQLITE_FULL"; break;
384 case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break;
385 case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break;
386 case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break;
387 case SQLITE_SCHEMA: zName = "SQLITE_SCHEMA"; break;
388 case SQLITE_TOOBIG: zName = "SQLITE_TOOBIG"; break;
389 case SQLITE_CONSTRAINT: zName = "SQLITE_CONSTRAINT"; break;
390 case SQLITE_MISMATCH: zName = "SQLITE_MISMATCH"; break;
391 case SQLITE_MISUSE: zName = "SQLITE_MISUSE"; break;
392 case SQLITE_NOLFS: zName = "SQLITE_NOLFS"; break;
393 case SQLITE_AUTH: zName = "SQLITE_AUTH"; break;
394 case SQLITE_FORMAT: zName = "SQLITE_FORMAT"; break;
395 case SQLITE_RANGE: zName = "SQLITE_RANGE"; break;
396 case SQLITE_ROW: zName = "SQLITE_ROW"; break;
397 case SQLITE_DONE: zName = "SQLITE_DONE"; break;
398 default: zName = "SQLITE_Unknown"; break;
399 }
400 Tcl_AppendResult(interp, zName, 0);
401 return TCL_OK;
402}
403
404/*
405** Usage: thread_error ID
406**
407** Wait on the most recent operation to complete, then return the
408** error string.
409*/
410static int tcl_thread_error(
411 void *NotUsed,
412 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
413 int argc, /* Number of arguments */
414 const char **argv /* Text of each argument */
415){
416 int i;
417
418 if( argc!=2 ){
419 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
420 " ID", 0);
421 return TCL_ERROR;
422 }
423 i = parse_thread_id(interp, argv[1]);
424 if( i<0 ) return TCL_ERROR;
425 if( !threadset[i].busy ){
426 Tcl_AppendResult(interp, "no such thread", 0);
427 return TCL_ERROR;
428 }
429 thread_wait(&threadset[i]);
430 Tcl_AppendResult(interp, threadset[i].zErr, 0);
431 return TCL_OK;
432}
433
434/*
435** This procedure runs in the thread to compile an SQL statement.
436*/
437static void do_compile(Thread *p){
438 if( p->db==0 ){
439 p->zErr = p->zStaticErr = "no database is open";
440 p->rc = SQLITE_ERROR;
441 return;
442 }
443 if( p->vm ){
444 sqlite_finalize(p->vm, 0);
445 p->vm = 0;
446 }
447 p->rc = sqlite_compile(p->db, p->zArg, 0, &p->vm, &p->zErr);
448}
449
450/*
451** Usage: thread_compile ID SQL
452**
453** Compile a new virtual machine.
454*/
455static int tcl_thread_compile(
456 void *NotUsed,
457 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
458 int argc, /* Number of arguments */
459 const char **argv /* Text of each argument */
460){
461 int i;
462 if( argc!=3 ){
463 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
464 " ID SQL", 0);
465 return TCL_ERROR;
466 }
467 i = parse_thread_id(interp, argv[1]);
468 if( i<0 ) return TCL_ERROR;
469 if( !threadset[i].busy ){
470 Tcl_AppendResult(interp, "no such thread", 0);
471 return TCL_ERROR;
472 }
473 thread_wait(&threadset[i]);
474 threadset[i].xOp = do_compile;
475 sqliteFree(threadset[i].zArg);
476 threadset[i].zArg = sqliteStrDup(argv[2]);
477 threadset[i].opnum++;
478 return TCL_OK;
479}
480
481/*
482** This procedure runs in the thread to step the virtual machine.
483*/
484static void do_step(Thread *p){
485 if( p->vm==0 ){
486 p->zErr = p->zStaticErr = "no virtual machine available";
487 p->rc = SQLITE_ERROR;
488 return;
489 }
490 p->rc = sqlite_step(p->vm, &p->argc, &p->argv, &p->colv);
491}
492
493/*
494** Usage: thread_step ID
495**
496** Advance the virtual machine by one step
497*/
498static int tcl_thread_step(
499 void *NotUsed,
500 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
501 int argc, /* Number of arguments */
502 const char **argv /* Text of each argument */
503){
504 int i;
505 if( argc!=2 ){
506 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
507 " IDL", 0);
508 return TCL_ERROR;
509 }
510 i = parse_thread_id(interp, argv[1]);
511 if( i<0 ) return TCL_ERROR;
512 if( !threadset[i].busy ){
513 Tcl_AppendResult(interp, "no such thread", 0);
514 return TCL_ERROR;
515 }
516 thread_wait(&threadset[i]);
517 threadset[i].xOp = do_step;
518 threadset[i].opnum++;
519 return TCL_OK;
520}
521
522/*
523** This procedure runs in the thread to finalize a virtual machine.
524*/
525static void do_finalize(Thread *p){
526 if( p->vm==0 ){
527 p->zErr = p->zStaticErr = "no virtual machine available";
528 p->rc = SQLITE_ERROR;
529 return;
530 }
531 p->rc = sqlite_finalize(p->vm, &p->zErr);
532 p->vm = 0;
533}
534
535/*
536** Usage: thread_finalize ID
537**
538** Finalize the virtual machine.
539*/
540static int tcl_thread_finalize(
541 void *NotUsed,
542 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
543 int argc, /* Number of arguments */
544 const char **argv /* Text of each argument */
545){
546 int i;
547 if( argc!=2 ){
548 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
549 " IDL", 0);
550 return TCL_ERROR;
551 }
552 i = parse_thread_id(interp, argv[1]);
553 if( i<0 ) return TCL_ERROR;
554 if( !threadset[i].busy ){
555 Tcl_AppendResult(interp, "no such thread", 0);
556 return TCL_ERROR;
557 }
558 thread_wait(&threadset[i]);
559 threadset[i].xOp = do_finalize;
560 sqliteFree(threadset[i].zArg);
561 threadset[i].zArg = 0;
562 threadset[i].opnum++;
563 return TCL_OK;
564}
565
566/*
567** Usage: thread_swap ID ID
568**
569** Interchange the sqlite* pointer between two threads.
570*/
571static int tcl_thread_swap(
572 void *NotUsed,
573 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
574 int argc, /* Number of arguments */
575 const char **argv /* Text of each argument */
576){
577 int i, j;
578 sqlite *temp;
579 if( argc!=3 ){
580 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
581 " ID1 ID2", 0);
582 return TCL_ERROR;
583 }
584 i = parse_thread_id(interp, argv[1]);
585 if( i<0 ) return TCL_ERROR;
586 if( !threadset[i].busy ){
587 Tcl_AppendResult(interp, "no such thread", 0);
588 return TCL_ERROR;
589 }
590 thread_wait(&threadset[i]);
591 j = parse_thread_id(interp, argv[2]);
592 if( j<0 ) return TCL_ERROR;
593 if( !threadset[j].busy ){
594 Tcl_AppendResult(interp, "no such thread", 0);
595 return TCL_ERROR;
596 }
597 thread_wait(&threadset[j]);
598 temp = threadset[i].db;
599 threadset[i].db = threadset[j].db;
600 threadset[j].db = temp;
601 return TCL_OK;
602}
603
604/*
605** Register commands with the TCL interpreter.
606*/
607int Sqlitetest4_Init(Tcl_Interp *interp){
608 static struct {
609 char *zName;
610 Tcl_CmdProc *xProc;
611 } aCmd[] = {
612 { "thread_create", (Tcl_CmdProc*)tcl_thread_create },
613 { "thread_wait", (Tcl_CmdProc*)tcl_thread_wait },
614 { "thread_halt", (Tcl_CmdProc*)tcl_thread_halt },
615 { "thread_argc", (Tcl_CmdProc*)tcl_thread_argc },
616 { "thread_argv", (Tcl_CmdProc*)tcl_thread_argv },
617 { "thread_colname", (Tcl_CmdProc*)tcl_thread_colname },
618 { "thread_result", (Tcl_CmdProc*)tcl_thread_result },
619 { "thread_error", (Tcl_CmdProc*)tcl_thread_error },
620 { "thread_compile", (Tcl_CmdProc*)tcl_thread_compile },
621 { "thread_step", (Tcl_CmdProc*)tcl_thread_step },
622 { "thread_finalize", (Tcl_CmdProc*)tcl_thread_finalize },
623 { "thread_swap", (Tcl_CmdProc*)tcl_thread_swap },
624 };
625 int i;
626
627 for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
628 Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
629 }
630 return TCL_OK;
631}
632#else
633int Sqlitetest4_Init(Tcl_Interp *interp){ return TCL_OK; }
634#endif /* OS_UNIX */
Note: See TracBrowser for help on using the repository browser.