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; }
static RC SetDestPgsz(Backup p) { RC rc = p.Dest.SetPageSize(p.Src.GetPageSize(), -1, false); return rc; }
static void AttachBackupObject(Backup p) { Debug.Assert(p.Src.HoldsMutex()); Backup pp = (Backup)p.Src.get_Pager().Backup; p.Next = pp; p.Src.get_Pager().Backup = p; p.IsAttached = true; }
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; }
static RC BackupOnePage(Backup p, Pid srcPg, byte[] srcData, bool update) { Pager destPager = p.Dest.get_Pager(); int srcPgsz = p.Src.GetPageSize(); int destPgsz = p.Dest.GetPageSize(); int copy = Math.Min(srcPgsz, destPgsz); long end = (long)srcPg * (long)srcPgsz; RC rc = RC.OK; Debug.Assert(p.Src.GetReserveNoMutex() >= 0); Debug.Assert(p.DestLocked); Debug.Assert(!IsFatalError(p.RC_)); Debug.Assert(srcPg != Btree.PENDING_BYTE_PAGE(p.Src.Bt)); Debug.Assert(srcData != null); // Catch the case where the destination is an in-memory database and the page sizes of the source and destination differ. if (srcPgsz != destPgsz && destPager.get_MemoryDB) rc = RC.READONLY; #if HAS_CODEC int srcReserve = p.Src.GetReserveNoMutex(); int destReserve = p.Dest.GetReserve(); // Backup is not possible if the page size of the destination is changing and a codec is in use. if (srcPgsz != destPgsz && Pager.GetCodec(destPager) != null) rc = RC.READONLY; // Backup is not possible if the number of bytes of reserve space differ between source and destination. If there is a difference, try to // fix the destination to agree with the source. If that is not possible, then the backup cannot proceed. if (srcReserve != destReserve) { uint newPgsz = (uint)srcPgsz; rc = destPager.SetPageSize(ref newPgsz, srcReserve); if (rc == RC.OK && newPgsz != srcPgsz) rc = RC.READONLY; } #endif // This loop runs once for each destination page spanned by the source page. For each iteration, variable iOff is set to the byte offset // of the destination page. for (long off = end - (long)srcPgsz; rc == RC.OK && off < end; off += destPgsz) { IPage destPg = null; uint dest = (uint)(off / destPgsz) + 1; if (dest == Btree.PENDING_BYTE_PAGE(p.Dest.Bt)) continue; if ((rc = destPager.Acquire(dest, ref destPg, false)) == RC.OK && (rc = Pager.Write(destPg)) == RC.OK) { byte[] destData = Pager.GetData(destPg); // Copy the data from the source page into the destination page. Then clear the Btree layer MemPage.isInit flag. Both this module // and the pager code use this trick (clearing the first byte of the page 'extra' space to invalidate the Btree layers // cached parse of the page). MemPage.isInit is marked "MUST BE FIRST" for this purpose. Buffer.BlockCopy(srcData, (int)(off % srcPgsz), destData, (int)(off % destPgsz), copy); Pager.GetExtra(destPg).IsInit = false; } Pager.Unref(destPg); } return rc; }