From c87d7bede0f0e350fc27c0817ab67788252597e8 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 14 Nov 2024 14:38:16 +0000 Subject: [PATCH] Fix a problem with window functions min() and max() when used with a FILTER clause. Forum post [forum:/forumpost/e9126d554a | e9126d554a]. FossilOrigin-Name: d15fb0f75e64bbfdb8df0c0d0358aafbbd7d5e2048df676dafe1abd5e9917f2a --- manifest | 20 +++++++-------- manifest.uuid | 2 +- src/window.c | 30 +++++++++++----------- test/window8.tcl | 62 ++++++++++++++++++++++++++++++++++++++++++++++ test/window8.test | 63 +++++++++++++++++++++++++++++++++++++++++++++++ test/window9.test | 58 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 210 insertions(+), 25 deletions(-) diff --git a/manifest b/manifest index f4236a85e5..01eec35bff 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Document\sthe\sif\sblock\sat\sthe\send\sof\ssqlite-check-tcl. -D 2024-11-14T12:23:05.149 +C Fix\sa\sproblem\swith\swindow\sfunctions\smin()\sand\smax()\swhen\sused\swith\sa\sFILTER\sclause.\sForum\spost\s[forum:/forumpost/e9126d554a\s|\se9126d554a]. +D 2024-11-14T14:38:16.785 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md e108e1e69ae8e8a59e93c455654b8ac9356a11720d3345df2a4743e9590fb20d @@ -863,7 +863,7 @@ F src/where.c 4de9e7ca5f49e4a21c1d733e2b2fbbc8b62b1a157a58a562c569da84cfcb005b F src/whereInt.h 1e36ec50392f7cc3d93d1152d4338064cd522b87156a0739388b7e273735f0ca F src/wherecode.c 81b9af89f4f85c8097d0da6a31499f015eabc4d3584963d30ba7b7b782e26514 F src/whereexpr.c 0f93a29cabd3a338d09a1f5c6770620a1ac51ec1157f3229502a7e7767c60b6f -F src/window.c 499d48f315a09242dc68f2fac635ed27dcf6bbb0d9ab9084857898c64489e975 +F src/window.c 6c386af5972a58f9a9847bba9d7ca70c4c682391ab8478d94a6e046b22a0dbb3 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627 F test/affinity3.test f094773025eddf31135c7ad4cde722b7696f8eb07b97511f98585addf2a510a9 @@ -2067,9 +2067,9 @@ F test/window5.test d328dd18221217c49c144181975eea17339eaeaf0e9aa558cee3afb84652 F test/window6.test 311de885bd7e453134fa6747680bfb4a1be87c91720bf58703db945891e7d30b F test/window7.tcl 6a1210f05d40ec89c22960213a22cd3f98d4e2f2eb20646c83c8c30d4d76108f F test/window7.test 1d31276961ae7801edc72173edaf7593e3cbc79c06d1f1f09e20d8418af403cd -F test/window8.tcl 5e02e41d9d9a80f597063aed1a381eb19d1d0ef677a4f0df352c5365cf23f79c -F test/window8.test 4ab16817414af0c904abe2ebdf88eb6c2b00058b84f9748c6174ff11fc45f1ed -F test/window9.test 349c71eab4288a1ffc19e2f65872ec2c37e6cf8a1dda2ad300364b7450ae4836 +F test/window8.tcl c57364e64d816f6e26df60437e1202e2c1031c7b818a1a67535d1006862a026a +F test/window8.test 3d931e58802b8ab8063da00f0cf30aa3351640238a952c0efb5a129e2349a4bb +F test/window9.test 7b98a7916dd87763ea35f56ea023e3b29e99744582204ccf2937a3bac411cd4d F test/windowA.test 6d63dc1260daa17141a55007600581778523a8b420629f1282d2acfc36af23be F test/windowB.test aad7c31739999f68a98a813cfd78390918fc70f56d2d925317a1523cab548ecf F test/windowC.test 6fd75f5bb2f1343d34e470e36e68f0ff638d8a42f6aa7d99471261b31a0d42f2 @@ -2199,8 +2199,8 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350 F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7 F tool/warnings.sh 49a486c5069de041aedcbde4de178293e0463ae9918ecad7539eedf0ec77a139 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 6940caa192fa0cc84dbd24191a940aec96c304c68e60ead8f239e85d093e01e0 -R a630c6439d1fbf64015d5e1115d0f02b -U stephan -Z 28b305c9589a667cd87e3823cca8351c +P 6bfd09408b9a51c0cbdb28f901a79c9774da755294d7eb67d88e4c42c5652830 +R 4dbd1e8b72bd923323c250178cad8662 +U dan +Z 283ba5d1ed32af60aeb437d70b951d46 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 258ecf84b0..a40565627e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6bfd09408b9a51c0cbdb28f901a79c9774da755294d7eb67d88e4c42c5652830 +d15fb0f75e64bbfdb8df0c0d0358aafbbd7d5e2048df676dafe1abd5e9917f2a diff --git a/src/window.c b/src/window.c index d4083beeb3..acc6ea8546 100644 --- a/src/window.c +++ b/src/window.c @@ -1670,6 +1670,7 @@ static void windowAggStep( int regArg; int nArg = pWin->bExprArgs ? 0 : windowArgCount(pWin); int i; + int addrIf = 0; assert( bInverse==0 || pWin->eStart!=TK_UNBOUNDED ); @@ -1686,6 +1687,18 @@ static void windowAggStep( } regArg = reg; + if( pWin->pFilter ){ + int regTmp; + assert( ExprUseXList(pWin->pOwner) ); + assert( pWin->bExprArgs || !nArg ||nArg==pWin->pOwner->x.pList->nExpr ); + assert( pWin->bExprArgs || nArg ||pWin->pOwner->x.pList==0 ); + regTmp = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp); + addrIf = sqlite3VdbeAddOp3(v, OP_IfNot, regTmp, 0, 1); + VdbeCoverage(v); + sqlite3ReleaseTempReg(pParse, regTmp); + } + if( pMWin->regStartRowid==0 && (pFunc->funcFlags & SQLITE_FUNC_MINMAX) && (pWin->eStart!=TK_UNBOUNDED) @@ -1705,25 +1718,13 @@ static void windowAggStep( } sqlite3VdbeJumpHere(v, addrIsNull); }else if( pWin->regApp ){ + assert( pWin->pFilter==0 ); assert( pFunc->zName==nth_valueName || pFunc->zName==first_valueName ); assert( bInverse==0 || bInverse==1 ); sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1-bInverse, 1); }else if( pFunc->xSFunc!=noopStepFunc ){ - int addrIf = 0; - if( pWin->pFilter ){ - int regTmp; - assert( ExprUseXList(pWin->pOwner) ); - assert( pWin->bExprArgs || !nArg ||nArg==pWin->pOwner->x.pList->nExpr ); - assert( pWin->bExprArgs || nArg ||pWin->pOwner->x.pList==0 ); - regTmp = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp); - addrIf = sqlite3VdbeAddOp3(v, OP_IfNot, regTmp, 0, 1); - VdbeCoverage(v); - sqlite3ReleaseTempReg(pParse, regTmp); - } - if( pWin->bExprArgs ){ int iOp = sqlite3VdbeCurrentAddr(v); int iEnd; @@ -1754,8 +1755,9 @@ static void windowAggStep( if( pWin->bExprArgs ){ sqlite3ReleaseTempRange(pParse, regArg, nArg); } - if( addrIf ) sqlite3VdbeJumpHere(v, addrIf); } + + if( addrIf ) sqlite3VdbeJumpHere(v, addrIf); } } diff --git a/test/window8.tcl b/test/window8.tcl index 69ad0ad263..fcb18249ae 100644 --- a/test/window8.tcl +++ b/test/window8.tcl @@ -489,6 +489,68 @@ execsql_test 9.2 { FROM t1 } +========== + +execsql_test 10.0 { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(a INTEGER, b INTEGER); + INSERT INTO t1 VALUES (10, 1), + (20, -1), + (5, 2), + (15, 0), + (25, 3); +} + +execsql_test 10.1 { + SELECT + a, b, MIN(a) FILTER(WHERE b > 0) OVER win + FROM t1 + WINDOW win AS (ORDER BY a ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING); +} + +execsql_test 10.2 { + SELECT + a, b, MIN(a) FILTER(WHERE b > 0) OVER win + FROM t1 + WINDOW win AS (); +} + +execsql_test 10.3 { + SELECT + a, b, MIN(a) FILTER(WHERE b > 0) OVER win + FROM t1 + WINDOW win AS (ORDER BY a); +} + +execsql_test 10.4 { + SELECT + a, b, MIN(a) OVER win + FROM t1 + WINDOW win AS (ORDER BY a ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING); +} + +========== + +execsql_test 11.0 { + DROP TABLE IF EXISTS t2; + CREATE TABLE t2(a INTEGER, b INTEGER); + INSERT INTO t2 VALUES(1, 12); + INSERT INTO t2 VALUES(2, 10); + INSERT INTO t2 VALUES(3, 15); + INSERT INTO t2 VALUES(4, 22); + INSERT INTO t2 VALUES(5, 1); + INSERT INTO t2 VALUES(6, 4); + INSERT INTO t2 VALUES(7, 7); + INSERT INTO t2 VALUES(8, 6); + INSERT INTO t2 VALUES(9, 22); + INSERT INTO t2 VALUES(10, 2); +} + +execsql_test 11.1 { + SELECT a, min(b) FILTER (WHERE a%2 != 0) OVER win + FROM t2 + WINDOW win AS (ORDER BY a ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING); +} finish_test diff --git a/test/window8.test b/test/window8.test index 0c5d39badb..b1b28f1c2f 100644 --- a/test/window8.test +++ b/test/window8.test @@ -6540,4 +6540,67 @@ do_execsql_test 9.2 { FROM t1 } {} +#========================================================================== + +do_execsql_test 10.0 { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(a INTEGER, b INTEGER); + INSERT INTO t1 VALUES (10, 1), + (20, -1), + (5, 2), + (15, 0), + (25, 3); +} {} + +do_execsql_test 10.1 { + SELECT + a, b, MIN(a) FILTER(WHERE b > 0) OVER win + FROM t1 + WINDOW win AS (ORDER BY a ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING); +} {5 2 5 10 1 5 15 0 10 20 -1 25 25 3 25} + +do_execsql_test 10.2 { + SELECT + a, b, MIN(a) FILTER(WHERE b > 0) OVER win + FROM t1 + WINDOW win AS (); +} {10 1 5 20 -1 5 5 2 5 15 0 5 25 3 5} + +do_execsql_test 10.3 { + SELECT + a, b, MIN(a) FILTER(WHERE b > 0) OVER win + FROM t1 + WINDOW win AS (ORDER BY a); +} {5 2 5 10 1 5 15 0 5 20 -1 5 25 3 5} + +do_execsql_test 10.4 { + SELECT + a, b, MIN(a) OVER win + FROM t1 + WINDOW win AS (ORDER BY a ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING); +} {5 2 5 10 1 5 15 0 10 20 -1 15 25 3 20} + +#========================================================================== + +do_execsql_test 11.0 { + DROP TABLE IF EXISTS t2; + CREATE TABLE t2(a INTEGER, b INTEGER); + INSERT INTO t2 VALUES(1, 12); + INSERT INTO t2 VALUES(2, 10); + INSERT INTO t2 VALUES(3, 15); + INSERT INTO t2 VALUES(4, 22); + INSERT INTO t2 VALUES(5, 1); + INSERT INTO t2 VALUES(6, 4); + INSERT INTO t2 VALUES(7, 7); + INSERT INTO t2 VALUES(8, 6); + INSERT INTO t2 VALUES(9, 22); + INSERT INTO t2 VALUES(10, 2); +} {} + +do_execsql_test 11.1 { + SELECT a, min(b) FILTER (WHERE a%2 != 0) OVER win + FROM t2 + WINDOW win AS (ORDER BY a ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING); +} {1 12 2 12 3 1 4 1 5 1 6 1 7 1 8 7 9 7 10 22} + finish_test diff --git a/test/window9.test b/test/window9.test index 4b8e4fa58f..0548624f82 100644 --- a/test/window9.test +++ b/test/window9.test @@ -282,4 +282,62 @@ do_catchsql_test 9.1 { FROM t1 } {1 {frame ending offset must be a non-negative number}} +#-------------------------------------------------------------------------- +reset_db + +do_execsql_test 10.0 { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 'a'); + INSERT INTO t1 VALUES(2, 'b'); + INSERT INTO t1 VALUES(3, 'c'); + INSERT INTO t1 VALUES(4, 'd'); + INSERT INTO t1 VALUES(5, 'e'); + INSERT INTO t1 VALUES(6, 'f'); +} + +do_execsql_test 10.1 { + SELECT a, min(b) OVER win + FROM t1 + WINDOW win AS (ORDER BY a ROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING) +} { + 1 a + 2 a + 3 a + 4 b + 5 c + 6 d +} + +do_execsql_test 10.2 { + SELECT a, min(b) FILTER (WHERE a%2) OVER win + FROM t1 + WINDOW win AS (ORDER BY a ROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING) +} { + 1 a + 2 a + 3 a + 4 c + 5 c + 6 e +} + +do_execsql_test 10.3 { + SELECT a, min(b) FILTER (WHERE (a%2)=0) OVER win + FROM t1 + WINDOW win AS (ORDER BY a ROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING) +} { + 1 b + 2 b + 3 b + 4 b + 5 d + 6 d +} + +do_catchsql_test 10.4 { + SELECT a, nth_value(b, 1) FILTER (WHERE (a%2)=0) OVER win + FROM t1 + WINDOW win AS (ORDER BY a ROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING) +} {1 {FILTER clause may only be used with aggregate window functions}} + finish_test