4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
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 client/server version of the SQLite library.
13 ** Derived from test4.c.
15 #include "sqliteInt.h"
19 ** This test only works on UNIX with a SQLITE_THREADSAFE build that includes
20 ** the SQLITE_SERVER option.
22 #if defined(SQLITE_SERVER) && !defined(SQLITE_OMIT_SHARED_CACHE) && \
23 SQLITE_OS_UNIX && SQLITE_THREADSAFE
32 ** Interfaces defined in server.c
34 int sqlite3_client_open(const char*, sqlite3
**);
35 int sqlite3_client_prepare(sqlite3
*,const char*,int,
36 sqlite3_stmt
**,const char**);
37 int sqlite3_client_step(sqlite3_stmt
*);
38 int sqlite3_client_reset(sqlite3_stmt
*);
39 int sqlite3_client_finalize(sqlite3_stmt
*);
40 int sqlite3_client_close(sqlite3
*);
41 int sqlite3_server_start(void);
42 int sqlite3_server_stop(void);
43 void sqlite3_server_start2(int *pnDecr
);
46 ** Each thread is controlled by an instance of the following
49 typedef struct Thread Thread
;
51 /* The first group of fields are writable by the supervisor thread
52 ** and read-only to the client threads
54 char *zFilename
; /* Name of database file */
55 void (*xOp
)(Thread
*); /* next operation to do */
56 char *zArg
; /* argument usable by xOp */
57 volatile int opnum
; /* Operation number */
58 volatile int busy
; /* True if this thread is in use */
60 /* The next group of fields are writable by the client threads
61 ** but read-only to the superviser thread.
63 volatile int completed
; /* Number of operations completed */
64 sqlite3
*db
; /* Open database */
65 sqlite3_stmt
*pStmt
; /* Pending operation */
66 char *zErr
; /* operation error */
67 char *zStaticErr
; /* Static error message */
68 int rc
; /* operation return code */
69 int argc
; /* number of columns in result */
70 const char *argv
[100]; /* result columns */
71 const char *colv
[100]; /* result column names */
73 /* Initialized to 1 by the supervisor thread when the client is
74 ** created, and then deemed read-only to the supervisor thread.
75 ** Is set to 0 by the server thread belonging to this client
76 ** just before it exits.
78 int nServer
; /* Number of server threads running */
82 ** There can be as many as 26 threads running at once. Each is named
83 ** by a capital letter: A, B, C, ..., Y, Z.
86 static Thread threadset
[N_THREAD
];
89 ** The main loop for a thread. Threads use busy waiting.
91 static void *client_main(void *pArg
){
92 Thread
*p
= (Thread
*)pArg
;
94 sqlite3_client_close(p
->db
);
96 sqlite3_client_open(p
->zFilename
, &p
->db
);
97 if( SQLITE_OK
!=sqlite3_errcode(p
->db
) ){
98 p
->zErr
= strdup(sqlite3_errmsg(p
->db
));
99 sqlite3_client_close(p
->db
);
104 while( p
->opnum
<=p
->completed
) sched_yield();
106 if( p
->zErr
&& p
->zErr
!=p
->zStaticErr
){
107 sqlite3_free(p
->zErr
);
112 while( p
->opnum
<=p
->completed
) sched_yield();
115 sqlite3_client_finalize(p
->pStmt
);
119 sqlite3_client_close(p
->db
);
122 if( p
->zErr
&& p
->zErr
!=p
->zStaticErr
){
123 sqlite3_free(p
->zErr
);
127 #ifndef SQLITE_OMIT_DEPRECATED
128 sqlite3_thread_cleanup();
134 ** Get a thread ID which is an upper case letter. Return the index.
135 ** If the argument is not a valid thread ID put an error message in
136 ** the interpreter and return -1.
138 static int parse_client_id(Tcl_Interp
*interp
, const char *zArg
){
139 if( zArg
==0 || zArg
[0]==0 || zArg
[1]!=0 || !isupper((unsigned char)zArg
[0]) ){
140 Tcl_AppendResult(interp
, "thread ID must be an upper case letter", 0);
143 return zArg
[0] - 'A';
147 ** Usage: client_create NAME FILENAME
149 ** NAME should be an upper case letter. Start the thread running with
150 ** an open connection to the given database.
152 static int tcl_client_create(
154 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
155 int argc
, /* Number of arguments */
156 const char **argv
/* Text of each argument */
163 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
167 i
= parse_client_id(interp
, argv
[1]);
168 if( i
<0 ) return TCL_ERROR
;
169 if( threadset
[i
].busy
){
170 Tcl_AppendResult(interp
, "thread ", argv
[1], " is already running", 0);
173 threadset
[i
].busy
= 1;
174 sqlite3_free(threadset
[i
].zFilename
);
175 threadset
[i
].zFilename
= sqlite3_mprintf("%s", argv
[2]);
176 threadset
[i
].opnum
= 1;
177 threadset
[i
].completed
= 0;
178 rc
= pthread_create(&x
, 0, client_main
, &threadset
[i
]);
180 Tcl_AppendResult(interp
, "failed to create the thread", 0);
181 sqlite3_free(threadset
[i
].zFilename
);
182 threadset
[i
].busy
= 0;
186 if( threadset
[i
].nServer
==0 ){
187 threadset
[i
].nServer
= 1;
188 sqlite3_server_start2(&threadset
[i
].nServer
);
194 ** Wait for a thread to reach its idle state.
196 static void client_wait(Thread
*p
){
197 while( p
->opnum
>p
->completed
) sched_yield();
201 ** Usage: client_wait ID
203 ** Wait on thread ID to reach its idle state.
205 static int tcl_client_wait(
207 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
208 int argc
, /* Number of arguments */
209 const char **argv
/* Text of each argument */
214 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
218 i
= parse_client_id(interp
, argv
[1]);
219 if( i
<0 ) return TCL_ERROR
;
220 if( !threadset
[i
].busy
){
221 Tcl_AppendResult(interp
, "no such thread", 0);
224 client_wait(&threadset
[i
]);
231 static void stop_thread(Thread
*p
){
236 sqlite3_free(p
->zArg
);
238 sqlite3_free(p
->zFilename
);
244 ** Usage: client_halt ID
246 ** Cause a client thread to shut itself down. Wait for the shutdown to be
247 ** completed. If ID is "*" then stop all client threads.
249 static int tcl_client_halt(
251 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
252 int argc
, /* Number of arguments */
253 const char **argv
/* Text of each argument */
258 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
262 if( argv
[1][0]=='*' && argv
[1][1]==0 ){
263 for(i
=0; i
<N_THREAD
; i
++){
264 if( threadset
[i
].busy
){
265 stop_thread(&threadset
[i
]);
269 i
= parse_client_id(interp
, argv
[1]);
270 if( i
<0 ) return TCL_ERROR
;
271 if( !threadset
[i
].busy
){
272 Tcl_AppendResult(interp
, "no such thread", 0);
275 stop_thread(&threadset
[i
]);
278 /* If no client threads are still running, also stop the server */
279 for(i
=0; i
<N_THREAD
&& threadset
[i
].busy
==0; i
++){}
281 sqlite3_server_stop();
283 for(i
=0; i
<N_THREAD
&& threadset
[i
].nServer
==0; i
++);
284 if( i
==N_THREAD
) break;
292 ** Usage: client_argc ID
294 ** Wait on the most recent client_step to complete, then return the
295 ** number of columns in the result set.
297 static int tcl_client_argc(
299 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
300 int argc
, /* Number of arguments */
301 const char **argv
/* Text of each argument */
307 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
311 i
= parse_client_id(interp
, argv
[1]);
312 if( i
<0 ) return TCL_ERROR
;
313 if( !threadset
[i
].busy
){
314 Tcl_AppendResult(interp
, "no such thread", 0);
317 client_wait(&threadset
[i
]);
318 sprintf(zBuf
, "%d", threadset
[i
].argc
);
319 Tcl_AppendResult(interp
, zBuf
, 0);
324 ** Usage: client_argv ID N
326 ** Wait on the most recent client_step to complete, then return the
327 ** value of the N-th columns in the result set.
329 static int tcl_client_argv(
331 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
332 int argc
, /* Number of arguments */
333 const char **argv
/* Text of each argument */
339 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
343 i
= parse_client_id(interp
, argv
[1]);
344 if( i
<0 ) return TCL_ERROR
;
345 if( !threadset
[i
].busy
){
346 Tcl_AppendResult(interp
, "no such thread", 0);
349 if( Tcl_GetInt(interp
, argv
[2], &n
) ) return TCL_ERROR
;
350 client_wait(&threadset
[i
]);
351 if( n
<0 || n
>=threadset
[i
].argc
){
352 Tcl_AppendResult(interp
, "column number out of range", 0);
355 Tcl_AppendResult(interp
, threadset
[i
].argv
[n
], 0);
360 ** Usage: client_colname ID N
362 ** Wait on the most recent client_step to complete, then return the
363 ** name of the N-th columns in the result set.
365 static int tcl_client_colname(
367 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
368 int argc
, /* Number of arguments */
369 const char **argv
/* Text of each argument */
375 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
379 i
= parse_client_id(interp
, argv
[1]);
380 if( i
<0 ) return TCL_ERROR
;
381 if( !threadset
[i
].busy
){
382 Tcl_AppendResult(interp
, "no such thread", 0);
385 if( Tcl_GetInt(interp
, argv
[2], &n
) ) return TCL_ERROR
;
386 client_wait(&threadset
[i
]);
387 if( n
<0 || n
>=threadset
[i
].argc
){
388 Tcl_AppendResult(interp
, "column number out of range", 0);
391 Tcl_AppendResult(interp
, threadset
[i
].colv
[n
], 0);
395 extern const char *sqlite3ErrName(int);
398 ** Usage: client_result ID
400 ** Wait on the most recent operation to complete, then return the
401 ** result code from that operation.
403 static int tcl_client_result(
405 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
406 int argc
, /* Number of arguments */
407 const char **argv
/* Text of each argument */
413 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
417 i
= parse_client_id(interp
, argv
[1]);
418 if( i
<0 ) return TCL_ERROR
;
419 if( !threadset
[i
].busy
){
420 Tcl_AppendResult(interp
, "no such thread", 0);
423 client_wait(&threadset
[i
]);
424 zName
= sqlite3ErrName(threadset
[i
].rc
);
425 Tcl_AppendResult(interp
, zName
, 0);
430 ** Usage: client_error ID
432 ** Wait on the most recent operation to complete, then return the
435 static int tcl_client_error(
437 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
438 int argc
, /* Number of arguments */
439 const char **argv
/* Text of each argument */
444 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
448 i
= parse_client_id(interp
, argv
[1]);
449 if( i
<0 ) return TCL_ERROR
;
450 if( !threadset
[i
].busy
){
451 Tcl_AppendResult(interp
, "no such thread", 0);
454 client_wait(&threadset
[i
]);
455 Tcl_AppendResult(interp
, threadset
[i
].zErr
, 0);
460 ** This procedure runs in the thread to compile an SQL statement.
462 static void do_compile(Thread
*p
){
464 p
->zErr
= p
->zStaticErr
= "no database is open";
465 p
->rc
= SQLITE_ERROR
;
469 sqlite3_client_finalize(p
->pStmt
);
472 p
->rc
= sqlite3_client_prepare(p
->db
, p
->zArg
, -1, &p
->pStmt
, 0);
476 ** Usage: client_compile ID SQL
478 ** Compile a new virtual machine.
480 static int tcl_client_compile(
482 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
483 int argc
, /* Number of arguments */
484 const char **argv
/* Text of each argument */
488 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
492 i
= parse_client_id(interp
, argv
[1]);
493 if( i
<0 ) return TCL_ERROR
;
494 if( !threadset
[i
].busy
){
495 Tcl_AppendResult(interp
, "no such thread", 0);
498 client_wait(&threadset
[i
]);
499 threadset
[i
].xOp
= do_compile
;
500 sqlite3_free(threadset
[i
].zArg
);
501 threadset
[i
].zArg
= sqlite3_mprintf("%s", argv
[2]);
502 threadset
[i
].opnum
++;
507 ** This procedure runs in the thread to step the virtual machine.
509 static void do_step(Thread
*p
){
512 p
->zErr
= p
->zStaticErr
= "no virtual machine available";
513 p
->rc
= SQLITE_ERROR
;
516 p
->rc
= sqlite3_client_step(p
->pStmt
);
517 if( p
->rc
==SQLITE_ROW
){
518 p
->argc
= sqlite3_column_count(p
->pStmt
);
519 for(i
=0; i
<sqlite3_data_count(p
->pStmt
); i
++){
520 p
->argv
[i
] = (char*)sqlite3_column_text(p
->pStmt
, i
);
522 for(i
=0; i
<p
->argc
; i
++){
523 p
->colv
[i
] = sqlite3_column_name(p
->pStmt
, i
);
529 ** Usage: client_step ID
531 ** Advance the virtual machine by one step
533 static int tcl_client_step(
535 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
536 int argc
, /* Number of arguments */
537 const char **argv
/* Text of each argument */
541 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
545 i
= parse_client_id(interp
, argv
[1]);
546 if( i
<0 ) return TCL_ERROR
;
547 if( !threadset
[i
].busy
){
548 Tcl_AppendResult(interp
, "no such thread", 0);
551 client_wait(&threadset
[i
]);
552 threadset
[i
].xOp
= do_step
;
553 threadset
[i
].opnum
++;
558 ** This procedure runs in the thread to finalize a virtual machine.
560 static void do_finalize(Thread
*p
){
562 p
->zErr
= p
->zStaticErr
= "no virtual machine available";
563 p
->rc
= SQLITE_ERROR
;
566 p
->rc
= sqlite3_client_finalize(p
->pStmt
);
571 ** Usage: client_finalize ID
573 ** Finalize the virtual machine.
575 static int tcl_client_finalize(
577 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
578 int argc
, /* Number of arguments */
579 const char **argv
/* Text of each argument */
583 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
587 i
= parse_client_id(interp
, argv
[1]);
588 if( i
<0 ) return TCL_ERROR
;
589 if( !threadset
[i
].busy
){
590 Tcl_AppendResult(interp
, "no such thread", 0);
593 client_wait(&threadset
[i
]);
594 threadset
[i
].xOp
= do_finalize
;
595 sqlite3_free(threadset
[i
].zArg
);
596 threadset
[i
].zArg
= 0;
597 threadset
[i
].opnum
++;
602 ** This procedure runs in the thread to reset a virtual machine.
604 static void do_reset(Thread
*p
){
606 p
->zErr
= p
->zStaticErr
= "no virtual machine available";
607 p
->rc
= SQLITE_ERROR
;
610 p
->rc
= sqlite3_client_reset(p
->pStmt
);
615 ** Usage: client_reset ID
617 ** Finalize the virtual machine.
619 static int tcl_client_reset(
621 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
622 int argc
, /* Number of arguments */
623 const char **argv
/* Text of each argument */
627 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
631 i
= parse_client_id(interp
, argv
[1]);
632 if( i
<0 ) return TCL_ERROR
;
633 if( !threadset
[i
].busy
){
634 Tcl_AppendResult(interp
, "no such thread", 0);
637 client_wait(&threadset
[i
]);
638 threadset
[i
].xOp
= do_reset
;
639 sqlite3_free(threadset
[i
].zArg
);
640 threadset
[i
].zArg
= 0;
641 threadset
[i
].opnum
++;
646 ** Usage: client_swap ID ID
648 ** Interchange the sqlite* pointer between two threads.
650 static int tcl_client_swap(
652 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
653 int argc
, /* Number of arguments */
654 const char **argv
/* Text of each argument */
659 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
663 i
= parse_client_id(interp
, argv
[1]);
664 if( i
<0 ) return TCL_ERROR
;
665 if( !threadset
[i
].busy
){
666 Tcl_AppendResult(interp
, "no such thread", 0);
669 client_wait(&threadset
[i
]);
670 j
= parse_client_id(interp
, argv
[2]);
671 if( j
<0 ) return TCL_ERROR
;
672 if( !threadset
[j
].busy
){
673 Tcl_AppendResult(interp
, "no such thread", 0);
676 client_wait(&threadset
[j
]);
677 temp
= threadset
[i
].db
;
678 threadset
[i
].db
= threadset
[j
].db
;
679 threadset
[j
].db
= temp
;
684 ** Register commands with the TCL interpreter.
686 int Sqlitetest7_Init(Tcl_Interp
*interp
){
691 { "client_create", (Tcl_CmdProc
*)tcl_client_create
},
692 { "client_wait", (Tcl_CmdProc
*)tcl_client_wait
},
693 { "client_halt", (Tcl_CmdProc
*)tcl_client_halt
},
694 { "client_argc", (Tcl_CmdProc
*)tcl_client_argc
},
695 { "client_argv", (Tcl_CmdProc
*)tcl_client_argv
},
696 { "client_colname", (Tcl_CmdProc
*)tcl_client_colname
},
697 { "client_result", (Tcl_CmdProc
*)tcl_client_result
},
698 { "client_error", (Tcl_CmdProc
*)tcl_client_error
},
699 { "client_compile", (Tcl_CmdProc
*)tcl_client_compile
},
700 { "client_step", (Tcl_CmdProc
*)tcl_client_step
},
701 { "client_reset", (Tcl_CmdProc
*)tcl_client_reset
},
702 { "client_finalize", (Tcl_CmdProc
*)tcl_client_finalize
},
703 { "client_swap", (Tcl_CmdProc
*)tcl_client_swap
},
707 for(i
=0; i
<sizeof(aCmd
)/sizeof(aCmd
[0]); i
++){
708 Tcl_CreateCommand(interp
, aCmd
[i
].zName
, aCmd
[i
].xProc
, 0, 0);
713 int Sqlitetest7_Init(Tcl_Interp
*interp
){ return TCL_OK
; }
714 #endif /* SQLITE_OS_UNIX */