From 0c4ba2662efca18411f39c30fff7746c892563ac Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 5 Sep 2014 19:31:15 +0000 Subject: [PATCH] Reorganize the code in sqlite3ota.c in preparation for adding support for update and delete operations. FossilOrigin-Name: 98387f05697526c7740e91d8a846a31f77639406 --- ext/ota/ota1.test | 2 +- ext/ota/sqlite3ota.c | 805 ++++++++++++++++++++++--------------------- manifest | 14 +- manifest.uuid | 2 +- 4 files changed, 419 insertions(+), 404 deletions(-) diff --git a/ext/ota/ota1.test b/ext/ota/ota1.test index aa334b9581..12afc6e3f1 100644 --- a/ext/ota/ota1.test +++ b/ext/ota/ota1.test @@ -63,7 +63,7 @@ proc step_ota {target ota} { set rc } -foreach {tn2 cmd} {1 step_ota 2 run_ota} { +foreach {tn2 cmd} {1 run_ota 2 step_ota} { foreach {tn schema} { 1 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); diff --git a/ext/ota/sqlite3ota.c b/ext/ota/sqlite3ota.c index 2f61c671ac..b5f6e2c436 100644 --- a/ext/ota/sqlite3ota.c +++ b/ext/ota/sqlite3ota.c @@ -30,7 +30,7 @@ ** "idx" -> Index currently being written (target database names). ** Or, if the main table is being written, a NULL value. ** -** "row" -> Last rowid processed from ota database table (i.e. data_%). +** "row" -> Number of rows for this object already processed ** ** "progress" -> total number of key/value b-tree operations performed ** so far as part of this ota update. @@ -38,63 +38,50 @@ #define OTA_CREATE_STATE "CREATE TABLE IF NOT EXISTS ota_state" \ "(tbl, idx, row, progress)" -typedef struct OtaTblIter OtaTblIter; -typedef struct OtaIdxIter OtaIdxIter; typedef struct OtaState OtaState; +typedef struct OtaObjIter OtaObjIter; +typedef unsigned char u8; /* -** Iterator used to iterate through all data tables in the OTA. As follows: -** -** OtaTblIter iter; -** for(rc=tblIterFirst(db, &iter); -** rc==SQLITE_OK && iter.zTarget; -** rc=tblIterNext(&iter) -** ){ -** } +** A structure to store values read from the ota_state table in memory. */ -struct OtaTblIter { - sqlite3_stmt *pTblIter; /* Iterate through tables */ - int iEntry; /* Index of current entry (from 1) */ - - /* Output varibles. zTarget==0 implies EOF. */ - const char *zTarget; /* Name of target table */ - const char *zSource; /* Name of source table */ - - /* Useful things populated by a call to tblIterPrepareAll() */ - int nCol; /* Number of columns in this table */ - char **azCol; /* Array of quoted column names */ - sqlite3_stmt *pSelect; /* PK b-tree SELECT statement */ - sqlite3_stmt *pInsert; /* PK b-tree INSERT statement */ +struct OtaState { + char *zTbl; + char *zIdx; + int nRow; }; /* -** API is: +** An iterator of this type is used to iterate through all objects in +** the target database that require updating. For each such table, the +** iterator visits, in order: ** -** idxIterFirst() -** idxIterNext() -** idxIterFinalize() -** idxIterPrepareAll() +** * the table itself, +** * each index of the table (zero or more points to visit), and +** * a special "cleanup table" point. */ -struct OtaIdxIter { - sqlite3_stmt *pIdxIter; /* Iterate through indexes */ - int iEntry; /* Index of current entry (from 1) */ - - /* Output varibles. zTarget==0 implies EOF. */ - const char *zIndex; /* Name of index */ - - int nCol; /* Number of columns in index */ - int *aiCol; /* Array of column indexes */ - sqlite3_stmt *pWriter; /* Index writer */ - sqlite3_stmt *pSelect; /* Select to read values in index order */ -}; - -struct OtaState { - char *zTbl; - char *zIdx; - sqlite3_int64 iRow; +struct OtaObjIter { + sqlite3_stmt *pTblIter; /* Iterate through tables */ + sqlite3_stmt *pIdxIter; /* Index iterator */ + int nTblCol; /* Size of azTblCol[] array */ + char **azTblCol; /* Array of quoted column names */ + u8 *abTblPk; /* Array of flags - true for PK columns */ + + /* Output variables. zTbl==0 implies EOF. */ + int bCleanup; /* True in "cleanup" state */ + const char *zTbl; /* Name of target db table */ + const char *zIdx; /* Name of target db index (or null) */ + int iVisit; /* Number of points visited, incl. current */ + + /* Statements created by otaObjIterPrepareAll() */ + int nCol; /* Number of columns in current object */ + sqlite3_stmt *pSelect; /* Source data */ + sqlite3_stmt *pInsert; /* Statement for INSERT operations */ }; - +/* +** OTA handle. +*/ struct sqlite3ota { sqlite3 *dbDest; /* Target db */ sqlite3 *dbOta; /* Ota db */ @@ -103,15 +90,25 @@ struct sqlite3ota { int rc; /* Value returned by last ota_step() call */ char *zErrmsg; /* Error message if rc!=SQLITE_OK */ - OtaTblIter tbliter; /* Used to iterate through tables */ - OtaIdxIter idxiter; /* Used to iterate through indexes */ + int nStep; /* Rows processed for current object */ + OtaObjIter objiter; }; +/* +** Prepare the SQL statement in buffer zSql against database handle db. +** If successful, set *ppStmt to point to the new statement and return +** SQLITE_OK. +** +** Otherwise, if an error does occur, set *ppStmt to NULL and return +** an SQLite error code. Additionally, set output variable *pzErrmsg to +** point to a buffer containing an error message. It is the responsibility +** of the caller to (eventually) free this buffer using sqlite3_free(). +*/ static int prepareAndCollectError( sqlite3 *db, - const char *zSql, sqlite3_stmt **ppStmt, - char **pzErrmsg + char **pzErrmsg, + const char *zSql ){ int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0); if( rc!=SQLITE_OK ){ @@ -121,6 +118,22 @@ static int prepareAndCollectError( return rc; } +/* +** Reset the SQL statement passed as the first argument. Return a copy +** of the value returned by sqlite3_reset(). +** +** If an error has occurred, then set *pzErrmsg to point to a buffer +** containing an error message. It is the responsibility of the caller +** to eventually free this buffer using sqlite3_free(). +*/ +static int resetAndCollectError(sqlite3_stmt *pStmt, char **pzErrmsg){ + int rc = sqlite3_reset(pStmt); + if( rc!=SQLITE_OK ){ + *pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(sqlite3_db_handle(pStmt))); + } + return rc; +} + /* ** Unless it is NULL, argument zSql points to a buffer allocated using ** sqlite3_malloc containing an SQL statement. This function prepares the SQL @@ -138,9 +151,9 @@ static int prepareAndCollectError( */ static int prepareFreeAndCollectError( sqlite3 *db, - char *zSql, sqlite3_stmt **ppStmt, - char **pzErrmsg + char **pzErrmsg, + char *zSql ){ int rc; assert( *pzErrmsg==0 ); @@ -148,359 +161,377 @@ static int prepareFreeAndCollectError( rc = SQLITE_NOMEM; *ppStmt = 0; }else{ - rc = prepareAndCollectError(db, zSql, ppStmt, pzErrmsg); + rc = prepareAndCollectError(db, ppStmt, pzErrmsg, zSql); sqlite3_free(zSql); } return rc; } -static char *quoteSqlName(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; inTblCol; i++){ + sqlite3_free(pIter->azTblCol[i]); } - return zRet; + sqlite3_free(pIter->azTblCol); + sqlite3_free(pIter->abTblPk); + pIter->azTblCol = 0; + pIter->abTblPk = 0; + pIter->nTblCol = 0; } -static int tblIterPrepareAll(sqlite3ota *p){ - OtaTblIter *pIter = &p->tbliter; - int rc = SQLITE_OK; - char *zCol = 0; - char *zBindings = 0; - char *zSql; - sqlite3_stmt *pPragma = 0; - int i; - int bSeenPk = 0; /* Set to true once PK column seen */ +/* +** Clean up any resources allocated as part of the iterator object passed +** as the only argument. +*/ +static void otaObjIterFinalize(OtaObjIter *pIter){ + sqlite3_finalize(pIter->pTblIter); + sqlite3_finalize(pIter->pIdxIter); + sqlite3_finalize(pIter->pSelect); + sqlite3_finalize(pIter->pInsert); + otaObjIterFreeCols(pIter); + memset(pIter, 0, sizeof(OtaObjIter)); +} - /* Allocate and populate the azCol[] array */ - zSql = sqlite3_mprintf("PRAGMA main.table_info(%Q)", pIter->zTarget); - rc = prepareFreeAndCollectError(p->dbDest, zSql, &pPragma, &p->zErrmsg); - pIter->nCol = 0; +/* +** Advance the iterator to the next position. +** +** If no error occurs, SQLITE_OK is returned and the iterator is left +** pointing to the next entry. Otherwise, an error code and message is +** left in the OTA handle passed as the first argument. A copy of the +** error code is returned. +*/ +static int otaObjIterNext(sqlite3ota *p, OtaObjIter *pIter){ + int rc = p->rc; if( rc==SQLITE_OK ){ - while( SQLITE_ROW==sqlite3_step(pPragma) ){ - const char *zName = (const char*)sqlite3_column_text(pPragma, 1); - if( (pIter->nCol % 4)==0 ){ - int nByte = sizeof(char*) * (pIter->nCol+4); - char **azNew = (char**)sqlite3_realloc(pIter->azCol, nByte); - if( azNew==0 ){ - rc = SQLITE_NOMEM; - break; - } - pIter->azCol = azNew; - } - pIter->azCol[pIter->nCol] = quoteSqlName(zName); - if( pIter->azCol[pIter->nCol]==0 ){ - rc = SQLITE_NOMEM; - break; - } - pIter->nCol++; - if( sqlite3_column_int(pPragma, 5) ) bSeenPk = 1; - } - if( rc==SQLITE_OK ){ - rc = sqlite3_finalize(pPragma); - }else{ - sqlite3_finalize(pPragma); - } - } - - /* If the table has no PRIMARY KEY, throw an exception. */ - if( bSeenPk==0 ){ - p->zErrmsg = sqlite3_mprintf("table %s has no PRIMARY KEY", pIter->zTarget); - rc = SQLITE_ERROR; - } - /* Populate the zCol variable */ - for(i=0; rc==SQLITE_OK && inCol; i++){ - zCol = sqlite3_mprintf("%z%s%s", zCol, (i==0?"":", "), pIter->azCol[i]); - if( zCol==0 ){ - rc = SQLITE_NOMEM; - } - } - - /* Allocate and populate zBindings */ - if( rc==SQLITE_OK ){ - zBindings = (char*)sqlite3_malloc(pIter->nCol * 2); - if( zBindings==0 ){ - rc = SQLITE_NOMEM; + /* Free any SQLite statements used while processing the previous object */ + sqlite3_finalize(pIter->pSelect); + sqlite3_finalize(pIter->pInsert); + pIter->pSelect = 0; + pIter->pInsert = 0; + pIter->nCol = 0; + + 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{ - int i; - for(i=0; inCol; i++){ - zBindings[i*2] = '?'; - zBindings[i*2+1] = ','; + 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); + rc = SQLITE_OK; } - zBindings[pIter->nCol*2-1] = '\0'; } } - /* Create OtaTblIter.pSelect */ - if( rc==SQLITE_OK ){ - zSql = sqlite3_mprintf("SELECT rowid, %s FROM %Q", zCol, pIter->zSource); - rc = prepareFreeAndCollectError(p->dbOta,zSql,&pIter->pSelect, &p->zErrmsg); - } - - /* Create OtaTblIter.pInsert */ - if( rc==SQLITE_OK ){ - zSql = sqlite3_mprintf("INSERT INTO %Q(%s) VALUES(%s)", - pIter->zTarget, zCol, zBindings - ); - rc = prepareFreeAndCollectError(p->dbDest,zSql,&pIter->pInsert,&p->zErrmsg); + if( rc!=SQLITE_OK ){ + otaObjIterFinalize(pIter); + p->rc = rc; } - - sqlite3_free(zCol); - sqlite3_free(zBindings); + pIter->iVisit++; return rc; } -static void tblIterFreeAll(OtaTblIter *pIter){ - int i; - - sqlite3_finalize(pIter->pSelect); - sqlite3_finalize(pIter->pInsert); - for(i=0; inCol; i++) sqlite3_free(pIter->azCol[i]); - sqlite3_free(pIter->azCol); - pIter->azCol = 0; - pIter->pSelect = 0; - pIter->pInsert = 0; - pIter->nCol = 0; -} - -static int tblIterNext(OtaTblIter *pIter){ +/* +** Initialize the iterator structure passed as the second argument. +** +** If no error occurs, SQLITE_OK is returned and the iterator is left +** pointing to the first entry. Otherwise, an error code and message is +** left in the OTA handle passed as the first argument. A copy of the +** error code is returned. +*/ +static int otaObjIterFirst(sqlite3ota *p, OtaObjIter *pIter){ int rc; + memset(pIter, 0, sizeof(OtaObjIter)); - tblIterFreeAll(pIter); - assert( pIter->pTblIter ); - rc = sqlite3_step(pIter->pTblIter); - if( rc==SQLITE_ROW ){ - pIter->zSource = (const char*)sqlite3_column_text(pIter->pTblIter, 0); - pIter->zTarget = &pIter->zSource[5]; assert( 5==strlen("data_") ); - pIter->iEntry++; - }else{ - pIter->zSource = 0; - pIter->zTarget = 0; - } - - if( rc==SQLITE_ROW || rc==SQLITE_DONE ) rc = SQLITE_OK; - return rc; -} - -static int tblIterFirst(sqlite3 *db, OtaTblIter *pIter){ - int rc; /* return code */ - memset(pIter, 0, sizeof(OtaTblIter)); - rc = sqlite3_prepare_v2(db, - "SELECT name FROM sqlite_master " - "WHERE type='table' AND name LIKE 'data_%'", -1, &pIter->pTblIter, 0 + rc = prepareAndCollectError(p->dbOta, &pIter->pTblIter, &p->zErrmsg, + "SELECT substr(name, 6) FROM sqlite_master " + "WHERE type='table' AND name LIKE 'data_%'" ); + if( rc==SQLITE_OK ){ - rc = tblIterNext(pIter); + rc = prepareAndCollectError(p->dbDest, &pIter->pIdxIter, &p->zErrmsg, + "SELECT name FROM sqlite_master " + "WHERE type='index' AND tbl_name = ?" + ); } - return rc; -} - -static void tblIterFinalize(OtaTblIter *pIter){ - tblIterFreeAll(pIter); - sqlite3_finalize(pIter->pTblIter); - memset(pIter, 0, sizeof(OtaTblIter)); + pIter->bCleanup = 1; + p->rc = rc; + return otaObjIterNext(p, pIter); } -static void idxIterFreeAll(OtaIdxIter *pIter){ - sqlite3_finalize(pIter->pWriter); - sqlite3_finalize(pIter->pSelect); - pIter->pWriter = 0; - pIter->pSelect = 0; - pIter->aiCol = 0; - pIter->nCol = 0; +/* +** 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; iidxiter; - - /* Prepare the writer statement to write (insert) entries into the index. */ - rc = sqlite3_index_writer( - p->dbDest, 0, pIter->zIndex, &pIter->pWriter, &pIter->aiCol, &pIter->nCol - ); +/* +** If they are not already populated, populate the pIter->azTblCol[], +** pIter->abTblPk[] and pIter->nTblCol variables according to the table +** 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){ + if( pIter->azTblCol==0 ){ + sqlite3_stmt *pStmt; + char *zSql; + int nCol = 0; + int bSeenPk = 0; + int rc2; /* sqlite3_finalize() return value */ + + zSql = sqlite3_mprintf("PRAGMA table_info(%Q)", pIter->zTbl); + p->rc = prepareFreeAndCollectError(p->dbDest, &pStmt, &p->zErrmsg, zSql); + while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ + if( (nCol % 8)==0 ){ + int nByte = sizeof(char*) * (nCol+8); + char **azNew = (char**)sqlite3_realloc(pIter->azTblCol, nByte); + u8 *abNew = (u8*)sqlite3_realloc(pIter->azTblCol, nCol+8); + + if( azNew ) pIter->azTblCol = azNew; + if( abNew ) pIter->abTblPk = abNew; + if( azNew==0 || abNew==0 ) p->rc = SQLITE_NOMEM; + } - /* Prepare a SELECT statement to read values from the source table in - ** the same order as they are stored in the current index. The statement - ** is: - ** - ** SELECT rowid, FROM data_ ORDER BY - */ - for(i=0; rc==SQLITE_OK && inCol; i++){ - const char *zQuoted = p->tbliter.azCol[ pIter->aiCol[i] ]; - zCols = sqlite3_mprintf("%z%s%s", zCols, zCols?", ":"", zQuoted); - if( !zCols ){ - rc = SQLITE_NOMEM; + if( p->rc==SQLITE_OK ){ + const char *zName = (const char*)sqlite3_column_text(pStmt, 1); + pIter->abTblPk[nCol] = sqlite3_column_int(pStmt, 5); + if( pIter->abTblPk[nCol] ) bSeenPk = 1; + pIter->azTblCol[nCol] = otaQuoteName(zName); + if( pIter->azTblCol[nCol]==0 ) p->rc = SQLITE_NOMEM; + nCol++; + } } - } - if( rc==SQLITE_OK ){ - const char *zFmt = "SELECT rowid, %s FROM %Q ORDER BY %s"; - zSql = sqlite3_mprintf(zFmt, zCols, p->tbliter.zSource, zCols); - if( zSql ){ - sqlite3_stmt **pp = &p->idxiter.pSelect; - rc = prepareFreeAndCollectError(p->dbOta, zSql, pp, &p->zErrmsg); - }else{ - rc = SQLITE_NOMEM; + pIter->nTblCol = nCol; + rc2 = sqlite3_finalize(pStmt); + if( p->rc==SQLITE_OK ) p->rc = rc2; + + if( p->rc==SQLITE_OK && bSeenPk==0 ){ + p->zErrmsg = sqlite3_mprintf("table %s has no PRIMARY KEY", pIter->zTbl); + p->rc = SQLITE_ERROR; } } - sqlite3_free(zCols); - return rc; + return p->rc; } -static int idxIterNext(OtaIdxIter *pIter){ - int rc; - - idxIterFreeAll(pIter); - assert( pIter->pIdxIter ); - rc = sqlite3_step(pIter->pIdxIter); - if( rc==SQLITE_ROW ){ - pIter->zIndex = (const char*)sqlite3_column_text(pIter->pIdxIter, 0); - pIter->iEntry++; - }else{ - pIter->zIndex = 0; - rc = sqlite3_finalize(pIter->pIdxIter); - pIter->pIdxIter = 0; +static char *otaObjIterGetCollist( + sqlite3ota *p, + OtaObjIter *pIter, + int nCol, + int *aiCol +){ + char *zList = 0; + if( p->rc==SQLITE_OK ){ + const char *zSep = ""; + int i; + for(i=0; iazTblCol[iCol]); + zSep = ", "; + if( zList==0 ){ + p->rc = SQLITE_NOMEM; + break; + } + } } - - if( rc==SQLITE_ROW ) rc = SQLITE_OK; - return rc; + return zList; } -static int idxIterFirst(sqlite3 *db, const char *zTable, OtaIdxIter *pIter){ - int rc; /* return code */ - memset(pIter, 0, sizeof(OtaIdxIter)); - rc = sqlite3_prepare_v2(db, - "SELECT name FROM sqlite_master " - "WHERE type='index' AND tbl_name = ?", -1, &pIter->pIdxIter, 0 - ); - if( rc==SQLITE_OK ){ - rc = sqlite3_bind_text(pIter->pIdxIter, 1, zTable, -1, SQLITE_TRANSIENT); - } - if( rc==SQLITE_OK ){ - rc = idxIterNext(pIter); +static char *otaObjIterGetBindlist(sqlite3ota *p, int nBind){ + char *zRet = 0; + if( p->rc==SQLITE_OK ){ + int nByte = nBind*2 + 1; + zRet = sqlite3_malloc(nByte); + if( zRet==0 ){ + p->rc = SQLITE_NOMEM; + }else{ + int i; + for(i=0; ipIdxIter); - memset(pIter, 0, sizeof(OtaIdxIter)); + return zRet; } /* -** Call sqlite3_reset() on the SQL statement passed as the second argument. -** If it returns anything other than SQLITE_OK, store the error code and -** error message in the OTA handle. +** Ensure that the SQLite statement handles required to update the +** target database object currently indicated by the iterator passed +** as the second argument are available. */ -static void otaResetStatement(sqlite3ota *p, sqlite3_stmt *pStmt){ - assert( p->rc==SQLITE_OK ); - assert( p->zErrmsg==0 ); - p->rc = sqlite3_reset(pStmt); - if( p->rc!=SQLITE_OK ){ - sqlite3 *db = sqlite3_db_handle(pStmt); - p->zErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db)); +static int otaObjIterPrepareAll( + sqlite3ota *p, + OtaObjIter *pIter, + int nOffset /* Add "LIMIT -1 OFFSET $nOffset" to SELECT */ +){ + assert( pIter->bCleanup==0 ); + if( pIter->pSelect==0 && otaObjIterGetCols(p, pIter)==SQLITE_OK ){ + char *zCollist = 0; /* List of indexed columns */ + char **pz = &p->zErrmsg; + const char *zIdx = pIter->zIdx; + char *zLimit = 0; + + if( nOffset ){ + zLimit = sqlite3_mprintf(" LIMIT -1 OFFSET %d", nOffset); + if( !zLimit ) p->rc = SQLITE_NOMEM; + } + + if( zIdx ){ + int *aiCol; /* Column map */ + + /* Create the index writer */ + if( p->rc==SQLITE_OK ){ + p->rc = sqlite3_index_writer( + p->dbDest, 0, zIdx, &pIter->pInsert, &aiCol, &pIter->nCol + ); + } + + /* Create the SELECT statement to read keys in sorted order */ + zCollist = otaObjIterGetCollist(p, pIter, pIter->nCol, aiCol); + if( p->rc==SQLITE_OK ){ + p->rc = prepareFreeAndCollectError(p->dbOta, &pIter->pSelect, pz, + sqlite3_mprintf( + "SELECT %s FROM 'data_%q' ORDER BY %s%s", + zCollist, pIter->zTbl, zCollist, zLimit + ) + ); + } + }else{ + char *zBindings = otaObjIterGetBindlist(p, pIter->nTblCol); + zCollist = otaObjIterGetCollist(p, pIter, pIter->nTblCol, 0); + pIter->nCol = pIter->nTblCol; + + /* Create the SELECT statement to read keys from data_xxx */ + if( p->rc==SQLITE_OK ){ + p->rc = prepareFreeAndCollectError(p->dbOta, &pIter->pSelect, pz, + sqlite3_mprintf( + "SELECT %s FROM 'data_%q'%s", + zCollist, pIter->zTbl, zLimit) + ); + } + + /* Create the INSERT statement to write to the target PK b-tree */ + if( p->rc==SQLITE_OK ){ + p->rc = prepareFreeAndCollectError(p->dbDest, &pIter->pInsert, pz, + sqlite3_mprintf( + "INSERT INTO %Q(%s) VALUES(%s)", pIter->zTbl, zCollist, zBindings + ) + ); + } + sqlite3_free(zBindings); + } + sqlite3_free(zCollist); + sqlite3_free(zLimit); } + + return p->rc; } -/* -** Check that all SQL statements required to process the current -** table and index have been prepared. If not, prepare them. If -** an error occurs, store the error code and message in the OTA -** handle before returning. +/* +** This function does the work for an sqlite3ota_step() call. +** +** The object-iterator (p->objiter) currently points to a valid object, +** and the input cursor (p->objiter.pSelect) currently points to a valid +** input row. Perform whatever processing is required and return. +** +** If no error occurs, SQLITE_OK is returned. Otherwise, an error code +** and message is left in the OTA handle and a copy of the error code +** returned. */ -static int otaPrepareAll(sqlite3ota *p){ - assert( p->rc==SQLITE_OK ); - assert( p->zErrmsg==0 ); - assert( p->tbliter.zTarget ); +static int otaStep(sqlite3ota *p){ + OtaObjIter *pIter = &p->objiter; + int i; - if( p->tbliter.pSelect==0 ){ - p->rc = tblIterPrepareAll(p); - } - if( p->rc==SQLITE_OK && p->idxiter.zIndex && 0==p->idxiter.pSelect ){ - p->rc = idxIterPrepareAll(p); + for(i=0; inCol; i++){ + sqlite3_value *pVal = sqlite3_column_value(pIter->pSelect, i); + sqlite3_bind_value(pIter->pInsert, i+1, pVal); } + + sqlite3_step(pIter->pInsert); + p->rc = resetAndCollectError(pIter->pInsert, &p->zErrmsg); return p->rc; } +/* +** Step the OTA object. +*/ int sqlite3ota_step(sqlite3ota *p){ if( p ){ - while( p && p->rc==SQLITE_OK && p->tbliter.zTarget ){ - sqlite3_stmt *pSelect; - int i; - - otaPrepareAll(p); - pSelect = (p->idxiter.zIndex ? p->idxiter.pSelect : p->tbliter.pSelect); - - /* Advance to the next input row. */ - if( p->rc==SQLITE_OK ){ - int rc = sqlite3_step(pSelect); - if( rc!=SQLITE_ROW ){ - otaResetStatement(p, pSelect); - - /* Go to the next index. */ - if( p->rc==SQLITE_OK ){ - if( p->idxiter.zIndex ){ - p->rc = idxIterNext(&p->idxiter); - }else{ - p->rc = idxIterFirst(p->dbDest, p->tbliter.zTarget, &p->idxiter); - } - } + OtaObjIter *pIter = &p->objiter; + while( p && p->rc==SQLITE_OK && pIter->zTbl ){ - /* If there is no next index, go to the next table. */ - if( p->rc==SQLITE_OK && p->idxiter.zIndex==0 ){ - p->rc = tblIterNext(&p->tbliter); + if( pIter->bCleanup ){ + /* this is where cleanup of the ota_xxx table will happen... */ + }else{ + otaObjIterPrepareAll(p, pIter, 0); + + /* Advance to the next row to process. */ + if( p->rc==SQLITE_OK ){ + int rc = sqlite3_step(pIter->pSelect); + if( rc==SQLITE_ROW ){ + p->nStep++; + return otaStep(p); } - continue; + p->rc = sqlite3_reset(pIter->pSelect); + p->nStep = 0; } } - /* Update the target database PK table according to the row that - ** tbliter.pSelect currently points to. - ** - ** todo: For now, we assume all rows are INSERT commands - this will - ** change. */ - if( p->rc==SQLITE_OK ){ - sqlite3_stmt *pInsert; - int nCol; - if( p->idxiter.zIndex ){ - pInsert = p->idxiter.pWriter; - nCol = p->idxiter.nCol; - }else{ - pInsert = p->tbliter.pInsert; - nCol = p->tbliter.nCol; - } - - for(i=0; irc==SQLITE_OK && p->tbliter.zTarget==0 ) p->rc = SQLITE_DONE; + if( p->rc==SQLITE_OK && pIter->zTbl==0 ){ + p->rc = SQLITE_DONE; + } } - - return (p ? p->rc : SQLITE_NOMEM); + return p->rc; } static void otaOpenDatabase(sqlite3ota *p, sqlite3 **pDb, const char *zFile){ @@ -514,15 +545,14 @@ static void otaOpenDatabase(sqlite3ota *p, sqlite3 **pDb, const char *zFile){ } static void otaSaveTransactionState(sqlite3ota *p){ - sqlite3_stmt *pSelect; char *zInsert; - pSelect = (p->idxiter.zIndex ? p->idxiter.pSelect : p->tbliter.pSelect); zInsert = sqlite3_mprintf( "INSERT OR REPLACE INTO ota_state(rowid, tbl, idx, row, progress)" - "VALUES(1, %Q, %Q, %lld, NULL)", - p->tbliter.zTarget, p->idxiter.zIndex, sqlite3_column_int64(pSelect, 0) + "VALUES(1, %Q, %Q, %d, NULL)", + p->objiter.zTbl, p->objiter.zIdx, p->nStep ); + if( zInsert==0 ){ p->rc = SQLITE_NOMEM; }else{ @@ -551,7 +581,7 @@ static OtaState *otaLoadState(sqlite3ota *p){ int rc; assert( p->rc==SQLITE_OK ); - rc = prepareAndCollectError(p->dbOta, zSelect, &pStmt, &p->zErrmsg); + rc = prepareAndCollectError(p->dbOta, &pStmt, &p->zErrmsg, zSelect); if( rc==SQLITE_OK ){ if( sqlite3_step(pStmt)==SQLITE_ROW ){ const char *zIdx = (const char*)sqlite3_column_text(pStmt, 1); @@ -570,7 +600,7 @@ static OtaState *otaLoadState(sqlite3ota *p){ }else{ pRet->zIdx = 0; } - pRet->iRow = sqlite3_column_int64(pStmt, 2); + pRet->nRow = sqlite3_column_int(pStmt, 2); } }else{ pRet = (OtaState*)sqlite3_malloc(sizeof(OtaState)); @@ -590,53 +620,35 @@ static OtaState *otaLoadState(sqlite3ota *p){ return pRet; } +static int otaStrCompare(const char *z1, const char *z2){ + if( z1==0 && z2==0 ) return 0; + if( z1==0 || z2==0 ) return 1; + return (sqlite3_stricmp(z1, z2)!=0); +} + static void otaLoadTransactionState(sqlite3ota *p, OtaState *pState){ assert( p->rc==SQLITE_OK ); if( pState->zTbl ){ + OtaObjIter *pIter = &p->objiter; int rc; - while( rc==SQLITE_OK - && p->tbliter.zTarget - && sqlite3_stricmp(p->tbliter.zTarget, pState->zTbl) - ){ - rc = tblIterNext(&p->tbliter); + + while( rc==SQLITE_OK && pIter->zTbl && (pIter->bCleanup + || otaStrCompare(pIter->zTbl, pState->zTbl) + || otaStrCompare(pIter->zIdx, pState->zIdx) + )){ + rc = otaObjIterNext(p, &p->objiter); } - if( rc==SQLITE_OK && !p->tbliter.zTarget ){ + + if( rc==SQLITE_OK && !p->objiter.zTbl ){ rc = SQLITE_ERROR; p->zErrmsg = sqlite3_mprintf("ota_state mismatch error"); } - if( rc==SQLITE_OK && pState->zIdx ){ - rc = idxIterFirst(p->dbDest, p->tbliter.zTarget, &p->idxiter); - while( rc==SQLITE_OK - && p->idxiter.zIndex - && sqlite3_stricmp(p->idxiter.zIndex, pState->zIdx) - ){ - rc = idxIterNext(&p->idxiter); - } - if( rc==SQLITE_OK && !p->idxiter.zIndex ){ - rc = SQLITE_ERROR; - p->zErrmsg = sqlite3_mprintf("ota_state mismatch error"); - } - } - if( rc==SQLITE_OK ){ - rc = otaPrepareAll(p); + p->nStep = pState->nRow; + rc = otaObjIterPrepareAll(p, &p->objiter, p->nStep); } - if( rc==SQLITE_OK ){ - sqlite3_stmt *pSelect; - pSelect = (p->idxiter.zIndex ? p->idxiter.pSelect : p->tbliter.pSelect); - while( sqlite3_column_int64(pSelect, 0)!=pState->iRow ){ - rc = sqlite3_step(pSelect); - if( rc!=SQLITE_ROW ) break; - } - if( rc==SQLITE_ROW ){ - rc = SQLITE_OK; - }else{ - rc = SQLITE_ERROR; - p->zErrmsg = sqlite3_mprintf("ota_state mismatch error"); - } - } p->rc = rc; } } @@ -703,7 +715,6 @@ sqlite3ota *sqlite3ota_open(const char *zTarget, const char *zOta){ } } - if( p->rc==SQLITE_OK ){ const char *zScript = "PRAGMA journal_mode=off;" @@ -719,9 +730,9 @@ sqlite3ota *sqlite3ota_open(const char *zTarget, const char *zOta){ p->rc = sqlite3_exec(p->dbOta, zScript, 0, 0, &p->zErrmsg); } - /* Point the table iterator at the first table */ + /* Point the object iterator at the first object */ if( p->rc==SQLITE_OK ){ - p->rc = tblIterFirst(p->dbOta, &p->tbliter); + p->rc = otaObjIterFirst(p, &p->objiter); } if( p->rc==SQLITE_OK ){ @@ -734,11 +745,9 @@ sqlite3ota *sqlite3ota_open(const char *zTarget, const char *zOta){ return p; } -static void otaCloseHandle(sqlite3 *db){ - int rc = sqlite3_close(db); - assert( rc==SQLITE_OK ); -} - +/* +** Close the OTA handle. +*/ int sqlite3ota_close(sqlite3ota *p, char **pzErrmsg){ int rc; if( p ){ @@ -753,17 +762,23 @@ int sqlite3ota_close(sqlite3ota *p, char **pzErrmsg){ } /* Close all open statement handles. */ - tblIterFinalize(&p->tbliter); - idxIterFinalize(&p->idxiter); + otaObjIterFinalize(&p->objiter); /* Commit the transaction to the *-oal file. */ if( p->rc==SQLITE_OK || p->rc==SQLITE_DONE ){ rc = sqlite3_exec(p->dbDest, "COMMIT", 0, 0, &p->zErrmsg); if( rc!=SQLITE_OK ) p->rc = rc; } - otaCloseHandle(p->dbDest); - otaCloseHandle(p->dbOta); + assert( sqlite3_next_stmt(p->dbDest, 0)==0 ); + assert( sqlite3_next_stmt(p->dbOta, 0)==0 ); + + /* Close the open database handles */ + sqlite3_close(p->dbDest); + sqlite3_close(p->dbOta); + + /* If the OTA has been completely applied and no error occurred, move + ** the *-oal file to *-wal. */ if( p->rc==SQLITE_DONE ){ otaMoveOalFile(p); } diff --git a/manifest b/manifest index 5c9d321e28..6cfb5e1441 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Avoid\sever\srunning\sa\scheckpoint\sin\sota\smode. -D 2014-09-04T19:05:31.658 +C Reorganize\sthe\scode\sin\ssqlite3ota.c\sin\spreparation\sfor\sadding\ssupport\sfor\supdate\sand\sdelete\soperations. +D 2014-09-05T19:31:15.782 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in cf57f673d77606ab0f2d9627ca52a9ba1464146a F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -122,9 +122,9 @@ F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95 F ext/misc/vtshim.c babb0dc2bf116029e3e7c9a618b8a1377045303e F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212 F ext/ota/ota.c d37097e92a005d3915883adefbb93019ea6f8841 -F ext/ota/ota1.test ea2865997ce573fadaf12eb0a0f80ef22d9dd77f +F ext/ota/ota1.test 0bbdffa5cb4c4bc26be5dae55c834830c7e8e5e3 F ext/ota/ota2.test 13f76922446c62ed96192e938b8e625ebf0142fa -F ext/ota/sqlite3ota.c 3e05e3fa5791977eb88261731a6be6d98935efb3 +F ext/ota/sqlite3ota.c c400c9e9ef188cedb9bada263145aaad47d90e75 F ext/ota/sqlite3ota.h 545f0008b5f02f2595899cb9841caddada5c17c0 F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761 F ext/rtree/rtree.c 57bec53e1a677ab74217fe1f20a58c3a47261d6b @@ -1198,7 +1198,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P fc4f7c115207b786e3cac8cb6be4db3131b99a46 -R 43c0251327b6a5ac610420013b39a3a5 +P 9ae44447256b425b5704a1cab3f6796befb92251 +R f29d93e4faa0368c9015e32b108d56ba U dan -Z a71ca63f1bbe8e3a7e1c7cbaa054e55c +Z f9daeef70873e6c36444c084d50eac94 diff --git a/manifest.uuid b/manifest.uuid index 5250b4a025..71abb619b3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9ae44447256b425b5704a1cab3f6796befb92251 \ No newline at end of file +98387f05697526c7740e91d8a846a31f77639406 \ No newline at end of file -- 2.47.2