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