From 3863fd3b40d065b537a566e5d1b84e5bda7941b1 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 20 Oct 2026 12:07:04 +0000 Subject: [PATCH] Do not allow a simplified constant expression (example: "123 ISNULL" → "1") to be used as the ordinal column number in an ORDER BY or GROUP BY clause. [bugs:/info/2026-10-16T12:58:09Z|Bug 2026-10-16T12:58:09Z] FossilOrigin-Name: c40e620b04aaa3c90501124db27711b1d4e8ae830f75828c7cccf84f7df42660 --- manifest | 18 +++++++++--------- manifest.uuid | 2 +- src/expr.c | 28 +++++++++++++++++++++++++++- src/parse.y | 6 ++++-- src/resolve.c | 6 +++--- test/orderby1.test | 9 +++++++++ 6 files changed, 53 insertions(+), 16 deletions(-) diff --git a/manifest b/manifest index f49ddd2dd7..4848e63e21 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sALTER\sTABLE\sRENAME\sso\sthat\sit\sworks\son\striggers\sthat\scontain\sUPSERT.\n[bugs:/info/2026-10-20T03:23:05Z|Bug\s2026-10-20T03:23:05Z] -D 2026-10-20T09:49:36.296 +C Do\snot\sallow\sa\ssimplified\sconstant\sexpression\s(example:\s"123\sISNULL"\s→\s"1")\nto\sbe\sused\sas\sthe\sordinal\scolumn\snumber\sin\san\sORDER\sBY\sor\sGROUP\sBY\sclause.\n[bugs:/info/2026-10-16T12:58:09Z|Bug\s2026-10-16T12:58:09Z] +D 2026-10-20T12:07:04.228 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -758,7 +758,7 @@ F src/date.c a55811d8e799ef76f3da9abf5a976dbe799208113f1da94ee25ae0aec2a14707 F src/dbpage.c ec8590c5e7a564cef28ca3c6156521316a4d26cb572a34841b0bcb26b67b3f97 F src/dbstat.c 34d4e825fbf763640ce3dbc56910ab080495ef4a6efb48b2b1852a43d43763fc F src/delete.c f3f07c1fb6ac87fac2425d48fd40ecb2f24a4495a9966f5a939668d3576916c8 -F src/expr.c b14e0b95e2c819727a77348d2d0764b958072cd766c0207fedb3d09acc337b5b +F src/expr.c 7ff00869afa1d3f11ea5f232c78e7b73a1a33bc1cb29c0af063b768658495234 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c 68307d6e2229616bd05a5e5d989dbe41104b103af3eeb5ca2fefc5e40e04076a F src/func.c 8c500a079a52d789613b173ba1894b143f8e38009bab224179bc89d3493201a0 @@ -797,7 +797,7 @@ F src/os_win.c 74473c76517956865581f12e4afc06c09a499ea70002cf9cedb3316301138be8 F src/os_win.h c06ccc3a090cf54202ea58981c298817f3309d4c9e4d52ad0a02927346493721 F src/pager.c fd9ca8fe01b09c490e66a440e2f56648d489ccb9e0e3b5508424079b046fc824 F src/pager.h 53cabc1fd881a9c078eab7db6c233bc0ed29d8938fc05585f0e75e80d905d308 -F src/parse.y 606d70804b1cfd426a86b893ac361d0474cde6abcbc745fb1efd82b06f102c80 +F src/parse.y 21f92beded2b0e1f1826b7f140b679c159ca0922be94b0b675fc9dabdf2fa892 F src/pcache.c 588cc3c5ccaaadde689ed35ce5c5c891a1f7b1f4d1f56f6cf0143b74d8ee6484 F src/pcache.h 00c5797ddf546ee0fe2828d7f75d1afbf1b40ba054f362905326c5ccca15e60c F src/pcache1.c 6bc38b44961f767985ae5022fb7887eda206040f57475812473929c702f6799d @@ -805,7 +805,7 @@ F src/pragma.c 78e1cf2aa4c260555a09c01e0e387c6ba14c85ff25b46e07b8d2c721ae7093a2 F src/prepare.c 8c4f4571668700664d0da6b6d2fe3e8d9d8808e3d1c643f14024e25d966df418 F src/printf.c 4bd228ed9e9346d558362be562757ee5eef6f9bf483b7f02751ca42cf95174d3 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c -F src/resolve.c 0b7924f036fc049909ca1d0a53cc660b2dc16d8449d38deb0474dcba5b50aa21 +F src/resolve.c 658d5d8f67b7565cbed56394d0fdbb124561a92c7455253186189db5e090e661 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c 71d639113349e0bcaab097629297e3b6bd6ca9d5ae3a842f9c77ab45e4ae1751 F src/shell.c.in 163279dc63d58ff501ccea12112885d2e19c1e97182ac64ab2484c6ccf35e10b @@ -1562,7 +1562,7 @@ F test/openv2.test 0d3040974bf402e19b7df4b783e447289d7ab394 F test/optfuzz-db01.c 9f2fa80b8f84ebbf1f2e8b13421a4e0477fe300f6686fbd76cac1d2db66e0fdc F test/optfuzz-db01.txt 21f6bdeadc701cf11528276e2a55c70bfcb846ba42df327f979bd9e7b6ce7041 F test/optfuzz.c 690430a0bf0ad047d5a168bf52b05b2ee97aedaad8c14337e9eb5050faa64994 -F test/orderby1.test fe68221ce63f8f6f5807218ba7b2ede855a4010b9b3bf83c11a76ae9233e99f5 +F test/orderby1.test c9a348c1f5b36c756994a14170e0541155d8cd9fb78e365a5a5fb434a70af93c F test/orderby2.test bc11009f7cd99d96b1b11e57b199b00633eb5b04 F test/orderby3.test 64465d29ab6dabb38731ff24ead2aa0bb262bb06a7b6aa17c6f874a5f24fee94 F test/orderby4.test daff719d0eead9ae92863a0b5a2e367bfdc7efcc9dfe16d83d667f84df597eff @@ -2294,8 +2294,8 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c -P 6d998905f8210f3e627e30fef2293afbd22bdcd39432d4388ecedbf927dc87dc -R da1e7f9fc4aed6e6f2b1816ae2282f4f +P be17b0b030c06169c92e49cbe56ed9228696fce741d4ecb0683b27e2113f0ce1 +R 778cc0bc7ac8ef6b1baf7c9e4817798e U drh -Z f55471c6792261b93191095116a46764 +Z ef1fd28c0001c884b534880086261e17 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 29be82dabd..5c28a5e4c7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -be17b0b030c06169c92e49cbe56ed9228696fce741d4ecb0683b27e2113f0ce1 +c40e620b04aaa3c90501124db27711b1d4e8ae830f75828c7cccf84f7df42660 diff --git a/src/expr.c b/src/expr.c index 4e592bf8e9..794470f749 100644 --- a/src/expr.c +++ b/src/expr.c @@ -2954,14 +2954,40 @@ int sqlite3ExprContainsSubquery(Expr *p){ ** is marked so as to cause the statement to be reprepared each time a new ** value is bound to it. Except, if parameter bRSI is true, then the statement ** will only be reprepared if the rebind changes the value to or from a -** "small integer" (either 0 or 1). +** "small integer" (either 0 or 1). +** +** If pParse is NULL but bRSI is non-zero, then to not accept the input +** as an integer if the TK_INTEGER opcode has its op2 field set to non-zero. +** A non-zero Expr.op2 indicates that the expression is a compile-time +** reduction of a constant expression. For example, "123 NOTNULL" would +** be rendered by the parser as a TK_INTEGER with value 1, but with op2 set. +** +** Summary: +** +** pParse bRsi What happens +** -------- ---- ------------------------------------------------ +** NULL 0 Reject if TK_VARIABLE. Accept if op2!=0 +** +** NULL 1 Reject if TK_VARIABLE or op2!=0 +** +** not-NULL 0 Accept TK_VARIABLE and/or op2!=0 but for TK_VARIABLE, +** cause the statement to reprepare on *any* +** binding change +** +** not-NULL 1 As previous, except reprepare only happens if a binding +** change move the variable value to or from an integer 0 +** or 1. */ int sqlite3ExprIsInteger(const Expr *p, int *pValue, Parse *pParse, int bRSI){ int iSign = 1; /* Either +1 or -1. */ while( 1/*exit-by-break*/ ){ if( NEVER(p==0) ) return 0; if( ExprUseUValue(p) ){ + assert( p->op==TK_INTEGER ); *pValue = p->u.iValue*iSign; + if( bRSI && pParse==0 && p->op2 ){ + return 0; + } return 1; } if( p->op==TK_UPLUS ){ diff --git a/src/parse.y b/src/parse.y index 2a60fc9dd1..e5ecf730dc 100644 --- a/src/parse.y +++ b/src/parse.y @@ -1407,9 +1407,11 @@ expr(A) ::= expr(A) likeop(OP) expr(Y) ESCAPE expr(E). [LIKE_KW] { case TK_INTEGER: case TK_STRING: case TK_FLOAT: - case TK_BLOB: + case TK_BLOB: sqlite3ExprDeferredDelete(pParse, pLeft); - return sqlite3ExprInt32(pParse->db, op==TK_NOTNULL); + p = sqlite3ExprInt32(pParse->db, op==TK_NOTNULL); + if( p ) p->op2 = 1; + return p; default: break; } diff --git a/src/resolve.c b/src/resolve.c index e0880f7d36..74d61402ce 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -1574,7 +1574,7 @@ static int resolveOrderByTermToExprList( int rc; /* Return code from subprocedures */ u8 savedSuppErr; /* Saved value of db->suppressErr */ - assert( sqlite3ExprIsInteger(pE, &i, 0, 0)==0 ); + assert( sqlite3ExprIsInteger(pE, &i, 0, 1)==0 ); pEList = pSelect->pEList; /* Resolve all names in the ORDER BY term expression @@ -1673,7 +1673,7 @@ static int resolveCompoundOrderBy( if( pItem->fg.done ) continue; pE = sqlite3ExprSkipCollateAndLikely(pItem->pExpr); if( NEVER(pE==0) ) continue; - if( sqlite3ExprIsInteger(pE, &iCol, 0, 0) ){ + if( sqlite3ExprIsInteger(pE, &iCol, 0, 1) ){ if( iCol<=0 || iCol>pEList->nExpr ){ resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr, pE); return 1; @@ -1856,7 +1856,7 @@ static int resolveOrderGroupBy( continue; } } - if( sqlite3ExprIsInteger(pE2, &iCol, 0, 0) ){ + if( sqlite3ExprIsInteger(pE2, &iCol, 0, 1) ){ /* The ORDER BY term is an integer constant. Again, set the column ** number so that sqlite3ResolveOrderGroupBy() will convert the ** order-by term to a copy of the result-set expression */ diff --git a/test/orderby1.test b/test/orderby1.test index e5911ecd7a..f69e2d6783 100644 --- a/test/orderby1.test +++ b/test/orderby1.test @@ -581,4 +581,13 @@ do_execsql_test 12.1 { LIMIT 1; } {2} +# Bug 2026-10-16T12:58:09Z +do_execsql_test 13.0 { + DROP TABLE t1; + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(4),(1),(3),(2); + PRAGMA reverse_unordered_selects=OFF; + SELECT * FROM t1 ORDER BY 123 ISNULL; +} {4 1 3 2} + finish_test