From 244593c8460bf9ab836921643a1ae94154c90989 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 24 Mar 2011 11:22:59 +0000 Subject: [PATCH] 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 --- ext/session/session1.test | 44 ++++++++++++++--------------- ext/session/session2.test | 52 +++++++++++++++++------------------ ext/session/sessionfault.test | 6 ++-- ext/session/sqlite3session.c | 24 +++++++++++++++- ext/session/sqlite3session.h | 31 +++++++++++++++++++++ ext/session/test_session.c | 14 ++++++++++ manifest | 34 ++++++++--------------- manifest.uuid | 2 +- 8 files changed, 132 insertions(+), 75 deletions(-) diff --git a/ext/session/session1.test b/ext/session/session1.test index 582f802bea..4a2b50e6c6 100644 --- a/ext/session/session1.test +++ b/ext/session/session1.test @@ -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 } {} diff --git a/ext/session/session2.test b/ext/session/session2.test index d6cb284659..a4776d80e5 100644 --- a/ext/session/session2.test +++ b/ext/session/session2.test @@ -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 diff --git a/ext/session/sessionfault.test b/ext/session/sessionfault.test index 04ac8b8b88..e1c2980b79 100644 --- a/ext/session/sessionfault.test +++ b/ext/session/sessionfault.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" } } diff --git a/ext/session/sqlite3session.c b/ext/session/sqlite3session.c index ff28487d3c..4ab8bb32ce 100644 --- a/ext/session/sqlite3session.c +++ b/ext/session/sqlite3session.c @@ -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; diff --git a/ext/session/sqlite3session.h b/ext/session/sqlite3session.h index ee0402bffa..b6ea72522d 100644 --- a/ext/session/sqlite3session.h +++ b/ext/session/sqlite3session.h @@ -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: +** +** +** +** 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 ** diff --git a/ext/session/test_session.c b/ext/session/test_session.c index fee52d7f9c..e3d6d60c22 100644 --- a/ext/session/test_session.c +++ b/ext/session/test_session.c @@ -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