public static void MemReleaseExternal(Mem mem) { Debug.Assert(mem.Ctx == null || MutexEx.Held(mem.Ctx.Mutex)); if ((mem.Flags & MEM.Agg) != 0) { MemFinalize(mem, mem.u.Def); Debug.Assert((mem.Flags & MEM.Agg) == 0); MemRelease(mem); } else if ((mem.Flags & MEM.Dyn) != 0 && mem.Del != null) { Debug.Assert((mem.Flags & MEM.RowSet) == 0); mem.Del(mem.Z); mem.Del = null; } else if ((mem.Flags & MEM.RowSet) != 0) { RowSet_Clear(mem.u.RowSet); } else if ((mem.Flags & MEM.Frame) != 0) { MemSetNull(mem); } mem.N = 0; mem.Z = null; mem.Z_ = null; }
public static double RealValue(Mem mem) { Debug.Assert(mem.Ctx == null || MutexEx.Held(mem.Ctx.Mutex)); //Debug.Assert(C._HASALIGNMENT8(mem)); if ((mem.Flags & MEM.Real) != 0) { return(mem.R); } else if ((mem.Flags & MEM.Int) != 0) { return((double)mem.u.I); } else if ((mem.Flags & (MEM.Str)) != 0) { double val = (double)0; ConvertEx.Atof(mem.Z, ref val, mem.N, mem.Encode); return(val); } else if ((mem.Flags & (MEM.Blob)) != 0) { double val = (double)0; Debug.Assert(mem.Z_ != null || mem.N == 0); ConvertEx.Atof(Encoding.UTF8.GetString(mem.Z_, 0, mem.N), ref val, mem.N, mem.Encode); return(val); } return((double)0); }
public static long IntValue(Mem mem) { Debug.Assert(mem.Ctx == null || MutexEx.Held(mem.Ctx.Mutex)); // assert( EIGHT_BYTE_ALIGNMENT(pMem) ); MEM flags = mem.Flags; if ((flags & MEM.Int) != 0) { return(mem.u.I); } else if ((flags & MEM.Real) != 0) { return(DoubleToInt64(mem.R)); } else if ((flags & (MEM.Str)) != 0) { Debug.Assert(mem.Z != null || mem.N == 0); C.ASSERTCOVERAGE(mem.Z == null); long value; ConvertEx.Atoi64(mem.Z, out value, mem.N, mem.Encode); return(value); } else if ((flags & (MEM.Blob)) != 0) { Debug.Assert(mem.Z_ != null || mem.N == 0); C.ASSERTCOVERAGE(mem.Z_ == null); long value; ConvertEx.Atoi64(Encoding.UTF8.GetString(mem.Z_, 0, mem.N), out value, mem.N, mem.Encode); return(value); } return(0); }
public static RC MemNumerify(Mem mem) { if ((mem.Flags & (MEM.Int | MEM.Real | MEM.Null)) == 0) { Debug.Assert((mem.Flags & (MEM.Blob | MEM.Str)) != 0); Debug.Assert(mem.Ctx == null || MutexEx.Held(mem.Ctx.Mutex)); if ((mem.Flags & MEM.Blob) != 0 && mem.Z == null) { if (ConvertEx.Atoi64(Encoding.UTF8.GetString(mem.Z_, 0, mem.Z_.Length), out mem.u.I, mem.N, mem.Encode) == 0) { E.MemSetTypeFlag(mem, MEM.Int); } else { mem.R = RealValue(mem); E.MemSetTypeFlag(mem, MEM.Real); IntegerAffinity(mem); } } else if (ConvertEx.Atoi64(mem.Z, out mem.u.I, mem.N, mem.Encode) == 0) { E.MemSetTypeFlag(mem, MEM.Int); } else { mem.R = RealValue(mem); E.MemSetTypeFlag(mem, MEM.Real); IntegerAffinity(mem); } } Debug.Assert((mem.Flags & (MEM.Int | MEM.Real | MEM.Null)) != 0); mem.Flags &= ~(MEM.Str | MEM.Blob); return(RC.OK); }
public static RC Reprepare(Vdbe p) { Context ctx = p.Ctx; Debug.Assert(MutexEx.Held(ctx.Mutex)); string sql = Vdbe.Sql(p); Debug.Assert(sql != null); // Reprepare only called for prepare_v2() statements Vdbe newVdbe = new Vdbe(); RC rc = LockAndPrepare(ctx, sql, -1, false, p, ref newVdbe, null); if (rc != 0) { if (rc == RC.NOMEM) { ctx.MallocFailed = true; } Debug.Assert(newVdbe == null); return(rc); } else { Debug.Assert(newVdbe != null); } Vdbe.Swap((Vdbe)newVdbe, p); Vdbe.TransferBindings(newVdbe, (Vdbe)p); newVdbe.ResetStepResult(); newVdbe.Finalize(); return(RC.OK); }
public static RC MemExpandBlob(Mem mem) { if ((mem.Flags & MEM.Zero) != 0) { Debug.Assert((mem.Flags & MEM.Blob) != 0); Debug.Assert((mem.Flags & MEM.RowSet) == 0); Debug.Assert(mem.Ctx == null || MutexEx.Held(mem.Ctx.Mutex)); // Set nByte to the number of bytes required to store the expanded blob. int bytes = mem.N + mem.u.Zeros; if (bytes <= 0) { bytes = 1; } if (MemGrow(mem, bytes, true) != 0) { return(RC.NOMEM); } //: _memset(&mem->Z[mem->N], 0, mem->u.Zeros); mem.Z_ = Encoding.UTF8.GetBytes(mem.Z); mem.Z = null; mem.N += (int)mem.u.Zeros; mem.u.I = 0; mem.Flags &= ~(MEM.Zero | MEM.Static | MEM.Ephem | MEM.Term); mem.Flags |= MEM.Dyn; } return(RC.OK); }
public static void Result_ErrorNoMem(FuncContext fctx) { Debug.Assert(MutexEx.Held(fctx.S.Ctx.Mutex)); MemSetNull(fctx.S); fctx.IsError = RC.NOMEM; fctx.S.Ctx.MallocFailed = true; }
public static RC MemRealify(Mem mem) { Debug.Assert(mem.Ctx == null || MutexEx.Held(mem.Ctx.Mutex)); //Debug.Assert(C._HASALIGNMENT8(mem)); mem.R = RealValue(mem); E.MemSetTypeFlag(mem, MEM.Real); return(RC.OK); }
public static void CloseExtensions(Context ctx) { Debug.Assert(MutexEx.Held(ctx.Mutex)); for (int i = 0; i < ctx.Extensions.length; i++) { ctx.Vfs.DlClose((HANDLE)ctx.Extensions[i]); } C._tagfree(ctx, ref ctx.Extensions.data); }
public static RC MemIntegerify(Mem mem) { Debug.Assert(mem.Ctx == null || MutexEx.Held(mem.Ctx.Mutex)); Debug.Assert((mem.Flags & MEM.RowSet) == 0); //Debug.Assert(C._HASALIGNMENT8(mem)); mem.u.I = IntValue(mem); E.MemSetTypeFlag(mem, MEM.Int); return(RC.OK); }
public static RC MemStringify(Mem mem, TEXTENCODE encode) { MEM f = mem.Flags; Debug.Assert(mem.Ctx == null || MutexEx.Held(mem.Ctx.Mutex)); Debug.Assert((f & MEM.Zero) == 0); Debug.Assert((f & (MEM.Str | MEM.Blob)) == 0); Debug.Assert((f & (MEM.Int | MEM.Real)) != 0); Debug.Assert((mem.Flags & MEM.RowSet) == 0); //: Debug.Assert(C._HASALIGNMENT8(mem)); const int bytes = 32; if (MemGrow(mem, bytes, false) != 0) { return(RC.NOMEM); } // For a Real or Integer, use sqlite3_mprintf() to produce the UTF-8 string representation of the value. Then, if the required encoding // is UTF-16le or UTF-16be do a translation. // FIX ME: It would be better if sqlite3_snprintf() could do UTF-16. if ((f & MEM.Int) != 0) { mem.Z = mem.u.I.ToString(); //: __snprintf(mem->Z, bytes, "%lld", mem->u.I); } else { Debug.Assert((f & MEM.Real) != 0); if (double.IsNegativeInfinity(mem.R)) { mem.Z = "-Inf"; } else if (double.IsInfinity(mem.R)) { mem.Z = "Inf"; } else if (double.IsPositiveInfinity(mem.R)) { mem.Z = "+Inf"; } else if (mem.R.ToString(CultureInfo.InvariantCulture).Contains(".")) { mem.Z = mem.R.ToString(CultureInfo.InvariantCulture).ToLower(); //: __snprintf(mem->Z, bytes, "%!.15g", mem->R); } else { mem.Z = mem.R.ToString(CultureInfo.InvariantCulture) + ".0"; } } mem.N = mem.Z.Length; mem.Encode = TEXTENCODE.UTF8; mem.Flags |= MEM.Str | MEM.Term; ChangeEncoding(mem, encode); return(RC.OK); }
public static object get_Auxdata(FuncContext fctx, int arg) { Debug.Assert(MutexEx.Held(fctx.S.Ctx.Mutex)); VdbeFunc vdbeFunc = fctx.VdbeFunc; if (vdbeFunc == null || arg >= vdbeFunc.AuxsLength || arg < 0) { return(null); } return(vdbeFunc.Auxs[arg].Aux); }
public static RC MemSetStr(Mem mem, byte[] z, int offset, int n, TEXTENCODE encode, Action <object> del) { Debug.Assert(mem.Ctx == null || MutexEx.Held(mem.Ctx.Mutex)); Debug.Assert((mem.Flags & MEM.RowSet) == 0); // If z is a NULL pointer, set pMem to contain an SQL NULL. if (z == null || z.Length < offset) { MemSetNull(mem); return(RC.OK); } int limit = (mem.Ctx != null ? mem.Ctx.Limits[(int)LIMIT.LENGTH] : CORE_MAX_LENGTH); // Maximum allowed string or blob size MEM flags = (encode == 0 ? MEM.Blob : MEM.Str); // New value for pMem->flags int bytes = n; // New value for pMem->n if (bytes < 0) { Debug.Assert(encode != 0); if (encode == TEXTENCODE.UTF8) { for (bytes = 0; bytes <= limit && bytes < z.Length - offset && z[offset + bytes] != 0; bytes++) { } } else { for (bytes = 0; bytes <= limit && z[bytes + offset] != 0 || z[offset + bytes + 1] != 0; bytes += 2) { } } } // The following block sets the new values of Mem.z and Mem.xDel. It also sets a flag in local variable "flags" to indicate the memory // management (one of MEM_Dyn or MEM_Static). Debug.Assert(encode == 0); { mem.Z = null; mem.Z_ = C._alloc(n); Buffer.BlockCopy(z, offset, mem.Z_, 0, n); } mem.N = bytes; mem.Flags = MEM.Blob | MEM.Term; mem.Encode = (encode == 0 ? TEXTENCODE.UTF8 : encode); mem.Type = (encode == 0 ? TYPE.BLOB : TYPE.TEXT); #if !OMIT_UTF16 if (mem.Encode != TEXTENCODE.UTF8 && MemHandleBom(mem) != 0) { return(RC.NOMEM); } #endif return(bytes > limit ? RC.TOOBIG : RC.OK); }
public static void MemMove(Mem to, Mem from) { Debug.Assert(from.Ctx == null || MutexEx.Held(from.Ctx.Mutex)); Debug.Assert(to.Ctx == null || MutexEx.Held(to.Ctx.Mutex)); Debug.Assert(from.Ctx == null || to.Ctx == null || from.Ctx == to.Ctx); MemRelease(to); from._memcpy(ref to); from.Flags = MEM.Null; from.Del = null; from.Z = null; from.Z_ = null; }
static void SchemaIsValid(Parse parse) { Context ctx = parse.Ctx; Debug.Assert(parse.CheckSchema != 0); Debug.Assert(MutexEx.Held(ctx.Mutex)); for (int db = 0; db < ctx.DBs.length; db++) { bool openedTransaction = false; // True if a transaction is opened Btree bt = ctx.DBs[db].Bt; // Btree database to read cookie from if (bt == null) { continue; } // If there is not already a read-only (or read-write) transaction opened on the b-tree database, open one now. If a transaction is opened, it // will be closed immediately after reading the meta-value. if (!bt.IsInReadTrans()) { RC rc = bt.BeginTrans(0); if (rc == RC.NOMEM || rc == RC.IOERR_NOMEM) { ctx.MallocFailed = true; } if (rc != RC.OK) { return; } openedTransaction = true; } // Read the schema cookie from the database. If it does not match the value stored as part of the in-memory schema representation, // set Parse.rc to SQLITE_SCHEMA. uint cookie; bt.GetMeta(Btree.META.SCHEMA_VERSION, ref cookie); Debug.Assert(Btree.SchemaMutexHeld(ctx, db, null)); if (cookie != ctx.DBs[db].Schema.SchemaCookie) { ResetOneSchema(ctx, db); parse.RC = RC.SCHEMA; } // Close the transaction, if one was opened. if (openedTransaction) { bt.Commit(); } } }
public static void Disconnect(Context ctx, Table table) { Debug.Assert(E.IsVirtual(table)); Debug.Assert(Btree.HoldsAllMutexes(ctx)); Debug.Assert(MutexEx.Held(ctx.Mutex)); for (VTable pvtable = table.VTables; pvtable != null; pvtable = pvtable.Next) { if (pvtable.Ctx == ctx) { VTable vtable = pvtable; vtable = vtable.Next; vtable.Unlock(); break; } } }
public static RC ReadSchema(Parse parse) { RC rc = RC.OK; Context ctx = parse.Ctx; Debug.Assert(MutexEx.Held(ctx.Mutex)); if (!ctx.Init.Busy) { rc = Init(ctx, ref parse.ErrMsg); } if (rc != RC.OK) { parse.RC = rc; parse.Errs++; } return(rc); }
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 void IntegerAffinity(Mem mem) { Debug.Assert((mem.Flags & MEM.Real) != 0); Debug.Assert((mem.Flags & MEM.RowSet) == 0); Debug.Assert(mem.Ctx == null || MutexEx.Held(mem.Ctx.Mutex)); //Debug.Assert(C._HASALIGNMENT8(mem)); mem.u.I = DoubleToInt64(mem.R); // Only mark the value as an integer if // (1) the round-trip conversion real->int->real is a no-op, and // (2) The integer is neither the largest nor the smallest possible integer (ticket #3922) // The second and third terms in the following conditional enforces the second condition under the assumption that addition overflow causes // values to wrap around. On x86 hardware, the third term is always true and could be omitted. But we leave it in because other // architectures might behave differently. if (mem.R == (double)mem.u.I && mem.u.I > long.MinValue && C._ALWAYS(mem.u.I < long.MaxValue)) { mem.Flags |= MEM.Int; } }
public static RC MemNulTerminate(Mem mem) { Debug.Assert(mem.Ctx == null || MutexEx.Held(mem.Ctx.Mutex)); if ((mem.Flags & MEM.Term) != 0 || (mem.Flags & MEM.Str) == 0) { return(RC.OK); // Nothing to do } if (MemGrow(mem, mem.N + 2, true) != 0) { return(RC.NOMEM); } //mem.Z[mem.N] = 0; //mem.Z[mem.N + 1] = 0; if (mem.Z != null && mem.N < mem.Z.Length) { mem.Z = mem.Z.Substring(0, mem.N); } mem.Flags |= MEM.Term; return(RC.OK); }
public static RC Init(Context ctx, ref string errMsg) { Debug.Assert(MutexEx.Held(ctx.Mutex)); RC rc = RC.OK; ctx.Init.Busy = true; for (int i = 0; rc == RC.OK && i < ctx.DBs.length; i++) { if (E.DbHasProperty(ctx, i, SCHEMA.SchemaLoaded) || i == 1) { continue; } rc = InitOne(ctx, i, ref errMsg); if (rc != 0) { ResetOneSchema(ctx, i); } } // Once all the other databases have been initialized, load the schema for the TEMP database. This is loaded last, as the TEMP database // schema may contain references to objects in other databases. #if !OMIT_TEMPDB if (rc == RC.OK && C._ALWAYS(ctx.DBs.length > 1) && !E.DbHasProperty(ctx, 1, SCHEMA.SchemaLoaded)) { rc = InitOne(ctx, 1, ref errMsg); if (rc != 0) { ResetOneSchema(ctx, 1); } } #endif bool commitInternal = !((ctx.Flags & Context.FLAG.InternChanges) != 0); ctx.Init.Busy = false; if (rc == RC.OK && commitInternal) { CommitInternalChanges(ctx); } return(rc); }
public static RC MemFinalize(Mem mem, FuncDef func) { RC rc = RC.OK; if (C._ALWAYS(func != null && func.Finalize != null)) { Debug.Assert((mem.Flags & MEM.Null) != 0 || func == mem.u.Def); Debug.Assert(mem.Ctx == null || MutexEx.Held(mem.Ctx.Mutex)); //memset(&ctx, 0, sizeof(ctx)); FuncContext ctx = new FuncContext(); ctx.S.Flags = MEM.Null; ctx.S.Ctx = mem.Ctx; ctx.Mem = mem; ctx.Func = func; func.Finalize(ctx); // IMP: R-24505-23230 Debug.Assert((mem.Flags & MEM.Dyn) == 0 && mem.Del == null); C._tagfree(mem.Ctx, ref mem.Z_); //: mem->Malloc); ctx.S._memcpy(ref mem); rc = ctx.IsError; } return(rc); }
public static RC MemMakeWriteable(Mem mem) { Debug.Assert(mem.Ctx == null || MutexEx.Held(mem.Ctx.Mutex)); Debug.Assert((mem.Flags & MEM.RowSet) == 0); E.ExpandBlob(mem); MEM f = mem.Flags; if ((f & (MEM.Str | MEM.Blob)) != 0) //: mem->Z != mem->Malloc) { if (MemGrow(mem, mem.N + 2, true) != 0) { return(RC.NOMEM); } //: mem.Z[mem.N] = 0; //: mem.Z[mem.N + 1] = 0; mem.Flags |= MEM.Term; #if DEBUG mem.ScopyFrom = null; #endif } return(RC.OK); }
public static string ValueText(Mem mem, TEXTENCODE encode) { if (mem == null) { return(null); } Debug.Assert(mem.Ctx == null || MutexEx.Held(mem.Ctx.Mutex)); Debug.Assert((encode & (TEXTENCODE)3) == (encode & ~TEXTENCODE.UTF16_ALIGNED)); Debug.Assert((mem.Flags & MEM.RowSet) == 0); if ((mem.Flags & MEM.Null) != 0) { return(null); } Debug.Assert(((int)MEM.Blob >> 3) == (int)MEM.Str); mem.Flags |= (MEM)((int)(mem.Flags & MEM.Blob) >> 3); E.ExpandBlob(mem); if ((mem.Flags & MEM.Str) != 0) { ChangeEncoding(mem, encode & ~TEXTENCODE.UTF16_ALIGNED); if ((encode & TEXTENCODE.UTF16_ALIGNED) != 0 && (1 & (mem.Z[0])) == 1) { Debug.Assert((mem.Flags & (MEM.Ephem | MEM.Static)) != 0); if (MemMakeWriteable(mem) != RC.OK) { return(null); } } MemNulTerminate(mem); // IMP: R-31275-44060 } else { Debug.Assert((mem.Flags & MEM.Blob) == 0); MemStringify(mem, encode); Debug.Assert((1 & (mem.Z[0])) == 0); } Debug.Assert(mem.Encode == (encode & ~TEXTENCODE.UTF16_ALIGNED) || mem.Ctx == null || mem.Ctx.MallocFailed); return(mem.Encode == (encode & ~TEXTENCODE.UTF16_ALIGNED) ? mem.Z : null); }
public static int SchemaToIndex(Context ctx, Schema schema) { // If pSchema is NULL, then return -1000000. This happens when code in expr.c is trying to resolve a reference to a transient table (i.e. one // created by a sub-select). In this case the return value of this function should never be used. // // We return -1000000 instead of the more usual -1 simply because using -1000000 as the incorrect index into ctx->aDb[] is much // more likely to cause a segfault than -1 (of course there are assert() statements too, but it never hurts to play the odds). Debug.Assert(MutexEx.Held(ctx.Mutex)); int i = -1000000; if (schema != null) { for (i = 0; C._ALWAYS(i < ctx.DBs.length); i++) { if (ctx.DBs[i].Schema == schema) { break; } } Debug.Assert(i >= 0 && i < ctx.DBs.length); } return(i); }
internal static void UnlinkVfs(VSystem vfs) { Debug.Assert(MutexEx.Held(MutexEx.Alloc(MutexEx.MUTEX.STATIC_MASTER))); if (vfs == null) { } else if (_vfsList == vfs) { _vfsList = vfs.Next; } else if (_vfsList != null) { var p = _vfsList; while (p.Next != null && p.Next != vfs) { p = p.Next; } if (p.Next == vfs) { p.Next = vfs.Next; } } }
public static void set_Auxdata(FuncContext fctx, int args, object aux, Action delete) { if (args < 0) { goto failed; } Debug.Assert(MutexEx.Held(fctx.S.Ctx.Mutex)); VdbeFunc vdbeFunc = fctx.VdbeFunc; if (vdbeFunc == null || vdbeFunc.AuxsLength <= args) { int auxLength = (vdbeFunc != null ? vdbeFunc.AuxsLength : 0); int newSize = args; vdbeFunc = new VdbeFunc(); if (vdbeFunc == null) { goto failed; } fctx.VdbeFunc = vdbeFunc; vdbeFunc.Auxs[auxLength] = new VdbeFunc.AuxData(); vdbeFunc.AuxsLength = args + 1; vdbeFunc.Func = fctx.Func; } VdbeFunc.AuxData auxData = vdbeFunc.Auxs[args]; if (auxData.Aux != null && auxData.Aux is IDisposable) { (auxData.Aux as IDisposable).Dispose(); } auxData.Aux = aux; return; failed: if (aux != null && aux is IDisposable) { (aux as IDisposable).Dispose(); } }
public static RC ChangeEncoding(Mem mem, TEXTENCODE newEncode) { Debug.Assert((mem.Flags & MEM.RowSet) == 0); Debug.Assert(newEncode == TEXTENCODE.UTF8 || newEncode == TEXTENCODE.UTF16LE || newEncode == TEXTENCODE.UTF16BE); if ((mem.Flags & MEM.Str) == 0 || mem.Encode == newEncode) { if (mem.Z == null && mem.Z_ != null) { mem.Z = Encoding.UTF8.GetString(mem.Z_, 0, mem.Z_.Length); } return(RC.OK); } Debug.Assert(mem.Ctx == null || MutexEx.Held(mem.Ctx.Mutex)); #if OMIT_UTF16 return(RC.ERROR); #else // MemTranslate() may return SQLITE_OK or SQLITE_NOMEM. If NOMEM is returned, then the encoding of the value may not have changed. RC rc = MemTranslate(mem, newEncode); Debug.Assert(rc == RC.OK || rc == RC.NOMEM); Debug.Assert(rc == RC.OK || mem.Encode != newEncode); Debug.Assert(rc == RC.NOMEM || mem.Encode == newEncode); return(rc); #endif }
public static Mem Aggregate_Context(FuncContext fctx, int bytes) { Debug.Assert(fctx != null && fctx.Func != null && fctx.Func.Step != null); Debug.Assert(MutexEx.Held(fctx.S.Ctx.Mutex)); Mem mem = fctx.Mem; C.ASSERTCOVERAGE(bytes < 0); if ((mem.Flags & MEM.Agg) == 0) { if (bytes <= 0) { MemReleaseExternal(mem); mem.Flags = 0; mem.Z = null; } else { MemGrow(mem, bytes, 0); mem.Flags = MEM.Agg; mem.u.Def = fctx.Func; } } return(Mem.ToMem_(mem)); }
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); }