1
0
mirror of https://github.com/sqlite/sqlite.git synced 2026-01-06 08:01:16 +03:00

Enhance the FSDIR virtual table with a new "level" column. The query planner

knows how to optimize to avoid search deeper than the maximum requested level.

FossilOrigin-Name: 1ddc0f9e79c33957961bc1443ccb74d756a02cbd20850052079782e76aef2706
This commit is contained in:
drh
2025-06-05 18:28:54 +00:00
parent 26370358b6
commit 6facd9566d
3 changed files with 99 additions and 44 deletions

View File

@@ -67,6 +67,7 @@
** data: For a regular file, a blob containing the file data. For a
** symlink, a text value containing the text of the link. For a
** directory, NULL.
** level: Directory hierarchy level. Topmost is 1.
**
** If a non-NULL value is specified for the optional $dir parameter and
** $path is a relative path, then $path is interpreted relative to $dir.
@@ -116,14 +117,16 @@ SQLITE_EXTENSION_INIT1
/*
** Structure of the fsdir() table-valued function
*/
/* 0 1 2 3 4 5 */
#define FSDIR_SCHEMA "(name,mode,mtime,data,path HIDDEN,dir HIDDEN)"
/* 0 1 2 3 4 5 6 */
#define FSDIR_SCHEMA "(name,mode,mtime,data,level,path HIDDEN,dir HIDDEN)"
#define FSDIR_COLUMN_NAME 0 /* Name of the file */
#define FSDIR_COLUMN_MODE 1 /* Access mode */
#define FSDIR_COLUMN_MTIME 2 /* Last modification time */
#define FSDIR_COLUMN_DATA 3 /* File content */
#define FSDIR_COLUMN_PATH 4 /* Path to top of search */
#define FSDIR_COLUMN_DIR 5 /* Path is relative to this directory */
#define FSDIR_COLUMN_LEVEL 4 /* Level. Topmost is 1 */
#define FSDIR_COLUMN_PATH 5 /* Path to top of search */
#define FSDIR_COLUMN_DIR 6 /* Path is relative to this directory */
/*
** UTF8 chmod() function for Windows
@@ -620,6 +623,7 @@ struct fsdir_cursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
int nLvl; /* Number of entries in aLvl[] array */
int mxLvl; /* Maximum level */
int iLvl; /* Index of current entry */
FsdirLevel *aLvl; /* Hierarchy of directories being traversed */
@@ -738,7 +742,7 @@ static int fsdirNext(sqlite3_vtab_cursor *cur){
mode_t m = pCur->sStat.st_mode;
pCur->iRowid++;
if( S_ISDIR(m) ){
if( S_ISDIR(m) && pCur->iLvl+3<pCur->mxLvl ){
/* Descend into this directory */
int iNew = pCur->iLvl + 1;
FsdirLevel *pLvl;
@@ -846,7 +850,11 @@ static int fsdirColumn(
}else{
readFileContents(ctx, pCur->zPath);
}
break;
}
case FSDIR_COLUMN_LEVEL:
sqlite3_result_int(ctx, pCur->iLvl+2);
break;
case FSDIR_COLUMN_PATH:
default: {
/* The FSDIR_COLUMN_PATH and FSDIR_COLUMN_DIR are input parameters.
@@ -880,8 +888,11 @@ static int fsdirEof(sqlite3_vtab_cursor *cur){
/*
** xFilter callback.
**
** idxNum==1 PATH parameter only
** idxNum==2 Both PATH and DIR supplied
** idxNum bit Meaning
** 0x01 PATH=N
** 0x02 DIR=N
** 0x04 LEVEL<N
** 0x08 LEVEL<=N
*/
static int fsdirFilter(
sqlite3_vtab_cursor *cur,
@@ -892,20 +903,31 @@ static int fsdirFilter(
fsdir_cursor *pCur = (fsdir_cursor*)cur;
(void)idxStr;
fsdirResetCursor(pCur);
int i;
if( idxNum==0 ){
fsdirSetErrmsg(pCur, "table function fsdir requires an argument");
return SQLITE_ERROR;
}
assert( argc==idxNum && (argc==1 || argc==2) );
assert( (idxNum & 0x01)!=0 && argc>0 );
zDir = (const char*)sqlite3_value_text(argv[0]);
if( zDir==0 ){
fsdirSetErrmsg(pCur, "table function fsdir requires a non-NULL argument");
return SQLITE_ERROR;
}
if( argc==2 ){
pCur->zBase = (const char*)sqlite3_value_text(argv[1]);
i = 1;
if( (idxNum & 0x02)!=0 ){
assert( argc>i );
pCur->zBase = (const char*)sqlite3_value_text(argv[i++]);
}
if( (idxNum & 0x0c)!=0 ){
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;
}
if( pCur->zBase ){
pCur->nBase = (int)strlen(pCur->zBase)+1;
@@ -934,10 +956,11 @@ static int fsdirFilter(
** In this implementation idxNum is used to represent the
** query plan. idxStr is unused.
**
** The query plan is represented by values of idxNum:
** The query plan is represented by bits in idxNum:
**
** (1) The path value is supplied by argv[0]
** (2) Path is in argv[0] and dir is in argv[1]
** 0x01 The path value is supplied by argv[0]
** 0x02 dir is in argv[1]
** 0x04 maxdepth is in argv[1] or [2]
*/
static int fsdirBestIndex(
sqlite3_vtab *tab,
@@ -946,6 +969,9 @@ static int fsdirBestIndex(
int i; /* Loop over constraints */
int idxPath = -1; /* Index in pIdxInfo->aConstraint of PATH= */
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;
@@ -953,25 +979,48 @@ static int fsdirBestIndex(
(void)tab;
pConstraint = pIdxInfo->aConstraint;
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
switch( pConstraint->iColumn ){
case FSDIR_COLUMN_PATH: {
if( pConstraint->usable ){
idxPath = i;
seenPath = 0;
}else if( idxPath<0 ){
seenPath = 1;
if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
switch( pConstraint->iColumn ){
case FSDIR_COLUMN_PATH: {
if( pConstraint->usable ){
idxPath = i;
seenPath = 0;
}else if( idxPath<0 ){
seenPath = 1;
}
break;
}
case FSDIR_COLUMN_DIR: {
if( pConstraint->usable ){
idxDir = i;
seenDir = 0;
}else if( idxDir<0 ){
seenDir = 1;
}
break;
}
case FSDIR_COLUMN_LEVEL: {
if( pConstraint->usable && idxLevel<0 ){
idxLevel = i;
idxLevelEQ = 0x08;
omitLevel = 0;
}
break;
}
break;
}
case FSDIR_COLUMN_DIR: {
if( pConstraint->usable ){
idxDir = i;
seenDir = 0;
}else if( idxDir<0 ){
seenDir = 1;
}
break;
}else
if( pConstraint->iColumn==FSDIR_COLUMN_LEVEL
&& pConstraint->usable
&& idxLevel<0
){
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;
}
}
}
@@ -988,14 +1037,20 @@ static int fsdirBestIndex(
}else{
pIdxInfo->aConstraintUsage[idxPath].omit = 1;
pIdxInfo->aConstraintUsage[idxPath].argvIndex = 1;
pIdxInfo->idxNum = 0x01;
pIdxInfo->estimatedCost = 1.0e9;
i = 2;
if( idxDir>=0 ){
pIdxInfo->aConstraintUsage[idxDir].omit = 1;
pIdxInfo->aConstraintUsage[idxDir].argvIndex = 2;
pIdxInfo->idxNum = 2;
pIdxInfo->estimatedCost = 10.0;
}else{
pIdxInfo->idxNum = 1;
pIdxInfo->estimatedCost = 100.0;
pIdxInfo->aConstraintUsage[idxDir].argvIndex = i++;
pIdxInfo->idxNum |= 0x02;
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 tea\sbuild:\sadd\san\sinfo-exists\scheck\safter\sa\s'scan'\scall,\sas\sscan\sdoes\snot\screate\sits\starget\svars\son\serror.\sProblem\sreported\sat\s[forum:fde857fb8101a4be\s|\sforum\spost\sfde857fb8101a4be]\sand\striggers\swhen\sthe\s'vsatisfies'\stest\sfor\sthe\shost's\sTcl\sversion\sfails\s(so\sthe\sbuild\swould\sfail\sanyway,\sbut\swill\sfail\smore\sinformatively\swith\sthis\sfix).
D 2025-06-04T18:34:20.003
C Enhance\sthe\sFSDIR\svirtual\stable\swith\sa\snew\s"level"\scolumn.\s\sThe\squery\splanner\nknows\show\sto\soptimize\sto\savoid\ssearch\sdeeper\sthan\sthe\smaximum\srequested\slevel.
D 2025-06-05T18:28:54.497
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
@@ -417,7 +417,7 @@ F ext/misc/dbdump.c b8592f6f2da292c62991a13864a60d6c573c47a9cc58362131b9e6a64f82
F ext/misc/decimal.c 228d47e9ef4de60daf5851da19e3ac9ac1eda9e94432816914469501db6a1129
F ext/misc/eval.c 04bc9aada78c888394204b4ed996ab834b99726fb59603b0ee3ed6e049755dc1
F ext/misc/explain.c 606100185fb90d6a1eade1ed0414d53503c86820d8956a06e3b0a56291894f2b
F ext/misc/fileio.c 34993b810514c58ff99d7b4254d4a388d844a4774ea77bec68a1dafe8de5ce41
F ext/misc/fileio.c f01aca52627d0a4b212800a024de62c6bb2c09403a09e782592d59902d49675f
F ext/misc/fossildelta.c 0aeb099e9627eea693cf21ae47826ecd1e0319b93143bed23090838b2ef0c162
F ext/misc/fuzzer.c 6b231352815304ba60d8e9ec2ee73d4918e74d9b76bda8940ba2b64e8777515e
F ext/misc/ieee754.c c9dd9d77c8e8e18e0a5706f8ffcccf4ccb6562073709f7453d4d73f5122f4362
@@ -2209,8 +2209,8 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350
F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
F tool/warnings.sh 1ad0169b022b280bcaaf94a7fa231591be96b514230ab5c98fbf15cd7df842dd
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
P 2f8a1b79533879e4975b405c46fea496ba8bffbef065e7dd0ad29fd4aa8f8f92
R 93ec2f0ba3ae7edbce8d608ede6c810e
U stephan
Z 0897533fcae3d269da2c3a6ccf2bfc1d
P 4f21874d5d20aef2e2d67a59e4fa03d98aa6514b16e4d956acfc817142cfbdb6
R 59f5d0a04d958d7cb4b261048dbd0b85
U drh
Z 276de0b7a4b37abe2f1a437bfc4df89c
# Remove this line to create a well-formed Fossil manifest.

View File

@@ -1 +1 @@
4f21874d5d20aef2e2d67a59e4fa03d98aa6514b16e4d956acfc817142cfbdb6
1ddc0f9e79c33957961bc1443ccb74d756a02cbd20850052079782e76aef2706