blob: d57e4b826b843b0a4ea8c5ab5442e8c031f6d7af [file] [log] [blame]
drh29c636b2006-01-09 23:40:25 +00001/*
2** 2006 January 09
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 client/server version of the SQLite library.
13** Derived from test4.c.
drh29c636b2006-01-09 23:40:25 +000014*/
15#include "sqliteInt.h"
mistachkin52b1dbb2016-07-28 14:37:04 +000016#if defined(INCLUDE_SQLITE_TCL_H)
17# include "sqlite_tcl.h"
18#else
19# include "tcl.h"
20#endif
drh29c636b2006-01-09 23:40:25 +000021
22/*
drhd677b3d2007-08-20 22:48:41 +000023** This test only works on UNIX with a SQLITE_THREADSAFE build that includes
drh29c636b2006-01-09 23:40:25 +000024** the SQLITE_SERVER option.
25*/
danielk197720e987a2007-10-05 15:04:12 +000026#if defined(SQLITE_SERVER) && !defined(SQLITE_OMIT_SHARED_CACHE) && \
shaneh3a2d29f2011-04-04 21:48:01 +000027 SQLITE_OS_UNIX && SQLITE_THREADSAFE
drh29c636b2006-01-09 23:40:25 +000028
29#include <stdlib.h>
30#include <string.h>
31#include <pthread.h>
32#include <sched.h>
33#include <ctype.h>
34
35/*
36** Interfaces defined in server.c
37*/
38int sqlite3_client_open(const char*, sqlite3**);
39int sqlite3_client_prepare(sqlite3*,const char*,int,
40 sqlite3_stmt**,const char**);
41int sqlite3_client_step(sqlite3_stmt*);
42int sqlite3_client_reset(sqlite3_stmt*);
43int sqlite3_client_finalize(sqlite3_stmt*);
44int sqlite3_client_close(sqlite3*);
45int sqlite3_server_start(void);
46int sqlite3_server_stop(void);
dancfe111b2013-10-15 15:35:27 +000047void sqlite3_server_start2(int *pnDecr);
drh29c636b2006-01-09 23:40:25 +000048
49/*
50** Each thread is controlled by an instance of the following
51** structure.
52*/
53typedef struct Thread Thread;
54struct Thread {
55 /* The first group of fields are writable by the supervisor thread
56 ** and read-only to the client threads
57 */
58 char *zFilename; /* Name of database file */
59 void (*xOp)(Thread*); /* next operation to do */
60 char *zArg; /* argument usable by xOp */
61 volatile int opnum; /* Operation number */
62 volatile int busy; /* True if this thread is in use */
63
64 /* The next group of fields are writable by the client threads
65 ** but read-only to the superviser thread.
66 */
67 volatile int completed; /* Number of operations completed */
68 sqlite3 *db; /* Open database */
69 sqlite3_stmt *pStmt; /* Pending operation */
70 char *zErr; /* operation error */
71 char *zStaticErr; /* Static error message */
72 int rc; /* operation return code */
73 int argc; /* number of columns in result */
74 const char *argv[100]; /* result columns */
75 const char *colv[100]; /* result column names */
dancfe111b2013-10-15 15:35:27 +000076
77 /* Initialized to 1 by the supervisor thread when the client is
78 ** created, and then deemed read-only to the supervisor thread.
79 ** Is set to 0 by the server thread belonging to this client
80 ** just before it exits.
81 */
82 int nServer; /* Number of server threads running */
drh29c636b2006-01-09 23:40:25 +000083};
84
85/*
86** There can be as many as 26 threads running at once. Each is named
87** by a capital letter: A, B, C, ..., Y, Z.
88*/
89#define N_THREAD 26
90static Thread threadset[N_THREAD];
91
92/*
93** The main loop for a thread. Threads use busy waiting.
94*/
95static void *client_main(void *pArg){
96 Thread *p = (Thread*)pArg;
97 if( p->db ){
98 sqlite3_client_close(p->db);
99 }
100 sqlite3_client_open(p->zFilename, &p->db);
101 if( SQLITE_OK!=sqlite3_errcode(p->db) ){
102 p->zErr = strdup(sqlite3_errmsg(p->db));
103 sqlite3_client_close(p->db);
104 p->db = 0;
105 }
106 p->pStmt = 0;
107 p->completed = 1;
108 while( p->opnum<=p->completed ) sched_yield();
109 while( p->xOp ){
110 if( p->zErr && p->zErr!=p->zStaticErr ){
111 sqlite3_free(p->zErr);
112 p->zErr = 0;
113 }
114 (*p->xOp)(p);
115 p->completed++;
116 while( p->opnum<=p->completed ) sched_yield();
117 }
118 if( p->pStmt ){
119 sqlite3_client_finalize(p->pStmt);
120 p->pStmt = 0;
121 }
122 if( p->db ){
123 sqlite3_client_close(p->db);
124 p->db = 0;
125 }
126 if( p->zErr && p->zErr!=p->zStaticErr ){
127 sqlite3_free(p->zErr);
128 p->zErr = 0;
129 }
130 p->completed++;
shaneeec556d2008-10-12 00:27:53 +0000131#ifndef SQLITE_OMIT_DEPRECATED
drhb4bc7052006-01-11 23:40:33 +0000132 sqlite3_thread_cleanup();
shaneeec556d2008-10-12 00:27:53 +0000133#endif
drh29c636b2006-01-09 23:40:25 +0000134 return 0;
135}
136
137/*
138** Get a thread ID which is an upper case letter. Return the index.
139** If the argument is not a valid thread ID put an error message in
140** the interpreter and return -1.
141*/
142static int parse_client_id(Tcl_Interp *interp, const char *zArg){
143 if( zArg==0 || zArg[0]==0 || zArg[1]!=0 || !isupper((unsigned char)zArg[0]) ){
144 Tcl_AppendResult(interp, "thread ID must be an upper case letter", 0);
145 return -1;
146 }
147 return zArg[0] - 'A';
148}
149
150/*
151** Usage: client_create NAME FILENAME
152**
153** NAME should be an upper case letter. Start the thread running with
154** an open connection to the given database.
155*/
mistachkin7617e4a2016-07-28 17:11:20 +0000156static int SQLITE_TCLAPI tcl_client_create(
drh29c636b2006-01-09 23:40:25 +0000157 void *NotUsed,
158 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
159 int argc, /* Number of arguments */
160 const char **argv /* Text of each argument */
161){
162 int i;
163 pthread_t x;
164 int rc;
165
166 if( argc!=3 ){
167 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
168 " ID FILENAME", 0);
169 return TCL_ERROR;
170 }
171 i = parse_client_id(interp, argv[1]);
172 if( i<0 ) return TCL_ERROR;
173 if( threadset[i].busy ){
174 Tcl_AppendResult(interp, "thread ", argv[1], " is already running", 0);
175 return TCL_ERROR;
176 }
177 threadset[i].busy = 1;
drhcab5ed72007-08-22 11:41:18 +0000178 sqlite3_free(threadset[i].zFilename);
drhb9755982010-07-24 16:34:37 +0000179 threadset[i].zFilename = sqlite3_mprintf("%s", argv[2]);
drh29c636b2006-01-09 23:40:25 +0000180 threadset[i].opnum = 1;
181 threadset[i].completed = 0;
182 rc = pthread_create(&x, 0, client_main, &threadset[i]);
183 if( rc ){
184 Tcl_AppendResult(interp, "failed to create the thread", 0);
drhcab5ed72007-08-22 11:41:18 +0000185 sqlite3_free(threadset[i].zFilename);
drh29c636b2006-01-09 23:40:25 +0000186 threadset[i].busy = 0;
187 return TCL_ERROR;
188 }
189 pthread_detach(x);
dancfe111b2013-10-15 15:35:27 +0000190 if( threadset[i].nServer==0 ){
191 threadset[i].nServer = 1;
192 sqlite3_server_start2(&threadset[i].nServer);
193 }
drh29c636b2006-01-09 23:40:25 +0000194 return TCL_OK;
195}
196
197/*
198** Wait for a thread to reach its idle state.
199*/
200static void client_wait(Thread *p){
201 while( p->opnum>p->completed ) sched_yield();
202}
203
204/*
205** Usage: client_wait ID
206**
207** Wait on thread ID to reach its idle state.
208*/
mistachkin7617e4a2016-07-28 17:11:20 +0000209static int SQLITE_TCLAPI tcl_client_wait(
drh29c636b2006-01-09 23:40:25 +0000210 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 i = parse_client_id(interp, argv[1]);
223 if( i<0 ) return TCL_ERROR;
224 if( !threadset[i].busy ){
225 Tcl_AppendResult(interp, "no such thread", 0);
226 return TCL_ERROR;
227 }
228 client_wait(&threadset[i]);
229 return TCL_OK;
230}
231
232/*
233** Stop a thread.
234*/
235static void stop_thread(Thread *p){
236 client_wait(p);
237 p->xOp = 0;
238 p->opnum++;
239 client_wait(p);
drhcab5ed72007-08-22 11:41:18 +0000240 sqlite3_free(p->zArg);
drh29c636b2006-01-09 23:40:25 +0000241 p->zArg = 0;
drhcab5ed72007-08-22 11:41:18 +0000242 sqlite3_free(p->zFilename);
drh29c636b2006-01-09 23:40:25 +0000243 p->zFilename = 0;
244 p->busy = 0;
245}
246
247/*
248** Usage: client_halt ID
249**
250** Cause a client thread to shut itself down. Wait for the shutdown to be
251** completed. If ID is "*" then stop all client threads.
252*/
mistachkin7617e4a2016-07-28 17:11:20 +0000253static int SQLITE_TCLAPI tcl_client_halt(
drh29c636b2006-01-09 23:40:25 +0000254 void *NotUsed,
255 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
256 int argc, /* Number of arguments */
257 const char **argv /* Text of each argument */
258){
259 int i;
260
261 if( argc!=2 ){
262 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
263 " ID", 0);
264 return TCL_ERROR;
265 }
266 if( argv[1][0]=='*' && argv[1][1]==0 ){
267 for(i=0; i<N_THREAD; i++){
268 if( threadset[i].busy ){
269 stop_thread(&threadset[i]);
270 }
271 }
272 }else{
273 i = parse_client_id(interp, argv[1]);
274 if( i<0 ) return TCL_ERROR;
275 if( !threadset[i].busy ){
276 Tcl_AppendResult(interp, "no such thread", 0);
277 return TCL_ERROR;
278 }
279 stop_thread(&threadset[i]);
280 }
281
282 /* If no client threads are still running, also stop the server */
283 for(i=0; i<N_THREAD && threadset[i].busy==0; i++){}
284 if( i>=N_THREAD ){
285 sqlite3_server_stop();
dancfe111b2013-10-15 15:35:27 +0000286 while( 1 ){
287 for(i=0; i<N_THREAD && threadset[i].nServer==0; i++);
288 if( i==N_THREAD ) break;
289 sched_yield();
290 }
drh29c636b2006-01-09 23:40:25 +0000291 }
292 return TCL_OK;
293}
294
295/*
296** Usage: client_argc ID
297**
298** Wait on the most recent client_step to complete, then return the
299** number of columns in the result set.
300*/
mistachkin7617e4a2016-07-28 17:11:20 +0000301static int SQLITE_TCLAPI tcl_client_argc(
drh29c636b2006-01-09 23:40:25 +0000302 void *NotUsed,
303 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
304 int argc, /* Number of arguments */
305 const char **argv /* Text of each argument */
306){
307 int i;
308 char zBuf[100];
309
310 if( argc!=2 ){
311 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
312 " ID", 0);
313 return TCL_ERROR;
314 }
315 i = parse_client_id(interp, argv[1]);
316 if( i<0 ) return TCL_ERROR;
317 if( !threadset[i].busy ){
318 Tcl_AppendResult(interp, "no such thread", 0);
319 return TCL_ERROR;
320 }
321 client_wait(&threadset[i]);
drh65545b52015-01-19 00:35:53 +0000322 sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", threadset[i].argc);
drh29c636b2006-01-09 23:40:25 +0000323 Tcl_AppendResult(interp, zBuf, 0);
324 return TCL_OK;
325}
326
327/*
328** Usage: client_argv ID N
329**
330** Wait on the most recent client_step to complete, then return the
331** value of the N-th columns in the result set.
332*/
mistachkin7617e4a2016-07-28 17:11:20 +0000333static int SQLITE_TCLAPI tcl_client_argv(
drh29c636b2006-01-09 23:40:25 +0000334 void *NotUsed,
335 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
336 int argc, /* Number of arguments */
337 const char **argv /* Text of each argument */
338){
339 int i;
340 int n;
341
342 if( argc!=3 ){
343 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
344 " ID N", 0);
345 return TCL_ERROR;
346 }
347 i = parse_client_id(interp, argv[1]);
348 if( i<0 ) return TCL_ERROR;
349 if( !threadset[i].busy ){
350 Tcl_AppendResult(interp, "no such thread", 0);
351 return TCL_ERROR;
352 }
353 if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
354 client_wait(&threadset[i]);
355 if( n<0 || n>=threadset[i].argc ){
356 Tcl_AppendResult(interp, "column number out of range", 0);
357 return TCL_ERROR;
358 }
359 Tcl_AppendResult(interp, threadset[i].argv[n], 0);
360 return TCL_OK;
361}
362
363/*
364** Usage: client_colname ID N
365**
366** Wait on the most recent client_step to complete, then return the
367** name of the N-th columns in the result set.
368*/
mistachkin7617e4a2016-07-28 17:11:20 +0000369static int SQLITE_TCLAPI tcl_client_colname(
drh29c636b2006-01-09 23:40:25 +0000370 void *NotUsed,
371 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
372 int argc, /* Number of arguments */
373 const char **argv /* Text of each argument */
374){
375 int i;
376 int n;
377
378 if( argc!=3 ){
379 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
380 " ID N", 0);
381 return TCL_ERROR;
382 }
383 i = parse_client_id(interp, argv[1]);
384 if( i<0 ) return TCL_ERROR;
385 if( !threadset[i].busy ){
386 Tcl_AppendResult(interp, "no such thread", 0);
387 return TCL_ERROR;
388 }
389 if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
390 client_wait(&threadset[i]);
391 if( n<0 || n>=threadset[i].argc ){
392 Tcl_AppendResult(interp, "column number out of range", 0);
393 return TCL_ERROR;
394 }
395 Tcl_AppendResult(interp, threadset[i].colv[n], 0);
396 return TCL_OK;
397}
398
mistachkine84d8d32013-04-29 03:09:10 +0000399extern const char *sqlite3ErrName(int);
drhd040e762013-04-10 23:48:37 +0000400
drh29c636b2006-01-09 23:40:25 +0000401/*
402** Usage: client_result ID
403**
404** Wait on the most recent operation to complete, then return the
405** result code from that operation.
406*/
mistachkin7617e4a2016-07-28 17:11:20 +0000407static int SQLITE_TCLAPI tcl_client_result(
drh29c636b2006-01-09 23:40:25 +0000408 void *NotUsed,
409 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
410 int argc, /* Number of arguments */
411 const char **argv /* Text of each argument */
412){
413 int i;
414 const char *zName;
415
416 if( argc!=2 ){
417 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
418 " ID", 0);
419 return TCL_ERROR;
420 }
421 i = parse_client_id(interp, argv[1]);
422 if( i<0 ) return TCL_ERROR;
423 if( !threadset[i].busy ){
424 Tcl_AppendResult(interp, "no such thread", 0);
425 return TCL_ERROR;
426 }
427 client_wait(&threadset[i]);
mistachkine84d8d32013-04-29 03:09:10 +0000428 zName = sqlite3ErrName(threadset[i].rc);
drh29c636b2006-01-09 23:40:25 +0000429 Tcl_AppendResult(interp, zName, 0);
430 return TCL_OK;
431}
432
433/*
434** Usage: client_error ID
435**
436** Wait on the most recent operation to complete, then return the
437** error string.
438*/
mistachkin7617e4a2016-07-28 17:11:20 +0000439static int SQLITE_TCLAPI tcl_client_error(
drh29c636b2006-01-09 23:40:25 +0000440 void *NotUsed,
441 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
442 int argc, /* Number of arguments */
443 const char **argv /* Text of each argument */
444){
445 int i;
446
447 if( argc!=2 ){
448 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
449 " ID", 0);
450 return TCL_ERROR;
451 }
452 i = parse_client_id(interp, argv[1]);
453 if( i<0 ) return TCL_ERROR;
454 if( !threadset[i].busy ){
455 Tcl_AppendResult(interp, "no such thread", 0);
456 return TCL_ERROR;
457 }
458 client_wait(&threadset[i]);
459 Tcl_AppendResult(interp, threadset[i].zErr, 0);
460 return TCL_OK;
461}
462
463/*
464** This procedure runs in the thread to compile an SQL statement.
465*/
466static void do_compile(Thread *p){
467 if( p->db==0 ){
468 p->zErr = p->zStaticErr = "no database is open";
469 p->rc = SQLITE_ERROR;
470 return;
471 }
472 if( p->pStmt ){
473 sqlite3_client_finalize(p->pStmt);
474 p->pStmt = 0;
475 }
476 p->rc = sqlite3_client_prepare(p->db, p->zArg, -1, &p->pStmt, 0);
477}
478
479/*
480** Usage: client_compile ID SQL
481**
482** Compile a new virtual machine.
483*/
mistachkin7617e4a2016-07-28 17:11:20 +0000484static int SQLITE_TCLAPI tcl_client_compile(
drh29c636b2006-01-09 23:40:25 +0000485 void *NotUsed,
486 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
487 int argc, /* Number of arguments */
488 const char **argv /* Text of each argument */
489){
490 int i;
491 if( argc!=3 ){
492 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
493 " ID SQL", 0);
494 return TCL_ERROR;
495 }
496 i = parse_client_id(interp, argv[1]);
497 if( i<0 ) return TCL_ERROR;
498 if( !threadset[i].busy ){
499 Tcl_AppendResult(interp, "no such thread", 0);
500 return TCL_ERROR;
501 }
502 client_wait(&threadset[i]);
503 threadset[i].xOp = do_compile;
drhcab5ed72007-08-22 11:41:18 +0000504 sqlite3_free(threadset[i].zArg);
drhb9755982010-07-24 16:34:37 +0000505 threadset[i].zArg = sqlite3_mprintf("%s", argv[2]);
drh29c636b2006-01-09 23:40:25 +0000506 threadset[i].opnum++;
507 return TCL_OK;
508}
509
510/*
511** This procedure runs in the thread to step the virtual machine.
512*/
513static void do_step(Thread *p){
514 int i;
515 if( p->pStmt==0 ){
516 p->zErr = p->zStaticErr = "no virtual machine available";
517 p->rc = SQLITE_ERROR;
518 return;
519 }
520 p->rc = sqlite3_client_step(p->pStmt);
521 if( p->rc==SQLITE_ROW ){
522 p->argc = sqlite3_column_count(p->pStmt);
523 for(i=0; i<sqlite3_data_count(p->pStmt); i++){
drh24bd82c2006-01-20 17:56:32 +0000524 p->argv[i] = (char*)sqlite3_column_text(p->pStmt, i);
drh29c636b2006-01-09 23:40:25 +0000525 }
526 for(i=0; i<p->argc; i++){
527 p->colv[i] = sqlite3_column_name(p->pStmt, i);
528 }
529 }
530}
531
532/*
533** Usage: client_step ID
534**
535** Advance the virtual machine by one step
536*/
mistachkin7617e4a2016-07-28 17:11:20 +0000537static int SQLITE_TCLAPI tcl_client_step(
drh29c636b2006-01-09 23:40:25 +0000538 void *NotUsed,
539 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
540 int argc, /* Number of arguments */
541 const char **argv /* Text of each argument */
542){
543 int i;
544 if( argc!=2 ){
545 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
546 " IDL", 0);
547 return TCL_ERROR;
548 }
549 i = parse_client_id(interp, argv[1]);
550 if( i<0 ) return TCL_ERROR;
551 if( !threadset[i].busy ){
552 Tcl_AppendResult(interp, "no such thread", 0);
553 return TCL_ERROR;
554 }
555 client_wait(&threadset[i]);
556 threadset[i].xOp = do_step;
557 threadset[i].opnum++;
558 return TCL_OK;
559}
560
561/*
562** This procedure runs in the thread to finalize a virtual machine.
563*/
564static void do_finalize(Thread *p){
565 if( p->pStmt==0 ){
566 p->zErr = p->zStaticErr = "no virtual machine available";
567 p->rc = SQLITE_ERROR;
568 return;
569 }
570 p->rc = sqlite3_client_finalize(p->pStmt);
571 p->pStmt = 0;
572}
573
574/*
575** Usage: client_finalize ID
576**
577** Finalize the virtual machine.
578*/
mistachkin7617e4a2016-07-28 17:11:20 +0000579static int SQLITE_TCLAPI tcl_client_finalize(
drh29c636b2006-01-09 23:40:25 +0000580 void *NotUsed,
581 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
582 int argc, /* Number of arguments */
583 const char **argv /* Text of each argument */
584){
585 int i;
586 if( argc!=2 ){
587 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
588 " IDL", 0);
589 return TCL_ERROR;
590 }
591 i = parse_client_id(interp, argv[1]);
592 if( i<0 ) return TCL_ERROR;
593 if( !threadset[i].busy ){
594 Tcl_AppendResult(interp, "no such thread", 0);
595 return TCL_ERROR;
596 }
597 client_wait(&threadset[i]);
598 threadset[i].xOp = do_finalize;
drhcab5ed72007-08-22 11:41:18 +0000599 sqlite3_free(threadset[i].zArg);
drh29c636b2006-01-09 23:40:25 +0000600 threadset[i].zArg = 0;
601 threadset[i].opnum++;
602 return TCL_OK;
603}
604
605/*
606** This procedure runs in the thread to reset a virtual machine.
607*/
608static void do_reset(Thread *p){
609 if( p->pStmt==0 ){
610 p->zErr = p->zStaticErr = "no virtual machine available";
611 p->rc = SQLITE_ERROR;
612 return;
613 }
614 p->rc = sqlite3_client_reset(p->pStmt);
615 p->pStmt = 0;
616}
617
618/*
619** Usage: client_reset ID
620**
621** Finalize the virtual machine.
622*/
mistachkin7617e4a2016-07-28 17:11:20 +0000623static int SQLITE_TCLAPI tcl_client_reset(
drh29c636b2006-01-09 23:40:25 +0000624 void *NotUsed,
625 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
626 int argc, /* Number of arguments */
627 const char **argv /* Text of each argument */
628){
629 int i;
630 if( argc!=2 ){
631 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
632 " IDL", 0);
633 return TCL_ERROR;
634 }
635 i = parse_client_id(interp, argv[1]);
636 if( i<0 ) return TCL_ERROR;
637 if( !threadset[i].busy ){
638 Tcl_AppendResult(interp, "no such thread", 0);
639 return TCL_ERROR;
640 }
641 client_wait(&threadset[i]);
642 threadset[i].xOp = do_reset;
drhcab5ed72007-08-22 11:41:18 +0000643 sqlite3_free(threadset[i].zArg);
drh29c636b2006-01-09 23:40:25 +0000644 threadset[i].zArg = 0;
645 threadset[i].opnum++;
646 return TCL_OK;
647}
648
649/*
650** Usage: client_swap ID ID
651**
652** Interchange the sqlite* pointer between two threads.
653*/
mistachkin7617e4a2016-07-28 17:11:20 +0000654static int SQLITE_TCLAPI tcl_client_swap(
drh29c636b2006-01-09 23:40:25 +0000655 void *NotUsed,
656 Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
657 int argc, /* Number of arguments */
658 const char **argv /* Text of each argument */
659){
660 int i, j;
661 sqlite3 *temp;
662 if( argc!=3 ){
663 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
664 " ID1 ID2", 0);
665 return TCL_ERROR;
666 }
667 i = parse_client_id(interp, argv[1]);
668 if( i<0 ) return TCL_ERROR;
669 if( !threadset[i].busy ){
670 Tcl_AppendResult(interp, "no such thread", 0);
671 return TCL_ERROR;
672 }
673 client_wait(&threadset[i]);
674 j = parse_client_id(interp, argv[2]);
675 if( j<0 ) return TCL_ERROR;
676 if( !threadset[j].busy ){
677 Tcl_AppendResult(interp, "no such thread", 0);
678 return TCL_ERROR;
679 }
680 client_wait(&threadset[j]);
681 temp = threadset[i].db;
682 threadset[i].db = threadset[j].db;
683 threadset[j].db = temp;
684 return TCL_OK;
685}
686
687/*
688** Register commands with the TCL interpreter.
689*/
690int Sqlitetest7_Init(Tcl_Interp *interp){
691 static struct {
692 char *zName;
693 Tcl_CmdProc *xProc;
694 } aCmd[] = {
695 { "client_create", (Tcl_CmdProc*)tcl_client_create },
696 { "client_wait", (Tcl_CmdProc*)tcl_client_wait },
697 { "client_halt", (Tcl_CmdProc*)tcl_client_halt },
698 { "client_argc", (Tcl_CmdProc*)tcl_client_argc },
699 { "client_argv", (Tcl_CmdProc*)tcl_client_argv },
700 { "client_colname", (Tcl_CmdProc*)tcl_client_colname },
701 { "client_result", (Tcl_CmdProc*)tcl_client_result },
702 { "client_error", (Tcl_CmdProc*)tcl_client_error },
703 { "client_compile", (Tcl_CmdProc*)tcl_client_compile },
704 { "client_step", (Tcl_CmdProc*)tcl_client_step },
705 { "client_reset", (Tcl_CmdProc*)tcl_client_reset },
706 { "client_finalize", (Tcl_CmdProc*)tcl_client_finalize },
707 { "client_swap", (Tcl_CmdProc*)tcl_client_swap },
708 };
709 int i;
710
711 for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
712 Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
713 }
714 return TCL_OK;
715}
716#else
717int Sqlitetest7_Init(Tcl_Interp *interp){ return TCL_OK; }
danielk197729bafea2008-06-26 10:41:19 +0000718#endif /* SQLITE_OS_UNIX */