| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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; |
| int nName = sqlite3Strlen30(zName); |
| pMod = (Module *)sqlite3Malloc(sizeof(Module) + nName + 1); |
| if( pMod==0 ){ |
| sqlite3OomFault(db); |
| }else{ |
| Module *pDel; |
| char *zCopy = (char *)(&pMod[1]); |
| memcpy(zCopy, zName, nName+1); |
| pMod->zName = zCopy; |
| pMod->pModule = pModule; |
| pMod->pAux = pAux; |
| pMod->xDestroy = xDestroy; |
| pMod->pEpoTab = 0; |
| pDel = (Module *)sqlite3HashInsert(&db->aModule,zCopy,(void*)pMod); |
| assert( pDel==0 || pDel==pMod ); |
| if( pDel ){ |
| sqlite3OomFault(db); |
| sqlite3DbFree(db, pDel); |
| pMod = 0; |
| } |
| } |
| 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); |
| if( sqlite3HashFind(&db->aModule, zName) ){ |
| rc = SQLITE_MISUSE_BKPT; |
| }else{ |
| (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 |
| ){ |
| |
| if( !sqlite3SafetyCheckOk(db) || zName==0 ) return SQLITE_MISUSE_BKPT; |
| |
| 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 *) |
| ){ |
| |
| if( !sqlite3SafetyCheckOk(db) || zName==0 ) return SQLITE_MISUSE_BKPT; |
| |
| return createModule(db, zName, pModule, pAux, xDestroy); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void sqlite3VtabLock(VTable *pVTab){ |
| pVTab->nRef++; |
| } |
| |
| |
| |
| |
| |
| |
| |
| VTable *sqlite3GetVTable(sqlite3 *db, Table *pTab){ |
| VTable *pVtab; |
| assert( IsVirtual(pTab) ); |
| for(pVtab=pTab->pVTable; 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->magic==SQLITE_MAGIC_OPEN || db->magic==SQLITE_MAGIC_ZOMBIE ); |
| |
| pVTab->nRef--; |
| if( pVTab->nRef==0 ){ |
| sqlite3_vtab *p = pVTab->pVtab; |
| if( p ){ |
| p->pModule->xDisconnect(p); |
| } |
| sqlite3DbFree(db, pVTab); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| static VTable *vtabDisconnectAll(sqlite3 *db, Table *p){ |
| VTable *pRet = 0; |
| VTable *pVTable = p->pVTable; |
| p->pVTable = 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->pVTable = 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->pVTable; *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; |
| db->pDisconnect = 0; |
| |
| assert( sqlite3BtreeHoldsAllMutexes(db) ); |
| assert( sqlite3_mutex_held(db->mutex) ); |
| |
| if( p ){ |
| sqlite3ExpirePreparedStatements(db, 0); |
| do { |
| VTable *pNext = p->pNext; |
| sqlite3VtabUnlock(p); |
| p = pNext; |
| }while( p ); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void sqlite3VtabClear(sqlite3 *db, Table *p){ |
| if( !db || db->pnBytesFreed==0 ) vtabDisconnectAll(0, p); |
| if( p->azModuleArg ){ |
| int i; |
| for(i=0; i<p->nModuleArg; i++){ |
| if( i!=1 ) sqlite3DbFree(db, p->azModuleArg[i]); |
| } |
| sqlite3DbFree(db, p->azModuleArg); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| static void addModuleArgument(sqlite3 *db, Table *pTable, char *zArg){ |
| int nBytes = sizeof(char *)*(2+pTable->nModuleArg); |
| char **azModuleArg; |
| azModuleArg = sqlite3DbRealloc(db, pTable->azModuleArg, nBytes); |
| if( azModuleArg==0 ){ |
| sqlite3DbFree(db, zArg); |
| }else{ |
| int i = pTable->nModuleArg++; |
| azModuleArg[i] = zArg; |
| azModuleArg[i+1] = 0; |
| pTable->azModuleArg = 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 ); |
| |
| db = pParse->db; |
| |
| assert( pTable->nModuleArg==0 ); |
| addModuleArgument(db, pTable, sqlite3NameFromToken(db, pModuleName)); |
| addModuleArgument(db, pTable, 0); |
| addModuleArgument(db, 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 |
| ); |
| |
| |
| |
| |
| |
| |
| |
| if( pTable->azModuleArg ){ |
| int iDb = sqlite3SchemaToIndex(db, pTable->pSchema); |
| assert( iDb>=0 ); |
| sqlite3AuthCheck(pParse, SQLITE_CREATE_VTABLE, pTable->zName, |
| pTable->azModuleArg[0], pParse->db->aDb[iDb].zDbSName); |
| } |
| |
| } |
| |
| |
| |
| |
| |
| |
| 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(db, pParse->pNewTable, sqlite3DbStrNDup(db, z, n)); |
| } |
| } |
| |
| |
| |
| |
| |
| void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ |
| Table *pTab = pParse->pNewTable; |
| sqlite3 *db = pParse->db; |
| |
| if( pTab==0 ) return; |
| addArgumentToVtab(pParse); |
| pParse->sArg.z = 0; |
| if( pTab->nModuleArg<1 ) return; |
| |
| |
| |
| |
| |
| |
| |
| if( !db->init.busy ){ |
| char *zStmt; |
| char *zWhere; |
| int iDb; |
| int iReg; |
| Vdbe *v; |
| |
| |
| 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.%s " |
| "SET type='table', name=%Q, tbl_name=%Q, rootpage=0, sql=%Q " |
| "WHERE rowid=#%d", |
| db->aDb[iDb].zDbSName, MASTER_NAME, |
| pTab->zName, |
| pTab->zName, |
| zStmt, |
| pParse->regRowid |
| ); |
| sqlite3DbFree(db, zStmt); |
| v = sqlite3GetVdbe(pParse); |
| sqlite3ChangeCookie(pParse, iDb); |
| |
| sqlite3VdbeAddOp0(v, OP_Expire); |
| zWhere = sqlite3MPrintf(db, "name='%q' AND type='table'", pTab->zName); |
| sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere); |
| |
| 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( sqlite3SchemaMutexHeld(db, 0, pSchema) ); |
| 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 = (const char *const*)pTab->azModuleArg; |
| int nArg = pTab->nModuleArg; |
| char *zErr = 0; |
| char *zModuleName; |
| int iDb; |
| VtabCtx *pCtx; |
| |
| |
| 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; |
| |
| iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| pTab->azModuleArg[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; |
| rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr); |
| 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; |
| 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; |
| u8 oooHidden = 0; |
| |
| |
| |
| |
| |
| pVTable->pNext = pTab->pVTable; |
| pTab->pVTable = 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; |
| 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 ); |
| if( !IsVirtual(pTab) || sqlite3GetVTable(db, pTab) ){ |
| return SQLITE_OK; |
| } |
| |
| |
| zMod = pTab->azModuleArg[0]; |
| pMod = (Module*)sqlite3HashFind(&db->aModule, zMod); |
| |
| if( !pMod ){ |
| const char *zModule = pTab->azModuleArg[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; |
| int nBytes = sizeof(sqlite3_vtab *) * (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->pVTable ); |
| |
| |
| zMod = pTab->azModuleArg[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; |
| char *zErr = 0; |
| Parse sParse; |
| |
| |
| if( !sqlite3SafetyCheckOk(db) || zCreateTable==0 ){ |
| return SQLITE_MISUSE_BKPT; |
| } |
| |
| 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) ); |
| |
| memset(&sParse, 0, sizeof(sParse)); |
| sParse.eParseMode = PARSE_MODE_DECLARE_VTAB; |
| sParse.db = db; |
| sParse.nQueryLoop = 1; |
| if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable, &zErr) |
| && sParse.pNewTable |
| && !db->mallocFailed |
| && !sParse.pNewTable->pSelect |
| && !IsVirtual(sParse.pNewTable) |
| ){ |
| if( !pTab->aCol ){ |
| Table *pNew = sParse.pNewTable; |
| Index *pIdx; |
| pTab->aCol = pNew->aCol; |
| 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, (zErr ? "%s" : 0), zErr); |
| sqlite3DbFree(db, zErr); |
| rc = SQLITE_ERROR; |
| } |
| sParse.eParseMode = PARSE_MODE_NORMAL; |
| |
| if( sParse.pVdbe ){ |
| sqlite3VdbeFinalize(sParse.pVdbe); |
| } |
| sqlite3DeleteTable(db, sParse.pNewTable); |
| sqlite3ParserReset(&sParse); |
| |
| 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( pTab!=0 && ALWAYS(pTab->pVTable!=0) ){ |
| VTable *p; |
| int (*xDestroy)(sqlite3_vtab *); |
| for(p=pTab->pVTable; p; p=p->pNext){ |
| assert( p->pVtab ); |
| if( p->pVtab->nRef>0 ){ |
| return SQLITE_LOCKED; |
| } |
| } |
| p = vtabDisconnectAll(db, pTab); |
| xDestroy = p->pMod->pModule->xDestroy; |
| assert( xDestroy!=0 ); |
| rc = xDestroy(p->pVtab); |
| |
| if( rc==SQLITE_OK ){ |
| assert( pTab->pVTable==p && p->pNext==0 ); |
| p->pVtab = 0; |
| pTab->pVTable = 0; |
| sqlite3VtabUnlock(p); |
| } |
| } |
| |
| 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); |
| 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; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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; |
| pTab = pExpr->y.pTab; |
| if( 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; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| { |
| int i; |
| for(i=0; pDef->zName[i]; i++){ |
| unsigned char x = (unsigned char)pDef->zName[i]; |
| assert( x==sqlite3UpperToLower[x] ); |
| } |
| } |
| |
| 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 = sqlite3_realloc64(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->pSchema = db->aDb[0].pSchema; |
| assert( pTab->nModuleArg==0 ); |
| pTab->iPKey = -1; |
| addModuleArgument(db, pTab, sqlite3DbStrDup(db, pTab->zName)); |
| addModuleArgument(db, pTab, 0); |
| addModuleArgument(db, 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 0; |
| } |
| 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 |
| }; |
| |
| if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; |
| |
| 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; |
| |
| |
| if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; |
| |
| 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 || IsVirtual(p->pTab) ); |
| 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; |
| } |
| |
| |