Merge recent enhancements from trunk.
FossilOrigin-Name: 327af5f644a49b2f41d5456958f9d61a2b704e1c
This commit is contained in:
commit
20f272c96f
@ -423,6 +423,7 @@ TESTSRC += \
|
||||
$(TOP)/ext/misc/fuzzer.c \
|
||||
$(TOP)/ext/fts5/fts5_tcl.c \
|
||||
$(TOP)/ext/fts5/fts5_test_mi.c \
|
||||
$(TOP)/ext/fts5/fts5_test_tok.c \
|
||||
$(TOP)/ext/misc/ieee754.c \
|
||||
$(TOP)/ext/misc/nextchar.c \
|
||||
$(TOP)/ext/misc/percentile.c \
|
||||
|
@ -1101,6 +1101,7 @@ TESTEXT = \
|
||||
$(TOP)\ext\misc\fuzzer.c \
|
||||
$(TOP)\ext\fts5\fts5_tcl.c \
|
||||
$(TOP)\ext\fts5\fts5_test_mi.c \
|
||||
$(TOP)\ext\fts5\fts5_test_tok.c \
|
||||
$(TOP)\ext\misc\ieee754.c \
|
||||
$(TOP)\ext\misc\nextchar.c \
|
||||
$(TOP)\ext\misc\percentile.c \
|
||||
@ -1742,8 +1743,8 @@ fts5_ext.lo: fts5.c $(HDR) $(EXTHDR)
|
||||
fts5.dll: fts5_ext.lo
|
||||
$(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /DLL /OUT:$@ fts5_ext.lo
|
||||
|
||||
sqlite3rbu.lo: $(TOP)/ext/rbu/sqlite3rbu.c $(HDR) $(EXTHDR)
|
||||
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/rbu/sqlite3rbu.c
|
||||
sqlite3rbu.lo: $(TOP)\ext\rbu\sqlite3rbu.c $(HDR) $(EXTHDR)
|
||||
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)\ext\rbu\sqlite3rbu.c
|
||||
|
||||
# Rules to build the 'testfixture' application.
|
||||
#
|
||||
@ -1872,8 +1873,8 @@ speedtest1.exe: $(TOP)\test\speedtest1.c $(SQLITE3C)
|
||||
$(TOP)\test\speedtest1.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
rbu.exe: $(TOP)\ext\rbu\rbu.c $(TOP)\ext\rbu\sqlite3rbu.c $(SQLITE3C)
|
||||
$(LTLINK) $(NO_WARN) -I. -DSQLITE_ENABLE_RBU -Fe$@ $(TOP)\ext\rbu\rbu.c $(SQLITE3C) \
|
||||
$(LDFLAGS) $(LTLINKOPTS)
|
||||
$(LTLINK) $(NO_WARN) -DSQLITE_ENABLE_RBU -Fe$@ $(TOP)\ext\rbu\rbu.c $(SQLITE3C) \
|
||||
/link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
clean:
|
||||
del /Q *.exp *.lo *.ilk *.lib *.obj *.ncb *.pdb *.sdf *.suo 2>NUL
|
||||
|
@ -225,13 +225,13 @@ int sqlite3Fts5ConfigParseRank(const char*, char**, char**);
|
||||
typedef struct Fts5Buffer Fts5Buffer;
|
||||
struct Fts5Buffer {
|
||||
u8 *p;
|
||||
int n;
|
||||
int nSpace;
|
||||
u32 n;
|
||||
u32 nSpace;
|
||||
};
|
||||
|
||||
int sqlite3Fts5BufferSize(int*, Fts5Buffer*, int);
|
||||
int sqlite3Fts5BufferSize(int*, Fts5Buffer*, u32);
|
||||
void sqlite3Fts5BufferAppendVarint(int*, Fts5Buffer*, i64);
|
||||
void sqlite3Fts5BufferAppendBlob(int*, Fts5Buffer*, int, const u8*);
|
||||
void sqlite3Fts5BufferAppendBlob(int*, Fts5Buffer*, u32, const u8*);
|
||||
void sqlite3Fts5BufferAppendString(int *, Fts5Buffer*, const char*);
|
||||
void sqlite3Fts5BufferFree(Fts5Buffer*);
|
||||
void sqlite3Fts5BufferZero(Fts5Buffer*);
|
||||
@ -586,7 +586,7 @@ int sqlite3Fts5StorageRename(Fts5Storage*, const char *zName);
|
||||
int sqlite3Fts5DropAll(Fts5Config*);
|
||||
int sqlite3Fts5CreateTable(Fts5Config*, const char*, const char*, int, char **);
|
||||
|
||||
int sqlite3Fts5StorageDelete(Fts5Storage *p, i64);
|
||||
int sqlite3Fts5StorageDelete(Fts5Storage *p, i64, sqlite3_value**);
|
||||
int sqlite3Fts5StorageContentInsert(Fts5Storage *p, sqlite3_value**, i64*);
|
||||
int sqlite3Fts5StorageIndexInsert(Fts5Storage *p, sqlite3_value**, i64);
|
||||
|
||||
@ -606,8 +606,6 @@ int sqlite3Fts5StorageConfigValue(
|
||||
Fts5Storage *p, const char*, sqlite3_value*, int
|
||||
);
|
||||
|
||||
int sqlite3Fts5StorageSpecialDelete(Fts5Storage *p, i64 iDel, sqlite3_value**);
|
||||
|
||||
int sqlite3Fts5StorageDeleteAll(Fts5Storage *p);
|
||||
int sqlite3Fts5StorageRebuild(Fts5Storage *p);
|
||||
int sqlite3Fts5StorageOptimize(Fts5Storage *p);
|
||||
|
@ -15,8 +15,8 @@
|
||||
|
||||
#include "fts5Int.h"
|
||||
|
||||
int sqlite3Fts5BufferSize(int *pRc, Fts5Buffer *pBuf, int nByte){
|
||||
int nNew = pBuf->nSpace ? pBuf->nSpace*2 : 64;
|
||||
int sqlite3Fts5BufferSize(int *pRc, Fts5Buffer *pBuf, u32 nByte){
|
||||
u32 nNew = pBuf->nSpace ? pBuf->nSpace*2 : 64;
|
||||
u8 *pNew;
|
||||
while( nNew<nByte ){
|
||||
nNew = nNew * 2;
|
||||
@ -61,10 +61,10 @@ int sqlite3Fts5Get32(const u8 *aBuf){
|
||||
void sqlite3Fts5BufferAppendBlob(
|
||||
int *pRc,
|
||||
Fts5Buffer *pBuf,
|
||||
int nData,
|
||||
u32 nData,
|
||||
const u8 *pData
|
||||
){
|
||||
assert( *pRc || nData>=0 );
|
||||
assert_nc( *pRc || nData>=0 );
|
||||
if( fts5BufferGrow(pRc, pBuf, nData) ) return;
|
||||
memcpy(&pBuf->p[pBuf->n], pData, nData);
|
||||
pBuf->n += nData;
|
||||
@ -322,14 +322,17 @@ int sqlite3Fts5TermsetAdd(
|
||||
*pbPresent = 0;
|
||||
if( p ){
|
||||
int i;
|
||||
int hash;
|
||||
int hash = 13;
|
||||
Fts5TermsetEntry *pEntry;
|
||||
|
||||
/* Calculate a hash value for this term */
|
||||
hash = 104 + iIdx;
|
||||
for(i=0; i<nTerm; i++){
|
||||
hash += (hash << 3) + (int)pTerm[i];
|
||||
/* Calculate a hash value for this term. This is the same hash checksum
|
||||
** used by the fts5_hash.c module. This is not important for correct
|
||||
** operation of the module, but is necessary to ensure that some tests
|
||||
** designed to produce hash table collisions really do work. */
|
||||
for(i=nTerm-1; i>=0; i--){
|
||||
hash = (hash << 3) ^ hash ^ pTerm[i];
|
||||
}
|
||||
hash = (hash << 3) ^ hash ^ iIdx;
|
||||
hash = hash % ArraySize(p->apHash);
|
||||
|
||||
for(pEntry=p->apHash[hash]; pEntry; pEntry=pEntry->pNext){
|
||||
|
@ -278,7 +278,7 @@ static int fts5ConfigParseSpecial(
|
||||
p++;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && (nPre<=0 || nPre>=1000) ){
|
||||
if( nPre<=0 || nPre>=1000 ){
|
||||
*pzErr = sqlite3_mprintf("prefix length out of range (max 999)");
|
||||
rc = SQLITE_ERROR;
|
||||
break;
|
||||
|
@ -834,7 +834,7 @@ static int fts5ExprTokenTest(
|
||||
assert( pPhrase->aTerm[0].pSynonym==0 );
|
||||
|
||||
rc = sqlite3Fts5IterPoslist(pIter, pColset,
|
||||
(const u8**)&pPhrase->poslist.p, &pPhrase->poslist.n, &pNode->iRowid
|
||||
(const u8**)&pPhrase->poslist.p, (int*)&pPhrase->poslist.n, &pNode->iRowid
|
||||
);
|
||||
pNode->bNomatch = (pPhrase->poslist.n==0);
|
||||
return rc;
|
||||
@ -2396,7 +2396,7 @@ int sqlite3Fts5ExprPopulatePoslists(
|
||||
}
|
||||
|
||||
return sqlite3Fts5Tokenize(pConfig,
|
||||
FTS5_TOKENIZE_AUX, z, n, (void*)&sCtx, fts5ExprPopulatePoslistsCb
|
||||
FTS5_TOKENIZE_DOCUMENT, z, n, (void*)&sCtx, fts5ExprPopulatePoslistsCb
|
||||
);
|
||||
}
|
||||
|
||||
@ -2412,49 +2412,44 @@ static void fts5ExprClearPoslists(Fts5ExprNode *pNode){
|
||||
}
|
||||
|
||||
static int fts5ExprCheckPoslists(Fts5ExprNode *pNode, i64 iRowid){
|
||||
if( pNode ){
|
||||
pNode->iRowid = iRowid;
|
||||
pNode->bEof = 0;
|
||||
switch( pNode->eType ){
|
||||
case FTS5_TERM:
|
||||
case FTS5_STRING:
|
||||
return (pNode->pNear->apPhrase[0]->poslist.n>0);
|
||||
pNode->iRowid = iRowid;
|
||||
pNode->bEof = 0;
|
||||
switch( pNode->eType ){
|
||||
case FTS5_TERM:
|
||||
case FTS5_STRING:
|
||||
return (pNode->pNear->apPhrase[0]->poslist.n>0);
|
||||
|
||||
case FTS5_AND: {
|
||||
int i;
|
||||
for(i=0; i<pNode->nChild; i++){
|
||||
if( fts5ExprCheckPoslists(pNode->apChild[i], iRowid)==0 ){
|
||||
fts5ExprClearPoslists(pNode);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case FTS5_OR: {
|
||||
int i;
|
||||
int bRet = 0;
|
||||
for(i=0; i<pNode->nChild; i++){
|
||||
if( fts5ExprCheckPoslists(pNode->apChild[i], iRowid) ){
|
||||
bRet = 1;
|
||||
}
|
||||
}
|
||||
if( bRet==0 ){
|
||||
fts5ExprClearPoslists(pNode);
|
||||
}
|
||||
return bRet;
|
||||
}
|
||||
|
||||
default: {
|
||||
assert( pNode->eType==FTS5_NOT );
|
||||
if( 0==fts5ExprCheckPoslists(pNode->apChild[0], iRowid)
|
||||
|| 0!=fts5ExprCheckPoslists(pNode->apChild[1], iRowid)
|
||||
){
|
||||
case FTS5_AND: {
|
||||
int i;
|
||||
for(i=0; i<pNode->nChild; i++){
|
||||
if( fts5ExprCheckPoslists(pNode->apChild[i], iRowid)==0 ){
|
||||
fts5ExprClearPoslists(pNode);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case FTS5_OR: {
|
||||
int i;
|
||||
int bRet = 0;
|
||||
for(i=0; i<pNode->nChild; i++){
|
||||
if( fts5ExprCheckPoslists(pNode->apChild[i], iRowid) ){
|
||||
bRet = 1;
|
||||
}
|
||||
}
|
||||
return bRet;
|
||||
}
|
||||
|
||||
default: {
|
||||
assert( pNode->eType==FTS5_NOT );
|
||||
if( 0==fts5ExprCheckPoslists(pNode->apChild[0], iRowid)
|
||||
|| 0!=fts5ExprCheckPoslists(pNode->apChild[1], iRowid)
|
||||
){
|
||||
fts5ExprClearPoslists(pNode);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
|
@ -1875,13 +1875,15 @@ static void fts5SegIterNext(
|
||||
int iOff;
|
||||
int bNewTerm = 0;
|
||||
int nKeep = 0;
|
||||
u8 *a;
|
||||
int n;
|
||||
|
||||
assert( pbNewTerm==0 || *pbNewTerm==0 );
|
||||
assert( p->pConfig->eDetail!=FTS5_DETAIL_NONE );
|
||||
|
||||
/* Search for the end of the position list within the current page. */
|
||||
u8 *a = pLeaf->p;
|
||||
int n = pLeaf->szLeaf;
|
||||
a = pLeaf->p;
|
||||
n = pLeaf->szLeaf;
|
||||
|
||||
ASSERT_SZLEAF_OK(pLeaf);
|
||||
iOff = pIter->iLeafOffset + pIter->nPos;
|
||||
@ -5902,6 +5904,47 @@ static int fts5DecodeDoclist(int *pRc, Fts5Buffer *pBuf, const u8 *a, int n){
|
||||
return iOff;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is part of the fts5_decode() debugging function. It is
|
||||
** only ever used with detail=none tables.
|
||||
**
|
||||
** Buffer (pData/nData) contains a doclist in the format used by detail=none
|
||||
** tables. This function appends a human-readable version of that list to
|
||||
** buffer pBuf.
|
||||
**
|
||||
** If *pRc is other than SQLITE_OK when this function is called, it is a
|
||||
** no-op. If an OOM or other error occurs within this function, *pRc is
|
||||
** set to an SQLite error code before returning. The final state of buffer
|
||||
** pBuf is undefined in this case.
|
||||
*/
|
||||
static void fts5DecodeRowidList(
|
||||
int *pRc, /* IN/OUT: Error code */
|
||||
Fts5Buffer *pBuf, /* Buffer to append text to */
|
||||
const u8 *pData, int nData /* Data to decode list-of-rowids from */
|
||||
){
|
||||
int i = 0;
|
||||
i64 iRowid = 0;
|
||||
|
||||
while( i<nData ){
|
||||
const char *zApp = "";
|
||||
u64 iVal;
|
||||
i += sqlite3Fts5GetVarint(&pData[i], &iVal);
|
||||
iRowid += iVal;
|
||||
|
||||
if( i<nData && pData[i]==0x00 ){
|
||||
i++;
|
||||
if( i<nData && pData[i]==0x00 ){
|
||||
i++;
|
||||
zApp = "+";
|
||||
}else{
|
||||
zApp = "*";
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " %lld%s", iRowid, zApp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** The implementation of user-defined scalar function fts5_decode().
|
||||
*/
|
||||
@ -5917,6 +5960,7 @@ static void fts5DecodeFunction(
|
||||
Fts5Buffer s; /* Build up text to return here */
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
int nSpace = 0;
|
||||
int eDetailNone = (sqlite3_user_data(pCtx)!=0);
|
||||
|
||||
assert( nArg==2 );
|
||||
memset(&s, 0, sizeof(Fts5Buffer));
|
||||
@ -5958,6 +6002,54 @@ static void fts5DecodeFunction(
|
||||
}else{
|
||||
fts5DecodeStructure(&rc, &s, a, n);
|
||||
}
|
||||
}else if( eDetailNone ){
|
||||
Fts5Buffer term; /* Current term read from page */
|
||||
int szLeaf;
|
||||
int iPgidxOff = szLeaf = fts5GetU16(&a[2]);
|
||||
int iTermOff;
|
||||
int nKeep = 0;
|
||||
int iOff;
|
||||
|
||||
memset(&term, 0, sizeof(Fts5Buffer));
|
||||
|
||||
/* Decode any entries that occur before the first term. */
|
||||
if( szLeaf<n ){
|
||||
iPgidxOff += fts5GetVarint32(&a[iPgidxOff], iTermOff);
|
||||
}else{
|
||||
iTermOff = szLeaf;
|
||||
}
|
||||
fts5DecodeRowidList(&rc, &s, &a[4], iTermOff-4);
|
||||
|
||||
iOff = iTermOff;
|
||||
while( iOff<szLeaf ){
|
||||
int nAppend;
|
||||
|
||||
/* Read the term data for the next term*/
|
||||
iOff += fts5GetVarint32(&a[iOff], nAppend);
|
||||
term.n = nKeep;
|
||||
fts5BufferAppendBlob(&rc, &term, nAppend, &a[iOff]);
|
||||
sqlite3Fts5BufferAppendPrintf(
|
||||
&rc, &s, " term=%.*s", term.n, (const char*)term.p
|
||||
);
|
||||
iOff += nAppend;
|
||||
|
||||
/* Figure out where the doclist for this term ends */
|
||||
if( iPgidxOff<n ){
|
||||
int nIncr;
|
||||
iPgidxOff += fts5GetVarint32(&a[iPgidxOff], nIncr);
|
||||
iTermOff += nIncr;
|
||||
}else{
|
||||
iTermOff = szLeaf;
|
||||
}
|
||||
|
||||
fts5DecodeRowidList(&rc, &s, &a[iOff], iTermOff-iOff);
|
||||
iOff = iTermOff;
|
||||
if( iOff<szLeaf ){
|
||||
iOff += fts5GetVarint32(&a[iOff], nKeep);
|
||||
}
|
||||
}
|
||||
|
||||
fts5BufferFree(&term);
|
||||
}else{
|
||||
Fts5Buffer term; /* Current term read from page */
|
||||
int szLeaf; /* Offset of pgidx in a[] */
|
||||
@ -6085,6 +6177,14 @@ int sqlite3Fts5IndexInit(sqlite3 *db){
|
||||
int rc = sqlite3_create_function(
|
||||
db, "fts5_decode", 2, SQLITE_UTF8, 0, fts5DecodeFunction, 0, 0
|
||||
);
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(
|
||||
db, "fts5_decode_none", 2,
|
||||
SQLITE_UTF8, (void*)db, fts5DecodeFunction, 0, 0
|
||||
);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(
|
||||
db, "fts5_rowid", -1, SQLITE_UTF8, 0, fts5RowidFunction, 0, 0
|
||||
|
@ -844,33 +844,32 @@ static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){
|
||||
}
|
||||
|
||||
|
||||
static sqlite3_stmt *fts5PrepareStatement(
|
||||
int *pRc,
|
||||
static int fts5PrepareStatement(
|
||||
sqlite3_stmt **ppStmt,
|
||||
Fts5Config *pConfig,
|
||||
const char *zFmt,
|
||||
...
|
||||
){
|
||||
sqlite3_stmt *pRet = 0;
|
||||
int rc;
|
||||
char *zSql;
|
||||
va_list ap;
|
||||
va_start(ap, zFmt);
|
||||
|
||||
if( *pRc==SQLITE_OK ){
|
||||
int rc;
|
||||
char *zSql = sqlite3_vmprintf(zFmt, ap);
|
||||
if( zSql==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &pRet, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
*pConfig->pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(pConfig->db));
|
||||
}
|
||||
sqlite3_free(zSql);
|
||||
va_start(ap, zFmt);
|
||||
zSql = sqlite3_vmprintf(zFmt, ap);
|
||||
if( zSql==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &pRet, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
*pConfig->pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(pConfig->db));
|
||||
}
|
||||
*pRc = rc;
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
|
||||
va_end(ap);
|
||||
return pRet;
|
||||
*ppStmt = pRet;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int fts5CursorFirstSorted(Fts5Table *pTab, Fts5Cursor *pCsr, int bDesc){
|
||||
@ -878,7 +877,7 @@ static int fts5CursorFirstSorted(Fts5Table *pTab, Fts5Cursor *pCsr, int bDesc){
|
||||
Fts5Sorter *pSorter;
|
||||
int nPhrase;
|
||||
int nByte;
|
||||
int rc = SQLITE_OK;
|
||||
int rc;
|
||||
const char *zRank = pCsr->zRank;
|
||||
const char *zRankArgs = pCsr->zRankArgs;
|
||||
|
||||
@ -896,7 +895,7 @@ static int fts5CursorFirstSorted(Fts5Table *pTab, Fts5Cursor *pCsr, int bDesc){
|
||||
** table, saving it creates a circular reference.
|
||||
**
|
||||
** If SQLite a built-in statement cache, this wouldn't be a problem. */
|
||||
pSorter->pStmt = fts5PrepareStatement(&rc, pConfig,
|
||||
rc = fts5PrepareStatement(&pSorter->pStmt, pConfig,
|
||||
"SELECT rowid, rank FROM %Q.%Q ORDER BY %s(%s%s%s) %s",
|
||||
pConfig->zDb, pConfig->zName, zRank, pConfig->zName,
|
||||
(zRankArgs ? ", " : ""),
|
||||
@ -1405,7 +1404,7 @@ static int fts5SpecialDelete(
|
||||
int eType1 = sqlite3_value_type(apVal[1]);
|
||||
if( eType1==SQLITE_INTEGER ){
|
||||
sqlite3_int64 iDel = sqlite3_value_int64(apVal[1]);
|
||||
rc = sqlite3Fts5StorageSpecialDelete(pTab->pStorage, iDel, &apVal[2]);
|
||||
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, &apVal[2]);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@ -1512,7 +1511,7 @@ static int fts5UpdateMethod(
|
||||
/* Case 1: DELETE */
|
||||
else if( nArg==1 ){
|
||||
i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */
|
||||
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel);
|
||||
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0);
|
||||
}
|
||||
|
||||
/* Case 2: INSERT */
|
||||
@ -1522,7 +1521,7 @@ static int fts5UpdateMethod(
|
||||
&& sqlite3_value_type(apVal[1])==SQLITE_INTEGER
|
||||
){
|
||||
i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */
|
||||
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew);
|
||||
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0);
|
||||
}
|
||||
fts5StorageInsert(&rc, pTab, apVal, pRowid);
|
||||
}
|
||||
@ -1533,22 +1532,22 @@ static int fts5UpdateMethod(
|
||||
i64 iNew = sqlite3_value_int64(apVal[1]); /* New rowid */
|
||||
if( iOld!=iNew ){
|
||||
if( eConflict==SQLITE_REPLACE ){
|
||||
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld);
|
||||
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew);
|
||||
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0);
|
||||
}
|
||||
fts5StorageInsert(&rc, pTab, apVal, pRowid);
|
||||
}else{
|
||||
rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, pRowid);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld);
|
||||
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal, *pRowid);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld);
|
||||
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0);
|
||||
fts5StorageInsert(&rc, pTab, apVal, pRowid);
|
||||
}
|
||||
}
|
||||
@ -1747,7 +1746,9 @@ static int fts5CacheInstArray(Fts5Cursor *pCsr){
|
||||
const u8 *a;
|
||||
int n;
|
||||
rc = fts5CsrPoslist(pCsr, i, &a, &n);
|
||||
sqlite3Fts5PoslistReaderInit(a, n, &aIter[i]);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3Fts5PoslistReaderInit(a, n, &aIter[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
@ -2037,8 +2038,15 @@ static int fts5ApiPhraseFirstColumn(
|
||||
Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig;
|
||||
|
||||
if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
|
||||
Fts5Sorter *pSorter = pCsr->pSorter;
|
||||
int n;
|
||||
rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, iPhrase, &pIter->a, &n);
|
||||
if( pSorter ){
|
||||
int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]);
|
||||
n = pSorter->aIdx[iPhrase] - i1;
|
||||
pIter->a = &pSorter->aPoslist[i1];
|
||||
}else{
|
||||
rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, iPhrase, &pIter->a, &n);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
pIter->b = &pIter->a[n];
|
||||
*piCol = 0;
|
||||
|
@ -378,39 +378,52 @@ static int fts5StorageInsertCallback(
|
||||
** delete-markers to the FTS index necessary to delete it. Do not actually
|
||||
** remove the %_content row at this time though.
|
||||
*/
|
||||
static int fts5StorageDeleteFromIndex(Fts5Storage *p, i64 iDel){
|
||||
static int fts5StorageDeleteFromIndex(
|
||||
Fts5Storage *p,
|
||||
i64 iDel,
|
||||
sqlite3_value **apVal
|
||||
){
|
||||
Fts5Config *pConfig = p->pConfig;
|
||||
sqlite3_stmt *pSeek; /* SELECT to read row iDel from %_data */
|
||||
sqlite3_stmt *pSeek = 0; /* SELECT to read row iDel from %_data */
|
||||
int rc; /* Return code */
|
||||
int rc2; /* sqlite3_reset() return code */
|
||||
int iCol;
|
||||
Fts5InsertCtx ctx;
|
||||
|
||||
rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP, &pSeek, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
int rc2;
|
||||
if( apVal==0 ){
|
||||
rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP, &pSeek, 0);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
sqlite3_bind_int64(pSeek, 1, iDel);
|
||||
if( sqlite3_step(pSeek)==SQLITE_ROW ){
|
||||
int iCol;
|
||||
Fts5InsertCtx ctx;
|
||||
ctx.pStorage = p;
|
||||
ctx.iCol = -1;
|
||||
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel);
|
||||
for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){
|
||||
if( pConfig->abUnindexed[iCol-1] ) continue;
|
||||
ctx.szCol = 0;
|
||||
rc = sqlite3Fts5Tokenize(pConfig,
|
||||
FTS5_TOKENIZE_DOCUMENT,
|
||||
(const char*)sqlite3_column_text(pSeek, iCol),
|
||||
sqlite3_column_bytes(pSeek, iCol),
|
||||
(void*)&ctx,
|
||||
fts5StorageInsertCallback
|
||||
);
|
||||
p->aTotalSize[iCol-1] -= (i64)ctx.szCol;
|
||||
}
|
||||
p->nTotalRow--;
|
||||
if( sqlite3_step(pSeek)!=SQLITE_ROW ){
|
||||
return sqlite3_reset(pSeek);
|
||||
}
|
||||
rc2 = sqlite3_reset(pSeek);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
}
|
||||
|
||||
ctx.pStorage = p;
|
||||
ctx.iCol = -1;
|
||||
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel);
|
||||
for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){
|
||||
if( pConfig->abUnindexed[iCol-1]==0 ){
|
||||
const char *zText;
|
||||
int nText;
|
||||
if( pSeek ){
|
||||
zText = (const char*)sqlite3_column_text(pSeek, iCol);
|
||||
nText = sqlite3_column_bytes(pSeek, iCol);
|
||||
}else{
|
||||
zText = (const char*)sqlite3_value_text(apVal[iCol-1]);
|
||||
nText = sqlite3_value_bytes(apVal[iCol-1]);
|
||||
}
|
||||
ctx.szCol = 0;
|
||||
rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT,
|
||||
zText, nText, (void*)&ctx, fts5StorageInsertCallback
|
||||
);
|
||||
p->aTotalSize[iCol-1] -= (i64)ctx.szCol;
|
||||
}
|
||||
}
|
||||
p->nTotalRow--;
|
||||
|
||||
rc2 = sqlite3_reset(pSeek);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -490,16 +503,17 @@ static int fts5StorageSaveTotals(Fts5Storage *p){
|
||||
/*
|
||||
** Remove a row from the FTS table.
|
||||
*/
|
||||
int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel){
|
||||
int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel, sqlite3_value **apVal){
|
||||
Fts5Config *pConfig = p->pConfig;
|
||||
int rc;
|
||||
sqlite3_stmt *pDel = 0;
|
||||
|
||||
assert( pConfig->eContent!=FTS5_CONTENT_NORMAL || apVal==0 );
|
||||
rc = fts5StorageLoadTotals(p, 1);
|
||||
|
||||
/* Delete the index records */
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts5StorageDeleteFromIndex(p, iDel);
|
||||
rc = fts5StorageDeleteFromIndex(p, iDel, apVal);
|
||||
}
|
||||
|
||||
/* Delete the %_docsize record */
|
||||
@ -532,61 +546,6 @@ int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel){
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sqlite3Fts5StorageSpecialDelete(
|
||||
Fts5Storage *p,
|
||||
i64 iDel,
|
||||
sqlite3_value **apVal
|
||||
){
|
||||
Fts5Config *pConfig = p->pConfig;
|
||||
int rc;
|
||||
sqlite3_stmt *pDel = 0;
|
||||
|
||||
assert( pConfig->eContent!=FTS5_CONTENT_NORMAL );
|
||||
rc = fts5StorageLoadTotals(p, 1);
|
||||
|
||||
/* Delete the index records */
|
||||
if( rc==SQLITE_OK ){
|
||||
int iCol;
|
||||
Fts5InsertCtx ctx;
|
||||
ctx.pStorage = p;
|
||||
ctx.iCol = -1;
|
||||
|
||||
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel);
|
||||
for(iCol=0; rc==SQLITE_OK && iCol<pConfig->nCol; iCol++){
|
||||
if( pConfig->abUnindexed[iCol] ) continue;
|
||||
ctx.szCol = 0;
|
||||
rc = sqlite3Fts5Tokenize(pConfig,
|
||||
FTS5_TOKENIZE_DOCUMENT,
|
||||
(const char*)sqlite3_value_text(apVal[iCol]),
|
||||
sqlite3_value_bytes(apVal[iCol]),
|
||||
(void*)&ctx,
|
||||
fts5StorageInsertCallback
|
||||
);
|
||||
p->aTotalSize[iCol] -= (i64)ctx.szCol;
|
||||
}
|
||||
p->nTotalRow--;
|
||||
}
|
||||
|
||||
/* Delete the %_docsize record */
|
||||
if( pConfig->bColumnsize ){
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_DOCSIZE, &pDel, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_int64(pDel, 1, iDel);
|
||||
sqlite3_step(pDel);
|
||||
rc = sqlite3_reset(pDel);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the averages record */
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts5StorageSaveTotals(p);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Delete all entries in the FTS5 index.
|
||||
*/
|
||||
|
@ -23,7 +23,8 @@
|
||||
#include <assert.h>
|
||||
|
||||
extern int sqlite3_fts5_may_be_corrupt;
|
||||
extern int sqlite3Fts5TestRegisterMatchinfo(sqlite3 *);
|
||||
extern int sqlite3Fts5TestRegisterMatchinfo(sqlite3*);
|
||||
extern int sqlite3Fts5TestRegisterTok(sqlite3*, fts5_api*);
|
||||
|
||||
/*************************************************************************
|
||||
** This is a copy of the first part of the SqliteDb structure in
|
||||
@ -446,10 +447,12 @@ static int xF5tApi(
|
||||
zColvar = Tcl_GetString(objv[3]);
|
||||
zOffvar = Tcl_GetString(objv[4]);
|
||||
|
||||
for(p->pApi->xPhraseFirst(p->pFts, iPhrase, &iter, &iCol, &iOff);
|
||||
iCol>=0;
|
||||
p->pApi->xPhraseNext(p->pFts, &iter, &iCol, &iOff)
|
||||
){
|
||||
rc = p->pApi->xPhraseFirst(p->pFts, iPhrase, &iter, &iCol, &iOff);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
for( ;iCol>=0; p->pApi->xPhraseNext(p->pFts, &iter, &iCol, &iOff) ){
|
||||
Tcl_SetVar2Ex(interp, zColvar, 0, Tcl_NewIntObj(iCol), 0);
|
||||
Tcl_SetVar2Ex(interp, zOffvar, 0, Tcl_NewIntObj(iOff), 0);
|
||||
rc = Tcl_EvalObjEx(interp, pScript, 0);
|
||||
@ -473,10 +476,12 @@ static int xF5tApi(
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &iPhrase) ) return TCL_ERROR;
|
||||
zColvar = Tcl_GetString(objv[3]);
|
||||
|
||||
for(p->pApi->xPhraseFirstColumn(p->pFts, iPhrase, &iter, &iCol);
|
||||
iCol>=0;
|
||||
p->pApi->xPhraseNextColumn(p->pFts, &iter, &iCol)
|
||||
){
|
||||
rc = p->pApi->xPhraseFirstColumn(p->pFts, iPhrase, &iter, &iCol);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
for( ; iCol>=0; p->pApi->xPhraseNextColumn(p->pFts, &iter, &iCol)){
|
||||
Tcl_SetVar2Ex(interp, zColvar, 0, Tcl_NewIntObj(iCol), 0);
|
||||
rc = Tcl_EvalObjEx(interp, pScript, 0);
|
||||
if( rc==TCL_CONTINUE ) rc = TCL_OK;
|
||||
@ -1078,6 +1083,32 @@ static int f5tRegisterMatchinfo(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
static int f5tRegisterTok(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc;
|
||||
sqlite3 *db = 0;
|
||||
fts5_api *pApi = 0;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( f5tDbAndApi(interp, objv[1], &db, &pApi) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
rc = sqlite3Fts5TestRegisterTok(db, pApi);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_SetResult(interp, (char*)sqlite3ErrName(rc), TCL_VOLATILE);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Entry point.
|
||||
*/
|
||||
@ -1093,7 +1124,8 @@ int Fts5tcl_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_fts5_create_function", f5tCreateFunction, 0 },
|
||||
{ "sqlite3_fts5_may_be_corrupt", f5tMayBeCorrupt, 0 },
|
||||
{ "sqlite3_fts5_token_hash", f5tTokenHash, 0 },
|
||||
{ "sqlite3_fts5_register_matchinfo", f5tRegisterMatchinfo, 0 }
|
||||
{ "sqlite3_fts5_register_matchinfo", f5tRegisterMatchinfo, 0 },
|
||||
{ "sqlite3_fts5_register_fts5tokenize", f5tRegisterTok, 0 }
|
||||
};
|
||||
int i;
|
||||
F5tTokenizerContext *pContext;
|
||||
|
482
ext/fts5/fts5_test_tok.c
Normal file
482
ext/fts5/fts5_test_tok.c
Normal file
@ -0,0 +1,482 @@
|
||||
/*
|
||||
** 2013 Apr 22
|
||||
**
|
||||
** 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 contains code for the "fts5tokenize" virtual table module.
|
||||
** An fts5tokenize virtual table is created as follows:
|
||||
**
|
||||
** CREATE VIRTUAL TABLE <tbl> USING fts5tokenize(
|
||||
** <tokenizer-name>, <arg-1>, ...
|
||||
** );
|
||||
**
|
||||
** The table created has the following schema:
|
||||
**
|
||||
** CREATE TABLE <tbl>(input HIDDEN, token, start, end, position)
|
||||
**
|
||||
** When queried, the query must include a WHERE clause of type:
|
||||
**
|
||||
** input = <string>
|
||||
**
|
||||
** The virtual table module tokenizes this <string>, using the FTS3
|
||||
** tokenizer specified by the arguments to the CREATE VIRTUAL TABLE
|
||||
** statement and returns one row for each token in the result. With
|
||||
** fields set as follows:
|
||||
**
|
||||
** input: Always set to a copy of <string>
|
||||
** token: A token from the input.
|
||||
** start: Byte offset of the token within the input <string>.
|
||||
** end: Byte offset of the byte immediately following the end of the
|
||||
** token within the input string.
|
||||
** pos: Token offset of token within input.
|
||||
**
|
||||
*/
|
||||
#if defined(SQLITE_TEST) && defined(SQLITE_ENABLE_FTS5)
|
||||
|
||||
#include <fts5.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
typedef struct Fts5tokTable Fts5tokTable;
|
||||
typedef struct Fts5tokCursor Fts5tokCursor;
|
||||
typedef struct Fts5tokRow Fts5tokRow;
|
||||
|
||||
/*
|
||||
** Virtual table structure.
|
||||
*/
|
||||
struct Fts5tokTable {
|
||||
sqlite3_vtab base; /* Base class used by SQLite core */
|
||||
fts5_tokenizer tok; /* Tokenizer functions */
|
||||
Fts5Tokenizer *pTok; /* Tokenizer instance */
|
||||
};
|
||||
|
||||
/*
|
||||
** A container for a rows values.
|
||||
*/
|
||||
struct Fts5tokRow {
|
||||
char *zToken;
|
||||
int iStart;
|
||||
int iEnd;
|
||||
int iPos;
|
||||
};
|
||||
|
||||
/*
|
||||
** Virtual table cursor structure.
|
||||
*/
|
||||
struct Fts5tokCursor {
|
||||
sqlite3_vtab_cursor base; /* Base class used by SQLite core */
|
||||
int iRowid; /* Current 'rowid' value */
|
||||
char *zInput; /* Input string */
|
||||
int nRow; /* Number of entries in aRow[] */
|
||||
Fts5tokRow *aRow; /* Array of rows to return */
|
||||
};
|
||||
|
||||
static void fts5tokDequote(char *z){
|
||||
char q = z[0];
|
||||
|
||||
if( q=='[' || q=='\'' || q=='"' || q=='`' ){
|
||||
int iIn = 1;
|
||||
int iOut = 0;
|
||||
if( q=='[' ) q = ']';
|
||||
|
||||
while( z[iIn] ){
|
||||
if( z[iIn]==q ){
|
||||
if( z[iIn+1]!=q ){
|
||||
/* Character iIn was the close quote. */
|
||||
iIn++;
|
||||
break;
|
||||
}else{
|
||||
/* Character iIn and iIn+1 form an escaped quote character. Skip
|
||||
** the input cursor past both and copy a single quote character
|
||||
** to the output buffer. */
|
||||
iIn += 2;
|
||||
z[iOut++] = q;
|
||||
}
|
||||
}else{
|
||||
z[iOut++] = z[iIn++];
|
||||
}
|
||||
}
|
||||
|
||||
z[iOut] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** The second argument, argv[], is an array of pointers to nul-terminated
|
||||
** strings. This function makes a copy of the array and strings into a
|
||||
** single block of memory. It then dequotes any of the strings that appear
|
||||
** to be quoted.
|
||||
**
|
||||
** If successful, output parameter *pazDequote is set to point at the
|
||||
** array of dequoted strings and SQLITE_OK is returned. The caller is
|
||||
** responsible for eventually calling sqlite3_free() to free the array
|
||||
** in this case. Or, if an error occurs, an SQLite error code is returned.
|
||||
** The final value of *pazDequote is undefined in this case.
|
||||
*/
|
||||
static int fts5tokDequoteArray(
|
||||
int argc, /* Number of elements in argv[] */
|
||||
const char * const *argv, /* Input array */
|
||||
char ***pazDequote /* Output array */
|
||||
){
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
if( argc==0 ){
|
||||
*pazDequote = 0;
|
||||
}else{
|
||||
int i;
|
||||
int nByte = 0;
|
||||
char **azDequote;
|
||||
|
||||
for(i=0; i<argc; i++){
|
||||
nByte += (int)(strlen(argv[i]) + 1);
|
||||
}
|
||||
|
||||
*pazDequote = azDequote = sqlite3_malloc(sizeof(char *)*argc + nByte);
|
||||
if( azDequote==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
char *pSpace = (char *)&azDequote[argc];
|
||||
for(i=0; i<argc; i++){
|
||||
int n = (int)strlen(argv[i]);
|
||||
azDequote[i] = pSpace;
|
||||
memcpy(pSpace, argv[i], n+1);
|
||||
fts5tokDequote(pSpace);
|
||||
pSpace += (n+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Schema of the tokenizer table.
|
||||
*/
|
||||
#define FTS3_TOK_SCHEMA "CREATE TABLE x(input HIDDEN, token, start, end, position)"
|
||||
|
||||
/*
|
||||
** This function does all the work for both the xConnect and xCreate methods.
|
||||
** These tables have no persistent representation of their own, so xConnect
|
||||
** and xCreate are identical operations.
|
||||
**
|
||||
** argv[0]: module name
|
||||
** argv[1]: database name
|
||||
** argv[2]: table name
|
||||
** argv[3]: first argument (tokenizer name)
|
||||
*/
|
||||
static int fts5tokConnectMethod(
|
||||
sqlite3 *db, /* Database connection */
|
||||
void *pCtx, /* Pointer to fts5_api object */
|
||||
int argc, /* Number of elements in argv array */
|
||||
const char * const *argv, /* xCreate/xConnect argument array */
|
||||
sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */
|
||||
char **pzErr /* OUT: sqlite3_malloc'd error message */
|
||||
){
|
||||
fts5_api *pApi = (fts5_api*)pCtx;
|
||||
Fts5tokTable *pTab = 0;
|
||||
int rc;
|
||||
char **azDequote = 0;
|
||||
int nDequote;
|
||||
|
||||
rc = sqlite3_declare_vtab(db,
|
||||
"CREATE TABLE x(input HIDDEN, token, start, end, position)"
|
||||
);
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
nDequote = argc-3;
|
||||
rc = fts5tokDequoteArray(nDequote, &argv[3], &azDequote);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
pTab = (Fts5tokTable*)sqlite3_malloc(sizeof(Fts5tokTable));
|
||||
if( pTab==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
memset(pTab, 0, sizeof(Fts5tokTable));
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
void *pTokCtx = 0;
|
||||
const char *zModule = 0;
|
||||
if( nDequote>0 ){
|
||||
zModule = azDequote[0];
|
||||
}
|
||||
|
||||
rc = pApi->xFindTokenizer(pApi, zModule, &pTokCtx, &pTab->tok);
|
||||
if( rc==SQLITE_OK ){
|
||||
const char **azArg = (const char **)&azDequote[1];
|
||||
int nArg = nDequote>0 ? nDequote-1 : 0;
|
||||
rc = pTab->tok.xCreate(pTokCtx, azArg, nArg, &pTab->pTok);
|
||||
}
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3_free(pTab);
|
||||
pTab = 0;
|
||||
}
|
||||
|
||||
*ppVtab = (sqlite3_vtab*)pTab;
|
||||
sqlite3_free(azDequote);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function does the work for both the xDisconnect and xDestroy methods.
|
||||
** These tables have no persistent representation of their own, so xDisconnect
|
||||
** and xDestroy are identical operations.
|
||||
*/
|
||||
static int fts5tokDisconnectMethod(sqlite3_vtab *pVtab){
|
||||
Fts5tokTable *pTab = (Fts5tokTable *)pVtab;
|
||||
if( pTab->pTok ){
|
||||
pTab->tok.xDelete(pTab->pTok);
|
||||
}
|
||||
sqlite3_free(pTab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** xBestIndex - Analyze a WHERE and ORDER BY clause.
|
||||
*/
|
||||
static int fts5tokBestIndexMethod(
|
||||
sqlite3_vtab *pVTab,
|
||||
sqlite3_index_info *pInfo
|
||||
){
|
||||
int i;
|
||||
|
||||
for(i=0; i<pInfo->nConstraint; i++){
|
||||
if( pInfo->aConstraint[i].usable
|
||||
&& pInfo->aConstraint[i].iColumn==0
|
||||
&& pInfo->aConstraint[i].op==SQLITE_INDEX_CONSTRAINT_EQ
|
||||
){
|
||||
pInfo->idxNum = 1;
|
||||
pInfo->aConstraintUsage[i].argvIndex = 1;
|
||||
pInfo->aConstraintUsage[i].omit = 1;
|
||||
pInfo->estimatedCost = 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
pInfo->idxNum = 0;
|
||||
assert( pInfo->estimatedCost>1000000.0 );
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** xOpen - Open a cursor.
|
||||
*/
|
||||
static int fts5tokOpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){
|
||||
Fts5tokCursor *pCsr;
|
||||
|
||||
pCsr = (Fts5tokCursor *)sqlite3_malloc(sizeof(Fts5tokCursor));
|
||||
if( pCsr==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
memset(pCsr, 0, sizeof(Fts5tokCursor));
|
||||
|
||||
*ppCsr = (sqlite3_vtab_cursor *)pCsr;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Reset the tokenizer cursor passed as the only argument. As if it had
|
||||
** just been returned by fts5tokOpenMethod().
|
||||
*/
|
||||
static void fts5tokResetCursor(Fts5tokCursor *pCsr){
|
||||
int i;
|
||||
for(i=0; i<pCsr->nRow; i++){
|
||||
sqlite3_free(pCsr->aRow[i].zToken);
|
||||
}
|
||||
sqlite3_free(pCsr->zInput);
|
||||
sqlite3_free(pCsr->aRow);
|
||||
pCsr->zInput = 0;
|
||||
pCsr->aRow = 0;
|
||||
pCsr->nRow = 0;
|
||||
pCsr->iRowid = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** xClose - Close a cursor.
|
||||
*/
|
||||
static int fts5tokCloseMethod(sqlite3_vtab_cursor *pCursor){
|
||||
Fts5tokCursor *pCsr = (Fts5tokCursor *)pCursor;
|
||||
fts5tokResetCursor(pCsr);
|
||||
sqlite3_free(pCsr);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** xNext - Advance the cursor to the next row, if any.
|
||||
*/
|
||||
static int fts5tokNextMethod(sqlite3_vtab_cursor *pCursor){
|
||||
Fts5tokCursor *pCsr = (Fts5tokCursor *)pCursor;
|
||||
pCsr->iRowid++;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int fts5tokCb(
|
||||
void *pCtx, /* Pointer to Fts5tokCursor */
|
||||
int tflags, /* Mask of FTS5_TOKEN_* flags */
|
||||
const char *pToken, /* Pointer to buffer containing token */
|
||||
int nToken, /* Size of token in bytes */
|
||||
int iStart, /* Byte offset of token within input text */
|
||||
int iEnd /* Byte offset of end of token within input text */
|
||||
){
|
||||
Fts5tokCursor *pCsr = (Fts5tokCursor*)pCtx;
|
||||
Fts5tokRow *pRow;
|
||||
|
||||
if( (pCsr->nRow & (pCsr->nRow-1))==0 ){
|
||||
int nNew = pCsr->nRow ? pCsr->nRow*2 : 32;
|
||||
Fts5tokRow *aNew;
|
||||
aNew = (Fts5tokRow*)sqlite3_realloc(pCsr->aRow, nNew*sizeof(Fts5tokRow));
|
||||
if( aNew==0 ) return SQLITE_NOMEM;
|
||||
memset(&aNew[pCsr->nRow], 0, sizeof(Fts5tokRow)*(nNew-pCsr->nRow));
|
||||
pCsr->aRow = aNew;
|
||||
}
|
||||
|
||||
pRow = &pCsr->aRow[pCsr->nRow];
|
||||
pRow->iStart = iStart;
|
||||
pRow->iEnd = iEnd;
|
||||
if( pCsr->nRow ){
|
||||
pRow->iPos = pRow[-1].iPos + ((tflags & FTS5_TOKEN_COLOCATED) ? 0 : 1);
|
||||
}
|
||||
pRow->zToken = sqlite3_malloc(nToken+1);
|
||||
if( pRow->zToken==0 ) return SQLITE_NOMEM;
|
||||
memcpy(pRow->zToken, pToken, nToken);
|
||||
pRow->zToken[nToken] = 0;
|
||||
pCsr->nRow++;
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** xFilter - Initialize a cursor to point at the start of its data.
|
||||
*/
|
||||
static int fts5tokFilterMethod(
|
||||
sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */
|
||||
int idxNum, /* Strategy index */
|
||||
const char *idxStr, /* Unused */
|
||||
int nVal, /* Number of elements in apVal */
|
||||
sqlite3_value **apVal /* Arguments for the indexing scheme */
|
||||
){
|
||||
int rc = SQLITE_ERROR;
|
||||
Fts5tokCursor *pCsr = (Fts5tokCursor *)pCursor;
|
||||
Fts5tokTable *pTab = (Fts5tokTable *)(pCursor->pVtab);
|
||||
|
||||
fts5tokResetCursor(pCsr);
|
||||
if( idxNum==1 ){
|
||||
const char *zByte = (const char *)sqlite3_value_text(apVal[0]);
|
||||
int nByte = sqlite3_value_bytes(apVal[0]);
|
||||
pCsr->zInput = sqlite3_malloc(nByte+1);
|
||||
if( pCsr->zInput==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
memcpy(pCsr->zInput, zByte, nByte);
|
||||
pCsr->zInput[nByte] = 0;
|
||||
rc = pTab->tok.xTokenize(
|
||||
pTab->pTok, (void*)pCsr, 0, zByte, nByte, fts5tokCb
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
return fts5tokNextMethod(pCursor);
|
||||
}
|
||||
|
||||
/*
|
||||
** xEof - Return true if the cursor is at EOF, or false otherwise.
|
||||
*/
|
||||
static int fts5tokEofMethod(sqlite3_vtab_cursor *pCursor){
|
||||
Fts5tokCursor *pCsr = (Fts5tokCursor *)pCursor;
|
||||
return (pCsr->iRowid>pCsr->nRow);
|
||||
}
|
||||
|
||||
/*
|
||||
** xColumn - Return a column value.
|
||||
*/
|
||||
static int fts5tokColumnMethod(
|
||||
sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
|
||||
sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */
|
||||
int iCol /* Index of column to read value from */
|
||||
){
|
||||
Fts5tokCursor *pCsr = (Fts5tokCursor *)pCursor;
|
||||
Fts5tokRow *pRow = &pCsr->aRow[pCsr->iRowid-1];
|
||||
|
||||
/* CREATE TABLE x(input, token, start, end, position) */
|
||||
switch( iCol ){
|
||||
case 0:
|
||||
sqlite3_result_text(pCtx, pCsr->zInput, -1, SQLITE_TRANSIENT);
|
||||
break;
|
||||
case 1:
|
||||
sqlite3_result_text(pCtx, pRow->zToken, -1, SQLITE_TRANSIENT);
|
||||
break;
|
||||
case 2:
|
||||
sqlite3_result_int(pCtx, pRow->iStart);
|
||||
break;
|
||||
case 3:
|
||||
sqlite3_result_int(pCtx, pRow->iEnd);
|
||||
break;
|
||||
default:
|
||||
assert( iCol==4 );
|
||||
sqlite3_result_int(pCtx, pRow->iPos);
|
||||
break;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** xRowid - Return the current rowid for the cursor.
|
||||
*/
|
||||
static int fts5tokRowidMethod(
|
||||
sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
|
||||
sqlite_int64 *pRowid /* OUT: Rowid value */
|
||||
){
|
||||
Fts5tokCursor *pCsr = (Fts5tokCursor *)pCursor;
|
||||
*pRowid = (sqlite3_int64)pCsr->iRowid;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Register the fts5tok module with database connection db. Return SQLITE_OK
|
||||
** if successful or an error code if sqlite3_create_module() fails.
|
||||
*/
|
||||
int sqlite3Fts5TestRegisterTok(sqlite3 *db, fts5_api *pApi){
|
||||
static const sqlite3_module fts5tok_module = {
|
||||
0, /* iVersion */
|
||||
fts5tokConnectMethod, /* xCreate */
|
||||
fts5tokConnectMethod, /* xConnect */
|
||||
fts5tokBestIndexMethod, /* xBestIndex */
|
||||
fts5tokDisconnectMethod, /* xDisconnect */
|
||||
fts5tokDisconnectMethod, /* xDestroy */
|
||||
fts5tokOpenMethod, /* xOpen */
|
||||
fts5tokCloseMethod, /* xClose */
|
||||
fts5tokFilterMethod, /* xFilter */
|
||||
fts5tokNextMethod, /* xNext */
|
||||
fts5tokEofMethod, /* xEof */
|
||||
fts5tokColumnMethod, /* xColumn */
|
||||
fts5tokRowidMethod, /* xRowid */
|
||||
0, /* xUpdate */
|
||||
0, /* xBegin */
|
||||
0, /* xSync */
|
||||
0, /* xCommit */
|
||||
0, /* xRollback */
|
||||
0, /* xFindFunction */
|
||||
0, /* xRename */
|
||||
0, /* xSavepoint */
|
||||
0, /* xRelease */
|
||||
0 /* xRollbackTo */
|
||||
};
|
||||
int rc; /* Return code */
|
||||
|
||||
rc = sqlite3_create_module(db, "fts5tokenize", &fts5tok_module, (void*)pApi);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* defined(SQLITE_TEST) && defined(SQLITE_ENABLE_FTS5) */
|
@ -427,6 +427,10 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){
|
||||
int ii = FTS5_POS2COLUMN(iPos);
|
||||
pCsr->aCnt[ii]++;
|
||||
if( iCol!=ii ){
|
||||
if( ii>=nCol ){
|
||||
rc = FTS5_CORRUPT;
|
||||
break;
|
||||
}
|
||||
pCsr->aDoc[ii]++;
|
||||
iCol = ii;
|
||||
}
|
||||
@ -444,7 +448,11 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){
|
||||
if( rc==SQLITE_OK ){
|
||||
while( 0==sqlite3Fts5PoslistNext64(buf.p, buf.n, &iOff,&iPos) ){
|
||||
assert_nc( iPos>=0 && iPos<nCol );
|
||||
if( iPos<nCol ) pCsr->aDoc[iPos]++;
|
||||
if( iPos>=nCol ){
|
||||
rc = FTS5_CORRUPT;
|
||||
break;
|
||||
}
|
||||
pCsr->aDoc[iPos]++;
|
||||
}
|
||||
}
|
||||
sqlite3Fts5BufferFree(&buf);
|
||||
@ -472,7 +480,7 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){
|
||||
}
|
||||
}
|
||||
|
||||
if( pCsr->bEof==0 && pTab->eType==FTS5_VOCAB_COL ){
|
||||
if( rc==SQLITE_OK && pCsr->bEof==0 && pTab->eType==FTS5_VOCAB_COL ){
|
||||
while( pCsr->aDoc[pCsr->iCol]==0 ) pCsr->iCol++;
|
||||
assert( pCsr->iCol<pCsr->pConfig->nCol );
|
||||
}
|
||||
|
@ -15,11 +15,22 @@ if {![info exists testdir]} {
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
catch {
|
||||
sqlite3_fts5_may_be_corrupt 0
|
||||
reset_db
|
||||
}
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is not defined, skip this test.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
proc fts5_test_poslist {cmd} {
|
||||
set res [list]
|
||||
for {set i 0} {$i < [$cmd xInstCount]} {incr i} {
|
||||
@ -510,6 +521,22 @@ proc fts5_poslist_data {expr tbl {order ASC} {aDictVar ""}} {
|
||||
set res
|
||||
}
|
||||
|
||||
proc fts5_collist_data {expr tbl {order ASC} {aDictVar ""}} {
|
||||
set res [list]
|
||||
|
||||
if {$aDictVar!=""} {
|
||||
upvar $aDictVar aDict
|
||||
set dict aDict
|
||||
} else {
|
||||
set dict ""
|
||||
}
|
||||
|
||||
foreach {rowid poslist collist} [fts5_query_data $expr $tbl $order $dict] {
|
||||
lappend res $rowid $collist
|
||||
}
|
||||
set res
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
@ -561,3 +588,54 @@ proc nearset_rc {aCol args} {
|
||||
list
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Code for a simple Tcl tokenizer that supports synonyms at query time.
|
||||
#
|
||||
proc tclnum_tokenize {mode tflags text} {
|
||||
foreach {w iStart iEnd} [fts5_tokenize_split $text] {
|
||||
sqlite3_fts5_token $w $iStart $iEnd
|
||||
if {$tflags == $mode && [info exists ::tclnum_syn($w)]} {
|
||||
foreach s $::tclnum_syn($w) { sqlite3_fts5_token -colo $s $iStart $iEnd }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proc tclnum_create {args} {
|
||||
set mode query
|
||||
if {[llength $args]} {
|
||||
set mode [lindex $args 0]
|
||||
}
|
||||
if {$mode != "query" && $mode != "document"} { error "bad mode: $mode" }
|
||||
return [list tclnum_tokenize $mode]
|
||||
}
|
||||
|
||||
proc fts5_tclnum_register {db} {
|
||||
foreach SYNDICT {
|
||||
{zero 0}
|
||||
{one 1 i}
|
||||
{two 2 ii}
|
||||
{three 3 iii}
|
||||
{four 4 iv}
|
||||
{five 5 v}
|
||||
{six 6 vi}
|
||||
{seven 7 vii}
|
||||
{eight 8 viii}
|
||||
{nine 9 ix}
|
||||
|
||||
{a1 a2 a3 a4 a5 a6 a7 a8 a9}
|
||||
{b1 b2 b3 b4 b5 b6 b7 b8 b9}
|
||||
{c1 c2 c3 c4 c5 c6 c7 c8 c9}
|
||||
} {
|
||||
foreach s $SYNDICT {
|
||||
set o [list]
|
||||
foreach x $SYNDICT {if {$x!=$s} {lappend o $x}}
|
||||
set ::tclnum_syn($s) $o
|
||||
}
|
||||
}
|
||||
sqlite3_fts5_create_tokenizer db tclnum tclnum_create
|
||||
}
|
||||
#
|
||||
# End of tokenizer code.
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
|
67
ext/fts5/test/fts5bigtok.test
Normal file
67
ext/fts5/test/fts5bigtok.test
Normal file
@ -0,0 +1,67 @@
|
||||
# 2016 Jan 19
|
||||
#
|
||||
# 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 testing the FTS5 module.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5bigtok
|
||||
|
||||
proc rndterm {} {
|
||||
set L [list a b c d e f g h i j k l m n o p q r s t u v w x y z]
|
||||
set l [lindex $L [expr int(rand() * [llength $L])]]
|
||||
string repeat $l [expr int(rand() * 5) + 60]
|
||||
}
|
||||
|
||||
proc rnddoc {n} {
|
||||
set res [list]
|
||||
for {set i 0} {$i < $n} {incr i} {
|
||||
lappend res [rndterm]
|
||||
}
|
||||
set res
|
||||
}
|
||||
|
||||
foreach_detail_mode $::testprefix {
|
||||
db func rnddoc rnddoc
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, detail=%DETAIL%);
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
|
||||
CREATE VIRTUAL TABLE t1vocab USING fts5vocab(t1, row);
|
||||
|
||||
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<10 )
|
||||
INSERT INTO t1 SELECT rnddoc(3) FROM s;
|
||||
|
||||
WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<10 )
|
||||
INSERT INTO t1 SELECT rnddoc(3) FROM s;
|
||||
}
|
||||
|
||||
foreach v [db eval {SELECT term FROM t1vocab}] {
|
||||
set res [db eval {SELECT rowid FROM t1($v)}]
|
||||
do_execsql_test 1.[string range $v 0 0] {
|
||||
SELECT rowid FROM t1($v) ORDER BY rowid DESC
|
||||
} [lsort -integer -decr $res]
|
||||
}
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
INSERT INTO t1(t1) VALUES('optimize');
|
||||
}
|
||||
|
||||
foreach v [db eval {SELECT term FROM t1vocab}] {
|
||||
set res [db eval {SELECT rowid FROM t1($v)}]
|
||||
do_execsql_test 2.[string range $v 0 0] {
|
||||
SELECT rowid FROM t1($v) ORDER BY rowid DESC
|
||||
} [lsort -integer -decr $res]
|
||||
}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -44,6 +44,8 @@ foreach {tn opt} {
|
||||
3 {prefix='$'}
|
||||
4 {prefix='1,2,'}
|
||||
5 {prefix=',1'}
|
||||
6 {prefix='1,2,3...'}
|
||||
7 {prefix='1,2,3xyz'}
|
||||
} {
|
||||
set res [list 1 {malformed prefix=... directive}]
|
||||
do_catchsql_test 2.$tn "CREATE VIRTUAL TABLE f1 USING fts5(x, $opt)" $res
|
||||
@ -159,6 +161,8 @@ do_catchsql_test 8.1 {
|
||||
# 9.1.* 'pgsz' options.
|
||||
# 9.2.* 'automerge' options.
|
||||
# 9.3.* 'crisismerge' options.
|
||||
# 9.4.* a non-existant option.
|
||||
# 9.5.* 'hashsize' options.
|
||||
#
|
||||
do_execsql_test 9.0 {
|
||||
CREATE VIRTUAL TABLE abc USING fts5(a, b);
|
||||
@ -203,6 +207,16 @@ do_catchsql_test 9.4.1 {
|
||||
INSERT INTO abc(abc, rank) VALUES('nosuchoption', 1);
|
||||
} {1 {SQL logic error or missing database}}
|
||||
|
||||
do_catchsql_test 9.5.1 {
|
||||
INSERT INTO abc(abc, rank) VALUES('hashsize', 'not an integer');
|
||||
} {1 {SQL logic error or missing database}}
|
||||
do_catchsql_test 9.5.2 {
|
||||
INSERT INTO abc(abc, rank) VALUES('hashsize', -500000);
|
||||
} {1 {SQL logic error or missing database}}
|
||||
do_catchsql_test 9.5.3 {
|
||||
INSERT INTO abc(abc, rank) VALUES('hashsize', 500000);
|
||||
} {0 {}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Too many prefix indexes. Maximum allowed is 31.
|
||||
#
|
||||
@ -214,5 +228,20 @@ foreach {tn spec} {
|
||||
do_catchsql_test 10.$tn $sql {1 {too many prefix indexes (max 31)}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# errors in the detail= option.
|
||||
#
|
||||
foreach {tn opt} {
|
||||
1 {detail=x}
|
||||
2 {detail='x'}
|
||||
3 {detail='$'}
|
||||
4 {detail='1,2,'}
|
||||
5 {detail=',1'}
|
||||
6 {detail=''}
|
||||
} {
|
||||
set res [list 1 {malformed detail=... directive}]
|
||||
do_catchsql_test 11.$tn "CREATE VIRTUAL TABLE f1 USING fts5(x, $opt)" $res
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -40,27 +40,6 @@ do_faultsim_test 1 -faults oom-* -prep {
|
||||
faultsim_test_result [list 0 {}]
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# An OOM within an "ORDER BY rank" query.
|
||||
#
|
||||
db func rnddoc fts5_rnddoc
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE xx USING fts5(x);
|
||||
INSERT INTO xx VALUES ('abc ' || rnddoc(10));
|
||||
INSERT INTO xx VALUES ('abc abc' || rnddoc(9));
|
||||
INSERT INTO xx VALUES ('abc abc abc' || rnddoc(8));
|
||||
} {}
|
||||
faultsim_save_and_close
|
||||
|
||||
do_faultsim_test 2 -faults oom-* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
execsql { SELECT * FROM xx }
|
||||
} -body {
|
||||
execsql { SELECT rowid FROM xx WHERE xx MATCH 'abc' ORDER BY rank }
|
||||
} -test {
|
||||
faultsim_test_result [list 0 {3 2 1}]
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# An OOM while "reseeking" an FTS cursor.
|
||||
#
|
||||
|
@ -65,19 +65,26 @@ do_faultsim_test 2.2 -faults oom-t* -body {
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# OOM while scanning an fts5vocab table.
|
||||
# OOM while scanning fts5vocab tables.
|
||||
#
|
||||
reset_db
|
||||
do_test 3.0 {
|
||||
execsql {
|
||||
CREATE VIRTUAL TABLE tt USING fts5(x);
|
||||
CREATE VIRTUAL TABLE tv USING fts5vocab(tt, 'row');
|
||||
|
||||
CREATE VIRTUAL TABLE tt2 USING fts5(x, detail=col);
|
||||
CREATE VIRTUAL TABLE tv2 USING fts5vocab(tt2, 'col');
|
||||
|
||||
INSERT INTO tt(tt, rank) VALUES('pgsz', 32);
|
||||
INSERT INTO tt2(tt2, rank) VALUES('pgsz', 32);
|
||||
BEGIN;
|
||||
}
|
||||
|
||||
for {set i 0} {$i < 20} {incr i} {
|
||||
set str [string repeat "$i " 50]
|
||||
execsql { INSERT INTO tt VALUES($str) }
|
||||
execsql { INSERT INTO tt2 VALUES($str) }
|
||||
}
|
||||
execsql COMMIT
|
||||
} {}
|
||||
@ -98,6 +105,28 @@ do_faultsim_test 3.2 -faults oom-t* -body {
|
||||
faultsim_test_result {0 {1 10 11 12 13 14 15 16 17 18 19 2}}
|
||||
}
|
||||
|
||||
breakpoint
|
||||
do_execsql_test 3.3.0 {
|
||||
SELECT * FROM tv2;
|
||||
} {
|
||||
0 x 1 {} 1 x 1 {} 10 x 1 {} 11 x 1 {} 12 x 1 {} 13 x 1 {}
|
||||
14 x 1 {} 15 x 1 {} 16 x 1 {} 17 x 1 {} 18 x 1 {} 19 x 1 {}
|
||||
2 x 1 {} 3 x 1 {} 4 x 1 {} 5 x 1 {} 6 x 1 {} 7 x 1 {} 8 x 1 {}
|
||||
9 x 1 {}
|
||||
}
|
||||
do_faultsim_test 3.3 -faults oom-t* -body {
|
||||
db eval {
|
||||
SELECT * FROM tv2;
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result [list 0 [list \
|
||||
0 x 1 {} 1 x 1 {} 10 x 1 {} 11 x 1 {} 12 x 1 {} 13 x 1 {} \
|
||||
14 x 1 {} 15 x 1 {} 16 x 1 {} 17 x 1 {} 18 x 1 {} 19 x 1 {} \
|
||||
2 x 1 {} 3 x 1 {} 4 x 1 {} 5 x 1 {} 6 x 1 {} 7 x 1 {} 8 x 1 {} \
|
||||
9 x 1 {}
|
||||
]]
|
||||
}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
@ -24,8 +24,6 @@ ifcapable !fts5 {
|
||||
|
||||
foreach_detail_mode $testprefix {
|
||||
|
||||
if {[detail_is_none]==0} continue
|
||||
|
||||
fts5_aux_test_functions db
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, detail=%DETAIL%);
|
||||
@ -34,7 +32,7 @@ do_execsql_test 1.0 {
|
||||
INSERT INTO t1 VALUES(NULL, '1 2 1 2');
|
||||
}
|
||||
|
||||
do_faultsim_test 1 -faults oom-t* -body {
|
||||
do_faultsim_test 1 -faults oom-* -body {
|
||||
execsql {
|
||||
SELECT rowid, fts5_test_poslist(t1) FROM t1 WHERE t1 MATCH 'b OR 2'
|
||||
}
|
||||
@ -43,6 +41,14 @@ do_faultsim_test 1 -faults oom-t* -body {
|
||||
{1 SQLITE_NOMEM}
|
||||
}
|
||||
|
||||
do_faultsim_test 2 -faults oom-* -body {
|
||||
execsql {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
156
ext/fts5/test/fts5fault9.test
Normal file
156
ext/fts5/test/fts5fault9.test
Normal file
@ -0,0 +1,156 @@
|
||||
# 2015 September 3
|
||||
#
|
||||
# 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 is focused on OOM errors.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
source $testdir/malloc_common.tcl
|
||||
set testprefix fts5fault9
|
||||
|
||||
# If SQLITE_ENABLE_FTS3 is defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
foreach_detail_mode $testprefix {
|
||||
|
||||
fts5_aux_test_functions db
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, detail=%DETAIL%);
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
|
||||
WITH seq(s) AS ( SELECT 1 UNION ALL SELECT s+1 FROM seq WHERE s<50)
|
||||
INSERT INTO t1 SELECT 'x x x y y y', 'a b c d e f' FROM seq;
|
||||
}
|
||||
|
||||
do_faultsim_test 1 -faults oom-* -body {
|
||||
execsql { SELECT count(*) FROM t1('x AND y') }
|
||||
} -test {
|
||||
faultsim_test_result {0 50}
|
||||
}
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(a, b, detail=%DETAIL%);
|
||||
INSERT INTO t2(t2, rank) VALUES('pgsz', 32);
|
||||
INSERT INTO t2 VALUES('abc cba', 'cba abc');
|
||||
INSERT INTO t2 VALUES('abc cba', 'cba abc');
|
||||
INSERT INTO t2 VALUES('abc cba', 'cba abc');
|
||||
|
||||
INSERT INTO t2 VALUES('axy cyx', 'cyx axy');
|
||||
INSERT INTO t2 VALUES('axy cyx', 'cyx axy');
|
||||
INSERT INTO t2 VALUES('axy cyx', 'cyx axy');
|
||||
}
|
||||
|
||||
do_faultsim_test 2 -faults oom-* -body {
|
||||
execsql { SELECT count(*) FROM t2('a* AND c*') }
|
||||
} -test {
|
||||
faultsim_test_result {0 6}
|
||||
}
|
||||
|
||||
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE t3 USING fts5(a, detail=%DETAIL%);
|
||||
INSERT INTO t3 VALUES('a x x a x a a a');
|
||||
INSERT INTO t3 VALUES('x a a x a x x x');
|
||||
}
|
||||
|
||||
do_faultsim_test 3.1 -faults oom-* -body {
|
||||
execsql { SELECT highlight(t3, 0, '[', ']') FROM t3('a') }
|
||||
} -test {
|
||||
faultsim_test_result {0 {{[a] x x [a] x [a] [a] [a]} {x [a] [a] x [a] x x x}}}
|
||||
}
|
||||
|
||||
do_faultsim_test 3.2 -faults oom-t* -body {
|
||||
execsql { SELECT fts5_test_poslist2(t3) FROM t3('x') }
|
||||
} -test {
|
||||
faultsim_test_result \
|
||||
{0 {{0.0.1 0.0.2 0.0.4} {0.0.0 0.0.3 0.0.5 0.0.6 0.0.7}}} \
|
||||
{1 SQLITE_NOMEM}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test OOM injection with the xPhraseFirstColumn() API and a tokenizer
|
||||
# uses query synonyms.
|
||||
#
|
||||
fts5_tclnum_register db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE t4 USING fts5(x, y, z, detail=%DETAIL%, tokenize=tclnum);
|
||||
INSERT INTO t4 VALUES('one two three', '1 2 3', 'i ii iii');
|
||||
INSERT INTO t4 VALUES('1 2 3', 'i ii iii', 'one two three');
|
||||
INSERT INTO t4 VALUES('i ii iii', 'one two three', 'i ii iii');
|
||||
|
||||
INSERT INTO t4 VALUES('a1 a2 a3', 'a4 a5 a6', 'a7 a8 a9');
|
||||
INSERT INTO t4 VALUES('b1 b2 b3', 'b4 b5 b6', 'b7 b8 b9');
|
||||
INSERT INTO t4 VALUES('c1 c2 c3', 'c4 c5 c6', 'c7 c8 c9');
|
||||
}
|
||||
|
||||
do_faultsim_test 4.1 -faults oom-t* -body {
|
||||
execsql { SELECT rowid, fts5_test_collist(t4) FROM t4('2') }
|
||||
} -test {
|
||||
faultsim_test_result \
|
||||
{0 {1 {0.0 0.1 0.2} 2 {0.0 0.1 0.2} 3 {0.0 0.1 0.2}}} {1 SQLITE_NOMEM}
|
||||
}
|
||||
|
||||
do_faultsim_test 4.2 -faults oom-t* -body {
|
||||
execsql { SELECT rowid, fts5_test_collist(t4) FROM t4('a5 OR b5 OR c5') }
|
||||
} -test {
|
||||
faultsim_test_result \
|
||||
{0 {4 {0.0 0.1 0.2} 5 {1.0 1.1 1.2} 6 {2.0 2.1 2.2}}} {1 SQLITE_NOMEM}
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# An OOM within an "ORDER BY rank" query.
|
||||
#
|
||||
db func rnddoc fts5_rnddoc
|
||||
do_execsql_test 5.0 {
|
||||
CREATE VIRTUAL TABLE xx USING fts5(x, y, detail=%DETAIL%);
|
||||
INSERT INTO xx VALUES ('def', 'abc ' || rnddoc(10));
|
||||
INSERT INTO xx VALUES ('def', 'abc abc' || rnddoc(9));
|
||||
INSERT INTO xx VALUES ('def', 'abc abc abc' || rnddoc(8));
|
||||
} {}
|
||||
faultsim_save_and_close
|
||||
|
||||
do_faultsim_test 5 -faults oom-* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
execsql { SELECT * FROM xx }
|
||||
} -body {
|
||||
execsql { SELECT rowid FROM xx('abc AND def') ORDER BY rank }
|
||||
} -test {
|
||||
faultsim_test_result [list 0 {3 2 1}]
|
||||
}
|
||||
|
||||
set doc [string repeat "xyz " 500]
|
||||
do_execsql_test 6.0 {
|
||||
CREATE VIRTUAL TABLE yy USING fts5(y, detail=%DETAIL%);
|
||||
INSERT INTO yy(yy, rank) VALUES('pgsz', 64);
|
||||
INSERT INTO yy VALUES ($doc);
|
||||
INSERT INTO yy VALUES ('1 2 3');
|
||||
INSERT INTO yy VALUES ('xyz');
|
||||
UPDATE yy SET y = y WHERE rowid = 1;
|
||||
UPDATE yy SET y = y WHERE rowid = 1;
|
||||
UPDATE yy SET y = y WHERE rowid = 1;
|
||||
UPDATE yy SET y = y WHERE rowid = 1;
|
||||
} {}
|
||||
|
||||
do_faultsim_test 6 -faults oom-* -body {
|
||||
execsql { SELECT rowid FROM yy('xyz') }
|
||||
} -test {
|
||||
faultsim_test_result [list 0 {1 3}]
|
||||
}
|
||||
|
||||
|
||||
} ;# foreach_detail_mode...
|
||||
|
||||
finish_test
|
||||
|
@ -64,11 +64,13 @@ proc random_doc {vocab nWord} {
|
||||
return $doc
|
||||
}
|
||||
|
||||
foreach_detail_mode $testprefix {
|
||||
|
||||
set vocab [build_vocab1]
|
||||
db func r random_doc
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE eee USING fts5(e, ee);
|
||||
CREATE VIRTUAL TABLE eee USING fts5(e, ee, detail=%DETAIL%);
|
||||
BEGIN;
|
||||
WITH ii(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM ii WHERE i<100)
|
||||
INSERT INTO eee SELECT r($vocab, 5), r($vocab, 7) FROM ii;
|
||||
@ -91,7 +93,7 @@ do_test 1.2 {
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test 1.2 {
|
||||
do_test 1.3 {
|
||||
db eval { SELECT term, doc FROM vocab } {
|
||||
set nRow [db one {SELECT count(*) FROM eee WHERE eee MATCH $term}]
|
||||
if {$nRow != $doc} {
|
||||
@ -101,10 +103,12 @@ do_test 1.2 {
|
||||
set {} {}
|
||||
} {}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
do_execsql_test 1.4 {
|
||||
COMMIT;
|
||||
INSERT INTO eee(eee) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
} ;# foreach_detail_mode
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -145,10 +145,69 @@ do_execsql_test 5.2 {
|
||||
INSERT INTO gg(gg) VALUES('optimize');
|
||||
}
|
||||
|
||||
breakpoint
|
||||
do_execsql_test 5.3 {
|
||||
INSERT INTO gg(gg) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
do_test 5.4.1 {
|
||||
set ok 0
|
||||
for {set i 0} {$i < 10000} {incr i} {
|
||||
set T [format %.5d $i]
|
||||
set res [db eval { SELECT rowid FROM gg($T) ORDER BY rowid ASC }]
|
||||
set res2 [db eval { SELECT rowid FROM gg($T) ORDER BY rowid DESC }]
|
||||
if {$res == [lsort -integer $res2]} { incr ok }
|
||||
}
|
||||
set ok
|
||||
} {10000}
|
||||
|
||||
do_test 5.4.2 {
|
||||
set ok 0
|
||||
for {set i 0} {$i < 100} {incr i} {
|
||||
set T "[format %.3d $i]*"
|
||||
set res [db eval { SELECT rowid FROM gg($T) ORDER BY rowid ASC }]
|
||||
set res2 [db eval { SELECT rowid FROM gg($T) ORDER BY rowid DESC }]
|
||||
if {$res == [lsort -integer $res2]} { incr ok }
|
||||
}
|
||||
set ok
|
||||
} {100}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Similar to 5.*.
|
||||
#
|
||||
foreach {tn pgsz} {
|
||||
1 32
|
||||
2 36
|
||||
3 40
|
||||
4 44
|
||||
5 48
|
||||
} {
|
||||
do_execsql_test 6.$tn.1 {
|
||||
DROP TABLE IF EXISTS hh;
|
||||
CREATE VIRTUAL TABLE hh USING fts5(y);
|
||||
INSERT INTO hh(hh, rank) VALUES('pgsz', $pgsz);
|
||||
|
||||
WITH s(i) AS (SELECT 0 UNION ALL SELECT i+1 FROM s WHERE i<999)
|
||||
INSERT INTO hh SELECT printf("%.3d%.3d%.3d %.3d%.3d%.3d",i,i,i,i+1,i+1,i+1)
|
||||
FROM s;
|
||||
|
||||
WITH s(i) AS (SELECT 0 UNION ALL SELECT i+1 FROM s WHERE i<999)
|
||||
INSERT INTO hh SELECT printf("%.3d%.3d%.3d %.3d%.3d%.3d",i,i,i,i+1,i+1,i+1)
|
||||
FROM s;
|
||||
|
||||
INSERT INTO hh(hh) VALUES('optimize');
|
||||
}
|
||||
|
||||
do_test 6.$tn.2 {
|
||||
set ok 0
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
set T [format %.3d%.3d%.3d $i $i $i]
|
||||
set res [db eval { SELECT rowid FROM hh($T) ORDER BY rowid ASC }]
|
||||
set res2 [db eval { SELECT rowid FROM hh($T) ORDER BY rowid DESC }]
|
||||
if {$res == [lsort -integer $res2]} { incr ok }
|
||||
}
|
||||
set ok
|
||||
} {1000}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
59
ext/fts5/test/fts5merge2.test
Normal file
59
ext/fts5/test/fts5merge2.test
Normal file
@ -0,0 +1,59 @@
|
||||
# 2014 Dec 20
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Test that focus on incremental merges of segments.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5merge
|
||||
|
||||
proc dump_structure {} {
|
||||
db eval {SELECT fts5_decode(id, block) AS t FROM t1_data WHERE id=10} {
|
||||
foreach lvl [lrange $t 1 end] {
|
||||
set seg [string repeat . [expr [llength $lvl]-2]]
|
||||
puts "[lrange $lvl 0 1] $seg"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach_detail_mode $testprefix {
|
||||
|
||||
if {[detail_is_none]==0} continue
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, detail=%DETAIL%);
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
|
||||
INSERT INTO t1(t1, rank) VALUES('crisismerge', 2);
|
||||
INSERT INTO t1 VALUES('1 2 3 4');
|
||||
}
|
||||
|
||||
expr srand(0)
|
||||
db func rnddoc fts5_rnddoc
|
||||
do_test 1.1 {
|
||||
for {set i 0} {$i < 100} {incr i} {
|
||||
execsql {
|
||||
BEGIN;
|
||||
DELETE FROM t1 WHERE rowid = 1;
|
||||
INSERT INTO t1(rowid, x) VALUES(1, '1 2 3 4');
|
||||
INSERT INTO t1 VALUES(rnddoc(10));
|
||||
COMMIT;
|
||||
}
|
||||
}
|
||||
} {}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
@ -63,6 +63,7 @@ do_execsql_test 2.2 {
|
||||
SELECT rnddoc(6), rnddoc(6) FROM r
|
||||
)
|
||||
INSERT INTO x1 SELECT * FROM r LIMIT 10000;
|
||||
DELETE FROM x1 WHERE (rowid%2);
|
||||
}
|
||||
|
||||
set res [db one {SELECT count(*) FROM x1_data}]
|
||||
@ -71,8 +72,7 @@ do_execsql_test 2.3 {
|
||||
} $res
|
||||
do_execsql_test 2.4 {
|
||||
UPDATE x1_data SET block = X'';
|
||||
-- SELECT count(fts5_decode(rowid, block)) FROM x1_data;
|
||||
SELECT count(*) FROM x1_data;
|
||||
SELECT count(fts5_decode(rowid, block)) FROM x1_data;
|
||||
} $res
|
||||
|
||||
do_execsql_test 2.5 {
|
||||
@ -184,5 +184,36 @@ do_execsql_test 5.2 {
|
||||
SELECT count(fts5_decode(rowid, block)) FROM x4_data;
|
||||
} $res
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
do_execsql_test 6.0 {
|
||||
CREATE VIRTUAL TABLE x5 USING fts5(x, detail=none);
|
||||
INSERT INTO x5(x5, rank) VALUES('pgsz', 32);
|
||||
INSERT INTO x5 VALUES('a b c d e f');
|
||||
INSERT INTO x5 VALUES('a b c d e f');
|
||||
INSERT INTO x5 VALUES('a b c d e f');
|
||||
BEGIN;
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100
|
||||
) INSERT INTO x5 SELECT 'a b c d e f' FROM s;
|
||||
COMMIT;
|
||||
SELECT count(fts5_decode_none(rowid, block)) FROM x5_data;
|
||||
} {32}
|
||||
|
||||
do_execsql_test 6.1 {
|
||||
DELETE FROM x5 WHERE rowid <= 2;
|
||||
SELECT count(fts5_decode_none(rowid, block)) FROM x5_data;
|
||||
} {34}
|
||||
|
||||
do_execsql_test 6.2 {
|
||||
UPDATE x5 SET x='a b c d e f' WHERE rowid=3;
|
||||
SELECT count(fts5_decode_none(rowid, block)) FROM x5_data;
|
||||
} {36}
|
||||
|
||||
#db eval {SELECT rowid, fts5_decode_none(rowid, block) aS r FROM x5_data} {puts $r}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -19,8 +19,6 @@ ifcapable !fts5 {
|
||||
return
|
||||
}
|
||||
|
||||
if 1 {
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, detail=none);
|
||||
INSERT INTO t1 VALUES('a b c');
|
||||
@ -267,8 +265,6 @@ do_execsql_test 14.1 {
|
||||
SELECT fts5_test_poslist(t1) FROM t1('b') ORDER BY rank;
|
||||
} {0.0.1}
|
||||
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
@ -299,6 +295,44 @@ do_execsql_test 15.3.2 {
|
||||
do_test 15.4 {
|
||||
execsql { INSERT INTO t1(t1) VALUES('integrity-check') }
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 16.0 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(x, detail=none);
|
||||
BEGIN;
|
||||
INSERT INTO t2(rowid, x) VALUES(1, 'a b c');
|
||||
INSERT INTO t2(rowid, x) VALUES(456, 'a b c');
|
||||
INSERT INTO t2(rowid, x) VALUES(1000, 'a b c');
|
||||
COMMIT;
|
||||
UPDATE t2 SET x=x;
|
||||
}
|
||||
|
||||
do_execsql_test 16.1 {
|
||||
INSERT INTO t2(t2) VALUES('integrity-check');
|
||||
} {}
|
||||
|
||||
do_execsql_test 16.2 {
|
||||
SELECT rowid FROM t2('b') ORDER BY rowid DESC
|
||||
} {1000 456 1}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 16.0 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(x, detail=none);
|
||||
BEGIN;
|
||||
INSERT INTO t2(rowid, x) VALUES(1, 'a b c');
|
||||
INSERT INTO t2(rowid, x) VALUES(456, 'a b c');
|
||||
INSERT INTO t2(rowid, x) VALUES(1000, 'a b c');
|
||||
COMMIT;
|
||||
UPDATE t2 SET x=x;
|
||||
DELETE FROM t2;
|
||||
}
|
||||
|
||||
#db eval {SELECT rowid, fts5_decode_none(rowid, block) aS r FROM t2_data} {puts $r}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -21,42 +21,16 @@ ifcapable !fts5 {
|
||||
return
|
||||
}
|
||||
|
||||
foreach S {
|
||||
{zero 0}
|
||||
{one 1 i}
|
||||
{two 2 ii}
|
||||
{three 3 iii}
|
||||
{four 4 iv}
|
||||
{five 5 v}
|
||||
{six 6 vi}
|
||||
{seven 7 vii}
|
||||
{eight 8 viii}
|
||||
{nine 9 ix}
|
||||
} {
|
||||
foreach s $S {
|
||||
set o [list]
|
||||
foreach x $S {if {$x!=$s} {lappend o $x}}
|
||||
set ::syn($s) $o
|
||||
}
|
||||
}
|
||||
proc tcl_create {args} { return "tcl_tokenize" }
|
||||
|
||||
proc tcl_tokenize {tflags text} {
|
||||
foreach {w iStart iEnd} [fts5_tokenize_split $text] {
|
||||
sqlite3_fts5_token $w $iStart $iEnd
|
||||
}
|
||||
}
|
||||
|
||||
proc tcl_create {args} {
|
||||
return "tcl_tokenize"
|
||||
}
|
||||
|
||||
sqlite3_fts5_create_tokenizer db tcl tcl_create
|
||||
foreach_detail_mode $testprefix {
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Warm body test for the code in fts5_tcl.c.
|
||||
#
|
||||
fts5_tclnum_register db
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x, tokenize = tcl);
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x, tokenize = "tclnum document", detail=%DETAIL%);
|
||||
INSERT INTO ft VALUES('abc def ghi');
|
||||
INSERT INTO ft VALUES('jkl mno pqr');
|
||||
SELECT rowid, x FROM ft WHERE ft MATCH 'def';
|
||||
@ -67,22 +41,13 @@ do_execsql_test 1.0 {
|
||||
# Test a tokenizer that supports synonyms by adding extra entries to the
|
||||
# FTS index.
|
||||
#
|
||||
|
||||
proc tcl_tokenize {tflags text} {
|
||||
foreach {w iStart iEnd} [fts5_tokenize_split $text] {
|
||||
sqlite3_fts5_token $w $iStart $iEnd
|
||||
if {$tflags=="document" && [info exists ::syn($w)]} {
|
||||
foreach s $::syn($w) {
|
||||
sqlite3_fts5_token -colo $s $iStart $iEnd
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
reset_db
|
||||
sqlite3_fts5_create_tokenizer db tcl tcl_create
|
||||
fts5_tclnum_register db
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x, tokenize = tcl);
|
||||
CREATE VIRTUAL TABLE ft USING fts5(
|
||||
x, tokenize = "tclnum document", detail=%DETAIL%
|
||||
);
|
||||
INSERT INTO ft VALUES('one two three');
|
||||
INSERT INTO ft VALUES('four five six');
|
||||
INSERT INTO ft VALUES('eight nine ten');
|
||||
@ -95,6 +60,7 @@ foreach {tn expr res} {
|
||||
4 "1*" {1}
|
||||
5 "1 + 2" {1}
|
||||
} {
|
||||
if {![fts5_expr_ok $expr ft]} continue
|
||||
do_execsql_test 2.1.$tn {
|
||||
SELECT rowid FROM ft WHERE ft MATCH $expr
|
||||
} $res
|
||||
@ -180,17 +146,7 @@ do_execsql_test 3.2.5 {
|
||||
# Check that expressions with synonyms can be parsed and executed.
|
||||
#
|
||||
reset_db
|
||||
sqlite3_fts5_create_tokenizer db tcl tcl_create
|
||||
proc tcl_tokenize {tflags text} {
|
||||
foreach {w iStart iEnd} [fts5_tokenize_split $text] {
|
||||
sqlite3_fts5_token $w $iStart $iEnd
|
||||
if {$tflags=="query" && [info exists ::syn($w)]} {
|
||||
foreach s $::syn($w) {
|
||||
sqlite3_fts5_token -colo $s $iStart $iEnd
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fts5_tclnum_register db
|
||||
|
||||
foreach {tn expr res} {
|
||||
1 {abc} {"abc"}
|
||||
@ -198,11 +154,13 @@ foreach {tn expr res} {
|
||||
3 {3} {"3"|"iii"|"three"}
|
||||
4 {3*} {"3"|"iii"|"three" *}
|
||||
} {
|
||||
do_execsql_test 4.1.$tn {SELECT fts5_expr($expr, 'tokenize=tcl')} [list $res]
|
||||
do_execsql_test 4.1.$tn {
|
||||
SELECT fts5_expr($expr, 'tokenize=tclnum')
|
||||
} [list $res]
|
||||
}
|
||||
|
||||
do_execsql_test 4.2.1 {
|
||||
CREATE VIRTUAL TABLE xx USING fts5(x, tokenize=tcl);
|
||||
CREATE VIRTUAL TABLE xx USING fts5(x, tokenize=tclnum, detail=%DETAIL%);
|
||||
INSERT INTO xx VALUES('one two');
|
||||
INSERT INTO xx VALUES('three four');
|
||||
}
|
||||
@ -217,7 +175,7 @@ do_execsql_test 4.2.3 {
|
||||
|
||||
do_test 5.0 {
|
||||
execsql {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, tokenize=tcl)
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, tokenize=tclnum, detail=%DETAIL%)
|
||||
}
|
||||
foreach {rowid a b} {
|
||||
1 {four v 4 i three} {1 3 five five 4 one}
|
||||
@ -285,6 +243,7 @@ foreach {tn q res} {
|
||||
5 {three i v i four 4 1} {ii [five five five] iii}
|
||||
}
|
||||
} {
|
||||
if {![fts5_expr_ok $q t1]} continue
|
||||
do_execsql_test 5.1.$tn {
|
||||
SELECT rowid, highlight(t1, 0, '[', ']'), highlight(t1, 1, '[', ']')
|
||||
FROM t1 WHERE t1 MATCH $q
|
||||
@ -316,7 +275,6 @@ foreach {tn q res} {
|
||||
} $res
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test terms with more than 4 synonyms.
|
||||
#
|
||||
@ -334,17 +292,19 @@ proc tcl_tokenize {tflags text} {
|
||||
}
|
||||
|
||||
do_execsql_test 6.0.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, tokenize=tcl);
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, tokenize=tcl, detail=%DETAIL%);
|
||||
INSERT INTO t1 VALUES('yy xx qq');
|
||||
INSERT INTO t1 VALUES('yy xx xx');
|
||||
}
|
||||
do_execsql_test 6.0.2 {
|
||||
SELECT * FROM t1 WHERE t1 MATCH 'NEAR(y q)';
|
||||
} {{yy xx qq}}
|
||||
if {[fts5_expr_ok "NEAR(y q)" t1]} {
|
||||
do_execsql_test 6.0.2 {
|
||||
SELECT * FROM t1 WHERE t1 MATCH 'NEAR(y q)';
|
||||
} {{yy xx qq}}
|
||||
}
|
||||
|
||||
do_test 6.0.3 {
|
||||
execsql {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(a, b, tokenize=tcl)
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(a, b, tokenize=tcl, detail=%DETAIL%)
|
||||
}
|
||||
foreach {rowid a b} {
|
||||
1 {yyyy vvvvv qq oo yyyyyy vvvv eee} {ffff uu r qq aaaa}
|
||||
@ -387,6 +347,8 @@ foreach {tn q res} {
|
||||
2 {ww oooooo bbbbb ssssss mm} {ffffff [yy] iiii rr s ccc [qqqqq]}
|
||||
}
|
||||
} {
|
||||
if {![fts5_expr_ok $q t2]} continue
|
||||
|
||||
do_execsql_test 6.1.$tn.asc {
|
||||
SELECT rowid, highlight(t2, 0, '[', ']'), highlight(t2, 1, '[', ']')
|
||||
FROM t2 WHERE t2 MATCH $q
|
||||
@ -435,7 +397,7 @@ proc tcl_tokenize {tflags text} {
|
||||
}
|
||||
|
||||
do_execsql_test 7.0.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, columnsize=1, tokenize=tcl);
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, columnsize=1, tokenize=tcl, detail=%DETAIL%);
|
||||
INSERT INTO t1 VALUES('0 2 3', '4 5 6 7');
|
||||
INSERT INTO t1 VALUES('8 9', '0 0 0 0 0 0 0 0 0 0');
|
||||
SELECT fts5_test_columnsize(t1) FROM t1 WHERE t1 MATCH '000 AND 00 AND 0';
|
||||
@ -446,7 +408,7 @@ do_execsql_test 7.0.2 {
|
||||
}
|
||||
|
||||
do_execsql_test 7.1.1 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(a, b, columnsize=0, tokenize=tcl);
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(a, b, columnsize=0, tokenize=tcl, detail=%DETAIL%);
|
||||
INSERT INTO t2 VALUES('0 2 3', '4 5 6 7');
|
||||
INSERT INTO t2 VALUES('8 9', '0 0 0 0 0 0 0 0 0 0');
|
||||
SELECT fts5_test_columnsize(t2) FROM t2 WHERE t2 MATCH '000 AND 00 AND 0';
|
||||
@ -456,5 +418,7 @@ do_execsql_test 7.1.2 {
|
||||
INSERT INTO t2(t2) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
} ;# foreach_detail_mode
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -21,52 +21,22 @@ ifcapable !fts5 {
|
||||
return
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Code for a simple Tcl tokenizer that supports synonyms at query time.
|
||||
#
|
||||
foreach SYNDICT {
|
||||
{zero 0}
|
||||
{one 1 i}
|
||||
{two 2 ii}
|
||||
{three 3 iii}
|
||||
{four 4 iv}
|
||||
{five 5 v}
|
||||
{six 6 vi}
|
||||
{seven 7 vii}
|
||||
{eight 8 viii}
|
||||
{nine 9 ix}
|
||||
} {
|
||||
foreach s $SYNDICT {
|
||||
set o [list]
|
||||
foreach x $SYNDICT {if {$x!=$s} {lappend o $x}}
|
||||
set ::syn($s) $o
|
||||
}
|
||||
}
|
||||
|
||||
proc tcl_tokenize {tflags text} {
|
||||
foreach {w iStart iEnd} [fts5_tokenize_split $text] {
|
||||
sqlite3_fts5_token $w $iStart $iEnd
|
||||
if {$tflags == "query"} {
|
||||
foreach s $::syn($w) { sqlite3_fts5_token -colo $s $iStart $iEnd }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proc tcl_create {args} {
|
||||
return "tcl_tokenize"
|
||||
}
|
||||
|
||||
#
|
||||
# End of tokenizer code.
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
foreach tok {query document} {
|
||||
foreach_detail_mode $testprefix {
|
||||
|
||||
sqlite3_fts5_create_tokenizer db tcl tcl_create
|
||||
fts5_tclnum_register db
|
||||
fts5_aux_test_functions db
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE ss USING fts5(a, b, tokenize=tcl, detail=%DETAIL%);
|
||||
proc fts5_rowid {cmd} { expr [$cmd xColumnText -1] }
|
||||
sqlite3_fts5_create_function db fts5_rowid fts5_rowid
|
||||
|
||||
do_execsql_test 1.$tok.0.1 "
|
||||
CREATE VIRTUAL TABLE ss USING fts5(a, b,
|
||||
tokenize='tclnum $tok', detail=%DETAIL%);
|
||||
INSERT INTO ss(ss, rank) VALUES('rank', 'fts5_rowid()');
|
||||
"
|
||||
|
||||
do_execsql_test 1.$tok.0.2 {
|
||||
INSERT INTO ss VALUES('5 5 five seven 3 seven i', '2 1 5 0 two 1 i');
|
||||
INSERT INTO ss VALUES('six ix iii 7 i vii iii', 'one seven nine 4 9 1 vi');
|
||||
INSERT INTO ss VALUES('6 viii i five six zero seven', '5 v iii iv iv 3');
|
||||
@ -137,17 +107,22 @@ foreach {tn expr} {
|
||||
4.3 "NEAR(eight nine, 1) OR NEAR(six seven, 1)"
|
||||
} {
|
||||
if {[fts5_expr_ok $expr ss]==0} {
|
||||
do_test 1.$tn.OMITTED { list } [list]
|
||||
do_test 1.$tok.$tn.OMITTED { list } [list]
|
||||
continue
|
||||
}
|
||||
|
||||
set res [fts5_query_data $expr ss ASC ::syn]
|
||||
breakpoint
|
||||
do_execsql_test 1.$tn.[llength $res].asc {
|
||||
set res [fts5_query_data $expr ss ASC ::tclnum_syn]
|
||||
do_execsql_test 1.$tok.$tn.[llength $res].asc.1 {
|
||||
SELECT rowid, fts5_test_poslist(ss), fts5_test_collist(ss) FROM ss($expr)
|
||||
} $res
|
||||
|
||||
do_execsql_test 1.$tok.$tn.[llength $res].asc.2 {
|
||||
SELECT rowid, fts5_test_poslist(ss), fts5_test_collist(ss) FROM ss($expr)
|
||||
ORDER BY rank ASC
|
||||
} $res
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
115
ext/fts5/test/fts5tok1.test
Normal file
115
ext/fts5/test/fts5tok1.test
Normal file
@ -0,0 +1,115 @@
|
||||
# 2016 Jan 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.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
ifcapable !fts5 { finish_test ; return }
|
||||
set ::testprefix fts5tok1
|
||||
|
||||
|
||||
sqlite3_fts5_register_fts5tokenize db
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Simple test cases. Using the default (ascii) tokenizer.
|
||||
#
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5tokenize(ascii);
|
||||
CREATE VIRTUAL TABLE t2 USING fts5tokenize();
|
||||
CREATE VIRTUAL TABLE t3 USING fts5tokenize(
|
||||
ascii, 'separators', 'xyz', tokenchars, ''''
|
||||
);
|
||||
}
|
||||
|
||||
foreach {tn tbl} {1 t1 2 t2 3 t3} {
|
||||
do_execsql_test 1.$tn.1 "SELECT input, * FROM $tbl ('one two three')" {
|
||||
{one two three} one 0 3 0
|
||||
{one two three} two 4 7 1
|
||||
{one two three} three 8 13 2
|
||||
}
|
||||
|
||||
do_execsql_test 1.$tn.2 "
|
||||
SELECT token FROM $tbl WHERE input = 'OnE tWo tHrEe'
|
||||
" {
|
||||
one two three
|
||||
}
|
||||
}
|
||||
|
||||
do_execsql_test 1.4 {
|
||||
SELECT token FROM t3 WHERE input = '1x2x3x'
|
||||
} {1 2 3}
|
||||
|
||||
do_execsql_test 1.5 {
|
||||
SELECT token FROM t1 WHERE input = '1x2x3x'
|
||||
} {1x2x3x}
|
||||
|
||||
do_execsql_test 1.6 {
|
||||
SELECT token FROM t3 WHERE input = '1''2x3x'
|
||||
} {1'2 3}
|
||||
|
||||
do_execsql_test 1.7 {
|
||||
SELECT token FROM t3 WHERE input = ''
|
||||
} {}
|
||||
|
||||
do_execsql_test 1.8 {
|
||||
SELECT token FROM t3 WHERE input = NULL
|
||||
} {}
|
||||
|
||||
do_execsql_test 1.9 {
|
||||
SELECT input, * FROM t3 WHERE input = 123
|
||||
} {123 123 0 3 0}
|
||||
|
||||
do_execsql_test 1.10 {
|
||||
SELECT input, * FROM t1 WHERE input = 'a b c' AND token = 'b';
|
||||
} {
|
||||
{a b c} b 2 3 1
|
||||
}
|
||||
|
||||
do_execsql_test 1.11 {
|
||||
SELECT input, * FROM t1 WHERE token = 'b' AND input = 'a b c';
|
||||
} {
|
||||
{a b c} b 2 3 1
|
||||
}
|
||||
|
||||
do_execsql_test 1.12 {
|
||||
SELECT input, * FROM t1 WHERE input < 'b' AND input = 'a b c';
|
||||
} {
|
||||
{a b c} a 0 1 0
|
||||
{a b c} b 2 3 1
|
||||
{a b c} c 4 5 2
|
||||
}
|
||||
|
||||
do_execsql_test 1.13.1 {
|
||||
CREATE TABLE c1(x);
|
||||
INSERT INTO c1(x) VALUES('a b c');
|
||||
INSERT INTO c1(x) VALUES('d e f');
|
||||
}
|
||||
do_execsql_test 1.13.2 {
|
||||
SELECT c1.*, input, t1.* FROM c1, t1 WHERE input = x AND c1.rowid=t1.rowid;
|
||||
} {
|
||||
{a b c} {a b c} a 0 1 0
|
||||
{d e f} {d e f} e 2 3 1
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Error cases.
|
||||
#
|
||||
do_catchsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE tX USING fts5tokenize(nosuchtokenizer);
|
||||
} {1 {vtable constructor failed: tX}}
|
||||
|
||||
do_catchsql_test 2.1 {
|
||||
CREATE VIRTUAL TABLE t4 USING fts5tokenize;
|
||||
SELECT * FROM t4;
|
||||
} {1 {SQL logic error or missing database}}
|
||||
|
||||
|
||||
finish_test
|
47
ext/fts5/test/fts5tok2.test
Normal file
47
ext/fts5/test/fts5tok2.test
Normal file
@ -0,0 +1,47 @@
|
||||
# 2016 Jan 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.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
ifcapable !fts5||!fts3 { finish_test ; return }
|
||||
set ::testprefix fts5tok2
|
||||
|
||||
sqlite3_fts5_register_fts5tokenize db
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Simple test cases. Using the default (ascii) tokenizer.
|
||||
#
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t5 USING fts5tokenize(unicode61);
|
||||
CREATE VIRTUAL TABLE t3 USING fts3tokenize(unicode61);
|
||||
}
|
||||
|
||||
do_test 1.1 {
|
||||
array unset -nocomplain A
|
||||
|
||||
for {set i 1} {$i < 65536} {incr i} {
|
||||
set input [format "abc%cxyz" $i]
|
||||
set expect [execsql {
|
||||
SELECT input, token, start, end FROM t3 WHERE input=$input
|
||||
}]
|
||||
|
||||
incr A([llength $expect])
|
||||
|
||||
set res [execsql {
|
||||
SELECT input, token, start, end FROM t5($input)
|
||||
}]
|
||||
if {$res != $expect} {error "failed at i=$i"}
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test 1.1.nTokenChars=$A(4).nSeparators=$A(8) {} {}
|
||||
|
||||
finish_test
|
121
ext/fts5/test/fts5update.test
Normal file
121
ext/fts5/test/fts5update.test
Normal file
@ -0,0 +1,121 @@
|
||||
# 2016 Jan 16
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#*************************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is testing the FTS5 module.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5update
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
set docs {
|
||||
"eight zero iv eight 7" "ix one 8 one three ii one"
|
||||
"1 9 9 three viii" "5 zero ii 6 nine ix 3"
|
||||
"3 zero 5 2 seven nine" "two eight viii eight 1"
|
||||
"4 six two 5 9 vii" "viii ii four 8 i i iv"
|
||||
"vii 0 iv seven 7 viii" "five 1 nine vi seven"
|
||||
"1 zero zero iii 1" "one one six 6 nine seven"
|
||||
"one v 4 zero 4 iii ii" "2 3 eight six ix"
|
||||
"six iv 7 three 5" "ix zero 0 8 ii 7 3"
|
||||
"four six nine 2 vii 3" "five viii 5 8 0 7"
|
||||
}
|
||||
|
||||
foreach_detail_mode $::testprefix {
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, detail=%DETAIL%);
|
||||
} {}
|
||||
|
||||
do_test 1.1 {
|
||||
foreach {a b} $docs {
|
||||
execsql {INSERT INTO t1 VALUES($a, $b)}
|
||||
}
|
||||
} {}
|
||||
|
||||
proc update {iRowid iA iB} {
|
||||
set a [lindex $::docs $iA]
|
||||
set b [lindex $::docs $iB]
|
||||
execsql { UPDATE t1 SET a=$a, b=$b WHERE rowid=$iRowid }
|
||||
}
|
||||
|
||||
set nDoc [llength $::docs]
|
||||
foreach n {1 5 10 50 100} {
|
||||
do_test 1.2.$n {
|
||||
execsql BEGIN
|
||||
for {set i 1} {$i <= 1000} {incr i} {
|
||||
set iRowid [expr {int(rand() * ($nDoc/2)) + 1}]
|
||||
set iA [expr {int(rand() * $nDoc)}]
|
||||
set iB [expr {int(rand() * $nDoc)}]
|
||||
update $iRowid $iA $iB
|
||||
|
||||
if {($i % $n)==0} {
|
||||
execsql { COMMIT; BEGIN }
|
||||
}
|
||||
|
||||
if {($i % $n)==100} {
|
||||
execsql { INSERT INTO t1(t1) VALUES('integrity-check') }
|
||||
}
|
||||
}
|
||||
execsql COMMIT
|
||||
execsql { INSERT INTO t1(t1) VALUES('integrity-check') }
|
||||
} {}
|
||||
}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
UPDATE t1 SET a=a AND b=b;
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
do_test 1.4 {
|
||||
execsql { INSERT INTO t1(t1, rank) VALUES('pgsz', 32) }
|
||||
for {set i 0} {$i < 50} {incr i} {
|
||||
execsql { UPDATE t1 SET a=a AND b=b }
|
||||
execsql { INSERT INTO t1(t1) VALUES('integrity-check') }
|
||||
}
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Lots of deletes/inserts of the same document with the same rowid.
|
||||
#
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE x2 USING fts5(x, detail=%DETAIL%);
|
||||
INSERT INTO x2(x2, rank) VALUES('crisismerge', 2);
|
||||
INSERT INTO x2 VALUES('a b c');
|
||||
INSERT INTO x2 VALUES('a b c');
|
||||
}
|
||||
do_test 2.1 {
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
execsql { DELETE FROM x2 WHERE rowid = 2 }
|
||||
execsql { INSERT INTO x2(rowid, x) VALUES(2, 'a b c') }
|
||||
}
|
||||
} {}
|
||||
do_execsql_test 2.1.integrity {
|
||||
INSERT INTO x2(x2) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
do_test 2.2 {
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
execsql { UPDATE x2 SET x=x WHERE rowid=2 }
|
||||
}
|
||||
} {}
|
||||
do_execsql_test 2.2.integrity {
|
||||
INSERT INTO x2(x2) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
}
|
||||
finish_test
|
||||
|
||||
|
@ -57,6 +57,8 @@ proc row_to_col {L} {
|
||||
set ret
|
||||
}
|
||||
|
||||
if 1 {
|
||||
|
||||
do_execsql_test 1.1.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(one, prefix=1, detail=%DETAIL%);
|
||||
CREATE VIRTUAL TABLE v1 USING fts5vocab(t1, 'row');
|
||||
@ -391,5 +393,57 @@ if {![detail_is_none]} {
|
||||
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test the fts5vocab tables response to a specific types of corruption:
|
||||
# where the fts5 index contains hits for columns that do not exist.
|
||||
#
|
||||
do_execsql_test 8.0 {
|
||||
CREATE VIRTUAL TABLE x1 USING fts5(a, b, c, detail=%DETAIL%);
|
||||
INSERT INTO x1 VALUES('a b c', 'd e f', 'g h i');
|
||||
INSERT INTO x1 VALUES('g h i', 'a b c', 'd e f');
|
||||
INSERT INTO x1 VALUES('d e f', 'g h i', 'a b c');
|
||||
CREATE VIRTUAL TABLE x1_r USING fts5vocab(x1, row);
|
||||
CREATE VIRTUAL TABLE x1_c USING fts5vocab(x1, col);
|
||||
}
|
||||
|
||||
set resr [star_from_row {a 3 3 b 3 3 c 3 3 d 3 3 e 3 3 f 3 3 g 3 3 h 3 3 i 3 3}]
|
||||
set resc [star_from_col {
|
||||
a a 1 1 a b 1 1 a c 1 1 b a 1 1
|
||||
b b 1 1 b c 1 1 c a 1 1 c b 1 1
|
||||
c c 1 1 d a 1 1 d b 1 1 d c 1 1
|
||||
e a 1 1 e b 1 1 e c 1 1 f a 1 1
|
||||
f b 1 1 f c 1 1 g a 1 1 g b 1 1
|
||||
g c 1 1 h a 1 1 h b 1 1 h c 1 1
|
||||
i a 1 1 i b 1 1 i c 1 1
|
||||
}]
|
||||
if {[detail_is_none]} { set resc [row_to_col $resr] }
|
||||
|
||||
do_execsql_test 8.1.1 { SELECT * FROM x1_r; } $resr
|
||||
do_execsql_test 8.1.2 { SELECT * FROM x1_c } $resc
|
||||
|
||||
do_execsql_test 8.2 {
|
||||
PRAGMA writable_schema = 1;
|
||||
UPDATE sqlite_master
|
||||
SET sql = 'CREATE VIRTUAL TABLE x1 USING fts5(a, detail=%DETAIL%)'
|
||||
WHERE name = 'x1';
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
sqlite3_fts5_may_be_corrupt 1
|
||||
|
||||
do_execsql_test 8.2.1 { SELECT * FROM x1_r } $resr
|
||||
|
||||
if {[detail_is_none]} {
|
||||
do_execsql_test 8.2.2 { SELECT * FROM x1_c } $resc
|
||||
} else {
|
||||
do_catchsql_test 8.2.2 {
|
||||
SELECT * FROM x1_c
|
||||
} {1 {database disk image is malformed}}
|
||||
}
|
||||
|
||||
sqlite3_fts5_may_be_corrupt 0
|
||||
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
3
main.mk
3
main.mk
@ -338,7 +338,8 @@ TESTSRC += \
|
||||
$(TOP)/ext/misc/wholenumber.c \
|
||||
$(TOP)/ext/misc/vfslog.c \
|
||||
$(TOP)/ext/fts5/fts5_tcl.c \
|
||||
$(TOP)/ext/fts5/fts5_test_mi.c
|
||||
$(TOP)/ext/fts5/fts5_test_mi.c \
|
||||
$(TOP)/ext/fts5/fts5_test_tok.c
|
||||
|
||||
|
||||
#TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c
|
||||
|
119
manifest
119
manifest
@ -1,8 +1,8 @@
|
||||
C Merge\sthe\slatest\sfixes\sand\senhancements\sfrom\strunk.
|
||||
D 2016-01-14T14:48:17.709
|
||||
F Makefile.in c61147c9afcc7a058a3a88876a5b9344fce55353
|
||||
C Merge\srecent\senhancements\sfrom\strunk.
|
||||
D 2016-01-20T11:33:37.587
|
||||
F Makefile.in a47ec69daac0cdfe3413e50ceea6dc8da3dc4ecb
|
||||
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
||||
F Makefile.msc d35026ada75f21433cd34be90dc3865972cea6df
|
||||
F Makefile.msc b4d0270955b3f102214b2242f2a1b802c22ee21b
|
||||
F README.md 8ecc12493ff9f820cdea6520a9016001cb2e59b7
|
||||
F VERSION 866588d1edf0ccb5b0d33896974338f97564f719
|
||||
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
|
||||
@ -97,24 +97,25 @@ F ext/fts3/unicode/mkunicode.tcl 95cf7ec186e48d4985e433ff8a1c89090a774252
|
||||
F ext/fts3/unicode/parseunicode.tcl da577d1384810fb4e2b209bf3313074353193e95
|
||||
F ext/fts5/extract_api_docs.tcl a36e54ec777172ddd3f9a88daf593b00848368e0
|
||||
F ext/fts5/fts5.h ff9c2782e8ed890b0de2f697a8d63971939e70c7
|
||||
F ext/fts5/fts5Int.h 6c5a332e6add01dd69166a252d1818fb75c42a08
|
||||
F ext/fts5/fts5Int.h 5599703af9c13512900a9f22fec39d48078d619d
|
||||
F ext/fts5/fts5_aux.c 2dafc3aee0c70d643140c77d8d70daffa51a9e9e
|
||||
F ext/fts5/fts5_buffer.c 87204c8b3b8bc62b27376eab09b74d6d5acc41f1
|
||||
F ext/fts5/fts5_config.c 9c243d04ac0ca997d2d2e2252891f2a10fbd7217
|
||||
F ext/fts5/fts5_expr.c 510db45967ca359f64f2ba2c707ab57d740cad56
|
||||
F ext/fts5/fts5_buffer.c 7d3f6f01f8fdc45204e6a33925ef8478a67d28dd
|
||||
F ext/fts5/fts5_config.c 0c384ebdd23fd055e2e50a93277b8d59da538238
|
||||
F ext/fts5/fts5_expr.c 4ab4504c54bbe24689c83411d8588f4ec99136e9
|
||||
F ext/fts5/fts5_hash.c 1b113977296cf4212c6ec667d5e3f2bd18036955
|
||||
F ext/fts5/fts5_index.c aa798d3a839847fd351b3d0f49520f190e57c2e3
|
||||
F ext/fts5/fts5_main.c 488ceecdb4400ecc6a3d3b2247cedef153955388
|
||||
F ext/fts5/fts5_storage.c f7b2d330dd7b29a9f4da09f6d85879ca8c41b2e8
|
||||
F ext/fts5/fts5_tcl.c 18e9382d8cdad4c05b49559c68494968b9b4a4fb
|
||||
F ext/fts5/fts5_index.c 716c301835a122ba36910b4f821c87d26ae9a5d9
|
||||
F ext/fts5/fts5_main.c 833db0a3df10ab26e0221a9baa40cf871c450df3
|
||||
F ext/fts5/fts5_storage.c fb2eaec3aa954b680d43096dc539f8270bd6390e
|
||||
F ext/fts5/fts5_tcl.c f8731e0508299bd43f1a2eff7dbeaac870768966
|
||||
F ext/fts5/fts5_test_mi.c 1ec66ffdf7632077fbd773b7a6df5153272ec070
|
||||
F ext/fts5/fts5_test_tok.c db08af63673c3a7d39f053b36fd6e065017706be
|
||||
F ext/fts5/fts5_tokenize.c 504984ac6993323247221eebe3cd55bead01b5f8
|
||||
F ext/fts5/fts5_unicode2.c 78273fbd588d1d9bd0a7e4e0ccc9207348bae33c
|
||||
F ext/fts5/fts5_varint.c 3f86ce09cab152e3d45490d7586b7ed2e40c13f1
|
||||
F ext/fts5/fts5_vocab.c da64ecbd217625980a1721fbd588a1e4118a51b6
|
||||
F ext/fts5/fts5_vocab.c ee6df1a3be103414d7b7af833ae1885c7b83a9d0
|
||||
F ext/fts5/fts5parse.y 1647eba089b9b3fc058b4dc989d9da87d15b9580
|
||||
F ext/fts5/mkportersteps.tcl 5acf962d2e0074f701620bb5308155fa1e4a63ba
|
||||
F ext/fts5/test/fts5_common.tcl 393882afb225a21edf033043bbf936951e9198c1
|
||||
F ext/fts5/test/fts5_common.tcl 6d0d74b695c4be055a8ba1dd807f22a2abc95b5e
|
||||
F ext/fts5/test/fts5aa.test 7e814df4a0e6c22a6fe2d84f210fdc0b5068a084
|
||||
F ext/fts5/test/fts5ab.test 30325a89453280160106be411bba3acf138e6d1b
|
||||
F ext/fts5/test/fts5ac.test d5073ca7bd2d9fe8aab0c82c6c75a7e4b0d70ced
|
||||
@ -132,8 +133,9 @@ F ext/fts5/test/fts5auto.test 401c20e89f1114d733b94809be1e6f893e16c09e
|
||||
F ext/fts5/test/fts5aux.test 8c687c948cc98e9a94be014df7d518acc1b3b74f
|
||||
F ext/fts5/test/fts5auxdata.test 141a7cbffcceb1bd2799b4b29c183ff8780d586e
|
||||
F ext/fts5/test/fts5bigpl.test 04ee0d7eebbebf17c31f5a0b5c5f9494eac3a0cb
|
||||
F ext/fts5/test/fts5bigtok.test 981b2790f6fa02773c889bd35d42c6b97f80f0f4
|
||||
F ext/fts5/test/fts5columnsize.test a8cfef21ffa1c264b9f670a7d94eeaccb5341c07
|
||||
F ext/fts5/test/fts5config.test 42c1336cc6ed33d7e9c4a05dbce81721b765e7d0
|
||||
F ext/fts5/test/fts5config.test 83941309b94d002ed6f55d9cd814e0353c9ae013
|
||||
F ext/fts5/test/fts5conflict.test 26f4e46c4d31e16221794832a990dc4e30e18de5
|
||||
F ext/fts5/test/fts5content.test 9a952c95518a14182dc3b59e3c8fa71cda82a4e1
|
||||
F ext/fts5/test/fts5corrupt.test c2ad090192708150d50d961278df10ae7a4b8b62
|
||||
@ -147,16 +149,18 @@ F ext/fts5/test/fts5eb.test 021aa80b7ac09b964249aa32ced9ee908703e4aa
|
||||
F ext/fts5/test/fts5fault1.test 4b39c47ca3544615daa8a2f733b911fa08022c77
|
||||
F ext/fts5/test/fts5fault2.test 28c36c843bb39ae855ba79827417ecc37f114341
|
||||
F ext/fts5/test/fts5fault3.test d6e9577d4312e331a913c72931bf131704efc8f3
|
||||
F ext/fts5/test/fts5fault4.test 4864f2b5c2c083440dbe85aff60897bc1aa04603
|
||||
F ext/fts5/test/fts5fault5.test f2b8645053d48982e8979749e93994c43011c118
|
||||
F ext/fts5/test/fts5fault4.test 532b6dacb963016cbf7003196bd87fb366540277
|
||||
F ext/fts5/test/fts5fault5.test 10c13a783de3f42a21e3e53e123b62ed0c3a1618
|
||||
F ext/fts5/test/fts5fault6.test 9682664d679643ac6736e90c225526cc84073cda
|
||||
F ext/fts5/test/fts5fault7.test 01be274bfc8d9bf22451a3bf5892e9399d044f1b
|
||||
F ext/fts5/test/fts5fault8.test aeb4717b7b293678bc4d2f3c0159206a525375d9
|
||||
F ext/fts5/test/fts5fault8.test f2d8a2b673a5f72ca1fa0e85bdbfb2041ffd347d
|
||||
F ext/fts5/test/fts5fault9.test e10e395428a9ea0596ebe752ff7123d16ab78e08
|
||||
F ext/fts5/test/fts5full.test 6f6143af0c6700501d9fd597189dfab1555bb741
|
||||
F ext/fts5/test/fts5hash.test 7cf4607b8657c383f0b520668a99971e95d8b139
|
||||
F ext/fts5/test/fts5integrity.test 87db5d4e7da0ce04a1dcba5ba91658673c997a65
|
||||
F ext/fts5/test/fts5hash.test 00668f6fa9b9bffbd7c1be29f408aa2bdade0451
|
||||
F ext/fts5/test/fts5integrity.test f5e4f8d284385875068ad0f3e894ce43e9de835d
|
||||
F ext/fts5/test/fts5matchinfo.test 86569026d20f1ed748236587ce798de8a96615f1
|
||||
F ext/fts5/test/fts5merge.test 8f3cdba2ec9c5e7e568246e81b700ad37f764367
|
||||
F ext/fts5/test/fts5merge2.test c0cb66eb38a41c26cc5848fb9e50093e0f59ac93
|
||||
F ext/fts5/test/fts5near.test b214cddb1c1f1bddf45c75af768f20145f7e71cc
|
||||
F ext/fts5/test/fts5onepass.test 7ed9608e258132cb8d55e7c479b08676ad68810c
|
||||
F ext/fts5/test/fts5optimize.test 42741e7c085ee0a1276140a752d4407d97c2c9f5
|
||||
@ -169,18 +173,21 @@ F ext/fts5/test/fts5query.test f5ec25f5f2fbb70033424113cdffc101b1985a40
|
||||
F ext/fts5/test/fts5rank.test 7e9e64eac7245637f6f2033aec4b292aaf611aab
|
||||
F ext/fts5/test/fts5rebuild.test 03935f617ace91ed23a6099c7c74d905227ff29b
|
||||
F ext/fts5/test/fts5restart.test c17728fdea26e7d0f617d22ad5b4b2862b994c17
|
||||
F ext/fts5/test/fts5rowid.test 400384798349d658eaf06aefa1e364957d5d4821
|
||||
F ext/fts5/test/fts5rowid.test 16908a99d6efc9ba21081b4f2b86b3fc699839a6
|
||||
F ext/fts5/test/fts5simple.test 2bc6451cbe887a9215f5b14ae307c70d850344c9
|
||||
F ext/fts5/test/fts5simple2.test 843f1f7fe439ff32bf74f4fd6430632f9636ef3a
|
||||
F ext/fts5/test/fts5synonym.test cf88c0a56d5ea9591e3939ef1f6e294f7f2d0671
|
||||
F ext/fts5/test/fts5synonym2.test d2d9099d9d105b55ea03fd52d61ae2847d534129
|
||||
F ext/fts5/test/fts5simple2.test 98377ae1ff7749a42c21fe1a139c1ed312522c46
|
||||
F ext/fts5/test/fts5synonym.test 6475d189c2e20d60795808f83e36bf9318708d48
|
||||
F ext/fts5/test/fts5synonym2.test eadb00c73ef0653258873e756b7e9102e0687539
|
||||
F ext/fts5/test/fts5tok1.test beb894c6f3468f10a574302f69ebe4436b0287c7
|
||||
F ext/fts5/test/fts5tok2.test dcacb32d4a2a3f0dd3215d4a3987f78ae4be21a2
|
||||
F ext/fts5/test/fts5tokenizer.test ea4df698b35cc427ebf2ba22829d0e28386d8c89
|
||||
F ext/fts5/test/fts5unicode.test fbef8d8a3b4b88470536cc57604a82ca52e51841
|
||||
F ext/fts5/test/fts5unicode2.test c1dd890ba32b7609adba78e420faa847abe43b59
|
||||
F ext/fts5/test/fts5unicode3.test 35c3d02aa7acf7d43d8de3bfe32c15ba96e8928e
|
||||
F ext/fts5/test/fts5unindexed.test e9539d5b78c677315e7ed8ea911d4fd25437c680
|
||||
F ext/fts5/test/fts5update.test 57c7012a7919889048947addae10e0613df45529
|
||||
F ext/fts5/test/fts5version.test 978f59541d8cef7e8591f8be2115ec5ccb863e2e
|
||||
F ext/fts5/test/fts5vocab.test e4b12f238f113795615ba6343b63fb326d6a360e
|
||||
F ext/fts5/test/fts5vocab.test 480d780aa6b699816c5066225fbd86f3a0239477
|
||||
F ext/fts5/tool/fts5speed.tcl aaee41894b552df8fbf8616aad003b2ea9ba3221
|
||||
F ext/fts5/tool/fts5txt2db.tcl c374c4c4797e8cdfadabdfaeeb5412dcd6686e84
|
||||
F ext/fts5/tool/loadfts5.tcl 4cc2d6af43b58d4fac05bc4fdabd0e5862c3b2c1
|
||||
@ -282,7 +289,7 @@ F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
|
||||
F main.mk 727b093cca39460048ae9948b283063857b04402
|
||||
F main.mk ad2c0fe02599cf4d58d13776ee57be1e53883909
|
||||
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
|
||||
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
|
||||
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
|
||||
@ -295,23 +302,23 @@ F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
|
||||
F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
|
||||
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
|
||||
F src/alter.c 9d649e46c780166e416fb11dbd23f8d49aab8267
|
||||
F src/analyze.c 977bd50c751bb939ef52917892e12bedbfcea7ce
|
||||
F src/attach.c e944d0052b577703b9b83aac1638452ff42a8395
|
||||
F src/analyze.c 0043d3e501f04297fed2bb50b488bc08d5c39f36
|
||||
F src/attach.c 07b3a34a1702dce92a7f1d3888c0c06222b63760
|
||||
F src/auth.c b56c78ebe40a2110fd361379f7e8162d23f92240
|
||||
F src/backup.c 2869a76c03eb393ee795416e2387005553df72bc
|
||||
F src/bitvec.c 1a78d450a17c5016710eec900bedfc5729bf9bdf
|
||||
F src/btmutex.c bc87dd3b062cc26edfe79918de2200ccb8d41e73
|
||||
F src/btree.c 5d93e2477acb99d50a8b045f2e26a0be3d7751fe
|
||||
F src/btree.c f224ae877fde69d1a9d430f502edaf8502752dbe
|
||||
F src/btree.h 68ef301795e00cdf1d3ab93abc44a43b7fe771e0
|
||||
F src/btreeInt.h c18b7d2a3494695133e4e60ee36061d37f45d9a5
|
||||
F src/build.c 9d497ff4bf3c82cecb520436e0e9963785627583
|
||||
F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0
|
||||
F src/build.c 31af80bba31ac159967951ef58f3144cc7db9d70
|
||||
F src/callback.c 29ae4faba226c7ebb9aee93016b5ce8a8f071261
|
||||
F src/complete.c addcd8160b081131005d5bc2d34adf20c1c5c92f
|
||||
F src/ctime.c 60e135af364d777a9ab41c97e5e89cd224da6198
|
||||
F src/date.c e4655393bb403fa310eef66cc4583d75d4d7fd93
|
||||
F src/date.c 997651e3ee6c2818fbf7fcdb7156cef9eb3ece20
|
||||
F src/dbstat.c ffd63fc8ba7541476ced189b95e95d7f2bc63f78
|
||||
F src/delete.c 86e3940d07fe69a40270c2aaf6ca6c7adf19246c
|
||||
F src/expr.c fe55c489362d1429c364e98c877514f4455f45a6
|
||||
F src/expr.c df0d7c3230d59abd679da22ff5ce4cfd0e3a0e63
|
||||
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
||||
F src/fkey.c e18b3dff7d47c7bcac5ac4fc178a89b9fd322b44
|
||||
F src/func.c ccaf46fa98f795673afbfab73dff7c18db88f3cd
|
||||
@ -319,12 +326,12 @@ F src/global.c bd5a0af3f30b0c01be6db756c626cd3c33a3d260
|
||||
F src/hash.c 4263fbc955f26c2e8cdc0cf214bc42435aa4e4f5
|
||||
F src/hash.h c8f3c31722cf3277d03713909761e152a5b81094
|
||||
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
|
||||
F src/insert.c a2302c96652242b2d871445c9975a7a849316dcf
|
||||
F src/insert.c 0be969e32a28107a4a7008ea4fe7cb15ff47b0ef
|
||||
F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
|
||||
F src/legacy.c ba1863ea58c4c840335a84ec276fc2b25e22bc4e
|
||||
F src/loadext.c 84996d7d70a605597d79c1f1d7b2012a5fd34f2b
|
||||
F src/main.c a88a45514be79e5f48b5cf4a147956f267425fa7
|
||||
F src/malloc.c 8f787669e79de26efc42272b5797bc00fff527c6
|
||||
F src/main.c c7d4a1858446db5f611891ddce62572b1fdec040
|
||||
F src/malloc.c b67c26c359c13836d370350b3f43d228dff5b360
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c 6919bcf12f221868ea066eec27e579fed95ce98b
|
||||
F src/mem2.c f1940d9e91948dd6a908fbb9ce3835c36b5d83c3
|
||||
@ -351,19 +358,19 @@ F src/parse.y caad1e98edeca6960493d0c60d31b76820dd7776
|
||||
F src/pcache.c 73895411fa6b7bd6f0091212feabbe833b358d23
|
||||
F src/pcache.h 4d0ccaad264d360981ec5e6a2b596d6e85242545
|
||||
F src/pcache1.c 72f644dc9e1468c72922eff5904048427b817051
|
||||
F src/pragma.c f3e7147299ca05ef4304a36f1fd6e002729c72c6
|
||||
F src/pragma.c ea290193369faa0a26ae2f924e7b86289b4a7987
|
||||
F src/pragma.h 64c78a648751b9f4f297276c4eb7507b14b4628c
|
||||
F src/prepare.c 82e5db1013846a819f198336fed72c44c974e7b1
|
||||
F src/prepare.c 74855ddbdfad6a1c4a4d5c4b0913ebb01174ba19
|
||||
F src/printf.c af589a27b7d40f6f4f704e9eea99f02f18ad6d32
|
||||
F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
|
||||
F src/resolve.c a83b41104e6ff69855d03cd0aaa09e93927ec39f
|
||||
F src/resolve.c 9f7ce3a3c087afb7597b7c916c99126ff3f12f0c
|
||||
F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e
|
||||
F src/select.c 5b0f2aa9f73ec7b65d1711d485471854d5bad23c
|
||||
F src/select.c 718954db86277d696c520fe671148db1e9c4ed3c
|
||||
F src/shell.c 01e109c27300379b1c35b254cd294cde635f0179
|
||||
F src/sqlite.h.in bb645ccc4090a6e95e7b5d92818f46e41033d076
|
||||
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
|
||||
F src/sqlite3ext.h dfbe62ffd95b99afe2140d8c35b180d11924072d
|
||||
F src/sqliteInt.h 09f70fe715a74ea0048ae05526f2a1b6e3a3b6a2
|
||||
F src/sqliteInt.h cf3ca8ec73daef3b744d41663e124c2275fb6f26
|
||||
F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46
|
||||
F src/status.c 70912d7be68e9e2dbc4010c93d344af61d4c59ba
|
||||
F src/table.c 51b46b2a62d1b3a959633d593b89bab5e2c9155e
|
||||
@ -418,29 +425,29 @@ F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/threads.c bbfb74450643cb5372a43ad4f6cffd7e9dfcecb0
|
||||
F src/tokenize.c 5606871a377f390af7040ec3c12e0d183512d785
|
||||
F src/treeview.c 78842e90c1f71269e7a73a1d4221b6fe360bab66
|
||||
F src/trigger.c de3ed31ad3218a20d7d7e18bf1b3b734e78bda66
|
||||
F src/trigger.c 056e51182a3677434423e3be0c74e61b90b4a663
|
||||
F src/update.c d8d675aa299336ac086ad2039d7e812cd6237db0
|
||||
F src/utf.c 32d7f82aa921322f3e1c956f4b58f019ebd2c6b3
|
||||
F src/util.c e802e8e311a0d6c48cd1b3e89db164f6f0248d70
|
||||
F src/vacuum.c 2ddd5cad2a7b9cef7f9e431b8c7771634c6b1701
|
||||
F src/vdbe.c 9b4da74dbc1554045c69dacbbceb373272d2e0be
|
||||
F src/vdbe.h bfe3f80dba435377cdb64fd917f2529f0f48ab77
|
||||
F src/vdbe.c 1443ca69877410c90ba521220d4336d5ef689cdd
|
||||
F src/vdbe.h 19162d5d75d00ee9e634ad630f2a873e1cf76359
|
||||
F src/vdbeInt.h 1bff4effc71888e3f304e2e6ac7484e39ab78c28
|
||||
F src/vdbeapi.c ab2cb8fe23fb9f3195f1311eaa800495d83b6118
|
||||
F src/vdbeaux.c 30f329b90af08d92739185788c6fbfd1c233b8b4
|
||||
F src/vdbeblob.c cc13eca96b8ec51b6248de785a1aec5df11f5805
|
||||
F src/vdbemem.c 25b6cfd665b5073480452426e84136edd94140c0
|
||||
F src/vdbeapi.c 75fa852c0d6e7e9c1334aaaa91b6821cba6a713b
|
||||
F src/vdbeaux.c 3781db199a8d9376c9512fb7e1304aae7af67dfa
|
||||
F src/vdbeblob.c 6ccda01a78b3f9d9a0c75f79e7a1150f3fb0cb39
|
||||
F src/vdbemem.c 6c962ee0a77de009dafb6d7f3c7e8bf7b82c47f8
|
||||
F src/vdbesort.c 0971557e5d3c289e46f56a52aed2197c13251de7
|
||||
F src/vdbetrace.c 8befe829faff6d9e6f6e4dee5a7d3f85cc85f1a0
|
||||
F src/vtab.c 2a8b44aa372c33f6154208e7a7f6c44254549806
|
||||
F src/vtab.c 320682cca733115b4cbe71320b5c5eeb1074ebde
|
||||
F src/vxworks.h 974e7d9a98f602d6310d563e1dc4e08f9fc48e47
|
||||
F src/wal.c d21b99fd1458159d0b1ecdccc8ee6ada4fdc4c54
|
||||
F src/wal.h 2f7c831cf3b071fa548bf2d5cac640846a7ff19c
|
||||
F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354
|
||||
F src/where.c bb69654f841ae7af0a20cc6fb8f0ac57901c31be
|
||||
F src/whereInt.h 78b6b4de94db84aecbdc07fe3e38f648eb391e9a
|
||||
F src/wherecode.c dfbfe198e418b01f208b489e088edd230c91a4e7
|
||||
F src/whereexpr.c eebba8340c90de73b3d3bbe8c43b84559b8e6e2c
|
||||
F src/wherecode.c 8dee26eb181ea9daa8b1a4d96f34c0860aaf99bd
|
||||
F src/whereexpr.c 197a448b52073aee43eca3a2233fc113369eb2d4
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
|
||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||
@ -784,7 +791,7 @@ F test/fuzzerfault.test 8792cd77fd5bce765b05d0c8e01b9edcf8af8536
|
||||
F test/genesis.tcl 1e2e2e8e5cc4058549a154ff1892fe5c9de19f98
|
||||
F test/hexlit.test d7b0a5f41123df1e43985b91b8b2e70f95282d21
|
||||
F test/hidden.test 23c1393a79e846d68fd902d72c85d5e5dcf98711
|
||||
F test/hook.test aa41c095d26822b8a51aa4c82904a14347961be6
|
||||
F test/hook.test 40523db3aa76d62bda71c26f824fa0eabc420f0e
|
||||
F test/icu.test 70df4faca133254c042d02ae342c0a141f2663f4
|
||||
F test/ieee754.test 806fc0ce7f305f57e3331eaceeddcfec9339e607
|
||||
F test/imposter1.test c3f1db2d3db2c24611a6596a3fc0ffc14f1466c8
|
||||
@ -1098,7 +1105,7 @@ F test/thread2.test f35d2106452b77523b3a2b7d1dcde2e5ee8f9e46
|
||||
F test/thread_common.tcl 334639cadcb9f912bf82aa73f49efd5282e6cadd
|
||||
F test/threadtest1.c 6029d9c5567db28e6dc908a0c63099c3ba6c383b
|
||||
F test/threadtest2.c a70a8e94bef23339d34226eb9521015ef99f4df8
|
||||
F test/threadtest3.c 7ca82330041456afa52e4968196bb0867371f91b
|
||||
F test/threadtest3.c 38a612ea62854349ed66372f330a40d73c5cf956
|
||||
F test/threadtest4.c c1e67136ceb6c7ec8184e56ac61db28f96bd2925
|
||||
F test/tkt-02a8e81d44.test 6c80d9c7514e2a42d4918bf87bf6bc54f379110c
|
||||
F test/tkt-26ff0c2d1e.test 888324e751512972c6e0d1a09df740d8f5aaf660
|
||||
@ -1433,7 +1440,7 @@ F tool/vdbe_profile.tcl 246d0da094856d72d2c12efec03250d71639d19f
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
P c4a858b228a164be2f89f5b01833f0b5e0d7735b 8dedff3b9ac3e6bf9c131fee19f7d26dc1ebd61f
|
||||
R 99385ae0fdf1facdff7c072dfc1f2a57
|
||||
P 007e5c6df60f9743ac6914332f59925e4a7a861c 18d61c8e40ed1466b6a3a2f53bf0eeb09687c20e
|
||||
R 062849fc5c2e599b32ec755e50c1d8e3
|
||||
U drh
|
||||
Z c131c7ae38d871cc572c248d8ac0f193
|
||||
Z 10a2fcf1cf68156c27545982ac8f140d
|
||||
|
@ -1 +1 @@
|
||||
007e5c6df60f9743ac6914332f59925e4a7a861c
|
||||
327af5f644a49b2f41d5456958f9d61a2b704e1c
|
@ -478,8 +478,7 @@ static const FuncDef statInitFuncdef = {
|
||||
SQLITE_UTF8, /* funcFlags */
|
||||
0, /* pUserData */
|
||||
0, /* pNext */
|
||||
statInit, /* xFunc */
|
||||
0, /* xStep */
|
||||
statInit, /* xSFunc */
|
||||
0, /* xFinalize */
|
||||
"stat_init", /* zName */
|
||||
0, /* pHash */
|
||||
@ -779,8 +778,7 @@ static const FuncDef statPushFuncdef = {
|
||||
SQLITE_UTF8, /* funcFlags */
|
||||
0, /* pUserData */
|
||||
0, /* pNext */
|
||||
statPush, /* xFunc */
|
||||
0, /* xStep */
|
||||
statPush, /* xSFunc */
|
||||
0, /* xFinalize */
|
||||
"stat_push", /* zName */
|
||||
0, /* pHash */
|
||||
@ -926,8 +924,7 @@ static const FuncDef statGetFuncdef = {
|
||||
SQLITE_UTF8, /* funcFlags */
|
||||
0, /* pUserData */
|
||||
0, /* pNext */
|
||||
statGet, /* xFunc */
|
||||
0, /* xStep */
|
||||
statGet, /* xSFunc */
|
||||
0, /* xFinalize */
|
||||
"stat_get", /* zName */
|
||||
0, /* pHash */
|
||||
@ -943,8 +940,8 @@ static void callStatGet(Vdbe *v, int regStat4, int iParam, int regOut){
|
||||
#else
|
||||
UNUSED_PARAMETER( iParam );
|
||||
#endif
|
||||
sqlite3VdbeAddOp3(v, OP_Function0, 0, regStat4, regOut);
|
||||
sqlite3VdbeChangeP4(v, -1, (char*)&statGetFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeAddOp4(v, OP_Function0, 0, regStat4, regOut,
|
||||
(char*)&statGetFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, 1 + IsStat34);
|
||||
}
|
||||
|
||||
@ -1098,8 +1095,8 @@ static void analyzeOneTable(
|
||||
#endif
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat4+1);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regStat4+2);
|
||||
sqlite3VdbeAddOp3(v, OP_Function0, 0, regStat4+1, regStat4);
|
||||
sqlite3VdbeChangeP4(v, -1, (char*)&statInitFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeAddOp4(v, OP_Function0, 0, regStat4+1, regStat4,
|
||||
(char*)&statInitFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, 2+IsStat34);
|
||||
|
||||
/* Implementation of the following:
|
||||
@ -1195,8 +1192,8 @@ static void analyzeOneTable(
|
||||
}
|
||||
#endif
|
||||
assert( regChng==(regStat4+1) );
|
||||
sqlite3VdbeAddOp3(v, OP_Function0, 1, regStat4, regTemp);
|
||||
sqlite3VdbeChangeP4(v, -1, (char*)&statPushFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeAddOp4(v, OP_Function0, 1, regStat4, regTemp,
|
||||
(char*)&statPushFuncdef, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, 2+IsStat34);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v);
|
||||
|
||||
|
12
src/attach.c
12
src/attach.c
@ -359,11 +359,11 @@ static void codeAttach(
|
||||
|
||||
assert( v || db->mallocFailed );
|
||||
if( v ){
|
||||
sqlite3VdbeAddOp3(v, OP_Function0, 0, regArgs+3-pFunc->nArg, regArgs+3);
|
||||
sqlite3VdbeAddOp4(v, OP_Function0, 0, regArgs+3-pFunc->nArg, regArgs+3,
|
||||
(char *)pFunc, P4_FUNCDEF);
|
||||
assert( pFunc->nArg==-1 || (pFunc->nArg&0xff)==pFunc->nArg );
|
||||
sqlite3VdbeChangeP5(v, (u8)(pFunc->nArg));
|
||||
sqlite3VdbeChangeP4(v, -1, (char *)pFunc, P4_FUNCDEF);
|
||||
|
||||
|
||||
/* Code an OP_Expire. For an ATTACH statement, set P1 to true (expire this
|
||||
** statement only). For DETACH, set it to false (expire all existing
|
||||
** statements).
|
||||
@ -388,8 +388,7 @@ void sqlite3Detach(Parse *pParse, Expr *pDbname){
|
||||
SQLITE_UTF8, /* funcFlags */
|
||||
0, /* pUserData */
|
||||
0, /* pNext */
|
||||
detachFunc, /* xFunc */
|
||||
0, /* xStep */
|
||||
detachFunc, /* xSFunc */
|
||||
0, /* xFinalize */
|
||||
"sqlite_detach", /* zName */
|
||||
0, /* pHash */
|
||||
@ -409,8 +408,7 @@ void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey){
|
||||
SQLITE_UTF8, /* funcFlags */
|
||||
0, /* pUserData */
|
||||
0, /* pNext */
|
||||
attachFunc, /* xFunc */
|
||||
0, /* xStep */
|
||||
attachFunc, /* xSFunc */
|
||||
0, /* xFinalize */
|
||||
"sqlite_attach", /* zName */
|
||||
0, /* pHash */
|
||||
|
122
src/btree.c
122
src/btree.c
@ -7186,9 +7186,8 @@ static int balance_nonroot(
|
||||
** long be able to find the cells if a pointer to each cell is not saved
|
||||
** first.
|
||||
*/
|
||||
memset(&b.szCell[b.nCell], 0, sizeof(b.szCell[0])*limit);
|
||||
memset(&b.szCell[b.nCell], 0, sizeof(b.szCell[0])*(limit+pOld->nOverflow));
|
||||
if( pOld->nOverflow>0 ){
|
||||
memset(&b.szCell[b.nCell+limit], 0, sizeof(b.szCell[0])*pOld->nOverflow);
|
||||
limit = pOld->aiOvfl[0];
|
||||
for(j=0; j<limit; j++){
|
||||
b.apCell[b.nCell] = aData + (maskPage & get2byteAligned(piCell));
|
||||
@ -8541,6 +8540,14 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){
|
||||
return SQLITE_LOCKED_SHAREDCACHE;
|
||||
}
|
||||
|
||||
/*
|
||||
** It is illegal to drop the sqlite_master table on page 1. But again,
|
||||
** this error is caught long before reaching this point.
|
||||
*/
|
||||
if( NEVER(iTable<2) ){
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
|
||||
rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0);
|
||||
if( rc ) return rc;
|
||||
rc = sqlite3BtreeClearTable(p, iTable, 0);
|
||||
@ -8551,76 +8558,67 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){
|
||||
|
||||
*piMoved = 0;
|
||||
|
||||
if( iTable>1 ){
|
||||
#ifdef SQLITE_OMIT_AUTOVACUUM
|
||||
freePage(pPage, &rc);
|
||||
releasePage(pPage);
|
||||
freePage(pPage, &rc);
|
||||
releasePage(pPage);
|
||||
#else
|
||||
if( pBt->autoVacuum ){
|
||||
Pgno maxRootPgno;
|
||||
sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &maxRootPgno);
|
||||
if( pBt->autoVacuum ){
|
||||
Pgno maxRootPgno;
|
||||
sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &maxRootPgno);
|
||||
|
||||
if( iTable==maxRootPgno ){
|
||||
/* If the table being dropped is the table with the largest root-page
|
||||
** number in the database, put the root page on the free list.
|
||||
*/
|
||||
freePage(pPage, &rc);
|
||||
releasePage(pPage);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
}else{
|
||||
/* The table being dropped does not have the largest root-page
|
||||
** number in the database. So move the page that does into the
|
||||
** gap left by the deleted root-page.
|
||||
*/
|
||||
MemPage *pMove;
|
||||
releasePage(pPage);
|
||||
rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
rc = relocatePage(pBt, pMove, PTRMAP_ROOTPAGE, 0, iTable, 0);
|
||||
releasePage(pMove);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
pMove = 0;
|
||||
rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0);
|
||||
freePage(pMove, &rc);
|
||||
releasePage(pMove);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
*piMoved = maxRootPgno;
|
||||
}
|
||||
|
||||
/* Set the new 'max-root-page' value in the database header. This
|
||||
** is the old value less one, less one more if that happens to
|
||||
** be a root-page number, less one again if that is the
|
||||
** PENDING_BYTE_PAGE.
|
||||
if( iTable==maxRootPgno ){
|
||||
/* If the table being dropped is the table with the largest root-page
|
||||
** number in the database, put the root page on the free list.
|
||||
*/
|
||||
maxRootPgno--;
|
||||
while( maxRootPgno==PENDING_BYTE_PAGE(pBt)
|
||||
|| PTRMAP_ISPAGE(pBt, maxRootPgno) ){
|
||||
maxRootPgno--;
|
||||
}
|
||||
assert( maxRootPgno!=PENDING_BYTE_PAGE(pBt) );
|
||||
|
||||
rc = sqlite3BtreeUpdateMeta(p, 4, maxRootPgno);
|
||||
}else{
|
||||
freePage(pPage, &rc);
|
||||
releasePage(pPage);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
}else{
|
||||
/* The table being dropped does not have the largest root-page
|
||||
** number in the database. So move the page that does into the
|
||||
** gap left by the deleted root-page.
|
||||
*/
|
||||
MemPage *pMove;
|
||||
releasePage(pPage);
|
||||
rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
rc = relocatePage(pBt, pMove, PTRMAP_ROOTPAGE, 0, iTable, 0);
|
||||
releasePage(pMove);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
pMove = 0;
|
||||
rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0);
|
||||
freePage(pMove, &rc);
|
||||
releasePage(pMove);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
*piMoved = maxRootPgno;
|
||||
}
|
||||
#endif
|
||||
}else{
|
||||
/* If sqlite3BtreeDropTable was called on page 1.
|
||||
** This really never should happen except in a corrupt
|
||||
** database.
|
||||
|
||||
/* Set the new 'max-root-page' value in the database header. This
|
||||
** is the old value less one, less one more if that happens to
|
||||
** be a root-page number, less one again if that is the
|
||||
** PENDING_BYTE_PAGE.
|
||||
*/
|
||||
zeroPage(pPage, PTF_INTKEY|PTF_LEAF );
|
||||
maxRootPgno--;
|
||||
while( maxRootPgno==PENDING_BYTE_PAGE(pBt)
|
||||
|| PTRMAP_ISPAGE(pBt, maxRootPgno) ){
|
||||
maxRootPgno--;
|
||||
}
|
||||
assert( maxRootPgno!=PENDING_BYTE_PAGE(pBt) );
|
||||
|
||||
rc = sqlite3BtreeUpdateMeta(p, 4, maxRootPgno);
|
||||
}else{
|
||||
freePage(pPage, &rc);
|
||||
releasePage(pPage);
|
||||
}
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){
|
||||
|
90
src/build.c
90
src/build.c
@ -228,15 +228,19 @@ void sqlite3FinishCoding(Parse *pParse){
|
||||
if( pParse->pAinc!=0 && pParse->nTab==0 ) pParse->nTab = 1;
|
||||
sqlite3VdbeMakeReady(v, pParse);
|
||||
pParse->rc = SQLITE_DONE;
|
||||
pParse->colNamesSet = 0;
|
||||
}else{
|
||||
pParse->rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/* We are done with this Parse object. There is no need to de-initialize it */
|
||||
#if 0
|
||||
pParse->colNamesSet = 0;
|
||||
pParse->nTab = 0;
|
||||
pParse->nMem = 0;
|
||||
pParse->nSet = 0;
|
||||
pParse->nVar = 0;
|
||||
DbMaskZero(pParse->cookieMask);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@ -495,7 +499,6 @@ void sqlite3CollapseDatabaseArray(sqlite3 *db){
|
||||
}
|
||||
j++;
|
||||
}
|
||||
memset(&db->aDb[j], 0, (db->nDb-j)*sizeof(db->aDb[j]));
|
||||
db->nDb = j;
|
||||
if( db->nDb<=2 && db->aDb!=db->aDbStatic ){
|
||||
memcpy(db->aDbStatic, db->aDb, 2*sizeof(db->aDb[0]));
|
||||
@ -758,7 +761,8 @@ int sqlite3TwoPartName(
|
||||
int iDb; /* Database holding the object */
|
||||
sqlite3 *db = pParse->db;
|
||||
|
||||
if( ALWAYS(pName2!=0) && pName2->n>0 ){
|
||||
assert( pName2!=0 );
|
||||
if( pName2->n>0 ){
|
||||
if( db->init.busy ) {
|
||||
sqlite3ErrorMsg(pParse, "corrupt database");
|
||||
return -1;
|
||||
@ -847,62 +851,46 @@ void sqlite3StartTable(
|
||||
int iDb; /* Database number to create the table in */
|
||||
Token *pName; /* Unqualified name of the table to create */
|
||||
|
||||
/* The table or view name to create is passed to this routine via tokens
|
||||
** pName1 and pName2. If the table name was fully qualified, for example:
|
||||
**
|
||||
** CREATE TABLE xxx.yyy (...);
|
||||
**
|
||||
** Then pName1 is set to "xxx" and pName2 "yyy". On the other hand if
|
||||
** the table name is not fully qualified, i.e.:
|
||||
**
|
||||
** CREATE TABLE yyy(...);
|
||||
**
|
||||
** Then pName1 is set to "yyy" and pName2 is "".
|
||||
**
|
||||
** The call below sets the pName pointer to point at the token (pName1 or
|
||||
** pName2) that stores the unqualified table name. The variable iDb is
|
||||
** set to the index of the database that the table or view is to be
|
||||
** created in.
|
||||
*/
|
||||
iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName);
|
||||
if( iDb<0 ) return;
|
||||
if( !OMIT_TEMPDB && isTemp && pName2->n>0 && iDb!=1 ){
|
||||
/* If creating a temp table, the name may not be qualified. Unless
|
||||
** the database name is "temp" anyway. */
|
||||
sqlite3ErrorMsg(pParse, "temporary table name must be unqualified");
|
||||
return;
|
||||
if( db->init.busy && db->init.newTnum==1 ){
|
||||
/* Special case: Parsing the sqlite_master or sqlite_temp_master schema */
|
||||
iDb = db->init.iDb;
|
||||
zName = sqlite3DbStrDup(db, SCHEMA_TABLE(iDb));
|
||||
pName = pName1;
|
||||
}else{
|
||||
/* The common case */
|
||||
iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName);
|
||||
if( iDb<0 ) return;
|
||||
if( !OMIT_TEMPDB && isTemp && pName2->n>0 && iDb!=1 ){
|
||||
/* If creating a temp table, the name may not be qualified. Unless
|
||||
** the database name is "temp" anyway. */
|
||||
sqlite3ErrorMsg(pParse, "temporary table name must be unqualified");
|
||||
return;
|
||||
}
|
||||
if( !OMIT_TEMPDB && isTemp ) iDb = 1;
|
||||
zName = sqlite3NameFromToken(db, pName);
|
||||
}
|
||||
if( !OMIT_TEMPDB && isTemp ) iDb = 1;
|
||||
|
||||
pParse->sNameToken = *pName;
|
||||
zName = sqlite3NameFromToken(db, pName);
|
||||
if( zName==0 ) return;
|
||||
if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
|
||||
goto begin_table_error;
|
||||
}
|
||||
if( db->init.iDb==1 ) isTemp = 1;
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
assert( (isTemp & 1)==isTemp );
|
||||
assert( isTemp==0 || isTemp==1 );
|
||||
assert( isView==0 || isView==1 );
|
||||
{
|
||||
int code;
|
||||
static const u8 aCode[] = {
|
||||
SQLITE_CREATE_TABLE,
|
||||
SQLITE_CREATE_TEMP_TABLE,
|
||||
SQLITE_CREATE_VIEW,
|
||||
SQLITE_CREATE_TEMP_VIEW
|
||||
};
|
||||
char *zDb = db->aDb[iDb].zName;
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0, zDb) ){
|
||||
goto begin_table_error;
|
||||
}
|
||||
if( isView ){
|
||||
if( !OMIT_TEMPDB && isTemp ){
|
||||
code = SQLITE_CREATE_TEMP_VIEW;
|
||||
}else{
|
||||
code = SQLITE_CREATE_VIEW;
|
||||
}
|
||||
}else{
|
||||
if( !OMIT_TEMPDB && isTemp ){
|
||||
code = SQLITE_CREATE_TEMP_TABLE;
|
||||
}else{
|
||||
code = SQLITE_CREATE_TABLE;
|
||||
}
|
||||
}
|
||||
if( !isVirtual && sqlite3AuthCheck(pParse, code, zName, 0, zDb) ){
|
||||
if( !isVirtual && sqlite3AuthCheck(pParse, (int)aCode[isTemp+2*isView],
|
||||
zName, 0, zDb) ){
|
||||
goto begin_table_error;
|
||||
}
|
||||
}
|
||||
@ -1864,9 +1852,13 @@ void sqlite3EndTable(
|
||||
** So do not write to the disk again. Extract the root page number
|
||||
** for the table from the db->init.newTnum field. (The page number
|
||||
** should have been put there by the sqliteOpenCb routine.)
|
||||
**
|
||||
** If the root page number is 1, that means this is the sqlite_master
|
||||
** table itself. So mark it read-only.
|
||||
*/
|
||||
if( db->init.busy ){
|
||||
p->tnum = db->init.newTnum;
|
||||
if( p->tnum==1 ) p->tabFlags |= TF_Readonly;
|
||||
}
|
||||
|
||||
/* Special processing for WITHOUT ROWID Tables */
|
||||
@ -2319,6 +2311,7 @@ void sqlite3RootPageMoved(sqlite3 *db, int iDb, int iFrom, int iTo){
|
||||
static void destroyRootPage(Parse *pParse, int iTable, int iDb){
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
int r1 = sqlite3GetTempReg(pParse);
|
||||
assert( iTable>1 );
|
||||
sqlite3VdbeAddOp3(v, OP_Destroy, iTable, r1, iDb);
|
||||
sqlite3MayAbort(pParse);
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
@ -3717,9 +3710,10 @@ SrcList *sqlite3SrcListAppend(
|
||||
struct SrcList_item *pItem;
|
||||
assert( pDatabase==0 || pTable!=0 ); /* Cannot have C without B */
|
||||
if( pList==0 ){
|
||||
pList = sqlite3DbMallocZero(db, sizeof(SrcList) );
|
||||
pList = sqlite3DbMallocRaw(db, sizeof(SrcList) );
|
||||
if( pList==0 ) return 0;
|
||||
pList->nAlloc = 1;
|
||||
pList->nSrc = 0;
|
||||
}
|
||||
pList = sqlite3SrcListEnlarge(db, pList, 1, pList->nSrc);
|
||||
if( db->mallocFailed ){
|
||||
@ -4122,7 +4116,7 @@ void sqlite3HaltConstraint(
|
||||
sqlite3MayAbort(pParse);
|
||||
}
|
||||
sqlite3VdbeAddOp4(v, OP_Halt, errCode, onError, 0, p4, p4type);
|
||||
if( p5Errmsg ) sqlite3VdbeChangeP5(v, p5Errmsg);
|
||||
sqlite3VdbeChangeP5(v, p5Errmsg);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -243,8 +243,8 @@ CollSeq *sqlite3FindCollSeq(
|
||||
** 5: UTF16 byte order conversion required - argument count matches exactly
|
||||
** 6: Perfect match: encoding and argument count match exactly.
|
||||
**
|
||||
** If nArg==(-2) then any function with a non-null xStep or xFunc is
|
||||
** a perfect match and any function with both xStep and xFunc NULL is
|
||||
** If nArg==(-2) then any function with a non-null xSFunc is
|
||||
** a perfect match and any function with xSFunc NULL is
|
||||
** a non-match.
|
||||
*/
|
||||
#define FUNC_PERFECT_MATCH 6 /* The score for a perfect match */
|
||||
@ -256,7 +256,7 @@ static int matchQuality(
|
||||
int match;
|
||||
|
||||
/* nArg of -2 is a special case */
|
||||
if( nArg==(-2) ) return (p->xFunc==0 && p->xStep==0) ? 0 : FUNC_PERFECT_MATCH;
|
||||
if( nArg==(-2) ) return (p->xSFunc==0) ? 0 : FUNC_PERFECT_MATCH;
|
||||
|
||||
/* Wrong number of arguments means "no match" */
|
||||
if( p->nArg!=nArg && p->nArg>=0 ) return 0;
|
||||
@ -334,7 +334,7 @@ void sqlite3FuncDefInsert(
|
||||
** no matching function previously existed.
|
||||
**
|
||||
** If nArg is -2, then the first valid function found is returned. A
|
||||
** function is valid if either xFunc or xStep is non-zero. The nArg==(-2)
|
||||
** function is valid if xSFunc is non-zero. The nArg==(-2)
|
||||
** case is used to see if zName is a valid function name for some number
|
||||
** of arguments. If nArg is -2, then createFlag must be 0.
|
||||
**
|
||||
@ -411,7 +411,7 @@ FuncDef *sqlite3FindFunction(
|
||||
sqlite3FuncDefInsert(&db->aFunc, pBest);
|
||||
}
|
||||
|
||||
if( pBest && (pBest->xStep || pBest->xFunc || createFlag) ){
|
||||
if( pBest && (pBest->xSFunc || createFlag) ){
|
||||
return pBest;
|
||||
}
|
||||
return 0;
|
||||
|
70
src/date.c
70
src/date.c
@ -70,34 +70,49 @@ struct DateTime {
|
||||
|
||||
|
||||
/*
|
||||
** Convert zDate into one or more integers. Additional arguments
|
||||
** come in groups of 5 as follows:
|
||||
** Convert zDate into one or more integers according to the conversion
|
||||
** specifier zFormat.
|
||||
**
|
||||
** N number of digits in the integer
|
||||
** min minimum allowed value of the integer
|
||||
** max maximum allowed value of the integer
|
||||
** nextC first character after the integer
|
||||
** pVal where to write the integers value.
|
||||
** zFormat[] contains 4 characters for each integer converted, except for
|
||||
** the last integer which is specified by three characters. The meaning
|
||||
** of a four-character format specifiers ABCD is:
|
||||
**
|
||||
** A: number of digits to convert. Always "2" or "4".
|
||||
** B: minimum value. Always "0" or "1".
|
||||
** C: maximum value, decoded as:
|
||||
** a: 12
|
||||
** b: 14
|
||||
** c: 24
|
||||
** d: 31
|
||||
** e: 59
|
||||
** f: 9999
|
||||
** D: the separator character, or \000 to indicate this is the
|
||||
** last number to convert.
|
||||
**
|
||||
** Example: To translate an ISO-8601 date YYYY-MM-DD, the format would
|
||||
** be "40f-21a-20c". The "40f-" indicates the 4-digit year followed by "-".
|
||||
** The "21a-" indicates the 2-digit month followed by "-". The "20c" indicates
|
||||
** the 2-digit day which is the last integer in the set.
|
||||
**
|
||||
** Conversions continue until one with nextC==0 is encountered.
|
||||
** The function returns the number of successful conversions.
|
||||
*/
|
||||
static int getDigits(const char *zDate, ...){
|
||||
static int getDigits(const char *zDate, const char *zFormat, ...){
|
||||
/* The aMx[] array translates the 3rd character of each format
|
||||
** spec into a max size: a b c d e f */
|
||||
static const u16 aMx[] = { 12, 14, 24, 31, 59, 9999 };
|
||||
va_list ap;
|
||||
int val;
|
||||
int N;
|
||||
int min;
|
||||
int max;
|
||||
int nextC;
|
||||
int *pVal;
|
||||
int cnt = 0;
|
||||
va_start(ap, zDate);
|
||||
char nextC;
|
||||
va_start(ap, zFormat);
|
||||
do{
|
||||
N = va_arg(ap, int);
|
||||
min = va_arg(ap, int);
|
||||
max = va_arg(ap, int);
|
||||
nextC = va_arg(ap, int);
|
||||
pVal = va_arg(ap, int*);
|
||||
char N = zFormat[0] - '0';
|
||||
char min = zFormat[1] - '0';
|
||||
int val = 0;
|
||||
u16 max;
|
||||
|
||||
assert( zFormat[2]>='a' && zFormat[2]<='f' );
|
||||
max = aMx[zFormat[2] - 'a'];
|
||||
nextC = zFormat[3];
|
||||
val = 0;
|
||||
while( N-- ){
|
||||
if( !sqlite3Isdigit(*zDate) ){
|
||||
@ -106,12 +121,13 @@ static int getDigits(const char *zDate, ...){
|
||||
val = val*10 + *zDate - '0';
|
||||
zDate++;
|
||||
}
|
||||
if( val<min || val>max || (nextC!=0 && nextC!=*zDate) ){
|
||||
if( val<(int)min || val>(int)max || (nextC!=0 && nextC!=*zDate) ){
|
||||
goto end_getDigits;
|
||||
}
|
||||
*pVal = val;
|
||||
*va_arg(ap,int*) = val;
|
||||
zDate++;
|
||||
cnt++;
|
||||
zFormat += 4;
|
||||
}while( nextC );
|
||||
end_getDigits:
|
||||
va_end(ap);
|
||||
@ -152,7 +168,7 @@ static int parseTimezone(const char *zDate, DateTime *p){
|
||||
return c!=0;
|
||||
}
|
||||
zDate++;
|
||||
if( getDigits(zDate, 2, 0, 14, ':', &nHr, 2, 0, 59, 0, &nMn)!=2 ){
|
||||
if( getDigits(zDate, "20b:20e", &nHr, &nMn)!=2 ){
|
||||
return 1;
|
||||
}
|
||||
zDate += 5;
|
||||
@ -173,13 +189,13 @@ zulu_time:
|
||||
static int parseHhMmSs(const char *zDate, DateTime *p){
|
||||
int h, m, s;
|
||||
double ms = 0.0;
|
||||
if( getDigits(zDate, 2, 0, 24, ':', &h, 2, 0, 59, 0, &m)!=2 ){
|
||||
if( getDigits(zDate, "20c:20e", &h, &m)!=2 ){
|
||||
return 1;
|
||||
}
|
||||
zDate += 5;
|
||||
if( *zDate==':' ){
|
||||
zDate++;
|
||||
if( getDigits(zDate, 2, 0, 59, 0, &s)!=1 ){
|
||||
if( getDigits(zDate, "20e", &s)!=1 ){
|
||||
return 1;
|
||||
}
|
||||
zDate += 2;
|
||||
@ -267,7 +283,7 @@ static int parseYyyyMmDd(const char *zDate, DateTime *p){
|
||||
}else{
|
||||
neg = 0;
|
||||
}
|
||||
if( getDigits(zDate,4,0,9999,'-',&Y,2,1,12,'-',&M,2,1,31,0,&D)!=3 ){
|
||||
if( getDigits(zDate, "40f-21a-21d", &Y, &M, &D)!=3 ){
|
||||
return 1;
|
||||
}
|
||||
zDate += 10;
|
||||
|
@ -1135,10 +1135,11 @@ ExprList *sqlite3ExprListAppend(
|
||||
){
|
||||
sqlite3 *db = pParse->db;
|
||||
if( pList==0 ){
|
||||
pList = sqlite3DbMallocZero(db, sizeof(ExprList) );
|
||||
pList = sqlite3DbMallocRaw(db, sizeof(ExprList) );
|
||||
if( pList==0 ){
|
||||
goto no_mem;
|
||||
}
|
||||
pList->nExpr = 0;
|
||||
pList->a = sqlite3DbMallocRaw(db, sizeof(pList->a[0]));
|
||||
if( pList->a==0 ) goto no_mem;
|
||||
}else if( (pList->nExpr & (pList->nExpr-1))==0 ){
|
||||
@ -2896,7 +2897,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
zId = pExpr->u.zToken;
|
||||
nId = sqlite3Strlen30(zId);
|
||||
pDef = sqlite3FindFunction(db, zId, nId, nFarg, enc, 0);
|
||||
if( pDef==0 || pDef->xFunc==0 ){
|
||||
if( pDef==0 || pDef->xFinalize!=0 ){
|
||||
sqlite3ErrorMsg(pParse, "unknown function: %.*s()", nId, zId);
|
||||
break;
|
||||
}
|
||||
|
@ -1604,7 +1604,7 @@ void sqlite3CompleteInsertion(
|
||||
assert( pParse->nested==0 );
|
||||
pik_flags |= OPFLAG_NCHANGE;
|
||||
}
|
||||
if( pik_flags ) sqlite3VdbeChangeP5(v, pik_flags);
|
||||
sqlite3VdbeChangeP5(v, pik_flags);
|
||||
}
|
||||
if( !HasRowid(pTab) ) return;
|
||||
regData = regNewData + 1;
|
||||
@ -2020,9 +2020,9 @@ static int xferOptimization(
|
||||
assert( (pDest->tabFlags & TF_Autoincrement)==0 );
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_RowData, iSrc, regData);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, iDest, regData, regRowid);
|
||||
sqlite3VdbeAddOp4(v, OP_Insert, iDest, regData, regRowid,
|
||||
(char*)pDest, P4_TABLE);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND);
|
||||
sqlite3VdbeChangeP4(v, -1, (char*)pDest, P4_TABLE);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);
|
||||
|
29
src/main.c
29
src/main.c
@ -1575,7 +1575,7 @@ int sqlite3CreateFunc(
|
||||
int nArg,
|
||||
int enc,
|
||||
void *pUserData,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value **),
|
||||
void (*xSFunc)(sqlite3_context*,int,sqlite3_value **),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value **),
|
||||
void (*xFinal)(sqlite3_context*),
|
||||
FuncDestructor *pDestructor
|
||||
@ -1586,9 +1586,9 @@ int sqlite3CreateFunc(
|
||||
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
if( zFunctionName==0 ||
|
||||
(xFunc && (xFinal || xStep)) ||
|
||||
(!xFunc && (xFinal && !xStep)) ||
|
||||
(!xFunc && (!xFinal && xStep)) ||
|
||||
(xSFunc && (xFinal || xStep)) ||
|
||||
(!xSFunc && (xFinal && !xStep)) ||
|
||||
(!xSFunc && (!xFinal && xStep)) ||
|
||||
(nArg<-1 || nArg>SQLITE_MAX_FUNCTION_ARG) ||
|
||||
(255<(nName = sqlite3Strlen30( zFunctionName))) ){
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
@ -1611,10 +1611,10 @@ int sqlite3CreateFunc(
|
||||
}else if( enc==SQLITE_ANY ){
|
||||
int rc;
|
||||
rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF8|extraFlags,
|
||||
pUserData, xFunc, xStep, xFinal, pDestructor);
|
||||
pUserData, xSFunc, xStep, xFinal, pDestructor);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF16LE|extraFlags,
|
||||
pUserData, xFunc, xStep, xFinal, pDestructor);
|
||||
pUserData, xSFunc, xStep, xFinal, pDestructor);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
@ -1658,8 +1658,7 @@ int sqlite3CreateFunc(
|
||||
p->pDestructor = pDestructor;
|
||||
p->funcFlags = (p->funcFlags & SQLITE_FUNC_ENCMASK) | extraFlags;
|
||||
testcase( p->funcFlags & SQLITE_DETERMINISTIC );
|
||||
p->xFunc = xFunc;
|
||||
p->xStep = xStep;
|
||||
p->xSFunc = xSFunc ? xSFunc : xStep;
|
||||
p->xFinalize = xFinal;
|
||||
p->pUserData = pUserData;
|
||||
p->nArg = (u16)nArg;
|
||||
@ -1675,11 +1674,11 @@ int sqlite3_create_function(
|
||||
int nArg,
|
||||
int enc,
|
||||
void *p,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value **),
|
||||
void (*xSFunc)(sqlite3_context*,int,sqlite3_value **),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value **),
|
||||
void (*xFinal)(sqlite3_context*)
|
||||
){
|
||||
return sqlite3_create_function_v2(db, zFunc, nArg, enc, p, xFunc, xStep,
|
||||
return sqlite3_create_function_v2(db, zFunc, nArg, enc, p, xSFunc, xStep,
|
||||
xFinal, 0);
|
||||
}
|
||||
|
||||
@ -1689,7 +1688,7 @@ int sqlite3_create_function_v2(
|
||||
int nArg,
|
||||
int enc,
|
||||
void *p,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value **),
|
||||
void (*xSFunc)(sqlite3_context*,int,sqlite3_value **),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value **),
|
||||
void (*xFinal)(sqlite3_context*),
|
||||
void (*xDestroy)(void *)
|
||||
@ -1712,7 +1711,7 @@ int sqlite3_create_function_v2(
|
||||
pArg->xDestroy = xDestroy;
|
||||
pArg->pUserData = p;
|
||||
}
|
||||
rc = sqlite3CreateFunc(db, zFunc, nArg, enc, p, xFunc, xStep, xFinal, pArg);
|
||||
rc = sqlite3CreateFunc(db, zFunc, nArg, enc, p, xSFunc, xStep, xFinal, pArg);
|
||||
if( pArg && pArg->nRef==0 ){
|
||||
assert( rc!=SQLITE_OK );
|
||||
xDestroy(p);
|
||||
@ -1732,7 +1731,7 @@ int sqlite3_create_function16(
|
||||
int nArg,
|
||||
int eTextRep,
|
||||
void *p,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xSFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*)
|
||||
){
|
||||
@ -1745,7 +1744,7 @@ int sqlite3_create_function16(
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
assert( !db->mallocFailed );
|
||||
zFunc8 = sqlite3Utf16to8(db, zFunctionName, -1, SQLITE_UTF16NATIVE);
|
||||
rc = sqlite3CreateFunc(db, zFunc8, nArg, eTextRep, p, xFunc, xStep, xFinal,0);
|
||||
rc = sqlite3CreateFunc(db, zFunc8, nArg, eTextRep, p, xSFunc,xStep,xFinal,0);
|
||||
sqlite3DbFree(db, zFunc8);
|
||||
rc = sqlite3ApiExit(db, rc);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
@ -2991,7 +2990,6 @@ static int openDatabase(
|
||||
sqlite3_wal_autocheckpoint(db, SQLITE_DEFAULT_WAL_AUTOCHECKPOINT);
|
||||
|
||||
opendb_out:
|
||||
sqlite3_free(zOpen);
|
||||
if( db ){
|
||||
assert( db->mutex!=0 || isThreadsafe==0
|
||||
|| sqlite3GlobalConfig.bFullMutex==0 );
|
||||
@ -3028,6 +3026,7 @@ opendb_out:
|
||||
}
|
||||
}
|
||||
#endif
|
||||
sqlite3_free(zOpen);
|
||||
return rc & 0xff;
|
||||
}
|
||||
|
||||
|
12
src/malloc.c
12
src/malloc.c
@ -583,8 +583,9 @@ void *sqlite3DbMallocZero(sqlite3 *db, u64 n){
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate and zero memory. If the allocation fails, make
|
||||
** the mallocFailed flag in the connection pointer.
|
||||
** Allocate memory, either lookaside (if possible) or heap.
|
||||
** If the allocation fails, set the mallocFailed flag in
|
||||
** the connection pointer.
|
||||
**
|
||||
** If db!=0 and db->mallocFailed is true (indicating a prior malloc
|
||||
** failure on the same database connection) then always return 0.
|
||||
@ -600,8 +601,8 @@ void *sqlite3DbMallocZero(sqlite3 *db, u64 n){
|
||||
** In other words, if a subsequent malloc (ex: "b") worked, it is assumed
|
||||
** that all prior mallocs (ex: "a") worked too.
|
||||
*/
|
||||
static SQLITE_NOINLINE void *dbMallocRawFinish(sqlite3 *db, u64 n);
|
||||
void *sqlite3DbMallocRaw(sqlite3 *db, u64 n){
|
||||
void *p;
|
||||
assert( db==0 || sqlite3_mutex_held(db->mutex) );
|
||||
assert( db==0 || db->pnBytesFreed==0 );
|
||||
#ifndef SQLITE_OMIT_LOOKASIDE
|
||||
@ -631,7 +632,10 @@ void *sqlite3DbMallocRaw(sqlite3 *db, u64 n){
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
p = sqlite3Malloc(n);
|
||||
return dbMallocRawFinish(db, n);
|
||||
}
|
||||
static SQLITE_NOINLINE void *dbMallocRawFinish(sqlite3 *db, u64 n){
|
||||
void *p = sqlite3Malloc(n);
|
||||
if( !p && db ){
|
||||
db->mallocFailed = 1;
|
||||
}
|
||||
|
87
src/pragma.c
87
src/pragma.c
@ -430,15 +430,17 @@ void sqlite3Pragma(
|
||||
{ OP_Noop, 0, 0, 0},
|
||||
{ OP_ResultRow, 1, 1, 0},
|
||||
};
|
||||
int addr;
|
||||
VdbeOp *aOp;
|
||||
sqlite3VdbeUsesBtree(v, iDb);
|
||||
if( !zRight ){
|
||||
setOneColumnName(v, "cache_size");
|
||||
pParse->nMem += 2;
|
||||
addr = sqlite3VdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize,iLn);
|
||||
sqlite3VdbeChangeP1(v, addr, iDb);
|
||||
sqlite3VdbeChangeP1(v, addr+1, iDb);
|
||||
sqlite3VdbeChangeP1(v, addr+6, SQLITE_DEFAULT_CACHE_SIZE);
|
||||
sqlite3VdbeVerifyNoMallocRequired(v, ArraySize(getCacheSize));
|
||||
aOp = sqlite3VdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize, iLn);
|
||||
if( ONLY_IF_REALLOC_STRESS(aOp==0) ) break;
|
||||
aOp[0].p1 = iDb;
|
||||
aOp[1].p1 = iDb;
|
||||
aOp[6].p1 = SQLITE_DEFAULT_CACHE_SIZE;
|
||||
}else{
|
||||
int size = sqlite3AbsInt32(sqlite3Atoi(zRight));
|
||||
sqlite3BeginWriteOperation(pParse, 0, iDb);
|
||||
@ -684,13 +686,16 @@ void sqlite3Pragma(
|
||||
{ OP_Integer, 0, 1, 0}, /* 4 */
|
||||
{ OP_SetCookie, 0, BTREE_INCR_VACUUM, 1}, /* 5 */
|
||||
};
|
||||
int iAddr;
|
||||
iAddr = sqlite3VdbeAddOpList(v, ArraySize(setMeta6), setMeta6, iLn);
|
||||
sqlite3VdbeChangeP1(v, iAddr, iDb);
|
||||
sqlite3VdbeChangeP1(v, iAddr+1, iDb);
|
||||
sqlite3VdbeChangeP2(v, iAddr+2, iAddr+4);
|
||||
sqlite3VdbeChangeP1(v, iAddr+4, eAuto-1);
|
||||
sqlite3VdbeChangeP1(v, iAddr+5, iDb);
|
||||
VdbeOp *aOp;
|
||||
int iAddr = sqlite3VdbeCurrentAddr(v);
|
||||
sqlite3VdbeVerifyNoMallocRequired(v, ArraySize(setMeta6));
|
||||
aOp = sqlite3VdbeAddOpList(v, ArraySize(setMeta6), setMeta6, iLn);
|
||||
if( ONLY_IF_REALLOC_STRESS(aOp==0) ) break;
|
||||
aOp[0].p1 = iDb;
|
||||
aOp[1].p1 = iDb;
|
||||
aOp[2].p2 = iAddr+4;
|
||||
aOp[4].p1 = eAuto - 1;
|
||||
aOp[5].p1 = iDb;
|
||||
sqlite3VdbeUsesBtree(v, iDb);
|
||||
}
|
||||
}
|
||||
@ -1396,18 +1401,6 @@ void sqlite3Pragma(
|
||||
case PragTyp_INTEGRITY_CHECK: {
|
||||
int i, j, addr, mxErr;
|
||||
|
||||
/* Code that appears at the end of the integrity check. If no error
|
||||
** messages have been generated, output OK. Otherwise output the
|
||||
** error message
|
||||
*/
|
||||
static const int iLn = VDBE_OFFSET_LINENO(2);
|
||||
static const VdbeOpList endCode[] = {
|
||||
{ OP_AddImm, 1, 0, 0}, /* 0 */
|
||||
{ OP_If, 1, 0, 0}, /* 1 */
|
||||
{ OP_String8, 0, 3, 0}, /* 2 */
|
||||
{ OP_ResultRow, 3, 1, 0},
|
||||
};
|
||||
|
||||
int isQuick = (sqlite3Tolower(zLeft[0])=='q');
|
||||
|
||||
/* If the PRAGMA command was of the form "PRAGMA <db>.integrity_check",
|
||||
@ -1604,10 +1597,24 @@ void sqlite3Pragma(
|
||||
#endif /* SQLITE_OMIT_BTREECOUNT */
|
||||
}
|
||||
}
|
||||
addr = sqlite3VdbeAddOpList(v, ArraySize(endCode), endCode, iLn);
|
||||
sqlite3VdbeChangeP2(v, addr, -mxErr);
|
||||
sqlite3VdbeJumpHere(v, addr+1);
|
||||
sqlite3VdbeChangeP4(v, addr+2, "ok", P4_STATIC);
|
||||
{
|
||||
static const int iLn = VDBE_OFFSET_LINENO(2);
|
||||
static const VdbeOpList endCode[] = {
|
||||
{ OP_AddImm, 1, 0, 0}, /* 0 */
|
||||
{ OP_If, 1, 0, 0}, /* 1 */
|
||||
{ OP_String8, 0, 3, 0}, /* 2 */
|
||||
{ OP_ResultRow, 3, 1, 0},
|
||||
};
|
||||
VdbeOp *aOp;
|
||||
|
||||
aOp = sqlite3VdbeAddOpList(v, ArraySize(endCode), endCode, iLn);
|
||||
if( aOp ){
|
||||
aOp[0].p2 = -mxErr;
|
||||
aOp[1].p2 = sqlite3VdbeCurrentAddr(v);
|
||||
aOp[2].p4type = P4_STATIC;
|
||||
aOp[2].p4.z = "ok";
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
|
||||
@ -1724,11 +1731,14 @@ void sqlite3Pragma(
|
||||
{ OP_Integer, 0, 1, 0}, /* 1 */
|
||||
{ OP_SetCookie, 0, 0, 1}, /* 2 */
|
||||
};
|
||||
int addr = sqlite3VdbeAddOpList(v, ArraySize(setCookie), setCookie, 0);
|
||||
sqlite3VdbeChangeP1(v, addr, iDb);
|
||||
sqlite3VdbeChangeP1(v, addr+1, sqlite3Atoi(zRight));
|
||||
sqlite3VdbeChangeP1(v, addr+2, iDb);
|
||||
sqlite3VdbeChangeP2(v, addr+2, iCookie);
|
||||
VdbeOp *aOp;
|
||||
sqlite3VdbeVerifyNoMallocRequired(v, ArraySize(setCookie));
|
||||
aOp = sqlite3VdbeAddOpList(v, ArraySize(setCookie), setCookie, 0);
|
||||
if( ONLY_IF_REALLOC_STRESS(aOp==0) ) break;
|
||||
aOp[0].p1 = iDb;
|
||||
aOp[1].p1 = sqlite3Atoi(zRight);
|
||||
aOp[2].p1 = iDb;
|
||||
aOp[2].p2 = iCookie;
|
||||
}else{
|
||||
/* Read the specified cookie value */
|
||||
static const VdbeOpList readCookie[] = {
|
||||
@ -1736,10 +1746,13 @@ void sqlite3Pragma(
|
||||
{ OP_ReadCookie, 0, 1, 0}, /* 1 */
|
||||
{ OP_ResultRow, 1, 1, 0}
|
||||
};
|
||||
int addr = sqlite3VdbeAddOpList(v, ArraySize(readCookie), readCookie, 0);
|
||||
sqlite3VdbeChangeP1(v, addr, iDb);
|
||||
sqlite3VdbeChangeP1(v, addr+1, iDb);
|
||||
sqlite3VdbeChangeP3(v, addr+1, iCookie);
|
||||
VdbeOp *aOp;
|
||||
sqlite3VdbeVerifyNoMallocRequired(v, ArraySize(readCookie));
|
||||
aOp = sqlite3VdbeAddOpList(v, ArraySize(readCookie),readCookie,0);
|
||||
if( ONLY_IF_REALLOC_STRESS(aOp==0) ) break;
|
||||
aOp[0].p1 = iDb;
|
||||
aOp[1].p1 = iDb;
|
||||
aOp[1].p3 = iCookie;
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLeft, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
@ -137,61 +137,27 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
int size;
|
||||
#endif
|
||||
Table *pTab;
|
||||
Db *pDb;
|
||||
char const *azArg[4];
|
||||
int meta[5];
|
||||
InitData initData;
|
||||
char const *zMasterSchema;
|
||||
char const *zMasterName;
|
||||
const char *zMasterName;
|
||||
int openedTransaction = 0;
|
||||
|
||||
/*
|
||||
** The master database table has a structure like this
|
||||
*/
|
||||
static const char master_schema[] =
|
||||
"CREATE TABLE sqlite_master(\n"
|
||||
" type text,\n"
|
||||
" name text,\n"
|
||||
" tbl_name text,\n"
|
||||
" rootpage integer,\n"
|
||||
" sql text\n"
|
||||
")"
|
||||
;
|
||||
#ifndef SQLITE_OMIT_TEMPDB
|
||||
static const char temp_master_schema[] =
|
||||
"CREATE TEMP TABLE sqlite_temp_master(\n"
|
||||
" type text,\n"
|
||||
" name text,\n"
|
||||
" tbl_name text,\n"
|
||||
" rootpage integer,\n"
|
||||
" sql text\n"
|
||||
")"
|
||||
;
|
||||
#else
|
||||
#define temp_master_schema 0
|
||||
#endif
|
||||
|
||||
assert( iDb>=0 && iDb<db->nDb );
|
||||
assert( db->aDb[iDb].pSchema );
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
assert( iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) );
|
||||
|
||||
/* zMasterSchema and zInitScript are set to point at the master schema
|
||||
** and initialisation script appropriate for the database being
|
||||
** initialized. zMasterName is the name of the master table.
|
||||
*/
|
||||
if( !OMIT_TEMPDB && iDb==1 ){
|
||||
zMasterSchema = temp_master_schema;
|
||||
}else{
|
||||
zMasterSchema = master_schema;
|
||||
}
|
||||
zMasterName = SCHEMA_TABLE(iDb);
|
||||
|
||||
/* Construct the schema tables. */
|
||||
azArg[0] = zMasterName;
|
||||
/* Construct the in-memory representation schema tables (sqlite_master or
|
||||
** sqlite_temp_master) by invoking the parser directly. The appropriate
|
||||
** table name will be inserted automatically by the parser so we can just
|
||||
** use the abbreviation "x" here. The parser will also automatically tag
|
||||
** the schema table as read-only. */
|
||||
azArg[0] = zMasterName = SCHEMA_TABLE(iDb);
|
||||
azArg[1] = "1";
|
||||
azArg[2] = zMasterSchema;
|
||||
azArg[2] = "CREATE TABLE x(type text,name text,tbl_name text,"
|
||||
"rootpage integer,sql text)";
|
||||
azArg[3] = 0;
|
||||
initData.db = db;
|
||||
initData.iDb = iDb;
|
||||
@ -202,10 +168,6 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
rc = initData.rc;
|
||||
goto error_out;
|
||||
}
|
||||
pTab = sqlite3FindTable(db, zMasterName, db->aDb[iDb].zName);
|
||||
if( ALWAYS(pTab) ){
|
||||
pTab->tabFlags |= TF_Readonly;
|
||||
}
|
||||
|
||||
/* Create a cursor to hold the database open
|
||||
*/
|
||||
@ -324,7 +286,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
{
|
||||
char *zSql;
|
||||
zSql = sqlite3MPrintf(db,
|
||||
"SELECT name, rootpage, sql FROM '%q'.%s ORDER BY rowid",
|
||||
"SELECT name, rootpage, sql FROM \"%w\".%s ORDER BY rowid",
|
||||
db->aDb[iDb].zName, zMasterName);
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
{
|
||||
|
@ -665,7 +665,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
wrong_num_args = 1;
|
||||
}
|
||||
}else{
|
||||
is_agg = pDef->xFunc==0;
|
||||
is_agg = pDef->xFinalize!=0;
|
||||
if( pDef->funcFlags & SQLITE_FUNC_UNLIKELY ){
|
||||
ExprSetProperty(pExpr, EP_Unlikely|EP_Skip);
|
||||
if( n==2 ){
|
||||
@ -1393,10 +1393,12 @@ int sqlite3ResolveExprNames(
|
||||
#endif
|
||||
savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg);
|
||||
pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg);
|
||||
memset(&w, 0, sizeof(w));
|
||||
w.pParse = pNC->pParse;
|
||||
w.xExprCallback = resolveExprStep;
|
||||
w.xSelectCallback = resolveSelectStep;
|
||||
w.pParse = pNC->pParse;
|
||||
w.xSelectCallback2 = 0;
|
||||
w.walkerDepth = 0;
|
||||
w.eCode = 0;
|
||||
w.u.pNC = pNC;
|
||||
sqlite3WalkExpr(&w, pExpr);
|
||||
#if SQLITE_MAX_EXPR_DEPTH>0
|
||||
|
@ -1005,8 +1005,8 @@ static void selectInnerLoop(
|
||||
** X extra columns.
|
||||
*/
|
||||
KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
|
||||
KeyInfo *p = sqlite3DbMallocZero(0,
|
||||
sizeof(KeyInfo) + (N+X)*(sizeof(CollSeq*)+1));
|
||||
int nExtra = (N+X)*(sizeof(CollSeq*)+1);
|
||||
KeyInfo *p = sqlite3Malloc(sizeof(KeyInfo) + nExtra);
|
||||
if( p ){
|
||||
p->aSortOrder = (u8*)&p->aColl[N+X];
|
||||
p->nField = (u16)N;
|
||||
@ -1014,6 +1014,7 @@ KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
|
||||
p->enc = ENC(db);
|
||||
p->db = db;
|
||||
p->nRef = 1;
|
||||
memset(&p[1], 0, nExtra);
|
||||
}else{
|
||||
db->mallocFailed = 1;
|
||||
}
|
||||
|
@ -402,6 +402,21 @@
|
||||
# define NEVER(X) (X)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Some malloc failures are only possible if SQLITE_TEST_REALLOC_STRESS is
|
||||
** defined. We need to defend against those failures when testing with
|
||||
** SQLITE_TEST_REALLOC_STRESS, but we don't want the unreachable branches
|
||||
** during a normal build. The following macro can be used to disable tests
|
||||
** that are always false except when SQLITE_TEST_REALLOC_STRESS is set.
|
||||
*/
|
||||
#if defined(SQLITE_TEST_REALLOC_STRESS)
|
||||
# define ONLY_IF_REALLOC_STRESS(X) (X)
|
||||
#elif !defined(NDEBUG)
|
||||
# define ONLY_IF_REALLOC_STRESS(X) ((X)?(assert(0),1):0)
|
||||
#else
|
||||
# define ONLY_IF_REALLOC_STRESS(X) (0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Declarations used for tracing the operating system interfaces.
|
||||
*/
|
||||
@ -1371,9 +1386,8 @@ struct FuncDef {
|
||||
u16 funcFlags; /* Some combination of SQLITE_FUNC_* */
|
||||
void *pUserData; /* User data parameter */
|
||||
FuncDef *pNext; /* Next function with same name */
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**); /* Regular function */
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**); /* Aggregate step */
|
||||
void (*xFinalize)(sqlite3_context*); /* Aggregate finalizer */
|
||||
void (*xSFunc)(sqlite3_context*,int,sqlite3_value**); /* func or agg-step */
|
||||
void (*xFinalize)(sqlite3_context*); /* Agg finalizer */
|
||||
char *zName; /* SQL name of the function. */
|
||||
FuncDef *pHash; /* Next with a different name but the same hash */
|
||||
FuncDestructor *pDestructor; /* Reference counted destructor function */
|
||||
@ -1456,28 +1470,28 @@ struct FuncDestructor {
|
||||
*/
|
||||
#define FUNCTION(zName, nArg, iArg, bNC, xFunc) \
|
||||
{nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
|
||||
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0}
|
||||
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, 0, 0}
|
||||
#define VFUNCTION(zName, nArg, iArg, bNC, xFunc) \
|
||||
{nArg, SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
|
||||
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0}
|
||||
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, 0, 0}
|
||||
#define DFUNCTION(zName, nArg, iArg, bNC, xFunc) \
|
||||
{nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
|
||||
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0}
|
||||
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, 0, 0}
|
||||
#define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags) \
|
||||
{nArg,SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags,\
|
||||
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0}
|
||||
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, 0, 0}
|
||||
#define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \
|
||||
{nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
|
||||
pArg, 0, xFunc, 0, 0, #zName, 0, 0}
|
||||
pArg, 0, xFunc, 0, #zName, 0, 0}
|
||||
#define LIKEFUNC(zName, nArg, arg, flags) \
|
||||
{nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|flags, \
|
||||
(void *)arg, 0, likeFunc, 0, 0, #zName, 0, 0}
|
||||
(void *)arg, 0, likeFunc, 0, #zName, 0, 0}
|
||||
#define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \
|
||||
{nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL), \
|
||||
SQLITE_INT_TO_PTR(arg), 0, 0, xStep,xFinal,#zName,0,0}
|
||||
SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,#zName,0,0}
|
||||
#define AGGREGATE2(zName, nArg, arg, nc, xStep, xFinal, extraFlags) \
|
||||
{nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL)|extraFlags, \
|
||||
SQLITE_INT_TO_PTR(arg), 0, 0, xStep,xFinal,#zName,0,0}
|
||||
SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,#zName,0,0}
|
||||
|
||||
/*
|
||||
** All current savepoints are stored in a linked list starting at
|
||||
@ -2788,7 +2802,7 @@ struct Parse {
|
||||
** in the recursive region.
|
||||
************************************************************************/
|
||||
|
||||
int nVar; /* Number of '?' variables seen in the SQL so far */
|
||||
ynVar nVar; /* Number of '?' variables seen in the SQL so far */
|
||||
int nzVar; /* Number of available slots in azVar[] */
|
||||
u8 iPkSortOrder; /* ASC or DESC for INTEGER PRIMARY KEY */
|
||||
u8 explain; /* True if the EXPLAIN flag is found on the query */
|
||||
@ -3074,10 +3088,10 @@ struct Sqlite3Config {
|
||||
** Context pointer passed down through the tree-walk.
|
||||
*/
|
||||
struct Walker {
|
||||
Parse *pParse; /* Parser context. */
|
||||
int (*xExprCallback)(Walker*, Expr*); /* Callback for expressions */
|
||||
int (*xSelectCallback)(Walker*,Select*); /* Callback for SELECTs */
|
||||
void (*xSelectCallback2)(Walker*,Select*);/* Second callback for SELECTs */
|
||||
Parse *pParse; /* Parser context. */
|
||||
int walkerDepth; /* Number of subqueries */
|
||||
u8 eCode; /* A small processing code */
|
||||
union { /* Extra data for callback */
|
||||
|
@ -952,8 +952,8 @@ void sqlite3CodeRowTriggerDirect(
|
||||
if( pPrg ){
|
||||
int bRecursive = (p->zName && 0==(pParse->db->flags&SQLITE_RecTriggers));
|
||||
|
||||
sqlite3VdbeAddOp3(v, OP_Program, reg, ignoreJump, ++pParse->nMem);
|
||||
sqlite3VdbeChangeP4(v, -1, (const char *)pPrg->pProgram, P4_SUBPROGRAM);
|
||||
sqlite3VdbeAddOp4(v, OP_Program, reg, ignoreJump, ++pParse->nMem,
|
||||
(const char *)pPrg->pProgram, P4_SUBPROGRAM);
|
||||
VdbeComment(
|
||||
(v, "Call: %s.%s", (p->zName?p->zName:"fkey"), onErrorText(orconf)));
|
||||
|
||||
|
@ -1672,8 +1672,8 @@ case OP_Function: {
|
||||
MemSetTypeFlag(pCtx->pOut, MEM_Null);
|
||||
pCtx->fErrorOrAux = 0;
|
||||
db->lastRowid = lastRowid;
|
||||
(*pCtx->pFunc->xFunc)(pCtx, pCtx->argc, pCtx->argv); /* IMP: R-24505-23230 */
|
||||
lastRowid = db->lastRowid; /* Remember rowid changes made by xFunc */
|
||||
(*pCtx->pFunc->xSFunc)(pCtx, pCtx->argc, pCtx->argv);/* IMP: R-24505-23230 */
|
||||
lastRowid = db->lastRowid; /* Remember rowid changes made by xSFunc */
|
||||
|
||||
/* If the function returned an error, throw an exception */
|
||||
if( pCtx->fErrorOrAux ){
|
||||
@ -5159,6 +5159,7 @@ case OP_Destroy: { /* out2 */
|
||||
int iDb;
|
||||
|
||||
assert( p->readOnly==0 );
|
||||
assert( pOp->p1>1 );
|
||||
pOut = out2Prerelease(p, pOp);
|
||||
pOut->flags = MEM_Null;
|
||||
if( db->nVdbeRead > db->nVDestroy+1 ){
|
||||
@ -5963,7 +5964,7 @@ case OP_AggStep: {
|
||||
pCtx->pOut = &t;
|
||||
pCtx->fErrorOrAux = 0;
|
||||
pCtx->skipFlag = 0;
|
||||
(pCtx->pFunc->xStep)(pCtx,pCtx->argc,pCtx->argv); /* IMP: R-24505-23230 */
|
||||
(pCtx->pFunc->xSFunc)(pCtx,pCtx->argc,pCtx->argv); /* IMP: R-24505-23230 */
|
||||
if( pCtx->fErrorOrAux ){
|
||||
if( pCtx->isError ){
|
||||
sqlite3VdbeError(p, "%s", sqlite3_value_text(&t));
|
||||
|
@ -182,7 +182,12 @@ int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int);
|
||||
int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int);
|
||||
int sqlite3VdbeAddOp4Dup8(Vdbe*,int,int,int,int,const u8*,int);
|
||||
int sqlite3VdbeAddOp4Int(Vdbe*,int,int,int,int,int);
|
||||
int sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp, int iLineno);
|
||||
#if defined(SQLITE_DEBUG) && !defined(SQLITE_TEST_REALLOC_STRESS)
|
||||
void sqlite3VdbeVerifyNoMallocRequired(Vdbe *p, int N);
|
||||
#else
|
||||
# define sqlite3VdbeVerifyNoMallocRequired(A,B)
|
||||
#endif
|
||||
VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp, int iLineno);
|
||||
void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*);
|
||||
void sqlite3VdbeChangeOpcode(Vdbe*, u32 addr, u8);
|
||||
void sqlite3VdbeChangeP1(Vdbe*, u32 addr, int P1);
|
||||
@ -190,7 +195,7 @@ void sqlite3VdbeChangeP2(Vdbe*, u32 addr, int P2);
|
||||
void sqlite3VdbeChangeP3(Vdbe*, u32 addr, int P3);
|
||||
void sqlite3VdbeChangeP5(Vdbe*, u8 P5);
|
||||
void sqlite3VdbeJumpHere(Vdbe*, int addr);
|
||||
void sqlite3VdbeChangeToNoop(Vdbe*, int addr);
|
||||
int sqlite3VdbeChangeToNoop(Vdbe*, int addr);
|
||||
int sqlite3VdbeDeletePriorOpcode(Vdbe*, u8 op);
|
||||
void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N);
|
||||
void sqlite3VdbeSetP4KeyInfo(Parse*, Index*);
|
||||
|
@ -779,7 +779,7 @@ static SQLITE_NOINLINE void *createAggContext(sqlite3_context *p, int nByte){
|
||||
** same context that was returned on prior calls.
|
||||
*/
|
||||
void *sqlite3_aggregate_context(sqlite3_context *p, int nByte){
|
||||
assert( p && p->pFunc && p->pFunc->xStep );
|
||||
assert( p && p->pFunc && p->pFunc->xFinalize );
|
||||
assert( sqlite3_mutex_held(p->pOut->db->mutex) );
|
||||
testcase( nByte<0 );
|
||||
if( (p->pMem->flags & MEM_Agg)==0 ){
|
||||
@ -870,7 +870,7 @@ failed:
|
||||
** context.
|
||||
*/
|
||||
int sqlite3_aggregate_count(sqlite3_context *p){
|
||||
assert( p && p->pMem && p->pFunc && p->pFunc->xStep );
|
||||
assert( p && p->pMem && p->pFunc && p->pFunc->xFinalize );
|
||||
return p->pMem->n;
|
||||
}
|
||||
#endif
|
||||
|
@ -251,8 +251,7 @@ void sqlite3VdbeMultiLoad(Vdbe *p, int iDest, const char *zTypes, ...){
|
||||
for(i=0; (c = zTypes[i])!=0; i++){
|
||||
if( c=='s' ){
|
||||
const char *z = va_arg(ap, const char*);
|
||||
int addr = sqlite3VdbeAddOp2(p, z==0 ? OP_Null : OP_String8, 0, iDest++);
|
||||
if( z ) sqlite3VdbeChangeP4(p, addr, z, 0);
|
||||
sqlite3VdbeAddOp4(p, z==0 ? OP_Null : OP_String8, 0, iDest++, 0, z, 0);
|
||||
}else{
|
||||
assert( c=='i' );
|
||||
sqlite3VdbeAddOp2(p, OP_Integer, va_arg(ap, int), iDest++);
|
||||
@ -607,6 +606,20 @@ int sqlite3VdbeCurrentAddr(Vdbe *p){
|
||||
return p->nOp;
|
||||
}
|
||||
|
||||
/*
|
||||
** Verify that at least N opcode slots are available in p without
|
||||
** having to malloc for more space (except when compiled using
|
||||
** SQLITE_TEST_REALLOC_STRESS). This interface is used during testing
|
||||
** to verify that certain calls to sqlite3VdbeAddOpList() can never
|
||||
** fail due to a OOM fault and hence that the return value from
|
||||
** sqlite3VdbeAddOpList() will always be non-NULL.
|
||||
*/
|
||||
#if defined(SQLITE_DEBUG) && !defined(SQLITE_TEST_REALLOC_STRESS)
|
||||
void sqlite3VdbeVerifyNoMallocRequired(Vdbe *p, int N){
|
||||
assert( p->nOp + N <= p->pParse->nOpAlloc );
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** This function returns a pointer to the array of opcodes associated with
|
||||
** the Vdbe passed as the first argument. It is the callers responsibility
|
||||
@ -632,19 +645,23 @@ VdbeOp *sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg){
|
||||
}
|
||||
|
||||
/*
|
||||
** Add a whole list of operations to the operation stack. Return the
|
||||
** address of the first operation added.
|
||||
** Add a whole list of operations to the operation stack. Return a
|
||||
** pointer to the first operation inserted.
|
||||
*/
|
||||
int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp, int iLineno){
|
||||
int addr, i;
|
||||
VdbeOp *pOut;
|
||||
VdbeOp *sqlite3VdbeAddOpList(
|
||||
Vdbe *p, /* Add opcodes to the prepared statement */
|
||||
int nOp, /* Number of opcodes to add */
|
||||
VdbeOpList const *aOp, /* The opcodes to be added */
|
||||
int iLineno /* Source-file line number of first opcode */
|
||||
){
|
||||
int i;
|
||||
VdbeOp *pOut, *pFirst;
|
||||
assert( nOp>0 );
|
||||
assert( p->magic==VDBE_MAGIC_INIT );
|
||||
if( p->nOp + nOp > p->pParse->nOpAlloc && growOpArray(p, nOp) ){
|
||||
return 0;
|
||||
}
|
||||
addr = p->nOp;
|
||||
pOut = &p->aOp[addr];
|
||||
pFirst = pOut = &p->aOp[p->nOp];
|
||||
for(i=0; i<nOp; i++, aOp++, pOut++){
|
||||
pOut->opcode = aOp->opcode;
|
||||
pOut->p1 = aOp->p1;
|
||||
@ -664,12 +681,12 @@ int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp, int iLineno){
|
||||
#endif
|
||||
#ifdef SQLITE_DEBUG
|
||||
if( p->db->flags & SQLITE_VdbeAddopTrace ){
|
||||
sqlite3VdbePrintOp(0, i+addr, &p->aOp[i+addr]);
|
||||
sqlite3VdbePrintOp(0, i+p->nOp, &p->aOp[i+p->nOp]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
p->nOp += nOp;
|
||||
return addr;
|
||||
return pFirst;
|
||||
}
|
||||
|
||||
#if defined(SQLITE_ENABLE_STMT_SCANSTATUS)
|
||||
@ -717,7 +734,7 @@ void sqlite3VdbeChangeP3(Vdbe *p, u32 addr, int val){
|
||||
sqlite3VdbeGetOp(p,addr)->p3 = val;
|
||||
}
|
||||
void sqlite3VdbeChangeP5(Vdbe *p, u8 p5){
|
||||
sqlite3VdbeGetOp(p,-1)->p5 = p5;
|
||||
if( !p->db->mallocFailed ) p->aOp[p->nOp-1].p5 = p5;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -827,14 +844,16 @@ void sqlite3VdbeLinkSubProgram(Vdbe *pVdbe, SubProgram *p){
|
||||
/*
|
||||
** Change the opcode at addr into OP_Noop
|
||||
*/
|
||||
void sqlite3VdbeChangeToNoop(Vdbe *p, int addr){
|
||||
if( addr<p->nOp ){
|
||||
VdbeOp *pOp = &p->aOp[addr];
|
||||
sqlite3 *db = p->db;
|
||||
freeP4(db, pOp->p4type, pOp->p4.p);
|
||||
memset(pOp, 0, sizeof(pOp[0]));
|
||||
pOp->opcode = OP_Noop;
|
||||
}
|
||||
int sqlite3VdbeChangeToNoop(Vdbe *p, int addr){
|
||||
VdbeOp *pOp;
|
||||
if( p->db->mallocFailed ) return 0;
|
||||
assert( addr>=0 && addr<p->nOp );
|
||||
pOp = &p->aOp[addr];
|
||||
freeP4(p->db, pOp->p4type, pOp->p4.p);
|
||||
pOp->p4type = P4_NOTUSED;
|
||||
pOp->p4.z = 0;
|
||||
pOp->opcode = OP_Noop;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -843,8 +862,7 @@ void sqlite3VdbeChangeToNoop(Vdbe *p, int addr){
|
||||
*/
|
||||
int sqlite3VdbeDeletePriorOpcode(Vdbe *p, u8 op){
|
||||
if( (p->nOp-1)>(p->pParse->iFixedOp) && p->aOp[p->nOp-1].opcode==op ){
|
||||
sqlite3VdbeChangeToNoop(p, p->nOp-1);
|
||||
return 1;
|
||||
return sqlite3VdbeChangeToNoop(p, p->nOp-1);
|
||||
}else{
|
||||
return 0;
|
||||
}
|
||||
@ -1881,7 +1899,6 @@ void sqlite3VdbeMakeReady(
|
||||
p->aMem = allocSpace(p->aMem, nMem*sizeof(Mem), zCsr, &nFree, &nByte);
|
||||
p->aVar = allocSpace(p->aVar, nVar*sizeof(Mem), zCsr, &nFree, &nByte);
|
||||
p->apArg = allocSpace(p->apArg, nArg*sizeof(Mem*), zCsr, &nFree, &nByte);
|
||||
p->azVar = allocSpace(p->azVar, nVar*sizeof(char*), zCsr, &nFree, &nByte);
|
||||
p->apCsr = allocSpace(p->apCsr, nCursor*sizeof(VdbeCursor*),
|
||||
zCsr, &nFree, &nByte);
|
||||
p->aOnceFlag = allocSpace(p->aOnceFlag, nOnce, zCsr, &nFree, &nByte);
|
||||
@ -1904,11 +1921,10 @@ void sqlite3VdbeMakeReady(
|
||||
p->aVar[n].db = db;
|
||||
}
|
||||
}
|
||||
if( p->azVar && pParse->nzVar>0 ){
|
||||
p->nzVar = pParse->nzVar;
|
||||
memcpy(p->azVar, pParse->azVar, p->nzVar*sizeof(p->azVar[0]));
|
||||
memset(pParse->azVar, 0, pParse->nzVar*sizeof(pParse->azVar[0]));
|
||||
}
|
||||
p->nzVar = pParse->nzVar;
|
||||
p->azVar = pParse->azVar;
|
||||
pParse->nzVar = 0;
|
||||
pParse->azVar = 0;
|
||||
if( p->aMem ){
|
||||
p->aMem--; /* aMem[] goes from 1..nMem */
|
||||
p->nMem = nMem; /* not from 0..nMem-1 */
|
||||
@ -2895,6 +2911,7 @@ void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){
|
||||
sqlite3DbFree(db, pSub);
|
||||
}
|
||||
for(i=p->nzVar-1; i>=0; i--) sqlite3DbFree(db, p->azVar[i]);
|
||||
sqlite3DbFree(db, p->azVar);
|
||||
vdbeFreeOpArray(db, p->aOp, p->nOp);
|
||||
sqlite3DbFree(db, p->aColName);
|
||||
sqlite3DbFree(db, p->zSql);
|
||||
@ -3640,9 +3657,9 @@ static int vdbeCompareMemString(
|
||||
v2 = sqlite3ValueText((sqlite3_value*)&c2, pColl->enc);
|
||||
n2 = v2==0 ? 0 : c2.n;
|
||||
rc = pColl->xCmp(pColl->pUser, n1, v1, n2, v2);
|
||||
if( (v1==0 || v2==0) && prcErr ) *prcErr = SQLITE_NOMEM;
|
||||
sqlite3VdbeMemRelease(&c1);
|
||||
sqlite3VdbeMemRelease(&c2);
|
||||
if( (v1==0 || v2==0) && prcErr ) *prcErr = SQLITE_NOMEM;
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
@ -4430,11 +4447,13 @@ void sqlite3VdbeSetVarmask(Vdbe *v, int iVar){
|
||||
** in memory obtained from sqlite3DbMalloc).
|
||||
*/
|
||||
void sqlite3VtabImportErrmsg(Vdbe *p, sqlite3_vtab *pVtab){
|
||||
sqlite3 *db = p->db;
|
||||
sqlite3DbFree(db, p->zErrMsg);
|
||||
p->zErrMsg = sqlite3DbStrDup(db, pVtab->zErrMsg);
|
||||
sqlite3_free(pVtab->zErrMsg);
|
||||
pVtab->zErrMsg = 0;
|
||||
if( pVtab->zErrMsg ){
|
||||
sqlite3 *db = p->db;
|
||||
sqlite3DbFree(db, p->zErrMsg);
|
||||
p->zErrMsg = sqlite3DbStrDup(db, pVtab->zErrMsg);
|
||||
sqlite3_free(pVtab->zErrMsg);
|
||||
pVtab->zErrMsg = 0;
|
||||
}
|
||||
}
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
|
113
src/vdbeblob.c
113
src/vdbeblob.c
@ -117,38 +117,6 @@ int sqlite3_blob_open(
|
||||
){
|
||||
int nAttempt = 0;
|
||||
int iCol; /* Index of zColumn in row-record */
|
||||
|
||||
/* This VDBE program seeks a btree cursor to the identified
|
||||
** db/table/row entry. The reason for using a vdbe program instead
|
||||
** of writing code to use the b-tree layer directly is that the
|
||||
** vdbe program will take advantage of the various transaction,
|
||||
** locking and error handling infrastructure built into the vdbe.
|
||||
**
|
||||
** After seeking the cursor, the vdbe executes an OP_ResultRow.
|
||||
** Code external to the Vdbe then "borrows" the b-tree cursor and
|
||||
** uses it to implement the blob_read(), blob_write() and
|
||||
** blob_bytes() functions.
|
||||
**
|
||||
** The sqlite3_blob_close() function finalizes the vdbe program,
|
||||
** which closes the b-tree cursor and (possibly) commits the
|
||||
** transaction.
|
||||
*/
|
||||
static const int iLn = VDBE_OFFSET_LINENO(4);
|
||||
static const VdbeOpList openBlob[] = {
|
||||
/* {OP_Transaction, 0, 0, 0}, // 0: Inserted separately */
|
||||
{OP_TableLock, 0, 0, 0}, /* 1: Acquire a read or write lock */
|
||||
/* One of the following two instructions is replaced by an OP_Noop. */
|
||||
{OP_OpenRead, 0, 0, 0}, /* 2: Open cursor 0 for reading */
|
||||
{OP_OpenWrite, 0, 0, 0}, /* 3: Open cursor 0 for read/write */
|
||||
{OP_Variable, 1, 1, 1}, /* 4: Push the rowid to the stack */
|
||||
{OP_NotExists, 0, 10, 1}, /* 5: Seek the cursor */
|
||||
{OP_Column, 0, 0, 1}, /* 6 */
|
||||
{OP_ResultRow, 1, 0, 0}, /* 7 */
|
||||
{OP_Goto, 0, 4, 0}, /* 8 */
|
||||
{OP_Close, 0, 0, 0}, /* 9 */
|
||||
{OP_Halt, 0, 0, 0}, /* 10 */
|
||||
};
|
||||
|
||||
int rc = SQLITE_OK;
|
||||
char *zErr = 0;
|
||||
Table *pTab;
|
||||
@ -269,45 +237,80 @@ int sqlite3_blob_open(
|
||||
pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(pParse);
|
||||
assert( pBlob->pStmt || db->mallocFailed );
|
||||
if( pBlob->pStmt ){
|
||||
|
||||
/* This VDBE program seeks a btree cursor to the identified
|
||||
** db/table/row entry. The reason for using a vdbe program instead
|
||||
** of writing code to use the b-tree layer directly is that the
|
||||
** vdbe program will take advantage of the various transaction,
|
||||
** locking and error handling infrastructure built into the vdbe.
|
||||
**
|
||||
** After seeking the cursor, the vdbe executes an OP_ResultRow.
|
||||
** Code external to the Vdbe then "borrows" the b-tree cursor and
|
||||
** uses it to implement the blob_read(), blob_write() and
|
||||
** blob_bytes() functions.
|
||||
**
|
||||
** The sqlite3_blob_close() function finalizes the vdbe program,
|
||||
** which closes the b-tree cursor and (possibly) commits the
|
||||
** transaction.
|
||||
*/
|
||||
static const int iLn = VDBE_OFFSET_LINENO(4);
|
||||
static const VdbeOpList openBlob[] = {
|
||||
/* addr/ofst */
|
||||
/* {OP_Transaction, 0, 0, 0}, // 0/ inserted separately */
|
||||
{OP_TableLock, 0, 0, 0}, /* 1/0: Acquire a read or write lock */
|
||||
{OP_OpenRead, 0, 0, 0}, /* 2/1: Open a cursor */
|
||||
{OP_Variable, 1, 1, 0}, /* 3/2: Move ?1 into reg[1] */
|
||||
{OP_NotExists, 0, 8, 1}, /* 4/3: Seek the cursor */
|
||||
{OP_Column, 0, 0, 1}, /* 5/4 */
|
||||
{OP_ResultRow, 1, 0, 0}, /* 6/5 */
|
||||
{OP_Goto, 0, 3, 0}, /* 7/6 */
|
||||
{OP_Close, 0, 0, 0}, /* 8/7 */
|
||||
{OP_Halt, 0, 0, 0}, /* 9/8 */
|
||||
};
|
||||
Vdbe *v = (Vdbe *)pBlob->pStmt;
|
||||
int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
|
||||
VdbeOp *aOp;
|
||||
|
||||
sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, flags,
|
||||
pTab->pSchema->schema_cookie,
|
||||
pTab->pSchema->iGeneration);
|
||||
sqlite3VdbeChangeP5(v, 1);
|
||||
sqlite3VdbeAddOpList(v, ArraySize(openBlob), openBlob, iLn);
|
||||
aOp = sqlite3VdbeAddOpList(v, ArraySize(openBlob), openBlob, iLn);
|
||||
|
||||
/* Make sure a mutex is held on the table to be accessed */
|
||||
sqlite3VdbeUsesBtree(v, iDb);
|
||||
|
||||
/* Configure the OP_TableLock instruction */
|
||||
if( db->mallocFailed==0 ){
|
||||
assert( aOp!=0 );
|
||||
/* Configure the OP_TableLock instruction */
|
||||
#ifdef SQLITE_OMIT_SHARED_CACHE
|
||||
sqlite3VdbeChangeToNoop(v, 1);
|
||||
aOp[0].opcode = OP_Noop;
|
||||
#else
|
||||
sqlite3VdbeChangeP1(v, 1, iDb);
|
||||
sqlite3VdbeChangeP2(v, 1, pTab->tnum);
|
||||
sqlite3VdbeChangeP3(v, 1, flags);
|
||||
sqlite3VdbeChangeP4(v, 1, pTab->zName, P4_TRANSIENT);
|
||||
aOp[0].p1 = iDb;
|
||||
aOp[0].p2 = pTab->tnum;
|
||||
aOp[0].p3 = flags;
|
||||
sqlite3VdbeChangeP4(v, 1, pTab->zName, P4_TRANSIENT);
|
||||
}
|
||||
if( db->mallocFailed==0 ){
|
||||
#endif
|
||||
|
||||
/* Remove either the OP_OpenWrite or OpenRead. Set the P2
|
||||
** parameter of the other to pTab->tnum. */
|
||||
sqlite3VdbeChangeToNoop(v, 3 - flags);
|
||||
sqlite3VdbeChangeP2(v, 2 + flags, pTab->tnum);
|
||||
sqlite3VdbeChangeP3(v, 2 + flags, iDb);
|
||||
/* Remove either the OP_OpenWrite or OpenRead. Set the P2
|
||||
** parameter of the other to pTab->tnum. */
|
||||
if( flags ) aOp[1].opcode = OP_OpenWrite;
|
||||
aOp[1].p2 = pTab->tnum;
|
||||
aOp[1].p3 = iDb;
|
||||
|
||||
/* Configure the number of columns. Configure the cursor to
|
||||
** think that the table has one more column than it really
|
||||
** does. An OP_Column to retrieve this imaginary column will
|
||||
** always return an SQL NULL. This is useful because it means
|
||||
** we can invoke OP_Column to fill in the vdbe cursors type
|
||||
** and offset cache without causing any IO.
|
||||
*/
|
||||
aOp[1].p4type = P4_INT32;
|
||||
aOp[1].p4.i = pTab->nCol+1;
|
||||
aOp[4].p2 = pTab->nCol;
|
||||
|
||||
/* Configure the number of columns. Configure the cursor to
|
||||
** think that the table has one more column than it really
|
||||
** does. An OP_Column to retrieve this imaginary column will
|
||||
** always return an SQL NULL. This is useful because it means
|
||||
** we can invoke OP_Column to fill in the vdbe cursors type
|
||||
** and offset cache without causing any IO.
|
||||
*/
|
||||
sqlite3VdbeChangeP4(v, 2+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32);
|
||||
sqlite3VdbeChangeP2(v, 6, pTab->nCol);
|
||||
if( !db->mallocFailed ){
|
||||
pParse->nVar = 1;
|
||||
pParse->nMem = 1;
|
||||
pParse->nTab = 1;
|
||||
|
@ -1220,7 +1220,7 @@ static int valueFromFunction(
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
ctx.pOut = pVal;
|
||||
ctx.pFunc = pFunc;
|
||||
pFunc->xFunc(&ctx, nVal, apVal);
|
||||
pFunc->xSFunc(&ctx, nVal, apVal);
|
||||
if( ctx.isError ){
|
||||
rc = ctx.isError;
|
||||
sqlite3ErrorMsg(pCtx->pParse, "%s", sqlite3_value_text(pVal));
|
||||
|
@ -1016,7 +1016,7 @@ FuncDef *sqlite3VtabOverloadFunction(
|
||||
Table *pTab;
|
||||
sqlite3_vtab *pVtab;
|
||||
sqlite3_module *pMod;
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**) = 0;
|
||||
void (*xSFunc)(sqlite3_context*,int,sqlite3_value**) = 0;
|
||||
void *pArg = 0;
|
||||
FuncDef *pNew;
|
||||
int rc = 0;
|
||||
@ -1044,7 +1044,7 @@ FuncDef *sqlite3VtabOverloadFunction(
|
||||
for(z=(unsigned char*)zLowerName; *z; z++){
|
||||
*z = sqlite3UpperToLower[*z];
|
||||
}
|
||||
rc = pMod->xFindFunction(pVtab, nArg, zLowerName, &xFunc, &pArg);
|
||||
rc = pMod->xFindFunction(pVtab, nArg, zLowerName, &xSFunc, &pArg);
|
||||
sqlite3DbFree(db, zLowerName);
|
||||
}
|
||||
if( rc==0 ){
|
||||
@ -1061,7 +1061,7 @@ FuncDef *sqlite3VtabOverloadFunction(
|
||||
*pNew = *pDef;
|
||||
pNew->zName = (char *)&pNew[1];
|
||||
memcpy(pNew->zName, pDef->zName, sqlite3Strlen30(pDef->zName)+1);
|
||||
pNew->xFunc = xFunc;
|
||||
pNew->xSFunc = xSFunc;
|
||||
pNew->pUserData = pArg;
|
||||
pNew->funcFlags |= SQLITE_FUNC_EPHEM;
|
||||
return pNew;
|
||||
|
@ -327,8 +327,7 @@ static void codeApplyAffinity(Parse *pParse, int base, int n, char *zAff){
|
||||
|
||||
/* Code the OP_Affinity opcode if there is anything left to do. */
|
||||
if( n>0 ){
|
||||
sqlite3VdbeAddOp2(v, OP_Affinity, base, n);
|
||||
sqlite3VdbeChangeP4(v, -1, zAff, n);
|
||||
sqlite3VdbeAddOp4(v, OP_Affinity, base, n, 0, zAff, n);
|
||||
sqlite3ExprCacheAffinityChange(pParse, base, n);
|
||||
}
|
||||
}
|
||||
|
@ -202,6 +202,7 @@ static int isLikeOrGlob(
|
||||
sqlite3 *db = pParse->db; /* Database connection */
|
||||
sqlite3_value *pVal = 0;
|
||||
int op; /* Opcode of pRight */
|
||||
int rc; /* Result code to return */
|
||||
|
||||
if( !sqlite3IsLikeFunction(db, pExpr, pnoCase, wc) ){
|
||||
return 0;
|
||||
@ -267,8 +268,9 @@ static int isLikeOrGlob(
|
||||
}
|
||||
}
|
||||
|
||||
rc = (z!=0);
|
||||
sqlite3ValueFree(pVal);
|
||||
return (z!=0);
|
||||
return rc;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */
|
||||
|
||||
|
@ -197,13 +197,10 @@ do_test hook-4.1.2 {
|
||||
# EVIDENCE-OF: R-33257-44249 The update hook is not invoked when WITHOUT
|
||||
# ROWID tables are modified.
|
||||
#
|
||||
breakpoint
|
||||
do_test hook-4.1.2w {
|
||||
set ::update_hook {}
|
||||
execsql {
|
||||
INSERT INTO t1w VALUES(4, 'four');
|
||||
PRAGMA vdbe_debug=on;
|
||||
PRAGMA vdbe_addoptrace=on;
|
||||
DELETE FROM t1w WHERE b = 'two';
|
||||
UPDATE t1w SET b = '' WHERE a = 1 OR a = 3;
|
||||
DELETE FROM t1w WHERE 1; -- Avoid the truncate optimization (for now)
|
||||
|
@ -880,7 +880,7 @@ static double currentTime(void){
|
||||
double t;
|
||||
static sqlite3_vfs *pTimelimitVfs = 0;
|
||||
if( pTimelimitVfs==0 ) pTimelimitVfs = sqlite3_vfs_find(0);
|
||||
if( pTimelimitVfs->iVersion>=1 && pTimelimitVfs->xCurrentTimeInt64!=0 ){
|
||||
if( pTimelimitVfs->iVersion>=2 && pTimelimitVfs->xCurrentTimeInt64!=0 ){
|
||||
sqlite3_int64 tm;
|
||||
pTimelimitVfs->xCurrentTimeInt64(pTimelimitVfs, &tm);
|
||||
t = tm/86400000.0;
|
||||
|
Loading…
Reference in New Issue
Block a user