/ Check-in [25bf734b]
Login

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

Overview
Comment:Fix a problem in the sessions module with logging sqlite_stat1 rows for which (idx IS NULL) is true.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | sessions-stat1
Files: files | file ages | folders
SHA3-256:25bf734be1b3883fccf12ac4d93d50289aa307fb60a52e0e32df12f7ee4edc7a
User & Date: dan 2018-01-17 20:57:20
Context
2018-01-18
15:06
Simplify the sessions preupdate-hook logic for transforming NULL to X'' for column sqlite_stat1.idx. check-in: 089d7cec user: dan tags: sessions-stat1
2018-01-17
20:57
Fix a problem in the sessions module with logging sqlite_stat1 rows for which (idx IS NULL) is true. check-in: 25bf734b user: dan tags: sessions-stat1
17:38
Fix a problem causing the sessions module to occasionally lose track of rows with composite primary keys when there are two rows with the same text value in the leftmost column of the PK. check-in: 09aed136 user: dan tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/session/sessionstat1.test.

   116    116   
   117    117   do_execsql_test -db db2 2.4 {
   118    118     SELECT * FROM sqlite_stat1
   119    119   } {
   120    120   }
   121    121   
   122    122   do_execsql_test -db db2 2.5 { SELECT count(*) FROM t1 } 32
          123  +
          124  +#-------------------------------------------------------------------------
          125  +db2 close
          126  +forcedelete test.db2
          127  +reset_db
          128  +sqlite3 db2 test.db2
          129  +
          130  +do_test 3.0 {
          131  +  do_common_sql {
          132  +    CREATE TABLE t1(a, b, c);
          133  +    ANALYZE;
          134  +    DELETE FROM sqlite_stat1;
          135  +  }
          136  +  execsql {
          137  +    INSERT INTO t1 VALUES(1, 1, 1);
          138  +    INSERT INTO t1 VALUES(2, 2, 2);
          139  +    INSERT INTO t1 VALUES(3, 3, 3);
          140  +    INSERT INTO t1 VALUES(4, 4, 4);
          141  +  }
          142  +} {} 
          143  +
          144  +do_iterator_test 3.1 {} {
          145  +  ANALYZE
          146  +} {
          147  +  {INSERT sqlite_stat1 0 XX. {} {t t1 b {} t 4}}
          148  +}
          149  +db null null
          150  +db2 null null
          151  +do_execsql_test 3.2 {
          152  +  SELECT * FROM sqlite_stat1;
          153  +} {t1 null 4}
          154  +do_test 3.3 {
          155  +  execsql { DELETE FROM sqlite_stat1 }
          156  +  do_then_apply_sql { ANALYZE }
          157  +  execsql { SELECT * FROM sqlite_stat1 } db2
          158  +} {t1 null 4}
          159  +do_test 3.4 {
          160  +  execsql { INSERT INTO t1 VALUES(5,5,5) }
          161  +  do_then_apply_sql { ANALYZE }
          162  +  execsql { SELECT * FROM sqlite_stat1 } db2
          163  +} {t1 null 5}
          164  +do_test 3.5 {
          165  +  do_then_apply_sql { DROP TABLE t1 }
          166  +  execsql { SELECT * FROM sqlite_stat1 } db2
          167  +} {}
          168  +
          169  +do_test 3.6.1 {
          170  +  execsql { 
          171  +    CREATE TABLE t1(a, b, c);
          172  +    CREATE TABLE t2(x, y, z);
          173  +    INSERT INTO t1 VALUES(1,1,1), (2,2,2), (3,3,3), (4,4,4), (5,5,5);
          174  +    INSERT INTO t2 SELECT * FROM t1;
          175  +    DELETE FROM sqlite_stat1;
          176  +  }
          177  +  sqlite3session S db main
          178  +  S attach sqlite_stat1
          179  +  execsql { ANALYZE }
          180  +} {}
          181  +do_changeset_test 3.6.2 S {
          182  +  {INSERT sqlite_stat1 0 XX. {} {t t2 b {} t 5}}
          183  +  {INSERT sqlite_stat1 0 XX. {} {t t1 b {} t 5}}
          184  +}
          185  +do_changeset_invert_test 3.6.3 S {
          186  +  {DELETE sqlite_stat1 0 XX. {t t2 b {} t 5} {}}
          187  +  {DELETE sqlite_stat1 0 XX. {t t1 b {} t 5} {}}
          188  +}
          189  +do_test 3.6.4 { S delete } {}
          190  +
          191  +proc sql_changeset_concat {args} {
          192  +  foreach sql $args {
          193  +    sqlite3session S db main
          194  +    S attach sqlite_stat1
          195  +    execsql $sql
          196  +    set change [S changeset]
          197  +    S delete
          198  +
          199  +    if {[info vars ret]!=""} {
          200  +      set ret [sqlite3changeset_concat $ret $change]
          201  +    } else {
          202  +      set ret $change
          203  +    }
          204  +  }
          205  +
          206  +  changeset_to_list $ret
          207  +}
          208  +
          209  +proc do_scc_test {tn args} {
          210  +  uplevel [list \
          211  +    do_test $tn [concat sql_changeset_concat [lrange $args 0 end-1]] \
          212  +    [list {*}[ lindex $args end ]]
          213  +  ]
          214  +}
          215  +
          216  +do_execsql_test 3.7.0 {
          217  +  DELETE FROM sqlite_stat1;
          218  +}
          219  +do_scc_test 3.7.1 {
          220  +  ANALYZE;
          221  +} {
          222  +  INSERT INTO t2 VALUES(6,6,6);
          223  +  ANALYZE;
          224  +} {
          225  +  {INSERT sqlite_stat1 0 XX. {} {t t1 b {} t 5}}
          226  +  {INSERT sqlite_stat1 0 XX. {} {t t2 b {} t 6}}
          227  +}
          228  +
          229  +
          230  +
          231  +
   123    232   
   124    233   finish_test
   125    234   

Changes to ext/session/sqlite3session.c.

   109    109   ** a subset of the initial values that the modified row contained at the
   110    110   ** start of the session. Or no initial values if the row was inserted.
   111    111   */
   112    112   struct SessionTable {
   113    113     SessionTable *pNext;
   114    114     char *zName;                    /* Local name of table */
   115    115     int nCol;                       /* Number of columns in table zName */
          116  +  int bStat1;                     /* True if this is sqlite_stat1 */
   116    117     const char **azCol;             /* Column names */
   117    118     u8 *abPK;                       /* Array of primary key flags */
   118    119     int nEntry;                     /* Total number of entries in hash table */
   119    120     int nChange;                    /* Size of apChange[] array */
   120    121     SessionChange **apChange;       /* Hash table buckets */
   121    122   };
   122    123   
................................................................................
   468    469           rc = pSession->hook.xNew(pSession->hook.pCtx, i, &pVal);
   469    470         }else{
   470    471           rc = pSession->hook.xOld(pSession->hook.pCtx, i, &pVal);
   471    472         }
   472    473         if( rc!=SQLITE_OK ) return rc;
   473    474   
   474    475         eType = sqlite3_value_type(pVal);
   475         -      h = sessionHashAppendType(h, eType);
   476         -      if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
   477         -        i64 iVal;
   478         -        if( eType==SQLITE_INTEGER ){
   479         -          iVal = sqlite3_value_int64(pVal);
   480         -        }else{
   481         -          double rVal = sqlite3_value_double(pVal);
   482         -          assert( sizeof(iVal)==8 && sizeof(rVal)==8 );
   483         -          memcpy(&iVal, &rVal, 8);
   484         -        }
   485         -        h = sessionHashAppendI64(h, iVal);
   486         -      }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
   487         -        const u8 *z;
   488         -        int n;
   489         -        if( eType==SQLITE_TEXT ){
   490         -          z = (const u8 *)sqlite3_value_text(pVal);
          476  +      if( pTab->bStat1 && eType==SQLITE_NULL ){
          477  +        h = sessionHashAppendType(h, SQLITE_BLOB);
          478  +      }else{
          479  +        h = sessionHashAppendType(h, eType);
          480  +        if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
          481  +          i64 iVal;
          482  +          if( eType==SQLITE_INTEGER ){
          483  +            iVal = sqlite3_value_int64(pVal);
          484  +          }else{
          485  +            double rVal = sqlite3_value_double(pVal);
          486  +            assert( sizeof(iVal)==8 && sizeof(rVal)==8 );
          487  +            memcpy(&iVal, &rVal, 8);
          488  +          }
          489  +          h = sessionHashAppendI64(h, iVal);
          490  +        }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
          491  +          const u8 *z;
          492  +          int n;
          493  +          if( eType==SQLITE_TEXT ){
          494  +            z = (const u8 *)sqlite3_value_text(pVal);
          495  +          }else{
          496  +            z = (const u8 *)sqlite3_value_blob(pVal);
          497  +          }
          498  +          n = sqlite3_value_bytes(pVal);
          499  +          if( !z && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM;
          500  +          h = sessionHashAppendBlob(h, n, z);
   491    501           }else{
   492         -          z = (const u8 *)sqlite3_value_blob(pVal);
          502  +          assert( eType==SQLITE_NULL );
          503  +          *pbNullPK = 1;
   493    504           }
   494         -        n = sqlite3_value_bytes(pVal);
   495         -        if( !z && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM;
   496         -        h = sessionHashAppendBlob(h, n, z);
   497         -      }else{
   498         -        assert( eType==SQLITE_NULL );
   499         -        *pbNullPK = 1;
   500    505         }
   501    506       }
   502    507     }
   503    508   
   504    509     *piHash = (h % pTab->nChange);
   505    510     return SQLITE_OK;
   506    511   }
................................................................................
   546    551       /* It is not possible for eType to be SQLITE_NULL here. The session 
   547    552       ** module does not record changes for rows with NULL values stored in
   548    553       ** primary key columns. */
   549    554       assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT 
   550    555            || eType==SQLITE_TEXT || eType==SQLITE_BLOB 
   551    556            || eType==SQLITE_NULL || eType==0 
   552    557       );
   553         -    assert( !isPK || (eType!=0 && eType!=SQLITE_NULL) );
          558  +    assert( !isPK || (eType!=0 && (pTab->bStat1 || eType!=SQLITE_NULL)) );
   554    559   
   555    560       if( isPK ){
   556    561         a++;
   557    562         h = sessionHashAppendType(h, eType);
   558    563         if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
   559    564           h = sessionHashAppendI64(h, sessionGetI64(a));
   560    565           a += 8;
   561         -      }else{
          566  +      }else if( eType!=SQLITE_NULL ){
   562    567           int n; 
   563    568           a += sessionVarintGet(a, &n);
   564    569           h = sessionHashAppendBlob(h, n, a);
   565    570           a += n;
   566    571         }
   567    572       }else{
   568    573         a += sessionSerialLen(a);
................................................................................
   790    795     for(iCol=0; iCol<pTab->nCol; iCol++){
   791    796       if( !pTab->abPK[iCol] ){
   792    797         a += sessionSerialLen(a);
   793    798       }else{
   794    799         sqlite3_value *pVal;        /* Value returned by preupdate_new/old */
   795    800         int rc;                     /* Error code from preupdate_new/old */
   796    801         int eType = *a++;           /* Type of value from change record */
          802  +      int eValType;
   797    803   
   798    804         /* The following calls to preupdate_new() and preupdate_old() can not
   799    805         ** fail. This is because they cache their return values, and by the
   800    806         ** time control flows to here they have already been called once from
   801    807         ** within sessionPreupdateHash(). The first two asserts below verify
   802    808         ** this (that the method has already been called). */
   803    809         if( op==SQLITE_INSERT ){
................................................................................
   804    810           /* assert( db->pPreUpdate->pNewUnpacked || db->pPreUpdate->aNew ); */
   805    811           rc = pSession->hook.xNew(pSession->hook.pCtx, iCol, &pVal);
   806    812         }else{
   807    813           /* assert( db->pPreUpdate->pUnpacked ); */
   808    814           rc = pSession->hook.xOld(pSession->hook.pCtx, iCol, &pVal);
   809    815         }
   810    816         assert( rc==SQLITE_OK );
   811         -      if( sqlite3_value_type(pVal)!=eType ) return 0;
          817  +      eValType = sqlite3_value_type(pVal);
          818  +      if( eType==SQLITE_BLOB && eValType==SQLITE_NULL && pTab->bStat1 ){
          819  +        int n;
          820  +        a += sessionVarintGet(a, &n);
          821  +        if( n!=0 ) return 0;
          822  +        continue;
          823  +      }
          824  +      if( eValType!=eType ) return 0;
   812    825   
   813    826         /* A SessionChange object never has a NULL value in a PK column */
   814    827         assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT
   815    828              || eType==SQLITE_BLOB    || eType==SQLITE_TEXT
   816    829         );
   817    830   
   818    831         if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
................................................................................
  1043   1056         int i;
  1044   1057         for(i=0; i<pTab->nCol; i++){
  1045   1058           if( abPK[i] ){
  1046   1059             pTab->abPK = abPK;
  1047   1060             break;
  1048   1061           }
  1049   1062         }
         1063  +      if( 0==sqlite3_stricmp("sqlite_stat1", pTab->zName) ){
         1064  +        pTab->bStat1 = 1;
         1065  +      }
  1050   1066       }
  1051   1067     }
  1052   1068     return (pSession->rc || pTab->abPK==0);
  1053   1069   }
  1054   1070   
  1055   1071   /*
  1056   1072   ** This function is only called from with a pre-update-hook reporting a 
................................................................................
  1089   1105   
  1090   1106     /* Calculate the hash-key for this change. If the primary key of the row
  1091   1107     ** includes a NULL value, exit early. Such changes are ignored by the
  1092   1108     ** session module. */
  1093   1109     rc = sessionPreupdateHash(pSession, pTab, op==SQLITE_INSERT, &iHash, &bNull);
  1094   1110     if( rc!=SQLITE_OK ) goto error_out;
  1095   1111   
  1096         -  if( bNull==0 ){
         1112  +  if( bNull==0 || pTab->bStat1 ){
  1097   1113       /* Search the hash table for an existing record for this row. */
  1098   1114       SessionChange *pC;
  1099   1115       for(pC=pTab->apChange[iHash]; pC; pC=pC->pNext){
  1100   1116         if( sessionPreupdateEqual(pSession, pTab, pC, op) ) break;
  1101   1117       }
  1102   1118   
  1103   1119       if( pC==0 ){
................................................................................
  1124   1140           }
  1125   1141   
  1126   1142           /* This may fail if SQLite value p contains a utf-16 string that must
  1127   1143           ** be converted to utf-8 and an OOM error occurs while doing so. */
  1128   1144           rc = sessionSerializeValue(0, p, &nByte);
  1129   1145           if( rc!=SQLITE_OK ) goto error_out;
  1130   1146         }
         1147  +      if( pTab->bStat1 ) nByte += 30;
  1131   1148     
  1132   1149         /* Allocate the change object */
  1133   1150         pChange = (SessionChange *)sqlite3_malloc(nByte);
  1134   1151         if( !pChange ){
  1135   1152           rc = SQLITE_NOMEM;
  1136   1153           goto error_out;
  1137   1154         }else{
................................................................................
  1147   1164         for(i=0; i<pTab->nCol; i++){
  1148   1165           sqlite3_value *p = 0;
  1149   1166           if( op!=SQLITE_INSERT ){
  1150   1167             pSession->hook.xOld(pSession->hook.pCtx, i, &p);
  1151   1168           }else if( pTab->abPK[i] ){
  1152   1169             pSession->hook.xNew(pSession->hook.pCtx, i, &p);
  1153   1170           }
  1154         -        sessionSerializeValue(&pChange->aRecord[nByte], p, &nByte);
         1171  +        if( p && pTab->bStat1 && sqlite3_value_type(p)==SQLITE_NULL ){
         1172  +          pChange->aRecord[nByte++] = SQLITE_BLOB;
         1173  +          nByte += sessionVarintPut(&pChange->aRecord[nByte], 0);
         1174  +        }else{
         1175  +          sessionSerializeValue(&pChange->aRecord[nByte], p, &nByte);
         1176  +        }
  1155   1177         }
  1156   1178   
  1157   1179         /* Add the change to the hash-table */
  1158   1180         if( pSession->bIndirect || pSession->hook.xDepth(pSession->hook.pCtx) ){
  1159   1181           pChange->bIndirect = 1;
  1160   1182         }
  1161   1183         pChange->nRecord = nByte;
................................................................................
  2100   2122     const char *zTab,               /* Table name */
  2101   2123     int nCol,                       /* Number of columns in table */
  2102   2124     const char **azCol,             /* Names of table columns */
  2103   2125     u8 *abPK,                       /* PRIMARY KEY  array */
  2104   2126     sqlite3_stmt **ppStmt           /* OUT: Prepared SELECT statement */
  2105   2127   ){
  2106   2128     int rc = SQLITE_OK;
  2107         -  int i;
  2108         -  const char *zSep = "";
  2109         -  SessionBuffer buf = {0, 0, 0};
  2110         -
  2111         -  sessionAppendStr(&buf, "SELECT * FROM ", &rc);
  2112         -  sessionAppendIdent(&buf, zDb, &rc);
  2113         -  sessionAppendStr(&buf, ".", &rc);
  2114         -  sessionAppendIdent(&buf, zTab, &rc);
  2115         -  sessionAppendStr(&buf, " WHERE ", &rc);
  2116         -  for(i=0; i<nCol; i++){
  2117         -    if( abPK[i] ){
  2118         -      sessionAppendStr(&buf, zSep, &rc);
  2119         -      sessionAppendIdent(&buf, azCol[i], &rc);
  2120         -      sessionAppendStr(&buf, " = ?", &rc);
  2121         -      sessionAppendInteger(&buf, i+1, &rc);
  2122         -      zSep = " AND ";
  2123         -    }
  2124         -  }
  2125         -  if( rc==SQLITE_OK ){
  2126         -    rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, ppStmt, 0);
  2127         -  }
  2128         -  sqlite3_free(buf.aBuf);
         2129  +  char *zSql = 0;
         2130  +  int nSql = -1;
         2131  +
         2132  +  if( 0==sqlite3_stricmp("sqlite_stat1", zTab) ){
         2133  +    zSql = sqlite3_mprintf(
         2134  +        "SELECT tbl, ?2, stat FROM %Q.sqlite_stat1 WHERE tbl IS ?1 AND "
         2135  +        "idx IS (CASE WHEN ?2=X'' THEN NULL ELSE ?2 END)", zDb
         2136  +    );
         2137  +  }else{
         2138  +    int i;
         2139  +    const char *zSep = "";
         2140  +    SessionBuffer buf = {0, 0, 0};
         2141  +
         2142  +    sessionAppendStr(&buf, "SELECT * FROM ", &rc);
         2143  +    sessionAppendIdent(&buf, zDb, &rc);
         2144  +    sessionAppendStr(&buf, ".", &rc);
         2145  +    sessionAppendIdent(&buf, zTab, &rc);
         2146  +    sessionAppendStr(&buf, " WHERE ", &rc);
         2147  +    for(i=0; i<nCol; i++){
         2148  +      if( abPK[i] ){
         2149  +        sessionAppendStr(&buf, zSep, &rc);
         2150  +        sessionAppendIdent(&buf, azCol[i], &rc);
         2151  +        sessionAppendStr(&buf, " IS ?", &rc);
         2152  +        sessionAppendInteger(&buf, i+1, &rc);
         2153  +        zSep = " AND ";
         2154  +      }
         2155  +    }
         2156  +    zSql = (char*)buf.aBuf;
         2157  +    nSql = buf.nBuf;
         2158  +  }
         2159  +
         2160  +  if( rc==SQLITE_OK ){
         2161  +    rc = sqlite3_prepare_v2(db, zSql, nSql, ppStmt, 0);
         2162  +  }
         2163  +  sqlite3_free(zSql);
  2129   2164     return rc;
  2130   2165   }
  2131   2166   
  2132   2167   /*
  2133   2168   ** Bind the PRIMARY KEY values from the change passed in argument pChange
  2134   2169   ** to the SELECT statement passed as the first argument. The SELECT statement
  2135   2170   ** is as prepared by function sessionSelectStmt().
................................................................................
  2149   2184   
  2150   2185     for(i=0; i<nCol && rc==SQLITE_OK; i++){
  2151   2186       int eType = *a++;
  2152   2187   
  2153   2188       switch( eType ){
  2154   2189         case 0:
  2155   2190         case SQLITE_NULL:
  2156         -        assert( abPK[i]==0 );
         2191  +        /* assert( abPK[i]==0 ); */
  2157   2192           break;
  2158   2193   
  2159   2194         case SQLITE_INTEGER: {
  2160   2195           if( abPK[i] ){
  2161   2196             i64 iVal = sessionGetI64(a);
  2162   2197             rc = sqlite3_bind_int64(pSelect, i+1, iVal);
  2163   2198           }
................................................................................
  3459   3494     if( rc==SQLITE_OK ){
  3460   3495       rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pUpdate, 0);
  3461   3496     }
  3462   3497     sqlite3_free(buf.aBuf);
  3463   3498   
  3464   3499     return rc;
  3465   3500   }
         3501  +
  3466   3502   
  3467   3503   /*
  3468   3504   ** Formulate and prepare an SQL statement to query table zTab by primary
  3469   3505   ** key. Assuming the following table structure:
  3470   3506   **
  3471   3507   **     CREATE TABLE x(a, b, c, d, PRIMARY KEY(a, c));
  3472   3508   **
................................................................................
  3520   3556   
  3521   3557     if( rc==SQLITE_OK ){
  3522   3558       rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pInsert, 0);
  3523   3559     }
  3524   3560     sqlite3_free(buf.aBuf);
  3525   3561     return rc;
  3526   3562   }
         3563  +
         3564  +static int sessionPrepare(sqlite3 *db, sqlite3_stmt **pp, const char *zSql){
         3565  +  return sqlite3_prepare_v2(db, zSql, -1, pp, 0);
         3566  +}
         3567  +
         3568  +/*
         3569  +** Prepare statements for applying changes to the sqlite_stat1 table.
         3570  +** These are similar to those created by sessionSelectRow(),
         3571  +** sessionInsertRow(), sessionUpdateRow() and sessionDeleteRow() for 
         3572  +** other tables.
         3573  +*/
         3574  +static int sessionStat1Sql(sqlite3 *db, SessionApplyCtx *p){
         3575  +  int rc = sessionSelectRow(db, "sqlite_stat1", p);
         3576  +  if( rc==SQLITE_OK ){
         3577  +    rc = sessionPrepare(db, &p->pInsert,
         3578  +        "INSERT INTO main.sqlite_stat1 VALUES(?1, "
         3579  +        "CASE WHEN length(?2)=0 AND typeof(?2)='blob' THEN NULL ELSE ?2 END, "
         3580  +        "?3)"
         3581  +    );
         3582  +  }
         3583  +  if( rc==SQLITE_OK ){
         3584  +    rc = sessionPrepare(db, &p->pUpdate,
         3585  +        "UPDATE main.sqlite_stat1 SET "
         3586  +        "tbl = CASE WHEN ?2 THEN ?3 ELSE tbl END, "
         3587  +        "idx = CASE WHEN ?5 THEN ?6 ELSE idx END, "
         3588  +        "stat = CASE WHEN ?8 THEN ?9 ELSE stat END  "
         3589  +        "WHERE tbl=?1 AND idx IS "
         3590  +        "CASE WHEN length(?4)=0 AND typeof(?4)='blob' THEN NULL ELSE ?4 END "
         3591  +        "AND (?10 OR ?8=0 OR stat IS ?7)"
         3592  +    );
         3593  +  }
         3594  +  if( rc==SQLITE_OK ){
         3595  +    rc = sessionPrepare(db, &p->pDelete,
         3596  +        "DELETE FROM main.sqlite_stat1 WHERE tbl=?1 AND idx IS "
         3597  +        "CASE WHEN length(?2)=0 AND typeof(?2)='blob' THEN NULL ELSE ?2 END "
         3598  +        "AND (?4 OR stat IS ?3)"
         3599  +    );
         3600  +  }
         3601  +  assert( rc==SQLITE_OK );
         3602  +  return rc;
         3603  +}
  3527   3604   
  3528   3605   /*
  3529   3606   ** A wrapper around sqlite3_bind_value() that detects an extra problem. 
  3530   3607   ** See comments in the body of this function for details.
  3531   3608   */
  3532   3609   static int sessionBindValue(
  3533   3610     sqlite3_stmt *pStmt,            /* Statement to bind value to */
................................................................................
  4088   4165             schemaMismatch = 1;
  4089   4166             sqlite3_log(SQLITE_SCHEMA, "sqlite3changeset_apply(): "
  4090   4167                 "primary key mismatch for table %s", zTab
  4091   4168             );
  4092   4169           }
  4093   4170           else{
  4094   4171             sApply.nCol = nCol;
         4172  +          if( 0==sqlite3_stricmp(zTab, "sqlite_stat1") ){
         4173  +            if( (rc = sessionStat1Sql(db, &sApply) ) ){
         4174  +              break;
         4175  +            }
         4176  +          }else
  4095   4177             if((rc = sessionSelectRow(db, zTab, &sApply))
  4096   4178             || (rc = sessionUpdateRow(db, zTab, &sApply))
  4097   4179             || (rc = sessionDeleteRow(db, zTab, &sApply))
  4098   4180             || (rc = sessionInsertRow(db, zTab, &sApply))
  4099   4181             ){
  4100   4182               break;
  4101   4183             }

Changes to src/analyze.c.

  1305   1305       jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1); VdbeCoverage(v);
  1306   1306       sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname);
  1307   1307       assert( "BBB"[0]==SQLITE_AFF_TEXT );
  1308   1308       sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0);
  1309   1309       sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid);
  1310   1310       sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regTemp, regNewRowid);
  1311   1311       sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
         1312  +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
         1313  +    sqlite3VdbeChangeP4(v, -1, (char*)pStat1, P4_TABLE);
         1314  +#endif
  1312   1315       sqlite3VdbeJumpHere(v, jZeroRows);
  1313   1316     }
  1314   1317   }
  1315   1318   
  1316   1319   
  1317   1320   /*
  1318   1321   ** Generate code that will cause the most recent index analysis to