Exemple #1
0
        public static RC CallCreate(Context ctx, int db, string tableName, ref string errorOut)
        {
            Table table = Parse.FindTable(ctx, tableName, ctx.DBs[db].Name);

            Debug.Assert(table != null && (table.TabFlags & TF.Virtual) != 0 && table.VTables == null);

            // Locate the required virtual table module
            string      moduleName = table.ModuleArgs[0];
            TableModule module     = (TableModule)ctx.Modules.Find(moduleName, moduleName.Length, (TableModule)null);

            // If the module has been registered and includes a Create method, invoke it now. If the module has not been registered, return an
            // error. Otherwise, do nothing.
            RC rc = RC.OK;

            if (module == null)
            {
                errorOut = C._mtagprintf(ctx, "no such module: %s", moduleName);
                rc       = RC.ERROR;
            }
            else
            {
                rc = VTableCallConstructor(ctx, table, module, module.IModule.Create, ref errorOut);
            }

            // Justification of ALWAYS():  The xConstructor method is required to create a valid sqlite3_vtab if it returns SQLITE_OK.
            if (rc == RC.OK && C._ALWAYS(GetVTable(ctx, table) != null))
            {
                rc = GrowVTrans(ctx);
                if (rc == RC.OK)
                {
                    AddToVTrans(ctx, GetVTable(ctx, table));
                }
            }
            return(rc);
        }
Exemple #2
0
        public static RC CallDestroy(Context ctx, int db, string tableName)
        {
            RC    rc    = RC.OK;
            Table table = Parse.FindTable(ctx, tableName, ctx.DBs[db].Name);

            if (C._ALWAYS(table != null && table.VTables != null))
            {
                VTable vtable = VTableDisconnectAll(ctx, table);
                Debug.Assert(rc == RC.OK);
                rc = vtable.Module.IModule.Destroy(vtable.IVTable);

                // Remove the sqlite3_vtab* from the aVTrans[] array, if applicable
                if (rc == RC.OK)
                {
                    Debug.Assert(table.VTables == vtable && vtable.Next == null);
                    vtable.IVTable = null;
                    table.VTables  = null;
                    vtable.Unlock();
                }
            }
            return(rc);
        }
Exemple #3
0
        //ctx.pDfltColl = sqlite3FindCollSeq( ctx, SQLITE_UTF8, "BINARY", 0 );
        public static RC InitOne(Context ctx, int db, ref string errMsg)
        {
            Debug.Assert(db >= 0 && db < ctx.DBs.length);
            Debug.Assert(ctx.DBs[db].Schema != null);
            Debug.Assert(MutexEx.Held(ctx.Mutex));
            Debug.Assert(db == 1 || ctx.DBs[db].Bt.HoldsMutex());
            RC  rc;
            int i;

            // zMasterSchema and zInitScript are set to point at the master schema and initialisation script appropriate for the database being
            // initialized. zMasterName is the name of the master table.
            string masterSchema = (E.OMIT_TEMPDB == 0 && db == 1 ? temp_master_schema : master_schema);
            string masterName   = E.SCHEMA_TABLE(db);

            // Construct the schema tables.
            string[] args = new string[4];
            args[0] = masterName;
            args[1] = "1";
            args[2] = masterSchema;
            args[3] = null;
            InitData initData = new InitData();

            initData.Ctx    = ctx;
            initData.Db     = db;
            initData.RC     = RC.OK;
            initData.ErrMsg = errMsg;
            InitCallback(initData, 3, args, null);
            if (initData.RC != 0)
            {
                rc = initData.RC;
                goto error_out;
            }
            Table table = Parse.FindTable(ctx, masterName, ctx.DBs[db].Name);

            if (C._ALWAYS(table != null))
            {
                table.TabFlags |= TF.Readonly;
            }

            // Create a cursor to hold the database open
            Context.DB dbAsObj = ctx.DBs[db];
            if (dbAsObj.Bt == null)
            {
                if (E.OMIT_TEMPDB == 0 && C._ALWAYS(db == 1))
                {
                    E.DbSetProperty(ctx, 1, SCHEMA.SchemaLoaded);
                }
                return(RC.OK);
            }

            // If there is not already a read-only (or read-write) transaction opened on the b-tree database, open one now. If a transaction is opened, it
            // will be closed before this function returns.
            bool openedTransaction = false;

            dbAsObj.Bt.Enter();
            if (!dbAsObj.Bt.IsInReadTrans())
            {
                rc = dbAsObj.Bt.BeginTrans(0);
                if (rc != RC.OK)
                {
                    C._setstring(ref errMsg, ctx, "%s", Main.ErrStr(rc));
                    goto initone_error_out;
                }
                openedTransaction = true;
            }

            // Get the database meta information.
            // Meta values are as follows:
            //    meta[0]   Schema cookie.  Changes with each schema change.
            //    meta[1]   File format of schema layer.
            //    meta[2]   Size of the page cache.
            //    meta[3]   Largest rootpage (auto/incr_vacuum mode)
            //    meta[4]   Db text encoding. 1:UTF-8 2:UTF-16LE 3:UTF-16BE
            //    meta[5]   User version
            //    meta[6]   Incremental vacuum mode
            //    meta[7]   unused
            //    meta[8]   unused
            //    meta[9]   unused
            // Note: The #defined SQLITE_UTF* symbols in sqliteInt.h correspond to the possible values of meta[4].
            uint[] meta = new uint[5];
            for (i = 0; i < meta.Length; i++)
            {
                dbAsObj.Bt.GetMeta((Btree.META)i + 1, ref meta[i]);
            }
            dbAsObj.Schema.SchemaCookie = (int)meta[(int)Btree.META.SCHEMA_VERSION - 1];

            // If opening a non-empty database, check the text encoding. For the main database, set sqlite3.enc to the encoding of the main database.
            // For an attached ctx, it is an error if the encoding is not the same as sqlite3.enc.
            if (meta[(int)Btree.META.TEXT_ENCODING - 1] != 0) // text encoding
            {
                if (db == 0)
                {
#if !OMIT_UTF16
                    // If opening the main database, set ENC(db).
                    TEXTENCODE encoding = (TEXTENCODE)(meta[(int)Btree.META.TEXT_ENCODING - 1] & 3);
                    if (encoding == 0)
                    {
                        encoding = TEXTENCODE.UTF8;
                    }
                    E.CTXENCODE(ctx, encoding);
#else
                    E.CTXENCODE(ctx, TEXTENCODE_UTF8);
#endif
                }
                else
                {
                    // If opening an attached database, the encoding much match ENC(db)
                    if ((TEXTENCODE)meta[(int)Btree.META.TEXT_ENCODING - 1] != E.CTXENCODE(ctx))
                    {
                        C._setstring(ref errMsg, ctx, "attached databases must use the same text encoding as main database");
                        rc = RC.ERROR;
                        goto initone_error_out;
                    }
                }
            }
            else
            {
                E.DbSetProperty(ctx, db, SCHEMA.Empty);
            }
            dbAsObj.Schema.Encode = E.CTXENCODE(ctx);

            if (dbAsObj.Schema.CacheSize == 0)
            {
                dbAsObj.Schema.CacheSize = DEFAULT_CACHE_SIZE;
                dbAsObj.Bt.SetCacheSize(dbAsObj.Schema.CacheSize);
            }

            // file_format==1    Version 3.0.0.
            // file_format==2    Version 3.1.3.  // ALTER TABLE ADD COLUMN
            // file_format==3    Version 3.1.4.  // ditto but with non-NULL defaults
            // file_format==4    Version 3.3.0.  // DESC indices.  Boolean constants
            dbAsObj.Schema.FileFormat = (byte)meta[(int)Btree.META.FILE_FORMAT - 1];
            if (dbAsObj.Schema.FileFormat == 0)
            {
                dbAsObj.Schema.FileFormat = 1;
            }
            if (dbAsObj.Schema.FileFormat > MAX_FILE_FORMAT)
            {
                C._setstring(ref errMsg, ctx, "unsupported file format");
                rc = RC.ERROR;
                goto initone_error_out;
            }

            // Ticket #2804:  When we open a database in the newer file format, clear the legacy_file_format pragma flag so that a VACUUM will
            // not downgrade the database and thus invalidate any descending indices that the user might have created.
            if (db == 0 && meta[(int)Btree.META.FILE_FORMAT - 1] >= 4)
            {
                ctx.Flags &= ~Context.FLAG.LegacyFileFmt;
            }

            // Read the schema information out of the schema tables
            Debug.Assert(ctx.Init.Busy);
            {
                string sql = C._mtagprintf(ctx, "SELECT name, rootpage, sql FROM '%q'.%s ORDER BY rowid", ctx.DBs[db].Name, masterName);
#if !OMIT_AUTHORIZATION
                {
                    Func <object, int, string, string, string, string, ARC> auth = ctx.Auth;
                    ctx.Auth = null;
#endif
                rc = sqlite3_exec(ctx, sql, InitCallback, initData, 0);
                //: errMsg = initData.ErrMsg;
#if !OMIT_AUTHORIZATION
                ctx.Auth = auth;
            }
#endif
                if (rc == RC.OK)
                {
                    rc = initData.RC;
                }
                C._tagfree(ctx, ref sql);
#if !OMIT_ANALYZE
                if (rc == RC.OK)
                {
                    sqlite3AnalysisLoad(ctx, db);
                }
#endif
            }
            if (ctx.MallocFailed)
            {
                rc = RC.NOMEM;
                Main.ResetAllSchemasOfConnection(ctx);
            }
            if (rc == RC.OK || (ctx.Flags & Context.FLAG.RecoveryMode) != 0)
            {
                // Black magic: If the SQLITE_RecoveryMode flag is set, then consider the schema loaded, even if errors occurred. In this situation the
                // current sqlite3_prepare() operation will fail, but the following one will attempt to compile the supplied statement against whatever subset
                // of the schema was loaded before the error occurred. The primary purpose of this is to allow access to the sqlite_master table
                // even when its contents have been corrupted.
                E.DbSetProperty(ctx, db, DB.SchemaLoaded);
                rc = RC.OK;
            }

            // Jump here for an error that occurs after successfully allocating curMain and calling sqlite3BtreeEnter(). For an error that occurs
            // before that point, jump to error_out.
initone_error_out:
            if (openedTransaction)
            {
                dbAsObj.Bt.Commit();
            }
            dbAsObj.Bt.Leave();

error_out:
            if (rc == RC.NOMEM || rc == RC.IOERR_NOMEM)
            {
                ctx.MallocFailed = true;
            }
            return(rc);
        }