Merge the latest trunk changes into the winrt branch.
FossilOrigin-Name: a1a85b846aac07979e732a9f36d178bd567e103a
This commit is contained in:
+76
-19
@@ -70,7 +70,7 @@
|
||||
** A doclist is stored like this:
|
||||
**
|
||||
** array {
|
||||
** varint docid;
|
||||
** varint docid; (delta from previous doclist)
|
||||
** array { (position list for column 0)
|
||||
** varint position; (2 more than the delta from previous position)
|
||||
** }
|
||||
@@ -101,8 +101,8 @@
|
||||
** at D signals the start of a new column; the 1 at E indicates that the
|
||||
** new column is column number 1. There are two positions at 12 and 45
|
||||
** (14-2 and 35-2+12). The 0 at H indicate the end-of-document. The
|
||||
** 234 at I is the next docid. It has one position 72 (72-2) and then
|
||||
** terminates with the 0 at K.
|
||||
** 234 at I is the delta to next docid (357). It has one position 70
|
||||
** (72-2) and then terminates with the 0 at K.
|
||||
**
|
||||
** A "position-list" is the list of positions for multiple columns for
|
||||
** a single docid. A "column-list" is the set of positions for a single
|
||||
@@ -570,6 +570,18 @@ static void fts3DeclareVtab(int *pRc, Fts3Table *p){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Create the %_stat table if it does not already exist.
|
||||
*/
|
||||
void sqlite3Fts3CreateStatTable(int *pRc, Fts3Table *p){
|
||||
fts3DbExec(pRc, p->db,
|
||||
"CREATE TABLE IF NOT EXISTS %Q.'%q_stat'"
|
||||
"(id INTEGER PRIMARY KEY, value BLOB);",
|
||||
p->zDb, p->zName
|
||||
);
|
||||
if( (*pRc)==SQLITE_OK ) p->bHasStat = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Create the backing store tables (%_content, %_segments and %_segdir)
|
||||
** required by the FTS3 table passed as the only argument. This is done
|
||||
@@ -630,11 +642,9 @@ static int fts3CreateTables(Fts3Table *p){
|
||||
p->zDb, p->zName
|
||||
);
|
||||
}
|
||||
assert( p->bHasStat==p->bFts4 );
|
||||
if( p->bHasStat ){
|
||||
fts3DbExec(&rc, db,
|
||||
"CREATE TABLE %Q.'%q_stat'(id INTEGER PRIMARY KEY, value BLOB);",
|
||||
p->zDb, p->zName
|
||||
);
|
||||
sqlite3Fts3CreateStatTable(&rc, p);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -1275,7 +1285,9 @@ static int fts3InitVtab(
|
||||
p->nMaxPendingData = FTS3_MAX_PENDING_DATA;
|
||||
p->bHasDocsize = (isFts4 && bNoDocsize==0);
|
||||
p->bHasStat = isFts4;
|
||||
p->bFts4 = isFts4;
|
||||
p->bDescIdx = bDescIdx;
|
||||
p->bAutoincrmerge = 0xff; /* 0xff means setting unknown */
|
||||
p->zContentTbl = zContent;
|
||||
p->zLanguageid = zLanguageid;
|
||||
zContent = 0;
|
||||
@@ -1328,6 +1340,16 @@ static int fts3InitVtab(
|
||||
rc = fts3CreateTables(p);
|
||||
}
|
||||
|
||||
/* Check to see if a legacy fts3 table has been "upgraded" by the
|
||||
** addition of a %_stat table so that it can use incremental merge.
|
||||
*/
|
||||
if( !isFts4 && !isCreate ){
|
||||
int rc2 = SQLITE_OK;
|
||||
fts3DbExec(&rc2, db, "SELECT 1 FROM %Q.'%q_stat' WHERE id=2",
|
||||
p->zDb, p->zName);
|
||||
if( rc2==SQLITE_OK ) p->bHasStat = 1;
|
||||
}
|
||||
|
||||
/* Figure out the page-size for the database. This is required in order to
|
||||
** estimate the cost of loading large doclists from the database. */
|
||||
fts3DatabasePageSize(&rc, p);
|
||||
@@ -2671,7 +2693,7 @@ static int fts3SegReaderCursor(
|
||||
*/
|
||||
int sqlite3Fts3SegReaderCursor(
|
||||
Fts3Table *p, /* FTS3 table handle */
|
||||
int iLangid,
|
||||
int iLangid, /* Language-id to search */
|
||||
int iIndex, /* Index to search (from 0 to p->nIndex-1) */
|
||||
int iLevel, /* Level of segments to scan */
|
||||
const char *zTerm, /* Term to query for */
|
||||
@@ -2689,12 +2711,7 @@ int sqlite3Fts3SegReaderCursor(
|
||||
assert( FTS3_SEGCURSOR_ALL<0 && FTS3_SEGCURSOR_PENDING<0 );
|
||||
assert( isPrefix==0 || isScan==0 );
|
||||
|
||||
/* "isScan" is only set to true by the ft4aux module, an ordinary
|
||||
** full-text tables. */
|
||||
assert( isScan==0 || p->aIndex==0 );
|
||||
|
||||
memset(pCsr, 0, sizeof(Fts3MultiSegReader));
|
||||
|
||||
return fts3SegReaderCursor(
|
||||
p, iLangid, iIndex, iLevel, zTerm, nTerm, isPrefix, isScan, pCsr
|
||||
);
|
||||
@@ -2959,7 +2976,7 @@ static int fts3FilterMethod(
|
||||
if( nVal==2 ) pCsr->iLangid = sqlite3_value_int(apVal[1]);
|
||||
|
||||
rc = sqlite3Fts3ExprParse(p->pTokenizer, pCsr->iLangid,
|
||||
p->azColumn, p->bHasStat, p->nColumn, iCol, zQuery, -1, &pCsr->pExpr
|
||||
p->azColumn, p->bFts4, p->nColumn, iCol, zQuery, -1, &pCsr->pExpr
|
||||
);
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( rc==SQLITE_ERROR ){
|
||||
@@ -3102,8 +3119,42 @@ static int fts3UpdateMethod(
|
||||
** hash-table to the database.
|
||||
*/
|
||||
static int fts3SyncMethod(sqlite3_vtab *pVtab){
|
||||
int rc = sqlite3Fts3PendingTermsFlush((Fts3Table *)pVtab);
|
||||
sqlite3Fts3SegmentsClose((Fts3Table *)pVtab);
|
||||
|
||||
/* Following an incremental-merge operation, assuming that the input
|
||||
** segments are not completely consumed (the usual case), they are updated
|
||||
** in place to remove the entries that have already been merged. This
|
||||
** involves updating the leaf block that contains the smallest unmerged
|
||||
** entry and each block (if any) between the leaf and the root node. So
|
||||
** if the height of the input segment b-trees is N, and input segments
|
||||
** are merged eight at a time, updating the input segments at the end
|
||||
** of an incremental-merge requires writing (8*(1+N)) blocks. N is usually
|
||||
** small - often between 0 and 2. So the overhead of the incremental
|
||||
** merge is somewhere between 8 and 24 blocks. To avoid this overhead
|
||||
** dwarfing the actual productive work accomplished, the incremental merge
|
||||
** is only attempted if it will write at least 64 leaf blocks. Hence
|
||||
** nMinMerge.
|
||||
**
|
||||
** Of course, updating the input segments also involves deleting a bunch
|
||||
** of blocks from the segments table. But this is not considered overhead
|
||||
** as it would also be required by a crisis-merge that used the same input
|
||||
** segments.
|
||||
*/
|
||||
const u32 nMinMerge = 64; /* Minimum amount of incr-merge work to do */
|
||||
|
||||
Fts3Table *p = (Fts3Table*)pVtab;
|
||||
int rc = sqlite3Fts3PendingTermsFlush(p);
|
||||
|
||||
if( rc==SQLITE_OK && p->bAutoincrmerge==1 && p->nLeafAdd>(nMinMerge/16) ){
|
||||
int mxLevel = 0; /* Maximum relative level value in db */
|
||||
int A; /* Incr-merge parameter A */
|
||||
|
||||
rc = sqlite3Fts3MaxLevel(p, &mxLevel);
|
||||
assert( rc==SQLITE_OK || mxLevel==0 );
|
||||
A = p->nLeafAdd * mxLevel;
|
||||
A += (A/2);
|
||||
if( A>(int)nMinMerge ) rc = sqlite3Fts3Incrmerge(p, A, 8);
|
||||
}
|
||||
sqlite3Fts3SegmentsClose(p);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -3111,13 +3162,14 @@ static int fts3SyncMethod(sqlite3_vtab *pVtab){
|
||||
** Implementation of xBegin() method. This is a no-op.
|
||||
*/
|
||||
static int fts3BeginMethod(sqlite3_vtab *pVtab){
|
||||
TESTONLY( Fts3Table *p = (Fts3Table*)pVtab );
|
||||
Fts3Table *p = (Fts3Table*)pVtab;
|
||||
UNUSED_PARAMETER(pVtab);
|
||||
assert( p->pSegments==0 );
|
||||
assert( p->nPendingData==0 );
|
||||
assert( p->inTransaction!=1 );
|
||||
TESTONLY( p->inTransaction = 1 );
|
||||
TESTONLY( p->mxSavepoint = -1; );
|
||||
p->nLeafAdd = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -3412,11 +3464,15 @@ static int fts3RenameMethod(
|
||||
** Flush the contents of the pending-terms table to disk.
|
||||
*/
|
||||
static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
|
||||
int rc = SQLITE_OK;
|
||||
UNUSED_PARAMETER(iSavepoint);
|
||||
assert( ((Fts3Table *)pVtab)->inTransaction );
|
||||
assert( ((Fts3Table *)pVtab)->mxSavepoint < iSavepoint );
|
||||
TESTONLY( ((Fts3Table *)pVtab)->mxSavepoint = iSavepoint );
|
||||
return fts3SyncMethod(pVtab);
|
||||
if( ((Fts3Table *)pVtab)->bIgnoreSavepoint==0 ){
|
||||
rc = fts3SyncMethod(pVtab);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -4331,7 +4387,7 @@ static int fts3EvalStart(Fts3Cursor *pCsr){
|
||||
fts3EvalAllocateReaders(pCsr, pCsr->pExpr, &nToken, &nOr, &rc);
|
||||
|
||||
/* Determine which, if any, tokens in the expression should be deferred. */
|
||||
if( rc==SQLITE_OK && nToken>1 && pTab->bHasStat ){
|
||||
if( rc==SQLITE_OK && nToken>1 && pTab->bFts4 ){
|
||||
Fts3TokenAndCost *aTC;
|
||||
Fts3Expr **apOr;
|
||||
aTC = (Fts3TokenAndCost *)sqlite3_malloc(
|
||||
@@ -5147,6 +5203,7 @@ void sqlite3Fts3EvalPhraseCleanup(Fts3Phrase *pPhrase){
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Return SQLITE_CORRUPT_VTAB.
|
||||
*/
|
||||
|
||||
+24
-9
@@ -67,6 +67,9 @@ extern const sqlite3_api_routines *sqlite3_api;
|
||||
#ifndef MIN
|
||||
# define MIN(x,y) ((x)<(y)?(x):(y))
|
||||
#endif
|
||||
#ifndef MAX
|
||||
# define MAX(x,y) ((x)>(y)?(x):(y))
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Maximum length of a varint encoded integer. The varint format is different
|
||||
@@ -121,7 +124,7 @@ extern const sqlite3_api_routines *sqlite3_api;
|
||||
# define NEVER(X) (0)
|
||||
#else
|
||||
# define ALWAYS(x) (x)
|
||||
# define NEVER(X) (x)
|
||||
# define NEVER(x) (x)
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -131,6 +134,7 @@ typedef unsigned char u8; /* 1-byte (or larger) unsigned integer */
|
||||
typedef short int i16; /* 2-byte (or larger) signed integer */
|
||||
typedef unsigned int u32; /* 4-byte unsigned integer */
|
||||
typedef sqlite3_uint64 u64; /* 8-byte unsigned integer */
|
||||
typedef sqlite3_int64 i64; /* 8-byte signed integer */
|
||||
|
||||
/*
|
||||
** Macro used to suppress compiler warnings for unused parameters.
|
||||
@@ -193,36 +197,43 @@ struct Fts3Table {
|
||||
sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */
|
||||
char *zContentTbl; /* content=xxx option, or NULL */
|
||||
char *zLanguageid; /* languageid=xxx option, or NULL */
|
||||
u8 bAutoincrmerge; /* True if automerge=1 */
|
||||
u32 nLeafAdd; /* Number of leaf blocks added this trans */
|
||||
|
||||
/* Precompiled statements used by the implementation. Each of these
|
||||
** statements is run and reset within a single virtual table API call.
|
||||
*/
|
||||
sqlite3_stmt *aStmt[28];
|
||||
sqlite3_stmt *aStmt[37];
|
||||
|
||||
char *zReadExprlist;
|
||||
char *zWriteExprlist;
|
||||
|
||||
int nNodeSize; /* Soft limit for node size */
|
||||
u8 bFts4; /* True for FTS4, false for FTS3 */
|
||||
u8 bHasStat; /* True if %_stat table exists */
|
||||
u8 bHasDocsize; /* True if %_docsize table exists */
|
||||
u8 bDescIdx; /* True if doclists are in reverse order */
|
||||
u8 bIgnoreSavepoint; /* True to ignore xSavepoint invocations */
|
||||
int nPgsz; /* Page size for host database */
|
||||
char *zSegmentsTbl; /* Name of %_segments table */
|
||||
sqlite3_blob *pSegments; /* Blob handle open on %_segments table */
|
||||
|
||||
/* TODO: Fix the first paragraph of this comment.
|
||||
**
|
||||
/*
|
||||
** The following array of hash tables is used to buffer pending index
|
||||
** updates during transactions. Variable nPendingData estimates the memory
|
||||
** size of the pending data, including hash table overhead, not including
|
||||
** malloc overhead. When nPendingData exceeds nMaxPendingData, the buffer
|
||||
** is flushed automatically. Variable iPrevDocid is the docid of the most
|
||||
** recently inserted record.
|
||||
** updates during transactions. All pending updates buffered at any one
|
||||
** time must share a common language-id (see the FTS4 langid= feature).
|
||||
** The current language id is stored in variable iPrevLangid.
|
||||
**
|
||||
** A single FTS4 table may have multiple full-text indexes. For each index
|
||||
** there is an entry in the aIndex[] array. Index 0 is an index of all the
|
||||
** terms that appear in the document set. Each subsequent index in aIndex[]
|
||||
** is an index of prefixes of a specific length.
|
||||
**
|
||||
** Variable nPendingData contains an estimate the memory consumed by the
|
||||
** pending data structures, including hash table overhead, but not including
|
||||
** malloc overhead. When nPendingData exceeds nMaxPendingData, all hash
|
||||
** tables are flushed to disk. Variable iPrevDocid is the docid of the most
|
||||
** recently inserted record.
|
||||
*/
|
||||
int nIndex; /* Size of aIndex[] */
|
||||
struct Fts3Index {
|
||||
@@ -421,6 +432,7 @@ int sqlite3Fts3DeferToken(Fts3Cursor *, Fts3PhraseToken *, int);
|
||||
int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *);
|
||||
void sqlite3Fts3FreeDeferredDoclists(Fts3Cursor *);
|
||||
void sqlite3Fts3SegmentsClose(Fts3Table *);
|
||||
int sqlite3Fts3MaxLevel(Fts3Table *, int *);
|
||||
|
||||
/* Special values interpreted by sqlite3SegReaderCursor() */
|
||||
#define FTS3_SEGCURSOR_PENDING -1
|
||||
@@ -472,6 +484,8 @@ struct Fts3MultiSegReader {
|
||||
int nDoclist; /* Size of aDoclist[] in bytes */
|
||||
};
|
||||
|
||||
int sqlite3Fts3Incrmerge(Fts3Table*,int,int);
|
||||
|
||||
/* fts3.c */
|
||||
int sqlite3Fts3PutVarint(char *, sqlite3_int64);
|
||||
int sqlite3Fts3GetVarint(const char *, sqlite_int64 *);
|
||||
@@ -481,6 +495,7 @@ void sqlite3Fts3Dequote(char *);
|
||||
void sqlite3Fts3DoclistPrev(int,char*,int,char**,sqlite3_int64*,int*,u8*);
|
||||
int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *);
|
||||
int sqlite3Fts3FirstFilter(sqlite3_int64, char *, int, char *);
|
||||
void sqlite3Fts3CreateStatTable(int*, Fts3Table*);
|
||||
|
||||
/* fts3_tokenizer.c */
|
||||
const char *sqlite3Fts3NextToken(const char *, int *);
|
||||
|
||||
@@ -794,8 +794,8 @@ static int fts3MatchinfoCheck(
|
||||
){
|
||||
if( (cArg==FTS3_MATCHINFO_NPHRASE)
|
||||
|| (cArg==FTS3_MATCHINFO_NCOL)
|
||||
|| (cArg==FTS3_MATCHINFO_NDOC && pTab->bHasStat)
|
||||
|| (cArg==FTS3_MATCHINFO_AVGLENGTH && pTab->bHasStat)
|
||||
|| (cArg==FTS3_MATCHINFO_NDOC && pTab->bFts4)
|
||||
|| (cArg==FTS3_MATCHINFO_AVGLENGTH && pTab->bFts4)
|
||||
|| (cArg==FTS3_MATCHINFO_LENGTH && pTab->bHasDocsize)
|
||||
|| (cArg==FTS3_MATCHINFO_LCS)
|
||||
|| (cArg==FTS3_MATCHINFO_HITS)
|
||||
|
||||
+1876
-40
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,874 @@
|
||||
/*
|
||||
** This program is a debugging and analysis utility that displays
|
||||
** information about an FTS3 or FTS4 index.
|
||||
**
|
||||
** Link this program against the SQLite3 amalgamation with the
|
||||
** SQLITE_ENABLE_FTS4 compile-time option. Then run it as:
|
||||
**
|
||||
** fts3view DATABASE
|
||||
**
|
||||
** to get a list of all FTS3/4 tables in DATABASE, or do
|
||||
**
|
||||
** fts3view DATABASE TABLE COMMAND ....
|
||||
**
|
||||
** to see various aspects of the TABLE table. Type fts3view with no
|
||||
** arguments for a list of available COMMANDs.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "sqlite3.h"
|
||||
|
||||
/*
|
||||
** Extra command-line arguments:
|
||||
*/
|
||||
int nExtra;
|
||||
char **azExtra;
|
||||
|
||||
/*
|
||||
** Look for a command-line argument.
|
||||
*/
|
||||
const char *findOption(const char *zName, int hasArg, const char *zDefault){
|
||||
int i;
|
||||
const char *zResult = zDefault;
|
||||
for(i=0; i<nExtra; i++){
|
||||
const char *z = azExtra[i];
|
||||
while( z[0]=='-' ) z++;
|
||||
if( strcmp(z, zName)==0 ){
|
||||
int j = 1;
|
||||
if( hasArg==0 || i==nExtra-1 ) j = 0;
|
||||
zResult = azExtra[i+j];
|
||||
while( i+j<nExtra ){
|
||||
azExtra[i] = azExtra[i+j+1];
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return zResult;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Prepare an SQL query
|
||||
*/
|
||||
static sqlite3_stmt *prepare(sqlite3 *db, const char *zFormat, ...){
|
||||
va_list ap;
|
||||
char *zSql;
|
||||
sqlite3_stmt *pStmt;
|
||||
int rc;
|
||||
|
||||
va_start(ap, zFormat);
|
||||
zSql = sqlite3_vmprintf(zFormat, ap);
|
||||
va_end(ap);
|
||||
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
||||
if( rc ){
|
||||
fprintf(stderr, "Error: %s\nSQL: %s\n", sqlite3_errmsg(db), zSql);
|
||||
exit(1);
|
||||
}
|
||||
sqlite3_free(zSql);
|
||||
return pStmt;
|
||||
}
|
||||
|
||||
/*
|
||||
** Run an SQL statement
|
||||
*/
|
||||
static int runSql(sqlite3 *db, const char *zFormat, ...){
|
||||
va_list ap;
|
||||
char *zSql;
|
||||
int rc;
|
||||
|
||||
va_start(ap, zFormat);
|
||||
zSql = sqlite3_vmprintf(zFormat, ap);
|
||||
rc = sqlite3_exec(db, zSql, 0, 0, 0);
|
||||
va_end(ap);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Show the table schema
|
||||
*/
|
||||
static void showSchema(sqlite3 *db, const char *zTab){
|
||||
sqlite3_stmt *pStmt;
|
||||
pStmt = prepare(db,
|
||||
"SELECT sql FROM sqlite_master"
|
||||
" WHERE name LIKE '%q%%'"
|
||||
" ORDER BY 1",
|
||||
zTab);
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
printf("%s;\n", sqlite3_column_text(pStmt, 0));
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
pStmt = prepare(db, "PRAGMA page_size");
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
printf("PRAGMA page_size=%s;\n", sqlite3_column_text(pStmt, 0));
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
pStmt = prepare(db, "PRAGMA journal_mode");
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
printf("PRAGMA journal_mode=%s;\n", sqlite3_column_text(pStmt, 0));
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
pStmt = prepare(db, "PRAGMA auto_vacuum");
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
const char *zType = "???";
|
||||
switch( sqlite3_column_int(pStmt, 0) ){
|
||||
case 0: zType = "OFF"; break;
|
||||
case 1: zType = "FULL"; break;
|
||||
case 2: zType = "INCREMENTAL"; break;
|
||||
}
|
||||
printf("PRAGMA auto_vacuum=%s;\n", zType);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
pStmt = prepare(db, "PRAGMA encoding");
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
printf("PRAGMA encoding=%s;\n", sqlite3_column_text(pStmt, 0));
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
}
|
||||
|
||||
/*
|
||||
** Read a 64-bit variable-length integer from memory starting at p[0].
|
||||
** Return the number of bytes read, or 0 on error.
|
||||
** The value is stored in *v.
|
||||
*/
|
||||
int getVarint(const unsigned char *p, sqlite_int64 *v){
|
||||
const unsigned char *q = p;
|
||||
sqlite_uint64 x = 0, y = 1;
|
||||
while( (*q&0x80)==0x80 && q-(unsigned char *)p<9 ){
|
||||
x += y * (*q++ & 0x7f);
|
||||
y <<= 7;
|
||||
}
|
||||
x += y * (*q++);
|
||||
*v = (sqlite_int64) x;
|
||||
return (int) (q - (unsigned char *)p);
|
||||
}
|
||||
|
||||
|
||||
/* Show the content of the %_stat table
|
||||
*/
|
||||
static void showStat(sqlite3 *db, const char *zTab){
|
||||
sqlite3_stmt *pStmt;
|
||||
pStmt = prepare(db, "SELECT id, value FROM '%q_stat'", zTab);
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
printf("stat[%d] =", sqlite3_column_int(pStmt, 0));
|
||||
switch( sqlite3_column_type(pStmt, 1) ){
|
||||
case SQLITE_INTEGER: {
|
||||
printf(" %d\n", sqlite3_column_int(pStmt, 1));
|
||||
break;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
unsigned char *x = (unsigned char*)sqlite3_column_blob(pStmt, 1);
|
||||
int len = sqlite3_column_bytes(pStmt, 1);
|
||||
int i = 0;
|
||||
sqlite3_int64 v;
|
||||
while( i<len ){
|
||||
i += getVarint(x, &v);
|
||||
printf(" %lld", v);
|
||||
}
|
||||
printf("\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
}
|
||||
|
||||
/*
|
||||
** Report on the vocabulary. This creates an fts4aux table with a random
|
||||
** name, but deletes it in the end.
|
||||
*/
|
||||
static void showVocabulary(sqlite3 *db, const char *zTab){
|
||||
char *zAux;
|
||||
sqlite3_uint64 r;
|
||||
sqlite3_stmt *pStmt;
|
||||
int nDoc = 0;
|
||||
int nToken = 0;
|
||||
int nOccurrence = 0;
|
||||
int nTop;
|
||||
int n, i;
|
||||
|
||||
sqlite3_randomness(sizeof(r), &r);
|
||||
zAux = sqlite3_mprintf("viewer_%llx", zTab, r);
|
||||
runSql(db, "BEGIN");
|
||||
pStmt = prepare(db, "SELECT count(*) FROM %Q", zTab);
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
nDoc = sqlite3_column_int(pStmt, 0);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
printf("Number of documents...................... %9d\n", nDoc);
|
||||
|
||||
runSql(db, "CREATE VIRTUAL TABLE %s USING fts4aux(%Q)", zAux, zTab);
|
||||
pStmt = prepare(db,
|
||||
"SELECT count(*), sum(occurrences) FROM %s WHERE col='*'",
|
||||
zAux);
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
nToken = sqlite3_column_int(pStmt, 0);
|
||||
nOccurrence = sqlite3_column_int(pStmt, 1);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
printf("Total tokens in all documents............ %9d\n", nOccurrence);
|
||||
printf("Total number of distinct tokens.......... %9d\n", nToken);
|
||||
if( nToken==0 ) goto end_vocab;
|
||||
|
||||
n = 0;
|
||||
pStmt = prepare(db, "SELECT count(*) FROM %s"
|
||||
" WHERE col='*' AND occurrences==1", zAux);
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
n = sqlite3_column_int(pStmt, 0);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
printf("Tokens used exactly once................. %9d %5.2f%%\n",
|
||||
n, n*100.0/nToken);
|
||||
|
||||
n = 0;
|
||||
pStmt = prepare(db, "SELECT count(*) FROM %s"
|
||||
" WHERE col='*' AND documents==1", zAux);
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
n = sqlite3_column_int(pStmt, 0);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
printf("Tokens used in only one document......... %9d %5.2f%%\n",
|
||||
n, n*100.0/nToken);
|
||||
|
||||
if( nDoc>=2000 ){
|
||||
n = 0;
|
||||
pStmt = prepare(db, "SELECT count(*) FROM %s"
|
||||
" WHERE col='*' AND occurrences<=%d", zAux, nDoc/1000);
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
n = sqlite3_column_int(pStmt, 0);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
printf("Tokens used in 0.1%% or less of docs...... %9d %5.2f%%\n",
|
||||
n, n*100.0/nToken);
|
||||
}
|
||||
|
||||
if( nDoc>=200 ){
|
||||
n = 0;
|
||||
pStmt = prepare(db, "SELECT count(*) FROM %s"
|
||||
" WHERE col='*' AND occurrences<=%d", zAux, nDoc/100);
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
n = sqlite3_column_int(pStmt, 0);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
printf("Tokens used in 1%% or less of docs........ %9d %5.2f%%\n",
|
||||
n, n*100.0/nToken);
|
||||
}
|
||||
|
||||
nTop = atoi(findOption("top", 1, "25"));
|
||||
printf("The %d most common tokens:\n", nTop);
|
||||
pStmt = prepare(db,
|
||||
"SELECT term, documents FROM %s"
|
||||
" WHERE col='*'"
|
||||
" ORDER BY documents DESC, term"
|
||||
" LIMIT %d", zAux, nTop);
|
||||
i = 0;
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
i++;
|
||||
n = sqlite3_column_int(pStmt, 1);
|
||||
printf(" %2d. %-30s %9d docs %5.2f%%\n", i,
|
||||
sqlite3_column_text(pStmt, 0), n, n*100.0/nDoc);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
|
||||
end_vocab:
|
||||
runSql(db, "ROLLBACK");
|
||||
sqlite3_free(zAux);
|
||||
}
|
||||
|
||||
/*
|
||||
** Report on the number and sizes of segments
|
||||
*/
|
||||
static void showSegmentStats(sqlite3 *db, const char *zTab){
|
||||
sqlite3_stmt *pStmt;
|
||||
int nSeg = 0;
|
||||
sqlite3_int64 szSeg = 0, mxSeg = 0;
|
||||
int nIdx = 0;
|
||||
sqlite3_int64 szIdx = 0, mxIdx = 0;
|
||||
int nRoot = 0;
|
||||
sqlite3_int64 szRoot = 0, mxRoot = 0;
|
||||
sqlite3_int64 mx;
|
||||
int nLeaf;
|
||||
int n;
|
||||
int pgsz;
|
||||
int mxLevel;
|
||||
int i;
|
||||
|
||||
pStmt = prepare(db,
|
||||
"SELECT count(*), sum(length(block)), max(length(block))"
|
||||
" FROM '%q_segments'",
|
||||
zTab);
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
nSeg = sqlite3_column_int(pStmt, 0);
|
||||
szSeg = sqlite3_column_int64(pStmt, 1);
|
||||
mxSeg = sqlite3_column_int64(pStmt, 2);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
pStmt = prepare(db,
|
||||
"SELECT count(*), sum(length(block)), max(length(block))"
|
||||
" FROM '%q_segments' a JOIN '%q_segdir' b"
|
||||
" WHERE a.blockid BETWEEN b.leaves_end_block+1 AND b.end_block",
|
||||
zTab, zTab);
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
nIdx = sqlite3_column_int(pStmt, 0);
|
||||
szIdx = sqlite3_column_int64(pStmt, 1);
|
||||
mxIdx = sqlite3_column_int64(pStmt, 2);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
pStmt = prepare(db,
|
||||
"SELECT count(*), sum(length(root)), max(length(root))"
|
||||
" FROM '%q_segdir'",
|
||||
zTab);
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
nRoot = sqlite3_column_int(pStmt, 0);
|
||||
szRoot = sqlite3_column_int64(pStmt, 1);
|
||||
mxRoot = sqlite3_column_int64(pStmt, 2);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
|
||||
printf("Number of segments....................... %9d\n", nSeg+nRoot);
|
||||
printf("Number of leaf segments.................. %9d\n", nSeg-nIdx);
|
||||
printf("Number of index segments................. %9d\n", nIdx);
|
||||
printf("Number of root segments.................. %9d\n", nRoot);
|
||||
printf("Total size of all segments............... %9lld\n", szSeg+szRoot);
|
||||
printf("Total size of all leaf segments.......... %9lld\n", szSeg-szIdx);
|
||||
printf("Total size of all index segments......... %9lld\n", szIdx);
|
||||
printf("Total size of all root segments.......... %9lld\n", szRoot);
|
||||
if( nSeg>0 ){
|
||||
printf("Average size of all segments............. %11.1f\n",
|
||||
(double)(szSeg+szRoot)/(double)(nSeg+nRoot));
|
||||
printf("Average size of leaf segments............ %11.1f\n",
|
||||
(double)(szSeg-szIdx)/(double)(nSeg-nIdx));
|
||||
}
|
||||
if( nIdx>0 ){
|
||||
printf("Average size of index segments........... %11.1f\n",
|
||||
(double)szIdx/(double)nIdx);
|
||||
}
|
||||
if( nRoot>0 ){
|
||||
printf("Average size of root segments............ %11.1f\n",
|
||||
(double)szRoot/(double)nRoot);
|
||||
}
|
||||
mx = mxSeg;
|
||||
if( mx<mxRoot ) mx = mxRoot;
|
||||
printf("Maximum segment size..................... %9lld\n", mx);
|
||||
printf("Maximum index segment size............... %9lld\n", mxIdx);
|
||||
printf("Maximum root segment size................ %9lld\n", mxRoot);
|
||||
|
||||
pStmt = prepare(db, "PRAGMA page_size");
|
||||
pgsz = 1024;
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
pgsz = sqlite3_column_int(pStmt, 0);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
printf("Database page size....................... %9d\n", pgsz);
|
||||
pStmt = prepare(db,
|
||||
"SELECT count(*)"
|
||||
" FROM '%q_segments' a JOIN '%q_segdir' b"
|
||||
" WHERE a.blockid BETWEEN b.start_block AND b.leaves_end_block"
|
||||
" AND length(a.block)>%d",
|
||||
zTab, zTab, pgsz-45);
|
||||
n = 0;
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
n = sqlite3_column_int(pStmt, 0);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
nLeaf = nSeg - nIdx;
|
||||
printf("Leaf segments larger than %5d bytes.... %9d %5.2f%%\n",
|
||||
pgsz-45, n, n*100.0/nLeaf);
|
||||
|
||||
pStmt = prepare(db, "SELECT max(level%%1024) FROM '%q_segdir'", zTab);
|
||||
mxLevel = 0;
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
mxLevel = sqlite3_column_int(pStmt, 0);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
|
||||
for(i=0; i<=mxLevel; i++){
|
||||
pStmt = prepare(db,
|
||||
"SELECT count(*), sum(len), avg(len), max(len), sum(len>%d),"
|
||||
" count(distinct idx)"
|
||||
" FROM (SELECT length(a.block) AS len, idx"
|
||||
" FROM '%q_segments' a JOIN '%q_segdir' b"
|
||||
" WHERE (a.blockid BETWEEN b.start_block"
|
||||
" AND b.leaves_end_block)"
|
||||
" AND (b.level%%1024)==%d)",
|
||||
pgsz-45, zTab, zTab, i);
|
||||
if( sqlite3_step(pStmt)==SQLITE_ROW
|
||||
&& (nLeaf = sqlite3_column_int(pStmt, 0))>0
|
||||
){
|
||||
int nIdx = sqlite3_column_int(pStmt, 5);
|
||||
sqlite3_int64 sz;
|
||||
printf("For level %d:\n", i);
|
||||
printf(" Number of indexes...................... %9d\n", nIdx);
|
||||
printf(" Number of leaf segments................ %9d\n", nLeaf);
|
||||
if( nIdx>1 ){
|
||||
printf(" Average leaf segments per index........ %11.1f\n",
|
||||
(double)nLeaf/(double)nIdx);
|
||||
}
|
||||
printf(" Total size of all leaf segments........ %9lld\n",
|
||||
(sz = sqlite3_column_int64(pStmt, 1)));
|
||||
printf(" Average size of leaf segments.......... %11.1f\n",
|
||||
sqlite3_column_double(pStmt, 2));
|
||||
if( nIdx>1 ){
|
||||
printf(" Average leaf segment size per index.... %11.1f\n",
|
||||
(double)sz/(double)nIdx);
|
||||
}
|
||||
printf(" Maximum leaf segment size.............. %9lld\n",
|
||||
sqlite3_column_int64(pStmt, 3));
|
||||
n = sqlite3_column_int(pStmt, 4);
|
||||
printf(" Leaf segments larger than %5d bytes.. %9d %5.2f%%\n",
|
||||
pgsz-45, n, n*100.0/nLeaf);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Print a single "tree" line of the segdir map output.
|
||||
*/
|
||||
static void printTreeLine(sqlite3_int64 iLower, sqlite3_int64 iUpper){
|
||||
printf(" tree %9lld", iLower);
|
||||
if( iUpper>iLower ){
|
||||
printf(" thru %9lld (%lld blocks)", iUpper, iUpper-iLower+1);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
** Check to see if the block of a %_segments entry is NULL.
|
||||
*/
|
||||
static int isNullSegment(sqlite3 *db, const char *zTab, sqlite3_int64 iBlockId){
|
||||
sqlite3_stmt *pStmt;
|
||||
int rc = 1;
|
||||
|
||||
pStmt = prepare(db, "SELECT block IS NULL FROM '%q_segments'"
|
||||
" WHERE blockid=%lld", zTab, iBlockId);
|
||||
if( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
rc = sqlite3_column_int(pStmt, 0);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Show a map of segments derived from the %_segdir table.
|
||||
*/
|
||||
static void showSegdirMap(sqlite3 *db, const char *zTab){
|
||||
int mxIndex, iIndex;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
sqlite3_stmt *pStmt2 = 0;
|
||||
int prevLevel;
|
||||
|
||||
pStmt = prepare(db, "SELECT max(level/1024) FROM '%q_segdir'", zTab);
|
||||
if( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
mxIndex = sqlite3_column_int(pStmt, 0);
|
||||
}else{
|
||||
mxIndex = 0;
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
|
||||
printf("Number of inverted indices............... %3d\n", mxIndex+1);
|
||||
pStmt = prepare(db,
|
||||
"SELECT level, idx, start_block, leaves_end_block, end_block, rowid"
|
||||
" FROM '%q_segdir'"
|
||||
" WHERE level/1024==?"
|
||||
" ORDER BY level DESC, idx",
|
||||
zTab);
|
||||
pStmt2 = prepare(db,
|
||||
"SELECT blockid FROM '%q_segments'"
|
||||
" WHERE blockid BETWEEN ? AND ? ORDER BY blockid",
|
||||
zTab);
|
||||
for(iIndex=0; iIndex<=mxIndex; iIndex++){
|
||||
if( mxIndex>0 ){
|
||||
printf("**************************** Index %d "
|
||||
"****************************\n", iIndex);
|
||||
}
|
||||
sqlite3_bind_int(pStmt, 1, iIndex);
|
||||
prevLevel = -1;
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
int iLevel = sqlite3_column_int(pStmt, 0)%1024;
|
||||
int iIdx = sqlite3_column_int(pStmt, 1);
|
||||
sqlite3_int64 iStart = sqlite3_column_int64(pStmt, 2);
|
||||
sqlite3_int64 iLEnd = sqlite3_column_int64(pStmt, 3);
|
||||
sqlite3_int64 iEnd = sqlite3_column_int64(pStmt, 4);
|
||||
char rtag[20];
|
||||
if( iLevel!=prevLevel ){
|
||||
printf("level %2d idx %2d", iLevel, iIdx);
|
||||
prevLevel = iLevel;
|
||||
}else{
|
||||
printf(" idx %2d", iIdx);
|
||||
}
|
||||
sqlite3_snprintf(sizeof(rtag), rtag, "r%lld",
|
||||
sqlite3_column_int64(pStmt,5));
|
||||
printf(" root %9s\n", rtag);
|
||||
if( iLEnd>iStart ){
|
||||
sqlite3_int64 iLower, iPrev, iX;
|
||||
if( iLEnd+1<=iEnd ){
|
||||
sqlite3_bind_int64(pStmt2, 1, iLEnd+1);
|
||||
sqlite3_bind_int64(pStmt2, 2, iEnd);
|
||||
iLower = -1;
|
||||
while( sqlite3_step(pStmt2)==SQLITE_ROW ){
|
||||
iX = sqlite3_column_int64(pStmt2, 0);
|
||||
if( iLower<0 ){
|
||||
iLower = iPrev = iX;
|
||||
}else if( iX==iPrev+1 ){
|
||||
iPrev = iX;
|
||||
}else{
|
||||
printTreeLine(iLower, iPrev);
|
||||
iLower = iPrev = iX;
|
||||
}
|
||||
}
|
||||
sqlite3_reset(pStmt2);
|
||||
if( iLower>=0 ){
|
||||
if( iLower==iPrev && iLower==iEnd
|
||||
&& isNullSegment(db,zTab,iLower)
|
||||
){
|
||||
printf(" null %9lld\n", iLower);
|
||||
}else{
|
||||
printTreeLine(iLower, iPrev);
|
||||
}
|
||||
}
|
||||
}
|
||||
printf(" leaves %9lld thru %9lld (%lld blocks)\n",
|
||||
iStart, iLEnd, iLEnd - iStart + 1);
|
||||
}
|
||||
}
|
||||
sqlite3_reset(pStmt);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
sqlite3_finalize(pStmt2);
|
||||
}
|
||||
|
||||
/*
|
||||
** Decode a single segment block and display the results on stdout.
|
||||
*/
|
||||
static void decodeSegment(
|
||||
const unsigned char *aData, /* Content to print */
|
||||
int nData /* Number of bytes of content */
|
||||
){
|
||||
sqlite3_int64 iChild;
|
||||
sqlite3_int64 iPrefix;
|
||||
sqlite3_int64 nTerm;
|
||||
sqlite3_int64 n;
|
||||
sqlite3_int64 iDocsz;
|
||||
int iHeight;
|
||||
int i = 0;
|
||||
int cnt = 0;
|
||||
char zTerm[1000];
|
||||
|
||||
i += getVarint(aData, &n);
|
||||
iHeight = (int)n;
|
||||
printf("height: %d\n", iHeight);
|
||||
if( iHeight>0 ){
|
||||
i += getVarint(aData+i, &iChild);
|
||||
printf("left-child: %lld\n", iChild);
|
||||
}
|
||||
while( i<nData ){
|
||||
if( (cnt++)>0 ){
|
||||
i += getVarint(aData+i, &iPrefix);
|
||||
}else{
|
||||
iPrefix = 0;
|
||||
}
|
||||
i += getVarint(aData+i, &nTerm);
|
||||
if( iPrefix+nTerm+1 >= sizeof(zTerm) ){
|
||||
fprintf(stderr, "term to long\n");
|
||||
exit(1);
|
||||
}
|
||||
memcpy(zTerm+iPrefix, aData+i, nTerm);
|
||||
zTerm[iPrefix+nTerm] = 0;
|
||||
i += nTerm;
|
||||
if( iHeight==0 ){
|
||||
i += getVarint(aData+i, &iDocsz);
|
||||
printf("term: %-25s doclist %7lld bytes offset %d\n", zTerm, iDocsz, i);
|
||||
i += iDocsz;
|
||||
}else{
|
||||
printf("term: %-25s child %lld\n", zTerm, ++iChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Print a a blob as hex and ascii.
|
||||
*/
|
||||
static void printBlob(
|
||||
const unsigned char *aData, /* Content to print */
|
||||
int nData /* Number of bytes of content */
|
||||
){
|
||||
int i, j;
|
||||
const char *zOfstFmt;
|
||||
const int perLine = 16;
|
||||
|
||||
if( (nData&~0xfff)==0 ){
|
||||
zOfstFmt = " %03x: ";
|
||||
}else if( (nData&~0xffff)==0 ){
|
||||
zOfstFmt = " %04x: ";
|
||||
}else if( (nData&~0xfffff)==0 ){
|
||||
zOfstFmt = " %05x: ";
|
||||
}else if( (nData&~0xffffff)==0 ){
|
||||
zOfstFmt = " %06x: ";
|
||||
}else{
|
||||
zOfstFmt = " %08x: ";
|
||||
}
|
||||
|
||||
for(i=0; i<nData; i += perLine){
|
||||
fprintf(stdout, zOfstFmt, i);
|
||||
for(j=0; j<perLine; j++){
|
||||
if( i+j>nData ){
|
||||
fprintf(stdout, " ");
|
||||
}else{
|
||||
fprintf(stdout,"%02x ", aData[i+j]);
|
||||
}
|
||||
}
|
||||
for(j=0; j<perLine; j++){
|
||||
if( i+j>nData ){
|
||||
fprintf(stdout, " ");
|
||||
}else{
|
||||
fprintf(stdout,"%c", isprint(aData[i+j]) ? aData[i+j] : '.');
|
||||
}
|
||||
}
|
||||
fprintf(stdout,"\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert text to a 64-bit integer
|
||||
*/
|
||||
static sqlite3_int64 atoi64(const char *z){
|
||||
sqlite3_int64 v = 0;
|
||||
while( z[0]>='0' && z[0]<='9' ){
|
||||
v = v*10 + z[0] - '0';
|
||||
z++;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return a prepared statement which, when stepped, will return in its
|
||||
** first column the blob associated with segment zId. If zId begins with
|
||||
** 'r' then it is a rowid of a %_segdir entry. Otherwise it is a
|
||||
** %_segment entry.
|
||||
*/
|
||||
static sqlite3_stmt *prepareToGetSegment(
|
||||
sqlite3 *db, /* The database */
|
||||
const char *zTab, /* The FTS3/4 table name */
|
||||
const char *zId /* ID of the segment to open */
|
||||
){
|
||||
sqlite3_stmt *pStmt;
|
||||
if( zId[0]=='r' ){
|
||||
pStmt = prepare(db, "SELECT root FROM '%q_segdir' WHERE rowid=%lld",
|
||||
zTab, atoi64(zId+1));
|
||||
}else{
|
||||
pStmt = prepare(db, "SELECT block FROM '%q_segments' WHERE blockid=%lld",
|
||||
zTab, atoi64(zId));
|
||||
}
|
||||
return pStmt;
|
||||
}
|
||||
|
||||
/*
|
||||
** Print the content of a segment or of the root of a segdir. The segment
|
||||
** or root is identified by azExtra[0]. If the first character of azExtra[0]
|
||||
** is 'r' then the remainder is the integer rowid of the %_segdir entry.
|
||||
** If the first character of azExtra[0] is not 'r' then, then all of
|
||||
** azExtra[0] is an integer which is the block number.
|
||||
**
|
||||
** If the --raw option is present in azExtra, then a hex dump is provided.
|
||||
** Otherwise a decoding is shown.
|
||||
*/
|
||||
static void showSegment(sqlite3 *db, const char *zTab){
|
||||
const unsigned char *aData;
|
||||
int nData;
|
||||
sqlite3_stmt *pStmt;
|
||||
|
||||
pStmt = prepareToGetSegment(db, zTab, azExtra[0]);
|
||||
if( sqlite3_step(pStmt)!=SQLITE_ROW ){
|
||||
sqlite3_finalize(pStmt);
|
||||
return;
|
||||
}
|
||||
nData = sqlite3_column_bytes(pStmt, 0);
|
||||
aData = sqlite3_column_blob(pStmt, 0);
|
||||
printf("Segment %s of size %d bytes:\n", azExtra[0], nData);
|
||||
if( findOption("raw", 0, 0)!=0 ){
|
||||
printBlob(aData, nData);
|
||||
}else{
|
||||
decodeSegment(aData, nData);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
}
|
||||
|
||||
/*
|
||||
** Decode a single doclist and display the results on stdout.
|
||||
*/
|
||||
static void decodeDoclist(
|
||||
const unsigned char *aData, /* Content to print */
|
||||
int nData /* Number of bytes of content */
|
||||
){
|
||||
sqlite3_int64 iPrevDocid = 0;
|
||||
sqlite3_int64 iDocid;
|
||||
sqlite3_int64 iPos;
|
||||
sqlite3_int64 iPrevPos = 0;
|
||||
sqlite3_int64 iCol;
|
||||
int i = 0;
|
||||
|
||||
while( i<nData ){
|
||||
i += getVarint(aData+i, &iDocid);
|
||||
printf("docid %lld col0", iDocid+iPrevDocid);
|
||||
iPrevDocid += iDocid;
|
||||
iPrevPos = 0;
|
||||
while( 1 ){
|
||||
i += getVarint(aData+i, &iPos);
|
||||
if( iPos==1 ){
|
||||
i += getVarint(aData+i, &iCol);
|
||||
printf(" col%lld", iCol);
|
||||
iPrevPos = 0;
|
||||
}else if( iPos==0 ){
|
||||
printf("\n");
|
||||
break;
|
||||
}else{
|
||||
iPrevPos += iPos - 2;
|
||||
printf(" %lld", iPrevPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Print the content of a doclist. The segment or segdir-root is
|
||||
** identified by azExtra[0]. If the first character of azExtra[0]
|
||||
** is 'r' then the remainder is the integer rowid of the %_segdir entry.
|
||||
** If the first character of azExtra[0] is not 'r' then, then all of
|
||||
** azExtra[0] is an integer which is the block number. The offset
|
||||
** into the segment is identified by azExtra[1]. The size of the doclist
|
||||
** is azExtra[2].
|
||||
**
|
||||
** If the --raw option is present in azExtra, then a hex dump is provided.
|
||||
** Otherwise a decoding is shown.
|
||||
*/
|
||||
static void showDoclist(sqlite3 *db, const char *zTab){
|
||||
const unsigned char *aData;
|
||||
sqlite3_int64 offset, nData;
|
||||
sqlite3_stmt *pStmt;
|
||||
|
||||
offset = atoi64(azExtra[1]);
|
||||
nData = atoi64(azExtra[2]);
|
||||
pStmt = prepareToGetSegment(db, zTab, azExtra[0]);
|
||||
if( sqlite3_step(pStmt)!=SQLITE_ROW ){
|
||||
sqlite3_finalize(pStmt);
|
||||
return;
|
||||
}
|
||||
aData = sqlite3_column_blob(pStmt, 0);
|
||||
printf("Doclist at %s offset %lld of size %lld bytes:\n",
|
||||
azExtra[0], offset, nData);
|
||||
if( findOption("raw", 0, 0)!=0 ){
|
||||
printBlob(aData+offset, nData);
|
||||
}else{
|
||||
decodeDoclist(aData+offset, nData);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
}
|
||||
|
||||
/*
|
||||
** Show the top N largest segments
|
||||
*/
|
||||
static void listBigSegments(sqlite3 *db, const char *zTab){
|
||||
int nTop, i;
|
||||
sqlite3_stmt *pStmt;
|
||||
sqlite3_int64 sz;
|
||||
sqlite3_int64 id;
|
||||
|
||||
nTop = atoi(findOption("top", 1, "25"));
|
||||
printf("The %d largest segments:\n", nTop);
|
||||
pStmt = prepare(db,
|
||||
"SELECT blockid, length(block) AS len FROM '%q_segments'"
|
||||
" ORDER BY 2 DESC, 1"
|
||||
" LIMIT %d", zTab, nTop);
|
||||
i = 0;
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
i++;
|
||||
id = sqlite3_column_int64(pStmt, 0);
|
||||
sz = sqlite3_column_int64(pStmt, 1);
|
||||
printf(" %2d. %9lld size %lld\n", i, id, sz);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void usage(const char *argv0){
|
||||
fprintf(stderr, "Usage: %s DATABASE\n"
|
||||
" or: %s DATABASE FTS3TABLE ARGS...\n", argv0, argv0);
|
||||
fprintf(stderr,
|
||||
"ARGS:\n"
|
||||
" big-segments [--top N] show the largest segments\n"
|
||||
" doclist BLOCKID OFFSET SIZE [--raw] Decode a doclist\n"
|
||||
" schema FTS table schema\n"
|
||||
" segdir directory of segments\n"
|
||||
" segment BLOCKID [--raw] content of a segment\n"
|
||||
" segment-stats info on segment sizes\n"
|
||||
" stat the %%_stat table\n"
|
||||
" vocabulary [--top N] document vocabulary\n"
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv){
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
const char *zTab;
|
||||
const char *zCmd;
|
||||
|
||||
if( argc<2 ) usage(argv[0]);
|
||||
rc = sqlite3_open(argv[1], &db);
|
||||
if( rc ){
|
||||
fprintf(stderr, "Cannot open %s\n", argv[1]);
|
||||
exit(1);
|
||||
}
|
||||
if( argc==2 ){
|
||||
sqlite3_stmt *pStmt;
|
||||
int cnt = 0;
|
||||
pStmt = prepare(db, "SELECT b.sql"
|
||||
" FROM sqlite_master a, sqlite_master b"
|
||||
" WHERE a.name GLOB '*_segdir'"
|
||||
" AND b.name=substr(a.name,1,length(a.name)-7)"
|
||||
" ORDER BY 1");
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
cnt++;
|
||||
printf("%s;\n", sqlite3_column_text(pStmt, 0));
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
if( cnt==0 ){
|
||||
printf("/* No FTS3/4 tables found in database %s */\n", argv[1]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if( argc<4 ) usage(argv[0]);
|
||||
zTab = argv[2];
|
||||
zCmd = argv[3];
|
||||
nExtra = argc-4;
|
||||
azExtra = argv+4;
|
||||
if( strcmp(zCmd,"big-segments")==0 ){
|
||||
listBigSegments(db, zTab);
|
||||
}else if( strcmp(zCmd,"doclist")==0 ){
|
||||
if( argc<7 ) usage(argv[0]);
|
||||
showDoclist(db, zTab);
|
||||
}else if( strcmp(zCmd,"schema")==0 ){
|
||||
showSchema(db, zTab);
|
||||
}else if( strcmp(zCmd,"segdir")==0 ){
|
||||
showSegdirMap(db, zTab);
|
||||
}else if( strcmp(zCmd,"segment")==0 ){
|
||||
if( argc<5 ) usage(argv[0]);
|
||||
showSegment(db, zTab);
|
||||
}else if( strcmp(zCmd,"segment-stats")==0 ){
|
||||
showSegmentStats(db, zTab);
|
||||
}else if( strcmp(zCmd,"stat")==0 ){
|
||||
showStat(db, zTab);
|
||||
}else if( strcmp(zCmd,"vocabulary")==0 ){
|
||||
showVocabulary(db, zTab);
|
||||
}else{
|
||||
usage(argv[0]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
C Simplify\sthe\swinRead\sand\swinWrite\sVFS\sfunctions\sto\sreduce\sthe\snumber\sof\ssystem\scalls.
|
||||
D 2012-03-30T12:27:55.620
|
||||
C Merge\sthe\slatest\strunk\schanges\sinto\sthe\swinrt\sbranch.
|
||||
D 2012-03-30T14:01:40.977
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in 2f37e468503dbe79d35c9f6dffcf3fae1ae9ec20
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
F Makefile.msc 47c5fd7e41e773835f78e2d8d32f67b0c8734113
|
||||
F Makefile.vxworks 3b7fe7a0571fdadc61363ebc1b23732d2d6363ca
|
||||
F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6
|
||||
F VERSION bb4c2a86abe53ea3c1d6ea515b69a426040e2414
|
||||
F VERSION f9313d88cb77df8617059a88eb382291321ef6bc
|
||||
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
|
||||
F addopcodes.awk 17dc593f791f874d2c23a0f9360850ded0286531
|
||||
F art/2005osaward.gif 0d1851b2a7c1c9d0ccce545f3e14bca42d7fd248
|
||||
@@ -63,24 +63,25 @@ F ext/fts3/README.content fdc666a70d5257a64fee209f97cf89e0e6e32b51
|
||||
F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a
|
||||
F ext/fts3/README.tokenizers 998756696647400de63d5ba60e9655036cb966e9
|
||||
F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
|
||||
F ext/fts3/fts3.c a62e09140c00f9c401efca661e7f2ae9909194f6
|
||||
F ext/fts3/fts3.c 111626ce72b0df93f509ebd14ce31804fed24be0
|
||||
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
|
||||
F ext/fts3/fts3Int.h d1d7f964ddee067bcd16a6af4ba7ecf66220056d
|
||||
F ext/fts3/fts3Int.h 5fd2ec4e47faf17bf4a508d6b8ec5fc0f2c80bff
|
||||
F ext/fts3/fts3_aux.c 5205182bd8f372782597888156404766edf5781e
|
||||
F ext/fts3/fts3_expr.c dbc7ba4c3a6061adde0f38ed8e9b349568299551
|
||||
F ext/fts3/fts3_hash.c 8dd2d06b66c72c628c2732555a32bc0943114914
|
||||
F ext/fts3/fts3_hash.h 8331fb2206c609f9fc4c4735b9ab5ad6137c88ec
|
||||
F ext/fts3/fts3_icu.c 6c8f395cdf9e1e3afa7fadb7e523dbbf381c6dfa
|
||||
F ext/fts3/fts3_porter.c a465b49fcb8249a755792f87516eff182efa42b3
|
||||
F ext/fts3/fts3_snippet.c c9e126c20760988aa7c43c6ea1379db34738282e
|
||||
F ext/fts3/fts3_snippet.c 51a3a34c217e24678a133782c1dfb6f2f70fe559
|
||||
F ext/fts3/fts3_term.c d3466cf99432291be08e379d89645462431809d6
|
||||
F ext/fts3/fts3_test.c 6b7cc68aef4efb084e1449f7d20c4b20d3bdf6b4
|
||||
F ext/fts3/fts3_tokenizer.c 3da7254a9881f7e270ab28e2004e0d22b3212bce
|
||||
F ext/fts3/fts3_tokenizer.h 66dec98e365854b6cd2d54f1a96bb6d428fc5a68
|
||||
F ext/fts3/fts3_tokenizer1.c 5c98225a53705e5ee34824087478cf477bdb7004
|
||||
F ext/fts3/fts3_write.c 8eedfeb2e61114cfd94fd1d917daf3658203332c
|
||||
F ext/fts3/fts3_write.c 545c3e2add64c27b2b03f9c79619ac5e47043252
|
||||
F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9
|
||||
F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100
|
||||
F ext/fts3/tool/fts3view.c 6cfc5b67a5f0e09c0d698f9fd012c784bfaa9197
|
||||
F ext/icu/README.txt bf8461d8cdc6b8f514c080e4e10dc3b2bbdfefa9
|
||||
F ext/icu/icu.c eb9ae1d79046bd7871aa97ee6da51eb770134b5a
|
||||
F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37
|
||||
@@ -125,19 +126,19 @@ F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
|
||||
F src/backup.c 6be23a344d3301ae38e92fddb3a33b91c309fce4
|
||||
F src/bitvec.c af50f1c8c0ff54d6bdb7a80e2fceca5a93670bef
|
||||
F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7
|
||||
F src/btree.c 253c3147a4ebbaee42cd329dbdc0856200bbbda7
|
||||
F src/btree.c 02aeee1f6d425e11f7b9b2d9d461ac501645ed6f
|
||||
F src/btree.h 48a013f8964f12d944d90e4700df47b72dd6d923
|
||||
F src/btreeInt.h 26d8ca625b141927fe6620c1d2cf58eaf494ca0c
|
||||
F src/build.c c4d36e527f457f9992a6663365871dfa7c5094b8
|
||||
F src/build.c 139dc386ebdaa78d2198247ebcf003305a5babe3
|
||||
F src/callback.c 0425c6320730e6d3981acfb9202c1bed9016ad1a
|
||||
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
|
||||
F src/ctime.c 500d019da966631ad957c37705642be87524463b
|
||||
F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4
|
||||
F src/delete.c 51d32f0a9c880663e54ce309f52e40c325d5e112
|
||||
F src/expr.c 00675123e0beec98f999aa4594d2cbe1fec33c1b
|
||||
F src/delete.c 4c20ea4f6213b3bc1c6a510586864b679946e05e
|
||||
F src/expr.c 7e40ea9f6899e31134be3c1b88b8347cf9ec40d7
|
||||
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
||||
F src/fkey.c 657212460bf5cfd3ae607d12ea62092844c227b5
|
||||
F src/func.c e75f41c421f00762ab9da7dc8fb90af3972cf99f
|
||||
F src/func.c c6b3c94320253a35bda43fb69cc292618e3285d6
|
||||
F src/global.c 4cfdca5cb0edd33c4d021baec4ede958cb2c793b
|
||||
F src/hash.c 458488dcc159c301b8e7686280ab209f1fb915af
|
||||
F src/hash.h 2894c932d84d9f892d4b4023a75e501f83050970
|
||||
@@ -168,9 +169,9 @@ F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04
|
||||
F src/os_os2.c 4a75888ba3dfc820ad5e8177025972d74d7f2440
|
||||
F src/os_unix.c 0e3d2942d228d0366fb80a3640f35caf413b66d1
|
||||
F src/os_win.c 6f8080e783082d3d94ea56537f2248df4e273843
|
||||
F src/pager.c 3955b62cdb5bb64559607cb474dd12a6c8e1d4a5
|
||||
F src/pager.c 85988507fa20acc60defb834722eddf4633e4aeb
|
||||
F src/pager.h ef1eaf8593e78f73885c1dfac27ad83bee23bdc5
|
||||
F src/parse.y 1ddd71ae55f4b7cbb2672526ea4de023de0f519e
|
||||
F src/parse.y 537c8db136af5f481630becdc0c8bdd36a704c30
|
||||
F src/pcache.c f8043b433a57aba85384a531e3937a804432a346
|
||||
F src/pcache.h 1b5dcc3dc8103d03e625b177023ee67764fa6b7c
|
||||
F src/pcache1.c b30b1c35908346ecc43d8d9d17f2ddf6817f8f60
|
||||
@@ -180,15 +181,15 @@ F src/printf.c 7ffb4ebb8b341f67e049695ba031da717b3d2699
|
||||
F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
|
||||
F src/resolve.c 3d3e80a98f203ac6b9329e9621e29eda85ddfd40
|
||||
F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
|
||||
F src/select.c 44ccdcb5d2a1c48622c179b2d72167b716388581
|
||||
F src/shell.c aa28f117033ba3e44b5eaaf2ad572222bcdfd66e
|
||||
F src/sqlite.h.in a3516084e920db2599c76f1286123adec5b20c37
|
||||
F src/select.c f6f141cb1ea13f1e6564d3e162700e4937baa2a1
|
||||
F src/shell.c abf18d6ee54f2631860a98fdd7ab1327f470db67
|
||||
F src/sqlite.h.in d7f0850ad874328e7ca477f836e8c5d189fbc7a2
|
||||
F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477
|
||||
F src/sqliteInt.h e65429a6f19b41720561b9434b2192574a91cfa2
|
||||
F src/sqliteInt.h 3756ece33f1e7f8fe2adf8e523566825c809316e
|
||||
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
||||
F src/status.c 4568e72dfd36b6a5911f93457364deb072e0b03a
|
||||
F src/status.c 35939e7e03abf1b7577ce311f48f682c40de3208
|
||||
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
|
||||
F src/tclsqlite.c 2aeb69958965dad0842d5ea1456f1a958ef296e6
|
||||
F src/tclsqlite.c 086dfdd72e5892de223968a258e1ccbd9693e717
|
||||
F src/test1.c 1a1df7eed3e8b58987b5716de7a37cc79a10f3f2
|
||||
F src/test2.c 711555927f1f7e8db9aab86b512bc6934a774abe
|
||||
F src/test3.c 91d3f1a09cfae3533ef17d8b484a160f3d1f1a21
|
||||
@@ -196,7 +197,7 @@ F src/test4.c d1e5a5e904d4b444cf572391fdcb017638e36ff7
|
||||
F src/test5.c a6d1ac55ac054d0b2b8f37b5e655b6c92645a013
|
||||
F src/test6.c 846ed1ed2f470de9b1e205fe3878a12e237b3e19
|
||||
F src/test7.c 2e0781754905c8adc3268d8f0967e7633af58843
|
||||
F src/test8.c 99f70341d6ec480313775127f4cd14b4a47db557
|
||||
F src/test8.c 61b41d79509a479dec1ac32b6d4209b27c4b1ba5
|
||||
F src/test9.c bea1e8cf52aa93695487badedd6e1886c321ea60
|
||||
F src/test_async.c 0612a752896fad42d55c3999a5122af10dcf22ad
|
||||
F src/test_autoext.c 30e7bd98ab6d70a62bb9ba572e4c7df347fe645e
|
||||
@@ -213,7 +214,7 @@ F src/test_intarray.c d879bbf8e4ce085ab966d1f3c896a7c8b4f5fc99
|
||||
F src/test_intarray.h 489edb9068bb926583445cb02589344961054207
|
||||
F src/test_journal.c a6a6baf343f79b942331f13378d045e7e270ae64
|
||||
F src/test_loadext.c df586c27176e3c2cb2e099c78da67bf14379a56e
|
||||
F src/test_malloc.c cfe25d74333892ababde61196821a889b4756dee
|
||||
F src/test_malloc.c 3f5903a1528fd32fe4c472a3bd0259128d8faaef
|
||||
F src/test_multiplex.c 0404a61d7795438be5ee5fd3711eed80724df34d
|
||||
F src/test_multiplex.h e99c571bc4968b7a9363b661481f3934bfead61d
|
||||
F src/test_mutex.c a6bd7b9cf6e19d989e31392b06ac8d189f0d573e
|
||||
@@ -240,7 +241,7 @@ F src/update.c d3076782c887c10e882996550345da9c4c9f9dea
|
||||
F src/utf.c 890c67dcfcc7a74623c95baac7535aadfe265e84
|
||||
F src/util.c 4f6cfad661b2e3454b0cdd5b1b9d39a54942d0e3
|
||||
F src/vacuum.c bfd53f9bd20a8fdb70b0fa8e77182b866875c0d8
|
||||
F src/vdbe.c 32720e873ed0a23e6ee928b676cd995864b984d6
|
||||
F src/vdbe.c 8913926230bfc9d183fcd44e3d023c6d393b6548
|
||||
F src/vdbe.h 18f581cac1f4339ec3299f3e0cc6e11aec654cdb
|
||||
F src/vdbeInt.h 6ff4180a05683566a8835d12f7ec504b22932c82
|
||||
F src/vdbeapi.c 3662b6a468a2a4605a15dfab313baa6dff81ad91
|
||||
@@ -253,7 +254,7 @@ F src/vtab.c ab90fb600a3f5e4b7c48d22a4cdb2d6b23239847
|
||||
F src/wal.c 7bb3ad807afc7973406c805d5157ec7a2f65e146
|
||||
F src/wal.h 29c197540b19044e6cd73487017e5e47a1d3dac6
|
||||
F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
|
||||
F src/where.c 6baab5dfcf4472552c0346d04f6fd2f4f8539c78
|
||||
F src/where.c 2112422a404dcca5d47f6630bdf180bccd36c62b
|
||||
F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823
|
||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
|
||||
@@ -288,13 +289,14 @@ F test/autoindex1.test 058d0b331ae6840a61bbee910d8cbae27bfd5991
|
||||
F test/autovacuum.test fcaf4616ae5bb18098db1cb36262565e5c841c3c
|
||||
F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4
|
||||
F test/avtrans.test 0252654f4295ddda3b2cce0e894812259e655a85
|
||||
F test/backcompat.test 71eeb75ea567c060774c4e8db4b0e703f21c7677
|
||||
F test/backcompat.test 94778872ed9f6158ba1b8534264d3413ae7e27f8
|
||||
F test/backup.test 717346db953e9e435c2a94916e4af177330d60d3
|
||||
F test/backup2.test 34986ef926ea522911a51dfdb2f8e99b7b75ebcf
|
||||
F test/backup_ioerr.test 40d208bc9224b666ee3ed423f49bc9062a36a9d0
|
||||
F test/backup_malloc.test 7162d604ec2b4683c4b3799a48657fb8b5e2d450
|
||||
F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f
|
||||
F test/badutf2.test f5bc7f2d280670ecd79b9cf4f0f1760c607fe51f
|
||||
F test/bc_common.tcl 5c8689cc6d2fb44b7c0968ae4f85eb26d50022fa
|
||||
F test/between.test 16b1776c6323faadb097a52d673e8e3d8be7d070
|
||||
F test/bigfile.test 8f88b5ef065e31c615c49d725ede94155fbe9609
|
||||
F test/bigfile2.test 8a3c242c3c3481e7cde5a6ef2a66fdc367a095f7
|
||||
@@ -365,7 +367,7 @@ F test/cse.test 277350a26264495e86b1785f34d2d0c8600e021c
|
||||
F test/ctime.test 7bd009071e242aac4f18521581536b652b789a47
|
||||
F test/date.test a18a2ce81add84b17b06559e82ad7bb91bc6ddff
|
||||
F test/dbstatus.test 207e5b63fcb7b9c3bb8e1fdf38ebd4654ad0e54b
|
||||
F test/dbstatus2.test dc57b0d9610851c0ff58a8e1b5b191678398b72a
|
||||
F test/dbstatus2.test b1de8250fde1f3474d6b86f0e89de38d84794f56
|
||||
F test/default.test 6faf23ccb300114924353007795aa9a8ec0aa9dc
|
||||
F test/delete.test a065b05d2ebf60fd16639c579a4adfb7c381c701
|
||||
F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa
|
||||
@@ -448,7 +450,7 @@ F test/fts2q.test b2fbbe038b7a31a52a6079b215e71226d8c6a682
|
||||
F test/fts2r.test b154c30b63061d8725e320fba1a39e2201cadd5e
|
||||
F test/fts2token.test d8070b241a15ff13592a9ae4a8b7c171af6f445a
|
||||
F test/fts3.test 672a040ea57036fb4b6fdc09027c18d7d24ab654
|
||||
F test/fts3_common.tcl 4d8eec9db565fed9098f45c378f28e1657802011
|
||||
F test/fts3_common.tcl 99cf6659b87c0f74f55963c2aea03b3a7d66ceb0
|
||||
F test/fts3aa.test 909d5f530d30a8e36b9328d67285eae6537c79c0
|
||||
F test/fts3ab.test 09aeaa162aee6513d9ff336b6932211008b9d1f9
|
||||
F test/fts3ac.test 636ed7486043055d4f126a0e385f2d5a82ebbf63
|
||||
@@ -495,9 +497,13 @@ F test/fts3shared.test 8bb266521d7c5495c0ae522bb4d376ad5387d4a2
|
||||
F test/fts3snippet.test 8e956051221a34c7daeb504f023cb54d5fa5a8b2
|
||||
F test/fts3sort.test 95be0b19d7e41c44b29014f13ea8bddd495fd659
|
||||
F test/fts4aa.test 6e7f90420b837b2c685f3bcbe84c868492d40a68
|
||||
F test/fts4check.test 66fa274cab2b615f2fb338b257713aba8fad88a8
|
||||
F test/fts4content.test 17b2360f7d1a9a7e5aa8022783f5c5731b6dfd4f
|
||||
F test/fts4langid.test 2081c357bb6f170f34ef8e08c6abb88002b95c69
|
||||
F test/func.test 6c5ce11e3a0021ca3c0649234e2d4454c89110ca
|
||||
F test/fts4langid.test 24a6e41063b416bbdf371ff6b4476fa41c194aa7
|
||||
F test/fts4merge.test c424309743fdd203f8e56a1f1cd7872cd66cc0ee
|
||||
F test/fts4merge2.test 5faa558d1b672f82b847d2a337465fa745e46891
|
||||
F test/fts4merge3.test 640611c05f01e9e018ac10afd188b018d8fcd4e5
|
||||
F test/func.test 9809b7622d721904a8cc33c1ffb87f46d506ed01
|
||||
F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f
|
||||
F test/func3.test 001021e5b88bd02a3b365a5c5fd8f6f49d39744a
|
||||
F test/fuzz.test 77fd50afc12847af50fcf1941679d90adebadde6
|
||||
@@ -516,6 +522,7 @@ F test/in4.test 64f3cc1acde1b9161ccdd8e5bde3daefdb5b2617
|
||||
F test/incrblob.test 26fde912a1e0aff158b3a84ef3b265f046aad3be
|
||||
F test/incrblob2.test edc3a96e557bd61fb39acc8d2edd43371fbbaa19
|
||||
F test/incrblob3.test aedbb35ea1b6450c33b98f2b6ed98e5020be8dc7
|
||||
F test/incrblob4.test 09be37d3dd996a31ea6993bba7837ece549414a8
|
||||
F test/incrblob_err.test d2562d2771ebffd4b3af89ef64c140dd44371597
|
||||
F test/incrblobfault.test 917c0292224c64a56ef7215fd633a3a82f805be0
|
||||
F test/incrvacuum.test d2a6ddf5e429720b5fe502766af747915ccf6c32
|
||||
@@ -625,7 +632,7 @@ F test/notnull.test cc7c78340328e6112a13c3e311a9ab3127114347
|
||||
F test/null.test a8b09b8ed87852742343b33441a9240022108993
|
||||
F test/openv2.test 0d3040974bf402e19b7df4b783e447289d7ab394
|
||||
F test/oserror.test 50417780d0e0d7cd23cf12a8277bb44024765df3
|
||||
F test/pager1.test 41995062277f6b9c5d57ce074e814ea4285a5c76
|
||||
F test/pager1.test 819a0ecc0ecf4850155cf18dcd2754121d1018b3
|
||||
F test/pager2.test 745b911dde3d1f24ae0870bd433dfa83d7c658c1
|
||||
F test/pager3.test 3856d9c80839be0668efee1b74811b1b7f7fc95f
|
||||
F test/pagerfault.test 452f2cc23e3bfcfa935f4442aec1da4fe1dc0442
|
||||
@@ -635,7 +642,7 @@ F test/pageropt.test 9191867ed19a2b3db6c42d1b36b6fbc657cd1ab0
|
||||
F test/pagesize.test 1dd51367e752e742f58e861e65ed7390603827a0
|
||||
F test/pcache.test 065aa286e722ab24f2e51792c1f093bf60656b16
|
||||
F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025
|
||||
F test/permutations.test 2b5a1b64a8e5114757457fbce9010387d1fe7682
|
||||
F test/permutations.test dbda172249564f43ec556108a704581044c57dbd
|
||||
F test/pragma.test eba5bd337ae68870985cd1776659bb136b33dada
|
||||
F test/pragma2.test 3a55f82b954242c642f8342b17dffc8b47472947
|
||||
F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552
|
||||
@@ -855,7 +862,7 @@ F test/tkt3997.test a335fa41ca3985660a139df7b734a26ef53284bd
|
||||
F test/tkt4018.test 7c2c9ba4df489c676a0a7a0e809a1fb9b2185bd1
|
||||
F test/tokenize.test ce430a7aed48fc98301611429595883fdfcab5d7
|
||||
F test/trace.test 4b36a41a3e9c7842151af6da5998f5080cdad9e5
|
||||
F test/trace2.test 37c3a05701e7f133ebdf078a61aa9479a23a73b0
|
||||
F test/trace2.test c1dc104a8d11a347c870cfea6235e3fc6f6cb06d
|
||||
F test/trans.test 6e1b4c6a42dba31bd65f8fa5e61a2708e08ddde6
|
||||
F test/trans2.test d5337e61de45e66b1fcbf9db833fa8c82e624b22
|
||||
F test/trans3.test 373ac5183cc56be69f48ae44090e7f672939f732
|
||||
@@ -889,7 +896,7 @@ F test/vacuum4.test d3f8ecff345f166911568f397d2432c16d2867d9
|
||||
F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102
|
||||
F test/veryquick.test 7701bb609fe8bf6535514e8b849a309e8f00573b
|
||||
F test/view.test b182a67ec43f490b156b5a710827a341be83dd17
|
||||
F test/vtab1.test 17d0db1096b603a357f943a9dcbdc3e30c6f04f2
|
||||
F test/vtab1.test e429a6835faa3870016c55d1178dcfead85f936a
|
||||
F test/vtab2.test 7bcffc050da5c68f4f312e49e443063e2d391c0d
|
||||
F test/vtab3.test baad99fd27217f5d6db10660522e0b7192446de1
|
||||
F test/vtab4.test 942f8b8280b3ea8a41dae20e7822d065ca1cb275
|
||||
@@ -992,7 +999,7 @@ F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06
|
||||
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
|
||||
F tool/warnings-clang.sh 9f406d66e750e8ac031c63a9ef3248aaa347ef2a
|
||||
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
|
||||
P c10794bfac0989c611ec3ea98d069cb9631a7b15
|
||||
R 23bdb0c42f14336dabd08b128245de8d
|
||||
U mistachkin
|
||||
Z 736498e20bf38fd876d04def8440845b
|
||||
P 10ce846759f6f22e70bb9b67bea7a0c2b8a156fe 36aa6665e709b5942b3558afbd555058b42f2c78
|
||||
R cfe4287a455a52ef8be1acca1474bbf5
|
||||
U drh
|
||||
Z 8c9488054f5fc194d8a0020e67475da7
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
10ce846759f6f22e70bb9b67bea7a0c2b8a156fe
|
||||
a1a85b846aac07979e732a9f36d178bd567e103a
|
||||
+20
-18
@@ -6789,13 +6789,6 @@ int sqlite3BtreeInsert(
|
||||
** blob of associated data. */
|
||||
assert( (pKey==0)==(pCur->pKeyInfo==0) );
|
||||
|
||||
/* If this is an insert into a table b-tree, invalidate any incrblob
|
||||
** cursors open on the row being replaced (assuming this is a replace
|
||||
** operation - if it is not, the following is a no-op). */
|
||||
if( pCur->pKeyInfo==0 ){
|
||||
invalidateIncrblobCursors(p, nKey, 0);
|
||||
}
|
||||
|
||||
/* Save the positions of any other cursors open on this table.
|
||||
**
|
||||
** In some cases, the call to btreeMoveto() below is a no-op. For
|
||||
@@ -6809,6 +6802,14 @@ int sqlite3BtreeInsert(
|
||||
*/
|
||||
rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
|
||||
if( rc ) return rc;
|
||||
|
||||
/* If this is an insert into a table b-tree, invalidate any incrblob
|
||||
** cursors open on the row being replaced (assuming this is a replace
|
||||
** operation - if it is not, the following is a no-op). */
|
||||
if( pCur->pKeyInfo==0 ){
|
||||
invalidateIncrblobCursors(p, nKey, 0);
|
||||
}
|
||||
|
||||
if( !loc ){
|
||||
rc = btreeMoveto(pCur, pKey, nKey, appendBias, &loc);
|
||||
if( rc ) return rc;
|
||||
@@ -6919,12 +6920,6 @@ int sqlite3BtreeDelete(BtCursor *pCur){
|
||||
return SQLITE_ERROR; /* Something has gone awry. */
|
||||
}
|
||||
|
||||
/* If this is a delete operation to remove a row from a table b-tree,
|
||||
** invalidate any incrblob cursors open on the row being deleted. */
|
||||
if( pCur->pKeyInfo==0 ){
|
||||
invalidateIncrblobCursors(p, pCur->info.nKey, 0);
|
||||
}
|
||||
|
||||
iCellDepth = pCur->iPage;
|
||||
iCellIdx = pCur->aiIdx[iCellDepth];
|
||||
pPage = pCur->apPage[iCellDepth];
|
||||
@@ -6950,6 +6945,13 @@ int sqlite3BtreeDelete(BtCursor *pCur){
|
||||
*/
|
||||
rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
|
||||
if( rc ) return rc;
|
||||
|
||||
/* If this is a delete operation to remove a row from a table b-tree,
|
||||
** invalidate any incrblob cursors open on the row being deleted. */
|
||||
if( pCur->pKeyInfo==0 ){
|
||||
invalidateIncrblobCursors(p, pCur->info.nKey, 0);
|
||||
}
|
||||
|
||||
rc = sqlite3PagerWrite(pPage->pDbPage);
|
||||
if( rc ) return rc;
|
||||
rc = clearCell(pPage, pCell);
|
||||
@@ -7231,13 +7233,13 @@ int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){
|
||||
sqlite3BtreeEnter(p);
|
||||
assert( p->inTrans==TRANS_WRITE );
|
||||
|
||||
/* Invalidate all incrblob cursors open on table iTable (assuming iTable
|
||||
** is the root of a table b-tree - if it is not, the following call is
|
||||
** a no-op). */
|
||||
invalidateIncrblobCursors(p, 0, 1);
|
||||
|
||||
rc = saveAllCursors(pBt, (Pgno)iTable, 0);
|
||||
|
||||
if( SQLITE_OK==rc ){
|
||||
/* Invalidate all incrblob cursors open on table iTable (assuming iTable
|
||||
** is the root of a table b-tree - if it is not, the following call is
|
||||
** a no-op). */
|
||||
invalidateIncrblobCursors(p, 0, 1);
|
||||
rc = clearDatabasePage(pBt, (Pgno)iTable, 0, pnChange);
|
||||
}
|
||||
sqlite3BtreeLeave(p);
|
||||
|
||||
+12
-10
@@ -3040,19 +3040,21 @@ exit_drop_index:
|
||||
}
|
||||
|
||||
/*
|
||||
** pArray is a pointer to an array of objects. Each object in the
|
||||
** array is szEntry bytes in size. This routine allocates a new
|
||||
** object on the end of the array.
|
||||
** pArray is a pointer to an array of objects. Each object in the
|
||||
** array is szEntry bytes in size. This routine uses sqlite3DbRealloc()
|
||||
** to extend the array so that there is space for a new object at the end.
|
||||
**
|
||||
** *pnEntry is the number of entries already in use. *pnAlloc is
|
||||
** the previously allocated size of the array. initSize is the
|
||||
** suggested initial array size allocation.
|
||||
** When this function is called, *pnEntry contains the current size of
|
||||
** the array (in entries - so the allocation is ((*pnEntry) * szEntry) bytes
|
||||
** in total).
|
||||
**
|
||||
** The index of the new entry is returned in *pIdx.
|
||||
** If the realloc() is successful (i.e. if no OOM condition occurs), the
|
||||
** space allocated for the new object is zeroed, *pnEntry updated to
|
||||
** reflect the new size of the array and a pointer to the new allocation
|
||||
** returned. *pIdx is set to the index of the new array entry in this case.
|
||||
**
|
||||
** This routine returns a pointer to the array of objects. This
|
||||
** might be the same as the pArray parameter or it might be a different
|
||||
** pointer if the array was resized.
|
||||
** Otherwise, if the realloc() fails, *pIdx is set to -1, *pnEntry remains
|
||||
** unchanged and a copy of pArray returned.
|
||||
*/
|
||||
void *sqlite3ArrayAllocate(
|
||||
sqlite3 *db, /* Connection to notify of malloc failures */
|
||||
|
||||
+1
-1
@@ -374,7 +374,7 @@ void sqlite3DeleteFrom(
|
||||
pParse, pTabList, pWhere, 0, 0, WHERE_DUPLICATES_OK
|
||||
);
|
||||
if( pWInfo==0 ) goto delete_from_cleanup;
|
||||
regRowid = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, iRowid);
|
||||
regRowid = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, iRowid, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, regRowid);
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1);
|
||||
|
||||
+28
-12
@@ -2032,15 +2032,6 @@ void sqlite3ExprCacheStore(Parse *pParse, int iTab, int iCol, int iReg){
|
||||
*/
|
||||
#ifndef NDEBUG
|
||||
for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
|
||||
#if 0 /* This code wold remove the entry from the cache if it existed */
|
||||
if( p->iReg && p->iTable==iTab && p->iColumn==iCol ){
|
||||
cacheEntryClear(pParse, p);
|
||||
p->iLevel = pParse->iCacheLevel;
|
||||
p->iReg = iReg;
|
||||
p->lru = pParse->iCacheCnt++;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
assert( p->iReg==0 || p->iTable!=iTab || p->iColumn!=iCol );
|
||||
}
|
||||
#endif
|
||||
@@ -2175,7 +2166,8 @@ int sqlite3ExprCodeGetColumn(
|
||||
Table *pTab, /* Description of the table we are reading from */
|
||||
int iColumn, /* Index of the table column */
|
||||
int iTable, /* The cursor pointing to the table */
|
||||
int iReg /* Store results here */
|
||||
int iReg, /* Store results here */
|
||||
u8 p5 /* P5 value for OP_Column */
|
||||
){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
int i;
|
||||
@@ -2190,7 +2182,11 @@ int sqlite3ExprCodeGetColumn(
|
||||
}
|
||||
assert( v!=0 );
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iTable, iColumn, iReg);
|
||||
sqlite3ExprCacheStore(pParse, iTable, iColumn, iReg);
|
||||
if( p5 ){
|
||||
sqlite3VdbeChangeP5(v, p5);
|
||||
}else{
|
||||
sqlite3ExprCacheStore(pParse, iTable, iColumn, iReg);
|
||||
}
|
||||
return iReg;
|
||||
}
|
||||
|
||||
@@ -2318,7 +2314,8 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
inReg = pExpr->iColumn + pParse->ckBase;
|
||||
}else{
|
||||
inReg = sqlite3ExprCodeGetColumn(pParse, pExpr->pTab,
|
||||
pExpr->iColumn, pExpr->iTable, target);
|
||||
pExpr->iColumn, pExpr->iTable, target,
|
||||
pExpr->op2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2595,6 +2592,25 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
|
||||
if( pFarg ){
|
||||
r1 = sqlite3GetTempRange(pParse, nFarg);
|
||||
|
||||
/* For length() and typeof() functions with a column argument,
|
||||
** set the P5 parameter to the OP_Column opcode to OPFLAG_LENGTHARG
|
||||
** or OPFLAG_TYPEOFARG respectively, to avoid unnecessary data
|
||||
** loading.
|
||||
*/
|
||||
if( (pDef->flags & (SQLITE_FUNC_LENGTH|SQLITE_FUNC_TYPEOF))!=0 ){
|
||||
u8 exprOp;
|
||||
assert( nFarg==1 );
|
||||
assert( pFarg->a[0].pExpr!=0 );
|
||||
exprOp = pFarg->a[0].pExpr->op;
|
||||
if( exprOp==TK_COLUMN || exprOp==TK_AGG_COLUMN ){
|
||||
assert( SQLITE_FUNC_LENGTH==OPFLAG_LENGTHARG );
|
||||
assert( SQLITE_FUNC_TYPEOF==OPFLAG_TYPEOFARG );
|
||||
testcase( pDef->flags==SQLITE_FUNC_LENGTH );
|
||||
pFarg->a[0].pExpr->op2 = pDef->flags;
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3ExprCachePush(pParse); /* Ticket 2ea2425d34be */
|
||||
sqlite3ExprCodeExprList(pParse, pFarg, r1, 1);
|
||||
sqlite3ExprCachePop(pParse, 1); /* Ticket 2ea2425d34be */
|
||||
|
||||
+4
-6
@@ -1542,8 +1542,8 @@ void sqlite3RegisterGlobalFunctions(void){
|
||||
FUNCTION(max, -1, 1, 1, minmaxFunc ),
|
||||
FUNCTION(max, 0, 1, 1, 0 ),
|
||||
AGGREGATE(max, 1, 1, 1, minmaxStep, minMaxFinalize ),
|
||||
FUNCTION(typeof, 1, 0, 0, typeofFunc ),
|
||||
FUNCTION(length, 1, 0, 0, lengthFunc ),
|
||||
FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF),
|
||||
FUNCTION2(length, 1, 0, 0, lengthFunc, SQLITE_FUNC_LENGTH),
|
||||
FUNCTION(substr, 2, 0, 0, substrFunc ),
|
||||
FUNCTION(substr, 3, 0, 0, substrFunc ),
|
||||
FUNCTION(abs, 1, 0, 0, absFunc ),
|
||||
@@ -1555,11 +1555,9 @@ void sqlite3RegisterGlobalFunctions(void){
|
||||
FUNCTION(lower, 1, 0, 0, lowerFunc ),
|
||||
FUNCTION(coalesce, 1, 0, 0, 0 ),
|
||||
FUNCTION(coalesce, 0, 0, 0, 0 ),
|
||||
/* FUNCTION(coalesce, -1, 0, 0, ifnullFunc ), */
|
||||
{-1,SQLITE_UTF8,SQLITE_FUNC_COALESCE,0,0,ifnullFunc,0,0,"coalesce",0,0},
|
||||
FUNCTION2(coalesce, -1, 0, 0, ifnullFunc, SQLITE_FUNC_COALESCE),
|
||||
FUNCTION(hex, 1, 0, 0, hexFunc ),
|
||||
/* FUNCTION(ifnull, 2, 0, 0, ifnullFunc ), */
|
||||
{2,SQLITE_UTF8,SQLITE_FUNC_COALESCE,0,0,ifnullFunc,0,0,"ifnull",0,0},
|
||||
FUNCTION2(ifnull, 2, 0, 0, ifnullFunc, SQLITE_FUNC_COALESCE),
|
||||
FUNCTION(random, 0, 0, 0, randomFunc ),
|
||||
FUNCTION(randomblob, 1, 0, 0, randomBlob ),
|
||||
FUNCTION(nullif, 2, 0, 1, nullifFunc ),
|
||||
|
||||
+35
-17
@@ -670,9 +670,9 @@ struct Pager {
|
||||
char *zJournal; /* Name of the journal file */
|
||||
int (*xBusyHandler)(void*); /* Function to call when busy */
|
||||
void *pBusyHandlerArg; /* Context argument for xBusyHandler */
|
||||
int nHit, nMiss; /* Total cache hits and misses */
|
||||
int aStat[3]; /* Total cache hits, misses and writes */
|
||||
#ifdef SQLITE_TEST
|
||||
int nRead, nWrite; /* Database pages read/written */
|
||||
int nRead; /* Database pages read */
|
||||
#endif
|
||||
void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
@@ -689,6 +689,15 @@ struct Pager {
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
** Indexes for use with Pager.aStat[]. The Pager.aStat[] array contains
|
||||
** the values accessed by passing SQLITE_DBSTATUS_CACHE_HIT, CACHE_MISS
|
||||
** or CACHE_WRITE to sqlite3_db_status().
|
||||
*/
|
||||
#define PAGER_STAT_HIT 0
|
||||
#define PAGER_STAT_MISS 1
|
||||
#define PAGER_STAT_WRITE 2
|
||||
|
||||
/*
|
||||
** The following global variables hold counters used for
|
||||
** testing purposes only. These variables do not exist in
|
||||
@@ -2971,6 +2980,7 @@ static int pagerWalFrames(
|
||||
int isCommit /* True if this is a commit */
|
||||
){
|
||||
int rc; /* Return code */
|
||||
int nList; /* Number of pages in pList */
|
||||
#if defined(SQLITE_DEBUG) || defined(SQLITE_CHECK_PAGES)
|
||||
PgHdr *p; /* For looping over pages */
|
||||
#endif
|
||||
@@ -2984,6 +2994,7 @@ static int pagerWalFrames(
|
||||
}
|
||||
#endif
|
||||
|
||||
assert( pList->pDirty==0 || isCommit );
|
||||
if( isCommit ){
|
||||
/* If a WAL transaction is being committed, there is no point in writing
|
||||
** any pages with page numbers greater than nTruncate into the WAL file.
|
||||
@@ -2991,11 +3002,18 @@ static int pagerWalFrames(
|
||||
** list here. */
|
||||
PgHdr *p;
|
||||
PgHdr **ppNext = &pList;
|
||||
nList = 0;
|
||||
for(p=pList; (*ppNext = p); p=p->pDirty){
|
||||
if( p->pgno<=nTruncate ) ppNext = &p->pDirty;
|
||||
if( p->pgno<=nTruncate ){
|
||||
ppNext = &p->pDirty;
|
||||
nList++;
|
||||
}
|
||||
}
|
||||
assert( pList );
|
||||
}else{
|
||||
nList = 1;
|
||||
}
|
||||
pPager->aStat[PAGER_STAT_WRITE] += nList;
|
||||
|
||||
if( pList->pgno==1 ) pager_write_changecounter(pList);
|
||||
rc = sqlite3WalFrames(pPager->pWal,
|
||||
@@ -4063,6 +4081,7 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
|
||||
if( pgno>pPager->dbFileSize ){
|
||||
pPager->dbFileSize = pgno;
|
||||
}
|
||||
pPager->aStat[PAGER_STAT_WRITE]++;
|
||||
|
||||
/* Update any backup objects copying the contents of this pager. */
|
||||
sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)pList->pData);
|
||||
@@ -4071,7 +4090,6 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
|
||||
PAGERID(pPager), pgno, pager_pagehash(pList)));
|
||||
IOTRACE(("PGOUT %p %d\n", pPager, pgno));
|
||||
PAGER_INCR(sqlite3_pager_writedb_count);
|
||||
PAGER_INCR(pPager->nWrite);
|
||||
}else{
|
||||
PAGERTRACE(("NOSTORE %d page %d\n", PAGERID(pPager), pgno));
|
||||
}
|
||||
@@ -5029,7 +5047,7 @@ int sqlite3PagerAcquire(
|
||||
/* In this case the pcache already contains an initialized copy of
|
||||
** the page. Return without further ado. */
|
||||
assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) );
|
||||
pPager->nHit++;
|
||||
pPager->aStat[PAGER_STAT_HIT]++;
|
||||
return SQLITE_OK;
|
||||
|
||||
}else{
|
||||
@@ -5071,7 +5089,7 @@ int sqlite3PagerAcquire(
|
||||
IOTRACE(("ZERO %p %d\n", pPager, pgno));
|
||||
}else{
|
||||
assert( pPg->pPager==pPager );
|
||||
pPager->nMiss++;
|
||||
pPager->aStat[PAGER_STAT_MISS]++;
|
||||
rc = readDbPage(pPg);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto pager_acquire_err;
|
||||
@@ -5656,6 +5674,7 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
|
||||
CODEC2(pPager, pPgHdr->pData, 1, 6, rc=SQLITE_NOMEM, zBuf);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3OsWrite(pPager->fd, zBuf, pPager->pageSize, 0);
|
||||
pPager->aStat[PAGER_STAT_WRITE]++;
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
pPager->changeCountDone = 1;
|
||||
@@ -6099,11 +6118,11 @@ int *sqlite3PagerStats(Pager *pPager){
|
||||
a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize;
|
||||
a[4] = pPager->eState;
|
||||
a[5] = pPager->errCode;
|
||||
a[6] = pPager->nHit;
|
||||
a[7] = pPager->nMiss;
|
||||
a[6] = pPager->aStat[PAGER_STAT_HIT];
|
||||
a[7] = pPager->aStat[PAGER_STAT_MISS];
|
||||
a[8] = 0; /* Used to be pPager->nOvfl */
|
||||
a[9] = pPager->nRead;
|
||||
a[10] = pPager->nWrite;
|
||||
a[10] = pPager->aStat[PAGER_STAT_WRITE];
|
||||
return a;
|
||||
}
|
||||
#endif
|
||||
@@ -6116,20 +6135,19 @@ int *sqlite3PagerStats(Pager *pPager){
|
||||
** returning.
|
||||
*/
|
||||
void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, int *pnVal){
|
||||
int *piStat;
|
||||
|
||||
assert( eStat==SQLITE_DBSTATUS_CACHE_HIT
|
||||
|| eStat==SQLITE_DBSTATUS_CACHE_MISS
|
||||
|| eStat==SQLITE_DBSTATUS_CACHE_WRITE
|
||||
);
|
||||
if( eStat==SQLITE_DBSTATUS_CACHE_HIT ){
|
||||
piStat = &pPager->nHit;
|
||||
}else{
|
||||
piStat = &pPager->nMiss;
|
||||
}
|
||||
|
||||
*pnVal += *piStat;
|
||||
assert( SQLITE_DBSTATUS_CACHE_HIT+1==SQLITE_DBSTATUS_CACHE_MISS );
|
||||
assert( SQLITE_DBSTATUS_CACHE_HIT+2==SQLITE_DBSTATUS_CACHE_WRITE );
|
||||
assert( PAGER_STAT_HIT==0 && PAGER_STAT_MISS==1 && PAGER_STAT_WRITE==2 );
|
||||
|
||||
*pnVal += pPager->aStat[eStat - SQLITE_DBSTATUS_CACHE_HIT];
|
||||
if( reset ){
|
||||
*piStat = 0;
|
||||
pPager->aStat[eStat - SQLITE_DBSTATUS_CACHE_HIT] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-7
@@ -75,7 +75,7 @@ struct LimitVal {
|
||||
*/
|
||||
struct LikeOp {
|
||||
Token eOperator; /* "like" or "glob" or "regexp" */
|
||||
int not; /* True if the NOT keyword is present */
|
||||
int bNot; /* True if the NOT keyword is present */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -881,16 +881,16 @@ expr(A) ::= expr(X) STAR|SLASH|REM(OP) expr(Y).
|
||||
{spanBinaryExpr(&A,pParse,@OP,&X,&Y);}
|
||||
expr(A) ::= expr(X) CONCAT(OP) expr(Y). {spanBinaryExpr(&A,pParse,@OP,&X,&Y);}
|
||||
%type likeop {struct LikeOp}
|
||||
likeop(A) ::= LIKE_KW(X). {A.eOperator = X; A.not = 0;}
|
||||
likeop(A) ::= NOT LIKE_KW(X). {A.eOperator = X; A.not = 1;}
|
||||
likeop(A) ::= MATCH(X). {A.eOperator = X; A.not = 0;}
|
||||
likeop(A) ::= NOT MATCH(X). {A.eOperator = X; A.not = 1;}
|
||||
likeop(A) ::= LIKE_KW(X). {A.eOperator = X; A.bNot = 0;}
|
||||
likeop(A) ::= NOT LIKE_KW(X). {A.eOperator = X; A.bNot = 1;}
|
||||
likeop(A) ::= MATCH(X). {A.eOperator = X; A.bNot = 0;}
|
||||
likeop(A) ::= NOT MATCH(X). {A.eOperator = X; A.bNot = 1;}
|
||||
expr(A) ::= expr(X) likeop(OP) expr(Y). [LIKE_KW] {
|
||||
ExprList *pList;
|
||||
pList = sqlite3ExprListAppend(pParse,0, Y.pExpr);
|
||||
pList = sqlite3ExprListAppend(pParse,pList, X.pExpr);
|
||||
A.pExpr = sqlite3ExprFunction(pParse, pList, &OP.eOperator);
|
||||
if( OP.not ) A.pExpr = sqlite3PExpr(pParse, TK_NOT, A.pExpr, 0, 0);
|
||||
if( OP.bNot ) A.pExpr = sqlite3PExpr(pParse, TK_NOT, A.pExpr, 0, 0);
|
||||
A.zStart = X.zStart;
|
||||
A.zEnd = Y.zEnd;
|
||||
if( A.pExpr ) A.pExpr->flags |= EP_InfixFunc;
|
||||
@@ -901,7 +901,7 @@ expr(A) ::= expr(X) likeop(OP) expr(Y) ESCAPE expr(E). [LIKE_KW] {
|
||||
pList = sqlite3ExprListAppend(pParse,pList, X.pExpr);
|
||||
pList = sqlite3ExprListAppend(pParse,pList, E.pExpr);
|
||||
A.pExpr = sqlite3ExprFunction(pParse, pList, &OP.eOperator);
|
||||
if( OP.not ) A.pExpr = sqlite3PExpr(pParse, TK_NOT, A.pExpr, 0, 0);
|
||||
if( OP.bNot ) A.pExpr = sqlite3PExpr(pParse, TK_NOT, A.pExpr, 0, 0);
|
||||
A.zStart = X.zStart;
|
||||
A.zEnd = E.zEnd;
|
||||
if( A.pExpr ) A.pExpr->flags |= EP_InfixFunc;
|
||||
|
||||
+1
-1
@@ -4228,7 +4228,7 @@ int sqlite3Select(
|
||||
int r2;
|
||||
|
||||
r2 = sqlite3ExprCodeGetColumn(pParse,
|
||||
pCol->pTab, pCol->iColumn, pCol->iTable, r1);
|
||||
pCol->pTab, pCol->iColumn, pCol->iTable, r1, 0);
|
||||
if( r1!=r2 ){
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, r2, r1);
|
||||
}
|
||||
|
||||
+22
-8
@@ -68,6 +68,8 @@
|
||||
# include <io.h>
|
||||
#define isatty(h) _isatty(h)
|
||||
#define access(f,m) _access((f),(m))
|
||||
#define popen(a,b) _popen((a),(b))
|
||||
#define pclose(x) _pclose(x)
|
||||
#else
|
||||
/* Make sure isatty() has a prototype.
|
||||
*/
|
||||
@@ -1077,6 +1079,9 @@ static int display_stats(
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1);
|
||||
fprintf(pArg->out, "Page cache misses: %d\n", iCur);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1);
|
||||
fprintf(pArg->out, "Page cache writes: %d\n", iCur);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset);
|
||||
fprintf(pArg->out, "Schema Heap Usage: %d bytes\n", iCur);
|
||||
iHiwtr = iCur = -1;
|
||||
@@ -1287,7 +1292,6 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
|
||||
char *zTableInfo = 0;
|
||||
char *zTmp = 0;
|
||||
int nRow = 0;
|
||||
int kk;
|
||||
|
||||
zTableInfo = appendText(zTableInfo, "PRAGMA table_info(", 0);
|
||||
zTableInfo = appendText(zTableInfo, zTable, '"');
|
||||
@@ -1300,12 +1304,9 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
|
||||
}
|
||||
|
||||
zSelect = appendText(zSelect, "SELECT 'INSERT INTO ' || ", 0);
|
||||
if( !isalpha(zTable[0]) ){
|
||||
kk = 0;
|
||||
}else{
|
||||
for(kk=1; isalnum(zTable[kk]); kk++){}
|
||||
}
|
||||
zTmp = appendText(zTmp, zTable, zTable[kk] ? '"' : 0);
|
||||
/* Always quote the table name, even if it appears to be pure ascii,
|
||||
** in case it is a keyword. Ex: INSERT INTO "table" ... */
|
||||
zTmp = appendText(zTmp, zTable, '"');
|
||||
if( zTmp ){
|
||||
zSelect = appendText(zSelect, zTmp, '\'');
|
||||
}
|
||||
@@ -2000,11 +2001,24 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
|
||||
if( c=='o' && strncmp(azArg[0], "output", n)==0 && nArg==2 ){
|
||||
if( p->out!=stdout ){
|
||||
fclose(p->out);
|
||||
if( p->outfile[0]=='|' ){
|
||||
pclose(p->out);
|
||||
}else{
|
||||
fclose(p->out);
|
||||
}
|
||||
}
|
||||
if( strcmp(azArg[1],"stdout")==0 ){
|
||||
p->out = stdout;
|
||||
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "stdout");
|
||||
}else if( azArg[1][0]=='|' ){
|
||||
p->out = popen(&azArg[1][1], "w");
|
||||
if( p->out==0 ){
|
||||
fprintf(stderr,"Error: cannot open pipe \"%s\"\n", &azArg[1][1]);
|
||||
p->out = stdout;
|
||||
rc = 1;
|
||||
}else{
|
||||
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", azArg[1]);
|
||||
}
|
||||
}else{
|
||||
p->out = fopen(azArg[1], "wb");
|
||||
if( p->out==0 ){
|
||||
|
||||
+13
-1
@@ -6037,6 +6037,17 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
|
||||
** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_MISS
|
||||
** is always 0.
|
||||
** </dd>
|
||||
**
|
||||
** [[SQLITE_DBSTATUS_CACHE_WRITE]] ^(<dt>SQLITE_DBSTATUS_CACHE_WRITE</dt>
|
||||
** <dd>This parameter returns the number of dirty cache entries that have
|
||||
** been written to disk. Specifically, the number of pages written to the
|
||||
** wal file in wal mode databases, or the number of pages written to the
|
||||
** database file in rollback mode databases. Any pages written as part of
|
||||
** transaction rollback or database recovery operations are not included.
|
||||
** If an IO or other error occurs while writing a page to disk, the effect
|
||||
** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined). ^The
|
||||
** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0.
|
||||
** </dd>
|
||||
** </dl>
|
||||
*/
|
||||
#define SQLITE_DBSTATUS_LOOKASIDE_USED 0
|
||||
@@ -6048,7 +6059,8 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
|
||||
#define SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL 6
|
||||
#define SQLITE_DBSTATUS_CACHE_HIT 7
|
||||
#define SQLITE_DBSTATUS_CACHE_MISS 8
|
||||
#define SQLITE_DBSTATUS_MAX 8 /* Largest defined DBSTATUS */
|
||||
#define SQLITE_DBSTATUS_CACHE_WRITE 9
|
||||
#define SQLITE_DBSTATUS_MAX 9 /* Largest defined DBSTATUS */
|
||||
|
||||
|
||||
/*
|
||||
|
||||
+17
-7
@@ -1009,14 +1009,18 @@ struct FuncDestructor {
|
||||
};
|
||||
|
||||
/*
|
||||
** Possible values for FuncDef.flags
|
||||
** Possible values for FuncDef.flags. Note that the _LENGTH and _TYPEOF
|
||||
** values must correspond to OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG. There
|
||||
** are assert() statements in the code to verify this.
|
||||
*/
|
||||
#define SQLITE_FUNC_LIKE 0x01 /* Candidate for the LIKE optimization */
|
||||
#define SQLITE_FUNC_CASE 0x02 /* Case-sensitive LIKE-type function */
|
||||
#define SQLITE_FUNC_EPHEM 0x04 /* Ephemeral. Delete with VDBE */
|
||||
#define SQLITE_FUNC_NEEDCOLL 0x08 /* sqlite3GetFuncCollSeq() might be called */
|
||||
#define SQLITE_FUNC_COUNT 0x20 /* Built-in count(*) aggregate */
|
||||
#define SQLITE_FUNC_COALESCE 0x40 /* Built-in coalesce() or ifnull() function */
|
||||
#define SQLITE_FUNC_COUNT 0x10 /* Built-in count(*) aggregate */
|
||||
#define SQLITE_FUNC_COALESCE 0x20 /* Built-in coalesce() or ifnull() function */
|
||||
#define SQLITE_FUNC_LENGTH 0x40 /* Built-in length() function */
|
||||
#define SQLITE_FUNC_TYPEOF 0x80 /* Built-in typeof() function */
|
||||
|
||||
/*
|
||||
** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are
|
||||
@@ -1044,7 +1048,10 @@ struct FuncDestructor {
|
||||
** parameter.
|
||||
*/
|
||||
#define FUNCTION(zName, nArg, iArg, bNC, xFunc) \
|
||||
{nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \
|
||||
{nArg, SQLITE_UTF8, (bNC*SQLITE_FUNC_NEEDCOLL), \
|
||||
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0}
|
||||
#define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags) \
|
||||
{nArg, SQLITE_UTF8, (bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags, \
|
||||
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0}
|
||||
#define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \
|
||||
{nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \
|
||||
@@ -1667,6 +1674,7 @@ struct Expr {
|
||||
i16 iRightJoinTable; /* If EP_FromJoin, the right table of the join */
|
||||
u8 flags2; /* Second set of flags. EP2_... */
|
||||
u8 op2; /* If a TK_REGISTER, the original value of Expr.op */
|
||||
/* If TK_COLUMN, the value of p5 for OP_Column */
|
||||
AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
|
||||
Table *pTab; /* Table for TK_COLUMN expressions. */
|
||||
#if SQLITE_MAX_EXPR_DEPTH>0
|
||||
@@ -1689,7 +1697,7 @@ struct Expr {
|
||||
#define EP_FixedDest 0x0200 /* Result needed in a specific register */
|
||||
#define EP_IntValue 0x0400 /* Integer value contained in u.iValue */
|
||||
#define EP_xIsSelect 0x0800 /* x.pSelect is valid (otherwise x.pList is) */
|
||||
#define EP_Hint 0x1000 /* Optimizer hint. Not required for correctness */
|
||||
#define EP_Hint 0x1000 /* Not used */
|
||||
#define EP_Reduced 0x2000 /* Expr struct is EXPR_REDUCEDSIZE bytes only */
|
||||
#define EP_TokenOnly 0x4000 /* Expr struct is EXPR_TOKENONLYSIZE bytes only */
|
||||
#define EP_Static 0x8000 /* Held in memory not obtained from malloc() */
|
||||
@@ -2276,7 +2284,7 @@ struct AuthContext {
|
||||
};
|
||||
|
||||
/*
|
||||
** Bitfield flags for P5 value in OP_Insert and OP_Delete
|
||||
** Bitfield flags for P5 value in various opcodes.
|
||||
*/
|
||||
#define OPFLAG_NCHANGE 0x01 /* Set to update db->nChange */
|
||||
#define OPFLAG_LASTROWID 0x02 /* Set to update db->lastRowid */
|
||||
@@ -2284,6 +2292,8 @@ struct AuthContext {
|
||||
#define OPFLAG_APPEND 0x08 /* This is likely to be an append */
|
||||
#define OPFLAG_USESEEKRESULT 0x10 /* Try to avoid a seek in BtreeInsert() */
|
||||
#define OPFLAG_CLEARCACHE 0x20 /* Clear pseudo-table cache in OP_Column */
|
||||
#define OPFLAG_LENGTHARG 0x40 /* OP_Column only used for length() */
|
||||
#define OPFLAG_TYPEOFARG 0x80 /* OP_Column only used for typeof() */
|
||||
|
||||
/*
|
||||
* Each trigger present in the database schema is stored as an instance of
|
||||
@@ -2767,7 +2777,7 @@ void sqlite3DeleteFrom(Parse*, SrcList*, Expr*);
|
||||
void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
|
||||
WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**,ExprList*,u16);
|
||||
void sqlite3WhereEnd(WhereInfo*);
|
||||
int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int);
|
||||
int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, u8);
|
||||
void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int);
|
||||
void sqlite3ExprCodeMove(Parse*, int, int, int);
|
||||
void sqlite3ExprCodeCopy(Parse*, int, int, int);
|
||||
|
||||
+3
-1
@@ -224,10 +224,12 @@ int sqlite3_db_status(
|
||||
** to zero.
|
||||
*/
|
||||
case SQLITE_DBSTATUS_CACHE_HIT:
|
||||
case SQLITE_DBSTATUS_CACHE_MISS: {
|
||||
case SQLITE_DBSTATUS_CACHE_MISS:
|
||||
case SQLITE_DBSTATUS_CACHE_WRITE:{
|
||||
int i;
|
||||
int nRet = 0;
|
||||
assert( SQLITE_DBSTATUS_CACHE_MISS==SQLITE_DBSTATUS_CACHE_HIT+1 );
|
||||
assert( SQLITE_DBSTATUS_CACHE_WRITE==SQLITE_DBSTATUS_CACHE_HIT+2 );
|
||||
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( db->aDb[i].pBt ){
|
||||
|
||||
+4
-8
@@ -3108,23 +3108,19 @@ EXTERN int Sqlite3_Init(Tcl_Interp *interp){
|
||||
return TCL_OK;
|
||||
}
|
||||
EXTERN int Tclsqlite3_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); }
|
||||
EXTERN int Sqlite3_SafeInit(Tcl_Interp *interp){ return TCL_OK; }
|
||||
EXTERN int Tclsqlite3_SafeInit(Tcl_Interp *interp){ return TCL_OK; }
|
||||
EXTERN int Sqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
|
||||
EXTERN int Tclsqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
|
||||
EXTERN int Sqlite3_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK; }
|
||||
EXTERN int Tclsqlite3_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK;}
|
||||
|
||||
/* Because it accesses the file-system and uses persistent state, SQLite
|
||||
** is not considered appropriate for safe interpreters. Hence, we deliberately
|
||||
** omit the _SafeInit() interfaces.
|
||||
*/
|
||||
|
||||
#ifndef SQLITE_3_SUFFIX_ONLY
|
||||
int Sqlite_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); }
|
||||
int Tclsqlite_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); }
|
||||
int Sqlite_SafeInit(Tcl_Interp *interp){ return TCL_OK; }
|
||||
int Tclsqlite_SafeInit(Tcl_Interp *interp){ return TCL_OK; }
|
||||
int Sqlite_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
|
||||
int Tclsqlite_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
|
||||
int Sqlite_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK; }
|
||||
int Tclsqlite_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK;}
|
||||
#endif
|
||||
|
||||
#ifdef TCLSH
|
||||
|
||||
+6
-10
@@ -831,13 +831,10 @@ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
if( !isIgnoreUsable && !pConstraint->usable ) continue;
|
||||
|
||||
iCol = pConstraint->iColumn;
|
||||
if( pVtab->aIndex[iCol] || iCol<0 ){
|
||||
char *zCol = pVtab->aCol[iCol];
|
||||
if( iCol<0 || pVtab->aIndex[iCol] ){
|
||||
char *zCol = iCol>=0 ? pVtab->aCol[iCol] : "rowid";
|
||||
char *zOp = 0;
|
||||
useIdx = 1;
|
||||
if( iCol<0 ){
|
||||
zCol = "rowid";
|
||||
}
|
||||
switch( pConstraint->op ){
|
||||
case SQLITE_INDEX_CONSTRAINT_EQ:
|
||||
zOp = "="; break;
|
||||
@@ -870,13 +867,12 @@ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
** on a column that this virtual table has an index for, then consume
|
||||
** the ORDER BY clause.
|
||||
*/
|
||||
if( pIdxInfo->nOrderBy==1 && pVtab->aIndex[pIdxInfo->aOrderBy->iColumn] ){
|
||||
if( pIdxInfo->nOrderBy==1 && (
|
||||
pIdxInfo->aOrderBy->iColumn<0 ||
|
||||
pVtab->aIndex[pIdxInfo->aOrderBy->iColumn]) ){
|
||||
int iCol = pIdxInfo->aOrderBy->iColumn;
|
||||
char *zCol = pVtab->aCol[iCol];
|
||||
char *zCol = iCol>=0 ? pVtab->aCol[iCol] : "rowid";
|
||||
char *zDir = pIdxInfo->aOrderBy->desc?"DESC":"ASC";
|
||||
if( iCol<0 ){
|
||||
zCol = "rowid";
|
||||
}
|
||||
zNew = sqlite3_mprintf(" ORDER BY %s %s", zCol, zDir);
|
||||
string_concat(&zQuery, zNew, 1, &rc);
|
||||
pIdxInfo->orderByConsumed = 1;
|
||||
|
||||
+2
-1
@@ -1323,7 +1323,8 @@ static int test_db_status(
|
||||
{ "LOOKASIDE_MISS_SIZE", SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE },
|
||||
{ "LOOKASIDE_MISS_FULL", SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL },
|
||||
{ "CACHE_HIT", SQLITE_DBSTATUS_CACHE_HIT },
|
||||
{ "CACHE_MISS", SQLITE_DBSTATUS_CACHE_MISS }
|
||||
{ "CACHE_MISS", SQLITE_DBSTATUS_CACHE_MISS },
|
||||
{ "CACHE_WRITE", SQLITE_DBSTATUS_CACHE_WRITE }
|
||||
};
|
||||
Tcl_Obj *pResult;
|
||||
if( objc!=4 ){
|
||||
|
||||
+31
-11
@@ -2127,6 +2127,11 @@ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */
|
||||
** then the cache of the cursor is reset prior to extracting the column.
|
||||
** The first OP_Column against a pseudo-table after the value of the content
|
||||
** register has changed should have this bit set.
|
||||
**
|
||||
** If the OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG bits are set on P5 when
|
||||
** the result is guaranteed to only be used as the argument of a length()
|
||||
** or typeof() function, respectively. The loading of large blobs can be
|
||||
** skipped for length() and all content loading can be skipped for typeof().
|
||||
*/
|
||||
case OP_Column: {
|
||||
u32 payloadSize; /* Number of bytes in the record */
|
||||
@@ -2267,7 +2272,7 @@ case OP_Column: {
|
||||
pC->aRow = 0;
|
||||
}
|
||||
}
|
||||
/* The following assert is true in all cases accept when
|
||||
/* The following assert is true in all cases except when
|
||||
** the database file has been corrupted externally.
|
||||
** assert( zRec!=0 || avail>=payloadSize || avail>=9 ); */
|
||||
szHdr = getVarint32((u8*)zData, offset);
|
||||
@@ -2342,11 +2347,11 @@ case OP_Column: {
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
/* If i is less that nField, then there are less fields in this
|
||||
/* If i is less that nField, then there are fewer fields in this
|
||||
** record than SetNumColumns indicated there are columns in the
|
||||
** table. Set the offset for any extra columns not present in
|
||||
** the record to 0. This tells code below to store a NULL
|
||||
** instead of deserializing a value from the record.
|
||||
** the record to 0. This tells code below to store the default value
|
||||
** for the column instead of deserializing a value from the record.
|
||||
*/
|
||||
aOffset[i] = 0;
|
||||
}
|
||||
@@ -2376,17 +2381,32 @@ case OP_Column: {
|
||||
if( aOffset[p2] ){
|
||||
assert( rc==SQLITE_OK );
|
||||
if( zRec ){
|
||||
/* This is the common case where the whole row fits on a single page */
|
||||
VdbeMemRelease(pDest);
|
||||
sqlite3VdbeSerialGet((u8 *)&zRec[aOffset[p2]], aType[p2], pDest);
|
||||
}else{
|
||||
len = sqlite3VdbeSerialTypeLen(aType[p2]);
|
||||
sqlite3VdbeMemMove(&sMem, pDest);
|
||||
rc = sqlite3VdbeMemFromBtree(pCrsr, aOffset[p2], len, pC->isIndex, &sMem);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto op_column_out;
|
||||
/* This branch happens only when the row overflows onto multiple pages */
|
||||
t = aType[p2];
|
||||
if( (pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0
|
||||
&& ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0)
|
||||
){
|
||||
/* Content is irrelevant for the typeof() function and for
|
||||
** the length(X) function if X is a blob. So we might as well use
|
||||
** bogus content rather than reading content from disk. NULL works
|
||||
** for text and blob and whatever is in the payloadSize64 variable
|
||||
** will work for everything else. */
|
||||
zData = t<12 ? (char*)&payloadSize64 : 0;
|
||||
}else{
|
||||
len = sqlite3VdbeSerialTypeLen(t);
|
||||
sqlite3VdbeMemMove(&sMem, pDest);
|
||||
rc = sqlite3VdbeMemFromBtree(pCrsr, aOffset[p2], len, pC->isIndex,
|
||||
&sMem);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto op_column_out;
|
||||
}
|
||||
zData = sMem.z;
|
||||
}
|
||||
zData = sMem.z;
|
||||
sqlite3VdbeSerialGet((u8*)zData, aType[p2], pDest);
|
||||
sqlite3VdbeSerialGet((u8*)zData, t, pDest);
|
||||
}
|
||||
pDest->enc = encoding;
|
||||
}else{
|
||||
|
||||
+5
-2
@@ -686,7 +686,10 @@ static int isLikeOrGlob(
|
||||
#endif
|
||||
pList = pExpr->x.pList;
|
||||
pLeft = pList->a[1].pExpr;
|
||||
if( pLeft->op!=TK_COLUMN || sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT ){
|
||||
if( pLeft->op!=TK_COLUMN
|
||||
|| sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT
|
||||
|| IsVirtual(pLeft->pTab)
|
||||
){
|
||||
/* IMP: R-02065-49465 The left-hand side of the LIKE or GLOB operator must
|
||||
** be the name of an indexed column with TEXT affinity. */
|
||||
return 0;
|
||||
@@ -4383,7 +4386,7 @@ static Bitmask codeOneLoopStart(
|
||||
int iSet = ((ii==pOrWc->nTerm-1)?-1:ii);
|
||||
int r;
|
||||
r = sqlite3ExprCodeGetColumn(pParse, pTabItem->pTab, -1, iCur,
|
||||
regRowid);
|
||||
regRowid, 0);
|
||||
sqlite3VdbeAddOp4Int(v, OP_RowSetTest, regRowset,
|
||||
sqlite3VdbeCurrentAddr(v)+2, r, iSet);
|
||||
}
|
||||
|
||||
+3
-27
@@ -27,37 +27,13 @@ set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/lock_common.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
source $testdir/bc_common.tcl
|
||||
db close
|
||||
|
||||
# Search for binaries to test against. Any executable files that match
|
||||
# our naming convention are assumed to be testfixture binaries to test
|
||||
# against.
|
||||
#
|
||||
set binaries [list]
|
||||
set pattern "[file tail [info nameofexec]]?*"
|
||||
if {$tcl_platform(platform)=="windows"} {
|
||||
set pattern [string map {\.exe {}} $pattern]
|
||||
}
|
||||
foreach file [glob -nocomplain $pattern] {
|
||||
if {[file executable $file] && [file isfile $file]} {lappend binaries $file}
|
||||
}
|
||||
if {[llength $binaries]==0} {
|
||||
puts "WARNING: No historical binaries to test against."
|
||||
puts "WARNING: No backwards-compatibility tests have been run."
|
||||
if {"" == [bc_find_binaries backcompat.test]} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
proc get_version {binary} {
|
||||
set chan [launch_testfixture $binary]
|
||||
set v [testfixture $chan { sqlite3 -version }]
|
||||
close $chan
|
||||
set v
|
||||
}
|
||||
foreach bin $binaries {
|
||||
puts -nonewline "Testing against $bin - "
|
||||
flush stdout
|
||||
puts "version [get_version $bin]"
|
||||
}
|
||||
|
||||
proc do_backcompat_test {rv bin1 bin2 script} {
|
||||
|
||||
@@ -93,7 +69,7 @@ proc do_backcompat_test {rv bin1 bin2 script} {
|
||||
array set ::incompatible [list]
|
||||
proc do_allbackcompat_test {script} {
|
||||
|
||||
foreach bin $::binaries {
|
||||
foreach bin $::BC(binaries) {
|
||||
set nErr [set_test_counter errors]
|
||||
foreach dir {0 1} {
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
|
||||
|
||||
|
||||
proc bc_find_binaries {zCaption} {
|
||||
# Search for binaries to test against. Any executable files that match
|
||||
# our naming convention are assumed to be testfixture binaries to test
|
||||
# against.
|
||||
#
|
||||
set binaries [list]
|
||||
set pattern "[file tail [info nameofexec]]?*"
|
||||
if {$::tcl_platform(platform)=="windows"} {
|
||||
set pattern [string map {\.exe {}} $pattern]
|
||||
}
|
||||
foreach file [glob -nocomplain $pattern] {
|
||||
if {[file executable $file] && [file isfile $file]} {lappend binaries $file}
|
||||
}
|
||||
|
||||
if {[llength $binaries]==0} {
|
||||
puts "WARNING: No historical binaries to test against."
|
||||
puts "WARNING: Omitting backwards-compatibility tests"
|
||||
}
|
||||
|
||||
foreach bin $binaries {
|
||||
puts -nonewline "Testing against $bin - "
|
||||
flush stdout
|
||||
puts "version [get_version $bin]"
|
||||
}
|
||||
|
||||
set ::BC(binaries) $binaries
|
||||
return $binaries
|
||||
}
|
||||
|
||||
proc get_version {binary} {
|
||||
set chan [launch_testfixture $binary]
|
||||
set v [testfixture $chan { sqlite3 -version }]
|
||||
close $chan
|
||||
set v
|
||||
}
|
||||
|
||||
proc do_bc_test {bin script} {
|
||||
|
||||
forcedelete test.db
|
||||
set ::bc_chan [launch_testfixture $bin]
|
||||
|
||||
proc code1 {tcl} { uplevel #0 $tcl }
|
||||
proc code2 {tcl} { testfixture $::bc_chan $tcl }
|
||||
proc sql1 sql { code1 [list db eval $sql] }
|
||||
proc sql2 sql { code2 [list db eval $sql] }
|
||||
|
||||
code1 { sqlite3 db test.db }
|
||||
code2 { sqlite3 db test.db }
|
||||
|
||||
set bintag [string map {testfixture {}} $bin]
|
||||
set bintag [string map {\.exe {}} $bintag]
|
||||
if {$bintag == ""} {set bintag self}
|
||||
set saved_prefix $::testprefix
|
||||
append ::testprefix ".$bintag"
|
||||
|
||||
uplevel $script
|
||||
|
||||
set ::testprefix $saved_prefix
|
||||
|
||||
catch { code1 { db close } }
|
||||
catch { code2 { db close } }
|
||||
catch { close $::bc_chan }
|
||||
}
|
||||
|
||||
proc do_all_bc_test {script} {
|
||||
foreach bin $::BC(binaries) {
|
||||
uplevel [list do_bc_test $bin $script]
|
||||
}
|
||||
}
|
||||
+24
-1
@@ -9,7 +9,7 @@
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Tests for the sqlite3_stmt_status() function
|
||||
# Tests for the sqlite3_db_status() function
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
@@ -33,6 +33,10 @@ proc db_hit_miss {db {reset 0}} {
|
||||
list $nHit $nMiss
|
||||
}
|
||||
|
||||
proc db_write {db {reset 0}} {
|
||||
sqlite3_db_status $db CACHE_WRITE $reset
|
||||
}
|
||||
|
||||
do_test 1.1 {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
@@ -72,5 +76,24 @@ do_test 1.7 {
|
||||
do_test 1.8 { sqlite3_db_status db CACHE_HIT 0 } {0 2 0}
|
||||
do_test 1.9 { sqlite3_db_status db CACHE_MISS 0 } {0 1 0}
|
||||
|
||||
do_test 2.1 { db_write db } {0 0 0}
|
||||
do_test 2.2 {
|
||||
execsql { INSERT INTO t1 VALUES(4, randomblob(600)) }
|
||||
db_write db
|
||||
} {0 4 0}
|
||||
do_test 2.3 { db_write db 1 } {0 4 0}
|
||||
do_test 2.4 { db_write db 0 } {0 0 0}
|
||||
do_test 2.5 { db_write db 1 } {0 0 0}
|
||||
|
||||
do_test 2.6 {
|
||||
execsql { PRAGMA journal_mode = WAL }
|
||||
db_write db 1
|
||||
} {0 1 0}
|
||||
do_test 2.7 {
|
||||
execsql { INSERT INTO t1 VALUES(5, randomblob(600)) }
|
||||
db_write db
|
||||
} {0 4 0}
|
||||
do_test 2.8 { db_write db 1 } {0 4 0}
|
||||
do_test 2.9 { db_write db 0 } {0 0 0}
|
||||
|
||||
finish_test
|
||||
|
||||
+130
-4
@@ -14,6 +14,129 @@
|
||||
# to use Tcl.
|
||||
#
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# INSTRUCTIONS
|
||||
#
|
||||
# The following commands are available:
|
||||
#
|
||||
# fts3_build_db_1 N
|
||||
# Using database handle [db] create an FTS4 table named t1 and populate
|
||||
# it with N rows of data. N must be less than 10,000. Refer to the
|
||||
# header comments above the proc implementation below for details.
|
||||
#
|
||||
# fts3_build_db_2 N
|
||||
# Using database handle [db] create an FTS4 table named t2 and populate
|
||||
# it with N rows of data. N must be less than 100,000. Refer to the
|
||||
# header comments above the proc implementation below for details.
|
||||
#
|
||||
# fts3_integrity_check TBL
|
||||
# TBL must be an FTS table in the database currently opened by handle
|
||||
# [db]. This proc loads and tokenizes all documents within the table,
|
||||
# then checks that the current contents of the FTS index matches the
|
||||
# results.
|
||||
#
|
||||
# fts3_terms TBL WHERE
|
||||
# Todo.
|
||||
#
|
||||
# fts3_doclist TBL TERM WHERE
|
||||
# Todo.
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# USAGE: fts3_build_db_1 SWITCHES N
|
||||
#
|
||||
# Build a sample FTS table in the database opened by database connection
|
||||
# [db]. The name of the new table is "t1".
|
||||
#
|
||||
proc fts3_build_db_1 {args} {
|
||||
|
||||
set default(-module) fts4
|
||||
|
||||
set nArg [llength $args]
|
||||
if {($nArg%2)==0} {
|
||||
error "wrong # args: should be \"fts3_build_db_1 ?switches? n\""
|
||||
}
|
||||
|
||||
set n [lindex $args [expr $nArg-1]]
|
||||
array set opts [array get default]
|
||||
array set opts [lrange $args 0 [expr $nArg-2]]
|
||||
foreach k [array names opts] {
|
||||
if {0==[info exists default($k)]} { error "unknown option: $k" }
|
||||
}
|
||||
|
||||
if {$n > 10000} {error "n must be <= 10000"}
|
||||
db eval "CREATE VIRTUAL TABLE t1 USING $opts(-module) (x, y)"
|
||||
|
||||
set xwords [list zero one two three four five six seven eight nine ten]
|
||||
set ywords [list alpha beta gamma delta epsilon zeta eta theta iota kappa]
|
||||
|
||||
for {set i 0} {$i < $n} {incr i} {
|
||||
set x ""
|
||||
set y ""
|
||||
|
||||
set x [list]
|
||||
lappend x [lindex $xwords [expr ($i / 1000) % 10]]
|
||||
lappend x [lindex $xwords [expr ($i / 100) % 10]]
|
||||
lappend x [lindex $xwords [expr ($i / 10) % 10]]
|
||||
lappend x [lindex $xwords [expr ($i / 1) % 10]]
|
||||
|
||||
set y [list]
|
||||
lappend y [lindex $ywords [expr ($i / 1000) % 10]]
|
||||
lappend y [lindex $ywords [expr ($i / 100) % 10]]
|
||||
lappend y [lindex $ywords [expr ($i / 10) % 10]]
|
||||
lappend y [lindex $ywords [expr ($i / 1) % 10]]
|
||||
|
||||
db eval { INSERT INTO t1(docid, x, y) VALUES($i, $x, $y) }
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# USAGE: fts3_build_db_2 N ARGS
|
||||
#
|
||||
# Build a sample FTS table in the database opened by database connection
|
||||
# [db]. The name of the new table is "t2".
|
||||
#
|
||||
proc fts3_build_db_2 {args} {
|
||||
|
||||
set default(-module) fts4
|
||||
set default(-extra) ""
|
||||
|
||||
set nArg [llength $args]
|
||||
if {($nArg%2)==0} {
|
||||
error "wrong # args: should be \"fts3_build_db_1 ?switches? n\""
|
||||
}
|
||||
|
||||
set n [lindex $args [expr $nArg-1]]
|
||||
array set opts [array get default]
|
||||
array set opts [lrange $args 0 [expr $nArg-2]]
|
||||
foreach k [array names opts] {
|
||||
if {0==[info exists default($k)]} { error "unknown option: $k" }
|
||||
}
|
||||
|
||||
if {$n > 100000} {error "n must be <= 100000"}
|
||||
|
||||
set sql "CREATE VIRTUAL TABLE t2 USING $opts(-module) (content"
|
||||
if {$opts(-extra) != ""} {
|
||||
append sql ", " $opts(-extra)
|
||||
}
|
||||
append sql ")"
|
||||
db eval $sql
|
||||
|
||||
set chars [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 ""]
|
||||
|
||||
for {set i 0} {$i < $n} {incr i} {
|
||||
set word ""
|
||||
set nChar [llength $chars]
|
||||
append word [lindex $chars [expr {($i / 1) % $nChar}]]
|
||||
append word [lindex $chars [expr {($i / $nChar) % $nChar}]]
|
||||
append word [lindex $chars [expr {($i / ($nChar*$nChar)) % $nChar}]]
|
||||
|
||||
db eval { INSERT INTO t2(docid, content) VALUES($i, $word) }
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# USAGE: fts3_integrity_check TBL
|
||||
#
|
||||
@@ -46,6 +169,7 @@ proc fts3_integrity_check {tbl} {
|
||||
fts3_read2 $tbl 1 A
|
||||
|
||||
foreach zTerm [array names A] {
|
||||
#puts $zTerm
|
||||
foreach doclist $A($zTerm) {
|
||||
set docid 0
|
||||
while {[string length $doclist]>0} {
|
||||
@@ -97,7 +221,7 @@ proc fts3_integrity_check {tbl} {
|
||||
set es "Error at docid=$iDoc col=$iCol pos=$pos. Index is missing"
|
||||
lappend errors $es
|
||||
} else {
|
||||
if {$C($iDoc,$iCol,$pos) != "$term"} {
|
||||
if {[string compare $C($iDoc,$iCol,$pos) $term]} {
|
||||
set es "Error at docid=$iDoc col=$iCol pos=$pos. Index "
|
||||
append es "has \"$C($iDoc,$iCol,$pos)\", document has \"$term\""
|
||||
lappend errors $es
|
||||
@@ -233,7 +357,8 @@ proc fts3_readleaf {blob} {
|
||||
|
||||
set zTerm [string range $zPrev 0 [expr $nPrefix-1]]
|
||||
append zTerm [gobble_string blob $nSuffix]
|
||||
set doclist [gobble_string blob [gobble_varint blob]]
|
||||
set nDoclist [gobble_varint blob]
|
||||
set doclist [gobble_string blob $nDoclist]
|
||||
|
||||
lappend terms $zTerm $doclist
|
||||
set zPrev $zTerm
|
||||
@@ -249,7 +374,9 @@ proc fts3_read2 {tbl where varname} {
|
||||
FROM ${tbl}_segdir WHERE $where
|
||||
ORDER BY level ASC, idx DESC
|
||||
" {
|
||||
if {$start_block == 0} {
|
||||
set c 0
|
||||
binary scan $root c c
|
||||
if {$c==0} {
|
||||
foreach {t d} [fts3_readleaf $root] { lappend a($t) $d }
|
||||
} else {
|
||||
db eval " SELECT block
|
||||
@@ -258,7 +385,6 @@ proc fts3_read2 {tbl where varname} {
|
||||
ORDER BY blockid
|
||||
" {
|
||||
foreach {t d} [fts3_readleaf $block] { lappend a($t) $d }
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
# 2012 March 26
|
||||
#
|
||||
# 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 FTS 'integrity-check' function,
|
||||
# used to check if the current FTS index accurately reflects the content
|
||||
# of the table.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/fts3_common.tcl
|
||||
set ::testprefix fts4check
|
||||
|
||||
# If SQLITE_ENABLE_FTS3 is defined, omit this file.
|
||||
ifcapable !fts3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Run the integrity-check on FTS table $tbl using database handle $db. If
|
||||
# the integrity-check passes, return "ok". Otherwise, throw an exception.
|
||||
#
|
||||
proc fts_integrity {db tbl} {
|
||||
$db eval "INSERT INTO $tbl ($tbl) VALUES('integrity-check')"
|
||||
return "ok"
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test cases 1.*
|
||||
#
|
||||
# 1.0: Build a reasonably sized FTS table (5000 rows).
|
||||
#
|
||||
# 1.1: Run the integrity check code to check it passes.
|
||||
#
|
||||
# 1.2: Make a series of minor changes to the underlying FTS data structures
|
||||
# (e.g. delete or insert a row from the %_content table). Check that
|
||||
# this causes the integrity-check code to fail.
|
||||
#
|
||||
|
||||
# Build an FTS table and check the integrity-check passes.
|
||||
#
|
||||
do_test 1.0 { fts3_build_db_1 5000 } {}
|
||||
do_test 1.1 { fts_integrity db t1 } {ok}
|
||||
|
||||
# Mess around with the underlying tables. Check that this causes the
|
||||
# integrity-check test to fail.
|
||||
#
|
||||
foreach {tn disruption} {
|
||||
1 {
|
||||
INSERT INTO t1_content(docid, c0x, c1y) VALUES(NULL, 'a', 'b');
|
||||
}
|
||||
2 {
|
||||
DELETE FROM t1_content WHERE docid = (SELECT max(docid) FROM t1_content);
|
||||
}
|
||||
3 {
|
||||
DELETE FROM t1_segdir WHERE level=0 AND idx=(
|
||||
SELECT max(idx) FROM t1_segdir WHERE level=0
|
||||
);
|
||||
}
|
||||
} {
|
||||
do_execsql_test 1.2.1.$tn "BEGIN; $disruption"
|
||||
do_catchsql_test 1.2.2.$tn {
|
||||
INSERT INTO t1 (t1) VALUES('integrity-check')
|
||||
} {1 {database disk image is malformed}}
|
||||
do_execsql_test 1.2.3.$tn "ROLLBACK"
|
||||
}
|
||||
|
||||
do_test 1.3 { fts_integrity db t1 } {ok}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test cases 2.*
|
||||
#
|
||||
# 2.0: Build a reasonably sized FTS table (20000 rows) that includes
|
||||
# prefix indexes.
|
||||
#
|
||||
# 2.1: Run the integrity check code to check it passes.
|
||||
#
|
||||
# 2.2: Make a series of minor changes to the underlying FTS data structures
|
||||
# (e.g. delete or insert a row from the %_content table). Check that
|
||||
# this causes the integrity-check code to fail.
|
||||
#
|
||||
|
||||
do_test 2.0 { fts3_build_db_2 -extra {prefix="3,1"} 20000 } {}
|
||||
do_test 2.1 { fts_integrity db t2 } {ok}
|
||||
foreach {tn disruption} {
|
||||
1 {
|
||||
INSERT INTO t2_content VALUES(NULL, 'xyz')
|
||||
}
|
||||
3 {
|
||||
DELETE FROM t2_segdir WHERE level=0 AND idx=(
|
||||
SELECT max(idx) FROM t2_segdir WHERE level=1024
|
||||
);
|
||||
}
|
||||
} {
|
||||
do_execsql_test 2.2.1.$tn "BEGIN; $disruption"
|
||||
do_catchsql_test 2.2.2.$tn {
|
||||
INSERT INTO t2 (t2) VALUES('integrity-check')
|
||||
} {1 {database disk image is malformed}}
|
||||
do_execsql_test 2.2.3.$tn "ROLLBACK"
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test cases 3.*
|
||||
#
|
||||
# 3.0: Build a reasonably sized FTS table (5000 rows) that includes
|
||||
# prefix indexes and uses the languageid= feature.
|
||||
#
|
||||
# 3.1: Run the integrity check code to check it passes.
|
||||
#
|
||||
# 3.2: Make a series of minor changes to the underlying FTS data structures
|
||||
# (e.g. delete or insert a row from the %_content table). Check that
|
||||
# this causes the integrity-check code to fail.
|
||||
#
|
||||
do_test 3.0 {
|
||||
reset_db
|
||||
fts3_build_db_1 5000
|
||||
execsql {
|
||||
CREATE VIRTUAL TABLE t3 USING fts4(x, y, prefix="2,3", languageid=langid);
|
||||
}
|
||||
foreach docid [execsql {SELECT docid FROM t1 ORDER BY 1 ASC}] {
|
||||
execsql {
|
||||
INSERT INTO t3(x, y, langid)
|
||||
SELECT x, y, (docid%9)*4 FROM t1 WHERE docid=$docid;
|
||||
}
|
||||
}
|
||||
} {}
|
||||
do_test 3.1 { fts_integrity db t3 } {ok}
|
||||
|
||||
foreach {tn disruption} {
|
||||
1 {
|
||||
INSERT INTO t3_content(c0x, c1y, langid) VALUES(NULL, 'a', 0);
|
||||
}
|
||||
2 {
|
||||
UPDATE t3_content SET langid=langid+1 WHERE rowid = (
|
||||
SELECT max(rowid) FROM t3_content
|
||||
)
|
||||
}
|
||||
} {
|
||||
do_execsql_test 3.2.1.$tn "BEGIN; $disruption"
|
||||
do_catchsql_test 3.2.2.$tn {
|
||||
INSERT INTO t3 (t3) VALUES('integrity-check')
|
||||
} {1 {database disk image is malformed}}
|
||||
do_execsql_test 3.2.3.$tn "ROLLBACK"
|
||||
}
|
||||
|
||||
finish_test
|
||||
@@ -472,15 +472,14 @@ foreach lid [list 4 [expr 1<<30]] {
|
||||
} {8 0}
|
||||
|
||||
do_execsql_test 5.4.$lid.4 {
|
||||
INSERT INTO t6(t6) VALUES('optimize');
|
||||
INSERT INTO t6(t6) VALUES('merge=100,3');
|
||||
INSERT INTO t6(t6) VALUES('merge=100,3');
|
||||
SELECT docid FROM t6 WHERE t6 MATCH '"zero zero"' AND lid=$lid;
|
||||
} {1 2 5}
|
||||
|
||||
do_execsql_test 5.4.$lid.5 {
|
||||
SELECT count(*) FROM t6_segdir;
|
||||
SELECT count(*) FROM t6_segments;
|
||||
} {1 0}
|
||||
} {4 4}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,341 @@
|
||||
# 2012 March 06
|
||||
#
|
||||
# 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 incremental merge function.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/fts3_common.tcl
|
||||
|
||||
# If SQLITE_ENABLE_FTS3 is defined, omit this file.
|
||||
ifcapable !fts3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
proc fts3_integrity_check {tbl} {
|
||||
db eval "INSERT INTO $tbl ($tbl) VALUES('integrity-check')"
|
||||
return "ok"
|
||||
}
|
||||
|
||||
foreach mod {fts3 fts4} {
|
||||
set ::testprefix fts4merge-$mod
|
||||
reset_db
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test cases 1.*
|
||||
#
|
||||
do_test 1.0 { fts3_build_db_1 -module $mod 1004 } {}
|
||||
do_test 1.1 { fts3_integrity_check t1 } {ok}
|
||||
do_execsql_test 1.1 {
|
||||
SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level
|
||||
} {
|
||||
0 {0 1 2 3 4 5 6 7 8 9 10 11}
|
||||
1 {0 1 2 3 4 5 6 7 8 9 10 11 12 13}
|
||||
2 {0 1 2}
|
||||
}
|
||||
|
||||
for {set i 0} {$i<20} {incr i} {
|
||||
do_execsql_test 1.2.$i.1 { INSERT INTO t1(t1) VALUES('merge=1') }
|
||||
do_test 1.2.$i.2 { fts3_integrity_check t1 } ok
|
||||
do_execsql_test 1.2.$i.3 {
|
||||
SELECT docid FROM t1 WHERE t1 MATCH 'zero one two three'
|
||||
} {123 132 213 231 312 321}
|
||||
}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level
|
||||
} {
|
||||
0 {0 1 2 3}
|
||||
1 {0 1 2 3 4 5 6}
|
||||
2 {0 1 2 3}
|
||||
}
|
||||
|
||||
for {set i 0} {$i<100} {incr i} {
|
||||
do_execsql_test 1.4.$i { INSERT INTO t1(t1) VALUES('merge=1,4') }
|
||||
do_test 1.4.$i.2 { fts3_integrity_check t1 } ok
|
||||
do_execsql_test 1.4.$i.3 {
|
||||
SELECT docid FROM t1 WHERE t1 MATCH 'zero one two three'
|
||||
} {123 132 213 231 312 321}
|
||||
}
|
||||
|
||||
do_execsql_test 1.5 {
|
||||
SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level
|
||||
} {
|
||||
2 {0 1}
|
||||
3 0
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test cases 2.* test that errors in the xxx part of the 'merge=xxx' are
|
||||
# handled correctly.
|
||||
#
|
||||
do_execsql_test 2.0 "CREATE VIRTUAL TABLE t2 USING $mod"
|
||||
|
||||
foreach {tn arg} {
|
||||
1 {merge=abc}
|
||||
2 {merge=%%%}
|
||||
3 {merge=,}
|
||||
4 {merge=5,}
|
||||
5 {merge=6,%}
|
||||
6 {merge=6,six}
|
||||
7 {merge=6,1}
|
||||
8 {merge=6,0}
|
||||
} {
|
||||
do_catchsql_test 2.$tn {
|
||||
INSERT INTO t2(t2) VALUES($arg);
|
||||
} {1 {SQL logic error or missing database}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test cases 3.*
|
||||
#
|
||||
do_test 3.0 {
|
||||
reset_db
|
||||
execsql { PRAGMA page_size = 512 }
|
||||
fts3_build_db_2 -module $mod 30040
|
||||
} {}
|
||||
do_test 3.1 { fts3_integrity_check t2 } {ok}
|
||||
|
||||
do_execsql_test 3.2 {
|
||||
SELECT level, group_concat(idx, ' ') FROM t2_segdir GROUP BY level
|
||||
} {
|
||||
0 {0 1 2 3 4 5 6}
|
||||
1 {0 1 2 3 4}
|
||||
2 {0 1 2 3 4}
|
||||
3 {0 1 2 3 4 5 6}
|
||||
}
|
||||
|
||||
do_execsql_test 3.3 {
|
||||
INSERT INTO t2(t2) VALUES('merge=1000000,2');
|
||||
SELECT level, group_concat(idx, ' ') FROM t2_segdir GROUP BY level
|
||||
} {
|
||||
0 0
|
||||
2 0
|
||||
3 0
|
||||
4 0
|
||||
6 0
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test cases 4.*
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 4.1 "
|
||||
PRAGMA page_size = 512;
|
||||
CREATE VIRTUAL TABLE t4 USING $mod;
|
||||
PRAGMA main.page_size;
|
||||
" {512}
|
||||
|
||||
do_test 4.2 {
|
||||
foreach x {a c b d e f g h i j k l m n o p} {
|
||||
execsql "INSERT INTO t4 VALUES('[string repeat $x 600]')"
|
||||
}
|
||||
execsql {SELECT level, group_concat(idx, ' ') FROM t4_segdir GROUP BY level}
|
||||
} {0 {0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15}}
|
||||
|
||||
foreach {tn expect} {
|
||||
1 "0 {0 1 2 3 4 5 6 7 8 9 10 11 12 13} 1 0"
|
||||
2 "0 {0 1 2 3 4 5 6 7 8 9 10 11 12} 1 0"
|
||||
3 "0 {0 1 2 3 4 5 6 7 8 9 10 11} 1 0"
|
||||
4 "0 {0 1 2 3 4 5 6 7 8 9 10} 1 0"
|
||||
5 "0 {0 1 2 3 4 5 6 7 8 9} 1 0"
|
||||
6 "0 {0 1 2 3 4 5 6 7 8} 1 0"
|
||||
7 "0 {0 1 2 3 4 5 6 7} 1 0"
|
||||
8 "0 {0 1 2 3 4 5 6} 1 0"
|
||||
9 "0 {0 1 2 3 4 5} 1 0"
|
||||
} {
|
||||
do_execsql_test 4.3.$tn {
|
||||
INSERT INTO t4(t4) VALUES('merge=1,16');
|
||||
SELECT level, group_concat(idx, ' ') FROM t4_segdir GROUP BY level;
|
||||
} $expect
|
||||
}
|
||||
|
||||
do_execsql_test 4.4.1 {
|
||||
SELECT quote(value) FROM t4_stat WHERE rowid=1
|
||||
} {X'0006'}
|
||||
|
||||
do_execsql_test 4.4.2 {
|
||||
DELETE FROM t4_stat WHERE rowid=1;
|
||||
INSERT INTO t4(t4) VALUES('merge=1,12');
|
||||
SELECT level, group_concat(idx, ' ') FROM t4_segdir GROUP BY level;
|
||||
} "0 {0 1 2 3 4 5} 1 0"
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test cases 5.*
|
||||
#
|
||||
# Test that if a crisis-merge occurs that disrupts an ongoing incremental
|
||||
# merge, the next call to "merge=A,B" identifies this and starts a new
|
||||
# incremental merge. There are two scenarios:
|
||||
#
|
||||
# * There are less segments on the input level that the disrupted
|
||||
# incremental merge operated on, or
|
||||
#
|
||||
# * Sufficient segments exist on the input level but the segments
|
||||
# contain keys smaller than the largest key in the potential output
|
||||
# segment.
|
||||
#
|
||||
do_test 5.1 {
|
||||
reset_db
|
||||
fts3_build_db_1 -module $mod 1000
|
||||
} {}
|
||||
|
||||
do_execsql_test 5.2 {
|
||||
SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level;
|
||||
} {
|
||||
0 {0 1 2 3 4 5 6 7}
|
||||
1 {0 1 2 3 4 5 6 7 8 9 10 11 12 13}
|
||||
2 {0 1 2}
|
||||
}
|
||||
|
||||
do_execsql_test 5.3 {
|
||||
INSERT INTO t1(t1) VALUES('merge=1,5');
|
||||
INSERT INTO t1(t1) VALUES('merge=1,5');
|
||||
SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level;
|
||||
} {
|
||||
0 {0 1 2}
|
||||
1 {0 1 2 3 4 5 6 7 8 9 10 11 12 13 14}
|
||||
2 {0 1 2 3}
|
||||
}
|
||||
|
||||
do_execsql_test 5.4 {SELECT quote(value) from t1_stat WHERE rowid=1} {X'0105'}
|
||||
do_test 5.5 {
|
||||
foreach docid [execsql {SELECT docid FROM t1}] {
|
||||
execsql {INSERT INTO t1 SELECT * FROM t1 WHERE docid=$docid}
|
||||
}
|
||||
} {}
|
||||
|
||||
do_execsql_test 5.6 {SELECT quote(value) from t1_stat WHERE rowid=1} {X'0105'}
|
||||
|
||||
do_execsql_test 5.7 {
|
||||
SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level;
|
||||
SELECT quote(value) from t1_stat WHERE rowid=1;
|
||||
} {
|
||||
0 {0 1 2 3 4 5 6 7 8 9 10}
|
||||
1 {0 1 2 3 4 5 6 7 8 9 10 11 12}
|
||||
2 {0 1 2 3 4 5 6 7}
|
||||
X'0105'
|
||||
}
|
||||
|
||||
do_execsql_test 5.8 {
|
||||
INSERT INTO t1(t1) VALUES('merge=1,6');
|
||||
INSERT INTO t1(t1) VALUES('merge=1,6');
|
||||
SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level;
|
||||
SELECT quote(value) from t1_stat WHERE rowid=1;
|
||||
} {
|
||||
0 {0 1 2 3 4}
|
||||
1 {0 1 2 3 4 5 6 7 8 9 10 11 12 13}
|
||||
2 {0 1 2 3 4 5 6 7 8} X'0106'
|
||||
}
|
||||
|
||||
do_test 5.8.1 { fts3_integrity_check t1 } ok
|
||||
|
||||
do_test 5.9 {
|
||||
set L [expr 16*16*7 + 16*3 + 12]
|
||||
foreach docid [execsql {
|
||||
SELECT docid FROM t1 UNION ALL SELECT docid FROM t1 LIMIT $L
|
||||
}] {
|
||||
execsql {INSERT INTO t1 SELECT * FROM t1 WHERE docid=$docid}
|
||||
}
|
||||
} {}
|
||||
|
||||
do_execsql_test 5.10 {
|
||||
SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level;
|
||||
SELECT quote(value) from t1_stat WHERE rowid=1;
|
||||
} {
|
||||
0 0 1 {0 1} 2 0 3 0 X'0106'
|
||||
}
|
||||
|
||||
do_execsql_test 5.11 {
|
||||
INSERT INTO t1(t1) VALUES('merge=1,6');
|
||||
SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level;
|
||||
SELECT quote(value) from t1_stat WHERE rowid=1;
|
||||
} {
|
||||
0 0 1 {0 1} 2 0 3 0 X''
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test cases 6.*
|
||||
#
|
||||
# At one point the following test caused an assert() to fail (because the
|
||||
# second 'merge=1,2' operation below actually "merges" a single input
|
||||
# segment, which was unexpected).
|
||||
#
|
||||
do_test 6.1 {
|
||||
reset_db
|
||||
set a [string repeat a 900]
|
||||
set b [string repeat b 900]
|
||||
set c [string repeat c 900]
|
||||
set d [string repeat d 900]
|
||||
|
||||
execsql "CREATE VIRTUAL TABLE t1 USING $mod"
|
||||
execsql {
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES($a);
|
||||
INSERT INTO t1 VALUES($b);
|
||||
COMMIT;
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES($c);
|
||||
INSERT INTO t1 VALUES($d);
|
||||
COMMIT;
|
||||
}
|
||||
|
||||
execsql {
|
||||
INSERT INTO t1(t1) VALUES('merge=1,2');
|
||||
INSERT INTO t1(t1) VALUES('merge=1,2');
|
||||
}
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test cases 7.*
|
||||
#
|
||||
# Test that the value returned by sqlite3_total_changes() increases by
|
||||
# 1 following a no-op "merge=A,B", or by more than 1 if actual work is
|
||||
# performed.
|
||||
#
|
||||
do_test 7.0 {
|
||||
reset_db
|
||||
fts3_build_db_1 -module $mod 1000
|
||||
} {}
|
||||
|
||||
do_execsql_test 7.1 {
|
||||
SELECT level, group_concat(idx, ' ') FROM t1_segdir GROUP BY level
|
||||
} {
|
||||
0 {0 1 2 3 4 5 6 7}
|
||||
1 {0 1 2 3 4 5 6 7 8 9 10 11 12 13}
|
||||
2 {0 1 2}
|
||||
}
|
||||
do_test 7.2 {
|
||||
set x [db total_changes]
|
||||
execsql { INSERT INTO t1(t1) VALUES('merge=2,10') }
|
||||
expr { ([db total_changes] - $x)>1 }
|
||||
} {1}
|
||||
do_test 7.3 {
|
||||
set x [db total_changes]
|
||||
execsql { INSERT INTO t1(t1) VALUES('merge=200,10') }
|
||||
expr { ([db total_changes] - $x)>1 }
|
||||
} {1}
|
||||
do_test 7.4 {
|
||||
set x [db total_changes]
|
||||
execsql { INSERT INTO t1(t1) VALUES('merge=200,10') }
|
||||
expr { ([db total_changes] - $x)>1 }
|
||||
} {0}
|
||||
do_test 7.5 {
|
||||
set x [db total_changes]
|
||||
execsql { INSERT INTO t1(t1) VALUES('merge=200,10') }
|
||||
expr { ([db total_changes] - $x)>1 }
|
||||
} {0}
|
||||
|
||||
}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,38 @@
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/fts3_common.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
set ::testprefix fts4merge2
|
||||
|
||||
# If SQLITE_ENABLE_FTS3 is defined, omit this file.
|
||||
ifcapable !fts3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_test 1.0 {
|
||||
fts3_build_db_1 1000
|
||||
faultsim_save_and_close
|
||||
} {}
|
||||
|
||||
do_faultsim_test 1.1 -faults oom-* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql { INSERT INTO t1(t1) VALUES('merge=32,4') }
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
|
||||
do_faultsim_test 1.2 -faults oom-t* -prep {
|
||||
if {$iFail<100} {set iFail 803}
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql { INSERT INTO t1(t1) VALUES('merge=1,2') }
|
||||
execsql { INSERT INTO t1(t1) VALUES('merge=1,2') }
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,97 @@
|
||||
# 2012 March 06
|
||||
#
|
||||
# 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 incremental merge function.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/fts3_common.tcl
|
||||
source $testdir/lock_common.tcl
|
||||
source $testdir/bc_common.tcl
|
||||
|
||||
set ::testprefix fts4merge3
|
||||
|
||||
if {"" == [bc_find_binaries backcompat.test]} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
db close
|
||||
do_all_bc_test {
|
||||
|
||||
sql2 { PRAGMA page_size = 512 }
|
||||
if { 0==[catch { sql2 { CREATE VIRTUAL TABLE x USING fts4 } } ] } {
|
||||
|
||||
# Build a large database.
|
||||
set msg "this takes around 12 seconds"
|
||||
do_test "1.1 ($msg)" { fts3_build_db_2 20000 } {}
|
||||
|
||||
# Run some queries on it, using the old and new versions.
|
||||
do_test 1.2 { sql1 "SELECT docid FROM t2 WHERE t2 MATCH 'abc'" } {1485}
|
||||
do_test 1.3 { sql2 "SELECT docid FROM t2 WHERE t2 MATCH 'abc'" } {1485}
|
||||
|
||||
do_test 1.4 { sql2 "PRAGMA page_count" } {1286}
|
||||
do_test 1.5 { sql2 {
|
||||
SELECT level, count(*) FROM t2_segdir GROUP BY level ORDER BY 1
|
||||
} } [list 0 15 1 1 2 14 3 4]
|
||||
|
||||
# Run some incr-merge operations on the db.
|
||||
for {set i 0} {$i<10} {incr i} {
|
||||
do_test 1.6.$i.1 { sql1 { INSERT INTO t2(t2) VALUES('merge=2,2') } } {}
|
||||
do_test 1.6.$i.2 {
|
||||
sql2 "SELECT docid FROM t2 WHERE t2 MATCH 'abc'"
|
||||
} {1485}
|
||||
}
|
||||
|
||||
do_test 1.7 { sql2 {
|
||||
SELECT level, count(*) FROM t2_segdir GROUP BY level ORDER BY 1
|
||||
} } [list 0 1 2 18 3 5]
|
||||
|
||||
# Using the old connection, insert many rows.
|
||||
do_test 1.8 {
|
||||
for {set i 0} {$i < 1500} {incr i} {
|
||||
sql2 "INSERT INTO t2 SELECT content FROM t2 WHERE docid = $i"
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test 1.9 { sql2 {
|
||||
SELECT level, count(*) FROM t2_segdir GROUP BY level ORDER BY 1
|
||||
} } [list 0 13 1 13 2 5 3 6]
|
||||
|
||||
# Run a big incr-merge operation on the db.
|
||||
do_test 1.10 { sql1 { INSERT INTO t2(t2) VALUES('merge=2000,2') } } {}
|
||||
do_test 1.11 {
|
||||
sql2 "SELECT docid FROM t2 WHERE t2 MATCH 'abc'"
|
||||
} {1485 21485}
|
||||
|
||||
do_test 1.12 {
|
||||
for {set i 0} {$i < 1500} {incr i} {
|
||||
sql2 "INSERT INTO t2 SELECT content FROM t2 WHERE docid = $i"
|
||||
}
|
||||
} {}
|
||||
do_test 1.13 {
|
||||
sql2 "SELECT docid FROM t2 WHERE t2 MATCH 'abc'"
|
||||
} {1485 21485 22985}
|
||||
|
||||
do_test 1.14 {
|
||||
sql2 "INSERT INTO t2(t2) VALUES('optimize')"
|
||||
sql2 "SELECT docid FROM t2 WHERE t2 MATCH 'abc'"
|
||||
} {1485 21485 22985}
|
||||
|
||||
do_test 1.15 { sql2 {
|
||||
SELECT level, count(*) FROM t2_segdir GROUP BY level ORDER BY 1
|
||||
} } {6 1}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -1247,4 +1247,48 @@ do_test func-28.1 {
|
||||
}
|
||||
} {1 {unknown function: nosuchfunc()}}
|
||||
|
||||
# Verify that the length() and typeof() functions do not actually load
|
||||
# the content of their argument.
|
||||
#
|
||||
do_test func-29.1 {
|
||||
db eval {
|
||||
CREATE TABLE t29(id INTEGER PRIMARY KEY, x, y);
|
||||
INSERT INTO t29 VALUES(1, 2, 3), (2, NULL, 4), (3, 4.5, 5);
|
||||
INSERT INTO t29 VALUES(4, randomblob(1000000), 6);
|
||||
INSERT INTO t29 VALUES(5, "hello", 7);
|
||||
}
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
sqlite3_db_status db CACHE_MISS 1
|
||||
db eval {SELECT typeof(x), length(x), typeof(y) FROM t29 ORDER BY id}
|
||||
} {integer 1 integer null {} integer real 3 integer blob 1000000 integer text 5 integer}
|
||||
do_test func-29.2 {
|
||||
set x [lindex [sqlite3_db_status db CACHE_MISS 1] 1]
|
||||
if {$x<5} {set x 1}
|
||||
set x
|
||||
} {1}
|
||||
do_test func-29.3 {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
sqlite3_db_status db CACHE_MISS 1
|
||||
db eval {SELECT typeof(+x) FROM t29 ORDER BY id}
|
||||
} {integer null real blob text}
|
||||
do_test func-29.4 {
|
||||
set x [lindex [sqlite3_db_status db CACHE_MISS 1] 1]
|
||||
if {$x>100} {set x many}
|
||||
set x
|
||||
} {many}
|
||||
do_test func-29.5 {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
sqlite3_db_status db CACHE_MISS 1
|
||||
db eval {SELECT sum(length(x)) FROM t29}
|
||||
} {1000009}
|
||||
do_test func-29.6 {
|
||||
set x [lindex [sqlite3_db_status db CACHE_MISS 1] 1]
|
||||
if {$x<5} {set x 1}
|
||||
set x
|
||||
} {1}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
# 2012 March 23
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
#
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
ifcapable {!incrblob} { finish_test ; return }
|
||||
set testprefix incrblob4
|
||||
|
||||
proc create_t1 {} {
|
||||
execsql {
|
||||
PRAGMA page_size = 1024;
|
||||
CREATE TABLE t1(k INTEGER PRIMARY KEY, v);
|
||||
}
|
||||
}
|
||||
|
||||
proc populate_t1 {} {
|
||||
set data [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]
|
||||
foreach d $data {
|
||||
set blob [string repeat $d 900]
|
||||
execsql { INSERT INTO t1(v) VALUES($blob) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
do_test 1.1 {
|
||||
create_t1
|
||||
populate_t1
|
||||
} {}
|
||||
|
||||
do_test 1.2 {
|
||||
set blob [db incrblob t1 v 5]
|
||||
read $blob 10
|
||||
} {eeeeeeeeee}
|
||||
|
||||
do_test 1.3 {
|
||||
execsql { DELETE FROM t1 }
|
||||
populate_t1
|
||||
} {}
|
||||
|
||||
|
||||
|
||||
do_test 2.1 {
|
||||
reset_db
|
||||
create_t1
|
||||
populate_t1
|
||||
} {}
|
||||
|
||||
do_test 2.2 {
|
||||
set blob [db incrblob t1 v 10]
|
||||
read $blob 10
|
||||
} {jjjjjjjjjj}
|
||||
|
||||
do_test 2.3 {
|
||||
set new [string repeat % 900]
|
||||
execsql { DELETE FROM t1 WHERE k=10 }
|
||||
execsql { DELETE FROM t1 WHERE k=9 }
|
||||
execsql { INSERT INTO t1(v) VALUES($new) }
|
||||
} {}
|
||||
|
||||
|
||||
|
||||
do_test 3.1 {
|
||||
reset_db
|
||||
create_t1
|
||||
populate_t1
|
||||
} {}
|
||||
|
||||
do_test 3.2 {
|
||||
set blob [db incrblob t1 v 20]
|
||||
read $blob 10
|
||||
} {tttttttttt}
|
||||
|
||||
do_test 3.3 {
|
||||
set new [string repeat % 900]
|
||||
execsql { UPDATE t1 SET v = $new WHERE k = 20 }
|
||||
execsql { DELETE FROM t1 WHERE k=19 }
|
||||
execsql { INSERT INTO t1(v) VALUES($new) }
|
||||
} {}
|
||||
|
||||
finish_test
|
||||
|
||||
+28
-3
@@ -1795,7 +1795,7 @@ do_test pager1-18.2 {
|
||||
catchsql { SELECT count(*) FROM t1 } db2
|
||||
} {1 {database disk image is malformed}}
|
||||
db2 close
|
||||
do_test pager1-18.3 {
|
||||
do_test pager1-18.3.1 {
|
||||
execsql {
|
||||
CREATE TABLE t2(x);
|
||||
INSERT INTO t2 VALUES(a_string(5000));
|
||||
@@ -1803,13 +1803,38 @@ do_test pager1-18.3 {
|
||||
set pgno [expr ([file size test.db] / 1024)-2]
|
||||
hexio_write test.db [expr ($pgno-1)*1024] 00000000
|
||||
sqlite3 db2 test.db
|
||||
catchsql { SELECT length(x) FROM t2 } db2
|
||||
# even though x is malformed, because typeof() does
|
||||
# not load the content of x, the error is not noticed.
|
||||
catchsql { SELECT typeof(x) FROM t2 } db2
|
||||
} {0 text}
|
||||
do_test pager1-18.3.2 {
|
||||
# in this case, the value of x is loaded and so the error is
|
||||
# detected
|
||||
catchsql { SELECT length(x||'') FROM t2 } db2
|
||||
} {1 {database disk image is malformed}}
|
||||
db2 close
|
||||
do_test pager1-18.3.3 {
|
||||
execsql {
|
||||
DELETE FROM t2;
|
||||
INSERT INTO t2 VALUES(randomblob(5000));
|
||||
}
|
||||
set pgno [expr ([file size test.db] / 1024)-2]
|
||||
hexio_write test.db [expr ($pgno-1)*1024] 00000000
|
||||
sqlite3 db2 test.db
|
||||
# even though x is malformed, because length() and typeof() do
|
||||
# not load the content of x, the error is not noticed.
|
||||
catchsql { SELECT length(x), typeof(x) FROM t2 } db2
|
||||
} {0 {5000 blob}}
|
||||
do_test pager1-18.3.4 {
|
||||
# in this case, the value of x is loaded and so the error is
|
||||
# detected
|
||||
catchsql { SELECT length(x||'') FROM t2 } db2
|
||||
} {1 {database disk image is malformed}}
|
||||
db2 close
|
||||
do_test pager1-18.4 {
|
||||
hexio_write test.db [expr ($pgno-1)*1024] 90000000
|
||||
sqlite3 db2 test.db
|
||||
catchsql { SELECT length(x) FROM t2 } db2
|
||||
catchsql { SELECT length(x||'') FROM t2 } db2
|
||||
} {1 {database disk image is malformed}}
|
||||
db2 close
|
||||
do_test pager1-18.5 {
|
||||
|
||||
@@ -184,7 +184,8 @@ test_suite "fts3" -prefix "" -description {
|
||||
fts3aux1.test fts3comp1.test fts3auto.test
|
||||
fts4aa.test fts4content.test
|
||||
fts3conf.test fts3prefix.test fts3fault2.test fts3corrupt.test
|
||||
fts3corrupt2.test fts3first.test fts4langid.test
|
||||
fts3corrupt2.test fts3first.test fts4langid.test fts4merge.test
|
||||
fts4check.test
|
||||
}
|
||||
|
||||
|
||||
|
||||
+4
-4
@@ -130,11 +130,11 @@ ifcapable fts3 {
|
||||
"INSERT INTO x1 VALUES('North northwest wind between 8 and 14 mph');"
|
||||
"-- INSERT INTO 'main'.'x1_content' VALUES(?,(?))"
|
||||
"-- REPLACE INTO 'main'.'x1_docsize' VALUES(?,?)"
|
||||
"-- SELECT value FROM 'main'.'x1_stat' WHERE id=0"
|
||||
"-- REPLACE INTO 'main'.'x1_stat' VALUES(0,?)"
|
||||
"-- SELECT value FROM 'main'.'x1_stat' WHERE id=?"
|
||||
"-- REPLACE INTO 'main'.'x1_stat' VALUES(?,?)"
|
||||
"-- SELECT (SELECT max(idx) FROM 'main'.'x1_segdir' WHERE level = ?) + 1"
|
||||
"-- SELECT coalesce((SELECT max(blockid) FROM 'main'.'x1_segments') + 1, 1)"
|
||||
"-- INSERT INTO 'main'.'x1_segdir' VALUES(?,?,?,?,?,?)"
|
||||
"-- REPLACE INTO 'main'.'x1_segdir' VALUES(?,?,?,?,?,?)"
|
||||
}
|
||||
|
||||
do_trace_test 2.3 {
|
||||
@@ -146,7 +146,7 @@ ifcapable fts3 {
|
||||
"-- SELECT max(level) FROM 'main'.'x1_segdir' WHERE level BETWEEN ? AND ?"
|
||||
"-- SELECT coalesce((SELECT max(blockid) FROM 'main'.'x1_segments') + 1, 1)"
|
||||
"-- DELETE FROM 'main'.'x1_segdir' WHERE level BETWEEN ? AND ?"
|
||||
"-- INSERT INTO 'main'.'x1_segdir' VALUES(?,?,?,?,?,?)"
|
||||
"-- REPLACE INTO 'main'.'x1_segdir' VALUES(?,?,?,?,?,?)"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix vtab1
|
||||
|
||||
ifcapable !vtab||!schema_pragmas {
|
||||
finish_test
|
||||
@@ -43,6 +44,9 @@ ifcapable !vtab||!schema_pragmas {
|
||||
#
|
||||
# vtab1-14.*: Test 'IN' constraints - i.e. "SELECT * FROM t1 WHERE id IN(...)"
|
||||
#
|
||||
# vtab1-18.*: Check that the LIKE optimization is not applied when the lhs
|
||||
# is a virtual table column.
|
||||
#
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
@@ -1218,5 +1222,57 @@ do_test vtab1-17.1 {
|
||||
}
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# The following tests - vtab1-18.* - test that the optimization of LIKE
|
||||
# constraints in where.c plays well with virtual tables.
|
||||
#
|
||||
# 18.1.*: Case-insensitive LIKE.
|
||||
# 18.2.*: Case-sensitive LIKE.
|
||||
#
|
||||
unset -nocomplain echo_module_begin_fail
|
||||
|
||||
do_execsql_test 18.1.0 {
|
||||
CREATE TABLE t6(a, b TEXT);
|
||||
CREATE INDEX i6 ON t6(b, a);
|
||||
INSERT INTO t6 VALUES(1, 'Peter');
|
||||
INSERT INTO t6 VALUES(2, 'Andrew');
|
||||
INSERT INTO t6 VALUES(3, 'James');
|
||||
INSERT INTO t6 VALUES(4, 'John');
|
||||
INSERT INTO t6 VALUES(5, 'Phillip');
|
||||
INSERT INTO t6 VALUES(6, 'Bartholomew');
|
||||
CREATE VIRTUAL TABLE e6 USING echo(t6);
|
||||
}
|
||||
|
||||
foreach {tn sql res filter} {
|
||||
1.1 "SELECT a FROM e6 WHERE b>'James'" {4 1 5}
|
||||
{xFilter {SELECT rowid, * FROM 't6' WHERE b > ?} James}
|
||||
|
||||
1.2 "SELECT a FROM e6 WHERE b>='J' AND b<'K'" {3 4}
|
||||
{xFilter {SELECT rowid, * FROM 't6' WHERE b >= ? AND b < ?} J K}
|
||||
|
||||
1.3 "SELECT a FROM e6 WHERE b LIKE 'J%'" {3 4}
|
||||
{xFilter {SELECT rowid, * FROM 't6'}}
|
||||
|
||||
1.4 "SELECT a FROM e6 WHERE b LIKE 'j%'" {3 4}
|
||||
{xFilter {SELECT rowid, * FROM 't6'}}
|
||||
} {
|
||||
set echo_module {}
|
||||
do_execsql_test 18.$tn.1 $sql $res
|
||||
do_test 18.$tn.2 { lrange $::echo_module 2 end } $filter
|
||||
}
|
||||
|
||||
do_execsql_test 18.2.0 { PRAGMA case_sensitive_like = ON }
|
||||
foreach {tn sql res filter} {
|
||||
2.1 "SELECT a FROM e6 WHERE b LIKE 'J%'" {3 4}
|
||||
{xFilter {SELECT rowid, * FROM 't6'}}
|
||||
|
||||
2.2 "SELECT a FROM e6 WHERE b LIKE 'j%'" {}
|
||||
{xFilter {SELECT rowid, * FROM 't6'}}
|
||||
} {
|
||||
set echo_module {}
|
||||
do_execsql_test 18.$tn.1 $sql $res
|
||||
do_test 18.$tn.2 { lrange $::echo_module 2 end } $filter
|
||||
}
|
||||
do_execsql_test 18.2.x { PRAGMA case_sensitive_like = OFF }
|
||||
|
||||
finish_test
|
||||
|
||||
Reference in New Issue
Block a user