/ Check-in [cbedcb9a]
Login

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

Overview
Comment:Add the sqlite3changeset_start_v2() - a new version of _start() that accepts a flags parameter - and a streaming equivalent to the sessions module. Also add the SQLITE_CHANGESETSTART_INVERT flag, used with start_v2() to invert a changeset while iterating through it.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: cbedcb9aaefdfe00453efbdf0eac6c15e1f53bbe8fff2e7d534a5adf23be04f5
User & Date: dan 2018-10-20 13:48:09
Context
2018-10-23
13:48
Fix a problem with using window functions in compound (UNION, INTERSECT etc.) queries. check-in: 059ff53a user: dan tags: trunk
2018-10-20
13:48
Add the sqlite3changeset_start_v2() - a new version of _start() that accepts a flags parameter - and a streaming equivalent to the sessions module. Also add the SQLITE_CHANGESETSTART_INVERT flag, used with start_v2() to invert a changeset while iterating through it. check-in: cbedcb9a user: dan tags: trunk
2018-10-18
15:17
Take steps to avoid a potential integer overflow in sessionBufferGrow(). check-in: f7affa2e user: dan tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/session/sessioninvert.test.

    16     16   } 
    17     17   source [file join [file dirname [info script]] session_common.tcl]
    18     18   source $testdir/tester.tcl
    19     19   ifcapable !session {finish_test; return}
    20     20   
    21     21   set testprefix sessioninvert
    22     22   
    23         -proc do_invert_test {tn sql} {
           23  +proc iter_invert {C} {
           24  +  set x [list]
           25  +  sqlite3session_foreach -invert c $C { lappend x $c }
           26  +  set x
           27  +}
           28  +
           29  +proc do_invert_test {tn sql {iter {}}} {
    24     30   
    25     31     forcecopy test.db test.db2
    26     32     sqlite3 db2 test.db2
    27     33   
    28     34     set C [changeset_from_sql $sql]
    29     35   
    30     36     forcecopy test.db test.db3
................................................................................
    33     39   
    34     40     set I [sqlite3changeset_invert $C]
    35     41     sqlite3changeset_apply db $I {}
    36     42     uplevel [list do_test $tn.2 [list compare_db db db2] {}]
    37     43     
    38     44     sqlite3changeset_apply_v2 -invert db3 $C {}
    39     45     uplevel [list do_test $tn.3 [list compare_db db db3] {}]
           46  +
           47  +  if {$iter!=""} {
           48  +    uplevel [list do_test $tn.4 [list iter_invert $C] [list {*}$iter]]
           49  +  }
    40     50   
    41     51     catch { db2 close }
    42     52     catch { db3 close }
    43     53   }
    44     54   
    45     55   do_execsql_test 1.0 {
    46     56     CREATE TABLE t1(a PRIMARY KEY, b, c);
................................................................................
    54     64     INSERT INTO t1 VALUES(6, 'six', 'vi');
    55     65   
    56     66     INSERT INTO t2 SELECT * FROM t1;
    57     67   }
    58     68   
    59     69   do_invert_test 1.1 {
    60     70     INSERT INTO t1 VALUES(7, 'seven', 'vii');
           71  +} {
           72  +  {DELETE t1 0 X.. {i 7 t seven t vii} {}}
    61     73   }
    62     74   
    63     75   do_invert_test 1.2 {
    64     76     DELETE FROM t1 WHERE a<4;
    65         -}
    66         -
    67         -do_invert_test 1.2 {
    68         -  UPDATE t1 SET c=5;
           77  +} {
           78  +  {INSERT t1 0 X.. {} {i 1 t one t i}}
           79  +  {INSERT t1 0 X.. {} {i 2 t two t ii}}
           80  +  {INSERT t1 0 X.. {} {i 3 t three t iii}}
    69     81   }
    70     82   
    71     83   do_invert_test 1.3 {
           84  +  UPDATE t1 SET c=5;
           85  +} {
           86  +  {UPDATE t1 0 X.. {i 1 {} {} i 5} {{} {} {} {} t i}}
           87  +  {UPDATE t1 0 X.. {i 2 {} {} i 5} {{} {} {} {} t ii}}
           88  +  {UPDATE t1 0 X.. {i 3 {} {} i 5} {{} {} {} {} t iii}}
           89  +  {UPDATE t1 0 X.. {i 4 {} {} i 5} {{} {} {} {} t iv}}
           90  +  {UPDATE t1 0 X.. {i 5 {} {} i 5} {{} {} {} {} t v}}
           91  +  {UPDATE t1 0 X.. {i 6 {} {} i 5} {{} {} {} {} t vi}}
           92  +}
           93  +
           94  +do_invert_test 1.4 {
    72     95     UPDATE t1 SET b = a+1 WHERE a%2;
    73     96     DELETE FROM t2;
    74     97     INSERT INTO t1 VALUES(10, 'ten', NULL);
    75     98   }
    76     99   
    77         -do_invert_test 1.4 {
          100  +do_invert_test 1.5 {
    78    101     UPDATE t2 SET d = d-1;
          102  +} {
          103  +  {UPDATE t2 0 .XX {i 2 t three t iii} {i 3 {} {} {} {}}}
          104  +  {UPDATE t2 0 .XX {i 1 t two t ii} {i 2 {} {} {} {}}}
          105  +  {UPDATE t2 0 .XX {i 5 t six t vi} {i 6 {} {} {} {}}}
          106  +  {UPDATE t2 0 .XX {i 3 t four t iv} {i 4 {} {} {} {}}}
          107  +  {UPDATE t2 0 .XX {i 0 t one t i} {i 1 {} {} {} {}}}
          108  +  {UPDATE t2 0 .XX {i 4 t five t v} {i 5 {} {} {} {}}}
    79    109   }
    80    110   
    81    111   do_execsql_test 2.0 { 
    82    112     ANALYZE;
    83    113     PRAGMA writable_schema = 1;
    84    114     DROP TABLE IF EXISTS sqlite_stat4;
    85    115     SELECT * FROM sqlite_stat1;
................................................................................
    86    116   } {
    87    117     t2 sqlite_autoindex_t2_1 {6 1 1} 
    88    118     t1 sqlite_autoindex_t1_1 {6 1}
    89    119   }
    90    120   
    91    121   do_invert_test 2.1 {
    92    122     INSERT INTO sqlite_stat1 VALUES('t3', 'idx2', '1 2 3');
          123  +} {
          124  +  {DELETE sqlite_stat1 0 XX. {t t3 t idx2 t {1 2 3}} {}}
    93    125   }
    94    126   
    95    127   do_invert_test 2.2 {
    96    128     DELETE FROM sqlite_stat1;
          129  +} {
          130  +  {INSERT sqlite_stat1 0 XX. {} {t t1 t sqlite_autoindex_t1_1 t {6 1}}}
          131  +  {INSERT sqlite_stat1 0 XX. {} {t t2 t sqlite_autoindex_t2_1 t {6 1 1}}}
    97    132   }
    98    133   
    99    134   do_invert_test 2.3 {
   100    135     UPDATE sqlite_stat1 SET stat = 'hello world';
   101    136   }
   102    137   
   103    138   do_test 3.0 {
................................................................................
   108    143       DELETE FROM t2 WHERE d = 3;
   109    144     }]
   110    145   
   111    146     list [catch { sqlite3changeset_apply_v2 -invert db2 $P {} } msg] $msg
   112    147   } {1 SQLITE_CORRUPT}
   113    148   
   114    149   do_test 3.1 {
          150  +  list [catch { sqlite3session_foreach -invert db2 $P {} } msg] $msg
          151  +} {1 SQLITE_CORRUPT}
          152  +
          153  +do_test 3.2 {
   115    154     sqlite3changeset_apply_v2 db2 $P {} 
   116    155     compare_db db db2
   117    156   } {}
   118    157   
   119    158   
   120    159   finish_test

Changes to ext/session/sqlite3session.c.

  2575   2575   int sqlite3changeset_start(
  2576   2576     sqlite3_changeset_iter **pp,    /* OUT: Changeset iterator handle */
  2577   2577     int nChangeset,                 /* Size of buffer pChangeset in bytes */
  2578   2578     void *pChangeset                /* Pointer to buffer containing changeset */
  2579   2579   ){
  2580   2580     return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, 0);
  2581   2581   }
         2582  +int sqlite3changeset_start_v2(
         2583  +  sqlite3_changeset_iter **pp,    /* OUT: Changeset iterator handle */
         2584  +  int nChangeset,                 /* Size of buffer pChangeset in bytes */
         2585  +  void *pChangeset,               /* Pointer to buffer containing changeset */
         2586  +  int flags
         2587  +){
         2588  +  int bInvert = !!(flags & SQLITE_CHANGESETSTART_INVERT);
         2589  +  return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, bInvert);
         2590  +}
  2582   2591   
  2583   2592   /*
  2584   2593   ** Streaming version of sqlite3changeset_start().
  2585   2594   */
  2586   2595   int sqlite3changeset_start_strm(
  2587   2596     sqlite3_changeset_iter **pp,    /* OUT: Changeset iterator handle */
  2588   2597     int (*xInput)(void *pIn, void *pData, int *pnData),
  2589   2598     void *pIn
  2590   2599   ){
  2591   2600     return sessionChangesetStart(pp, xInput, pIn, 0, 0, 0);
  2592   2601   }
         2602  +int sqlite3changeset_start_v2_strm(
         2603  +  sqlite3_changeset_iter **pp,    /* OUT: Changeset iterator handle */
         2604  +  int (*xInput)(void *pIn, void *pData, int *pnData),
         2605  +  void *pIn,
         2606  +  int flags
         2607  +){
         2608  +  int bInvert = !!(flags & SQLITE_CHANGESETSTART_INVERT);
         2609  +  return sessionChangesetStart(pp, xInput, pIn, 0, 0, bInvert);
         2610  +}
  2593   2611   
  2594   2612   /*
  2595   2613   ** If the SessionInput object passed as the only argument is a streaming
  2596   2614   ** object and the buffer is full, discard some data to free up space.
  2597   2615   */
  2598   2616   static void sessionDiscardData(SessionInput *pIn){
  2599   2617     if( pIn->xInput && pIn->iNext>=SESSIONS_STRM_CHUNK_SIZE ){

Changes to ext/session/sqlite3session.h.

   469    469   ** [sqlite3changeset_invert()] functions, all changes within the changeset 
   470    470   ** that apply to a single table are grouped together. This means that when 
   471    471   ** an application iterates through a changeset using an iterator created by 
   472    472   ** this function, all changes that relate to a single table are visited 
   473    473   ** consecutively. There is no chance that the iterator will visit a change 
   474    474   ** the applies to table X, then one for table Y, and then later on visit 
   475    475   ** another change for table X.
          476  +**
          477  +** The behavior of sqlite3changeset_start_v2() and its streaming equivalent
          478  +** may be modified by passing a combination of
          479  +** [SQLITE_CHANGESETSTART_INVERT | supported flags] as the 4th parameter.
          480  +**
          481  +** Note that the sqlite3changeset_start_v2() API is still <b>experimental</b>
          482  +** and therefore subject to change.
   476    483   */
   477    484   int sqlite3changeset_start(
   478    485     sqlite3_changeset_iter **pp,    /* OUT: New changeset iterator handle */
   479    486     int nChangeset,                 /* Size of changeset blob in bytes */
   480    487     void *pChangeset                /* Pointer to blob containing changeset */
   481    488   );
          489  +int sqlite3changeset_start_v2(
          490  +  sqlite3_changeset_iter **pp,    /* OUT: New changeset iterator handle */
          491  +  int nChangeset,                 /* Size of changeset blob in bytes */
          492  +  void *pChangeset,               /* Pointer to blob containing changeset */
          493  +  int flags                       /* SESSION_CHANGESETSTART_* flags */
          494  +);
          495  +
          496  +/*
          497  +** CAPI3REF: Flags for sqlite3changeset_start_v2
          498  +**
          499  +** The following flags may passed via the 4th parameter to
          500  +** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]:
          501  +**
          502  +** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd>
          503  +**   Invert the changeset while iterating through it. This is equivalent to
          504  +**   inverting a changeset using sqlite3changeset_invert() before applying it.
          505  +**   It is an error to specify this flag with a patchset.
          506  +*/
          507  +#define SQLITE_CHANGESETSTART_INVERT        0x0002
   482    508   
   483    509   
   484    510   /*
   485    511   ** CAPI3REF: Advance A Changeset Iterator
   486    512   ** METHOD: sqlite3_changeset_iter
   487    513   **
   488    514   ** This function may only be used with iterators created by function
................................................................................
  1129   1155     int(*xConflict)(
  1130   1156       void *pCtx,                   /* Copy of sixth arg to _apply() */
  1131   1157       int eConflict,                /* DATA, MISSING, CONFLICT, CONSTRAINT */
  1132   1158       sqlite3_changeset_iter *p     /* Handle describing change and conflict */
  1133   1159     ),
  1134   1160     void *pCtx,                     /* First argument passed to xConflict */
  1135   1161     void **ppRebase, int *pnRebase, /* OUT: Rebase data */
  1136         -  int flags                       /* Combination of SESSION_APPLY_* flags */
         1162  +  int flags                       /* SESSION_CHANGESETAPPLY_* flags */
  1137   1163   );
  1138   1164   
  1139   1165   /*
  1140   1166   ** CAPI3REF: Flags for sqlite3changeset_apply_v2
  1141   1167   **
  1142   1168   ** The following flags may passed via the 9th parameter to
  1143   1169   ** [sqlite3changeset_apply_v2] and [sqlite3changeset_apply_v2_strm]:
................................................................................
  1547   1573     int (*xOutput)(void *pOut, const void *pData, int nData),
  1548   1574     void *pOut
  1549   1575   );
  1550   1576   int sqlite3changeset_start_strm(
  1551   1577     sqlite3_changeset_iter **pp,
  1552   1578     int (*xInput)(void *pIn, void *pData, int *pnData),
  1553   1579     void *pIn
         1580  +);
         1581  +int sqlite3changeset_start_v2_strm(
         1582  +  sqlite3_changeset_iter **pp,
         1583  +  int (*xInput)(void *pIn, void *pData, int *pnData),
         1584  +  void *pIn,
         1585  +  int flags
  1554   1586   );
  1555   1587   int sqlite3session_changeset_strm(
  1556   1588     sqlite3_session *pSession,
  1557   1589     int (*xOutput)(void *pOut, const void *pData, int nData),
  1558   1590     void *pOut
  1559   1591   );
  1560   1592   int sqlite3session_patchset_strm(

Changes to ext/session/test_session.c.

   977    977     int nChangeset;
   978    978     sqlite3_changeset_iter *pIter;
   979    979     int rc;
   980    980     Tcl_Obj *pVarname;
   981    981     Tcl_Obj *pCS;
   982    982     Tcl_Obj *pScript;
   983    983     int isCheckNext = 0;
          984  +  int isInvert = 0;
   984    985   
   985    986     TestStreamInput sStr;
   986    987     memset(&sStr, 0, sizeof(sStr));
   987    988   
   988         -  if( objc>1 ){
          989  +  while( objc>1 ){
   989    990       char *zOpt = Tcl_GetString(objv[1]);
   990         -    isCheckNext = (strcmp(zOpt, "-next")==0);
          991  +    int nOpt = strlen(zOpt);
          992  +    if( zOpt[0]!='-' ) break;
          993  +    if( nOpt<=7 && 0==sqlite3_strnicmp(zOpt, "-invert", nOpt) ){
          994  +      isInvert = 1;
          995  +    }else
          996  +    if( nOpt<=5 && 0==sqlite3_strnicmp(zOpt, "-next", nOpt) ){
          997  +      isCheckNext = 1;
          998  +    }else{
          999  +      break;
         1000  +    }
         1001  +    objv++;
         1002  +    objc--;
   991   1003     }
   992         -  if( objc!=4+isCheckNext ){
   993         -    Tcl_WrongNumArgs(interp, 1, objv, "?-next? VARNAME CHANGESET SCRIPT");
         1004  +  if( objc!=4 ){
         1005  +    Tcl_WrongNumArgs(
         1006  +        interp, 1, objv, "?-next? ?-invert? VARNAME CHANGESET SCRIPT");
   994   1007       return TCL_ERROR;
   995   1008     }
   996   1009   
   997         -  pVarname = objv[1+isCheckNext];
   998         -  pCS = objv[2+isCheckNext];
   999         -  pScript = objv[3+isCheckNext];
         1010  +  pVarname = objv[1];
         1011  +  pCS = objv[2];
         1012  +  pScript = objv[3];
  1000   1013   
  1001   1014     pChangeset = (void *)Tcl_GetByteArrayFromObj(pCS, &nChangeset);
  1002   1015     sStr.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
  1003         -  if( sStr.nStream==0 ){
  1004         -    rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset);
         1016  +  if( isInvert ){
         1017  +    int f = SQLITE_CHANGESETSTART_INVERT;
         1018  +    if( sStr.nStream==0 ){
         1019  +      rc = sqlite3changeset_start_v2(&pIter, nChangeset, pChangeset, f);
         1020  +    }else{
         1021  +      void *pCtx = (void*)&sStr;
         1022  +      sStr.aData = (unsigned char*)pChangeset;
         1023  +      sStr.nData = nChangeset;
         1024  +      rc = sqlite3changeset_start_v2_strm(&pIter, testStreamInput, pCtx, f);
         1025  +    }
  1005   1026     }else{
  1006         -    sStr.aData = (unsigned char*)pChangeset;
  1007         -    sStr.nData = nChangeset;
  1008         -    rc = sqlite3changeset_start_strm(&pIter, testStreamInput, (void*)&sStr);
         1027  +    if( sStr.nStream==0 ){
         1028  +      rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset);
         1029  +    }else{
         1030  +      sStr.aData = (unsigned char*)pChangeset;
         1031  +      sStr.nData = nChangeset;
         1032  +      rc = sqlite3changeset_start_strm(&pIter, testStreamInput, (void*)&sStr);
         1033  +    }
  1009   1034     }
  1010   1035     if( rc!=SQLITE_OK ){
  1011   1036       return test_session_error(interp, rc, 0);
  1012   1037     }
  1013   1038   
  1014   1039     while( SQLITE_ROW==sqlite3changeset_next(pIter) ){
  1015   1040       int nCol;                     /* Number of columns in table */