internal bool removeFromSharingList()
        {
#if !SQLITE_OMIT_SHARED_CACHE
            MutexEx  pMaster;
            BtShared list;
            bool     removed = false;
            Debug.Assert(MutexEx.NotHeld(Mutex));
            pMaster = MutexEx.Alloc(MUTEX.STATIC_MASTER);
            MutexEx.Enter(pMaster);
            nRef--;
            if (nRef <= 0)
            {
                if (SysEx.getGLOBAL <BtShared>(Btree.s_sqlite3SharedCacheList) == this)
                {
                    SysEx.setGLOBAL <BtShared>(Btree.s_sqlite3SharedCacheList, Next);
                }
                else
                {
                    list = SysEx.getGLOBAL <BtShared>(Btree.s_sqlite3SharedCacheList);
                    while (Check.ALWAYS(list) != null && list.Next != this)
                    {
                        list = list.Next;
                    }
                    if (Check.ALWAYS(list) != null)
                    {
                        list.Next = Next;
                    }
                }
                if (MutexEx.SQLITE_THREADSAFE)
                {
                    MutexEx.Free(Mutex);
                }
                removed = true;
            }
            MutexEx.Leave(pMaster);
            return(removed);
#else
            return(true);
#endif
        }
Beispiel #2
0
        // 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;
        }