aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/btree.c191
-rw-r--r--src/delete.c8
-rw-r--r--src/insert.c9
-rw-r--r--src/shell.c10
-rw-r--r--src/tclsqlite.c2
-rw-r--r--src/test_intarray.c4
-rw-r--r--src/test_intarray.h4
-rw-r--r--src/vdbeapi.c8
-rw-r--r--src/where.c21
9 files changed, 146 insertions, 111 deletions
diff --git a/src/btree.c b/src/btree.c
index 60bc7de41..56718b69d 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -1197,7 +1197,6 @@ static int defragmentPage(MemPage *pPage){
static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
const int hdr = pPage->hdrOffset; /* Local cache of pPage->hdrOffset */
u8 * const data = pPage->aData; /* Local cache of pPage->aData */
- int nFrag; /* Number of fragmented bytes on pPage */
int top; /* First byte of cell content area */
int gap; /* First byte of gap between cell pointers and cell content */
int rc; /* Integer return code */
@@ -1212,25 +1211,26 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
usableSize = pPage->pBt->usableSize;
assert( nByte < usableSize-8 );
- nFrag = data[hdr+7];
assert( pPage->cellOffset == hdr + 12 - 4*pPage->leaf );
gap = pPage->cellOffset + 2*pPage->nCell;
- top = get2byteNotZero(&data[hdr+5]);
- if( gap>top ) return SQLITE_CORRUPT_BKPT;
+ assert( gap<=65536 );
+ top = get2byte(&data[hdr+5]);
+ if( gap>top ){
+ if( top==0 ){
+ top = 65536;
+ }else{
+ return SQLITE_CORRUPT_BKPT;
+ }
+ }
+
+ /* If there is enough space between gap and top for one more cell pointer
+ ** array entry offset, and if the freelist is not empty, then search the
+ ** freelist looking for a free slot big enough to satisfy the request.
+ */
testcase( gap+2==top );
testcase( gap+1==top );
testcase( gap==top );
-
- if( nFrag>=60 ){
- /* Always defragment highly fragmented pages */
- rc = defragmentPage(pPage);
- if( rc ) return rc;
- top = get2byteNotZero(&data[hdr+5]);
- }else if( gap+2<=top ){
- /* Search the freelist looking for a free slot big enough to satisfy
- ** the request. The allocation is made from the first free slot in
- ** the list that is large enough to accommodate it.
- */
+ if( gap+2<=top && (data[hdr+1] || data[hdr+2]) ){
int pc, addr;
for(addr=hdr+1; (pc = get2byte(&data[addr]))>0; addr=pc){
int size; /* Size of the free slot */
@@ -1243,10 +1243,11 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
testcase( x==4 );
testcase( x==3 );
if( x<4 ){
+ if( data[hdr+7]>=60 ) goto defragment_page;
/* Remove the slot from the free-list. Update the number of
** fragmented bytes within the page. */
memcpy(&data[addr], &data[pc], 2);
- data[hdr+7] = (u8)(nFrag + x);
+ data[hdr+7] += (u8)x;
}else if( size+pc > usableSize ){
return SQLITE_CORRUPT_BKPT;
}else{
@@ -1260,11 +1261,13 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
}
}
- /* Check to make sure there is enough space in the gap to satisfy
- ** the allocation. If not, defragment.
+ /* The request could not be fulfilled using a freelist slot. Check
+ ** to see if defragmentation is necessary.
*/
testcase( gap+2+nByte==top );
if( gap+2+nByte>top ){
+defragment_page:
+ testcase( pPage->nCell==0 );
rc = defragmentPage(pPage);
if( rc ) return rc;
top = get2byteNotZero(&data[hdr+5]);
@@ -1287,90 +1290,100 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
/*
** Return a section of the pPage->aData to the freelist.
-** The first byte of the new free block is pPage->aDisk[start]
-** and the size of the block is "size" bytes.
-**
-** Most of the effort here is involved in coalesing adjacent
-** free blocks into a single big free block.
-*/
-static int freeSpace(MemPage *pPage, int start, int size){
- int addr, pbegin, hdr;
- int iLast; /* Largest possible freeblock offset */
- unsigned char *data = pPage->aData;
+** The first byte of the new free block is pPage->aData[iStart]
+** and the size of the block is iSize bytes.
+**
+** Adjacent freeblocks are coalesced.
+**
+** Note that even though the freeblock list was checked by btreeInitPage(),
+** that routine will not detect overlap between cells or freeblocks. Nor
+** does it detect cells or freeblocks that encrouch into the reserved bytes
+** at the end of the page. So do additional corruption checks inside this
+** routine and return SQLITE_CORRUPT if any problems are found.
+*/
+static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
+ u16 iPtr; /* Address of pointer to next freeblock */
+ u16 iFreeBlk; /* Address of the next freeblock */
+ u8 hdr; /* Page header size. 0 or 100 */
+ u8 nFrag = 0; /* Reduction in fragmentation */
+ u16 iOrigSize = iSize; /* Original value of iSize */
+ u32 iLast = pPage->pBt->usableSize-4; /* Largest possible freeblock offset */
+ u32 iEnd = iStart + iSize; /* First byte past the iStart buffer */
+ unsigned char *data = pPage->aData; /* Page content */
assert( pPage->pBt!=0 );
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
- assert( start>=pPage->hdrOffset+6+pPage->childPtrSize );
- assert( (start + size) <= (int)pPage->pBt->usableSize );
+ assert( iStart>=pPage->hdrOffset+6+pPage->childPtrSize );
+ assert( iEnd <= pPage->pBt->usableSize );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
- assert( size>=0 ); /* Minimum cell size is 4 */
+ assert( iSize>=4 ); /* Minimum cell size is 4 */
+ assert( iStart<=iLast );
+ /* Overwrite deleted information with zeros when the secure_delete
+ ** option is enabled */
if( pPage->pBt->btsFlags & BTS_SECURE_DELETE ){
- /* Overwrite deleted information with zeros when the secure_delete
- ** option is enabled */
- memset(&data[start], 0, size);
- }
-
- /* Add the space back into the linked list of freeblocks. Note that
- ** even though the freeblock list was checked by btreeInitPage(),
- ** btreeInitPage() did not detect overlapping cells or
- ** freeblocks that overlapped cells. Nor does it detect when the
- ** cell content area exceeds the value in the page header. If these
- ** situations arise, then subsequent insert operations might corrupt
- ** the freelist. So we do need to check for corruption while scanning
- ** the freelist.
+ memset(&data[iStart], 0, iSize);
+ }
+
+ /* The list of freeblocks must be in ascending order. Find the
+ ** spot on the list where iStart should be inserted.
*/
hdr = pPage->hdrOffset;
- addr = hdr + 1;
- iLast = pPage->pBt->usableSize - 4;
- assert( start<=iLast );
- while( (pbegin = get2byte(&data[addr]))<start && pbegin>0 ){
- if( pbegin<addr+4 ){
- return SQLITE_CORRUPT_BKPT;
+ iPtr = hdr + 1;
+ if( data[iPtr+1]==0 && data[iPtr]==0 ){
+ iFreeBlk = 0; /* Shortcut for the case when the freelist is empty */
+ }else{
+ while( (iFreeBlk = get2byte(&data[iPtr]))>0 && iFreeBlk<iStart ){
+ if( iFreeBlk<iPtr+4 ) return SQLITE_CORRUPT_BKPT;
+ iPtr = iFreeBlk;
}
- addr = pbegin;
- }
- if( pbegin>iLast ){
- return SQLITE_CORRUPT_BKPT;
- }
- assert( pbegin>addr || pbegin==0 );
- put2byte(&data[addr], start);
- put2byte(&data[start], pbegin);
- put2byte(&data[start+2], size);
- pPage->nFree = pPage->nFree + (u16)size;
-
- /* Coalesce adjacent free blocks */
- addr = hdr + 1;
- while( (pbegin = get2byte(&data[addr]))>0 ){
- int pnext, psize, x;
- assert( pbegin>addr );
- assert( pbegin <= (int)pPage->pBt->usableSize-4 );
- pnext = get2byte(&data[pbegin]);
- psize = get2byte(&data[pbegin+2]);
- if( pbegin + psize + 3 >= pnext && pnext>0 ){
- int frag = pnext - (pbegin+psize);
- if( (frag<0) || (frag>(int)data[hdr+7]) ){
- return SQLITE_CORRUPT_BKPT;
+ if( iFreeBlk>iLast ) return SQLITE_CORRUPT_BKPT;
+ assert( iFreeBlk>iPtr || iFreeBlk==0 );
+
+ /* At this point:
+ ** iFreeBlk: First freeblock after iStart, or zero if none
+ ** iPtr: The address of a pointer iFreeBlk
+ **
+ ** Check to see if iFreeBlk should be coalesced onto the end of iStart.
+ */
+ if( iFreeBlk && iEnd+3>=iFreeBlk ){
+ nFrag = iFreeBlk - iEnd;
+ if( iEnd>iFreeBlk ) return SQLITE_CORRUPT_BKPT;
+ iEnd = iFreeBlk + get2byte(&data[iFreeBlk+2]);
+ iSize = iEnd - iStart;
+ iFreeBlk = get2byte(&data[iFreeBlk]);
+ }
+
+ /* If iPtr is another freeblock (that is, if iPtr is not the freelist pointer
+ ** in the page header) then check to see if iStart should be coalesced
+ ** onto the end of iPtr.
+ */
+ if( iPtr>hdr+1 ){
+ int iPtrEnd = iPtr + get2byte(&data[iPtr+2]);
+ if( iPtrEnd+3>=iStart ){
+ if( iPtrEnd>iStart ) return SQLITE_CORRUPT_BKPT;
+ nFrag += iStart - iPtrEnd;
+ iSize = iEnd - iPtr;
+ iStart = iPtr;
}
- data[hdr+7] -= (u8)frag;
- x = get2byte(&data[pnext]);
- put2byte(&data[pbegin], x);
- x = pnext + get2byte(&data[pnext+2]) - pbegin;
- put2byte(&data[pbegin+2], x);
- }else{
- addr = pbegin;
}
+ if( nFrag>data[hdr+7] ) return SQLITE_CORRUPT_BKPT;
+ data[hdr+7] -= nFrag;
}
-
- /* If the cell content area begins with a freeblock, remove it. */
- if( data[hdr+1]==data[hdr+5] && data[hdr+2]==data[hdr+6] ){
- int top;
- pbegin = get2byte(&data[hdr+1]);
- memcpy(&data[hdr+1], &data[pbegin], 2);
- top = get2byte(&data[hdr+5]) + get2byte(&data[pbegin+2]);
- put2byte(&data[hdr+5], top);
+ if( iStart==get2byte(&data[hdr+5]) ){
+ /* The new freeblock is at the beginning of the cell content area,
+ ** so just extend the cell content area rather than create another
+ ** freelist entry */
+ if( iPtr!=hdr+1 ) return SQLITE_CORRUPT_BKPT;
+ put2byte(&data[hdr+1], iFreeBlk);
+ put2byte(&data[hdr+5], iEnd);
+ }else{
+ /* Insert the new freeblock into the freelist */
+ put2byte(&data[iPtr], iStart);
+ put2byte(&data[iStart], iFreeBlk);
+ put2byte(&data[iStart+2], iSize);
}
- assert( sqlite3PagerIswriteable(pPage->pDbPage) );
+ pPage->nFree += iOrigSize;
return SQLITE_OK;
}
diff --git a/src/delete.c b/src/delete.c
index fb6fa77dd..4392de383 100644
--- a/src/delete.c
+++ b/src/delete.c
@@ -473,10 +473,11 @@ void sqlite3DeleteFrom(
** triggers.
*/
if( !isView ){
+ testcase( IsVirtual(pTab) );
sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, iTabCur, aToOpen,
&iDataCur, &iIdxCur);
- assert( pPk || iDataCur==iTabCur );
- assert( pPk || iIdxCur==iDataCur+1 );
+ assert( pPk || IsVirtual(pTab) || iDataCur==iTabCur );
+ assert( pPk || IsVirtual(pTab) || iIdxCur==iDataCur+1 );
}
/* Set up a loop over the rowids/primary-keys that were found in the
@@ -484,7 +485,8 @@ void sqlite3DeleteFrom(
*/
if( okOnePass ){
/* Just one row. Hence the top-of-loop is a no-op */
- assert( nKey==nPk ); /* OP_Found will use an unpacked key */
+ assert( nKey==nPk ); /* OP_Found will use an unpacked key */
+ assert( !IsVirtual(pTab) );
if( aToOpen[iDataCur-iTabCur] ){
assert( pPk!=0 );
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey);
diff --git a/src/insert.c b/src/insert.c
index d0eced5b5..ebbc448e0 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -1624,6 +1624,9 @@ void sqlite3CompleteInsertion(
** For a WITHOUT ROWID table, *piDataCur will be somewhere in the range
** of *piIdxCurs, depending on where the PRIMARY KEY index appears on the
** pTab->pIndex list.
+**
+** If pTab is a virtual table, then this routine is a no-op and the
+** *piDataCur and *piIdxCur values are left uninitialized.
*/
int sqlite3OpenTableAndIndices(
Parse *pParse, /* Parsing context */
@@ -1642,9 +1645,9 @@ int sqlite3OpenTableAndIndices(
assert( op==OP_OpenRead || op==OP_OpenWrite );
if( IsVirtual(pTab) ){
- assert( aToOpen==0 );
- *piDataCur = 0;
- *piIdxCur = 1;
+ /* This routine is a no-op for virtual tables. Leave the output
+ ** variables *piDataCur and *piIdxCur uninitialized so that valgrind
+ ** can detect if they are used by mistake in the caller. */
return 0;
}
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
diff --git a/src/shell.c b/src/shell.c
index ac4290958..932f561bd 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -1988,7 +1988,11 @@ static FILE *output_file_open(const char *zFile){
*/
static void sql_trace_callback(void *pArg, const char *z){
FILE *f = (FILE*)pArg;
- if( f ) fprintf(f, "%s\n", z);
+ if( f ){
+ int i = (int)strlen(z);
+ while( i>0 && z[i-1]==';' ){ i--; }
+ fprintf(f, "%.*s;\n", i, z);
+ }
}
/*
@@ -2600,7 +2604,7 @@ static int do_meta_command(char *zLine, ShellState *p){
" (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x"
" FROM sqlite_master UNION ALL"
" SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) "
- "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%'"
+ "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%' "
"ORDER BY rowid",
callback, &data, &zErrMsg
);
@@ -3155,7 +3159,7 @@ static int do_meta_command(char *zLine, ShellState *p){
" (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x"
" FROM sqlite_master UNION ALL"
" SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) "
- "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%'"
+ "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%' "
"ORDER BY rowid",
callback, &data, &zErrMsg
);
diff --git a/src/tclsqlite.c b/src/tclsqlite.c
index c61726ba1..791a58b42 100644
--- a/src/tclsqlite.c
+++ b/src/tclsqlite.c
@@ -2466,7 +2466,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
if( rc==TCL_OK ){
rc = createIncrblobChannel(
- interp, pDb, zDb, zTable, zColumn, iRow, isReadonly
+ interp, pDb, zDb, zTable, zColumn, (sqlite3_int64)iRow, isReadonly
);
}
#endif
diff --git a/src/test_intarray.c b/src/test_intarray.c
index f5c3d9e40..efcd21d40 100644
--- a/src/test_intarray.c
+++ b/src/test_intarray.c
@@ -216,7 +216,7 @@ static sqlite3_module intarrayModule = {
** explicitly by the application, the virtual table will be dropped implicitly
** by the system when the database connection is closed.
*/
-int sqlite3_intarray_create(
+SQLITE_API int sqlite3_intarray_create(
sqlite3 *db,
const char *zName,
sqlite3_intarray **ppReturn
@@ -250,7 +250,7 @@ int sqlite3_intarray_create(
** any query against the corresponding virtual table. If the integer
** array does change or is deallocated undefined behavior will result.
*/
-int sqlite3_intarray_bind(
+SQLITE_API int sqlite3_intarray_bind(
sqlite3_intarray *pIntArray, /* The intarray object to bind to */
int nElements, /* Number of elements in the intarray */
sqlite3_int64 *aElements, /* Content of the intarray */
diff --git a/src/test_intarray.h b/src/test_intarray.h
index 6d26235a8..84b1f3fe6 100644
--- a/src/test_intarray.h
+++ b/src/test_intarray.h
@@ -102,7 +102,7 @@ typedef struct sqlite3_intarray sqlite3_intarray;
** explicitly by the application, the virtual table will be dropped implicitly
** by the system when the database connection is closed.
*/
-int sqlite3_intarray_create(
+SQLITE_API int sqlite3_intarray_create(
sqlite3 *db,
const char *zName,
sqlite3_intarray **ppReturn
@@ -115,7 +115,7 @@ int sqlite3_intarray_create(
** any query against the corresponding virtual table. If the integer
** array does change or is deallocated undefined behavior will result.
*/
-int sqlite3_intarray_bind(
+SQLITE_API int sqlite3_intarray_bind(
sqlite3_intarray *pIntArray, /* The intarray object to bind to */
int nElements, /* Number of elements in the intarray */
sqlite3_int64 *aElements, /* Content of the intarray */
diff --git a/src/vdbeapi.c b/src/vdbeapi.c
index 46f53afa2..0631fce87 100644
--- a/src/vdbeapi.c
+++ b/src/vdbeapi.c
@@ -513,10 +513,12 @@ int sqlite3_step(sqlite3_stmt *pStmt){
sqlite3_mutex_enter(db->mutex);
v->doingRerun = 0;
while( (rc = sqlite3Step(v))==SQLITE_SCHEMA
- && cnt++ < SQLITE_MAX_SCHEMA_RETRY
- && (rc2 = rc = sqlite3Reprepare(v))==SQLITE_OK ){
+ && cnt++ < SQLITE_MAX_SCHEMA_RETRY ){
+ int savedPc = v->pc;
+ rc2 = rc = sqlite3Reprepare(v);
+ if( rc!=SQLITE_OK) break;
sqlite3_reset(pStmt);
- v->doingRerun = 1;
+ if( savedPc>=0 ) v->doingRerun = 1;
assert( v->expired==0 );
}
if( rc2!=SQLITE_OK ){
diff --git a/src/where.c b/src/where.c
index 9c30136e8..6a4299cc9 100644
--- a/src/where.c
+++ b/src/where.c
@@ -3781,8 +3781,8 @@ static void whereLoopPrint(WhereLoop *p, WhereClause *pWC){
sqlite3DebugPrintf(" %12s",
pItem->zAlias ? pItem->zAlias : pTab->zName);
if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){
- const char *zName;
- if( p->u.btree.pIndex && (zName = p->u.btree.pIndex->zName)!=0 ){
+ const char *zName;
+ if( p->u.btree.pIndex && (zName = p->u.btree.pIndex->zName)!=0 ){
if( strncmp(zName, "sqlite_autoindex_", 17)==0 ){
int i = sqlite3Strlen30(zName) - 1;
while( zName[i]!='_' ) i--;
@@ -3803,7 +3803,11 @@ static void whereLoopPrint(WhereLoop *p, WhereClause *pWC){
sqlite3DebugPrintf(" %-19s", z);
sqlite3_free(z);
}
- sqlite3DebugPrintf(" f %05x N %d", p->wsFlags, p->nLTerm);
+ if( p->wsFlags & WHERE_SKIPSCAN ){
+ sqlite3DebugPrintf(" f %05x %d-%d", p->wsFlags, p->nLTerm,p->u.btree.nSkip);
+ }else{
+ sqlite3DebugPrintf(" f %05x N %d", p->wsFlags, p->nLTerm);
+ }
sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut);
#ifdef SQLITE_ENABLE_TREE_EXPLAIN
/* If the 0x100 bit of wheretracing is set, then show all of the constraint
@@ -4316,8 +4320,7 @@ static int whereLoopAddBtreeIndex(
** On the other hand, the extra seeks could end up being significantly
** more expensive. */
assert( 42==sqlite3LogEst(18) );
- if( pTerm==0
- && saved_nEq==saved_nSkip
+ if( saved_nEq==saved_nSkip
&& saved_nEq+1<pProbe->nKeyCol
&& pProbe->aiRowLogEst[saved_nEq+1]>=42 /* TUNING: Minimum for skip-scan */
&& (rc = whereLoopResize(db, pNew, pNew->nLTerm+1))==SQLITE_OK
@@ -4328,9 +4331,17 @@ static int whereLoopAddBtreeIndex(
pNew->aLTerm[pNew->nLTerm++] = 0;
pNew->wsFlags |= WHERE_SKIPSCAN;
nIter = pProbe->aiRowLogEst[saved_nEq] - pProbe->aiRowLogEst[saved_nEq+1];
+ if( pTerm ){
+ /* TUNING: When estimating skip-scan for a term that is also indexable,
+ ** increase the cost of the skip-scan by 2x, to make it a little less
+ ** desirable than the regular index lookup. */
+ nIter += 10; assert( 10==sqlite3LogEst(2) );
+ }
pNew->nOut -= nIter;
whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nIter + nInMul);
pNew->nOut = saved_nOut;
+ pNew->u.btree.nEq = saved_nEq;
+ pNew->u.btree.nSkip = saved_nSkip;
}
for(; rc==SQLITE_OK && pTerm!=0; pTerm = whereScanNext(&scan)){
u16 eOp = pTerm->eOperator; /* Shorthand for pTerm->eOperator */