sqllogictest
Check-in [0a1f217d6b]
Not logged in

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

Overview
Comment:Added "skipif" logic. Added xGetEngineName() method to each interface. This may be different then the registered named for ODBC connections, "ODBC3". Various tweaks to error messges.
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:0a1f217d6beb4f4484a8ddf5461aaab09412e2c4
User & Date: shaneh 2008-12-05 20:33:53
Context
2008-12-05
23:09
Initial set of test files with "random" selects. slt_bad_*.proto produced different results on the engines, or didn't run at all. slt_good_*.proto produced exactly the same results with both SQLite and MSSQL, with mostly the same results on MySQL. Will be updating these to skip more tests MySQL has problems with. check-in: 2663d48f3c user: shaneh tags: trunk
20:33
Added "skipif" logic. Added xGetEngineName() method to each interface. This may be different then the registered named for ODBC connections, "ODBC3". Various tweaks to error messges. check-in: 0a1f217d6b user: shaneh tags: trunk
2008-12-03
00:44
Add the select5 test script that stresses multi-way joins up to 64 deep. check-in: 599e260e37 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/slt_odbc3.c.

86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
...
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
...
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
...
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
...
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
...
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
...
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
...
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
...
613
614
615
616
617
618
619


































620
621
622
623
624
625
626
627
628
629
630
631
632
633
634

635
636
637
638
639
640
641
642
643
644
645
646
                        state, 
                        &native, 
                        text,
                        sizeof(text), 
                        &len );
    if (SQL_SUCCEEDED(ret))
    {
      fprintf(stderr,
              "%s:%s:%ld:%ld:%s\n", fn, state, (long)i, (long)native, text);
    }
  }
  while( SQL_SUCCEEDED(ret) );
}

/*
................................................................................
  if( zValue ){
#ifdef WIN32
    z = _strdup(zValue);
#else
    z = strdup(zValue);
#endif
    if( z==0 ){
      fprintf(stderr, "out of memory at %s:%d\n", __FILE__, __LINE__);
      exit(1);
    }
  }else{
    z = 0;
  }
  if( p->nUsed>=p->nAlloc ){
    char **az;
    p->nAlloc += 200;
    az = realloc(p->azValue, p->nAlloc*sizeof(p->azValue[0]));
    if( az==0 ){
      fprintf(stderr, "out of memory at %s:%d\n", __FILE__, __LINE__);
      exit(1);
    }
    p->azValue = az;
  }
  p->azValue[p->nUsed++] = z;
}

................................................................................
    /* How many columns are there */
    SQLNumResultCols(stmt, &columns);
    if( columns != 5 ){
      /* Non-standard result set.  Could be non-standard ODBC
      ** driver, or we're looking at wrong DB.  Return an 
      ** error and force them to fix this by hand. 
      ** We don't want to accidentally delete something important. */
      fprintf(stderr, 
              "Result set of tables has wrong number of columns: %ld\n",
              (long)columns);
      rc = 1;
    }
  }

  if( !rc ){
    /* Loop through the rows in the result-set */
................................................................................
  SQLRETURN ret; /* ODBC API return status */
  ODBC3_Handles *pODBC3conn = NULL;
  char szConnStrIn[512] = "";

  /* Allocate a structure to hold all of our ODBC3 handles */
  pODBC3conn = (ODBC3_Handles *)malloc(sizeof(ODBC3_Handles));
  if( !pODBC3conn ){
    fprintf(stderr, "Out of memory at %s:%d\n", __FILE__, __LINE__);
    return 1;
  }
  pODBC3conn->env = SQL_NULL_HENV;
  pODBC3conn->dbc = SQL_NULL_HDBC;
  pODBC3conn->zConnStr = NULL;

  /* Allocate an environment handle */
................................................................................
  }
  
  /* Allocate storage space for the returned connection information.
  */
  if( !rc ){
    pODBC3conn->zConnStr = (SQLCHAR *)malloc(1024 * sizeof(SQLCHAR));
    if( !pODBC3conn->zConnStr ){
      fprintf(stderr, "Out of memory at %s:%d\n", __FILE__, __LINE__);
      rc = 1;
    }
  }
  
  if( !rc ){
    SQLSMALLINT outStrLen;

................................................................................
    /* How many columns are there */
    ret = SQLNumResultCols(stmt, &columns);
    if( !SQL_SUCCEEDED(ret) && (ret != SQL_SUCCESS_WITH_INFO) ){
      ODBC3_perror("SQLNumResultCols", stmt, SQL_HANDLE_STMT);
      rc = 1;
    }
    if( strlen(zType)!=columns ){
      fprintf(stderr, "Wrong number of result columns: Expected %d but got %d\n",
              (int)strlen(zType), (int)columns);
      rc = 1;
    }
  }

  if( !rc ){
    /* Loop through the rows in the result-set */
................................................................................
              }else{
                sprintf(zBuffer, "%.3f", r);
              }
              ODBC3_appendValue(&res, zBuffer);
              break;
            }
            default: {
              fprintf(stderr, "Unknown character in type-string: %c\n", zType[i-1]);
              rc = 1;
            }
          } /* end switch */
        } /* end for i */
      }
    } while( !rc && SQL_SUCCEEDED(ret) );
  }
................................................................................
  void *pConn                 /* Connection created by xConnect */
){
  int rc = 0;
  SQLRETURN ret; /* ODBC API return status */
  ODBC3_Handles *pODBC3conn = pConn;
  
  if ( !pODBC3conn ){
    fprintf(stderr, "Invalid ODBC3 connection object\n");
    return 1;
  }

  if( pODBC3conn->dbc != SQL_NULL_HDBC ){
    ret = SQLDisconnect(pODBC3conn->dbc);   /* disconnect from driver */
    if( !SQL_SUCCEEDED(ret) && (ret != SQL_SUCCESS_WITH_INFO) ){
      ODBC3_perror("SQLDisconnect", pODBC3conn->dbc, SQL_HANDLE_DBC);
................................................................................
  pODBC3conn->env = SQL_NULL_HENV;
  pODBC3conn->dbc = SQL_NULL_HDBC;
  pODBC3conn->zConnStr = NULL;
  
  free(pODBC3conn);
  return rc;
}



































/*
** This routine registers the ODBC3 database engine with the main
** driver.  New database engine interfaces should have a single
** routine similar to this one.  The main() function below should be
** modified to call that routine upon startup.
*/
void registerODBC3(void){
  /*
  ** This is the object that defines the database engine interface.
  */
  static const DbEngine ODBC3DbEngine = {
     "ODBC3",             /* zName */
     0,                    /* pAuxData */
     ODBC3Connect,        /* xConnect */

     ODBC3Statement,      /* xStatement */
     ODBC3Query,          /* xQuery */
     ODBC3FreeResults,    /* xFreeResults */
     ODBC3Disconnect      /* xDisconnect */
  };
  sqllogictestRegisterEngine(&ODBC3DbEngine);
}

/*
**************** End of the ODBC3 database engine interface *****************
*****************************************************************************/
#endif /* OMIT_ODBC */







|







 







|










|







 







|
|







 







|







 







|







 







|







 







|







 







|







 







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













|

>












86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
...
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
...
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
...
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
...
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
...
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
...
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
...
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
...
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
                        state, 
                        &native, 
                        text,
                        sizeof(text), 
                        &len );
    if (SQL_SUCCEEDED(ret))
    {
      fprintf(stdout,
              "%s:%s:%ld:%ld:%s\n", fn, state, (long)i, (long)native, text);
    }
  }
  while( SQL_SUCCEEDED(ret) );
}

/*
................................................................................
  if( zValue ){
#ifdef WIN32
    z = _strdup(zValue);
#else
    z = strdup(zValue);
#endif
    if( z==0 ){
      fprintf(stderr, "out of memory at %s:%d\n", __FILE__,__LINE__);
      exit(1);
    }
  }else{
    z = 0;
  }
  if( p->nUsed>=p->nAlloc ){
    char **az;
    p->nAlloc += 200;
    az = realloc(p->azValue, p->nAlloc*sizeof(p->azValue[0]));
    if( az==0 ){
      fprintf(stderr, "out of memory at %s:%d\n", __FILE__,__LINE__);
      exit(1);
    }
    p->azValue = az;
  }
  p->azValue[p->nUsed++] = z;
}

................................................................................
    /* How many columns are there */
    SQLNumResultCols(stmt, &columns);
    if( columns != 5 ){
      /* Non-standard result set.  Could be non-standard ODBC
      ** driver, or we're looking at wrong DB.  Return an 
      ** error and force them to fix this by hand. 
      ** We don't want to accidentally delete something important. */
      fprintf(stdout, 
              "result set of tables has wrong number of columns: %ld\n",
              (long)columns);
      rc = 1;
    }
  }

  if( !rc ){
    /* Loop through the rows in the result-set */
................................................................................
  SQLRETURN ret; /* ODBC API return status */
  ODBC3_Handles *pODBC3conn = NULL;
  char szConnStrIn[512] = "";

  /* Allocate a structure to hold all of our ODBC3 handles */
  pODBC3conn = (ODBC3_Handles *)malloc(sizeof(ODBC3_Handles));
  if( !pODBC3conn ){
    fprintf(stderr, "out of memory at %s:%d\n", __FILE__,__LINE__);
    return 1;
  }
  pODBC3conn->env = SQL_NULL_HENV;
  pODBC3conn->dbc = SQL_NULL_HDBC;
  pODBC3conn->zConnStr = NULL;

  /* Allocate an environment handle */
................................................................................
  }
  
  /* Allocate storage space for the returned connection information.
  */
  if( !rc ){
    pODBC3conn->zConnStr = (SQLCHAR *)malloc(1024 * sizeof(SQLCHAR));
    if( !pODBC3conn->zConnStr ){
      fprintf(stderr, "out of memory at %s:%d\n", __FILE__,__LINE__);
      rc = 1;
    }
  }
  
  if( !rc ){
    SQLSMALLINT outStrLen;

................................................................................
    /* How many columns are there */
    ret = SQLNumResultCols(stmt, &columns);
    if( !SQL_SUCCEEDED(ret) && (ret != SQL_SUCCESS_WITH_INFO) ){
      ODBC3_perror("SQLNumResultCols", stmt, SQL_HANDLE_STMT);
      rc = 1;
    }
    if( strlen(zType)!=columns ){
      fprintf(stderr, "wrong number of result columns: expected %d but got %d\n",
              (int)strlen(zType), (int)columns);
      rc = 1;
    }
  }

  if( !rc ){
    /* Loop through the rows in the result-set */
................................................................................
              }else{
                sprintf(zBuffer, "%.3f", r);
              }
              ODBC3_appendValue(&res, zBuffer);
              break;
            }
            default: {
              fprintf(stderr, "unknown character in type-string: %c\n", zType[i-1]);
              rc = 1;
            }
          } /* end switch */
        } /* end for i */
      }
    } while( !rc && SQL_SUCCEEDED(ret) );
  }
................................................................................
  void *pConn                 /* Connection created by xConnect */
){
  int rc = 0;
  SQLRETURN ret; /* ODBC API return status */
  ODBC3_Handles *pODBC3conn = pConn;
  
  if ( !pODBC3conn ){
    fprintf(stderr, "invalid ODBC3 connection object\n");
    return 1;
  }

  if( pODBC3conn->dbc != SQL_NULL_HDBC ){
    ret = SQLDisconnect(pODBC3conn->dbc);   /* disconnect from driver */
    if( !SQL_SUCCEEDED(ret) && (ret != SQL_SUCCESS_WITH_INFO) ){
      ODBC3_perror("SQLDisconnect", pODBC3conn->dbc, SQL_HANDLE_DBC);
................................................................................
  pODBC3conn->env = SQL_NULL_HENV;
  pODBC3conn->dbc = SQL_NULL_HDBC;
  pODBC3conn->zConnStr = NULL;
  
  free(pODBC3conn);
  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.
**
** Then returned DB name does not have to be freed by the called.
**
** This routine should be called only after a valid connection
** has been establihed with xConnect.
**
** For ODBC connections, the engine name is resolved by the 
** driver manager after a connection is made.
*/
static int ODBC3GetEngineName(
  void *pConn,                /* Connection created by xConnect */
  const char **zName          /* SQL statement to evaluate */
){
  SQLRETURN ret; /* ODBC API return status */
  ODBC3_Handles *pODBC3conn = pConn;
  SQLSMALLINT outLen;
  static char zDmbsName[512] = "";

  ret = SQLGetInfo(pODBC3conn->dbc,
                   SQL_DBMS_NAME,
                   zDmbsName,
                   sizeof(zDmbsName),
                   &outLen);
  if( SQL_SUCCEEDED(ret) || (ret == SQL_SUCCESS_WITH_INFO) ){
    *zName = zDmbsName;
    return 0;
  }
  return 1;
}

/*
** This routine registers the ODBC3 database engine with the main
** driver.  New database engine interfaces should have a single
** routine similar to this one.  The main() function below should be
** modified to call that routine upon startup.
*/
void registerODBC3(void){
  /*
  ** This is the object that defines the database engine interface.
  */
  static const DbEngine ODBC3DbEngine = {
     "ODBC3",             /* zName */
     0,                   /* pAuxData */
     ODBC3Connect,        /* xConnect */
     ODBC3GetEngineName,  /* xGetEngineName */
     ODBC3Statement,      /* xStatement */
     ODBC3Query,          /* xQuery */
     ODBC3FreeResults,    /* xFreeResults */
     ODBC3Disconnect      /* xDisconnect */
  };
  sqllogictestRegisterEngine(&ODBC3DbEngine);
}

/*
**************** End of the ODBC3 database engine interface *****************
*****************************************************************************/
#endif /* OMIT_ODBC */

Changes to src/slt_sqlite.c.

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
...
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
...
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
...
242
243
244
245
246
247
248






















249
250
251
252
253
254
255
...
257
258
259
260
261
262
263

264
265
266
267
268
269
270
271
272
273
274
** from malloc.  Or if zValue is NULL, then a NULL pointer is appended.
*/
static void appendValue(ResAccum *p, const char *zValue){
  char *z;
  if( zValue ){
    z = sqlite3_mprintf("%s", zValue);
    if( z==0 ){
      fprintf(stderr, "Out of memory at %s:%d\n", __FILE__, __LINE__);
      exit(1);
    }
  }else{
    z = 0;
  }
  if( p->nUsed>=p->nAlloc ){
    char **az;
    p->nAlloc += 200;
    az = sqlite3_realloc(p->azValue, p->nAlloc*sizeof(p->azValue[0]));
    if( az==0 ){
      fprintf(stderr, "Out of memory at %s:%d\n", __FILE__, __LINE__);
      exit(1);
    }
    p->azValue = az;
  }
  p->azValue[p->nUsed++] = z;
}

................................................................................
  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",
            (int)strlen(zType), sqlite3_column_count(pStmt));
    return 1;
  }
  while( sqlite3_step(pStmt)==SQLITE_ROW ){
    int i;
    for(i=0; zType[i]; i++){
      if( sqlite3_column_type(pStmt, i)==SQLITE_NULL ){
................................................................................
            double r = sqlite3_column_double(pStmt, i);
            sqlite3_snprintf(sizeof(zBuffer), zBuffer, "%.3f", r);
            appendValue(&res, zBuffer);
            break;
          }
          default: {
            sqlite3_finalize(pStmt);
            fprintf(stderr, "Unknown character in type-string: %c\n", zType[i]);
            return 1;
          }
        }
      }
    }
  }
  sqlite3_finalize(pStmt);
................................................................................
static int sqliteDisconnect(
  void *pConn                 /* Connection created by xConnect */
){
  sqlite3 *db = (sqlite3*)pConn;
  sqlite3_close(db);
  return 0;
}























/*
** This routine registers the SQLite database engine with the main
** driver.  New database engine interfaces should have a single
** routine similar to this one.  The main() function below should be
** modified to call that routine upon startup.
*/
................................................................................
  /*
  ** This is the object that defines the database engine interface.
  */
  static const DbEngine sqliteDbEngine = {
     "SQLite",             /* zName */
     0,                    /* pAuxData */
     sqliteConnect,        /* xConnect */

     sqliteStatement,      /* xStatement */
     sqliteQuery,          /* xQuery */
     sqliteFreeResults,    /* xFreeResults */
     sqliteDisconnect      /* xDisconnect */
  };
  sqllogictestRegisterEngine(&sqliteDbEngine);
}

/*
**************** End of the SQLite database engine interface *****************
*****************************************************************************/







|










|







 







|







 







|







 







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







 







>











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
...
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
...
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
...
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
...
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
** from malloc.  Or if zValue is NULL, then a NULL pointer is appended.
*/
static void appendValue(ResAccum *p, const char *zValue){
  char *z;
  if( zValue ){
    z = sqlite3_mprintf("%s", zValue);
    if( z==0 ){
      fprintf(stderr, "out of memory at %s:%d\n", __FILE__,__LINE__);
      exit(1);
    }
  }else{
    z = 0;
  }
  if( p->nUsed>=p->nAlloc ){
    char **az;
    p->nAlloc += 200;
    az = sqlite3_realloc(p->azValue, p->nAlloc*sizeof(p->azValue[0]));
    if( az==0 ){
      fprintf(stderr, "out of memory at %s:%d\n", __FILE__,__LINE__);
      exit(1);
    }
    p->azValue = az;
  }
  p->azValue[p->nUsed++] = z;
}

................................................................................
  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",
            (int)strlen(zType), sqlite3_column_count(pStmt));
    return 1;
  }
  while( sqlite3_step(pStmt)==SQLITE_ROW ){
    int i;
    for(i=0; zType[i]; i++){
      if( sqlite3_column_type(pStmt, i)==SQLITE_NULL ){
................................................................................
            double r = sqlite3_column_double(pStmt, i);
            sqlite3_snprintf(sizeof(zBuffer), zBuffer, "%.3f", r);
            appendValue(&res, zBuffer);
            break;
          }
          default: {
            sqlite3_finalize(pStmt);
            fprintf(stderr, "unknown character in type-string: %c\n", zType[i]);
            return 1;
          }
        }
      }
    }
  }
  sqlite3_finalize(pStmt);
................................................................................
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.
**
** Then returned DB name does not have to be freed by the called.
**
** This routine should be called only after a valid connection
** has been establihed with xConnect.
**
** For ODBC connections, the engine name is resolved by the 
** driver manager after a connection is made.
*/
static int sqliteGetEngineName(
  void *pConn,                /* Connection created by xConnect */
  const char **zName          /* SQL statement to evaluate */
){
  static char *zDmbsName = "SQLite";
  *zName = zDmbsName;
  return 0;
}

/*
** This routine registers the SQLite database engine with the main
** driver.  New database engine interfaces should have a single
** routine similar to this one.  The main() function below should be
** modified to call that routine upon startup.
*/
................................................................................
  /*
  ** This is the object that defines the database engine interface.
  */
  static const DbEngine sqliteDbEngine = {
     "SQLite",             /* zName */
     0,                    /* pAuxData */
     sqliteConnect,        /* xConnect */
     sqliteGetEngineName,  /* xGetEngineName */
     sqliteStatement,      /* xStatement */
     sqliteQuery,          /* xQuery */
     sqliteFreeResults,    /* xFreeResults */
     sqliteDisconnect      /* xDisconnect */
  };
  sqllogictestRegisterEngine(&sqliteDbEngine);
}

/*
**************** End of the SQLite database engine interface *****************
*****************************************************************************/

Changes to src/sqllogictest.c.

50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
...
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
...
314
315
316
317
318
319
320

321
322
323
324
325
326
327
...
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
...
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
...
426
427
428
429
430
431
432








433
434
435

436
437
438
439
























440
441
442
443
444
445
446
...
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
...
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
...
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
...
656
657
658
659
660
661
662
663
664
665
666
667
668
669
/*
** Register a new database engine.
*/
void sqllogictestRegisterEngine(const DbEngine *p){
  nEngine++;
  apEngine = realloc(apEngine, nEngine*sizeof(apEngine[0]));
  if( apEngine==0 ){
    fprintf(stderr, "out or memory - line %d\n", __LINE__);
    exit(1);
  }
  apEngine[nEngine-1] = (DbEngine *)p;
}

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

/*
................................................................................
  for(p = aHash[h]; p; p=p->pNext){
    if( strcmp(p->zKey, zKey)==0 ){
      return strcmp(p->zHash,zHash)!=0;
    }
  }
  p = malloc( sizeof(*p) );
  if( p==0 ){
    fprintf(stderr, "malloc failed on line %d\n", __LINE__);
    exit(1);
  }
  for(i=0; zKey[i] && i<sizeof(p->zKey)-1; i++){ p->zKey[i] = zKey[i]; }
  p->zKey[i] = 0;
  for(i=0; zHash[i] && i<sizeof(p->zHash)-1; i++){ p->zHash[i] = zHash[i]; }
  p->zHash[i] = 0;
  p->pAll = pAll;
................................................................................
  int i;                               /* Loop counter */
  char *zScript;                       /* Content of the script */
  long nScript;                        /* Size of the script in bytes */
  void *pConn;                         /* Connection to the database engine */
  int rc;                              /* Result code from subroutine call */
  int nErr = 0;                        /* Number of errors */
  int nCmd = 0;                        /* Number of SQL statements processed */

  int nResult;                         /* Number of query results */
  char **azResult;                     /* Query result vector */
  Script sScript;                      /* Script parsing status */
  FILE *in;                            /* For reading script */
  int hashThreshold = DEFAULT_HASH_THRESHOLD;  /* Hash result if this long or longer */
  int bHt = 0;                         /* Non-zero if a hash threshold option was */
                                       /* given on the command line. */
................................................................................
#ifndef OMIT_ODBC
  registerODBC3();
#endif

  /* Report an error if no registered engines
  */
  if( nEngine == 0 ){
    fprintf(stderr, "%s: no registered database engines\n", argv[0]);
    usage(argv[0]);
  }

  /* Default to first registered engine 
  */
  if( zDbEngine == NULL ){
    zDbEngine = apEngine[0]->zName;
................................................................................
  ** to use for this run.
  */
  if( zScriptFile==0 ){
    fprintf(stderr, "%s: no input script specified\n", argv[0]);
    usage(argv[0]);
  }
  for(i=0; i<nEngine; i++){
    if( strcmp(zDbEngine, apEngine[i]->zName)==0 ){
      pEngine = apEngine[i];
      break;
    }
  }
  if( pEngine==0 ){
    fprintf(stderr, "%s: unknown database engine: %s\n", argv[0], zDbEngine);
    fprintf(stderr, "Choices are:");
    for(i=0; i<nEngine; i++) fprintf(stderr, " %s", apEngine[i]->zName);
    fprintf(stderr, "\n");
    exit(1);
  }

  /*
  ** Read the entire script file contents into memory
  */
  in = fopen(zScriptFile, "rb");
................................................................................
  /* Open the database engine under test
  */
  rc = pEngine->xConnect(pEngine->pAuxData, zConnection, &pConn);
  if( rc ){
    fprintf(stderr, "%s: unable to connect to database\n", argv[0]);
    exit(1);
  }









  /* Loop over all records in the file */
  while( findStartOfNextRecord(&sScript) ){


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

























    /* Figure out the record type and do appropriate processing */
    if( strcmp(sScript.azToken[0],"statement")==0 ){
      int k = 0;

      /* Extract the SQL from second and subsequent lines of the
      ** record.  Copy the SQL into contiguous memory at the beginning
................................................................................
        rc = !rc;
      }else{
        fprintf(stderr, "%s:%d: statement argument should be 'ok' or 'error'\n",
                zScriptFile, sScript.startLine);
        nErr++;
        rc = 0;
      }

      /* Report an error if the results do not match expectation */
      if( rc ){
        fprintf(stderr, "%s:%d: statement error\n",
                zScriptFile, sScript.startLine);
        nErr++;
      }
    }else if( strcmp(sScript.azToken[0],"query")==0 ){
................................................................................
        ){
          fprintf(stderr, "%s:%d: labeled result [%s] does not agree with "
                          "previous values\n", zScriptFile, 
                          sScript.startLine, sScript.azToken[3]);
          nErr++;
        }
      }

      if( verifyMode ){
        /* In verify mode, first skip over the ---- line if we are still
        ** pointing at it. */
        if( strcmp(sScript.zLine, "----")==0 ) nextLine(&sScript);

        /* Compare subsequent lines of the script against the results
        ** from the query.  Report an error if any differences are found.
        */
        if( hashThreshold==0 || nResult<=hashThreshold ){
          for(i=0; i<nResult && sScript.zLine[0]; nextLine(&sScript), i++){
            if( strcmp(sScript.zLine, azResult[i])!=0 ){
              fprintf(stderr,"%s:%d: wrong result\n", zScriptFile,
                      sScript.nLine);
              nErr++;
              break;
            }
          }
        }else{
          if( strcmp(sScript.zLine, zHash)!=0 ){
................................................................................
      }
    }else if( strcmp(sScript.azToken[0],"halt")==0 ){
      /* Used for debugging.  Stop reading the test script and shut down.
      ** A "halt" record can be inserted in the middle of a test script in
      ** to run the script up to a particular point that is giving a
      ** faulty result, then terminate at that point for analysis.
      */
      fprintf(stderr, "%s:%d: halt\n", zScriptFile, sScript.startLine);
      break;
    }else{
      /* An unrecognized record type is an error */
      fprintf(stderr, "%s:%d: unknown record type: '%s'\n",
              zScriptFile, sScript.startLine, sScript.azToken[0]);
      nErr++;
      break;
................................................................................
  if( rc ){
    fprintf(stderr, "%s: disconnection from database failed\n", argv[0]);
    nErr++;
  }

  /* Report the number of errors and quit.
  */
  if( verifyMode || nErr ){
    printf("%s: %d errors out of %d SQL statement\n",
           zScriptFile, nErr, nCmd);
  }
  free(zScript);
  return nErr; 
}







|










|



|







 







|







 







>







 







|







 







|






|
|
|







 







>
>
>
>
>
>
>
>



>




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







 







|







 







|











|







 







|







 







|
|
|




50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
...
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
...
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
...
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
...
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
...
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
...
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
...
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
...
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
...
690
691
692
693
694
695
696
697
698
699
700
701
702
703
/*
** Register a new database engine.
*/
void sqllogictestRegisterEngine(const DbEngine *p){
  nEngine++;
  apEngine = realloc(apEngine, nEngine*sizeof(apEngine[0]));
  if( apEngine==0 ){
    fprintf(stderr, "out of memory at %s:%d\n", __FILE__,__LINE__);
    exit(1);
  }
  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);
}

/*
................................................................................
  for(p = aHash[h]; p; p=p->pNext){
    if( strcmp(p->zKey, zKey)==0 ){
      return strcmp(p->zHash,zHash)!=0;
    }
  }
  p = malloc( sizeof(*p) );
  if( p==0 ){
    fprintf(stderr, "out of memory at %s:%d\n", __FILE__,__LINE__);
    exit(1);
  }
  for(i=0; zKey[i] && i<sizeof(p->zKey)-1; i++){ p->zKey[i] = zKey[i]; }
  p->zKey[i] = 0;
  for(i=0; zHash[i] && i<sizeof(p->zHash)-1; i++){ p->zHash[i] = zHash[i]; }
  p->zHash[i] = 0;
  p->pAll = pAll;
................................................................................
  int i;                               /* Loop counter */
  char *zScript;                       /* Content of the script */
  long nScript;                        /* Size of the script in bytes */
  void *pConn;                         /* Connection to the database engine */
  int rc;                              /* Result code from subroutine call */
  int nErr = 0;                        /* Number of errors */
  int nCmd = 0;                        /* Number of SQL statements processed */
  int nSkipped = 0;                    /* Number of SQL statements skipped */
  int nResult;                         /* Number of query results */
  char **azResult;                     /* Query result vector */
  Script sScript;                      /* Script parsing status */
  FILE *in;                            /* For reading script */
  int hashThreshold = DEFAULT_HASH_THRESHOLD;  /* Hash result if this long or longer */
  int bHt = 0;                         /* Non-zero if a hash threshold option was */
                                       /* given on the command line. */
................................................................................
#ifndef OMIT_ODBC
  registerODBC3();
#endif

  /* Report an error if no registered engines
  */
  if( nEngine == 0 ){
    fprintf(stderr, "%s: No registered database engines\n", argv[0]);
    usage(argv[0]);
  }

  /* Default to first registered engine 
  */
  if( zDbEngine == NULL ){
    zDbEngine = apEngine[0]->zName;
................................................................................
  ** to use for this run.
  */
  if( zScriptFile==0 ){
    fprintf(stderr, "%s: no input script specified\n", argv[0]);
    usage(argv[0]);
  }
  for(i=0; i<nEngine; i++){
    if( stricmp(zDbEngine, apEngine[i]->zName)==0 ){
      pEngine = apEngine[i];
      break;
    }
  }
  if( pEngine==0 ){
    fprintf(stderr, "%s: unknown database engine: %s\n", argv[0], zDbEngine);
    fprintf(stdout, "Choices are:");
    for(i=0; i<nEngine; i++) fprintf(stdout, " %s", apEngine[i]->zName);
    fprintf(stdout, "\n");
    exit(1);
  }

  /*
  ** Read the entire script file contents into memory
  */
  in = fopen(zScriptFile, "rb");
................................................................................
  /* Open the database engine under test
  */
  rc = pEngine->xConnect(pEngine->pAuxData, zConnection, &pConn);
  if( rc ){
    fprintf(stderr, "%s: unable to connect to database\n", argv[0]);
    exit(1);
  }

  /* Get the "real" db name
  */
  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;
    while( strcmp(sScript.azToken[0],"skipif")==0 ){
      /* This allows skipping a statement or query record for a 
      ** particular database engine.  In this way, SQL features
      ** implmented by a majority of the engines can be tested 
      ** without causing spurious errors for engines that don't
      ** support it.
      **
      ** Once this record is encountered, an the current selected
      ** db interface matches the db engine specified in the record,
      ** the we skip this rest of this record.
      */
      if( stricmp(sScript.azToken[1], zDbEngine)==0 ){
        bSkip = -1;
        break;
      }
      nextLine(&sScript);
      tokenizeLine(&sScript);
    }
    if( bSkip ) {
      nSkipped++;
      continue;
    }

    /* Figure out the record type and do appropriate processing */
    if( strcmp(sScript.azToken[0],"statement")==0 ){
      int k = 0;

      /* Extract the SQL from second and subsequent lines of the
      ** record.  Copy the SQL into contiguous memory at the beginning
................................................................................
        rc = !rc;
      }else{
        fprintf(stderr, "%s:%d: statement argument should be 'ok' or 'error'\n",
                zScriptFile, sScript.startLine);
        nErr++;
        rc = 0;
      }
    
      /* Report an error if the results do not match expectation */
      if( rc ){
        fprintf(stderr, "%s:%d: statement error\n",
                zScriptFile, sScript.startLine);
        nErr++;
      }
    }else if( strcmp(sScript.azToken[0],"query")==0 ){
................................................................................
        ){
          fprintf(stderr, "%s:%d: labeled result [%s] does not agree with "
                          "previous values\n", zScriptFile, 
                          sScript.startLine, sScript.azToken[3]);
          nErr++;
        }
      }
      
      if( verifyMode ){
        /* In verify mode, first skip over the ---- line if we are still
        ** pointing at it. */
        if( strcmp(sScript.zLine, "----")==0 ) nextLine(&sScript);

        /* Compare subsequent lines of the script against the results
        ** from the query.  Report an error if any differences are found.
        */
        if( hashThreshold==0 || nResult<=hashThreshold ){
          for(i=0; i<nResult && sScript.zLine[0]; nextLine(&sScript), i++){
            if( strcmp(sScript.zLine, azResult[i])!=0 ){
              fprintf(stdout,"%s:%d: wrong result\n", zScriptFile,
                      sScript.nLine);
              nErr++;
              break;
            }
          }
        }else{
          if( strcmp(sScript.zLine, zHash)!=0 ){
................................................................................
      }
    }else if( strcmp(sScript.azToken[0],"halt")==0 ){
      /* Used for debugging.  Stop reading the test script and shut down.
      ** A "halt" record can be inserted in the middle of a test script in
      ** to run the script up to a particular point that is giving a
      ** faulty result, then terminate at that point for analysis.
      */
      fprintf(stdout, "%s:%d: halt\n", zScriptFile, sScript.startLine);
      break;
    }else{
      /* An unrecognized record type is an error */
      fprintf(stderr, "%s:%d: unknown record type: '%s'\n",
              zScriptFile, sScript.startLine, sScript.azToken[0]);
      nErr++;
      break;
................................................................................
  if( rc ){
    fprintf(stderr, "%s: disconnection from database failed\n", argv[0]);
    nErr++;
  }

  /* Report the number of errors and quit.
  */
  if( verifyMode || nErr || nSkipped){
    printf("%s: %d errors out of %d SQL statement.  %d skipped.\n",
           zScriptFile, nErr, nCmd, nSkipped);
  }
  free(zScript);
  return nErr; 
}

Changes to src/sqllogictest.h.

30
31
32
33
34
35
36

37
38
39
40
41
42
43
** following structure.
*/
typedef struct DbEngine DbEngine;
struct DbEngine {
  const char *zName;                      /* Name of this engine */
  void *pAuxData;                         /* Aux data passed to xConnect */
  int (*xConnect)(void *pAux, const char *zConnectStr, void **ppConn);

  int (*xStatement)(void*, const char *zSql);
  int (*xQuery)(void*, const char *zSql, const char *zTypes,
                char ***pazResult, int *pnResult);
  int (*xFreeResults)(void*, char **azResult, int nResult);
  int (*xDisconnect)(void*);
};








>







30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
** following structure.
*/
typedef struct DbEngine DbEngine;
struct DbEngine {
  const char *zName;                      /* Name of this engine */
  void *pAuxData;                         /* Aux data passed to xConnect */
  int (*xConnect)(void *pAux, const char *zConnectStr, void **ppConn);
  int (*xGetEngineName)(void*, const char **zName);
  int (*xStatement)(void*, const char *zSql);
  int (*xQuery)(void*, const char *zSql, const char *zTypes,
                char ***pazResult, int *pnResult);
  int (*xFreeResults)(void*, char **azResult, int nResult);
  int (*xDisconnect)(void*);
};