Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Change tclsqlite.c to use the Tcl_NRxxx() APIs in Tcl versions 8.6 and later. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
e9f72f1de459a9e8380609f6bd7d4b76 |
User & Date: | dan 2009-10-06 14:59:03.000 |
Context
2009-10-07
| ||
10:43 | Update header file comments describing the sqlite3_changes and sqlite3_total_changes functions to describe how changes made by foreign key actions are handled. (check-in: 4c342c026a user: dan tags: trunk) | |
2009-10-06
| ||
14:59 | Change tclsqlite.c to use the Tcl_NRxxx() APIs in Tcl versions 8.6 and later. (check-in: e9f72f1de4 user: dan tags: trunk) | |
2009-10-05
| ||
05:40 | Fix some errors in the guttman versions (disabled by default) of the algorithms in rtree.c. (check-in: 64bad00b4f user: dan tags: trunk) | |
Changes
Changes to src/tclsqlite.c.
︙ | ︙ | |||
80 81 82 83 84 85 86 87 88 89 90 91 92 93 | typedef struct SqlPreparedStmt SqlPreparedStmt; struct SqlPreparedStmt { SqlPreparedStmt *pNext; /* Next in linked list */ SqlPreparedStmt *pPrev; /* Previous on the list */ sqlite3_stmt *pStmt; /* The prepared statement */ int nSql; /* chars in zSql[] */ const char *zSql; /* Text of the SQL statement */ }; typedef struct IncrblobChannel IncrblobChannel; /* ** There is one instance of this structure for each SQLite database ** that has been opened by the SQLite TCL interface. | > > | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | typedef struct SqlPreparedStmt SqlPreparedStmt; struct SqlPreparedStmt { SqlPreparedStmt *pNext; /* Next in linked list */ SqlPreparedStmt *pPrev; /* Previous on the list */ sqlite3_stmt *pStmt; /* The prepared statement */ int nSql; /* chars in zSql[] */ const char *zSql; /* Text of the SQL statement */ int nParm; /* Size of apParm array */ Tcl_Obj **apParm; /* Array of referenced object pointers */ }; typedef struct IncrblobChannel IncrblobChannel; /* ** There is one instance of this structure for each SQLite database ** that has been opened by the SQLite TCL interface. |
︙ | ︙ | |||
751 752 753 754 755 756 757 | if( rc && rc!=TCL_RETURN ){ sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1); }else{ Tcl_Obj *pVar = Tcl_GetObjResult(p->interp); int n; u8 *data; | | | 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 | if( rc && rc!=TCL_RETURN ){ sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1); }else{ Tcl_Obj *pVar = Tcl_GetObjResult(p->interp); int n; u8 *data; const char *zType = (pVar->typePtr ? pVar->typePtr->name : ""); char c = zType[0]; if( c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0 ){ /* Only return a BLOB type if the Tcl variable is a bytearray and ** has no string representation. */ data = Tcl_GetByteArrayFromObj(pVar, &n); sqlite3_result_blob(context, data, n, SQLITE_TRANSIENT); }else if( c=='b' && strcmp(zType,"boolean")==0 ){ |
︙ | ︙ | |||
928 929 930 931 932 933 934 | } zLine = realloc( zLine, n+1 ); return zLine; } /* | > > > > > > > > > > > > > > > > > > > > > | > > | > > > > > > > | > > > > > > > > > > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | | < | | | | > > > > | > > > | > > > > > > > > > > > > > > > > > | | < | | | | > > | > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < | | 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 | } zLine = realloc( zLine, n+1 ); return zLine; } /* ** This function is part of the implementation of the command: ** ** $db transaction [-deferred|-immediate|-exclusive] SCRIPT ** ** It is invoked after evaluating the script SCRIPT to commit or rollback ** the transaction or savepoint opened by the [transaction] command. */ static int DbTransPostCmd( ClientData data[], /* data[0] is the Sqlite3Db* for $db */ Tcl_Interp *interp, /* Tcl interpreter */ int result /* Result of evaluating SCRIPT */ ){ static const char *azEnd[] = { "RELEASE _tcl_transaction", /* rc==TCL_ERROR, nTransaction!=0 */ "COMMIT", /* rc!=TCL_ERROR, nTransaction==0 */ "ROLLBACK TO _tcl_transaction ; RELEASE _tcl_transaction", "ROLLBACK" /* rc==TCL_ERROR, nTransaction==0 */ }; SqliteDb *pDb = (SqliteDb*)data[0]; int rc = result; const char *zEnd; pDb->nTransaction--; zEnd = azEnd[(rc==TCL_ERROR)*2 + (pDb->nTransaction==0)]; pDb->disableAuth++; if( sqlite3_exec(pDb->db, zEnd, 0, 0, 0) ){ /* This is a tricky scenario to handle. The most likely cause of an ** error is that the exec() above was an attempt to commit the ** top-level transaction that returned SQLITE_BUSY. Or, less likely, ** that an IO-error has occured. In either case, throw a Tcl exception ** and try to rollback the transaction. ** ** But it could also be that the user executed one or more BEGIN, ** COMMIT, SAVEPOINT, RELEASE or ROLLBACK commands that are confusing ** this method's logic. Not clear how this would be best handled. */ if( rc!=TCL_ERROR ){ Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), 0); rc = TCL_ERROR; } sqlite3_exec(pDb->db, "ROLLBACK", 0, 0, 0); } pDb->disableAuth--; return rc; } /* ** Search the cache for a prepared-statement object that implements the ** first SQL statement in the buffer pointed to by parameter zIn. If ** no such prepared-statement can be found, allocate and prepare a new ** one. In either case, bind the current values of the relevant Tcl ** variables to any $var, :var or @var variables in the statement. Before ** returning, set *ppPreStmt to point to the prepared-statement object. ** ** Output parameter *pzOut is set to point to the next SQL statement in ** buffer zIn, or to the '\0' byte at the end of zIn if there is no ** next statement. ** ** If successful, TCL_OK is returned. Otherwise, TCL_ERROR is returned ** and an error message loaded into interpreter pDb->interp. */ static int dbPrepareAndBind( SqliteDb *pDb, /* Database object */ char const *zIn, /* SQL to compile */ char const **pzOut, /* OUT: Pointer to next SQL statement */ SqlPreparedStmt **ppPreStmt /* OUT: Object used to cache statement */ ){ const char *zSql = zIn; /* Pointer to first SQL statement in zIn */ sqlite3_stmt *pStmt; /* Prepared statement object */ SqlPreparedStmt *pPreStmt; /* Pointer to cached statement */ int nSql; /* Length of zSql in bytes */ int nVar; /* Number of variables in statement */ int iParm = 0; /* Next free entry in apParm */ int i; Tcl_Interp *interp = pDb->interp; *ppPreStmt = 0; /* Trim spaces from the start of zSql and calculate the remaining length. */ while( isspace(zSql[0]) ){ zSql++; } nSql = strlen30(zSql); for(pPreStmt = pDb->stmtList; pPreStmt; pPreStmt=pPreStmt->pNext){ int n = pPreStmt->nSql; if( nSql>=n && memcmp(pPreStmt->zSql, zSql, n)==0 && (zSql[n]==0 || zSql[n-1]==';') ){ pStmt = pPreStmt->pStmt; *pzOut = &zSql[pPreStmt->nSql]; /* When a prepared statement is found, unlink it from the ** cache list. It will later be added back to the beginning ** of the cache list in order to implement LRU replacement. */ if( pPreStmt->pPrev ){ pPreStmt->pPrev->pNext = pPreStmt->pNext; }else{ pDb->stmtList = pPreStmt->pNext; } if( pPreStmt->pNext ){ pPreStmt->pNext->pPrev = pPreStmt->pPrev; }else{ pDb->stmtLast = pPreStmt->pPrev; } pDb->nStmt--; nVar = sqlite3_bind_parameter_count(pStmt); break; } } /* If no prepared statement was found. Compile the SQL text. Also allocate ** a new SqlPreparedStmt structure. */ if( pPreStmt==0 ){ int nByte; if( SQLITE_OK!=sqlite3_prepare_v2(pDb->db, zSql, -1, &pStmt, pzOut) ){ Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db))); return TCL_ERROR; } if( pStmt==0 ){ if( SQLITE_OK!=sqlite3_errcode(pDb->db) ){ /* A compile-time error in the statement. */ Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db))); return TCL_ERROR; }else{ /* The statement was a no-op. Continue to the next statement ** in the SQL string. */ return TCL_OK; } } assert( pPreStmt==0 ); nVar = sqlite3_bind_parameter_count(pStmt); nByte = sizeof(SqlPreparedStmt) + nVar*sizeof(Tcl_Obj *); pPreStmt = (SqlPreparedStmt*)Tcl_Alloc(nByte); memset(pPreStmt, 0, nByte); pPreStmt->pStmt = pStmt; pPreStmt->nSql = (*pzOut - zSql); pPreStmt->zSql = sqlite3_sql(pStmt); pPreStmt->apParm = (Tcl_Obj **)&pPreStmt[1]; } assert( pPreStmt ); assert( strlen30(pPreStmt->zSql)==pPreStmt->nSql ); assert( 0==memcmp(pPreStmt->zSql, zSql, pPreStmt->nSql) ); /* Bind values to parameters that begin with $ or : */ for(i=1; i<=nVar; i++){ const char *zVar = sqlite3_bind_parameter_name(pStmt, i); if( zVar!=0 && (zVar[0]=='$' || zVar[0]==':' || zVar[0]=='@') ){ Tcl_Obj *pVar = Tcl_GetVar2Ex(interp, &zVar[1], 0, 0); if( pVar ){ int n; u8 *data; const char *zType = (pVar->typePtr ? pVar->typePtr->name : ""); char c = zType[0]; if( zVar[0]=='@' || (c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0) ){ /* Load a BLOB type if the Tcl variable is a bytearray and ** it has no string representation or the host ** parameter name begins with "@". */ data = Tcl_GetByteArrayFromObj(pVar, &n); sqlite3_bind_blob(pStmt, i, data, n, SQLITE_STATIC); Tcl_IncrRefCount(pVar); pPreStmt->apParm[iParm++] = pVar; }else if( c=='b' && strcmp(zType,"boolean")==0 ){ Tcl_GetIntFromObj(interp, pVar, &n); sqlite3_bind_int(pStmt, i, n); }else if( c=='d' && strcmp(zType,"double")==0 ){ double r; Tcl_GetDoubleFromObj(interp, pVar, &r); sqlite3_bind_double(pStmt, i, r); }else if( (c=='w' && strcmp(zType,"wideInt")==0) || (c=='i' && strcmp(zType,"int")==0) ){ Tcl_WideInt v; Tcl_GetWideIntFromObj(interp, pVar, &v); sqlite3_bind_int64(pStmt, i, v); }else{ data = (unsigned char *)Tcl_GetStringFromObj(pVar, &n); sqlite3_bind_text(pStmt, i, (char *)data, n, SQLITE_STATIC); Tcl_IncrRefCount(pVar); pPreStmt->apParm[iParm++] = pVar; } }else{ sqlite3_bind_null(pStmt, i); } } } pPreStmt->nParm = iParm; *ppPreStmt = pPreStmt; return TCL_OK; } /* ** Release a statement reference obtained by calling dbPrepareAndBind(). ** There should be exactly one call to this function for each call to ** dbPrepareAndBind(). ** ** If the discard parameter is non-zero, then the statement is deleted ** immediately. Otherwise it is added to the LRU list and may be returned ** by a subsequent call to dbPrepareAndBind(). */ static void dbReleaseStmt( SqliteDb *pDb, /* Database handle */ SqlPreparedStmt *pPreStmt, /* Prepared statement handle to release */ int discard /* True to delete (not cache) the pPreStmt */ ){ int i; /* Free the bound string and blob parameters */ for(i=0; i<pPreStmt->nParm; i++){ Tcl_DecrRefCount(pPreStmt->apParm[i]); } pPreStmt->nParm = 0; if( pDb->maxStmt<=0 || discard ){ /* If the cache is turned off, deallocated the statement */ sqlite3_finalize(pPreStmt->pStmt); Tcl_Free((char *)pPreStmt); }else{ /* Add the prepared statement to the beginning of the cache list. */ pPreStmt->pNext = pDb->stmtList; pPreStmt->pPrev = 0; if( pDb->stmtList ){ pDb->stmtList->pPrev = pPreStmt; } pDb->stmtList = pPreStmt; if( pDb->stmtLast==0 ){ assert( pDb->nStmt==0 ); pDb->stmtLast = pPreStmt; }else{ assert( pDb->nStmt>0 ); } pDb->nStmt++; /* If we have too many statement in cache, remove the surplus from ** the end of the cache list. */ while( pDb->nStmt>pDb->maxStmt ){ sqlite3_finalize(pDb->stmtLast->pStmt); pDb->stmtLast = pDb->stmtLast->pPrev; Tcl_Free((char*)pDb->stmtLast->pNext); pDb->stmtLast->pNext = 0; pDb->nStmt--; } } } /* ** Structure used with dbEvalXXX() functions: ** ** dbEvalInit() ** dbEvalStep() ** dbEvalFinalize() ** dbEvalRowInfo() ** dbEvalColumnValue() */ typedef struct DbEvalContext DbEvalContext; struct DbEvalContext { SqliteDb *pDb; /* Database handle */ Tcl_Obj *pSql; /* Object holding string zSql */ const char *zSql; /* Remaining SQL to execute */ SqlPreparedStmt *pPreStmt; /* Current statement */ int nCol; /* Number of columns returned by pStmt */ Tcl_Obj *pArray; /* Name of array variable */ Tcl_Obj **apColName; /* Array of column names */ }; /* ** Release any cache of column names currently held as part of ** the DbEvalContext structure passed as the first argument. */ static void dbReleaseColumnNames(DbEvalContext *p){ if( p->apColName ){ int i; for(i=0; i<p->nCol; i++){ Tcl_DecrRefCount(p->apColName[i]); } Tcl_Free((char *)p->apColName); p->apColName = 0; } p->nCol = 0; } /* ** Initialize a DbEvalContext structure. ** ** If pArray is not NULL, then it contains the name of a Tcl array ** variable. The "*" member of this array is set to a list containing ** the names of the columns returned by the statement as part of each ** call to dbEvalStep(), in order from left to right. e.g. if the names ** of the returned columns are a, b and c, it does the equivalent of the ** tcl command: ** ** set ${pArray}(*) {a b c} */ static void dbEvalInit( DbEvalContext *p, /* Pointer to structure to initialize */ SqliteDb *pDb, /* Database handle */ Tcl_Obj *pSql, /* Object containing SQL script */ Tcl_Obj *pArray /* Name of Tcl array to set (*) element of */ ){ memset(p, 0, sizeof(DbEvalContext)); p->pDb = pDb; p->zSql = Tcl_GetString(pSql); p->pSql = pSql; Tcl_IncrRefCount(pSql); if( pArray ){ p->pArray = pArray; Tcl_IncrRefCount(pArray); } } /* ** Obtain information about the row that the DbEvalContext passed as the ** first argument currently points to. */ static void dbEvalRowInfo( DbEvalContext *p, /* Evaluation context */ int *pnCol, /* OUT: Number of column names */ Tcl_Obj ***papColName /* OUT: Array of column names */ ){ /* Compute column names */ if( 0==p->apColName ){ sqlite3_stmt *pStmt = p->pPreStmt->pStmt; int i; /* Iterator variable */ 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] = dbTextToObj(sqlite3_column_name(pStmt,i)); Tcl_IncrRefCount(apColName[i]); } p->apColName = apColName; } /* If results are being stored in an array variable, then create ** the array(*) entry for that array */ if( p->pArray ){ Tcl_Interp *interp = p->pDb->interp; Tcl_Obj *pColList = Tcl_NewObj(); Tcl_Obj *pStar = Tcl_NewStringObj("*", -1); for(i=0; i<nCol; i++){ Tcl_ListObjAppendElement(interp, pColList, apColName[i]); } Tcl_IncrRefCount(pStar); Tcl_ObjSetVar2(interp, p->pArray, pStar, pColList, 0); Tcl_DecrRefCount(pStar); } } if( papColName ){ *papColName = p->apColName; } if( pnCol ){ *pnCol = p->nCol; } } /* ** Return one of TCL_OK, TCL_BREAK or TCL_ERROR. If TCL_ERROR is ** returned, then an error message is stored in the interpreter before ** returning. ** ** A return value of TCL_OK means there is a row of data available. The ** data may be accessed using dbEvalRowInfo() and dbEvalColumnValue(). This ** is analogous to a return of SQLITE_ROW from sqlite3_step(). If TCL_BREAK ** is returned, then the SQL script has finished executing and there are ** no further rows available. This is similar to SQLITE_DONE. */ static int dbEvalStep(DbEvalContext *p){ while( p->zSql[0] || p->pPreStmt ){ int rc; if( p->pPreStmt==0 ){ rc = dbPrepareAndBind(p->pDb, p->zSql, &p->zSql, &p->pPreStmt); if( rc!=TCL_OK ) return rc; }else{ int rcs; SqliteDb *pDb = p->pDb; SqlPreparedStmt *pPreStmt = p->pPreStmt; sqlite3_stmt *pStmt = pPreStmt->pStmt; rcs = sqlite3_step(pStmt); if( rcs==SQLITE_ROW ){ return TCL_OK; } if( p->pArray ){ dbEvalRowInfo(p, 0, 0); } rcs = sqlite3_reset(pStmt); pDb->nStep = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_FULLSCAN_STEP,1); pDb->nSort = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_SORT,1); dbReleaseColumnNames(p); p->pPreStmt = 0; if( rcs!=SQLITE_OK ){ /* If a run-time error occurs, report the error and stop reading ** the SQL. */ Tcl_SetObjResult(pDb->interp, dbTextToObj(sqlite3_errmsg(pDb->db))); dbReleaseStmt(pDb, pPreStmt, 1); return TCL_ERROR; }else{ dbReleaseStmt(pDb, pPreStmt, 0); } } } /* Finished */ return TCL_BREAK; } /* ** Free all resources currently held by the DbEvalContext structure passed ** as the first argument. There should be exactly one call to this function ** for each call to dbEvalInit(). */ static void dbEvalFinalize(DbEvalContext *p){ if( p->pPreStmt ){ sqlite3_reset(p->pPreStmt->pStmt); dbReleaseStmt(p->pDb, p->pPreStmt, 0); p->pPreStmt = 0; } if( p->pArray ){ Tcl_DecrRefCount(p->pArray); p->pArray = 0; } Tcl_DecrRefCount(p->pSql); dbReleaseColumnNames(p); } /* ** Return a pointer to a Tcl_Obj structure with ref-count 0 that contains ** the value for the iCol'th column of the row currently pointed to by ** the DbEvalContext structure passed as the first argument. */ static Tcl_Obj *dbEvalColumnValue(DbEvalContext *p, int iCol){ sqlite3_stmt *pStmt = p->pPreStmt->pStmt; switch( sqlite3_column_type(pStmt, iCol) ){ case SQLITE_BLOB: { int bytes = sqlite3_column_bytes(pStmt, iCol); const char *zBlob = sqlite3_column_blob(pStmt, iCol); if( !zBlob ) bytes = 0; return Tcl_NewByteArrayObj((u8*)zBlob, bytes); } case SQLITE_INTEGER: { sqlite_int64 v = sqlite3_column_int64(pStmt, iCol); if( v>=-2147483647 && v<=2147483647 ){ return Tcl_NewIntObj(v); }else{ return Tcl_NewWideIntObj(v); } } case SQLITE_FLOAT: { return Tcl_NewDoubleObj(sqlite3_column_double(pStmt, iCol)); } case SQLITE_NULL: { return dbTextToObj(p->pDb->zNull); } } return dbTextToObj((char *)sqlite3_column_text(pStmt, iCol)); } /* ** 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. ** This allows stubs-enabled builds to be used with older Tcl libraries. */ #if TCL_MAJOR_VERSION>8 || (TCL_MAJOR_VERSION==8 && TCL_MINOR_VERSION>=6) static int DbUseNre(void){ int major, minor; Tcl_GetVersion(&major, &minor, 0, 0); return( (major==8 && minor>=6) || major>8 ); } #else /* ** Compiling using headers earlier than 8.6. In this case NR cannot be ** used, so DbUseNre() to always return zero. Add #defines for the other ** Tcl_NRxxx() functions to prevent them from causing compilation errors, ** even though the only invocations of them are within conditional blocks ** of the form: ** ** if( DbUseNre() ) { ... } */ # define DbUseNre() 0 # define Tcl_NRAddCallback(a,b,c,d,e,f) 0 # define Tcl_NREvalObj(a,b,c) 0 # define Tcl_NRCreateCommand(a,b,c,d,e,f) 0 #endif /* ** This function is part of the implementation of the command: ** ** $db eval SQL ?ARRAYNAME? SCRIPT */ static int DbEvalNextCmd( ClientData data[], /* data[0] is the (DbEvalContext*) */ Tcl_Interp *interp, /* Tcl interpreter */ int result /* Result so far */ ){ int rc = result; /* Return code */ /* The first element of the data[] array is a pointer to a DbEvalContext ** structure allocated using Tcl_Alloc(). The second element of data[] ** is a pointer to a Tcl_Obj containing the script to run for each row ** returned by the queries encapsulated in data[0]. */ DbEvalContext *p = (DbEvalContext *)data[0]; Tcl_Obj *pScript = (Tcl_Obj *)data[1]; Tcl_Obj *pArray = p->pArray; while( (rc==TCL_OK || rc==TCL_CONTINUE) && TCL_OK==(rc = dbEvalStep(p)) ){ int i; int nCol; Tcl_Obj **apColName; dbEvalRowInfo(p, &nCol, &apColName); for(i=0; i<nCol; i++){ Tcl_Obj *pVal = dbEvalColumnValue(p, i); if( pArray==0 ){ Tcl_ObjSetVar2(interp, apColName[i], 0, pVal, 0); }else{ Tcl_ObjSetVar2(interp, pArray, apColName[i], pVal, 0); } } /* The required interpreter variables are now populated with the data ** from the current row. If using NRE, schedule callbacks to evaluate ** script pScript, then to invoke this function again to fetch the next ** row (or clean up if there is no next row or the script throws an ** exception). After scheduling the callbacks, return control to the ** caller. ** ** If not using NRE, evaluate pScript directly and continue with the ** next iteration of this while(...) loop. */ if( DbUseNre() ){ Tcl_NRAddCallback(interp, DbEvalNextCmd, (void*)p, (void*)pScript, 0, 0); return Tcl_NREvalObj(interp, pScript, 0); }else{ rc = Tcl_EvalObjEx(interp, pScript, 0); } } Tcl_DecrRefCount(pScript); dbEvalFinalize(p); Tcl_Free((char *)p); if( rc==TCL_OK || rc==TCL_BREAK ){ Tcl_ResetResult(interp); rc = TCL_OK; } return rc; } /* ** The "sqlite" command below creates a new Tcl command for each ** connection it opens to an SQLite database. This routine is invoked ** whenever one of those connection-specific commands is executed ** in Tcl. For example, if you run Tcl code like this: |
︙ | ︙ | |||
1596 1597 1598 1599 1600 1601 1602 | ** Return the numeric error code that was returned by the most recent ** call to sqlite3_exec(). */ case DB_ERRORCODE: { Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite3_errcode(pDb->db))); break; } | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < | < < < < < < < < < < < < < < | | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | < < < | < | < < < > > | < < < < < < | > > | < < < < < < | < < | < < | < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < | | < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < | < < | | > < < < | | < < < | < < | | < < < < < > | < | < < < | < < < < < | < | < < < < | < < | < < < < | 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 | ** Return the numeric error code that was returned by the most recent ** call to sqlite3_exec(). */ case DB_ERRORCODE: { Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite3_errcode(pDb->db))); break; } /* ** $db exists $sql ** $db onecolumn $sql ** ** The onecolumn method is the equivalent of: ** lindex [$db eval $sql] 0 */ case DB_EXISTS: case DB_ONECOLUMN: { DbEvalContext sEval; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "SQL"); return TCL_ERROR; } dbEvalInit(&sEval, pDb, objv[2], 0); rc = dbEvalStep(&sEval); if( choice==DB_ONECOLUMN ){ if( rc==TCL_OK ){ Tcl_SetObjResult(interp, dbEvalColumnValue(&sEval, 0)); } }else if( rc==TCL_BREAK || rc==TCL_OK ){ Tcl_SetObjResult(interp, Tcl_NewBooleanObj(rc==TCL_OK)); } dbEvalFinalize(&sEval); if( rc==TCL_BREAK ){ rc = TCL_OK; } break; } /* ** $db eval $sql ?array? ?{ ...code... }? ** ** The SQL statement in $sql is evaluated. For each row, the values are ** placed in elements of the array named "array" and ...code... is executed. ** If "array" and "code" are omitted, then no callback is every invoked. ** If "array" is an empty string, then the values are placed in variables ** that have the same name as the fields extracted by the query. */ case DB_EVAL: { if( objc<3 || objc>5 ){ Tcl_WrongNumArgs(interp, 2, objv, "SQL ?ARRAY-NAME? ?SCRIPT?"); return TCL_ERROR; } if( objc==3 ){ DbEvalContext sEval; Tcl_Obj *pRet = Tcl_NewObj(); Tcl_IncrRefCount(pRet); dbEvalInit(&sEval, pDb, objv[2], 0); while( TCL_OK==(rc = dbEvalStep(&sEval)) ){ int i; int nCol; dbEvalRowInfo(&sEval, &nCol, 0); for(i=0; i<nCol; i++){ Tcl_ListObjAppendElement(interp, pRet, dbEvalColumnValue(&sEval, i)); } } dbEvalFinalize(&sEval); if( rc==TCL_BREAK ){ Tcl_SetObjResult(interp, pRet); rc = TCL_OK; } Tcl_DecrRefCount(pRet); }else{ ClientData cd[2]; DbEvalContext *p; Tcl_Obj *pArray = 0; Tcl_Obj *pScript; if( objc==5 && *(char *)Tcl_GetString(objv[3]) ){ pArray = objv[3]; } pScript = objv[objc-1]; Tcl_IncrRefCount(pScript); p = (DbEvalContext *)Tcl_Alloc(sizeof(DbEvalContext)); dbEvalInit(p, pDb, objv[2], pArray); cd[0] = (void *)p; cd[1] = (void *)pScript; rc = DbEvalNextCmd(cd, interp, TCL_OK); } break; } /* ** $db function NAME [-argcount N] SCRIPT ** |
︙ | ︙ | |||
2118 2119 2120 2121 2122 2123 2124 | rowid = sqlite3_last_insert_rowid(pDb->db); pResult = Tcl_GetObjResult(interp); Tcl_SetWideIntObj(pResult, rowid); break; } /* | | | 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 | rowid = sqlite3_last_insert_rowid(pDb->db); pResult = Tcl_GetObjResult(interp); Tcl_SetWideIntObj(pResult, rowid); break; } /* ** The DB_ONECOLUMN method is implemented together with DB_EXISTS. */ /* $db progress ?N CALLBACK? ** ** Invoke the given callback every N virtual machine opcodes while executing ** queries. */ |
︙ | ︙ | |||
2397 2398 2399 2400 2401 2402 2403 | ** ** This command was inspired by Dave Thomas's talk on Ruby at the ** 2005 O'Reilly Open Source Convention (OSCON). */ case DB_TRANSACTION: { Tcl_Obj *pScript; const char *zBegin = "SAVEPOINT _tcl_transaction"; | < | < < > < < < < > | > | > | > | < < < < < < < | < < < < < < < < < < < < < < < | < < < < | 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 | ** ** This command was inspired by Dave Thomas's talk on Ruby at the ** 2005 O'Reilly Open Source Convention (OSCON). */ case DB_TRANSACTION: { Tcl_Obj *pScript; const char *zBegin = "SAVEPOINT _tcl_transaction"; if( objc!=3 && objc!=4 ){ Tcl_WrongNumArgs(interp, 2, objv, "[TYPE] SCRIPT"); return TCL_ERROR; } if( pDb->nTransaction==0 && objc==4 ){ static const char *TTYPE_strs[] = { "deferred", "exclusive", "immediate", 0 }; enum TTYPE_enum { TTYPE_DEFERRED, TTYPE_EXCLUSIVE, TTYPE_IMMEDIATE }; int ttype; if( Tcl_GetIndexFromObj(interp, objv[2], TTYPE_strs, "transaction type", 0, &ttype) ){ return TCL_ERROR; } switch( (enum TTYPE_enum)ttype ){ case TTYPE_DEFERRED: /* no-op */; break; case TTYPE_EXCLUSIVE: zBegin = "BEGIN EXCLUSIVE"; break; case TTYPE_IMMEDIATE: zBegin = "BEGIN IMMEDIATE"; break; } } pScript = objv[objc-1]; /* Run the SQLite BEGIN command to open a transaction or savepoint. */ pDb->disableAuth++; rc = sqlite3_exec(pDb->db, zBegin, 0, 0, 0); pDb->disableAuth--; if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), 0); return TCL_ERROR; } pDb->nTransaction++; /* If using NRE, schedule a callback to invoke the script pScript, then ** a second callback to commit (or rollback) the transaction or savepoint ** opened above. If not using NRE, evaluate the script directly, then ** call function DbTransPostCmd() to commit (or rollback) the transaction ** or savepoint. */ if( DbUseNre() ){ Tcl_NRAddCallback(interp, DbTransPostCmd, cd, 0, 0, 0); Tcl_NREvalObj(interp, pScript, 0); }else{ rc = DbTransPostCmd(&cd, interp, Tcl_EvalObjEx(interp, pScript, 0)); } break; } /* ** $db unlock_notify ?script? */ case DB_UNLOCK_NOTIFY: { |
︙ | ︙ | |||
2708 2709 2710 2711 2712 2713 2714 | Tcl_Free((char*)p); sqlite3_free(zErrMsg); return TCL_ERROR; } p->maxStmt = NUM_PREPARED_STMTS; p->interp = interp; zArg = Tcl_GetStringFromObj(objv[1], 0); | > > > | > | 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 | Tcl_Free((char*)p); sqlite3_free(zErrMsg); return TCL_ERROR; } p->maxStmt = NUM_PREPARED_STMTS; p->interp = interp; zArg = Tcl_GetStringFromObj(objv[1], 0); if( DbUseNre() ){ Tcl_NRCreateCommand(interp, zArg, 0, DbObjCmd, (char*)p, DbDeleteCmd); }else{ Tcl_CreateObjCommand(interp, zArg, DbObjCmd, (char*)p, DbDeleteCmd); } return TCL_OK; } /* ** Provide a dummy Tcl_InitStubs if we are using this as a static ** library. */ |
︙ | ︙ |