aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordan <dan@noemail.net>2020-04-29 17:41:29 +0000
committerdan <dan@noemail.net>2020-04-29 17:41:29 +0000
commit9ed322d6c3c2562dffd590c59dfc8f83c31937ea (patch)
tree0e5db4a8ed99f585bccd399d009f73c2fdc69bc3
parent69887c99d4cd271fe6bf9b40e958c90e409120b1 (diff)
downloadsqlite-9ed322d6c3c2562dffd590c59dfc8f83c31937ea.tar.gz
sqlite-9ed322d6c3c2562dffd590c59dfc8f83c31937ea.zip
Fix various bugs in new feature on this branch.
FossilOrigin-Name: 823ba94e29dece1687e28711e503a1f56d392c306b0cbc0a20548180834530d1
-rw-r--r--manifest20
-rw-r--r--manifest.uuid2
-rw-r--r--src/select.c31
-rw-r--r--src/sqliteInt.h4
-rw-r--r--src/update.c15
-rw-r--r--test/upfrom2.test119
6 files changed, 164 insertions, 27 deletions
diff --git a/manifest b/manifest
index 78387769a..f4a1a2e9d 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Allow\sa\sFROM\sclause\sin\sUPDATE\sstatements.
-D 2020-04-27T20:55:33.061
+C Fix\svarious\sbugs\sin\snew\sfeature\son\sthis\sbranch.
+D 2020-04-29T17:41:29.116
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -532,12 +532,12 @@ F src/printf.c 9be6945837c839ba57837b4bc3af349eba630920fa5532aa518816defe42a7d4
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
F src/resolve.c d36a2b1639e1c33d7b508abfd3452a63e7fd81737f6f3940bfef085fca6f21f4
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
-F src/select.c 7e56a58673d027ab7951559adfda752192baff7c6083a88e4dd8db3c84e465e8
+F src/select.c 88ffd4e1a2b6bf221e3aaad026885485c7f2fe9291201f5e7d0656a3603b6bbe
F src/shell.c.in 1fc834b80c72dd37587ea87a4f4167cf5e6d98d12d143184ed2e732f529c0950
F src/sqlite.h.in fd6fcfe173accab8d9cb9a843856d9e9fb475f893b60a455e01d8739b5076f0e
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 2d1af80082edffd71c6f96f70ad1ce6a4fb46615ad10291fc77fe0dea9ff0197
-F src/sqliteInt.h bfed03b21bfa8fade8887a12d5bc0f5a349e98105aec675b9c1e027e9fd66f67
+F src/sqliteInt.h 3caabf4700e4d98bf6d4d3d60fab71c808d43ca9a98ee9674342f99d1a96d382
F src/sqliteLimit.h 95cb8479ca459496d9c1c6a9f76b38aee12203a56ce1092fe13e50ae2454c032
F src/status.c 9ff2210207c6c3b4d9631a8241a7d45ab1b26a0e9c84cb07a9b5ce2de9a3b278
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
@@ -599,7 +599,7 @@ F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c eee7bae3ec0bc4abee951554bf46a8ba567c0f7752ac90c820ed8afff4c612dc
F src/treeview.c 82c6391a3ba76215d4185fd4719a56ec4caf186a40c8a7b6e6ba4ae4467c2742
F src/trigger.c 4ada1037cc99777f647a882cdacbd1a4deb6567b69daf02946286401b88cdc04
-F src/update.c 72aae4f6198aca8290c1368f26f6f8b7d29e23d0d2bfbd4f773eaa8d9a9380a4
+F src/update.c 3dda0590ff4a87f29828cd21840f11e6ddc853061a94f4aebc2af8f37d0ba92c
F src/upsert.c 2920de71b20f04fe25eb00b655d086f0ba60ea133c59d7fa3325c49838818e78
F src/utf.c 95fb6e03a5ca679045c5adccd05380f0addccabef5911abddcb06af069500ab7
F src/util.c 3b6cedf7a0c69bd6e1acce832873952d416212d6293b18d03064e07d7a9b5118
@@ -1618,6 +1618,7 @@ F test/update.test e906ca7cb1dc6f52af1ea243e08f727edfa79f924c2691f2f9e72481f8473
F test/update2.test 67455bc61fcbcf96923c45b3bc4f87bc72be7d67575ad35f134906148c7b06d3
F test/upfrom1.tcl 62efddee869b3a6f3e076b2816793fec9422e38d10ea42b63da733cdd2b1ad8e
F test/upfrom1.test 543389b4eef43c7a21079df018cf95e29d7c2a4efd09b2597e54a03bbdbd30b9
+F test/upfrom2.test aba21e9988809b05de336bc882ad7318a0ca275c735c9a9c6688ebc01053ce29
F test/upsert1.test 88f9e258c6a0eeeb85937b08831e8daad440ba41f125af48439e9d33f266fb18
F test/upsert2.test 9c3cdbb1a890227f6504ce4b0e3de68f4cdfa16bb21d8641208a9239896c5a09
F test/upsert3.test 88d7d590a1948a9cb6eac1b54b0642f67a9f35a1fc0f19b200e97d5d39e3179c
@@ -1864,10 +1865,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P b73d9a7d6f7fec0ffc9640902a849289c305f8651e891388c01255c4da7a6c4b
-R bbc1e04364e396384c0537dbe66bc52f
-T *branch * update-from
-T *sym-update-from *
-T -sym-trunk *
+P f353a1a613bb7ad8cedcda377a7fe6fd05ee03b1f50665b00b84a868a71c5bec
+R ff3249230b7ba7b12347209dea6f6d16
U dan
-Z 657e2dd1dadbea443cdb52f63d6f9883
+Z 37eb2f9d556f45297467e2642dd5f97e
diff --git a/manifest.uuid b/manifest.uuid
index b06346868..8f74ee97e 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-f353a1a613bb7ad8cedcda377a7fe6fd05ee03b1f50665b00b84a868a71c5bec \ No newline at end of file
+823ba94e29dece1687e28711e503a1f56d392c306b0cbc0a20548180834530d1 \ No newline at end of file
diff --git a/src/select.c b/src/select.c
index f6b48b5a0..fdffe9163 100644
--- a/src/select.c
+++ b/src/select.c
@@ -117,6 +117,7 @@ static void clearSelect(sqlite3 *db, Select *p, int bFree){
void sqlite3SelectDestInit(SelectDest *pDest, int eDest, int iParm){
pDest->eDest = (u8)eDest;
pDest->iSDParm = iParm;
+ pDest->iSDParm2 = 0;
pDest->zAffSdst = 0;
pDest->iSdst = 0;
pDest->nSdst = 0;
@@ -1154,11 +1155,24 @@ static void selectInnerLoop(
break;
}
+ case SRT_Upfrom: {
+ assert( pSort==0 );
+ int i2 = pDest->iSDParm2;
+ int r1 = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord,regResult+(i2<0),nResultCol-(i2<0),r1);
+ if( i2<0 ){
+ sqlite3VdbeAddOp3(v, OP_Insert, iParm, r1, regResult);
+ }else{
+ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regResult, i2);
+ }
+ break;
+ }
+
+#ifndef SQLITE_OMIT_SUBQUERY
/* If we are creating a set for an "expr IN (SELECT ...)" construct,
** then there should be a single item on the stack. Write this
** item into the set table with bogus data.
*/
- case SRT_ISet:
case SRT_Set: {
if( pSort ){
/* At first glance you would think we could optimize out the
@@ -1168,22 +1182,17 @@ static void selectInnerLoop(
pushOntoSorter(
pParse, pSort, p, regResult, regOrig, nResultCol, nPrefixReg);
}else{
- int bITab = (eDest==SRT_ISet);
int r1 = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult+bITab, nResultCol-bITab,
- r1, pDest->zAffSdst, 0
- );
- if( bITab ){
- sqlite3VdbeAddOp3(v, OP_Insert, iParm, r1, regResult);
- }else{
- sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1,regResult,nResultCol);
- }
+ assert( sqlite3Strlen30(pDest->zAffSdst)==nResultCol );
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult, nResultCol,
+ r1, pDest->zAffSdst, nResultCol);
+ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regResult, nResultCol);
sqlite3ReleaseTempReg(pParse, r1);
}
break;
}
-#ifndef SQLITE_OMIT_SUBQUERY
+
/* If any row exist in the result set, record that fact and abort.
*/
case SRT_Exists: {
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 3cbc65cdd..34d6fea7e 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -3188,14 +3188,16 @@ struct Select {
#define SRT_Coroutine 13 /* Generate a single row of result */
#define SRT_Table 14 /* Store result as data with an automatic rowid */
#define SRT_ISet 15 /* Store result as data with rowid */
+#define SRT_Upfrom 16 /* Store result as data with rowid */
/*
** An instance of this object describes where to put of the results of
** a SELECT statement.
*/
struct SelectDest {
- u8 eDest; /* How to dispose of the results. On of SRT_* above. */
+ u8 eDest; /* How to dispose of the results. One of SRT_* above. */
int iSDParm; /* A parameter used by the eDest disposal method */
+ int iSDParm2; /* A second parameter for the eDest disposal method */
int iSdst; /* Base register where results are written */
int nSdst; /* Number of registers allocated */
char *zAffSdst; /* Affinity used when eDest==SRT_Set */
diff --git a/src/update.c b/src/update.c
index 7257b2c6f..80f07d3de 100644
--- a/src/update.c
+++ b/src/update.c
@@ -190,7 +190,7 @@ static void updatePopulateEphTable(
sqlite3Expr(db, TK_ID, pTab->aCol[pPk->aiColumn[i]].zName)
));
}
- eDest = SRT_Set;
+ eDest = SRT_Upfrom;
}else if( pTab->pSelect ){
pList = sqlite3ExprListAppend(pParse, pList,
sqlite3PExpr(pParse, TK_DOT,
@@ -204,7 +204,7 @@ static void updatePopulateEphTable(
sqlite3Expr(db, TK_ID, pTab->zName),
sqlite3Expr(db, TK_ID, "_rowid_")
));
- eDest = IsVirtual(pTab) ? SRT_Table : SRT_ISet;
+ eDest = IsVirtual(pTab) ? SRT_Table : SRT_Upfrom;
}
for(i=0; i<pChanges->nExpr; i++){
pList = sqlite3ExprListAppend(pParse, pList,
@@ -213,6 +213,7 @@ static void updatePopulateEphTable(
}
pSelect = sqlite3SelectNew(pParse, pList, pSrc, pWhere2, 0, 0, 0, 0, 0);
sqlite3SelectDestInit(&dest, eDest, iEph);
+ dest.iSDParm2 = (pPk ? pPk->nKeyCol : -1);
sqlite3Select(pParse, pSelect, &dest);
sqlite3SelectDelete(db, pSelect);
}
@@ -878,7 +879,15 @@ void sqlite3Update(
** documentation.
*/
if( pPk ){
- sqlite3VdbeAddOp4Int(v, OP_NotFound,iDataCur,labelContinue,regKey,nKey);
+ int p3, p4;
+ if( nChangeFrom ){
+ p3 = iPk;
+ p4 = nPk;
+ }else{
+ p3 = regKey;
+ p4 = nKey;
+ }
+ sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, p3, p4);
VdbeCoverage(v);
}else{
sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue,regOldRowid);
diff --git a/test/upfrom2.test b/test/upfrom2.test
new file mode 100644
index 000000000..0c3cf31ef
--- /dev/null
+++ b/test/upfrom2.test
@@ -0,0 +1,119 @@
+# 2020 April 29
+#
+# 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 upfrom2
+
+if 0 {
+do_execsql_test 0.0 {
+ CREATE TABLE t1 (a PRIMARY KEY, b, c) WITHOUT ROWID;
+}
+explain_i { REPLACE INTO t1 VALUES('one', 'two', 'three'); }
+breakpoint
+execsql {
+ REPLACE INTO t1 VALUES('one', 'two', 'three');
+ REPLACE INTO t1 VALUES('one', 'two', 'four');
+}
+do_execsql_test x {
+ SELECT * FROM t1
+} {one two four}
+exit
+}
+
+# Test cases:
+#
+# 1.*: Test that triggers are fired correctly for UPDATE FROM statements,
+# and only once for each row.
+#
+
+foreach {tn wo} {
+ 1 ""
+ 2 "WITHOUT ROWID"
+} {
+ reset_db
+
+ eval [string map [list %WO% $wo %TN% $tn] {
+ do_execsql_test 1.%TN%.0 {
+ CREATE TABLE log(t TEXT);
+ CREATE TABLE t1(x PRIMARY KEY, y, z UNIQUE) %WO%;
+ CREATE INDEX t1y ON t1(y);
+
+ INSERT INTO t1 VALUES(1, 'i', 'one');
+ INSERT INTO t1 VALUES(2, 'ii', 'two');
+ INSERT INTO t1 VALUES(3, 'iii', 'three');
+ INSERT INTO t1 VALUES(4, 'iv', 'four');
+
+ CREATE TRIGGER tr1 BEFORE UPDATE ON t1 BEGIN
+ INSERT INTO log VALUES(old.z || '->' || new.z);
+ END;
+ CREATE TRIGGER tr2 AFTER UPDATE ON t1 BEGIN
+ INSERT INTO log VALUES(old.y || '->' || new.y);
+ END;
+ }
+
+ do_execsql_test 1.%TN%.1 {
+ WITH data(k, v) AS (
+ VALUES(3, 'thirty'), (1, 'ten')
+ )
+ UPDATE t1 SET z=v FROM data WHERE x=k;
+
+ SELECT * FROM t1;
+ SELECT * FROM log;
+ } {
+ 1 i ten 2 ii two 3 iii thirty 4 iv four
+ one->ten i->i
+ three->thirty iii->iii
+ }
+
+ do_execsql_test 1.%TN%.2 {
+ CREATE TABLE t2(a, b);
+ CREATE TABLE t3(k, v);
+
+ INSERT INTO t3 VALUES(5, 'v');
+ INSERT INTO t3 VALUES(12, 'xii');
+
+ INSERT INTO t2 VALUES(2, 12);
+ INSERT INTO t2 VALUES(3, 5);
+
+ DELETE FROM log;
+ UPDATE t1 SET y=v FROM t2, t3 WHERE t1.x=t2.a AND t3.k=t2.b;
+
+ SELECT * FROM t1;
+ SELECT * FROM log;
+ } {
+ 1 i ten 2 xii two 3 v thirty 4 iv four
+ two->two ii->xii
+ thirty->thirty iii->v
+ }
+
+ do_execsql_test 1.%TN%.3 {
+ DELETE FROM log;
+ WITH data(k, v) AS (
+ VALUES(1, 'seven'), (1, 'eight'), (2, 'eleven'), (2, 'twelve')
+ )
+ UPDATE t1 SET z=v FROM data WHERE x=k;
+
+ SELECT * FROM t1;
+ SELECT * FROM log;
+ } {
+ 1 i eight 2 xii twelve 3 v thirty 4 iv four
+ ten->eight i->i
+ two->twelve xii->xii
+ }
+}]
+}
+
+
+
+finish_test
+