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);
45 ** Each thread is controlled by an instance of the following
48 typedef struct Thread Thread
;
50 /* The first group of fields are writable by the supervisor thread
51 ** and read-only to the client threads
53 char *zFilename
; /* Name of database file */
54 void (*xOp
)(Thread
*); /* next operation to do */
55 char *zArg
; /* argument usable by xOp */
56 volatile int opnum
; /* Operation number */
57 volatile int busy
; /* True if this thread is in use */
59 /* The next group of fields are writable by the client threads
60 ** but read-only to the superviser thread.
62 volatile int completed
; /* Number of operations completed */
63 sqlite3
*db
; /* Open database */
64 sqlite3_stmt
*pStmt
; /* Pending operation */
65 char *zErr
; /* operation error */
66 char *zStaticErr
; /* Static error message */
67 int rc
; /* operation return code */
68 int argc
; /* number of columns in result */
69 const char *argv
[100]; /* result columns */
70 const char *colv
[100]; /* result column names */
74 ** There can be as many as 26 threads running at once. Each is named
75 ** by a capital letter: A, B, C, ..., Y, Z.
78 static Thread threadset
[N_THREAD
];
81 ** The main loop for a thread. Threads use busy waiting.
83 static void *client_main(void *pArg
){
84 Thread
*p
= (Thread
*)pArg
;
86 sqlite3_client_close(p
->db
);
88 sqlite3_client_open(p
->zFilename
, &p
->db
);
89 if( SQLITE_OK
!=sqlite3_errcode(p
->db
) ){
90 p
->zErr
= strdup(sqlite3_errmsg(p
->db
));
91 sqlite3_client_close(p
->db
);
96 while( p
->opnum
<=p
->completed
) sched_yield();
98 if( p
->zErr
&& p
->zErr
!=p
->zStaticErr
){
99 sqlite3_free(p
->zErr
);
104 while( p
->opnum
<=p
->completed
) sched_yield();
107 sqlite3_client_finalize(p
->pStmt
);
111 sqlite3_client_close(p
->db
);
114 if( p
->zErr
&& p
->zErr
!=p
->zStaticErr
){
115 sqlite3_free(p
->zErr
);
119 #ifndef SQLITE_OMIT_DEPRECATED
120 sqlite3_thread_cleanup();
126 ** Get a thread ID which is an upper case letter. Return the index.
127 ** If the argument is not a valid thread ID put an error message in
128 ** the interpreter and return -1.
130 static int parse_client_id(Tcl_Interp
*interp
, const char *zArg
){
131 if( zArg
==0 || zArg
[0]==0 || zArg
[1]!=0 || !isupper((unsigned char)zArg
[0]) ){
132 Tcl_AppendResult(interp
, "thread ID must be an upper case letter", 0);
135 return zArg
[0] - 'A';
139 ** Usage: client_create NAME FILENAME
141 ** NAME should be an upper case letter. Start the thread running with
142 ** an open connection to the given database.
144 static int tcl_client_create(
146 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
147 int argc
, /* Number of arguments */
148 const char **argv
/* Text of each argument */
155 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
159 i
= parse_client_id(interp
, argv
[1]);
160 if( i
<0 ) return TCL_ERROR
;
161 if( threadset
[i
].busy
){
162 Tcl_AppendResult(interp
, "thread ", argv
[1], " is already running", 0);
165 threadset
[i
].busy
= 1;
166 sqlite3_free(threadset
[i
].zFilename
);
167 threadset
[i
].zFilename
= sqlite3_mprintf("%s", argv
[2]);
168 threadset
[i
].opnum
= 1;
169 threadset
[i
].completed
= 0;
170 rc
= pthread_create(&x
, 0, client_main
, &threadset
[i
]);
172 Tcl_AppendResult(interp
, "failed to create the thread", 0);
173 sqlite3_free(threadset
[i
].zFilename
);
174 threadset
[i
].busy
= 0;
178 sqlite3_server_start();
183 ** Wait for a thread to reach its idle state.
185 static void client_wait(Thread
*p
){
186 while( p
->opnum
>p
->completed
) sched_yield();
190 ** Usage: client_wait ID
192 ** Wait on thread ID to reach its idle state.
194 static int tcl_client_wait(
196 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
197 int argc
, /* Number of arguments */
198 const char **argv
/* Text of each argument */
203 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
207 i
= parse_client_id(interp
, argv
[1]);
208 if( i
<0 ) return TCL_ERROR
;
209 if( !threadset
[i
].busy
){
210 Tcl_AppendResult(interp
, "no such thread", 0);
213 client_wait(&threadset
[i
]);
220 static void stop_thread(Thread
*p
){
225 sqlite3_free(p
->zArg
);
227 sqlite3_free(p
->zFilename
);
233 ** Usage: client_halt ID
235 ** Cause a client thread to shut itself down. Wait for the shutdown to be
236 ** completed. If ID is "*" then stop all client threads.
238 static int tcl_client_halt(
240 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
241 int argc
, /* Number of arguments */
242 const char **argv
/* Text of each argument */
247 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
251 if( argv
[1][0]=='*' && argv
[1][1]==0 ){
252 for(i
=0; i
<N_THREAD
; i
++){
253 if( threadset
[i
].busy
){
254 stop_thread(&threadset
[i
]);
258 i
= parse_client_id(interp
, argv
[1]);
259 if( i
<0 ) return TCL_ERROR
;
260 if( !threadset
[i
].busy
){
261 Tcl_AppendResult(interp
, "no such thread", 0);
264 stop_thread(&threadset
[i
]);
267 /* If no client threads are still running, also stop the server */
268 for(i
=0; i
<N_THREAD
&& threadset
[i
].busy
==0; i
++){}
270 sqlite3_server_stop();
276 ** Usage: client_argc ID
278 ** Wait on the most recent client_step to complete, then return the
279 ** number of columns in the result set.
281 static int tcl_client_argc(
283 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
284 int argc
, /* Number of arguments */
285 const char **argv
/* Text of each argument */
291 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
295 i
= parse_client_id(interp
, argv
[1]);
296 if( i
<0 ) return TCL_ERROR
;
297 if( !threadset
[i
].busy
){
298 Tcl_AppendResult(interp
, "no such thread", 0);
301 client_wait(&threadset
[i
]);
302 sprintf(zBuf
, "%d", threadset
[i
].argc
);
303 Tcl_AppendResult(interp
, zBuf
, 0);
308 ** Usage: client_argv ID N
310 ** Wait on the most recent client_step to complete, then return the
311 ** value of the N-th columns in the result set.
313 static int tcl_client_argv(
315 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
316 int argc
, /* Number of arguments */
317 const char **argv
/* Text of each argument */
323 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
327 i
= parse_client_id(interp
, argv
[1]);
328 if( i
<0 ) return TCL_ERROR
;
329 if( !threadset
[i
].busy
){
330 Tcl_AppendResult(interp
, "no such thread", 0);
333 if( Tcl_GetInt(interp
, argv
[2], &n
) ) return TCL_ERROR
;
334 client_wait(&threadset
[i
]);
335 if( n
<0 || n
>=threadset
[i
].argc
){
336 Tcl_AppendResult(interp
, "column number out of range", 0);
339 Tcl_AppendResult(interp
, threadset
[i
].argv
[n
], 0);
344 ** Usage: client_colname ID N
346 ** Wait on the most recent client_step to complete, then return the
347 ** name of the N-th columns in the result set.
349 static int tcl_client_colname(
351 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
352 int argc
, /* Number of arguments */
353 const char **argv
/* Text of each argument */
359 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
363 i
= parse_client_id(interp
, argv
[1]);
364 if( i
<0 ) return TCL_ERROR
;
365 if( !threadset
[i
].busy
){
366 Tcl_AppendResult(interp
, "no such thread", 0);
369 if( Tcl_GetInt(interp
, argv
[2], &n
) ) return TCL_ERROR
;
370 client_wait(&threadset
[i
]);
371 if( n
<0 || n
>=threadset
[i
].argc
){
372 Tcl_AppendResult(interp
, "column number out of range", 0);
375 Tcl_AppendResult(interp
, threadset
[i
].colv
[n
], 0);
380 ** Usage: client_result ID
382 ** Wait on the most recent operation to complete, then return the
383 ** result code from that operation.
385 static int tcl_client_result(
387 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
388 int argc
, /* Number of arguments */
389 const char **argv
/* Text of each argument */
395 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
399 i
= parse_client_id(interp
, argv
[1]);
400 if( i
<0 ) return TCL_ERROR
;
401 if( !threadset
[i
].busy
){
402 Tcl_AppendResult(interp
, "no such thread", 0);
405 client_wait(&threadset
[i
]);
406 switch( threadset
[i
].rc
){
407 case SQLITE_OK
: zName
= "SQLITE_OK"; break;
408 case SQLITE_ERROR
: zName
= "SQLITE_ERROR"; break;
409 case SQLITE_PERM
: zName
= "SQLITE_PERM"; break;
410 case SQLITE_ABORT
: zName
= "SQLITE_ABORT"; break;
411 case SQLITE_BUSY
: zName
= "SQLITE_BUSY"; break;
412 case SQLITE_LOCKED
: zName
= "SQLITE_LOCKED"; break;
413 case SQLITE_NOMEM
: zName
= "SQLITE_NOMEM"; break;
414 case SQLITE_READONLY
: zName
= "SQLITE_READONLY"; break;
415 case SQLITE_INTERRUPT
: zName
= "SQLITE_INTERRUPT"; break;
416 case SQLITE_IOERR
: zName
= "SQLITE_IOERR"; break;
417 case SQLITE_CORRUPT
: zName
= "SQLITE_CORRUPT"; break;
418 case SQLITE_FULL
: zName
= "SQLITE_FULL"; break;
419 case SQLITE_CANTOPEN
: zName
= "SQLITE_CANTOPEN"; break;
420 case SQLITE_PROTOCOL
: zName
= "SQLITE_PROTOCOL"; break;
421 case SQLITE_EMPTY
: zName
= "SQLITE_EMPTY"; break;
422 case SQLITE_SCHEMA
: zName
= "SQLITE_SCHEMA"; break;
423 case SQLITE_CONSTRAINT
: zName
= "SQLITE_CONSTRAINT"; break;
424 case SQLITE_MISMATCH
: zName
= "SQLITE_MISMATCH"; break;
425 case SQLITE_MISUSE
: zName
= "SQLITE_MISUSE"; break;
426 case SQLITE_NOLFS
: zName
= "SQLITE_NOLFS"; break;
427 case SQLITE_AUTH
: zName
= "SQLITE_AUTH"; break;
428 case SQLITE_FORMAT
: zName
= "SQLITE_FORMAT"; break;
429 case SQLITE_RANGE
: zName
= "SQLITE_RANGE"; break;
430 case SQLITE_ROW
: zName
= "SQLITE_ROW"; break;
431 case SQLITE_DONE
: zName
= "SQLITE_DONE"; break;
432 default: zName
= "SQLITE_Unknown"; break;
434 Tcl_AppendResult(interp
, zName
, 0);
439 ** Usage: client_error ID
441 ** Wait on the most recent operation to complete, then return the
444 static int tcl_client_error(
446 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
447 int argc
, /* Number of arguments */
448 const char **argv
/* Text of each argument */
453 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
457 i
= parse_client_id(interp
, argv
[1]);
458 if( i
<0 ) return TCL_ERROR
;
459 if( !threadset
[i
].busy
){
460 Tcl_AppendResult(interp
, "no such thread", 0);
463 client_wait(&threadset
[i
]);
464 Tcl_AppendResult(interp
, threadset
[i
].zErr
, 0);
469 ** This procedure runs in the thread to compile an SQL statement.
471 static void do_compile(Thread
*p
){
473 p
->zErr
= p
->zStaticErr
= "no database is open";
474 p
->rc
= SQLITE_ERROR
;
478 sqlite3_client_finalize(p
->pStmt
);
481 p
->rc
= sqlite3_client_prepare(p
->db
, p
->zArg
, -1, &p
->pStmt
, 0);
485 ** Usage: client_compile ID SQL
487 ** Compile a new virtual machine.
489 static int tcl_client_compile(
491 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
492 int argc
, /* Number of arguments */
493 const char **argv
/* Text of each argument */
497 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
501 i
= parse_client_id(interp
, argv
[1]);
502 if( i
<0 ) return TCL_ERROR
;
503 if( !threadset
[i
].busy
){
504 Tcl_AppendResult(interp
, "no such thread", 0);
507 client_wait(&threadset
[i
]);
508 threadset
[i
].xOp
= do_compile
;
509 sqlite3_free(threadset
[i
].zArg
);
510 threadset
[i
].zArg
= sqlite3_mprintf("%s", argv
[2]);
511 threadset
[i
].opnum
++;
516 ** This procedure runs in the thread to step the virtual machine.
518 static void do_step(Thread
*p
){
521 p
->zErr
= p
->zStaticErr
= "no virtual machine available";
522 p
->rc
= SQLITE_ERROR
;
525 p
->rc
= sqlite3_client_step(p
->pStmt
);
526 if( p
->rc
==SQLITE_ROW
){
527 p
->argc
= sqlite3_column_count(p
->pStmt
);
528 for(i
=0; i
<sqlite3_data_count(p
->pStmt
); i
++){
529 p
->argv
[i
] = (char*)sqlite3_column_text(p
->pStmt
, i
);
531 for(i
=0; i
<p
->argc
; i
++){
532 p
->colv
[i
] = sqlite3_column_name(p
->pStmt
, i
);
538 ** Usage: client_step ID
540 ** Advance the virtual machine by one step
542 static int tcl_client_step(
544 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
545 int argc
, /* Number of arguments */
546 const char **argv
/* Text of each argument */
550 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
554 i
= parse_client_id(interp
, argv
[1]);
555 if( i
<0 ) return TCL_ERROR
;
556 if( !threadset
[i
].busy
){
557 Tcl_AppendResult(interp
, "no such thread", 0);
560 client_wait(&threadset
[i
]);
561 threadset
[i
].xOp
= do_step
;
562 threadset
[i
].opnum
++;
567 ** This procedure runs in the thread to finalize a virtual machine.
569 static void do_finalize(Thread
*p
){
571 p
->zErr
= p
->zStaticErr
= "no virtual machine available";
572 p
->rc
= SQLITE_ERROR
;
575 p
->rc
= sqlite3_client_finalize(p
->pStmt
);
580 ** Usage: client_finalize ID
582 ** Finalize the virtual machine.
584 static int tcl_client_finalize(
586 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
587 int argc
, /* Number of arguments */
588 const char **argv
/* Text of each argument */
592 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
596 i
= parse_client_id(interp
, argv
[1]);
597 if( i
<0 ) return TCL_ERROR
;
598 if( !threadset
[i
].busy
){
599 Tcl_AppendResult(interp
, "no such thread", 0);
602 client_wait(&threadset
[i
]);
603 threadset
[i
].xOp
= do_finalize
;
604 sqlite3_free(threadset
[i
].zArg
);
605 threadset
[i
].zArg
= 0;
606 threadset
[i
].opnum
++;
611 ** This procedure runs in the thread to reset a virtual machine.
613 static void do_reset(Thread
*p
){
615 p
->zErr
= p
->zStaticErr
= "no virtual machine available";
616 p
->rc
= SQLITE_ERROR
;
619 p
->rc
= sqlite3_client_reset(p
->pStmt
);
624 ** Usage: client_reset ID
626 ** Finalize the virtual machine.
628 static int tcl_client_reset(
630 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
631 int argc
, /* Number of arguments */
632 const char **argv
/* Text of each argument */
636 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
640 i
= parse_client_id(interp
, argv
[1]);
641 if( i
<0 ) return TCL_ERROR
;
642 if( !threadset
[i
].busy
){
643 Tcl_AppendResult(interp
, "no such thread", 0);
646 client_wait(&threadset
[i
]);
647 threadset
[i
].xOp
= do_reset
;
648 sqlite3_free(threadset
[i
].zArg
);
649 threadset
[i
].zArg
= 0;
650 threadset
[i
].opnum
++;
655 ** Usage: client_swap ID ID
657 ** Interchange the sqlite* pointer between two threads.
659 static int tcl_client_swap(
661 Tcl_Interp
*interp
, /* The TCL interpreter that invoked this command */
662 int argc
, /* Number of arguments */
663 const char **argv
/* Text of each argument */
668 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
672 i
= parse_client_id(interp
, argv
[1]);
673 if( i
<0 ) return TCL_ERROR
;
674 if( !threadset
[i
].busy
){
675 Tcl_AppendResult(interp
, "no such thread", 0);
678 client_wait(&threadset
[i
]);
679 j
= parse_client_id(interp
, argv
[2]);
680 if( j
<0 ) return TCL_ERROR
;
681 if( !threadset
[j
].busy
){
682 Tcl_AppendResult(interp
, "no such thread", 0);
685 client_wait(&threadset
[j
]);
686 temp
= threadset
[i
].db
;
687 threadset
[i
].db
= threadset
[j
].db
;
688 threadset
[j
].db
= temp
;
693 ** Register commands with the TCL interpreter.
695 int Sqlitetest7_Init(Tcl_Interp
*interp
){
700 { "client_create", (Tcl_CmdProc
*)tcl_client_create
},
701 { "client_wait", (Tcl_CmdProc
*)tcl_client_wait
},
702 { "client_halt", (Tcl_CmdProc
*)tcl_client_halt
},
703 { "client_argc", (Tcl_CmdProc
*)tcl_client_argc
},
704 { "client_argv", (Tcl_CmdProc
*)tcl_client_argv
},
705 { "client_colname", (Tcl_CmdProc
*)tcl_client_colname
},
706 { "client_result", (Tcl_CmdProc
*)tcl_client_result
},
707 { "client_error", (Tcl_CmdProc
*)tcl_client_error
},
708 { "client_compile", (Tcl_CmdProc
*)tcl_client_compile
},
709 { "client_step", (Tcl_CmdProc
*)tcl_client_step
},
710 { "client_reset", (Tcl_CmdProc
*)tcl_client_reset
},
711 { "client_finalize", (Tcl_CmdProc
*)tcl_client_finalize
},
712 { "client_swap", (Tcl_CmdProc
*)tcl_client_swap
},
716 for(i
=0; i
<sizeof(aCmd
)/sizeof(aCmd
[0]); i
++){
717 Tcl_CreateCommand(interp
, aCmd
[i
].zName
, aCmd
[i
].xProc
, 0, 0);
722 int Sqlitetest7_Init(Tcl_Interp
*interp
){ return TCL_OK
; }
723 #endif /* SQLITE_OS_UNIX */