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 } {}
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 } {}
} {}
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 } {}
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}}
}
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 } { }
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
}
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
# 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
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
}
#-------------------------------------------------------------------------
-# 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}
} {
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
# 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
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
}]
db close
-do_faultsim_test pagerfault-5 -faults oom* -body {
+do_faultsim_test 5 -faults oom* -body {
set ::inverse [sqlite3changeset_invert $::changeset]
set {} {}
} -test {
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" }
}
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. */
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 */
};
*/
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 */
SessionTable *pTab
){
sqlite3 *db = pSession->db;
- SessionChange *pChange;
- SessionChange *pC;
int iHash;
int bNullPk = 0;
int rc = SQLITE_OK;
** 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);
/* 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 */
}
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];
}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;
+ }
}
}
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;
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);
}
}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 ){
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.
*/
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);
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);
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;
}
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;
}
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
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 );
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 ){
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);
*/
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
**
** 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
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 */
);
/*
** $session changeset
** $session delete
** $session enable BOOL
+** $session indirect BOOL
*/
static int test_session_cmd(
void *clientData,
{ "attach", 1, "TABLE", }, /* 0 */
{ "changeset", 0, "", }, /* 1 */
{ "delete", 0, "", }, /* 2 */
- { "enable", 1, "", }, /* 3 */
+ { "enable", 1, "BOOL", }, /* 3 */
+ { "indirect", 1, "BOOL", }, /* 4 */
{ 0 }
};
int iSub;
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;
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(
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" :
"DELETE", -1
));
Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zTab, -1));
+ Tcl_ListObjAppendElement(0, pVar, Tcl_NewBooleanObj(bIndirect));
pOld = Tcl_NewObj();
if( op!=SQLITE_INSERT ){
-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
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
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
-bdea70895c2c686a4dd3f4bf0a475fd1501d9551
\ No newline at end of file
+1feaf2d35fd9ec777319717ae2c2929d66fe7baa
\ No newline at end of file