xref: /sqlite-3.40.0/src/test_fs.c (revision 9f5ff371)
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 #include "sqliteInt.h"
34 #include "tcl.h"
35 
36 #include <stdlib.h>
37 #include <string.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <unistd.h>
41 #include <fcntl.h>
42 
43 #ifndef SQLITE_OMIT_VIRTUALTABLE
44 
45 typedef struct fs_vtab fs_vtab;
46 typedef struct fs_cursor fs_cursor;
47 
48 /*
49 ** A fs virtual-table object
50 */
51 struct fs_vtab {
52   sqlite3_vtab base;
53   sqlite3 *db;
54   char *zDb;                      /* Name of db containing zTbl */
55   char *zTbl;                     /* Name of docid->file map table */
56 };
57 
58 /* A fs cursor object */
59 struct fs_cursor {
60   sqlite3_vtab_cursor base;
61   sqlite3_stmt *pStmt;
62   char *zBuf;
63   int nBuf;
64   int nAlloc;
65 };
66 
67 /*
68 ** This function is the implementation of both the xConnect and xCreate
69 ** methods of the fs virtual table.
70 **
71 ** The argv[] array contains the following:
72 **
73 **   argv[0]   -> module name  ("fs")
74 **   argv[1]   -> database name
75 **   argv[2]   -> table name
76 **   argv[...] -> other module argument fields.
77 */
78 static int fsConnect(
79   sqlite3 *db,
80   void *pAux,
81   int argc, const char *const*argv,
82   sqlite3_vtab **ppVtab,
83   char **pzErr
84 ){
85   fs_vtab *pVtab;
86   int nByte;
87   const char *zTbl;
88   const char *zDb = argv[1];
89 
90   if( argc!=4 ){
91     *pzErr = sqlite3_mprintf("wrong number of arguments");
92     return SQLITE_ERROR;
93   }
94   zTbl = argv[3];
95 
96   nByte = sizeof(fs_vtab) + strlen(zTbl) + 1 + strlen(zDb) + 1;
97   pVtab = (fs_vtab *)sqlite3MallocZero( nByte );
98   if( !pVtab ) return SQLITE_NOMEM;
99 
100   pVtab->zTbl = (char *)&pVtab[1];
101   pVtab->zDb = &pVtab->zTbl[strlen(zTbl)+1];
102   pVtab->db = db;
103   memcpy(pVtab->zTbl, zTbl, strlen(zTbl));
104   memcpy(pVtab->zDb, zDb, strlen(zDb));
105   *ppVtab = &pVtab->base;
106   sqlite3_declare_vtab(db, "CREATE TABLE xyz(path TEXT, data TEXT)");
107 
108   return SQLITE_OK;
109 }
110 /* Note that for this virtual table, the xCreate and xConnect
111 ** methods are identical. */
112 
113 static int fsDisconnect(sqlite3_vtab *pVtab){
114   sqlite3_free(pVtab);
115   return SQLITE_OK;
116 }
117 /* The xDisconnect and xDestroy methods are also the same */
118 
119 /*
120 ** Open a new fs cursor.
121 */
122 static int fsOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
123   fs_cursor *pCur;
124   pCur = sqlite3MallocZero(sizeof(fs_cursor));
125   *ppCursor = &pCur->base;
126   return SQLITE_OK;
127 }
128 
129 /*
130 ** Close a fs cursor.
131 */
132 static int fsClose(sqlite3_vtab_cursor *cur){
133   fs_cursor *pCur = (fs_cursor *)cur;
134   sqlite3_finalize(pCur->pStmt);
135   sqlite3_free(pCur->zBuf);
136   sqlite3_free(pCur);
137   return SQLITE_OK;
138 }
139 
140 static int fsNext(sqlite3_vtab_cursor *cur){
141   fs_cursor *pCur = (fs_cursor *)cur;
142   int rc;
143 
144   rc = sqlite3_step(pCur->pStmt);
145   if( rc==SQLITE_ROW || rc==SQLITE_DONE ) rc = SQLITE_OK;
146 
147   return rc;
148 }
149 
150 static int fsFilter(
151   sqlite3_vtab_cursor *pVtabCursor,
152   int idxNum, const char *idxStr,
153   int argc, sqlite3_value **argv
154 ){
155   int rc;
156   fs_cursor *pCur = (fs_cursor *)pVtabCursor;
157   fs_vtab *p = (fs_vtab *)(pVtabCursor->pVtab);
158 
159   assert( (idxNum==0 && argc==0) || (idxNum==1 && argc==1) );
160   if( idxNum==1 ){
161     char *zStmt = sqlite3_mprintf(
162         "SELECT * FROM %Q.%Q WHERE rowid=?", p->zDb, p->zTbl);
163     if( !zStmt ) return SQLITE_NOMEM;
164     rc = sqlite3_prepare_v2(p->db, zStmt, -1, &pCur->pStmt, 0);
165     sqlite3_free(zStmt);
166     if( rc==SQLITE_OK ){
167       sqlite3_bind_value(pCur->pStmt, 1, argv[0]);
168     }
169   }else{
170     char *zStmt = sqlite3_mprintf("SELECT * FROM %Q.%Q", p->zDb, p->zTbl);
171     if( !zStmt ) return SQLITE_NOMEM;
172     rc = sqlite3_prepare_v2(p->db, zStmt, -1, &pCur->pStmt, 0);
173     sqlite3_free(zStmt);
174   }
175 
176   if( rc==SQLITE_OK ){
177     rc = fsNext(pVtabCursor);
178   }
179   return rc;
180 }
181 
182 static int fsColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
183   fs_cursor *pCur = (fs_cursor*)cur;
184 
185   assert( i==0 || i==1 );
186   if( i==0 ){
187     sqlite3_result_value(ctx, sqlite3_column_value(pCur->pStmt, 0));
188   }else{
189     const char *zFile = (const char *)sqlite3_column_text(pCur->pStmt, 1);
190     struct stat sbuf;
191     int fd;
192 
193     fd = open(zFile, O_RDONLY);
194     if( fd<0 ) return SQLITE_IOERR;
195     fstat(fd, &sbuf);
196 
197     if( sbuf.st_size>=pCur->nAlloc ){
198       int nNew = sbuf.st_size*2;
199       char *zNew;
200       if( nNew<1024 ) nNew = 1024;
201 
202       zNew = sqlite3Realloc(pCur->zBuf, nNew);
203       if( zNew==0 ){
204         close(fd);
205         return SQLITE_NOMEM;
206       }
207       pCur->zBuf = zNew;
208       pCur->nAlloc = nNew;
209     }
210 
211     read(fd, pCur->zBuf, sbuf.st_size);
212     close(fd);
213     pCur->nBuf = sbuf.st_size;
214     pCur->zBuf[pCur->nBuf] = '\0';
215 
216     sqlite3_result_text(ctx, pCur->zBuf, -1, SQLITE_TRANSIENT);
217   }
218   return SQLITE_OK;
219 }
220 
221 static int fsRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
222   fs_cursor *pCur = (fs_cursor*)cur;
223   *pRowid = sqlite3_column_int64(pCur->pStmt, 0);
224   return SQLITE_OK;
225 }
226 
227 static int fsEof(sqlite3_vtab_cursor *cur){
228   fs_cursor *pCur = (fs_cursor*)cur;
229   return (sqlite3_data_count(pCur->pStmt)==0);
230 }
231 
232 static int fsBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
233   int ii;
234 
235   for(ii=0; ii<pIdxInfo->nConstraint; ii++){
236     struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii];
237     if( pCons->iColumn<0 && pCons->usable
238            && pCons->op==SQLITE_INDEX_CONSTRAINT_EQ ){
239       struct sqlite3_index_constraint_usage *pUsage;
240       pUsage = &pIdxInfo->aConstraintUsage[ii];
241       pUsage->omit = 0;
242       pUsage->argvIndex = 1;
243       pIdxInfo->idxNum = 1;
244       pIdxInfo->estimatedCost = 1.0;
245       break;
246     }
247   }
248 
249   return SQLITE_OK;
250 }
251 
252 /*
253 ** A virtual table module that provides read-only access to a
254 ** Tcl global variable namespace.
255 */
256 static sqlite3_module fsModule = {
257   0,                         /* iVersion */
258   fsConnect,
259   fsConnect,
260   fsBestIndex,
261   fsDisconnect,
262   fsDisconnect,
263   fsOpen,                      /* xOpen - open a cursor */
264   fsClose,                     /* xClose - close a cursor */
265   fsFilter,                    /* xFilter - configure scan constraints */
266   fsNext,                      /* xNext - advance a cursor */
267   fsEof,                       /* xEof - check for end of scan */
268   fsColumn,                    /* xColumn - read data */
269   fsRowid,                     /* xRowid - read data */
270   0,                           /* xUpdate */
271   0,                           /* xBegin */
272   0,                           /* xSync */
273   0,                           /* xCommit */
274   0,                           /* xRollback */
275   0,                           /* xFindMethod */
276   0,                           /* xRename */
277 };
278 
279 /*
280 ** Decode a pointer to an sqlite3 object.
281 */
282 extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
283 
284 /*
285 ** Register the echo virtual table module.
286 */
287 static int register_fs_module(
288   ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
289   Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
290   int objc,              /* Number of arguments */
291   Tcl_Obj *CONST objv[]  /* Command arguments */
292 ){
293   sqlite3 *db;
294   if( objc!=2 ){
295     Tcl_WrongNumArgs(interp, 1, objv, "DB");
296     return TCL_ERROR;
297   }
298   if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
299 #ifndef SQLITE_OMIT_VIRTUALTABLE
300   sqlite3_create_module(db, "fs", &fsModule, (void *)interp);
301 #endif
302   return TCL_OK;
303 }
304 
305 #endif
306 
307 
308 /*
309 ** Register commands with the TCL interpreter.
310 */
311 int Sqlitetestfs_Init(Tcl_Interp *interp){
312 #ifndef SQLITE_OMIT_VIRTUALTABLE
313   static struct {
314      char *zName;
315      Tcl_ObjCmdProc *xProc;
316      void *clientData;
317   } aObjCmd[] = {
318      { "register_fs_module",   register_fs_module, 0 },
319   };
320   int i;
321   for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
322     Tcl_CreateObjCommand(interp, aObjCmd[i].zName,
323         aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
324   }
325 #endif
326   return TCL_OK;
327 }
328