os/persistentdata/persistentstorage/sqlite3api/TEST/SRC/test4.c
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
sl@0
     1
/*
sl@0
     2
** 2003 December 18
sl@0
     3
**
sl@0
     4
** The author disclaims copyright to this source code.  In place of
sl@0
     5
** a legal notice, here is a blessing:
sl@0
     6
**
sl@0
     7
**    May you do good and not evil.
sl@0
     8
**    May you find forgiveness for yourself and forgive others.
sl@0
     9
**    May you share freely, never taking more than you give.
sl@0
    10
**
sl@0
    11
*************************************************************************
sl@0
    12
** Code for testing the the SQLite library in a multithreaded environment.
sl@0
    13
**
sl@0
    14
** $Id: test4.c,v 1.23 2008/07/28 19:34:54 drh Exp $
sl@0
    15
*/
sl@0
    16
#include "sqliteInt.h"
sl@0
    17
#include "tcl.h"
sl@0
    18
#if defined(SQLITE_OS_UNIX) && OS_UNIX==1 && SQLITE_THREADSAFE
sl@0
    19
#include <stdlib.h>
sl@0
    20
#include <string.h>
sl@0
    21
#include <pthread.h>
sl@0
    22
#include <sched.h>
sl@0
    23
#include <ctype.h>
sl@0
    24
sl@0
    25
/*
sl@0
    26
** Each thread is controlled by an instance of the following
sl@0
    27
** structure.
sl@0
    28
*/
sl@0
    29
typedef struct Thread Thread;
sl@0
    30
struct Thread {
sl@0
    31
  /* The first group of fields are writable by the master and read-only
sl@0
    32
  ** to the thread. */
sl@0
    33
  char *zFilename;       /* Name of database file */
sl@0
    34
  void (*xOp)(Thread*);  /* next operation to do */
sl@0
    35
  char *zArg;            /* argument usable by xOp */
sl@0
    36
  int opnum;             /* Operation number */
sl@0
    37
  int busy;              /* True if this thread is in use */
sl@0
    38
sl@0
    39
  /* The next group of fields are writable by the thread but read-only to the
sl@0
    40
  ** master. */
sl@0
    41
  int completed;        /* Number of operations completed */
sl@0
    42
  sqlite3 *db;           /* Open database */
sl@0
    43
  sqlite3_stmt *pStmt;     /* Pending operation */
sl@0
    44
  char *zErr;           /* operation error */
sl@0
    45
  char *zStaticErr;     /* Static error message */
sl@0
    46
  int rc;               /* operation return code */
sl@0
    47
  int argc;             /* number of columns in result */
sl@0
    48
  const char *argv[100];    /* result columns */
sl@0
    49
  const char *colv[100];    /* result column names */
sl@0
    50
};
sl@0
    51
sl@0
    52
/*
sl@0
    53
** There can be as many as 26 threads running at once.  Each is named
sl@0
    54
** by a capital letter: A, B, C, ..., Y, Z.
sl@0
    55
*/
sl@0
    56
#define N_THREAD 26
sl@0
    57
static Thread threadset[N_THREAD];
sl@0
    58
sl@0
    59
sl@0
    60
/*
sl@0
    61
** The main loop for a thread.  Threads use busy waiting. 
sl@0
    62
*/
sl@0
    63
static void *thread_main(void *pArg){
sl@0
    64
  Thread *p = (Thread*)pArg;
sl@0
    65
  if( p->db ){
sl@0
    66
    sqlite3_close(p->db);
sl@0
    67
  }
sl@0
    68
  sqlite3_open(p->zFilename, &p->db);
sl@0
    69
  if( SQLITE_OK!=sqlite3_errcode(p->db) ){
sl@0
    70
    p->zErr = strdup(sqlite3_errmsg(p->db));
sl@0
    71
    sqlite3_close(p->db);
sl@0
    72
    p->db = 0;
sl@0
    73
  }
sl@0
    74
  p->pStmt = 0;
sl@0
    75
  p->completed = 1;
sl@0
    76
  while( p->opnum<=p->completed ) sched_yield();
sl@0
    77
  while( p->xOp ){
sl@0
    78
    if( p->zErr && p->zErr!=p->zStaticErr ){
sl@0
    79
      sqlite3_free(p->zErr);
sl@0
    80
      p->zErr = 0;
sl@0
    81
    }
sl@0
    82
    (*p->xOp)(p);
sl@0
    83
    p->completed++;
sl@0
    84
    while( p->opnum<=p->completed ) sched_yield();
sl@0
    85
  }
sl@0
    86
  if( p->pStmt ){
sl@0
    87
    sqlite3_finalize(p->pStmt);
sl@0
    88
    p->pStmt = 0;
sl@0
    89
  }
sl@0
    90
  if( p->db ){
sl@0
    91
    sqlite3_close(p->db);
sl@0
    92
    p->db = 0;
sl@0
    93
  }
sl@0
    94
  if( p->zErr && p->zErr!=p->zStaticErr ){
sl@0
    95
    sqlite3_free(p->zErr);
sl@0
    96
    p->zErr = 0;
sl@0
    97
  }
sl@0
    98
  p->completed++;
sl@0
    99
  sqlite3_thread_cleanup();
sl@0
   100
  return 0;
sl@0
   101
}
sl@0
   102
sl@0
   103
/*
sl@0
   104
** Get a thread ID which is an upper case letter.  Return the index.
sl@0
   105
** If the argument is not a valid thread ID put an error message in
sl@0
   106
** the interpreter and return -1.
sl@0
   107
*/
sl@0
   108
static int parse_thread_id(Tcl_Interp *interp, const char *zArg){
sl@0
   109
  if( zArg==0 || zArg[0]==0 || zArg[1]!=0 || !isupper((unsigned char)zArg[0]) ){
sl@0
   110
    Tcl_AppendResult(interp, "thread ID must be an upper case letter", 0);
sl@0
   111
    return -1;
sl@0
   112
  }
sl@0
   113
  return zArg[0] - 'A';
sl@0
   114
}
sl@0
   115
sl@0
   116
/*
sl@0
   117
** Usage:    thread_create NAME  FILENAME
sl@0
   118
**
sl@0
   119
** NAME should be an upper case letter.  Start the thread running with
sl@0
   120
** an open connection to the given database.
sl@0
   121
*/
sl@0
   122
static int tcl_thread_create(
sl@0
   123
  void *NotUsed,
sl@0
   124
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
sl@0
   125
  int argc,              /* Number of arguments */
sl@0
   126
  const char **argv      /* Text of each argument */
sl@0
   127
){
sl@0
   128
  int i;
sl@0
   129
  pthread_t x;
sl@0
   130
  int rc;
sl@0
   131
sl@0
   132
  if( argc!=3 ){
sl@0
   133
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
sl@0
   134
       " ID FILENAME", 0);
sl@0
   135
    return TCL_ERROR;
sl@0
   136
  }
sl@0
   137
  i = parse_thread_id(interp, argv[1]);
sl@0
   138
  if( i<0 ) return TCL_ERROR;
sl@0
   139
  if( threadset[i].busy ){
sl@0
   140
    Tcl_AppendResult(interp, "thread ", argv[1], " is already running", 0);
sl@0
   141
    return TCL_ERROR;
sl@0
   142
  }
sl@0
   143
  threadset[i].busy = 1;
sl@0
   144
  sqlite3_free(threadset[i].zFilename);
sl@0
   145
  threadset[i].zFilename = sqlite3DbStrDup(0, argv[2]);
sl@0
   146
  threadset[i].opnum = 1;
sl@0
   147
  threadset[i].completed = 0;
sl@0
   148
  rc = pthread_create(&x, 0, thread_main, &threadset[i]);
sl@0
   149
  if( rc ){
sl@0
   150
    Tcl_AppendResult(interp, "failed to create the thread", 0);
sl@0
   151
    sqlite3_free(threadset[i].zFilename);
sl@0
   152
    threadset[i].busy = 0;
sl@0
   153
    return TCL_ERROR;
sl@0
   154
  }
sl@0
   155
  pthread_detach(x);
sl@0
   156
  return TCL_OK;
sl@0
   157
}
sl@0
   158
sl@0
   159
/*
sl@0
   160
** Wait for a thread to reach its idle state.
sl@0
   161
*/
sl@0
   162
static void thread_wait(Thread *p){
sl@0
   163
  while( p->opnum>p->completed ) sched_yield();
sl@0
   164
}
sl@0
   165
sl@0
   166
/*
sl@0
   167
** Usage:  thread_wait ID
sl@0
   168
**
sl@0
   169
** Wait on thread ID to reach its idle state.
sl@0
   170
*/
sl@0
   171
static int tcl_thread_wait(
sl@0
   172
  void *NotUsed,
sl@0
   173
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
sl@0
   174
  int argc,              /* Number of arguments */
sl@0
   175
  const char **argv      /* Text of each argument */
sl@0
   176
){
sl@0
   177
  int i;
sl@0
   178
sl@0
   179
  if( argc!=2 ){
sl@0
   180
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
sl@0
   181
       " ID", 0);
sl@0
   182
    return TCL_ERROR;
sl@0
   183
  }
sl@0
   184
  i = parse_thread_id(interp, argv[1]);
sl@0
   185
  if( i<0 ) return TCL_ERROR;
sl@0
   186
  if( !threadset[i].busy ){
sl@0
   187
    Tcl_AppendResult(interp, "no such thread", 0);
sl@0
   188
    return TCL_ERROR;
sl@0
   189
  }
sl@0
   190
  thread_wait(&threadset[i]);
sl@0
   191
  return TCL_OK;
sl@0
   192
}
sl@0
   193
sl@0
   194
/*
sl@0
   195
** Stop a thread.
sl@0
   196
*/
sl@0
   197
static void stop_thread(Thread *p){
sl@0
   198
  thread_wait(p);
sl@0
   199
  p->xOp = 0;
sl@0
   200
  p->opnum++;
sl@0
   201
  thread_wait(p);
sl@0
   202
  sqlite3_free(p->zArg);
sl@0
   203
  p->zArg = 0;
sl@0
   204
  sqlite3_free(p->zFilename);
sl@0
   205
  p->zFilename = 0;
sl@0
   206
  p->busy = 0;
sl@0
   207
}
sl@0
   208
sl@0
   209
/*
sl@0
   210
** Usage:  thread_halt ID
sl@0
   211
**
sl@0
   212
** Cause a thread to shut itself down.  Wait for the shutdown to be
sl@0
   213
** completed.  If ID is "*" then stop all threads.
sl@0
   214
*/
sl@0
   215
static int tcl_thread_halt(
sl@0
   216
  void *NotUsed,
sl@0
   217
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
sl@0
   218
  int argc,              /* Number of arguments */
sl@0
   219
  const char **argv      /* Text of each argument */
sl@0
   220
){
sl@0
   221
  int i;
sl@0
   222
sl@0
   223
  if( argc!=2 ){
sl@0
   224
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
sl@0
   225
       " ID", 0);
sl@0
   226
    return TCL_ERROR;
sl@0
   227
  }
sl@0
   228
  if( argv[1][0]=='*' && argv[1][1]==0 ){
sl@0
   229
    for(i=0; i<N_THREAD; i++){
sl@0
   230
      if( threadset[i].busy ) stop_thread(&threadset[i]);
sl@0
   231
    }
sl@0
   232
  }else{
sl@0
   233
    i = parse_thread_id(interp, argv[1]);
sl@0
   234
    if( i<0 ) return TCL_ERROR;
sl@0
   235
    if( !threadset[i].busy ){
sl@0
   236
      Tcl_AppendResult(interp, "no such thread", 0);
sl@0
   237
      return TCL_ERROR;
sl@0
   238
    }
sl@0
   239
    stop_thread(&threadset[i]);
sl@0
   240
  }
sl@0
   241
  return TCL_OK;
sl@0
   242
}
sl@0
   243
sl@0
   244
/*
sl@0
   245
** Usage: thread_argc  ID
sl@0
   246
**
sl@0
   247
** Wait on the most recent thread_step to complete, then return the
sl@0
   248
** number of columns in the result set.
sl@0
   249
*/
sl@0
   250
static int tcl_thread_argc(
sl@0
   251
  void *NotUsed,
sl@0
   252
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
sl@0
   253
  int argc,              /* Number of arguments */
sl@0
   254
  const char **argv      /* Text of each argument */
sl@0
   255
){
sl@0
   256
  int i;
sl@0
   257
  char zBuf[100];
sl@0
   258
sl@0
   259
  if( argc!=2 ){
sl@0
   260
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
sl@0
   261
       " ID", 0);
sl@0
   262
    return TCL_ERROR;
sl@0
   263
  }
sl@0
   264
  i = parse_thread_id(interp, argv[1]);
sl@0
   265
  if( i<0 ) return TCL_ERROR;
sl@0
   266
  if( !threadset[i].busy ){
sl@0
   267
    Tcl_AppendResult(interp, "no such thread", 0);
sl@0
   268
    return TCL_ERROR;
sl@0
   269
  }
sl@0
   270
  thread_wait(&threadset[i]);
sl@0
   271
  sprintf(zBuf, "%d", threadset[i].argc);
sl@0
   272
  Tcl_AppendResult(interp, zBuf, 0);
sl@0
   273
  return TCL_OK;
sl@0
   274
}
sl@0
   275
sl@0
   276
/*
sl@0
   277
** Usage: thread_argv  ID   N
sl@0
   278
**
sl@0
   279
** Wait on the most recent thread_step to complete, then return the
sl@0
   280
** value of the N-th columns in the result set.
sl@0
   281
*/
sl@0
   282
static int tcl_thread_argv(
sl@0
   283
  void *NotUsed,
sl@0
   284
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
sl@0
   285
  int argc,              /* Number of arguments */
sl@0
   286
  const char **argv      /* Text of each argument */
sl@0
   287
){
sl@0
   288
  int i;
sl@0
   289
  int n;
sl@0
   290
sl@0
   291
  if( argc!=3 ){
sl@0
   292
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
sl@0
   293
       " ID N", 0);
sl@0
   294
    return TCL_ERROR;
sl@0
   295
  }
sl@0
   296
  i = parse_thread_id(interp, argv[1]);
sl@0
   297
  if( i<0 ) return TCL_ERROR;
sl@0
   298
  if( !threadset[i].busy ){
sl@0
   299
    Tcl_AppendResult(interp, "no such thread", 0);
sl@0
   300
    return TCL_ERROR;
sl@0
   301
  }
sl@0
   302
  if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
sl@0
   303
  thread_wait(&threadset[i]);
sl@0
   304
  if( n<0 || n>=threadset[i].argc ){
sl@0
   305
    Tcl_AppendResult(interp, "column number out of range", 0);
sl@0
   306
    return TCL_ERROR;
sl@0
   307
  }
sl@0
   308
  Tcl_AppendResult(interp, threadset[i].argv[n], 0);
sl@0
   309
  return TCL_OK;
sl@0
   310
}
sl@0
   311
sl@0
   312
/*
sl@0
   313
** Usage: thread_colname  ID   N
sl@0
   314
**
sl@0
   315
** Wait on the most recent thread_step to complete, then return the
sl@0
   316
** name of the N-th columns in the result set.
sl@0
   317
*/
sl@0
   318
static int tcl_thread_colname(
sl@0
   319
  void *NotUsed,
sl@0
   320
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
sl@0
   321
  int argc,              /* Number of arguments */
sl@0
   322
  const char **argv      /* Text of each argument */
sl@0
   323
){
sl@0
   324
  int i;
sl@0
   325
  int n;
sl@0
   326
sl@0
   327
  if( argc!=3 ){
sl@0
   328
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
sl@0
   329
       " ID N", 0);
sl@0
   330
    return TCL_ERROR;
sl@0
   331
  }
sl@0
   332
  i = parse_thread_id(interp, argv[1]);
sl@0
   333
  if( i<0 ) return TCL_ERROR;
sl@0
   334
  if( !threadset[i].busy ){
sl@0
   335
    Tcl_AppendResult(interp, "no such thread", 0);
sl@0
   336
    return TCL_ERROR;
sl@0
   337
  }
sl@0
   338
  if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR;
sl@0
   339
  thread_wait(&threadset[i]);
sl@0
   340
  if( n<0 || n>=threadset[i].argc ){
sl@0
   341
    Tcl_AppendResult(interp, "column number out of range", 0);
sl@0
   342
    return TCL_ERROR;
sl@0
   343
  }
sl@0
   344
  Tcl_AppendResult(interp, threadset[i].colv[n], 0);
sl@0
   345
  return TCL_OK;
sl@0
   346
}
sl@0
   347
sl@0
   348
/*
sl@0
   349
** Usage: thread_result  ID
sl@0
   350
**
sl@0
   351
** Wait on the most recent operation to complete, then return the
sl@0
   352
** result code from that operation.
sl@0
   353
*/
sl@0
   354
static int tcl_thread_result(
sl@0
   355
  void *NotUsed,
sl@0
   356
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
sl@0
   357
  int argc,              /* Number of arguments */
sl@0
   358
  const char **argv      /* Text of each argument */
sl@0
   359
){
sl@0
   360
  int i;
sl@0
   361
  const char *zName;
sl@0
   362
sl@0
   363
  if( argc!=2 ){
sl@0
   364
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
sl@0
   365
       " ID", 0);
sl@0
   366
    return TCL_ERROR;
sl@0
   367
  }
sl@0
   368
  i = parse_thread_id(interp, argv[1]);
sl@0
   369
  if( i<0 ) return TCL_ERROR;
sl@0
   370
  if( !threadset[i].busy ){
sl@0
   371
    Tcl_AppendResult(interp, "no such thread", 0);
sl@0
   372
    return TCL_ERROR;
sl@0
   373
  }
sl@0
   374
  thread_wait(&threadset[i]);
sl@0
   375
  switch( threadset[i].rc ){
sl@0
   376
    case SQLITE_OK:         zName = "SQLITE_OK";          break;
sl@0
   377
    case SQLITE_ERROR:      zName = "SQLITE_ERROR";       break;
sl@0
   378
    case SQLITE_PERM:       zName = "SQLITE_PERM";        break;
sl@0
   379
    case SQLITE_ABORT:      zName = "SQLITE_ABORT";       break;
sl@0
   380
    case SQLITE_BUSY:       zName = "SQLITE_BUSY";        break;
sl@0
   381
    case SQLITE_LOCKED:     zName = "SQLITE_LOCKED";      break;
sl@0
   382
    case SQLITE_NOMEM:      zName = "SQLITE_NOMEM";       break;
sl@0
   383
    case SQLITE_READONLY:   zName = "SQLITE_READONLY";    break;
sl@0
   384
    case SQLITE_INTERRUPT:  zName = "SQLITE_INTERRUPT";   break;
sl@0
   385
    case SQLITE_IOERR:      zName = "SQLITE_IOERR";       break;
sl@0
   386
    case SQLITE_CORRUPT:    zName = "SQLITE_CORRUPT";     break;
sl@0
   387
    case SQLITE_FULL:       zName = "SQLITE_FULL";        break;
sl@0
   388
    case SQLITE_CANTOPEN:   zName = "SQLITE_CANTOPEN";    break;
sl@0
   389
    case SQLITE_PROTOCOL:   zName = "SQLITE_PROTOCOL";    break;
sl@0
   390
    case SQLITE_EMPTY:      zName = "SQLITE_EMPTY";       break;
sl@0
   391
    case SQLITE_SCHEMA:     zName = "SQLITE_SCHEMA";      break;
sl@0
   392
    case SQLITE_CONSTRAINT: zName = "SQLITE_CONSTRAINT";  break;
sl@0
   393
    case SQLITE_MISMATCH:   zName = "SQLITE_MISMATCH";    break;
sl@0
   394
    case SQLITE_MISUSE:     zName = "SQLITE_MISUSE";      break;
sl@0
   395
    case SQLITE_NOLFS:      zName = "SQLITE_NOLFS";       break;
sl@0
   396
    case SQLITE_AUTH:       zName = "SQLITE_AUTH";        break;
sl@0
   397
    case SQLITE_FORMAT:     zName = "SQLITE_FORMAT";      break;
sl@0
   398
    case SQLITE_RANGE:      zName = "SQLITE_RANGE";       break;
sl@0
   399
    case SQLITE_ROW:        zName = "SQLITE_ROW";         break;
sl@0
   400
    case SQLITE_DONE:       zName = "SQLITE_DONE";        break;
sl@0
   401
    default:                zName = "SQLITE_Unknown";     break;
sl@0
   402
  }
sl@0
   403
  Tcl_AppendResult(interp, zName, 0);
sl@0
   404
  return TCL_OK;
sl@0
   405
}
sl@0
   406
sl@0
   407
/*
sl@0
   408
** Usage: thread_error  ID
sl@0
   409
**
sl@0
   410
** Wait on the most recent operation to complete, then return the
sl@0
   411
** error string.
sl@0
   412
*/
sl@0
   413
static int tcl_thread_error(
sl@0
   414
  void *NotUsed,
sl@0
   415
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
sl@0
   416
  int argc,              /* Number of arguments */
sl@0
   417
  const char **argv      /* Text of each argument */
sl@0
   418
){
sl@0
   419
  int i;
sl@0
   420
sl@0
   421
  if( argc!=2 ){
sl@0
   422
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
sl@0
   423
       " ID", 0);
sl@0
   424
    return TCL_ERROR;
sl@0
   425
  }
sl@0
   426
  i = parse_thread_id(interp, argv[1]);
sl@0
   427
  if( i<0 ) return TCL_ERROR;
sl@0
   428
  if( !threadset[i].busy ){
sl@0
   429
    Tcl_AppendResult(interp, "no such thread", 0);
sl@0
   430
    return TCL_ERROR;
sl@0
   431
  }
sl@0
   432
  thread_wait(&threadset[i]);
sl@0
   433
  Tcl_AppendResult(interp, threadset[i].zErr, 0);
sl@0
   434
  return TCL_OK;
sl@0
   435
}
sl@0
   436
sl@0
   437
/*
sl@0
   438
** This procedure runs in the thread to compile an SQL statement.
sl@0
   439
*/
sl@0
   440
static void do_compile(Thread *p){
sl@0
   441
  if( p->db==0 ){
sl@0
   442
    p->zErr = p->zStaticErr = "no database is open";
sl@0
   443
    p->rc = SQLITE_ERROR;
sl@0
   444
    return;
sl@0
   445
  }
sl@0
   446
  if( p->pStmt ){
sl@0
   447
    sqlite3_finalize(p->pStmt);
sl@0
   448
    p->pStmt = 0;
sl@0
   449
  }
sl@0
   450
  p->rc = sqlite3_prepare(p->db, p->zArg, -1, &p->pStmt, 0);
sl@0
   451
}
sl@0
   452
sl@0
   453
/*
sl@0
   454
** Usage: thread_compile ID SQL
sl@0
   455
**
sl@0
   456
** Compile a new virtual machine.
sl@0
   457
*/
sl@0
   458
static int tcl_thread_compile(
sl@0
   459
  void *NotUsed,
sl@0
   460
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
sl@0
   461
  int argc,              /* Number of arguments */
sl@0
   462
  const char **argv      /* Text of each argument */
sl@0
   463
){
sl@0
   464
  int i;
sl@0
   465
  if( argc!=3 ){
sl@0
   466
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
sl@0
   467
       " ID SQL", 0);
sl@0
   468
    return TCL_ERROR;
sl@0
   469
  }
sl@0
   470
  i = parse_thread_id(interp, argv[1]);
sl@0
   471
  if( i<0 ) return TCL_ERROR;
sl@0
   472
  if( !threadset[i].busy ){
sl@0
   473
    Tcl_AppendResult(interp, "no such thread", 0);
sl@0
   474
    return TCL_ERROR;
sl@0
   475
  }
sl@0
   476
  thread_wait(&threadset[i]);
sl@0
   477
  threadset[i].xOp = do_compile;
sl@0
   478
  sqlite3_free(threadset[i].zArg);
sl@0
   479
  threadset[i].zArg = sqlite3DbStrDup(0, argv[2]);
sl@0
   480
  threadset[i].opnum++;
sl@0
   481
  return TCL_OK;
sl@0
   482
}
sl@0
   483
sl@0
   484
/*
sl@0
   485
** This procedure runs in the thread to step the virtual machine.
sl@0
   486
*/
sl@0
   487
static void do_step(Thread *p){
sl@0
   488
  int i;
sl@0
   489
  if( p->pStmt==0 ){
sl@0
   490
    p->zErr = p->zStaticErr = "no virtual machine available";
sl@0
   491
    p->rc = SQLITE_ERROR;
sl@0
   492
    return;
sl@0
   493
  }
sl@0
   494
  p->rc = sqlite3_step(p->pStmt);
sl@0
   495
  if( p->rc==SQLITE_ROW ){
sl@0
   496
    p->argc = sqlite3_column_count(p->pStmt);
sl@0
   497
    for(i=0; i<sqlite3_data_count(p->pStmt); i++){
sl@0
   498
      p->argv[i] = (char*)sqlite3_column_text(p->pStmt, i);
sl@0
   499
    }
sl@0
   500
    for(i=0; i<p->argc; i++){
sl@0
   501
      p->colv[i] = sqlite3_column_name(p->pStmt, i);
sl@0
   502
    }
sl@0
   503
  }
sl@0
   504
}
sl@0
   505
sl@0
   506
/*
sl@0
   507
** Usage: thread_step ID
sl@0
   508
**
sl@0
   509
** Advance the virtual machine by one step
sl@0
   510
*/
sl@0
   511
static int tcl_thread_step(
sl@0
   512
  void *NotUsed,
sl@0
   513
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
sl@0
   514
  int argc,              /* Number of arguments */
sl@0
   515
  const char **argv      /* Text of each argument */
sl@0
   516
){
sl@0
   517
  int i;
sl@0
   518
  if( argc!=2 ){
sl@0
   519
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
sl@0
   520
       " IDL", 0);
sl@0
   521
    return TCL_ERROR;
sl@0
   522
  }
sl@0
   523
  i = parse_thread_id(interp, argv[1]);
sl@0
   524
  if( i<0 ) return TCL_ERROR;
sl@0
   525
  if( !threadset[i].busy ){
sl@0
   526
    Tcl_AppendResult(interp, "no such thread", 0);
sl@0
   527
    return TCL_ERROR;
sl@0
   528
  }
sl@0
   529
  thread_wait(&threadset[i]);
sl@0
   530
  threadset[i].xOp = do_step;
sl@0
   531
  threadset[i].opnum++;
sl@0
   532
  return TCL_OK;
sl@0
   533
}
sl@0
   534
sl@0
   535
/*
sl@0
   536
** This procedure runs in the thread to finalize a virtual machine.
sl@0
   537
*/
sl@0
   538
static void do_finalize(Thread *p){
sl@0
   539
  if( p->pStmt==0 ){
sl@0
   540
    p->zErr = p->zStaticErr = "no virtual machine available";
sl@0
   541
    p->rc = SQLITE_ERROR;
sl@0
   542
    return;
sl@0
   543
  }
sl@0
   544
  p->rc = sqlite3_finalize(p->pStmt);
sl@0
   545
  p->pStmt = 0;
sl@0
   546
}
sl@0
   547
sl@0
   548
/*
sl@0
   549
** Usage: thread_finalize ID
sl@0
   550
**
sl@0
   551
** Finalize the virtual machine.
sl@0
   552
*/
sl@0
   553
static int tcl_thread_finalize(
sl@0
   554
  void *NotUsed,
sl@0
   555
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
sl@0
   556
  int argc,              /* Number of arguments */
sl@0
   557
  const char **argv      /* Text of each argument */
sl@0
   558
){
sl@0
   559
  int i;
sl@0
   560
  if( argc!=2 ){
sl@0
   561
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
sl@0
   562
       " IDL", 0);
sl@0
   563
    return TCL_ERROR;
sl@0
   564
  }
sl@0
   565
  i = parse_thread_id(interp, argv[1]);
sl@0
   566
  if( i<0 ) return TCL_ERROR;
sl@0
   567
  if( !threadset[i].busy ){
sl@0
   568
    Tcl_AppendResult(interp, "no such thread", 0);
sl@0
   569
    return TCL_ERROR;
sl@0
   570
  }
sl@0
   571
  thread_wait(&threadset[i]);
sl@0
   572
  threadset[i].xOp = do_finalize;
sl@0
   573
  sqlite3_free(threadset[i].zArg);
sl@0
   574
  threadset[i].zArg = 0;
sl@0
   575
  threadset[i].opnum++;
sl@0
   576
  return TCL_OK;
sl@0
   577
}
sl@0
   578
sl@0
   579
/*
sl@0
   580
** Usage: thread_swap ID ID
sl@0
   581
**
sl@0
   582
** Interchange the sqlite* pointer between two threads.
sl@0
   583
*/
sl@0
   584
static int tcl_thread_swap(
sl@0
   585
  void *NotUsed,
sl@0
   586
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
sl@0
   587
  int argc,              /* Number of arguments */
sl@0
   588
  const char **argv      /* Text of each argument */
sl@0
   589
){
sl@0
   590
  int i, j;
sl@0
   591
  sqlite3 *temp;
sl@0
   592
  if( argc!=3 ){
sl@0
   593
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
sl@0
   594
       " ID1 ID2", 0);
sl@0
   595
    return TCL_ERROR;
sl@0
   596
  }
sl@0
   597
  i = parse_thread_id(interp, argv[1]);
sl@0
   598
  if( i<0 ) return TCL_ERROR;
sl@0
   599
  if( !threadset[i].busy ){
sl@0
   600
    Tcl_AppendResult(interp, "no such thread", 0);
sl@0
   601
    return TCL_ERROR;
sl@0
   602
  }
sl@0
   603
  thread_wait(&threadset[i]);
sl@0
   604
  j = parse_thread_id(interp, argv[2]);
sl@0
   605
  if( j<0 ) return TCL_ERROR;
sl@0
   606
  if( !threadset[j].busy ){
sl@0
   607
    Tcl_AppendResult(interp, "no such thread", 0);
sl@0
   608
    return TCL_ERROR;
sl@0
   609
  }
sl@0
   610
  thread_wait(&threadset[j]);
sl@0
   611
  temp = threadset[i].db;
sl@0
   612
  threadset[i].db = threadset[j].db;
sl@0
   613
  threadset[j].db = temp;
sl@0
   614
  return TCL_OK;
sl@0
   615
}
sl@0
   616
sl@0
   617
/*
sl@0
   618
** Usage: thread_db_get ID
sl@0
   619
**
sl@0
   620
** Return the database connection pointer for the given thread.  Then
sl@0
   621
** remove the pointer from the thread itself.  Afterwards, the thread
sl@0
   622
** can be stopped and the connection can be used by the main thread.
sl@0
   623
*/
sl@0
   624
static int tcl_thread_db_get(
sl@0
   625
  void *NotUsed,
sl@0
   626
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
sl@0
   627
  int argc,              /* Number of arguments */
sl@0
   628
  const char **argv      /* Text of each argument */
sl@0
   629
){
sl@0
   630
  int i;
sl@0
   631
  char zBuf[100];
sl@0
   632
  extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);
sl@0
   633
  if( argc!=2 ){
sl@0
   634
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
sl@0
   635
       " ID", 0);
sl@0
   636
    return TCL_ERROR;
sl@0
   637
  }
sl@0
   638
  i = parse_thread_id(interp, argv[1]);
sl@0
   639
  if( i<0 ) return TCL_ERROR;
sl@0
   640
  if( !threadset[i].busy ){
sl@0
   641
    Tcl_AppendResult(interp, "no such thread", 0);
sl@0
   642
    return TCL_ERROR;
sl@0
   643
  }
sl@0
   644
  thread_wait(&threadset[i]);
sl@0
   645
  sqlite3TestMakePointerStr(interp, zBuf, threadset[i].db);
sl@0
   646
  threadset[i].db = 0;
sl@0
   647
  Tcl_AppendResult(interp, zBuf, (char*)0);
sl@0
   648
  return TCL_OK;
sl@0
   649
}
sl@0
   650
sl@0
   651
/*
sl@0
   652
** Usage: thread_stmt_get ID
sl@0
   653
**
sl@0
   654
** Return the database stmt pointer for the given thread.  Then
sl@0
   655
** remove the pointer from the thread itself. 
sl@0
   656
*/
sl@0
   657
static int tcl_thread_stmt_get(
sl@0
   658
  void *NotUsed,
sl@0
   659
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
sl@0
   660
  int argc,              /* Number of arguments */
sl@0
   661
  const char **argv      /* Text of each argument */
sl@0
   662
){
sl@0
   663
  int i;
sl@0
   664
  char zBuf[100];
sl@0
   665
  extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);
sl@0
   666
  if( argc!=2 ){
sl@0
   667
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
sl@0
   668
       " ID", 0);
sl@0
   669
    return TCL_ERROR;
sl@0
   670
  }
sl@0
   671
  i = parse_thread_id(interp, argv[1]);
sl@0
   672
  if( i<0 ) return TCL_ERROR;
sl@0
   673
  if( !threadset[i].busy ){
sl@0
   674
    Tcl_AppendResult(interp, "no such thread", 0);
sl@0
   675
    return TCL_ERROR;
sl@0
   676
  }
sl@0
   677
  thread_wait(&threadset[i]);
sl@0
   678
  sqlite3TestMakePointerStr(interp, zBuf, threadset[i].pStmt);
sl@0
   679
  threadset[i].pStmt = 0;
sl@0
   680
  Tcl_AppendResult(interp, zBuf, (char*)0);
sl@0
   681
  return TCL_OK;
sl@0
   682
}
sl@0
   683
sl@0
   684
/*
sl@0
   685
** Register commands with the TCL interpreter.
sl@0
   686
*/
sl@0
   687
int Sqlitetest4_Init(Tcl_Interp *interp){
sl@0
   688
  static struct {
sl@0
   689
     char *zName;
sl@0
   690
     Tcl_CmdProc *xProc;
sl@0
   691
  } aCmd[] = {
sl@0
   692
     { "thread_create",     (Tcl_CmdProc*)tcl_thread_create     },
sl@0
   693
     { "thread_wait",       (Tcl_CmdProc*)tcl_thread_wait       },
sl@0
   694
     { "thread_halt",       (Tcl_CmdProc*)tcl_thread_halt       },
sl@0
   695
     { "thread_argc",       (Tcl_CmdProc*)tcl_thread_argc       },
sl@0
   696
     { "thread_argv",       (Tcl_CmdProc*)tcl_thread_argv       },
sl@0
   697
     { "thread_colname",    (Tcl_CmdProc*)tcl_thread_colname    },
sl@0
   698
     { "thread_result",     (Tcl_CmdProc*)tcl_thread_result     },
sl@0
   699
     { "thread_error",      (Tcl_CmdProc*)tcl_thread_error      },
sl@0
   700
     { "thread_compile",    (Tcl_CmdProc*)tcl_thread_compile    },
sl@0
   701
     { "thread_step",       (Tcl_CmdProc*)tcl_thread_step       },
sl@0
   702
     { "thread_finalize",   (Tcl_CmdProc*)tcl_thread_finalize   },
sl@0
   703
     { "thread_swap",       (Tcl_CmdProc*)tcl_thread_swap       },
sl@0
   704
     { "thread_db_get",     (Tcl_CmdProc*)tcl_thread_db_get     },
sl@0
   705
     { "thread_stmt_get",   (Tcl_CmdProc*)tcl_thread_stmt_get   },
sl@0
   706
  };
sl@0
   707
  int i;
sl@0
   708
sl@0
   709
  for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
sl@0
   710
    Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
sl@0
   711
  }
sl@0
   712
  return TCL_OK;
sl@0
   713
}
sl@0
   714
#else
sl@0
   715
int Sqlitetest4_Init(Tcl_Interp *interp){ return TCL_OK; }
sl@0
   716
#endif /* SQLITE_OS_UNIX */