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); }
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); }
//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); }