sqllogictest
Check-in [86ab9a0ad0]
Not logged in

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

Overview
Comment:Broke out SQLite interface; Added ODBC3 interface; Minor bug fixes.
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 86ab9a0ad0c0898e3def39b1fff808cf4e496223
User & Date: shaneh 2008-12-01 04:54:59
Context
2008-12-01
14:42
Modified select1.* scripts to work with MS SQL (namely, removing OFFSET/LIMIT clauses); Fixed handle leak in slt_odbc3.c. check-in: 5c63a5e855 user: shaneh tags: trunk
04:54
Broke out SQLite interface; Added ODBC3 interface; Minor bug fixes. check-in: 86ab9a0ad0 user: shaneh tags: trunk
2008-11-30
20:42
Fix the select1.proto test script so that avoids the use of NULL values. In about.wiki, warn user to not use NULL values together with ORDER BY. check-in: f84e0eed40 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Added src/slt_odbc3.c.

















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
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
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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
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
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
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
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
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
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
/*
** Copyright (c) 2008 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License version 2 as published by the Free Software Foundation.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** General Public License for more details.
** 
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA  02111-1307, USA.
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
** Here begins the implementation of the ODBC3 DbEngine object.
**
** This DbEngine expects an ODBC3 DSN named 'sqllogictest'
** and a database named 'slt' available through that DSN to exist.
** The DSN should be accessible to the current user.
**
*/
#ifdef WIN32
#include <windows.h>
#endif
#define SQL_NOUNICODEMAP
#include <sql.h>
#include <sqlext.h>


#define SLT_DSN "sqllogictest"
#define SLT_DB  "slt"


/* forward prototypes */
static int ODBC3Statement(
  void *pConn,                /* Connection created by xConnect */
  const char *zSql            /* SQL statement to evaluate */
);


/*
** Structure used to hold handles needed for ODBC3 connection.
*/
typedef struct ODBC3_Handles ODBC3_Handles;
struct ODBC3_Handles {
  SQLHENV env;
  SQLHDBC dbc;
};

/*
** Utility function to display the details of an ODBC3 error.
*/
static void ODBC3_perror(char *fn,
                         SQLHANDLE handle,
                         SQLSMALLINT type)
{
  SQLSMALLINT i = 0;
  SQLINTEGER native;
  SQLCHAR state[ 7 ];
  SQLCHAR text[256];
  SQLSMALLINT len;
  SQLRETURN ret;

  do
  {
    ret = SQLGetDiagRec(type, 
                        handle, 
                        ++i, 
                        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( ret == SQL_SUCCESS );
}

/*
** Structure used to accumulate a result set.
*/
typedef struct ODBC3_resAccum ODBC3_resAccum;
struct ODBC3_resAccum {
  char **azValue;   /* Array of pointers to values, each malloced separately */
  int nAlloc;       /* Number of slots allocated in azValue */
  int nUsed;        /* Number of slots in azValue used */
};

/*
** Append a value to a result set.  zValue is copied into memory obtained
** from malloc.  Or if zValue is NULL, then a NULL pointer is appended.
*/
static void ODBC3_appendValue(ODBC3_resAccum *p, const char *zValue){
  char *z;
  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;
}

/*
** Drop all tables from the database on the current connection.
** This utility function goes to great lengths to ensure 
** only tables in the test database are dropped. 
*/
static int ODBC3_dropAllTables(ODBC3_Handles *pODBC3conn)
{
  int rc = 0;
  SQLRETURN ret;       /* ODBC API return status */
  SQLSMALLINT columns; /* number of columns in result-set */
  ODBC3_resAccum res;          /* query result accumulator */
  SQLUSMALLINT i;
  char zSql[512];
  SQLHSTMT stmt = SQL_NULL_HSTMT;

  /* zero out accumulator structure */
  memset(&res, 0, sizeof(res));

  /* Allocate a statement handle */
  ret = SQLAllocHandle(SQL_HANDLE_STMT, pODBC3conn->dbc, &stmt);
  if( !SQL_SUCCEEDED(ret) && (ret != SQL_SUCCESS_WITH_INFO) ){
    ODBC3_perror("SQLAllocHandle", pODBC3conn->dbc, SQL_HANDLE_DBC);
    return 1;
  }

  /* Retrieve a list of tables */
  ret = SQLTables(stmt, NULL, 0, NULL, 0, NULL, 0, "TABLE", SQL_NTS);
  if( !SQL_SUCCEEDED(ret) && (ret != SQL_SUCCESS_WITH_INFO) ){
    ODBC3_perror("SQLTables", stmt, SQL_HANDLE_STMT);
    rc = 1;
  }
  
  if( !rc ){
    /* 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 */
    do {
      ret = SQLFetch(stmt);
      if (SQL_SUCCEEDED(ret)) {
        /* Loop through the columns in the row */
        for( i=1; i<=columns; i++ ){
          SQLINTEGER indicator;
          char zBuffer[512];
          /* retrieve column data as a string */
          ret = SQLGetData(stmt, 
                           i, SQL_C_CHAR,
                           zBuffer, sizeof(zBuffer), 
                           &indicator);
          if (SQL_SUCCEEDED(ret)) {
            /* Handle null columns */
            if (indicator == SQL_NULL_DATA){
              strcpy(zBuffer, "NULL");
            } else if( *zBuffer == '\0' ) {
              strcpy(zBuffer, "(empty)");
            }
            /* add it to the result list */
            ODBC3_appendValue(&res, zBuffer);
          }
        } /* end for i */
      }
    } while (SQL_SUCCEEDED(ret) || (ret ==  SQL_SUCCESS_WITH_INFO));
  }
  
  if( stmt != SQL_NULL_HSTMT ){
    SQLFreeHandle(SQL_HANDLE_STMT, stmt);
  }

  if( !rc ){
    /* for each valid table found, drop it */
    for( i=0; !rc && (i+4<res.nUsed); i+=5 ){
      if(    (0 == strcmp(res.azValue[i], SLT_DB))
          && (0 == strcmp(res.azValue[i+1], "dbo"))
          && (0 == strcmp(res.azValue[i+3], "TABLE"))
          && (0 == strcmp(res.azValue[i+4], "NULL")) ){
        sprintf(zSql, "DROP TABLE %s", res.azValue[i+2]);
        rc = ODBC3Statement(pODBC3conn, zSql);
      }
    }
  }
  
  return rc;
}


/*
** This routine is called to open a connection to a new, empty database.
** The zConnectStr argument is the value of the -connection command-line
** option.  This is intended to contain information on how to connect to
** the database engine.  The zConnectStr argument will be NULL if there
** is no -connection on the command-line.  In the case of ODBC3, the
** zConnectStr is the DSN to open.
**
** An object that describes the newly opened and initialized database
** connection is returned by writing into *ppConn.
**
** This routine returns 0 on success and non-zero if there are any errors.
*/
static int ODBC3Connect(
  void *NotUsed,              /* Argument from DbEngine object.  Not used */
  const char *zConnectStr,    /* Connection string */
  void **ppConn               /* Write completed connection here */
){
  int rc = 0;
  SQLRETURN ret; /* ODBC API return status */
  ODBC3_Handles *pODBC3conn = NULL;
  char szSqlConnStr[512];

  /* Allocate a structure to hold all of our ODBC3 handles */
  pODBC3conn = (ODBC3_Handles *)malloc(sizeof(ODBC3_Handles));
  if( !pODBC3conn ){
    return 1;
  }
  pODBC3conn->env = SQL_NULL_HENV;
  pODBC3conn->dbc = SQL_NULL_HDBC;

  /* Allocate an environment handle */
  SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &pODBC3conn->env);
  /* We want ODBC 3 support */
  SQLSetEnvAttr(pODBC3conn->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
  /* Allocate a connection handle */
  SQLAllocHandle(SQL_HANDLE_DBC, pODBC3conn->env, &pODBC3conn->dbc);
  /* TBD: check all of the above for failures */

  /* Build the connection string.
  */
  sprintf(szSqlConnStr, "DSN=" SLT_DSN ";DATABASE=" SLT_DB ";%s", ( zConnectStr ) ? zConnectStr : "");

  /* Open a connection to the new database.
  */
  /* TBD: should we use SQLConnect() here? */
  ret = SQLDriverConnect(pODBC3conn->dbc, 
                         NULL, 
                         (SQLCHAR *)szSqlConnStr, 
                         SQL_NTS,
                         NULL, 
                         0, 
                         NULL,
                         SQL_DRIVER_COMPLETE);
  if( !SQL_SUCCEEDED(ret) && (ret != SQL_SUCCESS_WITH_INFO) ){
    ODBC3_perror("SQLDriverConnect", pODBC3conn->dbc, SQL_HANDLE_DBC);
    rc = 1;
  }
  
  /* Loop over all tables, etc. available in the database and drop them,
  ** thus resetting it to an empty database.  */
  if( !rc ){
    rc = ODBC3_dropAllTables(pODBC3conn);
  }
  
  /* TBD: is there a way to specify synchronous=OFF or equivalent */

  /* store connection info */
  *ppConn = (void*)pODBC3conn;

  return rc;  
}

/*
** Evaluate the single SQL statement given in zSql.  Return 0 on success.
** return non-zero if any error occurs.
*/
static int ODBC3Statement(
  void *pConn,                /* Connection created by xConnect */
  const char *zSql            /* SQL statement to evaluate */
){
  int rc = 0;
  SQLRETURN ret; /* ODBC API return status */
  ODBC3_Handles *pODBC3conn = pConn;
  SQLHSTMT stmt = SQL_NULL_HSTMT;

  /* Allocate a statement handle */
  ret = SQLAllocHandle(SQL_HANDLE_STMT, pODBC3conn->dbc, &stmt);
  if( !SQL_SUCCEEDED(ret) && (ret != SQL_SUCCESS_WITH_INFO) ){
    ODBC3_perror("SQLAllocHandle", pODBC3conn->dbc, SQL_HANDLE_DBC);
    return 1;
  }

  ret = SQLExecDirect(stmt, (SQLCHAR *)zSql, SQL_NTS);
  if( !SQL_SUCCEEDED(ret) && (ret != SQL_SUCCESS_WITH_INFO) ){
    ODBC3_perror("SQLExecDirect", stmt, SQL_HANDLE_STMT);
    rc = 1;
  }

  if( stmt != SQL_NULL_HSTMT ){
    SQLFreeHandle(SQL_HANDLE_STMT, stmt);
  }

 return rc;
}

/*
** This interface runs a query and accumulates the results into an array
** of pointers to strings.  *pazResult is made to point to the resulting
** array and *pnResult is set to the number of elements in the array.
**
** NULL values in the result set should be represented by a string "NULL".
** Empty strings should be shown as "(empty)".  Unprintable and
** control characters should be rendered as "@".
**
** Return 0 on success and 1 if there is an error.  It is not necessary
** to initialize *pazResult or *pnResult if an error occurs.
*/
static int ODBC3Query(
  void *pConn,                /* Connection created by xConnect */
  const char *zSql,           /* SQL statement to evaluate */
  const char *zType,          /* One character for each column of result */
  char ***pazResult,          /* RETURN:  Array of result values */
  int *pnResult               /* RETURN:  Number of result values */
){
  SQLRETURN ret; /* ODBC API return status */
  ODBC3_Handles *pODBC3conn = pConn;
  ODBC3_resAccum res;          /* query result accumulator */
  char zBuffer[512];          /* Buffer to render numbers */
  SQLSMALLINT columns;        /* number of columns in result-set */
  SQLHSTMT stmt = SQL_NULL_HSTMT;

  /* zero out accumulator structure */
  memset(&res, 0, sizeof(res));

  /* Allocate a statement handle */
  ret = SQLAllocHandle(SQL_HANDLE_STMT, pODBC3conn->dbc, &stmt);
  if( !SQL_SUCCEEDED(ret) && (ret != SQL_SUCCESS_WITH_INFO) ){
    ODBC3_perror("SQLAllocHandle", pODBC3conn->dbc, SQL_HANDLE_DBC);
    return 1;
  }

  ret = SQLExecDirect(stmt, (SQLCHAR *)zSql, SQL_NTS);
  if( !SQL_SUCCEEDED(ret) && (ret != SQL_SUCCESS_WITH_INFO) ){
    ODBC3_perror("SQLExecDirect", stmt, SQL_HANDLE_STMT);
    return 1;
  }

  /* How many columns are there */
  SQLNumResultCols(stmt, &columns);

  /* Loop through the rows in the result-set */
  while( SQL_SUCCEEDED(ret = SQLFetch(stmt)) ){
      SQLUSMALLINT i;
      /* Loop through the columns */
      for(i = 1; i <= columns; i++){
          SQLINTEGER indicator;
          /* retrieve column data as a string */
          ret = SQLGetData(stmt, 
                           i, 
                           SQL_C_CHAR,
                           zBuffer, 
                           sizeof(zBuffer), 
                           &indicator);
          if( SQL_SUCCEEDED(ret) ){
              char *z;
              /* Handle null columns */
              if( indicator == SQL_NULL_DATA ){ 
                strcpy(zBuffer, "NULL");
              /* Handle empty columns */
              } else if( *zBuffer == '\0' ) {
                strcpy(zBuffer, "(empty)");
              }
              ODBC3_appendValue(&res, zBuffer);
              /* Convert non-printing and control characters to '@' */
              z = res.azValue[res.nUsed-1];
              while( *z ){
                if( *z<' ' || *z>'~' ){ *z = '@'; }
                z++;
              }
          }
      }
  }
  *pazResult = res.azValue;
  *pnResult = res.nUsed;
  return 0;
}


/*
** This interface is called to free the memory that was returned
** by xQuery.
**
** It might be the case that nResult==0 or azResult==0.
*/
static int ODBC3FreeResults(
  void *pConn,                /* Connection created by xConnect */
  char **azResult,            /* The results to be freed */
  int nResult                 /* Number of rows of result */
){
  int i;
  for(i=0; i<nResult; i++){
    free(azResult[i]);
  }
  free(azResult);
  return 0;
}


/*
** This routine is called to close a connection previously opened
** by xConnect.
**
** This routine may or may not delete the database.  Whichever way
** it works, steps should be taken to avoid an accumulation of left-over
** 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 ODBC3 interface takes the latter approach.
*/
static int ODBC3Disconnect(
  void *pConn                 /* Connection created by xConnect */
){
  int rc = 0;
  SQLRETURN ret; /* ODBC API return status */
  ODBC3_Handles *pODBC3conn = pConn;
  
  if ( !pODBC3conn ){
    return 1;
  }

  ret = SQLDisconnect(pODBC3conn->dbc);   /* disconnect from driver */
  if( !SQL_SUCCEEDED(ret) && (ret != SQL_SUCCESS_WITH_INFO) ){
    ODBC3_perror("SQLDisconnect", pODBC3conn->dbc, SQL_HANDLE_DBC);
    rc = 1;
  }
  
  if( pODBC3conn->dbc != SQL_NULL_HDBC ){
    SQLFreeHandle(SQL_HANDLE_DBC, pODBC3conn->dbc);
  }
  if( pODBC3conn->env != SQL_NULL_HENV ){
    SQLFreeHandle(SQL_HANDLE_ENV, pODBC3conn->env);
  }

  pODBC3conn->env = SQL_NULL_HENV;
  pODBC3conn->dbc = SQL_NULL_HDBC;

  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 *****************
*****************************************************************************/

Added src/slt_sqlite.c.

























































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
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
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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
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
/*
** Copyright (c) 2008 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License version 2 as published by the Free Software Foundation.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** General Public License for more details.
** 
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA  02111-1307, USA.
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
** Here begins the implementation of the SQLite DbEngine object.
**
** Use this interface as a model for other database engine interfaces.
*/
#include "sqlite3.h"

/*
** This routine is called to open a connection to a new, empty database.
** The zConnectStr argument is the value of the -connection command-line
** option.  This is intended to contain information on how to connect to
** the database engine.  The zConnectStr argument will be NULL if there
** is no -connection on the command-line.  In the case of SQLite, the
** zConnectStr is the name of the database file to open.
**
** An object that describes the newly opened and initialized database
** connection is returned by writing into *ppConn.
**
** This routine returns 0 on success and non-zero if there are any errors.
*/
static int sqliteConnect(
  void *NotUsed,              /* Argument from DbEngine object.  Not used */
  const char *zConnectStr,    /* Connection string */
  void **ppConn               /* Write completed connection here */
){
  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 ){
#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;
  }
  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 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;
struct ResAccum {
  char **azValue;   /* Array of pointers to values, each malloced separately */
  int nAlloc;       /* Number of slots allocated in azValue */
  int nUsed;        /* Number of slots in azValue used */
};

/*
** Append a value to a result set.  zValue is copied into memory obtained
** 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;
}

/*
** This interface runs a query and accumulates the results into an array
** of pointers to strings.  *pazResult is made to point to the resulting
** array and *pnResult is set to the number of elements in the array.
**
** NULL values in the result set should be represented by a string "NULL".
** Empty strings should be shown as "(empty)".  Unprintable and
** control characters should be rendered as "@".
**
** Return 0 on success and 1 if there is an error.  It is not necessary
** to initialize *pazResult or *pnResult if an error occurs.
*/
static int sqliteQuery(
  void *pConn,                /* Connection created by xConnect */
  const char *zSql,           /* SQL statement to evaluate */
  const char *zType,          /* One character for each column of result */
  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;
  }
  while( sqlite3_step(pStmt)==SQLITE_ROW ){
    int i;
    for(i=0; zType[i]; i++){
      if( sqlite3_column_type(pStmt, i)==SQLITE_NULL ){
        appendValue(&res, "NULL");
      }else{
        switch( zType[i] ){
          case 'T': {
            const char *zValue = (const char*)sqlite3_column_text(pStmt, i);
            char *z;
            if( zValue[0]==0 ) zValue = "(empty)";
            appendValue(&res, zValue);

            /* Convert non-printing and control characters to '@' */
            z = res.azValue[res.nUsed-1];
            while( *z ){
              if( *z<' ' || *z>'~' ){ *z = '@'; }
              z++;
            }
            break;
          }
          case 'I': {
            int ii = sqlite3_column_int(pStmt, i);
            sqlite3_snprintf(sizeof(zBuffer), zBuffer, "%d", ii);
            appendValue(&res, zBuffer);
            break;
          }
          case 'R': {
            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);
  *pazResult = res.azValue;
  *pnResult = res.nUsed;
  return 0;
}

/*
** This interface is called to free the memory that was returned
** by xQuery.
**
** It might be the case that nResult==0 or azResult==0.
*/
static int sqliteFreeResults(
  void *pConn,                /* Connection created by xConnect */
  char **azResult,            /* The results to be freed */
  int nResult                 /* Number of rows of result */
){
  int i;
  for(i=0; i<nResult; i++){
    sqlite3_free(azResult[i]);
  }
  sqlite3_free(azResult);
  return 0;
}

/*
** This routine is called to close a connection previously opened
** by xConnect.
**
** This routine may or may not delete the database.  Whichever way
** it works, steps should be taken to avoid an accumulation of left-over
** 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 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.
*/
void registerSqlite(void){
  /*
  ** 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 *****************
*****************************************************************************/

Changes to src/sqllogictest.c.

19
20
21
22
23
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
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
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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
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
278
279
280
281
282
283
...
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
...
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
...
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
...
498
499
500
501
502
503
504














505
506
507
508
509
510
511
512
513
514
515
516
...
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This main driver for the sqllogictest program.
*/




#include "sqllogictest.h"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#include <unistd.h>

#include <string.h>

/*****************************************************************************
** Here begins the implementation of the SQLite DbEngine object.
**
** Use this interface as a model for other database engine interfaces.
*/
#include "sqlite3.h"

/*
** This routine is called to open a connection to a new, empty database.
** The zConnectStr argument is the value of the -connection command-line
** option.  This is intended to contain information on how to connect to
** the database engine.  The zConnectStr argument will be NULL if there
** is no -connection on the command-line.  In the case of SQLite, the
** zConnectStr is the name of the database file to open.
**
** An object that describes the newly opened and initialized database
** connection is returned by writing into *ppConn.
**
** This routine returns 0 on success and non-zero if there are any errors.
*/
static int sqliteConnect(
  void *NotUsed,              /* Argument from DbEngine object.  Not used */
  const char *zConnectStr,    /* Connection string */
  void **ppConn               /* Write completed connection here */
){
  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 ){
    unlink(zConnectStr);
  }

  /* Open a connection to the new database.
  */
  rc = sqlite3_open(zConnectStr, &db);
  if( rc!=SQLITE_OK ){
    return 1;
  }
  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 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;
struct ResAccum {
  char **azValue;   /* Array of pointers to values, each malloced separately */
  int nAlloc;       /* Number of slots allocated in azValue */
  int nUsed;        /* Number of slots in azValue used */
};

/*
** Append a value to a result set.  zValue is copied into memory obtained
** 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;
}

/*
** This interface runs a query and accumulates the results into an array
** of pointers to strings.  *pazResult is made to point to the resulting
** array and *pnResult is set to the number of elements in the array.
**
** NULL values in the result set should be represented by a string "NULL".
** Empty strings should be shown as "(empty)".  Unprintable and
** control characters should be rendered as "@".
**
** Return 0 on success and 1 if there is an error.  It is not necessary
** to initialize *pazResult or *pnResult if an error occurs.
*/
static int sqliteQuery(
  void *pConn,                /* Connection created by xConnect */
  const char *zSql,           /* SQL statement to evaluate */
  const char *zType,          /* One character for each column of result */
  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;
  }
  while( sqlite3_step(pStmt)==SQLITE_ROW ){
    int i;
    for(i=0; zType[i]; i++){
      if( sqlite3_column_type(pStmt, i)==SQLITE_NULL ){
        appendValue(&res, "NULL");
      }else{
        switch( zType[i] ){
          case 'T': {
            const char *zValue = (const char*)sqlite3_column_text(pStmt, i);
            char *z;
            if( zValue[0]==0 ) zValue = "(empty)";
            appendValue(&res, zValue);

            /* Convert non-printing and control characters to '@' */
            z = res.azValue[res.nUsed-1];
            while( *z ){
              if( *z<' ' || *z>'~' ){ *z = '@'; }
              z++;
            }
            break;
          }
          case 'I': {
            int ii = sqlite3_column_int(pStmt, i);
            sqlite3_snprintf(sizeof(zBuffer), zBuffer, "%d", ii);
            appendValue(&res, zBuffer);
            break;
          }
          case 'R': {
            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);
  *pazResult = res.azValue;
  *pnResult = res.nUsed;
  return 0;
}

/*
** This interface is called to free the memory that was returned
** by xQuery.
**
** It might be the case that nResult==0 or azResult==0.
*/
static int sqliteFreeResults(
  void *pConn,                /* Connection created by xConnect */
  char **azResult,            /* The results to be freed */
  int nResult                 /* Number of rows of result */
){
  int i;
  for(i=0; i<nResult; i++){
    sqlite3_free(azResult[i]);
  }
  sqlite3_free(azResult);
  return 0;
}

/*
** This routine is called to close a connection previously opened
** by xConnect.
**
** This routine may or may not delete the database.  Whichever way
** it works, steps should be taken to avoid an accumulation of left-over
** 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 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.
*/
void registerSqlite(void){
  /*
  ** 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 *****************
*****************************************************************************/



/*
** An array of registered database engines
*/
static int nEngine = 0;
static const DbEngine **apEngine = 0;

................................................................................
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] = p;
}

/*
** Print a usage comment and die
*/
static void usage(const char *argv0){
  fprintf(stderr,
................................................................................
    p->len = i - p->iCur;
    p->iNext = i+1;

    /* If the current line ends in a \r then remove the \r. */
    if( p->len>0 && p->zScript[i-1]=='\r' ){
      p->len--;
      i--;
      p->zScript[i-1] = 0;
    }

    /* If the line consists of all spaces, make it an empty line */
    for(i=i-1; i>=p->iCur && isspace(p->zScript[i]); i--){}
    if( i<p->iCur ){
      p->zLine[0] = 0;
    }
................................................................................
/*
** 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 */
  void *pConn;                         /* Connection to the database engine */
  int rc;                              /* Result code from subroutine call */
................................................................................
  FILE *in;                            /* For reading script */
  

  /* Add calls to the registration procedures for new database engine
  ** interfaces here
  */
  registerSqlite();















  /* Scan the command-line and process arguments
  */
  for(i=1; i<argc; i++){
    int n = strlen(argv[i]);
    if( strncmp(argv[i], "-verify",n)==0 ){
      verifyMode = 1;
    }else if( strncmp(argv[i], "-engine",n)==0 ){
      zDbEngine = argv[++i];
    }else if( strncmp(argv[i], "-connection",n)==0 ){
      zConnection = argv[++i];
    }else if( zScriptFile==0 ){
................................................................................
      }

      /* Do any required sorting of query results */
      if( sScript.azToken[2][0]==0 || strcmp(sScript.azToken[2],"nosort")==0 ){
        /* Do no sorting */
      }else if( strcmp(sScript.azToken[2],"rowsort")==0 ){
        /* Row-oriented sorting */
        nColumn = strlen(sScript.azToken[1]);
        qsort(azResult, nResult/nColumn, sizeof(azResult[0])*nColumn,
              rowCompare);
      }else if( strcmp(sScript.azToken[2],"valuesort")==0 ){
        /* Sort all values independently */
        nColumn = 1;
        qsort(azResult, nResult, sizeof(azResult[0]), rowCompare);
      }else{







>
>
>
>




>

>


<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
>







 







|







 







|







 







|







 







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




|







 







|







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38





39














































































































































































































































40
41
42
43
44
45
46
47
..
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
...
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
...
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
...
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
...
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This main driver for the sqllogictest program.
*/
#if WIN32
#define _CRT_SECURE_NO_WARNINGS
#endif

#include "sqllogictest.h"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include <string.h>






#include "slt_sqlite.c"














































































































































































































































#include "slt_odbc3.c"

/*
** An array of registered database engines
*/
static int nEngine = 0;
static const DbEngine **apEngine = 0;

................................................................................
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){
  fprintf(stderr,
................................................................................
    p->len = i - p->iCur;
    p->iNext = i+1;

    /* If the current line ends in a \r then remove the \r. */
    if( p->len>0 && p->zScript[i-1]=='\r' ){
      p->len--;
      i--;
      p->zScript[i] = 0;
    }

    /* If the line consists of all spaces, make it an empty line */
    for(i=i-1; i>=p->iCur && isspace(p->zScript[i]); i--){}
    if( i<p->iCur ){
      p->zLine[0] = 0;
    }
................................................................................
/*
** 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 = NULL;        /* 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 */
  void *pConn;                         /* Connection to the database engine */
  int rc;                              /* Result code from subroutine call */
................................................................................
  FILE *in;                            /* For reading script */
  

  /* Add calls to the registration procedures for new database engine
  ** interfaces here
  */
  registerSqlite();
  registerODBC3();

  /* 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;
  }

  /* Scan the command-line and process arguments
  */
  for(i=1; i<argc; i++){
    int n = (int)strlen(argv[i]);
    if( strncmp(argv[i], "-verify",n)==0 ){
      verifyMode = 1;
    }else if( strncmp(argv[i], "-engine",n)==0 ){
      zDbEngine = argv[++i];
    }else if( strncmp(argv[i], "-connection",n)==0 ){
      zConnection = argv[++i];
    }else if( zScriptFile==0 ){
................................................................................
      }

      /* Do any required sorting of query results */
      if( sScript.azToken[2][0]==0 || strcmp(sScript.azToken[2],"nosort")==0 ){
        /* Do no sorting */
      }else if( strcmp(sScript.azToken[2],"rowsort")==0 ){
        /* Row-oriented sorting */
        nColumn = (int)strlen(sScript.azToken[1]);
        qsort(azResult, nResult/nColumn, sizeof(azResult[0])*nColumn,
              rowCompare);
      }else if( strcmp(sScript.azToken[2],"valuesort")==0 ){
        /* Sort all values independently */
        nColumn = 1;
        qsort(azResult, nResult, sizeof(azResult[0]), rowCompare);
      }else{