#ifndef SQLITE_OMIT_VIRTUALTABLE
#include "sqliteInt.h"
struct VtabCtx {
VTable *pVTable;
Table *pTab;
VtabCtx *pPrior;
int bDeclared;
};
Module *sqlite3VtabCreateModule(
sqlite3 *db,
const char *zName,
const sqlite3_module *pModule,
void *pAux,
void (*xDestroy)(void *)
){
Module *pMod;
Module *pDel;
char *zCopy;
if( pModule==0 ){
zCopy = (char*)zName;
pMod = 0;
}else{
int nName = sqlite3Strlen30(zName);
pMod = (Module *)sqlite3Malloc(sizeof(Module) + nName + 1);
if( pMod==0 ){
sqlite3OomFault(db);
return 0;
}
zCopy = (char *)(&pMod[1]);
memcpy(zCopy, zName, nName+1);
pMod->zName = zCopy;
pMod->pModule = pModule;
pMod->pAux = pAux;
pMod->xDestroy = xDestroy;
pMod->pEpoTab = 0;
pMod->nRefModule = 1;
}
pDel = (Module *)sqlite3HashInsert(&db->aModule,zCopy,(void*)pMod);
if( pDel ){
if( pDel==pMod ){
sqlite3OomFault(db);
sqlite3DbFree(db, pDel);
pMod = 0;
}else{
sqlite3VtabEponymousTableClear(db, pDel);
sqlite3VtabModuleUnref(db, pDel);
}
}
return pMod;
}
static int createModule(
sqlite3 *db,
const char *zName,
const sqlite3_module *pModule,
void *pAux,
void (*xDestroy)(void *)
){
int rc = SQLITE_OK;
sqlite3_mutex_enter(db->mutex);
(void)sqlite3VtabCreateModule(db, zName, pModule, pAux, xDestroy);
rc = sqlite3ApiExit(db, rc);
if( rc!=SQLITE_OK && xDestroy ) xDestroy(pAux);
sqlite3_mutex_leave(db->mutex);
return rc;
}
int sqlite3_create_module(
sqlite3 *db,
const char *zName,
const sqlite3_module *pModule,
void *pAux
){
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) || zName==0 ) return SQLITE_MISUSE_BKPT;
#endif
return createModule(db, zName, pModule, pAux, 0);
}
int sqlite3_create_module_v2(
sqlite3 *db,
const char *zName,
const sqlite3_module *pModule,
void *pAux,
void (*xDestroy)(void *)
){
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) || zName==0 ) return SQLITE_MISUSE_BKPT;
#endif
return createModule(db, zName, pModule, pAux, xDestroy);
}
int sqlite3_drop_modules(sqlite3 *db, const char** azNames){
HashElem *pThis, *pNext;
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
#endif
for(pThis=sqliteHashFirst(&db->aModule); pThis; pThis=pNext){
Module *pMod = (Module*)sqliteHashData(pThis);
pNext = sqliteHashNext(pThis);
if( azNames ){
int ii;
for(ii=0; azNames[ii]!=0 && strcmp(azNames[ii],pMod->zName)!=0; ii++){}
if( azNames[ii]!=0 ) continue;
}
createModule(db, pMod->zName, 0, 0, 0);
}
return SQLITE_OK;
}
void sqlite3VtabModuleUnref(sqlite3 *db, Module *pMod){
assert( pMod->nRefModule>0 );
pMod->nRefModule--;
if( pMod->nRefModule==0 ){
if( pMod->xDestroy ){
pMod->xDestroy(pMod->pAux);
}
assert( pMod->pEpoTab==0 );
sqlite3DbFree(db, pMod);
}
}
void sqlite3VtabLock(VTable *pVTab){
pVTab->nRef++;
}
VTable *sqlite3GetVTable(sqlite3 *db, Table *pTab){
VTable *pVtab;
assert( IsVirtual(pTab) );
for(pVtab=pTab->u.vtab.p; pVtab && pVtab->db!=db; pVtab=pVtab->pNext);
return pVtab;
}
void sqlite3VtabUnlock(VTable *pVTab){
sqlite3 *db = pVTab->db;
assert( db );
assert( pVTab->nRef>0 );
assert( db->eOpenState==SQLITE_STATE_OPEN
|| db->eOpenState==SQLITE_STATE_ZOMBIE );
pVTab->nRef--;
if( pVTab->nRef==0 ){
sqlite3_vtab *p = pVTab->pVtab;
if( p ){
p->pModule->xDisconnect(p);
}
sqlite3VtabModuleUnref(pVTab->db, pVTab->pMod);
sqlite3DbFree(db, pVTab);
}
}
static VTable *vtabDisconnectAll(sqlite3 *db, Table *p){
VTable *pRet = 0;
VTable *pVTable;
assert( IsVirtual(p) );
pVTable = p->u.vtab.p;
p->u.vtab.p = 0;
assert( db==0 || sqlite3SchemaMutexHeld(db, 0, p->pSchema) );
while( pVTable ){
sqlite3 *db2 = pVTable->db;
VTable *pNext = pVTable->pNext;
assert( db2 );
if( db2==db ){
pRet = pVTable;
p->u.vtab.p = pRet;
pRet->pNext = 0;
}else{
pVTable->pNext = db2->pDisconnect;
db2->pDisconnect = pVTable;
}
pVTable = pNext;
}
assert( !db || pRet );
return pRet;
}
void sqlite3VtabDisconnect(sqlite3 *db, Table *p){
VTable **ppVTab;
assert( IsVirtual(p) );
assert( sqlite3BtreeHoldsAllMutexes(db) );
assert( sqlite3_mutex_held(db->mutex) );
for(ppVTab=&p->u.vtab.p; *ppVTab; ppVTab=&(*ppVTab)->pNext){
if( (*ppVTab)->db==db ){
VTable *pVTab = *ppVTab;
*ppVTab = pVTab->pNext;
sqlite3VtabUnlock(pVTab);
break;
}
}
}
void sqlite3VtabUnlockList(sqlite3 *db){
VTable *p = db->pDisconnect;
assert( sqlite3BtreeHoldsAllMutexes(db) );
assert( sqlite3_mutex_held(db->mutex) );
if( p ){
db->pDisconnect = 0;
sqlite3ExpirePreparedStatements(db, 0);
do {
VTable *pNext = p->pNext;
sqlite3VtabUnlock(p);
p = pNext;
}while( p );
}
}
void sqlite3VtabClear(sqlite3 *db, Table *p){
assert( IsVirtual(p) );
assert( db!=0 );
if( db->pnBytesFreed==0 ) vtabDisconnectAll(0, p);
if( p->u.vtab.azArg ){
int i;
for(i=0; i<p->u.vtab.nArg; i++){
if( i!=1 ) sqlite3DbFree(db, p->u.vtab.azArg[i]);
}
sqlite3DbFree(db, p->u.vtab.azArg);
}
}
static void addModuleArgument(Parse *pParse, Table *pTable, char *zArg){
sqlite3_int64 nBytes;
char **azModuleArg;
sqlite3 *db = pParse->db;
assert( IsVirtual(pTable) );
nBytes = sizeof(char *)*(2+pTable->u.vtab.nArg);
if( pTable->u.vtab.nArg+3>=db->aLimit[SQLITE_LIMIT_COLUMN] ){
sqlite3ErrorMsg(pParse, "too many columns on %s", pTable->zName);
}
azModuleArg = sqlite3DbRealloc(db, pTable->u.vtab.azArg, nBytes);
if( azModuleArg==0 ){
sqlite3DbFree(db, zArg);
}else{
int i = pTable->u.vtab.nArg++;
azModuleArg[i] = zArg;
azModuleArg[i+1] = 0;
pTable->u.vtab.azArg = azModuleArg;
}
}
void sqlite3VtabBeginParse(
Parse *pParse,
Token *pName1,
Token *pName2,
Token *pModuleName,
int ifNotExists
){
Table *pTable;
sqlite3 *db;
sqlite3StartTable(pParse, pName1, pName2, 0, 0, 1, ifNotExists);
pTable = pParse->pNewTable;
if( pTable==0 ) return;
assert( 0==pTable->pIndex );
pTable->eTabType = TABTYP_VTAB;
db = pParse->db;
assert( pTable->u.vtab.nArg==0 );
addModuleArgument(pParse, pTable, sqlite3NameFromToken(db, pModuleName));
addModuleArgument(pParse, pTable, 0);
addModuleArgument(pParse, pTable, sqlite3DbStrDup(db, pTable->zName));
assert( (pParse->sNameToken.z==pName2->z && pName2->z!=0)
|| (pParse->sNameToken.z==pName1->z && pName2->z==0)
);
pParse->sNameToken.n = (int)(
&pModuleName->z[pModuleName->n] - pParse->sNameToken.z
);
#ifndef SQLITE_OMIT_AUTHORIZATION
if( pTable->u.vtab.azArg ){
int iDb = sqlite3SchemaToIndex(db, pTable->pSchema);
assert( iDb>=0 );
sqlite3AuthCheck(pParse, SQLITE_CREATE_VTABLE, pTable->zName,
pTable->u.vtab.azArg[0], pParse->db->aDb[iDb].zDbSName);
}
#endif
}
static void addArgumentToVtab(Parse *pParse){
if( pParse->sArg.z && pParse->pNewTable ){
const char *z = (const char*)pParse->sArg.z;
int n = pParse->sArg.n;
sqlite3 *db = pParse->db;
addModuleArgument(pParse, pParse->pNewTable, sqlite3DbStrNDup(db, z, n));
}
}
void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
Table *pTab = pParse->pNewTable;
sqlite3 *db = pParse->db;
if( pTab==0 ) return;
assert( IsVirtual(pTab) );
addArgumentToVtab(pParse);
pParse->sArg.z = 0;
if( pTab->u.vtab.nArg<1 ) return;
if( !db->init.busy ){
char *zStmt;
char *zWhere;
int iDb;
int iReg;
Vdbe *v;
sqlite3MayAbort(pParse);
if( pEnd ){
pParse->sNameToken.n = (int)(pEnd->z - pParse->sNameToken.z) + pEnd->n;
}
zStmt = sqlite3MPrintf(db, "CREATE VIRTUAL TABLE %T", &pParse->sNameToken);
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
sqlite3NestedParse(pParse,
"UPDATE %Q." LEGACY_SCHEMA_TABLE " "
"SET type='table', name=%Q, tbl_name=%Q, rootpage=0, sql=%Q "
"WHERE rowid=#%d",
db->aDb[iDb].zDbSName,
pTab->zName,
pTab->zName,
zStmt,
pParse->regRowid
);
v = sqlite3GetVdbe(pParse);
sqlite3ChangeCookie(pParse, iDb);
sqlite3VdbeAddOp0(v, OP_Expire);
zWhere = sqlite3MPrintf(db, "name=%Q AND sql=%Q", pTab->zName, zStmt);
sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere, 0);
sqlite3DbFree(db, zStmt);
iReg = ++pParse->nMem;
sqlite3VdbeLoadString(v, iReg, pTab->zName);
sqlite3VdbeAddOp2(v, OP_VCreate, iDb, iReg);
}else{
Table *pOld;
Schema *pSchema = pTab->pSchema;
const char *zName = pTab->zName;
assert( zName!=0 );
sqlite3MarkAllShadowTablesOf(db, pTab);
pOld = sqlite3HashInsert(&pSchema->tblHash, zName, pTab);
if( pOld ){
sqlite3OomFault(db);
assert( pTab==pOld );
return;
}
pParse->pNewTable = 0;
}
}
void sqlite3VtabArgInit(Parse *pParse){
addArgumentToVtab(pParse);
pParse->sArg.z = 0;
pParse->sArg.n = 0;
}
void sqlite3VtabArgExtend(Parse *pParse, Token *p){
Token *pArg = &pParse->sArg;
if( pArg->z==0 ){
pArg->z = p->z;
pArg->n = p->n;
}else{
assert(pArg->z <= p->z);
pArg->n = (int)(&p->z[p->n] - pArg->z);
}
}
static int vtabCallConstructor(
sqlite3 *db,
Table *pTab,
Module *pMod,
int (*xConstruct)(sqlite3*,void*,int,const char*const*,sqlite3_vtab**,char**),
char **pzErr
){
VtabCtx sCtx;
VTable *pVTable;
int rc;
const char *const*azArg;
int nArg = pTab->u.vtab.nArg;
char *zErr = 0;
char *zModuleName;
int iDb;
VtabCtx *pCtx;
assert( IsVirtual(pTab) );
azArg = (const char *const*)pTab->u.vtab.azArg;
for(pCtx=db->pVtabCtx; pCtx; pCtx=pCtx->pPrior){
if( pCtx->pTab==pTab ){
*pzErr = sqlite3MPrintf(db,
"vtable constructor called recursively: %s", pTab->zName
);
return SQLITE_LOCKED;
}
}
zModuleName = sqlite3DbStrDup(db, pTab->zName);
if( !zModuleName ){
return SQLITE_NOMEM_BKPT;
}
pVTable = sqlite3MallocZero(sizeof(VTable));
if( !pVTable ){
sqlite3OomFault(db);
sqlite3DbFree(db, zModuleName);
return SQLITE_NOMEM_BKPT;
}
pVTable->db = db;
pVTable->pMod = pMod;
pVTable->eVtabRisk = SQLITE_VTABRISK_Normal;
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
pTab->u.vtab.azArg[1] = db->aDb[iDb].zDbSName;
assert( &db->pVtabCtx );
assert( xConstruct );
sCtx.pTab = pTab;
sCtx.pVTable = pVTable;
sCtx.pPrior = db->pVtabCtx;
sCtx.bDeclared = 0;
db->pVtabCtx = &sCtx;
pTab->nTabRef++;
rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr);
sqlite3DeleteTable(db, pTab);
db->pVtabCtx = sCtx.pPrior;
if( rc==SQLITE_NOMEM ) sqlite3OomFault(db);
assert( sCtx.pTab==pTab );
if( SQLITE_OK!=rc ){
if( zErr==0 ){
*pzErr = sqlite3MPrintf(db, "vtable constructor failed: %s", zModuleName);
}else {
*pzErr = sqlite3MPrintf(db, "%s", zErr);
sqlite3_free(zErr);
}
sqlite3DbFree(db, pVTable);
}else if( ALWAYS(pVTable->pVtab) ){
memset(pVTable->pVtab, 0, sizeof(pVTable->pVtab[0]));
pVTable->pVtab->pModule = pMod->pModule;
pMod->nRefModule++;
pVTable->nRef = 1;
if( sCtx.bDeclared==0 ){
const char *zFormat = "vtable constructor did not declare schema: %s";
*pzErr = sqlite3MPrintf(db, zFormat, pTab->zName);
sqlite3VtabUnlock(pVTable);
rc = SQLITE_ERROR;
}else{
int iCol;
u16 oooHidden = 0;
pVTable->pNext = pTab->u.vtab.p;
pTab->u.vtab.p = pVTable;
for(iCol=0; iCol<pTab->nCol; iCol++){
char *zType = sqlite3ColumnType(&pTab->aCol[iCol], "");
int nType;
int i = 0;
nType = sqlite3Strlen30(zType);
for(i=0; i<nType; i++){
if( 0==sqlite3StrNICmp("hidden", &zType[i], 6)
&& (i==0 || zType[i-1]==' ')
&& (zType[i+6]=='\0' || zType[i+6]==' ')
){
break;
}
}
if( i<nType ){
int j;
int nDel = 6 + (zType[i+6] ? 1 : 0);
for(j=i; (j+nDel)<=nType; j++){
zType[j] = zType[j+nDel];
}
if( zType[i]=='\0' && i>0 ){
assert(zType[i-1]==' ');
zType[i-1] = '\0';
}
pTab->aCol[iCol].colFlags |= COLFLAG_HIDDEN;
pTab->tabFlags |= TF_HasHidden;
oooHidden = TF_OOOHidden;
}else{
pTab->tabFlags |= oooHidden;
}
}
}
}
sqlite3DbFree(db, zModuleName);
return rc;
}
int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){
sqlite3 *db = pParse->db;
const char *zMod;
Module *pMod;
int rc;
assert( pTab );
assert( IsVirtual(pTab) );
if( sqlite3GetVTable(db, pTab) ){
return SQLITE_OK;
}
zMod = pTab->u.vtab.azArg[0];
pMod = (Module*)sqlite3HashFind(&db->aModule, zMod);
if( !pMod ){
const char *zModule = pTab->u.vtab.azArg[0];
sqlite3ErrorMsg(pParse, "no such module: %s", zModule);
rc = SQLITE_ERROR;
}else{
char *zErr = 0;
rc = vtabCallConstructor(db, pTab, pMod, pMod->pModule->xConnect, &zErr);
if( rc!=SQLITE_OK ){
sqlite3ErrorMsg(pParse, "%s", zErr);
pParse->rc = rc;
}
sqlite3DbFree(db, zErr);
}
return rc;
}
static int growVTrans(sqlite3 *db){
const int ARRAY_INCR = 5;
if( (db->nVTrans%ARRAY_INCR)==0 ){
VTable **aVTrans;
sqlite3_int64 nBytes = sizeof(sqlite3_vtab*)*
((sqlite3_int64)db->nVTrans + ARRAY_INCR);
aVTrans = sqlite3DbRealloc(db, (void *)db->aVTrans, nBytes);
if( !aVTrans ){
return SQLITE_NOMEM_BKPT;
}
memset(&aVTrans[db->nVTrans], 0, sizeof(sqlite3_vtab *)*ARRAY_INCR);
db->aVTrans = aVTrans;
}
return SQLITE_OK;
}
static void addToVTrans(sqlite3 *db, VTable *pVTab){
db->aVTrans[db->nVTrans++] = pVTab;
sqlite3VtabLock(pVTab);
}
int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, char **pzErr){
int rc = SQLITE_OK;
Table *pTab;
Module *pMod;
const char *zMod;
pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zDbSName);
assert( pTab && IsVirtual(pTab) && !pTab->u.vtab.p );
zMod = pTab->u.vtab.azArg[0];
pMod = (Module*)sqlite3HashFind(&db->aModule, zMod);
if( pMod==0 || pMod->pModule->xCreate==0 || pMod->pModule->xDestroy==0 ){
*pzErr = sqlite3MPrintf(db, "no such module: %s", zMod);
rc = SQLITE_ERROR;
}else{
rc = vtabCallConstructor(db, pTab, pMod, pMod->pModule->xCreate, pzErr);
}
if( rc==SQLITE_OK && ALWAYS(sqlite3GetVTable(db, pTab)) ){
rc = growVTrans(db);
if( rc==SQLITE_OK ){
addToVTrans(db, sqlite3GetVTable(db, pTab));
}
}
return rc;
}
int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
VtabCtx *pCtx;
int rc = SQLITE_OK;
Table *pTab;
Parse sParse;
int initBusy;
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) || zCreateTable==0 ){
return SQLITE_MISUSE_BKPT;
}
#endif
sqlite3_mutex_enter(db->mutex);
pCtx = db->pVtabCtx;
if( !pCtx || pCtx->bDeclared ){
sqlite3Error(db, SQLITE_MISUSE);
sqlite3_mutex_leave(db->mutex);
return SQLITE_MISUSE_BKPT;
}
pTab = pCtx->pTab;
assert( IsVirtual(pTab) );
sqlite3ParseObjectInit(&sParse, db);
sParse.eParseMode = PARSE_MODE_DECLARE_VTAB;
sParse.disableTriggers = 1;
assert( db->init.busy==0 );
initBusy = db->init.busy;
db->init.busy = 0;
sParse.nQueryLoop = 1;
if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable)
&& ALWAYS(sParse.pNewTable!=0)
&& ALWAYS(!db->mallocFailed)
&& IsOrdinaryTable(sParse.pNewTable)
){
assert( sParse.zErrMsg==0 );
if( !pTab->aCol ){
Table *pNew = sParse.pNewTable;
Index *pIdx;
pTab->aCol = pNew->aCol;
sqlite3ExprListDelete(db, pNew->u.tab.pDfltList);
pTab->nNVCol = pTab->nCol = pNew->nCol;
pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid);
pNew->nCol = 0;
pNew->aCol = 0;
assert( pTab->pIndex==0 );
assert( HasRowid(pNew) || sqlite3PrimaryKeyIndex(pNew)!=0 );
if( !HasRowid(pNew)
&& pCtx->pVTable->pMod->pModule->xUpdate!=0
&& sqlite3PrimaryKeyIndex(pNew)->nKeyCol!=1
){
rc = SQLITE_ERROR;
}
pIdx = pNew->pIndex;
if( pIdx ){
assert( pIdx->pNext==0 );
pTab->pIndex = pIdx;
pNew->pIndex = 0;
pIdx->pTable = pTab;
}
}
pCtx->bDeclared = 1;
}else{
sqlite3ErrorWithMsg(db, SQLITE_ERROR,
(sParse.zErrMsg ? "%s" : 0), sParse.zErrMsg);
sqlite3DbFree(db, sParse.zErrMsg);
rc = SQLITE_ERROR;
}
sParse.eParseMode = PARSE_MODE_NORMAL;
if( sParse.pVdbe ){
sqlite3VdbeFinalize(sParse.pVdbe);
}
sqlite3DeleteTable(db, sParse.pNewTable);
sqlite3ParseObjectReset(&sParse);
db->init.busy = initBusy;
assert( (rc&0xff)==rc );
rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(db->mutex);
return rc;
}
int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab){
int rc = SQLITE_OK;
Table *pTab;
pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zDbSName);
if( ALWAYS(pTab!=0)
&& ALWAYS(IsVirtual(pTab))
&& ALWAYS(pTab->u.vtab.p!=0)
){
VTable *p;
int (*xDestroy)(sqlite3_vtab *);
for(p=pTab->u.vtab.p; p; p=p->pNext){
assert( p->pVtab );
if( p->pVtab->nRef>0 ){
return SQLITE_LOCKED;
}
}
p = vtabDisconnectAll(db, pTab);
xDestroy = p->pMod->pModule->xDestroy;
if( xDestroy==0 ) xDestroy = p->pMod->pModule->xDisconnect;
assert( xDestroy!=0 );
pTab->nTabRef++;
rc = xDestroy(p->pVtab);
if( rc==SQLITE_OK ){
assert( pTab->u.vtab.p==p && p->pNext==0 );
p->pVtab = 0;
pTab->u.vtab.p = 0;
sqlite3VtabUnlock(p);
}
sqlite3DeleteTable(db, pTab);
}
return rc;
}
static void callFinaliser(sqlite3 *db, int offset){
int i;
if( db->aVTrans ){
VTable **aVTrans = db->aVTrans;
db->aVTrans = 0;
for(i=0; i<db->nVTrans; i++){
VTable *pVTab = aVTrans[i];
sqlite3_vtab *p = pVTab->pVtab;
if( p ){
int (*x)(sqlite3_vtab *);
x = *(int (**)(sqlite3_vtab *))((char *)p->pModule + offset);
if( x ) x(p);
}
pVTab->iSavepoint = 0;
sqlite3VtabUnlock(pVTab);
}
sqlite3DbFree(db, aVTrans);
db->nVTrans = 0;
}
}
int sqlite3VtabSync(sqlite3 *db, Vdbe *p){
int i;
int rc = SQLITE_OK;
VTable **aVTrans = db->aVTrans;
db->aVTrans = 0;
for(i=0; rc==SQLITE_OK && i<db->nVTrans; i++){
int (*x)(sqlite3_vtab *);
sqlite3_vtab *pVtab = aVTrans[i]->pVtab;
if( pVtab && (x = pVtab->pModule->xSync)!=0 ){
rc = x(pVtab);
sqlite3VtabImportErrmsg(p, pVtab);
}
}
db->aVTrans = aVTrans;
return rc;
}
int sqlite3VtabRollback(sqlite3 *db){
callFinaliser(db, offsetof(sqlite3_module,xRollback));
return SQLITE_OK;
}
int sqlite3VtabCommit(sqlite3 *db){
callFinaliser(db, offsetof(sqlite3_module,xCommit));
return SQLITE_OK;
}
int sqlite3VtabBegin(sqlite3 *db, VTable *pVTab){
int rc = SQLITE_OK;
const sqlite3_module *pModule;
if( sqlite3VtabInSync(db) ){
return SQLITE_LOCKED;
}
if( !pVTab ){
return SQLITE_OK;
}
pModule = pVTab->pVtab->pModule;
if( pModule->xBegin ){
int i;
for(i=0; i<db->nVTrans; i++){
if( db->aVTrans[i]==pVTab ){
return SQLITE_OK;
}
}
rc = growVTrans(db);
if( rc==SQLITE_OK ){
rc = pModule->xBegin(pVTab->pVtab);
if( rc==SQLITE_OK ){
int iSvpt = db->nStatement + db->nSavepoint;
addToVTrans(db, pVTab);
if( iSvpt && pModule->xSavepoint ){
pVTab->iSavepoint = iSvpt;
rc = pModule->xSavepoint(pVTab->pVtab, iSvpt-1);
}
}
}
}
return rc;
}
int sqlite3VtabSavepoint(sqlite3 *db, int op, int iSavepoint){
int rc = SQLITE_OK;
assert( op==SAVEPOINT_RELEASE||op==SAVEPOINT_ROLLBACK||op==SAVEPOINT_BEGIN );
assert( iSavepoint>=-1 );
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);
sqlite3VtabLock(pVTab);
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 ){
u64 savedFlags = (db->flags & SQLITE_Defensive);
db->flags &= ~(u64)SQLITE_Defensive;
rc = xMethod(pVTab->pVtab, iSavepoint);
db->flags |= savedFlags;
}
sqlite3VtabUnlock(pVTab);
}
}
}
return rc;
}
FuncDef *sqlite3VtabOverloadFunction(
sqlite3 *db,
FuncDef *pDef,
int nArg,
Expr *pExpr
){
Table *pTab;
sqlite3_vtab *pVtab;
sqlite3_module *pMod;
void (*xSFunc)(sqlite3_context*,int,sqlite3_value**) = 0;
void *pArg = 0;
FuncDef *pNew;
int rc = 0;
if( NEVER(pExpr==0) ) return pDef;
if( pExpr->op!=TK_COLUMN ) return pDef;
assert( ExprUseYTab(pExpr) );
pTab = pExpr->y.pTab;
if( NEVER(pTab==0) ) return pDef;
if( !IsVirtual(pTab) ) return pDef;
pVtab = sqlite3GetVTable(db, pTab)->pVtab;
assert( pVtab!=0 );
assert( pVtab->pModule!=0 );
pMod = (sqlite3_module *)pVtab->pModule;
if( pMod->xFindFunction==0 ) return pDef;
#ifdef SQLITE_DEBUG
{
int i;
for(i=0; pDef->zName[i]; i++){
unsigned char x = (unsigned char)pDef->zName[i];
assert( x==sqlite3UpperToLower[x] );
}
}
#endif
rc = pMod->xFindFunction(pVtab, nArg, pDef->zName, &xSFunc, &pArg);
if( rc==0 ){
return pDef;
}
pNew = sqlite3DbMallocZero(db, sizeof(*pNew)
+ sqlite3Strlen30(pDef->zName) + 1);
if( pNew==0 ){
return pDef;
}
*pNew = *pDef;
pNew->zName = (const char*)&pNew[1];
memcpy((char*)&pNew[1], pDef->zName, sqlite3Strlen30(pDef->zName)+1);
pNew->xSFunc = xSFunc;
pNew->pUserData = pArg;
pNew->funcFlags |= SQLITE_FUNC_EPHEM;
return pNew;
}
void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){
Parse *pToplevel = sqlite3ParseToplevel(pParse);
int i, n;
Table **apVtabLock;
assert( IsVirtual(pTab) );
for(i=0; i<pToplevel->nVtabLock; i++){
if( pTab==pToplevel->apVtabLock[i] ) return;
}
n = (pToplevel->nVtabLock+1)*sizeof(pToplevel->apVtabLock[0]);
apVtabLock = sqlite3Realloc(pToplevel->apVtabLock, n);
if( apVtabLock ){
pToplevel->apVtabLock = apVtabLock;
pToplevel->apVtabLock[pToplevel->nVtabLock++] = pTab;
}else{
sqlite3OomFault(pToplevel->db);
}
}
int sqlite3VtabEponymousTableInit(Parse *pParse, Module *pMod){
const sqlite3_module *pModule = pMod->pModule;
Table *pTab;
char *zErr = 0;
int rc;
sqlite3 *db = pParse->db;
if( pMod->pEpoTab ) return 1;
if( pModule->xCreate!=0 && pModule->xCreate!=pModule->xConnect ) return 0;
pTab = sqlite3DbMallocZero(db, sizeof(Table));
if( pTab==0 ) return 0;
pTab->zName = sqlite3DbStrDup(db, pMod->zName);
if( pTab->zName==0 ){
sqlite3DbFree(db, pTab);
return 0;
}
pMod->pEpoTab = pTab;
pTab->nTabRef = 1;
pTab->eTabType = TABTYP_VTAB;
pTab->pSchema = db->aDb[0].pSchema;
assert( pTab->u.vtab.nArg==0 );
pTab->iPKey = -1;
pTab->tabFlags |= TF_Eponymous;
addModuleArgument(pParse, pTab, sqlite3DbStrDup(db, pTab->zName));
addModuleArgument(pParse, pTab, 0);
addModuleArgument(pParse, pTab, sqlite3DbStrDup(db, pTab->zName));
rc = vtabCallConstructor(db, pTab, pMod, pModule->xConnect, &zErr);
if( rc ){
sqlite3ErrorMsg(pParse, "%s", zErr);
sqlite3DbFree(db, zErr);
sqlite3VtabEponymousTableClear(db, pMod);
}
return 1;
}
void sqlite3VtabEponymousTableClear(sqlite3 *db, Module *pMod){
Table *pTab = pMod->pEpoTab;
if( pTab!=0 ){
pTab->tabFlags |= TF_Ephemeral;
sqlite3DeleteTable(db, pTab);
pMod->pEpoTab = 0;
}
}
int sqlite3_vtab_on_conflict(sqlite3 *db){
static const unsigned char aMap[] = {
SQLITE_ROLLBACK, SQLITE_ABORT, SQLITE_FAIL, SQLITE_IGNORE, SQLITE_REPLACE
};
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
#endif
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];
}
int sqlite3_vtab_config(sqlite3 *db, int op, ...){
va_list ap;
int rc = SQLITE_OK;
VtabCtx *p;
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
#endif
sqlite3_mutex_enter(db->mutex);
p = db->pVtabCtx;
if( !p ){
rc = SQLITE_MISUSE_BKPT;
}else{
assert( p->pTab==0 || IsVirtual(p->pTab) );
va_start(ap, op);
switch( op ){
case SQLITE_VTAB_CONSTRAINT_SUPPORT: {
p->pVTable->bConstraint = (u8)va_arg(ap, int);
break;
}
case SQLITE_VTAB_INNOCUOUS: {
p->pVTable->eVtabRisk = SQLITE_VTABRISK_Low;
break;
}
case SQLITE_VTAB_DIRECTONLY: {
p->pVTable->eVtabRisk = SQLITE_VTABRISK_High;
break;
}
case SQLITE_VTAB_USES_ALL_SCHEMAS: {
p->pVTable->bAllSchemas = 1;
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