xref: /sqlite-3.40.0/src/test_fs.c (revision af4d50c4)
19f5ff371Sdan /*
29f5ff371Sdan ** 2013 Jan 11
39f5ff371Sdan **
49f5ff371Sdan ** The author disclaims copyright to this source code.  In place of
59f5ff371Sdan ** a legal notice, here is a blessing:
69f5ff371Sdan **
79f5ff371Sdan **    May you do good and not evil.
89f5ff371Sdan **    May you find forgiveness for yourself and forgive others.
99f5ff371Sdan **    May you share freely, never taking more than you give.
109f5ff371Sdan **
119f5ff371Sdan *************************************************************************
129f5ff371Sdan ** Code for testing the virtual table interfaces.  This code
139f5ff371Sdan ** is not included in the SQLite library.  It is used for automated
149f5ff371Sdan ** testing of the SQLite library.
159f5ff371Sdan **
169f5ff371Sdan ** The FS virtual table is created as follows:
179f5ff371Sdan **
189f5ff371Sdan **   CREATE VIRTUAL TABLE tbl USING fs(idx);
199f5ff371Sdan **
209f5ff371Sdan ** where idx is the name of a table in the db with 2 columns.  The virtual
219f5ff371Sdan ** table also has two columns - file path and file contents.
229f5ff371Sdan **
239f5ff371Sdan ** The first column of table idx must be an IPK, and the second contains file
249f5ff371Sdan ** paths. For example:
259f5ff371Sdan **
269f5ff371Sdan **   CREATE TABLE idx(id INTEGER PRIMARY KEY, path TEXT);
279f5ff371Sdan **   INSERT INTO idx VALUES(4, '/etc/passwd');
289f5ff371Sdan **
299f5ff371Sdan ** Adding the row to the idx table automatically creates a row in the
309f5ff371Sdan ** virtual table with rowid=4, path=/etc/passwd and a text field that
319f5ff371Sdan ** contains data read from file /etc/passwd on disk.
321e93173fSdan **
331e93173fSdan *************************************************************************
341e93173fSdan ** Virtual table module "fsdir"
351e93173fSdan **
361e93173fSdan ** This module is designed to be used as a read-only eponymous virtual table.
371e93173fSdan ** Its schema is as follows:
381e93173fSdan **
391e93173fSdan **   CREATE TABLE fsdir(dir TEXT, name TEXT);
401e93173fSdan **
411e93173fSdan ** When queried, a WHERE term of the form "dir = $dir" must be provided. The
421e93173fSdan ** virtual table then appears to have one row for each entry in file-system
431e93173fSdan ** directory $dir. Column dir contains a copy of $dir, and column "name"
441e93173fSdan ** contains the name of the directory entry.
451e93173fSdan **
461e93173fSdan ** If the specified $dir cannot be opened or is not a directory, it is not
471e93173fSdan ** an error. The virtual table appears to be empty in this case.
481e93173fSdan **
491e93173fSdan *************************************************************************
501e93173fSdan ** Virtual table module "fstree"
511e93173fSdan **
521e93173fSdan ** This module is also a read-only eponymous virtual table with the
531e93173fSdan ** following schema:
541e93173fSdan **
551e93173fSdan **   CREATE TABLE fstree(path TEXT, size INT, data BLOB);
561e93173fSdan **
571e93173fSdan ** Running a "SELECT * FROM fstree" query on this table returns the entire
581e93173fSdan ** contents of the file-system, starting at "/". To restrict the search
591e93173fSdan ** space, the virtual table supports LIKE and GLOB constraints on the
601e93173fSdan ** 'path' column. For example:
611e93173fSdan **
621e93173fSdan **   SELECT * FROM fstree WHERE path LIKE '/home/dan/sqlite/%'
639f5ff371Sdan */
649f5ff371Sdan #include "sqliteInt.h"
6552b1dbb5Smistachkin #if defined(INCLUDE_SQLITE_TCL_H)
6652b1dbb5Smistachkin #  include "sqlite_tcl.h"
6752b1dbb5Smistachkin #else
689f5ff371Sdan #  include "tcl.h"
6952b1dbb5Smistachkin #endif
709f5ff371Sdan 
719f5ff371Sdan #include <stdlib.h>
729f5ff371Sdan #include <string.h>
739f5ff371Sdan #include <sys/types.h>
749f5ff371Sdan #include <sys/stat.h>
759f5ff371Sdan #include <fcntl.h>
769f5ff371Sdan 
7798486c04Smistachkin #if SQLITE_OS_UNIX || defined(__MINGW_H)
78413c63baSmistachkin # include <unistd.h>
791e93173fSdan # include <dirent.h>
8092af1ebcSmistachkin # ifndef DIRENT
8192af1ebcSmistachkin #  define DIRENT dirent
8292af1ebcSmistachkin # endif
83413c63baSmistachkin #endif
84413c63baSmistachkin #if SQLITE_OS_WIN
85413c63baSmistachkin # include <io.h>
8698486c04Smistachkin # if !defined(__MINGW_H)
8792af1ebcSmistachkin #  include "test_windirent.h"
8898486c04Smistachkin # endif
8992af1ebcSmistachkin # ifndef S_ISREG
9092af1ebcSmistachkin #  define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
9192af1ebcSmistachkin # endif
92413c63baSmistachkin #endif
93413c63baSmistachkin 
949f5ff371Sdan #ifndef SQLITE_OMIT_VIRTUALTABLE
959f5ff371Sdan 
969f5ff371Sdan typedef struct fs_vtab fs_vtab;
979f5ff371Sdan typedef struct fs_cursor fs_cursor;
989f5ff371Sdan 
999f5ff371Sdan /*
1009f5ff371Sdan ** A fs virtual-table object
1019f5ff371Sdan */
1029f5ff371Sdan struct fs_vtab {
1039f5ff371Sdan   sqlite3_vtab base;
1049f5ff371Sdan   sqlite3 *db;
1059f5ff371Sdan   char *zDb;                      /* Name of db containing zTbl */
1069f5ff371Sdan   char *zTbl;                     /* Name of docid->file map table */
1079f5ff371Sdan };
1089f5ff371Sdan 
1099f5ff371Sdan /* A fs cursor object */
1109f5ff371Sdan struct fs_cursor {
1119f5ff371Sdan   sqlite3_vtab_cursor base;
1129f5ff371Sdan   sqlite3_stmt *pStmt;
1139f5ff371Sdan   char *zBuf;
1149f5ff371Sdan   int nBuf;
1159f5ff371Sdan   int nAlloc;
1169f5ff371Sdan };
1179f5ff371Sdan 
1181e93173fSdan /*************************************************************************
1191e93173fSdan ** Start of fsdir implementation.
1201e93173fSdan */
1211e93173fSdan typedef struct FsdirVtab FsdirVtab;
1221e93173fSdan typedef struct FsdirCsr FsdirCsr;
1231e93173fSdan struct FsdirVtab {
1241e93173fSdan   sqlite3_vtab base;
1251e93173fSdan };
1261e93173fSdan 
1271e93173fSdan struct FsdirCsr {
1281e93173fSdan   sqlite3_vtab_cursor base;
1291e93173fSdan   char *zDir;                     /* Buffer containing directory scanned */
1301e93173fSdan   DIR *pDir;                      /* Open directory */
1311e93173fSdan   sqlite3_int64 iRowid;
132*af4d50c4Sdan   struct DIRENT *pEntry;
1331e93173fSdan };
1341e93173fSdan 
1351e93173fSdan /*
1361e93173fSdan ** This function is the implementation of both the xConnect and xCreate
1371e93173fSdan ** methods of the fsdir virtual table.
1381e93173fSdan **
1391e93173fSdan ** The argv[] array contains the following:
1401e93173fSdan **
1411e93173fSdan **   argv[0]   -> module name  ("fs")
1421e93173fSdan **   argv[1]   -> database name
1431e93173fSdan **   argv[2]   -> table name
1441e93173fSdan **   argv[...] -> other module argument fields.
1451e93173fSdan */
fsdirConnect(sqlite3 * db,void * pAux,int argc,const char * const * argv,sqlite3_vtab ** ppVtab,char ** pzErr)1461e93173fSdan static int fsdirConnect(
1471e93173fSdan   sqlite3 *db,
1481e93173fSdan   void *pAux,
1491e93173fSdan   int argc, const char *const*argv,
1501e93173fSdan   sqlite3_vtab **ppVtab,
1511e93173fSdan   char **pzErr
1521e93173fSdan ){
1531e93173fSdan   FsdirVtab *pTab;
1541e93173fSdan 
1551e93173fSdan   if( argc!=3 ){
1561e93173fSdan     *pzErr = sqlite3_mprintf("wrong number of arguments");
1571e93173fSdan     return SQLITE_ERROR;
1581e93173fSdan   }
1591e93173fSdan 
1601e93173fSdan   pTab = (FsdirVtab *)sqlite3_malloc(sizeof(FsdirVtab));
1611e93173fSdan   if( !pTab ) return SQLITE_NOMEM;
1621e93173fSdan   memset(pTab, 0, sizeof(FsdirVtab));
1631e93173fSdan 
1641e93173fSdan   *ppVtab = &pTab->base;
1651e93173fSdan   sqlite3_declare_vtab(db, "CREATE TABLE xyz(dir, name);");
1661e93173fSdan 
1671e93173fSdan   return SQLITE_OK;
1681e93173fSdan }
1691e93173fSdan 
1701e93173fSdan /*
1711e93173fSdan ** xDestroy/xDisconnect implementation.
1721e93173fSdan */
fsdirDisconnect(sqlite3_vtab * pVtab)1731e93173fSdan static int fsdirDisconnect(sqlite3_vtab *pVtab){
1741e93173fSdan   sqlite3_free(pVtab);
1751e93173fSdan   return SQLITE_OK;
1761e93173fSdan }
1771e93173fSdan 
1781e93173fSdan /*
1791e93173fSdan ** xBestIndex implementation. The only constraint supported is:
1801e93173fSdan **
1811e93173fSdan **   (dir = ?)
1821e93173fSdan */
fsdirBestIndex(sqlite3_vtab * tab,sqlite3_index_info * pIdxInfo)1831e93173fSdan static int fsdirBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
1841e93173fSdan   int ii;
1851e93173fSdan 
1861e93173fSdan   pIdxInfo->estimatedCost = 1000000000.0;
1871e93173fSdan 
1881e93173fSdan   for(ii=0; ii<pIdxInfo->nConstraint; ii++){
1891e93173fSdan     struct sqlite3_index_constraint const *p = &pIdxInfo->aConstraint[ii];
1901e93173fSdan     if( p->iColumn==0 && p->usable && p->op==SQLITE_INDEX_CONSTRAINT_EQ ){
1911e93173fSdan       struct sqlite3_index_constraint_usage *pUsage;
1921e93173fSdan       pUsage = &pIdxInfo->aConstraintUsage[ii];
1931e93173fSdan       pUsage->omit = 1;
1941e93173fSdan       pUsage->argvIndex = 1;
1951e93173fSdan       pIdxInfo->idxNum = 1;
1961e93173fSdan       pIdxInfo->estimatedCost = 1.0;
1971e93173fSdan       break;
1981e93173fSdan     }
1991e93173fSdan   }
2001e93173fSdan 
2011e93173fSdan   return SQLITE_OK;
2021e93173fSdan }
2031e93173fSdan 
2041e93173fSdan /*
2051e93173fSdan ** xOpen implementation.
2061e93173fSdan **
2071e93173fSdan ** Open a new fsdir cursor.
2081e93173fSdan */
fsdirOpen(sqlite3_vtab * pVTab,sqlite3_vtab_cursor ** ppCursor)2091e93173fSdan static int fsdirOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
2101e93173fSdan   FsdirCsr *pCur;
2112f5bfd95Sdrh   /* Allocate an extra 256 bytes because it is undefined how big dirent.d_name
2122f5bfd95Sdrh   ** is and we need enough space.  Linux provides plenty already, but
2132f5bfd95Sdrh   ** Solaris only provides one byte. */
2142f5bfd95Sdrh   pCur = (FsdirCsr*)sqlite3_malloc(sizeof(FsdirCsr)+256);
2151e93173fSdan   if( pCur==0 ) return SQLITE_NOMEM;
2161e93173fSdan   memset(pCur, 0, sizeof(FsdirCsr));
2171e93173fSdan   *ppCursor = &pCur->base;
2181e93173fSdan   return SQLITE_OK;
2191e93173fSdan }
2201e93173fSdan 
2211e93173fSdan /*
2221e93173fSdan ** Close a fsdir cursor.
2231e93173fSdan */
fsdirClose(sqlite3_vtab_cursor * cur)2241e93173fSdan static int fsdirClose(sqlite3_vtab_cursor *cur){
2251e93173fSdan   FsdirCsr *pCur = (FsdirCsr*)cur;
2261e93173fSdan   if( pCur->pDir ) closedir(pCur->pDir);
2271e93173fSdan   sqlite3_free(pCur->zDir);
2281e93173fSdan   sqlite3_free(pCur);
2291e93173fSdan   return SQLITE_OK;
2301e93173fSdan }
2311e93173fSdan 
2321e93173fSdan /*
2331e93173fSdan ** Skip the cursor to the next entry.
2341e93173fSdan */
fsdirNext(sqlite3_vtab_cursor * cur)2351e93173fSdan static int fsdirNext(sqlite3_vtab_cursor *cur){
2361e93173fSdan   FsdirCsr *pCsr = (FsdirCsr*)cur;
2371e93173fSdan 
2381e93173fSdan   if( pCsr->pDir ){
239*af4d50c4Sdan     pCsr->pEntry = readdir(pCsr->pDir);
240*af4d50c4Sdan     if( pCsr->pEntry==0 ){
2411e93173fSdan       closedir(pCsr->pDir);
2421e93173fSdan       pCsr->pDir = 0;
2431e93173fSdan     }
2441e93173fSdan     pCsr->iRowid++;
2451e93173fSdan   }
2461e93173fSdan 
2471e93173fSdan   return SQLITE_OK;
2481e93173fSdan }
2491e93173fSdan 
2501e93173fSdan /*
2511e93173fSdan ** xFilter method implementation.
2521e93173fSdan */
fsdirFilter(sqlite3_vtab_cursor * pVtabCursor,int idxNum,const char * idxStr,int argc,sqlite3_value ** argv)2531e93173fSdan static int fsdirFilter(
2541e93173fSdan   sqlite3_vtab_cursor *pVtabCursor,
2551e93173fSdan   int idxNum, const char *idxStr,
2561e93173fSdan   int argc, sqlite3_value **argv
2571e93173fSdan ){
2581e93173fSdan   FsdirCsr *pCsr = (FsdirCsr*)pVtabCursor;
2591e93173fSdan   const char *zDir;
2601e93173fSdan   int nDir;
2611e93173fSdan 
2621e93173fSdan 
2631e93173fSdan   if( idxNum!=1 || argc!=1 ){
2641e93173fSdan     return SQLITE_ERROR;
2651e93173fSdan   }
2661e93173fSdan 
2671e93173fSdan   pCsr->iRowid = 0;
2681e93173fSdan   sqlite3_free(pCsr->zDir);
2691e93173fSdan   if( pCsr->pDir ){
2701e93173fSdan     closedir(pCsr->pDir);
2711e93173fSdan     pCsr->pDir = 0;
2721e93173fSdan   }
2731e93173fSdan 
2741e93173fSdan   zDir = (const char*)sqlite3_value_text(argv[0]);
2751e93173fSdan   nDir = sqlite3_value_bytes(argv[0]);
2761e93173fSdan   pCsr->zDir = sqlite3_malloc(nDir+1);
2771e93173fSdan   if( pCsr->zDir==0 ) return SQLITE_NOMEM;
2781e93173fSdan   memcpy(pCsr->zDir, zDir, nDir+1);
2791e93173fSdan 
2801e93173fSdan   pCsr->pDir = opendir(pCsr->zDir);
2811e93173fSdan   return fsdirNext(pVtabCursor);
2821e93173fSdan }
2831e93173fSdan 
2841e93173fSdan /*
2851e93173fSdan ** xEof method implementation.
2861e93173fSdan */
fsdirEof(sqlite3_vtab_cursor * cur)2871e93173fSdan static int fsdirEof(sqlite3_vtab_cursor *cur){
2881e93173fSdan   FsdirCsr *pCsr = (FsdirCsr*)cur;
2891e93173fSdan   return pCsr->pDir==0;
2901e93173fSdan }
2911e93173fSdan 
2921e93173fSdan /*
2931e93173fSdan ** xColumn method implementation.
2941e93173fSdan */
fsdirColumn(sqlite3_vtab_cursor * cur,sqlite3_context * ctx,int i)2951e93173fSdan static int fsdirColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
2961e93173fSdan   FsdirCsr *pCsr = (FsdirCsr*)cur;
2971e93173fSdan   switch( i ){
2981e93173fSdan     case 0: /* dir */
2991e93173fSdan       sqlite3_result_text(ctx, pCsr->zDir, -1, SQLITE_STATIC);
3001e93173fSdan       break;
3011e93173fSdan 
3021e93173fSdan     case 1: /* name */
303*af4d50c4Sdan       sqlite3_result_text(ctx, pCsr->pEntry->d_name, -1, SQLITE_TRANSIENT);
3041e93173fSdan       break;
3051e93173fSdan 
3061e93173fSdan     default:
3071e93173fSdan       assert( 0 );
3081e93173fSdan   }
3091e93173fSdan 
3101e93173fSdan   return SQLITE_OK;
3111e93173fSdan }
3121e93173fSdan 
3131e93173fSdan /*
3141e93173fSdan ** xRowid method implementation.
3151e93173fSdan */
fsdirRowid(sqlite3_vtab_cursor * cur,sqlite_int64 * pRowid)3161e93173fSdan static int fsdirRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
3171e93173fSdan   FsdirCsr *pCsr = (FsdirCsr*)cur;
3181e93173fSdan   *pRowid = pCsr->iRowid;
3191e93173fSdan   return SQLITE_OK;
3201e93173fSdan }
3211e93173fSdan /*
3221e93173fSdan ** End of fsdir implementation.
3231e93173fSdan *************************************************************************/
3241e93173fSdan 
3251e93173fSdan /*************************************************************************
3261e93173fSdan ** Start of fstree implementation.
3271e93173fSdan */
3281e93173fSdan typedef struct FstreeVtab FstreeVtab;
3291e93173fSdan typedef struct FstreeCsr FstreeCsr;
3301e93173fSdan struct FstreeVtab {
3311e93173fSdan   sqlite3_vtab base;
3321e93173fSdan   sqlite3 *db;
3331e93173fSdan };
3341e93173fSdan 
3351e93173fSdan struct FstreeCsr {
3361e93173fSdan   sqlite3_vtab_cursor base;
3371e93173fSdan   sqlite3_stmt *pStmt;            /* Statement to list paths */
3381e93173fSdan   int fd;                         /* File descriptor open on current path */
3391e93173fSdan };
3401e93173fSdan 
3411e93173fSdan /*
3421e93173fSdan ** This function is the implementation of both the xConnect and xCreate
3431e93173fSdan ** methods of the fstree virtual table.
3441e93173fSdan **
3451e93173fSdan ** The argv[] array contains the following:
3461e93173fSdan **
3471e93173fSdan **   argv[0]   -> module name  ("fs")
3481e93173fSdan **   argv[1]   -> database name
3491e93173fSdan **   argv[2]   -> table name
3501e93173fSdan **   argv[...] -> other module argument fields.
3511e93173fSdan */
fstreeConnect(sqlite3 * db,void * pAux,int argc,const char * const * argv,sqlite3_vtab ** ppVtab,char ** pzErr)3521e93173fSdan static int fstreeConnect(
3531e93173fSdan   sqlite3 *db,
3541e93173fSdan   void *pAux,
3551e93173fSdan   int argc, const char *const*argv,
3561e93173fSdan   sqlite3_vtab **ppVtab,
3571e93173fSdan   char **pzErr
3581e93173fSdan ){
3591e93173fSdan   FstreeVtab *pTab;
3601e93173fSdan 
3611e93173fSdan   if( argc!=3 ){
3621e93173fSdan     *pzErr = sqlite3_mprintf("wrong number of arguments");
3631e93173fSdan     return SQLITE_ERROR;
3641e93173fSdan   }
3651e93173fSdan 
3661e93173fSdan   pTab = (FstreeVtab *)sqlite3_malloc(sizeof(FstreeVtab));
3671e93173fSdan   if( !pTab ) return SQLITE_NOMEM;
3681e93173fSdan   memset(pTab, 0, sizeof(FstreeVtab));
3691e93173fSdan   pTab->db = db;
3701e93173fSdan 
3711e93173fSdan   *ppVtab = &pTab->base;
3721e93173fSdan   sqlite3_declare_vtab(db, "CREATE TABLE xyz(path, size, data);");
3731e93173fSdan 
3741e93173fSdan   return SQLITE_OK;
3751e93173fSdan }
3761e93173fSdan 
3771e93173fSdan /*
3781e93173fSdan ** xDestroy/xDisconnect implementation.
3791e93173fSdan */
fstreeDisconnect(sqlite3_vtab * pVtab)3801e93173fSdan static int fstreeDisconnect(sqlite3_vtab *pVtab){
3811e93173fSdan   sqlite3_free(pVtab);
3821e93173fSdan   return SQLITE_OK;
3831e93173fSdan }
3841e93173fSdan 
3851e93173fSdan /*
3861e93173fSdan ** xBestIndex implementation. The only constraint supported is:
3871e93173fSdan **
3881e93173fSdan **   (dir = ?)
3891e93173fSdan */
fstreeBestIndex(sqlite3_vtab * tab,sqlite3_index_info * pIdxInfo)3901e93173fSdan static int fstreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
3911e93173fSdan   int ii;
3921e93173fSdan 
3931e93173fSdan   for(ii=0; ii<pIdxInfo->nConstraint; ii++){
3941e93173fSdan     struct sqlite3_index_constraint const *p = &pIdxInfo->aConstraint[ii];
3951e93173fSdan     if( p->iColumn==0 && p->usable && (
3961e93173fSdan           p->op==SQLITE_INDEX_CONSTRAINT_GLOB
3971e93173fSdan        || p->op==SQLITE_INDEX_CONSTRAINT_LIKE
3981e93173fSdan        || p->op==SQLITE_INDEX_CONSTRAINT_EQ
3991e93173fSdan     )){
4001e93173fSdan       struct sqlite3_index_constraint_usage *pUsage;
4011e93173fSdan       pUsage = &pIdxInfo->aConstraintUsage[ii];
4021e93173fSdan       pIdxInfo->idxNum = p->op;
4031e93173fSdan       pUsage->argvIndex = 1;
4041e93173fSdan       pIdxInfo->estimatedCost = 100000.0;
4051e93173fSdan       return SQLITE_OK;
4061e93173fSdan     }
4071e93173fSdan   }
4081e93173fSdan 
4091e93173fSdan   pIdxInfo->estimatedCost = 1000000000.0;
4101e93173fSdan   return SQLITE_OK;
4111e93173fSdan }
4121e93173fSdan 
4131e93173fSdan /*
4141e93173fSdan ** xOpen implementation.
4151e93173fSdan **
4161e93173fSdan ** Open a new fstree cursor.
4171e93173fSdan */
fstreeOpen(sqlite3_vtab * pVTab,sqlite3_vtab_cursor ** ppCursor)4181e93173fSdan static int fstreeOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
4191e93173fSdan   FstreeCsr *pCur;
4201e93173fSdan   pCur = (FstreeCsr*)sqlite3_malloc(sizeof(FstreeCsr));
4211e93173fSdan   if( pCur==0 ) return SQLITE_NOMEM;
4221e93173fSdan   memset(pCur, 0, sizeof(FstreeCsr));
4231e93173fSdan   pCur->fd = -1;
4241e93173fSdan   *ppCursor = &pCur->base;
4251e93173fSdan   return SQLITE_OK;
4261e93173fSdan }
4271e93173fSdan 
fstreeCloseFd(FstreeCsr * pCsr)4281e93173fSdan static void fstreeCloseFd(FstreeCsr *pCsr){
4291e93173fSdan   if( pCsr->fd>=0 ){
4301e93173fSdan     close(pCsr->fd);
4311e93173fSdan     pCsr->fd = -1;
4321e93173fSdan   }
4331e93173fSdan }
4341e93173fSdan 
4351e93173fSdan /*
4361e93173fSdan ** Close a fstree cursor.
4371e93173fSdan */
fstreeClose(sqlite3_vtab_cursor * cur)4381e93173fSdan static int fstreeClose(sqlite3_vtab_cursor *cur){
4391e93173fSdan   FstreeCsr *pCsr = (FstreeCsr*)cur;
4401e93173fSdan   sqlite3_finalize(pCsr->pStmt);
4411e93173fSdan   fstreeCloseFd(pCsr);
4421e93173fSdan   sqlite3_free(pCsr);
4431e93173fSdan   return SQLITE_OK;
4441e93173fSdan }
4451e93173fSdan 
4461e93173fSdan /*
4471e93173fSdan ** Skip the cursor to the next entry.
4481e93173fSdan */
fstreeNext(sqlite3_vtab_cursor * cur)4491e93173fSdan static int fstreeNext(sqlite3_vtab_cursor *cur){
4501e93173fSdan   FstreeCsr *pCsr = (FstreeCsr*)cur;
4511e93173fSdan   int rc;
4521e93173fSdan 
4531e93173fSdan   fstreeCloseFd(pCsr);
4541e93173fSdan   rc = sqlite3_step(pCsr->pStmt);
4551e93173fSdan   if( rc!=SQLITE_ROW ){
4561e93173fSdan     rc = sqlite3_finalize(pCsr->pStmt);
4571e93173fSdan     pCsr->pStmt = 0;
4581e93173fSdan   }else{
4591e93173fSdan     rc = SQLITE_OK;
460489f1e86Sdrh     pCsr->fd = open((const char*)sqlite3_column_text(pCsr->pStmt, 0), O_RDONLY);
4611e93173fSdan   }
4621e93173fSdan 
4631e93173fSdan   return rc;
4641e93173fSdan }
4651e93173fSdan 
4661e93173fSdan /*
4671e93173fSdan ** xFilter method implementation.
4681e93173fSdan */
fstreeFilter(sqlite3_vtab_cursor * pVtabCursor,int idxNum,const char * idxStr,int argc,sqlite3_value ** argv)4691e93173fSdan static int fstreeFilter(
4701e93173fSdan   sqlite3_vtab_cursor *pVtabCursor,
4711e93173fSdan   int idxNum, const char *idxStr,
4721e93173fSdan   int argc, sqlite3_value **argv
4731e93173fSdan ){
4741e93173fSdan   FstreeCsr *pCsr = (FstreeCsr*)pVtabCursor;
4751e93173fSdan   FstreeVtab *pTab = (FstreeVtab*)(pCsr->base.pVtab);
4761e93173fSdan   int rc;
4771e93173fSdan   const char *zSql =
4781e93173fSdan "WITH r(d) AS ("
47992af1ebcSmistachkin "  SELECT CASE WHEN dir=?2 THEN ?3 ELSE dir END || '/' || name "
48092af1ebcSmistachkin "    FROM fsdir WHERE dir=?1 AND name NOT LIKE '.%'"
4811e93173fSdan "  UNION ALL"
4821e93173fSdan "  SELECT dir || '/' || name FROM r, fsdir WHERE dir=d AND name NOT LIKE '.%'"
4831e93173fSdan ") SELECT d FROM r;";
4841e93173fSdan 
48592af1ebcSmistachkin   char *zRoot;
48692af1ebcSmistachkin   int nRoot;
48792af1ebcSmistachkin   char *zPrefix;
48892af1ebcSmistachkin   int nPrefix;
48992af1ebcSmistachkin   const char *zDir;
49092af1ebcSmistachkin   int nDir;
4911e93173fSdan   char aWild[2] = { '\0', '\0' };
4921e93173fSdan 
49392af1ebcSmistachkin #if SQLITE_OS_WIN
4947dd7d98bSmistachkin   const char *zDrive = windirent_getenv("fstreeDrive");
4957dd7d98bSmistachkin   if( zDrive==0 ){
4967dd7d98bSmistachkin     zDrive = windirent_getenv("SystemDrive");
4977dd7d98bSmistachkin   }
4987dd7d98bSmistachkin   zRoot = sqlite3_mprintf("%s%c", zDrive, '/');
499e683b898Sdrh   nRoot = sqlite3Strlen30(zRoot);
5007dd7d98bSmistachkin   zPrefix = sqlite3_mprintf("%s", zDrive);
501e683b898Sdrh   nPrefix = sqlite3Strlen30(zPrefix);
50292af1ebcSmistachkin #else
50392af1ebcSmistachkin   zRoot = "/";
50492af1ebcSmistachkin   nRoot = 1;
50592af1ebcSmistachkin   zPrefix = "";
50692af1ebcSmistachkin   nPrefix = 0;
50792af1ebcSmistachkin #endif
50892af1ebcSmistachkin 
50992af1ebcSmistachkin   zDir = zRoot;
51092af1ebcSmistachkin   nDir = nRoot;
51192af1ebcSmistachkin 
5121e93173fSdan   fstreeCloseFd(pCsr);
5131e93173fSdan   sqlite3_finalize(pCsr->pStmt);
5141e93173fSdan   pCsr->pStmt = 0;
5151e93173fSdan   rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0);
5161e93173fSdan   if( rc!=SQLITE_OK ) return rc;
5171e93173fSdan 
5181e93173fSdan   if( idxNum ){
5191e93173fSdan     const char *zQuery = (const char*)sqlite3_value_text(argv[0]);
5201e93173fSdan     switch( idxNum ){
5211e93173fSdan       case SQLITE_INDEX_CONSTRAINT_GLOB:
5221e93173fSdan         aWild[0] = '*';
5231e93173fSdan         aWild[1] = '?';
5241e93173fSdan         break;
5251e93173fSdan       case SQLITE_INDEX_CONSTRAINT_LIKE:
5261e93173fSdan         aWild[0] = '_';
5271e93173fSdan         aWild[1] = '%';
5281e93173fSdan         break;
5291e93173fSdan     }
5301e93173fSdan 
53192af1ebcSmistachkin     if( sqlite3_strnicmp(zQuery, zPrefix, nPrefix)==0 ){
5321e93173fSdan       int i;
53392af1ebcSmistachkin       for(i=nPrefix; zQuery[i]; i++){
5341e93173fSdan         if( zQuery[i]==aWild[0] || zQuery[i]==aWild[1] ) break;
5351e93173fSdan         if( zQuery[i]=='/' ) nDir = i;
5361e93173fSdan       }
5371e93173fSdan       zDir = zQuery;
5381e93173fSdan     }
5391e93173fSdan   }
540b1d66c59Sdan   if( nDir==0 ) nDir = 1;
5411e93173fSdan 
5421e93173fSdan   sqlite3_bind_text(pCsr->pStmt, 1, zDir, nDir, SQLITE_TRANSIENT);
54392af1ebcSmistachkin   sqlite3_bind_text(pCsr->pStmt, 2, zRoot, nRoot, SQLITE_TRANSIENT);
54492af1ebcSmistachkin   sqlite3_bind_text(pCsr->pStmt, 3, zPrefix, nPrefix, SQLITE_TRANSIENT);
54592af1ebcSmistachkin 
54692af1ebcSmistachkin #if SQLITE_OS_WIN
54792af1ebcSmistachkin   sqlite3_free(zPrefix);
54892af1ebcSmistachkin   sqlite3_free(zRoot);
54992af1ebcSmistachkin #endif
5501e93173fSdan 
5511e93173fSdan   return fstreeNext(pVtabCursor);
5521e93173fSdan }
5531e93173fSdan 
5541e93173fSdan /*
5551e93173fSdan ** xEof method implementation.
5561e93173fSdan */
fstreeEof(sqlite3_vtab_cursor * cur)5571e93173fSdan static int fstreeEof(sqlite3_vtab_cursor *cur){
5581e93173fSdan   FstreeCsr *pCsr = (FstreeCsr*)cur;
5591e93173fSdan   return pCsr->pStmt==0;
5601e93173fSdan }
5611e93173fSdan 
5621e93173fSdan /*
5631e93173fSdan ** xColumn method implementation.
5641e93173fSdan */
fstreeColumn(sqlite3_vtab_cursor * cur,sqlite3_context * ctx,int i)5651e93173fSdan static int fstreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
5661e93173fSdan   FstreeCsr *pCsr = (FstreeCsr*)cur;
5671e93173fSdan   if( i==0 ){      /* path */
5681e93173fSdan     sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pStmt, 0));
5691e93173fSdan   }else{
5701e93173fSdan     struct stat sBuf;
5711e93173fSdan     fstat(pCsr->fd, &sBuf);
5721e93173fSdan 
5731e93173fSdan     if( S_ISREG(sBuf.st_mode) ){
5741e93173fSdan       if( i==1 ){
5751e93173fSdan         sqlite3_result_int64(ctx, sBuf.st_size);
5761e93173fSdan       }else{
5771e93173fSdan         int nRead;
5781e93173fSdan         char *aBuf = sqlite3_malloc(sBuf.st_mode+1);
5791e93173fSdan         if( !aBuf ) return SQLITE_NOMEM;
5801e93173fSdan         nRead = read(pCsr->fd, aBuf, sBuf.st_mode);
5811e93173fSdan         if( nRead!=sBuf.st_mode ){
5821e93173fSdan           return SQLITE_IOERR;
5831e93173fSdan         }
5841e93173fSdan         sqlite3_result_blob(ctx, aBuf, nRead, SQLITE_TRANSIENT);
5851e93173fSdan         sqlite3_free(aBuf);
5861e93173fSdan       }
5871e93173fSdan     }
5881e93173fSdan   }
5891e93173fSdan 
5901e93173fSdan   return SQLITE_OK;
5911e93173fSdan }
5921e93173fSdan 
5931e93173fSdan /*
5941e93173fSdan ** xRowid method implementation.
5951e93173fSdan */
fstreeRowid(sqlite3_vtab_cursor * cur,sqlite_int64 * pRowid)5961e93173fSdan static int fstreeRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
5971e93173fSdan   *pRowid = 0;
5981e93173fSdan   return SQLITE_OK;
5991e93173fSdan }
6001e93173fSdan /*
6011e93173fSdan ** End of fstree implementation.
6021e93173fSdan *************************************************************************/
6031e93173fSdan 
6041e93173fSdan 
6051e93173fSdan 
6061e93173fSdan 
6079f5ff371Sdan /*
6089f5ff371Sdan ** This function is the implementation of both the xConnect and xCreate
6099f5ff371Sdan ** methods of the fs virtual table.
6109f5ff371Sdan **
6119f5ff371Sdan ** The argv[] array contains the following:
6129f5ff371Sdan **
6139f5ff371Sdan **   argv[0]   -> module name  ("fs")
6149f5ff371Sdan **   argv[1]   -> database name
6159f5ff371Sdan **   argv[2]   -> table name
6169f5ff371Sdan **   argv[...] -> other module argument fields.
6179f5ff371Sdan */
fsConnect(sqlite3 * db,void * pAux,int argc,const char * const * argv,sqlite3_vtab ** ppVtab,char ** pzErr)6189f5ff371Sdan static int fsConnect(
6199f5ff371Sdan   sqlite3 *db,
6209f5ff371Sdan   void *pAux,
6219f5ff371Sdan   int argc, const char *const*argv,
6229f5ff371Sdan   sqlite3_vtab **ppVtab,
6239f5ff371Sdan   char **pzErr
6249f5ff371Sdan ){
6259f5ff371Sdan   fs_vtab *pVtab;
6269f5ff371Sdan   int nByte;
6279f5ff371Sdan   const char *zTbl;
6289f5ff371Sdan   const char *zDb = argv[1];
6299f5ff371Sdan 
6309f5ff371Sdan   if( argc!=4 ){
6319f5ff371Sdan     *pzErr = sqlite3_mprintf("wrong number of arguments");
6329f5ff371Sdan     return SQLITE_ERROR;
6339f5ff371Sdan   }
6349f5ff371Sdan   zTbl = argv[3];
6359f5ff371Sdan 
6364b11bad3Sdrh   nByte = sizeof(fs_vtab) + (int)strlen(zTbl) + 1 + (int)strlen(zDb) + 1;
6379f5ff371Sdan   pVtab = (fs_vtab *)sqlite3MallocZero( nByte );
6389f5ff371Sdan   if( !pVtab ) return SQLITE_NOMEM;
6399f5ff371Sdan 
6409f5ff371Sdan   pVtab->zTbl = (char *)&pVtab[1];
6419f5ff371Sdan   pVtab->zDb = &pVtab->zTbl[strlen(zTbl)+1];
6429f5ff371Sdan   pVtab->db = db;
6439f5ff371Sdan   memcpy(pVtab->zTbl, zTbl, strlen(zTbl));
6449f5ff371Sdan   memcpy(pVtab->zDb, zDb, strlen(zDb));
6459f5ff371Sdan   *ppVtab = &pVtab->base;
6461e93173fSdan   sqlite3_declare_vtab(db, "CREATE TABLE x(path TEXT, data TEXT)");
6479f5ff371Sdan 
6489f5ff371Sdan   return SQLITE_OK;
6499f5ff371Sdan }
6509f5ff371Sdan /* Note that for this virtual table, the xCreate and xConnect
6519f5ff371Sdan ** methods are identical. */
6529f5ff371Sdan 
fsDisconnect(sqlite3_vtab * pVtab)6539f5ff371Sdan static int fsDisconnect(sqlite3_vtab *pVtab){
6549f5ff371Sdan   sqlite3_free(pVtab);
6559f5ff371Sdan   return SQLITE_OK;
6569f5ff371Sdan }
6579f5ff371Sdan /* The xDisconnect and xDestroy methods are also the same */
6589f5ff371Sdan 
6599f5ff371Sdan /*
6609f5ff371Sdan ** Open a new fs cursor.
6619f5ff371Sdan */
fsOpen(sqlite3_vtab * pVTab,sqlite3_vtab_cursor ** ppCursor)6629f5ff371Sdan static int fsOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
6639f5ff371Sdan   fs_cursor *pCur;
6649f5ff371Sdan   pCur = sqlite3MallocZero(sizeof(fs_cursor));
6659f5ff371Sdan   *ppCursor = &pCur->base;
6669f5ff371Sdan   return SQLITE_OK;
6679f5ff371Sdan }
6689f5ff371Sdan 
6699f5ff371Sdan /*
6709f5ff371Sdan ** Close a fs cursor.
6719f5ff371Sdan */
fsClose(sqlite3_vtab_cursor * cur)6729f5ff371Sdan static int fsClose(sqlite3_vtab_cursor *cur){
6739f5ff371Sdan   fs_cursor *pCur = (fs_cursor *)cur;
6749f5ff371Sdan   sqlite3_finalize(pCur->pStmt);
6759f5ff371Sdan   sqlite3_free(pCur->zBuf);
6769f5ff371Sdan   sqlite3_free(pCur);
6779f5ff371Sdan   return SQLITE_OK;
6789f5ff371Sdan }
6799f5ff371Sdan 
fsNext(sqlite3_vtab_cursor * cur)6809f5ff371Sdan static int fsNext(sqlite3_vtab_cursor *cur){
6819f5ff371Sdan   fs_cursor *pCur = (fs_cursor *)cur;
6829f5ff371Sdan   int rc;
6839f5ff371Sdan 
6849f5ff371Sdan   rc = sqlite3_step(pCur->pStmt);
6859f5ff371Sdan   if( rc==SQLITE_ROW || rc==SQLITE_DONE ) rc = SQLITE_OK;
6869f5ff371Sdan 
6879f5ff371Sdan   return rc;
6889f5ff371Sdan }
6899f5ff371Sdan 
fsFilter(sqlite3_vtab_cursor * pVtabCursor,int idxNum,const char * idxStr,int argc,sqlite3_value ** argv)6909f5ff371Sdan static int fsFilter(
6919f5ff371Sdan   sqlite3_vtab_cursor *pVtabCursor,
6929f5ff371Sdan   int idxNum, const char *idxStr,
6939f5ff371Sdan   int argc, sqlite3_value **argv
6949f5ff371Sdan ){
6959f5ff371Sdan   int rc;
6969f5ff371Sdan   fs_cursor *pCur = (fs_cursor *)pVtabCursor;
6979f5ff371Sdan   fs_vtab *p = (fs_vtab *)(pVtabCursor->pVtab);
6989f5ff371Sdan 
6999f5ff371Sdan   assert( (idxNum==0 && argc==0) || (idxNum==1 && argc==1) );
7009f5ff371Sdan   if( idxNum==1 ){
7019f5ff371Sdan     char *zStmt = sqlite3_mprintf(
7029f5ff371Sdan         "SELECT * FROM %Q.%Q WHERE rowid=?", p->zDb, p->zTbl);
7039f5ff371Sdan     if( !zStmt ) return SQLITE_NOMEM;
7049f5ff371Sdan     rc = sqlite3_prepare_v2(p->db, zStmt, -1, &pCur->pStmt, 0);
7059f5ff371Sdan     sqlite3_free(zStmt);
7069f5ff371Sdan     if( rc==SQLITE_OK ){
7079f5ff371Sdan       sqlite3_bind_value(pCur->pStmt, 1, argv[0]);
7089f5ff371Sdan     }
7099f5ff371Sdan   }else{
7109f5ff371Sdan     char *zStmt = sqlite3_mprintf("SELECT * FROM %Q.%Q", p->zDb, p->zTbl);
7119f5ff371Sdan     if( !zStmt ) return SQLITE_NOMEM;
7129f5ff371Sdan     rc = sqlite3_prepare_v2(p->db, zStmt, -1, &pCur->pStmt, 0);
7139f5ff371Sdan     sqlite3_free(zStmt);
7149f5ff371Sdan   }
7159f5ff371Sdan 
7169f5ff371Sdan   if( rc==SQLITE_OK ){
7179f5ff371Sdan     rc = fsNext(pVtabCursor);
7189f5ff371Sdan   }
7199f5ff371Sdan   return rc;
7209f5ff371Sdan }
7219f5ff371Sdan 
fsColumn(sqlite3_vtab_cursor * cur,sqlite3_context * ctx,int i)7229f5ff371Sdan static int fsColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
7239f5ff371Sdan   fs_cursor *pCur = (fs_cursor*)cur;
7249f5ff371Sdan 
7251e93173fSdan   assert( i==0 || i==1 || i==2 );
7269f5ff371Sdan   if( i==0 ){
7279f5ff371Sdan     sqlite3_result_value(ctx, sqlite3_column_value(pCur->pStmt, 0));
7289f5ff371Sdan   }else{
7299f5ff371Sdan     const char *zFile = (const char *)sqlite3_column_text(pCur->pStmt, 1);
7309f5ff371Sdan     struct stat sbuf;
7319f5ff371Sdan     int fd;
7329f5ff371Sdan 
7331e93173fSdan     int n;
7349f5ff371Sdan     fd = open(zFile, O_RDONLY);
7359f5ff371Sdan     if( fd<0 ) return SQLITE_IOERR;
7369f5ff371Sdan     fstat(fd, &sbuf);
7379f5ff371Sdan 
7389f5ff371Sdan     if( sbuf.st_size>=pCur->nAlloc ){
7390aa3231fSdrh       sqlite3_int64 nNew = sbuf.st_size*2;
7409f5ff371Sdan       char *zNew;
7419f5ff371Sdan       if( nNew<1024 ) nNew = 1024;
7429f5ff371Sdan 
7439f5ff371Sdan       zNew = sqlite3Realloc(pCur->zBuf, nNew);
7449f5ff371Sdan       if( zNew==0 ){
7459f5ff371Sdan         close(fd);
7469f5ff371Sdan         return SQLITE_NOMEM;
7479f5ff371Sdan       }
7489f5ff371Sdan       pCur->zBuf = zNew;
7499f5ff371Sdan       pCur->nAlloc = nNew;
7509f5ff371Sdan     }
7519f5ff371Sdan 
752d3f49641Sdrh     n = (int)read(fd, pCur->zBuf, sbuf.st_size);
7539f5ff371Sdan     close(fd);
754d3f49641Sdrh     if( n!=sbuf.st_size ) return SQLITE_ERROR;
7559f5ff371Sdan     pCur->nBuf = sbuf.st_size;
7569f5ff371Sdan     pCur->zBuf[pCur->nBuf] = '\0';
7579f5ff371Sdan 
7589f5ff371Sdan     sqlite3_result_text(ctx, pCur->zBuf, -1, SQLITE_TRANSIENT);
7599f5ff371Sdan   }
7609f5ff371Sdan   return SQLITE_OK;
7619f5ff371Sdan }
7629f5ff371Sdan 
fsRowid(sqlite3_vtab_cursor * cur,sqlite_int64 * pRowid)7639f5ff371Sdan static int fsRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
7649f5ff371Sdan   fs_cursor *pCur = (fs_cursor*)cur;
7659f5ff371Sdan   *pRowid = sqlite3_column_int64(pCur->pStmt, 0);
7669f5ff371Sdan   return SQLITE_OK;
7679f5ff371Sdan }
7689f5ff371Sdan 
fsEof(sqlite3_vtab_cursor * cur)7699f5ff371Sdan static int fsEof(sqlite3_vtab_cursor *cur){
7709f5ff371Sdan   fs_cursor *pCur = (fs_cursor*)cur;
7719f5ff371Sdan   return (sqlite3_data_count(pCur->pStmt)==0);
7729f5ff371Sdan }
7739f5ff371Sdan 
fsBestIndex(sqlite3_vtab * tab,sqlite3_index_info * pIdxInfo)7749f5ff371Sdan static int fsBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
7759f5ff371Sdan   int ii;
7769f5ff371Sdan 
7779f5ff371Sdan   for(ii=0; ii<pIdxInfo->nConstraint; ii++){
7789f5ff371Sdan     struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii];
7799f5ff371Sdan     if( pCons->iColumn<0 && pCons->usable
7809f5ff371Sdan            && pCons->op==SQLITE_INDEX_CONSTRAINT_EQ ){
7819f5ff371Sdan       struct sqlite3_index_constraint_usage *pUsage;
7829f5ff371Sdan       pUsage = &pIdxInfo->aConstraintUsage[ii];
7839f5ff371Sdan       pUsage->omit = 0;
7849f5ff371Sdan       pUsage->argvIndex = 1;
7859f5ff371Sdan       pIdxInfo->idxNum = 1;
7869f5ff371Sdan       pIdxInfo->estimatedCost = 1.0;
7879f5ff371Sdan       break;
7889f5ff371Sdan     }
7899f5ff371Sdan   }
7909f5ff371Sdan 
7919f5ff371Sdan   return SQLITE_OK;
7929f5ff371Sdan }
7939f5ff371Sdan 
7949f5ff371Sdan /*
7959f5ff371Sdan ** A virtual table module that provides read-only access to a
7969f5ff371Sdan ** Tcl global variable namespace.
7979f5ff371Sdan */
7989f5ff371Sdan static sqlite3_module fsModule = {
7999f5ff371Sdan   0,                         /* iVersion */
8009f5ff371Sdan   fsConnect,
8019f5ff371Sdan   fsConnect,
8029f5ff371Sdan   fsBestIndex,
8039f5ff371Sdan   fsDisconnect,
8049f5ff371Sdan   fsDisconnect,
8059f5ff371Sdan   fsOpen,                      /* xOpen - open a cursor */
8069f5ff371Sdan   fsClose,                     /* xClose - close a cursor */
8079f5ff371Sdan   fsFilter,                    /* xFilter - configure scan constraints */
8089f5ff371Sdan   fsNext,                      /* xNext - advance a cursor */
8099f5ff371Sdan   fsEof,                       /* xEof - check for end of scan */
8109f5ff371Sdan   fsColumn,                    /* xColumn - read data */
8119f5ff371Sdan   fsRowid,                     /* xRowid - read data */
8129f5ff371Sdan   0,                           /* xUpdate */
8139f5ff371Sdan   0,                           /* xBegin */
8149f5ff371Sdan   0,                           /* xSync */
8159f5ff371Sdan   0,                           /* xCommit */
8169f5ff371Sdan   0,                           /* xRollback */
8179f5ff371Sdan   0,                           /* xFindMethod */
8189f5ff371Sdan   0,                           /* xRename */
8199f5ff371Sdan };
8209f5ff371Sdan 
8211e93173fSdan static sqlite3_module fsdirModule = {
8221e93173fSdan   0,                              /* iVersion */
8231e93173fSdan   fsdirConnect,                   /* xCreate */
8241e93173fSdan   fsdirConnect,                   /* xConnect */
8251e93173fSdan   fsdirBestIndex,                 /* xBestIndex */
8261e93173fSdan   fsdirDisconnect,                /* xDisconnect */
8271e93173fSdan   fsdirDisconnect,                /* xDestroy */
8281e93173fSdan   fsdirOpen,                      /* xOpen - open a cursor */
8291e93173fSdan   fsdirClose,                     /* xClose - close a cursor */
8301e93173fSdan   fsdirFilter,                    /* xFilter - configure scan constraints */
8311e93173fSdan   fsdirNext,                      /* xNext - advance a cursor */
8321e93173fSdan   fsdirEof,                       /* xEof - check for end of scan */
8331e93173fSdan   fsdirColumn,                    /* xColumn - read data */
8341e93173fSdan   fsdirRowid,                     /* xRowid - read data */
8351e93173fSdan   0,                              /* xUpdate */
8361e93173fSdan   0,                              /* xBegin */
8371e93173fSdan   0,                              /* xSync */
8381e93173fSdan   0,                              /* xCommit */
8391e93173fSdan   0,                              /* xRollback */
8401e93173fSdan   0,                              /* xFindMethod */
8411e93173fSdan   0,                              /* xRename */
8421e93173fSdan };
8431e93173fSdan 
8441e93173fSdan static sqlite3_module fstreeModule = {
8451e93173fSdan   0,                              /* iVersion */
8461e93173fSdan   fstreeConnect,                  /* xCreate */
8471e93173fSdan   fstreeConnect,                  /* xConnect */
8481e93173fSdan   fstreeBestIndex,                /* xBestIndex */
8491e93173fSdan   fstreeDisconnect,               /* xDisconnect */
8501e93173fSdan   fstreeDisconnect,               /* xDestroy */
8511e93173fSdan   fstreeOpen,                     /* xOpen - open a cursor */
8521e93173fSdan   fstreeClose,                    /* xClose - close a cursor */
8531e93173fSdan   fstreeFilter,                   /* xFilter - configure scan constraints */
8541e93173fSdan   fstreeNext,                     /* xNext - advance a cursor */
8551e93173fSdan   fstreeEof,                      /* xEof - check for end of scan */
8561e93173fSdan   fstreeColumn,                   /* xColumn - read data */
8571e93173fSdan   fstreeRowid,                    /* xRowid - read data */
8581e93173fSdan   0,                              /* xUpdate */
8591e93173fSdan   0,                              /* xBegin */
8601e93173fSdan   0,                              /* xSync */
8611e93173fSdan   0,                              /* xCommit */
8621e93173fSdan   0,                              /* xRollback */
8631e93173fSdan   0,                              /* xFindMethod */
8641e93173fSdan   0,                              /* xRename */
8651e93173fSdan };
8661e93173fSdan 
8679f5ff371Sdan /*
8689f5ff371Sdan ** Decode a pointer to an sqlite3 object.
8699f5ff371Sdan */
8709f5ff371Sdan extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
8719f5ff371Sdan 
8729f5ff371Sdan /*
8739f5ff371Sdan ** Register the echo virtual table module.
8749f5ff371Sdan */
register_fs_module(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])8757617e4a8Smistachkin static int SQLITE_TCLAPI register_fs_module(
8769f5ff371Sdan   ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
8779f5ff371Sdan   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
8789f5ff371Sdan   int objc,              /* Number of arguments */
8799f5ff371Sdan   Tcl_Obj *CONST objv[]  /* Command arguments */
8809f5ff371Sdan ){
8819f5ff371Sdan   sqlite3 *db;
8829f5ff371Sdan   if( objc!=2 ){
8839f5ff371Sdan     Tcl_WrongNumArgs(interp, 1, objv, "DB");
8849f5ff371Sdan     return TCL_ERROR;
8859f5ff371Sdan   }
8869f5ff371Sdan   if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
8879f5ff371Sdan #ifndef SQLITE_OMIT_VIRTUALTABLE
8889f5ff371Sdan   sqlite3_create_module(db, "fs", &fsModule, (void *)interp);
8891e93173fSdan   sqlite3_create_module(db, "fsdir", &fsdirModule, 0);
8901e93173fSdan   sqlite3_create_module(db, "fstree", &fstreeModule, 0);
8919f5ff371Sdan #endif
8929f5ff371Sdan   return TCL_OK;
8939f5ff371Sdan }
8949f5ff371Sdan 
8959f5ff371Sdan #endif
8969f5ff371Sdan 
8979f5ff371Sdan 
8989f5ff371Sdan /*
8999f5ff371Sdan ** Register commands with the TCL interpreter.
9009f5ff371Sdan */
Sqlitetestfs_Init(Tcl_Interp * interp)9019f5ff371Sdan int Sqlitetestfs_Init(Tcl_Interp *interp){
9029f5ff371Sdan #ifndef SQLITE_OMIT_VIRTUALTABLE
9039f5ff371Sdan   static struct {
9049f5ff371Sdan      char *zName;
9059f5ff371Sdan      Tcl_ObjCmdProc *xProc;
9069f5ff371Sdan      void *clientData;
9079f5ff371Sdan   } aObjCmd[] = {
9089f5ff371Sdan      { "register_fs_module",   register_fs_module, 0 },
9099f5ff371Sdan   };
9109f5ff371Sdan   int i;
9119f5ff371Sdan   for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
9129f5ff371Sdan     Tcl_CreateObjCommand(interp, aObjCmd[i].zName,
9139f5ff371Sdan         aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
9149f5ff371Sdan   }
9159f5ff371Sdan #endif
9169f5ff371Sdan   return TCL_OK;
9179f5ff371Sdan }
918