SQLite

Check-in [df0b2d21dc]
Login

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

Overview
Comment:Add tests and fixes for OOM handling in sqlite3changeset_concat().
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | sessions
Files: files | file ages | folders
SHA1: df0b2d21dcd81679e55b24866f97568019932983
User & Date: dan 2011-04-15 16:03:32.057
Context
2011-04-15
19:18
Improve coverage of session module. (check-in: 3dfd1d63bd user: dan tags: sessions)
16:03
Add tests and fixes for OOM handling in sqlite3changeset_concat(). (check-in: df0b2d21dc user: dan tags: sessions)
15:04
Have sqlite3changeset_concat() return SQLITE_SCHEMA if an attempt is made to concatenate changesets based on incompatible database schemas. (check-in: 343b64517d user: dan tags: sessions)
Changes
Unified Diff Ignore Whitespace Patch
Changes to ext/session/session5.test.
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#                 correct results.
#
#   session5-2.*: More complicated tests.
#   
#   session5-3.*: Schema mismatch errors.
#

proc changeset_to_list {c} {
  set list [list]
  sqlite3session_foreach elem $c { lappend list $elem }
  lsort $list
}

proc do_concat_test {tn args} {

  set subtest 0
  foreach sql $args {
    incr subtest
    sqlite3session S db main ; S attach *
    execsql $sql







<
<
<
<
<
<







27
28
29
30
31
32
33






34
35
36
37
38
39
40
#                 correct results.
#
#   session5-2.*: More complicated tests.
#   
#   session5-3.*: Schema mismatch errors.
#







proc do_concat_test {tn args} {

  set subtest 0
  foreach sql $args {
    incr subtest
    sqlite3session S db main ; S attach *
    execsql $sql
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
} {
  SELECT indirect(1);
  UPDATE abc SET a='one point five' WHERE c = 'three';
} {
  SELECT indirect(0);
  UPDATE abc SET a='one point six' WHERE c = 'three';
}




















#-------------------------------------------------------------------------
# Test that schema incompatibilities are detected correctly.
#
#   session5-3.1: Incompatible number of columns.
#   session5-3.2: Incompatible PK definition.
#
proc sql_to_changeset {sql} {
  sqlite3session S db main 
  S attach *
  execsql $sql
  set c [S changeset]
  S delete
  return $c
}

do_test 3.1 {
  db close
  forcedelete test.db
  sqlite3 db test.db

  execsql { CREATE TABLE t1(a PRIMARY KEY, b) }
  set c1 [sql_to_changeset { INSERT INTO t1 VALUES(1, 2) }]
  execsql { 
    DROP TABLE t1;
    CREATE TABLE t1(a PRIMARY KEY, b, c);
  }
  set c2 [sql_to_changeset { INSERT INTO t1 VALUES(2, 3, 4) }]

  list [catch { sqlite3changeset_concat $c1 $c2 } msg] $msg
} {1 SQLITE_SCHEMA}

do_test 3.2 {
  db close
  forcedelete test.db
  sqlite3 db test.db

  execsql { CREATE TABLE t1(a PRIMARY KEY, b) }
  set c1 [sql_to_changeset { INSERT INTO t1 VALUES(1, 2) }]
  execsql { 
    DROP TABLE t1;
    CREATE TABLE t1(a, b PRIMARY KEY);
  }
  set c2 [sql_to_changeset { INSERT INTO t1 VALUES(2, 3) }]

  list [catch { sqlite3changeset_concat $c1 $c2 } msg] $msg
} {1 SQLITE_SCHEMA}


finish_test







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







<
<
<
<
<
<
<
<







|




|










|




|






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
} {
  SELECT indirect(1);
  UPDATE abc SET a='one point five' WHERE c = 'three';
} {
  SELECT indirect(0);
  UPDATE abc SET a='one point six' WHERE c = 'three';
}

catch {db close}
forcedelete test.db
sqlite3 db test.db
do_concat_test 2.2 {
  CREATE TABLE t1(a, b, PRIMARY KEY(b));
  CREATE TABLE t2(a PRIMARY KEY, b);
  INSERT INTO t1 VALUES('string', 1);
  INSERT INTO t1 VALUES(4, 2);
  INSERT INTO t1 VALUES(X'FFAAFFAAFFAA', 3);
} {
  INSERT INTO t2 VALUES('one', 'two');
  INSERT INTO t2 VALUES(1, NULL);
  UPDATE t1 SET a = 5 WHERE a = 2;
} {
  DELETE FROM t2 WHERE a = 1;
  UPDATE t1 SET a = 4 WHERE a = 2;
  INSERT INTO t2 VALUES('x', 'y');
}

#-------------------------------------------------------------------------
# Test that schema incompatibilities are detected correctly.
#
#   session5-3.1: Incompatible number of columns.
#   session5-3.2: Incompatible PK definition.
#









do_test 3.1 {
  db close
  forcedelete test.db
  sqlite3 db test.db

  execsql { CREATE TABLE t1(a PRIMARY KEY, b) }
  set c1 [changeset_from_sql { INSERT INTO t1 VALUES(1, 2) }]
  execsql { 
    DROP TABLE t1;
    CREATE TABLE t1(a PRIMARY KEY, b, c);
  }
  set c2 [changeset_from_sql { INSERT INTO t1 VALUES(2, 3, 4) }]

  list [catch { sqlite3changeset_concat $c1 $c2 } msg] $msg
} {1 SQLITE_SCHEMA}

do_test 3.2 {
  db close
  forcedelete test.db
  sqlite3 db test.db

  execsql { CREATE TABLE t1(a PRIMARY KEY, b) }
  set c1 [changeset_from_sql { INSERT INTO t1 VALUES(1, 2) }]
  execsql { 
    DROP TABLE t1;
    CREATE TABLE t1(a, b PRIMARY KEY);
  }
  set c2 [changeset_from_sql { INSERT INTO t1 VALUES(2, 3) }]

  list [catch { sqlite3changeset_concat $c1 $c2 } msg] $msg
} {1 SQLITE_SCHEMA}


finish_test
Changes to ext/session/session_common.tcl.
124
125
126
127
128
129
130




131
132
      error "table $tbl data mismatch" 
    }
  }

  return ""
}














>
>
>
>
|

124
125
126
127
128
129
130
131
132
133
134
135
136
      error "table $tbl data mismatch" 
    }
  }

  return ""
}

proc changeset_to_list {c} {
  set list [list]
  sqlite3session_foreach elem $c { lappend list $elem }
  lsort $list
}

Changes to ext/session/sessionfault.test.
246
247
248
249
250
251
252











































253
254
        {DELETE t1 0 .X {t xxx t yyy} {}} 
        {INSERT t1 0 .X {} {t string i 1}} 
        {UPDATE t1 0 .X {i 20 {} {}} {i 4 i 2}}
    } { lappend y $c }
    if {$x != $y} { error "changeset no good" }
  }
}












































finish_test







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


246
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
289
290
291
292
293
294
295
296
297
        {DELETE t1 0 .X {t xxx t yyy} {}} 
        {INSERT t1 0 .X {} {t string i 1}} 
        {UPDATE t1 0 .X {i 20 {} {}} {i 4 i 2}}
    } { 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
do_execsql_test 5.prep1 {
  CREATE TABLE t1(a, b, PRIMARY KEY(b));
  CREATE TABLE t2(a PRIMARY KEY, b);
  INSERT INTO t1 VALUES('string', 1);
  INSERT INTO t1 VALUES(4, 2);
  INSERT INTO t1 VALUES(X'FFAAFFAAFFAA', 3);
}

do_test 5.prep2 {
  sqlite3session M db main
  M attach *
  set ::c2 [changeset_from_sql {
    INSERT INTO t2 VALUES(randomblob(1000), randomblob(1000));
    INSERT INTO t2 VALUES('one', 'two');
    INSERT INTO t2 VALUES(1, NULL);
    UPDATE t1 SET a = 5 WHERE a = 2;
  }]
  set ::c1 [changeset_from_sql {
    DELETE FROM t2 WHERE a = 1;
    UPDATE t1 SET a = 4 WHERE a = 2;
    INSERT INTO t2 VALUES('x', 'y');
  }]
  set ::total [changeset_to_list [M changeset]]
  M delete
} {}

do_faultsim_test 6 -faults oom-* -body {
  set ::result [sqlite3changeset_concat $::c1 $::c2]
  set {} {}
} -test {
  faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
  if {$testrc==0} {
    set v [changeset_to_list $::result]
    if {$v != $::total} { error "result no good" }
  }
}

finish_test
Changes to ext/session/sqlite3session.c.
2782
2783
2784
2785
2786
2787
2788

2789
2790
2791
2792
2793
2794
2795
    }else{
      int nByte;
      u8 *aCsr;

      nByte = sizeof(SessionChange) + pExist->nRecord + nRec;
      pNew = (SessionChange *)sqlite3_malloc(nByte);
      if( !pNew ){

        return SQLITE_NOMEM;
      }
      memset(pNew, 0, sizeof(SessionChange));
      pNew->bIndirect = (bIndirect && pExist->bIndirect);
      aCsr = pNew->aRecord = (u8 *)&pNew[1];

      if( op1==SQLITE_INSERT && op2==SQLITE_UPDATE ){







>







2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
    }else{
      int nByte;
      u8 *aCsr;

      nByte = sizeof(SessionChange) + pExist->nRecord + nRec;
      pNew = (SessionChange *)sqlite3_malloc(nByte);
      if( !pNew ){
        sqlite3_free(pExist);
        return SQLITE_NOMEM;
      }
      memset(pNew, 0, sizeof(SessionChange));
      pNew->bIndirect = (bIndirect && pExist->bIndirect);
      aCsr = pNew->aRecord = (u8 *)&pNew[1];

      if( op1==SQLITE_INSERT && op2==SQLITE_UPDATE ){
2882
2883
2884
2885
2886
2887
2888
2889



2890
2891
2892
2893
2894
2895
2896
      }else if( pTab->nCol!=nCol || memcmp(pTab->abPK, abPK, nCol) ){
        rc = SQLITE_SCHEMA;
        break;
      }
      pTab->zName = (char *)zNew;
    }

    if( sessionGrowHash(pTab) ) break;



    iHash = sessionChangeHash(pTab, aRec, pTab->nChange);

    /* Search for existing entry. If found, remove it from the hash table. 
    ** Code below may link it back in.
    */
    for(pp=&pTab->apChange[iHash]; *pp; pp=&(*pp)->pNext){
      if( sessionChangeEqual(pTab, (*pp)->aRecord, aRec) ){







|
>
>
>







2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
      }else if( pTab->nCol!=nCol || memcmp(pTab->abPK, abPK, nCol) ){
        rc = SQLITE_SCHEMA;
        break;
      }
      pTab->zName = (char *)zNew;
    }

    if( sessionGrowHash(pTab) ){
      rc = SQLITE_NOMEM;
      break;
    }
    iHash = sessionChangeHash(pTab, aRec, pTab->nChange);

    /* Search for existing entry. If found, remove it from the hash table. 
    ** Code below may link it back in.
    */
    for(pp=&pTab->apChange[iHash]; *pp; pp=&(*pp)->pNext){
      if( sessionChangeEqual(pTab, (*pp)->aRecord, aRec) ){