Add sqlite3_changeset_apply_v2() and apply_v2_strm() to the sessions module.

FossilOrigin-Name: 445bfe977d9f3a891e08ef33237862ed047fe83e134ef3ed8b47ee0f5abd8cd6
This commit is contained in:
dan 2018-03-13 20:31:23 +00:00
parent 58db4c760f
commit a38e6c57bc
6 changed files with 375 additions and 65 deletions

View 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

View File

@ -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
);
}
/*

View File

@ -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,

View File

@ -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 },

View File

@ -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

View File

@ -1 +1 @@
0171d4a71ca7911a9fd409a42eeed0eda4521b6e48df5cd058364c0a736313b7
445bfe977d9f3a891e08ef33237862ed047fe83e134ef3ed8b47ee0f5abd8cd6