aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bitvec.c208
-rw-r--r--src/pager.c114
-rw-r--r--src/sqliteInt.h9
-rw-r--r--src/test2.c105
4 files changed, 368 insertions, 68 deletions
diff --git a/src/bitvec.c b/src/bitvec.c
new file mode 100644
index 000000000..23a002c64
--- /dev/null
+++ b/src/bitvec.c
@@ -0,0 +1,208 @@
+/*
+** 2008 February 16
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file implements an object that represents a fixed-length
+** bitmap. Bits are numbered starting with 1.
+**
+** A bitmap is used to record what pages a database file have been
+** journalled during a transaction. Usually only a few pages are
+** journalled. So the bitmap is usually sparse and has low cardinality.
+** But sometimes (for example when during a DROP of a large table) most
+** or all of the pages get journalled. In those cases, the bitmap becomes
+** dense. The algorithm needs to handle both cases well.
+**
+** The size of the bitmap is fixed when the object is created.
+**
+** All bits are clear when the bitmap is created. Individual bits
+** may be set or cleared one at a time.
+**
+** Test operations are about 100 times more common that set operations.
+** Clear operations are exceedingly rare. There are usually between
+** 5 and 500 set operations per Bitvec object, though the number of sets can
+** sometimes grow into tens of thousands or larger. The size of the
+** Bitvec object is the number of pages in the database file at the
+** start of a transaction, and is thus usually less than a few thousand,
+** but can be as large as 2 billion for a really big database.
+**
+** @(#) $Id: bitvec.c,v 1.1 2008/02/18 14:47:34 drh Exp $
+*/
+#include "sqliteInt.h"
+
+#define BITVEC_SZ 512
+#define BITVEC_NCHAR (BITVEC_SZ-12)
+#define BITVEC_NBIT (BITVEC_NCHAR*8)
+#define BITVEC_NINT ((BITVEC_SZ-12)/4)
+#define BITVEC_MXHASH (BITVEC_NINT/2)
+#define BITVEC_NPTR ((BITVEC_SZ-12)/8)
+
+#define BITVEC_HASH(X) (((X)*37)%BITVEC_NINT)
+
+/*
+** A bitmap is an instance of the following structure.
+**
+** This bitmap records the existance of zero or more bits
+** with values between 1 and iSize, inclusive.
+**
+** There are three possible representations of the bitmap.
+** If iSize<=BITVEC_NBIT, then Bitvec.u.aBitmap[] is a straight
+** bitmap. The least significant bit is bit 1.
+**
+** If iSize>BITVEC_NBIT and iDivisor==0 then Bitvec.u.aHash[] is
+** a hash table that will hold up to BITVEC_MXHASH distinct values.
+**
+** Otherwise, the value i is redirected into one of BITVEC_NPTR
+** sub-bitmaps pointed to by Bitvec.u.apSub[]. Each subbitmap
+** handles up to iDivisor separate values of i. apSub[0] holds
+** values between 1 and iDivisor. apSub[1] holds values between
+** iDivisor+1 and 2*iDivisor. apSub[N] holds values between
+** N*iDivisor+1 and (N+1)*iDivisor. Each subbitmap is normalized
+** to hold deal with values between 1 and iDivisor.
+*/
+struct Bitvec {
+ u32 iSize; /* Maximum bit index */
+ u32 nSet; /* Number of bits that are set */
+ u32 iDivisor; /* Number of bits handled by each apSub[] entry */
+ union {
+ u8 aBitmap[BITVEC_NCHAR]; /* Bitmap representation */
+ u32 aHash[BITVEC_NINT]; /* Hash table representation */
+ Bitvec *apSub[BITVEC_NPTR]; /* Recursive representation */
+ } u;
+};
+
+/*
+** Create a new bitmap object able to handle bits between 0 and iSize,
+** inclusive. Return a pointer to the new object. Return NULL if
+** malloc fails.
+*/
+Bitvec *sqlite3BitvecCreate(u32 iSize){
+ Bitvec *p;
+ assert( sizeof(*p)==BITVEC_SZ );
+ p = sqlite3MallocZero( sizeof(*p) );
+ if( p ){
+ p->iSize = iSize;
+ }
+ return p;
+}
+
+/*
+** Check to see if the i-th bit is set. Return true or false.
+** If p is NULL (if the bitmap has not been created) or if
+** i is out of range, then return false.
+*/
+int sqlite3BitvecTest(Bitvec *p, u32 i){
+ assert( i>0 );
+ if( p==0 ) return 0;
+ if( i>p->iSize ) return 0;
+ if( p->iSize<=BITVEC_NBIT ){
+ i--;
+ return (p->u.aBitmap[i/8] & (1<<(i&7)))!=0;
+ }
+ if( p->iDivisor>0 ){
+ u32 bin = (i-1)/p->iDivisor;
+ i = (i-1)%p->iDivisor + 1;
+ return sqlite3BitvecTest(p->u.apSub[bin], i);
+ }else{
+ u32 h = BITVEC_HASH(i);
+ while( p->u.aHash[h] ){
+ if( p->u.aHash[h]==i ) return 1;
+ h++;
+ if( h>=BITVEC_NINT ) h = 0;
+ }
+ return 0;
+ }
+}
+
+/*
+** Set the i-th bit. Return 0 on success and an error code if
+** anything goes wrong.
+*/
+int sqlite3BitvecSet(Bitvec *p, u32 i){
+ u32 h;
+ assert( p!=0 );
+ if( p->iSize<=BITVEC_NBIT ){
+ i--;
+ p->u.aBitmap[i/8] |= 1 << (i&7);
+ return SQLITE_OK;
+ }
+ if( p->iDivisor ){
+ u32 bin = (i-1)/p->iDivisor;
+ i = (i-1)%p->iDivisor + 1;
+ if( p->u.apSub[bin]==0 ){
+ sqlite3FaultBenign(SQLITE_FAULTINJECTOR_MALLOC, 1);
+ p->u.apSub[bin] = sqlite3BitvecCreate( p->iDivisor );
+ sqlite3FaultBenign(SQLITE_FAULTINJECTOR_MALLOC, 0);
+ if( p->u.apSub[bin]==0 ) return SQLITE_NOMEM;
+ }
+ return sqlite3BitvecSet(p->u.apSub[bin], i);
+ }
+ h = BITVEC_HASH(i);
+ while( p->u.aHash[h] ){
+ if( p->u.aHash[h]==i ) return SQLITE_OK;
+ h++;
+ if( h==BITVEC_NINT ) h = 0;
+ }
+ p->nSet++;
+ if( p->nSet>=BITVEC_MXHASH ){
+ int j, rc;
+ u32 aiValues[BITVEC_NINT];
+ memcpy(aiValues, p->u.aHash, sizeof(aiValues));
+ memset(p->u.apSub, 0, sizeof(p->u.apSub[0])*BITVEC_NPTR);
+ p->iDivisor = (p->iSize + BITVEC_NPTR - 1)/BITVEC_NPTR;
+ sqlite3BitvecSet(p, i);
+ for(rc=j=0; j<BITVEC_NINT; j++){
+ if( aiValues[j] ) rc |= sqlite3BitvecSet(p, aiValues[j]);
+ }
+ return rc;
+ }
+ p->u.aHash[h] = i;
+ return SQLITE_OK;
+}
+
+/*
+** Clear the i-th bit. Return 0 on success and an error code if
+** anything goes wrong.
+*/
+void sqlite3BitvecClear(Bitvec *p, u32 i){
+ assert( p!=0 );
+ if( p->iSize<=BITVEC_NBIT ){
+ i--;
+ p->u.aBitmap[i/8] &= ~(1 << (i&7));
+ }else if( p->iDivisor ){
+ u32 bin = (i-1)/p->iDivisor;
+ i = (i-1)%p->iDivisor + 1;
+ if( p->u.apSub[bin] ){
+ sqlite3BitvecClear(p->u.apSub[bin], i);
+ }
+ }else{
+ int j;
+ u32 aiValues[BITVEC_NINT];
+ memcpy(aiValues, p->u.aHash, sizeof(aiValues));
+ memset(p->u.aHash, 0, sizeof(p->u.aHash[0])*BITVEC_NINT);
+ p->nSet = 0;
+ for(j=0; j<BITVEC_NINT; j++){
+ if( aiValues[j] && aiValues[j]!=i ) sqlite3BitvecSet(p, aiValues[j]);
+ }
+ }
+}
+
+/*
+** Destroy a bitmap object. Reclaim all memory used.
+*/
+void sqlite3BitvecDestroy(Bitvec *p){
+ if( p==0 ) return;
+ if( p->iDivisor ){
+ int i;
+ for(i=0; i<BITVEC_NPTR; i++){
+ sqlite3BitvecDestroy(p->u.apSub[i]);
+ }
+ }
+ sqlite3_free(p);
+}
diff --git a/src/pager.c b/src/pager.c
index 7780110ec..80469db9f 100644
--- a/src/pager.c
+++ b/src/pager.c
@@ -18,7 +18,7 @@
** file simultaneously, or one process from reading the database while
** another is writing.
**
-** @(#) $Id: pager.c,v 1.406 2008/02/14 23:26:56 drh Exp $
+** @(#) $Id: pager.c,v 1.407 2008/02/18 14:47:34 drh Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
@@ -213,9 +213,9 @@ struct PagerLruLink {
** has been synced to disk. For pages that are in the original
** database file, the following expression should always be true:
**
-** inJournal = (pPager->aInJournal[(pgno-1)/8] & (1<<((pgno-1)%8))!=0
+** inJournal = sqlite3BitvecTest(pPager->pInJournal, pgno)
**
-** The pPager->aInJournal[] array is only valid for the original
+** The pPager->pInJournal object is only valid for the original
** pages of the database, not new pages that are added to the end
** of the database, so obviously the above expression cannot be
** valid for new pages. For new pages inJournal is always 0.
@@ -365,8 +365,8 @@ struct Pager {
int nRef; /* Number of in-memory pages with PgHdr.nRef>0 */
int mxPage; /* Maximum number of pages to hold in cache */
Pgno mxPgno; /* Maximum allowed size of the database */
- u8 *aInJournal; /* One bit for each page in the database file */
- u8 *aInStmt; /* One bit for each page in the database */
+ Bitvec *pInJournal; /* One bit for each page in the database file */
+ Bitvec *pInStmt; /* One bit for each page in the database */
char *zFilename; /* Name of the database file */
char *zJournal; /* Name of the journal file */
char *zDirectory; /* Directory hold database and journal files */
@@ -666,9 +666,7 @@ static int pageInStatement(PgHdr *pPg){
if( MEMDB ){
return PGHDR_TO_HIST(pPg, pPager)->inStmt;
}else{
- Pgno pgno = pPg->pgno;
- u8 *a = pPager->aInStmt;
- return (a && (int)pgno<=pPager->stmtSize && (a[pgno/8] & (1<<(pgno&7))));
+ return sqlite3BitvecTest(pPager->pInStmt, pPg->pgno);
}
}
@@ -1252,14 +1250,14 @@ static void pager_unlock(Pager *pPager){
pager_reset(pPager);
if( pPager->stmtOpen ){
sqlite3OsClose(pPager->stfd);
- sqlite3_free(pPager->aInStmt);
- pPager->aInStmt = 0;
+ sqlite3BitvecDestroy(pPager->pInStmt);
+ pPager->pInStmt = 0;
}
if( pPager->journalOpen ){
sqlite3OsClose(pPager->jfd);
pPager->journalOpen = 0;
- sqlite3_free(pPager->aInJournal);
- pPager->aInJournal = 0;
+ sqlite3BitvecDestroy(pPager->pInJournal);
+ pPager->pInJournal = 0;
}
pPager->stmtOpen = 0;
pPager->stmtInUse = 0;
@@ -1334,8 +1332,8 @@ static int pager_end_transaction(Pager *pPager){
rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
}
}
- sqlite3_free( pPager->aInJournal );
- pPager->aInJournal = 0;
+ sqlite3BitvecDestroy(pPager->pInJournal);
+ pPager->pInJournal = 0;
for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
pPg->inJournal = 0;
pPg->dirty = 0;
@@ -1349,7 +1347,7 @@ static int pager_end_transaction(Pager *pPager){
pPager->dirtyCache = 0;
pPager->nRec = 0;
}else{
- assert( pPager->aInJournal==0 );
+ assert( pPager->pInJournal==0 );
assert( pPager->dirtyCache==0 || pPager->useJournal==0 );
}
@@ -2661,7 +2659,7 @@ int sqlite3PagerClose(Pager *pPager){
if( pPager->journalOpen ){
sqlite3OsClose(pPager->jfd);
}
- sqlite3_free(pPager->aInJournal);
+ sqlite3BitvecDestroy(pPager->pInJournal);
if( pPager->stmtOpen ){
sqlite3OsClose(pPager->stfd);
}
@@ -3625,17 +3623,8 @@ static int pagerAcquire(
pPg->pgno = pgno;
assert( !MEMDB || pgno>pPager->stmtSize );
- if( pPager->aInJournal && (int)pgno<=pPager->origDbSize ){
-#if 0
- sqlite3CheckMemory(pPager->aInJournal, pgno/8);
-#endif
- assert( pPager->journalOpen );
- pPg->inJournal = (pPager->aInJournal[pgno/8] & (1<<(pgno&7)))!=0;
- pPg->needSync = 0;
- }else{
- pPg->inJournal = 0;
- pPg->needSync = 0;
- }
+ pPg->inJournal = sqlite3BitvecTest(pPager->pInJournal, pgno);
+ pPg->needSync = 0;
makeClean(pPg);
pPg->nRef = 1;
@@ -3801,12 +3790,12 @@ static int pager_open_journal(Pager *pPager){
assert( pPager->state>=PAGER_RESERVED );
assert( pPager->journalOpen==0 );
assert( pPager->useJournal );
- assert( pPager->aInJournal==0 );
+ assert( pPager->pInJournal==0 );
sqlite3PagerPagecount(pPager);
pagerLeave(pPager);
- pPager->aInJournal = sqlite3MallocZero( pPager->dbSize/8 + 1 );
+ pPager->pInJournal = sqlite3BitvecCreate(pPager->dbSize);
pagerEnter(pPager);
- if( pPager->aInJournal==0 ){
+ if( pPager->pInJournal==0 ){
rc = SQLITE_NOMEM;
goto failed_to_open_journal;
}
@@ -3858,8 +3847,8 @@ static int pager_open_journal(Pager *pPager){
return rc;
failed_to_open_journal:
- sqlite3_free(pPager->aInJournal);
- pPager->aInJournal = 0;
+ sqlite3BitvecDestroy(pPager->pInJournal);
+ pPager->pInJournal = 0;
return rc;
}
@@ -3897,7 +3886,7 @@ int sqlite3PagerBegin(DbPage *pPg, int exFlag){
assert( pPg->nRef>0 );
assert( pPager->state!=PAGER_UNLOCK );
if( pPager->state==PAGER_SHARED ){
- assert( pPager->aInJournal==0 );
+ assert( pPager->pInJournal==0 );
if( MEMDB ){
pPager->state = PAGER_EXCLUSIVE;
pPager->origDbSize = pPager->dbSize;
@@ -3927,12 +3916,12 @@ int sqlite3PagerBegin(DbPage *pPg, int exFlag){
*/
assert( pPager->nRec==0 );
assert( pPager->origDbSize==0 );
- assert( pPager->aInJournal==0 );
+ assert( pPager->pInJournal==0 );
sqlite3PagerPagecount(pPager);
pagerLeave(pPager);
- pPager->aInJournal = sqlite3MallocZero( pPager->dbSize/8 + 1 );
+ pPager->pInJournal = sqlite3BitvecCreate( pPager->dbSize );
pagerEnter(pPager);
- if( !pPager->aInJournal ){
+ if( !pPager->pInJournal ){
rc = SQLITE_NOMEM;
}else{
pPager->origDbSize = pPager->dbSize;
@@ -4108,11 +4097,11 @@ static int pager_write(PgHdr *pPg){
}
pPager->nRec++;
- assert( pPager->aInJournal!=0 );
- pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7);
+ assert( pPager->pInJournal!=0 );
+ sqlite3BitvecSet(pPager->pInJournal, pPg->pgno);
pPg->needSync = !pPager->noSync;
if( pPager->stmtInUse ){
- pPager->aInStmt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
+ sqlite3BitvecSet(pPager->pInStmt, pPg->pgno);
}
}
}else{
@@ -4157,8 +4146,8 @@ static int pager_write(PgHdr *pPg){
return rc;
}
pPager->stmtNRec++;
- assert( pPager->aInStmt!=0 );
- pPager->aInStmt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
+ assert( pPager->pInStmt!=0 );
+ sqlite3BitvecSet(pPager->pInStmt, pPg->pgno);
}
}
}
@@ -4227,9 +4216,7 @@ int sqlite3PagerWrite(DbPage *pDbPage){
for(ii=0; ii<nPage && rc==SQLITE_OK; ii++){
Pgno pg = pg1+ii;
PgHdr *pPage;
- if( !pPager->aInJournal || pg==pPg->pgno ||
- pg>pPager->origDbSize || !(pPager->aInJournal[pg/8]&(1<<(pg&7)))
- ) {
+ if( pg==pPg->pgno || !sqlite3BitvecTest(pPager->pInJournal, pg) ){
if( pg!=PAGER_MJ_PGNO(pPager) ){
rc = sqlite3PagerGet(pPager, pg, &pPage);
if( rc==SQLITE_OK ){
@@ -4403,13 +4390,13 @@ void sqlite3PagerDontRollback(DbPage *pPg){
*/
assert( !pPg->inJournal && (int)pPg->pgno <= pPager->origDbSize );
- assert( pPager->aInJournal!=0 );
- pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7);
+ assert( pPager->pInJournal!=0 );
+ sqlite3BitvecSet(pPager->pInJournal, pPg->pgno);
pPg->inJournal = 1;
pPg->needRead = 0;
if( pPager->stmtInUse ){
assert( pPager->stmtSize <= pPager->origDbSize );
- pPager->aInStmt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
+ sqlite3BitvecSet(pPager->pInStmt, pPg->pgno);
}
PAGERTRACE3("DONT_ROLLBACK page %d of %d\n", pPg->pgno, PAGERID(pPager));
IOTRACE(("GARBAGE %p %d\n", pPager, pPg->pgno))
@@ -4545,7 +4532,7 @@ int sqlite3PagerCommitPhaseOne(Pager *pPager, const char *zMaster, Pgno nTrunc){
Pgno i;
int iSkip = PAGER_MJ_PGNO(pPager);
for( i=nTrunc+1; i<=pPager->origDbSize; i++ ){
- if( !(pPager->aInJournal[i/8] & (1<<(i&7))) && i!=iSkip ){
+ if( !sqlite3BitvecTest(pPager->pInJournal, i) && i!=iSkip ){
rc = sqlite3PagerGet(pPager, i, &pPg);
if( rc!=SQLITE_OK ) goto sync_exit;
rc = sqlite3PagerWrite(pPg);
@@ -4803,10 +4790,10 @@ static int pagerStmtBegin(Pager *pPager){
}
assert( pPager->journalOpen );
pagerLeave(pPager);
- assert( pPager->aInStmt==0 );
- pPager->aInStmt = sqlite3MallocZero( pPager->dbSize/8 + 1 );
+ assert( pPager->pInStmt==0 );
+ pPager->pInStmt = sqlite3BitvecCreate(pPager->dbSize);
pagerEnter(pPager);
- if( pPager->aInStmt==0 ){
+ if( pPager->pInStmt==0 ){
/* sqlite3OsLock(pPager->fd, SHARED_LOCK); */
return SQLITE_NOMEM;
}
@@ -4832,9 +4819,9 @@ static int pagerStmtBegin(Pager *pPager){
return SQLITE_OK;
stmt_begin_failed:
- if( pPager->aInStmt ){
- sqlite3_free(pPager->aInStmt);
- pPager->aInStmt = 0;
+ if( pPager->pInStmt ){
+ sqlite3BitvecDestroy(pPager->pInStmt);
+ pPager->pInStmt = 0;
}
return rc;
}
@@ -4856,8 +4843,8 @@ int sqlite3PagerStmtCommit(Pager *pPager){
PAGERTRACE2("STMT-COMMIT %d\n", PAGERID(pPager));
if( !MEMDB ){
/* sqlite3OsTruncate(pPager->stfd, 0); */
- sqlite3_free( pPager->aInStmt );
- pPager->aInStmt = 0;
+ sqlite3BitvecDestroy(pPager->pInStmt);
+ pPager->pInStmt = 0;
}else{
for(pPg=pPager->pStmt; pPg; pPg=pNext){
PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager);
@@ -5027,12 +5014,7 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno){
}else{
pPg->needSync = 0;
}
- if( pPager->aInJournal && (int)pgno<=pPager->origDbSize ){
- pPg->inJournal = (pPager->aInJournal[pgno/8] & (1<<(pgno&7)))!=0;
- }else{
- pPg->inJournal = 0;
- assert( pPg->needSync==0 || (int)pgno>pPager->origDbSize );
- }
+ pPg->inJournal = sqlite3BitvecTest(pPager->pInJournal, pgno);
/* Change the page number for pPg and insert it into the new hash-chain. */
assert( pgno!=0 );
@@ -5053,11 +5035,11 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno){
/* If needSyncPgno is non-zero, then the journal file needs to be
** sync()ed before any data is written to database file page needSyncPgno.
** Currently, no such page exists in the page-cache and the
- ** Pager.aInJournal bit has been set. This needs to be remedied by loading
+ ** Pager.pInJournal bit has been set. This needs to be remedied by loading
** the page into the pager-cache and setting the PgHdr.needSync flag.
**
** If the attempt to load the page into the page-cache fails, (due
- ** to a malloc() or IO failure), clear the bit in the aInJournal[]
+ ** to a malloc() or IO failure), clear the bit in the pInJournal[]
** array. Otherwise, if the page is loaded and written again in
** this transaction, it may be written to the database file before
** it is synced into the journal file. This way, it may end up in
@@ -5071,8 +5053,8 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno){
assert( pPager->needSync );
rc = sqlite3PagerGet(pPager, needSyncPgno, &pPgHdr);
if( rc!=SQLITE_OK ){
- if( pPager->aInJournal && (int)needSyncPgno<=pPager->origDbSize ){
- pPager->aInJournal[needSyncPgno/8] &= ~(1<<(needSyncPgno&7));
+ if( pPager->pInJournal && (int)needSyncPgno<=pPager->origDbSize ){
+ sqlite3BitvecClear(pPager->pInJournal, needSyncPgno);
}
pagerLeave(pPager);
return rc;
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 60ac09e9e..e81802ef1 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -11,7 +11,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
-** @(#) $Id: sqliteInt.h,v 1.662 2008/02/14 23:26:56 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.663 2008/02/18 14:47:34 drh Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_
@@ -363,6 +363,7 @@ struct BusyHandler {
*/
typedef struct AggInfo AggInfo;
typedef struct AuthContext AuthContext;
+typedef struct Bitvec Bitvec;
typedef struct CollSeq CollSeq;
typedef struct Column Column;
typedef struct Db Db;
@@ -1759,6 +1760,12 @@ void sqlite3AddDefaultValue(Parse*,Expr*);
void sqlite3AddCollateType(Parse*, Token*);
void sqlite3EndTable(Parse*,Token*,Token*,Select*);
+Bitvec *sqlite3BitvecCreate(u32);
+int sqlite3BitvecTest(Bitvec*, u32);
+int sqlite3BitvecSet(Bitvec*, u32);
+void sqlite3BitvecClear(Bitvec*, u32);
+void sqlite3BitvecDestroy(Bitvec*);
+
void sqlite3CreateView(Parse*,Token*,Token*,Token*,Select*,int,int);
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE)
diff --git a/src/test2.c b/src/test2.c
index 9b77ff382..4d7031dbc 100644
--- a/src/test2.c
+++ b/src/test2.c
@@ -13,7 +13,7 @@
** is not included in the SQLite library. It is used for automated
** testing of the SQLite library.
**
-** $Id: test2.c,v 1.52 2007/09/03 15:19:35 drh Exp $
+** $Id: test2.c,v 1.53 2008/02/18 14:47:34 drh Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
@@ -562,6 +562,104 @@ static int fake_big_file(
#endif
/*
+** sqlite3BitvecCreate SIZE
+** sqlite3BitvecTest POINTER N
+** sqlite3BitvecSet POINTER N
+** sqlite3BitvecClear POINTER N
+** sqlite3BitvecDestroy POINTER
+*/
+static int testBitvecCreate(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int size;
+ Bitvec *p;
+ char zBuf[100];
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " N\"",
+ (void*)0);
+ }
+ if( Tcl_GetInt(interp, argv[1], &size) ) return TCL_ERROR;
+ p = sqlite3BitvecCreate(size);
+ sqlite3_snprintf(sizeof(zBuf),zBuf,"%p",p);
+ Tcl_AppendResult(interp, zBuf, 0);
+ return TCL_OK;
+}
+static int testBitvecTest(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int N;
+ Bitvec *p;
+ char zBuf[100];
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " PTR N\"",
+ (void*)0);
+ }
+ p = (Bitvec*)sqlite3TextToPtr(argv[1]);
+ if( Tcl_GetInt(interp, argv[2], &N) ) return TCL_ERROR;
+ sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",sqlite3BitvecTest(p,N));
+ Tcl_AppendResult(interp, zBuf, 0);
+ return TCL_OK;
+}
+static int testBitvecSet(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int N;
+ Bitvec *p;
+ char zBuf[100];
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " PTR N\"",
+ (void*)0);
+ }
+ p = (Bitvec*)sqlite3TextToPtr(argv[1]);
+ if( Tcl_GetInt(interp, argv[2], &N) ) return TCL_ERROR;
+ sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",sqlite3BitvecSet(p,N));
+ Tcl_AppendResult(interp, zBuf, 0);
+ return TCL_OK;
+}
+static int testBitvecClear(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ int N;
+ Bitvec *p;
+ if( argc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " PTR N\"",
+ (void*)0);
+ }
+ p = (Bitvec*)sqlite3TextToPtr(argv[1]);
+ if( Tcl_GetInt(interp, argv[2], &N) ) return TCL_ERROR;
+ sqlite3BitvecClear(p,N);
+ return TCL_OK;
+}
+static int testBitvecDestroy(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ Bitvec *p;
+ if( argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " PTR\"",
+ (void*)0);
+ }
+ p = (Bitvec*)sqlite3TextToPtr(argv[1]);
+ sqlite3BitvecDestroy(p);
+ return TCL_OK;
+}
+
+
+/*
** Register commands with the TCL interpreter.
*/
int Sqlitetest2_Init(Tcl_Interp *interp){
@@ -594,6 +692,11 @@ int Sqlitetest2_Init(Tcl_Interp *interp){
#ifndef SQLITE_OMIT_DISKIO
{ "fake_big_file", (Tcl_CmdProc*)fake_big_file },
#endif
+ { "sqlite3BitvecCreate", (Tcl_CmdProc*)testBitvecCreate },
+ { "sqlite3BitvecTest", (Tcl_CmdProc*)testBitvecTest },
+ { "sqlite3BitvecSet", (Tcl_CmdProc*)testBitvecSet },
+ { "sqlite3BitvecClear", (Tcl_CmdProc*)testBitvecClear },
+ { "sqlite3BitvecDestroy", (Tcl_CmdProc*)testBitvecDestroy },
};
int i;
for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){