mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-30 19:03:16 +03:00
Bring the sessions branch up-to-date with all the latest trunk changes.
FossilOrigin-Name: 086a127236ee99d67513490fb7b5549e8b752c44
This commit is contained in:
@ -1081,6 +1081,8 @@ static int fts3InitVtab(
|
||||
char *zUncompress = 0; /* uncompress=? parameter (or NULL) */
|
||||
char *zContent = 0; /* content=? parameter (or NULL) */
|
||||
char *zLanguageid = 0; /* languageid=? parameter (or NULL) */
|
||||
char **azNotindexed = 0; /* The set of notindexed= columns */
|
||||
int nNotindexed = 0; /* Size of azNotindexed[] array */
|
||||
|
||||
assert( strlen(argv[0])==4 );
|
||||
assert( (sqlite3_strnicmp(argv[0], "fts4", 4)==0 && isFts4)
|
||||
@ -1090,9 +1092,19 @@ static int fts3InitVtab(
|
||||
nDb = (int)strlen(argv[1]) + 1;
|
||||
nName = (int)strlen(argv[2]) + 1;
|
||||
|
||||
aCol = (const char **)sqlite3_malloc(sizeof(const char *) * (argc-2) );
|
||||
if( !aCol ) return SQLITE_NOMEM;
|
||||
memset((void *)aCol, 0, sizeof(const char *) * (argc-2));
|
||||
nByte = sizeof(const char *) * (argc-2);
|
||||
aCol = (const char **)sqlite3_malloc(nByte);
|
||||
if( aCol ){
|
||||
memset(aCol, 0, nByte);
|
||||
azNotindexed = (char **)sqlite3_malloc(nByte);
|
||||
}
|
||||
if( azNotindexed ){
|
||||
memset(azNotindexed, 0, nByte);
|
||||
}
|
||||
if( !aCol || !azNotindexed ){
|
||||
rc = SQLITE_NOMEM;
|
||||
goto fts3_init_out;
|
||||
}
|
||||
|
||||
/* Loop through all of the arguments passed by the user to the FTS3/4
|
||||
** module (i.e. all the column names and special arguments). This loop
|
||||
@ -1131,7 +1143,8 @@ static int fts3InitVtab(
|
||||
{ "uncompress", 10 }, /* 3 -> UNCOMPRESS */
|
||||
{ "order", 5 }, /* 4 -> ORDER */
|
||||
{ "content", 7 }, /* 5 -> CONTENT */
|
||||
{ "languageid", 10 } /* 6 -> LANGUAGEID */
|
||||
{ "languageid", 10 }, /* 6 -> LANGUAGEID */
|
||||
{ "notindexed", 10 } /* 7 -> NOTINDEXED */
|
||||
};
|
||||
|
||||
int iOpt;
|
||||
@ -1197,6 +1210,11 @@ static int fts3InitVtab(
|
||||
zLanguageid = zVal;
|
||||
zVal = 0;
|
||||
break;
|
||||
|
||||
case 7: /* NOTINDEXED */
|
||||
azNotindexed[nNotindexed++] = zVal;
|
||||
zVal = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
sqlite3_free(zVal);
|
||||
@ -1268,6 +1286,7 @@ static int fts3InitVtab(
|
||||
nByte = sizeof(Fts3Table) + /* Fts3Table */
|
||||
nCol * sizeof(char *) + /* azColumn */
|
||||
nIndex * sizeof(struct Fts3Index) + /* aIndex */
|
||||
nCol * sizeof(u8) + /* abNotindexed */
|
||||
nName + /* zName */
|
||||
nDb + /* zDb */
|
||||
nString; /* Space for azColumn strings */
|
||||
@ -1301,9 +1320,10 @@ static int fts3InitVtab(
|
||||
for(i=0; i<nIndex; i++){
|
||||
fts3HashInit(&p->aIndex[i].hPending, FTS3_HASH_STRING, 1);
|
||||
}
|
||||
p->abNotindexed = (u8 *)&p->aIndex[nIndex];
|
||||
|
||||
/* Fill in the zName and zDb fields of the vtab structure. */
|
||||
zCsr = (char *)&p->aIndex[nIndex];
|
||||
zCsr = (char *)&p->abNotindexed[nCol];
|
||||
p->zName = zCsr;
|
||||
memcpy(zCsr, argv[2], nName);
|
||||
zCsr += nName;
|
||||
@ -1324,7 +1344,26 @@ static int fts3InitVtab(
|
||||
assert( zCsr <= &((char *)p)[nByte] );
|
||||
}
|
||||
|
||||
if( (zCompress==0)!=(zUncompress==0) ){
|
||||
/* Fill in the abNotindexed array */
|
||||
for(iCol=0; iCol<nCol; iCol++){
|
||||
int n = strlen(p->azColumn[iCol]);
|
||||
for(i=0; i<nNotindexed; i++){
|
||||
char *zNot = azNotindexed[i];
|
||||
if( zNot && 0==sqlite3_strnicmp(p->azColumn[iCol], zNot, n) ){
|
||||
p->abNotindexed[iCol] = 1;
|
||||
sqlite3_free(zNot);
|
||||
azNotindexed[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(i=0; i<nNotindexed; i++){
|
||||
if( azNotindexed[i] ){
|
||||
*pzErr = sqlite3_mprintf("no such column: %s", azNotindexed[i]);
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && (zCompress==0)!=(zUncompress==0) ){
|
||||
char const *zMiss = (zCompress==0 ? "compress" : "uncompress");
|
||||
rc = SQLITE_ERROR;
|
||||
*pzErr = sqlite3_mprintf("missing %s parameter in fts4 constructor", zMiss);
|
||||
@ -1365,7 +1404,9 @@ fts3_init_out:
|
||||
sqlite3_free(zUncompress);
|
||||
sqlite3_free(zContent);
|
||||
sqlite3_free(zLanguageid);
|
||||
for(i=0; i<nNotindexed; i++) sqlite3_free(azNotindexed[i]);
|
||||
sqlite3_free((void *)aCol);
|
||||
sqlite3_free((void *)azNotindexed);
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( p ){
|
||||
fts3DisconnectMethod((sqlite3_vtab *)p);
|
||||
@ -1423,7 +1464,7 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
|
||||
** strategy is possible.
|
||||
*/
|
||||
pInfo->idxNum = FTS3_FULLSCAN_SEARCH;
|
||||
pInfo->estimatedCost = 500000;
|
||||
pInfo->estimatedCost = 5000000;
|
||||
for(i=0; i<pInfo->nConstraint; i++){
|
||||
struct sqlite3_index_constraint *pCons = &pInfo->aConstraint[i];
|
||||
if( pCons->usable==0 ) continue;
|
||||
@ -2984,11 +3025,7 @@ static int fts3FilterMethod(
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = sqlite3Fts3ReadLock(p);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
rc = fts3EvalStart(pCsr);
|
||||
|
||||
sqlite3Fts3SegmentsClose(p);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
pCsr->pNextId = pCsr->aDoclist;
|
||||
|
@ -39,6 +39,18 @@ extern const sqlite3_api_routines *sqlite3_api;
|
||||
#include "fts3_tokenizer.h"
|
||||
#include "fts3_hash.h"
|
||||
|
||||
/*
|
||||
** This constant determines the maximum depth of an FTS expression tree
|
||||
** that the library will create and use. FTS uses recursion to perform
|
||||
** various operations on the query tree, so the disadvantage of a large
|
||||
** limit is that it may allow very large queries to use large amounts
|
||||
** of stack space (perhaps causing a stack overflow).
|
||||
*/
|
||||
#ifndef SQLITE_FTS3_MAX_EXPR_DEPTH
|
||||
# define SQLITE_FTS3_MAX_EXPR_DEPTH 12
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** This constant controls how often segments are merged. Once there are
|
||||
** FTS3_MERGE_COUNT segments of level N, they are merged into a single
|
||||
@ -194,6 +206,7 @@ struct Fts3Table {
|
||||
const char *zName; /* virtual table name */
|
||||
int nColumn; /* number of named columns in virtual table */
|
||||
char **azColumn; /* column names. malloced */
|
||||
u8 *abNotindexed; /* True for 'notindexed' columns */
|
||||
sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */
|
||||
char *zContentTbl; /* content=xxx option, or NULL */
|
||||
char *zLanguageid; /* languageid=xxx option, or NULL */
|
||||
@ -421,7 +434,6 @@ int sqlite3Fts3SegReaderPending(
|
||||
Fts3Table*,int,const char*,int,int,Fts3SegReader**);
|
||||
void sqlite3Fts3SegReaderFree(Fts3SegReader *);
|
||||
int sqlite3Fts3AllSegdirs(Fts3Table*, int, int, int, sqlite3_stmt **);
|
||||
int sqlite3Fts3ReadLock(Fts3Table *);
|
||||
int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char **, int*, int*);
|
||||
|
||||
int sqlite3Fts3SelectDoctotal(Fts3Table *, sqlite3_stmt **);
|
||||
|
@ -1000,17 +1000,16 @@ int sqlite3Fts3ExprParse(
|
||||
Fts3Expr **ppExpr, /* OUT: Parsed query structure */
|
||||
char **pzErr /* OUT: Error message (sqlite3_malloc) */
|
||||
){
|
||||
static const int MAX_EXPR_DEPTH = 12;
|
||||
int rc = fts3ExprParseUnbalanced(
|
||||
pTokenizer, iLangid, azCol, bFts4, nCol, iDefaultCol, z, n, ppExpr
|
||||
);
|
||||
|
||||
/* Rebalance the expression. And check that its depth does not exceed
|
||||
** MAX_EXPR_DEPTH. */
|
||||
** SQLITE_FTS3_MAX_EXPR_DEPTH. */
|
||||
if( rc==SQLITE_OK && *ppExpr ){
|
||||
rc = fts3ExprBalance(ppExpr, MAX_EXPR_DEPTH);
|
||||
rc = fts3ExprBalance(ppExpr, SQLITE_FTS3_MAX_EXPR_DEPTH);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts3ExprCheckDepth(*ppExpr, MAX_EXPR_DEPTH);
|
||||
rc = fts3ExprCheckDepth(*ppExpr, SQLITE_FTS3_MAX_EXPR_DEPTH);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1019,7 +1018,8 @@ int sqlite3Fts3ExprParse(
|
||||
*ppExpr = 0;
|
||||
if( rc==SQLITE_TOOBIG ){
|
||||
*pzErr = sqlite3_mprintf(
|
||||
"FTS expression tree is too large (maximum depth %d)", MAX_EXPR_DEPTH
|
||||
"FTS expression tree is too large (maximum depth %d)",
|
||||
SQLITE_FTS3_MAX_EXPR_DEPTH
|
||||
);
|
||||
rc = SQLITE_ERROR;
|
||||
}else if( rc==SQLITE_ERROR ){
|
||||
|
@ -101,28 +101,27 @@ int sqlite3FtsUnicodeIsalnum(int c){
|
||||
0x02A97004, 0x02A9DC03, 0x02A9EC01, 0x02AAC001, 0x02AAC803,
|
||||
0x02AADC02, 0x02AAF802, 0x02AB0401, 0x02AB7802, 0x02ABAC07,
|
||||
0x02ABD402, 0x02AF8C0B, 0x03600001, 0x036DFC02, 0x036FFC02,
|
||||
0x037FFC02, 0x03E3FC01, 0x03EC7801, 0x03ECA401, 0x03EEC810,
|
||||
0x03F4F802, 0x03F7F002, 0x03F8001A, 0x03F88007, 0x03F8C023,
|
||||
0x03F95013, 0x03F9A004, 0x03FBFC01, 0x03FC040F, 0x03FC6807,
|
||||
0x03FCEC06, 0x03FD6C0B, 0x03FF8007, 0x03FFA007, 0x03FFE405,
|
||||
0x04040003, 0x0404DC09, 0x0405E411, 0x0406400C, 0x0407402E,
|
||||
0x040E7C01, 0x040F4001, 0x04215C01, 0x04247C01, 0x0424FC01,
|
||||
0x04280403, 0x04281402, 0x04283004, 0x0428E003, 0x0428FC01,
|
||||
0x04294009, 0x0429FC01, 0x042CE407, 0x04400003, 0x0440E016,
|
||||
0x04420003, 0x0442C012, 0x04440003, 0x04449C0E, 0x04450004,
|
||||
0x04460003, 0x0446CC0E, 0x04471404, 0x045AAC0D, 0x0491C004,
|
||||
0x05BD442E, 0x05BE3C04, 0x074000F6, 0x07440027, 0x0744A4B5,
|
||||
0x07480046, 0x074C0057, 0x075B0401, 0x075B6C01, 0x075BEC01,
|
||||
0x075C5401, 0x075CD401, 0x075D3C01, 0x075DBC01, 0x075E2401,
|
||||
0x075EA401, 0x075F0C01, 0x07BBC002, 0x07C0002C, 0x07C0C064,
|
||||
0x07C2800F, 0x07C2C40E, 0x07C3040F, 0x07C3440F, 0x07C4401F,
|
||||
0x07C4C03C, 0x07C5C02B, 0x07C7981D, 0x07C8402B, 0x07C90009,
|
||||
0x07C94002, 0x07CC0021, 0x07CCC006, 0x07CCDC46, 0x07CE0014,
|
||||
0x07CE8025, 0x07CF1805, 0x07CF8011, 0x07D0003F, 0x07D10001,
|
||||
0x07D108B6, 0x07D3E404, 0x07D4003E, 0x07D50004, 0x07D54018,
|
||||
0x07D7EC46, 0x07D9140B, 0x07DA0046, 0x07DC0074, 0x38000401,
|
||||
0x38008060, 0x380400F0, 0x3C000001, 0x3FFFF401, 0x40000001,
|
||||
0x43FFF401,
|
||||
0x037FFC01, 0x03EC7801, 0x03ECA401, 0x03EEC810, 0x03F4F802,
|
||||
0x03F7F002, 0x03F8001A, 0x03F88007, 0x03F8C023, 0x03F95013,
|
||||
0x03F9A004, 0x03FBFC01, 0x03FC040F, 0x03FC6807, 0x03FCEC06,
|
||||
0x03FD6C0B, 0x03FF8007, 0x03FFA007, 0x03FFE405, 0x04040003,
|
||||
0x0404DC09, 0x0405E411, 0x0406400C, 0x0407402E, 0x040E7C01,
|
||||
0x040F4001, 0x04215C01, 0x04247C01, 0x0424FC01, 0x04280403,
|
||||
0x04281402, 0x04283004, 0x0428E003, 0x0428FC01, 0x04294009,
|
||||
0x0429FC01, 0x042CE407, 0x04400003, 0x0440E016, 0x04420003,
|
||||
0x0442C012, 0x04440003, 0x04449C0E, 0x04450004, 0x04460003,
|
||||
0x0446CC0E, 0x04471404, 0x045AAC0D, 0x0491C004, 0x05BD442E,
|
||||
0x05BE3C04, 0x074000F6, 0x07440027, 0x0744A4B5, 0x07480046,
|
||||
0x074C0057, 0x075B0401, 0x075B6C01, 0x075BEC01, 0x075C5401,
|
||||
0x075CD401, 0x075D3C01, 0x075DBC01, 0x075E2401, 0x075EA401,
|
||||
0x075F0C01, 0x07BBC002, 0x07C0002C, 0x07C0C064, 0x07C2800F,
|
||||
0x07C2C40E, 0x07C3040F, 0x07C3440F, 0x07C4401F, 0x07C4C03C,
|
||||
0x07C5C02B, 0x07C7981D, 0x07C8402B, 0x07C90009, 0x07C94002,
|
||||
0x07CC0021, 0x07CCC006, 0x07CCDC46, 0x07CE0014, 0x07CE8025,
|
||||
0x07CF1805, 0x07CF8011, 0x07D0003F, 0x07D10001, 0x07D108B6,
|
||||
0x07D3E404, 0x07D4003E, 0x07D50004, 0x07D54018, 0x07D7EC46,
|
||||
0x07D9140B, 0x07DA0046, 0x07DC0074, 0x38000401, 0x38008060,
|
||||
0x380400F0,
|
||||
};
|
||||
static const unsigned int aAscii[4] = {
|
||||
0xFFFFFFFF, 0xFC00FFFF, 0xF8000001, 0xF8000001,
|
||||
|
@ -489,37 +489,30 @@ static void fts3SqlExec(
|
||||
|
||||
|
||||
/*
|
||||
** This function ensures that the caller has obtained a shared-cache
|
||||
** table-lock on the %_content table. This is required before reading
|
||||
** data from the fts3 table. If this lock is not acquired first, then
|
||||
** the caller may end up holding read-locks on the %_segments and %_segdir
|
||||
** tables, but no read-lock on the %_content table. If this happens
|
||||
** a second connection will be able to write to the fts3 table, but
|
||||
** attempting to commit those writes might return SQLITE_LOCKED or
|
||||
** SQLITE_LOCKED_SHAREDCACHE (because the commit attempts to obtain
|
||||
** write-locks on the %_segments and %_segdir ** tables).
|
||||
** This function ensures that the caller has obtained an exclusive
|
||||
** shared-cache table-lock on the %_segdir table. This is required before
|
||||
** writing data to the fts3 table. If this lock is not acquired first, then
|
||||
** the caller may end up attempting to take this lock as part of committing
|
||||
** a transaction, causing SQLite to return SQLITE_LOCKED or
|
||||
** LOCKED_SHAREDCACHEto a COMMIT command.
|
||||
**
|
||||
** We try to avoid this because if FTS3 returns any error when committing
|
||||
** a transaction, the whole transaction will be rolled back. And this is
|
||||
** not what users expect when they get SQLITE_LOCKED_SHAREDCACHE. It can
|
||||
** still happen if the user reads data directly from the %_segments or
|
||||
** %_segdir tables instead of going through FTS3 though.
|
||||
**
|
||||
** This reasoning does not apply to a content=xxx table.
|
||||
** It is best to avoid this because if FTS3 returns any error when
|
||||
** committing a transaction, the whole transaction will be rolled back.
|
||||
** And this is not what users expect when they get SQLITE_LOCKED_SHAREDCACHE.
|
||||
** It can still happen if the user locks the underlying tables directly
|
||||
** instead of accessing them via FTS.
|
||||
*/
|
||||
int sqlite3Fts3ReadLock(Fts3Table *p){
|
||||
int rc; /* Return code */
|
||||
sqlite3_stmt *pStmt; /* Statement used to obtain lock */
|
||||
|
||||
if( p->zContentTbl==0 ){
|
||||
rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pStmt, 0);
|
||||
static int fts3Writelock(Fts3Table *p){
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
if( p->nPendingData==0 ){
|
||||
sqlite3_stmt *pStmt;
|
||||
rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_LEVEL, &pStmt, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_null(pStmt, 1);
|
||||
sqlite3_step(pStmt);
|
||||
rc = sqlite3_reset(pStmt);
|
||||
}
|
||||
}else{
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
return rc;
|
||||
@ -907,12 +900,15 @@ static int fts3InsertTerms(
|
||||
){
|
||||
int i; /* Iterator variable */
|
||||
for(i=2; i<p->nColumn+2; i++){
|
||||
const char *zText = (const char *)sqlite3_value_text(apVal[i]);
|
||||
int rc = fts3PendingTermsAdd(p, iLangid, zText, i-2, &aSz[i-2]);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
int iCol = i-2;
|
||||
if( p->abNotindexed[iCol]==0 ){
|
||||
const char *zText = (const char *)sqlite3_value_text(apVal[i]);
|
||||
int rc = fts3PendingTermsAdd(p, iLangid, zText, iCol, &aSz[iCol]);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
aSz[p->nColumn] += sqlite3_value_bytes(apVal[i]);
|
||||
}
|
||||
aSz[p->nColumn] += sqlite3_value_bytes(apVal[i]);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -1059,9 +1055,12 @@ static void fts3DeleteTerms(
|
||||
int iLangid = langidFromSelect(p, pSelect);
|
||||
rc = fts3PendingTermsDocid(p, iLangid, sqlite3_column_int64(pSelect, 0));
|
||||
for(i=1; rc==SQLITE_OK && i<=p->nColumn; i++){
|
||||
const char *zText = (const char *)sqlite3_column_text(pSelect, i);
|
||||
rc = fts3PendingTermsAdd(p, iLangid, zText, -1, &aSz[i-1]);
|
||||
aSz[p->nColumn] += sqlite3_column_bytes(pSelect, i);
|
||||
int iCol = i-1;
|
||||
if( p->abNotindexed[iCol]==0 ){
|
||||
const char *zText = (const char *)sqlite3_column_text(pSelect, i);
|
||||
rc = fts3PendingTermsAdd(p, iLangid, zText, -1, &aSz[iCol]);
|
||||
aSz[p->nColumn] += sqlite3_column_bytes(pSelect, i);
|
||||
}
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3_reset(pSelect);
|
||||
@ -3303,9 +3302,11 @@ static int fts3DoRebuild(Fts3Table *p){
|
||||
rc = fts3PendingTermsDocid(p, iLangid, sqlite3_column_int64(pStmt, 0));
|
||||
memset(aSz, 0, sizeof(aSz[0]) * (p->nColumn+1));
|
||||
for(iCol=0; rc==SQLITE_OK && iCol<p->nColumn; iCol++){
|
||||
const char *z = (const char *) sqlite3_column_text(pStmt, iCol+1);
|
||||
rc = fts3PendingTermsAdd(p, iLangid, z, iCol, &aSz[iCol]);
|
||||
aSz[p->nColumn] += sqlite3_column_bytes(pStmt, iCol+1);
|
||||
if( p->abNotindexed[iCol]==0 ){
|
||||
const char *z = (const char *) sqlite3_column_text(pStmt, iCol+1);
|
||||
rc = fts3PendingTermsAdd(p, iLangid, z, iCol, &aSz[iCol]);
|
||||
aSz[p->nColumn] += sqlite3_column_bytes(pStmt, iCol+1);
|
||||
}
|
||||
}
|
||||
if( p->bHasDocsize ){
|
||||
fts3InsertDocsize(&rc, p, aSz);
|
||||
@ -5108,35 +5109,37 @@ int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *pCsr){
|
||||
iDocid = sqlite3_column_int64(pCsr->pStmt, 0);
|
||||
|
||||
for(i=0; i<p->nColumn && rc==SQLITE_OK; i++){
|
||||
const char *zText = (const char *)sqlite3_column_text(pCsr->pStmt, i+1);
|
||||
sqlite3_tokenizer_cursor *pTC = 0;
|
||||
|
||||
rc = sqlite3Fts3OpenTokenizer(pT, pCsr->iLangid, zText, -1, &pTC);
|
||||
while( rc==SQLITE_OK ){
|
||||
char const *zToken; /* Buffer containing token */
|
||||
int nToken = 0; /* Number of bytes in token */
|
||||
int iDum1 = 0, iDum2 = 0; /* Dummy variables */
|
||||
int iPos = 0; /* Position of token in zText */
|
||||
|
||||
rc = pModule->xNext(pTC, &zToken, &nToken, &iDum1, &iDum2, &iPos);
|
||||
for(pDef=pCsr->pDeferred; pDef && rc==SQLITE_OK; pDef=pDef->pNext){
|
||||
Fts3PhraseToken *pPT = pDef->pToken;
|
||||
if( (pDef->iCol>=p->nColumn || pDef->iCol==i)
|
||||
&& (pPT->bFirst==0 || iPos==0)
|
||||
&& (pPT->n==nToken || (pPT->isPrefix && pPT->n<nToken))
|
||||
&& (0==memcmp(zToken, pPT->z, pPT->n))
|
||||
){
|
||||
fts3PendingListAppend(&pDef->pList, iDocid, i, iPos, &rc);
|
||||
if( p->abNotindexed[i]==0 ){
|
||||
const char *zText = (const char *)sqlite3_column_text(pCsr->pStmt, i+1);
|
||||
sqlite3_tokenizer_cursor *pTC = 0;
|
||||
|
||||
rc = sqlite3Fts3OpenTokenizer(pT, pCsr->iLangid, zText, -1, &pTC);
|
||||
while( rc==SQLITE_OK ){
|
||||
char const *zToken; /* Buffer containing token */
|
||||
int nToken = 0; /* Number of bytes in token */
|
||||
int iDum1 = 0, iDum2 = 0; /* Dummy variables */
|
||||
int iPos = 0; /* Position of token in zText */
|
||||
|
||||
rc = pModule->xNext(pTC, &zToken, &nToken, &iDum1, &iDum2, &iPos);
|
||||
for(pDef=pCsr->pDeferred; pDef && rc==SQLITE_OK; pDef=pDef->pNext){
|
||||
Fts3PhraseToken *pPT = pDef->pToken;
|
||||
if( (pDef->iCol>=p->nColumn || pDef->iCol==i)
|
||||
&& (pPT->bFirst==0 || iPos==0)
|
||||
&& (pPT->n==nToken || (pPT->isPrefix && pPT->n<nToken))
|
||||
&& (0==memcmp(zToken, pPT->z, pPT->n))
|
||||
){
|
||||
fts3PendingListAppend(&pDef->pList, iDocid, i, iPos, &rc);
|
||||
}
|
||||
}
|
||||
}
|
||||
if( pTC ) pModule->xClose(pTC);
|
||||
if( rc==SQLITE_DONE ) rc = SQLITE_OK;
|
||||
}
|
||||
if( pTC ) pModule->xClose(pTC);
|
||||
if( rc==SQLITE_DONE ) rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
for(pDef=pCsr->pDeferred; pDef && rc==SQLITE_OK; pDef=pDef->pNext){
|
||||
if( pDef->pList ){
|
||||
rc = fts3PendingListAppendVarint(&pDef->pList, 0);
|
||||
|
||||
for(pDef=pCsr->pDeferred; pDef && rc==SQLITE_OK; pDef=pDef->pNext){
|
||||
if( pDef->pList ){
|
||||
rc = fts3PendingListAppendVarint(&pDef->pList, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5297,6 +5300,9 @@ int sqlite3Fts3UpdateMethod(
|
||||
aSzIns = &aSzDel[p->nColumn+1];
|
||||
memset(aSzDel, 0, sizeof(aSzDel[0])*(p->nColumn+1)*2);
|
||||
|
||||
rc = fts3Writelock(p);
|
||||
if( rc!=SQLITE_OK ) goto update_out;
|
||||
|
||||
/* If this is an INSERT operation, or an UPDATE that modifies the rowid
|
||||
** value, then this operation requires constraint handling.
|
||||
**
|
||||
|
@ -239,7 +239,10 @@ proc an_load_unicodedata_text {zName} {
|
||||
foreach $lField $fields {}
|
||||
|
||||
set iCode [expr "0x$code"]
|
||||
set bAlnum [expr {[lsearch {L N} [string range $general_category 0 0]]>=0}]
|
||||
set bAlnum [expr {
|
||||
[lsearch {L N} [string range $general_category 0 0]] >= 0
|
||||
|| $general_category=="Co"
|
||||
}]
|
||||
|
||||
if { !$bAlnum } { lappend lRet $iCode }
|
||||
}
|
||||
@ -360,7 +363,7 @@ proc print_isalnum {zFunc lRange} {
|
||||
}
|
||||
assert( aEntry[0]<key );
|
||||
assert( key>=aEntry[iRes] );
|
||||
return (c >= ((aEntry[iRes]>>10) + (aEntry[iRes]&0x3FF)));
|
||||
return (((unsigned int)c) >= ((aEntry[iRes]>>10) + (aEntry[iRes]&0x3FF)));
|
||||
}
|
||||
return 1;}
|
||||
puts "\}"
|
||||
@ -729,7 +732,7 @@ proc print_fileheader {} {
|
||||
*/
|
||||
}]
|
||||
puts ""
|
||||
puts "#if !defined(SQLITE_DISABLE_FTS3_UNICODE)"
|
||||
puts "#if defined(SQLITE_ENABLE_FTS4_UNICODE61)"
|
||||
puts "#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)"
|
||||
puts ""
|
||||
puts "#include <assert.h>"
|
||||
@ -805,4 +808,4 @@ if {$::generate_test_code} {
|
||||
}
|
||||
|
||||
puts "#endif /* defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) */"
|
||||
puts "#endif /* !defined(SQLITE_DISABLE_FTS3_UNICODE) */"
|
||||
puts "#endif /* !defined(SQLITE_ENABLE_FTS4_UNICODE61) */"
|
||||
|
@ -22,7 +22,7 @@
|
||||
** * Implementations of the SQL scalar upper() and lower() functions
|
||||
** for case mapping.
|
||||
**
|
||||
** * Integration of ICU and SQLite collation seqences.
|
||||
** * Integration of ICU and SQLite collation sequences.
|
||||
**
|
||||
** * An implementation of the LIKE operator that uses ICU to
|
||||
** provide case-independent matching.
|
||||
|
@ -826,11 +826,17 @@ static int closureBestIndex(
|
||||
int iPlan = 0;
|
||||
int i;
|
||||
int idx = 1;
|
||||
int seenMatch = 0;
|
||||
const struct sqlite3_index_constraint *pConstraint;
|
||||
closure_vtab *pVtab = (closure_vtab*)pTab;
|
||||
double rCost = 10000000.0;
|
||||
|
||||
pConstraint = pIdxInfo->aConstraint;
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
||||
if( pConstraint->iColumn==CLOSURE_COL_ROOT
|
||||
&& pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
|
||||
seenMatch = 1;
|
||||
}
|
||||
if( pConstraint->usable==0 ) continue;
|
||||
if( (iPlan & 1)==0
|
||||
&& pConstraint->iColumn==CLOSURE_COL_ROOT
|
||||
@ -839,6 +845,7 @@ static int closureBestIndex(
|
||||
iPlan |= 1;
|
||||
pIdxInfo->aConstraintUsage[i].argvIndex = 1;
|
||||
pIdxInfo->aConstraintUsage[i].omit = 1;
|
||||
rCost /= 100.0;
|
||||
}
|
||||
if( (iPlan & 0x0000f0)==0
|
||||
&& pConstraint->iColumn==CLOSURE_COL_DEPTH
|
||||
@ -849,6 +856,7 @@ static int closureBestIndex(
|
||||
iPlan |= idx<<4;
|
||||
pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
|
||||
if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ) iPlan |= 0x000002;
|
||||
rCost /= 5.0;
|
||||
}
|
||||
if( (iPlan & 0x000f00)==0
|
||||
&& pConstraint->iColumn==CLOSURE_COL_TABLENAME
|
||||
@ -857,6 +865,7 @@ static int closureBestIndex(
|
||||
iPlan |= idx<<8;
|
||||
pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
|
||||
pIdxInfo->aConstraintUsage[i].omit = 1;
|
||||
rCost /= 5.0;
|
||||
}
|
||||
if( (iPlan & 0x00f000)==0
|
||||
&& pConstraint->iColumn==CLOSURE_COL_IDCOLUMN
|
||||
@ -891,7 +900,8 @@ static int closureBestIndex(
|
||||
){
|
||||
pIdxInfo->orderByConsumed = 1;
|
||||
}
|
||||
pIdxInfo->estimatedCost = (double)10000;
|
||||
if( seenMatch && (iPlan&1)==0 ) rCost *= 1e30;
|
||||
pIdxInfo->estimatedCost = rCost;
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
@ -1077,9 +1077,16 @@ static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
int iDistTerm = -1;
|
||||
int iRulesetTerm = -1;
|
||||
int i;
|
||||
int seenMatch = 0;
|
||||
const struct sqlite3_index_constraint *pConstraint;
|
||||
double rCost = 1e12;
|
||||
|
||||
pConstraint = pIdxInfo->aConstraint;
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
||||
if( pConstraint->iColumn==0
|
||||
&& pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH ){
|
||||
seenMatch = 1;
|
||||
}
|
||||
if( pConstraint->usable==0 ) continue;
|
||||
if( (iPlan & 1)==0
|
||||
&& pConstraint->iColumn==0
|
||||
@ -1088,6 +1095,7 @@ static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
iPlan |= 1;
|
||||
pIdxInfo->aConstraintUsage[i].argvIndex = 1;
|
||||
pIdxInfo->aConstraintUsage[i].omit = 1;
|
||||
rCost /= 1e6;
|
||||
}
|
||||
if( (iPlan & 2)==0
|
||||
&& pConstraint->iColumn==1
|
||||
@ -1096,6 +1104,7 @@ static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
){
|
||||
iPlan |= 2;
|
||||
iDistTerm = i;
|
||||
rCost /= 10.0;
|
||||
}
|
||||
if( (iPlan & 4)==0
|
||||
&& pConstraint->iColumn==2
|
||||
@ -1104,6 +1113,7 @@ static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
iPlan |= 4;
|
||||
pIdxInfo->aConstraintUsage[i].omit = 1;
|
||||
iRulesetTerm = i;
|
||||
rCost /= 10.0;
|
||||
}
|
||||
}
|
||||
if( iPlan & 2 ){
|
||||
@ -1122,7 +1132,8 @@ static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
){
|
||||
pIdxInfo->orderByConsumed = 1;
|
||||
}
|
||||
pIdxInfo->estimatedCost = (double)10000;
|
||||
if( seenMatch && (iPlan&1)==0 ) rCost = 1e99;
|
||||
pIdxInfo->estimatedCost = rCost;
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
219
ext/misc/percentile.c
Normal file
219
ext/misc/percentile.c
Normal file
@ -0,0 +1,219 @@
|
||||
/*
|
||||
** 2013-05-28
|
||||
**
|
||||
** 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 to implement the percentile(Y,P) SQL function
|
||||
** as described below:
|
||||
**
|
||||
** (1) The percentile(Y,P) function is an aggregate function taking
|
||||
** exactly two arguments.
|
||||
**
|
||||
** (2) If the P argument to percentile(Y,P) is not the same for every
|
||||
** row in the aggregate then an error is thrown. The word "same"
|
||||
** in the previous sentence means that the value differ by less
|
||||
** than 0.001.
|
||||
**
|
||||
** (3) If the P argument to percentile(Y,P) evaluates to anything other
|
||||
** than a number in the range of 0.0 to 100.0 inclusive then an
|
||||
** error is thrown.
|
||||
**
|
||||
** (4) If any Y argument to percentile(Y,P) evaluates to a value that
|
||||
** is not NULL and is not numeric then an error is thrown.
|
||||
**
|
||||
** (5) If any Y argument to percentile(Y,P) evaluates to plus or minus
|
||||
** infinity then an error is thrown. (SQLite always interprets NaN
|
||||
** values as NULL.)
|
||||
**
|
||||
** (6) Both Y and P in percentile(Y,P) can be arbitrary expressions,
|
||||
** including CASE WHEN expressions.
|
||||
**
|
||||
** (7) The percentile(Y,P) aggregate is able to handle inputs of at least
|
||||
** one million (1,000,000) rows.
|
||||
**
|
||||
** (8) If there are no non-NULL values for Y, then percentile(Y,P)
|
||||
** returns NULL.
|
||||
**
|
||||
** (9) If there is exactly one non-NULL value for Y, the percentile(Y,P)
|
||||
** returns the one Y value.
|
||||
**
|
||||
** (10) If there N non-NULL values of Y where N is two or more and
|
||||
** the Y values are ordered from least to greatest and a graph is
|
||||
** drawn from 0 to N-1 such that the height of the graph at J is
|
||||
** the J-th Y value and such that straight lines are drawn between
|
||||
** adjacent Y values, then the percentile(Y,P) function returns
|
||||
** the height of the graph at P*(N-1)/100.
|
||||
**
|
||||
** (11) The percentile(Y,P) function always returns either a floating
|
||||
** point number or NULL.
|
||||
**
|
||||
** (12) The percentile(Y,P) is implemented as a single C99 source-code
|
||||
** file that compiles into a shared-library or DLL that can be loaded
|
||||
** into SQLite using the sqlite3_load_extension() interface.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* The following object is the session context for a single percentile()
|
||||
** function. We have to remember all input Y values until the very end.
|
||||
** Those values are accumulated in the Percentile.a[] array.
|
||||
*/
|
||||
typedef struct Percentile Percentile;
|
||||
struct Percentile {
|
||||
unsigned nAlloc; /* Number of slots allocated for a[] */
|
||||
unsigned nUsed; /* Number of slots actually used in a[] */
|
||||
double rPct; /* 1.0 more than the value for P */
|
||||
double *a; /* Array of Y values */
|
||||
};
|
||||
|
||||
/*
|
||||
** Return TRUE if the input floating-point number is an infinity.
|
||||
*/
|
||||
static int isInfinity(double r){
|
||||
sqlite3_uint64 u;
|
||||
assert( sizeof(u)==sizeof(r) );
|
||||
memcpy(&u, &r, sizeof(u));
|
||||
return ((u>>52)&0x7ff)==0x7ff;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if two doubles differ by 0.001 or less
|
||||
*/
|
||||
static int sameValue(double a, double b){
|
||||
a -= b;
|
||||
return a>=-0.001 && a<=0.001;
|
||||
}
|
||||
|
||||
/*
|
||||
** The "step" function for percentile(Y,P) is called once for each
|
||||
** input row.
|
||||
*/
|
||||
static void percentStep(sqlite3_context *pCtx, int argc, sqlite3_value **argv){
|
||||
Percentile *p;
|
||||
double rPct;
|
||||
int eType;
|
||||
double y;
|
||||
assert( argc==2 );
|
||||
|
||||
/* Requirement 3: P must be a number between 0 and 100 */
|
||||
eType = sqlite3_value_numeric_type(argv[1]);
|
||||
rPct = sqlite3_value_double(argv[1]);
|
||||
if( (eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT) ||
|
||||
((rPct = sqlite3_value_double(argv[1]))<0.0 || rPct>100.0) ){
|
||||
sqlite3_result_error(pCtx, "2nd argument to percentile() is not "
|
||||
"a number between 0.0 and 100.0", -1);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Allocate the session context. */
|
||||
p = (Percentile*)sqlite3_aggregate_context(pCtx, sizeof(*p));
|
||||
if( p==0 ) return;
|
||||
|
||||
/* Remember the P value. Throw an error if the P value is different
|
||||
** from any prior row, per Requirement (2). */
|
||||
if( p->rPct==0.0 ){
|
||||
p->rPct = rPct+1.0;
|
||||
}else if( !sameValue(p->rPct,rPct+1.0) ){
|
||||
sqlite3_result_error(pCtx, "2nd argument to percentile() is not the "
|
||||
"same for all input rows", -1);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ignore rows for which Y is NULL */
|
||||
eType = sqlite3_value_type(argv[0]);
|
||||
if( eType==SQLITE_NULL ) return;
|
||||
|
||||
/* If not NULL, then Y must be numeric. Otherwise throw an error.
|
||||
** Requirement 4 */
|
||||
if( eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT ){
|
||||
sqlite3_result_error(pCtx, "1st argument to percentile() is not "
|
||||
"numeric", -1);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Throw an error if the Y value is infinity or NaN */
|
||||
y = sqlite3_value_double(argv[0]);
|
||||
if( isInfinity(y) ){
|
||||
sqlite3_result_error(pCtx, "Inf input to percentile()", -1);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Allocate and store the Y */
|
||||
if( p->nUsed>=p->nAlloc ){
|
||||
unsigned n = p->nAlloc*2 + 250;
|
||||
double *a = sqlite3_realloc(p->a, sizeof(double)*n);
|
||||
if( a==0 ){
|
||||
sqlite3_free(p->a);
|
||||
memset(p, 0, sizeof(*p));
|
||||
sqlite3_result_error_nomem(pCtx);
|
||||
return;
|
||||
}
|
||||
p->nAlloc = n;
|
||||
p->a = a;
|
||||
}
|
||||
p->a[p->nUsed++] = y;
|
||||
}
|
||||
|
||||
/*
|
||||
** Compare to doubles for sorting using qsort()
|
||||
*/
|
||||
static int doubleCmp(const void *pA, const void *pB){
|
||||
double a = *(double*)pA;
|
||||
double b = *(double*)pB;
|
||||
if( a==b ) return 0;
|
||||
if( a<b ) return -1;
|
||||
return +1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Called to compute the final output of percentile() and to clean
|
||||
** up all allocated memory.
|
||||
*/
|
||||
static void percentFinal(sqlite3_context *pCtx){
|
||||
Percentile *p;
|
||||
unsigned i1, i2;
|
||||
double v1, v2;
|
||||
double ix, vx;
|
||||
p = (Percentile*)sqlite3_aggregate_context(pCtx, 0);
|
||||
if( p==0 ) return;
|
||||
if( p->a==0 ) return;
|
||||
if( p->nUsed ){
|
||||
qsort(p->a, p->nUsed, sizeof(double), doubleCmp);
|
||||
ix = (p->rPct-1.0)*(p->nUsed-1)*0.01;
|
||||
i1 = ix;
|
||||
i2 = ix==(double)i1 || i1==p->nUsed-1 ? i1 : i1+1;
|
||||
v1 = p->a[i1];
|
||||
v2 = p->a[i2];
|
||||
vx = v1 + (v2-v1)*(ix-i1);
|
||||
sqlite3_result_double(pCtx, vx);
|
||||
}
|
||||
sqlite3_free(p->a);
|
||||
memset(p, 0, sizeof(*p));
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_percentile_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "percentile", 2, SQLITE_UTF8, 0,
|
||||
0, percentStep, percentFinal);
|
||||
return rc;
|
||||
}
|
@ -74,36 +74,36 @@ do_test rtree6-1.5 {
|
||||
do_eqp_test rtree6.2.1 {
|
||||
SELECT * FROM t1,t2 WHERE k=+ii AND x1<10
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:Ca (~0 rows)}
|
||||
0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
|
||||
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:Ca}
|
||||
0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)}
|
||||
}
|
||||
|
||||
do_eqp_test rtree6.2.2 {
|
||||
SELECT * FROM t1,t2 WHERE k=ii AND x1<10
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:Ca (~0 rows)}
|
||||
0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
|
||||
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:Ca}
|
||||
0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)}
|
||||
}
|
||||
|
||||
do_eqp_test rtree6.2.3 {
|
||||
SELECT * FROM t1,t2 WHERE k=ii
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2: (~0 rows)}
|
||||
0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
|
||||
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:}
|
||||
0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)}
|
||||
}
|
||||
|
||||
do_eqp_test rtree6.2.4 {
|
||||
SELECT * FROM t1,t2 WHERE v=10 and x1<10 and x2>10
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:CaEb (~0 rows)}
|
||||
0 1 1 {SCAN TABLE t2 (~100000 rows)}
|
||||
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:CaEb}
|
||||
0 1 1 {SCAN TABLE t2}
|
||||
}
|
||||
|
||||
do_eqp_test rtree6.2.5 {
|
||||
SELECT * FROM t1,t2 WHERE k=ii AND x1<v
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2: (~0 rows)}
|
||||
0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
|
||||
0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:}
|
||||
0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)}
|
||||
}
|
||||
|
||||
do_execsql_test rtree6-3.1 {
|
||||
|
@ -168,4 +168,3 @@ do_test rtree8-5.3 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
Reference in New Issue
Block a user