Add the "indirect flag" to the changeset blob format. Also the sqlite3session_indirect() API.

FossilOrigin-Name: 1feaf2d35fd9ec777319717ae2c2929d66fe7baa
This commit is contained in:
dan 2011-03-23 16:03:11 +00:00
parent 1e7a2d4315
commit b4480e942f
8 changed files with 260 additions and 85 deletions

View File

@ -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 {} {i 1 t Sukhothai}}
{INSERT t1 {} {i 2 t Ayutthaya}}
{INSERT t1 {} {i 3 t Thonburi}}
{INSERT t1 0 {} {i 1 t Sukhothai}}
{INSERT t1 0 {} {i 2 t Ayutthaya}}
{INSERT t1 0 {} {i 3 t Thonburi}}
}
do_changeset_invert_test 2.1.3 S {
{DELETE t1 {i 1 t Sukhothai} {}}
{DELETE t1 {i 2 t Ayutthaya} {}}
{DELETE t1 {i 3 t Thonburi} {}}
{DELETE t1 0 {i 1 t Sukhothai} {}}
{DELETE t1 0 {i 2 t Ayutthaya} {}}
{DELETE t1 0 {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 {i 1 t Sukhothai} {}}
{DELETE t1 {i 2 t Ayutthaya} {}}
{DELETE t1 {i 3 t Thonburi} {}}
{DELETE t1 0 {i 1 t Sukhothai} {}}
{DELETE t1 0 {i 2 t Ayutthaya} {}}
{DELETE t1 0 {i 3 t Thonburi} {}}
}
do_changeset_invert_test 2.2.3 S {
{INSERT t1 {} {i 1 t Sukhothai}}
{INSERT t1 {} {i 2 t Ayutthaya}}
{INSERT t1 {} {i 3 t Thonburi}}
{INSERT t1 0 {} {i 1 t Sukhothai}}
{INSERT t1 0 {} {i 2 t Ayutthaya}}
{INSERT t1 0 {} {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 {} {i 10 t Sukhothai}}
{DELETE t1 {i 1 t Sukhothai} {}}
{UPDATE t1 {i 2 t Ayutthaya} {{} {} t Surin}}
{DELETE t1 {i 3 t Thonburi} {}}
{INSERT t1 {} {i 20 t Thapae}}
{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}}
}
do_changeset_invert_test 2.3.3 S {
{DELETE t1 {i 10 t Sukhothai} {}}
{INSERT t1 {} {i 1 t Sukhothai}}
{UPDATE t1 {{} {} t Surin} {i 2 t Ayutthaya}}
{INSERT t1 {} {i 3 t Thonburi}}
{DELETE t1 {i 20 t Thapae} {}}
{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} {}}
}
do_test 2.3.4 { S delete } {}

View File

@ -41,13 +41,13 @@ do_iterator_test 1.1 t1 {
DELETE FROM t1 WHERE a = 'i';
INSERT INTO t1 VALUES('ii', 'two');
} {
{DELETE t1 {t i t one} {}}
{INSERT t1 {} {t ii t two}}
{DELETE t1 0 {t i t one} {}}
{INSERT t1 0 {} {t ii t two}}
}
do_iterator_test 1.2 t1 {
INSERT INTO t1 VALUES(1.5, 99.9)
} {
{INSERT t1 {} {f 1.5 f 99.9}}
{INSERT t1 0 {} {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 {} {i 456}}
{INSERT t1 {} {i 123}}
{INSERT t1 0 {} {i 456}}
{INSERT t1 0 {} {i 123}}
}
2 {
UPDATE t1 SET a = NULL;
} {
{DELETE t1 {i 456} {}}
{DELETE t1 {i 123} {}}
{DELETE t1 0 {i 456} {}}
{DELETE t1 0 {i 123} {}}
}
3 { DELETE FROM t1 } { }
@ -244,14 +244,14 @@ foreach {tn sql changeset} {
4 {
INSERT INTO t3 VALUES(NULL, NULL)
} {
{INSERT t3 {} {n {} i 1}}
{INSERT t3 0 {} {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 {} {i 1 i 2 i 3}} }
9 { DELETE FROM t2 WHERE 1 } { {DELETE t2 {i 1 i 2 i 3} {}} }
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} {}} }
} {
do_iterator_test 4.$tn {t1 t2 t3} $sql $changeset
@ -269,18 +269,112 @@ do_execsql_test 5.0 {
}
foreach {tn sql changeset} {
1 { INSERT INTO t1 VALUES(35) } { {INSERT t1 {} {i 35}} }
2 { INSERT INTO t2 VALUES(36, 37) } { {INSERT t2 {} {i 36 i 37}} }
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}} }
3 {
DELETE FROM t1 WHERE 1;
UPDATE t2 SET x = 34;
} {
{UPDATE t2 {i 36 i 37} {i 34 {} {}}}
{DELETE t1 {i 35} {}}
{UPDATE t2 0 {i 36 i 37} {i 34 {} {}}}
{DELETE t1 0 {i 35} {}}
}
} {
do_iterator_test 5.$tn * $sql $changeset
}
finish_test
#-------------------------------------------------------------------------
# The next block of tests verify that the "indirect" flag is set
# correctly within changesets. The indirect flag is set for a change
# if either of the following are true:
#
# * The sqlite3session_indirect() API has been used to set the session
# indirect flag to true, or
# * The change was made by a trigger.
#
# If the same row is updated more than once during a session, then the
# change is considered indirect only if all changes meet the criteria
# above.
#
test_reset
db function indirect [list S indirect]
do_execsql_test 6.0 {
CREATE TABLE t1(a PRIMARY KEY, b, c);
CREATE TABLE t2(x PRIMARY KEY, y);
CREATE TRIGGER AFTER INSERT ON t2 WHEN new.x%2 BEGIN
INSERT INTO t2 VALUES(new.x+1, NULL);
END;
}
do_iterator_test 6.1.1 * {
INSERT INTO t1 VALUES(1, 'one', 'i');
SELECT indirect(1);
INSERT INTO t1 VALUES(2, 'two', 'ii');
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}}
}
do_iterator_test 6.1.2 * {
SELECT indirect(1);
UPDATE t1 SET c = 'I' WHERE a = 1;
SELECT indirect(0);
} {
{UPDATE t1 1 {i 1 {} {} t i} {{} {} {} {} t I}}
}
do_iterator_test 6.1.3 * {
SELECT indirect(1);
UPDATE t1 SET c = '.' WHERE a = 1;
SELECT indirect(0);
UPDATE t1 SET c = 'o' WHERE a = 1;
} {
{UPDATE t1 0 {i 1 {} {} t I} {{} {} {} {} t o}}
}
do_iterator_test 6.1.4 * {
SELECT indirect(0);
UPDATE t1 SET c = 'x' WHERE a = 1;
SELECT indirect(1);
UPDATE t1 SET c = 'i' WHERE a = 1;
} {
{UPDATE t1 0 {i 1 {} {} t o} {{} {} {} {} t i}}
}
do_iterator_test 6.1.4 * {
SELECT indirect(1);
UPDATE t1 SET c = 'y' WHERE a = 1;
SELECT indirect(1);
UPDATE t1 SET c = 'I' WHERE a = 1;
} {
{UPDATE t1 1 {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 {}}}
}
do_iterator_test 6.1.6 * {
SELECT indirect(1);
INSERT INTO t2 VALUES(3, 'x');
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}}
}
do_iterator_test 6.1.7 * {
SELECT indirect(1);
DELETE FROM t2 WHERE x = 4;
SELECT indirect(0);
INSERT INTO t2 VALUES(4, 'new');
} {
{UPDATE t2 0 {i 4 t y} {{} {} t new}}
}
finish_test

View File

@ -38,7 +38,7 @@ db2 close
# Whereas test 1.2 passes NULL to sqlite3session_attach() to attach all
# tables.
#
do_faultsim_test pagerfault-1.1 -faults oom-* -prep {
do_faultsim_test 1.1 -faults oom-* -prep {
catch {db2 close}
catch {db close}
faultsim_restore_and_reopen
@ -55,7 +55,7 @@ do_faultsim_test pagerfault-1.1 -faults oom-* -prep {
if {$testrc==0} { compare_db db db2 }
}
do_faultsim_test pagerfault-1.2 -faults oom-* -prep {
do_faultsim_test 1.2 -faults oom-* -prep {
catch {db2 close}
catch {db close}
faultsim_restore_and_reopen
@ -82,7 +82,7 @@ do_faultsim_test pagerfault-1.2 -faults oom-* -prep {
}
#-------------------------------------------------------------------------
# The following block of tests - pagerfault-2.* - are designed to check
# The following block of tests - 2.* - are designed to check
# the handling of faults in the sqlite3changeset_apply() function.
#
catch {db close}
@ -110,7 +110,7 @@ foreach {tn conflict_policy sql sql2} {
} {
proc xConflict args [list return $conflict_policy]
do_faultsim_test pagerfault-2.$tn -faults oom-transient -prep {
do_faultsim_test 2.$tn -faults oom-transient -prep {
catch {db2 close}
catch {db close}
faultsim_restore_and_reopen
@ -132,7 +132,7 @@ foreach {tn conflict_policy sql sql2} {
# resizing the session object hash-table from 256 to 512 buckets. This
# is not an error, just a sub-optimal condition.
#
do_faultsim_test pagerfault-3 -faults oom-* -prep {
do_faultsim_test 3 -faults oom-* -prep {
catch {db2 close}
catch {db close}
faultsim_restore_and_reopen
@ -191,7 +191,7 @@ do_test 4.0 {
faultsim_save_and_close
db2 close
do_faultsim_test pagerfault-4 -faults oom-* -prep {
do_faultsim_test 4 -faults oom-* -prep {
catch {db2 close}
catch {db close}
faultsim_restore_and_reopen
@ -234,7 +234,7 @@ set changeset [changeset_from_sql {
}]
db close
do_faultsim_test pagerfault-5 -faults oom* -body {
do_faultsim_test 5 -faults oom* -body {
set ::inverse [sqlite3changeset_invert $::changeset]
set {} {}
} -test {
@ -243,9 +243,9 @@ do_faultsim_test pagerfault-5 -faults oom* -body {
set x [list]
sqlite3session_foreach c $::inverse { lappend x $c }
foreach c {
{DELETE t1 {t xxx t yyy} {}}
{INSERT t1 {} {t string i 1}}
{UPDATE t1 {i 20 {} {}} {i 4 i 2}}
{DELETE t1 0 {t xxx t yyy} {}}
{INSERT t1 0 {} {t string i 1}}
{UPDATE t1 0 {i 20 {} {}} {i 4 i 2}}
} { lappend y $c }
if {$x != $y} { error "changeset no good" }
}

View File

@ -19,6 +19,7 @@ struct sqlite3_session {
sqlite3 *db; /* Database handle session is attached to */
char *zDb; /* Name of database session is attached to */
int bEnable; /* True if currently recording */
int bIndirect; /* True if all changes are indirect */
int bAutoAttach; /* True to auto-attach tables */
int rc; /* Non-zero if an error has occurred */
sqlite3_session *pNext; /* Next session object on same db. */
@ -37,6 +38,7 @@ struct sqlite3_changeset_iter {
char *zTab; /* Current table */
int nCol; /* Number of columns in zTab */
int op; /* Current operation */
int bIndirect; /* True if current change was indirect */
sqlite3_value **apValue; /* old.* and new.* values */
};
@ -131,6 +133,7 @@ struct SessionTable {
*/
struct SessionChange {
int bInsert; /* True if row was inserted this session */
int bIndirect; /* True if this change is "indirect" */
int nRecord; /* Number of bytes in buffer aRecord[] */
u8 *aRecord; /* Buffer containing old.* record */
SessionChange *pNext; /* For hash-table collisions */
@ -660,8 +663,6 @@ static void sessionPreupdateOneChange(
SessionTable *pTab
){
sqlite3 *db = pSession->db;
SessionChange *pChange;
SessionChange *pC;
int iHash;
int bNullPk = 0;
int rc = SQLITE_OK;
@ -679,7 +680,8 @@ static void sessionPreupdateOneChange(
** the hash table. Otherwise, set pChange to NULL.
*/
rc = sessionPreupdateHash(db, pTab, op==SQLITE_INSERT, &iHash, &bNullPk);
if( bNullPk==0 ){
if( rc==SQLITE_OK && bNullPk==0 ){
SessionChange *pC;
for(pC=pTab->apChange[iHash]; rc==SQLITE_OK && pC; pC=pC->pNext){
int bEqual;
rc = sessionPreupdateEqual(db, pTab, pC, op==SQLITE_INSERT, &bEqual);
@ -689,9 +691,11 @@ static void sessionPreupdateOneChange(
/* Create a new change object containing all the old values (if
** this is an SQLITE_UPDATE or SQLITE_DELETE), or just the PK
** values (if this is an INSERT). */
SessionChange *pChange; /* New change object */
int nByte; /* Number of bytes to allocate */
int i; /* Used to iterate through columns */
assert( rc==SQLITE_OK );
pTab->nEntry++;
/* Figure out how large an allocation is required */
@ -732,6 +736,9 @@ static void sessionPreupdateOneChange(
}
if( rc==SQLITE_OK ){
/* Add the change back to the hash-table */
if( pSession->bIndirect || sqlite3_preupdate_depth(pSession->db) ){
pChange->bIndirect = 1;
}
pChange->nRecord = nByte;
pChange->bInsert = (op==SQLITE_INSERT);
pChange->pNext = pTab->apChange[iHash];
@ -739,6 +746,12 @@ static void sessionPreupdateOneChange(
}else{
sqlite3_free(pChange);
}
}else if( rc==SQLITE_OK && pC->bIndirect ){
/* If the existing change is considered "indirect", but this current
** change is "direct", mark the change object as direct. */
if( sqlite3_preupdate_depth(pSession->db)==0 && pSession->bIndirect==0 ){
pC->bIndirect = 0;
}
}
}
@ -1136,6 +1149,7 @@ static void sessionAppendUpdate(
u8 *pCsr = p->aRecord; /* Used to iterate through old.* values */
sessionAppendByte(pBuf, SQLITE_UPDATE, pRc);
sessionAppendByte(pBuf, p->bIndirect, pRc);
for(i=0; i<sqlite3_column_count(pStmt); i++){
int bChanged = 0;
int nAdvance;
@ -1366,6 +1380,7 @@ int sqlite3session_changeset(
int iCol;
if( p->bInsert ){
sessionAppendByte(&buf, SQLITE_INSERT, &rc);
sessionAppendByte(&buf, p->bIndirect, &rc);
for(iCol=0; iCol<nCol; iCol++){
sessionAppendCol(&buf, pSel, iCol, &rc);
}
@ -1375,6 +1390,7 @@ int sqlite3session_changeset(
}else if( !p->bInsert ){
/* A DELETE change */
sessionAppendByte(&buf, SQLITE_DELETE, &rc);
sessionAppendByte(&buf, p->bIndirect, &rc);
sessionAppendBlob(&buf, p->aRecord, p->nRecord, &rc);
}
if( rc==SQLITE_OK ){
@ -1416,6 +1432,20 @@ int sqlite3session_enable(sqlite3_session *pSession, int bEnable){
return ret;
}
/*
** Enable or disable the session object passed as the first argument.
*/
int sqlite3session_indirect(sqlite3_session *pSession, int bIndirect){
int ret;
sqlite3_mutex_enter(sqlite3_db_mutex(pSession->db));
if( bIndirect>=0 ){
pSession->bIndirect = bIndirect;
}
ret = pSession->bIndirect;
sqlite3_mutex_leave(sqlite3_db_mutex(pSession->db));
return ret;
}
/*
** Create an iterator used to iterate through the contents of a changeset.
*/
@ -1548,6 +1578,7 @@ int sqlite3changeset_next(sqlite3_changeset_iter *p){
p->zTab = (char *)aChange;
aChange += (strlen((char *)aChange) + 1);
p->op = *(aChange++);
p->bIndirect = *(aChange++);
sqlite3_free(p->apValue);
nByte = sizeof(sqlite3_value *) * p->nCol * 2;
p->apValue = (sqlite3_value **)sqlite3_malloc(nByte);
@ -1557,6 +1588,7 @@ int sqlite3changeset_next(sqlite3_changeset_iter *p){
memset(p->apValue, 0, sizeof(sqlite3_value*)*p->nCol*2);
}else{
p->op = c;
p->bIndirect = *(aChange++);
}
if( p->op!=SQLITE_UPDATE && p->op!=SQLITE_DELETE && p->op!=SQLITE_INSERT ){
return (p->rc = SQLITE_CORRUPT);
@ -1587,11 +1619,13 @@ int sqlite3changeset_op(
sqlite3_changeset_iter *pIter, /* Iterator handle */
const char **pzTab, /* OUT: Pointer to table name */
int *pnCol, /* OUT: Number of columns in table */
int *pOp /* OUT: SQLITE_INSERT, DELETE or UPDATE */
int *pOp, /* OUT: SQLITE_INSERT, DELETE or UPDATE */
int *pbIndirect /* OUT: True if change is indirect */
){
*pOp = pIter->op;
*pnCol = pIter->nCol;
*pzTab = pIter->zTab;
if( pbIndirect ) *pbIndirect = pIter->bIndirect;
return SQLITE_OK;
}
@ -1740,31 +1774,33 @@ int sqlite3changeset_invert(
case SQLITE_INSERT:
case SQLITE_DELETE: {
int nByte;
u8 *aEnd = &aIn[i+1];
u8 *aEnd = &aIn[i+2];
sessionReadRecord(&aEnd, nCol, 0);
aOut[i] = (eType==SQLITE_DELETE ? SQLITE_INSERT : SQLITE_DELETE);
nByte = aEnd - &aIn[i+1];
memcpy(&aOut[i+1], &aIn[i+1], nByte);
i += 1 + nByte;
aOut[i+1] = aIn[i+1];
nByte = aEnd - &aIn[i+2];
memcpy(&aOut[i+2], &aIn[i+2], nByte);
i += 2 + nByte;
break;
}
case SQLITE_UPDATE: {
int nByte1; /* Size of old.* record in bytes */
int nByte2; /* Size of new.* record in bytes */
u8 *aEnd = &aIn[i+1];
u8 *aEnd = &aIn[i+2];
sessionReadRecord(&aEnd, nCol, 0);
nByte1 = aEnd - &aIn[i+1];
nByte1 = aEnd - &aIn[i+2];
sessionReadRecord(&aEnd, nCol, 0);
nByte2 = aEnd - &aIn[i+1] - nByte1;
nByte2 = aEnd - &aIn[i+2] - nByte1;
aOut[i] = SQLITE_UPDATE;
memcpy(&aOut[i+1], &aIn[i+1+nByte1], nByte2);
memcpy(&aOut[i+1+nByte2], &aIn[i+1], nByte1);
aOut[i+1] = aIn[i+1];
memcpy(&aOut[i+2], &aIn[i+2+nByte1], nByte2);
memcpy(&aOut[i+2+nByte2], &aIn[i+2], nByte1);
i += 1 + nByte1 + nByte2;
i += 2 + nByte1 + nByte2;
break;
}
@ -2097,7 +2133,7 @@ static int sessionSeekToRow(
int op; /* Changset operation (SQLITE_UPDATE etc.) */
const char *zDummy; /* Unused */
sqlite3changeset_op(pIter, &zDummy, &nCol, &op);
sqlite3changeset_op(pIter, &zDummy, &nCol, &op, 0);
rc = sessionBindRow(pIter,
op==SQLITE_INSERT ? sqlite3changeset_new : sqlite3changeset_old,
nCol, abPK, pSelect
@ -2160,7 +2196,7 @@ static int sessionConflictHandler(
int op;
const char *zDummy;
sqlite3changeset_op(pIter, &zDummy, &nCol, &op);
sqlite3changeset_op(pIter, &zDummy, &nCol, &op, 0);
assert( eType==SQLITE_CHANGESET_CONFLICT || eType==SQLITE_CHANGESET_DATA );
assert( SQLITE_CHANGESET_CONFLICT+1==SQLITE_CHANGESET_CONSTRAINT );
@ -2248,7 +2284,7 @@ static int sessionApplyOneOp(
assert( p->azCol && p->abPK );
assert( !pbReplace || *pbReplace==0 );
sqlite3changeset_op(pIter, &zDummy, &nCol, &op);
sqlite3changeset_op(pIter, &zDummy, &nCol, &op, 0);
if( op==SQLITE_DELETE ){
@ -2362,7 +2398,7 @@ int sqlite3changeset_apply(
int bReplace = 0;
int bRetry = 0;
const char *zNew;
sqlite3changeset_op(pIter, &zNew, &nCol, &op);
sqlite3changeset_op(pIter, &zNew, &nCol, &op, 0);
if( zTab==0 || sqlite3_strnicmp(zNew, zTab, nTab+1) ){
sqlite3_free(sApply.azCol);

View File

@ -90,6 +90,35 @@ void sqlite3session_delete(sqlite3_session *pSession);
*/
int sqlite3session_enable(sqlite3_session *pSession, int bEnable);
/*
** CAPI3REF: Set Or Clear the Indirect Change Flag
**
** Each change recorded by a session object is marked as either direct or
** indirect. A change is marked as indirect if either:
**
** <ul>
** <li> The session object "indirect" flag is set when the change is
** made, or
** <li> The change is made by an SQL trigger or foreign key action
** instead of directly as a result of a users SQL statement.
** </ul>
**
** If a single row is affected by more than one operation within a session,
** then the change is considered indirect if all operations meet the criteria
** for an indirect change above, or direct otherwise.
**
** This function is used to set, clear or query the session object indirect
** flag. If the second argument passed to this function is zero, then the
** indirect flag is cleared. If it is greater than zero, the indirect flag
** is set. Passing a value less than zero does not modify the current value
** of the indirect flag, and may be used to query the current state of the
** indirect flag for the specified session object.
**
** The return value indicates the final state of the indirect flag: 0 if
** it is clear, or 1 if it is set.
*/
int sqlite3session_indirect(sqlite3_session *pSession, int bIndirect);
/*
** CAPI3REF: Attach A Table To A Session Object
**
@ -292,10 +321,13 @@ int sqlite3changeset_next(sqlite3_changeset_iter *pIter);
** affected by the current change. The buffer remains valid until either
** sqlite3changeset_next() is called on the iterator or until the
** conflict-handler function returns. If pnCol is not NULL, then *pnCol is
** set to the number of columns in the table affected by the change. Finally,
** if pOp is not NULL, then *pOp is set to one of [SQLITE_INSERT],
** [SQLITE_DELETE] or [SQLITE_UPDATE], depending on the type of change that
** the iterator currently points to.
** set to the number of columns in the table affected by the change. If
** pbIncorrect is not NULL, then *pbIndirect is set to true (1) if the change
** is an indirect change, or false (0) otherwise. See the documentation for
** [sqlite3session_indirect()] for a description of direct and indirect
** changes. Finally, if pOp is not NULL, then *pOp is set to one of
** [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE], depending on the
** type of change that the iterator currently points to.
**
** If no error occurs, SQLITE_OK is returned. If an error does occur, an
** SQLite error code is returned. The values of the output variables may not
@ -305,7 +337,8 @@ int sqlite3changeset_op(
sqlite3_changeset_iter *pIter, /* Iterator object */
const char **pzTab, /* OUT: Pointer to table name */
int *pnCol, /* OUT: Number of columns in table */
int *pOp /* OUT: SQLITE_INSERT, DELETE or UPDATE */
int *pOp, /* OUT: SQLITE_INSERT, DELETE or UPDATE */
int *pbIndirect /* OUT: True for an 'indirect' change */
);
/*

View File

@ -17,6 +17,7 @@ static int test_session_error(Tcl_Interp *interp, int rc){
** $session changeset
** $session delete
** $session enable BOOL
** $session indirect BOOL
*/
static int test_session_cmd(
void *clientData,
@ -34,7 +35,8 @@ static int test_session_cmd(
{ "attach", 1, "TABLE", }, /* 0 */
{ "changeset", 0, "", }, /* 1 */
{ "delete", 0, "", }, /* 2 */
{ "enable", 1, "", }, /* 3 */
{ "enable", 1, "BOOL", }, /* 3 */
{ "indirect", 1, "BOOL", }, /* 4 */
{ 0 }
};
int iSub;
@ -88,6 +90,14 @@ static int test_session_cmd(
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(val));
break;
}
case 4: { /* indirect */
int val;
if( Tcl_GetBooleanFromObj(interp, objv[2], &val) ) return TCL_ERROR;
val = sqlite3session_indirect(pSession, val);
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(val));
break;
}
}
return TCL_OK;
@ -205,7 +215,7 @@ static int test_conflict_handler(
pEval = Tcl_DuplicateObj(p->pScript);
Tcl_IncrRefCount(pEval);
sqlite3changeset_op(pIter, &zTab, &nCol, &op);
sqlite3changeset_op(pIter, &zTab, &nCol, &op, 0);
/* Append the operation type. */
Tcl_ListObjAppendElement(0, pEval, Tcl_NewStringObj(
@ -396,8 +406,9 @@ static int test_sqlite3session_foreach(
Tcl_Obj *pVar; /* Tcl value to set $VARNAME to */
Tcl_Obj *pOld; /* Vector of old.* values */
Tcl_Obj *pNew; /* Vector of new.* values */
int bIndirect;
sqlite3changeset_op(pIter, &zTab, &nCol, &op);
sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
pVar = Tcl_NewObj();
Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(
op==SQLITE_INSERT ? "INSERT" :
@ -405,6 +416,7 @@ static int test_sqlite3session_foreach(
"DELETE", -1
));
Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zTab, -1));
Tcl_ListObjAppendElement(0, pVar, Tcl_NewBooleanObj(bIndirect));
pOld = Tcl_NewObj();
if( op!=SQLITE_INSERT ){

View File

@ -1,5 +1,5 @@
C Add\sAPI\sfunction\ssqlite3_preupdate_depth(),\sfor\sdetermining\sthe\sdepth\sof\sthe\strigger\sstack\sfrom\swithin\sa\spre-update\scallback.
D 2011-03-22T18:45:30
C Add\sthe\s"indirect\sflag"\sto\sthe\schangeset\sblob\sformat.\sAlso\sthe\ssqlite3session_indirect()\sAPI.
D 2011-03-23T16:03:12
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 27701a1653595a1f2187dc61c8117e00a6c1d50f
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@ -99,13 +99,13 @@ F ext/rtree/rtree_util.tcl 06aab2ed5b826545bf215fff90ecb9255a8647ea
F ext/rtree/sqlite3rtree.h 1af0899c63a688e272d69d8e746f24e76f10a3f0
F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de
F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
F ext/session/session1.test 1e8cda2cc8a60171dabc0fbec4124f9f7c943f23
F ext/session/session2.test f993ee243db15025a7d9b1dae903fe3f82f04229
F ext/session/session1.test bca38efbc34c0cdecc10e599246962860e3f664b
F ext/session/session2.test b0d305ee1397d7c17f9743126f636e97ddf88542
F ext/session/session_common.tcl fb91560b6dbd086010df8b3a137a452f1ac21a28
F ext/session/sessionfault.test 495f87fb9bd764ae90d4b40d0c33a76d86d9063e
F ext/session/sqlite3session.c ec0f440ae73229ae8ae307a31e631b9d71991cd4
F ext/session/sqlite3session.h 5055e21ca0cb6ddacd46b773a15d90bc0298b0a2
F ext/session/test_session.c d31fbf5902d0cff5e5495a5ce62efda77b596f2b
F ext/session/sessionfault.test 4489a49d2d44c74c24251d5802b2cc011dbdac21
F ext/session/sqlite3session.c 0c2e8f6a6d9872943edd04d0a19bf7b05db9df83
F ext/session/sqlite3session.h 000c1ed86322d9d8e8118cd5ba815269aac608f2
F ext/session/test_session.c 0fcfbd51e3f5885b958fbc8cdd8cba01b85b8b16
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F main.mk ae0868e05c76eaa8a0ae3d6927a949b1c8e810d7
@ -923,7 +923,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
P 183c236e991faaabdc768e52e926c52cf4a7abc9
R 3714f49d9e3ab391fe868f9d204fa8d5
P bdea70895c2c686a4dd3f4bf0a475fd1501d9551
R c02abbe676895f559af6fe54b071c0da
U dan
Z 4e2f4fe62e6298f01b15895da5c35a23
Z 27e4954529d298836a72824c8b085018

View File

@ -1 +1 @@
bdea70895c2c686a4dd3f4bf0a475fd1501d9551
1feaf2d35fd9ec777319717ae2c2929d66fe7baa