Index: src/btree.c ================================================================== --- src/btree.c +++ src/btree.c @@ -7,11 +7,11 @@ ** 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. ** ************************************************************************* -** $Id: btree.c,v 1.442 2008/03/23 00:20:36 drh Exp $ +** $Id: btree.c,v 1.443 2008/03/25 00:22:21 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** See the header comment on "btreeInt.h" for additional information. ** Including a description of file format and an overview of operation. */ @@ -2647,27 +2647,10 @@ } sqlite3BtreeLeave(p); return rc; } -/* -** Default key comparison function to be used if no comparison function -** is specified on the sqlite3BtreeCursor() call. -*/ -static int dfltCompare( - void *NotUsed, /* User data is not used */ - int n1, const void *p1, /* First key to compare */ - int n2, const void *p2 /* Second key to compare */ -){ - int c; - c = memcmp(p1, p2, n1pBt; @@ -2748,12 +2724,11 @@ /* Now that no other errors can occur, finish filling in the BtCursor ** variables, link the cursor into the BtShared list and set *ppCur (the ** output argument to this function). */ - pCur->xCompare = xCmp ? xCmp : dfltCompare; - pCur->pArg = pArg; + pCur->pKeyInfo = pKeyInfo; pCur->pBtree = p; pCur->pBt = pBt; pCur->wrFlag = wrFlag; pCur->pNext = pBt->pCursor; if( pCur->pNext ){ @@ -2772,21 +2747,20 @@ } unlockBtreeIfUnused(pBt); return rc; } int sqlite3BtreeCursor( - Btree *p, /* The btree */ - int iTable, /* Root page of table to open */ - int wrFlag, /* 1 to write. 0 read-only */ - int (*xCmp)(void*,int,const void*,int,const void*), /* Key Comparison func */ - void *pArg, /* First arg to xCompare() */ - BtCursor **ppCur /* Write new cursor here */ + Btree *p, /* The btree */ + int iTable, /* Root page of table to open */ + int wrFlag, /* 1 to write. 0 read-only */ + struct KeyInfo *pKeyInfo, /* First arg to xCompare() */ + BtCursor **ppCur /* Write new cursor here */ ){ int rc; sqlite3BtreeEnter(p); p->pBt->db = p->db; - rc = btreeCursor(p, iTable, wrFlag, xCmp, pArg, ppCur); + rc = btreeCursor(p, iTable, wrFlag, pKeyInfo, ppCur); sqlite3BtreeLeave(p); return rc; } @@ -3576,12 +3550,11 @@ /* Move the cursor so that it points to an entry near pKey/nKey. ** Return a success code. ** ** For INTKEY tables, only the nKey parameter is used. pKey is ** ignored. For other tables, nKey is the number of bytes of data -** in pKey. The comparison function specified when the cursor was -** created is used to compare keys. +** in pKey. ** ** If an exact match is not found, then the cursor is always ** left pointing at a leaf page which would hold the entry if it ** were present. The cursor might point to an entry that comes ** before or after the key. @@ -3607,10 +3580,12 @@ i64 nKey, /* Size of pKey. Or the key for tables */ int biasRight, /* If true, bias the search to the high end */ int *pRes /* Search result flag */ ){ int rc; + VdbeParsedRecord *pPKey; + char aSpace[200]; assert( cursorHoldsMutex(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); rc = moveToRoot(pCur); if( rc ){ @@ -3621,19 +3596,27 @@ if( pCur->eState==CURSOR_INVALID ){ *pRes = -1; assert( pCur->pPage->nCell==0 ); return SQLITE_OK; } + if( pCur->pPage->intKey ){ + pPKey = 0; + }else{ + pPKey = sqlite3VdbeRecordParse(pCur->pKeyInfo, nKey, pKey, + aSpace, sizeof(aSpace)); + if( pPKey==0 ) return SQLITE_NOMEM; + } for(;;){ int lwr, upr; Pgno chldPg; MemPage *pPage = pCur->pPage; int c = -1; /* pRes return if table is empty must be -1 */ lwr = 0; upr = pPage->nCell-1; if( !pPage->intKey && pKey==0 ){ - return SQLITE_CORRUPT_BKPT; + rc = SQLITE_CORRUPT_BKPT; + goto moveto_finish; } if( biasRight ){ pCur->idx = upr; }else{ pCur->idx = (upr+lwr)/2; @@ -3660,30 +3643,29 @@ }else{ int available; pCellKey = (void *)fetchPayload(pCur, &available, 0); nCellKey = pCur->info.nKey; if( available>=nCellKey ){ - c = pCur->xCompare(pCur->pArg, nCellKey, pCellKey, nKey, pKey); + c = sqlite3VdbeRecordCompareParsed(nCellKey, pCellKey, pPKey); }else{ pCellKey = sqlite3_malloc( nCellKey ); if( pCellKey==0 ) return SQLITE_NOMEM; rc = sqlite3BtreeKey(pCur, 0, nCellKey, (void *)pCellKey); - c = pCur->xCompare(pCur->pArg, nCellKey, pCellKey, nKey, pKey); + c = sqlite3VdbeRecordCompareParsed(nCellKey, pCellKey, pPKey); sqlite3_free(pCellKey); - if( rc ){ - return rc; - } + if( rc ) goto moveto_finish; } } if( c==0 ){ if( pPage->leafData && !pPage->leaf ){ lwr = pCur->idx; upr = lwr - 1; break; }else{ if( pRes ) *pRes = 0; - return SQLITE_OK; + rc = SQLITE_OK; + goto moveto_finish; } } if( c<0 ){ lwr = pCur->idx+1; }else{ @@ -3704,20 +3686,21 @@ chldPg = get4byte(findCell(pPage, lwr)); } if( chldPg==0 ){ assert( pCur->idx>=0 && pCur->idxpPage->nCell ); if( pRes ) *pRes = c; - return SQLITE_OK; + rc = SQLITE_OK; + goto moveto_finish; } pCur->idx = lwr; pCur->info.nSize = 0; rc = moveToChild(pCur, chldPg); - if( rc ){ - return rc; - } + if( rc ) goto moveto_finish; } - /* NOT REACHED */ +moveto_finish: + sqlite3VdbeRecordUnparse(pPKey); + return rc; } /* ** Return TRUE if the cursor is not pointing at an entry of the table. Index: src/btree.h ================================================================== --- src/btree.h +++ src/btree.h @@ -11,11 +11,11 @@ ************************************************************************* ** This header file defines the interface that the sqlite B-Tree file ** subsystem. See comments in the source code for a detailed description ** of what each interface routine does. ** -** @(#) $Id: btree.h,v 1.94 2007/12/07 18:55:28 drh Exp $ +** @(#) $Id: btree.h,v 1.95 2008/03/25 00:22:21 drh Exp $ */ #ifndef _BTREE_H_ #define _BTREE_H_ /* TODO: This definition is just included so other modules compile. It @@ -126,16 +126,15 @@ int sqlite3BtreeGetMeta(Btree*, int idx, u32 *pValue); int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value); void sqlite3BtreeTripAllCursors(Btree*, int); int sqlite3BtreeCursor( - Btree*, /* BTree containing table to open */ - int iTable, /* Index of root page */ - int wrFlag, /* 1 for writing. 0 for read-only */ - int(*)(void*,int,const void*,int,const void*), /* Key comparison function */ - void*, /* First argument to compare function */ - BtCursor **ppCursor /* Returned cursor */ + Btree*, /* BTree containing table to open */ + int iTable, /* Index of root page */ + int wrFlag, /* 1 for writing. 0 for read-only */ + struct KeyInfo*, /* First argument to compare function */ + BtCursor **ppCursor /* Returned cursor */ ); int sqlite3BtreeCloseCursor(BtCursor*); int sqlite3BtreeMoveto(BtCursor*,const void *pKey,i64 nKey,int bias,int *pRes); int sqlite3BtreeDelete(BtCursor*); Index: src/btreeInt.h ================================================================== --- src/btreeInt.h +++ src/btreeInt.h @@ -7,11 +7,11 @@ ** 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. ** ************************************************************************* -** $Id: btreeInt.h,v 1.17 2008/03/04 17:45:01 mlcreech Exp $ +** $Id: btreeInt.h,v 1.18 2008/03/25 00:22:21 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to ** ** Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3: @@ -433,12 +433,11 @@ */ struct BtCursor { Btree *pBtree; /* The Btree to which this cursor belongs */ BtShared *pBt; /* The BtShared this cursor points to */ BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */ - int (*xCompare)(void*,int,const void*,int,const void*); /* Key comp func */ - void *pArg; /* First arg to xCompare() */ + struct KeyInfo *pKeyInfo; /* Argument passed to comparison function */ Pgno pgnoRoot; /* The root page of this tree */ MemPage *pPage; /* Page that contains the entry */ int idx; /* Index of the entry in pPage->aCell[] */ CellInfo info; /* A parse of the cell we are pointing at */ u8 wrFlag; /* True if writable */ Index: src/prepare.c ================================================================== --- src/prepare.c +++ src/prepare.c @@ -11,11 +11,11 @@ ************************************************************************* ** This file contains the implementation of the sqlite3_prepare() ** interface, and routines that contribute to loading the database schema ** from disk. ** -** $Id: prepare.c,v 1.80 2008/03/20 14:03:29 drh Exp $ +** $Id: prepare.c,v 1.81 2008/03/25 00:22:21 drh Exp $ */ #include "sqliteInt.h" #include /* @@ -206,11 +206,11 @@ DbSetProperty(db, 1, DB_SchemaLoaded); } return SQLITE_OK; } sqlite3BtreeEnter(pDb->pBt); - rc = sqlite3BtreeCursor(pDb->pBt, MASTER_ROOT, 0, 0, 0, &curMain); + rc = sqlite3BtreeCursor(pDb->pBt, MASTER_ROOT, 0, 0, &curMain); if( rc!=SQLITE_OK && rc!=SQLITE_EMPTY ){ sqlite3SetString(pzErrMsg, sqlite3ErrStr(rc), (char*)0); sqlite3BtreeLeave(pDb->pBt); goto error_out; } @@ -444,11 +444,11 @@ assert( sqlite3_mutex_held(db->mutex) ); for(iDb=0; allOk && iDbnDb; iDb++){ Btree *pBt; pBt = db->aDb[iDb].pBt; if( pBt==0 ) continue; - rc = sqlite3BtreeCursor(pBt, MASTER_ROOT, 0, 0, 0, &curTemp); + rc = sqlite3BtreeCursor(pBt, MASTER_ROOT, 0, 0, &curTemp); if( rc==SQLITE_OK ){ rc = sqlite3BtreeGetMeta(pBt, 1, (u32 *)&cookie); if( rc==SQLITE_OK && cookie!=db->aDb[iDb].pSchema->schema_cookie ){ allOk = 0; } Index: src/test3.c ================================================================== --- src/test3.c +++ src/test3.c @@ -11,11 +11,11 @@ ************************************************************************* ** 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.91 2008/03/04 17:45:02 mlcreech Exp $ +** $Id: test3.c,v 1.92 2008/03/25 00:22:21 drh Exp $ */ #include "sqliteInt.h" #include "btreeInt.h" #include "tcl.h" #include @@ -710,11 +710,11 @@ } pBt = sqlite3TextToPtr(argv[1]); if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR; if( Tcl_GetBoolean(interp, argv[3], &wrFlag) ) return TCL_ERROR; sqlite3BtreeEnter(pBt); - rc = sqlite3BtreeCursor(pBt, iTable, wrFlag, 0, 0, &pCur); + rc = sqlite3BtreeCursor(pBt, iTable, wrFlag, 0, &pCur); sqlite3BtreeLeave(pBt); if( rc ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; } Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -41,11 +41,11 @@ ** documentation, headers files, or other derived files. The formatting ** of the code in this file is, therefore, important. See other comments ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. ** -** $Id: vdbe.c,v 1.715 2008/03/21 17:13:13 drh Exp $ +** $Id: vdbe.c,v 1.716 2008/03/25 00:22:21 drh Exp $ */ #include "sqliteInt.h" #include #include "vdbeInt.h" @@ -2052,13 +2052,12 @@ /* Opcode: MakeRecord P1 P2 P3 P4 * ** ** Convert P2 registers beginning with P1 into a single entry ** suitable for use as a data record in a database table or as a key ** in an index. The details of the format are irrelavant as long as -** the OP_Column opcode can decode the record later and as long as the -** sqlite3VdbeRecordCompare function will correctly compare two encoded -** records. Refer to source code comments for the details of the record +** the OP_Column opcode can decode the record later. +** Refer to source code comments for the details of the record ** format. ** ** P4 may be a string that is P1 characters long. The nth character of the ** string indicates the column affinity that should be used for the nth ** field of the index key. @@ -2517,15 +2516,11 @@ } assert( i>=0 ); pCur = allocateCursor(p, i, iDb); if( pCur==0 ) goto no_mem; pCur->nullRow = 1; - /* We always provide a key comparison function. If the table being - ** opened is of type INTKEY, the comparision function will be ignored. */ - rc = sqlite3BtreeCursor(pX, p2, wrFlag, - sqlite3VdbeRecordCompare, pOp->p4.p, - &pCur->pCursor); + rc = sqlite3BtreeCursor(pX, p2, wrFlag, pOp->p4.p, &pCur->pCursor); if( pOp->p4type==P4_KEYINFO ){ pCur->pKeyInfo = pOp->p4.pKeyInfo; pCur->pIncrKey = &pCur->pKeyInfo->incrKey; pCur->pKeyInfo->enc = ENC(p->db); }else{ @@ -2623,19 +2618,19 @@ int pgno; assert( pOp->p4type==P4_KEYINFO ); rc = sqlite3BtreeCreateTable(pCx->pBt, &pgno, BTREE_ZERODATA); if( rc==SQLITE_OK ){ assert( pgno==MASTER_ROOT+1 ); - rc = sqlite3BtreeCursor(pCx->pBt, pgno, 1, sqlite3VdbeRecordCompare, - pOp->p4.z, &pCx->pCursor); + rc = sqlite3BtreeCursor(pCx->pBt, pgno, 1, + (KeyInfo*)pOp->p4.z, &pCx->pCursor); pCx->pKeyInfo = pOp->p4.pKeyInfo; pCx->pKeyInfo->enc = ENC(p->db); pCx->pIncrKey = &pCx->pKeyInfo->incrKey; } pCx->isTable = 0; }else{ - rc = sqlite3BtreeCursor(pCx->pBt, MASTER_ROOT, 1, 0, 0, &pCx->pCursor); + rc = sqlite3BtreeCursor(pCx->pBt, MASTER_ROOT, 1, 0, &pCx->pCursor); pCx->isTable = 1; pCx->pIncrKey = &pCx->bogusIncrKey; } } pCx->nField = pOp->p2; Index: src/vdbe.h ================================================================== --- src/vdbe.h +++ src/vdbe.h @@ -13,11 +13,11 @@ ** ** This header defines the interface to the virtual database engine ** or VDBE. The VDBE implements an abstract machine that runs a ** simple program to access and modify the underlying database. ** -** $Id: vdbe.h,v 1.126 2008/03/22 01:07:18 drh Exp $ +** $Id: vdbe.h,v 1.127 2008/03/25 00:22:21 drh Exp $ */ #ifndef _SQLITE_VDBE_H_ #define _SQLITE_VDBE_H_ #include @@ -32,10 +32,11 @@ ** The names of the following types declared in vdbeInt.h are required ** for the VdbeOp definition. */ typedef struct VdbeFunc VdbeFunc; typedef struct Mem Mem; +typedef struct VdbeParsedRecord VdbeParsedRecord; /* ** A single instruction of the virtual machine has an opcode ** and as many as three operands. The instruction is recorded ** as an instance of the following structure: @@ -178,14 +179,19 @@ int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, int); void sqlite3VdbeCountChanges(Vdbe*); sqlite3 *sqlite3VdbeDb(Vdbe*); void sqlite3VdbeSetSql(Vdbe*, const char *z, int n); void sqlite3VdbeSwap(Vdbe*,Vdbe*); + +VdbeParsedRecord *sqlite3VdbeRecordParse(KeyInfo*,int,const void*,void*,int); +void sqlite3VdbeRecordUnparse(VdbeParsedRecord*); +int sqlite3VdbeRecordCompareParsed(int,const void*,VdbeParsedRecord*); + #ifndef NDEBUG void sqlite3VdbeComment(Vdbe*, const char*, ...); # define VdbeComment(X) sqlite3VdbeComment X #else # define VdbeComment(X) #endif #endif Index: src/vdbeInt.h ================================================================== --- src/vdbeInt.h +++ src/vdbeInt.h @@ -360,11 +360,10 @@ int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *); int sqlite3VdbeIdxKeyCompare(Cursor*,int,const unsigned char*,int*); int sqlite3VdbeIdxRowid(BtCursor *, i64 *); int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*); -int sqlite3VdbeRecordCompare(void*,int,const void*,int, const void*); int sqlite3VdbeIdxRowidLen(const u8*); int sqlite3VdbeExec(Vdbe*); int sqlite3VdbeList(Vdbe*); int sqlite3VdbeHalt(Vdbe*); int sqlite3VdbeChangeEncoding(Mem *, int); Index: src/vdbeaux.c ================================================================== --- src/vdbeaux.c +++ src/vdbeaux.c @@ -2128,10 +2128,11 @@ ** x = GetVarint( A, B ); ** */ #define GetVarint(A,B) ((B = *(A))<=0x7f ? 1 : sqlite3GetVarint32(A, &B)) +#if 0 /* ** This function compares the two table rows or index records specified by ** {nKey1, pKey1} and {nKey2, pKey2}, returning a negative, zero ** or positive integer if {nKey1, pKey1} is less than, equal to or ** greater than {nKey2, pKey2}. Both Key1 and Key2 must be byte strings @@ -2214,10 +2215,194 @@ rc = -rc; } return rc; } +#endif + +/* +** An instance of the following structure holds information about a +** single index record that has already been parsed out into individual +** values. +** +** A record is an object that contains one or more fields of data. +** Records are used to store the content of a table row and to store +** the key of an index. A blob encoding of a record is created by +** the OP_MakeRecord opcode of the VDBE and is disassemblied by the +** OP_Column opcode. +** +** This structure holds a record that has already been disassembled +** into its constitutent fields. +*/ +struct VdbeParsedRecord { + KeyInfo *pKeyInfo; /* Collation and sort-order information */ + u16 nField; /* Number of entries in apMem[] */ + u8 needFree; /* True if memory obtained from sqlite3_malloc() */ + u8 needDestroy; /* True if apMem[]s should be destroyed on close */ + Mem *apMem[1]; /* Values */ +}; + +/* +** Given the nKey-byte encoding of a record in pKey[], parse the +** record into a VdbeParsedRecord structure. Return a pointer to +** that structure. +** +** The calling function might provide szSpace bytes of memory +** space at pSpace. This space can be used to hold the returned +** VDbeParsedRecord structure if it is large enough. If it is +** not big enough, space is obtained from sqlite3_malloc(). +** +** The returned structure should be closed by a call to +** sqlite3VdbeRecordUnparse(). +*/ +VdbeParsedRecord *sqlite3VdbeRecordParse( + KeyInfo *pKeyInfo, /* Information about the record format */ + int nKey, /* Size of the binary record */ + const void *pKey, /* The binary record */ + void *pSpace, /* Space available to hold resulting object */ + int szSpace /* Size of pSpace[] in bytes */ +){ + const unsigned char *aKey = (const unsigned char *)pKey; + VdbeParsedRecord *p; + int nByte; + int i, idx, d; + u32 szHdr; + Mem *pMem; + + nByte = sizeof(*p) + sizeof(Mem*)*pKeyInfo->nField + + sizeof(Mem)*(pKeyInfo->nField+1); + if( nByte>szSpace ){ + p = sqlite3DbMallocRaw(pKeyInfo->db, nByte); + if( p==0 ) return 0; + p->needFree = 1; + }else{ + p = pSpace; + p->needFree = 0; + } + p->pKeyInfo = pKeyInfo; + p->nField = pKeyInfo->nField + 1; + p->needDestroy = 1; + pMem = (Mem*)&p->apMem[pKeyInfo->nField+1]; + idx = GetVarint(aKey, szHdr); + d = szHdr; + i = 0; + while( idxnField ){ + u32 serial_type; + + idx += GetVarint( aKey+idx, serial_type); + if( d>=nKey && sqlite3VdbeSerialTypeLen(serial_type)>0 ) break; + pMem->enc = pKeyInfo->enc; + pMem->db = pKeyInfo->db; + pMem->flags = 0; + d += sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem); + p->apMem[i++] = pMem++; + } + p->nField = i; + return (void*)p; +} + +/* +** This routine destroys a VdbeParsedRecord object +*/ +void sqlite3VdbeRecordUnparse(VdbeParsedRecord *p){ + if( p ){ + if( p->needDestroy ){ + int i; + for(i=0; inField; i++){ + if( p->apMem[i]->flags & MEM_Dyn ){ + sqlite3VdbeMemRelease(p->apMem[i]); + } + } + } + if( p->needFree ){ + sqlite3_free(p); + } + } +} + +/* +** This function compares the two table rows or index records +** specified by {nKey1, pKey1} and pPKey2. It returns a negative, zero +** or positive integer if {nKey1, pKey1} is less than, equal to or +** greater than pPKey2. The {nKey1, pKey1} key must be a blob +** created by th OP_MakeRecord opcode of the VDBE. The pPKey2 +** key must be a parsed key such as obtained from +** sqlite3VdbeParseRecord. +** +** Key1 and Key2 do not have to contain the same number of fields. +** But if the lengths differ, Key2 must be the shorter of the two. +** +** Historical note: In earlier versions of this routine both Key1 +** and Key2 were blobs obtained from OP_MakeRecord. But we found +** that in typical use the same Key2 would be submitted multiple times +** in a row. So an optimization was added to parse the Key2 key +** separately and submit the parsed version. In this way, we avoid +** parsing the same Key2 multiple times in a row. +*/ +int sqlite3VdbeRecordCompareParsed( + int nKey1, const void *pKey1, + VdbeParsedRecord *pPKey2 +){ + u32 d1; /* Offset into aKey[] of next data element */ + u32 idx1; /* Offset into aKey[] of next header element */ + u32 szHdr1; /* Number of bytes in header */ + int i = 0; + int nField; + int rc = 0; + const unsigned char *aKey1 = (const unsigned char *)pKey1; + KeyInfo *pKeyInfo; + Mem mem1; + + pKeyInfo = pPKey2->pKeyInfo; + mem1.enc = pKeyInfo->enc; + mem1.db = pKeyInfo->db; + mem1.flags = 0; + + idx1 = GetVarint(aKey1, szHdr1); + d1 = szHdr1; + nField = pKeyInfo->nField; + while( idx1nField ){ + u32 serial_type1; + + /* Read the serial types for the next element in each key. */ + idx1 += GetVarint( aKey1+idx1, serial_type1 ); + if( d1>=nKey1 && sqlite3VdbeSerialTypeLen(serial_type1)>0 ) break; + + /* Extract the values to be compared. + */ + d1 += sqlite3VdbeSerialGet(&aKey1[d1], serial_type1, &mem1); + + /* Do the comparison + */ + rc = sqlite3MemCompare(&mem1, pPKey2->apMem[i], + iaColl[i] : 0); + if( mem1.flags&MEM_Dyn ) sqlite3VdbeMemRelease(&mem1); + if( rc!=0 ){ + break; + } + i++; + } + + /* One of the keys ran out of fields, but all the fields up to that point + ** were equal. If the incrKey flag is true, then the second key is + ** treated as larger. + */ + if( rc==0 ){ + if( pKeyInfo->incrKey ){ + rc = -1; + }else if( !pKeyInfo->prefixIsEqual ){ + if( d1aSortOrder && inField + && pKeyInfo->aSortOrder[i] ){ + rc = -rc; + } + + return rc; +} /* ** The argument is an index entry composed using the OP_MakeRecord opcode. ** The last entry in this record should be an integer (specifically ** an integer rowid). This routine returns the number of bytes in @@ -2283,10 +2468,12 @@ i64 nCellKey = 0; int rc; BtCursor *pCur = pC->pCursor; int lenRowid; Mem m; + VdbeParsedRecord *pRec; + char zSpace[200]; sqlite3BtreeKeySize(pCur, &nCellKey); if( nCellKey<=0 ){ *res = 0; return SQLITE_OK; @@ -2296,11 +2483,17 @@ rc = sqlite3VdbeMemFromBtree(pC->pCursor, 0, nCellKey, 1, &m); if( rc ){ return rc; } lenRowid = sqlite3VdbeIdxRowidLen((u8*)m.z); - *res = sqlite3VdbeRecordCompare(pC->pKeyInfo, m.n-lenRowid, m.z, nKey, pKey); + pRec = sqlite3VdbeRecordParse(pC->pKeyInfo, nKey, pKey, + zSpace, sizeof(zSpace)); + if( pRec==0 ){ + return SQLITE_NOMEM; + } + *res = sqlite3VdbeRecordCompareParsed(m.n-lenRowid, m.z, pRec); + sqlite3VdbeRecordUnparse(pRec); sqlite3VdbeMemRelease(&m); return SQLITE_OK; } /* DELETED test/btree.test Index: test/btree.test ================================================================== --- test/btree.test +++ /dev/null @@ -1,1066 +0,0 @@ -# 2001 September 15 -# -# 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 regression tests for SQLite library. The -# focus of this script is btree database backend -# -# $Id: btree.test,v 1.43 2008/02/02 20:47:38 drh Exp $ - - -set testdir [file dirname $argv0] -source $testdir/tester.tcl - -ifcapable default_autovacuum { - finish_test - return -} - -# Basic functionality. Open and close a database. -# -do_test btree-1.1 { - file delete -force test1.bt - file delete -force test1.bt-journal - set rc [catch {btree_open test1.bt 2000 0} ::b1] -} {0} - -# The second element of the list returned by btree_pager_stats is the -# number of pages currently checked out. We'll be checking this value -# frequently during this test script, to make sure the btree library -# is properly releasing the pages it checks out, and thus avoiding -# page leaks. -# -do_test btree-1.1.1 { - lindex [btree_pager_stats $::b1] 1 -} {0} -do_test btree-1.2 { - set rc [catch {btree_open test1.bt 2000 0} ::b2] -} {0} -do_test btree-1.3 { - set rc [catch {btree_close $::b2} msg] - lappend rc $msg -} {0 {}} - -# Do an insert and verify that the database file grows in size. -# -do_test btree-1.4 { - set rc [catch {btree_begin_transaction $::b1} msg] - lappend rc $msg -} {0 {}} -do_test btree-1.4.1 { - lindex [btree_pager_stats $::b1] 1 -} {1} -do_test btree-1.5 { - set rc [catch {btree_cursor $::b1 1 1} ::c1] - if {$rc} {lappend rc $::c1} - set rc -} {0} -do_test btree-1.6 { - set rc [catch {btree_insert $::c1 100 1.00} msg] - lappend rc $msg -} {0 {}} -do_test btree-1.7 { - btree_move_to $::c1 100 - btree_key $::c1 -} {100} -do_test btree-1.8 { - btree_data $::c1 -} {1.00} -do_test btree-1.9 { - set rc [catch {btree_close_cursor $::c1} msg] - lappend rc $msg -} {0 {}} -do_test btree-1.10 { - set rc [catch {btree_commit $::b1} msg] - lappend rc $msg -} {0 {}} -do_test btree-1.11 { - file size test1.bt -} {1024} -do_test btree-1.12 { - lindex [btree_pager_stats $::b1] 1 -} {0} - -# Reopen the database and attempt to read the record that we wrote. -# -do_test btree-2.1 { - set rc [catch {btree_cursor $::b1 1 1} ::c1] - if {$rc} {lappend rc $::c1} - set rc -} {0} -do_test btree-2.1.1 { - btree_cursor_list $::b1 -} {} -do_test btree-2.2 { - btree_move_to $::c1 99 -} {1} -do_test btree-2.3 { - btree_move_to $::c1 101 -} {-1} -do_test btree-2.4 { - btree_move_to $::c1 100 -} {0} -do_test btree-2.5 { - btree_key $::c1 -} {100} -do_test btree-2.6 { - btree_data $::c1 -} {1.00} -do_test btree-2.7 { - lindex [btree_pager_stats $::b1] 1 -} {1} - -# Do some additional inserts -# -do_test btree-3.1 { - btree_begin_transaction $::b1 - btree_insert $::c1 200 2.00 - btree_move_to $::c1 200 - btree_key $::c1 -} {200} -do_test btree-3.1.1 { - lindex [btree_pager_stats $::b1] 1 -} {1} -do_test btree-3.2 { - btree_insert $::c1 300 3.00 - btree_move_to $::c1 300 - btree_key $::c1 -} {300} -do_test btree-3.4 { - btree_insert $::c1 400 4.00 - btree_move_to $::c1 400 - btree_key $::c1 -} {400} -do_test btree-3.5 { - btree_insert $::c1 500 5.00 - btree_move_to $::c1 500 - btree_key $::c1 -} {500} -do_test btree-3.6 { - btree_insert $::c1 600 6.00 - btree_move_to $::c1 600 - btree_key $::c1 -} {600} -#btree_page_dump $::b1 2 -do_test btree-3.7 { - set rc [btree_move_to $::c1 0] - expr {$rc>0} -} {1} -do_test btree-3.8 { - btree_key $::c1 -} {100} -do_test btree-3.9 { - btree_data $::c1 -} {1.00} -do_test btree-3.10 { - btree_next $::c1 - btree_key $::c1 -} {200} -do_test btree-3.11 { - btree_data $::c1 -} {2.00} -do_test btree-3.12 { - btree_next $::c1 - btree_key $::c1 -} {300} -do_test btree-3.13 { - btree_data $::c1 -} {3.00} -do_test btree-3.14 { - btree_next $::c1 - btree_key $::c1 -} {400} -do_test btree-3.15 { - btree_data $::c1 -} {4.00} -do_test btree-3.16 { - btree_next $::c1 - btree_key $::c1 -} {500} -do_test btree-3.17 { - btree_data $::c1 -} {5.00} -do_test btree-3.18 { - btree_next $::c1 - btree_key $::c1 -} {600} -do_test btree-3.19 { - btree_data $::c1 -} {6.00} -do_test btree-3.20.1 { - btree_next $::c1 - btree_key $::c1 -} {0} -do_test btree-3.20.2 { - btree_eof $::c1 -} {1} -# This test case used to test that one couldn't request data from an -# invalid cursor. That is now an assert()ed condition. -# -# do_test btree-3.21 { -# set rc [catch {btree_data $::c1} res] -# lappend rc $res -# } {1 SQLITE_INTERNAL} - -# Commit the changes, reopen and reread the data -# -do_test btree-3.22 { - set rc [catch {btree_close_cursor $::c1} msg] - lappend rc $msg -} {0 {}} -do_test btree-3.22.1 { - lindex [btree_pager_stats $::b1] 1 -} {1} -do_test btree-3.23 { - set rc [catch {btree_commit $::b1} msg] - lappend rc $msg -} {0 {}} -do_test btree-3.23.1 { - lindex [btree_pager_stats $::b1] 1 -} {0} -do_test btree-3.24 { - file size test1.bt -} {1024} -do_test btree-3.25 { - set rc [catch {btree_cursor $::b1 1 1} ::c1] - if {$rc} {lappend rc $::c1} - set rc -} {0} -do_test btree-3.25.1 { - lindex [btree_pager_stats $::b1] 1 -} {1} -do_test btree-3.26 { - set rc [btree_move_to $::c1 0] - expr {$rc>0} -} {1} -do_test btree-3.27 { - btree_key $::c1 -} {100} -do_test btree-3.28 { - btree_data $::c1 -} {1.00} -do_test btree-3.29 { - btree_next $::c1 - btree_key $::c1 -} {200} -do_test btree-3.30 { - btree_data $::c1 -} {2.00} -do_test btree-3.31 { - btree_next $::c1 - btree_key $::c1 -} {300} -do_test btree-3.32 { - btree_data $::c1 -} {3.00} -do_test btree-3.33 { - btree_next $::c1 - btree_key $::c1 -} {400} -do_test btree-3.34 { - btree_data $::c1 -} {4.00} -do_test btree-3.35 { - btree_next $::c1 - btree_key $::c1 -} {500} -do_test btree-3.36 { - btree_data $::c1 -} {5.00} -do_test btree-3.37 { - btree_next $::c1 - btree_key $::c1 -} {600} -do_test btree-3.38 { - btree_data $::c1 -} {6.00} -do_test btree-3.39 { - btree_next $::c1 - btree_key $::c1 -} {0} -# This test case used to test that requesting data from an invalid cursor -# returned SQLITE_INTERNAL. That is now an assert()ed condition. -# -# do_test btree-3.40 { -# set rc [catch {btree_data $::c1} res] -# lappend rc $res -# } {1 SQLITE_INTERNAL} -do_test btree-3.41 { - lindex [btree_pager_stats $::b1] 1 -} {1} - - -# Now try a delete -# -do_test btree-4.1 { - btree_begin_transaction $::b1 - btree_move_to $::c1 100 - btree_key $::c1 -} {100} -do_test btree-4.1.1 { - lindex [btree_pager_stats $::b1] 1 -} {1} -do_test btree-4.2 { - btree_delete $::c1 -} {} -do_test btree-4.3 { - btree_move_to $::c1 100 - btree_key $::c1 -} {200} -do_test btree-4.4 { - btree_next $::c1 - btree_key $::c1 -} {300} -do_test btree-4.5 { - btree_next $::c1 - btree_key $::c1 -} {400} -do_test btree-4.4 { - btree_move_to $::c1 0 - set r {} - while 1 { - set key [btree_key $::c1] - if {[btree_eof $::c1]} break - lappend r $key - lappend r [btree_data $::c1] - btree_next $::c1 - } - set r -} {200 2.00 300 3.00 400 4.00 500 5.00 600 6.00} - -# Commit and make sure the delete is still there. -# -do_test btree-4.5 { - btree_commit $::b1 - btree_move_to $::c1 0 - set r {} - while 1 { - set key [btree_key $::c1] - if {[btree_eof $::c1]} break - lappend r $key - lappend r [btree_data $::c1] - btree_next $::c1 - } - set r -} {200 2.00 300 3.00 400 4.00 500 5.00 600 6.00} - -# Completely close the database and reopen it. Then check -# the data again. -# -do_test btree-4.6 { - lindex [btree_pager_stats $::b1] 1 -} {1} -do_test btree-4.7 { - btree_close_cursor $::c1 - lindex [btree_pager_stats $::b1] 1 -} {0} -do_test btree-4.8 { - btree_close $::b1 - set ::b1 [btree_open test1.bt 2000 0] - set ::c1 [btree_cursor $::b1 1 1] - lindex [btree_pager_stats $::b1] 1 -} {1} -do_test btree-4.9 { - set r {} - btree_first $::c1 - while 1 { - set key [btree_key $::c1] - if {[btree_eof $::c1]} break - lappend r $key - lappend r [btree_data $::c1] - btree_next $::c1 - } - set r -} {200 2.00 300 3.00 400 4.00 500 5.00 600 6.00} - -# Try to read and write meta data -# -do_test btree-5.1 { - btree_get_meta $::b1 -} {0 0 0 0 0 0 0 0 0 0} -do_test btree-5.2 { - set rc [catch { - btree_update_meta $::b1 0 1 2 3 4 5 6 7 8 9 - } msg] - lappend rc $msg -} {1 SQLITE_ERROR} -do_test btree-5.3 { - btree_begin_transaction $::b1 - set rc [catch { - btree_update_meta $::b1 0 1 2 3 0 5 6 0 8 9 - } msg] - lappend rc $msg -} {0 {}} -do_test btree-5.4 { - btree_get_meta $::b1 -} {0 1 2 3 0 5 6 0 8 9} -do_test btree-5.5 { - btree_close_cursor $::c1 - btree_rollback $::b1 - btree_get_meta $::b1 -} {0 0 0 0 0 0 0 0 0 0} -do_test btree-5.6 { - btree_begin_transaction $::b1 - btree_update_meta $::b1 0 10 20 30 0 50 60 0 80 90 - btree_commit $::b1 - btree_get_meta $::b1 -} {0 10 20 30 0 50 60 0 80 90} - -proc select_all {cursor} { - set r {} - btree_first $cursor - while {![btree_eof $cursor]} { - set key [btree_key $cursor] - lappend r $key - lappend r [btree_data $cursor] - btree_next $cursor - } - return $r -} -proc select_keys {cursor} { - set r {} - btree_first $cursor - while {![btree_eof $cursor]} { - set key [btree_key $cursor] - lappend r $key - btree_next $cursor - } - return $r -} - -# Try to create a new table in the database file -# -do_test btree-6.1 { - set rc [catch {btree_create_table $::b1 0} msg] - lappend rc $msg -} {1 SQLITE_ERROR} -do_test btree-6.2 { - btree_begin_transaction $::b1 - set ::t2 [btree_create_table $::b1 0] -} {2} -do_test btree-6.2.1 { - lindex [btree_pager_stats $::b1] 1 -} {1} -do_test btree-6.2.2 { - set ::c2 [btree_cursor $::b1 $::t2 1] - lindex [btree_pager_stats $::b1] 1 -} {2} -do_test btree-6.2.3 { - btree_insert $::c2 ten 10 - btree_move_to $::c2 ten - btree_key $::c2 -} {ten} -do_test btree-6.3 { - btree_commit $::b1 - set ::c1 [btree_cursor $::b1 1 1] - lindex [btree_pager_stats $::b1] 1 -} {2} -do_test btree-6.3.1 { - select_all $::c1 -} {200 2.00 300 3.00 400 4.00 500 5.00 600 6.00} -#btree_page_dump $::b1 3 -do_test btree-6.4 { - select_all $::c2 -} {ten 10} - -# Drop the new table, then create it again anew. -# -do_test btree-6.5 { - btree_begin_transaction $::b1 -} {} -do_test btree-6.6 { - btree_close_cursor $::c2 -} {} -do_test btree-6.6.1 { - lindex [btree_pager_stats $::b1] 1 -} {1} -do_test btree-6.7 { - btree_close_cursor $::c1 - btree_drop_table $::b1 $::t2 -} {} -do_test btree-6.7.1 { - lindex [btree_get_meta $::b1] 0 -} {1} -do_test btree-6.8 { - set ::t2 [btree_create_table $::b1 0] -} {2} -do_test btree-6.8.1 { - lindex [btree_get_meta $::b1] 0 -} {0} -do_test btree-6.9 { - set ::c2 [btree_cursor $::b1 $::t2 1] - lindex [btree_pager_stats $::b1] 1 -} {2} - -# This test case used to test that requesting the key from an invalid cursor -# returned an empty string. But that is now an assert()ed condition. -# -# do_test btree-6.9.1 { -# btree_move_to $::c2 {} -# btree_key $::c2 -# } {} - -# If we drop table 1 it just clears the table. Table 1 always exists. -# -do_test btree-6.10 { - btree_close_cursor $::c2 - btree_drop_table $::b1 1 - set ::c2 [btree_cursor $::b1 $::t2 1] - set ::c1 [btree_cursor $::b1 1 1] - btree_first $::c1 - btree_eof $::c1 -} {1} -do_test btree-6.11 { - btree_commit $::b1 - select_all $::c1 -} {} -do_test btree-6.12 { - select_all $::c2 -} {} -do_test btree-6.13 { - btree_close_cursor $::c2 - lindex [btree_pager_stats $::b1] 1 -} {1} - -# Check to see that pages defragment properly. To do this test we will -# -# 1. Fill the first page of table 1 with data. -# 2. Delete every other entry of table 1. -# 3. Insert a single entry that requires more contiguous -# space than is available. -# -do_test btree-7.1 { - btree_begin_transaction $::b1 -} {} -catch {unset key} -catch {unset data} - -# Check to see that data on overflow pages work correctly. -# -do_test btree-8.1 { - set data "*** This is a very long key " - while {[string length $data]<1234} {append data $data} - set ::data $data - btree_insert $::c1 2020 $data -} {} -btree_page_dump $::b1 1 -btree_page_dump $::b1 2 -do_test btree-8.1.1 { - lindex [btree_pager_stats $::b1] 1 -} {1} -btree_pager_ref_dump $::b1 -do_test btree-8.2 { - btree_move_to $::c1 2020 - string length [btree_data $::c1] -} [string length $::data] -do_test btree-8.3 { - btree_data $::c1 -} $::data -do_test btree-8.4 { - btree_delete $::c1 -} {} -do_test btree-8.4.1 { - lindex [btree_get_meta $::b1] 0 -} [expr {int(([string length $::data]-238+1019)/1020)}] -do_test btree-8.4.2 { - btree_integrity_check $::b1 1 2 -} {} -do_test btree-8.5 { - set data "*** This is an even longer key " - while {[string length $data]<2000} {append data $data} - append data END - set ::data $data - btree_insert $::c1 2030 $data -} {} -do_test btree-8.6 { - btree_move_to $::c1 2030 - string length [btree_data $::c1] -} [string length $::data] -do_test btree-8.7 { - btree_data $::c1 -} $::data -do_test btree-8.8 { - btree_commit $::b1 - btree_data $::c1 -} $::data -do_test btree-8.9.1 { - btree_close_cursor $::c1 - btree_close $::b1 - set ::b1 [btree_open test1.bt 2000 0] - set ::c1 [btree_cursor $::b1 1 1] - btree_move_to $::c1 2030 - btree_data $::c1 -} $::data -do_test btree-8.9.2 { - btree_integrity_check $::b1 1 2 -} {} -do_test btree-8.10 { - btree_begin_transaction $::b1 - btree_delete $::c1 -} {} -do_test btree-8.11 { - lindex [btree_get_meta $::b1] 0 -} {4} - -# Now check out keys on overflow pages. -# -do_test btree-8.12.1 { - set ::keyprefix "This is a long prefix to a key " - while {[string length $::keyprefix]<256} {append ::keyprefix $::keyprefix} - btree_close_cursor $::c1 - btree_clear_table $::b1 2 - lindex [btree_get_meta $::b1] 0 -} {4} -do_test btree-8.12.2 { - btree_integrity_check $::b1 1 2 -} {} -do_test btree-8.12.3 { - set ::c1 [btree_cursor $::b1 2 1] - btree_insert $::c1 ${::keyprefix}1 1 - btree_first $::c1 - btree_data $::c1 -} {1} -do_test btree-8.13 { - btree_key $::c1 -} ${keyprefix}1 -do_test btree-8.14 { - btree_insert $::c1 ${::keyprefix}2 2 - btree_insert $::c1 ${::keyprefix}3 3 - btree_last $::c1 - btree_key $::c1 -} ${keyprefix}3 -do_test btree-8.15 { - btree_move_to $::c1 ${::keyprefix}2 - btree_data $::c1 -} {2} -do_test btree-8.16 { - btree_move_to $::c1 ${::keyprefix}1 - btree_data $::c1 -} {1} -do_test btree-8.17 { - btree_move_to $::c1 ${::keyprefix}3 - btree_data $::c1 -} {3} -do_test btree-8.18 { - lindex [btree_get_meta $::b1] 0 -} {1} -do_test btree-8.19 { - btree_move_to $::c1 ${::keyprefix}2 - btree_key $::c1 -} ${::keyprefix}2 -#btree_page_dump $::b1 2 -do_test btree-8.20 { - btree_delete $::c1 - btree_next $::c1 - btree_key $::c1 -} ${::keyprefix}3 -#btree_page_dump $::b1 2 -do_test btree-8.21 { - lindex [btree_get_meta $::b1] 0 -} {2} -do_test btree-8.22 { - lindex [btree_pager_stats $::b1] 1 -} {2} -do_test btree-8.23.1 { - btree_close_cursor $::c1 - btree_drop_table $::b1 2 - btree_integrity_check $::b1 1 -} {} -do_test btree-8.23.2 { - btree_create_table $::b1 0 -} {2} -do_test btree-8.23.3 { - set ::c1 [btree_cursor $::b1 2 1] - lindex [btree_get_meta $::b1] 0 -} {4} -do_test btree-8.24 { - lindex [btree_pager_stats $::b1] 1 -} {2} -#btree_pager_ref_dump $::b1 -do_test btree-8.25 { - btree_integrity_check $::b1 1 2 -} {} - -# Check page splitting logic -# -do_test btree-9.1 { - for {set i 1} {$i<=19} {incr i} { - set key [format %03d $i] - set data "*** $key *** $key *** $key *** $key ***" - btree_insert $::c1 $key $data - } -} {} -do_test btree-9.2 { - btree_insert $::c1 020 {*** 020 *** 020 *** 020 *** 020 ***} - select_keys $::c1 -} {001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020} - -# The previous "select_keys" command left the cursor pointing at the root -# page. So there should only be two pages checked out. 2 (the root) and -# page 1. -do_test btree-9.2.1 { - lindex [btree_pager_stats $::b1] 1 -} {2} -for {set i 1} {$i<=20} {incr i} { - do_test btree-9.3.$i.1 [subst { - btree_move_to $::c1 [format %03d $i] - btree_key $::c1 - }] [format %03d $i] - do_test btree-9.3.$i.2 [subst { - btree_move_to $::c1 [format %03d $i] - string range \[btree_data $::c1\] 0 10 - }] "*** [format %03d $i] ***" -} -do_test btree-9.4.1 { - lindex [btree_pager_stats $::b1] 1 -} {2} - -# Check the page joining logic. -# -#btree_page_dump $::b1 2 -#btree_pager_ref_dump $::b1 -do_test btree-9.4.2 { - btree_move_to $::c1 005 - btree_delete $::c1 -} {} -#btree_page_dump $::b1 2 -for {set i 1} {$i<=19} {incr i} { - if {$i==5} continue - do_test btree-9.5.$i.1 [subst { - btree_move_to $::c1 [format %03d $i] - btree_key $::c1 - }] [format %03d $i] - do_test btree-9.5.$i.2 [subst { - btree_move_to $::c1 [format %03d $i] - string range \[btree_data $::c1\] 0 10 - }] "*** [format %03d $i] ***" -} -#btree_pager_ref_dump $::b1 -do_test btree-9.6 { - btree_close_cursor $::c1 - lindex [btree_pager_stats $::b1] 1 -} {1} -do_test btree-9.7 { - btree_integrity_check $::b1 1 2 -} {} -do_test btree-9.8 { - btree_rollback $::b1 - lindex [btree_pager_stats $::b1] 1 -} {0} -do_test btree-9.9 { - btree_integrity_check $::b1 1 2 -} {} -do_test btree-9.10 { - btree_close $::b1 - set ::b1 [btree_open test1.bt 2000 0] - btree_integrity_check $::b1 1 2 -} {} - -# Create a tree of depth two. That is, there is a single divider entry -# on the root pages and two leaf pages. Then delete the divider entry -# see what happens. -# -do_test btree-10.1 { - btree_begin_transaction $::b1 - btree_clear_table $::b1 2 - lindex [btree_pager_stats $::b1] 1 -} {1} -do_test btree-10.2 { - set ::c1 [btree_cursor $::b1 2 1] - lindex [btree_pager_stats $::b1] 1 -} {2} -do_test btree-10.3 { -btree_breakpoint - for {set i 1} {$i<=30} {incr i} { - set key [format %03d $i] - set data "*** $key *** $key *** $key *** $key ***" - btree_insert $::c1 $key $data - } - select_keys $::c1 -} {001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030} -#btree_tree_dump $::b1 2 -do_test btree-10.4 { - # The divider entry is 012. This is found by uncommenting the - # btree_tree_dump call above and looking at the tree. If the page size - # changes, this test will no longer work. - btree_move_to $::c1 012 - btree_delete $::c1 - select_keys $::c1 -} {001 002 003 004 005 006 007 008 009 010 011 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030} -#btree_pager_ref_dump $::b1 -#btree_tree_dump $::b1 2 -for {set i 1} {$i<=30} {incr i} { - # Check the number of unreference pages. This should be 3 in most cases, - # but 2 when the cursor is pointing to the divider entry which is now 013. - do_test btree-10.5.$i { - btree_move_to $::c1 [format %03d $i] - lindex [btree_pager_stats $::b1] 1 - } [expr {$i==13?2:3}] - #btree_pager_ref_dump $::b1 - #btree_tree_dump $::b1 2 -} - -# Create a tree with lots more pages -# -catch {unset ::data} -catch {unset ::key} -for {set i 31} {$i<=2000} {incr i} { - do_test btree-11.1.$i.1 { - set key [format %03d $i] - set ::data "*** $key *** $key *** $key *** $key ***" - btree_insert $::c1 $key $data - btree_move_to $::c1 $key - btree_key $::c1 - } [format %03d $i] - do_test btree-11.1.$i.2 { - btree_data $::c1 - } $::data - set ::key [format %03d [expr {$i/2}]] - if {$::key=="012"} {set ::key 013} - do_test btree-11.1.$i.3 { - btree_move_to $::c1 $::key - btree_key $::c1 - } $::key -} -catch {unset ::data} -catch {unset ::key} - -# Make sure our reference count is still correct. -# -do_test btree-11.2 { - btree_close_cursor $::c1 - lindex [btree_pager_stats $::b1] 1 -} {1} -do_test btree-11.3 { - set ::c1 [btree_cursor $::b1 2 1] - lindex [btree_pager_stats $::b1] 1 -} {2} - -# Delete the dividers on the root page -# -#btree_page_dump $::b1 2 -do_test btree-11.4 { - btree_move_to $::c1 1667 - btree_delete $::c1 - btree_move_to $::c1 1667 - set k [btree_key $::c1] - if {$k==1666} { - set k [btree_next $::c1] - } - btree_key $::c1 -} {1668} -#btree_page_dump $::b1 2 - -# Change the data on an intermediate node such that the node becomes overfull -# and has to split. We happen to know that intermediate nodes exist on -# 337, 401 and 465 by the btree_page_dumps above -# -catch {unset ::data} -set ::data {This is going to be a very long data segment} -append ::data $::data -append ::data $::data -do_test btree-12.1 { - btree_insert $::c1 337 $::data - btree_move_to $::c1 337 - btree_data $::c1 -} $::data -do_test btree-12.2 { - btree_insert $::c1 401 $::data - btree_move_to $::c1 401 - btree_data $::c1 -} $::data -do_test btree-12.3 { - btree_insert $::c1 465 $::data - btree_move_to $::c1 465 - btree_data $::c1 -} $::data -do_test btree-12.4 { - btree_move_to $::c1 337 - btree_key $::c1 -} {337} -do_test btree-12.5 { - btree_data $::c1 -} $::data -do_test btree-12.6 { - btree_next $::c1 - btree_key $::c1 -} {338} -do_test btree-12.7 { - btree_move_to $::c1 464 - btree_key $::c1 -} {464} -do_test btree-12.8 { - btree_next $::c1 - btree_data $::c1 -} $::data -do_test btree-12.9 { - btree_next $::c1 - btree_key $::c1 -} {466} -do_test btree-12.10 { - btree_move_to $::c1 400 - btree_key $::c1 -} {400} -do_test btree-12.11 { - btree_next $::c1 - btree_data $::c1 -} $::data -do_test btree-12.12 { - btree_next $::c1 - btree_key $::c1 -} {402} -# btree_commit $::b1 -# btree_tree_dump $::b1 1 -do_test btree-13.1 { - btree_integrity_check $::b1 1 2 -} {} - -# To Do: -# -# 1. Do some deletes from the 3-layer tree -# 2. Commit and reopen the database -# 3. Read every 15th entry and make sure it works -# 4. Implement btree_sanity and put it throughout this script -# - -do_test btree-15.98 { - btree_close_cursor $::c1 - lindex [btree_pager_stats $::b1] 1 -} {1} -do_test btree-15.99 { - btree_rollback $::b1 - lindex [btree_pager_stats $::b1] 1 -} {0} -btree_pager_ref_dump $::b1 - -# Miscellaneous tests. -# -# btree-16.1 - Check that a statement cannot be started if a transaction -# is not active. -# btree-16.2 - Check that it is an error to request more payload from a -# btree entry than the entry contains. -do_test btree-16.1 { - catch {btree_begin_statement $::b1} msg - set msg -} SQLITE_ERROR - -do_test btree-16.2 { - btree_begin_transaction $::b1 - set ::c1 [btree_cursor $::b1 2 1] - btree_insert $::c1 1 helloworld - btree_close_cursor $::c1 - btree_commit $::b1 -} {} -do_test btree-16.3 { - set ::c1 [btree_cursor $::b1 2 1] - btree_first $::c1 -} 0 -do_test btree-16.4 { - catch {btree_data $::c1 [expr [btree_payload_size $::c1] + 10]} msg - set msg -} SQLITE_ERROR - -if {$tcl_platform(platform)=="unix"} { - do_test btree-16.5 { - btree_close $::b1 - set ::origperm [file attributes test1.bt -permissions] - file attributes test1.bt -permissions o-w,g-w,a-w - set ::b1 [btree_open test1.bt 2000 0] - catch {btree_cursor $::b1 2 1} msg - file attributes test1.bt -permissions $::origperm - btree_close $::b1 - set ::b1 [btree_open test1.bt 2000 0] - set msg - } {SQLITE_READONLY} -} - -do_test btree-16.6 { - set ::c1 [btree_cursor $::b1 2 1] - set ::c2 [btree_cursor $::b1 2 1] - btree_begin_transaction $::b1 - for {set i 0} {$i<100} {incr i} { - btree_insert $::c1 $i [string repeat helloworld 10] - } - btree_last $::c2 - btree_insert $::c1 100 [string repeat helloworld 10] -} {} - -do_test btree-16.7 { - btree_close_cursor $::c1 - btree_close_cursor $::c2 - btree_commit $::b1 - set ::c1 [btree_cursor $::b1 2 1] - catch {btree_insert $::c1 101 helloworld} msg - set msg -} {SQLITE_ERROR} -do_test btree-16.8 { - btree_first $::c1 - catch {btree_delete $::c1} msg - set msg -} {SQLITE_ERROR} -do_test btree-16.9 { - btree_close_cursor $::c1 - btree_begin_transaction $::b1 - set ::c1 [btree_cursor $::b1 2 0] - catch {btree_insert $::c1 101 helloworld} msg - set msg -} {SQLITE_PERM} -do_test btree-16.10 { - catch {btree_delete $::c1} msg - set msg -} {SQLITE_PERM} - -# As of 2006-08-16 (version 3.3.7+) a read cursor will no -# longer block a write cursor from the same database -# connectiin. The following three tests uses to return -# the SQLITE_LOCK error, but no more. -# -do_test btree-16.11 { - btree_close_cursor $::c1 - set ::c2 [btree_cursor $::b1 2 1] - set ::c1 [btree_cursor $::b1 2 0] - catch {btree_insert $::c2 101 helloworld} msg - set msg -} {} -do_test btree-16.12 { - btree_first $::c2 - catch {btree_delete $::c2} msg - set msg -} {} -do_test btree-16.13 { - catch {btree_clear_table $::b1 2} msg - set msg -} {} - - -do_test btree-16.14 { - btree_close_cursor $::c1 - btree_close_cursor $::c2 - btree_commit $::b1 - catch {btree_clear_table $::b1 2} msg - set msg -} {SQLITE_ERROR} -do_test btree-16.15 { - catch {btree_drop_table $::b1 2} msg - set msg -} {SQLITE_ERROR} -do_test btree-16.16 { - btree_begin_transaction $::b1 - set ::c1 [btree_cursor $::b1 2 0] - catch {btree_drop_table $::b1 2} msg - set msg -} {SQLITE_LOCKED} - -do_test btree-99.1 { - btree_close $::b1 -} {} -catch {unset data} -catch {unset key} - -finish_test DELETED test/btree2.test Index: test/btree2.test ================================================================== --- test/btree2.test +++ /dev/null @@ -1,502 +0,0 @@ -# 2001 September 15 -# -# 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 regression tests for SQLite library. The -# focus of this script is btree database backend -# -# $Id: btree2.test,v 1.15 2006/03/19 13:00:25 drh Exp $ - - -set testdir [file dirname $argv0] -source $testdir/tester.tcl - -if {[info commands btree_open]!=""} { - -# Create a new database file containing no entries. The database should -# contain 5 tables: -# -# 2 The descriptor table -# 3 The foreground table -# 4 The background table -# 5 The long key table -# 6 The long data table -# -# An explanation for what all these tables are used for is provided below. -# -do_test btree2-1.1 { - expr srand(1) - file delete -force test2.bt - file delete -force test2.bt-journal - set ::b [btree_open test2.bt 2000 0] - btree_begin_transaction $::b - btree_create_table $::b 0 -} {2} -do_test btree2-1.2 { - btree_create_table $::b 0 -} {3} -do_test btree2-1.3 { - btree_create_table $::b 0 -} {4} -do_test btree2-1.4 { - btree_create_table $::b 0 -} {5} -do_test btree2-1.5 { - btree_create_table $::b 0 -} {6} -do_test btree2-1.6 { - set ::c2 [btree_cursor $::b 2 1] - btree_insert $::c2 {one} {1} - btree_move_to $::c2 {one} - btree_delete $::c2 - btree_close_cursor $::c2 - btree_commit $::b - btree_integrity_check $::b 1 2 3 4 5 6 -} {} - -# This test module works by making lots of pseudo-random changes to a -# database while simultaneously maintaining an invariant on that database. -# Periodically, the script does a sanity check on the database and verifies -# that the invariant is satisfied. -# -# The invariant is as follows: -# -# 1. The descriptor table always contains 2 enters. An entry keyed by -# "N" is the number of elements in the foreground and background tables -# combined. The entry keyed by "L" is the number of digits in the keys -# for foreground and background tables. -# -# 2. The union of the foreground an background tables consists of N entries -# where each entry has an L-digit key. (Actually, some keys can be longer -# than L characters, but they always start with L digits.) The keys -# cover all integers between 1 and N. Whenever an entry is added to -# the foreground it is removed form the background and vice versa. -# -# 3. Some entries in the foreground and background tables have keys that -# begin with an L-digit number but are followed by additional characters. -# For each such entry there is a corresponding entry in the long key -# table. The long key table entry has a key which is just the L-digit -# number and data which is the length of the key in the foreground and -# background tables. -# -# 4. The data for both foreground and background entries is usually a -# short string. But some entries have long data strings. For each -# such entries there is an entry in the long data type. The key to -# long data table is an L-digit number. (The extension on long keys -# is omitted.) The data is the number of charaters in the data of the -# foreground or background entry. -# -# The following function builds a database that satisfies all of the above -# invariants. -# -proc build_db {N L} { - for {set i 2} {$i<=6} {incr i} { - catch {btree_close_cursor [set ::c$i]} - btree_clear_table $::b $i - set ::c$i [btree_cursor $::b $i 1] - } - btree_insert $::c2 N $N - btree_insert $::c2 L $L - set format %0${L}d - for {set i 1} {$i<=$N} {incr i} { - set key [format $format $i] - set data $key - btree_insert $::c3 $key $data - } -} - -# Given a base key number and a length, construct the full text of the key -# or data. -# -proc make_payload {keynum L len} { - set key [format %0${L}d $keynum] - set r $key - set i 1 - while {[string length $r]<$len} { - append r " ($i) $key" - incr i - } - return [string range $r 0 [expr {$len-1}]] -} - -# Verify the invariants on the database. Return an empty string on -# success or an error message if something is amiss. -# -proc check_invariants {} { - set ck [btree_integrity_check $::b 1 2 3 4 5 6] - if {$ck!=""} { - puts "\n*** SANITY:\n$ck" - exit - return $ck - } - btree_move_to $::c3 {} - btree_move_to $::c4 {} - btree_move_to $::c2 N - set N [btree_data $::c2] - btree_move_to $::c2 L - set L [btree_data $::c2] - set LM1 [expr {$L-1}] - for {set i 1} {$i<=$N} {incr i} { - set key {} - if {![btree_eof $::c3]} { - set key [btree_key $::c3] - } - if {[scan $key %d k]<1} {set k 0} - if {$k!=$i} { - set key {} - if {![btree_eof $::c4]} { - set key [btree_key $::c4] - } - if {[scan $key %d k]<1} {set k 0} - if {$k!=$i} { - return "Key $i is missing from both foreground and background" - } - set data [btree_data $::c4] - btree_next $::c4 - } else { - set data [btree_data $::c3] - btree_next $::c3 - } - set skey [string range $key 0 $LM1] - if {[btree_move_to $::c5 $skey]==0} { - set keylen [btree_data $::c5] - } else { - set keylen $L - } - if {[string length $key]!=$keylen} { - return "Key $i is the wrong size.\ - Is \"$key\" but should be \"[make_payload $k $L $keylen]\"" - } - if {[make_payload $k $L $keylen]!=$key} { - return "Key $i has an invalid extension" - } - if {[btree_move_to $::c6 $skey]==0} { - set datalen [btree_data $::c6] - } else { - set datalen $L - } - if {[string length $data]!=$datalen} { - return "Data for $i is the wrong size.\ - Is [string length $data] but should be $datalen" - } - if {[make_payload $k $L $datalen]!=$data} { - return "Entry $i has an incorrect data" - } - } -} - -# Look at all elements in both the foreground and background tables. -# Make sure the key is always the same as the prefix of the data. -# -# This routine was used for hunting bugs. It is not a part of standard -# tests. -# -proc check_data {n key} { - global c3 c4 - incr n -1 - foreach c [list $c3 $c4] { - btree_first $c ;# move_to $c $key - set cnt 0 - while {![btree_eof $c]} { - set key [btree_key $c] - set data [btree_data $c] - if {[string range $key 0 $n] ne [string range $data 0 $n]} { - puts "key=[list $key] data=[list $data] n=$n" - puts "cursor info = [btree_cursor_info $c]" - btree_page_dump $::b [lindex [btree_cursor_info $c] 0] - exit - } - btree_next $c - } - } -} - -# Make random changes to the database such that each change preserves -# the invariants. The number of changes is $n*N where N is the parameter -# from the descriptor table. Each changes begins with a random key. -# the entry with that key is put in the foreground table with probability -# $I and it is put in background with probability (1.0-$I). It gets -# a long key with probability $K and long data with probability $D. -# -set chngcnt 0 -proc random_changes {n I K D} { - global chngcnt - btree_move_to $::c2 N - set N [btree_data $::c2] - btree_move_to $::c2 L - set L [btree_data $::c2] - set LM1 [expr {$L-1}] - set total [expr {int($N*$n)}] - set format %0${L}d - for {set i 0} {$i<$total} {incr i} { - set k [expr {int(rand()*$N)+1}] - set insert [expr {rand()<=$I}] - set longkey [expr {rand()<=$K}] - set longdata [expr {rand()<=$D}] - if {$longkey} { - set x [expr {rand()}] - set keylen [expr {int($x*$x*$x*$x*3000)+10}] - } else { - set keylen $L - } - set key [make_payload $k $L $keylen] - if {$longdata} { - set x [expr {rand()}] - set datalen [expr {int($x*$x*$x*$x*3000)+10}] - } else { - set datalen $L - } - set data [make_payload $k $L $datalen] - set basekey [format $format $k] - if {[set c [btree_move_to $::c3 $basekey]]==0} { - btree_delete $::c3 - } else { - if {$c<0} {btree_next $::c3} - if {![btree_eof $::c3]} { - if {[string match $basekey* [btree_key $::c3]]} { - btree_delete $::c3 - } - } - } - if {[set c [btree_move_to $::c4 $basekey]]==0} { - btree_delete $::c4 - } else { - if {$c<0} {btree_next $::c4} - if {![btree_eof $::c4]} { - if {[string match $basekey* [btree_key $::c4]]} { - btree_delete $::c4 - } - } - } - set kx -1 - if {![btree_eof $::c4]} { - if {[scan [btree_key $::c4] %d kx]<1} {set kx -1} - } - if {$kx==$k} { - btree_delete $::c4 - } - # For debugging - change the "0" to "1" to integrity check after - # every change. - if 0 { - incr chngcnt - puts check----$chngcnt - set ck [btree_integrity_check $::b 1 2 3 4 5 6] - if {$ck!=""} { - puts "\nSANITY CHECK FAILED!\n$ck" - exit - } - } - if {$insert} { - btree_insert $::c3 $key $data - } else { - btree_insert $::c4 $key $data - } - if {$longkey} { - btree_insert $::c5 $basekey $keylen - } elseif {[btree_move_to $::c5 $basekey]==0} { - btree_delete $::c5 - } - if {$longdata} { - btree_insert $::c6 $basekey $datalen - } elseif {[btree_move_to $::c6 $basekey]==0} { - btree_delete $::c6 - } - # For debugging - change the "0" to "1" to integrity check after - # every change. - if 0 { - incr chngcnt - puts check----$chngcnt - set ck [btree_integrity_check $::b 1 2 3 4 5 6] - if {$ck!=""} { - puts "\nSANITY CHECK FAILED!\n$ck" - exit - } - } - } -} -set btree_trace 0 - -# Repeat this test sequence on database of various sizes -# -set testno 2 -foreach {N L} { - 10 2 - 50 2 - 200 3 - 2000 5 -} { - puts "**** N=$N L=$L ****" - set hash [md5file test2.bt] - do_test btree2-$testno.1 [subst -nocommands { - set ::c2 [btree_cursor $::b 2 1] - set ::c3 [btree_cursor $::b 3 1] - set ::c4 [btree_cursor $::b 4 1] - set ::c5 [btree_cursor $::b 5 1] - set ::c6 [btree_cursor $::b 6 1] - btree_begin_transaction $::b - build_db $N $L - check_invariants - }] {} - do_test btree2-$testno.2 { - btree_close_cursor $::c2 - btree_close_cursor $::c3 - btree_close_cursor $::c4 - btree_close_cursor $::c5 - btree_close_cursor $::c6 - btree_rollback $::b - md5file test2.bt - } $hash - do_test btree2-$testno.3 [subst -nocommands { - btree_begin_transaction $::b - set ::c2 [btree_cursor $::b 2 1] - set ::c3 [btree_cursor $::b 3 1] - set ::c4 [btree_cursor $::b 4 1] - set ::c5 [btree_cursor $::b 5 1] - set ::c6 [btree_cursor $::b 6 1] - build_db $N $L - check_invariants - }] {} - do_test btree2-$testno.4 { - btree_commit $::b - check_invariants - } {} - do_test btree2-$testno.5 { - lindex [btree_pager_stats $::b] 1 - } {6} - do_test btree2-$testno.6 { - btree_cursor_info $::c2 - btree_cursor_info $::c3 - btree_cursor_info $::c4 - btree_cursor_info $::c5 - btree_cursor_info $::c6 - btree_close_cursor $::c2 - btree_close_cursor $::c3 - btree_close_cursor $::c4 - btree_close_cursor $::c5 - btree_close_cursor $::c6 - lindex [btree_pager_stats $::b] 1 - } {0} - do_test btree2-$testno.7 { - btree_close $::b - } {} - - # For each database size, run various changes tests. - # - set num2 1 - foreach {n I K D} { - 0.5 0.5 0.1 0.1 - 1.0 0.2 0.1 0.1 - 1.0 0.8 0.1 0.1 - 2.0 0.0 0.1 0.1 - 2.0 1.0 0.1 0.1 - 2.0 0.0 0.0 0.0 - 2.0 1.0 0.0 0.0 - } { - set testid btree2-$testno.8.$num2 - set hash [md5file test2.bt] - do_test $testid.0 { - set ::b [btree_open test2.bt 2000 0] - set ::c2 [btree_cursor $::b 2 1] - set ::c3 [btree_cursor $::b 3 1] - set ::c4 [btree_cursor $::b 4 1] - set ::c5 [btree_cursor $::b 5 1] - set ::c6 [btree_cursor $::b 6 1] - check_invariants - } {} - set cnt 6 - for {set i 2} {$i<=6} {incr i} { - if {[lindex [btree_cursor_info [set ::c$i]] 0]!=$i} {incr cnt} - } - do_test $testid.1 { - btree_begin_transaction $::b - lindex [btree_pager_stats $::b] 1 - } $cnt - do_test $testid.2 [subst { - random_changes $n $I $K $D - }] {} - do_test $testid.3 { - check_invariants - } {} - do_test $testid.4 { - btree_close_cursor $::c2 - btree_close_cursor $::c3 - btree_close_cursor $::c4 - btree_close_cursor $::c5 - btree_close_cursor $::c6 - btree_rollback $::b - md5file test2.bt - } $hash - btree_begin_transaction $::b - set ::c2 [btree_cursor $::b 2 1] - set ::c3 [btree_cursor $::b 3 1] - set ::c4 [btree_cursor $::b 4 1] - set ::c5 [btree_cursor $::b 5 1] - set ::c6 [btree_cursor $::b 6 1] - do_test $testid.5 [subst { - random_changes $n $I $K $D - }] {} - do_test $testid.6 { - check_invariants - } {} - do_test $testid.7 { - btree_commit $::b - check_invariants - } {} - set hash [md5file test2.bt] - do_test $testid.8 { - btree_close_cursor $::c2 - btree_close_cursor $::c3 - btree_close_cursor $::c4 - btree_close_cursor $::c5 - btree_close_cursor $::c6 - lindex [btree_pager_stats $::b] 1 - } {0} - do_test $testid.9 { - btree_close $::b - set ::b [btree_open test2.bt 2000 0] - set ::c2 [btree_cursor $::b 2 1] - set ::c3 [btree_cursor $::b 3 1] - set ::c4 [btree_cursor $::b 4 1] - set ::c5 [btree_cursor $::b 5 1] - set ::c6 [btree_cursor $::b 6 1] - check_invariants - } {} - do_test $testid.10 { - btree_close_cursor $::c2 - btree_close_cursor $::c3 - btree_close_cursor $::c4 - btree_close_cursor $::c5 - btree_close_cursor $::c6 - lindex [btree_pager_stats $::b] 1 - } {0} - do_test $testid.11 { - btree_close $::b - } {} - incr num2 - } - incr testno - set ::b [btree_open test2.bt 2000 0] -} - -# Testing is complete. Shut everything down. -# -do_test btree-999.1 { - lindex [btree_pager_stats $::b] 1 -} {0} -do_test btree-999.2 { - btree_close $::b -} {} -do_test btree-999.3 { - file delete -force test2.bt - file exists test2.bt-journal -} {0} - -} ;# end if( not mem: and has pager_open command ); - -finish_test DELETED test/btree4.test Index: test/btree4.test ================================================================== --- test/btree4.test +++ /dev/null @@ -1,101 +0,0 @@ -# 2002 December 03 -# -# 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 regression tests for SQLite library. The -# focus of this script is btree database backend -# -# This file focuses on testing the sqliteBtreeNext() and -# sqliteBtreePrevious() procedures and making sure they are able -# to step through an entire table from either direction. -# -# $Id: btree4.test,v 1.2 2004/05/09 20:40:12 drh Exp $ - - -set testdir [file dirname $argv0] -source $testdir/tester.tcl - -if {[info commands btree_open]!=""} { - -# Open a test database. -# -file delete -force test1.bt -file delete -force test1.bt-journal -set b1 [btree_open test1.bt 2000 0] -btree_begin_transaction $b1 -do_test btree4-0.1 { - btree_create_table $b1 0 -} 2 - -set data {abcdefghijklmnopqrstuvwxyz0123456789} -append data $data -append data $data -append data $data -append data $data - -foreach N {10 100 1000} { - btree_clear_table $::b1 2 - set ::c1 [btree_cursor $::b1 2 1] - do_test btree4-$N.1 { - for {set i 1} {$i<=$N} {incr i} { - btree_insert $::c1 [format k-%05d $i] $::data-$i - } - btree_first $::c1 - btree_key $::c1 - } {k-00001} - do_test btree4-$N.2 { - btree_data $::c1 - } $::data-1 - for {set i 2} {$i<=$N} {incr i} { - do_test btree-$N.3.$i.1 { - btree_next $::c1 - } 0 - do_test btree-$N.3.$i.2 { - btree_key $::c1 - } [format k-%05d $i] - do_test btree-$N.3.$i.3 { - btree_data $::c1 - } $::data-$i - } - do_test btree4-$N.4 { - btree_next $::c1 - } 1 - do_test btree4-$N.5 { - btree_last $::c1 - } 0 - do_test btree4-$N.6 { - btree_key $::c1 - } [format k-%05d $N] - do_test btree4-$N.7 { - btree_data $::c1 - } $::data-$N - for {set i [expr {$N-1}]} {$i>=1} {incr i -1} { - do_test btree4-$N.8.$i.1 { - btree_prev $::c1 - } 0 - do_test btree4-$N.8.$i.2 { - btree_key $::c1 - } [format k-%05d $i] - do_test btree4-$N.8.$i.3 { - btree_data $::c1 - } $::data-$i - } - do_test btree4-$N.9 { - btree_prev $::c1 - } 1 - btree_close_cursor $::c1 -} - -btree_rollback $::b1 -btree_pager_ref_dump $::b1 -btree_close $::b1 - -} ;# end if( not mem: and has pager_open command ); - -finish_test DELETED test/btree5.test Index: test/btree5.test ================================================================== --- test/btree5.test +++ /dev/null @@ -1,292 +0,0 @@ -# 2004 May 10 -# -# 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 regression tests for SQLite library. The -# focus of this script is btree database backend -# -# $Id: btree5.test,v 1.5 2004/05/14 12:17:46 drh Exp $ - - -set testdir [file dirname $argv0] -source $testdir/tester.tcl - -# Attempting to read table 1 of an empty file gives an SQLITE_EMPTY -# error. -# -do_test btree5-1.1 { - file delete -force test1.bt - file delete -force test1.bt-journal - set rc [catch {btree_open test1.bt 2000 0} ::b1] -} {0} -do_test btree5-1.2 { - set rc [catch {btree_cursor $::b1 1 0} ::c1] -} {1} -do_test btree5-1.3 { - set ::c1 -} {SQLITE_EMPTY} -do_test btree5-1.4 { - set rc [catch {btree_cursor $::b1 1 1} ::c1] -} {1} -do_test btree5-1.5 { - set ::c1 -} {SQLITE_EMPTY} - -# Starting a transaction initializes the first page of the database -# and the error goes away. -# -do_test btree5-1.6 { - btree_begin_transaction $b1 - set rc [catch {btree_cursor $b1 1 0} c1] -} {0} -do_test btree5-1.7 { - btree_first $c1 -} {1} -do_test btree5-1.8 { - btree_close_cursor $c1 - btree_rollback $b1 - set rc [catch {btree_cursor $b1 1 0} c1] -} {1} -do_test btree5-1.9 { - set c1 -} {SQLITE_EMPTY} -do_test btree5-1.10 { - btree_begin_transaction $b1 - set rc [catch {btree_cursor $b1 1 0} c1] -} {0} -do_test btree5-1.11 { - btree_first $c1 -} {1} -do_test btree5-1.12 { - btree_close_cursor $c1 - btree_commit $b1 - set rc [catch {btree_cursor $b1 1 0} c1] -} {0} -do_test btree5-1.13 { - btree_first $c1 -} {1} -do_test btree5-1.14 { - btree_close_cursor $c1 - btree_integrity_check $b1 1 -} {} - -# Insert many entries into table 1. This is designed to test the -# virtual-root logic that comes into play for page one. It is also -# a good test of INTKEY tables. -# -# Stagger the inserts. After the inserts complete, go back and do -# deletes. Stagger the deletes too. Repeat this several times. -# - -# Do N inserts into table 1 using random keys between 0 and 1000000 -# -proc random_inserts {N} { - global c1 - while {$N>0} { - set k [expr {int(rand()*1000000)}] - if {[btree_move_to $c1 $k]==0} continue; # entry already exists - btree_insert $c1 $k data-for-$k - incr N -1 - } -} - -# Do N delete from table 1 -# -proc random_deletes {N} { - global c1 - while {$N>0} { - set k [expr {int(rand()*1000000)}] - btree_move_to $c1 $k - btree_delete $c1 - incr N -1 - } -} - -# Make sure the table has exactly N entries. Make sure the data for -# each entry agrees with its key. -# -proc check_table {N} { - global c1 - btree_first $c1 - set cnt 0 - while {![btree_eof $c1]} { - if {[set data [btree_data $c1]] ne "data-for-[btree_key $c1]"} { - return "wrong data for entry $cnt" - } - set n [string length $data] - set fdata1 [btree_fetch_data $c1 $n] - set fdata2 [btree_fetch_data $c1 -1] - if {$fdata1 ne "" && $fdata1 ne $data} { - return "DataFetch returned the wrong value with amt=$n" - } - if {$fdata1 ne $fdata2} { - return "DataFetch returned the wrong value when amt=-1" - } - if {$n>10} { - set fdata3 [btree_fetch_data $c1 10] - if {$fdata3 ne [string range $data 0 9]} { - return "DataFetch returned the wrong value when amt=10" - } - } - incr cnt - btree_next $c1 - } - if {$cnt!=$N} { - return "wrong number of entries" - } - return {} -} - -# Initialize the database -# -btree_begin_transaction $b1 -set c1 [btree_cursor $b1 1 1] -set btree_trace 0 - -# Do the tests. -# -set cnt 0 -for {set i 1} {$i<=100} {incr i} { - do_test btree5-2.$i.1 { - random_inserts 200 - incr cnt 200 - check_table $cnt - } {} - do_test btree5-2.$i.2 { - btree_integrity_check $b1 1 - } {} - do_test btree5-2.$i.3 { - random_deletes 190 - incr cnt -190 - check_table $cnt - } {} - do_test btree5-2.$i.4 { - btree_integrity_check $b1 1 - } {} -} - -#btree_tree_dump $b1 1 -btree_close_cursor $c1 -btree_commit $b1 -btree_begin_transaction $b1 - -# This procedure converts an integer into a variable-length text key. -# The conversion is reversible. -# -# The first two characters of the string are alphabetics derived from -# the least significant bits of the number. Because they are derived -# from least significant bits, the sort order of the resulting string -# is different from numeric order. After the alphabetic prefix comes -# the original number. A variable-length suffix follows. The length -# of the suffix is based on a hash of the original number. -# -proc num_to_key {n} { - global charset ncharset suffix - set c1 [string index $charset [expr {$n%$ncharset}]] - set c2 [string index $charset [expr {($n/$ncharset)%$ncharset}]] - set nsuf [expr {($n*211)%593}] - return $c1$c2-$n-[string range $suffix 0 $nsuf] -} -set charset {abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ} -set ncharset [string length $charset] -set suffix $charset$charset -while {[string length $suffix]<1000} {append suffix $suffix} - -# This procedures extracts the original integer used to create -# a key by num_to_key -# -proc key_to_num {key} { - regexp {^..-([0-9]+)} $key all n - return $n -} - -# Insert into table $tab keys corresponding to all values between -# $start and $end, inclusive. -# -proc insert_range {tab start end} { - for {set i $start} {$i<=$end} {incr i} { - btree_insert $tab [num_to_key $i] {} - } -} - -# Delete from table $tab keys corresponding to all values between -# $start and $end, inclusive. -# -proc delete_range {tab start end} { - for {set i $start} {$i<=$end} {incr i} { - if {[btree_move_to $tab [num_to_key $i]]==0} { - btree_delete $tab - } - } -} - -# Make sure table $tab contains exactly those keys corresponding -# to values between $start and $end -# -proc check_range {tab start end} { - btree_first $tab - while {![btree_eof $tab]} { - set key [btree_key $tab] - set i [key_to_num $key] - if {[num_to_key $i] ne $key} { - return "malformed key: $key" - } - set got($i) 1 - btree_next $tab - } - set all [lsort -integer [array names got]] - if {[llength $all]!=$end+1-$start} { - return "table contains wrong number of values" - } - if {[lindex $all 0]!=$start} { - return "wrong starting value" - } - if {[lindex $all end]!=$end} { - return "wrong ending value" - } - return {} -} - -# Create a zero-data table and test it out. -# -do_test btree5-3.1 { - set rc [catch {btree_create_table $b1 2} t2] -} {0} -do_test btree5-3.2 { - set rc [catch {btree_cursor $b1 $t2 1} c2] -} {0} -set start 1 -set end 100 -for {set i 1} {$i<=100} {incr i} { - do_test btree5-3.3.$i.1 { - insert_range $c2 $start $end - btree_integrity_check $b1 1 $t2 - } {} - do_test btree5-3.3.$i.2 { - check_range $c2 $start $end - } {} - set nstart $start - incr nstart 89 - do_test btree5-3.3.$i.3 { - delete_range $c2 $start $nstart - btree_integrity_check $b1 1 $t2 - } {} - incr start 90 - do_test btree5-3.3.$i.4 { - check_range $c2 $start $end - } {} - incr end 100 -} - - -btree_close_cursor $c2 -btree_commit $b1 -btree_close $b1 - -finish_test DELETED test/btree6.test Index: test/btree6.test ================================================================== --- test/btree6.test +++ /dev/null @@ -1,128 +0,0 @@ -# 2004 May 10 -# -# 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 regression tests for SQLite library. The -# focus of this script is btree database backend - specifically -# the B+tree tables. B+trees store all data on the leaves rather -# that storing data with keys on interior nodes. -# -# $Id: btree6.test,v 1.4 2004/05/20 22:16:31 drh Exp $ - - -set testdir [file dirname $argv0] -source $testdir/tester.tcl - - -# Insert many entries into the table that cursor $cur points to. -# The table should be an INTKEY table. -# -# Stagger the inserts. After the inserts complete, go back and do -# deletes. Stagger the deletes too. Repeat this several times. -# - -# Do N inserts into table $tab using random keys between 0 and 1000000 -# -proc random_inserts {cur N} { - global inscnt - while {$N>0} { - set k [expr {int(rand()*1000000)}] - if {[btree_move_to $cur $k]==0} { - continue; # entry already exists - } - incr inscnt - btree_insert $cur $k data-for-$k - incr N -1 - } -} -set inscnt 0 - -# Do N delete from the table that $cur points to. -# -proc random_deletes {cur N} { - while {$N>0} { - set k [expr {int(rand()*1000000)}] - btree_move_to $cur $k - btree_delete $cur - incr N -1 - } -} - -# Make sure the table that $cur points to has exactly N entries. -# Make sure the data for each entry agrees with its key. -# -proc check_table {cur N} { - btree_first $cur - set cnt 0 - while {![btree_eof $cur]} { - if {[set data [btree_data $cur]] ne "data-for-[btree_key $cur]"} { - return "wrong data for entry $cnt" - } - set n [string length $data] - set fdata1 [btree_fetch_data $cur $n] - set fdata2 [btree_fetch_data $cur -1] - if {$fdata1 ne "" && $fdata1 ne $data} { - return "DataFetch returned the wrong value with amt=$n" - } - if {$fdata1 ne $fdata2} { - return "DataFetch returned the wrong value when amt=-1" - } - if {$n>10} { - set fdata3 [btree_fetch_data $cur 10] - if {$fdata3 ne [string range $data 0 9]} { - return "DataFetch returned the wrong value when amt=10" - } - } - incr cnt - btree_next $cur - } - if {$cnt!=$N} { - return "wrong number of entries. Got $cnt. Looking for $N" - } - return {} -} - -# Initialize the database -# -file delete -force test1.bt -file delete -force test1.bt-journal -set b1 [btree_open test1.bt 2000 0] -btree_begin_transaction $b1 -set tab [btree_create_table $b1 5] -set cur [btree_cursor $b1 $tab 1] -set btree_trace 0 -expr srand(1) - -# Do the tests. -# -set cnt 0 -for {set i 1} {$i<=40} {incr i} { - do_test btree6-1.$i.1 { - random_inserts $cur 200 - incr cnt 200 - check_table $cur $cnt - } {} - do_test btree6-1.$i.2 { - btree_integrity_check $b1 1 $tab - } {} - do_test btree6-1.$i.3 { - random_deletes $cur 90 - incr cnt -90 - check_table $cur $cnt - } {} - do_test btree6-1.$i.4 { - btree_integrity_check $b1 1 $tab - } {} -} - -btree_close_cursor $cur -btree_commit $b1 -btree_close $b1 - -finish_test DELETED test/btree7.test Index: test/btree7.test ================================================================== --- test/btree7.test +++ /dev/null @@ -1,50 +0,0 @@ -# 2004 Jun 4 -# -# 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 regression tests for SQLite library. The -# focus of this script is btree database backend. -# -# $Id: btree7.test,v 1.2 2004/11/04 14:47:13 drh Exp $ - - -set testdir [file dirname $argv0] -source $testdir/tester.tcl - -# Stress the balance routine by trying to create situations where -# 3 neighboring nodes split into 5. -# -set bigdata _123456789 ;# 10 -append bigdata $bigdata ;# 20 -append bigdata $bigdata ;# 40 -append bigdata $bigdata ;# 80 -append bigdata $bigdata ;# 160 -append bigdata $bigdata ;# 320 -append bigdata $bigdata ;# 640 -set data450 [string range $bigdata 0 449] -do_test btree7-1.1 { - execsql " - CREATE TABLE t1(x INTEGER PRIMARY KEY, y TEXT); - INSERT INTO t1 VALUES(1, '$bigdata'); - INSERT INTO t1 VALUES(2, '$bigdata'); - INSERT INTO t1 VALUES(3, '$data450'); - INSERT INTO t1 VALUES(5, '$data450'); - INSERT INTO t1 VALUES(8, '$bigdata'); - INSERT INTO t1 VALUES(9, '$bigdata'); - " -} {} -integrity_check btree7-1.2 -do_test btree7-1.3 { - execsql " - INSERT INTO t1 VALUES(4, '$bigdata'); - " -} {} -integrity_check btree7-1.4 - -finish_test DELETED test/btree8.test Index: test/btree8.test ================================================================== --- test/btree8.test +++ /dev/null @@ -1,43 +0,0 @@ -# 2005 August 2 -# -# 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 regression tests for SQLite library. The -# focus of this script is btree database backend. -# -# $Id: btree8.test,v 1.6 2005/08/02 17:13:12 drh Exp $ - -set testdir [file dirname $argv0] -source $testdir/tester.tcl - -# Ticket #1346: If the table rooted on page 1 contains a single entry -# and that single entries has to flow out into another page because -# page 1 is 100-bytes smaller than most other pages, then you delete that -# one entry, everything should still work. -# -do_test btree8-1.1 { - execsql { -CREATE TABLE t1(x - ---------------------------------------------------------------------------- - ---------------------------------------------------------------------------- - ---------------------------------------------------------------------------- - ---------------------------------------------------------------------------- - ---------------------------------------------------------------------------- - ---------------------------------------------------------------------------- - ---------------------------------------------------------------------------- - ---------------------------------------------------------------------------- - ---------------------------------------------------------------------------- - ---------------------------------------------------------------------------- - ---------------------------------------------------------------------------- - ---------------------------------------------------------------------------- -); -DROP table t1; - } -} {} -integrity_check btree8-1.2 DELETED test/btree9.test Index: test/btree9.test ================================================================== --- test/btree9.test +++ /dev/null @@ -1,49 +0,0 @@ -# 2007 May 01 -# -# 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 regression tests for SQLite library. The -# focus of this script is btree database backend. -# -# $Id: btree9.test,v 1.1 2007/05/02 01:34:32 drh Exp $ - -set testdir [file dirname $argv0] -source $testdir/tester.tcl - -# The sqlite3BtreeInsert() API now has an additional "nZero" parameter -# which specifies the number of zero bytes to append to the end of the -# data. This feature allows large zero-filled BLOBs to be created without -# having to allocate a big chunk of memory to instantiate the blob. -# -# The following code tests the new feature. -# - -# Create the database -# -do_test btree9-1.1 { - file delete -force test1.bt - file delete -force test1.bt-journal - set b1 [btree_open test1.bt 2000 0] - btree_begin_transaction $b1 - set t1 [btree_create_table $b1 5] - set c1 [btree_cursor $b1 $t1 1] - btree_insert $c1 1 data-for-1 20000 - btree_move_to $c1 1 - btree_key $c1 -} {1} -do_test btree9-1.2 { - btree_payload_size $c1 -} {20010} - - -btree_close_cursor $c1 -btree_commit $b1 -btree_close $b1 - -finish_test Index: test/quick.test ================================================================== --- test/quick.test +++ test/quick.test @@ -4,11 +4,11 @@ # May you share freely, never taking more than you give. # #*********************************************************************** # This file runs all tests. # -# $Id: quick.test,v 1.72 2008/03/22 01:08:01 drh Exp $ +# $Id: quick.test,v 1.73 2008/03/25 00:22:22 drh Exp $ proc lshift {lvar} { upvar $lvar l set ret [lindex $l 0] set l [lrange $l 1 end] @@ -37,15 +37,10 @@ set EXCLUDE { all.test async.test async2.test - btree2.test - btree3.test - btree4.test - btree5.test - btree6.test corrupt.test crash.test crash2.test crash3.test crash4.test