Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Merge latest changes from trunk |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | ben-testing |
| Files: | files | file ages | folders |
| SHA1: |
7bbeb8820592aea52f3d0aadfd7568c3 |
| User & Date: | ben 2011-06-18 17:00:14.593 |
Context
|
2011-07-20
| ||
| 08:29 | Merge trunk with ben-testing ... (check-in: 74d65bab28 user: ben tags: ben-testing) | |
|
2011-06-18
| ||
| 17:00 | Merge latest changes from trunk ... (check-in: 7bbeb88205 user: ben tags: ben-testing) | |
| 16:50 | Add a version number that is increased by one on each release. Make the initial version number 1.18 since there have been 17 prior releases. ... (check-in: e0303181a5 user: drh tags: trunk) | |
|
2011-06-05
| ||
| 08:54 | Merge latest SQLite from trunk ... (check-in: be264779de user: ben tags: ben-testing) | |
Changes
Added VERSION.
> | 1 | 1.18 |
Changes to src/checkout.c.
| ︙ | ︙ | |||
72 73 74 75 76 77 78 |
if( !is_a_version(vid) ){
fossil_fatal("object [%.10s] is not a check-in", blob_str(&uuid));
}
load_vfile_from_rid(vid);
return vid;
}
| < < < < < < < < < < | 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
if( !is_a_version(vid) ){
fossil_fatal("object [%.10s] is not a check-in", blob_str(&uuid));
}
load_vfile_from_rid(vid);
return vid;
}
/*
** Set or clear the vfile.isexe flag for a file.
*/
static void set_or_clear_isexe(const char *zFilename, int vid, int onoff){
static Stmt s;
db_static_prepare(&s,
"UPDATE vfile SET isexe=:isexe"
|
| ︙ | ︙ |
Changes to src/descendants.c.
| ︙ | ︙ | |||
184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
pqueue_insert(&queue, pid, -mtime, 0);
}
}
db_reset(&q);
}
bag_clear(&seen);
pqueue_clear(&queue);
db_finalize(&ins);
db_finalize(&q);
}
/*
** Load the record ID rid and up to N-1 closest descendants into
** the "ok" table.
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
pqueue_insert(&queue, pid, -mtime, 0);
}
}
db_reset(&q);
}
bag_clear(&seen);
pqueue_clear(&queue);
db_finalize(&ins);
db_finalize(&q);
}
/*
** Compute up to N direct ancestors (merge ancestors do not count)
** for the check-in rid and put them in a table named "ancestor".
** Label each generation with consecutive integers going backwards
** in time such that rid has the smallest generation number and the oldest
** direct ancestor as the largest generation number.
*/
void compute_direct_ancestors(int rid, int N){
Stmt ins;
Stmt q;
int gen = 0;
db_multi_exec(
"CREATE TEMP TABLE ancestor(rid INTEGER, generation INTEGER PRIMARY KEY);"
"INSERT INTO ancestor VALUES(%d, 0);", rid
);
db_prepare(&ins, "INSERT INTO ancestor VALUES(:rid, :gen)");
db_prepare(&q,
"SELECT pid FROM plink"
" WHERE cid=:rid AND isprim"
);
while( (N--)>0 ){
db_bind_int(&q, ":rid", rid);
if( db_step(&q)!=SQLITE_ROW ) break;
rid = db_column_int(&q, 0);
db_reset(&q);
gen++;
db_bind_int(&ins, ":rid", rid);
db_bind_int(&ins, ":gen", gen);
db_step(&ins);
db_reset(&ins);
}
db_finalize(&ins);
db_finalize(&q);
}
/*
** Load the record ID rid and up to N-1 closest descendants into
** the "ok" table.
|
| ︙ | ︙ |
Changes to src/diff.c.
| ︙ | ︙ | |||
756 757 758 759 760 761 762 | ** Compute a complete annotation on a file. The file is identified ** by its filename number (filename.fnid) and the baseline in which ** it was checked in (mlink.mid). */ static void annotate_file( Annotator *p, /* The annotator */ int fnid, /* The name of the file to be annotated */ | | | > | | | | | | 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 |
** Compute a complete annotation on a file. The file is identified
** by its filename number (filename.fnid) and the baseline in which
** it was checked in (mlink.mid).
*/
static void annotate_file(
Annotator *p, /* The annotator */
int fnid, /* The name of the file to be annotated */
int mid, /* Use the version of the file in this check-in */
int webLabel, /* Use web-style annotations if true */
int iLimit, /* Limit the number of levels if greater than zero */
int annFlags /* Flags to alter the annotation */
){
Blob toAnnotate; /* Text of the final (mid) version of the file */
Blob step; /* Text of previous revision */
int rid; /* Artifact ID of the file being annotated */
char *zLabel; /* Label to apply to a line */
Stmt q; /* Query returning all ancestor versions */
/* Initialize the annotation */
rid = db_int(0, "SELECT fid FROM mlink WHERE mid=%d AND fnid=%d",mid,fnid);
if( rid==0 ){
fossil_panic("file #%d is unchanged in manifest #%d", fnid, mid);
}
if( !content_get(rid, &toAnnotate) ){
fossil_panic("unable to retrieve content of artifact #%d", rid);
}
db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
if( iLimit<=0 ) iLimit = 1000000000;
compute_direct_ancestors(mid, iLimit);
annotation_start(p, &toAnnotate);
db_prepare(&q,
"SELECT mlink.fid,"
" (SELECT uuid FROM blob WHERE rid=mlink.%s),"
" date(event.mtime), "
" coalesce(event.euser,event.user) "
" FROM ancestor, mlink, event"
" WHERE mlink.fnid=%d"
" AND mlink.mid=ancestor.rid"
" AND event.objid=ancestor.rid"
" ORDER BY ancestor.generation ASC"
" LIMIT %d",
(annFlags & ANN_FILE_VERS)!=0 ? "fid" : "mid",
fnid,
iLimit>0 ? iLimit : 10000000
);
while( db_step(&q)==SQLITE_ROW ){
int pid = db_column_int(&q, 0);
|
| ︙ | ︙ |
Changes to src/export.c.
| ︙ | ︙ | |||
37 38 39 40 41 42 43 |
}
db_static_prepare(&q, "SELECT info FROM user WHERE login=:user");
db_bind_text(&q, ":user", zUser);
if( db_step(&q)!=SQLITE_ROW ){
db_reset(&q);
for(i=0; zUser[i] && zUser[i]!='>' && zUser[i]!='<'; i++){}
if( zUser[i]==0 ){
| | | | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
}
db_static_prepare(&q, "SELECT info FROM user WHERE login=:user");
db_bind_text(&q, ":user", zUser);
if( db_step(&q)!=SQLITE_ROW ){
db_reset(&q);
for(i=0; zUser[i] && zUser[i]!='>' && zUser[i]!='<'; i++){}
if( zUser[i]==0 ){
printf(" %s <%s>", zUser, zUser);
return;
}
zName = mprintf("%s", zUser);
for(i=j=0; zName[i]; i++){
if( zName[i]!='<' && zName[i]!='>' ){
zName[j++] = zName[i];
}
}
zName[j] = 0;
printf(" %s <%s>", zName, zUser);
free(zName);
return;
}
zContact = db_column_text(&q, 0);
for(i=0; zContact[i] && zContact[i]!='>' && zContact[i]!='<'; i++){}
if( zContact[i]==0 ){
printf(" %s <%s>", zContact[0] ? zContact : zUser, zUser);
db_reset(&q);
return;
}
if( zContact[i]=='<' ){
zEmail = mprintf("%s", &zContact[i]);
for(i=0; zEmail[i] && zEmail[i]!='>'; i++){}
if( zEmail[i]=='>' ) zEmail[i+1] = 0;
|
| ︙ | ︙ | |||
194 195 196 197 198 199 200 201 202 203 204 |
db_prepare(&q,
"SELECT tagname, rid, strftime('%%s',mtime)"
" FROM tagxref JOIN tag USING(tagid)"
" WHERE tagtype=1 AND tagname GLOB 'sym-*'"
);
while( db_step(&q)==SQLITE_ROW ){
const char *zTagname = db_column_text(&q, 0);
int rid = db_column_int(&q, 1);
const char *zSecSince1970 = db_column_text(&q, 2);
if( rid==0 || !bag_find(&vers, rid) ) continue;
zTagname += 4;
| > > > > > > | > | 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
db_prepare(&q,
"SELECT tagname, rid, strftime('%%s',mtime)"
" FROM tagxref JOIN tag USING(tagid)"
" WHERE tagtype=1 AND tagname GLOB 'sym-*'"
);
while( db_step(&q)==SQLITE_ROW ){
const char *zTagname = db_column_text(&q, 0);
char *zEncoded = 0;
int rid = db_column_int(&q, 1);
const char *zSecSince1970 = db_column_text(&q, 2);
int i;
if( rid==0 || !bag_find(&vers, rid) ) continue;
zTagname += 4;
zEncoded = mprintf("%s", zTagname);
for(i=0; zEncoded[i]; i++){
if( !fossil_isalnum(zEncoded[i]) ) zEncoded[i] = '_';
}
printf("tag %s\n", zEncoded);
printf("from :%d\n", rid+firstCkin);
printf("tagger <tagger> %s +0000\n", zSecSince1970);
printf("data 0\n");
fossil_free(zEncoded);
}
db_finalize(&q);
bag_clear(&vers);
}
|
Changes to src/file.c.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 24 25 26 27 28 29 30 | ** ** File utilities */ #include "config.h" #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include "file.h" /* ** The file status information from the most recent stat() call. ** ** Use _stati64 rather than stat on windows, in order to handle files ** larger than 2GB. | > > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | ** ** File utilities */ #include "config.h" #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <string.h> #include <errno.h> #include "file.h" /* ** The file status information from the most recent stat() call. ** ** Use _stati64 rather than stat on windows, in order to handle files ** larger than 2GB. |
| ︙ | ︙ | |||
197 198 199 200 201 202 203 |
*/
int file_setexe(const char *zFilename, int onoff){
int rc = 0;
#if !defined(_WIN32)
struct stat buf;
if( stat(zFilename, &buf)!=0 ) return 0;
if( onoff ){
| > | | | 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
*/
int file_setexe(const char *zFilename, int onoff){
int rc = 0;
#if !defined(_WIN32)
struct stat buf;
if( stat(zFilename, &buf)!=0 ) return 0;
if( onoff ){
int targetMode = (buf.st_mode & 0444)>>2;
if( (buf.st_mode & 0111)!=targetMode ){
chmod(zFilename, buf.st_mode | targetMode);
rc = 1;
}
}else{
if( (buf.st_mode & 0111)!=0 ){
chmod(zFilename, buf.st_mode & ~0111);
rc = 1;
}
|
| ︙ | ︙ | |||
394 395 396 397 398 399 400 |
void file_getcwd(char *zBuf, int nBuf){
#ifdef _WIN32
char *zPwdUtf8;
int nPwd;
int i;
char zPwd[2000];
if( getcwd(zPwd, sizeof(zPwd)-1)==0 ){
| | > | > > > > | 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 |
void file_getcwd(char *zBuf, int nBuf){
#ifdef _WIN32
char *zPwdUtf8;
int nPwd;
int i;
char zPwd[2000];
if( getcwd(zPwd, sizeof(zPwd)-1)==0 ){
fossil_fatal("cannot find the current working directory.");
}
zPwdUtf8 = fossil_mbcs_to_utf8(zPwd);
nPwd = strlen(zPwdUtf8);
if( nPwd > nBuf-1 ){
fossil_fatal("pwd too big: max %d\n", nBuf-1);
}
for(i=0; zPwdUtf8[i]; i++) if( zPwdUtf8[i]=='\\' ) zPwdUtf8[i] = '/';
memcpy(zBuf, zPwdUtf8, nPwd+1);
fossil_mbcs_free(zPwdUtf8);
#else
if( getcwd(zBuf, nBuf-1)==0 ){
if( errno==ERANGE ){
fossil_fatal("pwd too big: max %d\n", nBuf-1);
}else{
fossil_fatal("cannot find current working directory; %s",
strerror(errno));
}
}
#endif
}
/*
** Compute a canonical pathname for a file or directory.
** Make the name absolute if it is relative.
|
| ︙ | ︙ | |||
669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 |
};
static const unsigned char zChars[] =
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789";
unsigned int i, j;
const char *zDir = ".";
for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); i++){
if( !file_isdir(azDirs[i]) ) continue;
zDir = azDirs[i];
break;
}
/* Check that the output buffer is large enough for the temporary file
** name. If it is not, return SQLITE_ERROR.
*/
if( (strlen(zDir) + 17) >= (size_t)nBuf ){
fossil_fatal("insufficient space for temporary filename");
}
do{
sqlite3_snprintf(nBuf-17, zBuf, "%s/", zDir);
j = (int)strlen(zBuf);
sqlite3_randomness(15, &zBuf[j]);
for(i=0; i<15; i++, j++){
zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
}
zBuf[j] = 0;
| > > | | 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 |
};
static const unsigned char zChars[] =
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789";
unsigned int i, j;
const char *zDir = ".";
int cnt = 0;
for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); i++){
if( !file_isdir(azDirs[i]) ) continue;
zDir = azDirs[i];
break;
}
/* Check that the output buffer is large enough for the temporary file
** name. If it is not, return SQLITE_ERROR.
*/
if( (strlen(zDir) + 17) >= (size_t)nBuf ){
fossil_fatal("insufficient space for temporary filename");
}
do{
if( cnt++>20 ) fossil_panic("cannot generate a temporary filename");
sqlite3_snprintf(nBuf-17, zBuf, "%s/", zDir);
j = (int)strlen(zBuf);
sqlite3_randomness(15, &zBuf[j]);
for(i=0; i<15; i++, j++){
zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
}
zBuf[j] = 0;
}while( file_size(zBuf)>=0 );
}
/*
** Return true if a file named zName exists and has identical content
** to the blob pContent. If zName does not exist or if the content is
** different in any way, then return false.
|
| ︙ | ︙ |
Changes to src/http.c.
| ︙ | ︙ | |||
108 109 110 111 112 113 114 |
char *zCredentials = mprintf("%s:%s", g.urlUser, &g.urlPasswd[1]);
char *zEncoded = encode64(zCredentials, -1);
blob_appendf(pHdr, "Authorization: Basic %s\r\n", zEncoded);
fossil_free(zEncoded);
fossil_free(zCredentials);
}
blob_appendf(pHdr, "Host: %s\r\n", g.urlHostname);
| | > | 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
char *zCredentials = mprintf("%s:%s", g.urlUser, &g.urlPasswd[1]);
char *zEncoded = encode64(zCredentials, -1);
blob_appendf(pHdr, "Authorization: Basic %s\r\n", zEncoded);
fossil_free(zEncoded);
fossil_free(zCredentials);
}
blob_appendf(pHdr, "Host: %s\r\n", g.urlHostname);
blob_appendf(pHdr, "User-Agent: Fossil/" RELEASE_VERSION
"-" MANIFEST_VERSION "\r\n");
if( g.fHttpTrace ){
blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n");
}else{
blob_appendf(pHdr, "Content-Type: application/x-fossil\r\n");
}
blob_appendf(pHdr, "Content-Length: %d\r\n\r\n", blob_size(pPayload));
}
|
| ︙ | ︙ |
Changes to src/import.c.
| ︙ | ︙ | |||
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
** Compare two ImportFile objects for sorting
*/
static int mfile_cmp(const void *pLeft, const void *pRight){
const ImportFile *pA = (const ImportFile*)pLeft;
const ImportFile *pB = (const ImportFile*)pRight;
return fossil_strcmp(pA->zName, pB->zName);
}
/* Forward reference */
static void import_prior_files(void);
/*
** Use data accumulated in gg from a "commit" record to add a new
** manifest artifact to the BLOB table.
*/
static void finish_commit(void){
int i;
char *zFromBranch;
Blob record, cksum;
import_prior_files();
qsort(gg.aFile, gg.nFile, sizeof(gg.aFile[0]), mfile_cmp);
blob_zero(&record);
blob_appendf(&record, "C %F\n", gg.zComment);
blob_appendf(&record, "D %s\n", gg.zDate);
for(i=0; i<gg.nFile; i++){
const char *zUuid = gg.aFile[i].zUuid;
| > > > > > > > > > > > > | 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
** Compare two ImportFile objects for sorting
*/
static int mfile_cmp(const void *pLeft, const void *pRight){
const ImportFile *pA = (const ImportFile*)pLeft;
const ImportFile *pB = (const ImportFile*)pRight;
return fossil_strcmp(pA->zName, pB->zName);
}
/*
** Compare two strings for sorting.
*/
static int string_cmp(const void *pLeft, const void *pRight){
const char *zLeft = *(char const **)pLeft;
const char *zRight = *(char const **)pRight;
return fossil_strcmp(zLeft, zRight);
}
/* Forward reference */
static void import_prior_files(void);
/*
** Use data accumulated in gg from a "commit" record to add a new
** manifest artifact to the BLOB table.
*/
static void finish_commit(void){
int i;
char *zFromBranch;
char *aTCard[4]; /* Array of T cards for manifest */
int nTCard = 0; /* Entries used in aTCard[] */
Blob record, cksum;
import_prior_files();
qsort(gg.aFile, gg.nFile, sizeof(gg.aFile[0]), mfile_cmp);
blob_zero(&record);
blob_appendf(&record, "C %F\n", gg.zComment);
blob_appendf(&record, "D %s\n", gg.zDate);
for(i=0; i<gg.nFile; i++){
const char *zUuid = gg.aFile[i].zUuid;
|
| ︙ | ︙ | |||
243 244 245 246 247 248 249 250 |
}
blob_append(&record, "\n", 1);
zFromBranch = db_text(0, "SELECT brnm FROM xbranch WHERE tname=%Q",
gg.zFromMark);
}else{
zFromBranch = 0;
}
if( !gg.tagCommit && fossil_strcmp(zFromBranch, gg.zBranch)!=0 ){
| > > > > | | | < > > > > > | | > > > > | 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
}
blob_append(&record, "\n", 1);
zFromBranch = db_text(0, "SELECT brnm FROM xbranch WHERE tname=%Q",
gg.zFromMark);
}else{
zFromBranch = 0;
}
/* Add the required "T" cards to the manifest. Make sure they are added
** in sorted order and without any duplicates. Otherwise, fossil will not
** recognize the document as a valid manifest. */
if( !gg.tagCommit && fossil_strcmp(zFromBranch, gg.zBranch)!=0 ){
aTCard[nTCard++] = mprintf("T *branch * %F\n", gg.zBranch);
aTCard[nTCard++] = mprintf("T *sym-%F *\n", gg.zBranch);
if( zFromBranch ){
aTCard[nTCard++] = mprintf("T -sym-%F *\n", zFromBranch);
}
}
if( gg.zFrom==0 ){
aTCard[nTCard++] = mprintf("T *sym-trunk *\n");
}
qsort(aTCard, nTCard, sizeof(char *), string_cmp);
for(i=0; i<nTCard; i++){
if( i==0 || fossil_strcmp(aTCard[i-1], aTCard[i]) ){
blob_appendf(&record, "%s", aTCard[i]);
}
}
for(i=0; i<nTCard; i++) free(aTCard[i]);
free(zFromBranch);
db_multi_exec("INSERT INTO xbranch(tname, brnm) VALUES(%Q,%Q)",
gg.zMark, gg.zBranch);
blob_appendf(&record, "U %F\n", gg.zUser);
md5sum_blob(&record, &cksum);
blob_appendf(&record, "Z %b\n", &cksum);
fast_insert_content(&record, gg.zMark, 1);
blob_reset(&record);
|
| ︙ | ︙ |
Changes to src/info.c.
| ︙ | ︙ | |||
188 189 190 191 192 193 194 |
db_prepare(&q,
"SELECT tag.tagid, tagname, "
" (SELECT uuid FROM blob WHERE rid=tagxref.srcid AND rid!=%d),"
" value, datetime(tagxref.mtime,'localtime'), tagtype,"
" (SELECT uuid FROM blob WHERE rid=tagxref.origid AND rid!=%d)"
" FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid"
" WHERE tagxref.rid=%d AND tagname NOT GLOB '%s'"
| | | 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
db_prepare(&q,
"SELECT tag.tagid, tagname, "
" (SELECT uuid FROM blob WHERE rid=tagxref.srcid AND rid!=%d),"
" value, datetime(tagxref.mtime,'localtime'), tagtype,"
" (SELECT uuid FROM blob WHERE rid=tagxref.origid AND rid!=%d)"
" FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid"
" WHERE tagxref.rid=%d AND tagname NOT GLOB '%s'"
" ORDER BY tagname /*sort*/", rid, rid, rid, zNotGlob
);
while( db_step(&q)==SQLITE_ROW ){
const char *zTagname = db_column_text(&q, 1);
const char *zSrcUuid = db_column_text(&q, 2);
const char *zValue = db_column_text(&q, 3);
const char *zDate = db_column_text(&q, 4);
int tagtype = db_column_int(&q, 5);
|
| ︙ | ︙ | |||
523 524 525 526 527 528 529 |
"SELECT name,"
" mperm,"
" (SELECT uuid FROM blob WHERE rid=mlink.pid),"
" (SELECT uuid FROM blob WHERE rid=mlink.fid),"
" (SELECT name FROM filename WHERE filename.fnid=mlink.pfnid)"
" FROM mlink JOIN filename ON filename.fnid=mlink.fnid"
" WHERE mlink.mid=%d"
| | | 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 |
"SELECT name,"
" mperm,"
" (SELECT uuid FROM blob WHERE rid=mlink.pid),"
" (SELECT uuid FROM blob WHERE rid=mlink.fid),"
" (SELECT name FROM filename WHERE filename.fnid=mlink.pfnid)"
" FROM mlink JOIN filename ON filename.fnid=mlink.fnid"
" WHERE mlink.mid=%d"
" ORDER BY name /*sort*/",
rid
);
while( db_step(&q)==SQLITE_ROW ){
const char *zName = db_column_text(&q,0);
int mperm = db_column_int(&q, 1);
const char *zOld = db_column_text(&q,2);
const char *zNew = db_column_text(&q,3);
|
| ︙ | ︙ | |||
1788 1789 1790 1791 1792 1793 1794 |
@ <input type="checkbox" name="newtag"%s(zNewTagFlag) />
@ Add the following new tag name to this check-in:
@ <input type="text" style="width:15;" name="tagname" value="%h(zNewTag)" />
db_prepare(&q,
"SELECT tag.tagid, tagname FROM tagxref, tag"
" WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid"
" ORDER BY CASE WHEN tagname GLOB 'sym-*' THEN substr(tagname,5)"
| | | 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 |
@ <input type="checkbox" name="newtag"%s(zNewTagFlag) />
@ Add the following new tag name to this check-in:
@ <input type="text" style="width:15;" name="tagname" value="%h(zNewTag)" />
db_prepare(&q,
"SELECT tag.tagid, tagname FROM tagxref, tag"
" WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid"
" ORDER BY CASE WHEN tagname GLOB 'sym-*' THEN substr(tagname,5)"
" ELSE tagname END /*sort*/",
rid
);
while( db_step(&q)==SQLITE_ROW ){
int tagid = db_column_int(&q, 0);
const char *zTagName = db_column_text(&q, 1);
char zLabel[30];
sqlite3_snprintf(sizeof(zLabel), zLabel, "c%d", tagid);
|
| ︙ | ︙ |
Changes to src/main.c.
1 2 3 4 5 6 | /* ** Copyright (c) 2006 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /* ** Copyright (c) 2006 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ |
| ︙ | ︙ | |||
644 645 646 647 648 649 650 |
** COMMAND: version
**
** Usage: %fossil version
**
** Print the source code version number for the fossil executable.
*/
void version_cmd(void){
| | | 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 |
** COMMAND: version
**
** Usage: %fossil version
**
** Print the source code version number for the fossil executable.
*/
void version_cmd(void){
fossil_print("This is fossil version " RELEASE_VERSION " "
MANIFEST_VERSION " " MANIFEST_DATE " UTC\n");
}
/*
** COMMAND: help
**
|
| ︙ | ︙ |
Changes to src/main.mk.
| ︙ | ︙ | |||
279 280 281 282 283 284 285 286 287 288 289 290 291 | $(OBJDIR)/makeheaders: $(SRCDIR)/makeheaders.c $(BCC) -o $(OBJDIR)/makeheaders $(SRCDIR)/makeheaders.c $(OBJDIR)/mkindex: $(SRCDIR)/mkindex.c $(BCC) -o $(OBJDIR)/mkindex $(SRCDIR)/mkindex.c # WARNING. DANGER. Running the testsuite modifies the repository the # build is done from, i.e. the checkout belongs to. Do not sync/push # the repository after running the tests. test: $(APPNAME) $(TCLSH) test/tester.tcl $(APPNAME) | > > > | | < < | 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 | $(OBJDIR)/makeheaders: $(SRCDIR)/makeheaders.c $(BCC) -o $(OBJDIR)/makeheaders $(SRCDIR)/makeheaders.c $(OBJDIR)/mkindex: $(SRCDIR)/mkindex.c $(BCC) -o $(OBJDIR)/mkindex $(SRCDIR)/mkindex.c $(OBJDIR)/mkversion: $(SRCDIR)/mkversion.c $(BCC) -o $(OBJDIR)/mkversion $(SRCDIR)/mkversion.c # WARNING. DANGER. Running the testsuite modifies the repository the # build is done from, i.e. the checkout belongs to. Do not sync/push # the repository after running the tests. test: $(APPNAME) $(TCLSH) test/tester.tcl $(APPNAME) $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h EXTRAOBJ = $(OBJDIR)/sqlite3.o $(OBJDIR)/shell.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) # This rule prevents make from using its default rules to try build |
| ︙ | ︙ |
Changes to src/makeheaders.c.
| ︙ | ︙ | |||
3064 3065 3066 3067 3068 3069 3070 |
/*
** If a separate header file is specified, use it
*/
if( zSrc[nSrc]==':' ){
int nHdr;
char *zHdr;
zHdr = &zSrc[nSrc+1];
| | | 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 |
/*
** If a separate header file is specified, use it
*/
if( zSrc[nSrc]==':' ){
int nHdr;
char *zHdr;
zHdr = &zSrc[nSrc+1];
for(nHdr=0; zHdr[nHdr]; nHdr++){}
pFile->zHdr = StrDup(zHdr,nHdr);
}
/* Look for any 'c' or 'C' in the suffix of the file name and change
** that character to 'h' or 'H' respectively. If no 'c' or 'C' is found,
** then assume we are dealing with a header.
*/
|
| ︙ | ︙ |
Changes to src/makemake.tcl.
| ︙ | ︙ | |||
174 175 176 177 178 179 180 181 182 183 184 185 186 | $(OBJDIR)/makeheaders: $(SRCDIR)/makeheaders.c $(BCC) -o $(OBJDIR)/makeheaders $(SRCDIR)/makeheaders.c $(OBJDIR)/mkindex: $(SRCDIR)/mkindex.c $(BCC) -o $(OBJDIR)/mkindex $(SRCDIR)/mkindex.c # WARNING. DANGER. Running the testsuite modifies the repository the # build is done from, i.e. the checkout belongs to. Do not sync/push # the repository after running the tests. test: $(APPNAME) $(TCLSH) test/tester.tcl $(APPNAME) | > > > | < | < | < < | | 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | $(OBJDIR)/makeheaders: $(SRCDIR)/makeheaders.c $(BCC) -o $(OBJDIR)/makeheaders $(SRCDIR)/makeheaders.c $(OBJDIR)/mkindex: $(SRCDIR)/mkindex.c $(BCC) -o $(OBJDIR)/mkindex $(SRCDIR)/mkindex.c $(OBJDIR)/mkversion: $(SRCDIR)/mkversion.c $(BCC) -o $(OBJDIR)/mkversion $(SRCDIR)/mkversion.c # WARNING. DANGER. Running the testsuite modifies the repository the # build is done from, i.e. the checkout belongs to. Do not sync/push # the repository after running the tests. test: $(APPNAME) $(TCLSH) test/tester.tcl $(APPNAME) $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid \ $(SRCDIR)/../manifest \ $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h EXTRAOBJ = \ $(OBJDIR)/sqlite3.o \ $(OBJDIR)/shell.o \ $(OBJDIR)/th.o \ $(OBJDIR)/th_lang.o |
| ︙ | ︙ | |||
387 388 389 390 391 392 393 | $(OBJDIR)/makeheaders: $(SRCDIR)/makeheaders.c $(BCC) -o $(OBJDIR)/makeheaders $(SRCDIR)/makeheaders.c $(OBJDIR)/mkindex: $(SRCDIR)/mkindex.c $(BCC) -o $(OBJDIR)/mkindex $(SRCDIR)/mkindex.c | | | | 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 | $(OBJDIR)/makeheaders: $(SRCDIR)/makeheaders.c $(BCC) -o $(OBJDIR)/makeheaders $(SRCDIR)/makeheaders.c $(OBJDIR)/mkindex: $(SRCDIR)/mkindex.c $(BCC) -o $(OBJDIR)/mkindex $(SRCDIR)/mkindex.c $(VERSION): $(SRCDIR)/mkversion.c $(BCC) -o $(OBJDIR)/version $(SRCDIR)/mkversion.c # WARNING. DANGER. Running the testsuite modifies the repository the # build is done from, i.e. the checkout belongs to. Do not sync/push # the repository after running the tests. test: $(APPNAME) $(TCLSH) test/tester.tcl $(APPNAME) |
| ︙ | ︙ | |||
554 555 556 557 558 559 560 | makeheaders$E: $(SRCDIR)\makeheaders.c $(BCC) -o$@ $** mkindex$E: $(SRCDIR)\mkindex.c $(BCC) -o$@ $** | | | 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 | makeheaders$E: $(SRCDIR)\makeheaders.c $(BCC) -o$@ $** mkindex$E: $(SRCDIR)\mkindex.c $(BCC) -o$@ $** version$E: $B\src\mkversion.c $(BCC) -o$@ $** $(OBJDIR)\shell$O : $(SRCDIR)\shell.c $(TCC) -o$@ -c -Dmain=sqlite3_shell $(SQLITE_OPTIONS) $** $(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c $(TCC) -o$@ -c $(SQLITE_OPTIONS) $** |
| ︙ | ︙ | |||
667 668 669 670 671 672 673 | APPNAME = $(OX)\fossil$(E) all: $(OX) $(APPNAME) $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OX)\linkopts cd $(OX) | | | | | > < | | | 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 |
APPNAME = $(OX)\fossil$(E)
all: $(OX) $(APPNAME)
$(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OX)\linkopts
cd $(OX)
link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) @linkopts
$(OX)\linkopts: $B\win\Makefile.msc}
set redir {>}
foreach s [lsort [concat $src {shell sqlite3 th th_lang}]] {
writeln "\techo \$(OX)\\$s.obj $redir \$@"
set redir {>>}
}
writeln "\techo \$(LIBS) >> \$@\n\n"
writeln {
$(OX):
@-mkdir $@
translate$E: $(SRCDIR)\translate.c
$(BCC) $**
makeheaders$E: $(SRCDIR)\makeheaders.c
$(BCC) $**
mkindex$E: $(SRCDIR)\mkindex.c
$(BCC) $**
version$E: $B\src\mkversion.c
$(BCC) $**
$(OX)\shell$O : $(SRCDIR)\shell.c
$(TCC) /Fo$@ /Dmain=sqlite3_shell $(SQLITE_OPTIONS) -c $(SRCDIR)\shell.c
$(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c
$(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $**
$(OX)\th$O : $(SRCDIR)\th.c
$(TCC) /Fo$@ -c $**
|
| ︙ | ︙ | |||
878 879 880 881 882 883 884 | $(LINK) $(LINKFLAGS) -out:"$@" $< # compiling standard fossil utils $(UTILS_OBJ): %.obj: $(SRCDIR)%.c $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" # compile special windows utils | | | 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 | $(LINK) $(LINKFLAGS) -out:"$@" $< # compiling standard fossil utils $(UTILS_OBJ): %.obj: $(SRCDIR)%.c $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" # compile special windows utils version.obj: $(SRCDIR)mkversion.c $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" # generate the translated c-source files $(TRANSLATEDSRC): %_.c: $(SRCDIR)%.c translate.exe translate.exe $< >$@ # generate the index source, containing all web references,.. |
| ︙ | ︙ |
Changes to src/manifest.c.
| ︙ | ︙ | |||
340 341 342 343 344 345 346 | int n; char *zUuid; int sz = 0; /* Every control artifact ends with a '\n' character. Exit early ** if that is not the case for this artifact. */ | | | 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 |
int n;
char *zUuid;
int sz = 0;
/* Every control artifact ends with a '\n' character. Exit early
** if that is not the case for this artifact.
*/
z = blob_materialize(pContent);
n = blob_size(pContent);
if( n<=0 || z[n-1]!='\n' ){
blob_reset(pContent);
return 0;
}
/* Strip off the PGP signature if there is one. Then verify the
|
| ︙ | ︙ |
Name change from win/version.c to src/mkversion.c.
1 | /* | | > > > | > | | > > | > > > | > > > > > > > > > > > > > > > > > > | > | > > > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
/*
** This C program generates the "VERSION.h" header file from information
** extracted out of the "manifest", "manifest.uuid", and "VERSION" files.
** Call this program with three arguments:
**
** ./a.out manifest.uuid manifest VERSION
**
** Note that the manifest.uuid and manifest files are generated by Fossil.
*/
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]){
FILE *m,*u,*v;
char *z;
int i, x;
char b[1000];
char vx[1000];
u = fopen(argv[1],"r");
fgets(b, sizeof(b)-1,u);
fclose(u);
for(z=b; z[0] && z[0]!='\r' && z[0]!='\n'; z++){}
*z = 0;
printf("#define MANIFEST_UUID \"%s\"\n",b);
printf("#define MANIFEST_VERSION \"[%10.10s]\"\n",b);
m = fopen(argv[2],"r");
while(b == fgets(b, sizeof(b)-1,m)){
if(0 == strncmp("D ",b,2)){
printf("#define MANIFEST_DATE \"%.10s %.8s\"\n",b+2,b+13);
printf("#define MANIFEST_YEAR \"%.4s\"\n",b+2);
}
}
fclose(m);
v = fopen(argv[3],"r");
fgets(b, sizeof(b)-1,v);
fclose(v);
for(z=b; z[0] && z[0]!='\r' && z[0]!='\n'; z++){}
*z = 0;
printf("#define RELEASE_VERSION \"%s\"\n", b);
x=0;
i=0;
z=b;
while(1){
if( z[0]>='0' && z[0]<='9' ){
x = x*10 + z[0] - '0';
}else{
sprintf(&vx[i],"%02d",x);
i += 2;
x = 0;
if( z[0]==0 ) break;
}
z++;
}
for(z=vx; z[0]=='0'; z++){}
printf("#define RELEASE_VERSION_NUMBER %s\n", z);
return 0;
}
|
Changes to src/path.c.
| ︙ | ︙ | |||
364 365 366 367 368 369 370 | int i; /* Loop counter */ Stmt q1; /* Query of name changes */ *pnChng = 0; *aiChng = 0; if( iFrom==iTo ) return; path_reset(); | | | 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 |
int i; /* Loop counter */
Stmt q1; /* Query of name changes */
*pnChng = 0;
*aiChng = 0;
if( iFrom==iTo ) return;
path_reset();
p = path_shortest(iFrom, iTo, 1);
if( p==0 ) return;
path_reverse_path();
db_prepare(&q1,
"SELECT pfnid, fnid FROM mlink WHERE mid=:mid AND pfnid>0"
);
for(p=path.pStart; p; p=p->u.pTo){
int fnid, pfnid;
|
| ︙ | ︙ |
Changes to src/rebuild.c.
| ︙ | ︙ | |||
746 747 748 749 750 751 752 753 754 755 756 757 758 759 |
"DELETE FROM private;"
);
}
if( !privateOnly ){
db_multi_exec(
"UPDATE user SET pw='';"
"DELETE FROM config WHERE name GLOB 'last-sync-*';"
);
if( bVerily ){
db_multi_exec(
"DELETE FROM concealed;"
"UPDATE rcvfrom SET ipaddr='unknown';"
"UPDATE user SET photo=NULL, info='';"
);
| > > > > | 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 |
"DELETE FROM private;"
);
}
if( !privateOnly ){
db_multi_exec(
"UPDATE user SET pw='';"
"DELETE FROM config WHERE name GLOB 'last-sync-*';"
"DELETE FROM config WHERE name GLOB 'peer-*';"
"DELETE FROM config WHERE name GLOB 'login-group-*';"
"DELETE FROM config WHERE name GLOB 'skin:*';"
"DELETE FROM config WHERE name GLOB 'subrepo:*';"
);
if( bVerily ){
db_multi_exec(
"DELETE FROM concealed;"
"UPDATE rcvfrom SET ipaddr='unknown';"
"UPDATE user SET photo=NULL, info='';"
);
|
| ︙ | ︙ |
Changes to src/sqlite3.c.
| ︙ | ︙ | |||
648 649 650 651 652 653 654 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.7.7" #define SQLITE_VERSION_NUMBER 3007007 | | | 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.7.7" #define SQLITE_VERSION_NUMBER 3007007 #define SQLITE_SOURCE_ID "2011-06-15 13:11:06 f9750870ee04935f338e4d808900fee5a8b2b389" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version, sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros |
| ︙ | ︙ | |||
8481 8482 8483 8484 8485 8486 8487 8488 8489 8490 8491 8492 8493 8494 | SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe*,int); SQLITE_PRIVATE int sqlite3VdbeAddOp1(Vdbe*,int,int); SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe*,int,int,int); SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int); SQLITE_PRIVATE int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int); SQLITE_PRIVATE int sqlite3VdbeAddOp4Int(Vdbe*,int,int,int,int,int); SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp); SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe*, int addr, int P1); SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2); SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, int addr, int P3); SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe*, u8 P5); SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe*, int addr); SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe*, int addr, int N); SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N); | > | 8481 8482 8483 8484 8485 8486 8487 8488 8489 8490 8491 8492 8493 8494 8495 | SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe*,int); SQLITE_PRIVATE int sqlite3VdbeAddOp1(Vdbe*,int,int); SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe*,int,int,int); SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int); SQLITE_PRIVATE int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int); SQLITE_PRIVATE int sqlite3VdbeAddOp4Int(Vdbe*,int,int,int,int,int); SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp); SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*); SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe*, int addr, int P1); SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2); SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, int addr, int P3); SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe*, u8 P5); SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe*, int addr); SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe*, int addr, int N); SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N); |
| ︙ | ︙ | |||
9280 9281 9282 9283 9284 9285 9286 | ** Schema objects are automatically deallocated when the last Btree that ** references them is destroyed. The TEMP Schema is manually freed by ** sqlite3_close(). * ** A thread must be holding a mutex on the corresponding Btree in order ** to access Schema content. This implies that the thread must also be ** holding a mutex on the sqlite3 connection pointer that owns the Btree. | | | 9281 9282 9283 9284 9285 9286 9287 9288 9289 9290 9291 9292 9293 9294 9295 |
** Schema objects are automatically deallocated when the last Btree that
** references them is destroyed. The TEMP Schema is manually freed by
** sqlite3_close().
*
** A thread must be holding a mutex on the corresponding Btree in order
** to access Schema content. This implies that the thread must also be
** holding a mutex on the sqlite3 connection pointer that owns the Btree.
** For a TEMP Schema, only the connection mutex is required.
*/
struct Schema {
int schema_cookie; /* Database schema version number for this file */
int iGeneration; /* Generation counter. Incremented with each change */
Hash tblHash; /* All tables indexed by name */
Hash idxHash; /* All (named) indices indexed by name */
Hash trigHash; /* All triggers indexed by name */
|
| ︙ | ︙ | |||
11477 11478 11479 11480 11481 11482 11483 | SQLITE_PRIVATE int sqlite3FixExprList(DbFixer*, ExprList*); SQLITE_PRIVATE int sqlite3FixTriggerStep(DbFixer*, TriggerStep*); SQLITE_PRIVATE int sqlite3AtoF(const char *z, double*, int, u8); SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*); SQLITE_PRIVATE int sqlite3Atoi(const char*); SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar); SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte); | | | 11478 11479 11480 11481 11482 11483 11484 11485 11486 11487 11488 11489 11490 11491 11492 | SQLITE_PRIVATE int sqlite3FixExprList(DbFixer*, ExprList*); SQLITE_PRIVATE int sqlite3FixTriggerStep(DbFixer*, TriggerStep*); SQLITE_PRIVATE int sqlite3AtoF(const char *z, double*, int, u8); SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*); SQLITE_PRIVATE int sqlite3Atoi(const char*); SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar); SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte); SQLITE_PRIVATE u32 sqlite3Utf8Read(const u8*, const u8**); /* ** Routines to read and write variable-length integers. These used to ** be defined locally, but now we use the varint routines in the util.c ** file. Code should use the MACRO forms below, as the Varint32 versions ** are coded to assume the single byte case is already handled (which ** the MACRO form does). |
| ︙ | ︙ | |||
20084 20085 20086 20087 20088 20089 20090 |
while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \
c = (c<<6) + (0x3f & *(zIn++)); \
} \
if( c<0x80 \
|| (c&0xFFFFF800)==0xD800 \
|| (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \
}
| | | 20085 20086 20087 20088 20089 20090 20091 20092 20093 20094 20095 20096 20097 20098 20099 |
while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \
c = (c<<6) + (0x3f & *(zIn++)); \
} \
if( c<0x80 \
|| (c&0xFFFFF800)==0xD800 \
|| (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \
}
SQLITE_PRIVATE u32 sqlite3Utf8Read(
const unsigned char *zIn, /* First byte of UTF-8 character */
const unsigned char **pzNext /* Write first byte past UTF-8 char here */
){
unsigned int c;
/* Same as READ_UTF8() above but without the zTerm parameter.
** For this routine, we assume the UTF8 string is always zero-terminated.
|
| ︙ | ︙ | |||
35803 35804 35805 35806 35807 35808 35809 |
pCache = (PCache1 *)sqlite3_malloc(sz);
if( pCache ){
memset(pCache, 0, sz);
if( separateCache ){
pGroup = (PGroup*)&pCache[1];
pGroup->mxPinned = 10;
}else{
| | | 35804 35805 35806 35807 35808 35809 35810 35811 35812 35813 35814 35815 35816 35817 35818 |
pCache = (PCache1 *)sqlite3_malloc(sz);
if( pCache ){
memset(pCache, 0, sz);
if( separateCache ){
pGroup = (PGroup*)&pCache[1];
pGroup->mxPinned = 10;
}else{
pGroup = &pcache1.grp;
}
pCache->pGroup = pGroup;
pCache->szPage = szPage;
pCache->bPurgeable = (bPurgeable ? 1 : 0);
if( bPurgeable ){
pCache->nMin = 10;
pcache1EnterMutex(pGroup);
|
| ︙ | ︙ | |||
48322 48323 48324 48325 48326 48327 48328 48329 48330 48331 48332 48333 48334 48335 |
** the page, 1 means the second cell, and so forth) return a pointer
** to the cell content.
**
** This routine works only for pages that do not contain overflow cells.
*/
#define findCell(P,I) \
((P)->aData + ((P)->maskPage & get2byte(&(P)->aData[(P)->cellOffset+2*(I)])))
/*
** This a more complex version of findCell() that works for
** pages that do contain overflow cells.
*/
static u8 *findOverflowCell(MemPage *pPage, int iCell){
int i;
| > > | 48323 48324 48325 48326 48327 48328 48329 48330 48331 48332 48333 48334 48335 48336 48337 48338 |
** the page, 1 means the second cell, and so forth) return a pointer
** to the cell content.
**
** This routine works only for pages that do not contain overflow cells.
*/
#define findCell(P,I) \
((P)->aData + ((P)->maskPage & get2byte(&(P)->aData[(P)->cellOffset+2*(I)])))
#define findCellv2(D,M,O,I) (D+(M&get2byte(D+(O+2*(I)))))
/*
** This a more complex version of findCell() that works for
** pages that do contain overflow cells.
*/
static u8 *findOverflowCell(MemPage *pPage, int iCell){
int i;
|
| ︙ | ︙ | |||
51916 51917 51918 51919 51920 51921 51922 |
if( pCur->eState==CURSOR_INVALID ){
*pRes = -1;
assert( pCur->apPage[pCur->iPage]->nCell==0 );
return SQLITE_OK;
}
assert( pCur->apPage[0]->intKey || pIdxKey );
for(;;){
| | | | < > | 51919 51920 51921 51922 51923 51924 51925 51926 51927 51928 51929 51930 51931 51932 51933 51934 51935 51936 51937 51938 51939 51940 51941 51942 51943 51944 51945 51946 51947 51948 51949 51950 51951 51952 51953 51954 51955 51956 |
if( pCur->eState==CURSOR_INVALID ){
*pRes = -1;
assert( pCur->apPage[pCur->iPage]->nCell==0 );
return SQLITE_OK;
}
assert( pCur->apPage[0]->intKey || pIdxKey );
for(;;){
int lwr, upr, idx;
Pgno chldPg;
MemPage *pPage = pCur->apPage[pCur->iPage];
int c;
/* pPage->nCell must be greater than zero. If this is the root-page
** the cursor would have been INVALID above and this for(;;) loop
** not run. If this is not the root-page, then the moveToChild() routine
** would have already detected db corruption. Similarly, pPage must
** be the right kind (index or table) of b-tree page. Otherwise
** a moveToChild() or moveToRoot() call would have detected corruption. */
assert( pPage->nCell>0 );
assert( pPage->intKey==(pIdxKey==0) );
lwr = 0;
upr = pPage->nCell-1;
if( biasRight ){
pCur->aiIdx[pCur->iPage] = (u16)(idx = upr);
}else{
pCur->aiIdx[pCur->iPage] = (u16)(idx = (upr+lwr)/2);
}
for(;;){
u8 *pCell; /* Pointer to current cell in pPage */
assert( idx==pCur->aiIdx[pCur->iPage] );
pCur->info.nSize = 0;
pCell = findCell(pPage, idx) + pPage->childPtrSize;
if( pPage->intKey ){
i64 nCellKey;
if( pPage->hasData ){
u32 dummy;
pCell += getVarint32(pCell, dummy);
|
| ︙ | ︙ | |||
52022 52023 52024 52025 52026 52027 52028 |
lwr = idx+1;
}else{
upr = idx-1;
}
if( lwr>upr ){
break;
}
| | | 52025 52026 52027 52028 52029 52030 52031 52032 52033 52034 52035 52036 52037 52038 52039 |
lwr = idx+1;
}else{
upr = idx-1;
}
if( lwr>upr ){
break;
}
pCur->aiIdx[pCur->iPage] = (u16)(idx = (lwr+upr)/2);
}
assert( lwr==upr+1 );
assert( pPage->isInit );
if( pPage->leaf ){
chldPg = 0;
}else if( lwr>=pPage->nCell ){
chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]);
|
| ︙ | ︙ | |||
52884 52885 52886 52887 52888 52889 52890 52891 |
}
rc = freeSpace(pPage, pc, sz);
if( rc ){
*pRC = rc;
return;
}
endPtr = &data[pPage->cellOffset + 2*pPage->nCell - 2];
while( ptr<endPtr ){
| > | < | 52887 52888 52889 52890 52891 52892 52893 52894 52895 52896 52897 52898 52899 52900 52901 52902 52903 |
}
rc = freeSpace(pPage, pc, sz);
if( rc ){
*pRC = rc;
return;
}
endPtr = &data[pPage->cellOffset + 2*pPage->nCell - 2];
assert( (SQLITE_PTR_TO_INT(ptr)&1)==0 ); /* ptr is always 2-byte aligned */
while( ptr<endPtr ){
*(u16*)ptr = *(u16*)&ptr[2];
ptr += 2;
}
pPage->nCell--;
put2byte(&data[hdr+3], pPage->nCell);
pPage->nFree += 2;
}
|
| ︙ | ︙ | |||
52927 52928 52929 52930 52931 52932 52933 52934 52935 52936 52937 52938 52939 52940 | int idx = 0; /* Where to write new cell content in data[] */ int j; /* Loop counter */ int end; /* First byte past the last cell pointer in data[] */ int ins; /* Index in data[] where new cell pointer is inserted */ int cellOffset; /* Address of first cell pointer in data[] */ u8 *data; /* The content of the whole page */ u8 *ptr; /* Used for moving information around in data[] */ int nSkip = (iChild ? 4 : 0); if( *pRC ) return; assert( i>=0 && i<=pPage->nCell+pPage->nOverflow ); assert( pPage->nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=10921 ); | > | 52930 52931 52932 52933 52934 52935 52936 52937 52938 52939 52940 52941 52942 52943 52944 | int idx = 0; /* Where to write new cell content in data[] */ int j; /* Loop counter */ int end; /* First byte past the last cell pointer in data[] */ int ins; /* Index in data[] where new cell pointer is inserted */ int cellOffset; /* Address of first cell pointer in data[] */ u8 *data; /* The content of the whole page */ u8 *ptr; /* Used for moving information around in data[] */ u8 *endPtr; /* End of the loop */ int nSkip = (iChild ? 4 : 0); if( *pRC ) return; assert( i>=0 && i<=pPage->nCell+pPage->nOverflow ); assert( pPage->nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=10921 ); |
| ︙ | ︙ | |||
52977 52978 52979 52980 52981 52982 52983 |
assert( idx+sz <= (int)pPage->pBt->usableSize );
pPage->nCell++;
pPage->nFree -= (u16)(2 + sz);
memcpy(&data[idx+nSkip], pCell+nSkip, sz-nSkip);
if( iChild ){
put4byte(&data[idx], iChild);
}
| | > > | > | | 52981 52982 52983 52984 52985 52986 52987 52988 52989 52990 52991 52992 52993 52994 52995 52996 52997 52998 52999 53000 |
assert( idx+sz <= (int)pPage->pBt->usableSize );
pPage->nCell++;
pPage->nFree -= (u16)(2 + sz);
memcpy(&data[idx+nSkip], pCell+nSkip, sz-nSkip);
if( iChild ){
put4byte(&data[idx], iChild);
}
ptr = &data[end];
endPtr = &data[ins];
assert( (SQLITE_PTR_TO_INT(ptr)&1)==0 ); /* ptr is always 2-byte aligned */
while( ptr>endPtr ){
*(u16*)ptr = *(u16*)&ptr[-2];
ptr -= 2;
}
put2byte(&data[ins], idx);
put2byte(&data[pPage->hdrOffset+3], pPage->nCell);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pPage->pBt->autoVacuum ){
/* The cell may contain a pointer to an overflow page. If so, write
** the entry for the overflow page into the pointer map.
|
| ︙ | ︙ | |||
53024 53025 53026 53027 53028 53029 53030 53031 |
/* Check that the page has just been zeroed by zeroPage() */
assert( pPage->nCell==0 );
assert( get2byteNotZero(&data[hdr+5])==nUsable );
pCellptr = &data[pPage->cellOffset + nCell*2];
cellbody = nUsable;
for(i=nCell-1; i>=0; i--){
pCellptr -= 2;
| > | | | 53031 53032 53033 53034 53035 53036 53037 53038 53039 53040 53041 53042 53043 53044 53045 53046 53047 53048 53049 |
/* Check that the page has just been zeroed by zeroPage() */
assert( pPage->nCell==0 );
assert( get2byteNotZero(&data[hdr+5])==nUsable );
pCellptr = &data[pPage->cellOffset + nCell*2];
cellbody = nUsable;
for(i=nCell-1; i>=0; i--){
u16 sz = aSize[i];
pCellptr -= 2;
cellbody -= sz;
put2byte(pCellptr, cellbody);
memcpy(&data[cellbody], apCell[i], sz);
}
put2byte(&data[hdr+3], nCell);
put2byte(&data[hdr+5], cellbody);
pPage->nFree -= (nCell*2 + nUsable - cellbody);
pPage->nCell = (u16)nCell;
}
|
| ︙ | ︙ | |||
53481 53482 53483 53484 53485 53486 53487 |
** process of being overwritten. */
MemPage *pOld = apCopy[i] = (MemPage*)&aSpace1[pBt->pageSize + k*i];
memcpy(pOld, apOld[i], sizeof(MemPage));
pOld->aData = (void*)&pOld[1];
memcpy(pOld->aData, apOld[i]->aData, pBt->pageSize);
limit = pOld->nCell+pOld->nOverflow;
| > | | | | | | > > > > > > > > > > > | 53489 53490 53491 53492 53493 53494 53495 53496 53497 53498 53499 53500 53501 53502 53503 53504 53505 53506 53507 53508 53509 53510 53511 53512 53513 53514 53515 53516 53517 53518 53519 53520 |
** process of being overwritten. */
MemPage *pOld = apCopy[i] = (MemPage*)&aSpace1[pBt->pageSize + k*i];
memcpy(pOld, apOld[i], sizeof(MemPage));
pOld->aData = (void*)&pOld[1];
memcpy(pOld->aData, apOld[i]->aData, pBt->pageSize);
limit = pOld->nCell+pOld->nOverflow;
if( pOld->nOverflow>0 ){
for(j=0; j<limit; j++){
assert( nCell<nMaxCells );
apCell[nCell] = findOverflowCell(pOld, j);
szCell[nCell] = cellSizePtr(pOld, apCell[nCell]);
nCell++;
}
}else{
u8 *aData = pOld->aData;
u16 maskPage = pOld->maskPage;
u16 cellOffset = pOld->cellOffset;
for(j=0; j<limit; j++){
assert( nCell<nMaxCells );
apCell[nCell] = findCellv2(aData, maskPage, cellOffset, j);
szCell[nCell] = cellSizePtr(pOld, apCell[nCell]);
nCell++;
}
}
if( i<nOld-1 && !leafData){
u16 sz = (u16)szNew[i];
u8 *pTemp;
assert( nCell<nMaxCells );
szCell[nCell] = sz;
pTemp = &aSpace1[iSpace1];
iSpace1 += sz;
|
| ︙ | ︙ | |||
57635 57636 57637 57638 57639 57640 57641 | pOp->opcode = (u8)op; pOp->p5 = 0; pOp->p1 = p1; pOp->p2 = p2; pOp->p3 = p3; pOp->p4.p = 0; pOp->p4type = P4_NOTUSED; | < < < < < < < | 57655 57656 57657 57658 57659 57660 57661 57662 57663 57664 57665 57666 57667 57668 | pOp->opcode = (u8)op; pOp->p5 = 0; pOp->p1 = p1; pOp->p2 = p2; pOp->p3 = p3; pOp->p4.p = 0; pOp->p4type = P4_NOTUSED; #ifdef SQLITE_DEBUG pOp->zComment = 0; if( sqlite3VdbeAddopTrace ) sqlite3VdbePrintOp(0, i, &p->aOp[i]); #endif #ifdef VDBE_PROFILE pOp->cycles = 0; pOp->cnt = 0; |
| ︙ | ︙ | |||
57679 57680 57681 57682 57683 57684 57685 57686 57687 57688 57689 57690 57691 57692 |
const char *zP4, /* The P4 operand */
int p4type /* P4 operand type */
){
int addr = sqlite3VdbeAddOp3(p, op, p1, p2, p3);
sqlite3VdbeChangeP4(p, addr, zP4, p4type);
return addr;
}
/*
** Add an opcode that includes the p4 value as an integer.
*/
SQLITE_PRIVATE int sqlite3VdbeAddOp4Int(
Vdbe *p, /* Add the opcode to this VM */
int op, /* The new opcode */
| > > > > > > > > > > > > > > | 57692 57693 57694 57695 57696 57697 57698 57699 57700 57701 57702 57703 57704 57705 57706 57707 57708 57709 57710 57711 57712 57713 57714 57715 57716 57717 57718 57719 |
const char *zP4, /* The P4 operand */
int p4type /* P4 operand type */
){
int addr = sqlite3VdbeAddOp3(p, op, p1, p2, p3);
sqlite3VdbeChangeP4(p, addr, zP4, p4type);
return addr;
}
/*
** Add an OP_ParseSchema opcode. This routine is broken out from
** sqlite3VdbeAddOp4() since it needs to also local all btrees.
**
** The zWhere string must have been obtained from sqlite3_malloc().
** This routine will take ownership of the allocated memory.
*/
SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere){
int j;
int addr = sqlite3VdbeAddOp3(p, OP_ParseSchema, iDb, 0, 0);
sqlite3VdbeChangeP4(p, addr, zWhere, P4_DYNAMIC);
for(j=0; j<p->db->nDb; j++) sqlite3VdbeUsesBtree(p, j);
}
/*
** Add an opcode that includes the p4 value as an integer.
*/
SQLITE_PRIVATE int sqlite3VdbeAddOp4Int(
Vdbe *p, /* Add the opcode to this VM */
int op, /* The new opcode */
|
| ︙ | ︙ | |||
64081 64082 64083 64084 64085 64086 64087 64088 64089 64090 64091 64092 64093 64094 64095 64096 64097 |
assert( pOp[-1].p4type==P4_COLLSEQ );
assert( pOp[-1].opcode==OP_CollSeq );
u.ag.ctx.pColl = pOp[-1].p4.pColl;
}
db->lastRowid = lastRowid;
(*u.ag.ctx.pFunc->xFunc)(&u.ag.ctx, u.ag.n, u.ag.apVal); /* IMP: R-24505-23230 */
lastRowid = db->lastRowid;
if( db->mallocFailed ){
/* Even though a malloc() has failed, the implementation of the
** user function may have called an sqlite3_result_XXX() function
** to return a value. The following call releases any resources
** associated with such a value.
*/
sqlite3VdbeMemRelease(&u.ag.ctx.s);
goto no_mem;
}
| > > > > > > > > > > < < < < < < < < < | 64108 64109 64110 64111 64112 64113 64114 64115 64116 64117 64118 64119 64120 64121 64122 64123 64124 64125 64126 64127 64128 64129 64130 64131 64132 64133 64134 64135 64136 64137 64138 64139 64140 64141 |
assert( pOp[-1].p4type==P4_COLLSEQ );
assert( pOp[-1].opcode==OP_CollSeq );
u.ag.ctx.pColl = pOp[-1].p4.pColl;
}
db->lastRowid = lastRowid;
(*u.ag.ctx.pFunc->xFunc)(&u.ag.ctx, u.ag.n, u.ag.apVal); /* IMP: R-24505-23230 */
lastRowid = db->lastRowid;
/* If any auxiliary data functions have been called by this user function,
** immediately call the destructor for any non-static values.
*/
if( u.ag.ctx.pVdbeFunc ){
sqlite3VdbeDeleteAuxData(u.ag.ctx.pVdbeFunc, pOp->p1);
pOp->p4.pVdbeFunc = u.ag.ctx.pVdbeFunc;
pOp->p4type = P4_VDBEFUNC;
}
if( db->mallocFailed ){
/* Even though a malloc() has failed, the implementation of the
** user function may have called an sqlite3_result_XXX() function
** to return a value. The following call releases any resources
** associated with such a value.
*/
sqlite3VdbeMemRelease(&u.ag.ctx.s);
goto no_mem;
}
/* If the function returned an error, throw an exception */
if( u.ag.ctx.isError ){
sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&u.ag.ctx.s));
rc = u.ag.ctx.isError;
}
/* Copy the result of the function into register P3 */
|
| ︙ | ︙ | |||
75253 75254 75255 75256 75257 75258 75259 | /* Drop the table and index from the internal schema. */ sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab->zName, 0); /* Reload the table, index and permanent trigger schemas. */ zWhere = sqlite3MPrintf(pParse->db, "tbl_name=%Q", zName); if( !zWhere ) return; | | | | 75281 75282 75283 75284 75285 75286 75287 75288 75289 75290 75291 75292 75293 75294 75295 75296 75297 75298 75299 75300 75301 75302 |
/* Drop the table and index from the internal schema. */
sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab->zName, 0);
/* Reload the table, index and permanent trigger schemas. */
zWhere = sqlite3MPrintf(pParse->db, "tbl_name=%Q", zName);
if( !zWhere ) return;
sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere);
#ifndef SQLITE_OMIT_TRIGGER
/* Now, if the table is not stored in the temp database, reload any temp
** triggers. Don't use IN(...) in case SQLITE_OMIT_SUBQUERY is defined.
*/
if( (zWhere=whereTempTriggers(pParse, pTab))!=0 ){
sqlite3VdbeAddParseSchemaOp(v, 1, zWhere);
}
#endif
}
/*
** Parameter zName is the name of a table that is about to be altered
** (either with ALTER TABLE ... RENAME TO or ALTER TABLE ... ADD COLUMN).
|
| ︙ | ︙ | |||
78873 78874 78875 78876 78877 78878 78879 |
pDb->zName
);
}
}
#endif
/* Reparse everything to update our internal data structures */
| | | | 78901 78902 78903 78904 78905 78906 78907 78908 78909 78910 78911 78912 78913 78914 78915 78916 |
pDb->zName
);
}
}
#endif
/* Reparse everything to update our internal data structures */
sqlite3VdbeAddParseSchemaOp(v, iDb,
sqlite3MPrintf(db, "tbl_name='%q'", p->zName));
}
/* Add the table to the in-memory representation of the database.
*/
if( db->init.busy ){
Table *pOld;
|
| ︙ | ︙ | |||
80071 80072 80073 80074 80075 80076 80077 |
/* Fill the index with data and reparse the schema. Code an OP_Expire
** to invalidate all pre-compiled statements.
*/
if( pTblName ){
sqlite3RefillIndex(pParse, pIndex, iMem);
sqlite3ChangeCookie(pParse, iDb);
| | | < | 80099 80100 80101 80102 80103 80104 80105 80106 80107 80108 80109 80110 80111 80112 80113 80114 |
/* Fill the index with data and reparse the schema. Code an OP_Expire
** to invalidate all pre-compiled statements.
*/
if( pTblName ){
sqlite3RefillIndex(pParse, pIndex, iMem);
sqlite3ChangeCookie(pParse, iDb);
sqlite3VdbeAddParseSchemaOp(v, iDb,
sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName));
sqlite3VdbeAddOp1(v, OP_Expire, 0);
}
}
/* When adding an index to the list of indices for a table, make
** sure all indices labeled OE_Replace come after all those labeled
** OE_Ignore. This is necessary for the correct constraint check
|
| ︙ | ︙ | |||
82619 82620 82621 82622 82623 82624 82625 | /* ** For LIKE and GLOB matching on EBCDIC machines, assume that every ** character is exactly one byte in size. Also, all characters are ** able to participate in upper-case-to-lower-case mappings in EBCDIC ** whereas only characters less than 0x80 do in ASCII. */ #if defined(SQLITE_EBCDIC) | | | | | 82646 82647 82648 82649 82650 82651 82652 82653 82654 82655 82656 82657 82658 82659 82660 82661 82662 82663 |
/*
** For LIKE and GLOB matching on EBCDIC machines, assume that every
** character is exactly one byte in size. Also, all characters are
** able to participate in upper-case-to-lower-case mappings in EBCDIC
** whereas only characters less than 0x80 do in ASCII.
*/
#if defined(SQLITE_EBCDIC)
# define sqlite3Utf8Read(A,C) (*(A++))
# define GlogUpperToLower(A) A = sqlite3UpperToLower[A]
#else
# define GlogUpperToLower(A) if( !((A)&~0x7f) ){ A = sqlite3UpperToLower[A]; }
#endif
static const struct compareInfo globInfo = { '*', '?', '[', 0 };
/* The correct SQL-92 behavior is for the LIKE operator to ignore
** case. Thus 'a' LIKE 'A' would be true. */
static const struct compareInfo likeInfoNorm = { '%', '_', 0, 1 };
/* If SQLITE_CASE_SENSITIVE_LIKE is defined, then the LIKE operator
|
| ︙ | ︙ | |||
82665 82666 82667 82668 82669 82670 82671 | ** ** abc[*]xyz Matches "abc*xyz" only */ static int patternCompare( const u8 *zPattern, /* The glob pattern */ const u8 *zString, /* The string to compare against the glob */ const struct compareInfo *pInfo, /* Information about how to do the compare */ | | | | 82692 82693 82694 82695 82696 82697 82698 82699 82700 82701 82702 82703 82704 82705 82706 82707 82708 |
**
** abc[*]xyz Matches "abc*xyz" only
*/
static int patternCompare(
const u8 *zPattern, /* The glob pattern */
const u8 *zString, /* The string to compare against the glob */
const struct compareInfo *pInfo, /* Information about how to do the compare */
u32 esc /* The escape character */
){
u32 c, c2;
int invert;
int seen;
u8 matchOne = pInfo->matchOne;
u8 matchAll = pInfo->matchAll;
u8 matchSet = pInfo->matchSet;
u8 noCase = pInfo->noCase;
int prevEscape = 0; /* True if the previous character was 'escape' */
|
| ︙ | ︙ | |||
82721 82722 82723 82724 82725 82726 82727 |
}
return 0;
}else if( !prevEscape && c==matchOne ){
if( sqlite3Utf8Read(zString, &zString)==0 ){
return 0;
}
}else if( c==matchSet ){
| | | 82748 82749 82750 82751 82752 82753 82754 82755 82756 82757 82758 82759 82760 82761 82762 |
}
return 0;
}else if( !prevEscape && c==matchOne ){
if( sqlite3Utf8Read(zString, &zString)==0 ){
return 0;
}
}else if( c==matchSet ){
u32 prior_c = 0;
assert( esc==0 ); /* This only occurs for GLOB, not LIKE */
seen = 0;
invert = 0;
c = sqlite3Utf8Read(zString, &zString);
if( c==0 ) return 0;
c2 = sqlite3Utf8Read(zPattern, &zPattern);
if( c2=='^' ){
|
| ︙ | ︙ | |||
82797 82798 82799 82800 82801 82802 82803 |
*/
static void likeFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const unsigned char *zA, *zB;
| | | 82824 82825 82826 82827 82828 82829 82830 82831 82832 82833 82834 82835 82836 82837 82838 |
*/
static void likeFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const unsigned char *zA, *zB;
u32 escape = 0;
int nPat;
sqlite3 *db = sqlite3_context_db_handle(context);
zB = sqlite3_value_text(argv[0]);
zA = sqlite3_value_text(argv[1]);
/* Limit the length of the LIKE or GLOB pattern to avoid problems
|
| ︙ | ︙ | |||
84108 84109 84110 84111 84112 84113 84114 |
for(i=0; i<nCol; i++){
sqlite3VdbeAddOp2(v, OP_Copy, aiCol[i]+1+regData, regTemp+i);
}
/* If the parent table is the same as the child table, and we are about
** to increment the constraint-counter (i.e. this is an INSERT operation),
** then check if the row being inserted matches itself. If so, do not
| | > > > > > > > > > > > > | 84135 84136 84137 84138 84139 84140 84141 84142 84143 84144 84145 84146 84147 84148 84149 84150 84151 84152 84153 84154 84155 84156 84157 84158 84159 84160 84161 84162 84163 84164 84165 84166 84167 |
for(i=0; i<nCol; i++){
sqlite3VdbeAddOp2(v, OP_Copy, aiCol[i]+1+regData, regTemp+i);
}
/* If the parent table is the same as the child table, and we are about
** to increment the constraint-counter (i.e. this is an INSERT operation),
** then check if the row being inserted matches itself. If so, do not
** increment the constraint-counter.
**
** If any of the parent-key values are NULL, then the row cannot match
** itself. So set JUMPIFNULL to make sure we do the OP_Found if any
** of the parent-key values are NULL (at this point it is known that
** none of the child key values are).
*/
if( pTab==pFKey->pFrom && nIncr==1 ){
int iJump = sqlite3VdbeCurrentAddr(v) + nCol + 1;
for(i=0; i<nCol; i++){
int iChild = aiCol[i]+1+regData;
int iParent = pIdx->aiColumn[i]+1+regData;
assert( aiCol[i]!=pTab->iPKey );
if( pIdx->aiColumn[i]==pTab->iPKey ){
/* The parent key is a composite key that includes the IPK column */
iParent = regData;
}
sqlite3VdbeAddOp3(v, OP_Ne, iChild, iJump, iParent);
sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL);
}
sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk);
}
sqlite3VdbeAddOp3(v, OP_MakeRecord, regTemp, nCol, regRec);
sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v,pIdx), P4_TRANSIENT);
sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0);
|
| ︙ | ︙ | |||
95334 95335 95336 95337 95338 95339 95340 |
z = sqlite3DbStrNDup(db, (char*)pAll->z, pAll->n);
sqlite3NestedParse(pParse,
"INSERT INTO %Q.%s VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')",
db->aDb[iDb].zName, SCHEMA_TABLE(iDb), zName,
pTrig->table, z);
sqlite3DbFree(db, z);
sqlite3ChangeCookie(pParse, iDb);
| | | < | 95373 95374 95375 95376 95377 95378 95379 95380 95381 95382 95383 95384 95385 95386 95387 95388 |
z = sqlite3DbStrNDup(db, (char*)pAll->z, pAll->n);
sqlite3NestedParse(pParse,
"INSERT INTO %Q.%s VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')",
db->aDb[iDb].zName, SCHEMA_TABLE(iDb), zName,
pTrig->table, z);
sqlite3DbFree(db, z);
sqlite3ChangeCookie(pParse, iDb);
sqlite3VdbeAddParseSchemaOp(v, iDb,
sqlite3MPrintf(db, "type='trigger' AND name='%q'", zName));
}
if( db->init.busy ){
Trigger *pLink = pTrig;
Hash *pHash = &db->aDb[iDb].pSchema->trigHash;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
pTrig = sqlite3HashInsert(pHash, zName, sqlite3Strlen30(zName), pTrig);
|
| ︙ | ︙ | |||
96390 96391 96392 96393 96394 96395 96396 |
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){}
if( nIdx>0 ){
aRegIdx = sqlite3DbMallocRaw(db, sizeof(Index*) * nIdx );
if( aRegIdx==0 ) goto update_cleanup;
}
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
int reg;
| | | 96428 96429 96430 96431 96432 96433 96434 96435 96436 96437 96438 96439 96440 96441 96442 |
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){}
if( nIdx>0 ){
aRegIdx = sqlite3DbMallocRaw(db, sizeof(Index*) * nIdx );
if( aRegIdx==0 ) goto update_cleanup;
}
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
int reg;
if( hasFK || chngRowid ){
reg = ++pParse->nMem;
}else{
reg = 0;
for(i=0; i<pIdx->nColumn; i++){
if( aXRef[pIdx->aiColumn[i]]>=0 ){
reg = ++pParse->nMem;
break;
|
| ︙ | ︙ | |||
97545 97546 97547 97548 97549 97550 97551 |
);
sqlite3DbFree(db, zStmt);
v = sqlite3GetVdbe(pParse);
sqlite3ChangeCookie(pParse, iDb);
sqlite3VdbeAddOp2(v, OP_Expire, 0, 0);
zWhere = sqlite3MPrintf(db, "name='%q' AND type='table'", pTab->zName);
| | | 97583 97584 97585 97586 97587 97588 97589 97590 97591 97592 97593 97594 97595 97596 97597 |
);
sqlite3DbFree(db, zStmt);
v = sqlite3GetVdbe(pParse);
sqlite3ChangeCookie(pParse, iDb);
sqlite3VdbeAddOp2(v, OP_Expire, 0, 0);
zWhere = sqlite3MPrintf(db, "name='%q' AND type='table'", pTab->zName);
sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere);
sqlite3VdbeAddOp4(v, OP_VCreate, iDb, 0, 0,
pTab->zName, sqlite3Strlen30(pTab->zName) + 1);
}
/* If we are rereading the sqlite_master table create the in-memory
** record of the table. The xConnect() method is not called until
** the first time the virtual table is used in an SQL statement. This
|
| ︙ | ︙ | |||
107272 107273 107274 107275 107276 107277 107278 |
return i;
}
#ifndef SQLITE_OMIT_BLOB_LITERAL
case 'x': case 'X': {
testcase( z[0]=='x' ); testcase( z[0]=='X' );
if( z[1]=='\'' ){
*tokenType = TK_BLOB;
| < | > | > | < < | | 107310 107311 107312 107313 107314 107315 107316 107317 107318 107319 107320 107321 107322 107323 107324 107325 107326 107327 107328 107329 |
return i;
}
#ifndef SQLITE_OMIT_BLOB_LITERAL
case 'x': case 'X': {
testcase( z[0]=='x' ); testcase( z[0]=='X' );
if( z[1]=='\'' ){
*tokenType = TK_BLOB;
for(i=2; sqlite3Isxdigit(z[i]); i++){}
if( z[i]!='\'' || i%2 ){
*tokenType = TK_ILLEGAL;
while( z[i] && z[i]!='\'' ){ i++; }
}
if( z[i] ) i++;
return i;
}
/* Otherwise fall through to the next case */
}
#endif
default: {
if( !IdChar(*z) ){
|
| ︙ | ︙ | |||
111708 111709 111710 111711 111712 111713 111714 111715 111716 111717 111718 111719 111720 111721 111722 111723 111724 111725 111726 111727 | /* ** Macro to return the number of elements in an array. SQLite has a ** similar macro called ArraySize(). Use a different name to avoid ** a collision when building an amalgamation with built-in FTS3. */ #define SizeofArray(X) ((int)(sizeof(X)/sizeof(X[0]))) /* ** Maximum length of a varint encoded integer. The varint format is different ** from that used by SQLite, so the maximum length is 10, not 9. */ #define FTS3_VARINT_MAX 10 /* ** The testcase() macro is only used by the amalgamation. If undefined, ** make it a no-op. */ #ifndef testcase # define testcase(X) #endif | > > > > > > > > > > > > > > > > > > > > > > > | 111745 111746 111747 111748 111749 111750 111751 111752 111753 111754 111755 111756 111757 111758 111759 111760 111761 111762 111763 111764 111765 111766 111767 111768 111769 111770 111771 111772 111773 111774 111775 111776 111777 111778 111779 111780 111781 111782 111783 111784 111785 111786 111787 | /* ** Macro to return the number of elements in an array. SQLite has a ** similar macro called ArraySize(). Use a different name to avoid ** a collision when building an amalgamation with built-in FTS3. */ #define SizeofArray(X) ((int)(sizeof(X)/sizeof(X[0]))) #ifndef MIN # define MIN(x,y) ((x)<(y)?(x):(y)) #endif /* ** Maximum length of a varint encoded integer. The varint format is different ** from that used by SQLite, so the maximum length is 10, not 9. */ #define FTS3_VARINT_MAX 10 /* ** FTS4 virtual tables may maintain multiple indexes - one index of all terms ** in the document set and zero or more prefix indexes. All indexes are stored ** as one or more b+-trees in the %_segments and %_segdir tables. ** ** It is possible to determine which index a b+-tree belongs to based on the ** value stored in the "%_segdir.level" column. Given this value L, the index ** that the b+-tree belongs to is (L<<10). In other words, all b+-trees with ** level values between 0 and 1023 (inclusive) belong to index 0, all levels ** between 1024 and 2047 to index 1, and so on. ** ** It is considered impossible for an index to use more than 1024 levels. In ** theory though this may happen, but only after at least ** (FTS3_MERGE_COUNT^1024) separate flushes of the pending-terms tables. */ #define FTS3_SEGDIR_MAXLEVEL 1024 #define FTS3_SEGDIR_MAXLEVEL_STR "1024" /* ** The testcase() macro is only used by the amalgamation. If undefined, ** make it a no-op. */ #ifndef testcase # define testcase(X) #endif |
| ︙ | ︙ | |||
111785 111786 111787 111788 111789 111790 111791 111792 111793 111794 | typedef struct Fts3Table Fts3Table; typedef struct Fts3Cursor Fts3Cursor; typedef struct Fts3Expr Fts3Expr; typedef struct Fts3Phrase Fts3Phrase; typedef struct Fts3PhraseToken Fts3PhraseToken; typedef struct Fts3SegFilter Fts3SegFilter; typedef struct Fts3DeferredToken Fts3DeferredToken; typedef struct Fts3SegReader Fts3SegReader; | > | | > > > | > > > > > > > > > > | | | < | 111845 111846 111847 111848 111849 111850 111851 111852 111853 111854 111855 111856 111857 111858 111859 111860 111861 111862 111863 111864 111865 111866 111867 111868 111869 111870 111871 111872 111873 111874 111875 111876 111877 111878 111879 111880 111881 111882 111883 111884 111885 111886 111887 111888 111889 111890 111891 111892 111893 111894 111895 111896 111897 111898 111899 111900 111901 111902 111903 111904 111905 111906 111907 111908 111909 111910 111911 111912 111913 111914 111915 111916 111917 111918 |
typedef struct Fts3Table Fts3Table;
typedef struct Fts3Cursor Fts3Cursor;
typedef struct Fts3Expr Fts3Expr;
typedef struct Fts3Phrase Fts3Phrase;
typedef struct Fts3PhraseToken Fts3PhraseToken;
typedef struct Fts3Doclist Fts3Doclist;
typedef struct Fts3SegFilter Fts3SegFilter;
typedef struct Fts3DeferredToken Fts3DeferredToken;
typedef struct Fts3SegReader Fts3SegReader;
typedef struct Fts3MultiSegReader Fts3MultiSegReader;
/*
** A connection to a fulltext index is an instance of the following
** structure. The xCreate and xConnect methods create an instance
** of this structure and xDestroy and xDisconnect free that instance.
** All other methods receive a pointer to the structure as one of their
** arguments.
*/
struct Fts3Table {
sqlite3_vtab base; /* Base class used by SQLite core */
sqlite3 *db; /* The database connection */
const char *zDb; /* logical database name */
const char *zName; /* virtual table name */
int nColumn; /* number of named columns in virtual table */
char **azColumn; /* column names. malloced */
sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */
/* Precompiled statements used by the implementation. Each of these
** statements is run and reset within a single virtual table API call.
*/
sqlite3_stmt *aStmt[27];
char *zReadExprlist;
char *zWriteExprlist;
int nNodeSize; /* Soft limit for node size */
u8 bHasStat; /* True if %_stat table exists */
u8 bHasDocsize; /* True if %_docsize table exists */
u8 bDescIdx; /* True if doclists are in reverse order */
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 hash table is used to buffer pending index updates during
** transactions. Variable nPendingData estimates the memory size of the
** pending data, including hash table overhead, but not malloc overhead.
** When nPendingData exceeds nMaxPendingData, the buffer is flushed
** automatically. Variable iPrevDocid is the docid of the most recently
** inserted record.
**
** 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.
*/
int nIndex; /* Size of aIndex[] */
struct Fts3Index {
int nPrefix; /* Prefix length (0 for main terms index) */
Fts3Hash hPending; /* Pending terms table for this index */
} *aIndex;
int nMaxPendingData; /* Max pending data before flush to disk */
int nPendingData; /* Current bytes of pending data */
sqlite_int64 iPrevDocid; /* Docid of most recently inserted document */
#if defined(SQLITE_DEBUG)
/* State variables used for validating that the transaction control
** methods of the virtual table are called at appropriate times. These
** values do not contribution to the FTS computation; they are used for
** verifying the SQLite core.
*/
|
| ︙ | ︙ | |||
111862 111863 111864 111865 111866 111867 111868 | Fts3Expr *pExpr; /* Parsed MATCH query string */ int nPhrase; /* Number of matchable phrases in query */ Fts3DeferredToken *pDeferred; /* Deferred search tokens, if any */ sqlite3_int64 iPrevId; /* Previous id read from aDoclist */ char *pNextId; /* Pointer into the body of aDoclist */ char *aDoclist; /* List of docids for full-text queries */ int nDoclist; /* Size of buffer at aDoclist */ | | > | 111935 111936 111937 111938 111939 111940 111941 111942 111943 111944 111945 111946 111947 111948 111949 111950 111951 111952 | Fts3Expr *pExpr; /* Parsed MATCH query string */ int nPhrase; /* Number of matchable phrases in query */ Fts3DeferredToken *pDeferred; /* Deferred search tokens, if any */ sqlite3_int64 iPrevId; /* Previous id read from aDoclist */ char *pNextId; /* Pointer into the body of aDoclist */ char *aDoclist; /* List of docids for full-text queries */ int nDoclist; /* Size of buffer at aDoclist */ u8 bDesc; /* True to sort in descending order */ int eEvalmode; /* An FTS3_EVAL_XX constant */ int nRowAvg; /* Average size of database rows, in pages */ int nDoc; /* Documents in table */ int isMatchinfoNeeded; /* True when aMatchinfo[] needs filling in */ u32 *aMatchinfo; /* Information about most recent match */ int nMatchinfo; /* Number of elements in aMatchinfo[] */ char *zMatchinfo; /* Matchinfo specification */ }; |
| ︙ | ︙ | |||
111895 111896 111897 111898 111899 111900 111901 111902 111903 111904 111905 111906 | ** indicating that all columns should be searched, ** then eSearch would be set to FTS3_FULLTEXT_SEARCH+4. */ #define FTS3_FULLSCAN_SEARCH 0 /* Linear scan of %_content table */ #define FTS3_DOCID_SEARCH 1 /* Lookup by rowid on %_content table */ #define FTS3_FULLTEXT_SEARCH 2 /* Full-text index search */ /* ** A "phrase" is a sequence of one or more tokens that must match in ** sequence. A single token is the base case and the most common case. ** For a sequence of tokens contained in double-quotes (i.e. "one two three") ** nToken will be the number of tokens in the string. | > > > > > > > > > > > > < < < < < < > > > > < > > > > > | > > < | | | | | | | > > > > > > > > | > > | | | < | | 111969 111970 111971 111972 111973 111974 111975 111976 111977 111978 111979 111980 111981 111982 111983 111984 111985 111986 111987 111988 111989 111990 111991 111992 111993 111994 111995 111996 111997 111998 111999 112000 112001 112002 112003 112004 112005 112006 112007 112008 112009 112010 112011 112012 112013 112014 112015 112016 112017 112018 112019 112020 112021 112022 112023 112024 112025 112026 112027 112028 112029 112030 112031 112032 112033 112034 112035 112036 112037 112038 112039 112040 112041 112042 112043 112044 112045 112046 112047 112048 112049 112050 112051 112052 112053 112054 112055 112056 112057 112058 112059 112060 112061 112062 |
** indicating that all columns should be searched,
** then eSearch would be set to FTS3_FULLTEXT_SEARCH+4.
*/
#define FTS3_FULLSCAN_SEARCH 0 /* Linear scan of %_content table */
#define FTS3_DOCID_SEARCH 1 /* Lookup by rowid on %_content table */
#define FTS3_FULLTEXT_SEARCH 2 /* Full-text index search */
struct Fts3Doclist {
char *aAll; /* Array containing doclist (or NULL) */
int nAll; /* Size of a[] in bytes */
char *pNextDocid; /* Pointer to next docid */
sqlite3_int64 iDocid; /* Current docid (if pList!=0) */
int bFreeList; /* True if pList should be sqlite3_free()d */
char *pList; /* Pointer to position list following iDocid */
int nList; /* Length of position list */
} doclist;
/*
** A "phrase" is a sequence of one or more tokens that must match in
** sequence. A single token is the base case and the most common case.
** For a sequence of tokens contained in double-quotes (i.e. "one two three")
** nToken will be the number of tokens in the string.
*/
struct Fts3PhraseToken {
char *z; /* Text of the token */
int n; /* Number of bytes in buffer z */
int isPrefix; /* True if token ends with a "*" character */
/* Variables above this point are populated when the expression is
** parsed (by code in fts3_expr.c). Below this point the variables are
** used when evaluating the expression. */
int bFulltext; /* True if full-text index was used */
Fts3DeferredToken *pDeferred; /* Deferred token object for this token */
Fts3MultiSegReader *pSegcsr; /* Segment-reader for this token */
};
struct Fts3Phrase {
/* Cache of doclist for this phrase. */
Fts3Doclist doclist;
int bIncr; /* True if doclist is loaded incrementally */
/* Variables below this point are populated by fts3_expr.c when parsing
** a MATCH expression. Everything above is part of the evaluation phase.
*/
int nToken; /* Number of tokens in the phrase */
int iColumn; /* Index of column this phrase must match */
Fts3PhraseToken aToken[1]; /* One entry for each token in the phrase */
};
/*
** A tree of these objects forms the RHS of a MATCH operator.
**
** If Fts3Expr.eType is FTSQUERY_PHRASE and isLoaded is true, then aDoclist
** points to a malloced buffer, size nDoclist bytes, containing the results
** of this phrase query in FTS3 doclist format. As usual, the initial
** "Length" field found in doclists stored on disk is omitted from this
** buffer.
**
** Variable aMI is used only for FTSQUERY_NEAR nodes to store the global
** matchinfo data. If it is not NULL, it points to an array of size nCol*3,
** where nCol is the number of columns in the queried FTS table. The array
** is populated as follows:
**
** aMI[iCol*3 + 0] = Undefined
** aMI[iCol*3 + 1] = Number of occurrences
** aMI[iCol*3 + 2] = Number of rows containing at least one instance
**
** The aMI array is allocated using sqlite3_malloc(). It should be freed
** when the expression node is.
*/
struct Fts3Expr {
int eType; /* One of the FTSQUERY_XXX values defined below */
int nNear; /* Valid if eType==FTSQUERY_NEAR */
Fts3Expr *pParent; /* pParent->pLeft==this or pParent->pRight==this */
Fts3Expr *pLeft; /* Left operand */
Fts3Expr *pRight; /* Right operand */
Fts3Phrase *pPhrase; /* Valid if eType==FTSQUERY_PHRASE */
/* The following are used by the fts3_eval.c module. */
sqlite3_int64 iDocid; /* Current docid */
u8 bEof; /* True this expression is at EOF already */
u8 bStart; /* True if iDocid is valid */
u8 bDeferred; /* True if this expression is entirely deferred */
u32 *aMI;
};
/*
** Candidate values for Fts3Query.eType. Note that the order of the first
** four values is in order of precedence when parsing expressions. For
** example, the following:
**
|
| ︙ | ︙ | |||
111978 111979 111980 111981 111982 111983 111984 | /* fts3_write.c */ SQLITE_PRIVATE int sqlite3Fts3UpdateMethod(sqlite3_vtab*,int,sqlite3_value**,sqlite3_int64*); SQLITE_PRIVATE int sqlite3Fts3PendingTermsFlush(Fts3Table *); SQLITE_PRIVATE void sqlite3Fts3PendingTermsClear(Fts3Table *); SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *); SQLITE_PRIVATE int sqlite3Fts3SegReaderNew(int, sqlite3_int64, sqlite3_int64, sqlite3_int64, const char *, int, Fts3SegReader**); | | > < | | < > | | | | | > | | > > | | > > < | < < | 112076 112077 112078 112079 112080 112081 112082 112083 112084 112085 112086 112087 112088 112089 112090 112091 112092 112093 112094 112095 112096 112097 112098 112099 112100 112101 112102 112103 112104 112105 112106 112107 112108 112109 112110 112111 112112 112113 112114 112115 112116 112117 112118 112119 112120 112121 112122 112123 112124 112125 112126 112127 112128 112129 112130 112131 112132 112133 112134 112135 112136 112137 112138 112139 112140 112141 112142 112143 112144 112145 112146 112147 112148 112149 112150 112151 112152 112153 112154 112155 112156 112157 112158 112159 112160 112161 112162 |
/* fts3_write.c */
SQLITE_PRIVATE int sqlite3Fts3UpdateMethod(sqlite3_vtab*,int,sqlite3_value**,sqlite3_int64*);
SQLITE_PRIVATE int sqlite3Fts3PendingTermsFlush(Fts3Table *);
SQLITE_PRIVATE void sqlite3Fts3PendingTermsClear(Fts3Table *);
SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *);
SQLITE_PRIVATE int sqlite3Fts3SegReaderNew(int, sqlite3_int64,
sqlite3_int64, sqlite3_int64, const char *, int, Fts3SegReader**);
SQLITE_PRIVATE int sqlite3Fts3SegReaderPending(
Fts3Table*,int,const char*,int,int,Fts3SegReader**);
SQLITE_PRIVATE void sqlite3Fts3SegReaderFree(Fts3SegReader *);
SQLITE_PRIVATE int sqlite3Fts3AllSegdirs(Fts3Table*, int, int, sqlite3_stmt **);
SQLITE_PRIVATE int sqlite3Fts3ReadLock(Fts3Table *);
SQLITE_PRIVATE int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char **, int*, int*);
SQLITE_PRIVATE int sqlite3Fts3SelectDoctotal(Fts3Table *, sqlite3_stmt **);
SQLITE_PRIVATE int sqlite3Fts3SelectDocsize(Fts3Table *, sqlite3_int64, sqlite3_stmt **);
SQLITE_PRIVATE void sqlite3Fts3FreeDeferredTokens(Fts3Cursor *);
SQLITE_PRIVATE int sqlite3Fts3DeferToken(Fts3Cursor *, Fts3PhraseToken *, int);
SQLITE_PRIVATE int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *);
SQLITE_PRIVATE void sqlite3Fts3FreeDeferredDoclists(Fts3Cursor *);
SQLITE_PRIVATE void sqlite3Fts3SegmentsClose(Fts3Table *);
/* Special values interpreted by sqlite3SegReaderCursor() */
#define FTS3_SEGCURSOR_PENDING -1
#define FTS3_SEGCURSOR_ALL -2
SQLITE_PRIVATE int sqlite3Fts3SegReaderStart(Fts3Table*, Fts3MultiSegReader*, Fts3SegFilter*);
SQLITE_PRIVATE int sqlite3Fts3SegReaderStep(Fts3Table *, Fts3MultiSegReader *);
SQLITE_PRIVATE void sqlite3Fts3SegReaderFinish(Fts3MultiSegReader *);
SQLITE_PRIVATE int sqlite3Fts3SegReaderCursor(
Fts3Table *, int, int, const char *, int, int, int, Fts3MultiSegReader *);
/* Flags allowed as part of the 4th argument to SegmentReaderIterate() */
#define FTS3_SEGMENT_REQUIRE_POS 0x00000001
#define FTS3_SEGMENT_IGNORE_EMPTY 0x00000002
#define FTS3_SEGMENT_COLUMN_FILTER 0x00000004
#define FTS3_SEGMENT_PREFIX 0x00000008
#define FTS3_SEGMENT_SCAN 0x00000010
/* Type passed as 4th argument to SegmentReaderIterate() */
struct Fts3SegFilter {
const char *zTerm;
int nTerm;
int iCol;
int flags;
};
struct Fts3MultiSegReader {
/* Used internally by sqlite3Fts3SegReaderXXX() calls */
Fts3SegReader **apSegment; /* Array of Fts3SegReader objects */
int nSegment; /* Size of apSegment array */
int nAdvance; /* How many seg-readers to advance */
Fts3SegFilter *pFilter; /* Pointer to filter object */
char *aBuffer; /* Buffer to merge doclists in */
int nBuffer; /* Allocated size of aBuffer[] in bytes */
int iColFilter; /* If >=0, filter for this column */
/* Used by fts3.c only. */
int nCost; /* Cost of running iterator */
int bLookup; /* True if a lookup of a single entry. */
/* Output values. Valid only after Fts3SegReaderStep() returns SQLITE_ROW. */
char *zTerm; /* Pointer to term buffer */
int nTerm; /* Size of zTerm in bytes */
char *aDoclist; /* Pointer to doclist buffer */
int nDoclist; /* Size of aDoclist[] in bytes */
};
/* fts3.c */
SQLITE_PRIVATE int sqlite3Fts3PutVarint(char *, sqlite3_int64);
SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *, sqlite_int64 *);
SQLITE_PRIVATE int sqlite3Fts3GetVarint32(const char *, int *);
SQLITE_PRIVATE int sqlite3Fts3VarintLen(sqlite3_uint64);
SQLITE_PRIVATE void sqlite3Fts3Dequote(char *);
SQLITE_PRIVATE void sqlite3Fts3DoclistPrev(int,char*,int,char**,sqlite3_int64*,int*,u8*);
SQLITE_PRIVATE int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *);
/* fts3_tokenizer.c */
SQLITE_PRIVATE const char *sqlite3Fts3NextToken(const char *, int *);
SQLITE_PRIVATE int sqlite3Fts3InitHashTable(sqlite3 *, Fts3Hash *, const char *);
SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(Fts3Hash *pHash, const char *,
sqlite3_tokenizer **, char **
);
|
| ︙ | ︙ | |||
112077 112078 112079 112080 112081 112082 112083 112084 112085 112086 112087 112088 112089 112090 | #ifdef SQLITE_TEST SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3 *db); SQLITE_PRIVATE int sqlite3Fts3InitTerm(sqlite3 *db); #endif /* fts3_aux.c */ SQLITE_PRIVATE int sqlite3Fts3InitAux(sqlite3 *db); #endif /* _FTSINT_H */ /************** End of fts3Int.h *********************************************/ /************** Continuing where we left off in fts3.c ***********************/ | > > > > > > > > > > > > > > > > > > > > > > > | 112177 112178 112179 112180 112181 112182 112183 112184 112185 112186 112187 112188 112189 112190 112191 112192 112193 112194 112195 112196 112197 112198 112199 112200 112201 112202 112203 112204 112205 112206 112207 112208 112209 112210 112211 112212 112213 |
#ifdef SQLITE_TEST
SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3 *db);
SQLITE_PRIVATE int sqlite3Fts3InitTerm(sqlite3 *db);
#endif
/* fts3_aux.c */
SQLITE_PRIVATE int sqlite3Fts3InitAux(sqlite3 *db);
SQLITE_PRIVATE int sqlite3Fts3TermSegReaderCursor(
Fts3Cursor *pCsr, /* Virtual table cursor handle */
const char *zTerm, /* Term to query for */
int nTerm, /* Size of zTerm in bytes */
int isPrefix, /* True for a prefix search */
Fts3MultiSegReader **ppSegcsr /* OUT: Allocated seg-reader cursor */
);
SQLITE_PRIVATE void sqlite3Fts3EvalPhraseCleanup(Fts3Phrase *);
SQLITE_PRIVATE int sqlite3Fts3EvalStart(Fts3Cursor *, Fts3Expr *, int);
SQLITE_PRIVATE int sqlite3Fts3EvalNext(Fts3Cursor *pCsr);
SQLITE_PRIVATE int sqlite3Fts3MsrIncrStart(
Fts3Table*, Fts3MultiSegReader*, int, const char*, int);
SQLITE_PRIVATE int sqlite3Fts3MsrIncrNext(
Fts3Table *, Fts3MultiSegReader *, sqlite3_int64 *, char **, int *);
SQLITE_PRIVATE char *sqlite3Fts3EvalPhrasePoslist(Fts3Cursor *, Fts3Expr *, int iCol);
SQLITE_PRIVATE int sqlite3Fts3MsrOvfl(Fts3Cursor *, Fts3MultiSegReader *, int *);
SQLITE_PRIVATE int sqlite3Fts3DeferredTokenList(Fts3DeferredToken *, char **, int *);
#endif /* _FTSINT_H */
/************** End of fts3Int.h *********************************************/
/************** Continuing where we left off in fts3.c ***********************/
|
| ︙ | ︙ | |||
112198 112199 112200 112201 112202 112203 112204 | *pVal += iVal; } /* ** When this function is called, *pp points to the first byte following a ** varint that is part of a doclist (or position-list, or any other list ** of varints). This function moves *pp to point to the start of that varint, | | | | < < < < < < < < < < < < < < | 112321 112322 112323 112324 112325 112326 112327 112328 112329 112330 112331 112332 112333 112334 112335 112336 112337 112338 112339 112340 112341 112342 112343 112344 112345 112346 112347 112348 112349 112350 112351 112352 112353 112354 112355 112356 |
*pVal += iVal;
}
/*
** When this function is called, *pp points to the first byte following a
** varint that is part of a doclist (or position-list, or any other list
** of varints). This function moves *pp to point to the start of that varint,
** and sets *pVal by the varint value.
**
** Argument pStart points to the first byte of the doclist that the
** varint is part of.
*/
static void fts3GetReverseVarint(
char **pp,
char *pStart,
sqlite3_int64 *pVal
){
sqlite3_int64 iVal;
char *p = *pp;
/* Pointer p now points at the first byte past the varint we are
** interested in. So, unless the doclist is corrupt, the 0x80 bit is
** clear on character p[-1]. */
for(p = (*pp)-2; p>=pStart && *p&0x80; p--);
p++;
*pp = p;
sqlite3Fts3GetVarint(p, &iVal);
*pVal = iVal;
}
/*
** The xDisconnect() virtual table method.
*/
static int fts3DisconnectMethod(sqlite3_vtab *pVtab){
Fts3Table *p = (Fts3Table *)pVtab;
|
| ︙ | ︙ | |||
112606 112607 112608 112609 112610 112611 112612 112613 112614 112615 112616 112617 112618 112619 |
fts3Appendf(pRc, &zRet, "?");
for(i=0; i<p->nColumn; i++){
fts3Appendf(pRc, &zRet, ",%s(?)", zFunction);
}
sqlite3_free(zFree);
return zRet;
}
/*
** This function is the implementation of both the xConnect and xCreate
** methods of the FTS3 virtual table.
**
** The argv[] array contains the following:
**
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 112715 112716 112717 112718 112719 112720 112721 112722 112723 112724 112725 112726 112727 112728 112729 112730 112731 112732 112733 112734 112735 112736 112737 112738 112739 112740 112741 112742 112743 112744 112745 112746 112747 112748 112749 112750 112751 112752 112753 112754 112755 112756 112757 112758 112759 112760 112761 112762 112763 112764 112765 112766 112767 112768 112769 112770 112771 112772 112773 112774 112775 112776 112777 112778 112779 112780 |
fts3Appendf(pRc, &zRet, "?");
for(i=0; i<p->nColumn; i++){
fts3Appendf(pRc, &zRet, ",%s(?)", zFunction);
}
sqlite3_free(zFree);
return zRet;
}
static int fts3GobbleInt(const char **pp, int *pnOut){
const char *p = *pp;
int nInt = 0;
for(p=*pp; p[0]>='0' && p[0]<='9'; p++){
nInt = nInt * 10 + (p[0] - '0');
}
if( p==*pp ) return SQLITE_ERROR;
*pnOut = nInt;
*pp = p;
return SQLITE_OK;
}
static int fts3PrefixParameter(
const char *zParam, /* ABC in prefix=ABC parameter to parse */
int *pnIndex, /* OUT: size of *apIndex[] array */
struct Fts3Index **apIndex, /* OUT: Array of indexes for this table */
struct Fts3Index **apFree /* OUT: Free this with sqlite3_free() */
){
struct Fts3Index *aIndex;
int nIndex = 1;
if( zParam && zParam[0] ){
const char *p;
nIndex++;
for(p=zParam; *p; p++){
if( *p==',' ) nIndex++;
}
}
aIndex = sqlite3_malloc(sizeof(struct Fts3Index) * nIndex);
*apIndex = *apFree = aIndex;
*pnIndex = nIndex;
if( !aIndex ){
return SQLITE_NOMEM;
}
memset(aIndex, 0, sizeof(struct Fts3Index) * nIndex);
if( zParam ){
const char *p = zParam;
int i;
for(i=1; i<nIndex; i++){
int nPrefix;
if( fts3GobbleInt(&p, &nPrefix) ) return SQLITE_ERROR;
aIndex[i].nPrefix = nPrefix;
p++;
}
}
return SQLITE_OK;
}
/*
** This function is the implementation of both the xConnect and xCreate
** methods of the FTS3 virtual table.
**
** The argv[] array contains the following:
**
|
| ︙ | ︙ | |||
112639 112640 112641 112642 112643 112644 112645 | int iCol; /* Column index */ int nString = 0; /* Bytes required to hold all column names */ int nCol = 0; /* Number of columns in the FTS table */ char *zCsr; /* Space for holding column names */ int nDb; /* Bytes required to hold database name */ int nName; /* Bytes required to hold table name */ int isFts4 = (argv[0][3]=='4'); /* True for FTS4, false for FTS3 */ | < > > > > > > > > | | | 112800 112801 112802 112803 112804 112805 112806 112807 112808 112809 112810 112811 112812 112813 112814 112815 112816 112817 112818 112819 112820 112821 112822 112823 112824 112825 112826 |
int iCol; /* Column index */
int nString = 0; /* Bytes required to hold all column names */
int nCol = 0; /* Number of columns in the FTS table */
char *zCsr; /* Space for holding column names */
int nDb; /* Bytes required to hold database name */
int nName; /* Bytes required to hold table name */
int isFts4 = (argv[0][3]=='4'); /* True for FTS4, false for FTS3 */
const char **aCol; /* Array of column names */
sqlite3_tokenizer *pTokenizer = 0; /* Tokenizer for this table */
int nIndex; /* Size of aIndex[] array */
struct Fts3Index *aIndex; /* Array of indexes for this table */
struct Fts3Index *aFree = 0; /* Free this before returning */
/* The results of parsing supported FTS4 key=value options: */
int bNoDocsize = 0; /* True to omit %_docsize table */
int bDescIdx = 0; /* True to store descending indexes */
char *zPrefix = 0; /* Prefix parameter value (or NULL) */
char *zCompress = 0; /* compress=? parameter (or NULL) */
char *zUncompress = 0; /* uncompress=? parameter (or NULL) */
assert( strlen(argv[0])==4 );
assert( (sqlite3_strnicmp(argv[0], "fts4", 4)==0 && isFts4)
|| (sqlite3_strnicmp(argv[0], "fts3", 4)==0 && !isFts4)
);
nDb = (int)strlen(argv[1]) + 1;
|
| ︙ | ︙ | |||
112685 112686 112687 112688 112689 112690 112691 112692 112693 |
&& 0==sqlite3Fts3IsIdChar(z[8])
){
rc = sqlite3Fts3InitTokenizer(pHash, &z[9], &pTokenizer, pzErr);
}
/* Check if it is an FTS4 special argument. */
else if( isFts4 && fts3IsSpecialColumn(z, &nKey, &zVal) ){
if( !zVal ){
rc = SQLITE_NOMEM;
| > > > > > > > > > > > > > | > > > > | > > | < | > > > | | | > > | > > > > > > > > | | > | > > | | > | > > > > | | | > > > > | > | 112853 112854 112855 112856 112857 112858 112859 112860 112861 112862 112863 112864 112865 112866 112867 112868 112869 112870 112871 112872 112873 112874 112875 112876 112877 112878 112879 112880 112881 112882 112883 112884 112885 112886 112887 112888 112889 112890 112891 112892 112893 112894 112895 112896 112897 112898 112899 112900 112901 112902 112903 112904 112905 112906 112907 112908 112909 112910 112911 112912 112913 112914 112915 112916 112917 112918 112919 112920 112921 112922 112923 112924 112925 112926 112927 112928 112929 112930 112931 112932 |
&& 0==sqlite3Fts3IsIdChar(z[8])
){
rc = sqlite3Fts3InitTokenizer(pHash, &z[9], &pTokenizer, pzErr);
}
/* Check if it is an FTS4 special argument. */
else if( isFts4 && fts3IsSpecialColumn(z, &nKey, &zVal) ){
struct Fts4Option {
const char *zOpt;
int nOpt;
char **pzVar;
} aFts4Opt[] = {
{ "matchinfo", 9, 0 }, /* 0 -> MATCHINFO */
{ "prefix", 6, 0 }, /* 1 -> PREFIX */
{ "compress", 8, 0 }, /* 2 -> COMPRESS */
{ "uncompress", 10, 0 }, /* 3 -> UNCOMPRESS */
{ "order", 5, 0 } /* 4 -> ORDER */
};
int iOpt;
if( !zVal ){
rc = SQLITE_NOMEM;
}else{
for(iOpt=0; iOpt<SizeofArray(aFts4Opt); iOpt++){
struct Fts4Option *pOp = &aFts4Opt[iOpt];
if( nKey==pOp->nOpt && !sqlite3_strnicmp(z, pOp->zOpt, pOp->nOpt) ){
break;
}
}
if( iOpt==SizeofArray(aFts4Opt) ){
*pzErr = sqlite3_mprintf("unrecognized parameter: %s", z);
rc = SQLITE_ERROR;
}else{
switch( iOpt ){
case 0: /* MATCHINFO */
if( strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "fts3", 4) ){
*pzErr = sqlite3_mprintf("unrecognized matchinfo: %s", zVal);
rc = SQLITE_ERROR;
}
bNoDocsize = 1;
break;
case 1: /* PREFIX */
sqlite3_free(zPrefix);
zPrefix = zVal;
zVal = 0;
break;
case 2: /* COMPRESS */
sqlite3_free(zCompress);
zCompress = zVal;
zVal = 0;
break;
case 3: /* UNCOMPRESS */
sqlite3_free(zUncompress);
zUncompress = zVal;
zVal = 0;
break;
case 4: /* ORDER */
if( (strlen(zVal)!=3 || sqlite3_strnicmp(zVal, "asc", 3))
&& (strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "desc", 3))
){
*pzErr = sqlite3_mprintf("unrecognized order: %s", zVal);
rc = SQLITE_ERROR;
}
bDescIdx = (zVal[0]=='d' || zVal[0]=='D');
break;
}
}
sqlite3_free(zVal);
}
}
/* Otherwise, the argument is a column name. */
else {
nString += (int)(strlen(z) + 1);
aCol[nCol++] = z;
}
|
| ︙ | ︙ | |||
112730 112731 112732 112733 112734 112735 112736 |
if( pTokenizer==0 ){
rc = sqlite3Fts3InitTokenizer(pHash, "simple", &pTokenizer, pzErr);
if( rc!=SQLITE_OK ) goto fts3_init_out;
}
assert( pTokenizer );
| > > > > | > > | > < > > > > > > | | > | | 112942 112943 112944 112945 112946 112947 112948 112949 112950 112951 112952 112953 112954 112955 112956 112957 112958 112959 112960 112961 112962 112963 112964 112965 112966 112967 112968 112969 112970 112971 112972 112973 112974 112975 112976 112977 112978 112979 112980 112981 112982 112983 112984 112985 112986 112987 112988 112989 112990 112991 112992 112993 112994 112995 112996 |
if( pTokenizer==0 ){
rc = sqlite3Fts3InitTokenizer(pHash, "simple", &pTokenizer, pzErr);
if( rc!=SQLITE_OK ) goto fts3_init_out;
}
assert( pTokenizer );
rc = fts3PrefixParameter(zPrefix, &nIndex, &aIndex, &aFree);
if( rc==SQLITE_ERROR ){
assert( zPrefix );
*pzErr = sqlite3_mprintf("error parsing prefix parameter: %s", zPrefix);
}
if( rc!=SQLITE_OK ) goto fts3_init_out;
/* Allocate and populate the Fts3Table structure. */
nByte = sizeof(Fts3Table) + /* Fts3Table */
nCol * sizeof(char *) + /* azColumn */
nIndex * sizeof(struct Fts3Index) + /* aIndex */
nName + /* zName */
nDb + /* zDb */
nString; /* Space for azColumn strings */
p = (Fts3Table*)sqlite3_malloc(nByte);
if( p==0 ){
rc = SQLITE_NOMEM;
goto fts3_init_out;
}
memset(p, 0, nByte);
p->db = db;
p->nColumn = nCol;
p->nPendingData = 0;
p->azColumn = (char **)&p[1];
p->pTokenizer = pTokenizer;
p->nMaxPendingData = FTS3_MAX_PENDING_DATA;
p->bHasDocsize = (isFts4 && bNoDocsize==0);
p->bHasStat = isFts4;
p->bDescIdx = bDescIdx;
TESTONLY( p->inTransaction = -1 );
TESTONLY( p->mxSavepoint = -1 );
p->aIndex = (struct Fts3Index *)&p->azColumn[nCol];
memcpy(p->aIndex, aIndex, sizeof(struct Fts3Index) * nIndex);
p->nIndex = nIndex;
for(i=0; i<nIndex; i++){
fts3HashInit(&p->aIndex[i].hPending, FTS3_HASH_STRING, 1);
}
/* Fill in the zName and zDb fields of the vtab structure. */
zCsr = (char *)&p->aIndex[nIndex];
p->zName = zCsr;
memcpy(zCsr, argv[2], nName);
zCsr += nName;
p->zDb = zCsr;
memcpy(zCsr, argv[1], nDb);
zCsr += nDb;
|
| ︙ | ︙ | |||
112795 112796 112797 112798 112799 112800 112801 |
** database. TODO: For xConnect(), it could verify that said tables exist.
*/
if( isCreate ){
rc = fts3CreateTables(p);
}
/* Figure out the page-size for the database. This is required in order to
| | < < > > > > | 113020 113021 113022 113023 113024 113025 113026 113027 113028 113029 113030 113031 113032 113033 113034 113035 113036 113037 113038 113039 113040 113041 113042 113043 113044 113045 113046 113047 113048 113049 113050 113051 113052 113053 113054 |
** database. TODO: For xConnect(), it could verify that said tables exist.
*/
if( isCreate ){
rc = fts3CreateTables(p);
}
/* 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);
p->nNodeSize = p->nPgsz-35;
/* Declare the table schema to SQLite. */
fts3DeclareVtab(&rc, p);
fts3_init_out:
sqlite3_free(zPrefix);
sqlite3_free(aFree);
sqlite3_free(zCompress);
sqlite3_free(zUncompress);
sqlite3_free((void *)aCol);
if( rc!=SQLITE_OK ){
if( p ){
fts3DisconnectMethod((sqlite3_vtab *)p);
}else if( pTokenizer ){
pTokenizer->pModule->xDestroy(pTokenizer);
}
}else{
assert( p->pSegments==0 );
*ppVTab = &p->base;
}
return rc;
}
/*
** The xConnect() and xCreate() methods for the virtual table. All the
|
| ︙ | ︙ | |||
112911 112912 112913 112914 112915 112916 112917 |
struct sqlite3_index_orderby *pOrder = &pInfo->aOrderBy[0];
if( pOrder->iColumn<0 || pOrder->iColumn==p->nColumn+1 ){
if( pOrder->desc ){
pInfo->idxStr = "DESC";
}else{
pInfo->idxStr = "ASC";
}
| < | | | > > | 113138 113139 113140 113141 113142 113143 113144 113145 113146 113147 113148 113149 113150 113151 113152 113153 113154 113155 113156 |
struct sqlite3_index_orderby *pOrder = &pInfo->aOrderBy[0];
if( pOrder->iColumn<0 || pOrder->iColumn==p->nColumn+1 ){
if( pOrder->desc ){
pInfo->idxStr = "DESC";
}else{
pInfo->idxStr = "ASC";
}
pInfo->orderByConsumed = 1;
}
}
assert( p->pSegments==0 );
return SQLITE_OK;
}
/*
** Implementation of xOpen method.
*/
static int fts3OpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){
|
| ︙ | ︙ | |||
112950 112951 112952 112953 112954 112955 112956 112957 112958 112959 112960 112961 112962 112963 112964 112965 112966 112967 |
Fts3Cursor *pCsr = (Fts3Cursor *)pCursor;
assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 );
sqlite3_finalize(pCsr->pStmt);
sqlite3Fts3ExprFree(pCsr->pExpr);
sqlite3Fts3FreeDeferredTokens(pCsr);
sqlite3_free(pCsr->aDoclist);
sqlite3_free(pCsr->aMatchinfo);
sqlite3_free(pCsr);
return SQLITE_OK;
}
/*
** Position the pCsr->pStmt statement so that it is on the row
** of the %_content table that contains the last match. Return
** SQLITE_OK on success.
*/
static int fts3CursorSeek(sqlite3_context *pContext, Fts3Cursor *pCsr){
if( pCsr->isRequireSeek ){
| > < > | 113178 113179 113180 113181 113182 113183 113184 113185 113186 113187 113188 113189 113190 113191 113192 113193 113194 113195 113196 113197 113198 113199 113200 113201 113202 113203 113204 113205 |
Fts3Cursor *pCsr = (Fts3Cursor *)pCursor;
assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 );
sqlite3_finalize(pCsr->pStmt);
sqlite3Fts3ExprFree(pCsr->pExpr);
sqlite3Fts3FreeDeferredTokens(pCsr);
sqlite3_free(pCsr->aDoclist);
sqlite3_free(pCsr->aMatchinfo);
assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 );
sqlite3_free(pCsr);
return SQLITE_OK;
}
/*
** Position the pCsr->pStmt statement so that it is on the row
** of the %_content table that contains the last match. Return
** SQLITE_OK on success.
*/
static int fts3CursorSeek(sqlite3_context *pContext, Fts3Cursor *pCsr){
if( pCsr->isRequireSeek ){
sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iPrevId);
pCsr->isRequireSeek = 0;
if( SQLITE_ROW==sqlite3_step(pCsr->pStmt) ){
return SQLITE_OK;
}else{
int rc = sqlite3_reset(pCsr->pStmt);
if( rc==SQLITE_OK ){
/* If no row was found and no error has occured, then the %_content
** table is missing a row that is present in the full-text index.
|
| ︙ | ︙ | |||
113143 113144 113145 113146 113147 113148 113149 |
assert( !piLeaf2 || !piLeaf || rc!=SQLITE_OK || (*piLeaf<=*piLeaf2) );
if( rc==SQLITE_OK && iHeight>1 ){
char *zBlob = 0; /* Blob read from %_segments table */
int nBlob; /* Size of zBlob in bytes */
if( piLeaf && piLeaf2 && (*piLeaf!=*piLeaf2) ){
| | | | 113372 113373 113374 113375 113376 113377 113378 113379 113380 113381 113382 113383 113384 113385 113386 113387 113388 113389 113390 113391 113392 113393 113394 113395 113396 |
assert( !piLeaf2 || !piLeaf || rc!=SQLITE_OK || (*piLeaf<=*piLeaf2) );
if( rc==SQLITE_OK && iHeight>1 ){
char *zBlob = 0; /* Blob read from %_segments table */
int nBlob; /* Size of zBlob in bytes */
if( piLeaf && piLeaf2 && (*piLeaf!=*piLeaf2) ){
rc = sqlite3Fts3ReadBlock(p, *piLeaf, &zBlob, &nBlob, 0);
if( rc==SQLITE_OK ){
rc = fts3SelectLeaf(p, zTerm, nTerm, zBlob, nBlob, piLeaf, 0);
}
sqlite3_free(zBlob);
piLeaf = 0;
zBlob = 0;
}
if( rc==SQLITE_OK ){
rc = sqlite3Fts3ReadBlock(p, piLeaf?*piLeaf:*piLeaf2, &zBlob, &nBlob, 0);
}
if( rc==SQLITE_OK ){
rc = fts3SelectLeaf(p, zTerm, nTerm, zBlob, nBlob, piLeaf, piLeaf2);
}
sqlite3_free(zBlob);
}
|
| ︙ | ︙ | |||
113529 113530 113531 113532 113533 113534 113535 | } *p++ = 0x00; *pp = p; return 1; } /* | | > > > > > > > > > > > > < < < < < < | | | | | | | | | | | | | | | | | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < | | | > > > > | | < < | 113758 113759 113760 113761 113762 113763 113764 113765 113766 113767 113768 113769 113770 113771 113772 113773 113774 113775 113776 113777 113778 113779 113780 113781 113782 113783 113784 113785 113786 113787 113788 113789 113790 113791 113792 113793 113794 113795 113796 113797 113798 113799 113800 113801 113802 113803 113804 113805 113806 113807 113808 113809 113810 113811 113812 113813 113814 113815 113816 113817 113818 113819 113820 113821 113822 113823 113824 113825 113826 113827 113828 113829 113830 113831 113832 113833 113834 113835 113836 113837 113838 113839 113840 113841 113842 113843 113844 113845 113846 113847 113848 113849 113850 113851 113852 113853 113854 113855 113856 113857 113858 113859 113860 113861 113862 113863 113864 113865 113866 113867 113868 113869 113870 113871 113872 113873 113874 113875 113876 113877 113878 113879 113880 113881 113882 113883 113884 113885 113886 113887 113888 113889 113890 113891 113892 113893 113894 113895 113896 113897 113898 113899 113900 113901 113902 113903 113904 113905 113906 113907 113908 113909 113910 113911 113912 113913 113914 113915 113916 113917 113918 113919 113920 113921 113922 113923 113924 113925 113926 113927 113928 113929 113930 113931 113932 113933 113934 113935 113936 113937 113938 113939 113940 113941 113942 113943 113944 113945 113946 113947 113948 113949 113950 113951 113952 113953 113954 113955 113956 113957 113958 113959 113960 113961 113962 113963 113964 113965 113966 113967 113968 113969 113970 113971 113972 113973 113974 113975 113976 113977 113978 113979 113980 113981 113982 113983 113984 113985 113986 113987 113988 113989 113990 113991 113992 113993 113994 113995 113996 113997 113998 113999 114000 114001 114002 114003 114004 114005 114006 114007 |
}
*p++ = 0x00;
*pp = p;
return 1;
}
/*
** Merge two position-lists as required by the NEAR operator. The argument
** position lists correspond to the left and right phrases of an expression
** like:
**
** "phrase 1" NEAR "phrase number 2"
**
** Position list *pp1 corresponds to the left-hand side of the NEAR
** expression and *pp2 to the right. As usual, the indexes in the position
** lists are the offsets of the last token in each phrase (tokens "1" and "2"
** in the example above).
**
** The output position list - written to *pp - is a copy of *pp2 with those
** entries that are not sufficiently NEAR entries in *pp1 removed.
*/
static int fts3PoslistNearMerge(
char **pp, /* Output buffer */
char *aTmp, /* Temporary buffer space */
int nRight, /* Maximum difference in token positions */
int nLeft, /* Maximum difference in token positions */
char **pp1, /* IN/OUT: Left input list */
char **pp2 /* IN/OUT: Right input list */
){
char *p1 = *pp1;
char *p2 = *pp2;
char *pTmp1 = aTmp;
char *pTmp2;
char *aTmp2;
int res = 1;
fts3PoslistPhraseMerge(&pTmp1, nRight, 0, 0, pp1, pp2);
aTmp2 = pTmp2 = pTmp1;
*pp1 = p1;
*pp2 = p2;
fts3PoslistPhraseMerge(&pTmp2, nLeft, 1, 0, pp2, pp1);
if( pTmp1!=aTmp && pTmp2!=aTmp2 ){
fts3PoslistMerge(pp, &aTmp, &aTmp2);
}else if( pTmp1!=aTmp ){
fts3PoslistCopy(pp, &aTmp);
}else if( pTmp2!=aTmp2 ){
fts3PoslistCopy(pp, &aTmp2);
}else{
res = 0;
}
return res;
}
/*
** A pointer to an instance of this structure is used as the context
** argument to sqlite3Fts3SegReaderIterate()
*/
typedef struct TermSelect TermSelect;
struct TermSelect {
int isReqPos;
char *aaOutput[16]; /* Malloc'd output buffer */
int anOutput[16]; /* Size of output in bytes */
};
static void fts3GetDeltaVarint3(
char **pp,
char *pEnd,
int bDescIdx,
sqlite3_int64 *pVal
){
if( *pp>=pEnd ){
*pp = 0;
}else{
sqlite3_int64 iVal;
*pp += sqlite3Fts3GetVarint(*pp, &iVal);
if( bDescIdx ){
*pVal -= iVal;
}else{
*pVal += iVal;
}
}
}
static void fts3PutDeltaVarint3(
char **pp, /* IN/OUT: Output pointer */
int bDescIdx, /* True for descending docids */
sqlite3_int64 *piPrev, /* IN/OUT: Previous value written to list */
int *pbFirst, /* IN/OUT: True after first int written */
sqlite3_int64 iVal /* Write this value to the list */
){
sqlite3_int64 iWrite;
if( bDescIdx==0 || *pbFirst==0 ){
iWrite = iVal - *piPrev;
}else{
iWrite = *piPrev - iVal;
}
assert( *pbFirst || *piPrev==0 );
assert( *pbFirst==0 || iWrite>0 );
*pp += sqlite3Fts3PutVarint(*pp, iWrite);
*piPrev = iVal;
*pbFirst = 1;
}
#define COMPARE_DOCID(i1, i2) ((bDescIdx?-1:1) * (i1-i2))
static int fts3DoclistOrMerge(
int bDescIdx, /* True if arguments are desc */
char *a1, int n1, /* First doclist */
char *a2, int n2, /* Second doclist */
char **paOut, int *pnOut /* OUT: Malloc'd doclist */
){
sqlite3_int64 i1 = 0;
sqlite3_int64 i2 = 0;
sqlite3_int64 iPrev = 0;
char *pEnd1 = &a1[n1];
char *pEnd2 = &a2[n2];
char *p1 = a1;
char *p2 = a2;
char *p;
char *aOut;
int bFirstOut = 0;
*paOut = 0;
*pnOut = 0;
aOut = sqlite3_malloc(n1+n2);
if( !aOut ) return SQLITE_NOMEM;
p = aOut;
fts3GetDeltaVarint3(&p1, pEnd1, 0, &i1);
fts3GetDeltaVarint3(&p2, pEnd2, 0, &i2);
while( p1 || p2 ){
sqlite3_int64 iDiff = COMPARE_DOCID(i1, i2);
if( p2 && p1 && iDiff==0 ){
fts3PutDeltaVarint3(&p, bDescIdx, &iPrev, &bFirstOut, i1);
fts3PoslistMerge(&p, &p1, &p2);
fts3GetDeltaVarint3(&p1, pEnd1, bDescIdx, &i1);
fts3GetDeltaVarint3(&p2, pEnd2, bDescIdx, &i2);
}else if( !p2 || (p1 && iDiff<0) ){
fts3PutDeltaVarint3(&p, bDescIdx, &iPrev, &bFirstOut, i1);
fts3PoslistCopy(&p, &p1);
fts3GetDeltaVarint3(&p1, pEnd1, bDescIdx, &i1);
}else{
fts3PutDeltaVarint3(&p, bDescIdx, &iPrev, &bFirstOut, i2);
fts3PoslistCopy(&p, &p2);
fts3GetDeltaVarint3(&p2, pEnd2, bDescIdx, &i2);
}
}
*paOut = aOut;
*pnOut = (p-aOut);
return SQLITE_OK;
}
static void fts3DoclistPhraseMerge(
int bDescIdx, /* True if arguments are desc */
int nDist, /* Distance from left to right (1=adjacent) */
char *aLeft, int nLeft, /* Left doclist */
char *aRight, int *pnRight /* IN/OUT: Right/output doclist */
){
sqlite3_int64 i1 = 0;
sqlite3_int64 i2 = 0;
sqlite3_int64 iPrev = 0;
char *pEnd1 = &aLeft[nLeft];
char *pEnd2 = &aRight[*pnRight];
char *p1 = aLeft;
char *p2 = aRight;
char *p;
int bFirstOut = 0;
char *aOut = aRight;
assert( nDist>0 );
p = aOut;
fts3GetDeltaVarint3(&p1, pEnd1, 0, &i1);
fts3GetDeltaVarint3(&p2, pEnd2, 0, &i2);
while( p1 && p2 ){
sqlite3_int64 iDiff = COMPARE_DOCID(i1, i2);
if( iDiff==0 ){
char *pSave = p;
sqlite3_int64 iPrevSave = iPrev;
int bFirstOutSave = bFirstOut;
fts3PutDeltaVarint3(&p, bDescIdx, &iPrev, &bFirstOut, i1);
if( 0==fts3PoslistPhraseMerge(&p, nDist, 0, 1, &p1, &p2) ){
p = pSave;
iPrev = iPrevSave;
bFirstOut = bFirstOutSave;
}
fts3GetDeltaVarint3(&p1, pEnd1, bDescIdx, &i1);
fts3GetDeltaVarint3(&p2, pEnd2, bDescIdx, &i2);
}else if( iDiff<0 ){
fts3PoslistCopy(0, &p1);
fts3GetDeltaVarint3(&p1, pEnd1, bDescIdx, &i1);
}else{
fts3PoslistCopy(0, &p2);
fts3GetDeltaVarint3(&p2, pEnd2, bDescIdx, &i2);
}
}
*pnRight = p - aOut;
}
/*
** Merge all doclists in the TermSelect.aaOutput[] array into a single
** doclist stored in TermSelect.aaOutput[0]. If successful, delete all
** other doclists (except the aaOutput[0] one) and return SQLITE_OK.
**
** If an OOM error occurs, return SQLITE_NOMEM. In this case it is
** the responsibility of the caller to free any doclists left in the
** TermSelect.aaOutput[] array.
*/
static int fts3TermSelectMerge(Fts3Table *p, TermSelect *pTS){
char *aOut = 0;
int nOut = 0;
int i;
/* Loop through the doclists in the aaOutput[] array. Merge them all
** into a single doclist.
*/
for(i=0; i<SizeofArray(pTS->aaOutput); i++){
if( pTS->aaOutput[i] ){
if( !aOut ){
aOut = pTS->aaOutput[i];
nOut = pTS->anOutput[i];
pTS->aaOutput[i] = 0;
}else{
int nNew;
char *aNew;
int rc = fts3DoclistOrMerge(p->bDescIdx,
pTS->aaOutput[i], pTS->anOutput[i], aOut, nOut, &aNew, &nNew
);
if( rc!=SQLITE_OK ){
sqlite3_free(aOut);
return rc;
}
sqlite3_free(pTS->aaOutput[i]);
sqlite3_free(aOut);
pTS->aaOutput[i] = 0;
aOut = aNew;
nOut = nNew;
}
}
|
| ︙ | ︙ | |||
113832 113833 113834 113835 113836 113837 113838 |
UNUSED_PARAMETER(p);
UNUSED_PARAMETER(zTerm);
UNUSED_PARAMETER(nTerm);
if( pTS->aaOutput[0]==0 ){
/* If this is the first term selected, copy the doclist to the output
| | < < < < < | > > > | | | | < < | | < < < | | | | | | | | | | | | > > > > | > | < < < > | | | < | < < < < < < < < | < < | < | > | > | > | < < < < | < < < < < | | | | < < > | > | | | < < < < < < < | | | < | > > < < < < < < < < < < < < | | | < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > | | | > > | > > > | | | > | > | > > > | > > > > | > > > > > > > > | > | | 114029 114030 114031 114032 114033 114034 114035 114036 114037 114038 114039 114040 114041 114042 114043 114044 114045 114046 114047 114048 114049 114050 114051 114052 114053 114054 114055 114056 114057 114058 114059 114060 114061 114062 114063 114064 114065 114066 114067 114068 114069 114070 114071 114072 114073 114074 114075 114076 114077 114078 114079 114080 114081 114082 114083 114084 114085 114086 114087 114088 114089 114090 114091 114092 114093 114094 114095 114096 114097 114098 114099 114100 114101 114102 114103 114104 114105 114106 114107 114108 114109 114110 114111 114112 114113 114114 114115 114116 114117 114118 114119 114120 114121 114122 114123 114124 114125 114126 114127 114128 114129 114130 114131 114132 114133 114134 114135 114136 114137 114138 114139 114140 114141 114142 114143 114144 114145 114146 114147 114148 114149 114150 114151 114152 114153 114154 114155 114156 114157 114158 114159 114160 114161 114162 114163 114164 114165 114166 114167 114168 114169 114170 114171 114172 114173 114174 114175 114176 114177 114178 114179 114180 114181 114182 114183 114184 114185 114186 114187 114188 114189 114190 114191 114192 114193 114194 114195 114196 114197 114198 114199 114200 114201 114202 114203 114204 114205 114206 114207 114208 114209 114210 114211 114212 114213 114214 114215 114216 114217 114218 114219 114220 114221 114222 114223 114224 114225 114226 114227 114228 114229 114230 114231 114232 114233 114234 114235 114236 114237 114238 114239 114240 114241 114242 114243 114244 114245 114246 114247 114248 114249 114250 114251 114252 114253 114254 114255 114256 114257 114258 114259 114260 114261 114262 114263 114264 114265 114266 114267 114268 114269 114270 114271 114272 114273 |
UNUSED_PARAMETER(p);
UNUSED_PARAMETER(zTerm);
UNUSED_PARAMETER(nTerm);
if( pTS->aaOutput[0]==0 ){
/* If this is the first term selected, copy the doclist to the output
** buffer using memcpy(). */
pTS->aaOutput[0] = sqlite3_malloc(nDoclist);
pTS->anOutput[0] = nDoclist;
if( pTS->aaOutput[0] ){
memcpy(pTS->aaOutput[0], aDoclist, nDoclist);
}else{
return SQLITE_NOMEM;
}
}else{
char *aMerge = aDoclist;
int nMerge = nDoclist;
int iOut;
for(iOut=0; iOut<SizeofArray(pTS->aaOutput); iOut++){
if( pTS->aaOutput[iOut]==0 ){
assert( iOut>0 );
pTS->aaOutput[iOut] = aMerge;
pTS->anOutput[iOut] = nMerge;
break;
}else{
char *aNew;
int nNew;
int rc = fts3DoclistOrMerge(p->bDescIdx, aMerge, nMerge,
pTS->aaOutput[iOut], pTS->anOutput[iOut], &aNew, &nNew
);
if( rc!=SQLITE_OK ){
if( aMerge!=aDoclist ) sqlite3_free(aMerge);
return rc;
}
if( aMerge!=aDoclist ) sqlite3_free(aMerge);
sqlite3_free(pTS->aaOutput[iOut]);
pTS->aaOutput[iOut] = 0;
aMerge = aNew;
nMerge = nNew;
if( (iOut+1)==SizeofArray(pTS->aaOutput) ){
pTS->aaOutput[iOut] = aMerge;
pTS->anOutput[iOut] = nMerge;
}
}
}
}
return SQLITE_OK;
}
/*
** Append SegReader object pNew to the end of the pCsr->apSegment[] array.
*/
static int fts3SegReaderCursorAppend(
Fts3MultiSegReader *pCsr,
Fts3SegReader *pNew
){
if( (pCsr->nSegment%16)==0 ){
Fts3SegReader **apNew;
int nByte = (pCsr->nSegment + 16)*sizeof(Fts3SegReader*);
apNew = (Fts3SegReader **)sqlite3_realloc(pCsr->apSegment, nByte);
if( !apNew ){
sqlite3Fts3SegReaderFree(pNew);
return SQLITE_NOMEM;
}
pCsr->apSegment = apNew;
}
pCsr->apSegment[pCsr->nSegment++] = pNew;
return SQLITE_OK;
}
static int fts3SegReaderCursor(
Fts3Table *p, /* FTS3 table handle */
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 */
int nTerm, /* Size of zTerm in bytes */
int isPrefix, /* True for a prefix search */
int isScan, /* True to scan from zTerm to EOF */
Fts3MultiSegReader *pCsr /* Cursor object to populate */
){
int rc = SQLITE_OK;
int rc2;
sqlite3_stmt *pStmt = 0;
/* If iLevel is less than 0 and this is not a scan, include a seg-reader
** for the pending-terms. If this is a scan, then this call must be being
** made by an fts4aux module, not an FTS table. In this case calling
** Fts3SegReaderPending might segfault, as the data structures used by
** fts4aux are not completely populated. So it's easiest to filter these
** calls out here. */
if( iLevel<0 && p->aIndex ){
Fts3SegReader *pSeg = 0;
rc = sqlite3Fts3SegReaderPending(p, iIndex, zTerm, nTerm, isPrefix, &pSeg);
if( rc==SQLITE_OK && pSeg ){
rc = fts3SegReaderCursorAppend(pCsr, pSeg);
}
}
if( iLevel!=FTS3_SEGCURSOR_PENDING ){
if( rc==SQLITE_OK ){
rc = sqlite3Fts3AllSegdirs(p, iIndex, iLevel, &pStmt);
}
while( rc==SQLITE_OK && SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){
Fts3SegReader *pSeg = 0;
/* Read the values returned by the SELECT into local variables. */
sqlite3_int64 iStartBlock = sqlite3_column_int64(pStmt, 1);
sqlite3_int64 iLeavesEndBlock = sqlite3_column_int64(pStmt, 2);
sqlite3_int64 iEndBlock = sqlite3_column_int64(pStmt, 3);
int nRoot = sqlite3_column_bytes(pStmt, 4);
char const *zRoot = sqlite3_column_blob(pStmt, 4);
/* If zTerm is not NULL, and this segment is not stored entirely on its
** root node, the range of leaves scanned can be reduced. Do this. */
if( iStartBlock && zTerm ){
sqlite3_int64 *pi = (isPrefix ? &iLeavesEndBlock : 0);
rc = fts3SelectLeaf(p, zTerm, nTerm, zRoot, nRoot, &iStartBlock, pi);
if( rc!=SQLITE_OK ) goto finished;
if( isPrefix==0 && isScan==0 ) iLeavesEndBlock = iStartBlock;
}
rc = sqlite3Fts3SegReaderNew(pCsr->nSegment+1,
iStartBlock, iLeavesEndBlock, iEndBlock, zRoot, nRoot, &pSeg
);
if( rc!=SQLITE_OK ) goto finished;
rc = fts3SegReaderCursorAppend(pCsr, pSeg);
}
}
finished:
rc2 = sqlite3_reset(pStmt);
if( rc==SQLITE_DONE ) rc = rc2;
return rc;
}
/*
** Set up a cursor object for iterating through a full-text index or a
** single level therein.
*/
SQLITE_PRIVATE int sqlite3Fts3SegReaderCursor(
Fts3Table *p, /* FTS3 table handle */
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 */
int nTerm, /* Size of zTerm in bytes */
int isPrefix, /* True for a prefix search */
int isScan, /* True to scan from zTerm to EOF */
Fts3MultiSegReader *pCsr /* Cursor object to populate */
){
assert( iIndex>=0 && iIndex<p->nIndex );
assert( iLevel==FTS3_SEGCURSOR_ALL
|| iLevel==FTS3_SEGCURSOR_PENDING
|| iLevel>=0
);
assert( iLevel<FTS3_SEGDIR_MAXLEVEL );
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, iIndex, iLevel, zTerm, nTerm, isPrefix, isScan, pCsr
);
}
static int fts3SegReaderCursorAddZero(
Fts3Table *p,
const char *zTerm,
int nTerm,
Fts3MultiSegReader *pCsr
){
return fts3SegReaderCursor(p, 0, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 0, 0,pCsr);
}
SQLITE_PRIVATE int sqlite3Fts3TermSegReaderCursor(
Fts3Cursor *pCsr, /* Virtual table cursor handle */
const char *zTerm, /* Term to query for */
int nTerm, /* Size of zTerm in bytes */
int isPrefix, /* True for a prefix search */
Fts3MultiSegReader **ppSegcsr /* OUT: Allocated seg-reader cursor */
){
Fts3MultiSegReader *pSegcsr; /* Object to allocate and return */
int rc = SQLITE_NOMEM; /* Return code */
pSegcsr = sqlite3_malloc(sizeof(Fts3MultiSegReader));
if( pSegcsr ){
int i;
int bFound = 0; /* True once an index has been found */
Fts3Table *p = (Fts3Table *)pCsr->base.pVtab;
if( isPrefix ){
for(i=1; bFound==0 && i<p->nIndex; i++){
if( p->aIndex[i].nPrefix==nTerm ){
bFound = 1;
rc = sqlite3Fts3SegReaderCursor(
p, i, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 0, 0, pSegcsr);
pSegcsr->bLookup = 1;
}
}
for(i=1; bFound==0 && i<p->nIndex; i++){
if( p->aIndex[i].nPrefix==nTerm+1 ){
bFound = 1;
rc = sqlite3Fts3SegReaderCursor(
p, i, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 1, 0, pSegcsr
);
if( rc==SQLITE_OK ){
rc = fts3SegReaderCursorAddZero(p, zTerm, nTerm, pSegcsr);
}
}
}
}
if( bFound==0 ){
rc = sqlite3Fts3SegReaderCursor(
p, 0, FTS3_SEGCURSOR_ALL, zTerm, nTerm, isPrefix, 0, pSegcsr
);
pSegcsr->bLookup = !isPrefix;
}
}
*ppSegcsr = pSegcsr;
return rc;
}
static void fts3SegReaderCursorFree(Fts3MultiSegReader *pSegcsr){
sqlite3Fts3SegReaderFinish(pSegcsr);
sqlite3_free(pSegcsr);
}
/*
** This function retreives the doclist for the specified term (or term
** prefix) from the database.
|
| ︙ | ︙ | |||
114063 114064 114065 114066 114067 114068 114069 |
Fts3PhraseToken *pTok, /* Token to query for */
int iColumn, /* Column to query (or -ve for all columns) */
int isReqPos, /* True to include position lists in output */
int *pnOut, /* OUT: Size of buffer at *ppOut */
char **ppOut /* OUT: Malloced result buffer */
){
int rc; /* Return code */
| | | 114284 114285 114286 114287 114288 114289 114290 114291 114292 114293 114294 114295 114296 114297 114298 |
Fts3PhraseToken *pTok, /* Token to query for */
int iColumn, /* Column to query (or -ve for all columns) */
int isReqPos, /* True to include position lists in output */
int *pnOut, /* OUT: Size of buffer at *ppOut */
char **ppOut /* OUT: Malloced result buffer */
){
int rc; /* Return code */
Fts3MultiSegReader *pSegcsr; /* Seg-reader cursor for this term */
TermSelect tsc; /* Context object for fts3TermSelectCb() */
Fts3SegFilter filter; /* Segment term filter configuration */
pSegcsr = pTok->pSegcsr;
memset(&tsc, 0, sizeof(TermSelect));
tsc.isReqPos = isReqPos;
|
| ︙ | ︙ | |||
114089 114090 114091 114092 114093 114094 114095 |
){
rc = fts3TermSelectCb(p, (void *)&tsc,
pSegcsr->zTerm, pSegcsr->nTerm, pSegcsr->aDoclist, pSegcsr->nDoclist
);
}
if( rc==SQLITE_OK ){
| | | 114310 114311 114312 114313 114314 114315 114316 114317 114318 114319 114320 114321 114322 114323 114324 |
){
rc = fts3TermSelectCb(p, (void *)&tsc,
pSegcsr->zTerm, pSegcsr->nTerm, pSegcsr->aDoclist, pSegcsr->nDoclist
);
}
if( rc==SQLITE_OK ){
rc = fts3TermSelectMerge(p, &tsc);
}
if( rc==SQLITE_OK ){
*ppOut = tsc.aaOutput[0];
*pnOut = tsc.anOutput[0];
}else{
int i;
for(i=0; i<SizeofArray(tsc.aaOutput); i++){
|
| ︙ | ︙ | |||
114139 114140 114141 114142 114143 114144 114145 |
}
}
}
return nDoc;
}
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < | < < < | | | < | < < < | < | < | < < < < < < | < < | < | | 114360 114361 114362 114363 114364 114365 114366 114367 114368 114369 114370 114371 114372 114373 114374 114375 114376 114377 114378 114379 114380 114381 114382 114383 114384 114385 114386 114387 114388 114389 114390 114391 114392 114393 114394 114395 114396 114397 114398 114399 |
}
}
}
return nDoc;
}
/*
** Advance the cursor to the next row in the %_content table that
** matches the search criteria. For a MATCH search, this will be
** the next row that matches. For a full-table scan, this will be
** simply the next row in the %_content table. For a docid lookup,
** this routine simply sets the EOF flag.
**
** Return SQLITE_OK if nothing goes wrong. SQLITE_OK is returned
** even if we reach end-of-file. The fts3EofMethod() will be called
** subsequently to determine whether or not an EOF was hit.
*/
static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){
int rc;
Fts3Cursor *pCsr = (Fts3Cursor *)pCursor;
if( pCsr->eSearch==FTS3_DOCID_SEARCH || pCsr->eSearch==FTS3_FULLSCAN_SEARCH ){
if( SQLITE_ROW!=sqlite3_step(pCsr->pStmt) ){
pCsr->isEof = 1;
rc = sqlite3_reset(pCsr->pStmt);
}else{
pCsr->iPrevId = sqlite3_column_int64(pCsr->pStmt, 0);
rc = SQLITE_OK;
}
}else{
rc = sqlite3Fts3EvalNext((Fts3Cursor *)pCursor);
}
assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 );
return rc;
}
/*
** This is the xFilter interface for the virtual table. See
** the virtual table xFilter method documentation for additional
** information.
|
| ︙ | ︙ | |||
114864 114865 114866 114867 114868 114869 114870 |
static int fts3FilterMethod(
sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */
int idxNum, /* Strategy index */
const char *idxStr, /* Unused */
int nVal, /* Number of elements in apVal */
sqlite3_value **apVal /* Arguments for the indexing scheme */
){
| < < < < | > > > > > > > > | < | > | > > | > > | < > | < < | | > | | < < < < | < < < < < | < < < < < < < | < | < < < < < < < < | < < | < > > | 114412 114413 114414 114415 114416 114417 114418 114419 114420 114421 114422 114423 114424 114425 114426 114427 114428 114429 114430 114431 114432 114433 114434 114435 114436 114437 114438 114439 114440 114441 114442 114443 114444 114445 114446 114447 114448 114449 114450 114451 114452 114453 114454 114455 114456 114457 114458 114459 114460 114461 114462 114463 114464 114465 114466 114467 114468 114469 114470 114471 114472 114473 114474 114475 114476 114477 114478 114479 114480 114481 114482 114483 114484 114485 114486 114487 114488 114489 114490 114491 114492 114493 114494 114495 114496 114497 114498 114499 114500 114501 114502 114503 114504 114505 114506 114507 114508 114509 114510 114511 114512 114513 114514 114515 114516 114517 114518 114519 114520 114521 114522 114523 114524 114525 114526 114527 114528 114529 114530 114531 114532 114533 114534 114535 114536 114537 114538 114539 114540 114541 114542 114543 114544 114545 114546 114547 114548 114549 114550 114551 114552 114553 114554 114555 114556 114557 114558 114559 114560 114561 |
static int fts3FilterMethod(
sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */
int idxNum, /* Strategy index */
const char *idxStr, /* Unused */
int nVal, /* Number of elements in apVal */
sqlite3_value **apVal /* Arguments for the indexing scheme */
){
int rc;
char *zSql; /* SQL statement used to access %_content */
Fts3Table *p = (Fts3Table *)pCursor->pVtab;
Fts3Cursor *pCsr = (Fts3Cursor *)pCursor;
UNUSED_PARAMETER(idxStr);
UNUSED_PARAMETER(nVal);
assert( idxNum>=0 && idxNum<=(FTS3_FULLTEXT_SEARCH+p->nColumn) );
assert( nVal==0 || nVal==1 );
assert( (nVal==0)==(idxNum==FTS3_FULLSCAN_SEARCH) );
assert( p->pSegments==0 );
/* In case the cursor has been used before, clear it now. */
sqlite3_finalize(pCsr->pStmt);
sqlite3_free(pCsr->aDoclist);
sqlite3Fts3ExprFree(pCsr->pExpr);
memset(&pCursor[1], 0, sizeof(Fts3Cursor)-sizeof(sqlite3_vtab_cursor));
if( idxStr ){
pCsr->bDesc = (idxStr[0]=='D');
}else{
pCsr->bDesc = p->bDescIdx;
}
pCsr->eSearch = (i16)idxNum;
if( idxNum!=FTS3_DOCID_SEARCH && idxNum!=FTS3_FULLSCAN_SEARCH ){
int iCol = idxNum-FTS3_FULLTEXT_SEARCH;
const char *zQuery = (const char *)sqlite3_value_text(apVal[0]);
if( zQuery==0 && sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
return SQLITE_NOMEM;
}
rc = sqlite3Fts3ExprParse(p->pTokenizer, p->azColumn, p->nColumn,
iCol, zQuery, -1, &pCsr->pExpr
);
if( rc!=SQLITE_OK ){
if( rc==SQLITE_ERROR ){
static const char *zErr = "malformed MATCH expression: [%s]";
p->base.zErrMsg = sqlite3_mprintf(zErr, zQuery);
}
return rc;
}
rc = sqlite3Fts3ReadLock(p);
if( rc!=SQLITE_OK ) return rc;
rc = sqlite3Fts3EvalStart(pCsr, pCsr->pExpr, 1);
sqlite3Fts3SegmentsClose(p);
if( rc!=SQLITE_OK ) return rc;
pCsr->pNextId = pCsr->aDoclist;
pCsr->iPrevId = 0;
}
/* Compile a SELECT statement for this cursor. For a full-table-scan, the
** statement loops through all rows of the %_content table. For a
** full-text query or docid lookup, the statement retrieves a single
** row by docid.
*/
if( idxNum==FTS3_FULLSCAN_SEARCH ){
const char *zSort = (pCsr->bDesc ? "DESC" : "ASC");
const char *zTmpl = "SELECT %s FROM %Q.'%q_content' AS x ORDER BY docid %s";
zSql = sqlite3_mprintf(zTmpl, p->zReadExprlist, p->zDb, p->zName, zSort);
}else{
const char *zTmpl = "SELECT %s FROM %Q.'%q_content' AS x WHERE docid = ?";
zSql = sqlite3_mprintf(zTmpl, p->zReadExprlist, p->zDb, p->zName);
}
if( !zSql ) return SQLITE_NOMEM;
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0);
sqlite3_free(zSql);
if( rc!=SQLITE_OK ) return rc;
if( idxNum==FTS3_DOCID_SEARCH ){
rc = sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]);
if( rc!=SQLITE_OK ) return rc;
}
return fts3NextMethod(pCursor);
}
/*
** This is the xEof method of the virtual table. SQLite calls this
** routine to find out if it has reached the end of a result set.
*/
static int fts3EofMethod(sqlite3_vtab_cursor *pCursor){
return ((Fts3Cursor *)pCursor)->isEof;
}
/*
** This is the xRowid method. The SQLite core calls this routine to
** retrieve the rowid for the current row of the result set. fts3
** exposes %_content.docid as the rowid for the virtual table. The
** rowid should be written to *pRowid.
*/
static int fts3RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
Fts3Cursor *pCsr = (Fts3Cursor *) pCursor;
*pRowid = pCsr->iPrevId;
return SQLITE_OK;
}
/*
** This is the xColumn method, called by SQLite to request a value from
** the row that the supplied cursor currently points to.
*/
static int fts3ColumnMethod(
sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
sqlite3_context *pContext, /* Context for sqlite3_result_xxx() calls */
int iCol /* Index of column to read value from */
){
int rc = SQLITE_OK; /* Return Code */
Fts3Cursor *pCsr = (Fts3Cursor *) pCursor;
Fts3Table *p = (Fts3Table *)pCursor->pVtab;
/* The column value supplied by SQLite must be in range. */
assert( iCol>=0 && iCol<=p->nColumn+1 );
if( iCol==p->nColumn+1 ){
/* This call is a request for the "docid" column. Since "docid" is an
** alias for "rowid", use the xRowid() method to obtain the value.
*/
sqlite3_result_int64(pContext, pCsr->iPrevId);
}else if( iCol==p->nColumn ){
/* The extra column whose name is the same as the table.
** Return a blob which is a pointer to the cursor.
*/
sqlite3_result_blob(pContext, &pCsr, sizeof(pCsr), SQLITE_TRANSIENT);
}else{
rc = fts3CursorSeek(0, pCsr);
if( rc==SQLITE_OK ){
sqlite3_result_value(pContext, sqlite3_column_value(pCsr->pStmt, iCol+1));
}
}
assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 );
return rc;
}
/*
** This function is the implementation of the xUpdate callback used by
** FTS3 virtual tables. It is invoked by SQLite each time a row is to be
** inserted, updated or deleted.
|
| ︙ | ︙ | |||
115052 115053 115054 115055 115056 115057 115058 115059 115060 115061 115062 115063 115064 115065 115066 115067 115068 115069 115070 115071 115072 115073 115074 115075 115076 115077 115078 115079 115080 115081 115082 115083 115084 115085 115086 115087 115088 115089 115090 115091 115092 |
/*
** Implementation of xBegin() method. This is a no-op.
*/
static int fts3BeginMethod(sqlite3_vtab *pVtab){
UNUSED_PARAMETER(pVtab);
TESTONLY( Fts3Table *p = (Fts3Table*)pVtab );
assert( p->nPendingData==0 );
assert( p->inTransaction!=1 );
TESTONLY( p->inTransaction = 1 );
TESTONLY( p->mxSavepoint = -1; );
return SQLITE_OK;
}
/*
** Implementation of xCommit() method. This is a no-op. The contents of
** the pending-terms hash-table have already been flushed into the database
** by fts3SyncMethod().
*/
static int fts3CommitMethod(sqlite3_vtab *pVtab){
UNUSED_PARAMETER(pVtab);
TESTONLY( Fts3Table *p = (Fts3Table*)pVtab );
assert( p->nPendingData==0 );
assert( p->inTransaction!=0 );
TESTONLY( p->inTransaction = 0 );
TESTONLY( p->mxSavepoint = -1; );
return SQLITE_OK;
}
/*
** Implementation of xRollback(). Discard the contents of the pending-terms
** hash-table. Any changes made to the database are reverted by SQLite.
*/
static int fts3RollbackMethod(sqlite3_vtab *pVtab){
Fts3Table *p = (Fts3Table*)pVtab;
sqlite3Fts3PendingTermsClear(p);
assert( p->inTransaction!=0 );
TESTONLY( p->inTransaction = 0 );
TESTONLY( p->mxSavepoint = -1; );
return SQLITE_OK;
}
| > > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | > > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 114581 114582 114583 114584 114585 114586 114587 114588 114589 114590 114591 114592 114593 114594 114595 114596 114597 114598 114599 114600 114601 114602 114603 114604 114605 114606 114607 114608 114609 114610 114611 114612 114613 114614 114615 114616 114617 114618 114619 114620 114621 114622 114623 114624 114625 114626 114627 114628 114629 114630 114631 114632 114633 114634 114635 114636 114637 114638 114639 114640 114641 114642 114643 114644 114645 114646 114647 114648 114649 114650 |
/*
** Implementation of xBegin() method. This is a no-op.
*/
static int fts3BeginMethod(sqlite3_vtab *pVtab){
UNUSED_PARAMETER(pVtab);
TESTONLY( Fts3Table *p = (Fts3Table*)pVtab );
assert( p->pSegments==0 );
assert( p->nPendingData==0 );
assert( p->inTransaction!=1 );
TESTONLY( p->inTransaction = 1 );
TESTONLY( p->mxSavepoint = -1; );
return SQLITE_OK;
}
/*
** Implementation of xCommit() method. This is a no-op. The contents of
** the pending-terms hash-table have already been flushed into the database
** by fts3SyncMethod().
*/
static int fts3CommitMethod(sqlite3_vtab *pVtab){
UNUSED_PARAMETER(pVtab);
TESTONLY( Fts3Table *p = (Fts3Table*)pVtab );
assert( p->nPendingData==0 );
assert( p->inTransaction!=0 );
assert( p->pSegments==0 );
TESTONLY( p->inTransaction = 0 );
TESTONLY( p->mxSavepoint = -1; );
return SQLITE_OK;
}
/*
** Implementation of xRollback(). Discard the contents of the pending-terms
** hash-table. Any changes made to the database are reverted by SQLite.
*/
static int fts3RollbackMethod(sqlite3_vtab *pVtab){
Fts3Table *p = (Fts3Table*)pVtab;
sqlite3Fts3PendingTermsClear(p);
assert( p->inTransaction!=0 );
TESTONLY( p->inTransaction = 0 );
TESTONLY( p->mxSavepoint = -1; );
return SQLITE_OK;
}
/*
** When called, *ppPoslist must point to the byte immediately following the
** end of a position-list. i.e. ( (*ppPoslist)[-1]==POS_END ). This function
** moves *ppPoslist so that it instead points to the first byte of the
** same position list.
*/
static void fts3ReversePoslist(char *pStart, char **ppPoslist){
char *p = &(*ppPoslist)[-2];
char c;
while( p>pStart && (c=*p--)==0 );
while( p>pStart && (*p & 0x80) | c ){
c = *p--;
}
if( p>pStart ){ p = &p[2]; }
while( *p++&0x80 );
*ppPoslist = p;
}
/*
** Helper function used by the implementation of the overloaded snippet(),
** offsets() and optimize() SQL functions.
**
** If the value passed as the third argument is a blob of size
** sizeof(Fts3Cursor*), then the blob contents are copied to the
** output variable *ppCsr and SQLITE_OK is returned. Otherwise, an error
|
| ︙ | ︙ | |||
115439 115440 115441 115442 115443 115444 115445 |
"ALTER TABLE %Q.'%q_segdir' RENAME TO '%q_segdir';",
p->zDb, p->zName, zName
);
return rc;
}
static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
| < | | | | | 114868 114869 114870 114871 114872 114873 114874 114875 114876 114877 114878 114879 114880 114881 114882 114883 114884 114885 114886 |
"ALTER TABLE %Q.'%q_segdir' RENAME TO '%q_segdir';",
p->zDb, p->zName, zName
);
return rc;
}
static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
UNUSED_PARAMETER(iSavepoint);
assert( ((Fts3Table *)pVtab)->inTransaction );
assert( ((Fts3Table *)pVtab)->mxSavepoint < iSavepoint );
TESTONLY( ((Fts3Table *)pVtab)->mxSavepoint = iSavepoint );
return fts3SyncMethod(pVtab);
}
static int fts3ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
TESTONLY( Fts3Table *p = (Fts3Table*)pVtab );
UNUSED_PARAMETER(iSavepoint);
UNUSED_PARAMETER(pVtab);
assert( p->inTransaction );
assert( p->mxSavepoint >= iSavepoint );
|
| ︙ | ︙ | |||
115615 115616 115617 115618 115619 115620 115621 115622 115623 115624 115625 115626 115627 115628 |
const sqlite3_api_routines *pApi
){
SQLITE_EXTENSION_INIT2(pApi)
return sqlite3Fts3Init(db);
}
#endif
#endif
/************** End of fts3.c ************************************************/
/************** Begin file fts3_aux.c ****************************************/
/*
** 2011 Jan 27
**
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 115043 115044 115045 115046 115047 115048 115049 115050 115051 115052 115053 115054 115055 115056 115057 115058 115059 115060 115061 115062 115063 115064 115065 115066 115067 115068 115069 115070 115071 115072 115073 115074 115075 115076 115077 115078 115079 115080 115081 115082 115083 115084 115085 115086 115087 115088 115089 115090 115091 115092 115093 115094 115095 115096 115097 115098 115099 115100 115101 115102 115103 115104 115105 115106 115107 115108 115109 115110 115111 115112 115113 115114 115115 115116 115117 115118 115119 115120 115121 115122 115123 115124 115125 115126 115127 115128 115129 115130 115131 115132 115133 115134 115135 115136 115137 115138 115139 115140 115141 115142 115143 115144 115145 115146 115147 115148 115149 115150 115151 115152 115153 115154 115155 115156 115157 115158 115159 115160 115161 115162 115163 115164 115165 115166 115167 115168 115169 115170 115171 115172 115173 115174 115175 115176 115177 115178 115179 115180 115181 115182 115183 115184 115185 115186 115187 115188 115189 115190 115191 115192 115193 115194 115195 115196 115197 115198 115199 115200 115201 115202 115203 115204 115205 115206 115207 115208 115209 115210 115211 115212 115213 115214 115215 115216 115217 115218 115219 115220 115221 115222 115223 115224 115225 115226 115227 115228 115229 115230 115231 115232 115233 115234 115235 115236 115237 115238 115239 115240 115241 115242 115243 115244 115245 115246 115247 115248 115249 115250 115251 115252 115253 115254 115255 115256 115257 115258 115259 115260 115261 115262 115263 115264 115265 115266 115267 115268 115269 115270 115271 115272 115273 115274 115275 115276 115277 115278 115279 115280 115281 115282 115283 115284 115285 115286 115287 115288 115289 115290 115291 115292 115293 115294 115295 115296 115297 115298 115299 115300 115301 115302 115303 115304 115305 115306 115307 115308 115309 115310 115311 115312 115313 115314 115315 115316 115317 115318 115319 115320 115321 115322 115323 115324 115325 115326 115327 115328 115329 115330 115331 115332 115333 115334 115335 115336 115337 115338 115339 115340 115341 115342 115343 115344 115345 115346 115347 115348 115349 115350 115351 115352 115353 115354 115355 115356 115357 115358 115359 115360 115361 115362 115363 115364 115365 115366 115367 115368 115369 115370 115371 115372 115373 115374 115375 115376 115377 115378 115379 115380 115381 115382 115383 115384 115385 115386 115387 115388 115389 115390 115391 115392 115393 115394 115395 115396 115397 115398 115399 115400 115401 115402 115403 115404 115405 115406 115407 115408 115409 115410 115411 115412 115413 115414 115415 115416 115417 115418 115419 115420 115421 115422 115423 115424 115425 115426 115427 115428 115429 115430 115431 115432 115433 115434 115435 115436 115437 115438 115439 115440 115441 115442 115443 115444 115445 115446 115447 115448 115449 115450 115451 115452 115453 115454 115455 115456 115457 115458 115459 115460 115461 115462 115463 115464 115465 115466 115467 115468 115469 115470 115471 115472 115473 115474 115475 115476 115477 115478 115479 115480 115481 115482 115483 115484 115485 115486 115487 115488 115489 115490 115491 115492 115493 115494 115495 115496 115497 115498 115499 115500 115501 115502 115503 115504 115505 115506 115507 115508 115509 115510 115511 115512 115513 115514 115515 115516 115517 115518 115519 115520 115521 115522 115523 115524 115525 115526 115527 115528 115529 115530 115531 115532 115533 115534 115535 115536 115537 115538 115539 115540 115541 115542 115543 115544 115545 115546 115547 115548 115549 115550 115551 115552 115553 115554 115555 115556 115557 115558 115559 115560 115561 115562 115563 115564 115565 115566 115567 115568 115569 115570 115571 115572 115573 115574 115575 115576 115577 115578 115579 115580 115581 115582 115583 115584 115585 115586 115587 115588 115589 115590 115591 115592 115593 115594 115595 115596 115597 115598 115599 115600 115601 115602 115603 115604 115605 115606 115607 115608 115609 115610 115611 115612 115613 115614 115615 115616 115617 115618 115619 115620 115621 115622 115623 115624 115625 115626 115627 115628 115629 115630 115631 115632 115633 115634 115635 115636 115637 115638 115639 115640 115641 115642 115643 115644 115645 115646 115647 115648 115649 115650 115651 115652 115653 115654 115655 115656 115657 115658 115659 115660 115661 115662 115663 115664 115665 115666 115667 115668 115669 115670 115671 115672 115673 115674 115675 115676 115677 115678 115679 115680 115681 115682 115683 115684 115685 115686 115687 115688 115689 115690 115691 115692 115693 115694 115695 115696 115697 115698 115699 115700 115701 115702 115703 115704 115705 115706 115707 115708 115709 115710 115711 115712 115713 115714 115715 115716 115717 115718 115719 115720 115721 115722 115723 115724 115725 115726 115727 115728 115729 115730 115731 115732 115733 115734 115735 115736 115737 115738 115739 115740 115741 115742 115743 115744 115745 115746 115747 115748 115749 115750 115751 115752 115753 115754 115755 115756 115757 115758 115759 115760 115761 115762 115763 115764 115765 115766 115767 115768 115769 115770 115771 115772 115773 115774 115775 115776 115777 115778 115779 115780 115781 115782 115783 115784 115785 115786 115787 115788 115789 115790 115791 115792 115793 115794 115795 115796 115797 115798 115799 115800 115801 115802 115803 115804 115805 115806 115807 115808 115809 115810 115811 115812 115813 115814 115815 115816 115817 115818 115819 115820 115821 115822 115823 115824 115825 115826 115827 115828 115829 115830 115831 115832 115833 115834 115835 115836 115837 115838 115839 115840 115841 115842 115843 115844 115845 115846 115847 115848 115849 115850 115851 115852 115853 115854 115855 115856 115857 115858 115859 115860 115861 115862 115863 115864 115865 115866 115867 115868 115869 115870 115871 115872 115873 115874 115875 115876 115877 115878 115879 115880 115881 115882 115883 115884 115885 115886 115887 115888 115889 115890 115891 115892 115893 115894 115895 115896 115897 115898 115899 115900 115901 115902 115903 115904 115905 115906 115907 115908 115909 115910 115911 115912 115913 115914 115915 115916 115917 115918 115919 115920 115921 115922 115923 115924 115925 115926 115927 115928 115929 115930 115931 115932 115933 115934 115935 115936 115937 115938 115939 115940 115941 115942 115943 115944 115945 115946 115947 115948 115949 115950 115951 115952 115953 115954 115955 115956 115957 115958 115959 115960 115961 115962 115963 115964 115965 115966 115967 115968 115969 115970 115971 115972 115973 115974 115975 115976 115977 115978 115979 115980 115981 115982 115983 115984 115985 115986 115987 115988 115989 115990 115991 115992 115993 115994 115995 115996 115997 115998 115999 116000 116001 116002 116003 116004 116005 116006 116007 116008 116009 116010 116011 116012 116013 116014 116015 116016 116017 116018 116019 116020 116021 116022 116023 116024 116025 116026 116027 116028 116029 116030 116031 116032 116033 116034 116035 116036 116037 116038 116039 116040 116041 116042 116043 116044 116045 116046 116047 116048 116049 116050 116051 116052 116053 116054 116055 116056 116057 116058 116059 116060 116061 116062 116063 116064 116065 116066 116067 116068 116069 116070 116071 116072 116073 116074 116075 116076 116077 116078 116079 116080 116081 116082 116083 116084 116085 116086 116087 116088 116089 116090 116091 116092 116093 116094 116095 116096 116097 116098 116099 116100 116101 116102 116103 116104 116105 116106 116107 116108 116109 116110 116111 116112 116113 116114 116115 116116 116117 116118 116119 116120 116121 116122 116123 116124 116125 116126 116127 116128 116129 116130 116131 116132 116133 116134 116135 116136 116137 116138 116139 116140 116141 116142 116143 116144 116145 116146 116147 116148 116149 116150 116151 116152 116153 116154 116155 116156 116157 116158 116159 116160 116161 116162 116163 116164 116165 116166 116167 116168 116169 116170 116171 116172 116173 116174 116175 116176 116177 116178 116179 116180 116181 116182 116183 116184 116185 116186 116187 116188 116189 116190 116191 116192 116193 116194 116195 116196 116197 116198 116199 116200 116201 116202 116203 116204 116205 116206 116207 116208 116209 116210 116211 116212 116213 116214 116215 116216 116217 116218 116219 116220 116221 116222 116223 116224 116225 116226 116227 116228 116229 116230 116231 116232 116233 116234 116235 116236 116237 116238 116239 116240 116241 116242 116243 116244 116245 116246 116247 116248 116249 116250 116251 116252 116253 116254 116255 116256 116257 116258 116259 116260 116261 116262 116263 116264 116265 116266 116267 116268 116269 116270 116271 116272 116273 116274 116275 116276 116277 116278 116279 116280 116281 116282 116283 116284 116285 116286 116287 116288 116289 116290 116291 116292 116293 116294 116295 116296 116297 116298 116299 116300 116301 116302 116303 116304 116305 116306 116307 116308 116309 116310 116311 116312 116313 116314 116315 116316 116317 116318 116319 116320 116321 116322 116323 116324 116325 116326 116327 116328 |
const sqlite3_api_routines *pApi
){
SQLITE_EXTENSION_INIT2(pApi)
return sqlite3Fts3Init(db);
}
#endif
/*
** Allocate an Fts3MultiSegReader for each token in the expression headed
** by pExpr.
**
** An Fts3SegReader object is a cursor that can seek or scan a range of
** entries within a single segment b-tree. An Fts3MultiSegReader uses multiple
** Fts3SegReader objects internally to provide an interface to seek or scan
** within the union of all segments of a b-tree. Hence the name.
**
** If the allocated Fts3MultiSegReader just seeks to a single entry in a
** segment b-tree (if the term is not a prefix or it is a prefix for which
** there exists prefix b-tree of the right length) then it may be traversed
** and merged incrementally. Otherwise, it has to be merged into an in-memory
** doclist and then traversed.
*/
static void fts3EvalAllocateReaders(
Fts3Cursor *pCsr,
Fts3Expr *pExpr,
int *pnToken, /* OUT: Total number of tokens in phrase. */
int *pnOr, /* OUT: Total number of OR nodes in expr. */
int *pRc
){
if( pExpr && SQLITE_OK==*pRc ){
if( pExpr->eType==FTSQUERY_PHRASE ){
int i;
int nToken = pExpr->pPhrase->nToken;
*pnToken += nToken;
for(i=0; i<nToken; i++){
Fts3PhraseToken *pToken = &pExpr->pPhrase->aToken[i];
int rc = sqlite3Fts3TermSegReaderCursor(pCsr,
pToken->z, pToken->n, pToken->isPrefix, &pToken->pSegcsr
);
if( rc!=SQLITE_OK ){
*pRc = rc;
return;
}
}
}else{
*pnOr += (pExpr->eType==FTSQUERY_OR);
fts3EvalAllocateReaders(pCsr, pExpr->pLeft, pnToken, pnOr, pRc);
fts3EvalAllocateReaders(pCsr, pExpr->pRight, pnToken, pnOr, pRc);
}
}
}
static int fts3EvalPhraseLoad(
Fts3Cursor *pCsr,
Fts3Phrase *p
){
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
int iToken;
int rc = SQLITE_OK;
char *aDoclist = 0;
int nDoclist = 0;
int iPrev = -1;
for(iToken=0; rc==SQLITE_OK && iToken<p->nToken; iToken++){
Fts3PhraseToken *pToken = &p->aToken[iToken];
assert( pToken->pSegcsr || pToken->pDeferred );
if( pToken->pDeferred==0 ){
int nThis = 0;
char *pThis = 0;
rc = fts3TermSelect(pTab, pToken, p->iColumn, 1, &nThis, &pThis);
if( rc==SQLITE_OK ){
if( pThis==0 ){
sqlite3_free(aDoclist);
aDoclist = 0;
nDoclist = 0;
break;
}else if( aDoclist==0 ){
aDoclist = pThis;
nDoclist = nThis;
}else{
assert( iPrev>=0 );
fts3DoclistPhraseMerge(pTab->bDescIdx,
iToken-iPrev, aDoclist, nDoclist, pThis, &nThis
);
sqlite3_free(aDoclist);
aDoclist = pThis;
nDoclist = nThis;
}
iPrev = iToken;
}
}
}
if( rc==SQLITE_OK ){
p->doclist.aAll = aDoclist;
p->doclist.nAll = nDoclist;
}else{
sqlite3_free(aDoclist);
}
return rc;
}
static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
int iToken;
int rc = SQLITE_OK;
int nMaxUndeferred = -1;
char *aPoslist = 0;
int nPoslist = 0;
int iPrev = -1;
assert( pPhrase->doclist.bFreeList==0 );
for(iToken=0; rc==SQLITE_OK && iToken<pPhrase->nToken; iToken++){
Fts3PhraseToken *pToken = &pPhrase->aToken[iToken];
Fts3DeferredToken *pDeferred = pToken->pDeferred;
if( pDeferred ){
char *pList;
int nList;
rc = sqlite3Fts3DeferredTokenList(pDeferred, &pList, &nList);
if( rc!=SQLITE_OK ) return rc;
if( pList==0 ){
sqlite3_free(aPoslist);
pPhrase->doclist.pList = 0;
pPhrase->doclist.nList = 0;
return SQLITE_OK;
}else if( aPoslist==0 ){
aPoslist = pList;
nPoslist = nList;
}else{
char *aOut = pList;
char *p1 = aPoslist;
char *p2 = aOut;
assert( iPrev>=0 );
fts3PoslistPhraseMerge(&aOut, iToken-iPrev, 0, 1, &p1, &p2);
sqlite3_free(aPoslist);
aPoslist = pList;
nPoslist = aOut - aPoslist;
if( nPoslist==0 ){
sqlite3_free(aPoslist);
pPhrase->doclist.pList = 0;
pPhrase->doclist.nList = 0;
return SQLITE_OK;
}
}
iPrev = iToken;
}else{
nMaxUndeferred = iToken;
}
}
if( iPrev>=0 ){
if( nMaxUndeferred<0 ){
pPhrase->doclist.pList = aPoslist;
pPhrase->doclist.nList = nPoslist;
pPhrase->doclist.iDocid = pCsr->iPrevId;
pPhrase->doclist.bFreeList = 1;
}else{
int nDistance;
char *p1;
char *p2;
char *aOut;
if( nMaxUndeferred>iPrev ){
p1 = aPoslist;
p2 = pPhrase->doclist.pList;
nDistance = nMaxUndeferred - iPrev;
}else{
p1 = pPhrase->doclist.pList;
p2 = aPoslist;
nDistance = iPrev - nMaxUndeferred;
}
aOut = (char *)sqlite3_malloc(nPoslist+8);
if( !aOut ){
sqlite3_free(aPoslist);
return SQLITE_NOMEM;
}
pPhrase->doclist.pList = aOut;
if( fts3PoslistPhraseMerge(&aOut, nDistance, 0, 1, &p1, &p2) ){
pPhrase->doclist.bFreeList = 1;
pPhrase->doclist.nList = (aOut - pPhrase->doclist.pList);
}else{
sqlite3_free(aOut);
pPhrase->doclist.pList = 0;
pPhrase->doclist.nList = 0;
}
sqlite3_free(aPoslist);
}
}
return SQLITE_OK;
}
/*
** This function is called for each Fts3Phrase in a full-text query
** expression to initialize the mechanism for returning rows. Once this
** function has been called successfully on an Fts3Phrase, it may be
** used with fts3EvalPhraseNext() to iterate through the matching docids.
*/
static int fts3EvalPhraseStart(Fts3Cursor *pCsr, int bOptOk, Fts3Phrase *p){
int rc;
Fts3PhraseToken *pFirst = &p->aToken[0];
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
assert( p->doclist.aAll==0 );
if( pCsr->bDesc==pTab->bDescIdx && bOptOk==1 && p->nToken==1
&& pFirst->pSegcsr && pFirst->pSegcsr->bLookup
){
/* Use the incremental approach. */
int iCol = (p->iColumn >= pTab->nColumn ? -1 : p->iColumn);
rc = sqlite3Fts3MsrIncrStart(
pTab, pFirst->pSegcsr, iCol, pFirst->z, pFirst->n);
p->bIncr = 1;
}else{
/* Load the full doclist for the phrase into memory. */
rc = fts3EvalPhraseLoad(pCsr, p);
p->bIncr = 0;
}
assert( rc!=SQLITE_OK || p->nToken<1 || p->aToken[0].pSegcsr==0 || p->bIncr );
return rc;
}
/*
** This function is used to iterate backwards (from the end to start)
** through doclists.
*/
SQLITE_PRIVATE void sqlite3Fts3DoclistPrev(
int bDescIdx, /* True if the doclist is desc */
char *aDoclist, /* Pointer to entire doclist */
int nDoclist, /* Length of aDoclist in bytes */
char **ppIter, /* IN/OUT: Iterator pointer */
sqlite3_int64 *piDocid, /* IN/OUT: Docid pointer */
int *pnList, /* IN/OUT: List length pointer */
u8 *pbEof /* OUT: End-of-file flag */
){
char *p = *ppIter;
assert( nDoclist>0 );
assert( *pbEof==0 );
assert( p || *piDocid==0 );
assert( !p || (p>aDoclist && p<&aDoclist[nDoclist]) );
if( p==0 ){
sqlite3_int64 iDocid = 0;
char *pNext = 0;
char *pDocid = aDoclist;
char *pEnd = &aDoclist[nDoclist];
int iMul = 1;
while( pDocid<pEnd ){
sqlite3_int64 iDelta;
pDocid += sqlite3Fts3GetVarint(pDocid, &iDelta);
iDocid += (iMul * iDelta);
pNext = pDocid;
fts3PoslistCopy(0, &pDocid);
while( pDocid<pEnd && *pDocid==0 ) pDocid++;
iMul = (bDescIdx ? -1 : 1);
}
*pnList = pEnd - pNext;
*ppIter = pNext;
*piDocid = iDocid;
}else{
int iMul = (bDescIdx ? -1 : 1);
sqlite3_int64 iDelta;
fts3GetReverseVarint(&p, aDoclist, &iDelta);
*piDocid -= (iMul * iDelta);
if( p==aDoclist ){
*pbEof = 1;
}else{
char *pSave = p;
fts3ReversePoslist(aDoclist, &p);
*pnList = (pSave - p);
}
*ppIter = p;
}
}
/*
** Attempt to move the phrase iterator to point to the next matching docid.
** If an error occurs, return an SQLite error code. Otherwise, return
** SQLITE_OK.
**
** If there is no "next" entry and no error occurs, then *pbEof is set to
** 1 before returning. Otherwise, if no error occurs and the iterator is
** successfully advanced, *pbEof is set to 0.
*/
static int fts3EvalPhraseNext(
Fts3Cursor *pCsr,
Fts3Phrase *p,
u8 *pbEof
){
int rc = SQLITE_OK;
Fts3Doclist *pDL = &p->doclist;
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
if( p->bIncr ){
assert( p->nToken==1 );
assert( pDL->pNextDocid==0 );
rc = sqlite3Fts3MsrIncrNext(pTab, p->aToken[0].pSegcsr,
&pDL->iDocid, &pDL->pList, &pDL->nList
);
if( rc==SQLITE_OK && !pDL->pList ){
*pbEof = 1;
}
}else if( pCsr->bDesc!=pTab->bDescIdx && pDL->nAll ){
sqlite3Fts3DoclistPrev(pTab->bDescIdx, pDL->aAll, pDL->nAll,
&pDL->pNextDocid, &pDL->iDocid, &pDL->nList, pbEof
);
pDL->pList = pDL->pNextDocid;
}else{
char *pIter; /* Used to iterate through aAll */
char *pEnd = &pDL->aAll[pDL->nAll]; /* 1 byte past end of aAll */
if( pDL->pNextDocid ){
pIter = pDL->pNextDocid;
}else{
pIter = pDL->aAll;
}
if( pIter>=pEnd ){
/* We have already reached the end of this doclist. EOF. */
*pbEof = 1;
}else{
sqlite3_int64 iDelta;
pIter += sqlite3Fts3GetVarint(pIter, &iDelta);
if( pTab->bDescIdx==0 || pDL->pNextDocid==0 ){
pDL->iDocid += iDelta;
}else{
pDL->iDocid -= iDelta;
}
pDL->pList = pIter;
fts3PoslistCopy(0, &pIter);
pDL->nList = (pIter - pDL->pList);
/* pIter now points just past the 0x00 that terminates the position-
** list for document pDL->iDocid. However, if this position-list was
** edited in place by fts3EvalNearTrim2(), then pIter may not actually
** point to the start of the next docid value. The following line deals
** with this case by advancing pIter past the zero-padding added by
** fts3EvalNearTrim2(). */
while( pIter<pEnd && *pIter==0 ) pIter++;
pDL->pNextDocid = pIter;
assert( *pIter || pIter>=&pDL->aAll[pDL->nAll] );
*pbEof = 0;
}
}
return rc;
}
static void fts3EvalStartReaders(
Fts3Cursor *pCsr,
Fts3Expr *pExpr,
int bOptOk,
int *pRc
){
if( pExpr && SQLITE_OK==*pRc ){
if( pExpr->eType==FTSQUERY_PHRASE ){
int i;
int nToken = pExpr->pPhrase->nToken;
for(i=0; i<nToken; i++){
if( pExpr->pPhrase->aToken[i].pDeferred==0 ) break;
}
pExpr->bDeferred = (i==nToken);
*pRc = fts3EvalPhraseStart(pCsr, bOptOk, pExpr->pPhrase);
}else{
fts3EvalStartReaders(pCsr, pExpr->pLeft, bOptOk, pRc);
fts3EvalStartReaders(pCsr, pExpr->pRight, bOptOk, pRc);
pExpr->bDeferred = (pExpr->pLeft->bDeferred && pExpr->pRight->bDeferred);
}
}
}
typedef struct Fts3TokenAndCost Fts3TokenAndCost;
struct Fts3TokenAndCost {
Fts3PhraseToken *pToken;
Fts3Expr *pRoot;
int nOvfl;
int iCol;
};
static void fts3EvalTokenCosts(
Fts3Cursor *pCsr,
Fts3Expr *pRoot,
Fts3Expr *pExpr,
Fts3TokenAndCost **ppTC,
Fts3Expr ***ppOr,
int *pRc
){
if( *pRc==SQLITE_OK && pExpr ){
if( pExpr->eType==FTSQUERY_PHRASE ){
Fts3Phrase *pPhrase = pExpr->pPhrase;
int i;
for(i=0; *pRc==SQLITE_OK && i<pPhrase->nToken; i++){
Fts3TokenAndCost *pTC = (*ppTC)++;
pTC->pRoot = pRoot;
pTC->pToken = &pPhrase->aToken[i];
pTC->iCol = pPhrase->iColumn;
*pRc = sqlite3Fts3MsrOvfl(pCsr, pTC->pToken->pSegcsr, &pTC->nOvfl);
}
}else if( pExpr->eType!=FTSQUERY_NOT ){
if( pExpr->eType==FTSQUERY_OR ){
pRoot = pExpr->pLeft;
**ppOr = pRoot;
(*ppOr)++;
}
fts3EvalTokenCosts(pCsr, pRoot, pExpr->pLeft, ppTC, ppOr, pRc);
if( pExpr->eType==FTSQUERY_OR ){
pRoot = pExpr->pRight;
**ppOr = pRoot;
(*ppOr)++;
}
fts3EvalTokenCosts(pCsr, pRoot, pExpr->pRight, ppTC, ppOr, pRc);
}
}
}
static int fts3EvalAverageDocsize(Fts3Cursor *pCsr, int *pnPage){
if( pCsr->nRowAvg==0 ){
/* The average document size, which is required to calculate the cost
** of each doclist, has not yet been determined. Read the required
** data from the %_stat table to calculate it.
**
** Entry 0 of the %_stat table is a blob containing (nCol+1) FTS3
** varints, where nCol is the number of columns in the FTS3 table.
** The first varint is the number of documents currently stored in
** the table. The following nCol varints contain the total amount of
** data stored in all rows of each column of the table, from left
** to right.
*/
int rc;
Fts3Table *p = (Fts3Table*)pCsr->base.pVtab;
sqlite3_stmt *pStmt;
sqlite3_int64 nDoc = 0;
sqlite3_int64 nByte = 0;
const char *pEnd;
const char *a;
rc = sqlite3Fts3SelectDoctotal(p, &pStmt);
if( rc!=SQLITE_OK ) return rc;
a = sqlite3_column_blob(pStmt, 0);
assert( a );
pEnd = &a[sqlite3_column_bytes(pStmt, 0)];
a += sqlite3Fts3GetVarint(a, &nDoc);
while( a<pEnd ){
a += sqlite3Fts3GetVarint(a, &nByte);
}
if( nDoc==0 || nByte==0 ){
sqlite3_reset(pStmt);
return SQLITE_CORRUPT_VTAB;
}
pCsr->nDoc = nDoc;
pCsr->nRowAvg = (int)(((nByte / nDoc) + p->nPgsz) / p->nPgsz);
assert( pCsr->nRowAvg>0 );
rc = sqlite3_reset(pStmt);
if( rc!=SQLITE_OK ) return rc;
}
*pnPage = pCsr->nRowAvg;
return SQLITE_OK;
}
static int fts3EvalSelectDeferred(
Fts3Cursor *pCsr,
Fts3Expr *pRoot,
Fts3TokenAndCost *aTC,
int nTC
){
int nDocSize = 0;
int nDocEst = 0;
int rc = SQLITE_OK;
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
int ii;
int nOvfl = 0;
int nTerm = 0;
for(ii=0; ii<nTC; ii++){
if( aTC[ii].pRoot==pRoot ){
nOvfl += aTC[ii].nOvfl;
nTerm++;
}
}
if( nOvfl==0 || nTerm<2 ) return SQLITE_OK;
rc = fts3EvalAverageDocsize(pCsr, &nDocSize);
for(ii=0; ii<nTerm && rc==SQLITE_OK; ii++){
int jj;
Fts3TokenAndCost *pTC = 0;
for(jj=0; jj<nTC; jj++){
if( aTC[jj].pToken && aTC[jj].pRoot==pRoot
&& (!pTC || aTC[jj].nOvfl<pTC->nOvfl)
){
pTC = &aTC[jj];
}
}
assert( pTC );
/* At this point pTC points to the cheapest remaining token. */
if( ii==0 ){
if( pTC->nOvfl ){
nDocEst = (pTC->nOvfl * pTab->nPgsz + pTab->nPgsz) / 10;
}else{
/* TODO: Fix this so that the doclist need not be read twice. */
Fts3PhraseToken *pToken = pTC->pToken;
int nList = 0;
char *pList = 0;
rc = fts3TermSelect(pTab, pToken, pTC->iCol, 1, &nList, &pList);
if( rc==SQLITE_OK ){
nDocEst = fts3DoclistCountDocids(1, pList, nList);
}
sqlite3_free(pList);
if( rc==SQLITE_OK ){
rc = sqlite3Fts3TermSegReaderCursor(pCsr,
pToken->z, pToken->n, pToken->isPrefix, &pToken->pSegcsr
);
}
}
}else{
if( pTC->nOvfl>=(nDocEst*nDocSize) ){
Fts3PhraseToken *pToken = pTC->pToken;
rc = sqlite3Fts3DeferToken(pCsr, pToken, pTC->iCol);
fts3SegReaderCursorFree(pToken->pSegcsr);
pToken->pSegcsr = 0;
}
nDocEst = 1 + (nDocEst/4);
}
pTC->pToken = 0;
}
return rc;
}
SQLITE_PRIVATE int sqlite3Fts3EvalStart(Fts3Cursor *pCsr, Fts3Expr *pExpr, int bOptOk){
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
int rc = SQLITE_OK;
int nToken = 0;
int nOr = 0;
/* Allocate a MultiSegReader for each token in the expression. */
fts3EvalAllocateReaders(pCsr, pExpr, &nToken, &nOr, &rc);
/* Call fts3EvalPhraseStart() on all phrases in the expression. TODO:
** This call will eventually also be responsible for determining which
** tokens are 'deferred' until the document text is loaded into memory.
**
** Each token in each phrase is dealt with using one of the following
** three strategies:
**
** 1. Entire doclist loaded into memory as part of the
** fts3EvalStartReaders() call.
**
** 2. Doclist loaded into memory incrementally, as part of each
** sqlite3Fts3EvalNext() call.
**
** 3. Token doclist is never loaded. Instead, documents are loaded into
** memory and scanned for the token as part of the sqlite3Fts3EvalNext()
** call. This is known as a "deferred" token.
*/
/* If bOptOk is true, check if there are any tokens that should be deferred.
*/
if( rc==SQLITE_OK && bOptOk && nToken>1 && pTab->bHasStat ){
Fts3TokenAndCost *aTC;
Fts3Expr **apOr;
aTC = (Fts3TokenAndCost *)sqlite3_malloc(
sizeof(Fts3TokenAndCost) * nToken
+ sizeof(Fts3Expr *) * nOr * 2
);
apOr = (Fts3Expr **)&aTC[nToken];
if( !aTC ){
rc = SQLITE_NOMEM;
}else{
int ii;
Fts3TokenAndCost *pTC = aTC;
Fts3Expr **ppOr = apOr;
fts3EvalTokenCosts(pCsr, 0, pExpr, &pTC, &ppOr, &rc);
nToken = pTC-aTC;
nOr = ppOr-apOr;
if( rc==SQLITE_OK ){
rc = fts3EvalSelectDeferred(pCsr, 0, aTC, nToken);
for(ii=0; rc==SQLITE_OK && ii<nOr; ii++){
rc = fts3EvalSelectDeferred(pCsr, apOr[ii], aTC, nToken);
}
}
sqlite3_free(aTC);
}
}
fts3EvalStartReaders(pCsr, pExpr, bOptOk, &rc);
return rc;
}
static void fts3EvalZeroPoslist(Fts3Phrase *pPhrase){
if( pPhrase->doclist.bFreeList ){
sqlite3_free(pPhrase->doclist.pList);
}
pPhrase->doclist.pList = 0;
pPhrase->doclist.nList = 0;
pPhrase->doclist.bFreeList = 0;
}
static int fts3EvalNearTrim2(
int nNear,
char *aTmp, /* Temporary space to use */
char **paPoslist, /* IN/OUT: Position list */
int *pnToken, /* IN/OUT: Tokens in phrase of *paPoslist */
Fts3Phrase *pPhrase /* The phrase object to trim the doclist of */
){
int nParam1 = nNear + pPhrase->nToken;
int nParam2 = nNear + *pnToken;
int nNew;
char *p2;
char *pOut;
int res;
assert( pPhrase->doclist.pList );
p2 = pOut = pPhrase->doclist.pList;
res = fts3PoslistNearMerge(
&pOut, aTmp, nParam1, nParam2, paPoslist, &p2
);
if( res ){
nNew = (pOut - pPhrase->doclist.pList) - 1;
assert( pPhrase->doclist.pList[nNew]=='\0' );
assert( nNew<=pPhrase->doclist.nList && nNew>0 );
memset(&pPhrase->doclist.pList[nNew], 0, pPhrase->doclist.nList - nNew);
pPhrase->doclist.nList = nNew;
*paPoslist = pPhrase->doclist.pList;
*pnToken = pPhrase->nToken;
}
return res;
}
static int fts3EvalNearTest(Fts3Expr *pExpr, int *pRc){
int res = 1;
/* The following block runs if pExpr is the root of a NEAR query.
** For example, the query:
**
** "w" NEAR "x" NEAR "y" NEAR "z"
**
** which is represented in tree form as:
**
** |
** +--NEAR--+ <-- root of NEAR query
** | |
** +--NEAR--+ "z"
** | |
** +--NEAR--+ "y"
** | |
** "w" "x"
**
** The right-hand child of a NEAR node is always a phrase. The
** left-hand child may be either a phrase or a NEAR node. There are
** no exceptions to this.
*/
if( *pRc==SQLITE_OK
&& pExpr->eType==FTSQUERY_NEAR
&& pExpr->bEof==0
&& (pExpr->pParent==0 || pExpr->pParent->eType!=FTSQUERY_NEAR)
){
Fts3Expr *p;
int nTmp = 0; /* Bytes of temp space */
char *aTmp; /* Temp space for PoslistNearMerge() */
/* Allocate temporary working space. */
for(p=pExpr; p->pLeft; p=p->pLeft){
nTmp += p->pRight->pPhrase->doclist.nList;
}
nTmp += p->pPhrase->doclist.nList;
aTmp = sqlite3_malloc(nTmp*2);
if( !aTmp ){
*pRc = SQLITE_NOMEM;
res = 0;
}else{
char *aPoslist = p->pPhrase->doclist.pList;
int nToken = p->pPhrase->nToken;
for(p=p->pParent;res && p && p->eType==FTSQUERY_NEAR; p=p->pParent){
Fts3Phrase *pPhrase = p->pRight->pPhrase;
int nNear = p->nNear;
res = fts3EvalNearTrim2(nNear, aTmp, &aPoslist, &nToken, pPhrase);
}
aPoslist = pExpr->pRight->pPhrase->doclist.pList;
nToken = pExpr->pRight->pPhrase->nToken;
for(p=pExpr->pLeft; p && res; p=p->pLeft){
int nNear = p->pParent->nNear;
Fts3Phrase *pPhrase = (
p->eType==FTSQUERY_NEAR ? p->pRight->pPhrase : p->pPhrase
);
res = fts3EvalNearTrim2(nNear, aTmp, &aPoslist, &nToken, pPhrase);
}
}
sqlite3_free(aTmp);
}
return res;
}
/*
** This macro is used by the fts3EvalNext() function. The two arguments are
** 64-bit docid values. If the current query is "ORDER BY docid ASC", then
** the macro returns (i1 - i2). Or if it is "ORDER BY docid DESC", then
** it returns (i2 - i1). This allows the same code to be used for merging
** doclists in ascending or descending order.
*/
#define DOCID_CMP(i1, i2) ((pCsr->bDesc?-1:1) * (i1-i2))
static void fts3EvalNext(
Fts3Cursor *pCsr,
Fts3Expr *pExpr,
int *pRc
){
if( *pRc==SQLITE_OK ){
assert( pExpr->bEof==0 );
pExpr->bStart = 1;
switch( pExpr->eType ){
case FTSQUERY_NEAR:
case FTSQUERY_AND: {
Fts3Expr *pLeft = pExpr->pLeft;
Fts3Expr *pRight = pExpr->pRight;
assert( !pLeft->bDeferred || !pRight->bDeferred );
if( pLeft->bDeferred ){
fts3EvalNext(pCsr, pRight, pRc);
pExpr->iDocid = pRight->iDocid;
pExpr->bEof = pRight->bEof;
}else if( pRight->bDeferred ){
fts3EvalNext(pCsr, pLeft, pRc);
pExpr->iDocid = pLeft->iDocid;
pExpr->bEof = pLeft->bEof;
}else{
fts3EvalNext(pCsr, pLeft, pRc);
fts3EvalNext(pCsr, pRight, pRc);
while( !pLeft->bEof && !pRight->bEof && *pRc==SQLITE_OK ){
sqlite3_int64 iDiff = DOCID_CMP(pLeft->iDocid, pRight->iDocid);
if( iDiff==0 ) break;
if( iDiff<0 ){
fts3EvalNext(pCsr, pLeft, pRc);
}else{
fts3EvalNext(pCsr, pRight, pRc);
}
}
pExpr->iDocid = pLeft->iDocid;
pExpr->bEof = (pLeft->bEof || pRight->bEof);
}
break;
}
case FTSQUERY_OR: {
Fts3Expr *pLeft = pExpr->pLeft;
Fts3Expr *pRight = pExpr->pRight;
sqlite3_int64 iCmp = DOCID_CMP(pLeft->iDocid, pRight->iDocid);
assert( pLeft->bStart || pLeft->iDocid==pRight->iDocid );
assert( pRight->bStart || pLeft->iDocid==pRight->iDocid );
if( pRight->bEof || (pLeft->bEof==0 && iCmp<0) ){
fts3EvalNext(pCsr, pLeft, pRc);
}else if( pLeft->bEof || (pRight->bEof==0 && iCmp>0) ){
fts3EvalNext(pCsr, pRight, pRc);
}else{
fts3EvalNext(pCsr, pLeft, pRc);
fts3EvalNext(pCsr, pRight, pRc);
}
pExpr->bEof = (pLeft->bEof && pRight->bEof);
iCmp = DOCID_CMP(pLeft->iDocid, pRight->iDocid);
if( pRight->bEof || (pLeft->bEof==0 && iCmp<0) ){
pExpr->iDocid = pLeft->iDocid;
}else{
pExpr->iDocid = pRight->iDocid;
}
break;
}
case FTSQUERY_NOT: {
Fts3Expr *pLeft = pExpr->pLeft;
Fts3Expr *pRight = pExpr->pRight;
if( pRight->bStart==0 ){
fts3EvalNext(pCsr, pRight, pRc);
assert( *pRc!=SQLITE_OK || pRight->bStart );
}
fts3EvalNext(pCsr, pLeft, pRc);
if( pLeft->bEof==0 ){
while( !*pRc
&& !pRight->bEof
&& DOCID_CMP(pLeft->iDocid, pRight->iDocid)>0
){
fts3EvalNext(pCsr, pRight, pRc);
}
}
pExpr->iDocid = pLeft->iDocid;
pExpr->bEof = pLeft->bEof;
break;
}
default: {
Fts3Phrase *pPhrase = pExpr->pPhrase;
fts3EvalZeroPoslist(pPhrase);
*pRc = fts3EvalPhraseNext(pCsr, pPhrase, &pExpr->bEof);
pExpr->iDocid = pPhrase->doclist.iDocid;
break;
}
}
}
}
static int fts3EvalDeferredTest(Fts3Cursor *pCsr, Fts3Expr *pExpr, int *pRc){
int bHit = 1;
if( *pRc==SQLITE_OK ){
switch( pExpr->eType ){
case FTSQUERY_NEAR:
case FTSQUERY_AND:
bHit = (
fts3EvalDeferredTest(pCsr, pExpr->pLeft, pRc)
&& fts3EvalDeferredTest(pCsr, pExpr->pRight, pRc)
&& fts3EvalNearTest(pExpr, pRc)
);
/* If the NEAR expression does not match any rows, zero the doclist for
** all phrases involved in the NEAR. This is because the snippet(),
** offsets() and matchinfo() functions are not supposed to recognize
** any instances of phrases that are part of unmatched NEAR queries.
** For example if this expression:
**
** ... MATCH 'a OR (b NEAR c)'
**
** is matched against a row containing:
**
** 'a b d e'
**
** then any snippet() should ony highlight the "a" term, not the "b"
** (as "b" is part of a non-matching NEAR clause).
*/
if( bHit==0
&& pExpr->eType==FTSQUERY_NEAR
&& (pExpr->pParent==0 || pExpr->pParent->eType!=FTSQUERY_NEAR)
){
Fts3Expr *p;
for(p=pExpr; p->pPhrase==0; p=p->pLeft){
if( p->pRight->iDocid==pCsr->iPrevId ){
fts3EvalZeroPoslist(p->pRight->pPhrase);
}
}
if( p->iDocid==pCsr->iPrevId ){
fts3EvalZeroPoslist(p->pPhrase);
}
}
break;
case FTSQUERY_OR: {
int bHit1 = fts3EvalDeferredTest(pCsr, pExpr->pLeft, pRc);
int bHit2 = fts3EvalDeferredTest(pCsr, pExpr->pRight, pRc);
bHit = bHit1 || bHit2;
break;
}
case FTSQUERY_NOT:
bHit = (
fts3EvalDeferredTest(pCsr, pExpr->pLeft, pRc)
&& !fts3EvalDeferredTest(pCsr, pExpr->pRight, pRc)
);
break;
default: {
if( pCsr->pDeferred
&& (pExpr->iDocid==pCsr->iPrevId || pExpr->bDeferred)
){
Fts3Phrase *pPhrase = pExpr->pPhrase;
assert( pExpr->bDeferred || pPhrase->doclist.bFreeList==0 );
if( pExpr->bDeferred ){
fts3EvalZeroPoslist(pPhrase);
}
*pRc = fts3EvalDeferredPhrase(pCsr, pPhrase);
bHit = (pPhrase->doclist.pList!=0);
pExpr->iDocid = pCsr->iPrevId;
}else{
bHit = (pExpr->bEof==0 && pExpr->iDocid==pCsr->iPrevId);
}
break;
}
}
}
return bHit;
}
/*
** Return 1 if both of the following are true:
**
** 1. *pRc is SQLITE_OK when this function returns, and
**
** 2. After scanning the current FTS table row for the deferred tokens,
** it is determined that the row does not match the query.
**
** Or, if no error occurs and it seems the current row does match the FTS
** query, return 0.
*/
static int fts3EvalLoadDeferred(Fts3Cursor *pCsr, int *pRc){
int rc = *pRc;
int bMiss = 0;
if( rc==SQLITE_OK ){
if( pCsr->pDeferred ){
rc = fts3CursorSeek(0, pCsr);
if( rc==SQLITE_OK ){
rc = sqlite3Fts3CacheDeferredDoclists(pCsr);
}
}
bMiss = (0==fts3EvalDeferredTest(pCsr, pCsr->pExpr, &rc));
sqlite3Fts3FreeDeferredDoclists(pCsr);
*pRc = rc;
}
return (rc==SQLITE_OK && bMiss);
}
/*
** Advance to the next document that matches the FTS expression in
** Fts3Cursor.pExpr.
*/
SQLITE_PRIVATE int sqlite3Fts3EvalNext(Fts3Cursor *pCsr){
int rc = SQLITE_OK; /* Return Code */
Fts3Expr *pExpr = pCsr->pExpr;
assert( pCsr->isEof==0 );
if( pExpr==0 ){
pCsr->isEof = 1;
}else{
do {
if( pCsr->isRequireSeek==0 ){
sqlite3_reset(pCsr->pStmt);
}
assert( sqlite3_data_count(pCsr->pStmt)==0 );
fts3EvalNext(pCsr, pExpr, &rc);
pCsr->isEof = pExpr->bEof;
pCsr->isRequireSeek = 1;
pCsr->isMatchinfoNeeded = 1;
pCsr->iPrevId = pExpr->iDocid;
}while( pCsr->isEof==0 && fts3EvalLoadDeferred(pCsr, &rc) );
}
return rc;
}
/*
** Restart interation for expression pExpr so that the next call to
** sqlite3Fts3EvalNext() visits the first row. Do not allow incremental
** loading or merging of phrase doclists for this iteration.
**
** If *pRc is other than SQLITE_OK when this function is called, it is
** a no-op. If an error occurs within this function, *pRc is set to an
** SQLite error code before returning.
*/
static void fts3EvalRestart(
Fts3Cursor *pCsr,
Fts3Expr *pExpr,
int *pRc
){
if( pExpr && *pRc==SQLITE_OK ){
Fts3Phrase *pPhrase = pExpr->pPhrase;
if( pPhrase ){
fts3EvalZeroPoslist(pPhrase);
if( pPhrase->bIncr ){
sqlite3Fts3EvalPhraseCleanup(pPhrase);
memset(&pPhrase->doclist, 0, sizeof(Fts3Doclist));
*pRc = sqlite3Fts3EvalStart(pCsr, pExpr, 0);
}else{
pPhrase->doclist.pNextDocid = 0;
pPhrase->doclist.iDocid = 0;
}
}
pExpr->iDocid = 0;
pExpr->bEof = 0;
pExpr->bStart = 0;
fts3EvalRestart(pCsr, pExpr->pLeft, pRc);
fts3EvalRestart(pCsr, pExpr->pRight, pRc);
}
}
/*
** After allocating the Fts3Expr.aMI[] array for each phrase in the
** expression rooted at pExpr, the cursor iterates through all rows matched
** by pExpr, calling this function for each row. This function increments
** the values in Fts3Expr.aMI[] according to the position-list currently
** found in Fts3Expr.pPhrase->doclist.pList for each of the phrase
** expression nodes.
*/
static void fts3EvalUpdateCounts(Fts3Expr *pExpr){
if( pExpr ){
Fts3Phrase *pPhrase = pExpr->pPhrase;
if( pPhrase && pPhrase->doclist.pList ){
int iCol = 0;
char *p = pPhrase->doclist.pList;
assert( *p );
while( 1 ){
u8 c = 0;
int iCnt = 0;
while( 0xFE & (*p | c) ){
if( (c&0x80)==0 ) iCnt++;
c = *p++ & 0x80;
}
/* aMI[iCol*3 + 1] = Number of occurrences
** aMI[iCol*3 + 2] = Number of rows containing at least one instance
*/
pExpr->aMI[iCol*3 + 1] += iCnt;
pExpr->aMI[iCol*3 + 2] += (iCnt>0);
if( *p==0x00 ) break;
p++;
p += sqlite3Fts3GetVarint32(p, &iCol);
}
}
fts3EvalUpdateCounts(pExpr->pLeft);
fts3EvalUpdateCounts(pExpr->pRight);
}
}
/*
** Expression pExpr must be of type FTSQUERY_PHRASE.
**
** If it is not already allocated and populated, this function allocates and
** populates the Fts3Expr.aMI[] array for expression pExpr. If pExpr is part
** of a NEAR expression, then it also allocates and populates the same array
** for all other phrases that are part of the NEAR expression.
**
** SQLITE_OK is returned if the aMI[] array is successfully allocated and
** populated. Otherwise, if an error occurs, an SQLite error code is returned.
*/
static int fts3EvalGatherStats(
Fts3Cursor *pCsr, /* Cursor object */
Fts3Expr *pExpr /* FTSQUERY_PHRASE expression */
){
int rc = SQLITE_OK; /* Return code */
assert( pExpr->eType==FTSQUERY_PHRASE );
if( pExpr->aMI==0 ){
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
Fts3Expr *pRoot; /* Root of NEAR expression */
Fts3Expr *p; /* Iterator used for several purposes */
sqlite3_int64 iPrevId = pCsr->iPrevId;
sqlite3_int64 iDocid;
u8 bEof;
/* Find the root of the NEAR expression */
pRoot = pExpr;
while( pRoot->pParent && pRoot->pParent->eType==FTSQUERY_NEAR ){
pRoot = pRoot->pParent;
}
iDocid = pRoot->iDocid;
bEof = pRoot->bEof;
assert( pRoot->bStart );
/* Allocate space for the aMSI[] array of each FTSQUERY_PHRASE node */
for(p=pRoot; p; p=p->pLeft){
Fts3Expr *pE = (p->eType==FTSQUERY_PHRASE?p:p->pRight);
assert( pE->aMI==0 );
pE->aMI = (u32 *)sqlite3_malloc(pTab->nColumn * 3 * sizeof(u32));
if( !pE->aMI ) return SQLITE_NOMEM;
memset(pE->aMI, 0, pTab->nColumn * 3 * sizeof(u32));
}
fts3EvalRestart(pCsr, pRoot, &rc);
while( pCsr->isEof==0 && rc==SQLITE_OK ){
do {
/* Ensure the %_content statement is reset. */
if( pCsr->isRequireSeek==0 ) sqlite3_reset(pCsr->pStmt);
assert( sqlite3_data_count(pCsr->pStmt)==0 );
/* Advance to the next document */
fts3EvalNext(pCsr, pRoot, &rc);
pCsr->isEof = pRoot->bEof;
pCsr->isRequireSeek = 1;
pCsr->isMatchinfoNeeded = 1;
pCsr->iPrevId = pRoot->iDocid;
}while( pCsr->isEof==0
&& pRoot->eType==FTSQUERY_NEAR
&& fts3EvalLoadDeferred(pCsr, &rc)
);
if( rc==SQLITE_OK && pCsr->isEof==0 ){
fts3EvalUpdateCounts(pRoot);
}
}
pCsr->isEof = 0;
pCsr->iPrevId = iPrevId;
if( bEof ){
pRoot->bEof = bEof;
}else{
/* Caution: pRoot may iterate through docids in ascending or descending
** order. For this reason, even though it seems more defensive, the
** do loop can not be written:
**
** do {...} while( pRoot->iDocid<iDocid && rc==SQLITE_OK );
*/
fts3EvalRestart(pCsr, pRoot, &rc);
do {
fts3EvalNext(pCsr, pRoot, &rc);
assert( pRoot->bEof==0 );
}while( pRoot->iDocid!=iDocid && rc==SQLITE_OK );
fts3EvalLoadDeferred(pCsr, &rc);
}
}
return rc;
}
/*
** This function is used by the matchinfo() module to query a phrase
** expression node for the following information:
**
** 1. The total number of occurrences of the phrase in each column of
** the FTS table (considering all rows), and
**
** 2. For each column, the number of rows in the table for which the
** column contains at least one instance of the phrase.
**
** If no error occurs, SQLITE_OK is returned and the values for each column
** written into the array aiOut as follows:
**
** aiOut[iCol*3 + 1] = Number of occurrences
** aiOut[iCol*3 + 2] = Number of rows containing at least one instance
**
** Caveats:
**
** * If a phrase consists entirely of deferred tokens, then all output
** values are set to the number of documents in the table. In other
** words we assume that very common tokens occur exactly once in each
** column of each row of the table.
**
** * If a phrase contains some deferred tokens (and some non-deferred
** tokens), count the potential occurrence identified by considering
** the non-deferred tokens instead of actual phrase occurrences.
**
** * If the phrase is part of a NEAR expression, then only phrase instances
** that meet the NEAR constraint are included in the counts.
*/
SQLITE_PRIVATE int sqlite3Fts3EvalPhraseStats(
Fts3Cursor *pCsr, /* FTS cursor handle */
Fts3Expr *pExpr, /* Phrase expression */
u32 *aiOut /* Array to write results into (see above) */
){
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
int rc = SQLITE_OK;
int iCol;
if( pExpr->bDeferred && pExpr->pParent->eType!=FTSQUERY_NEAR ){
assert( pCsr->nDoc>0 );
for(iCol=0; iCol<pTab->nColumn; iCol++){
aiOut[iCol*3 + 1] = pCsr->nDoc;
aiOut[iCol*3 + 2] = pCsr->nDoc;
}
}else{
rc = fts3EvalGatherStats(pCsr, pExpr);
if( rc==SQLITE_OK ){
assert( pExpr->aMI );
for(iCol=0; iCol<pTab->nColumn; iCol++){
aiOut[iCol*3 + 1] = pExpr->aMI[iCol*3 + 1];
aiOut[iCol*3 + 2] = pExpr->aMI[iCol*3 + 2];
}
}
}
return rc;
}
/*
** The expression pExpr passed as the second argument to this function
** must be of type FTSQUERY_PHRASE.
**
** The returned value is either NULL or a pointer to a buffer containing
** a position-list indicating the occurrences of the phrase in column iCol
** of the current row.
**
** More specifically, the returned buffer contains 1 varint for each
** occurence of the phrase in the column, stored using the normal (delta+2)
** compression and is terminated by either an 0x01 or 0x00 byte. For example,
** if the requested column contains "a b X c d X X" and the position-list
** for 'X' is requested, the buffer returned may contain:
**
** 0x04 0x05 0x03 0x01 or 0x04 0x05 0x03 0x00
**
** This function works regardless of whether or not the phrase is deferred,
** incremental, or neither.
*/
SQLITE_PRIVATE char *sqlite3Fts3EvalPhrasePoslist(
Fts3Cursor *pCsr, /* FTS3 cursor object */
Fts3Expr *pExpr, /* Phrase to return doclist for */
int iCol /* Column to return position list for */
){
Fts3Phrase *pPhrase = pExpr->pPhrase;
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
char *pIter = pPhrase->doclist.pList;
int iThis;
assert( iCol>=0 && iCol<pTab->nColumn );
if( !pIter
|| pExpr->bEof
|| pExpr->iDocid!=pCsr->iPrevId
|| (pPhrase->iColumn<pTab->nColumn && pPhrase->iColumn!=iCol)
){
return 0;
}
assert( pPhrase->doclist.nList>0 );
if( *pIter==0x01 ){
pIter++;
pIter += sqlite3Fts3GetVarint32(pIter, &iThis);
}else{
iThis = 0;
}
while( iThis<iCol ){
fts3ColumnlistCopy(0, &pIter);
if( *pIter==0x00 ) return 0;
pIter++;
pIter += sqlite3Fts3GetVarint32(pIter, &iThis);
}
return ((iCol==iThis)?pIter:0);
}
/*
** Free all components of the Fts3Phrase structure that were allocated by
** the eval module. Specifically, this means to free:
**
** * the contents of pPhrase->doclist, and
** * any Fts3MultiSegReader objects held by phrase tokens.
*/
SQLITE_PRIVATE void sqlite3Fts3EvalPhraseCleanup(Fts3Phrase *pPhrase){
if( pPhrase ){
int i;
sqlite3_free(pPhrase->doclist.aAll);
fts3EvalZeroPoslist(pPhrase);
memset(&pPhrase->doclist, 0, sizeof(Fts3Doclist));
for(i=0; i<pPhrase->nToken; i++){
fts3SegReaderCursorFree(pPhrase->aToken[i].pSegcsr);
pPhrase->aToken[i].pSegcsr = 0;
}
}
}
#endif
/************** End of fts3.c ************************************************/
/************** Begin file fts3_aux.c ****************************************/
/*
** 2011 Jan 27
**
|
| ︙ | ︙ | |||
115646 115647 115648 115649 115650 115651 115652 |
struct Fts3auxTable {
sqlite3_vtab base; /* Base class used by SQLite core */
Fts3Table *pFts3Tab;
};
struct Fts3auxCursor {
sqlite3_vtab_cursor base; /* Base class used by SQLite core */
| | | 116346 116347 116348 116349 116350 116351 116352 116353 116354 116355 116356 116357 116358 116359 116360 |
struct Fts3auxTable {
sqlite3_vtab base; /* Base class used by SQLite core */
Fts3Table *pFts3Tab;
};
struct Fts3auxCursor {
sqlite3_vtab_cursor base; /* Base class used by SQLite core */
Fts3MultiSegReader csr; /* Must be right after "base" */
Fts3SegFilter filter;
char *zStop;
int nStop; /* Byte-length of string zStop */
int isEof; /* True if cursor is at EOF */
sqlite3_int64 iRowid; /* Current rowid */
int iCol; /* Current value of 'col' column */
|
| ︙ | ︙ | |||
115714 115715 115716 115717 115718 115719 115720 115721 115722 115723 115724 115725 115726 115727 | if( !p ) return SQLITE_NOMEM; memset(p, 0, nByte); p->pFts3Tab = (Fts3Table *)&p[1]; p->pFts3Tab->zDb = (char *)&p->pFts3Tab[1]; p->pFts3Tab->zName = &p->pFts3Tab->zDb[nDb+1]; p->pFts3Tab->db = db; memcpy((char *)p->pFts3Tab->zDb, zDb, nDb); memcpy((char *)p->pFts3Tab->zName, zFts3, nFts3); sqlite3Fts3Dequote((char *)p->pFts3Tab->zName); *ppVtab = (sqlite3_vtab *)p; return SQLITE_OK; | > | 116414 116415 116416 116417 116418 116419 116420 116421 116422 116423 116424 116425 116426 116427 116428 | if( !p ) return SQLITE_NOMEM; memset(p, 0, nByte); p->pFts3Tab = (Fts3Table *)&p[1]; p->pFts3Tab->zDb = (char *)&p->pFts3Tab[1]; p->pFts3Tab->zName = &p->pFts3Tab->zDb[nDb+1]; p->pFts3Tab->db = db; p->pFts3Tab->nIndex = 1; memcpy((char *)p->pFts3Tab->zDb, zDb, nDb); memcpy((char *)p->pFts3Tab->zName, zFts3, nFts3); sqlite3Fts3Dequote((char *)p->pFts3Tab->zName); *ppVtab = (sqlite3_vtab *)p; return SQLITE_OK; |
| ︙ | ︙ | |||
115994 115995 115996 115997 115998 115999 116000 |
if( idxNum&FTS4AUX_LE_CONSTRAINT ){
int iIdx = (idxNum&FTS4AUX_GE_CONSTRAINT) ? 1 : 0;
pCsr->zStop = sqlite3_mprintf("%s", sqlite3_value_text(apVal[iIdx]));
pCsr->nStop = sqlite3_value_bytes(apVal[iIdx]);
if( pCsr->zStop==0 ) return SQLITE_NOMEM;
}
| | | 116695 116696 116697 116698 116699 116700 116701 116702 116703 116704 116705 116706 116707 116708 116709 |
if( idxNum&FTS4AUX_LE_CONSTRAINT ){
int iIdx = (idxNum&FTS4AUX_GE_CONSTRAINT) ? 1 : 0;
pCsr->zStop = sqlite3_mprintf("%s", sqlite3_value_text(apVal[iIdx]));
pCsr->nStop = sqlite3_value_bytes(apVal[iIdx]);
if( pCsr->zStop==0 ) return SQLITE_NOMEM;
}
rc = sqlite3Fts3SegReaderCursor(pFts3, 0, FTS3_SEGCURSOR_ALL,
pCsr->filter.zTerm, pCsr->filter.nTerm, 0, isScan, &pCsr->csr
);
if( rc==SQLITE_OK ){
rc = sqlite3Fts3SegReaderStart(pFts3, &pCsr->csr, &pCsr->filter);
}
if( rc==SQLITE_OK ) rc = fts3auxNextMethod(pCursor);
|
| ︙ | ︙ | |||
116173 116174 116175 116176 116177 116178 116179 116180 116181 116182 116183 116184 116185 116186 116187 116188 116189 116190 116191 116192 |
/*
** Default span for NEAR operators.
*/
#define SQLITE_FTS3_DEFAULT_NEAR_PARAM 10
typedef struct ParseContext ParseContext;
struct ParseContext {
sqlite3_tokenizer *pTokenizer; /* Tokenizer module */
const char **azCol; /* Array of column names for fts3 table */
int nCol; /* Number of entries in azCol[] */
int iDefaultCol; /* Default column to query */
sqlite3_context *pCtx; /* Write error message here */
int nNest; /* Number of nested brackets */
};
/*
** This function is equivalent to the standard isspace() function.
**
| > > > > > > > > > | 116874 116875 116876 116877 116878 116879 116880 116881 116882 116883 116884 116885 116886 116887 116888 116889 116890 116891 116892 116893 116894 116895 116896 116897 116898 116899 116900 116901 116902 |
/*
** Default span for NEAR operators.
*/
#define SQLITE_FTS3_DEFAULT_NEAR_PARAM 10
/*
** isNot:
** This variable is used by function getNextNode(). When getNextNode() is
** called, it sets ParseContext.isNot to true if the 'next node' is a
** FTSQUERY_PHRASE with a unary "-" attached to it. i.e. "mysql" in the
** FTS3 query "sqlite -mysql". Otherwise, ParseContext.isNot is set to
** zero.
*/
typedef struct ParseContext ParseContext;
struct ParseContext {
sqlite3_tokenizer *pTokenizer; /* Tokenizer module */
const char **azCol; /* Array of column names for fts3 table */
int nCol; /* Number of entries in azCol[] */
int iDefaultCol; /* Default column to query */
int isNot; /* True if getNextNode() sees a unary - */
sqlite3_context *pCtx; /* Write error message here */
int nNest; /* Number of nested brackets */
};
/*
** This function is equivalent to the standard isspace() function.
**
|
| ︙ | ︙ | |||
116264 116265 116266 116267 116268 116269 116270 |
memcpy(pRet->pPhrase->aToken[0].z, zToken, nToken);
if( iEnd<n && z[iEnd]=='*' ){
pRet->pPhrase->aToken[0].isPrefix = 1;
iEnd++;
}
if( !sqlite3_fts3_enable_parentheses && iStart>0 && z[iStart-1]=='-' ){
| | | 116974 116975 116976 116977 116978 116979 116980 116981 116982 116983 116984 116985 116986 116987 116988 |
memcpy(pRet->pPhrase->aToken[0].z, zToken, nToken);
if( iEnd<n && z[iEnd]=='*' ){
pRet->pPhrase->aToken[0].isPrefix = 1;
iEnd++;
}
if( !sqlite3_fts3_enable_parentheses && iStart>0 && z[iStart-1]=='-' ){
pParse->isNot = 1;
}
}
nConsumed = iEnd;
}
pModule->xClose(pCursor);
}
|
| ︙ | ︙ | |||
116316 116317 116318 116319 116320 116321 116322 116323 116324 116325 116326 116327 |
sqlite3_tokenizer_module const *pModule = pTokenizer->pModule;
int rc;
Fts3Expr *p = 0;
sqlite3_tokenizer_cursor *pCursor = 0;
char *zTemp = 0;
int nTemp = 0;
rc = pModule->xOpen(pTokenizer, zInput, nInput, &pCursor);
if( rc==SQLITE_OK ){
int ii;
pCursor->pTokenizer = pTokenizer;
for(ii=0; rc==SQLITE_OK; ii++){
| > > > > > > > > > > > > > > > > > > > > > > | | | | | | < | | > | < < | > | | | < | | < < < < | > > > | | < < | | > > > > | | < | | < | | < | | < < < | 117026 117027 117028 117029 117030 117031 117032 117033 117034 117035 117036 117037 117038 117039 117040 117041 117042 117043 117044 117045 117046 117047 117048 117049 117050 117051 117052 117053 117054 117055 117056 117057 117058 117059 117060 117061 117062 117063 117064 117065 117066 117067 117068 117069 117070 117071 117072 117073 117074 117075 117076 117077 117078 117079 117080 117081 117082 117083 117084 117085 117086 117087 117088 117089 117090 117091 117092 117093 117094 117095 117096 117097 117098 117099 117100 117101 117102 117103 117104 117105 117106 117107 117108 117109 117110 117111 117112 117113 117114 117115 |
sqlite3_tokenizer_module const *pModule = pTokenizer->pModule;
int rc;
Fts3Expr *p = 0;
sqlite3_tokenizer_cursor *pCursor = 0;
char *zTemp = 0;
int nTemp = 0;
const int nSpace = sizeof(Fts3Expr) + sizeof(Fts3Phrase);
int nToken = 0;
/* The final Fts3Expr data structure, including the Fts3Phrase,
** Fts3PhraseToken structures token buffers are all stored as a single
** allocation so that the expression can be freed with a single call to
** sqlite3_free(). Setting this up requires a two pass approach.
**
** The first pass, in the block below, uses a tokenizer cursor to iterate
** through the tokens in the expression. This pass uses fts3ReallocOrFree()
** to assemble data in two dynamic buffers:
**
** Buffer p: Points to the Fts3Expr structure, followed by the Fts3Phrase
** structure, followed by the array of Fts3PhraseToken
** structures. This pass only populates the Fts3PhraseToken array.
**
** Buffer zTemp: Contains copies of all tokens.
**
** The second pass, in the block that begins "if( rc==SQLITE_DONE )" below,
** appends buffer zTemp to buffer p, and fills in the Fts3Expr and Fts3Phrase
** structures.
*/
rc = pModule->xOpen(pTokenizer, zInput, nInput, &pCursor);
if( rc==SQLITE_OK ){
int ii;
pCursor->pTokenizer = pTokenizer;
for(ii=0; rc==SQLITE_OK; ii++){
const char *zByte;
int nByte, iBegin, iEnd, iPos;
rc = pModule->xNext(pCursor, &zByte, &nByte, &iBegin, &iEnd, &iPos);
if( rc==SQLITE_OK ){
Fts3PhraseToken *pToken;
p = fts3ReallocOrFree(p, nSpace + ii*sizeof(Fts3PhraseToken));
if( !p ) goto no_mem;
zTemp = fts3ReallocOrFree(zTemp, nTemp + nByte);
if( !zTemp ) goto no_mem;
assert( nToken==ii );
pToken = &((Fts3Phrase *)(&p[1]))->aToken[ii];
memset(pToken, 0, sizeof(Fts3PhraseToken));
memcpy(&zTemp[nTemp], zByte, nByte);
nTemp += nByte;
pToken->n = nByte;
pToken->isPrefix = (iEnd<nInput && zInput[iEnd]=='*');
nToken = ii+1;
}
}
pModule->xClose(pCursor);
pCursor = 0;
}
if( rc==SQLITE_DONE ){
int jj;
char *zBuf = 0;
p = fts3ReallocOrFree(p, nSpace + nToken*sizeof(Fts3PhraseToken) + nTemp);
if( !p ) goto no_mem;
memset(p, 0, (char *)&(((Fts3Phrase *)&p[1])->aToken[0])-(char *)p);
p->eType = FTSQUERY_PHRASE;
p->pPhrase = (Fts3Phrase *)&p[1];
p->pPhrase->iColumn = pParse->iDefaultCol;
p->pPhrase->nToken = nToken;
zBuf = (char *)&p->pPhrase->aToken[nToken];
memcpy(zBuf, zTemp, nTemp);
sqlite3_free(zTemp);
for(jj=0; jj<p->pPhrase->nToken; jj++){
p->pPhrase->aToken[jj].z = zBuf;
zBuf += p->pPhrase->aToken[jj].n;
}
rc = SQLITE_OK;
}
*ppExpr = p;
return rc;
no_mem:
|
| ︙ | ︙ | |||
116432 116433 116434 116435 116436 116437 116438 116439 116440 116441 116442 116443 116444 116445 |
int iCol;
int iColLen;
int rc;
Fts3Expr *pRet = 0;
const char *zInput = z;
int nInput = n;
/* Skip over any whitespace before checking for a keyword, an open or
** close bracket, or a quoted string.
*/
while( nInput>0 && fts3isspace(*zInput) ){
nInput--;
zInput++;
| > > | 117157 117158 117159 117160 117161 117162 117163 117164 117165 117166 117167 117168 117169 117170 117171 117172 |
int iCol;
int iColLen;
int rc;
Fts3Expr *pRet = 0;
const char *zInput = z;
int nInput = n;
pParse->isNot = 0;
/* Skip over any whitespace before checking for a keyword, an open or
** close bracket, or a quoted string.
*/
while( nInput>0 && fts3isspace(*zInput) ){
nInput--;
zInput++;
|
| ︙ | ︙ | |||
116651 116652 116653 116654 116655 116656 116657 |
Fts3Expr *p = 0;
int nByte = 0;
rc = getNextNode(pParse, zIn, nIn, &p, &nByte);
if( rc==SQLITE_OK ){
int isPhrase;
if( !sqlite3_fts3_enable_parentheses
| | < | 117378 117379 117380 117381 117382 117383 117384 117385 117386 117387 117388 117389 117390 117391 117392 117393 117394 117395 117396 117397 117398 117399 117400 117401 117402 117403 117404 117405 117406 117407 117408 117409 |
Fts3Expr *p = 0;
int nByte = 0;
rc = getNextNode(pParse, zIn, nIn, &p, &nByte);
if( rc==SQLITE_OK ){
int isPhrase;
if( !sqlite3_fts3_enable_parentheses
&& p->eType==FTSQUERY_PHRASE && pParse->isNot
){
/* Create an implicit NOT operator. */
Fts3Expr *pNot = fts3MallocZero(sizeof(Fts3Expr));
if( !pNot ){
sqlite3Fts3ExprFree(p);
rc = SQLITE_NOMEM;
goto exprparse_out;
}
pNot->eType = FTSQUERY_NOT;
pNot->pRight = p;
if( pNotBranch ){
pNot->pLeft = pNotBranch;
}
pNotBranch = pNot;
p = pPrev;
}else{
int eType = p->eType;
isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft);
/* The isRequirePhrase variable is set to true if a phrase or
** an expression contained in parenthesis is required. If a
** binary operator (AND, OR, NOT or NEAR) is encounted when
** isRequirePhrase is set, this is a syntax error.
*/
|
| ︙ | ︙ | |||
116832 116833 116834 116835 116836 116837 116838 116839 116840 |
}
/*
** Free a parsed fts3 query expression allocated by sqlite3Fts3ExprParse().
*/
SQLITE_PRIVATE void sqlite3Fts3ExprFree(Fts3Expr *p){
if( p ){
sqlite3Fts3ExprFree(p->pLeft);
sqlite3Fts3ExprFree(p->pRight);
| > > | | 117558 117559 117560 117561 117562 117563 117564 117565 117566 117567 117568 117569 117570 117571 117572 117573 117574 117575 117576 |
}
/*
** Free a parsed fts3 query expression allocated by sqlite3Fts3ExprParse().
*/
SQLITE_PRIVATE void sqlite3Fts3ExprFree(Fts3Expr *p){
if( p ){
assert( p->eType==FTSQUERY_PHRASE || p->pPhrase==0 );
sqlite3Fts3ExprFree(p->pLeft);
sqlite3Fts3ExprFree(p->pRight);
sqlite3Fts3EvalPhraseCleanup(p->pPhrase);
sqlite3_free(p->aMI);
sqlite3_free(p);
}
}
/****************************************************************************
*****************************************************************************
** Everything after this point is just test code.
|
| ︙ | ︙ | |||
116891 116892 116893 116894 116895 116896 116897 |
*/
static char *exprToString(Fts3Expr *pExpr, char *zBuf){
switch( pExpr->eType ){
case FTSQUERY_PHRASE: {
Fts3Phrase *pPhrase = pExpr->pPhrase;
int i;
zBuf = sqlite3_mprintf(
| | | 117619 117620 117621 117622 117623 117624 117625 117626 117627 117628 117629 117630 117631 117632 117633 |
*/
static char *exprToString(Fts3Expr *pExpr, char *zBuf){
switch( pExpr->eType ){
case FTSQUERY_PHRASE: {
Fts3Phrase *pPhrase = pExpr->pPhrase;
int i;
zBuf = sqlite3_mprintf(
"%zPHRASE %d 0", zBuf, pPhrase->iColumn);
for(i=0; zBuf && i<pPhrase->nToken; i++){
zBuf = sqlite3_mprintf("%z %.*s%s", zBuf,
pPhrase->aToken[i].n, pPhrase->aToken[i].z,
(pPhrase->aToken[i].isPrefix?"+":"")
);
}
return zBuf;
|
| ︙ | ︙ | |||
118809 118810 118811 118812 118813 118814 118815 118816 118817 118818 118819 118820 | ** ** This means that if we have a pointer into a buffer containing node data, ** it is always safe to read up to two varints from it without risking an ** overread, even if the node data is corrupted. */ #define FTS3_NODE_PADDING (FTS3_VARINT_MAX*2) typedef struct PendingList PendingList; typedef struct SegmentNode SegmentNode; typedef struct SegmentWriter SegmentWriter; /* | > > > > > > > > > > > > > > > > > > > > > > > > > > > < < | > | 119537 119538 119539 119540 119541 119542 119543 119544 119545 119546 119547 119548 119549 119550 119551 119552 119553 119554 119555 119556 119557 119558 119559 119560 119561 119562 119563 119564 119565 119566 119567 119568 119569 119570 119571 119572 119573 119574 119575 119576 119577 119578 119579 119580 119581 119582 119583 119584 |
**
** This means that if we have a pointer into a buffer containing node data,
** it is always safe to read up to two varints from it without risking an
** overread, even if the node data is corrupted.
*/
#define FTS3_NODE_PADDING (FTS3_VARINT_MAX*2)
/*
** Under certain circumstances, b-tree nodes (doclists) can be loaded into
** memory incrementally instead of all at once. This can be a big performance
** win (reduced IO and CPU) if SQLite stops calling the virtual table xNext()
** method before retrieving all query results (as may happen, for example,
** if a query has a LIMIT clause).
**
** Incremental loading is used for b-tree nodes FTS3_NODE_CHUNK_THRESHOLD
** bytes and larger. Nodes are loaded in chunks of FTS3_NODE_CHUNKSIZE bytes.
** The code is written so that the hard lower-limit for each of these values
** is 1. Clearly such small values would be inefficient, but can be useful
** for testing purposes.
**
** If this module is built with SQLITE_TEST defined, these constants may
** be overridden at runtime for testing purposes. File fts3_test.c contains
** a Tcl interface to read and write the values.
*/
#ifdef SQLITE_TEST
int test_fts3_node_chunksize = (4*1024);
int test_fts3_node_chunk_threshold = (4*1024)*4;
# define FTS3_NODE_CHUNKSIZE test_fts3_node_chunksize
# define FTS3_NODE_CHUNK_THRESHOLD test_fts3_node_chunk_threshold
#else
# define FTS3_NODE_CHUNKSIZE (4*1024)
# define FTS3_NODE_CHUNK_THRESHOLD (FTS3_NODE_CHUNKSIZE*4)
#endif
typedef struct PendingList PendingList;
typedef struct SegmentNode SegmentNode;
typedef struct SegmentWriter SegmentWriter;
/*
** An instance of the following data structure is used to build doclists
** incrementally. See function fts3PendingListAppend() for details.
*/
struct PendingList {
int nData;
char *aData;
int nSpace;
sqlite3_int64 iLastDocid;
sqlite3_int64 iLastCol;
|
| ︙ | ︙ | |||
118847 118848 118849 118850 118851 118852 118853 | ** a contiguous set of segment b-tree leaf nodes. Although the details of ** this structure are only manipulated by code in this file, opaque handles ** of type Fts3SegReader* are also used by code in fts3.c to iterate through ** terms when querying the full-text index. See functions: ** ** sqlite3Fts3SegReaderNew() ** sqlite3Fts3SegReaderFree() | < > > > | > > > | 119601 119602 119603 119604 119605 119606 119607 119608 119609 119610 119611 119612 119613 119614 119615 119616 119617 119618 119619 119620 119621 119622 119623 119624 119625 119626 119627 119628 119629 119630 119631 119632 119633 119634 119635 119636 119637 119638 119639 119640 119641 119642 119643 119644 119645 119646 119647 119648 119649 119650 119651 119652 119653 |
** a contiguous set of segment b-tree leaf nodes. Although the details of
** this structure are only manipulated by code in this file, opaque handles
** of type Fts3SegReader* are also used by code in fts3.c to iterate through
** terms when querying the full-text index. See functions:
**
** sqlite3Fts3SegReaderNew()
** sqlite3Fts3SegReaderFree()
** sqlite3Fts3SegReaderIterate()
**
** Methods used to manipulate Fts3SegReader structures:
**
** fts3SegReaderNext()
** fts3SegReaderFirstDocid()
** fts3SegReaderNextDocid()
*/
struct Fts3SegReader {
int iIdx; /* Index within level, or 0x7FFFFFFF for PT */
sqlite3_int64 iStartBlock; /* Rowid of first leaf block to traverse */
sqlite3_int64 iLeafEndBlock; /* Rowid of final leaf block to traverse */
sqlite3_int64 iEndBlock; /* Rowid of final block in segment (or 0) */
sqlite3_int64 iCurrentBlock; /* Current leaf block (or 0) */
char *aNode; /* Pointer to node data (or NULL) */
int nNode; /* Size of buffer at aNode (or 0) */
int nPopulate; /* If >0, bytes of buffer aNode[] loaded */
sqlite3_blob *pBlob; /* If not NULL, blob handle to read node */
Fts3HashElem **ppNextElem;
/* Variables set by fts3SegReaderNext(). These may be read directly
** by the caller. They are valid from the time SegmentReaderNew() returns
** until SegmentReaderNext() returns something other than SQLITE_OK
** (i.e. SQLITE_DONE).
*/
int nTerm; /* Number of bytes in current term */
char *zTerm; /* Pointer to current term */
int nTermAlloc; /* Allocated size of zTerm buffer */
char *aDoclist; /* Pointer to doclist of current entry */
int nDoclist; /* Size of doclist in current entry */
/* The following variables are used by fts3SegReaderNextDocid() to iterate
** through the current doclist (aDoclist/nDoclist).
*/
char *pOffsetList;
int nOffsetList; /* For descending pending seg-readers only */
sqlite3_int64 iDocid;
};
#define fts3SegReaderIsPending(p) ((p)->ppNextElem!=0)
#define fts3SegReaderIsRootOnly(p) ((p)->aNode==(char *)&(p)[1])
/*
|
| ︙ | ︙ | |||
118918 118919 118920 118921 118922 118923 118924 118925 118926 118927 118928 118929 118930 118931 |
** the interior part of the segment b+-tree structures (everything except
** the leaf nodes). These functions and type are only ever used by code
** within the fts3SegWriterXXX() family of functions described above.
**
** fts3NodeAddTerm()
** fts3NodeWrite()
** fts3NodeFree()
*/
struct SegmentNode {
SegmentNode *pParent; /* Parent node (or NULL for root node) */
SegmentNode *pRight; /* Pointer to right-sibling */
SegmentNode *pLeftmost; /* Pointer to left-most node of this depth */
int nEntry; /* Number of terms written to node so far */
char *zTerm; /* Pointer to previous term buffer */
| > > > > > > > > | 119677 119678 119679 119680 119681 119682 119683 119684 119685 119686 119687 119688 119689 119690 119691 119692 119693 119694 119695 119696 119697 119698 |
** the interior part of the segment b+-tree structures (everything except
** the leaf nodes). These functions and type are only ever used by code
** within the fts3SegWriterXXX() family of functions described above.
**
** fts3NodeAddTerm()
** fts3NodeWrite()
** fts3NodeFree()
**
** When a b+tree is written to the database (either as a result of a merge
** or the pending-terms table being flushed), leaves are written into the
** database file as soon as they are completely populated. The interior of
** the tree is assembled in memory and written out only once all leaves have
** been populated and stored. This is Ok, as the b+-tree fanout is usually
** very large, meaning that the interior of the tree consumes relatively
** little memory.
*/
struct SegmentNode {
SegmentNode *pParent; /* Parent node (or NULL for root node) */
SegmentNode *pRight; /* Pointer to right-sibling */
SegmentNode *pLeftmost; /* Pointer to left-most node of this depth */
int nEntry; /* Number of terms written to node so far */
char *zTerm; /* Pointer to previous term buffer */
|
| ︙ | ︙ | |||
118948 118949 118950 118951 118952 118953 118954 | #define SQL_DELETE_ALL_STAT 6 #define SQL_SELECT_CONTENT_BY_ROWID 7 #define SQL_NEXT_SEGMENT_INDEX 8 #define SQL_INSERT_SEGMENTS 9 #define SQL_NEXT_SEGMENTS_ID 10 #define SQL_INSERT_SEGDIR 11 #define SQL_SELECT_LEVEL 12 | | | | > > > > > | 119715 119716 119717 119718 119719 119720 119721 119722 119723 119724 119725 119726 119727 119728 119729 119730 119731 119732 119733 119734 119735 119736 119737 119738 119739 119740 119741 119742 119743 119744 | #define SQL_DELETE_ALL_STAT 6 #define SQL_SELECT_CONTENT_BY_ROWID 7 #define SQL_NEXT_SEGMENT_INDEX 8 #define SQL_INSERT_SEGMENTS 9 #define SQL_NEXT_SEGMENTS_ID 10 #define SQL_INSERT_SEGDIR 11 #define SQL_SELECT_LEVEL 12 #define SQL_SELECT_LEVEL_RANGE 13 #define SQL_SELECT_LEVEL_COUNT 14 #define SQL_SELECT_SEGDIR_MAX_LEVEL 15 #define SQL_DELETE_SEGDIR_LEVEL 16 #define SQL_DELETE_SEGMENTS_RANGE 17 #define SQL_CONTENT_INSERT 18 #define SQL_DELETE_DOCSIZE 19 #define SQL_REPLACE_DOCSIZE 20 #define SQL_SELECT_DOCSIZE 21 #define SQL_SELECT_DOCTOTAL 22 #define SQL_REPLACE_DOCTOTAL 23 #define SQL_SELECT_ALL_PREFIX_LEVEL 24 #define SQL_DELETE_ALL_TERMS_SEGDIR 25 #define SQL_DELETE_SEGDIR_RANGE 26 /* ** This function is used to obtain an SQLite prepared statement handle ** for the statement identified by the second argument. If successful, ** *pp is set to the requested statement handle and SQLITE_OK returned. ** Otherwise, an SQLite error code is returned and *pp is set to 0. ** |
| ︙ | ︙ | |||
118995 118996 118997 118998 118999 119000 119001 |
/* 10 */ "SELECT coalesce((SELECT max(blockid) FROM %Q.'%q_segments') + 1, 1)",
/* 11 */ "INSERT INTO %Q.'%q_segdir' VALUES(?,?,?,?,?,?)",
/* Return segments in order from oldest to newest.*/
/* 12 */ "SELECT idx, start_block, leaves_end_block, end_block, root "
"FROM %Q.'%q_segdir' WHERE level = ? ORDER BY idx ASC",
/* 13 */ "SELECT idx, start_block, leaves_end_block, end_block, root "
| > | | > > > > > | 119767 119768 119769 119770 119771 119772 119773 119774 119775 119776 119777 119778 119779 119780 119781 119782 119783 119784 119785 119786 119787 119788 119789 119790 119791 119792 119793 119794 119795 119796 119797 119798 119799 |
/* 10 */ "SELECT coalesce((SELECT max(blockid) FROM %Q.'%q_segments') + 1, 1)",
/* 11 */ "INSERT INTO %Q.'%q_segdir' VALUES(?,?,?,?,?,?)",
/* Return segments in order from oldest to newest.*/
/* 12 */ "SELECT idx, start_block, leaves_end_block, end_block, root "
"FROM %Q.'%q_segdir' WHERE level = ? ORDER BY idx ASC",
/* 13 */ "SELECT idx, start_block, leaves_end_block, end_block, root "
"FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ?"
"ORDER BY level DESC, idx ASC",
/* 14 */ "SELECT count(*) FROM %Q.'%q_segdir' WHERE level = ?",
/* 15 */ "SELECT max(level) FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ?",
/* 16 */ "DELETE FROM %Q.'%q_segdir' WHERE level = ?",
/* 17 */ "DELETE FROM %Q.'%q_segments' WHERE blockid BETWEEN ? AND ?",
/* 18 */ "INSERT INTO %Q.'%q_content' VALUES(%s)",
/* 19 */ "DELETE FROM %Q.'%q_docsize' WHERE docid = ?",
/* 20 */ "REPLACE INTO %Q.'%q_docsize' VALUES(?,?)",
/* 21 */ "SELECT size FROM %Q.'%q_docsize' WHERE docid=?",
/* 22 */ "SELECT value FROM %Q.'%q_stat' WHERE id=0",
/* 23 */ "REPLACE INTO %Q.'%q_stat' VALUES(0,?)",
/* 24 */ "",
/* 25 */ "",
/* 26 */ "DELETE FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ?",
};
int rc = SQLITE_OK;
sqlite3_stmt *pStmt;
assert( SizeofArray(azSql)==SizeofArray(p->aStmt) );
assert( eStmt<SizeofArray(azSql) && eStmt>=0 );
|
| ︙ | ︙ | |||
119163 119164 119165 119166 119167 119168 119169 | ** ** 0: idx ** 1: start_block ** 2: leaves_end_block ** 3: end_block ** 4: root */ | | > > > > > > > > > > > | > > > > > | > > | 119941 119942 119943 119944 119945 119946 119947 119948 119949 119950 119951 119952 119953 119954 119955 119956 119957 119958 119959 119960 119961 119962 119963 119964 119965 119966 119967 119968 119969 119970 119971 119972 119973 119974 119975 119976 119977 119978 119979 119980 |
**
** 0: idx
** 1: start_block
** 2: leaves_end_block
** 3: end_block
** 4: root
*/
SQLITE_PRIVATE int sqlite3Fts3AllSegdirs(
Fts3Table *p, /* FTS3 table */
int iIndex, /* Index for p->aIndex[] */
int iLevel, /* Level to select */
sqlite3_stmt **ppStmt /* OUT: Compiled statement */
){
int rc;
sqlite3_stmt *pStmt = 0;
assert( iLevel==FTS3_SEGCURSOR_ALL || iLevel>=0 );
assert( iLevel<FTS3_SEGDIR_MAXLEVEL );
assert( iIndex>=0 && iIndex<p->nIndex );
if( iLevel<0 ){
/* "SELECT * FROM %_segdir WHERE level BETWEEN ? AND ? ORDER BY ..." */
rc = fts3SqlStmt(p, SQL_SELECT_LEVEL_RANGE, &pStmt, 0);
if( rc==SQLITE_OK ){
sqlite3_bind_int(pStmt, 1, iIndex*FTS3_SEGDIR_MAXLEVEL);
sqlite3_bind_int(pStmt, 2, (iIndex+1)*FTS3_SEGDIR_MAXLEVEL-1);
}
}else{
/* "SELECT * FROM %_segdir WHERE level = ? ORDER BY ..." */
rc = fts3SqlStmt(p, SQL_SELECT_LEVEL, &pStmt, 0);
if( rc==SQLITE_OK ){
sqlite3_bind_int(pStmt, 1, iLevel+iIndex*FTS3_SEGDIR_MAXLEVEL);
}
}
*ppStmt = pStmt;
return rc;
}
/*
|
| ︙ | ︙ | |||
119284 119285 119286 119287 119288 119289 119290 119291 119292 119293 119294 119295 119296 119297 |
*pRc = rc;
if( p!=*pp ){
*pp = p;
return 1;
}
return 0;
}
/*
** Tokenize the nul-terminated string zText and add all tokens to the
** pending-terms hash-table. The docid used is that currently stored in
** p->iPrevDocid, and the column is specified by argument iCol.
**
** If successful, SQLITE_OK is returned. Otherwise, an SQLite error code.
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 120080 120081 120082 120083 120084 120085 120086 120087 120088 120089 120090 120091 120092 120093 120094 120095 120096 120097 120098 120099 120100 120101 120102 120103 120104 120105 120106 120107 120108 120109 120110 120111 120112 120113 120114 120115 120116 120117 120118 120119 120120 120121 120122 120123 120124 120125 120126 120127 120128 120129 120130 120131 120132 120133 120134 |
*pRc = rc;
if( p!=*pp ){
*pp = p;
return 1;
}
return 0;
}
/*
** Free a PendingList object allocated by fts3PendingListAppend().
*/
static void fts3PendingListDelete(PendingList *pList){
sqlite3_free(pList);
}
/*
** Add an entry to one of the pending-terms hash tables.
*/
static int fts3PendingTermsAddOne(
Fts3Table *p,
int iCol,
int iPos,
Fts3Hash *pHash, /* Pending terms hash table to add entry to */
const char *zToken,
int nToken
){
PendingList *pList;
int rc = SQLITE_OK;
pList = (PendingList *)fts3HashFind(pHash, zToken, nToken);
if( pList ){
p->nPendingData -= (pList->nData + nToken + sizeof(Fts3HashElem));
}
if( fts3PendingListAppend(&pList, p->iPrevDocid, iCol, iPos, &rc) ){
if( pList==fts3HashInsert(pHash, zToken, nToken, pList) ){
/* Malloc failed while inserting the new entry. This can only
** happen if there was no previous entry for this token.
*/
assert( 0==fts3HashFind(pHash, zToken, nToken) );
sqlite3_free(pList);
rc = SQLITE_NOMEM;
}
}
if( rc==SQLITE_OK ){
p->nPendingData += (pList->nData + nToken + sizeof(Fts3HashElem));
}
return rc;
}
/*
** Tokenize the nul-terminated string zText and add all tokens to the
** pending-terms hash-table. The docid used is that currently stored in
** p->iPrevDocid, and the column is specified by argument iCol.
**
** If successful, SQLITE_OK is returned. Otherwise, an SQLite error code.
|
| ︙ | ︙ | |||
119333 119334 119335 119336 119337 119338 119339 |
}
pCsr->pTokenizer = pTokenizer;
xNext = pModule->xNext;
while( SQLITE_OK==rc
&& SQLITE_OK==(rc = xNext(pCsr, &zToken, &nToken, &iStart, &iEnd, &iPos))
){
| < | | < < < | < < < < | | < | < > > | > > | > > | 120170 120171 120172 120173 120174 120175 120176 120177 120178 120179 120180 120181 120182 120183 120184 120185 120186 120187 120188 120189 120190 120191 120192 120193 120194 120195 120196 120197 120198 120199 120200 120201 120202 120203 120204 120205 120206 120207 |
}
pCsr->pTokenizer = pTokenizer;
xNext = pModule->xNext;
while( SQLITE_OK==rc
&& SQLITE_OK==(rc = xNext(pCsr, &zToken, &nToken, &iStart, &iEnd, &iPos))
){
int i;
if( iPos>=nWord ) nWord = iPos+1;
/* Positions cannot be negative; we use -1 as a terminator internally.
** Tokens must have a non-zero length.
*/
if( iPos<0 || !zToken || nToken<=0 ){
rc = SQLITE_ERROR;
break;
}
/* Add the term to the terms index */
rc = fts3PendingTermsAddOne(
p, iCol, iPos, &p->aIndex[0].hPending, zToken, nToken
);
/* Add the term to each of the prefix indexes that it is not too
** short for. */
for(i=1; rc==SQLITE_OK && i<p->nIndex; i++){
struct Fts3Index *pIndex = &p->aIndex[i];
if( nToken<pIndex->nPrefix ) continue;
rc = fts3PendingTermsAddOne(
p, iCol, iPos, &pIndex->hPending, zToken, pIndex->nPrefix
);
}
}
pModule->xClose(pCsr);
*pnWord = nWord;
return (rc==SQLITE_DONE ? SQLITE_OK : rc);
}
|
| ︙ | ︙ | |||
119390 119391 119392 119393 119394 119395 119396 |
if( rc!=SQLITE_OK ) return rc;
}
p->iPrevDocid = iDocid;
return SQLITE_OK;
}
/*
| | > > | > | | > | | > | 120223 120224 120225 120226 120227 120228 120229 120230 120231 120232 120233 120234 120235 120236 120237 120238 120239 120240 120241 120242 120243 120244 120245 120246 120247 120248 120249 |
if( rc!=SQLITE_OK ) return rc;
}
p->iPrevDocid = iDocid;
return SQLITE_OK;
}
/*
** Discard the contents of the pending-terms hash tables.
*/
SQLITE_PRIVATE void sqlite3Fts3PendingTermsClear(Fts3Table *p){
int i;
for(i=0; i<p->nIndex; i++){
Fts3HashElem *pElem;
Fts3Hash *pHash = &p->aIndex[i].hPending;
for(pElem=fts3HashFirst(pHash); pElem; pElem=fts3HashNext(pElem)){
PendingList *pList = (PendingList *)fts3HashData(pElem);
fts3PendingListDelete(pList);
}
fts3HashClear(pHash);
}
p->nPendingData = 0;
}
/*
** This function is called by the xUpdate() method as part of an INSERT
** operation. It adds entries for each term in the new record to the
** pendingTerms hash table.
|
| ︙ | ︙ | |||
119553 119554 119555 119556 119557 119558 119559 | *pRC = rc; } /* ** Forward declaration to account for the circular dependency between ** functions fts3SegmentMerge() and fts3AllocateSegdirIdx(). */ | | | > > > > > | | | 120391 120392 120393 120394 120395 120396 120397 120398 120399 120400 120401 120402 120403 120404 120405 120406 120407 120408 120409 120410 120411 120412 120413 120414 120415 120416 120417 120418 120419 120420 120421 120422 120423 120424 120425 120426 120427 120428 120429 120430 120431 120432 120433 120434 120435 120436 120437 120438 120439 120440 120441 120442 120443 120444 120445 120446 120447 120448 120449 |
*pRC = rc;
}
/*
** Forward declaration to account for the circular dependency between
** functions fts3SegmentMerge() and fts3AllocateSegdirIdx().
*/
static int fts3SegmentMerge(Fts3Table *, int, int);
/*
** This function allocates a new level iLevel index in the segdir table.
** Usually, indexes are allocated within a level sequentially starting
** with 0, so the allocated index is one greater than the value returned
** by:
**
** SELECT max(idx) FROM %_segdir WHERE level = :iLevel
**
** However, if there are already FTS3_MERGE_COUNT indexes at the requested
** level, they are merged into a single level (iLevel+1) segment and the
** allocated index is 0.
**
** If successful, *piIdx is set to the allocated index slot and SQLITE_OK
** returned. Otherwise, an SQLite error code is returned.
*/
static int fts3AllocateSegdirIdx(
Fts3Table *p,
int iIndex, /* Index for p->aIndex */
int iLevel,
int *piIdx
){
int rc; /* Return Code */
sqlite3_stmt *pNextIdx; /* Query for next idx at level iLevel */
int iNext = 0; /* Result of query pNextIdx */
/* Set variable iNext to the next available segdir index at level iLevel. */
rc = fts3SqlStmt(p, SQL_NEXT_SEGMENT_INDEX, &pNextIdx, 0);
if( rc==SQLITE_OK ){
sqlite3_bind_int(pNextIdx, 1, iIndex*FTS3_SEGDIR_MAXLEVEL + iLevel);
if( SQLITE_ROW==sqlite3_step(pNextIdx) ){
iNext = sqlite3_column_int(pNextIdx, 0);
}
rc = sqlite3_reset(pNextIdx);
}
if( rc==SQLITE_OK ){
/* If iNext is FTS3_MERGE_COUNT, indicating that level iLevel is already
** full, merge all segments in level iLevel into a single iLevel+1
** segment and allocate (newly freed) index 0 at level iLevel. Otherwise,
** if iNext is less than FTS3_MERGE_COUNT, allocate index iNext.
*/
if( iNext>=FTS3_MERGE_COUNT ){
rc = fts3SegmentMerge(p, iIndex, iLevel);
*piIdx = 0;
}else{
*piIdx = iNext;
}
}
return rc;
|
| ︙ | ︙ | |||
119633 119634 119635 119636 119637 119638 119639 | ** method (xFilter etc.) that may directly or indirectly call this function ** must call sqlite3Fts3SegmentsClose() before returning. */ SQLITE_PRIVATE int sqlite3Fts3ReadBlock( Fts3Table *p, /* FTS3 table handle */ sqlite3_int64 iBlockid, /* Access the row with blockid=$iBlockid */ char **paBlob, /* OUT: Blob data in malloc'd buffer */ | | > > > > > > < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > < > > | > > > > > > | > > > > > | < > > > > | | | > > > > > > > > > > > | | > > > | > | | | > < < < < < | > > > | | < | | | | | | | > > | | < < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > | | | | > | > > | | < < < < < < < | | | | > > | < | < < < < | > > > > | | | | < | < < < < < < < < < < < < < < < < < | | < < < < < < < < < < < < < < < < < < < < < < < < < < | | | < | | | | | > | 120476 120477 120478 120479 120480 120481 120482 120483 120484 120485 120486 120487 120488 120489 120490 120491 120492 120493 120494 120495 120496 120497 120498 120499 120500 120501 120502 120503 120504 120505 120506 120507 120508 120509 120510 120511 120512 120513 120514 120515 120516 120517 120518 120519 120520 120521 120522 120523 120524 120525 120526 120527 120528 120529 120530 120531 120532 120533 120534 120535 120536 120537 120538 120539 120540 120541 120542 120543 120544 120545 120546 120547 120548 120549 120550 120551 120552 120553 120554 120555 120556 120557 120558 120559 120560 120561 120562 120563 120564 120565 120566 120567 120568 120569 120570 120571 120572 120573 120574 120575 120576 120577 120578 120579 120580 120581 120582 120583 120584 120585 120586 120587 120588 120589 120590 120591 120592 120593 120594 120595 120596 120597 120598 120599 120600 120601 120602 120603 120604 120605 120606 120607 120608 120609 120610 120611 120612 120613 120614 120615 120616 120617 120618 120619 120620 120621 120622 120623 120624 120625 120626 120627 120628 120629 120630 120631 120632 120633 120634 120635 120636 120637 120638 120639 120640 120641 120642 120643 120644 120645 120646 120647 120648 120649 120650 120651 120652 120653 120654 120655 120656 120657 120658 120659 120660 120661 120662 120663 120664 120665 120666 120667 120668 120669 120670 120671 120672 120673 120674 120675 120676 120677 120678 120679 120680 120681 120682 120683 120684 120685 120686 120687 120688 120689 120690 120691 120692 120693 120694 120695 120696 120697 120698 120699 120700 120701 120702 120703 120704 120705 120706 120707 120708 120709 120710 120711 120712 120713 120714 120715 120716 120717 120718 120719 120720 120721 120722 120723 120724 120725 120726 120727 120728 120729 120730 120731 120732 120733 120734 120735 120736 120737 120738 120739 120740 120741 120742 120743 120744 120745 120746 120747 120748 120749 120750 120751 120752 120753 120754 120755 120756 120757 120758 120759 120760 120761 120762 120763 120764 120765 120766 120767 120768 120769 120770 120771 120772 120773 120774 120775 120776 120777 120778 120779 120780 120781 120782 120783 120784 120785 120786 120787 120788 120789 120790 120791 120792 120793 120794 120795 120796 120797 120798 120799 120800 120801 120802 120803 120804 120805 120806 120807 120808 120809 120810 120811 120812 120813 120814 120815 120816 120817 120818 120819 120820 120821 120822 120823 120824 120825 120826 120827 120828 120829 120830 120831 120832 120833 120834 120835 120836 120837 120838 120839 120840 120841 120842 120843 120844 120845 120846 120847 120848 120849 120850 120851 120852 120853 120854 120855 120856 120857 120858 120859 |
** method (xFilter etc.) that may directly or indirectly call this function
** must call sqlite3Fts3SegmentsClose() before returning.
*/
SQLITE_PRIVATE int sqlite3Fts3ReadBlock(
Fts3Table *p, /* FTS3 table handle */
sqlite3_int64 iBlockid, /* Access the row with blockid=$iBlockid */
char **paBlob, /* OUT: Blob data in malloc'd buffer */
int *pnBlob, /* OUT: Size of blob data */
int *pnLoad /* OUT: Bytes actually loaded */
){
int rc; /* Return code */
/* pnBlob must be non-NULL. paBlob may be NULL or non-NULL. */
assert( pnBlob);
if( p->pSegments ){
rc = sqlite3_blob_reopen(p->pSegments, iBlockid);
}else{
if( 0==p->zSegmentsTbl ){
p->zSegmentsTbl = sqlite3_mprintf("%s_segments", p->zName);
if( 0==p->zSegmentsTbl ) return SQLITE_NOMEM;
}
rc = sqlite3_blob_open(
p->db, p->zDb, p->zSegmentsTbl, "block", iBlockid, 0, &p->pSegments
);
}
if( rc==SQLITE_OK ){
int nByte = sqlite3_blob_bytes(p->pSegments);
*pnBlob = nByte;
if( paBlob ){
char *aByte = sqlite3_malloc(nByte + FTS3_NODE_PADDING);
if( !aByte ){
rc = SQLITE_NOMEM;
}else{
if( pnLoad && nByte>(FTS3_NODE_CHUNK_THRESHOLD) ){
nByte = FTS3_NODE_CHUNKSIZE;
*pnLoad = nByte;
}
rc = sqlite3_blob_read(p->pSegments, aByte, nByte, 0);
memset(&aByte[nByte], 0, FTS3_NODE_PADDING);
if( rc!=SQLITE_OK ){
sqlite3_free(aByte);
aByte = 0;
}
}
*paBlob = aByte;
}
}
return rc;
}
/*
** Close the blob handle at p->pSegments, if it is open. See comments above
** the sqlite3Fts3ReadBlock() function for details.
*/
SQLITE_PRIVATE void sqlite3Fts3SegmentsClose(Fts3Table *p){
sqlite3_blob_close(p->pSegments);
p->pSegments = 0;
}
static int fts3SegReaderIncrRead(Fts3SegReader *pReader){
int nRead; /* Number of bytes to read */
int rc; /* Return code */
nRead = MIN(pReader->nNode - pReader->nPopulate, FTS3_NODE_CHUNKSIZE);
rc = sqlite3_blob_read(
pReader->pBlob,
&pReader->aNode[pReader->nPopulate],
nRead,
pReader->nPopulate
);
if( rc==SQLITE_OK ){
pReader->nPopulate += nRead;
memset(&pReader->aNode[pReader->nPopulate], 0, FTS3_NODE_PADDING);
if( pReader->nPopulate==pReader->nNode ){
sqlite3_blob_close(pReader->pBlob);
pReader->pBlob = 0;
pReader->nPopulate = 0;
}
}
return rc;
}
static int fts3SegReaderRequire(Fts3SegReader *pReader, char *pFrom, int nByte){
int rc = SQLITE_OK;
assert( !pReader->pBlob
|| (pFrom>=pReader->aNode && pFrom<&pReader->aNode[pReader->nNode])
);
while( pReader->pBlob && rc==SQLITE_OK
&& (pFrom - pReader->aNode + nByte)>pReader->nPopulate
){
rc = fts3SegReaderIncrRead(pReader);
}
return rc;
}
/*
** Move the iterator passed as the first argument to the next term in the
** segment. If successful, SQLITE_OK is returned. If there is no next term,
** SQLITE_DONE. Otherwise, an SQLite error code.
*/
static int fts3SegReaderNext(
Fts3Table *p,
Fts3SegReader *pReader,
int bIncr
){
int rc; /* Return code of various sub-routines */
char *pNext; /* Cursor variable */
int nPrefix; /* Number of bytes in term prefix */
int nSuffix; /* Number of bytes in term suffix */
if( !pReader->aDoclist ){
pNext = pReader->aNode;
}else{
pNext = &pReader->aDoclist[pReader->nDoclist];
}
if( !pNext || pNext>=&pReader->aNode[pReader->nNode] ){
if( fts3SegReaderIsPending(pReader) ){
Fts3HashElem *pElem = *(pReader->ppNextElem);
if( pElem==0 ){
pReader->aNode = 0;
}else{
PendingList *pList = (PendingList *)fts3HashData(pElem);
pReader->zTerm = (char *)fts3HashKey(pElem);
pReader->nTerm = fts3HashKeysize(pElem);
pReader->nNode = pReader->nDoclist = pList->nData + 1;
pReader->aNode = pReader->aDoclist = pList->aData;
pReader->ppNextElem++;
assert( pReader->aNode );
}
return SQLITE_OK;
}
if( !fts3SegReaderIsRootOnly(pReader) ){
sqlite3_free(pReader->aNode);
sqlite3_blob_close(pReader->pBlob);
pReader->pBlob = 0;
}
pReader->aNode = 0;
/* If iCurrentBlock>=iLeafEndBlock, this is an EOF condition. All leaf
** blocks have already been traversed. */
assert( pReader->iCurrentBlock<=pReader->iLeafEndBlock );
if( pReader->iCurrentBlock>=pReader->iLeafEndBlock ){
return SQLITE_OK;
}
rc = sqlite3Fts3ReadBlock(
p, ++pReader->iCurrentBlock, &pReader->aNode, &pReader->nNode,
(bIncr ? &pReader->nPopulate : 0)
);
if( rc!=SQLITE_OK ) return rc;
assert( pReader->pBlob==0 );
if( bIncr && pReader->nPopulate<pReader->nNode ){
pReader->pBlob = p->pSegments;
p->pSegments = 0;
}
pNext = pReader->aNode;
}
assert( !fts3SegReaderIsPending(pReader) );
rc = fts3SegReaderRequire(pReader, pNext, FTS3_VARINT_MAX*2);
if( rc!=SQLITE_OK ) return rc;
/* Because of the FTS3_NODE_PADDING bytes of padding, the following is
** safe (no risk of overread) even if the node data is corrupted. */
pNext += sqlite3Fts3GetVarint32(pNext, &nPrefix);
pNext += sqlite3Fts3GetVarint32(pNext, &nSuffix);
if( nPrefix<0 || nSuffix<=0
|| &pNext[nSuffix]>&pReader->aNode[pReader->nNode]
){
return SQLITE_CORRUPT_VTAB;
}
if( nPrefix+nSuffix>pReader->nTermAlloc ){
int nNew = (nPrefix+nSuffix)*2;
char *zNew = sqlite3_realloc(pReader->zTerm, nNew);
if( !zNew ){
return SQLITE_NOMEM;
}
pReader->zTerm = zNew;
pReader->nTermAlloc = nNew;
}
rc = fts3SegReaderRequire(pReader, pNext, nSuffix+FTS3_VARINT_MAX);
if( rc!=SQLITE_OK ) return rc;
memcpy(&pReader->zTerm[nPrefix], pNext, nSuffix);
pReader->nTerm = nPrefix+nSuffix;
pNext += nSuffix;
pNext += sqlite3Fts3GetVarint32(pNext, &pReader->nDoclist);
pReader->aDoclist = pNext;
pReader->pOffsetList = 0;
/* Check that the doclist does not appear to extend past the end of the
** b-tree node. And that the final byte of the doclist is 0x00. If either
** of these statements is untrue, then the data structure is corrupt.
*/
if( &pReader->aDoclist[pReader->nDoclist]>&pReader->aNode[pReader->nNode]
|| (pReader->nPopulate==0 && pReader->aDoclist[pReader->nDoclist-1])
){
return SQLITE_CORRUPT_VTAB;
}
return SQLITE_OK;
}
/*
** Set the SegReader to point to the first docid in the doclist associated
** with the current term.
*/
static int fts3SegReaderFirstDocid(Fts3Table *pTab, Fts3SegReader *pReader){
int rc = SQLITE_OK;
assert( pReader->aDoclist );
assert( !pReader->pOffsetList );
if( pTab->bDescIdx && fts3SegReaderIsPending(pReader) ){
u8 bEof = 0;
pReader->iDocid = 0;
pReader->nOffsetList = 0;
sqlite3Fts3DoclistPrev(0,
pReader->aDoclist, pReader->nDoclist, &pReader->pOffsetList,
&pReader->iDocid, &pReader->nOffsetList, &bEof
);
}else{
rc = fts3SegReaderRequire(pReader, pReader->aDoclist, FTS3_VARINT_MAX);
if( rc==SQLITE_OK ){
int n = sqlite3Fts3GetVarint(pReader->aDoclist, &pReader->iDocid);
pReader->pOffsetList = &pReader->aDoclist[n];
}
}
return rc;
}
/*
** Advance the SegReader to point to the next docid in the doclist
** associated with the current term.
**
** If arguments ppOffsetList and pnOffsetList are not NULL, then
** *ppOffsetList is set to point to the first column-offset list
** in the doclist entry (i.e. immediately past the docid varint).
** *pnOffsetList is set to the length of the set of column-offset
** lists, not including the nul-terminator byte. For example:
*/
static int fts3SegReaderNextDocid(
Fts3Table *pTab,
Fts3SegReader *pReader, /* Reader to advance to next docid */
char **ppOffsetList, /* OUT: Pointer to current position-list */
int *pnOffsetList /* OUT: Length of *ppOffsetList in bytes */
){
int rc = SQLITE_OK;
char *p = pReader->pOffsetList;
char c = 0;
assert( p );
if( pTab->bDescIdx && fts3SegReaderIsPending(pReader) ){
/* A pending-terms seg-reader for an FTS4 table that uses order=desc.
** Pending-terms doclists are always built up in ascending order, so
** we have to iterate through them backwards here. */
u8 bEof = 0;
if( ppOffsetList ){
*ppOffsetList = pReader->pOffsetList;
*pnOffsetList = pReader->nOffsetList - 1;
}
sqlite3Fts3DoclistPrev(0,
pReader->aDoclist, pReader->nDoclist, &p, &pReader->iDocid,
&pReader->nOffsetList, &bEof
);
if( bEof ){
pReader->pOffsetList = 0;
}else{
pReader->pOffsetList = p;
}
}else{
/* Pointer p currently points at the first byte of an offset list. The
** following block advances it to point one byte past the end of
** the same offset list. */
while( 1 ){
/* The following line of code (and the "p++" below the while() loop) is
** normally all that is required to move pointer p to the desired
** position. The exception is if this node is being loaded from disk
** incrementally and pointer "p" now points to the first byte passed
** the populated part of pReader->aNode[].
*/
while( *p | c ) c = *p++ & 0x80;
assert( *p==0 );
if( pReader->pBlob==0 || p<&pReader->aNode[pReader->nPopulate] ) break;
rc = fts3SegReaderIncrRead(pReader);
if( rc!=SQLITE_OK ) return rc;
}
p++;
/* If required, populate the output variables with a pointer to and the
** size of the previous offset-list.
*/
if( ppOffsetList ){
*ppOffsetList = pReader->pOffsetList;
*pnOffsetList = (int)(p - pReader->pOffsetList - 1);
}
/* If there are no more entries in the doclist, set pOffsetList to
** NULL. Otherwise, set Fts3SegReader.iDocid to the next docid and
** Fts3SegReader.pOffsetList to point to the next offset list before
** returning.
*/
if( p>=&pReader->aDoclist[pReader->nDoclist] ){
pReader->pOffsetList = 0;
}else{
rc = fts3SegReaderRequire(pReader, p, FTS3_VARINT_MAX);
if( rc==SQLITE_OK ){
sqlite3_int64 iDelta;
pReader->pOffsetList = p + sqlite3Fts3GetVarint(p, &iDelta);
if( pTab->bDescIdx ){
pReader->iDocid -= iDelta;
}else{
pReader->iDocid += iDelta;
}
}
}
}
return SQLITE_OK;
}
SQLITE_PRIVATE int sqlite3Fts3MsrOvfl(
Fts3Cursor *pCsr,
Fts3MultiSegReader *pMsr,
int *pnOvfl
){
Fts3Table *p = (Fts3Table*)pCsr->base.pVtab;
int nOvfl = 0;
int ii;
int rc = SQLITE_OK;
int pgsz = p->nPgsz;
assert( p->bHasStat );
assert( pgsz>0 );
for(ii=0; rc==SQLITE_OK && ii<pMsr->nSegment; ii++){
Fts3SegReader *pReader = pMsr->apSegment[ii];
if( !fts3SegReaderIsPending(pReader)
&& !fts3SegReaderIsRootOnly(pReader)
){
int jj;
for(jj=pReader->iStartBlock; jj<=pReader->iLeafEndBlock; jj++){
int nBlob;
rc = sqlite3Fts3ReadBlock(p, jj, 0, &nBlob, 0);
if( rc!=SQLITE_OK ) break;
if( (nBlob+35)>pgsz ){
nOvfl += (nBlob + 34)/pgsz;
}
}
}
}
*pnOvfl = nOvfl;
return rc;
}
/*
** Free all allocations associated with the iterator passed as the
** second argument.
*/
SQLITE_PRIVATE void sqlite3Fts3SegReaderFree(Fts3SegReader *pReader){
if( pReader && !fts3SegReaderIsPending(pReader) ){
sqlite3_free(pReader->zTerm);
if( !fts3SegReaderIsRootOnly(pReader) ){
sqlite3_free(pReader->aNode);
sqlite3_blob_close(pReader->pBlob);
}
}
sqlite3_free(pReader);
}
/*
** Allocate a new SegReader object.
|
| ︙ | ︙ | |||
120008 120009 120010 120011 120012 120013 120014 120015 120016 120017 120018 120019 | } return c; } /* ** This function is used to allocate an Fts3SegReader that iterates through ** a subset of the terms stored in the Fts3Table.pendingTerms array. */ SQLITE_PRIVATE int sqlite3Fts3SegReaderPending( Fts3Table *p, /* Virtual table handle */ const char *zTerm, /* Term to search for */ int nTerm, /* Size of buffer zTerm */ | > > > > > > > > > > > > > > > > | > > | | > > > | | | 120922 120923 120924 120925 120926 120927 120928 120929 120930 120931 120932 120933 120934 120935 120936 120937 120938 120939 120940 120941 120942 120943 120944 120945 120946 120947 120948 120949 120950 120951 120952 120953 120954 120955 120956 120957 120958 120959 120960 120961 120962 120963 120964 120965 120966 120967 120968 120969 120970 120971 120972 120973 120974 120975 120976 120977 120978 120979 120980 120981 120982 120983 120984 120985 120986 120987 120988 120989 120990 120991 120992 120993 120994 120995 120996 120997 120998 120999 121000 121001 121002 121003 121004 121005 121006 121007 121008 121009 121010 121011 121012 121013 121014 121015 121016 121017 121018 121019 121020 121021 121022 121023 121024 |
}
return c;
}
/*
** This function is used to allocate an Fts3SegReader that iterates through
** a subset of the terms stored in the Fts3Table.pendingTerms array.
**
** If the isPrefixIter parameter is zero, then the returned SegReader iterates
** through each term in the pending-terms table. Or, if isPrefixIter is
** non-zero, it iterates through each term and its prefixes. For example, if
** the pending terms hash table contains the terms "sqlite", "mysql" and
** "firebird", then the iterator visits the following 'terms' (in the order
** shown):
**
** f fi fir fire fireb firebi firebir firebird
** m my mys mysq mysql
** s sq sql sqli sqlit sqlite
**
** Whereas if isPrefixIter is zero, the terms visited are:
**
** firebird mysql sqlite
*/
SQLITE_PRIVATE int sqlite3Fts3SegReaderPending(
Fts3Table *p, /* Virtual table handle */
int iIndex, /* Index for p->aIndex */
const char *zTerm, /* Term to search for */
int nTerm, /* Size of buffer zTerm */
int bPrefix, /* True for a prefix iterator */
Fts3SegReader **ppReader /* OUT: SegReader for pending-terms */
){
Fts3SegReader *pReader = 0; /* Fts3SegReader object to return */
Fts3HashElem **aElem = 0; /* Array of term hash entries to scan */
int nElem = 0; /* Size of array at aElem */
int rc = SQLITE_OK; /* Return Code */
Fts3Hash *pHash;
pHash = &p->aIndex[iIndex].hPending;
if( bPrefix ){
int nAlloc = 0; /* Size of allocated array at aElem */
Fts3HashElem *pE = 0; /* Iterator variable */
for(pE=fts3HashFirst(pHash); pE; pE=fts3HashNext(pE)){
char *zKey = (char *)fts3HashKey(pE);
int nKey = fts3HashKeysize(pE);
if( nTerm==0 || (nKey>=nTerm && 0==memcmp(zKey, zTerm, nTerm)) ){
if( nElem==nAlloc ){
Fts3HashElem **aElem2;
nAlloc += 16;
aElem2 = (Fts3HashElem **)sqlite3_realloc(
aElem, nAlloc*sizeof(Fts3HashElem *)
);
if( !aElem2 ){
rc = SQLITE_NOMEM;
nElem = 0;
break;
}
aElem = aElem2;
}
aElem[nElem++] = pE;
}
}
/* If more than one term matches the prefix, sort the Fts3HashElem
** objects in term order using qsort(). This uses the same comparison
** callback as is used when flushing terms to disk.
*/
if( nElem>1 ){
qsort(aElem, nElem, sizeof(Fts3HashElem *), fts3CompareElemByTerm);
}
}else{
/* The query is a simple term lookup that matches at most one term in
** the index. All that is required is a straight hash-lookup. */
Fts3HashElem *pE = fts3HashFindElem(pHash, zTerm, nTerm);
if( pE ){
aElem = &pE;
nElem = 1;
}
}
if( nElem>0 ){
int nByte = sizeof(Fts3SegReader) + (nElem+1)*sizeof(Fts3HashElem *);
pReader = (Fts3SegReader *)sqlite3_malloc(nByte);
if( !pReader ){
rc = SQLITE_NOMEM;
}else{
memset(pReader, 0, nByte);
pReader->iIdx = 0x7FFFFFFF;
pReader->ppNextElem = (Fts3HashElem **)&pReader[1];
memcpy(pReader->ppNextElem, aElem, nElem*sizeof(Fts3HashElem *));
}
}
if( bPrefix ){
sqlite3_free(aElem);
}
*ppReader = pReader;
return rc;
}
/*
|
| ︙ | ︙ | |||
120135 120136 120137 120138 120139 120140 120141 120142 120143 120144 120145 120146 120147 120148 |
int rc = (pLhs->pOffsetList==0)-(pRhs->pOffsetList==0);
if( rc==0 ){
if( pLhs->iDocid==pRhs->iDocid ){
rc = pRhs->iIdx - pLhs->iIdx;
}else{
rc = (pLhs->iDocid > pRhs->iDocid) ? 1 : -1;
}
}
assert( pLhs->aNode && pRhs->aNode );
return rc;
}
/*
** Compare the term that the Fts3SegReader object passed as the first argument
| > > > > > > > > > > > > | 121070 121071 121072 121073 121074 121075 121076 121077 121078 121079 121080 121081 121082 121083 121084 121085 121086 121087 121088 121089 121090 121091 121092 121093 121094 121095 |
int rc = (pLhs->pOffsetList==0)-(pRhs->pOffsetList==0);
if( rc==0 ){
if( pLhs->iDocid==pRhs->iDocid ){
rc = pRhs->iIdx - pLhs->iIdx;
}else{
rc = (pLhs->iDocid > pRhs->iDocid) ? 1 : -1;
}
}
assert( pLhs->aNode && pRhs->aNode );
return rc;
}
static int fts3SegReaderDoclistCmpRev(Fts3SegReader *pLhs, Fts3SegReader *pRhs){
int rc = (pLhs->pOffsetList==0)-(pRhs->pOffsetList==0);
if( rc==0 ){
if( pLhs->iDocid==pRhs->iDocid ){
rc = pRhs->iIdx - pLhs->iIdx;
}else{
rc = (pLhs->iDocid < pRhs->iDocid) ? 1 : -1;
}
}
assert( pLhs->aNode && pRhs->aNode );
return rc;
}
/*
** Compare the term that the Fts3SegReader object passed as the first argument
|
| ︙ | ︙ | |||
120687 120688 120689 120690 120691 120692 120693 |
}
rc = sqlite3_reset(pStmt);
}
return rc;
}
/*
| < | > > | | > > > > > > > | > > < | > | 121634 121635 121636 121637 121638 121639 121640 121641 121642 121643 121644 121645 121646 121647 121648 121649 121650 121651 121652 121653 121654 121655 121656 121657 121658 121659 121660 121661 121662 121663 121664 121665 121666 121667 121668 121669 121670 121671 121672 121673 121674 121675 121676 121677 121678 121679 121680 121681 121682 121683 121684 121685 121686 121687 121688 121689 121690 121691 121692 |
}
rc = sqlite3_reset(pStmt);
}
return rc;
}
/*
** Set *pnMax to the largest segment level in the database for the index
** iIndex.
**
** Segment levels are stored in the 'level' column of the %_segdir table.
**
** Return SQLITE_OK if successful, or an SQLite error code if not.
*/
static int fts3SegmentMaxLevel(Fts3Table *p, int iIndex, int *pnMax){
sqlite3_stmt *pStmt;
int rc;
assert( iIndex>=0 && iIndex<p->nIndex );
/* Set pStmt to the compiled version of:
**
** SELECT max(level) FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ?
**
** (1024 is actually the value of macro FTS3_SEGDIR_PREFIXLEVEL_STR).
*/
rc = fts3SqlStmt(p, SQL_SELECT_SEGDIR_MAX_LEVEL, &pStmt, 0);
if( rc!=SQLITE_OK ) return rc;
sqlite3_bind_int(pStmt, 1, iIndex*FTS3_SEGDIR_MAXLEVEL);
sqlite3_bind_int(pStmt, 2, (iIndex+1)*FTS3_SEGDIR_MAXLEVEL - 1);
if( SQLITE_ROW==sqlite3_step(pStmt) ){
*pnMax = sqlite3_column_int(pStmt, 0);
}
return sqlite3_reset(pStmt);
}
/*
** This function is used after merging multiple segments into a single large
** segment to delete the old, now redundant, segment b-trees. Specifically,
** it:
**
** 1) Deletes all %_segments entries for the segments associated with
** each of the SegReader objects in the array passed as the third
** argument, and
**
** 2) deletes all %_segdir entries with level iLevel, or all %_segdir
** entries regardless of level if (iLevel<0).
**
** SQLITE_OK is returned if successful, otherwise an SQLite error code.
*/
static int fts3DeleteSegdir(
Fts3Table *p, /* Virtual table handle */
int iIndex, /* Index for p->aIndex */
int iLevel, /* Level of %_segdir entries to delete */
Fts3SegReader **apSegment, /* Array of SegReader objects */
int nReader /* Size of array apSegment */
){
int rc; /* Return Code */
int i; /* Iterator variable */
sqlite3_stmt *pDelete; /* SQL statement to delete rows */
|
| ︙ | ︙ | |||
120744 120745 120746 120747 120748 120749 120750 120751 |
rc = sqlite3_reset(pDelete);
}
}
if( rc!=SQLITE_OK ){
return rc;
}
if( iLevel==FTS3_SEGCURSOR_ALL ){
| > | > | | > < | | > > > > | | | < | 121701 121702 121703 121704 121705 121706 121707 121708 121709 121710 121711 121712 121713 121714 121715 121716 121717 121718 121719 121720 121721 121722 121723 121724 121725 121726 121727 121728 121729 121730 121731 121732 |
rc = sqlite3_reset(pDelete);
}
}
if( rc!=SQLITE_OK ){
return rc;
}
assert( iLevel>=0 || iLevel==FTS3_SEGCURSOR_ALL );
if( iLevel==FTS3_SEGCURSOR_ALL ){
rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_RANGE, &pDelete, 0);
if( rc==SQLITE_OK ){
sqlite3_bind_int(pDelete, 1, iIndex*FTS3_SEGDIR_MAXLEVEL);
sqlite3_bind_int(pDelete, 2, (iIndex+1) * FTS3_SEGDIR_MAXLEVEL - 1);
}
}else{
rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_LEVEL, &pDelete, 0);
if( rc==SQLITE_OK ){
sqlite3_bind_int(pDelete, 1, iIndex*FTS3_SEGDIR_MAXLEVEL + iLevel);
}
}
if( rc==SQLITE_OK ){
sqlite3_step(pDelete);
rc = sqlite3_reset(pDelete);
}
return rc;
}
/*
** When this function is called, buffer *ppList (size *pnList bytes) contains
** a position list that may (or may not) feature multiple columns. This
|
| ︙ | ︙ | |||
120803 120804 120805 120806 120807 120808 120809 120810 |
p = &pList[1];
p += sqlite3Fts3GetVarint32(p, &iCurrent);
}
*ppList = pList;
*pnList = nList;
}
| | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > | | 121765 121766 121767 121768 121769 121770 121771 121772 121773 121774 121775 121776 121777 121778 121779 121780 121781 121782 121783 121784 121785 121786 121787 121788 121789 121790 121791 121792 121793 121794 121795 121796 121797 121798 121799 121800 121801 121802 121803 121804 121805 121806 121807 121808 121809 121810 121811 121812 121813 121814 121815 121816 121817 121818 121819 121820 121821 121822 121823 121824 121825 121826 121827 121828 121829 121830 121831 121832 121833 121834 121835 121836 121837 121838 121839 121840 121841 121842 121843 121844 121845 121846 121847 121848 121849 121850 121851 121852 121853 121854 121855 121856 121857 121858 121859 121860 121861 121862 121863 121864 121865 121866 121867 121868 121869 121870 121871 121872 121873 121874 121875 121876 121877 121878 121879 121880 121881 121882 121883 121884 121885 121886 121887 121888 121889 121890 121891 121892 121893 121894 121895 121896 121897 121898 121899 121900 121901 121902 121903 121904 121905 121906 121907 121908 121909 121910 121911 121912 121913 121914 121915 121916 121917 121918 121919 121920 121921 121922 121923 121924 121925 121926 121927 121928 121929 121930 121931 121932 121933 121934 121935 121936 121937 121938 121939 121940 121941 121942 121943 121944 121945 121946 121947 121948 121949 121950 |
p = &pList[1];
p += sqlite3Fts3GetVarint32(p, &iCurrent);
}
*ppList = pList;
*pnList = nList;
}
SQLITE_PRIVATE int sqlite3Fts3MsrIncrStart(
Fts3Table *p, /* Virtual table handle */
Fts3MultiSegReader *pCsr, /* Cursor object */
int iCol, /* Column to match on. */
const char *zTerm, /* Term to iterate through a doclist for */
int nTerm /* Number of bytes in zTerm */
){
int i;
int nSegment = pCsr->nSegment;
int (*xCmp)(Fts3SegReader *, Fts3SegReader *) = (
p->bDescIdx ? fts3SegReaderDoclistCmpRev : fts3SegReaderDoclistCmp
);
assert( pCsr->pFilter==0 );
assert( zTerm && nTerm>0 );
/* Advance each segment iterator until it points to the term zTerm/nTerm. */
for(i=0; i<nSegment; i++){
Fts3SegReader *pSeg = pCsr->apSegment[i];
do {
int rc = fts3SegReaderNext(p, pSeg, 1);
if( rc!=SQLITE_OK ) return rc;
}while( fts3SegReaderTermCmp(pSeg, zTerm, nTerm)<0 );
}
fts3SegReaderSort(pCsr->apSegment, nSegment, nSegment, fts3SegReaderCmp);
/* Determine how many of the segments actually point to zTerm/nTerm. */
for(i=0; i<nSegment; i++){
Fts3SegReader *pSeg = pCsr->apSegment[i];
if( !pSeg->aNode || fts3SegReaderTermCmp(pSeg, zTerm, nTerm) ){
break;
}
}
pCsr->nAdvance = i;
/* Advance each of the segments to point to the first docid. */
for(i=0; i<pCsr->nAdvance; i++){
int rc = fts3SegReaderFirstDocid(p, pCsr->apSegment[i]);
if( rc!=SQLITE_OK ) return rc;
}
fts3SegReaderSort(pCsr->apSegment, i, i, xCmp);
assert( iCol<0 || iCol<p->nColumn );
pCsr->iColFilter = iCol;
return SQLITE_OK;
}
SQLITE_PRIVATE int sqlite3Fts3MsrIncrNext(
Fts3Table *p, /* Virtual table handle */
Fts3MultiSegReader *pMsr, /* Multi-segment-reader handle */
sqlite3_int64 *piDocid, /* OUT: Docid value */
char **paPoslist, /* OUT: Pointer to position list */
int *pnPoslist /* OUT: Size of position list in bytes */
){
int nMerge = pMsr->nAdvance;
Fts3SegReader **apSegment = pMsr->apSegment;
int (*xCmp)(Fts3SegReader *, Fts3SegReader *) = (
p->bDescIdx ? fts3SegReaderDoclistCmpRev : fts3SegReaderDoclistCmp
);
if( nMerge==0 ){
*paPoslist = 0;
return SQLITE_OK;
}
while( 1 ){
Fts3SegReader *pSeg;
pSeg = pMsr->apSegment[0];
if( pSeg->pOffsetList==0 ){
*paPoslist = 0;
break;
}else{
int rc;
char *pList;
int nList;
int j;
sqlite3_int64 iDocid = apSegment[0]->iDocid;
rc = fts3SegReaderNextDocid(p, apSegment[0], &pList, &nList);
j = 1;
while( rc==SQLITE_OK
&& j<nMerge
&& apSegment[j]->pOffsetList
&& apSegment[j]->iDocid==iDocid
){
rc = fts3SegReaderNextDocid(p, apSegment[j], 0, 0);
j++;
}
if( rc!=SQLITE_OK ) return rc;
fts3SegReaderSort(pMsr->apSegment, nMerge, j, xCmp);
if( pMsr->iColFilter>=0 ){
fts3ColumnFilter(pMsr->iColFilter, &pList, &nList);
}
if( nList>0 ){
*piDocid = iDocid;
*paPoslist = pList;
*pnPoslist = nList;
break;
}
}
}
return SQLITE_OK;
}
SQLITE_PRIVATE int sqlite3Fts3SegReaderStart(
Fts3Table *p, /* Virtual table handle */
Fts3MultiSegReader *pCsr, /* Cursor object */
Fts3SegFilter *pFilter /* Restrictions on range of iteration */
){
int i;
/* Initialize the cursor object */
pCsr->pFilter = pFilter;
/* If the Fts3SegFilter defines a specific term (or term prefix) to search
** for, then advance each segment iterator until it points to a term of
** equal or greater value than the specified term. This prevents many
** unnecessary merge/sort operations for the case where single segment
** b-tree leaf nodes contain more than one term.
*/
for(i=0; i<pCsr->nSegment; i++){
int nTerm = pFilter->nTerm;
const char *zTerm = pFilter->zTerm;
Fts3SegReader *pSeg = pCsr->apSegment[i];
do {
int rc = fts3SegReaderNext(p, pSeg, 0);
if( rc!=SQLITE_OK ) return rc;
}while( zTerm && fts3SegReaderTermCmp(pSeg, zTerm, nTerm)<0 );
}
fts3SegReaderSort(
pCsr->apSegment, pCsr->nSegment, pCsr->nSegment, fts3SegReaderCmp);
return SQLITE_OK;
}
SQLITE_PRIVATE int sqlite3Fts3SegReaderStep(
Fts3Table *p, /* Virtual table handle */
Fts3MultiSegReader *pCsr /* Cursor object */
){
int rc = SQLITE_OK;
int isIgnoreEmpty = (pCsr->pFilter->flags & FTS3_SEGMENT_IGNORE_EMPTY);
int isRequirePos = (pCsr->pFilter->flags & FTS3_SEGMENT_REQUIRE_POS);
int isColFilter = (pCsr->pFilter->flags & FTS3_SEGMENT_COLUMN_FILTER);
int isPrefix = (pCsr->pFilter->flags & FTS3_SEGMENT_PREFIX);
int isScan = (pCsr->pFilter->flags & FTS3_SEGMENT_SCAN);
Fts3SegReader **apSegment = pCsr->apSegment;
int nSegment = pCsr->nSegment;
Fts3SegFilter *pFilter = pCsr->pFilter;
int (*xCmp)(Fts3SegReader *, Fts3SegReader *) = (
p->bDescIdx ? fts3SegReaderDoclistCmpRev : fts3SegReaderDoclistCmp
);
if( pCsr->nSegment==0 ) return SQLITE_OK;
do {
int nMerge;
int i;
/* Advance the first pCsr->nAdvance entries in the apSegment[] array
** forward. Then sort the list in order of current term again.
*/
for(i=0; i<pCsr->nAdvance; i++){
rc = fts3SegReaderNext(p, apSegment[i], 0);
if( rc!=SQLITE_OK ) return rc;
}
fts3SegReaderSort(apSegment, nSegment, pCsr->nAdvance, fts3SegReaderCmp);
pCsr->nAdvance = 0;
/* If all the seg-readers are at EOF, we're finished. return SQLITE_OK. */
assert( rc==SQLITE_OK );
|
| ︙ | ︙ | |||
120900 120901 120902 120903 120904 120905 120906 |
&& apSegment[nMerge]->nTerm==pCsr->nTerm
&& 0==memcmp(pCsr->zTerm, apSegment[nMerge]->zTerm, pCsr->nTerm)
){
nMerge++;
}
assert( isIgnoreEmpty || (isRequirePos && !isColFilter) );
| | > > > | | | | > > > > > > > > > > > > | | < < | > | | 121975 121976 121977 121978 121979 121980 121981 121982 121983 121984 121985 121986 121987 121988 121989 121990 121991 121992 121993 121994 121995 121996 121997 121998 121999 122000 122001 122002 122003 122004 122005 122006 122007 122008 122009 122010 122011 122012 122013 122014 122015 122016 122017 122018 122019 122020 122021 122022 122023 122024 122025 122026 122027 122028 122029 122030 122031 122032 122033 122034 122035 122036 122037 122038 122039 122040 122041 122042 122043 122044 122045 122046 122047 122048 122049 122050 122051 122052 122053 122054 122055 122056 122057 122058 122059 122060 122061 122062 122063 122064 122065 122066 122067 122068 122069 122070 122071 122072 122073 122074 122075 122076 |
&& apSegment[nMerge]->nTerm==pCsr->nTerm
&& 0==memcmp(pCsr->zTerm, apSegment[nMerge]->zTerm, pCsr->nTerm)
){
nMerge++;
}
assert( isIgnoreEmpty || (isRequirePos && !isColFilter) );
if( nMerge==1
&& !isIgnoreEmpty
&& (p->bDescIdx==0 || fts3SegReaderIsPending(apSegment[0])==0)
){
pCsr->aDoclist = apSegment[0]->aDoclist;
pCsr->nDoclist = apSegment[0]->nDoclist;
rc = SQLITE_ROW;
}else{
int nDoclist = 0; /* Size of doclist */
sqlite3_int64 iPrev = 0; /* Previous docid stored in doclist */
/* The current term of the first nMerge entries in the array
** of Fts3SegReader objects is the same. The doclists must be merged
** and a single term returned with the merged doclist.
*/
for(i=0; i<nMerge; i++){
fts3SegReaderFirstDocid(p, apSegment[i]);
}
fts3SegReaderSort(apSegment, nMerge, nMerge, xCmp);
while( apSegment[0]->pOffsetList ){
int j; /* Number of segments that share a docid */
char *pList;
int nList;
int nByte;
sqlite3_int64 iDocid = apSegment[0]->iDocid;
fts3SegReaderNextDocid(p, apSegment[0], &pList, &nList);
j = 1;
while( j<nMerge
&& apSegment[j]->pOffsetList
&& apSegment[j]->iDocid==iDocid
){
fts3SegReaderNextDocid(p, apSegment[j], 0, 0);
j++;
}
if( isColFilter ){
fts3ColumnFilter(pFilter->iCol, &pList, &nList);
}
if( !isIgnoreEmpty || nList>0 ){
/* Calculate the 'docid' delta value to write into the merged
** doclist. */
sqlite3_int64 iDelta;
if( p->bDescIdx && nDoclist>0 ){
iDelta = iPrev - iDocid;
}else{
iDelta = iDocid - iPrev;
}
assert( iDelta>0 || (nDoclist==0 && iDelta==iDocid) );
assert( nDoclist>0 || iDelta==iDocid );
nByte = sqlite3Fts3VarintLen(iDelta) + (isRequirePos?nList+1:0);
if( nDoclist+nByte>pCsr->nBuffer ){
char *aNew;
pCsr->nBuffer = (nDoclist+nByte)*2;
aNew = sqlite3_realloc(pCsr->aBuffer, pCsr->nBuffer);
if( !aNew ){
return SQLITE_NOMEM;
}
pCsr->aBuffer = aNew;
}
nDoclist += sqlite3Fts3PutVarint(&pCsr->aBuffer[nDoclist], iDelta);
iPrev = iDocid;
if( isRequirePos ){
memcpy(&pCsr->aBuffer[nDoclist], pList, nList);
nDoclist += nList;
pCsr->aBuffer[nDoclist++] = '\0';
}
}
fts3SegReaderSort(apSegment, nMerge, j, xCmp);
}
if( nDoclist>0 ){
pCsr->aDoclist = pCsr->aBuffer;
pCsr->nDoclist = nDoclist;
rc = SQLITE_ROW;
}
}
pCsr->nAdvance = nMerge;
}while( rc==SQLITE_OK );
return rc;
}
SQLITE_PRIVATE void sqlite3Fts3SegReaderFinish(
Fts3MultiSegReader *pCsr /* Cursor object */
){
if( pCsr ){
int i;
for(i=0; i<pCsr->nSegment; i++){
sqlite3Fts3SegReaderFree(pCsr->apSegment[i]);
}
sqlite3_free(pCsr->apSegment);
|
| ︙ | ︙ | |||
121000 121001 121002 121003 121004 121005 121006 | ** currently present in the database. ** ** If this function is called with iLevel<0, but there is only one ** segment in the database, SQLITE_DONE is returned immediately. ** Otherwise, if successful, SQLITE_OK is returned. If an error occurs, ** an SQLite error code is returned. */ | | | | > > > > > > > > | | | < | > > > > > | < | > | > | > | | > | > > > | > > > > | 122089 122090 122091 122092 122093 122094 122095 122096 122097 122098 122099 122100 122101 122102 122103 122104 122105 122106 122107 122108 122109 122110 122111 122112 122113 122114 122115 122116 122117 122118 122119 122120 122121 122122 122123 122124 122125 122126 122127 122128 122129 122130 122131 122132 122133 122134 122135 122136 122137 122138 122139 122140 122141 122142 122143 122144 122145 122146 122147 122148 122149 122150 122151 122152 122153 122154 122155 122156 122157 122158 122159 122160 122161 122162 122163 122164 122165 122166 122167 122168 122169 122170 122171 122172 122173 122174 122175 122176 122177 122178 122179 122180 122181 122182 122183 122184 122185 122186 122187 122188 |
** currently present in the database.
**
** If this function is called with iLevel<0, but there is only one
** segment in the database, SQLITE_DONE is returned immediately.
** Otherwise, if successful, SQLITE_OK is returned. If an error occurs,
** an SQLite error code is returned.
*/
static int fts3SegmentMerge(Fts3Table *p, int iIndex, int iLevel){
int rc; /* Return code */
int iIdx = 0; /* Index of new segment */
int iNewLevel = 0; /* Level/index to create new segment at */
SegmentWriter *pWriter = 0; /* Used to write the new, merged, segment */
Fts3SegFilter filter; /* Segment term filter condition */
Fts3MultiSegReader csr; /* Cursor to iterate through level(s) */
int bIgnoreEmpty = 0; /* True to ignore empty segments */
assert( iLevel==FTS3_SEGCURSOR_ALL
|| iLevel==FTS3_SEGCURSOR_PENDING
|| iLevel>=0
);
assert( iLevel<FTS3_SEGDIR_MAXLEVEL );
assert( iIndex>=0 && iIndex<p->nIndex );
rc = sqlite3Fts3SegReaderCursor(p, iIndex, iLevel, 0, 0, 1, 0, &csr);
if( rc!=SQLITE_OK || csr.nSegment==0 ) goto finished;
if( iLevel==FTS3_SEGCURSOR_ALL ){
/* This call is to merge all segments in the database to a single
** segment. The level of the new segment is equal to the the numerically
** greatest segment level currently present in the database for this
** index. The idx of the new segment is always 0. */
if( csr.nSegment==1 ){
rc = SQLITE_DONE;
goto finished;
}
rc = fts3SegmentMaxLevel(p, iIndex, &iNewLevel);
bIgnoreEmpty = 1;
}else if( iLevel==FTS3_SEGCURSOR_PENDING ){
iNewLevel = iIndex * FTS3_SEGDIR_MAXLEVEL;
rc = fts3AllocateSegdirIdx(p, iIndex, 0, &iIdx);
}else{
/* This call is to merge all segments at level iLevel. find the next
** available segment index at level iLevel+1. The call to
** fts3AllocateSegdirIdx() will merge the segments at level iLevel+1 to
** a single iLevel+2 segment if necessary. */
rc = fts3AllocateSegdirIdx(p, iIndex, iLevel+1, &iIdx);
iNewLevel = iIndex * FTS3_SEGDIR_MAXLEVEL + iLevel+1;
}
if( rc!=SQLITE_OK ) goto finished;
assert( csr.nSegment>0 );
assert( iNewLevel>=(iIndex*FTS3_SEGDIR_MAXLEVEL) );
assert( iNewLevel<((iIndex+1)*FTS3_SEGDIR_MAXLEVEL) );
memset(&filter, 0, sizeof(Fts3SegFilter));
filter.flags = FTS3_SEGMENT_REQUIRE_POS;
filter.flags |= (bIgnoreEmpty ? FTS3_SEGMENT_IGNORE_EMPTY : 0);
rc = sqlite3Fts3SegReaderStart(p, &csr, &filter);
while( SQLITE_OK==rc ){
rc = sqlite3Fts3SegReaderStep(p, &csr);
if( rc!=SQLITE_ROW ) break;
rc = fts3SegWriterAdd(p, &pWriter, 1,
csr.zTerm, csr.nTerm, csr.aDoclist, csr.nDoclist);
}
if( rc!=SQLITE_OK ) goto finished;
assert( pWriter );
if( iLevel!=FTS3_SEGCURSOR_PENDING ){
rc = fts3DeleteSegdir(p, iIndex, iLevel, csr.apSegment, csr.nSegment);
if( rc!=SQLITE_OK ) goto finished;
}
rc = fts3SegWriterFlush(p, pWriter, iNewLevel, iIdx);
finished:
fts3SegWriterFree(pWriter);
sqlite3Fts3SegReaderFinish(&csr);
return rc;
}
/*
** Flush the contents of pendingTerms to level 0 segments.
*/
SQLITE_PRIVATE int sqlite3Fts3PendingTermsFlush(Fts3Table *p){
int rc = SQLITE_OK;
int i;
for(i=0; rc==SQLITE_OK && i<p->nIndex; i++){
rc = fts3SegmentMerge(p, i, FTS3_SEGCURSOR_PENDING);
if( rc==SQLITE_DONE ) rc = SQLITE_OK;
}
sqlite3Fts3PendingTermsClear(p);
return rc;
}
/*
** Encode N integers as varints into a blob.
*/
static void fts3EncodeIntArray(
int N, /* The number of integers to encode */
|
| ︙ | ︙ | |||
121213 121214 121215 121216 121217 121218 121219 121220 121221 121222 121223 121224 121225 121226 121227 121228 121229 121230 121231 121232 121233 121234 121235 121236 |
return;
}
sqlite3_bind_blob(pStmt, 1, pBlob, nBlob, SQLITE_STATIC);
sqlite3_step(pStmt);
*pRC = sqlite3_reset(pStmt);
sqlite3_free(a);
}
/*
** Handle a 'special' INSERT of the form:
**
** "INSERT INTO tbl(tbl) VALUES(<expr>)"
**
** Argument pVal contains the result of <expr>. Currently the only
** meaningful value to insert is the text 'optimize'.
*/
static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
int rc; /* Return Code */
const char *zVal = (const char *)sqlite3_value_text(pVal);
int nVal = sqlite3_value_bytes(pVal);
if( !zVal ){
return SQLITE_NOMEM;
}else if( nVal==8 && 0==sqlite3_strnicmp(zVal, "optimize", 8) ){
| > > > > > > > > > > > > > > > > > | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < | | 122324 122325 122326 122327 122328 122329 122330 122331 122332 122333 122334 122335 122336 122337 122338 122339 122340 122341 122342 122343 122344 122345 122346 122347 122348 122349 122350 122351 122352 122353 122354 122355 122356 122357 122358 122359 122360 122361 122362 122363 122364 122365 122366 122367 122368 122369 122370 122371 122372 122373 122374 122375 122376 122377 122378 122379 122380 122381 122382 122383 122384 122385 122386 122387 122388 122389 122390 122391 122392 122393 122394 122395 122396 122397 122398 122399 122400 122401 122402 122403 122404 122405 122406 122407 122408 122409 |
return;
}
sqlite3_bind_blob(pStmt, 1, pBlob, nBlob, SQLITE_STATIC);
sqlite3_step(pStmt);
*pRC = sqlite3_reset(pStmt);
sqlite3_free(a);
}
static int fts3DoOptimize(Fts3Table *p, int bReturnDone){
int i;
int bSeenDone = 0;
int rc = SQLITE_OK;
for(i=0; rc==SQLITE_OK && i<p->nIndex; i++){
rc = fts3SegmentMerge(p, i, FTS3_SEGCURSOR_ALL);
if( rc==SQLITE_DONE ){
bSeenDone = 1;
rc = SQLITE_OK;
}
}
sqlite3Fts3SegmentsClose(p);
sqlite3Fts3PendingTermsClear(p);
return (rc==SQLITE_OK && bReturnDone && bSeenDone) ? SQLITE_DONE : rc;
}
/*
** Handle a 'special' INSERT of the form:
**
** "INSERT INTO tbl(tbl) VALUES(<expr>)"
**
** Argument pVal contains the result of <expr>. Currently the only
** meaningful value to insert is the text 'optimize'.
*/
static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
int rc; /* Return Code */
const char *zVal = (const char *)sqlite3_value_text(pVal);
int nVal = sqlite3_value_bytes(pVal);
if( !zVal ){
return SQLITE_NOMEM;
}else if( nVal==8 && 0==sqlite3_strnicmp(zVal, "optimize", 8) ){
rc = fts3DoOptimize(p, 0);
#ifdef SQLITE_TEST
}else if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){
p->nNodeSize = atoi(&zVal[9]);
rc = SQLITE_OK;
}else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){
p->nMaxPendingData = atoi(&zVal[11]);
rc = SQLITE_OK;
#endif
}else{
rc = SQLITE_ERROR;
}
return rc;
}
/*
** Delete all cached deferred doclists. Deferred doclists are cached
** (allocated) by the sqlite3Fts3CacheDeferredDoclists() function.
*/
SQLITE_PRIVATE void sqlite3Fts3FreeDeferredDoclists(Fts3Cursor *pCsr){
Fts3DeferredToken *pDef;
for(pDef=pCsr->pDeferred; pDef; pDef=pDef->pNext){
fts3PendingListDelete(pDef->pList);
pDef->pList = 0;
}
}
/*
** Free all entries in the pCsr->pDeffered list. Entries are added to
** this list using sqlite3Fts3DeferToken().
*/
SQLITE_PRIVATE void sqlite3Fts3FreeDeferredTokens(Fts3Cursor *pCsr){
Fts3DeferredToken *pDef;
Fts3DeferredToken *pNext;
for(pDef=pCsr->pDeferred; pDef; pDef=pNext){
pNext = pDef->pNext;
fts3PendingListDelete(pDef->pList);
sqlite3_free(pDef);
}
pCsr->pDeferred = 0;
}
/*
** Generate deferred-doclists for all tokens in the pCsr->pDeferred list
|
| ︙ | ︙ | |||
121374 121375 121376 121377 121378 121379 121380 121381 121382 121383 121384 121385 121386 121387 |
rc = fts3PendingListAppendVarint(&pDef->pList, 0);
}
}
}
return rc;
}
/*
** Add an entry for token pToken to the pCsr->pDeferred list.
*/
SQLITE_PRIVATE int sqlite3Fts3DeferToken(
Fts3Cursor *pCsr, /* Fts3 table cursor */
Fts3PhraseToken *pToken, /* Token to defer */
| > > > > > > > > > > > > > > > > > > > > > > > > > > > | 122459 122460 122461 122462 122463 122464 122465 122466 122467 122468 122469 122470 122471 122472 122473 122474 122475 122476 122477 122478 122479 122480 122481 122482 122483 122484 122485 122486 122487 122488 122489 122490 122491 122492 122493 122494 122495 122496 122497 122498 122499 |
rc = fts3PendingListAppendVarint(&pDef->pList, 0);
}
}
}
return rc;
}
SQLITE_PRIVATE int sqlite3Fts3DeferredTokenList(
Fts3DeferredToken *p,
char **ppData,
int *pnData
){
char *pRet;
int nSkip;
sqlite3_int64 dummy;
*ppData = 0;
*pnData = 0;
if( p->pList==0 ){
return SQLITE_OK;
}
pRet = (char *)sqlite3_malloc(p->pList->nData);
if( !pRet ) return SQLITE_NOMEM;
nSkip = sqlite3Fts3GetVarint(p->pList->aData, &dummy);
*pnData = p->pList->nData - nSkip;
*ppData = pRet;
memcpy(pRet, &p->pList->aData[nSkip], *pnData);
return SQLITE_OK;
}
/*
** Add an entry for token pToken to the pCsr->pDeferred list.
*/
SQLITE_PRIVATE int sqlite3Fts3DeferToken(
Fts3Cursor *pCsr, /* Fts3 table cursor */
Fts3PhraseToken *pToken, /* Token to defer */
|
| ︙ | ︙ | |||
121449 121450 121451 121452 121453 121454 121455 |
sqlite3_value **apVal, /* Array of arguments */
sqlite_int64 *pRowid /* OUT: The affected (or effected) rowid */
){
Fts3Table *p = (Fts3Table *)pVtab;
int rc = SQLITE_OK; /* Return Code */
int isRemove = 0; /* True for an UPDATE or DELETE */
sqlite3_int64 iRemove = 0; /* Rowid removed by UPDATE or DELETE */
| | | > | > > > | 122561 122562 122563 122564 122565 122566 122567 122568 122569 122570 122571 122572 122573 122574 122575 122576 122577 122578 122579 122580 122581 122582 122583 122584 122585 122586 122587 122588 122589 122590 122591 122592 122593 122594 122595 122596 122597 122598 122599 |
sqlite3_value **apVal, /* Array of arguments */
sqlite_int64 *pRowid /* OUT: The affected (or effected) rowid */
){
Fts3Table *p = (Fts3Table *)pVtab;
int rc = SQLITE_OK; /* Return Code */
int isRemove = 0; /* True for an UPDATE or DELETE */
sqlite3_int64 iRemove = 0; /* Rowid removed by UPDATE or DELETE */
u32 *aSzIns = 0; /* Sizes of inserted documents */
u32 *aSzDel; /* Sizes of deleted documents */
int nChng = 0; /* Net change in number of documents */
int bInsertDone = 0;
assert( p->pSegments==0 );
/* Check for a "special" INSERT operation. One of the form:
**
** INSERT INTO xyz(xyz) VALUES('command');
*/
if( nArg>1
&& sqlite3_value_type(apVal[0])==SQLITE_NULL
&& sqlite3_value_type(apVal[p->nColumn+2])!=SQLITE_NULL
){
rc = fts3SpecialInsert(p, apVal[p->nColumn+2]);
goto update_out;
}
/* Allocate space to hold the change in document sizes */
aSzIns = sqlite3_malloc( sizeof(aSzIns[0])*(p->nColumn+1)*2 );
if( aSzIns==0 ){
rc = SQLITE_NOMEM;
goto update_out;
}
aSzDel = &aSzIns[p->nColumn+1];
memset(aSzIns, 0, sizeof(aSzIns[0])*(p->nColumn+1)*2);
/* If this is an INSERT operation, or an UPDATE that modifies the rowid
** value, then this operation requires constraint handling.
**
** If the on-conflict mode is REPLACE, this means that the existing row
|
| ︙ | ︙ | |||
121519 121520 121521 121522 121523 121524 121525 |
}else{
rc = fts3InsertData(p, apVal, pRowid);
bInsertDone = 1;
}
}
}
if( rc!=SQLITE_OK ){
| | < | 122635 122636 122637 122638 122639 122640 122641 122642 122643 122644 122645 122646 122647 122648 122649 |
}else{
rc = fts3InsertData(p, apVal, pRowid);
bInsertDone = 1;
}
}
}
if( rc!=SQLITE_OK ){
goto update_out;
}
/* If this is a DELETE or UPDATE operation, remove the old record. */
if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
assert( sqlite3_value_type(apVal[0])==SQLITE_INTEGER );
rc = fts3DeleteByRowid(p, apVal[0], &nChng, aSzDel);
isRemove = 1;
|
| ︙ | ︙ | |||
121553 121554 121555 121556 121557 121558 121559 121560 121561 121562 121563 121564 121565 121566 121567 121568 121569 121570 121571 121572 121573 |
nChng++;
}
if( p->bHasStat ){
fts3UpdateDocTotals(&rc, p, aSzIns, aSzDel, nChng);
}
sqlite3_free(aSzIns);
sqlite3Fts3SegmentsClose(p);
return rc;
}
/*
** Flush any data in the pending-terms hash table to disk. If successful,
** merge all segments in the database (including the new segment, if
** there was any data to flush) into a single segment.
*/
SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *p){
int rc;
rc = sqlite3_exec(p->db, "SAVEPOINT fts3", 0, 0, 0);
if( rc==SQLITE_OK ){
| > | | | | < < | 122668 122669 122670 122671 122672 122673 122674 122675 122676 122677 122678 122679 122680 122681 122682 122683 122684 122685 122686 122687 122688 122689 122690 122691 122692 122693 122694 122695 122696 122697 122698 122699 122700 |
nChng++;
}
if( p->bHasStat ){
fts3UpdateDocTotals(&rc, p, aSzIns, aSzDel, nChng);
}
update_out:
sqlite3_free(aSzIns);
sqlite3Fts3SegmentsClose(p);
return rc;
}
/*
** Flush any data in the pending-terms hash table to disk. If successful,
** merge all segments in the database (including the new segment, if
** there was any data to flush) into a single segment.
*/
SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *p){
int rc;
rc = sqlite3_exec(p->db, "SAVEPOINT fts3", 0, 0, 0);
if( rc==SQLITE_OK ){
rc = fts3DoOptimize(p, 1);
if( rc==SQLITE_OK || rc==SQLITE_DONE ){
int rc2 = sqlite3_exec(p->db, "RELEASE fts3", 0, 0, 0);
if( rc2!=SQLITE_OK ) rc = rc2;
}else{
sqlite3_exec(p->db, "ROLLBACK TO fts3", 0, 0, 0);
sqlite3_exec(p->db, "RELEASE fts3", 0, 0, 0);
}
}
sqlite3Fts3SegmentsClose(p);
return rc;
|
| ︙ | ︙ | |||
121761 121762 121763 121764 121765 121766 121767 |
int (*x)(Fts3Expr*,int,void*), /* Callback function to invoke for phrases */
void *pCtx /* Second argument to pass to callback */
){
int iPhrase = 0; /* Variable used as the phrase counter */
return fts3ExprIterate2(pExpr, &iPhrase, x, pCtx);
}
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > | < < < < < < < < | 122875 122876 122877 122878 122879 122880 122881 122882 122883 122884 122885 122886 122887 122888 122889 122890 122891 122892 122893 122894 122895 122896 122897 122898 122899 122900 122901 122902 |
int (*x)(Fts3Expr*,int,void*), /* Callback function to invoke for phrases */
void *pCtx /* Second argument to pass to callback */
){
int iPhrase = 0; /* Variable used as the phrase counter */
return fts3ExprIterate2(pExpr, &iPhrase, x, pCtx);
}
/*
** This is an fts3ExprIterate() callback used while loading the doclists
** for each phrase into Fts3Expr.aDoclist[]/nDoclist. See also
** fts3ExprLoadDoclists().
*/
static int fts3ExprLoadDoclistsCb(Fts3Expr *pExpr, int iPhrase, void *ctx){
int rc = SQLITE_OK;
Fts3Phrase *pPhrase = pExpr->pPhrase;
LoadDoclistCtx *p = (LoadDoclistCtx *)ctx;
UNUSED_PARAMETER(iPhrase);
p->nPhrase++;
p->nToken += pPhrase->nToken;
return rc;
}
/*
** Load the doclists for each phrase in the query associated with FTS3 cursor
** pCsr.
|
| ︙ | ︙ | |||
122000 122001 122002 122003 122004 122005 122006 |
static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){
SnippetIter *p = (SnippetIter *)ctx;
SnippetPhrase *pPhrase = &p->aPhrase[iPhrase];
char *pCsr;
pPhrase->nToken = pExpr->pPhrase->nToken;
| | | 123062 123063 123064 123065 123066 123067 123068 123069 123070 123071 123072 123073 123074 123075 123076 |
static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){
SnippetIter *p = (SnippetIter *)ctx;
SnippetPhrase *pPhrase = &p->aPhrase[iPhrase];
char *pCsr;
pPhrase->nToken = pExpr->pPhrase->nToken;
pCsr = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol);
if( pCsr ){
int iFirst = 0;
pPhrase->pList = pCsr;
fts3GetDeltaPosition(&pCsr, &iFirst);
pPhrase->pHead = pCsr;
pPhrase->pTail = pCsr;
pPhrase->iHead = iFirst;
|
| ︙ | ︙ | |||
122357 122358 122359 122360 122361 122362 122363 |
if( !c ) nEntry++;
}
*ppCollist = pEnd;
return nEntry;
}
| < < < < < < < < < < < < < < < < < < < < | 123419 123420 123421 123422 123423 123424 123425 123426 123427 123428 123429 123430 123431 123432 |
if( !c ) nEntry++;
}
*ppCollist = pEnd;
return nEntry;
}
/*
** fts3ExprIterate() callback used to collect the "global" matchinfo stats
** for a single query.
**
** fts3ExprIterate() callback to load the 'global' elements of a
** FTS3_MATCHINFO_HITS matchinfo array. The global stats are those elements
** of the matchinfo array that are constant for all rows returned by the
|
| ︙ | ︙ | |||
122410 122411 122412 122413 122414 122415 122416 |
*/
static int fts3ExprGlobalHitsCb(
Fts3Expr *pExpr, /* Phrase expression node */
int iPhrase, /* Phrase number (numbered from zero) */
void *pCtx /* Pointer to MatchInfo structure */
){
MatchInfo *p = (MatchInfo *)pCtx;
| | < < < | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < | < > > | | 123452 123453 123454 123455 123456 123457 123458 123459 123460 123461 123462 123463 123464 123465 123466 123467 123468 123469 123470 123471 123472 123473 123474 123475 123476 123477 123478 123479 123480 123481 123482 123483 123484 123485 123486 123487 123488 123489 123490 123491 |
*/
static int fts3ExprGlobalHitsCb(
Fts3Expr *pExpr, /* Phrase expression node */
int iPhrase, /* Phrase number (numbered from zero) */
void *pCtx /* Pointer to MatchInfo structure */
){
MatchInfo *p = (MatchInfo *)pCtx;
return sqlite3Fts3EvalPhraseStats(
p->pCursor, pExpr, &p->aMatchinfo[3*iPhrase*p->nCol]
);
}
/*
** fts3ExprIterate() callback used to collect the "local" part of the
** FTS3_MATCHINFO_HITS array. The local stats are those elements of the
** array that are different for each row returned by the query.
*/
static int fts3ExprLocalHitsCb(
Fts3Expr *pExpr, /* Phrase expression node */
int iPhrase, /* Phrase number */
void *pCtx /* Pointer to MatchInfo structure */
){
MatchInfo *p = (MatchInfo *)pCtx;
int iStart = iPhrase * p->nCol * 3;
int i;
for(i=0; i<p->nCol; i++){
char *pCsr;
pCsr = sqlite3Fts3EvalPhrasePoslist(p->pCursor, pExpr, i);
if( pCsr ){
p->aMatchinfo[iStart+i*3] = fts3ColumnlistCount(&pCsr);
}else{
p->aMatchinfo[iStart+i*3] = 0;
}
}
return SQLITE_OK;
}
static int fts3MatchinfoCheck(
|
| ︙ | ︙ | |||
122561 122562 122563 122564 122565 122566 122567 |
** iterating through a multi-column position-list corresponding to the
** hits for a single phrase on a single row in order to calculate the
** values for a matchinfo() FTS3_MATCHINFO_LCS request.
*/
typedef struct LcsIterator LcsIterator;
struct LcsIterator {
Fts3Expr *pExpr; /* Pointer to phrase expression */
| < | | 123563 123564 123565 123566 123567 123568 123569 123570 123571 123572 123573 123574 123575 123576 123577 123578 |
** iterating through a multi-column position-list corresponding to the
** hits for a single phrase on a single row in order to calculate the
** values for a matchinfo() FTS3_MATCHINFO_LCS request.
*/
typedef struct LcsIterator LcsIterator;
struct LcsIterator {
Fts3Expr *pExpr; /* Pointer to phrase expression */
int iPosOffset; /* Tokens count up to end of this phrase */
char *pRead; /* Cursor used to iterate through aDoclist */
int iPos; /* Current position */
};
/*
** If LcsIterator.iCol is set to the following value, the iterator has
** finished iterating through all offsets for all columns.
*/
|
| ︙ | ︙ | |||
122594 122595 122596 122597 122598 122599 122600 |
*/
static int fts3LcsIteratorAdvance(LcsIterator *pIter){
char *pRead = pIter->pRead;
sqlite3_int64 iRead;
int rc = 0;
pRead += sqlite3Fts3GetVarint(pRead, &iRead);
| | | < < < < < < < | 123595 123596 123597 123598 123599 123600 123601 123602 123603 123604 123605 123606 123607 123608 123609 123610 123611 123612 |
*/
static int fts3LcsIteratorAdvance(LcsIterator *pIter){
char *pRead = pIter->pRead;
sqlite3_int64 iRead;
int rc = 0;
pRead += sqlite3Fts3GetVarint(pRead, &iRead);
if( iRead==0 || iRead==1 ){
pRead = 0;
rc = 1;
}else{
pIter->iPos += (int)(iRead-2);
}
pIter->pRead = pRead;
return rc;
}
|
| ︙ | ︙ | |||
122636 122637 122638 122639 122640 122641 122642 122643 122644 122645 122646 |
/* Allocate and populate the array of LcsIterator objects. The array
** contains one element for each matchable phrase in the query.
**/
aIter = sqlite3_malloc(sizeof(LcsIterator) * pCsr->nPhrase);
if( !aIter ) return SQLITE_NOMEM;
memset(aIter, 0, sizeof(LcsIterator) * pCsr->nPhrase);
(void)fts3ExprIterate(pCsr->pExpr, fts3MatchinfoLcsCb, (void*)aIter);
for(i=0; i<pInfo->nPhrase; i++){
LcsIterator *pIter = &aIter[i];
nToken -= pIter->pExpr->pPhrase->nToken;
pIter->iPosOffset = nToken;
| > < < < < < < < < < < > > > > | | | | | < < < | | 123630 123631 123632 123633 123634 123635 123636 123637 123638 123639 123640 123641 123642 123643 123644 123645 123646 123647 123648 123649 123650 123651 123652 123653 123654 123655 123656 123657 123658 123659 123660 123661 123662 123663 123664 123665 123666 123667 123668 123669 123670 123671 |
/* Allocate and populate the array of LcsIterator objects. The array
** contains one element for each matchable phrase in the query.
**/
aIter = sqlite3_malloc(sizeof(LcsIterator) * pCsr->nPhrase);
if( !aIter ) return SQLITE_NOMEM;
memset(aIter, 0, sizeof(LcsIterator) * pCsr->nPhrase);
(void)fts3ExprIterate(pCsr->pExpr, fts3MatchinfoLcsCb, (void*)aIter);
for(i=0; i<pInfo->nPhrase; i++){
LcsIterator *pIter = &aIter[i];
nToken -= pIter->pExpr->pPhrase->nToken;
pIter->iPosOffset = nToken;
}
for(iCol=0; iCol<pInfo->nCol; iCol++){
int nLcs = 0; /* LCS value for this column */
int nLive = 0; /* Number of iterators in aIter not at EOF */
for(i=0; i<pInfo->nPhrase; i++){
LcsIterator *pIt = &aIter[i];
pIt->pRead = sqlite3Fts3EvalPhrasePoslist(pCsr, pIt->pExpr, iCol);
if( pIt->pRead ){
pIt->iPos = pIt->iPosOffset;
fts3LcsIteratorAdvance(&aIter[i]);
nLive++;
}
}
while( nLive>0 ){
LcsIterator *pAdv = 0; /* The iterator to advance by one position */
int nThisLcs = 0; /* LCS for the current iterator positions */
for(i=0; i<pInfo->nPhrase; i++){
LcsIterator *pIter = &aIter[i];
if( pIter->pRead==0 ){
/* This iterator is already at EOF for this column. */
nThisLcs = 0;
}else{
if( pAdv==0 || pIter->iPos<pAdv->iPos ){
pAdv = pIter;
}
if( nThisLcs==0 || pIter->iPos==pIter[-1].iPos ){
|
| ︙ | ︙ | |||
123011 123012 123013 123014 123015 123016 123017 | TermOffsetCtx *p = (TermOffsetCtx *)ctx; int nTerm; /* Number of tokens in phrase */ int iTerm; /* For looping through nTerm phrase terms */ char *pList; /* Pointer to position list for phrase */ int iPos = 0; /* First position in position-list */ UNUSED_PARAMETER(iPhrase); | | | 123997 123998 123999 124000 124001 124002 124003 124004 124005 124006 124007 124008 124009 124010 124011 |
TermOffsetCtx *p = (TermOffsetCtx *)ctx;
int nTerm; /* Number of tokens in phrase */
int iTerm; /* For looping through nTerm phrase terms */
char *pList; /* Pointer to position list for phrase */
int iPos = 0; /* First position in position-list */
UNUSED_PARAMETER(iPhrase);
pList = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol);
nTerm = pExpr->pPhrase->nToken;
if( pList ){
fts3GetDeltaPosition(&pList, &iPos);
assert( iPos>=0 );
}
for(iTerm=0; iTerm<nTerm; iTerm++){
|
| ︙ | ︙ |
Changes to src/sqlite3.h.
| ︙ | ︙ | |||
105 106 107 108 109 110 111 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.7.7" #define SQLITE_VERSION_NUMBER 3007007 | | | 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.7.7" #define SQLITE_VERSION_NUMBER 3007007 #define SQLITE_SOURCE_ID "2011-06-15 13:11:06 f9750870ee04935f338e4d808900fee5a8b2b389" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version, sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros |
| ︙ | ︙ |
Changes to src/stat.c.
| ︙ | ︙ | |||
102 103 104 105 106 107 108 |
@ %h(db_get("project-code",""))
@ </td></tr>
@ <tr><th>Server ID:</th><td>
@ %h(db_get("server-code",""))
@ </td></tr>
@ <tr><th>Fossil Version:</th><td>
| | > | 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
@ %h(db_get("project-code",""))
@ </td></tr>
@ <tr><th>Server ID:</th><td>
@ %h(db_get("server-code",""))
@ </td></tr>
@ <tr><th>Fossil Version:</th><td>
@ %h(RELEASE_VERSION) %h(MANIFEST_DATE) %h(MANIFEST_VERSION)
@ (%h(COMPILER_NAME))
@ </td></tr>
@ <tr><th>SQLite Version:</th><td>
sqlite3_snprintf(sizeof(zBuf), zBuf, "%.19s [%.10s] (%s)",
SQLITE_SOURCE_ID, &SQLITE_SOURCE_ID[20], SQLITE_VERSION);
zDb = db_name("repository");
@ %s(zBuf)
@ </td></tr>
|
| ︙ | ︙ |
Changes to src/style.c.
| ︙ | ︙ | |||
96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
/* Generate the header up through the main menu */
Th_Store("project_name", db_get("project-name","Unnamed Fossil Project"));
Th_Store("title", zTitle);
Th_Store("baseurl", g.zBaseURL);
Th_Store("home", g.zTop);
Th_Store("index_page", db_get("index-page","/home"));
Th_Store("current_page", g.zPath);
Th_Store("manifest_version", MANIFEST_VERSION);
Th_Store("manifest_date", MANIFEST_DATE);
Th_Store("compiler_name", COMPILER_NAME);
if( g.zLogin ){
Th_Store("login", g.zLogin);
}
if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br />\n", -1);
| > | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
/* Generate the header up through the main menu */
Th_Store("project_name", db_get("project-name","Unnamed Fossil Project"));
Th_Store("title", zTitle);
Th_Store("baseurl", g.zBaseURL);
Th_Store("home", g.zTop);
Th_Store("index_page", db_get("index-page","/home"));
Th_Store("current_page", g.zPath);
Th_Store("release_version", RELEASE_VERSION);
Th_Store("manifest_version", MANIFEST_VERSION);
Th_Store("manifest_date", MANIFEST_DATE);
Th_Store("compiler_name", COMPILER_NAME);
if( g.zLogin ){
Th_Store("login", g.zLogin);
}
if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br />\n", -1);
|
| ︙ | ︙ | |||
243 244 245 246 247 248 249 | ; /* ** The default page footer */ const char zDefaultFooter[] = @ <div class="footer"> | | | 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 | ; /* ** The default page footer */ const char zDefaultFooter[] = @ <div class="footer"> @ Fossil version $release_version $manifest_version $manifest_date @ </div> @ </body></html> ; /* ** The default Cascading Style Sheet. ** It's assembled by different strings for each class. |
| ︙ | ︙ |
Changes to src/timeline.c.
| ︙ | ︙ | |||
331 332 333 334 335 336 337 |
"SELECT (pid==0) AS isnew,"
" (fid==0) AS isdel,"
" (SELECT name FROM filename WHERE fnid=mlink.fnid) AS name,"
" (SELECT uuid FROM blob WHERE rid=fid),"
" (SELECT uuid FROM blob WHERE rid=pid)"
" FROM mlink"
" WHERE mid=:mid AND pid!=fid"
| | | 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 |
"SELECT (pid==0) AS isnew,"
" (fid==0) AS isdel,"
" (SELECT name FROM filename WHERE fnid=mlink.fnid) AS name,"
" (SELECT uuid FROM blob WHERE rid=fid),"
" (SELECT uuid FROM blob WHERE rid=pid)"
" FROM mlink"
" WHERE mid=:mid AND pid!=fid"
" ORDER BY 3 /*sort*/"
);
fchngQueryInit = 1;
}
db_bind_int(&fchngQuery, ":mid", rid);
while( db_step(&fchngQuery)==SQLITE_ROW ){
const char *zFilename = db_column_text(&fchngQuery, 2);
int isNew = db_column_int(&fchngQuery, 0);
|
| ︙ | ︙ |
Changes to src/tkt.c.
| ︙ | ︙ | |||
887 888 889 890 891 892 893 894 895 896 897 898 899 900 | ** change ticket identified by TICKETUUID and set the value of ** field FIELD to VALUE. Valid field descriptions are: ** status, type, severity, priority, resolution, ** foundin, private_contact, resolution, title or comment ** Field names given above are the ones, defined in a standard ** fossil environment. If you have added, deleted columns, you ** change the all your configured columns. ** You can use more than one field/value pair on the commandline. ** Using -q|--quote enables the special character decoding as ** in "ticket show". So it's possible, to set multiline text or ** text with special characters. ** ** %fossil ticket add FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote? ** | > | 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 | ** change ticket identified by TICKETUUID and set the value of ** field FIELD to VALUE. Valid field descriptions are: ** status, type, severity, priority, resolution, ** foundin, private_contact, resolution, title or comment ** Field names given above are the ones, defined in a standard ** fossil environment. If you have added, deleted columns, you ** change the all your configured columns. ** If you use +FIELD, the VALUE Is appended to the field FIELD. ** You can use more than one field/value pair on the commandline. ** Using -q|--quote enables the special character decoding as ** in "ticket show". So it's possible, to set multiline text or ** text with special characters. ** ** %fossil ticket add FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote? ** |
| ︙ | ︙ | |||
1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 |
}
getAllTicketFields();
/* read commandline and assign fields in the azValue array */
while( i<g.argc ){
char *zFName;
char *zFValue;
int j;
zFName = g.argv[i++];
if( i==g.argc ){
fossil_fatal("missing value for '%s'!",zFName);
}
zFValue = g.argv[i++];
| > < > > > > > > > > | > | > > > | | > > > > > | | | | | | | | | < | | | 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 |
}
getAllTicketFields();
/* read commandline and assign fields in the azValue array */
while( i<g.argc ){
char *zFName;
char *zFValue;
int j;
int append = 0;
zFName = g.argv[i++];
if( i==g.argc ){
fossil_fatal("missing value for '%s'!",zFName);
}
zFValue = g.argv[i++];
if( tktEncoding == tktFossilize ){
zFValue=mprintf("%s",zFValue);
defossilize(zFValue);
}
append = (zFName[0] == '+');
if (append){
zFName++;
}
j = fieldId(zFName);
if( j == -1 ){
fossil_fatal("unknown field name '%s'!",zFName);
}else{
if (append) {
azAppend[j] = zFValue;
} else {
azValue[j] = zFValue;
}
}
}
/* now add the needed artifacts to the repository */
blob_zero(&tktchng);
{ /* add the time to the ticket manifest */
char *zDate;
zDate = date_in_standard_format("now");
blob_appendf(&tktchng, "D %s\n", zDate);
free(zDate);
}
/* append defined elements */
for(i=0; i<nField; i++){
char *zValue = 0;
char *zPfx;
if (azAppend[i] && azAppend[i][0] ){
zPfx = " +";
zValue = azAppend[i];
} else if( azValue[i] && azValue[i][0] ){
zPfx = " ";
zValue = azValue[i];
} else {
continue;
}
if( strncmp(azField[i], "private_", 8)==0 ){
zValue = db_conceal(zValue, strlen(zValue));
blob_appendf(&tktchng, "J%s%s %s\n", zPfx, azField[i], zValue);
}else{
blob_appendf(&tktchng, "J%s%s %#F\n", zPfx,
azField[i], strlen(zValue), zValue);
}
if( tktEncoding == tktFossilize ){
free(azValue[i]);
}
}
blob_appendf(&tktchng, "K %s\n", zTktUuid);
blob_appendf(&tktchng, "U %F\n", g.zLogin);
md5sum_blob(&tktchng, &cksum);
blob_appendf(&tktchng, "Z %b\n", &cksum);
rid = content_put(&tktchng);
if( rid==0 ){
fossil_panic("trouble committing ticket: %s", g.zErrMsg);
}
manifest_crosslink_begin();
manifest_crosslink(rid, &tktchng);
manifest_crosslink_end();
assert( blob_is_reset(&tktchng) );
printf("ticket %s succeeded for UID %s\n",
(eCmd==set?"set":"add"),zTktUuid);
}
}
}
}
|
Changes to src/update.c.
| ︙ | ︙ | |||
325 326 327 328 329 330 331 |
const char *zName = db_column_text(&q, 0); /* The filename from root */
int idv = db_column_int(&q, 1); /* VFILE entry for current */
int ridv = db_column_int(&q, 2); /* RecordID for current */
int idt = db_column_int(&q, 3); /* VFILE entry for target */
int ridt = db_column_int(&q, 4); /* RecordID for target */
int chnged = db_column_int(&q, 5); /* Current is edited */
const char *zNewName = db_column_text(&q,6);/* New filename */
| | | 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 |
const char *zName = db_column_text(&q, 0); /* The filename from root */
int idv = db_column_int(&q, 1); /* VFILE entry for current */
int ridv = db_column_int(&q, 2); /* RecordID for current */
int idt = db_column_int(&q, 3); /* VFILE entry for target */
int ridt = db_column_int(&q, 4); /* RecordID for target */
int chnged = db_column_int(&q, 5); /* Current is edited */
const char *zNewName = db_column_text(&q,6);/* New filename */
int isexe = db_column_int(&q, 7); /* EXE perm for new file */
char *zFullPath; /* Full pathname of the file */
char *zFullNewPath; /* Full pathname of dest */
char nameChng; /* True if the name changed */
zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName);
nameChng = fossil_strcmp(zName, zNewName);
|
| ︙ | ︙ | |||
430 431 432 433 434 435 436 |
/* Report on conflicts
*/
if( nConflict && !nochangeFlag ){
if( internalUpdate ){
internalConflictCnt = nConflict;
}else{
| > | | | | 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 |
/* Report on conflicts
*/
if( nConflict && !nochangeFlag ){
if( internalUpdate ){
internalConflictCnt = nConflict;
}else{
fossil_print(
"WARNING: %d merge conflicts - see messages above for details.\n",
nConflict);
}
}
/*
** Clean up the mid and pid VFILE entries. Then commit the changes.
*/
if( nochangeFlag ){
db_end_transaction(1); /* With --nochange, rollback changes */
}else{
ensure_empty_dirs_created();
if( g.argc<=3 ){
/* All files updated. Shift the current checkout to the target. */
db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid);
checkout_set_all_exe(tid);
manifest_to_disk(tid);
db_lset_int("checkout", tid);
}else{
/* A subset of files have been checked out. Keep the current
** checkout unchanged. */
db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
}
|
| ︙ | ︙ |
Changes to src/vfile.c.
| ︙ | ︙ | |||
68 69 70 71 72 73 74 75 76 |
canonical16(z, sz);
rid = fast_uuid_to_rid(z);
if( rid==0 && phantomize ){
rid = content_new(zUuid, phantomize-1);
}
return rid;
}
/*
| > | | > > > > | 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
canonical16(z, sz);
rid = fast_uuid_to_rid(z);
if( rid==0 && phantomize ){
rid = content_new(zUuid, phantomize-1);
}
return rid;
}
/*
** Load a vfile from a record ID.
*/
void load_vfile_from_rid(int vid){
int rid, size;
Stmt ins, ridq;
Manifest *p;
ManifestFile *pFile;
if( db_exists("SELECT 1 FROM vfile WHERE vid=%d", vid) ){
return;
}
db_begin_transaction();
p = manifest_get(vid, CFTYPE_MANIFEST);
if( p==0 ) return;
db_multi_exec("DELETE FROM vfile WHERE vid=%d", vid);
db_prepare(&ins,
"INSERT INTO vfile(vid,isexe,rid,mrid,pathname) "
|
| ︙ | ︙ |
Changes to win/Makefile.PellesCGMake.
| ︙ | ︙ | |||
124 125 126 127 128 129 130 | $(LINK) $(LINKFLAGS) -out:"$@" $< # compiling standard fossil utils $(UTILS_OBJ): %.obj: $(SRCDIR)%.c $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" # compile special windows utils | | | 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | $(LINK) $(LINKFLAGS) -out:"$@" $< # compiling standard fossil utils $(UTILS_OBJ): %.obj: $(SRCDIR)%.c $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" # compile special windows utils version.obj: $(SRCDIR)mkversion.c $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" # generate the translated c-source files $(TRANSLATEDSRC): %_.c: $(SRCDIR)%.c translate.exe translate.exe $< >$@ # generate the index source, containing all web references,.. |
| ︙ | ︙ |
Changes to win/Makefile.dmc.
| ︙ | ︙ | |||
56 57 58 59 60 61 62 | makeheaders$E: $(SRCDIR)\makeheaders.c $(BCC) -o$@ $** mkindex$E: $(SRCDIR)\mkindex.c $(BCC) -o$@ $** | | | 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | makeheaders$E: $(SRCDIR)\makeheaders.c $(BCC) -o$@ $** mkindex$E: $(SRCDIR)\mkindex.c $(BCC) -o$@ $** version$E: $B\src\mkversion.c $(BCC) -o$@ $** $(OBJDIR)\shell$O : $(SRCDIR)\shell.c $(TCC) -o$@ -c -Dmain=sqlite3_shell $(SQLITE_OPTIONS) $** $(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c $(TCC) -o$@ -c $(SQLITE_OPTIONS) $** |
| ︙ | ︙ |
Changes to win/Makefile.mingw.
| ︙ | ︙ | |||
349 350 351 352 353 354 355 | $(OBJDIR)/makeheaders: $(SRCDIR)/makeheaders.c $(BCC) -o $(OBJDIR)/makeheaders $(SRCDIR)/makeheaders.c $(OBJDIR)/mkindex: $(SRCDIR)/mkindex.c $(BCC) -o $(OBJDIR)/mkindex $(SRCDIR)/mkindex.c | | | | 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 | $(OBJDIR)/makeheaders: $(SRCDIR)/makeheaders.c $(BCC) -o $(OBJDIR)/makeheaders $(SRCDIR)/makeheaders.c $(OBJDIR)/mkindex: $(SRCDIR)/mkindex.c $(BCC) -o $(OBJDIR)/mkindex $(SRCDIR)/mkindex.c $(VERSION): $(SRCDIR)/mkversion.c $(BCC) -o $(OBJDIR)/version $(SRCDIR)/mkversion.c # WARNING. DANGER. Running the testsuite modifies the repository the # build is done from, i.e. the checkout belongs to. Do not sync/push # the repository after running the tests. test: $(APPNAME) $(TCLSH) test/tester.tcl $(APPNAME) |
| ︙ | ︙ |
Changes to win/Makefile.msc.
| ︙ | ︙ | |||
45 46 47 48 49 50 51 | APPNAME = $(OX)\fossil$(E) all: $(OX) $(APPNAME) $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OX)\linkopts cd $(OX) | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | APPNAME = $(OX)\fossil$(E) all: $(OX) $(APPNAME) $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OX)\linkopts cd $(OX) link /NODEFAULTLIB:msvcrt -OUT:$@ $(LIBDIR) @linkopts $(OX)\linkopts: $B\win\Makefile.msc echo $(OX)\add.obj > $@ echo $(OX)\allrepo.obj >> $@ echo $(OX)\attach.obj >> $@ echo $(OX)\bag.obj >> $@ echo $(OX)\bisect.obj >> $@ echo $(OX)\blob.obj >> $@ echo $(OX)\branch.obj >> $@ echo $(OX)\browse.obj >> $@ echo $(OX)\captcha.obj >> $@ echo $(OX)\cgi.obj >> $@ echo $(OX)\checkin.obj >> $@ echo $(OX)\checkout.obj >> $@ echo $(OX)\clearsign.obj >> $@ echo $(OX)\clone.obj >> $@ echo $(OX)\comformat.obj >> $@ echo $(OX)\configure.obj >> $@ echo $(OX)\content.obj >> $@ echo $(OX)\db.obj >> $@ echo $(OX)\delta.obj >> $@ echo $(OX)\deltacmd.obj >> $@ echo $(OX)\descendants.obj >> $@ echo $(OX)\diff.obj >> $@ echo $(OX)\diffcmd.obj >> $@ echo $(OX)\doc.obj >> $@ echo $(OX)\encode.obj >> $@ echo $(OX)\event.obj >> $@ echo $(OX)\export.obj >> $@ echo $(OX)\file.obj >> $@ echo $(OX)\finfo.obj >> $@ echo $(OX)\glob.obj >> $@ echo $(OX)\graph.obj >> $@ echo $(OX)\gzip.obj >> $@ echo $(OX)\http.obj >> $@ echo $(OX)\http_socket.obj >> $@ echo $(OX)\http_ssl.obj >> $@ echo $(OX)\http_transport.obj >> $@ echo $(OX)\import.obj >> $@ echo $(OX)\info.obj >> $@ echo $(OX)\leaf.obj >> $@ echo $(OX)\login.obj >> $@ echo $(OX)\main.obj >> $@ echo $(OX)\manifest.obj >> $@ echo $(OX)\md5.obj >> $@ echo $(OX)\merge.obj >> $@ echo $(OX)\merge3.obj >> $@ echo $(OX)\name.obj >> $@ echo $(OX)\path.obj >> $@ echo $(OX)\pivot.obj >> $@ echo $(OX)\popen.obj >> $@ echo $(OX)\pqueue.obj >> $@ echo $(OX)\printf.obj >> $@ echo $(OX)\rebuild.obj >> $@ echo $(OX)\report.obj >> $@ echo $(OX)\rss.obj >> $@ echo $(OX)\schema.obj >> $@ echo $(OX)\search.obj >> $@ echo $(OX)\setup.obj >> $@ echo $(OX)\sha1.obj >> $@ echo $(OX)\shell.obj >> $@ echo $(OX)\shun.obj >> $@ echo $(OX)\skins.obj >> $@ echo $(OX)\sqlcmd.obj >> $@ echo $(OX)\sqlite3.obj >> $@ echo $(OX)\stash.obj >> $@ echo $(OX)\stat.obj >> $@ echo $(OX)\style.obj >> $@ echo $(OX)\sync.obj >> $@ echo $(OX)\tag.obj >> $@ echo $(OX)\tar.obj >> $@ echo $(OX)\th.obj >> $@ echo $(OX)\th_lang.obj >> $@ echo $(OX)\th_main.obj >> $@ echo $(OX)\timeline.obj >> $@ echo $(OX)\tkt.obj >> $@ echo $(OX)\tktsetup.obj >> $@ echo $(OX)\undo.obj >> $@ echo $(OX)\update.obj >> $@ echo $(OX)\url.obj >> $@ echo $(OX)\user.obj >> $@ echo $(OX)\verify.obj >> $@ echo $(OX)\vfile.obj >> $@ echo $(OX)\wiki.obj >> $@ echo $(OX)\wikiformat.obj >> $@ echo $(OX)\winhttp.obj >> $@ echo $(OX)\xfer.obj >> $@ echo $(OX)\zip.obj >> $@ echo $(LIBS) >> $@ $(OX): @-mkdir $@ translate$E: $(SRCDIR)\translate.c $(BCC) $** makeheaders$E: $(SRCDIR)\makeheaders.c $(BCC) $** mkindex$E: $(SRCDIR)\mkindex.c $(BCC) $** version$E: $B\src\mkversion.c $(BCC) $** $(OX)\shell$O : $(SRCDIR)\shell.c $(TCC) /Fo$@ /Dmain=sqlite3_shell $(SQLITE_OPTIONS) -c $(SRCDIR)\shell.c $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $** $(OX)\th$O : $(SRCDIR)\th.c $(TCC) /Fo$@ -c $** |
| ︙ | ︙ |