| 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 */
|
|
|