internal RC btreeDropTable(Pgno iTable, ref int piMoved) { MemPage pPage = null; var pBt = this.Shared; Debug.Assert(sqlite3BtreeHoldsMutex()); Debug.Assert(this.InTransaction == TRANS.WRITE); // It is illegal to drop a table if any cursors are open on the database. This is because in auto-vacuum mode the backend may // need to move another root-page to fill a gap left by the deleted root page. If an open cursor was using this page a problem would occur. // This error is caught long before control reaches this point. if (Check.NEVER(pBt.Cursors) != null) { sqlite3b.sqlite3ConnectionBlocked(this.DB, pBt.Cursors.Tree.DB); return(RC.LOCKED_SHAREDCACHE); } var rc = pBt.btreeGetPage((Pgno)iTable, ref pPage, 0); if (rc != RC.OK) { return(rc); } var dummy0 = 0; rc = ClearTable((int)iTable, ref dummy0); if (rc != RC.OK) { pPage.releasePage(); return(rc); } piMoved = 0; if (iTable > 1) { #if SQLITE_OMIT_AUTOVACUUM freePage(pPage, ref rc); releasePage(pPage); #else if (pBt.AutoVacuum) { Pgno maxRootPgno = 0; GetMeta((int)META.LARGEST_ROOT_PAGE, ref maxRootPgno); if (iTable == maxRootPgno) { // If the table being dropped is the table with the largest root-page number in the database, put the root page on the free list. pPage.freePage(ref rc); pPage.releasePage(); if (rc != RC.OK) { return(rc); } } else { // The table being dropped does not have the largest root-page number in the database. So move the page that does into the // gap left by the deleted root-page. var pMove = new MemPage(); pPage.releasePage(); rc = pBt.btreeGetPage(maxRootPgno, ref pMove, 0); if (rc != RC.OK) { return(rc); } rc = MemPage.relocatePage(pBt, pMove, PTRMAP.ROOTPAGE, 0, iTable, 0); pMove.releasePage(); if (rc != RC.OK) { return(rc); } pMove = null; rc = pBt.btreeGetPage(maxRootPgno, ref pMove, 0); pMove.freePage(ref rc); pMove.releasePage(); if (rc != RC.OK) { return(rc); } piMoved = (int)maxRootPgno; } // Set the new 'max-root-page' value in the database header. This is the old value less one, less one more if that happens to // be a root-page number, less one again if that is the PENDING_BYTE_PAGE. maxRootPgno--; while (maxRootPgno == MemPage.PENDING_BYTE_PAGE(pBt) || MemPage.PTRMAP_ISPAGE(pBt, maxRootPgno)) { maxRootPgno--; } Debug.Assert(maxRootPgno != MemPage.PENDING_BYTE_PAGE(pBt)); rc = SetMeta(4, maxRootPgno); } else { pPage.freePage(ref rc); pPage.releasePage(); } #endif } else { // If sqlite3BtreeDropTable was called on page 1. This really never should happen except in a corrupt database. pPage.zeroPage(PTF_INTKEY | PTF_LEAF); pPage.releasePage(); } return(rc); }
internal RC btreeCreateTable(ref int piTable, CREATETABLE createTabFlags) { var pBt = this.Shared; var pRoot = new MemPage(); Pgno pgnoRoot = 0; int ptfFlags; // Page-type flage for the root page of new table RC rc; Debug.Assert(sqlite3BtreeHoldsMutex()); Debug.Assert(pBt.InTransaction == TRANS.WRITE); Debug.Assert(!pBt.ReadOnly); #if SQLITE_OMIT_AUTOVACUUM rc = allocateBtreePage(pBt, ref pRoot, ref pgnoRoot, 1, 0); if (rc != SQLITE.OK) { return(rc); } #else if (pBt.AutoVacuum) { Pgno pgnoMove = 0; // Move a page here to make room for the root-page var pPageMove = new MemPage(); // The page to move to. // Creating a new table may probably require moving an existing database to make room for the new tables root page. In case this page turns // out to be an overflow page, delete all overflow page-map caches held by open cursors. invalidateAllOverflowCache(pBt); // Read the value of meta[3] from the database to determine where the root page of the new table should go. meta[3] is the largest root-page // created so far, so the new root-page is (meta[3]+1). GetMeta((int)META.LARGEST_ROOT_PAGE, ref pgnoRoot); pgnoRoot++; // The new root-page may not be allocated on a pointer-map page, or the PENDING_BYTE page. while (pgnoRoot == MemPage.PTRMAP_PAGENO(pBt, pgnoRoot) || pgnoRoot == MemPage.PENDING_BYTE_PAGE(pBt)) { pgnoRoot++; } Debug.Assert(pgnoRoot >= 3); // Allocate a page. The page that currently resides at pgnoRoot will be moved to the allocated page (unless the allocated page happens to reside at pgnoRoot). rc = pBt.allocateBtreePage(ref pPageMove, ref pgnoMove, pgnoRoot, 1); if (rc != RC.OK) { return(rc); } if (pgnoMove != pgnoRoot) { // pgnoRoot is the page that will be used for the root-page of the new table (assuming an error did not occur). But we were // allocated pgnoMove. If required (i.e. if it was not allocated by extending the file), the current page at position pgnoMove // is already journaled. PTRMAP eType = 0; Pgno iPtrPage = 0; pPageMove.releasePage(); // Move the page currently at pgnoRoot to pgnoMove. rc = pBt.btreeGetPage(pgnoRoot, ref pRoot, 0); if (rc != RC.OK) { return(rc); } rc = pBt.ptrmapGet(pgnoRoot, ref eType, ref iPtrPage); if (eType == PTRMAP.ROOTPAGE || eType == PTRMAP.FREEPAGE) { rc = SysEx.SQLITE_CORRUPT_BKPT(); } if (rc != RC.OK) { pRoot.releasePage(); return(rc); } Debug.Assert(eType != PTRMAP.ROOTPAGE); Debug.Assert(eType != PTRMAP.FREEPAGE); rc = MemPage.relocatePage(pBt, pRoot, eType, iPtrPage, pgnoMove, 0); pRoot.releasePage(); // Obtain the page at pgnoRoot if (rc != RC.OK) { return(rc); } rc = pBt.btreeGetPage(pgnoRoot, ref pRoot, 0); if (rc != RC.OK) { return(rc); } rc = Pager.Write(pRoot.DbPage); if (rc != RC.OK) { pRoot.releasePage(); return(rc); } } else { pRoot = pPageMove; } // Update the pointer-map and meta-data with the new root-page number. pBt.ptrmapPut(pgnoRoot, PTRMAP.ROOTPAGE, 0, ref rc); if (rc != RC.OK) { pRoot.releasePage(); return(rc); } // When the new root page was allocated, page 1 was made writable in order either to increase the database filesize, or to decrement the // freelist count. Hence, the sqlite3BtreeUpdateMeta() call cannot fail. Debug.Assert(Pager.IsPageWriteable(pBt.Page1.DbPage)); rc = SetMeta(4, pgnoRoot); if (Check.NEVER(rc != RC.OK)) { pRoot.releasePage(); return(rc); } } else { rc = pBt.allocateBtreePage(ref pRoot, ref pgnoRoot, 1, 0); if (rc != RC.OK) { return(rc); } } #endif Debug.Assert(Pager.IsPageWriteable(pRoot.DbPage)); ptfFlags = ((createTabFlags & CREATETABLE.INTKEY) != 0 ? PTF_INTKEY | PTF_LEAFDATA | PTF_LEAF : PTF_ZERODATA | PTF_LEAF); pRoot.zeroPage(ptfFlags); Pager.Unref(pRoot.DbPage); Debug.Assert((pBt.OpenFlags & OPEN.SINGLE) == 0 || pgnoRoot == 2); piTable = (int)pgnoRoot; return(RC.OK); }