/ Check-in [09985fa6]
Login

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

Overview
Comment:Fix a segfault in the sessions module that could follow an OOM.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | sessions
Files: files | file ages | folders
SHA1:09985fa6b60a0bf38e23bbccd4d8e1d1cbf66124
User & Date: dan 2014-09-27 18:18:32
Context
2014-09-27
19:51
Merge recent trunk changes (performance enhancements) into the sessions branch. check-in: 497367cb user: drh tags: sessions
18:18
Fix a segfault in the sessions module that could follow an OOM. check-in: 09985fa6 user: dan tags: sessions
16:33
Fix a segfault in the streaming API functions triggered by a very long table name. check-in: d2642543 user: dan tags: sessions
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to ext/session/session9.test.

196
197
198
199
200
201
202

203
204
205
206
207
208
209
...
246
247
248
249
250
251
252
253
































254

    SELECT * FROM t2;
  }
} {one {value one} {value one} one}

do_test 3.3.1 {
  list [catch {sqlite3changeset_apply db $::ccdel1 conflict_handler} msg] $msg
} {1 SQLITE_CONSTRAINT}

do_test 3.3.2 {
  list [catch {sqlite3changeset_apply db $::ccdel2 conflict_handler} msg] $msg
} {1 SQLITE_CONSTRAINT}

do_test 3.3.4.1 {
  list [catch {sqlite3changeset_apply db $::ccdel conflict_handler} msg] $msg
} {0 {}}
................................................................................
}
do_catchsql_test 4.3.2 { 
  INSERT INTO c1 VALUES(1, 'x') 
} {1 {UNIQUE constraint failed: c1.a}}

do_catchsql_test 4.3.3 { COMMIT } {0 {}}
do_catchsql_test 4.3.4 { BEGIN ; COMMIT } {0 {}}

































finish_test








>







 








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

>
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
...
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
    SELECT * FROM t2;
  }
} {one {value one} {value one} one}

do_test 3.3.1 {
  list [catch {sqlite3changeset_apply db $::ccdel1 conflict_handler} msg] $msg
} {1 SQLITE_CONSTRAINT}

do_test 3.3.2 {
  list [catch {sqlite3changeset_apply db $::ccdel2 conflict_handler} msg] $msg
} {1 SQLITE_CONSTRAINT}

do_test 3.3.4.1 {
  list [catch {sqlite3changeset_apply db $::ccdel conflict_handler} msg] $msg
} {0 {}}
................................................................................
}
do_catchsql_test 4.3.2 { 
  INSERT INTO c1 VALUES(1, 'x') 
} {1 {UNIQUE constraint failed: c1.a}}

do_catchsql_test 4.3.3 { COMMIT } {0 {}}
do_catchsql_test 4.3.4 { BEGIN ; COMMIT } {0 {}}

#--------------------------------------------------------------------
# Test that if a DELETE change cannot be applied due to an 
# SQLITE_CONSTRAINT error thrown by a trigger program, things do not
# go awry.

drop_all_tables
reset_db
do_execsql_test 5.1 {
  CREATE TABLE x1(x PRIMARY KEY, y);
  CREATE TABLE x2(x PRIMARY KEY, y);
  INSERT INTO x2 VALUES(1, 1);
  INSERT INTO x1 VALUES(1, 1);
}

set ::cc [changeset_from_sql { DELETE FROM x1; }]

do_execsql_test 5.2 {
  INSERT INTO x1 VALUES(1, 1);
  CREATE TRIGGER tr1 AFTER DELETE ON x1 BEGIN
    INSERT INTO x2 VALUES(old.x, old.y);
  END;
} {}

proc conflict_handler {args} { return "ABORT" }
do_test 5.3 {
  list [catch {sqlite3changeset_apply db $::cc conflict_handler} msg] $msg
} {1 SQLITE_ABORT}

do_execsql_test 5.4 {
  SELECT * FROM X1;
} {1 1}

finish_test

Changes to ext/session/sessionA.test.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
..
59
60
61
62
63
64
65



66





































67
68
69
#
#    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 file tests that the sessions module handles foreign key constraint
# violations when applying changesets as required.
#

if {![info exists testdir]} {
  set testdir [file join [file dirname [info script]] .. .. test]
} 
source [file join [file dirname [info script]] session_common.tcl]
source $testdir/tester.tcl
................................................................................
  execsql {
    SELECT * FROM t1;
    SELECT * FROM t2;
    SELECT * FROM t3;
  } db2
} {a b e f}










































finish_test









|
<







 







>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



5
6
7
8
9
10
11
12

13
14
15
16
17
18
19
..
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#
#    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 file tests that filter callbacks work as required.

#

if {![info exists testdir]} {
  set testdir [file join [file dirname [info script]] .. .. test]
} 
source [file join [file dirname [info script]] session_common.tcl]
source $testdir/tester.tcl
................................................................................
  execsql {
    SELECT * FROM t1;
    SELECT * FROM t2;
    SELECT * FROM t3;
  } db2
} {a b e f}

#-------------------------------------------------------------------------
# Test that filter callbacks passed to sqlite3changeset_apply() are 
# invoked correctly.
#
reset_db
do_execsql_test 3.1 {
  CREATE TABLE t1(a PRIMARY KEY, b);
  CREATE TABLE t2(x PRIMARY KEY, y);
}

do_test 3.2 {
  execsql BEGIN
  set ::cs [changeset_from_sql { 
    INSERT INTO t1 VALUES(1, 2);
    INSERT INTO t2 VALUES('x', 'y');
  }]
  execsql ROLLBACK
  set {} {}
} {}

proc filter {x y} { 
  return [string equal $x $y] 
}

do_test 3.3 {
  sqlite3changeset_apply db $::cs {} [list filter t1]
  execsql {
    SELECT * FROM t1;
    SELECT * FROM t2;
  }
} {1 2}

do_test 3.4 {
  execsql { DELETE FROM t1 }
  sqlite3changeset_apply db $::cs {} [list filter t2]
  execsql {
    SELECT * FROM t1;
    SELECT * FROM t2;
  }
} {x y}

finish_test


Changes to ext/session/sessionfault.test.

280
281
282
283
284
285
286
































287
288
289
290
291
292
293
...
511
512
513
514
515
516
517


518
































519
520
521
    foreach c "
        {UPDATE t2 0 X. {i 1 t $::abc} {{} {} t abc}}
        {UPDATE t2 0 X. {i 2 t $::def} {{} {} t def}}
    " { lappend y $c }
    if {$x != $y} { error "changeset no good" }
  }
}

































#-------------------------------------------------------------------------
# Test that OOM errors in sqlite3changeset_concat() are handled correctly.
#
catch {db close}
forcedelete test.db
sqlite3 db test.db
................................................................................
  if {$testrc==0} {
    if {[db one {SELECT b=$bigstr||'x' FROM t1}]==0} {
      error "data does not look right"
    }
  }
}






































finish_test







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







 







>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
...
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
    foreach c "
        {UPDATE t2 0 X. {i 1 t $::abc} {{} {} t abc}}
        {UPDATE t2 0 X. {i 2 t $::def} {{} {} t def}}
    " { lappend y $c }
    if {$x != $y} { error "changeset no good" }
  }
}

catch {db close}
catch {db2 close}
forcedelete test.db
sqlite3 db test.db
set abc [string repeat abc 256]
set def [string repeat def 256]
execsql "
  CREATE TABLE t2(a PRIMARY KEY, b);
  INSERT INTO t2 VALUES(1, '$abc');
"
set changeset [changeset_from_sql "
  INSERT INTO t2 VALUES(2, '$def');
  DELETE FROM t2 WHERE a = 1;
"]
db close

do_faultsim_test 5.3 -faults oom-tra* -body {
  set ::inverse [sqlite3changeset_invert $::changeset]
  set {} {}
} -test {
  faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
  if {$testrc==0} {
    set x [list]
    sqlite3session_foreach c $::inverse { lappend x $c }
    foreach c "
        {INSERT t2 0 X. {} {i 1 t $::abc}}
        {DELETE t2 0 X. {i 2 t $::def} {}}
    " { lappend y $c }
    if {$x != $y} { error "changeset no good" }
  }
}

#-------------------------------------------------------------------------
# Test that OOM errors in sqlite3changeset_concat() are handled correctly.
#
catch {db close}
forcedelete test.db
sqlite3 db test.db
................................................................................
  if {$testrc==0} {
    if {[db one {SELECT b=$bigstr||'x' FROM t1}]==0} {
      error "data does not look right"
    }
  }
}

#-------------------------------------------------------------------------
# Test an OOM with an sqlite3changeset_apply() filter callback.
#
reset_db
do_test 11.prep {
  execsql {
    CREATE TABLE t1(a PRIMARY KEY, b);
    CREATE TABLE t2(x PRIMARY KEY, y);
    BEGIN;
  }

  set ::cs [changeset_from_sql { 
    INSERT INTO t1 VALUES(1, 2);
    INSERT INTO t2 VALUES('x', 'y');
  }]

  execsql ROLLBACK
  set {} {}
} {}

proc filter {x} { return [string equal t1 $x] } 
faultsim_save_and_close

do_faultsim_test 11 -faults oom-tra* -prep {
  faultsim_restore_and_reopen
} -body {
  sqlite3changeset_apply db $::cs {} filter
} -test {
  faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
  if {$testrc==0} {
    if {[db eval {SELECT * FROM t1 UNION ALL SELECT * FROM t2}] != "1 2"} {
      error "data does not look right"
    }
  }
}


finish_test

Changes to ext/session/sqlite3session.c.

3491
3492
3493
3494
3495
3496
3497




3498
3499
3500
3501
3502
3503
3504

      /* If an xFilter() callback was specified, invoke it now. If the 
      ** xFilter callback returns zero, skip this table. If it returns
      ** non-zero, proceed. */
      schemaMismatch = (xFilter && (0==xFilter(pCtx, zNew)));
      if( schemaMismatch ){
        zTab = sqlite3_mprintf("%s", zNew);




        nTab = (int)strlen(zTab);
        sApply.azCol = (const char **)zTab;
      }else{
        sqlite3changeset_pk(pIter, &abPK, 0);
        rc = sessionTableInfo(
            db, "main", zNew, &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK
        );







>
>
>
>







3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508

      /* If an xFilter() callback was specified, invoke it now. If the 
      ** xFilter callback returns zero, skip this table. If it returns
      ** non-zero, proceed. */
      schemaMismatch = (xFilter && (0==xFilter(pCtx, zNew)));
      if( schemaMismatch ){
        zTab = sqlite3_mprintf("%s", zNew);
        if( zTab==0 ){
          rc = SQLITE_NOMEM;
          break;
        }
        nTab = (int)strlen(zTab);
        sApply.azCol = (const char **)zTab;
      }else{
        sqlite3changeset_pk(pIter, &abPK, 0);
        rc = sessionTableInfo(
            db, "main", zNew, &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK
        );