Modify the documentation for sqlite3_changes() to make it more testable. Add tests and minor fixes for the same.
FossilOrigin-Name: 41cdd0c422d61533a94870cb5ad094682956d472
This commit is contained in:
parent
dd8c460081
commit
c3da667b25
19
manifest
19
manifest
@ -1,5 +1,5 @@
|
||||
C Fix\sa\scrash\sthat\scould\soccur\sif\sthe\sWHERE\sclause\sof\san\sUPDATE\sstatement\son\sa\sview\sthat\sdoes\snot\sfeature\sa\scolumn\snamed\s"rowid"\scontains\sa\sterm\ssuch\sas\s"rowid=?".
|
||||
D 2014-10-28T16:50:10.527
|
||||
C Modify\sthe\sdocumentation\sfor\ssqlite3_changes()\sto\smake\sit\smore\stestable.\sAdd\stests\sand\sminor\sfixes\sfor\sthe\ssame.
|
||||
D 2014-10-28T18:24:16.387
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in cf57f673d77606ab0f2d9627ca52a9ba1464146a
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@ -229,7 +229,7 @@ F src/resolve.c 4965007d6497b6a4d7a6d98751cc39712885f952
|
||||
F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e
|
||||
F src/select.c 428165951748151e87a15295b7357221433e311b
|
||||
F src/shell.c 282f8f5278e0c78eb442217531172ec9e1538796
|
||||
F src/sqlite.h.in a9f2e5a0e2472c8c7819f3a16074c14b9376608f
|
||||
F src/sqlite.h.in 1c5624f8b21cc8261a8b048033815581347b375f
|
||||
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
|
||||
F src/sqlite3ext.h 17d487c3c91b0b8c584a32fbeb393f6f795eea7d
|
||||
F src/sqliteInt.h 90519c3b3e8ee90adfce013234c4bd07275d77b5
|
||||
@ -289,11 +289,11 @@ F src/update.c 3c4ecc282accf12d39edb8d524cf089645e55a13
|
||||
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
|
||||
F src/util.c 3b627daa45c7308c1e36e3dbaa3f9ce7e5c7fa73
|
||||
F src/vacuum.c 59f03f92bcff57faa6a8ca256eb29ccddfb0614a
|
||||
F src/vdbe.c 5ee15a66ce07e0482b92aa29e4dd0c5827a22d79
|
||||
F src/vdbe.c 1b7e8ccaca2a23ae2804568f34b7c645adfd332d
|
||||
F src/vdbe.h 09f5b4e3719fa454f252322b1cdab5cf1f361327
|
||||
F src/vdbeInt.h e2a060a55ee18a6ab973353a5e2ec7ee569bf787
|
||||
F src/vdbeInt.h acc36ac461f973f46ac7942f86abdd93d2f8cfbc
|
||||
F src/vdbeapi.c 02d8afcff710eb35e3d9e49cb677308296b00009
|
||||
F src/vdbeaux.c edbb7a9c8b2a8f7a68ac75c2475edd4040266b76
|
||||
F src/vdbeaux.c 3d6b2b412ef2193aa4729922dfc5df1efadbf6df
|
||||
F src/vdbeblob.c 8b5442ff0954c44b45cbabbe2e94091a2e16fdef
|
||||
F src/vdbemem.c 31d8eabb0cd78bfeab4e5124c7363c3e9e54db9f
|
||||
F src/vdbesort.c 975aeffa99acb0991b2f288d30294756bff41438
|
||||
@ -451,6 +451,7 @@ F test/descidx3.test 09ddbe3f5295f482d2f8b687cf6db8bad7acd9a2
|
||||
F test/diskfull.test 106391384780753ea6896b7b4f005d10e9866b6e
|
||||
F test/distinct.test 086e70c765f172e8974e9f83b9ac5ca03c154e77
|
||||
F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376
|
||||
F test/e_changes.test fd66105385153dbf21fdb35eb8ef6c3e1eade579
|
||||
F test/e_createtable.test c7e67b49e6cf92473c8fb30ab26143e9e2128cf7
|
||||
F test/e_delete.test d5186e2f5478b659f16a2c8b66c09892823e542a
|
||||
F test/e_droptrigger.test 3cd080807622c13e5bbb61fc9a57bd7754da2412
|
||||
@ -1207,7 +1208,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
P 95f8ebdbf87326f23cd38e561ac5632b5367a449
|
||||
R c4fade85ca4f95e680190bc9e417c1ff
|
||||
P 8523670d50004f3112b7871f11c8b8b02aab96ab
|
||||
R 8192ca2d13d614d67e4c391f6685b170
|
||||
U dan
|
||||
Z 12d8cc683b13ea652b467efe210b5438
|
||||
Z 02a949e02b7399a00fe453126f032ba0
|
||||
|
@ -1 +1 @@
|
||||
8523670d50004f3112b7871f11c8b8b02aab96ab
|
||||
41cdd0c422d61533a94870cb5ad094682956d472
|
@ -1874,47 +1874,45 @@ sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*);
|
||||
/*
|
||||
** CAPI3REF: Count The Number Of Rows Modified
|
||||
**
|
||||
** ^This function returns the number of database rows that were changed
|
||||
** or inserted or deleted by the most recently completed SQL statement
|
||||
** on the [database connection] specified by the first parameter.
|
||||
** ^(Only changes that are directly specified by the [INSERT], [UPDATE],
|
||||
** or [DELETE] statement are counted. Auxiliary changes caused by
|
||||
** triggers or [foreign key actions] are not counted.)^ Use the
|
||||
** [sqlite3_total_changes()] function to find the total number of changes
|
||||
** including changes caused by triggers and foreign key actions.
|
||||
** ^This function returns the number of rows modified, inserted or
|
||||
** deleted by the most recently completed INSERT, UPDATE or DELETE
|
||||
** statement on the database connection specified by the only parameter.
|
||||
** ^Executing any other type of SQL statement does not modify the value
|
||||
** returned by this function.
|
||||
**
|
||||
** ^Changes to a view that are simulated by an [INSTEAD OF trigger]
|
||||
** are not counted. Only real table changes are counted.
|
||||
** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are
|
||||
** considered - auxiliary changes caused by [CREATE TRIGGER | triggers],
|
||||
** [foreign key actions] or [REPLACE] constraint resolution are not counted.
|
||||
**
|
||||
** Changes to a view that are intercepted by
|
||||
** [INSTEAD OF trigger | INSTEAD OF triggers] are not counted. ^The value
|
||||
** returned by sqlite3_changes() immediately after an INSERT, UPDATE or
|
||||
** DELETE statement run on a view is always zero. Only changes made to real
|
||||
** tables are counted.
|
||||
**
|
||||
** ^(A "row change" is a change to a single row of a single table
|
||||
** caused by an INSERT, DELETE, or UPDATE statement. Rows that
|
||||
** are changed as side effects of [REPLACE] constraint resolution,
|
||||
** rollback, ABORT processing, [DROP TABLE], or by any other
|
||||
** mechanisms do not count as direct row changes.)^
|
||||
**
|
||||
** A "trigger context" is a scope of execution that begins and
|
||||
** ends with the script of a [CREATE TRIGGER | trigger].
|
||||
** Most SQL statements are
|
||||
** evaluated outside of any trigger. This is the "top level"
|
||||
** trigger context. If a trigger fires from the top level, a
|
||||
** new trigger context is entered for the duration of that one
|
||||
** trigger. Subtriggers create subcontexts for their duration.
|
||||
**
|
||||
** ^Calling [sqlite3_exec()] or [sqlite3_step()] recursively does
|
||||
** not create a new trigger context.
|
||||
**
|
||||
** ^This function returns the number of direct row changes in the
|
||||
** most recent INSERT, UPDATE, or DELETE statement within the same
|
||||
** trigger context.
|
||||
**
|
||||
** ^Thus, when called from the top level, this function returns the
|
||||
** number of changes in the most recent INSERT, UPDATE, or DELETE
|
||||
** that also occurred at the top level. ^(Within the body of a trigger,
|
||||
** the sqlite3_changes() interface can be called to find the number of
|
||||
** changes in the most recently completed INSERT, UPDATE, or DELETE
|
||||
** statement within the body of the same trigger.
|
||||
** However, the number returned does not include changes
|
||||
** caused by subtriggers since those have their own context.)^
|
||||
** Things are more complicated if the sqlite3_changes() function is
|
||||
** executed while a trigger program is running. This may happen if the
|
||||
** program uses the [changes() SQL function], or if some other callback
|
||||
** function invokes sqlite3_changes() directly. Essentially:
|
||||
**
|
||||
** <ul>
|
||||
** <li> ^(Before entering a trigger program the value returned by
|
||||
** sqlite3_changes() function is saved. After the trigger program
|
||||
** has finished, the original value is restored.)^
|
||||
**
|
||||
** <li> ^(Within a trigger program each INSERT, UPDATE and DELETE
|
||||
** statement sets the value returned by sqlite3_changes()
|
||||
** upon completion as normal. Of course, this value will not include
|
||||
** any changes performed by sub-triggers, as the sqlite3_changes()
|
||||
** value will be saved and restored after each sub-trigger has run.)^
|
||||
** </ul>
|
||||
**
|
||||
** ^This means that if the changes() SQL function (or similar) is used
|
||||
** by the first INSERT, UPDATE or DELETE statement within a trigger, it
|
||||
** returns the value as set when the calling statement began executing.
|
||||
** ^If it is used by the second or subsequent such statement within a trigger
|
||||
** program, the value returned reflects the number of rows modified by the
|
||||
** previous INSERT, UPDATE or DELETE statement within the same trigger.
|
||||
**
|
||||
** See also the [sqlite3_total_changes()] interface, the
|
||||
** [count_changes pragma], and the [changes() SQL function].
|
||||
@ -1934,7 +1932,7 @@ int sqlite3_changes(sqlite3*);
|
||||
** from all [CREATE TRIGGER | trigger] contexts and changes made by
|
||||
** [foreign key actions]. However,
|
||||
** the count does not include changes used to implement [REPLACE] constraints,
|
||||
** do rollbacks or ABORT processing, or [DROP TABLE] processing. The
|
||||
** rollbacks or [DROP TABLE] commands. The
|
||||
** count does not include rows of views that fire an [INSTEAD OF trigger],
|
||||
** though if the INSTEAD OF trigger makes changes of its own, those changes
|
||||
** are counted.)^
|
||||
|
@ -5423,6 +5423,7 @@ case OP_Program: { /* jump */
|
||||
pFrame->pParent = p->pFrame;
|
||||
pFrame->lastRowid = lastRowid;
|
||||
pFrame->nChange = p->nChange;
|
||||
pFrame->nDbChange = p->db->nChange;
|
||||
p->nChange = 0;
|
||||
p->pFrame = pFrame;
|
||||
p->aMem = aMem = &VdbeFrameMem(pFrame)[-1];
|
||||
|
@ -144,7 +144,8 @@ struct VdbeFrame {
|
||||
int nOnceFlag; /* Number of entries in aOnceFlag */
|
||||
int nChildMem; /* Number of memory cells for child frame */
|
||||
int nChildCsr; /* Number of cursors for child frame */
|
||||
int nChange; /* Statement changes (Vdbe.nChanges) */
|
||||
int nChange; /* Statement changes (Vdbe.nChange) */
|
||||
int nDbChange; /* Value of db->nChange */
|
||||
};
|
||||
|
||||
#define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))])
|
||||
|
@ -1772,6 +1772,7 @@ int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){
|
||||
v->nCursor = pFrame->nCursor;
|
||||
v->db->lastRowid = pFrame->lastRowid;
|
||||
v->nChange = pFrame->nChange;
|
||||
v->db->nChange = pFrame->nDbChange;
|
||||
return pFrame->pc;
|
||||
}
|
||||
|
||||
@ -2339,6 +2340,7 @@ int sqlite3VdbeHalt(Vdbe *p){
|
||||
sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
|
||||
sqlite3CloseSavepoints(db);
|
||||
db->autoCommit = 1;
|
||||
p->nChange = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2379,6 +2381,7 @@ int sqlite3VdbeHalt(Vdbe *p){
|
||||
}else if( rc!=SQLITE_OK ){
|
||||
p->rc = rc;
|
||||
sqlite3RollbackAll(db, SQLITE_OK);
|
||||
p->nChange = 0;
|
||||
}else{
|
||||
db->nDeferredCons = 0;
|
||||
db->nDeferredImmCons = 0;
|
||||
@ -2387,6 +2390,7 @@ int sqlite3VdbeHalt(Vdbe *p){
|
||||
}
|
||||
}else{
|
||||
sqlite3RollbackAll(db, SQLITE_OK);
|
||||
p->nChange = 0;
|
||||
}
|
||||
db->nStatement = 0;
|
||||
}else if( eStatementOp==0 ){
|
||||
@ -2398,6 +2402,7 @@ int sqlite3VdbeHalt(Vdbe *p){
|
||||
sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
|
||||
sqlite3CloseSavepoints(db);
|
||||
db->autoCommit = 1;
|
||||
p->nChange = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2418,6 +2423,7 @@ int sqlite3VdbeHalt(Vdbe *p){
|
||||
sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
|
||||
sqlite3CloseSavepoints(db);
|
||||
db->autoCommit = 1;
|
||||
p->nChange = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
441
test/e_changes.test
Normal file
441
test/e_changes.test
Normal file
@ -0,0 +1,441 @@
|
||||
# 2011 October 28
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix e_changes
|
||||
|
||||
# Like [do_execsql_test], except it appends the value returned by
|
||||
# [db changes] to the result of executing the SQL script.
|
||||
#
|
||||
proc do_changes_test {tn sql res} {
|
||||
uplevel [list \
|
||||
do_test $tn "concat \[execsql {$sql}\] \[db changes\]" $res
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-15996-49369 This function returns the number of rows
|
||||
# modified, inserted or deleted by the most recently completed INSERT,
|
||||
# UPDATE or DELETE statement on the database connection specified by the
|
||||
# only parameter.
|
||||
#
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE t2(x, y, PRIMARY KEY(x, y)) WITHOUT ROWID;
|
||||
CREATE INDEX i1 ON t1(a);
|
||||
CREATE INDEX i2 ON t2(y);
|
||||
}
|
||||
foreach {tn schema} {
|
||||
1 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(b);
|
||||
}
|
||||
2 {
|
||||
CREATE TABLE t1(a, b, PRIMARY KEY(a, b)) WITHOUT ROWID;
|
||||
CREATE INDEX i1 ON t1(b);
|
||||
}
|
||||
} {
|
||||
reset_db
|
||||
execsql $schema
|
||||
|
||||
# Insert 1 row.
|
||||
do_changes_test 1.$tn.1 { INSERT INTO t1 VALUES(0, 0) } 1
|
||||
|
||||
# Insert 10 rows.
|
||||
do_changes_test 1.$tn.2 {
|
||||
WITH rows(i, j) AS (
|
||||
SELECT 1, 1 UNION ALL SELECT i+1, j+i FROM rows WHERE i<10
|
||||
)
|
||||
INSERT INTO t1 SELECT * FROM rows
|
||||
} 10
|
||||
|
||||
# Modify 5 rows.
|
||||
do_changes_test 1.$tn.3 {
|
||||
UPDATE t1 SET b=b+1 WHERE a<5;
|
||||
} 5
|
||||
|
||||
# Delete 4 rows
|
||||
do_changes_test 1.$tn.4 {
|
||||
DELETE FROM t1 WHERE a>6
|
||||
} 4
|
||||
|
||||
# Check the "on the database connecton specified" part of hte
|
||||
# requirement - changes made by other connections do not show up in
|
||||
# the return value of sqlite3_changes().
|
||||
do_test 1.$tn.5 {
|
||||
sqlite3 db2 test.db
|
||||
execsql { INSERT INTO t1 VALUES(-1, -1) } db2
|
||||
db2 changes
|
||||
} 1
|
||||
do_test 1.$tn.6 {
|
||||
db changes
|
||||
} 4
|
||||
db2 close
|
||||
|
||||
# Test that statements that modify no rows because they hit UNIQUE
|
||||
# constraints set the sqlite3_changes() value to 0. Regardless of
|
||||
# whether or not they are executed inside an explicit transaction.
|
||||
#
|
||||
# 1.$tn.8-9: outside of a transaction
|
||||
# 1.$tn.10-12: inside a transaction
|
||||
#
|
||||
do_changes_test 1.$tn.7 {
|
||||
CREATE UNIQUE INDEX i2 ON t1(a);
|
||||
} 4
|
||||
do_catchsql_test 1.$tn.8 {
|
||||
INSERT INTO t1 VALUES('a', 0), ('b', 0), ('c', 0), (0, 11);
|
||||
} {1 {UNIQUE constraint failed: t1.a}}
|
||||
do_test 1.$tn.9 { db changes } 0
|
||||
do_catchsql_test 1.$tn.10 {
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES('a', 0), ('b', 0), ('c', 0), (0, 11);
|
||||
} {1 {UNIQUE constraint failed: t1.a}}
|
||||
do_test 1.$tn.11 { db changes } 0
|
||||
do_changes_test 1.$tn.12 COMMIT 0
|
||||
|
||||
}
|
||||
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-44877-05564 Executing any other type of SQL statement
|
||||
# does not modify the value returned by this function.
|
||||
#
|
||||
reset_db
|
||||
do_changes_test 2.1 { CREATE TABLE t1(x) } 0
|
||||
do_changes_test 2.2 {
|
||||
WITH d(y) AS (SELECT 1 UNION ALL SELECT y+1 FROM d WHERE y<47)
|
||||
INSERT INTO t1 SELECT y FROM d;
|
||||
} 47
|
||||
|
||||
# The statement above set changes() to 47. Check that none of the following
|
||||
# modify this.
|
||||
do_changes_test 2.3 { SELECT count(x) FROM t1 } {47 47}
|
||||
do_changes_test 2.4 { DROP TABLE t1 } 47
|
||||
do_changes_test 2.5 { CREATE TABLE t1(x) } 47
|
||||
do_changes_test 2.6 { ALTER TABLE t1 ADD COLUMN b } 47
|
||||
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-53938-27527 Only changes made directly by the INSERT,
|
||||
# UPDATE or DELETE statement are considered - auxiliary changes caused
|
||||
# by triggers, foreign key actions or REPLACE constraint resolution are
|
||||
# not counted.
|
||||
#
|
||||
# 3.1.*: triggers
|
||||
# 3.2.*: foreign key actions
|
||||
# 3.3.*: replace constraints
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 3.1.0 {
|
||||
CREATE TABLE log(x);
|
||||
CREATE TABLE p1(one PRIMARY KEY, two);
|
||||
|
||||
CREATE TRIGGER tr_ai AFTER INSERT ON p1 BEGIN
|
||||
INSERT INTO log VALUES('insert');
|
||||
END;
|
||||
CREATE TRIGGER tr_bd BEFORE DELETE ON p1 BEGIN
|
||||
INSERT INTO log VALUES('delete');
|
||||
END;
|
||||
CREATE TRIGGER tr_au AFTER UPDATE ON p1 BEGIN
|
||||
INSERT INTO log VALUES('update');
|
||||
END;
|
||||
|
||||
}
|
||||
|
||||
do_changes_test 3.1.1 {
|
||||
INSERT INTO p1 VALUES('a', 'A'), ('b', 'B'), ('c', 'C');
|
||||
} 3
|
||||
do_changes_test 3.1.2 {
|
||||
UPDATE p1 SET two = two||two;
|
||||
} 3
|
||||
do_changes_test 3.1.3 {
|
||||
DELETE FROM p1 WHERE one IN ('a', 'c');
|
||||
} 2
|
||||
do_execsql_test 3.1.4 {
|
||||
-- None of the inserts on table log were counted.
|
||||
SELECT count(*) FROM log
|
||||
} 8
|
||||
|
||||
do_execsql_test 3.2.0 {
|
||||
DELETE FROM p1;
|
||||
INSERT INTO p1 VALUES('a', 'A'), ('b', 'B'), ('c', 'C');
|
||||
|
||||
CREATE TABLE c1(a, b, FOREIGN KEY(a) REFERENCES p1 ON DELETE SET NULL);
|
||||
CREATE TABLE c2(a, b, FOREIGN KEY(a) REFERENCES p1 ON DELETE SET DEFAULT);
|
||||
CREATE TABLE c3(a, b, FOREIGN KEY(a) REFERENCES p1 ON DELETE CASCADE);
|
||||
INSERT INTO c1 VALUES('a', 'aaa');
|
||||
INSERT INTO c2 VALUES('b', 'bbb');
|
||||
INSERT INTO c3 VALUES('c', 'ccc');
|
||||
|
||||
INSERT INTO p1 VALUES('d', 'D'), ('e', 'E'), ('f', 'F');
|
||||
CREATE TABLE c4(a, b, FOREIGN KEY(a) REFERENCES p1 ON UPDATE SET NULL);
|
||||
CREATE TABLE c5(a, b, FOREIGN KEY(a) REFERENCES p1 ON UPDATE SET DEFAULT);
|
||||
CREATE TABLE c6(a, b, FOREIGN KEY(a) REFERENCES p1 ON UPDATE CASCADE);
|
||||
INSERT INTO c4 VALUES('d', 'aaa');
|
||||
INSERT INTO c5 VALUES('e', 'bbb');
|
||||
INSERT INTO c6 VALUES('f', 'ccc');
|
||||
|
||||
PRAGMA foreign_keys = ON;
|
||||
}
|
||||
|
||||
do_changes_test 3.2.1 { DELETE FROM p1 WHERE one = 'a' } 1
|
||||
do_changes_test 3.2.2 { DELETE FROM p1 WHERE one = 'b' } 1
|
||||
do_changes_test 3.2.3 { DELETE FROM p1 WHERE one = 'c' } 1
|
||||
do_execsql_test 3.2.4 {
|
||||
SELECT * FROM c1;
|
||||
SELECT * FROM c2;
|
||||
SELECT * FROM c3;
|
||||
} {{} aaa {} bbb}
|
||||
|
||||
do_changes_test 3.2.5 { UPDATE p1 SET one = 'g' WHERE one = 'd' } 1
|
||||
do_changes_test 3.2.6 { UPDATE p1 SET one = 'h' WHERE one = 'e' } 1
|
||||
do_changes_test 3.2.7 { UPDATE p1 SET one = 'i' WHERE one = 'f' } 1
|
||||
do_execsql_test 3.2.8 {
|
||||
SELECT * FROM c4;
|
||||
SELECT * FROM c5;
|
||||
SELECT * FROM c6;
|
||||
} {{} aaa {} bbb i ccc}
|
||||
|
||||
do_execsql_test 3.3.0 {
|
||||
CREATE TABLE r1(a UNIQUE, b UNIQUE);
|
||||
INSERT INTO r1 VALUES('i', 'i');
|
||||
INSERT INTO r1 VALUES('ii', 'ii');
|
||||
INSERT INTO r1 VALUES('iii', 'iii');
|
||||
INSERT INTO r1 VALUES('iv', 'iv');
|
||||
INSERT INTO r1 VALUES('v', 'v');
|
||||
INSERT INTO r1 VALUES('vi', 'vi');
|
||||
INSERT INTO r1 VALUES('vii', 'vii');
|
||||
}
|
||||
|
||||
do_changes_test 3.3.1 { INSERT OR REPLACE INTO r1 VALUES('i', 1) } 1
|
||||
do_changes_test 3.3.2 { INSERT OR REPLACE INTO r1 VALUES('iv', 'v') } 1
|
||||
do_changes_test 3.3.3 { UPDATE OR REPLACE r1 SET b='v' WHERE a='iii' } 1
|
||||
do_changes_test 3.3.4 { UPDATE OR REPLACE r1 SET b='vi',a='vii' WHERE a='ii' } 1
|
||||
do_execsql_test 3.3.5 {
|
||||
SELECT * FROM r1 ORDER BY a;
|
||||
} {i 1 iii v vii vi}
|
||||
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-09813-48563 The value returned by sqlite3_changes()
|
||||
# immediately after an INSERT, UPDATE or DELETE statement run on a view
|
||||
# is always zero.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 4.1 {
|
||||
CREATE TABLE log(log);
|
||||
CREATE TABLE t1(x, y);
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
INSERT INTO t1 VALUES(3, 4);
|
||||
INSERT INTO t1 VALUES(5, 6);
|
||||
|
||||
CREATE VIEW v1 AS SELECT * FROM t1;
|
||||
CREATE TRIGGER v1_i INSTEAD OF INSERT ON v1 BEGIN
|
||||
INSERT INTO log VALUES('insert');
|
||||
END;
|
||||
CREATE TRIGGER v1_u INSTEAD OF UPDATE ON v1 BEGIN
|
||||
INSERT INTO log VALUES('update'), ('update');
|
||||
END;
|
||||
CREATE TRIGGER v1_d INSTEAD OF DELETE ON v1 BEGIN
|
||||
INSERT INTO log VALUES('delete'), ('delete'), ('delete');
|
||||
END;
|
||||
}
|
||||
|
||||
do_changes_test 4.2.1 { INSERT INTO t1 SELECT * FROM t1 } 3
|
||||
do_changes_test 4.2.2 { INSERT INTO v1 VALUES(1, 2) } 0
|
||||
|
||||
do_changes_test 4.3.1 { INSERT INTO t1 SELECT * FROM t1 } 6
|
||||
do_changes_test 4.3.2 { UPDATE v1 SET y='xyz' WHERE x=1 } 0
|
||||
|
||||
do_changes_test 4.4.1 { INSERT INTO t1 SELECT * FROM t1 } 12
|
||||
do_changes_test 4.4.2 { DELETE FROM v1 WHERE x=5 } 0
|
||||
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-32918-61474 Before entering a trigger program the value
|
||||
# returned by sqlite3_changes() function is saved. After the trigger
|
||||
# program has finished, the original value is restored.
|
||||
#
|
||||
reset_db
|
||||
db func my_changes my_changes
|
||||
set ::changes [list]
|
||||
proc my_changes {x} {
|
||||
set res [db changes]
|
||||
lappend ::changes $x $res
|
||||
return $res
|
||||
}
|
||||
|
||||
do_execsql_test 5.1.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||
CREATE TABLE t2(x);
|
||||
INSERT INTO t1 VALUES(1, NULL);
|
||||
INSERT INTO t1 VALUES(2, NULL);
|
||||
INSERT INTO t1 VALUES(3, NULL);
|
||||
CREATE TRIGGER AFTER UPDATE ON t1 BEGIN
|
||||
INSERT INTO t2 VALUES('a'), ('b'), ('c');
|
||||
SELECT my_changes('trigger');
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 5.1.1 {
|
||||
INSERT INTO t2 VALUES('a'), ('b');
|
||||
UPDATE t1 SET b = my_changes('update');
|
||||
SELECT * FROM t1;
|
||||
} {1 2 2 2 3 2}
|
||||
|
||||
# Value is being restored to "2" when the trigger program exits.
|
||||
do_test 5.1.2 {
|
||||
set ::changes
|
||||
} {update 2 trigger 3 update 2 trigger 3 update 2 trigger 3}
|
||||
|
||||
|
||||
reset_db
|
||||
do_execsql_test 5.2.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE log(x);
|
||||
INSERT INTO t1 VALUES(1, 0);
|
||||
INSERT INTO t1 VALUES(2, 0);
|
||||
INSERT INTO t1 VALUES(3, 0);
|
||||
CREATE TRIGGER t1_a_u AFTER UPDATE ON t1 BEGIN
|
||||
INSERT INTO log VALUES(old.b || ' -> ' || new.b || ' c = ' || changes() );
|
||||
END;
|
||||
CREATE TABLE t2(a);
|
||||
INSERT INTO t2 VALUES(1), (2), (3);
|
||||
UPDATE t1 SET b = changes();
|
||||
}
|
||||
do_execsql_test 5.2.1 {
|
||||
SELECT * FROM t1;
|
||||
} {1 3 2 3 3 3}
|
||||
do_execsql_test 5.2.2 {
|
||||
SELECT * FROM log;
|
||||
} {{0 -> 3 c = 3} {0 -> 3 c = 3} {0 -> 3 c = 3}}
|
||||
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-17146-37073 Within a trigger program each INSERT,
|
||||
# UPDATE and DELETE statement sets the value returned by
|
||||
# sqlite3_changes() upon completion as normal. Of course, this value
|
||||
# will not include any changes performed by sub-triggers, as the
|
||||
# sqlite3_changes() value will be saved and restored after each
|
||||
# sub-trigger has run.
|
||||
reset_db
|
||||
do_execsql_test 6.0 {
|
||||
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE t2(a, b);
|
||||
CREATE TABLE t3(a, b);
|
||||
CREATE TABLE log(x);
|
||||
|
||||
CREATE TRIGGER t1_i BEFORE INSERT ON t1 BEGIN
|
||||
INSERT INTO t2 VALUES(new.a, new.b), (new.a, new.b);
|
||||
INSERT INTO log VALUES('t2->' || changes());
|
||||
END;
|
||||
|
||||
CREATE TRIGGER t2_i AFTER INSERT ON t2 BEGIN
|
||||
INSERT INTO t3 VALUES(new.a, new.b), (new.a, new.b), (new.a, new.b);
|
||||
INSERT INTO log VALUES('t3->' || changes());
|
||||
END;
|
||||
|
||||
CREATE TRIGGER t1_u AFTER UPDATE ON t1 BEGIN
|
||||
UPDATE t2 SET b=new.b WHERE a=old.a;
|
||||
INSERT INTO log VALUES('t2->' || changes());
|
||||
END;
|
||||
|
||||
CREATE TRIGGER t2_u BEFORE UPDATE ON t2 BEGIN
|
||||
UPDATE t3 SET b=new.b WHERE a=old.a;
|
||||
INSERT INTO log VALUES('t3->' || changes());
|
||||
END;
|
||||
|
||||
CREATE TRIGGER t1_d AFTER DELETE ON t1 BEGIN
|
||||
DELETE FROM t2 WHERE a=old.a AND b=old.b;
|
||||
INSERT INTO log VALUES('t2->' || changes());
|
||||
END;
|
||||
|
||||
CREATE TRIGGER t2_d BEFORE DELETE ON t2 BEGIN
|
||||
DELETE FROM t3 WHERE a=old.a AND b=old.b;
|
||||
INSERT INTO log VALUES('t3->' || changes());
|
||||
END;
|
||||
}
|
||||
|
||||
do_changes_test 6.1 {
|
||||
INSERT INTO t1 VALUES('+', 'o');
|
||||
SELECT * FROM log;
|
||||
} {t3->3 t3->3 t2->2 1}
|
||||
|
||||
do_changes_test 6.2 {
|
||||
DELETE FROM log;
|
||||
UPDATE t1 SET b='*';
|
||||
SELECT * FROM log;
|
||||
} {t3->6 t3->6 t2->2 1}
|
||||
|
||||
do_changes_test 6.3 {
|
||||
DELETE FROM log;
|
||||
DELETE FROM t1;
|
||||
SELECT * FROM log;
|
||||
} {t3->6 t3->0 t2->2 1}
|
||||
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-43399-09409 This means that if the changes() SQL
|
||||
# function (or similar) is used by the first INSERT, UPDATE or DELETE
|
||||
# statement within a trigger, it returns the value as set when the
|
||||
# calling statement began executing.
|
||||
#
|
||||
# EVIDENCE-OF: R-53215-27584 If it is used by the second or subsequent
|
||||
# such statement within a trigger program, the value returned reflects
|
||||
# the number of rows modified by the previous INSERT, UPDATE or DELETE
|
||||
# statement within the same trigger.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 7.1 {
|
||||
CREATE TABLE q1(t);
|
||||
CREATE TABLE q2(u, v);
|
||||
CREATE TABLE q3(w);
|
||||
|
||||
CREATE TRIGGER q2_insert BEFORE INSERT ON q2 BEGIN
|
||||
|
||||
/* changes() returns value from previous I/U/D in callers context */
|
||||
INSERT INTO q1 VALUES('1:' || changes());
|
||||
|
||||
/* changes() returns value of previous I/U/D in this context */
|
||||
INSERT INTO q3 VALUES(changes()), (2), (3);
|
||||
INSERT INTO q1 VALUES('2:' || changes());
|
||||
INSERT INTO q3 VALUES(changes() + 3), (changes()+4);
|
||||
SELECT 'this does not affect things!';
|
||||
INSERT INTO q1 VALUES('3:' || changes());
|
||||
UPDATE q3 SET w = w+10 WHERE w%2;
|
||||
INSERT INTO q1 VALUES('4:' || changes());
|
||||
DELETE FROM q3;
|
||||
INSERT INTO q1 VALUES('5:' || changes());
|
||||
END;
|
||||
}
|
||||
|
||||
do_execsql_test 7.2 {
|
||||
INSERT INTO q2 VALUES('x', 'y');
|
||||
SELECT * FROM q1;
|
||||
} {
|
||||
1:0 2:3 3:2 4:3 5:5
|
||||
}
|
||||
|
||||
do_execsql_test 7.3 {
|
||||
DELETE FROM q1;
|
||||
INSERT INTO q2 VALUES('x', 'y');
|
||||
SELECT * FROM q1;
|
||||
} {
|
||||
1:5 2:3 3:2 4:3 5:5
|
||||
}
|
||||
|
||||
|
||||
|
||||
finish_test
|
Loading…
x
Reference in New Issue
Block a user