mirror of
https://github.com/sqlite/sqlite.git
synced 2026-10-23 21:49:02 +03:00
Add an API to the sessions module to add changes one at a time to an sqlite3_changegroup object.
FossilOrigin-Name: cf1122ea6409bcbd341b67ae45147abb8d5cd491ae95491c801d0621181da40b
This commit is contained in:
400
ext/session/sessionchange2.test
Normal file
400
ext/session/sessionchange2.test
Normal file
@@ -0,0 +1,400 @@
|
||||
# 2026 January 09
|
||||
#
|
||||
# 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 sessionchange2
|
||||
|
||||
do_test 1.1.1 {
|
||||
sqlite3changegroup grp
|
||||
list [catch { grp change_begin INSERT "nosuchtable" 0 } msg] $msg
|
||||
} {1 {SQLITE_ERROR - no such table: nosuchtable}}
|
||||
|
||||
do_test 1.1.2 {
|
||||
grp schema db main
|
||||
list [catch { grp change_begin INSERT "nosuchtable" 0 } msg] $msg
|
||||
} {1 {SQLITE_ERROR - no such table: nosuchtable}}
|
||||
|
||||
do_test 1.1.3 {
|
||||
list [catch { grp change_begin_ne INSERT "nosuchtable" 0 } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
|
||||
do_execsql_test 1.2.1 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||
}
|
||||
do_test 1.2.2 {
|
||||
list [catch { grp change_begin 435 "t1" 0 } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||
CREATE TABLE t2(a, b);
|
||||
}
|
||||
|
||||
do_test 2.1.1 {
|
||||
sqlite3changegroup grp
|
||||
grp schema db main
|
||||
grp change_begin INSERT "t1" 0
|
||||
grp change_int64 new 0 55
|
||||
grp change_int64 new 1 101
|
||||
grp change_finish false
|
||||
} {}
|
||||
|
||||
do_test 2.1.2 {
|
||||
set C [grp output]
|
||||
grp delete
|
||||
changeset_to_list $C
|
||||
} {
|
||||
{INSERT t1 0 X. {} {i 55 i 101}}
|
||||
}
|
||||
|
||||
do_test 2.2.1 {
|
||||
sqlite3changegroup grp
|
||||
grp schema db main
|
||||
grp change_begin INSERT "t2" 0
|
||||
grp change_int64 new 0 -5
|
||||
grp change_int64 new 1 -10
|
||||
grp change_int64 new 2 -20
|
||||
grp change_finish false
|
||||
} {}
|
||||
|
||||
do_test 2.2.2 {
|
||||
set C [grp output]
|
||||
grp delete
|
||||
changeset_to_list $C
|
||||
} {
|
||||
{INSERT t2 0 X.. {} {i -5 i -10 i -20}}
|
||||
}
|
||||
|
||||
do_test 2.2.3 {
|
||||
sqlite3changegroup grp
|
||||
grp schema db main
|
||||
grp change_begin INSERT "t1" 0
|
||||
grp change_int64 new 0 223344
|
||||
grp change_null new 1
|
||||
grp change_finish false
|
||||
} {}
|
||||
|
||||
do_test 2.2.4 {
|
||||
set C [grp output]
|
||||
grp delete
|
||||
changeset_to_list $C
|
||||
} {
|
||||
{INSERT t1 0 X. {} {i 223344 n {}}}
|
||||
}
|
||||
|
||||
do_test 2.2.5 {
|
||||
sqlite3changegroup grp
|
||||
grp schema db main
|
||||
grp change_begin DELETE "t1" 0
|
||||
|
||||
grp change_int64 old 0 1
|
||||
grp change_int64 old 1 123
|
||||
|
||||
grp change_finish false
|
||||
} {}
|
||||
|
||||
do_test 2.2.6 {
|
||||
set C [grp output]
|
||||
grp delete
|
||||
changeset_to_list $C
|
||||
} {
|
||||
{DELETE t1 0 X. {i 1 i 123} {}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b, c);
|
||||
CREATE TABLE t2(x, y);
|
||||
}
|
||||
|
||||
foreach {tn script error} {
|
||||
1 {
|
||||
grp change_begin UPDATE t1 0
|
||||
} {SQLITE_ERROR - invalid change: undefined value in PK of old.* record}
|
||||
|
||||
2 {
|
||||
grp change_begin UPDATE t1 0
|
||||
grp change_int64 old 0 1234
|
||||
grp change_int64 new 0 5678
|
||||
} {SQLITE_ERROR - invalid change: defined value in PK of new.* record}
|
||||
|
||||
3 {
|
||||
grp change_begin UPDATE t1 0
|
||||
grp change_null old 0
|
||||
} {SQLITE_ERROR - invalid change: null value in PK of old.* record}
|
||||
|
||||
4 {
|
||||
grp change_begin UPDATE t1 0
|
||||
grp change_int64 old 0 1234
|
||||
grp change_int64 old 1 20
|
||||
} {SQLITE_ERROR - invalid change: column 1 - old.* value is defined but new.* is undefined}
|
||||
|
||||
5 {
|
||||
grp change_begin UPDATE t1 0
|
||||
grp change_int64 old 0 1234
|
||||
grp change_int64 new 1 20
|
||||
} {SQLITE_ERROR - invalid change: column 1 - old.* value is undefined but new.* is defined}
|
||||
|
||||
6 {
|
||||
grp change_begin INSERT t1 0
|
||||
grp change_null new 0
|
||||
grp change_int64 new 1 20
|
||||
} {SQLITE_ERROR - invalid change: null value in PK}
|
||||
|
||||
7 {
|
||||
grp change_begin INSERT t1 0
|
||||
grp change_int64 new 1 20
|
||||
} {SQLITE_ERROR - invalid change: column 0 is undefined}
|
||||
|
||||
8 {
|
||||
grp change_begin INSERT t1 0
|
||||
grp change_int64 new 0 20
|
||||
} {SQLITE_ERROR - invalid change: column 1 is undefined}
|
||||
|
||||
9 {
|
||||
grp change_begin DELETE t1 0
|
||||
grp change_int64 old 0 20
|
||||
} {SQLITE_ERROR - invalid change: column 1 is undefined}
|
||||
|
||||
10 {
|
||||
grp change_begin DELETE t1 0
|
||||
grp change_int64 old 1 20
|
||||
} {SQLITE_ERROR - invalid change: column 0 is undefined}
|
||||
|
||||
} {
|
||||
sqlite3changegroup grp
|
||||
grp schema db main
|
||||
eval $script
|
||||
do_test 3.1.$tn {
|
||||
list [catch { grp change_finish false } msg] $msg
|
||||
} [list 1 $error]
|
||||
grp delete
|
||||
}
|
||||
|
||||
do_test 3.2.1 {
|
||||
sqlite3changegroup grp
|
||||
grp schema db main
|
||||
grp change_begin DELETE t1 0
|
||||
list [catch {grp change_int64 new 0 20} msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
do_test 3.2.2 {
|
||||
grp change_finish true
|
||||
grp change_begin INSERT t1 0
|
||||
list [catch {grp change_int64 old 0 20} msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
|
||||
grp delete
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||
CREATE TABLE t2(a, b);
|
||||
}
|
||||
|
||||
proc do_changegroup_script_test {tn script changeset patchset} {
|
||||
|
||||
breakpoint
|
||||
sqlite3changegroup grp
|
||||
grp schema db main
|
||||
eval $script
|
||||
do_test $tn.1 {
|
||||
changeset_to_list [grp output]
|
||||
} [list {*}$changeset]
|
||||
grp delete
|
||||
|
||||
sqlite3changegroup grp
|
||||
grp config patchset 1
|
||||
grp schema db main
|
||||
eval $script
|
||||
do_test $tn.2 {
|
||||
changeset_to_list [grp output]
|
||||
} [list {*}$patchset]
|
||||
grp delete
|
||||
|
||||
}
|
||||
|
||||
proc do_changegroup_patchset_test {tn script patchset} {
|
||||
|
||||
sqlite3changegroup grp
|
||||
grp config patchset 1
|
||||
grp schema db main
|
||||
eval $script
|
||||
do_test $tn {
|
||||
changeset_to_list [grp output]
|
||||
} [list {*}$patchset]
|
||||
grp delete
|
||||
|
||||
}
|
||||
|
||||
do_changegroup_script_test 3.1 {
|
||||
grp change_begin UPDATE t1 0
|
||||
grp change_int64 old 0 123
|
||||
grp change_int64 old 1 456
|
||||
grp change_int64 new 1 789
|
||||
grp change_finish false
|
||||
|
||||
grp change_begin INSERT t1 0
|
||||
grp change_int64 new 0 124
|
||||
grp change_text new 1 hello_world
|
||||
grp change_finish false
|
||||
|
||||
grp change_begin DELETE t1 0
|
||||
grp change_int64 old 0 125
|
||||
grp change_double old 1 45.67
|
||||
grp change_finish false
|
||||
} {
|
||||
{DELETE t1 0 X. {i 125 f 45.67} {}}
|
||||
{INSERT t1 0 X. {} {i 124 t hello_world}}
|
||||
{UPDATE t1 0 X. {i 123 i 456} {{} {} i 789}}
|
||||
} {
|
||||
{DELETE t1 0 X. {i 125 {} {}} {}}
|
||||
{INSERT t1 0 X. {} {i 124 t hello_world}}
|
||||
{UPDATE t1 0 X. {i 123 {} {}} {{} {} i 789}}
|
||||
}
|
||||
|
||||
do_changegroup_script_test 3.1.2 {
|
||||
grp change_begin INSERT t1 0
|
||||
grp change_int64 new 0 124
|
||||
grp change_text new 1 hello_world
|
||||
grp change_finish false
|
||||
} {
|
||||
{INSERT t1 0 X. {} {i 124 t hello_world}}
|
||||
} {
|
||||
{INSERT t1 0 X. {} {i 124 t hello_world}}
|
||||
}
|
||||
|
||||
do_changegroup_patchset_test 3.2 {
|
||||
grp change_begin UPDATE t1 0
|
||||
grp change_int64 old 0 123
|
||||
grp change_int64 new 1 789
|
||||
grp change_finish false
|
||||
|
||||
grp change_begin DELETE t1 1
|
||||
grp change_int64 old 0 122
|
||||
grp change_finish false
|
||||
} {
|
||||
{DELETE t1 1 X. {i 122 {} {}} {}}
|
||||
{UPDATE t1 0 X. {i 123 {} {}} {{} {} i 789}}
|
||||
}
|
||||
|
||||
do_changegroup_patchset_test 3.3 {
|
||||
grp change_begin DELETE t1 0
|
||||
grp change_int64 old 0 123
|
||||
grp change_finish false
|
||||
} {
|
||||
{DELETE t1 0 X. {i 123 {} {}} {}}
|
||||
}
|
||||
|
||||
do_changegroup_script_test 3.4 {
|
||||
grp change_begin INSERT t1 0
|
||||
grp change_int64 new 0 124
|
||||
grp change_text new 1 hello_world
|
||||
grp change_finish false
|
||||
|
||||
grp change_begin DELETE t1 0
|
||||
grp change_int64 old 0 124
|
||||
grp change_text old 1 hello_world
|
||||
grp change_finish false
|
||||
|
||||
grp change_begin INSERT t1 0
|
||||
grp change_int64 new 0 1000
|
||||
grp change_blob new 1 abcd
|
||||
grp change_finish false
|
||||
|
||||
grp change_begin UPDATE t1 0
|
||||
grp change_int64 old 0 1000
|
||||
grp change_blob old 1 abcd
|
||||
grp change_blob new 1 bcda
|
||||
grp change_finish false
|
||||
} {
|
||||
{INSERT t1 0 X. {} {i 1000 b bcda}}
|
||||
} {
|
||||
{INSERT t1 0 X. {} {i 1000 b bcda}}
|
||||
}
|
||||
|
||||
do_changegroup_script_test 3.5 {
|
||||
grp change_begin DELETE t1 0
|
||||
grp change_int64 old 0 1000
|
||||
grp change_text-1 old 1 "hello world"
|
||||
grp change_finish false
|
||||
} {
|
||||
{DELETE t1 0 X. {i 1000 t {hello world}} {}}
|
||||
} {
|
||||
{DELETE t1 0 X. {i 1000 {} {}} {}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
# Calling sqlite3changegroup_change_finish() if a change has not been
|
||||
# started is a no-op.
|
||||
#
|
||||
do_test 4.0 {
|
||||
sqlite3changegroup grp
|
||||
grp change_finish false
|
||||
} {}
|
||||
|
||||
# But calling any other sqlite3changegroup_change_xxx() function if a
|
||||
# change has not been started is a misuse.
|
||||
#
|
||||
foreach {tn cmd} {
|
||||
1 { grp change_blob old 0 abcd }
|
||||
2 { grp change_text new 1 helloworld }
|
||||
3 { grp change_int64 old 2 12345678 }
|
||||
4 { grp change_double new 3 12.345 }
|
||||
5 { grp change_null old 4 }
|
||||
} {
|
||||
do_test 4.1.$tn {
|
||||
list [catch $::cmd msg] $msg
|
||||
} {1 SQLITE_MISUSE}
|
||||
}
|
||||
grp delete
|
||||
|
||||
do_execsql_test 4.2.1 {
|
||||
CREATE TABLE t3(a, b PRIMARY KEY);
|
||||
}
|
||||
do_test 4.2 {
|
||||
sqlite3changegroup grp
|
||||
grp schema db main
|
||||
grp change_begin INSERT t3 0
|
||||
|
||||
list [catch { grp change_begin INSERT t3 0 } msg] $msg
|
||||
} {1 SQLITE_MISUSE}
|
||||
grp delete
|
||||
|
||||
do_test 4.3 {
|
||||
sqlite3changegroup grp
|
||||
grp schema db main
|
||||
grp change_begin UPDATE t3 0
|
||||
} {}
|
||||
foreach {tn cmd} {
|
||||
1 { grp change_blob old -1 abcd }
|
||||
2 { grp change_text new 2 helloworld }
|
||||
} {
|
||||
do_test 4.4.$tn {
|
||||
list [catch $::cmd msg] $msg
|
||||
} {1 SQLITE_RANGE}
|
||||
}
|
||||
grp delete
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -43,7 +43,7 @@ set C2 [changeset_from_sql {
|
||||
INSERT INTO t1 VALUES(0, 0, 0, 0);
|
||||
}]
|
||||
|
||||
do_faultsim_test 1 -faults oom* -prep {
|
||||
do_faultsim_test 1.2 -faults oom* -prep {
|
||||
sqlite3changegroup G
|
||||
} -body {
|
||||
G schema db main
|
||||
@@ -56,6 +56,20 @@ do_faultsim_test 1 -faults oom* -prep {
|
||||
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
|
||||
}
|
||||
|
||||
do_faultsim_test 1.3 -faults oom* -prep {
|
||||
sqlite3changegroup G
|
||||
} -body {
|
||||
G schema db main
|
||||
G change_begin INSERT t1 0
|
||||
G change_int64 new 0 12345
|
||||
G change_int64 new 1 67890
|
||||
G output
|
||||
set {} {}
|
||||
} -test {
|
||||
catch { G delete }
|
||||
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
|
||||
@@ -391,6 +391,19 @@ static void sessionPutI64(u8 *aBuf, sqlite3_int64 i){
|
||||
aBuf[7] = (i>> 0) & 0xFF;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write a double value to the buffer aBuf[].
|
||||
*/
|
||||
static void sessionPutDouble(u8 *aBuf, double r){
|
||||
/* TODO: SQLite does something special to deal with mixed-endian
|
||||
** floating point values (e.g. ARM7). This code probably should
|
||||
** too. */
|
||||
u64 i;
|
||||
assert( sizeof(double)==8 && sizeof(u64)==8 );
|
||||
memcpy(&i, &r, 8);
|
||||
sessionPutI64(aBuf, i);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is used to serialize the contents of value pValue (see
|
||||
** comment titled "RECORD FORMAT" above).
|
||||
@@ -428,16 +441,13 @@ static int sessionSerializeValue(
|
||||
/* TODO: SQLite does something special to deal with mixed-endian
|
||||
** floating point values (e.g. ARM7). This code probably should
|
||||
** too. */
|
||||
u64 i;
|
||||
if( eType==SQLITE_INTEGER ){
|
||||
i = (u64)sqlite3_value_int64(pValue);
|
||||
u64 i = (u64)sqlite3_value_int64(pValue);
|
||||
sessionPutI64(&aBuf[1], i);
|
||||
}else{
|
||||
double r;
|
||||
assert( sizeof(double)==8 && sizeof(u64)==8 );
|
||||
r = sqlite3_value_double(pValue);
|
||||
memcpy(&i, &r, 8);
|
||||
double r = sqlite3_value_double(pValue);
|
||||
sessionPutDouble(&aBuf[1], r);
|
||||
}
|
||||
sessionPutI64(&aBuf[1], i);
|
||||
}
|
||||
nByte = 9;
|
||||
break;
|
||||
@@ -1369,9 +1379,7 @@ static void sessionUpdateOneChange(
|
||||
|
||||
case SQLITE_FLOAT: {
|
||||
double rVal = sqlite3_column_double(pDflt, iField);
|
||||
i64 iVal = 0;
|
||||
memcpy(&iVal, &rVal, sizeof(rVal));
|
||||
sessionPutI64(&pNew->aRecord[pNew->nRecord], iVal);
|
||||
sessionPutDouble(&pNew->aRecord[pNew->nRecord], rVal);
|
||||
pNew->nRecord += 8;
|
||||
break;
|
||||
}
|
||||
@@ -2628,15 +2636,14 @@ static void sessionAppendCol(
|
||||
int eType = sqlite3_column_type(pStmt, iCol);
|
||||
sessionAppendByte(p, (u8)eType, pRc);
|
||||
if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
|
||||
sqlite3_int64 i;
|
||||
u8 aBuf[8];
|
||||
if( eType==SQLITE_INTEGER ){
|
||||
i = sqlite3_column_int64(pStmt, iCol);
|
||||
sqlite3_int64 i = sqlite3_column_int64(pStmt, iCol);
|
||||
sessionPutI64(aBuf, i);
|
||||
}else{
|
||||
double r = sqlite3_column_double(pStmt, iCol);
|
||||
memcpy(&i, &r, 8);
|
||||
sessionPutDouble(aBuf, r);
|
||||
}
|
||||
sessionPutI64(aBuf, i);
|
||||
sessionAppendBlob(p, aBuf, 8, pRc);
|
||||
}
|
||||
if( eType==SQLITE_BLOB || eType==SQLITE_TEXT ){
|
||||
@@ -5677,6 +5684,21 @@ int sqlite3changeset_apply_strm(
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
** The parts of the sqlite3_changegroup structure used by the
|
||||
** sqlite3changegroup_change_xxx() APIs.
|
||||
*/
|
||||
typedef struct ChangeData ChangeData;
|
||||
struct ChangeData {
|
||||
SessionTable *pTab;
|
||||
int bIndirect;
|
||||
int eOp;
|
||||
|
||||
int nBufAlloc;
|
||||
SessionBuffer *aBuf;
|
||||
SessionBuffer record;
|
||||
};
|
||||
|
||||
/*
|
||||
** sqlite3_changegroup handle.
|
||||
*/
|
||||
@@ -5688,6 +5710,7 @@ struct sqlite3_changegroup {
|
||||
|
||||
sqlite3 *db; /* Configured by changegroup_schema() */
|
||||
char *zDb; /* Configured by changegroup_schema() */
|
||||
ChangeData cd; /* Used by changegroup_change_xxx() APIs. */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -5927,15 +5950,14 @@ static int sessionChangesetExtendRecord(
|
||||
switch( eType ){
|
||||
case SQLITE_FLOAT:
|
||||
case SQLITE_INTEGER: {
|
||||
i64 iVal;
|
||||
if( eType==SQLITE_INTEGER ){
|
||||
iVal = sqlite3_column_int64(pTab->pDfltStmt, ii);
|
||||
}else{
|
||||
double rVal = sqlite3_column_int64(pTab->pDfltStmt, ii);
|
||||
memcpy(&iVal, &rVal, sizeof(i64));
|
||||
}
|
||||
if( SQLITE_OK==sessionBufferGrow(pOut, 8, &rc) ){
|
||||
sessionPutI64(&pOut->aBuf[pOut->nBuf], iVal);
|
||||
if( eType==SQLITE_INTEGER ){
|
||||
sqlite3_int64 iVal = sqlite3_column_int64(pTab->pDfltStmt, ii);
|
||||
sessionPutI64(&pOut->aBuf[pOut->nBuf], iVal);
|
||||
}else{
|
||||
double rVal = sqlite3_column_double(pTab->pDfltStmt, ii);
|
||||
sessionPutDouble(&pOut->aBuf[pOut->nBuf], rVal);
|
||||
}
|
||||
pOut->nBuf += 8;
|
||||
}
|
||||
break;
|
||||
@@ -6006,13 +6028,19 @@ static int sessionChangesetFindTable(
|
||||
int nCol = 0;
|
||||
|
||||
*ppTab = 0;
|
||||
sqlite3changeset_pk(pIter, &abPK, &nCol);
|
||||
|
||||
/* Search the list for an existing table */
|
||||
for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){
|
||||
if( 0==sqlite3_strnicmp(pTab->zName, zTab, nTab+1) ) break;
|
||||
}
|
||||
|
||||
|
||||
if( pIter ){
|
||||
sqlite3changeset_pk(pIter, &abPK, &nCol);
|
||||
}else if( !pTab && !pGrp->db ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* If one was not found above, create a new table now */
|
||||
if( !pTab ){
|
||||
SessionTable **ppNew;
|
||||
@@ -6031,8 +6059,8 @@ static int sessionChangesetFindTable(
|
||||
if( pGrp->db ){
|
||||
pTab->nCol = 0;
|
||||
rc = sessionInitTable(0, pTab, pGrp->db, pGrp->zDb);
|
||||
if( rc ){
|
||||
assert( pTab->azCol==0 );
|
||||
if( rc || pTab->nCol==0 ){
|
||||
sqlite3_free(pTab->azCol);
|
||||
sqlite3_free(pTab);
|
||||
return rc;
|
||||
}
|
||||
@@ -6047,7 +6075,7 @@ static int sessionChangesetFindTable(
|
||||
}
|
||||
|
||||
/* Check that the table is compatible. */
|
||||
if( !sessionChangesetCheckCompat(pTab, nCol, abPK) ){
|
||||
if( pIter && !sessionChangesetCheckCompat(pTab, nCol, abPK) ){
|
||||
rc = SQLITE_SCHEMA;
|
||||
}
|
||||
|
||||
@@ -6056,44 +6084,27 @@ static int sessionChangesetFindTable(
|
||||
}
|
||||
|
||||
/*
|
||||
** Add the change currently indicated by iterator pIter to the hash table
|
||||
** belonging to changegroup pGrp.
|
||||
** Add a single change to the changegroup pGrp.
|
||||
*/
|
||||
static int sessionOneChangeToHash(
|
||||
sqlite3_changegroup *pGrp,
|
||||
sqlite3_changeset_iter *pIter,
|
||||
int bRebase
|
||||
sqlite3_changegroup *pGrp, /* Changegroup to update */
|
||||
SessionTable *pTab, /* Table change pertains to */
|
||||
int op, /* One of SQLITE_INSERT, UPDATE, DELETE */
|
||||
int bIndirect, /* True to flag change as "indirect" */
|
||||
int nCol, /* Number of columns in record(s) */
|
||||
u8 *aRec, /* Serialized change record(s) */
|
||||
int nRec, /* Size of aRec[] in bytes */
|
||||
int bRebase /* True if this is a rebase blob */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
int nCol = 0;
|
||||
int op = 0;
|
||||
int iHash = 0;
|
||||
int bIndirect = 0;
|
||||
SessionChange *pChange = 0;
|
||||
SessionChange *pExist = 0;
|
||||
SessionChange **pp = 0;
|
||||
SessionTable *pTab = 0;
|
||||
u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2];
|
||||
int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2;
|
||||
|
||||
assert( nRec>0 );
|
||||
|
||||
/* Ensure that only changesets, or only patchsets, but not a mixture
|
||||
** of both, are being combined. It is an error to try to combine a
|
||||
** changeset and a patchset. */
|
||||
if( pGrp->pList==0 ){
|
||||
pGrp->bPatch = pIter->bPatchset;
|
||||
}else if( pIter->bPatchset!=pGrp->bPatch ){
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
const char *zTab = 0;
|
||||
sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
|
||||
rc = sessionChangesetFindTable(pGrp, zTab, pIter, &pTab);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && nCol<pTab->nCol ){
|
||||
if( nCol<pTab->nCol ){
|
||||
SessionBuffer *pBuf = &pGrp->rec;
|
||||
rc = sessionChangesetExtendRecord(pGrp, pTab, nCol, op, aRec, nRec, pBuf);
|
||||
aRec = pBuf->aBuf;
|
||||
@@ -6101,7 +6112,7 @@ static int sessionOneChangeToHash(
|
||||
assert( pGrp->db );
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && sessionGrowHash(0, pIter->bPatchset, pTab) ){
|
||||
if( rc==SQLITE_OK && sessionGrowHash(0, pGrp->bPatch, pTab) ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
@@ -6109,12 +6120,12 @@ static int sessionOneChangeToHash(
|
||||
/* Search for existing entry. If found, remove it from the hash table.
|
||||
** Code below may link it back in. */
|
||||
iHash = sessionChangeHash(
|
||||
pTab, (pIter->bPatchset && op==SQLITE_DELETE), aRec, pTab->nChange
|
||||
pTab, (pGrp->bPatch && op==SQLITE_DELETE), aRec, pTab->nChange
|
||||
);
|
||||
for(pp=&pTab->apChange[iHash]; *pp; pp=&(*pp)->pNext){
|
||||
int bPkOnly1 = 0;
|
||||
int bPkOnly2 = 0;
|
||||
if( pIter->bPatchset ){
|
||||
if( pGrp->bPatch ){
|
||||
bPkOnly1 = (*pp)->op==SQLITE_DELETE;
|
||||
bPkOnly2 = op==SQLITE_DELETE;
|
||||
}
|
||||
@@ -6129,7 +6140,7 @@ static int sessionOneChangeToHash(
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sessionChangeMerge(pTab, bRebase,
|
||||
pIter->bPatchset, pExist, op, bIndirect, aRec, nRec, &pChange
|
||||
pGrp->bPatch, pExist, op, bIndirect, aRec, nRec, &pChange
|
||||
);
|
||||
}
|
||||
if( rc==SQLITE_OK && pChange ){
|
||||
@@ -6138,6 +6149,47 @@ static int sessionOneChangeToHash(
|
||||
pTab->nEntry++;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Add the change currently indicated by iterator pIter to the hash table
|
||||
** belonging to changegroup pGrp.
|
||||
*/
|
||||
static int sessionOneChangeIterToHash(
|
||||
sqlite3_changegroup *pGrp,
|
||||
sqlite3_changeset_iter *pIter,
|
||||
int bRebase
|
||||
){
|
||||
u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2];
|
||||
int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2;
|
||||
const char *zTab = 0;
|
||||
int nCol = 0;
|
||||
int op = 0;
|
||||
int bIndirect = 0;
|
||||
int rc = SQLITE_OK;
|
||||
SessionTable *pTab = 0;
|
||||
|
||||
/* Ensure that only changesets, or only patchsets, but not a mixture
|
||||
** of both, are being combined. It is an error to try to combine a
|
||||
** changeset and a patchset. */
|
||||
if( pGrp->pList==0 ){
|
||||
pGrp->bPatch = pIter->bPatchset;
|
||||
}else if( pIter->bPatchset!=pGrp->bPatch ){
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
|
||||
rc = sessionChangesetFindTable(pGrp, zTab, pIter, &pTab);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sessionOneChangeToHash(
|
||||
pGrp, pTab, op, bIndirect, nCol, aRec, nRec, bRebase
|
||||
);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ) rc = pIter->rc;
|
||||
return rc;
|
||||
}
|
||||
@@ -6157,7 +6209,7 @@ static int sessionChangesetToHash(
|
||||
|
||||
pIter->in.bNoDiscard = 1;
|
||||
while( SQLITE_ROW==(sessionChangesetNext(pIter, &aRec, &nRec, 0)) ){
|
||||
rc = sessionOneChangeToHash(pGrp, pIter, bRebase);
|
||||
rc = sessionOneChangeIterToHash(pGrp, pIter, bRebase);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
}
|
||||
|
||||
@@ -6247,6 +6299,33 @@ int sqlite3changegroup_new(sqlite3_changegroup **pp){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Configure a changegroup object.
|
||||
*/
|
||||
int sqlite3changegroup_config(
|
||||
sqlite3_changegroup *pGrp,
|
||||
int op,
|
||||
void *pArg
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
switch( op ){
|
||||
case SQLITE_CHANGEGROUP_CONFIG_PATCHSET: {
|
||||
int arg = *(int*)pArg;
|
||||
if( pGrp->pList==0 && arg>=0 ){
|
||||
pGrp->bPatch = (arg>0);
|
||||
}
|
||||
*(int*)pArg = pGrp->bPatch;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
rc = SQLITE_MISUSE;
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Provide a database schema to the changegroup object.
|
||||
*/
|
||||
@@ -6305,7 +6384,7 @@ int sqlite3changegroup_add_change(
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
pIter->in.bNoDiscard = 1;
|
||||
rc = sessionOneChangeToHash(pGrp, pIter, 0);
|
||||
rc = sessionOneChangeIterToHash(pGrp, pIter, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -6357,6 +6436,12 @@ int sqlite3changegroup_output_strm(
|
||||
*/
|
||||
void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){
|
||||
if( pGrp ){
|
||||
int ii;
|
||||
for(ii=0; ii<pGrp->cd.nBufAlloc; ii++){
|
||||
sqlite3_free(pGrp->cd.aBuf[ii].aBuf);
|
||||
}
|
||||
sqlite3_free(pGrp->cd.record.aBuf);
|
||||
sqlite3_free(pGrp->cd.aBuf);
|
||||
sqlite3_free(pGrp->zDb);
|
||||
sessionDeleteTable(0, pGrp->pList);
|
||||
sqlite3_free(pGrp->rec.aBuf);
|
||||
@@ -6787,4 +6872,326 @@ int sqlite3session_config(int op, void *pArg){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Begin adding a change to a changegroup object.
|
||||
*/
|
||||
int sqlite3changegroup_change_begin(
|
||||
sqlite3_changegroup *pGrp,
|
||||
int eOp,
|
||||
const char *zTab,
|
||||
int bIndirect,
|
||||
char **pzErr
|
||||
){
|
||||
SessionTable *pTab = 0;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
if( pGrp->cd.pTab ){
|
||||
rc = SQLITE_MISUSE;
|
||||
}else if( eOp!=SQLITE_INSERT && eOp!=SQLITE_UPDATE && eOp!=SQLITE_DELETE ){
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
rc = sessionChangesetFindTable(pGrp, zTab, 0, &pTab);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
if( pTab==0 ){
|
||||
if( pzErr ){
|
||||
*pzErr = sqlite3_mprintf("no such table: %s", zTab);
|
||||
}
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
int nReq = pTab->nCol * (eOp==SQLITE_UPDATE ? 2 : 1);
|
||||
pGrp->cd.pTab = pTab;
|
||||
pGrp->cd.eOp = eOp;
|
||||
pGrp->cd.bIndirect = bIndirect;
|
||||
|
||||
if( pGrp->cd.nBufAlloc<nReq ){
|
||||
SessionBuffer *aBuf = (SessionBuffer*)sqlite3_realloc(
|
||||
pGrp->cd.aBuf, nReq * sizeof(SessionBuffer)
|
||||
);
|
||||
if( aBuf==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
memset(&aBuf[pGrp->cd.nBufAlloc], 0,
|
||||
sizeof(SessionBuffer) * (nReq - pGrp->cd.nBufAlloc)
|
||||
);
|
||||
pGrp->cd.aBuf = aBuf;
|
||||
pGrp->cd.nBufAlloc = nReq;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
{
|
||||
/* Assert that all column values are currently undefined */
|
||||
int ii;
|
||||
for(ii=0; ii<pGrp->cd.nBufAlloc; ii++){
|
||||
assert( pGrp->cd.aBuf[ii].nBuf==0 );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function does processing common to the _change_int64(), _change_text()
|
||||
** and other similar APIs.
|
||||
*/
|
||||
static int checkChangeParams(
|
||||
sqlite3_changegroup *pGrp,
|
||||
int bNew,
|
||||
int iCol,
|
||||
sqlite3_int64 nReq,
|
||||
SessionBuffer **ppBuf
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
if( pGrp->cd.pTab==0 ){
|
||||
rc = SQLITE_MISUSE;
|
||||
}else if( iCol<0 || iCol>=pGrp->cd.pTab->nCol ){
|
||||
rc = SQLITE_RANGE;
|
||||
}else if(
|
||||
(bNew && pGrp->cd.eOp==SQLITE_DELETE)
|
||||
|| (!bNew && pGrp->cd.eOp==SQLITE_INSERT)
|
||||
){
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
SessionBuffer *pBuf = &pGrp->cd.aBuf[iCol];
|
||||
if( pGrp->cd.eOp==SQLITE_UPDATE && bNew ){
|
||||
pBuf += pGrp->cd.pTab->nCol;
|
||||
}
|
||||
pBuf->nBuf = 0;
|
||||
sessionBufferGrow(pBuf, nReq, &rc);
|
||||
pBuf->nBuf = nReq;
|
||||
*ppBuf = pBuf;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Configure the change currently under construction with an integer value.
|
||||
*/
|
||||
int sqlite3changegroup_change_int64(
|
||||
sqlite3_changegroup *pGrp,
|
||||
int bNew,
|
||||
int iCol,
|
||||
sqlite3_int64 iVal
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SessionBuffer *pBuf = 0;
|
||||
|
||||
if( SQLITE_OK!=(rc = checkChangeParams(pGrp, bNew, iCol, 9, &pBuf)) ){
|
||||
return rc;
|
||||
}
|
||||
|
||||
pBuf->aBuf[0] = SQLITE_INTEGER;
|
||||
sessionPutI64(&pBuf->aBuf[1], iVal);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Configure the change currently under construction with a null value.
|
||||
*/
|
||||
int sqlite3changegroup_change_null(
|
||||
sqlite3_changegroup *pGrp,
|
||||
int bNew,
|
||||
int iCol
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SessionBuffer *pBuf = 0;
|
||||
|
||||
if( SQLITE_OK!=(rc = checkChangeParams(pGrp, bNew, iCol, 1, &pBuf)) ){
|
||||
return rc;
|
||||
}
|
||||
|
||||
pBuf->aBuf[0] = SQLITE_NULL;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Configure the change currently under construction with a real value.
|
||||
*/
|
||||
int sqlite3changegroup_change_double(
|
||||
sqlite3_changegroup *pGrp,
|
||||
int bNew,
|
||||
int iCol,
|
||||
double fVal
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SessionBuffer *pBuf = 0;
|
||||
|
||||
if( SQLITE_OK!=(rc = checkChangeParams(pGrp, bNew, iCol, 9, &pBuf)) ){
|
||||
return rc;
|
||||
}
|
||||
|
||||
pBuf->aBuf[0] = SQLITE_FLOAT;
|
||||
sessionPutDouble(&pBuf->aBuf[1], fVal);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Configure the change currently under construction with a text value.
|
||||
*/
|
||||
int sqlite3changegroup_change_text(
|
||||
sqlite3_changegroup *pGrp,
|
||||
int bNew,
|
||||
int iCol,
|
||||
const char *pVal,
|
||||
int nVal
|
||||
){
|
||||
int nText = nVal>=0 ? nVal : strlen(pVal);
|
||||
sqlite3_int64 nByte = 1 + sessionVarintLen(nText) + nText;
|
||||
int rc = SQLITE_OK;
|
||||
SessionBuffer *pBuf = 0;
|
||||
|
||||
if( SQLITE_OK!=(rc = checkChangeParams(pGrp, bNew, iCol, nByte, &pBuf)) ){
|
||||
return rc;
|
||||
}
|
||||
|
||||
pBuf->aBuf[0] = SQLITE_TEXT;
|
||||
pBuf->nBuf = (1 + sessionVarintPut(&pBuf->aBuf[1], nText));
|
||||
memcpy(&pBuf->aBuf[pBuf->nBuf], pVal, nText);
|
||||
pBuf->nBuf += nText;
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Configure the change currently under construction with a blob value.
|
||||
*/
|
||||
int sqlite3changegroup_change_blob(
|
||||
sqlite3_changegroup *pGrp,
|
||||
int bNew,
|
||||
int iCol,
|
||||
const void *pVal,
|
||||
int nVal
|
||||
){
|
||||
sqlite3_int64 nByte = 1 + sessionVarintLen(nVal) + nVal;
|
||||
int rc = SQLITE_OK;
|
||||
SessionBuffer *pBuf = 0;
|
||||
|
||||
if( SQLITE_OK!=(rc = checkChangeParams(pGrp, bNew, iCol, nByte, &pBuf)) ){
|
||||
return rc;
|
||||
}
|
||||
|
||||
pBuf->aBuf[0] = SQLITE_BLOB;
|
||||
pBuf->nBuf = (1 + sessionVarintPut(&pBuf->aBuf[1], nVal));
|
||||
memcpy(&pBuf->aBuf[pBuf->nBuf], pVal, nVal);
|
||||
pBuf->nBuf += nVal;
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Finish any change currently being constructed by the changegroup object.
|
||||
*/
|
||||
int sqlite3changegroup_change_finish(
|
||||
sqlite3_changegroup *pGrp,
|
||||
int bDiscard,
|
||||
char **pzErr
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
if( pGrp->cd.pTab ){
|
||||
SessionBuffer *aBuf = pGrp->cd.aBuf;
|
||||
int ii;
|
||||
|
||||
if( bDiscard==0 ){
|
||||
int nBuf = pGrp->cd.pTab->nCol;
|
||||
u8 eUndef = SQLITE_NULL;
|
||||
if( pGrp->cd.eOp==SQLITE_UPDATE ){
|
||||
for(ii=0; ii<nBuf; ii++){
|
||||
if( pGrp->cd.pTab->abPK[ii] ){
|
||||
if( aBuf[ii].nBuf<=1 ){
|
||||
*pzErr = sqlite3_mprintf(
|
||||
"invalid change: %s value in PK of old.* record",
|
||||
aBuf[ii].nBuf==1 ? "null" : "undefined"
|
||||
);
|
||||
rc = SQLITE_ERROR;
|
||||
break;
|
||||
}else if( aBuf[ii + nBuf].nBuf>0 ){
|
||||
*pzErr = sqlite3_mprintf(
|
||||
"invalid change: defined value in PK of new.* record"
|
||||
);
|
||||
rc = SQLITE_ERROR;
|
||||
break;
|
||||
}
|
||||
}else
|
||||
if( pGrp->bPatch==0 && (aBuf[ii].nBuf>0)!=(aBuf[ii+nBuf].nBuf>0) ){
|
||||
*pzErr = sqlite3_mprintf(
|
||||
"invalid change: column %d "
|
||||
"- old.* value is %sdefined but new.* is %sdefined",
|
||||
ii, aBuf[ii].nBuf ? "" : "un", aBuf[ii+nBuf].nBuf ? "" : "un"
|
||||
);
|
||||
rc = SQLITE_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
eUndef = 0x00;
|
||||
if( pGrp->bPatch==0 ) nBuf = nBuf * 2;
|
||||
}else{
|
||||
for(ii=0; ii<nBuf; ii++){
|
||||
int isPK = pGrp->cd.pTab->abPK[ii];
|
||||
if( (pGrp->cd.eOp==SQLITE_INSERT || pGrp->bPatch==0 || isPK)
|
||||
&& aBuf[ii].nBuf==0
|
||||
){
|
||||
*pzErr = sqlite3_mprintf(
|
||||
"invalid change: column %d is undefined", ii
|
||||
);
|
||||
rc = SQLITE_ERROR;
|
||||
break;
|
||||
}
|
||||
if( aBuf[ii].nBuf==1 && isPK ){
|
||||
*pzErr = sqlite3_mprintf(
|
||||
"invalid change: null value in PK"
|
||||
);
|
||||
rc = SQLITE_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pGrp->cd.record.nBuf = 0;
|
||||
for(ii=0; ii<nBuf; ii++){
|
||||
SessionBuffer *p = &pGrp->cd.aBuf[ii];
|
||||
if( pGrp->bPatch ){
|
||||
if( pGrp->cd.pTab->abPK[ii]==0 ){
|
||||
if( pGrp->cd.eOp==SQLITE_UPDATE ){
|
||||
p += pGrp->cd.pTab->nCol;
|
||||
}else if( pGrp->cd.eOp==SQLITE_DELETE ){
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( 0==sessionBufferGrow(&pGrp->cd.record, p->nBuf?p->nBuf:1, &rc) ){
|
||||
if( p->nBuf ){
|
||||
memcpy(&pGrp->cd.record.aBuf[pGrp->cd.record.nBuf],p->aBuf,p->nBuf);
|
||||
pGrp->cd.record.nBuf += p->nBuf;
|
||||
}else{
|
||||
pGrp->cd.record.aBuf[pGrp->cd.record.nBuf++] = eUndef;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sessionOneChangeToHash(
|
||||
pGrp, pGrp->cd.pTab,
|
||||
pGrp->cd.eOp, pGrp->cd.bIndirect, pGrp->cd.pTab->nCol,
|
||||
pGrp->cd.record.aBuf, pGrp->cd.record.nBuf, 0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset all aBuf[] entries to "undefined". */
|
||||
{
|
||||
int nZero = pGrp->cd.pTab->nCol;
|
||||
if( pGrp->cd.eOp==SQLITE_UPDATE ) nZero += nZero;
|
||||
for(ii=0; ii<nZero; ii++){
|
||||
pGrp->cd.aBuf[ii].nBuf = 0;
|
||||
}
|
||||
}
|
||||
pGrp->cd.pTab = 0;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */
|
||||
|
||||
@@ -1853,6 +1853,220 @@ int sqlite3session_config(int op, void *pArg);
|
||||
*/
|
||||
#define SQLITE_SESSION_CONFIG_STRMSIZE 1
|
||||
|
||||
/*
|
||||
** CAPI3REF: Configure a changegroup object
|
||||
**
|
||||
** Configure the changegroup object passed as the first argument.
|
||||
** At present the only valid value for the second parameter is
|
||||
** [SQLITE_CHANGEGROUP_CONFIG_PATCHSET].
|
||||
*/
|
||||
int sqlite3changegroup_config(sqlite3_changegroup*, int, void *pArg);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Options for sqlite3changegroup_config().
|
||||
**
|
||||
** The following values may be passed as the 2nd parameter to
|
||||
** sqlite3changegroup_config().
|
||||
**
|
||||
** <dt>SQLITE_CHANGEGROUP_CONFIG_PATCHSET <dd>
|
||||
** A changegroup object generates either a changeset or patchset. Usually,
|
||||
** this is determined by whether the first call to sqlite3changegroup_add()
|
||||
** is passed a changeset or a patchset. Or, if the first changes are added
|
||||
** to the changegroup object using the sqlite3changegroup_change_xxx()
|
||||
** APIs, then this option may be used to configure whether the changegroup
|
||||
** object generates a changeset or patchset.
|
||||
**
|
||||
** When this option is invoked, parameter pArg must point to a value of
|
||||
** type int. If the changegroup currently contains zero changes, and the
|
||||
** value of the int variable is zero or greater than zero, then the
|
||||
** changegroup is configured to generate a changeset or patchset,
|
||||
** respectively. It is a no-op, not an error, if the changegroup is not
|
||||
** configured because it has already started accumulating changes.
|
||||
**
|
||||
** Before returning, the int variable is set to 0 if the changegroup is
|
||||
** configured to generate a changeset, or 1 if it is configured to generate
|
||||
** a patchset.
|
||||
*/
|
||||
#define SQLITE_CHANGEGROUP_CONFIG_PATCHSET 1
|
||||
|
||||
|
||||
/*
|
||||
** CAPI3REF: Begin adding a change to a changegroup
|
||||
**
|
||||
** This API is used, in concert with other sqlite3changegroup_change_xxx()
|
||||
** APIs, to add changes to a changegroup object one at a time. To add a
|
||||
** single change, the caller must:
|
||||
**
|
||||
** 1. Invoke sqlite3changegroup_change_begin() to indicate the type of
|
||||
** change (INSERT, UPDATE or DELETE), the affected table and whether
|
||||
** or not the change should be marked as indirect.
|
||||
**
|
||||
** 2. Invoke sqlite3changegroup_change_int64() or one of the other four
|
||||
** value functions - _null(), _double(), _text() or _blob() - one or
|
||||
** more times to specify old.* and new.* values for the change being
|
||||
** constructed.
|
||||
**
|
||||
** 3. Invoke sqlite3changegroup_change_finish() to either finish adding
|
||||
** the change to the group, or to discard the change altogether.
|
||||
**
|
||||
** The first argument to this function must be a pointer to the existing
|
||||
** changegroup object that the change will be added to. The second argument
|
||||
** must be SQLITE_INSERT, SQLITE_UPDATE or SQLITE_DELETE. The third is the
|
||||
** name of the table that the change affects, and the fourth is a boolean
|
||||
** flag specifying whether the change should be marked as "indirect" (if
|
||||
** bIndirect is non-zero) or not indirect (if bIndirect is zero).
|
||||
**
|
||||
** Following a successful call to this function, this function may not be
|
||||
** called again on the same changegroup object until after
|
||||
** sqlite3changegroup_change_finish() has been called. Doing so is an
|
||||
** SQLITE_MISUSE error.
|
||||
**
|
||||
** The changegroup object passed as the first argument must be already
|
||||
** configured with schema data for the specified table. It may be configured
|
||||
** either by calling sqlite3changegroup_schema() with a database that contains
|
||||
** the table, or sqlite3changegroup_add() with a changeset that contains the
|
||||
** table. If the changegroup object has not been configured with a schema for
|
||||
** the specified table when this function is called, SQLITE_ERROR is returned.
|
||||
**
|
||||
** If successful, SQLITE_OK is returned. Otherwise, if an error occurs, an
|
||||
** SQLite error code is returned. In this case, if argument pzErr is non-NULL,
|
||||
** then (*pzErr) may be set to point to a buffer containing a utf-8 formated,
|
||||
** nul-terminated, English language error message. It is the responsibility
|
||||
** of the caller to eventually free this buffer using sqlite3_free().
|
||||
*/
|
||||
int sqlite3changegroup_change_begin(
|
||||
sqlite3_changegroup*,
|
||||
int eOp,
|
||||
const char *zTab,
|
||||
int bIndirect,
|
||||
char **pzErr
|
||||
);
|
||||
|
||||
/*
|
||||
** This function may only be called between a successful call to
|
||||
** sqlite3changegroup_change_begin() and its matching
|
||||
** sqlite3changegroup_change_finish() call. If it is called at any
|
||||
** other time, it is an SQLITE_MISUSE error. Calling this function
|
||||
** specifies a 64-bit integer value to be used in the change currently being
|
||||
** added to the changegroup object passed as the first argument.
|
||||
**
|
||||
** The second parameter, bNew, specifies whether the value is to be part of
|
||||
** the new.* (if bNew is non-zero) or old.* (if bNew is zero) record of
|
||||
** the change under construction. If this does not match the type of change
|
||||
** specified by the preceding call to sqlite3changegroup_change_begin() (i.e.
|
||||
** an old.* value for an SQLITE_INSERT change, or a new.* value for an
|
||||
** SQLITE_DELETE), then SQLITE_ERROR is returned.
|
||||
**
|
||||
** The third parameter specifies the column of the old.* or new.* record that
|
||||
** the value will be a part of. If the specified table has an explicit primary
|
||||
** key, then this is the index of the table column, numbered from 0 in the order
|
||||
** specified within the CREATE TABLE statement. Or, if the table uses an
|
||||
** implicit rowid key, then the column 0 is the rowid and the explicit columns
|
||||
** are numbered starting from 1. If the iCol parameter is less than 0 or greater
|
||||
** than the index of the last column in the table, SQLITE_RANGE is returned.
|
||||
**
|
||||
** The fourth parameter is the integer value to use as part of the old.* or
|
||||
** new.* record.
|
||||
**
|
||||
** If this call is successful, SQLITE_OK is returned. Otherwise, if an
|
||||
** error occurs, an SQLite error code is returned.
|
||||
*/
|
||||
int sqlite3changegroup_change_int64(
|
||||
sqlite3_changegroup*,
|
||||
int bNew,
|
||||
int iCol,
|
||||
sqlite3_int64 iVal
|
||||
);
|
||||
|
||||
/*
|
||||
** This function is similar to sqlite3changegroup_change_int64(). Except that
|
||||
** it configures the change currently under construction with a NULL value
|
||||
** instead of a 64-bit integer.
|
||||
*/
|
||||
int sqlite3changegroup_change_null(sqlite3_changegroup*, int, int);
|
||||
|
||||
/*
|
||||
** This function is similar to sqlite3changegroup_change_int64(). Except that
|
||||
** it configures the change currently being constructed with a real value
|
||||
** instead of a 64-bit integer.
|
||||
*/
|
||||
int sqlite3changegroup_change_double(sqlite3_changegroup*, int, int, double);
|
||||
|
||||
/*
|
||||
** This function is similar to sqlite3changegroup_change_int64(). It configures
|
||||
** the currently accumulated change with a text value instead of a 64-bit
|
||||
** integer. Parameter pVal points to a buffer containing the text encoded using
|
||||
** utf-8. Parameter nVal may either be the size of the text value in bytes, or
|
||||
** else a negative value, in which case the buffer pVal points to is assumed to
|
||||
** be nul-terminated.
|
||||
*/
|
||||
int sqlite3changegroup_change_text(
|
||||
sqlite3_changegroup*, int, int, const char *pVal, int nVal
|
||||
);
|
||||
|
||||
/*
|
||||
** This function is similar to sqlite3changegroup_change_int64(). It configures
|
||||
** the currently accumulated change with a blob value instead of a 64-bit
|
||||
** integer. Parameter pVal points to a buffer containing the blob. Parameter
|
||||
** nVal is the size of the blob in bytes.
|
||||
*/
|
||||
int sqlite3changegroup_change_blob(
|
||||
sqlite3_changegroup*, int, int, const void *pVal, int nVal
|
||||
);
|
||||
|
||||
/*
|
||||
** This function may only be called following a successful call to
|
||||
** sqlite3changegroup_change_begin(). Otherwise, it is an SQLITE_MISUSE error.
|
||||
**
|
||||
** If parameter bDiscard is non-zero, then the current change is simply
|
||||
** discarded. In this case this function is always successful and SQLITE_OK
|
||||
** returned.
|
||||
**
|
||||
** If parameter bDiscard is zero, then an attempt is made to add the current
|
||||
** change to the changegroup. Assuming the changegroup is configured to
|
||||
** produce a changeset (not a patchset), this requires that:
|
||||
**
|
||||
** * If the change is an INSERT or DELETE, then a value must be specified
|
||||
** for all columns of the new.* or old.* record, respectively.
|
||||
**
|
||||
** * If the change is an UPDATE record, then values must be provided for
|
||||
** the PRIMARY KEY columns of the old.* record, but must not be provided
|
||||
** for PRIMARY KEY columns of the new.* record.
|
||||
**
|
||||
** * If the change is an UPDATE record, then for each non-PRIMARY KEY
|
||||
** column in the old.* record for which a value has been provided, a
|
||||
** value must also be provided for the same column in the new.* record.
|
||||
** Similarly, for each non-PK column in the old.* record for which
|
||||
** a value is not provided, a value must not be provided for the same
|
||||
** column in the new.* record.
|
||||
**
|
||||
** * All values specified for PRIMARY KEY columns must be non-NULL.
|
||||
**
|
||||
** Otherwise, it is an error.
|
||||
**
|
||||
** If the changegroup already contains a change for the same row (identified
|
||||
** by PRIMARY KEY columns), then the current change is combined with the
|
||||
** existing change in the same way as for sqlite3changegroup_add().
|
||||
**
|
||||
** For a patchset, all of the above rules apply except that it doesn't matter
|
||||
** whether or not values are provided for the non-PK old.* record columns
|
||||
** for an UPDATE or DELETE change. This means that code used to produce
|
||||
** a changeset using the sqlite3changegroup_change_xxx() APIs may also
|
||||
** be used to produce patchsets.
|
||||
**
|
||||
** If the call is successful, SQLITE_OK is returned. Otherwise, if an error
|
||||
** occurs, an SQLite error code is returned. If an error is returned and
|
||||
** parameter pzErr is not NULL, then (*pzErr) may be set to point to a buffer
|
||||
** containing a nul-terminated, utf-8 encoded, English language error message.
|
||||
** It is the responsibility of the caller to eventually free any such error
|
||||
** message buffer using sqlite3_free().
|
||||
*/
|
||||
int sqlite3changegroup_change_finish(
|
||||
sqlite3_changegroup*,
|
||||
int bDiscard,
|
||||
char **pzErr
|
||||
);
|
||||
|
||||
/*
|
||||
** Make sure we can call this stuff from C++.
|
||||
*/
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
typedef unsigned char u8;
|
||||
#endif
|
||||
|
||||
extern const char *sqlite3ErrName(int);
|
||||
|
||||
typedef struct TestSession TestSession;
|
||||
struct TestSession {
|
||||
sqlite3_session *pSession;
|
||||
@@ -397,7 +399,6 @@ static int SQLITE_TCLAPI test_session_cmd(
|
||||
}
|
||||
rc = sqlite3session_object_config(pSession, aOpt[iOpt].opt, &iArg);
|
||||
if( rc!=SQLITE_OK ){
|
||||
extern const char *sqlite3ErrName(int);
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
|
||||
}else{
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(iArg));
|
||||
@@ -1554,6 +1555,14 @@ static void test_changegroup_del(void *clientData){
|
||||
ckfree(pGrp);
|
||||
}
|
||||
|
||||
static int testGetNewOrOld(Tcl_Interp *interp, Tcl_Obj *pObj, int *pbNew){
|
||||
const char *azVal[] = { "old", "new", 0 };
|
||||
int iIdx = 0;
|
||||
int rc = Tcl_GetIndexFromObj(interp, pObj, azVal, "record", 0, &iIdx);
|
||||
*pbNew = iIdx;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Tclcmd: $changegroup schema DB DBNAME
|
||||
** Tclcmd: $changegroup add CHANGESET
|
||||
@@ -1571,14 +1580,25 @@ static int SQLITE_TCLAPI test_changegroup_cmd(
|
||||
const char *zSub;
|
||||
int nArg;
|
||||
const char *zMsg;
|
||||
int iSub;
|
||||
} aSub[] = {
|
||||
{ "schema", 2, "DB DBNAME", }, /* 0 */
|
||||
{ "add", 1, "CHANGESET", }, /* 1 */
|
||||
{ "output", 0, "", }, /* 2 */
|
||||
{ "delete", 0, "", }, /* 3 */
|
||||
{ "add_change", 1, "ITERATOR", }, /* 4 */
|
||||
{ 0 }
|
||||
{ "schema", 2, "DB DBNAME" }, /* 0 */
|
||||
{ "add", 1, "CHANGESET" }, /* 1 */
|
||||
{ "output", 0, "" }, /* 2 */
|
||||
{ "delete", 0, "" }, /* 3 */
|
||||
{ "add_change", 1, "ITERATOR" }, /* 4 */
|
||||
|
||||
{ "change_begin", 3, "TYPE TABLE INDIRECT" }, /* 5 */
|
||||
{ "change_int64", 3, "[new|old] ICOL VALUE" }, /* 6 */
|
||||
{ "change_null", 2, "[new|old] ICOL" }, /* 7 */
|
||||
{ "change_double", 3, "[new|old] ICOL VALUE" }, /* 8 */
|
||||
{ "change_text", 3, "[new|old] ICOL VALUE" }, /* 9 */
|
||||
{ "change_blob", 3, "[new|old] ICOL VALUE" }, /* 10 */
|
||||
{ "change_finish", 1, "BDISCARD" }, /* 11 */
|
||||
|
||||
{ "config", 2, "OPTION INTVAL" }, /* 12 */
|
||||
{ "change_text-1", 3, "[new|old] ICOL VALUE" }, /* 13 */
|
||||
{ "change_begin_ne", 3, "TYPE TABLE INDIRECT" }, /* 14 */
|
||||
{ 0, 0, 0 }
|
||||
};
|
||||
int rc = TCL_OK;
|
||||
int iSub = 0;
|
||||
@@ -1647,6 +1667,193 @@ static int SQLITE_TCLAPI test_changegroup_cmd(
|
||||
break;
|
||||
};
|
||||
|
||||
case 14: /* change_beginne */
|
||||
case 5: { /* change_begin */
|
||||
struct ChangeType {
|
||||
const char *zType;
|
||||
int eType;
|
||||
} aType[] = {
|
||||
{ "INSERT", SQLITE_INSERT },
|
||||
{ "UPDATE", SQLITE_UPDATE },
|
||||
{ "DELETE", SQLITE_DELETE },
|
||||
{ 0, 0 }
|
||||
};
|
||||
int eType = 0;
|
||||
const char *zTab = 0;
|
||||
int bIndirect;
|
||||
int iIdx = 0;
|
||||
char *zErr = 0;
|
||||
char **pz = ((iSub==5) ? &zErr : 0);
|
||||
|
||||
if( TCL_OK!=Tcl_GetIntFromObj(0, objv[2], &eType) ){
|
||||
rc = Tcl_GetIndexFromObjStruct(
|
||||
interp, objv[2], aType, sizeof(aType[0]), "TYPE", 0, &iIdx
|
||||
);
|
||||
if( rc!=TCL_OK ) return rc;
|
||||
eType = aType[iIdx].eType;
|
||||
}
|
||||
zTab = Tcl_GetString(objv[3]);
|
||||
if( Tcl_GetBooleanFromObj(interp, objv[4], &bIndirect) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
rc = sqlite3changegroup_change_begin(p->pGrp, eType, zTab, bIndirect, pz);
|
||||
assert( zErr==0 || rc!=SQLITE_OK );
|
||||
if( rc!=SQLITE_OK ){
|
||||
rc = test_session_error(interp, rc, zErr);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 6: { /* change_int64 */
|
||||
int bNew = 0;
|
||||
int iCol = 0;
|
||||
sqlite3_int64 iVal = 0;
|
||||
if( TCL_OK!=testGetNewOrOld(interp, objv[2], &bNew)
|
||||
|| TCL_OK!=Tcl_GetIntFromObj(interp, objv[3], &iCol)
|
||||
|| TCL_OK!=Tcl_GetWideIntFromObj(interp, objv[4], &iVal)
|
||||
){
|
||||
rc = TCL_ERROR;
|
||||
}else{
|
||||
rc = sqlite3changegroup_change_int64(p->pGrp, bNew, iCol, iVal);
|
||||
if( rc!=SQLITE_OK ){
|
||||
rc = test_session_error(interp, rc, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 7: { /* change_null */
|
||||
int bNew = 0;
|
||||
int iCol = 0;
|
||||
if( TCL_OK!=testGetNewOrOld(interp, objv[2], &bNew)
|
||||
|| TCL_OK!=Tcl_GetIntFromObj(interp, objv[3], &iCol)
|
||||
){
|
||||
rc = TCL_ERROR;
|
||||
}else{
|
||||
rc = sqlite3changegroup_change_null(p->pGrp, bNew, iCol);
|
||||
if( rc!=SQLITE_OK ){
|
||||
rc = test_session_error(interp, rc, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 8: { /* change_double */
|
||||
int bNew = 0;
|
||||
int iCol = 0;
|
||||
double rVal = 0;
|
||||
if( TCL_OK!=testGetNewOrOld(interp, objv[2], &bNew)
|
||||
|| TCL_OK!=Tcl_GetIntFromObj(interp, objv[3], &iCol)
|
||||
|| TCL_OK!=Tcl_GetDoubleFromObj(interp, objv[4], &rVal)
|
||||
){
|
||||
rc = TCL_ERROR;
|
||||
}else{
|
||||
rc = sqlite3changegroup_change_double(p->pGrp, bNew, iCol, rVal);
|
||||
if( rc!=SQLITE_OK ){
|
||||
rc = test_session_error(interp, rc, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 9: { /* change_text */
|
||||
int bNew = 0;
|
||||
int iCol = 0;
|
||||
if( TCL_OK!=testGetNewOrOld(interp, objv[2], &bNew)
|
||||
|| TCL_OK!=Tcl_GetIntFromObj(interp, objv[3], &iCol)
|
||||
){
|
||||
rc = TCL_ERROR;
|
||||
}else{
|
||||
int nVal = 0;
|
||||
const char *pVal = Tcl_GetStringFromObj(objv[4], &nVal);
|
||||
rc = sqlite3changegroup_change_text(p->pGrp, bNew, iCol, pVal, nVal);
|
||||
if( rc!=SQLITE_OK ){
|
||||
rc = test_session_error(interp, rc, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 10: { /* change_blob */
|
||||
int bNew = 0;
|
||||
int iCol = 0;
|
||||
if( TCL_OK!=testGetNewOrOld(interp, objv[2], &bNew)
|
||||
|| TCL_OK!=Tcl_GetIntFromObj(interp, objv[3], &iCol)
|
||||
){
|
||||
rc = TCL_ERROR;
|
||||
}else{
|
||||
int nVal = 0;
|
||||
const u8 *pVal = Tcl_GetByteArrayFromObj(objv[4], &nVal);
|
||||
rc = sqlite3changegroup_change_blob(p->pGrp, bNew, iCol, pVal, nVal);
|
||||
if( rc!=SQLITE_OK ){
|
||||
rc = test_session_error(interp, rc, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 11: { /* change_finish */
|
||||
int bDiscard = 0;
|
||||
if( TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[2], &bDiscard) ){
|
||||
rc = TCL_ERROR;
|
||||
}else{
|
||||
char *zErr = 0;
|
||||
rc = sqlite3changegroup_change_finish(p->pGrp, bDiscard, &zErr);
|
||||
if( rc!=SQLITE_OK ){
|
||||
rc = test_session_error(interp, rc, zErr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 12: { /* config */
|
||||
struct OptionName {
|
||||
const char *zOpt;
|
||||
int op;
|
||||
} aOp[] = {
|
||||
{ "patchset", SQLITE_CHANGEGROUP_CONFIG_PATCHSET },
|
||||
{ 0, 0 }
|
||||
};
|
||||
int iIdx = 0;
|
||||
int iArg = 0;
|
||||
rc = Tcl_GetIndexFromObjStruct(
|
||||
interp, objv[2], aOp, sizeof(aOp[0]), "option", 0, &iIdx
|
||||
);
|
||||
if( rc==TCL_OK
|
||||
&& (rc = Tcl_GetIntFromObj(interp, objv[3], &iArg))==TCL_OK
|
||||
){
|
||||
int op = aOp[iIdx].op;
|
||||
void *pArg = (void*)&iArg;
|
||||
|
||||
rc = sqlite3changegroup_config(p->pGrp, op, pArg);
|
||||
if( rc!=SQLITE_OK ){
|
||||
rc = test_session_error(interp, rc, 0);
|
||||
}else{
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(iArg));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 13: { /* change_text-1 */
|
||||
int bNew = 0;
|
||||
int iCol = 0;
|
||||
if( TCL_OK!=testGetNewOrOld(interp, objv[2], &bNew)
|
||||
|| TCL_OK!=Tcl_GetIntFromObj(interp, objv[3], &iCol)
|
||||
){
|
||||
rc = TCL_ERROR;
|
||||
}else{
|
||||
const char *pVal = Tcl_GetString(objv[4]);
|
||||
rc = sqlite3changegroup_change_text(p->pGrp, bNew, iCol, pVal, -1);
|
||||
if( rc!=SQLITE_OK ){
|
||||
rc = test_session_error(interp, rc, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: { /* delete */
|
||||
assert( iSub==3 );
|
||||
Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
|
||||
|
||||
22
manifest
22
manifest
@@ -1,5 +1,5 @@
|
||||
C Turn\san\sincorrect\sassert()\sinto\sa\stestcase().\s\sFix\sover-length\scode\slines\nin\sthe\ssqlite.h.in\ssource\sfile.\s\sFix\sthe\sdocumentation\son\sOOM\sconditions\nin\sthe\ssqlite3_value_text()\sfamily\sof\sAPIs.
|
||||
D 2026-03-31T10:32:23.251
|
||||
C Add\san\sAPI\sto\sthe\ssessions\smodule\sto\sadd\schanges\sone\sat\sa\stime\sto\san\ssqlite3_changegroup\sobject.
|
||||
D 2026-03-31T10:59:13.118
|
||||
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
@@ -555,11 +555,12 @@ F ext/session/sessionat.test 00c8badb35e43a2f12a716d2734a44d614ff62361979b6b8541
|
||||
F ext/session/sessionbig.test 47c381e7acfabeef17d98519a3080d69151723354d220afa2053852182ca7adf
|
||||
F ext/session/sessionblob.test 87faf667870b72f08e91969abd9f52a383ab7b514506ee194d64a39d8faff00a
|
||||
F ext/session/sessionchange.test 6618cb1c1338a4b6df173b6ac42d09623fb71269962abf23ebb7617fe9f45a50
|
||||
F ext/session/sessionchange2.test 6b5b7e3d1cc4ede43817f7fb68e3771aac4aa6500adb21a458b3e5a9fd841f83
|
||||
F ext/session/sessionconflict.test 19e4a53795c4c930bfec49e809311e09b2a9e202d9446e56d7a8b139046a0c07 x
|
||||
F ext/session/sessiondiff.test e89f7aedcdd89e5ebac3a455224eb553a171e9586fc3e1e6a7b3388d2648ba8d
|
||||
F ext/session/sessionfault.test c2b43d01213b389a3f518e90775fca2120812ba51e50444c4066962263e45c11
|
||||
F ext/session/sessionfault2.test b0d6a7c1d7398a7e800d84657404909c7d385965ea8576dc79ed344c46fbf41c
|
||||
F ext/session/sessionfault3.test ce0b5d182133935c224d72507dbf1c5be1a1febf7e85d0b0fbd6d2f724b32b96
|
||||
F ext/session/sessionfault3.test 9397819ec25b0960c5bc03c78613f9cb5cacc970f83e817aec1775c2a839a787
|
||||
F ext/session/sessioninvert.test 9018f6a7387ac745084b6374c5e1aa14d648b372e6e1181cfab3df632b662d26 x
|
||||
F ext/session/sessionmem.test f2a735db84a3e9e19f571033b725b0b2daf847f3f28b1da55a0c1a4e74f1de09
|
||||
F ext/session/sessionnoact.test 4c7ae5c7d351cb5323bca62b6b095592ad24bd90a6713c178b62ab0063d23e19
|
||||
@@ -570,9 +571,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 6ebd02be470f36d41c4bd78927f39d507b62051ba025eacaed9936c769902a07
|
||||
F ext/session/sqlite3session.h 7404723606074fcb2afdc6b72c206072cdb2b7d8ba097ca1559174a80bc26f7a
|
||||
F ext/session/test_session.c 190110e3bd9463717248dec1272b44fe9943e57b7646d0b4200dcf11e4dccee6
|
||||
F ext/session/sqlite3session.c bcec91b7fc7f7acdf7fae6020de5f9131ce91c61b6d9953d5f96fc058f6d25d4
|
||||
F ext/session/sqlite3session.h b117cddb6ce93411b3b25c8a355d155a120e30efbdf506fc6899b71a333b0c56
|
||||
F ext/session/test_session.c beefbb051a2baa2636f463ad1c50558294a418f955219be9423de54dff946f0f
|
||||
F ext/wasm/GNUmakefile 68c750f173106d9d63f12c1edf1256c6f4bad9894b155da5db64322f4912de4b
|
||||
F ext/wasm/README-dist.txt f01081a850ce38a56706af6b481e3a7878e24e42b314cfcd4b129f0f8427066a
|
||||
F ext/wasm/README.md 2e87804e12c98f1d194b7a06162a88441d33bb443efcfe00dc6565a780d2f259
|
||||
@@ -2196,8 +2197,9 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee
|
||||
F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
|
||||
P 240294653ceb202b847699e9691140db4fe623a03d86fa3cf7495f8831576331
|
||||
R 219a9645b2361c943ce9fe72fc39f9f5
|
||||
U drh
|
||||
Z 00514e08c5b641bd2fdcb8859fb87fc8
|
||||
P 934582706109d2ebda26952c7c95f7a5095fa47ec837570a66c991866f7d08cf 716e62e3d1d6ff96f6e4906175818439103e9534e89bec4a86776265a04954b9
|
||||
R aa5223b212f1ad46f0f376c6a9330268
|
||||
T +closed 716e62e3d1d6ff96f6e4906175818439103e9534e89bec4a86776265a04954b9
|
||||
U dan
|
||||
Z 567722104ff813fa7a84013ce4b3243e
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
||||
@@ -1 +1 @@
|
||||
934582706109d2ebda26952c7c95f7a5095fa47ec837570a66c991866f7d08cf
|
||||
cf1122ea6409bcbd341b67ae45147abb8d5cd491ae95491c801d0621181da40b
|
||||
|
||||
Reference in New Issue
Block a user