1
0
mirror of https://github.com/sqlite/sqlite.git synced 2026-10-23 21:49:02 +03:00

Fix the fsdir() table-valued function to better cope with

unreasonable LEVEL constraints.
[bugs:/info/2026-10-21T10:49:41Z|Bug 2026-10-21T10:49:41Z]

FossilOrigin-Name: 3e8932ebf2cec334b4867e0192d22b6de8102eda48f9f6e168fe5d2d24a8db0c
This commit is contained in:
drh
2026-10-21 14:28:22 +00:00
parent 4242856e53
commit 77207b3d94
4 changed files with 121 additions and 20 deletions

View File

@@ -962,13 +962,38 @@ static int fsdirFilter(
assert( argc>i );
pCur->zBase = (const char*)sqlite3_value_text(argv[i++]);
}
pCur->mxLvl = 1000000000;
if( (idxNum & 0x0c)!=0 ){
/* The LEVEL constraint is only used to limit the depth of the
** search. SQLite still checks the constraint against every row, so
** mxLvl may admit rows that do not match, but must never exclude
** a row that does. Only entries with level<mxLvl are visited. */
sqlite3_value *pMx;
assert( argc>i );
pCur->mxLvl = sqlite3_value_int(argv[i++]);
if( idxNum & 0x08 ) pCur->mxLvl++;
if( pCur->mxLvl<=0 ) pCur->mxLvl = 1000000000;
}else{
pCur->mxLvl = 1000000000;
pMx = argv[i++];
switch( sqlite3_value_type(pMx) ){
case SQLITE_NULL: {
/* No row can match a NULL constraint */
pCur->mxLvl = 1;
break;
}
case SQLITE_INTEGER:
case SQLITE_FLOAT: {
double r = sqlite3_value_double(pMx);
if( r<1.0 ){
pCur->mxLvl = 1;
}else if( r<1.0e9 ){
int iMx = (int)r;
if( (idxNum & 0x08)!=0 || (double)iMx<r ) iMx++;
pCur->mxLvl = iMx;
}
break;
}
default: {
/* TEXT or BLOB. Do not limit the search. */
break;
}
}
}
if( pCur->zBase ){
pCur->nBase = (int)strlen(pCur->zBase)+1;
@@ -1001,7 +1026,11 @@ static int fsdirFilter(
**
** 0x01 The path value is supplied by argv[0]
** 0x02 dir is in argv[1]
** 0x04 maxdepth is in argv[1] or [2]
** 0x04 LEVEL< bound is in argv[1] or [2]
** 0x08 LEVEL<= or LEVEL= bound is in argv[1] or [2]
**
** The LEVEL constraint is never omitted. It is used only to limit the
** depth of the directory search, and SQLite rechecks it for each row.
*/
static int fsdirBestIndex(
sqlite3_vtab *tab,
@@ -1012,7 +1041,6 @@ static int fsdirBestIndex(
int idxDir = -1; /* Index in pIdxInfo->aConstraint of DIR= */
int idxLevel = -1; /* Index in pIdxInfo->aConstraint of LEVEL< or <= */
int idxLevelEQ = 0; /* 0x08 for LEVEL<= or LEVEL=. 0x04 for LEVEL< */
int omitLevel = 0; /* omit the LEVEL constraint */
int seenPath = 0; /* True if an unusable PATH= constraint is seen */
int seenDir = 0; /* True if an unusable DIR= constraint is seen */
const struct sqlite3_index_constraint *pConstraint;
@@ -1044,7 +1072,6 @@ static int fsdirBestIndex(
if( pConstraint->usable && idxLevel<0 ){
idxLevel = i;
idxLevelEQ = 0x08;
omitLevel = 0;
}
break;
}
@@ -1057,11 +1084,9 @@ static int fsdirBestIndex(
if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE ){
idxLevel = i;
idxLevelEQ = 0x08;
omitLevel = 1;
}else if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ){
idxLevel = i;
idxLevelEQ = 0x04;
omitLevel = 1;
}
}
}
@@ -1088,7 +1113,6 @@ static int fsdirBestIndex(
pIdxInfo->estimatedCost /= 1.0e4;
}
if( idxLevel>=0 ){
pIdxInfo->aConstraintUsage[idxLevel].omit = omitLevel;
pIdxInfo->aConstraintUsage[idxLevel].argvIndex = i++;
pIdxInfo->idxNum |= idxLevelEQ;
pIdxInfo->estimatedCost /= 1.0e4;

View File

@@ -1,5 +1,5 @@
C Ensure\sthat\sall\s(parent_key\s=\schild_key)\scomparisons\sperformed\sas\spart\sof\sforeign\skey\sprocessing\sare\sdone\susing\sthe\saffinity\sof\sthe\sparent\skey.\sReport\s[bugs:/info/2026-10-20T02:56:10Z\s|\s2026-10-20T02:56:10Z].
D 2026-10-21T11:52:56.928
C Fix\sthe\sfsdir()\stable-valued\sfunction\sto\sbetter\scope\swith\nunreasonable\sLEVEL\sconstraints.\n[bugs:/info/2026-10-21T10:49:41Z|Bug\s2026-10-21T10:49:41Z]
D 2026-10-21T14:28:22.100
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
@@ -379,7 +379,7 @@ F ext/misc/decimal.c 68e987966e4772efdb8df03e2bf253d02bae3254af29fd4a99d34fc6e6f
F ext/misc/diskused.c 444139c01398df6f73e8a021c91e24d97d7914f5c1b49f55dd503abbca347c6e
F ext/misc/eval.c d4ea2d9898fbe4d30d38ff675ee51958d3bb0601a145bade56dee8b8f438cc5b
F ext/misc/explain.c 04c9270fd8cf93a9bceb12d2f5f67e3f09a4e58b3e0efe14d24531444d2fffdf
F ext/misc/fileio.c 6bf2543ae190401f5fda74661c20387a450071eb5d17bfc05d3b45416bbedcb7
F ext/misc/fileio.c 04a2ba5c4a1dc985315c302d7123dda0209860a5bfe4a5541d0faeac137196e8
F ext/misc/fossildelta.c 0af6a5ef3db6bba299271c29b604489e4a5d9e75395c6b83460405a9ccf05c2a
F ext/misc/fuzzer.c 32cef9a29864a1bc1329ccb6dfa613f197bb83491a7612c2ca2c83ee26cd8f14
F ext/misc/ieee754.c 2901d08a586d00a1d3c0fd89e03c57ee9e2b5f013b0daab9e49c7a48a9d5946b
@@ -2198,7 +2198,7 @@ F test/zeroblob.test 7b74cefc7b281dfa2b07cd237987fbe94b4a2037a7771e9e83f2d5f608b
F test/zeroblobfault.test 861d8191a0d944dfebb3cb4d2c5b4e46a5a119eaec5a63dd996c2389f8063441
F test/zerodamage.test 9c41628db7e8d9e8a0181e59ea5f189df311a9f6ce99cc376dc461f66db6f8dc
F test/zipfile.test 88b8b8babbe24dd3c1e58e8f96685429029c283fbf54b6df07b145b66032de9a
F test/zipfile2.test b43ff7de2ae9453b3d441ad62e357c67927dad9a5cdbea014596f46ff14c066a
F test/zipfile2.test e4aaa5d3eea4d48f54f168d8739f2b8dc222a935b73eb8882efc4f861ffe354e
F test/zipfilefault.test 44d4d7a7f7cca7521d569d7f71026b241d65a6b1757aa409c1a168827edbbc2c
F tool/GetFile.cs 47852aa0d806fe47ed1ac5138bdce7f000fe87aaa7f28107d0cb1e26682aeb44
F tool/GetTclKit.bat d84033c6a93dfe735d247f48ba00292a1cc284dcf69963e5e672444e04534bbf
@@ -2295,8 +2295,8 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee
F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
P decd49e6565bd5105e3b5cbe1067c8992582076a7c4e12cb6f3d089f38aba524
R a84e136060b7946f27e7ae696367e34b
U dan
Z 340908e2de6a8df53446363a056624d5
P feb6d3a3016d890fb1a8ca2a1752949b8adc0c06debb6ca305cacafc6b6edd10
R 854fb1ab222ea4ef60886c214072ce9e
U drh
Z 937fcd40605458ba83a093198ece3d68
# Remove this line to create a well-formed Fossil manifest.

View File

@@ -1 +1 @@
feb6d3a3016d890fb1a8ca2a1752949b8adc0c06debb6ca305cacafc6b6edd10
3e8932ebf2cec334b4867e0192d22b6de8102eda48f9f6e168fe5d2d24a8db0c

View File

@@ -373,4 +373,81 @@ if {[catch { load_static_extension db fileio }]==0} {
} {65535}
}
#-------------------------------------------------------------------------
# Bug 7faad02e21dced4d. The LEVEL constraint on fsdir() must give the
# same results as if it were not passed to xBestIndex, even for values
# that are out of range, negative, NULL or non-integer.
#
if {[catch { load_static_extension db fileio }]==0} {
forcedelete test_fsdir
file mkdir test_fsdir/a/b/c
foreach f {test_fsdir/x test_fsdir/a/y test_fsdir/a/b/z test_fsdir/a/b/c/w} {
close [open $f w]
}
set i 0
foreach {w res} {
{level<=2147483647} 8
{level<=4294967296} 8
{level<=4294967297} 8
{level<0} 0
{level<=-1} 0
{level<=0} 0
{level<1} 0
{level<=1} 1
{level<2} 1
{level<=2} 3
{level<2.5} 3
{level<=2.5} 3
{level<=NULL} 0
{level<NULL} 0
{level=NULL} 0
{level<'x'} 8
{level<x'00'} 8
{level=3} 2
{level=3.0} 2
{level=3.5} 0
{level=4294967299} 0
} {
incr i
do_execsql_test 10.$i.1 "
SELECT count(*) FROM fsdir('test_fsdir') WHERE $w
" $res
do_execsql_test 10.$i.2 "
SELECT count(*) FROM fsdir('test_fsdir') WHERE +$w
" $res
}
# Check that the LEVEL constraint still limits the depth of the
# search. Make directory test_fsdir/a/b/c (itself at level 4) unreadable.
# Then queries that need the entries within it (level 5) fail, and
# those that do not succeed.
#
if {$::tcl_platform(platform)=="unix"} {
file attributes test_fsdir/a/b/c -permissions 0000
if {[catch {glob -nocomplain test_fsdir/a/b/c/*}]} {
do_execsql_test 10.100 {
SELECT count(*) FROM fsdir('test_fsdir') WHERE level<=4
} {7}
do_execsql_test 10.101 {
SELECT count(*) FROM fsdir('test_fsdir') WHERE level<5
} {7}
do_execsql_test 10.102 {
SELECT count(*) FROM fsdir('test_fsdir') WHERE level<4.5
} {7}
do_execsql_test 10.103 {
SELECT count(*) FROM fsdir('test_fsdir') WHERE level=4
} {2}
do_catchsql_test 10.104 {
SELECT count(*) FROM fsdir('test_fsdir') WHERE level<5.5
} {1 {cannot read directory: test_fsdir/a/b/c}}
do_catchsql_test 10.105 {
SELECT count(*) FROM fsdir('test_fsdir') WHERE level<=5
} {1 {cannot read directory: test_fsdir/a/b/c}}
}
file attributes test_fsdir/a/b/c -permissions 0755
}
forcedelete test_fsdir
}
finish_test