Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fix the TCL interface so that SQL functions implemented in TCL honor the "nullvalue" setting. Also remove from the TCL interface some unused legacy UTF8 translation code left over from SQLite2. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | branch-3.7.14 |
Files: | files | file ages | folders |
SHA1: |
9bf64b6612c243ea66d04f502dc23f87 |
User & Date: | drh 2012-10-03 11:11:01 |
Context
2012-10-04
| ||
15:36 | Increase the version number to 3.7.14.1. The version of autoconf used is different from the previous release so there are huge differences in the generated "configure" script. (check-in: 972dbd5f user: drh tags: branch-3.7.14) | |
2012-10-03
| ||
11:11 | Fix the TCL interface so that SQL functions implemented in TCL honor the "nullvalue" setting. Also remove from the TCL interface some unused legacy UTF8 translation code left over from SQLite2. (check-in: 9bf64b66 user: drh tags: branch-3.7.14) | |
2012-10-02
| ||
23:26 | Work around an optimization issue with the MSVC compiler for ARM. (check-in: 9fab9edd user: drh tags: branch-3.7.14) | |
Changes
Changes to src/tclsqlite.c.
︙ | ︙ | |||
49 50 51 52 53 54 55 | #undef TCL_STORAGE_CLASS #define TCL_STORAGE_CLASS DLLEXPORT #endif /* BUILD_sqlite */ #define NUM_PREPARED_STMTS 10 #define MAX_PREPARED_STMTS 100 | | | < < < < < < < > | 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 | #undef TCL_STORAGE_CLASS #define TCL_STORAGE_CLASS DLLEXPORT #endif /* BUILD_sqlite */ #define NUM_PREPARED_STMTS 10 #define MAX_PREPARED_STMTS 100 /* Forward declaration */ typedef struct SqliteDb SqliteDb; /* ** New SQL functions can be created as TCL scripts. Each such function ** is described by an instance of the following structure. */ typedef struct SqlFunc SqlFunc; struct SqlFunc { Tcl_Interp *interp; /* The TCL interpret to execute the function */ Tcl_Obj *pScript; /* The Tcl_Obj representation of the script */ SqliteDb *pDb; /* Database connection that owns this function */ int useEvalObjv; /* True if it is safe to use Tcl_EvalObjv */ char *zName; /* Name of this function */ SqlFunc *pNext; /* Next function on the list of them all */ }; /* ** New collation sequences function can be created as TCL scripts. Each such |
︙ | ︙ | |||
109 110 111 112 113 114 115 | ** that has been opened by the SQLite TCL interface. ** ** If this module is built with SQLITE_TEST defined (to create the SQLite ** testfixture executable), then it may be configured to use either ** sqlite3_prepare_v2() or sqlite3_prepare() to prepare SQL statements. ** If SqliteDb.bLegacyPrepare is true, sqlite3_prepare() is used. */ | < | 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | ** that has been opened by the SQLite TCL interface. ** ** If this module is built with SQLITE_TEST defined (to create the SQLite ** testfixture executable), then it may be configured to use either ** sqlite3_prepare_v2() or sqlite3_prepare() to prepare SQL statements. ** If SqliteDb.bLegacyPrepare is true, sqlite3_prepare() is used. */ struct SqliteDb { sqlite3 *db; /* The "real" database structure. MUST BE FIRST */ Tcl_Interp *interp; /* The interpreter used for this database */ char *zBusy; /* The busy callback routine */ char *zCommit; /* The commit hook callback routine */ char *zTrace; /* The trace callback routine */ char *zProfile; /* The profile callback routine */ |
︙ | ︙ | |||
427 428 429 430 431 432 433 434 435 436 437 438 439 440 | for(p=pDb->pFunc; p; p=p->pNext){ if( strcmp(p->zName, pNew->zName)==0 ){ Tcl_Free((char*)pNew); return p; } } pNew->interp = pDb->interp; pNew->pScript = 0; pNew->pNext = pDb->pFunc; pDb->pFunc = pNew; return pNew; } /* | > | 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 | for(p=pDb->pFunc; p; p=p->pNext){ if( strcmp(p->zName, pNew->zName)==0 ){ Tcl_Free((char*)pNew); return p; } } pNew->interp = pDb->interp; pNew->pDb = pDb; pNew->pScript = 0; pNew->pNext = pDb->pFunc; pDb->pFunc = pNew; return pNew; } /* |
︙ | ︙ | |||
474 475 476 477 478 479 480 481 482 483 484 485 486 487 | SqliteDb *pDb = (SqliteDb*)db; flushStmtCache(pDb); closeIncrblobChannels(pDb); sqlite3_close(pDb->db); while( pDb->pFunc ){ SqlFunc *pFunc = pDb->pFunc; pDb->pFunc = pFunc->pNext; Tcl_DecrRefCount(pFunc->pScript); Tcl_Free((char*)pFunc); } while( pDb->pCollate ){ SqlCollate *pCollate = pDb->pCollate; pDb->pCollate = pCollate->pNext; Tcl_Free((char*)pCollate); | > | 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 | SqliteDb *pDb = (SqliteDb*)db; flushStmtCache(pDb); closeIncrblobChannels(pDb); sqlite3_close(pDb->db); while( pDb->pFunc ){ SqlFunc *pFunc = pDb->pFunc; pDb->pFunc = pFunc->pNext; assert( pFunc->pDb==pDb ); Tcl_DecrRefCount(pFunc->pScript); Tcl_Free((char*)pFunc); } while( pDb->pCollate ){ SqlCollate *pCollate = pDb->pCollate; pDb->pCollate = pCollate->pNext; Tcl_Free((char*)pCollate); |
︙ | ︙ | |||
790 791 792 793 794 795 796 | } case SQLITE_FLOAT: { double r = sqlite3_value_double(pIn); pVal = Tcl_NewDoubleObj(r); break; } case SQLITE_NULL: { | | | 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 | } case SQLITE_FLOAT: { double r = sqlite3_value_double(pIn); pVal = Tcl_NewDoubleObj(r); break; } case SQLITE_NULL: { pVal = Tcl_NewStringObj(p->pDb->zNull, -1); break; } default: { int bytes = sqlite3_value_bytes(pIn); pVal = Tcl_NewStringObj((char *)sqlite3_value_text(pIn), bytes); break; } |
︙ | ︙ | |||
929 930 931 932 933 934 935 | }else{ rc = 999; } return rc; } #endif /* SQLITE_OMIT_AUTHORIZATION */ | < < < < < < < < < < < < < < < < < < < < | 924 925 926 927 928 929 930 931 932 933 934 935 936 937 | }else{ rc = 999; } return rc; } #endif /* SQLITE_OMIT_AUTHORIZATION */ /* ** This routine reads a line of text from FILE in, stores ** the text in memory obtained from malloc() and returns a pointer ** to the text. NULL is returned at end of file, or if malloc() ** fails. ** ** The interface is like "readline" but no command-line editing |
︙ | ︙ | |||
1136 1137 1138 1139 1140 1141 1142 | /* If no prepared statement was found. Compile the SQL text. Also allocate ** a new SqlPreparedStmt structure. */ if( pPreStmt==0 ){ int nByte; if( SQLITE_OK!=dbPrepare(pDb, zSql, &pStmt, pzOut) ){ | | | | 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 | /* If no prepared statement was found. Compile the SQL text. Also allocate ** a new SqlPreparedStmt structure. */ if( pPreStmt==0 ){ int nByte; if( SQLITE_OK!=dbPrepare(pDb, zSql, &pStmt, pzOut) ){ Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3_errmsg(pDb->db), -1)); return TCL_ERROR; } if( pStmt==0 ){ if( SQLITE_OK!=sqlite3_errcode(pDb->db) ){ /* A compile-time error in the statement. */ Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3_errmsg(pDb->db), -1)); return TCL_ERROR; }else{ /* The statement was a no-op. Continue to the next statement ** in the SQL string. */ return TCL_OK; } |
︙ | ︙ | |||
1361 1362 1363 1364 1365 1366 1367 | int nCol; /* Number of columns returned by pStmt */ Tcl_Obj **apColName = 0; /* Array of column names */ p->nCol = nCol = sqlite3_column_count(pStmt); if( nCol>0 && (papColName || p->pArray) ){ apColName = (Tcl_Obj**)Tcl_Alloc( sizeof(Tcl_Obj*)*nCol ); for(i=0; i<nCol; i++){ | | | 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 | int nCol; /* Number of columns returned by pStmt */ Tcl_Obj **apColName = 0; /* Array of column names */ p->nCol = nCol = sqlite3_column_count(pStmt); if( nCol>0 && (papColName || p->pArray) ){ apColName = (Tcl_Obj**)Tcl_Alloc( sizeof(Tcl_Obj*)*nCol ); for(i=0; i<nCol; i++){ apColName[i] = Tcl_NewStringObj(sqlite3_column_name(pStmt,i), -1); Tcl_IncrRefCount(apColName[i]); } p->apColName = apColName; } /* If results are being stored in an array variable, then create ** the array(*) entry for that array |
︙ | ︙ | |||
1448 1449 1450 1451 1452 1453 1454 | ** interface, retry prepare()/step() on the same SQL statement. ** This only happens once. If there is a second SQLITE_SCHEMA ** error, the error will be returned to the caller. */ p->zSql = zPrevSql; continue; } #endif | | > | 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 | ** interface, retry prepare()/step() on the same SQL statement. ** This only happens once. If there is a second SQLITE_SCHEMA ** error, the error will be returned to the caller. */ p->zSql = zPrevSql; continue; } #endif Tcl_SetObjResult(pDb->interp, Tcl_NewStringObj(sqlite3_errmsg(pDb->db), -1)); return TCL_ERROR; }else{ dbReleaseStmt(pDb, pPreStmt, 0); } } } |
︙ | ︙ | |||
1505 1506 1507 1508 1509 1510 1511 | return Tcl_NewWideIntObj(v); } } case SQLITE_FLOAT: { return Tcl_NewDoubleObj(sqlite3_column_double(pStmt, iCol)); } case SQLITE_NULL: { | | | | 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 | return Tcl_NewWideIntObj(v); } } case SQLITE_FLOAT: { return Tcl_NewDoubleObj(sqlite3_column_double(pStmt, iCol)); } case SQLITE_NULL: { return Tcl_NewStringObj(p->pDb->zNull, -1); } } return Tcl_NewStringObj(sqlite3_column_text(pStmt, iCol), -1); } /* ** If using Tcl version 8.6 or greater, use the NR functions to avoid ** recursive evalution of scripts by the [db eval] and [db trans] ** commands. Even if the headers used while compiling the extension ** are 8.6 or newer, the code still tests the Tcl version at runtime. |
︙ | ︙ | |||
2429 2430 2431 2432 2433 2434 2435 | pDb->zNull = Tcl_Alloc( len + 1 ); memcpy(pDb->zNull, zNull, len); pDb->zNull[len] = '\0'; }else{ pDb->zNull = 0; } } | | | 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 | pDb->zNull = Tcl_Alloc( len + 1 ); memcpy(pDb->zNull, zNull, len); pDb->zNull[len] = '\0'; }else{ pDb->zNull = 0; } } Tcl_SetObjResult(interp, Tcl_NewStringObj(pDb->zNull, -1)); break; } /* ** $db last_insert_rowid ** ** Return an integer which is the ROWID for the most recent insert. |
︙ | ︙ |
Changes to test/tclsqlite.test.
︙ | ︙ | |||
315 316 317 318 319 320 321 322 323 324 325 | # modify and reset the NULL representation # do_test tcl-8.1 { db nullvalue NaN execsql {INSERT INTO t1 VALUES(30,NULL)} db eval {SELECT * FROM t1 WHERE b IS NULL} } {30 NaN} do_test tcl-8.2 { db nullvalue NULL db nullvalue } {NULL} | > > > > > | > > > > | 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 | # modify and reset the NULL representation # do_test tcl-8.1 { db nullvalue NaN execsql {INSERT INTO t1 VALUES(30,NULL)} db eval {SELECT * FROM t1 WHERE b IS NULL} } {30 NaN} proc concatFunc args {return [join $args {}]} do_test tcl-8.2 { db function concat concatFunc db eval {SELECT concat('a', b, 'z') FROM t1 WHERE b is NULL} } {aNaNz} do_test tcl-8.3 { db nullvalue NULL db nullvalue } {NULL} do_test tcl-8.4 { db nullvalue {} db eval {SELECT * FROM t1 WHERE b IS NULL} } {30 {}} do_test tcl-8.5 { db function concat concatFunc db eval {SELECT concat('a', b, 'z') FROM t1 WHERE b is NULL} } {az} # Test the return type of user-defined functions # do_test tcl-9.1 { db function ret_str {return "hi"} execsql {SELECT typeof(ret_str())} } {text} |
︙ | ︙ |