/ Check-in [ad91d300]
Login

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

Overview
Comment:Fix a problem involving session objects and attached databases.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | sessions
Files: files | file ages | folders
SHA1: ad91d30073a8faa7eb064dd2e1cc4d2297d7b3f8
User & Date: dan 2011-03-19 19:19:26
Context
2011-03-20
11:20
Documentation format updates to sqlite3session.h. check-in: f227f602 user: dan tags: sessions
2011-03-19
19:19
Fix a problem involving session objects and attached databases. check-in: ad91d300 user: dan tags: sessions
18:46
Fix a bug in changeset generation code. check-in: 825df75b user: dan tags: sessions
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to ext/session/session2.test.

30
31
32
33
34
35
36
37

38
39
40
41
42
43
44
45
46
...
167
168
169
170
171
172
173
174
175
176
177
178























































































179
180

proc do_common_sql {sql} {
  execsql $sql db
  execsql $sql db2
}
proc xConflict args { return "OMIT" }

proc do_then_apply_sql {sql} {

  sqlite3session S db main
  db eval {SELECT name FROM sqlite_master WHERE type = 'table'} {
    S attach $name
  }

  db eval $sql
  sqlite3changeset_apply db2 [S changeset] xConflict
  S delete
}
................................................................................
  }
  10 {
    BEGIN;
      DELETE FROM t1 WHERE 1;
    ROLLBACK;
  }
} {
if {$tn==9} breakpoint
  do_then_apply_sql $sql
  do_test $tn { compare_db db db2 } {}
}
























































































finish_test








|
>
|
|







 







<

|


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
...
168
169
170
171
172
173
174

175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267

proc do_common_sql {sql} {
  execsql $sql db
  execsql $sql db2
}
proc xConflict args { return "OMIT" }

proc do_then_apply_sql {sql {dbname main}} {

  sqlite3session S db $dbname
  db eval "SELECT name FROM $dbname.sqlite_master WHERE type = 'table'" {
    S attach $name
  }

  db eval $sql
  sqlite3changeset_apply db2 [S changeset] xConflict
  S delete
}
................................................................................
  }
  10 {
    BEGIN;
      DELETE FROM t1 WHERE 1;
    ROLLBACK;
  }
} {

  do_then_apply_sql $sql
  do_test 1.$tn { compare_db db db2 } {}
}

# The following block of tests is similar to the last, except that the
# session object is recording changes made to an attached database. The
# main database contains a table of the same name as the table being
# modified within the attached db.
#
test_reset
forcedelete test.db3
sqlite3 db3 test.db3
do_test 2.0 {
  execsql {
    ATTACH 'test.db3' AS 'aux';
    CREATE TABLE t1(a, b PRIMARY KEY);
    CREATE TABLE t2(x, y, z);
    CREATE TABLE t3(a);

    CREATE TABLE aux.t1(a PRIMARY KEY, b);
    CREATE TABLE aux.t2(a, b INTEGER PRIMARY KEY);
    CREATE TABLE aux.t3(a, b, c, PRIMARY KEY(a, b));
  }
  execsql {
    CREATE TABLE t1(a PRIMARY KEY, b);
    CREATE TABLE t2(a, b INTEGER PRIMARY KEY);
    CREATE TABLE t3(a, b, c, PRIMARY KEY(a, b));
  } db2
} {}

foreach {tn sql} {
  1 { INSERT INTO aux.t1 VALUES(1, 2) } 

  2 {
    INSERT INTO aux.t2 VALUES(1, NULL);
    INSERT INTO aux.t2 VALUES(2, NULL);
    INSERT INTO aux.t2 VALUES(3, NULL);
    DELETE FROM aux.t2 WHERE a = 2;
    INSERT INTO aux.t2 VALUES(4, NULL);
    UPDATE aux.t2 SET b=0 WHERE b=1;
  } 

  3 { INSERT INTO aux.t3 SELECT *, NULL FROM aux.t2 }

  4 {
    INSERT INTO aux.t3 SELECT a||a, b||b, NULL FROM aux.t3;
    DELETE FROM aux.t3 WHERE rowid%2;
  }

  5 { UPDATE aux.t3 SET c = a||b }

  6 { UPDATE aux.t1 SET a = 32 }

  7 { 
    INSERT INTO aux.t1 SELECT randomblob(32), randomblob(32) FROM aux.t1; 
    INSERT INTO aux.t1 SELECT randomblob(32), randomblob(32) FROM aux.t1; 
    INSERT INTO aux.t1 SELECT randomblob(32), randomblob(32) FROM aux.t1; 
    INSERT INTO aux.t1 SELECT randomblob(32), randomblob(32) FROM aux.t1; 
    INSERT INTO aux.t1 SELECT randomblob(32), randomblob(32) FROM aux.t1; 
    INSERT INTO aux.t1 SELECT randomblob(32), randomblob(32) FROM aux.t1; 
    INSERT INTO aux.t1 SELECT randomblob(32), randomblob(32) FROM aux.t1; 
    INSERT INTO aux.t1 SELECT randomblob(32), randomblob(32) FROM aux.t1; 
    INSERT INTO aux.t1 SELECT randomblob(32), randomblob(32) FROM aux.t1; 
    INSERT INTO aux.t1 SELECT randomblob(32), randomblob(32) FROM aux.t1; 
    INSERT INTO aux.t1 SELECT randomblob(32), randomblob(32) FROM aux.t1; 
    DELETE FROM aux.t1 WHERE (rowid%3)==0;
  }

  8 {
    BEGIN;
      INSERT INTO aux.t1 SELECT randomblob(32), randomblob(32) FROM aux.t1;
    ROLLBACK;
  }
  9 {
    BEGIN;
      UPDATE aux.t1 SET b = 'xxx';
    ROLLBACK;
  }
  10 {
    BEGIN;
      DELETE FROM aux.t1 WHERE 1;
    ROLLBACK;
  }
} {
  do_then_apply_sql $sql aux
  do_test 2.$tn { compare_db db3 db2 } {}
}


catch {db3 close}

finish_test

Changes to ext/session/sqlite3session.c.

1187
1188
1189
1190
1191
1192
1193

1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205


1206
1207
1208
1209
1210
1211
1212
....
1309
1310
1311
1312
1313
1314
1315

1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329

1330
1331
1332
1333
1334
1335
1336
1337
....
1938
1939
1940
1941
1942
1943
1944

1945
1946
1947
1948
1949
1950
1951
1952
    }
    sqlite3_free(buf2.aBuf);
  }
}

static int sessionSelectStmt(
  sqlite3 *db,                    /* Database handle */

  const char *zTab,               /* Table name */
  int nCol,
  const char **azCol,
  u8 *abPK,
  sqlite3_stmt **ppStmt
){
  int rc = SQLITE_OK;
  int i;
  const char *zSep = "";
  SessionBuffer buf = {0, 0, 0};

  sessionAppendStr(&buf, "SELECT * FROM ", &rc);


  sessionAppendIdent(&buf, zTab, &rc);
  sessionAppendStr(&buf, " WHERE ", &rc);
  for(i=0; i<nCol; i++){
    if( abPK[i] ){
      sessionAppendStr(&buf, zSep, &rc);
      sessionAppendIdent(&buf, azCol[i], &rc);
      sessionAppendStr(&buf, " = ?", &rc);
................................................................................
  ** this call will be a no-op.  */
  *pnChangeset = 0;
  *ppChangeset = 0;
  rc = pSession->rc;

  for(pTab=pSession->pTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){
    if( pTab->nEntry ){

      int nCol = pTab->nCol;      /* Local copy of member variable */
      u8 *abPK = pTab->abPK;      /* Local copy of member variable */
      int i;                      /* Used to iterate through hash buckets */
      sqlite3_stmt *pSel = 0;     /* SELECT statement to query table pTab */
      int nRewind = buf.nBuf;     /* Initial size of write buffer */
      int nNoop;                  /* Size of buffer after writing tbl header */

      /* Write a table header */
      sessionAppendByte(&buf, 'T', &rc);
      sessionAppendVarint(&buf, nCol, &rc);
      sessionAppendBlob(&buf, (u8 *)pTab->zName, strlen(pTab->zName)+1, &rc);

      /* Build and compile a statement to execute: */
      if( rc==SQLITE_OK ){

        rc = sessionSelectStmt(db, pTab->zName, nCol, pTab->azCol, abPK, &pSel);
      }

      if( rc==SQLITE_OK && nCol!=sqlite3_column_count(pSel) ){
        rc = SQLITE_SCHEMA;
      }

      nNoop = buf.nBuf;
................................................................................
** pointing to the prepared version of the SQL statement.
*/
static int sessionSelectRow(
  sqlite3 *db,                    /* Database handle */
  const char *zTab,               /* Table name */
  SessionApplyCtx *p              /* Session changeset-apply context */
){

  return sessionSelectStmt(db, zTab, p->nCol, p->azCol, p->abPK, &p->pSelect);
}

/*
** Formulate and prepare an INSERT statement to add a record to table zTab.
** For example:
**
**     INSERT INTO main."zTab" VALUES(?1, ?2, ?3 ...);







>












>
>







 







>










|



>
|







 







>
|







1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
....
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
....
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
    }
    sqlite3_free(buf2.aBuf);
  }
}

static int sessionSelectStmt(
  sqlite3 *db,                    /* Database handle */
  const char *zDb,                /* Database name */
  const char *zTab,               /* Table name */
  int nCol,
  const char **azCol,
  u8 *abPK,
  sqlite3_stmt **ppStmt
){
  int rc = SQLITE_OK;
  int i;
  const char *zSep = "";
  SessionBuffer buf = {0, 0, 0};

  sessionAppendStr(&buf, "SELECT * FROM ", &rc);
  sessionAppendIdent(&buf, zDb, &rc);
  sessionAppendStr(&buf, ".", &rc);
  sessionAppendIdent(&buf, zTab, &rc);
  sessionAppendStr(&buf, " WHERE ", &rc);
  for(i=0; i<nCol; i++){
    if( abPK[i] ){
      sessionAppendStr(&buf, zSep, &rc);
      sessionAppendIdent(&buf, azCol[i], &rc);
      sessionAppendStr(&buf, " = ?", &rc);
................................................................................
  ** this call will be a no-op.  */
  *pnChangeset = 0;
  *ppChangeset = 0;
  rc = pSession->rc;

  for(pTab=pSession->pTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){
    if( pTab->nEntry ){
      const char *zName = pTab->zName;
      int nCol = pTab->nCol;      /* Local copy of member variable */
      u8 *abPK = pTab->abPK;      /* Local copy of member variable */
      int i;                      /* Used to iterate through hash buckets */
      sqlite3_stmt *pSel = 0;     /* SELECT statement to query table pTab */
      int nRewind = buf.nBuf;     /* Initial size of write buffer */
      int nNoop;                  /* Size of buffer after writing tbl header */

      /* Write a table header */
      sessionAppendByte(&buf, 'T', &rc);
      sessionAppendVarint(&buf, nCol, &rc);
      sessionAppendBlob(&buf, (u8 *)zName, strlen(zName)+1, &rc);

      /* Build and compile a statement to execute: */
      if( rc==SQLITE_OK ){
        rc = sessionSelectStmt(
            db, pSession->zDb, zName, nCol, pTab->azCol, abPK, &pSel);
      }

      if( rc==SQLITE_OK && nCol!=sqlite3_column_count(pSel) ){
        rc = SQLITE_SCHEMA;
      }

      nNoop = buf.nBuf;
................................................................................
** pointing to the prepared version of the SQL statement.
*/
static int sessionSelectRow(
  sqlite3 *db,                    /* Database handle */
  const char *zTab,               /* Table name */
  SessionApplyCtx *p              /* Session changeset-apply context */
){
  return sessionSelectStmt(
      db, "main", zTab, p->nCol, p->azCol, p->abPK, &p->pSelect);
}

/*
** Formulate and prepare an INSERT statement to add a record to table zTab.
** For example:
**
**     INSERT INTO main."zTab" VALUES(?1, ?2, ?3 ...);