Avoid a linear scan of the entire table when ota updates or deletes a row from a table with an external primary key index.
FossilOrigin-Name: 1db198ccca1e5c5a922cefe3daeff8d2e5d3a7f7
This commit is contained in:
parent
1e35850229
commit
738a3b2fcb
@ -118,7 +118,8 @@ struct OtaObjIter {
|
||||
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 */
|
||||
|
||||
@ -320,7 +321,7 @@ static int otaObjIterNext(sqlite3ota *p, OtaObjIter *pIter){
|
||||
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{
|
||||
@ -334,7 +335,7 @@ 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->iTnum = sqlite3_column_int(pIter->pIdxIter, 1);
|
||||
pIter->bUnique = sqlite3_column_int(pIter->pIdxIter, 2);
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
@ -470,7 +471,8 @@ static int otaObjIterCacheTableInfo(sqlite3ota *p, OtaObjIter *pIter){
|
||||
/* 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
|
||||
@ -761,6 +763,19 @@ static char *otaObjIterGetWhere(
|
||||
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; i<pIter->nTblCol; 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;
|
||||
@ -879,6 +894,75 @@ static char *otaWithoutRowidPK(sqlite3ota *p, OtaObjIter *pIter){
|
||||
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
|
||||
@ -905,11 +989,13 @@ static char *otaWithoutRowidPK(sqlite3ota *p, OtaObjIter *pIter){
|
||||
** 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;
|
||||
@ -966,7 +1052,7 @@ static int otaObjIterPrepareAll(
|
||||
){
|
||||
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;
|
||||
@ -1067,6 +1153,7 @@ static int otaObjIterPrepareAll(
|
||||
|
||||
/* 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 */
|
||||
@ -1941,7 +2028,6 @@ 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;
|
||||
}
|
||||
|
14
manifest
14
manifest
@ -1,5 +1,5 @@
|
||||
C Fix\sa\smemory\sleak\sthat\scould\sfollow\san\sOOM\serror\sin\sota.
|
||||
D 2015-02-04T11:08:47.037
|
||||
C Avoid\sa\slinear\sscan\sof\sthe\sentire\stable\swhen\sota\supdates\sor\sdeletes\sa\srow\sfrom\sa\stable\swith\san\sexternal\sprimary\skey\sindex.
|
||||
D 2015-02-04T16:32:47.267
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in 5407a688f4d77a05c18a8142be8ae5a2829dd610
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@ -136,7 +136,7 @@ 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 d8910eb0f88d8641fd14c3aa7497d06cb9007745
|
||||
F ext/ota/sqlite3ota.c 8d1f7f1643b85bd442cc99a919b1bbbac5b1d785
|
||||
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 55d548a2c2f32d27366968c394d091475f7ea00a
|
||||
F src/main.c 893ca8955539f5dcff8c798b508e94a6d08b6d99
|
||||
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 a21fefb79a161e6bb057ae4f6ba554f659706ac1
|
||||
R d3231c518b6e7b2eec719e82b7a1a42f
|
||||
P 0d5415f26427d0af89663c5a3e944e488143b648
|
||||
R f9a2d13b5d717bd34c88d8b13812d5f5
|
||||
U dan
|
||||
Z 7434d64dd874abe2fbb928ef046bb349
|
||||
Z 54797f4495fbc966460447f60ea02156
|
||||
|
@ -1 +1 @@
|
||||
0d5415f26427d0af89663c5a3e944e488143b648
|
||||
1db198ccca1e5c5a922cefe3daeff8d2e5d3a7f7
|
18
src/main.c
18
src/main.c
@ -3655,7 +3655,7 @@ int sqlite3_test_control(int op, ...){
|
||||
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
|
||||
@ -3667,13 +3667,22 @@ int sqlite3_test_control(int op, ...){
|
||||
** 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);
|
||||
@ -3687,7 +3696,12 @@ int sqlite3_test_control(int op, ...){
|
||||
*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);
|
||||
|
Loading…
Reference in New Issue
Block a user