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