1: /*
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.
14: */
15: #include "sqliteInt.h"
16: #include "tcl.h"
17:
18: /*
19: ** This test only works on UNIX with a SQLITE_THREADSAFE build that includes
20: ** the SQLITE_SERVER option.
21: */
22: #if defined(SQLITE_SERVER) && !defined(SQLITE_OMIT_SHARED_CACHE) && \
23: SQLITE_OS_UNIX && SQLITE_THREADSAFE
24:
25: #include <stdlib.h>
26: #include <string.h>
27: #include <pthread.h>
28: #include <sched.h>
29: #include <ctype.h>
30:
31: /*
32: ** Interfaces defined in server.c
33: */
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:
44: /*
45: ** Each thread is controlled by an instance of the following
46: ** structure.
47: */
48: typedef struct Thread Thread;
49: struct Thread {
50: /* The first group of fields are writable by the supervisor thread
51: ** and read-only to the client threads
52: */
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 */
58:
59: /* The next group of fields are writable by the client threads
60: ** but read-only to the superviser thread.
61: */
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 */
71: };
72:
73: /*
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.
76: */
77: #define N_THREAD 26
78: static Thread threadset[N_THREAD];
79:
80: /*
81: ** The main loop for a thread. Threads use busy waiting.
82: */
83: static void *client_main(void *pArg){
84: Thread *p = (Thread*)pArg;
85: if( p->db ){
86: sqlite3_client_close(p->db);
87: }
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);
92: p->db = 0;
93: }
94: p->pStmt = 0;
95: p->completed = 1;
96: while( p->opnum<=p->completed ) sched_yield();
97: while( p->xOp ){
98: if( p->zErr && p->zErr!=p->zStaticErr ){
99: sqlite3_free(p->zErr);
100: p->zErr = 0;
101: }
102: (*p->xOp)(p);
103: p->completed++;
104: while( p->opnum<=p->completed ) sched_yield();
105: }
106: if( p->pStmt ){
107: sqlite3_client_finalize(p->pStmt);
108: p->pStmt = 0;
109: }
110: if( p->db ){
111: sqlite3_client_close(p->db);
112: p->db = 0;
113: }
114: if( p->zErr && p->zErr!=p->zStaticErr ){
115: sqlite3_free(p->zErr);
116: p->zErr = 0;
117: }
118: p->completed++;
119: #ifndef SQLITE_OMIT_DEPRECATED
120: sqlite3_thread_cleanup();
121: #endif
122: return 0;
123: }
124:
125: /*
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.
129: */
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);
133: return -1;
134: }
135: return zArg[0] - 'A';
136: }
137:
138: /*
139: ** Usage: client_create NAME FILENAME
140: **
141: ** NAME should be an upper case letter. Start the thread running with
142: ** an open connection to the given database.
143: */
144: static int tcl_client_create(
145: void *NotUsed,
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 */
149: ){
150: int i;
151: pthread_t x;
152: int rc;
153:
154: if( argc!=3 ){
155: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
156: " ID FILENAME", 0);
157: return TCL_ERROR;
158: }
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);
163: return TCL_ERROR;
164: }
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]);
171: if( rc ){
172: Tcl_AppendResult(interp, "failed to create the thread", 0);
173: sqlite3_free(threadset[i].zFilename);
174: threadset[i].busy = 0;
175: return TCL_ERROR;
176: }
177: pthread_detach(x);
178: sqlite3_server_start();
179: return TCL_OK;
180: }
181:
182: /*
183: ** Wait for a thread to reach its idle state.
184: */
185: static void client_wait(Thread *p){
186: while( p->opnum>p->completed ) sched_yield();
187: }
188:
189: /*
190: ** Usage: client_wait ID
191: **
192: ** Wait on thread ID to reach its idle state.
193: */
194: static int tcl_client_wait(
195: void *NotUsed,
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 */
199: ){
200: int i;
201:
202: if( argc!=2 ){
203: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
204: " ID", 0);
205: return TCL_ERROR;
206: }
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);
211: return TCL_ERROR;
212: }
213: client_wait(&threadset[i]);
214: return TCL_OK;
215: }
216:
217: /*
218: ** Stop a thread.
219: */
220: static void stop_thread(Thread *p){
221: client_wait(p);
222: p->xOp = 0;
223: p->opnum++;
224: client_wait(p);
225: sqlite3_free(p->zArg);
226: p->zArg = 0;
227: sqlite3_free(p->zFilename);
228: p->zFilename = 0;
229: p->busy = 0;
230: }
231:
232: /*
233: ** Usage: client_halt ID
234: **
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.
237: */
238: static int tcl_client_halt(
239: void *NotUsed,
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 */
243: ){
244: int i;
245:
246: if( argc!=2 ){
247: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
248: " ID", 0);
249: return TCL_ERROR;
250: }
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]);
255: }
256: }
257: }else{
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);
262: return TCL_ERROR;
263: }
264: stop_thread(&threadset[i]);
265: }
266:
267: /* If no client threads are still running, also stop the server */
268: for(i=0; i<N_THREAD && threadset[i].busy==0; i++){}
269: if( i>=N_THREAD ){
270: sqlite3_server_stop();
271: }
272: return TCL_OK;
273: }
274:
275: /*
276: ** Usage: client_argc ID
277: **
278: ** Wait on the most recent client_step to complete, then return the
279: ** number of columns in the result set.
280: */
281: static int tcl_client_argc(
282: void *NotUsed,
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 */
286: ){
287: int i;
288: char zBuf[100];
289:
290: if( argc!=2 ){
291: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
292: " ID", 0);
293: return TCL_ERROR;
294: }
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);
299: return TCL_ERROR;
300: }
301: client_wait(&threadset[i]);
302: sprintf(zBuf, "%d", threadset[i].argc);
303: Tcl_AppendResult(interp, zBuf, 0);
304: return TCL_OK;
305: }
306:
307: /*
308: ** Usage: client_argv ID N
309: **
310: ** Wait on the most recent client_step to complete, then return the
311: ** value of the N-th columns in the result set.
312: */
313: static int tcl_client_argv(
314: void *NotUsed,
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 */
318: ){
319: int i;
320: int n;
321:
322: if( argc!=3 ){
323: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
324: " ID N", 0);
325: return TCL_ERROR;
326: }
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);
331: return TCL_ERROR;
332: }
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);
337: return TCL_ERROR;
338: }
339: Tcl_AppendResult(interp, threadset[i].argv[n], 0);
340: return TCL_OK;
341: }
342:
343: /*
344: ** Usage: client_colname ID N
345: **
346: ** Wait on the most recent client_step to complete, then return the
347: ** name of the N-th columns in the result set.
348: */
349: static int tcl_client_colname(
350: void *NotUsed,
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 */
354: ){
355: int i;
356: int n;
357:
358: if( argc!=3 ){
359: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
360: " ID N", 0);
361: return TCL_ERROR;
362: }
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);
367: return TCL_ERROR;
368: }
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);
373: return TCL_ERROR;
374: }
375: Tcl_AppendResult(interp, threadset[i].colv[n], 0);
376: return TCL_OK;
377: }
378:
379: /*
380: ** Usage: client_result ID
381: **
382: ** Wait on the most recent operation to complete, then return the
383: ** result code from that operation.
384: */
385: static int tcl_client_result(
386: void *NotUsed,
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 */
390: ){
391: int i;
392: const char *zName;
393:
394: if( argc!=2 ){
395: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
396: " ID", 0);
397: return TCL_ERROR;
398: }
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);
403: return TCL_ERROR;
404: }
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;
433: }
434: Tcl_AppendResult(interp, zName, 0);
435: return TCL_OK;
436: }
437:
438: /*
439: ** Usage: client_error ID
440: **
441: ** Wait on the most recent operation to complete, then return the
442: ** error string.
443: */
444: static int tcl_client_error(
445: void *NotUsed,
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 */
449: ){
450: int i;
451:
452: if( argc!=2 ){
453: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
454: " ID", 0);
455: return TCL_ERROR;
456: }
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);
461: return TCL_ERROR;
462: }
463: client_wait(&threadset[i]);
464: Tcl_AppendResult(interp, threadset[i].zErr, 0);
465: return TCL_OK;
466: }
467:
468: /*
469: ** This procedure runs in the thread to compile an SQL statement.
470: */
471: static void do_compile(Thread *p){
472: if( p->db==0 ){
473: p->zErr = p->zStaticErr = "no database is open";
474: p->rc = SQLITE_ERROR;
475: return;
476: }
477: if( p->pStmt ){
478: sqlite3_client_finalize(p->pStmt);
479: p->pStmt = 0;
480: }
481: p->rc = sqlite3_client_prepare(p->db, p->zArg, -1, &p->pStmt, 0);
482: }
483:
484: /*
485: ** Usage: client_compile ID SQL
486: **
487: ** Compile a new virtual machine.
488: */
489: static int tcl_client_compile(
490: void *NotUsed,
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 */
494: ){
495: int i;
496: if( argc!=3 ){
497: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
498: " ID SQL", 0);
499: return TCL_ERROR;
500: }
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);
505: return TCL_ERROR;
506: }
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++;
512: return TCL_OK;
513: }
514:
515: /*
516: ** This procedure runs in the thread to step the virtual machine.
517: */
518: static void do_step(Thread *p){
519: int i;
520: if( p->pStmt==0 ){
521: p->zErr = p->zStaticErr = "no virtual machine available";
522: p->rc = SQLITE_ERROR;
523: return;
524: }
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);
530: }
531: for(i=0; i<p->argc; i++){
532: p->colv[i] = sqlite3_column_name(p->pStmt, i);
533: }
534: }
535: }
536:
537: /*
538: ** Usage: client_step ID
539: **
540: ** Advance the virtual machine by one step
541: */
542: static int tcl_client_step(
543: void *NotUsed,
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 */
547: ){
548: int i;
549: if( argc!=2 ){
550: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
551: " IDL", 0);
552: return TCL_ERROR;
553: }
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);
558: return TCL_ERROR;
559: }
560: client_wait(&threadset[i]);
561: threadset[i].xOp = do_step;
562: threadset[i].opnum++;
563: return TCL_OK;
564: }
565:
566: /*
567: ** This procedure runs in the thread to finalize a virtual machine.
568: */
569: static void do_finalize(Thread *p){
570: if( p->pStmt==0 ){
571: p->zErr = p->zStaticErr = "no virtual machine available";
572: p->rc = SQLITE_ERROR;
573: return;
574: }
575: p->rc = sqlite3_client_finalize(p->pStmt);
576: p->pStmt = 0;
577: }
578:
579: /*
580: ** Usage: client_finalize ID
581: **
582: ** Finalize the virtual machine.
583: */
584: static int tcl_client_finalize(
585: void *NotUsed,
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 */
589: ){
590: int i;
591: if( argc!=2 ){
592: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
593: " IDL", 0);
594: return TCL_ERROR;
595: }
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);
600: return TCL_ERROR;
601: }
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++;
607: return TCL_OK;
608: }
609:
610: /*
611: ** This procedure runs in the thread to reset a virtual machine.
612: */
613: static void do_reset(Thread *p){
614: if( p->pStmt==0 ){
615: p->zErr = p->zStaticErr = "no virtual machine available";
616: p->rc = SQLITE_ERROR;
617: return;
618: }
619: p->rc = sqlite3_client_reset(p->pStmt);
620: p->pStmt = 0;
621: }
622:
623: /*
624: ** Usage: client_reset ID
625: **
626: ** Finalize the virtual machine.
627: */
628: static int tcl_client_reset(
629: void *NotUsed,
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 */
633: ){
634: int i;
635: if( argc!=2 ){
636: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
637: " IDL", 0);
638: return TCL_ERROR;
639: }
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);
644: return TCL_ERROR;
645: }
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++;
651: return TCL_OK;
652: }
653:
654: /*
655: ** Usage: client_swap ID ID
656: **
657: ** Interchange the sqlite* pointer between two threads.
658: */
659: static int tcl_client_swap(
660: void *NotUsed,
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 */
664: ){
665: int i, j;
666: sqlite3 *temp;
667: if( argc!=3 ){
668: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
669: " ID1 ID2", 0);
670: return TCL_ERROR;
671: }
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);
676: return TCL_ERROR;
677: }
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);
683: return TCL_ERROR;
684: }
685: client_wait(&threadset[j]);
686: temp = threadset[i].db;
687: threadset[i].db = threadset[j].db;
688: threadset[j].db = temp;
689: return TCL_OK;
690: }
691:
692: /*
693: ** Register commands with the TCL interpreter.
694: */
695: int Sqlitetest7_Init(Tcl_Interp *interp){
696: static struct {
697: char *zName;
698: Tcl_CmdProc *xProc;
699: } aCmd[] = {
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 },
713: };
714: int i;
715:
716: for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
717: Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
718: }
719: return TCL_OK;
720: }
721: #else
722: int Sqlitetest7_Init(Tcl_Interp *interp){ return TCL_OK; }
723: #endif /* SQLITE_OS_UNIX */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>