From: dan Date: Wed, 3 Dec 2025 14:39:25 +0000 (+0000) Subject: Fix a problem that could occur when a scalar subquery had both a DISTINCT and OFFSET... X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fheads%2Fdistinct-offset-subquery-fix;p=thirdparty%2Fsqlite.git Fix a problem that could occur when a scalar subquery had both a DISTINCT and OFFSET clause. First reported by [forum:41bd7af485 | forum post 41bd7af485]. FossilOrigin-Name: 496bbff910b73775b16c1ccb95dadf37416d3f3032722a1140f2a9134666d7f1 --- diff --git a/manifest b/manifest index 73febf3f63..9e978b4481 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Clarification\sof\scomment\stext\sin\sexpr.c.\s\sNo\schanges\sto\scode. -D 2025-12-03T12:07:35.684 +C Fix\sa\sproblem\sthat\scould\soccur\swhen\sa\sscalar\ssubquery\shad\sboth\sa\sDISTINCT\sand\sOFFSET\sclause.\sFirst\sreported\sby\s[forum:41bd7af485\s|\sforum\spost\s41bd7af485]. +D 2025-12-03T14:39:25.881 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -687,7 +687,7 @@ F src/date.c e19e0cfff9a41bfdd884c655755f6f00bca4c1a22272b56e0dd6667b7ea893a2 F src/dbpage.c c9ea81c11727f27e02874611e92773e68e2a90a875ef2404b084564c235fd91f F src/dbstat.c 73362c0df0f40ad5523a6f5501224959d0976757b511299bf892313e79d14f5c F src/delete.c 03a77ba20e54f0f42ebd8eddf15411ed6bdb06a2c472ac4b6b336521bf7cea42 -F src/expr.c 684f391581c62cb0ff6e37e05b5642e5a5c938f07897fbf6d0ae58de96a2c555 +F src/expr.c 0f65d201a4f1f3ff1bb49a40af896fd7082a7f28dae138d0b9150d94b0db1e5b F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c 928ed2517e8732113d2b9821aa37af639688d752f4ea9ac6e0e393d713eeb76f F src/func.c 0b802107498048d3dcac0b757720bcb8506507ce02159e213ab8161458eb293b @@ -736,7 +736,7 @@ F src/printf.c b1b29b5e58e1530d5daeee5963d3c318d8ab2d7e38437580e28755753e0c1ded F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 5616fbcf3b833c7c705b24371828215ad0925d0c0073216c4f153348d5753f0a F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 -F src/select.c 6a509cddd815d64f6141e539fff633a518a393772a44dffb4490f7fc3f0d83a9 +F src/select.c e556448217b0d6d42f47cfa79bc9b667a9cb26cedce77cf41f91be2bd535a6ca F src/shell.c.in 62b286951404fd72c116bb3b96b5ee9330de4dc4c8753ca33967ca1a47b3b972 F src/sqlite.h.in 706cacea5308b0244fb6cec92e08310fb427a125375c64137cc1f878ae4cf5c0 F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479 @@ -1675,7 +1675,7 @@ F test/strict1.test a7f9091603fe71cdc62baab0766684cba12a97ec69bfbb70be965532669c F test/strict2.test b22c7a98b5000aef937f1990776497f0e979b1a23bc4f63e2d53b00e59b20070 F test/subjournal.test 8d4e2572c0ee9a15549f0d8e40863161295107e52f07a3e8012a2e1fdd093c49 F test/subquery.test 23087f9b1c15ab9cc5231d04946bdebc51db527c95eb9d7434a2222127e17a84 -F test/subquery2.test 5f06ec2dbce42a3f595ab1b73b146592f9ce001cd4ff023d887d643d3560c281 +F test/subquery2.test ab96ff3fa9c4e3dce0d699f74e61c50250ed4335bc8f400e127707d552a8999e F test/subselect.test 0966aa8e720224dbd6a5e769a3ec2a723e332303 F test/substr.test a673e3763e247e9b5e497a6cacbaf3da2bd8ec8921c0677145c109f2e633f36b F test/subtype1.test 96fd2a59bfc845c955b5f339d23b37ef4d50de5f8a04acd1450a68605fa2e3e7 @@ -2183,8 +2183,11 @@ F tool/version-info.c 33d0390ef484b3b1cb685d59362be891ea162123cea181cb8e6d2cf6dd F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7 F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P bccc0d65b0fb617f6c8eb111b6717d3ad419f80d3495dc5c6a69ccd0771b3d9d -R b657241f08cf4f1a9f15fdda4d21e254 -U drh -Z 32d06a64dcd8b620cb1a299ac970a7bc +P afe5ee64f1cde4945c878220b029f2a22578c2bccda4fddc005c4e6a4718c6cd +R 047e6324568940b4556b75113b868f2a +T *branch * distinct-offset-subquery-fix +T *sym-distinct-offset-subquery-fix * +T -sym-trunk * +U dan +Z 769c0bdc01c64bb06852dc79c8e1f0eb # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.tags b/manifest.tags index bec971799f..90125c2838 100644 --- a/manifest.tags +++ b/manifest.tags @@ -1,2 +1,2 @@ -branch trunk -tag trunk +branch distinct-offset-subquery-fix +tag distinct-offset-subquery-fix diff --git a/manifest.uuid b/manifest.uuid index 87aad9cf83..dfe56a33cb 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -afe5ee64f1cde4945c878220b029f2a22578c2bccda4fddc005c4e6a4718c6cd +496bbff910b73775b16c1ccb95dadf37416d3f3032722a1140f2a9134666d7f1 diff --git a/src/expr.c b/src/expr.c index 2eb271cec5..8a47b978da 100644 --- a/src/expr.c +++ b/src/expr.c @@ -3891,9 +3891,22 @@ int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ pParse->nMem += nReg; if( pExpr->op==TK_SELECT ){ dest.eDest = SRT_Mem; - dest.iSdst = dest.iSDParm; + if( (pSel->selFlags&SF_Distinct) && pSel->pLimit && pSel->pLimit->pRight ){ + /* If there is both a DISTINCT and an OFFSET clause, then allocate + ** a separate dest.iSdst array for sqlite3Select() and other + ** routines to populate. In this case results will be copied over + ** into the dest.iSDParm array only after OFFSET processing. This + ** ensures that in the case where OFFSET excludes all rows, the + ** dest.iSDParm array is not left populated with the contents of the + ** last row visited - it should be all NULLs if all rows were + ** excluded by OFFSET. */ + dest.iSdst = pParse->nMem+1; + pParse->nMem += nReg; + }else{ + dest.iSdst = dest.iSDParm; + } dest.nSdst = nReg; - sqlite3VdbeAddOp3(v, OP_Null, 0, dest.iSDParm, dest.iSDParm+nReg-1); + sqlite3VdbeAddOp3(v, OP_Null, 0, dest.iSDParm, pParse->nMem); VdbeComment((v, "Init subquery result")); }else{ dest.eDest = SRT_Exists; diff --git a/src/select.c b/src/select.c index 0a00e769ba..4aac5a27b1 100644 --- a/src/select.c +++ b/src/select.c @@ -1445,9 +1445,14 @@ static void selectInnerLoop( assert( nResultCol<=pDest->nSdst ); pushOntoSorter( pParse, pSort, p, regResult, regOrig, nResultCol, nPrefixReg); + pDest->iSDParm = regResult; }else{ assert( nResultCol==pDest->nSdst ); - assert( regResult==iParm ); + if( regResult!=iParm ){ + /* This occurs in cases where the SELECT had both a DISTINCT and + ** an OFFSET clause. */ + sqlite3VdbeAddOp3(v, OP_Copy, regResult, iParm, nResultCol-1); + } /* The LIMIT clause will jump out of the loop for us */ } break; diff --git a/test/subquery2.test b/test/subquery2.test index 8513dc75c8..99a1e903f8 100644 --- a/test/subquery2.test +++ b/test/subquery2.test @@ -215,4 +215,72 @@ do_execsql_test 5.1 { SELECT ( SELECT y FROM t2 WHERE x = y ORDER BY y, z) FROM t1; } {ALFKI ANATR} +#------------------------------------------------------------------------- +reset_db +do_execsql_test 6.0 { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(1234); +} + +do_execsql_test 6.1 { + SELECT DISTINCT 'string' FROM t1 LIMIT 1 OFFSET 5; +} + +do_execsql_test 6.2 { + SELECT ( + SELECT 'string' FROM t1 LIMIT 1 OFFSET 5 + ); +} {{}} + +do_execsql_test 6.3 { + SELECT ( + SELECT DISTINCT 'string' FROM t1 LIMIT 1 OFFSET 5 + ); +} {{}} + +do_execsql_test 6.4 { + SELECT ( + SELECT DISTINCT 'string' FROM t1 ORDER BY 1 LIMIT 1 OFFSET 5 + ); +} {{}} + +do_execsql_test 6.5 { + SELECT ( + SELECT 'string' FROM t1 ORDER BY 1 LIMIT 1 OFFSET 5 + ); +} {{}} + +do_execsql_test 6.6 { + SELECT (SELECT DISTINCT x, x FROM t1 LIMIT 1 OFFSET 5)==(1234, 1234) +} {{}} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 7.0 { + CREATE TABLE t1(x); + CREATE INDEX i1 ON t1(x); + INSERT INTO t1 VALUES(1234); +} + +do_execsql_test 7.1 { + SELECT ( + SELECT DISTINCT 'string' FROM t1 ORDER BY x LIMIT 1 OFFSET 5 + ); +} {{}} + +do_execsql_test 7.2 { + DROP INDEX i1; + CREATE UNIQUE INDEX i1 ON t1(x); +} + +do_execsql_test 7.3 { + SELECT ( + SELECT DISTINCT x FROM t1 ORDER BY 1 LIMIT 1 OFFSET 5 + ); +} {{}} + +do_execsql_test 7.4 { + SELECT (SELECT DISTINCT x FROM t1 ORDER BY +x LIMIT 1 OFFSET 0); +} {1234} + finish_test