File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / sqlite3 / src / test7.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 17:04:17 2012 UTC (12 years, 8 months ago) by misho
Branches: sqlite3, MAIN
CVS tags: v3_7_10, HEAD
sqlite3

    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>