mirror of https://github.com/sqlite/sqlite
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
This commit is contained in:
parent
161a6845f9
commit
3b660d7da3
|
@ -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
|
||||
|
|
|
@ -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; i<pIter->nTblCol; 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 = <idxname>" 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; i<pIter->nTblCol && pIter->abTblPk[i]==0; i++);
|
||||
assert( i<pIter->nTblCol );
|
||||
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 index writers */
|
||||
/* 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 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; i<pIter->nCol; i++){
|
||||
for(i=0; p->rc==SQLITE_OK && i<pIter->nCol; 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;
|
||||
}
|
||||
|
|
18
manifest
18
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
|
||||
|
|
|
@ -1 +1 @@
|
|||
f9b6dc77021ee421bffd5697d5d337d3bbd07eb9
|
||||
cdaeab467f6aa3217be161377a9b78a4eec37093
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue