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:
dan 2015-01-31 20:42:04 +00:00
parent 161a6845f9
commit 3b660d7da3
5 changed files with 190 additions and 47 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -1 +1 @@
f9b6dc77021ee421bffd5697d5d337d3bbd07eb9
cdaeab467f6aa3217be161377a9b78a4eec37093

View File

@ -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;
}
}