Index: third_party/sqlite/src/src/vtab.c |
diff --git a/third_party/sqlite/src/src/vtab.c b/third_party/sqlite/src/src/vtab.c |
index b052de23a5ff5c7b32511ab5faed98dbb4efcc07..faee4ae4788dd59b5fe99fe695712876a5483872 100644 |
--- a/third_party/sqlite/src/src/vtab.c |
+++ b/third_party/sqlite/src/src/vtab.c |
@@ -15,6 +15,18 @@ |
#include "sqliteInt.h" |
/* |
+** Before a virtual table xCreate() or xConnect() method is invoked, the |
+** sqlite3.pVtabCtx member variable is set to point to an instance of |
+** this struct allocated on the stack. It is used by the implementation of |
+** the sqlite3_declare_vtab() and sqlite3_vtab_config() APIs, both of which |
+** are invoked only from within xCreate and xConnect methods. |
+*/ |
+struct VtabCtx { |
+ VTable *pVTable; /* The virtual table being constructed */ |
+ Table *pTab; /* The Table object to which the virtual table belongs */ |
+}; |
+ |
+/* |
** The actual function that does the work of creating a new module. |
** This function implements the sqlite3_create_module() and |
** sqlite3_create_module_v2() interfaces. |
@@ -26,33 +38,35 @@ static int createModule( |
void *pAux, /* Context pointer for xCreate/xConnect */ |
void (*xDestroy)(void *) /* Module destructor function */ |
){ |
- int rc, nName; |
- Module *pMod; |
+ int rc = SQLITE_OK; |
+ int nName; |
sqlite3_mutex_enter(db->mutex); |
nName = sqlite3Strlen30(zName); |
- pMod = (Module *)sqlite3DbMallocRaw(db, sizeof(Module) + nName + 1); |
- if( pMod ){ |
- Module *pDel; |
- char *zCopy = (char *)(&pMod[1]); |
- memcpy(zCopy, zName, nName+1); |
- pMod->zName = zCopy; |
- pMod->pModule = pModule; |
- pMod->pAux = pAux; |
- pMod->xDestroy = xDestroy; |
- pDel = (Module *)sqlite3HashInsert(&db->aModule, zCopy, nName, (void*)pMod); |
- if( pDel && pDel->xDestroy ){ |
- pDel->xDestroy(pDel->pAux); |
- } |
- sqlite3DbFree(db, pDel); |
- if( pDel==pMod ){ |
- db->mallocFailed = 1; |
+ if( sqlite3HashFind(&db->aModule, zName) ){ |
+ rc = SQLITE_MISUSE_BKPT; |
+ }else{ |
+ Module *pMod; |
+ pMod = (Module *)sqlite3DbMallocRaw(db, sizeof(Module) + nName + 1); |
+ if( pMod ){ |
+ Module *pDel; |
+ char *zCopy = (char *)(&pMod[1]); |
+ memcpy(zCopy, zName, nName+1); |
+ pMod->zName = zCopy; |
+ pMod->pModule = pModule; |
+ pMod->pAux = pAux; |
+ pMod->xDestroy = xDestroy; |
+ pDel = (Module *)sqlite3HashInsert(&db->aModule,zCopy,(void*)pMod); |
+ assert( pDel==0 || pDel==pMod ); |
+ if( pDel ){ |
+ db->mallocFailed = 1; |
+ sqlite3DbFree(db, pDel); |
+ } |
} |
- sqlite3ResetInternalSchema(db, -1); |
- }else if( xDestroy ){ |
- xDestroy(pAux); |
} |
- rc = sqlite3ApiExit(db, SQLITE_OK); |
+ rc = sqlite3ApiExit(db, rc); |
+ if( rc!=SQLITE_OK && xDestroy ) xDestroy(pAux); |
+ |
sqlite3_mutex_leave(db->mutex); |
return rc; |
} |
@@ -117,7 +131,7 @@ void sqlite3VtabUnlock(VTable *pVTab){ |
assert( db ); |
assert( pVTab->nRef>0 ); |
- assert( sqlite3SafetyCheckOk(db) ); |
+ assert( db->magic==SQLITE_MAGIC_OPEN || db->magic==SQLITE_MAGIC_ZOMBIE ); |
pVTab->nRef--; |
if( pVTab->nRef==0 ){ |
@@ -168,6 +182,31 @@ static VTable *vtabDisconnectAll(sqlite3 *db, Table *p){ |
return pRet; |
} |
+/* |
+** Table *p is a virtual table. This function removes the VTable object |
+** for table *p associated with database connection db from the linked |
+** list in p->pVTab. It also decrements the VTable ref count. This is |
+** used when closing database connection db to free all of its VTable |
+** objects without disturbing the rest of the Schema object (which may |
+** be being used by other shared-cache connections). |
+*/ |
+void sqlite3VtabDisconnect(sqlite3 *db, Table *p){ |
+ VTable **ppVTab; |
+ |
+ assert( IsVirtual(p) ); |
+ assert( sqlite3BtreeHoldsAllMutexes(db) ); |
+ assert( sqlite3_mutex_held(db->mutex) ); |
+ |
+ for(ppVTab=&p->pVTable; *ppVTab; ppVTab=&(*ppVTab)->pNext){ |
+ if( (*ppVTab)->db==db ){ |
+ VTable *pVTab = *ppVTab; |
+ *ppVTab = pVTab->pNext; |
+ sqlite3VtabUnlock(pVTab); |
+ break; |
+ } |
+ } |
+} |
+ |
/* |
** Disconnect all the virtual table objects in the sqlite3.pDisconnect list. |
@@ -225,7 +264,7 @@ void sqlite3VtabClear(sqlite3 *db, Table *p){ |
if( p->azModuleArg ){ |
int i; |
for(i=0; i<p->nModuleArg; i++){ |
- sqlite3DbFree(db, p->azModuleArg[i]); |
+ if( i!=1 ) sqlite3DbFree(db, p->azModuleArg[i]); |
} |
sqlite3DbFree(db, p->azModuleArg); |
} |
@@ -266,13 +305,14 @@ void sqlite3VtabBeginParse( |
Parse *pParse, /* Parsing context */ |
Token *pName1, /* Name of new table, or database name */ |
Token *pName2, /* Name of new table or NULL */ |
- Token *pModuleName /* Name of the module for the virtual table */ |
+ Token *pModuleName, /* Name of the module for the virtual table */ |
+ int ifNotExists /* No error if the table already exists */ |
){ |
int iDb; /* The database the table is being created in */ |
Table *pTable; /* The new virtual table */ |
sqlite3 *db; /* Database connection */ |
- sqlite3StartTable(pParse, pName1, pName2, 0, 0, 1, 0); |
+ sqlite3StartTable(pParse, pName1, pName2, 0, 0, 1, ifNotExists); |
pTable = pParse->pNewTable; |
if( pTable==0 ) return; |
assert( 0==pTable->pIndex ); |
@@ -284,7 +324,7 @@ void sqlite3VtabBeginParse( |
pTable->tabFlags |= TF_Virtual; |
pTable->nModuleArg = 0; |
addModuleArgument(db, pTable, sqlite3NameFromToken(db, pModuleName)); |
- addModuleArgument(db, pTable, sqlite3DbStrDup(db, db->aDb[iDb].zName)); |
+ addModuleArgument(db, pTable, 0); |
addModuleArgument(db, pTable, sqlite3DbStrDup(db, pTable->zName)); |
pParse->sNameToken.n = (int)(&pModuleName->z[pModuleName->n] - pName1->z); |
@@ -307,7 +347,7 @@ void sqlite3VtabBeginParse( |
** virtual table currently under construction in pParse->pTable. |
*/ |
static void addArgumentToVtab(Parse *pParse){ |
- if( pParse->sArg.z && ALWAYS(pParse->pNewTable) ){ |
+ if( pParse->sArg.z && pParse->pNewTable ){ |
const char *z = (const char*)pParse->sArg.z; |
int n = pParse->sArg.n; |
sqlite3 *db = pParse->db; |
@@ -371,7 +411,7 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ |
sqlite3VdbeAddOp2(v, OP_Expire, 0, 0); |
zWhere = sqlite3MPrintf(db, "name='%q' AND type='table'", pTab->zName); |
- sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0, zWhere, P4_DYNAMIC); |
+ sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere); |
sqlite3VdbeAddOp4(v, OP_VCreate, iDb, 0, 0, |
pTab->zName, sqlite3Strlen30(pTab->zName) + 1); |
} |
@@ -385,9 +425,8 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ |
Table *pOld; |
Schema *pSchema = pTab->pSchema; |
const char *zName = pTab->zName; |
- int nName = sqlite3Strlen30(zName); |
assert( sqlite3SchemaMutexHeld(db, 0, pSchema) ); |
- pOld = sqlite3HashInsert(&pSchema->tblHash, zName, nName, pTab); |
+ pOld = sqlite3HashInsert(&pSchema->tblHash, zName, pTab); |
if( pOld ){ |
db->mallocFailed = 1; |
assert( pTab==pOld ); /* Malloc must have failed inside HashInsert() */ |
@@ -434,12 +473,14 @@ static int vtabCallConstructor( |
int (*xConstruct)(sqlite3*,void*,int,const char*const*,sqlite3_vtab**,char**), |
char **pzErr |
){ |
+ VtabCtx sCtx, *pPriorCtx; |
VTable *pVTable; |
int rc; |
const char *const*azArg = (const char *const*)pTab->azModuleArg; |
int nArg = pTab->nModuleArg; |
char *zErr = 0; |
char *zModuleName = sqlite3MPrintf(db, "%s", pTab->zName); |
+ int iDb; |
if( !zModuleName ){ |
return SQLITE_NOMEM; |
@@ -453,12 +494,18 @@ static int vtabCallConstructor( |
pVTable->db = db; |
pVTable->pMod = pMod; |
- assert( !db->pVTab ); |
- assert( xConstruct ); |
- db->pVTab = pTab; |
+ iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
+ pTab->azModuleArg[1] = db->aDb[iDb].zName; |
/* Invoke the virtual table constructor */ |
+ assert( &db->pVtabCtx ); |
+ assert( xConstruct ); |
+ sCtx.pTab = pTab; |
+ sCtx.pVTable = pVTable; |
+ pPriorCtx = db->pVtabCtx; |
+ db->pVtabCtx = &sCtx; |
rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr); |
+ db->pVtabCtx = pPriorCtx; |
if( rc==SQLITE_NOMEM ) db->mallocFailed = 1; |
if( SQLITE_OK!=rc ){ |
@@ -472,9 +519,10 @@ static int vtabCallConstructor( |
}else if( ALWAYS(pVTable->pVtab) ){ |
/* Justification of ALWAYS(): A correct vtab constructor must allocate |
** the sqlite3_vtab object if successful. */ |
+ memset(pVTable->pVtab, 0, sizeof(pVTable->pVtab[0])); |
pVTable->pVtab->pModule = pMod->pModule; |
pVTable->nRef = 1; |
- if( db->pVTab ){ |
+ if( sCtx.pTab ){ |
const char *zFormat = "vtable constructor did not declare schema: %s"; |
*pzErr = sqlite3MPrintf(db, zFormat, pTab->zName); |
sqlite3VtabUnlock(pVTable); |
@@ -484,7 +532,7 @@ static int vtabCallConstructor( |
/* If everything went according to plan, link the new VTable structure |
** into the linked list headed by pTab->pVTable. Then loop through the |
** columns of the table to see if any of them contain the token "hidden". |
- ** If so, set the Column.isHidden flag and remove the token from |
+ ** If so, set the Column COLFLAG_HIDDEN flag and remove the token from |
** the type string. */ |
pVTable->pNext = pTab->pVTable; |
pTab->pVTable = pVTable; |
@@ -515,14 +563,13 @@ static int vtabCallConstructor( |
assert(zType[i-1]==' '); |
zType[i-1] = '\0'; |
} |
- pTab->aCol[iCol].isHidden = 1; |
+ pTab->aCol[iCol].colFlags |= COLFLAG_HIDDEN; |
} |
} |
} |
} |
sqlite3DbFree(db, zModuleName); |
- db->pVTab = 0; |
return rc; |
} |
@@ -546,7 +593,7 @@ int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){ |
/* Locate the required virtual table module */ |
zMod = pTab->azModuleArg[0]; |
- pMod = (Module*)sqlite3HashFind(&db->aModule, zMod, sqlite3Strlen30(zMod)); |
+ pMod = (Module*)sqlite3HashFind(&db->aModule, zMod); |
if( !pMod ){ |
const char *zModule = pTab->azModuleArg[0]; |
@@ -563,11 +610,11 @@ int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){ |
return rc; |
} |
- |
/* |
-** Add the virtual table pVTab to the array sqlite3.aVTrans[]. |
+** Grow the db->aVTrans[] array so that there is room for at least one |
+** more v-table. Return SQLITE_NOMEM if a malloc fails, or SQLITE_OK otherwise. |
*/ |
-static int addToVTrans(sqlite3 *db, VTable *pVTab){ |
+static int growVTrans(sqlite3 *db){ |
const int ARRAY_INCR = 5; |
/* Grow the sqlite3.aVTrans array if required */ |
@@ -582,10 +629,17 @@ static int addToVTrans(sqlite3 *db, VTable *pVTab){ |
db->aVTrans = aVTrans; |
} |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** Add the virtual table pVTab to the array sqlite3.aVTrans[]. Space should |
+** have already been reserved using growVTrans(). |
+*/ |
+static void addToVTrans(sqlite3 *db, VTable *pVTab){ |
/* Add pVtab to the end of sqlite3.aVTrans */ |
db->aVTrans[db->nVTrans++] = pVTab; |
sqlite3VtabLock(pVTab); |
- return SQLITE_OK; |
} |
/* |
@@ -607,7 +661,7 @@ int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, char **pzErr){ |
/* Locate the required virtual table module */ |
zMod = pTab->azModuleArg[0]; |
- pMod = (Module*)sqlite3HashFind(&db->aModule, zMod, sqlite3Strlen30(zMod)); |
+ pMod = (Module*)sqlite3HashFind(&db->aModule, zMod); |
/* If the module has been registered and includes a Create method, |
** invoke it now. If the module has not been registered, return an |
@@ -623,7 +677,10 @@ int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, char **pzErr){ |
/* Justification of ALWAYS(): The xConstructor method is required to |
** create a valid sqlite3_vtab if it returns SQLITE_OK. */ |
if( rc==SQLITE_OK && ALWAYS(sqlite3GetVTable(db, pTab)) ){ |
- rc = addToVTrans(db, sqlite3GetVTable(db, pTab)); |
+ rc = growVTrans(db); |
+ if( rc==SQLITE_OK ){ |
+ addToVTrans(db, sqlite3GetVTable(db, pTab)); |
+ } |
} |
return rc; |
@@ -642,9 +699,8 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ |
char *zErr = 0; |
sqlite3_mutex_enter(db->mutex); |
- pTab = db->pVTab; |
- if( !pTab ){ |
- sqlite3Error(db, SQLITE_MISUSE, 0); |
+ if( !db->pVtabCtx || !(pTab = db->pVtabCtx->pTab) ){ |
+ sqlite3Error(db, SQLITE_MISUSE); |
sqlite3_mutex_leave(db->mutex); |
return SQLITE_MISUSE_BKPT; |
} |
@@ -670,9 +726,9 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ |
pParse->pNewTable->nCol = 0; |
pParse->pNewTable->aCol = 0; |
} |
- db->pVTab = 0; |
+ db->pVtabCtx->pTab = 0; |
}else{ |
- sqlite3Error(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr); |
+ sqlite3ErrorWithMsg(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr); |
sqlite3DbFree(db, zErr); |
rc = SQLITE_ERROR; |
} |
@@ -682,6 +738,7 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ |
sqlite3VdbeFinalize(pParse->pVdbe); |
} |
sqlite3DeleteTable(db, pParse->pNewTable); |
+ sqlite3ParserReset(pParse); |
sqlite3StackFree(db, pParse); |
} |
@@ -740,6 +797,7 @@ static void callFinaliser(sqlite3 *db, int offset){ |
x = *(int (**)(sqlite3_vtab *))((char *)p->pModule + offset); |
if( x ) x(p); |
} |
+ pVTab->iSavepoint = 0; |
sqlite3VtabUnlock(pVTab); |
} |
sqlite3DbFree(db, db->aVTrans); |
@@ -753,10 +811,9 @@ static void callFinaliser(sqlite3 *db, int offset){ |
** array. Return the error code for the first error that occurs, or |
** SQLITE_OK if all xSync operations are successful. |
** |
-** Set *pzErrmsg to point to a buffer that should be released using |
-** sqlite3DbFree() containing an error message, if one is available. |
+** If an error message is available, leave it in p->zErrMsg. |
*/ |
-int sqlite3VtabSync(sqlite3 *db, char **pzErrmsg){ |
+int sqlite3VtabSync(sqlite3 *db, Vdbe *p){ |
int i; |
int rc = SQLITE_OK; |
VTable **aVTrans = db->aVTrans; |
@@ -767,9 +824,7 @@ int sqlite3VtabSync(sqlite3 *db, char **pzErrmsg){ |
sqlite3_vtab *pVtab = aVTrans[i]->pVtab; |
if( pVtab && (x = pVtab->pModule->xSync)!=0 ){ |
rc = x(pVtab); |
- sqlite3DbFree(db, *pzErrmsg); |
- *pzErrmsg = sqlite3DbStrDup(db, pVtab->zErrMsg); |
- sqlite3_free(pVtab->zErrMsg); |
+ sqlite3VtabImportErrmsg(p, pVtab); |
} |
} |
db->aVTrans = aVTrans; |
@@ -822,7 +877,6 @@ int sqlite3VtabBegin(sqlite3 *db, VTable *pVTab){ |
if( pModule->xBegin ){ |
int i; |
- |
/* If pVtab is already in the aVTrans array, return early */ |
for(i=0; i<db->nVTrans; i++){ |
if( db->aVTrans[i]==pVTab ){ |
@@ -830,10 +884,62 @@ int sqlite3VtabBegin(sqlite3 *db, VTable *pVTab){ |
} |
} |
- /* Invoke the xBegin method */ |
- rc = pModule->xBegin(pVTab->pVtab); |
+ /* Invoke the xBegin method. If successful, add the vtab to the |
+ ** sqlite3.aVTrans[] array. */ |
+ rc = growVTrans(db); |
if( rc==SQLITE_OK ){ |
- rc = addToVTrans(db, pVTab); |
+ rc = pModule->xBegin(pVTab->pVtab); |
+ if( rc==SQLITE_OK ){ |
+ addToVTrans(db, pVTab); |
+ } |
+ } |
+ } |
+ return rc; |
+} |
+ |
+/* |
+** Invoke either the xSavepoint, xRollbackTo or xRelease method of all |
+** virtual tables that currently have an open transaction. Pass iSavepoint |
+** as the second argument to the virtual table method invoked. |
+** |
+** If op is SAVEPOINT_BEGIN, the xSavepoint method is invoked. If it is |
+** SAVEPOINT_ROLLBACK, the xRollbackTo method. Otherwise, if op is |
+** SAVEPOINT_RELEASE, then the xRelease method of each virtual table with |
+** an open transaction is invoked. |
+** |
+** If any virtual table method returns an error code other than SQLITE_OK, |
+** processing is abandoned and the error returned to the caller of this |
+** function immediately. If all calls to virtual table methods are successful, |
+** SQLITE_OK is returned. |
+*/ |
+int sqlite3VtabSavepoint(sqlite3 *db, int op, int iSavepoint){ |
+ int rc = SQLITE_OK; |
+ |
+ assert( op==SAVEPOINT_RELEASE||op==SAVEPOINT_ROLLBACK||op==SAVEPOINT_BEGIN ); |
+ assert( iSavepoint>=0 ); |
+ if( db->aVTrans ){ |
+ int i; |
+ for(i=0; rc==SQLITE_OK && i<db->nVTrans; i++){ |
+ VTable *pVTab = db->aVTrans[i]; |
+ const sqlite3_module *pMod = pVTab->pMod->pModule; |
+ if( pVTab->pVtab && pMod->iVersion>=2 ){ |
+ int (*xMethod)(sqlite3_vtab *, int); |
+ switch( op ){ |
+ case SAVEPOINT_BEGIN: |
+ xMethod = pMod->xSavepoint; |
+ pVTab->iSavepoint = iSavepoint+1; |
+ break; |
+ case SAVEPOINT_ROLLBACK: |
+ xMethod = pMod->xRollbackTo; |
+ break; |
+ default: |
+ xMethod = pMod->xRelease; |
+ break; |
+ } |
+ if( xMethod && pVTab->iSavepoint>iSavepoint ){ |
+ rc = xMethod(pVTab->pVtab, iSavepoint); |
+ } |
+ } |
} |
} |
return rc; |
@@ -908,7 +1014,7 @@ FuncDef *sqlite3VtabOverloadFunction( |
memcpy(pNew->zName, pDef->zName, sqlite3Strlen30(pDef->zName)+1); |
pNew->xFunc = xFunc; |
pNew->pUserData = pArg; |
- pNew->flags |= SQLITE_FUNC_EPHEM; |
+ pNew->funcFlags |= SQLITE_FUNC_EPHEM; |
return pNew; |
} |
@@ -937,4 +1043,55 @@ void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){ |
} |
} |
+/* |
+** Return the ON CONFLICT resolution mode in effect for the virtual |
+** table update operation currently in progress. |
+** |
+** The results of this routine are undefined unless it is called from |
+** within an xUpdate method. |
+*/ |
+int sqlite3_vtab_on_conflict(sqlite3 *db){ |
+ static const unsigned char aMap[] = { |
+ SQLITE_ROLLBACK, SQLITE_ABORT, SQLITE_FAIL, SQLITE_IGNORE, SQLITE_REPLACE |
+ }; |
+ assert( OE_Rollback==1 && OE_Abort==2 && OE_Fail==3 ); |
+ assert( OE_Ignore==4 && OE_Replace==5 ); |
+ assert( db->vtabOnConflict>=1 && db->vtabOnConflict<=5 ); |
+ return (int)aMap[db->vtabOnConflict-1]; |
+} |
+ |
+/* |
+** Call from within the xCreate() or xConnect() methods to provide |
+** the SQLite core with additional information about the behavior |
+** of the virtual table being implemented. |
+*/ |
+int sqlite3_vtab_config(sqlite3 *db, int op, ...){ |
+ va_list ap; |
+ int rc = SQLITE_OK; |
+ |
+ sqlite3_mutex_enter(db->mutex); |
+ |
+ va_start(ap, op); |
+ switch( op ){ |
+ case SQLITE_VTAB_CONSTRAINT_SUPPORT: { |
+ VtabCtx *p = db->pVtabCtx; |
+ if( !p ){ |
+ rc = SQLITE_MISUSE_BKPT; |
+ }else{ |
+ assert( p->pTab==0 || (p->pTab->tabFlags & TF_Virtual)!=0 ); |
+ p->pVTable->bConstraint = (u8)va_arg(ap, int); |
+ } |
+ break; |
+ } |
+ default: |
+ rc = SQLITE_MISUSE_BKPT; |
+ break; |
+ } |
+ va_end(ap); |
+ |
+ if( rc!=SQLITE_OK ) sqlite3Error(db, rc); |
+ sqlite3_mutex_leave(db->mutex); |
+ return rc; |
+} |
+ |
#endif /* SQLITE_OMIT_VIRTUALTABLE */ |