Index: ext/ota/sqlite3ota.c ================================================================== --- ext/ota/sqlite3ota.c +++ ext/ota/sqlite3ota.c @@ -116,11 +116,12 @@ /* 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 iTnum; /* Root page of current object */ + int iPkTnum; /* If eType==EXTERNAL, root of PK index */ int bUnique; /* Current index is unique */ int iVisit; /* Number of points visited, incl. current */ /* Statements created by otaObjIterPrepareAll() */ int nCol; /* Number of columns in current object */ @@ -318,11 +319,11 @@ 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); + pIter->iTnum = sqlite3_column_int(pIter->pTblIter, 1); rc = SQLITE_OK; } }else{ if( pIter->zIdx==0 ){ sqlite3_bind_text(pIter->pIdxIter, 1, pIter->zTbl, -1, SQLITE_STATIC); @@ -332,11 +333,11 @@ 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->iTnum = sqlite3_column_int(pIter->pIdxIter, 1); pIter->bUnique = sqlite3_column_int(pIter->pIdxIter, 2); rc = SQLITE_OK; } } } @@ -468,11 +469,12 @@ 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 + SQLITE_TESTCTRL_TBLTYPE, p->db, "main", pIter->zTbl, &pIter->eType, + &pIter->iPkTnum ); 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 ); @@ -759,10 +761,23 @@ OtaObjIter *pIter ){ char *zList = 0; if( pIter->eType==OTA_PK_VTAB || pIter->eType==OTA_PK_NONE ){ zList = otaMPrintf(p, "_rowid_ = ?%d", pIter->nTblCol+1); + }else if( pIter->eType==OTA_PK_EXTERNAL ){ + const char *zSep = ""; + int i; + for(i=0; inTblCol; i++){ + if( pIter->abTblPk[i] ){ + zList = otaMPrintf(p, "%z%sc%d=?%d", zList, zSep, i, i+1); + zSep = " AND "; + } + } + zList = otaMPrintf(p, + "_rowid_ = (SELECT id FROM ota_imposter2 WHERE %z)", zList + ); + }else{ const char *zSep = ""; int i; for(i=0; inTblCol; i++){ if( pIter->abTblPk[i] ){ @@ -876,10 +891,79 @@ rc = sqlite3_finalize(pXInfo); if( p->rc==SQLITE_OK ) p->rc = rc; } return z; } + +static void otaCreateImposterTable2(sqlite3ota *p, OtaObjIter *pIter){ + if( p->rc==SQLITE_OK && pIter->eType==OTA_PK_EXTERNAL ){ + int tnum = pIter->iPkTnum; /* Root page of PK index */ + sqlite3_stmt *pQuery = 0; /* SELECT name ... WHERE rootpage = $tnum */ + const char *zIdx = 0; /* Name of PK index */ + sqlite3_stmt *pXInfo = 0; /* PRAGMA main.index_xinfo = $zIdx */ + int rc; + + const char *zComma = ""; + + char *zCols = 0; /* Used to build up list of table cols */ + char *zPk = 0; /* Used to build up table PK declaration */ + char *zSql = 0; /* CREATE TABLE statement */ + + /* Figure out the name of the primary key index for the current table. + ** This is needed for the argument to "PRAGMA index_xinfo". Set + ** zIdx to point to a nul-terminated string containing this name. */ + p->rc = prepareAndCollectError(p->db, &pQuery, &p->zErrmsg, + "SELECT name FROM sqlite_master WHERE rootpage = ?" + ); + if( p->rc==SQLITE_OK ){ + sqlite3_bind_int(pQuery, 1, tnum); + if( SQLITE_ROW==sqlite3_step(pQuery) ){ + zIdx = (const char*)sqlite3_column_text(pQuery, 0); + } + if( zIdx==0 ){ + p->rc = SQLITE_CORRUPT; + } + } + assert( (zIdx==0)==(p->rc!=SQLITE_OK) ); + + if( p->rc==SQLITE_OK ){ + p->rc = prepareFreeAndCollectError(p->db, &pXInfo, &p->zErrmsg, + sqlite3_mprintf("PRAGMA main.index_xinfo = %Q", zIdx) + ); + } + sqlite3_finalize(pQuery); + + while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXInfo) ){ + int bKey = sqlite3_column_int(pXInfo, 5); + if( bKey ){ + int iCid = sqlite3_column_int(pXInfo, 1); + int bDesc = sqlite3_column_int(pXInfo, 3); + const char *zCollate = (const char*)sqlite3_column_text(pXInfo, 4); + zCols = otaMPrintf(p, "%z%sc%d %s COLLATE %s", zCols, zComma, + iCid, pIter->azTblType[iCid], zCollate + ); + zPk = otaMPrintf(p, "%z%sc%d%s", zPk, zComma, iCid, bDesc?" DESC":""); + zComma = ", "; + } + } + zCols = otaMPrintf(p, "%z, id INTEGER", zCols); + rc = sqlite3_finalize(pXInfo); + if( p->rc==SQLITE_OK ) p->rc = rc; + + zSql = otaMPrintf(p, + "CREATE TABLE ota_imposter2(%z, PRIMARY KEY(%z)) WITHOUT ROWID", + zCols, zPk + ); + assert( (zSql==0)==(p->rc!=SQLITE_OK) ); + if( zSql ){ + 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); + } +} /* ** 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 @@ -903,15 +987,17 @@ ** ** OTA_PK_VTAB: ** No imposters required. ** ** OTA_PK_EXTERNAL: -** Two imposters are required (TODO!!) +** Two imposters are required. The first has the same schema as the +** target database table, with no PRIMARY KEY or UNIQUE clauses. The +** second is used to access the PK b-tree index on disk. */ static void otaCreateImposterTable(sqlite3ota *p, OtaObjIter *pIter){ if( p->rc==SQLITE_OK && pIter->eType!=OTA_PK_VTAB ){ - int tnum = pIter->tnum; + int tnum = pIter->iTnum; const char *zComma = ""; char *zSql = 0; int iCol; sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 1); @@ -964,11 +1050,11 @@ OtaObjIter *pIter, int nOffset /* Add "LIMIT -1 OFFSET $nOffset" to SELECT */ ){ assert( pIter->bCleanup==0 ); if( pIter->pSelect==0 && otaObjIterCacheTableInfo(p, pIter)==SQLITE_OK ){ - const int tnum = pIter->tnum; + const int tnum = pIter->iTnum; char *zCollist = 0; /* List of indexed columns */ char **pz = &p->zErrmsg; const char *zIdx = pIter->zIdx; char *zLimit = 0; @@ -1065,10 +1151,11 @@ ); } /* Create the imposter table or tables (if required). */ otaCreateImposterTable(p, pIter); + otaCreateImposterTable2(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, @@ -1939,11 +2026,10 @@ sqlite3 *db = sqlite3ota_db(pOta); int rc = sqlite3_create_function( 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; } default: /* seems unlikely */ Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -3653,11 +3653,11 @@ } sqlite3_mutex_leave(db->mutex); break; } - /* sqlite3_test_control(SQLITE_TESTCTRL_TBLTYPE, db, dbName, zTbl, peType) + /* sqlite3_test_control(TESTCTRL_TBLTYPE, db, dbName, zTbl, peType, piPk) ** ** peType is of type (int*), a pointer to an output parameter of type ** (int). This call sets the output parameter as follows, depending ** on the type of the table specified by parameters dbName and zTbl. ** @@ -3665,17 +3665,26 @@ ** 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. + ** + ** Argument *piPk is also of type (int*), and also points to an output + ** parameter. Unless the table has an external primary key index + ** (i.e. unless *peType is set to 3), then *piPk is set to zero. Or, + ** if the table does have an external primary key index, then *piPk + ** is set to the root page number of the primary key index before + ** returning. */ case SQLITE_TESTCTRL_TBLTYPE: { sqlite3 *db = va_arg(ap, sqlite3*); const char *zDb = va_arg(ap, const char*); const char *zTab = va_arg(ap, const char*); int *peType = va_arg(ap, int*); + int *piPk = va_arg(ap, int*); Table *pTab; + *piPk = 0; sqlite3_mutex_enter(db->mutex); sqlite3BtreeEnterAll(db); pTab = sqlite3FindTable(db, zTab, zDb); if( pTab==0 ){ *peType = 0; @@ -3685,11 +3694,16 @@ *peType = 4; }else if( pTab->iPKey>=0 ){ *peType = 2; }else{ Index *pPk = sqlite3PrimaryKeyIndex(pTab); - *peType = (pPk ? 3 : 1); + if( pPk ){ + *peType = 3; + *piPk = pPk->tnum; + }else{ + *peType = 1; + } } sqlite3BtreeLeaveAll(db); sqlite3_mutex_leave(db->mutex); break; }