From a059ad070bb2e953aabe933ae687de896077c569 Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 17 Apr 2001 20:09:11 +0000 Subject: [PATCH] Begin adding BTree code (CVS 213) FossilOrigin-Name: bdb1c425f577d455155982ee2cd8cb686bcaf0da --- manifest | 14 +-- manifest.uuid | 2 +- src/btree.c | 266 ++++++++++++++++++++++++++++++++++++++++++++++++ src/btree.h | 56 ++++++++++ test/pager.test | 27 ++++- 5 files changed, 356 insertions(+), 9 deletions(-) create mode 100644 src/btree.c create mode 100644 src/btree.h diff --git a/manifest b/manifest index 789baf3070..196c2dc270 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Version\s1.0.31\s(CVS\s472) -D 2001-04-15T02:30:00 +C Begin\sadding\sBTree\scode\s(CVS\s213) +D 2001-04-17T20:09:11 F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4 F Makefile.in ac01d6145714b0d1c9e99382caf03cf30d6f4c8d F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958 @@ -9,6 +9,8 @@ F configure.in 6940e3f88bf3d28a10c73b06ab99fd3a7e039a61 F doc/lemon.html e233a3e97a779c7a87e1bc4528c664a58e49dd47 F doc/report1.txt 734cbae63b1310cc643fe5e9e3da1ab55a79b99e F src/TODO 38a68a489e56e9fd4a96263e0ff9404a47368ad4 +F src/btree.c 670b20349bd1d1448cc90f7b9487c2bf7a5c039c +F src/btree.h f21c240d0c95f93e2a128106d04a6c448ed0eb94 F src/build.c 4f6a2d551c56342cd4a0420654835be3ad179651 F src/dbbe.c ec82c602c598748204a61a35ab0c31e34ca58223 F src/dbbe.h 7235b15c6c5d8be0c4da469cef9620cee70b1cc8 @@ -59,7 +61,7 @@ F test/insert2.test 732405e30331635af8d159fccabe835eea5cd0c6 F test/lock.test bca7d53de73138b1f670a2fbdb1f481ff7eaa45a F test/main.test da635f9e078cd21ddf074e727381a715064489ff F test/malloc.test 3daa97f6a9577d8f4c6e468b274333af19ce5861 -F test/pager.test 4c27be3e0da89caa36fa3c2b84468785f330acca +F test/pager.test 3416a155c2dc3b1b3c07d4bb0192cbb15b76a90c F test/printf.test 4c71871e1a75a2dacb673945fc13ddb30168798f F test/rowid.test 128453599def7435e988216f7fe89c7450b8a9a3 F test/select1.test 223507655cdb4f9901d83fa7f5c5328e022c211f @@ -101,7 +103,7 @@ F www/opcode.tcl cb3a1abf8b7b9be9f3a228d097d6bf8b742c2b6f F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f F www/tclsqlite.tcl 06f81c401f79a04f2c5ebfb97e7c176225c0aef2 F www/vdbe.tcl 0c8aaa529dd216ccbf7daaabd80985e413d5f9ad -P 1f07abe46e2de8c5d9659bd5a78efe4ce23144ae -R 6b66dff388a84887995d516ce20e212a +P a7bfcbb4131ab17a58d5e843c418b2e1a3ab2abc +R a9d053b754fd8fb60eea783a8c6d6da7 U drh -Z 1ee28b0946e3569d6ec390d67bea5d2f +Z b0f122553586c19814acf39c12755271 diff --git a/manifest.uuid b/manifest.uuid index eb77777caa..0f06e8051b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a7bfcbb4131ab17a58d5e843c418b2e1a3ab2abc \ No newline at end of file +bdb1c425f577d455155982ee2cd8cb686bcaf0da \ No newline at end of file diff --git a/src/btree.c b/src/btree.c new file mode 100644 index 0000000000..5e5f71ec6c --- /dev/null +++ b/src/btree.c @@ -0,0 +1,266 @@ +/* +** Copyright (c) 2001 D. Richard Hipp +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public +** License as published by the Free Software Foundation; either +** version 2 of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public +** License along with this library; if not, write to the +** Free Software Foundation, Inc., 59 Temple Place - Suite 330, +** Boston, MA 02111-1307, USA. +** +** Author contact information: +** drh@hwaci.com +** http://www.hwaci.com/drh/ +** +************************************************************************* +** $Id: btree.c,v 1.1 2001/04/17 20:09:11 drh Exp $ +*/ +#include "sqliteInt.h" +#include "pager.h" +#include "btree.h" +#include + +typedef unsigned int u32; + +/* +** Everything we need to know about an open database +*/ +struct Btree { + Pager *pPager; /* The page cache */ + BtCursor *pCursor; /* All open cursors */ + u32 *page1; /* First page of the database */ + int inTrans; /* True if a transaction is current */ +}; +typedef Btree Bt; + +/* +** The maximum depth of a cursor +*/ +#define MX_LEVEL 20 + +/* +** Within a cursor, each level off the search tree is an instance of +** this structure. +*/ +typedef struct BtIdxpt BtIdxpt; +struct BtIdxpt { + Pgno pgno; /* The page number */ + u32 *aPage; /* The page data */ + int idx; /* Index into pPage[] */ +}; + +/* +** Everything we need to know about a cursor +*/ +struct BtCursor { + Btree *pBt; /* The whole database */ + BtCursor *pPrev, *pNext; /* Linked list of all cursors */ + int valid; /* True if the cursor points to something */ + int nLevel; /* Number of levels of indexing used */ + BtIdxpt aLevel[MX_LEVEL]; /* The index levels */ +}; + +/* +** The first page contains the following additional information: +** +** MAGIC-1 +** MAGIC-2 +** First free block +*/ +#define EXTRA_PAGE_1_CELLS 3 +#define MAGIC_1 0x7264dc61 +#define MAGIC_2 0x54e55d9e + +/* +** Open a new database +*/ +int sqliteBtreeOpen(const char *zFilename, int mode, Btree **ppBtree){ + Btree *pBt; + + pBt = sqliteMalloc( sizeof(*pBt) ); + if( pBt==0 ){ + **ppBtree = 0; + return SQLITE_NOMEM; + } + rc = sqlitepager_open(&pBt->pPager, zFilename, 100); + if( rc!=SQLITE_OK ){ + if( pBt->pPager ) sqlitepager_close(pBt->pPager); + sqliteFree(pBt); + *ppBtree = 0; + return rc; + } + pBt->pCursor = 0; + pBt->page1 = 0; + *ppBtree = pBt; + return SQLITE_OK; +} + +/* +** Close an open database and invalidate all cursors. +*/ +int sqliteBtreeClose(Btree *pBt){ + while( pBt->pCursor ){ + sqliteBtreeCloseCursor(pBt->pCursor); + } + sqlitepager_close(pBt->pPager); + sqliteFree(pBt); + return SQLITE_OK; +} + +/* +** Start a new transaction +*/ +int sqliteBtreeBeginTrans(Btree *pBt){ + int rc; + if( pBt->inTrans ) return SQLITE_ERROR; + if( pBt->page1==0 ){ + rc = sqlitepager_get(pBt->pPager, 1, &pBt->page1); + if( rc!=SQLITE_OK ) return rc; + } + rc = sqlitepager_write(pBt->page1); + if( rc==SQLITE_OK ){ + pBt->inTrans = 1; + } + return rc; +} + +/* +** Get a reference to page1 of the database file. This will +** also acquire a readlock on that file. +*/ +static int lockBtree(Btree *pBt){ + int rc; + if( pBt->page1 ) return SQLITE_OK; + rc = sqlitepager_get(pBt->pPager, 1, &pBt->page1); + if( rc!=SQLITE_OK ) return rc; + /* Sanity checking on the database file format */ + return rc; +} + +/* +** Remove the last reference to the database file. This will +** remove the read lock. +*/ +static void unlockBtree(Btree *pBt){ + if( pBt->pCursor==0 && pBt->page1!=0 ){ + sqlitepager_unref(pBt->page1); + pBt->page1 = 0; + pBt->inTrans = 0; + } +} + +/* +** Commit the transaction currently in progress. All cursors +** must be closed before this routine is called. +*/ +int sqliteBtreeCommit(Btree *pBt){ + int rc; + assert( pBt->pCursor==0 ); + rc = sqlitepager_commit(pBt->pPager); + unlockBtree(pBt); + return rc; +} + +/* +** Rollback the transaction in progress. All cursors must be +** closed before this routine is called. +*/ +int sqliteBtreeRollback(Btree *pBt){ + int rc; + assert( pBt->pCursor==0 ); + rc = sqlitepager_rollback(pBt->pPager); + unlockBtree(pBt); + return rc; +} + +/* +** Create a new cursor. The act of acquiring a cursor +** gets a read lock on the database file. +*/ +int sqliteBtreeCursor(Btree *pBt, BtCursor **ppCur){ + int rc; + BtCursor *pCur; + if( pBt->page1==0 ){ + rc = lockBtree(pBt); + if( rc!=SQLITE_OK ){ + *ppCur = 0; + return rc; + } + } + pCur = sqliteMalloc( sizeof(*pCur) ); + if( pCur==0 ){ + *ppCur = 0; + unlockBtree(pBt); + return SQLITE_NOMEM; + } + pCur->pPrev = 0; + pCur->pNext = pBt->pCursor; + if( pCur->pNext ){ + pCur->pNext->pPrev = pCur; + } + pBt->pCursor = pCur; + pCur->pBt = pBt; + pCur->nLevel = 1; + pCur->aLevel[0].pgno = 1; + pCur->aLevel[0].aPage = pBt->page1; + pCur->aLevel[0].idx = 0; +} + +/* +** Close a cursor. +*/ +int sqliteBtreeCloseCursor(BtCursor *pCur){ + Btree *pBt = pCur->pBt; + int i; + if( pCur->pPrev ){ + pCur->pPrev->pNext = pCur->pNext; + }else{ + pBt->pCursor = pCur->pNext; + } + if( pCur->pNext ){ + pCur->pNext->pPrev = pCur->pPrev; + } + for(i=pCur->nLevel-1; i>0; i--){ + sqlitepager_unref(pCur->aLevel[i].aPage); + } + if( pBt->pCursor==0 && pBt->inTrans==0 ){ + unlockBtree(pBt); + } + sqliteFree(pCur); +} + +int sqliteBtreeKeySize(BtCursor *pCur){ + int nEntry; + u32 *aPage; + BtIdxpt *pIdx; + int offset; + if( !pCur->valid ) return 0; + pIdx = &pCur->aLevel[pCur->nLevel-1]; + aPage = pIdx->aPage; + offset = (pIdx->pgno==1)*EXTRA_PAGE_1_CELLS; + nEntry = aPage[offset]; + if( pIdx->idx