public static void ResetAutoExtension() { #if !OMIT_AUTOINIT if (Initialize() == RC.OK) #endif { #if THREADSAFE MutexEx mutex = MutexEx.Alloc(MutexEx.MUTEX.STATIC_MASTER); #endif MutexEx.Enter(mutex); g_autoext.Exts = null; g_autoext.ExtsLength = 0; MutexEx.Leave(mutex); } }
public static void PostInitialize(MutexEx masterMutex) { MutexEx.Leave(_GlobalStatics.InitMutex); // Go back under the static mutex and clean up the recursive mutex to prevent a resource leak. MutexEx.Enter(masterMutex); _GlobalStatics.InitMutexRefs--; if (_GlobalStatics.InitMutexRefs <= 0) { Debug.Assert(_GlobalStatics.InitMutexRefs == 0); MutexEx.Free(_GlobalStatics.InitMutex); _GlobalStatics.InitMutex.Tag = null; } MutexEx.Leave(masterMutex); }
public static void PutRandom(int length, ref long bufferIdx) { bufferIdx = 0; byte[] b = new byte[length]; #if THREADSAFE MutexEx mutex = MutexEx.Alloc(MutexEx.MUTEX.STATIC_PRNG); MutexEx.Enter(mutex); #endif while (length-- > 0) { bufferIdx = (uint)((bufferIdx << 8) + RandomByte()); } #if THREADSAFE MutexEx.Leave(mutex); #endif }
public static RC Reset(Vdbe p) { if (p == null) { return(RC.OK); } #if THREADSAFE MutexEx mutex = p.Ctx.Mutex; #endif MutexEx.Enter(mutex); RC rc = p.Reset(); p.Rewind(); Debug.Assert((rc & (RC)p.Ctx.ErrMask) == rc); rc = SysEx.ApiExit(p.Ctx, rc); MutexEx.Leave(mutex); return(rc); }
public static void PutRandom(int length, byte[] buffer, int offset) { long bufferIdx = System.DateTime.Now.Ticks; #if THREADSAFE MutexEx mutex = MutexEx.Alloc(MutexEx.MUTEX.STATIC_PRNG); MutexEx.Enter(mutex); #endif while (length-- > 0) { bufferIdx = (uint)((bufferIdx << 8) + RandomByte()); buffer[offset++] = (byte)bufferIdx; } #if THREADSAFE MutexEx.Leave(mutex); #endif }
public RC ClearBindings(Vdbe p) { #if THREADSAFE MutexEx mutex = Ctx.Mutex; #endif MutexEx.Enter(mutex); for (int i = 0; i < Vars.length; i++) { MemRelease(p.Vars[i]); p.Vars[i].Flags = MEM.Null; } if (p.IsPrepareV2 && p.Expmask != 0) { p.Expired = true; } MutexEx.Leave(mutex); return(RC.OK); }
public static RC RegisterVfs(VSystem vfs, bool default_, Func <VFile> createOsFile) { var mutex = MutexEx.Alloc(MutexEx.MUTEX.STATIC_MASTER); MutexEx.Enter(mutex); UnlinkVfs(vfs); vfs.CreateOsFile = createOsFile; if (default_ || _vfsList == null) { vfs.Next = _vfsList; _vfsList = vfs; } else { vfs.Next = _vfsList.Next; _vfsList.Next = vfs; } Debug.Assert(_vfsList != null); MutexEx.Leave(mutex); return(RC.OK); }
public static RC LockAndPrepare(Context ctx, string sql, int bytes, bool isPrepareV2, Vdbe reprepare, ref Vdbe stmtOut, ref string tailOut) { if (!sqlite3SafetyCheckOk(ctx)) { stmtOut = null; tailOut = null; return(SysEx.MISUSE_BKPT()); } MutexEx.Enter(ctx.Mutex); Btree.EnterAll(ctx); RC rc = Prepare_(ctx, sql, bytes, isPrepareV2, reprepare, ref stmtOut, ref tailOut); if (rc == RC.SCHEMA) { stmtOut.Finalize(); rc = Prepare_(ctx, sql, bytes, isPrepareV2, reprepare, ref stmtOut, ref tailOut); } Btree.LeaveAll(ctx); MutexEx.Leave(ctx.Mutex); Debug.Assert(rc == RC.OK || stmtOut == null); return(rc); }
// Routines used to attach values to wildcards in a compiled SQL statement. static RC VdbeUnbind(Vdbe p, int i) { if (VdbeSafetyNotNull(p)) { return(SysEx.MISUSE_BKPT()); } MutexEx.Enter(p.Ctx.Mutex); if (p.Magic != VDBE_MAGIC_RUN || p.PC >= 0) { SysEx.Error(p.Ctx, RC.MISUSE, 0); MutexEx.Leave(p.Ctx.Mutex); SysEx.LOG(RC.MISUSE, "bind on a busy prepared statement: [%s]", p.Sql); return(SysEx.MISUSE_BKPT()); } if (i < 1 || i > p.Vars) { SysEx.Error(p.Ctx, RC.RANGE, 0); MutexEx.Leave(p.Ctx.Mutex); return(RC.RANGE); } i--; Mem var = p.Vars[i]; MemRelease(var); var.Flags = MEM.Null; SysEx.Error(p.Ctx, RC.OK, 0); // If the bit corresponding to this variable in Vdbe.expmask is set, then binding a new value to this variable invalidates the current query plan. // // IMPLEMENTATION-OF: R-48440-37595 If the specific value bound to host parameter in the WHERE clause might influence the choice of query plan // for a statement, then the statement will be automatically recompiled, as if there had been a schema change, on the first sqlite3_step() call // following any change to the bindings of that parameter. if (p.IsPrepareV2 && ((i < 32 && p.Expmask != 0 & ((uint)1 << i) != 0) || p.Expmask == 0xffffffff)) { p.Expired = true; } return(RC.OK); }
public static RC CreateModule(Context ctx, string name, ITableModule imodule, object aux, Action <object> destroy) { RC rc = RC.OK; MutexEx.Enter(ctx.Mutex); int nameLength = name.Length; if (ctx.Modules.Find(name, nameLength, (TableModule)null) != null) { rc = SysEx.MISUSE_BKPT(); } else { TableModule module = new TableModule(); //: _tagalloc(ctx, sizeof(TableModule) + nameLength + 1) if (module != null) { var nameCopy = name; module.Name = nameCopy; module.IModule = imodule; module.Aux = aux; module.Destroy = destroy; TableModule del = (TableModule)ctx.Modules.Insert(nameCopy, nameLength, module); Debug.Assert(del == null && del == module); if (del != null) { ctx.MallocFailed = true; C._tagfree(ctx, ref del); } } } rc = SysEx.ApiExit(ctx, rc); if (rc != RC.OK && destroy != null) { destroy(aux); } MutexEx.Leave(ctx.Mutex); return(rc); }
public static string ColumnName(Vdbe p, int n, Func <Mem, string> func, bool useType) { Context ctx = p.Ctx; Debug.Assert(ctx != null); string r = null; int n2 = Column_Count(p); if (n < n2 && n >= 0) { n += (useType ? n2 : 0); MutexEx.Enter(ctx.Mutex); Debug.Assert(ctx.MallocFailed); r = func(p.ColNames[n]); // A malloc may have failed inside of the xFunc() call. If this is the case, clear the mallocFailed flag and return NULL. if (ctx.MallocFailed) { ctx.MallocFailed = false; r = null; } MutexEx.Leave(ctx.Mutex); } return(r); }
public RC Step() { RC rc = RC.OK; // Result from sqlite3Step() RC rc2 = RC.OK; // Result from sqlite3Reprepare() int cnt = 0; // Counter to prevent infinite loop of reprepares Context ctx = Ctx; // The database connection MutexEx.Enter(ctx.Mutex); DoingRerun = false; while ((rc = Step2()) == RC.SCHEMA && cnt++ < MAX_SCHEMA_RETRY && (rc2 = rc = Reprepare()) == RC.OK) { Reset(this); DoingRerun = true; Debug.Assert(!Expired); } if (rc2 != RC.OK && C._ALWAYS(IsPrepareV2) && C._ALWAYS(ctx.Err != null)) { // This case occurs after failing to recompile an sql statement. The error message from the SQL compiler has already been loaded // into the database handle. This block copies the error message from the database handle into the statement and sets the statement // program counter to 0 to ensure that when the statement is finalized or reset the parser error message is available via // sqlite3_errmsg() and sqlite3_errcode(). string err = Value_Text(ctx.Err); C._tagfree(ctx, ref ErrMsg); if (!ctx.MallocFailed) { ErrMsg = err; RC_ = rc2; } else { ErrMsg = null; RC_ = rc = RC.NOMEM; } } rc = SysEx.ApiExit(ctx, rc); MutexEx.Leave(ctx.Mutex); return(rc); }
public static void AutoLoadExtensions(Context ctx) { if (g_autoext.ExtsLength == 0) { return; // Common case: early out without every having to acquire a mutex } bool go = true; for (int i = 0; go; i++) { string errmsg = null; #if THREADSAFE MutexEx mutex = MutexEx.Alloc(MutexEx.MUTEX.STATIC_MASTER); #endif MutexEx.Enter(mutex); Func <Context, string, core_api_routines, RC> init; if (i >= g_autoext.ExtsLength) { init = null; go = false; } else { init = g_autoext.Exts[i]; } MutexEx.Leave(mutex); errmsg = null; RC rc; if (init != null && (rc = init(ctx, errmsg, g_apis)) != 0) { Error(ctx, rc, "automatic extension loading failed: %s", errmsg); go = false; } C._tagfree(ctx, ref errmsg); } }
static RC AutoExtension(Func <Context, string, core_api_routines, RC> init) { RC rc = RC.OK; #if !OMIT_AUTOINIT rc = Initialize(); if (rc != 0) { return(rc); } else #endif { #if THREADSAFE MutexEx mutex = MutexEx.Alloc(MutexEx.MUTEX.STATIC_MASTER); #endif MutexEx.Enter(mutex); int i; for (i = 0; i < g_autoext.ExtsLength; i++) { if (g_autoext.Exts[i] == init) { break; } } if (i == g_autoext.ExtsLength) { Array.Resize(ref g_autoext.Exts, g_autoext.ExtsLength + 1); g_autoext.Exts[g_autoext.ExtsLength] = init; g_autoext.ExtsLength++; } MutexEx.Leave(mutex); Debug.Assert((rc & (RC)0xff) == rc); return(rc); } }
public RC Step(int pages) { MutexEx.Enter(SrcCtx.Mutex); Src.Enter(); if (DestCtx != null) MutexEx.Enter(DestCtx.Mutex); RC rc = RC_; if (!IsFatalError(rc)) { Pager srcPager = Src.get_Pager(); // Source pager Pager destPager = Dest.get_Pager(); // Dest pager Pid srcPage = 0; // Size of source db in pages bool closeTrans = false; // True if src db requires unlocking // If the source pager is currently in a write-transaction, return SQLITE_BUSY immediately. rc = (DestCtx != null && Src.Bt.InTransaction == TRANS.WRITE ? RC.BUSY : RC.OK); // Lock the destination database, if it is not locked already. if (rc == RC.OK && !DestLocked && (rc = Dest.BeginTrans(2)) == RC.OK) { DestLocked = true; Dest.GetMeta(Btree.META.SCHEMA_VERSION, ref DestSchema); } // If there is no open read-transaction on the source database, open one now. If a transaction is opened here, then it will be closed // before this function exits. if (rc == RC.OK && !Src.IsInReadTrans()) { rc = Src.BeginTrans(0); closeTrans = true; } // Do not allow backup if the destination database is in WAL mode and the page sizes are different between source and destination int pgszSrc = Src.GetPageSize(); // Source page size int pgszDest = Dest.GetPageSize(); // Destination page size IPager.JOURNALMODE destMode = Dest.get_Pager().GetJournalMode(); // Destination journal mode if (rc == RC.OK && destMode == IPager.JOURNALMODE.WAL && pgszSrc != pgszDest) rc = RC.READONLY; // Now that there is a read-lock on the source database, query the source pager for the number of pages in the database. srcPage = Src.LastPage(); Debug.Assert(srcPage >= 0); for (int ii = 0; (pages < 0 || ii < pages) && NextId <= (Pid)srcPage && rc == 0; ii++) { Pid srcPg = NextId; // Source page number if (srcPg != Btree.PENDING_BYTE_PAGE(Src.Bt)) { IPage srcPgAsObj = null; // Source page object rc = srcPager.Acquire(srcPg, ref srcPgAsObj, false); if (rc == RC.OK) { rc = BackupOnePage(p, srcPg, Pager.GetData(srcPgAsObj), false); Pager.Unref(srcPgAsObj); } } NextId++; } if (rc == RC.OK) { Pagecount = srcPage; Remaining = (srcPage + 1 - NextId); if (NextId > srcPage) rc = RC.DONE; else if (!IsAttached) AttachBackupObject(p); } // Update the schema version field in the destination database. This is to make sure that the schema-version really does change in // the case where the source and destination databases have the same schema version. if (rc == RC.DONE) { if (srcPage == null) { rc = Dest.NewDb(); srcPage = 1; } if (rc == RC.OK || rc == RC.DONE) rc = Dest.UpdateMeta(Btree.META.SCHEMA_VERSION, DestSchema + 1); if (rc == RC.OK) { if (DestCtx != null) Main.ResetAllSchemasOfConnection(DestCtx); if (destMode == IPager.JOURNALMODE.WAL) rc = Dest.SetVersion(2); } if (rc == RC.OK) { // Set nDestTruncate to the final number of pages in the destination database. The complication here is that the destination page // size may be different to the source page size. // // If the source page size is smaller than the destination page size, round up. In this case the call to sqlite3OsTruncate() below will // fix the size of the file. However it is important to call sqlite3PagerTruncateImage() here so that any pages in the // destination file that lie beyond the nDestTruncate page mark are journalled by PagerCommitPhaseOne() before they are destroyed // by the file truncation. Debug.Assert(pgszSrc == Src.GetPageSize()); Debug.Assert(pgszDest == Dest.GetPageSize()); Pid destTruncate; if (pgszSrc < pgszDest) { int ratio = pgszDest / pgszSrc; destTruncate = (Pid)((srcPage + ratio - 1) / ratio); if (destTruncate == Btree.PENDING_BYTE_PAGE(Dest.Bt)) destTruncate--; } else destTruncate = (Pid)(srcPage * (pgszSrc / pgszDest)); Debug.Assert(destTruncate > 0); if (pgszSrc < pgszDest) { // If the source page-size is smaller than the destination page-size, two extra things may need to happen: // // * The destination may need to be truncated, and // // * Data stored on the pages immediately following the pending-byte page in the source database may need to be // copied into the destination database. int size = (int)(pgszSrc * srcPage); VFile file = destPager.get_File(); Debug.Assert(file != null); Debug.Assert((long)destTruncate * (long)pgszDest >= size || (destTruncate == (int)(Btree.PENDING_BYTE_PAGE(Dest.Bt) - 1) && size >= VFile.PENDING_BYTE && size <= VFile.PENDING_BYTE + pgszDest)); // This block ensures that all data required to recreate the original database has been stored in the journal for pDestPager and the // journal synced to disk. So at this point we may safely modify the database file in any way, knowing that if a power failure // occurs, the original database will be reconstructed from the journal file. uint dstPage; destPager.Pages(out dstPage); for (Pid pg = destTruncate; rc == RC.OK && pg <= (Pid)dstPage; pg++) { if (pg != Btree.PENDING_BYTE_PAGE(Dest.Bt)) { IPage pgAsObj; rc = destPager.Acquire(pg, ref pgAsObj, false); if (rc == RC.OK) { rc = Pager.Write(pgAsObj); Pager.Unref(pgAsObj); } } } if (rc == RC.OK) rc = destPager.CommitPhaseOne(null, true); // Write the extra pages and truncate the database file as required. long end = Math.Min(VFile.PENDING_BYTE + pgszDest, size); for (long off = VFile.PENDING_BYTE + pgszSrc; rc == RC.OK && off < end; off += pgszSrc) { Pid srcPg = (Pid)((off / pgszSrc) + 1); PgHdr srcPgAsObj = null; rc = srcPager.Acquire(srcPg, ref srcPgAsObj, false); if (rc == RC.OK) { byte[] data = Pager.GetData(srcPgAsObj); rc = file.Write(data, pgszSrc, off); } Pager.Unref(srcPgAsObj); } if (rc == RC.OK) rc = BackupTruncateFile(file, (int)size); // Sync the database file to disk. if (rc == RC.OK) rc = destPager.Sync(); } else { destPager.TruncateImage(destTruncate); rc = destPager.CommitPhaseOne(null, false); } // Finish committing the transaction to the destination database. if (rc == RC.OK && (rc = Dest.CommitPhaseTwo(false)) == RC.OK) rc = RC.DONE; } } // If bCloseTrans is true, then this function opened a read transaction on the source database. Close the read transaction here. There is // no need to check the return values of the btree methods here, as "committing" a read-only transaction cannot fail. if (closeTrans) { #if !DEBUG || COVERAGE_TEST RC rc2 = Src.CommitPhaseOne(null); rc2 |= Src.CommitPhaseTwo(false); Debug.Assert(rc2 == RC.OK); #else Src.CommitPhaseOne(null); Src.CommitPhaseTwo(false); #endif } if (rc == RC.IOERR_NOMEM) rc = RC.NOMEM; RC_ = rc; } if (DestCtx != null) MutexEx.Leave(DestCtx.Mutex); Src.Leave(); MutexEx.Leave(SrcCtx.Mutex); return rc; }
public static RC PreInitialize(out MutexEx masterMutex) { masterMutex = default(MutexEx); // If SQLite is already completely initialized, then this call to sqlite3_initialize() should be a no-op. But the initialization // must be complete. So isInit must not be set until the very end of this routine. if (_GlobalStatics.IsInit) { return(RC.OK); } // The following is just a sanity check to make sure SQLite has been compiled correctly. It is important to run this code, but // we don't want to run it too often and soak up CPU cycles for no reason. So we run it once during initialization. #if !NDEBUG && !OMIT_FLOATING_POINT // This section of code's only "output" is via assert() statements. //ulong x = (((ulong)1)<<63)-1; //double y; //Debug.Assert(sizeof(ulong) == 8); //Debug.Assert(sizeof(ulong) == sizeof(double)); //_memcpy<void>(&y, &x, 8); //Debug.Assert(double.IsNaN(y)); #endif RC rc; #if ENABLE_SQLLOG { Init_Sqllog(); } #endif // Make sure the mutex subsystem is initialized. If unable to initialize the mutex subsystem, return early with the error. // If the system is so sick that we are unable to allocate a mutex, there is not much SQLite is going to be able to do. // The mutex subsystem must take care of serializing its own initialization. rc = RC.OK; //_mutex_init(); if (rc != 0) { return(rc); } // Initialize the malloc() system and the recursive pInitMutex mutex. This operation is protected by the STATIC_MASTER mutex. Note that // MutexAlloc() is called for a static mutex prior to initializing the malloc subsystem - this implies that the allocation of a static // mutex must not require support from the malloc subsystem. masterMutex = MutexEx.Alloc(MutexEx.MUTEX.STATIC_MASTER); // The main static mutex MutexEx.Enter(masterMutex); _GlobalStatics.IsMutexInit = true; //if (!SysEx_GlobalStatics.IsMallocInit) // rc = sqlite3MallocInit(); if (rc == RC.OK) { _GlobalStatics.IsMallocInit = true; if (_GlobalStatics.InitMutex.Tag == null) { _GlobalStatics.InitMutex = MutexEx.Alloc(MutexEx.MUTEX.RECURSIVE); if (_GlobalStatics.CoreMutex && _GlobalStatics.InitMutex.Tag == null) { rc = RC.NOMEM; } } } if (rc == RC.OK) { _GlobalStatics.InitMutexRefs++; } MutexEx.Leave(masterMutex); // If rc is not SQLITE_OK at this point, then either the malloc subsystem could not be initialized or the system failed to allocate // the pInitMutex mutex. Return an error in either case. if (rc != RC.OK) { return(rc); } // Do the rest of the initialization under the recursive mutex so that we will be able to handle recursive calls into // sqlite3_initialize(). The recursive calls normally come through sqlite3_os_init() when it invokes sqlite3_vfs_register(), but other // recursive calls might also be possible. // // IMPLEMENTATION-OF: R-00140-37445 SQLite automatically serializes calls to the xInit method, so the xInit method need not be threadsafe. // // The following mutex is what serializes access to the appdef pcache xInit methods. The sqlite3_pcache_methods.xInit() all is embedded in the // call to sqlite3PcacheInitialize(). MutexEx.Enter(_GlobalStatics.InitMutex); if (!_GlobalStatics.IsInit && !_GlobalStatics.InProgress) { _GlobalStatics.InProgress = true; rc = VSystem.Initialize(); } if (rc != RC.OK) { MutexEx.Leave(_GlobalStatics.InitMutex); } return(rc); }
public static RC Exec(Context ctx, string sql, Func <object, int, string[], string[], bool> callback, object arg, ref string errmsg) { RC rc = RC.OK; // Return code if (!SafetyCheckOk(ctx)) { return(SysEx.MISUSE_BKPT()); } if (sql == null) { sql = string.Empty; } MutexEx.Enter(ctx.Mutex); Error(ctx, RC.OK, null); Vdbe stmt = null; // The current SQL statement int retrys = 0; // Number of retry attempts string[] colsNames = null; // Names of result columns while ((rc == RC.OK || (rc == RC.SCHEMA && (++retrys) < 2)) && sql.Length > 0) { stmt = null; string leftover = null; // Tail of unprocessed SQL rc = Prepare.Prepare_(ctx, sql, -1, ref stmt, ref leftover); Debug.Assert(rc == RC.OK || stmt == null); if (rc != RC.OK) { continue; } if (stmt == null) { sql = leftover; // this happens for a comment or white-space continue; } bool callbackIsInit = false; // True if callback data is initialized int cols = Vdbe.Column_Count(stmt); while (true) { rc = stmt.Step(); // Invoke the callback function if required int i; if (callback != null && (rc == RC.ROW || (rc == RC.DONE && !callbackIsInit && (ctx.Flags & Context.FLAG.NullCallback) != 0))) { if (!callbackIsInit) { colsNames = new string[cols]; if (colsNames == null) { goto exec_out; } for (i = 0; i < cols; i++) { colsNames[i] = Vdbe.Column_Name(stmt, i); // Vdbe::SetColName() installs column names as UTF8 strings so there is no way for sqlite3_column_name() to fail. Debug.Assert(colsNames[i] != null); } callbackIsInit = true; } string[] colsValues = null; if (rc == RC.ROW) { colsValues = new string[cols]; for (i = 0; i < cols; i++) { colsValues[i] = Vdbe.Column_Text(stmt, i); if (colsValues[i] == null && Vdbe.Column_Type(stmt, i) != TYPE.NULL) { ctx.MallocFailed = true; goto exec_out; } } } if (callback(arg, cols, colsValues, colsNames)) { rc = RC.ABORT; stmt.Finalize(); stmt = null; Error(ctx, RC.ABORT, null); goto exec_out; } } if (rc != RC.ROW) { rc = stmt.Finalize(); stmt = null; if (rc != RC.SCHEMA) { retrys = 0; if ((sql = leftover) != string.Empty) { int idx = 0; while (idx < sql.Length && char.IsWhiteSpace(sql[idx])) { idx++; } if (idx != 0) { sql = (idx < sql.Length ? sql.Substring(idx) : string.Empty); } } } break; } } C._tagfree(ctx, ref colsNames); colsNames = null; } exec_out: if (stmt != null) { stmt.Finalize(); } C._tagfree(ctx, ref colsNames); rc = ApiExit(ctx, rc); if (rc != RC.OK && C._ALWAYS(rc == ErrCode(ctx)) && errmsg != null) { errmsg = ErrMsg(ctx); } else if (errmsg != null) { errmsg = null; } Debug.Assert((rc & (RC)ctx.ErrMask) == rc); MutexEx.Leave(ctx.Mutex); return(rc); }