mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-07 02:42:48 +03:00
continued work on btree (CVS 222)
FossilOrigin-Name: d07e0e80a0b33081adda8651e9a6750b2e40141a
This commit is contained in:
17
manifest
17
manifest
@@ -1,5 +1,5 @@
|
|||||||
C :-)\s(CVS\s1720)
|
C continued\swork\son\sbtree\s(CVS\s222)
|
||||||
D 2001-05-28T00:41:24
|
D 2001-06-02T02:40:57
|
||||||
F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
|
F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
|
||||||
F Makefile.in acef0f0275a5ca8e68bda165f7f05d810a207664
|
F Makefile.in acef0f0275a5ca8e68bda165f7f05d810a207664
|
||||||
F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
|
F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
|
||||||
@@ -12,8 +12,8 @@ F notes/notes1.txt b7c0812b704a022e88c621146ae50955c923d464
|
|||||||
F notes/notes2.txt 7e3fafd5e25906c1fe1e95f13b089aa398ca403e
|
F notes/notes2.txt 7e3fafd5e25906c1fe1e95f13b089aa398ca403e
|
||||||
F notes/notes3.txt cd5e7bd2167d7ef89b1077abdfa68f0af6337744
|
F notes/notes3.txt cd5e7bd2167d7ef89b1077abdfa68f0af6337744
|
||||||
F src/TODO 38a68a489e56e9fd4a96263e0ff9404a47368ad4
|
F src/TODO 38a68a489e56e9fd4a96263e0ff9404a47368ad4
|
||||||
F src/btree.c a80c891f7bec7fb79b87136480743056be1f3fd4
|
F src/btree.c 3768a7dd7f2d37ef618b2dca55b8fffb76bce52b
|
||||||
F src/btree.h a0ef90514e16fab863c7825ab22724317894e1ac
|
F src/btree.h 4a50996c9bd912e8feeff28a45e936fe33f828c1
|
||||||
F src/build.c 4f6a2d551c56342cd4a0420654835be3ad179651
|
F src/build.c 4f6a2d551c56342cd4a0420654835be3ad179651
|
||||||
F src/dbbe.c b18259f99d87240cbe751021cf14dd3aa83a48af
|
F src/dbbe.c b18259f99d87240cbe751021cf14dd3aa83a48af
|
||||||
F src/dbbe.h 7235b15c6c5d8be0c4da469cef9620cee70b1cc8
|
F src/dbbe.h 7235b15c6c5d8be0c4da469cef9620cee70b1cc8
|
||||||
@@ -31,7 +31,7 @@ F src/ex/sizes.tcl f54bad4a2ac567624be59131a6ee42d71b41a3d7
|
|||||||
F src/expr.c c4c24c3af1eba094a816522eb0e085bed518ee16
|
F src/expr.c c4c24c3af1eba094a816522eb0e085bed518ee16
|
||||||
F src/insert.c aa528e20a787af85432a61daaea6df394bd251d7
|
F src/insert.c aa528e20a787af85432a61daaea6df394bd251d7
|
||||||
F src/main.c 0a13c7a2beb8ce36aee43daf8c95989b200727a7
|
F src/main.c 0a13c7a2beb8ce36aee43daf8c95989b200727a7
|
||||||
F src/pager.c debcf7b00e73c031c47ffc12cdeed5cb5f02b761
|
F src/pager.c 5224dc4b7f678af2b7e9affb933eb1cee5e7977e
|
||||||
F src/pager.h e527411d88e31085f07eba6776dc337b8b027921
|
F src/pager.h e527411d88e31085f07eba6776dc337b8b027921
|
||||||
F src/parse.y 8fc096948994a7ffbf61ba13129cc589f794a9cb
|
F src/parse.y 8fc096948994a7ffbf61ba13129cc589f794a9cb
|
||||||
F src/printf.c b1e22a47be8cdf707815647239991e08e8cb69f9
|
F src/printf.c b1e22a47be8cdf707815647239991e08e8cb69f9
|
||||||
@@ -45,6 +45,7 @@ F src/table.c adcaf074f6c1075e86359174e68701fa2acfc4d6
|
|||||||
F src/tclsqlite.c 1f2bf4691a6bd81fbff1856ae4a12db24d1265f7
|
F src/tclsqlite.c 1f2bf4691a6bd81fbff1856ae4a12db24d1265f7
|
||||||
F src/test1.c abb3cb427e735ae87e6533f5b3b7164b7da91bc4
|
F src/test1.c abb3cb427e735ae87e6533f5b3b7164b7da91bc4
|
||||||
F src/test2.c 0183625225a860397b4fd3041aefb48f77e4630a
|
F src/test2.c 0183625225a860397b4fd3041aefb48f77e4630a
|
||||||
|
F src/test3.c a1868c55e03776f2e59f713247e77c734d8badfe
|
||||||
F src/tokenize.c 0118b57702cb6550769316e8443b06760b067acf
|
F src/tokenize.c 0118b57702cb6550769316e8443b06760b067acf
|
||||||
F src/update.c 0cf789656a936d4356668393267692fa4b03ffc6
|
F src/update.c 0cf789656a936d4356668393267692fa4b03ffc6
|
||||||
F src/util.c 1b396ac34e30dd6222d82e996c17b161bbc906bc
|
F src/util.c 1b396ac34e30dd6222d82e996c17b161bbc906bc
|
||||||
@@ -106,7 +107,7 @@ F www/opcode.tcl cb3a1abf8b7b9be9f3a228d097d6bf8b742c2b6f
|
|||||||
F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f
|
F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f
|
||||||
F www/tclsqlite.tcl 06f81c401f79a04f2c5ebfb97e7c176225c0aef2
|
F www/tclsqlite.tcl 06f81c401f79a04f2c5ebfb97e7c176225c0aef2
|
||||||
F www/vdbe.tcl 0c8aaa529dd216ccbf7daaabd80985e413d5f9ad
|
F www/vdbe.tcl 0c8aaa529dd216ccbf7daaabd80985e413d5f9ad
|
||||||
P c8d3bdd91e333f3fc519558e40c07e7e7c2ebeec
|
P d78febd197b7514b63afe6626e6639a3c3c2f0fc
|
||||||
R 94ebfe2075cd85e8e5b61fd444665d42
|
R 84627a160819d6448d4e47c4eb2cbfd2
|
||||||
U drh
|
U drh
|
||||||
Z d7ae8aba50f98a47259bcacc564e5de1
|
Z 48913baed71abfbe21354883b544670d
|
||||||
|
@@ -1 +1 @@
|
|||||||
d78febd197b7514b63afe6626e6639a3c3c2f0fc
|
d07e0e80a0b33081adda8651e9a6750b2e40141a
|
511
src/btree.c
511
src/btree.c
@@ -21,7 +21,7 @@
|
|||||||
** http://www.hwaci.com/drh/
|
** http://www.hwaci.com/drh/
|
||||||
**
|
**
|
||||||
*************************************************************************
|
*************************************************************************
|
||||||
** $Id: btree.c,v 1.9 2001/05/28 00:41:15 drh Exp $
|
** $Id: btree.c,v 1.10 2001/06/02 02:40:57 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
#include "pager.h"
|
#include "pager.h"
|
||||||
@@ -40,7 +40,7 @@ typedef unsigned char u8;
|
|||||||
/*
|
/*
|
||||||
** Forward declarations of structures used only in this file.
|
** Forward declarations of structures used only in this file.
|
||||||
*/
|
*/
|
||||||
typedef struct Page1Header Page1Header;
|
typedef struct PageOne PageOne;
|
||||||
typedef struct MemPage MemPage;
|
typedef struct MemPage MemPage;
|
||||||
typedef struct PageHdr PageHdr;
|
typedef struct PageHdr PageHdr;
|
||||||
typedef struct Cell Cell;
|
typedef struct Cell Cell;
|
||||||
@@ -58,26 +58,31 @@ typedef struct OverflowPage OverflowPage;
|
|||||||
#define ROUNDUP(X) ((X+3) & ~3)
|
#define ROUNDUP(X) ((X+3) & ~3)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** The first page of the database file contains some additional
|
** This is a magic string that appears at the beginning of every
|
||||||
** information used for housekeeping and sanity checking. Otherwise,
|
** SQLite database in order to identify the fail as a real database.
|
||||||
** the first page is just like any other. The additional information
|
|
||||||
** found on the first page is described by the following structure.
|
|
||||||
*/
|
*/
|
||||||
struct Page1Header {
|
static const char zMagicHeader[] =
|
||||||
u32 magic1; /* A magic number to verify the file really is a database */
|
"** This file contains an SQLite 2.0 database **"
|
||||||
u32 magic2; /* A second magic number to be extra sure */
|
#define MAGIC_SIZE (sizeof(zMagicHeader))
|
||||||
Pgno firstList; /* First free page in a list of all free pages */
|
|
||||||
};
|
|
||||||
#define MAGIC_1 0x7264dc61
|
|
||||||
#define MAGIC_2 0x54e55d9e
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Each database page has a header as follows:
|
** The first page of the database file contains a magic header string
|
||||||
|
** to identify the file as an SQLite database file. It also contains
|
||||||
|
** a pointer to the first free page of the file. Page 2 contains the
|
||||||
|
** root of the BTree.
|
||||||
**
|
**
|
||||||
** page1_header Optional instance of Page1Header structure
|
** Remember that pages are numbered beginning with 1. (See pager.c
|
||||||
** rightmost_pgno Page number of the right-most child page
|
** for additional information.) Page 0 does not exist and a page
|
||||||
** first_cell Index into MemPage.aDisk of first cell
|
** number of 0 is used to mean "no such page".
|
||||||
** first_free Index of first free block
|
*/
|
||||||
|
struct PageOne {
|
||||||
|
char zMagic[MAGIC_SIZE]; /* String that identifies the file as a database */
|
||||||
|
Pgno firstList; /* First free page in a list of all free pages */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Each database page has a header that is an instance of this
|
||||||
|
** structure.
|
||||||
**
|
**
|
||||||
** MemPage.pHdr always points to the rightmost_pgno. First_free is
|
** MemPage.pHdr always points to the rightmost_pgno. First_free is
|
||||||
** 0 if there is no free space on this page. Otherwise, first_free is
|
** 0 if there is no free space on this page. Otherwise, first_free is
|
||||||
@@ -95,11 +100,15 @@ struct PageHdr {
|
|||||||
u16 firstFree; /* Index in MemPage.aDisk[] of the first free block */
|
u16 firstFree; /* Index in MemPage.aDisk[] of the first free block */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Entries on a page of the database are called "Cells". Each Cell
|
** Entries on a page of the database are called "Cells". Each Cell
|
||||||
** has a header and data. This structure defines the header. The
|
** has a header and data. This structure defines the header. The
|
||||||
** definition of the complete Cell including the data is given below.
|
** key and data (collectively the "payload") follow this header on
|
||||||
|
** the database page.
|
||||||
|
**
|
||||||
|
** A definition of the complete Cell structure is given below. The
|
||||||
|
** header for the cell must be defined separately in order to do some
|
||||||
|
** of the sizing #defines that follow.
|
||||||
*/
|
*/
|
||||||
struct CellHdr {
|
struct CellHdr {
|
||||||
Pgno leftChild; /* Child page that comes before this cell */
|
Pgno leftChild; /* Child page that comes before this cell */
|
||||||
@@ -110,7 +119,7 @@ struct CellHdr {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
** The minimum size of a complete Cell. The Cell must contain a header
|
** The minimum size of a complete Cell. The Cell must contain a header
|
||||||
** and at least 4 bytes of data.
|
** and at least 4 bytes of payload.
|
||||||
*/
|
*/
|
||||||
#define MIN_CELL_SIZE (sizeof(CellHdr)+4)
|
#define MIN_CELL_SIZE (sizeof(CellHdr)+4)
|
||||||
|
|
||||||
@@ -124,6 +133,8 @@ struct CellHdr {
|
|||||||
** The maximum amount of data (in bytes) that can be stored locally for a
|
** The maximum amount of data (in bytes) that can be stored locally for a
|
||||||
** database entry. If the entry contains more data than this, the
|
** database entry. If the entry contains more data than this, the
|
||||||
** extra goes onto overflow pages.
|
** extra goes onto overflow pages.
|
||||||
|
**
|
||||||
|
** This number is chosen so that at least 4 cells will fit on every page.
|
||||||
*/
|
*/
|
||||||
#define MX_LOCAL_PAYLOAD \
|
#define MX_LOCAL_PAYLOAD \
|
||||||
((SQLITE_PAGE_SIZE-sizeof(PageHdr))/4-(sizeof(CellHdr)+sizeof(Pgno)))
|
((SQLITE_PAGE_SIZE-sizeof(PageHdr))/4-(sizeof(CellHdr)+sizeof(Pgno)))
|
||||||
@@ -137,7 +148,7 @@ struct CellHdr {
|
|||||||
** page number of the first overflow page.
|
** page number of the first overflow page.
|
||||||
**
|
**
|
||||||
** Though this structure is fixed in size, the Cell on the database
|
** Though this structure is fixed in size, the Cell on the database
|
||||||
** page varies in size. Very cell has a CellHdr and at least 4 bytes
|
** page varies in size. Every cell has a CellHdr and at least 4 bytes
|
||||||
** of payload space. Additional payload bytes (up to the maximum of
|
** of payload space. Additional payload bytes (up to the maximum of
|
||||||
** MX_LOCAL_PAYLOAD) and the Cell.ovfl value are allocated only as
|
** MX_LOCAL_PAYLOAD) and the Cell.ovfl value are allocated only as
|
||||||
** needed.
|
** needed.
|
||||||
@@ -171,7 +182,7 @@ struct FreeBlk {
|
|||||||
** Each overflow page is an instance of the following structure.
|
** Each overflow page is an instance of the following structure.
|
||||||
**
|
**
|
||||||
** Unused pages in the database are also represented by instances of
|
** Unused pages in the database are also represented by instances of
|
||||||
** the OverflowPage structure. The Page1Header.freeList field is the
|
** the OverflowPage structure. The PageOne.freeList field is the
|
||||||
** page number of the first page in a linked list of unused database
|
** page number of the first page in a linked list of unused database
|
||||||
** pages.
|
** pages.
|
||||||
*/
|
*/
|
||||||
@@ -182,27 +193,26 @@ struct OverflowPage {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
** For every page in the database file, an instance of the following structure
|
** For every page in the database file, an instance of the following structure
|
||||||
** is stored in memory. The aDisk[] array contains the data obtained from
|
** is stored in memory. The aDisk[] array contains the raw bits read from
|
||||||
** the disk. The rest is auxiliary data that held in memory only. The
|
** the disk. The rest is auxiliary information that held in memory only. The
|
||||||
** auxiliary data is only valid for regular database pages - the auxiliary
|
** auxiliary info is only valid for regular database pages - it is not
|
||||||
** data is meaningless for overflow pages and pages on the freelist.
|
** used for overflow pages and pages on the freelist.
|
||||||
**
|
**
|
||||||
** Of particular interest in the auxiliary data is the apCell[] entry. Each
|
** Of particular interest in the auxiliary info is the apCell[] entry. Each
|
||||||
** apCell[] entry is a pointer to a Cell structure in aDisk[]. The cells are
|
** apCell[] entry is a pointer to a Cell structure in aDisk[]. The cells are
|
||||||
** put in this array so that they can be accessed in constant time, rather
|
** put in this array so that they can be accessed in constant time, rather
|
||||||
** than in linear time which would be needed if we walked the linked list.
|
** than in linear time which would be needed if we had to walk the linked
|
||||||
|
** list on every access.
|
||||||
**
|
**
|
||||||
** The pParent field points back to the parent page. This allows us to
|
** The pParent field points back to the parent page. This allows us to
|
||||||
** walk up the BTree from any leaf to the root. Care must be taken to
|
** walk up the BTree from any leaf to the root. Care must be taken to
|
||||||
** unref() the parent page pointer when this page is no longer referenced.
|
** unref() the parent page pointer when this page is no longer referenced.
|
||||||
** The pageDestructor() routine handles that.
|
** The pageDestructor() routine handles that chore.
|
||||||
*/
|
*/
|
||||||
struct MemPage {
|
struct MemPage {
|
||||||
char aDisk[SQLITE_PAGE_SIZE]; /* Page data stored on disk */
|
char aDisk[SQLITE_PAGE_SIZE]; /* Page data stored on disk */
|
||||||
int isInit; /* True if auxiliary data is initialized */
|
int isInit; /* True if auxiliary data is initialized */
|
||||||
MemPage *pParent; /* The parent of this page. NULL for root */
|
MemPage *pParent; /* The parent of this page. NULL for root */
|
||||||
int idxStart; /* Index in aDisk[] of real data */
|
|
||||||
PageHdr *pHdr; /* Points to aDisk[idxStart] */
|
|
||||||
int nFree; /* Number of free bytes in aDisk[] */
|
int nFree; /* Number of free bytes in aDisk[] */
|
||||||
int nCell; /* Number of entries on this page */
|
int nCell; /* Number of entries on this page */
|
||||||
Cell *apCell[MX_CELL]; /* All data entires in sorted order */
|
Cell *apCell[MX_CELL]; /* All data entires in sorted order */
|
||||||
@@ -221,7 +231,7 @@ struct MemPage {
|
|||||||
struct Btree {
|
struct Btree {
|
||||||
Pager *pPager; /* The page cache */
|
Pager *pPager; /* The page cache */
|
||||||
BtCursor *pCursor; /* A list of all open cursors */
|
BtCursor *pCursor; /* A list of all open cursors */
|
||||||
MemPage *page1; /* First page of the database */
|
PageOne *page1; /* First page of the database */
|
||||||
int inTrans; /* True if a transaction is in progress */
|
int inTrans; /* True if a transaction is in progress */
|
||||||
};
|
};
|
||||||
typedef Btree Bt;
|
typedef Btree Bt;
|
||||||
@@ -244,8 +254,8 @@ struct BtCursor {
|
|||||||
** Compute the total number of bytes that a Cell needs on the main
|
** Compute the total number of bytes that a Cell needs on the main
|
||||||
** database page. The number returned includes the Cell header,
|
** database page. The number returned includes the Cell header,
|
||||||
** local payload storage, and the pointer to overflow pages (if
|
** local payload storage, and the pointer to overflow pages (if
|
||||||
** applicable). The point of this routine is that it does not
|
** applicable). Additional spaced allocated on overflow pages
|
||||||
** include payload storage on overflow pages.
|
** is NOT included in the value returned from this routine.
|
||||||
*/
|
*/
|
||||||
static int cellSize(Cell *pCell){
|
static int cellSize(Cell *pCell){
|
||||||
int n = pCell->h.nKey + pCell->h.nData;
|
int n = pCell->h.nKey + pCell->h.nData;
|
||||||
@@ -269,8 +279,8 @@ static void defragmentPage(MemPage *pPage){
|
|||||||
FreeBlk *pFBlk;
|
FreeBlk *pFBlk;
|
||||||
char newPage[SQLITE_PAGE_SIZE];
|
char newPage[SQLITE_PAGE_SIZE];
|
||||||
|
|
||||||
pc = ROUNDUP(pPage->idxStart + sizeof(PageHdr));
|
pc = sizeof(PageHdr);
|
||||||
pPage->pHdr->firstCell = pc;
|
((PageHdr*)pPage)->firstCell = pc;
|
||||||
memcpy(newPage, pPage->aDisk, pc);
|
memcpy(newPage, pPage->aDisk, pc);
|
||||||
for(i=0; i<pPage->nCell; i++){
|
for(i=0; i<pPage->nCell; i++){
|
||||||
Cell *pCell = &pPage->apCell[i];
|
Cell *pCell = &pPage->apCell[i];
|
||||||
@@ -285,36 +295,36 @@ static void defragmentPage(MemPage *pPage){
|
|||||||
pFBlk = &pPage->aDisk[pc];
|
pFBlk = &pPage->aDisk[pc];
|
||||||
pFBlk->iSize = SQLITE_PAGE_SIZE - pc;
|
pFBlk->iSize = SQLITE_PAGE_SIZE - pc;
|
||||||
pFBlk->iNext = 0;
|
pFBlk->iNext = 0;
|
||||||
pPage->pHdr->firstFree = pc;
|
((PageHdr*)pPage)->firstFree = pc;
|
||||||
memset(&pFBlk[1], 0, SQLITE_PAGE_SIZE - pc - sizeof(FreeBlk));
|
memset(&pFBlk[1], 0, SQLITE_PAGE_SIZE - pc - sizeof(FreeBlk));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Allocate space on a page. The space needs to be at least
|
** Allocate space on a page. The space needs to be at least
|
||||||
** nByte bytes in size. (Actually, all allocations are rounded
|
** nByte bytes in size. nByte must be a multiple of 4.
|
||||||
** up to the next even multiple of 4.) Return the index into
|
**
|
||||||
** pPage->aDisk[] of the first byte of the new allocation.
|
** Return the index into pPage->aDisk[] of the first byte of
|
||||||
** Or return 0 if there is not enough free space on the page to
|
** the new allocation. Or return 0 if there is not enough free
|
||||||
** satisfy the allocation request.
|
** space on the page to satisfy the allocation request.
|
||||||
**
|
**
|
||||||
** If the page contains nBytes of free space but does not contain
|
** If the page contains nBytes of free space but does not contain
|
||||||
** nBytes of contiguous free space, then defragementPage() is
|
** nBytes of contiguous free space, then defragementPage() is
|
||||||
** called to consolidate all free space before allocating the
|
** called to consolidate all free space before allocating the
|
||||||
** new chunk.
|
** new chunk.
|
||||||
*/
|
*/
|
||||||
static int allocSpace(MemPage *pPage, int nByte){
|
static int allocateSpace(MemPage *pPage, int nByte){
|
||||||
FreeBlk *p;
|
FreeBlk *p;
|
||||||
u16 *pIdx;
|
u16 *pIdx;
|
||||||
int start;
|
int start;
|
||||||
|
|
||||||
assert( nByte==ROUNDUP(nByte) );
|
assert( nByte==ROUNDUP(nByte) );
|
||||||
if( pPage->nFree<nByte ) return 0;
|
if( pPage->nFree<nByte ) return 0;
|
||||||
pIdx = &pPage->pHdr->firstFree;
|
pIdx = &((PageHdr*)pPage)->firstFree;
|
||||||
p = (FreeBlk*)&pPage->aDisk[*pIdx];
|
p = (FreeBlk*)&pPage->aDisk[*pIdx];
|
||||||
while( p->iSize<nByte ){
|
while( p->iSize<nByte ){
|
||||||
if( p->iNext==0 ){
|
if( p->iNext==0 ){
|
||||||
defragmentPage(pPage);
|
defragmentPage(pPage);
|
||||||
pIdx = &pPage->pHdr->firstFree;
|
pIdx = &((PageHdr*)pPage)->firstFree;
|
||||||
}else{
|
}else{
|
||||||
pIdx = &p->iNext;
|
pIdx = &p->iNext;
|
||||||
}
|
}
|
||||||
@@ -351,7 +361,7 @@ static void freeSpace(MemPage *pPage, int start, int size){
|
|||||||
|
|
||||||
assert( size == ROUNDUP(size) );
|
assert( size == ROUNDUP(size) );
|
||||||
assert( start == ROUNDUP(start) );
|
assert( start == ROUNDUP(start) );
|
||||||
pIdx = &pPage->pHdr->firstFree;
|
pIdx = &((PageHdr*)pPage)->firstFree;
|
||||||
idx = *pIdx;
|
idx = *pIdx;
|
||||||
while( idx!=0 && idx<start ){
|
while( idx!=0 && idx<start ){
|
||||||
pFBlk = (FreeBlk*)&pPage->aDisk[idx];
|
pFBlk = (FreeBlk*)&pPage->aDisk[idx];
|
||||||
@@ -384,7 +394,9 @@ static void freeSpace(MemPage *pPage, int start, int size){
|
|||||||
/*
|
/*
|
||||||
** Initialize the auxiliary information for a disk block.
|
** Initialize the auxiliary information for a disk block.
|
||||||
**
|
**
|
||||||
** The pParent field is always
|
** The pParent parameter must be a pointer to the MemPage which
|
||||||
|
** is the parent of the page being initialized. The root of the
|
||||||
|
** BTree (page 2) has no parent and so for that page, pParent==NULL.
|
||||||
**
|
**
|
||||||
** Return SQLITE_OK on success. If we see that the page does
|
** Return SQLITE_OK on success. If we see that the page does
|
||||||
** not contained a well-formed database page, then return
|
** not contained a well-formed database page, then return
|
||||||
@@ -408,15 +420,13 @@ static int initPage(MemPage *pPage, Pgno pgnoThis, MemPage *pParent){
|
|||||||
sqlitepager_ref(pParent);
|
sqlitepager_ref(pParent);
|
||||||
}
|
}
|
||||||
if( pPage->isInit ) return SQLITE_OK;
|
if( pPage->isInit ) return SQLITE_OK;
|
||||||
pPage->idxStart = (pgnoThis==1) ? sizeof(Page1Header) : 0;
|
|
||||||
pPage->pHdr = (PageHdr*)&pPage->aDisk[pPage->idxStart];
|
|
||||||
pPage->isInit = 1;
|
pPage->isInit = 1;
|
||||||
pPage->nCell = 0;
|
pPage->nCell = 0;
|
||||||
freeSpace = SQLITE_PAGE_SIZE - pPage->idxStart - sizeof(PageHeader);
|
freeSpace = SQLITE_PAGE_SIZE - sizeof(PageHdr);
|
||||||
idx = pPage->pHdr->firstCell;
|
idx = ((PageHdr*)pPage)->firstCell;
|
||||||
while( idx!=0 ){
|
while( idx!=0 ){
|
||||||
if( idx>SQLITE_PAGE_SIZE-MN_CELL_SIZE ) goto page_format_error;
|
if( idx>SQLITE_PAGE_SIZE-MN_CELL_SIZE ) goto page_format_error;
|
||||||
if( idx<pPage->idxStart + sizeof(PageHeader) ) goto page_format_error;
|
if( idx<sizeof(PageHdr) ) goto page_format_error;
|
||||||
pCell = (Cell*)&pPage->aDisk[idx];
|
pCell = (Cell*)&pPage->aDisk[idx];
|
||||||
sz = cellSize(pCell);
|
sz = cellSize(pCell);
|
||||||
if( idx+sz > SQLITE_PAGE_SIZE ) goto page_format_error;
|
if( idx+sz > SQLITE_PAGE_SIZE ) goto page_format_error;
|
||||||
@@ -425,10 +435,10 @@ static int initPage(MemPage *pPage, Pgno pgnoThis, MemPage *pParent){
|
|||||||
idx = pCell->h.iNext;
|
idx = pCell->h.iNext;
|
||||||
}
|
}
|
||||||
pPage->nFree = 0;
|
pPage->nFree = 0;
|
||||||
idx = pPage->pHdr->firstFree;
|
idx = ((PageHdr*)pPage)->firstFree;
|
||||||
while( idx!=0 ){
|
while( idx!=0 ){
|
||||||
if( idx>SQLITE_PAGE_SIZE-sizeof(FreeBlk) ) goto page_format_error;
|
if( idx>SQLITE_PAGE_SIZE-sizeof(FreeBlk) ) goto page_format_error;
|
||||||
if( idx<pPage->idxStart + sizeof(PageHeader) ) goto page_format_error;
|
if( idx<sizeof(PageHdr) ) goto page_format_error;
|
||||||
pFBlk = (FreeBlk*)&pPage->aDisk[idx];
|
pFBlk = (FreeBlk*)&pPage->aDisk[idx];
|
||||||
pPage->nFree += pFBlk->iSize;
|
pPage->nFree += pFBlk->iSize;
|
||||||
if( pFBlk->iNext <= idx ) goto page_format_error;
|
if( pFBlk->iNext <= idx ) goto page_format_error;
|
||||||
@@ -441,6 +451,51 @@ page_format_error:
|
|||||||
return SQLITE_CORRUPT;
|
return SQLITE_CORRUPT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Recompute the MemPage.apCell[], MemPage.nCell, and MemPage.nFree parameters
|
||||||
|
** for a cell after the content has be changed significantly.
|
||||||
|
**
|
||||||
|
** The computation here is similar to initPage() except that in this case
|
||||||
|
** the MemPage.aDisk[] field has been set up internally (instead of
|
||||||
|
** having been read from disk) so we do not need to do as much error
|
||||||
|
** checking.
|
||||||
|
*/
|
||||||
|
static void reinitPage(MemPage *pPage){
|
||||||
|
Cell *pCell;
|
||||||
|
|
||||||
|
pPage->nCell = 0;
|
||||||
|
idx = ((PageHdr*)pPage)->firstCell;
|
||||||
|
while( idx!=0 ){
|
||||||
|
pCell = (Cell*)&pPage->aDisk[idx];
|
||||||
|
sz = cellSize(pCell);
|
||||||
|
pPage->apCell[pPage->nCell++] = pCell;
|
||||||
|
idx = pCell->h.iNext;
|
||||||
|
}
|
||||||
|
pPage->nFree = 0;
|
||||||
|
idx = ((PageHdr*)pPage)->firstFree;
|
||||||
|
while( idx!=0 ){
|
||||||
|
pFBlk = (FreeBlk*)&pPage->aDisk[idx];
|
||||||
|
pPage->nFree += pFBlk->iSize;
|
||||||
|
idx = pFBlk->iNext;
|
||||||
|
}
|
||||||
|
return SQLITE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Initialize a database page so that it holds no entries at all.
|
||||||
|
*/
|
||||||
|
static void zeroPage(MemPage *pPage){
|
||||||
|
PageHdr *pHdr;
|
||||||
|
FreeBlk *pFBlk;
|
||||||
|
memset(pPage, 0, SQLITE_PAGE_SIZE);
|
||||||
|
pHdr = (PageHdr*)pPage;
|
||||||
|
pHdr->firstCell = 0;
|
||||||
|
pHdr->firstFree = sizeof(*pHdr);
|
||||||
|
pFBlk = (FreeBlk*)&pHdr[1];
|
||||||
|
pFBlk->iNext = 0;
|
||||||
|
pFBlk->iSize = SQLITE_PAGE_SIZE - sizeof(*pHdr);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** This routine is called when the reference count for a page
|
** This routine is called when the reference count for a page
|
||||||
** reaches zero. We need to unref the pParent pointer when that
|
** reaches zero. We need to unref the pParent pointer when that
|
||||||
@@ -511,15 +566,13 @@ static int lockBtree(Btree *pBt){
|
|||||||
if( pBt->page1 ) return SQLITE_OK;
|
if( pBt->page1 ) return SQLITE_OK;
|
||||||
rc = sqlitepager_get(pBt->pPager, 1, &pBt->page1);
|
rc = sqlitepager_get(pBt->pPager, 1, &pBt->page1);
|
||||||
if( rc!=SQLITE_OK ) return rc;
|
if( rc!=SQLITE_OK ) return rc;
|
||||||
rc = initPage(pBt->page1, 1, 0);
|
|
||||||
if( rc!=SQLITE_OK ) goto page1_init_failed;
|
|
||||||
|
|
||||||
/* Do some checking to help insure the file we opened really is
|
/* Do some checking to help insure the file we opened really is
|
||||||
** a valid database file.
|
** a valid database file.
|
||||||
*/
|
*/
|
||||||
if( sqlitepager_pagecount(pBt->pPager)>0 ){
|
if( sqlitepager_pagecount(pBt->pPager)>0 ){
|
||||||
Page1Header *pP1 = (Page1Header*)pBt->page1;
|
PageOne *pP1 = pBt->page1;
|
||||||
if( pP1->magic1!=MAGIC_1 || pP1->magic2!=MAGIC_2 ){
|
if( strcmp(pP1->zMagic1,zMagicHeader)!=0 ){
|
||||||
rc = SQLITE_CORRUPT;
|
rc = SQLITE_CORRUPT;
|
||||||
goto page1_init_failed;
|
goto page1_init_failed;
|
||||||
}
|
}
|
||||||
@@ -607,9 +660,16 @@ int sqliteBtreeCursor(Btree *pBt, BtCursor **ppCur){
|
|||||||
}
|
}
|
||||||
pCur = sqliteMalloc( sizeof(*pCur) );
|
pCur = sqliteMalloc( sizeof(*pCur) );
|
||||||
if( pCur==0 ){
|
if( pCur==0 ){
|
||||||
*ppCur = 0;
|
rc = SQLITE_NOMEM;
|
||||||
unlockBtree(pBt);
|
goto create_cursor_exception;
|
||||||
return SQLITE_NOMEM;
|
}
|
||||||
|
rc = sqlitepager_get(pBt->pPager, 2, &pCur->pPage);
|
||||||
|
if( rc!=SQLITE_OK ){
|
||||||
|
goto create_cursor_exception;
|
||||||
|
}
|
||||||
|
rc = initPage(pCur->pPage, 2, 0);
|
||||||
|
if( rc!=SQLITE_OK ){
|
||||||
|
goto create_cursor_exception;
|
||||||
}
|
}
|
||||||
pCur->pPrev = 0;
|
pCur->pPrev = 0;
|
||||||
pCur->pNext = pBt->pCursor;
|
pCur->pNext = pBt->pCursor;
|
||||||
@@ -618,20 +678,23 @@ int sqliteBtreeCursor(Btree *pBt, BtCursor **ppCur){
|
|||||||
}
|
}
|
||||||
pBt->pCursor = pCur;
|
pBt->pCursor = pCur;
|
||||||
pCur->pBt = pBt;
|
pCur->pBt = pBt;
|
||||||
rc = sqlitepager_get(pBt->pPager, 1, &pCur->pPage);
|
|
||||||
if( rc!=SQLITE_OK ){
|
|
||||||
sqliteFree(pCur);
|
|
||||||
*ppCur = 0;
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
initPage(pCur->pPage, 1, 0);
|
|
||||||
pCur->idx = 0;
|
pCur->idx = 0;
|
||||||
*ppCur = pCur;
|
*ppCur = pCur;
|
||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
|
|
||||||
|
create_cursor_exception:
|
||||||
|
*ppCur = 0;
|
||||||
|
if( pCur ){
|
||||||
|
if( pCur->pPage ) sqlitepager_unref(pCur->pPage);
|
||||||
|
sqliteFree(pCur);
|
||||||
|
}
|
||||||
|
unlinkBtree(pBt);
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Close a cursor.
|
** Close a cursor. The lock on the database file is released
|
||||||
|
** when the last cursor is closed.
|
||||||
*/
|
*/
|
||||||
int sqliteBtreeCloseCursor(BtCursor *pCur){
|
int sqliteBtreeCloseCursor(BtCursor *pCur){
|
||||||
Btree *pBt = pCur->pBt;
|
Btree *pBt = pCur->pBt;
|
||||||
@@ -645,9 +708,7 @@ int sqliteBtreeCloseCursor(BtCursor *pCur){
|
|||||||
pCur->pNext->pPrev = pCur->pPrev;
|
pCur->pNext->pPrev = pCur->pPrev;
|
||||||
}
|
}
|
||||||
sqlitepager_unref(pCur->pPage);
|
sqlitepager_unref(pCur->pPage);
|
||||||
if( pBt->pCursor==0 && pBt->inTrans==0 ){
|
unlockBtree(pBt);
|
||||||
unlockBtree(pBt);
|
|
||||||
}
|
|
||||||
sqliteFree(pCur);
|
sqliteFree(pCur);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -655,7 +716,7 @@ int sqliteBtreeCloseCursor(BtCursor *pCur){
|
|||||||
** Make a temporary cursor by filling in the fields of pTempCur.
|
** Make a temporary cursor by filling in the fields of pTempCur.
|
||||||
** The temporary cursor is not on the cursor list for the Btree.
|
** The temporary cursor is not on the cursor list for the Btree.
|
||||||
*/
|
*/
|
||||||
static void createTemporaryCursor(BtCursor *pCur, BtCursor *pTempCur){
|
static void CreateTemporaryCursor(BtCursor *pCur, BtCursor *pTempCur){
|
||||||
memcpy(pTempCur, pCur, sizeof(*pCur));
|
memcpy(pTempCur, pCur, sizeof(*pCur));
|
||||||
pTempCur->pNext = 0;
|
pTempCur->pNext = 0;
|
||||||
pTempCur->pPrev = 0;
|
pTempCur->pPrev = 0;
|
||||||
@@ -663,17 +724,19 @@ static void createTemporaryCursor(BtCursor *pCur, BtCursor *pTempCur){
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Delete a temporary cursor such as was made by the createTemporaryCursor()
|
** Delete a temporary cursor such as was made by the CreateTemporaryCursor()
|
||||||
** function above.
|
** function above.
|
||||||
*/
|
*/
|
||||||
static void destroyTemporaryCursor(BeCursor *pCur){
|
static void DestroyTemporaryCursor(BeCursor *pCur){
|
||||||
sqlitepager_unref(pCur->pPage);
|
sqlitepager_unref(pCur->pPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Write the number of bytes of key for the entry the cursor is
|
** Set *pSize to the number of bytes of key in the entry the
|
||||||
** pointing to into *pSize. Return SQLITE_OK. Failure is not
|
** cursor currently points to. Always return SQLITE_OK.
|
||||||
** possible.
|
** Failure is not possible. If the cursor is not currently
|
||||||
|
** pointing to an entry (which can happen, for example, if
|
||||||
|
** the database is empty) then *pSize is set to 0.
|
||||||
*/
|
*/
|
||||||
int sqliteBtreeKeySize(BtCursor *pCur, int *pSize){
|
int sqliteBtreeKeySize(BtCursor *pCur, int *pSize){
|
||||||
Cell *pCell;
|
Cell *pCell;
|
||||||
@@ -716,10 +779,9 @@ static int getPayload(BtCursor *pCur, int offset, int amt, char *zBuf){
|
|||||||
offset += a;
|
offset += a;
|
||||||
zBuf += a;
|
zBuf += a;
|
||||||
amt -= a;
|
amt -= a;
|
||||||
if( amt>0 ){
|
}
|
||||||
assert( a==ROUNDUP(a) );
|
if( amt>0 ){
|
||||||
nextPage = *(Pgno*)&aPayload[a];
|
nextPage = pCur->pPage->apCell[pCur->idx].ovfl;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
while( amt>0 && nextPage ){
|
while( amt>0 && nextPage ){
|
||||||
OverflowPage *pOvfl;
|
OverflowPage *pOvfl;
|
||||||
@@ -734,10 +796,10 @@ static int getPayload(BtCursor *pCur, int offset, int amt, char *zBuf){
|
|||||||
a = OVERFLOW_SIZE - offset;
|
a = OVERFLOW_SIZE - offset;
|
||||||
}
|
}
|
||||||
memcpy(zBuf, &pOvfl->aPayload[offset], a);
|
memcpy(zBuf, &pOvfl->aPayload[offset], a);
|
||||||
offset += a;
|
|
||||||
amt -= a;
|
amt -= a;
|
||||||
zBuf += a;
|
zBuf += a;
|
||||||
}
|
}
|
||||||
|
offset -= OVERFLOW_SIZE;
|
||||||
sqlitepager_unref(pOvfl);
|
sqlitepager_unref(pOvfl);
|
||||||
}
|
}
|
||||||
return amt==0 ? SQLITE_OK : SQLITE_CORRUPT;
|
return amt==0 ? SQLITE_OK : SQLITE_CORRUPT;
|
||||||
@@ -764,13 +826,17 @@ int sqliteBtreeKey(BtCursor *pCur, int offset, int amt, char *zBuf){
|
|||||||
}
|
}
|
||||||
pCell = pPage->apCell[pCur->idx];
|
pCell = pPage->apCell[pCur->idx];
|
||||||
if( amt+offset > pCell->h.nKey ){
|
if( amt+offset > pCell->h.nKey ){
|
||||||
|
return SQLITE_ERROR;
|
||||||
|
}
|
||||||
return getPayload(pCur, offset, amt, zBuf);
|
return getPayload(pCur, offset, amt, zBuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Write the number of bytes of data on the entry that the cursor
|
** Set *pSize to the number of bytes of data in the entry the
|
||||||
** is pointing to into *pSize. Return SQLITE_OK. Failure is
|
** cursor currently points to. Always return SQLITE_OK.
|
||||||
** not possible.
|
** Failure is not possible. If the cursor is not currently
|
||||||
|
** pointing to an entry (which can happen, for example, if
|
||||||
|
** the database is empty) then *pSize is set to 0.
|
||||||
*/
|
*/
|
||||||
int sqliteBtreeDataSize(BtCursor *pCur, int *pSize){
|
int sqliteBtreeDataSize(BtCursor *pCur, int *pSize){
|
||||||
Cell *pCell;
|
Cell *pCell;
|
||||||
@@ -807,7 +873,9 @@ int sqliteBtreeData(BtCursor *pCur, int offset, int amt, char *zBuf){
|
|||||||
return SQLITE_ERROR;
|
return SQLITE_ERROR;
|
||||||
}
|
}
|
||||||
pCell = pPage->apCell[pCur->idx];
|
pCell = pPage->apCell[pCur->idx];
|
||||||
if( amt+offset > pCell->h.nKey ){
|
if( amt+offset > pCell->h.nData ){
|
||||||
|
return SQLITE_ERROR;
|
||||||
|
}
|
||||||
return getPayload(pCur, offset + pCell->h.nKey, amt, zBuf);
|
return getPayload(pCur, offset + pCell->h.nKey, amt, zBuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -830,7 +898,7 @@ static int compareKey(BtCursor *pCur, char *pKey, int nKeyOrig, int *pResult){
|
|||||||
|
|
||||||
assert( pCur->pPage );
|
assert( pCur->pPage );
|
||||||
assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
|
assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
|
||||||
pCell = &pCur->pPage->apCell[pCur->idx];
|
pCell = pCur->pPage->apCell[pCur->idx];
|
||||||
if( nKey > pCell->h.nKey ){
|
if( nKey > pCell->h.nKey ){
|
||||||
nKey = pCell->h.nKey;
|
nKey = pCell->h.nKey;
|
||||||
}
|
}
|
||||||
@@ -898,17 +966,15 @@ static int moveToChild(BtCursor *pCur, int newPgno){
|
|||||||
** pCur->idx is set to the cell index that contains the pointer
|
** pCur->idx is set to the cell index that contains the pointer
|
||||||
** to the page we are coming from. If we are coming from the
|
** to the page we are coming from. If we are coming from the
|
||||||
** right-most child page then pCur->idx is set to one more than
|
** right-most child page then pCur->idx is set to one more than
|
||||||
** the largets cell index.
|
** the largest cell index.
|
||||||
*/
|
*/
|
||||||
static int moveToParent(BtCursor *pCur){
|
static int moveToParent(BtCursor *pCur){
|
||||||
Pgno oldPgno;
|
Pgno oldPgno;
|
||||||
MemPage *pParent;
|
MemPage *pParent;
|
||||||
|
|
||||||
pParent = pCur->pPage->pParent;
|
pParent = pCur->pPage->pParent;
|
||||||
|
if( pParent==0 ) return SQLITE_INTERNAL;
|
||||||
oldPgno = sqlitepager_pagenumber(pCur->pPage);
|
oldPgno = sqlitepager_pagenumber(pCur->pPage);
|
||||||
if( pParent==0 ){
|
|
||||||
return SQLITE_INTERNAL;
|
|
||||||
}
|
|
||||||
sqlitepager_ref(pParent);
|
sqlitepager_ref(pParent);
|
||||||
sqlitepager_unref(pCur->pPage);
|
sqlitepager_unref(pCur->pPage);
|
||||||
pCur->pPage = pParent;
|
pCur->pPage = pParent;
|
||||||
@@ -927,8 +993,10 @@ static int moveToParent(BtCursor *pCur){
|
|||||||
*/
|
*/
|
||||||
static int moveToRoot(BtCursor *pCur){
|
static int moveToRoot(BtCursor *pCur){
|
||||||
MemPage *pNew;
|
MemPage *pNew;
|
||||||
pNew = pCur->pBt->page1;
|
int rc;
|
||||||
sqlitepager_ref(pNew);
|
|
||||||
|
rc = sqlitepager_get(pCur->pBt->pPager, 2, &pNew);
|
||||||
|
if( rc ) return rc;
|
||||||
sqlitepager_unref(pCur->pPage);
|
sqlitepager_unref(pCur->pPage);
|
||||||
pCur->pPage = pNew;
|
pCur->pPage = pNew;
|
||||||
pCur->idx = 0;
|
pCur->idx = 0;
|
||||||
@@ -955,17 +1023,23 @@ static int moveToLeftmost(BtCursor *pCur){
|
|||||||
** Return a success code.
|
** Return a success code.
|
||||||
**
|
**
|
||||||
** If an exact match is not found, then the cursor is always
|
** If an exact match is not found, then the cursor is always
|
||||||
** left point at a root page which would hold the entry if it
|
** left pointing at a leaf page which would hold the entry if it
|
||||||
** were present. The cursor might point to an entry that comes
|
** were present. The cursor might point to an entry that comes
|
||||||
** before or after the key.
|
** before or after the key.
|
||||||
**
|
**
|
||||||
** If pRes!=NULL, then *pRes is written with an integer code to
|
** The result of comparing the key with the entry to which the
|
||||||
** describe the results. *pRes is set to 0 if the cursor is left
|
** cursor is left pointing is stored in pCur->iMatch. The same
|
||||||
** pointing at an entry that exactly matches pKey. *pRes is made
|
** value is also written to *pRes if pRes!=NULL. The meaning of
|
||||||
** negative if the cursor is on the largest entry less than pKey.
|
** this value is as follows:
|
||||||
** *pRes is set positive if the cursor is on the smallest entry
|
**
|
||||||
** greater than pKey. *pRes is not changed if the return value
|
** *pRes<0 The cursor is left pointing at an entry that
|
||||||
** is something other than SQLITE_OK;
|
** is larger than pKey.
|
||||||
|
**
|
||||||
|
** *pRes==0 The cursor is left pointing at an entry that
|
||||||
|
** exactly matches pKey.
|
||||||
|
**
|
||||||
|
** *pRes>0 The cursor is left pointing at an entry that
|
||||||
|
** is smaller than pKey.
|
||||||
*/
|
*/
|
||||||
int sqliteBtreeMoveto(BtCursor *pCur, void *pKey, int nKey, int *pRes){
|
int sqliteBtreeMoveto(BtCursor *pCur, void *pKey, int nKey, int *pRes){
|
||||||
int rc;
|
int rc;
|
||||||
@@ -995,7 +1069,7 @@ int sqliteBtreeMoveto(BtCursor *pCur, void *pKey, int nKey, int *pRes){
|
|||||||
}
|
}
|
||||||
assert( lwr==upr+1 );
|
assert( lwr==upr+1 );
|
||||||
if( lwr>=pPage->nCell ){
|
if( lwr>=pPage->nCell ){
|
||||||
chldPg = pPage->pHdr->rightChild;
|
chldPg = ((PageHdr*)pPage)->rightChild;
|
||||||
}else{
|
}else{
|
||||||
chldPg = pPage->apCell[lwr]->h.leftChild;
|
chldPg = pPage->apCell[lwr]->h.leftChild;
|
||||||
}
|
}
|
||||||
@@ -1007,12 +1081,14 @@ int sqliteBtreeMoveto(BtCursor *pCur, void *pKey, int nKey, int *pRes){
|
|||||||
rc = moveToChild(pCur, chldPg);
|
rc = moveToChild(pCur, chldPg);
|
||||||
if( rc ) return rc;
|
if( rc ) return rc;
|
||||||
}
|
}
|
||||||
|
/* NOT REACHED */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Advance the cursor to the next entry in the database. If pRes!=NULL
|
** Advance the cursor to the next entry in the database. If
|
||||||
** then set *pRes=0 on success and set *pRes=1 if the cursor was
|
** successful and pRes!=NULL then set *pRes=0. If the cursor
|
||||||
** pointing to the last entry in the database.
|
** was already pointing to the last entry in the database before
|
||||||
|
** this routine was called, then set *pRes=1 if pRes!=NULL.
|
||||||
*/
|
*/
|
||||||
int sqliteBtreeNext(BtCursor *pCur, int *pRes){
|
int sqliteBtreeNext(BtCursor *pCur, int *pRes){
|
||||||
int rc;
|
int rc;
|
||||||
@@ -1023,8 +1099,8 @@ int sqliteBtreeNext(BtCursor *pCur, int *pRes){
|
|||||||
}
|
}
|
||||||
pCur->idx++;
|
pCur->idx++;
|
||||||
if( pCur->idx>=pCur->pPage->nCell ){
|
if( pCur->idx>=pCur->pPage->nCell ){
|
||||||
if( pPage->pHdr->rightChild ){
|
if( ((PageHdr*)pPage)->rightChild ){
|
||||||
rc = moveToChild(pCur, pPage->pHdr->rightChild);
|
rc = moveToChild(pCur, ((PageHdr*)pPage)->rightChild);
|
||||||
if( rc ) return rc;
|
if( rc ) return rc;
|
||||||
rc = moveToLeftmost(pCur);
|
rc = moveToLeftmost(pCur);
|
||||||
if( rc ) return rc;
|
if( rc ) return rc;
|
||||||
@@ -1061,7 +1137,7 @@ int sqliteBtreeNext(BtCursor *pCur, int *pRes){
|
|||||||
** Do not invoke sqlitepager_unref() on *ppPage if an error is returned.
|
** Do not invoke sqlitepager_unref() on *ppPage if an error is returned.
|
||||||
*/
|
*/
|
||||||
static int allocatePage(Btree *pBt, MemPage **ppPage, Pgno *pPgno){
|
static int allocatePage(Btree *pBt, MemPage **ppPage, Pgno *pPgno){
|
||||||
Page1Header *pPage1 = (Page1Header*)pBt->page1;
|
PageOne *pPage1 = pBt->page1;
|
||||||
if( pPage1->freeList ){
|
if( pPage1->freeList ){
|
||||||
OverflowPage *pOvfl;
|
OverflowPage *pOvfl;
|
||||||
rc = sqlitepager_write(pPage1);
|
rc = sqlitepager_write(pPage1);
|
||||||
@@ -1093,7 +1169,7 @@ static int allocatePage(Btree *pBt, MemPage **ppPage, Pgno *pPgno){
|
|||||||
** needs to do that.
|
** needs to do that.
|
||||||
*/
|
*/
|
||||||
static int freePage(Btree *pBt, void *pPage, Pgno pgno){
|
static int freePage(Btree *pBt, void *pPage, Pgno pgno){
|
||||||
Page1Header *pPage1 = (Page1Header*)pBt->page1;
|
PageOne *pPage1 = pBt->page1;
|
||||||
OverflowPage *pOvfl = (OverflowPage*)pPage;
|
OverflowPage *pOvfl = (OverflowPage*)pPage;
|
||||||
int rc;
|
int rc;
|
||||||
int needOvflUnref = 0;
|
int needOvflUnref = 0;
|
||||||
@@ -1119,6 +1195,8 @@ static int freePage(Btree *pBt, void *pPage, Pgno pgno){
|
|||||||
pOvfl->next = pPage1->freeList;
|
pOvfl->next = pPage1->freeList;
|
||||||
pPage1->freeList = pgno;
|
pPage1->freeList = pgno;
|
||||||
memset(pOvfl->aPayload, 0, OVERFLOW_SIZE);
|
memset(pOvfl->aPayload, 0, OVERFLOW_SIZE);
|
||||||
|
pPage->isInit = 0;
|
||||||
|
assert( pPage->pParent==0 );
|
||||||
rc = sqlitepager_unref(pOvfl);
|
rc = sqlitepager_unref(pOvfl);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@@ -1130,7 +1208,6 @@ static int freePage(Btree *pBt, void *pPage, Pgno pgno){
|
|||||||
static int clearCell(Btree *pBt, Cell *pCell){
|
static int clearCell(Btree *pBt, Cell *pCell){
|
||||||
Pager *pPager = pBt->pPager;
|
Pager *pPager = pBt->pPager;
|
||||||
OverflowPage *pOvfl;
|
OverflowPage *pOvfl;
|
||||||
Page1Header *pPage1 = (Page1Header*)pBt->page1;
|
|
||||||
Pgno ovfl, nextOvfl;
|
Pgno ovfl, nextOvfl;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
@@ -1143,7 +1220,8 @@ static int clearCell(Btree *pBt, Cell *pCell){
|
|||||||
rc = sqlitepager_get(pPager, ovfl, &pOvfl);
|
rc = sqlitepager_get(pPager, ovfl, &pOvfl);
|
||||||
if( rc ) return rc;
|
if( rc ) return rc;
|
||||||
nextOvfl = pOvfl->next;
|
nextOvfl = pOvfl->next;
|
||||||
freePage(pBt, pOvfl, ovfl);
|
rc = freePage(pBt, pOvfl, ovfl);
|
||||||
|
if( rc ) return rc;
|
||||||
ovfl = nextOvfl;
|
ovfl = nextOvfl;
|
||||||
sqlitepager_unref(pOvfl);
|
sqlitepager_unref(pOvfl);
|
||||||
}
|
}
|
||||||
@@ -1208,6 +1286,39 @@ static int fillInCell(
|
|||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Change the MemPage.pParent pointer on the page whose number is
|
||||||
|
** given in the second argument sot that MemPage.pParent holds the
|
||||||
|
** pointer in the third argument.
|
||||||
|
*/
|
||||||
|
static void reparentPage(Pager *pPager, Pgno pgno, MemPage *pNewParent){
|
||||||
|
MemPage *pThis;
|
||||||
|
|
||||||
|
assert( pPager!=0 && pgno!=0 );
|
||||||
|
pThis = sqlitepager_lookup(pPager, pgno);
|
||||||
|
if( pThis && pThis->pParent!=pNewParent ){
|
||||||
|
if( pThis->pParent ) sqlitepager_unref(pThis->pParent);
|
||||||
|
pThis->pParent = pNewParent;
|
||||||
|
if( pNewParent ) sqlitepager_ref(pNewParent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Reparent all children of the given page to be the given page.
|
||||||
|
** In other words, for every child of pPage, invoke reparentPage()
|
||||||
|
** to make sure that child knows that pPage is its parent.
|
||||||
|
**
|
||||||
|
** This routine gets called after you memcpy() one page into
|
||||||
|
** another.
|
||||||
|
*/
|
||||||
|
static void reparentChildPages(Pager *pPager, Page *pPage){
|
||||||
|
int i;
|
||||||
|
for(i=0; i<pPage->nCell; i++){
|
||||||
|
reparentPage(pPager, pPage->apCell[i]->leftChild, pPage);
|
||||||
|
}
|
||||||
|
reparentPage(pPager, ((PageHdr*)pPage)->rightChild, pPage);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Attempt to move N or more bytes out of the page that the cursor
|
** Attempt to move N or more bytes out of the page that the cursor
|
||||||
** points to into the left sibling page. (The left sibling page
|
** points to into the left sibling page. (The left sibling page
|
||||||
@@ -1222,7 +1333,7 @@ static int fillInCell(
|
|||||||
** (3) some kind of file I/O error occurred
|
** (3) some kind of file I/O error occurred
|
||||||
**
|
**
|
||||||
** Note that a partial rotation may have occurred even if this routine
|
** Note that a partial rotation may have occurred even if this routine
|
||||||
** returns FALSE. Failure means we could not rotation a fill N bytes.
|
** returns FALSE. Failure means we could not rotation a full N bytes.
|
||||||
** If it is possible to rotation some smaller number M, then the
|
** If it is possible to rotation some smaller number M, then the
|
||||||
** rotation occurs but we still return false.
|
** rotation occurs but we still return false.
|
||||||
**
|
**
|
||||||
@@ -1267,6 +1378,36 @@ static int rotateRight(BtCursor *pCur, int N){
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Append a cell onto the end of a page.
|
||||||
|
**
|
||||||
|
** The child page of the cell is reparented if pPager!=NULL.
|
||||||
|
*/
|
||||||
|
static void appendCell(
|
||||||
|
Pager *pPager, /* The page cache. Needed for reparenting */
|
||||||
|
Cell *pSrc, /* The Cell to be copied onto a new page */
|
||||||
|
MemPage *pPage /* The page into which the cell is copied */
|
||||||
|
){
|
||||||
|
int pc;
|
||||||
|
int sz;
|
||||||
|
Cell *pDest;
|
||||||
|
|
||||||
|
sz = cellSize(pSrc);
|
||||||
|
pc = allocateSpace(pPage, sz);
|
||||||
|
assert( pc>0 ){
|
||||||
|
pDest = pPage->apCell[pPage->nCell] = &pPage->aDisk[pc];
|
||||||
|
memcpy(pDest, pSrc, sz);
|
||||||
|
pDest->h.iNext = 0;
|
||||||
|
if( pPage->nCell>0 ){
|
||||||
|
pPage->apCell[pPage->nCell-1]->h.iNext = pc;
|
||||||
|
}else{
|
||||||
|
((PageHdr*)pPage)->firstCell = pc;
|
||||||
|
}
|
||||||
|
if( pPager && pDest->h.leftChild ){
|
||||||
|
reparentPage(pPager, pDest->h.leftChild, pPage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Split a single database page into two roughly equal-sized pages.
|
** Split a single database page into two roughly equal-sized pages.
|
||||||
**
|
**
|
||||||
@@ -1291,7 +1432,73 @@ static int split(
|
|||||||
Cell *pCenter, /* Write the cell that divides the two pages here */
|
Cell *pCenter, /* Write the cell that divides the two pages here */
|
||||||
MemPage **ppOut /* If not NULL, put larger cells in new page at *ppOut */
|
MemPage **ppOut /* If not NULL, put larger cells in new page at *ppOut */
|
||||||
){
|
){
|
||||||
|
MemPage *pLeft, *pRight;
|
||||||
|
Pgno pgnoLeft, pgnoRight;
|
||||||
|
PageHdr *pHdr;
|
||||||
|
int rc;
|
||||||
|
Pager *pPager = pCur->pBt->pPager;
|
||||||
|
MemPage tempPage;
|
||||||
|
|
||||||
|
/* Allocate pages to hold cells after the split and make pRight and
|
||||||
|
** pLeft point to the newly allocated pages.
|
||||||
|
*/
|
||||||
|
rc = allocatePage(pCur->pBt, &pLeft, &pgnoLeft);
|
||||||
|
if( rc ) return rc;
|
||||||
|
if( ppOut ){
|
||||||
|
rc = allocatePage(pCur->pBt, &pRight, &pgnoRight);
|
||||||
|
if( rc ){
|
||||||
|
freePage(pCur->pBt, pLeft, pgnoLeft);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
*ppOut = pRight;
|
||||||
|
}else{
|
||||||
|
*ppOut = tempPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy the smaller cells from the original page into the left page
|
||||||
|
** of the split.
|
||||||
|
*/
|
||||||
|
zeroPage(pLeft);
|
||||||
|
if( pCur->idx==0 && pCur->match>0 ){
|
||||||
|
appendCell(pPager, pNewCell, pLeft);
|
||||||
|
}
|
||||||
|
do{
|
||||||
|
assert( i<pPage->nCell );
|
||||||
|
appendCell(pPager, pPage->apCell[i++], pLeft);
|
||||||
|
if( pCur->idx==i && pCur->iMatch>0 ){
|
||||||
|
appendCell(pPager, pNewCell, Left);
|
||||||
|
}
|
||||||
|
}while( pc < SQLITE_PAGE_SIZE/2 );
|
||||||
|
|
||||||
|
/* Copy the middle entry into *pCenter
|
||||||
|
*/
|
||||||
|
assert( i<pPage->nCell );
|
||||||
|
memcpy(pCenter, pPage->aCell[i], cellSize(pPage->aCell[i]));
|
||||||
|
i++;
|
||||||
|
pHdr = (PageHdr*)pLeft;
|
||||||
|
pHdr->rightChild = pCenter->h.leftChild;
|
||||||
|
if( pHdr->rightChild ){
|
||||||
|
reparentPage(pPager, pHdr->rightChild, pLeft);
|
||||||
|
}
|
||||||
|
pCenter->h.leftChild = pgnoLeft;
|
||||||
|
|
||||||
|
/* Copy the larger cells from the original page into the right
|
||||||
|
** page of the split
|
||||||
|
*/
|
||||||
|
zeroPage(pRight);
|
||||||
|
while( i<pPage->nCell ){
|
||||||
|
appendCell(0, pPage->apCell[i++], pRight);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If ppOut==NULL then copy the temporary right page over top of
|
||||||
|
** the original input page.
|
||||||
|
*/
|
||||||
|
if( ppOut==0 ){
|
||||||
|
pRight->pParent = pPage->pParent;
|
||||||
|
pRight->isInit = 1;
|
||||||
|
memcpy(pPage, pRight, sizeof(*pPage));
|
||||||
|
}
|
||||||
|
reparentChildPages(pPager, pPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1310,6 +1517,7 @@ static void unlinkCell(BtCursor *pCur){
|
|||||||
int i; /* Loop counter */
|
int i; /* Loop counter */
|
||||||
|
|
||||||
pPage = pCur->pPage;
|
pPage = pCur->pPage;
|
||||||
|
sqlitepager_write(pPage);
|
||||||
idx = pCur->idx;
|
idx = pCur->idx;
|
||||||
pCell = pPage->apCell[idx];
|
pCell = pPage->apCell[idx];
|
||||||
if( idx==0 ){
|
if( idx==0 ){
|
||||||
@@ -1333,7 +1541,7 @@ static void unlinkCell(BtCursor *pCur){
|
|||||||
** database page that pCur points to. The calling routine has made
|
** database page that pCur points to. The calling routine has made
|
||||||
** sure it will fit. All this routine needs to do is add the Cell
|
** sure it will fit. All this routine needs to do is add the Cell
|
||||||
** to the page. The addToPage() routine should be used for cases
|
** to the page. The addToPage() routine should be used for cases
|
||||||
** were it is not know if the new cell will fit.
|
** were it is not known if the new cell will fit.
|
||||||
**
|
**
|
||||||
** The new cell is added to the page either before or after the cell
|
** The new cell is added to the page either before or after the cell
|
||||||
** to which the cursor is pointing. The new cell is added before
|
** to which the cursor is pointing. The new cell is added before
|
||||||
@@ -1407,6 +1615,8 @@ static int addToPage(BtCursor *pCur, Cell *pNewCell){
|
|||||||
|
|
||||||
for(;;){
|
for(;;){
|
||||||
MemPage *pPage = pCur->pPage;
|
MemPage *pPage = pCur->pPage;
|
||||||
|
rc = sqlitepager_write(pPage);
|
||||||
|
if( rc ) return rc;
|
||||||
int sz = cellSize(pNewCell);
|
int sz = cellSize(pNewCell);
|
||||||
if( sz<=pPage->nFree ){
|
if( sz<=pPage->nFree ){
|
||||||
insertCell(pCur, pNewCell);
|
insertCell(pCur, pNewCell);
|
||||||
@@ -1422,7 +1632,7 @@ static int addToPage(BtCursor *pCur, Cell *pNewCell){
|
|||||||
pHdr = pPage->pHdr;
|
pHdr = pPage->pHdr;
|
||||||
pHdr->right = sqlitepager_pagenumber(pRight);
|
pHdr->right = sqlitepager_pagenumber(pRight);
|
||||||
sqlitepager_unref(pRight);
|
sqlitepager_unref(pRight);
|
||||||
pHdr->firstCell = pc = pPage->idxStart + sizeof(*pHdr);
|
pHdr->firstCell = pc = sizeof(*pHdr);
|
||||||
sz = cellSize(¢erCell);
|
sz = cellSize(¢erCell);
|
||||||
memcpy(&pPage->aDisk[pc], ¢erCell, sz);
|
memcpy(&pPage->aDisk[pc], ¢erCell, sz);
|
||||||
pc += sz;
|
pc += sz;
|
||||||
@@ -1466,6 +1676,8 @@ int sqliteBtreeInsert(
|
|||||||
|
|
||||||
rc = sqliteBtreeMoveTo(pCur, pKey, nKey, &loc);
|
rc = sqliteBtreeMoveTo(pCur, pKey, nKey, &loc);
|
||||||
if( rc ) return rc;
|
if( rc ) return rc;
|
||||||
|
rc = sqlitepager_write(pCur->pPage);
|
||||||
|
if( rc ) return rc;
|
||||||
rc = fillInCell(pBt, &newCell, pKey, nKey, pData, nData);
|
rc = fillInCell(pBt, &newCell, pKey, nKey, pData, nData);
|
||||||
if( rc ) return rc;
|
if( rc ) return rc;
|
||||||
if( loc==0 ){
|
if( loc==0 ){
|
||||||
@@ -1478,23 +1690,51 @@ int sqliteBtreeInsert(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Check the page given as the argument to see if it is less than
|
** Check the page at which the cursor points to see if it is less than
|
||||||
** half full. If it is less than half full, then try to increase
|
** half full. If it is less than half full, then try to increase
|
||||||
** its fill factor by grabbing cells from siblings or by merging
|
** its fill factor by grabbing cells from siblings or by merging
|
||||||
** the page with siblings.
|
** the page with siblings.
|
||||||
*/
|
*/
|
||||||
static int refillPage(Btree *pBt, MemPage *pPage){
|
static int refillPage(BtCursor *pCur){
|
||||||
|
MemPage *pPage;
|
||||||
|
BtCursor tempCur;
|
||||||
|
int rc;
|
||||||
|
Pager *pPager;
|
||||||
|
|
||||||
|
pPage = pCur->pPage;
|
||||||
if( pPage->nFree < SQLITE_PAGE_SIZE/2 ){
|
if( pPage->nFree < SQLITE_PAGE_SIZE/2 ){
|
||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
if( pPage->nCell==0 ){
|
rc = sqlitepager_write(pPage);
|
||||||
assert( pPage->pParent==0 );
|
if( rc ) return rc;
|
||||||
if( pPage->pHdr->rightChild ){
|
pPager = pCur->pBt->pPager;
|
||||||
|
|
||||||
|
if( pPage->nCell==0 ){
|
||||||
|
/* The page being refilled is the root of the BTree and it has
|
||||||
|
** no entries of its own. If there is a child page, then make the
|
||||||
|
** child become the new root.
|
||||||
|
*/
|
||||||
|
MemPage *pChild;
|
||||||
|
Pgno pgnoChild;
|
||||||
|
assert( pPage->pParent==0 );
|
||||||
|
assert( sqlitepager_pagenumber(pPage)==2 );
|
||||||
|
pgnoChild = ((PageHdr*)pPage)->rightChild;
|
||||||
|
if( pgnoChild==0 ){
|
||||||
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
rc = sqlitepager_get(pPager, pgno, &pChild);
|
||||||
|
if( rc ) return rc;
|
||||||
|
memcpy(pPage, pChild, SQLITE_PAGE_SIZE);
|
||||||
|
memset(&pPage->aDisk[SQLITE_PAGE_SIZE], 0, EXTRA_SIZE);
|
||||||
|
freePage(pCur->pBt, pChild, pgnoChild);
|
||||||
|
sqlitepager_unref(pChild);
|
||||||
|
rc = initPage(pPage, 2, 0);
|
||||||
|
reparentChildPages(pPager, pPage);
|
||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** merge with siblings **/
|
/** merge with siblings **/
|
||||||
|
|
||||||
/** borrow from siblings **/
|
/** borrow from siblings **/
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1506,7 +1746,7 @@ static int refillPage(Btree *pBt, MemPage *pPage){
|
|||||||
** If the size of pNewContent is greater than the current size of the
|
** If the size of pNewContent is greater than the current size of the
|
||||||
** cursor cell then the page that cursor points to might have to split.
|
** cursor cell then the page that cursor points to might have to split.
|
||||||
*/
|
*/
|
||||||
static int replaceContent(BtCursor *pCur, Cell *pNewContent){
|
static int ReplaceContentOfCell(BtCursor *pCur, Cell *pNewContent){
|
||||||
Cell *pCell; /* The cell whose content will be changed */
|
Cell *pCell; /* The cell whose content will be changed */
|
||||||
Pgno pgno; /* Temporary storage for a page number */
|
Pgno pgno; /* Temporary storage for a page number */
|
||||||
|
|
||||||
@@ -1522,19 +1762,24 @@ static int replaceContent(BtCursor *pCur, Cell *pNewContent){
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Delete the record that the cursor is pointing to.
|
** Delete the entry that the cursor is pointing to.
|
||||||
**
|
**
|
||||||
** The cursor is left point at either the next or the previous
|
** The cursor is left pointing at either the next or the previous
|
||||||
** entry. If left pointing to the next entry, then the pCur->bSkipNext
|
** entry. If the cursor is left pointing to the next entry, then
|
||||||
** flag is set which forces the next call to sqliteBtreeNext() to be
|
** the pCur->bSkipNext flag is set which forces the next call to
|
||||||
** a no-op. That way, you can always call sqliteBtreeNext() after
|
** sqliteBtreeNext() to be a no-op. That way, you can always call
|
||||||
** a delete and the cursor will be left pointing to the first entry
|
** sqliteBtreeNext() after a delete and the cursor will be left
|
||||||
** after the deleted entry.
|
** pointing to the first entry after the deleted entry.
|
||||||
*/
|
*/
|
||||||
int sqliteBtreeDelete(BtCursor *pCur){
|
int sqliteBtreeDelete(BtCursor *pCur){
|
||||||
MemPage *pPage = pCur->pPage;
|
MemPage *pPage = pCur->pPage;
|
||||||
Cell *pCell;
|
Cell *pCell;
|
||||||
int rc;
|
int rc;
|
||||||
|
if( pCur->idx >= pPage->nCell ){
|
||||||
|
return SQLITE_ERROR; /* The cursor is not pointing to anything */
|
||||||
|
}
|
||||||
|
rc = sqlitepager_write(pPage);
|
||||||
|
if( rc ) return rc;
|
||||||
pCell = pPage->apCell[pCur->idx];
|
pCell = pPage->apCell[pCur->idx];
|
||||||
if( pPage->pHdr->rightChild ){
|
if( pPage->pHdr->rightChild ){
|
||||||
/* The entry to be deleted is not on a leaf page. Non-leaf entries
|
/* The entry to be deleted is not on a leaf page. Non-leaf entries
|
||||||
@@ -1545,14 +1790,14 @@ int sqliteBtreeDelete(BtCursor *pCur){
|
|||||||
** entry, then delete the next entry.
|
** entry, then delete the next entry.
|
||||||
*/
|
*/
|
||||||
BtCursor origCur;
|
BtCursor origCur;
|
||||||
createTemporaryCursor(pCur, &origCur);
|
CreateTemporaryCursor(pCur, &origCur);
|
||||||
rc = sqliteBtreeNext(pCur, 0);
|
rc = sqliteBtreeNext(pCur, 0);
|
||||||
if( rc==SQLITE_OK ){
|
if( rc==SQLITE_OK ){
|
||||||
pPage = pCur->pPage;
|
pPage = pCur->pPage;
|
||||||
pCell = pPage->apCell[pCur->idx];
|
pCell = pPage->apCell[pCur->idx];
|
||||||
rc = replaceContent(&origCur, pCell);
|
rc = ReplaceContentOfCell(&origCur, pCell);
|
||||||
}
|
}
|
||||||
destroyTemporaryCursor(&origCur);
|
DestroyTemporaryCursor(&origCur);
|
||||||
if( rc ) return rc;
|
if( rc ) return rc;
|
||||||
}
|
}
|
||||||
rc = clearCell(pCell);
|
rc = clearCell(pCell);
|
||||||
@@ -1563,6 +1808,6 @@ int sqliteBtreeDelete(BtCursor *pCur){
|
|||||||
}else{
|
}else{
|
||||||
pCur->idx--;
|
pCur->idx--;
|
||||||
}
|
}
|
||||||
rc = refillPage(pCur->pBt, pPage);
|
rc = refillPage(pCur);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@@ -24,7 +24,7 @@
|
|||||||
** This header file defines the interface that the sqlite B-Tree file
|
** This header file defines the interface that the sqlite B-Tree file
|
||||||
** subsystem.
|
** subsystem.
|
||||||
**
|
**
|
||||||
** @(#) $Id: btree.h,v 1.2 2001/05/24 21:06:36 drh Exp $
|
** @(#) $Id: btree.h,v 1.3 2001/06/02 02:40:57 drh Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
typedef struct Btree Btree;
|
typedef struct Btree Btree;
|
||||||
@@ -37,8 +37,10 @@ int sqliteBtreeBeginTrans(Btree*);
|
|||||||
int sqliteBtreeCommit(Btree*);
|
int sqliteBtreeCommit(Btree*);
|
||||||
int sqliteBtreeRollback(Btree*);
|
int sqliteBtreeRollback(Btree*);
|
||||||
|
|
||||||
|
int sqliteBtreeCreateTable(Btree*, int*);
|
||||||
|
int sqliteBtreeDropTable(Btree*, int);
|
||||||
|
|
||||||
int sqliteBtreeCursor(Btree*, BtCursor **ppCur);
|
int sqliteBtreeCursor(Btree*, int iTable, BtCursor **ppCur);
|
||||||
int sqliteBtreeMoveto(BtCursor*, void *pKey, int nKey, *pRes);
|
int sqliteBtreeMoveto(BtCursor*, void *pKey, int nKey, *pRes);
|
||||||
int sqliteBtreeDelete(BtCursor*);
|
int sqliteBtreeDelete(BtCursor*);
|
||||||
int sqliteBtreeInsert(BtCursor*, void *pKey, int nKey, void *pData, int nData);
|
int sqliteBtreeInsert(BtCursor*, void *pKey, int nKey, void *pData, int nData);
|
||||||
|
@@ -27,7 +27,7 @@
|
|||||||
** all writes in order to support rollback. Locking is used to limit
|
** all writes in order to support rollback. Locking is used to limit
|
||||||
** access to one or more reader or one writer.
|
** access to one or more reader or one writer.
|
||||||
**
|
**
|
||||||
** @(#) $Id: pager.c,v 1.7 2001/05/24 21:06:36 drh Exp $
|
** @(#) $Id: pager.c,v 1.8 2001/06/02 02:40:57 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
#include "pager.h"
|
#include "pager.h"
|
||||||
@@ -74,6 +74,8 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
** Each in-memory image of a page begins with the following header.
|
** Each in-memory image of a page begins with the following header.
|
||||||
|
** This header is only visible to this pager module. The client
|
||||||
|
** code that calls pager sees only the data that follows the header.
|
||||||
*/
|
*/
|
||||||
typedef struct PgHdr PgHdr;
|
typedef struct PgHdr PgHdr;
|
||||||
struct PgHdr {
|
struct PgHdr {
|
||||||
|
273
src/test3.c
Normal file
273
src/test3.c
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
/*
|
||||||
|
** 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/
|
||||||
|
**
|
||||||
|
*************************************************************************
|
||||||
|
** Code for testing the btree.c module in SQLite. This code
|
||||||
|
** is not included in the SQLite library. It is used for automated
|
||||||
|
** testing of the SQLite library.
|
||||||
|
**
|
||||||
|
** $Id: test3.c,v 1.1 2001/06/02 02:40:57 drh Exp $
|
||||||
|
*/
|
||||||
|
#include "sqliteInt.h"
|
||||||
|
#include "pager.h"
|
||||||
|
#include "btree.h"
|
||||||
|
#include "tcl.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Interpret an SQLite error number
|
||||||
|
*/
|
||||||
|
static char *errorName(int rc){
|
||||||
|
char *zName;
|
||||||
|
switch( rc ){
|
||||||
|
case SQLITE_OK: zName = "SQLITE_OK"; break;
|
||||||
|
case SQLITE_ERROR: zName = "SQLITE_ERROR"; break;
|
||||||
|
case SQLITE_INTERNAL: zName = "SQLITE_INTERNAL"; break;
|
||||||
|
case SQLITE_PERM: zName = "SQLITE_PERM"; break;
|
||||||
|
case SQLITE_ABORT: zName = "SQLITE_ABORT"; break;
|
||||||
|
case SQLITE_BUSY: zName = "SQLITE_BUSY"; break;
|
||||||
|
case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break;
|
||||||
|
case SQLITE_READONLY: zName = "SQLITE_READONLY"; break;
|
||||||
|
case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break;
|
||||||
|
case SQLITE_IOERR: zName = "SQLITE_IOERR"; break;
|
||||||
|
case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break;
|
||||||
|
case SQLITE_NOTFOUND: zName = "SQLITE_NOTFOUND"; break;
|
||||||
|
case SQLITE_FULL: zName = "SQLITE_FULL"; break;
|
||||||
|
case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break;
|
||||||
|
case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break;
|
||||||
|
default: zName = "SQLITE_Unknown"; break;
|
||||||
|
}
|
||||||
|
return zName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Usage: btree_open FILENAME
|
||||||
|
**
|
||||||
|
** Open a new database
|
||||||
|
*/
|
||||||
|
static int btree_open(
|
||||||
|
void *NotUsed,
|
||||||
|
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||||
|
int argc, /* Number of arguments */
|
||||||
|
char **argv /* Text of each argument */
|
||||||
|
){
|
||||||
|
BTree *pBt;
|
||||||
|
int nPage;
|
||||||
|
int rc;
|
||||||
|
char zBuf[100];
|
||||||
|
if( argc!=2 ){
|
||||||
|
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||||
|
" FILENAME\"", 0);
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
rc = sqliteBtreeOpen(argv[1], 0666, &pBt);
|
||||||
|
if( rc!=SQLITE_OK ){
|
||||||
|
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
sprintf(zBuf,"0x%x",(int)pBt);
|
||||||
|
Tcl_AppendResult(interp, zBuf, 0);
|
||||||
|
return TCL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Usage: btree_close ID
|
||||||
|
**
|
||||||
|
** Close the given database.
|
||||||
|
*/
|
||||||
|
static int btree_close(
|
||||||
|
void *NotUsed,
|
||||||
|
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||||
|
int argc, /* Number of arguments */
|
||||||
|
char **argv /* Text of each argument */
|
||||||
|
){
|
||||||
|
Btree *pBt;
|
||||||
|
int rc;
|
||||||
|
if( argc!=2 ){
|
||||||
|
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||||
|
" ID\"", 0);
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
if( Tcl_GetInt(interp, argv[1], (int*)&pBt) ) return TCL_ERROR;
|
||||||
|
rc = sqliteBtreeClose(pBt);
|
||||||
|
if( rc!=SQLITE_OK ){
|
||||||
|
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
return TCL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Usage: btree_begin_transaction ID
|
||||||
|
**
|
||||||
|
** Start a new transaction
|
||||||
|
*/
|
||||||
|
static int btree_begin_transaction(
|
||||||
|
void *NotUsed,
|
||||||
|
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||||
|
int argc, /* Number of arguments */
|
||||||
|
char **argv /* Text of each argument */
|
||||||
|
){
|
||||||
|
Btree *pBt;
|
||||||
|
int rc;
|
||||||
|
if( argc!=2 ){
|
||||||
|
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||||
|
" ID\"", 0);
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
if( Tcl_GetInt(interp, argv[1], (int*)&pBt) ) return TCL_ERROR;
|
||||||
|
rc = sqliteBtreeBeginTrans(pBt);
|
||||||
|
if( rc!=SQLITE_OK ){
|
||||||
|
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
return TCL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Usage: btree_rollback ID
|
||||||
|
**
|
||||||
|
** Rollback changes
|
||||||
|
*/
|
||||||
|
static int btree_rollback(
|
||||||
|
void *NotUsed,
|
||||||
|
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||||
|
int argc, /* Number of arguments */
|
||||||
|
char **argv /* Text of each argument */
|
||||||
|
){
|
||||||
|
Btree *pBt
|
||||||
|
int rc;
|
||||||
|
if( argc!=2 ){
|
||||||
|
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||||
|
" ID\"", 0);
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
if( Tcl_GetInt(interp, argv[1], (int*)&pBt) ) return TCL_ERROR;
|
||||||
|
rc = sqliteBtreeRollback(pBt);
|
||||||
|
if( rc!=SQLITE_OK ){
|
||||||
|
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
return TCL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Usage: btree_commit ID
|
||||||
|
**
|
||||||
|
** Commit all changes
|
||||||
|
*/
|
||||||
|
static int btree_commit(
|
||||||
|
void *NotUsed,
|
||||||
|
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||||
|
int argc, /* Number of arguments */
|
||||||
|
char **argv /* Text of each argument */
|
||||||
|
){
|
||||||
|
Btree *pBt;
|
||||||
|
int rc;
|
||||||
|
if( argc!=2 ){
|
||||||
|
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||||
|
" ID\"", 0);
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
if( Tcl_GetInt(interp, argv[1], (int*)&pBt) ) return TCL_ERROR;
|
||||||
|
rc = sqliteBtreeCommit(pBt);
|
||||||
|
if( rc!=SQLITE_OK ){
|
||||||
|
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
return TCL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Usage: btree_create_table ID
|
||||||
|
**
|
||||||
|
** Create a new table in the database
|
||||||
|
*/
|
||||||
|
static int btree_create_table(
|
||||||
|
void *NotUsed,
|
||||||
|
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||||
|
int argc, /* Number of arguments */
|
||||||
|
char **argv /* Text of each argument */
|
||||||
|
){
|
||||||
|
Btree *pBt;
|
||||||
|
int rc, iTable;
|
||||||
|
char zBuf[30];
|
||||||
|
if( argc!=2 ){
|
||||||
|
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||||
|
" ID\"", 0);
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
if( Tcl_GetInt(interp, argv[1], (int*)&pBt) ) return TCL_ERROR;
|
||||||
|
rc = sqliteBtreeCreateTable(pBt, &iTable);
|
||||||
|
if( rc!=SQLITE_OK ){
|
||||||
|
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
sprintf(zBuf, "%d", iTable);
|
||||||
|
Tcl_AppendResult(interp, zBuf, 0);
|
||||||
|
return TCL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Usage: btree_drop_table ID TABLENUM
|
||||||
|
**
|
||||||
|
** Delete an entire table from the database
|
||||||
|
*/
|
||||||
|
static int btree_drop_table(
|
||||||
|
void *NotUsed,
|
||||||
|
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
|
||||||
|
int argc, /* Number of arguments */
|
||||||
|
char **argv /* Text of each argument */
|
||||||
|
){
|
||||||
|
Pager *pPager;
|
||||||
|
int iTable;
|
||||||
|
char zBuf[100];
|
||||||
|
if( argc!=3 ){
|
||||||
|
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
|
||||||
|
" ID TABLENUM\"", 0);
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
if( Tcl_GetInt(interp, argv[1], (int*)&pBt) ) return TCL_ERROR;
|
||||||
|
if( Tcl_GetInt(interp, argv[2], &iTable ) return TCL_ERROR;
|
||||||
|
rc = sqliteBtreeDropTable(pBt, iTable);
|
||||||
|
if( rc!=SQLITE_OK ){
|
||||||
|
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
return TCL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Register commands with the TCL interpreter.
|
||||||
|
*/
|
||||||
|
int Sqlitetest3_Init(Tcl_Interp *interp){
|
||||||
|
Tcl_CreateCommand(interp, "btree_open", btree_open, 0, 0);
|
||||||
|
Tcl_CreateCommand(interp, "btree_close", btree_close, 0, 0);
|
||||||
|
Tcl_CreateCommand(interp, "btree_begin_transaction",
|
||||||
|
btree_begin_transaction, 0, 0);
|
||||||
|
Tcl_CreateCommand(interp, "btree_commit", btree_commit, 0, 0);
|
||||||
|
Tcl_CreateCommand(interp, "btree_rollback", btree_rollback, 0, 0);
|
||||||
|
Tcl_CreateCommand(interp, "btree_create_table", btree_create_table, 0, 0);
|
||||||
|
Tcl_CreateCommand(interp, "btree_drop_table", btree_drop_table, 0, 0);
|
||||||
|
return TCL_OK;
|
||||||
|
}
|
Reference in New Issue
Block a user