public static void UnlockList(Context ctx) { Debug.Assert(Btree.HoldsAllMutexes(ctx)); Debug.Assert(MutexEx.Held(ctx.Mutex)); VTable vtable = ctx.Disconnect; ctx.Disconnect = null; if (vtable != null) { Vdbe.ExpirePreparedStatements(ctx); do { VTable next = vtable.Next; vtable.Unlock(); vtable = next; } while (vtable != null); } }
public static RC Savepoint(Context ctx, IPager.SAVEPOINT op, int savepoint) { Debug.Assert(op == IPager.SAVEPOINT.RELEASE || op == IPager.SAVEPOINT.ROLLBACK || op == IPager.SAVEPOINT.BEGIN); Debug.Assert(savepoint >= 0); RC rc = RC.OK; if (ctx.VTrans.data != null) { for (int i = 0; rc == RC.OK && i < ctx.VTrans.length; i++) { VTable vtable = ctx.VTrans[i]; ITableModule itablemodule = vtable.Module.IModule; if (vtable.IVTable != null && itablemodule.Version >= 2) { Func <VTable, int, int> method = null; switch (op) { case IPager.SAVEPOINT.BEGIN: method = itablemodule.Savepoint; vtable.Savepoints = savepoint + 1; break; case IPager.SAVEPOINT.ROLLBACK: method = itablemodule.RollbackTo; break; default: method = itablemodule.Release; break; } if (method != null && vtable.Savepoints > savepoint) { rc = (RC)method(vtable.IVTable, savepoint); } } } } return(rc); }
public static RC CallDestroy(Context ctx, int db, string tableName) { RC rc = RC.OK; Table table = Parse.FindTable(ctx, tableName, ctx.DBs[db].Name); if (C._ALWAYS(table != null && table.VTables != null)) { VTable vtable = VTableDisconnectAll(ctx, table); Debug.Assert(rc == RC.OK); rc = vtable.Module.IModule.Destroy(vtable.IVTable); // Remove the sqlite3_vtab* from the aVTrans[] array, if applicable if (rc == RC.OK) { Debug.Assert(table.VTables == vtable && vtable.Next == null); vtable.IVTable = null; table.VTables = null; vtable.Unlock(); } } return(rc); }
public static RC Begin(Context ctx, VTable vtable) { // Special case: If ctx->aVTrans is NULL and ctx->nVTrans is greater than zero, then this function is being called from within a // virtual module xSync() callback. It is illegal to write to virtual module tables in this case, so return SQLITE_LOCKED. if (InSync(ctx)) { return(RC.LOCKED); } if (vtable == null) { return(RC.OK); } RC rc = RC.OK; ITableModule imodule = vtable.IVTable.IModule; if (imodule.Begin != null) { // If pVtab is already in the aVTrans array, return early for (int i = 0; i < ctx.VTrans.length; i++) { if (ctx.VTrans[i] == vtable) { return(RC.OK); } } // Invoke the xBegin method. If successful, add the vtab to the sqlite3.aVTrans[] array. rc = GrowVTrans(ctx); if (rc == RC.OK) { rc = imodule.Begin(vtable.IVTable); if (rc == RC.OK) { AddToVTrans(ctx, vtable); } } } return(rc); }
static void CallFinaliser(Context ctx, int offset) { if (ctx.VTrans.data != null) { for (int i = 0; i < ctx.VTrans.length; i++) { VTable vtable = ctx.VTrans[i]; IVTable ivtable = vtable.IVTable; if (ivtable != null) { Func <IVTable, int> x = null; if (offset == 0) { x = ivtable.IModule.Rollback; } else if (offset == 1) { x = ivtable.IModule.Commit; } else { throw new InvalidOperationException(); } if (x != null) { x(ivtable); } } vtable.Savepoints = 0; vtable.Unlock(); } C._tagfree(ctx, ref ctx.VTrans.data); ctx.VTrans.length = 0; ctx.VTrans.data = null; } }
public static RC Prepare_(Context ctx, string sql, int bytes, bool isPrepareV2, Vdbe reprepare, ref Vdbe stmtOut, ref string tailOut) { stmtOut = null; tailOut = null; string errMsg = null; // Error message RC rc = RC.OK; int i; // Allocate the parsing context Parse parse = new Parse(); // Parsing context if (parse == null) { rc = RC.NOMEM; goto end_prepare; } parse.Reprepare = reprepare; parse.LastToken.data = null; //: C#? Debug.Assert(tailOut == null); Debug.Assert(!ctx.MallocFailed); Debug.Assert(MutexEx.Held(ctx.Mutex)); // Check to verify that it is possible to get a read lock on all database schemas. The inability to get a read lock indicates that // some other database connection is holding a write-lock, which in turn means that the other connection has made uncommitted changes // to the schema. // // Were we to proceed and prepare the statement against the uncommitted schema changes and if those schema changes are subsequently rolled // back and different changes are made in their place, then when this prepared statement goes to run the schema cookie would fail to detect // the schema change. Disaster would follow. // // This thread is currently holding mutexes on all Btrees (because of the sqlite3BtreeEnterAll() in sqlite3LockAndPrepare()) so it // is not possible for another thread to start a new schema change while this routine is running. Hence, we do not need to hold // locks on the schema, we just need to make sure nobody else is holding them. // // Note that setting READ_UNCOMMITTED overrides most lock detection, but it does *not* override schema lock detection, so this all still // works even if READ_UNCOMMITTED is set. for (i = 0; i < ctx.DBs.length; i++) { Btree bt = ctx.DBs[i].Bt; if (bt != null) { Debug.Assert(bt.HoldsMutex()); rc = bt.SchemaLocked(); if (rc != 0) { string dbName = ctx.DBs[i].Name; sqlite3Error(ctx, rc, "database schema is locked: %s", dbName); C.ASSERTCOVERAGE((ctx.Flags & Context.FLAG.ReadUncommitted) != 0); goto end_prepare; } } } VTable.UnlockList(ctx); parse.Ctx = ctx; parse.QueryLoops = (double)1; if (bytes >= 0 && (bytes == 0 || sql[bytes - 1] != 0)) { int maxLen = ctx.aLimit[SQLITE_LIMIT_SQL_LENGTH]; C.ASSERTCOVERAGE(bytes == maxLen); C.ASSERTCOVERAGE(bytes == maxLen + 1); if (bytes > maxLen) { sqlite3Error(ctx, RC.TOOBIG, "statement too long"); rc = SysEx.ApiExit(ctx, RC.TOOBIG); goto end_prepare; } string sqlCopy = sql.Substring(0, bytes); if (sqlCopy != null) { parse.RunParser(sqlCopy, ref errMsg); C._tagfree(ctx, ref sqlCopy); parse.Tail = null; //: &sql[parse->Tail - sqlCopy]; } else { parse.Tail = null; //: &sql[bytes]; } } else { parse.RunParser(sql, ref errMsg); } Debug.Assert((int)parse.QueryLoops == 1); if (ctx.MallocFailed) { parse.RC = RC.NOMEM; } if (parse.RC == RC.DONE) { parse.RC = RC.OK; } if (parse.CheckSchema != 0) { SchemaIsValid(parse); } if (ctx.MallocFailed) { parse.RC = RC.NOMEM; } tailOut = (parse.Tail == null ? null : parse.Tail.ToString()); rc = parse.RC; Vdbe v = parse.V; #if !OMIT_EXPLAIN if (rc == RC.OK && parse.V != null && parse.Explain != 0) { int first, max; if (parse.Explain == 2) { v.SetNumCols(4); first = 8; max = 12; } else { v.SetNumCols(8); first = 0; max = 8; } for (i = first; i < max; i++) { v.SetColName(i - first, COLNAME_NAME, _colName[i], C.DESTRUCTOR_STATIC); } } #endif Debug.Assert(!ctx.Init.Busy || !isPrepareV2); if (!ctx.Init.Busy) { Vdbe.SetSql(v, sql, (int)(sql.Length - (parse.Tail == null ? 0 : parse.Tail.Length)), isPrepareV2); } if (v != null && (rc != RC.OK || ctx.MallocFailed)) { v.Finalize(); Debug.Assert(stmtOut == null); } else { stmtOut = v; } if (errMsg != null) { sqlite3Error(ctx, rc, "%s", errMsg); C._tagfree(ctx, ref errMsg); } else { sqlite3Error(ctx, rc, null); } // Delete any TriggerPrg structures allocated while parsing this statement. while (parse.TriggerPrg != null) { TriggerPrg t = parse.TriggerPrg; parse.TriggerPrg = t.Next; C._tagfree(ctx, ref t); } end_prepare: //sqlite3StackFree( db, pParse ); rc = SysEx.ApiExit(ctx, rc); Debug.Assert((RC)((int)rc & ctx.ErrMask) == rc); return(rc); }
static void AddToVTrans(Context ctx, VTable vtable) { // Add pVtab to the end of sqlite3.aVTrans ctx.VTrans[ctx.VTrans.length++] = vtable; vtable.Lock(); }
static RC VTableCallConstructor(Context ctx, Table table, TableModule module, Construct_t construct, ref string errorOut) { string moduleName = table.Name; if (moduleName == null) { return(RC.NOMEM); } VTable vtable = new VTable(); if (vtable == null) { C._tagfree(ctx, ref moduleName); return(RC.NOMEM); } vtable.Ctx = ctx; vtable.Module = module; int db = sqlite3SchemaToIndex(ctx, table.Schema); table.ModuleArgs[1] = ctx.DBs[db].Name; // Invoke the virtual table constructor Debug.Assert(ctx.VTableCtx != null); Debug.Assert(construct != null); VTableContext sVtableCtx = new VTableContext(); sVtableCtx.Table = table; sVtableCtx.VTable = vtable; VTableContext priorCtx = ctx.VTableCtx; ctx.VTableCtx = sVtableCtx; string[] args = table.ModuleArgs.data; int argsLength = table.ModuleArgs.length; string error = null; RC rc = construct(ctx, module.Aux, argsLength, args, out vtable.IVTable, out error); ctx.VTableCtx = null; if (rc == RC.NOMEM) { ctx.MallocFailed = true; } if (rc != RC.OK) { if (error == null) { errorOut = C._mtagprintf(ctx, "vtable constructor failed: %s", moduleName); } else { errorOut = error; error = null; //: _free(error); } C._tagfree(ctx, ref vtable); } else if (C._ALWAYS(vtable.IVTable != null)) { // Justification of ALWAYS(): A correct vtab constructor must allocate the sqlite3_vtab object if successful. vtable.IVTable.IModule = module.IModule; vtable.Refs = 1; if (sVtableCtx.Table != null) { errorOut = C._mtagprintf(ctx, "vtable constructor did not declare schema: %s", table.Name); vtable.Unlock(); rc = RC.ERROR; } else { // 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 COLFLAG_HIDDEN flag and remove the token from // the type string. vtable.Next = table.VTables; table.VTables = vtable; for (int col = 0; col < table.Cols.length; col++) { string type = table.Cols[col].Type; if (type == null) { continue; } int typeLength = type.Length; int i = 0; if (string.Compare("hidden", 0, type, 0, 6, StringComparison.OrdinalIgnoreCase) == 0 || (type.Length > 6 && type[6] != ' ')) { for (i = 0; i < typeLength; i++) { if (string.Compare(" hidden", 0, type, i, 7, StringComparison.OrdinalIgnoreCase) == 0 && (i + 7 == type.Length || (type[i + 7] == '\0' || type[i + 7] == ' '))) { i++; break; } } } if (i < typeLength) { StringBuilder type2 = new StringBuilder(type); int del = 6 + (type2.Length > i + 6 ? 1 : 0); int j; for (j = i; (j + del) < typeLength; j++) { type2[j] = type2[j + del]; } if (type2[i] == '\0' && i > 0) { Debug.Assert(type[i - 1] == ' '); type2.Length = i; //: type[i - 1] = '\0'; } table.Cols[col].ColFlags |= COLFLAG.HIDDEN; table.Cols[col].Type = type.ToString().Substring(0, j); } } } } C._tagfree(ctx, ref moduleName); return(rc); }