mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-11 01:42:22 +03:00
Fix problems with using LIMIT and FROM clauses as part of single UPDATE statement.
FossilOrigin-Name: b717dc3c5fafb9b86a141e7ecffc030fd9b36aa57a0b3e5200d64ad23a0aa13d
This commit is contained in:
20
manifest
20
manifest
@@ -1,5 +1,5 @@
|
||||
C Fix\svarious\sbugs\sin\snew\sfeature\son\sthis\sbranch.
|
||||
D 2020-04-29T17:41:29.116
|
||||
C Fix\sproblems\swith\susing\sLIMIT\sand\sFROM\sclauses\sas\spart\sof\ssingle\sUPDATE\sstatement.
|
||||
D 2020-04-29T20:11:01.860
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||
@@ -530,14 +530,14 @@ F src/pragma.h 9473160d220416456b40f27323bb4b316d4e4e08ffbf8bf88c5f7045d49c38e5
|
||||
F src/prepare.c 8d4d6c8aa6afefc48027c54b41cdf134b4d6bc2fc4badbe483ad7fd9e1728a28
|
||||
F src/printf.c 9be6945837c839ba57837b4bc3af349eba630920fa5532aa518816defe42a7d4
|
||||
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
|
||||
F src/resolve.c d36a2b1639e1c33d7b508abfd3452a63e7fd81737f6f3940bfef085fca6f21f4
|
||||
F src/resolve.c 05471a183504f72aedf249851d94ad6feeac4d02be044fefdb4b42a8a6e13e42
|
||||
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
|
||||
F src/select.c 88ffd4e1a2b6bf221e3aaad026885485c7f2fe9291201f5e7d0656a3603b6bbe
|
||||
F src/select.c 51882f7b24503163414ddcdc33705ea4f74707da70d7d0545e52aa8dff9f8983
|
||||
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 3caabf4700e4d98bf6d4d3d60fab71c808d43ca9a98ee9674342f99d1a96d382
|
||||
F src/sqliteInt.h cea0d39df23798779db3cd0a2aa9cecc769ba9b13f58d5958e4a9e9325fa1b58
|
||||
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 3dda0590ff4a87f29828cd21840f11e6ddc853061a94f4aebc2af8f37d0ba92c
|
||||
F src/update.c 15c1e9b6191be56d0752235f38e8c4e0eddf7b8a26e31992631a848cf3a87517
|
||||
F src/upsert.c 2920de71b20f04fe25eb00b655d086f0ba60ea133c59d7fa3325c49838818e78
|
||||
F src/utf.c 95fb6e03a5ca679045c5adccd05380f0addccabef5911abddcb06af069500ab7
|
||||
F src/util.c 3b6cedf7a0c69bd6e1acce832873952d416212d6293b18d03064e07d7a9b5118
|
||||
@@ -1618,7 +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/upfrom2.test 1c0f8d3b963304f0feb04ca172484942a15ffca975cd25463fdfd4614d1245d0
|
||||
F test/upsert1.test 88f9e258c6a0eeeb85937b08831e8daad440ba41f125af48439e9d33f266fb18
|
||||
F test/upsert2.test 9c3cdbb1a890227f6504ce4b0e3de68f4cdfa16bb21d8641208a9239896c5a09
|
||||
F test/upsert3.test 88d7d590a1948a9cb6eac1b54b0642f67a9f35a1fc0f19b200e97d5d39e3179c
|
||||
@@ -1865,7 +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 f353a1a613bb7ad8cedcda377a7fe6fd05ee03b1f50665b00b84a868a71c5bec
|
||||
R ff3249230b7ba7b12347209dea6f6d16
|
||||
P 823ba94e29dece1687e28711e503a1f56d392c306b0cbc0a20548180834530d1
|
||||
R c7060e5765cacf17cc72ccb6e64f7f11
|
||||
U dan
|
||||
Z 37eb2f9d556f45297467e2642dd5f97e
|
||||
Z 2cbbaf692ee6084a41961733ac9445a3
|
||||
|
||||
@@ -1 +1 @@
|
||||
823ba94e29dece1687e28711e503a1f56d392c306b0cbc0a20548180834530d1
|
||||
b717dc3c5fafb9b86a141e7ecffc030fd9b36aa57a0b3e5200d64ad23a0aa13d
|
||||
@@ -756,7 +756,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
case TK_ROW: {
|
||||
SrcList *pSrcList = pNC->pSrcList;
|
||||
struct SrcList_item *pItem;
|
||||
assert( pSrcList && pSrcList->nSrc==1 );
|
||||
assert( pSrcList && pSrcList->nSrc>=1 );
|
||||
pItem = pSrcList->a;
|
||||
assert( HasRowid(pItem->pTab) && pItem->pTab->pSelect==0 );
|
||||
pExpr->op = TK_COLUMN;
|
||||
|
||||
20
src/select.c
20
src/select.c
@@ -1006,7 +1006,8 @@ static void selectInnerLoop(
|
||||
testcase( eDest==SRT_Coroutine );
|
||||
testcase( eDest==SRT_Output );
|
||||
assert( eDest==SRT_Set || eDest==SRT_Mem
|
||||
|| eDest==SRT_Coroutine || eDest==SRT_Output );
|
||||
|| eDest==SRT_Coroutine || eDest==SRT_Output
|
||||
|| eDest==SRT_Upfrom );
|
||||
}
|
||||
sRowLoadInfo.regResult = regResult;
|
||||
sRowLoadInfo.ecelFlags = ecelFlags;
|
||||
@@ -1156,7 +1157,10 @@ static void selectInnerLoop(
|
||||
}
|
||||
|
||||
case SRT_Upfrom: {
|
||||
assert( pSort==0 );
|
||||
if( pSort ){
|
||||
pushOntoSorter(
|
||||
pParse, pSort, p, regResult, regOrig, nResultCol, nPrefixReg);
|
||||
}else{
|
||||
int i2 = pDest->iSDParm2;
|
||||
int r1 = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord,regResult+(i2<0),nResultCol-(i2<0),r1);
|
||||
@@ -1165,6 +1169,7 @@ static void selectInnerLoop(
|
||||
}else{
|
||||
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regResult, i2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1600,6 +1605,17 @@ static void generateSortTail(
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case SRT_Upfrom: {
|
||||
int i2 = pDest->iSDParm2;
|
||||
int r1 = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord,regRow+(i2<0),nColumn-(i2<0),r1);
|
||||
if( i2<0 ){
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, iParm, r1, regRow);
|
||||
}else{
|
||||
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regRow, i2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assert( eDest==SRT_Output || eDest==SRT_Coroutine );
|
||||
testcase( eDest==SRT_Output );
|
||||
|
||||
@@ -3187,8 +3187,7 @@ struct Select {
|
||||
#define SRT_EphemTab 12 /* Create transient tab and store like SRT_Table */
|
||||
#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 */
|
||||
#define SRT_Upfrom 15 /* Store result as data with rowid */
|
||||
|
||||
/*
|
||||
** An instance of this object describes where to put of the results of
|
||||
|
||||
43
src/update.c
43
src/update.c
@@ -164,7 +164,9 @@ static void updatePopulateEphTable(
|
||||
Index *pPk, /* PK if table 0 is WITHOUT ROWID */
|
||||
ExprList *pChanges, /* List of expressions to return */
|
||||
SrcList *pTabList, /* List of tables to select from */
|
||||
Expr *pWhere /* WHERE clause for query */
|
||||
Expr *pWhere, /* WHERE clause for query */
|
||||
ExprList *pOrderBy,
|
||||
Expr *pLimit
|
||||
){
|
||||
int i;
|
||||
sqlite3 *db = pParse->db;
|
||||
@@ -174,6 +176,9 @@ static void updatePopulateEphTable(
|
||||
Table *pTab = pTabList->a[0].pTab;
|
||||
SrcList *pSrc = sqlite3SrcListDup(db, pTabList, 0);
|
||||
Expr *pWhere2 = sqlite3ExprDup(db, pWhere, 0);
|
||||
Expr *pLimit2 = sqlite3ExprDup(db, pLimit, 0);
|
||||
ExprList *pOrderBy2 = sqlite3ExprListDup(db, pOrderBy, 0);
|
||||
ExprList *pGroupBy = 0;
|
||||
int eDest;
|
||||
|
||||
assert( pTabList->nSrc>1 );
|
||||
@@ -184,11 +189,16 @@ static void updatePopulateEphTable(
|
||||
}
|
||||
if( pPk ){
|
||||
for(i=0; i<pPk->nKeyCol; i++){
|
||||
pList = sqlite3ExprListAppend(pParse, pList,
|
||||
sqlite3PExpr(pParse, TK_DOT,
|
||||
Expr *pNew = sqlite3PExpr(pParse, TK_DOT,
|
||||
sqlite3Expr(db, TK_ID, pTab->zName),
|
||||
sqlite3Expr(db, TK_ID, pTab->aCol[pPk->aiColumn[i]].zName)
|
||||
));
|
||||
);
|
||||
if( pLimit ){
|
||||
pGroupBy = sqlite3ExprListAppend(pParse, pGroupBy,
|
||||
sqlite3ExprDup(db, pNew, 0)
|
||||
);
|
||||
}
|
||||
pList = sqlite3ExprListAppend(pParse, pList, pNew);
|
||||
}
|
||||
eDest = SRT_Upfrom;
|
||||
}else if( pTab->pSelect ){
|
||||
@@ -199,19 +209,24 @@ static void updatePopulateEphTable(
|
||||
));
|
||||
eDest = SRT_Table;
|
||||
}else{
|
||||
pList = sqlite3ExprListAppend(pParse, pList,
|
||||
sqlite3PExpr(pParse, TK_DOT,
|
||||
sqlite3Expr(db, TK_ID, pTab->zName),
|
||||
sqlite3Expr(db, TK_ID, "_rowid_")
|
||||
));
|
||||
eDest = IsVirtual(pTab) ? SRT_Table : SRT_Upfrom;
|
||||
pList = sqlite3ExprListAppend(pParse, pList,
|
||||
sqlite3PExpr(pParse, TK_ROW, 0, 0)
|
||||
);
|
||||
if( pLimit ){
|
||||
pGroupBy = sqlite3ExprListAppend(pParse, pGroupBy,
|
||||
sqlite3PExpr(pParse, TK_ROW, 0, 0)
|
||||
);
|
||||
}
|
||||
}
|
||||
for(i=0; i<pChanges->nExpr; i++){
|
||||
pList = sqlite3ExprListAppend(pParse, pList,
|
||||
sqlite3ExprDup(db, pChanges->a[i].pExpr, 0)
|
||||
);
|
||||
}
|
||||
pSelect = sqlite3SelectNew(pParse, pList, pSrc, pWhere2, 0, 0, 0, 0, 0);
|
||||
pSelect = sqlite3SelectNew(
|
||||
pParse, pList, pSrc, pWhere2, pGroupBy, 0, pOrderBy2, 0, pLimit2
|
||||
);
|
||||
sqlite3SelectDestInit(&dest, eDest, iEph);
|
||||
dest.iSDParm2 = (pPk ? pPk->nKeyCol : -1);
|
||||
sqlite3Select(pParse, pSelect, &dest);
|
||||
@@ -323,7 +338,7 @@ void sqlite3Update(
|
||||
assert( nChangeFrom==0 || pUpsert==0 );
|
||||
|
||||
#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
||||
if( !isView ){
|
||||
if( !isView && nChangeFrom==0 ){
|
||||
pWhere = sqlite3LimitWhere(
|
||||
pParse, pTabList, pWhere, pOrderBy, pLimit, "UPDATE"
|
||||
);
|
||||
@@ -608,7 +623,9 @@ void sqlite3Update(
|
||||
addrOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nEphCol);
|
||||
if( pPk ) sqlite3VdbeSetP4KeyInfo(pParse, pPk);
|
||||
if( nChangeFrom ){
|
||||
updatePopulateEphTable(pParse, iEph, pPk, pChanges, pTabList, pWhere);
|
||||
updatePopulateEphTable(
|
||||
pParse, iEph, pPk, pChanges, pTabList, pWhere, pOrderBy, pLimit
|
||||
);
|
||||
#ifndef SQLITE_OMIT_SUBQUERY
|
||||
if( isView ) iDataCur = iEph;
|
||||
#endif
|
||||
@@ -1148,7 +1165,7 @@ static void updateVirtualTable(
|
||||
}
|
||||
}
|
||||
|
||||
updatePopulateEphTable(pParse, ephemTab, 0, pList, pSrc, pWhere);
|
||||
updatePopulateEphTable(pParse, ephemTab, 0, pList, pSrc, pWhere, 0, 0);
|
||||
sqlite3ExprListDelete(db, pList);
|
||||
eOnePass = ONEPASS_OFF;
|
||||
}else{
|
||||
|
||||
@@ -14,26 +14,14 @@ 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.
|
||||
# and only once for each row. Except for INSTEAD OF triggers on
|
||||
# views - these are fired once for each row returned by the join,
|
||||
# including duplicates.
|
||||
#
|
||||
# 2.*: Test adding ORDER BY and LIMIT clauses with UPDATE FROM statements.
|
||||
#
|
||||
|
||||
foreach {tn wo} {
|
||||
@@ -110,9 +98,75 @@ foreach {tn wo} {
|
||||
ten->eight i->i
|
||||
two->twelve xii->xii
|
||||
}
|
||||
|
||||
do_test 1.%TN%.4 { db changes } {2}
|
||||
|
||||
do_execsql_test 1.%TN%.5 {
|
||||
CREATE VIEW v1 AS SELECT * FROM t1;
|
||||
CREATE TRIGGER v1tr INSTEAD OF UPDATE ON v1 BEGIN
|
||||
UPDATE t1 SET y=new.y, z=new.z WHERE x=new.x;
|
||||
END;
|
||||
|
||||
DELETE FROM log;
|
||||
WITH data(k, v) AS (
|
||||
VALUES(3, 'thirteen'), (3, 'fourteen'), (4, 'fifteen'), (4, 'sixteen')
|
||||
)
|
||||
UPDATE v1 SET z=v FROM data WHERE x=k;
|
||||
}
|
||||
|
||||
do_execsql_test 1.%TN%.6 {
|
||||
SELECT * FROM v1;
|
||||
SELECT * FROM log;
|
||||
} {
|
||||
1 i eight 2 xii twelve 3 v fourteen 4 iv sixteen
|
||||
thirty->thirteen v->v
|
||||
thirteen->fourteen v->v
|
||||
four->fifteen iv->iv
|
||||
fifteen->sixteen iv->iv
|
||||
}
|
||||
|
||||
}]
|
||||
}
|
||||
|
||||
ifcapable update_delete_limit {
|
||||
foreach {tn wo} {
|
||||
1 ""
|
||||
2 "WITHOUT ROWID"
|
||||
} {
|
||||
reset_db
|
||||
|
||||
eval [string map [list %WO% $wo %TN% $tn] {
|
||||
do_execsql_test 2.%TN%.1 {
|
||||
CREATE TABLE x1(a INTEGER PRIMARY KEY, b) %WO%;
|
||||
INSERT INTO x1 VALUES
|
||||
(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four'),
|
||||
(5, 'five'), (6, 'six'), (7, 'seven'), (8, 'eight');
|
||||
}
|
||||
|
||||
do_execsql_test 2.%TN%.2 {
|
||||
CREATE TABLE data1(x, y);
|
||||
INSERT INTO data1 VALUES
|
||||
(1, 'eleven'), (1, 'twenty-one'), (2, 'twelve'), (2, 'twenty-two'),
|
||||
(3, 'thirteen'), (3, 'twenty-three'), (4, 'fourteen'), (4, 'twenty-four');
|
||||
}
|
||||
|
||||
do_execsql_test 2.%TN%.3 {
|
||||
UPDATE x1 SET b=y FROM data1 WHERE a=x ORDER BY a LIMIT 3;
|
||||
SELECT * FROM x1;
|
||||
} {
|
||||
1 eleven 2 twelve 3 thirteen 4 four 5 five 6 six 7 seven 8 eight
|
||||
}
|
||||
|
||||
do_execsql_test 2.%TN%.4 {
|
||||
UPDATE x1 SET b=b||y FROM data1 WHERE a=x ORDER BY b LIMIT 3;
|
||||
SELECT * FROM x1;
|
||||
} {
|
||||
1 eleveneleven 2 twelve 3 thirteenthirteen 4 fourfourteen
|
||||
5 five 6 six 7 seven 8 eight
|
||||
}
|
||||
|
||||
}]
|
||||
}}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
Reference in New Issue
Block a user