Exemple #1
0
        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);
        }
Exemple #2
0
        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);
        }
Exemple #3
0
        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);
        }
Exemple #4
0
        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);
        }
Exemple #5
0
 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);
 }
Exemple #6
0
        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);
        }
Exemple #7
0
        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);
        }
Exemple #8
0
        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);
        }
Exemple #9
0
 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);
 }
Exemple #10
0
 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);
 }
Exemple #11
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 #12
0
        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);
            }
        }
Exemple #13
0
        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);
        }
Exemple #14
0
        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
        }
Exemple #15
0
        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);
        }
Exemple #16
0
        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
        }
Exemple #17
0
        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);
        }
Exemple #18
0
        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);
        }
Exemple #19
0
        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);
        }
Exemple #20
0
        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);
        }
Exemple #21
0
        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);
        }
Exemple #22
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 #23
0
        // 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);
        }
Exemple #24
0
        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);
        }
Exemple #25
0
        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);
            }
        }
Exemple #26
0
        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);
            }
        }
Exemple #27
0
        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);
        }
Exemple #28
0
        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);
        }
Exemple #29
0
        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);
        }
Exemple #30
0
        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;
        }