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 } 917