/ Check-in [75d5dff7]
Login

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

Overview
Comment:Add a function to the session extension invert a changeset.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | sessions
Files: files | file ages | folders
SHA1: 75d5dff725dbb66d67d56ad042926f1daae56dbe
User & Date: dan 2011-03-09 11:17:05
Context
2011-03-11
19:05
Add the sqlite3changeset_apply() function. Does not yet handle all cases. check-in: 2b19be7b user: dan tags: sessions
2011-03-09
11:17
Add a function to the session extension invert a changeset. check-in: 75d5dff7 user: dan tags: sessions
2011-03-08
19:22
Add start of sessions feature. check-in: 269a81a3 user: dan tags: sessions
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to ext/session/sqlite3session.c.

835
836
837
838
839
840
841
842
843

844
845

846
847
848
849
850

851

852
853
854

855
856
857
858
859
860
861
862
863


864
865
866
867
868
869
870
...
985
986
987
988
989
990
991
992














































































993
  sqlite3_value **apOut           /* Write values to this array */
){
  int i;
  u8 *aRec = *paChange;

  for(i=0; i<nCol; i++){
    int eType = *aRec++;
    assert( apOut[i]==0 );
    if( eType ){

      apOut[i] = sqlite3ValueNew(0);
      if( !apOut[i] ) return SQLITE_NOMEM;


      if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
        int nByte;
        int enc = (eType==SQLITE_TEXT ? SQLITE_UTF8 : 0);
        aRec += sessionVarintGet(aRec, &nByte);

        sqlite3ValueSetStr(apOut[i], nByte, aRec, enc, SQLITE_STATIC);

        aRec += nByte;
      }
      if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){

        sqlite3_int64 v = sessionGetI64(aRec);
        aRec += 8;
        if( eType==SQLITE_INTEGER ){
          sqlite3VdbeMemSetInt64(apOut[i], v);
        }else{
          double d;
          memcpy(&d, &i, 8);
          sqlite3VdbeMemSetDouble(apOut[i], d);
        }


      }
    }
  }

  *paChange = aRec;
  return SQLITE_OK;
}
................................................................................
  int i;
  int rc = p->rc;
  for(i=0; i<p->nCol*2; i++) sqlite3ValueFree(p->apValue[i]);
  sqlite3_free(p->apValue);
  sqlite3_free(p);
  return rc;
}















































































#endif        /* #ifdef SQLITE_ENABLE_SESSION */







|

>
|
|
>





>
|
>



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







 








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

835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860

861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
...
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
  sqlite3_value **apOut           /* Write values to this array */
){
  int i;
  u8 *aRec = *paChange;

  for(i=0; i<nCol; i++){
    int eType = *aRec++;
    assert( !apOut || apOut[i]==0 );
    if( eType ){
      if( apOut ){
        apOut[i] = sqlite3ValueNew(0);
        if( !apOut[i] ) return SQLITE_NOMEM;
      }

      if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
        int nByte;
        int enc = (eType==SQLITE_TEXT ? SQLITE_UTF8 : 0);
        aRec += sessionVarintGet(aRec, &nByte);
        if( apOut ){
          sqlite3ValueSetStr(apOut[i], nByte, aRec, enc, SQLITE_STATIC);
        }
        aRec += nByte;
      }
      if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
        if( apOut ){
          sqlite3_int64 v = sessionGetI64(aRec);

          if( eType==SQLITE_INTEGER ){
            sqlite3VdbeMemSetInt64(apOut[i], v);
          }else{
            double d;
            memcpy(&d, &i, 8);
            sqlite3VdbeMemSetDouble(apOut[i], d);
          }
        }
        aRec += 8;
      }
    }
  }

  *paChange = aRec;
  return SQLITE_OK;
}
................................................................................
  int i;
  int rc = p->rc;
  for(i=0; i<p->nCol*2; i++) sqlite3ValueFree(p->apValue[i]);
  sqlite3_free(p->apValue);
  sqlite3_free(p);
  return rc;
}

/*
** Invert a changeset object.
*/
int sqlite3changeset_invert(
  int nChangeset,                 /* Number of bytes in input */
  void *pChangeset,               /* Input changeset */
  int *pnInverted,                /* OUT: Number of bytes in output changeset */
  void **ppInverted               /* OUT: Inverse of pChangeset */
){
  u8 *aOut;
  u8 *aIn;
  int i;
  int nCol = 0;

  /* Zero the output variables in case an error occurs. */
  *ppInverted = 0;
  *pnInverted = 0;
  if( nChangeset==0 ) return SQLITE_OK;

  aOut = (u8 *)sqlite3_malloc(nChangeset);
  if( !aOut ) return SQLITE_NOMEM;
  aIn = (u8 *)pChangeset;

  i = 0;
  while( i<nChangeset ){
    u8 eType = aIn[i];
    switch( eType ){
      case 'T': {
        int nByte = 1 + sessionVarintGet(&aIn[i+1], &nCol);
        nByte += 1 + strlen((char *)&aIn[i+nByte]);
        memcpy(&aOut[i], &aIn[i], nByte);
        i += nByte;
        break;
      }

      case SQLITE_INSERT:
      case SQLITE_DELETE: {
        int nByte;
        u8 *aEnd = &aIn[i+1];

        sessionReadRecord(&aEnd, nCol, 0);
        aOut[i] = (eType==SQLITE_DELETE ? SQLITE_INSERT : SQLITE_DELETE);
        nByte = aEnd - &aIn[i+1];
        memcpy(&aOut[i+1], &aIn[i+1], nByte);
        i += 1 + nByte;
        break;
      }

      case SQLITE_UPDATE: {
        int nByte1;              /* Size of old.* record in bytes */
        int nByte2;              /* Size of new.* record in bytes */
        u8 *aEnd = &aIn[i+1];    

        sessionReadRecord(&aEnd, nCol, 0);
        nByte1 = aEnd - &aIn[i+1];
        sessionReadRecord(&aEnd, nCol, 0);
        nByte2 = aEnd - &aIn[i+1] - nByte1;

        aOut[i] = SQLITE_UPDATE;
        memcpy(&aOut[i+1], &aIn[i+1+nByte1], nByte2);
        memcpy(&aOut[i+1+nByte2], &aIn[i+1], nByte1);

        i += 1 + nByte1 + nByte2;
        break;
      }

      default:
        sqlite3_free(aOut);
        return SQLITE_CORRUPT;
    }
  }

  *pnInverted = nChangeset;
  *ppInverted = (void *)aOut;
  return SQLITE_OK;
}


#endif        /* #ifdef SQLITE_ENABLE_SESSION */

Changes to ext/session/sqlite3session.h.

116
117
118
119
120
121
122









123
124
125
/*
** Finalize an iterator allocated with sqlite3changeset_start().
**
** This function may not be called on iterators passed to a conflict handler
** callback by changeset_apply().
*/
int sqlite3changeset_finalize(sqlite3_changeset_iter *pIter);










#endif








>
>
>
>
>
>
>
>
>



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/*
** Finalize an iterator allocated with sqlite3changeset_start().
**
** This function may not be called on iterators passed to a conflict handler
** callback by changeset_apply().
*/
int sqlite3changeset_finalize(sqlite3_changeset_iter *pIter);

/*
** Invert a changeset object.
*/
int sqlite3changeset_invert(
  int nIn, void *pIn,             /* Input changeset */
  int *pnOut, void **ppOut        /* OUT: Inverse of input */
);


#endif

Changes to ext/session/test_session.c.

163
164
165
166
167
168
169





























170
171
172
173
174
175
176
...
247
248
249
250
251
252
253



254
255
256
257
258
            sqlite3_value_bytes(pVal)
        );
        break;
    }
    Tcl_ListObjAppendElement(0, pList, pObj);
  }
}






























/*
** sqlite3session_foreach VARNAME CHANGESET SCRIPT
*/
static int test_sqlite3session_foreach(
  void * clientData,
  Tcl_Interp *interp,
................................................................................
}

int TestSession_Init(Tcl_Interp *interp){
  Tcl_CreateObjCommand(interp, "sqlite3session", test_sqlite3session, 0, 0);
  Tcl_CreateObjCommand(
      interp, "sqlite3session_foreach", test_sqlite3session_foreach, 0, 0
  );



  return TCL_OK;
}

#endif








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







 







>
>
>





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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
...
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
            sqlite3_value_bytes(pVal)
        );
        break;
    }
    Tcl_ListObjAppendElement(0, pList, pObj);
  }
}

/*
** sqlite3changeset_invert CHANGESET
*/
static int test_sqlite3changeset_invert(
  void * clientData,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  int rc;                         /* Return code from changeset_invert() */
  void *aChangeset;               /* Input changeset */
  int nChangeSet;                 /* Size of buffer aChangeset in bytes */
  void *aOut;                     /* Output changeset */
  int nOut;                       /* Size of buffer aOut in bytes */

  if( objc!=2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "CHANGESET");
  }
  aChangeset = (void *)Tcl_GetByteArrayFromObj(objv[1], &nChangeSet);

  rc = sqlite3changeset_invert(nChangeSet, aChangeset, &nOut, &aOut);
  if( rc!=SQLITE_OK ){
    return test_session_error(interp, rc);
  }
  Tcl_SetObjResult(interp, Tcl_NewByteArrayObj((unsigned char *)aOut, nOut));
  sqlite3_free(aOut);
  return TCL_OK;
}

/*
** sqlite3session_foreach VARNAME CHANGESET SCRIPT
*/
static int test_sqlite3session_foreach(
  void * clientData,
  Tcl_Interp *interp,
................................................................................
}

int TestSession_Init(Tcl_Interp *interp){
  Tcl_CreateObjCommand(interp, "sqlite3session", test_sqlite3session, 0, 0);
  Tcl_CreateObjCommand(
      interp, "sqlite3session_foreach", test_sqlite3session_foreach, 0, 0
  );
  Tcl_CreateObjCommand(
      interp, "sqlite3changeset_invert", test_sqlite3changeset_invert, 0, 0
  );
  return TCL_OK;
}

#endif

Changes to test/session1.test.

10
11
12
13
14
15
16





















17
18
19
20
21
22



23
24
25
26
27
28
29
..
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
71
72





73
74
75
76
77
78
79
80
81
82
83
84





85
86
87
88
89
90
91
92
..
98
99
100
101
102
103
104





105
106
107
108
109
110
111
112
113
114
115

116
117
118
#***********************************************************************
# This file implements regression tests for SQLite library.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix session1






















do_execsql_test 1.0 {
  CREATE TABLE t1(x, y);
  INSERT INTO t1 VALUES('abc', 'def');
}




do_test 1.1 { sqlite3session S db main } {S}
do_test 1.2 { S delete } {}
do_test 1.3 { sqlite3session S db main } {S}
do_test 1.4 { S attach t1 } {}
do_test 1.5 { S delete } {}
do_test 1.6 { sqlite3session S db main } {S}
do_test 1.7 { S attach t1 ; S attach t2 ; S attach t3 } {}
................................................................................
  execsql { DELETE FROM t1 WHERE rowid = 2 }
} {}
do_test 1.13 {
  S changeset
  S delete
} {}

proc do_changeset_test {tn session res} {

  set r [list]
  foreach x $res {lappend r $x}
  uplevel do_test $tn [list [subst -nocommands {
    set x [list]
    sqlite3session_foreach c [$session changeset] { lappend x [set c] }
    set x
  }]] [list $r]
}

do_test 2.1.1 {
  execsql { DELETE FROM t1 }
  sqlite3session S db main
  S attach t1
  execsql { INSERT INTO t1 VALUES(1, 'Sukhothai') }
  execsql { INSERT INTO t1 VALUES(2, 'Ayutthaya') }
  execsql { INSERT INTO t1 VALUES(3, 'Thonburi') }
} {}
do_changeset_test 2.1.2 S {
  {INSERT t1 {} {i 1 t Sukhothai}}
  {INSERT t1 {} {i 2 t Ayutthaya}}
  {INSERT t1 {} {i 3 t Thonburi}}
}





do_test 2.1.3 { S delete } {}

do_test 2.2.1 {
  sqlite3session S db main
  S attach t1
  execsql { DELETE FROM t1 WHERE 1 }
} {}
do_changeset_test 2.2.2 S {
  {DELETE t1 {i 1 t Sukhothai} {}}
  {DELETE t1 {i 2 t Ayutthaya} {}}
  {DELETE t1 {i 3 t Thonburi} {}}
}





do_test 2.2.3 { S delete } {}

do_test 2.3.1 {
  sqlite3session S db main
  execsql { INSERT INTO t1 VALUES(1, 'Sukhothai') }
  execsql { INSERT INTO t1 VALUES(2, 'Ayutthaya') }
  execsql { INSERT INTO t1 VALUES(3, 'Thonburi') }
  S attach t1
................................................................................
} {}

do_changeset_test 2.3.2 S {
  {UPDATE t1 {i 1 {} {}} {i 10 {} {}}} 
  {UPDATE t1 {{} {} t Ayutthaya} {{} {} t Surin}} 
  {UPDATE t1 {i 3 t Thonburi} {i 20 t Thapae}}
}





do_test 2.3.3 { S delete } {}

do_test 2.4.1 {
  sqlite3session S db main
  S attach t1
  execsql { INSERT INTO t1 VALUES(100, 'Bangkok') }
  execsql { DELETE FROM t1 WHERE x = 100 }
} {}

breakpoint
do_changeset_test 2.4.2 S {}

do_test 2.4.3 { S delete } {}

finish_test







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






>
>
>







 







<
>
|
|
<
<
<
<
<
<
|













>
>
>
>
>
|











>
>
>
>
>
|







 







>
>
>
>
>
|







<
<

>
|


10
11
12
13
14
15
16
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
..
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
109
110
111
112
113
114
115
116
117
118
119
120
...
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145


146
147
148
149
150
#***********************************************************************
# This file implements regression tests for SQLite library.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix session1

proc do_changeset_test {tn session res} {
  set r [list]
  foreach x $res {lappend r $x}
  uplevel do_test $tn [list [subst -nocommands {
    set x [list]
    sqlite3session_foreach c [$session changeset] { lappend x [set c] }
    set x
  }]] [list $r]
}

proc do_changeset_invert_test {tn session res} {
  set r [list]
  foreach x $res {lappend r $x}
  uplevel do_test $tn [list [subst -nocommands {
    set x [list]
    set changeset [sqlite3changeset_invert [$session changeset]]
    sqlite3session_foreach c [set changeset] { lappend x [set c] }
    set x
  }]] [list $r]
}

do_execsql_test 1.0 {
  CREATE TABLE t1(x, y);
  INSERT INTO t1 VALUES('abc', 'def');
}

#-------------------------------------------------------------------------
# Test creating, attaching tables to and deleting session objects.
#
do_test 1.1 { sqlite3session S db main } {S}
do_test 1.2 { S delete } {}
do_test 1.3 { sqlite3session S db main } {S}
do_test 1.4 { S attach t1 } {}
do_test 1.5 { S delete } {}
do_test 1.6 { sqlite3session S db main } {S}
do_test 1.7 { S attach t1 ; S attach t2 ; S attach t3 } {}
................................................................................
  execsql { DELETE FROM t1 WHERE rowid = 2 }
} {}
do_test 1.13 {
  S changeset
  S delete
} {}


#-------------------------------------------------------------------------
# Simple changeset tests. Also test the sqlite3changeset_invert() 
# function.






#
do_test 2.1.1 {
  execsql { DELETE FROM t1 }
  sqlite3session S db main
  S attach t1
  execsql { INSERT INTO t1 VALUES(1, 'Sukhothai') }
  execsql { INSERT INTO t1 VALUES(2, 'Ayutthaya') }
  execsql { INSERT INTO t1 VALUES(3, 'Thonburi') }
} {}
do_changeset_test 2.1.2 S {
  {INSERT t1 {} {i 1 t Sukhothai}}
  {INSERT t1 {} {i 2 t Ayutthaya}}
  {INSERT t1 {} {i 3 t Thonburi}}
}
do_changeset_invert_test 2.1.3 S {
  {DELETE t1 {i 1 t Sukhothai} {}}
  {DELETE t1 {i 2 t Ayutthaya} {}}
  {DELETE t1 {i 3 t Thonburi} {}}
}
do_test 2.1.4 { S delete } {}

do_test 2.2.1 {
  sqlite3session S db main
  S attach t1
  execsql { DELETE FROM t1 WHERE 1 }
} {}
do_changeset_test 2.2.2 S {
  {DELETE t1 {i 1 t Sukhothai} {}}
  {DELETE t1 {i 2 t Ayutthaya} {}}
  {DELETE t1 {i 3 t Thonburi} {}}
}
do_changeset_invert_test 2.2.3 S {
  {INSERT t1 {} {i 1 t Sukhothai}}
  {INSERT t1 {} {i 2 t Ayutthaya}}
  {INSERT t1 {} {i 3 t Thonburi}}
}
do_test 2.2.4 { S delete } {}

do_test 2.3.1 {
  sqlite3session S db main
  execsql { INSERT INTO t1 VALUES(1, 'Sukhothai') }
  execsql { INSERT INTO t1 VALUES(2, 'Ayutthaya') }
  execsql { INSERT INTO t1 VALUES(3, 'Thonburi') }
  S attach t1
................................................................................
} {}

do_changeset_test 2.3.2 S {
  {UPDATE t1 {i 1 {} {}} {i 10 {} {}}} 
  {UPDATE t1 {{} {} t Ayutthaya} {{} {} t Surin}} 
  {UPDATE t1 {i 3 t Thonburi} {i 20 t Thapae}}
}
do_changeset_invert_test 2.3.3 S {
  {UPDATE t1 {i 10 {} {}} {i 1 {} {}}} 
  {UPDATE t1 {{} {} t Surin} {{} {} t Ayutthaya}} 
  {UPDATE t1 {i 20 t Thapae} {i 3 t Thonburi}}
}
do_test 2.3.4 { S delete } {}

do_test 2.4.1 {
  sqlite3session S db main
  S attach t1
  execsql { INSERT INTO t1 VALUES(100, 'Bangkok') }
  execsql { DELETE FROM t1 WHERE x = 100 }
} {}


do_changeset_test 2.4.2 S {}
do_changeset_invert_test 2.4.3 S {}
do_test 2.4.4 { S delete } {}

finish_test