Пример #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);
        }
Пример #2
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);
        }
Пример #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);
        }
Пример #4
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);
     }
 }
Пример #5
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);
        }
Пример #6
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);
        }
Пример #7
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);
        }
Пример #8
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);
        }
Пример #9
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);
        }
Пример #10
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);
        }