public static RC Prepare16(Context ctx, string sql, int bytes, bool isPrepareV2, out Vdbe stmtOut, out string tailOut) { // This function currently works by first transforming the UTF-16 encoded string to UTF-8, then invoking sqlite3_prepare(). The // tricky bit is figuring out the pointer to return in *pzTail. stmtOut = null; if (!sqlite3SafetyCheckOk(ctx)) { return(SysEx.MISUSE_BKPT()); } MutexEx.Enter(ctx.Mutex); RC rc = RC.OK; string tail8 = null; string sql8 = Vdbe.Utf16to8(ctx, sql, bytes, TEXTENCODE.UTF16NATIVE); if (sql8 != null) { rc = LockAndPrepare(ctx, sql8, -1, isPrepareV2, null, ref stmtOut, ref tail8); } if (tail8 != null && tailOut != null) { // If sqlite3_prepare returns a tail pointer, we calculate the equivalent pointer into the UTF-16 string by counting the unicode // characters between zSql8 and zTail8, and then returning a pointer the same number of characters into the UTF-16 string. Debugger.Break(); //: int charsParsed = Vdbe::Utf8CharLen(sql8, (int)(tail8 - sql8)); //: *tailOut = (uint8 *)sql + Vdbe::Utf16ByteLen(sql, charsParsed); } C._tagfree(ctx, ref sql8); rc = SysEx.ApiExit(ctx, rc); MutexEx.Leave(ctx.Mutex); return(rc); }
public static RC Config(Context ctx, VTABLECONFIG op, object arg1) { RC rc = RC.OK; MutexEx.Enter(ctx.Mutex); switch (op) { case VTABLECONFIG.CONSTRAINT: { VTableContext p = ctx.VTableCtx; if (p == null) { rc = SysEx.MISUSE_BKPT(); } else { Debug.Assert(p.Table == null || (p.Table.TabFlags & TF.Virtual) != 0); p.VTable.Constraint = (bool)arg1; } break; } default: rc = SysEx.MISUSE_BKPT(); break; } if (rc != RC.OK) { Main.Error(ctx, rc, null); } MutexEx.Leave(ctx.Mutex); return(rc); }
public static RC DeclareVTable(Context ctx, string createTableName) { MutexEx.Enter(ctx.Mutex); Table table; if (ctx.VTableCtx == null || (table = ctx.VTableCtx.Table) == null) { sqlite3Error(ctx, RC.MISUSE, null); MutexEx.Leave(ctx.Mutex); return(SysEx.MISUSE_BKPT()); } Debug.Assert((table.TabFlags & TF.Virtual) != 0); RC rc = RC.OK; Parse parse = new Parse(); //: _stackalloc(ctx, sizeof(Parse)); if (parse == null) { rc = RC.NOMEM; } else { parse.DeclareVTable = true; parse.Ctx = ctx; parse.QueryLoops = 1; string error = null; if (sqlite3RunParser(parse, createTableName, ref error) == RC.OK && parse.NewTable != null && !ctx.MallocFailed && parse.NewTable.Select == null && (parse.NewTable.TabFlags & TF.Virtual) == 0) { if (table.Cols.data == null) { table.Cols.data = parse.NewTable.Cols.data; table.Cols.length = parse.NewTable.Cols.length; parse.NewTable.Cols.length = 0; parse.NewTable.Cols.data = null; } ctx.VTableCtx.Table = null; } else { sqlite3Error(ctx, RC.ERROR, (error != null ? "%s" : null), error); C._tagfree(ctx, ref error); rc = RC.ERROR; } parse.DeclareVTable = false; if (parse.V != null) { parse.V.Finalize(); } Parse.DeleteTable(ctx, ref parse.NewTable); parse = null; //: C._stackfree(ctx, parse); } Debug.Assert(((int)rc & 0xff) == (int)rc); rc = SysEx.ApiExit(ctx, rc); MutexEx.Leave(ctx.Mutex); return(rc); }
public static Vdbe Stmt_Next(Context ctx, Vdbe p) { MutexEx.Enter(ctx.Mutex); Vdbe next = (p == null ? ctx.Vdbes[0] : p.Next); MutexEx.Leave(ctx.Mutex); return(next); }
public RC SetAuthorizer(Context ctx, Func <object, int, string, string, string, string, ARC> auth, object arg) { MutexEx.Enter(ctx.Mutex); ctx.Auth = auth; ctx.AuthArg = arg; Vdbe.ExpirePreparedStatements(ctx); MutexEx.Leave(ctx.Mutex); return(RC.OK); }
public static RC LoadExtension(Context ctx, string fileName, string procName, ref string errMsg) { MutexEx.Enter(ctx.Mutex); RC rc = LoadExtension_(ctx, fileName, procName, ref errMsg); rc = ApiExit(ctx, rc); MutexEx.Leave(ctx.Mutex); return(rc); }
public static RC UnregisterVfs(VSystem vfs) { var mutex = MutexEx.Alloc(MutexEx.MUTEX.STATIC_MASTER); MutexEx.Enter(mutex); UnlinkVfs(vfs); MutexEx.Leave(mutex); return(RC.OK); }
public static VSystem FindVfs(string name) { VSystem vfs = null; var mutex = MutexEx.Alloc(MutexEx.MUTEX.STATIC_MASTER); MutexEx.Enter(mutex); for (vfs = _vfsList; vfs != null && name != vfs.Name; vfs = vfs.Next) { } MutexEx.Leave(mutex); return(vfs); }
public static RC TransferBindings(Vdbe from, Vdbe to) { Debug.Assert(to.Ctx == from.Ctx); Debug.Assert(to.Vars.length == from.Vars.length); MutexEx.Enter(to.Ctx.Mutex); for (int i = 0; i < from.Vars.length; i++) { MemMove(to.Vars[i], from.Vars[i]); } MutexEx.Leave(to.Ctx.Mutex); return(RC.OK); }
public static RC EnableLoadExtension(Context ctx, bool onoff) { MutexEx.Enter(ctx.Mutex); if (onoff) { ctx.Flags |= Context.FLAG.LoadExtension; } else { ctx.Flags &= ~Context.FLAG.LoadExtension; } MutexEx.Leave(ctx.Mutex); return(RC.OK); }
public static Backup Init(Context destCtx, string destDbName, Context srcCtx, string srcDbName) { // Lock the source database handle. The destination database handle is not locked in this routine, but it is locked in // sqlite3_backup_step(). The user is required to ensure that no other thread accesses the destination handle for the duration // of the backup operation. Any attempt to use the destination database connection while a backup is in progress may cause // a malfunction or a deadlock. MutexEx.Enter(srcCtx.Mutex); MutexEx.Enter(destCtx.Mutex); Backup p; if (srcCtx == destCtx) { Main.Error(destCtx, RC.ERROR, "source and destination must be distinct"); p = null; } else { // Allocate space for a new sqlite3_backup object... EVIDENCE-OF: R-64852-21591 The sqlite3_backup object is created by a // call to sqlite3_backup_init() and is destroyed by a call to sqlite3_backup_finish(). p = new Backup(); if (p == null) Main.Error(destCtx, RC.NOMEM, null); } // If the allocation succeeded, populate the new object. if (p != null) { p.Src = FindBtree(destCtx, srcCtx, srcDbName); p.Dest = FindBtree(destCtx, destCtx, destDbName); p.DestCtx = destCtx; p.SrcCtx = srcCtx; p.NextId = 1; p.IsAttached = false; if (p.Src == null || p.Dest == null || SetDestPgsz(p) == RC.NOMEM) { // One (or both) of the named databases did not exist or an OOM error was hit. The error has already been written into the // pDestDb handle. All that is left to do here is free the sqlite3_backup structure. //C._free(ref p); p = null; } } if (p != null) p.Src.Backups++; MutexEx.Leave(destCtx.Mutex); MutexEx.Leave(srcCtx.Mutex); return p; }
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 Finalize(Vdbe p) { if (p == null) { return(RC.OK); // IMPLEMENTATION-OF: R-57228-12904 Invoking sqlite3_finalize() on a NULL pointer is a harmless no-op. } Context ctx = p.Ctx; if (VdbeSafety(p)) { return(SysEx.MISUSE_BKPT()); } MutexEx.Enter(ctx.Mutex); RC rc = p.Finalize(); rc = SysEx.ApiExit(ctx, rc); DataEx.LeaveMutexAndCloseZombie(ctx); return(rc); }
static Mem ColumnMem(Vdbe p, int i) { Mem r; if (p != null && p.ResultSet != null && i < p.ResColumns && i >= 0) { MutexEx.Enter(p.Ctx.Mutex); r = p.ResultSet[i]; } else { if (p != null && C._ALWAYS(p.Ctx != null)) { MutexEx.Enter(p.Ctx.Mutex); SysEx.Error(p.Ctx, RC.RANGE, 0); } r = _nullMem; } return(r); }
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); }
public static RC Finish(Backup p) { // Enter the mutexes if (p == null) return RC.OK; Context srcCtx = p.SrcCtx; // Source database connection MutexEx.Enter(srcCtx.Mutex); p.Src.Enter(); if (p.DestCtx != null) MutexEx.Enter(p.DestCtx.Mutex); // Detach this backup from the source pager. if (p.DestCtx != null) p.Src.Backups--; if (p.IsAttached) { Backup pp = (Backup)p.Src.get_Pager().Backup; while (pp != p) pp = (pp).Next; p.Src.get_Pager().Backup = p.Next; } // If a transaction is still open on the Btree, roll it back. p.Dest.Rollback(RC.OK); // Set the error code of the destination database handle. RC rc = (p.RC_ == RC.DONE ? RC.OK : p.RC_); Main.Error(p.DestCtx, rc, null); // Exit the mutexes and free the backup context structure. if (p.DestCtx != null) Main.LeaveMutexAndCloseZombie(p.DestCtx.Mutex); p.Src.Leave(); //// EVIDENCE-OF: R-64852-21591 The sqlite3_backup object is created by a call to sqlite3_backup_init() and is destroyed by a call to sqlite3_backup_finish(). //if (p.DestCtx != null) // C._free(ref p); Main.LeaveMutexAndCloseZombie(mutex); 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 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() { 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 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 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); }
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; }