From: dan Date: Sat, 31 Jan 2015 20:42:04 +0000 (+0000) Subject: Have ota use imposter tables to write to indexes instead of the sqlite3_index_writer... X-Git-Tag: version-3.8.11~252^2~60 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3b660d7da3faf36d4bbe6537cd13b7911590741b;p=thirdparty%2Fsqlite.git Have ota use imposter tables to write to indexes instead of the sqlite3_index_writer() interface. The error handling in this version is broken in a few small ways. FossilOrigin-Name: cdaeab467f6aa3217be161377a9b78a4eec37093 --- diff --git a/ext/ota/otafault.test b/ext/ota/otafault.test index 915d6448d2..5c641dc675 100644 --- a/ext/ota/otafault.test +++ b/ext/ota/otafault.test @@ -64,7 +64,6 @@ do_faultsim_test 2 -faults oom-trans* -prep { {1 SQLITE_NOMEM} \ {1 SQLITE_IOERR_NOMEM} \ {1 {SQLITE_NOMEM - unable to open a temporary database file for storing temporary tables}} - if {$testrc==0} { sqlite3 db test.db faultsim_integrity_check diff --git a/ext/ota/sqlite3ota.c b/ext/ota/sqlite3ota.c index a3d73ae67b..1727e51f1f 100644 --- a/ext/ota/sqlite3ota.c +++ b/ext/ota/sqlite3ota.c @@ -96,24 +96,23 @@ struct OtaState { ** ** * the table itself, ** * each index of the table (zero or more points to visit), and -** * a special "cleanup table" point. +** * a special "cleanup table" state. */ 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 */ + char **azTblType; /* Array of column types */ unsigned char *abTblPk; /* Array of flags - true for PK columns */ int eType; -#if 0 - unsigned char bRowid; /* True for implicit IPK tables */ - unsigned char bVtab; /* True for a virtual table */ -#endif /* 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 tnum; /* Root page of index (not table) */ + int bUnique; /* Current index is unique */ int iVisit; /* Number of points visited, incl. current */ /* Statements created by otaObjIterPrepareAll() */ @@ -232,9 +231,11 @@ static void otaObjIterFreeCols(OtaObjIter *pIter){ int i; for(i=0; inTblCol; i++){ sqlite3_free(pIter->azTblCol[i]); + sqlite3_free(pIter->azTblType[i]); } sqlite3_free(pIter->azTblCol); pIter->azTblCol = 0; + pIter->azTblType = 0; pIter->abTblPk = 0; pIter->nTblCol = 0; sqlite3_free(pIter->zMask); @@ -307,6 +308,8 @@ static int otaObjIterNext(sqlite3ota *p, OtaObjIter *pIter){ 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; } } @@ -339,8 +342,9 @@ static int otaObjIterFirst(sqlite3ota *p, OtaObjIter *pIter){ if( rc==SQLITE_OK ){ rc = prepareAndCollectError(p->db, &pIter->pIdxIter, &p->zErrmsg, - "SELECT name FROM main.sqlite_master " - "WHERE type='index' AND tbl_name = ?" + "SELECT name, rootpage, sql IS NULL OR substr(8, 6)=='UNIQUE' " + " FROM main.sqlite_master " + " WHERE type='index' AND tbl_name = ?" ); } @@ -428,7 +432,7 @@ static int otaMPrintfExec(sqlite3ota *p, const char *zFmt, ...){ ** 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 + sizeof(unsigned char*) * nCol; + int nByte = sizeof(char*) * nCol * 2 + sizeof(unsigned char*) * nCol; char **azNew; assert( p->rc==SQLITE_OK ); @@ -436,12 +440,32 @@ static void otaAllocateIterArrays(sqlite3ota *p, OtaObjIter *pIter, int nCol){ if( azNew ){ memset(azNew, 0, nByte); pIter->azTblCol = azNew; - pIter->abTblPk = (unsigned char*)&pIter->azTblCol[nCol]; + pIter->azTblType = &azNew[nCol]; + pIter->abTblPk = (unsigned char*)&pIter->azTblType[nCol]; }else{ p->rc = SQLITE_NOMEM; } } +static char *otaStrndup(const char *zStr, int nStr, int *pRc){ + char *zRet = 0; + assert( *pRc==SQLITE_OK ); + + if( zStr ){ + int nCopy = nStr; + if( nCopy<0 ) nCopy = strlen(zStr) + 1; + zRet = (char*)sqlite3_malloc(nCopy); + if( zRet ){ + memcpy(zRet, zStr, nCopy); + }else{ + *pRc = SQLITE_NOMEM; + } + } + + return zRet; +} + + /* ** Return true if zTab is the name of a virtual table within the target ** database. @@ -531,6 +555,8 @@ static int otaObjIterGetCols(sqlite3ota *p, OtaObjIter *pIter){ ); }else{ int iPk = sqlite3_column_int(pStmt, 5); + const char *zType = (const char*)sqlite3_column_text(pStmt, 2); + 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; @@ -644,6 +670,115 @@ static char *otaObjIterGetCollist( return zList; } +/* +** This function is used to create a SELECT list (the list of SQL +** expressions that follows a SELECT keyword) for a SELECT statement +** used to read from an ota_xxx table while updating the index object +** currently indicated by the iterator object passed as the second +** argument. A "PRAGMA index_xinfo = " statement is used to +** obtain the required information. +** +** If the index is of the following form: +** +** CREATE INDEX i1 ON t1(c, b COLLATE nocase); +** +** and "t1" is a table with an explicit INTEGER PRIMARY KEY column +** "ipk", the returned string is: +** +** "`c` COLLATE 'BINARY', `b` COLLATE 'NOCASE', `ipk` COLLATE 'BINARY'" +** +** As well as the returned string, three other malloc'd strings are +** returned via output parameters. As follows: +** +** pzImposterCols: ... +** pzImposterPk: ... +** pzWhere: ... +*/ +static char *otaObjIterGetIndexCols( + sqlite3ota *p, /* OTA object */ + OtaObjIter *pIter, /* Object iterator for column names */ + char **pzImposterCols, /* OUT: Columns for imposter table */ + char **pzImposterPk, /* OUT: Imposter PK clause */ + char **pzWhere, /* OUT: WHERE clause */ + int *pnBind /* OUT: Total number of columns */ +){ + int rc = p->rc; /* Error code */ + int rc2; /* sqlite3_finalize() return code */ + char *zRet = 0; /* String to return */ + char *zImpCols = 0; /* String to return via *pzImposterCols */ + 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 *zAnd = ""; /* Set to " AND " later on */ + sqlite3_stmt *pXInfo = 0; /* PRAGMA index_xinfo = ? */ + + if( rc==SQLITE_OK ){ + assert( p->zErrmsg==0 ); + rc = prepareFreeAndCollectError(p->db, &pXInfo, &p->zErrmsg, + sqlite3_mprintf("PRAGMA main.index_xinfo = %Q", pIter->zIdx) + ); + } + + while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXInfo) ){ + const char *zCollate = (const char*)sqlite3_column_text(pXInfo, 4); + int iCid = sqlite3_column_int(pXInfo, 1); + const char *zCol; + const char *zType; + + 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 ){ + int i; + for(i=0; inTblCol && pIter->abTblPk[i]==0; i++); + assert( inTblCol ); + zCol = pIter->azTblCol[i]; + }else{ + zCol = "ota_rowid"; + } + zType = "INTEGER"; + }else{ + zCol = pIter->azTblCol[iCid]; + zType = pIter->azTblType[iCid]; + } + + zRet = sqlite3_mprintf("%z%s%s COLLATE %Q", zRet, zComma, zCol, zCollate); + if( pIter->bUnique==0 || sqlite3_column_int(pXInfo, 5) ){ + zImpPK = sqlite3_mprintf("%z%sc%d", zImpPK, zComma, nBind); + } + zImpCols = sqlite3_mprintf( + "%z%sc%d %s COLLATE %Q", zImpCols, zComma, 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 = ", "; + zAnd = " AND "; + nBind++; + } + + rc2 = sqlite3_finalize(pXInfo); + if( rc==SQLITE_OK ) rc = rc2; + + if( rc!=SQLITE_OK ){ + sqlite3_free(zRet); + sqlite3_free(zImpCols); + sqlite3_free(zImpPK); + sqlite3_free(zWhere); + zRet = 0; + zImpCols = 0; + zImpPK = 0; + zWhere = 0; + p->rc = rc; + } + + *pzImposterCols = zImpCols; + *pzImposterPk = zImpPK; + *pzWhere = zWhere; + *pnBind = nBind; + return zRet; +} + /* ** Assuming the current table columns are "a", "b" and "c", and the zObj ** paramter is passed "old", return a string of the form: @@ -792,6 +927,7 @@ static int otaObjIterPrepareAll( ){ assert( pIter->bCleanup==0 ); if( pIter->pSelect==0 && otaObjIterGetCols(p, pIter)==SQLITE_OK ){ + const int tnum = pIter->tnum; char *zCollist = 0; /* List of indexed columns */ char **pz = &p->zErrmsg; const char *zIdx = pIter->zIdx; @@ -803,25 +939,43 @@ static int otaObjIterPrepareAll( } if( zIdx ){ - int *aiCol; /* Column map */ - const char **azColl; /* Collation sequences */ + char *zImposterCols = 0; + char *zImposterPK = 0; + char *zWhere = 0; + char *zBind = 0; + int nBind = 0; assert( pIter->eType!=OTA_PK_VTAB ); + zCollist = otaObjIterGetIndexCols( + p, pIter, &zImposterCols, &zImposterPK, &zWhere, &nBind + ); + zBind = otaObjIterGetBindlist(p, nBind); + + /* Create the imposter table used to write to this index. */ + sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 1); + sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 1, tnum); + otaMPrintfExec(p, + "CREATE TABLE ota_imposter( %s, PRIMARY KEY( %s ) ) WITHOUT ROWID", + zImposterCols, zImposterPK + ); + sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 0); - /* Create the index writers */ + /* Create the statement to insert index entries */ + pIter->nCol = nBind; if( p->rc==SQLITE_OK ){ - p->rc = sqlite3_index_writer( - p->db, 0, zIdx, &pIter->pInsert, &azColl, &aiCol, &pIter->nCol + p->rc = prepareFreeAndCollectError(p->db, &pIter->pInsert, &p->zErrmsg, + sqlite3_mprintf("INSERT INTO ota_imposter VALUES(%s)", zBind) ); } + + /* And to delete index entries */ if( p->rc==SQLITE_OK ){ - p->rc = sqlite3_index_writer( - p->db, 1, zIdx, &pIter->pDelete, &azColl, &aiCol, &pIter->nCol + p->rc = prepareFreeAndCollectError(p->db, &pIter->pDelete, &p->zErrmsg, + sqlite3_mprintf("DELETE FROM ota_imposter WHERE %s", zWhere) ); } /* Create the SELECT statement to read keys in sorted order */ - zCollist = otaObjIterGetCollist(p, pIter, pIter->nCol, aiCol, azColl); if( p->rc==SQLITE_OK ){ char *zSql; if( pIter->eType==OTA_PK_EXTERNAL || pIter->eType==OTA_PK_NONE ){ @@ -844,6 +998,11 @@ static int otaObjIterPrepareAll( } p->rc = prepareFreeAndCollectError(p->db, &pIter->pSelect, pz, zSql); } + + sqlite3_free(zImposterCols); + sqlite3_free(zImposterPK); + sqlite3_free(zWhere); + sqlite3_free(zBind); }else{ int bOtaRowid = (pIter->eType==OTA_PK_VTAB || pIter->eType==OTA_PK_NONE); const char *zTbl = pIter->zTbl; @@ -1205,7 +1364,7 @@ static int otaStep(sqlite3ota *p){ sqlite3_stmt *pUpdate = 0; otaGetUpdateStmt(p, pIter, zMask, &pUpdate); if( pUpdate ){ - for(i=0; inCol; i++){ + for(i=0; p->rc==SQLITE_OK && inCol; i++){ pVal = sqlite3_column_value(pIter->pSelect, i); sqlite3_bind_value(pUpdate, i+1, pVal); } @@ -1375,24 +1534,6 @@ static void otaSaveTransactionState(sqlite3ota *p){ } } -static char *otaStrndup(char *zStr, int nStr, int *pRc){ - char *zRet = 0; - assert( *pRc==SQLITE_OK ); - - if( zStr ){ - int nCopy = nStr; - if( nCopy<0 ) nCopy = strlen(zStr) + 1; - zRet = (char*)sqlite3_malloc(nCopy); - if( zRet ){ - memcpy(zRet, zStr, nCopy); - }else{ - *pRc = SQLITE_NOMEM; - } - } - - return zRet; -} - static void otaFreeState(OtaState *p){ if( p ){ sqlite3_free(p->zTbl); @@ -1740,6 +1881,7 @@ static int test_sqlite3ota_cmd( db, "ota_delta", -1, SQLITE_UTF8, (void*)interp, test_ota_delta, 0, 0 ); Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); + sqlite3_exec(db, "PRAGMA vdbe_trace = 1", 0, 0, 0); ret = (rc==SQLITE_OK ? TCL_OK : TCL_ERROR); break; } diff --git a/manifest b/manifest index 109bbd6537..3c7bd57c80 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sin\ssupport\sfor\sthe\sindex_xinfo\spragma. -D 2015-01-31T02:34:23.039 +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 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 5407a688f4d77a05c18a8142be8ae5a2829dd610 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -135,8 +135,8 @@ 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 be02466863015a583cc0ceb6aca871a5e6f7a71b -F ext/ota/sqlite3ota.c 84cab0f965144772068ec0183252ae5e5278f0be +F ext/ota/otafault.test 508ba87c83d632670ac0f94371a465d4bb4d49dd +F ext/ota/sqlite3ota.c 975ccfe032ee81ee39368ed5e9cb33cbb6edc603 F ext/ota/sqlite3ota.h ce378c0c503f625611713133f9c79704ea4ee7a4 F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761 F ext/rtree/rtree.c 14e6239434d4e3f65d3e90320713f26aa24e167f @@ -210,7 +210,7 @@ F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d F src/legacy.c ba1863ea58c4c840335a84ec276fc2b25e22bc4e F src/lempar.c 7274c97d24bb46631e504332ccd3bd1b37841770 F src/loadext.c 86bd4e2fccd520b748cba52492ab60c4a770f660 -F src/main.c d6c5fd51a719fcdcf1cc9ef08349dbd4454cf2f3 +F src/main.c c4cb192ebf0bcc975648ae05ac40bc1f40018c52 F src/malloc.c 740db54387204c9a2eb67c6d98e68b08e9ef4eab F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c abe6ee469b6c5a35c7f22bfeb9c9bac664a1c987 @@ -1254,7 +1254,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 3ed6eb2fab5d95709ef392170339e6dd5ba13971 30f51d7b3b292191e8351223242e708bb7f3dfa6 -R 50758dde054ce12d446bde117108ee7b -U drh -Z ec35f224c29e962e36a2b69233e35843 +P f9b6dc77021ee421bffd5697d5d337d3bbd07eb9 +R 907483269529ba471a525b7f8d05bc2e +U dan +Z 87f206a3330dd276f74650f9e68af8ed diff --git a/manifest.uuid b/manifest.uuid index 6038dc4c5d..ecf8a3c6bb 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f9b6dc77021ee421bffd5697d5d337d3bbd07eb9 \ No newline at end of file +cdaeab467f6aa3217be161377a9b78a4eec37093 \ No newline at end of file diff --git a/src/main.c b/src/main.c index 6ccc6d68f6..7bca1e5777 100644 --- a/src/main.c +++ b/src/main.c @@ -3644,12 +3644,14 @@ int sqlite3_test_control(int op, ...){ */ case SQLITE_TESTCTRL_IMPOSTER: { sqlite3 *db = va_arg(ap, sqlite3*); + sqlite3_mutex_enter(db->mutex); db->init.iDb = sqlite3FindDbName(db, va_arg(ap,const char*)); db->init.busy = db->init.imposterTable = va_arg(ap,int); db->init.newTnum = va_arg(ap,int); if( db->init.busy==0 && db->init.newTnum>0 ){ sqlite3ResetAllSchemasOfConnection(db); } + sqlite3_mutex_leave(db->mutex); break; } }