modernc.org/cc@v1.0.1/v2/testdata/_sqlite/src/test_fs.c (about)

     1  /*
     2  ** 2013 Jan 11
     3  **
     4  ** The author disclaims copyright to this source code.  In place of
     5  ** a legal notice, here is a blessing:
     6  **
     7  **    May you do good and not evil.
     8  **    May you find forgiveness for yourself and forgive others.
     9  **    May you share freely, never taking more than you give.
    10  **
    11  *************************************************************************
    12  ** Code for testing the virtual table interfaces.  This code
    13  ** is not included in the SQLite library.  It is used for automated
    14  ** testing of the SQLite library.
    15  **
    16  ** The FS virtual table is created as follows:
    17  **
    18  **   CREATE VIRTUAL TABLE tbl USING fs(idx);
    19  **
    20  ** where idx is the name of a table in the db with 2 columns.  The virtual
    21  ** table also has two columns - file path and file contents.
    22  **
    23  ** The first column of table idx must be an IPK, and the second contains file
    24  ** paths. For example:
    25  **
    26  **   CREATE TABLE idx(id INTEGER PRIMARY KEY, path TEXT);
    27  **   INSERT INTO idx VALUES(4, '/etc/passwd');
    28  **
    29  ** Adding the row to the idx table automatically creates a row in the 
    30  ** virtual table with rowid=4, path=/etc/passwd and a text field that 
    31  ** contains data read from file /etc/passwd on disk.
    32  **
    33  *************************************************************************
    34  ** Virtual table module "fsdir"
    35  **
    36  ** This module is designed to be used as a read-only eponymous virtual table.
    37  ** Its schema is as follows:
    38  **
    39  **   CREATE TABLE fsdir(dir TEXT, name TEXT);
    40  **
    41  ** When queried, a WHERE term of the form "dir = $dir" must be provided. The
    42  ** virtual table then appears to have one row for each entry in file-system
    43  ** directory $dir. Column dir contains a copy of $dir, and column "name"
    44  ** contains the name of the directory entry.
    45  **
    46  ** If the specified $dir cannot be opened or is not a directory, it is not
    47  ** an error. The virtual table appears to be empty in this case.
    48  **
    49  *************************************************************************
    50  ** Virtual table module "fstree"
    51  **
    52  ** This module is also a read-only eponymous virtual table with the 
    53  ** following schema:
    54  **
    55  **   CREATE TABLE fstree(path TEXT, size INT, data BLOB);
    56  **
    57  ** Running a "SELECT * FROM fstree" query on this table returns the entire
    58  ** contents of the file-system, starting at "/". To restrict the search
    59  ** space, the virtual table supports LIKE and GLOB constraints on the
    60  ** 'path' column. For example:
    61  **
    62  **   SELECT * FROM fstree WHERE path LIKE '/home/dan/sqlite/%'
    63  */
    64  #include "sqliteInt.h"
    65  #if defined(INCLUDE_SQLITE_TCL_H)
    66  #  include "sqlite_tcl.h"
    67  #else
    68  #  include "tcl.h"
    69  #endif
    70  
    71  #include <stdlib.h>
    72  #include <string.h>
    73  #include <sys/types.h>
    74  #include <sys/stat.h>
    75  #include <fcntl.h>
    76  
    77  #if SQLITE_OS_UNIX || defined(__MINGW_H)
    78  # include <unistd.h>
    79  # include <dirent.h>
    80  # ifndef DIRENT
    81  #  define DIRENT dirent
    82  # endif
    83  #endif
    84  #if SQLITE_OS_WIN
    85  # include <io.h>
    86  # if !defined(__MINGW_H)
    87  #  include "test_windirent.h"
    88  # endif
    89  # ifndef S_ISREG
    90  #  define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
    91  # endif
    92  #endif
    93  
    94  #ifndef SQLITE_OMIT_VIRTUALTABLE
    95  
    96  typedef struct fs_vtab fs_vtab;
    97  typedef struct fs_cursor fs_cursor;
    98  
    99  /* 
   100  ** A fs virtual-table object 
   101  */
   102  struct fs_vtab {
   103    sqlite3_vtab base;
   104    sqlite3 *db;
   105    char *zDb;                      /* Name of db containing zTbl */
   106    char *zTbl;                     /* Name of docid->file map table */
   107  };
   108  
   109  /* A fs cursor object */
   110  struct fs_cursor {
   111    sqlite3_vtab_cursor base;
   112    sqlite3_stmt *pStmt;
   113    char *zBuf;
   114    int nBuf;
   115    int nAlloc;
   116  };
   117  
   118  /*************************************************************************
   119  ** Start of fsdir implementation.
   120  */
   121  typedef struct FsdirVtab FsdirVtab;
   122  typedef struct FsdirCsr FsdirCsr;
   123  struct FsdirVtab {
   124    sqlite3_vtab base;
   125  };
   126  
   127  struct FsdirCsr {
   128    sqlite3_vtab_cursor base;
   129    char *zDir;                     /* Buffer containing directory scanned */
   130    DIR *pDir;                      /* Open directory */
   131    sqlite3_int64 iRowid;
   132    struct DIRENT entry;            /* Current entry */
   133  };
   134  
   135  /*
   136  ** This function is the implementation of both the xConnect and xCreate
   137  ** methods of the fsdir virtual table.
   138  **
   139  ** The argv[] array contains the following:
   140  **
   141  **   argv[0]   -> module name  ("fs")
   142  **   argv[1]   -> database name
   143  **   argv[2]   -> table name
   144  **   argv[...] -> other module argument fields.
   145  */
   146  static int fsdirConnect(
   147    sqlite3 *db,
   148    void *pAux,
   149    int argc, const char *const*argv,
   150    sqlite3_vtab **ppVtab,
   151    char **pzErr
   152  ){
   153    FsdirVtab *pTab;
   154  
   155    if( argc!=3 ){
   156      *pzErr = sqlite3_mprintf("wrong number of arguments");
   157      return SQLITE_ERROR;
   158    }
   159  
   160    pTab = (FsdirVtab *)sqlite3_malloc(sizeof(FsdirVtab));
   161    if( !pTab ) return SQLITE_NOMEM;
   162    memset(pTab, 0, sizeof(FsdirVtab));
   163  
   164    *ppVtab = &pTab->base;
   165    sqlite3_declare_vtab(db, "CREATE TABLE xyz(dir, name);");
   166  
   167    return SQLITE_OK;
   168  }
   169  
   170  /*
   171  ** xDestroy/xDisconnect implementation.
   172  */
   173  static int fsdirDisconnect(sqlite3_vtab *pVtab){
   174    sqlite3_free(pVtab);
   175    return SQLITE_OK;
   176  }
   177  
   178  /*
   179  ** xBestIndex implementation. The only constraint supported is:
   180  **
   181  **   (dir = ?)
   182  */
   183  static int fsdirBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
   184    int ii;
   185  
   186    pIdxInfo->estimatedCost = 1000000000.0;
   187  
   188    for(ii=0; ii<pIdxInfo->nConstraint; ii++){
   189      struct sqlite3_index_constraint const *p = &pIdxInfo->aConstraint[ii];
   190      if( p->iColumn==0 && p->usable && p->op==SQLITE_INDEX_CONSTRAINT_EQ ){
   191        struct sqlite3_index_constraint_usage *pUsage;
   192        pUsage = &pIdxInfo->aConstraintUsage[ii];
   193        pUsage->omit = 1;
   194        pUsage->argvIndex = 1;
   195        pIdxInfo->idxNum = 1;
   196        pIdxInfo->estimatedCost = 1.0;
   197        break;
   198      }
   199    }
   200  
   201    return SQLITE_OK;
   202  }
   203  
   204  /*
   205  ** xOpen implementation.
   206  **
   207  ** Open a new fsdir cursor.
   208  */
   209  static int fsdirOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
   210    FsdirCsr *pCur;
   211    /* Allocate an extra 256 bytes because it is undefined how big dirent.d_name
   212    ** is and we need enough space.  Linux provides plenty already, but
   213    ** Solaris only provides one byte. */
   214    pCur = (FsdirCsr*)sqlite3_malloc(sizeof(FsdirCsr)+256);
   215    if( pCur==0 ) return SQLITE_NOMEM;
   216    memset(pCur, 0, sizeof(FsdirCsr));
   217    *ppCursor = &pCur->base;
   218    return SQLITE_OK;
   219  }
   220  
   221  /*
   222  ** Close a fsdir cursor.
   223  */
   224  static int fsdirClose(sqlite3_vtab_cursor *cur){
   225    FsdirCsr *pCur = (FsdirCsr*)cur;
   226    if( pCur->pDir ) closedir(pCur->pDir);
   227    sqlite3_free(pCur->zDir);
   228    sqlite3_free(pCur);
   229    return SQLITE_OK;
   230  }
   231  
   232  /*
   233  ** Skip the cursor to the next entry.
   234  */
   235  static int fsdirNext(sqlite3_vtab_cursor *cur){
   236    FsdirCsr *pCsr = (FsdirCsr*)cur;
   237  
   238    if( pCsr->pDir ){
   239      struct DIRENT *pRes = 0;
   240  #if defined(__MINGW_H)
   241      pRes = readdir(pCsr->pDir);
   242      if( pRes!=0 ){
   243        memcpy(&pCsr->entry, pRes, sizeof(struct DIRENT));
   244      }
   245  #else
   246      readdir_r(pCsr->pDir, &pCsr->entry, &pRes);
   247  #endif
   248      if( pRes==0 ){
   249        closedir(pCsr->pDir);
   250        pCsr->pDir = 0;
   251      }
   252      pCsr->iRowid++;
   253    }
   254  
   255    return SQLITE_OK;
   256  }
   257  
   258  /*
   259  ** xFilter method implementation.
   260  */
   261  static int fsdirFilter(
   262    sqlite3_vtab_cursor *pVtabCursor, 
   263    int idxNum, const char *idxStr,
   264    int argc, sqlite3_value **argv
   265  ){
   266    FsdirCsr *pCsr = (FsdirCsr*)pVtabCursor;
   267    const char *zDir;
   268    int nDir;
   269  
   270  
   271    if( idxNum!=1 || argc!=1 ){
   272      return SQLITE_ERROR;
   273    }
   274  
   275    pCsr->iRowid = 0;
   276    sqlite3_free(pCsr->zDir);
   277    if( pCsr->pDir ){
   278      closedir(pCsr->pDir);
   279      pCsr->pDir = 0;
   280    }
   281  
   282    zDir = (const char*)sqlite3_value_text(argv[0]);
   283    nDir = sqlite3_value_bytes(argv[0]);
   284    pCsr->zDir = sqlite3_malloc(nDir+1);
   285    if( pCsr->zDir==0 ) return SQLITE_NOMEM;
   286    memcpy(pCsr->zDir, zDir, nDir+1);
   287  
   288    pCsr->pDir = opendir(pCsr->zDir);
   289    return fsdirNext(pVtabCursor); 
   290  }
   291  
   292  /*
   293  ** xEof method implementation.
   294  */
   295  static int fsdirEof(sqlite3_vtab_cursor *cur){
   296    FsdirCsr *pCsr = (FsdirCsr*)cur;
   297    return pCsr->pDir==0;
   298  }
   299  
   300  /*
   301  ** xColumn method implementation.
   302  */
   303  static int fsdirColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
   304    FsdirCsr *pCsr = (FsdirCsr*)cur;
   305    switch( i ){
   306      case 0: /* dir */
   307        sqlite3_result_text(ctx, pCsr->zDir, -1, SQLITE_STATIC);
   308        break;
   309  
   310      case 1: /* name */
   311        sqlite3_result_text(ctx, pCsr->entry.d_name, -1, SQLITE_TRANSIENT);
   312        break;
   313  
   314      default:
   315        assert( 0 );
   316    }
   317  
   318    return SQLITE_OK;
   319  }
   320  
   321  /*
   322  ** xRowid method implementation.
   323  */
   324  static int fsdirRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
   325    FsdirCsr *pCsr = (FsdirCsr*)cur;
   326    *pRowid = pCsr->iRowid;
   327    return SQLITE_OK;
   328  }
   329  /*
   330  ** End of fsdir implementation.
   331  *************************************************************************/
   332  
   333  /*************************************************************************
   334  ** Start of fstree implementation.
   335  */
   336  typedef struct FstreeVtab FstreeVtab;
   337  typedef struct FstreeCsr FstreeCsr;
   338  struct FstreeVtab {
   339    sqlite3_vtab base;
   340    sqlite3 *db;
   341  };
   342  
   343  struct FstreeCsr {
   344    sqlite3_vtab_cursor base;
   345    sqlite3_stmt *pStmt;            /* Statement to list paths */
   346    int fd;                         /* File descriptor open on current path */
   347  };
   348  
   349  /*
   350  ** This function is the implementation of both the xConnect and xCreate
   351  ** methods of the fstree virtual table.
   352  **
   353  ** The argv[] array contains the following:
   354  **
   355  **   argv[0]   -> module name  ("fs")
   356  **   argv[1]   -> database name
   357  **   argv[2]   -> table name
   358  **   argv[...] -> other module argument fields.
   359  */
   360  static int fstreeConnect(
   361    sqlite3 *db,
   362    void *pAux,
   363    int argc, const char *const*argv,
   364    sqlite3_vtab **ppVtab,
   365    char **pzErr
   366  ){
   367    FstreeVtab *pTab;
   368  
   369    if( argc!=3 ){
   370      *pzErr = sqlite3_mprintf("wrong number of arguments");
   371      return SQLITE_ERROR;
   372    }
   373  
   374    pTab = (FstreeVtab *)sqlite3_malloc(sizeof(FstreeVtab));
   375    if( !pTab ) return SQLITE_NOMEM;
   376    memset(pTab, 0, sizeof(FstreeVtab));
   377    pTab->db = db;
   378  
   379    *ppVtab = &pTab->base;
   380    sqlite3_declare_vtab(db, "CREATE TABLE xyz(path, size, data);");
   381  
   382    return SQLITE_OK;
   383  }
   384  
   385  /*
   386  ** xDestroy/xDisconnect implementation.
   387  */
   388  static int fstreeDisconnect(sqlite3_vtab *pVtab){
   389    sqlite3_free(pVtab);
   390    return SQLITE_OK;
   391  }
   392  
   393  /*
   394  ** xBestIndex implementation. The only constraint supported is:
   395  **
   396  **   (dir = ?)
   397  */
   398  static int fstreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
   399    int ii;
   400  
   401    for(ii=0; ii<pIdxInfo->nConstraint; ii++){
   402      struct sqlite3_index_constraint const *p = &pIdxInfo->aConstraint[ii];
   403      if( p->iColumn==0 && p->usable && (
   404            p->op==SQLITE_INDEX_CONSTRAINT_GLOB
   405         || p->op==SQLITE_INDEX_CONSTRAINT_LIKE
   406         || p->op==SQLITE_INDEX_CONSTRAINT_EQ
   407      )){
   408        struct sqlite3_index_constraint_usage *pUsage;
   409        pUsage = &pIdxInfo->aConstraintUsage[ii];
   410        pIdxInfo->idxNum = p->op;
   411        pUsage->argvIndex = 1;
   412        pIdxInfo->estimatedCost = 100000.0;
   413        return SQLITE_OK;
   414      }
   415    }
   416  
   417    pIdxInfo->estimatedCost = 1000000000.0;
   418    return SQLITE_OK;
   419  }
   420  
   421  /*
   422  ** xOpen implementation.
   423  **
   424  ** Open a new fstree cursor.
   425  */
   426  static int fstreeOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
   427    FstreeCsr *pCur;
   428    pCur = (FstreeCsr*)sqlite3_malloc(sizeof(FstreeCsr));
   429    if( pCur==0 ) return SQLITE_NOMEM;
   430    memset(pCur, 0, sizeof(FstreeCsr));
   431    pCur->fd = -1;
   432    *ppCursor = &pCur->base;
   433    return SQLITE_OK;
   434  }
   435  
   436  static void fstreeCloseFd(FstreeCsr *pCsr){
   437    if( pCsr->fd>=0 ){
   438      close(pCsr->fd);
   439      pCsr->fd = -1;
   440    }
   441  }
   442  
   443  /*
   444  ** Close a fstree cursor.
   445  */
   446  static int fstreeClose(sqlite3_vtab_cursor *cur){
   447    FstreeCsr *pCsr = (FstreeCsr*)cur;
   448    sqlite3_finalize(pCsr->pStmt);
   449    fstreeCloseFd(pCsr);
   450    sqlite3_free(pCsr);
   451    return SQLITE_OK;
   452  }
   453  
   454  /*
   455  ** Skip the cursor to the next entry.
   456  */
   457  static int fstreeNext(sqlite3_vtab_cursor *cur){
   458    FstreeCsr *pCsr = (FstreeCsr*)cur;
   459    int rc;
   460  
   461    fstreeCloseFd(pCsr);
   462    rc = sqlite3_step(pCsr->pStmt);
   463    if( rc!=SQLITE_ROW ){
   464      rc = sqlite3_finalize(pCsr->pStmt);
   465      pCsr->pStmt = 0;
   466    }else{
   467      rc = SQLITE_OK;
   468      pCsr->fd = open((const char*)sqlite3_column_text(pCsr->pStmt, 0), O_RDONLY);
   469    }
   470  
   471    return rc;
   472  }
   473  
   474  /*
   475  ** xFilter method implementation.
   476  */
   477  static int fstreeFilter(
   478    sqlite3_vtab_cursor *pVtabCursor, 
   479    int idxNum, const char *idxStr,
   480    int argc, sqlite3_value **argv
   481  ){
   482    FstreeCsr *pCsr = (FstreeCsr*)pVtabCursor;
   483    FstreeVtab *pTab = (FstreeVtab*)(pCsr->base.pVtab);
   484    int rc;
   485    const char *zSql = 
   486  "WITH r(d) AS ("
   487  "  SELECT CASE WHEN dir=?2 THEN ?3 ELSE dir END || '/' || name "
   488  "    FROM fsdir WHERE dir=?1 AND name NOT LIKE '.%'"
   489  "  UNION ALL"
   490  "  SELECT dir || '/' || name FROM r, fsdir WHERE dir=d AND name NOT LIKE '.%'"
   491  ") SELECT d FROM r;";
   492  
   493    char *zRoot;
   494    int nRoot;
   495    char *zPrefix;
   496    int nPrefix;
   497    const char *zDir;
   498    int nDir;
   499    char aWild[2] = { '\0', '\0' };
   500  
   501  #if SQLITE_OS_WIN
   502    const char *zDrive = windirent_getenv("fstreeDrive");
   503    if( zDrive==0 ){
   504      zDrive = windirent_getenv("SystemDrive");
   505    }
   506    zRoot = sqlite3_mprintf("%s%c", zDrive, '/');
   507    nRoot = sqlite3Strlen30(zRoot);
   508    zPrefix = sqlite3_mprintf("%s", zDrive);
   509    nPrefix = sqlite3Strlen30(zPrefix);
   510  #else
   511    zRoot = "/";
   512    nRoot = 1;
   513    zPrefix = "";
   514    nPrefix = 0;
   515  #endif
   516  
   517    zDir = zRoot;
   518    nDir = nRoot;
   519  
   520    fstreeCloseFd(pCsr);
   521    sqlite3_finalize(pCsr->pStmt);
   522    pCsr->pStmt = 0;
   523    rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0);
   524    if( rc!=SQLITE_OK ) return rc;
   525  
   526    if( idxNum ){
   527      const char *zQuery = (const char*)sqlite3_value_text(argv[0]);
   528      switch( idxNum ){
   529        case SQLITE_INDEX_CONSTRAINT_GLOB:
   530          aWild[0] = '*';
   531          aWild[1] = '?';
   532          break;
   533        case SQLITE_INDEX_CONSTRAINT_LIKE:
   534          aWild[0] = '_';
   535          aWild[1] = '%';
   536          break;
   537      }
   538  
   539      if( sqlite3_strnicmp(zQuery, zPrefix, nPrefix)==0 ){
   540        int i;
   541        for(i=nPrefix; zQuery[i]; i++){
   542          if( zQuery[i]==aWild[0] || zQuery[i]==aWild[1] ) break;
   543          if( zQuery[i]=='/' ) nDir = i;
   544        }
   545        zDir = zQuery;
   546      }
   547    }
   548    if( nDir==0 ) nDir = 1;
   549  
   550    sqlite3_bind_text(pCsr->pStmt, 1, zDir, nDir, SQLITE_TRANSIENT);
   551    sqlite3_bind_text(pCsr->pStmt, 2, zRoot, nRoot, SQLITE_TRANSIENT);
   552    sqlite3_bind_text(pCsr->pStmt, 3, zPrefix, nPrefix, SQLITE_TRANSIENT);
   553  
   554  #if SQLITE_OS_WIN
   555    sqlite3_free(zPrefix);
   556    sqlite3_free(zRoot);
   557  #endif
   558  
   559    return fstreeNext(pVtabCursor); 
   560  }
   561  
   562  /*
   563  ** xEof method implementation.
   564  */
   565  static int fstreeEof(sqlite3_vtab_cursor *cur){
   566    FstreeCsr *pCsr = (FstreeCsr*)cur;
   567    return pCsr->pStmt==0;
   568  }
   569  
   570  /*
   571  ** xColumn method implementation.
   572  */
   573  static int fstreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
   574    FstreeCsr *pCsr = (FstreeCsr*)cur;
   575    if( i==0 ){      /* path */
   576      sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pStmt, 0));
   577    }else{
   578      struct stat sBuf;
   579      fstat(pCsr->fd, &sBuf);
   580  
   581      if( S_ISREG(sBuf.st_mode) ){
   582        if( i==1 ){
   583          sqlite3_result_int64(ctx, sBuf.st_size);
   584        }else{
   585          int nRead;
   586          char *aBuf = sqlite3_malloc(sBuf.st_mode+1);
   587          if( !aBuf ) return SQLITE_NOMEM;
   588          nRead = read(pCsr->fd, aBuf, sBuf.st_mode);
   589          if( nRead!=sBuf.st_mode ){
   590            return SQLITE_IOERR;
   591          }
   592          sqlite3_result_blob(ctx, aBuf, nRead, SQLITE_TRANSIENT);
   593          sqlite3_free(aBuf);
   594        }
   595      }
   596    }
   597  
   598    return SQLITE_OK;
   599  }
   600  
   601  /*
   602  ** xRowid method implementation.
   603  */
   604  static int fstreeRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
   605    *pRowid = 0;
   606    return SQLITE_OK;
   607  }
   608  /*
   609  ** End of fstree implementation.
   610  *************************************************************************/
   611  
   612  
   613  
   614  
   615  /*
   616  ** This function is the implementation of both the xConnect and xCreate
   617  ** methods of the fs virtual table.
   618  **
   619  ** The argv[] array contains the following:
   620  **
   621  **   argv[0]   -> module name  ("fs")
   622  **   argv[1]   -> database name
   623  **   argv[2]   -> table name
   624  **   argv[...] -> other module argument fields.
   625  */
   626  static int fsConnect(
   627    sqlite3 *db,
   628    void *pAux,
   629    int argc, const char *const*argv,
   630    sqlite3_vtab **ppVtab,
   631    char **pzErr
   632  ){
   633    fs_vtab *pVtab;
   634    int nByte;
   635    const char *zTbl;
   636    const char *zDb = argv[1];
   637  
   638    if( argc!=4 ){
   639      *pzErr = sqlite3_mprintf("wrong number of arguments");
   640      return SQLITE_ERROR;
   641    }
   642    zTbl = argv[3];
   643  
   644    nByte = sizeof(fs_vtab) + (int)strlen(zTbl) + 1 + (int)strlen(zDb) + 1;
   645    pVtab = (fs_vtab *)sqlite3MallocZero( nByte );
   646    if( !pVtab ) return SQLITE_NOMEM;
   647  
   648    pVtab->zTbl = (char *)&pVtab[1];
   649    pVtab->zDb = &pVtab->zTbl[strlen(zTbl)+1];
   650    pVtab->db = db;
   651    memcpy(pVtab->zTbl, zTbl, strlen(zTbl));
   652    memcpy(pVtab->zDb, zDb, strlen(zDb));
   653    *ppVtab = &pVtab->base;
   654    sqlite3_declare_vtab(db, "CREATE TABLE x(path TEXT, data TEXT)");
   655  
   656    return SQLITE_OK;
   657  }
   658  /* Note that for this virtual table, the xCreate and xConnect
   659  ** methods are identical. */
   660  
   661  static int fsDisconnect(sqlite3_vtab *pVtab){
   662    sqlite3_free(pVtab);
   663    return SQLITE_OK;
   664  }
   665  /* The xDisconnect and xDestroy methods are also the same */
   666  
   667  /*
   668  ** Open a new fs cursor.
   669  */
   670  static int fsOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
   671    fs_cursor *pCur;
   672    pCur = sqlite3MallocZero(sizeof(fs_cursor));
   673    *ppCursor = &pCur->base;
   674    return SQLITE_OK;
   675  }
   676  
   677  /*
   678  ** Close a fs cursor.
   679  */
   680  static int fsClose(sqlite3_vtab_cursor *cur){
   681    fs_cursor *pCur = (fs_cursor *)cur;
   682    sqlite3_finalize(pCur->pStmt);
   683    sqlite3_free(pCur->zBuf);
   684    sqlite3_free(pCur);
   685    return SQLITE_OK;
   686  }
   687  
   688  static int fsNext(sqlite3_vtab_cursor *cur){
   689    fs_cursor *pCur = (fs_cursor *)cur;
   690    int rc;
   691  
   692    rc = sqlite3_step(pCur->pStmt);
   693    if( rc==SQLITE_ROW || rc==SQLITE_DONE ) rc = SQLITE_OK;
   694  
   695    return rc;
   696  }
   697  
   698  static int fsFilter(
   699    sqlite3_vtab_cursor *pVtabCursor, 
   700    int idxNum, const char *idxStr,
   701    int argc, sqlite3_value **argv
   702  ){
   703    int rc;
   704    fs_cursor *pCur = (fs_cursor *)pVtabCursor;
   705    fs_vtab *p = (fs_vtab *)(pVtabCursor->pVtab);
   706  
   707    assert( (idxNum==0 && argc==0) || (idxNum==1 && argc==1) );
   708    if( idxNum==1 ){
   709      char *zStmt = sqlite3_mprintf(
   710          "SELECT * FROM %Q.%Q WHERE rowid=?", p->zDb, p->zTbl);
   711      if( !zStmt ) return SQLITE_NOMEM;
   712      rc = sqlite3_prepare_v2(p->db, zStmt, -1, &pCur->pStmt, 0);
   713      sqlite3_free(zStmt);
   714      if( rc==SQLITE_OK ){
   715        sqlite3_bind_value(pCur->pStmt, 1, argv[0]);
   716      }
   717    }else{
   718      char *zStmt = sqlite3_mprintf("SELECT * FROM %Q.%Q", p->zDb, p->zTbl);
   719      if( !zStmt ) return SQLITE_NOMEM;
   720      rc = sqlite3_prepare_v2(p->db, zStmt, -1, &pCur->pStmt, 0);
   721      sqlite3_free(zStmt);
   722    }
   723  
   724    if( rc==SQLITE_OK ){
   725      rc = fsNext(pVtabCursor); 
   726    }
   727    return rc;
   728  }
   729  
   730  static int fsColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
   731    fs_cursor *pCur = (fs_cursor*)cur;
   732  
   733    assert( i==0 || i==1 || i==2 );
   734    if( i==0 ){
   735      sqlite3_result_value(ctx, sqlite3_column_value(pCur->pStmt, 0));
   736    }else{
   737      const char *zFile = (const char *)sqlite3_column_text(pCur->pStmt, 1);
   738      struct stat sbuf;
   739      int fd;
   740  
   741      int n;
   742      fd = open(zFile, O_RDONLY);
   743      if( fd<0 ) return SQLITE_IOERR;
   744      fstat(fd, &sbuf);
   745  
   746      if( sbuf.st_size>=pCur->nAlloc ){
   747        int nNew = sbuf.st_size*2;
   748        char *zNew;
   749        if( nNew<1024 ) nNew = 1024;
   750  
   751        zNew = sqlite3Realloc(pCur->zBuf, nNew);
   752        if( zNew==0 ){
   753          close(fd);
   754          return SQLITE_NOMEM;
   755        }
   756        pCur->zBuf = zNew;
   757        pCur->nAlloc = nNew;
   758      }
   759  
   760      n = (int)read(fd, pCur->zBuf, sbuf.st_size);
   761      close(fd);
   762      if( n!=sbuf.st_size ) return SQLITE_ERROR;
   763      pCur->nBuf = sbuf.st_size;
   764      pCur->zBuf[pCur->nBuf] = '\0';
   765  
   766      sqlite3_result_text(ctx, pCur->zBuf, -1, SQLITE_TRANSIENT);
   767    }
   768    return SQLITE_OK;
   769  }
   770  
   771  static int fsRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
   772    fs_cursor *pCur = (fs_cursor*)cur;
   773    *pRowid = sqlite3_column_int64(pCur->pStmt, 0);
   774    return SQLITE_OK;
   775  }
   776  
   777  static int fsEof(sqlite3_vtab_cursor *cur){
   778    fs_cursor *pCur = (fs_cursor*)cur;
   779    return (sqlite3_data_count(pCur->pStmt)==0);
   780  }
   781  
   782  static int fsBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
   783    int ii;
   784  
   785    for(ii=0; ii<pIdxInfo->nConstraint; ii++){
   786      struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii];
   787      if( pCons->iColumn<0 && pCons->usable
   788             && pCons->op==SQLITE_INDEX_CONSTRAINT_EQ ){
   789        struct sqlite3_index_constraint_usage *pUsage;
   790        pUsage = &pIdxInfo->aConstraintUsage[ii];
   791        pUsage->omit = 0;
   792        pUsage->argvIndex = 1;
   793        pIdxInfo->idxNum = 1;
   794        pIdxInfo->estimatedCost = 1.0;
   795        break;
   796      }
   797    }
   798  
   799    return SQLITE_OK;
   800  }
   801  
   802  /*
   803  ** A virtual table module that provides read-only access to a
   804  ** Tcl global variable namespace.
   805  */
   806  static sqlite3_module fsModule = {
   807    0,                         /* iVersion */
   808    fsConnect,
   809    fsConnect,
   810    fsBestIndex,
   811    fsDisconnect, 
   812    fsDisconnect,
   813    fsOpen,                      /* xOpen - open a cursor */
   814    fsClose,                     /* xClose - close a cursor */
   815    fsFilter,                    /* xFilter - configure scan constraints */
   816    fsNext,                      /* xNext - advance a cursor */
   817    fsEof,                       /* xEof - check for end of scan */
   818    fsColumn,                    /* xColumn - read data */
   819    fsRowid,                     /* xRowid - read data */
   820    0,                           /* xUpdate */
   821    0,                           /* xBegin */
   822    0,                           /* xSync */
   823    0,                           /* xCommit */
   824    0,                           /* xRollback */
   825    0,                           /* xFindMethod */
   826    0,                           /* xRename */
   827  };
   828  
   829  static sqlite3_module fsdirModule = {
   830    0,                              /* iVersion */
   831    fsdirConnect,                   /* xCreate */
   832    fsdirConnect,                   /* xConnect */
   833    fsdirBestIndex,                 /* xBestIndex */
   834    fsdirDisconnect,                /* xDisconnect */
   835    fsdirDisconnect,                /* xDestroy */
   836    fsdirOpen,                      /* xOpen - open a cursor */
   837    fsdirClose,                     /* xClose - close a cursor */
   838    fsdirFilter,                    /* xFilter - configure scan constraints */
   839    fsdirNext,                      /* xNext - advance a cursor */
   840    fsdirEof,                       /* xEof - check for end of scan */
   841    fsdirColumn,                    /* xColumn - read data */
   842    fsdirRowid,                     /* xRowid - read data */
   843    0,                              /* xUpdate */
   844    0,                              /* xBegin */
   845    0,                              /* xSync */
   846    0,                              /* xCommit */
   847    0,                              /* xRollback */
   848    0,                              /* xFindMethod */
   849    0,                              /* xRename */
   850  };
   851  
   852  static sqlite3_module fstreeModule = {
   853    0,                              /* iVersion */
   854    fstreeConnect,                  /* xCreate */
   855    fstreeConnect,                  /* xConnect */
   856    fstreeBestIndex,                /* xBestIndex */
   857    fstreeDisconnect,               /* xDisconnect */
   858    fstreeDisconnect,               /* xDestroy */
   859    fstreeOpen,                     /* xOpen - open a cursor */
   860    fstreeClose,                    /* xClose - close a cursor */
   861    fstreeFilter,                   /* xFilter - configure scan constraints */
   862    fstreeNext,                     /* xNext - advance a cursor */
   863    fstreeEof,                      /* xEof - check for end of scan */
   864    fstreeColumn,                   /* xColumn - read data */
   865    fstreeRowid,                    /* xRowid - read data */
   866    0,                              /* xUpdate */
   867    0,                              /* xBegin */
   868    0,                              /* xSync */
   869    0,                              /* xCommit */
   870    0,                              /* xRollback */
   871    0,                              /* xFindMethod */
   872    0,                              /* xRename */
   873  };
   874  
   875  /*
   876  ** Decode a pointer to an sqlite3 object.
   877  */
   878  extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
   879  
   880  /*
   881  ** Register the echo virtual table module.
   882  */
   883  static int SQLITE_TCLAPI register_fs_module(
   884    ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
   885    Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   886    int objc,              /* Number of arguments */
   887    Tcl_Obj *CONST objv[]  /* Command arguments */
   888  ){
   889    sqlite3 *db;
   890    if( objc!=2 ){
   891      Tcl_WrongNumArgs(interp, 1, objv, "DB");
   892      return TCL_ERROR;
   893    }
   894    if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
   895  #ifndef SQLITE_OMIT_VIRTUALTABLE
   896    sqlite3_create_module(db, "fs", &fsModule, (void *)interp);
   897    sqlite3_create_module(db, "fsdir", &fsdirModule, 0);
   898    sqlite3_create_module(db, "fstree", &fstreeModule, 0);
   899  #endif
   900    return TCL_OK;
   901  }
   902  
   903  #endif
   904  
   905  
   906  /*
   907  ** Register commands with the TCL interpreter.
   908  */
   909  int Sqlitetestfs_Init(Tcl_Interp *interp){
   910  #ifndef SQLITE_OMIT_VIRTUALTABLE
   911    static struct {
   912       char *zName;
   913       Tcl_ObjCmdProc *xProc;
   914       void *clientData;
   915    } aObjCmd[] = {
   916       { "register_fs_module",   register_fs_module, 0 },
   917    };
   918    int i;
   919    for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
   920      Tcl_CreateObjCommand(interp, aObjCmd[i].zName, 
   921          aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
   922    }
   923  #endif
   924    return TCL_OK;
   925  }