sqlite3_stmt *pTblIter; /* Iterate through tables */
sqlite3_stmt *pIdxIter; /* Index iterator */
int nTblCol; /* Size of azTblCol[] array */
- char **azTblCol; /* Array of quoted column names */
+ char **azTblCol; /* Array of unquoted column names */
char **azTblType; /* Array of column types */
+ int *aiTblOrder; /* Order of columns in target table */
unsigned char *abTblPk; /* Array of flags - true for PK columns */
int eType;
/*
** Values for OtaObjIter.eType
+**
+** 1: Table has an implicit rowid.
+** 2: Table has an explicit IPK column.
+** 3: Table has an external PK index.
+** 4: Table is WITHOUT ROWID.
+** 5: Table is a virtual table.
*/
-#define OTA_PK_REAL 1 /* Table has a real primary key */
-#define OTA_PK_EXTERNAL 2 /* Table has an external primary key index */
-#define OTA_PK_NONE 3 /* Table has no PK (use rowid) */
-#define OTA_PK_VTAB 4 /* Table is a virtual table (use rowid) */
+#define OTA_PK_NONE 1
+#define OTA_PK_IPK 2
+#define OTA_PK_EXTERNAL 3
+#define OTA_PK_WITHOUT_ROWID 4
+#define OTA_PK_VTAB 5
/*
** OTA handle.
/*
** Free the OtaObjIter.azTblCol[] and OtaObjIter.abTblPk[] arrays allocated
-** by an earlier call to otaObjIterGetCols().
+** by an earlier call to otaObjIterCacheTableInfo().
*/
static void otaObjIterFreeCols(OtaObjIter *pIter){
int i;
sqlite3_free(pIter->azTblCol);
pIter->azTblCol = 0;
pIter->azTblType = 0;
+ pIter->aiTblOrder = 0;
pIter->abTblPk = 0;
pIter->nTblCol = 0;
sqlite3_free(pIter->zMask);
/* Free any SQLite statements used while processing the previous object */
otaObjIterClearStatements(pIter);
+ if( pIter->zIdx==0 ){
+ rc = sqlite3_exec(p->db,
+ "DROP TRIGGER IF EXISTS temp.ota_insert_tr;"
+ "DROP TRIGGER IF EXISTS temp.ota_update1_tr;"
+ "DROP TRIGGER IF EXISTS temp.ota_update2_tr;"
+ "DROP TRIGGER IF EXISTS temp.ota_delete_tr;"
+ , 0, 0, &p->zErrmsg
+ );
+ }
- if( pIter->bCleanup ){
- otaObjIterFreeCols(pIter);
- pIter->bCleanup = 0;
- rc = sqlite3_step(pIter->pTblIter);
- if( rc!=SQLITE_ROW ){
- rc = sqlite3_reset(pIter->pTblIter);
- pIter->zTbl = 0;
- }else{
- pIter->zTbl = (const char*)sqlite3_column_text(pIter->pTblIter, 0);
- rc = SQLITE_OK;
- }
- }else{
- if( pIter->zIdx==0 ){
- sqlite3_bind_text(pIter->pIdxIter, 1, pIter->zTbl, -1, SQLITE_STATIC);
- }
- rc = sqlite3_step(pIter->pIdxIter);
- if( rc!=SQLITE_ROW ){
- rc = sqlite3_reset(pIter->pIdxIter);
- pIter->bCleanup = 1;
- pIter->zIdx = 0;
+ if( rc==SQLITE_OK ){
+ if( pIter->bCleanup ){
+ otaObjIterFreeCols(pIter);
+ pIter->bCleanup = 0;
+ rc = sqlite3_step(pIter->pTblIter);
+ if( rc!=SQLITE_ROW ){
+ rc = sqlite3_reset(pIter->pTblIter);
+ pIter->zTbl = 0;
+ }else{
+ pIter->zTbl = (const char*)sqlite3_column_text(pIter->pTblIter, 0);
+ pIter->tnum = sqlite3_column_int(pIter->pTblIter, 1);
+ rc = SQLITE_OK;
+ }
}else{
- pIter->zIdx = (const char*)sqlite3_column_text(pIter->pIdxIter, 0);
- pIter->tnum = sqlite3_column_int(pIter->pIdxIter, 1);
- pIter->bUnique = sqlite3_column_int(pIter->pIdxIter, 2);
- rc = SQLITE_OK;
+ if( pIter->zIdx==0 ){
+ sqlite3_bind_text(pIter->pIdxIter, 1, pIter->zTbl, -1, SQLITE_STATIC);
+ }
+ rc = sqlite3_step(pIter->pIdxIter);
+ if( rc!=SQLITE_ROW ){
+ rc = sqlite3_reset(pIter->pIdxIter);
+ pIter->bCleanup = 1;
+ pIter->zIdx = 0;
+ }else{
+ pIter->zIdx = (const char*)sqlite3_column_text(pIter->pIdxIter, 0);
+ pIter->tnum = sqlite3_column_int(pIter->pIdxIter, 1);
+ pIter->bUnique = sqlite3_column_int(pIter->pIdxIter, 2);
+ rc = SQLITE_OK;
+ }
}
}
}
memset(pIter, 0, sizeof(OtaObjIter));
rc = prepareAndCollectError(p->db, &pIter->pTblIter, &p->zErrmsg,
- "SELECT substr(name, 6) FROM ota.sqlite_master "
- "WHERE type='table' AND name LIKE 'data_%'"
+ "SELECT substr(a.name, 6), b.rootpage FROM ota.sqlite_master AS a "
+ "LEFT JOIN main.sqlite_master AS b ON "
+ "(substr(a.name, 6)==b.name) "
+ "WHERE a.type='table' AND a.name LIKE 'data_%'"
);
if( rc==SQLITE_OK ){
return otaObjIterNext(p, pIter);
}
-/*
-** Allocate a buffer and populate it with the double-quoted version of the
-** string in the argument buffer, suitable for use as an SQL identifier.
-** For example:
-**
-** [quick `brown` fox] -> [`quick ``brown`` fox`]
-**
-** Assuming the allocation is successful, a pointer to the new buffer is
-** returned. It is the responsibility of the caller to free it using
-** sqlite3_free() at some point in the future. Or, if the allocation fails,
-** a NULL pointer is returned.
-*/
-static char *otaQuoteName(const char *zName){
- int nName = strlen(zName);
- char *zRet = sqlite3_malloc(nName * 2 + 2 + 1);
- if( zRet ){
- int i;
- char *p = zRet;
- *p++ = '`';
- for(i=0; i<nName; i++){
- if( zName[i]=='`' ) *p++ = '`';
- *p++ = zName[i];
- }
- *p++ = '`';
- *p++ = '\0';
- }
- return zRet;
-}
-
-/*
-** Argument zName points to a column name. Argument zQuoted also points
-** to a column name, but one that has been quoted using otaQuoteName().
-** Return true if the column names are the same, or false otherwise.
-*/
-static int otaMatchName(const char *zName, const char *zQuoted){
- const char *p = zName;
- const char *q = &zQuoted[1];
- while( 1 ){
- if( *q=='`' ) q++;
- if( sqlite3_strnicmp(q, p, 1) ) return 0;
- if( !*q ) break;
- p++;
- q++;
- }
- return 1;
-}
-
/*
** Argument zFmt is a sqlite3_mprintf() style format string. The trailing
** arguments are the usual subsitution values. This function performs
** error code in the OTA handle passed as the first argument.
*/
static void otaAllocateIterArrays(sqlite3ota *p, OtaObjIter *pIter, int nCol){
- int nByte = sizeof(char*) * nCol * 2 + sizeof(unsigned char*) * nCol;
+ int nByte = (sizeof(char*) * 2 + sizeof(int) + sizeof(unsigned char)) * nCol;
char **azNew;
assert( p->rc==SQLITE_OK );
memset(azNew, 0, nByte);
pIter->azTblCol = azNew;
pIter->azTblType = &azNew[nCol];
- pIter->abTblPk = (unsigned char*)&pIter->azTblType[nCol];
+ pIter->aiTblOrder = (int*)&pIter->azTblType[nCol];
+ pIter->abTblPk = (unsigned char*)&pIter->aiTblOrder[nCol];
}else{
p->rc = SQLITE_NOMEM;
}
}
-/*
-** Return true if zTab is the name of a virtual table within the target
-** database.
-*/
-static int otaIsVtab(sqlite3ota *p, const char *zTab){
- int res = 0;
- sqlite3_stmt *pSelect = 0;
-
- if( p->rc==SQLITE_OK ){
- p->rc = prepareAndCollectError(p->db, &pSelect, &p->zErrmsg,
- "SELECT count(*) FROM sqlite_master WHERE name = ? AND type='table' "
- "AND sql LIKE 'CREATE VIRTUAL TABLE%'"
- );
- }
-
- if( p->rc==SQLITE_OK ){
- sqlite3_bind_text(pSelect, 1, zTab, -1, SQLITE_STATIC);
- if( sqlite3_step(pSelect)==SQLITE_ROW ){
- res = sqlite3_column_int(pSelect, 0);
- }
- p->rc = sqlite3_finalize(pSelect);
- }
-
- return res;
-}
-
/*
** If they are not already populated, populate the pIter->azTblCol[],
** pIter->abTblPk[], pIter->nTblCol and pIter->bRowid variables according to
-** the table that the iterator currently points to.
+** the table (not index) that the iterator currently points to.
**
** Return SQLITE_OK if successful, or an SQLite error code otherwise. If
** an error does occur, an error code and error message are also left in
** the OTA handle.
*/
-static int otaObjIterGetCols(sqlite3ota *p, OtaObjIter *pIter){
+static int otaObjIterCacheTableInfo(sqlite3ota *p, OtaObjIter *pIter){
if( pIter->azTblCol==0 ){
sqlite3_stmt *pStmt = 0;
int nCol = 0;
int i; /* for() loop iterator variable */
int rc2; /* sqlite3_finalize() return value */
int bOtaRowid = 0; /* If input table has column "ota_rowid" */
+ int iOrder = 0;
+ /* Figure out the type of table this step will deal with. */
assert( pIter->eType==0 );
+ sqlite3_test_control(
+ SQLITE_TESTCTRL_TBLTYPE, p->db, "main", pIter->zTbl, &pIter->eType
+ );
+ assert( pIter->eType==OTA_PK_NONE || pIter->eType==OTA_PK_IPK
+ || pIter->eType==OTA_PK_EXTERNAL || pIter->eType==OTA_PK_WITHOUT_ROWID
+ || pIter->eType==OTA_PK_VTAB
+ );
/* Populate the azTblCol[] and nTblCol variables based on the columns
** of the input table. Ignore any input table columns that begin with
for(i=0; p->rc==SQLITE_OK && i<nCol; i++){
const char *zName = (const char*)sqlite3_column_name(pStmt, i);
if( sqlite3_strnicmp("ota_", zName, 4) ){
- char *zCopy = otaQuoteName(zName);
+ char *zCopy = otaStrndup(zName, -1, &p->rc);
pIter->azTblCol[pIter->nTblCol++] = zCopy;
- if( zCopy==0 ) p->rc = SQLITE_NOMEM;
}
else if( 0==sqlite3_stricmp("ota_rowid", zName) ){
bOtaRowid = 1;
sqlite3_finalize(pStmt);
pStmt = 0;
+ if( p->rc==SQLITE_OK
+ && bOtaRowid!=(pIter->eType==OTA_PK_VTAB || pIter->eType==OTA_PK_NONE)
+ ){
+ p->rc = SQLITE_ERROR;
+ p->zErrmsg = sqlite3_mprintf(
+ "table data_%q %s ota_rowid column", pIter->zTbl,
+ (bOtaRowid ? "may not have" : "requires")
+ );
+ }
+
/* Check that all non-HIDDEN columns in the destination table are also
- ** present in the input table. Populate the abTblPk[] array at the
- ** same time. */
+ ** present in the input table. Populate the abTblPk[], azTblType[] and
+ ** aiTblOrder[] arrays at the same time. */
if( p->rc==SQLITE_OK ){
p->rc = prepareFreeAndCollectError(p->db, &pStmt, &p->zErrmsg,
sqlite3_mprintf("PRAGMA main.table_info(%Q)", pIter->zTbl)
while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
const char *zName = (const char*)sqlite3_column_text(pStmt, 1);
for(i=0; i<pIter->nTblCol; i++){
- if( otaMatchName(zName, pIter->azTblCol[i]) ) break;
+ if( 0==strcmp(zName, pIter->azTblCol[i]) ) break;
}
if( i==pIter->nTblCol ){
p->rc = SQLITE_ERROR;
}else{
int iPk = sqlite3_column_int(pStmt, 5);
const char *zType = (const char*)sqlite3_column_text(pStmt, 2);
+ pIter->aiTblOrder[i] = iOrder++;
pIter->azTblType[i] = otaStrndup(zType, -1, &p->rc);
pIter->abTblPk[i] = (iPk!=0);
- if( iPk ){
- pIter->eType = (iPk<0) ? OTA_PK_EXTERNAL : OTA_PK_REAL;
- }
}
}
- rc2 = sqlite3_finalize(pStmt);
- if( p->rc==SQLITE_OK ) p->rc = rc2;
-
- if( p->rc==SQLITE_OK ){
- if( pIter->eType==0 ){
- /* This must either be a virtual table, or a regular table with no
- ** PRIMARY KEY declaration whatsoever. */
- if( bOtaRowid==0 ){
- p->rc = SQLITE_ERROR;
- p->zErrmsg = sqlite3_mprintf(
- "table data_%q requires ota_rowid column", pIter->zTbl
- );
- }else if( otaIsVtab(p, pIter->zTbl) ){
- pIter->eType = OTA_PK_VTAB;
- }else{
- pIter->eType = OTA_PK_NONE;
+ while( iOrder<pIter->nTblCol ){
+ for(i=0; i<pIter->nTblCol; i++){
+ if( pIter->aiTblOrder[i]==0 ){
+ pIter->aiTblOrder[i] = iOrder++;
+ continue;
}
- }else if( bOtaRowid ){
- p->rc = SQLITE_ERROR;
- p->zErrmsg = sqlite3_mprintf(
- "table data_%q may not have ota_rowid column", pIter->zTbl
- );
}
}
+
+ /* Check that there were no extra columns in the data_xxx table that
+ ** are not present in the target table. If there are, an error. */
+#if 0
+ assert( iOrder<=pIter->nTblCol );
+ if( p->rc==SQLITE_OK && iOrder!=pIter->nTblCol ){
+ p->rc = SQLITE_ERROR;
+ p->zErrmsg = sqlite3_mprintf("data_%q has %d columns, expected %d",
+ pIter->zTbl, iOrder, pIter->nTblCol
+ );
+ }
+#endif
+
+ rc2 = sqlite3_finalize(pStmt);
+ if( p->rc==SQLITE_OK ) p->rc = rc2;
}
return p->rc;
** This function constructs and returns a pointer to a nul-terminated
** string containing some SQL clause or list based on one or more of the
** column names currently stored in the pIter->azTblCol[] array.
-**
-** If an OOM error is encountered, NULL is returned and an error code
-** left in the OTA handle passed as the first argument. Otherwise, a pointer
-** to the allocated string buffer is returned. It is the responsibility
-** of the caller to eventually free this buffer using sqlite3_free().
-**
-** The number of column names to include in the returned string is passed
-** as the third argument.
-**
-** If arguments aiCol and azCollate are both NULL, then the returned string
-** contains the first nCol column names as a comma-separated list. For
-** example:
-**
-** "a", "b", "c"
-**
-** If argument aiCol is not NULL, it must point to an array containing nCol
-** entries - the index of each column name to include in the comma-separated
-** list. For example, if aiCol[] contains {2, 0, 1), then the returned
-** string is changed to:
-**
-** "c", "a", "b"
-**
-** If azCollate is not NULL, it must also point to an array containing nCol
-** entries - collation sequence names to associated with each element of
-** the comma separated list. For example, ef azCollate[] contains
-** {"BINARY", "NOCASE", "REVERSE"}, then the retuned string is:
-**
-** "c" COLLATE "BINARY", "a" COLLATE "NOCASE", "b" COLLATE "REVERSE"
-**
*/
static char *otaObjIterGetCollist(
sqlite3ota *p, /* OTA object */
- OtaObjIter *pIter, /* Object iterator for column names */
- int nCol, /* Number of column names */
- int *aiCol, /* Array of nCol column indexes */
- const char **azCollate /* Array of nCol collation sequence names */
+ OtaObjIter *pIter /* Object iterator for column names */
){
char *zList = 0;
- if( p->rc==SQLITE_OK ){
- const char *zSep = "";
- int i;
- for(i=0; i<nCol; i++){
- int iCol = aiCol ? aiCol[i] : i;
- char *zCol = (iCol>=0 ? pIter->azTblCol[iCol] : "ota_rowid");
- zList = sqlite3_mprintf("%z%s%s", zList, zSep, zCol);
- if( zList && azCollate ){
- zList = sqlite3_mprintf("%z COLLATE %Q", zList, azCollate[i]);
- }
- zSep = ", ";
- if( zList==0 ){
- p->rc = SQLITE_NOMEM;
- break;
- }
- }
+ const char *zSep = "";
+ int i;
+ for(i=0; i<pIter->nTblCol; i++){
+ const char *z = pIter->azTblCol[i];
+ zList = otaMPrintfAndCollectError(p, "%z%s\"%w\"", zList, zSep, z);
+ zSep = ", ";
}
return zList;
}
char *zImpPK = 0; /* String to return via *pzImposterPK */
char *zWhere = 0; /* String to return via *pzWhere */
int nBind = 0; /* Value to return via *pnBind */
- const char *zComma = ""; /* Set to ", " later on */
+ const char *zCom = ""; /* Set to ", " later on */
const char *zAnd = ""; /* Set to " AND " later on */
sqlite3_stmt *pXInfo = 0; /* PRAGMA index_xinfo = ? */
if( iCid<0 ){
/* An integer primary key. If the table has an explicit IPK, use
** its name. Otherwise, use "ota_rowid". */
- if( pIter->eType==OTA_PK_REAL ){
+ if( pIter->eType==OTA_PK_IPK ){
int i;
for(i=0; i<pIter->nTblCol && pIter->abTblPk[i]==0; i++);
assert( i<pIter->nTblCol );
zType = pIter->azTblType[iCid];
}
- zRet = sqlite3_mprintf("%z%s%s COLLATE %Q", zRet, zComma, zCol, zCollate);
+ zRet = sqlite3_mprintf("%z%s\"%w\" COLLATE %Q", zRet, zCom, zCol, zCollate);
if( pIter->bUnique==0 || sqlite3_column_int(pXInfo, 5) ){
- zImpPK = sqlite3_mprintf("%z%sc%d", zImpPK, zComma, nBind);
+ zImpPK = sqlite3_mprintf("%z%sc%d", zImpPK, zCom, nBind);
}
zImpCols = sqlite3_mprintf(
- "%z%sc%d %s COLLATE %Q", zImpCols, zComma, nBind, zType, zCollate
+ "%z%sc%d %s COLLATE %Q", zImpCols, zCom, nBind, zType, zCollate
);
zWhere = sqlite3_mprintf("%z%sc%d IS ?", zWhere, zAnd, nBind);
if( zRet==0 || zImpPK==0 || zImpCols==0 || zWhere==0 ) rc = SQLITE_NOMEM;
- zComma = ", ";
+ zCom = ", ";
zAnd = " AND ";
nBind++;
}
const char *zS = "";
int i;
for(i=0; i<pIter->nTblCol; i++){
- zList = sqlite3_mprintf("%z%s%s.%s", zList, zS, zObj, pIter->azTblCol[i]);
+ const char *zCol = pIter->azTblCol[i];
+ zList = sqlite3_mprintf("%z%s%s.\"%w\"", zList, zS, zObj, zCol);
zS = ", ";
if( zList==0 ){
p->rc = SQLITE_NOMEM;
OtaObjIter *pIter
){
char *zList = 0;
- if( p->rc==SQLITE_OK ){
- if( pIter->eType==OTA_PK_VTAB || pIter->eType==OTA_PK_NONE ){
- zList = otaMPrintfAndCollectError(p, "_rowid_ = ?%d", pIter->nTblCol+1);
- }else{
- const char *zSep = "";
- int i;
- for(i=0; i<pIter->nTblCol; i++){
- if( pIter->abTblPk[i] ){
- const char *zCol = pIter->azTblCol[i];
- zList = otaMPrintfAndCollectError(
- p, "%z%s%s=?%d", zList, zSep, zCol, i+1
- );
- zSep = " AND ";
- }
+ if( pIter->eType==OTA_PK_VTAB || pIter->eType==OTA_PK_NONE ){
+ zList = otaMPrintfAndCollectError(p, "_rowid_ = ?%d", pIter->nTblCol+1);
+ }else{
+ const char *zSep = "";
+ int i;
+ for(i=0; i<pIter->nTblCol; i++){
+ if( pIter->abTblPk[i] ){
+ const char *zCol = pIter->azTblCol[i];
+ zList = otaMPrintfAndCollectError(
+ p, "%z%s\"%w\"=?%d", zList, zSep, zCol, i+1
+ );
+ zSep = " AND ";
}
}
}
for(i=0; i<pIter->nTblCol; i++){
char c = zMask[i];
if( c=='x' ){
- zList = otaMPrintfAndCollectError(p, "%z%s%s=?%d",
+ zList = otaMPrintfAndCollectError(p, "%z%s\"%w\"=?%d",
zList, zSep, pIter->azTblCol[i], i+1
);
zSep = ", ";
}
if( c=='d' ){
- zList = otaMPrintfAndCollectError(p, "%z%s%s=ota_delta(%s, ?%d)",
+ zList = otaMPrintfAndCollectError(p,
+ "%z%s\"%w\"=ota_delta(\"%w\", ?%d)",
zList, zSep, pIter->azTblCol[i], pIter->azTblCol[i], i+1
);
zSep = ", ";
return zRet;
}
+/*
+** If an error has already occurred when this function is called, it
+** immediately returns zero (without doing any work). Or, if an error
+** occurs during the execution of this function, it sets the error code
+** in the sqlite3ota object indicated by the first argument and returns
+** zero.
+**
+** The iterator passed as the second argument is guaranteed to point to
+** a table (not an index) when this function is called. This function
+** attempts to create any imposter tables required to write to the main
+** table b-tree of the table before returning. Non-zero is returned if
+** imposter tables are created, or zero otherwise.
+**
+** The required imposter tables depend on the type of table that the
+** iterator currently points to.
+**
+** OTA_PK_NONE, OTA_PK_IPK, OTA_PK_WITHOUT_ROWID:
+** A single imposter table is required. With the same schema as
+** the actual target table (less any UNIQUE constraints). More
+** precisely, the "same schema" means the same columns, types, collation
+** sequences and primary key declaration.
+**
+** OTA_PK_VTAB:
+** No imposters required.
+**
+** OTA_PK_EXTERNAL:
+** Two imposters are required (TODO!!)
+*/
+static void otaCreateImposterTable(sqlite3ota *p, OtaObjIter *pIter){
+ if( p->rc==SQLITE_OK && pIter->eType!=OTA_PK_VTAB ){
+ int tnum = pIter->tnum;
+ const char *zComma = "";
+ char *zSql = 0;
+ int iCol;
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 1);
+
+ for(iCol=0; p->rc==SQLITE_OK && iCol<pIter->nTblCol; iCol++){
+ int iDataCol = pIter->aiTblOrder[iCol];
+ const char *zCol = pIter->azTblCol[iDataCol];
+ const char *zColl = 0;
+ p->rc = sqlite3_table_column_metadata(
+ p->db, "main", pIter->zTbl, zCol, 0, &zColl, 0, 0, 0
+ );
+ zSql = otaMPrintfAndCollectError(p, "%z%s\"%w\" %s COLLATE %s",
+ zSql, zComma, zCol, pIter->azTblType[iDataCol], zColl
+ );
+ zComma = ", ";
+ }
+
+ if( pIter->eType==OTA_PK_IPK || pIter->eType==OTA_PK_WITHOUT_ROWID ){
+ zSql = otaMPrintfAndCollectError(p, "%z, PRIMARY KEY(", zSql);
+ zComma = "";
+ for(iCol=0; iCol<pIter->nTblCol; iCol++){
+ if( pIter->abTblPk[iCol] ){
+ zSql = otaMPrintfAndCollectError(p, "%z%s\"%w\"",
+ zSql, zComma, pIter->azTblCol[iCol]
+ );
+ zComma = ", ";
+ }
+ }
+ zSql = otaMPrintfAndCollectError(p, "%z)", zSql);
+ }
+
+ zSql = otaMPrintfAndCollectError(p, "CREATE TABLE ota_imposter(%z)%s",
+ zSql, (pIter->eType==OTA_PK_WITHOUT_ROWID ? " WITHOUT ROWID" : "")
+ );
+ if( p->rc==SQLITE_OK ){
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 1, tnum);
+ p->rc = sqlite3_exec(p->db, zSql, 0, 0, &p->zErrmsg);
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 0);
+ }
+ sqlite3_free(zSql);
+ }
+}
+
/*
** Ensure that the SQLite statement handles required to update the
** target database object currently indicated by the iterator passed
int nOffset /* Add "LIMIT -1 OFFSET $nOffset" to SELECT */
){
assert( pIter->bCleanup==0 );
- if( pIter->pSelect==0 && otaObjIterGetCols(p, pIter)==SQLITE_OK ){
+ if( pIter->pSelect==0 && otaObjIterCacheTableInfo(p, pIter)==SQLITE_OK ){
const int tnum = pIter->tnum;
char *zCollist = 0; /* List of indexed columns */
char **pz = &p->zErrmsg;
}
if( zIdx ){
- char *zImposterCols = 0;
- char *zImposterPK = 0;
- char *zWhere = 0;
+ char *zImposterCols = 0; /* Columns for imposter table */
+ char *zImposterPK = 0; /* Primary key declaration for imposter */
+ char *zWhere = 0; /* WHERE clause on PK columns */
char *zBind = 0;
int nBind = 0;
sqlite3_free(zBind);
}else{
int bOtaRowid = (pIter->eType==OTA_PK_VTAB || pIter->eType==OTA_PK_NONE);
- const char *zTbl = pIter->zTbl;
+ const char *zTbl = pIter->zTbl; /* Table this step applies to */
+ const char *zWrite; /* Imposter table name */
+
+ char *zBindings = otaObjIterGetBindlist(p, pIter->nTblCol + bOtaRowid);
char *zWhere = otaObjIterGetWhere(p, pIter);
char *zOldlist = otaObjIterGetOldlist(p, pIter, "old");
char *zNewlist = otaObjIterGetOldlist(p, pIter, "new");
- char *zBindings = otaObjIterGetBindlist(p, pIter->nTblCol + bOtaRowid);
- zCollist = otaObjIterGetCollist(p, pIter, pIter->nTblCol, 0, 0);
+ zCollist = otaObjIterGetCollist(p, pIter);
pIter->nCol = pIter->nTblCol;
/* Create the SELECT statement to read keys from data_xxx */
);
}
+ /* Create the imposter table or tables (if required). */
+ otaCreateImposterTable(p, pIter);
+ zWrite = (pIter->eType==OTA_PK_VTAB ? zTbl : "ota_imposter");
+
/* Create the INSERT statement to write to the target PK b-tree */
if( p->rc==SQLITE_OK ){
p->rc = prepareFreeAndCollectError(p->db, &pIter->pInsert, pz,
sqlite3_mprintf(
"INSERT INTO main.%Q(%s%s) VALUES(%s)",
- zTbl, zCollist, (bOtaRowid ? ", _rowid_" : ""), zBindings
+ zWrite, zCollist, (bOtaRowid ? ", _rowid_" : ""), zBindings
)
);
}
if( p->rc==SQLITE_OK ){
p->rc = prepareFreeAndCollectError(p->db, &pIter->pDelete, pz,
sqlite3_mprintf(
- "DELETE FROM main.%Q WHERE %s", zTbl, zWhere
+ "DELETE FROM main.%Q WHERE %s", zWrite, zWhere
)
);
}
"CREATE TABLE IF NOT EXISTS ota.'ota_tmp_%q' AS "
"SELECT *%s FROM ota.'data_%q' WHERE 0;"
- "CREATE TEMP TRIGGER ota_delete_%q BEFORE DELETE ON main.%Q "
+ "CREATE TEMP TRIGGER ota_delete_tr BEFORE DELETE ON ota_imposter "
"BEGIN "
" INSERT INTO 'ota_tmp_%q'(ota_control, %s%s) VALUES(2, %s);"
"END;"
- "CREATE TEMP TRIGGER ota_update1_%q BEFORE UPDATE ON main.%Q "
+ "CREATE TEMP TRIGGER ota_update1_tr BEFORE UPDATE ON ota_imposter "
"BEGIN "
" INSERT INTO 'ota_tmp_%q'(ota_control, %s%s) VALUES(2, %s);"
"END;"
- "CREATE TEMP TRIGGER ota_update2_%q AFTER UPDATE ON main.%Q "
+ "CREATE TEMP TRIGGER ota_update2_tr AFTER UPDATE ON ota_imposter "
"BEGIN "
" INSERT INTO 'ota_tmp_%q'(ota_control, %s%s) VALUES(3, %s);"
"END;"
, zTbl, (pIter->eType==OTA_PK_EXTERNAL ? ", 0 AS ota_rowid" : "")
, zTbl,
- zTbl, zTbl, zTbl, zCollist, zOtaRowid, zOldlist,
- zTbl, zTbl, zTbl, zCollist, zOtaRowid, zOldlist,
- zTbl, zTbl, zTbl, zCollist, zOtaRowid, zNewlist
+ zTbl, zCollist, zOtaRowid, zOldlist,
+ zTbl, zCollist, zOtaRowid, zOldlist,
+ zTbl, zCollist, zOtaRowid, zNewlist
);
if( pIter->eType==OTA_PK_EXTERNAL || pIter->eType==OTA_PK_NONE ){
otaMPrintfExec(p,
- "CREATE TEMP TRIGGER ota_insert_%q AFTER INSERT ON main.%Q "
+ "CREATE TEMP TRIGGER ota_insert_tr AFTER INSERT ON ota_imposter "
"BEGIN "
" INSERT INTO 'ota_tmp_%q'(ota_control, %s, ota_rowid)"
" VALUES(0, %s);"
"END;"
- , zTbl, zTbl, zTbl, zCollist, zNewlist
+ , zTbl, zCollist, zNewlist
);
}
}else if( p->rc==SQLITE_OK ){
sqlite3_finalize(pIter->pUpdate);
pIter->pUpdate = 0;
if( p->rc==SQLITE_OK ){
- zUpdate = sqlite3_mprintf("UPDATE %Q SET %s WHERE %s",
- pIter->zTbl, zSet, zWhere
+ zUpdate = sqlite3_mprintf("UPDATE \"%w\" SET %s WHERE %s",
+ (pIter->eType==OTA_PK_VTAB ? pIter->zTbl : "ota_imposter"),
+ zSet, zWhere
);
p->rc = prepareFreeAndCollectError(
p->db, &pIter->pUpdate, &p->zErrmsg, zUpdate
}
for(i=0; i<pIter->nCol; i++){
+ /* If this is an INSERT into a table b-tree and the table has an
+ ** explicit INTEGER PRIMARY KEY, check that this is not an attempt
+ ** to write a NULL into the IPK column. That is not permitted. */
+ if( eType==OTA_INSERT
+ && pIter->zIdx==0 && pIter->eType==OTA_PK_IPK && pIter->abTblPk[i]
+ && sqlite3_column_type(pIter->pSelect, i)==SQLITE_NULL
+ ){
+ p->rc = SQLITE_MISMATCH;
+ p->zErrmsg = sqlite3_mprintf("datatype mismatch");
+ goto step_out;
+ }
+
if( eType==SQLITE_DELETE && pIter->zIdx==0 && pIter->abTblPk[i]==0 ){
continue;
}
+
pVal = sqlite3_column_value(pIter->pSelect, i);
sqlite3_bind_value(pWriter, i+1, pVal);
}
}
}
+ step_out:
return p->rc;
}
-C Have\sota\suse\simposter\stables\sto\swrite\sto\sindexes\sinstead\sof\sthe\ssqlite3_index_writer()\sinterface.\sThe\serror\shandling\sin\sthis\sversion\sis\sbroken\sin\sa\sfew\ssmall\sways.
-D 2015-01-31T20:42:04.027
+C Remove\s"PRAGMA\sota_mode".
+D 2015-02-03T15:56:08.271
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 5407a688f4d77a05c18a8142be8ae5a2829dd610
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
F ext/ota/ota1.test d76b9ec77437759e9da0ff4abe9c070bb9f4eae1
F ext/ota/ota10.test ab815dff9cef7248c504f06b888627d236f25e9c
F ext/ota/ota2.test 2829bc08ffbb71b605392a68fedfd554763356a7
-F ext/ota/ota3.test 71bd8cc0cf8d7e7d9bb11a1fcc238320a5a9d8c8
-F ext/ota/ota4.test 60f897f329a6782ef2f24862640acf3c52e48077
+F ext/ota/ota3.test a77efbce7723332eb688d2b28bf18204fc9614d7
+F ext/ota/ota4.test 82434aa39c9acca6cd6317f6b0ab07b0ec6c2e7d
F ext/ota/ota5.test ad0799daf8923ddebffe75ae8c5504ca90b7fadb
F ext/ota/ota6.test 82f1f757ec9b2ad07d6de4060b8e3ba8e44dfdd3
F ext/ota/ota7.test 1fe2c5761705374530e29f70c39693076028221a
F ext/ota/ota8.test cd70e63a0c29c45c0906692827deafa34638feda
F ext/ota/ota9.test d3eee95dd836824d07a22e5efcdb7bf6e869358b
F ext/ota/otafault.test 508ba87c83d632670ac0f94371a465d4bb4d49dd
-F ext/ota/sqlite3ota.c 975ccfe032ee81ee39368ed5e9cb33cbb6edc603
+F ext/ota/sqlite3ota.c 52c91eec41b8fbb5ed12a8f0a2159bc5ec16498f
F ext/ota/sqlite3ota.h ce378c0c503f625611713133f9c79704ea4ee7a4
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
F ext/rtree/rtree.c 14e6239434d4e3f65d3e90320713f26aa24e167f
F src/backup.c 7ddee9c7d505e07e959a575b18498f17c71e53ea
F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb
F src/btmutex.c 49ca66250c7dfa844a4d4cb8272b87420d27d3a5
-F src/btree.c ddca0ae681c49813a4bfd88d5f922fac71e0ffaa
+F src/btree.c 2a1245df0356a229bcd0fd87a8536b5067f16e82
F src/btree.h 94277c1d30c0b75705974bcc8b0c05e79c03d474
F src/btreeInt.h a3d0ae1d511365e1a2b76ad10960dbe55c286f34
F src/build.c eefaa4f1d86bc3c08023a61fdd1e695b47796975
F src/complete.c 198a0066ba60ab06fc00fba1998d870a4d575463
F src/ctime.c 98f89724adc891a1a4c655bee04e33e716e05887
F src/date.c e4d50b3283696836ec1036b695ead9a19e37a5ac
-F src/delete.c e68b70ac41dcf6e92a813d860fa984fcd9aec042
+F src/delete.c bd1a91ddd247ce13004075251e0b7fe2bf9925ef
F src/expr.c abe930897ccafae3819fd2855cbc1b00c262fd12
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
F src/fkey.c e0444b61bed271a76840cbe6182df93a9baa3f12
F src/hash.c 4263fbc955f26c2e8cdc0cf214bc42435aa4e4f5
F src/hash.h c8f3c31722cf3277d03713909761e152a5b81094
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
-F src/insert.c fef86ab8218cf0d926db93280b9eb5b583981353
+F src/insert.c 5b9243a33726008cc4132897d2be371db12a13be
F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
F src/legacy.c ba1863ea58c4c840335a84ec276fc2b25e22bc4e
F src/lempar.c 7274c97d24bb46631e504332ccd3bd1b37841770
F src/loadext.c 86bd4e2fccd520b748cba52492ab60c4a770f660
-F src/main.c c4cb192ebf0bcc975648ae05ac40bc1f40018c52
+F src/main.c 55d548a2c2f32d27366968c394d091475f7ea00a
F src/malloc.c 740db54387204c9a2eb67c6d98e68b08e9ef4eab
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c abe6ee469b6c5a35c7f22bfeb9c9bac664a1c987
F src/pcache.c d210cf90d04365a74f85d21374dded65af67b0cb
F src/pcache.h b44658c9c932d203510279439d891a2a83e12ba8
F src/pcache1.c 1e77432b40b7d3288327d9cdf399dcdfd2b6d3bf
-F src/pragma.c e49831e54c72894cce08702fe2e127e5c53d90f0
+F src/pragma.c 26fc55619109828c9b7add4cfa8a961b6f4c456d
F src/prepare.c 173a5a499138451b2561614ecb87d78f9f4644b9
F src/printf.c 05edc41450d0eb2c05ef7db113bf32742ae65325
F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e
F src/select.c 1f2087523007c42900ffcbdeaef06a23ad9329fc
F src/shell.c 22b4406b0b59efd14b3b351a5809dda517df6d30
-F src/sqlite.h.in 78e493f94202d8083dd270e257786a6311d1fb3b
+F src/sqlite.h.in 8913937ba11415bf369818431700adf3a921fb18
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
F src/sqlite3ext.h 17d487c3c91b0b8c584a32fbeb393f6f795eea7d
-F src/sqliteInt.h 66180aa8f81155a7f391bbf759ee5a3b61d2f89f
+F src/sqliteInt.h 57f8f45028598cc2877fc08ac03b402242242c68
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
F src/status.c 81712116e826b0089bb221b018929536b2b5406f
F src/table.c e7a09215315a978057fb42c640f890160dbcc45e
F src/tclsqlite.c b321464aba1fff1ed9317ebc82a1a94887f97af8
-F src/test1.c 313567541c980e45220d6faed393b6ad454f8ecd
+F src/test1.c ce8ea168800d129acb2c0afdf2831ddf8667e082
F src/test2.c 577961fe48961b2f2e5c8b56ee50c3f459d3359d
F src/test3.c 64d2afdd68feac1bb5e2ffb8226c8c639f798622
F src/test4.c d168f83cc78d02e8d35567bb5630e40dcd85ac1e
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 6bbcc9fe50c917864d48287b4792d46d6e873481
F src/tokenize.c e00458c9938072b0ea711c850b8dcf4ddcb5fe18
-F src/trigger.c 6dcdf46a21acf4d4e011c809b2c971e63f797a1a
+F src/trigger.c 25571661fdeae8c7f975ff40ffec205520a3f92f
F src/update.c 3c4ecc282accf12d39edb8d524cf089645e55a13
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
F src/util.c 98a7627ca48ad3265b6940915a1d08355eb3fc7e
F src/vdbeInt.h 9bb69ff2447c34b6ccc58b34ec35b615f86ead78
F src/vdbeapi.c 4bc511a46b9839392ae0e90844a71dc96d9dbd71
F src/vdbeaux.c 97911edb61074b871ec4aa2d6bb779071643dee5
-F src/vdbeblob.c ad7787440295e43c12248dc48cde4b13e5df4ca0
+F src/vdbeblob.c 4af4bfb71f6df7778397b4a0ebc1879793276778
F src/vdbemem.c 31d8eabb0cd78bfeab4e5124c7363c3e9e54db9f
F src/vdbesort.c 6d64c5448b64851b99931ede980addc3af70d5e2
F src/vdbetrace.c 7e4222955e07dd707a2f360c0eb73452be1cb010
F tool/mkautoconfamal.sh d1a2da0e15b2ed33d60af35c7e9d483f13a8eb9f
F tool/mkkeywordhash.c dfff09dbbfaf950e89af294f48f902181b144670
F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e
-F tool/mkpragmatab.tcl 5a401a3f5461fedb2e3230eb9908a18d485fefea
+F tool/mkpragmatab.tcl aea392b69f8e72715760629cd50411d37bc85de4
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
F tool/mksqlite3c-noext.tcl 9ef48e1748dce7b844f67e2450ff9dfeb0fb4ab5
F tool/mksqlite3c.tcl cfde806851c413db7689b9cb74a4eeb92539c601
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P f9b6dc77021ee421bffd5697d5d337d3bbd07eb9
-R 907483269529ba471a525b7f8d05bc2e
+P cdaeab467f6aa3217be161377a9b78a4eec37093
+R 2703aba199c695992db82956950982ca
U dan
-Z 87f206a3330dd276f74650f9e68af8ed
+Z fae9dc4e5e5c1a2d2a162b422a05ccf9
return rc;
}
-#ifdef SQLITE_ENABLE_OTA
-/*
-** Allocate and populate the output arrays returned by the
-** sqlite3_index_writer() function.
-*/
-static int indexWriterOutputVars(
- sqlite3 *db,
- Index *pIdx,
- const char ***pazColl, /* OUT: Array of collation sequences */
- int **paiCol, /* OUT: Array of column indexes */
- int *pnCol /* OUT: Total columns in index keys */
-){
- Table *pTbl = pIdx->pTable; /* Table index is attached to */
- Index *pPk = 0;
- int nByte = 0; /* Total bytes of space to allocate */
- int i; /* Iterator variable */
-
- int *aiCol;
- const char **azColl;
- char *pCsr;
-
- if( !HasRowid(pTbl) ){
- pPk = sqlite3PrimaryKeyIndex(pTbl);
- }
-
- for(i=0; i<pIdx->nColumn; i++){
- const char *zColl = 0;
- if( i<pIdx->nKeyCol ){
- zColl = pIdx->azColl[i];
- }else if( pPk ){
- zColl = pPk->azColl[i-pIdx->nKeyCol];
- }
- if( zColl==0 ) zColl = "BINARY";
- nByte += sqlite3Strlen30(zColl) + 1;
- }
- nByte += (pIdx->nColumn) * (sizeof(const char*) + sizeof(int));
-
- /* Populate the output variables */
- *pazColl = azColl = (const char**)sqlite3DbMallocZero(db, nByte);
- if( azColl==0 ) return SQLITE_NOMEM;
- *paiCol = aiCol = (int*)&azColl[pIdx->nColumn];
- *pnCol = pIdx->nColumn;
- pCsr = (char*)&aiCol[pIdx->nColumn];
-
- for(i=0; i<pIdx->nColumn; i++){
- const char *zColl = 0;
- int nColl;
- int iCol = pTbl->iPKey;
- if( i<pIdx->nKeyCol ){
- zColl = pIdx->azColl[i];
- iCol = pIdx->aiColumn[i];
- }else if( pPk ){
- zColl = pPk->azColl[i-pIdx->nKeyCol];
- iCol = pPk->aiColumn[i-pIdx->nKeyCol];
- }
- if( zColl==0 ) zColl = "BINARY";
-
- aiCol[i] = iCol;
- azColl[i] = pCsr;
- nColl = 1 + sqlite3Strlen30(zColl);
- memcpy(pCsr, zColl, nColl);
- pCsr += nColl;
- }
-
- return SQLITE_OK;
-}
-
-/*
-** Prepare and return an SQL statement handle that can be used to write
-** directly to an index b-tree.
-*/
-int sqlite3_index_writer(
- sqlite3 *db,
- int bDelete,
- const char *zIndex,
- sqlite3_stmt **ppStmt,
- const char ***pazColl, /* OUT: Array of collation sequences */
- int **paiCol, /* OUT: Array of column indexes */
- int *pnCol /* OUT: Total columns in index keys */
-){
- int rc = SQLITE_OK;
- Parse *pParse = 0;
- Index *pIdx = 0; /* The index to write to */
- Table *pTab;
- int i; /* Used to iterate through index columns */
- Vdbe *v = 0;
- int regRec; /* Register to assemble record in */
- const char *zAffinity = 0; /* Affinity string for the current index */
-
- sqlite3_mutex_enter(db->mutex);
- sqlite3BtreeEnterAll(db);
-
- /* Allocate the parse context */
- pParse = sqlite3StackAllocRaw(db, sizeof(*pParse));
- if( !pParse ) goto index_writer_out;
- memset(pParse, 0, sizeof(Parse));
- pParse->db = db;
-
- /* Allocate the Vdbe */
- v = sqlite3GetVdbe(pParse);
- if( v==0 ) goto index_writer_out;
-
- /* Find the index to write to */
- pIdx = sqlite3FindIndex(db, zIndex, "main");
- if( pIdx==0 ){
- sqlite3ErrorMsg(pParse, "no such index: %s", zIndex);
- goto index_writer_out;
- }
- pTab = pIdx->pTable;
- zAffinity = sqlite3IndexAffinityStr(v, pIdx);
-
- rc = indexWriterOutputVars(db, pIdx, pazColl, paiCol, pnCol);
- if( rc!=SQLITE_OK ) goto index_writer_out;
-
- /* Add an OP_Noop to the VDBE program. Then store a pointer to the
- ** output array *paiCol as its P4 value. This is so that the array
- ** is automatically deleted when the user finalizes the statement. The
- ** OP_Noop serves no other purpose. */
- sqlite3VdbeAddOp0(v, OP_Noop);
- sqlite3VdbeChangeP4(v, -1, (const char*)(*pazColl), P4_INTARRAY);
-
- sqlite3BeginWriteOperation(pParse, 0, 0);
-
- /* Open a write cursor on the index */
- pParse->nTab = 1;
- sqlite3VdbeAddOp3(v, OP_OpenWrite, 0, pIdx->tnum, 0);
- sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
-
- /* Create the record to insert into the index. Store it in register regRec. */
- pParse->nVar = pIdx->nColumn;
- pParse->nMem = pIdx->nColumn;
- for(i=1; i<=pIdx->nColumn; i++){
- sqlite3VdbeAddOp2(v, OP_Variable, i, i);
- }
- regRec = ++pParse->nMem;
-
- /* If this is a rowid table, check that the rowid field is an integer. */
- if( HasRowid(pTab) ){
- sqlite3VdbeAddOp2(v, OP_MustBeInt, pIdx->nColumn, 0);
- VdbeCoverageNeverTaken(v);
- }
-
- if( bDelete==0 ){
- sqlite3VdbeAddOp4(v, OP_MakeRecord, 1, pIdx->nColumn, regRec, zAffinity, 0);
-
- /* If this is a UNIQUE index, check the constraint. */
- if( pIdx->onError ){
- int addr = sqlite3VdbeAddOp4Int(v, OP_NoConflict, 0, 0, 1, pIdx->nKeyCol);
- VdbeCoverage(v);
- sqlite3UniqueConstraint(pParse, SQLITE_ABORT, pIdx);
- sqlite3VdbeJumpHere(v, addr);
- }
-
- /* Code the IdxInsert to write to the b-tree index. */
- sqlite3VdbeAddOp2(v, OP_IdxInsert, 0, regRec);
- }else{
- /* Code the IdxDelete to remove the entry from the b-tree index. */
- sqlite3VdbeAddOp4(v, OP_Affinity, 1, pIdx->nColumn, 0, zAffinity, 0);
- sqlite3VdbeAddOp3(v, OP_IdxDelete, 0, 1, pIdx->nColumn);
- }
- sqlite3FinishCoding(pParse);
-
-index_writer_out:
- if( rc==SQLITE_OK && db->mallocFailed==0 ){
- *ppStmt = (sqlite3_stmt*)v;
- }else{
- *ppStmt = 0;
- if( v ) sqlite3VdbeFinalize(v);
- }
-
- sqlite3ParserReset(pParse);
- sqlite3StackFree(db, pParse);
- sqlite3BtreeLeaveAll(db);
- rc = sqlite3ApiExit(db, rc);
- sqlite3_mutex_leave(db->mutex);
- return rc;
-}
-#endif /* SQLITE_ENABLE_OTA */
-
#endif /* #ifndef SQLITE_OMIT_INCRBLOB */