Exemple #1
0
        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;
        }
Exemple #2
0
 static RC SetDestPgsz(Backup p)
 {
     RC rc = p.Dest.SetPageSize(p.Src.GetPageSize(), -1, false);
     return rc;
 }
Exemple #3
0
 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;
 }
Exemple #4
0
        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;
        }
Exemple #5
0
        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;
        }