/ Check-in [76d2d2ad]
Login

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

Overview
Comment:Fix handling of schema changes mid-session.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | sessions
Files: files | file ages | folders
SHA1: 76d2d2ad3b2a5171393b7894f35f463ff284e53b
User & Date: dan 2011-03-24 16:53:57
Context
2011-03-25
10:52
Improve coverage of session module code. check-in: 666123c8 user: dan tags: sessions
2011-03-24
16:53
Fix handling of schema changes mid-session. check-in: 76d2d2ad user: dan tags: sessions
16:04
Fix handling of schema mismatches in sqlite3session.c so that it matches the docs in sqlite3session.h. check-in: 506a0d7a user: dan tags: sessions
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/session/session3.test.

    15     15     set testdir [file join [file dirname [info script]] .. .. test]
    16     16   } 
    17     17   source [file join [file dirname [info script]] session_common.tcl]
    18     18   source $testdir/tester.tcl
    19     19   
    20     20   set testprefix session3
    21     21   
           22  +#-------------------------------------------------------------------------
    22     23   # These tests - session3-1.* - verify that the session module behaves
    23     24   # correctly when confronted with a schema mismatch when applying a 
    24     25   # changeset (in function sqlite3changeset_apply()).
    25     26   #
    26     27   #   session3-1.1.*: Table does not exist in target db.
    27     28   #   session3-1.2.*: Table has wrong number of columns in target db.
    28     29   #   session3-1.3.*: Table has wrong PK columns in target db.
    29     30   #
    30         -
    31     31   db close
    32     32   sqlite3_shutdown
    33     33   test_sqlite3_log log
    34     34   sqlite3 db test.db
    35     35   
    36     36   proc log {code msg} { lappend ::log $code $msg }
    37     37   
................................................................................
    72     72     set ::log {}
    73     73     do_then_apply_sql {
    74     74       INSERT INTO t1 VALUES(9, 10);
    75     75       INSERT INTO t1 VALUES(11, 12);
    76     76     }
    77     77     set ::log
    78     78   } {SQLITE_SCHEMA {sqlite3changeset_apply(): primary key mismatch for table t1}}
           79  +
           80  +#-------------------------------------------------------------------------
           81  +# These tests - session3-2.* - verify that the session module behaves
           82  +# correctly when the schema of an attached table is modified during the
           83  +# session.
           84  +#
           85  +#   session3-2.1.*: Table is dropped midway through the session.
           86  +#   session3-2.2.*: Table is dropped and recreated with a different # cols.
           87  +#   session3-2.3.*: Table is dropped and recreated with a different PK.
           88  +#
           89  +# In all of these scenarios, the call to sqlite3session_changeset() will
           90  +# return SQLITE_SCHEMA. Also:
           91  +#   
           92  +#   session3-2.4.*: Table is dropped and recreated with an identical schema.
           93  +#                   In this case sqlite3session_changeset() returns SQLITE_OK.
           94  +#
           95  +
           96  +do_test 2.1 {
           97  +  execsql { CREATE TABLE t2(a, b PRIMARY KEY) }
           98  +  sqlite3session S db main
           99  +  S attach t2
          100  +  execsql {
          101  +    INSERT INTO t2 VALUES(1, 2);
          102  +    DROP TABLE t2;
          103  +  }
          104  +  list [catch { S changeset } msg] $msg
          105  +} {1 SQLITE_SCHEMA}
          106  +
          107  +do_test 2.2.1 {
          108  +  S delete
          109  +  sqlite3session S db main
          110  +  execsql { CREATE TABLE t2(a, b PRIMARY KEY, c) }
          111  +  S attach t2
          112  +  execsql {
          113  +    INSERT INTO t2 VALUES(1, 2, 3);
          114  +    DROP TABLE t2;
          115  +    CREATE TABLE t2(a, b PRIMARY KEY);
          116  +  }
          117  +  list [catch { S changeset } msg] $msg
          118  +} {1 SQLITE_SCHEMA}
          119  +
          120  +do_test 2.2.2 {
          121  +  S delete
          122  +  sqlite3session S db main
          123  +  execsql { 
          124  +    DROP TABLE t2;
          125  +    CREATE TABLE t2(a, b PRIMARY KEY, c);
          126  +  }
          127  +  S attach t2
          128  +  execsql {
          129  +    INSERT INTO t2 VALUES(1, 2, 3);
          130  +    DROP TABLE t2;
          131  +    CREATE TABLE t2(a, b PRIMARY KEY, c, d);
          132  +  }
          133  +  list [catch { S changeset } msg] $msg
          134  +} {1 SQLITE_SCHEMA}
          135  +
          136  +do_test 2.3 {
          137  +  S delete
          138  +  sqlite3session S db main
          139  +  execsql { 
          140  +    DROP TABLE t2;
          141  +    CREATE TABLE t2(a, b PRIMARY KEY);
          142  +  }
          143  +  S attach t2
          144  +  execsql {
          145  +    INSERT INTO t2 VALUES(1, 2);
          146  +    DROP TABLE t2;
          147  +    CREATE TABLE t2(a PRIMARY KEY, b, c);
          148  +  }
          149  +  list [catch { S changeset } msg] $msg
          150  +} {1 SQLITE_SCHEMA}
          151  +
          152  +do_test 2.4 {
          153  +  S delete
          154  +  sqlite3session S db main
          155  +  execsql { 
          156  +    DROP TABLE t2;
          157  +    CREATE TABLE t2(a, b PRIMARY KEY);
          158  +  }
          159  +  S attach t2
          160  +  execsql {
          161  +    INSERT INTO t2 VALUES(1, 2);
          162  +    DROP TABLE t2;
          163  +    CREATE TABLE t2(a, b PRIMARY KEY);
          164  +  }
          165  +  list [catch { S changeset } msg] $msg
          166  +} {0 {}}
          167  +
          168  +S delete
    79    169   
    80    170   
    81    171   catch { db close }
    82    172   catch { db2 close }
    83    173   sqlite3_shutdown
    84    174   test_sqlite3_log
    85    175   sqlite3_initialize
    86    176   
    87    177   finish_test
    88    178   

Changes to ext/session/sqlite3session.c.

  1345   1345     *pnChangeset = 0;
  1346   1346     *ppChangeset = 0;
  1347   1347     rc = pSession->rc;
  1348   1348   
  1349   1349     for(pTab=pSession->pTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){
  1350   1350       if( pTab->nEntry ){
  1351   1351         const char *zName = pTab->zName;
  1352         -      int nCol = pTab->nCol;      /* Local copy of member variable */
  1353         -      u8 *abPK = pTab->abPK;      /* Local copy of member variable */
         1352  +      int nCol;                   /* Number of columns in table */
         1353  +      u8 *abPK;                   /* Primary key array */
         1354  +      const char **azCol = 0;     /* Table columns */
  1354   1355         int i;                      /* Used to iterate through hash buckets */
  1355   1356         sqlite3_stmt *pSel = 0;     /* SELECT statement to query table pTab */
  1356   1357         int nRewind = buf.nBuf;     /* Initial size of write buffer */
  1357   1358         int nNoop;                  /* Size of buffer after writing tbl header */
         1359  +
         1360  +      /* Check the table schema is still Ok. */
         1361  +      rc = sessionTableInfo(db, pSession->zDb, zName, &nCol, 0, &azCol, &abPK);
         1362  +      if( !rc && (pTab->nCol!=nCol || memcmp(abPK, pTab->abPK, nCol)) ){
         1363  +        rc = SQLITE_SCHEMA;
         1364  +      }
  1358   1365   
  1359   1366         /* Write a table header */
  1360   1367         sessionAppendByte(&buf, 'T', &rc);
  1361   1368         sessionAppendVarint(&buf, nCol, &rc);
  1362   1369         sessionAppendBlob(&buf, pTab->abPK, nCol, &rc);
  1363   1370         sessionAppendBlob(&buf, (u8 *)zName, strlen(zName)+1, &rc);
  1364   1371   
  1365   1372         /* Build and compile a statement to execute: */
  1366   1373         if( rc==SQLITE_OK ){
  1367   1374           rc = sessionSelectStmt(
  1368         -            db, pSession->zDb, zName, nCol, pTab->azCol, abPK, &pSel);
         1375  +            db, pSession->zDb, zName, nCol, azCol, abPK, &pSel);
  1369   1376         }
  1370   1377   
  1371   1378         if( rc==SQLITE_OK && nCol!=sqlite3_column_count(pSel) ){
  1372   1379           rc = SQLITE_SCHEMA;
  1373   1380         }
  1374   1381   
  1375   1382         nNoop = buf.nBuf;
................................................................................
  1403   1410           }
  1404   1411         }
  1405   1412   
  1406   1413         sqlite3_finalize(pSel);
  1407   1414         if( buf.nBuf==nNoop ){
  1408   1415           buf.nBuf = nRewind;
  1409   1416         }
         1417  +      sqlite3_free(azCol);
  1410   1418       }
  1411   1419     }
  1412   1420   
  1413   1421     if( rc==SQLITE_OK ){
  1414   1422       *pnChangeset = buf.nBuf;
  1415   1423       *ppChangeset = buf.aBuf;
  1416   1424     }else{