sqllogictest

Check-in [8828b124a6]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add command-line options --halt and --trace. Add the "integrity_check" parameter to the SQLite3 engine that runs integrity_check after each statement. Enhance the SQLite3 engine to report memory leaks.
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 8828b124a657640a11695b72bbd47fb9d87d79d9
User & Date: drh 2015-09-28 12:06:29.212
Context
2015-09-28
14:58
Update the built-in SQLite to the 3.8.12 alpha version that includes the ONEPASS bug fix. check-in: c70107362d user: drh tags: trunk
12:06
Add command-line options --halt and --trace. Add the "integrity_check" parameter to the SQLite3 engine that runs integrity_check after each statement. Enhance the SQLite3 engine to report memory leaks. check-in: 8828b124a6 user: drh tags: trunk
11:00
Update built-in SQLite to 3.8.12 alpha. Allow two prefix dashes on command-line options. Omit run-all.sh and rely on run-all.tcl for testing. Use all optimizer bits for the optimizer off tests in run-all.tcl. check-in: fcd2cc6991 user: drh tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/run-all.tcl.
33
34
35
36
37
38
39

40
41
42
43
44
45
46
47
48
# Run the tests
#
set totalerr 0
set totaltest 0
set totalrun 0
foreach tx [lsort [array names tcase]] {
  foreach opt {0 0xfff} {

    catch {
      exec $BIN -verify -parameter optimizer=[expr {$opt+0}] $tx
    } res
    puts $res
    if {[regexp {(\d+) errors out of (\d+) tests} $res all nerr ntst]} {
      incr totalerr $nerr
      incr totaltest $ntst
    } else {
      error "test did not complete: $BIN -verify -parameter optimizer=$opt $tx"







>

|







33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# Run the tests
#
set totalerr 0
set totaltest 0
set totalrun 0
foreach tx [lsort [array names tcase]] {
  foreach opt {0 0xfff} {
    set opt "integrity_check;optimizer=[expr {$opt+0}]"
    catch {
      exec $BIN -verify -parameter $opt $tx
    } res
    puts $res
    if {[regexp {(\d+) errors out of (\d+) tests} $res all nerr ntst]} {
      incr totalerr $nerr
      incr totaltest $ntst
    } else {
      error "test did not complete: $BIN -verify -parameter optimizer=$opt $tx"
Changes to src/slt_sqlite.c.
24
25
26
27
28
29
30














31
32
33
34
35
36
37
**
** Use this interface as a model for other database engine interfaces.
*/
#include "sqlite3.h"
#include <ctype.h>
#include <stdio.h>
















/*
** Skip forward over whitespace in a string.
*/
static const char *skipWhitespace(const char *z){
  while( isspace(*z) ){ z++; }
  return z;







>
>
>
>
>
>
>
>
>
>
>
>
>
>







24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
**
** Use this interface as a model for other database engine interfaces.
*/
#include "sqlite3.h"
#include <ctype.h>
#include <stdio.h>

/*
** The SQLite database connection object
*/
typedef struct SQLiteConn SQLiteConn;
struct SQLiteConn {
  sqlite3 *db;            /* The database connection */
  unsigned flags;         /* Various flags */
};

/*
** Allowed values for SQLiteConn.flags
*/
#define FG_INTEGRITY_CHECK  0x0001    /* Run PRAGMA integrity_check */


/*
** Skip forward over whitespace in a string.
*/
static const char *skipWhitespace(const char *z){
  while( isspace(*z) ){ z++; }
  return z;
62
63
64
65
66
67
68
69
70

71
72
73
74
75
76
77
78
79
80
81
82
83






84
85
86
87
88

89
90
91
92
93
94
95
96


97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

119
120
121
122






















123
124
125
126
127
128
129
*/
static int sqliteConnect(
  void *NotUsed,              /* Argument from DbEngine object.  Not used */
  const char *zConnectStr,    /* Connection string */
  void **ppConn,              /* Write completed connection here */
  const char *zParam          /* Value of the -parameters command-line option */
){
  sqlite3 *db;
  int rc;


  /* If the database filename is defined and the database already exists,
  ** then delete the database before we start, thus resetting it to an
  ** empty database.
  */
  if( zConnectStr==0 ) zConnectStr = "test.db";
  if( zConnectStr && zConnectStr[0] ){
#ifndef WIN32
    unlink(zConnectStr);
#else
    _unlink(zConnectStr);
#endif
  }







  /* Open a connection to the new database.
  */
  rc = sqlite3_open(zConnectStr, &db);
  if( rc!=SQLITE_OK ){

    return 1;
  }
  if( zParam ){
    zParam = skipWhitespace(zParam);
    while( zParam[0] ){
      if( memcmp(zParam, "optimizer=", 10)==0 ){
        int x = atoi(&zParam[10]);
        sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, db, x);


      }else{
        fprintf(stderr, "unknown parameter: [%s]\n", zParam);
        exit(1);
      }
      zParam = skipToNextParameter(zParam);
    }
  }
  sqlite3_exec(db, "PRAGMA synchronous=OFF", 0, 0, 0);
  *ppConn = (void*)db;
  return 0;  
}

/*
** Evaluate the single SQL statement given in zSql.  Return 0 on success.
** return non-zero if any error occurs.
*/
static int sqliteStatement(
  void *pConn,                /* Connection created by xConnect */
  const char *zSql,           /* SQL statement to evaluate */
  int bQuiet                  /* True to suppress printing errors. */
){
  int rc;

  sqlite3 *db;

  db = (sqlite3*)pConn;
  rc = sqlite3_exec(db, zSql, 0, 0, 0);






















  return rc!=SQLITE_OK;
}

/*
** Structure used to accumulate a result set.
*/
typedef struct ResAccum ResAccum;







<

>













>
>
>
>
>
>



|

>





|

|
>
>







|
|













>
|

|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







76
77
78
79
80
81
82

83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
*/
static int sqliteConnect(
  void *NotUsed,              /* Argument from DbEngine object.  Not used */
  const char *zConnectStr,    /* Connection string */
  void **ppConn,              /* Write completed connection here */
  const char *zParam          /* Value of the -parameters command-line option */
){

  int rc;
  SQLiteConn *p;

  /* If the database filename is defined and the database already exists,
  ** then delete the database before we start, thus resetting it to an
  ** empty database.
  */
  if( zConnectStr==0 ) zConnectStr = "test.db";
  if( zConnectStr && zConnectStr[0] ){
#ifndef WIN32
    unlink(zConnectStr);
#else
    _unlink(zConnectStr);
#endif
  }

  p = sqlite3_malloc( sizeof(*p) );
  if( p==0 ){
    return 1;
  }
  memset(p, 0, sizeof(*p));

  /* Open a connection to the new database.
  */
  rc = sqlite3_open(zConnectStr, &p->db);
  if( rc!=SQLITE_OK ){
    sqlite3_free(p);
    return 1;
  }
  if( zParam ){
    zParam = skipWhitespace(zParam);
    while( zParam[0] ){
      if( strncmp(zParam, "optimizer=", 10)==0 ){
        int x = atoi(&zParam[10]);
        sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, p->db, x);
      }else if( strncmp(zParam, "integrity_check", 15)==0 ){
        p->flags |= FG_INTEGRITY_CHECK;
      }else{
        fprintf(stderr, "unknown parameter: [%s]\n", zParam);
        exit(1);
      }
      zParam = skipToNextParameter(zParam);
    }
  }
  sqlite3_exec(p->db, "PRAGMA synchronous=OFF", 0, 0, 0);
  *ppConn = (void*)p;
  return 0;  
}

/*
** Evaluate the single SQL statement given in zSql.  Return 0 on success.
** return non-zero if any error occurs.
*/
static int sqliteStatement(
  void *pConn,                /* Connection created by xConnect */
  const char *zSql,           /* SQL statement to evaluate */
  int bQuiet                  /* True to suppress printing errors. */
){
  int rc;
  SQLiteConn *p;
  

  p = (SQLiteConn*)pConn;
  rc = sqlite3_exec(p->db, zSql, 0, 0, 0);
  if( rc==SQLITE_OK && (p->flags & FG_INTEGRITY_CHECK)!=0 ){
    sqlite3_stmt *pStmt;
    rc = sqlite3_prepare_v2(p->db, "PRAGMA integrity_check;", -1, &pStmt, 0);
    if( rc!=SQLITE_OK ){
      fprintf(stderr, "cannot prepare integrity_check\n");
      rc = 1;
    }else{
      char *z;
      if( sqlite3_step(pStmt)!=SQLITE_ROW ){
        fprintf(stderr, "cannot run integrity_check\n");
        rc = 1;
      }else if( (z = (char*)sqlite3_column_text(pStmt,0))==0
            || strcmp(z,"ok")!=0
      ){
        fprintf(stderr, "integrity_check returns: %s\n", z);
        rc = 1;
      }else{
        rc = 0;
      }
      sqlite3_finalize(pStmt);
    }
  }
  return rc!=SQLITE_OK;
}

/*
** Structure used to accumulate a result set.
*/
typedef struct ResAccum ResAccum;
180
181
182
183
184
185
186

187
188
189
190

191
192
193
194
195
196
197
  char ***pazResult,          /* RETURN:  Array of result values */
  int *pnResult               /* RETURN:  Number of result values */
){
  sqlite3 *db;                /* The database connection */
  sqlite3_stmt *pStmt;        /* Prepared statement */
  int rc;                     /* Result code from subroutine calls */
  ResAccum res;               /* query result accumulator */

  char zBuffer[200];          /* Buffer to render numbers */

  memset(&res, 0, sizeof(res));
  db = (sqlite3*)pConn;

  rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
  if( rc!=SQLITE_OK ){
    sqlite3_finalize(pStmt);
    return 1;
  }
  if( strlen(zType)!=sqlite3_column_count(pStmt) ){
    fprintf(stderr, "wrong number of result columns: expected %d but got %d\n",







>



|
>







226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
  char ***pazResult,          /* RETURN:  Array of result values */
  int *pnResult               /* RETURN:  Number of result values */
){
  sqlite3 *db;                /* The database connection */
  sqlite3_stmt *pStmt;        /* Prepared statement */
  int rc;                     /* Result code from subroutine calls */
  ResAccum res;               /* query result accumulator */
  SQLiteConn *p;              /* The database connection */
  char zBuffer[200];          /* Buffer to render numbers */

  memset(&res, 0, sizeof(res));
  p = (SQLiteConn*)pConn;
  db = p->db;
  rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
  if( rc!=SQLITE_OK ){
    sqlite3_finalize(pStmt);
    return 1;
  }
  if( strlen(zType)!=sqlite3_column_count(pStmt) ){
    fprintf(stderr, "wrong number of result columns: expected %d but got %d\n",
274
275
276
277
278
279
280
281

282





283
284
285
286
287
288
289
290
** database files.  If the database is deleted here, that is one approach.
** The other approach is to delete left-over databases in the xConnect
** method.  The SQLite interface takes the latter approach.
*/
static int sqliteDisconnect(
  void *pConn                 /* Connection created by xConnect */
){
  sqlite3 *db = (sqlite3*)pConn;

  sqlite3_close(db);





  return 0;
}

/*
** This routine is called to return the name of the DB engine
** used by the connection pConn.  This name may or may not
** be the same as specified in the DbEngine structure.
**







|
>
|
>
>
>
>
>
|







322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
** database files.  If the database is deleted here, that is one approach.
** The other approach is to delete left-over databases in the xConnect
** method.  The SQLite interface takes the latter approach.
*/
static int sqliteDisconnect(
  void *pConn                 /* Connection created by xConnect */
){
  SQLiteConn *p = (SQLiteConn*)pConn;
  int rc;
  rc = sqlite3_close(p->db);
  sqlite3_free(p);
  if( sqlite3_memory_used()>0 ){
    fprintf(stderr, "memory leak after connection close\n");
    rc = 1;
  }
  return rc;
}

/*
** This routine is called to return the name of the DB engine
** used by the connection pConn.  This name may or may not
** be the same as specified in the DbEngine structure.
**
Changes to src/sqllogictest.c.
61
62
63
64
65
66
67
68
69
70
71
72
73





74



75
76
77
78
79
80
81
82
83
  apEngine[nEngine-1] = (DbEngine *)p;
}

/*
** Print a usage comment and die
*/
static void usage(const char *argv0){
#if 0  /* Obsolete code */
  fprintf(stdout,
    "Usage: %s [-verify] [-engine DBENGINE] [-connection STR] [-ht NUM] script\n",
    argv0);
#else
  fprintf(stdout,





    "Usage: %s [-verify] [-odbc STR] [-ht NUM] script\n",



    argv0);
#endif
  exit(1);
}

/*
** A structure to keep track of the state of scanning the input script.
*/
typedef struct Script Script;







<
|
<
<
<

>
>
>
>
>
|
>
>
>
|
<







61
62
63
64
65
66
67

68



69
70
71
72
73
74
75
76
77
78
79

80
81
82
83
84
85
86
  apEngine[nEngine-1] = (DbEngine *)p;
}

/*
** Print a usage comment and die
*/
static void usage(const char *argv0){

  fprintf(stdout, "Usage: %s [options] script\n", argv0);



  fprintf(stdout,
    "Options:\n"
    "  --connection STR       The connection string\n"
    "  --engine DBENGINE      The engine name (ex: SQLite, ODBC3)\n"
    "  --halt                 Stop when first error is seen\n"
    "  --ht NUM               Check results by hash if numbe of lines > NUM\n"
    "  --odbc STR             Shorthand for \"--engine ODBC3 --connection STR\"\n"
    "  --parameters TXT       Extra parameters to the connection string\n"
    "  --trace                Enable tracing of SQL to standard output\n"
    "  --verify               Use \"verify MODE\"\n"
  );

  exit(1);
}

/*
** A structure to keep track of the state of scanning the input script.
*/
typedef struct Script Script;
304
305
306
307
308
309
310


311
312
313
314
315
316
317

/*
** This is the main routine.  This routine runs first.  It processes
** command-line arguments then runs the test.
*/
int main(int argc, char **argv){
  int verifyMode = 0;                  /* True if in -verify mode */


  const char *zScriptFile = 0;         /* Input script filename */
  const char *zDbEngine = "SQLite";    /* Name of database engine */
  const char *zConnection = 0;         /* Connection string on DB engine */
  const DbEngine *pEngine = 0;         /* Pointer to DbEngine object */
  int i;                               /* Loop counter */
  char *zScript;                       /* Content of the script */
  long nScript;                        /* Size of the script in bytes */







>
>







307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322

/*
** This is the main routine.  This routine runs first.  It processes
** command-line arguments then runs the test.
*/
int main(int argc, char **argv){
  int verifyMode = 0;                  /* True if in -verify mode */
  int haltOnError = 0;                 /* Stop on first error if true */
  int enableTrace = 0;                 /* Trace SQL statements if true */
  const char *zScriptFile = 0;         /* Input script filename */
  const char *zDbEngine = "SQLite";    /* Name of database engine */
  const char *zConnection = 0;         /* Connection string on DB engine */
  const DbEngine *pEngine = 0;         /* Pointer to DbEngine object */
  int i;                               /* Loop counter */
  char *zScript;                       /* Content of the script */
  long nScript;                        /* Size of the script in bytes */
353
354
355
356
357
358
359
360
361
362
363


364
365

366
367
368
369
370
371
372

373
374
375
376
377
378
379
380
  /* Scan the command-line and process arguments
  */
  for(i=1; i<argc; i++){
    int n;
    const char *z = argv[i];
    if( z[0]=='-' && z[1]=='-' ) z++;
    n = (int)strlen(z);
    if( strncmp(z, "-verify",n)==0 ){
      verifyMode = 1;
    }else if( strncmp(z, "-engine",n)==0 ){
      zDbEngine = argv[++i];


    }else if( strncmp(z, "-connection",n)==0 ){
      zConnection = argv[++i];

    }else if( strncmp(z, "-odbc",n)==0 ){
      zDbEngine = "ODBC3";
      zConnection = argv[++i];
    }else if( strncmp(z, "-parameters",n)==0 ){
      zParam = argv[++i];
    }else if( strncmp(z, "-ht",n)==0 ){
      hashThreshold = atoi(argv[++i]);

      bHt = -1;
    }else if( zScriptFile==0 ){
      zScriptFile = z;
    }else{
      fprintf(stderr, "%s: unknown argument: %s\n", argv[0], argv[i]);
      usage(argv[0]);
    }
  }







|
|


>
>
|
|
>





|
|
>
|







358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
  /* Scan the command-line and process arguments
  */
  for(i=1; i<argc; i++){
    int n;
    const char *z = argv[i];
    if( z[0]=='-' && z[1]=='-' ) z++;
    n = (int)strlen(z);
    if( strncmp(z, "-connection",n)==0 ){
      zConnection = argv[++i];
    }else if( strncmp(z, "-engine",n)==0 ){
      zDbEngine = argv[++i];
    }else if( strncmp(z, "-halt",n)==0 ){
      haltOnError = 1;
    }else if( strncmp(z, "-ht",n)==0 ){
      hashThreshold = atoi(argv[++i]);
      bHt = -1;
    }else if( strncmp(z, "-odbc",n)==0 ){
      zDbEngine = "ODBC3";
      zConnection = argv[++i];
    }else if( strncmp(z, "-parameters",n)==0 ){
      zParam = argv[++i];
    }else if( strncmp(z, "-trace",n)==0 ){
      enableTrace = 1;
    }else if( strncmp(z, "-verify",n)==0 ){
      verifyMode = 1;
    }else if( zScriptFile==0 ){
      zScriptFile = z;
    }else{
      fprintf(stderr, "%s: unknown argument: %s\n", argv[0], argv[i]);
      usage(argv[0]);
    }
  }
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
  rc = pEngine->xGetEngineName(pConn, &zDbEngine);
  if( rc ){
    fprintf(stderr, "%s: unable to get DB name from connection\n", argv[0]);
    exit(1);
  }

  /* Loop over all records in the file */
  while( findStartOfNextRecord(&sScript) ){
    int bSkip = 0; /* True if we should skip the current record. */

    /* Tokenizer the first line of the record.  This also records the
    ** line number of the first record in sScript.startLine */
    tokenizeLine(&sScript);

    bSkip = 0;







|







451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
  rc = pEngine->xGetEngineName(pConn, &zDbEngine);
  if( rc ){
    fprintf(stderr, "%s: unable to get DB name from connection\n", argv[0]);
    exit(1);
  }

  /* Loop over all records in the file */
  while( (nErr==0 || !haltOnError) && findStartOfNextRecord(&sScript) ){
    int bSkip = 0; /* True if we should skip the current record. */

    /* Tokenizer the first line of the record.  This also records the
    ** line number of the first record in sScript.startLine */
    tokenizeLine(&sScript);

    bSkip = 0;
534
535
536
537
538
539
540

541
542
543
544
545
546
547
      bExpectOk = strcmp(sScript.azToken[1],"ok")==0;
      bExpectError = strcmp(sScript.azToken[1],"error")==0;

      /* Run the statement.  Remember the results 
      ** If we're expecting an error, pass true to suppress
      ** printing of any errors. 
      */

      rc = pEngine->xStatement(pConn, zScript, bExpectError);
      nCmd++;

      /* Check to see if we are expecting success or failure */
      if( bExpectOk ){
        /* do nothing if we expect success */
      }else if( bExpectError ){







>







543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
      bExpectOk = strcmp(sScript.azToken[1],"ok")==0;
      bExpectError = strcmp(sScript.azToken[1],"error")==0;

      /* Run the statement.  Remember the results 
      ** If we're expecting an error, pass true to suppress
      ** printing of any errors. 
      */
      if( enableTrace ) printf("%s;\n", zScript);
      rc = pEngine->xStatement(pConn, zScript, bExpectError);
      nCmd++;

      /* Check to see if we are expecting success or failure */
      if( bExpectOk ){
        /* do nothing if we expect success */
      }else if( bExpectError ){
593
594
595
596
597
598
599

600
601
602
603
604
605
606
        k += sScript.len;
      }
      zScript[k] = 0;

      /* Run the query */
      nResult = 0;
      azResult = 0;

      rc = pEngine->xQuery(pConn, zScript, sScript.azToken[1],
                           &azResult, &nResult);
      nCmd++;
      if( rc ){
        fprintf(stderr, "%s:%d: query failed\n",
                zScriptFile, sScript.startLine);
        pEngine->xFreeResults(pConn, azResult, nResult);







>







603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
        k += sScript.len;
      }
      zScript[k] = 0;

      /* Run the query */
      nResult = 0;
      azResult = 0;
      if( enableTrace ) printf("%s;\n", zScript);
      rc = pEngine->xQuery(pConn, zScript, sScript.azToken[1],
                           &azResult, &nResult);
      nCmd++;
      if( rc ){
        fprintf(stderr, "%s:%d: query failed\n",
                zScriptFile, sScript.startLine);
        pEngine->xFreeResults(pConn, azResult, nResult);