SQLite

Check-in [1fc3f15d88]
Login

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

Overview
Comment:Add further tests for the sqlite3changeset_concat() function. Also fixes.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | sessions
Files: files | file ages | folders
SHA1: 1fc3f15d88c160b45642b46d1d54c591af058ba2
User & Date: dan 2011-04-14 18:01:41.789
Context
2011-04-15
12:04
Add documentation for sqlite3changeset_concat() to sqlite3session.h. (check-in: ada9efa53a user: dan tags: sessions)
2011-04-14
18:01
Add further tests for the sqlite3changeset_concat() function. Also fixes. (check-in: 1fc3f15d88 user: dan tags: sessions)
11:16
Start adding the sqlite3changeset_concat() function to the session module. (check-in: 8927b2260b user: dan tags: sessions)
Changes
Unified Diff Ignore Whitespace Patch
Changes to ext/session/session5.test.
17
18
19
20
21
22
23
24




25

26
27
28
29
30


31
32

33




34

35



36
37

38
39
40
41
42
43
44
45
46
47
48

49
50

51
52
53
54
55
56
57
} 
source [file join [file dirname [info script]] session_common.tcl]
source $testdir/tester.tcl
ifcapable !session {finish_test; return}

set testprefix session5

proc do_concat_test {tn sql1 sql2} {




  sqlite3session S1 db main ; S1 attach *

  sqlite3session S2 db main ; S2 attach *

  execsql $sql1
  set C1 [S1 changeset]
  S1 delete



  sqlite3session S1 db main ; S1 attach *






  execsql $sql2

  set C2 [S1 changeset]



  S1 delete


  set C3 [S2 changeset]
  S2 delete

  set C4 [sqlite3changeset_concat $C1 $C2]

  set c3 [list]
  set c4 [list]
  sqlite3session_foreach c $C3 { lappend c3 $c }
  sqlite3session_foreach c $C4 { lappend c4 $c }
  set c3 [lsort $c3]
  set c4 [lsort $c4]


  do_test $tn [list set {} $c4] $c3

}

do_execsql_test 1.0 {
  CREATE TABLE t1(a PRIMARY KEY, b);
}

do_concat_test 1.1.1 {







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

>
>
>
>
|
>
|
>
>
>
|

>
|
<

|
|
|
<
<
<
<
<
>
|
|
>







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

56
57
58
59





60
61
62
63
64
65
66
67
68
69
70
} 
source [file join [file dirname [info script]] session_common.tcl]
source $testdir/tester.tcl
ifcapable !session {finish_test; return}

set testprefix session5

# Organization of tests:
#
#   session5-1.*: Simple tests to check the concat() function produces 
#                 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

    set c [S changeset]
    if {[info commands s_prev] != ""} {
      set c_concat [sqlite3changeset_concat $c_prev $c]
      set c_two [s_prev changeset]
      s_prev delete

      set h_concat [changeset_to_list $c_concat]
      set h_two [changeset_to_list $c_two]


      do_test $tn.$subtest [list set {} $h_concat] $h_two
    }
    set c_prev $c





    rename S s_prev
  }

  catch { s_prev delete }
}

do_execsql_test 1.0 {
  CREATE TABLE t1(a PRIMARY KEY, b);
}

do_concat_test 1.1.1 {
116
117
118
119
120
121
122
123



















































124
  UPDATE t1 SET b = 'seven' WHERE a = 'I';
}
do_concat_test 1.2.7 {
  UPDATE t1 SET b = 'eight' WHERE a = 'I';
} {
  DELETE FROM t1 WHERE a = 'I';
}




















































finish_test








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

129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
  UPDATE t1 SET b = 'seven' WHERE a = 'I';
}
do_concat_test 1.2.7 {
  UPDATE t1 SET b = 'eight' WHERE a = 'I';
} {
  DELETE FROM t1 WHERE a = 'I';
}

db function indirect indirect 
proc indirect {{x -1}} {
  S indirect $x
  s_prev indirect $x
}

do_concat_test 2.1 {
  CREATE TABLE abc(a, b, c PRIMARY KEY);
  INSERT INTO abc VALUES(NULL, NULL, 1);
  INSERT INTO abc VALUES('abcdefghijkl', NULL, 2);
} {
  DELETE FROM abc WHERE c = 1;
  UPDATE abc SET c = 1 WHERE c = 2;
} {
  INSERT INTO abc VALUES('abcdefghijkl', NULL, 2);
  INSERT INTO abc VALUES(1.0, 2.0, 3);
} {
  UPDATE abc SET a = a-1;
} {
  CREATE TABLE def(d, e, f, PRIMARY KEY(e, f));
  INSERT INTO def VALUES('x', randomblob(11000), 67);
  INSERT INTO def SELECT d, e, f+1 FROM def;
  INSERT INTO def SELECT d, e, f+2 FROM def;
  INSERT INTO def SELECT d, e, f+4 FROM def;
} {
  DELETE FROM def WHERE rowid>4;
} { 
  INSERT INTO def SELECT d, e, f+4 FROM def; 
} {
  INSERT INTO abc VALUES(22, 44, -1);
} { 
  UPDATE abc SET c=-2 WHERE c=-1;
  UPDATE abc SET c=-3 WHERE c=-2;
} {
  UPDATE abc SET c=-4 WHERE c=-3;
} {
  UPDATE abc SET a=a+1 WHERE c=-3;
  UPDATE abc SET a=a+1 WHERE c=-3;
} {
  UPDATE abc SET a=a+1 WHERE c=-3;
  UPDATE abc SET a=a+1 WHERE c=-3;
} {
  INSERT INTO abc VALUES('one', 'two', 'three');
} {
  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';
}

finish_test
Changes to ext/session/sqlite3session.c.
348
349
350
351
352
353
354














355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379

380

381


382
383
384
385
386
387
388
389
390



391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
      }
    }
  }

  *piHash = (h % pTab->nChange);
  return SQLITE_OK;
}















/*
** Based on the primary key values stored in change aRecord, calculate a
** hash key, assuming the has table has nBucket buckets. The hash keys
** calculated by this function are compatible with those calculated by
** sessionPreupdateHash().
*/
static unsigned int sessionChangeHash(
  SessionTable *pTab,             /* Table handle */
  u8 *aRecord,                    /* Change record */
  int nBucket                     /* Assume this many buckets in hash table */
){
  unsigned int h = 0;             /* Value to return */
  int i;                          /* Used to iterate through columns */
  u8 *a = aRecord;                /* Used to iterate through change record */

  for(i=0; i<pTab->nCol; i++){
    int eType = *a++;
    int isPK = pTab->abPK[i];

    /* It is not possible for eType to be SQLITE_NULL here. The session 
    ** module does not record changes for rows with NULL values stored in
    ** primary key columns. */
    assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT 
         || eType==SQLITE_TEXT || eType==SQLITE_BLOB 

    );




    if( isPK ) h = HASH_APPEND(h, eType);
    if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
      if( isPK ) h = sessionHashAppendI64(h, sessionGetI64(a));
      a += 8;
    }else{
      int n; 
      a += sessionVarintGet(a, &n);
      if( isPK ) h = sessionHashAppendBlob(h, n, a);
      a += n;



    }
  }
  return (h % nBucket);
}

static int sessionSerialLen(u8 *a){
  int e = *a;
  int n;
  if( e==0 ) return 1;
  if( e==SQLITE_NULL ) return 1;
  if( e==SQLITE_INTEGER || e==SQLITE_FLOAT ) return 9;
  return sessionVarintGet(&a[1], &n) + 1 + n;
}

static int sessionChangeEqual(
  SessionTable *pTab,
  u8 *aLeft,                      /* Change record */
  u8 *aRight                      /* Change record */
){
  u8 *a1 = aLeft;
  u8 *a2 = aRight;
  int i;

  for(i=0; i<pTab->nCol; i++){
    int n1 = sessionSerialLen(a1);
    int n2 = sessionSerialLen(a2);

    if( pTab->abPK[i] && (n1!=n2 || memcmp(a1, a2, n1)) ){
      return 0;
    }
    a1 += n1;
    a2 += n1;
  }

  return 1;
}

static void sessionMergeRecord(
  u8 **paOut, 







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

















|







>

>

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





<
<
<
<
<
<
<
<
<

















|







348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416









417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
      }
    }
  }

  *piHash = (h % pTab->nChange);
  return SQLITE_OK;
}

/*
** The buffer that the argument points to contains a serialized SQL value.
** Return the number of bytes of space occupied by the value (including
** the type byte).
*/
static int sessionSerialLen(u8 *a){
  int e = *a;
  int n;
  if( e==0 ) return 1;
  if( e==SQLITE_NULL ) return 1;
  if( e==SQLITE_INTEGER || e==SQLITE_FLOAT ) return 9;
  return sessionVarintGet(&a[1], &n) + 1 + n;
}

/*
** Based on the primary key values stored in change aRecord, calculate a
** hash key, assuming the has table has nBucket buckets. The hash keys
** calculated by this function are compatible with those calculated by
** sessionPreupdateHash().
*/
static unsigned int sessionChangeHash(
  SessionTable *pTab,             /* Table handle */
  u8 *aRecord,                    /* Change record */
  int nBucket                     /* Assume this many buckets in hash table */
){
  unsigned int h = 0;             /* Value to return */
  int i;                          /* Used to iterate through columns */
  u8 *a = aRecord;                /* Used to iterate through change record */

  for(i=0; i<pTab->nCol; i++){
    int eType = *a;
    int isPK = pTab->abPK[i];

    /* It is not possible for eType to be SQLITE_NULL here. The session 
    ** module does not record changes for rows with NULL values stored in
    ** primary key columns. */
    assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT 
         || eType==SQLITE_TEXT || eType==SQLITE_BLOB 
         || eType==SQLITE_NULL || eType==0 
    );
    assert( !isPK || (eType!=0 && eType!=SQLITE_NULL) );

    if( isPK ){
      a++;
      h = HASH_APPEND(h, eType);
      if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
        if( isPK ) h = sessionHashAppendI64(h, sessionGetI64(a));
        a += 8;
      }else{
        int n; 
        a += sessionVarintGet(a, &n);
        if( isPK ) h = sessionHashAppendBlob(h, n, a);
        a += n;
      }
    }else{
      a += sessionSerialLen(a);
    }
  }
  return (h % nBucket);
}










static int sessionChangeEqual(
  SessionTable *pTab,
  u8 *aLeft,                      /* Change record */
  u8 *aRight                      /* Change record */
){
  u8 *a1 = aLeft;
  u8 *a2 = aRight;
  int i;

  for(i=0; i<pTab->nCol; i++){
    int n1 = sessionSerialLen(a1);
    int n2 = sessionSerialLen(a2);

    if( pTab->abPK[i] && (n1!=n2 || memcmp(a1, a2, n1)) ){
      return 0;
    }
    a1 += n1;
    a2 += n2;
  }

  return 1;
}

static void sessionMergeRecord(
  u8 **paOut, 
Changes to ext/session/sqlite3session.h.
518
519
520
521
522
523
524



525
526
527
528
529
530
531
532
533
534
535
** changeset. If it is not, the results are undefined.
*/
int sqlite3changeset_invert(
  int nIn, void *pIn,             /* Input changeset */
  int *pnOut, void **ppOut        /* OUT: Inverse of input */
);




int sqlite3changeset_concat(
  int nLeft, void *pLeft,         /* Input changeset */
  int nRight, void *Right,        /* Input changeset */
  int *pnOut, void **ppOut        /* OUT: Inverse of input */
);

/*
** CAPI3REF: Apply A Changeset To A Database
**
** Apply a changeset to a database. This function attempts to update the
** "main" database attached to handle db with the changes found in the







>
>
>

|
|
|







518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
** changeset. If it is not, the results are undefined.
*/
int sqlite3changeset_invert(
  int nIn, void *pIn,             /* Input changeset */
  int *pnOut, void **ppOut        /* OUT: Inverse of input */
);

/*
** CAPI3REF: Combine Two Changeset Objects
*/
int sqlite3changeset_concat(
  int nLeft, void *pLeft,         /* First input changeset */
  int nRight, void *Right,        /* Second input changeset */
  int *pnOut, void **ppOut        /* OUT: Output changeset */
);

/*
** CAPI3REF: Apply A Changeset To A Database
**
** Apply a changeset to a database. This function attempts to update the
** "main" database attached to handle db with the changes found in the