private RC pager_open_journal() { var rc = RC.OK; var pVfs = this.pVfs; Debug.Assert(this.eState == PAGER.WRITER_LOCKED); Debug.Assert(assert_pager_state()); Debug.Assert(this.pInJournal == null); // If already in the error state, this function is a no-op. But on the other hand, this routine is never called if we are already in an error state. if (Check.NEVER(this.errCode) != RC.OK) { return(this.errCode); } if (!this.pagerUseWal() && this.journalMode != JOURNALMODE.OFF) { this.pInJournal = new BitArray(this.dbSize); // Open the journal file if it is not already open. if (!this.jfd.IsOpen) { if (this.journalMode == JOURNALMODE.MEMORY) { this.jfd = new MemJournalFile(); } else { var flags = VFSOPEN.READWRITE | VFSOPEN.CREATE | (this.tempFile ? VFSOPEN.DELETEONCLOSE | VFSOPEN.TEMP_JOURNAL : VFSOPEN.MAIN_JOURNAL); #if SQLITE_ENABLE_ATOMIC_WRITE rc = sqlite3JournalOpen(pVfs, pPager.zJournal, pPager.jfd, flags, jrnlBufferSize(pPager)); #else VFSOPEN int0 = 0; rc = FileEx.sqlite3OsOpen(pVfs, this.zJournal, this.jfd, flags, ref int0); #endif } Debug.Assert(rc != RC.OK || this.jfd.IsOpen); } // Write the first journal header to the journal file and open the sub-journal if necessary. if (rc == RC.OK) { // TODO: Check if all of these are really required. this.nRec = 0; this.journalOff = 0; this.setMaster = 0; this.journalHdr = 0; rc = writeJournalHdr(); } } if (rc != RC.OK) { BitArray.Destroy(ref this.pInJournal); this.pInJournal = null; } else { Debug.Assert(this.eState == PAGER.WRITER_LOCKED); this.eState = PAGER.WRITER_CACHEMOD; } return(rc); }
private RC pager_delmaster(string zMaster) { //string zMasterJournal = null; /* Contents of master journal file */ //long nMasterJournal; /* Size of master journal file */ //string zJournal; /* Pointer to one journal within MJ file */ //string zMasterPtr; /* Space to hold MJ filename from a journal file */ //int nMasterPtr; /* Amount of space allocated to zMasterPtr[] */ // Allocate space for both the pJournal and pMaster file descriptors. If successful, open the master journal file for reading. var pMaster = new VirtualFile(); // Malloc'd master-journal file descriptor var pJournal = new VirtualFile(); // Malloc'd child-journal file descriptor var pVfs = this.pVfs; VFSOPEN iDummy = 0; var rc = FileEx.sqlite3OsOpen(pVfs, zMaster, pMaster, VFSOPEN.READONLY | VFSOPEN.MASTER_JOURNAL, ref iDummy); if (rc != RC.OK) { goto delmaster_out; } Debugger.Break(); //TODO -- // Load the entire master journal file into space obtained from sqlite3_malloc() and pointed to by zMasterJournal. Also obtain // sufficient space (in zMasterPtr) to hold the names of master journal files extracted from regular rollback-journals. //rc = sqlite3OsFileSize(pMaster, &nMasterJournal); //if (rc != SQLITE.OK) goto delmaster_out; //nMasterPtr = pVfs.mxPathname + 1; // zMasterJournal = sqlite3Malloc((int)nMasterJournal + nMasterPtr + 1); // if ( !zMasterJournal ) // { // rc = SQLITE_NOMEM; // goto delmaster_out; // } // zMasterPtr = &zMasterJournal[nMasterJournal+1]; // rc = sqlite3OsRead( pMaster, zMasterJournal, (int)nMasterJournal, 0 ); // if ( rc != SQLITE.OK ) goto delmaster_out; // zMasterJournal[nMasterJournal] = 0; // zJournal = zMasterJournal; // while ( ( zJournal - zMasterJournal ) < nMasterJournal ) // { // int exists; // rc = sqlite3OsAccess( pVfs, zJournal, SQLITE_ACCESS_EXISTS, &exists ); // if ( rc != SQLITE.OK ) // goto delmaster_out; // if ( exists ) // { // // One of the journals pointed to by the master journal exists. Open it and check if it points at the master journal. If // // so, return without deleting the master journal file. // int c; // int flags = ( SQLITE_OPEN_READONLY | SQLITE_OPEN_MAIN_JOURNAL ); // rc = sqlite3OsOpen( pVfs, zJournal, pJournal, flags, 0 ); // if ( rc != SQLITE.OK ) // goto delmaster_out; // rc = readMasterJournal( pJournal, zMasterPtr, nMasterPtr ); // sqlite3OsClose( pJournal ); // if ( rc != SQLITE.OK ) // goto delmaster_out; // c = zMasterPtr[0] != 0 && strcmp( zMasterPtr, zMaster ) == 0; // if ( c ) // // We have a match. Do not delete the master journal file. // goto delmaster_out; // } // zJournal += ( sqlite3Strlen30( zJournal ) + 1 ); // } // //sqlite3OsClose(pMaster); //rc = sqlite3OsDelete( pVfs, zMaster, 0 ); goto delmaster_out; delmaster_out: if (pMaster != null) { FileEx.sqlite3OsClose(pMaster); Debug.Assert(!pJournal.IsOpen); } return(rc); }
// was:sqlite3BtreeOpen public static RC Open(VirtualFileSystem pVfs, string zFilename, sqlite3 db, ref Btree rTree, OPEN flags, VFSOPEN vfsFlags) { Btree p; // Handle to return var rc = RC.OK; byte nReserve; // Byte of unused space on each page var zDbHeader = new byte[100]; // Database header content // True if opening an ephemeral, temporary database */ bool isTempDb = string.IsNullOrEmpty(zFilename); // Set the variable isMemdb to true for an in-memory database, or false for a file-based database. #if SQLITE_OMIT_MEMORYDB var isMemdb = false; #else var isMemdb = (zFilename == ":memory:" || isTempDb && db.sqlite3TempInMemory()); #endif Debug.Assert(db != null); Debug.Assert(pVfs != null); Debug.Assert(MutexEx.Held(db.Mutex)); Debug.Assert(((uint)flags & 0xff) == (uint)flags); // flags fit in 8 bits // Only a BTREE_SINGLE database can be BTREE_UNORDERED Debug.Assert((flags & OPEN.UNORDERED) == 0 || (flags & OPEN.SINGLE) != 0); // A BTREE_SINGLE database is always a temporary and/or ephemeral Debug.Assert((flags & OPEN.SINGLE) == 0 || isTempDb); if ((db.flags & sqlite3b.SQLITE.NoReadlock) != 0) flags |= OPEN.NO_READLOCK; if (isMemdb) flags |= OPEN.MEMORY; if ((vfsFlags & VFSOPEN.MAIN_DB) != 0 && (isMemdb || isTempDb)) vfsFlags = (vfsFlags & ~VFSOPEN.MAIN_DB) | VFSOPEN.TEMP_DB; p = new Btree(); p.InTransaction = TRANS.NONE; p.DB = db; #if !SQLITE_OMIT_SHARED_CACHE p.Locks.Tree = p; p.Locks.TableID = 1; #endif BtShared shared = null; // Shared part of btree structure MutexEx mutexOpen = null; // Prevents a race condition. #if !SQLITE_OMIT_SHARED_CACHE && !SQLITE_OMIT_DISKIO // If this Btree is a candidate for shared cache, try to find an existing BtShared object that we can share with if (!isMemdb && !isTempDb) { if ((vfsFlags & VFSOPEN.SHAREDCACHE) != 0) { p.Sharable = true; string zPathname; rc = pVfs.xFullPathname(zFilename, out zPathname); mutexOpen = MutexEx.Alloc(MUTEX.STATIC_OPEN); MutexEx.Enter(mutexOpen); var mutexShared = MutexEx.Alloc(MUTEX.STATIC_MASTER); MutexEx.Enter(mutexShared); for (shared = SysEx.getGLOBAL<BtShared>(s_sqlite3SharedCacheList); shared != null; shared = shared.Next) { Debug.Assert(shared.nRef > 0); if (string.Equals(zPathname, shared.Pager.sqlite3PagerFilename) && shared.Pager.sqlite3PagerVfs == pVfs) { for (var iDb = db.DBs - 1; iDb >= 0; iDb--) { var existingTree = db.AllocDBs[iDb].Tree; if (existingTree != null && existingTree.Shared == shared) { MutexEx.Leave(mutexShared); MutexEx.Leave(mutexOpen); p = null; return RC.CONSTRAINT; } } p.Shared = shared; shared.nRef++; break; } } MutexEx.Leave(mutexShared); } #if DEBUG else // In debug mode, we mark all persistent databases as sharable even when they are not. This exercises the locking code and // gives more opportunity for asserts(sqlite3_mutex_held()) statements to find locking problems. p.Sharable = true; #endif } #endif if (shared == null) { // The following asserts make sure that structures used by the btree are the right size. This is to guard against size changes that result // when compiling on a different architecture. Debug.Assert(sizeof(long) == 8 || sizeof(long) == 4); Debug.Assert(sizeof(ulong) == 8 || sizeof(ulong) == 4); Debug.Assert(sizeof(uint) == 4); Debug.Assert(sizeof(ushort) == 2); Debug.Assert(sizeof(Pgno) == 4); shared = new BtShared(); rc = Pager.Open(pVfs, out shared.Pager, zFilename, EXTRA_SIZE, (Pager.PAGEROPEN)flags, vfsFlags, pageReinit, () => new MemPage()); if (rc == RC.OK) rc = shared.Pager.ReadFileHeader(zDbHeader.Length, zDbHeader); if (rc != RC.OK) goto btree_open_out; shared.OpenFlags = flags; shared.DB = db; shared.Pager.SetBusyHandler(btreeInvokeBusyHandler, shared); p.Shared = shared; shared.Cursors = null; shared.Page1 = null; shared.ReadOnly = shared.Pager.IsReadonly; #if SQLITE_SECURE_DELETE pBt.secureDelete = true; #endif shared.PageSize = (uint)((zDbHeader[16] << 8) | (zDbHeader[17] << 16)); if (shared.PageSize < 512 || shared.PageSize > Pager.SQLITE_MAX_PAGE_SIZE || ((shared.PageSize - 1) & shared.PageSize) != 0) { shared.PageSize = 0; #if !SQLITE_OMIT_AUTOVACUUM // If the magic name ":memory:" will create an in-memory database, then leave the autoVacuum mode at 0 (do not auto-vacuum), even if // SQLITE_DEFAULT_AUTOVACUUM is true. On the other hand, if SQLITE_OMIT_MEMORYDB has been defined, then ":memory:" is just a // regular file-name. In this case the auto-vacuum applies as per normal. if (zFilename != string.Empty && !isMemdb) { shared.AutoVacuum = (AUTOVACUUM.DEFAULT != AUTOVACUUM.NONE); shared.IncrVacuum = (AUTOVACUUM.DEFAULT == AUTOVACUUM.INCR); } #endif nReserve = 0; } else { nReserve = zDbHeader[20]; shared.PageSizeFixed = true; #if !SQLITE_OMIT_AUTOVACUUM shared.AutoVacuum = ConvertEx.Get4(zDbHeader, 36 + 4 * 4) != 0; shared.IncrVacuum = ConvertEx.Get4(zDbHeader, 36 + 7 * 4) != 0; #endif } rc = shared.Pager.SetPageSize(ref shared.PageSize, nReserve); if (rc != RC.OK) goto btree_open_out; shared.UsableSize = (ushort)(shared.PageSize - nReserve); Debug.Assert((shared.PageSize & 7) == 0); // 8-byte alignment of pageSize #if !SQLITE_OMIT_SHARED_CACHE && !SQLITE_OMIT_DISKIO // Add the new BtShared object to the linked list sharable BtShareds. if (p.Sharable) { MutexEx mutexShared; shared.nRef = 1; mutexShared = MutexEx.Alloc(MUTEX.STATIC_MASTER); if (MutexEx.SQLITE_THREADSAFE && MutexEx.WantsCoreMutex) shared.Mutex = MutexEx.Alloc(MUTEX.FAST); MutexEx.Enter(mutexShared); shared.Next = SysEx.getGLOBAL<BtShared>(s_sqlite3SharedCacheList); SysEx.setGLOBAL<BtShared>(s_sqlite3SharedCacheList, shared); MutexEx.Leave(mutexShared); } #endif } #if !SQLITE_OMIT_SHARED_CACHE && !SQLITE_OMIT_DISKIO // If the new Btree uses a sharable pBtShared, then link the new Btree into the list of all sharable Btrees for the same connection. // The list is kept in ascending order by pBt address. Btree existingTree2; if (p.Sharable) for (var i = 0; i < db.DBs; i++) if ((existingTree2 = db.AllocDBs[i].Tree) != null && existingTree2.Sharable) { while (existingTree2.Prev != null) { existingTree2 = existingTree2.Prev; } if (p.Shared.Version < existingTree2.Shared.Version) { p.Next = existingTree2; p.Prev = null; existingTree2.Prev = p; } else { while (existingTree2.Next != null && existingTree2.Next.Shared.Version < p.Shared.Version) existingTree2 = existingTree2.Next; p.Next = existingTree2.Next; p.Prev = existingTree2; if (p.Next != null) p.Next.Prev = p; existingTree2.Next = p; } break; } #endif rTree = p; // btree_open_out: if (rc != RC.OK) { if (shared != null && shared.Pager != null) shared.Pager.Close(); shared = null; p = null; rTree = null; } else { // If the B-Tree was successfully opened, set the pager-cache size to the default value. Except, when opening on an existing shared pager-cache, // do not change the pager-cache size. if (p.GetSchema(0, null, null) == null) p.Shared.Pager.SetCacheSize(SQLITE_DEFAULT_CACHE_SIZE); } if (mutexOpen != null) { Debug.Assert(MutexEx.Held(mutexOpen)); MutexEx.Leave(mutexOpen); } return rc; }
private RC pagerOpentemp(ref VirtualFile pFile, VFSOPEN vfsFlags) { vfsFlags |= VFSOPEN.READWRITE | VFSOPEN.CREATE | VFSOPEN.EXCLUSIVE | VFSOPEN.DELETEONCLOSE; VFSOPEN dummy = 0; var rc = FileEx.sqlite3OsOpen(this.pVfs, null, pFile, vfsFlags, ref dummy); Debug.Assert(rc != RC.OK || pFile.IsOpen); return rc; }
// was:sqlite3PagerOpen public static RC Open(VirtualFileSystem pVfs, out Pager ppPager, string zFilename, int nExtra, PAGEROPEN flags, VFSOPEN vfsFlags, Action<PgHdr> xReinit, Func<object> memPageBuilder) { Pager pPager = null; // Pager object to allocate and return byte memDb = 0; // True if this is an in-memory file bool readOnly = false; // True if this is a read-only file string zPathname = null; // Full path to database file //int nPathname = 0; // Number of bytes in zPathname bool useJournal = (flags & PAGEROPEN.OMIT_JOURNAL) == 0; // False to omit journal bool noReadlock = (flags & PAGEROPEN.NO_READLOCK) != 0; // True to omit read-lock int pcacheSize = PCache.sqlite3PcacheSize(); // Bytes to allocate for PCache uint szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; // Default page size string zUri = null; // URI args to copy int nUri = 0; // Number of bytes of URI args at *zUri // Figure out how much space is required for each journal file-handle (there are two of them, the main journal and the sub-journal). This // is the maximum space required for an in-memory journal file handle and a regular journal file-handle. Note that a "regular journal-handle" // may be a wrapper capable of caching the first portion of the journal file in memory to implement the atomic-write optimization (see // source file journal.c). int journalFileSize = SysEx.ROUND8(sqlite3JournalSize(pVfs) > MemJournalFile.sqlite3MemJournalSize() ? sqlite3JournalSize(pVfs) : MemJournalFile.sqlite3MemJournalSize()); // Bytes to allocate for each journal fd // Set the output variable to NULL in case an error occurs. ppPager = null; #if !SQLITE_OMIT_MEMORYDB if ((flags & PAGEROPEN.MEMORY) != 0) { memDb = 1; zFilename = null; } #endif // Compute and store the full pathname in an allocated buffer pointed to by zPathname, length nPathname. Or, if this is a temporary file, // leave both nPathname and zPathname set to 0. var rc = RC.OK; if (!string.IsNullOrEmpty(zFilename)) { rc = pVfs.xFullPathname(zFilename, out zPathname); var z = zUri = zFilename; nUri = zUri.Length; if (rc == RC.OK && zPathname.Length + 8 > pVfs.mxPathname) // This branch is taken when the journal path required by the database being opened will be more than pVfs.mxPathname // bytes in length. This means the database cannot be opened, as it will not be possible to open the journal file or even // check for a hot-journal before reading. rc = SysEx.SQLITE_CANTOPEN_BKPT(); if (rc != RC.OK) return rc; } // Allocate memory for the Pager structure, PCache object, the three file descriptors, the database file name and the journal // file name. The layout in memory is as follows: // Pager object (sizeof(Pager) bytes) // PCache object (sqlite3PcacheSize() bytes) // Database file handle (pVfs.szOsFile bytes) // Sub-journal file handle (journalFileSize bytes) // Main journal file handle (journalFileSize bytes) // Database file name (nPathname+1 bytes) // Journal file name (nPathname+8+1 bytes) pPager = new Pager(memPageBuilder); pPager.pPCache = new PCache(); pPager.fd = new VirtualFile(); pPager.sjfd = new VirtualFile(); pPager.jfd = new VirtualFile(); // Fill in the Pager.zFilename and Pager.zJournal buffers, if required. if (zPathname != null) { Debug.Assert(zPathname.Length > 0); pPager.zFilename = zPathname.ToString(); zUri = pPager.zFilename; pPager.zJournal = pPager.zFilename + "-journal"; #if !SQLITE_OMIT_WAL pPager.zWal = &pPager.zJournal[nPathname + 8 + 1]; memcpy(pPager.zWal, zPathname, nPathname); memcpy(&pPager.zWal[nPathname], "-wal", 4); #endif } else pPager.zFilename = string.Empty; pPager.pVfs = pVfs; pPager.vfsFlags = vfsFlags; // Open the pager file. var tempFile = LOCKINGMODE.NORMAL; // True for temp files (incl. in-memory files) if (!string.IsNullOrEmpty(zFilename)) { VFSOPEN fout = 0; // VFS flags returned by xOpen() rc = FileEx.sqlite3OsOpen(pVfs, zFilename, pPager.fd, vfsFlags, ref fout); Debug.Assert(0 == memDb); readOnly = (fout & VFSOPEN.READONLY) != 0; // If the file was successfully opened for read/write access, choose a default page size in case we have to create the // database file. The default page size is the maximum of: // + SQLITE_DEFAULT_PAGE_SIZE, // + The value returned by sqlite3OsSectorSize() // + The largest page size that can be written atomically. if (rc == RC.OK && !readOnly) { pPager.setSectorSize(); Debug.Assert(SQLITE_DEFAULT_PAGE_SIZE <= SQLITE_MAX_DEFAULT_PAGE_SIZE); if (szPageDflt < pPager.sectorSize) szPageDflt = (pPager.sectorSize > SQLITE_MAX_DEFAULT_PAGE_SIZE ? SQLITE_MAX_DEFAULT_PAGE_SIZE : (uint)pPager.sectorSize); #if SQLITE_ENABLE_ATOMIC_WRITE int iDc = sqlite3OsDeviceCharacteristics(pPager.fd); Debug.Assert(SQLITE_IOCAP_ATOMIC512 == (512 >> 8)); Debug.Assert(SQLITE_IOCAP_ATOMIC64K == (65536 >> 8)); Debug.Assert(SQLITE_MAX_DEFAULT_PAGE_SIZE <= 65536); for (var ii = szPageDflt; ii <= SQLITE_MAX_DEFAULT_PAGE_SIZE; ii = ii * 2) if (iDc & (SQLITE_IOCAP_ATOMIC | (ii >> 8))) szPageDflt = ii; #endif } } else { // If a temporary file is requested, it is not opened immediately. In this case we accept the default page size and delay actually // opening the file until the first call to OsWrite(). // This branch is also run for an in-memory database. An in-memory database is the same as a temp-file that is never written out to // disk and uses an in-memory rollback journal. tempFile = LOCKINGMODE.EXCLUSIVE; pPager.eState = PAGER.READER; pPager.eLock = VFSLOCK.EXCLUSIVE; readOnly = (vfsFlags & VFSOPEN.READONLY) != 0; } // The following call to PagerSetPagesize() serves to set the value of Pager.pageSize and to allocate the Pager.pTmpSpace buffer. if (rc == RC.OK) { Debug.Assert(pPager.memDb == 0); rc = pPager.SetPageSize(ref szPageDflt, -1); } // If an error occurred in either of the blocks above, free the Pager structure and close the file. if (rc != RC.OK) { Debug.Assert(null == pPager.pTmpSpace); FileEx.sqlite3OsClose(pPager.fd); return rc; } // Initialize the PCache object. Debug.Assert(nExtra < 1000); nExtra = SysEx.ROUND8(nExtra); PCache.Open((int)szPageDflt, nExtra, (memDb == 0), (memDb == 0 ? (Func<object, PgHdr, RC>)pagerStress : null), pPager, pPager.pPCache); PAGERTRACE("OPEN {0} {1}", FILEHANDLEID(pPager.fd), pPager.zFilename); SysEx.IOTRACE("OPEN {0:x} {1}", pPager.GetHashCode(), pPager.zFilename); pPager.useJournal = (byte)(useJournal ? 1 : 0); pPager.noReadlock = (byte)(noReadlock && readOnly ? 1 : 0); pPager.mxPgno = SQLITE_MAX_PAGE_COUNT; #if false Debug.Assert(pPager.state == (tempFile != 0 ? PAGER.EXCLUSIVE : PAGER.UNLOCK)); #endif pPager.tempFile = tempFile != 0; Debug.Assert(tempFile == LOCKINGMODE.NORMAL || tempFile == LOCKINGMODE.EXCLUSIVE); pPager.exclusiveMode = tempFile != 0; pPager.changeCountDone = pPager.tempFile; pPager.memDb = memDb; pPager.readOnly = readOnly; Debug.Assert(useJournal || pPager.tempFile); pPager.noSync = pPager.tempFile; pPager.fullSync = pPager.noSync; pPager.syncFlags = (pPager.noSync ? 0 : VirtualFile.SYNC.NORMAL); pPager.ckptSyncFlags = pPager.syncFlags; pPager.nExtra = (ushort)nExtra; pPager.journalSizeLimit = SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT; Debug.Assert(pPager.fd.IsOpen || tempFile != 0); pPager.setSectorSize(); if (!useJournal) pPager.journalMode = JOURNALMODE.OFF; else if (memDb != 0) pPager.journalMode = JOURNALMODE.MEMORY; pPager.xReiniter = xReinit; ppPager = pPager; return RC.OK; }
// was:sqlite3BtreeOpen public static RC Open(VirtualFileSystem pVfs, string zFilename, sqlite3 db, ref Btree rTree, OPEN flags, VFSOPEN vfsFlags) { Btree p; // Handle to return var rc = RC.OK; byte nReserve; // Byte of unused space on each page var zDbHeader = new byte[100]; // Database header content // True if opening an ephemeral, temporary database */ bool isTempDb = string.IsNullOrEmpty(zFilename); // Set the variable isMemdb to true for an in-memory database, or false for a file-based database. #if SQLITE_OMIT_MEMORYDB var isMemdb = false; #else var isMemdb = (zFilename == ":memory:" || isTempDb && db.sqlite3TempInMemory()); #endif Debug.Assert(db != null); Debug.Assert(pVfs != null); Debug.Assert(MutexEx.Held(db.Mutex)); Debug.Assert(((uint)flags & 0xff) == (uint)flags); // flags fit in 8 bits // Only a BTREE_SINGLE database can be BTREE_UNORDERED Debug.Assert((flags & OPEN.UNORDERED) == 0 || (flags & OPEN.SINGLE) != 0); // A BTREE_SINGLE database is always a temporary and/or ephemeral Debug.Assert((flags & OPEN.SINGLE) == 0 || isTempDb); if ((db.flags & sqlite3b.SQLITE.NoReadlock) != 0) flags |= OPEN.NO_READLOCK; if (isMemdb) flags |= OPEN.MEMORY; if ((vfsFlags & VFSOPEN.MAIN_DB) != 0 && (isMemdb || isTempDb)) vfsFlags = (vfsFlags & ~VFSOPEN.MAIN_DB) | VFSOPEN.TEMP_DB; p = new Btree(); p.InTransaction = TRANS.NONE; p.DB = db; #if !SQLITE_OMIT_SHARED_CACHE p.Locks.Tree = p; p.Locks.TableID = 1; #endif BtShared shared = null; // Shared part of btree structure sqlite3_mutex mutexOpen = null; // Prevents a race condition. #if !SQLITE_OMIT_SHARED_CACHE && !SQLITE_OMIT_DISKIO // If this Btree is a candidate for shared cache, try to find an existing BtShared object that we can share with if (!isMemdb && !isTempDb) { if ((vfsFlags & VFSOPEN.SHAREDCACHE) != 0) { p.Sharable = true; string zPathname; rc = pVfs.xFullPathname(zFilename, out zPathname); mutexOpen = MutexEx.sqlite3MutexAlloc(MUTEX.STATIC_OPEN); MutexEx.sqlite3_mutex_enter(mutexOpen); var mutexShared = MutexEx.sqlite3MutexAlloc(MUTEX.STATIC_MASTER); MutexEx.sqlite3_mutex_enter(mutexShared); for (shared = SysEx.getGLOBAL<BtShared>(s_sqlite3SharedCacheList); shared != null; shared = shared.Next) { Debug.Assert(shared.nRef > 0); if (string.Equals(zPathname, shared.Pager.sqlite3PagerFilename) && shared.Pager.sqlite3PagerVfs == pVfs) { for (var iDb = db.DBs - 1; iDb >= 0; iDb--) { var existingTree = db.AllocDBs[iDb].Tree; if (existingTree != null && existingTree.Shared == shared) { MutexEx.sqlite3_mutex_leave(mutexShared); MutexEx.sqlite3_mutex_leave(mutexOpen); p = null; return RC.CONSTRAINT; } } p.Shared = shared; shared.nRef++; break; } } MutexEx.sqlite3_mutex_leave(mutexShared); } #if DEBUG else // In debug mode, we mark all persistent databases as sharable even when they are not. This exercises the locking code and // gives more opportunity for asserts(sqlite3_mutex_held()) statements to find locking problems. p.Sharable = true; #endif } #endif if (shared == null) { // The following asserts make sure that structures used by the btree are the right size. This is to guard against size changes that result // when compiling on a different architecture. Debug.Assert(sizeof(long) == 8 || sizeof(long) == 4); Debug.Assert(sizeof(ulong) == 8 || sizeof(ulong) == 4); Debug.Assert(sizeof(uint) == 4); Debug.Assert(sizeof(ushort) == 2); Debug.Assert(sizeof(Pgno) == 4); shared = new BtShared(); rc = Pager.Open(pVfs, out shared.Pager, zFilename, EXTRA_SIZE, (Pager.PAGEROPEN)flags, vfsFlags, pageReinit, () => new MemPage()); if (rc == RC.OK) rc = shared.Pager.ReadFileHeader(zDbHeader.Length, zDbHeader); if (rc != RC.OK) goto btree_open_out; shared.OpenFlags = flags; shared.DB = db; shared.Pager.SetBusyHandler(btreeInvokeBusyHandler, shared); p.Shared = shared; shared.Cursors = null; shared.Page1 = null; shared.ReadOnly = shared.Pager.IsReadonly; #if SQLITE_SECURE_DELETE pBt.secureDelete = true; #endif shared.PageSize = (uint)((zDbHeader[16] << 8) | (zDbHeader[17] << 16)); if (shared.PageSize < 512 || shared.PageSize > Pager.SQLITE_MAX_PAGE_SIZE || ((shared.PageSize - 1) & shared.PageSize) != 0) { shared.PageSize = 0; #if !SQLITE_OMIT_AUTOVACUUM // If the magic name ":memory:" will create an in-memory database, then leave the autoVacuum mode at 0 (do not auto-vacuum), even if // SQLITE_DEFAULT_AUTOVACUUM is true. On the other hand, if SQLITE_OMIT_MEMORYDB has been defined, then ":memory:" is just a // regular file-name. In this case the auto-vacuum applies as per normal. if (zFilename != string.Empty && !isMemdb) { shared.AutoVacuum = (AUTOVACUUM.DEFAULT != AUTOVACUUM.NONE); shared.IncrVacuum = (AUTOVACUUM.DEFAULT == AUTOVACUUM.INCR); } #endif nReserve = 0; } else { nReserve = zDbHeader[20]; shared.PageSizeFixed = true; #if !SQLITE_OMIT_AUTOVACUUM shared.AutoVacuum = ConvertEx.Get4(zDbHeader, 36 + 4 * 4) != 0; shared.IncrVacuum = ConvertEx.Get4(zDbHeader, 36 + 7 * 4) != 0; #endif } rc = shared.Pager.SetPageSize(ref shared.PageSize, nReserve); if (rc != RC.OK) goto btree_open_out; shared.UsableSize = (ushort)(shared.PageSize - nReserve); Debug.Assert((shared.PageSize & 7) == 0); // 8-byte alignment of pageSize #if !SQLITE_OMIT_SHARED_CACHE && !SQLITE_OMIT_DISKIO // Add the new BtShared object to the linked list sharable BtShareds. if (p.Sharable) { sqlite3_mutex mutexShared; shared.nRef = 1; mutexShared = MutexEx.sqlite3MutexAlloc(MUTEX.STATIC_MASTER); if (MutexEx.SQLITE_THREADSAFE && MutexEx.WantsCoreMutex) shared.Mutex = MutexEx.sqlite3MutexAlloc(MUTEX.FAST); MutexEx.sqlite3_mutex_enter(mutexShared); shared.Next = SysEx.getGLOBAL<BtShared>(s_sqlite3SharedCacheList); SysEx.setGLOBAL<BtShared>(s_sqlite3SharedCacheList, shared); MutexEx.sqlite3_mutex_leave(mutexShared); } #endif } #if !SQLITE_OMIT_SHARED_CACHE && !SQLITE_OMIT_DISKIO // If the new Btree uses a sharable pBtShared, then link the new Btree into the list of all sharable Btrees for the same connection. // The list is kept in ascending order by pBt address. Btree existingTree2; if (p.Sharable) for (var i = 0; i < db.DBs; i++) if ((existingTree2 = db.AllocDBs[i].Tree) != null && existingTree2.Sharable) { while (existingTree2.Prev != null) { existingTree2 = existingTree2.Prev; } if (p.Shared.Version < existingTree2.Shared.Version) { p.Next = existingTree2; p.Prev = null; existingTree2.Prev = p; } else { while (existingTree2.Next != null && existingTree2.Next.Shared.Version < p.Shared.Version) existingTree2 = existingTree2.Next; p.Next = existingTree2.Next; p.Prev = existingTree2; if (p.Next != null) p.Next.Prev = p; existingTree2.Next = p; } break; } #endif rTree = p; // btree_open_out: if (rc != RC.OK) { if (shared != null && shared.Pager != null) shared.Pager.Close(); shared = null; p = null; rTree = null; } else { // If the B-Tree was successfully opened, set the pager-cache size to the default value. Except, when opening on an existing shared pager-cache, // do not change the pager-cache size. if (p.GetSchema(0, null, null) == null) p.Shared.Pager.SetCacheSize(SQLITE_DEFAULT_CACHE_SIZE); } if (mutexOpen != null) { Debug.Assert(MutexEx.Held(mutexOpen)); MutexEx.sqlite3_mutex_leave(mutexOpen); } return rc; }