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