mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-30 19:03:16 +03:00
Add new sessions function sqlite3changeset_apply_v3() and its streaming equivalent. This allows changesets to be filtered on a per-change basis, not just per-table.
FossilOrigin-Name: 10ebd7a119ef1985755ef143a941fbaed1b5ca1c8a71e011c8bbc70e383fd337
This commit is contained in:
88
ext/session/sessionI.test
Normal file
88
ext/session/sessionI.test
Normal file
@ -0,0 +1,88 @@
|
||||
# 2015 July 14
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
#
|
||||
|
||||
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
|
||||
ifcapable !session {finish_test; return}
|
||||
|
||||
set testprefix sessionI
|
||||
|
||||
forcedelete test.db2
|
||||
sqlite3 db2 test.db2
|
||||
|
||||
do_test 1.0 {
|
||||
do_common_sql {
|
||||
CREATE TABLE t1(x INTEGER PRIMARY KEY, y);
|
||||
}
|
||||
} {}
|
||||
|
||||
set C [changeset_from_sql {
|
||||
INSERT INTO t1 VALUES(1, 'one');
|
||||
INSERT INTO t1 VALUES(2, 'two');
|
||||
INSERT INTO t1 VALUES(3, 'three');
|
||||
INSERT INTO t1 VALUES(4, 'four');
|
||||
INSERT INTO t1 VALUES(5, 'five');
|
||||
INSERT INTO t1 VALUES(6, 'six');
|
||||
}]
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
SELECT * FROM t1
|
||||
} {
|
||||
1 one 2 two 3 three 4 four 5 five 6 six
|
||||
}
|
||||
|
||||
proc xFilter {data} {
|
||||
foreach {op tname flag pk old new} $data {}
|
||||
if {$op=="INSERT"} {
|
||||
set ipk [lindex $new 1]
|
||||
return [expr $ipk % 2]
|
||||
}
|
||||
return 1
|
||||
}
|
||||
proc xConflict {args} {
|
||||
}
|
||||
|
||||
sqlite3changeset_apply_v3 db2 $C xConflict xFilter
|
||||
|
||||
do_execsql_test -db db2 1.2 {
|
||||
SELECT * FROM t1
|
||||
} {
|
||||
1 one 3 three 5 five
|
||||
}
|
||||
|
||||
do_execsql_test -db db2 1.3 {
|
||||
DELETE FROM t1
|
||||
}
|
||||
sqlite3changeset_apply_v3 db2 $C xConflict
|
||||
|
||||
do_execsql_test -db db2 1.4 {
|
||||
SELECT * FROM t1
|
||||
} {
|
||||
1 one 2 two 3 three 4 four 5 five 6 six
|
||||
}
|
||||
|
||||
proc xFilter2 {data} {
|
||||
return 0
|
||||
}
|
||||
do_execsql_test -db db2 1.5 {
|
||||
DELETE FROM t1
|
||||
}
|
||||
sqlite3changeset_apply_v3 db2 $C xConflict xFilter2
|
||||
do_execsql_test -db db2 1.6 {
|
||||
SELECT * FROM t1
|
||||
} { }
|
||||
|
||||
finish_test
|
@ -5182,6 +5182,10 @@ static int sessionChangesetApply(
|
||||
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||
const char *zTab /* Table name */
|
||||
),
|
||||
int(*xFilterIter)(
|
||||
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||
sqlite3_changeset_iter *p
|
||||
),
|
||||
int(*xConflict)(
|
||||
void *pCtx, /* Copy of fifth arg to _apply() */
|
||||
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
|
||||
@ -5322,6 +5326,9 @@ static int sessionChangesetApply(
|
||||
** next change. A log message has already been issued. */
|
||||
if( schemaMismatch ) continue;
|
||||
|
||||
/* If this is a call to apply_v3(), invoke xFilterIter here. */
|
||||
if( xFilterIter && 0==xFilterIter(pCtx, pIter) ) continue;
|
||||
|
||||
rc = sessionApplyOneWithRetry(db, pIter, &sApply, xConflict, pCtx);
|
||||
}
|
||||
|
||||
@ -5389,6 +5396,87 @@ static int sessionChangesetApply(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called by all six sqlite3changeset_apply() variants:
|
||||
**
|
||||
** + sqlite3changeset_apply()
|
||||
** + sqlite3changeset_apply_v2()
|
||||
** + sqlite3changeset_apply_v3()
|
||||
** + sqlite3changeset_apply_strm()
|
||||
** + sqlite3changeset_apply_strm_v2()
|
||||
** + sqlite3changeset_apply_strm_v3()
|
||||
**
|
||||
** Arguments passed to this function are as follows:
|
||||
**
|
||||
** db:
|
||||
** Database handle to apply changeset to main database of.
|
||||
**
|
||||
** nChangeset/pChangeset:
|
||||
** These are both passed zero for the streaming variants. For the normal
|
||||
** apply() functions, these are passed the size of and the buffer containing
|
||||
** the changeset, respectively.
|
||||
**
|
||||
** xInput/pIn:
|
||||
** These are both passed zero for the normal variants. For the streaming
|
||||
** apply() functions, these are passed the input callback and context
|
||||
** pointer, respectively.
|
||||
**
|
||||
** xFilter:
|
||||
** The filter function as passed to apply() or apply_v2() (to filter by
|
||||
** table name), if any. This is always NULL for apply_v3() calls.
|
||||
**
|
||||
** xFilterIter:
|
||||
** The filter function as passed to apply_v3(), if any.
|
||||
**
|
||||
** xConflict:
|
||||
** The conflict handler callback (must not be NULL).
|
||||
**
|
||||
** pCtx:
|
||||
** The context pointer passed to the xFilter and xConflict handler callbacks.
|
||||
**
|
||||
** ppRebase, pnRebase:
|
||||
** Zero for apply(). The rebase changeset output pointers, if any, for
|
||||
** apply_v2() and apply_v3().
|
||||
**
|
||||
** flags:
|
||||
** Zero for apply(). The flags parameter for apply_v2() and apply_v3().
|
||||
*/
|
||||
static int sessionChangesetApplyV23(
|
||||
sqlite3 *db, /* Apply change to "main" db of this handle */
|
||||
int nChangeset, /* Size of changeset in bytes */
|
||||
void *pChangeset, /* Changeset blob */
|
||||
int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
|
||||
void *pIn, /* First arg for xInput */
|
||||
int(*xFilter)(
|
||||
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||
const char *zTab /* Table name */
|
||||
),
|
||||
int(*xFilterIter)(
|
||||
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||
sqlite3_changeset_iter *p /* Handle describing current change */
|
||||
),
|
||||
int(*xConflict)(
|
||||
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
|
||||
sqlite3_changeset_iter *p /* Handle describing change and conflict */
|
||||
),
|
||||
void *pCtx, /* First argument passed to xConflict */
|
||||
void **ppRebase, int *pnRebase,
|
||||
int flags
|
||||
){
|
||||
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
|
||||
int bInverse = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
|
||||
int rc = sessionChangesetStart(
|
||||
&pIter, xInput, pIn, nChangeset, pChangeset, bInverse, 1
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sessionChangesetApply(db, pIter,
|
||||
xFilter, xFilterIter, xConflict, pCtx, ppRebase, pnRebase, flags
|
||||
);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Apply the changeset passed via pChangeset/nChangeset to the main
|
||||
** database attached to handle "db".
|
||||
@ -5410,17 +5498,39 @@ int sqlite3changeset_apply_v2(
|
||||
void **ppRebase, int *pnRebase,
|
||||
int flags
|
||||
){
|
||||
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
|
||||
int bInv = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
|
||||
int rc = sessionChangesetStart(&pIter, 0, 0, nChangeset, pChangeset, bInv, 1);
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sessionChangesetApply(
|
||||
db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags
|
||||
return sessionChangesetApplyV23(db,
|
||||
nChangeset, pChangeset, 0, 0,
|
||||
xFilter, 0, xConflict, pCtx,
|
||||
ppRebase, pnRebase, flags
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
/*
|
||||
** Apply the changeset passed via pChangeset/nChangeset to the main
|
||||
** database attached to handle "db".
|
||||
*/
|
||||
int sqlite3changeset_apply_v3(
|
||||
sqlite3 *db, /* Apply change to "main" db of this handle */
|
||||
int nChangeset, /* Size of changeset in bytes */
|
||||
void *pChangeset, /* Changeset blob */
|
||||
int(*xFilter)(
|
||||
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||
sqlite3_changeset_iter *p /* Handle describing current change */
|
||||
),
|
||||
int(*xConflict)(
|
||||
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
|
||||
sqlite3_changeset_iter *p /* Handle describing change and conflict */
|
||||
),
|
||||
void *pCtx, /* First argument passed to xConflict */
|
||||
void **ppRebase, int *pnRebase,
|
||||
int flags
|
||||
){
|
||||
return sessionChangesetApplyV23(db,
|
||||
nChangeset, pChangeset, 0, 0,
|
||||
0, xFilter, xConflict, pCtx,
|
||||
ppRebase, pnRebase, flags
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -5443,8 +5553,10 @@ int sqlite3changeset_apply(
|
||||
),
|
||||
void *pCtx /* First argument passed to xConflict */
|
||||
){
|
||||
return sqlite3changeset_apply_v2(
|
||||
db, nChangeset, pChangeset, xFilter, xConflict, pCtx, 0, 0, 0
|
||||
return sessionChangesetApplyV23(db,
|
||||
nChangeset, pChangeset, 0, 0,
|
||||
xFilter, 0, xConflict, pCtx,
|
||||
0, 0, 0
|
||||
);
|
||||
}
|
||||
|
||||
@ -5453,6 +5565,29 @@ int sqlite3changeset_apply(
|
||||
** attached to handle "db". Invoke the supplied conflict handler callback
|
||||
** to resolve any conflicts encountered while applying the change.
|
||||
*/
|
||||
int sqlite3changeset_apply_v3_strm(
|
||||
sqlite3 *db, /* Apply change to "main" db of this handle */
|
||||
int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
|
||||
void *pIn, /* First arg for xInput */
|
||||
int(*xFilter)(
|
||||
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||
sqlite3_changeset_iter *p
|
||||
),
|
||||
int(*xConflict)(
|
||||
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
|
||||
sqlite3_changeset_iter *p /* Handle describing change and conflict */
|
||||
),
|
||||
void *pCtx, /* First argument passed to xConflict */
|
||||
void **ppRebase, int *pnRebase,
|
||||
int flags
|
||||
){
|
||||
return sessionChangesetApplyV23(db,
|
||||
0, 0, xInput, pIn,
|
||||
0, xFilter, xConflict, pCtx,
|
||||
ppRebase, pnRebase, flags
|
||||
);
|
||||
}
|
||||
int sqlite3changeset_apply_v2_strm(
|
||||
sqlite3 *db, /* Apply change to "main" db of this handle */
|
||||
int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
|
||||
@ -5470,15 +5605,11 @@ int sqlite3changeset_apply_v2_strm(
|
||||
void **ppRebase, int *pnRebase,
|
||||
int flags
|
||||
){
|
||||
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
|
||||
int bInverse = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
|
||||
int rc = sessionChangesetStart(&pIter, xInput, pIn, 0, 0, bInverse, 1);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sessionChangesetApply(
|
||||
db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags
|
||||
return sessionChangesetApplyV23(db,
|
||||
0, 0, xInput, pIn,
|
||||
xFilter, 0, xConflict, pCtx,
|
||||
ppRebase, pnRebase, flags
|
||||
);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
int sqlite3changeset_apply_strm(
|
||||
sqlite3 *db, /* Apply change to "main" db of this handle */
|
||||
@ -5495,8 +5626,10 @@ int sqlite3changeset_apply_strm(
|
||||
),
|
||||
void *pCtx /* First argument passed to xConflict */
|
||||
){
|
||||
return sqlite3changeset_apply_v2_strm(
|
||||
db, xInput, pIn, xFilter, xConflict, pCtx, 0, 0, 0
|
||||
return sessionChangesetApplyV23(db,
|
||||
0, 0, xInput, pIn,
|
||||
xFilter, 0, xConflict, pCtx,
|
||||
0, 0, 0
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1115,13 +1115,22 @@ void sqlite3changegroup_delete(sqlite3_changegroup*);
|
||||
** the changeset passed via the second and third arguments.
|
||||
**
|
||||
** The fourth argument (xFilter) passed to these functions is the "filter
|
||||
** callback". If it is not NULL, then for each table affected by at least one
|
||||
** change in the changeset, the filter callback is invoked with
|
||||
** the table name as the second argument, and a copy of the context pointer
|
||||
** passed as the sixth argument as the first. If the "filter callback"
|
||||
** returns zero, then no attempt is made to apply any changes to the table.
|
||||
** Otherwise, if the return value is non-zero or the xFilter argument to
|
||||
** is NULL, all changes related to the table are attempted.
|
||||
** callback". This may be passed NULL, in which case all changes in the
|
||||
** changeset are applied to the database. For sqlite3changeset_apply() and
|
||||
** sqlite3_changeset_apply_v2(), if it is not NULL, then it is invoked once
|
||||
** for each table affected by at least one change in the changeset. In this
|
||||
** case the table name is passed as the second argument, and a copy of
|
||||
** the context pointer passed as the sixth argument to apply() or apply_v2()
|
||||
** as the first. If the "filter callback" returns zero, then no attempt is
|
||||
** made to apply any changes to the table. Otherwise, if the return value is
|
||||
** non-zero, all changes related to the table are attempted.
|
||||
**
|
||||
** For sqlite3_changeset_apply_v3(), the xFilter callback is invoked once
|
||||
** per change. The second argument in this case is an sqlite3_changeset_iter
|
||||
** that may be queried using the usual APIs for the details of the current
|
||||
** change. If the "filter callback" returns zero in this case, then no attempt
|
||||
** is made to apply the current change. If it returns non-zero, the change
|
||||
** is applied.
|
||||
**
|
||||
** For each table that is not excluded by the filter callback, this function
|
||||
** tests that the target database contains a compatible table. A table is
|
||||
@ -1142,11 +1151,11 @@ void sqlite3changegroup_delete(sqlite3_changegroup*);
|
||||
** one such warning is issued for each table in the changeset.
|
||||
**
|
||||
** For each change for which there is a compatible table, an attempt is made
|
||||
** to modify the table contents according to the UPDATE, INSERT or DELETE
|
||||
** change. If a change cannot be applied cleanly, the conflict handler
|
||||
** function passed as the fifth argument to sqlite3changeset_apply() may be
|
||||
** invoked. A description of exactly when the conflict handler is invoked for
|
||||
** each type of change is below.
|
||||
** to modify the table contents according to each UPDATE, INSERT or DELETE
|
||||
** change that is not excluded by a filter callback. If a change cannot be
|
||||
** applied cleanly, the conflict handler function passed as the fifth argument
|
||||
** to sqlite3changeset_apply() may be invoked. A description of exactly when
|
||||
** the conflict handler is invoked for each type of change is below.
|
||||
**
|
||||
** Unlike the xFilter argument, xConflict may not be passed NULL. The results
|
||||
** of passing anything other than a valid function pointer as the xConflict
|
||||
@ -1297,6 +1306,23 @@ int sqlite3changeset_apply_v2(
|
||||
void **ppRebase, int *pnRebase, /* OUT: Rebase data */
|
||||
int flags /* SESSION_CHANGESETAPPLY_* flags */
|
||||
);
|
||||
int sqlite3changeset_apply_v3(
|
||||
sqlite3 *db, /* Apply change to "main" db of this handle */
|
||||
int nChangeset, /* Size of changeset in bytes */
|
||||
void *pChangeset, /* Changeset blob */
|
||||
int(*xFilter)(
|
||||
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||
sqlite3_changeset_iter *p /* Handle describing change */
|
||||
),
|
||||
int(*xConflict)(
|
||||
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
|
||||
sqlite3_changeset_iter *p /* Handle describing change and conflict */
|
||||
),
|
||||
void *pCtx, /* First argument passed to xConflict */
|
||||
void **ppRebase, int *pnRebase, /* OUT: Rebase data */
|
||||
int flags /* SESSION_CHANGESETAPPLY_* flags */
|
||||
);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Flags for sqlite3changeset_apply_v2
|
||||
@ -1716,6 +1742,23 @@ int sqlite3changeset_apply_v2_strm(
|
||||
void **ppRebase, int *pnRebase,
|
||||
int flags
|
||||
);
|
||||
int sqlite3changeset_apply_v3_strm(
|
||||
sqlite3 *db, /* Apply change to "main" db of this handle */
|
||||
int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
|
||||
void *pIn, /* First arg for xInput */
|
||||
int(*xFilter)(
|
||||
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||
sqlite3_changeset_iter *p
|
||||
),
|
||||
int(*xConflict)(
|
||||
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
|
||||
sqlite3_changeset_iter *p /* Handle describing change and conflict */
|
||||
),
|
||||
void *pCtx, /* First argument passed to xConflict */
|
||||
void **ppRebase, int *pnRebase,
|
||||
int flags
|
||||
);
|
||||
int sqlite3changeset_concat_strm(
|
||||
int (*xInputA)(void *pIn, void *pData, int *pnData),
|
||||
void *pInA,
|
||||
|
@ -520,6 +520,65 @@ static int test_obj_eq_string(Tcl_Obj *p, const char *z){
|
||||
return (nObj==n && (n==0 || 0==memcmp(zObj, z, n)));
|
||||
}
|
||||
|
||||
static Tcl_Obj *testIterData(sqlite3_changeset_iter *pIter){
|
||||
Tcl_Obj *pVar = 0;
|
||||
int nCol; /* Number of columns in table */
|
||||
int nCol2; /* Number of columns in table */
|
||||
int op; /* SQLITE_INSERT, UPDATE or DELETE */
|
||||
const char *zTab; /* Name of table change applies to */
|
||||
Tcl_Obj *pOld; /* Vector of old.* values */
|
||||
Tcl_Obj *pNew; /* Vector of new.* values */
|
||||
int bIndirect;
|
||||
|
||||
char *zPK;
|
||||
unsigned char *abPK;
|
||||
int i;
|
||||
|
||||
sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
|
||||
pVar = Tcl_NewObj();
|
||||
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(
|
||||
op==SQLITE_INSERT ? "INSERT" :
|
||||
op==SQLITE_UPDATE ? "UPDATE" :
|
||||
"DELETE", -1
|
||||
));
|
||||
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zTab, -1));
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewBooleanObj(bIndirect));
|
||||
|
||||
zPK = ckalloc(nCol+1);
|
||||
memset(zPK, 0, nCol+1);
|
||||
sqlite3changeset_pk(pIter, &abPK, &nCol2);
|
||||
assert( nCol==nCol2 );
|
||||
for(i=0; i<nCol; i++){
|
||||
zPK[i] = (abPK[i] ? 'X' : '.');
|
||||
}
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zPK, -1));
|
||||
ckfree(zPK);
|
||||
|
||||
pOld = Tcl_NewObj();
|
||||
if( op!=SQLITE_INSERT ){
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3_value *pVal;
|
||||
sqlite3changeset_old(pIter, i, &pVal);
|
||||
test_append_value(pOld, pVal);
|
||||
}
|
||||
}
|
||||
pNew = Tcl_NewObj();
|
||||
if( op!=SQLITE_DELETE ){
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3_value *pVal;
|
||||
sqlite3changeset_new(pIter, i, &pVal);
|
||||
test_append_value(pNew, pVal);
|
||||
}
|
||||
}
|
||||
Tcl_ListObjAppendElement(0, pVar, pOld);
|
||||
Tcl_ListObjAppendElement(0, pVar, pNew);
|
||||
|
||||
return pVar;
|
||||
}
|
||||
|
||||
|
||||
static int test_filter_handler(
|
||||
void *pCtx, /* Pointer to TestConflictHandler structure */
|
||||
const char *zTab /* Table name */
|
||||
@ -543,6 +602,29 @@ static int test_filter_handler(
|
||||
return res;
|
||||
}
|
||||
|
||||
static int test_filter_v3_handler(
|
||||
void *pCtx, /* Pointer to TestConflictHandler structure */
|
||||
sqlite3_changeset_iter *pIter
|
||||
){
|
||||
TestConflictHandler *p = (TestConflictHandler *)pCtx;
|
||||
int res = 1;
|
||||
Tcl_Obj *pEval = 0;
|
||||
Tcl_Interp *interp = p->interp;
|
||||
|
||||
pEval = Tcl_DuplicateObj(p->pFilterScript);
|
||||
Tcl_IncrRefCount(pEval);
|
||||
Tcl_ListObjAppendElement(0, pEval, testIterData(pIter));
|
||||
|
||||
if( TCL_OK!=Tcl_EvalObjEx(interp, pEval, TCL_EVAL_GLOBAL)
|
||||
|| TCL_OK!=Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &res)
|
||||
){
|
||||
Tcl_BackgroundError(interp);
|
||||
}
|
||||
|
||||
Tcl_DecrRefCount(pEval);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int test_conflict_handler(
|
||||
void *pCtx, /* Pointer to TestConflictHandler structure */
|
||||
int eConf, /* DATA, MISSING, CONFLICT, CONSTRAINT */
|
||||
@ -776,7 +858,7 @@ static int testStreamInput(
|
||||
|
||||
|
||||
static int SQLITE_TCLAPI testSqlite3changesetApply(
|
||||
int bV2,
|
||||
int iVersion,
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@ -793,11 +875,13 @@ static int SQLITE_TCLAPI testSqlite3changesetApply(
|
||||
int nRebase = 0;
|
||||
int flags = 0; /* Flags for apply_v2() */
|
||||
|
||||
assert( iVersion==1 || iVersion==2 || iVersion==3 );
|
||||
|
||||
memset(&sStr, 0, sizeof(sStr));
|
||||
sStr.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
|
||||
|
||||
/* Check for the -nosavepoint, -invert or -ignorenoop switches */
|
||||
if( bV2 ){
|
||||
if( iVersion==2 || iVersion==3 ){
|
||||
while( objc>1 ){
|
||||
const char *z1 = Tcl_GetString(objv[1]);
|
||||
int n = (int)strlen(z1);
|
||||
@ -822,7 +906,7 @@ static int SQLITE_TCLAPI testSqlite3changesetApply(
|
||||
|
||||
if( objc!=4 && objc!=5 ){
|
||||
const char *zMsg;
|
||||
if( bV2 ){
|
||||
if( iVersion==2 || iVersion==3 ){
|
||||
zMsg = "?-nosavepoint? ?-inverse? ?-ignorenoop? "
|
||||
"DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?";
|
||||
}else{
|
||||
@ -842,30 +926,49 @@ static int SQLITE_TCLAPI testSqlite3changesetApply(
|
||||
ctx.interp = interp;
|
||||
|
||||
if( sStr.nStream==0 ){
|
||||
if( bV2==0 ){
|
||||
switch( iVersion ){
|
||||
case 1:
|
||||
rc = sqlite3changeset_apply(db, (int)nChangeset, pChangeset,
|
||||
(objc==5)?test_filter_handler:0, test_conflict_handler, (void *)&ctx
|
||||
(objc==5)?test_filter_handler:0, test_conflict_handler, (void*)&ctx
|
||||
);
|
||||
}else{
|
||||
break;
|
||||
case 2:
|
||||
rc = sqlite3changeset_apply_v2(db, (int)nChangeset, pChangeset,
|
||||
(objc==5)?test_filter_handler:0, test_conflict_handler, (void *)&ctx,
|
||||
(objc==5)?test_filter_handler:0, test_conflict_handler, (void*)&ctx,
|
||||
&pRebase, &nRebase, flags
|
||||
);
|
||||
break;
|
||||
case 3:
|
||||
rc = sqlite3changeset_apply_v3(db, (int)nChangeset, pChangeset,
|
||||
(objc==5)?test_filter_v3_handler:0, test_conflict_handler,
|
||||
(void*)&ctx, &pRebase, &nRebase, flags
|
||||
);
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
sStr.aData = (unsigned char*)pChangeset;
|
||||
sStr.nData = (int)nChangeset;
|
||||
if( bV2==0 ){
|
||||
switch( iVersion ){
|
||||
case 1:
|
||||
rc = sqlite3changeset_apply_strm(db, testStreamInput, (void*)&sStr,
|
||||
(objc==5) ? test_filter_handler : 0,
|
||||
test_conflict_handler, (void *)&ctx
|
||||
);
|
||||
}else{
|
||||
break;
|
||||
case 2:
|
||||
rc = sqlite3changeset_apply_v2_strm(db, testStreamInput, (void*)&sStr,
|
||||
(objc==5) ? test_filter_handler : 0,
|
||||
test_conflict_handler, (void *)&ctx,
|
||||
&pRebase, &nRebase, flags
|
||||
);
|
||||
break;
|
||||
case 3:
|
||||
rc = sqlite3changeset_apply_v3_strm(db, testStreamInput, (void*)&sStr,
|
||||
(objc==5) ? test_filter_v3_handler : 0,
|
||||
test_conflict_handler, (void *)&ctx,
|
||||
&pRebase, &nRebase, flags
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -873,7 +976,7 @@ static int SQLITE_TCLAPI testSqlite3changesetApply(
|
||||
return test_session_error(interp, rc, 0);
|
||||
}else{
|
||||
Tcl_ResetResult(interp);
|
||||
if( bV2 && pRebase ){
|
||||
if( (iVersion==2 || iVersion==3) && pRebase ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pRebase, nRebase));
|
||||
}
|
||||
}
|
||||
@ -890,7 +993,7 @@ static int SQLITE_TCLAPI test_sqlite3changeset_apply(
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
return testSqlite3changesetApply(0, clientData, interp, objc, objv);
|
||||
return testSqlite3changesetApply(1, clientData, interp, objc, objv);
|
||||
}
|
||||
/*
|
||||
** sqlite3changeset_apply_v2 DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?
|
||||
@ -901,7 +1004,18 @@ static int SQLITE_TCLAPI test_sqlite3changeset_apply_v2(
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
return testSqlite3changesetApply(1, clientData, interp, objc, objv);
|
||||
return testSqlite3changesetApply(2, clientData, interp, objc, objv);
|
||||
}
|
||||
/*
|
||||
** sqlite3changeset_apply_v3 DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_sqlite3changeset_apply_v3(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
return testSqlite3changesetApply(3, clientData, interp, objc, objv);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1034,64 +1148,6 @@ static int SQLITE_TCLAPI test_sqlite3changeset_concat(
|
||||
return rc;
|
||||
}
|
||||
|
||||
static Tcl_Obj *testIterData(sqlite3_changeset_iter *pIter){
|
||||
Tcl_Obj *pVar = 0;
|
||||
int nCol; /* Number of columns in table */
|
||||
int nCol2; /* Number of columns in table */
|
||||
int op; /* SQLITE_INSERT, UPDATE or DELETE */
|
||||
const char *zTab; /* Name of table change applies to */
|
||||
Tcl_Obj *pOld; /* Vector of old.* values */
|
||||
Tcl_Obj *pNew; /* Vector of new.* values */
|
||||
int bIndirect;
|
||||
|
||||
char *zPK;
|
||||
unsigned char *abPK;
|
||||
int i;
|
||||
|
||||
sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
|
||||
pVar = Tcl_NewObj();
|
||||
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(
|
||||
op==SQLITE_INSERT ? "INSERT" :
|
||||
op==SQLITE_UPDATE ? "UPDATE" :
|
||||
"DELETE", -1
|
||||
));
|
||||
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zTab, -1));
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewBooleanObj(bIndirect));
|
||||
|
||||
zPK = ckalloc(nCol+1);
|
||||
memset(zPK, 0, nCol+1);
|
||||
sqlite3changeset_pk(pIter, &abPK, &nCol2);
|
||||
assert( nCol==nCol2 );
|
||||
for(i=0; i<nCol; i++){
|
||||
zPK[i] = (abPK[i] ? 'X' : '.');
|
||||
}
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zPK, -1));
|
||||
ckfree(zPK);
|
||||
|
||||
pOld = Tcl_NewObj();
|
||||
if( op!=SQLITE_INSERT ){
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3_value *pVal;
|
||||
sqlite3changeset_old(pIter, i, &pVal);
|
||||
test_append_value(pOld, pVal);
|
||||
}
|
||||
}
|
||||
pNew = Tcl_NewObj();
|
||||
if( op!=SQLITE_DELETE ){
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3_value *pVal;
|
||||
sqlite3changeset_new(pIter, i, &pVal);
|
||||
test_append_value(pNew, pVal);
|
||||
}
|
||||
}
|
||||
Tcl_ListObjAppendElement(0, pVar, pOld);
|
||||
Tcl_ListObjAppendElement(0, pVar, pNew);
|
||||
|
||||
return pVar;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3session_foreach VARNAME CHANGESET SCRIPT
|
||||
*/
|
||||
@ -1751,6 +1807,7 @@ int TestSession_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3changeset_concat", test_sqlite3changeset_concat },
|
||||
{ "sqlite3changeset_apply", test_sqlite3changeset_apply },
|
||||
{ "sqlite3changeset_apply_v2", test_sqlite3changeset_apply_v2 },
|
||||
{ "sqlite3changeset_apply_v3", test_sqlite3changeset_apply_v3 },
|
||||
{ "sqlite3changeset_apply_replace_all",
|
||||
test_sqlite3changeset_apply_replace_all },
|
||||
{ "sql_exec_changeset", test_sql_exec_changeset },
|
||||
|
19
manifest
19
manifest
@ -1,5 +1,5 @@
|
||||
C Fix\sharmless\scompiler\swarnings\sin\sbuilds\sthat\suse\sSQLITE_OMIT_DATETIME_FUNCS.
|
||||
D 2025-07-14T09:41:59.400
|
||||
C Add\snew\ssessions\sfunction\ssqlite3changeset_apply_v3()\sand\sits\sstreaming\sequivalent.\sThis\sallows\schangesets\sto\sbe\sfiltered\son\sa\sper-change\sbasis,\snot\sjust\sper-table.
|
||||
D 2025-07-14T14:51:43.802
|
||||
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
@ -594,6 +594,7 @@ F ext/session/sessionE.test b2010949c9d7415306f64e3c2072ddabc4b8250c98478d3c0c4d
|
||||
F ext/session/sessionF.test d37ed800881e742c208df443537bf29aa49fd56eac520d0f0c6df3e6320f3401
|
||||
F ext/session/sessionG.test 3efe388282d641b65485b5462e67851002cd91a282dc95b685d085eb8efdad0a
|
||||
F ext/session/sessionH.test 71bbff6b1abb2c4ac62b84dee53273c37e0b21e5fde3aed80929403e091ef859
|
||||
F ext/session/sessionI.test 11e7b6729fc942982a5104a40132f70a2e964d64d60dc5809b8206465af74822
|
||||
F ext/session/session_common.tcl a31f537a929a695a852d241c9434f2847cadf329856401921139fbb03a5a7697
|
||||
F ext/session/session_gen.test 942a0002df10da53c45b40b581cc3ed25e7ff42bda1e7ba497273dc2887aa8e6
|
||||
F ext/session/session_speed_test.c dcf0ef58d76b70c8fbd9eab3be77cf9deb8bc1638fed8be518b62d6cbdef88b3
|
||||
@ -617,9 +618,9 @@ F ext/session/sessionrowid.test 85187c2f1b38861a5844868126f69f9ec62223a03449a98a
|
||||
F ext/session/sessionsize.test 8fcf4685993c3dbaa46a24183940ab9f5aa9ed0d23e5fb63bfffbdb56134b795
|
||||
F ext/session/sessionstat1.test 5e718d5888c0c49bbb33a7a4f816366db85f59f6a4f97544a806421b85dc2dec
|
||||
F ext/session/sessionwor.test 6fd9a2256442cebde5b2284936ae9e0d54bde692d0f5fd009ecef8511f4cf3fc
|
||||
F ext/session/sqlite3session.c 6b0877fe1ab832aa4b85eaca72606dfd1630a1363a1be7af10ee1042a5ec719e
|
||||
F ext/session/sqlite3session.h 9bb1a6687b467764b35178dc29bbd2c57ab8cd3acdc8a62f088c34ad17e4fe2b
|
||||
F ext/session/test_session.c 2ddff73ea368d827028c32851b291416e1008845832feb27b751d15e57e13cc3
|
||||
F ext/session/sqlite3session.c 19e14bcca2fbc63a8022ffd708ea6e6986c4003a1e9bbca9b2989fd230362e15
|
||||
F ext/session/sqlite3session.h b81e8536ce4b83babafd700f4ff67017804b6c1d71df963b30d3972958e7f4a7
|
||||
F ext/session/test_session.c 8766b5973a6323934cb51248f621c3dc87ad2a98f023c3cc280d79e7d78d36fb
|
||||
F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c
|
||||
F ext/wasm/GNUmakefile d62af1b0914eb2e03fa6e4e75e93acadc8f4faeb2d56335da25d61b9ea144c53
|
||||
F ext/wasm/README-dist.txt f01081a850ce38a56706af6b481e3a7878e24e42b314cfcd4b129f0f8427066a
|
||||
@ -2212,8 +2213,8 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350
|
||||
F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
|
||||
F tool/warnings.sh 1ad0169b022b280bcaaf94a7fa231591be96b514230ab5c98fbf15cd7df842dd
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
P e5d079549594ca44852773b8919894866394e47ad725dadc7f65242413a219d3
|
||||
R 07c15e107127475edb35cdd6de618874
|
||||
U drh
|
||||
Z cde724828852556a37035896eff32ef8
|
||||
P e11fbf9fd630a7de2e0b0e4b67dded05b905b2a0ba04aa7e915ca9df2d9ebe21
|
||||
R 082d403ad4bd97cbce0234772b2da39f
|
||||
U dan
|
||||
Z 455b5bdcd48088e3f2c57d9eeedfc2e8
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
@ -1 +1 @@
|
||||
e11fbf9fd630a7de2e0b0e4b67dded05b905b2a0ba04aa7e915ca9df2d9ebe21
|
||||
10ebd7a119ef1985755ef143a941fbaed1b5ca1c8a71e011c8bbc70e383fd337
|
||||
|
Reference in New Issue
Block a user