mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-01 06:27:03 +03:00
Store primary key definitions for modified tables in changesets. Add the sqlite3changeset_pk() API to extract this data from a changeset iterator.
FossilOrigin-Name: 54298ee5ed183d1f1c49524f25e8ae1407f3d4b5
This commit is contained in:
@ -88,14 +88,14 @@ do_test 2.1.1 {
|
||||
execsql { INSERT INTO t1 VALUES(3, 'Thonburi') }
|
||||
} {}
|
||||
do_changeset_test 2.1.2 S {
|
||||
{INSERT t1 0 {} {i 1 t Sukhothai}}
|
||||
{INSERT t1 0 {} {i 2 t Ayutthaya}}
|
||||
{INSERT t1 0 {} {i 3 t Thonburi}}
|
||||
{INSERT t1 0 X. {} {i 1 t Sukhothai}}
|
||||
{INSERT t1 0 X. {} {i 2 t Ayutthaya}}
|
||||
{INSERT t1 0 X. {} {i 3 t Thonburi}}
|
||||
}
|
||||
do_changeset_invert_test 2.1.3 S {
|
||||
{DELETE t1 0 {i 1 t Sukhothai} {}}
|
||||
{DELETE t1 0 {i 2 t Ayutthaya} {}}
|
||||
{DELETE t1 0 {i 3 t Thonburi} {}}
|
||||
{DELETE t1 0 X. {i 1 t Sukhothai} {}}
|
||||
{DELETE t1 0 X. {i 2 t Ayutthaya} {}}
|
||||
{DELETE t1 0 X. {i 3 t Thonburi} {}}
|
||||
}
|
||||
do_test 2.1.4 { S delete } {}
|
||||
|
||||
@ -105,14 +105,14 @@ do_test 2.2.1 {
|
||||
execsql { DELETE FROM t1 WHERE 1 }
|
||||
} {}
|
||||
do_changeset_test 2.2.2 S {
|
||||
{DELETE t1 0 {i 1 t Sukhothai} {}}
|
||||
{DELETE t1 0 {i 2 t Ayutthaya} {}}
|
||||
{DELETE t1 0 {i 3 t Thonburi} {}}
|
||||
{DELETE t1 0 X. {i 1 t Sukhothai} {}}
|
||||
{DELETE t1 0 X. {i 2 t Ayutthaya} {}}
|
||||
{DELETE t1 0 X. {i 3 t Thonburi} {}}
|
||||
}
|
||||
do_changeset_invert_test 2.2.3 S {
|
||||
{INSERT t1 0 {} {i 1 t Sukhothai}}
|
||||
{INSERT t1 0 {} {i 2 t Ayutthaya}}
|
||||
{INSERT t1 0 {} {i 3 t Thonburi}}
|
||||
{INSERT t1 0 X. {} {i 1 t Sukhothai}}
|
||||
{INSERT t1 0 X. {} {i 2 t Ayutthaya}}
|
||||
{INSERT t1 0 X. {} {i 3 t Thonburi}}
|
||||
}
|
||||
do_test 2.2.4 { S delete } {}
|
||||
|
||||
@ -131,19 +131,19 @@ do_test 2.3.1 {
|
||||
} {}
|
||||
|
||||
do_changeset_test 2.3.2 S {
|
||||
{INSERT t1 0 {} {i 10 t Sukhothai}}
|
||||
{DELETE t1 0 {i 1 t Sukhothai} {}}
|
||||
{UPDATE t1 0 {i 2 t Ayutthaya} {{} {} t Surin}}
|
||||
{DELETE t1 0 {i 3 t Thonburi} {}}
|
||||
{INSERT t1 0 {} {i 20 t Thapae}}
|
||||
{INSERT t1 0 X. {} {i 10 t Sukhothai}}
|
||||
{DELETE t1 0 X. {i 1 t Sukhothai} {}}
|
||||
{UPDATE t1 0 X. {i 2 t Ayutthaya} {{} {} t Surin}}
|
||||
{DELETE t1 0 X. {i 3 t Thonburi} {}}
|
||||
{INSERT t1 0 X. {} {i 20 t Thapae}}
|
||||
}
|
||||
|
||||
do_changeset_invert_test 2.3.3 S {
|
||||
{DELETE t1 0 {i 10 t Sukhothai} {}}
|
||||
{INSERT t1 0 {} {i 1 t Sukhothai}}
|
||||
{UPDATE t1 0 {{} {} t Surin} {i 2 t Ayutthaya}}
|
||||
{INSERT t1 0 {} {i 3 t Thonburi}}
|
||||
{DELETE t1 0 {i 20 t Thapae} {}}
|
||||
{DELETE t1 0 X. {i 10 t Sukhothai} {}}
|
||||
{INSERT t1 0 X. {} {i 1 t Sukhothai}}
|
||||
{UPDATE t1 0 X. {{} {} t Surin} {i 2 t Ayutthaya}}
|
||||
{INSERT t1 0 X. {} {i 3 t Thonburi}}
|
||||
{DELETE t1 0 X. {i 20 t Thapae} {}}
|
||||
}
|
||||
do_test 2.3.4 { S delete } {}
|
||||
|
||||
|
@ -41,13 +41,13 @@ do_iterator_test 1.1 t1 {
|
||||
DELETE FROM t1 WHERE a = 'i';
|
||||
INSERT INTO t1 VALUES('ii', 'two');
|
||||
} {
|
||||
{DELETE t1 0 {t i t one} {}}
|
||||
{INSERT t1 0 {} {t ii t two}}
|
||||
{DELETE t1 0 X. {t i t one} {}}
|
||||
{INSERT t1 0 X. {} {t ii t two}}
|
||||
}
|
||||
do_iterator_test 1.2 t1 {
|
||||
INSERT INTO t1 VALUES(1.5, 99.9)
|
||||
} {
|
||||
{INSERT t1 0 {} {f 1.5 f 99.9}}
|
||||
{INSERT t1 0 X. {} {f 1.5 f 99.9}}
|
||||
}
|
||||
|
||||
|
||||
@ -228,15 +228,15 @@ foreach {tn sql changeset} {
|
||||
INSERT INTO t1 VALUES(NULL);
|
||||
INSERT INTO t1 VALUES(456);
|
||||
} {
|
||||
{INSERT t1 0 {} {i 456}}
|
||||
{INSERT t1 0 {} {i 123}}
|
||||
{INSERT t1 0 X {} {i 456}}
|
||||
{INSERT t1 0 X {} {i 123}}
|
||||
}
|
||||
|
||||
2 {
|
||||
UPDATE t1 SET a = NULL;
|
||||
} {
|
||||
{DELETE t1 0 {i 456} {}}
|
||||
{DELETE t1 0 {i 123} {}}
|
||||
{DELETE t1 0 X {i 456} {}}
|
||||
{DELETE t1 0 X {i 123} {}}
|
||||
}
|
||||
|
||||
3 { DELETE FROM t1 } { }
|
||||
@ -244,14 +244,14 @@ foreach {tn sql changeset} {
|
||||
4 {
|
||||
INSERT INTO t3 VALUES(NULL, NULL)
|
||||
} {
|
||||
{INSERT t3 0 {} {n {} i 1}}
|
||||
{INSERT t3 0 .X {} {n {} i 1}}
|
||||
}
|
||||
|
||||
5 { INSERT INTO t2 VALUES(1, 2, NULL) } { }
|
||||
6 { INSERT INTO t2 VALUES(1, NULL, 3) } { }
|
||||
7 { INSERT INTO t2 VALUES(1, NULL, NULL) } { }
|
||||
8 { INSERT INTO t2 VALUES(1, 2, 3) } { {INSERT t2 0 {} {i 1 i 2 i 3}} }
|
||||
9 { DELETE FROM t2 WHERE 1 } { {DELETE t2 0 {i 1 i 2 i 3} {}} }
|
||||
8 { INSERT INTO t2 VALUES(1, 2, 3) } { {INSERT t2 0 .XX {} {i 1 i 2 i 3}} }
|
||||
9 { DELETE FROM t2 WHERE 1 } { {DELETE t2 0 .XX {i 1 i 2 i 3} {}} }
|
||||
|
||||
} {
|
||||
do_iterator_test 4.$tn {t1 t2 t3} $sql $changeset
|
||||
@ -269,14 +269,14 @@ do_execsql_test 5.0 {
|
||||
}
|
||||
|
||||
foreach {tn sql changeset} {
|
||||
1 { INSERT INTO t1 VALUES(35) } { {INSERT t1 0 {} {i 35}} }
|
||||
2 { INSERT INTO t2 VALUES(36, 37) } { {INSERT t2 0 {} {i 36 i 37}} }
|
||||
1 { INSERT INTO t1 VALUES(35) } { {INSERT t1 0 X {} {i 35}} }
|
||||
2 { INSERT INTO t2 VALUES(36, 37) } { {INSERT t2 0 .X {} {i 36 i 37}} }
|
||||
3 {
|
||||
DELETE FROM t1 WHERE 1;
|
||||
UPDATE t2 SET x = 34;
|
||||
} {
|
||||
{UPDATE t2 0 {i 36 i 37} {i 34 {} {}}}
|
||||
{DELETE t1 0 {i 35} {}}
|
||||
{UPDATE t2 0 .X {i 36 i 37} {i 34 {} {}}}
|
||||
{DELETE t1 0 X {i 35} {}}
|
||||
}
|
||||
} {
|
||||
do_iterator_test 5.$tn * $sql $changeset
|
||||
@ -314,9 +314,9 @@ do_iterator_test 6.1.1 * {
|
||||
SELECT indirect(0);
|
||||
INSERT INTO t1 VALUES(3, 'three', 'iii');
|
||||
} {
|
||||
{INSERT t1 0 {} {i 1 t one t i}}
|
||||
{INSERT t1 1 {} {i 2 t two t ii}}
|
||||
{INSERT t1 0 {} {i 3 t three t iii}}
|
||||
{INSERT t1 0 X.. {} {i 1 t one t i}}
|
||||
{INSERT t1 1 X.. {} {i 2 t two t ii}}
|
||||
{INSERT t1 0 X.. {} {i 3 t three t iii}}
|
||||
}
|
||||
|
||||
do_iterator_test 6.1.2 * {
|
||||
@ -324,7 +324,7 @@ do_iterator_test 6.1.2 * {
|
||||
UPDATE t1 SET c = 'I' WHERE a = 1;
|
||||
SELECT indirect(0);
|
||||
} {
|
||||
{UPDATE t1 1 {i 1 {} {} t i} {{} {} {} {} t I}}
|
||||
{UPDATE t1 1 X.. {i 1 {} {} t i} {{} {} {} {} t I}}
|
||||
}
|
||||
do_iterator_test 6.1.3 * {
|
||||
SELECT indirect(1);
|
||||
@ -332,7 +332,7 @@ do_iterator_test 6.1.3 * {
|
||||
SELECT indirect(0);
|
||||
UPDATE t1 SET c = 'o' WHERE a = 1;
|
||||
} {
|
||||
{UPDATE t1 0 {i 1 {} {} t I} {{} {} {} {} t o}}
|
||||
{UPDATE t1 0 X.. {i 1 {} {} t I} {{} {} {} {} t o}}
|
||||
}
|
||||
do_iterator_test 6.1.4 * {
|
||||
SELECT indirect(0);
|
||||
@ -340,7 +340,7 @@ do_iterator_test 6.1.4 * {
|
||||
SELECT indirect(1);
|
||||
UPDATE t1 SET c = 'i' WHERE a = 1;
|
||||
} {
|
||||
{UPDATE t1 0 {i 1 {} {} t o} {{} {} {} {} t i}}
|
||||
{UPDATE t1 0 X.. {i 1 {} {} t o} {{} {} {} {} t i}}
|
||||
}
|
||||
do_iterator_test 6.1.4 * {
|
||||
SELECT indirect(1);
|
||||
@ -348,14 +348,14 @@ do_iterator_test 6.1.4 * {
|
||||
SELECT indirect(1);
|
||||
UPDATE t1 SET c = 'I' WHERE a = 1;
|
||||
} {
|
||||
{UPDATE t1 1 {i 1 {} {} t i} {{} {} {} {} t I}}
|
||||
{UPDATE t1 1 X.. {i 1 {} {} t i} {{} {} {} {} t I}}
|
||||
}
|
||||
|
||||
do_iterator_test 6.1.5 * {
|
||||
INSERT INTO t2 VALUES(1, 'x');
|
||||
} {
|
||||
{INSERT t2 0 {} {i 1 t x}}
|
||||
{INSERT t2 1 {} {i 2 n {}}}
|
||||
{INSERT t2 0 X. {} {i 1 t x}}
|
||||
{INSERT t2 1 X. {} {i 2 n {}}}
|
||||
}
|
||||
|
||||
do_iterator_test 6.1.6 * {
|
||||
@ -364,8 +364,8 @@ do_iterator_test 6.1.6 * {
|
||||
SELECT indirect(0);
|
||||
UPDATE t2 SET y = 'y' WHERE x>2;
|
||||
} {
|
||||
{INSERT t2 0 {} {i 3 t y}}
|
||||
{INSERT t2 0 {} {i 4 t y}}
|
||||
{INSERT t2 0 X. {} {i 3 t y}}
|
||||
{INSERT t2 0 X. {} {i 4 t y}}
|
||||
}
|
||||
|
||||
do_iterator_test 6.1.7 * {
|
||||
@ -374,7 +374,7 @@ do_iterator_test 6.1.7 * {
|
||||
SELECT indirect(0);
|
||||
INSERT INTO t2 VALUES(4, 'new');
|
||||
} {
|
||||
{UPDATE t2 0 {i 4 t y} {{} {} t new}}
|
||||
{UPDATE t2 0 X. {i 4 t y} {{} {} t new}}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
@ -243,9 +243,9 @@ do_faultsim_test 5 -faults oom* -body {
|
||||
set x [list]
|
||||
sqlite3session_foreach c $::inverse { lappend x $c }
|
||||
foreach c {
|
||||
{DELETE t1 0 {t xxx t yyy} {}}
|
||||
{INSERT t1 0 {} {t string i 1}}
|
||||
{UPDATE t1 0 {i 20 {} {}} {i 4 i 2}}
|
||||
{DELETE t1 0 .X {t xxx t yyy} {}}
|
||||
{INSERT t1 0 .X {} {t string i 1}}
|
||||
{UPDATE t1 0 .X {i 20 {} {}} {i 4 i 2}}
|
||||
} { lappend y $c }
|
||||
if {$x != $y} { error "changeset no good" }
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ struct sqlite3_changeset_iter {
|
||||
int nCol; /* Number of columns in zTab */
|
||||
int op; /* Current operation */
|
||||
int bIndirect; /* True if current change was indirect */
|
||||
u8 *abPK; /* Primary key array */
|
||||
sqlite3_value **apValue; /* old.* and new.* values */
|
||||
};
|
||||
|
||||
@ -1357,6 +1358,7 @@ int sqlite3session_changeset(
|
||||
/* Write a table header */
|
||||
sessionAppendByte(&buf, 'T', &rc);
|
||||
sessionAppendVarint(&buf, nCol, &rc);
|
||||
sessionAppendBlob(&buf, pTab->abPK, nCol, &rc);
|
||||
sessionAppendBlob(&buf, (u8 *)zName, strlen(zName)+1, &rc);
|
||||
|
||||
/* Build and compile a statement to execute: */
|
||||
@ -1575,6 +1577,8 @@ int sqlite3changeset_next(sqlite3_changeset_iter *p){
|
||||
if( c=='T' ){
|
||||
int nByte; /* Bytes to allocate for apValue */
|
||||
aChange += sessionVarintGet(aChange, &p->nCol);
|
||||
p->abPK = (u8 *)aChange;
|
||||
aChange += p->nCol;
|
||||
p->zTab = (char *)aChange;
|
||||
aChange += (strlen((char *)aChange) + 1);
|
||||
p->op = *(aChange++);
|
||||
@ -1611,7 +1615,7 @@ int sqlite3changeset_next(sqlite3_changeset_iter *p){
|
||||
}
|
||||
|
||||
/*
|
||||
** The following three functions extract information on the current change
|
||||
** The following function extracts information on the current change
|
||||
** from a changeset iterator. They may only be called after changeset_next()
|
||||
** has returned SQLITE_ROW.
|
||||
*/
|
||||
@ -1629,6 +1633,16 @@ int sqlite3changeset_op(
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
int sqlite3changeset_pk(
|
||||
sqlite3_changeset_iter *pIter, /* Iterator object */
|
||||
unsigned char **pabPK, /* OUT: Array of boolean - true for PK cols */
|
||||
int *pnCol /* OUT: Number of entries in output array */
|
||||
){
|
||||
*pabPK = pIter->abPK;
|
||||
if( pnCol ) *pnCol = pIter->nCol;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function may only be called while the iterator is pointing to an
|
||||
** SQLITE_UPDATE or SQLITE_DELETE change (see sqlite3changeset_op()).
|
||||
@ -1764,7 +1778,15 @@ int sqlite3changeset_invert(
|
||||
u8 eType = aIn[i];
|
||||
switch( eType ){
|
||||
case 'T': {
|
||||
/* A 'table' record consists of:
|
||||
**
|
||||
** * A constant 'T' character,
|
||||
** * Number of columns in said table (a varint),
|
||||
** * An array of nCol bytes (abPK),
|
||||
** * A nul-terminated table name.
|
||||
*/
|
||||
int nByte = 1 + sessionVarintGet(&aIn[i+1], &nCol);
|
||||
nByte += nCol;
|
||||
nByte += 1 + strlen((char *)&aIn[i+nByte]);
|
||||
memcpy(&aOut[i], &aIn[i], nByte);
|
||||
i += nByte;
|
||||
|
@ -341,6 +341,37 @@ int sqlite3changeset_op(
|
||||
int *pbIndirect /* OUT: True for an 'indirect' change */
|
||||
);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Obtain The Primary Key Definition Of A Table
|
||||
**
|
||||
** For each modified table, a changeset includes the following:
|
||||
**
|
||||
** <ul>
|
||||
** <li> The number of columns in the table, and
|
||||
** <li> Which of those columns make up the tables PRIMARY KEY.
|
||||
** </ul>
|
||||
**
|
||||
** This function is used to find which columns comprise the PRIMARY KEY of
|
||||
** the table modified by the change that iterator pIter currently points to.
|
||||
** If successful, *pabPK is set to point to an array of nCol entries, where
|
||||
** nCol is the number of columns in the table. Elements of *pabPK are set to
|
||||
** 0x01 if the corresponding column is part of the tables primary key, or
|
||||
** 0x00 if it is not.
|
||||
**
|
||||
** If argumet pnCol is not NULL, then *pnCol is set to the number of columns
|
||||
** in the table.
|
||||
**
|
||||
** If this function is called when the iterator does not point to a valid
|
||||
** entry, SQLITE_MISUSE is returned and the output variables zeroed. Otherwise,
|
||||
** SQLITE_OK is returned and the output variables populated as described
|
||||
** above.
|
||||
*/
|
||||
int sqlite3changeset_pk(
|
||||
sqlite3_changeset_iter *pIter, /* Iterator object */
|
||||
unsigned char **pabPK, /* OUT: Array of boolean - true for PK cols */
|
||||
int *pnCol /* OUT: Number of entries in output array */
|
||||
);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Obtain old.* Values From A Changeset Iterator
|
||||
**
|
||||
|
@ -408,6 +408,10 @@ static int test_sqlite3session_foreach(
|
||||
Tcl_Obj *pNew; /* Vector of new.* values */
|
||||
int bIndirect;
|
||||
|
||||
char *zPK;
|
||||
unsigned char *abPK;
|
||||
int i;
|
||||
|
||||
sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
|
||||
pVar = Tcl_NewObj();
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(
|
||||
@ -415,9 +419,19 @@ static int test_sqlite3session_foreach(
|
||||
op==SQLITE_UPDATE ? "UPDATE" :
|
||||
"DELETE", -1
|
||||
));
|
||||
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zTab, -1));
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewBooleanObj(bIndirect));
|
||||
|
||||
zPK = ckalloc(nCol+1);
|
||||
memset(zPK, 0, nCol+1);
|
||||
sqlite3changeset_pk(pIter, &abPK, 0);
|
||||
for(i=0; i<nCol; i++){
|
||||
zPK[i] = (abPK[i] ? 'X' : '.');
|
||||
}
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zPK, -1));
|
||||
ckfree(zPK);
|
||||
|
||||
pOld = Tcl_NewObj();
|
||||
if( op!=SQLITE_INSERT ){
|
||||
int i;
|
||||
|
Reference in New Issue
Block a user