aboutsummaryrefslogtreecommitdiff
path: root/ext/misc/fileio.c
diff options
context:
space:
mode:
authordrh <>2025-06-05 18:28:54 +0000
committerdrh <>2025-06-05 18:28:54 +0000
commit6facd9566d21e0bb9795bcec7650257101fe43e4 (patch)
tree68835611023e80f92b152b51102ce5702e53b25d /ext/misc/fileio.c
parent26370358b60dca54cf7e0d7c2e571e441c66dc01 (diff)
downloadsqlite-6facd9566d21e0bb9795bcec7650257101fe43e4.tar.gz
sqlite-6facd9566d21e0bb9795bcec7650257101fe43e4.zip
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
Diffstat (limited to 'ext/misc/fileio.c')
-rw-r--r--ext/misc/fileio.c127
1 files changed, 91 insertions, 36 deletions
diff --git a/ext/misc/fileio.c b/ext/misc/fileio.c
index 96a7f82bd..c67fa9600 100644
--- a/ext/misc/fileio.c
+++ b/ext/misc/fileio.c
@@ -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;
}
- break;
- }
- case FSDIR_COLUMN_DIR: {
- if( pConstraint->usable ){
- idxDir = i;
- seenDir = 0;
- }else if( idxDir<0 ){
- seenDir = 1;
+ 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;
+ }
+ }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;
}
}