Annotation of embedaddon/sqlite3/src/test7.c, revision 1.1.1.1

1.1       misho       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>