Add sqlite3_changeset_apply_v2() and apply_v2_strm() to the sessions module.
FossilOrigin-Name: 445bfe977d9f3a891e08ef33237862ed047fe83e134ef3ed8b47ee0f5abd8cd6
This commit is contained in:
parent
58db4c760f
commit
a38e6c57bc
125
ext/session/sessionrebase.test
Normal file
125
ext/session/sessionrebase.test
Normal file
@ -0,0 +1,125 @@
|
||||
# 2018 March 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library.
|
||||
#
|
||||
|
||||
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 sessionrebase
|
||||
|
||||
set ::lConflict [list]
|
||||
proc xConflict {args} {
|
||||
set res [lindex $::lConflict 0]
|
||||
set ::lConflict [lrange $::lConflict 1 end]
|
||||
return $res
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# The following test cases - 1.* - test that the rebase blobs output by
|
||||
# sqlite3_changeset_apply_v2 look correct in some simple cases. The blob
|
||||
# is itself a changeset, containing records determined as follows:
|
||||
#
|
||||
# * For each conflict resolved with REPLACE, the rebase blob contains
|
||||
# a DELETE record. All fields other than the PK fields are undefined.
|
||||
#
|
||||
# * For each conflict resolved with OMIT, the rebase blob contains an
|
||||
# INSERT record. For an INSERT or UPDATE operation, the indirect flag
|
||||
# is clear and all updated fields are defined. For a DELETE operation,
|
||||
# the indirect flag is set and all non-PK fields left undefined.
|
||||
#
|
||||
proc do_apply_v2_test {tn sql modsql conflict_handler res} {
|
||||
|
||||
execsql BEGIN
|
||||
sqlite3session S db main
|
||||
S attach *
|
||||
execsql $sql
|
||||
set changeset [S changeset]
|
||||
S delete
|
||||
execsql ROLLBACK
|
||||
|
||||
execsql BEGIN
|
||||
execsql $modsql
|
||||
set ::lConflict $conflict_handler
|
||||
set blob [sqlite3changeset_apply_v2 db $changeset xConflict]
|
||||
execsql ROLLBACK
|
||||
|
||||
uplevel [list do_test $tn [list changeset_to_list $blob] [list {*}$res]]
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||
INSERT INTO t1 VALUES(1, 'value A');
|
||||
}
|
||||
|
||||
do_apply_v2_test 1.1.1 {
|
||||
UPDATE t1 SET b = 'value B' WHERE a=1;
|
||||
} {
|
||||
UPDATE t1 SET b = 'value C' WHERE a=1;
|
||||
} {
|
||||
OMIT
|
||||
} {
|
||||
{INSERT t1 0 X. {} {i 1 t {value B}}}
|
||||
}
|
||||
do_apply_v2_test 1.1.2 {
|
||||
UPDATE t1 SET b = 'value B' WHERE a=1;
|
||||
} {
|
||||
UPDATE t1 SET b = 'value C' WHERE a=1;
|
||||
} {
|
||||
REPLACE
|
||||
} {
|
||||
{DELETE t1 0 X. {i 1 {} {}} {}}
|
||||
}
|
||||
|
||||
do_apply_v2_test 1.2.1 {
|
||||
INSERT INTO t1 VALUES(2, 'first');
|
||||
} {
|
||||
INSERT INTO t1 VALUES(2, 'second');
|
||||
} {
|
||||
OMIT
|
||||
} {
|
||||
{INSERT t1 0 X. {} {i 2 t first}}
|
||||
}
|
||||
do_apply_v2_test 1.2.2 {
|
||||
INSERT INTO t1 VALUES(2, 'first');
|
||||
} {
|
||||
INSERT INTO t1 VALUES(2, 'second');
|
||||
} {
|
||||
REPLACE
|
||||
} {
|
||||
{DELETE t1 0 X. {i 2 {} {}} {}}
|
||||
}
|
||||
|
||||
do_apply_v2_test 1.3.1 {
|
||||
DELETE FROM t1 WHERE a=1;
|
||||
} {
|
||||
UPDATE t1 SET b='value D' WHERE a=1;
|
||||
} {
|
||||
OMIT
|
||||
} {
|
||||
{INSERT t1 1 X. {} {i 1 {} {}}}
|
||||
}
|
||||
do_apply_v2_test 1.3.2 {
|
||||
DELETE FROM t1 WHERE a=1;
|
||||
} {
|
||||
UPDATE t1 SET b='value D' WHERE a=1;
|
||||
} {
|
||||
REPLACE
|
||||
} {
|
||||
{DELETE t1 0 X. {i 1 {} {}} {}}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
@ -3410,6 +3410,8 @@ struct SessionApplyCtx {
|
||||
int bStat1; /* True if table is sqlite_stat1 */
|
||||
int bDeferConstraints; /* True to defer constraints */
|
||||
SessionBuffer constraints; /* Deferred constraints are stored here */
|
||||
SessionBuffer rebase; /* Rebase information (if any) here */
|
||||
int bRebaseStarted; /* If table header is already in rebase */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -3791,6 +3793,60 @@ static int sessionSeekToRow(
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sessionRebaseAdd(
|
||||
SessionApplyCtx *p,
|
||||
int eType,
|
||||
sqlite3_changeset_iter *pIter
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
int i;
|
||||
int eOp = pIter->op;
|
||||
if( p->bRebaseStarted==0 ){
|
||||
/* Append a table-header to the rebase buffer */
|
||||
const char *zTab = pIter->zTab;
|
||||
sessionAppendByte(&p->rebase, 'T', &rc);
|
||||
sessionAppendVarint(&p->rebase, p->nCol, &rc);
|
||||
sessionAppendBlob(&p->rebase, p->abPK, p->nCol, &rc);
|
||||
sessionAppendBlob(&p->rebase, (u8*)zTab, (int)strlen(zTab)+1, &rc);
|
||||
p->bRebaseStarted = 1;
|
||||
}
|
||||
|
||||
assert( eType==SQLITE_CHANGESET_REPLACE||eType==SQLITE_CHANGESET_OMIT );
|
||||
assert( eOp==SQLITE_DELETE || eOp==SQLITE_INSERT || eOp==SQLITE_UPDATE );
|
||||
|
||||
if( eType==SQLITE_CHANGESET_REPLACE ){
|
||||
sessionAppendByte(&p->rebase, SQLITE_DELETE, &rc);
|
||||
sessionAppendByte(&p->rebase, 0, &rc);
|
||||
for(i=0; i<p->nCol; i++){
|
||||
if( p->abPK[i]==0 ){
|
||||
sessionAppendByte(&p->rebase, 0, &rc);
|
||||
}else{
|
||||
sqlite3_value *pVal = 0;
|
||||
if( eOp==SQLITE_INSERT ){
|
||||
sqlite3changeset_new(pIter, i, &pVal);
|
||||
}else{
|
||||
sqlite3changeset_old(pIter, i, &pVal);
|
||||
}
|
||||
sessionAppendValue(&p->rebase, pVal, &rc);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
sessionAppendByte(&p->rebase, SQLITE_INSERT, &rc);
|
||||
sessionAppendByte(&p->rebase, eOp==SQLITE_DELETE, &rc);
|
||||
for(i=0; i<p->nCol; i++){
|
||||
sqlite3_value *pVal = 0;
|
||||
if( eOp!=SQLITE_INSERT && p->abPK[i] ){
|
||||
sqlite3changeset_old(pIter, i, &pVal);
|
||||
}else{
|
||||
sqlite3changeset_new(pIter, i, &pVal);
|
||||
}
|
||||
sessionAppendValue(&p->rebase, pVal, &rc);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Invoke the conflict handler for the change that the changeset iterator
|
||||
** currently points to.
|
||||
@ -3866,7 +3922,7 @@ static int sessionConflictHandler(
|
||||
u8 *aBlob = &pIter->in.aData[pIter->in.iCurrent];
|
||||
int nBlob = pIter->in.iNext - pIter->in.iCurrent;
|
||||
sessionAppendBlob(&p->constraints, aBlob, nBlob, &rc);
|
||||
res = SQLITE_CHANGESET_OMIT;
|
||||
return SQLITE_OK;
|
||||
}else{
|
||||
/* No other row with the new.* primary key. */
|
||||
res = xConflict(pCtx, eType+1, pIter);
|
||||
@ -3892,6 +3948,9 @@ static int sessionConflictHandler(
|
||||
rc = SQLITE_MISUSE;
|
||||
break;
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sessionRebaseAdd(p, res, pIter);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
@ -4067,42 +4126,42 @@ static int sessionApplyOneWithRetry(
|
||||
int rc;
|
||||
|
||||
rc = sessionApplyOneOp(pIter, pApply, xConflict, pCtx, &bReplace, &bRetry);
|
||||
assert( rc==SQLITE_OK || (bRetry==0 && bReplace==0) );
|
||||
|
||||
/* If the bRetry flag is set, the change has not been applied due to an
|
||||
** SQLITE_CHANGESET_DATA problem (i.e. this is an UPDATE or DELETE and
|
||||
** a row with the correct PK is present in the db, but one or more other
|
||||
** fields do not contain the expected values) and the conflict handler
|
||||
** returned SQLITE_CHANGESET_REPLACE. In this case retry the operation,
|
||||
** but pass NULL as the final argument so that sessionApplyOneOp() ignores
|
||||
** the SQLITE_CHANGESET_DATA problem. */
|
||||
if( bRetry ){
|
||||
assert( pIter->op==SQLITE_UPDATE || pIter->op==SQLITE_DELETE );
|
||||
rc = sessionApplyOneOp(pIter, pApply, xConflict, pCtx, 0, 0);
|
||||
}
|
||||
|
||||
/* If the bReplace flag is set, the change is an INSERT that has not
|
||||
** been performed because the database already contains a row with the
|
||||
** specified primary key and the conflict handler returned
|
||||
** SQLITE_CHANGESET_REPLACE. In this case remove the conflicting row
|
||||
** before reattempting the INSERT. */
|
||||
else if( bReplace ){
|
||||
assert( pIter->op==SQLITE_INSERT );
|
||||
rc = sqlite3_exec(db, "SAVEPOINT replace_op", 0, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sessionBindRow(pIter,
|
||||
sqlite3changeset_new, pApply->nCol, pApply->abPK, pApply->pDelete);
|
||||
sqlite3_bind_int(pApply->pDelete, pApply->nCol+1, 1);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_step(pApply->pDelete);
|
||||
rc = sqlite3_reset(pApply->pDelete);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
if( rc==SQLITE_OK ){
|
||||
/* If the bRetry flag is set, the change has not been applied due to an
|
||||
** SQLITE_CHANGESET_DATA problem (i.e. this is an UPDATE or DELETE and
|
||||
** a row with the correct PK is present in the db, but one or more other
|
||||
** fields do not contain the expected values) and the conflict handler
|
||||
** returned SQLITE_CHANGESET_REPLACE. In this case retry the operation,
|
||||
** but pass NULL as the final argument so that sessionApplyOneOp() ignores
|
||||
** the SQLITE_CHANGESET_DATA problem. */
|
||||
if( bRetry ){
|
||||
assert( pIter->op==SQLITE_UPDATE || pIter->op==SQLITE_DELETE );
|
||||
rc = sessionApplyOneOp(pIter, pApply, xConflict, pCtx, 0, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_exec(db, "RELEASE replace_op", 0, 0, 0);
|
||||
|
||||
/* If the bReplace flag is set, the change is an INSERT that has not
|
||||
** been performed because the database already contains a row with the
|
||||
** specified primary key and the conflict handler returned
|
||||
** SQLITE_CHANGESET_REPLACE. In this case remove the conflicting row
|
||||
** before reattempting the INSERT. */
|
||||
else if( bReplace ){
|
||||
assert( pIter->op==SQLITE_INSERT );
|
||||
rc = sqlite3_exec(db, "SAVEPOINT replace_op", 0, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sessionBindRow(pIter,
|
||||
sqlite3changeset_new, pApply->nCol, pApply->abPK, pApply->pDelete);
|
||||
sqlite3_bind_int(pApply->pDelete, pApply->nCol+1, 1);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_step(pApply->pDelete);
|
||||
rc = sqlite3_reset(pApply->pDelete);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sessionApplyOneOp(pIter, pApply, xConflict, pCtx, 0, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_exec(db, "RELEASE replace_op", 0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4178,7 +4237,8 @@ static int sessionChangesetApply(
|
||||
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
|
||||
sqlite3_changeset_iter *p /* Handle describing change and conflict */
|
||||
),
|
||||
void *pCtx /* First argument passed to xConflict */
|
||||
void *pCtx, /* First argument passed to xConflict */
|
||||
void **ppRebase, int *pnRebase /* OUT: Rebase information */
|
||||
){
|
||||
int schemaMismatch = 0;
|
||||
int rc; /* Return code */
|
||||
@ -4219,6 +4279,7 @@ static int sessionChangesetApply(
|
||||
memset(&sApply, 0, sizeof(sApply));
|
||||
sApply.db = db;
|
||||
sApply.bDeferConstraints = 1;
|
||||
sApply.bRebaseStarted = 0;
|
||||
|
||||
/* If an xFilter() callback was specified, invoke it now. If the
|
||||
** xFilter callback returns zero, skip this table. If it returns
|
||||
@ -4328,16 +4389,48 @@ static int sessionChangesetApply(
|
||||
sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && ppRebase && pnRebase ){
|
||||
*ppRebase = (void*)sApply.rebase.aBuf;
|
||||
*pnRebase = sApply.rebase.nBuf;
|
||||
sApply.rebase.aBuf = 0;
|
||||
}
|
||||
sqlite3_finalize(sApply.pInsert);
|
||||
sqlite3_finalize(sApply.pDelete);
|
||||
sqlite3_finalize(sApply.pUpdate);
|
||||
sqlite3_finalize(sApply.pSelect);
|
||||
sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */
|
||||
sqlite3_free((char*)sApply.constraints.aBuf);
|
||||
sqlite3_free((char*)sApply.rebase.aBuf);
|
||||
sqlite3_mutex_leave(sqlite3_db_mutex(db));
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sqlite3changeset_apply_v2(
|
||||
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() */
|
||||
const char *zTab /* Table name */
|
||||
),
|
||||
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
|
||||
){
|
||||
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
|
||||
int rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sessionChangesetApply(
|
||||
db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase
|
||||
);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Apply the changeset passed via pChangeset/nChangeset to the main database
|
||||
** attached to handle "db". Invoke the supplied conflict handler callback
|
||||
@ -4358,12 +4451,9 @@ int sqlite3changeset_apply(
|
||||
),
|
||||
void *pCtx /* First argument passed to xConflict */
|
||||
){
|
||||
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
|
||||
int rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sessionChangesetApply(db, pIter, xFilter, xConflict, pCtx);
|
||||
}
|
||||
return rc;
|
||||
return sqlite3changeset_apply_v2(
|
||||
db, nChangeset, pChangeset, xFilter, xConflict, pCtx, 0, 0
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -4371,6 +4461,31 @@ 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_v2_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() */
|
||||
const char *zTab /* Table name */
|
||||
),
|
||||
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
|
||||
){
|
||||
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
|
||||
int rc = sqlite3changeset_start_strm(&pIter, xInput, pIn);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sessionChangesetApply(
|
||||
db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase
|
||||
);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
int sqlite3changeset_apply_strm(
|
||||
sqlite3 *db, /* Apply change to "main" db of this handle */
|
||||
int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
|
||||
@ -4386,12 +4501,9 @@ int sqlite3changeset_apply_strm(
|
||||
),
|
||||
void *pCtx /* First argument passed to xConflict */
|
||||
){
|
||||
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
|
||||
int rc = sqlite3changeset_start_strm(&pIter, xInput, pIn);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sessionChangesetApply(db, pIter, xFilter, xConflict, pCtx);
|
||||
}
|
||||
return rc;
|
||||
return sqlite3changeset_apply_v2_strm(
|
||||
db, xInput, pIn, xFilter, xConflict, pCtx, 0, 0
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1103,6 +1103,23 @@ int sqlite3changeset_apply(
|
||||
void *pCtx /* First argument passed to xConflict */
|
||||
);
|
||||
|
||||
int sqlite3changeset_apply_v2(
|
||||
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() */
|
||||
const char *zTab /* Table name */
|
||||
),
|
||||
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
|
||||
);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Constants Passed To The Conflict Handler
|
||||
**
|
||||
@ -1303,6 +1320,22 @@ int sqlite3changeset_apply_strm(
|
||||
),
|
||||
void *pCtx /* First argument passed to xConflict */
|
||||
);
|
||||
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 */
|
||||
void *pIn, /* First arg for xInput */
|
||||
int(*xFilter)(
|
||||
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||
const char *zTab /* Table name */
|
||||
),
|
||||
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 sqlite3changeset_concat_strm(
|
||||
int (*xInputA)(void *pIn, void *pData, int *pnData),
|
||||
void *pInA,
|
||||
|
@ -711,10 +711,8 @@ static int testStreamInput(
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** sqlite3changeset_apply DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_sqlite3changeset_apply(
|
||||
static int SQLITE_TCLAPI testSqlite3changesetApply(
|
||||
int bV2,
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@ -727,6 +725,8 @@ static int SQLITE_TCLAPI test_sqlite3changeset_apply(
|
||||
int nChangeset; /* Size of buffer aChangeset in bytes */
|
||||
TestConflictHandler ctx;
|
||||
TestStreamInput sStr;
|
||||
void *pRebase = 0;
|
||||
int nRebase = 0;
|
||||
|
||||
memset(&sStr, 0, sizeof(sStr));
|
||||
sStr.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
|
||||
@ -748,9 +748,16 @@ static int SQLITE_TCLAPI test_sqlite3changeset_apply(
|
||||
ctx.interp = interp;
|
||||
|
||||
if( sStr.nStream==0 ){
|
||||
rc = sqlite3changeset_apply(db, nChangeset, pChangeset,
|
||||
(objc==5) ? test_filter_handler : 0, test_conflict_handler, (void *)&ctx
|
||||
);
|
||||
if( bV2==0 ){
|
||||
rc = sqlite3changeset_apply(db, nChangeset, pChangeset,
|
||||
(objc==5)?test_filter_handler:0, test_conflict_handler, (void *)&ctx
|
||||
);
|
||||
}else{
|
||||
rc = sqlite3changeset_apply_v2(db, nChangeset, pChangeset,
|
||||
(objc==5)?test_filter_handler:0, test_conflict_handler, (void *)&ctx,
|
||||
&pRebase, &nRebase
|
||||
);
|
||||
}
|
||||
}else{
|
||||
sStr.aData = (unsigned char*)pChangeset;
|
||||
sStr.nData = nChangeset;
|
||||
@ -761,11 +768,39 @@ static int SQLITE_TCLAPI test_sqlite3changeset_apply(
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
return test_session_error(interp, rc, 0);
|
||||
}else{
|
||||
Tcl_ResetResult(interp);
|
||||
if( bV2 && pRebase ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pRebase, nRebase));
|
||||
}
|
||||
}
|
||||
Tcl_ResetResult(interp);
|
||||
sqlite3_free(pRebase);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3changeset_apply DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_sqlite3changeset_apply(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
return testSqlite3changesetApply(0, clientData, interp, objc, objv);
|
||||
}
|
||||
/*
|
||||
** sqlite3changeset_apply_v2 DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_sqlite3changeset_apply_v2(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
return testSqlite3changesetApply(1, clientData, interp, objc, objv);
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3changeset_apply_replace_all DB CHANGESET
|
||||
*/
|
||||
@ -1029,6 +1064,7 @@ int TestSession_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3changeset_invert", test_sqlite3changeset_invert },
|
||||
{ "sqlite3changeset_concat", test_sqlite3changeset_concat },
|
||||
{ "sqlite3changeset_apply", test_sqlite3changeset_apply },
|
||||
{ "sqlite3changeset_apply_v2", test_sqlite3changeset_apply_v2 },
|
||||
{ "sqlite3changeset_apply_replace_all",
|
||||
test_sqlite3changeset_apply_replace_all },
|
||||
{ "sql_exec_changeset", test_sql_exec_changeset },
|
||||
|
20
manifest
20
manifest
@ -1,5 +1,5 @@
|
||||
C Fix\sa\stypo\scausing\sSQLITE_LOG_CACHE_SPILL\sbuilds\sto\sfail.
|
||||
D 2018-03-12T21:09:16.462
|
||||
C Add\ssqlite3_changeset_apply_v2()\sand\sapply_v2_strm()\sto\sthe\ssessions\smodule.
|
||||
D 2018-03-13T20:31:23.940
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F Makefile.in 7016fc56c6b9bfe5daac4f34be8be38d8c0b5fab79ccbfb764d3b23bf1c6fff3
|
||||
@ -400,11 +400,12 @@ F ext/session/sessionat.test efe88965e74ff1bc2af9c310b28358c02d420c1fb2705cc7a28
|
||||
F ext/session/sessiondiff.test ad13dd65664bae26744e1f18eb3cbd5588349b7e9118851d8f9364248d67bcec
|
||||
F ext/session/sessionfault.test da273f2712b6411e85e71465a1733b8501dbf6f7
|
||||
F ext/session/sessionfault2.test 04aa0bc9aa70ea43d8de82c4f648db4de1e990b0
|
||||
F ext/session/sessionrebase.test b4ac7545e3c69deaeab061c2bf36ad9e99aa6c38db94c340d7e48a230a9d4be8
|
||||
F ext/session/sessionstat1.test 41cd97c2e48619a41cdf8ae749e1b25f34719de638689221aa43971be693bf4e
|
||||
F ext/session/sessionwor.test 2f3744236dc8b170a695b7d8ddc8c743c7e79fdc
|
||||
F ext/session/sqlite3session.c 9edfaaa74977ddecd7bbd94e8f844d9b0f6eec22d1d547e806361670db814c1e
|
||||
F ext/session/sqlite3session.h 2e1584b030fbd841cefdce15ba984871978d305f586da2d1972f6e1958fa10b1
|
||||
F ext/session/test_session.c eb0bd6c1ea791c1d66ee4ef94c16500dad936386
|
||||
F ext/session/sqlite3session.c 564e609f3086510d319e8abc0899fa79131d071b7dc01138e1b75bbf4a43cb03
|
||||
F ext/session/sqlite3session.h 8fe499b736633021094b814cf128691587f19c1943e372a23db37175bdeb8c67
|
||||
F ext/session/test_session.c 6c45bee58063f2c3d1edc213833d72f0a3ec651cbe27393b9e4a054e711b7c44
|
||||
F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
|
||||
F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
|
||||
F ext/userauth/userauth.c 3410be31283abba70255d71fd24734e017a4497f
|
||||
@ -1712,7 +1713,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 61eeb48f03f8a9a32330a5cae4387bb4e5618078cf669a5831910f99126900ec
|
||||
R cc8ceb6c97c49d267aa8c6c8a3936381
|
||||
P 0171d4a71ca7911a9fd409a42eeed0eda4521b6e48df5cd058364c0a736313b7
|
||||
R 1a6994480e1e2c2b73e68977f31547a3
|
||||
T *branch * sessions-rebase
|
||||
T *sym-sessions-rebase *
|
||||
T -sym-trunk *
|
||||
U dan
|
||||
Z 467f1d68de166a5be6bc42e03b4e2dce
|
||||
Z d8bbc56681a1d6ff1f4fb97dc44d1b66
|
||||
|
@ -1 +1 @@
|
||||
0171d4a71ca7911a9fd409a42eeed0eda4521b6e48df5cd058364c0a736313b7
|
||||
445bfe977d9f3a891e08ef33237862ed047fe83e134ef3ed8b47ee0f5abd8cd6
|
Loading…
x
Reference in New Issue
Block a user