Beispiel #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);
        }
Beispiel #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);
        }
Beispiel #3
0
        static RC BindText(Vdbe p, int i, string z, int n, Action <string> del, TEXTENCODE encoding)
        {
            RC rc = VdbeUnbind(p, i);

            if (rc == RC.OK)
            {
                if (z != null)
                {
                    Mem var = p.Vars[i - 1];
                    rc = MemSetStr(var, z, n, encoding, del);
                    if (rc == RC.OK && encoding != 0)
                    {
                        rc = ChangeEncoding(var, E.CTXENCODE(p.Ctx));
                    }
                    SysEx.Error(p.Ctx, rc, 0);
                    rc = SysEx.ApiExit(p.Ctx, rc);
                }
                MutexEx.Leave(p.Ctx.Mutex);
            }
            else if (del != null)
            {
                del(z);
            }
            return(rc);
        }
Beispiel #4
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);
        }
Beispiel #5
0
 public static void Result_ErrorCode(FuncContext fctx, RC errCode)
 {
     fctx.IsError = errCode;
     if ((fctx.S.Flags & MEM.Null) != 0)
     {
         SetResultStrOrError(fctx, SysEx.ErrStr(errCode), -1, TEXTENCODE.UTF8, DESTRUCTOR_STATIC);
     }
 }
Beispiel #6
0
 static bool VdbeSafetyNotNull(Vdbe p)
 {
     if (p == null)
     {
         SysEx.LOG(RC.MISUSE, "API called with NULL prepared statement");
         return(true);
     }
     return(VdbeSafety(p));
 }
Beispiel #7
0
 static bool VdbeSafety(Vdbe p)
 {
     if (p.Ctx == null)
     {
         SysEx.LOG(RC.MISUSE, "API called with finalized prepared statement");
         return(true);
     }
     return(false);
 }
Beispiel #8
0
 static void ColumnMallocFailure(Vdbe p)
 {
     // If malloc() failed during an encoding conversion within an sqlite3_column_XXX API, then set the return code of the statement to
     // RC_NOMEM. The next call to _step() (if any) will return RC_ERROR and _finalize() will return NOMEM.
     if (p != null)
     {
         p.RC_ = SysEx.ApiExit(p.Ctx, p.RC_);
         MutexEx.Leave(p.Ctx.Mutex);
     }
 }
Beispiel #9
0
 public static RC Status(STATUS op, ref int current, ref int highwater, int resetFlag)
 {
     if (op < 0 || (int)op >= _stat.NowValue.Length)
     {
         return(SysEx.MISUSE_BKPT());
     }
     current   = _stat.NowValue[(int)op];
     highwater = _stat.MaxValue[(int)op];
     if (resetFlag != 0)
     {
         _stat.MaxValue[(int)op] = _stat.NowValue[(int)op];
     }
     return(RC.OK);
 }
Beispiel #10
0
        public static bool Complete16(string sql)
        {
            RC rc = RC.NOMEM;

#if !OMIT_AUTOINIT
            rc = SysEx.Initialize();
            if (rc != RC.OK)
            {
                return(rc);
            }
#endif
            Mem val = sqlite3ValueNew(0);
            sqlite3ValueSetStr(val, -1, sql, TEXTENCODE.UTF16NATIVE, DESTRUCTOR.STATIC);
            string sql8 = sqlite3ValueText(val, TEXTENCODE.UTF8);
            rc = (sql8 != null ? (RC)Complete(sql8) : RC.NOMEM);
Beispiel #11
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);
        }
Beispiel #12
0
        static void CorruptSchema(InitData data, string obj, string extra)
        {
            Context ctx = data.Ctx;

            if (!ctx.MallocFailed && (ctx.Flags & Context.FLAG.RecoveryMode) == 0)
            {
                if (obj == null)
                {
                    obj = "?";
                }
                C._setstring(ref data.ErrMsg, ctx, "malformed database schema (%s)", obj);
                if (extra == null)
                {
                    data.ErrMsg = C._mtagappendf(ctx, data.ErrMsg, "%s - %s", data.ErrMsg, extra);
                }
                data.RC = (ctx.MallocFailed ? RC.NOMEM : SysEx.CORRUPT_BKPT());
            }
        }
Beispiel #13
0
        static RC walIndexPage(Wal wal, Pid id, ref object idOut)
        {
            // Enlarge the pWal->apWiData[] array if required
            if (wal.WiData.Length <= id)
            {
                var bytes     = (int)(1 * (id + 1));
                var newWiData = SysEx.Realloc <object>(1, wal.WiData, bytes);
                if (newWiData != null)
                {
                    idOut = null;
                    return(RC.NOMEM);
                }
                Array.Clear(newWiData, wal.WiData.Length, (int)(id + 1 - wal.WiData.Length));
                wal.WiData = newWiData;
            }

            // Request a pointer to the required page from the VFS
            var rc = RC.OK;

            if (wal.WiData[id] == null)
            {
                if (wal.ExclusiveMode_ == Wal.MODE.HEAPMEMORY)
                {
                    wal.WiData[id] = C._alloc <Pid>(4, WALINDEX_PGSZ, true);
                    if (wal.WiData[id] != null)
                    {
                        rc = RC.NOMEM;
                    }
                }
                else
                {
                    rc = wal.DBFile.ShmMap((int)id, WALINDEX_PGSZ, (int)wal.WriteLock, wal.WiData, (int)id);
                    if (rc == RC.READONLY)
                    {
                        wal.ReadOnly_ |= READONLY.SHM_RDONLY;
                        rc             = RC.OK;
                    }
                }
            }

            idOut = wal.WiData[id];
            Debug.Assert(id == 0 || idOut != null || rc != RC.OK);
            return(rc);
        }
Beispiel #14
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);
        }
Beispiel #15
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);
        }
Beispiel #16
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);
        }
Beispiel #17
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);
        }
Beispiel #18
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);
        }
Beispiel #19
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);
        }
Beispiel #20
0
        public static RC Prepare_(Context ctx, string sql, int bytes, bool isPrepareV2, Vdbe reprepare, ref Vdbe stmtOut, ref string tailOut)
        {
            stmtOut = null;
            tailOut = null;
            string errMsg = null; // Error message
            RC     rc     = RC.OK;
            int    i;

            // Allocate the parsing context
            Parse parse = new Parse(); // Parsing context

            if (parse == null)
            {
                rc = RC.NOMEM;
                goto end_prepare;
            }
            parse.Reprepare      = reprepare;
            parse.LastToken.data = null; //: C#?
            Debug.Assert(tailOut == null);
            Debug.Assert(!ctx.MallocFailed);
            Debug.Assert(MutexEx.Held(ctx.Mutex));

            // Check to verify that it is possible to get a read lock on all database schemas.  The inability to get a read lock indicates that
            // some other database connection is holding a write-lock, which in turn means that the other connection has made uncommitted changes
            // to the schema.
            //
            // Were we to proceed and prepare the statement against the uncommitted schema changes and if those schema changes are subsequently rolled
            // back and different changes are made in their place, then when this prepared statement goes to run the schema cookie would fail to detect
            // the schema change.  Disaster would follow.
            //
            // This thread is currently holding mutexes on all Btrees (because of the sqlite3BtreeEnterAll() in sqlite3LockAndPrepare()) so it
            // is not possible for another thread to start a new schema change while this routine is running.  Hence, we do not need to hold
            // locks on the schema, we just need to make sure nobody else is holding them.
            //
            // Note that setting READ_UNCOMMITTED overrides most lock detection, but it does *not* override schema lock detection, so this all still
            // works even if READ_UNCOMMITTED is set.
            for (i = 0; i < ctx.DBs.length; i++)
            {
                Btree bt = ctx.DBs[i].Bt;
                if (bt != null)
                {
                    Debug.Assert(bt.HoldsMutex());
                    rc = bt.SchemaLocked();
                    if (rc != 0)
                    {
                        string dbName = ctx.DBs[i].Name;
                        sqlite3Error(ctx, rc, "database schema is locked: %s", dbName);
                        C.ASSERTCOVERAGE((ctx.Flags & Context.FLAG.ReadUncommitted) != 0);
                        goto end_prepare;
                    }
                }
            }

            VTable.UnlockList(ctx);

            parse.Ctx        = ctx;
            parse.QueryLoops = (double)1;
            if (bytes >= 0 && (bytes == 0 || sql[bytes - 1] != 0))
            {
                int maxLen = ctx.aLimit[SQLITE_LIMIT_SQL_LENGTH];
                C.ASSERTCOVERAGE(bytes == maxLen);
                C.ASSERTCOVERAGE(bytes == maxLen + 1);
                if (bytes > maxLen)
                {
                    sqlite3Error(ctx, RC.TOOBIG, "statement too long");
                    rc = SysEx.ApiExit(ctx, RC.TOOBIG);
                    goto end_prepare;
                }
                string sqlCopy = sql.Substring(0, bytes);
                if (sqlCopy != null)
                {
                    parse.RunParser(sqlCopy, ref errMsg);
                    C._tagfree(ctx, ref sqlCopy);
                    parse.Tail = null; //: &sql[parse->Tail - sqlCopy];
                }
                else
                {
                    parse.Tail = null; //: &sql[bytes];
                }
            }
            else
            {
                parse.RunParser(sql, ref errMsg);
            }
            Debug.Assert((int)parse.QueryLoops == 1);

            if (ctx.MallocFailed)
            {
                parse.RC = RC.NOMEM;
            }
            if (parse.RC == RC.DONE)
            {
                parse.RC = RC.OK;
            }
            if (parse.CheckSchema != 0)
            {
                SchemaIsValid(parse);
            }
            if (ctx.MallocFailed)
            {
                parse.RC = RC.NOMEM;
            }
            tailOut = (parse.Tail == null ? null : parse.Tail.ToString());
            rc      = parse.RC;

            Vdbe v = parse.V;

#if !OMIT_EXPLAIN
            if (rc == RC.OK && parse.V != null && parse.Explain != 0)
            {
                int first, max;
                if (parse.Explain == 2)
                {
                    v.SetNumCols(4);
                    first = 8;
                    max   = 12;
                }
                else
                {
                    v.SetNumCols(8);
                    first = 0;
                    max   = 8;
                }
                for (i = first; i < max; i++)
                {
                    v.SetColName(i - first, COLNAME_NAME, _colName[i], C.DESTRUCTOR_STATIC);
                }
            }
#endif

            Debug.Assert(!ctx.Init.Busy || !isPrepareV2);
            if (!ctx.Init.Busy)
            {
                Vdbe.SetSql(v, sql, (int)(sql.Length - (parse.Tail == null ? 0 : parse.Tail.Length)), isPrepareV2);
            }
            if (v != null && (rc != RC.OK || ctx.MallocFailed))
            {
                v.Finalize();
                Debug.Assert(stmtOut == null);
            }
            else
            {
                stmtOut = v;
            }

            if (errMsg != null)
            {
                sqlite3Error(ctx, rc, "%s", errMsg);
                C._tagfree(ctx, ref errMsg);
            }
            else
            {
                sqlite3Error(ctx, rc, null);
            }

            // Delete any TriggerPrg structures allocated while parsing this statement.
            while (parse.TriggerPrg != null)
            {
                TriggerPrg t = parse.TriggerPrg;
                parse.TriggerPrg = t.Next;
                C._tagfree(ctx, ref t);
            }

end_prepare:
            //sqlite3StackFree( db, pParse );
            rc = SysEx.ApiExit(ctx, rc);
            Debug.Assert((RC)((int)rc & ctx.ErrMask) == rc);
            return(rc);
        }
Beispiel #21
0
        public RC Fetch(Pid id, bool createFlag, out PgHdr pageOut)
        {
            Debug.Assert(id > 0);
            // If the pluggable cache (sqlite3_pcache*) has not been allocated, allocate it now.
            if (Cache == null && createFlag)
            {
                var p = _pcache.Create(SizePage, SizeExtra + 0, Purgeable);
                p.Cachesize(get_CacheSize());
                Cache = p;
            }
            ICachePage page   = null;
            var        create = (createFlag ? 1 : 0) * (1 + ((!Purgeable || Dirty == null) ? 1 : 0));

            if (Cache != null)
            {
                page = Cache.Fetch(id, create > 0);
            }
            if (page == null && create == 1)
            {
                // Find a dirty page to write-out and recycle. First try to find a page that does not require a journal-sync (one with PGHDR_NEED_SYNC
                // cleared), but if that is not possible settle for any other unreferenced dirty page.
#if EXPENSIVE_ASSERT
                CheckSynced(this);
#endif
                PgHdr pg;
                for (pg = Synced; pg != null && (pg.Refs != 0 || (pg.Flags & PgHdr.PGHDR.NEED_SYNC) != 0); pg = pg.DirtyPrev)
                {
                    ;
                }
                Synced = pg;
                if (pg == null)
                {
                    for (pg = DirtyTail; pg != null && pg.Refs != 0; pg = pg.DirtyPrev)
                    {
                        ;
                    }
                }
                if (pg != null)
                {
#if LOG_CACHE_SPILL
                    SysEx.Log(RC.FULL, "spill page %d making room for %d - cache used: %d/%d", pg.ID, id, _pcache->Pagecount(Cache), NumberOfCachePages(this));
#endif
                    var rc = Stress(StressArg, pg);
                    if (rc != RC.OK && rc != RC.BUSY)
                    {
                        pageOut = null;
                        return(rc);
                    }
                }
                page = Cache.Fetch(id, true);
            }
            PgHdr pgHdr = null;
            if (page != null)
            {
                //pgHdr = page.Extra;
                if (page.Data == null)
                {
                    //page.Page = page;
                    page.Data = C._alloc(SizePage);
                    //page.Extra = this;
                    page.Cache = this;
                    page.ID    = id;
                }
                Debug.Assert(page.Cache == Cache);
                Debug.Assert(page.ID == id);
                //Debug.Assert(page.Data == page.Buffer);
                //Debug.Assert(page.Extra == this);
                if (page.Refs == 0)
                {
                    Refs++;
                }
                page.Refs++;
                if (id == 1)
                {
                    Page1 = pgHdr;
                }
            }
            pageOut = pgHdr;
            return(pgHdr == null && create != 0 ? RC.NOMEM : RC.OK);
        }
Beispiel #22
0
        public RC Step2()
        {
            if (Magic != VDBE_MAGIC_RUN)
            {
                // We used to require that sqlite3_reset() be called before retrying sqlite3_step() after any error or after SQLITE_DONE.  But beginning
                // with version 3.7.0, we changed this so that sqlite3_reset() would be called automatically instead of throwing the SQLITE_MISUSE error.
                // This "automatic-reset" change is not technically an incompatibility, since any application that receives an SQLITE_MISUSE is broken by
                // definition.
                //
                // Nevertheless, some published applications that were originally written for version 3.6.23 or earlier do in fact depend on SQLITE_MISUSE
                // returns, and those were broken by the automatic-reset change.  As a work-around, the SQLITE_OMIT_AUTORESET compile-time restores the
                // legacy behavior of returning SQLITE_MISUSE for cases where the previous sqlite3_step() returned something other than a SQLITE_LOCKED
                // or SQLITE_BUSY error.
#if OMIT_AUTORESET
                if (RC == RC.BUSY || RC == RC.LOCKED)
                {
                    Reset(this);
                }
                else
                {
                    return(SysEx.MISUSE_BKPT());
                }
#else
                Reset(this);
#endif
            }

            // Check that malloc() has not failed. If it has, return early.
            RC      rc;
            Context ctx = Ctx;
            if (ctx.MallocFailed)
            {
                RC_ = RC.NOMEM;
                return(RC.NOMEM);
            }

            if (PC <= 0 && Expired)
            {
                RC_ = RC.SCHEMA;
                rc  = RC.ERROR;
                goto end_of_step;
            }
            if (PC < 0)
            {
                // If there are no other statements currently running, then reset the interrupt flag.  This prevents a call to sqlite3_interrupt
                // from interrupting a statement that has not yet started.
                if (ctx.ActiveVdbeCnt == 0)
                {
                    ctx.u1.IsInterrupted = false;
                }
                Debug.Assert(ctx.WriteVdbeCnt > 0 || ctx.AutoCommit == 0 || ctx.DeferredCons == 0);
#if  !OMIT_TRACE
                if (ctx.Profile != null && !ctx.Init.Busy)
                {
                    ctx.Vfs.CurrentTimeInt64(ref StartTime);
                }
#endif
                ctx.ActiveVdbeCnt++;
                if (!ReadOnly)
                {
                    ctx.WriteVdbeCnt++;
                }
                PC = 0;
            }

#if  !OMIT_EXPLAIN
            if (_explain)
            {
                rc = List();
            }
            else
#endif
            {
                ctx.VdbeExecCnt++;
                rc = Exec();
                ctx.VdbeExecCnt--;
            }

#if !OMIT_TRACE
            // Invoke the profile callback if there is one
            if (rc != RC.ROW && ctx.Profile != null && !ctx.Init.Busy && Sql != null)
            {
                long now = 0;
                ctx.Vfs.CurrentTimeInt64(ref now);
                ctx.Profile(ctx.ProfileArg, Sql, (now - StartTime) * 1000000);
            }
#endif

            if (rc == RC.DONE)
            {
                Debug.Assert(RC_ == RC.OK);
                RC_ = DoWalCallbacks(ctx);
                if (RC_ != RC.OK)
                {
                    rc = RC.ERROR;
                }
            }

            ctx.ErrCode = rc;
            if (SysEx.ApiExit(Ctx, RC_) == RC.NOMEM)
            {
                RC_ = RC.NOMEM;
            }

end_of_step:
            // At this point local variable rc holds the value that should be returned if this statement was compiled using the legacy
            // sqlite3_prepare() interface. According to the docs, this can only be one of the values in the first assert() below. Variable p->rc
            // contains the value that would be returned if sqlite3_finalize() were called on statement p.
            Debug.Assert(rc == RC.ROW || rc == RC.DONE || rc == RC.ERROR || rc == RC.BUSY || rc == RC.MISUSE);
            Debug.Assert(RC_ != RC.ROW && RC_ != RC.DONE);
            // If this statement was prepared using sqlite3_prepare_v2(), and an error has occurred, then return the error code in p->rc to the
            // caller. Set the error code in the database handle to the same value.
            if (IsPrepareV2 && rc != RC.ROW && rc != RC.DONE)
            {
                rc = TransferError();
            }
            return(rc & (RC)ctx.ErrMask);
        }
Beispiel #23
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);
        }
Beispiel #24
0
        public int RunParser(string sql, ref string errMsg)
        {
            Debug.Assert(errMsg != null);
            Debug.Assert(NewTable == null);
            Debug.Assert(NewTrigger == null);
            Debug.Assert(VarsSeen == 0);
            Debug.Assert(Vars.length == 0);
            Debug.Assert(Vars.data == null);
            Context ctx          = Ctx;                               // The database connection
            int     maxSqlLength = ctx.Limits[(int)LIMIT.SQL_LENGTH]; // Max length of an SQL string

            if (ctx.ActiveVdbeCnt == 0)
            {
                ctx.u1.IsInterrupted = false;
            }
            RC   = RC.OK;
            Tail = new StringBuilder(sql);
            yyParser engine = Parser_Alloc(); // The LEMON-generated LALR(1) parser

            if (engine == null)
            {
                ctx.MallocFailed = true;
                return((int)RC.NOMEM);
            }
            bool enableLookaside = ctx.Lookaside.Enabled; // Saved value of db->lookaside.bEnabled

            if (ctx.Lookaside.Start != 0)
            {
                ctx.Lookaside.Enabled = true;
            }
            int errs            = 0;      // Number of errors encountered
            TK  tokenType       = 0;      // type of the next token
            TK  lastTokenParsed = 0;      // type of the previous token
            int i = 0;

            while (/*  !ctx.MallocFailed && */ i < sql.Length)
            {
                Debug.Assert(i >= 0);
                LastToken.data   = sql.Substring(i);
                LastToken.length = (uint)GetToken(sql, i, ref tokenType);
                i += (int)LastToken.length;
                if (i > maxSqlLength)
                {
                    RC = RC.TOOBIG;
                    break;
                }
                switch (tokenType)
                {
                case TK.SPACE:
                {
                    if (ctx.u1.IsInterrupted)
                    {
                        ErrorMsg("interrupt");
                        RC = RC.INTERRUPT;
                        goto abort_parse;
                    }
                    break;
                }

                case TK.ILLEGAL:
                {
                    C._tagfree(ctx, ref errMsg);
                    errMsg = SysEx.Mprintf(ctx, "unrecognized token: \"%T\"", (object)LastToken);
                    errs++;
                    goto abort_parse;
                }

                case TK.SEMI:
                {
                    //Tail = new StringBuilder(sql.Substring(i, sql.Length - i));
                    goto default;
                }

                default:
                {
                    Parser(engine, tokenType, LastToken, this);
                    lastTokenParsed = tokenType;
                    if (RC != RC.OK)
                    {
                        goto abort_parse;
                    }
                    break;
                }
                }
            }
abort_parse:
            Tail = new StringBuilder(sql.Length <= i ? string.Empty : sql.Substring(i, sql.Length - i));
            if (sql.Length >= i && errs == 0 && RC == RC.OK)
            {
                if (lastTokenParsed != TK.SEMI)
                {
                    Parser(engine, TK.SEMI, LastToken, this);
                }
                Parser(engine, 0, LastToken, this);
            }
#if YYTRACKMAXSTACKDEPTH
            sqlite3StatusSet(SQLITE_STATUS_PARSER_STACK, sqlite3ParserStackPeak(engine));
#endif
            Parser_Free(engine, null);
            ctx.Lookaside.Enabled = enableLookaside;
            //if (ctx.MallocFailed)
            //    RC = RC.NOMEM;
            if (RC != RC.OK && RC != RC.DONE && string.IsNullOrEmpty(ErrMsg))
            {
                SetString(ref ErrMsg, ctx, ErrStr(RC));
            }
            if (ErrMsg != null)
            {
                errMsg = ErrMsg;
                SysEx.LOG(RC, "%s", errMsg);
                ErrMsg = string.Empty;
                errs++;
            }
            if (V != null && Errs > 0 && Nested == 0)
            {
                Vdbe.Delete(ref V);
                V = null;
            }
#if !OMIT_SHARED_CACHE
            if (Nested == 0)
            {
                C._tagfree(ctx, ref TableLocks.data);
                TableLocks.data   = null;
                TableLocks.length = 0;
            }
#endif
#if !OMIT_VIRTUALTABLE
            VTableLocks.data = null;
#endif
            if (!E.INDECLARE_VTABLE(this))
            {
                // If the pParse.declareVtab flag is set, do not delete any table structure built up in pParse.pNewTable. The calling code (see vtab.c)
                // will take responsibility for freeing the Table structure.
                DeleteTable(ctx, ref NewTable);
            }

#if !OMIT_TRIGGER
            DeleteTrigger(ctx, ref NewTrigger);
#endif
            //for (i = Vars.length - 1; i >= 0; i--)
            //    C._tagfree(ctx, ref Vars.data[i]);
            C._tagfree(ctx, ref Vars.data);
            C._tagfree(ctx, ref Alias.data);
            while (Ainc != null)
            {
                AutoincInfo p = Ainc;
                Ainc = p.Next;
                C._tagfree(ctx, ref p);
            }
            while (ZombieTab != null)
            {
                Table p = ZombieTab;
                ZombieTab = p.NextZombie;
                DeleteTable(ctx, ref p);
            }
            if (errs > 0 && RC == RC.OK)
            {
                RC = RC.ERROR;
            }
            return(errs);
        }
Beispiel #25
0
        public static RC Config(CONFIG op, params object[] args)
        {
            // sqlite3_config() shall return SQLITE_MISUSE if it is invoked while the SQLite library is in use.
            if (_GlobalStatics.IsInit)
            {
                return(SysEx.MISUSE_BKPT());
            }
            RC rc = RC.OK;

            switch (op)
            {
#if THREADSAFE
            // Mutex configuration options are only available in a threadsafe compile.
            case CONFIG.SINGLETHREAD:
            {         // Disable all mutexing
                _GlobalStatics.CoreMutex = false;
                _GlobalStatics.FullMutex = false;
                break;
            }

            case CONFIG.MULTITHREAD:
            {         // Disable mutexing of database connections, Enable mutexing of core data structures
                _GlobalStatics.CoreMutex = true;
                _GlobalStatics.FullMutex = false;
                break;
            }

            case CONFIG.SERIALIZED:
            {         // Enable all mutexing
                _GlobalStatics.CoreMutex = true;
                _GlobalStatics.FullMutex = true;
                break;
            }

            case CONFIG.MUTEX:
            {         // Specify an alternative mutex implementation
                //_GlobalStatics.Mutex = (sqlite3_mutex_methods)args[0];
                break;
            }

            case CONFIG.GETMUTEX:
            {         // Retrieve the current mutex implementation
                //args[0] = _GlobalStatics.Mutex;
                break;
            }
#endif
            case CONFIG.MALLOC:
            {         // Specify an alternative malloc implementation
                //_GlobalStatics.m = *va_arg(args, sqlite3_mem_methods*);
                break;
            }

            case CONFIG.GETMALLOC:
            {         // Retrieve the current malloc() implementation
                //if (_GlobalStatics.m.xMalloc==0) sqlite3MemSetDefault();
                //args[0]= _GlobalStatics.m;
                break;
            }

            case CONFIG.MEMSTATUS:
            {         // Enable or disable the malloc status collection
                _GlobalStatics.Memstat = (bool)args[0];
                break;
            }

            case CONFIG.SCRATCH:
            {         // Designate a buffer for scratch memory space
                _GlobalStatics.Scratch     = (byte[][])args[0];
                _GlobalStatics.ScratchSize = (int)args[1];
                _GlobalStatics.Scratchs    = (int)args[2];
                break;
            }

#if ENABLE_MEMSYS3 || ENABLE_MEMSYS5
            case CONFIG_HEAP: {
                // Designate a buffer for heap memory space
                _GlobalStatics.Heap.data   = va_arg(args, void *);
                _GlobalStatics.Heap.length = va_arg(args, int);
                _GlobalStatics.MinReq      = va_arg(ap, int);
                if (_GlobalStatics.MinReq < 1)
                {
                    _GlobalStatics.MinReq = 1;
                }
                else if (SysEx_GlobalStatics.MinReq > (1 << 12))       // cap min request size at 2^12
                {
                    _GlobalStatics.MinReq = (1 << 12);
                }
                if (!_GlobalStatics.Heap.data)
                {
                    // If the heap pointer is NULL, then restore the malloc implementation back to NULL pointers too.  This will cause the malloc to go back to its default implementation when sqlite3_initialize() is run.
                    memset(&_GlobalStatics.m, 0, sizeof(_GlobalStatics.m));
                }
                else
                // The heap pointer is not NULL, then install one of the mem5.c/mem3.c methods. If neither ENABLE_MEMSYS3 nor ENABLE_MEMSYS5 is defined, return an error.
#if ENABLE_MEMSYS3
                { _GlobalStatics.m = sqlite3MemGetMemsys3(); }
#endif
#if ENABLE_MEMSYS5
                { _GlobalStatics.m = sqlite3MemGetMemsys5(); }
#endif
                { break; }
            }
#endif
            case CONFIG.LOOKASIDE:
            {
                _GlobalStatics.LookasideSize = (int)args[0];
                _GlobalStatics.Lookasides    = (int)args[1];
                break;
            }

            case CONFIG.LOG:
            {         // Record a pointer to the logger function and its first argument. The default is NULL.  Logging is disabled if the function pointer is NULL.
                // MSVC is picky about pulling func ptrs from va lists.
                // http://support.microsoft.com/kb/47961
                _GlobalStatics.Log    = (Action <object, int, string>)args[0];
                _GlobalStatics.LogArg = (object)args[1];
                break;
            }

            case CONFIG.URI:
            {
                _GlobalStatics.OpenUri = (bool)args[0];
                break;
            }

#if ENABLE_SQLLOG
            case CONFIG.SQLLOG: {
                _GlobalStatics.Sqllog    = (Action <object, TagBase, int, string>)args[0];;
                _GlobalStatics.SqllogArg = (object)args[1];
                break;
            }
#endif
            default:
            {
                rc = RC.ERROR;
                break;
            }
            }
            return(rc);
        }