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: |
86ab9a0ad0c0898e3def39b1fff808cf |
User & Date: | shaneh 2008-12-01 04:54:59.000 |
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
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 | ** 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> | > > > > > > < < < < < | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | 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 | ** 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; /* ** 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){ fprintf(stderr, |
︙ | ︙ | |||
352 353 354 355 356 357 358 | 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--; | | | 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | 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; } |
︙ | ︙ | |||
478 479 480 481 482 483 484 | /* ** 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 */ | | > > > > > > > > > > > > > > | | 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 | /* ** 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 */ 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 */ /* 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 ){ |
︙ | ︙ | |||
672 673 674 675 676 677 678 | } /* 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 */ | | | 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 | } /* 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{ |
︙ | ︙ |