/ Check-in [f12b8a0f]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Merge trunk enhancements into the apple-osx branch.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | apple-osx
Files: files | file ages | folders
SHA1: f12b8a0f7958260cb8e6b9be1aaa583cf383b39c
User & Date: drh 2015-09-15 17:20:41
Context
2015-09-24
14:43
Merge recent trunk enhancements into the apple-osx branch. check-in: 4dd06d8b user: drh tags: apple-osx
2015-09-15
17:20
Merge trunk enhancements into the apple-osx branch. check-in: f12b8a0f user: drh tags: apple-osx
14:39
Add test cases to cover fts5 integrity-check code. check-in: 1d018c35 user: dan tags: trunk
2015-09-03
14:31
Merge enhancements from trunk. check-in: d01658ad user: drh tags: apple-osx
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/fts5/extract_api_docs.tcl.

   104    104     set res "<dl>\n"
   105    105     foreach line [split [string trim $docs] "\n"] {
   106    106       regexp {[*][*](.*)} $line -> line
   107    107       if {[regexp {^ ?x.*:} $line]} {
   108    108         append res "<dt><b>$line</b></dt><dd><p style=margin-top:0>\n"
   109    109         continue
   110    110       }
          111  +    if {[regexp {SYNONYM SUPPORT} $line]} {
          112  +      set line "</dl><h3>Synonym Support</h3>"
          113  +    }
   111    114       if {[string trim $line] == ""} {
   112    115         append res "<p>\n"
   113    116       } else {
   114    117         append res "$line\n"
   115    118       }
   116    119     }
   117         -  append res "</dl>\n"
   118    120   
   119    121     set res
   120    122   }
   121    123   
   122    124   proc get_api_docs {data} {
   123    125     # Initialize global array M as a map from Fts5StructureApi member name
   124    126     # to member definition. i.e.
................................................................................
   204    206     switch $::extract_api_docs_mode {
   205    207       fts5_api {
   206    208         output [get_fts5_struct $data "typedef struct fts5_api" "^\};"]
   207    209       }
   208    210   
   209    211       fts5_tokenizer {
   210    212         output [get_fts5_struct $data "typedef struct Fts5Tokenizer" "^\};"]
          213  +      output [get_fts5_struct $data \
          214  +        "Flags that may be passed as the third argument to xTokenize()" \
          215  +        "#define FTS5_TOKEN_COLOCATED"
          216  +      ]
   211    217       }
   212    218   
   213    219       fts5_extension {
   214    220         output [get_fts5_struct $data "typedef.*Fts5ExtensionApi" "^.;"]
   215    221       }
   216    222   
   217    223       Fts5ExtensionApi {

Changes to ext/fts5/fts5.h.

   213    213     int (*xColumnCount)(Fts5Context*);
   214    214     int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow);
   215    215     int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken);
   216    216   
   217    217     int (*xTokenize)(Fts5Context*, 
   218    218       const char *pText, int nText, /* Text to tokenize */
   219    219       void *pCtx,                   /* Context passed to xToken() */
   220         -    int (*xToken)(void*, const char*, int, int, int)       /* Callback */
          220  +    int (*xToken)(void*, int, const char*, int, int, int)       /* Callback */
   221    221     );
   222    222   
   223    223     int (*xPhraseCount)(Fts5Context*);
   224    224     int (*xPhraseSize)(Fts5Context*, int iPhrase);
   225    225   
   226    226     int (*xInstCount)(Fts5Context*, int *pnInst);
   227    227     int (*xInst)(Fts5Context*, int iIdx, int *piPhrase, int *piCol, int *piOff);
................................................................................
   274    274   ** xDelete:
   275    275   **   This function is invoked to delete a tokenizer handle previously
   276    276   **   allocated using xCreate(). Fts5 guarantees that this function will
   277    277   **   be invoked exactly once for each successful call to xCreate().
   278    278   **
   279    279   ** xTokenize:
   280    280   **   This function is expected to tokenize the nText byte string indicated 
   281         -**   by argument pText. pText may not be nul-terminated. The first argument
   282         -**   passed to this function is a pointer to an Fts5Tokenizer object returned 
   283         -**   by an earlier call to xCreate().
          281  +**   by argument pText. pText may or may not be nul-terminated. The first
          282  +**   argument passed to this function is a pointer to an Fts5Tokenizer object
          283  +**   returned by an earlier call to xCreate().
          284  +**
          285  +**   The second argument indicates the reason that FTS5 is requesting
          286  +**   tokenization of the supplied text. This is always one of the following
          287  +**   four values:
          288  +**
          289  +**   <ul><li> <b>FTS5_TOKENIZE_DOCUMENT</b> - A document is being inserted into
          290  +**            or removed from the FTS table. The tokenizer is being invoked to
          291  +**            determine the set of tokens to add to (or delete from) the
          292  +**            FTS index.
          293  +**
          294  +**       <li> <b>FTS5_TOKENIZE_QUERY</b> - A MATCH query is being executed 
          295  +**            against the FTS index. The tokenizer is being called to tokenize 
          296  +**            a bareword or quoted string specified as part of the query.
          297  +**
          298  +**       <li> <b>(FTS5_TOKENIZE_QUERY | FTS5_TOKENIZE_PREFIX)</b> - Same as
          299  +**            FTS5_TOKENIZE_QUERY, except that the bareword or quoted string is
          300  +**            followed by a "*" character, indicating that the last token
          301  +**            returned by the tokenizer will be treated as a token prefix.
          302  +**
          303  +**       <li> <b>FTS5_TOKENIZE_AUX</b> - The tokenizer is being invoked to 
          304  +**            satisfy an fts5_api.xTokenize() request made by an auxiliary
          305  +**            function. Or an fts5_api.xColumnSize() request made by the same
          306  +**            on a columnsize=0 database.  
          307  +**   </ul>
   284    308   **
   285    309   **   For each token in the input string, the supplied callback xToken() must
   286    310   **   be invoked. The first argument to it should be a copy of the pointer
   287         -**   passed as the second argument to xTokenize(). The next two arguments
   288         -**   are a pointer to a buffer containing the token text, and the size of
   289         -**   the token in bytes. The 4th and 5th arguments are the byte offsets of
   290         -**   the first byte of and first byte immediately following the text from 
          311  +**   passed as the second argument to xTokenize(). The third and fourth
          312  +**   arguments are a pointer to a buffer containing the token text, and the
          313  +**   size of the token in bytes. The 4th and 5th arguments are the byte offsets
          314  +**   of the first byte of and first byte immediately following the text from
   291    315   **   which the token is derived within the input.
          316  +**
          317  +**   The second argument passed to the xToken() callback ("tflags") should
          318  +**   normally be set to 0. The exception is if the tokenizer supports 
          319  +**   synonyms. In this case see the discussion below for details.
   292    320   **
   293    321   **   FTS5 assumes the xToken() callback is invoked for each token in the 
   294    322   **   order that they occur within the input text.
   295    323   **
   296    324   **   If an xToken() callback returns any value other than SQLITE_OK, then
   297    325   **   the tokenization should be abandoned and the xTokenize() method should
   298    326   **   immediately return a copy of the xToken() return value. Or, if the
   299    327   **   input buffer is exhausted, xTokenize() should return SQLITE_OK. Finally,
   300    328   **   if an error occurs with the xTokenize() implementation itself, it
   301    329   **   may abandon the tokenization and return any error code other than
   302    330   **   SQLITE_OK or SQLITE_DONE.
   303    331   **
          332  +** SYNONYM SUPPORT
          333  +**
          334  +**   Custom tokenizers may also support synonyms. Consider a case in which a
          335  +**   user wishes to query for a phrase such as "first place". Using the 
          336  +**   built-in tokenizers, the FTS5 query 'first + place' will match instances
          337  +**   of "first place" within the document set, but not alternative forms
          338  +**   such as "1st place". In some applications, it would be better to match
          339  +**   all instances of "first place" or "1st place" regardless of which form
          340  +**   the user specified in the MATCH query text.
          341  +**
          342  +**   There are several ways to approach this in FTS5:
          343  +**
          344  +**   <ol><li> By mapping all synonyms to a single token. In this case, the 
          345  +**            In the above example, this means that the tokenizer returns the
          346  +**            same token for inputs "first" and "1st". Say that token is in
          347  +**            fact "first", so that when the user inserts the document "I won
          348  +**            1st place" entries are added to the index for tokens "i", "won",
          349  +**            "first" and "place". If the user then queries for '1st + place',
          350  +**            the tokenizer substitutes "first" for "1st" and the query works
          351  +**            as expected.
          352  +**
          353  +**       <li> By adding multiple synonyms for a single term to the FTS index.
          354  +**            In this case, when tokenizing query text, the tokenizer may 
          355  +**            provide multiple synonyms for a single term within the document.
          356  +**            FTS5 then queries the index for each synonym individually. For
          357  +**            example, faced with the query:
          358  +**
          359  +**   <codeblock>
          360  +**     ... MATCH 'first place'</codeblock>
          361  +**
          362  +**            the tokenizer offers both "1st" and "first" as synonyms for the
          363  +**            first token in the MATCH query and FTS5 effectively runs a query 
          364  +**            similar to:
          365  +**
          366  +**   <codeblock>
          367  +**     ... MATCH '(first OR 1st) place'</codeblock>
          368  +**
          369  +**            except that, for the purposes of auxiliary functions, the query
          370  +**            still appears to contain just two phrases - "(first OR 1st)" 
          371  +**            being treated as a single phrase.
          372  +**
          373  +**       <li> By adding multiple synonyms for a single term to the FTS index.
          374  +**            Using this method, when tokenizing document text, the tokenizer
          375  +**            provides multiple synonyms for each token. So that when a 
          376  +**            document such as "I won first place" is tokenized, entries are
          377  +**            added to the FTS index for "i", "won", "first", "1st" and
          378  +**            "place".
          379  +**
          380  +**            This way, even if the tokenizer does not provide synonyms
          381  +**            when tokenizing query text (it should not - to do would be
          382  +**            inefficient), it doesn't matter if the user queries for 
          383  +**            'first + place' or '1st + place', as there are entires in the
          384  +**            FTS index corresponding to both forms of the first token.
          385  +**   </ol>
          386  +**
          387  +**   Whether is is parsing document or query text, any call to xToken that
          388  +**   specifies a <i>tflags</i> argument with the FTS5_TOKEN_COLOCATED bit
          389  +**   is considered to supply a synonym for the previous token. For example,
          390  +**   when parsing the document "I won first place", a tokenizer that supports
          391  +**   synonyms would call xToken() 5 times, as follows:
          392  +**
          393  +**   <codeblock>
          394  +**       xToken(pCtx, 0, "i",                      1,  0,  1);
          395  +**       xToken(pCtx, 0, "won",                    3,  2,  5);
          396  +**       xToken(pCtx, 0, "first",                  5,  6, 11);
          397  +**       xToken(pCtx, FTS5_TOKEN_COLOCATED, "1st", 3,  6, 11);
          398  +**       xToken(pCtx, 0, "place",                  5, 12, 17);
          399  +**</codeblock>
          400  +**
          401  +**   It is an error to specify the FTS5_TOKEN_COLOCATED flag the first time
          402  +**   xToken() is called. Multiple synonyms may be specified for a single token
          403  +**   by making multiple calls to xToken(FTS5_TOKEN_COLOCATED) in sequence. 
          404  +**   There is no limit to the number of synonyms that may be provided for a
          405  +**   single token.
          406  +**
          407  +**   In many cases, method (1) above is the best approach. It does not add 
          408  +**   extra data to the FTS index or require FTS5 to query for multiple terms,
          409  +**   so it is efficient in terms of disk space and query speed. However, it
          410  +**   does not support prefix queries very well. If, as suggested above, the
          411  +**   token "first" is subsituted for "1st" by the tokenizer, then the query:
          412  +**
          413  +**   <codeblock>
          414  +**     ... MATCH '1s*'</codeblock>
          415  +**
          416  +**   will not match documents that contain the token "1st" (as the tokenizer
          417  +**   will probably not map "1s" to any prefix of "first").
          418  +**
          419  +**   For full prefix support, method (3) may be preferred. In this case, 
          420  +**   because the index contains entries for both "first" and "1st", prefix
          421  +**   queries such as 'fi*' or '1s*' will match correctly. However, because
          422  +**   extra entries are added to the FTS index, this method uses more space
          423  +**   within the database.
          424  +**
          425  +**   Method (2) offers a midpoint between (1) and (3). Using this method,
          426  +**   a query such as '1s*' will match documents that contain the literal 
          427  +**   token "1st", but not "first" (assuming the tokenizer is not able to
          428  +**   provide synonyms for prefixes). However, a non-prefix query like '1st'
          429  +**   will match against "1st" and "first". This method does not require
          430  +**   extra disk space, as no extra entries are added to the FTS index. 
          431  +**   On the other hand, it may require more CPU cycles to run MATCH queries,
          432  +**   as separate queries of the FTS index are required for each synonym.
          433  +**
          434  +**   When using methods (2) or (3), it is important that the tokenizer only
          435  +**   provide synonyms when tokenizing document text (method (2)) or query
          436  +**   text (method (3)), not both. Doing so will not cause any errors, but is
          437  +**   inefficient.
   304    438   */
   305    439   typedef struct Fts5Tokenizer Fts5Tokenizer;
   306    440   typedef struct fts5_tokenizer fts5_tokenizer;
   307    441   struct fts5_tokenizer {
   308    442     int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut);
   309    443     void (*xDelete)(Fts5Tokenizer*);
   310    444     int (*xTokenize)(Fts5Tokenizer*, 
   311    445         void *pCtx,
          446  +      int flags,            /* Mask of FTS5_TOKENIZE_* flags */
   312    447         const char *pText, int nText, 
   313    448         int (*xToken)(
   314    449           void *pCtx,         /* Copy of 2nd argument to xTokenize() */
          450  +        int tflags,         /* Mask of FTS5_TOKEN_* flags */
   315    451           const char *pToken, /* Pointer to buffer containing token */
   316    452           int nToken,         /* Size of token in bytes */
   317    453           int iStart,         /* Byte offset of token within input text */
   318    454           int iEnd            /* Byte offset of end of token within input text */
   319    455         )
   320    456     );
   321    457   };
   322    458   
          459  +/* Flags that may be passed as the third argument to xTokenize() */
          460  +#define FTS5_TOKENIZE_QUERY     0x0001
          461  +#define FTS5_TOKENIZE_PREFIX    0x0002
          462  +#define FTS5_TOKENIZE_DOCUMENT  0x0004
          463  +#define FTS5_TOKENIZE_AUX       0x0008
          464  +
          465  +/* Flags that may be passed by the tokenizer implementation back to FTS5
          466  +** as the third argument to the supplied xToken callback. */
          467  +#define FTS5_TOKEN_COLOCATED    0x0001      /* Same position as prev. token */
          468  +
   323    469   /*
   324    470   ** END OF CUSTOM TOKENIZERS
   325    471   *************************************************************************/
   326    472   
   327    473   /*************************************************************************
   328    474   ** FTS5 EXTENSION REGISTRATION API
   329    475   */
   330    476   typedef struct fts5_api fts5_api;
   331    477   struct fts5_api {
   332         -  int iVersion;                   /* Currently always set to 1 */
          478  +  int iVersion;                   /* Currently always set to 2 */
   333    479   
   334    480     /* Create a new tokenizer */
   335    481     int (*xCreateTokenizer)(
   336    482       fts5_api *pApi,
   337    483       const char *zName,
   338    484       void *pContext,
   339    485       fts5_tokenizer *pTokenizer,

Changes to ext/fts5/fts5Int.h.

   113    113   **   This exists in order to allow the fts5_index.c module to return a 
   114    114   **   decent error message if it encounters a file-format version it does
   115    115   **   not understand.
   116    116   **
   117    117   ** bColumnsize:
   118    118   **   True if the %_docsize table is created.
   119    119   **
          120  +** bPrefixIndex:
          121  +**   This is only used for debugging. If set to false, any prefix indexes
          122  +**   are ignored. This value is configured using:
          123  +**
          124  +**       INSERT INTO tbl(tbl, rank) VALUES('prefix-index', $bPrefixIndex);
          125  +**
   120    126   */
   121    127   struct Fts5Config {
   122    128     sqlite3 *db;                    /* Database handle */
   123    129     char *zDb;                      /* Database holding FTS index (e.g. "main") */
   124    130     char *zName;                    /* Name of FTS index */
   125    131     int nCol;                       /* Number of columns */
   126    132     char **azCol;                   /* Column names */
................................................................................
   141    147     int nAutomerge;                 /* 'automerge' setting */
   142    148     int nCrisisMerge;               /* Maximum allowed segments per level */
   143    149     char *zRank;                    /* Name of rank function */
   144    150     char *zRankArgs;                /* Arguments to rank function */
   145    151   
   146    152     /* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */
   147    153     char **pzErrmsg;
          154  +
          155  +#ifdef SQLITE_DEBUG
          156  +  int bPrefixIndex;               /* True to use prefix-indexes */
          157  +#endif
   148    158   };
   149    159   
   150    160   /* Current expected value of %_config table 'version' field */
   151         -#define FTS5_CURRENT_VERSION 3
          161  +#define FTS5_CURRENT_VERSION 4
   152    162   
   153    163   #define FTS5_CONTENT_NORMAL   0
   154    164   #define FTS5_CONTENT_NONE     1
   155    165   #define FTS5_CONTENT_EXTERNAL 2
   156    166   
   157    167   
   158    168   
................................................................................
   162    172   );
   163    173   void sqlite3Fts5ConfigFree(Fts5Config*);
   164    174   
   165    175   int sqlite3Fts5ConfigDeclareVtab(Fts5Config *pConfig);
   166    176   
   167    177   int sqlite3Fts5Tokenize(
   168    178     Fts5Config *pConfig,            /* FTS5 Configuration object */
          179  +  int flags,                      /* FTS5_TOKENIZE_* flags */
   169    180     const char *pText, int nText,   /* Text to tokenize */
   170    181     void *pCtx,                     /* Context passed to xToken() */
   171         -  int (*xToken)(void*, const char*, int, int, int)    /* Callback */
          182  +  int (*xToken)(void*, int, const char*, int, int, int)    /* Callback */
   172    183   );
   173    184   
   174    185   void sqlite3Fts5Dequote(char *z);
   175    186   
   176    187   /* Load the contents of the %_config table */
   177    188   int sqlite3Fts5ConfigLoad(Fts5Config*, int);
   178    189   
................................................................................
   230    241   struct Fts5PoslistReader {
   231    242     /* Variables used only by sqlite3Fts5PoslistIterXXX() functions. */
   232    243     int iCol;                       /* If (iCol>=0), this column only */
   233    244     const u8 *a;                    /* Position list to iterate through */
   234    245     int n;                          /* Size of buffer at a[] in bytes */
   235    246     int i;                          /* Current offset in a[] */
   236    247   
          248  +  u8 bFlag;                       /* For client use (any custom purpose) */
          249  +
   237    250     /* Output variables */
   238         -  int bEof;                       /* Set to true at EOF */
          251  +  u8 bEof;                        /* Set to true at EOF */
   239    252     i64 iPos;                       /* (iCol<<32) + iPos */
   240    253   };
   241    254   int sqlite3Fts5PoslistReaderInit(
   242    255     int iCol,                       /* If (iCol>=0), this column only */
   243    256     const u8 *a, int n,             /* Poslist buffer to iterate through */
   244    257     Fts5PoslistReader *pIter        /* Iterator object to initialize */
   245    258   );
................................................................................
   371    384   ** to the database. Additionally, assume that the contents of the %_data
   372    385   ** table may have changed on disk. So any in-memory caches of %_data 
   373    386   ** records must be invalidated.
   374    387   */
   375    388   int sqlite3Fts5IndexRollback(Fts5Index *p);
   376    389   
   377    390   /*
   378         -** Retrieve and clear the current error code, respectively.
          391  +** Get or set the "averages" values.
   379    392   */
   380         -int sqlite3Fts5IndexErrcode(Fts5Index*);
   381         -void sqlite3Fts5IndexReset(Fts5Index*);
   382         -
   383         -/*
   384         -** Get or set the "averages" record.
   385         -*/
   386         -int sqlite3Fts5IndexGetAverages(Fts5Index *p, Fts5Buffer *pBuf);
          393  +int sqlite3Fts5IndexGetAverages(Fts5Index *p, i64 *pnRow, i64 *anSize);
   387    394   int sqlite3Fts5IndexSetAverages(Fts5Index *p, const u8*, int);
   388    395   
   389    396   /*
   390    397   ** Functions called by the storage module as part of integrity-check.
   391    398   */
   392    399   u64 sqlite3Fts5IndexCksum(Fts5Config*,i64,int,int,const char*,int);
   393    400   int sqlite3Fts5IndexIntegrityCheck(Fts5Index*, u64 cksum);
................................................................................
   592    599   /* Called during startup to register a UDF with SQLite */
   593    600   int sqlite3Fts5ExprInit(Fts5Global*, sqlite3*);
   594    601   
   595    602   int sqlite3Fts5ExprPhraseCount(Fts5Expr*);
   596    603   int sqlite3Fts5ExprPhraseSize(Fts5Expr*, int iPhrase);
   597    604   int sqlite3Fts5ExprPoslist(Fts5Expr*, int, const u8 **);
   598    605   
   599         -int sqlite3Fts5ExprPhraseExpr(Fts5Config*, Fts5Expr*, int, Fts5Expr**);
          606  +int sqlite3Fts5ExprClonePhrase(Fts5Config*, Fts5Expr*, int, Fts5Expr**);
   600    607   
   601    608   /*******************************************
   602    609   ** The fts5_expr.c API above this point is used by the other hand-written
   603    610   ** C code in this module. The interfaces below this point are called by
   604    611   ** the parser code in fts5parse.y.  */
   605    612   
   606    613   void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...);
................................................................................
   659    666   /**************************************************************************
   660    667   ** Interface to code in fts5_tokenizer.c. 
   661    668   */
   662    669   
   663    670   int sqlite3Fts5TokenizerInit(fts5_api*);
   664    671   /*
   665    672   ** End of interface to code in fts5_tokenizer.c.
   666         -**************************************************************************/
   667         -
   668         -/**************************************************************************
   669         -** Interface to code in fts5_sorter.c. 
   670         -*/
   671         -typedef struct Fts5Sorter Fts5Sorter;
   672         -
   673         -int sqlite3Fts5SorterNew(Fts5Expr *pExpr, Fts5Sorter **pp);
   674         -
   675         -/*
   676         -** End of interface to code in fts5_sorter.c.
   677    673   **************************************************************************/
   678    674   
   679    675   /**************************************************************************
   680    676   ** Interface to code in fts5_vocab.c. 
   681    677   */
   682    678   
   683    679   int sqlite3Fts5VocabInit(Fts5Global*, sqlite3*);

Changes to ext/fts5/fts5_aux.c.

   144    144   }
   145    145   
   146    146   /*
   147    147   ** Tokenizer callback used by implementation of highlight() function.
   148    148   */
   149    149   static int fts5HighlightCb(
   150    150     void *pContext,                 /* Pointer to HighlightContext object */
          151  +  int tflags,                     /* Mask of FTS5_TOKEN_* flags */
   151    152     const char *pToken,             /* Buffer containing token */
   152    153     int nToken,                     /* Size of token in bytes */
   153    154     int iStartOff,                  /* Start offset of token */
   154    155     int iEndOff                     /* End offset of token */
   155    156   ){
   156    157     HighlightContext *p = (HighlightContext*)pContext;
   157    158     int rc = SQLITE_OK;
   158         -  int iPos = p->iPos++;
          159  +  int iPos;
          160  +
          161  +  if( tflags & FTS5_TOKEN_COLOCATED ) return SQLITE_OK;
          162  +  iPos = p->iPos++;
   159    163   
   160    164     if( p->iRangeEnd>0 ){
   161    165       if( iPos<p->iRangeStart || iPos>p->iRangeEnd ) return SQLITE_OK;
   162    166       if( p->iRangeStart && iPos==p->iRangeStart ) p->iOff = iStartOff;
   163    167     }
   164    168   
   165    169     if( iPos==p->iter.iStart ){

Changes to ext/fts5/fts5_buffer.c.

    12     12   */
    13     13   
    14     14   
    15     15   
    16     16   #include "fts5Int.h"
    17     17   
    18     18   int sqlite3Fts5BufferGrow(int *pRc, Fts5Buffer *pBuf, int nByte){
    19         -  /* A no-op if an error has already occurred */
    20         -  if( *pRc ) return 1;
    21     19   
    22     20     if( (pBuf->n + nByte) > pBuf->nSpace ){
    23     21       u8 *pNew;
    24     22       int nNew = pBuf->nSpace ? pBuf->nSpace*2 : 64;
           23  +
           24  +    /* A no-op if an error has already occurred */
           25  +    if( *pRc ) return 1;
           26  +
    25     27       while( nNew<(pBuf->n + nByte) ){
    26     28         nNew = nNew * 2;
    27     29       }
    28     30       pNew = sqlite3_realloc(pBuf->p, nNew);
    29     31       if( pNew==0 ){
    30     32         *pRc = SQLITE_NOMEM;
    31     33         return 1;

Changes to ext/fts5/fts5_config.c.

   476    476   
   477    477     nByte = nArg * (sizeof(char*) + sizeof(u8));
   478    478     pRet->azCol = (char**)sqlite3Fts5MallocZero(&rc, nByte);
   479    479     pRet->abUnindexed = (u8*)&pRet->azCol[nArg];
   480    480     pRet->zDb = sqlite3Fts5Strndup(&rc, azArg[1], -1);
   481    481     pRet->zName = sqlite3Fts5Strndup(&rc, azArg[2], -1);
   482    482     pRet->bColumnsize = 1;
          483  +#ifdef SQLITE_DEBUG
          484  +  pRet->bPrefixIndex = 1;
          485  +#endif
   483    486     if( rc==SQLITE_OK && sqlite3_stricmp(pRet->zName, FTS5_RANK_NAME)==0 ){
   484    487       *pzErr = sqlite3_mprintf("reserved fts5 table name: %s", pRet->zName);
   485    488       rc = SQLITE_ERROR;
   486    489     }
   487    490   
   488    491     for(i=3; rc==SQLITE_OK && i<nArg; i++){
   489    492       const char *zOrig = azArg[i];
................................................................................
   641    644   ** the callback returned SQLITE_DONE, this is not an error and this function
   642    645   ** still returns SQLITE_OK. Or, if the tokenization was abandoned early
   643    646   ** because the callback returned another non-zero value, it is assumed
   644    647   ** to be an SQLite error code and returned to the caller.
   645    648   */
   646    649   int sqlite3Fts5Tokenize(
   647    650     Fts5Config *pConfig,            /* FTS5 Configuration object */
          651  +  int flags,                      /* FTS5_TOKENIZE_* flags */
   648    652     const char *pText, int nText,   /* Text to tokenize */
   649    653     void *pCtx,                     /* Context passed to xToken() */
   650         -  int (*xToken)(void*, const char*, int, int, int)    /* Callback */
          654  +  int (*xToken)(void*, int, const char*, int, int, int)    /* Callback */
   651    655   ){
   652    656     if( pText==0 ) return SQLITE_OK;
   653         -  return pConfig->pTokApi->xTokenize(pConfig->pTok, pCtx, pText, nText, xToken);
          657  +  return pConfig->pTokApi->xTokenize(
          658  +      pConfig->pTok, pCtx, flags, pText, nText, xToken
          659  +  );
   654    660   }
   655    661   
   656    662   /*
   657    663   ** Argument pIn points to the first character in what is expected to be
   658    664   ** a comma-separated list of SQL literals followed by a ')' character.
   659    665   ** If it actually is this, return a pointer to the ')'. Otherwise, return
   660    666   ** NULL to indicate a parse error.

Changes to ext/fts5/fts5_expr.c.

    18     18   #include "fts5parse.h"
    19     19   
    20     20   /*
    21     21   ** All token types in the generated fts5parse.h file are greater than 0.
    22     22   */
    23     23   #define FTS5_EOF 0
    24     24   
           25  +#define FTS5_LARGEST_INT64  (0xffffffff|(((i64)0x7fffffff)<<32))
           26  +
    25     27   typedef struct Fts5ExprTerm Fts5ExprTerm;
    26     28   
    27     29   /*
    28     30   ** Functions generated by lemon from fts5parse.y.
    29     31   */
    30     32   void *sqlite3Fts5ParserAlloc(void *(*mallocProc)(u64));
    31     33   void sqlite3Fts5ParserFree(void*, void (*freeProc)(void*));
................................................................................
    69     71   ** An instance of the following structure represents a single search term
    70     72   ** or term prefix.
    71     73   */
    72     74   struct Fts5ExprTerm {
    73     75     int bPrefix;                    /* True for a prefix term */
    74     76     char *zTerm;                    /* nul-terminated term */
    75     77     Fts5IndexIter *pIter;           /* Iterator for this term */
           78  +  Fts5ExprTerm *pSynonym;         /* Pointer to first in list of synonyms */
    76     79   };
    77     80   
    78     81   /*
    79     82   ** A phrase. One or more terms that must appear in a contiguous sequence
    80     83   ** within a document for it to match.
    81     84   */
    82     85   struct Fts5ExprPhrase {
................................................................................
   177    180         }
   178    181         pToken->n = (z2 - z);
   179    182         break;
   180    183       }
   181    184   
   182    185       default: {
   183    186         const char *z2;
          187  +      if( sqlite3Fts5IsBareword(z[0])==0 ){
          188  +        sqlite3Fts5ParseError(pParse, "fts5: syntax error near \"%.1s\"", z);
          189  +        return FTS5_EOF;
          190  +      }
   184    191         tok = FTS5_STRING;
   185    192         for(z2=&z[1]; sqlite3Fts5IsBareword(*z2); z2++);
   186    193         pToken->n = (z2 - z);
   187    194         if( pToken->n==2 && memcmp(pToken->p, "OR", 2)==0 )  tok = FTS5_OR;
   188    195         if( pToken->n==3 && memcmp(pToken->p, "NOT", 3)==0 ) tok = FTS5_NOT;
   189    196         if( pToken->n==3 && memcmp(pToken->p, "AND", 3)==0 ) tok = FTS5_AND;
   190    197         break;
................................................................................
   240    247     }
   241    248   
   242    249     sqlite3_free(sParse.apPhrase);
   243    250     *pzErr = sParse.zErr;
   244    251     return sParse.rc;
   245    252   }
   246    253   
   247         -/*
   248         -** Create a new FTS5 expression by cloning phrase iPhrase of the
   249         -** expression passed as the second argument.
   250         -*/
   251         -int sqlite3Fts5ExprPhraseExpr(
   252         -  Fts5Config *pConfig,
   253         -  Fts5Expr *pExpr, 
   254         -  int iPhrase, 
   255         -  Fts5Expr **ppNew
   256         -){
   257         -  int rc = SQLITE_OK;             /* Return code */
   258         -  Fts5ExprPhrase *pOrig;          /* The phrase extracted from pExpr */
   259         -  Fts5ExprPhrase *pCopy;          /* Copy of pOrig */
   260         -  Fts5Expr *pNew = 0;             /* Expression to return via *ppNew */
   261         -
   262         -  pOrig = pExpr->apExprPhrase[iPhrase];
   263         -  pCopy = (Fts5ExprPhrase*)sqlite3Fts5MallocZero(&rc, 
   264         -      sizeof(Fts5ExprPhrase) + sizeof(Fts5ExprTerm) * pOrig->nTerm
   265         -  );
   266         -  if( pCopy ){
   267         -    int i;                          /* Used to iterate through phrase terms */
   268         -    Fts5ExprPhrase **apPhrase;
   269         -    Fts5ExprNode *pNode;
   270         -    Fts5ExprNearset *pNear;
   271         -
   272         -    pNew = (Fts5Expr*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Expr));
   273         -    apPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc, 
   274         -        sizeof(Fts5ExprPhrase*)
   275         -    );
   276         -    pNode = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprNode));
   277         -    pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc, 
   278         -        sizeof(Fts5ExprNearset) + sizeof(Fts5ExprPhrase*)
   279         -    );
   280         -
   281         -    for(i=0; i<pOrig->nTerm; i++){
   282         -      pCopy->aTerm[i].zTerm = sqlite3Fts5Strndup(&rc, pOrig->aTerm[i].zTerm,-1);
   283         -      pCopy->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix;
   284         -    }
   285         -
   286         -    if( rc==SQLITE_OK ){
   287         -      /* All the allocations succeeded. Put the expression object together. */
   288         -      pNew->pIndex = pExpr->pIndex;
   289         -      pNew->pRoot = pNode;
   290         -      pNew->nPhrase = 1;
   291         -      pNew->apExprPhrase = apPhrase;
   292         -      pNew->apExprPhrase[0] = pCopy;
   293         -
   294         -      pNode->eType = (pOrig->nTerm==1 ? FTS5_TERM : FTS5_STRING);
   295         -      pNode->pNear = pNear;
   296         -
   297         -      pNear->nPhrase = 1;
   298         -      pNear->apPhrase[0] = pCopy;
   299         -
   300         -      pCopy->nTerm = pOrig->nTerm;
   301         -      pCopy->pNode = pNode;
   302         -    }else{
   303         -      /* At least one allocation failed. Free them all. */
   304         -      for(i=0; i<pOrig->nTerm; i++){
   305         -        sqlite3_free(pCopy->aTerm[i].zTerm);
   306         -      }
   307         -      sqlite3_free(pCopy);
   308         -      sqlite3_free(pNear);
   309         -      sqlite3_free(pNode);
   310         -      sqlite3_free(apPhrase);
   311         -      sqlite3_free(pNew);
   312         -      pNew = 0;
   313         -    }
   314         -  }
   315         -
   316         -  *ppNew = pNew;
   317         -  return rc;
   318         -}
   319         -
   320    254   /*
   321    255   ** Free the expression node object passed as the only argument.
   322    256   */
   323    257   void sqlite3Fts5ParseNodeFree(Fts5ExprNode *p){
   324    258     if( p ){
   325    259       int i;
   326    260       for(i=0; i<p->nChild; i++){
................................................................................
   345    279   static int fts5ExprColsetTest(Fts5ExprColset *pColset, int iCol){
   346    280     int i;
   347    281     for(i=0; i<pColset->nCol; i++){
   348    282       if( pColset->aiCol[i]==iCol ) return 1;
   349    283     }
   350    284     return 0;
   351    285   }
          286  +
          287  +/*
          288  +** Argument pTerm must be a synonym iterator. Return the current rowid
          289  +** that it points to.
          290  +*/
          291  +static i64 fts5ExprSynonymRowid(Fts5ExprTerm *pTerm, int bDesc, int *pbEof){
          292  +  i64 iRet = 0;
          293  +  int bRetValid = 0;
          294  +  Fts5ExprTerm *p;
          295  +
          296  +  assert( pTerm->pSynonym );
          297  +  assert( bDesc==0 || bDesc==1 );
          298  +  for(p=pTerm; p; p=p->pSynonym){
          299  +    if( 0==sqlite3Fts5IterEof(p->pIter) ){
          300  +      i64 iRowid = sqlite3Fts5IterRowid(p->pIter);
          301  +      if( bRetValid==0 || (bDesc!=(iRowid<iRet)) ){
          302  +        iRet = iRowid;
          303  +        bRetValid = 1;
          304  +      }
          305  +    }
          306  +  }
          307  +
          308  +  if( pbEof && bRetValid==0 ) *pbEof = 1;
          309  +  return iRet;
          310  +}
          311  +
          312  +/*
          313  +** Argument pTerm must be a synonym iterator.
          314  +*/
          315  +static int fts5ExprSynonymPoslist(
          316  +  Fts5ExprTerm *pTerm, 
          317  +  i64 iRowid,
          318  +  int *pbDel,                     /* OUT: Caller should sqlite3_free(*pa) */
          319  +  u8 **pa, int *pn
          320  +){
          321  +  Fts5PoslistReader aStatic[4];
          322  +  Fts5PoslistReader *aIter = aStatic;
          323  +  int nIter = 0;
          324  +  int nAlloc = 4;
          325  +  int rc = SQLITE_OK;
          326  +  Fts5ExprTerm *p;
          327  +
          328  +  assert( pTerm->pSynonym );
          329  +  for(p=pTerm; p; p=p->pSynonym){
          330  +    Fts5IndexIter *pIter = p->pIter;
          331  +    if( sqlite3Fts5IterEof(pIter)==0 && sqlite3Fts5IterRowid(pIter)==iRowid ){
          332  +      const u8 *a;
          333  +      int n;
          334  +      i64 dummy;
          335  +      rc = sqlite3Fts5IterPoslist(pIter, &a, &n, &dummy);
          336  +      if( rc!=SQLITE_OK ) goto synonym_poslist_out;
          337  +      if( nIter==nAlloc ){
          338  +        int nByte = sizeof(Fts5PoslistReader) * nAlloc * 2;
          339  +        Fts5PoslistReader *aNew = (Fts5PoslistReader*)sqlite3_malloc(nByte);
          340  +        if( aNew==0 ){
          341  +          rc = SQLITE_NOMEM;
          342  +          goto synonym_poslist_out;
          343  +        }
          344  +        memcpy(aNew, aIter, sizeof(Fts5PoslistReader) * nIter);
          345  +        nAlloc = nAlloc*2;
          346  +        if( aIter!=aStatic ) sqlite3_free(aIter);
          347  +        aIter = aNew;
          348  +      }
          349  +      sqlite3Fts5PoslistReaderInit(-1, a, n, &aIter[nIter]);
          350  +      assert( aIter[nIter].bEof==0 );
          351  +      nIter++;
          352  +    }
          353  +  }
          354  +
          355  +  assert( *pbDel==0 );
          356  +  if( nIter==1 ){
          357  +    *pa = (u8*)aIter[0].a;
          358  +    *pn = aIter[0].n;
          359  +  }else{
          360  +    Fts5PoslistWriter writer = {0};
          361  +    Fts5Buffer buf = {0,0,0};
          362  +    i64 iPrev = -1;
          363  +    while( 1 ){
          364  +      int i;
          365  +      i64 iMin = FTS5_LARGEST_INT64;
          366  +      for(i=0; i<nIter; i++){
          367  +        if( aIter[i].bEof==0 ){
          368  +          if( aIter[i].iPos==iPrev ){
          369  +            if( sqlite3Fts5PoslistReaderNext(&aIter[i]) ) continue;
          370  +          }
          371  +          if( aIter[i].iPos<iMin ){
          372  +            iMin = aIter[i].iPos;
          373  +          }
          374  +        }
          375  +      }
          376  +      if( iMin==FTS5_LARGEST_INT64 || rc!=SQLITE_OK ) break;
          377  +      rc = sqlite3Fts5PoslistWriterAppend(&buf, &writer, iMin);
          378  +      iPrev = iMin;
          379  +    }
          380  +    if( rc ){
          381  +      sqlite3_free(buf.p);
          382  +    }else{
          383  +      *pa = buf.p;
          384  +      *pn = buf.n;
          385  +      *pbDel = 1;
          386  +    }
          387  +  }
          388  +
          389  + synonym_poslist_out:
          390  +  if( aIter!=aStatic ) sqlite3_free(aIter);
          391  +  return rc;
          392  +}
          393  +
   352    394   
   353    395   /*
   354    396   ** All individual term iterators in pPhrase are guaranteed to be valid and
   355    397   ** pointing to the same rowid when this function is called. This function 
   356    398   ** checks if the current rowid really is a match, and if so populates
   357    399   ** the pPhrase->poslist buffer accordingly. Output parameter *pbMatch
   358    400   ** is set to true if this is really a match, or false otherwise.
   359    401   **
   360    402   ** SQLITE_OK is returned if an error occurs, or an SQLite error code 
   361    403   ** otherwise. It is not considered an error code if the current rowid is 
   362    404   ** not a match.
   363    405   */
   364    406   static int fts5ExprPhraseIsMatch(
   365         -  Fts5Expr *pExpr,                /* Expression pPhrase belongs to */
          407  +  Fts5ExprNode *pNode,            /* Node pPhrase belongs to */
   366    408     Fts5ExprColset *pColset,        /* Restrict matches to these columns */
   367    409     Fts5ExprPhrase *pPhrase,        /* Phrase object to initialize */
   368    410     int *pbMatch                    /* OUT: Set to true if really a match */
   369    411   ){
   370    412     Fts5PoslistWriter writer = {0};
   371    413     Fts5PoslistReader aStatic[4];
   372    414     Fts5PoslistReader *aIter = aStatic;
................................................................................
   384    426     /* If the aStatic[] array is not large enough, allocate a large array
   385    427     ** using sqlite3_malloc(). This approach could be improved upon. */
   386    428     if( pPhrase->nTerm>(sizeof(aStatic) / sizeof(aStatic[0])) ){
   387    429       int nByte = sizeof(Fts5PoslistReader) * pPhrase->nTerm;
   388    430       aIter = (Fts5PoslistReader*)sqlite3_malloc(nByte);
   389    431       if( !aIter ) return SQLITE_NOMEM;
   390    432     }
          433  +  memset(aIter, 0, sizeof(Fts5PoslistReader) * pPhrase->nTerm);
   391    434   
   392    435     /* Initialize a term iterator for each term in the phrase */
   393    436     for(i=0; i<pPhrase->nTerm; i++){
          437  +    Fts5ExprTerm *pTerm = &pPhrase->aTerm[i];
   394    438       i64 dummy;
   395         -    int n;
   396         -    const u8 *a;
   397         -    rc = sqlite3Fts5IterPoslist(pPhrase->aTerm[i].pIter, &a, &n, &dummy);
   398         -    if( rc || sqlite3Fts5PoslistReaderInit(iCol, a, n, &aIter[i]) ){
   399         -      goto ismatch_out;
          439  +    int n = 0;
          440  +    int bFlag = 0;
          441  +    const u8 *a = 0;
          442  +    if( pTerm->pSynonym ){
          443  +      rc = fts5ExprSynonymPoslist(pTerm, pNode->iRowid, &bFlag, (u8**)&a, &n);
          444  +    }else{
          445  +      rc = sqlite3Fts5IterPoslist(pTerm->pIter, &a, &n, &dummy);
   400    446       }
          447  +    if( rc!=SQLITE_OK ) goto ismatch_out;
          448  +    sqlite3Fts5PoslistReaderInit(iCol, a, n, &aIter[i]);
          449  +    aIter[i].bFlag = bFlag;
          450  +    if( aIter[i].bEof ) goto ismatch_out;
   401    451     }
   402    452   
   403    453     while( 1 ){
   404    454       int bMatch;
   405    455       i64 iPos = aIter[0].iPos;
   406    456       do {
   407    457         bMatch = 1;
................................................................................
   427    477       for(i=0; i<pPhrase->nTerm; i++){
   428    478         if( sqlite3Fts5PoslistReaderNext(&aIter[i]) ) goto ismatch_out;
   429    479       }
   430    480     }
   431    481   
   432    482    ismatch_out:
   433    483     *pbMatch = (pPhrase->poslist.n>0);
          484  +  for(i=0; i<pPhrase->nTerm; i++){
          485  +    if( aIter[i].bFlag ) sqlite3_free((u8*)aIter[i].a);
          486  +  }
   434    487     if( aIter!=aStatic ) sqlite3_free(aIter);
   435    488     return rc;
   436    489   }
   437    490   
   438    491   typedef struct Fts5LookaheadReader Fts5LookaheadReader;
   439    492   struct Fts5LookaheadReader {
   440    493     const u8 *a;                    /* Buffer containing position list */
................................................................................
   594    647   */
   595    648   static int fts5ExprNearAdvanceFirst(
   596    649     Fts5Expr *pExpr,                /* Expression pPhrase belongs to */
   597    650     Fts5ExprNode *pNode,            /* FTS5_STRING or FTS5_TERM node */
   598    651     int bFromValid,
   599    652     i64 iFrom 
   600    653   ){
   601         -  Fts5IndexIter *pIter = pNode->pNear->apPhrase[0]->aTerm[0].pIter;
   602         -  int rc;
   603         -
   604         -  assert( Fts5NodeIsString(pNode) );
   605         -  if( bFromValid ){
   606         -    rc = sqlite3Fts5IterNextFrom(pIter, iFrom);
   607         -  }else{
   608         -    rc = sqlite3Fts5IterNext(pIter);
   609         -  }
   610         -
   611         -  pNode->bEof = (rc || sqlite3Fts5IterEof(pIter));
          654  +  Fts5ExprTerm *pTerm = &pNode->pNear->apPhrase[0]->aTerm[0];
          655  +  int rc = SQLITE_OK;
          656  +
          657  +  if( pTerm->pSynonym ){
          658  +    int bEof = 1;
          659  +    Fts5ExprTerm *p;
          660  +
          661  +    /* Find the firstest rowid any synonym points to. */
          662  +    i64 iRowid = fts5ExprSynonymRowid(pTerm, pExpr->bDesc, 0);
          663  +
          664  +    /* Advance each iterator that currently points to iRowid. Or, if iFrom
          665  +    ** is valid - each iterator that points to a rowid before iFrom.  */
          666  +    for(p=pTerm; p; p=p->pSynonym){
          667  +      if( sqlite3Fts5IterEof(p->pIter)==0 ){
          668  +        i64 ii = sqlite3Fts5IterRowid(p->pIter);
          669  +        if( ii==iRowid 
          670  +         || (bFromValid && ii!=iFrom && (ii>iFrom)==pExpr->bDesc) 
          671  +        ){
          672  +          if( bFromValid ){
          673  +            rc = sqlite3Fts5IterNextFrom(p->pIter, iFrom);
          674  +          }else{
          675  +            rc = sqlite3Fts5IterNext(p->pIter);
          676  +          }
          677  +          if( rc!=SQLITE_OK ) break;
          678  +          if( sqlite3Fts5IterEof(p->pIter)==0 ){
          679  +            bEof = 0;
          680  +          }
          681  +        }else{
          682  +          bEof = 0;
          683  +        }
          684  +      }
          685  +    }
          686  +
          687  +    /* Set the EOF flag if either all synonym iterators are at EOF or an
          688  +    ** error has occurred.  */
          689  +    pNode->bEof = (rc || bEof);
          690  +  }else{
          691  +    Fts5IndexIter *pIter = pTerm->pIter;
          692  +
          693  +    assert( Fts5NodeIsString(pNode) );
          694  +    if( bFromValid ){
          695  +      rc = sqlite3Fts5IterNextFrom(pIter, iFrom);
          696  +    }else{
          697  +      rc = sqlite3Fts5IterNext(pIter);
          698  +    }
          699  +
          700  +    pNode->bEof = (rc || sqlite3Fts5IterEof(pIter));
          701  +  }
          702  +
   612    703     return rc;
   613    704   }
   614    705   
   615    706   /*
   616    707   ** Advance iterator pIter until it points to a value equal to or laster
   617    708   ** than the initial value of *piLast. If this means the iterator points
   618    709   ** to a value laster than *piLast, update *piLast to the new lastest value.
................................................................................
   642    733       iRowid = sqlite3Fts5IterRowid(pIter);
   643    734       assert( (bDesc==0 && iRowid>=iLast) || (bDesc==1 && iRowid<=iLast) );
   644    735     }
   645    736     *piLast = iRowid;
   646    737   
   647    738     return 0;
   648    739   }
          740  +
          741  +static int fts5ExprSynonymAdvanceto(
          742  +  Fts5ExprTerm *pTerm,            /* Term iterator to advance */
          743  +  int bDesc,                      /* True if iterator is "rowid DESC" */
          744  +  i64 *piLast,                    /* IN/OUT: Lastest rowid seen so far */
          745  +  int *pRc                        /* OUT: Error code */
          746  +){
          747  +  int rc = SQLITE_OK;
          748  +  i64 iLast = *piLast;
          749  +  Fts5ExprTerm *p;
          750  +  int bEof = 0;
          751  +
          752  +  for(p=pTerm; rc==SQLITE_OK && p; p=p->pSynonym){
          753  +    if( sqlite3Fts5IterEof(p->pIter)==0 ){
          754  +      i64 iRowid = sqlite3Fts5IterRowid(p->pIter);
          755  +      if( (bDesc==0 && iLast>iRowid) || (bDesc && iLast<iRowid) ){
          756  +        rc = sqlite3Fts5IterNextFrom(p->pIter, iLast);
          757  +      }
          758  +    }
          759  +  }
          760  +
          761  +  if( rc!=SQLITE_OK ){
          762  +    *pRc = rc;
          763  +    bEof = 1;
          764  +  }else{
          765  +    *piLast = fts5ExprSynonymRowid(pTerm, bDesc, &bEof);
          766  +  }
          767  +  return bEof;
          768  +}
   649    769   
   650    770   /*
   651    771   ** IN/OUT parameter (*pa) points to a position list n bytes in size. If
   652    772   ** the position list contains entries for column iCol, then (*pa) is set
   653    773   ** to point to the sub-position-list for that column and the number of
   654    774   ** bytes in it returned. Or, if the argument position list does not
   655    775   ** contain any entries for column iCol, return 0.
................................................................................
   713    833     int i;
   714    834   
   715    835     /* Check that each phrase in the nearset matches the current row.
   716    836     ** Populate the pPhrase->poslist buffers at the same time. If any
   717    837     ** phrase is not a match, break out of the loop early.  */
   718    838     for(i=0; rc==SQLITE_OK && i<pNear->nPhrase; i++){
   719    839       Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
   720         -    if( pPhrase->nTerm>1 || pNear->pColset ){
          840  +    if( pPhrase->nTerm>1 || pPhrase->aTerm[0].pSynonym || pNear->pColset ){
   721    841         int bMatch = 0;
   722         -      rc = fts5ExprPhraseIsMatch(pExpr, pNear->pColset, pPhrase, &bMatch);
          842  +      rc = fts5ExprPhraseIsMatch(pNode, pNear->pColset, pPhrase, &bMatch);
   723    843         if( bMatch==0 ) break;
   724    844       }else{
   725    845         rc = sqlite3Fts5IterPoslistBuffer(
   726    846             pPhrase->aTerm[0].pIter, &pPhrase->poslist
   727    847         );
   728    848       }
   729    849     }
................................................................................
   751    871     Fts5ExprColset *pColset = pNear->pColset;
   752    872     const u8 *pPos;
   753    873     int nPos;
   754    874     int rc;
   755    875   
   756    876     assert( pNode->eType==FTS5_TERM );
   757    877     assert( pNear->nPhrase==1 && pPhrase->nTerm==1 );
          878  +  assert( pPhrase->aTerm[0].pSynonym==0 );
   758    879   
   759    880     rc = sqlite3Fts5IterPoslist(pIter, &pPos, &nPos, &pNode->iRowid);
   760    881   
   761    882     /* If the term may match any column, then this must be a match. 
   762    883     ** Return immediately in this case. Otherwise, try to find the
   763    884     ** part of the poslist that corresponds to the required column.
   764    885     ** If it can be found, return. If it cannot, the next iteration
................................................................................
   797    918   ){
   798    919     Fts5ExprNearset *pNear = pNode->pNear;
   799    920     Fts5ExprPhrase *pLeft = pNear->apPhrase[0];
   800    921     int rc = SQLITE_OK;
   801    922     i64 iLast;                      /* Lastest rowid any iterator points to */
   802    923     int i, j;                       /* Phrase and token index, respectively */
   803    924     int bMatch;                     /* True if all terms are at the same rowid */
          925  +  const int bDesc = pExpr->bDesc;
   804    926   
   805         -  assert( pNear->nPhrase>1 || pNear->apPhrase[0]->nTerm>1 );
          927  +  /* Check that this node should not be FTS5_TERM */
          928  +  assert( pNear->nPhrase>1 
          929  +       || pNear->apPhrase[0]->nTerm>1 
          930  +       || pNear->apPhrase[0]->aTerm[0].pSynonym
          931  +  );
   806    932   
   807    933     /* Initialize iLast, the "lastest" rowid any iterator points to. If the
   808    934     ** iterator skips through rowids in the default ascending order, this means
   809    935     ** the maximum rowid. Or, if the iterator is "ORDER BY rowid DESC", then it
   810    936     ** means the minimum rowid.  */
   811         -  iLast = sqlite3Fts5IterRowid(pLeft->aTerm[0].pIter);
          937  +  if( pLeft->aTerm[0].pSynonym ){
          938  +    iLast = fts5ExprSynonymRowid(&pLeft->aTerm[0], bDesc, 0);
          939  +  }else{
          940  +    iLast = sqlite3Fts5IterRowid(pLeft->aTerm[0].pIter);
          941  +  }
   812    942   
   813    943     do {
   814    944       bMatch = 1;
   815    945       for(i=0; i<pNear->nPhrase; i++){
   816    946         Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
   817    947         for(j=0; j<pPhrase->nTerm; j++){
   818         -        Fts5IndexIter *pIter = pPhrase->aTerm[j].pIter;
   819         -        i64 iRowid = sqlite3Fts5IterRowid(pIter);
   820         -        if( iRowid!=iLast ) bMatch = 0;
   821         -        if( fts5ExprAdvanceto(pIter, pExpr->bDesc, &iLast,&rc,&pNode->bEof) ){
   822         -          return rc;
          948  +        Fts5ExprTerm *pTerm = &pPhrase->aTerm[j];
          949  +        if( pTerm->pSynonym ){
          950  +          i64 iRowid = fts5ExprSynonymRowid(pTerm, bDesc, 0);
          951  +          if( iRowid==iLast ) continue;
          952  +          bMatch = 0;
          953  +          if( fts5ExprSynonymAdvanceto(pTerm, bDesc, &iLast, &rc) ){
          954  +            pNode->bEof = 1;
          955  +            return rc;
          956  +          }
          957  +        }else{
          958  +          Fts5IndexIter *pIter = pPhrase->aTerm[j].pIter;
          959  +          i64 iRowid = sqlite3Fts5IterRowid(pIter);
          960  +          if( iRowid==iLast ) continue;
          961  +          bMatch = 0;
          962  +          if( fts5ExprAdvanceto(pIter, bDesc, &iLast, &rc, &pNode->bEof) ){
          963  +            return rc;
          964  +          }
   823    965           }
   824    966         }
   825    967       }
   826    968     }while( bMatch==0 );
   827    969   
   828         -  pNode->bNomatch = (0==fts5ExprNearTest(&rc, pExpr, pNode));
   829    970     pNode->iRowid = iLast;
          971  +  pNode->bNomatch = (0==fts5ExprNearTest(&rc, pExpr, pNode));
   830    972   
   831    973     return rc;
   832    974   }
   833    975   
   834    976   /*
   835    977   ** Initialize all term iterators in the pNear object. If any term is found
   836         -** to match no documents at all, set *pbEof to true and return immediately,
   837         -** without initializing any further iterators.
          978  +** to match no documents at all, return immediately without initializing any
          979  +** further iterators.
   838    980   */
   839    981   static int fts5ExprNearInitAll(
   840    982     Fts5Expr *pExpr,
   841    983     Fts5ExprNode *pNode
   842    984   ){
   843    985     Fts5ExprNearset *pNear = pNode->pNear;
   844         -  Fts5ExprTerm *pTerm;
   845         -  Fts5ExprPhrase *pPhrase;
   846    986     int i, j;
   847    987     int rc = SQLITE_OK;
   848    988   
   849    989     for(i=0; rc==SQLITE_OK && i<pNear->nPhrase; i++){
   850         -    pPhrase = pNear->apPhrase[i];
          990  +    Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
   851    991       for(j=0; j<pPhrase->nTerm; j++){
   852         -      pTerm = &pPhrase->aTerm[j];
   853         -      if( pTerm->pIter ){
   854         -        sqlite3Fts5IterClose(pTerm->pIter);
   855         -        pTerm->pIter = 0;
          992  +      Fts5ExprTerm *pTerm = &pPhrase->aTerm[j];
          993  +      Fts5ExprTerm *p;
          994  +      int bEof = 1;
          995  +
          996  +      for(p=pTerm; p && rc==SQLITE_OK; p=p->pSynonym){
          997  +        if( p->pIter ){
          998  +          sqlite3Fts5IterClose(p->pIter);
          999  +          p->pIter = 0;
         1000  +        }
         1001  +        rc = sqlite3Fts5IndexQuery(
         1002  +            pExpr->pIndex, p->zTerm, strlen(p->zTerm),
         1003  +            (pTerm->bPrefix ? FTS5INDEX_QUERY_PREFIX : 0) |
         1004  +            (pExpr->bDesc ? FTS5INDEX_QUERY_DESC : 0),
         1005  +            &p->pIter
         1006  +        );
         1007  +        assert( rc==SQLITE_OK || p->pIter==0 );
         1008  +        if( p->pIter && 0==sqlite3Fts5IterEof(p->pIter) ){
         1009  +          bEof = 0;
         1010  +        }
   856   1011         }
   857         -      rc = sqlite3Fts5IndexQuery(
   858         -          pExpr->pIndex, pTerm->zTerm, strlen(pTerm->zTerm),
   859         -          (pTerm->bPrefix ? FTS5INDEX_QUERY_PREFIX : 0) |
   860         -          (pExpr->bDesc ? FTS5INDEX_QUERY_DESC : 0),
   861         -          &pTerm->pIter
   862         -      );
   863         -      assert( rc==SQLITE_OK || pTerm->pIter==0 );
   864         -      if( pTerm->pIter==0 || sqlite3Fts5IterEof(pTerm->pIter) ){
         1012  +
         1013  +      if( bEof ){
   865   1014           pNode->bEof = 1;
   866         -        break;
         1015  +        return rc;
   867   1016         }
   868   1017       }
   869   1018     }
   870   1019   
   871   1020     return rc;
   872   1021   }
   873   1022   
................................................................................
  1025   1174       switch( pNode->eType ){
  1026   1175         case FTS5_STRING: {
  1027   1176           rc = fts5ExprNearAdvanceFirst(pExpr, pNode, bFromValid, iFrom);
  1028   1177           break;
  1029   1178         };
  1030   1179   
  1031   1180         case FTS5_TERM: {
  1032         -        rc = fts5ExprNearAdvanceFirst(pExpr, pNode, bFromValid, iFrom);
  1033         -        if( pNode->bEof==0 ){
         1181  +        Fts5IndexIter *pIter = pNode->pNear->apPhrase[0]->aTerm[0].pIter;
         1182  +        if( bFromValid ){
         1183  +          rc = sqlite3Fts5IterNextFrom(pIter, iFrom);
         1184  +        }else{
         1185  +          rc = sqlite3Fts5IterNext(pIter);
         1186  +        }
         1187  +        if( rc==SQLITE_OK && sqlite3Fts5IterEof(pIter)==0 ){
  1034   1188             assert( rc==SQLITE_OK );
  1035   1189             rc = fts5ExprTokenTest(pExpr, pNode);
         1190  +        }else{
         1191  +          pNode->bEof = 1;
  1036   1192           }
  1037   1193           return rc;
  1038   1194         };
  1039   1195   
  1040   1196         case FTS5_AND: {
  1041   1197           Fts5ExprNode *pLeft = pNode->apChild[0];
  1042   1198           rc = fts5ExprNodeNext(pExpr, pLeft, bFromValid, iFrom);
................................................................................
  1262   1418   /*
  1263   1419   ** Free the phrase object passed as the only argument.
  1264   1420   */
  1265   1421   static void fts5ExprPhraseFree(Fts5ExprPhrase *pPhrase){
  1266   1422     if( pPhrase ){
  1267   1423       int i;
  1268   1424       for(i=0; i<pPhrase->nTerm; i++){
         1425  +      Fts5ExprTerm *pSyn;
         1426  +      Fts5ExprTerm *pNext;
  1269   1427         Fts5ExprTerm *pTerm = &pPhrase->aTerm[i];
  1270   1428         sqlite3_free(pTerm->zTerm);
  1271         -      if( pTerm->pIter ){
  1272         -        sqlite3Fts5IterClose(pTerm->pIter);
         1429  +      sqlite3Fts5IterClose(pTerm->pIter);
         1430  +
         1431  +      for(pSyn=pTerm->pSynonym; pSyn; pSyn=pNext){
         1432  +        pNext = pSyn->pSynonym;
         1433  +        sqlite3Fts5IterClose(pSyn->pIter);
         1434  +        sqlite3_free(pSyn);
  1273   1435         }
  1274   1436       }
  1275   1437       if( pPhrase->poslist.nSpace>0 ) fts5BufferFree(&pPhrase->poslist);
  1276   1438       sqlite3_free(pPhrase);
  1277   1439     }
  1278   1440   }
  1279   1441   
................................................................................
  1327   1489     }
  1328   1490     return pRet;
  1329   1491   }
  1330   1492   
  1331   1493   typedef struct TokenCtx TokenCtx;
  1332   1494   struct TokenCtx {
  1333   1495     Fts5ExprPhrase *pPhrase;
         1496  +  int rc;
  1334   1497   };
  1335   1498   
  1336   1499   /*
  1337   1500   ** Callback for tokenizing terms used by ParseTerm().
  1338   1501   */
  1339   1502   static int fts5ParseTokenize(
  1340   1503     void *pContext,                 /* Pointer to Fts5InsertCtx object */
         1504  +  int tflags,                     /* Mask of FTS5_TOKEN_* flags */
  1341   1505     const char *pToken,             /* Buffer containing token */
  1342   1506     int nToken,                     /* Size of token in bytes */
  1343         -  int iStart,                     /* Start offset of token */
  1344         -  int iEnd                        /* End offset of token */
         1507  +  int iUnused1,                   /* Start offset of token */
         1508  +  int iUnused2                    /* End offset of token */
  1345   1509   ){
  1346   1510     int rc = SQLITE_OK;
  1347   1511     const int SZALLOC = 8;
  1348   1512     TokenCtx *pCtx = (TokenCtx*)pContext;
  1349   1513     Fts5ExprPhrase *pPhrase = pCtx->pPhrase;
  1350         -  Fts5ExprTerm *pTerm;
  1351         -
  1352         -  if( pPhrase==0 || (pPhrase->nTerm % SZALLOC)==0 ){
  1353         -    Fts5ExprPhrase *pNew;
  1354         -    int nNew = SZALLOC + (pPhrase ? pPhrase->nTerm : 0);
  1355         -
  1356         -    pNew = (Fts5ExprPhrase*)sqlite3_realloc(pPhrase, 
  1357         -        sizeof(Fts5ExprPhrase) + sizeof(Fts5ExprTerm) * nNew
  1358         -    );
  1359         -    if( pNew==0 ) return SQLITE_NOMEM;
  1360         -    if( pPhrase==0 ) memset(pNew, 0, sizeof(Fts5ExprPhrase));
  1361         -    pCtx->pPhrase = pPhrase = pNew;
  1362         -    pNew->nTerm = nNew - SZALLOC;
  1363         -  }
  1364         -
  1365         -  pTerm = &pPhrase->aTerm[pPhrase->nTerm++];
  1366         -  memset(pTerm, 0, sizeof(Fts5ExprTerm));
  1367         -  pTerm->zTerm = sqlite3Fts5Strndup(&rc, pToken, nToken);
  1368         -
         1514  +
         1515  +  /* If an error has already occurred, this is a no-op */
         1516  +  if( pCtx->rc!=SQLITE_OK ) return pCtx->rc;
         1517  +
         1518  +  assert( pPhrase==0 || pPhrase->nTerm>0 );
         1519  +  if( pPhrase && (tflags & FTS5_TOKEN_COLOCATED) ){
         1520  +    Fts5ExprTerm *pSyn;
         1521  +    int nByte = sizeof(Fts5ExprTerm) + nToken+1;
         1522  +    pSyn = (Fts5ExprTerm*)sqlite3_malloc(nByte);
         1523  +    if( pSyn==0 ){
         1524  +      rc = SQLITE_NOMEM;
         1525  +    }else{
         1526  +      memset(pSyn, 0, nByte);
         1527  +      pSyn->zTerm = (char*)&pSyn[1];
         1528  +      memcpy(pSyn->zTerm, pToken, nToken);
         1529  +      pSyn->pSynonym = pPhrase->aTerm[pPhrase->nTerm-1].pSynonym;
         1530  +      pPhrase->aTerm[pPhrase->nTerm-1].pSynonym = pSyn;
         1531  +    }
         1532  +  }else{
         1533  +    Fts5ExprTerm *pTerm;
         1534  +    if( pPhrase==0 || (pPhrase->nTerm % SZALLOC)==0 ){
         1535  +      Fts5ExprPhrase *pNew;
         1536  +      int nNew = SZALLOC + (pPhrase ? pPhrase->nTerm : 0);
         1537  +
         1538  +      pNew = (Fts5ExprPhrase*)sqlite3_realloc(pPhrase, 
         1539  +          sizeof(Fts5ExprPhrase) + sizeof(Fts5ExprTerm) * nNew
         1540  +      );
         1541  +      if( pNew==0 ){
         1542  +        rc = SQLITE_NOMEM;
         1543  +      }else{
         1544  +        if( pPhrase==0 ) memset(pNew, 0, sizeof(Fts5ExprPhrase));
         1545  +        pCtx->pPhrase = pPhrase = pNew;
         1546  +        pNew->nTerm = nNew - SZALLOC;
         1547  +      }
         1548  +    }
         1549  +
         1550  +    if( rc==SQLITE_OK ){
         1551  +      pTerm = &pPhrase->aTerm[pPhrase->nTerm++];
         1552  +      memset(pTerm, 0, sizeof(Fts5ExprTerm));
         1553  +      pTerm->zTerm = sqlite3Fts5Strndup(&rc, pToken, nToken);
         1554  +    }
         1555  +  }
         1556  +
         1557  +  pCtx->rc = rc;
  1369   1558     return rc;
  1370   1559   }
  1371   1560   
  1372   1561   
  1373   1562   /*
  1374   1563   ** Free the phrase object passed as the only argument.
  1375   1564   */
................................................................................
  1413   1602     char *z = 0;
  1414   1603   
  1415   1604     memset(&sCtx, 0, sizeof(TokenCtx));
  1416   1605     sCtx.pPhrase = pAppend;
  1417   1606   
  1418   1607     rc = fts5ParseStringFromToken(pToken, &z);
  1419   1608     if( rc==SQLITE_OK ){
         1609  +    int flags = FTS5_TOKENIZE_QUERY | (bPrefix ? FTS5_TOKENIZE_QUERY : 0);
         1610  +    int n;
  1420   1611       sqlite3Fts5Dequote(z);
  1421         -    rc = sqlite3Fts5Tokenize(pConfig, z, strlen(z), &sCtx, fts5ParseTokenize);
         1612  +    n = strlen(z);
         1613  +    rc = sqlite3Fts5Tokenize(pConfig, flags, z, n, &sCtx, fts5ParseTokenize);
  1422   1614     }
  1423   1615     sqlite3_free(z);
  1424         -  if( rc ){
         1616  +  if( rc || (rc = sCtx.rc) ){
  1425   1617       pParse->rc = rc;
  1426   1618       fts5ExprPhraseFree(sCtx.pPhrase);
  1427   1619       sCtx.pPhrase = 0;
  1428   1620     }else if( sCtx.pPhrase ){
  1429   1621   
  1430   1622       if( pAppend==0 ){
  1431   1623         if( (pParse->nPhrase % 8)==0 ){
................................................................................
  1445   1637       pParse->apPhrase[pParse->nPhrase-1] = sCtx.pPhrase;
  1446   1638       assert( sCtx.pPhrase->nTerm>0 );
  1447   1639       sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = bPrefix;
  1448   1640     }
  1449   1641   
  1450   1642     return sCtx.pPhrase;
  1451   1643   }
         1644  +
         1645  +/*
         1646  +** Create a new FTS5 expression by cloning phrase iPhrase of the
         1647  +** expression passed as the second argument.
         1648  +*/
         1649  +int sqlite3Fts5ExprClonePhrase(
         1650  +  Fts5Config *pConfig,
         1651  +  Fts5Expr *pExpr, 
         1652  +  int iPhrase, 
         1653  +  Fts5Expr **ppNew
         1654  +){
         1655  +  int rc = SQLITE_OK;             /* Return code */
         1656  +  Fts5ExprPhrase *pOrig;          /* The phrase extracted from pExpr */
         1657  +  int i;                          /* Used to iterate through phrase terms */
         1658  +
         1659  +  Fts5Expr *pNew = 0;             /* Expression to return via *ppNew */
         1660  +
         1661  +  TokenCtx sCtx = {0,0};          /* Context object for fts5ParseTokenize */
         1662  +
         1663  +
         1664  +  pOrig = pExpr->apExprPhrase[iPhrase];
         1665  +
         1666  +  pNew = (Fts5Expr*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Expr));
         1667  +  if( rc==SQLITE_OK ){
         1668  +    pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc, 
         1669  +        sizeof(Fts5ExprPhrase*));
         1670  +  }
         1671  +  if( rc==SQLITE_OK ){
         1672  +    pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc, 
         1673  +        sizeof(Fts5ExprNode));
         1674  +  }
         1675  +  if( rc==SQLITE_OK ){
         1676  +    pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc, 
         1677  +        sizeof(Fts5ExprNearset) + sizeof(Fts5ExprPhrase*));
         1678  +  }
         1679  +
         1680  +  for(i=0; rc==SQLITE_OK && i<pOrig->nTerm; i++){
         1681  +    int tflags = 0;
         1682  +    Fts5ExprTerm *p;
         1683  +    for(p=&pOrig->aTerm[i]; p && rc==SQLITE_OK; p=p->pSynonym){
         1684  +      const char *zTerm = p->zTerm;
         1685  +      rc = fts5ParseTokenize((void*)&sCtx, tflags, zTerm, strlen(zTerm), 0, 0);
         1686  +      tflags = FTS5_TOKEN_COLOCATED;
         1687  +    }
         1688  +    if( rc==SQLITE_OK ){
         1689  +      sCtx.pPhrase->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix;
         1690  +    }
         1691  +  }
         1692  +
         1693  +  if( rc==SQLITE_OK ){
         1694  +    /* All the allocations succeeded. Put the expression object together. */
         1695  +    pNew->pIndex = pExpr->pIndex;
         1696  +    pNew->nPhrase = 1;
         1697  +    pNew->apExprPhrase[0] = sCtx.pPhrase;
         1698  +    pNew->pRoot->pNear->apPhrase[0] = sCtx.pPhrase;
         1699  +    pNew->pRoot->pNear->nPhrase = 1;
         1700  +    sCtx.pPhrase->pNode = pNew->pRoot;
         1701  +
         1702  +    if( pOrig->nTerm==1 && pOrig->aTerm[0].pSynonym==0 ){
         1703  +      pNew->pRoot->eType = FTS5_TERM;
         1704  +    }else{
         1705  +      pNew->pRoot->eType = FTS5_STRING;
         1706  +    }
         1707  +  }else{
         1708  +    sqlite3Fts5ExprFree(pNew);
         1709  +    fts5ExprPhraseFree(sCtx.pPhrase);
         1710  +    pNew = 0;
         1711  +  }
         1712  +
         1713  +  *ppNew = pNew;
         1714  +  return rc;
         1715  +}
         1716  +
  1452   1717   
  1453   1718   /*
  1454   1719   ** Token pTok has appeared in a MATCH expression where the NEAR operator
  1455   1720   ** is expected. If token pTok does not contain "NEAR", store an error
  1456   1721   ** in the pParse object.
  1457   1722   */
  1458   1723   void sqlite3Fts5ParseNear(Fts5Parse *pParse, Fts5Token *pTok){
................................................................................
  1626   1891         pRet->eType = eType;
  1627   1892         pRet->pNear = pNear;
  1628   1893         if( eType==FTS5_STRING ){
  1629   1894           int iPhrase;
  1630   1895           for(iPhrase=0; iPhrase<pNear->nPhrase; iPhrase++){
  1631   1896             pNear->apPhrase[iPhrase]->pNode = pRet;
  1632   1897           }
  1633         -        if( pNear->nPhrase==1 && pNear->apPhrase[0]->nTerm==1 ){
         1898  +        if( pNear->nPhrase==1 
         1899  +         && pNear->apPhrase[0]->nTerm==1 
         1900  +         && pNear->apPhrase[0]->aTerm[0].pSynonym==0
         1901  +        ){
  1634   1902             pRet->eType = FTS5_TERM;
  1635   1903           }
  1636   1904         }else{
  1637   1905           fts5ExprAddChildren(pRet, pLeft);
  1638   1906           fts5ExprAddChildren(pRet, pRight);
  1639   1907         }
  1640   1908       }
................................................................................
  1646   1914       sqlite3Fts5ParseNodeFree(pRight);
  1647   1915       sqlite3Fts5ParseNearsetFree(pNear);
  1648   1916     }
  1649   1917     return pRet;
  1650   1918   }
  1651   1919   
  1652   1920   static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){
  1653         -  char *zQuoted = sqlite3_malloc(strlen(pTerm->zTerm) * 2 + 3 + 2);
         1921  +  int nByte = 0;
         1922  +  Fts5ExprTerm *p;
         1923  +  char *zQuoted;
         1924  +
         1925  +  /* Determine the maximum amount of space required. */
         1926  +  for(p=pTerm; p; p=p->pSynonym){
         1927  +    nByte += strlen(pTerm->zTerm) * 2 + 3 + 2;
         1928  +  }
         1929  +  zQuoted = sqlite3_malloc(nByte);
         1930  +
  1654   1931     if( zQuoted ){
  1655   1932       int i = 0;
  1656         -    char *zIn = pTerm->zTerm;
  1657         -    zQuoted[i++] = '"';
  1658         -    while( *zIn ){
  1659         -      if( *zIn=='"' ) zQuoted[i++] = '"';
  1660         -      zQuoted[i++] = *zIn++;
         1933  +    for(p=pTerm; p; p=p->pSynonym){
         1934  +      char *zIn = p->zTerm;
         1935  +      zQuoted[i++] = '"';
         1936  +      while( *zIn ){
         1937  +        if( *zIn=='"' ) zQuoted[i++] = '"';
         1938  +        zQuoted[i++] = *zIn++;
         1939  +      }
         1940  +      zQuoted[i++] = '"';
         1941  +      if( p->pSynonym ) zQuoted[i++] = '|';
  1661   1942       }
  1662         -    zQuoted[i++] = '"';
  1663   1943       if( pTerm->bPrefix ){
  1664   1944         zQuoted[i++] = ' ';
  1665   1945         zQuoted[i++] = '*';
  1666   1946       }
  1667   1947       zQuoted[i++] = '\0';
  1668   1948     }
  1669   1949     return zQuoted;

Changes to ext/fts5/fts5_index.c.

    83     83   **
    84     84   **   Then, for each level from 0 to nMax:
    85     85   **
    86     86   **     + number of input segments in ongoing merge.
    87     87   **     + total number of segments in level.
    88     88   **     + for each segment from oldest to newest:
    89     89   **         + segment id (always > 0)
    90         -**         + b-tree height (1 -> root is leaf, 2 -> root is parent of leaf etc.)
    91     90   **         + first leaf page number (often 1, always greater than 0)
    92     91   **         + final leaf page number
    93     92   **
    94     93   ** 2. The Averages Record:
    95     94   **
    96     95   **   A single record within the %_data table. The data is a list of varints.
    97     96   **   The first value is the number of rows in the index. Then, for each column
    98         -**   from left to right, the total number of tokens in the column for all 
           97  +**   from left to right, the total number of tokens in the column for all
    99     98   **   rows of the table.
   100     99   **
   101    100   ** 3. Segment leaves:
   102    101   **
   103         -**   TERM DOCLIST FORMAT:
          102  +**   TERM/DOCLIST FORMAT:
   104    103   **
   105    104   **     Most of each segment leaf is taken up by term/doclist data. The 
   106         -**     general format of the term/doclist data is:
          105  +**     general format of term/doclist, starting with the first term
          106  +**     on the leaf page, is:
   107    107   **
   108    108   **         varint : size of first term
   109    109   **         blob:    first term data
   110    110   **         doclist: first doclist
   111    111   **         zero-or-more {
   112    112   **           varint:  number of bytes in common with previous term
   113    113   **           varint:  number of bytes of new term data (nNew)
................................................................................
   119    119   **
   120    120   **         varint:  first rowid
   121    121   **         poslist: first poslist
   122    122   **         zero-or-more {
   123    123   **           varint:  rowid delta (always > 0)
   124    124   **           poslist: next poslist
   125    125   **         }
   126         -**         0x00 byte
   127    126   **
   128    127   **     poslist format:
   129    128   **
   130    129   **         varint: size of poslist in bytes multiplied by 2, not including
   131    130   **                 this field. Plus 1 if this entry carries the "delete" flag.
   132    131   **         collist: collist for column 0
   133    132   **         zero-or-more {
................................................................................
   139    138   **     collist format:
   140    139   **
   141    140   **         varint: first offset + 2
   142    141   **         zero-or-more {
   143    142   **           varint: offset delta + 2
   144    143   **         }
   145    144   **
   146         -**   PAGINATION
          145  +**   PAGE FORMAT
   147    146   **
   148         -**     The format described above is only accurate if the entire term/doclist
   149         -**     data fits on a single leaf page. If this is not the case, the format
   150         -**     is changed in two ways:
          147  +**     Each leaf page begins with a 4-byte header containing 2 16-bit 
          148  +**     unsigned integer fields in big-endian format. They are:
          149  +**
          150  +**       * The byte offset of the first rowid on the page, if it exists
          151  +**         and occurs before the first term (otherwise 0).
          152  +**
          153  +**       * The byte offset of the start of the page footer. If the page
          154  +**         footer is 0 bytes in size, then this field is the same as the
          155  +**         size of the leaf page in bytes.
          156  +**
          157  +**     The page footer consists of a single varint for each term located
          158  +**     on the page. Each varint is the byte offset of the current term
          159  +**     within the page, delta-compressed against the previous value. In
          160  +**     other words, the first varint in the footer is the byte offset of
          161  +**     the first term, the second is the byte offset of the second less that
          162  +**     of the first, and so on.
          163  +**
          164  +**     The term/doclist format described above is accurate if the entire
          165  +**     term/doclist data fits on a single leaf page. If this is not the case,
          166  +**     the format is changed in two ways:
   151    167   **
   152    168   **       + if the first rowid on a page occurs before the first term, it
   153    169   **         is stored as a literal value:
   154    170   **
   155    171   **             varint:  first rowid
   156    172   **
   157    173   **       + the first term on each page is stored in the same way as the
   158    174   **         very first term of the segment:
   159    175   **
   160    176   **             varint : size of first term
   161    177   **             blob:    first term data
   162    178   **
   163         -**     Each leaf page begins with:
   164         -**
   165         -**       + 2-byte unsigned containing offset to first rowid (or 0).
   166         -**       + 2-byte unsigned containing offset to first term (or 0).
   167         -**
   168         -**   Followed by term/doclist data.
   169         -**
   170         -** 4. Segment interior nodes:
   171         -**
   172         -**   The interior nodes turn the list of leaves into a b+tree. 
   173         -**
   174         -**   Each interior node begins with a varint - the page number of the left
   175         -**   most child node. Following this, for each leaf page except the first,
   176         -**   the interior nodes contain:
   177         -**
   178         -**     a) If the leaf page contains at least one term, then a term-prefix that
   179         -**        is greater than all previous terms, and less than or equal to the
   180         -**        first term on the leaf page.
   181         -**
   182         -**     b) If the leaf page no terms, a record indicating how many consecutive
   183         -**        leaves contain no terms, and whether or not there is an associated
   184         -**        by-rowid index record.
   185         -**
   186         -**   By definition, there is never more than one type (b) record in a row.
   187         -**   Type (b) records only ever appear on height=1 pages - immediate parents
   188         -**   of leaves. Only type (a) records are pushed to higher levels.
   189         -**
   190         -**   Term format:
   191         -**
   192         -**     * Number of bytes in common with previous term plus 2, as a varint.
   193         -**     * Number of bytes of new term data, as a varint.
   194         -**     * new term data.
   195         -**
   196         -**   No-term format:
   197         -**
   198         -**     * either an 0x00 or 0x01 byte. If the value 0x01 is used, then there 
   199         -**       is an associated index-by-rowid record.
   200         -**     * the number of zero-term leaves as a varint.
   201         -**
   202    179   ** 5. Segment doclist indexes:
   203    180   **
   204    181   **   Doclist indexes are themselves b-trees, however they usually consist of
   205    182   **   a single leaf record only. The format of each doclist index leaf page 
   206    183   **   is:
   207    184   **
   208    185   **     * Flags byte. Bits are:
................................................................................
   233    210   /*
   234    211   ** Rowids for the averages and structure records in the %_data table.
   235    212   */
   236    213   #define FTS5_AVERAGES_ROWID     1    /* Rowid used for the averages record */
   237    214   #define FTS5_STRUCTURE_ROWID   10    /* The structure record */
   238    215   
   239    216   /*
   240         -** Macros determining the rowids used by segment nodes. All nodes in all
   241         -** segments for all indexes (the regular FTS index and any prefix indexes)
   242         -** are stored in the %_data table with large positive rowids.
          217  +** Macros determining the rowids used by segment leaves and dlidx leaves
          218  +** and nodes. All nodes and leaves are stored in the %_data table with large
          219  +** positive rowids.
   243    220   **
   244         -** The %_data table may contain up to (1<<FTS5_SEGMENT_INDEX_BITS) 
   245         -** indexes - one regular term index and zero or more prefix indexes.
          221  +** Each segment has a unique non-zero 16-bit id.
   246    222   **
   247         -** Each segment in an index has a unique id greater than zero.
   248         -**
   249         -** Each node in a segment b-tree is assigned a "page number" that is unique
   250         -** within nodes of its height within the segment (leaf nodes have a height 
   251         -** of 0, parents 1, etc.). Page numbers are allocated sequentially so that
   252         -** a nodes page number is always one more than its left sibling.
   253         -**
   254         -** The rowid for a node is then found using the FTS5_SEGMENT_ROWID() macro
   255         -** below. The FTS5_SEGMENT_*_BITS macros define the number of bits used
   256         -** to encode the three FTS5_SEGMENT_ROWID() arguments. This module returns
   257         -** SQLITE_FULL and fails the current operation if they ever prove too small.
          223  +** The rowid for each segment leaf is found by passing the segment id and 
          224  +** the leaf page number to the FTS5_SEGMENT_ROWID macro. Leaves are numbered
          225  +** sequentially starting from 1.
   258    226   */
   259    227   #define FTS5_DATA_ID_B     16     /* Max seg id number 65535 */
   260    228   #define FTS5_DATA_DLI_B     1     /* Doclist-index flag (1 bit) */
   261         -#define FTS5_DATA_HEIGHT_B  5     /* Max b-tree height of 32 */
          229  +#define FTS5_DATA_HEIGHT_B  5     /* Max dlidx tree height of 32 */
   262    230   #define FTS5_DATA_PAGE_B   31     /* Max page number of 2147483648 */
   263    231   
   264    232   #define fts5_dri(segid, dlidx, height, pgno) (                                 \
   265    233    ((i64)(segid)  << (FTS5_DATA_PAGE_B+FTS5_DATA_HEIGHT_B+FTS5_DATA_DLI_B)) +    \
   266    234    ((i64)(dlidx)  << (FTS5_DATA_PAGE_B + FTS5_DATA_HEIGHT_B)) +                  \
   267    235    ((i64)(height) << (FTS5_DATA_PAGE_B)) +                                       \
   268    236    ((i64)(pgno))                                                                 \
   269    237   )
   270    238   
   271         -#define FTS5_SEGMENT_ROWID(segid, height, pgno) fts5_dri(segid, 0, height, pgno)
   272         -#define FTS5_DLIDX_ROWID(segid, height, pgno)   fts5_dri(segid, 1, height, pgno)
          239  +#define FTS5_SEGMENT_ROWID(segid, pgno)       fts5_dri(segid, 0, 0, pgno)
          240  +#define FTS5_DLIDX_ROWID(segid, height, pgno) fts5_dri(segid, 1, height, pgno)
   273    241   
   274    242   /*
   275    243   ** Maximum segments permitted in a single index 
   276    244   */
   277    245   #define FTS5_MAX_SEGMENT 2000
   278    246   
   279    247   #ifdef SQLITE_DEBUG
................................................................................
   289    257   #define FTS5_DATA_ZERO_PADDING 8
   290    258   #define FTS5_DATA_PADDING 20
   291    259   
   292    260   typedef struct Fts5Data Fts5Data;
   293    261   typedef struct Fts5DlidxIter Fts5DlidxIter;
   294    262   typedef struct Fts5DlidxLvl Fts5DlidxLvl;
   295    263   typedef struct Fts5DlidxWriter Fts5DlidxWriter;
   296         -typedef struct Fts5NodeIter Fts5NodeIter;
   297    264   typedef struct Fts5PageWriter Fts5PageWriter;
   298    265   typedef struct Fts5SegIter Fts5SegIter;
   299    266   typedef struct Fts5DoclistIter Fts5DoclistIter;
   300    267   typedef struct Fts5SegWriter Fts5SegWriter;
   301    268   typedef struct Fts5Structure Fts5Structure;
   302    269   typedef struct Fts5StructureLevel Fts5StructureLevel;
   303    270   typedef struct Fts5StructureSegment Fts5StructureSegment;
   304    271   
   305    272   struct Fts5Data {
   306    273     u8 *p;                          /* Pointer to buffer containing record */
   307         -  int n;                          /* Size of record in bytes */
          274  +  int nn;                         /* Size of record in bytes */
          275  +  int szLeaf;                     /* Size of leaf without page-index */
   308    276   };
   309    277   
   310    278   /*
   311    279   ** One object per %_data table.
   312    280   */
   313    281   struct Fts5Index {
   314    282     Fts5Config *pConfig;            /* Virtual table configuration */
................................................................................
   352    320   /*
   353    321   ** The contents of the "structure" record for each index are represented
   354    322   ** using an Fts5Structure record in memory. Which uses instances of the 
   355    323   ** other Fts5StructureXXX types as components.
   356    324   */
   357    325   struct Fts5StructureSegment {
   358    326     int iSegid;                     /* Segment id */
   359         -  int nHeight;                    /* Height of segment b-tree */
   360    327     int pgnoFirst;                  /* First leaf page number in segment */
   361    328     int pgnoLast;                   /* Last leaf page number in segment */
   362    329   };
   363    330   struct Fts5StructureLevel {
   364    331     int nMerge;                     /* Number of segments in incr-merge */
   365    332     int nSeg;                       /* Total number of segments on level */
   366    333     Fts5StructureSegment *aSeg;     /* Array of segments. aSeg[0] is oldest. */
................................................................................
   374    341   };
   375    342   
   376    343   /*
   377    344   ** An object of type Fts5SegWriter is used to write to segments.
   378    345   */
   379    346   struct Fts5PageWriter {
   380    347     int pgno;                       /* Page number for this page */
   381         -  Fts5Buffer buf;                 /* Buffer containing page data */
          348  +  int iPrevPgidx;                 /* Previous value written into pgidx */
          349  +  Fts5Buffer buf;                 /* Buffer containing leaf data */
          350  +  Fts5Buffer pgidx;               /* Buffer containing page-index */
   382    351     Fts5Buffer term;                /* Buffer containing previous term on page */
   383    352   };
   384    353   struct Fts5DlidxWriter {
   385    354     int pgno;                       /* Page number for this page */
   386    355     int bPrevValid;                 /* True if iPrev is valid */
   387    356     i64 iPrev;                      /* Previous rowid value written to page */
   388    357     Fts5Buffer buf;                 /* Buffer containing page data */
................................................................................
   389    358   };
   390    359   struct Fts5SegWriter {
   391    360     int iSegid;                     /* Segid to write to */
   392    361     Fts5PageWriter writer;          /* PageWriter object */
   393    362     i64 iPrevRowid;                 /* Previous rowid written to current leaf */
   394    363     u8 bFirstRowidInDoclist;        /* True if next rowid is first in doclist */
   395    364     u8 bFirstRowidInPage;           /* True if next rowid is first in page */
          365  +  /* TODO1: Can use (writer.pgidx.n==0) instead of bFirstTermInPage */
   396    366     u8 bFirstTermInPage;            /* True if next term will be first in leaf */
   397    367     int nLeafWritten;               /* Number of leaf pages written */
   398    368     int nEmpty;                     /* Number of contiguous term-less nodes */
   399    369   
   400    370     int nDlidx;                     /* Allocated size of aDlidx[] array */
   401    371     Fts5DlidxWriter *aDlidx;        /* Array of Fts5DlidxWriter objects */
   402    372   
................................................................................
   469    439   **
   470    440   ** iRowidOffset/nRowidOffset/aRowidOffset:
   471    441   **     These are used if the FTS5_SEGITER_REVERSE flag is set.
   472    442   **
   473    443   **     For each rowid on the page corresponding to the current term, the
   474    444   **     corresponding aRowidOffset[] entry is set to the byte offset of the
   475    445   **     start of the "position-list-size" field within the page.
          446  +**
          447  +** iTermIdx:
          448  +**     Index of current term on iTermLeafPgno.
   476    449   */
   477    450   struct Fts5SegIter {
   478    451     Fts5StructureSegment *pSeg;     /* Segment to iterate through */
   479    452     int flags;                      /* Mask of configuration flags */
   480    453     int iLeafPgno;                  /* Current leaf page number */
   481    454     Fts5Data *pLeaf;                /* Current leaf data */
   482    455     Fts5Data *pNextLeaf;            /* Leaf page (iLeafPgno+1) */
   483    456     int iLeafOffset;                /* Byte offset within current leaf */
   484    457   
   485    458     /* The page and offset from which the current term was read. The offset 
   486    459     ** is the offset of the first rowid in the current doclist.  */
   487    460     int iTermLeafPgno;
   488    461     int iTermLeafOffset;
          462  +
          463  +  int iPgidxOff;                  /* Next offset in pgidx */
          464  +  int iEndofDoclist;
   489    465   
   490    466     /* The following are only used if the FTS5_SEGITER_REVERSE flag is set. */
   491    467     int iRowidOffset;               /* Current entry in aRowidOffset[] */
   492    468     int nRowidOffset;               /* Allocated size of aRowidOffset[] array */
   493    469     int *aRowidOffset;              /* Array of offset to rowid fields */
   494    470   
   495    471     Fts5DlidxIter *pDlidx;          /* If there is a doclist-index */
................................................................................
   496    472   
   497    473     /* Variables populated based on current entry. */
   498    474     Fts5Buffer term;                /* Current term */
   499    475     i64 iRowid;                     /* Current rowid */
   500    476     int nPos;                       /* Number of bytes in current position list */
   501    477     int bDel;                       /* True if the delete flag is set */
   502    478   };
          479  +
          480  +/*
          481  +** Argument is a pointer to an Fts5Data structure that contains a 
          482  +** leaf page.
          483  +*/
          484  +#define ASSERT_SZLEAF_OK(x) assert( \
          485  +    (x)->szLeaf==(x)->nn || (x)->szLeaf==fts5GetU16(&(x)->p[2]) \
          486  +)
   503    487   
   504    488   #define FTS5_SEGITER_ONETERM 0x01
   505    489   #define FTS5_SEGITER_REVERSE 0x02
   506    490   
   507    491   
          492  +/* 
          493  +** Argument is a pointer to an Fts5Data structure that contains a leaf
          494  +** page. This macro evaluates to true if the leaf contains no terms, or
          495  +** false if it contains at least one term.
          496  +*/
          497  +#define fts5LeafIsTermless(x) ((x)->szLeaf >= (x)->nn)
          498  +
          499  +#define fts5LeafTermOff(x, i) (fts5GetU16(&(x)->p[(x)->szLeaf + (i)*2]))
          500  +
          501  +#define fts5LeafFirstRowidOff(x) (fts5GetU16((x)->p))
          502  +
   508    503   /*
   509    504   ** poslist:
   510    505   **   Used by sqlite3Fts5IterPoslist() when the poslist needs to be buffered.
   511    506   **   There is no way to tell if this is populated or not.
   512    507   */
   513    508   struct Fts5IndexIter {
   514    509     Fts5Index *pIndex;              /* Index that owns this iterator */
................................................................................
   522    517   
   523    518     i64 iSwitchRowid;               /* Firstest rowid of other than aFirst[1] */
   524    519     Fts5CResult *aFirst;            /* Current merge state (see above) */
   525    520     Fts5SegIter aSeg[1];            /* Array of segment iterators */
   526    521   };
   527    522   
   528    523   
   529         -/*
   530         -** Object for iterating through the conents of a single internal node in 
   531         -** memory.
   532         -*/
   533         -struct Fts5NodeIter {
   534         -  /* Internal. Set and managed by fts5NodeIterXXX() functions. Except, 
   535         -  ** the EOF test for the iterator is (Fts5NodeIter.aData==0).  */
   536         -  const u8 *aData;
   537         -  int nData;
   538         -  int iOff;
   539         -
   540         -  /* Output variables */
   541         -  Fts5Buffer term;
   542         -  int nEmpty;
   543         -  int iChild;
   544         -  int bDlidx;
   545         -};
   546         -
   547    524   /*
   548    525   ** An instance of the following type is used to iterate through the contents
   549    526   ** of a doclist-index record.
   550    527   **
   551    528   ** pData:
   552    529   **   Record containing the doclist-index data.
   553    530   **
................................................................................
   569    546   };
   570    547   struct Fts5DlidxIter {
   571    548     int nLvl;
   572    549     int iSegid;
   573    550     Fts5DlidxLvl aLvl[1];
   574    551   };
   575    552   
   576         -
   577         -
   578         -/*
   579         -** The first argument passed to this macro is a pointer to an Fts5Buffer
   580         -** object.
   581         -*/
   582         -#define fts5BufferSize(pBuf,n) {                \
   583         -  if( pBuf->nSpace<n ) {                        \
   584         -    u8 *pNew = sqlite3_realloc(pBuf->p, n);     \
   585         -    if( pNew==0 ){                              \
   586         -      sqlite3_free(pBuf->p);                    \
   587         -    }                                           \
   588         -    pBuf->nSpace = n;                           \
   589         -    pBuf->p = pNew;                             \
   590         -  }                                             \
   591         -}
   592         -
   593    553   static void fts5PutU16(u8 *aOut, u16 iVal){
   594    554     aOut[0] = (iVal>>8);
   595    555     aOut[1] = (iVal&0xFF);
   596    556   }
   597    557   
   598    558   static u16 fts5GetU16(const u8 *aIn){
   599    559     return ((u16)aIn[0] << 8) + aIn[1];
................................................................................
   613    573   ** Compare the contents of the pLeft buffer with the pRight/nRight blob.
   614    574   **
   615    575   ** Return -ve if pLeft is smaller than pRight, 0 if they are equal or
   616    576   ** +ve if pRight is smaller than pLeft. In other words:
   617    577   **
   618    578   **     res = *pLeft - *pRight
   619    579   */
          580  +#ifdef SQLITE_DEBUG
   620    581   static int fts5BufferCompareBlob(
   621    582     Fts5Buffer *pLeft,              /* Left hand side of comparison */
   622    583     const u8 *pRight, int nRight    /* Right hand side of comparison */
   623    584   ){
   624    585     int nCmp = MIN(pLeft->n, nRight);
   625    586     int res = memcmp(pLeft->p, pRight, nCmp);
   626    587     return (res==0 ? (pLeft->n - nRight) : res);
   627    588   }
   628         -
          589  +#endif
   629    590   
   630    591   /*
   631    592   ** Compare the contents of the two buffers using memcmp(). If one buffer
   632    593   ** is a prefix of the other, it is considered the lesser.
   633    594   **
   634    595   ** Return -ve if pLeft is smaller than pRight, 0 if they are equal or
   635    596   ** +ve if pRight is smaller than pLeft. In other words:
................................................................................
   649    610   ){
   650    611     int nCmp = MIN(nLeft, nRight);
   651    612     int res = memcmp(pLeft, pRight, nCmp);
   652    613     return (res==0 ? (nLeft - nRight) : res);
   653    614   }
   654    615   #endif
   655    616   
          617  +static int fts5LeafFirstTermOff(Fts5Data *pLeaf){
          618  +  int ret;
          619  +  fts5GetVarint32(&pLeaf->p[pLeaf->szLeaf], ret);
          620  +  return ret;
          621  +}
   656    622   
   657    623   /*
   658    624   ** Close the read-only blob handle, if it is open.
   659    625   */
   660    626   static void fts5CloseReader(Fts5Index *p){
   661    627     if( p->pReader ){
   662    628       sqlite3_blob *pReader = p->pReader;
   663    629       p->pReader = 0;
   664    630       sqlite3_blob_close(pReader);
   665    631     }
   666    632   }
   667    633   
   668         -static Fts5Data *fts5DataReadOrBuffer(
   669         -  Fts5Index *p, 
   670         -  Fts5Buffer *pBuf, 
   671         -  i64 iRowid
   672         -){
          634  +
          635  +/*
          636  +** Retrieve a record from the %_data table.
          637  +**
          638  +** If an error occurs, NULL is returned and an error left in the 
          639  +** Fts5Index object.
          640  +*/
          641  +static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){
   673    642     Fts5Data *pRet = 0;
   674    643     if( p->rc==SQLITE_OK ){
   675    644       int rc = SQLITE_OK;
   676    645   
   677    646       if( p->pReader ){
   678    647         /* This call may return SQLITE_ABORT if there has been a savepoint
   679    648         ** rollback since it was last used. In this case a new blob handle
................................................................................
   685    654         p->pReader = pBlob;
   686    655         if( rc!=SQLITE_OK ){
   687    656           fts5CloseReader(p);
   688    657         }
   689    658         if( rc==SQLITE_ABORT ) rc = SQLITE_OK;
   690    659       }
   691    660   
   692         -    /* If the blob handle is not yet open, open and seek it. Otherwise, use
   693         -    ** the blob_reopen() API to reseek the existing blob handle.  */
          661  +    /* If the blob handle is not open at this point, open it and seek 
          662  +    ** to the requested entry.  */
   694    663       if( p->pReader==0 && rc==SQLITE_OK ){
   695    664         Fts5Config *pConfig = p->pConfig;
   696    665         rc = sqlite3_blob_open(pConfig->db, 
   697    666             pConfig->zDb, p->zDataTbl, "block", iRowid, 0, &p->pReader
   698    667         );
   699    668       }
   700    669   
................................................................................
   704    673       ** table, missing row, non-blob/text in block column - indicate 
   705    674       ** backing store corruption.  */
   706    675       if( rc==SQLITE_ERROR ) rc = FTS5_CORRUPT;
   707    676   
   708    677       if( rc==SQLITE_OK ){
   709    678         u8 *aOut = 0;               /* Read blob data into this buffer */
   710    679         int nByte = sqlite3_blob_bytes(p->pReader);
   711         -      if( pBuf ){
   712         -        fts5BufferSize(pBuf, MAX(nByte, p->pConfig->pgsz) + 20);
   713         -        pBuf->n = nByte;
   714         -        aOut = pBuf->p;
   715         -        if( aOut==0 ){
   716         -          rc = SQLITE_NOMEM;
   717         -        }
          680  +      int nAlloc = sizeof(Fts5Data) + nByte + FTS5_DATA_PADDING;
          681  +      pRet = (Fts5Data*)sqlite3_malloc(nAlloc);
          682  +      if( pRet ){
          683  +        pRet->nn = nByte;
          684  +        aOut = pRet->p = (u8*)&pRet[1];
   718    685         }else{
   719         -        int nSpace = nByte + FTS5_DATA_PADDING;
   720         -        pRet = (Fts5Data*)sqlite3_malloc(nSpace+sizeof(Fts5Data));
   721         -        if( pRet ){
   722         -          pRet->n = nByte;
   723         -          aOut = pRet->p = (u8*)&pRet[1];
   724         -        }else{
   725         -          rc = SQLITE_NOMEM;
   726         -        }
          686  +        rc = SQLITE_NOMEM;
   727    687         }
   728    688   
   729    689         if( rc==SQLITE_OK ){
   730    690           rc = sqlite3_blob_read(p->pReader, aOut, nByte, 0);
   731    691         }
   732    692         if( rc!=SQLITE_OK ){
   733    693           sqlite3_free(pRet);
   734    694           pRet = 0;
          695  +      }else{
          696  +        /* TODO1: Fix this */
          697  +        pRet->szLeaf = fts5GetU16(&pRet->p[2]);
   735    698         }
   736    699       }
   737    700       p->rc = rc;
   738    701       p->nRead++;
   739    702     }
   740    703   
   741         -  return pRet;
   742         -}
   743         -
   744         -/*
   745         -** Retrieve a record from the %_data table.
   746         -**
   747         -** If an error occurs, NULL is returned and an error left in the 
   748         -** Fts5Index object.
   749         -*/
   750         -static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){
   751         -  Fts5Data *pRet = fts5DataReadOrBuffer(p, 0, iRowid);
   752    704     assert( (pRet==0)==(p->rc!=SQLITE_OK) );
   753    705     return pRet;
   754    706   }
   755    707   
   756         -/*
   757         -** Read a record from the %_data table into the buffer supplied as the
   758         -** second argument.
   759         -**
   760         -** If an error occurs, an error is left in the Fts5Index object. If an
   761         -** error has already occurred when this function is called, it is a 
   762         -** no-op.
   763         -*/
   764         -static void fts5DataBuffer(Fts5Index *p, Fts5Buffer *pBuf, i64 iRowid){
   765         -  (void)fts5DataReadOrBuffer(p, pBuf, iRowid);
   766         -}
   767         -
   768    708   /*
   769    709   ** Release a reference to data record returned by an earlier call to
   770    710   ** fts5DataRead().
   771    711   */
   772    712   static void fts5DataRelease(Fts5Data *pData){
   773    713     sqlite3_free(pData);
   774    714   }
................................................................................
   793    733   /*
   794    734   ** INSERT OR REPLACE a record into the %_data table.
   795    735   */
   796    736   static void fts5DataWrite(Fts5Index *p, i64 iRowid, const u8 *pData, int nData){
   797    737     if( p->rc!=SQLITE_OK ) return;
   798    738   
   799    739     if( p->pWriter==0 ){
   800         -    int rc = SQLITE_OK;
   801    740       Fts5Config *pConfig = p->pConfig;
   802    741       fts5IndexPrepareStmt(p, &p->pWriter, sqlite3_mprintf(
   803    742             "REPLACE INTO '%q'.'%q_data'(id, block) VALUES(?,?)", 
   804    743             pConfig->zDb, pConfig->zName
   805    744       ));
   806    745       if( p->rc ) return;
   807    746     }
................................................................................
   845    784     p->rc = sqlite3_reset(p->pDeleter);
   846    785   }
   847    786   
   848    787   /*
   849    788   ** Remove all records associated with segment iSegid.
   850    789   */
   851    790   static void fts5DataRemoveSegment(Fts5Index *p, int iSegid){
   852         -  i64 iFirst = FTS5_SEGMENT_ROWID(iSegid, 0, 0);
   853         -  i64 iLast = FTS5_SEGMENT_ROWID(iSegid+1, 0, 0)-1;
          791  +  i64 iFirst = FTS5_SEGMENT_ROWID(iSegid, 0);
          792  +  i64 iLast = FTS5_SEGMENT_ROWID(iSegid+1, 0)-1;
   854    793     fts5DataDelete(p, iFirst, iLast);
   855    794     if( p->pIdxDeleter==0 ){
   856    795       Fts5Config *pConfig = p->pConfig;
   857    796       fts5IndexPrepareStmt(p, &p->pIdxDeleter, sqlite3_mprintf(
   858    797             "DELETE FROM '%q'.'%q_idx' WHERE segid=?",
   859    798             pConfig->zDb, pConfig->zName
   860    799       ));
................................................................................
   943    882             nTotal * sizeof(Fts5StructureSegment)
   944    883         );
   945    884   
   946    885         if( rc==SQLITE_OK ){
   947    886           pLvl->nSeg = nTotal;
   948    887           for(iSeg=0; iSeg<nTotal; iSeg++){
   949    888             i += fts5GetVarint32(&pData[i], pLvl->aSeg[iSeg].iSegid);
   950         -          i += fts5GetVarint32(&pData[i], pLvl->aSeg[iSeg].nHeight);
   951    889             i += fts5GetVarint32(&pData[i], pLvl->aSeg[iSeg].pgnoFirst);
   952    890             i += fts5GetVarint32(&pData[i], pLvl->aSeg[iSeg].pgnoLast);
   953    891           }
   954    892         }else{
   955    893           fts5StructureRelease(pRet);
   956    894           pRet = 0;
   957    895         }
................................................................................
  1029    967   ** Fts5Index handle. If an error has already occurred when this function
  1030    968   ** is called, it is a no-op.
  1031    969   */
  1032    970   static Fts5Structure *fts5StructureRead(Fts5Index *p){
  1033    971     Fts5Config *pConfig = p->pConfig;
  1034    972     Fts5Structure *pRet = 0;        /* Object to return */
  1035    973     int iCookie;                    /* Configuration cookie */
  1036         -  Fts5Buffer buf = {0, 0, 0};
          974  +  Fts5Data *pData;
  1037    975   
  1038         -  fts5DataBuffer(p, &buf, FTS5_STRUCTURE_ROWID);
  1039         -  if( buf.p==0 ) return 0;
  1040         -  assert( buf.nSpace>=(buf.n + FTS5_DATA_ZERO_PADDING) );
  1041         -  memset(&buf.p[buf.n], 0, FTS5_DATA_ZERO_PADDING);
  1042         -  p->rc = fts5StructureDecode(buf.p, buf.n, &iCookie, &pRet);
  1043         -
          976  +  pData = fts5DataRead(p, FTS5_STRUCTURE_ROWID);
          977  +  if( p->rc ) return 0;
          978  +  /* TODO: Do we need this if the leaf-index is appended? Probably... */
          979  +  memset(&pData->p[pData->nn], 0, FTS5_DATA_PADDING);
          980  +  p->rc = fts5StructureDecode(pData->p, pData->nn, &iCookie, &pRet);
  1044    981     if( p->rc==SQLITE_OK && pConfig->iCookie!=iCookie ){
  1045    982       p->rc = sqlite3Fts5ConfigLoad(pConfig, iCookie);
  1046    983     }
  1047    984   
  1048         -  fts5BufferFree(&buf);
          985  +  fts5DataRelease(pData);
  1049    986     if( p->rc!=SQLITE_OK ){
  1050    987       fts5StructureRelease(pRet);
  1051    988       pRet = 0;
  1052    989     }
  1053    990     return pRet;
  1054    991   }
  1055    992   
................................................................................
  1100   1037         Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl];
  1101   1038         fts5BufferAppendVarint(&p->rc, &buf, pLvl->nMerge);
  1102   1039         fts5BufferAppendVarint(&p->rc, &buf, pLvl->nSeg);
  1103   1040         assert( pLvl->nMerge<=pLvl->nSeg );
  1104   1041   
  1105   1042         for(iSeg=0; iSeg<pLvl->nSeg; iSeg++){
  1106   1043           fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].iSegid);
  1107         -        fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].nHeight);
  1108   1044           fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].pgnoFirst);
  1109   1045           fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].pgnoLast);
  1110   1046         }
  1111   1047       }
  1112   1048   
  1113   1049       fts5DataWrite(p, FTS5_STRUCTURE_ROWID, buf.p, buf.n);
  1114   1050       fts5BufferFree(&buf);
................................................................................
  1189   1125   ){
  1190   1126     if( p->rc==SQLITE_OK ){
  1191   1127       int iTst;
  1192   1128       int iPromote = -1;
  1193   1129       int szPromote = 0;            /* Promote anything this size or smaller */
  1194   1130       Fts5StructureSegment *pSeg;   /* Segment just written */
  1195   1131       int szSeg;                    /* Size of segment just written */
         1132  +    int nSeg = pStruct->aLevel[iLvl].nSeg;
  1196   1133   
  1197         -
         1134  +    if( nSeg==0 ) return;
  1198   1135       pSeg = &pStruct->aLevel[iLvl].aSeg[pStruct->aLevel[iLvl].nSeg-1];
  1199   1136       szSeg = (1 + pSeg->pgnoLast - pSeg->pgnoFirst);
  1200   1137   
  1201   1138       /* Check for condition (a) */
  1202   1139       for(iTst=iLvl-1; iTst>=0 && pStruct->aLevel[iTst].nSeg==0; iTst--);
  1203   1140       if( iTst>=0 ){
  1204   1141         int i;
................................................................................
  1224   1161         szPromote = szSeg;
  1225   1162       }
  1226   1163       fts5StructurePromoteTo(p, iPromote, szPromote, pStruct);
  1227   1164     }
  1228   1165   }
  1229   1166   
  1230   1167   
  1231         -/*
  1232         -** If the pIter->iOff offset currently points to an entry indicating one
  1233         -** or more term-less nodes, advance past it and set pIter->nEmpty to
  1234         -** the number of empty child nodes.
  1235         -*/
  1236         -static void fts5NodeIterGobbleNEmpty(Fts5NodeIter *pIter){
  1237         -  if( pIter->iOff<pIter->nData && 0==(pIter->aData[pIter->iOff] & 0xfe) ){
  1238         -    pIter->bDlidx = pIter->aData[pIter->iOff] & 0x01;
  1239         -    pIter->iOff++;
  1240         -    pIter->iOff += fts5GetVarint32(&pIter->aData[pIter->iOff], pIter->nEmpty);
  1241         -  }else{
  1242         -    pIter->nEmpty = 0;
  1243         -    pIter->bDlidx = 0;
  1244         -  }
  1245         -}
  1246         -
  1247         -/*
  1248         -** Advance to the next entry within the node.
  1249         -*/
  1250         -static void fts5NodeIterNext(int *pRc, Fts5NodeIter *pIter){
  1251         -  if( pIter->iOff>=pIter->nData ){
  1252         -    pIter->aData = 0;
  1253         -    pIter->iChild += pIter->nEmpty;
  1254         -  }else{
  1255         -    int nPre, nNew;
  1256         -    pIter->iOff += fts5GetVarint32(&pIter->aData[pIter->iOff], nPre);
  1257         -    pIter->iOff += fts5GetVarint32(&pIter->aData[pIter->iOff], nNew);
  1258         -    pIter->term.n = nPre-2;
  1259         -    fts5BufferAppendBlob(pRc, &pIter->term, nNew, pIter->aData+pIter->iOff);
  1260         -    pIter->iOff += nNew;
  1261         -    pIter->iChild += (1 + pIter->nEmpty);
  1262         -    fts5NodeIterGobbleNEmpty(pIter);
  1263         -    if( *pRc ) pIter->aData = 0;
  1264         -  }
  1265         -}
  1266         -
  1267         -
  1268         -/*
  1269         -** Initialize the iterator object pIter to iterate through the internal
  1270         -** segment node in pData.
  1271         -*/
  1272         -static void fts5NodeIterInit(const u8 *aData, int nData, Fts5NodeIter *pIter){
  1273         -  memset(pIter, 0, sizeof(*pIter));
  1274         -  pIter->aData = aData;
  1275         -  pIter->nData = nData;
  1276         -  pIter->iOff = fts5GetVarint32(aData, pIter->iChild);
  1277         -  fts5NodeIterGobbleNEmpty(pIter);
  1278         -}
  1279         -
  1280         -/*
  1281         -** Free any memory allocated by the iterator object.
  1282         -*/
  1283         -static void fts5NodeIterFree(Fts5NodeIter *pIter){
  1284         -  fts5BufferFree(&pIter->term);
  1285         -}
  1286         -
  1287   1168   /*
  1288   1169   ** Advance the iterator passed as the only argument. If the end of the 
  1289   1170   ** doclist-index page is reached, return non-zero.
  1290   1171   */
  1291   1172   static int fts5DlidxLvlNext(Fts5DlidxLvl *pLvl){
  1292   1173     Fts5Data *pData = pLvl->pData;
  1293   1174   
................................................................................
  1295   1176       assert( pLvl->bEof==0 );
  1296   1177       pLvl->iOff = 1;
  1297   1178       pLvl->iOff += fts5GetVarint32(&pData->p[1], pLvl->iLeafPgno);
  1298   1179       pLvl->iOff += fts5GetVarint(&pData->p[pLvl->iOff], (u64*)&pLvl->iRowid);
  1299   1180       pLvl->iFirstOff = pLvl->iOff;
  1300   1181     }else{
  1301   1182       int iOff;
  1302         -    for(iOff=pLvl->iOff; iOff<pData->n; iOff++){
         1183  +    for(iOff=pLvl->iOff; iOff<pData->nn; iOff++){
  1303   1184         if( pData->p[iOff] ) break; 
  1304   1185       }
  1305   1186   
  1306         -    if( iOff<pData->n ){
         1187  +    if( iOff<pData->nn ){
  1307   1188         i64 iVal;
  1308   1189         pLvl->iLeafPgno += (iOff - pLvl->iOff) + 1;
  1309   1190         iOff += fts5GetVarint(&pData->p[iOff], (u64*)&iVal);
  1310   1191         pLvl->iRowid += iVal;
  1311   1192         pLvl->iOff = iOff;
  1312   1193       }else{
  1313   1194         pLvl->bEof = 1;
................................................................................
  1530   1411   static i64 fts5DlidxIterRowid(Fts5DlidxIter *pIter){
  1531   1412     return pIter->aLvl[0].iRowid;
  1532   1413   }
  1533   1414   static int fts5DlidxIterPgno(Fts5DlidxIter *pIter){
  1534   1415     return pIter->aLvl[0].iLeafPgno;
  1535   1416   }
  1536   1417   
  1537         -static void fts5LeafHeader(Fts5Data *pLeaf, int *piRowid, int *piTerm){
  1538         -  *piRowid = (int)fts5GetU16(&pLeaf->p[0]);
  1539         -  *piTerm = (int)fts5GetU16(&pLeaf->p[2]);
  1540         -}
  1541         -
  1542   1418   /*
  1543   1419   ** Load the next leaf page into the segment iterator.
  1544   1420   */
  1545   1421   static void fts5SegIterNextPage(
  1546   1422     Fts5Index *p,                   /* FTS5 backend object */
  1547   1423     Fts5SegIter *pIter              /* Iterator to advance to next page */
  1548   1424   ){
         1425  +  Fts5Data *pLeaf;
  1549   1426     Fts5StructureSegment *pSeg = pIter->pSeg;
  1550   1427     fts5DataRelease(pIter->pLeaf);
  1551   1428     pIter->iLeafPgno++;
  1552   1429     if( pIter->pNextLeaf ){
  1553         -    assert( pIter->iLeafPgno<=pSeg->pgnoLast );
  1554   1430       pIter->pLeaf = pIter->pNextLeaf;
  1555   1431       pIter->pNextLeaf = 0;
  1556   1432     }else if( pIter->iLeafPgno<=pSeg->pgnoLast ){
  1557   1433       pIter->pLeaf = fts5DataRead(p, 
  1558         -        FTS5_SEGMENT_ROWID(pSeg->iSegid, 0, pIter->iLeafPgno)
         1434  +        FTS5_SEGMENT_ROWID(pSeg->iSegid, pIter->iLeafPgno)
  1559   1435       );
  1560   1436     }else{
  1561   1437       pIter->pLeaf = 0;
         1438  +  }
         1439  +  pLeaf = pIter->pLeaf;
         1440  +
         1441  +  if( pLeaf ){
         1442  +    pIter->iPgidxOff = pLeaf->szLeaf;
         1443  +    if( fts5LeafIsTermless(pLeaf) ){
         1444  +      pIter->iEndofDoclist = pLeaf->nn+1;
         1445  +    }else{
         1446  +      pIter->iPgidxOff += fts5GetVarint32(&pLeaf->p[pIter->iPgidxOff],
         1447  +          pIter->iEndofDoclist
         1448  +      );
         1449  +    }
  1562   1450     }
  1563   1451   }
  1564   1452   
  1565   1453   /*
  1566   1454   ** Argument p points to a buffer containing a varint to be interpreted as a
  1567   1455   ** position list size field. Read the varint and return the number of bytes
  1568   1456   ** read. Before returning, set *pnSz to the number of bytes in the position
................................................................................
  1587   1475   **
  1588   1476   ** Leave Fts5SegIter.iLeafOffset pointing to the first byte of the 
  1589   1477   ** position list content (if any).
  1590   1478   */
  1591   1479   static void fts5SegIterLoadNPos(Fts5Index *p, Fts5SegIter *pIter){
  1592   1480     if( p->rc==SQLITE_OK ){
  1593   1481       int iOff = pIter->iLeafOffset;  /* Offset to read at */
  1594         -    if( iOff>=pIter->pLeaf->n ){
         1482  +    ASSERT_SZLEAF_OK(pIter->pLeaf);
         1483  +    if( iOff>=pIter->pLeaf->szLeaf ){
  1595   1484         p->rc = FTS5_CORRUPT;
  1596   1485       }else{
  1597   1486         const u8 *a = &pIter->pLeaf->p[iOff];
  1598   1487         pIter->iLeafOffset += fts5GetPoslistSize(a, &pIter->nPos, &pIter->bDel);
  1599   1488       }
  1600   1489     }
  1601   1490   }
  1602   1491   
  1603   1492   static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){
  1604   1493     u8 *a = pIter->pLeaf->p;        /* Buffer to read data from */
  1605   1494     int iOff = pIter->iLeafOffset;
  1606   1495   
  1607         -  if( iOff>=pIter->pLeaf->n ){
         1496  +  ASSERT_SZLEAF_OK(pIter->pLeaf);
         1497  +  if( iOff>=pIter->pLeaf->szLeaf ){
  1608   1498       fts5SegIterNextPage(p, pIter);
  1609   1499       if( pIter->pLeaf==0 ){
  1610   1500         if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT;
  1611   1501         return;
  1612   1502       }
  1613   1503       iOff = 4;
  1614   1504       a = pIter->pLeaf->p;
................................................................................
  1640   1530     iOff += fts5GetVarint32(&a[iOff], nNew);
  1641   1531     pIter->term.n = nKeep;
  1642   1532     fts5BufferAppendBlob(&p->rc, &pIter->term, nNew, &a[iOff]);
  1643   1533     iOff += nNew;
  1644   1534     pIter->iTermLeafOffset = iOff;
  1645   1535     pIter->iTermLeafPgno = pIter->iLeafPgno;
  1646   1536     pIter->iLeafOffset = iOff;
         1537  +
         1538  +  if( pIter->iPgidxOff>=pIter->pLeaf->nn ){
         1539  +    pIter->iEndofDoclist = pIter->pLeaf->nn+1;
         1540  +  }else{
         1541  +    int nExtra;
         1542  +    pIter->iPgidxOff += fts5GetVarint32(&a[pIter->iPgidxOff], nExtra);
         1543  +    pIter->iEndofDoclist += nExtra;
         1544  +  }
  1647   1545   
  1648   1546     fts5SegIterLoadRowid(p, pIter);
  1649   1547   }
  1650   1548   
  1651   1549   /*
  1652   1550   ** Initialize the iterator object pIter to iterate through the entries in
  1653   1551   ** segment pSeg. The iterator is left pointing to the first entry when 
................................................................................
  1675   1573       memset(pIter, 0, sizeof(*pIter));
  1676   1574       pIter->pSeg = pSeg;
  1677   1575       pIter->iLeafPgno = pSeg->pgnoFirst-1;
  1678   1576       fts5SegIterNextPage(p, pIter);
  1679   1577     }
  1680   1578   
  1681   1579     if( p->rc==SQLITE_OK ){
  1682         -    u8 *a = pIter->pLeaf->p;
  1683         -    pIter->iLeafOffset = fts5GetU16(&a[2]);
         1580  +    pIter->iLeafOffset = 4;
         1581  +    assert_nc( pIter->pLeaf->nn>4 );
         1582  +    assert( fts5LeafFirstTermOff(pIter->pLeaf)==4 );
         1583  +    pIter->iPgidxOff = pIter->pLeaf->szLeaf+1;
  1684   1584       fts5SegIterLoadTerm(p, pIter, 0);
  1685   1585       fts5SegIterLoadNPos(p, pIter);
  1686   1586     }
  1687   1587   }
  1688   1588   
  1689   1589   /*
  1690   1590   ** This function is only ever called on iterators created by calls to
................................................................................
  1698   1598   ** This function advances the iterator so that it points to the last 
  1699   1599   ** relevant rowid on the page and, if necessary, initializes the 
  1700   1600   ** aRowidOffset[] and iRowidOffset variables. At this point the iterator
  1701   1601   ** is in its regular state - Fts5SegIter.iLeafOffset points to the first
  1702   1602   ** byte of the position list content associated with said rowid.
  1703   1603   */
  1704   1604   static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){
  1705         -  int n = pIter->pLeaf->n;
         1605  +  int n = pIter->pLeaf->szLeaf;
  1706   1606     int i = pIter->iLeafOffset;
  1707   1607     u8 *a = pIter->pLeaf->p;
  1708   1608     int iRowidOffset = 0;
  1709   1609   
         1610  +  if( n>pIter->iEndofDoclist ){
         1611  +    n = pIter->iEndofDoclist;
         1612  +  }
         1613  +
         1614  +  ASSERT_SZLEAF_OK(pIter->pLeaf);
  1710   1615     while( 1 ){
  1711   1616       i64 iDelta = 0;
  1712   1617       int nPos;
  1713   1618       int bDummy;
  1714   1619   
  1715   1620       i += fts5GetPoslistSize(&a[i], &nPos, &bDummy);
  1716   1621       i += nPos;
  1717   1622       if( i>=n ) break;
  1718   1623       i += fts5GetVarint(&a[i], (u64*)&iDelta);
  1719         -    if( iDelta==0 ) break;
  1720   1624       pIter->iRowid += iDelta;
  1721   1625   
  1722   1626       if( iRowidOffset>=pIter->nRowidOffset ){
  1723   1627         int nNew = pIter->nRowidOffset + 8;
  1724   1628         int *aNew = (int*)sqlite3_realloc(pIter->aRowidOffset, nNew*sizeof(int));
  1725   1629         if( aNew==0 ){
  1726   1630           p->rc = SQLITE_NOMEM;
................................................................................
  1746   1650   
  1747   1651     fts5DataRelease(pIter->pLeaf);
  1748   1652     pIter->pLeaf = 0;
  1749   1653     while( p->rc==SQLITE_OK && pIter->iLeafPgno>pIter->iTermLeafPgno ){
  1750   1654       Fts5Data *pNew;
  1751   1655       pIter->iLeafPgno--;
  1752   1656       pNew = fts5DataRead(p, FTS5_SEGMENT_ROWID(
  1753         -          pIter->pSeg->iSegid, 0, pIter->iLeafPgno
         1657  +          pIter->pSeg->iSegid, pIter->iLeafPgno
  1754   1658       ));
  1755   1659       if( pNew ){
  1756         -      if( pIter->iLeafPgno==pIter->iTermLeafPgno ){
  1757         -        if( pIter->iTermLeafOffset<pNew->n ){
  1758         -          pIter->pLeaf = pNew;
  1759         -          pIter->iLeafOffset = pIter->iTermLeafOffset;
  1760         -        }
         1660  +      /* iTermLeafOffset may be equal to szLeaf if the term is the last
         1661  +      ** thing on the page - i.e. the first rowid is on the following page.
         1662  +      ** In this case leaf pIter->pLeaf==0, this iterator is at EOF. */
         1663  +      if( pIter->iLeafPgno==pIter->iTermLeafPgno 
         1664  +       && pIter->iTermLeafOffset<pNew->szLeaf 
         1665  +      ){
         1666  +        pIter->pLeaf = pNew;
         1667  +        pIter->iLeafOffset = pIter->iTermLeafOffset;
  1761   1668         }else{
  1762         -        int iRowidOff, dummy;
  1763         -        fts5LeafHeader(pNew, &iRowidOff, &dummy);
         1669  +        int iRowidOff;
         1670  +        iRowidOff = fts5LeafFirstRowidOff(pNew);
  1764   1671           if( iRowidOff ){
  1765   1672             pIter->pLeaf = pNew;
  1766   1673             pIter->iLeafOffset = iRowidOff;
  1767   1674           }
  1768   1675         }
  1769   1676   
  1770   1677         if( pIter->pLeaf ){
................................................................................
  1774   1681         }else{
  1775   1682           fts5DataRelease(pNew);
  1776   1683         }
  1777   1684       }
  1778   1685     }
  1779   1686   
  1780   1687     if( pIter->pLeaf ){
         1688  +    pIter->iEndofDoclist = pIter->pLeaf->nn+1;
  1781   1689       fts5SegIterReverseInitPage(p, pIter);
  1782   1690     }
  1783   1691   }
  1784   1692   
  1785   1693   /*
  1786   1694   ** Return true if the iterator passed as the second argument currently
  1787   1695   ** points to a delete marker. A delete marker is an entry with a 0 byte
................................................................................
  1829   1737         Fts5Data *pLeaf = pIter->pLeaf;
  1830   1738         int iOff;
  1831   1739         int bNewTerm = 0;
  1832   1740         int nKeep = 0;
  1833   1741   
  1834   1742         /* Search for the end of the position list within the current page. */
  1835   1743         u8 *a = pLeaf->p;
  1836         -      int n = pLeaf->n;
         1744  +      int n = pLeaf->szLeaf;
  1837   1745   
         1746  +      ASSERT_SZLEAF_OK(pLeaf);
  1838   1747         iOff = pIter->iLeafOffset + pIter->nPos;
  1839   1748   
  1840   1749         if( iOff<n ){
  1841         -        /* The next entry is on the current page */
  1842         -        u64 iDelta;
  1843         -        iOff += sqlite3Fts5GetVarint(&a[iOff], &iDelta);
  1844         -        pIter->iLeafOffset = iOff;
  1845         -        if( iDelta==0 ){
         1750  +        /* The next entry is on the current page. */
         1751  +        assert_nc( iOff<=pIter->iEndofDoclist );
         1752  +        if( iOff>=pIter->iEndofDoclist ){
  1846   1753             bNewTerm = 1;
  1847         -          if( iOff>=n ){
  1848         -            fts5SegIterNextPage(p, pIter);
  1849         -            pIter->iLeafOffset = 4;
  1850         -          }else if( iOff!=fts5GetU16(&a[2]) ){
  1851         -            pIter->iLeafOffset += fts5GetVarint32(&a[iOff], nKeep);
         1754  +          if( iOff!=fts5LeafFirstTermOff(pLeaf) ){
         1755  +            iOff += fts5GetVarint32(&a[iOff], nKeep);
  1852   1756             }
  1853   1757           }else{
         1758  +          u64 iDelta;
         1759  +          iOff += sqlite3Fts5GetVarint(&a[iOff], &iDelta);
  1854   1760             pIter->iRowid += iDelta;
         1761  +          assert_nc( iDelta>0 );
  1855   1762           }
         1763  +        pIter->iLeafOffset = iOff;
         1764  +
  1856   1765         }else if( pIter->pSeg==0 ){
  1857   1766           const u8 *pList = 0;
  1858   1767           const char *zTerm = 0;
  1859   1768           int nList = 0;
  1860   1769           if( 0==(pIter->flags & FTS5_SEGITER_ONETERM) ){
  1861   1770             sqlite3Fts5HashScanNext(p->pHash);
  1862   1771             sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList);
  1863   1772           }
  1864   1773           if( pList==0 ){
  1865   1774             fts5DataRelease(pIter->pLeaf);
  1866   1775             pIter->pLeaf = 0;
  1867   1776           }else{
  1868   1777             pIter->pLeaf->p = (u8*)pList;
  1869         -          pIter->pLeaf->n = nList;
         1778  +          pIter->pLeaf->nn = nList;
         1779  +          pIter->pLeaf->szLeaf = nList;
         1780  +          pIter->iEndofDoclist = nList+1;
  1870   1781             sqlite3Fts5BufferSet(&p->rc, &pIter->term, strlen(zTerm), (u8*)zTerm);
  1871   1782             pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid);
  1872   1783           }
  1873   1784         }else{
  1874   1785           iOff = 0;
  1875   1786           /* Next entry is not on the current page */
  1876   1787           while( iOff==0 ){
  1877   1788             fts5SegIterNextPage(p, pIter);
  1878   1789             pLeaf = pIter->pLeaf;
  1879   1790             if( pLeaf==0 ) break;
  1880         -          if( (iOff = fts5GetU16(&pLeaf->p[0])) && iOff<pLeaf->n ){
         1791  +          ASSERT_SZLEAF_OK(pLeaf);
         1792  +          if( (iOff = fts5LeafFirstRowidOff(pLeaf)) && iOff<pLeaf->szLeaf ){
  1881   1793               iOff += sqlite3Fts5GetVarint(&pLeaf->p[iOff], (u64*)&pIter->iRowid);
  1882   1794               pIter->iLeafOffset = iOff;
         1795  +
         1796  +            if( pLeaf->nn>pLeaf->szLeaf ){
         1797  +              pIter->iPgidxOff = pLeaf->szLeaf + fts5GetVarint32(
         1798  +                  &pLeaf->p[pLeaf->szLeaf], pIter->iEndofDoclist
         1799  +              );
         1800  +            }
         1801  +
  1883   1802             }
  1884         -          else if( (iOff = fts5GetU16(&pLeaf->p[2])) ){
         1803  +          else if( pLeaf->nn>pLeaf->szLeaf ){
         1804  +            pIter->iPgidxOff = pLeaf->szLeaf + fts5GetVarint32(
         1805  +                &pLeaf->p[pLeaf->szLeaf], iOff
         1806  +            );
  1885   1807               pIter->iLeafOffset = iOff;
         1808  +            pIter->iEndofDoclist = iOff;
  1886   1809               bNewTerm = 1;
  1887   1810             }
  1888         -          if( iOff>=pLeaf->n ){
         1811  +          if( iOff>=pLeaf->szLeaf ){
  1889   1812               p->rc = FTS5_CORRUPT;
  1890   1813               return;
  1891   1814             }
  1892   1815           }
  1893   1816         }
  1894   1817   
  1895   1818         /* Check if the iterator is now at EOF. If so, return early. */
................................................................................
  1922   1845     Fts5DlidxIter *pDlidx = pIter->pDlidx;
  1923   1846     Fts5Data *pLast = 0;
  1924   1847     int pgnoLast = 0;
  1925   1848   
  1926   1849     if( pDlidx ){
  1927   1850       int iSegid = pIter->pSeg->iSegid;
  1928   1851       pgnoLast = fts5DlidxIterPgno(pDlidx);
  1929         -    pLast = fts5DataRead(p, FTS5_SEGMENT_ROWID(iSegid, 0, pgnoLast));
         1852  +    pLast = fts5DataRead(p, FTS5_SEGMENT_ROWID(iSegid, pgnoLast));
  1930   1853     }else{
  1931         -    int iOff;                               /* Byte offset within pLeaf */
  1932   1854       Fts5Data *pLeaf = pIter->pLeaf;         /* Current leaf data */
  1933   1855   
  1934         -    /* Currently, Fts5SegIter.iLeafOffset (and iOff) points to the first 
  1935         -    ** byte of position-list content for the current rowid. Back it up
  1936         -    ** so that it points to the start of the position-list size field. */
         1856  +    /* Currently, Fts5SegIter.iLeafOffset points to the first byte of
         1857  +    ** position-list content for the current rowid. Back it up so that it
         1858  +    ** points to the start of the position-list size field. */
  1937   1859       pIter->iLeafOffset -= sqlite3Fts5GetVarintLen(pIter->nPos*2+pIter->bDel);
  1938         -    iOff = pIter->iLeafOffset;
  1939         -    assert( iOff>=4 );
  1940         -
  1941         -    /* Search for a new term within the current leaf. If one can be found,
  1942         -    ** then this page contains the largest rowid for the current term. */
  1943         -    while( iOff<pLeaf->n ){
  1944         -      int nPos;
  1945         -      i64 iDelta;
  1946         -      int bDummy;
  1947         -
  1948         -      /* Read the position-list size field */
  1949         -      iOff += fts5GetPoslistSize(&pLeaf->p[iOff], &nPos, &bDummy);
  1950         -      iOff += nPos;
  1951         -      if( iOff>=pLeaf->n ) break;
  1952         -
  1953         -      /* Rowid delta. Or, if 0x00, the end of doclist marker. */
  1954         -      nPos = fts5GetVarint(&pLeaf->p[iOff], (u64*)&iDelta);
  1955         -      if( iDelta==0 ) break;
  1956         -      iOff += nPos;
  1957         -    }
  1958   1860   
  1959   1861       /* If this condition is true then the largest rowid for the current
  1960   1862       ** term may not be stored on the current page. So search forward to
  1961   1863       ** see where said rowid really is.  */
  1962         -    if( iOff>=pLeaf->n ){
         1864  +    if( pIter->iEndofDoclist>=pLeaf->szLeaf ){
  1963   1865         int pgno;
  1964   1866         Fts5StructureSegment *pSeg = pIter->pSeg;
  1965   1867   
  1966   1868         /* The last rowid in the doclist may not be on the current page. Search
  1967   1869         ** forward to find the page containing the last rowid.  */
  1968   1870         for(pgno=pIter->iLeafPgno+1; !p->rc && pgno<=pSeg->pgnoLast; pgno++){
  1969         -        i64 iAbs = FTS5_SEGMENT_ROWID(pSeg->iSegid, 0, pgno);
         1871  +        i64 iAbs = FTS5_SEGMENT_ROWID(pSeg->iSegid, pgno);
  1970   1872           Fts5Data *pNew = fts5DataRead(p, iAbs);
  1971   1873           if( pNew ){
  1972         -          int iRowid, iTerm;
  1973         -          fts5LeafHeader(pNew, &iRowid, &iTerm);
         1874  +          int iRowid, bTermless;
         1875  +          iRowid = fts5LeafFirstRowidOff(pNew);
         1876  +          bTermless = fts5LeafIsTermless(pNew);
  1974   1877             if( iRowid ){
  1975   1878               SWAPVAL(Fts5Data*, pNew, pLast);
  1976   1879               pgnoLast = pgno;
  1977   1880             }
  1978   1881             fts5DataRelease(pNew);
  1979         -          if( iTerm ) break;
         1882  +          if( bTermless==0 ) break;
  1980   1883           }
  1981   1884         }
  1982   1885       }
  1983   1886     }
  1984   1887   
  1985   1888     /* If pLast is NULL at this point, then the last rowid for this doclist
  1986   1889     ** lies on the page currently indicated by the iterator. In this case 
................................................................................
  1988   1891     ** field associated with the first relevant rowid on the page.
  1989   1892     **
  1990   1893     ** Or, if pLast is non-NULL, then it is the page that contains the last
  1991   1894     ** rowid. In this case configure the iterator so that it points to the
  1992   1895     ** first rowid on this page.
  1993   1896     */
  1994   1897     if( pLast ){
  1995         -    int dummy;
  1996   1898       int iOff;
  1997   1899       fts5DataRelease(pIter->pLeaf);
  1998   1900       pIter->pLeaf = pLast;
  1999   1901       pIter->iLeafPgno = pgnoLast;
  2000         -    fts5LeafHeader(pLast, &iOff, &dummy);
         1902  +    iOff = fts5LeafFirstRowidOff(pLast);
  2001   1903       iOff += fts5GetVarint(&pLast->p[iOff], (u64*)&pIter->iRowid);
  2002   1904       pIter->iLeafOffset = iOff;
         1905  +
         1906  +    if( fts5LeafIsTermless(pLast) ){
         1907  +      pIter->iEndofDoclist = pLast->nn+1;
         1908  +    }else{
         1909  +      pIter->iEndofDoclist = fts5LeafFirstTermOff(pLast);
         1910  +    }
         1911  +
  2003   1912     }
  2004   1913   
  2005   1914     fts5SegIterReverseInitPage(p, pIter);
  2006   1915   }
  2007   1916   
  2008   1917   /*
  2009   1918   ** Iterator pIter currently points to the first rowid of a doclist.
................................................................................
  2018   1927   
  2019   1928     assert( pIter->flags & FTS5_SEGITER_ONETERM );
  2020   1929     assert( pIter->pDlidx==0 );
  2021   1930   
  2022   1931     /* Check if the current doclist ends on this page. If it does, return
  2023   1932     ** early without loading the doclist-index (as it belongs to a different
  2024   1933     ** term. */
  2025         -  if( pIter->iTermLeafPgno==pIter->iLeafPgno ){
  2026         -    int iOff = pIter->iLeafOffset + pIter->nPos;
  2027         -    while( iOff<pLeaf->n ){
  2028         -      int bDummy;
  2029         -      int nPos;
  2030         -      i64 iDelta;
  2031         -
  2032         -      /* iOff is currently the offset of the start of position list data */
  2033         -      iOff += fts5GetVarint(&pLeaf->p[iOff], (u64*)&iDelta);
  2034         -      if( iDelta==0 ) return;
  2035         -      assert_nc( iOff<pLeaf->n );
  2036         -      iOff += fts5GetPoslistSize(&pLeaf->p[iOff], &nPos, &bDummy);
  2037         -      iOff += nPos;
  2038         -    }
         1934  +  if( pIter->iTermLeafPgno==pIter->iLeafPgno 
         1935  +   && pIter->iEndofDoclist<pLeaf->szLeaf 
         1936  +  ){
         1937  +    return;
  2039   1938     }
  2040   1939   
  2041   1940     pIter->pDlidx = fts5DlidxIterInit(p, bRev, iSeg, pIter->iTermLeafPgno);
  2042   1941   }
  2043   1942   
  2044         -#ifdef SQLITE_DEBUG
  2045         -static void fts5AssertNodeSeekOk(
  2046         -  Fts5Buffer *pNode,
  2047         -  const u8 *pTerm, int nTerm,     /* Term to search for */
  2048         -  int iExpectPg,
  2049         -  int bExpectDlidx
  2050         -){
  2051         -  int bDlidx;
  2052         -  int iPg;
  2053         -  int rc = SQLITE_OK;
  2054         -  Fts5NodeIter node;
  2055         -
  2056         -  fts5NodeIterInit(pNode->p, pNode->n, &node);
  2057         -  assert( node.term.n==0 );
  2058         -  iPg = node.iChild;
  2059         -  bDlidx = node.bDlidx;
  2060         -  for(fts5NodeIterNext(&rc, &node);
  2061         -      node.aData && fts5BufferCompareBlob(&node.term, pTerm, nTerm)<=0;
  2062         -      fts5NodeIterNext(&rc, &node)
  2063         -  ){
  2064         -    iPg = node.iChild;
  2065         -    bDlidx = node.bDlidx;
  2066         -  }
  2067         -  fts5NodeIterFree(&node);
  2068         -
  2069         -  assert( rc!=SQLITE_OK || iPg==iExpectPg );
  2070         -  assert( rc!=SQLITE_OK || bDlidx==bExpectDlidx );
  2071         -}
  2072         -#else
  2073         -#define fts5AssertNodeSeekOk(v,w,x,y,z)
  2074         -#endif
  2075         -
  2076         -/*
  2077         -** Argument pNode is an internal b-tree node. This function searches
  2078         -** within the node for the largest term that is smaller than or equal
  2079         -** to (pTerm/nTerm).
  2080         -**
  2081         -** It returns the associated page number. Or, if (pTerm/nTerm) is smaller
  2082         -** than all terms within the node, the leftmost child page number. 
  2083         -**
  2084         -** Before returning, (*pbDlidx) is set to true if the last term on the
  2085         -** returned child page number has a doclist-index. Or left as is otherwise.
  2086         -*/
  2087         -static int fts5NodeSeek(
  2088         -  Fts5Buffer *pNode,              /* Node to search */
  2089         -  const u8 *pTerm, int nTerm,     /* Term to search for */
  2090         -  int *pbDlidx                    /* OUT: True if dlidx flag is set */
  2091         -){
  2092         -  int iPg;
  2093         -  u8 *pPtr = pNode->p;
  2094         -  u8 *pEnd = &pPtr[pNode->n];
  2095         -  int nMatch = 0;                 /* Number of bytes of pTerm already matched */
  2096         -  
  2097         -  assert( *pbDlidx==0 );
  2098         -
  2099         -  pPtr += fts5GetVarint32(pPtr, iPg);
  2100         -  while( pPtr<pEnd ){
  2101         -    int nEmpty = 0;
  2102         -    int nKeep;
  2103         -    int nNew;
  2104         -
  2105         -    /* If there is a "no terms" record at pPtr, read it now. Store the
  2106         -    ** number of termless pages in nEmpty. If it indicates a doclist-index, 
  2107         -    ** set (*pbDlidx) to true.*/
  2108         -    if( *pPtr<2 ){
  2109         -      *pbDlidx = (*pPtr==0x01);
  2110         -      pPtr++;
  2111         -      pPtr += fts5GetVarint32(pPtr, nEmpty);
  2112         -      if( pPtr>=pEnd ) break;
  2113         -    }
  2114         -
  2115         -    /* Read the next "term" pointer. Set nKeep to the number of bytes to
  2116         -    ** keep from the previous term, and nNew to the number of bytes of
  2117         -    ** new data that will be appended to it. */
  2118         -    nKeep = (int)*pPtr++;
  2119         -    nNew = (int)*pPtr++;
  2120         -    if( (nKeep | nNew) & 0x0080 ){
  2121         -      pPtr -= 2;
  2122         -      pPtr += fts5GetVarint32(pPtr, nKeep);
  2123         -      pPtr += fts5GetVarint32(pPtr, nNew);
  2124         -    }
  2125         -    nKeep -= 2;
  2126         -
  2127         -    /* Compare (pTerm/nTerm) to the current term on the node (the one described
  2128         -    ** by nKeep/nNew). If the node term is larger, break out of the while()
  2129         -    ** loop. 
  2130         -    **
  2131         -    ** Otherwise, if (pTerm/nTerm) is larger or the two terms are equal, 
  2132         -    ** leave variable nMatch set to the size of the largest prefix common to
  2133         -    ** both terms in bytes.  */
  2134         -    if( nKeep==nMatch ){
  2135         -      int nTst = MIN(nNew, nTerm-nMatch);
  2136         -      int i;
  2137         -      for(i=0; i<nTst; i++){
  2138         -        if( pTerm[nKeep+i]!=pPtr[i] ) break;
  2139         -      }
  2140         -      nMatch += i;
  2141         -      assert( nMatch<=nTerm );
  2142         -
  2143         -      if( i<nNew && (nMatch==nTerm || pPtr[i] > pTerm[nMatch]) ) break;
  2144         -    }else if( nKeep<nMatch ){
  2145         -      break;
  2146         -    }
  2147         -
  2148         -    iPg += 1 + nEmpty;
  2149         -    *pbDlidx = 0;
  2150         -    pPtr += nNew;
  2151         -  }
  2152         -
  2153         -  fts5AssertNodeSeekOk(pNode, pTerm, nTerm, iPg, *pbDlidx);
  2154         -  return iPg;
  2155         -}
  2156         -
  2157   1943   #define fts5IndexGetVarint32(a, iOff, nVal) {     \
  2158         -  nVal = a[iOff++];                               \
         1944  +  nVal = (a)[iOff++];                             \
  2159   1945     if( nVal & 0x80 ){                              \
  2160   1946       iOff--;                                       \
  2161         -    iOff += fts5GetVarint32(&a[iOff], nVal);      \
         1947  +    iOff += fts5GetVarint32(&(a)[iOff], nVal);    \
  2162   1948     }                                               \
  2163   1949   }
  2164   1950   
  2165   1951   #define fts5IndexSkipVarint(a, iOff) {            \
  2166   1952     int iEnd = iOff+9;                              \
  2167   1953     while( (a[iOff++] & 0x80) && iOff<iEnd );       \
  2168   1954   }
................................................................................
  2185   1971     Fts5Index *p,                   /* Leave any error code here */
  2186   1972     int bGe,                        /* True for a >= search */
  2187   1973     Fts5SegIter *pIter,             /* Iterator to seek */
  2188   1974     const u8 *pTerm, int nTerm      /* Term to search for */
  2189   1975   ){
  2190   1976     int iOff;
  2191   1977     const u8 *a = pIter->pLeaf->p;
  2192         -  int n = pIter->pLeaf->n;
         1978  +  int szLeaf = pIter->pLeaf->szLeaf;
         1979  +  int n = pIter->pLeaf->nn;
  2193   1980   
  2194   1981     int nMatch = 0;
  2195   1982     int nKeep = 0;
  2196   1983     int nNew = 0;
         1984  +  int iTermOff;
         1985  +  int iPgidx;                     /* Current offset in pgidx */
         1986  +  int bEndOfPage = 0;
  2197   1987   
  2198   1988     assert( p->rc==SQLITE_OK );
  2199         -  assert( pIter->pLeaf );
  2200   1989   
  2201         -  iOff = fts5GetU16(&a[2]);
  2202         -  if( iOff<4 || iOff>=n ){
  2203         -    p->rc = FTS5_CORRUPT;
  2204         -    return;
  2205         -  }
         1990  +  iPgidx = szLeaf;
         1991  +  iPgidx += fts5GetVarint32(&a[iPgidx], iTermOff);
         1992  +  iOff = iTermOff;
  2206   1993   
  2207   1994     while( 1 ){
  2208         -    int i;
  2209         -    int nCmp;
  2210   1995   
  2211   1996       /* Figure out how many new bytes are in this term */
  2212   1997       fts5IndexGetVarint32(a, iOff, nNew);
  2213         -
  2214   1998       if( nKeep<nMatch ){
  2215   1999         goto search_failed;
  2216   2000       }
  2217   2001   
  2218   2002       assert( nKeep>=nMatch );
  2219   2003       if( nKeep==nMatch ){
         2004  +      int nCmp;
         2005  +      int i;
  2220   2006         nCmp = MIN(nNew, nTerm-nMatch);
  2221   2007         for(i=0; i<nCmp; i++){
  2222   2008           if( a[iOff+i]!=pTerm[nMatch+i] ) break;
  2223   2009         }
  2224   2010         nMatch += i;
  2225   2011   
  2226   2012         if( nTerm==nMatch ){
................................................................................
  2229   2015           }else{
  2230   2016             goto search_failed;
  2231   2017           }
  2232   2018         }else if( i<nNew && a[iOff+i]>pTerm[nMatch] ){
  2233   2019           goto search_failed;
  2234   2020         }
  2235   2021       }
  2236         -    iOff += nNew;
  2237   2022   
  2238         -    /* Skip past the doclist. If the end of the page is reached, bail out. */
  2239         -    while( 1 ){
  2240         -      int nPos;
         2023  +    if( iPgidx>=n ){
         2024  +      bEndOfPage = 1;
         2025  +      break;
         2026  +    }
  2241   2027   
  2242         -      /* Skip past rowid delta */
  2243         -      fts5IndexSkipVarint(a, iOff);
  2244         -
  2245         -      /* Skip past position list */
  2246         -      fts5IndexGetVarint32(a, iOff, nPos);
  2247         -      iOff += (nPos >> 1);
  2248         -      if( iOff>=(n-1) ){
  2249         -        iOff = n;
  2250         -        goto search_failed;
  2251         -      }
  2252         -
  2253         -      /* If this is the end of the doclist, break out of the loop */
  2254         -      if( a[iOff]==0x00 ){
  2255         -        iOff++;
  2256         -        break;
  2257         -      }
  2258         -    };
         2028  +    iPgidx += fts5GetVarint32(&a[iPgidx], nKeep);
         2029  +    iTermOff += nKeep;
         2030  +    iOff = iTermOff;
  2259   2031   
  2260   2032       /* Read the nKeep field of the next term. */
  2261   2033       fts5IndexGetVarint32(a, iOff, nKeep);
  2262   2034     }
  2263   2035   
  2264   2036    search_failed:
  2265   2037     if( bGe==0 ){
  2266   2038       fts5DataRelease(pIter->pLeaf);
  2267   2039       pIter->pLeaf = 0;
  2268   2040       return;
  2269         -  }else if( iOff>=n ){
         2041  +  }else if( bEndOfPage ){
  2270   2042       do {
  2271   2043         fts5SegIterNextPage(p, pIter);
  2272   2044         if( pIter->pLeaf==0 ) return;
  2273   2045         a = pIter->pLeaf->p;
  2274         -      iOff = fts5GetU16(&a[2]);
  2275         -      if( iOff ){
  2276         -        if( iOff<4 || iOff>=n ){
         2046  +      if( fts5LeafIsTermless(pIter->pLeaf)==0 ){
         2047  +        fts5GetVarint32(&pIter->pLeaf->p[pIter->pLeaf->szLeaf], iOff);
         2048  +        if( iOff<4 || iOff>=pIter->pLeaf->szLeaf ){
  2277   2049             p->rc = FTS5_CORRUPT;
  2278   2050           }else{
  2279   2051             nKeep = 0;
  2280   2052             iOff += fts5GetVarint32(&a[iOff], nNew);
  2281   2053             break;
  2282   2054           }
  2283   2055         }
  2284   2056       }while( 1 );
  2285   2057     }
  2286   2058   
  2287   2059    search_success:
         2060  +
  2288   2061     pIter->iLeafOffset = iOff + nNew;
  2289   2062     pIter->iTermLeafOffset = pIter->iLeafOffset;
  2290   2063     pIter->iTermLeafPgno = pIter->iLeafPgno;
  2291   2064   
  2292   2065     fts5BufferSet(&p->rc, &pIter->term, nKeep, pTerm);
  2293   2066     fts5BufferAppendBlob(&p->rc, &pIter->term, nNew, &a[iOff]);
  2294   2067   
         2068  +  if( iPgidx>=n ){
         2069  +    pIter->iEndofDoclist = pIter->pLeaf->nn+1;
         2070  +  }else{
         2071  +    int nExtra;
         2072  +    iPgidx += fts5GetVarint32(&a[iPgidx], nExtra);
         2073  +    pIter->iEndofDoclist = iTermOff + nExtra;
         2074  +  }
         2075  +  pIter->iPgidxOff = iPgidx;
         2076  +
  2295   2077     fts5SegIterLoadRowid(p, pIter);
  2296   2078     fts5SegIterLoadNPos(p, pIter);
  2297   2079   }
  2298   2080   
  2299   2081   /*
  2300   2082   ** Initialize the object pIter to point to term pTerm/nTerm within segment
  2301   2083   ** pSeg. If there is no such term in the index, the iterator is set to EOF.
................................................................................
  2420   2202   
  2421   2203     if( pList ){
  2422   2204       Fts5Data *pLeaf;
  2423   2205       sqlite3Fts5BufferSet(&p->rc, &pIter->term, n, z);
  2424   2206       pLeaf = fts5IdxMalloc(p, sizeof(Fts5Data));
  2425   2207       if( pLeaf==0 ) return;
  2426   2208       pLeaf->p = (u8*)pList;
  2427         -    pLeaf->n = nList;
         2209  +    pLeaf->nn = pLeaf->szLeaf = nList;
  2428   2210       pIter->pLeaf = pLeaf;
  2429   2211       pIter->iLeafOffset = fts5GetVarint(pLeaf->p, (u64*)&pIter->iRowid);
         2212  +    pIter->iEndofDoclist = pLeaf->nn+1;
  2430   2213   
  2431   2214       if( flags & FTS5INDEX_QUERY_DESC ){
  2432   2215         pIter->flags |= FTS5_SEGITER_REVERSE;
  2433   2216         fts5SegIterReverseInitPage(p, pIter);
  2434   2217       }else{
  2435   2218         fts5SegIterLoadNPos(p, pIter);
  2436   2219       }
................................................................................
  2613   2396       pIter->iLeafPgno = iLeafPgno-1;
  2614   2397       fts5SegIterNextPage(p, pIter);
  2615   2398       assert( p->rc!=SQLITE_OK || pIter->iLeafPgno==iLeafPgno );
  2616   2399   
  2617   2400       if( p->rc==SQLITE_OK ){
  2618   2401         int iOff;
  2619   2402         u8 *a = pIter->pLeaf->p;
  2620         -      int n = pIter->pLeaf->n;
         2403  +      int n = pIter->pLeaf->szLeaf;
  2621   2404   
  2622         -      iOff = fts5GetU16(&a[0]);
         2405  +      iOff = fts5LeafFirstRowidOff(pIter->pLeaf);
  2623   2406         if( iOff<4 || iOff>=n ){
  2624   2407           p->rc = FTS5_CORRUPT;
  2625   2408         }else{
  2626   2409           iOff += fts5GetVarint(&a[iOff], (u64*)&pIter->iRowid);
  2627   2410           pIter->iLeafOffset = iOff;
  2628   2411           fts5SegIterLoadNPos(p, pIter);
  2629   2412         }
................................................................................
  2673   2456       if( iLeafPgno<pIter->iLeafPgno ){
  2674   2457         pIter->iLeafPgno = iLeafPgno+1;
  2675   2458         fts5SegIterReverseNewPage(p, pIter);
  2676   2459         bMove = 0;
  2677   2460       }
  2678   2461     }
  2679   2462   
  2680         -  while( p->rc==SQLITE_OK ){
         2463  +  do{
  2681   2464       if( bMove ) fts5SegIterNext(p, pIter, 0);
  2682   2465       if( pIter->pLeaf==0 ) break;
  2683   2466       if( bRev==0 && pIter->iRowid>=iMatch ) break;
  2684   2467       if( bRev!=0 && pIter->iRowid<=iMatch ) break;
  2685   2468       bMove = 1;
  2686         -  }
         2469  +  }while( p->rc==SQLITE_OK );
  2687   2470   }
  2688   2471   
  2689   2472   
  2690   2473   /*
  2691   2474   ** Free the iterator object passed as the second argument.
  2692   2475   */
  2693   2476   static void fts5MultiIterFree(Fts5Index *p, Fts5IndexIter *pIter){
................................................................................
  2947   2730   ){
  2948   2731     Fts5IndexIter *pNew;
  2949   2732     pNew = fts5MultiIterAlloc(p, 2);
  2950   2733     if( pNew ){
  2951   2734       Fts5SegIter *pIter = &pNew->aSeg[1];
  2952   2735   
  2953   2736       pIter->flags = FTS5_SEGITER_ONETERM;
  2954         -    if( pData->n>0 ){
         2737  +    if( pData->szLeaf>0 ){
  2955   2738         pIter->pLeaf = pData;
  2956   2739         pIter->iLeafOffset = fts5GetVarint(pData->p, (u64*)&pIter->iRowid);
         2740  +      pIter->iEndofDoclist = pData->nn;
  2957   2741         pNew->aFirst[1].iFirst = 1;
  2958   2742         if( bDesc ){
  2959   2743           pNew->bRev = 1;
  2960   2744           pIter->flags |= FTS5_SEGITER_REVERSE;
  2961   2745           fts5SegIterReverseInitPage(p, pIter);
  2962   2746         }else{
  2963   2747           fts5SegIterLoadNPos(p, pIter);
................................................................................
  3027   2811     Fts5SegIter *pSeg,              /* Poslist of this iterator */
  3028   2812     void *pCtx,                     /* Context pointer for xChunk callback */
  3029   2813     void (*xChunk)(Fts5Index*, void*, const u8*, int)
  3030   2814   ){
  3031   2815     int nRem = pSeg->nPos;          /* Number of bytes still to come */
  3032   2816     Fts5Data *pData = 0;
  3033   2817     u8 *pChunk = &pSeg->pLeaf->p[pSeg->iLeafOffset];
  3034         -  int nChunk = MIN(nRem, pSeg->pLeaf->n - pSeg->iLeafOffset);
         2818  +  int nChunk = MIN(nRem, pSeg->pLeaf->szLeaf - pSeg->iLeafOffset);
  3035   2819     int pgno = pSeg->iLeafPgno;
  3036   2820     int pgnoSave = 0;
  3037   2821   
  3038   2822     if( (pSeg->flags & FTS5_SEGITER_REVERSE)==0 ){
  3039   2823       pgnoSave = pgno+1;
  3040   2824     }
  3041   2825   
................................................................................
  3043   2827       xChunk(p, pCtx, pChunk, nChunk);
  3044   2828       nRem -= nChunk;
  3045   2829       fts5DataRelease(pData);
  3046   2830       if( nRem<=0 ){
  3047   2831         break;
  3048   2832       }else{
  3049   2833         pgno++;
  3050         -      pData = fts5DataRead(p, FTS5_SEGMENT_ROWID(pSeg->pSeg->iSegid, 0, pgno));
         2834  +      pData = fts5DataRead(p, FTS5_SEGMENT_ROWID(pSeg->pSeg->iSegid, pgno));
  3051   2835         if( pData==0 ) break;
  3052   2836         pChunk = &pData->p[4];
  3053         -      nChunk = MIN(nRem, pData->n - 4);
         2837  +      nChunk = MIN(nRem, pData->szLeaf - 4);
  3054   2838         if( pgno==pgnoSave ){
  3055   2839           assert( pSeg->pNextLeaf==0 );
  3056   2840           pSeg->pNextLeaf = pData;
  3057   2841           pData = 0;
  3058   2842         }
  3059   2843       }
  3060   2844     }
................................................................................
  3332   3116   }
  3333   3117   
  3334   3118   static void fts5WriteFlushLeaf(Fts5Index *p, Fts5SegWriter *pWriter){
  3335   3119     static const u8 zero[] = { 0x00, 0x00, 0x00, 0x00 };
  3336   3120     Fts5PageWriter *pPage = &pWriter->writer;
  3337   3121     i64 iRowid;
  3338   3122   
         3123  +  assert( (pPage->pgidx.n==0)==(pWriter->bFirstTermInPage) );
         3124  +
         3125  +  /* Set the szLeaf header field. */
         3126  +  assert( 0==fts5GetU16(&pPage->buf.p[2]) );
         3127  +  fts5PutU16(&pPage->buf.p[2], pPage->buf.n);
         3128  +
  3339   3129     if( pWriter->bFirstTermInPage ){
  3340   3130       /* No term was written to this page. */
  3341         -    assert( 0==fts5GetU16(&pPage->buf.p[2]) );
         3131  +    assert( pPage->pgidx.n==0 );
  3342   3132       fts5WriteBtreeNoTerm(p, pWriter);
         3133  +  }else{
         3134  +    /* Append the pgidx to the page buffer. Set the szLeaf header field. */
         3135  +    fts5BufferAppendBlob(&p->rc, &pPage->buf, pPage->pgidx.n, pPage->pgidx.p);
  3343   3136     }
  3344   3137   
  3345         -  /* Write the current page to the db. */
  3346         -  iRowid = FTS5_SEGMENT_ROWID(pWriter->iSegid, 0, pPage->pgno);
         3138  +  /* Write the page out to disk */
         3139  +  iRowid = FTS5_SEGMENT_ROWID(pWriter->iSegid, pPage->pgno);
  3347   3140     fts5DataWrite(p, iRowid, pPage->buf.p, pPage->buf.n);
  3348   3141   
  3349   3142     /* Initialize the next page. */
  3350   3143     fts5BufferZero(&pPage->buf);
         3144  +  fts5BufferZero(&pPage->pgidx);
  3351   3145     fts5BufferAppendBlob(&p->rc, &pPage->buf, 4, zero);
         3146  +  pPage->iPrevPgidx = 0;
  3352   3147     pPage->pgno++;
  3353   3148   
  3354   3149     /* Increase the leaves written counter */
  3355   3150     pWriter->nLeafWritten++;
  3356   3151   
  3357   3152     /* The new leaf holds no terms or rowids */
  3358   3153     pWriter->bFirstTermInPage = 1;
................................................................................
  3369   3164   static void fts5WriteAppendTerm(
  3370   3165     Fts5Index *p, 
  3371   3166     Fts5SegWriter *pWriter,
  3372   3167     int nTerm, const u8 *pTerm 
  3373   3168   ){
  3374   3169     int nPrefix;                    /* Bytes of prefix compression for term */
  3375   3170     Fts5PageWriter *pPage = &pWriter->writer;
         3171  +  Fts5Buffer *pPgidx = &pWriter->writer.pgidx;
  3376   3172   
  3377         -  assert( pPage->buf.n==0 || pPage->buf.n>4 );
  3378         -  if( pPage->buf.n==0 ){
  3379         -    /* Zero the first term and first rowid fields */
  3380         -    static const u8 zero[] = { 0x00, 0x00, 0x00, 0x00 };
  3381         -    fts5BufferAppendBlob(&p->rc, &pPage->buf, 4, zero);
  3382         -    assert( pWriter->bFirstTermInPage );
         3173  +  assert( p->rc==SQLITE_OK );
         3174  +  assert( pPage->buf.n>=4 );
         3175  +  assert( pPage->buf.n>4 || pWriter->bFirstTermInPage );
         3176  +
         3177  +  /* If the current leaf page is full, flush it to disk. */
         3178  +  if( (pPage->buf.n + pPgidx->n + nTerm + 2)>=p->pConfig->pgsz ){
         3179  +    if( pPage->buf.n>4 ){
         3180  +      fts5WriteFlushLeaf(p, pWriter);
         3181  +    }
         3182  +    fts5BufferGrow(&p->rc, &pPage->buf, nTerm+FTS5_DATA_PADDING);
  3383   3183     }
  3384         -  if( p->rc ) return;
  3385   3184     
         3185  +  /* TODO1: Updating pgidx here. */
         3186  +  pPgidx->n += sqlite3Fts5PutVarint(
         3187  +      &pPgidx->p[pPgidx->n], pPage->buf.n - pPage->iPrevPgidx
         3188  +  );
         3189  +  pPage->iPrevPgidx = pPage->buf.n;
         3190  +#if 0
         3191  +  fts5PutU16(&pPgidx->p[pPgidx->n], pPage->buf.n);
         3192  +  pPgidx->n += 2;
         3193  +#endif
         3194  +
  3386   3195     if( pWriter->bFirstTermInPage ){
  3387         -    /* Update the "first term" field of the page header. */
  3388         -    assert( pPage->buf.p[2]==0 && pPage->buf.p[3]==0 );
  3389         -    fts5PutU16(&pPage->buf.p[2], pPage->buf.n);
  3390   3196       nPrefix = 0;
  3391   3197       if( pPage->pgno!=1 ){
  3392   3198         /* This is the first term on a leaf that is not the leftmost leaf in
  3393   3199         ** the segment b-tree. In this case it is necessary to add a term to
  3394   3200         ** the b-tree hierarchy that is (a) larger than the largest term 
  3395   3201         ** already written to the segment and (b) smaller than or equal to
  3396   3202         ** this term. In other words, a prefix of (pTerm/nTerm) that is one
................................................................................
  3424   3230     pWriter->bFirstTermInPage = 0;
  3425   3231   
  3426   3232     pWriter->bFirstRowidInPage = 0;
  3427   3233     pWriter->bFirstRowidInDoclist = 1;
  3428   3234   
  3429   3235     assert( p->rc || (pWriter->nDlidx>0 && pWriter->aDlidx[0].buf.n==0) );
  3430   3236     pWriter->aDlidx[0].pgno = pPage->pgno;
  3431         -
  3432         -  /* If the current leaf page is full, flush it to disk. */
  3433         -  if( pPage->buf.n>=p->pConfig->pgsz ){
  3434         -    fts5WriteFlushLeaf(p, pWriter);
  3435         -  }
  3436   3237   }
  3437   3238   
  3438   3239   /*
  3439   3240   ** Append a rowid and position-list size field to the writers output. 
  3440   3241   */
  3441   3242   static void fts5WriteAppendRowid(
  3442   3243     Fts5Index *p, 
  3443   3244     Fts5SegWriter *pWriter,
  3444   3245     i64 iRowid,
  3445   3246     int nPos
  3446   3247   ){
  3447   3248     if( p->rc==SQLITE_OK ){
  3448   3249       Fts5PageWriter *pPage = &pWriter->writer;
         3250  +
         3251  +    if( (pPage->buf.n + pPage->pgidx.n)>=p->pConfig->pgsz ){
         3252  +      fts5WriteFlushLeaf(p, pWriter);
         3253  +    }
  3449   3254   
  3450   3255       /* If this is to be the first rowid written to the page, set the 
  3451   3256       ** rowid-pointer in the page-header. Also append a value to the dlidx
  3452   3257       ** buffer, in case a doclist-index is required.  */
  3453   3258       if( pWriter->bFirstRowidInPage ){
  3454   3259         fts5PutU16(pPage->buf.p, pPage->buf.n);
  3455   3260         fts5WriteDlidxAppend(p, pWriter, iRowid);
................................................................................
  3463   3268         fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid - pWriter->iPrevRowid);
  3464   3269       }
  3465   3270       pWriter->iPrevRowid = iRowid;
  3466   3271       pWriter->bFirstRowidInDoclist = 0;
  3467   3272       pWriter->bFirstRowidInPage = 0;
  3468   3273   
  3469   3274       fts5BufferAppendVarint(&p->rc, &pPage->buf, nPos);
  3470         -
  3471         -    if( pPage->buf.n>=p->pConfig->pgsz ){
  3472         -      fts5WriteFlushLeaf(p, pWriter);
  3473         -    }
  3474   3275     }
  3475   3276   }
  3476   3277   
  3477   3278   static void fts5WriteAppendPoslistData(
  3478   3279     Fts5Index *p, 
  3479   3280     Fts5SegWriter *pWriter, 
  3480   3281     const u8 *aData, 
................................................................................
  3481   3282     int nData
  3482   3283   ){
  3483   3284     Fts5PageWriter *pPage = &pWriter->writer;
  3484   3285     const u8 *a = aData;
  3485   3286     int n = nData;
  3486   3287     
  3487   3288     assert( p->pConfig->pgsz>0 );
  3488         -  while( p->rc==SQLITE_OK && (pPage->buf.n + n)>=p->pConfig->pgsz ){
  3489         -    int nReq = p->pConfig->pgsz - pPage->buf.n;
         3289  +  while( p->rc==SQLITE_OK 
         3290  +     && (pPage->buf.n + pPage->pgidx.n + n)>=p->pConfig->pgsz 
         3291  +  ){
         3292  +    int nReq = p->pConfig->pgsz - pPage->buf.n - pPage->pgidx.n;
  3490   3293       int nCopy = 0;
  3491   3294       while( nCopy<nReq ){
  3492   3295         i64 dummy;
  3493   3296         nCopy += fts5GetVarint(&a[nCopy], (u64*)&dummy);
  3494   3297       }
  3495   3298       fts5BufferAppendBlob(&p->rc, &pPage->buf, nCopy, a);
  3496   3299       a += nCopy;
................................................................................
  3498   3301       fts5WriteFlushLeaf(p, pWriter);
  3499   3302     }
  3500   3303     if( n>0 ){
  3501   3304       fts5BufferAppendBlob(&p->rc, &pPage->buf, n, a);
  3502   3305     }
  3503   3306   }
  3504   3307   
  3505         -static void fts5WriteAppendZerobyte(Fts5Index *p, Fts5SegWriter *pWriter){
  3506         -  fts5BufferAppendVarint(&p->rc, &pWriter->writer.buf, 0);
  3507         -}
  3508         -
  3509   3308   /*
  3510   3309   ** Flush any data cached by the writer object to the database. Free any
  3511   3310   ** allocations associated with the writer.
  3512   3311   */
  3513   3312   static void fts5WriteFinish(
  3514   3313     Fts5Index *p, 
  3515   3314     Fts5SegWriter *pWriter,         /* Writer object */
  3516         -  int *pnHeight,                  /* OUT: Height of the b-tree */
  3517   3315     int *pnLeaf                     /* OUT: Number of leaf pages in b-tree */
  3518   3316   ){
  3519   3317     int i;
  3520   3318     Fts5PageWriter *pLeaf = &pWriter->writer;
  3521   3319     if( p->rc==SQLITE_OK ){
  3522         -    if( pLeaf->pgno==1 && pLeaf->buf.n==0 ){
  3523         -      *pnLeaf = 0;
  3524         -      *pnHeight = 0;
  3525         -    }else{
  3526         -      if( pLeaf->buf.n>4 ){
  3527         -        fts5WriteFlushLeaf(p, pWriter);
  3528         -      }
  3529         -      *pnLeaf = pLeaf->pgno-1;
  3530         -
  3531         -      fts5WriteFlushBtree(p, pWriter);
  3532         -      *pnHeight = 0;
         3320  +    assert( pLeaf->pgno>=1 );
         3321  +    if( pLeaf->buf.n>4 ){
         3322  +      fts5WriteFlushLeaf(p, pWriter);
  3533   3323       }
         3324  +    *pnLeaf = pLeaf->pgno-1;
         3325  +    fts5WriteFlushBtree(p, pWriter);
  3534   3326     }
  3535   3327     fts5BufferFree(&pLeaf->term);
  3536   3328     fts5BufferFree(&pLeaf->buf);
         3329  +  fts5BufferFree(&pLeaf->pgidx);
  3537   3330     fts5BufferFree(&pWriter->btterm);
  3538   3331   
  3539   3332     for(i=0; i<pWriter->nDlidx; i++){
  3540   3333       sqlite3Fts5BufferFree(&pWriter->aDlidx[i].buf);
  3541   3334     }
  3542   3335     sqlite3_free(pWriter->aDlidx);
  3543   3336   }
  3544   3337   
  3545   3338   static void fts5WriteInit(
  3546   3339     Fts5Index *p, 
  3547   3340     Fts5SegWriter *pWriter, 
  3548   3341     int iSegid
  3549   3342   ){
         3343  +  const int nBuffer = p->pConfig->pgsz + FTS5_DATA_PADDING;
         3344  +
  3550   3345     memset(pWriter, 0, sizeof(Fts5SegWriter));
  3551   3346     pWriter->iSegid = iSegid;
  3552   3347   
  3553   3348     fts5WriteDlidxGrow(p, pWriter, 1);
  3554   3349     pWriter->writer.pgno = 1;
  3555   3350     pWriter->bFirstTermInPage = 1;
  3556   3351     pWriter->iBtPage = 1;
         3352  +
         3353  +  /* Grow the two buffers to pgsz + padding bytes in size. */
         3354  +  fts5BufferGrow(&p->rc, &pWriter->writer.pgidx, nBuffer);
         3355  +  fts5BufferGrow(&p->rc, &pWriter->writer.buf, nBuffer);
  3557   3356   
  3558   3357     if( p->pIdxWriter==0 ){
  3559   3358       Fts5Config *pConfig = p->pConfig;
  3560   3359       fts5IndexPrepareStmt(p, &p->pIdxWriter, sqlite3_mprintf(
  3561   3360             "INSERT INTO '%q'.'%q_idx'(segid,term,pgno) VALUES(?,?,?)", 
  3562   3361             pConfig->zDb, pConfig->zName
  3563   3362       ));
  3564   3363     }
  3565   3364   
  3566   3365     if( p->rc==SQLITE_OK ){
         3366  +    /* Initialize the 4-byte leaf-page header to 0x00. */
         3367  +    memset(pWriter->writer.buf.p, 0, 4);
         3368  +    pWriter->writer.buf.n = 4;
         3369  +
         3370  +    /* Bind the current output segment id to the index-writer. This is an
         3371  +    ** optimization over binding the same value over and over as rows are
         3372  +    ** inserted into %_idx by the current writer.  */
  3567   3373       sqlite3_bind_int(p->pIdxWriter, 1, pWriter->iSegid);
  3568   3374     }
  3569   3375   }
  3570   3376   
  3571   3377   /*
  3572   3378   ** Iterator pIter was used to iterate through the input segments of on an
  3573   3379   ** incremental merge operation. This function is called if the incremental
................................................................................
  3588   3394         pSeg->pSeg->pgnoLast = 0;
  3589   3395         pSeg->pSeg->pgnoFirst = 0;
  3590   3396       }else{
  3591   3397         int iOff = pSeg->iTermLeafOffset;     /* Offset on new first leaf page */
  3592   3398         i64 iLeafRowid;
  3593   3399         Fts5Data *pData;
  3594   3400         int iId = pSeg->pSeg->iSegid;
  3595         -      u8 aHdr[4] = {0x00, 0x00, 0x00, 0x04};
         3401  +      u8 aHdr[4] = {0x00, 0x00, 0x00, 0x00};
  3596   3402   
  3597         -      iLeafRowid = FTS5_SEGMENT_ROWID(iId, 0, pSeg->iTermLeafPgno);
         3403  +      iLeafRowid = FTS5_SEGMENT_ROWID(iId, pSeg->iTermLeafPgno);
  3598   3404         pData = fts5DataRead(p, iLeafRowid);
  3599   3405         if( pData ){
  3600   3406           fts5BufferZero(&buf);
         3407  +        fts5BufferGrow(&p->rc, &buf, pData->nn);
  3601   3408           fts5BufferAppendBlob(&p->rc, &buf, sizeof(aHdr), aHdr);
  3602   3409           fts5BufferAppendVarint(&p->rc, &buf, pSeg->term.n);
  3603   3410           fts5BufferAppendBlob(&p->rc, &buf, pSeg->term.n, pSeg->term.p);
  3604         -        fts5BufferAppendBlob(&p->rc, &buf, pData->n - iOff, &pData->p[iOff]);
         3411  +        fts5BufferAppendBlob(&p->rc, &buf, pData->szLeaf-iOff, &pData->p[iOff]);
         3412  +        if( p->rc==SQLITE_OK ){
         3413  +          /* Set the szLeaf field */
         3414  +          fts5PutU16(&buf.p[2], buf.n);
         3415  +        }
         3416  +
         3417  +        /* Set up the new page-index array */
         3418  +        fts5BufferAppendVarint(&p->rc, &buf, 4);
         3419  +        if( pSeg->iLeafPgno==pSeg->iTermLeafPgno 
         3420  +         && pSeg->iEndofDoclist<pData->szLeaf 
         3421  +        ){
         3422  +          int nDiff = pData->szLeaf - pSeg->iEndofDoclist;
         3423  +          fts5BufferAppendVarint(&p->rc, &buf, buf.n - 1 - nDiff - 4);
         3424  +          fts5BufferAppendBlob(&p->rc, &buf, 
         3425  +              pData->nn - pSeg->iPgidxOff, &pData->p[pSeg->iPgidxOff]
         3426  +          );
         3427  +        }
         3428  +
  3605   3429           fts5DataRelease(pData);
  3606   3430           pSeg->pSeg->pgnoFirst = pSeg->iTermLeafPgno;
  3607         -        fts5DataDelete(p, FTS5_SEGMENT_ROWID(iId, 0, 1), iLeafRowid);
         3431  +        fts5DataDelete(p, FTS5_SEGMENT_ROWID(iId, 1), iLeafRowid);
  3608   3432           fts5DataWrite(p, iLeafRowid, buf.p, buf.n);
  3609   3433         }
  3610   3434       }
  3611   3435     }
  3612   3436     fts5BufferFree(&buf);
  3613   3437   }
  3614   3438   
................................................................................
  3635   3459     Fts5StructureLevel *pLvlOut;
  3636   3460     Fts5IndexIter *pIter = 0;       /* Iterator to read input data */
  3637   3461     int nRem = pnRem ? *pnRem : 0;  /* Output leaf pages left to write */
  3638   3462     int nInput;                     /* Number of input segments */
  3639   3463     Fts5SegWriter writer;           /* Writer object */
  3640   3464     Fts5StructureSegment *pSeg;     /* Output segment */
  3641   3465     Fts5Buffer term;
  3642         -  int bRequireDoclistTerm = 0;    /* Doclist terminator (0x00) required */
  3643   3466     int bOldest;                    /* True if the output segment is the oldest */
  3644   3467   
  3645   3468     assert( iLvl<pStruct->nLevel );
  3646   3469     assert( pLvl->nMerge<=pLvl->nSeg );
  3647   3470   
  3648   3471     memset(&writer, 0, sizeof(Fts5SegWriter));
  3649   3472     memset(&term, 0, sizeof(Fts5Buffer));
................................................................................
  3700   3523       pTerm = fts5MultiIterTerm(pIter, &nTerm);
  3701   3524       if( nTerm!=term.n || memcmp(pTerm, term.p, nTerm) ){
  3702   3525         if( pnRem && writer.nLeafWritten>nRem ){
  3703   3526           break;
  3704   3527         }
  3705   3528   
  3706   3529         /* This is a new term. Append a term to the output segment. */
  3707         -      if( bRequireDoclistTerm ){
  3708         -        fts5WriteAppendZerobyte(p, &writer);
  3709         -      }
  3710   3530         fts5WriteAppendTerm(p, &writer, nTerm, pTerm);
  3711   3531         fts5BufferSet(&p->rc, &term, nTerm, pTerm);
  3712         -      bRequireDoclistTerm = 1;
  3713   3532       }
  3714   3533   
  3715   3534       /* Append the rowid to the output */
  3716   3535       /* WRITEPOSLISTSIZE */
  3717   3536       nPos = pSegIter->nPos*2 + pSegIter->bDel;
  3718   3537       fts5WriteAppendRowid(p, &writer, fts5MultiIterRowid(pIter), nPos);
  3719   3538   
  3720   3539       /* Append the position-list data to the output */
  3721   3540       fts5ChunkIterate(p, pSegIter, (void*)&writer, fts5MergeChunkCallback);
  3722   3541     }
  3723   3542   
  3724   3543     /* Flush the last leaf page to disk. Set the output segment b-tree height
  3725   3544     ** and last leaf page number at the same time.  */
  3726         -  fts5WriteFinish(p, &writer, &pSeg->nHeight, &pSeg->pgnoLast);
         3545  +  fts5WriteFinish(p, &writer, &pSeg->pgnoLast);
  3727   3546   
  3728   3547     if( fts5MultiIterEof(p, pIter) ){
  3729   3548       int i;
  3730   3549   
  3731   3550       /* Remove the redundant segments from the %_data table */
  3732   3551       for(i=0; i<nInput; i++){
  3733   3552         fts5DataRemoveSegment(p, pLvl->aSeg[i].iSegid);
................................................................................
  3844   3663     const int nCrisis = p->pConfig->nCrisisMerge;
  3845   3664     Fts5Structure *pStruct = *ppStruct;
  3846   3665     int iLvl = 0;
  3847   3666   
  3848   3667     assert( p->rc!=SQLITE_OK || pStruct->nLevel>0 );
  3849   3668     while( p->rc==SQLITE_OK && pStruct->aLevel[iLvl].nSeg>=nCrisis ){
  3850   3669       fts5IndexMergeLevel(p, &pStruct, iLvl, 0);
         3670  +    assert( p->rc!=SQLITE_OK || pStruct->nLevel>(iLvl+1) );
  3851   3671       fts5StructurePromote(p, iLvl+1, pStruct);
  3852   3672       iLvl++;
  3853   3673     }
  3854   3674     *ppStruct = pStruct;
  3855   3675   }
  3856   3676   
  3857   3677   static int fts5IndexReturn(Fts5Index *p){
................................................................................
  3871   3691   ** in a 32-bit integer. Return the size of the largest prefix of this 
  3872   3692   ** list nMax bytes or less in size.
  3873   3693   */
  3874   3694   static int fts5PoslistPrefix(const u8 *aBuf, int nMax){
  3875   3695     int ret;
  3876   3696     u32 dummy;
  3877   3697     ret = fts5GetVarint32(aBuf, dummy);
  3878         -  while( 1 ){
  3879         -    int i = fts5GetVarint32(&aBuf[ret], dummy);
  3880         -    if( (ret + i) > nMax ) break;
  3881         -    ret += i;
         3698  +  if( ret<nMax ){
         3699  +    while( 1 ){
         3700  +      int i = fts5GetVarint32(&aBuf[ret], dummy);
         3701  +      if( (ret + i) > nMax ) break;
         3702  +      ret += i;
         3703  +    }
  3882   3704     }
  3883   3705     return ret;
  3884   3706   }
  3885   3707   
  3886   3708   #define fts5BufferSafeAppendBlob(pBuf, pBlob, nBlob) { \
  3887   3709     assert( pBuf->nSpace>=(pBuf->n+nBlob) );             \
  3888   3710     memcpy(&pBuf->p[pBuf->n], pBlob, nBlob);             \
................................................................................
  3907   3729     pStruct = fts5StructureRead(p);
  3908   3730     iSegid = fts5AllocateSegid(p, pStruct);
  3909   3731   
  3910   3732     if( iSegid ){
  3911   3733       const int pgsz = p->pConfig->pgsz;
  3912   3734   
  3913   3735       Fts5StructureSegment *pSeg;   /* New segment within pStruct */
  3914         -    int nHeight;                  /* Height of new segment b-tree */
  3915   3736       Fts5Buffer *pBuf;             /* Buffer in which to assemble leaf page */
  3916         -    const u8 *zPrev = 0;
         3737  +    Fts5Buffer *pPgidx;           /* Buffer in which to assemble pgidx */
  3917   3738   
  3918   3739       Fts5SegWriter writer;
  3919   3740       fts5WriteInit(p, &writer, iSegid);
  3920   3741   
  3921         -    /* Pre-allocate the buffer used to assemble leaf pages to the target
  3922         -    ** page size.  */
  3923         -    assert( pgsz>0 );
  3924   3742       pBuf = &writer.writer.buf;
  3925         -    fts5BufferGrow(&p->rc, pBuf, pgsz + 20);
         3743  +    pPgidx = &writer.writer.pgidx;
         3744  +
         3745  +    /* fts5WriteInit() should have initialized the buffers to (most likely)
         3746  +    ** the maximum space required. */
         3747  +    assert( p->rc || pBuf->nSpace>=(pgsz + FTS5_DATA_PADDING) );
         3748  +    assert( p->rc || pPgidx->nSpace>=(pgsz + FTS5_DATA_PADDING) );
  3926   3749   
  3927   3750       /* Begin scanning through hash table entries. This loop runs once for each
  3928   3751       ** term/doclist currently stored within the hash table. */
  3929   3752       if( p->rc==SQLITE_OK ){
  3930         -      memset(pBuf->p, 0, 4);
  3931         -      pBuf->n = 4;
  3932   3753         p->rc = sqlite3Fts5HashScanInit(pHash, 0, 0);
  3933   3754       }
  3934   3755       while( p->rc==SQLITE_OK && 0==sqlite3Fts5HashScanEof(pHash) ){
  3935   3756         const char *zTerm;          /* Buffer containing term */
  3936         -      int nTerm;                  /* Size of zTerm in bytes */
  3937   3757         const u8 *pDoclist;         /* Pointer to doclist for this term */
  3938   3758         int nDoclist;               /* Size of doclist in bytes */
  3939         -      int nSuffix;                /* Size of term suffix */
  3940   3759   
         3760  +      /* Write the term for this entry to disk. */
  3941   3761         sqlite3Fts5HashScanEntry(pHash, &zTerm, &pDoclist, &nDoclist);
  3942         -      nTerm = strlen(zTerm);
         3762  +      fts5WriteAppendTerm(p, &writer, strlen(zTerm), (const u8*)zTerm);
  3943   3763   
  3944         -      /* Decide if the term will fit on the current leaf. If it will not, 
  3945         -      ** flush the leaf to disk here.  */
  3946         -      if( pBuf->n>4 && (pBuf->n + nTerm + 2) > pgsz ){
  3947         -        fts5WriteFlushLeaf(p, &writer);
  3948         -        pBuf = &writer.writer.buf;
  3949         -        if( (nTerm + 32) > pBuf->nSpace ){
  3950         -          fts5BufferGrow(&p->rc, pBuf, nTerm + 32 - pBuf->n);
  3951         -          if( p->rc ) break;
  3952         -        }
  3953         -      }
  3954         -
  3955         -      /* Write the term to the leaf. And if it is the first on the leaf, and
  3956         -      ** the leaf is not page number 1, push it up into the b-tree hierarchy 
  3957         -      ** as well.  */
  3958         -      if( writer.bFirstTermInPage==0 ){
  3959         -        int nPre = fts5PrefixCompress(nTerm, zPrev, nTerm, (const u8*)zTerm);
  3960         -        pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], nPre);
  3961         -        nSuffix = nTerm - nPre;
  3962         -      }else{
  3963         -        fts5PutU16(&pBuf->p[2], pBuf->n);
  3964         -        writer.bFirstTermInPage = 0;
  3965         -        if( writer.writer.pgno!=1 ){
  3966         -          int nPre = fts5PrefixCompress(nTerm, zPrev, nTerm, (const u8*)zTerm);
  3967         -          fts5WriteBtreeTerm(p, &writer, nPre+1, (const u8*)zTerm);
  3968         -          pBuf = &writer.writer.buf;
  3969         -          assert( nPre<nTerm );
  3970         -        }
  3971         -        nSuffix = nTerm;
  3972         -      }
  3973         -      pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], nSuffix);
  3974         -      fts5BufferSafeAppendBlob(pBuf, (const u8*)&zTerm[nTerm-nSuffix], nSuffix);
  3975         -
  3976         -      /* We just wrote a term into page writer.aWriter[0].pgno. If a 
  3977         -      ** doclist-index is to be generated for this doclist, it will be
  3978         -      ** associated with this page. */
  3979         -      assert( writer.nDlidx>0 && writer.aDlidx[0].buf.n==0 );
  3980         -      writer.aDlidx[0].pgno = writer.writer.pgno;
  3981         -
  3982         -      if( pgsz>=(pBuf->n + nDoclist + 1) ){
         3764  +      assert( writer.bFirstRowidInPage==0 );
         3765  +      if( pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){
  3983   3766           /* The entire doclist will fit on the current leaf. */
  3984   3767           fts5BufferSafeAppendBlob(pBuf, pDoclist, nDoclist);
  3985   3768         }else{
  3986   3769           i64 iRowid = 0;
  3987   3770           i64 iDelta = 0;
  3988   3771           int iOff = 0;
  3989   3772   
  3990         -        writer.bFirstRowidInPage = 0;
  3991         -
  3992   3773           /* The entire doclist will not fit on this leaf. The following 
  3993   3774           ** loop iterates through the poslists that make up the current 
  3994   3775           ** doclist.  */
  3995   3776           while( p->rc==SQLITE_OK && iOff<nDoclist ){
  3996   3777             int nPos;
  3997   3778             int nCopy;
  3998   3779             int bDummy;
................................................................................
  4007   3788               writer.bFirstRowidInPage = 0;
  4008   3789               fts5WriteDlidxAppend(p, &writer, iRowid);
  4009   3790             }else{
  4010   3791               pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iDelta);
  4011   3792             }
  4012   3793             assert( pBuf->n<=pBuf->nSpace );
  4013   3794   
  4014         -          if( (pBuf->n + nCopy) <= pgsz ){
         3795  +          if( (pBuf->n + pPgidx->n + nCopy) <= pgsz ){
  4015   3796               /* The entire poslist will fit on the current leaf. So copy
  4016   3797               ** it in one go. */
  4017   3798               fts5BufferSafeAppendBlob(pBuf, &pDoclist[iOff], nCopy);
  4018   3799             }else{
  4019   3800               /* The entire poslist will not fit on this leaf. So it needs
  4020   3801               ** to be broken into sections. The only qualification being
  4021   3802               ** that each varint must be stored contiguously.  */
  4022   3803               const u8 *pPoslist = &pDoclist[iOff];
  4023   3804               int iPos = 0;
  4024   3805               while( p->rc==SQLITE_OK ){
  4025         -              int nSpace = pgsz - pBuf->n;
         3806  +              int nSpace = pgsz - pBuf->n - pPgidx->n;
  4026   3807                 int n = 0;
  4027   3808                 if( (nCopy - iPos)<=nSpace ){
  4028   3809                   n = nCopy - iPos;
  4029   3810                 }else{
  4030   3811                   n = fts5PoslistPrefix(&pPoslist[iPos], nSpace);
  4031   3812                 }
  4032   3813                 assert( n>0 );
  4033   3814                 fts5BufferSafeAppendBlob(pBuf, &pPoslist[iPos], n);
  4034   3815                 iPos += n;
  4035         -              if( pBuf->n>=pgsz ){
         3816  +              if( (pBuf->n + pPgidx->n)>=pgsz ){
  4036   3817                   fts5WriteFlushLeaf(p, &writer);
  4037         -                pBuf = &writer.writer.buf;
  4038   3818                 }
  4039   3819                 if( iPos>=nCopy ) break;
  4040   3820               }
  4041   3821             }
  4042   3822             iOff += nCopy;
  4043   3823           }
  4044   3824         }
  4045   3825   
  4046         -      pBuf->p[pBuf->n++] = '\0';
         3826  +      /* TODO2: Doclist terminator written here. */
         3827  +      /* pBuf->p[pBuf->n++] = '\0'; */
  4047   3828         assert( pBuf->n<=pBuf->nSpace );
  4048         -      zPrev = (const u8*)zTerm;
  4049   3829         sqlite3Fts5HashScanNext(pHash);
  4050   3830       }
  4051   3831       sqlite3Fts5HashClear(pHash);
  4052         -    fts5WriteFinish(p, &writer, &nHeight, &pgnoLast);
         3832  +    fts5WriteFinish(p, &writer, &pgnoLast);
  4053   3833   
  4054   3834       /* Update the Fts5Structure. It is written back to the database by the
  4055   3835       ** fts5StructureRelease() call below.  */
  4056   3836       if( pStruct->nLevel==0 ){
  4057   3837         fts5StructureAddLevel(&p->rc, &pStruct);
  4058   3838       }
  4059   3839       fts5StructureExtendLevel(&p->rc, pStruct, 0, 1, 0);
  4060   3840       if( p->rc==SQLITE_OK ){
  4061   3841         pSeg = &pStruct->aLevel[0].aSeg[ pStruct->aLevel[0].nSeg++ ];
  4062   3842         pSeg->iSegid = iSegid;
  4063         -      pSeg->nHeight = nHeight;
  4064   3843         pSeg->pgnoFirst = 1;
  4065   3844         pSeg->pgnoLast = pgnoLast;
  4066   3845         pStruct->nSegment++;
  4067   3846       }
  4068   3847       fts5StructurePromote(p, 0, pStruct);
  4069   3848     }
  4070   3849   
................................................................................
  4158   3937   }
  4159   3938   
  4160   3939   static void fts5PoslistCallback(
  4161   3940     Fts5Index *p, 
  4162   3941     void *pCtx, 
  4163   3942     const u8 *pChunk, int nChunk
  4164   3943   ){
  4165         -  fts5BufferAppendBlob(&p->rc, (Fts5Buffer*)pCtx, nChunk, pChunk);
         3944  +  assert_nc( nChunk>=0 );
         3945  +  if( nChunk>0 ){
         3946  +    fts5BufferAppendBlob(&p->rc, (Fts5Buffer*)pCtx, nChunk, pChunk);
         3947  +  }
  4166   3948   }
  4167   3949   
  4168   3950   /*
  4169   3951   ** Iterator pIter currently points to a valid entry (not EOF). This
  4170   3952   ** function appends the position list data for the current entry to
  4171   3953   ** buffer pBuf. It does not make a copy of the position-list size
  4172   3954   ** field.
................................................................................
  4364   4146       for(fts5MultiIterNew(p, pStruct, 1, flags, pToken, nToken, -1, 0, &p1);
  4365   4147           fts5MultiIterEof(p, p1)==0;
  4366   4148           fts5MultiIterNext(p, p1, 0, 0)
  4367   4149       ){
  4368   4150         i64 iRowid = fts5MultiIterRowid(p1);
  4369   4151         int nTerm;
  4370   4152         const u8 *pTerm = fts5MultiIterTerm(p1, &nTerm);
  4371         -      assert( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 );
         4153  +      assert_nc( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 );
  4372   4154         if( nTerm<nToken || memcmp(pToken, pTerm, nToken) ) break;
  4373   4155   
  4374   4156         if( doclist.n>0 && iRowid<=iLastRowid ){
  4375   4157           for(i=0; p->rc==SQLITE_OK && doclist.n; i++){
  4376   4158             assert( i<nBuf );
  4377   4159             if( aBuf[i].n==0 ){
  4378   4160               fts5BufferSwap(&doclist, &aBuf[i]);
................................................................................
  4393   4175         fts5BufferFree(&aBuf[i]);
  4394   4176       }
  4395   4177       fts5MultiIterFree(p, p1);
  4396   4178   
  4397   4179       pData = fts5IdxMalloc(p, sizeof(Fts5Data) + doclist.n);
  4398   4180       if( pData ){
  4399   4181         pData->p = (u8*)&pData[1];
  4400         -      pData->n = doclist.n;
         4182  +      pData->nn = pData->szLeaf = doclist.n;
  4401   4183         memcpy(pData->p, doclist.p, doclist.n);
  4402   4184         fts5MultiIterNew2(p, pData, bDesc, ppIter);
  4403   4185       }
  4404   4186       fts5BufferFree(&doclist);
  4405   4187     }
  4406   4188   
  4407   4189     fts5StructureRelease(pStruct);
................................................................................
  4455   4237   /*
  4456   4238   ** The %_data table is completely empty when this function is called. This
  4457   4239   ** function populates it with the initial structure objects for each index,
  4458   4240   ** and the initial version of the "averages" record (a zero-byte blob).
  4459   4241   */
  4460   4242   int sqlite3Fts5IndexReinit(Fts5Index *p){
  4461   4243     Fts5Structure s;
  4462         -
  4463         -  assert( p->rc==SQLITE_OK );
  4464         -  p->rc = sqlite3Fts5IndexSetAverages(p, (const u8*)"", 0);
  4465         -
  4466   4244     memset(&s, 0, sizeof(Fts5Structure));
         4245  +  fts5DataWrite(p, FTS5_AVERAGES_ROWID, (const u8*)"", 0);
  4467   4246     fts5StructureWrite(p, &s);
  4468         -
  4469   4247     return fts5IndexReturn(p);
  4470   4248   }
  4471   4249   
  4472   4250   /*
  4473   4251   ** Open a new Fts5Index handle. If the bCreate argument is true, create
  4474   4252   ** and initialize the underlying %_data table.
  4475   4253   **
................................................................................
  4627   4405          || (flags & FTS5INDEX_QUERY_SCAN)==FTS5INDEX_QUERY_SCAN
  4628   4406     );
  4629   4407   
  4630   4408     if( sqlite3Fts5BufferGrow(&p->rc, &buf, nToken+1)==0 ){
  4631   4409       memcpy(&buf.p[1], pToken, nToken);
  4632   4410   
  4633   4411   #ifdef SQLITE_DEBUG
  4634         -    if( flags & FTS5INDEX_QUERY_TEST_NOIDX ){
         4412  +    /* If the QUERY_TEST_NOIDX flag was specified, then this must be a
         4413  +    ** prefix-query. Instead of using a prefix-index (if one exists), 
         4414  +    ** evaluate the prefix query using the main FTS index. This is used
         4415  +    ** for internal sanity checking by the integrity-check in debug 
         4416  +    ** mode only.  */
         4417  +    if( pConfig->bPrefixIndex==0 || (flags & FTS5INDEX_QUERY_TEST_NOIDX) ){
  4635   4418         assert( flags & FTS5INDEX_QUERY_PREFIX );
  4636   4419         iIdx = 1+pConfig->nPrefix;
  4637   4420       }else
  4638   4421   #endif
  4639   4422       if( flags & FTS5INDEX_QUERY_PREFIX ){
  4640   4423         int nChar = fts5IndexCharlen(pToken, nToken);
  4641   4424         for(iIdx=1; iIdx<=pConfig->nPrefix; iIdx++){
................................................................................
  4747   4530     int *pn,                        /* OUT: Size of position-list in bytes */
  4748   4531     i64 *piRowid                    /* OUT: Current rowid */
  4749   4532   ){
  4750   4533     Fts5SegIter *pSeg = &pIter->aSeg[ pIter->aFirst[1].iFirst ];
  4751   4534     assert( pIter->pIndex->rc==SQLITE_OK );
  4752   4535     *piRowid = pSeg->iRowid;
  4753   4536     *pn = pSeg->nPos;
  4754         -  if( pSeg->iLeafOffset+pSeg->nPos <= pSeg->pLeaf->n ){
         4537  +  if( pSeg->iLeafOffset+pSeg->nPos <= pSeg->pLeaf->szLeaf ){
  4755   4538       *pp = &pSeg->pLeaf->p[pSeg->iLeafOffset];
  4756   4539     }else{
  4757   4540       fts5BufferZero(&pIter->poslist);
  4758   4541       fts5SegiterPoslist(pIter->pIndex, pSeg, &pIter->poslist);
  4759   4542       *pp = pIter->poslist.p;
  4760   4543     }
  4761   4544     return fts5IndexReturn(pIter->pIndex);
................................................................................
  4783   4566       Fts5Index *pIndex = pIter->pIndex;
  4784   4567       fts5MultiIterFree(pIter->pIndex, pIter);
  4785   4568       fts5CloseReader(pIndex);
  4786   4569     }
  4787   4570   }
  4788   4571   
  4789   4572   /*
  4790         -** Read the "averages" record into the buffer supplied as the second 
  4791         -** argument. Return SQLITE_OK if successful, or an SQLite error code
  4792         -** if an error occurs.
         4573  +** Read and decode the "averages" record from the database. 
         4574  +**
         4575  +** Parameter anSize must point to an array of size nCol, where nCol is
         4576  +** the number of user defined columns in the FTS table.
  4793   4577   */
  4794         -int sqlite3Fts5IndexGetAverages(Fts5Index *p, Fts5Buffer *pBuf){
  4795         -  assert( p->rc==SQLITE_OK );
  4796         -  fts5DataReadOrBuffer(p, pBuf, FTS5_AVERAGES_ROWID);
         4578  +int sqlite3Fts5IndexGetAverages(Fts5Index *p, i64 *pnRow, i64 *anSize){
         4579  +  int nCol = p->pConfig->nCol;
         4580  +  Fts5Data *pData;
         4581  +
         4582  +  *pnRow = 0;
         4583  +  memset(anSize, 0, sizeof(i64) * nCol);
         4584  +  pData = fts5DataRead(p, FTS5_AVERAGES_ROWID);
         4585  +  if( p->rc==SQLITE_OK && pData->nn ){
         4586  +    int i = 0;
         4587  +    int iCol;
         4588  +    i += fts5GetVarint(&pData->p[i], (u64*)pnRow);
         4589  +    for(iCol=0; i<pData->nn && iCol<nCol; iCol++){
         4590  +      i += fts5GetVarint(&pData->p[i], (u64*)&anSize[iCol]);
         4591  +    }
         4592  +  }
         4593  +
         4594  +  fts5DataRelease(pData);
  4797   4595     return fts5IndexReturn(p);
  4798   4596   }
  4799   4597   
  4800   4598   /*
  4801   4599   ** Replace the current "averages" record with the contents of the buffer 
  4802   4600   ** supplied as the second argument.
  4803   4601   */
................................................................................
  4989   4787       if( rc==SQLITE_OK ){
  4990   4788         int f = flags|FTS5INDEX_QUERY_DESC;
  4991   4789         rc = fts5QueryCksum(p, iIdx, zTerm, nTerm, f, &ck2);
  4992   4790       }
  4993   4791       if( rc==SQLITE_OK && ck1!=ck2 ) rc = FTS5_CORRUPT;
  4994   4792   
  4995   4793       /* If this is a prefix query, check that the results returned if the
  4996         -    ** the index is disabled are the same. In both ASC and DESC order. */
  4997         -    if( iIdx>0 && rc==SQLITE_OK ){
  4998         -      int f = flags|FTS5INDEX_QUERY_TEST_NOIDX;
  4999         -      ck2 = 0;
  5000         -      rc = fts5QueryCksum(p, iIdx, zTerm, nTerm, f, &ck2);
  5001         -      if( rc==SQLITE_OK && ck1!=ck2 ) rc = FTS5_CORRUPT;
  5002         -    }
  5003         -    if( iIdx>0 && rc==SQLITE_OK ){
  5004         -      int f = flags|FTS5INDEX_QUERY_TEST_NOIDX|FTS5INDEX_QUERY_DESC;
  5005         -      ck2 = 0;
  5006         -      rc = fts5QueryCksum(p, iIdx, zTerm, nTerm, f, &ck2);
  5007         -      if( rc==SQLITE_OK && ck1!=ck2 ) rc = FTS5_CORRUPT;
         4794  +    ** the index is disabled are the same. In both ASC and DESC order. 
         4795  +    **
         4796  +    ** This check may only be performed if the hash table is empty. This
         4797  +    ** is because the hash table only supports a single scan query at
         4798  +    ** a time, and the multi-iter loop from which this function is called
         4799  +    ** is already performing such a scan. */
         4800  +    if( p->nPendingData==0 ){
         4801  +      if( iIdx>0 && rc==SQLITE_OK ){
         4802  +        int f = flags|FTS5INDEX_QUERY_TEST_NOIDX;
         4803  +        ck2 = 0;
         4804  +        rc = fts5QueryCksum(p, iIdx, zTerm, nTerm, f, &ck2);
         4805  +        if( rc==SQLITE_OK && ck1!=ck2 ) rc = FTS5_CORRUPT;
         4806  +      }
         4807  +      if( iIdx>0 && rc==SQLITE_OK ){
         4808  +        int f = flags|FTS5INDEX_QUERY_TEST_NOIDX|FTS5INDEX_QUERY_DESC;
         4809  +        ck2 = 0;
         4810  +        rc = fts5QueryCksum(p, iIdx, zTerm, nTerm, f, &ck2);
         4811  +        if( rc==SQLITE_OK && ck1!=ck2 ) rc = FTS5_CORRUPT;
         4812  +      }
  5008   4813       }
  5009   4814   
  5010   4815       cksum3 ^= ck1;
  5011   4816       fts5BufferSet(&rc, pPrev, n, (const u8*)z);
  5012   4817   
  5013   4818       if( rc==SQLITE_OK && cksum3!=expected ){
  5014   4819         rc = FTS5_CORRUPT;
................................................................................
  5039   4844     int iLast
  5040   4845   ){
  5041   4846     int i;
  5042   4847   
  5043   4848     /* Now check that the iter.nEmpty leaves following the current leaf
  5044   4849     ** (a) exist and (b) contain no terms. */
  5045   4850     for(i=iFirst; p->rc==SQLITE_OK && i<=iLast; i++){
  5046         -    Fts5Data *pLeaf = fts5DataRead(p, FTS5_SEGMENT_ROWID(pSeg->iSegid, 0, i));
         4851  +    Fts5Data *pLeaf = fts5DataRead(p, FTS5_SEGMENT_ROWID(pSeg->iSegid, i));
  5047   4852       if( pLeaf ){
  5048         -      if( 0!=fts5GetU16(&pLeaf->p[2]) ) p->rc = FTS5_CORRUPT;
  5049         -      if( i>=iNoRowid && 0!=fts5GetU16(&pLeaf->p[0]) ) p->rc = FTS5_CORRUPT;
         4853  +      if( !fts5LeafIsTermless(pLeaf) ) p->rc = FTS5_CORRUPT;
         4854  +      if( i>=iNoRowid && 0!=fts5LeafFirstRowidOff(pLeaf) ) p->rc = FTS5_CORRUPT;
  5050   4855       }
  5051   4856       fts5DataRelease(pLeaf);
  5052         -    if( p->rc ) break;
  5053   4857     }
  5054   4858   }
         4859  +
         4860  +static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){
         4861  +  int iTermOff = 0;
         4862  +  int ii;
         4863  +
         4864  +  Fts5Buffer buf1 = {0,0,0};
         4865  +  Fts5Buffer buf2 = {0,0,0};
         4866  +
         4867  +  ii = pLeaf->szLeaf;
         4868  +  while( ii<pLeaf->nn && p->rc==SQLITE_OK ){
         4869  +    int res;
         4870  +    int iOff;
         4871  +    int nIncr;
         4872  +
         4873  +    ii += fts5GetVarint32(&pLeaf->p[ii], nIncr);
         4874  +    iTermOff += nIncr;
         4875  +    iOff = iTermOff;
         4876  +
         4877  +    if( iOff>=pLeaf->szLeaf ){
         4878  +      p->rc = FTS5_CORRUPT;
         4879  +    }else if( iTermOff==nIncr ){
         4880  +      int nByte;
         4881  +      iOff += fts5GetVarint32(&pLeaf->p[iOff], nByte);
         4882  +      if( (iOff+nByte)>pLeaf->szLeaf ){
         4883  +        p->rc = FTS5_CORRUPT;
         4884  +      }else{
         4885  +        fts5BufferSet(&p->rc, &buf1, nByte, &pLeaf->p[iOff]);
         4886  +      }
         4887  +    }else{
         4888  +      int nKeep, nByte;
         4889  +      iOff += fts5GetVarint32(&pLeaf->p[iOff], nKeep);
         4890  +      iOff += fts5GetVarint32(&pLeaf->p[iOff], nByte);
         4891  +      if( nKeep>buf1.n || (iOff+nByte)>pLeaf->szLeaf ){
         4892  +        p->rc = FTS5_CORRUPT;
         4893  +      }else{
         4894  +        buf1.n = nKeep;
         4895  +        fts5BufferAppendBlob(&p->rc, &buf1, nByte, &pLeaf->p[iOff]);
         4896  +      }
         4897  +
         4898  +      if( p->rc==SQLITE_OK ){
         4899  +        res = fts5BufferCompare(&buf1, &buf2);
         4900  +        if( res<=0 ) p->rc = FTS5_CORRUPT;
         4901  +      }
         4902  +    }
         4903  +    fts5BufferSet(&p->rc, &buf2, buf1.n, buf1.p);
         4904  +  }
         4905  +
         4906  +  fts5BufferFree(&buf1);
         4907  +  fts5BufferFree(&buf2);
         4908  +}
  5055   4909   
  5056   4910   static void fts5IndexIntegrityCheckSegment(
  5057   4911     Fts5Index *p,                   /* FTS5 backend object */
  5058   4912     Fts5StructureSegment *pSeg      /* Segment to check internal consistency */
  5059   4913   ){
  5060   4914     Fts5Config *pConfig = p->pConfig;
  5061   4915     sqlite3_stmt *pStmt = 0;
................................................................................
  5062   4916     int rc2;
  5063   4917     int iIdxPrevLeaf = pSeg->pgnoFirst-1;
  5064   4918     int iDlidxPrevLeaf = pSeg->pgnoLast;
  5065   4919   
  5066   4920     if( pSeg->pgnoFirst==0 ) return;
  5067   4921   
  5068   4922     fts5IndexPrepareStmt(p, &pStmt, sqlite3_mprintf(
  5069         -      "SELECT segid, term, (pgno>>1), (pgno & 1) FROM '%q'.'%q_idx' WHERE segid=%d",
         4923  +      "SELECT segid, term, (pgno>>1), (pgno&1) FROM %Q.'%q_idx' WHERE segid=%d",
  5070   4924         pConfig->zDb, pConfig->zName, pSeg->iSegid
  5071   4925     ));
  5072   4926   
  5073   4927     /* Iterate through the b-tree hierarchy.  */
  5074   4928     while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
  5075   4929       i64 iRow;                     /* Rowid for this leaf */
  5076   4930       Fts5Data *pLeaf;              /* Data for this leaf */
  5077         -    int iOff;                     /* Offset of first term on leaf */
  5078   4931   
  5079   4932       int nIdxTerm = sqlite3_column_bytes(pStmt, 1);
  5080   4933       const char *zIdxTerm = (const char*)sqlite3_column_text(pStmt, 1);
  5081   4934       int iIdxLeaf = sqlite3_column_int(pStmt, 2);
  5082   4935       int bIdxDlidx = sqlite3_column_int(pStmt, 3);
  5083   4936   
  5084   4937       /* If the leaf in question has already been trimmed from the segment, 
  5085   4938       ** ignore this b-tree entry. Otherwise, load it into memory. */
  5086   4939       if( iIdxLeaf<pSeg->pgnoFirst ) continue;
  5087         -    iRow = FTS5_SEGMENT_ROWID(pSeg->iSegid, 0, iIdxLeaf);
         4940  +    iRow = FTS5_SEGMENT_ROWID(pSeg->iSegid, iIdxLeaf);
  5088   4941       pLeaf = fts5DataRead(p, iRow);
  5089   4942       if( pLeaf==0 ) break;
  5090   4943   
  5091   4944       /* Check that the leaf contains at least one term, and that it is equal
  5092   4945       ** to or larger than the split-key in zIdxTerm.  Also check that if there
  5093   4946       ** is also a rowid pointer within the leaf page header, it points to a
  5094   4947       ** location before the term.  */
  5095         -    iOff = fts5GetU16(&pLeaf->p[2]);
  5096         -    if( iOff==0 ){
         4948  +    if( pLeaf->nn<=pLeaf->szLeaf ){
  5097   4949         p->rc = FTS5_CORRUPT;
  5098   4950       }else{
  5099         -      int iRowidOff;
         4951  +      int iOff;                   /* Offset of first term on leaf */
         4952  +      int iRowidOff;              /* Offset of first rowid on leaf */
  5100   4953         int nTerm;                  /* Size of term on leaf in bytes */
  5101   4954         int res;                    /* Comparison of term and split-key */
  5102   4955   
  5103         -      iRowidOff = fts5GetU16(&pLeaf->p[0]);
         4956  +      iOff = fts5LeafFirstTermOff(pLeaf);
         4957  +      iRowidOff = fts5LeafFirstRowidOff(pLeaf);
  5104   4958         if( iRowidOff>=iOff ){
  5105   4959           p->rc = FTS5_CORRUPT;
  5106   4960         }else{
  5107   4961           iOff += fts5GetVarint32(&pLeaf->p[iOff], nTerm);
  5108   4962           res = memcmp(&pLeaf->p[iOff], zIdxTerm, MIN(nTerm, nIdxTerm));
  5109   4963           if( res==0 ) res = nTerm - nIdxTerm;
  5110   4964           if( res<0 ) p->rc = FTS5_CORRUPT;
  5111   4965         }
         4966  +
         4967  +      fts5IntegrityCheckPgidx(p, pLeaf);
  5112   4968       }
  5113   4969       fts5DataRelease(pLeaf);
  5114   4970       if( p->rc ) break;
  5115   4971   
  5116   4972   
  5117   4973       /* Now check that the iter.nEmpty leaves following the current leaf
  5118   4974       ** (a) exist and (b) contain no terms. */
................................................................................
  5132   4988         for(pDlidx=fts5DlidxIterInit(p, 0, iSegid, iIdxLeaf);
  5133   4989             fts5DlidxIterEof(p, pDlidx)==0;
  5134   4990             fts5DlidxIterNext(p, pDlidx)
  5135   4991         ){
  5136   4992   
  5137   4993           /* Check any rowid-less pages that occur before the current leaf. */
  5138   4994           for(iPg=iPrevLeaf+1; iPg<fts5DlidxIterPgno(pDlidx); iPg++){
  5139         -          iKey = FTS5_SEGMENT_ROWID(iSegid, 0, iPg);
         4995  +          iKey = FTS5_SEGMENT_ROWID(iSegid, iPg);
  5140   4996             pLeaf = fts5DataRead(p, iKey);
  5141   4997             if( pLeaf ){
  5142         -            if( fts5GetU16(&pLeaf->p[0])!=0 ) p->rc = FTS5_CORRUPT;
         4998  +            if( fts5LeafFirstRowidOff(pLeaf)!=0 ) p->rc = FTS5_CORRUPT;
  5143   4999               fts5DataRelease(pLeaf);
  5144   5000             }
  5145   5001           }
  5146   5002           iPrevLeaf = fts5DlidxIterPgno(pDlidx);
  5147   5003   
  5148   5004           /* Check that the leaf page indicated by the iterator really does
  5149   5005           ** contain the rowid suggested by the same. */
  5150         -        iKey = FTS5_SEGMENT_ROWID(iSegid, 0, iPrevLeaf);
         5006  +        iKey = FTS5_SEGMENT_ROWID(iSegid, iPrevLeaf);
  5151   5007           pLeaf = fts5DataRead(p, iKey);
  5152   5008           if( pLeaf ){
  5153   5009             i64 iRowid;
  5154         -          int iRowidOff = fts5GetU16(&pLeaf->p[0]);
  5155         -          if( iRowidOff>=pLeaf->n ){
         5010  +          int iRowidOff = fts5LeafFirstRowidOff(pLeaf);
         5011  +          ASSERT_SZLEAF_OK(pLeaf);
         5012  +          if( iRowidOff>=pLeaf->szLeaf ){
  5156   5013               p->rc = FTS5_CORRUPT;
  5157   5014             }else{
  5158   5015               fts5GetVarint(&pLeaf->p[iRowidOff], (u64*)&iRowid);
  5159   5016               if( iRowid!=fts5DlidxIterRowid(pDlidx) ) p->rc = FTS5_CORRUPT;
  5160   5017             }
  5161   5018             fts5DataRelease(pLeaf);
  5162   5019           }
................................................................................
  5323   5180   
  5324   5181   static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){
  5325   5182     int iSegid, iHeight, iPgno, bDlidx;       /* Rowid compenents */
  5326   5183     fts5DecodeRowid(iKey, &iSegid, &bDlidx, &iHeight, &iPgno);
  5327   5184   
  5328   5185     if( iSegid==0 ){
  5329   5186       if( iKey==FTS5_AVERAGES_ROWID ){
  5330         -      sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "(averages) ");
         5187  +      sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{averages} ");
  5331   5188       }else{
  5332         -      sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "(structure)");
         5189  +      sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{structure}");
  5333   5190       }
  5334   5191     }
  5335   5192     else{
  5336         -    sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "(%ssegid=%d h=%d pgno=%d)",
         5193  +    sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{%ssegid=%d h=%d pgno=%d}",
  5337   5194           bDlidx ? "dlidx " : "", iSegid, iHeight, iPgno
  5338   5195       );
  5339   5196     }
  5340   5197   }
  5341   5198   
  5342   5199   static void fts5DebugStructure(
  5343   5200     int *pRc,                       /* IN/OUT: error code */
................................................................................
  5349   5206     for(iLvl=0; iLvl<p->nLevel; iLvl++){
  5350   5207       Fts5StructureLevel *pLvl = &p->aLevel[iLvl];
  5351   5208       sqlite3Fts5BufferAppendPrintf(pRc, pBuf, 
  5352   5209           " {lvl=%d nMerge=%d nSeg=%d", iLvl, pLvl->nMerge, pLvl->nSeg
  5353   5210       );
  5354   5211       for(iSeg=0; iSeg<pLvl->nSeg; iSeg++){
  5355   5212         Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg];
  5356         -      sqlite3Fts5BufferAppendPrintf(pRc, pBuf, 
  5357         -          " {id=%d h=%d leaves=%d..%d}", pSeg->iSegid, pSeg->nHeight, 
  5358         -          pSeg->pgnoFirst, pSeg->pgnoLast
         5213  +      sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " {id=%d leaves=%d..%d}", 
         5214  +          pSeg->iSegid, pSeg->pgnoFirst, pSeg->pgnoLast
  5359   5215         );
  5360   5216       }
  5361   5217       sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "}");
  5362   5218     }
  5363   5219   }
  5364   5220   
  5365   5221   /*
................................................................................
  5409   5265   ** may or may not finish within the buffer. This function appends a text
  5410   5266   ** representation of the part of the doclist that is present to buffer
  5411   5267   ** pBuf. 
  5412   5268   **
  5413   5269   ** The return value is the number of bytes read from the input buffer.
  5414   5270   */
  5415   5271   static int fts5DecodeDoclist(int *pRc, Fts5Buffer *pBuf, const u8 *a, int n){
  5416         -  i64 iDocid;
         5272  +  i64 iDocid = 0;
  5417   5273     int iOff = 0;
  5418   5274   
  5419         -  iOff = sqlite3Fts5GetVarint(&a[iOff], (u64*)&iDocid);
  5420         -  sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " rowid=%lld", iDocid);
         5275  +  if( n>0 ){
         5276  +    iOff = sqlite3Fts5GetVarint(a, (u64*)&iDocid);
         5277  +    sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " id=%lld", iDocid);
         5278  +  }
  5421   5279     while( iOff<n ){
  5422   5280       int nPos;
  5423   5281       int bDummy;
  5424   5282       iOff += fts5GetPoslistSize(&a[iOff], &nPos, &bDummy);
  5425   5283       iOff += fts5DecodePoslist(pRc, pBuf, &a[iOff], MIN(n-iOff, nPos));
  5426   5284       if( iOff<n ){
  5427   5285         i64 iDelta;
  5428   5286         iOff += sqlite3Fts5GetVarint(&a[iOff], (u64*)&iDelta);
  5429         -      if( iDelta==0 ) return iOff;
  5430   5287         iDocid += iDelta;
  5431         -      sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " rowid=%lld", iDocid);
         5288  +      sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " id=%lld", iDocid);
  5432   5289       }
  5433   5290     }
  5434   5291   
  5435   5292     return iOff;
  5436   5293   }
  5437   5294   
  5438   5295   /*
................................................................................
  5450   5307     Fts5Buffer s;                   /* Build up text to return here */
  5451   5308     int rc = SQLITE_OK;             /* Return code */
  5452   5309     int nSpace = 0;
  5453   5310   
  5454   5311     assert( nArg==2 );
  5455   5312     memset(&s, 0, sizeof(Fts5Buffer));
  5456   5313     iRowid = sqlite3_value_int64(apVal[0]);
         5314  +
         5315  +  /* Make a copy of the second argument (a blob) in aBlob[]. The aBlob[]
         5316  +  ** copy is followed by FTS5_DATA_ZERO_PADDING 0x00 bytes, which prevents
         5317  +  ** buffer overreads even if the record is corrupt.  */
  5457   5318     n = sqlite3_value_bytes(apVal[1]);
  5458   5319     aBlob = sqlite3_value_blob(apVal[1]);
  5459         -
  5460   5320     nSpace = n + FTS5_DATA_ZERO_PADDING;
  5461   5321     a = (u8*)sqlite3Fts5MallocZero(&rc, nSpace);
  5462   5322     if( a==0 ) goto decode_out;
  5463   5323     memcpy(a, aBlob, n);
         5324  +
         5325  +
  5464   5326     fts5DecodeRowid(iRowid, &iSegid, &bDlidx, &iHeight, &iPgno);
  5465   5327   
  5466   5328     fts5DebugRowid(&rc, &s, iRowid);
  5467   5329     if( bDlidx ){
  5468   5330       Fts5Data dlidx;
  5469   5331       Fts5DlidxLvl lvl;
  5470   5332   
  5471   5333       dlidx.p = a;
  5472         -    dlidx.n = n;
         5334  +    dlidx.nn = n;
  5473   5335   
  5474   5336       memset(&lvl, 0, sizeof(Fts5DlidxLvl));
  5475   5337       lvl.pData = &dlidx;
  5476   5338       lvl.iLeafPgno = iPgno;
  5477   5339   
  5478   5340       for(fts5DlidxLvlNext(&lvl); lvl.bEof==0; fts5DlidxLvlNext(&lvl)){
  5479   5341         sqlite3Fts5BufferAppendPrintf(&rc, &s, 
................................................................................
  5483   5345     }else if( iSegid==0 ){
  5484   5346       if( iRowid==FTS5_AVERAGES_ROWID ){
  5485   5347         /* todo */
  5486   5348       }else{
  5487   5349         fts5DecodeStructure(&rc, &s, a, n);
  5488   5350       }
  5489   5351     }else{
         5352  +    Fts5Buffer term;              /* Current term read from page */
         5353  +    int szLeaf;                   /* Offset of pgidx in a[] */
         5354  +    int iPgidxOff;
         5355  +    int iPgidxPrev = 0;           /* Previous value read from pgidx */
         5356  +    int iTermOff = 0;
         5357  +    int iRowidOff = 0;
         5358  +    int iOff;
         5359  +    int nDoclist;
  5490   5360   
  5491         -    Fts5Buffer term;
  5492   5361       memset(&term, 0, sizeof(Fts5Buffer));
  5493   5362   
  5494         -    if( iHeight==0 ){
  5495         -      int iTermOff = 0;
  5496         -      int iRowidOff = 0;
  5497         -      int iOff;
  5498         -      int nKeep = 0;
  5499         -
  5500         -      if( n>=4 ){
  5501         -        iRowidOff = fts5GetU16(&a[0]);
  5502         -        iTermOff = fts5GetU16(&a[2]);
  5503         -      }else{
  5504         -        sqlite3Fts5BufferSet(&rc, &s, 8, (const u8*)"corrupt");
  5505         -        goto decode_out;
         5363  +    if( n<4 ){
         5364  +      sqlite3Fts5BufferSet(&rc, &s, 8, (const u8*)"corrupt");
         5365  +      goto decode_out;
         5366  +    }else{
         5367  +      iRowidOff = fts5GetU16(&a[0]);
         5368  +      iPgidxOff = szLeaf = fts5GetU16(&a[2]);
         5369  +      if( iPgidxOff<n ){
         5370  +        fts5GetVarint32(&a[iPgidxOff], iTermOff);
  5506   5371         }
         5372  +    }
         5373  +
         5374  +    /* Decode the position list tail at the start of the page */
         5375  +    if( iRowidOff!=0 ){
         5376  +      iOff = iRowidOff;
         5377  +    }else if( iTermOff!=0 ){
         5378  +      iOff = iTermOff;
         5379  +    }else{
         5380  +      iOff = szLeaf;
         5381  +    }
         5382  +    fts5DecodePoslist(&rc, &s, &a[4], iOff-4);
         5383  +
         5384  +    /* Decode any more doclist data that appears on the page before the
         5385  +    ** first term. */
         5386  +    nDoclist = (iTermOff ? iTermOff : szLeaf) - iOff;
         5387  +    fts5DecodeDoclist(&rc, &s, &a[iOff], nDoclist);
  5507   5388   
  5508         -      if( iRowidOff ){
  5509         -        iOff = iRowidOff;
  5510         -      }else if( iTermOff ){
  5511         -        iOff = iTermOff;
  5512         -      }else{
  5513         -        iOff = n;
  5514         -      }
  5515         -      fts5DecodePoslist(&rc, &s, &a[4], iOff-4);
         5389  +    while( iPgidxOff<n ){
         5390  +      int bFirst = (iPgidxOff==szLeaf);     /* True for first term on page */
         5391  +      int nByte;                            /* Bytes of data */
         5392  +      int iEnd;
         5393  +      
         5394  +      iPgidxOff += fts5GetVarint32(&a[iPgidxOff], nByte);
         5395  +      iPgidxPrev += nByte;
         5396  +      iOff = iPgidxPrev;
  5516   5397   
  5517         -      assert( iRowidOff==0 || iOff==iRowidOff );
  5518         -      if( iRowidOff ){
  5519         -        iOff += fts5DecodeDoclist(&rc, &s, &a[iOff], n-iOff);
         5398  +      if( iPgidxOff<n ){
         5399  +        fts5GetVarint32(&a[iPgidxOff], nByte);
         5400  +        iEnd = iPgidxPrev + nByte;
         5401  +      }else{
         5402  +        iEnd = szLeaf;
  5520   5403         }
  5521   5404   
  5522         -      assert( iTermOff==0 || iOff==iTermOff );
  5523         -      while( iOff<n ){
  5524         -        int nByte;
         5405  +      if( bFirst==0 ){
  5525   5406           iOff += fts5GetVarint32(&a[iOff], nByte);
  5526         -        term.n= nKeep;
  5527         -        fts5BufferAppendBlob(&rc, &term, nByte, &a[iOff]);
  5528         -        iOff += nByte;
  5529         -
  5530         -        sqlite3Fts5BufferAppendPrintf(
  5531         -            &rc, &s, " term=%.*s", term.n, (const char*)term.p
  5532         -        );
  5533         -        iOff += fts5DecodeDoclist(&rc, &s, &a[iOff], n-iOff);
  5534         -        if( iOff<n ){
  5535         -          iOff += fts5GetVarint32(&a[iOff], nKeep);
  5536         -        }
         5407  +        term.n = nByte;
  5537   5408         }
  5538         -      fts5BufferFree(&term);
  5539         -    }else{
  5540         -      Fts5NodeIter ss;
  5541         -      for(fts5NodeIterInit(a, n, &ss); ss.aData; fts5NodeIterNext(&rc, &ss)){
  5542         -        if( ss.term.n==0 ){
  5543         -          sqlite3Fts5BufferAppendPrintf(&rc, &s, " left=%d", ss.iChild);
  5544         -        }else{
  5545         -          sqlite3Fts5BufferAppendPrintf(&rc,&s, " \"%.*s\"", 
  5546         -              ss.term.n, ss.term.p
  5547         -          );
  5548         -        }
  5549         -        if( ss.nEmpty ){
  5550         -          sqlite3Fts5BufferAppendPrintf(&rc, &s, " empty=%d%s", ss.nEmpty,
  5551         -              ss.bDlidx ? "*" : ""
  5552         -          );
  5553         -        }
  5554         -      }
  5555         -      fts5NodeIterFree(&ss);
         5409  +      iOff += fts5GetVarint32(&a[iOff], nByte);
         5410  +      fts5BufferAppendBlob(&rc, &term, nByte, &a[iOff]);
         5411  +      iOff += nByte;
         5412  +
         5413  +      sqlite3Fts5BufferAppendPrintf(
         5414  +          &rc, &s, " term=%.*s", term.n, (const char*)term.p
         5415  +      );
         5416  +      iOff += fts5DecodeDoclist(&rc, &s, &a[iOff], iEnd-iOff);
  5556   5417       }
         5418  +
         5419  +    fts5BufferFree(&term);
  5557   5420     }
  5558   5421     
  5559   5422    decode_out:
  5560   5423     sqlite3_free(a);
  5561   5424     if( rc==SQLITE_OK ){
  5562   5425       sqlite3_result_text(pCtx, (const char*)s.p, s.n, SQLITE_TRANSIENT);
  5563   5426     }else{
................................................................................
  5577   5440     const char *zArg;
  5578   5441     if( nArg==0 ){
  5579   5442       sqlite3_result_error(pCtx, "should be: fts5_rowid(subject, ....)", -1);
  5580   5443     }else{
  5581   5444       zArg = (const char*)sqlite3_value_text(apVal[0]);
  5582   5445       if( 0==sqlite3_stricmp(zArg, "segment") ){
  5583   5446         i64 iRowid;
  5584         -      int segid, height, pgno;
  5585         -      if( nArg!=4 ){
         5447  +      int segid, pgno;
         5448  +      if( nArg!=3 ){
  5586   5449           sqlite3_result_error(pCtx, 
  5587         -            "should be: fts5_rowid('segment', segid, height, pgno))", -1
         5450  +            "should be: fts5_rowid('segment', segid, pgno))", -1
  5588   5451           );
  5589   5452         }else{
  5590   5453           segid = sqlite3_value_int(apVal[1]);
  5591         -        height = sqlite3_value_int(apVal[2]);
  5592         -        pgno = sqlite3_value_int(apVal[3]);
  5593         -        iRowid = FTS5_SEGMENT_ROWID(segid, height, pgno);
         5454  +        pgno = sqlite3_value_int(apVal[2]);
         5455  +        iRowid = FTS5_SEGMENT_ROWID(segid, pgno);
  5594   5456           sqlite3_result_int64(pCtx, iRowid);
  5595   5457         }
  5596         -    }else {
         5458  +    }else{
  5597   5459         sqlite3_result_error(pCtx, 
  5598         -        "first arg to fts5_rowid() must be 'segment' "
  5599         -        "or 'start-of-index'"
  5600         -        , -1
         5460  +        "first arg to fts5_rowid() must be 'segment'" , -1
  5601   5461         );
  5602   5462       }
  5603   5463     }
  5604   5464   }
  5605   5465   
  5606   5466   /*
  5607   5467   ** This is called as part of registering the FTS5 module with database

Changes to ext/fts5/fts5_main.c.

    21     21   ** structures should not be corrupt. Otherwise, true. If it is false, extra
    22     22   ** assert() conditions in the fts5 code are activated - conditions that are
    23     23   ** only true if it is guaranteed that the fts5 database is not corrupt.
    24     24   */
    25     25   int sqlite3_fts5_may_be_corrupt = 1;
    26     26   
    27     27   
    28         -typedef struct Fts5Table Fts5Table;
    29         -typedef struct Fts5Cursor Fts5Cursor;
           28  +typedef struct Fts5Auxdata Fts5Auxdata;
    30     29   typedef struct Fts5Auxiliary Fts5Auxiliary;
    31         -typedef struct Fts5Auxdata Fts5Auxdata;
    32         -
           30  +typedef struct Fts5Cursor Fts5Cursor;
           31  +typedef struct Fts5Sorter Fts5Sorter;
           32  +typedef struct Fts5Table Fts5Table;
    33     33   typedef struct Fts5TokenizerModule Fts5TokenizerModule;
    34     34   
    35     35   /*
    36     36   ** NOTES ON TRANSACTIONS: 
    37     37   **
    38     38   ** SQLite invokes the following virtual table methods as transactions are 
    39     39   ** opened and closed by the user:
................................................................................
  1313   1313     }else if( 0==sqlite3_stricmp("optimize", z) ){
  1314   1314       rc = sqlite3Fts5StorageOptimize(pTab->pStorage);
  1315   1315     }else if( 0==sqlite3_stricmp("merge", z) ){
  1316   1316       int nMerge = sqlite3_value_int(pVal);
  1317   1317       rc = sqlite3Fts5StorageMerge(pTab->pStorage, nMerge);
  1318   1318     }else if( 0==sqlite3_stricmp("integrity-check", z) ){
  1319   1319       rc = sqlite3Fts5StorageIntegrity(pTab->pStorage);
         1320  +#ifdef SQLITE_DEBUG
         1321  +  }else if( 0==sqlite3_stricmp("prefix-index", z) ){
         1322  +    pConfig->bPrefixIndex = sqlite3_value_int(pVal);
         1323  +#endif
  1320   1324     }else{
  1321   1325       rc = sqlite3Fts5IndexLoadConfig(pTab->pIndex);
  1322   1326       if( rc==SQLITE_OK ){
  1323   1327         rc = sqlite3Fts5ConfigSetValue(pTab->pConfig, z, pVal, &bError);
  1324   1328       }
  1325   1329       if( rc==SQLITE_OK ){
  1326   1330         if( bError ){
................................................................................
  1494   1498     return sqlite3Fts5StorageRowCount(pTab->pStorage, pnRow);
  1495   1499   }
  1496   1500   
  1497   1501   static int fts5ApiTokenize(
  1498   1502     Fts5Context *pCtx, 
  1499   1503     const char *pText, int nText, 
  1500   1504     void *pUserData,
  1501         -  int (*xToken)(void*, const char*, int, int, int)
         1505  +  int (*xToken)(void*, int, const char*, int, int, int)
  1502   1506   ){
  1503   1507     Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
  1504   1508     Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
  1505         -  return sqlite3Fts5Tokenize(pTab->pConfig, pText, nText, pUserData, xToken);
         1509  +  return sqlite3Fts5Tokenize(
         1510  +      pTab->pConfig, FTS5_TOKENIZE_AUX, pText, nText, pUserData, xToken
         1511  +  );
  1506   1512   }
  1507   1513   
  1508   1514   static int fts5ApiPhraseCount(Fts5Context *pCtx){
  1509   1515     Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
  1510   1516     return sqlite3Fts5ExprPhraseCount(pCsr->pExpr);
  1511   1517   }
  1512   1518   
................................................................................
  1651   1657       }
  1652   1658     }
  1653   1659     return rc;
  1654   1660   }
  1655   1661   
  1656   1662   static int fts5ColumnSizeCb(
  1657   1663     void *pContext,                 /* Pointer to int */
         1664  +  int tflags,
  1658   1665     const char *pToken,             /* Buffer containing token */
  1659   1666     int nToken,                     /* Size of token in bytes */
  1660   1667     int iStart,                     /* Start offset of token */
  1661   1668     int iEnd                        /* End offset of token */
  1662   1669   ){
  1663   1670     int *pCnt = (int*)pContext;
  1664         -  *pCnt = *pCnt + 1;
         1671  +  if( (tflags & FTS5_TOKEN_COLOCATED)==0 ){
         1672  +    (*pCnt)++;
         1673  +  }
  1665   1674     return SQLITE_OK;
  1666   1675   }
  1667   1676   
  1668   1677   static int fts5ApiColumnSize(Fts5Context *pCtx, int iCol, int *pnToken){
  1669   1678     Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
  1670   1679     Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
  1671   1680     Fts5Config *pConfig = pTab->pConfig;
................................................................................
  1687   1696         for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
  1688   1697           if( pConfig->abUnindexed[i]==0 ){
  1689   1698             const char *z; int n;
  1690   1699             void *p = (void*)(&pCsr->aColumnSize[i]);
  1691   1700             pCsr->aColumnSize[i] = 0;
  1692   1701             rc = fts5ApiColumnText(pCtx, i, &z, &n);
  1693   1702             if( rc==SQLITE_OK ){
  1694         -            rc = sqlite3Fts5Tokenize(pConfig, z, n, p, fts5ColumnSizeCb);
         1703  +            rc = sqlite3Fts5Tokenize(
         1704  +                pConfig, FTS5_TOKENIZE_AUX, z, n, p, fts5ColumnSizeCb
         1705  +            );
  1695   1706             }
  1696   1707           }
  1697   1708         }
  1698   1709       }
  1699   1710       CsrFlagClear(pCsr, FTS5CSR_REQUIRE_DOCSIZE);
  1700   1711     }
  1701   1712     if( iCol<0 ){
................................................................................
  1849   1860     rc = fts5OpenMethod(pCsr->base.pVtab, (sqlite3_vtab_cursor**)&pNew);
  1850   1861     if( rc==SQLITE_OK ){
  1851   1862       Fts5Config *pConf = pTab->pConfig;
  1852   1863       pNew->ePlan = FTS5_PLAN_MATCH;
  1853   1864       pNew->iFirstRowid = SMALLEST_INT64;
  1854   1865       pNew->iLastRowid = LARGEST_INT64;
  1855   1866       pNew->base.pVtab = (sqlite3_vtab*)pTab;
  1856         -    rc = sqlite3Fts5ExprPhraseExpr(pConf, pCsr->pExpr, iPhrase, &pNew->pExpr);
         1867  +    rc = sqlite3Fts5ExprClonePhrase(pConf, pCsr->pExpr, iPhrase, &pNew->pExpr);
  1857   1868     }
  1858   1869   
  1859   1870     if( rc==SQLITE_OK ){
  1860   1871       for(rc = fts5CursorFirst(pTab, pNew, 0);
  1861   1872           rc==SQLITE_OK && CsrFlagTest(pNew, FTS5CSR_EOF)==0;
  1862   1873           rc = fts5NextMethod((sqlite3_vtab_cursor*)pNew)
  1863   1874       ){
................................................................................
  2340   2351     pGlobal = (Fts5Global*)sqlite3_malloc(sizeof(Fts5Global));
  2341   2352     if( pGlobal==0 ){
  2342   2353       rc = SQLITE_NOMEM;
  2343   2354     }else{
  2344   2355       void *p = (void*)pGlobal;
  2345   2356       memset(pGlobal, 0, sizeof(Fts5Global));
  2346   2357       pGlobal->db = db;
  2347         -    pGlobal->api.iVersion = 1;
         2358  +    pGlobal->api.iVersion = 2;
  2348   2359       pGlobal->api.xCreateFunction = fts5CreateAux;
  2349   2360       pGlobal->api.xCreateTokenizer = fts5CreateTokenizer;
  2350   2361       pGlobal->api.xFindTokenizer = fts5FindTokenizer;
  2351   2362       rc = sqlite3_create_module_v2(db, "fts5", &fts5Mod, p, fts5ModuleDestroy);
  2352   2363       if( rc==SQLITE_OK ) rc = sqlite3Fts5IndexInit(db);
  2353   2364       if( rc==SQLITE_OK ) rc = sqlite3Fts5ExprInit(pGlobal, db);
  2354   2365       if( rc==SQLITE_OK ) rc = sqlite3Fts5AuxInit(&pGlobal->api);

Changes to ext/fts5/fts5_storage.c.

   355    355   };
   356    356   
   357    357   /*
   358    358   ** Tokenization callback used when inserting tokens into the FTS index.
   359    359   */
   360    360   static int fts5StorageInsertCallback(
   361    361     void *pContext,                 /* Pointer to Fts5InsertCtx object */
          362  +  int tflags,
   362    363     const char *pToken,             /* Buffer containing token */
   363    364     int nToken,                     /* Size of token in bytes */
   364    365     int iStart,                     /* Start offset of token */
   365    366     int iEnd                        /* End offset of token */
   366    367   ){
   367    368     Fts5InsertCtx *pCtx = (Fts5InsertCtx*)pContext;
   368    369     Fts5Index *pIdx = pCtx->pStorage->pIndex;
   369         -  int iPos = pCtx->szCol++;
   370         -  return sqlite3Fts5IndexWrite(pIdx, pCtx->iCol, iPos, pToken, nToken);
          370  +  if( (tflags & FTS5_TOKEN_COLOCATED)==0 || pCtx->szCol==0 ){
          371  +    pCtx->szCol++;
          372  +  }
          373  +  return sqlite3Fts5IndexWrite(pIdx, pCtx->iCol, pCtx->szCol-1, pToken, nToken);
   371    374   }
   372    375   
   373    376   /*
   374    377   ** If a row with rowid iDel is present in the %_content table, add the
   375    378   ** delete-markers to the FTS index necessary to delete it. Do not actually
   376    379   ** remove the %_content row at this time though.
   377    380   */
................................................................................
   390    393         ctx.pStorage = p;
   391    394         ctx.iCol = -1;
   392    395         rc = sqlite3Fts5IndexBeginWrite(p->pIndex, iDel);
   393    396         for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){
   394    397           if( pConfig->abUnindexed[iCol-1] ) continue;
   395    398           ctx.szCol = 0;
   396    399           rc = sqlite3Fts5Tokenize(pConfig, 
          400  +            FTS5_TOKENIZE_DOCUMENT,
   397    401               (const char*)sqlite3_column_text(pSeek, iCol),
   398    402               sqlite3_column_bytes(pSeek, iCol),
   399    403               (void*)&ctx,
   400    404               fts5StorageInsertCallback
   401    405           );
   402    406           p->aTotalSize[iCol-1] -= (i64)ctx.szCol;
   403    407         }
................................................................................
   447    451   **
   448    452   ** Return SQLITE_OK if successful, or an SQLite error code if an error
   449    453   ** occurs.
   450    454   */
   451    455   static int fts5StorageLoadTotals(Fts5Storage *p, int bCache){
   452    456     int rc = SQLITE_OK;
   453    457     if( p->bTotalsValid==0 ){
   454         -    int nCol = p->pConfig->nCol;
   455         -    Fts5Buffer buf;
   456         -    memset(&buf, 0, sizeof(buf));
   457         -
   458         -    memset(p->aTotalSize, 0, sizeof(i64) * nCol);
   459         -    p->nTotalRow = 0;
   460         -    rc = sqlite3Fts5IndexGetAverages(p->pIndex, &buf);
   461         -    if( rc==SQLITE_OK && buf.n ){
   462         -      int i = 0;
   463         -      int iCol;
   464         -      i += fts5GetVarint(&buf.p[i], (u64*)&p->nTotalRow);
   465         -      for(iCol=0; i<buf.n && iCol<nCol; iCol++){
   466         -        i += fts5GetVarint(&buf.p[i], (u64*)&p->aTotalSize[iCol]);
   467         -      }
   468         -    }
   469         -    sqlite3_free(buf.p);
          458  +    rc = sqlite3Fts5IndexGetAverages(p->pIndex, &p->nTotalRow, p->aTotalSize);
   470    459       p->bTotalsValid = bCache;
   471    460     }
   472    461     return rc;
   473    462   }
   474    463   
   475    464   /*
   476    465   ** Store the current contents of the p->nTotalRow and p->aTotalSize[] 
................................................................................
   561    550       ctx.iCol = -1;
   562    551   
   563    552       rc = sqlite3Fts5IndexBeginWrite(p->pIndex, iDel);
   564    553       for(iCol=0; rc==SQLITE_OK && iCol<pConfig->nCol; iCol++){
   565    554         if( pConfig->abUnindexed[iCol] ) continue;
   566    555         ctx.szCol = 0;
   567    556         rc = sqlite3Fts5Tokenize(pConfig, 
          557  +        FTS5_TOKENIZE_DOCUMENT,
   568    558           (const char*)sqlite3_value_text(apVal[iCol]),
   569    559           sqlite3_value_bytes(apVal[iCol]),
   570    560           (void*)&ctx,
   571    561           fts5StorageInsertCallback
   572    562         );
   573    563         p->aTotalSize[iCol] -= (i64)ctx.szCol;
   574    564       }
................................................................................
   650    640   
   651    641       sqlite3Fts5BufferZero(&buf);
   652    642       rc = sqlite3Fts5IndexBeginWrite(p->pIndex, iRowid);
   653    643       for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){
   654    644         ctx.szCol = 0;
   655    645         if( pConfig->abUnindexed[ctx.iCol]==0 ){
   656    646           rc = sqlite3Fts5Tokenize(pConfig, 
          647  +            FTS5_TOKENIZE_DOCUMENT,
   657    648               (const char*)sqlite3_column_text(pScan, ctx.iCol+1),
   658    649               sqlite3_column_bytes(pScan, ctx.iCol+1),
   659    650               (void*)&ctx,
   660    651               fts5StorageInsertCallback
   661    652           );
   662    653         }
   663    654         sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol);
................................................................................
   767    758       rc = sqlite3Fts5IndexBeginWrite(p->pIndex, *piRowid);
   768    759       ctx.pStorage = p;
   769    760     }
   770    761     for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){
   771    762       ctx.szCol = 0;
   772    763       if( pConfig->abUnindexed[ctx.iCol]==0 ){
   773    764         rc = sqlite3Fts5Tokenize(pConfig, 
          765  +          FTS5_TOKENIZE_DOCUMENT,
   774    766             (const char*)sqlite3_value_text(apVal[ctx.iCol+2]),
   775    767             sqlite3_value_bytes(apVal[ctx.iCol+2]),
   776    768             (void*)&ctx,
   777    769             fts5StorageInsertCallback
   778    770         );
   779    771       }
   780    772       sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol);
................................................................................
   834    826   };
   835    827   
   836    828   /*
   837    829   ** Tokenization callback used by integrity check.
   838    830   */
   839    831   static int fts5StorageIntegrityCallback(
   840    832     void *pContext,                 /* Pointer to Fts5InsertCtx object */
          833  +  int tflags,
   841    834     const char *pToken,             /* Buffer containing token */
   842    835     int nToken,                     /* Size of token in bytes */
   843    836     int iStart,                     /* Start offset of token */
   844    837     int iEnd                        /* End offset of token */
   845    838   ){
   846    839     Fts5IntegrityCtx *pCtx = (Fts5IntegrityCtx*)pContext;
   847         -  int iPos = pCtx->szCol++;
          840  +  if( (tflags & FTS5_TOKEN_COLOCATED)==0 || pCtx->szCol==0 ){
          841  +    pCtx->szCol++;
          842  +  }
   848    843     pCtx->cksum ^= sqlite3Fts5IndexCksum(
   849         -      pCtx->pConfig, pCtx->iRowid, pCtx->iCol, iPos, pToken, nToken
          844  +      pCtx->pConfig, pCtx->iRowid, pCtx->iCol, pCtx->szCol-1, pToken, nToken
   850    845     );
   851    846     return SQLITE_OK;
   852    847   }
   853    848   
   854    849   /*
   855    850   ** Check that the contents of the FTS index match that of the %_content
   856    851   ** table. Return SQLITE_OK if they do, or SQLITE_CORRUPT if not. Return
................................................................................
   877    872     rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0);
   878    873     if( rc==SQLITE_OK ){
   879    874       int rc2;
   880    875       while( SQLITE_ROW==sqlite3_step(pScan) ){
   881    876         int i;
   882    877         ctx.iRowid = sqlite3_column_int64(pScan, 0);
   883    878         ctx.szCol = 0;
   884         -      rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize);
          879  +      if( pConfig->bColumnsize ){
          880  +        rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize);
          881  +      }
   885    882         for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
   886    883           if( pConfig->abUnindexed[i] ) continue;
   887    884           ctx.iCol = i;
   888    885           ctx.szCol = 0;
   889         -        rc = sqlite3Fts5Tokenize(
   890         -            pConfig, 
          886  +        rc = sqlite3Fts5Tokenize(pConfig, 
          887  +            FTS5_TOKENIZE_DOCUMENT,
   891    888               (const char*)sqlite3_column_text(pScan, i+1),
   892    889               sqlite3_column_bytes(pScan, i+1),
   893    890               (void*)&ctx,
   894    891               fts5StorageIntegrityCallback
   895    892           );
   896         -        if( ctx.szCol!=aColSize[i] ) rc = FTS5_CORRUPT;
          893  +        if( pConfig->bColumnsize && ctx.szCol!=aColSize[i] ){
          894  +          rc = FTS5_CORRUPT;
          895  +        }
   897    896           aTotalSize[i] += ctx.szCol;
   898    897         }
   899    898         if( rc!=SQLITE_OK ) break;
   900    899       }
   901    900       rc2 = sqlite3_reset(pScan);
   902    901       if( rc==SQLITE_OK ) rc = rc2;
   903    902     }
................................................................................
   914    913     /* Check that the %_docsize and %_content tables contain the expected
   915    914     ** number of rows.  */
   916    915     if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
   917    916       i64 nRow;
   918    917       rc = fts5StorageCount(p, "content", &nRow);
   919    918       if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT;
   920    919     }
   921         -  if( rc==SQLITE_OK ){
          920  +  if( rc==SQLITE_OK && pConfig->bColumnsize ){
   922    921       i64 nRow;
   923    922       rc = fts5StorageCount(p, "docsize", &nRow);
   924    923       if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT;
   925    924     }
   926    925   
   927    926     /* Pass the expected checksum down to the FTS index module. It will
   928    927     ** verify, amongst other things, that it matches the checksum generated by
................................................................................
   998    997   ** each table column. This function reads the %_docsize record for the
   999    998   ** specified rowid and populates aCol[] with the results.
  1000    999   **
  1001   1000   ** An SQLite error code is returned if an error occurs, or SQLITE_OK
  1002   1001   ** otherwise.
  1003   1002   */
  1004   1003   int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol){
  1005         -  int nCol = p->pConfig->nCol;
  1006         -  sqlite3_stmt *pLookup = 0;
  1007         -  int rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0);
         1004  +  int nCol = p->pConfig->nCol;    /* Number of user columns in table */
         1005  +  sqlite3_stmt *pLookup = 0;      /* Statement to query %_docsize */
         1006  +  int rc;                         /* Return Code */
         1007  +
         1008  +  assert( p->pConfig->bColumnsize );
         1009  +  rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0);
  1008   1010     if( rc==SQLITE_OK ){
  1009   1011       int bCorrupt = 1;
  1010   1012       sqlite3_bind_int64(pLookup, 1, iRowid);
  1011   1013       if( SQLITE_ROW==sqlite3_step(pLookup) ){
  1012   1014         const u8 *aBlob = sqlite3_column_blob(pLookup, 0);
  1013   1015         int nBlob = sqlite3_column_bytes(pLookup, 0);
  1014   1016         if( 0==fts5StorageDecodeSizeArray(aCol, nCol, aBlob, nBlob) ){

Changes to ext/fts5/fts5_tcl.c.

   137    137   typedef struct F5tAuxData F5tAuxData;
   138    138   struct F5tAuxData {
   139    139     Tcl_Obj *pObj;
   140    140   };
   141    141   
   142    142   static int xTokenizeCb(
   143    143     void *pCtx, 
          144  +  int tflags,
   144    145     const char *zToken, int nToken, 
   145    146     int iStart, int iEnd
   146    147   ){
   147    148     F5tFunction *p = (F5tFunction*)pCtx;
   148    149     Tcl_Obj *pEval = Tcl_DuplicateObj(p->pScript);
   149    150     int rc;
   150    151   
................................................................................
   580    581     Tcl_Obj *pRet;
   581    582     int bSubst;
   582    583     const char *zInput;
   583    584   };
   584    585   
   585    586   static int xTokenizeCb2(
   586    587     void *pCtx, 
          588  +  int tflags,
   587    589     const char *zToken, int nToken, 
   588    590     int iStart, int iEnd
   589    591   ){
   590    592     F5tTokenizeCtx *p = (F5tTokenizeCtx*)pCtx;
   591    593     if( p->bSubst ){
   592    594       Tcl_ListObjAppendElement(0, p->pRet, Tcl_NewStringObj(zToken, nToken));
   593    595       Tcl_ListObjAppendElement(
................................................................................
   662    664     }
   663    665   
   664    666     pRet = Tcl_NewObj();
   665    667     Tcl_IncrRefCount(pRet);
   666    668     ctx.bSubst = (objc==5);
   667    669     ctx.pRet = pRet;
   668    670     ctx.zInput = zText;
   669         -  rc = tokenizer.xTokenize(pTok, (void*)&ctx, zText, nText, xTokenizeCb2);
          671  +  rc = tokenizer.xTokenize(
          672  +      pTok, (void*)&ctx, FTS5_TOKENIZE_DOCUMENT, zText, nText, xTokenizeCb2
          673  +  );
   670    674     tokenizer.xDelete(pTok);
   671    675     if( rc!=SQLITE_OK ){
   672    676       Tcl_AppendResult(interp, "error in tokenizer.xTokenize()", 0);
   673    677       Tcl_DecrRefCount(pRet);
   674    678       return TCL_ERROR;
   675    679     }
   676    680   
................................................................................
   684    688   /*************************************************************************
   685    689   ** Start of tokenizer wrapper.
   686    690   */
   687    691   
   688    692   typedef struct F5tTokenizerContext F5tTokenizerContext;
   689    693   typedef struct F5tTokenizerCb F5tTokenizerCb;
   690    694   typedef struct F5tTokenizerModule F5tTokenizerModule;
   691         -typedef struct F5tTokenizerModule F5tTokenizerInstance;
          695  +typedef struct F5tTokenizerInstance F5tTokenizerInstance;
   692    696   
   693    697   struct F5tTokenizerContext {
   694    698     void *pCtx;
   695         -  int (*xToken)(void*, const char*, int, int, int);
          699  +  int (*xToken)(void*, int, const char*, int, int, int);
   696    700   };
   697    701   
   698    702   struct F5tTokenizerModule {
   699    703     Tcl_Interp *interp;
          704  +  Tcl_Obj *pScript;
          705  +  F5tTokenizerContext *pContext;
          706  +};
          707  +
          708  +struct F5tTokenizerInstance {
          709  +  Tcl_Interp *interp;
   700    710     Tcl_Obj *pScript;
   701    711     F5tTokenizerContext *pContext;
   702    712   };
   703    713   
   704    714   static int f5tTokenizerCreate(
   705    715     void *pCtx, 
   706    716     const char **azArg, 
................................................................................
   744    754     Tcl_DecrRefCount(pInst->pScript);
   745    755     ckfree((char *)pInst);
   746    756   }
   747    757   
   748    758   static int f5tTokenizerTokenize(
   749    759     Fts5Tokenizer *p, 
   750    760     void *pCtx,
          761  +  int flags,
   751    762     const char *pText, int nText, 
   752         -  int (*xToken)(void*, const char*, int, int, int)
          763  +  int (*xToken)(void*, int, const char*, int, int, int)
   753    764   ){
   754    765     F5tTokenizerInstance *pInst = (F5tTokenizerInstance*)p;
   755    766     void *pOldCtx;
   756         -  int (*xOldToken)(void*, const char*, int, int, int);
          767  +  int (*xOldToken)(void*, int, const char*, int, int, int);
   757    768     Tcl_Obj *pEval;
   758    769     int rc;
          770  +  const char *zFlags;
   759    771   
   760    772     pOldCtx = pInst->pContext->pCtx;
   761    773     xOldToken = pInst->pContext->xToken;
   762    774   
          775  +  pInst->pContext->pCtx = pCtx;
          776  +  pInst->pContext->xToken = xToken;
          777  +
          778  +  assert( 
          779  +      flags==FTS5_TOKENIZE_DOCUMENT
          780  +   || flags==FTS5_TOKENIZE_AUX
          781  +   || flags==FTS5_TOKENIZE_QUERY
          782  +   || flags==(FTS5_TOKENIZE_QUERY | FTS5_TOKENIZE_PREFIX)
          783  +  );
   763    784     pEval = Tcl_DuplicateObj(pInst->pScript);
   764    785     Tcl_IncrRefCount(pEval);
   765         -  rc = Tcl_ListObjAppendElement(
   766         -      pInst->interp, pEval, Tcl_NewStringObj(pText, nText)
   767         -  );
   768         -  if( rc==TCL_OK ){
   769         -    rc = Tcl_EvalObjEx(pInst->interp, pEval, TCL_GLOBAL_ONLY);
          786  +  switch( flags ){
          787  +    case FTS5_TOKENIZE_DOCUMENT:
          788  +      zFlags = "document";
          789  +      break;
          790  +    case FTS5_TOKENIZE_AUX:
          791  +      zFlags = "aux";
          792  +      break;
          793  +    case FTS5_TOKENIZE_QUERY:
          794  +      zFlags = "query";
          795  +      break;
          796  +    case (FTS5_TOKENIZE_PREFIX | FTS5_TOKENIZE_QUERY):
          797  +      zFlags = "prefixquery";
          798  +      break;
          799  +    default:
          800  +      assert( 0 );
          801  +      zFlags = "invalid";
          802  +      break;
   770    803     }
          804  +
          805  +  Tcl_ListObjAppendElement(pInst->interp, pEval, Tcl_NewStringObj(zFlags, -1));
          806  +  Tcl_ListObjAppendElement(pInst->interp, pEval, Tcl_NewStringObj(pText,nText));
          807  +  rc = Tcl_EvalObjEx(pInst->interp, pEval, TCL_GLOBAL_ONLY);
   771    808     Tcl_DecrRefCount(pEval);
   772    809   
   773    810     pInst->pContext->pCtx = pOldCtx;
   774    811     pInst->pContext->xToken = xOldToken;
   775    812     return rc;
   776    813   }
   777    814   
   778    815   /*
   779         -** sqlite3_fts5_token TEXT START END POS
          816  +** sqlite3_fts5_token ?-colocated? TEXT START END
   780    817   */
   781    818   static int f5tTokenizerReturn(
   782    819     void * clientData,
   783    820     Tcl_Interp *interp,
   784    821     int objc,
   785    822     Tcl_Obj *CONST objv[]
   786    823   ){
   787    824     F5tTokenizerContext *p = (F5tTokenizerContext*)clientData;
   788    825     int iStart;
   789    826     int iEnd;
   790    827     int nToken;
          828  +  int tflags = 0;
   791    829     char *zToken;
   792    830     int rc;
   793    831   
   794         -  assert( p );
   795         -  if( objc!=4 ){
   796         -    Tcl_WrongNumArgs(interp, 1, objv, "TEXT START END");
          832  +  if( objc==5 ){
          833  +    int nArg;
          834  +    char *zArg = Tcl_GetStringFromObj(objv[1], &nArg);
          835  +    if( nArg<=10 && nArg>=2 && memcmp("-colocated", zArg, nArg)==0 ){
          836  +      tflags |= FTS5_TOKEN_COLOCATED;
          837  +    }else{
          838  +      goto usage;
          839  +    }
          840  +  }else if( objc!=4 ){
          841  +    goto usage;
          842  +  }
          843  +
          844  +  zToken = Tcl_GetStringFromObj(objv[objc-3], &nToken);
          845  +  if( Tcl_GetIntFromObj(interp, objv[objc-2], &iStart) 
          846  +   || Tcl_GetIntFromObj(interp, objv[objc-1], &iEnd) 
          847  +  ){
   797    848       return TCL_ERROR;
   798    849     }
          850  +
   799    851     if( p->xToken==0 ){
   800    852       Tcl_AppendResult(interp, 
   801    853           "sqlite3_fts5_token may only be used by tokenizer callback", 0
   802    854       );
   803    855       return TCL_ERROR;
   804    856     }
   805    857   
   806         -  zToken = Tcl_GetStringFromObj(objv[1], &nToken);
   807         -  if( Tcl_GetIntFromObj(interp, objv[2], &iStart) 
   808         -   || Tcl_GetIntFromObj(interp, objv[3], &iEnd) 
   809         -  ){
   810         -    return TCL_ERROR;
   811         -  }
   812         -
   813         -  rc = p->xToken(p->pCtx, zToken, nToken, iStart, iEnd);
          858  +  rc = p->xToken(p->pCtx, tflags, zToken, nToken, iStart, iEnd);
   814    859     Tcl_SetResult(interp, (char*)sqlite3ErrName(rc), TCL_VOLATILE);
   815    860     return TCL_OK;
          861  +
          862  + usage:
          863  +  Tcl_WrongNumArgs(interp, 1, objv, "?-colocated? TEXT START END");
          864  +  return TCL_ERROR;
   816    865   }
   817    866   
   818    867   static void f5tDelTokenizer(void *pCtx){
   819    868     F5tTokenizerModule *pMod = (F5tTokenizerModule*)pCtx;
   820    869     Tcl_DecrRefCount(pMod->pScript);
   821    870     ckfree((char *)pMod);
   822    871   }

Changes to ext/fts5/fts5_test_mi.c.

   348    348     Fts5Context *pFts,              /* First arg to pass to pApi functions */
   349    349     sqlite3_context *pCtx,          /* Context for returning result/error */
   350    350     int nVal,                       /* Number of values in apVal[] array */
   351    351     sqlite3_value **apVal           /* Array of trailing arguments */
   352    352   ){
   353    353     const char *zArg;
   354    354     Fts5MatchinfoCtx *p;
   355         -  int rc;
          355  +  int rc = SQLITE_OK;
   356    356   
   357    357     if( nVal>0 ){
   358    358       zArg = (const char*)sqlite3_value_text(apVal[0]);
   359    359     }else{
   360    360       zArg = "pcx";
   361    361     }
   362    362   
   363    363     p = (Fts5MatchinfoCtx*)pApi->xGetAuxdata(pFts, 0);
   364    364     if( p==0 || sqlite3_stricmp(zArg, p->zArg) ){
   365    365       p = fts5MatchinfoNew(pApi, pFts, pCtx, zArg);
   366         -    pApi->xSetAuxdata(pFts, p, sqlite3_free);
   367         -    if( p==0 ) return;
          366  +    if( p==0 ){
          367  +      rc = SQLITE_NOMEM;
          368  +    }else{
          369  +      rc = pApi->xSetAuxdata(pFts, p, sqlite3_free);
          370  +    }
   368    371     }
   369    372   
   370         -  rc = fts5MatchinfoIter(pApi, pFts, p, fts5MatchinfoLocalCb);
          373  +  if( rc==SQLITE_OK ){
          374  +    rc = fts5MatchinfoIter(pApi, pFts, p, fts5MatchinfoLocalCb);
          375  +  }
   371    376     if( rc!=SQLITE_OK ){
   372    377       sqlite3_result_error_code(pCtx, rc);
   373    378     }else{
   374    379       /* No errors has occured, so return a copy of the array of integers. */
   375    380       int nByte = p->nRet * sizeof(u32);
   376    381       sqlite3_result_blob(pCtx, (void*)p->aRet, nByte, SQLITE_TRANSIENT);
   377    382     }

Changes to ext/fts5/fts5_tokenize.c.

   112    112   
   113    113   /*
   114    114   ** Tokenize some text using the ascii tokenizer.
   115    115   */
   116    116   static int fts5AsciiTokenize(
   117    117     Fts5Tokenizer *pTokenizer,
   118    118     void *pCtx,
          119  +  int flags,
   119    120     const char *pText, int nText,
   120         -  int (*xToken)(void*, const char*, int nToken, int iStart, int iEnd)
          121  +  int (*xToken)(void*, int, const char*, int nToken, int iStart, int iEnd)
   121    122   ){
   122    123     AsciiTokenizer *p = (AsciiTokenizer*)pTokenizer;
   123    124     int rc = SQLITE_OK;
   124    125     int ie;
   125    126     int is = 0;
   126    127   
   127    128     char aFold[64];
................................................................................
   154    155           break;
   155    156         }
   156    157         nFold = nByte*2;
   157    158       }
   158    159       asciiFold(pFold, &pText[is], nByte);
   159    160   
   160    161       /* Invoke the token callback */
   161         -    rc = xToken(pCtx, pFold, nByte, is, ie);
          162  +    rc = xToken(pCtx, 0, pFold, nByte, is, ie);
   162    163       is = ie+1;
   163    164     }
   164    165     
   165    166     if( pFold!=aFold ) sqlite3_free(pFold);
   166    167     if( rc==SQLITE_DONE ) rc = SQLITE_OK;
   167    168     return rc;
   168    169   }
................................................................................
   381    382     assert( (sqlite3Fts5UnicodeIsalnum(iCode) & 0xFFFFFFFE)==0 );
   382    383     return sqlite3Fts5UnicodeIsalnum(iCode) ^ fts5UnicodeIsException(p, iCode);
   383    384   }
   384    385   
   385    386   static int fts5UnicodeTokenize(
   386    387     Fts5Tokenizer *pTokenizer,
   387    388     void *pCtx,
          389  +  int flags,
   388    390     const char *pText, int nText,
   389         -  int (*xToken)(void*, const char*, int nToken, int iStart, int iEnd)
          391  +  int (*xToken)(void*, int, const char*, int nToken, int iStart, int iEnd)
   390    392   ){
   391    393     Unicode61Tokenizer *p = (Unicode61Tokenizer*)pTokenizer;
   392    394     int rc = SQLITE_OK;
   393    395     unsigned char *a = p->aTokenChar;
   394    396   
   395    397     unsigned char *zTerm = (unsigned char*)&pText[nText];
   396    398     unsigned char *zCsr = (unsigned char *)pText;
................................................................................
   471    473           }
   472    474           zCsr++;
   473    475         }
   474    476         ie = zCsr - (unsigned char*)pText;
   475    477       }
   476    478   
   477    479       /* Invoke the token callback */
   478         -    rc = xToken(pCtx, aFold, zOut-aFold, is, ie);
          480  +    rc = xToken(pCtx, 0, aFold, zOut-aFold, is, ie); 
   479    481     }
   480    482     
   481    483    tokenize_done:
   482    484     if( rc==SQLITE_DONE ) rc = SQLITE_OK;
   483    485     return rc;
   484    486   }
   485    487   
................................................................................
   549    551     *ppOut = (Fts5Tokenizer*)pRet;
   550    552     return rc;
   551    553   }
   552    554   
   553    555   typedef struct PorterContext PorterContext;
   554    556   struct PorterContext {
   555    557     void *pCtx;
   556         -  int (*xToken)(void*, const char*, int, int, int);
          558  +  int (*xToken)(void*, int, const char*, int, int, int);
   557    559     char *aBuf;
   558    560   };
   559    561   
   560    562   typedef struct PorterRule PorterRule;
   561    563   struct PorterRule {
   562    564     const char *zSuffix;
   563    565     int nSuffix;
................................................................................
  1114   1116         *pnBuf = nBuf-1;
  1115   1117       }
  1116   1118     }
  1117   1119   }
  1118   1120   
  1119   1121   static int fts5PorterCb(
  1120   1122     void *pCtx, 
         1123  +  int tflags,
  1121   1124     const char *pToken, 
  1122   1125     int nToken, 
  1123   1126     int iStart, 
  1124   1127     int iEnd
  1125   1128   ){
  1126   1129     PorterContext *p = (PorterContext*)pCtx;
  1127   1130   
................................................................................
  1171   1174     /* Step 5b. */
  1172   1175     if( nBuf>1 && aBuf[nBuf-1]=='l' 
  1173   1176      && aBuf[nBuf-2]=='l' && fts5Porter_MGt1(aBuf, nBuf-1) 
  1174   1177     ){
  1175   1178       nBuf--;
  1176   1179     }
  1177   1180   
  1178         -  return p->xToken(p->pCtx, aBuf, nBuf, iStart, iEnd);
         1181  +  return p->xToken(p->pCtx, tflags, aBuf, nBuf, iStart, iEnd);
  1179   1182   
  1180   1183    pass_through:
  1181         -  return p->xToken(p->pCtx, pToken, nToken, iStart, iEnd);
         1184  +  return p->xToken(p->pCtx, tflags, pToken, nToken, iStart, iEnd);
  1182   1185   }
  1183   1186   
  1184   1187   /*
  1185   1188   ** Tokenize using the porter tokenizer.
  1186   1189   */
  1187   1190   static int fts5PorterTokenize(
  1188   1191     Fts5Tokenizer *pTokenizer,
  1189   1192     void *pCtx,
         1193  +  int flags,
  1190   1194     const char *pText, int nText,
  1191         -  int (*xToken)(void*, const char*, int nToken, int iStart, int iEnd)
         1195  +  int (*xToken)(void*, int, const char*, int nToken, int iStart, int iEnd)
  1192   1196   ){
  1193   1197     PorterTokenizer *p = (PorterTokenizer*)pTokenizer;
  1194   1198     PorterContext sCtx;
  1195   1199     sCtx.xToken = xToken;
  1196   1200     sCtx.pCtx = pCtx;
  1197   1201     sCtx.aBuf = p->aBuf;
  1198   1202     return p->tokenizer.xTokenize(
  1199         -      p->pTokenizer, (void*)&sCtx, pText, nText, fts5PorterCb
         1203  +      p->pTokenizer, (void*)&sCtx, flags, pText, nText, fts5PorterCb
  1200   1204     );
  1201   1205   }
  1202   1206   
  1203   1207   /*
  1204   1208   ** Register all built-in tokenizers with FTS5.
  1205   1209   */
  1206   1210   int sqlite3Fts5TokenizerInit(fts5_api *pApi){
................................................................................
  1221   1225           aBuiltin[i].zName,
  1222   1226           (void*)pApi,
  1223   1227           &aBuiltin[i].x,
  1224   1228           0
  1225   1229       );
  1226   1230     }
  1227   1231   
  1228         -  return SQLITE_OK;
         1232  +  return rc;
  1229   1233   }
  1230   1234   
  1231   1235   

Changes to ext/fts5/test/fts5_common.tcl.

   290    290   proc OR {args} {
   291    291     sort_poslist [concat {*}$args]
   292    292   }
   293    293   proc NOT {a b} {
   294    294     if {[llength $b]>0} { return [list] }
   295    295     return $a
   296    296   }
          297  +
          298  +#-------------------------------------------------------------------------
          299  +# This command is similar to [split], except that it also provides the
          300  +# start and end offsets of each token. For example:
          301  +#
          302  +#   [fts5_tokenize_split "abc d ef"] -> {abc 0 3 d 4 5 ef 6 8}
          303  +#
          304  +
          305  +proc gobble_whitespace {textvar} {
          306  +  upvar $textvar t
          307  +  regexp {([ ]*)(.*)} $t -> space t
          308  +  return [string length $space]
          309  +}
          310  +
          311  +proc gobble_text {textvar wordvar} {
          312  +  upvar $textvar t
          313  +  upvar $wordvar w
          314  +  regexp {([^ ]*)(.*)} $t -> w t
          315  +  return [string length $w]
          316  +}
          317  +
          318  +proc fts5_tokenize_split {text} {
          319  +  set token ""
          320  +  set ret [list]
          321  +  set iOff [gobble_whitespace text]
          322  +  while {[set nToken [gobble_text text word]]} {
          323  +    lappend ret $word $iOff [expr $iOff+$nToken]
          324  +    incr iOff $nToken
          325  +    incr iOff [gobble_whitespace text]
          326  +  }
          327  +
          328  +  set ret
          329  +}
   297    330   

Changes to ext/fts5/test/fts5aa.test.

    47     47   }
    48     48   do_execsql_test 2.1 {
    49     49     INSERT INTO t1 VALUES('a b c', 'd e f');
    50     50   }
    51     51   
    52     52   do_test 2.2 {
    53     53     execsql { SELECT fts5_decode(id, block) FROM t1_data WHERE id==10 }
    54         -} {/{\(structure\) {lvl=0 nMerge=0 nSeg=1 {id=[0123456789]* h=0 leaves=1..1}}}/}
           54  +} {/{{structure} {lvl=0 nMerge=0 nSeg=1 {id=[0123456789]* leaves=1..1}}}/}
    55     55   
    56     56   foreach w {a b c d e f} {
    57     57     do_execsql_test 2.3.$w.asc {
    58     58       SELECT rowid FROM t1 WHERE t1 MATCH $w;
    59     59     } {1}
    60     60     do_execsql_test 2.3.$w.desc {
    61     61       SELECT rowid FROM t1 WHERE t1 MATCH $w ORDER BY rowid DESC;
................................................................................
   135    135     do_execsql_test 5.$i.1 { INSERT INTO t1 VALUES($x, $y) }
   136    136     do_execsql_test 5.$i.2 { INSERT INTO t1(t1) VALUES('integrity-check') }
   137    137     if {[set_test_counter errors]} break
   138    138   }
   139    139   
   140    140   #-------------------------------------------------------------------------
   141    141   #
   142         -breakpoint
   143    142   reset_db
   144    143   do_execsql_test 6.0 {
   145    144     CREATE VIRTUAL TABLE t1 USING fts5(x,y);
   146    145     INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
   147    146   }
   148    147   
   149    148   do_execsql_test 6.1 {
................................................................................
   197    196         set y [doc]
   198    197         set z [doc]
   199    198         set rowid [expr int(rand() * 100)]
   200    199         execsql { REPLACE INTO t1(rowid,x,y,z) VALUES($rowid, $x, $y, $z) }
   201    200       }
   202    201       execsql { INSERT INTO t1(t1) VALUES('integrity-check'); }
   203    202     } {}
          203  +  if {[set_test_counter errors]} break
   204    204   }
   205    205   
   206    206   #-------------------------------------------------------------------------
   207    207   #
   208    208   reset_db
   209    209   do_execsql_test 8.0 {
   210    210     CREATE VIRTUAL TABLE t1 USING fts5(x, prefix="1,2,3");
................................................................................
   339    339   } {}
   340    340   
   341    341   do_execsql_test 13.5 {
   342    342     SELECT rowid FROM t1 WHERE t1 MATCH 'o';
   343    343   } {1}
   344    344   
   345    345   do_execsql_test 13.6 {
   346         -  SELECT rowid FROM t1 WHERE t1 MATCH '.';
          346  +  SELECT rowid FROM t1 WHERE t1 MATCH '""';
   347    347   } {}
   348    348   
   349    349   #-------------------------------------------------------------------------
   350    350   #
   351    351   reset_db
   352    352   do_execsql_test 14.1 {
   353    353     CREATE VIRTUAL TABLE t1 USING fts5(x, y);
................................................................................
   501    501   }
   502    502   do_execsql_test 18.2 {
   503    503     SELECT t1.rowid, t2.rowid FROM t1, t2 WHERE t2 MATCH t1.a AND t1.rowid = t2.c
   504    504   } {1 1}
   505    505   do_execsql_test 18.3 {
   506    506     SELECT t1.rowid, t2.rowid FROM t2, t1 WHERE t2 MATCH t1.a AND t1.rowid = t2.c
   507    507   } {1 1}
          508  +
          509  +#--------------------------------------------------------------------
          510  +# fts5 table in the temp schema.
          511  +#
          512  +reset_db
          513  +do_execsql_test 19.0 {
          514  +  CREATE VIRTUAL TABLE temp.t1 USING fts5(x);
          515  +  INSERT INTO t1 VALUES('x y z');
          516  +  INSERT INTO t1 VALUES('w x 1');
          517  +  SELECT rowid FROM t1 WHERE t1 MATCH 'x';
          518  +} {1 2}
          519  +
          520  +#--------------------------------------------------------------------
          521  +# Test that 6 and 7 byte varints can be read.
          522  +#
          523  +reset_db
          524  +do_execsql_test 20.0 {
          525  +  CREATE VIRTUAL TABLE temp.tmp USING fts5(x);
          526  +}
          527  +set ::ids [list \
          528  +  0 [expr 1<<36] [expr 2<<36] [expr 1<<43] [expr 2<<43]
          529  +]
          530  +do_test 20.1 {
          531  +  foreach id $::ids {
          532  +    execsql { INSERT INTO tmp(rowid, x) VALUES($id, 'x y z') }
          533  +  }
          534  +  execsql { SELECT rowid FROM tmp WHERE tmp MATCH 'y' }
          535  +} $::ids
          536  +
          537  +
   508    538   
   509    539   finish_test
   510    540   
   511    541   

Changes to ext/fts5/test/fts5ad.test.

   201    201           }
   202    202         }
   203    203         if {$bMatch} { lappend ret $rowid }
   204    204       }
   205    205       return $ret
   206    206     }
   207    207   
          208  +  do_execsql_test $T.integrity {
          209  +    INSERT INTO t1(t1) VALUES('integrity-check');
          210  +  }
   208    211     
   209    212     foreach {bAsc sql} {
   210    213       1 {SELECT rowid FROM t1 WHERE t1 MATCH $prefix}
   211    214       0 {SELECT rowid FROM t1 WHERE t1 MATCH $prefix ORDER BY rowid DESC}
   212    215     } {
   213    216       foreach {tn prefix} {
   214    217         1  {a*} 2 {ab*} 3 {abc*} 4 {abcd*} 5 {abcde*} 

Changes to ext/fts5/test/fts5ah.test.

    86     86     2 { SELECT rowid FROM t1 WHERE t1 MATCH 'x + w'   }  [list $W]
    87     87     3 { SELECT rowid FROM t1 WHERE t1 MATCH 'x AND w' }  [list $W]
    88     88     4 { SELECT rowid FROM t1 WHERE t1 MATCH 'y AND x' }  [list $Y]
    89     89   " {
    90     90   
    91     91     do_test 1.6.$tn.1 {
    92     92       set n [execsql_reads $q]
    93         -    puts -nonewline "(n=$n nReadX=$nReadX)"
           93  +    #puts -nonewline "(n=$n nReadX=$nReadX)"
    94     94       expr {$n < ($nReadX / 8)}
    95     95     } {1}
    96     96   
    97     97     do_test 1.6.$tn.2 {
    98     98       set n [execsql_reads "$q ORDER BY rowid DESC"]
    99         -    puts -nonewline "(n=$n nReadX=$nReadX)"
           99  +    #puts -nonewline "(n=$n nReadX=$nReadX)"
   100    100       expr {$n < ($nReadX / 8)}
   101    101     } {1}
   102    102   
   103    103     do_execsql_test 1.6.$tn.3 $q [lsort -int -incr $res]
   104    104     do_execsql_test 1.6.$tn.4 "$q ORDER BY rowid DESC" [lsort -int -decr $res]
   105    105   }
   106    106   

Changes to ext/fts5/test/fts5al.test.

    22     22     finish_test
    23     23     return
    24     24   }
    25     25   
    26     26   do_execsql_test 1.1 {
    27     27     CREATE VIRTUAL TABLE ft1 USING fts5(x);
    28     28     SELECT * FROM ft1_config;
    29         -} {version 3}
           29  +} {version 4}
    30     30   
    31     31   do_execsql_test 1.2 {
    32     32     INSERT INTO ft1(ft1, rank) VALUES('pgsz', 32);
    33     33     SELECT * FROM ft1_config;
    34         -} {pgsz 32 version 3}
           34  +} {pgsz 32 version 4}
    35     35   
    36     36   do_execsql_test 1.3 {
    37     37     INSERT INTO ft1(ft1, rank) VALUES('pgsz', 64);
    38     38     SELECT * FROM ft1_config;
    39         -} {pgsz 64 version 3}
           39  +} {pgsz 64 version 4}
    40     40   
    41     41   #--------------------------------------------------------------------------
    42     42   # Test the logic for parsing the rank() function definition.
    43     43   #
    44     44   foreach {tn defn} {
    45     45     1 "fname()"
    46     46     2 "fname(1)"

Changes to ext/fts5/test/fts5columnsize.test.

   130    130   }
   131    131   do_execsql_test 3.2.1 {
   132    132     SELECT rowid, fts5_test_columnsize(t4) FROM t4 WHERE t4 MATCH 'a'
   133    133   } {
   134    134     1 {-1 0 -1} 2 {-1 0 -1}
   135    135   }
   136    136   
          137  +#-------------------------------------------------------------------------
          138  +# Test the integrity-check
          139  +#
          140  +do_execsql_test 4.1.1 {
          141  +  CREATE VIRTUAL TABLE t5 USING fts5(x, columnsize=0);
          142  +  INSERT INTO t5 VALUES('1 2 3 4');
          143  +  INSERT INTO t5 VALUES('2 4 6 8');
          144  +}
          145  +
          146  +breakpoint
          147  +do_execsql_test 4.1.2 {
          148  +  INSERT INTO t5(t5) VALUES('integrity-check');
          149  +}
   137    150   
   138    151   finish_test

Changes to ext/fts5/test/fts5corrupt.test.

    39     39   db_save
    40     40   
    41     41   do_execsql_test 1.2 { INSERT INTO t1(t1) VALUES('integrity-check') }
    42     42   set segid [lindex [fts5_level_segids t1] 0]
    43     43   
    44     44   do_test 1.3 {
    45     45     execsql {
    46         -    DELETE FROM t1_data WHERE rowid = fts5_rowid('segment', $segid, 0, 4);
           46  +    DELETE FROM t1_data WHERE rowid = fts5_rowid('segment', $segid, 4);
    47     47     }
    48     48     catchsql { INSERT INTO t1(t1) VALUES('integrity-check') }
    49     49   } {1 {database disk image is malformed}}
    50     50   
    51     51   do_test 1.4 {
    52     52     db_restore_and_reopen
    53     53     execsql {
    54     54       UPDATE t1_data set block = X'00000000' || substr(block, 5) WHERE
    55         -    rowid = fts5_rowid('segment', $segid, 0, 4);
           55  +    rowid = fts5_rowid('segment', $segid, 4);
    56     56     }
    57     57     catchsql { INSERT INTO t1(t1) VALUES('integrity-check') }
    58     58   } {1 {database disk image is malformed}}
    59     59   
    60     60   db_restore_and_reopen
    61     61   #db eval {SELECT rowid, fts5_decode(rowid, block) aS r FROM t1_data} {puts $r}
    62     62   

Changes to ext/fts5/test/fts5corrupt2.test.

   205    205         if {$res == "1 {database disk image is malformed}"} {incr nCorrupt}
   206    206         set {} 1
   207    207       } {1}
   208    208   
   209    209       execsql ROLLBACK
   210    210     }
   211    211   
   212         -  do_test 4.$tn.x { expr $nCorrupt>0 } 1
          212  +  # do_test 4.$tn.x { expr $nCorrupt>0 } 1
   213    213   }
   214    214   
   215    215   }
   216    216   
   217    217   set doc [string repeat "A B C " 1000]
   218         -do_execsql_test 4.0 {
          218  +do_execsql_test 5.0 {
   219    219     CREATE VIRTUAL TABLE x5 USING fts5(tt);
   220    220     INSERT INTO x5(x5, rank) VALUES('pgsz', 32);
   221    221     WITH ii(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM ii WHERE i<10) 
   222    222     INSERT INTO x5 SELECT $doc FROM ii;
   223    223   }
   224    224   
   225    225   foreach {tn hdr} {
................................................................................
   226    226     1 "\x00\x01"
   227    227   } {
   228    228     set tn2 0
   229    229     set nCorrupt 0
   230    230     foreach rowid [db eval {SELECT rowid FROM x5_data WHERE rowid>10}] {
   231    231       if {$rowid & $mask} continue
   232    232       incr tn2
   233         -    do_test 4.$tn.$tn2 {
          233  +    do_test 5.$tn.$tn2 {
   234    234         execsql BEGIN
   235    235   
   236    236         set fd [db incrblob main x5_data block $rowid]
   237    237         fconfigure $fd -encoding binary -translation binary
   238    238         puts -nonewline $fd $hdr
   239    239         close $fd
   240    240   
................................................................................
   244    244   
   245    245       execsql ROLLBACK
   246    246     }
   247    247   }
   248    248   
   249    249   #--------------------------------------------------------------------
   250    250   reset_db
   251         -do_execsql_test 5.1 {
          251  +do_execsql_test 6.1 {
   252    252     CREATE VIRTUAL TABLE x5 USING fts5(tt);
   253    253     INSERT INTO x5 VALUES('a');
   254    254     INSERT INTO x5 VALUES('a a');
   255    255     INSERT INTO x5 VALUES('a a a');
   256    256     INSERT INTO x5 VALUES('a a a a');
   257    257   
   258    258     UPDATE x5_docsize SET sz = X'' WHERE id=3;
   259    259   }
   260    260   proc colsize {cmd i} { 
   261    261     $cmd xColumnSize $i
   262    262   }
   263    263   sqlite3_fts5_create_function db colsize colsize
   264    264   
   265         -do_catchsql_test 5.2 {
          265  +do_catchsql_test 6.2 {
   266    266     SELECT colsize(x5, 0) FROM x5 WHERE x5 MATCH 'a'
   267    267   } {1 SQLITE_CORRUPT_VTAB}
   268    268   
   269    269   
   270    270   sqlite3_fts5_may_be_corrupt 0
   271    271   finish_test
   272    272   

Changes to ext/fts5/test/fts5corrupt3.test.

    19     19   
    20     20   # If SQLITE_ENABLE_FTS5 is defined, omit this file.
    21     21   ifcapable !fts5 {
    22     22     finish_test
    23     23     return
    24     24   }
    25     25   sqlite3_fts5_may_be_corrupt 1
           26  +
           27  +proc create_t1 {} {
           28  +  expr srand(0)
           29  +  db func rnddoc fts5_rnddoc
           30  +  db eval {
           31  +    CREATE VIRTUAL TABLE t1 USING fts5(x);
           32  +    INSERT INTO t1(t1, rank) VALUES('pgsz', 64);
           33  +    WITH ii(i) AS (SELECT 1 UNION SELECT i+1 FROM ii WHERE i<100)
           34  +      INSERT INTO t1 SELECT rnddoc(10) FROM ii;
           35  +  }
           36  +}
           37  +
           38  +if 1 {
    26     39   
    27     40   # Create a simple FTS5 table containing 100 documents. Each document 
    28     41   # contains 10 terms, each of which start with the character "x".
    29     42   #
    30         -expr srand(0)
    31         -db func rnddoc fts5_rnddoc
    32         -do_execsql_test 1.0 {
    33         -  CREATE VIRTUAL TABLE t1 USING fts5(x);
    34         -  INSERT INTO t1(t1, rank) VALUES('pgsz', 64);
    35         -  WITH ii(i) AS (SELECT 1 UNION SELECT i+1 FROM ii WHERE i<100)
    36         -  INSERT INTO t1 SELECT rnddoc(10) FROM ii;
    37         -}
    38         -set mask [expr 31 << 31]
           43  +do_test 1.0 { create_t1 } {}
    39     44   
    40     45   do_test 1.1 {
    41     46     # Pick out the rowid of the right-most b-tree leaf in the new segment.
    42     47     set rowid [db one {
    43     48       SELECT max(rowid) FROM t1_data WHERE ((rowid>>31) & 0x0F)==1
    44     49     }]
    45     50     set L [db one {SELECT length(block) FROM t1_data WHERE rowid = $rowid}]
................................................................................
    71     76     SELECT length(block) FROM t2_data WHERE id=1;
    72     77   } {6}
    73     78   do_execsql_test 2.2 {
    74     79     INSERT INTO t2 VALUES(rnddoc(10));
    75     80     SELECT length(block) FROM t2_data WHERE id=1;
    76     81   } {2}
    77     82   
           83  +
           84  +#-------------------------------------------------------------------------
           85  +# Test that missing leaf pages are recognized as corruption.
           86  +#
           87  +reset_db
           88  +do_test 3.0 { create_t1 } {}
           89  +
           90  +do_execsql_test 3.1 {
           91  +  SELECT count(*) FROM t1_data;
           92  +} {105}
           93  +
           94  +proc do_3_test {tn} {
           95  +  set i 0
           96  +  foreach ::rowid [db eval "SELECT rowid FROM t1_data WHERE rowid>100"] {
           97  +    incr i
           98  +    do_test $tn.$i {
           99  +      db eval BEGIN
          100  +      db eval {DELETE FROM t1_data WHERE rowid = $::rowid}
          101  +      list [
          102  +        catch { db eval {SELECT rowid FROM t1 WHERE t1 MATCH 'x*'} } msg
          103  +      ] $msg
          104  +    } {1 {database disk image is malformed}}
          105  +    catch { db eval ROLLBACK }
          106  +  }
          107  +}
          108  +
          109  +do_3_test 3.2
          110  +
          111  +do_execsql_test 3.3 {
          112  +  INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
          113  +  INSERT INTO t1 SELECT x FROM t1;
          114  +  INSERT INTO t1(t1) VALUES('optimize');
          115  +} {}
          116  +
          117  +do_3_test 3.4
          118  +
          119  +do_test 3.5 {
          120  +  execsql { 
          121  +    DELETE FROM t1;
          122  +    INSERT INTO t1(t1, rank) VALUES('pgsz', 40);
          123  +  }
          124  +  for {set i 0} {$i < 1000} {incr i} {
          125  +    set rnd [expr int(rand() * 1000)]
          126  +    set doc [string repeat "x$rnd " [expr int(rand() * 3) + 1]]
          127  +    execsql { INSERT INTO t1(rowid, x) VALUES($i, $doc) }
          128  +  }
          129  +} {}
          130  +
          131  +do_3_test 3.6
          132  +
          133  +do_test 3.7 {
          134  +  execsql {
          135  +    INSERT INTO t1(t1, rank) VALUES('pgsz', 40);
          136  +    INSERT INTO t1 SELECT x FROM t1;
          137  +    INSERT INTO t1(t1) VALUES('optimize');
          138  +  }
          139  +} {}
          140  +
          141  +do_3_test 3.8
          142  +
          143  +do_test 3.9 {
          144  +  execsql { 
          145  +    DELETE FROM t1;
          146  +    INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
          147  +  }
          148  +  for {set i 0} {$i < 100} {incr i} {
          149  +    set rnd [expr int(rand() * 100)]
          150  +    set doc "x[string repeat $rnd 20]"
          151  +    execsql { INSERT INTO t1(rowid, x) VALUES($i, $doc) }
          152  +  }
          153  +} {}
          154  +
          155  +do_3_test 3.10
          156  +
          157  +#-------------------------------------------------------------------------
          158  +# Test that segments that end unexpectedly are identified as corruption.
          159  +#
          160  +reset_db
          161  +do_test 4.0 {
          162  +  execsql { 
          163  +    CREATE VIRTUAL TABLE t1 USING fts5(x);
          164  +    INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
          165  +  }
          166  +  for {set i 0} {$i < 100} {incr i} {
          167  +    set rnd [expr int(rand() * 100)]
          168  +    set doc "x[string repeat $rnd 20]"
          169  +    execsql { INSERT INTO t1(rowid, x) VALUES($i, $doc) }
          170  +  }
          171  +  execsql { INSERT INTO t1(t1) VALUES('optimize') }
          172  +} {}
          173  +
          174  +set nErr 0
          175  +for {set i 1} {1} {incr i} {
          176  +  set struct [db one {SELECT block FROM t1_data WHERE id=10}]
          177  +  binary scan $struct c* var
          178  +  set end [lindex $var end]
          179  +  if {$end<=$i} break
          180  +  lset var end [expr $end - $i]
          181  +  set struct [binary format c* $var]
          182  +  db eval {
          183  +    BEGIN;
          184  +    UPDATE t1_data SET block = $struct WHERE id=10;
          185  +  }
          186  +  do_test 4.1.$i {
          187  +    incr nErr [catch { db eval { SELECT rowid FROM t1 WHERE t1 MATCH 'x*' } }]
          188  +    set {} {}
          189  +  } {}
          190  +  catch { db eval ROLLBACK }
          191  +}
          192  +do_test 4.1.x { expr $nErr>45 } 1
          193  +
          194  +#-------------------------------------------------------------------------
          195  +#
          196  +
          197  +# The first argument passed to this command must be a binary blob 
          198  +# containing an FTS5 leaf page. This command returns a copy of this
          199  +# blob, with the pgidx of the leaf page replaced by a single varint
          200  +# containing value $iVal.
          201  +#
          202  +proc rewrite_pgidx {blob iVal} {
          203  +  binary scan $blob SS off1 szLeaf
          204  +  if {$iVal<0 || $iVal>=128} {
          205  +    error "$iVal out of range!"
          206  +  } else {
          207  +    set pgidx [binary format c $iVal]
          208  +  }
          209  +
          210  +  binary format a${szLeaf}a* $blob $pgidx
          211  +}
          212  +
          213  +reset_db
          214  +do_execsql_test 5.1 {
          215  +  CREATE VIRTUAL TABLE x1 USING fts5(x);
          216  +  INSERT INTO x1(x1, rank) VALUES('pgsz', 40);
          217  +  BEGIN;
          218  +  INSERT INTO x1 VALUES('xaaa xabb xccc xcdd xeee xeff xggg xghh xiii xijj');
          219  +  INSERT INTO x1 SELECT x FROM x1;
          220  +  INSERT INTO x1 SELECT x FROM x1;
          221  +  INSERT INTO x1 SELECT x FROM x1;
          222  +  INSERT INTO x1 SELECT x FROM x1;
          223  +  INSERT INTO x1(x1) VALUES('optimize');
          224  +  COMMIT;
          225  +}
          226  +
          227  +#db eval { SELECT fts5_decode(id, block) b from x1_data } { puts $b }
          228  +#
          229  +db func rewrite_pgidx rewrite_pgidx  
          230  +set i 0
          231  +foreach rowid [db eval {SELECT rowid FROM x1_data WHERE rowid>100}] {
          232  +  foreach val {2 100} {
          233  +    do_test 5.2.$val.[incr i] {
          234  +      catchsql {
          235  +        BEGIN;
          236  +        UPDATE x1_data SET block=rewrite_pgidx(block, $val) WHERE id=$rowid;
          237  +        SELECT rowid FROM x1 WHERE x1 MATCH 'xa*';
          238  +        SELECT rowid FROM x1 WHERE x1 MATCH 'xb*';
          239  +        SELECT rowid FROM x1 WHERE x1 MATCH 'xc*';
          240  +        SELECT rowid FROM x1 WHERE x1 MATCH 'xd*';
          241  +        SELECT rowid FROM x1 WHERE x1 MATCH 'xe*';
          242  +        SELECT rowid FROM x1 WHERE x1 MATCH 'xf*';
          243  +        SELECT rowid FROM x1 WHERE x1 MATCH 'xg*';
          244  +        SELECT rowid FROM x1 WHERE x1 MATCH 'xh*';
          245  +        SELECT rowid FROM x1 WHERE x1 MATCH 'xi*';
          246  +      }
          247  +      set {} {}
          248  +    } {}
          249  +    catch { db eval ROLLBACK }
          250  +  }
          251  +}
          252  +
          253  +}
          254  +
          255  +#------------------------------------------------------------------------
          256  +#
          257  +reset_db
          258  +do_execsql_test 6.1.0 {
          259  +  CREATE VIRTUAL TABLE t1 USING fts5(a);
          260  +  INSERT INTO t1 VALUES('bbbbb ccccc');
          261  +  SELECT quote(block) FROM t1_data WHERE rowid>100;
          262  +} {X'000000180630626262626201020201056363636363010203040A'}
          263  +do_execsql_test 6.1.1 {
          264  +  UPDATE t1_data SET block = 
          265  +  X'000000180630626262626201020201056161616161010203040A'
          266  +  WHERE rowid>100;
          267  +}
          268  +do_catchsql_test 6.1.2 {
          269  +  INSERT INTO t1(t1) VALUES('integrity-check');
          270  +} {1 {database disk image is malformed}}
          271  +
          272  +#-------
          273  +reset_db
          274  +do_execsql_test 6.2.0 {
          275  +  CREATE VIRTUAL TABLE t1 USING fts5(a);
          276  +  INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
          277  +  INSERT INTO t1 VALUES('aa bb cc dd ee');
          278  +  SELECT pgno, quote(term) FROM t1_idx;
          279  +} {2 X'' 4 X'3064'}
          280  +do_execsql_test 6.2.1 {
          281  +  UPDATE t1_idx SET term = X'3065' WHERE pgno=4;
          282  +}
          283  +do_catchsql_test 6.2.2 {
          284  +  INSERT INTO t1(t1) VALUES('integrity-check');
          285  +} {1 {database disk image is malformed}}
          286  +
          287  +#-------
          288  +reset_db
          289  +do_execsql_test 6.3.0 {
          290  +  CREATE VIRTUAL TABLE t1 USING fts5(a);
          291  +  INSERT INTO t1 VALUES('abc abcdef abcdefghi');
          292  +  SELECT quote(block) FROM t1_data WHERE id>100;
          293  +}    {X'0000001C043061626301020204036465660102030703676869010204040808'}
          294  +do_execsql_test 6.3.1 {
          295  +  BEGIN;
          296  +    UPDATE t1_data SET block = 
          297  +      X'0000001C043061626301020204036465660102035003676869010204040808'
          298  +      ------------------------------------------^^---------------------
          299  +    WHERE id>100;
          300  +}
          301  +do_catchsql_test 6.3.2 {
          302  +  INSERT INTO t1(t1) VALUES('integrity-check');
          303  +} {1 {database disk image is malformed}}
          304  +do_execsql_test 6.3.3 {
          305  +  ROLLBACK;
          306  +  BEGIN;
          307  +    UPDATE t1_data SET block = 
          308  +      X'0000001C043061626301020204036465660102030750676869010204040808'
          309  +      --------------------------------------------^^-------------------
          310  +    WHERE id>100;
          311  +}
          312  +do_catchsql_test 6.3.3 {
          313  +  INSERT INTO t1(t1) VALUES('integrity-check');
          314  +} {1 {database disk image is malformed}}
          315  +do_execsql_test 6.3.4 {
          316  +  ROLLBACK;
          317  +  BEGIN;
          318  +    UPDATE t1_data SET block = 
          319  +      X'0000001C043061626301020204036465660102030707676869010204040850'
          320  +      --------------------------------------------------------------^^-
          321  +    WHERE id>100;
          322  +}
          323  +do_catchsql_test 6.3.5 {
          324  +  INSERT INTO t1(t1) VALUES('integrity-check');
          325  +} {1 {database disk image is malformed}}
          326  +do_execsql_test 6.3.6 {
          327  +  ROLLBACK;
          328  +  BEGIN;
          329  +    UPDATE t1_data SET block = 
          330  +      X'0000001C503061626301020204036465660102030707676869010204040808'
          331  +      ----------^^-----------------------------------------------------
          332  +    WHERE id>100;
          333  +}
          334  +do_catchsql_test 6.3.5 {
          335  +  INSERT INTO t1(t1) VALUES('integrity-check');
          336  +} {1 {database disk image is malformed}}
          337  +
    78    338   sqlite3_fts5_may_be_corrupt 0
    79    339   finish_test
    80    340   

Changes to ext/fts5/test/fts5dlidx.test.

    21     21     return
    22     22   }
    23     23   
    24     24   if { $tcl_platform(wordSize)<8 } {
    25     25     finish_test
    26     26     return
    27     27   }
           28  +
           29  +if 1 {
    28     30   
    29     31   proc do_fb_test {tn sql res} {
    30     32     set res2 [lsort -integer -decr $res]
    31     33     uplevel [list do_execsql_test $tn.1 $sql $res]
    32     34     uplevel [list do_execsql_test $tn.2 "$sql ORDER BY rowid DESC" $res2]
    33     35   }
    34     36   
................................................................................
   123    125     breakpoint
   124    126     do_execsql_test $tn.2 {
   125    127       SELECT rowid FROM t1 WHERE t1 MATCH 'b AND a' ORDER BY rowid DESC
   126    128     } {1}
   127    129   }
   128    130   
   129    131   do_dlidx_test2 2.1 [expr 20] [expr 1<<57] [expr (1<<57) + 128]
          132  +
          133  +}
          134  +
          135  +#--------------------------------------------------------------------
          136  +#
          137  +reset_db
          138  +
          139  +set ::vocab [list \
          140  +  IteratorpItercurrentlypointstothefirstrowidofadoclist \
          141  +  Thereisadoclistindexassociatedwiththefinaltermonthecurrent \
          142  +  pageIfthecurrenttermisthelasttermonthepageloadthe \
          143  +  doclistindexfromdiskandinitializeaniteratoratpIterpDlidx \
          144  +  IteratorpItercurrentlypointstothefirstrowidofadoclist \
          145  +  Thereisadoclistindexassociatedwiththefinaltermonthecurrent \
          146  +  pageIfthecurrenttermisthelasttermonthepageloadthe \
          147  +  doclistindexfromdiskandinitializeaniteratoratpIterpDlidx \
          148  +]
          149  +proc rnddoc {} {
          150  +  global vocab
          151  +  set nVocab [llength $vocab]
          152  +  set ret [list]
          153  +  for {set i 0} {$i < 64} {incr i} {
          154  +    lappend ret [lindex $vocab [expr $i % $nVocab]]
          155  +  }
          156  +  set ret
          157  +}
          158  +db func rnddoc rnddoc
          159  +
          160  +do_execsql_test 3.1 {
          161  +  CREATE VIRTUAL TABLE abc USING fts5(a);
          162  +  INSERT INTO abc(abc, rank) VALUES('pgsz', 32);
          163  +
          164  +  INSERT INTO abc VALUES ( rnddoc() );
          165  +  INSERT INTO abc VALUES ( rnddoc() );
          166  +  INSERT INTO abc VALUES ( rnddoc() );
          167  +  INSERT INTO abc VALUES ( rnddoc() );
          168  +
          169  +  INSERT INTO abc SELECT rnddoc() FROM abc;
          170  +  INSERT INTO abc SELECT rnddoc() FROM abc;
          171  +}
          172  +
          173  +
          174  +
          175  +do_execsql_test 3.2 {
          176  +  SELECT rowid FROM abc WHERE abc 
          177  +  MATCH 'IteratorpItercurrentlypointstothefirstrowidofadoclist' 
          178  +  ORDER BY rowid DESC;
          179  +} {16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1}
          180  +
          181  +do_execsql_test 3.2 {
          182  +  INSERT INTO abc(abc) VALUES('integrity-check');
          183  +  INSERT INTO abc(abc) VALUES('optimize');
          184  +  INSERT INTO abc(abc) VALUES('integrity-check');
          185  +}
          186  +
          187  +set v [lindex $vocab 0]
          188  +set i 0
          189  +foreach v $vocab {
          190  +  do_execsql_test 3.3.[incr i] {
          191  +    SELECT rowid FROM abc WHERE abc MATCH $v
          192  +  } {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16}
          193  +}
          194  +
   130    195   
   131    196   finish_test
   132    197   

Changes to ext/fts5/test/fts5ea.test.

    83     83   #-------------------------------------------------------------------------
    84     84   # Experiment with a tokenizer that considers " to be a token character.
    85     85   #
    86     86   do_execsql_test 4.0 {
    87     87     SELECT fts5_expr('a AND """"', 'x', 'tokenize="unicode61 tokenchars ''""''"');
    88     88   } {{"a" AND """"}}
    89     89   
           90  +#-------------------------------------------------------------------------
           91  +# Experiment with a tokenizer that considers " to be a token character.
           92  +#
           93  +do_catchsql_test 5.0 {
           94  +  SELECT fts5_expr('abc | def');
           95  +} {1 {fts5: syntax error near "|"}}
    90     96   
    91     97   
    92     98   
    93     99   finish_test

Changes to ext/fts5/test/fts5eb.test.

    26     26   
    27     27   proc do_syntax_test {tn expr res} {
    28     28     set ::se_expr $expr
    29     29     do_execsql_test $tn {SELECT fts5_expr($se_expr)} [list $res]
    30     30   }
    31     31   
    32     32   foreach {tn expr res} {
    33         -  1  {abc}                           {"abc"}
    34         -  2  {abc .}                         {"abc"}
    35         -  3  {.}                             {}
    36         -  4  {abc OR .}                      {"abc"}
    37         -  5  {abc NOT .}                     {"abc"}
    38         -  6  {abc AND .}                     {"abc"}
    39         -  7  {. OR abc}                      {"abc"}
    40         -  8  {. NOT abc}                     {"abc"}
    41         -  9  {. AND abc}                     {"abc"}
    42         -  10 {abc + . + def}                 {"abc" + "def"}
    43         -  11 {abc . def}                     {"abc" AND "def"}
    44         -  12 {r+e OR w}                      {"r" + "e" OR "w"}
           33  +  1  {abc}                            {"abc"}
           34  +  2  {abc ""}                         {"abc"}
           35  +  3  {""}                             {}
           36  +  4  {abc OR ""}                      {"abc"}
           37  +  5  {abc NOT ""}                     {"abc"}
           38  +  6  {abc AND ""}                     {"abc"}
           39  +  7  {"" OR abc}                      {"abc"}
           40  +  8  {"" NOT abc}                     {"abc"}
           41  +  9  {"" AND abc}                     {"abc"}
           42  +  10 {abc + "" + def}                 {"abc" + "def"}
           43  +  11 {abc "" def}                     {"abc" AND "def"}
           44  +  12 {r+e OR w}                       {"r" + "e" OR "w"}
    45     45   } {
    46     46     do_execsql_test 1.$tn {SELECT fts5_expr($expr)} [list $res]
    47     47   }
    48     48   
    49     49   do_catchsql_test 2.1 {
    50     50     SELECT fts5_expr()
    51     51   } {1 {wrong number of arguments to function fts5_expr}}

Changes to ext/fts5/test/fts5fault6.test.

    17     17   set testprefix fts5fault6
    18     18   
    19     19   # If SQLITE_ENABLE_FTS5 is defined, omit this file.
    20     20   ifcapable !fts5 {
    21     21     finish_test
    22     22     return
    23     23   }
           24  +
    24     25   
    25     26   #-------------------------------------------------------------------------
    26     27   # OOM while rebuilding an FTS5 table.
    27     28   #
    28     29   do_execsql_test 1.0 {
    29     30     CREATE VIRTUAL TABLE tt USING fts5(a, b);
    30     31     INSERT INTO tt VALUES('c d c g g f', 'a a a d g a');
................................................................................
   144    145     db eval { 
   145    146       CREATE VIRTUAL TABLE yu USING fts5(x, tokenize="unicode61 separators abc");
   146    147     }
   147    148   } -test {
   148    149     faultsim_test_result {0 {}}
   149    150   }
   150    151   
          152  +#-------------------------------------------------------------------------
          153  +#
          154  +# 5.2.* OOM while running a query that includes synonyms and matchinfo().
          155  +#
          156  +# 5.3.* OOM while running a query that returns a row containing instances
          157  +#       of more than 4 synonyms for a single term.
          158  +#
          159  +proc mit {blob} {
          160  +  set scan(littleEndian) i*
          161  +  set scan(bigEndian) I*
          162  +  binary scan $blob $scan($::tcl_platform(byteOrder)) r
          163  +  return $r
          164  +}
          165  +proc tcl_tokenize {tflags text} {
          166  +  foreach {w iStart iEnd} [fts5_tokenize_split $text] {
          167  +    sqlite3_fts5_token $w $iStart $iEnd
          168  +    if {$tflags=="query" && [string length $w]==1} {
          169  +      for {set i 2} {$i < 7} {incr i} {
          170  +        sqlite3_fts5_token -colo [string repeat $w $i] $iStart $iEnd
          171  +      }
          172  +    }
          173  +  }
          174  +}
          175  +proc tcl_create {args} { return "tcl_tokenize" }
          176  +reset_db
          177  +sqlite3_fts5_create_tokenizer db tcl tcl_create
          178  +db func mit mit
          179  +sqlite3_fts5_register_matchinfo db
          180  +do_test 5.0 {
          181  +  execsql { CREATE VIRTUAL TABLE t1 USING fts5(a, tokenize=tcl) }
          182  +  execsql { INSERT INTO t1(t1, rank) VALUES('pgsz', 32) }
          183  +  foreach {rowid text} {
          184  +    1 {aaaa cc b aaaaa cc aa} 
          185  +    2 {aa aa bb a bbb}
          186  +    3 {bb aaaaa aaaaa b aaaa aaaaa}
          187  +    4 {aa a b aaaa aa}
          188  +    5 {aa b ccc aaaaa cc}
          189  +    6 {aa aaaaa bbbb cc aaa}
          190  +    7 {aaaaa aa aa ccccc bb}
          191  +    8 {ccc bbbbb ccccc bbb c}
          192  +    9 {cccccc bbbb a aaa cccc c}
          193  +
          194  +    20 {ddd f ddd eeeee fff ffff eeee ddd fff eeeee dddddd eeee}
          195  +    21 {fffff eee dddd fffff dd ee ee eeeee eee eeeeee ee dd e}
          196  +    22 {fffff d eeee dddd fffff dddddd ffff ddddd eeeee ee eee dddd ddddd}
          197  +    23 {ddddd fff ddd eeeee ffff eeee ddd ff ff ffffff eeeeee dddd ffffff}
          198  +    24 {eee dd ee dddd dddd eeeeee e eee fff ffff}
          199  +    25 {ddddd ffffff dddddd fff ddd ddddd ddd f eeee fff dddd f}
          200  +    26 {f ffff fff fff eeeeee dddd d dddddd ddddd eee ff eeeee}
          201  +    27 {eee fff dddddd eeeee eeeee dddd ddddd ffff f eeeee eee dddddd ddddd d}
          202  +    28 {dd ddddd d ddd d fff d dddd ee dddd ee ddd dddddd dddddd}
          203  +    29 {eeee dddd ee dddd eeee dddd dd fffff f ddd eeeee ddd ee}
          204  +    30 {ff ffffff eeeeee eeeee eee ffffff ff ffff f fffff eeeee}
          205  +    31 {fffff eeeeee dddd eeee eeee eeeeee eee fffff d ddddd ffffff ffff dddddd}
          206  +    32 {dddddd fffff ee eeeeee eeee ee fff dddd fff eeee ffffff eeeeee ffffff}
          207  +    33 {ddddd eeee dd ffff dddddd fff eeee ddddd ffff eeee ddd}
          208  +    34 {ee dddd ddddd dddddd eeee eeeeee f dd ee dddddd ffffff}
          209  +    35 {ee dddd dd eeeeee ddddd eee d eeeeee dddddd eee dddd fffff}
          210  +    36 {eee ffffff ffffff e fffff eeeee ff dddddd dddddd fff}
          211  +    37 {eeeee fffff dddddd dddd ffffff fff f dd ee dd dd eeeee}
          212  +    38 {eeeeee ee d ff eeeeee eeeeee eee eeeee ee ffffff dddd eeee dddddd ee}
          213  +    39 {eeeeee ddd fffff e dddd ee eee eee ffffff ee f d dddd}
          214  +    40 {ffffff dddddd eee ee ffffff eee eeee ddddd ee eeeeee f}
          215  +    41 {ddd ddd fff fffff ee fffff f fff ddddd fffff}
          216  +    42 {dddd ee ff d f ffffff fff ffffff ff dd dddddd f eeee}
          217  +    43 {d dd fff fffff d f fff e dddd ee ee}
          218  +    44 {ff ffff eee ddd d dd ffff dddd d eeee d eeeeee}
          219  +    45 {eeee f eeeee ee e ffff f ddd e fff}
          220  +    46 {ffff d ffff eeee ffff eeeee f ffff ddddd eee}
          221  +    47 {dd dd dddddd ddddd fffff dddddd ddd ddddd eeeeee ffff eeee eee ee}
          222  +    48 {ffff ffff e dddd ffffff dd dd dddd f fffff}
          223  +    49 {ffffff d dddddd ffff eeeee f ffff ffff d dd fffff eeeee}
          224  +
          225  +    50 {x e}
          226  +  } {
          227  +    execsql { INSERT INTO t1(rowid, a) VALUES($rowid, $text) }
          228  +  }
          229  +} {}
          230  +
          231  +set res [list {*}{
          232  +  1 {3 24 8 2 12 6}
          233  +  5 {2 24 8 2 12 6}
          234  +  6 {3 24 8 1 12 6}
          235  +  7 {3 24 8 1 12 6}
          236  +  9 {2 24 8 3 12 6}
          237  +}]
          238  +do_execsql_test 5.1.1 {
          239  +  SELECT rowid, mit(matchinfo(t1, 'x')) FROM t1 WHERE t1 MATCH 'a AND c'
          240  +} $res
          241  +do_execsql_test 5.1.2 {
          242  +  SELECT count(*) FROM t1 WHERE t1 MATCH 'd e f'
          243  +} 29
          244  +
          245  +faultsim_save_and_close
          246  +do_faultsim_test 5.2 -faults oom* -prep {
          247  +  faultsim_restore_and_reopen
          248  +  sqlite3_fts5_create_tokenizer db tcl tcl_create
          249  +  sqlite3_fts5_register_matchinfo db
          250  +  db func mit mit
          251  +} -body {
          252  +  db eval { 
          253  +    SELECT rowid, mit(matchinfo(t1, 'x')) FROM t1 WHERE t1 MATCH 'a AND c'
          254  +  }
          255  +} -test {
          256  +  faultsim_test_result [list 0 $::res]
          257  +}
          258  +
          259  +do_faultsim_test 5.3 -faults oom* -prep {
          260  +  faultsim_restore_and_reopen
          261  +  sqlite3_fts5_create_tokenizer db tcl tcl_create
          262  +} -body {
          263  +  db eval { 
          264  +    SELECT count(*) FROM t1 WHERE t1 MATCH 'd AND e AND f'
          265  +  }
          266  +} -test {
          267  +  faultsim_test_result {0 29}
          268  +}
          269  +
          270  +do_faultsim_test 5.4 -faults oom* -prep {
          271  +  faultsim_restore_and_reopen
          272  +  sqlite3_fts5_create_tokenizer db tcl tcl_create
          273  +} -body {
          274  +  db eval { 
          275  +    SELECT count(*) FROM t1 WHERE t1 MATCH 'x + e'
          276  +  }
          277  +} -test {
          278  +  faultsim_test_result {0 1}
          279  +}
          280  +
          281  +#-------------------------------------------------------------------------
          282  +catch { db close }
          283  +breakpoint
          284  +do_faultsim_test 6 -faults oom* -prep {
          285  +  sqlite_orig db test.db
          286  +  sqlite3_db_config_lookaside db 0 0 0
          287  +} -body {
          288  +  load_static_extension db fts5
          289  +} -test {
          290  +  faultsim_test_result {0 {}} {1 {initialization of fts5 failed: }}
          291  +  if {$testrc==0} {
          292  +    db eval { CREATE VIRTUAL TABLE temp.t1 USING fts5(x) }
          293  +  }
          294  +  db close
          295  +}
   151    296   finish_test
   152    297   

Added ext/fts5/test/fts5fault7.test.

            1  +# 2015 September 3
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#*************************************************************************
           11  +#
           12  +# This file is focused on OOM errors.
           13  +#
           14  +
           15  +source [file join [file dirname [info script]] fts5_common.tcl]
           16  +source $testdir/malloc_common.tcl
           17  +set testprefix fts5fault7
           18  +
           19  +# If SQLITE_ENABLE_FTS3 is defined, omit this file.
           20  +ifcapable !fts5 {
           21  +  finish_test
           22  +  return
           23  +}
           24  +
           25  +if 1 {
           26  +
           27  +#-------------------------------------------------------------------------
           28  +# Test fault-injection on a query that uses xColumnSize() on columnsize=0
           29  +# table.
           30  +#
           31  +do_execsql_test 1.0 {
           32  +  CREATE VIRTUAL TABLE t1 USING fts5(x, columnsize=0);
           33  +  INSERT INTO t1 VALUES('a b c d e f g');
           34  +  INSERT INTO t1 VALUES('a b c d');
           35  +  INSERT INTO t1 VALUES('a b c d e f g h i j');
           36  +}
           37  +
           38  +
           39  +fts5_aux_test_functions db
           40  +do_faultsim_test 1 -faults oom* -body {
           41  +  execsql { SELECT fts5_test_columnsize(t1) FROM t1 WHERE t1 MATCH 'b' }
           42  +} -test {
           43  +  faultsim_test_result {0 {7 4 10}} {1 SQLITE_NOMEM}
           44  +}
           45  +
           46  +}
           47  +
           48  +#-------------------------------------------------------------------------
           49  +# Test fault-injection when a segment is promoted.
           50  +#
           51  +do_execsql_test 1.0 {
           52  +  CREATE VIRTUAL TABLE t2 USING fts5(a);
           53  +  INSERT INTO t2(t2, rank) VALUES('automerge', 0);
           54  +  INSERT INTO t2(t2, rank) VALUES('crisismerge', 4);
           55  +  INSERT INTO t2(t2, rank) VALUES('pgsz', 40);
           56  +
           57  +  INSERT INTO t2 VALUES('a b c');
           58  +  INSERT INTO t2 VALUES('d e f');
           59  +  INSERT INTO t2 VALUES('f e d');
           60  +  INSERT INTO t2 VALUES('c b a');
           61  +
           62  +  INSERT INTO t2 VALUES('a b c');
           63  +  INSERT INTO t2 VALUES('d e f');
           64  +  INSERT INTO t2 VALUES('f e d');
           65  +  INSERT INTO t2 VALUES('c b a');
           66  +} {}
           67  +
           68  +faultsim_save_and_close
           69  +do_faultsim_test 1 -faults oom-t* -prep {
           70  +  faultsim_restore_and_reopen
           71  +  db eval {
           72  +    BEGIN;
           73  +    INSERT INTO t2 VALUES('c d c g g f');
           74  +    INSERT INTO t2 VALUES('c d g b f d');
           75  +    INSERT INTO t2 VALUES('c c f d e d');
           76  +    INSERT INTO t2 VALUES('e a f c e f');
           77  +    INSERT INTO t2 VALUES('c g f b b d');
           78  +    INSERT INTO t2 VALUES('d a g a b b');
           79  +    INSERT INTO t2 VALUES('e f a b c e');
           80  +    INSERT INTO t2 VALUES('e c a g c d');
           81  +    INSERT INTO t2 VALUES('g b d d e b');
           82  +    INSERT INTO t2 VALUES('e a d a e d');
           83  +  }
           84  +} -body {
           85  +  db eval COMMIT
           86  +} -test {
           87  +  faultsim_test_result {0 {}}
           88  +}
           89  +
           90  +finish_test
           91  +

Changes to ext/fts5/test/fts5matchinfo.test.

   351    351      GROUP BY t10.rowid
   352    352      ORDER BY 1;
   353    353   } {1 1 one 2 2 two 3 3 three}
   354    354     
   355    355   #---------------------------------------------------------------------------
   356    356   # Test the 'y' matchinfo flag
   357    357   #
   358         -set sqlite_fts3_enable_parentheses 1
   359    358   reset_db
          359  +sqlite3_fts5_register_matchinfo db
   360    360   do_execsql_test 11.0 {
   361         -  CREATE VIRTUAL TABLE tt USING fts3(x, y);
          361  +  CREATE VIRTUAL TABLE tt USING fts5(x, y);
   362    362     INSERT INTO tt VALUES('c d a c d d', 'e a g b d a');   -- 1
   363    363     INSERT INTO tt VALUES('c c g a e b', 'c g d g e c');   -- 2
   364    364     INSERT INTO tt VALUES('b e f d e g', 'b a c b c g');   -- 3
   365    365     INSERT INTO tt VALUES('a c f f g d', 'd b f d e g');   -- 4
   366    366     INSERT INTO tt VALUES('g a c f c f', 'd g g b c c');   -- 5
   367    367     INSERT INTO tt VALUES('g a c e b b', 'd b f b g g');   -- 6
   368    368     INSERT INTO tt VALUES('f d a a f c', 'e e a d c f');   -- 7
................................................................................
   428    428       SELECT rowid, mit(matchinfo(tt, 'b')) FROM tt WHERE tt MATCH $expr
   429    429     } $r2
   430    430   
   431    431     do_execsql_test 11.1.$tn.2  {
   432    432       SELECT rowid, mit(matchinfo(tt, 'b')) FROM tt WHERE tt MATCH $expr
   433    433     } $r2
   434    434   }
   435         -set sqlite_fts3_enable_parentheses 0
   436    435   
   437    436   #---------------------------------------------------------------------------
   438    437   # Test the 'b' matchinfo flag
   439    438   #
   440         -set sqlite_fts3_enable_parentheses 1
   441    439   reset_db
          440  +sqlite3_fts5_register_matchinfo db
   442    441   db func mit mit
   443    442   
   444    443   do_test 12.0 {
   445    444     set cols [list]
   446    445     for {set i 0} {$i < 50} {incr i} { lappend cols "c$i" }
   447         -  execsql "CREATE VIRTUAL TABLE tt USING fts3([join $cols ,])"
          446  +  execsql "CREATE VIRTUAL TABLE tt USING fts5([join $cols ,])"
   448    447   } {}
   449    448   
   450    449   do_execsql_test 12.1 {
   451    450     INSERT INTO tt (rowid, c4, c45) VALUES(1, 'abc', 'abc');
   452    451     SELECT mit(matchinfo(tt, 'b')) FROM tt WHERE tt MATCH 'abc';
   453    452   } [list [list [expr 1<<4] [expr 1<<(45-32)]]]
   454    453   
   455         -set sqlite_fts3_enable_parentheses 0
   456    454   finish_test
   457    455   

Changes to ext/fts5/test/fts5rowid.test.

    23     23   
    24     24   do_catchsql_test 1.1 {
    25     25     SELECT fts5_rowid()
    26     26   } {1 {should be: fts5_rowid(subject, ....)}}
    27     27   
    28     28   do_catchsql_test 1.2 {
    29     29     SELECT fts5_rowid('segment')
    30         -} {1 {should be: fts5_rowid('segment', segid, height, pgno))}}
           30  +} {1 {should be: fts5_rowid('segment', segid, pgno))}}
    31     31   
    32     32   do_execsql_test 1.3 {
    33         -  SELECT fts5_rowid('segment', 1, 1, 1)
    34         -} {139586437121}
           33  +  SELECT fts5_rowid('segment', 1, 1)
           34  +} {137438953473}
    35     35   
    36     36   do_catchsql_test 1.4 {
    37     37     SELECT fts5_rowid('nosucharg');
    38         -} {1 {first arg to fts5_rowid() must be 'segment' or 'start-of-index'}} 
           38  +} {1 {first arg to fts5_rowid() must be 'segment'}} 
    39     39   
    40     40   
    41     41   #-------------------------------------------------------------------------
    42     42   # Tests of the fts5_decode() function.
    43     43   #
    44     44   reset_db
    45     45   do_execsql_test 2.1 { 
................................................................................
    86     86   } $res
    87     87   
    88     88   # This is really a corruption test...
    89     89   #do_execsql_test 2.7 {
    90     90   #  UPDATE x1_data SET block = X'';
    91     91   #  SELECT count(fts5_decode(rowid, block)) FROM x1_data;
    92     92   #} $res
           93  +
           94  +do_execsql_test 2.8 {
           95  +  SELECT fts5_decode(fts5_rowid('segment', 1000, 1), X'AB')
           96  +} {corrupt}
    93     97   
    94     98   #-------------------------------------------------------------------------
    95     99   # Tests with very large tokens.
    96    100   #
    97    101   set strlist [list \
    98    102     "[string repeat x 400]"                       \
    99    103     "[string repeat x 300][string repeat w 100]"  \

Added ext/fts5/test/fts5simple.test.

            1  +# 2015 September 05
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#*************************************************************************
           11  +#
           12  +
           13  +source [file join [file dirname [info script]] fts5_common.tcl]
           14  +set testprefix fts5simple
           15  +
           16  +# If SQLITE_ENABLE_FTS5 is defined, omit this file.
           17  +ifcapable !fts5 {
           18  +  finish_test
           19  +  return
           20  +}
           21  +
           22  +if 1 {
           23  +#-------------------------------------------------------------------------
           24  +#
           25  +set doc "x x [string repeat {y } 50]z z"
           26  +do_execsql_test 1.0 {
           27  +  CREATE VIRTUAL TABLE t1 USING fts5(x);
           28  +  INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
           29  +  BEGIN;
           30  +    INSERT INTO t1 VALUES($doc);
           31  +  COMMIT;
           32  +}
           33  +
           34  +do_execsql_test 1.1 {
           35  +  INSERT INTO t1(t1) VALUES('integrity-check');
           36  +}
           37  +
           38  +#-------------------------------------------------------------------------
           39  +#
           40  +reset_db
           41  +do_execsql_test 2.0 {
           42  +  CREATE VIRTUAL TABLE t1 USING fts5(x);
           43  +  INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
           44  +  INSERT INTO t1 VALUES('a b c');
           45  +  INSERT INTO t1 VALUES('d e f');
           46  +  INSERT INTO t1(t1) VALUES('optimize');
           47  +}
           48  +
           49  +do_execsql_test 2.1 {
           50  +  INSERT INTO t1(t1) VALUES('integrity-check');
           51  +} {}
           52  +
           53  +
           54  +#-------------------------------------------------------------------------
           55  +#
           56  +reset_db
           57  +do_execsql_test 3.0 {
           58  +  CREATE VIRTUAL TABLE t1 USING fts5(x, prefix='1,2');
           59  +  INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
           60  +  BEGIN;
           61  +  INSERT INTO t1 VALUES('one');
           62  +  SELECT * FROM t1 WHERE t1 MATCH 'o*';
           63  +} {one}
           64  +
           65  +do_execsql_test 3.1 {
           66  +  INSERT INTO t1(t1) VALUES('integrity-check');
           67  +} {}
           68  +
           69  +#-------------------------------------------------------------------------
           70  +reset_db
           71  +do_execsql_test 4.1 {
           72  +  CREATE VIRTUAL TABLE t11 USING fts5(content);
           73  +  INSERT INTO t11(t11, rank) VALUES('pgsz', 32);
           74  +  INSERT INTO t11 VALUES('another');
           75  +  INSERT INTO t11 VALUES('string');
           76  +  INSERT INTO t11 VALUES('of');
           77  +  INSERT INTO t11 VALUES('text');
           78  +}
           79  +do_test 4.2 {
           80  +  execsql { INSERT INTO t11(t11) VALUES('optimize') }
           81  +} {}
           82  +do_execsql_test 4.3 {
           83  +  INSERT INTO t11(t11) VALUES('integrity-check');
           84  +} {}
           85  +
           86  +#db eval { SELECT fts5_decode(rowid, block) as x FROM t11_data } { puts $x }
           87  +
           88  +#-------------------------------------------------------------------------
           89  +reset_db
           90  +set doc [string repeat "x y " 5]
           91  +do_execsql_test 5.1 {
           92  +  CREATE VIRTUAL TABLE yy USING fts5(content);
           93  +  INSERT INTO yy(yy, rank) VALUES('pgsz', 32);
           94  +  BEGIN;
           95  +    INSERT INTO yy VALUES($doc);
           96  +    INSERT INTO yy VALUES($doc);
           97  +    INSERT INTO yy VALUES($doc);
           98  +    INSERT INTO yy VALUES($doc);
           99  +    INSERT INTO yy VALUES($doc);
          100  +    INSERT INTO yy VALUES($doc);
          101  +    INSERT INTO yy VALUES($doc);
          102  +    INSERT INTO yy VALUES($doc);
          103  +  COMMIT;
          104  +}
          105  +
          106  +do_execsql_test 5.2 {
          107  +  SELECT rowid FROM yy WHERE yy MATCH 'y' ORDER BY rowid ASC
          108  +} {1 2 3 4 5 6 7 8}
          109  +
          110  +do_execsql_test 5.3 {
          111  +  SELECT rowid FROM yy WHERE yy MATCH 'y' ORDER BY rowid DESC
          112  +} {8 7 6 5 4 3 2 1}
          113  +
          114  +#db eval { SELECT fts5_decode(rowid, block) as x FROM yy_data } { puts $x }
          115  +
          116  +#-------------------------------------------------------------------------
          117  +reset_db
          118  +do_execsql_test 5.1 {
          119  +  CREATE VIRTUAL TABLE tt USING fts5(content);
          120  +  INSERT INTO tt(tt, rank) VALUES('pgsz', 32);
          121  +  INSERT INTO tt VALUES('aa');
          122  +}
          123  +
          124  +do_execsql_test 5.2 {
          125  +  SELECT rowid FROM tt WHERE tt MATCH 'a*';
          126  +} {1}
          127  +
          128  +do_execsql_test 5.3 {
          129  +  DELETE FROM tt;
          130  +  BEGIN;
          131  +    INSERT INTO tt VALUES('aa');
          132  +    INSERT INTO tt VALUES('ab');
          133  +  COMMIT;
          134  +} {}
          135  +
          136  +do_execsql_test 5.4 {
          137  +  SELECT rowid FROM tt WHERE tt MATCH 'a*';
          138  +} {1 2}
          139  +
          140  +}
          141  +
          142  +do_execsql_test 5.5 {
          143  +  DELETE FROM tt;
          144  +  BEGIN;
          145  +    INSERT INTO tt VALUES('aa');
          146  +    INSERT INTO tt VALUES('ab');
          147  +    INSERT INTO tt VALUES('aa');
          148  +    INSERT INTO tt VALUES('ab');
          149  +    INSERT INTO tt VALUES('aa');
          150  +    INSERT INTO tt VALUES('ab');
          151  +    INSERT INTO tt VALUES('aa');
          152  +    INSERT INTO tt VALUES('ab');
          153  +  COMMIT;
          154  +  SELECT rowid FROM tt WHERE tt MATCH 'a*';
          155  +} {1 2 3 4 5 6 7 8}
          156  +
          157  +do_execsql_test 5.6 {
          158  +  INSERT INTO tt(tt) VALUES('integrity-check');
          159  +}
          160  +
          161  +reset_db
          162  +do_execsql_test 5.7 {
          163  +  CREATE VIRTUAL TABLE tt USING fts5(content);
          164  +  INSERT INTO tt(tt, rank) VALUES('pgsz', 32);
          165  +  INSERT INTO tt VALUES('aa ab ac ad ae af');
          166  +}
          167  +
          168  +do_execsql_test 5.8 {
          169  +  SELECT rowid FROM tt WHERE tt MATCH 'a*';
          170  +} {1}
          171  +
          172  +finish_test
          173  +

Added ext/fts5/test/fts5synonym.test.

            1  +# 2014 Dec 20
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +#
           12  +# Tests focusing on custom tokenizers that support synonyms.
           13  +#
           14  +
           15  +source [file join [file dirname [info script]] fts5_common.tcl]
           16  +set testprefix fts5synonym
           17  +
           18  +# If SQLITE_ENABLE_FTS5 is defined, omit this file.
           19  +ifcapable !fts5 {
           20  +  finish_test
           21  +  return
           22  +}
           23  +
           24  +foreach S {
           25  +  {zero 0}
           26  +  {one 1 i}
           27  +  {two 2 ii}
           28  +  {three 3 iii}
           29  +  {four 4 iv}
           30  +  {five 5 v}
           31  +  {six 6 vi}
           32  +  {seven 7 vii}
           33  +  {eight 8 viii}
           34  +  {nine 9 ix}
           35  +} {
           36  +  foreach s $S {
           37  +    set o [list]
           38  +    foreach x $S {if {$x!=$s} {lappend o $x}}
           39  +    set ::syn($s) $o
           40  +  }
           41  +}
           42  +
           43  +proc tcl_tokenize {tflags text} {
           44  +  foreach {w iStart iEnd} [fts5_tokenize_split $text] {
           45  +    sqlite3_fts5_token $w $iStart $iEnd
           46  +  }
           47  +}
           48  +
           49  +proc tcl_create {args} {
           50  +  return "tcl_tokenize"
           51  +}
           52  +
           53  +sqlite3_fts5_create_tokenizer db tcl tcl_create
           54  +
           55  +#-------------------------------------------------------------------------
           56  +# Warm body test for the code in fts5_tcl.c.
           57  +#
           58  +do_execsql_test 1.0 {
           59  +  CREATE VIRTUAL TABLE ft USING fts5(x, tokenize = tcl);
           60  +  INSERT INTO ft VALUES('abc def ghi');
           61  +  INSERT INTO ft VALUES('jkl mno pqr');
           62  +  SELECT rowid, x FROM ft WHERE ft MATCH 'def';
           63  +  SELECT x, rowid FROM ft WHERE ft MATCH 'pqr';
           64  +} {1 {abc def ghi} {jkl mno pqr} 2}
           65  +
           66  +#-------------------------------------------------------------------------
           67  +# Test a tokenizer that supports synonyms by adding extra entries to the
           68  +# FTS index.
           69  +#
           70  +
           71  +proc tcl_tokenize {tflags text} {
           72  +  foreach {w iStart iEnd} [fts5_tokenize_split $text] {
           73  +    sqlite3_fts5_token $w $iStart $iEnd
           74  +    if {$tflags=="document" && [info exists ::syn($w)]} {
           75  +      foreach s $::syn($w) {
           76  +        sqlite3_fts5_token -colo $s $iStart $iEnd
           77  +      }
           78  +    }
           79  +  }
           80  +}
           81  +reset_db
           82  +sqlite3_fts5_create_tokenizer db tcl tcl_create
           83  +
           84  +do_execsql_test 2.0 {
           85  +  CREATE VIRTUAL TABLE ft USING fts5(x, tokenize = tcl);
           86  +  INSERT INTO ft VALUES('one two three');
           87  +  INSERT INTO ft VALUES('four five six');
           88  +  INSERT INTO ft VALUES('eight nine ten');
           89  +} {}
           90  +
           91  +foreach {tn expr res} {
           92  +  1 "3" 1
           93  +  2 "eight OR 8 OR 5" {2 3}
           94  +  3 "10" {}
           95  +  4 "1*" {1}
           96  +  5 "1 + 2" {1}
           97  +} {
           98  +  do_execsql_test 2.1.$tn {
           99  +    SELECT rowid FROM ft WHERE ft MATCH $expr
          100  +  } $res
          101  +}
          102  +
          103  +#-------------------------------------------------------------------------
          104  +# Test some broken tokenizers:
          105  +#
          106  +#   3.1.*: A tokenizer that declares the very first token to be colocated.
          107  +#
          108  +#   3.2.*: A tokenizer that reports two identical tokens at the same position.
          109  +#          This is allowed.
          110  +#
          111  +reset_db
          112  +sqlite3_fts5_create_tokenizer db tcl tcl_create
          113  +proc tcl_tokenize {tflags text} {
          114  +  set bColo 1
          115  +  foreach {w iStart iEnd} [fts5_tokenize_split $text] {
          116  +    if {$bColo} {
          117  +      sqlite3_fts5_token -colo $w $iStart $iEnd
          118  +      set bColo 0
          119  +    } {
          120  +      sqlite3_fts5_token $w $iStart $iEnd
          121  +    }
          122  +  }
          123  +}
          124  +do_execsql_test 3.1.0 {
          125  +  CREATE VIRTUAL TABLE ft USING fts5(x, tokenize = tcl);
          126  +  INSERT INTO ft VALUES('one two three');
          127  +  CREATE VIRTUAL TABLE vv USING fts5vocab(ft, row);
          128  +  SELECT * FROM vv;
          129  +} {
          130  +  one 1 1   three 1 1   two 1 1
          131  +}
          132  +
          133  +do_execsql_test 3.1.1 {
          134  +  INSERT INTO ft(ft) VALUES('integrity-check');
          135  +} {}
          136  +
          137  +proc tcl_tokenize {tflags text} {
          138  +  foreach {w iStart iEnd} [fts5_tokenize_split $text] {
          139  +    sqlite3_fts5_token $w $iStart $iEnd
          140  +  }
          141  +}
          142  +
          143  +do_execsql_test 3.1.2 {
          144  +  SELECT rowid FROM ft WHERE ft MATCH 'one two three'
          145  +} {1}
          146  +
          147  +reset_db
          148  +sqlite3_fts5_create_tokenizer db tcl tcl_create
          149  +proc tcl_tokenize {tflags text} {
          150  +  foreach {w iStart iEnd} [fts5_tokenize_split $text] {
          151  +    sqlite3_fts5_token $w $iStart $iEnd
          152  +    sqlite3_fts5_token -colo $w $iStart $iEnd
          153  +  }
          154  +}
          155  +do_execsql_test 3.2.0 {
          156  +  CREATE VIRTUAL TABLE ft USING fts5(x, tokenize = tcl);
          157  +  INSERT INTO ft VALUES('one one two three');
          158  +  CREATE VIRTUAL TABLE vv USING fts5vocab(ft, row);
          159  +  SELECT * FROM vv;
          160  +} {
          161  +  one 1 4   three 1 2   two 1 2
          162  +}
          163  +do_execsql_test 3.2.1 {
          164  +  SELECT rowid FROM ft WHERE ft MATCH 'one';
          165  +} {1}
          166  +do_execsql_test 3.2.2 {
          167  +  SELECT rowid FROM ft WHERE ft MATCH 'one two three';
          168  +} {1}
          169  +do_execsql_test 3.2.3 {
          170  +  SELECT rowid FROM ft WHERE ft MATCH 'one + one + two + three';
          171  +} {1}
          172  +do_execsql_test 3.2.4 {
          173  +  SELECT rowid FROM ft WHERE ft MATCH 'one two two three';
          174  +} {1}
          175  +do_execsql_test 3.2.5 {
          176  +  SELECT rowid FROM ft WHERE ft MATCH 'one + two + two + three';
          177  +} {}
          178  +
          179  +#-------------------------------------------------------------------------
          180  +# Check that expressions with synonyms can be parsed and executed.
          181  +#
          182  +reset_db
          183  +sqlite3_fts5_create_tokenizer db tcl tcl_create
          184  +proc tcl_tokenize {tflags text} {
          185  +  foreach {w iStart iEnd} [fts5_tokenize_split $text] {
          186  +    sqlite3_fts5_token $w $iStart $iEnd
          187  +    if {$tflags=="query" && [info exists ::syn($w)]} {
          188  +      foreach s $::syn($w) {
          189  +        sqlite3_fts5_token -colo $s $iStart $iEnd
          190  +      }
          191  +    }
          192  +  }
          193  +}
          194  +
          195  +foreach {tn expr res} {
          196  +  1  {abc}                           {"abc"}
          197  +  2  {one}                           {"one"|"i"|"1"}
          198  +  3  {3}                             {"3"|"iii"|"three"}
          199  +  4  {3*}                            {"3"|"iii"|"three" *}
          200  +} {
          201  +  do_execsql_test 4.1.$tn {SELECT fts5_expr($expr, 'tokenize=tcl')} [list $res]
          202  +}
          203  +
          204  +do_execsql_test 4.2.1 {
          205  +  CREATE VIRTUAL TABLE xx USING fts5(x, tokenize=tcl);
          206  +  INSERT INTO xx VALUES('one two');
          207  +  INSERT INTO xx VALUES('three four');
          208  +}
          209  +
          210  +do_execsql_test 4.2.2 {
          211  +  SELECT rowid FROM xx WHERE xx MATCH '2'
          212  +} {1}
          213  +
          214  +do_execsql_test 4.2.3 {
          215  +  SELECT rowid FROM xx WHERE xx MATCH '3'
          216  +} {2}
          217  +
          218  +do_test 5.0 {
          219  +  execsql { 
          220  +    CREATE VIRTUAL TABLE t1 USING fts5(a, b, tokenize=tcl)
          221  +  }
          222  +  foreach {rowid a b} {
          223  +    1 {four v 4 i three} {1 3 five five 4 one}
          224  +    2 {5 1 3 4 i} {2 2 v two 4}
          225  +    3 {5 i 5 2 four 4 1} {iii ii five two 1}
          226  +    4 {ii four 4 one 5 three five} {one 5 1 iii 4 3}
          227  +    5 {three i v i four 4 1} {ii five five five iii}
          228  +    6 {4 2 ii two 2 iii} {three 1 four 4 iv 1 iv}
          229  +    7 {ii ii two three 2 5} {iii i ii iii iii one one}
          230  +    8 {2 ii i two 3 three 2} {two iv v iii 3 five}
          231  +    9 {i 2 iv 3 five four v} {iii 4 three i three ii 1}
          232  +  } {
          233  +    execsql { INSERT INTO t1(rowid, a, b) VALUES($rowid, $a, $b) }
          234  +  }
          235  +} {}
          236  +
          237  +
          238  +foreach {tn q res} {
          239  +  1 {one} {
          240  +    1 {four v 4 [i] three} {[1] 3 five five 4 [one]}
          241  +    2 {5 [1] 3 4 [i]} {2 2 v two 4}
          242  +    3 {5 [i] 5 2 four 4 [1]} {iii ii five two [1]}
          243  +    4 {ii four 4 [one] 5 three five} {[one] 5 [1] iii 4 3}
          244  +    5 {three [i] v [i] four 4 [1]} {ii five five five iii}
          245  +    6 {4 2 ii two 2 iii} {three [1] four 4 iv [1] iv}
          246  +    7 {ii ii two three 2 5} {iii [i] ii iii iii [one] [one]}
          247  +    8 {2 ii [i] two 3 three 2} {two iv v iii 3 five}
          248  +    9 {[i] 2 iv 3 five four v} {iii 4 three [i] three ii [1]}
          249  +  }
          250  +  2 {five four} {
          251  +    1 {[four] [v] [4] i three} {1 3 [five] [five] [4] one}
          252  +    2 {[5] 1 3 [4] i} {2 2 [v] two [4]}
          253  +    3 {[5] i [5] 2 [four] [4] 1} {iii ii [five] two 1}
          254  +    4 {ii [four] [4] one [5] three [five]} {one [5] 1 iii [4] 3}
          255  +    5 {three i [v] i [four] [4] 1} {ii [five] [five] [five] iii}
          256  +    8 {2 ii i two 3 three 2} {two [iv] [v] iii 3 [five]}
          257  +    9 {i 2 [iv] 3 [five] [four] [v]} {iii [4] three i three ii 1}
          258  +  }
          259  +  3 {one OR two OR iii OR 4 OR v} {
          260  +    1 {[four] [v] [4] [i] [three]} {[1] [3] [five] [five] [4] [one]}
          261  +    2 {[5] [1] [3] [4] [i]} {[2] [2] [v] [two] [4]}
          262  +    3 {[5] [i] [5] [2] [four] [4] [1]} {[iii] [ii] [five] [two] [1]}
          263  +    4 {[ii] [four] [4] [one] [5] [three] [five]} {[one] [5] [1] [iii] [4] [3]}
          264  +    5 {[three] [i] [v] [i] [four] [4] [1]} {[ii] [five] [five] [five] [iii]}
          265  +    6 {[4] [2] [ii] [two] [2] [iii]} {[three] [1] [four] [4] [iv] [1] [iv]}
          266  +    7 {[ii] [ii] [two] [three] [2] [5]} {[iii] [i] [ii] [iii] [iii] [one] [one]}
          267  +    8 {[2] [ii] [i] [two] [3] [three] [2]} {[two] [iv] [v] [iii] [3] [five]}
          268  +    9 {[i] [2] [iv] [3] [five] [four] [v]} {[iii] [4] [three] [i] [three] [ii] [1]}
          269  +  }
          270  +
          271  +  4 {5 + 1} {
          272  +    2 {[5 1] 3 4 i} {2 2 v two 4} 
          273  +    3 {[5 i] 5 2 four 4 1} {iii ii five two 1} 
          274  +    4 {ii four 4 one 5 three five} {one [5 1] iii 4 3} 
          275  +    5 {three i [v i] four 4 1} {ii five five five iii}
          276  +  }
          277  +
          278  +  5 {one + two + three} {
          279  +    7 {ii ii two three 2 5} {iii [i ii iii] iii one one}
          280  +    8 {2 ii [i two 3] three 2} {two iv v iii 3 five}
          281  +  }
          282  +
          283  +  6 {"v v"} {
          284  +    1 {four v 4 i three} {1 3 [five five] 4 one}
          285  +    5 {three i v i four 4 1} {ii [five five five] iii}
          286  +  }
          287  +} {
          288  +  do_execsql_test 5.1.$tn {
          289  +    SELECT rowid, highlight(t1, 0, '[', ']'), highlight(t1, 1, '[', ']')
          290  +    FROM t1 WHERE t1 MATCH $q
          291  +  } $res
          292  +}
          293  +
          294  +# Test that the xQueryPhrase() API works with synonyms.
          295  +#
          296  +proc mit {blob} {
          297  +  set scan(littleEndian) i*
          298  +  set scan(bigEndian) I*
          299  +  binary scan $blob $scan($::tcl_platform(byteOrder)) r
          300  +  return $r
          301  +}
          302  +db func mit mit
          303  +sqlite3_fts5_register_matchinfo db
          304  +
          305  +foreach {tn q res} {
          306  +  1 {one} {
          307  +      1 {1 11 7 2 12 6}     2 {2 11 7 0 12 6} 
          308  +      3 {2 11 7 1 12 6}     4 {1 11 7 2 12 6} 
          309  +      5 {3 11 7 0 12 6}     6 {0 11 7 2 12 6} 
          310  +      7 {0 11 7 3 12 6}     8 {1 11 7 0 12 6} 
          311  +      9 {1 11 7 2 12 6}
          312  +  }
          313  +} {
          314  +  do_execsql_test 5.2.$tn {
          315  +    SELECT rowid, mit(matchinfo(t1, 'x')) FROM t1 WHERE t1 MATCH $q
          316  +  } $res
          317  +}
          318  +
          319  +
          320  +#-------------------------------------------------------------------------
          321  +# Test terms with more than 4 synonyms.
          322  +#
          323  +reset_db
          324  +sqlite3_fts5_create_tokenizer db tcl tcl_create
          325  +proc tcl_tokenize {tflags text} {
          326  +  foreach {w iStart iEnd} [fts5_tokenize_split $text] {
          327  +    sqlite3_fts5_token $w $iStart $iEnd
          328  +    if {$tflags=="query" && [string length $w]==1} {
          329  +      for {set i 2} {$i<=10} {incr i} {
          330  +        sqlite3_fts5_token -colo [string repeat $w $i] $iStart $iEnd
          331  +      }
          332  +    }
          333  +  }
          334  +}
          335  +
          336  +do_execsql_test 6.0.1 {
          337  +  CREATE VIRTUAL TABLE t1 USING fts5(x, tokenize=tcl);
          338  +  INSERT INTO t1 VALUES('yy xx qq');
          339  +  INSERT INTO t1 VALUES('yy xx xx');
          340  +}
          341  +do_execsql_test 6.0.2 {
          342  +  SELECT * FROM t1 WHERE t1 MATCH 'NEAR(y q)';
          343  +} {{yy xx qq}}
          344  +
          345  +do_test 6.0.3 {
          346  +  execsql { 
          347  +    CREATE VIRTUAL TABLE t2 USING fts5(a, b, tokenize=tcl)
          348  +  }
          349  +  foreach {rowid a b} {
          350  +    1 {yyyy vvvvv qq oo yyyyyy vvvv eee} {ffff uu r qq aaaa}
          351  +    2 {ww oooooo bbbbb ssssss mm} {ffffff yy iiii rr s ccc qqqqq}
          352  +    3 {zzzz llll gggggg cccc uu} {hhhhhh aaaa ppppp rr ee jjjj}
          353  +    4 {r f i rrrrrr ww hhh} {aa yyy t x aaaaa ii}
          354  +    5 {fffff mm vvvv ooo ffffff kkkk tttt} {cccccc bb e zzz d n}
          355  +    6 {iii dddd hh qqqq ddd ooo} {ttt d c b aaaaaa qqqq}
          356  +    7 {jjjj rrrr v zzzzz u tt t} {ppppp pp dddd mm hhh uuu}
          357  +    8 {gggg rrrrrr kkkk vvvv gggg jjjjjj b} {dddddd jj r w cccc wwwwww ss}
          358  +    9 {kkkkk qqq oooo e tttttt mmm} {e ss qqqqqq hhhh llllll gg}
          359  +  } {
          360  +    execsql { INSERT INTO t2(rowid, a, b) VALUES($rowid, $a, $b) }
          361  +  }
          362  +} {}
          363  +
          364  +foreach {tn q res} {
          365  +  1 {a} {
          366  +    1 {yyyy vvvvv qq oo yyyyyy vvvv eee} {ffff uu r qq [aaaa]}
          367  +    3 {zzzz llll gggggg cccc uu} {hhhhhh [aaaa] ppppp rr ee jjjj}
          368  +    4 {r f i rrrrrr ww hhh} {[aa] yyy t x [aaaaa] ii}
          369  +    6 {iii dddd hh qqqq ddd ooo} {ttt d c b [aaaaaa] qqqq}
          370  +  }
          371  +
          372  +  2 {a AND q} {
          373  +    1 {yyyy vvvvv [qq] oo yyyyyy vvvv eee} {ffff uu r [qq] [aaaa]}
          374  +    6 {iii dddd hh [qqqq] ddd ooo} {ttt d c b [aaaaaa] [qqqq]}
          375  +  }
          376  +
          377  +  3 {o OR (q AND a)} {
          378  +    1 {yyyy vvvvv [qq] [oo] yyyyyy vvvv eee} {ffff uu r [qq] [aaaa]}
          379  +    2 {ww [oooooo] bbbbb ssssss mm} {ffffff yy iiii rr s ccc qqqqq}
          380  +    5 {fffff mm vvvv [ooo] ffffff kkkk tttt} {cccccc bb e zzz d n}
          381  +    6 {iii dddd hh [qqqq] ddd [ooo]} {ttt d c b [aaaaaa] [qqqq]}
          382  +    9 {kkkkk qqq [oooo] e tttttt mmm} {e ss qqqqqq hhhh llllll gg}
          383  +  }
          384  +
          385  +  4 {NEAR(q y, 20)} {
          386  +    1 {[yyyy] vvvvv [qq] oo [yyyyyy] vvvv eee} {ffff uu r qq aaaa}
          387  +    2 {ww oooooo bbbbb ssssss mm} {ffffff [yy] iiii rr s ccc [qqqqq]}
          388  +  }
          389  +} {
          390  +  do_execsql_test 6.1.$tn.asc {
          391  +    SELECT rowid, highlight(t2, 0, '[', ']'), highlight(t2, 1, '[', ']')
          392  +    FROM t2 WHERE t2 MATCH $q
          393  +  } $res
          394  +
          395  +  set res2 [list]
          396  +  foreach {rowid a b} $res {
          397  +    set res2 [concat [list $rowid $a $b] $res2]
          398  +  }
          399  +
          400  +  do_execsql_test 6.1.$tn.desc {
          401  +    SELECT rowid, highlight(t2, 0, '[', ']'), highlight(t2, 1, '[', ']')
          402  +    FROM t2 WHERE t2 MATCH $q ORDER BY rowid DESC
          403  +  } $res2
          404  +}
          405  +
          406  +do_execsql_test 6.2.1 {
          407  +  INSERT INTO t2(rowid, a, b) VALUES(13,
          408  +      'x xx xxx xxxx xxxxx xxxxxx xxxxxxx', 'y yy yyy yyyy yyyyy yyyyyy yyyyyyy'
          409  +  );
          410  +  SELECT rowid, highlight(t2, 0, '<', '>'), highlight(t2, 1, '(', ')')
          411  +  FROM t2 WHERE t2 MATCH 'x OR y'
          412  +} {
          413  +  1 {<yyyy> vvvvv qq oo <yyyyyy> vvvv eee} {ffff uu r qq aaaa}
          414  +  2 {ww oooooo bbbbb ssssss mm} {ffffff (yy) iiii rr s ccc qqqqq}
          415  +  4 {r f i rrrrrr ww hhh} {aa (yyy) t (x) aaaaa ii}
          416  +  13 {<x> <xx> <xxx> <xxxx> <xxxxx> <xxxxxx> <xxxxxxx>}
          417  +     {(y) (yy) (yyy) (yyyy) (yyyyy) (yyyyyy) (yyyyyyy)}
          418  +}
          419  +
          420  +#-------------------------------------------------------------------------
          421  +# Test that the xColumnSize() API is not confused by colocated tokens.
          422  +#
          423  +reset_db
          424  +sqlite3_fts5_create_tokenizer db tcl tcl_create
          425  +fts5_aux_test_functions db
          426  +proc tcl_tokenize {tflags text} {
          427  +  foreach {w iStart iEnd} [fts5_tokenize_split $text] {
          428  +    sqlite3_fts5_token $w $iStart $iEnd
          429  +    if {[string length $w]==1} {
          430  +      for {set i 2} {$i<=10} {incr i} {
          431  +        sqlite3_fts5_token -colo [string repeat $w $i] $iStart $iEnd
          432  +      }
          433  +    }
          434  +  }
          435  +}
          436  +
          437  +do_execsql_test 7.0.1 {
          438  +  CREATE VIRTUAL TABLE t1 USING fts5(a, b, columnsize=1, tokenize=tcl);
          439  +  INSERT INTO t1 VALUES('0 2 3', '4 5 6 7');
          440  +  INSERT INTO t1 VALUES('8 9', '0 0 0 0 0 0 0 0 0 0');
          441  +  SELECT fts5_test_columnsize(t1) FROM t1 WHERE t1 MATCH '000 AND 00 AND 0';
          442  +} {{3 4} {2 10}}
          443  +
          444  +do_execsql_test 7.0.2 {
          445  +  INSERT INTO t1(t1) VALUES('integrity-check');
          446  +}
          447  +
          448  +do_execsql_test 7.1.1 {
          449  +  CREATE VIRTUAL TABLE t2 USING fts5(a, b, columnsize=0, tokenize=tcl);
          450  +  INSERT INTO t2 VALUES('0 2 3', '4 5 6 7');
          451  +  INSERT INTO t2 VALUES('8 9', '0 0 0 0 0 0 0 0 0 0');
          452  +  SELECT fts5_test_columnsize(t2) FROM t2 WHERE t2 MATCH '000 AND 00 AND 0';
          453  +} {{3 4} {2 10}}
          454  +
          455  +do_execsql_test 7.1.2 {
          456  +  INSERT INTO t2(t2) VALUES('integrity-check');
          457  +}
          458  +
          459  +finish_test
          460  +

Changes to ext/fts5/test/fts5version.test.

    26     26   do_execsql_test 1.1 {
    27     27     CREATE VIRTUAL TABLE t1 USING fts5(one);
    28     28     INSERT INTO t1 VALUES('a b c d');
    29     29   } {}
    30     30   
    31     31   do_execsql_test 1.2 {
    32     32     SELECT * FROM t1_config WHERE k='version'
    33         -} {version 3}
           33  +} {version 4}
    34     34   
    35     35   do_execsql_test 1.3 {
    36     36     SELECT rowid FROM t1 WHERE t1 MATCH 'a';
    37     37   } {1}
    38     38   
    39     39   do_execsql_test 1.4 {
    40         -  UPDATE t1_config set v=4 WHERE k='version';
           40  +  UPDATE t1_config set v=5 WHERE k='version';
    41     41   } 
    42     42   
    43     43   do_test 1.5 {
    44     44     db close
    45     45     sqlite3 db test.db
    46     46     catchsql { SELECT * FROM t1 WHERE t1 MATCH 'a' }
    47         -} {1 {invalid fts5 file format (found 4, expected 3) - run 'rebuild'}}
           47  +} {1 {invalid fts5 file format (found 5, expected 4) - run 'rebuild'}}
    48     48   
    49     49   do_test 1.6 {
    50     50     db close
    51     51     sqlite3 db test.db
    52     52     catchsql { INSERT INTO t1 VALUES('x y z') }
    53         -} {1 {invalid fts5 file format (found 4, expected 3) - run 'rebuild'}}
           53  +} {1 {invalid fts5 file format (found 5, expected 4) - run 'rebuild'}}
    54     54   
    55     55   do_test 1.7 {
    56     56     execsql { DELETE FROM t1_config WHERE k='version' }
    57     57     db close
    58     58     sqlite3 db test.db
    59     59     catchsql { SELECT * FROM t1 WHERE t1 MATCH 'a' }
    60         -} {1 {invalid fts5 file format (found 0, expected 3) - run 'rebuild'}}
           60  +} {1 {invalid fts5 file format (found 0, expected 4) - run 'rebuild'}}
    61     61   
    62     62   
    63     63   finish_test
    64     64   

Changes to ext/fts5/tool/loadfts5.tcl.

    14     14     foreach f [glob -nocomplain -dir $dir *] {
    15     15       if {$::O(limit) && $::nRow>=$::O(limit)} break
    16     16       if {[file isdir $f]} {
    17     17         load_hierachy $f
    18     18       } else {
    19     19         db eval { INSERT INTO t1 VALUES($f, loadfile($f)) }
    20     20         incr ::nRow
           21  +
           22  +      if {$::O(trans) && ($::nRow % $::O(trans))==0} {
           23  +        db eval { COMMIT }
           24  +        db eval { INSERT INTO t1(t1) VALUES('integrity-check') }
           25  +        db eval { BEGIN }
           26  +      }
    21     27   
    22     28         if {($::nRow % $::nRowPerDot)==0} {
    23     29           puts -nonewline .
    24     30           if {($::nRow % (65*$::nRowPerDot))==0} { puts "" }
    25     31           flush stdout
    26     32         }
    27     33   
................................................................................
    37     43     puts stderr "  -fts5        (use fts5)"
    38     44     puts stderr "  -porter      (use porter tokenizer)"
    39     45     puts stderr "  -delete      (delete the database file before starting)"
    40     46     puts stderr "  -limit N     (load no more than N documents)"
    41     47     puts stderr "  -automerge N (set the automerge parameter to N)"
    42     48     puts stderr "  -crisismerge N (set the crisismerge parameter to N)"
    43     49     puts stderr "  -prefix PREFIX (comma separated prefix= argument)"
           50  +  puts stderr "  -trans N     (commit after N inserts - 0 == never)"
    44     51     exit 1
    45     52   }
    46     53   
    47     54   set O(vtab)       fts5
    48     55   set O(tok)        ""
    49     56   set O(limit)      0
    50     57   set O(delete)     0
    51     58   set O(automerge)  -1
    52     59   set O(crisismerge)  -1
    53     60   set O(prefix)     ""
           61  +set O(trans)      0
    54     62   
    55     63   if {[llength $argv]<2} usage
    56     64   set nOpt [expr {[llength $argv]-2}]
    57     65   for {set i 0} {$i < $nOpt} {incr i} {
    58     66     set arg [lindex $argv $i]
    59     67     switch -- [lindex $argv $i] {
    60     68       -fts4 {
................................................................................
    73     81         set O(delete) 1
    74     82       }
    75     83   
    76     84       -limit {
    77     85         if { [incr i]>=$nOpt } usage
    78     86         set O(limit) [lindex $argv $i]
    79     87       }
           88  +
           89  +    -trans {
           90  +      if { [incr i]>=$nOpt } usage
           91  +      set O(trans) [lindex $argv $i]
           92  +    }
    80     93       
    81     94       -automerge {
    82     95         if { [incr i]>=$nOpt } usage
    83     96         set O(automerge) [lindex $argv $i]
    84     97       }
    85     98   
    86     99       -crisismerge {
................................................................................
   100    113   }
   101    114   
   102    115   set dbfile [lindex $argv end-1]
   103    116   if {$O(delete)} { file delete -force $dbfile }
   104    117   sqlite3 db $dbfile
   105    118   catch { load_static_extension db fts5 }
   106    119   db func loadfile loadfile
          120  +db eval "PRAGMA page_size=4096"
   107    121   
   108         -db transaction {
          122  +db eval BEGIN
   109    123     set pref ""
   110    124     if {$O(prefix)!=""} { set pref ", prefix='$O(prefix)'" }
   111    125     catch {
   112    126       db eval "CREATE VIRTUAL TABLE t1 USING $O(vtab) (path, content$O(tok)$pref)"
   113    127       db eval "INSERT INTO t1(t1, rank) VALUES('pgsz', 4050);"
   114    128     }
   115    129     if {$O(automerge)>=0} {
................................................................................
   122    136     if {$O(crisismerge)>=0} {
   123    137       if {$O(vtab) == "fts5"} {
   124    138         db eval {INSERT INTO t1(t1, rank) VALUES('crisismerge', $O(crisismerge))}
   125    139       } else {
   126    140       }
   127    141     }
   128    142     load_hierachy [lindex $argv end]
   129         -}
          143  +db eval COMMIT
   130    144   
   131    145   
   132    146   

Changes to ext/fts5/tool/showfts5.tcl.

     1      1   
     2      2   
     3      3   
     4      4   #-------------------------------------------------------------------------
     5      5   # Process command line arguments.
     6      6   #
     7      7   proc usage {} {
     8         -  puts stderr "usage: $::argv0 database table"
            8  +  puts stderr "usage: $::argv0 ?OPTIONS? database table"
            9  +  puts stderr ""
           10  +  puts stderr "  -nterm                (count number of terms in each segment)"
     9     11     puts stderr ""
    10     12     exit 1
    11     13   }
    12         -if {[llength $argv]!=2} usage
    13         -set database [lindex $argv 0]
    14         -set tbl [lindex $argv 1]
           14  +
           15  +set O(nterm) 0
           16  +
           17  +if {[llength $argv]<2} usage
           18  +foreach a [lrange $argv 0 end-2] {
           19  +  switch -- $a {
           20  +    -nterm {
           21  +      set O(nterm) 1
           22  +    }
           23  +
           24  +    default {
           25  +      usage
           26  +    }
           27  +  }
           28  +}
           29  +
           30  +set database [lindex $argv end-1]
           31  +set tbl [lindex $argv end]
           32  +
    15     33   
           34  +#-------------------------------------------------------------------------
           35  +# Count the number of terms in each segment of fts5 table $tbl. Store the
           36  +# counts in the array variable in the parent context named by parameter
           37  +# $arrayname, indexed by segment-id. Example:
           38  +#
           39  +#   count_terms fts_tbl A
           40  +#   foreach {k v} [array get A] { puts "segid=$k nTerm=$v" }
           41  +#
           42  +proc count_terms {tbl arrayname} {
           43  +  upvar A $arrayname
           44  +  array unset A
           45  +  db eval "SELECT fts5_decode(rowid, block) AS d FROM ${tbl}_data" {
           46  +    set desc [lindex $d 0]
           47  +    if {[regexp {^segid=([0-9]*)} $desc -> id]} {
           48  +      foreach i [lrange $d 1 end] {
           49  +        if {[string match {term=*} $i]} { incr A($id) }
           50  +      }
           51  +    }
           52  +  }
           53  +}
    16     54   
    17     55   
    18     56   #-------------------------------------------------------------------------
    19     57   # Start of main program.
    20     58   #
    21     59   sqlite3 db $database
    22     60   catch { load_static_extension db fts5 }
           61  +
           62  +if {$O(nterm)} { count_terms $tbl A }
    23     63   
    24     64   db eval "SELECT fts5_decode(rowid, block) AS d FROM ${tbl}_data WHERE id=10" {
    25     65     foreach lvl [lrange $d 1 end] {
    26     66       puts [lrange $lvl 0 2]
           67  +
    27     68       foreach seg [lrange $lvl 3 end] {
    28         -      puts "        $seg"
           69  +      if {$::O(nterm)} {
           70  +        regexp {^id=([0-9]*)} $seg -> id
           71  +        set nTerm 0
           72  +        catch { set nTerm $A($id) }
           73  +        puts [format "        % -28s    nTerm=%d" $seg $nTerm]
           74  +      } else {
           75  +        puts [format "        % -28s" $seg]
           76  +      }
    29     77       }
    30     78     }
    31     79   }
    32     80   
    33     81   
    34     82   
    35     83   
    36     84   

Changes to ext/misc/json1.c.

    64     64   #define JSON_FALSE    2
    65     65   #define JSON_INT      3
    66     66   #define JSON_REAL     4
    67     67   #define JSON_STRING   5
    68     68   #define JSON_ARRAY    6
    69     69   #define JSON_OBJECT   7
    70     70   
           71  +/* The "subtype" set for JSON values */
           72  +#define JSON_SUBTYPE  74    /* Ascii for "J" */
           73  +
    71     74   /*
    72     75   ** Names of the various JSON types:
    73     76   */
    74     77   static const char * const jsonType[] = {
    75     78     "null", "true", "false", "integer", "real", "text", "array", "object"
    76     79   };
    77     80   
................................................................................
    78     81   /* Bit values for the JsonNode.jnFlag field
    79     82   */
    80     83   #define JNODE_RAW     0x01         /* Content is raw, not JSON encoded */
    81     84   #define JNODE_ESCAPE  0x02         /* Content is text with \ escapes */
    82     85   #define JNODE_REMOVE  0x04         /* Do not output */
    83     86   #define JNODE_REPLACE 0x08         /* Replace with JsonNode.iVal */
    84     87   #define JNODE_APPEND  0x10         /* More ARRAY/OBJECT entries at u.iAppend */
    85         -#define JNODE_JSON    0x20         /* Treat REPLACE as JSON text */
           88  +#define JNODE_LABEL   0x20         /* Is a label of an object */
    86     89   
    87     90   
    88     91   /* A single node of parsed JSON
    89     92   */
    90     93   struct JsonNode {
    91     94     u8 eType;              /* One of the JSON_ type values */
    92     95     u8 jnFlags;            /* JNODE flags */
................................................................................
   238    241   
   239    242   /*
   240    243   ** Append a function parameter value to the JSON string under 
   241    244   ** construction.
   242    245   */
   243    246   static void jsonAppendValue(
   244    247     JsonString *p,                 /* Append to this JSON string */
   245         -  sqlite3_value *pValue,         /* Value to append */
   246         -  u8 textIsJson                  /* Try to treat text values as JSON */
          248  +  sqlite3_value *pValue          /* Value to append */
   247    249   ){
   248    250     switch( sqlite3_value_type(pValue) ){
   249    251       case SQLITE_NULL: {
   250    252         jsonAppendRaw(p, "null", 4);
   251    253         break;
   252    254       }
   253    255       case SQLITE_INTEGER:
................................................................................
   256    258         u32 n = (u32)sqlite3_value_bytes(pValue);
   257    259         jsonAppendRaw(p, z, n);
   258    260         break;
   259    261       }
   260    262       case SQLITE_TEXT: {
   261    263         const char *z = (const char*)sqlite3_value_text(pValue);
   262    264         u32 n = (u32)sqlite3_value_bytes(pValue);
   263         -      if( textIsJson ){
          265  +      if( sqlite3_value_subtype(pValue)==JSON_SUBTYPE ){
   264    266           jsonAppendRaw(p, z, n);
   265    267         }else{
   266    268           jsonAppendString(p, z, n);
   267    269         }
   268    270         break;
   269    271       }
   270    272       default: {
................................................................................
   360    362         u32 j = 1;
   361    363         jsonAppendChar(pOut, '[');
   362    364         for(;;){
   363    365           while( j<=pNode->n ){
   364    366             if( pNode[j].jnFlags & (JNODE_REMOVE|JNODE_REPLACE) ){
   365    367               if( pNode[j].jnFlags & JNODE_REPLACE ){
   366    368                 jsonAppendSeparator(pOut);
   367         -              jsonAppendValue(pOut, aReplace[pNode[j].iVal],
   368         -                              (pNode[j].jnFlags & JNODE_JSON)!=0);
          369  +              jsonAppendValue(pOut, aReplace[pNode[j].iVal]);
   369    370               }
   370    371             }else{
   371    372               jsonAppendSeparator(pOut);
   372    373               jsonRenderNode(&pNode[j], pOut, aReplace);
   373    374             }
   374    375             j += jsonNodeSize(&pNode[j]);
   375    376           }
................................................................................
   386    387         for(;;){
   387    388           while( j<=pNode->n ){
   388    389             if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 ){
   389    390               jsonAppendSeparator(pOut);
   390    391               jsonRenderNode(&pNode[j], pOut, aReplace);
   391    392               jsonAppendChar(pOut, ':');
   392    393               if( pNode[j+1].jnFlags & JNODE_REPLACE ){
   393         -              jsonAppendValue(pOut, aReplace[pNode[j+1].iVal],
   394         -                              (pNode[j+1].jnFlags & JNODE_JSON)!=0);
          394  +              jsonAppendValue(pOut, aReplace[pNode[j+1].iVal]);
   395    395               }else{
   396    396                 jsonRenderNode(&pNode[j+1], pOut, aReplace);
   397    397               }
   398    398             }
   399    399             j += 1 + jsonNodeSize(&pNode[j+1]);
   400    400           }
   401    401           if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
................................................................................
   416    416     sqlite3_context *pCtx,      /* Return value for this function */
   417    417     sqlite3_value **aReplace    /* Array of replacement values */
   418    418   ){
   419    419     JsonString s;
   420    420     jsonInit(&s, pCtx);
   421    421     jsonRenderNode(pNode, &s, aReplace);
   422    422     jsonResult(&s);
          423  +  sqlite3_result_subtype(pCtx, JSON_SUBTYPE);
   423    424   }
   424    425   
   425    426   /*
   426    427   ** Make the JsonNode the return value of the function.
   427    428   */
   428    429   static void jsonReturn(
   429    430     JsonNode *pNode,            /* Node to return */
................................................................................
   490    491                   if( c>='0' && c<='9' ) v = v*16 + c - '0';
   491    492                   else if( c>='A' && c<='F' ) v = v*16 + c - 'A' + 10;
   492    493                   else if( c>='a' && c<='f' ) v = v*16 + c - 'a' + 10;
   493    494                   else break;
   494    495                 }
   495    496                 if( v==0 ) break;
   496    497                 if( v<=0x7f ){
   497         -                zOut[j++] = v;
          498  +                zOut[j++] = (char)v;
   498    499                 }else if( v<=0x7ff ){
   499         -                zOut[j++] = 0xc0 | (v>>6);
          500  +                zOut[j++] = (char)(0xc0 | (v>>6));
   500    501                   zOut[j++] = 0x80 | (v&0x3f);
   501    502                 }else{
   502         -                zOut[j++] = 0xe0 | (v>>12);
          503  +                zOut[j++] = (char)(0xe0 | (v>>12));
   503    504                   zOut[j++] = 0x80 | ((v>>6)&0x3f);
   504    505                   zOut[j++] = 0x80 | (v&0x3f);
   505    506                 }
   506    507               }else{
   507    508                 if( c=='b' ){
   508    509                   c = '\b';
   509    510                 }else if( c=='f' ){
................................................................................
   579    580   ** non-whitespace character is ']'.
   580    581   */
   581    582   static int jsonParseValue(JsonParse *pParse, u32 i){
   582    583     char c;
   583    584     u32 j;
   584    585     int iThis;
   585    586     int x;
          587  +  JsonNode *pNode;
   586    588     while( isspace(pParse->zJson[i]) ){ i++; }
   587    589     if( (c = pParse->zJson[i])==0 ) return 0;
   588    590     if( c=='{' ){
   589    591       /* Parse object */
   590    592       iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
   591    593       if( iThis<0 ) return -1;
   592    594       for(j=i+1;;j++){
................................................................................
   593    595         while( isspace(pParse->zJson[j]) ){ j++; }
   594    596         x = jsonParseValue(pParse, j);
   595    597         if( x<0 ){
   596    598           if( x==(-2) && pParse->nNode==(u32)iThis+1 ) return j+1;
   597    599           return -1;
   598    600         }
   599    601         if( pParse->oom ) return -1;
   600         -      if( pParse->aNode[pParse->nNode-1].eType!=JSON_STRING ) return -1;
          602  +      pNode = &pParse->aNode[pParse->nNode-1];
          603  +      if( pNode->eType!=JSON_STRING ) return -1;
          604  +      pNode->jnFlags |= JNODE_LABEL;
   601    605         j = x;
   602    606         while( isspace(pParse->zJson[j]) ){ j++; }
   603    607         if( pParse->zJson[j]!=':' ) return -1;
   604    608         j++;
   605    609         x = jsonParseValue(pParse, j);
   606    610         if( x<0 ) return -1;
   607    611         j = x;
................................................................................
   943    947   ** Return NULL if not found or if there is an error.
   944    948   **
   945    949   ** On an error, write an error message into pCtx and increment the
   946    950   ** pParse->nErr counter.
   947    951   **
   948    952   ** If pApnd!=NULL then try to append missing nodes and set *pApnd = 1 if
   949    953   ** nodes are appended.
   950         -**
   951         -** If the path starts with $$ then set *pFlags to JNODE_REPLACE|JNODE_JSON
   952         -** as a single to the caller that the input text to be inserted should be 
   953         -** interpreted as JSON rather than as ordinary text.
   954    954   */
   955    955   static JsonNode *jsonLookup(
   956    956     JsonParse *pParse,      /* The JSON to search */
   957    957     const char *zPath,      /* The path to search */
   958    958     int *pApnd,             /* Append nodes to complete path if not NULL */
   959         -  sqlite3_context *pCtx,  /* Report errors here, if not NULL */
   960         -  u8 *pFlags              /* Write JNODE_REPLACE or _REPLACE|_JSON here */
          959  +  sqlite3_context *pCtx   /* Report errors here, if not NULL */
   961    960   ){
   962    961     const char *zErr = 0;
   963    962     JsonNode *pNode = 0;
   964         -  u8 fg = JNODE_REPLACE;
   965    963   
   966    964     if( zPath==0 ) return 0;
   967    965     if( zPath[0]!='$' ){
   968    966       zErr = zPath;
   969    967       goto lookup_err;
   970    968     }
   971    969     zPath++;
   972         -  if( zPath[0]=='$' ){
   973         -    if( pFlags==0 ){
   974         -      zErr = zPath;
   975         -      goto lookup_err;
   976         -    }
   977         -    zPath++;
   978         -    fg = JNODE_REPLACE|JNODE_JSON;
   979         -  }
   980         -  if( pFlags ) *pFlags = fg;
   981    970     pNode = jsonLookupStep(pParse, 0, zPath, pApnd, &zErr);
   982    971     return pNode;
   983    972   
   984    973   lookup_err:
   985    974     pParse->nErr++;
   986    975     if( zErr!=0 && pCtx!=0 ){
   987    976       char *z = jsonPathSyntaxError(zErr);
................................................................................
   988    977       if( z ){
   989    978         sqlite3_result_error(pCtx, z, -1);
   990    979         sqlite3_free(z);
   991    980       }else{
   992    981         sqlite3_result_error_nomem(pCtx);
   993    982       }
   994    983     }
   995         -  if( pFlags ) *pFlags = fg;
   996    984     return 0;
   997    985   }
   998    986   
   999    987   
  1000    988   /*
  1001    989   ** Report the wrong number of arguments for json_insert(), json_replace()
  1002    990   ** or json_set().
................................................................................
  1032   1020     u32 i;
  1033   1021   
  1034   1022     assert( argc==1 );
  1035   1023     if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
  1036   1024     jsonParseFindParents(&x);
  1037   1025     jsonInit(&s, ctx);
  1038   1026     for(i=0; i<x.nNode; i++){
  1039         -    jsonPrintf(100, &s,"node %3u: %7s n=%-4d up=%d\n",
  1040         -               i, jsonType[x.aNode[i].eType], x.aNode[i].n, x.aUp[i]);
         1027  +    const char *zType;
         1028  +    if( x.aNode[i].jnFlags & JNODE_LABEL ){
         1029  +      assert( x.aNode[i].eType==JSON_STRING );
         1030  +      zType = "label";
         1031  +    }else{
         1032  +      zType = jsonType[x.aNode[i].eType];
         1033  +    }
         1034  +    jsonPrintf(100, &s,"node %3u: %7s n=%-4d up=%-4d",
         1035  +               i, zType, x.aNode[i].n, x.aUp[i]);
  1041   1036       if( x.aNode[i].u.zJContent!=0 ){
  1042         -      jsonAppendRaw(&s, "    text: ", 10);
         1037  +      jsonAppendRaw(&s, " ", 1);
  1043   1038         jsonAppendRaw(&s, x.aNode[i].u.zJContent, x.aNode[i].n);
  1044         -      jsonAppendRaw(&s, "\n", 1);
  1045   1039       }
         1040  +    jsonAppendRaw(&s, "\n", 1);
  1046   1041     }
  1047   1042     jsonParseReset(&x);
  1048   1043     jsonResult(&s);
  1049   1044   }
  1050   1045   
  1051   1046   /*
  1052         -** The json_test1(JSON) function parses and rebuilds the JSON string.
         1047  +** The json_test1(JSON) function return true (1) if the input is JSON
         1048  +** text generated by another json function.  It returns (0) if the input
         1049  +** is not known to be JSON.
  1053   1050   */
  1054   1051   static void jsonTest1Func(
  1055   1052     sqlite3_context *ctx,
  1056   1053     int argc,
  1057   1054     sqlite3_value **argv
  1058   1055   ){
  1059         -  JsonParse x;  /* The parse */
  1060         -  if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
  1061         -  jsonReturnJson(x.aNode, ctx, 0);
  1062         -  jsonParseReset(&x);
  1063         -}
  1064         -
  1065         -/*
  1066         -** The json_nodecount(JSON) function returns the number of nodes in the
  1067         -** input JSON string.
  1068         -*/
  1069         -static void jsonNodeCountFunc(
  1070         -  sqlite3_context *ctx,
  1071         -  int argc,
  1072         -  sqlite3_value **argv
  1073         -){
  1074         -  JsonParse x;  /* The parse */
  1075         -  if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
  1076         -  sqlite3_result_int64(ctx, (sqlite3_int64)x.nNode);
  1077         -  jsonParseReset(&x);
         1056  +  UNUSED_PARAM(argc);
         1057  +  sqlite3_result_int(ctx, sqlite3_value_subtype(argv[0])==JSON_SUBTYPE);
  1078   1058   }
  1079   1059   #endif /* SQLITE_DEBUG */
  1080   1060   
  1081   1061   /****************************************************************************
  1082   1062   ** SQL function implementations
  1083   1063   ****************************************************************************/
  1084   1064   
................................................................................
  1095   1075     int i;
  1096   1076     JsonString jx;
  1097   1077   
  1098   1078     jsonInit(&jx, ctx);
  1099   1079     jsonAppendChar(&jx, '[');
  1100   1080     for(i=0; i<argc; i++){
  1101   1081       jsonAppendSeparator(&jx);
  1102         -    jsonAppendValue(&jx, argv[i], 0);
         1082  +    jsonAppendValue(&jx, argv[i]);
  1103   1083     }
  1104   1084     jsonAppendChar(&jx, ']');
  1105   1085     jsonResult(&jx);
         1086  +  sqlite3_result_subtype(ctx, JSON_SUBTYPE);
  1106   1087   }
  1107   1088   
  1108   1089   
  1109   1090   /*
  1110   1091   ** json_array_length(JSON)
  1111   1092   ** json_array_length(JSON, PATH)
  1112   1093   **
................................................................................
  1123   1104     u32 i;
  1124   1105   
  1125   1106     if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
  1126   1107     if( x.nNode ){
  1127   1108       JsonNode *pNode;
  1128   1109       if( argc==2 ){
  1129   1110         const char *zPath = (const char*)sqlite3_value_text(argv[1]);
  1130         -      pNode = jsonLookup(&x, zPath, 0, ctx, 0);
         1111  +      pNode = jsonLookup(&x, zPath, 0, ctx);
  1131   1112       }else{
  1132   1113         pNode = x.aNode;
  1133   1114       }
  1134   1115       if( pNode==0 ){
  1135   1116         x.nErr = 1;
  1136   1117       }else if( pNode->eType==JSON_ARRAY ){
  1137   1118         assert( (pNode->jnFlags & JNODE_APPEND)==0 );
................................................................................
  1165   1146   
  1166   1147     if( argc<2 ) return;
  1167   1148     if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
  1168   1149     jsonInit(&jx, ctx);
  1169   1150     jsonAppendChar(&jx, '[');
  1170   1151     for(i=1; i<argc; i++){
  1171   1152       zPath = (const char*)sqlite3_value_text(argv[i]);
  1172         -    pNode = jsonLookup(&x, zPath, 0, ctx, 0);
         1153  +    pNode = jsonLookup(&x, zPath, 0, ctx);
  1173   1154       if( x.nErr ) break;
  1174   1155       if( argc>2 ){
  1175   1156         jsonAppendSeparator(&jx);
  1176   1157         if( pNode ){
  1177   1158           jsonRenderNode(pNode, &jx, 0);
  1178   1159         }else{
  1179   1160           jsonAppendRaw(&jx, "null", 4);
................................................................................
  1181   1162       }else if( pNode ){
  1182   1163         jsonReturn(pNode, ctx, 0);
  1183   1164       }
  1184   1165     }
  1185   1166     if( argc>2 && i==argc ){
  1186   1167       jsonAppendChar(&jx, ']');
  1187   1168       jsonResult(&jx);
         1169  +    sqlite3_result_subtype(ctx, JSON_SUBTYPE);
  1188   1170     }
  1189   1171     jsonReset(&jx);
  1190   1172     jsonParseReset(&x);
  1191   1173   }
  1192   1174   
  1193   1175   /*
  1194   1176   ** Implementation of the json_object(NAME,VALUE,...) function.  Return a JSON
................................................................................
  1219   1201         return;
  1220   1202       }
  1221   1203       jsonAppendSeparator(&jx);
  1222   1204       z = (const char*)sqlite3_value_text(argv[i]);
  1223   1205       n = (u32)sqlite3_value_bytes(argv[i]);
  1224   1206       jsonAppendString(&jx, z, n);
  1225   1207       jsonAppendChar(&jx, ':');
  1226         -    jsonAppendValue(&jx, argv[i+1], 0);
         1208  +    jsonAppendValue(&jx, argv[i+1]);
  1227   1209     }
  1228   1210     jsonAppendChar(&jx, '}');
  1229   1211     jsonResult(&jx);
         1212  +  sqlite3_result_subtype(ctx, JSON_SUBTYPE);
  1230   1213   }
  1231   1214   
  1232   1215   
  1233   1216   /*
  1234   1217   ** json_remove(JSON, PATH, ...)
  1235   1218   **
  1236   1219   ** Remove the named elements from JSON and return the result.  malformed
................................................................................
  1248   1231   
  1249   1232     if( argc<1 ) return;
  1250   1233     if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
  1251   1234     if( x.nNode ){
  1252   1235       for(i=1; i<(u32)argc; i++){
  1253   1236         zPath = (const char*)sqlite3_value_text(argv[i]);
  1254   1237         if( zPath==0 ) goto remove_done;
  1255         -      pNode = jsonLookup(&x, zPath, 0, ctx, 0);
         1238  +      pNode = jsonLookup(&x, zPath, 0, ctx);
  1256   1239         if( x.nErr ) goto remove_done;
  1257   1240         if( pNode ) pNode->jnFlags |= JNODE_REMOVE;
  1258   1241       }
  1259   1242       if( (x.aNode[0].jnFlags & JNODE_REMOVE)==0 ){
  1260   1243         jsonReturnJson(x.aNode, ctx, 0);
  1261   1244       }
  1262   1245     }
................................................................................
  1284   1267     if( (argc&1)==0 ) {
  1285   1268       jsonWrongNumArgs(ctx, "replace");
  1286   1269       return;
  1287   1270     }
  1288   1271     if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
  1289   1272     if( x.nNode ){
  1290   1273       for(i=1; i<(u32)argc; i+=2){
  1291         -      u8 jnFlags = JNODE_REPLACE;
  1292   1274         zPath = (const char*)sqlite3_value_text(argv[i]);
  1293         -      pNode = jsonLookup(&x, zPath, 0, ctx, &jnFlags);
         1275  +      pNode = jsonLookup(&x, zPath, 0, ctx);
  1294   1276         if( x.nErr ) goto replace_err;
  1295   1277         if( pNode ){
  1296         -        pNode->jnFlags &= ~JNODE_JSON;
  1297         -        pNode->jnFlags |= jnFlags;
  1298         -        pNode->iVal = i+1;
         1278  +        pNode->jnFlags |= (u8)JNODE_REPLACE;
         1279  +        pNode->iVal = (u8)(i+1);
  1299   1280         }
  1300   1281       }
  1301   1282       if( x.aNode[0].jnFlags & JNODE_REPLACE ){
  1302   1283         sqlite3_result_value(ctx, argv[x.aNode[0].iVal]);
  1303   1284       }else{
  1304   1285         jsonReturnJson(x.aNode, ctx, argv);
  1305   1286       }
................................................................................
  1336   1317     if( (argc&1)==0 ) {
  1337   1318       jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert");
  1338   1319       return;
  1339   1320     }
  1340   1321     if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
  1341   1322     if( x.nNode ){
  1342   1323       for(i=1; i<(u32)argc; i+=2){
  1343         -      u8 jnFlags = JNODE_REPLACE;
  1344   1324         zPath = (const char*)sqlite3_value_text(argv[i]);
  1345   1325         bApnd = 0;
  1346         -      pNode = jsonLookup(&x, zPath, &bApnd, ctx, &jnFlags);
         1326  +      pNode = jsonLookup(&x, zPath, &bApnd, ctx);
  1347   1327         if( x.oom ){
  1348   1328           sqlite3_result_error_nomem(ctx);
  1349   1329           goto jsonSetDone;
  1350   1330         }else if( x.nErr ){
  1351   1331           goto jsonSetDone;
  1352   1332         }else if( pNode && (bApnd || bIsSet) ){
  1353         -        pNode->jnFlags &= ~JNODE_JSON;
  1354         -        pNode->jnFlags |= jnFlags;
  1355         -        pNode->iVal = i+1;
         1333  +        pNode->jnFlags |= (u8)JNODE_REPLACE;
         1334  +        pNode->iVal = (u8)(i+1);
  1356   1335         }
  1357   1336       }
  1358   1337       if( x.aNode[0].jnFlags & JNODE_REPLACE ){
  1359   1338         sqlite3_result_value(ctx, argv[x.aNode[0].iVal]);
  1360   1339       }else{
  1361   1340         jsonReturnJson(x.aNode, ctx, argv);
  1362   1341       }
................................................................................
  1381   1360     const char *zPath;
  1382   1361   
  1383   1362     if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
  1384   1363     if( x.nNode ){
  1385   1364       JsonNode *pNode;
  1386   1365       if( argc==2 ){
  1387   1366         zPath = (const char*)sqlite3_value_text(argv[1]);
  1388         -      pNode = jsonLookup(&x, zPath, 0, ctx, 0);
         1367  +      pNode = jsonLookup(&x, zPath, 0, ctx);
  1389   1368       }else{
  1390   1369         pNode = x.aNode;
  1391   1370       }
  1392   1371       if( pNode ){
  1393   1372         sqlite3_result_text(ctx, jsonType[pNode->eType], -1, SQLITE_STATIC);
  1394   1373       }
  1395   1374     }
................................................................................
  1406   1385     sqlite3_context *ctx,
  1407   1386     int argc,
  1408   1387     sqlite3_value **argv
  1409   1388   ){
  1410   1389     JsonParse x;          /* The parse */
  1411   1390     int rc = 0;
  1412   1391   
         1392  +  UNUSED_PARAM(argc);
  1413   1393     if( jsonParse(&x, 0, (const char*)sqlite3_value_text(argv[0]))==0 
  1414   1394      && x.nNode>0
  1415   1395     ){
  1416   1396       rc = 1;
  1417   1397     }
  1418   1398     jsonParseReset(&x);
  1419   1399     sqlite3_result_int(ctx, rc);
................................................................................
  1423   1403   /****************************************************************************
  1424   1404   ** The json_each virtual table
  1425   1405   ****************************************************************************/
  1426   1406   typedef struct JsonEachCursor JsonEachCursor;
  1427   1407   struct JsonEachCursor {
  1428   1408     sqlite3_vtab_cursor base;  /* Base class - must be first */
  1429   1409     u32 iRowid;                /* The rowid */
         1410  +  u32 iBegin;                /* The first node of the scan */
  1430   1411     u32 i;                     /* Index in sParse.aNode[] of current row */
  1431   1412     u32 iEnd;                  /* EOF when i equals or exceeds this value */
  1432   1413     u8 eType;                  /* Type of top-level element */
  1433   1414     u8 bRecursive;             /* True for json_tree().  False for json_each() */
  1434   1415     char *zJson;               /* Input JSON */
  1435         -  char *zPath;               /* Path by which to filter zJson */
         1416  +  char *zRoot;               /* Path by which to filter zJson */
  1436   1417     JsonParse sParse;          /* Parse of the input JSON */
  1437   1418   };
  1438   1419   
  1439   1420   /* Constructor for the json_each virtual table */
  1440   1421   static int jsonEachConnect(
  1441   1422     sqlite3 *db,
  1442   1423     void *pAux,
................................................................................
  1451   1432   #define JEACH_KEY     0
  1452   1433   #define JEACH_VALUE   1
  1453   1434   #define JEACH_TYPE    2
  1454   1435   #define JEACH_ATOM    3
  1455   1436   #define JEACH_ID      4
  1456   1437   #define JEACH_PARENT  5
  1457   1438   #define JEACH_FULLKEY 6
  1458         -#define JEACH_JSON    7
  1459         -#define JEACH_PATH    8
         1439  +#define JEACH_PATH    7
         1440  +#define JEACH_JSON    8
         1441  +#define JEACH_ROOT    9
  1460   1442   
  1461   1443     UNUSED_PARAM(pzErr);
  1462   1444     UNUSED_PARAM(argv);
  1463   1445     UNUSED_PARAM(argc);
  1464   1446     UNUSED_PARAM(pAux);
  1465   1447     rc = sqlite3_declare_vtab(db, 
  1466         -     "CREATE TABLE x(key,value,type,atom,id,parent,fullkey,"
  1467         -                    "json HIDDEN,path HIDDEN)");
         1448  +     "CREATE TABLE x(key,value,type,atom,id,parent,fullkey,path,"
         1449  +                    "json HIDDEN,root HIDDEN)");
  1468   1450     if( rc==SQLITE_OK ){
  1469   1451       pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
  1470   1452       if( pNew==0 ) return SQLITE_NOMEM;
  1471   1453       memset(pNew, 0, sizeof(*pNew));
  1472   1454     }
  1473   1455     return rc;
  1474   1456   }
................................................................................
  1501   1483     return rc;
  1502   1484   }
  1503   1485   
  1504   1486   /* Reset a JsonEachCursor back to its original state.  Free any memory
  1505   1487   ** held. */
  1506   1488   static void jsonEachCursorReset(JsonEachCursor *p){
  1507   1489     sqlite3_free(p->zJson);
  1508         -  sqlite3_free(p->zPath);
         1490  +  sqlite3_free(p->zRoot);
  1509   1491     jsonParseReset(&p->sParse);
  1510   1492     p->iRowid = 0;
  1511   1493     p->i = 0;
  1512   1494     p->iEnd = 0;
  1513   1495     p->eType = 0;
  1514   1496     p->zJson = 0;
  1515         -  p->zPath = 0;
         1497  +  p->zRoot = 0;
  1516   1498   }
  1517   1499   
  1518   1500   /* Destructor for a jsonEachCursor object */
  1519   1501   static int jsonEachClose(sqlite3_vtab_cursor *cur){
  1520   1502     JsonEachCursor *p = (JsonEachCursor*)cur;
  1521   1503     jsonEachCursorReset(p);
  1522   1504     sqlite3_free(cur);
................................................................................
  1530   1512     return p->i >= p->iEnd;
  1531   1513   }
  1532   1514   
  1533   1515   /* Advance the cursor to the next element for json_tree() */
  1534   1516   static int jsonEachNext(sqlite3_vtab_cursor *cur){
  1535   1517     JsonEachCursor *p = (JsonEachCursor*)cur;
  1536   1518     if( p->bRecursive ){
  1537         -    if( p->i==0 ){
  1538         -      p->i = 1;
  1539         -    }else{
  1540         -      u32 iUp = p->sParse.aUp[p->i];
  1541         -      JsonNode *pUp = &p->sParse.aNode[iUp];
  1542         -      p->i++;
  1543         -      if( pUp->eType==JSON_OBJECT && (pUp->n + iUp >= p->i) ) p->i++;
  1544         -    }
         1519  +    if( p->sParse.aNode[p->i].jnFlags & JNODE_LABEL ) p->i++;
         1520  +    p->i++;
  1545   1521       p->iRowid++;
  1546         -    if( p->i<p->sParse.nNode ){
         1522  +    if( p->i<p->iEnd ){
  1547   1523         u32 iUp = p->sParse.aUp[p->i];
  1548   1524         JsonNode *pUp = &p->sParse.aNode[iUp];
  1549   1525         p->eType = pUp->eType;
  1550   1526         if( pUp->eType==JSON_ARRAY ){
  1551   1527           if( iUp==p->i-1 ){
  1552   1528             pUp->u.iKey = 0;
  1553   1529           }else{
................................................................................
  1593   1569     jsonEachComputePath(p, pStr, iUp);
  1594   1570     pNode = &p->sParse.aNode[i];
  1595   1571     pUp = &p->sParse.aNode[iUp];
  1596   1572     if( pUp->eType==JSON_ARRAY ){
  1597   1573       jsonPrintf(30, pStr, "[%d]", pUp->u.iKey);
  1598   1574     }else{
  1599   1575       assert( pUp->eType==JSON_OBJECT );
  1600         -    if( pNode->eType>=JSON_ARRAY ) pNode--;
         1576  +    if( (pNode->jnFlags & JNODE_LABEL)==0 ) pNode--;
  1601   1577       assert( pNode->eType==JSON_STRING );
         1578  +    assert( pNode->jnFlags & JNODE_LABEL );
  1602   1579       jsonPrintf(pNode->n+1, pStr, ".%.*s", pNode->n-2, pNode->u.zJContent+1);
  1603   1580     }
  1604   1581   }
  1605   1582   
  1606   1583   /* Return the value of a column */
  1607   1584   static int jsonEachColumn(
  1608   1585     sqlite3_vtab_cursor *cur,   /* The cursor */
................................................................................
  1625   1602             iKey = p->iRowid;
  1626   1603           }
  1627   1604           sqlite3_result_int64(ctx, (sqlite3_int64)iKey);
  1628   1605         }
  1629   1606         break;
  1630   1607       }
  1631   1608       case JEACH_VALUE: {
  1632         -      if( p->eType==JSON_OBJECT && p->i>0 ) pThis++;
         1609  +      if( pThis->jnFlags & JNODE_LABEL ) pThis++;
  1633   1610         jsonReturn(pThis, ctx, 0);
  1634   1611         break;
  1635   1612       }
  1636   1613       case JEACH_TYPE: {
  1637         -      if( p->eType==JSON_OBJECT && p->i>0 ) pThis++;
         1614  +      if( pThis->jnFlags & JNODE_LABEL ) pThis++;
  1638   1615         sqlite3_result_text(ctx, jsonType[pThis->eType], -1, SQLITE_STATIC);
  1639   1616         break;
  1640   1617       }
  1641   1618       case JEACH_ATOM: {
  1642         -      if( p->eType==JSON_OBJECT && p->i>0 ) pThis++;
         1619  +      if( pThis->jnFlags & JNODE_LABEL ) pThis++;
  1643   1620         if( pThis->eType>=JSON_ARRAY ) break;
  1644   1621         jsonReturn(pThis, ctx, 0);
  1645   1622         break;
  1646   1623       }
  1647   1624       case JEACH_ID: {
  1648         -      sqlite3_result_int64(ctx, (sqlite3_int64)p->i + (p->eType==JSON_OBJECT));
         1625  +      sqlite3_result_int64(ctx, 
         1626  +         (sqlite3_int64)p->i + ((pThis->jnFlags & JNODE_LABEL)!=0));
  1649   1627         break;
  1650   1628       }
  1651   1629       case JEACH_PARENT: {
  1652         -      if( p->i>0 && p->bRecursive ){
         1630  +      if( p->i>p->iBegin && p->bRecursive ){
  1653   1631           sqlite3_result_int64(ctx, (sqlite3_int64)p->sParse.aUp[p->i]);
  1654   1632         }
  1655   1633         break;
  1656   1634       }
  1657   1635       case JEACH_FULLKEY: {
  1658   1636         JsonString x;
  1659   1637         jsonInit(&x, ctx);
  1660   1638         if( p->bRecursive ){
  1661   1639           jsonEachComputePath(p, &x, p->i);
  1662   1640         }else{
  1663         -        if( p->zPath ){
  1664         -          jsonAppendRaw(&x, p->zPath, (int)strlen(p->zPath));
         1641  +        if( p->zRoot ){
         1642  +          jsonAppendRaw(&x, p->zRoot, (int)strlen(p->zRoot));
  1665   1643           }else{
  1666   1644             jsonAppendChar(&x, '$');
  1667   1645           }
  1668   1646           if( p->eType==JSON_ARRAY ){
  1669   1647             jsonPrintf(30, &x, "[%d]", p->iRowid);
  1670   1648           }else{
  1671   1649             jsonPrintf(pThis->n, &x, ".%.*s", pThis->n-2, pThis->u.zJContent+1);
  1672   1650           }
  1673   1651         }
  1674   1652         jsonResult(&x);
  1675   1653         break;
  1676   1654       }
  1677   1655       case JEACH_PATH: {
  1678         -      const char *zPath = p->zPath;
  1679         -       if( zPath==0 ){
  1680         -        if( p->bRecursive ){
  1681         -          JsonString x;
  1682         -          jsonInit(&x, ctx);
  1683         -          jsonEachComputePath(p, &x, p->sParse.aUp[p->i]);
  1684         -          jsonResult(&x);
  1685         -          break;
  1686         -        }
  1687         -        zPath = "$";
         1656  +      if( p->bRecursive ){
         1657  +        JsonString x;
         1658  +        jsonInit(&x, ctx);
         1659  +        jsonEachComputePath(p, &x, p->sParse.aUp[p->i]);
         1660  +        jsonResult(&x);
         1661  +        break;
  1688   1662         }
  1689         -      sqlite3_result_text(ctx, zPath, -1, SQLITE_STATIC);
         1663  +      /* For json_each() path and root are the same so fall through
         1664  +      ** into the root case */
         1665  +    }
         1666  +    case JEACH_ROOT: {
         1667  +      const char *zRoot = p->zRoot;
         1668  +       if( zRoot==0 ) zRoot = "$";
         1669  +      sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC);
  1690   1670         break;
  1691   1671       }
  1692   1672       default: {
  1693   1673         assert( i==JEACH_JSON );
  1694   1674         sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC);
  1695   1675         break;
  1696   1676       }
................................................................................
  1703   1683     JsonEachCursor *p = (JsonEachCursor*)cur;
  1704   1684     *pRowid = p->iRowid;
  1705   1685     return SQLITE_OK;
  1706   1686   }
  1707   1687   
  1708   1688   /* The query strategy is to look for an equality constraint on the json
  1709   1689   ** column.  Without such a constraint, the table cannot operate.  idxNum is
  1710         -** 1 if the constraint is found, 3 if the constraint and zPath are found,
         1690  +** 1 if the constraint is found, 3 if the constraint and zRoot are found,
  1711   1691   ** and 0 otherwise.
  1712   1692   */
  1713   1693   static int jsonEachBestIndex(
  1714   1694     sqlite3_vtab *tab,
  1715   1695     sqlite3_index_info *pIdxInfo
  1716   1696   ){
  1717   1697     int i;
  1718   1698     int jsonIdx = -1;
  1719         -  int pathIdx = -1;
         1699  +  int rootIdx = -1;
  1720   1700     const struct sqlite3_index_constraint *pConstraint;
  1721   1701   
  1722   1702     UNUSED_PARAM(tab);
  1723   1703     pConstraint = pIdxInfo->aConstraint;
  1724   1704     for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
  1725   1705       if( pConstraint->usable==0 ) continue;
  1726   1706       if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
  1727   1707       switch( pConstraint->iColumn ){
  1728   1708         case JEACH_JSON:   jsonIdx = i;    break;
  1729         -      case JEACH_PATH:   pathIdx = i;    break;
         1709  +      case JEACH_ROOT:   rootIdx = i;    break;
  1730   1710         default:           /* no-op */     break;
  1731   1711       }
  1732   1712     }
  1733   1713     if( jsonIdx<0 ){
  1734   1714       pIdxInfo->idxNum = 0;
  1735   1715       pIdxInfo->estimatedCost = 1e99;
  1736   1716     }else{
  1737   1717       pIdxInfo->estimatedCost = 1.0;
  1738   1718       pIdxInfo->aConstraintUsage[jsonIdx].argvIndex = 1;
  1739   1719       pIdxInfo->aConstraintUsage[jsonIdx].omit = 1;
  1740         -    if( pathIdx<0 ){
         1720  +    if( rootIdx<0 ){
  1741   1721         pIdxInfo->idxNum = 1;
  1742   1722       }else{
  1743         -      pIdxInfo->aConstraintUsage[pathIdx].argvIndex = 2;
  1744         -      pIdxInfo->aConstraintUsage[pathIdx].omit = 1;
         1723  +      pIdxInfo->aConstraintUsage[rootIdx].argvIndex = 2;
         1724  +      pIdxInfo->aConstraintUsage[rootIdx].omit = 1;
  1745   1725         pIdxInfo->idxNum = 3;
  1746   1726       }
  1747   1727     }
  1748   1728     return SQLITE_OK;
  1749   1729   }
  1750   1730   
  1751   1731   /* Start a search on a new JSON string */
................................................................................
  1752   1732   static int jsonEachFilter(
  1753   1733     sqlite3_vtab_cursor *cur,
  1754   1734     int idxNum, const char *idxStr,
  1755   1735     int argc, sqlite3_value **argv
  1756   1736   ){
  1757   1737     JsonEachCursor *p = (JsonEachCursor*)cur;
  1758   1738     const char *z;
  1759         -  const char *zPath;
         1739  +  const char *zRoot = 0;
  1760   1740     sqlite3_int64 n;
  1761   1741   
  1762   1742     UNUSED_PARAM(idxStr);
  1763   1743     UNUSED_PARAM(argc);
  1764   1744     jsonEachCursorReset(p);
  1765   1745     if( idxNum==0 ) return SQLITE_OK;
  1766   1746     z = (const char*)sqlite3_value_text(argv[0]);
  1767   1747     if( z==0 ) return SQLITE_OK;
  1768   1748     if( idxNum&2 ){
  1769         -    zPath = (const char*)sqlite3_value_text(argv[1]);
  1770         -    if( zPath==0 ) return SQLITE_OK;
  1771         -    if( zPath[0]!='$' ){
         1749  +    zRoot = (const char*)sqlite3_value_text(argv[1]);
         1750  +    if( zRoot==0 ) return SQLITE_OK;
         1751  +    if( zRoot[0]!='$' ){
  1772   1752         sqlite3_free(cur->pVtab->zErrMsg);
  1773         -      cur->pVtab->zErrMsg = jsonPathSyntaxError(zPath);
         1753  +      cur->pVtab->zErrMsg = jsonPathSyntaxError(zRoot);
  1774   1754         return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
  1775   1755       }
  1776   1756     }
  1777   1757     n = sqlite3_value_bytes(argv[0]);
  1778   1758     p->zJson = sqlite3_malloc64( n+1 );
  1779   1759     if( p->zJson==0 ) return SQLITE_NOMEM;
  1780   1760     memcpy(p->zJson, z, (size_t)n+1);
................................................................................
  1790   1770     }else if( p->bRecursive && jsonParseFindParents(&p->sParse) ){
  1791   1771       jsonEachCursorReset(p);
  1792   1772       return SQLITE_NOMEM;
  1793   1773     }else{
  1794   1774       JsonNode *pNode;
  1795   1775       if( idxNum==3 ){
  1796   1776         const char *zErr = 0;
  1797         -      p->bRecursive = 0;
  1798   1777         n = sqlite3_value_bytes(argv[1]);
  1799         -      p->zPath = sqlite3_malloc64( n+1 );
  1800         -      if( p->zPath==0 ) return SQLITE_NOMEM;
  1801         -      memcpy(p->zPath, zPath, (size_t)n+1);
  1802         -      pNode = jsonLookupStep(&p->sParse, 0, p->zPath+1, 0, &zErr);
         1778  +      p->zRoot = sqlite3_malloc64( n+1 );
         1779  +      if( p->zRoot==0 ) return SQLITE_NOMEM;
         1780  +      memcpy(p->zRoot, zRoot, (size_t)n+1);
         1781  +      pNode = jsonLookupStep(&p->sParse, 0, p->zRoot+1, 0, &zErr);
  1803   1782         if( p->sParse.nErr ){
  1804   1783           sqlite3_free(cur->pVtab->zErrMsg);
  1805   1784           cur->pVtab->zErrMsg = jsonPathSyntaxError(zErr);
  1806   1785           jsonEachCursorReset(p);
  1807   1786           return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
  1808   1787         }else if( pNode==0 ){
  1809   1788           return SQLITE_OK;
  1810   1789         }
  1811   1790       }else{
  1812   1791         pNode = p->sParse.aNode;
  1813   1792       }
  1814         -    p->i = (int)(pNode - p->sParse.aNode);
         1793  +    p->iBegin = p->i = (int)(pNode - p->sParse.aNode);
  1815   1794       p->eType = pNode->eType;
  1816   1795       if( p->eType>=JSON_ARRAY ){
  1817   1796         pNode->u.iKey = 0;
  1818   1797         p->iEnd = p->i + pNode->n + 1;
  1819         -      if( !p->bRecursive ) p->i++;
         1798  +      if( p->bRecursive ){
         1799  +        if( p->i>0 && (p->sParse.aNode[p->i-1].jnFlags & JNODE_LABEL)!=0 ){
         1800  +          p->i--;
         1801  +        }
         1802  +      }else{
         1803  +        p->i++;
         1804  +      }
  1820   1805       }else{
  1821   1806         p->iEnd = p->i+1;
  1822   1807       }
  1823   1808     }
  1824   1809     return p->sParse.oom ? SQLITE_NOMEM : SQLITE_OK;
  1825   1810   }
  1826   1811   
................................................................................
  1897   1882     unsigned int i;
  1898   1883     static const struct {
  1899   1884        const char *zName;
  1900   1885        int nArg;
  1901   1886        int flag;
  1902   1887        void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
  1903   1888     } aFunc[] = {
         1889  +    { "json",                 1, 0,   jsonRemoveFunc        },
  1904   1890       { "json_array",          -1, 0,   jsonArrayFunc         },
  1905   1891       { "json_array_length",    1, 0,   jsonArrayLengthFunc   },
  1906   1892       { "json_array_length",    2, 0,   jsonArrayLengthFunc   },
  1907   1893       { "json_extract",        -1, 0,   jsonExtractFunc       },
  1908   1894       { "json_insert",         -1, 0,   jsonSetFunc           },
  1909   1895       { "json_object",         -1, 0,   jsonObjectFunc        },
  1910   1896       { "json_remove",         -1, 0,   jsonRemoveFunc        },
................................................................................
  1914   1900       { "json_type",            2, 0,   jsonTypeFunc          },
  1915   1901       { "json_valid",           1, 0,   jsonValidFunc         },
  1916   1902   
  1917   1903   #if SQLITE_DEBUG
  1918   1904       /* DEBUG and TESTING functions */
  1919   1905       { "json_parse",           1, 0,   jsonParseFunc         },
  1920   1906       { "json_test1",           1, 0,   jsonTest1Func         },
  1921         -    { "json_nodecount",       1, 0,   jsonNodeCountFunc     },
  1922   1907   #endif
  1923   1908     };
  1924   1909   #ifndef SQLITE_OMIT_VIRTUALTABLE
  1925   1910     static const struct {
  1926   1911        const char *zName;
  1927   1912        sqlite3_module *pModule;
  1928   1913     } aMod[] = {

Changes to main.mk.

    43     43   ################################################################################
    44     44   
    45     45   # This is how we compile
    46     46   #
    47     47   TCCX =  $(TCC) $(OPTS) -I. -I$(TOP)/src -I$(TOP) 
    48     48   TCCX += -I$(TOP)/ext/rtree -I$(TOP)/ext/icu -I$(TOP)/ext/fts3
    49     49   TCCX += -I$(TOP)/ext/async -I$(TOP)/ext/userauth
           50  +TCCX += -I$(TOP)/ext/fts5
    50     51   
    51     52   # Object files for the SQLite library.
    52     53   #
    53     54   LIBOBJ+= vdbe.o parse.o \
    54     55            alter.o analyze.o attach.o auth.o \
    55     56            backup.o bitvec.o btmutex.o btree.o build.o \
    56     57            callback.o complete.o ctime.o date.o dbstat.o delete.o expr.o fault.o fkey.o \
................................................................................
   227    228     $(TOP)/ext/userauth/userauth.c \
   228    229     $(TOP)/ext/userauth/sqlite3userauth.h 
   229    230   
   230    231   SRC += \
   231    232     $(TOP)/ext/rbu/sqlite3rbu.c \
   232    233     $(TOP)/ext/rbu/sqlite3rbu.h
   233    234   
          235  +
          236  +# FTS5 things
          237  +#
          238  +FTS5_HDR = \
          239  +   $(TOP)/ext/fts5/fts5.h \
          240  +   $(TOP)/ext/fts5/fts5Int.h \
          241  +   fts5parse.h
          242  +	   
          243  +FTS5_SRC = \
          244  +   $(TOP)/ext/fts5/fts5_aux.c \
          245  +   $(TOP)/ext/fts5/fts5_buffer.c \
          246  +   $(TOP)/ext/fts5/fts5_main.c \
          247  +   $(TOP)/ext/fts5/fts5_config.c \
          248  +   $(TOP)/ext/fts5/fts5_expr.c \
          249  +   $(TOP)/ext/fts5/fts5_hash.c \
          250  +   $(TOP)/ext/fts5/fts5_index.c \
          251  +   fts5parse.c \
          252  +   $(TOP)/ext/fts5/fts5_storage.c \
          253  +   $(TOP)/ext/fts5/fts5_tokenize.c \
          254  +   $(TOP)/ext/fts5/fts5_unicode2.c \
          255  +   $(TOP)/ext/fts5/fts5_varint.c \
          256  +   $(TOP)/ext/fts5/fts5_vocab.c  \
          257  +
   234    258   
   235    259   # Generated source code files
   236    260   #
   237    261   SRC += \
   238    262     keywordhash.h \
   239    263     opcodes.c \
   240    264     opcodes.h \
................................................................................
   541    565   # Rules to build parse.c and parse.h - the outputs of lemon.
   542    566   #
   543    567   parse.h:	parse.c
   544    568   
   545    569   parse.c:	$(TOP)/src/parse.y lemon $(TOP)/addopcodes.awk
   546    570   	cp $(TOP)/src/parse.y .
   547    571   	rm -f parse.h
   548         -	./lemon $(OPTS) parse.y
          572  +	./lemon -s $(OPTS) parse.y
   549    573   	mv parse.h parse.h.temp
   550    574   	$(NAWK) -f $(TOP)/addopcodes.awk parse.h.temp >parse.h
   551    575   
   552    576   sqlite3.h:	$(TOP)/src/sqlite.h.in $(TOP)/manifest.uuid $(TOP)/VERSION $(TOP)/ext/rtree/sqlite3rtree.h
   553    577   	tclsh $(TOP)/tool/mksqlite3h.tcl $(TOP) >sqlite3.h
   554    578   
   555    579   keywordhash.h:	$(TOP)/tool/mkkeywordhash.c
................................................................................
   619    643   
   620    644   fts3_write.o:	$(TOP)/ext/fts3/fts3_write.c $(HDR) $(EXTHDR)
   621    645   	$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_write.c
   622    646   
   623    647   rtree.o:	$(TOP)/ext/rtree/rtree.c $(HDR) $(EXTHDR)
   624    648   	$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/rtree/rtree.c
   625    649   
   626         -# FTS5 things
   627         -#
   628         -FTS5_SRC = \
   629         -   $(TOP)/ext/fts5/fts5.h \
   630         -   $(TOP)/ext/fts5/fts5Int.h \
   631         -   $(TOP)/ext/fts5/fts5_aux.c \
   632         -   $(TOP)/ext/fts5/fts5_buffer.c \
   633         -   $(TOP)/ext/fts5/fts5_main.c \
   634         -   $(TOP)/ext/fts5/fts5_config.c \
   635         -   $(TOP)/ext/fts5/fts5_expr.c \
   636         -   $(TOP)/ext/fts5/fts5_hash.c \
   637         -   $(TOP)/ext/fts5/fts5_index.c \
   638         -   fts5parse.c fts5parse.h \
   639         -   $(TOP)/ext/fts5/fts5_storage.c \
   640         -   $(TOP)/ext/fts5/fts5_tokenize.c \
   641         -   $(TOP)/ext/fts5/fts5_unicode2.c \
   642         -   $(TOP)/ext/fts5/fts5_varint.c \
   643         -   $(TOP)/ext/fts5/fts5_vocab.c  \
   644         -
   645    650   fts5parse.c:	$(TOP)/ext/fts5/fts5parse.y lemon 
   646    651   	cp $(TOP)/ext/fts5/fts5parse.y .
   647    652   	rm -f fts5parse.h
   648    653   	./lemon $(OPTS) fts5parse.y
   649    654   
   650    655   fts5parse.h: fts5parse.c
   651    656   
   652         -fts5.c: $(FTS5_SRC)
          657  +fts5.c: $(FTS5_SRC) $(FTS5_HDR)
   653    658   	tclsh $(TOP)/ext/fts5/tool/mkfts5c.tcl
   654    659   	cp $(TOP)/ext/fts5/fts5.h .
   655         -
   656    660   
   657    661   userauth.o:	$(TOP)/ext/userauth/userauth.c $(HDR) $(EXTHDR)
   658    662   	$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/userauth/userauth.c
   659    663   
   660    664   sqlite3rbu.o:	$(TOP)/ext/rbu/sqlite3rbu.c $(HDR) $(EXTHDR)
   661    665   	$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/rbu/sqlite3rbu.c
   662    666   

Changes to src/analyze.c.

  1182   1182         sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid);
  1183   1183       }else{
  1184   1184         Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
  1185   1185         int j, k, regKey;
  1186   1186         regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol);
  1187   1187         for(j=0; j<pPk->nKeyCol; j++){
  1188   1188           k = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[j]);
         1189  +        assert( k>=0 && k<pTab->nCol );
  1189   1190           sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKey+j);
  1190   1191           VdbeComment((v, "%s", pTab->aCol[pPk->aiColumn[j]].zName));
  1191   1192         }
  1192   1193         sqlite3VdbeAddOp3(v, OP_MakeRecord, regKey, pPk->nKeyCol, regRowid);
  1193   1194         sqlite3ReleaseTempRange(pParse, regKey, pPk->nKeyCol);
  1194   1195       }
  1195   1196   #endif
................................................................................
  1231   1232         callStatGet(v, regStat4, STAT_GET_NDLT, regDLt);
  1232   1233         sqlite3VdbeAddOp4Int(v, seekOp, iTabCur, addrNext, regSampleRowid, 0);
  1233   1234         /* We know that the regSampleRowid row exists because it was read by
  1234   1235         ** the previous loop.  Thus the not-found jump of seekOp will never
  1235   1236         ** be taken */
  1236   1237         VdbeCoverageNeverTaken(v);
  1237   1238   #ifdef SQLITE_ENABLE_STAT3
  1238         -      sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur, 
  1239         -                                      pIdx->aiColumn[0], regSample);
         1239  +      sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iTabCur, 0, regSample);
  1240   1240   #else
  1241   1241         for(i=0; i<nCol; i++){
  1242         -        i16 iCol = pIdx->aiColumn[i];
  1243         -        sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur, iCol, regCol+i);
         1242  +        sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iTabCur, i, regCol+i);
  1244   1243         }
  1245   1244         sqlite3VdbeAddOp3(v, OP_MakeRecord, regCol, nCol, regSample);
  1246   1245   #endif
  1247   1246         sqlite3VdbeAddOp3(v, OP_MakeRecord, regTabname, 6, regTemp);
  1248   1247         sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regNewRowid);
  1249   1248         sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regTemp, regNewRowid);
  1250   1249         sqlite3VdbeAddOp2(v, OP_Goto, 1, addrNext); /* P1==1 for end-of-loop */

Changes to src/btree.c.

   593    593     for(i=0; i<=pCur->iPage; i++){
   594    594       releasePage(pCur->apPage[i]);
   595    595       pCur->apPage[i] = 0;
   596    596     }
   597    597     pCur->iPage = -1;
   598    598   }
   599    599   
          600  +/*
          601  +** The cursor passed as the only argument must point to a valid entry
          602  +** when this function is called (i.e. have eState==CURSOR_VALID). This
          603  +** function saves the current cursor key in variables pCur->nKey and
          604  +** pCur->pKey. SQLITE_OK is returned if successful or an SQLite error 
          605  +** code otherwise.
          606  +**
          607  +** If the cursor is open on an intkey table, then the integer key
          608  +** (the rowid) is stored in pCur->nKey and pCur->pKey is left set to
          609  +** NULL. If the cursor is open on a non-intkey table, then pCur->pKey is 
          610  +** set to point to a malloced buffer pCur->nKey bytes in size containing 
          611  +** the key.
          612  +*/
          613  +static int saveCursorKey(BtCursor *pCur){
          614  +  int rc;
          615  +  assert( CURSOR_VALID==pCur->eState );
          616  +  assert( 0==pCur->pKey );
          617  +  assert( cursorHoldsMutex(pCur) );
          618  +
          619  +  rc = sqlite3BtreeKeySize(pCur, &pCur->nKey);
          620  +  assert( rc==SQLITE_OK );  /* KeySize() cannot fail */
          621  +
          622  +  /* If this is an intKey table, then the above call to BtreeKeySize()
          623  +  ** stores the integer key in pCur->nKey. In this case this value is
          624  +  ** all that is required. Otherwise, if pCur is not open on an intKey
          625  +  ** table, then malloc space for and store the pCur->nKey bytes of key 
          626  +  ** data.  */
          627  +  if( 0==pCur->curIntKey ){
          628  +    void *pKey = sqlite3Malloc( pCur->nKey );
          629  +    if( pKey ){
          630  +      rc = sqlite3BtreeKey(pCur, 0, (int)pCur->nKey, pKey);
          631  +      if( rc==SQLITE_OK ){
          632  +        pCur->pKey = pKey;
          633  +      }else{
          634  +        sqlite3_free(pKey);
          635  +      }
          636  +    }else{
          637  +      rc = SQLITE_NOMEM;
          638  +    }
          639  +  }
          640  +  assert( !pCur->curIntKey || !pCur->pKey );
          641  +  return rc;
          642  +}
   600    643   
   601    644   /*
   602    645   ** Save the current cursor position in the variables BtCursor.nKey 
   603    646   ** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK.
   604    647   **
   605    648   ** The caller must ensure that the cursor is valid (has eState==CURSOR_VALID)
   606    649   ** prior to calling this routine.  
................................................................................
   613    656     assert( cursorHoldsMutex(pCur) );
   614    657   
   615    658     if( pCur->eState==CURSOR_SKIPNEXT ){
   616    659       pCur->eState = CURSOR_VALID;
   617    660     }else{
   618    661       pCur->skipNext = 0;
   619    662     }
   620         -  rc = sqlite3BtreeKeySize(pCur, &pCur->nKey);
   621         -  assert( rc==SQLITE_OK );  /* KeySize() cannot fail */
   622    663   
   623         -  /* If this is an intKey table, then the above call to BtreeKeySize()
   624         -  ** stores the integer key in pCur->nKey. In this case this value is
   625         -  ** all that is required. Otherwise, if pCur is not open on an intKey
   626         -  ** table, then malloc space for and store the pCur->nKey bytes of key 
   627         -  ** data.
   628         -  */
   629         -  if( 0==pCur->curIntKey ){
   630         -    void *pKey = sqlite3Malloc( pCur->nKey );
   631         -    if( pKey ){
   632         -      rc = sqlite3BtreeKey(pCur, 0, (int)pCur->nKey, pKey);
   633         -      if( rc==SQLITE_OK ){
   634         -        pCur->pKey = pKey;
   635         -      }else{
   636         -        sqlite3_free(pKey);
   637         -      }
   638         -    }else{
   639         -      rc = SQLITE_NOMEM;
   640         -    }
   641         -  }
   642         -  assert( !pCur->curIntKey || !pCur->pKey );
   643         -
          664  +  rc = saveCursorKey(pCur);
   644    665     if( rc==SQLITE_OK ){
   645    666       btreeReleaseAllCursorPages(pCur);
   646    667       pCur->eState = CURSOR_REQUIRESEEK;
   647    668     }
   648    669   
   649    670     invalidateOverflowCache(pCur);
   650    671     return rc;
................................................................................
  8049   8070     assert( pCur->apPage[pCur->iPage]->nOverflow==0 );
  8050   8071   
  8051   8072   end_insert:
  8052   8073     return rc;
  8053   8074   }
  8054   8075   
  8055   8076   /*
  8056         -** Delete the entry that the cursor is pointing to.  The cursor
  8057         -** is left pointing at an arbitrary location.
         8077  +** Delete the entry that the cursor is pointing to. 
         8078  +**
         8079  +** If the second parameter is zero, then the cursor is left pointing at an
         8080  +** arbitrary location after the delete. If it is non-zero, then the cursor 
         8081  +** is left in a state such that the next call to BtreeNext() or BtreePrev()
         8082  +** moves it to the same row as it would if the call to BtreeDelete() had
         8083  +** been omitted.
  8058   8084   */
  8059         -int sqlite3BtreeDelete(BtCursor *pCur){
         8085  +int sqlite3BtreeDelete(BtCursor *pCur, int bPreserve){
  8060   8086     Btree *p = pCur->pBtree;
  8061   8087     BtShared *pBt = p->pBt;              
  8062   8088     int rc;                              /* Return code */
  8063   8089     MemPage *pPage;                      /* Page to delete cell from */
  8064   8090     unsigned char *pCell;                /* Pointer to cell to delete */
  8065   8091     int iCellIdx;                        /* Index of cell to delete */
  8066   8092     int iCellDepth;                      /* Depth of node containing pCell */ 
  8067   8093     u16 szCell;                          /* Size of the cell being deleted */
         8094  +  int bSkipnext = 0;                   /* Leaf cursor in SKIPNEXT state */
  8068   8095   
  8069   8096     assert( cursorHoldsMutex(pCur) );
  8070   8097     assert( pBt->inTransaction==TRANS_WRITE );
  8071   8098     assert( (pBt->btsFlags & BTS_READ_ONLY)==0 );
  8072   8099     assert( pCur->curFlags & BTCF_WriteFlag );
  8073   8100     assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );
  8074   8101     assert( !hasReadConflicts(p, pCur->pgnoRoot) );
................................................................................
  8090   8117     if( !pPage->leaf ){
  8091   8118       int notUsed = 0;
  8092   8119       rc = sqlite3BtreePrevious(pCur, &notUsed);
  8093   8120       if( rc ) return rc;
  8094   8121     }
  8095   8122   
  8096   8123     /* Save the positions of any other cursors open on this table before
  8097         -  ** making any modifications. Make the page containing the entry to be 
  8098         -  ** deleted writable. Then free any overflow pages associated with the 
  8099         -  ** entry and finally remove the cell itself from within the page.  
  8100         -  */
         8124  +  ** making any modifications.  */
  8101   8125     if( pCur->curFlags & BTCF_Multiple ){
  8102   8126       rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
  8103   8127       if( rc ) return rc;
  8104   8128     }
  8105   8129   
  8106   8130     /* If this is a delete operation to remove a row from a table b-tree,
  8107   8131     ** invalidate any incrblob cursors open on the row being deleted.  */
  8108   8132     if( pCur->pKeyInfo==0 ){
  8109   8133       invalidateIncrblobCursors(p, pCur->info.nKey, 0);
  8110   8134     }
  8111   8135   
         8136  +  /* If the bPreserve flag is set to true, then the cursor position must
         8137  +  ** be preserved following this delete operation. If the current delete
         8138  +  ** will cause a b-tree rebalance, then this is done by saving the cursor
         8139  +  ** key and leaving the cursor in CURSOR_REQUIRESEEK state before 
         8140  +  ** returning. 
         8141  +  **
         8142  +  ** Or, if the current delete will not cause a rebalance, then the cursor
         8143  +  ** will be left in CURSOR_SKIPNEXT state pointing to the entry immediately
         8144  +  ** before or after the deleted entry. In this case set bSkipnext to true.  */
         8145  +  if( bPreserve ){
         8146  +    if( !pPage->leaf 
         8147  +     || (pPage->nFree+cellSizePtr(pPage,pCell)+2)>(int)(pBt->usableSize*2/3)
         8148  +    ){
         8149  +      /* A b-tree rebalance will be required after deleting this entry.
         8150  +      ** Save the cursor key.  */
         8151  +      rc = saveCursorKey(pCur);
         8152  +      if( rc ) return rc;
         8153  +    }else{
         8154  +      bSkipnext = 1;
         8155  +    }
         8156  +  }
         8157  +
         8158  +  /* Make the page containing the entry to be deleted writable. Then free any
         8159  +  ** overflow pages associated with the entry and finally remove the cell
         8160  +  ** itself from within the page.  */
  8112   8161     rc = sqlite3PagerWrite(pPage->pDbPage);
  8113   8162     if( rc ) return rc;
  8114   8163     rc = clearCell(pPage, pCell, &szCell);
  8115   8164     dropCell(pPage, iCellIdx, szCell, &rc);
  8116   8165     if( rc ) return rc;
  8117   8166   
  8118   8167     /* If the cell deleted was not located on a leaf page, then the cursor
................................................................................
  8158   8207       while( pCur->iPage>iCellDepth ){
  8159   8208         releasePage(pCur->apPage[pCur->iPage--]);
  8160   8209       }
  8161   8210       rc = balance(pCur);
  8162   8211     }
  8163   8212   
  8164   8213     if( rc==SQLITE_OK ){
  8165         -    moveToRoot(pCur);
         8214  +    if( bSkipnext ){
         8215  +      assert( bPreserve && pCur->iPage==iCellDepth );
         8216  +      assert( pPage->nCell>0 && iCellIdx<=pPage->nCell );
         8217  +      pCur->eState = CURSOR_SKIPNEXT;
         8218  +      if( iCellIdx>=pPage->nCell ){
         8219  +        pCur->skipNext = -1;
         8220  +        pCur->aiIdx[iCellDepth] = pPage->nCell-1;
         8221  +      }else{
         8222  +        pCur->skipNext = 1;
         8223  +      }
         8224  +    }else{
         8225  +      rc = moveToRoot(pCur);
         8226  +      if( bPreserve ){
         8227  +        pCur->eState = CURSOR_REQUIRESEEK;
         8228  +      }
         8229  +    }
  8166   8230     }
  8167   8231     return rc;
  8168   8232   }
  8169   8233   
  8170   8234   /*
  8171   8235   ** Create a new BTree table.  Write into *piTable the page
  8172   8236   ** number for the root page of the new table.

Changes to src/btree.h.

   181    181     UnpackedRecord *pUnKey,
   182    182     i64 intKey,
   183    183     int bias,
   184    184     int *pRes
   185    185   );
   186    186   int sqlite3BtreeCursorHasMoved(BtCursor*);
   187    187   int sqlite3BtreeCursorRestore(BtCursor*, int*);
   188         -int sqlite3BtreeDelete(BtCursor*);
          188  +int sqlite3BtreeDelete(BtCursor*, int);
   189    189   int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey,
   190    190                                     const void *pData, int nData,
   191    191                                     int nZero, int bias, int seekResult);
   192    192   int sqlite3BtreeFirst(BtCursor*, int *pRes);
   193    193   int sqlite3BtreeLast(BtCursor*, int *pRes);
   194    194   int sqlite3BtreeNext(BtCursor*, int *pRes);
   195    195   int sqlite3BtreeEof(BtCursor*);

Changes to src/build.c.

   353    353       return 0;
   354    354     }
   355    355   
   356    356     p = sqlite3FindTable(pParse->db, zName, zDbase);
   357    357     if( p==0 ){
   358    358       const char *zMsg = isView ? "no such view" : "no such table";
   359    359   #ifndef SQLITE_OMIT_VIRTUALTABLE
   360         -    /* If zName is the not the name of a table in the schema created using
   361         -    ** CREATE, then check to see if it is the name of an virtual table that
   362         -    ** can be an eponymous virtual table. */
   363         -    Module *pMod = (Module*)sqlite3HashFind(&pParse->db->aModule, zName);
   364         -    if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){
   365         -      return pMod->pEpoTab;
          360  +    if( sqlite3FindDbName(pParse->db, zDbase)<1 ){
          361  +      /* If zName is the not the name of a table in the schema created using
          362  +      ** CREATE, then check to see if it is the name of an virtual table that
          363  +      ** can be an eponymous virtual table. */
          364  +      Module *pMod = (Module*)sqlite3HashFind(&pParse->db->aModule, zName);
          365  +      if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){
          366  +        return pMod->pEpoTab;
          367  +      }
   366    368       }
   367    369   #endif
   368    370       if( zDbase ){
   369    371         sqlite3ErrorMsg(pParse, "%s: %s.%s", zMsg, zDbase, zName);
   370    372       }else{
   371    373         sqlite3ErrorMsg(pParse, "%s: %s", zMsg, zName);
   372    374       }
   373    375       pParse->checkSchema = 1;
   374    376     }
   375         -#if SQLITE_USER_AUTHENICATION
          377  +#if SQLITE_USER_AUTHENTICATION
   376    378     else if( pParse->db->auth.authLevel<UAUTH_User ){
   377    379       sqlite3ErrorMsg(pParse, "user not authenticated");
   378    380       p = 0;
   379    381     }
   380    382   #endif
   381    383     return p;
   382    384   }
................................................................................
   439    441   ** Reclaim the memory used by an index
   440    442   */
   441    443   static void freeIndex(sqlite3 *db, Index *p){
   442    444   #ifndef SQLITE_OMIT_ANALYZE
   443    445     sqlite3DeleteIndexSamples(db, p);
   444    446   #endif
   445    447     sqlite3ExprDelete(db, p->pPartIdxWhere);
          448  +  sqlite3ExprListDelete(db, p->aColExpr);
   446    449     sqlite3DbFree(db, p->zColAff);
   447    450     if( p->isResized ) sqlite3DbFree(db, p->azColl);
   448    451   #ifdef SQLITE_ENABLE_STAT3_OR_STAT4
   449    452     sqlite3_free(p->aiRowEst);
   450    453   #endif
   451    454     sqlite3DbFree(db, p);
   452    455   }
................................................................................
   979    982     ** indices.  Hence, the record number for the table must be allocated
   980    983     ** now.
   981    984     */
   982    985     if( !db->init.busy && (v = sqlite3GetVdbe(pParse))!=0 ){
   983    986       int j1;
   984    987       int fileFormat;
   985    988       int reg1, reg2, reg3;
          989  +    /* nullRow[] is an OP_Record encoding of a row containing 5 NULLs */
          990  +    static const char nullRow[] = { 6, 0, 0, 0, 0, 0 };
   986    991       sqlite3BeginWriteOperation(pParse, 1, iDb);
   987    992   
   988    993   #ifndef SQLITE_OMIT_VIRTUALTABLE
   989    994       if( isVirtual ){
   990    995         sqlite3VdbeAddOp0(v, OP_VBegin);
   991    996       }
   992    997   #endif
................................................................................
  1023   1028       }else
  1024   1029   #endif
  1025   1030       {
  1026   1031         pParse->addrCrTab = sqlite3VdbeAddOp2(v, OP_CreateTable, iDb, reg2);
  1027   1032       }
  1028   1033       sqlite3OpenMasterTable(pParse, iDb);
  1029   1034       sqlite3VdbeAddOp2(v, OP_NewRowid, 0, reg1);
  1030         -    sqlite3VdbeAddOp2(v, OP_Null, 0, reg3);
         1035  +    sqlite3VdbeAddOp4(v, OP_Blob, 6, reg3, 0, nullRow, P4_STATIC);
  1031   1036       sqlite3VdbeAddOp3(v, OP_Insert, 0, reg3, reg1);
  1032   1037       sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
  1033   1038       sqlite3VdbeAddOp0(v, OP_Close);
  1034   1039     }
  1035   1040   
  1036   1041     /* Normal (non-error) return. */
  1037   1042     return;
................................................................................
  1305   1310       pTab->aCol[iCol].colFlags |= COLFLAG_PRIMKEY;
  1306   1311       zType = pTab->aCol[iCol].zType;
  1307   1312       nTerm = 1;
  1308   1313     }else{
  1309   1314       nTerm = pList->nExpr;
  1310   1315       for(i=0; i<nTerm; i++){
  1311   1316         Expr *pCExpr = sqlite3ExprSkipCollate(pList->a[i].pExpr);
  1312         -      if( pCExpr && pCExpr->op==TK_ID ){
         1317  +      assert( pCExpr!=0 );
         1318  +      if( pCExpr->op==TK_ID ){
  1313   1319           const char *zCName = pCExpr->u.zToken;
  1314   1320           for(iCol=0; iCol<pTab->nCol; iCol++){
  1315   1321             if( sqlite3StrICmp(zCName, pTab->aCol[iCol].zName)==0 ){
  1316   1322               pTab->aCol[iCol].colFlags |= COLFLAG_PRIMKEY;
  1317   1323               zType = pTab->aCol[iCol].zType;
  1318   1324               break;
  1319   1325             }
................................................................................
  2843   2849       p->aSortOrder = (u8*)pExtra;
  2844   2850       p->nColumn = nCol;
  2845   2851       p->nKeyCol = nCol - 1;
  2846   2852       *ppExtra = ((char*)p) + nByte;
  2847   2853     }
  2848   2854     return p;
  2849   2855   }
         2856  +
         2857  +/*
         2858  +** Backwards Compatibility Hack:
         2859  +** 
         2860  +** Historical versions of SQLite accepted strings as column names in
         2861  +** indexes and PRIMARY KEY constraints and in UNIQUE constraints.  Example:
         2862  +**
         2863  +**     CREATE TABLE xyz(a,b,c,d,e,PRIMARY KEY('a'),UNIQUE('b','c' COLLATE trim)
         2864  +**     CREATE INDEX abc ON xyz('c','d' DESC,'e' COLLATE nocase DESC);
         2865  +**
         2866  +** This is goofy.  But to preserve backwards compatibility we continue to
         2867  +** accept it.  This routine does the necessary conversion.  It converts
         2868  +** the expression given in its argument from a TK_STRING into a TK_ID
         2869  +** if the expression is just a TK_STRING with an optional COLLATE clause.
         2870  +** If the epxression is anything other than TK_STRING, the expression is
         2871  +** unchanged.
         2872  +*/
         2873  +static void sqlite3StringToId(Expr *p){
         2874  +  if( p->op==TK_STRING ){
         2875  +    p->op = TK_ID;
         2876  +  }else if( p->op==TK_COLLATE && p->pLeft->op==TK_STRING ){
         2877  +    p->pLeft->op = TK_ID;
         2878  +  }
         2879  +}
  2850   2880   
  2851   2881   /*
  2852   2882   ** Create a new index for an SQL table.  pName1.pName2 is the name of the index 
  2853   2883   ** and pTblList is the name of the table that is to be indexed.  Both will 
  2854   2884   ** be NULL for a primary key or an index that is created to satisfy a
  2855   2885   ** UNIQUE constraint.  If pTable and pIndex are NULL, use pParse->pNewTable
  2856   2886   ** as the table to be indexed.  pParse->pNewTable is a table that is
................................................................................
  2885   2915     DbFixer sFix;        /* For assigning database names to pTable */
  2886   2916     int sortOrderMask;   /* 1 to honor DESC in index.  0 to ignore. */
  2887   2917     sqlite3 *db = pParse->db;
  2888   2918     Db *pDb;             /* The specific table containing the indexed database */
  2889   2919     int iDb;             /* Index of the database that is being written */
  2890   2920     Token *pName = 0;    /* Unqualified name of the index to create */
  2891   2921     struct ExprList_item *pListItem; /* For looping over pList */
  2892         -  const Column *pTabCol;           /* A column in the table */
  2893   2922     int nExtra = 0;                  /* Space allocated for zExtra[] */
  2894   2923     int nExtraCol;                   /* Number of extra columns needed */
  2895   2924     char *zExtra = 0;                /* Extra space after the Index object */
  2896   2925     Index *pPk = 0;      /* PRIMARY KEY index for WITHOUT ROWID tables */
  2897   2926   
  2898   2927     if( db->mallocFailed || IN_DECLARE_VTAB || pParse->nErr>0 ){
  2899   2928       goto exit_create_index;
................................................................................
  3057   3086     }
  3058   3087   
  3059   3088     /* Figure out how many bytes of space are required to store explicitly
  3060   3089     ** specified collation sequence names.
  3061   3090     */
  3062   3091     for(i=0; i<pList->nExpr; i++){
  3063   3092       Expr *pExpr = pList->a[i].pExpr;
  3064         -    if( pExpr && pExpr->op==TK_COLLATE ){
         3093  +    assert( pExpr!=0 );
         3094  +    if( pExpr->op==TK_COLLATE ){
  3065   3095         nExtra += (1 + sqlite3Strlen30(pExpr->u.zToken));
  3066   3096       }
  3067   3097     }
  3068   3098   
  3069   3099     /* 
  3070   3100     ** Allocate the index structure. 
  3071   3101     */
................................................................................
  3098   3128     */
  3099   3129     if( pDb->pSchema->file_format>=4 ){
  3100   3130       sortOrderMask = -1;   /* Honor DESC */
  3101   3131     }else{
  3102   3132       sortOrderMask = 0;    /* Ignore DESC */
  3103   3133     }
  3104   3134   
  3105         -  /* Scan the names of the columns of the table to be indexed and
  3106         -  ** load the column indices into the Index structure.  Report an error
  3107         -  ** if any column is not found.
         3135  +  /* Analyze the list of expressions that form the terms of the index and
         3136  +  ** report any errors.  In the common case where the expression is exactly
         3137  +  ** a table column, store that column in aiColumn[].  For general expressions,
         3138  +  ** populate pIndex->aColExpr and store -2 in aiColumn[].
  3108   3139     **
  3109         -  ** TODO:  Add a test to make sure that the same column is not named
  3110         -  ** more than once within the same index.  Only the first instance of
  3111         -  ** the column will ever be used by the optimizer.  Note that using the
  3112         -  ** same column more than once cannot be an error because that would 
  3113         -  ** break backwards compatibility - it needs to be a warning.
         3140  +  ** TODO: Issue a warning if two or more columns of the index are identical.
         3141  +  ** TODO: Issue a warning if the table primary key is used as part of the
         3142  +  ** index key.
  3114   3143     */
  3115   3144     for(i=0, pListItem=pList->a; i<pList->nExpr; i++, pListItem++){
  3116         -    const char *zColName;
  3117         -    Expr *pCExpr;
  3118         -    int requestedSortOrder;
         3145  +    Expr *pCExpr;                  /* The i-th index expression */
         3146  +    int requestedSortOrder;        /* ASC or DESC on the i-th expression */
  3119   3147       char *zColl;                   /* Collation sequence name */
  3120   3148   
         3149  +    sqlite3StringToId(pListItem->pExpr);
         3150  +    sqlite3ResolveSelfReference(pParse, pTab, NC_IdxExpr, pListItem->pExpr, 0);
         3151  +    if( pParse->nErr ) goto exit_create_index;
  3121   3152       pCExpr = sqlite3ExprSkipCollate(pListItem->pExpr);
  3122         -    if( pCExpr->op!=TK_ID ){
  3123         -      sqlite3ErrorMsg(pParse, "indexes on expressions not yet supported");
  3124         -      continue;
  3125         -    }
  3126         -    zColName = pCExpr->u.zToken;
  3127         -    for(j=0, pTabCol=pTab->aCol; j<pTab->nCol; j++, pTabCol++){
  3128         -      if( sqlite3StrICmp(zColName, pTabCol->zName)==0 ) break;
  3129         -    }
  3130         -    if( j>=pTab->nCol ){
  3131         -      sqlite3ErrorMsg(pParse, "table %s has no column named %s",
  3132         -        pTab->zName, zColName);
  3133         -      pParse->checkSchema = 1;
  3134         -      goto exit_create_index;
  3135         -    }
  3136         -    assert( j<=0x7fff );
  3137         -    pIndex->aiColumn[i] = (i16)j;
         3153  +    if( pCExpr->op!=TK_COLUMN ){
         3154  +      if( pTab==pParse->pNewTable ){
         3155  +        sqlite3ErrorMsg(pParse, "expressions prohibited in PRIMARY KEY and "
         3156  +                                "UNIQUE constraints");
         3157  +        goto exit_create_index;
         3158  +      }
         3159  +      if( pIndex->aColExpr==0 ){
         3160  +        ExprList *pCopy = sqlite3ExprListDup(db, pList, 0);
         3161  +        pIndex->aColExpr = pCopy;
         3162  +        if( !db->mallocFailed ){
         3163  +          assert( pCopy!=0 );
         3164  +          pListItem = &pCopy->a[i];
         3165  +        }
         3166  +      }
         3167  +      j = -2;
         3168  +      pIndex->aiColumn[i] = -2;
         3169  +      pIndex->uniqNotNull = 0;
         3170  +    }else{
         3171  +      j = pCExpr->iColumn;
         3172  +      assert( j<=0x7fff );
         3173  +      if( j<0 ){
         3174  +        j = pTab->iPKey;
         3175  +      }else if( pTab->aCol[j].notNull==0 ){
         3176  +        pIndex->uniqNotNull = 0;
         3177  +      }
         3178  +      pIndex->aiColumn[i] = (i16)j;
         3179  +    }
         3180  +    zColl = 0;
  3138   3181       if( pListItem->pExpr->op==TK_COLLATE ){
  3139   3182         int nColl;
  3140   3183         zColl = pListItem->pExpr->u.zToken;
  3141   3184         nColl = sqlite3Strlen30(zColl) + 1;
  3142   3185         assert( nExtra>=nColl );
  3143   3186         memcpy(zExtra, zColl, nColl);
  3144   3187         zColl = zExtra;
  3145   3188         zExtra += nColl;
  3146   3189         nExtra -= nColl;
  3147         -    }else{
         3190  +    }else if( j>=0 ){
  3148   3191         zColl = pTab->aCol[j].zColl;
  3149         -      if( !zColl ) zColl = "BINARY";
  3150   3192       }
         3193  +    if( !zColl ) zColl = "BINARY";
  3151   3194       if( !db->init.busy && !sqlite3LocateCollSeq(pParse, zColl) ){
  3152   3195         goto exit_create_index;
  3153   3196       }
  3154   3197       pIndex->azColl[i] = zColl;
  3155   3198       requestedSortOrder = pListItem->sortOrder & sortOrderMask;
  3156   3199       pIndex->aSortOrder[i] = (u8)requestedSortOrder;
  3157         -    if( pTab->aCol[j].notNull==0 ) pIndex->uniqNotNull = 0;
  3158   3200     }
         3201  +
         3202  +  /* Append the table key to the end of the index.  For WITHOUT ROWID
         3203  +  ** tables (when pPk!=0) this will be the declared PRIMARY KEY.  For
         3204  +  ** normal tables (when pPk==0) this will be the rowid.
         3205  +  */
  3159   3206     if( pPk ){
  3160   3207       for(j=0; j<pPk->nKeyCol; j++){
  3161   3208         int x = pPk->aiColumn[j];
         3209  +      assert( x>=0 );
  3162   3210         if( hasColumn(pIndex->aiColumn, pIndex->nKeyCol, x) ){
  3163   3211           pIndex->nColumn--; 
  3164   3212         }else{
  3165   3213           pIndex->aiColumn[i] = x;
  3166   3214           pIndex->azColl[i] = pPk->azColl[j];
  3167   3215           pIndex->aSortOrder[i] = pPk->aSortOrder[j];
  3168   3216           i++;
................................................................................
  3205   3253         assert( pIdx->idxType!=SQLITE_IDXTYPE_APPDEF );
  3206   3254         assert( IsUniqueIndex(pIndex) );
  3207   3255   
  3208   3256         if( pIdx->nKeyCol!=pIndex->nKeyCol ) continue;
  3209   3257         for(k=0; k<pIdx->nKeyCol; k++){
  3210   3258           const char *z1;
  3211   3259           const char *z2;
         3260  +        assert( pIdx->aiColumn[k]>=0 );
  3212   3261           if( pIdx->aiColumn[k]!=pIndex->aiColumn[k] ) break;
  3213   3262           z1 = pIdx->azColl[k];
  3214   3263           z2 = pIndex->azColl[k];
  3215   3264           if( z1!=z2 && sqlite3StrICmp(z1, z2) ) break;
  3216   3265         }
  3217   3266         if( k==pIdx->nKeyCol ){
  3218   3267           if( pIdx->onError!=pIndex->onError ){
................................................................................
  3236   3285         }
  3237   3286       }
  3238   3287     }
  3239   3288   
  3240   3289     /* Link the new Index structure to its table and to the other
  3241   3290     ** in-memory database structures. 
  3242   3291     */
         3292  +  assert( pParse->nErr==0 );
  3243   3293     if( db->init.busy ){
  3244   3294       Index *p;
  3245   3295       assert( sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) );
  3246   3296       p = sqlite3HashInsert(&pIndex->pSchema->idxHash, 
  3247   3297                             pIndex->zName, pIndex);
  3248   3298       if( p ){
  3249   3299         assert( p==pIndex );  /* Malloc must have failed */
................................................................................
  3265   3315     ** of a WITHOUT ROWID table.
  3266   3316     **
  3267   3317     ** If pTblName==0 it means this index is generated as an implied PRIMARY KEY
  3268   3318     ** or UNIQUE index in a CREATE TABLE statement.  Since the table
  3269   3319     ** has just been created, it contains no data and the index initialization
  3270   3320     ** step can be skipped.
  3271   3321     */
  3272         -  else if( pParse->nErr==0 && (HasRowid(pTab) || pTblName!=0) ){
         3322  +  else if( HasRowid(pTab) || pTblName!=0 ){
  3273   3323       Vdbe *v;
  3274   3324       char *zStmt;
  3275   3325       int iMem = ++pParse->nMem;
  3276   3326   
  3277   3327       v = sqlite3GetVdbe(pParse);
  3278   3328       if( v==0 ) goto exit_create_index;
  3279   3329   
................................................................................
  4095   4145   ){
  4096   4146     char *zErr;
  4097   4147     int j;
  4098   4148     StrAccum errMsg;
  4099   4149     Table *pTab = pIdx->pTable;
  4100   4150   
  4101   4151     sqlite3StrAccumInit(&errMsg, pParse->db, 0, 0, 200);
  4102         -  for(j=0; j<pIdx->nKeyCol; j++){
  4103         -    char *zCol = pTab->aCol[pIdx->aiColumn[j]].zName;
  4104         -    if( j ) sqlite3StrAccumAppend(&errMsg, ", ", 2);
  4105         -    sqlite3StrAccumAppendAll(&errMsg, pTab->zName);
  4106         -    sqlite3StrAccumAppend(&errMsg, ".", 1);
  4107         -    sqlite3StrAccumAppendAll(&errMsg, zCol);
         4152  +  if( pIdx->aColExpr ){
         4153  +    sqlite3XPrintf(&errMsg, 0, "index '%q'", pIdx->zName);
         4154  +  }else{
         4155  +    for(j=0; j<pIdx->nKeyCol; j++){
         4156  +      char *zCol;
         4157  +      assert( pIdx->aiColumn[j]>=0 );
         4158  +      zCol = pTab->aCol[pIdx->aiColumn[j]].zName;
         4159  +      if( j ) sqlite3StrAccumAppend(&errMsg, ", ", 2);
         4160  +      sqlite3XPrintf(&errMsg, 0, "%s.%s", pTab->zName, zCol);
         4161  +    }
  4108   4162     }
  4109   4163     zErr = sqlite3StrAccumFinish(&errMsg);
  4110   4164     sqlite3HaltConstraint(pParse, 
  4111   4165       IsPrimaryKeyIndex(pIdx) ? SQLITE_CONSTRAINT_PRIMARYKEY 
  4112   4166                               : SQLITE_CONSTRAINT_UNIQUE,
  4113   4167       onError, zErr, P4_DYNAMIC, P5_ConstraintUnique);
  4114   4168   }

Changes to src/date.c.

  1111   1111   ** This function registered all of the above C functions as SQL
  1112   1112   ** functions.  This should be the only routine in this file with
  1113   1113   ** external linkage.
  1114   1114   */
  1115   1115   void sqlite3RegisterDateTimeFunctions(void){
  1116   1116     static SQLITE_WSD FuncDef aDateTimeFuncs[] = {
  1117   1117   #ifndef SQLITE_OMIT_DATETIME_FUNCS
  1118         -    FUNCTION(julianday,        -1, 0, 0, juliandayFunc ),
  1119         -    FUNCTION(date,             -1, 0, 0, dateFunc      ),
  1120         -    FUNCTION(time,             -1, 0, 0, timeFunc      ),
  1121         -    FUNCTION(datetime,         -1, 0, 0, datetimeFunc  ),
  1122         -    FUNCTION(strftime,         -1, 0, 0, strftimeFunc  ),
  1123         -    FUNCTION(current_time,      0, 0, 0, ctimeFunc     ),
  1124         -    FUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc),
  1125         -    FUNCTION(current_date,      0, 0, 0, cdateFunc     ),
         1118  +    DFUNCTION(julianday,        -1, 0, 0, juliandayFunc ),
         1119  +    DFUNCTION(date,             -1, 0, 0, dateFunc      ),
         1120  +    DFUNCTION(time,             -1, 0, 0, timeFunc      ),
         1121  +    DFUNCTION(datetime,         -1, 0, 0, datetimeFunc  ),
         1122  +    DFUNCTION(strftime,         -1, 0, 0, strftimeFunc  ),
         1123  +    DFUNCTION(current_time,      0, 0, 0, ctimeFunc     ),
         1124  +    DFUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc),
         1125  +    DFUNCTION(current_date,      0, 0, 0, cdateFunc     ),
  1126   1126   #else
  1127   1127       STR_FUNCTION(current_time,      0, "%H:%M:%S",          0, currentTimeFunc),
  1128   1128       STR_FUNCTION(current_date,      0, "%Y-%m-%d",          0, currentTimeFunc),
  1129   1129       STR_FUNCTION(current_timestamp, 0, "%Y-%m-%d %H:%M:%S", 0, currentTimeFunc),
  1130   1130   #endif
  1131   1131     };
  1132   1132     int i;

Changes to src/dbstat.c.

    12     12   **
    13     13   ** This file contains an implementation of the "dbstat" virtual table.
    14     14   **
    15     15   ** The dbstat virtual table is used to extract low-level formatting
    16     16   ** information from an SQLite database in order to implement the
    17     17   ** "sqlite3_analyzer" utility.  See the ../tool/spaceanal.tcl script
    18     18   ** for an example implementation.
           19  +**
           20  +** Additional information is available on the "dbstat.html" page of the
           21  +** official SQLite documentation.
    19     22   */
    20     23   
    21     24   #include "sqliteInt.h"   /* Requires access to internal data structures */
    22     25   #if (defined(SQLITE_ENABLE_DBSTAT_VTAB) || defined(SQLITE_TEST)) \
    23     26       && !defined(SQLITE_OMIT_VIRTUALTABLE)
    24     27   
    25     28   /*
................................................................................
    60     63     "  pageno     INTEGER,          /* Page number */"                        \
    61     64     "  pagetype   STRING,           /* 'internal', 'leaf' or 'overflow' */"   \
    62     65     "  ncell      INTEGER,          /* Cells on page (0 for overflow) */"     \
    63     66     "  payload    INTEGER,          /* Bytes of payload on this page */"      \
    64     67     "  unused     INTEGER,          /* Bytes of unused space on this page */" \
    65     68     "  mx_payload INTEGER,          /* Largest payload size of all cells */"  \
    66     69     "  pgoffset   INTEGER,          /* Offset of page in file */"             \
    67         -  "  pgsize     INTEGER           /* Size of the page */"                   \
           70  +  "  pgsize     INTEGER,          /* Size of the page */"                   \
           71  +  "  schema     TEXT HIDDEN       /* Database schema being analyzed */"     \
    68     72     ");"
    69     73   
    70     74   
    71     75   typedef struct StatTable StatTable;
    72     76   typedef struct StatCursor StatCursor;
    73     77   typedef struct StatPage StatPage;
    74     78   typedef struct StatCell StatCell;
................................................................................
    98    102     int nMxPayload;                 /* Largest payload of any cell on this page */
    99    103   };
   100    104   
   101    105   struct StatCursor {
   102    106     sqlite3_vtab_cursor base;
   103    107     sqlite3_stmt *pStmt;            /* Iterates through set of root pages */
   104    108     int isEof;                      /* After pStmt has returned SQLITE_DONE */
          109  +  int iDb;                        /* Schema used for this query */
   105    110   
   106    111     StatPage aPage[32];
   107    112     int iPage;                      /* Current entry in aPage[] */
   108    113   
   109    114     /* Values to return. */
   110    115     char *zName;                    /* Value of 'name' column */
   111    116     char *zPath;                    /* Value of 'path' column */
................................................................................
   175    180   static int statDisconnect(sqlite3_vtab *pVtab){
   176    181     sqlite3_free(pVtab);
   177    182     return SQLITE_OK;
   178    183   }
   179    184   
   180    185   /*
   181    186   ** There is no "best-index". This virtual table always does a linear
   182         -** scan of the binary VFS log file.
          187  +** scan.  However, a schema=? constraint should cause this table to
          188  +** operate on a different database schema, so check for it.
          189  +**
          190  +** idxNum is normally 0, but will be 1 if a schema=? constraint exists.
   183    191   */
   184    192   static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
          193  +  int i;
          194  +
          195  +  pIdxInfo->estimatedCost = 1.0e6;  /* Initial cost estimate */
          196  +
          197  +  /* Look for a valid schema=? constraint.  If found, change the idxNum to
          198  +  ** 1 and request the value of that constraint be sent to xFilter.  And
          199  +  ** lower the cost estimate to encourage the constrained version to be
          200  +  ** used.
          201  +  */
          202  +  for(i=0; i<pIdxInfo->nConstraint; i++){
          203  +    if( pIdxInfo->aConstraint[i].usable==0 ) continue;
          204  +    if( pIdxInfo->aConstraint[i].op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
          205  +    if( pIdxInfo->aConstraint[i].iColumn!=10 ) continue;
          206  +    pIdxInfo->idxNum = 1;
          207  +    pIdxInfo->estimatedCost = 1.0;
          208  +    pIdxInfo->aConstraintUsage[i].argvIndex = 1;
          209  +    pIdxInfo->aConstraintUsage[i].omit = 1;
          210  +    break;
          211  +  }
          212  +
   185    213   
   186    214     /* Records are always returned in ascending order of (name, path). 
   187    215     ** If this will satisfy the client, set the orderByConsumed flag so that 
   188    216     ** SQLite does not do an external sort.
   189    217     */
   190    218     if( ( pIdxInfo->nOrderBy==1
   191    219        && pIdxInfo->aOrderBy[0].iColumn==0
................................................................................
   197    225        && pIdxInfo->aOrderBy[1].iColumn==1
   198    226        && pIdxInfo->aOrderBy[1].desc==0
   199    227        )
   200    228     ){
   201    229       pIdxInfo->orderByConsumed = 1;
   202    230     }
   203    231   
   204         -  pIdxInfo->estimatedCost = 10.0;
   205    232     return SQLITE_OK;
   206    233   }
   207    234   
   208    235   /*
   209    236   ** Open a new statvfs cursor.
   210    237   */
   211    238   static int statOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
   212    239     StatTable *pTab = (StatTable *)pVTab;
   213    240     StatCursor *pCsr;
   214         -  int rc;
   215    241   
   216    242     pCsr = (StatCursor *)sqlite3_malloc64(sizeof(StatCursor));
   217    243     if( pCsr==0 ){
   218         -    rc = SQLITE_NOMEM;
          244  +    return SQLITE_NOMEM;
   219    245     }else{
   220         -    char *zSql;
   221    246       memset(pCsr, 0, sizeof(StatCursor));
   222    247       pCsr->base.pVtab = pVTab;
   223         -
   224         -    zSql = sqlite3_mprintf(
   225         -        "SELECT 'sqlite_master' AS name, 1 AS rootpage, 'table' AS type"
   226         -        "  UNION ALL  "
   227         -        "SELECT name, rootpage, type"
   228         -        "  FROM \"%w\".sqlite_master WHERE rootpage!=0"
   229         -        "  ORDER BY name", pTab->db->aDb[pTab->iDb].zName);
   230         -    if( zSql==0 ){
   231         -      rc = SQLITE_NOMEM;
   232         -    }else{
   233         -      rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0);
   234         -      sqlite3_free(zSql);
   235         -    }
   236         -    if( rc!=SQLITE_OK ){
   237         -      sqlite3_free(pCsr);
   238         -      pCsr = 0;
   239         -    }
          248  +    pCsr->iDb = pTab->iDb;
   240    249     }
   241    250   
   242    251     *ppCursor = (sqlite3_vtab_cursor *)pCsr;
   243         -  return rc;
          252  +  return SQLITE_OK;
   244    253   }
   245    254   
   246    255   static void statClearPage(StatPage *p){
   247    256     int i;
   248    257     if( p->aCell ){
   249    258       for(i=0; i<p->nCell; i++){
   250    259         sqlite3_free(p->aCell[i].aOvfl);
................................................................................
   261    270     sqlite3_reset(pCsr->pStmt);
   262    271     for(i=0; i<ArraySize(pCsr->aPage); i++){
   263    272       statClearPage(&pCsr->aPage[i]);
   264    273     }
   265    274     pCsr->iPage = 0;
   266    275     sqlite3_free(pCsr->zPath);
   267    276     pCsr->zPath = 0;
          277  +  pCsr->isEof = 0;
   268    278   }
   269    279   
   270    280   /*
   271    281   ** Close a statvfs cursor.
   272    282   */
   273    283   static int statClose(sqlite3_vtab_cursor *pCursor){
   274    284     StatCursor *pCsr = (StatCursor *)pCursor;
................................................................................
   423    433   */
   424    434   static int statNext(sqlite3_vtab_cursor *pCursor){
   425    435     int rc;
   426    436     int nPayload;
   427    437     char *z;
   428    438     StatCursor *pCsr = (StatCursor *)pCursor;
   429    439     StatTable *pTab = (StatTable *)pCursor->pVtab;
   430         -  Btree *pBt = pTab->db->aDb[pTab->iDb].pBt;
          440  +  Btree *pBt = pTab->db->aDb[pCsr->iDb].pBt;
   431    441     Pager *pPager = sqlite3BtreePager(pBt);
   432    442   
   433    443     sqlite3_free(pCsr->zPath);
   434    444     pCsr->zPath = 0;
   435    445   
   436    446   statNextRestart:
   437    447     if( pCsr->aPage[0].pPg==0 ){
................................................................................
   561    571   
   562    572   static int statFilter(
   563    573     sqlite3_vtab_cursor *pCursor, 
   564    574     int idxNum, const char *idxStr,
   565    575     int argc, sqlite3_value **argv
   566    576   ){
   567    577     StatCursor *pCsr = (StatCursor *)pCursor;
          578  +  StatTable *pTab = (StatTable*)(pCursor->pVtab);
          579  +  char *zSql;
          580  +  int rc = SQLITE_OK;
          581  +  char *zMaster;
   568    582   
          583  +  if( idxNum==1 ){
          584  +    const char *zDbase = (const char*)sqlite3_value_text(argv[0]);
          585  +    pCsr->iDb = sqlite3FindDbName(pTab->db, zDbase);
          586  +    if( pCsr->iDb<0 ){
          587  +      sqlite3_free(pCursor->pVtab->zErrMsg);
          588  +      pCursor->pVtab->zErrMsg = sqlite3_mprintf("no such schema: %s", zDbase);
          589  +      return pCursor->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
          590  +    }
          591  +  }else{
          592  +    pCsr->iDb = pTab->iDb;
          593  +  }
   569    594     statResetCsr(pCsr);
   570         -  return statNext(pCursor);
          595  +  sqlite3_finalize(pCsr->pStmt);
          596  +  pCsr->pStmt = 0;
          597  +  zMaster = pCsr->iDb==1 ? "sqlite_temp_master" : "sqlite_master";
          598  +  zSql = sqlite3_mprintf(
          599  +      "SELECT 'sqlite_master' AS name, 1 AS rootpage, 'table' AS type"
          600  +      "  UNION ALL  "
          601  +      "SELECT name, rootpage, type"
          602  +      "  FROM \"%w\".%s WHERE rootpage!=0"
          603  +      "  ORDER BY name", pTab->db->aDb[pCsr->iDb].zName, zMaster);
          604  +  if( zSql==0 ){
          605  +    return SQLITE_NOMEM;
          606  +  }else{
          607  +    rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0);
          608  +    sqlite3_free(zSql);
          609  +  }
          610  +
          611  +  if( rc==SQLITE_OK ){
          612  +    rc = statNext(pCursor);
          613  +  }
          614  +  return rc;
   571    615   }
   572    616   
   573    617   static int statColumn(
   574    618     sqlite3_vtab_cursor *pCursor, 
   575    619     sqlite3_context *ctx, 
   576    620     int i
   577    621   ){
................................................................................
   600    644         break;
   601    645       case 7:            /* mx_payload */
   602    646         sqlite3_result_int(ctx, pCsr->nMxPayload);
   603    647         break;
   604    648       case 8:            /* pgoffset */
   605    649         sqlite3_result_int64(ctx, pCsr->iOffset);
   606    650         break;
   607         -    default:           /* pgsize */
   608         -      assert( i==9 );
          651  +    case 9:            /* pgsize */
   609    652         sqlite3_result_int(ctx, pCsr->szPage);
   610    653         break;
          654  +    default: {          /* schema */
          655  +      sqlite3 *db = sqlite3_context_db_handle(ctx);
          656  +      int iDb = pCsr->iDb;
          657  +      sqlite3_result_text(ctx, db->aDb[iDb].zName, -1, SQLITE_STATIC);
          658  +      break;
          659  +    }
   611    660     }
   612    661     return SQLITE_OK;
   613    662   }
   614    663   
   615    664   static int statRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
   616    665     StatCursor *pCsr = (StatCursor *)pCursor;
   617    666     *pRowid = pCsr->iPageno;

Changes to src/delete.c.

   231    231     int nIdx;              /* Number of indices */
   232    232     sqlite3 *db;           /* Main database structure */
   233    233     AuthContext sContext;  /* Authorization context */
   234    234     NameContext sNC;       /* Name context to resolve expressions in */
   235    235     int iDb;               /* Database number */
   236    236     int memCnt = -1;       /* Memory cell used for change counting */
   237    237     int rcauth;            /* Value returned by authorization callback */
   238         -  int okOnePass;         /* True for one-pass algorithm without the FIFO */
          238  +  int eOnePass;          /* ONEPASS_OFF or _SINGLE or _MULTI */
   239    239     int aiCurOnePass[2];   /* The write cursors opened by WHERE_ONEPASS */
   240    240     u8 *aToOpen = 0;       /* Open cursor iTabCur+j if aToOpen[j] is true */
   241    241     Index *pPk;            /* The PRIMARY KEY index on the table */
   242    242     int iPk = 0;           /* First of nPk registers holding PRIMARY KEY value */
   243    243     i16 nPk = 1;           /* Number of columns in the PRIMARY KEY */
   244    244     int iKey;              /* Memory cell holding key of row to be deleted */
   245    245     i16 nKey;              /* Number of memory cells in the row key */
   246    246     int iEphCur = 0;       /* Ephemeral table holding all primary key values */
   247    247     int iRowSet = 0;       /* Register for rowset of rows to delete */
   248    248     int addrBypass = 0;    /* Address of jump over the delete logic */
   249    249     int addrLoop = 0;      /* Top of the delete loop */
   250         -  int addrDelete = 0;    /* Jump directly to the delete logic */
   251    250     int addrEphOpen = 0;   /* Instruction to open the Ephemeral table */
   252    251    
   253    252   #ifndef SQLITE_OMIT_TRIGGER
   254    253     int isView;                  /* True if attempting to delete from a view */
   255    254     Trigger *pTrigger;           /* List of table triggers, if required */
          255  +  int bComplex;                /* True if there are either triggers or FKs */
   256    256   #endif
   257    257   
   258    258     memset(&sContext, 0, sizeof(sContext));
   259    259     db = pParse->db;
   260    260     if( pParse->nErr || db->mallocFailed ){
   261    261       goto delete_from_cleanup;
   262    262     }
................................................................................
   272    272   
   273    273     /* Figure out if we have any triggers and if the table being
   274    274     ** deleted from is a view
   275    275     */
   276    276   #ifndef SQLITE_OMIT_TRIGGER
   277    277     pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
   278    278     isView = pTab->pSelect!=0;
          279  +  bComplex = pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0);
   279    280   #else
   280    281   # define pTrigger 0
   281    282   # define isView 0
          283  +# define bComplex 0
   282    284   #endif
   283    285   #ifdef SQLITE_OMIT_VIEW
   284    286   # undef isView
   285    287   # define isView 0
   286    288   #endif
   287    289   
   288    290     /* If pTab is really a view, make sure it has been initialized.
................................................................................
   355    357     }
   356    358   
   357    359   #ifndef SQLITE_OMIT_TRUNCATE_OPTIMIZATION
   358    360     /* Special case: A DELETE without a WHERE clause deletes everything.
   359    361     ** It is easier just to erase the whole table. Prior to version 3.6.5,
   360    362     ** this optimization caused the row change count (the value returned by 
   361    363     ** API function sqlite3_count_changes) to be set incorrectly.  */
   362         -  if( rcauth==SQLITE_OK && pWhere==0 && !pTrigger && !IsVirtual(pTab) 
   363         -   && 0==sqlite3FkRequired(pParse, pTab, 0, 0)
          364  +  if( rcauth==SQLITE_OK
          365  +   && pWhere==0
          366  +   && !bComplex
          367  +   && !IsVirtual(pTab)
   364    368     ){
   365    369       assert( !isView );
   366    370       sqlite3TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName);
   367    371       if( HasRowid(pTab) ){
   368    372         sqlite3VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt,
   369    373                           pTab->zName, P4_STATIC);
   370    374       }
................................................................................
   371    375       for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
   372    376         assert( pIdx->pSchema==pTab->pSchema );
   373    377         sqlite3VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb);
   374    378       }
   375    379     }else
   376    380   #endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */
   377    381     {
          382  +    u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK;
          383  +    wcf |= (bComplex ? 0 : WHERE_ONEPASS_MULTIROW);
   378    384       if( HasRowid(pTab) ){
   379    385         /* For a rowid table, initialize the RowSet to an empty set */
   380    386         pPk = 0;
   381    387         nPk = 1;
   382    388         iRowSet = ++pParse->nMem;
   383    389         sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet);
   384    390       }else{
................................................................................
   391    397         pParse->nMem += nPk;
   392    398         iEphCur = pParse->nTab++;
   393    399         addrEphOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEphCur, nPk);
   394    400         sqlite3VdbeSetP4KeyInfo(pParse, pPk);
   395    401       }
   396    402     
   397    403       /* Construct a query to find the rowid or primary key for every row
   398         -    ** to be deleted, based on the WHERE clause.
          404  +    ** to be deleted, based on the WHERE clause. Set variable eOnePass
          405  +    ** to indicate the strategy used to implement this delete:
          406  +    **
          407  +    **  ONEPASS_OFF:    Two-pass approach - use a FIFO for rowids/PK values.
          408  +    **  ONEPASS_SINGLE: One-pass approach - at most one row deleted.
          409  +    **  ONEPASS_MULTI:  One-pass approach - any number of rows may be deleted.
   399    410       */
   400         -    pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, 
   401         -                               WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK,
   402         -                               iTabCur+1);
          411  +    pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, wcf, iTabCur+1);
   403    412       if( pWInfo==0 ) goto delete_from_cleanup;
   404         -    okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
          413  +    eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
          414  +    assert( IsVirtual(pTab)==0 || eOnePass==ONEPASS_OFF );
   405    415     
   406    416       /* Keep track of the number of rows to be deleted */
   407    417       if( db->flags & SQLITE_CountRows ){
   408    418         sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1);
   409    419       }
   410    420     
   411    421       /* Extract the rowid or primary key for the current row */
   412    422       if( pPk ){
   413    423         for(i=0; i<nPk; i++){
          424  +        assert( pPk->aiColumn[i]>=(-1) );
   414    425           sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur,
   415    426                                           pPk->aiColumn[i], iPk+i);
   416    427         }
   417    428         iKey = iPk;
   418    429       }else{
   419    430         iKey = pParse->nMem + 1;
   420    431         iKey = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iTabCur, iKey, 0);
   421    432         if( iKey>pParse->nMem ) pParse->nMem = iKey;
   422    433       }
   423    434     
   424         -    if( okOnePass ){
   425         -      /* For ONEPASS, no need to store the rowid/primary-key.  There is only
          435  +    if( eOnePass!=ONEPASS_OFF ){
          436  +      /* For ONEPASS, no need to store the rowid/primary-key. There is only
   426    437         ** one, so just keep it in its register(s) and fall through to the
   427         -      ** delete code.
   428         -      */
          438  +      ** delete code.  */
   429    439         nKey = nPk; /* OP_Found will use an unpacked key */
   430    440         aToOpen = sqlite3DbMallocRaw(db, nIdx+2);
   431    441         if( aToOpen==0 ){
   432    442           sqlite3WhereEnd(pWInfo);
   433    443           goto delete_from_cleanup;
   434    444         }
   435    445         memset(aToOpen, 1, nIdx+1);
   436    446         aToOpen[nIdx+1] = 0;
   437    447         if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iTabCur] = 0;
   438    448         if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iTabCur] = 0;
   439    449         if( addrEphOpen ) sqlite3VdbeChangeToNoop(v, addrEphOpen);
   440         -      addrDelete = sqlite3VdbeAddOp0(v, OP_Goto); /* Jump to DELETE logic */
   441         -    }else if( pPk ){
   442         -      /* Construct a composite key for the row to be deleted and remember it */
   443         -      iKey = ++pParse->nMem;
   444         -      nKey = 0;   /* Zero tells OP_Found to use a composite key */
   445         -      sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, iKey,
   446         -                        sqlite3IndexAffinityStr(pParse->db, pPk), nPk);
   447         -      sqlite3VdbeAddOp2(v, OP_IdxInsert, iEphCur, iKey);
   448    450       }else{
   449         -      /* Get the rowid of the row to be deleted and remember it in the RowSet */
   450         -      nKey = 1;  /* OP_Seek always uses a single rowid */
   451         -      sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, iKey);
          451  +      if( pPk ){
          452  +        /* Add the PK key for this row to the temporary table */
          453  +        iKey = ++pParse->nMem;
          454  +        nKey = 0;   /* Zero tells OP_Found to use a composite key */
          455  +        sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, iKey,
          456  +            sqlite3IndexAffinityStr(pParse->db, pPk), nPk);
          457  +        sqlite3VdbeAddOp2(v, OP_IdxInsert, iEphCur, iKey);
          458  +      }else{
          459  +        /* Add the rowid of the row to be deleted to the RowSet */
          460  +        nKey = 1;  /* OP_Seek always uses a single rowid */
          461  +        sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, iKey);
          462  +      }
   452    463       }
   453    464     
   454         -    /* End of the WHERE loop */
   455         -    sqlite3WhereEnd(pWInfo);
   456         -    if( okOnePass ){
   457         -      /* Bypass the delete logic below if the WHERE loop found zero rows */
          465  +    /* If this DELETE cannot use the ONEPASS strategy, this is the 
          466  +    ** end of the WHERE loop */
          467  +    if( eOnePass!=ONEPASS_OFF ){
   458    468         addrBypass = sqlite3VdbeMakeLabel(v);
   459         -      sqlite3VdbeGoto(v, addrBypass);
   460         -      sqlite3VdbeJumpHere(v, addrDelete);
          469  +    }else{
          470  +      sqlite3WhereEnd(pWInfo);
   461    471       }
   462    472     
   463    473       /* Unless this is a view, open cursors for the table we are 
   464    474       ** deleting from and all its indices. If this is a view, then the
   465    475       ** only effect this statement has is to fire the INSTEAD OF 
   466    476       ** triggers.
   467    477       */
   468    478       if( !isView ){
          479  +      int iAddrOnce = 0;
          480  +      if( eOnePass==ONEPASS_MULTI ){
          481  +        iAddrOnce = sqlite3CodeOnce(pParse); VdbeCoverage(v);
          482  +      }
   469    483         testcase( IsVirtual(pTab) );
   470    484         sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, iTabCur, aToOpen,
   471    485                                    &iDataCur, &iIdxCur);
   472    486         assert( pPk || IsVirtual(pTab) || iDataCur==iTabCur );
   473    487         assert( pPk || IsVirtual(pTab) || iIdxCur==iDataCur+1 );
          488  +      if( eOnePass==ONEPASS_MULTI ) sqlite3VdbeJumpHere(v, iAddrOnce);
   474    489       }
   475    490     
   476    491       /* Set up a loop over the rowids/primary-keys that were found in the
   477    492       ** where-clause loop above.
   478    493       */
   479         -    if( okOnePass ){
   480         -      /* Just one row.  Hence the top-of-loop is a no-op */
          494  +    if( eOnePass!=ONEPASS_OFF ){
   481    495         assert( nKey==nPk );  /* OP_Found will use an unpacked key */
   482         -      assert( !IsVirtual(pTab) );
   483    496         if( aToOpen[iDataCur-iTabCur] ){
   484    497           assert( pPk!=0 || pTab->pSelect!=0 );
   485    498           sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey);
   486    499           VdbeCoverage(v);
   487    500         }
   488    501       }else if( pPk ){
   489    502         addrLoop = sqlite3VdbeAddOp1(v, OP_Rewind, iEphCur); VdbeCoverage(v);
................................................................................
   503    516         sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iKey, pVTab, P4_VTAB);
   504    517         sqlite3VdbeChangeP5(v, OE_Abort);
   505    518         sqlite3MayAbort(pParse);
   506    519       }else
   507    520   #endif
   508    521       {
   509    522         int count = (pParse->nested==0);    /* True to count changes */
          523  +      int iIdxNoSeek = -1;
          524  +      if( bComplex==0 && aiCurOnePass[1]!=iDataCur ){
          525  +        iIdxNoSeek = aiCurOnePass[1];
          526  +      }
   510    527         sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
   511         -                               iKey, nKey, count, OE_Default, okOnePass);
          528  +          iKey, nKey, count, OE_Default, eOnePass, iIdxNoSeek);
   512    529       }
   513    530     
   514    531       /* End of the loop over all rowids/primary-keys. */
   515         -    if( okOnePass ){
          532  +    if( eOnePass!=ONEPASS_OFF ){
   516    533         sqlite3VdbeResolveLabel(v, addrBypass);
          534  +      sqlite3WhereEnd(pWInfo);
   517    535       }else if( pPk ){
   518    536         sqlite3VdbeAddOp2(v, OP_Next, iEphCur, addrLoop+1); VdbeCoverage(v);
   519    537         sqlite3VdbeJumpHere(v, addrLoop);
   520    538       }else{
   521    539         sqlite3VdbeGoto(v, addrLoop);
   522    540         sqlite3VdbeJumpHere(v, addrLoop);
   523    541       }     
................................................................................
   581    599   **   2.  Read/write cursors for all indices of pTab must be open as
   582    600   **       cursor number iIdxCur+i for the i-th index.
   583    601   **
   584    602   **   3.  The primary key for the row to be deleted must be stored in a
   585    603   **       sequence of nPk memory cells starting at iPk.  If nPk==0 that means
   586    604   **       that a search record formed from OP_MakeRecord is contained in the
   587    605   **       single memory location iPk.
          606  +**
          607  +** eMode:
          608  +**   Parameter eMode may be passed either ONEPASS_OFF (0), ONEPASS_SINGLE, or
          609  +**   ONEPASS_MULTI.  If eMode is not ONEPASS_OFF, then the cursor
          610  +**   iDataCur already points to the row to delete. If eMode is ONEPASS_OFF
          611  +**   then this function must seek iDataCur to the entry identified by iPk
          612  +**   and nPk before reading from it.
          613  +**
          614  +**   If eMode is ONEPASS_MULTI, then this call is being made as part
          615  +**   of a ONEPASS delete that affects multiple rows. In this case, if 
          616  +**   iIdxNoSeek is a valid cursor number (>=0), then its position should
          617  +**   be preserved following the delete operation. Or, if iIdxNoSeek is not
          618  +**   a valid cursor number, the position of iDataCur should be preserved
          619  +**   instead.
          620  +**
          621  +** iIdxNoSeek:
          622  +**   If iIdxNoSeek is a valid cursor number (>=0), then it identifies an
          623  +**   index cursor (from within array of cursors starting at iIdxCur) that
          624  +**   already points to the index entry to be deleted.
   588    625   */
   589    626   void sqlite3GenerateRowDelete(
   590    627     Parse *pParse,     /* Parsing context */
   591    628     Table *pTab,       /* Table containing the row to be deleted */
   592    629     Trigger *pTrigger, /* List of triggers to (potentially) fire */
   593    630     int iDataCur,      /* Cursor from which column data is extracted */
   594    631     int iIdxCur,       /* First index cursor */
   595    632     int iPk,           /* First memory cell containing the PRIMARY KEY */
   596    633     i16 nPk,           /* Number of PRIMARY KEY memory cells */
   597    634     u8 count,          /* If non-zero, increment the row change counter */
   598    635     u8 onconf,         /* Default ON CONFLICT policy for triggers */
   599         -  u8 bNoSeek         /* iDataCur is already pointing to the row to delete */
          636  +  u8 eMode,          /* ONEPASS_OFF, _SINGLE, or _MULTI.  See above */
          637  +  int iIdxNoSeek     /* Cursor number of cursor that does not need seeking */
   600    638   ){
   601    639     Vdbe *v = pParse->pVdbe;        /* Vdbe */
   602    640     int iOld = 0;                   /* First register in OLD.* array */
   603    641     int iLabel;                     /* Label resolved to end of generated code */
   604    642     u8 opSeek;                      /* Seek opcode */
   605    643   
   606    644     /* Vdbe is guaranteed to have been allocated by this stage. */
................................................................................
   609    647                            iDataCur, iIdxCur, iPk, (int)nPk));
   610    648   
   611    649     /* Seek cursor iCur to the row to delete. If this row no longer exists 
   612    650     ** (this can happen if a trigger program has already deleted it), do
   613    651     ** not attempt to delete it or fire any DELETE triggers.  */
   614    652     iLabel = sqlite3VdbeMakeLabel(v);
   615    653     opSeek = HasRowid(pTab) ? OP_NotExists : OP_NotFound;
   616         -  if( !bNoSeek ){
          654  +  if( eMode==ONEPASS_OFF ){
   617    655       sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk);
   618    656       VdbeCoverageIf(v, opSeek==OP_NotExists);
   619    657       VdbeCoverageIf(v, opSeek==OP_NotFound);
   620    658     }
   621    659    
   622    660     /* If there are any triggers to fire, allocate a range of registers to
   623    661     ** use for the old.* references in the triggers.  */
................................................................................
   669    707       sqlite3FkCheck(pParse, pTab, iOld, 0, 0, 0);
   670    708     }
   671    709   
   672    710     /* Delete the index and table entries. Skip this step if pTab is really
   673    711     ** a view (in which case the only effect of the DELETE statement is to
   674    712     ** fire the INSTEAD OF triggers).  */ 
   675    713     if( pTab->pSelect==0 ){
   676         -    sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, 0);
          714  +    sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,iIdxNoSeek);
   677    715       sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, (count?OPFLAG_NCHANGE:0));
   678    716       if( count ){
   679    717         sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT);
   680    718       }
          719  +    if( iIdxNoSeek>=0 ){
          720  +      sqlite3VdbeAddOp1(v, OP_Delete, iIdxNoSeek);
          721  +    }
          722  +    sqlite3VdbeChangeP5(v, eMode==ONEPASS_MULTI);
   681    723     }
   682    724   
   683    725     /* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to
   684    726     ** handle rows (possibly in other tables) that refer via a foreign key
   685    727     ** to the row just deleted. */ 
   686    728     sqlite3FkActions(pParse, pTab, 0, iOld, 0, 0);
   687    729   
................................................................................
   716    758   **       that is to be deleted.
   717    759   */
   718    760   void sqlite3GenerateRowIndexDelete(
   719    761     Parse *pParse,     /* Parsing and code generating context */
   720    762     Table *pTab,       /* Table containing the row to be deleted */
   721    763     int iDataCur,      /* Cursor of table holding data. */
   722    764     int iIdxCur,       /* First index cursor */
   723         -  int *aRegIdx       /* Only delete if aRegIdx!=0 && aRegIdx[i]>0 */
          765  +  int *aRegIdx,      /* Only delete if aRegIdx!=0 && aRegIdx[i]>0 */
          766  +  int iIdxNoSeek     /* Do not delete from this cursor */
   724    767   ){
   725    768     int i;             /* Index loop counter */
   726    769     int r1 = -1;       /* Register holding an index key */
   727    770     int iPartIdxLabel; /* Jump destination for skipping partial index entries */
   728    771     Index *pIdx;       /* Current index */
   729    772     Index *pPrior = 0; /* Prior index */
   730    773     Vdbe *v;           /* The prepared statement under construction */
................................................................................
   732    775   
   733    776     v = pParse->pVdbe;
   734    777     pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
   735    778     for(i=0, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
   736    779       assert( iIdxCur+i!=iDataCur || pPk==pIdx );
   737    780       if( aRegIdx!=0 && aRegIdx[i]==0 ) continue;
   738    781       if( pIdx==pPk ) continue;
          782  +    if( iIdxCur+i==iIdxNoSeek ) continue;
   739    783       VdbeModuleComment((v, "GenRowIdxDel for %s", pIdx->zName));
   740    784       r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1,
   741         -                                 &iPartIdxLabel, pPrior, r1);
          785  +        &iPartIdxLabel, pPrior, r1);
   742    786       sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1,
   743         -                      pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn);
          787  +        pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn);
   744    788       sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel);
   745    789       pPrior = pIdx;
   746    790     }
   747    791   }
   748    792   
   749    793   /*
   750    794   ** Generate code that will assemble an index key and stores it in register
................................................................................
   785    829     int prefixOnly,      /* Compute only a unique prefix of the key */
   786    830     int *piPartIdxLabel, /* OUT: Jump to this label to skip partial index */
   787    831     Index *pPrior,       /* Previously generated index key */
   788    832     int regPrior         /* Register holding previous generated key */
   789    833   ){
   790    834     Vdbe *v = pParse->pVdbe;
   791    835     int j;
   792         -  Table *pTab = pIdx->pTable;
   793    836     int regBase;
   794    837     int nCol;
   795    838   
   796    839     if( piPartIdxLabel ){
   797    840       if( pIdx->pPartIdxWhere ){
   798    841         *piPartIdxLabel = sqlite3VdbeMakeLabel(v);
   799         -      pParse->iPartIdxTab = iDataCur;
          842  +      pParse->iSelfTab = iDataCur;
   800    843         sqlite3ExprCachePush(pParse);
   801    844         sqlite3ExprIfFalseDup(pParse, pIdx->pPartIdxWhere, *piPartIdxLabel, 
   802    845                               SQLITE_JUMPIFNULL);
   803    846       }else{
   804    847         *piPartIdxLabel = 0;
   805    848       }
   806    849     }
   807    850     nCol = (prefixOnly && pIdx->uniqNotNull) ? pIdx->nKeyCol : pIdx->nColumn;
   808    851     regBase = sqlite3GetTempRange(pParse, nCol);
   809    852     if( pPrior && (regBase!=regPrior || pPrior->pPartIdxWhere) ) pPrior = 0;
   810    853     for(j=0; j<nCol; j++){
   811         -    if( pPrior && pPrior->aiColumn[j]==pIdx->aiColumn[j] ) continue;
   812         -    sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, pIdx->aiColumn[j],
   813         -                                    regBase+j);
          854  +    if( pPrior
          855  +     && pPrior->aiColumn[j]==pIdx->aiColumn[j]
          856  +     && pPrior->aiColumn[j]>=(-1)
          857  +    ){
          858  +      /* This column was already computed by the previous index */
          859  +      continue;
          860  +    }
          861  +    sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iDataCur, j, regBase+j);
   814    862       /* If the column affinity is REAL but the number is an integer, then it
   815    863       ** might be stored in the table as an integer (using a compact
   816    864       ** representation) then converted to REAL by an OP_RealAffinity opcode.
   817    865       ** But we are getting ready to store this value back into an index, where
   818    866       ** it should be converted by to INTEGER again.  So omit the OP_RealAffinity
   819    867       ** opcode if it is present */
   820    868       sqlite3VdbeDeletePriorOpcode(v, OP_RealAffinity);

Changes to src/expr.c.

    87     87     assert( zC!=0 );
    88     88     s.z = zC;
    89     89     s.n = sqlite3Strlen30(s.z);
    90     90     return sqlite3ExprAddCollateToken(pParse, pExpr, &s, 0);
    91     91   }
    92     92   
    93     93   /*
    94         -** Skip over any TK_COLLATE or TK_AS operators and any unlikely()
           94  +** Skip over any TK_COLLATE operators and any unlikely()
    95     95   ** or likelihood() function at the root of an expression.
    96     96   */
    97     97   Expr *sqlite3ExprSkipCollate(Expr *pExpr){
    98     98     while( pExpr && ExprHasProperty(pExpr, EP_Skip) ){
    99     99       if( ExprHasProperty(pExpr, EP_Unlikely) ){
   100    100         assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
   101    101         assert( pExpr->x.pList->nExpr>0 );
   102    102         assert( pExpr->op==TK_FUNCTION );
   103    103         pExpr = pExpr->x.pList->a[0].pExpr;
   104    104       }else{
   105         -      assert( pExpr->op==TK_COLLATE || pExpr->op==TK_AS );
          105  +      assert( pExpr->op==TK_COLLATE );
   106    106         pExpr = pExpr->pLeft;
   107    107       }
   108    108     }   
   109    109     return pExpr;
   110    110   }
   111    111   
   112    112   /*
................................................................................
  2427   2427     struct yColCache *p;
  2428   2428     for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
  2429   2429       if( p->iReg==iReg ){
  2430   2430         p->tempReg = 0;
  2431   2431       }
  2432   2432     }
  2433   2433   }
         2434  +
         2435  +/* Generate code that will load into register regOut a value that is
         2436  +** appropriate for the iIdxCol-th column of index pIdx.
         2437  +*/
         2438  +void sqlite3ExprCodeLoadIndexColumn(
         2439  +  Parse *pParse,  /* The parsing context */
         2440  +  Index *pIdx,    /* The index whose column is to be loaded */
         2441  +  int iTabCur,    /* Cursor pointing to a table row */
         2442  +  int iIdxCol,    /* The column of the index to be loaded */
         2443  +  int regOut      /* Store the index column value in this register */
         2444  +){
         2445  +  i16 iTabCol = pIdx->aiColumn[iIdxCol];
         2446  +  if( iTabCol>=(-1) ){
         2447  +    sqlite3ExprCodeGetColumnOfTable(pParse->pVdbe, pIdx->pTable, iTabCur,
         2448  +                                    iTabCol, regOut);
         2449  +    return;
         2450  +  }
         2451  +  assert( pIdx->aColExpr );
         2452  +  assert( pIdx->aColExpr->nExpr>iIdxCol );
         2453  +  pParse->iSelfTab = iTabCur;
         2454  +  sqlite3ExprCode(pParse, pIdx->aColExpr->a[iIdxCol].pExpr, regOut);
         2455  +}
  2434   2456   
  2435   2457   /*
  2436   2458   ** Generate code to extract the value of the iCol-th column of a table.
  2437   2459   */
  2438   2460   void sqlite3ExprCodeGetColumnOfTable(
  2439   2461     Vdbe *v,        /* The VDBE under construction */
  2440   2462     Table *pTab,    /* The table containing the value */
................................................................................
  2613   2635         int iTab = pExpr->iTable;
  2614   2636         if( iTab<0 ){
  2615   2637           if( pParse->ckBase>0 ){
  2616   2638             /* Generating CHECK constraints or inserting into partial index */
  2617   2639             inReg = pExpr->iColumn + pParse->ckBase;
  2618   2640             break;
  2619   2641           }else{
  2620         -          /* Deleting from a partial index */
  2621         -          iTab = pParse->iPartIdxTab;
         2642  +          /* Coding an expression that is part of an index where column names
         2643  +          ** in the index refer to the table to which the index belongs */
         2644  +          iTab = pParse->iSelfTab;
  2622   2645           }
  2623   2646         }
  2624   2647         inReg = sqlite3ExprCodeGetColumn(pParse, pExpr->pTab,
  2625   2648                                  pExpr->iColumn, iTab, target,
  2626   2649                                  pExpr->op2);
  2627   2650         break;
  2628   2651       }
................................................................................
  2674   2697         }
  2675   2698         break;
  2676   2699       }
  2677   2700       case TK_REGISTER: {
  2678   2701         inReg = pExpr->iTable;
  2679   2702         break;
  2680   2703       }
  2681         -    case TK_AS: {
  2682         -      inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
  2683         -      break;
  2684         -    }
  2685   2704   #ifndef SQLITE_OMIT_CAST
  2686   2705       case TK_CAST: {
  2687   2706         /* Expressions of the form:   CAST(pLeft AS token) */
  2688   2707         inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
  2689   2708         if( inReg!=target ){
  2690   2709           sqlite3VdbeAddOp2(v, OP_SCopy, inReg, target);
  2691   2710           inReg = target;
................................................................................
  3761   3780       }
  3762   3781       if( pB->op==TK_COLLATE && sqlite3ExprCompare(pA, pB->pLeft, iTab)<2 ){
  3763   3782         return 1;
  3764   3783       }
  3765   3784       return 2;
  3766   3785     }
  3767   3786     if( pA->op!=TK_COLUMN && ALWAYS(pA->op!=TK_AGG_COLUMN) && pA->u.zToken ){
  3768         -    if( strcmp(pA->u.zToken,pB->u.zToken)!=0 ){
         3787  +    if( pA->op==TK_FUNCTION ){
         3788  +      if( sqlite3StrICmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2;
         3789  +    }else if( strcmp(pA->u.zToken,pB->u.zToken)!=0 ){
  3769   3790         return pA->op==TK_COLLATE ? 1 : 2;
  3770   3791       }
  3771   3792     }
  3772   3793     if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 2;
  3773   3794     if( ALWAYS((combinedFlags & EP_TokenOnly)==0) ){
  3774   3795       if( combinedFlags & EP_xIsSelect ) return 2;
  3775   3796       if( sqlite3ExprCompare(pA->pLeft, pB->pLeft, iTab) ) return 2;

Changes to src/func.c.

  1733   1733       FUNCTION2(ifnull,            2, 0, 0, noopFunc,  SQLITE_FUNC_COALESCE),
  1734   1734       FUNCTION2(unlikely,          1, 0, 0, noopFunc,  SQLITE_FUNC_UNLIKELY),
  1735   1735       FUNCTION2(likelihood,        2, 0, 0, noopFunc,  SQLITE_FUNC_UNLIKELY),
  1736   1736       FUNCTION2(likely,            1, 0, 0, noopFunc,  SQLITE_FUNC_UNLIKELY),
  1737   1737       VFUNCTION(random,            0, 0, 0, randomFunc       ),
  1738   1738       VFUNCTION(randomblob,        1, 0, 0, randomBlob       ),
  1739   1739       FUNCTION(nullif,             2, 0, 1, nullifFunc       ),
  1740         -    FUNCTION(sqlite_version,     0, 0, 0, versionFunc      ),
  1741         -    FUNCTION(sqlite_source_id,   0, 0, 0, sourceidFunc     ),
         1740  +    DFUNCTION(sqlite_version,    0, 0, 0, versionFunc      ),
         1741  +    DFUNCTION(sqlite_source_id,  0, 0, 0, sourceidFunc     ),
  1742   1742       FUNCTION(sqlite_log,         2, 0, 0, errlogFunc       ),
  1743   1743   #if SQLITE_USER_AUTHENTICATION
  1744   1744       FUNCTION(sqlite_crypt,       2, 0, 0, sqlite3CryptFunc ),
  1745   1745   #endif
  1746   1746   #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
  1747         -    FUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc  ),
  1748         -    FUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc  ),
         1747  +    DFUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc  ),
         1748  +    DFUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc  ),
  1749   1749   #endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */
  1750   1750       FUNCTION(quote,              1, 0, 0, quoteFunc        ),
  1751   1751       VFUNCTION(last_insert_rowid, 0, 0, 0, last_insert_rowid),
  1752   1752       VFUNCTION(changes,           0, 0, 0, changes          ),
  1753   1753       VFUNCTION(total_changes,     0, 0, 0, total_changes    ),
  1754   1754       FUNCTION(replace,            3, 0, 0, replaceFunc      ),
  1755   1755       FUNCTION(zeroblob,           1, 0, 0, zeroblobFunc     ),
  1756   1756     #ifdef SQLITE_SOUNDEX
  1757   1757       FUNCTION(soundex,            1, 0, 0, soundexFunc      ),
  1758   1758     #endif
  1759   1759     #ifndef SQLITE_OMIT_LOAD_EXTENSION
  1760         -    FUNCTION(load_extension,     1, 0, 0, loadExt          ),
  1761         -    FUNCTION(load_extension,     2, 0, 0, loadExt          ),
         1760  +    VFUNCTION(load_extension,    1, 0, 0, loadExt          ),
         1761  +    VFUNCTION(load_extension,    2, 0, 0, loadExt          ),
  1762   1762     #endif
  1763   1763       AGGREGATE(sum,               1, 0, 0, sumStep,         sumFinalize    ),
  1764   1764       AGGREGATE(total,             1, 0, 0, sumStep,         totalFinalize    ),
  1765   1765       AGGREGATE(avg,               1, 0, 0, sumStep,         avgFinalize    ),
  1766   1766       AGGREGATE2(count,            0, 0, 0, countStep,       countFinalize,
  1767   1767                  SQLITE_FUNC_COUNT  ),
  1768   1768       AGGREGATE(count,             1, 0, 0, countStep,       countFinalize  ),

Changes to src/insert.c.

    84     84       pIdx->zColAff = (char *)sqlite3DbMallocRaw(0, pIdx->nColumn+1);
    85     85       if( !pIdx->zColAff ){
    86     86         db->mallocFailed = 1;
    87     87         return 0;
    88     88       }
    89     89       for(n=0; n<pIdx->nColumn; n++){
    90     90         i16 x = pIdx->aiColumn[n];
    91         -      pIdx->zColAff[n] = x<0 ? SQLITE_AFF_INTEGER : pTab->aCol[x].affinity;
           91  +      if( x>=0 ){
           92  +        pIdx->zColAff[n] = pTab->aCol[x].affinity;
           93  +      }else if( x==(-1) ){
           94  +        pIdx->zColAff[n] = SQLITE_AFF_INTEGER;
           95  +      }else{
           96  +        char aff;
           97  +        assert( x==(-2) );
           98  +        assert( pIdx->aColExpr!=0 );
           99  +        aff = sqlite3ExprAffinity(pIdx->aColExpr->a[n].pExpr);
          100  +        if( aff==0 ) aff = SQLITE_AFF_BLOB;
          101  +        pIdx->zColAff[n] = aff;
          102  +      }
    92    103       }
    93    104       pIdx->zColAff[n] = 0;
    94    105     }
    95    106    
    96    107     return pIdx->zColAff;
    97    108   }
    98    109   
................................................................................
  1332   1343           Trigger *pTrigger = 0;
  1333   1344           if( db->flags&SQLITE_RecTriggers ){
  1334   1345             pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
  1335   1346           }
  1336   1347           if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){
  1337   1348             sqlite3MultiWrite(pParse);
  1338   1349             sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
  1339         -                                   regNewData, 1, 0, OE_Replace, 1);
  1340         -        }else if( pTab->pIndex ){
  1341         -          sqlite3MultiWrite(pParse);
  1342         -          sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, 0);
         1350  +                                   regNewData, 1, 0, OE_Replace,
         1351  +                                   ONEPASS_SINGLE, -1);
         1352  +        }else{
         1353  +          if( pTab->pIndex ){
         1354  +            sqlite3MultiWrite(pParse);
         1355  +            sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,-1);
         1356  +          }
  1343   1357           }
  1344   1358           seenReplace = 1;
  1345   1359           break;
  1346   1360         }
  1347   1361         case OE_Ignore: {
  1348   1362           /*assert( seenReplace==0 );*/
  1349   1363           sqlite3VdbeGoto(v, ignoreDest);
................................................................................
  1390   1404       /* Create a record for this index entry as it should appear after
  1391   1405       ** the insert or update.  Store that record in the aRegIdx[ix] register
  1392   1406       */
  1393   1407       regIdx = sqlite3GetTempRange(pParse, pIdx->nColumn);
  1394   1408       for(i=0; i<pIdx->nColumn; i++){
  1395   1409         int iField = pIdx->aiColumn[i];
  1396   1410         int x;
  1397         -      if( iField<0 || iField==pTab->iPKey ){
  1398         -        if( regRowid==regIdx+i ) continue; /* ROWID already in regIdx+i */
  1399         -        x = regNewData;
  1400         -        regRowid =  pIdx->pPartIdxWhere ? -1 : regIdx+i;
         1411  +      if( iField==(-2) ){
         1412  +        pParse->ckBase = regNewData+1;
         1413  +        sqlite3ExprCode(pParse, pIdx->aColExpr->a[i].pExpr, regIdx+i);
         1414  +        pParse->ckBase = 0;
         1415  +        VdbeComment((v, "%s column %d", pIdx->zName, i));
  1401   1416         }else{
  1402         -        x = iField + regNewData + 1;
         1417  +        if( iField==(-1) || iField==pTab->iPKey ){
         1418  +          if( regRowid==regIdx+i ) continue; /* ROWID already in regIdx+i */
         1419  +          x = regNewData;
         1420  +          regRowid =  pIdx->pPartIdxWhere ? -1 : regIdx+i;
         1421  +        }else{
         1422  +          x = iField + regNewData + 1;
         1423  +        }
         1424  +        sqlite3VdbeAddOp2(v, OP_SCopy, x, regIdx+i);
         1425  +        VdbeComment((v, "%s", iField<0 ? "rowid" : pTab->aCol[iField].zName));
  1403   1426         }
  1404         -      sqlite3VdbeAddOp2(v, OP_SCopy, x, regIdx+i);
  1405         -      VdbeComment((v, "%s", iField<0 ? "rowid" : pTab->aCol[iField].zName));
  1406   1427       }
  1407   1428       sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn, aRegIdx[ix]);
  1408   1429       VdbeComment((v, "for %s", pIdx->zName));
  1409   1430       sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn);
  1410   1431   
  1411   1432       /* In an UPDATE operation, if this index is the PRIMARY KEY index 
  1412   1433       ** of a WITHOUT ROWID table and there has been no change the
................................................................................
  1506   1527           Trigger *pTrigger = 0;
  1507   1528           assert( onError==OE_Replace );
  1508   1529           sqlite3MultiWrite(pParse);
  1509   1530           if( db->flags&SQLITE_RecTriggers ){
  1510   1531             pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
  1511   1532           }
  1512   1533           sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
  1513         -                                 regR, nPkField, 0, OE_Replace, pIdx==pPk);
         1534  +            regR, nPkField, 0, OE_Replace,
         1535  +            (pIdx==pPk ? ONEPASS_SINGLE : ONEPASS_OFF), -1);
  1514   1536           seenReplace = 1;
  1515   1537           break;
  1516   1538         }
  1517   1539       }
  1518   1540       sqlite3VdbeResolveLabel(v, addrUniqueOk);
  1519   1541       sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn);
  1520   1542       if( regR!=regIdx ) sqlite3ReleaseTempRange(pParse, regR, nPkField);
................................................................................
  1718   1740     }
  1719   1741     if( pDest->onError!=pSrc->onError ){
  1720   1742       return 0;   /* Different conflict resolution strategies */
  1721   1743     }
  1722   1744     for(i=0; i<pSrc->nKeyCol; i++){
  1723   1745       if( pSrc->aiColumn[i]!=pDest->aiColumn[i] ){
  1724   1746         return 0;   /* Different columns indexed */
         1747  +    }
         1748  +    if( pSrc->aiColumn[i]==(-2) ){
         1749  +      assert( pSrc->aColExpr!=0 && pDest->aColExpr!=0 );
         1750  +      if( sqlite3ExprCompare(pSrc->aColExpr->a[i].pExpr,
         1751  +                             pDest->aColExpr->a[i].pExpr, -1)!=0 ){
         1752  +        return 0;   /* Different expressions in the index */
         1753  +      }
  1725   1754       }
  1726   1755       if( pSrc->aSortOrder[i]!=pDest->aSortOrder[i] ){
  1727   1756         return 0;   /* Different sort orders */
  1728   1757       }
  1729   1758       if( !xferCompatibleCollation(pSrc->azColl[i],pDest->azColl[i]) ){
  1730   1759         return 0;   /* Different collating sequences */
  1731   1760       }

Changes to src/lempar.c.

    52     52   **                       for base tokens is called "yy0".
    53     53   **    YYSTACKDEPTH       is the maximum depth of the parser's stack.  If
    54     54   **                       zero the stack is dynamically sized using realloc()
    55     55   **    ParseARG_SDECL     A static variable declaration for the %extra_argument
    56     56   **    ParseARG_PDECL     A parameter declaration for the %extra_argument
    57     57   **    ParseARG_STORE     Code to store %extra_argument into yypParser
    58     58   **    ParseARG_FETCH     Code to extract %extra_argument from yypParser
    59         -**    YYNSTATE           the combined number of states.
    60         -**    YYNRULE            the number of rules in the grammar
    61     59   **    YYERRORSYMBOL      is the code number of the error symbol.  If not
    62     60   **                       defined, then do no error processing.
           61  +**    YYNSTATE           the combined number of states.
           62  +**    YYNRULE            the number of rules in the grammar
           63  +**    YY_MAX_SHIFT       Maximum value for shift actions
           64  +**    YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions
           65  +**    YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions
           66  +**    YY_MIN_REDUCE      Maximum value for reduce actions
           67  +**    YY_ERROR_ACTION    The yy_action[] code for syntax error
           68  +**    YY_ACCEPT_ACTION   The yy_action[] code for accept
           69  +**    YY_NO_ACTION       The yy_action[] code for no-op
    63     70   */
    64     71   %%
    65         -#define YY_NO_ACTION      (YYNSTATE+YYNRULE+2)
    66         -#define YY_ACCEPT_ACTION  (YYNSTATE+YYNRULE+1)
    67         -#define YY_ERROR_ACTION   (YYNSTATE+YYNRULE)
    68     72   
    69     73   /* The yyzerominor constant is used to initialize instances of
    70     74   ** YYMINORTYPE objects to zero. */
    71     75   static const YYMINORTYPE yyzerominor = { 0 };
    72     76   
    73     77   /* Define the yytestcase() macro to be a no-op if is not already defined
    74     78   ** otherwise.
................................................................................
    87     91   ** current state and lookahead token.  These tables are used to implement
    88     92   ** functions that take a state number and lookahead value and return an
    89     93   ** action integer.  
    90     94   **
    91     95   ** Suppose the action integer is N.  Then the action is determined as
    92     96   ** follows
    93     97   **
    94         -**   0 <= N < YYNSTATE                  Shift N.  That is, push the lookahead
           98  +**   0 <= N <= YY_MAX_SHIFT             Shift N.  That is, push the lookahead
    95     99   **                                      token onto the stack and goto state N.
    96    100   **
    97         -**   YYNSTATE <= N < YYNSTATE+YYNRULE   Reduce by rule N-YYNSTATE.
          101  +**   N between YY_MIN_SHIFTREDUCE       Shift to an arbitrary state then
          102  +**     and YY_MAX_SHIFTREDUCE           reduce by rule N-YY_MIN_SHIFTREDUCE.
    98    103   **
    99         -**   N == YYNSTATE+YYNRULE              A syntax error has occurred.
          104  +**   N between YY_MIN_REDUCE            Reduce by rule N-YY_MIN_REDUCE
          105  +**     and YY_MAX_REDUCE
          106  +
          107  +**   N == YY_ERROR_ACTION               A syntax error has occurred.
   100    108   **
   101         -**   N == YYNSTATE+YYNRULE+1            The parser accepts its input.
          109  +**   N == YY_ACCEPT_ACTION              The parser accepts its input.
   102    110   **
   103         -**   N == YYNSTATE+YYNRULE+2            No such action.  Denotes unused
          111  +**   N == YY_NO_ACTION                  No such action.  Denotes unused
   104    112   **                                      slots in the yy_action[] table.
   105    113   **
   106    114   ** The action table is constructed as a single large table named yy_action[].
   107    115   ** Given state S and lookahead X, the action is computed as
   108    116   **
   109    117   **      yy_action[ yy_shift_ofst[S] + X ]
   110    118   **
................................................................................
   155    163   **
   156    164   **   +  The value of the token stored at this level of the stack.
   157    165   **      (In other words, the "major" token.)
   158    166   **
   159    167   **   +  The semantic value stored at this level of the stack.  This is
   160    168   **      the information used by the action routines in the grammar.
   161    169   **      It is sometimes called the "minor" token.
          170  +**
          171  +** After the "shift" half of a SHIFTREDUCE action, the stateno field
          172  +** actually contains the reduce action for the second half of the
          173  +** SHIFTREDUCE.
   162    174   */
   163    175   struct yyStackEntry {
   164         -  YYACTIONTYPE stateno;  /* The state-number */
          176  +  YYACTIONTYPE stateno;  /* The state-number, or reduce action in SHIFTREDUCE */
   165    177     YYCODETYPE major;      /* The major token value.  This is the code
   166    178                            ** number for the token at this stack level */
   167    179     YYMINORTYPE minor;     /* The user-supplied minor token value.  This
   168    180                            ** is the value of the token  */
   169    181   };
   170    182   typedef struct yyStackEntry yyStackEntry;
   171    183   
................................................................................
   391    403   static int yy_find_shift_action(
   392    404     yyParser *pParser,        /* The parser */
   393    405     YYCODETYPE iLookAhead     /* The look-ahead token */
   394    406   ){
   395    407     int i;
   396    408     int stateno = pParser->yystack[pParser->yyidx].stateno;
   397    409    
   398         -  if( stateno>YY_SHIFT_COUNT
   399         -   || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){
   400         -    return yy_default[stateno];
   401         -  }
          410  +  if( stateno>=YY_MIN_REDUCE ) return stateno;
          411  +  assert( stateno <= YY_SHIFT_COUNT );
          412  +  i = yy_shift_ofst[stateno];
          413  +  if( i==YY_SHIFT_USE_DFLT ) return yy_default[stateno];
   402    414     assert( iLookAhead!=YYNOCODE );
   403    415     i += iLookAhead;
   404    416     if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){
   405    417       if( iLookAhead>0 ){
   406    418   #ifdef YYFALLBACK
   407    419         YYCODETYPE iFallback;            /* Fallback token */
   408    420         if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0])
................................................................................
   495    507      /* Here code is inserted which will execute if the parser
   496    508      ** stack every overflows */
   497    509   %%
   498    510      ParseARG_STORE; /* Suppress warning about unused %extra_argument var */
   499    511   }
   500    512   
   501    513   /*
   502         -** Perform a shift action.
          514  +** Print tracing information for a SHIFT action
          515  +*/
          516  +#ifndef NDEBUG
          517  +static void yyTraceShift(yyParser *yypParser, int yyNewState){
          518  +  if( yyTraceFILE ){
          519  +    int i;
          520  +    if( yyNewState<YYNSTATE ){
          521  +      fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState);
          522  +      fprintf(yyTraceFILE,"%sStack:",yyTracePrompt);
          523  +      for(i=1; i<=yypParser->yyidx; i++)
          524  +        fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]);
          525  +      fprintf(yyTraceFILE,"\n");
          526  +    }else{
          527  +      fprintf(yyTraceFILE,"%sShift *\n",yyTracePrompt);
          528  +    }
          529  +  }
          530  +}
          531  +#else
          532  +# define yyTraceShift(X,Y)
          533  +#endif
          534  +
          535  +/*
          536  +** Perform a shift action.  Return the number of errors.
   503    537   */
   504    538   static void yy_shift(
   505    539     yyParser *yypParser,          /* The parser to be shifted */
   506    540     int yyNewState,               /* The new state to shift in */
   507    541     int yyMajor,                  /* The major token to shift in */
   508    542     YYMINORTYPE *yypMinor         /* Pointer to the minor token to shift in */
   509    543   ){
................................................................................
   528    562       }
   529    563     }
   530    564   #endif
   531    565     yytos = &yypParser->yystack[yypParser->yyidx];
   532    566     yytos->stateno = (YYACTIONTYPE)yyNewState;
   533    567     yytos->major = (YYCODETYPE)yyMajor;
   534    568     yytos->minor = *yypMinor;
   535         -#ifndef NDEBUG
   536         -  if( yyTraceFILE && yypParser->yyidx>0 ){
   537         -    int i;
   538         -    fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState);
   539         -    fprintf(yyTraceFILE,"%sStack:",yyTracePrompt);
   540         -    for(i=1; i<=yypParser->yyidx; i++)
   541         -      fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]);
   542         -    fprintf(yyTraceFILE,"\n");
   543         -  }
   544         -#endif
          569  +  yyTraceShift(yypParser, yyNewState);
   545    570   }
   546    571   
   547    572   /* The following table contains information about every rule that
   548    573   ** is used during the reduce.
   549    574   */
   550    575   static const struct {
   551    576     YYCODETYPE lhs;         /* Symbol on the left-hand side of the rule */
................................................................................
   570    595     yyStackEntry *yymsp;            /* The top of the parser's stack */
   571    596     int yysize;                     /* Amount to pop the stack */
   572    597     ParseARG_FETCH;
   573    598     yymsp = &yypParser->yystack[yypParser->yyidx];
   574    599   #ifndef NDEBUG
   575    600     if( yyTraceFILE && yyruleno>=0 
   576    601           && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){
   577         -    fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt,
   578         -      yyRuleName[yyruleno]);
          602  +    yysize = yyRuleInfo[yyruleno].nrhs;
          603  +    fprintf(yyTraceFILE, "%sReduce [%s] -> state %d.\n", yyTracePrompt,
          604  +      yyRuleName[yyruleno], yymsp[-yysize].stateno);
   579    605     }
   580    606   #endif /* NDEBUG */
   581    607   
   582    608     /* Silence complaints from purify about yygotominor being uninitialized
   583    609     ** in some cases when it is copied into the stack after the following
   584    610     ** switch.  yygotominor is uninitialized when a rule reduces that does
   585    611     ** not set the value of its left-hand side nonterminal.  Leaving the
................................................................................
   609    635   %%
   610    636     };
   611    637     assert( yyruleno>=0 && yyruleno<sizeof(yyRuleInfo)/sizeof(yyRuleInfo[0]) );
   612    638     yygoto = yyRuleInfo[yyruleno].lhs;
   613    639     yysize = yyRuleInfo[yyruleno].nrhs;
   614    640     yypParser->yyidx -= yysize;
   615    641     yyact = yy_find_reduce_action(yymsp[-yysize].stateno,(YYCODETYPE)yygoto);
   616         -  if( yyact < YYNSTATE ){
   617         -#ifdef NDEBUG
   618         -    /* If we are not debugging and the reduce action popped at least
          642  +  if( yyact <= YY_MAX_SHIFTREDUCE ){
          643  +    if( yyact>YY_MAX_SHIFT ) yyact += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
          644  +    /* If the reduce action popped at least
   619    645       ** one element off the stack, then we can push the new element back
   620    646       ** onto the stack here, and skip the stack overflow test in yy_shift().
   621    647       ** That gives a significant speed improvement. */
   622    648       if( yysize ){
   623    649         yypParser->yyidx++;
   624    650         yymsp -= yysize-1;
   625    651         yymsp->stateno = (YYACTIONTYPE)yyact;
   626    652         yymsp->major = (YYCODETYPE)yygoto;
   627    653         yymsp->minor = yygotominor;
   628         -    }else
   629         -#endif
   630         -    {
          654  +      yyTraceShift(yypParser, yyact);
          655  +    }else{
   631    656         yy_shift(yypParser,yyact,yygoto,&yygotominor);
   632    657       }
   633    658     }else{
   634         -    assert( yyact == YYNSTATE + YYNRULE + 1 );
          659  +    assert( yyact == YY_ACCEPT_ACTION );
   635    660       yy_accept(yypParser);
   636    661     }
   637    662   }
   638    663   
   639    664   /*
   640    665   ** The following code executes when the parse fails
   641    666   */
................................................................................
   751    776     if( yyTraceFILE ){
   752    777       fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]);
   753    778     }
   754    779   #endif
   755    780   
   756    781     do{
   757    782       yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor);
   758         -    if( yyact<YYNSTATE ){
          783  +    if( yyact <= YY_MAX_SHIFTREDUCE ){
          784  +      if( yyact > YY_MAX_SHIFT ) yyact += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
   759    785         yy_shift(yypParser,yyact,yymajor,&yyminorunion);
   760    786         yypParser->yyerrcnt--;
   761    787         yymajor = YYNOCODE;
   762         -    }else if( yyact < YYNSTATE + YYNRULE ){
   763         -      yy_reduce(yypParser,yyact-YYNSTATE);
          788  +    }else if( yyact <= YY_MAX_REDUCE ){
          789  +      yy_reduce(yypParser,yyact-YY_MIN_REDUCE);
   764    790       }else{
   765    791         assert( yyact == YY_ERROR_ACTION );
   766    792   #ifdef YYERRORSYMBOL
   767    793         int yymx;
   768    794   #endif
   769    795   #ifndef NDEBUG
   770    796         if( yyTraceFILE ){
................................................................................
   806    832           yymajor = YYNOCODE;
   807    833         }else{
   808    834            while(
   809    835             yypParser->yyidx >= 0 &&
   810    836             yymx != YYERRORSYMBOL &&
   811    837             (yyact = yy_find_reduce_action(
   812    838                           yypParser->yystack[yypParser->yyidx].stateno,
   813         -                        YYERRORSYMBOL)) >= YYNSTATE
          839  +                        YYERRORSYMBOL)) >= YY_MIN_REDUCE
   814    840           ){
   815    841             yy_pop_parser_stack(yypParser);
   816    842           }
   817    843           if( yypParser->yyidx < 0 || yymajor==0 ){
   818    844             yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
   819    845             yy_parse_failed(yypParser);
   820    846             yymajor = YYNOCODE;
................................................................................
   856    882         if( yyendofinput ){
   857    883           yy_parse_failed(yypParser);
   858    884         }
   859    885         yymajor = YYNOCODE;
   860    886   #endif
   861    887       }
   862    888     }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 );
          889  +#ifndef NDEBUG
          890  +  if( yyTraceFILE ){
          891  +    fprintf(yyTraceFILE,"%sReturn\n",yyTracePrompt);
          892  +  }
          893  +#endif
   863    894     return;
   864    895   }

Changes to src/loadext.c.

   403    403     sqlite3_result_blob64,
   404    404     sqlite3_result_text64,
   405    405     sqlite3_strglob,
   406    406     /* Version 3.8.11 and later */
   407    407     (sqlite3_value*(*)(const sqlite3_value*))sqlite3_value_dup,
   408    408     sqlite3_value_free,
   409    409     sqlite3_result_zeroblob64,
   410         -  sqlite3_bind_zeroblob64
          410  +  sqlite3_bind_zeroblob64,
          411  +  /* Version 3.8.12 and later */
          412  +  sqlite3_value_subtype,
          413  +  sqlite3_result_subtype
   411    414   };
   412    415   
   413    416   /*
   414    417   ** Attempt to load an SQLite extension library contained in the file
   415    418   ** zFile.  The entry point is zProc.  zProc may be 0 in which case a
   416    419   ** default entry point name (sqlite3_extension_init) is used.  Use
   417    420   ** of the default name is recommended.

Changes to src/malloc.c.

    41     41   } ScratchFreeslot;
    42     42   
    43     43   /*
    44     44   ** State information local to the memory allocation subsystem.
    45     45   */
    46     46   static SQLITE_WSD struct Mem0Global {
    47     47     sqlite3_mutex *mutex;         /* Mutex to serialize access */
    48         -  sqlite3_int64 alarmThreshold;  /* The soft heap limit */
           48  +  sqlite3_int64 alarmThreshold; /* The soft heap limit */
    49     49   
    50     50     /*
    51     51     ** Pointers to the end of sqlite3GlobalConfig.pScratch memory
    52     52     ** (so that a range test can be used to determine if an allocation
    53     53     ** being freed came from pScratch) and a pointer to the list of
    54     54     ** unused scratch allocations.
    55     55     */
................................................................................
    69     69   /*
    70     70   ** Return the memory allocator mutex. sqlite3_status() needs it.
    71     71   */
    72     72   sqlite3_mutex *sqlite3MallocMutex(void){
    73     73     return mem0.mutex;
    74     74   }
    75     75   
    76         -/*
    77         -** Return the amount of memory currently in use.
    78         -*/
    79         -static sqlite3_int64 memInUse(void){
    80         -  assert( sqlite3_mutex_held(mem0.mutex) );
    81         -  return sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
    82         -}
    83         -
    84         -/*
    85         -** Called when the soft heap limit is exceeded for an allocation
    86         -** of nBytes.
    87         -*/
    88         -#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
    89         -static void sqlite3HeapLimitExceeded(int nByte){
    90         -  sqlite3_int64 excess = memInUse() + nByte - mem0.alarmThreshold;
    91         -  sqlite3_mutex_leave(mem0.mutex);
    92         -  sqlite3_release_memory((int)(excess & 0x7fffffff));
    93         -  sqlite3_mutex_enter(mem0.mutex);
    94         -}
    95         -#else
    96         -# define sqlite3HeapLimitExceeded(X)  /* no-op */
    97         -#endif
    98         -
    99         -/*
   100         -** Check to see if increasing the total memory usage by nNew bytes
   101         -** will exceed the soft heap limit.  
   102         -**
   103         -** If the soft heap limit is exceeded, set the mem0.nearlyFull flag
   104         -** and invoke sqlite3HeapLimitExceeded() to try to free up some
   105         -** memory.
   106         -*/
   107         -static void sqlite3CheckSoftHeapLimit(int nNew){
   108         -  assert( sqlite3_mutex_held(mem0.mutex) );
   109         -  if( mem0.alarmThreshold>0 ){
   110         -    if( mem0.alarmThreshold-nNew >= memInUse() ){
   111         -      mem0.nearlyFull = 1;
   112         -      sqlite3HeapLimitExceeded(nNew);
   113         -    }else{
   114         -      mem0.nearlyFull = 0;
   115         -    }
   116         -  }
   117         -}
   118         -
   119     76   #ifndef SQLITE_OMIT_DEPRECATED
   120     77   /*
   121         -** Deprecated external interface.  First deprecated 2007-11-05.  Changed
   122         -** into a no-op on 2015-09-02.
           78  +** Deprecated external interface.  It used to set an alarm callback
           79  +** that was invoked when memory usage grew too large.  Now it is a
           80  +** no-op.
   123     81   */
   124     82   int sqlite3_memory_alarm(
   125     83     void(*xCallback)(void *pArg, sqlite3_int64 used,int N),
   126     84     void *pArg,
   127     85     sqlite3_int64 iThreshold
   128     86   ){
           87  +  (void)xCallback;
           88  +  (void)pArg;
           89  +  (void)iThreshold;
   129     90     return SQLITE_OK;
   130     91   }
   131     92   #endif
   132     93   
   133     94   /*
   134     95   ** Set the soft heap-size limit for the library. Passing a zero or 
   135     96   ** negative value indicates no limit.
   136     97   */
   137     98   sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 n){
   138     99     sqlite3_int64 priorLimit;
          100  +  sqlite3_int64 excess;
          101  +  sqlite3_int64 nUsed;
   139    102   #ifndef SQLITE_OMIT_AUTOINIT
   140    103     int rc = sqlite3_initialize();
   141    104     if( rc ) return -1;
   142    105   #endif
   143    106     sqlite3_mutex_enter(mem0.mutex);
   144    107     priorLimit = mem0.alarmThreshold;
   145         -  if( n>0 ){
   146         -    mem0.alarmThreshold = n;
   147         -    sqlite3CheckSoftHeapLimit(0);
   148         -  }else if( n==0 ){
   149         -    mem0.alarmThreshold = 0;
   150         -    mem0.nearlyFull = 0;
          108  +  if( n<0 ){
          109  +    sqlite3_mutex_leave(mem0.mutex);
          110  +    return priorLimit;
   151    111     }
          112  +  mem0.alarmThreshold = n;
          113  +  nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
          114  +  mem0.nearlyFull = (n>0 && n<=nUsed);
   152    115     sqlite3_mutex_leave(mem0.mutex);
          116  +  excess = sqlite3_memory_used() - n;
          117  +  if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff));
   153    118     return priorLimit;
   154    119   }
   155    120   void sqlite3_soft_heap_limit(int n){
   156    121     if( n<0 ) n = 0;
   157    122     sqlite3_soft_heap_limit64(n);
   158    123   }
   159    124   
................................................................................
   235    200   ** or since the most recent reset.
   236    201   */
   237    202   sqlite3_int64 sqlite3_memory_highwater(int resetFlag){
   238    203     sqlite3_int64 res, mx;
   239    204     sqlite3_status64(SQLITE_STATUS_MEMORY_USED, &res, &mx, resetFlag);
   240    205     return mx;
   241    206   }
          207  +
          208  +/*
          209  +** Trigger the alarm 
          210  +*/
          211  +static void sqlite3MallocAlarm(int nByte){
          212  +  if( mem0.alarmThreshold<=0 ) return;
          213  +  sqlite3_mutex_leave(mem0.mutex);
          214  +  sqlite3_release_memory(nByte);
          215  +  sqlite3_mutex_enter(mem0.mutex);
          216  +}
   242    217   
   243    218   /*
   244    219   ** Do a memory allocation with statistics and alarms.  Assume the
   245    220   ** lock is already held.
   246    221   */
   247    222   static int mallocWithAlarm(int n, void **pp){
   248    223     int nFull;
   249    224     void *p;
   250    225     assert( sqlite3_mutex_held(mem0.mutex) );
   251    226     nFull = sqlite3GlobalConfig.m.xRoundup(n);
   252    227     sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, n);
   253         -  sqlite3CheckSoftHeapLimit(nFull);
          228  +  if( mem0.alarmThreshold>0 ){
          229  +    sqlite3_int64 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
          230  +    if( nUsed >= mem0.alarmThreshold - nFull ){
          231  +      mem0.nearlyFull = 1;
          232  +      sqlite3MallocAlarm(nFull);
          233  +    }else{
          234  +      mem0.nearlyFull = 0;
          235  +    }
          236  +  }
   254    237     p = sqlite3GlobalConfig.m.xMalloc(nFull);
   255    238   #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
   256         -  if( p==0 && mem0.alarmThreshold ){
   257         -    sqlite3HeapLimitExceeded(nFull);
          239  +  if( p==0 && mem0.alarmThreshold>0 ){
          240  +    sqlite3MallocAlarm(nFull);
   258    241       p = sqlite3GlobalConfig.m.xMalloc(nFull);
   259    242     }
   260    243   #endif
   261    244     if( p ){
   262    245       nFull = sqlite3MallocSize(p);
   263    246       sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nFull);
   264    247       sqlite3StatusUp(SQLITE_STATUS_MALLOC_COUNT, 1);
................................................................................
   533    516     nNew = sqlite3GlobalConfig.m.xRoundup((int)nBytes);
   534    517     if( nOld==nNew ){
   535    518       pNew = pOld;
   536    519     }else if( sqlite3GlobalConfig.bMemstat ){
   537    520       sqlite3_mutex_enter(mem0.mutex);
   538    521       sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, (int)nBytes);
   539    522       nDiff = nNew - nOld;
   540         -    sqlite3CheckSoftHeapLimit(nDiff);
          523  +    if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >= 
          524  +          mem0.alarmThreshold-nDiff ){
          525  +      sqlite3MallocAlarm(nDiff);
          526  +    }
   541    527       pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
   542         -#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
   543         -    if( pNew==0 && mem0.alarmThreshold ){
   544         -      sqlite3HeapLimitExceeded((int)nBytes);
          528  +    if( pNew==0 && mem0.alarmThreshold>0 ){
          529  +      sqlite3MallocAlarm((int)nBytes);
   545    530         pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
   546    531       }
   547         -#endif
   548    532       if( pNew ){
   549    533         nNew = sqlite3MallocSize(pNew);
   550    534         sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nNew-nOld);
   551    535       }
   552    536       sqlite3_mutex_leave(mem0.mutex);
   553    537     }else{
   554    538       pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);

Changes to src/mutex.c.

    18     18   #if defined(SQLITE_DEBUG) && !defined(SQLITE_MUTEX_OMIT)
    19     19   /*
    20     20   ** For debugging purposes, record when the mutex subsystem is initialized
    21     21   ** and uninitialized so that we can assert() if there is an attempt to
    22     22   ** allocate a mutex while the system is uninitialized.
    23     23   */
    24     24   static SQLITE_WSD int mutexIsInit = 0;
    25         -#endif /* SQLITE_DEBUG */
           25  +#endif /* SQLITE_DEBUG && !defined(SQLITE_MUTEX_OMIT) */
    26     26   
    27     27   
    28     28   #ifndef SQLITE_MUTEX_OMIT
    29     29   /*
    30     30   ** Initialize the mutex system.
    31     31   */
    32     32   int sqlite3MutexInit(void){ 
................................................................................
    49     49       pTo->xMutexEnd = pFrom->xMutexEnd;
    50     50       pTo->xMutexFree = pFrom->xMutexFree;
    51     51       pTo->xMutexEnter = pFrom->xMutexEnter;
    52     52       pTo->xMutexTry = pFrom->xMutexTry;
    53     53       pTo->xMutexLeave = pFrom->xMutexLeave;
    54     54       pTo->xMutexHeld = pFrom->xMutexHeld;
    55     55       pTo->xMutexNotheld = pFrom->xMutexNotheld;
           56  +    sqlite3MemoryBarrier();
    56     57       pTo->xMutexAlloc = pFrom->xMutexAlloc;
    57     58     }
           59  +  assert( sqlite3GlobalConfig.mutex.xMutexInit );
    58     60     rc = sqlite3GlobalConfig.mutex.xMutexInit();
    59     61   
    60     62   #ifdef SQLITE_DEBUG
    61     63     GLOBAL(int, mutexIsInit) = 1;
    62     64   #endif
    63     65   
    64     66     return rc;
................................................................................
    85     87   ** Retrieve a pointer to a static mutex or allocate a new dynamic one.
    86     88   */
    87     89   sqlite3_mutex *sqlite3_mutex_alloc(int id){
    88     90   #ifndef SQLITE_OMIT_AUTOINIT
    89     91     if( id<=SQLITE_MUTEX_RECURSIVE && sqlite3_initialize() ) return 0;
    90     92     if( id>SQLITE_MUTEX_RECURSIVE && sqlite3MutexInit() ) return 0;
    91     93   #endif
           94  +  assert( sqlite3GlobalConfig.mutex.xMutexAlloc );
    92     95     return sqlite3GlobalConfig.mutex.xMutexAlloc(id);
    93     96   }
    94     97   
    95     98   sqlite3_mutex *sqlite3MutexAlloc(int id){
    96     99     if( !sqlite3GlobalConfig.bCoreMutex ){
    97    100       return 0;
    98    101     }
    99    102     assert( GLOBAL(int, mutexIsInit) );
          103  +  assert( sqlite3GlobalConfig.mutex.xMutexAlloc );
   100    104     return sqlite3GlobalConfig.mutex.xMutexAlloc(id);
   101    105   }
   102    106   
   103    107   /*
   104    108   ** Free a dynamic mutex.
   105    109   */
   106    110   void sqlite3_mutex_free(sqlite3_mutex *p){
   107    111     if( p ){
          112  +    assert( sqlite3GlobalConfig.mutex.xMutexFree );
   108    113       sqlite3GlobalConfig.mutex.xMutexFree(p);
   109    114     }
   110    115   }
   111    116   
   112    117   /*
   113    118   ** Obtain the mutex p. If some other thread already has the mutex, block
   114    119   ** until it can be obtained.
   115    120   */
   116    121   void sqlite3_mutex_enter(sqlite3_mutex *p){
   117    122     if( p ){
          123  +    assert( sqlite3GlobalConfig.mutex.xMutexEnter );
   118    124       sqlite3GlobalConfig.mutex.xMutexEnter(p);
   119    125     }
   120    126   }
   121    127   
   122    128   /*
   123    129   ** Obtain the mutex p. If successful, return SQLITE_OK. Otherwise, if another
   124    130   ** thread holds the mutex and it cannot be obtained, return SQLITE_BUSY.
   125    131   */
   126    132   int sqlite3_mutex_try(sqlite3_mutex *p){
   127    133     int rc = SQLITE_OK;
   128    134     if( p ){
          135  +    assert( sqlite3GlobalConfig.mutex.xMutexTry );
   129    136       return sqlite3GlobalConfig.mutex.xMutexTry(p);
   130    137     }
   131    138     return rc;
   132    139   }
   133    140   
   134    141   /*
   135    142   ** The sqlite3_mutex_leave() routine exits a mutex that was previously
   136    143   ** entered by the same thread.  The behavior is undefined if the mutex 
   137    144   ** is not currently entered. If a NULL pointer is passed as an argument
   138    145   ** this function is a no-op.
   139    146   */
   140    147   void sqlite3_mutex_leave(sqlite3_mutex *p){
   141    148     if( p ){
          149  +    assert( sqlite3GlobalConfig.mutex.xMutexLeave );
   142    150       sqlite3GlobalConfig.mutex.xMutexLeave(p);
   143    151     }
   144    152   }
   145    153   
   146    154   #ifndef NDEBUG
   147    155   /*
   148    156   ** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
   149    157   ** intended for use inside assert() statements.
   150    158   */
   151    159   int sqlite3_mutex_held(sqlite3_mutex *p){
          160  +  assert( p==0 || sqlite3GlobalConfig.mutex.xMutexHeld );
   152    161     return p==0 || sqlite3GlobalConfig.mutex.xMutexHeld(p);
   153    162   }
   154    163   int sqlite3_mutex_notheld(sqlite3_mutex *p){
          164  +  assert( p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld );
   155    165     return p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld(p);
   156    166   }
   157    167   #endif
   158    168   
   159    169   #endif /* !defined(SQLITE_MUTEX_OMIT) */

Changes to src/mutex_unix.c.

    75     75   static int pthreadMutexHeld(sqlite3_mutex *p){
    76     76     return (p->nRef!=0 && pthread_equal(p->owner, pthread_self()));
    77     77   }
    78     78   static int pthreadMutexNotheld(sqlite3_mutex *p){
    79     79     return p->nRef==0 || pthread_equal(p->owner, pthread_self())==0;
    80     80   }
    81     81   #endif
           82  +
           83  +/*
           84  +** Try to provide a memory barrier operation, needed for initialization only.
           85  +*/
           86  +void sqlite3MemoryBarrier(void){
           87  +#if defined(SQLITE_MEMORY_BARRIER)
           88  +  SQLITE_MEMORY_BARRIER;
           89  +#elif defined(__GNUC__) && GCC_VERSION>=4001000
           90  +  __sync_synchronize();
           91  +#endif
           92  +}
    82     93   
    83     94   /*
    84     95   ** Initialize and deinitialize the mutex subsystem.
    85     96   */
    86     97   static int pthreadMutexInit(void){ return SQLITE_OK; }
    87     98   static int pthreadMutexEnd(void){ return SQLITE_OK; }
    88     99   

Changes to src/mutex_w32.c.

    72     72   }
    73     73   
    74     74   static int winMutexNotheld(sqlite3_mutex *p){
    75     75     DWORD tid = GetCurrentThreadId();
    76     76     return winMutexNotheld2(p, tid);
    77     77   }
    78     78   #endif
           79  +
           80  +/*
           81  +** Try to provide a memory barrier operation, needed for initialization only.
           82  +*/
           83  +void sqlite3MemoryBarrier(void){
           84  +#if defined(SQLITE_MEMORY_BARRIER)
           85  +  SQLITE_MEMORY_BARRIER;
           86  +#elif defined(__GNUC__)
           87  +  __sync_synchronize();
           88  +#else
           89  +  MemoryBarrier();
           90  +#endif
           91  +}
    79     92   
    80     93   /*
    81     94   ** Initialize and deinitialize the mutex subsystem.
    82     95   */
    83     96   static sqlite3_mutex winMutex_staticMutexes[] = {
    84     97     SQLITE3_MUTEX_INITIALIZER,
    85     98     SQLITE3_MUTEX_INITIALIZER,

Changes to src/pager.c.

   643    643     u8 eState;                  /* Pager state (OPEN, READER, WRITER_LOCKED..) */
   644    644     u8 eLock;                   /* Current lock held on database file */
   645    645     u8 changeCountDone;         /* Set after incrementing the change-counter */
   646    646     u8 setMaster;               /* True if a m-j name has been written to jrnl */
   647    647     u8 doNotSpill;              /* Do not spill the cache when non-zero */
   648    648     u8 subjInMemory;            /* True to use in-memory sub-journals */
   649    649     u8 bUseFetch;               /* True to use xFetch() */
   650         -  u8 hasBeenUsed;             /* True if any content previously read */
          650  +  u8 hasHeldSharedLock;       /* True if a shared lock has ever been held */
   651    651     Pgno dbSize;                /* Number of pages in the database */
   652    652     Pgno dbOrigSize;            /* dbSize before the current transaction */
   653    653     Pgno dbFileSize;            /* Number of pages in the database file */
   654    654     Pgno dbHintSize;            /* Value passed to FCNTL_SIZE_HINT call */
   655    655     int errCode;                /* One of several kinds of errors */
   656    656     int nRec;                   /* Pages journalled since last j-header written */
   657    657     u32 cksumInit;              /* Quasi-random value added to every checksum */
................................................................................
  5116   5116   
  5117   5117         assert( pPager->eState==PAGER_OPEN );
  5118   5118         assert( (pPager->eLock==SHARED_LOCK)
  5119   5119              || (pPager->exclusiveMode && pPager->eLock>SHARED_LOCK)
  5120   5120         );
  5121   5121       }
  5122   5122   
  5123         -    if( !pPager->tempFile && pPager->hasBeenUsed ){
         5123  +    if( !pPager->tempFile && pPager->hasHeldSharedLock ){
  5124   5124         /* The shared-lock has just been acquired then check to
  5125   5125         ** see if the database has been modified.  If the database has changed,
  5126         -      ** flush the cache.  The pPager->hasBeenUsed flag prevents this from
         5126  +      ** flush the cache.  The hasHeldSharedLock flag prevents this from
  5127   5127         ** occurring on the very first access to a file, in order to save a
  5128   5128         ** single unnecessary sqlite3OsRead() call at the start-up.
  5129   5129         **
  5130   5130         ** Database changes are detected by looking at 15 bytes beginning
  5131   5131         ** at offset 24 into the file.  The first 4 of these 16 bytes are
  5132   5132         ** a 32-bit counter that is incremented with each change.  The
  5133   5133         ** other bytes change randomly with each file change when
................................................................................
  5189   5189    failed:
  5190   5190     if( rc!=SQLITE_OK ){
  5191   5191       assert( !MEMDB );
  5192   5192       pager_unlock(pPager);
  5193   5193       assert( pPager->eState==PAGER_OPEN );
  5194   5194     }else{
  5195   5195       pPager->eState = PAGER_READER;
         5196  +    pPager->hasHeldSharedLock = 1;
  5196   5197     }
  5197   5198     return rc;
  5198   5199   }
  5199   5200   
  5200   5201   /*
  5201   5202   ** If the reference count has reached zero, rollback any active
  5202   5203   ** transaction and unlock the pager.
................................................................................
  5272   5273     u32 iFrame = 0;                 /* Frame to read from WAL file */
  5273   5274     const int noContent = (flags & PAGER_GET_NOCONTENT);
  5274   5275   
  5275   5276     /* It is acceptable to use a read-only (mmap) page for any page except
  5276   5277     ** page 1 if there is no write-transaction open or the ACQUIRE_READONLY
  5277   5278     ** flag was specified by the caller. And so long as the db is not a 
  5278   5279     ** temporary or in-memory database.  */
  5279         -  const int bMmapOk = (pgno!=1 && USEFETCH(pPager)
         5280  +  const int bMmapOk = (pgno>1 && USEFETCH(pPager)
  5280   5281      && (pPager->eState==PAGER_READER || (flags & PAGER_GET_READONLY))
  5281   5282   #ifdef SQLITE_HAS_CODEC
  5282   5283      && pPager->xCodec==0
  5283   5284   #endif
  5284   5285     );
  5285   5286   
         5287  +  /* Optimization note:  Adding the "pgno<=1" term before "pgno==0" here
         5288  +  ** allows the compiler optimizer to reuse the results of the "pgno>1"
         5289  +  ** test in the previous statement, and avoid testing pgno==0 in the
         5290  +  ** common case where pgno is large. */
         5291  +  if( pgno<=1 && pgno==0 ){
         5292  +    return SQLITE_CORRUPT_BKPT;
         5293  +  }
  5286   5294     assert( pPager->eState>=PAGER_READER );
  5287   5295     assert( assert_pager_state(pPager) );
  5288   5296     assert( noContent==0 || bMmapOk==0 );
  5289   5297   
  5290         -  if( pgno==0 ){
  5291         -    return SQLITE_CORRUPT_BKPT;
  5292         -  }
  5293         -  pPager->hasBeenUsed = 1;
         5298  +  assert( pPager->hasHeldSharedLock==1 );
  5294   5299   
  5295   5300     /* If the pager is in the error state, return an error immediately. 
  5296   5301     ** Otherwise, request the page from the PCache layer. */
  5297   5302     if( pPager->errCode!=SQLITE_OK ){
  5298   5303       rc = pPager->errCode;
  5299   5304     }else{
  5300   5305       if( bMmapOk && pagerUseWal(pPager) ){
................................................................................
  5441   5446   */
  5442   5447   DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){
  5443   5448     sqlite3_pcache_page *pPage;
  5444   5449     assert( pPager!=0 );
  5445   5450     assert( pgno!=0 );
  5446   5451     assert( pPager->pPCache!=0 );
  5447   5452     pPage = sqlite3PcacheFetch(pPager->pPCache, pgno, 0);
  5448         -  assert( pPage==0 || pPager->hasBeenUsed );
         5453  +  assert( pPage==0 || pPager->hasHeldSharedLock );
  5449   5454     if( pPage==0 ) return 0;
  5450   5455     return sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pPage);
  5451   5456   }
  5452   5457   
  5453   5458   /*
  5454   5459   ** Release a page reference.
  5455   5460   **
................................................................................
  6411   6416   */
  6412   6417   u8 sqlite3PagerIsreadonly(Pager *pPager){
  6413   6418     return pPager->readOnly;
  6414   6419   }
  6415   6420   
  6416   6421   #ifdef SQLITE_DEBUG
  6417   6422   /*
  6418         -** Return the number of references to the pager.
         6423  +** Return the sum of the reference counts for all pages held by pPager.
  6419   6424   */
  6420   6425   int sqlite3PagerRefcount(Pager *pPager){
  6421   6426     return sqlite3PcacheRefCount(pPager->pPCache);
  6422   6427   }
  6423   6428   #endif
  6424   6429   
  6425   6430   /*

Changes to src/pcache.c.

    15     15   
    16     16   /*
    17     17   ** A complete page cache is an instance of this structure.
    18     18   */
    19     19   struct PCache {
    20     20     PgHdr *pDirty, *pDirtyTail;         /* List of dirty pages in LRU order */
    21     21     PgHdr *pSynced;                     /* Last synced page in dirty page list */
    22         -  int nRef;                           /* Number of referenced pages */
           22  +  int nRefSum;                        /* Sum of ref counts over all pages */
    23     23     int szCache;                        /* Configured cache size */
    24     24     int szPage;                         /* Size of every page in this cache */
    25     25     int szExtra;                        /* Size of extra space for each page */
    26     26     u8 bPurgeable;                      /* True if pages are on backing store */
    27     27     u8 eCreate;                         /* eCreate value for for xFetch() */
    28     28     int (*xStress)(void*,PgHdr*);       /* Call to try make a page clean */
    29     29     void *pStress;                      /* Argument to xStress */
................................................................................
   180    180   }
   181    181   
   182    182   /*
   183    183   ** Change the page size for PCache object. The caller must ensure that there
   184    184   ** are no outstanding page references when this function is called.
   185    185   */
   186    186   int sqlite3PcacheSetPageSize(PCache *pCache, int szPage){
   187         -  assert( pCache->nRef==0 && pCache->pDirty==0 );
          187  +  assert( pCache->nRefSum==0 && pCache->pDirty==0 );
   188    188     if( pCache->szPage ){
   189    189       sqlite3_pcache *pNew;
   190    190       pNew = sqlite3GlobalConfig.pcache2.xCreate(
   191    191                   szPage, pCache->szExtra + ROUND8(sizeof(PgHdr)),
   192    192                   pCache->bPurgeable
   193    193       );
   194    194       if( pNew==0 ) return SQLITE_NOMEM;
................................................................................
   347    347   
   348    348     assert( pPage!=0 );
   349    349     pPgHdr = (PgHdr *)pPage->pExtra;
   350    350   
   351    351     if( !pPgHdr->pPage ){
   352    352       return pcacheFetchFinishWithInit(pCache, pgno, pPage);
   353    353     }
   354         -  if( 0==pPgHdr->nRef ){
   355         -    pCache->nRef++;
   356         -  }
          354  +  pCache->nRefSum++;
   357    355     pPgHdr->nRef++;
   358    356     return pPgHdr;
   359    357   }
   360    358   
   361    359   /*
   362    360   ** Decrement the reference count on a page. If the page is clean and the
   363    361   ** reference count drops to 0, then it is made eligible for recycling.
   364    362   */
   365    363   void SQLITE_NOINLINE sqlite3PcacheRelease(PgHdr *p){
   366    364     assert( p->nRef>0 );
   367         -  p->nRef--;
   368         -  if( p->nRef==0 ){
   369         -    p->pCache->nRef--;
          365  +  p->pCache->nRefSum--;
          366  +  if( (--p->nRef)==0 ){
   370    367       if( p->flags&PGHDR_CLEAN ){
   371    368         pcacheUnpin(p);
   372    369       }else if( p->pDirtyPrev!=0 ){
   373    370         /* Move the page to the head of the dirty list. */
   374    371         pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT);
   375    372       }
   376    373     }
................................................................................
   378    375   
   379    376   /*
   380    377   ** Increase the reference count of a supplied page by 1.
   381    378   */
   382    379   void sqlite3PcacheRef(PgHdr *p){
   383    380     assert(p->nRef>0);
   384    381     p->nRef++;
          382  +  p->pCache->nRefSum++;
   385    383   }
   386    384   
   387    385   /*
   388    386   ** Drop a page from the cache. There must be exactly one reference to the
   389    387   ** page. This function deletes that reference, so after it returns the
   390    388   ** page pointed to by p is invalid.
   391    389   */
   392    390   void sqlite3PcacheDrop(PgHdr *p){
   393    391     assert( p->nRef==1 );
   394    392     if( p->flags&PGHDR_DIRTY ){
   395    393       pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE);
   396    394     }
   397         -  p->pCache->nRef--;
          395  +  p->pCache->nRefSum--;
   398    396     sqlite3GlobalConfig.pcache2.xUnpin(p->pCache->pCache, p->pPage, 1);
   399    397   }
   400    398   
   401    399   /*
   402    400   ** Make sure the page is marked as dirty. If it isn't dirty already,
   403    401   ** make it so.
   404    402   */
................................................................................
   486    484         */
   487    485         assert( p->pgno>0 );
   488    486         if( ALWAYS(p->pgno>pgno) ){
   489    487           assert( p->flags&PGHDR_DIRTY );
   490    488           sqlite3PcacheMakeClean(p);
   491    489         }
   492    490       }
   493         -    if( pgno==0 && pCache->nRef ){
          491  +    if( pgno==0 && pCache->nRefSum ){
   494    492         sqlite3_pcache_page *pPage1;
   495    493         pPage1 = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache,1,0);
   496    494         if( ALWAYS(pPage1) ){  /* Page 1 is always available in cache, because
   497         -                             ** pCache->nRef>0 */
          495  +                             ** pCache->nRefSum>0 */
   498    496           memset(pPage1->pBuf, 0, pCache->szPage);
   499    497           pgno = 1;
   500    498         }
   501    499       }
   502    500       sqlite3GlobalConfig.pcache2.xTruncate(pCache->pCache, pgno+1);
   503    501     }
   504    502   }
................................................................................
   596    594     for(p=pCache->pDirty; p; p=p->pDirtyNext){
   597    595       p->pDirty = p->pDirtyNext;
   598    596     }
   599    597     return pcacheSortDirtyList(pCache->pDirty);
   600    598   }
   601    599   
   602    600   /* 
   603         -** Return the total number of referenced pages held by the cache.
          601  +** Return the total number of references to all pages held by the cache.
          602  +**
          603  +** This is not the total number of pages referenced, but the sum of the
          604  +** reference count for all pages.
   604    605   */
   605    606   int sqlite3PcacheRefCount(PCache *pCache){
   606         -  return pCache->nRef;
          607  +  return pCache->nRefSum;
   607    608   }
   608    609   
   609    610   /*
   610    611   ** Return the number of references to the page supplied as an argument.
   611    612   */
   612    613   int sqlite3PcachePageRefcount(PgHdr *p){
   613    614     return p->nRef;

Changes to src/pcache1.c.

    82     82   */
    83     83   #include "sqliteInt.h"
    84     84   
    85     85   typedef struct PCache1 PCache1;
    86     86   typedef struct PgHdr1 PgHdr1;
    87     87   typedef struct PgFreeslot PgFreeslot;
    88     88   typedef struct PGroup PGroup;
           89  +
           90  +/*
           91  +** Each cache entry is represented by an instance of the following 
           92  +** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of
           93  +** PgHdr1.pCache->szPage bytes is allocated directly before this structure 
           94  +** in memory.
           95  +*/
           96  +struct PgHdr1 {
           97  +  sqlite3_pcache_page page;      /* Base class. Must be first. pBuf & pExtra */
           98  +  unsigned int iKey;             /* Key value (page number) */
           99  +  u8 isPinned;                   /* Page in use, not on the LRU list */
          100  +  u8 isBulkLocal;                /* This page from bulk local storage */
          101  +  u8 isAnchor;                   /* This is the PGroup.lru element */
          102  +  PgHdr1 *pNext;                 /* Next in hash table chain */
          103  +  PCache1 *pCache;               /* Cache that currently owns this page */
          104  +  PgHdr1 *pLruNext;              /* Next in LRU list of unpinned pages */
          105  +  PgHdr1 *pLruPrev;              /* Previous in LRU list of unpinned pages */
          106  +};
    89    107   
    90    108   /* Each page cache (or PCache) belongs to a PGroup.  A PGroup is a set 
    91    109   ** of one or more PCaches that are able to recycle each other's unpinned
    92    110   ** pages when they are under memory pressure.  A PGroup is an instance of
    93    111   ** the following object.
    94    112   **
    95    113   ** This page cache implementation works in one of two modes:
................................................................................
   111    129   */
   112    130   struct PGroup {
   113    131     sqlite3_mutex *mutex;          /* MUTEX_STATIC_LRU or NULL */
   114    132     unsigned int nMaxPage;         /* Sum of nMax for purgeable caches */
   115    133     unsigned int nMinPage;         /* Sum of nMin for purgeable caches */
   116    134     unsigned int mxPinned;         /* nMaxpage + 10 - nMinPage */
   117    135     unsigned int nCurrentPage;     /* Number of purgeable pages allocated */
   118         -  PgHdr1 *pLruHead, *pLruTail;   /* LRU list of unpinned pages */
          136  +  PgHdr1 lru;                    /* The beginning and end of the LRU list */
   119    137   };
   120    138   
   121    139   /* Each page cache is an instance of the following object.  Every
   122    140   ** open database file (including each in-memory database and each
   123    141   ** temporary or transient database) has a single page cache which
   124    142   ** is an instance of this object.
   125    143   **
................................................................................
   149    167     unsigned int nPage;                 /* Total number of pages in apHash */
   150    168     unsigned int nHash;                 /* Number of slots in apHash[] */
   151    169     PgHdr1 **apHash;                    /* Hash table for fast lookup by key */
   152    170     PgHdr1 *pFree;                      /* List of unused pcache-local pages */
   153    171     void *pBulk;                        /* Bulk memory used by pcache-local */
   154    172   };
   155    173   
   156         -/*
   157         -** Each cache entry is represented by an instance of the following 
   158         -** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of
   159         -** PgHdr1.pCache->szPage bytes is allocated directly before this structure 
   160         -** in memory.
   161         -*/
   162         -struct PgHdr1 {
   163         -  sqlite3_pcache_page page;
   164         -  unsigned int iKey;             /* Key value (page number) */
   165         -  u8 isPinned;                   /* Page in use, not on the LRU list */
   166         -  u8 isBulkLocal;                /* This page from bulk local storage */
   167         -  PgHdr1 *pNext;                 /* Next in hash table chain */
   168         -  PCache1 *pCache;               /* Cache that currently owns this page */
   169         -  PgHdr1 *pLruNext;              /* Next in LRU list of unpinned pages */
   170         -  PgHdr1 *pLruPrev;              /* Previous in LRU list of unpinned pages */
   171         -};
   172         -
   173    174   /*
   174    175   ** Free slots in the allocator used to divide up the global page cache
   175    176   ** buffer provided using the SQLITE_CONFIG_PAGECACHE mechanism.
   176    177   */
   177    178   struct PgFreeslot {
   178    179     PgFreeslot *pNext;  /* Next free slot */
   179    180   };
................................................................................
   225    226   # define pcache1EnterMutex(X) sqlite3_mutex_enter((X)->mutex)
   226    227   # define pcache1LeaveMutex(X) sqlite3_mutex_leave((X)->mutex)
   227    228   # define PCACHE1_MIGHT_USE_GROUP_MUTEX 1
   228    229   #endif
   229    230   
   230    231   /******************************************************************************/
   231    232   /******** Page Allocation/SQLITE_CONFIG_PCACHE Related Functions **************/
          233  +
   232    234   
   233    235   /*
   234    236   ** This function is called during initialization if a static buffer is 
   235    237   ** supplied to use for the page-cache by passing the SQLITE_CONFIG_PAGECACHE
   236    238   ** verb to sqlite3_config(). Parameter pBuf points to an allocation large
   237    239   ** enough to contain 'n' buffers of 'sz' bytes each.
   238    240   **
................................................................................
   285    287       int nBulk = sqlite3MallocSize(zBulk)/pCache->szAlloc;
   286    288       int i;
   287    289       for(i=0; i<nBulk; i++){
   288    290         PgHdr1 *pX = (PgHdr1*)&zBulk[pCache->szPage];
   289    291         pX->page.pBuf = zBulk;
   290    292         pX->page.pExtra = &pX[1];
   291    293         pX->isBulkLocal = 1;
          294  +      pX->isAnchor = 0;
   292    295         pX->pNext = pCache->pFree;
   293    296         pCache->pFree = pX;
   294    297         zBulk += pCache->szAlloc;
   295    298       }
   296    299     }
   297    300     return pCache->pFree!=0;
   298    301   }
................................................................................
   427    430   #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
   428    431       pcache1EnterMutex(pCache->pGroup);
   429    432   #endif
   430    433       if( pPg==0 ) return 0;
   431    434       p->page.pBuf = pPg;
   432    435       p->page.pExtra = &p[1];
   433    436       p->isBulkLocal = 0;
          437  +    p->isAnchor = 0;
   434    438     }
   435    439     if( pCache->bPurgeable ){
   436    440       pCache->pGroup->nCurrentPage++;
   437    441     }
   438    442     return p;
   439    443   }
   440    444   
................................................................................
   553    557   */
   554    558   static PgHdr1 *pcache1PinPage(PgHdr1 *pPage){
   555    559     PCache1 *pCache;
   556    560   
   557    561     assert( pPage!=0 );
   558    562     assert( pPage->isPinned==0 );
   559    563     pCache = pPage->pCache;
   560         -  assert( pPage->pLruNext || pPage==pCache->pGroup->pLruTail );
   561         -  assert( pPage->pLruPrev || pPage==pCache->pGroup->pLruHead );
          564  +  assert( pPage->pLruNext );
          565  +  assert( pPage->pLruPrev );
   562    566     assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
   563         -  if( pPage->pLruPrev ){
   564         -    pPage->pLruPrev->pLruNext = pPage->pLruNext;
   565         -  }else{
   566         -    pCache->pGroup->pLruHead = pPage->pLruNext;
   567         -  }
   568         -  if( pPage->pLruNext ){
   569         -    pPage->pLruNext->pLruPrev = pPage->pLruPrev;
   570         -  }else{
   571         -    pCache->pGroup->pLruTail = pPage->pLruPrev;
   572         -  }
          567  +  pPage->pLruPrev->pLruNext = pPage->pLruNext;
          568  +  pPage->pLruNext->pLruPrev = pPage->pLruPrev;
   573    569     pPage->pLruNext = 0;
   574    570     pPage->pLruPrev = 0;
   575    571     pPage->isPinned = 1;
          572  +  assert( pPage->isAnchor==0 );
          573  +  assert( pCache->pGroup->lru.isAnchor==1 );
   576    574     pCache->nRecyclable--;
   577    575     return pPage;
   578    576   }
   579    577   
   580    578   
   581    579   /*
   582    580   ** Remove the page supplied as an argument from the hash table 
................................................................................
   601    599   
   602    600   /*
   603    601   ** If there are currently more than nMaxPage pages allocated, try
   604    602   ** to recycle pages to reduce the number allocated to nMaxPage.
   605    603   */
   606    604   static void pcache1EnforceMaxPage(PCache1 *pCache){
   607    605     PGroup *pGroup = pCache->pGroup;
          606  +  PgHdr1 *p;
   608    607     assert( sqlite3_mutex_held(pGroup->mutex) );
   609         -  while( pGroup->nCurrentPage>pGroup->nMaxPage && pGroup->pLruTail ){
   610         -    PgHdr1 *p = pGroup->pLruTail;
          608  +  while( pGroup->nCurrentPage>pGroup->nMaxPage
          609  +      && (p=pGroup->lru.pLruPrev)->isAnchor==0
          610  +  ){
   611    611       assert( p->pCache->pGroup==pGroup );
   612    612       assert( p->isPinned==0 );
   613    613       pcache1PinPage(p);
   614    614       pcache1RemoveFromHash(p, 1);
   615    615     }
   616    616     if( pCache->nPage==0 && pCache->pBulk ){
   617    617       sqlite3_free(pCache->pBulk);
................................................................................
   737    737     if( pCache ){
   738    738       if( pcache1.separateCache ){
   739    739         pGroup = (PGroup*)&pCache[1];
   740    740         pGroup->mxPinned = 10;
   741    741       }else{
   742    742         pGroup = &pcache1.grp;
   743    743       }
          744  +    if( pGroup->lru.isAnchor==0 ){
          745  +      pGroup->lru.isAnchor = 1;
          746  +      pGroup->lru.pLruPrev = pGroup->lru.pLruNext = &pGroup->lru;
          747  +    }
   744    748       pCache->pGroup = pGroup;
   745    749       pCache->szPage = szPage;
   746    750       pCache->szExtra = szExtra;
   747    751       pCache->szAlloc = szPage + szExtra + ROUND8(sizeof(PgHdr1));
   748    752       pCache->bPurgeable = (bPurgeable ? 1 : 0);
   749    753       pcache1EnterMutex(pGroup);
   750    754       pcache1ResizeHash(pCache);
................................................................................
   844    848     }
   845    849   
   846    850     if( pCache->nPage>=pCache->nHash ) pcache1ResizeHash(pCache);
   847    851     assert( pCache->nHash>0 && pCache->apHash );
   848    852   
   849    853     /* Step 4. Try to recycle a page. */
   850    854     if( pCache->bPurgeable
   851         -   && pGroup->pLruTail
          855  +   && !pGroup->lru.pLruPrev->isAnchor
   852    856      && ((pCache->nPage+1>=pCache->nMax) || pcache1UnderMemoryPressure(pCache))
   853    857     ){
   854    858       PCache1 *pOther;
   855         -    pPage = pGroup->pLruTail;
          859  +    pPage = pGroup->lru.pLruPrev;
   856    860       assert( pPage->isPinned==0 );
   857    861       pcache1RemoveFromHash(pPage, 0);
   858    862       pcache1PinPage(pPage);
   859    863       pOther = pPage->pCache;
   860    864       if( pOther->szAlloc != pCache->szAlloc ){
   861    865         pcache1FreePage(pPage);
   862    866         pPage = 0;
................................................................................
   957    961     PCache1 *pCache = (PCache1 *)p;
   958    962     PgHdr1 *pPage = 0;
   959    963   
   960    964     /* Step 1: Search the hash table for an existing entry. */
   961    965     pPage = pCache->apHash[iKey % pCache->nHash];
   962    966     while( pPage && pPage->iKey!=iKey ){ pPage = pPage->pNext; }
   963    967   
   964         -  /* Step 2: Abort if no existing page is found and createFlag is 0 */
          968  +  /* Step 2: If the page was found in the hash table, then return it.
          969  +  ** If the page was not in the hash table and createFlag is 0, abort.
          970  +  ** Otherwise (page not in hash and createFlag!=0) continue with
          971  +  ** subsequent steps to try to create the page. */
   965    972     if( pPage ){
   966    973       if( !pPage->isPinned ){
   967    974         return pcache1PinPage(pPage);
   968    975       }else{
   969    976         return pPage;
   970    977       }
   971    978     }else if( createFlag ){
................................................................................
  1034   1041     assert( pPage->pCache==pCache );
  1035   1042     pcache1EnterMutex(pGroup);
  1036   1043   
  1037   1044     /* It is an error to call this function if the page is already 
  1038   1045     ** part of the PGroup LRU list.
  1039   1046     */
  1040   1047     assert( pPage->pLruPrev==0 && pPage->pLruNext==0 );
  1041         -  assert( pGroup->pLruHead!=pPage && pGroup->pLruTail!=pPage );
  1042   1048     assert( pPage->isPinned==1 );
  1043   1049   
  1044   1050     if( reuseUnlikely || pGroup->nCurrentPage>pGroup->nMaxPage ){
  1045   1051       pcache1RemoveFromHash(pPage, 1);
  1046   1052     }else{
  1047   1053       /* Add the page to the PGroup LRU list. */
  1048         -    if( pGroup->pLruHead ){
  1049         -      pGroup->pLruHead->pLruPrev = pPage;
  1050         -      pPage->pLruNext = pGroup->pLruHead;
  1051         -      pGroup->pLruHead = pPage;
  1052         -    }else{
  1053         -      pGroup->pLruTail = pPage;
  1054         -      pGroup->pLruHead = pPage;
  1055         -    }
         1054  +    PgHdr1 **ppFirst = &pGroup->lru.pLruNext;
         1055  +    pPage->pLruPrev = &pGroup->lru;
         1056  +    (pPage->pLruNext = *ppFirst)->pLruPrev = pPage;
         1057  +    *ppFirst = pPage;
  1056   1058       pCache->nRecyclable++;
  1057   1059       pPage->isPinned = 0;
  1058   1060     }
  1059   1061   
  1060   1062     pcache1LeaveMutex(pCache->pGroup);
  1061   1063   }
  1062   1064   
................................................................................
  1186   1188   int sqlite3PcacheReleaseMemory(int nReq){
  1187   1189     int nFree = 0;
  1188   1190     assert( sqlite3_mutex_notheld(pcache1.grp.mutex) );
  1189   1191     assert( sqlite3_mutex_notheld(pcache1.mutex) );
  1190   1192     if( sqlite3GlobalConfig.nPage==0 ){
  1191   1193       PgHdr1 *p;
  1192   1194       pcache1EnterMutex(&pcache1.grp);
  1193         -    while( (nReq<0 || nFree<nReq) && ((p=pcache1.grp.pLruTail)!=0) ){
         1195  +    while( (nReq<0 || nFree<nReq)
         1196  +       &&  (p=pcache1.grp.lru.pLruPrev)!=0
         1197  +       &&  p->isAnchor==0
         1198  +    ){
  1194   1199         nFree += pcache1MemSize(p->page.pBuf);
  1195   1200   #ifdef SQLITE_PCACHE_SEPARATE_HEADER
  1196   1201         nFree += sqlite3MemSize(p);
  1197   1202   #endif
  1198   1203         assert( p->isPinned==0 );
  1199   1204         pcache1PinPage(p);
  1200   1205         pcache1RemoveFromHash(p, 1);
................................................................................
  1214   1219     int *pnCurrent,      /* OUT: Total number of pages cached */
  1215   1220     int *pnMax,          /* OUT: Global maximum cache size */
  1216   1221     int *pnMin,          /* OUT: Sum of PCache1.nMin for purgeable caches */
  1217   1222     int *pnRecyclable    /* OUT: Total number of pages available for recycling */
  1218   1223   ){
  1219   1224     PgHdr1 *p;
  1220   1225     int nRecyclable = 0;
  1221         -  for(p=pcache1.grp.pLruHead; p; p=p->pLruNext){
         1226  +  for(p=pcache1.grp.lru.pLruNext; p && !p->isAnchor; p=p->pLruNext){
  1222   1227       assert( p->isPinned==0 );
  1223   1228       nRecyclable++;
  1224   1229     }
  1225   1230     *pnCurrent