From: drh <> Date: Tue, 9 Dec 2025 13:29:08 +0000 (+0000) Subject: Fix an incorrect answer that might arise if a scalar query is both X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=55385fb79cc7b6d92635694f0f7a77d7fb382580;p=thirdparty%2Fsqlite.git Fix an incorrect answer that might arise if a scalar query is both DISTINCT and contains an OFFSET clause. FossilOrigin-Name: 35b306565a10c16737ee433728ca188852f01c12dfae0cc9212d21db932486fb --- diff --git a/manifest b/manifest index ca70b087b2..99856fc853 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Increase\sthe\spatch\snumber\sto\s3.51.2 -D 2025-12-05T01:43:23.648 +C Fix\san\sincorrect\sanswer\sthat\smight\sarise\sif\sa\sscalar\squery\sis\sboth\nDISTINCT\sand\scontains\san\sOFFSET\sclause. +D 2025-12-09T13:29:08.680 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -686,7 +686,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 17b0cbe08e004c1653030f5de9b6b050e84feaa112239f7f576af2dc5e53a5fb +F src/expr.c 28b1cc3d2f147cc888703d5482f9581f17656d02abfa331c34370cb3350776be F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c 928ed2517e8732113d2b9821aa37af639688d752f4ea9ac6e0e393d713eeb76f F src/func.c 0b802107498048d3dcac0b757720bcb8506507ce02159e213ab8161458eb293b @@ -735,7 +735,7 @@ F src/printf.c 7297c2aeed4d90d80c5ba82920d9e57b7bfad04b3466be1d7e042db382fe296e F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 5616fbcf3b833c7c705b24371828215ad0925d0c0073216c4f153348d5753f0a F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 -F src/select.c ba9cd07ffa3277883c1986085f6ddc4320f4d35d5f212ab58df79a7ecc1a576a +F src/select.c 016cb24f1d576b919ee4ba53b21ba1a9976bd5837371b83ca73da82003063633 F src/shell.c.in 223e3703657f5e66c136521a32fc8cc9a7dbbe6b1ade6fd47457e78c38f33e6e F src/sqlite.h.in c0979f9ac1f5be887397dd2a0bb485636893a81b34d64df85123aae9650c42f2 F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479 @@ -1664,7 +1664,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 @@ -2171,8 +2171,9 @@ F tool/version-info.c 33d0390ef484b3b1cb685d59362be891ea162123cea181cb8e6d2cf6dd F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7 F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 0cf6211f0994474dc23ba5f38d2e6d0984360039798293eca757047e66d00c58 -R 9a294695f5a1bfd0c3e3d80b269eb61a +P 58a2a8417f2e0baade43e4da1c74893ab991cfbb34e5e5767d82f201c0b0b389 +Q +aef5397569d65d2971367b0278fe3a6f42544cf771572e7d046e2472f052364d +R 6cbacd75cad3bb79ac6f69b5fc500a02 U drh -Z 4f10b1c6bc3ee34da754ab3ed2e40cf8 +Z 4b715867d91a4e9be040246f23605a95 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 12d6b105ee..ffe7f58401 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -58a2a8417f2e0baade43e4da1c74893ab991cfbb34e5e5767d82f201c0b0b389 +35b306565a10c16737ee433728ca188852f01c12dfae0cc9212d21db932486fb diff --git a/src/expr.c b/src/expr.c index 1a7b273fd1..fdc05366cf 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 bec00ecb9a..a82e8f2e77 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