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) { DataEx.Error(ctx, rc, null); } MutexEx.Leave(ctx.Mutex); return(rc); }
public Backup Next; // Next backup associated with source pager static Btree FindBtree(Context errorCtx, Context ctx, string dbName) { int i = Parse.FindDbName(ctx, dbName); if (i == 1) { RC rc = 0; Parse parse = new Parse(); if (parse == null) { DataEx.Error(errorCtx, RC.NOMEM, "out of memory"); rc = RC.NOMEM; } else { parse.Ctx = ctx; if (parse.OpenTempDatabase() != 0) { DataEx.Error(errorCtx, parse.RC, "%s", parse.ErrMsg); rc = RC.ERROR; } C._tagfree(errorCtx, ref parse.ErrMsg); } if (rc != 0) return null; } if (i < 0) { DataEx.Error(errorCtx, RC.ERROR, "unknown database %s", dbName); return null; } return ctx.DBs[i].Bt; }
static RC BlobSeekToRow(Incrblob p, long row, out string errOut) { string err = null; // Error message Vdbe v = p.Stmt; // Set the value of the SQL statements only variable to integer iRow. This is done directly instead of using sqlite3_bind_int64() to avoid // triggering asserts related to mutexes. Debug.Assert((v.Vars[0].Flags & MEM.Int) != 0); v.Vars[0].u.I = row; RC rc = v.Step(); if (rc == RC.ROW) { uint type = v.Cursors[0].Types[p.Col]; if (type < 12) { err = C._mtagprintf(p.Ctx, "cannot open value of type %s", type == 0 ? "null" : type == 7 ? "real" : "integer"); rc = RC.ERROR; Vdbe.Finalize(v); p.Stmt = null; } else { p.Offset = v.Cursors[0].Offsets[p.Col]; p.Bytes = SerialTypeLen(type); p.Cursor = v.Cursors[0].Cursor; p.Cursor.EnterCursor(); Btree.CacheOverflow(p.Cursor); p.Cursor.LeaveCursor(); } } if (rc == RC.ROW) { rc = RC.OK; } else if (p.Stmt != null) { rc = Vdbe.Finalize(p.Stmt); p.Stmt = null; if (rc == RC.OK) { err = C._mtagprintf(p.Ctx, "no such rowid: %lld", row); rc = RC.ERROR; } else { err = C._mtagprintf(p.Ctx, "%s", DataEx.Errmsg(p.Ctx)); } } Debug.Assert(rc != RC.OK || err == null); Debug.Assert(rc != RC.ROW && rc != RC.DONE); errOut = err; return(rc); }
public static RC GetTable(Context db, string sql, ref string[] results, ref int rows, ref int columns, ref string errMsg) { results = null; columns = 0; rows = 0; errMsg = null; TabResult r = new TabResult(); r.ErrMsg = null; r.Rows = 0; r.Columns = 0; r.RC = RC.OK; r.ResultsAlloc = 20; r.Results = new string[r.ResultsAlloc]; r.ResultsLength = 1; if (r.Results == null) { return(db.ErrCode = RC.NOMEM); } r.Results[0] = null; RC rc = DataEx.Exec(db, sql, GetTableCallback, r, ref errMsg); //Debug.Assert(sizeof(r.Results[0])>= sizeof(r.ResultsLength)); //r.Results = INT_TO_PTR(r.ResultsLength); if (((int)rc & 0xff) == (int)RC.ABORT) { FreeTable(ref r.Results); if (r.ErrMsg != null) { if (errMsg != null) { C._free(ref errMsg); errMsg = r.ErrMsg; } C._free(ref r.ErrMsg); } return(db.ErrCode = r.RC); // Assume 32-bit assignment is atomic } C._free(ref r.ErrMsg); if (rc != RC.OK) { FreeTable(ref r.Results); return(rc); } if (r.ResultsAlloc > r.ResultsLength) { Array.Resize(ref r.Results, r.ResultsLength); } results = r.Results; columns = r.Columns; rows = r.Rows; return(rc); }
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) { DataEx.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) DataEx.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; }
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); }
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_); DataEx.Error(p.DestCtx, rc, null); // Exit the mutexes and free the backup context structure. if (p.DestCtx != null) DataEx.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); DataEx.LeaveMutexAndCloseZombie(mutex); 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._mtagassignf(ref errMsg, ctx, "%s", DataEx.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._mtagassignf(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._mtagassignf(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; DataEx.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); }
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) DataEx.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; }