Check-in [1c2d878d12]
Not logged in
Overview

SHA1 Hash:1c2d878d12ee5ffb9f36a01ac69ed693a7dd39dc
Date: 2009-12-13 01:16:13
User: btheado
Comment:Merge with trunk
Timelines: ancestors | descendants | both | sql-func
Other Links: files | ZIP archive | manifest

Tags And Properties
Changes
[hide diffs]

Modified BUILD.txt from [334b80c575] to [4f7988767e].

@@ -2,16 +2,43 @@
 But there is a lot of generated code, so you will probably want to
 use the Makefile.  To do a complete build, just type:
 
    make
 
-That should work out-of-the-box on Macs and Linux systems.  If
-you have trouble, or you want to do something fancy, just edit
-the Makefile in the top-level folder.  There are 5 things you
-might want to change in the Makefile.  All 5 things are well
-commented.  The complete Makefile is only a few dozen lines long.
-Do not be intimidated.
+That should work out-of-the-box on Macs and Linux systems. If you are
+building on a Windows box, install MinGW as well as MinGW's make (or
+MSYS). You can then type:
+
+  make -f Makefile.w32
+
+If you have trouble, or you want to do something fancy, just look at
+top level makefile. There are 5 configuration options that are all well
+commented. Instead of editing the Makefile, create a new file named
+config.mak (for Macs and Linux systems) or config.w32 (for Windows) and
+override any settings you wish there.
+
+Out of source builds?
+--------------------------------------------------------------------------
+
+An out of source build is pretty easy:
+
+  1. Make a new directory to do the builds in.
+  2. Create a config.mak (or .w32 ... explained above) and add something
+  along the lines of:
+
+    SRCDIR=../src
+
+  3. From that directory, type:
+
+    Macs and Linux:
+      $ make -f ../Makefile
+
+    Windows:
+      C:\fossil\build> make -f ../Makefile.w32
+
+This will now keep all generates files seperate from the maintained
+source code.
 
 --------------------------------------------------------------------------
 
 Here are some notes on what is happening behind the scenes:
 

Modified Makefile from [c357142045] to [21a564c8f5].

@@ -43,9 +43,13 @@
 
 
 #### Tcl shell for use in running the fossil testsuite.
 #
 TCLSH = tclsh
+
+#### Include a configuration file that can override any one of these settings.
+#
+-include config.mak
 
 # You should not need to change anything below this line
 ###############################################################################
 include $(SRCDIR)/main.mk

Modified Makefile.w32 from [900e354562] to [a8929670d9].

@@ -41,9 +41,13 @@
 LIB = -lmingwex -lz -lws2_32
 
 #### Tcl shell for use in running the fossil testsuite.
 #
 TCLSH = tclsh
+
+#### Include a configuration file that can override any one of these settings.
+#
+-include config.w32
 
 # You should not need to change anything below this line
 ###############################################################################
 include $(SRCDIR)/main.mk

Modified src/add.c from [c094792c50] to [d5685d9fd3].

@@ -198,10 +198,11 @@
     if( !db_exists(
              "SELECT 1 FROM vfile WHERE pathname=%Q AND NOT deleted", zPath) ){
       fossil_fatal("not in the repository: %s", zName);
     }else{
       db_multi_exec("UPDATE vfile SET deleted=1 WHERE pathname=%Q", zPath);
+      printf("DELETED  %s\n", zPath);
     }
     blob_reset(&pathname);
     free(zName);
   }
   db_multi_exec("DELETE FROM vfile WHERE deleted AND rid=0");

Modified src/branch.c from [3ef4b504fa] to [80adbdb5e4].

@@ -132,11 +132,11 @@
   md5sum_blob(&branch, &mcksum);
   blob_appendf(&branch, "Z %b\n", &mcksum);
   if( !noSign && clearsign(&branch, &branch) ){
     Blob ans;
     blob_zero(&ans);
-    prompt_user("unable to sign manifest.  continue [y/N]? ", &ans);
+    prompt_user("unable to sign manifest.  continue (y/N)? ", &ans);
     if( blob_str(&ans)[0]!='y' ){
       db_end_transaction(1);
       exit(1);
     }
   }

Modified src/checkin.c from [b32786b578] to [c1819242e8].

@@ -237,11 +237,11 @@
   while( db_step(&q)==SQLITE_ROW ){
     if( allFlag ){
       unlink(db_column_text(&q, 0));
     }else{
       Blob ans;
-      char *prompt = mprintf("remove unmanaged file \"%s\" [y/N]? ",
+      char *prompt = mprintf("remove unmanaged file \"%s\" (y/N)? ",
                               db_column_text(&q, 0));
       blob_zero(&ans);
       prompt_user(prompt, &ans);
       if( blob_str(&ans)[0]=='y' ){
         unlink(db_column_text(&q, 0));
@@ -257,22 +257,23 @@
 ** the VISUAL or EDITOR environment variable.
 **
 ** Store the final commit comment in pComment.  pComment is assumed
 ** to be uninitialized - any prior content is overwritten.
 */
-static void prepare_commit_comment(Blob *pComment){
+static void prepare_commit_comment(Blob *pComment, char *zInit){
   const char *zEditor;
   char *zCmd;
   char *zFile;
   Blob text, line;
   char *zComment;
   int i;
-  blob_set(&text,
+  blob_init(&text, zInit, -1);
+  blob_append(&text,
     "\n"
     "# Enter comments on this check-in.  Lines beginning with # are ignored.\n"
     "# The check-in comment follows wiki formatting rules.\n"
-    "#\n"
+    "#\n", -1
   );
   if( g.markPrivate ){
     blob_append(&text,
       "# PRIVATE BRANCH: This check-in will be private and will not sync to\n"
       "# repositories.\n"
@@ -388,15 +389,22 @@
 **
 ** Usage: %fossil commit ?OPTIONS? ?FILE...?
 **
 ** Create a new version containing all of the changes in the current
 ** checkout.  You will be prompted to enter a check-in comment unless
-** the "-m" option is used to specify a comment line.  You will be
-** prompted for your GPG passphrase in order to sign the new manifest
-** unless the "--nosign" options is used.  All files that have
-** changed will be committed unless some subset of files is specified
-** on the command line.
+** one of the "-m" or "-M" options are used to specify a comment.
+** "-m" takes a single string for the commit message and "-M" requires
+** a filename from which to read the commit message. If neither "-m"
+** nor "-M" are specified then the editor defined in the "editor"
+** fossil option (see %fossil help set) will be used, or from the
+** "VISUAL" or "EDITOR" environment variables (in that order) if no
+** editor is set.
+**
+** You will be prompted for your GPG passphrase in order to sign the
+** new manifest unless the "--nosign" options is used.  All files that
+** have changed will be committed unless some subset of files is
+** specified on the command line.
 **
 ** The --branch option followed by a branch name cases the new check-in
 ** to be placed in the named branch.  The --bgcolor option can be followed
 ** by a color name (ex:  '#ffc0c0') to specify the background color of
 ** entries in the new branch when shown in the web timeline interface.
@@ -413,10 +421,11 @@
 **    --branch NEW-BRANCH-NAME
 **    --bgcolor COLOR
 **    --nosign
 **    --force|-f
 **    --private
+**    --message-file|-M COMMENT-FILE
 **
 */
 void commit_cmd(void){
   int rc;
   int vid, nrid, nvid;
@@ -432,10 +441,11 @@
   int nBasename;         /* Length of "g.zLocalRoot/" */
   const char *zBranch;   /* Create a new branch with this name */
   const char *zBgColor;  /* Set background color when branching */
   const char *zDateOvrd; /* Override date string */
   const char *zUserOvrd; /* Override user name */
+  const char *zCommentFile; /* Read commit message from this file */
   Blob filename;         /* complete filename */
   Blob manifest;
   Blob muuid;            /* Manifest uuid */
   Blob mcksum;           /* Self-checksum on the manifest */
   Blob cksum1, cksum2;   /* Before and after commit checksums */
@@ -445,10 +455,11 @@
   noSign = find_option("nosign",0,0)!=0;
   zComment = find_option("comment","m",1);
   forceFlag = find_option("force", "f", 0)!=0;
   zBranch = find_option("branch","b",1);
   zBgColor = find_option("bgcolor",0,1);
+  zCommentFile = find_option("message-file", "M", 1);
   if( find_option("private",0,0) ){
     g.markPrivate = 1;
     if( zBranch==0 ) zBranch = "private";
     if( zBgColor==0 ) zBgColor = "#fec084";  /* Orange */
   }
@@ -537,21 +548,30 @@
 
   vfile_aggregate_checksum_disk(vid, &cksum1);
   if( zComment ){
     blob_zero(&comment);
     blob_append(&comment, zComment, -1);
+  }else if( zCommentFile ){
+    blob_zero(&comment);
+    blob_read_from_file(&comment, zCommentFile);
   }else{
-    prepare_commit_comment(&comment);
-    if( blob_size(&comment)==0 ){
-      Blob ans;
-      blob_zero(&ans);
-      prompt_user("empty check-in comment.  continue [y/N]? ", &ans);
-      if( blob_str(&ans)[0]!='y' ){
-        db_end_transaction(1);
-        exit(1);
-      }
+    char *zInit = db_text(0, "SELECT value FROM vvar WHERE name='ci-comment'");
+    prepare_commit_comment(&comment, zInit);
+    free(zInit);
+  }
+  if( blob_size(&comment)==0 ){
+    Blob ans;
+    blob_zero(&ans);
+    prompt_user("empty check-in comment.  continue (y/N)? ", &ans);
+    if( blob_str(&ans)[0]!='y' ){
+      db_end_transaction(1);
+      exit(1);
     }
+  }else{
+    db_multi_exec("REPLACE INTO vvar VALUES('ci-comment',%B)", &comment);
+    db_end_transaction(0);
+    db_begin_transaction();
   }
 
   /* Step 1: Insert records for all modified files into the blob
   ** table. If there were arguments passed to this command, only
   ** the identified fils are inserted (if they have been modified).
@@ -667,11 +687,11 @@
   blob_appendf(&manifest, "Z %b\n", &mcksum);
   zManifestFile = mprintf("%smanifest", g.zLocalRoot);
   if( !noSign && !g.markPrivate && clearsign(&manifest, &manifest) ){
     Blob ans;
     blob_zero(&ans);
-    prompt_user("unable to sign manifest.  continue [y/N]? ", &ans);
+    prompt_user("unable to sign manifest.  continue (y/N)? ", &ans);
     if( blob_str(&ans)[0]!='y' ){
       db_end_transaction(1);
       exit(1);
     }
   }
@@ -735,10 +755,11 @@
 
   /* Clear the undo/redo stack */
   undo_reset();
 
   /* Commit */
+  db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'");
   db_end_transaction(0);
 
   if( !g.markPrivate ){
     autosync(AUTOSYNC_PUSH);
   }

Modified src/db.c from [afc733b37d] to [6a9e0f8839].

@@ -1468,10 +1468,16 @@
 ** values.  With just a property name it shows the value of that property.
 ** With a value argument it changes the property for the current repository.
 **
 ** The "unset" command clears a property setting.
 **
+**
+**    anon-login-enable-captcha-filler
+**                     If enabled, the Login page will provide a button
+**                     which uses JavaScript to fill out the captcha for
+**                     the user. (Most bots cannot use JavaScript.)
+**
 **    autosync         If enabled, automatically pull prior to
 **                     commit or update and automatically push
 **                     after commit or tag or branch creation.
 **
 **    diff-command     External command to run when performing a diff.
@@ -1513,10 +1519,11 @@
 **                     Defaults to "start" on windows, "open" on Mac,
 **                     and "firefox" on Unix.
 */
 void setting_cmd(void){
   static const char *azName[] = {
+    "anon-login-enable-captcha-filler",
     "autosync",
     "diff-command",
     "dont-push",
     "editor",
     "gdiff-command",

Modified src/login.c from [3b52c138db] to [646f690c52].

@@ -225,11 +225,11 @@
     @   <td><input type="text" id="u" name="u" value="" size=30></td>
   }
   @ </tr>
   @ <tr>
   @  <td align="right">Password:</td>
-  @   <td><input type="password" name="p" value="" size=30></td>
+  @   <td><input type="password" id="p" name="p" value="" size=30></td>
   @ </tr>
   if( g.zLogin==0 ){
     zAnonPw = db_text(0, "SELECT pw FROM user"
                          " WHERE login='anonymous'"
                          "   AND cap!=''");
@@ -250,18 +250,24 @@
   @ "Login" button.  Your user name will be stored in a browser cookie.
   @ You must configure your web browser to accept cookies in order for
   @ the login to take.</p>
   if( zAnonPw ){
     unsigned int uSeed = captcha_seed();
-    char *zCaptcha = captcha_render(captcha_decode(uSeed));
-
-    @ <input type="hidden" name="cs" value="%u(uSeed)">
+    char const * zDecoded = captcha_decode(uSeed);
+    int iAllowPasswordFill = db_get_boolean( "anon-login-enable-captcha-filler", 0 );
+    char *zCaptcha = captcha_render(zDecoded);
+
+    @ <input type="hidden" name="cs" value="%u(uSeed)"/>
     @ <p>Visitors may enter <b>anonymous</b> as the user-ID with
     @ the 8-character hexadecimal password shown below:</p>
     @ <center><table border="1" cellpadding="10"><tr><td><pre>
     @ %s(zCaptcha)
-    @ </pre></td></tr></table></center>
+    @ </pre></td></tr></table>
+    if( iAllowPasswordFill ) {
+        @ <input type="button" value="Fill out captcha" onclick="document.getElementById('u').value='anonymous'; document.getElementById('p').value='%s(zDecoded)';"/>
+    }
+    @ </center>
     free(zCaptcha);
   }
   if( g.zLogin ){
     @ <br clear="both"><hr>
     @ <p>To log off the system (and delete your login cookie)

Modified src/rebuild.c from [fa901199b7] to [ab6ed5de1a].

@@ -371,11 +371,11 @@
   if( !bForce ){
     Blob ans;
     blob_zero(&ans);
     prompt_user("Scrubbing the repository will permanently remove user\n"
                 "passwords and other information. Changes cannot be undone.\n"
-                "Continue [y/N]? ", &ans);
+                "Continue (y/N)? ", &ans);
     if( blob_str(&ans)[0]!='y' ){
       exit(1);
     }
   }
   db_begin_transaction();

Modified src/rss.c from [945efe289e] to [cc90868801].

@@ -26,27 +26,13 @@
 #include "config.h"
 #include "rss.h"
 #include <assert.h>
 #include <time.h>
 
-time_t rss_datetime_to_time_t(const char *dt){
-  struct tm the_tm;
-
-  the_tm.tm_year = atoi(dt)-1900;
-  the_tm.tm_mon  = atoi(&dt[5])-1;
-  the_tm.tm_mday = atoi(&dt[8]);
-  the_tm.tm_hour = atoi(&dt[11]);
-  the_tm.tm_min  = atoi(&dt[14]);
-  the_tm.tm_sec  = atoi(&dt[17]);
-
-  return mktime(&the_tm);
-}
-
 /*
 ** WEBPAGE: timeline.rss
 */
-
 void page_timeline_rss(void){
   Stmt q;
   int nLine=0;
   char *zPubDate, *zProjectName, *zProjectDescr, *zFreeProjectName=0;
   Blob bSQL;
@@ -53,11 +39,11 @@
   const char *zType = PD("y","all"); /* Type of events.  All if NULL */
   const char zSQL1[] =
     @ SELECT
     @   blob.rid,
     @   uuid,
-    @   datetime(event.mtime),
+    @   event.mtime,
     @   coalesce(ecomment,comment),
     @   coalesce(euser,user),
     @   (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim),
     @   (SELECT count(*) FROM plink WHERE cid=blob.rid)
     @ FROM event, blob
@@ -96,18 +82,20 @@
   @     <generator>Fossil version %s(MANIFEST_VERSION) %s(MANIFEST_DATE)</generator>
   db_prepare(&q, blob_buffer(&bSQL));
   blob_reset( &bSQL );
   while( db_step(&q)==SQLITE_ROW && nLine<=20 ){
     const char *zId = db_column_text(&q, 1);
-    const char *zDate = db_column_text(&q, 2);
     const char *zCom = db_column_text(&q, 3);
     const char *zAuthor = db_column_text(&q, 4);
     char *zPrefix = "";
+    char *zDate;
     int nChild = db_column_int(&q, 5);
     int nParent = db_column_int(&q, 6);
+    time_t ts;
 
-    zDate = cgi_rfc822_datestamp(rss_datetime_to_time_t(zDate));
+    ts = (time_t)((db_column_double(&q,2) - 2440587.5)*86400.0);
+    zDate = cgi_rfc822_datestamp(ts);
 
     if( nParent>1 && nChild>1 ){
       zPrefix = "*MERGE/FORK* ";
     }else if( nParent>1 ){
       zPrefix = "*MERGE* ";
@@ -121,10 +109,11 @@
     @       <description>%s(zPrefix)%s(zCom)</description>
     @       <pubDate>%s(zDate)</pubDate>
     @       <author>%s(zAuthor)</author>
     @       <guid>%s(g.zBaseURL)/ci/%s(zId)</guid>
     @     </item>
+    free(zDate);
     nLine++;
   }
 
   db_finalize(&q);
   @   </channel>

Modified src/setup.c from [41b6907a33] to [800f3104a6].

@@ -269,10 +269,11 @@
   ** modified user record.  After writing the user record, redirect
   ** to the page that displays a list of users.
   */
   doWrite = cgi_all("login","info","pw") && !higherUser;
   if( doWrite ){
+    char const * anonLoginCheckedbox = PD("anonymousEnableAutofill",0);
     char zCap[50];
     int i = 0;
     int aa = P("aa")!=0;
     int ad = P("ad")!=0;
     int ae = P("ae")!=0;
@@ -327,20 +328,26 @@
     ){
       style_header("User Creation Error");
       @ <font color="red">Login "%h(zLogin)" is already used by a different
       @ user.</font>
       @
-      @ <p><a href="setup_uedit?id=%d(uid))>[Bummer]</a></p>
+      @ <p><a href="setup_uedit?id=%d(uid)">[Bummer]</a></p>
       style_footer();
       return;
     }
     login_verify_csrf_secret();
     db_multi_exec(
        "REPLACE INTO user(uid,login,info,pw,cap) "
        "VALUES(nullif(%d,0),%Q,%Q,%Q,'%s')",
       uid, P("login"), P("info"), zPw, zCap
     );
+    if( anonLoginCheckedbox && (*anonLoginCheckedbox) ){
+      db_set( "anon-login-enable-captcha-filler", "on", 0 );
+    }
+    else{
+      db_set( "anon-login-enable-captcha-filler", "off", 0 );
+    }
     cgi_redirect("setup_ulist");
     return;
   }
 
   /* Load the existing information about the user, if any
@@ -445,39 +452,43 @@
   @ <tr>
   @   <td align="right" valign="top">Capabilities:</td>
   @   <td>
 #define B(x) inherit[x]
   if( g.okSetup ){
-    @    <input type="checkbox" name="as"%s(oas)>%s(B('s'))Setup</input><br>
-  }
-  @    <input type="checkbox" name="aa"%s(oaa)>%s(B('a'))Admin</input><br>
-  @    <input type="checkbox" name="ad"%s(oad)>%s(B('d'))Delete</input><br>
-  @    <input type="checkbox" name="ae"%s(oae)>%s(B('e'))Email</input><br>
-  @    <input type="checkbox" name="ap"%s(oap)>%s(B('p'))Password</input><br>
-  @    <input type="checkbox" name="ai"%s(oai)>%s(B('i'))Check-In</input><br>
-  @    <input type="checkbox" name="ao"%s(oao)>%s(B('o'))Check-Out</input><br>
-  @    <input type="checkbox" name="ah"%s(oah)>%s(B('h'))History</input><br>
-  @    <input type="checkbox" name="au"%s(oau)>%s(B('u'))Reader</input><br>
-  @    <input type="checkbox" name="av"%s(oav)>%s(B('v'))Developer</input><br>
-  @    <input type="checkbox" name="ag"%s(oag)>%s(B('g'))Clone</input><br>
-  @    <input type="checkbox" name="aj"%s(oaj)>%s(B('j'))Read Wiki</input><br>
-  @    <input type="checkbox" name="af"%s(oaf)>%s(B('f'))New Wiki</input><br>
-  @    <input type="checkbox" name="am"%s(oam)>%s(B('m'))Append Wiki</input><br>
-  @    <input type="checkbox" name="ak"%s(oak)>%s(B('k'))Write Wiki</input><br>
-  @    <input type="checkbox" name="ar"%s(oar)>%s(B('r'))Read Tkt</input><br>
-  @    <input type="checkbox" name="an"%s(oan)>%s(B('n'))New Tkt</input><br>
-  @    <input type="checkbox" name="ac"%s(oac)>%s(B('c'))Append Tkt</input><br>
-  @    <input type="checkbox" name="aw"%s(oaw)>%s(B('w'))Write Tkt</input><br>
-  @    <input type="checkbox" name="at"%s(oat)>%s(B('t'))Tkt Report</input><br>
-  @    <input type="checkbox" name="az"%s(oaz)>%s(B('z'))Download Zip</input>
+    @    <input type="checkbox" name="as"%s(oas)/>%s(B('s'))Setup<br>
+  }
+  @    <input type="checkbox" name="aa"%s(oaa)/>%s(B('a'))Admin<br>
+  @    <input type="checkbox" name="ad"%s(oad)/>%s(B('d'))Delete<br>
+  @    <input type="checkbox" name="ae"%s(oae)/>%s(B('e'))Email<br>
+  @    <input type="checkbox" name="ap"%s(oap)/>%s(B('p'))Password<br>
+  @    <input type="checkbox" name="ai"%s(oai)/>%s(B('i'))Check-In<br>
+  @    <input type="checkbox" name="ao"%s(oao)/>%s(B('o'))Check-Out<br>
+  @    <input type="checkbox" name="ah"%s(oah)/>%s(B('h'))History<br>
+  @    <input type="checkbox" name="au"%s(oau)/>%s(B('u'))Reader<br>
+  @    <input type="checkbox" name="av"%s(oav)/>%s(B('v'))Developer<br>
+  @    <input type="checkbox" name="ag"%s(oag)/>%s(B('g'))Clone<br>
+  @    <input type="checkbox" name="aj"%s(oaj)/>%s(B('j'))Read Wiki<br>
+  @    <input type="checkbox" name="af"%s(oaf)/>%s(B('f'))New Wiki<br>
+  @    <input type="checkbox" name="am"%s(oam)/>%s(B('m'))Append Wiki<br>
+  @    <input type="checkbox" name="ak"%s(oak)/>%s(B('k'))Write Wiki<br>
+  @    <input type="checkbox" name="ar"%s(oar)/>%s(B('r'))Read Tkt<br>
+  @    <input type="checkbox" name="an"%s(oan)/>%s(B('n'))New Tkt<br>
+  @    <input type="checkbox" name="ac"%s(oac)/>%s(B('c'))Append Tkt<br>
+  @    <input type="checkbox" name="aw"%s(oaw)/>%s(B('w'))Write Tkt<br>
+  @    <input type="checkbox" name="at"%s(oat)/>%s(B('t'))Tkt Report<br>
+  @    <input type="checkbox" name="az"%s(oaz)/>%s(B('z'))Download Zip
   @   </td>
   @ </tr>
   @ <tr>
   @   <td align="right">Password:</td>
   if( strcmp(zLogin, "anonymous")==0 ){
+    int enabled = db_get_boolean( "anon-login-enable-captcha-filler", 0 );
+    char const * checked = enabled ? "checked=\"checked\"" : "";
     /* User the password for "anonymous" as cleartext */
-    @   <td><input type="text" name="pw" value="%h(zPw)"></td>
+    @   <td><input type="text" name="pw" value="%h(zPw)"/>
+    @   <br/>Enable password-filler button for anonymous login? <input type="checkbox" name="anonymousEnableAutofill" %s(checked)/><br/>
+    @   </td>
   }else if( zPw[0] ){
     /* Obscure the password for all other users */
     @   <td><input type="password" name="pw" value="**********"></td>
   }else{
     /* Show an empty password as an empty input field */
@@ -570,11 +581,11 @@
   @ </p></li>
   @
   @ <li><p>
   @ Users with the <b>Password</b> privilege are allowed to change their
   @ own password.  Recommended ON for most users but OFF for special
-  @ users "developer, "anonymous", and "nobody".
+  @ users "developer", "anonymous", and "nobody".
   @ </p></li>
   @
   @ <li><p>
   @ The <b>EMail</b> privilege allows the display of sensitive information
   @ such as the email address of users and contact information on tickets.
@@ -618,11 +629,11 @@
   @ with check-in privileges.  When adding new trusted users, simply
   @ select the <b>Developer</b> privilege to cause the new user to inherit
   @ all privileges of the "developer" user.  Similarly, the "<b>reader</b>"
   @ user is a template for users who are allowed more access than anonymous,
   @ but less than a developer.
-  @ </li></p>
+  @ </p></li>
   @ </ul>
   @ </form>
   style_footer();
 }
 

Modified src/sqlite3.c from [9b458b28f3] to [bc27f16f36].

@@ -643,11 +643,11 @@
 **
 ** Requirements: [H10011] [H10014]
 */
 #define SQLITE_VERSION        "3.6.21"
 #define SQLITE_VERSION_NUMBER 3006021
-#define SQLITE_SOURCE_ID      "2009-11-25 21:05:09 5086bf8e838c824accda531afeb56a51dd40d795"
+#define SQLITE_SOURCE_ID      "2009-12-07 16:39:13 1ed88e9d01e9eda5cbc622e7614277f29bcc551c"
 
 /*
 ** CAPI3REF: Run-Time Library Version Numbers {H10020} <S60100>
 ** KEYWORDS: sqlite3_version
 **
@@ -1817,10 +1817,13 @@
 ** the return value of this interface.
 **
 ** For the purposes of this routine, an [INSERT] is considered to
 ** be successful even if it is subsequently rolled back.
 **
+** This function is accessible to SQL statements via the
+** [last_insert_rowid() SQL function].
+**
 ** Requirements:
 ** [H12221] [H12223]
 **
 ** If a separate thread performs a new [INSERT] on the same
 ** database connection while the [sqlite3_last_insert_rowid()]
@@ -1874,12 +1877,12 @@
 ** changes in the most recently completed INSERT, UPDATE, or DELETE
 ** statement within the body of the same trigger.
 ** However, the number returned does not include changes
 ** caused by subtriggers since those have their own context.
 **
-** See also the [sqlite3_total_changes()] interface and the
-** [count_changes pragma].
+** See also the [sqlite3_total_changes()] interface, the
+** [count_changes pragma], and the [changes() SQL function].
 **
 ** Requirements:
 ** [H12241] [H12243]
 **
 ** If a separate thread makes changes on the same database connection
@@ -1902,12 +1905,12 @@
 ** are counted.
 ** The changes are counted as soon as the statement that makes them is
 ** completed (when the statement handle is passed to [sqlite3_reset()] or
 ** [sqlite3_finalize()]).
 **
-** See also the [sqlite3_changes()] interface and the
-** [count_changes pragma].
+** See also the [sqlite3_changes()] interface, the
+** [count_changes pragma], and the [total_changes() SQL function].
 **
 ** Requirements:
 ** [H12261] [H12263]
 **
 ** If a separate thread makes changes on the same database connection
@@ -4664,10 +4667,12 @@
 **          should free this memory by calling [sqlite3_free()].
 **
 ** {H12606} Extension loading must be enabled using
 **          [sqlite3_enable_load_extension()] prior to calling this API,
 **          otherwise an error will be returned.
+**
+** See also the [load_extension() SQL function].
 */
 SQLITE_API int sqlite3_load_extension(
   sqlite3 *db,          /* Load the extension into this database connection */
   const char *zFile,    /* Name of the shared library containing extension */
   const char *zProc,    /* Entry point.  Derived from zFile if 0 */
@@ -6750,13 +6755,23 @@
 ** Round down to the nearest multiple of 8
 */
 #define ROUNDDOWN8(x) ((x)&~7)
 
 /*
-** Assert that the pointer X is aligned to an 8-byte boundary.
-*/
-#define EIGHT_BYTE_ALIGNMENT(X)   ((((char*)(X) - (char*)0)&7)==0)
+** Assert that the pointer X is aligned to an 8-byte boundary.  This
+** macro is used only within assert() to verify that the code gets
+** all alignment restrictions correct.
+**
+** Except, if SQLITE_4_BYTE_ALIGNED_MALLOC is defined, then the
+** underlying malloc() implemention might return us 4-byte aligned
+** pointers.  In that case, only verify 4-byte alignment.
+*/
+#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC
+# define EIGHT_BYTE_ALIGNMENT(X)   ((((char*)(X) - (char*)0)&3)==0)
+#else
+# define EIGHT_BYTE_ALIGNMENT(X)   ((((char*)(X) - (char*)0)&7)==0)
+#endif
 
 
 /*
 ** An instance of the following structure is used to store the busy-handler
 ** callback for a given sqlite handle.
@@ -9662,19 +9677,20 @@
 ** TriggerPrg.pTrigger, assuming a default ON CONFLICT clause of
 ** TriggerPrg.orconf, is stored in the TriggerPrg.pProgram variable.
 ** The Parse.pTriggerPrg list never contains two entries with the same
 ** values for both pTrigger and orconf.
 **
-** The TriggerPrg.oldmask variable is set to a mask of old.* columns
+** The TriggerPrg.aColmask[0] variable is set to a mask of old.* columns
 ** accessed (or set to 0 for triggers fired as a result of INSERT
-** statements).
+** statements). Similarly, the TriggerPrg.aColmask[1] variable is set to
+** a mask of new.* columns used by the program.
 */
 struct TriggerPrg {
   Trigger *pTrigger;      /* Trigger this program was coded from */
   int orconf;             /* Default ON CONFLICT policy */
   SubProgram *pProgram;   /* Program implementing pTrigger/orconf */
-  u32 oldmask;            /* Mask of old.* columns accessed */
+  u32 aColmask[2];        /* Masks of old.*, new.* columns accessed */
   TriggerPrg *pNext;      /* Next entry in Parse.pTriggerPrg list */
 };
 
 /*
 ** An SQL parser context.  A copy of this structure is passed through
@@ -9742,10 +9758,11 @@
 
   /* Information used while coding trigger programs. */
   Parse *pToplevel;    /* Parse structure for main program (or NULL) */
   Table *pTriggerTab;  /* Table triggers are being coded for */
   u32 oldmask;         /* Mask of old.* columns referenced */
+  u32 newmask;         /* Mask of new.* columns referenced */
   u8 eTriggerOp;       /* TK_UPDATE, TK_INSERT or TK_DELETE */
   u8 eOrconf;          /* Default ON CONFLICT policy for trigger steps */
   u8 disableTriggers;  /* True to disable triggers */
 
   /* Above is constant between recursions.  Below is reset before and after
@@ -10339,11 +10356,11 @@
                                         ExprList*,Select*,u8);
 SQLITE_PRIVATE   TriggerStep *sqlite3TriggerUpdateStep(sqlite3*,Token*,ExprList*, Expr*, u8);
 SQLITE_PRIVATE   TriggerStep *sqlite3TriggerDeleteStep(sqlite3*,Token*, Expr*);
 SQLITE_PRIVATE   void sqlite3DeleteTrigger(sqlite3*, Trigger*);
 SQLITE_PRIVATE   void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*);
-SQLITE_PRIVATE   u32 sqlite3TriggerOldmask(Parse*,Trigger*,ExprList*,Table*,int);
+SQLITE_PRIVATE   u32 sqlite3TriggerColmask(Parse*,Trigger*,ExprList*,int,int,Table*,int);
 # define sqlite3ParseToplevel(p) ((p)->pToplevel ? (p)->pToplevel : (p))
 #else
 # define sqlite3TriggersExist(B,C,D,E,F) 0
 # define sqlite3DeleteTrigger(A,B)
 # define sqlite3DropTriggerPtr(A,B)
@@ -10350,11 +10367,11 @@
 # define sqlite3UnlinkAndDeleteTrigger(A,B,C)
 # define sqlite3CodeRowTrigger(A,B,C,D,E,F,G,H,I)
 # define sqlite3CodeRowTriggerDirect(A,B,C,D,E,F)
 # define sqlite3TriggerList(X, Y) 0
 # define sqlite3ParseToplevel(p) p
-# define sqlite3TriggerOldmask(A,B,C,D,E) 0
+# define sqlite3TriggerColmask(A,B,C,D,E,F,G) 0
 #endif
 
 SQLITE_PRIVATE int sqlite3JoinType(Parse*, Token*, Token*, Token*);
 SQLITE_PRIVATE void sqlite3CreateForeignKey(Parse*, ExprList*, Token*, ExprList*, int);
 SQLITE_PRIVATE void sqlite3DeferForeignKey(Parse*, int);
@@ -10564,10 +10581,11 @@
 SQLITE_PRIVATE int sqlite3VtabCallConnect(Parse*, Table*);
 SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3*, int, const char *);
 SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *, VTable *);
 SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction(sqlite3 *,FuncDef*, int nArg, Expr*);
 SQLITE_PRIVATE void sqlite3InvalidFunction(sqlite3_context*,int,sqlite3_value**);
+SQLITE_PRIVATE int sqlite3VdbeParameterIndex(Vdbe*, const char*, int);
 SQLITE_PRIVATE int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *);
 SQLITE_PRIVATE int sqlite3Reprepare(Vdbe*);
 SQLITE_PRIVATE void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*);
 SQLITE_PRIVATE CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *);
 SQLITE_PRIVATE int sqlite3TempInMemory(const sqlite3*);
@@ -14914,10 +14932,43 @@
   assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );
   DosCloseMutexSem( p->mutex );
   sqlite3_free( p );
 }
 
+#ifdef SQLITE_DEBUG
+/*
+** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
+** intended for use inside assert() statements.
+*/
+static int os2MutexHeld(sqlite3_mutex *p){
+  TID tid;
+  PID pid;
+  ULONG ulCount;
+  PTIB ptib;
+  if( p!=0 ) {
+    DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
+  } else {
+    DosGetInfoBlocks(&ptib, NULL);
+    tid = ptib->tib_ptib2->tib2_ultid;
+  }
+  return p==0 || (p->nRef!=0 && p->owner==tid);
+}
+static int os2MutexNotheld(sqlite3_mutex *p){
+  TID tid;
+  PID pid;
+  ULONG ulCount;
+  PTIB ptib;
+  if( p!= 0 ) {
+    DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
+  } else {
+    DosGetInfoBlocks(&ptib, NULL);
+    tid = ptib->tib_ptib2->tib2_ultid;
+  }
+  return p==0 || p->nRef==0 || p->owner!=tid;
+}
+#endif
+
 /*
 ** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt
 ** to enter a mutex.  If another thread is already within the mutex,
 ** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return
 ** SQLITE_BUSY.  The sqlite3_mutex_try() interface returns SQLITE_OK
@@ -14973,43 +15024,10 @@
   assert( p->owner==tid );
   p->nRef--;
   assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
   DosReleaseMutexSem(p->mutex);
 }
-
-#ifdef SQLITE_DEBUG
-/*
-** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
-** intended for use inside assert() statements.
-*/
-static int os2MutexHeld(sqlite3_mutex *p){
-  TID tid;
-  PID pid;
-  ULONG ulCount;
-  PTIB ptib;
-  if( p!=0 ) {
-    DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
-  } else {
-    DosGetInfoBlocks(&ptib, NULL);
-    tid = ptib->tib_ptib2->tib2_ultid;
-  }
-  return p==0 || (p->nRef!=0 && p->owner==tid);
-}
-static int os2MutexNotheld(sqlite3_mutex *p){
-  TID tid;
-  PID pid;
-  ULONG ulCount;
-  PTIB ptib;
-  if( p!= 0 ) {
-    DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
-  } else {
-    DosGetInfoBlocks(&ptib, NULL);
-    tid = ptib->tib_ptib2->tib2_ultid;
-  }
-  return p==0 || p->nRef==0 || p->owner!=tid;
-}
-#endif
 
 SQLITE_PRIVATE sqlite3_mutex_methods *sqlite3DefaultMutex(void){
   static sqlite3_mutex_methods sMutex = {
     os2MutexInit,
     os2MutexEnd,
@@ -22701,11 +22719,11 @@
     }
   }
 #endif
 
   unixLeaveMutex();
-  OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved);
+  OSTRACE4("TEST WR-LOCK %d %d %d (unix)\n", pFile->h, rc, reserved);
 
   *pResOut = reserved;
   return rc;
 }
 
@@ -22834,20 +22852,20 @@
   struct flock lock;
   int s = 0;
   int tErrno;
 
   assert( pFile );
-  OSTRACE7("LOCK    %d %s was %s(%s,%d) pid=%d\n", pFile->h,
+  OSTRACE7("LOCK    %d %s was %s(%s,%d) pid=%d (unix)\n", pFile->h,
       locktypeName(locktype), locktypeName(pFile->locktype),
       locktypeName(pLock->locktype), pLock->cnt , getpid());
 
   /* If there is already a lock of this type or more restrictive on the
   ** unixFile, do nothing. Don't use the end_lock: exit path, as
   ** unixEnterMutex() hasn't been called yet.
   */
   if( pFile->locktype>=locktype ){
-    OSTRACE3("LOCK    %d %s ok (already held)\n", pFile->h,
+    OSTRACE3("LOCK    %d %s ok (already held) (unix)\n", pFile->h,
             locktypeName(locktype));
     return SQLITE_OK;
   }
 
   /* Make sure the locking sequence is correct.
@@ -23013,11 +23031,11 @@
     pLock->locktype = PENDING_LOCK;
   }
 
 end_lock:
   unixLeaveMutex();
-  OSTRACE4("LOCK    %d %s %s\n", pFile->h, locktypeName(locktype),
+  OSTRACE4("LOCK    %d %s %s (unix)\n", pFile->h, locktypeName(locktype),
       rc==SQLITE_OK ? "ok" : "failed");
   return rc;
 }
 
 /*
@@ -23077,11 +23095,11 @@
   int rc = SQLITE_OK;              /* Return code from this interface */
   int h;                           /* The underlying file descriptor */
   int tErrno;                      /* Error code from system call errors */
 
   assert( pFile );
-  OSTRACE7("UNLOCK  %d %d was %d(%d,%d) pid=%d\n", pFile->h, locktype,
+  OSTRACE7("UNLOCK  %d %d was %d(%d,%d) pid=%d (unix)\n", pFile->h, locktype,
       pFile->locktype, pFile->pLock->locktype, pFile->pLock->cnt, getpid());
 
   assert( locktype<=SHARED_LOCK );
   if( pFile->locktype<=locktype ){
     return SQLITE_OK;
@@ -23358,11 +23376,11 @@
   }else{
     /* The lock is held if and only if the lockfile exists */
     const char *zLockFile = (const char*)pFile->lockingContext;
     reserved = access(zLockFile, 0)==0;
   }
-  OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved);
+  OSTRACE4("TEST WR-LOCK %d %d %d (dotlock)\n", pFile->h, rc, reserved);
   *pResOut = reserved;
   return rc;
 }
 
 /*
@@ -23448,11 +23466,11 @@
 static int dotlockUnlock(sqlite3_file *id, int locktype) {
   unixFile *pFile = (unixFile*)id;
   char *zLockFile = (char *)pFile->lockingContext;
 
   assert( pFile );
-  OSTRACE5("UNLOCK  %d %d was %d pid=%d\n", pFile->h, locktype,
+  OSTRACE5("UNLOCK  %d %d was %d pid=%d (dotlock)\n", pFile->h, locktype,
 	   pFile->locktype, getpid());
   assert( locktype<=SHARED_LOCK );
 
   /* no-op if possible */
   if( pFile->locktype==locktype ){
@@ -23562,11 +23580,11 @@
         pFile->lastErrno = tErrno;
         rc = lrc;
       }
     }
   }
-  OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved);
+  OSTRACE4("TEST WR-LOCK %d %d %d (flock)\n", pFile->h, rc, reserved);
 
 #ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS
   if( (rc & SQLITE_IOERR) == SQLITE_IOERR ){
     rc = SQLITE_OK;
     reserved=1;
@@ -23629,11 +23647,11 @@
     }
   } else {
     /* got it, set the type and return ok */
     pFile->locktype = locktype;
   }
-  OSTRACE4("LOCK    %d %s %s\n", pFile->h, locktypeName(locktype),
+  OSTRACE4("LOCK    %d %s %s (flock)\n", pFile->h, locktypeName(locktype),
            rc==SQLITE_OK ? "ok" : "failed");
 #ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS
   if( (rc & SQLITE_IOERR) == SQLITE_IOERR ){
     rc = SQLITE_BUSY;
   }
@@ -23651,11 +23669,11 @@
 */
 static int flockUnlock(sqlite3_file *id, int locktype) {
   unixFile *pFile = (unixFile*)id;
 
   assert( pFile );
-  OSTRACE5("UNLOCK  %d %d was %d pid=%d\n", pFile->h, locktype,
+  OSTRACE5("UNLOCK  %d %d was %d pid=%d (flock)\n", pFile->h, locktype,
            pFile->locktype, getpid());
   assert( locktype<=SHARED_LOCK );
 
   /* no-op if possible */
   if( pFile->locktype==locktype ){
@@ -23753,11 +23771,11 @@
     }else{
       /* we could have it if we want it */
       sem_post(pSem);
     }
   }
-  OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved);
+  OSTRACE4("TEST WR-LOCK %d %d %d (sem)\n", pFile->h, rc, reserved);
 
   *pResOut = reserved;
   return rc;
 }
 
@@ -23828,11 +23846,11 @@
   unixFile *pFile = (unixFile*)id;
   sem_t *pSem = pFile->pOpen->pSem;
 
   assert( pFile );
   assert( pSem );
-  OSTRACE5("UNLOCK  %d %d was %d pid=%d\n", pFile->h, locktype,
+  OSTRACE5("UNLOCK  %d %d was %d pid=%d (sem)\n", pFile->h, locktype,
 	   pFile->locktype, getpid());
   assert( locktype<=SHARED_LOCK );
 
   /* no-op if possible */
   if( pFile->locktype==locktype ){
@@ -23998,11 +24016,11 @@
     if( IS_LOCK_ERROR(lrc) ){
       rc=lrc;
     }
   }
 
-  OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved);
+  OSTRACE4("TEST WR-LOCK %d %d %d (afp)\n", pFile->h, rc, reserved);
 
   *pResOut = reserved;
   return rc;
 }
 
@@ -24034,19 +24052,19 @@
   int rc = SQLITE_OK;
   unixFile *pFile = (unixFile*)id;
   afpLockingContext *context = (afpLockingContext *) pFile->lockingContext;
 
   assert( pFile );
-  OSTRACE5("LOCK    %d %s was %s pid=%d\n", pFile->h,
+  OSTRACE5("LOCK    %d %s was %s pid=%d (afp)\n", pFile->h,
          locktypeName(locktype), locktypeName(pFile->locktype), getpid());
 
   /* If there is already a lock of this type or more restrictive on the
   ** unixFile, do nothing. Don't use the afp_end_lock: exit path, as
   ** unixEnterMutex() hasn't been called yet.
   */
   if( pFile->locktype>=locktype ){
-    OSTRACE3("LOCK    %d %s ok (already held)\n", pFile->h,
+    OSTRACE3("LOCK    %d %s ok (already held) (afp)\n", pFile->h,
            locktypeName(locktype));
     return SQLITE_OK;
   }
 
   /* Make sure the locking sequence is correct
@@ -24161,11 +24179,11 @@
     pFile->locktype = PENDING_LOCK;
   }
 
 afp_end_lock:
   unixLeaveMutex();
-  OSTRACE4("LOCK    %d %s %s\n", pFile->h, locktypeName(locktype),
+  OSTRACE4("LOCK    %d %s %s (afp)\n", pFile->h, locktypeName(locktype),
          rc==SQLITE_OK ? "ok" : "failed");
   return rc;
 }
 
 /*
@@ -24179,11 +24197,11 @@
   int rc = SQLITE_OK;
   unixFile *pFile = (unixFile*)id;
   afpLockingContext *pCtx = (afpLockingContext *) pFile->lockingContext;
 
   assert( pFile );
-  OSTRACE5("UNLOCK  %d %d was %d pid=%d\n", pFile->h, locktype,
+  OSTRACE5("UNLOCK  %d %d was %d pid=%d (afp)\n", pFile->h, locktype,
          pFile->locktype, getpid());
 
   assert( locktype<=SHARED_LOCK );
   if( pFile->locktype<=locktype ){
     return SQLITE_OK;
@@ -32430,10 +32448,11 @@
       pager_reset(pPager);
     }
 
     pPager->changeCountDone = 0;
     pPager->state = PAGER_UNLOCK;
+    pPager->dbModified = 0;
   }
 }
 
 /*
 ** This function should be called when an IOERR, CORRUPT or FULL error
@@ -33806,12 +33825,15 @@
   /* The OS lock values must be the same as the Pager lock values */
   assert( PAGER_SHARED==SHARED_LOCK );
   assert( PAGER_RESERVED==RESERVED_LOCK );
   assert( PAGER_EXCLUSIVE==EXCLUSIVE_LOCK );
 
-  /* If the file is currently unlocked then the size must be unknown */
+  /* If the file is currently unlocked then the size must be unknown. It
+  ** must not have been modified at this point.
+  */
   assert( pPager->state>=PAGER_SHARED || pPager->dbSizeValid==0 );
+  assert( pPager->state>=PAGER_SHARED || pPager->dbModified==0 );
 
   /* Check that this is either a no-op (because the requested lock is
   ** already held, or one of the transistions that the busy-handler
   ** may be invoked during, according to the comment above
   ** sqlite3PagerSetBusyhandler().
@@ -38763,18 +38785,20 @@
   u8 * const data = pPage->aData;      /* Local cache of pPage->aData */
   int nFrag;                           /* Number of fragmented bytes on pPage */
   int top;                             /* First byte of cell content area */
   int gap;        /* First byte of gap between cell pointers and cell content */
   int rc;         /* Integer return code */
+  int usableSize; /* Usable size of the page */
 
   assert( sqlite3PagerIswriteable(pPage->pDbPage) );
   assert( pPage->pBt );
   assert( sqlite3_mutex_held(pPage->pBt->mutex) );
   assert( nByte>=0 );  /* Minimum cell size is 4 */
   assert( pPage->nFree>=nByte );
   assert( pPage->nOverflow==0 );
-  assert( nByte<pPage->pBt->usableSize-8 );
+  usableSize = pPage->pBt->usableSize;
+  assert( nByte < usableSize-8 );
 
   nFrag = data[hdr+7];
   assert( pPage->cellOffset == hdr + 12 - 4*pPage->leaf );
   gap = pPage->cellOffset + 2*pPage->nCell;
   top = get2byte(&data[hdr+5]);
@@ -38793,20 +38817,26 @@
     ** the request. The allocation is made from the first free slot in
     ** the list that is large enough to accomadate it.
     */
     int pc, addr;
     for(addr=hdr+1; (pc = get2byte(&data[addr]))>0; addr=pc){
-      int size = get2byte(&data[pc+2]);     /* Size of free slot */
+      int size;            /* Size of the free slot */
+      if( pc>usableSize-4 || pc<addr+4 ){
+        return SQLITE_CORRUPT_BKPT;
+      }
+      size = get2byte(&data[pc+2]);
       if( size>=nByte ){
         int x = size - nByte;
         testcase( x==4 );
         testcase( x==3 );
         if( x<4 ){
           /* Remove the slot from the free-list. Update the number of
           ** fragmented bytes within the page. */
           memcpy(&data[addr], &data[pc], 2);
           data[hdr+7] = (u8)(nFrag + x);
+        }else if( size+pc > usableSize ){
+          return SQLITE_CORRUPT_BKPT;
         }else{
           /* The slot remains on the free-list. Reduce its size to account
           ** for the portion used by the new allocation. */
           put2byte(&data[pc+2], x);
         }
@@ -39226,11 +39256,10 @@
 ** Release a MemPage.  This should be called once for each prior
 ** call to btreeGetPage.
 */
 static void releasePage(MemPage *pPage){
   if( pPage ){
-    assert( pPage->nOverflow==0 || sqlite3PagerPageRefcount(pPage->pDbPage)>1 );
     assert( pPage->aData );
     assert( pPage->pBt );
     assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage );
     assert( sqlite3PagerGetData(pPage->pDbPage)==pPage->aData );
     assert( sqlite3_mutex_held(pPage->pBt->mutex) );
@@ -39966,15 +39995,12 @@
   unsigned char *data;
   int rc;
   int nPage;
 
   assert( sqlite3_mutex_held(pBt->mutex) );
-  /* The database size has already been measured and cached, so failure
-  ** is impossible here.  If the original size measurement failed, then
-  ** processing aborts before entering this routine. */
   rc = sqlite3PagerPagecount(pBt->pPager, &nPage);
-  if( NEVER(rc!=SQLITE_OK) || nPage>0 ){
+  if( rc!=SQLITE_OK || nPage>0 ){
     return rc;
   }
   pP1 = pBt->pPage1;
   assert( pP1!=0 );
   data = pP1->aData;
@@ -42918,12 +42944,17 @@
   if( *pRC ) return;
 
   assert( i>=0 && i<=pPage->nCell+pPage->nOverflow );
   assert( pPage->nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=5460 );
   assert( pPage->nOverflow<=ArraySize(pPage->aOvfl) );
-  assert( sz==cellSizePtr(pPage, pCell) );
   assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+  /* The cell should normally be sized correctly.  However, when moving a
+  ** malformed cell from a leaf page to an interior page, if the cell size
+  ** wanted to be less than 4 but got rounded up to 4 on the leaf, then size
+  ** might be less than 8 (leaf-size + pointer) on the interior node.  Hence
+  ** the term after the || in the following assert(). */
+  assert( sz==cellSizePtr(pPage, pCell) || (sz==8 && iChild>0) );
   if( pPage->nOverflow || sz+2>pPage->nFree ){
     if( pTemp ){
       memcpy(pTemp+nSkip, pCell+nSkip, sz-nSkip);
       pCell = pTemp;
     }
@@ -43198,11 +43229,11 @@
     BtShared * const pBt = pFrom->pBt;
     u8 * const aFrom = pFrom->aData;
     u8 * const aTo = pTo->aData;
     int const iFromHdr = pFrom->hdrOffset;
     int const iToHdr = ((pTo->pgno==1) ? 100 : 0);
-    TESTONLY(int rc;)
+    int rc;
     int iData;
 
 
     assert( pFrom->isInit );
     assert( pFrom->nFree>=iToHdr );
@@ -43212,15 +43243,20 @@
     iData = get2byte(&aFrom[iFromHdr+5]);
     memcpy(&aTo[iData], &aFrom[iData], pBt->usableSize-iData);
     memcpy(&aTo[iToHdr], &aFrom[iFromHdr], pFrom->cellOffset + 2*pFrom->nCell);
 
     /* Reinitialize page pTo so that the contents of the MemPage structure
-    ** match the new data. The initialization of pTo "cannot" fail, as the
-    ** data copied from pFrom is known to be valid.  */
+    ** match the new data. The initialization of pTo can actually fail under
+    ** fairly obscure circumstances, even though it is a copy of initialized
+    ** page pFrom.
+    */
     pTo->isInit = 0;
-    TESTONLY(rc = ) btreeInitPage(pTo);
-    assert( rc==SQLITE_OK );
+    rc = btreeInitPage(pTo);
+    if( rc!=SQLITE_OK ){
+      *pRC = rc;
+      return;
+    }
 
     /* If this is an auto-vacuum database, update the pointer-map entries
     ** for any b-tree or overflow pages that pTo now contains the pointers to.
     */
     if( ISAUTOVACUUM ){
@@ -51381,26 +51417,28 @@
 /*
 ** Given a wildcard parameter name, return the index of the variable
 ** with that name.  If there is no variable with the given name,
 ** return 0.
 */
-SQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt *pStmt, const char *zName){
-  Vdbe *p = (Vdbe*)pStmt;
+SQLITE_PRIVATE int sqlite3VdbeParameterIndex(Vdbe *p, const char *zName, int nName){
   int i;
   if( p==0 ){
     return 0;
   }
   createVarMap(p);
   if( zName ){
     for(i=0; i<p->nVar; i++){
       const char *z = p->azVar[i];
-      if( z && strcmp(z,zName)==0 ){
+      if( z && memcmp(z,zName,nName)==0 && z[nName]==0 ){
         return i+1;
       }
     }
   }
   return 0;
+}
+SQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt *pStmt, const char *zName){
+  return sqlite3VdbeParameterIndex((Vdbe*)pStmt, zName, sqlite3Strlen30(zName));
 }
 
 /*
 ** Transfer all bindings from the first statement over to the second.
 */
@@ -51509,29 +51547,33 @@
 ** zSql is a zero-terminated string of UTF-8 SQL text.  Return the number of
 ** bytes in this text up to but excluding the first character in
 ** a host parameter.  If the text contains no host parameters, return
 ** the total number of bytes in the text.
 */
-static int findNextHostParameter(const char *zSql){
+static int findNextHostParameter(const char *zSql, int *pnToken){
   int tokenType;
   int nTotal = 0;
   int n;
 
+  *pnToken = 0;
   while( zSql[0] ){
     n = sqlite3GetToken((u8*)zSql, &tokenType);
     assert( n>0 && tokenType!=TK_ILLEGAL );
-    if( tokenType==TK_VARIABLE ) break;
+    if( tokenType==TK_VARIABLE ){
+      *pnToken = n;
+      break;
+    }
     nTotal += n;
     zSql += n;
   }
   return nTotal;
 }
 
 /*
-** Return a pointer to a string in memory obtained form sqlite3Malloc() which
+** Return a pointer to a string in memory obtained form sqlite3DbMalloc() which
 ** holds a copy of zRawSql but with host parameters expanded to their
-** current values.
+** current bindings.
 **
 ** The calling function is responsible for making sure the memory returned
 ** is eventually freed.
 **
 ** ALGORITHM:  Scan the input string looking for host parameters in any of
@@ -51545,60 +51587,46 @@
 SQLITE_PRIVATE char *sqlite3VdbeExpandSql(
   Vdbe *p,                 /* The prepared statement being evaluated */
   const char *zRawSql      /* Raw text of the SQL statement */
 ){
   sqlite3 *db;             /* The database connection */
-  int idx;                 /* Index of a host parameter */
+  int idx = 0;             /* Index of a host parameter */
   int nextIndex = 1;       /* Index of next ? host parameter */
   int n;                   /* Length of a token prefix */
+  int nToken;              /* Length of the parameter token */
   int i;                   /* Loop counter */
-  int dummy;               /* For holding a unused return value */
-  Mem *pVar;               /* Value of a host parameter */
-  VdbeOp *pOp;             /* For looping over opcodes */
+  Mem *pVar;               /* Value of a host parameter */
   StrAccum out;            /* Accumulate the output here */
   char zBase[100];         /* Initial working space */
 
   db = p->db;
   sqlite3StrAccumInit(&out, zBase, sizeof(zBase),
                       db->aLimit[SQLITE_LIMIT_LENGTH]);
   out.db = db;
   while( zRawSql[0] ){
-    n = findNextHostParameter(zRawSql);
+    n = findNextHostParameter(zRawSql, &nToken);
     assert( n>0 );
     sqlite3StrAccumAppend(&out, zRawSql, n);
     zRawSql += n;
-    if( zRawSql[0]==0 ) break;
+    assert( zRawSql[0] || nToken==0 );
+    if( nToken==0 ) break;
     if( zRawSql[0]=='?' ){
-      zRawSql++;
-      if( sqlite3Isdigit(zRawSql[0]) ){
-        idx = 0;
-        while( sqlite3Isdigit(zRawSql[0]) ){
-          idx = idx*10 + zRawSql[0] - '0';
-          zRawSql++;
-        }
+      if( nToken>1 ){
+        assert( sqlite3Isdigit(zRawSql[1]) );
+        sqlite3GetInt32(&zRawSql[1], &idx);
       }else{
         idx = nextIndex;
       }
     }else{
       assert( zRawSql[0]==':' || zRawSql[0]=='$' || zRawSql[0]=='@' );
       testcase( zRawSql[0]==':' );
       testcase( zRawSql[0]=='$' );
       testcase( zRawSql[0]=='@' );
-      n = sqlite3GetToken((u8*)zRawSql, &dummy);
-      idx = 0;
-      for(i=0, pOp=p->aOp; ALWAYS(i<p->nOp); i++, pOp++){
-        if( pOp->opcode!=OP_Variable ) continue;
-        if( pOp->p3>1 ) continue;
-        if( pOp->p4.z==0 ) continue;
-        if( memcmp(pOp->p4.z, zRawSql, n)==0 && pOp->p4.z[n]==0 ){
-          idx = pOp->p1;
-          break;
-        }
-      }
+      idx = sqlite3VdbeParameterIndex(p, zRawSql, nToken);
       assert( idx>0 );
-      zRawSql += n;
-    }
+    }
+    zRawSql += nToken;
     nextIndex = idx + 1;
     assert( idx>0 && idx<=p->nVar );
     pVar = &p->aVar[idx-1];
     if( pVar->flags & MEM_Null ){
       sqlite3StrAccumAppend(&out, "NULL", 4);
@@ -51606,15 +51634,16 @@
       sqlite3XPrintf(&out, "%lld", pVar->u.i);
     }else if( pVar->flags & MEM_Real ){
       sqlite3XPrintf(&out, "%!.15g", pVar->r);
     }else if( pVar->flags & MEM_Str ){
 #ifndef SQLITE_OMIT_UTF16
-      if( ENC(db)!=SQLITE_UTF8 ){
+      u8 enc = ENC(db);
+      if( enc!=SQLITE_UTF8 ){
         Mem utf8;
         memset(&utf8, 0, sizeof(utf8));
         utf8.db = db;
-        sqlite3VdbeMemSetStr(&utf8, pVar->z, pVar->n, ENC(db), SQLITE_STATIC);
+        sqlite3VdbeMemSetStr(&utf8, pVar->z, pVar->n, enc, SQLITE_STATIC);
         sqlite3VdbeChangeEncoding(&utf8, SQLITE_UTF8);
         sqlite3XPrintf(&out, "'%.*q'", utf8.n, utf8.z);
         sqlite3VdbeMemRelease(&utf8);
       }else
 #endif
@@ -52272,11 +52301,11 @@
   int rc = SQLITE_OK;        /* Value to return */
   sqlite3 *db = p->db;       /* The database */
   u8 resetSchemaOnFault = 0; /* Reset schema after an error if true */
   u8 encoding = ENC(db);     /* The database encoding */
 #ifndef SQLITE_OMIT_PROGRESS_CALLBACK
-  u8 checkProgress;          /* True if progress callbacks are enabled */
+  int checkProgress;         /* True if progress callbacks are enabled */
   int nProgressOps = 0;      /* Opcodes executed since progress callback. */
 #endif
   Mem *aMem = p->aMem;       /* Copy of p->aMem */
   Mem *pIn1 = 0;             /* 1st input operand */
   Mem *pIn2 = 0;             /* 2nd input operand */
@@ -55461,11 +55490,11 @@
       **     u.az.r.flags = UNPACKED_INCRKEY;
       **   }else{
       **     u.az.r.flags = 0;
       **   }
       */
-      u.az.r.flags = UNPACKED_INCRKEY * (1 & (u.az.oc - OP_SeekLt));
+      u.az.r.flags = (u16)(UNPACKED_INCRKEY * (1 & (u.az.oc - OP_SeekLt)));
       assert( u.az.oc!=OP_SeekGt || u.az.r.flags==UNPACKED_INCRKEY );
       assert( u.az.oc!=OP_SeekLe || u.az.r.flags==UNPACKED_INCRKEY );
       assert( u.az.oc!=OP_SeekGe || u.az.r.flags==0 );
       assert( u.az.oc!=OP_SeekLt || u.az.r.flags==0 );
 
@@ -55594,11 +55623,11 @@
   if( ALWAYS(u.bb.pC->pCursor!=0) ){
 
     assert( u.bb.pC->isTable==0 );
     if( pOp->p4.i>0 ){
       u.bb.r.pKeyInfo = u.bb.pC->pKeyInfo;
-      u.bb.r.nField = pOp->p4.i;
+      u.bb.r.nField = (u16)pOp->p4.i;
       u.bb.r.aMem = pIn3;
       u.bb.r.flags = UNPACKED_PREFIX_MATCH;
       u.bb.pIdxKey = &u.bb.r;
     }else{
       assert( pIn3->flags & MEM_Blob );
@@ -59309,10 +59338,14 @@
             pExpr->affinity = SQLITE_AFF_INTEGER;
           }else if( pExpr->iTable==0 ){
             testcase( iCol==31 );
             testcase( iCol==32 );
             pParse->oldmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<<iCol));
+          }else{
+            testcase( iCol==31 );
+            testcase( iCol==32 );
+            pParse->newmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<<iCol));
           }
           pExpr->iColumn = (i16)iCol;
           pExpr->pTab = pTab;
           isTrigger = 1;
         }
@@ -70890,11 +70923,13 @@
     u32 mask;                     /* Mask of OLD.* columns in use */
     int iCol;                     /* Iterator used while populating OLD.* */
 
     /* TODO: Could use temporary registers here. Also could attempt to
     ** avoid copying the contents of the rowid register.  */
-    mask = sqlite3TriggerOldmask(pParse, pTrigger, 0, pTab, onconf);
+    mask = sqlite3TriggerColmask(
+        pParse, pTrigger, 0, 0, TRIGGER_BEFORE|TRIGGER_AFTER, pTab, onconf
+    );
     mask |= sqlite3FkOldmask(pParse, pTab);
     iOld = pParse->nMem+1;
     pParse->nMem += (1 + pTab->nCol);
 
     /* Populate the OLD.* pseudo-table register array. These values will be
@@ -84240,11 +84275,12 @@
   pPrg->pProgram = pProgram = sqlite3DbMallocZero(db, sizeof(SubProgram));
   if( !pProgram ) return 0;
   pProgram->nRef = 1;
   pPrg->pTrigger = pTrigger;
   pPrg->orconf = orconf;
-  pPrg->oldmask = 0xffffffff;
+  pPrg->aColmask[0] = 0xffffffff;
+  pPrg->aColmask[1] = 0xffffffff;
 
   /* Allocate and populate a new Parse context to use for coding the
   ** trigger sub-program.  */
   pSubParse = sqlite3StackAllocZero(db, sizeof(Parse));
   if( !pSubParse ) return 0;
@@ -84301,11 +84337,12 @@
       pProgram->aOp = sqlite3VdbeTakeOpArray(v, &pProgram->nOp, &pTop->nMaxArg);
     }
     pProgram->nMem = pSubParse->nMem;
     pProgram->nCsr = pSubParse->nTab;
     pProgram->token = (void *)pTrigger;
-    pPrg->oldmask = pSubParse->oldmask;
+    pPrg->aColmask[0] = pSubParse->oldmask;
+    pPrg->aColmask[1] = pSubParse->newmask;
     sqlite3VdbeDelete(v);
   }
 
   assert( !pSubParse->pAinc       && !pSubParse->pZombieTab );
   assert( !pSubParse->pTriggerPrg && !pSubParse->nMaxArg );
@@ -84461,45 +84498,56 @@
     }
   }
 }
 
 /*
-** Triggers fired by UPDATE or DELETE statements may access values stored
-** in the old.* pseudo-table. This function returns a 32-bit bitmask
-** indicating which columns of the old.* table actually are used by
-** triggers. This information may be used by the caller to avoid having
-** to load the entire old.* record into memory when executing an UPDATE
-** or DELETE command.
+** Triggers may access values stored in the old.* or new.* pseudo-table.
+** This function returns a 32-bit bitmask indicating which columns of the
+** old.* or new.* tables actually are used by triggers. This information
+** may be used by the caller, for example, to avoid having to load the entire
+** old.* record into memory when executing an UPDATE or DELETE command.
 **
 ** Bit 0 of the returned mask is set if the left-most column of the
-** table may be accessed using an old.<col> reference. Bit 1 is set if
+** table may be accessed using an [old|new].<col> reference. Bit 1 is set if
 ** the second leftmost column value is required, and so on. If there
 ** are more than 32 columns in the table, and at least one of the columns
 ** with an index greater than 32 may be accessed, 0xffffffff is returned.
 **
-** It is not possible to determine if the old.rowid column is accessed
-** by triggers. The caller must always assume that it is.
-**
-** There is no equivalent function for new.* references.
-*/
-SQLITE_PRIVATE u32 sqlite3TriggerOldmask(
+** It is not possible to determine if the old.rowid or new.rowid column is
+** accessed by triggers. The caller must always assume that it is.
+**
+** Parameter isNew must be either 1 or 0. If it is 0, then the mask returned
+** applies to the old.* table. If 1, the new.* table.
+**
+** Parameter tr_tm must be a mask with one or both of the TRIGGER_BEFORE
+** and TRIGGER_AFTER bits set. Values accessed by BEFORE triggers are only
+** included in the returned mask if the TRIGGER_BEFORE bit is set in the
+** tr_tm parameter. Similarly, values accessed by AFTER triggers are only
+** included in the returned mask if the TRIGGER_AFTER bit is set in tr_tm.
+*/
+SQLITE_PRIVATE u32 sqlite3TriggerColmask(
   Parse *pParse,       /* Parse context */
   Trigger *pTrigger,   /* List of triggers on table pTab */
   ExprList *pChanges,  /* Changes list for any UPDATE OF triggers */
+  int isNew,           /* 1 for new.* ref mask, 0 for old.* ref mask */
+  int tr_tm,           /* Mask of TRIGGER_BEFORE|TRIGGER_AFTER */
   Table *pTab,         /* The table to code triggers from */
   int orconf           /* Default ON CONFLICT policy for trigger steps */
 ){
   const int op = pChanges ? TK_UPDATE : TK_DELETE;
   u32 mask = 0;
   Trigger *p;
 
+  assert( isNew==1 || isNew==0 );
   for(p=pTrigger; p; p=p->pNext){
-    if( p->op==op && checkColumnOverlap(p->pColumns,pChanges) ){
+    if( p->op==op && (tr_tm&p->tr_tm)
+     && checkColumnOverlap(p->pColumns,pChanges)
+    ){
       TriggerPrg *pPrg;
       pPrg = getRowTrigger(pParse, p, pTab, orconf);
       if( pPrg ){
-        mask |= pPrg->oldmask;
+        mask |= pPrg->aColmask[isNew];
       }
     }
   }
 
   return mask;
@@ -84619,18 +84667,19 @@
   Expr *pRowidExpr = 0;  /* Expression defining the new record number */
   int openAll = 0;       /* True if all indices need to be opened */
   AuthContext sContext;  /* The authorization context */
   NameContext sNC;       /* The name-context to resolve expressions in */
   int iDb;               /* Database containing the table being updated */
-  int j1;                /* Addresses of jump instructions */
   int okOnePass;         /* True for one-pass algorithm without the FIFO */
   int hasFK;             /* True if foreign key processing is required */
 
 #ifndef SQLITE_OMIT_TRIGGER
-  int isView;                  /* Trying to update a view */
-  Trigger *pTrigger;           /* List of triggers on pTab, if required */
-#endif
+  int isView;            /* True when updating a view (INSTEAD OF trigger) */
+  Trigger *pTrigger;     /* List of triggers on pTab, if required */
+  int tmask;             /* Mask of TRIGGER_BEFORE|TRIGGER_AFTER */
+#endif
+  int newmask;           /* Mask of NEW.* columns accessed by BEFORE triggers */
 
   /* Register Allocations */
   int regRowCount = 0;   /* A count of rows changed */
   int regOldRowid;       /* The old rowid */
   int regNewRowid;       /* The new rowid */
@@ -84654,25 +84703,27 @@
 
   /* Figure out if we have any triggers and if the table being
   ** updated is a view.
   */
 #ifndef SQLITE_OMIT_TRIGGER
-  pTrigger = sqlite3TriggersExist(pParse, pTab, TK_UPDATE, pChanges, 0);
+  pTrigger = sqlite3TriggersExist(pParse, pTab, TK_UPDATE, pChanges, &tmask);
   isView = pTab->pSelect!=0;
+  assert( pTrigger || tmask==0 );
 #else
 # define pTrigger 0
 # define isView 0
+# define tmask 0
 #endif
 #ifdef SQLITE_OMIT_VIEW
 # undef isView
 # define isView 0
 #endif
 
   if( sqlite3ViewGetColumnNames(pParse, pTab) ){
     goto update_cleanup;
   }
-  if( sqlite3IsReadOnly(pParse, pTab, (pTrigger?1:0)) ){
+  if( sqlite3IsReadOnly(pParse, pTab, tmask) ){
     goto update_cleanup;
   }
   aXRef = sqlite3DbMallocRaw(db, sizeof(int) * pTab->nCol );
   if( aXRef==0 ) goto update_cleanup;
   for(i=0; i<pTab->nCol; i++) aXRef[i] = -1;
@@ -84896,11 +84947,13 @@
 
   /* If there are triggers on this table, populate an array of registers
   ** with the required old.* column data.  */
   if( hasFK || pTrigger ){
     u32 oldmask = (hasFK ? sqlite3FkOldmask(pParse, pTab) : 0);
-    oldmask |= sqlite3TriggerOldmask(pParse, pTrigger, pChanges, pTab, onError);
+    oldmask |= sqlite3TriggerColmask(pParse,
+        pTrigger, pChanges, 0, TRIGGER_BEFORE|TRIGGER_AFTER, pTab, onError
+    );
     for(i=0; i<pTab->nCol; i++){
       if( aXRef[i]<0 || oldmask==0xffffffff || (oldmask & (1<<i)) ){
         sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regOld+i);
         sqlite3ColumnDefault(v, pTab, i, regOld+i);
       }else{
@@ -84913,42 +84966,76 @@
   }
 
   /* Populate the array of registers beginning at regNew with the new
   ** row data. This array is used to check constaints, create the new
   ** table and index records, and as the values for any new.* references
-  ** made by triggers.  */
+  ** made by triggers.
+  **
+  ** If there are one or more BEFORE triggers, then do not populate the
+  ** registers associated with columns that are (a) not modified by
+  ** this UPDATE statement and (b) not accessed by new.* references. The
+  ** values for registers not modified by the UPDATE must be reloaded from
+  ** the database after the BEFORE triggers are fired anyway (as the trigger
+  ** may have modified them). So not loading those that are not going to
+  ** be used eliminates some redundant opcodes.
+  */
+  newmask = sqlite3TriggerColmask(
+      pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError
+  );
   for(i=0; i<pTab->nCol; i++){
     if( i==pTab->iPKey ){
       sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);
     }else{
       j = aXRef[i];
-      if( j<0 ){
+      if( j>=0 ){
+        sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regNew+i);
+      }else if( 0==(tmask&TRIGGER_BEFORE) || i>31 || (newmask&(1<<i)) ){
+        /* This branch loads the value of a column that will not be changed
+        ** into a register. This is done if there are no BEFORE triggers, or
+        ** if there are one or more BEFORE triggers that use this value via
+        ** a new.* reference in a trigger program.
+        */
+        testcase( i==31 );
+        testcase( i==32 );
         sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew+i);
         sqlite3ColumnDefault(v, pTab, i, regNew+i);
-      }else{
-        sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regNew+i);
       }
     }
   }
 
   /* Fire any BEFORE UPDATE triggers. This happens before constraints are
-  ** verified. One could argue that this is wrong.  */
-  if( pTrigger ){
+  ** verified. One could argue that this is wrong.
+  */
+  if( tmask&TRIGGER_BEFORE ){
     sqlite3VdbeAddOp2(v, OP_Affinity, regNew, pTab->nCol);
     sqlite3TableAffinityStr(v, pTab);
     sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
         TRIGGER_BEFORE, pTab, regOldRowid, onError, addr);
 
     /* The row-trigger may have deleted the row being updated. In this
     ** case, jump to the next row. No updates or AFTER triggers are
     ** required. This behaviour - what happens when the row being updated
     ** is deleted or renamed by a BEFORE trigger - is left undefined in the
-    ** documentation.  */
+    ** documentation.
+    */
     sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid);
+
+    /* If it did not delete it, the row-trigger may still have modified
+    ** some of the columns of the row being updated. Load the values for
+    ** all columns not modified by the update statement into their
+    ** registers in case this has happened.
+    */
+    for(i=0; i<pTab->nCol; i++){
+      if( aXRef[i]<0 && i!=pTab->iPKey ){
+        sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew+i);
+        sqlite3ColumnDefault(v, pTab, i, regNew+i);
+      }
+    }
   }
 
   if( !isView ){
+    int j1;                       /* Address of jump instruction */
 
     /* Do constraint checks. */
     sqlite3GenerateConstraintChecks(pParse, pTab, iCur, regNewRowid,
         aRegIdx, (chngRowid?regOldRowid:0), 1, onError, addr, 0);
 
@@ -90583,11 +90670,11 @@
   /* A routine to convert a binary TK_IS or TK_ISNOT expression into a
   ** unary TK_ISNULL or TK_NOTNULL expression. */
   static void binaryToUnaryIfNull(Parse *pParse, Expr *pY, Expr *pA, int op){
     sqlite3 *db = pParse->db;
     if( db->mallocFailed==0 && pY->op==TK_NULL ){
-      pA->op = op;
+      pA->op = (u8)op;
       sqlite3ExprDelete(db, pA->pRight);
       pA->pRight = 0;
     }
   }
 
@@ -97946,11 +98033,558 @@
 
 #if defined(SQLITE_ENABLE_FTS3) && !defined(SQLITE_CORE)
 # define SQLITE_CORE 1
 #endif
 
-#include "fts3Int.h"
+/************** Include fts3Int.h in the middle of fts3.c ********************/
+/************** Begin file fts3Int.h *****************************************/
+/*
+** 2009 Nov 12
+**
+** The author disclaims copyright to this source code.  In place of
+** a legal notice, here is a blessing:
+**
+**    May you do good and not evil.
+**    May you find forgiveness for yourself and forgive others.
+**    May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+*/
+
+#ifndef _FTSINT_H
+#define _FTSINT_H
+
+#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
+# define NDEBUG 1
+#endif
+
+/************** Include fts3_tokenizer.h in the middle of fts3Int.h **********/
+/************** Begin file fts3_tokenizer.h **********************************/
+/*
+** 2006 July 10
+**
+** The author disclaims copyright to this source code.
+**
+*************************************************************************
+** Defines the interface to tokenizers used by fulltext-search.  There
+** are three basic components:
+**
+** sqlite3_tokenizer_module is a singleton defining the tokenizer
+** interface functions.  This is essentially the class structure for
+** tokenizers.
+**
+** sqlite3_tokenizer is used to define a particular tokenizer, perhaps
+** including customization information defined at creation time.
+**
+** sqlite3_tokenizer_cursor is generated by a tokenizer to generate
+** tokens from a particular input.
+*/
+#ifndef _FTS3_TOKENIZER_H_
+#define _FTS3_TOKENIZER_H_
+
+/* TODO(shess) Only used for SQLITE_OK and SQLITE_DONE at this time.
+** If tokenizers are to be allowed to call sqlite3_*() functions, then
+** we will need a way to register the API consistently.
+*/
+
+/*
+** Structures used by the tokenizer interface. When a new tokenizer
+** implementation is registered, the caller provides a pointer to
+** an sqlite3_tokenizer_module containing pointers to the callback
+** functions that make up an implementation.
+**
+** When an fts3 table is created, it passes any arguments passed to
+** the tokenizer clause of the CREATE VIRTUAL TABLE statement to the
+** sqlite3_tokenizer_module.xCreate() function of the requested tokenizer
+** implementation. The xCreate() function in turn returns an
+** sqlite3_tokenizer structure representing the specific tokenizer to
+** be used for the fts3 table (customized by the tokenizer clause arguments).
+**
+** To tokenize an input buffer, the sqlite3_tokenizer_module.xOpen()
+** method is called. It returns an sqlite3_tokenizer_cursor object
+** that may be used to tokenize a specific input buffer based on
+** the tokenization rules supplied by a specific sqlite3_tokenizer
+** object.
+*/
+typedef struct sqlite3_tokenizer_module sqlite3_tokenizer_module;
+typedef struct sqlite3_tokenizer sqlite3_tokenizer;
+typedef struct sqlite3_tokenizer_cursor sqlite3_tokenizer_cursor;
+
+struct sqlite3_tokenizer_module {
+
+  /*
+  ** Structure version. Should always be set to 0.
+  */
+  int iVersion;
+
+  /*
+  ** Create a new tokenizer. The values in the argv[] array are the
+  ** arguments passed to the "tokenizer" clause of the CREATE VIRTUAL
+  ** TABLE statement that created the fts3 table. For example, if
+  ** the following SQL is executed:
+  **
+  **   CREATE .. USING fts3( ... , tokenizer <tokenizer-name> arg1 arg2)
+  **
+  ** then argc is set to 2, and the argv[] array contains pointers
+  ** to the strings "arg1" and "arg2".
+  **
+  ** This method should return either SQLITE_OK (0), or an SQLite error
+  ** code. If SQLITE_OK is returned, then *ppTokenizer should be set
+  ** to point at the newly created tokenizer structure. The generic
+  ** sqlite3_tokenizer.pModule variable should not be initialised by
+  ** this callback. The caller will do so.
+  */
+  int (*xCreate)(
+    int argc,                           /* Size of argv array */
+    const char *const*argv,             /* Tokenizer argument strings */
+    sqlite3_tokenizer **ppTokenizer     /* OUT: Created tokenizer */
+  );
+
+  /*
+  ** Destroy an existing tokenizer. The fts3 module calls this method
+  ** exactly once for each successful call to xCreate().
+  */
+  int (*xDestroy)(sqlite3_tokenizer *pTokenizer);
+
+  /*
+  ** Create a tokenizer cursor to tokenize an input buffer. The caller
+  ** is responsible for ensuring that the input buffer remains valid
+  ** until the cursor is closed (using the xClose() method).
+  */
+  int (*xOpen)(
+    sqlite3_tokenizer *pTokenizer,       /* Tokenizer object */
+    const char *pInput, int nBytes,      /* Input buffer */
+    sqlite3_tokenizer_cursor **ppCursor  /* OUT: Created tokenizer cursor */
+  );
+
+  /*
+  ** Destroy an existing tokenizer cursor. The fts3 module calls this
+  ** method exactly once for each successful call to xOpen().
+  */
+  int (*xClose)(sqlite3_tokenizer_cursor *pCursor);
+
+  /*
+  ** Retrieve the next token from the tokenizer cursor pCursor. This
+  ** method should either return SQLITE_OK and set the values of the
+  ** "OUT" variables identified below, or SQLITE_DONE to indicate that
+  ** the end of the buffer has been reached, or an SQLite error code.
+  **
+  ** *ppToken should be set to point at a buffer containing the
+  ** normalized version of the token (i.e. after any case-folding and/or
+  ** stemming has been performed). *pnBytes should be set to the length
+  ** of this buffer in bytes. The input text that generated the token is
+  ** identified by the byte offsets returned in *piStartOffset and
+  ** *piEndOffset. *piStartOffset should be set to the index of the first
+  ** byte of the token in the input buffer. *piEndOffset should be set
+  ** to the index of the first byte just past the end of the token in
+  ** the input buffer.
+  **
+  ** The buffer *ppToken is set to point at is managed by the tokenizer
+  ** implementation. It is only required to be valid until the next call
+  ** to xNext() or xClose().
+  */
+  /* TODO(shess) current implementation requires pInput to be
+  ** nul-terminated.  This should either be fixed, or pInput/nBytes
+  ** should be converted to zInput.
+  */
+  int (*xNext)(
+    sqlite3_tokenizer_cursor *pCursor,   /* Tokenizer cursor */
+    const char **ppToken, int *pnBytes,  /* OUT: Normalized text for token */
+    int *piStartOffset,  /* OUT: Byte offset of token in input buffer */
+    int *piEndOffset,    /* OUT: Byte offset of end of token in input buffer */
+    int *piPosition      /* OUT: Number of tokens returned before this one */
+  );
+};
+
+struct sqlite3_tokenizer {
+  const sqlite3_tokenizer_module *pModule;  /* The module for this tokenizer */
+  /* Tokenizer implementations will typically add additional fields */
+};
+
+struct sqlite3_tokenizer_cursor {
+  sqlite3_tokenizer *pTokenizer;       /* Tokenizer for this cursor. */
+  /* Tokenizer implementations will typically add additional fields */
+};
+
+#endif /* _FTS3_TOKENIZER_H_ */
+
+/************** End of fts3_tokenizer.h **************************************/
+/************** Continuing where we left off in fts3Int.h ********************/
+/************** Include fts3_hash.h in the middle of fts3Int.h ***************/
+/************** Begin file fts3_hash.h ***************************************/
+/*
+** 2001 September 22
+**
+** The author disclaims copyright to this source code.  In place of
+** a legal notice, here is a blessing:
+**
+**    May you do good and not evil.
+**    May you find forgiveness for yourself and forgive others.
+**    May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This is the header file for the generic hash-table implemenation
+** used in SQLite.  We've modified it slightly to serve as a standalone
+** hash table implementation for the full-text indexing module.
+**
+*/
+#ifndef _FTS3_HASH_H_
+#define _FTS3_HASH_H_
+
+/* Forward declarations of structures. */
+typedef struct Fts3Hash Fts3Hash;
+typedef struct Fts3HashElem Fts3HashElem;
+
+/* A complete hash table is an instance of the following structure.
+** The internals of this structure are intended to be opaque -- client
+** code should not attempt to access or modify the fields of this structure
+** directly.  Change this structure only by using the routines below.
+** However, many of the "procedures" and "functions" for modifying and
+** accessing this structure are really macros, so we can't really make
+** this structure opaque.
+*/
+struct Fts3Hash {
+  char keyClass;          /* HASH_INT, _POINTER, _STRING, _BINARY */
+  char copyKey;           /* True if copy of key made on insert */
+  int count;              /* Number of entries in this table */
+  Fts3HashElem *first;    /* The first element of the array */
+  int htsize;             /* Number of buckets in the hash table */
+  struct _fts3ht {        /* the hash table */
+    int count;               /* Number of entries with this hash */
+    Fts3HashElem *chain;     /* Pointer to first entry with this hash */
+  } *ht;
+};
+
+/* Each element in the hash table is an instance of the following
+** structure.  All elements are stored on a single doubly-linked list.
+**
+** Again, this structure is intended to be opaque, but it can't really
+** be opaque because it is used by macros.
+*/
+struct Fts3HashElem {
+  Fts3HashElem *next, *prev; /* Next and previous elements in the table */
+  void *data;                /* Data associated with this element */
+  void *pKey; int nKey;      /* Key associated with this element */
+};
+
+/*
+** There are 2 different modes of operation for a hash table:
+**
+**   FTS3_HASH_STRING        pKey points to a string that is nKey bytes long
+**                           (including the null-terminator, if any).  Case
+**                           is respected in comparisons.
+**
+**   FTS3_HASH_BINARY        pKey points to binary data nKey bytes long.
+**                           memcmp() is used to compare keys.
+**
+** A copy of the key is made if the copyKey parameter to fts3HashInit is 1.
+*/
+#define FTS3_HASH_STRING    1
+#define FTS3_HASH_BINARY    2
+
+/*
+** Access routines.  To delete, insert a NULL pointer.
+*/
+SQLITE_PRIVATE void sqlite3Fts3HashInit(Fts3Hash *pNew, char keyClass, char copyKey);
+SQLITE_PRIVATE void *sqlite3Fts3HashInsert(Fts3Hash*, const void *pKey, int nKey, void *pData);
+SQLITE_PRIVATE void *sqlite3Fts3HashFind(const Fts3Hash*, const void *pKey, int nKey);
+SQLITE_PRIVATE void sqlite3Fts3HashClear(Fts3Hash*);
+
+/*
+** Shorthand for the functions above
+*/
+#define fts3HashInit   sqlite3Fts3HashInit
+#define fts3HashInsert sqlite3Fts3HashInsert
+#define fts3HashFind   sqlite3Fts3HashFind
+#define fts3HashClear  sqlite3Fts3HashClear
+
+/*
+** Macros for looping over all elements of a hash table.  The idiom is
+** like this:
+**
+**   Fts3Hash h;
+**   Fts3HashElem *p;
+**   ...
+**   for(p=fts3HashFirst(&h); p; p=fts3HashNext(p)){
+**     SomeStructure *pData = fts3HashData(p);
+**     // do something with pData
+**   }
+*/
+#define fts3HashFirst(H)  ((H)->first)
+#define fts3HashNext(E)   ((E)->next)
+#define fts3HashData(E)   ((E)->data)
+#define fts3HashKey(E)    ((E)->pKey)
+#define fts3HashKeysize(E) ((E)->nKey)
+
+/*
+** Number of entries in a hash table
+*/
+#define fts3HashCount(H)  ((H)->count)
+
+#endif /* _FTS3_HASH_H_ */
+
+/************** End of fts3_hash.h *******************************************/
+/************** Continuing where we left off in fts3Int.h ********************/
+
+/*
+** This constant controls how often segments are merged. Once there are
+** FTS3_MERGE_COUNT segments of level N, they are merged into a single
+** segment of level N+1.
+*/
+#define FTS3_MERGE_COUNT 16
+
+/*
+** This is the maximum amount of data (in bytes) to store in the
+** Fts3Table.pendingTerms hash table. Normally, the hash table is
+** populated as documents are inserted/updated/deleted in a transaction
+** and used to create a new segment when the transaction is committed.
+** However if this limit is reached midway through a transaction, a new
+** segment is created and the hash table cleared immediately.
+*/
+#define FTS3_MAX_PENDING_DATA (1*1024*1024)
+
+/*
+** Macro to return the number of elements in an array. SQLite has a
+** similar macro called ArraySize(). Use a different name to avoid
+** a collision when building an amalgamation with built-in FTS3.
+*/
+#define SizeofArray(X) ((int)(sizeof(X)/sizeof(X[0])))
+
+/*
+** Maximum length of a varint encoded integer. The varint format is different
+** from that used by SQLite, so the maximum length is 10, not 9.
+*/
+#define FTS3_VARINT_MAX 10
+
+/*
+** This section provides definitions to allow the
+** FTS3 extension to be compiled outside of the
+** amalgamation.
+*/
+#ifndef SQLITE_AMALGAMATION
+/*
+** Macros indicating that conditional expressions are always true or
+** false.
+*/
+# define ALWAYS(x) (x)
+# define NEVER(X)  (x)
+/*
+** Internal types used by SQLite.
+*/
+typedef unsigned char u8;         /* 1-byte (or larger) unsigned integer */
+typedef short int i16;            /* 2-byte (or larger) signed integer */
+/*
+** Macro used to suppress compiler warnings for unused parameters.
+*/
+#define UNUSED_PARAMETER(x) (void)(x)
+#endif
+
+typedef struct Fts3Table Fts3Table;
+typedef struct Fts3Cursor Fts3Cursor;
+typedef struct Fts3Expr Fts3Expr;
+typedef struct Fts3Phrase Fts3Phrase;
+typedef struct Fts3SegReader Fts3SegReader;
+typedef struct Fts3SegFilter Fts3SegFilter;
+
+/*
+** A connection to a fulltext index is an instance of the following
+** structure. The xCreate and xConnect methods create an instance
+** of this structure and xDestroy and xDisconnect free that instance.
+** All other methods receive a pointer to the structure as one of their
+** arguments.
+*/
+struct Fts3Table {
+  sqlite3_vtab base;              /* Base class used by SQLite core */
+  sqlite3 *db;                    /* The database connection */
+  const char *zDb;                /* logical database name */
+  const char *zName;              /* virtual table name */
+  int nColumn;                    /* number of named columns in virtual table */
+  char **azColumn;                /* column names.  malloced */
+  sqlite3_tokenizer *pTokenizer;  /* tokenizer for inserts and queries */
+
+  /* Precompiled statements used by the implementation. Each of these
+  ** statements is run and reset within a single virtual table API call.
+  */
+  sqlite3_stmt *aStmt[18];
+
+  /* Pointer to string containing the SQL:
+  **
+  ** "SELECT block FROM %_segments WHERE blockid BETWEEN ? AND ?
+  **    ORDER BY blockid"
+  */
+  char *zSelectLeaves;
+  int nLeavesStmt;                /* Valid statements in aLeavesStmt */
+  int nLeavesTotal;               /* Total number of prepared leaves stmts */
+  int nLeavesAlloc;               /* Allocated size of aLeavesStmt */
+  sqlite3_stmt **aLeavesStmt;     /* Array of prepared zSelectLeaves stmts */
+
+  int nNodeSize;                  /* Soft limit for node size */
+
+  /* The following hash table is used to buffer pending index updates during
+  ** transactions. Variable nPendingData estimates the memory size of the
+  ** pending data, including hash table overhead, but not malloc overhead.
+  ** When nPendingData exceeds FTS3_MAX_PENDING_DATA, the buffer is flushed
+  ** automatically. Variable iPrevDocid is the docid of the most recently
+  ** inserted record.
+  */
+  int nPendingData;
+  sqlite_int64 iPrevDocid;
+  Fts3Hash pendingTerms;
+};
+
+/*
+** When the core wants to read from the virtual table, it creates a
+** virtual table cursor (an instance of the following structure) using
+** the xOpen method. Cursors are destroyed using the xClose method.
+*/
+struct Fts3Cursor {
+  sqlite3_vtab_cursor base;       /* Base class used by SQLite core */
+  i16 eSearch;                    /* Search strategy (see below) */
+  u8 isEof;                       /* True if at End Of Results */
+  u8 isRequireSeek;               /* True if must seek pStmt to %_content row */
+  sqlite3_stmt *pStmt;            /* Prepared statement in use by the cursor */
+  Fts3Expr *pExpr;                /* Parsed MATCH query string */
+  sqlite3_int64 iPrevId;          /* Previous id read from aDoclist */
+  char *pNextId;                  /* Pointer into the body of aDoclist */
+  char *aDoclist;                 /* List of docids for full-text queries */
+  int nDoclist;                   /* Size of buffer at aDoclist */
+};
+
+/*
+** The Fts3Cursor.eSearch member is always set to one of the following.
+** Actualy, Fts3Cursor.eSearch can be greater than or equal to
+** FTS3_FULLTEXT_SEARCH.  If so, then Fts3Cursor.eSearch - 2 is the index
+** of the column to be searched.  For example, in
+**
+**     CREATE VIRTUAL TABLE ex1 USING fts3(a,b,c,d);
+**     SELECT docid FROM ex1 WHERE b MATCH 'one two three';
+**
+** Because the LHS of the MATCH operator is 2nd column "b",
+** Fts3Cursor.eSearch will be set to FTS3_FULLTEXT_SEARCH+1.  (+0 for a,
+** +1 for b, +2 for c, +3 for d.)  If the LHS of MATCH were "ex1"
+** indicating that all columns should be searched,
+** then eSearch would be set to FTS3_FULLTEXT_SEARCH+4.
+*/
+#define FTS3_FULLSCAN_SEARCH 0    /* Linear scan of %_content table */
+#define FTS3_DOCID_SEARCH    1    /* Lookup by rowid on %_content table */
+#define FTS3_FULLTEXT_SEARCH 2    /* Full-text index search */
+
+/*
+** A "phrase" is a sequence of one or more tokens that must match in
+** sequence.  A single token is the base case and the most common case.
+** For a sequence of tokens contained in "...", nToken will be the number
+** of tokens in the string.
+*/
+struct Fts3Phrase {
+  int nToken;                /* Number of tokens in the phrase */
+  int iColumn;               /* Index of column this phrase must match */
+  int isNot;                 /* Phrase prefixed by unary not (-) operator */
+  struct PhraseToken {
+    char *z;                 /* Text of the token */
+    int n;                   /* Number of bytes in buffer pointed to by z */
+    int isPrefix;            /* True if token ends in with a "*" character */
+  } aToken[1];               /* One entry for each token in the phrase */
+};
+
+/*
+** A tree of these objects forms the RHS of a MATCH operator.
+*/
+struct Fts3Expr {
+  int eType;                 /* One of the FTSQUERY_XXX values defined below */
+  int nNear;                 /* Valid if eType==FTSQUERY_NEAR */
+  Fts3Expr *pParent;         /* pParent->pLeft==this or pParent->pRight==this */
+  Fts3Expr *pLeft;           /* Left operand */
+  Fts3Expr *pRight;          /* Right operand */
+  Fts3Phrase *pPhrase;       /* Valid if eType==FTSQUERY_PHRASE */
+};
+
+/*
+** Candidate values for Fts3Query.eType. Note that the order of the first
+** four values is in order of precedence when parsing expressions. For
+** example, the following:
+**
+**   "a OR b AND c NOT d NEAR e"
+**
+** is equivalent to:
+**
+**   "a OR (b AND (c NOT (d NEAR e)))"
+*/
+#define FTSQUERY_NEAR   1
+#define FTSQUERY_NOT    2
+#define FTSQUERY_AND    3
+#define FTSQUERY_OR     4
+#define FTSQUERY_PHRASE 5
+
+
+/* fts3_init.c */
+SQLITE_PRIVATE int sqlite3Fts3DeleteVtab(int, sqlite3_vtab *);
+SQLITE_PRIVATE int sqlite3Fts3InitVtab(int, sqlite3*, void*, int, const char*const*,
+                        sqlite3_vtab **, char **);
+
+/* fts3_write.c */
+SQLITE_PRIVATE int sqlite3Fts3UpdateMethod(sqlite3_vtab*,int,sqlite3_value**,sqlite3_int64*);
+SQLITE_PRIVATE int sqlite3Fts3PendingTermsFlush(Fts3Table *);
+SQLITE_PRIVATE void sqlite3Fts3PendingTermsClear(Fts3Table *);
+SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *);
+SQLITE_PRIVATE int sqlite3Fts3SegReaderNew(Fts3Table *,int, sqlite3_int64,
+  sqlite3_int64, sqlite3_int64, const char *, int, Fts3SegReader**);
+SQLITE_PRIVATE void sqlite3Fts3SegReaderFree(Fts3Table *, Fts3SegReader *);
+SQLITE_PRIVATE int sqlite3Fts3SegReaderIterate(
+  Fts3Table *, Fts3SegReader **, int, Fts3SegFilter *,
+  int (*)(Fts3Table *, void *, char *, int, char *, int),  void *
+);
+SQLITE_PRIVATE int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char const**, int*);
+SQLITE_PRIVATE int sqlite3Fts3AllSegdirs(Fts3Table*, sqlite3_stmt **);
+
+/* Flags allowed as part of the 4th argument to SegmentReaderIterate() */
+#define FTS3_SEGMENT_REQUIRE_POS   0x00000001
+#define FTS3_SEGMENT_IGNORE_EMPTY  0x00000002
+#define FTS3_SEGMENT_COLUMN_FILTER 0x00000004
+#define FTS3_SEGMENT_PREFIX        0x00000008
+
+/* Type passed as 4th argument to SegmentReaderIterate() */
+struct Fts3SegFilter {
+  const char *zTerm;
+  int nTerm;
+  int iCol;
+  int flags;
+};
+
+/* fts3.c */
+SQLITE_PRIVATE int sqlite3Fts3PutVarint(char *, sqlite3_int64);
+SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *, sqlite_int64 *);
+SQLITE_PRIVATE int sqlite3Fts3GetVarint32(const char *, int *);
+SQLITE_PRIVATE int sqlite3Fts3VarintLen(sqlite3_uint64);
+SQLITE_PRIVATE void sqlite3Fts3Dequote(char *);
+
+/* fts3_tokenizer.c */
+SQLITE_PRIVATE const char *sqlite3Fts3NextToken(const char *, int *);
+SQLITE_PRIVATE int sqlite3Fts3InitHashTable(sqlite3 *, Fts3Hash *, const char *);
+SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(Fts3Hash *pHash,
+  const char *, sqlite3_tokenizer **, const char **, char **
+);
+
+/* fts3_snippet.c */
+SQLITE_PRIVATE void sqlite3Fts3Offsets(sqlite3_context*, Fts3Cursor*);
+SQLITE_PRIVATE void sqlite3Fts3Snippet(sqlite3_context*, Fts3Cursor*,
+  const char *, const char *, const char *
+);
+
+/* fts3_expr.c */
+SQLITE_PRIVATE int sqlite3Fts3ExprParse(sqlite3_tokenizer *,
+  char **, int, int, const char *, int, Fts3Expr **
+);
+SQLITE_PRIVATE void sqlite3Fts3ExprFree(Fts3Expr *);
+#ifdef SQLITE_TEST
+SQLITE_PRIVATE void sqlite3Fts3ExprInitTestInterface(sqlite3 *db);
+#endif
+
+#endif /* _FTSINT_H */
+
+/************** End of fts3Int.h *********************************************/
+/************** Continuing where we left off in fts3.c ***********************/
 
 
 #ifndef SQLITE_CORE
   SQLITE_EXTENSION_INIT1
 #endif
@@ -98108,11 +98742,11 @@
     default:    return;
   }
   for(i=1, j=0; z[i]; i++){
     if( z[i]==quote ){
       if( z[i+1]==quote ){
-        z[j++] = quote;
+        z[j++] = (char)quote;
         i++;
       }else{
         z[j++] = 0;
         break;
       }
@@ -98132,22 +98766,10 @@
   if( *pp>=pEnd ){
     *pp = 0;
   }else{
     fts3GetDeltaVarint(pp, pVal);
   }
-}
-
-
-/*
-** The Fts3Cursor.eType member is always set to one of the following.
-*/
-#define FTS3_FULLSCAN_SEARCH 0    /* Linear scan of %_content table */
-#define FTS3_DOCID_SEARCH    1    /* Lookup by rowid on %_content table */
-#define FTS3_FULLTEXT_SEARCH 2    /* Full-text index search */
-
-static Fts3Table *cursor_vtab(Fts3Cursor *c){
-  return (Fts3Table *) c->base.pVtab;
 }
 
 /*
 ** The xDisconnect() virtual table method.
 */
@@ -98295,11 +98917,11 @@
 ** The argv[] array contains the following:
 **
 **   argv[0]   -> module name
 **   argv[1]   -> database name
 **   argv[2]   -> table name
-**   argv[...] -> "column name" fields...
+**   argv[...] -> "column name" and other module argument fields.
 */
 int fts3InitVtab(
   int isCreate,                   /* True for xCreate, false for xConnect */
   sqlite3 *db,                    /* The SQLite database connection */
   void *pAux,                     /* Hash table containing tokenizers */
@@ -98307,43 +98929,55 @@
   const char * const *argv,       /* xCreate/xConnect argument array */
   sqlite3_vtab **ppVTab,          /* Write the resulting vtab structure here */
   char **pzErr                    /* Write any error message here */
 ){
   Fts3Hash *pHash = (Fts3Hash *)pAux;
-  Fts3Table *p;               /* Pointer to allocated vtab */
+  Fts3Table *p;                   /* Pointer to allocated vtab */
   int rc;                         /* Return code */
-  int i;
-  int nByte;
+  int i;                          /* Iterator variable */
+  int nByte;                      /* Size of allocation used for *p */
   int iCol;
   int nString = 0;
   int nCol = 0;
   char *zCsr;
   int nDb;
   int nName;
 
-  const char *zTokenizer = 0;
-  sqlite3_tokenizer *pTokenizer;  /* Tokenizer for this table */
-
-  nDb = strlen(argv[1]) + 1;
-  nName = strlen(argv[2]) + 1;
+  const char *zTokenizer = 0;               /* Name of tokenizer to use */
+  sqlite3_tokenizer *pTokenizer = 0;        /* Tokenizer for this table */
+
+#ifdef SQLITE_TEST
+  const char *zTestParam = 0;
+  if( strncmp(argv[argc-1], "test:", 5)==0 ){
+    zTestParam = argv[argc-1];
+    argc--;
+  }
+#endif
+
+  nDb = (int)strlen(argv[1]) + 1;
+  nName = (int)strlen(argv[2]) + 1;
   for(i=3; i<argc; i++){
     char const *z = argv[i];
     rc = sqlite3Fts3InitTokenizer(pHash, z, &pTokenizer, &zTokenizer, pzErr);
     if( rc!=SQLITE_OK ){
       return rc;
     }
     if( z!=zTokenizer ){
-      nString += strlen(z) + 1;
+      nString += (int)(strlen(z) + 1);
     }
   }
   nCol = argc - 3 - (zTokenizer!=0);
   if( zTokenizer==0 ){
     rc = sqlite3Fts3InitTokenizer(pHash, 0, &pTokenizer, 0, pzErr);
     if( rc!=SQLITE_OK ){
       return rc;
     }
     assert( pTokenizer );
+  }
+
+  if( nCol==0 ){
+    nCol = 1;
   }
 
   /* Allocate and populate the Fts3Table structure. */
   nByte = sizeof(Fts3Table) +              /* Fts3Table */
           nCol * sizeof(char *) +              /* azColumn */
@@ -98360,10 +98994,11 @@
   p->db = db;
   p->nColumn = nCol;
   p->nPendingData = 0;
   p->azColumn = (char **)&p[1];
   p->pTokenizer = pTokenizer;
+  p->nNodeSize = 1000;
   zCsr = (char *)&p->azColumn[nCol];
 
   fts3HashInit(&p->pendingTerms, FTS3_HASH_STRING, 1);
 
   /* Fill in the zName and zDb fields of the vtab structure. */
@@ -98387,10 +99022,14 @@
       p->azColumn[iCol++] = zCsr;
       zCsr += n+1;
       assert( zCsr <= &((char *)p)[nByte] );
     }
   }
+  if( iCol==0 ){
+    assert( nCol==1 );
+    p->azColumn[0] = "content";
+  }
 
   /* If this is an xCreate call, create the underlying tables in the
   ** database. TODO: For xConnect(), it could verify that said tables exist.
   */
   if( isCreate ){
@@ -98399,16 +99038,25 @@
   }
 
   rc = fts3DeclareVtab(p);
   if( rc!=SQLITE_OK ) goto fts3_init_out;
 
+#ifdef SQLITE_TEST
+  if( zTestParam ){
+    p->nNodeSize = atoi(&zTestParam[5]);
+  }
+#endif
   *ppVTab = &p->base;
 
 fts3_init_out:
-  if( rc!=SQLITE_OK ){
-    if( p ) fts3DisconnectMethod((sqlite3_vtab *)p);
-    else if( pTokenizer ) pTokenizer->pModule->xDestroy(pTokenizer);
+  assert( p || (pTokenizer && rc!=SQLITE_OK) );
+  if( rc!=SQLITE_OK ){
+    if( p ){
+      fts3DisconnectMethod((sqlite3_vtab *)p);
+    }else{
+      pTokenizer->pModule->xDestroy(pTokenizer);
+    }
   }
   return rc;
 }
 
 /*
@@ -98497,10 +99145,12 @@
 /*
 ** Implementation of xOpen method.
 */
 static int fts3OpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){
   sqlite3_vtab_cursor *pCsr;               /* Allocated cursor */
+
+  UNUSED_PARAMETER(pVTab);
 
   /* Allocate a buffer large enough for an Fts3Cursor structure. If the
   ** allocation succeeds, zero it and return SQLITE_OK. Otherwise,
   ** if the allocation fails, return SQLITE_NOMEM.
   */
@@ -98664,16 +99314,20 @@
   }
   sqlite3_free(zBuffer);
   return rc;
 }
 
+/*
+** This function is used to create delta-encoded serialized lists of FTS3
+** varints. Each call to this function appends a single varint to a list.
+*/
 static void fts3PutDeltaVarint(
-  char **pp,
-  sqlite3_int64 *piPrev,
-  sqlite3_int64 iVal
-){
-  assert( iVal-*piPrev > 0 );
+  char **pp,                      /* IN/OUT: Output pointer */
+  sqlite3_int64 *piPrev,          /* IN/OUT: Previous value written to list */
+  sqlite3_int64 iVal              /* Write this value to the list */
+){
+  assert( iVal-*piPrev > 0 || (*piPrev==0 && iVal==0) );
   *pp += sqlite3Fts3PutVarint(*pp, iVal-*piPrev);
   *piPrev = iVal;
 }
 
 static void fts3PoslistCopy(char **pp, char **ppPoslist){
@@ -98680,11 +99334,11 @@
   char *pEnd = *ppPoslist;
   char c = 0;
   while( *pEnd | c ) c = *pEnd++ & 0x80;
   pEnd++;
   if( pp ){
-    int n = pEnd - *ppPoslist;
+    int n = (int)(pEnd - *ppPoslist);
     char *p = *pp;
     memcpy(p, *ppPoslist, n);
     p += n;
     *pp = p;
   }
@@ -98692,19 +99346,69 @@
 }
 
 static void fts3ColumnlistCopy(char **pp, char **ppPoslist){
   char *pEnd = *ppPoslist;
   char c = 0;
+
+  /* A column-list is terminated by either a 0x01 or 0x00. */
   while( 0xFE & (*pEnd | c) ) c = *pEnd++ & 0x80;
   if( pp ){
-    int n = pEnd - *ppPoslist;
+    int n = (int)(pEnd - *ppPoslist);
     char *p = *pp;
     memcpy(p, *ppPoslist, n);
     p += n;
     *pp = p;
   }
   *ppPoslist = pEnd;
+}
+
+/*
+** Value used to signify the end of an offset-list. This is safe because
+** it is not possible to have a document with 2^31 terms.
+*/
+#define OFFSET_LIST_END 0x7fffffff
+
+/*
+** This function is used to help parse offset-lists. When this function is
+** called, *pp may point to the start of the next varint in the offset-list
+** being parsed, or it may point to 1 byte past the end of the offset-list
+** (in which case **pp will be 0x00 or 0x01).
+**
+** If *pp points past the end of the current offset list, set *pi to
+** OFFSET_LIST_END and return. Otherwise, read the next varint from *pp,
+** increment the current value of *pi by the value read, and set *pp to
+** point to the next value before returning.
+*/
+static void fts3ReadNextPos(
+  char **pp,                      /* IN/OUT: Pointer into offset-list buffer */
+  sqlite3_int64 *pi               /* IN/OUT: Value read from offset-list */
+){
+  if( **pp&0xFE ){
+    fts3GetDeltaVarint(pp, pi);
+    *pi -= 2;
+  }else{
+    *pi = OFFSET_LIST_END;
+  }
+}
+
+/*
+** If parameter iCol is not 0, write an 0x01 byte followed by the value of
+** iCol encoded as a varint to *pp.
+**
+** Set *pp to point to the byte just after the last byte written before
+** returning (do not modify it if iCol==0). Return the total number of bytes
+** written (0 if iCol==0).
+*/
+static int fts3PutColNumber(char **pp, int iCol){
+  int n = 0;                      /* Number of bytes written */
+  if( iCol ){
+    char *p = *pp;                /* Output pointer */
+    n = 1 + sqlite3Fts3PutVarint(&p[1], iCol);
+    *p = 0x01;
+    *pp = &p[n];
+  }
+  return n;
 }
 
 /*
 **
 */
@@ -98715,44 +99419,57 @@
 ){
   char *p = *pp;
   char *p1 = *pp1;
   char *p2 = *pp2;
 
-  while( *p1 && *p2 ){
-    int iCol1 = 0;
-    int iCol2 = 0;
+  while( *p1 || *p2 ){
+    int iCol1;
+    int iCol2;
+
     if( *p1==0x01 ) sqlite3Fts3GetVarint32(&p1[1], &iCol1);
+    else if( *p1==0x00 ) iCol1 = OFFSET_LIST_END;
+    else iCol1 = 0;
+
     if( *p2==0x01 ) sqlite3Fts3GetVarint32(&p2[1], &iCol2);
+    else if( *p2==0x00 ) iCol2 = OFFSET_LIST_END;
+    else iCol2 = 0;
 
     if( iCol1==iCol2 ){
       sqlite3_int64 i1 = 0;
       sqlite3_int64 i2 = 0;
       sqlite3_int64 iPrev = 0;
-      if( iCol1!=0 ){
-        int n;
-        *p++ = 0x01;
-        n = sqlite3Fts3PutVarint(p, iCol1);
-        p += n;
-        p1 += 1 + n;
-        p2 += 1 + n;
-      }
-      while( (*p1&0xFE) || (*p2&0xFE) ){
+      int n = fts3PutColNumber(&p, iCol1);
+      p1 += n;
+      p2 += n;
+
+      /* At this point, both p1 and p2 point to the start of offset-lists.
+      ** An offset-list is a list of non-negative delta-encoded varints, each
+      ** incremented by 2 before being stored. Each list is terminated by a 0
+      ** or 1 value (0x00 or 0x01). The following block merges the two lists
+      ** and writes the results to buffer p. p is left pointing to the byte
+      ** after the list written. No terminator (0x00 or 0x01) is written to
+      ** the output.
+      */
+      fts3GetDeltaVarint(&p1, &i1);
+      fts3GetDeltaVarint(&p2, &i2);
+      do {
+        fts3PutDeltaVarint(&p, &iPrev, (i1<i2) ? i1 : i2);
+        iPrev -= 2;
         if( i1==i2 ){
-          fts3GetDeltaVarint(&p1, &i1); i1 -= 2;
-          fts3GetDeltaVarint(&p2, &i2); i2 -= 2;
+          fts3ReadNextPos(&p1, &i1);
+          fts3ReadNextPos(&p2, &i2);
         }else if( i1<i2 ){
-          fts3GetDeltaVarint(&p1, &i1); i1 -= 2;
-        }else{
-          fts3GetDeltaVarint(&p2, &i2); i2 -= 2;
-        }
-        fts3PutDeltaVarint(&p, &iPrev, (i1<i2 ? i1 : i2) + 2); iPrev -= 2;
-        if( 0==(*p1&0xFE) ) i1 = 0x7FFFFFFF;
-        if( 0==(*p2&0xFE) ) i2 = 0x7FFFFFFF;
-      }
+          fts3ReadNextPos(&p1, &i1);
+        }else{
+          fts3ReadNextPos(&p2, &i2);
+        }
+      }while( i1!=OFFSET_LIST_END || i2!=OFFSET_LIST_END );
     }else if( iCol1<iCol2 ){
+      p1 += fts3PutColNumber(&p, iCol1);
       fts3ColumnlistCopy(&p, &p1);
     }else{
+      p2 += fts3PutColNumber(&p, iCol2);
       fts3ColumnlistCopy(&p, &p2);
     }
   }
 
   *p++ = '\0';
@@ -98815,17 +99532,18 @@
           }
           iSave = isSaveLeft ? iPos1 : iPos2;
           fts3PutDeltaVarint(&p, &iPrev, iSave+2); iPrev -= 2;
           pSave = 0;
         }
-        if( iPos2<=iPos1 ){
+        if( (!isSaveLeft && iPos2<=(iPos1+nToken)) || iPos2<=iPos1 ){
           if( (*p2&0xFE)==0 ) break;
           fts3GetDeltaVarint(&p2, &iPos2); iPos2 -= 2;
         }else{
           if( (*p1&0xFE)==0 ) break;
           fts3GetDeltaVarint(&p1, &iPos1); iPos1 -= 2;
         }
+
       }
       if( pSave && pp ){
         p = pSave;
       }
 
@@ -98868,17 +99586,20 @@
   *p++ = 0x00;
   *pp = p;
   return 1;
 }
 
+/*
+** Merge two position-lists as required by the NEAR operator.
+*/
 static int fts3PoslistNearMerge(
   char **pp,                      /* Output buffer */
   char *aTmp,                     /* Temporary buffer space */
   int nRight,                     /* Maximum difference in token positions */
   int nLeft,                      /* Maximum difference in token positions */
-  char **pp1,                     /* Left input list */
-  char **pp2                      /* Right input list */
+  char **pp1,                     /* IN/OUT: Left input list */
+  char **pp2                      /* IN/OUT: Right input list */
 ){
   char *p1 = *pp1;
   char *p2 = *pp2;
 
   if( !pp ){
@@ -98961,10 +99682,14 @@
   );
 
   if( !aBuffer ){
     return SQLITE_NOMEM;
   }
+  if( n1==0 && n2==0 ){
+    *pnBuffer = 0;
+    return SQLITE_OK;
+  }
 
   /* Read the first docid from each doclist */
   fts3GetDeltaVarint2(&p1, pEnd1, &i1);
   fts3GetDeltaVarint2(&p2, pEnd2, &i2);
 
@@ -99040,12 +99765,11 @@
         }
       }
       break;
     }
 
-    case MERGE_POS_NEAR:
-    case MERGE_NEAR: {
+    default: assert( mergetype==MERGE_POS_NEAR || mergetype==MERGE_NEAR ); {
       char *aTmp = 0;
       char **ppPos = 0;
       if( mergetype==MERGE_POS_NEAR ){
         ppPos = &p;
         aTmp = sqlite3_malloc(2*(n1+n2));
@@ -99076,16 +99800,13 @@
         }
       }
       sqlite3_free(aTmp);
       break;
     }
-
-    default:
-      assert(!"Invalid mergetype value passed to fts3DoclistMerge()");
-  }
-
-  *pnBuffer = (p-aBuffer);
+  }
+
+  *pnBuffer = (int)(p-aBuffer);
   return SQLITE_OK;
 }
 
 /*
 ** A pointer to an instance of this structure is used as the context
@@ -99112,10 +99833,14 @@
   int nDoclist
 ){
   TermSelect *pTS = (TermSelect *)pContext;
   int nNew = pTS->nOutput + nDoclist;
   char *aNew = sqlite3_malloc(nNew);
+
+  UNUSED_PARAMETER(p);
+  UNUSED_PARAMETER(zTerm);
+  UNUSED_PARAMETER(nTerm);
 
   if( !aNew ){
     return SQLITE_NOMEM;
   }
 
@@ -99194,25 +99919,38 @@
       ** leaf). Do not bother inspecting any data in this case, just
       ** create a Fts3SegReader to scan the single leaf.
       */
       rc = sqlite3Fts3SegReaderNew(p, iAge, 0, 0, 0, zRoot, nRoot, &pNew);
     }else{
-      sqlite3_int64 i1;
+      int rc2;                    /* Return value of sqlite3Fts3ReadBlock() */
+      sqlite3_int64 i1;           /* Blockid of leaf that may contain zTerm */
       rc = fts3SelectLeaf(p, zTerm, nTerm, zRoot, nRoot, &i1);
       if( rc==SQLITE_OK ){
         sqlite3_int64 i2 = sqlite3_column_int64(pStmt, 2);
         rc = sqlite3Fts3SegReaderNew(p, iAge, i1, i2, 0, 0, 0, &pNew);
       }
+
+      /* The following call to ReadBlock() serves to reset the SQL statement
+      ** used to retrieve blocks of data from the %_segments table. If it is
+      ** not reset here, then it may remain classified as an active statement
+      ** by SQLite, which may lead to "DROP TABLE" or "DETACH" commands
+      ** failing.
+      */
+      rc2 = sqlite3Fts3ReadBlock(p, 0, 0, 0);
+      if( rc==SQLITE_OK ){
+        rc = rc2;
+      }
     }
     iAge++;
 
     /* If a new Fts3SegReader was allocated, add it to the apSegment array. */
-    assert( (rc==SQLITE_OK)==(pNew!=0) );
+    assert( pNew!=0 || rc!=SQLITE_OK );
     if( pNew ){
       if( nSegment==nAlloc ){
+        Fts3SegReader **pArray;
         nAlloc += 16;
-        Fts3SegReader **pArray = (Fts3SegReader **)sqlite3_realloc(
+        pArray = (Fts3SegReader **)sqlite3_realloc(
             apSegment, nAlloc*sizeof(Fts3SegReader *)
         );
         if( !pArray ){
           sqlite3Fts3SegReaderFree(p, pNew);
           rc = SQLITE_NOMEM;
@@ -99348,10 +100086,13 @@
       int nRight;
 
       if( SQLITE_OK==(rc = evalFts3Expr(p, pExpr->pRight, &aRight, &nRight))
        && SQLITE_OK==(rc = evalFts3Expr(p, pExpr->pLeft, &aLeft, &nLeft))
       ){
+        assert( pExpr->eType==FTSQUERY_NEAR || pExpr->eType==FTSQUERY_OR
+            || pExpr->eType==FTSQUERY_AND  || pExpr->eType==FTSQUERY_NOT
+        );
         switch( pExpr->eType ){
           case FTSQUERY_NEAR: {
             Fts3Expr *pLeft;
             Fts3Expr *pRight;
             int mergetype = MERGE_NEAR;
@@ -99398,12 +100139,11 @@
             *paOut = aBuffer;
             sqlite3_free(aLeft);
             break;
           }
 
-          case FTSQUERY_AND:
-          case FTSQUERY_NOT: {
+          default: {
             assert( FTSQUERY_NOT==MERGE_NOT && FTSQUERY_AND==MERGE_AND );
             fts3DoclistMerge(pExpr->eType, 0, 0, aLeft, pnOut,
                 aLeft, nLeft, aRight, nRight
             );
             *paOut = aLeft;
@@ -99453,10 +100193,13 @@
   int rc;                         /* Return code */
   char *zSql;                     /* SQL statement used to access %_content */
   Fts3Table *p = (Fts3Table *)pCursor->pVtab;
   Fts3Cursor *pCsr = (Fts3Cursor *)pCursor;
 
+  UNUSED_PARAMETER(idxStr);
+  UNUSED_PARAMETER(nVal);
+
   assert( idxNum>=0 && idxNum<=(FTS3_FULLTEXT_SEARCH+p->nColumn) );
   assert( nVal==0 || nVal==1 );
   assert( (nVal==0)==(idxNum==FTS3_FULLSCAN_SEARCH) );
 
   /* In case the cursor has been used before, clear it now. */
@@ -99475,18 +100218,21 @@
   }else{
     rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0);
     sqlite3_free(zSql);
   }
   if( rc!=SQLITE_OK ) return rc;
-  pCsr->eType = idxNum;
+  pCsr->eSearch = (i16)idxNum;
 
   if( idxNum==FTS3_DOCID_SEARCH ){
     rc = sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]);
   }else if( idxNum!=FTS3_FULLSCAN_SEARCH ){
     int iCol = idxNum-FTS3_FULLTEXT_SEARCH;
     const char *zQuery = (const char *)sqlite3_value_text(apVal[0]);
 
+    if( zQuery==0 && sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
+      return SQLITE_NOMEM;
+    }
     rc = sqlite3Fts3PendingTermsFlush(p);
     if( rc!=SQLITE_OK ) return rc;
 
     rc = sqlite3Fts3ExprParse(p->pTokenizer, p->azColumn, p->nColumn,
         iCol, zQuery, -1, &pCsr->pExpr
@@ -99509,42 +100255,10 @@
 static int fts3EofMethod(sqlite3_vtab_cursor *pCursor){
   return ((Fts3Cursor *)pCursor)->isEof;
 }
 
 /*
-** This is the xColumn method of the virtual table.  The SQLite
-** core calls this method during a query when it needs the value
-** of a column from the virtual table.  This method needs to use
-** one of the sqlite3_result_*() routines to store the requested
-** value back in the pContext.
-*/
-static int fts3ColumnMethod(sqlite3_vtab_cursor *pCursor,
-                          sqlite3_context *pContext, int idxCol){
-  Fts3Cursor *c = (Fts3Cursor *) pCursor;
-  Fts3Table *v = cursor_vtab(c);
-  int rc = fts3CursorSeek(c);
-  if( rc!=SQLITE_OK ){
-    return rc;
-  }
-
-  if( idxCol<v->nColumn ){
-    sqlite3_value *pVal = sqlite3_column_value(c->pStmt, idxCol+1);
-    sqlite3_result_value(pContext, pVal);
-  }else if( idxCol==v->nColumn ){
-    /* The extra column whose name is the same as the table.
-    ** Return a blob which is a pointer to the cursor
-    */
-    sqlite3_result_blob(pContext, &c, sizeof(c), SQLITE_TRANSIENT);
-  }else if( idxCol==v->nColumn+1 ){
-    /* The docid column, which is an alias for rowid. */
-    sqlite3_value *pVal = sqlite3_column_value(c->pStmt, 0);
-    sqlite3_result_value(pContext, pVal);
-  }
-  return SQLITE_OK;
-}
-
-/*
 ** This is the xRowid method. The SQLite core calls this routine to
 ** retrieve the rowid for the current row of the result set. fts3
 ** exposes %_content.docid as the rowid for the virtual table. The
 ** rowid should be written to *pRowid.
 */
@@ -99554,10 +100268,47 @@
     *pRowid = pCsr->iPrevId;
   }else{
     *pRowid = sqlite3_column_int64(pCsr->pStmt, 0);
   }
   return SQLITE_OK;
+}
+
+/*
+** This is the xColumn method, called by SQLite to request a value from
+** the row that the supplied cursor currently points to.
+*/
+static int fts3ColumnMethod(
+  sqlite3_vtab_cursor *pCursor,   /* Cursor to retrieve value from */
+  sqlite3_context *pContext,      /* Context for sqlite3_result_xxx() calls */
+  int iCol                        /* Index of column to read value from */
+){
+  int rc;                         /* Return Code */
+  Fts3Cursor *pCsr = (Fts3Cursor *) pCursor;
+  Fts3Table *p = (Fts3Table *)pCursor->pVtab;
+
+  /* The column value supplied by SQLite must be in range. */
+  assert( iCol>=0 && iCol<=p->nColumn+1 );
+
+  rc = fts3CursorSeek(pCsr);
+  if( rc==SQLITE_OK ){
+    if( iCol==p->nColumn+1 ){
+      /* This call is a request for the "docid" column. Since "docid" is an
+      ** alias for "rowid", use the xRowid() method to obtain the value.
+      */
+      sqlite3_int64 iRowid;
+      rc = fts3RowidMethod(pCursor, &iRowid);
+      sqlite3_result_int64(pContext, iRowid);
+    }else if( iCol==p->nColumn ){
+      /* The extra column whose name is the same as the table.
+      ** Return a blob which is a pointer to the cursor.
+      */
+      sqlite3_result_blob(pContext, &pCsr, sizeof(pCsr), SQLITE_TRANSIENT);
+    }else{
+      sqlite3_result_value(pContext, sqlite3_column_value(pCsr->pStmt, iCol+1));
+    }
+  }
+  return rc;
 }
 
 /*
 ** This function is the implementation of the xUpdate callback used by
 ** FTS3 virtual tables. It is invoked by SQLite each time a row is to be
@@ -99582,10 +100333,11 @@
 
 /*
 ** Implementation of xBegin() method. This is a no-op.
 */
 static int fts3BeginMethod(sqlite3_vtab *pVtab){
+  UNUSED_PARAMETER(pVtab);
   assert( ((Fts3Table *)pVtab)->nPendingData==0 );
   return SQLITE_OK;
 }
 
 /*
@@ -99592,10 +100344,11 @@
 ** Implementation of xCommit() method. This is a no-op. The contents of
 ** the pending-terms hash-table have already been flushed into the database
 ** by fts3SyncMethod().
 */
 static int fts3CommitMethod(sqlite3_vtab *pVtab){
+  UNUSED_PARAMETER(pVtab);
   assert( ((Fts3Table *)pVtab)->nPendingData==0 );
   return SQLITE_OK;
 }
 
 /*
@@ -99623,11 +100376,11 @@
   sqlite3_value *pVal,            /* argv[0] passed to function */
   Fts3Cursor **ppCsr         /* OUT: Store cursor handle here */
 ){
   Fts3Cursor *pRet;
   if( sqlite3_value_type(pVal)!=SQLITE_BLOB
-   && sqlite3_value_bytes(pVal)!=sizeof(Fts3Cursor *)
+   || sqlite3_value_bytes(pVal)!=sizeof(Fts3Cursor *)
   ){
     char *zErr = sqlite3_mprintf("illegal first argument to %s", zFunc);
     sqlite3_result_error(pContext, zErr, -1);
     sqlite3_free(zErr);
     return SQLITE_ERROR;
@@ -99639,26 +100392,35 @@
 
 /*
 ** Implementation of the snippet() function for FTS3
 */
 static void fts3SnippetFunc(
-  sqlite3_context *pContext,
-  int argc,
-  sqlite3_value **argv
+  sqlite3_context *pContext,      /* SQLite function call context */
+  int nVal,                       /* Size of apVal[] array */
+  sqlite3_value **apVal           /* Array of arguments */
 ){
   Fts3Cursor *pCsr;               /* Cursor handle passed through apVal[0] */
   const char *zStart = "<b>";
   const char *zEnd = "</b>";
   const char *zEllipsis = "<b>...</b>";
 
-  if( argc<1 || argc>4 ) return;
-  if( fts3FunctionArg(pContext, "snippet", argv[0], &pCsr) ) return;
-
-  switch( argc ){
-    case 4: zEllipsis = (const char*)sqlite3_value_text(argv[3]);
-    case 3: zEnd = (const char*)sqlite3_value_text(argv[2]);
-    case 2: zStart = (const char*)sqlite3_value_text(argv[1]);
+  /* There must be at least one argument passed to this function (otherwise
+  ** the non-overloaded version would have been called instead of this one).
+  */
+  assert( nVal>=1 );
+
+  if( nVal>4 ){
+    sqlite3_result_error(pContext,
+        "wrong number of arguments to function snippet()", -1);
+    return;
+  }
+  if( fts3FunctionArg(pContext, "snippet", apVal[0], &pCsr) ) return;
+
+  switch( nVal ){
+    case 4: zEllipsis = (const char*)sqlite3_value_text(apVal[3]);
+    case 3: zEnd = (const char*)sqlite3_value_text(apVal[2]);
+    case 2: zStart = (const char*)sqlite3_value_text(apVal[1]);
   }
 
   sqlite3Fts3Snippet(pContext, pCsr, zStart, zEnd, zEllipsis);
 }
 
@@ -99669,10 +100431,12 @@
   sqlite3_context *pContext,      /* SQLite function call context */
   int nVal,                       /* Size of argument array */
   sqlite3_value **apVal           /* Array of arguments */
 ){
   Fts3Cursor *pCsr;               /* Cursor handle passed through apVal[0] */
+
+  UNUSED_PARAMETER(nVal);
 
   assert( nVal==1 );
   if( fts3FunctionArg(pContext, "offsets", apVal[0], &pCsr) ) return;
   assert( pCsr );
   sqlite3Fts3Offsets(pContext, pCsr);
@@ -99693,10 +100457,12 @@
   sqlite3_value **apVal           /* Array of arguments */
 ){
   int rc;                         /* Return code */
   Fts3Table *p;                   /* Virtual table handle */
   Fts3Cursor *pCursor;            /* Cursor handle passed through apVal[0] */
+
+  UNUSED_PARAMETER(nVal);
 
   assert( nVal==1 );
   if( fts3FunctionArg(pContext, "optimize", apVal[0], &pCursor) ) return;
   p = (Fts3Table *)pCursor->base.pVtab;
   assert( p );
@@ -99734,10 +100500,15 @@
     { "snippet", fts3SnippetFunc },
     { "offsets", fts3OffsetsFunc },
     { "optimize", fts3OptimizeFunc },
   };
   int i;                          /* Iterator variable */
+
+  UNUSED_PARAMETER(pVtab);
+  UNUSED_PARAMETER(nArg);
+  UNUSED_PARAMETER(ppArg);
+
   for(i=0; i<SizeofArray(aOverload); i++){
     if( strcmp(zName, aOverload[i].zName)==0 ){
       *pxFunc = aOverload[i].xFunc;
       return 1;
     }
@@ -100150,11 +100921,11 @@
     pCursor = 0;
   }
 
   if( rc==SQLITE_DONE ){
     int jj;
-    char *zNew;
+    char *zNew = NULL;
     int nNew = 0;
     int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase);
     nByte += (p?(p->pPhrase->nToken-1):0) * sizeof(struct PhraseToken);
     p = fts3ReallocOrFree(p, nByte + nTemp);
     if( !p ){
@@ -100209,11 +100980,11 @@
   const char *z, int n,                   /* Input string */
   Fts3Expr **ppExpr,                      /* OUT: expression */
   int *pnConsumed                         /* OUT: Number of bytes consumed */
 ){
   static const struct Fts3Keyword {
-    char z[4];                            /* Keyword text */
+    char *z;                              /* Keyword text */
     unsigned char n;                      /* Length of the keyword */
     unsigned char parenOnly;              /* Only valid in paren mode */
     unsigned char eType;                  /* Keyword code */
   } aKeyword[] = {
     { "OR" ,  2, 0, FTSQUERY_OR   },
@@ -100279,11 +101050,11 @@
         }
         memset(pRet, 0, sizeof(Fts3Expr));
         pRet->eType = pKey->eType;
         pRet->nNear = nNear;
         *ppExpr = pRet;
-        *pnConsumed = (zInput - z) + nKey;
+        *pnConsumed = (int)((zInput - z) + nKey);
         return SQLITE_OK;
       }
 
       /* Turns out that wasn't a keyword after all. This happens if the
       ** user has supplied a token such as "ORacle". Continue.
@@ -100299,18 +101070,18 @@
       pParse->nNest++;
       rc = fts3ExprParse(pParse, &zInput[1], nInput-1, ppExpr, &nConsumed);
       if( rc==SQLITE_OK && !*ppExpr ){
         rc = SQLITE_DONE;
       }
-      *pnConsumed = (zInput - z) + 1 + nConsumed;
+      *pnConsumed = (int)((zInput - z) + 1 + nConsumed);
       return rc;
     }
 
     /* Check for a close bracket. */
     if( *zInput==')' ){
       pParse->nNest--;
-      *pnConsumed = (zInput - z) + 1;
+      *pnConsumed = (int)((zInput - z) + 1);
       return SQLITE_DONE;
     }
   }
 
   /* See if we are dealing with a quoted phrase. If this is the case, then
@@ -100318,11 +101089,11 @@
   ** for processing. This is easy to do, as fts3 has no syntax for escaping
   ** a quote character embedded in a string.
   */
   if( *zInput=='"' ){
     for(ii=1; ii<nInput && zInput[ii]!='"'; ii++);
-    *pnConsumed = (zInput - z) + ii + 1;
+    *pnConsumed = (int)((zInput - z) + ii + 1);
     if( ii==nInput ){
       return SQLITE_ERROR;
     }
     return getNextString(pParse, &zInput[1], ii-1, ppExpr);
   }
@@ -100341,16 +101112,16 @@
   */
   iCol = pParse->iDefaultCol;
   iColLen = 0;
   for(ii=0; ii<pParse->nCol; ii++){
     const char *zStr = pParse->azCol[ii];
-    int nStr = strlen(zStr);
+    int nStr = (int)strlen(zStr);
     if( nInput>nStr && zInput[nStr]==':'
      && sqlite3_strnicmp(zStr, zInput, nStr)==0
     ){
       iCol = ii;
-      iColLen = ((zInput - z) + nStr + 1);
+      iColLen = (int)((zInput - z) + nStr + 1);
       break;
     }
   }
   rc = getNextToken(pParse, iCol, &z[iColLen], n-iColLen, ppExpr, pnConsumed);
   *pnConsumed += iColLen;
@@ -100612,11 +101383,11 @@
   if( z==0 ){
     *ppExpr = 0;
     return SQLITE_OK;
   }
   if( n<0 ){
-    n = strlen(z);
+    n = (int)strlen(z);
   }
   rc = fts3ExprParse(&sParse, z, n, ppExpr, &nParsed);
 
   /* Check for mismatched parenthesis */
   if( rc==SQLITE_OK && sParse.nNest ){
@@ -100666,11 +101437,11 @@
   }
 
   sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC);
   if( SQLITE_ROW==sqlite3_step(pStmt) ){
     if( sqlite3_column_type(pStmt, 0)==SQLITE_BLOB ){
-      memcpy(pp, sqlite3_column_blob(pStmt, 0), sizeof(*pp));
+      memcpy((void *)pp, sqlite3_column_blob(pStmt, 0), sizeof(*pp));
     }
   }
 
   return sqlite3_finalize(pStmt);
 }
@@ -100849,125 +101620,10 @@
 **       SQLite (in which case SQLITE_ENABLE_FTS3 is defined).
 */
 #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
 
 
-/************** Include fts3_hash.h in the middle of fts3_hash.c *************/
-/************** Begin file fts3_hash.h ***************************************/
-/*
-** 2001 September 22
-**
-** The author disclaims copyright to this source code.  In place of
-** a legal notice, here is a blessing:
-**
-**    May you do good and not evil.
-**    May you find forgiveness for yourself and forgive others.
-**    May you share freely, never taking more than you give.
-**
-*************************************************************************
-** This is the header file for the generic hash-table implemenation
-** used in SQLite.  We've modified it slightly to serve as a standalone
-** hash table implementation for the full-text indexing module.
-**
-*/
-#ifndef _FTS3_HASH_H_
-#define _FTS3_HASH_H_
-
-/* Forward declarations of structures. */
-typedef struct Fts3Hash Fts3Hash;
-typedef struct Fts3HashElem Fts3HashElem;
-
-/* A complete hash table is an instance of the following structure.
-** The internals of this structure are intended to be opaque -- client
-** code should not attempt to access or modify the fields of this structure
-** directly.  Change this structure only by using the routines below.
-** However, many of the "procedures" and "functions" for modifying and
-** accessing this structure are really macros, so we can't really make
-** this structure opaque.
-*/
-struct Fts3Hash {
-  char keyClass;          /* HASH_INT, _POINTER, _STRING, _BINARY */
-  char copyKey;           /* True if copy of key made on insert */
-  int count;              /* Number of entries in this table */
-  Fts3HashElem *first;    /* The first element of the array */
-  int htsize;             /* Number of buckets in the hash table */
-  struct _fts3ht {        /* the hash table */
-    int count;               /* Number of entries with this hash */
-    Fts3HashElem *chain;     /* Pointer to first entry with this hash */
-  } *ht;
-};
-
-/* Each element in the hash table is an instance of the following
-** structure.  All elements are stored on a single doubly-linked list.
-**
-** Again, this structure is intended to be opaque, but it can't really
-** be opaque because it is used by macros.
-*/
-struct Fts3HashElem {
-  Fts3HashElem *next, *prev; /* Next and previous elements in the table */
-  void *data;                /* Data associated with this element */
-  void *pKey; int nKey;      /* Key associated with this element */
-};
-
-/*
-** There are 2 different modes of operation for a hash table:
-**
-**   FTS3_HASH_STRING        pKey points to a string that is nKey bytes long
-**                           (including the null-terminator, if any).  Case
-**                           is respected in comparisons.
-**
-**   FTS3_HASH_BINARY        pKey points to binary data nKey bytes long.
-**                           memcmp() is used to compare keys.
-**
-** A copy of the key is made if the copyKey parameter to fts3HashInit is 1.
-*/
-#define FTS3_HASH_STRING    1
-#define FTS3_HASH_BINARY    2
-
-/*
-** Access routines.  To delete, insert a NULL pointer.
-*/
-SQLITE_PRIVATE void sqlite3Fts3HashInit(Fts3Hash*, int keytype, int copyKey);
-SQLITE_PRIVATE void *sqlite3Fts3HashInsert(Fts3Hash*, const void *pKey, int nKey, void *pData);
-SQLITE_PRIVATE void *sqlite3Fts3HashFind(const Fts3Hash*, const void *pKey, int nKey);
-SQLITE_PRIVATE void sqlite3Fts3HashClear(Fts3Hash*);
-
-/*
-** Shorthand for the functions above
-*/
-#define fts3HashInit   sqlite3Fts3HashInit
-#define fts3HashInsert sqlite3Fts3HashInsert
-#define fts3HashFind   sqlite3Fts3HashFind
-#define fts3HashClear  sqlite3Fts3HashClear
-
-/*
-** Macros for looping over all elements of a hash table.  The idiom is
-** like this:
-**
-**   Fts3Hash h;
-**   Fts3HashElem *p;
-**   ...
-**   for(p=fts3HashFirst(&h); p; p=fts3HashNext(p)){
-**     SomeStructure *pData = fts3HashData(p);
-**     // do something with pData
-**   }
-*/
-#define fts3HashFirst(H)  ((H)->first)
-#define fts3HashNext(E)   ((E)->next)
-#define fts3HashData(E)   ((E)->data)
-#define fts3HashKey(E)    ((E)->pKey)
-#define fts3HashKeysize(E) ((E)->nKey)
-
-/*
-** Number of entries in a hash table
-*/
-#define fts3HashCount(H)  ((H)->count)
-
-#endif /* _FTS3_HASH_H_ */
-
-/************** End of fts3_hash.h *******************************************/
-/************** Continuing where we left off in fts3_hash.c ******************/
 
 /*
 ** Malloc and Free functions
 */
 static void *fts3HashMalloc(int n){
@@ -100989,11 +101645,11 @@
 ** FTS3_HASH_BINARY or FTS3_HASH_STRING.  The value of keyClass
 ** determines what kind of key the hash table will use.  "copyKey" is
 ** true if the hash table should make its own private copy of keys and
 ** false if it should just use the supplied pointer.
 */
-SQLITE_PRIVATE void sqlite3Fts3HashInit(Fts3Hash *pNew, int keyClass, int copyKey){
+SQLITE_PRIVATE void sqlite3Fts3HashInit(Fts3Hash *pNew, char keyClass, char copyKey){
   assert( pNew!=0 );
   assert( keyClass>=FTS3_HASH_STRING && keyClass<=FTS3_HASH_BINARY );
   pNew->keyClass = keyClass;
   pNew->copyKey = copyKey;
   pNew->first = 0;
@@ -101123,28 +101779,31 @@
 
 
 /* Resize the hash table so that it cantains "new_size" buckets.
 ** "new_size" must be a power of 2.  The hash table might fail
 ** to resize if sqliteMalloc() fails.
-*/
-static void fts3Rehash(Fts3Hash *pH, int new_size){
+**
+** Return non-zero if a memory allocation error occurs.
+*/
+static int fts3Rehash(Fts3Hash *pH, int new_size){
   struct _fts3ht *new_ht;          /* The new hash table */
   Fts3HashElem *elem, *next_elem;  /* For looping over existing elements */
   int (*xHash)(const void*,int);   /* The hash function */
 
   assert( (new_size & (new_size-1))==0 );
   new_ht = (struct _fts3ht *)fts3HashMalloc( new_size*sizeof(struct _fts3ht) );
-  if( new_ht==0 ) return;
+  if( new_ht==0 ) return 1;
   fts3HashFree(pH->ht);
   pH->ht = new_ht;
   pH->htsize = new_size;
   xHash = ftsHashFunction(pH->keyClass);
   for(elem=pH->first, pH->first=0; elem; elem = next_elem){
     int h = (*xHash)(elem->pKey, elem->nKey) & (new_size-1);
     next_elem = elem->next;
     fts3HashInsertElement(pH, &new_ht[h], elem);
   }
+  return 0;
 }
 
 /* This function (for internal use only) locates an element in an
 ** hash table that matches the given key.  The hash for this key has
 ** already been computed and is passed as the 4th parameter.
@@ -101271,17 +101930,17 @@
       elem->data = data;
     }
     return old_data;
   }
   if( data==0 ) return 0;
-  if( pH->htsize==0 ){
-    fts3Rehash(pH,8);
-    if( pH->htsize==0 ){
-      pH->count = 0;
-      return data;
-    }
-  }
+  if( (pH->htsize==0 && fts3Rehash(pH,8))
+   || (pH->count>=pH->htsize && fts3Rehash(pH, pH->htsize*2))
+  ){
+    pH->count = 0;
+    return data;
+  }
+  assert( pH->htsize>0 );
   new_elem = (Fts3HashElem*)fts3HashMalloc( sizeof(Fts3HashElem) );
   if( new_elem==0 ) return data;
   if( pH->copyKey && pKey!=0 ){
     new_elem->pKey = fts3HashMalloc( nKey );
     if( new_elem->pKey==0 ){
@@ -101292,13 +101951,10 @@
   }else{
     new_elem->pKey = (void*)pKey;
   }
   new_elem->nKey = nKey;
   pH->count++;
-  if( pH->count > pH->htsize ){
-    fts3Rehash(pH,pH->htsize*2);
-  }
   assert( pH->htsize>0 );
   assert( (pH->htsize & (pH->htsize-1))==0 );
   h = hraw & (pH->htsize-1);
   fts3HashInsertElement(pH, &pH->ht[h], new_elem);
   new_elem->data = data;
@@ -101335,162 +101991,10 @@
 */
 #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
 
 
 
-/************** Include fts3_tokenizer.h in the middle of fts3_porter.c ******/
-/************** Begin file fts3_tokenizer.h **********************************/
-/*
-** 2006 July 10
-**
-** The author disclaims copyright to this source code.
-**
-*************************************************************************
-** Defines the interface to tokenizers used by fulltext-search.  There
-** are three basic components:
-**
-** sqlite3_tokenizer_module is a singleton defining the tokenizer
-** interface functions.  This is essentially the class structure for
-** tokenizers.
-**
-** sqlite3_tokenizer is used to define a particular tokenizer, perhaps
-** including customization information defined at creation time.
-**
-** sqlite3_tokenizer_cursor is generated by a tokenizer to generate
-** tokens from a particular input.
-*/
-#ifndef _FTS3_TOKENIZER_H_
-#define _FTS3_TOKENIZER_H_
-
-/* TODO(shess) Only used for SQLITE_OK and SQLITE_DONE at this time.
-** If tokenizers are to be allowed to call sqlite3_*() functions, then
-** we will need a way to register the API consistently.
-*/
-
-/*
-** Structures used by the tokenizer interface. When a new tokenizer
-** implementation is registered, the caller provides a pointer to
-** an sqlite3_tokenizer_module containing pointers to the callback
-** functions that make up an implementation.
-**
-** When an fts3 table is created, it passes any arguments passed to
-** the tokenizer clause of the CREATE VIRTUAL TABLE statement to the
-** sqlite3_tokenizer_module.xCreate() function of the requested tokenizer
-** implementation. The xCreate() function in turn returns an
-** sqlite3_tokenizer structure representing the specific tokenizer to
-** be used for the fts3 table (customized by the tokenizer clause arguments).
-**
-** To tokenize an input buffer, the sqlite3_tokenizer_module.xOpen()
-** method is called. It returns an sqlite3_tokenizer_cursor object
-** that may be used to tokenize a specific input buffer based on
-** the tokenization rules supplied by a specific sqlite3_tokenizer
-** object.
-*/
-typedef struct sqlite3_tokenizer_module sqlite3_tokenizer_module;
-typedef struct sqlite3_tokenizer sqlite3_tokenizer;
-typedef struct sqlite3_tokenizer_cursor sqlite3_tokenizer_cursor;
-
-struct sqlite3_tokenizer_module {
-
-  /*
-  ** Structure version. Should always be set to 0.
-  */
-  int iVersion;
-
-  /*
-  ** Create a new tokenizer. The values in the argv[] array are the
-  ** arguments passed to the "tokenizer" clause of the CREATE VIRTUAL
-  ** TABLE statement that created the fts3 table. For example, if
-  ** the following SQL is executed:
-  **
-  **   CREATE .. USING fts3( ... , tokenizer <tokenizer-name> arg1 arg2)
-  **
-  ** then argc is set to 2, and the argv[] array contains pointers
-  ** to the strings "arg1" and "arg2".
-  **
-  ** This method should return either SQLITE_OK (0), or an SQLite error
-  ** code. If SQLITE_OK is returned, then *ppTokenizer should be set
-  ** to point at the newly created tokenizer structure. The generic
-  ** sqlite3_tokenizer.pModule variable should not be initialised by
-  ** this callback. The caller will do so.
-  */
-  int (*xCreate)(
-    int argc,                           /* Size of argv array */
-    const char *const*argv,             /* Tokenizer argument strings */
-    sqlite3_tokenizer **ppTokenizer     /* OUT: Created tokenizer */
-  );
-
-  /*
-  ** Destroy an existing tokenizer. The fts3 module calls this method
-  ** exactly once for each successful call to xCreate().
-  */
-  int (*xDestroy)(sqlite3_tokenizer *pTokenizer);
-
-  /*
-  ** Create a tokenizer cursor to tokenize an input buffer. The caller
-  ** is responsible for ensuring that the input buffer remains valid
-  ** until the cursor is closed (using the xClose() method).
-  */
-  int (*xOpen)(
-    sqlite3_tokenizer *pTokenizer,       /* Tokenizer object */
-    const char *pInput, int nBytes,      /* Input buffer */
-    sqlite3_tokenizer_cursor **ppCursor  /* OUT: Created tokenizer cursor */
-  );
-
-  /*
-  ** Destroy an existing tokenizer cursor. The fts3 module calls this
-  ** method exactly once for each successful call to xOpen().
-  */
-  int (*xClose)(sqlite3_tokenizer_cursor *pCursor);
-
-  /*
-  ** Retrieve the next token from the tokenizer cursor pCursor. This
-  ** method should either return SQLITE_OK and set the values of the
-  ** "OUT" variables identified below, or SQLITE_DONE to indicate that
-  ** the end of the buffer has been reached, or an SQLite error code.
-  **
-  ** *ppToken should be set to point at a buffer containing the
-  ** normalized version of the token (i.e. after any case-folding and/or
-  ** stemming has been performed). *pnBytes should be set to the length
-  ** of this buffer in bytes. The input text that generated the token is
-  ** identified by the byte offsets returned in *piStartOffset and
-  ** *piEndOffset. *piStartOffset should be set to the index of the first
-  ** byte of the token in the input buffer. *piEndOffset should be set
-  ** to the index of the first byte just past the end of the token in
-  ** the input buffer.
-  **
-  ** The buffer *ppToken is set to point at is managed by the tokenizer
-  ** implementation. It is only required to be valid until the next call
-  ** to xNext() or xClose().
-  */
-  /* TODO(shess) current implementation requires pInput to be
-  ** nul-terminated.  This should either be fixed, or pInput/nBytes
-  ** should be converted to zInput.
-  */
-  int (*xNext)(
-    sqlite3_tokenizer_cursor *pCursor,   /* Tokenizer cursor */
-    const char **ppToken, int *pnBytes,  /* OUT: Normalized text for token */
-    int *piStartOffset,  /* OUT: Byte offset of token in input buffer */
-    int *piEndOffset,    /* OUT: Byte offset of end of token in input buffer */
-    int *piPosition      /* OUT: Number of tokens returned before this one */
-  );
-};
-
-struct sqlite3_tokenizer {
-  const sqlite3_tokenizer_module *pModule;  /* The module for this tokenizer */
-  /* Tokenizer implementations will typically add additional fields */
-};
-
-struct sqlite3_tokenizer_cursor {
-  sqlite3_tokenizer *pTokenizer;       /* Tokenizer for this cursor. */
-  /* Tokenizer implementations will typically add additional fields */
-};
-
-#endif /* _FTS3_TOKENIZER_H_ */
-
-/************** End of fts3_tokenizer.h **************************************/
-/************** Continuing where we left off in fts3_porter.c ****************/
 
 /*
 ** Class derived from sqlite3_tokenizer
 */
 typedef struct porter_tokenizer {
@@ -101509,22 +102013,22 @@
   char *zToken;                /* storage for current token */
   int nAllocated;              /* space allocated to zToken buffer */
 } porter_tokenizer_cursor;
 
 
-/* Forward declaration */
-static const sqlite3_tokenizer_module porterTokenizerModule;
-
-
 /*
 ** Create a new tokenizer instance.
 */
 static int porterCreate(
   int argc, const char * const *argv,
   sqlite3_tokenizer **ppTokenizer
 ){
   porter_tokenizer *t;
+
+  UNUSED_PARAMETER(argc);
+  UNUSED_PARAMETER(argv);
+
   t = (porter_tokenizer *) sqlite3_malloc(sizeof(*t));
   if( t==NULL ) return SQLITE_NOMEM;
   memset(t, 0, sizeof(*t));
   *ppTokenizer = &t->base;
   return SQLITE_OK;
@@ -101548,10 +102052,12 @@
   sqlite3_tokenizer *pTokenizer,         /* The tokenizer */
   const char *zInput, int nInput,        /* String to be tokenized */
   sqlite3_tokenizer_cursor **ppCursor    /* OUT: Tokenization cursor */
 ){
   porter_tokenizer_cursor *c;
+
+  UNUSED_PARAMETER(pTokenizer);
 
   c = (porter_tokenizer_cursor *) sqlite3_malloc(sizeof(*c));
   if( c==NULL ) return SQLITE_NOMEM;
 
   c->zInput = zInput;
@@ -101689,11 +102195,11 @@
 **
 ** The text is reversed here. So we are really looking at
 ** the first two characters of z[].
 */
 static int doubleConsonant(const char *z){
-  return isConsonant(z) && z[0]==z[1] && isConsonant(z+1);
+  return isConsonant(z) && z[0]==z[1];
 }
 
 /*
 ** Return TRUE if the word ends with three letters which
 ** are consonant-vowel-consonent and where the final consonant
@@ -101702,14 +102208,14 @@
 ** The word is reversed here.  So we are really checking the
 ** first three letters and the first one cannot be in [wxy].
 */
 static int star_oh(const char *z){
   return
-    z[0]!=0 && isConsonant(z) &&
+    isConsonant(z) &&
     z[0]!='w' && z[0]!='x' && z[0]!='y' &&
-    z[1]!=0 && isVowel(z+1) &&
-    z[2]!=0 && isConsonant(z+2);
+    isVowel(z+1) &&
+    isConsonant(z+2);
 }
 
 /*
 ** If the word ends with zFrom and xCond() is true for the stem
 ** of the word that preceeds the zFrom ending, then change the
@@ -101749,11 +102255,11 @@
 */
 static void copy_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
   int i, mx, j;
   int hasDigit = 0;
   for(i=0; i<nIn; i++){
-    int c = zIn[i];
+    char c = zIn[i];
     if( c>='A' && c<='Z' ){
       zOut[i] = c - 'A' + 'a';
     }else{
       if( c>='0' && c<='9' ) hasDigit = 1;
       zOut[i] = c;
@@ -101793,21 +102299,21 @@
 **
 ** Stemming never increases the length of the word.  So there is
 ** no chance of overflowing the zOut buffer.
 */
 static void porter_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
-  int i, j, c;
+  int i, j;
   char zReverse[28];
   char *z, *z2;
   if( nIn<3 || nIn>=sizeof(zReverse)-7 ){
     /* The word is too big or too small for the porter stemmer.
     ** Fallback to the copy stemmer */
     copy_stemmer(zIn, nIn, zOut, pnOut);
     return;
   }
   for(i=0, j=sizeof(zReverse)-6; i<nIn; i++, j--){
-    c = zIn[i];
+    char c = zIn[i];
     if( c>='A' && c<='Z' ){
       zReverse[j] = c + 'a' - 'A';
     }else if( c>='a' && c<='z' ){
       zReverse[j] = c;
     }else{
@@ -102002,11 +102508,11 @@
   }
 
   /* z[] is now the stemmed word in reverse order.  Flip it back
   ** around into forward order and return.
   */
-  *pnOut = i = strlen(z);
+  *pnOut = i = (int)strlen(z);
   zOut[i] = 0;
   while( *z ){
     zOut[--i] = *(z++);
   }
 }
@@ -102240,11 +102746,11 @@
           z1++;
         }
     }
   }
 
-  *pn = (z2-z1);
+  *pn = (int)(z2-z1);
   return z1;
 }
 
 SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(
   Fts3Hash *pHash,                /* Tokenizer hash table */
@@ -102261,11 +102767,10 @@
   sqlite3_tokenizer_module *m;
 
   if( !z ){
     zCopy = sqlite3_mprintf("simple");
   }else{
-    while( (*z&0x80) && isspace(*z) ) z++;
     if( sqlite3_strnicmp(z, "tokenize", 8) || fts3IsIdChar(z[8])){
       return SQLITE_OK;
     }
     zCopy = sqlite3_mprintf("%s", &z[8]);
     *pzTokenizer = zArg;
@@ -102278,24 +102783,24 @@
 
   z = (char *)sqlite3Fts3NextToken(zCopy, &n);
   z[n] = '\0';
   sqlite3Fts3Dequote(z);
 
-  m = (sqlite3_tokenizer_module *)sqlite3Fts3HashFind(pHash, z, strlen(z)+1);
+  m = (sqlite3_tokenizer_module *)sqlite3Fts3HashFind(pHash, z, (int)strlen(z)+1);
   if( !m ){
     *pzErr = sqlite3_mprintf("unknown tokenizer: %s", z);
     rc = SQLITE_ERROR;
   }else{
     char const **aArg = 0;
     int iArg = 0;
     z = &z[n+1];
-    while( z<zEnd && (z = (char *)sqlite3Fts3NextToken(z, &n)) ){
+    while( z<zEnd && (NULL!=(z = (char *)sqlite3Fts3NextToken(z, &n))) ){
       int nNew = sizeof(char *)*(iArg+1);
-      char const **aNew = (const char **)sqlite3_realloc(aArg, nNew);
+      char const **aNew = (const char **)sqlite3_realloc((void *)aArg, nNew);
       if( !aNew ){
         sqlite3_free(zCopy);
-        sqlite3_free(aArg);
+        sqlite3_free((void *)aArg);
         return SQLITE_NOMEM;
       }
       aArg = aNew;
       aArg[iArg++] = z;
       z[n] = '\0';
@@ -102303,15 +102808,15 @@
       z = &z[n+1];
     }
     rc = m->xCreate(iArg, aArg, ppTok);
     assert( rc!=SQLITE_OK || *ppTok );
     if( rc!=SQLITE_OK ){
-      *pzErr = sqlite3_mprintf("unknown tokenizer: %s", z);
+      *pzErr = sqlite3_mprintf("unknown tokenizer");
     }else{
       (*ppTok)->pModule = m;
     }
-    sqlite3_free(aArg);
+    sqlite3_free((void *)aArg);
   }
 
   sqlite3_free(zCopy);
   return rc;
 }
@@ -102473,11 +102978,11 @@
   }
 
   sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC);
   if( SQLITE_ROW==sqlite3_step(pStmt) ){
     if( sqlite3_column_type(pStmt, 0)==SQLITE_BLOB ){
-      memcpy(pp, sqlite3_column_blob(pStmt, 0), sizeof(*pp));
+      memcpy((void *)pp, sqlite3_column_blob(pStmt, 0), sizeof(*pp));
     }
   }
 
   return sqlite3_finalize(pStmt);
 }
@@ -102509,10 +103014,13 @@
 ){
   int rc;
   const sqlite3_tokenizer_module *p1;
   const sqlite3_tokenizer_module *p2;
   sqlite3 *db = (sqlite3 *)sqlite3_user_data(context);
+
+  UNUSED_PARAMETER(argc);
+  UNUSED_PARAMETER(argv);
 
   /* Test the query function */
   sqlite3Fts3SimpleTokenizerModule(&p1);
   rc = queryTokenizer(db, "simple", &p2);
   assert( rc==SQLITE_OK );
@@ -102569,17 +103077,17 @@
   if( !zTest || !zTest2 ){
     rc = SQLITE_NOMEM;
   }
 #endif
 
-  if( rc!=SQLITE_OK
-   || (rc = sqlite3_create_function(db, zName, 1, any, p, scalarFunc, 0, 0))
-   || (rc = sqlite3_create_function(db, zName, 2, any, p, scalarFunc, 0, 0))
+  if( SQLITE_OK!=rc
+   || SQLITE_OK!=(rc = sqlite3_create_function(db, zName, 1, any, p, scalarFunc, 0, 0))
+   || SQLITE_OK!=(rc = sqlite3_create_function(db, zName, 2, any, p, scalarFunc, 0, 0))
 #ifdef SQLITE_TEST
-   || (rc = sqlite3_create_function(db, zTest, 2, any, p, testFunc, 0, 0))
-   || (rc = sqlite3_create_function(db, zTest, 3, any, p, testFunc, 0, 0))
-   || (rc = sqlite3_create_function(db, zTest2, 0, any, pdb, intTestFunc, 0, 0))
+   || SQLITE_OK!=(rc = sqlite3_create_function(db, zTest, 2, any, p, testFunc, 0, 0))
+   || SQLITE_OK!=(rc = sqlite3_create_function(db, zTest, 3, any, p, testFunc, 0, 0))
+   || SQLITE_OK!=(rc = sqlite3_create_function(db, zTest2, 0, any, pdb, intTestFunc, 0, 0))
 #endif
   );
 
   sqlite3_free(zTest);
   sqlite3_free(zTest2);
@@ -102633,13 +103141,10 @@
   char *pToken;                /* storage for current token */
   int nTokenAllocated;         /* space allocated to zToken buffer */
 } simple_tokenizer_cursor;
 
 
-/* Forward declaration */
-static const sqlite3_tokenizer_module simpleTokenizerModule;
-
 static int simpleDelim(simple_tokenizer *t, unsigned char c){
   return c<0x80 && t->delim[c];
 }
 
 /*
@@ -102659,11 +103164,11 @@
   ** else we need to reindex.  One solution would be a meta-table to
   ** track such information in the database, then we'd only want this
   ** information on the initial create.
   */
   if( argc>1 ){
-    int i, n = strlen(argv[1]);
+    int i, n = (int)strlen(argv[1]);
     for(i=0; i<n; i++){
       unsigned char ch = argv[1][i];
       /* We explicitly don't support UTF-8 delimiters for now. */
       if( ch>=0x80 ){
         sqlite3_free(t);
@@ -102673,11 +103178,11 @@
     }
   } else {
     /* Mark non-alphanumeric ASCII characters as delimiters */
     int i;
     for(i=1; i<0x80; i++){
-      t->delim[i] = !isalnum(i);
+      t->delim[i] = !isalnum(i) ? -1 : 0;
     }
   }
 
   *ppTokenizer = &t->base;
   return SQLITE_OK;
@@ -102701,10 +103206,12 @@
   sqlite3_tokenizer *pTokenizer,         /* The tokenizer */
   const char *pInput, int nBytes,        /* String to be tokenized */
   sqlite3_tokenizer_cursor **ppCursor    /* OUT: Tokenization cursor */
 ){
   simple_tokenizer_cursor *c;
+
+  UNUSED_PARAMETER(pTokenizer);
 
   c = (simple_tokenizer_cursor *) sqlite3_malloc(sizeof(*c));
   if( c==NULL ) return SQLITE_NOMEM;
 
   c->pInput = pInput;
@@ -102775,11 +103282,11 @@
       for(i=0; i<n; i++){
         /* TODO(shess) This needs expansion to handle UTF-8
         ** case-insensitivity.
         */
         unsigned char ch = p[iStartOffset+i];
-        c->pToken[i] = ch<0x80 ? tolower(ch) : ch;
+        c->pToken[i] = (char)(ch<0x80 ? tolower(ch) : ch);
       }
       *ppToken = c->pToken;
       *pnBytes = n;
       *piStartOffset = iStartOffset;
       *piEndOffset = c->iOffset;
@@ -102836,13 +103343,10 @@
 ** code in fts3.c.
 */
 
 #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
 
-
-#define INTERIOR_MAX 2048         /* Soft limit for segment node size */
-#define LEAF_MAX 2048             /* Soft limit for segment leaf size */
 
 typedef struct PendingList PendingList;
 typedef struct SegmentNode SegmentNode;
 typedef struct SegmentWriter SegmentWriter;
 
@@ -103092,20 +103596,22 @@
   sqlite3_stmt *pStmt;
   int rc = fts3SqlStmt(p, SQL_GET_BLOCK, &pStmt, 0);
   if( rc!=SQLITE_OK ) return rc;
   sqlite3_reset(pStmt);
 
-  sqlite3_bind_int64(pStmt, 1, iBlock);
-  rc = sqlite3_step(pStmt);
-  if( rc!=SQLITE_ROW ){
-    return SQLITE_CORRUPT;
-  }
-
-  *pnBlock = sqlite3_column_bytes(pStmt, 0);
-  *pzBlock = (char *)sqlite3_column_blob(pStmt, 0);
-  if( !*pzBlock ){
-    return SQLITE_NOMEM;
+  if( pzBlock ){
+    sqlite3_bind_int64(pStmt, 1, iBlock);
+    rc = sqlite3_step(pStmt);
+    if( rc!=SQLITE_ROW ){
+      return SQLITE_CORRUPT;
+    }
+
+    *pnBlock = sqlite3_column_bytes(pStmt, 0);
+    *pzBlock = (char *)sqlite3_column_blob(pStmt, 0);
+    if( !*pzBlock ){
+      return SQLITE_NOMEM;
+    }
   }
   return SQLITE_OK;
 }
 
 /*
@@ -103222,11 +103728,13 @@
     p->iLastPos = 0;
   }
   if( iCol>=0 ){
     assert( iPos>p->iLastPos || (iPos==0 && p->iLastPos==0) );
     rc = fts3PendingListAppendVarint(&p, 2+iPos-p->iLastPos);
-    p->iLastPos = iPos;
+    if( rc==SQLITE_OK ){
+      p->iLastPos = iPos;
+    }
   }
 
  pendinglistappend_out:
   *pRc = rc;
   if( p!=*pp ){
@@ -103371,11 +103879,10 @@
   Fts3Table *p,                   /* Full-text table */
   sqlite3_value **apVal,          /* Array of values to insert */
   sqlite3_int64 *piDocid          /* OUT: Docid for row just inserted */
 ){
   int rc;                         /* Return code */
-  int i;                          /* Iterator variable */
   sqlite3_stmt *pContentInsert;   /* INSERT INTO %_content VALUES(...) */
 
   /* Locate the statement handle used to insert data into the %_content
   ** table. The SQL for this statement is:
   **
@@ -103394,14 +103901,20 @@
   ** Which is a problem, since "rowid" and "docid" are aliases for the
   ** same value. For example:
   **
   **   INSERT INTO fts3tbl(rowid, docid) VALUES(1, 2);
   **
-  ** In FTS3, if a non-NULL docid value is specified, it is the value
-  ** inserted. Otherwise, the rowid value is used.
+  ** In FTS3, this is an error. It is an error to specify non-NULL values
+  ** for both docid and some other rowid alias.
   */
   if( SQLITE_NULL!=sqlite3_value_type(apVal[3+p->nColumn]) ){
+    if( SQLITE_NULL==sqlite3_value_type(apVal[0])
+     && SQLITE_NULL!=sqlite3_value_type(apVal[1])
+    ){
+      /* A rowid/docid conflict. */
+      return SQLITE_ERROR;
+    }
     rc = sqlite3_bind_value(pContentInsert, 1, apVal[3+p->nColumn]);
     if( rc!=SQLITE_OK ) return rc;
   }
 
   /* Execute the statement to insert the record. Set *piDocid to the
@@ -103457,13 +103970,15 @@
           sqlite3_reset(pSelect);
           return rc;
         }
       }
     }
-  }
-
-  return sqlite3_reset(pSelect);
+    rc = sqlite3_reset(pSelect);
+  }else{
+    sqlite3_reset(pSelect);
+  }
+  return rc;
 }
 
 /*
 ** Forward declaration to account for the circular dependency between
 ** functions fts3SegmentMerge() and fts3AllocateSegdirIdx().
@@ -103486,18 +104001,18 @@
 ** returned. Otherwise, an SQLite error code is returned.
 */
 static int fts3AllocateSegdirIdx(Fts3Table *p, int iLevel, int *piIdx){
   int rc;                         /* Return Code */
   sqlite3_stmt *pNextIdx;         /* Query for next idx at level iLevel */
-  int iNext;                      /* Result of query pNextIdx */
+  int iNext = 0;                  /* Result of query pNextIdx */
 
   /* Set variable iNext to the next available segdir index at level iLevel. */
   rc = fts3SqlStmt(p, SQL_NEXT_SEGMENT_INDEX, &pNextIdx, 0);
   if( rc==SQLITE_OK ){
     sqlite3_bind_int(pNextIdx, 1, iLevel);
     if( SQLITE_ROW==sqlite3_step(pNextIdx) ){
-      iNext = sqlite3_column_int64(pNextIdx, 0);
+      iNext = sqlite3_column_int(pNextIdx, 0);
     }
     rc = sqlite3_reset(pNextIdx);
   }
 
   if( rc==SQLITE_OK ){
@@ -103611,11 +104126,11 @@
   /* If required, populate the output variables with a pointer to and the
   ** size of the previous offset-list.
   */
   if( ppOffsetList ){
     *ppOffsetList = pReader->pOffsetList;
-    *pnOffsetList = p - pReader->pOffsetList - 1;
+    *pnOffsetList = (int)(p - pReader->pOffsetList - 1);
   }
 
   /* If there are no more entries in the doclist, set pOffsetList to
   ** NULL. Otherwise, set Fts3SegReader.iDocid to the next docid and
   ** Fts3SegReader.pOffsetList to point to the next offset list before
@@ -103683,12 +104198,10 @@
     /* The entire segment is stored in the root node. */
     pReader->aNode = (char *)&pReader[1];
     pReader->nNode = nRoot;
     memcpy(pReader->aNode, zRoot, nRoot);
   }else{
-    sqlite3_stmt *pStmt;
-
     /* If the text of the SQL statement to iterate through a contiguous
     ** set of entries in the %_segments table has not yet been composed,
     ** compose it now.
     */
     if( !p->zSelectLeaves ){
@@ -103966,10 +104479,11 @@
   int nPrev,                      /* Size of buffer zPrev in bytes */
   const char *zNext,              /* Buffer containing next term */
   int nNext                       /* Size of buffer zNext in bytes */
 ){
   int n;
+  UNUSED_PARAMETER(nNext);
   for(n=0; n<nPrev && zPrev[n]==zNext[n]; n++);
   return n;
 }
 
 /*
@@ -103998,17 +104512,17 @@
 
     nPrefix = fts3PrefixCompress(pTree->zTerm, pTree->nTerm, zTerm, nTerm);
     nSuffix = nTerm-nPrefix;
 
     nReq += sqlite3Fts3VarintLen(nPrefix)+sqlite3Fts3VarintLen(nSuffix)+nSuffix;
-    if( nReq<=INTERIOR_MAX || !pTree->zTerm ){
-
-      if( nReq>INTERIOR_MAX ){
+    if( nReq<=p->nNodeSize || !pTree->zTerm ){
+
+      if( nReq>p->nNodeSize ){
         /* An unusual case: this is the first term to be added to the node
-        ** and the static node buffer (INTERIOR_MAX bytes) is not large
+        ** and the static node buffer (p->nNodeSize bytes) is not large
         ** enough. Use a separately malloced buffer instead This wastes
-        ** INTERIOR_MAX bytes, but since this scenario only comes about when
+        ** p->nNodeSize bytes, but since this scenario only comes about when
         ** the database contain two terms that share a prefix of almost 2KB,
         ** this is not expected to be a serious problem.
         */
         assert( pTree->aData==(char *)&pTree[1] );
         pTree->aData = (char *)sqlite3_malloc(nReq);
@@ -104053,11 +104567,11 @@
   **
   ** Otherwise, the term is not added to the new node, it is left empty for
   ** now. Instead, the term is inserted into the parent of pTree. If pTree
   ** has no parent, one is created here.
   */
-  pNew = (SegmentNode *)sqlite3_malloc(sizeof(SegmentNode) + INTERIOR_MAX);
+  pNew = (SegmentNode *)sqlite3_malloc(sizeof(SegmentNode) + p->nNodeSize);
   if( !pNew ){
     return SQLITE_NOMEM;
   }
   memset(pNew, 0, sizeof(SegmentNode));
   pNew->nData = 1 + FTS3_VARINT_MAX;
@@ -104206,13 +104720,13 @@
     if( !pWriter ) return SQLITE_NOMEM;
     memset(pWriter, 0, sizeof(SegmentWriter));
     *ppWriter = pWriter;
 
     /* Allocate a buffer in which to accumulate data */
-    pWriter->aData = (char *)sqlite3_malloc(LEAF_MAX);
+    pWriter->aData = (char *)sqlite3_malloc(p->nNodeSize);
     if( !pWriter->aData ) return SQLITE_NOMEM;
-    pWriter->nSize = LEAF_MAX;
+    pWriter->nSize = p->nNodeSize;
 
     /* Find the next free blockid in the %_segments table */
     rc = fts3SqlStmt(p, SQL_NEXT_SEGMENTS_ID, &pStmt, 0);
     if( rc!=SQLITE_OK ) return rc;
     if( SQLITE_ROW==sqlite3_step(pStmt) ){
@@ -104232,11 +104746,11 @@
     sqlite3Fts3VarintLen(nSuffix) +         /* varint containing suffix size */
     nSuffix +                               /* Term suffix */
     sqlite3Fts3VarintLen(nDoclist) +        /* Size of doclist */
     nDoclist;                               /* Doclist data */
 
-  if( nData>0 && nData+nReq>LEAF_MAX ){
+  if( nData>0 && nData+nReq>p->nNodeSize ){
     int rc;
 
     /* The current leaf node is full. Write it out to the database. */
     rc = fts3WriteSegment(p, pWriter->iFree++, pWriter->aData, nData);
     if( rc!=SQLITE_OK ) return rc;
@@ -104326,14 +104840,14 @@
   int iLevel,                     /* Value for 'level' column of %_segdir */
   int iIdx                        /* Value for 'idx' column of %_segdir */
 ){
   int rc;                         /* Return code */
   if( pWriter->pTree ){
-    sqlite3_int64 iLast;          /* Largest block id written to database */
+    sqlite3_int64 iLast = 0;      /* Largest block id written to database */
     sqlite3_int64 iLastLeaf;      /* Largest leaf block id written to db */
-    char *zRoot;                  /* Pointer to buffer containing root node */
-    int nRoot;                    /* Size of buffer zRoot */
+    char *zRoot = NULL;           /* Pointer to buffer containing root node */
+    int nRoot = 0;                /* Size of buffer zRoot */
 
     iLastLeaf = pWriter->iFree;
     rc = fts3WriteSegment(p, pWriter->iFree++, pWriter->aData, pWriter->nData);
     if( rc==SQLITE_OK ){
       rc = fts3NodeWrite(p, pWriter->pTree, 1,
@@ -104502,15 +105016,15 @@
   while( 1 ){
     char c = 0;
     while( p<pEnd && (c | *p)&0xFE ) c = *p++ & 0x80;
 
     if( iCol==iCurrent ){
-      nList = (p - pList);
+      nList = (int)(p - pList);
       break;
     }
 
-    nList -= (p - pList);
+    nList -= (int)(p - pList);
     pList = p;
     if( nList==0 ){
       break;
     }
     p = &pList[1];
@@ -104577,10 +105091,15 @@
 
   int isIgnoreEmpty =  (pFilter->flags & FTS3_SEGMENT_IGNORE_EMPTY);
   int isRequirePos =   (pFilter->flags & FTS3_SEGMENT_REQUIRE_POS);
   int isColFilter =    (pFilter->flags & FTS3_SEGMENT_COLUMN_FILTER);
   int isPrefix =       (pFilter->flags & FTS3_SEGMENT_PREFIX);
+
+  /* If there are zero segments, this function is a no-op. This scenario
+  ** comes about only when reading from an empty database.
+  */
+  if( nSegment==0 ) goto finished;
 
   /* If the Fts3SegFilter defines a specific term (or term prefix) to search
   ** for, then advance each segment iterator until it points to a term of
   ** equal or greater value than the specified term. This prevents many
   ** unnecessary merge/sort operations for the case where single segment
@@ -104906,11 +105425,11 @@
   sqlite_int64 *pRowid            /* OUT: The affected (or effected) rowid */
 ){
   Fts3Table *p = (Fts3Table *)pVtab;
   int rc = SQLITE_OK;             /* Return Code */
   int isRemove = 0;               /* True for an UPDATE or DELETE */
-  sqlite3_int64 iRemove;          /* Rowid removed by UPDATE or DELETE */
+  sqlite3_int64 iRemove = 0;      /* Rowid removed by UPDATE or DELETE */
 
   /* If this is a DELETE or UPDATE operation, remove the old record. */
   if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
     int isEmpty;
     rc = fts3IsEmpty(p, apVal, &isEmpty);
@@ -104953,13 +105472,22 @@
 ** Flush any data in the pending-terms hash table to disk. If successful,
 ** merge all segments in the database (including the new segment, if
 ** there was any data to flush) into a single segment.
 */
 SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *p){
-  int rc = sqlite3Fts3PendingTermsFlush(p);
+  int rc;
+  rc = sqlite3_exec(p->db, "SAVEPOINT fts3", 0, 0, 0);
   if( rc==SQLITE_OK ){
-    rc = fts3SegmentMerge(p, -1);
+    rc = sqlite3Fts3PendingTermsFlush(p);
+    if( rc==SQLITE_OK ){
+      rc = fts3SegmentMerge(p, -1);
+    }
+    if( rc==SQLITE_OK ){
+      rc = sqlite3_exec(p->db, "RELEASE fts3", 0, 0, 0);
+    }else{
+      sqlite3_exec(p->db, "ROLLBACK TO fts3 ; RELEASE fts3", 0, 0, 0);
+    }
   }
   return rc;
 }
 
 #endif
@@ -104991,14 +105519,14 @@
 struct Snippet {
   int nMatch;                     /* Total number of matches */
   int nAlloc;                     /* Space allocated for aMatch[] */
   struct snippetMatch {  /* One entry for each matching term */
     char snStatus;       /* Status flag for use while constructing snippets */
+    short int nByte;     /* Number of bytes in the term */
     short int iCol;      /* The column that contains the match */
     short int iTerm;     /* The index in Query.pTerms[] of the matching term */
     int iToken;          /* The index of the matching document token */
-    short int nByte;     /* Number of bytes in the term */
     int iStart;          /* The offset to the first character of the term */
   } *aMatch;                      /* Points to space obtained from malloc */
   char *zOffset;                  /* Text rendering of aMatch[] */
   int nOffset;                    /* strlen(zOffset) */
   char *zSnippet;                 /* Snippet text */
@@ -105008,171 +105536,125 @@
 
 /* It is not safe to call isspace(), tolower(), or isalnum() on
 ** hi-bit-set characters.  This is the same solution used in the
 ** tokenizer.
 */
-/* TODO(shess) The snippet-generation code should be using the
-** tokenizer-generated tokens rather than doing its own local
-** tokenization.
-*/
-/* TODO(shess) Is __isascii() a portable version of (c&0x80)==0? */
-static int safe_isspace(char c){
+static int fts3snippetIsspace(char c){
   return (c&0x80)==0 ? isspace(c) : 0;
 }
-static int safe_isalnum(char c){
-  return (c&0x80)==0 ? isalnum(c) : 0;
-}
-
-/*******************************************************************/
-/* DataBuffer is used to collect data into a buffer in piecemeal
-** fashion.  It implements the usual distinction between amount of
-** data currently stored (nData) and buffer capacity (nCapacity).
-**
-** dataBufferInit - create a buffer with given initial capacity.
-** dataBufferReset - forget buffer's data, retaining capacity.
-** dataBufferSwap - swap contents of two buffers.
-** dataBufferExpand - expand capacity without adding data.
-** dataBufferAppend - append data.
-** dataBufferAppend2 - append two pieces of data at once.
-** dataBufferReplace - replace buffer's data.
-*/
-typedef struct DataBuffer {
-  char *pData;          /* Pointer to malloc'ed buffer. */
-  int nCapacity;        /* Size of pData buffer. */
-  int nData;            /* End of data loaded into pData. */
-} DataBuffer;
-
-static void dataBufferInit(DataBuffer *pBuffer, int nCapacity){
-  assert( nCapacity>=0 );
-  pBuffer->nData = 0;
-  pBuffer->nCapacity = nCapacity;
-  pBuffer->pData = nCapacity==0 ? NULL : sqlite3_malloc(nCapacity);
-}
-static void dataBufferReset(DataBuffer *pBuffer){
-  pBuffer->nData = 0;
-}
-static void dataBufferExpand(DataBuffer *pBuffer, int nAddCapacity){
-  assert( nAddCapacity>0 );
-  /* TODO(shess) Consider expanding more aggressively.  Note that the
-  ** underlying malloc implementation may take care of such things for
-  ** us already.
-  */
-  if( pBuffer->nData+nAddCapacity>pBuffer->nCapacity ){
-    pBuffer->nCapacity = pBuffer->nData+nAddCapacity;
-    pBuffer->pData = sqlite3_realloc(pBuffer->pData, pBuffer->nCapacity);
-  }
-}
-static void dataBufferAppend(DataBuffer *pBuffer,
-                             const char *pSource, int nSource){
-  assert( nSource>0 && pSource!=NULL );
-  dataBufferExpand(pBuffer, nSource);
-  memcpy(pBuffer->pData+pBuffer->nData, pSource, nSource);
-  pBuffer->nData += nSource;
-}
-static void dataBufferAppend2(DataBuffer *pBuffer,
-                              const char *pSource1, int nSource1,
-                              const char *pSource2, int nSource2){
-  assert( nSource1>0 && pSource1!=NULL );
-  assert( nSource2>0 && pSource2!=NULL );
-  dataBufferExpand(pBuffer, nSource1+nSource2);
-  memcpy(pBuffer->pData+pBuffer->nData, pSource1, nSource1);
-  memcpy(pBuffer->pData+pBuffer->nData+nSource1, pSource2, nSource2);
-  pBuffer->nData += nSource1+nSource2;
-}
-static void dataBufferReplace(DataBuffer *pBuffer,
-                              const char *pSource, int nSource){
-  dataBufferReset(pBuffer);
-  dataBufferAppend(pBuffer, pSource, nSource);
-}
-
-
-/* StringBuffer is a null-terminated version of DataBuffer. */
+
+
+/*
+** A StringBuffer object holds a zero-terminated string that grows
+** arbitrarily by appending.  Space to hold the string is obtained
+** from sqlite3_malloc().  After any memory allocation failure,
+** StringBuffer.z is set to NULL and no further allocation is attempted.
+*/
 typedef struct StringBuffer {
-  DataBuffer b;            /* Includes null terminator. */
+  char *z;         /* Text of the string.  Space from malloc. */
+  int nUsed;       /* Number bytes of z[] used, not counting \000 terminator */
+  int nAlloc;      /* Bytes allocated for z[] */
 } StringBuffer;
 
-static void initStringBuffer(StringBuffer *sb){
-  dataBufferInit(&sb->b, 100);
-  dataBufferReplace(&sb->b, "", 1);
-}
-static int stringBufferLength(StringBuffer *sb){
-  return sb->b.nData-1;
-}
-static char *stringBufferData(StringBuffer *sb){
-  return sb->b.pData;
-}
-
-static void nappend(StringBuffer *sb, const char *zFrom, int nFrom){
-  assert( sb->b.nData>0 );
-  if( nFrom>0 ){
-    sb->b.nData--;
-    dataBufferAppend2(&sb->b, zFrom, nFrom, "", 1);
-  }
-}
-static void append(StringBuffer *sb, const char *zFrom){
-  nappend(sb, zFrom, strlen(zFrom));
-}
-
-static int endsInWhiteSpace(StringBuffer *p){
-  return stringBufferLength(p)>0 &&
-    safe_isspace(stringBufferData(p)[stringBufferLength(p)-1]);
+
+/*
+** Initialize a new StringBuffer.
+*/
+static void fts3SnippetSbInit(StringBuffer *p){
+  p->nAlloc = 100;
+  p->nUsed = 0;
+  p->z = sqlite3_malloc( p->nAlloc );
+}
+
+/*
+** Append text to the string buffer.
+*/
+static void fts3SnippetAppend(StringBuffer *p, const char *zNew, int nNew){
+  if( p->z==0 ) return;
+  if( nNew<0 ) nNew = (int)strlen(zNew);
+  if( p->nUsed + nNew >= p->nAlloc ){
+    int nAlloc;
+    char *zNew;
+
+    nAlloc = p->nUsed + nNew + p->nAlloc;
+    zNew = sqlite3_realloc(p->z, nAlloc);
+    if( zNew==0 ){
+      sqlite3_free(p->z);
+      p->z = 0;
+      return;
+    }
+    p->z = zNew;
+    p->nAlloc = nAlloc;
+  }
+  memcpy(&p->z[p->nUsed], zNew, nNew);
+  p->nUsed += nNew;
+  p->z[p->nUsed] = 0;
 }
 
 /* If the StringBuffer ends in something other than white space, add a
 ** single space character to the end.
 */
-static void appendWhiteSpace(StringBuffer *p){
-  if( stringBufferLength(p)==0 ) return;
-  if( !endsInWhiteSpace(p) ) append(p, " ");
+static void fts3SnippetAppendWhiteSpace(StringBuffer *p){
+  if( p->z && p->nUsed && !fts3snippetIsspace(p->z[p->nUsed-1]) ){
+    fts3SnippetAppend(p, " ", 1);
+  }
 }
 
 /* Remove white space from the end of the StringBuffer */
-static void trimWhiteSpace(StringBuffer *p){
-  while( endsInWhiteSpace(p) ){
-    p->b.pData[--p->b.nData-1] = '\0';
-  }
-}
-
+static void fts3SnippetTrimWhiteSpace(StringBuffer *p){
+  if( p->z ){
+    while( p->nUsed && fts3snippetIsspace(p->z[p->nUsed-1]) ){
+      p->nUsed--;
+    }
+    p->z[p->nUsed] = 0;
+  }
+}
 
 /*
 ** Release all memory associated with the Snippet structure passed as
 ** an argument.
 */
 static void fts3SnippetFree(Snippet *p){
-  sqlite3_free(p->aMatch);
-  sqlite3_free(p->zOffset);
-  sqlite3_free(p->zSnippet);
-  sqlite3_free(p);
+  if( p ){
+    sqlite3_free(p->aMatch);
+    sqlite3_free(p->zOffset);
+    sqlite3_free(p->zSnippet);
+    sqlite3_free(p);
+  }
 }
 
 /*
 ** Append a single entry to the p->aMatch[] log.
 */
-static void snippetAppendMatch(
+static int snippetAppendMatch(
   Snippet *p,               /* Append the entry to this snippet */
   int iCol, int iTerm,      /* The column and query term */
   int iToken,               /* Matching token in document */
   int iStart, int nByte     /* Offset and size of the match */
 ){
   int i;
   struct snippetMatch *pMatch;
   if( p->nMatch+1>=p->nAlloc ){
+    struct snippetMatch *pNew;
     p->nAlloc = p->nAlloc*2 + 10;
-    p->aMatch = sqlite3_realloc(p->aMatch, p->nAlloc*sizeof(p->aMatch[0]) );
-    if( p->aMatch==0 ){
+    pNew = sqlite3_realloc(p->aMatch, p->nAlloc*sizeof(p->aMatch[0]) );
+    if( pNew==0 ){
+      p->aMatch = 0;
       p->nMatch = 0;
       p->nAlloc = 0;
-      return;
-    }
+      return SQLITE_NOMEM;
+    }
+    p->aMatch = pNew;
   }
   i = p->nMatch++;
   pMatch = &p->aMatch[i];
-  pMatch->iCol = iCol;
-  pMatch->iTerm = iTerm;
+  pMatch->iCol = (short)iCol;
+  pMatch->iTerm = (short)iTerm;
   pMatch->iToken = iToken;
   pMatch->iStart = iStart;
-  pMatch->nByte = nByte;
+  pMatch->nByte = (short)nByte;
+  return SQLITE_OK;
 }
 
 /*
 ** Sizing information for the circular buffer used in snippetOffsetsOfColumn()
 */
@@ -105242,11 +105724,11 @@
 
 /*
 ** Add entries to pSnippet->aMatch[] for every match that occurs against
 ** document zDoc[0..nDoc-1] which is stored in column iColumn.
 */
-static void snippetOffsetsOfColumn(
+static int snippetOffsetsOfColumn(
   Fts3Cursor *pCur,         /* The fulltest search cursor */
   Snippet *pSnippet,             /* The Snippet object to be filled in */
   int iColumn,                   /* Index of fulltext table column */
   const char *zDoc,              /* Text of the fulltext table column */
   int nDoc                       /* Length of zDoc in bytes */
@@ -105272,15 +105754,16 @@
   pVtab =  (Fts3Table *)pCur->base.pVtab;
   nColumn = pVtab->nColumn;
   pTokenizer = pVtab->pTokenizer;
   pTModule = pTokenizer->pModule;
   rc = pTModule->xOpen(pTokenizer, zDoc, nDoc, &pTCursor);
-  if( rc ) return;
+  if( rc ) return rc;
   pTCursor->pTokenizer = pTokenizer;
 
   prevMatch = 0;
-  while( !pTModule->xNext(pTCursor, &zToken, &nToken, &iBegin, &iEnd, &iPos) ){
+  while( (rc = pTModule->xNext(pTCursor, &zToken, &nToken,
+                               &iBegin, &iEnd, &iPos))==SQLITE_OK ){
     Fts3Expr *pIter = pCur->pExpr;
     int iIter = -1;
     iRotorBegin[iRotor&FTS3_ROTOR_MASK] = iBegin;
     iRotorLen[iRotor&FTS3_ROTOR_MASK] = iEnd-iBegin;
     match = 0;
@@ -105301,19 +105784,22 @@
       if( iIter>0 && (prevMatch & (1<<i))==0 ) continue;
       match |= 1<<i;
       if( i==(FTS3_ROTOR_SZ-2) || nPhrase==iIter+1 ){
         for(j=nPhrase-1; j>=0; j--){
           int k = (iRotor-j) & FTS3_ROTOR_MASK;
-          snippetAppendMatch(pSnippet, iColumn, i-j, iPos-j,
-                iRotorBegin[k], iRotorLen[k]);
+          rc = snippetAppendMatch(pSnippet, iColumn, i-j, iPos-j,
+                                  iRotorBegin[k], iRotorLen[k]);
+          if( rc ) goto end_offsets_of_column;
         }
       }
     }
     prevMatch = match<<1;
     iRotor++;
   }
+end_offsets_of_column:
   pTModule->xClose(pTCursor);
+  return rc==SQLITE_DONE ? SQLITE_OK : rc;
 }
 
 /*
 ** Remove entries from the pSnippet structure to account for the NEAR
 ** operator. When this is called, pSnippet contains the list of token
@@ -105445,16 +105931,19 @@
 /*
 ** Compute all offsets for the current row of the query.
 ** If the offsets have already been computed, this routine is a no-op.
 */
 static int snippetAllOffsets(Fts3Cursor *pCsr, Snippet **ppSnippet){
-  Fts3Table *p = (Fts3Table *)pCsr->base.pVtab;
-  int nColumn;
-  int iColumn, i;
-  int iFirst, iLast;
+  Fts3Table *p = (Fts3Table *)pCsr->base.pVtab;  /* The FTS3 virtual table */
+  int nColumn;           /* Number of columns.  Docid does count */
+  int iColumn;           /* Index of of a column */
+  int i;                 /* Loop index */
+  int iFirst;            /* First column to search */
+  int iLast;             /* Last coumn to search */
   int iTerm = 0;
   Snippet *pSnippet;
+  int rc = SQLITE_OK;
 
   if( pCsr->pExpr==0 ){
     return SQLITE_OK;
   }
 
@@ -105464,33 +105953,37 @@
     return SQLITE_NOMEM;
   }
   memset(pSnippet, 0, sizeof(Snippet));
 
   nColumn = p->nColumn;
-  iColumn = (pCsr->eType - 2);
+  iColumn = (pCsr->eSearch - 2);
   if( iColumn<0 || iColumn>=nColumn ){
     /* Look for matches over all columns of the full-text index */
     iFirst = 0;
     iLast = nColumn-1;
   }else{
     /* Look for matches in the iColumn-th column of the index only */
     iFirst = iColumn;
     iLast = iColumn;
   }
-  for(i=iFirst; i<=iLast; i++){
+  for(i=iFirst; rc==SQLITE_OK && i<=iLast; i++){
     const char *zDoc;
     int nDoc;
     zDoc = (const char*)sqlite3_column_text(pCsr->pStmt, i+1);
     nDoc = sqlite3_column_bytes(pCsr->pStmt, i+1);
-    snippetOffsetsOfColumn(pCsr, pSnippet, i, zDoc, nDoc);
+    if( zDoc==0 && sqlite3_column_type(pCsr->pStmt, i+1)!=SQLITE_NULL ){
+      rc = SQLITE_NOMEM;
+    }else{
+      rc = snippetOffsetsOfColumn(pCsr, pSnippet, i, zDoc, nDoc);
+    }
   }
 
   while( trimSnippetOffsets(pCsr->pExpr, pSnippet, &iTerm) ){
     iTerm = 0;
   }
 
-  return SQLITE_OK;
+  return rc;
 }
 
 /*
 ** Convert the information in the aMatch[] array of the snippet
 ** into the string zOffset[0..nOffset-1]. This string is used as
@@ -105500,11 +105993,11 @@
   int i;
   int cnt = 0;
   StringBuffer sb;
   char zBuf[200];
   if( p->zOffset ) return;
-  initStringBuffer(&sb);
+  fts3SnippetSbInit(&sb);
   for(i=0; i<p->nMatch; i++){
     struct snippetMatch *pMatch = &p->aMatch[i];
     if( pMatch->iTerm>=0 ){
       /* If snippetMatch.iTerm is less than 0, then the match was
       ** discarded as part of processing the NEAR operator (see the
@@ -105512,16 +106005,16 @@
       ** it in this case
       */
       zBuf[0] = ' ';
       sqlite3_snprintf(sizeof(zBuf)-1, &zBuf[cnt>0], "%d %d %d %d",
           pMatch->iCol, pMatch->iTerm, pMatch->iStart, pMatch->nByte);
-      append(&sb, zBuf);
+      fts3SnippetAppend(&sb, zBuf, -1);
       cnt++;
     }
   }
-  p->zOffset = stringBufferData(&sb);
-  p->nOffset = stringBufferLength(&sb);
+  p->zOffset = sb.z;
+  p->nOffset = sb.z ? sb.nUsed : 0;
 }
 
 /*
 ** zDoc[0..nDoc-1] is phrase of text.  aMatch[0..nMatch-1] are a set
 ** of matching words some of which might be in zDoc.  zDoc is column
@@ -105544,11 +106037,11 @@
     return 0;
   }
   if( iBreak>=nDoc-10 ){
     return nDoc;
   }
-  for(i=0; i<nMatch && aMatch[i].iCol<iCol; i++){}
+  for(i=0; ALWAYS(i<nMatch) && aMatch[i].iCol<iCol; i++){}
   while( i<nMatch && aMatch[i].iStart+aMatch[i].nByte<iBreak ){ i++; }
   if( i<nMatch ){
     if( aMatch[i].iStart<iBreak+10 ){
       return aMatch[i].iStart;
     }
@@ -105555,14 +106048,14 @@
     if( i>0 && aMatch[i-1].iStart+aMatch[i-1].nByte>=iBreak ){
       return aMatch[i-1].iStart;
     }
   }
   for(i=1; i<=10; i++){
-    if( safe_isspace(zDoc[iBreak-i]) ){
+    if( fts3snippetIsspace(zDoc[iBreak-i]) ){
       return iBreak - i + 1;
     }
-    if( safe_isspace(zDoc[iBreak+i]) ){
+    if( fts3snippetIsspace(zDoc[iBreak+i]) ){
       return iBreak + i + 1;
     }
   }
   return iBreak;
 }
@@ -105602,11 +106095,11 @@
 
   sqlite3_free(pSnippet->zSnippet);
   pSnippet->zSnippet = 0;
   aMatch = pSnippet->aMatch;
   nMatch = pSnippet->nMatch;
-  initStringBuffer(&sb);
+  fts3SnippetSbInit(&sb);
 
   for(i=0; i<nMatch; i++){
     aMatch[i].snStatus = SNIPPET_IGNORE;
   }
   nDesired = 0;
@@ -105636,14 +106129,14 @@
     }
     if( iCol==tailCol && iStart<=tailOffset+20 ){
       iStart = tailOffset;
     }
     if( (iCol!=tailCol && tailCol>=0) || iStart!=tailOffset ){
-      trimWhiteSpace(&sb);
-      appendWhiteSpace(&sb);
-      append(&sb, zEllipsis);
-      appendWhiteSpace(&sb);
+      fts3SnippetTrimWhiteSpace(&sb);
+      fts3SnippetAppendWhiteSpace(&sb);
+      fts3SnippetAppend(&sb, zEllipsis, -1);
+      fts3SnippetAppendWhiteSpace(&sb);
     }
     iEnd = aMatch[i].iStart + aMatch[i].nByte + 40;
     iEnd = wordBoundary(iEnd, zDoc, nDoc, aMatch, nMatch, iCol);
     if( iEnd>=nDoc-10 ){
       iEnd = nDoc;
@@ -105657,48 +106150,56 @@
              && aMatch[iMatch].iCol<=iCol ){
         iMatch++;
       }
       if( iMatch<nMatch && aMatch[iMatch].iStart<iEnd
              && aMatch[iMatch].iCol==iCol ){
-        nappend(&sb, &zDoc[iStart], aMatch[iMatch].iStart - iStart);
+        fts3SnippetAppend(&sb, &zDoc[iStart], aMatch[iMatch].iStart - iStart);
         iStart = aMatch[iMatch].iStart;
-        append(&sb, zStartMark);
-        nappend(&sb, &zDoc[iStart], aMatch[iMatch].nByte);
-        append(&sb, zEndMark);
+        fts3SnippetAppend(&sb, zStartMark, -1);
+        fts3SnippetAppend(&sb, &zDoc[iStart], aMatch[iMatch].nByte);
+        fts3SnippetAppend(&sb, zEndMark, -1);
         iStart += aMatch[iMatch].nByte;
         for(j=iMatch+1; j<nMatch; j++){
           if( aMatch[j].iTerm==aMatch[iMatch].iTerm
               && aMatch[j].snStatus==SNIPPET_DESIRED ){
             nDesired--;
             aMatch[j].snStatus = SNIPPET_IGNORE;
           }
         }
       }else{
-        nappend(&sb, &zDoc[iStart], iEnd - iStart);
+        fts3SnippetAppend(&sb, &zDoc[iStart], iEnd - iStart);
         iStart = iEnd;
       }
     }
     tailCol = iCol;
     tailOffset = iEnd;
   }
-  trimWhiteSpace(&sb);
+  fts3SnippetTrimWhiteSpace(&sb);
   if( tailEllipsis ){
-    appendWhiteSpace(&sb);
-    append(&sb, zEllipsis);
-  }
-  pSnippet->zSnippet = stringBufferData(&sb);
-  pSnippet->nSnippet = stringBufferLength(&sb);
+    fts3SnippetAppendWhiteSpace(&sb);
+    fts3SnippetAppend(&sb, zEllipsis, -1);
+  }
+  pSnippet->zSnippet = sb.z;
+  pSnippet->nSnippet = sb.z ? sb.nUsed : 0;
 }
 
 SQLITE_PRIVATE void sqlite3Fts3Offsets(
   sqlite3_context *pCtx,          /* SQLite function call context */
   Fts3Cursor *pCsr                /* Cursor object */
 ){
   Snippet *p;                     /* Snippet structure */
   int rc = snippetAllOffsets(pCsr, &p);
-  snippetOffsetText(p);
-  sqlite3_result_text(pCtx, p->zOffset, p->nOffset, SQLITE_TRANSIENT);
+  if( rc==SQLITE_OK ){
+    snippetOffsetText(p);
+    if( p->zOffset ){
+      sqlite3_result_text(pCtx, p->zOffset, p->nOffset, SQLITE_TRANSIENT);
+    }else{
+      sqlite3_result_error_nomem(pCtx);
+    }
+  }else{
+    sqlite3_result_error_nomem(pCtx);
+  }
   fts3SnippetFree(p);
 }
 
 SQLITE_PRIVATE void sqlite3Fts3Snippet(
   sqlite3_context *pCtx,          /* SQLite function call context */
@@ -105707,12 +106208,20 @@
   const char *zEnd,               /* Snippet end text - "</b>" */
   const char *zEllipsis           /* Snippet ellipsis text - "<b>...</b>" */
 ){
   Snippet *p;                     /* Snippet structure */
   int rc = snippetAllOffsets(pCsr, &p);
-  snippetText(pCsr, p, zStart, zEnd, zEllipsis);
-  sqlite3_result_text(pCtx, p->zSnippet, p->nSnippet, SQLITE_TRANSIENT);
+  if( rc==SQLITE_OK ){
+    snippetText(pCsr, p, zStart, zEnd, zEllipsis);
+    if( p->zSnippet ){
+      sqlite3_result_text(pCtx, p->zSnippet, p->nSnippet, SQLITE_TRANSIENT);
+    }else{
+      sqlite3_result_error_nomem(pCtx);
+    }
+  }else{
+    sqlite3_result_error_nomem(pCtx);
+  }
   fts3SnippetFree(p);
 }
 
 #endif
 

Modified src/sqlite3.h from [260f444797] to [99a4282d9b].

@@ -119,11 +119,11 @@
 **
 ** Requirements: [H10011] [H10014]
 */
 #define SQLITE_VERSION        "3.6.21"
 #define SQLITE_VERSION_NUMBER 3006021
-#define SQLITE_SOURCE_ID      "2009-11-25 21:05:09 5086bf8e838c824accda531afeb56a51dd40d795"
+#define SQLITE_SOURCE_ID      "2009-12-07 16:39:13 1ed88e9d01e9eda5cbc622e7614277f29bcc551c"
 
 /*
 ** CAPI3REF: Run-Time Library Version Numbers {H10020} <S60100>
 ** KEYWORDS: sqlite3_version
 **
@@ -1293,10 +1293,13 @@
 ** the return value of this interface.
 **
 ** For the purposes of this routine, an [INSERT] is considered to
 ** be successful even if it is subsequently rolled back.
 **
+** This function is accessible to SQL statements via the
+** [last_insert_rowid() SQL function].
+**
 ** Requirements:
 ** [H12221] [H12223]
 **
 ** If a separate thread performs a new [INSERT] on the same
 ** database connection while the [sqlite3_last_insert_rowid()]
@@ -1350,12 +1353,12 @@
 ** changes in the most recently completed INSERT, UPDATE, or DELETE
 ** statement within the body of the same trigger.
 ** However, the number returned does not include changes
 ** caused by subtriggers since those have their own context.
 **
-** See also the [sqlite3_total_changes()] interface and the
-** [count_changes pragma].
+** See also the [sqlite3_total_changes()] interface, the
+** [count_changes pragma], and the [changes() SQL function].
 **
 ** Requirements:
 ** [H12241] [H12243]
 **
 ** If a separate thread makes changes on the same database connection
@@ -1378,12 +1381,12 @@
 ** are counted.
 ** The changes are counted as soon as the statement that makes them is
 ** completed (when the statement handle is passed to [sqlite3_reset()] or
 ** [sqlite3_finalize()]).
 **
-** See also the [sqlite3_changes()] interface and the
-** [count_changes pragma].
+** See also the [sqlite3_changes()] interface, the
+** [count_changes pragma], and the [total_changes() SQL function].
 **
 ** Requirements:
 ** [H12261] [H12263]
 **
 ** If a separate thread makes changes on the same database connection
@@ -4140,10 +4143,12 @@
 **          should free this memory by calling [sqlite3_free()].
 **
 ** {H12606} Extension loading must be enabled using
 **          [sqlite3_enable_load_extension()] prior to calling this API,
 **          otherwise an error will be returned.
+**
+** See also the [load_extension() SQL function].
 */
 SQLITE_API int sqlite3_load_extension(
   sqlite3 *db,          /* Load the extension into this database connection */
   const char *zFile,    /* Name of the shared library containing extension */
   const char *zProc,    /* Entry point.  Derived from zFile if 0 */

Modified src/style.c from [3c29836aa3] to [c7fc56d774].

@@ -143,11 +143,11 @@
   @ <div class="content">
   cgi_destination(CGI_BODY);
 
   /* Put the footer at the bottom of the page.
   */
-  @ </div><br clear="both"></br>
+  @ </div><br clear="both"/>
   zFooter = db_get("footer", (char*)zDefaultFooter);
   if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1);
   Th_Render(zFooter);
   if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1);
 

Modified src/update.c from [807405a4a8] to [74a2862e7d].

@@ -304,11 +304,11 @@
   file_tree_name(zFile, &fname, 1);
 
   if( access(zFile, 0) ) yesRevert = 1;
   if( yesRevert==0 ){
     char *prompt = mprintf("revert file %B? this will"
-                           " destroy local changes [y/N]? ",
+                           " destroy local changes (y/N)? ",
                            &fname);
     blob_zero(&ans);
     prompt_user(prompt, &ans);
     free( prompt );
     if( blob_str(&ans)[0]=='y' ){

Modified src/wiki.c from [c28c68f6a8] to [362c97b880].

@@ -630,18 +630,20 @@
 */
 void wikirules_page(void){
   style_header("Wiki Formatting Rules");
   @ <h2>Formatting Rule Summary</h2>
   @ <ol>
-  @ <li> Blank lines are paragraph breaks
-  @ <li> Bullets are "*" surrounded by two spaces at the beginning of the line.
-  @ <li> Enumeration items are a number surrounded by two space
-  @ at the beginning of a line.
-  @ <li> Indented pargraphs begin with a tab or two spaces.
-  @ <li> Hyperlinks are contained with square brackets:  "[target]"
-  @ <li> Most ordinary HTML works.
-  @ <li> &lt;verbatim&gt; and &lt;nowiki&gt;.
+  @ <li>Blank lines are paragraph breaks</li>
+  @ <li>Bullets are "*" surrounded by two spaces at the beginning of the
+  @ line.</li>
+  @ <li>Enumeration items are "#" surrounded by two spaces at the beginning of
+  @ a line.</li>
+  @ <li>Indented pargraphs begin with a tab or two spaces.</li>
+  @ <li>Hyperlinks are contained with square brackets:  "[target]" or
+  @ "[target|name]".</li>
+  @ <li>Most ordinary HTML works.</li>
+  @ <li>&lt;verbatim&gt; and &lt;nowiki&gt;.</li>
   @ </ol>
   @ <p>We call the first five rules above "wiki" formatting rules.  The
   @ last two rules are the HTML formatting rule.</p>
   @ <h2>Formatting Rule Details</h2>
   @ <ol>
@@ -653,16 +655,13 @@
   @ A bullet list item is a line that begins with a single "*" character
   @ surrounded on
   @ both sides by two or more spaces or by a tab.  Only a single level
   @ of bullet list is supported by wiki.  For nested lists, use HTML.</p>
   @ <li> <p><b>Enumeration Lists</b>.
-  @ An enumeration list item is a line that begins
-  @ with one or more digits optionally
-  @ followed by a "." and is surrounded on both sides by two or more spaces or
-  @ by a tab.  The number is significant and becomes the number shown
-  @ in the rendered enumeration item.  Only a single level of enumeration
-  @ list is supported by wiki.  For nested enumerations or for
+  @ An enumeration list item is a line that begins with a single "#" character
+  @ surrounded on both sides by two or more spaces or by a tab.  Only a single
+  @ level of enumeration list is supported by wiki.  For nested lists or for
   @ enumerations that count using letters or roman numerials, use HTML.</p>
   @ <li> <p><b>Indented Paragraphs</b>.
   @ Any paragraph that begins with two or more spaces or a tab and
   @ which is not a bullet or enumeration list item is rendered
   @ indented.  Only a single level of indentation is supported by wiki; use

Modified src/wikiformat.c from [0e2f5af22b] to [b1603948aa].

@@ -329,14 +329,15 @@
 #define TOKEN_MARKUP        1    /* <...> */
 #define TOKEN_CHARACTER     2    /* "&" or "<" not part of markup */
 #define TOKEN_LINK          3    /* [...] */
 #define TOKEN_PARAGRAPH     4    /* blank lines */
 #define TOKEN_NEWLINE       5    /* A single "\n" */
-#define TOKEN_BULLET        6    /*  "  *  " */
-#define TOKEN_ENUM          7    /*  "  \(?\d+[.)]?  " */
-#define TOKEN_INDENT        8    /*  "   " */
-#define TOKEN_TEXT          9    /* None of the above */
+#define TOKEN_BUL_LI        6    /*  "  *  " */
+#define TOKEN_NUM_LI        7    /*  "  #  " */
+#define TOKEN_ENUM          8    /*  "  \(?\d+[.)]?  " */
+#define TOKEN_INDENT        9    /*  "   " */
+#define TOKEN_TEXT          10   /* None of the above */
 
 /*
 ** State flags
 */
 #define AT_NEWLINE          0x001  /* At start of a line */
@@ -452,23 +453,23 @@
     return i>1 && z[i]==';';
   }
 }
 
 /*
-** Check to see if the z[] string is the beginning of a wiki bullet.
+** Check to see if the z[] string is the beginning of a wiki list item.
 ** If it is, return the length of the bullet text.  Otherwise return 0.
 */
-static int bulletLength(const char *z){
+static int listItemLength(const char *z, const char listChar){
   int i, n;
   n = 0;
   i = 0;
   while( z[n]==' ' || z[n]=='\t' ){
     if( z[n]=='\t' ) i++;
     i++;
     n++;
   }
-  if( i<2 || z[n]!='*' ) return 0;
+  if( i<2 || z[n]!=listChar ) return 0;
   n++;
   i = 0;
   while( z[n]==' ' || z[n]=='\t' ){
     if( z[n]=='\t' ) i++;
     i++;
@@ -578,13 +579,18 @@
         *pTokenType = TOKEN_NEWLINE;
         return 1;
       }
     }
     if( (p->state & AT_NEWLINE)!=0 && isspace(z[0]) ){
-      n = bulletLength(z);
+      n = listItemLength(z, '*');
+      if( n>0 ){
+        *pTokenType = TOKEN_BUL_LI;
+        return n;
+      }
+      n = listItemLength(z, '#');
       if( n>0 ){
-        *pTokenType = TOKEN_BULLET;
+        *pTokenType = TOKEN_NUM_LI;
         return n;
       }
       n = enumLength(z);
       if( n>0 ){
         *pTokenType = TOKEN_ENUM;
@@ -838,11 +844,11 @@
 
 /*
 ** Begin a new paragraph if that something that is needed.
 */
 static void startAutoParagraph(Renderer *p){
-  if( p->wantAutoParagraph==0 ) return;
+  if( p->wantAutoParagraph==0 || p->wikiList==MARKUP_OL || p->wikiList==MARKUP_UL ) return;
   blob_appendf(p->pOut, "<p>", -1);
   pushStack(p, MARKUP_P);
   p->wantAutoParagraph = 0;
   p->inAutoParagraph = 1;
 }
@@ -1059,11 +1065,11 @@
       case TOKEN_NEWLINE: {
         blob_append(p->pOut, "\n", 1);
         p->state |= AT_NEWLINE;
         break;
       }
-      case TOKEN_BULLET: {
+      case TOKEN_BUL_LI: {
         if( inlineOnly ){
           blob_append(p->pOut, " &bull; ", -1);
         }else{
           if( p->wikiList!=MARKUP_UL ){
             if( p->wikiList ){
@@ -1070,10 +1076,29 @@
               popStackToTag(p, p->wikiList);
             }
             pushStack(p, MARKUP_UL);
             blob_append(p->pOut, "<ul>", 4);
             p->wikiList = MARKUP_UL;
+          }
+          popStackToTag(p, MARKUP_LI);
+          startAutoParagraph(p);
+          pushStack(p, MARKUP_LI);
+          blob_append(p->pOut, "<li>", 4);
+        }
+        break;
+      }
+      case TOKEN_NUM_LI: {
+        if( inlineOnly ){
+          blob_append(p->pOut, " # ", -1);
+        }else{
+          if( p->wikiList!=MARKUP_OL ){
+            if( p->wikiList ){
+              popStackToTag(p, p->wikiList);
+            }
+            pushStack(p, MARKUP_OL);
+            blob_append(p->pOut, "<ol>", 4);
+            p->wikiList = MARKUP_OL;
           }
           popStackToTag(p, MARKUP_LI);
           startAutoParagraph(p);
           pushStack(p, MARKUP_LI);
           blob_append(p->pOut, "<li>", 4);