Experiment with a different fts5 leaf page format that allows faster seeks.
FossilOrigin-Name: a1f4c3b543eed84e808f6b901a38179786fffe16
This commit is contained in:
parent
28b9e0fc05
commit
e386a1ba25
@ -303,7 +303,8 @@ typedef struct Fts5StructureSegment Fts5StructureSegment;
|
||||
|
||||
struct Fts5Data {
|
||||
u8 *p; /* Pointer to buffer containing record */
|
||||
int n; /* Size of record in bytes */
|
||||
int nn; /* Size of record in bytes */
|
||||
int szLeaf; /* Size of leaf without page-index */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -377,7 +378,8 @@ struct Fts5Structure {
|
||||
*/
|
||||
struct Fts5PageWriter {
|
||||
int pgno; /* Page number for this page */
|
||||
Fts5Buffer buf; /* Buffer containing page data */
|
||||
Fts5Buffer buf; /* Buffer containing leaf data */
|
||||
Fts5Buffer pgidx; /* Buffer containing page-index */
|
||||
Fts5Buffer term; /* Buffer containing previous term on page */
|
||||
};
|
||||
struct Fts5DlidxWriter {
|
||||
@ -392,6 +394,7 @@ struct Fts5SegWriter {
|
||||
i64 iPrevRowid; /* Previous rowid written to current leaf */
|
||||
u8 bFirstRowidInDoclist; /* True if next rowid is first in doclist */
|
||||
u8 bFirstRowidInPage; /* True if next rowid is first in page */
|
||||
/* TODO1: Can use (writer.pgidx.n==0) instead of bFirstTermInPage */
|
||||
u8 bFirstTermInPage; /* True if next term will be first in leaf */
|
||||
int nLeafWritten; /* Number of leaf pages written */
|
||||
int nEmpty; /* Number of contiguous term-less nodes */
|
||||
@ -500,10 +503,27 @@ struct Fts5SegIter {
|
||||
int bDel; /* True if the delete flag is set */
|
||||
};
|
||||
|
||||
/*
|
||||
** Argument is a pointer to an Fts5Data structure that contains a
|
||||
** leaf page.
|
||||
*/
|
||||
#define ASSERT_SZLEAF_OK(x) assert( \
|
||||
(x)->szLeaf==fts5GetU16(&(x)->p[2]) || (x)->szLeaf==(x)->nn \
|
||||
)
|
||||
|
||||
#define FTS5_SEGITER_ONETERM 0x01
|
||||
#define FTS5_SEGITER_REVERSE 0x02
|
||||
|
||||
|
||||
/*
|
||||
** Argument is a pointer to an Fts5Data structure that contains a leaf
|
||||
** page. This macro evaluates to true if the leaf contains no terms, or
|
||||
** false if it contains at least one term.
|
||||
*/
|
||||
#define fts5LeafIsTermless(x) ((x)->szLeaf >= (x)->nn)
|
||||
|
||||
#define fts5LeafFirstTermOff(x) (fts5GetU16(&x->p[(x)->szLeaf]))
|
||||
|
||||
/*
|
||||
** poslist:
|
||||
** Used by sqlite3Fts5IterPoslist() when the poslist needs to be buffered.
|
||||
@ -679,7 +699,7 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){
|
||||
int nAlloc = sizeof(Fts5Data) + nByte + FTS5_DATA_PADDING;
|
||||
pRet = (Fts5Data*)sqlite3_malloc(nAlloc);
|
||||
if( pRet ){
|
||||
pRet->n = nByte;
|
||||
pRet->nn = nByte;
|
||||
aOut = pRet->p = (u8*)&pRet[1];
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
@ -691,6 +711,9 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3_free(pRet);
|
||||
pRet = 0;
|
||||
}else{
|
||||
/* TODO1: Fix this */
|
||||
pRet->szLeaf = fts5GetU16(&pRet->p[2]);
|
||||
}
|
||||
}
|
||||
p->rc = rc;
|
||||
@ -974,8 +997,9 @@ static Fts5Structure *fts5StructureRead(Fts5Index *p){
|
||||
|
||||
pData = fts5DataRead(p, FTS5_STRUCTURE_ROWID);
|
||||
if( p->rc ) return 0;
|
||||
memset(&pData->p[pData->n], 0, FTS5_DATA_PADDING);
|
||||
p->rc = fts5StructureDecode(pData->p, pData->n, &iCookie, &pRet);
|
||||
/* TODO: Do we need this if the leaf-index is appended? Probably... */
|
||||
memset(&pData->p[pData->nn], 0, FTS5_DATA_PADDING);
|
||||
p->rc = fts5StructureDecode(pData->p, pData->nn, &iCookie, &pRet);
|
||||
if( p->rc==SQLITE_OK && pConfig->iCookie!=iCookie ){
|
||||
p->rc = sqlite3Fts5ConfigLoad(pConfig, iCookie);
|
||||
}
|
||||
@ -1178,11 +1202,11 @@ static int fts5DlidxLvlNext(Fts5DlidxLvl *pLvl){
|
||||
pLvl->iFirstOff = pLvl->iOff;
|
||||
}else{
|
||||
int iOff;
|
||||
for(iOff=pLvl->iOff; iOff<pData->n; iOff++){
|
||||
for(iOff=pLvl->iOff; iOff<pData->nn; iOff++){
|
||||
if( pData->p[iOff] ) break;
|
||||
}
|
||||
|
||||
if( iOff<pData->n ){
|
||||
if( iOff<pData->nn ){
|
||||
i64 iVal;
|
||||
pLvl->iLeafPgno += (iOff - pLvl->iOff) + 1;
|
||||
iOff += fts5GetVarint(&pData->p[iOff], (u64*)&iVal);
|
||||
@ -1470,7 +1494,8 @@ static int fts5GetPoslistSize(const u8 *p, int *pnSz, int *pbDel){
|
||||
static void fts5SegIterLoadNPos(Fts5Index *p, Fts5SegIter *pIter){
|
||||
if( p->rc==SQLITE_OK ){
|
||||
int iOff = pIter->iLeafOffset; /* Offset to read at */
|
||||
if( iOff>=pIter->pLeaf->n ){
|
||||
ASSERT_SZLEAF_OK(pIter->pLeaf);
|
||||
if( iOff>=pIter->pLeaf->szLeaf ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
}else{
|
||||
const u8 *a = &pIter->pLeaf->p[iOff];
|
||||
@ -1483,7 +1508,8 @@ static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){
|
||||
u8 *a = pIter->pLeaf->p; /* Buffer to read data from */
|
||||
int iOff = pIter->iLeafOffset;
|
||||
|
||||
if( iOff>=pIter->pLeaf->n ){
|
||||
ASSERT_SZLEAF_OK(pIter->pLeaf);
|
||||
if( iOff>=pIter->pLeaf->szLeaf ){
|
||||
fts5SegIterNextPage(p, pIter);
|
||||
if( pIter->pLeaf==0 ){
|
||||
if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT;
|
||||
@ -1559,7 +1585,8 @@ static void fts5SegIterInit(
|
||||
|
||||
if( p->rc==SQLITE_OK ){
|
||||
u8 *a = pIter->pLeaf->p;
|
||||
pIter->iLeafOffset = fts5GetU16(&a[2]);
|
||||
pIter->iLeafOffset = fts5GetU16(&a[pIter->pLeaf->szLeaf]);
|
||||
assert( pIter->iLeafOffset==4 );
|
||||
fts5SegIterLoadTerm(p, pIter, 0);
|
||||
fts5SegIterLoadNPos(p, pIter);
|
||||
}
|
||||
@ -1581,11 +1608,12 @@ static void fts5SegIterInit(
|
||||
** byte of the position list content associated with said rowid.
|
||||
*/
|
||||
static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){
|
||||
int n = pIter->pLeaf->n;
|
||||
int n = pIter->pLeaf->szLeaf;
|
||||
int i = pIter->iLeafOffset;
|
||||
u8 *a = pIter->pLeaf->p;
|
||||
int iRowidOffset = 0;
|
||||
|
||||
ASSERT_SZLEAF_OK(pIter->pLeaf);
|
||||
while( 1 ){
|
||||
i64 iDelta = 0;
|
||||
int nPos;
|
||||
@ -1633,7 +1661,7 @@ static void fts5SegIterReverseNewPage(Fts5Index *p, Fts5SegIter *pIter){
|
||||
));
|
||||
if( pNew ){
|
||||
if( pIter->iLeafPgno==pIter->iTermLeafPgno ){
|
||||
if( pIter->iTermLeafOffset<pNew->n ){
|
||||
if( pIter->iTermLeafOffset<pNew->szLeaf ){
|
||||
pIter->pLeaf = pNew;
|
||||
pIter->iLeafOffset = pIter->iTermLeafOffset;
|
||||
}
|
||||
@ -1712,8 +1740,9 @@ static void fts5SegIterNext(
|
||||
|
||||
/* Search for the end of the position list within the current page. */
|
||||
u8 *a = pLeaf->p;
|
||||
int n = pLeaf->n;
|
||||
int n = pLeaf->szLeaf;
|
||||
|
||||
ASSERT_SZLEAF_OK(pLeaf);
|
||||
iOff = pIter->iLeafOffset + pIter->nPos;
|
||||
|
||||
if( iOff<n ){
|
||||
@ -1726,7 +1755,7 @@ static void fts5SegIterNext(
|
||||
if( iOff>=n ){
|
||||
fts5SegIterNextPage(p, pIter);
|
||||
pIter->iLeafOffset = 4;
|
||||
}else if( iOff!=fts5GetU16(&a[2]) ){
|
||||
}else if( iOff!=fts5LeafFirstTermOff(pLeaf) ){
|
||||
pIter->iLeafOffset += fts5GetVarint32(&a[iOff], nKeep);
|
||||
}
|
||||
}else{
|
||||
@ -1745,7 +1774,8 @@ static void fts5SegIterNext(
|
||||
pIter->pLeaf = 0;
|
||||
}else{
|
||||
pIter->pLeaf->p = (u8*)pList;
|
||||
pIter->pLeaf->n = nList;
|
||||
pIter->pLeaf->nn = nList;
|
||||
pIter->pLeaf->szLeaf = nList;
|
||||
sqlite3Fts5BufferSet(&p->rc, &pIter->term, strlen(zTerm), (u8*)zTerm);
|
||||
pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid);
|
||||
}
|
||||
@ -1756,15 +1786,17 @@ static void fts5SegIterNext(
|
||||
fts5SegIterNextPage(p, pIter);
|
||||
pLeaf = pIter->pLeaf;
|
||||
if( pLeaf==0 ) break;
|
||||
if( (iOff = fts5GetU16(&pLeaf->p[0])) && iOff<pLeaf->n ){
|
||||
ASSERT_SZLEAF_OK(pLeaf);
|
||||
if( (iOff = fts5GetU16(&pLeaf->p[0])) && iOff<pLeaf->szLeaf ){
|
||||
iOff += sqlite3Fts5GetVarint(&pLeaf->p[iOff], (u64*)&pIter->iRowid);
|
||||
pIter->iLeafOffset = iOff;
|
||||
}
|
||||
else if( (iOff = fts5GetU16(&pLeaf->p[2])) ){
|
||||
else if( pLeaf->nn>pLeaf->szLeaf ){
|
||||
iOff = fts5GetU16(&pLeaf->p[pLeaf->szLeaf]);
|
||||
pIter->iLeafOffset = iOff;
|
||||
bNewTerm = 1;
|
||||
}
|
||||
if( iOff>=pLeaf->n ){
|
||||
if( iOff>=pLeaf->szLeaf ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
return;
|
||||
}
|
||||
@ -1819,7 +1851,7 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){
|
||||
|
||||
/* Search for a new term within the current leaf. If one can be found,
|
||||
** then this page contains the largest rowid for the current term. */
|
||||
while( iOff<pLeaf->n ){
|
||||
while( iOff<pLeaf->szLeaf ){
|
||||
int nPos;
|
||||
i64 iDelta;
|
||||
int bDummy;
|
||||
@ -1827,7 +1859,7 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){
|
||||
/* Read the position-list size field */
|
||||
iOff += fts5GetPoslistSize(&pLeaf->p[iOff], &nPos, &bDummy);
|
||||
iOff += nPos;
|
||||
if( iOff>=pLeaf->n ) break;
|
||||
if( iOff>=pLeaf->szLeaf ) break;
|
||||
|
||||
/* Rowid delta. Or, if 0x00, the end of doclist marker. */
|
||||
nPos = fts5GetVarint(&pLeaf->p[iOff], (u64*)&iDelta);
|
||||
@ -1838,7 +1870,7 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){
|
||||
/* If this condition is true then the largest rowid for the current
|
||||
** term may not be stored on the current page. So search forward to
|
||||
** see where said rowid really is. */
|
||||
if( iOff>=pLeaf->n ){
|
||||
if( iOff>=pLeaf->szLeaf ){
|
||||
int pgno;
|
||||
Fts5StructureSegment *pSeg = pIter->pSeg;
|
||||
|
||||
@ -1848,14 +1880,15 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){
|
||||
i64 iAbs = FTS5_SEGMENT_ROWID(pSeg->iSegid, 0, pgno);
|
||||
Fts5Data *pNew = fts5DataRead(p, iAbs);
|
||||
if( pNew ){
|
||||
int iRowid, iTerm;
|
||||
fts5LeafHeader(pNew, &iRowid, &iTerm);
|
||||
int iRowid, bTermless;
|
||||
iRowid = fts5GetU16(pNew->p);
|
||||
bTermless = fts5LeafIsTermless(pNew);
|
||||
if( iRowid ){
|
||||
SWAPVAL(Fts5Data*, pNew, pLast);
|
||||
pgnoLast = pgno;
|
||||
}
|
||||
fts5DataRelease(pNew);
|
||||
if( iTerm ) break;
|
||||
if( bTermless==0 ) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1903,7 +1936,7 @@ static void fts5SegIterLoadDlidx(Fts5Index *p, Fts5SegIter *pIter){
|
||||
** term. */
|
||||
if( pIter->iTermLeafPgno==pIter->iLeafPgno ){
|
||||
int iOff = pIter->iLeafOffset + pIter->nPos;
|
||||
while( iOff<pLeaf->n ){
|
||||
while( iOff<pLeaf->szLeaf ){
|
||||
int bDummy;
|
||||
int nPos;
|
||||
i64 iDelta;
|
||||
@ -1911,7 +1944,7 @@ static void fts5SegIterLoadDlidx(Fts5Index *p, Fts5SegIter *pIter){
|
||||
/* iOff is currently the offset of the start of position list data */
|
||||
iOff += fts5GetVarint(&pLeaf->p[iOff], (u64*)&iDelta);
|
||||
if( iDelta==0 ) return;
|
||||
assert_nc( iOff<pLeaf->n );
|
||||
assert_nc( iOff<pLeaf->szLeaf );
|
||||
iOff += fts5GetPoslistSize(&pLeaf->p[iOff], &nPos, &bDummy);
|
||||
iOff += nPos;
|
||||
}
|
||||
@ -1955,16 +1988,18 @@ static void fts5LeafSeek(
|
||||
){
|
||||
int iOff;
|
||||
const u8 *a = pIter->pLeaf->p;
|
||||
int n = pIter->pLeaf->n;
|
||||
int n = pIter->pLeaf->szLeaf;
|
||||
|
||||
int nMatch = 0;
|
||||
int nKeep = 0;
|
||||
int nNew = 0;
|
||||
int iTerm = 0;
|
||||
int nPgTerm = (pIter->pLeaf->nn - pIter->pLeaf->szLeaf) >> 1;
|
||||
|
||||
assert( p->rc==SQLITE_OK );
|
||||
assert( pIter->pLeaf );
|
||||
|
||||
iOff = fts5GetU16(&a[2]);
|
||||
iOff = fts5GetU16(&a[n]);
|
||||
if( iOff<4 || iOff>=n ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
return;
|
||||
@ -2001,6 +2036,7 @@ static void fts5LeafSeek(
|
||||
}
|
||||
iOff += nNew;
|
||||
|
||||
#if 0
|
||||
/* Skip past the doclist. If the end of the page is reached, bail out. */
|
||||
while( 1 ){
|
||||
int nPos;
|
||||
@ -2023,6 +2059,19 @@ static void fts5LeafSeek(
|
||||
}
|
||||
};
|
||||
|
||||
iTerm++;
|
||||
assert( iTerm<nPgTerm );
|
||||
assert( iOff==fts5GetU16(&a[n + iTerm*2]) );
|
||||
|
||||
#else
|
||||
iTerm++;
|
||||
if( iTerm>=nPgTerm ){
|
||||
iOff = n;
|
||||
break;
|
||||
}
|
||||
iOff = fts5GetU16(&a[n + iTerm*2]);
|
||||
#endif
|
||||
|
||||
/* Read the nKeep field of the next term. */
|
||||
fts5IndexGetVarint32(a, iOff, nKeep);
|
||||
}
|
||||
@ -2037,9 +2086,9 @@ static void fts5LeafSeek(
|
||||
fts5SegIterNextPage(p, pIter);
|
||||
if( pIter->pLeaf==0 ) return;
|
||||
a = pIter->pLeaf->p;
|
||||
iOff = fts5GetU16(&a[2]);
|
||||
if( iOff ){
|
||||
if( iOff<4 || iOff>=n ){
|
||||
if( fts5LeafIsTermless(pIter->pLeaf)==0 ){
|
||||
iOff = fts5LeafFirstTermOff(pIter->pLeaf);
|
||||
if( iOff<4 || iOff>=pIter->pLeaf->szLeaf ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
}else{
|
||||
nKeep = 0;
|
||||
@ -2190,7 +2239,7 @@ static void fts5SegIterHashInit(
|
||||
pLeaf = fts5IdxMalloc(p, sizeof(Fts5Data));
|
||||
if( pLeaf==0 ) return;
|
||||
pLeaf->p = (u8*)pList;
|
||||
pLeaf->n = nList;
|
||||
pLeaf->nn = pLeaf->szLeaf = nList;
|
||||
pIter->pLeaf = pLeaf;
|
||||
pIter->iLeafOffset = fts5GetVarint(pLeaf->p, (u64*)&pIter->iRowid);
|
||||
|
||||
@ -2383,7 +2432,7 @@ static void fts5SegIterGotoPage(
|
||||
if( p->rc==SQLITE_OK ){
|
||||
int iOff;
|
||||
u8 *a = pIter->pLeaf->p;
|
||||
int n = pIter->pLeaf->n;
|
||||
int n = pIter->pLeaf->szLeaf;
|
||||
|
||||
iOff = fts5GetU16(&a[0]);
|
||||
if( iOff<4 || iOff>=n ){
|
||||
@ -2717,7 +2766,7 @@ static void fts5MultiIterNew2(
|
||||
Fts5SegIter *pIter = &pNew->aSeg[1];
|
||||
|
||||
pIter->flags = FTS5_SEGITER_ONETERM;
|
||||
if( pData->n>0 ){
|
||||
if( pData->szLeaf>0 ){
|
||||
pIter->pLeaf = pData;
|
||||
pIter->iLeafOffset = fts5GetVarint(pData->p, (u64*)&pIter->iRowid);
|
||||
pNew->aFirst[1].iFirst = 1;
|
||||
@ -2797,7 +2846,7 @@ static void fts5ChunkIterate(
|
||||
int nRem = pSeg->nPos; /* Number of bytes still to come */
|
||||
Fts5Data *pData = 0;
|
||||
u8 *pChunk = &pSeg->pLeaf->p[pSeg->iLeafOffset];
|
||||
int nChunk = MIN(nRem, pSeg->pLeaf->n - pSeg->iLeafOffset);
|
||||
int nChunk = MIN(nRem, pSeg->pLeaf->szLeaf - pSeg->iLeafOffset);
|
||||
int pgno = pSeg->iLeafPgno;
|
||||
int pgnoSave = 0;
|
||||
|
||||
@ -2816,7 +2865,7 @@ static void fts5ChunkIterate(
|
||||
pData = fts5DataRead(p, FTS5_SEGMENT_ROWID(pSeg->pSeg->iSegid, 0, pgno));
|
||||
if( pData==0 ) break;
|
||||
pChunk = &pData->p[4];
|
||||
nChunk = MIN(nRem, pData->n - 4);
|
||||
nChunk = MIN(nRem, pData->szLeaf - 4);
|
||||
if( pgno==pgnoSave ){
|
||||
assert( pSeg->pNextLeaf==0 );
|
||||
pSeg->pNextLeaf = pData;
|
||||
@ -3102,18 +3151,28 @@ static void fts5WriteFlushLeaf(Fts5Index *p, Fts5SegWriter *pWriter){
|
||||
Fts5PageWriter *pPage = &pWriter->writer;
|
||||
i64 iRowid;
|
||||
|
||||
assert( (pPage->pgidx.n==0)==(pWriter->bFirstTermInPage) );
|
||||
|
||||
/* Set the szLeaf header field. */
|
||||
assert( 0==fts5GetU16(&pPage->buf.p[2]) );
|
||||
fts5PutU16(&pPage->buf.p[2], pPage->buf.n);
|
||||
|
||||
if( pWriter->bFirstTermInPage ){
|
||||
/* No term was written to this page. */
|
||||
assert( 0==fts5GetU16(&pPage->buf.p[2]) );
|
||||
assert( pPage->pgidx.n==0 );
|
||||
fts5WriteBtreeNoTerm(p, pWriter);
|
||||
}else{
|
||||
/* Append the pgidx to the page buffer. Set the szLeaf header field. */
|
||||
fts5BufferAppendBlob(&p->rc, &pPage->buf, pPage->pgidx.n, pPage->pgidx.p);
|
||||
}
|
||||
|
||||
/* Write the current page to the db. */
|
||||
/* Write the page out to disk */
|
||||
iRowid = FTS5_SEGMENT_ROWID(pWriter->iSegid, 0, pPage->pgno);
|
||||
fts5DataWrite(p, iRowid, pPage->buf.p, pPage->buf.n);
|
||||
|
||||
/* Initialize the next page. */
|
||||
fts5BufferZero(&pPage->buf);
|
||||
fts5BufferZero(&pPage->pgidx);
|
||||
fts5BufferAppendBlob(&p->rc, &pPage->buf, 4, zero);
|
||||
pPage->pgno++;
|
||||
|
||||
@ -3139,6 +3198,7 @@ static void fts5WriteAppendTerm(
|
||||
){
|
||||
int nPrefix; /* Bytes of prefix compression for term */
|
||||
Fts5PageWriter *pPage = &pWriter->writer;
|
||||
Fts5Buffer *pPgidx = &pWriter->writer.pgidx;
|
||||
|
||||
assert( pPage->buf.n==0 || pPage->buf.n>4 );
|
||||
if( pPage->buf.n==0 ){
|
||||
@ -3149,10 +3209,16 @@ static void fts5WriteAppendTerm(
|
||||
}
|
||||
if( p->rc ) return;
|
||||
|
||||
/* TODO1: Can this be consolidated with FlushOneHash version? */
|
||||
fts5PutU16(&pPgidx->p[pPgidx->n], pPage->buf.n);
|
||||
pPgidx->n += 2;
|
||||
|
||||
if( pWriter->bFirstTermInPage ){
|
||||
/* Update the "first term" field of the page header. */
|
||||
#if 0
|
||||
assert( pPage->buf.p[2]==0 && pPage->buf.p[3]==0 );
|
||||
fts5PutU16(&pPage->buf.p[2], pPage->buf.n);
|
||||
#endif
|
||||
nPrefix = 0;
|
||||
if( pPage->pgno!=1 ){
|
||||
/* This is the first term on a leaf that is not the leftmost leaf in
|
||||
@ -3196,7 +3262,7 @@ static void fts5WriteAppendTerm(
|
||||
pWriter->aDlidx[0].pgno = pPage->pgno;
|
||||
|
||||
/* If the current leaf page is full, flush it to disk. */
|
||||
if( pPage->buf.n>=p->pConfig->pgsz ){
|
||||
if( (pPage->buf.n + pPage->pgidx.n)>=p->pConfig->pgsz ){
|
||||
fts5WriteFlushLeaf(p, pWriter);
|
||||
}
|
||||
}
|
||||
@ -3234,7 +3300,7 @@ static void fts5WriteAppendRowid(
|
||||
|
||||
fts5BufferAppendVarint(&p->rc, &pPage->buf, nPos);
|
||||
|
||||
if( pPage->buf.n>=p->pConfig->pgsz ){
|
||||
if( (pPage->buf.n + pPage->pgidx.n)>=p->pConfig->pgsz ){
|
||||
fts5WriteFlushLeaf(p, pWriter);
|
||||
}
|
||||
}
|
||||
@ -3251,8 +3317,10 @@ static void fts5WriteAppendPoslistData(
|
||||
int n = nData;
|
||||
|
||||
assert( p->pConfig->pgsz>0 );
|
||||
while( p->rc==SQLITE_OK && (pPage->buf.n + n)>=p->pConfig->pgsz ){
|
||||
int nReq = p->pConfig->pgsz - pPage->buf.n;
|
||||
while( p->rc==SQLITE_OK
|
||||
&& (pPage->buf.n + pPage->pgidx.n + n)>=p->pConfig->pgsz
|
||||
){
|
||||
int nReq = p->pConfig->pgsz - pPage->buf.n - pPage->pgidx.n;
|
||||
int nCopy = 0;
|
||||
while( nCopy<nReq ){
|
||||
i64 dummy;
|
||||
@ -3300,6 +3368,7 @@ static void fts5WriteFinish(
|
||||
}
|
||||
fts5BufferFree(&pLeaf->term);
|
||||
fts5BufferFree(&pLeaf->buf);
|
||||
fts5BufferFree(&pLeaf->pgidx);
|
||||
fts5BufferFree(&pWriter->btterm);
|
||||
|
||||
for(i=0; i<pWriter->nDlidx; i++){
|
||||
@ -3321,6 +3390,7 @@ static void fts5WriteInit(
|
||||
pWriter->bFirstTermInPage = 1;
|
||||
pWriter->iBtPage = 1;
|
||||
|
||||
fts5BufferGrow(&p->rc, &pWriter->writer.pgidx, p->pConfig->pgsz + 20);
|
||||
if( p->pIdxWriter==0 ){
|
||||
Fts5Config *pConfig = p->pConfig;
|
||||
fts5IndexPrepareStmt(p, &p->pIdxWriter, sqlite3_mprintf(
|
||||
@ -3334,6 +3404,51 @@ static void fts5WriteInit(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** The buffer passed as the second argument contains a leaf page that is
|
||||
** missing its page-idx array. The first term is guaranteed to start at
|
||||
** byte offset 4 of the buffer. The szLeaf field of the leaf page header
|
||||
** is already populated.
|
||||
**
|
||||
** This function appends a page-index to the buffer. The buffer is
|
||||
** guaranteed to be large enough to fit the page-index.
|
||||
*/
|
||||
static void fts5MakePageidx(Fts5Index *p, Fts5Buffer *pBuf){
|
||||
if( p->rc==SQLITE_OK ){
|
||||
u8 *a = pBuf->p;
|
||||
int szLeaf = pBuf->n;
|
||||
int iOff = 4;
|
||||
int nTerm;
|
||||
|
||||
fts5PutU16(&pBuf->p[pBuf->n], iOff);
|
||||
pBuf->n += 2;
|
||||
fts5IndexGetVarint32(a, iOff, nTerm);
|
||||
iOff += nTerm;
|
||||
|
||||
while( iOff<szLeaf ){
|
||||
/* Skip the rowid delta rowid delta. */
|
||||
fts5IndexSkipVarint(a, iOff);
|
||||
if( iOff>=szLeaf ) break;
|
||||
|
||||
/* Skip past position list */
|
||||
fts5IndexGetVarint32(a, iOff, nTerm);
|
||||
iOff += (nTerm >> 1);
|
||||
|
||||
if( iOff>=(szLeaf-2) ) break;
|
||||
|
||||
/* If this is the end of the doclist, break out of the loop */
|
||||
if( a[iOff]==0x00 ){
|
||||
iOff++;
|
||||
fts5PutU16(&pBuf->p[pBuf->n], iOff);
|
||||
pBuf->n += 2;
|
||||
fts5IndexGetVarint32(a, iOff, nTerm);
|
||||
fts5IndexGetVarint32(a, iOff, nTerm);
|
||||
iOff += nTerm;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Iterator pIter was used to iterate through the input segments of on an
|
||||
** incremental merge operation. This function is called if the incremental
|
||||
@ -3358,16 +3473,25 @@ static void fts5TrimSegments(Fts5Index *p, Fts5IndexIter *pIter){
|
||||
i64 iLeafRowid;
|
||||
Fts5Data *pData;
|
||||
int iId = pSeg->pSeg->iSegid;
|
||||
u8 aHdr[4] = {0x00, 0x00, 0x00, 0x04};
|
||||
u8 aHdr[4] = {0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
iLeafRowid = FTS5_SEGMENT_ROWID(iId, 0, pSeg->iTermLeafPgno);
|
||||
pData = fts5DataRead(p, iLeafRowid);
|
||||
if( pData ){
|
||||
fts5BufferZero(&buf);
|
||||
fts5BufferGrow(&p->rc, &buf, pData->nn);
|
||||
fts5BufferAppendBlob(&p->rc, &buf, sizeof(aHdr), aHdr);
|
||||
fts5BufferAppendVarint(&p->rc, &buf, pSeg->term.n);
|
||||
fts5BufferAppendBlob(&p->rc, &buf, pSeg->term.n, pSeg->term.p);
|
||||
fts5BufferAppendBlob(&p->rc, &buf, pData->n - iOff, &pData->p[iOff]);
|
||||
fts5BufferAppendBlob(&p->rc, &buf, pData->szLeaf-iOff, &pData->p[iOff]);
|
||||
if( p->rc==SQLITE_OK ){
|
||||
/* Set the szLeaf field */
|
||||
fts5PutU16(&buf.p[2], buf.n);
|
||||
}
|
||||
|
||||
/* Set up the new page-index array */
|
||||
fts5MakePageidx(p, &buf);
|
||||
|
||||
fts5DataRelease(pData);
|
||||
pSeg->pSeg->pgnoFirst = pSeg->iTermLeafPgno;
|
||||
fts5DataDelete(p, FTS5_SEGMENT_ROWID(iId, 0, 1), iLeafRowid);
|
||||
@ -3679,6 +3803,7 @@ static void fts5FlushOneHash(Fts5Index *p){
|
||||
Fts5StructureSegment *pSeg; /* New segment within pStruct */
|
||||
int nHeight; /* Height of new segment b-tree */
|
||||
Fts5Buffer *pBuf; /* Buffer in which to assemble leaf page */
|
||||
Fts5Buffer *pPgidx; /* Buffer in which to assemble pgidx */
|
||||
const u8 *zPrev = 0;
|
||||
|
||||
Fts5SegWriter writer;
|
||||
@ -3688,6 +3813,7 @@ static void fts5FlushOneHash(Fts5Index *p){
|
||||
** page size. */
|
||||
assert( pgsz>0 );
|
||||
pBuf = &writer.writer.buf;
|
||||
pPgidx = &writer.writer.pgidx;
|
||||
fts5BufferGrow(&p->rc, pBuf, pgsz + 20);
|
||||
|
||||
/* Begin scanning through hash table entries. This loop runs once for each
|
||||
@ -3707,11 +3833,11 @@ static void fts5FlushOneHash(Fts5Index *p){
|
||||
sqlite3Fts5HashScanEntry(pHash, &zTerm, &pDoclist, &nDoclist);
|
||||
nTerm = strlen(zTerm);
|
||||
|
||||
/* Decide if the term will fit on the current leaf. If it will not,
|
||||
** flush the leaf to disk here. */
|
||||
if( pBuf->n>4 && (pBuf->n + nTerm + 2) > pgsz ){
|
||||
/* Decide if the term will fit on the current leaf. If it will not,
|
||||
** flush the leaf to disk here.
|
||||
** TODO1: Is this calculation still correct? */
|
||||
if( pBuf->n>4 && (pBuf->n + nTerm + 2 + pPgidx->n + 2) > pgsz ){
|
||||
fts5WriteFlushLeaf(p, &writer);
|
||||
pBuf = &writer.writer.buf;
|
||||
if( (nTerm + 32) > pBuf->nSpace ){
|
||||
fts5BufferGrow(&p->rc, pBuf, nTerm + 32 - pBuf->n);
|
||||
if( p->rc ) break;
|
||||
@ -3721,12 +3847,15 @@ static void fts5FlushOneHash(Fts5Index *p){
|
||||
/* Write the term to the leaf. And if it is the first on the leaf, and
|
||||
** the leaf is not page number 1, push it up into the b-tree hierarchy
|
||||
** as well. */
|
||||
|
||||
/* TODO1: Writing pgidx here! */
|
||||
fts5PutU16(&pPgidx->p[pPgidx->n], pBuf->n);
|
||||
pPgidx->n += 2;
|
||||
if( writer.bFirstTermInPage==0 ){
|
||||
int nPre = fts5PrefixCompress(nTerm, zPrev, nTerm, (const u8*)zTerm);
|
||||
pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], nPre);
|
||||
nSuffix = nTerm - nPre;
|
||||
}else{
|
||||
fts5PutU16(&pBuf->p[2], pBuf->n);
|
||||
writer.bFirstTermInPage = 0;
|
||||
if( writer.writer.pgno!=1 ){
|
||||
int nPre = fts5PrefixCompress(nTerm, zPrev, nTerm, (const u8*)zTerm);
|
||||
@ -3745,7 +3874,7 @@ static void fts5FlushOneHash(Fts5Index *p){
|
||||
assert( writer.nDlidx>0 && writer.aDlidx[0].buf.n==0 );
|
||||
writer.aDlidx[0].pgno = writer.writer.pgno;
|
||||
|
||||
if( pgsz>=(pBuf->n + nDoclist + 1) ){
|
||||
if( pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){
|
||||
/* The entire doclist will fit on the current leaf. */
|
||||
fts5BufferSafeAppendBlob(pBuf, pDoclist, nDoclist);
|
||||
}else{
|
||||
@ -3777,7 +3906,7 @@ static void fts5FlushOneHash(Fts5Index *p){
|
||||
}
|
||||
assert( pBuf->n<=pBuf->nSpace );
|
||||
|
||||
if( (pBuf->n + nCopy) <= pgsz ){
|
||||
if( (pBuf->n + pPgidx->n + nCopy) <= pgsz ){
|
||||
/* The entire poslist will fit on the current leaf. So copy
|
||||
** it in one go. */
|
||||
fts5BufferSafeAppendBlob(pBuf, &pDoclist[iOff], nCopy);
|
||||
@ -3788,7 +3917,7 @@ static void fts5FlushOneHash(Fts5Index *p){
|
||||
const u8 *pPoslist = &pDoclist[iOff];
|
||||
int iPos = 0;
|
||||
while( p->rc==SQLITE_OK ){
|
||||
int nSpace = pgsz - pBuf->n;
|
||||
int nSpace = pgsz - pBuf->n - pPgidx->n;
|
||||
int n = 0;
|
||||
if( (nCopy - iPos)<=nSpace ){
|
||||
n = nCopy - iPos;
|
||||
@ -3798,9 +3927,8 @@ static void fts5FlushOneHash(Fts5Index *p){
|
||||
assert( n>0 );
|
||||
fts5BufferSafeAppendBlob(pBuf, &pPoslist[iPos], n);
|
||||
iPos += n;
|
||||
if( pBuf->n>=pgsz ){
|
||||
if( (pBuf->n + pPgidx->n)>=pgsz ){
|
||||
fts5WriteFlushLeaf(p, &writer);
|
||||
pBuf = &writer.writer.buf;
|
||||
}
|
||||
if( iPos>=nCopy ) break;
|
||||
}
|
||||
@ -4163,7 +4291,7 @@ static void fts5SetupPrefixIter(
|
||||
pData = fts5IdxMalloc(p, sizeof(Fts5Data) + doclist.n);
|
||||
if( pData ){
|
||||
pData->p = (u8*)&pData[1];
|
||||
pData->n = doclist.n;
|
||||
pData->nn = pData->szLeaf = doclist.n;
|
||||
memcpy(pData->p, doclist.p, doclist.n);
|
||||
fts5MultiIterNew2(p, pData, bDesc, ppIter);
|
||||
}
|
||||
@ -4393,6 +4521,11 @@ int sqlite3Fts5IndexQuery(
|
||||
memcpy(&buf.p[1], pToken, nToken);
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* If the QUERY_TEST_NOIDX flag was specified, then this must be a
|
||||
** prefix-query. Instead of using a prefix-index (if one exists),
|
||||
** evaluate the prefix query using the main FTS index. This is used
|
||||
** for internal sanity checking by the integrity-check in debug
|
||||
** mode only. */
|
||||
if( flags & FTS5INDEX_QUERY_TEST_NOIDX ){
|
||||
assert( flags & FTS5INDEX_QUERY_PREFIX );
|
||||
iIdx = 1+pConfig->nPrefix;
|
||||
@ -4513,7 +4646,7 @@ int sqlite3Fts5IterPoslist(
|
||||
assert( pIter->pIndex->rc==SQLITE_OK );
|
||||
*piRowid = pSeg->iRowid;
|
||||
*pn = pSeg->nPos;
|
||||
if( pSeg->iLeafOffset+pSeg->nPos <= pSeg->pLeaf->n ){
|
||||
if( pSeg->iLeafOffset+pSeg->nPos <= pSeg->pLeaf->szLeaf ){
|
||||
*pp = &pSeg->pLeaf->p[pSeg->iLeafOffset];
|
||||
}else{
|
||||
fts5BufferZero(&pIter->poslist);
|
||||
@ -4561,11 +4694,11 @@ int sqlite3Fts5IndexGetAverages(Fts5Index *p, i64 *pnRow, i64 *anSize){
|
||||
*pnRow = 0;
|
||||
memset(anSize, 0, sizeof(i64) * nCol);
|
||||
pData = fts5DataRead(p, FTS5_AVERAGES_ROWID);
|
||||
if( p->rc==SQLITE_OK && pData->n ){
|
||||
if( p->rc==SQLITE_OK && pData->nn ){
|
||||
int i = 0;
|
||||
int iCol;
|
||||
i += fts5GetVarint(&pData->p[i], (u64*)pnRow);
|
||||
for(iCol=0; i<pData->n && iCol<nCol; iCol++){
|
||||
for(iCol=0; i<pData->nn && iCol<nCol; iCol++){
|
||||
i += fts5GetVarint(&pData->p[i], (u64*)&anSize[iCol]);
|
||||
}
|
||||
}
|
||||
@ -4770,18 +4903,25 @@ static void fts5TestTerm(
|
||||
if( rc==SQLITE_OK && ck1!=ck2 ) rc = FTS5_CORRUPT;
|
||||
|
||||
/* If this is a prefix query, check that the results returned if the
|
||||
** the index is disabled are the same. In both ASC and DESC order. */
|
||||
if( iIdx>0 && rc==SQLITE_OK ){
|
||||
int f = flags|FTS5INDEX_QUERY_TEST_NOIDX;
|
||||
ck2 = 0;
|
||||
rc = fts5QueryCksum(p, iIdx, zTerm, nTerm, f, &ck2);
|
||||
if( rc==SQLITE_OK && ck1!=ck2 ) rc = FTS5_CORRUPT;
|
||||
}
|
||||
if( iIdx>0 && rc==SQLITE_OK ){
|
||||
int f = flags|FTS5INDEX_QUERY_TEST_NOIDX|FTS5INDEX_QUERY_DESC;
|
||||
ck2 = 0;
|
||||
rc = fts5QueryCksum(p, iIdx, zTerm, nTerm, f, &ck2);
|
||||
if( rc==SQLITE_OK && ck1!=ck2 ) rc = FTS5_CORRUPT;
|
||||
** the index is disabled are the same. In both ASC and DESC order.
|
||||
**
|
||||
** This check may only be performed if the hash table is empty. This
|
||||
** is because the hash table only supports a single scan query at
|
||||
** a time, and the multi-iter loop from which this function is called
|
||||
** is already performing such a scan. */
|
||||
if( p->nPendingData==0 ){
|
||||
if( iIdx>0 && rc==SQLITE_OK ){
|
||||
int f = flags|FTS5INDEX_QUERY_TEST_NOIDX;
|
||||
ck2 = 0;
|
||||
rc = fts5QueryCksum(p, iIdx, zTerm, nTerm, f, &ck2);
|
||||
if( rc==SQLITE_OK && ck1!=ck2 ) rc = FTS5_CORRUPT;
|
||||
}
|
||||
if( iIdx>0 && rc==SQLITE_OK ){
|
||||
int f = flags|FTS5INDEX_QUERY_TEST_NOIDX|FTS5INDEX_QUERY_DESC;
|
||||
ck2 = 0;
|
||||
rc = fts5QueryCksum(p, iIdx, zTerm, nTerm, f, &ck2);
|
||||
if( rc==SQLITE_OK && ck1!=ck2 ) rc = FTS5_CORRUPT;
|
||||
}
|
||||
}
|
||||
|
||||
cksum3 ^= ck1;
|
||||
@ -4822,7 +4962,7 @@ static void fts5IndexIntegrityCheckEmpty(
|
||||
for(i=iFirst; p->rc==SQLITE_OK && i<=iLast; i++){
|
||||
Fts5Data *pLeaf = fts5DataRead(p, FTS5_SEGMENT_ROWID(pSeg->iSegid, 0, i));
|
||||
if( pLeaf ){
|
||||
if( 0!=fts5GetU16(&pLeaf->p[2]) ) p->rc = FTS5_CORRUPT;
|
||||
if( !fts5LeafIsTermless(pLeaf) ) p->rc = FTS5_CORRUPT;
|
||||
if( i>=iNoRowid && 0!=fts5GetU16(&pLeaf->p[0]) ) p->rc = FTS5_CORRUPT;
|
||||
}
|
||||
fts5DataRelease(pLeaf);
|
||||
@ -4851,7 +4991,6 @@ static void fts5IndexIntegrityCheckSegment(
|
||||
while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
i64 iRow; /* Rowid for this leaf */
|
||||
Fts5Data *pLeaf; /* Data for this leaf */
|
||||
int iOff; /* Offset of first term on leaf */
|
||||
|
||||
int nIdxTerm = sqlite3_column_bytes(pStmt, 1);
|
||||
const char *zIdxTerm = (const char*)sqlite3_column_text(pStmt, 1);
|
||||
@ -4869,14 +5008,15 @@ static void fts5IndexIntegrityCheckSegment(
|
||||
** to or larger than the split-key in zIdxTerm. Also check that if there
|
||||
** is also a rowid pointer within the leaf page header, it points to a
|
||||
** location before the term. */
|
||||
iOff = fts5GetU16(&pLeaf->p[2]);
|
||||
if( iOff==0 ){
|
||||
if( pLeaf->nn<=pLeaf->szLeaf ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
}else{
|
||||
int iRowidOff;
|
||||
int iOff; /* Offset of first term on leaf */
|
||||
int iRowidOff; /* Offset of first rowid on leaf */
|
||||
int nTerm; /* Size of term on leaf in bytes */
|
||||
int res; /* Comparison of term and split-key */
|
||||
|
||||
iOff = fts5LeafFirstTermOff(pLeaf);
|
||||
iRowidOff = fts5GetU16(&pLeaf->p[0]);
|
||||
if( iRowidOff>=iOff ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
@ -4929,7 +5069,8 @@ static void fts5IndexIntegrityCheckSegment(
|
||||
if( pLeaf ){
|
||||
i64 iRowid;
|
||||
int iRowidOff = fts5GetU16(&pLeaf->p[0]);
|
||||
if( iRowidOff>=pLeaf->n ){
|
||||
ASSERT_SZLEAF_OK(pLeaf);
|
||||
if( iRowidOff>=pLeaf->szLeaf ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
}else{
|
||||
fts5GetVarint(&pLeaf->p[iRowidOff], (u64*)&iRowid);
|
||||
@ -5193,8 +5334,10 @@ static int fts5DecodeDoclist(int *pRc, Fts5Buffer *pBuf, const u8 *a, int n){
|
||||
i64 iDocid;
|
||||
int iOff = 0;
|
||||
|
||||
iOff = sqlite3Fts5GetVarint(&a[iOff], (u64*)&iDocid);
|
||||
sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " rowid=%lld", iDocid);
|
||||
if( n>0 ){
|
||||
iOff = sqlite3Fts5GetVarint(a, (u64*)&iDocid);
|
||||
sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " id=%lld", iDocid);
|
||||
}
|
||||
while( iOff<n ){
|
||||
int nPos;
|
||||
int bDummy;
|
||||
@ -5205,7 +5348,7 @@ static int fts5DecodeDoclist(int *pRc, Fts5Buffer *pBuf, const u8 *a, int n){
|
||||
iOff += sqlite3Fts5GetVarint(&a[iOff], (u64*)&iDelta);
|
||||
if( iDelta==0 ) return iOff;
|
||||
iDocid += iDelta;
|
||||
sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " rowid=%lld", iDocid);
|
||||
sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " id=%lld", iDocid);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5231,13 +5374,18 @@ static void fts5DecodeFunction(
|
||||
assert( nArg==2 );
|
||||
memset(&s, 0, sizeof(Fts5Buffer));
|
||||
iRowid = sqlite3_value_int64(apVal[0]);
|
||||
|
||||
/* Make a copy of the second argument (a blob) in aBlob[]. The aBlob[]
|
||||
** copy is followed by FTS5_DATA_ZERO_PADDING 0x00 bytes, which prevents
|
||||
** buffer overreads even if the record is corrupt. */
|
||||
n = sqlite3_value_bytes(apVal[1]);
|
||||
aBlob = sqlite3_value_blob(apVal[1]);
|
||||
|
||||
nSpace = n + FTS5_DATA_ZERO_PADDING;
|
||||
a = (u8*)sqlite3Fts5MallocZero(&rc, nSpace);
|
||||
if( a==0 ) goto decode_out;
|
||||
memcpy(a, aBlob, n);
|
||||
|
||||
|
||||
fts5DecodeRowid(iRowid, &iSegid, &bDlidx, &iHeight, &iPgno);
|
||||
|
||||
fts5DebugRowid(&rc, &s, iRowid);
|
||||
@ -5246,7 +5394,7 @@ static void fts5DecodeFunction(
|
||||
Fts5DlidxLvl lvl;
|
||||
|
||||
dlidx.p = a;
|
||||
dlidx.n = n;
|
||||
dlidx.nn = n;
|
||||
|
||||
memset(&lvl, 0, sizeof(Fts5DlidxLvl));
|
||||
lvl.pData = &dlidx;
|
||||
@ -5266,6 +5414,7 @@ static void fts5DecodeFunction(
|
||||
}else{
|
||||
Fts5Buffer term;
|
||||
int iTermOff = 0;
|
||||
int szLeaf = 0;
|
||||
int iRowidOff = 0;
|
||||
int iOff;
|
||||
int nKeep = 0;
|
||||
@ -5274,7 +5423,10 @@ static void fts5DecodeFunction(
|
||||
|
||||
if( n>=4 ){
|
||||
iRowidOff = fts5GetU16(&a[0]);
|
||||
iTermOff = fts5GetU16(&a[2]);
|
||||
szLeaf = fts5GetU16(&a[2]);
|
||||
if( szLeaf<n ){
|
||||
iTermOff = fts5GetU16(&a[szLeaf]);
|
||||
}
|
||||
}else{
|
||||
sqlite3Fts5BufferSet(&rc, &s, 8, (const u8*)"corrupt");
|
||||
goto decode_out;
|
||||
@ -5285,17 +5437,17 @@ static void fts5DecodeFunction(
|
||||
}else if( iTermOff ){
|
||||
iOff = iTermOff;
|
||||
}else{
|
||||
iOff = n;
|
||||
iOff = szLeaf;
|
||||
}
|
||||
fts5DecodePoslist(&rc, &s, &a[4], iOff-4);
|
||||
|
||||
assert( iRowidOff==0 || iOff==iRowidOff );
|
||||
if( iRowidOff ){
|
||||
iOff += fts5DecodeDoclist(&rc, &s, &a[iOff], n-iOff);
|
||||
iOff += fts5DecodeDoclist(&rc, &s, &a[iOff], szLeaf-iOff);
|
||||
}
|
||||
|
||||
assert( iTermOff==0 || iOff==iTermOff );
|
||||
while( iOff<n ){
|
||||
while( iOff<szLeaf ){
|
||||
int nByte;
|
||||
iOff += fts5GetVarint32(&a[iOff], nByte);
|
||||
term.n= nKeep;
|
||||
@ -5304,9 +5456,9 @@ static void fts5DecodeFunction(
|
||||
|
||||
sqlite3Fts5BufferAppendPrintf(
|
||||
&rc, &s, " term=%.*s", term.n, (const char*)term.p
|
||||
);
|
||||
iOff += fts5DecodeDoclist(&rc, &s, &a[iOff], n-iOff);
|
||||
if( iOff<n ){
|
||||
);
|
||||
iOff += fts5DecodeDoclist(&rc, &s, &a[iOff], szLeaf-iOff);
|
||||
if( iOff<szLeaf ){
|
||||
iOff += fts5GetVarint32(&a[iOff], nKeep);
|
||||
}
|
||||
}
|
||||
|
@ -139,7 +139,6 @@ foreach {i x y} {
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
breakpoint
|
||||
reset_db
|
||||
do_execsql_test 6.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x,y);
|
||||
@ -201,8 +200,14 @@ for {set i 1} {$i <= 10} {incr i} {
|
||||
}
|
||||
execsql { INSERT INTO t1(t1) VALUES('integrity-check'); }
|
||||
} {}
|
||||
if {[set_test_counter errors]} break
|
||||
}
|
||||
|
||||
#db eval { SELECT fts5_decode(rowid, block) as x FROM t1_data } { puts $x }
|
||||
#puts [db eval {SELECT rowid FROM t1 WHERE t1 MATCH 'aaa' ORDER BY rowid ASC}]
|
||||
#puts [db eval {SELECT rowid FROM t1 WHERE t1 MATCH 'aaa' ORDER BY rowid DESC}]
|
||||
#exit
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
|
@ -205,6 +205,9 @@ foreach {T create} {
|
||||
return $ret
|
||||
}
|
||||
|
||||
do_execsql_test $T.integrity {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
foreach {bAsc sql} {
|
||||
1 {SELECT rowid FROM t1 WHERE t1 MATCH $prefix}
|
||||
|
74
ext/fts5/test/fts5simple.test
Normal file
74
ext/fts5/test/fts5simple.test
Normal file
@ -0,0 +1,74 @@
|
||||
# 2015 September 05
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5aa
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
if 1 {
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
set doc "x x [string repeat {y } 50]z z"
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x);
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES($doc);
|
||||
COMMIT;
|
||||
}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x);
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
|
||||
INSERT INTO t1 VALUES('a b c');
|
||||
INSERT INTO t1 VALUES('d e f');
|
||||
INSERT INTO t1(t1) VALUES('optimize');
|
||||
}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
} {}
|
||||
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, prefix='1,2');
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES('one');
|
||||
SELECT * FROM t1 WHERE t1 MATCH 'o*';
|
||||
} {one}
|
||||
|
||||
breakpoint
|
||||
do_execsql_test 3.1 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
} {}
|
||||
|
||||
# db eval { SELECT fts5_decode(rowid, block) as x FROM t1_data } { puts $x }
|
||||
|
||||
finish_test
|
||||
|
@ -104,6 +104,7 @@ if {$O(delete)} { file delete -force $dbfile }
|
||||
sqlite3 db $dbfile
|
||||
catch { load_static_extension db fts5 }
|
||||
db func loadfile loadfile
|
||||
db eval "PRAGMA page_size=4096"
|
||||
|
||||
db transaction {
|
||||
set pref ""
|
||||
|
26
manifest
26
manifest
@ -1,5 +1,5 @@
|
||||
C Fix\san\sunreachable\sconditional\sin\sthe\sWHERE\sclause\sanalysis\slogic.
|
||||
D 2015-09-05T19:21:00.671
|
||||
C Experiment\swith\sa\sdifferent\sfts5\sleaf\spage\sformat\sthat\sallows\sfaster\sseeks.
|
||||
D 2015-09-05T19:52:08.105
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in f85066ce844a28b671aaeeff320921cd0ce36239
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@ -112,7 +112,7 @@ F ext/fts5/fts5_buffer.c 80f9ba4431848cb857e3d2158f5280093dcd8015
|
||||
F ext/fts5/fts5_config.c 80b61fd2c6844b64a3e72a64572d50a812da9384
|
||||
F ext/fts5/fts5_expr.c 1c24e1a2ffb286bfe37e537a43b7fadabfe993d4
|
||||
F ext/fts5/fts5_hash.c 4bf4b99708848357b8a2b5819e509eb6d3df9246
|
||||
F ext/fts5/fts5_index.c 950e37028cc81ae21534819e79c73aea7efa6c8e
|
||||
F ext/fts5/fts5_index.c c34a64666c3b573aaed0fe103ce739ca2c0b88e5
|
||||
F ext/fts5/fts5_main.c e9d0892424bb7f0a8b58613d4ff75cb650cf286e
|
||||
F ext/fts5/fts5_storage.c 120f7b143688b5b7710dacbd48cff211609b8059
|
||||
F ext/fts5/fts5_tcl.c 6da58d6e8f42a93c4486b5ba9b187a7f995dee37
|
||||
@ -124,10 +124,10 @@ F ext/fts5/fts5_vocab.c 4622e0b7d84a488a1585aaa56eb214ee67a988bc
|
||||
F ext/fts5/fts5parse.y 833db1101b78c0c47686ab1b84918e38c36e9452
|
||||
F ext/fts5/mkportersteps.tcl 5acf962d2e0074f701620bb5308155fa1e4a63ba
|
||||
F ext/fts5/test/fts5_common.tcl b6e6a40ef5d069c8e86ca4fbad491e1195485dbc
|
||||
F ext/fts5/test/fts5aa.test f558e1e5ccffa75d69e9a4814245d468ec6b6608
|
||||
F ext/fts5/test/fts5aa.test 1ac5a3bd88406183b00ea7cb4701bd58e5c3c7ff
|
||||
F ext/fts5/test/fts5ab.test 6fe3a56731d15978afbb74ae51b355fc9310f2ad
|
||||
F ext/fts5/test/fts5ac.test 9737992d08c56bfd4803e933744d2d764e23795c
|
||||
F ext/fts5/test/fts5ad.test b2edee8b7de0c21d2c88f8a18c195034aad6952d
|
||||
F ext/fts5/test/fts5ad.test e3dfb150fce971b4fd832498c29f56924d451b63
|
||||
F ext/fts5/test/fts5ae.test 0a9984fc3479f89f8c63d9848d6ed0c465dfcebe
|
||||
F ext/fts5/test/fts5af.test c2501ec2b61d6b179c305f5d2b8782ab3d4f832a
|
||||
F ext/fts5/test/fts5ag.test ec3e119b728196620a31507ef503c455a7a73505
|
||||
@ -173,6 +173,7 @@ F ext/fts5/test/fts5rank.test 11dcebba31d822f7e99685b4ea2c2ae3ec0b16f1
|
||||
F ext/fts5/test/fts5rebuild.test 03935f617ace91ed23a6099c7c74d905227ff29b
|
||||
F ext/fts5/test/fts5restart.test c17728fdea26e7d0f617d22ad5b4b2862b994c17
|
||||
F ext/fts5/test/fts5rowid.test 6f9833b23b176dc4aa15b7fc02afeb2b220fd460
|
||||
F ext/fts5/test/fts5simple.test f520b360c40a5a61aabebead53e1e5f68683e5a3
|
||||
F ext/fts5/test/fts5synonym.test cf88c0a56d5ea9591e3939ef1f6e294f7f2d0671
|
||||
F ext/fts5/test/fts5tokenizer.test ea4df698b35cc427ebf2ba22829d0e28386d8c89
|
||||
F ext/fts5/test/fts5unicode.test fbef8d8a3b4b88470536cc57604a82ca52e51841
|
||||
@ -181,7 +182,7 @@ F ext/fts5/test/fts5unicode3.test 35c3d02aa7acf7d43d8de3bfe32c15ba96e8928e
|
||||
F ext/fts5/test/fts5unindexed.test e9539d5b78c677315e7ed8ea911d4fd25437c680
|
||||
F ext/fts5/test/fts5version.test 205beb2a67d9496af64df959e6a19238f69b83e8
|
||||
F ext/fts5/test/fts5vocab.test cdf97b9678484e9bad5062edf9c9106e5c3b0c5c
|
||||
F ext/fts5/tool/loadfts5.tcl 95edf0b6b92a09f9ed85595038b1108127987556
|
||||
F ext/fts5/tool/loadfts5.tcl 78253d64562774e7fd62b72ce866eeb3fcac4d0e
|
||||
F ext/fts5/tool/mkfts5c.tcl 5745072c7de346e18c7f491e4c3281fe8a1cfe51
|
||||
F ext/fts5/tool/showfts5.tcl 9eaf6c3df352f98a2ab5ce1921dd94128ab1381d
|
||||
F ext/icu/README.txt d9fbbad0c2f647c3fdf715fc9fd64af53aedfc43
|
||||
@ -918,7 +919,7 @@ F test/parser1.test 222b5cbf3e2e659fec1bf7d723488c8b9c94f1d0
|
||||
F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
|
||||
F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
|
||||
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
|
||||
F test/permutations.test ac3b00c299250cc087d4a527b5c75a0f8aef4e54
|
||||
F test/permutations.test 3c66f062605c63e30ce00a693e607dc2518a006a
|
||||
F test/pragma.test be7195f0aa72bdb8a512133e9640ac40f15b57a2
|
||||
F test/pragma2.test 8e72df3a16c0fda748ad52abf79cb8256b04a6fe
|
||||
F test/pragma3.test 6f849ccffeee7e496d2f2b5e74152306c0b8757c
|
||||
@ -1383,7 +1384,10 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
P d2761357a0496ec1e590c7c9e397c5b5c904f91a
|
||||
R 9921ff0c69ded6987711a9a840df906d
|
||||
U drh
|
||||
Z 612add4179403f74341dc4eb50c9c3d3
|
||||
P 24924a58197e558a9e8800cc5c91dc8fb32f3557
|
||||
R 00e6b769eaa54af1e95eef69109c890a
|
||||
T *branch * fts5-incompatible
|
||||
T *sym-fts5-incompatible *
|
||||
T -sym-trunk *
|
||||
U dan
|
||||
Z 9f2973699597cf834c49e709dc04f160
|
||||
|
@ -1 +1 @@
|
||||
24924a58197e558a9e8800cc5c91dc8fb32f3557
|
||||
a1f4c3b543eed84e808f6b901a38179786fffe16
|
@ -258,7 +258,7 @@ test_suite "fts5-light" -prefix "" -description {
|
||||
} -files [
|
||||
test_set \
|
||||
[glob -nocomplain $::testdir/../ext/fts5/test/*.test] \
|
||||
-exclude *corrupt* *fault* *big* *fts5aj*
|
||||
-exclude *corrupt*
|
||||
]
|
||||
|
||||
test_suite "nofaultsim" -prefix "" -description {
|
||||
|
Loading…
Reference in New Issue
Block a user