| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <ctype.h> |
| #include <assert.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <dirent.h> |
| #include "sqlite3.h" |
|
|
| |
| |
| |
| |
| |
| |
| static void readfileFunc( |
| sqlite3_context *context, |
| int argc, |
| sqlite3_value **argv |
| ){ |
| const char *zName; |
| FILE *in; |
| long nIn; |
| void *pBuf; |
|
|
| zName = (const char*)sqlite3_value_text(argv[0]); |
| if( zName==0 ) return; |
| in = fopen(zName, "rb"); |
| if( in==0 ) return; |
| fseek(in, 0, SEEK_END); |
| nIn = ftell(in); |
| rewind(in); |
| pBuf = sqlite3_malloc( nIn ); |
| if( pBuf && 1==fread(pBuf, nIn, 1, in) ){ |
| sqlite3_result_text(context, pBuf, nIn, sqlite3_free); |
| }else{ |
| sqlite3_free(pBuf); |
| } |
| fclose(in); |
| } |
|
|
| |
| |
| |
| static void showHelp(const char *zArgv0){ |
| printf("\n" |
| "Usage: %s SWITCHES... DB\n" |
| "\n" |
| " This program opens the database named on the command line and attempts to\n" |
| " create an FTS table named \"fts\" with a single column. If successful, it\n" |
| " recursively traverses the directory named by the -dir option and inserts\n" |
| " the contents of each file into the fts table. All files are assumed to\n" |
| " contain UTF-8 text.\n" |
| "\n" |
| "Switches are:\n" |
| " -fts [345] FTS version to use (default=5)\n" |
| " -idx [01] Create a mapping from filename to rowid (default=0)\n" |
| " -dir <path> Root of directory tree to load data from (default=.)\n" |
| " -trans <integer> Number of inserts per transaction (default=1)\n" |
| , zArgv0 |
| ); |
| exit(1); |
| } |
|
|
| |
| |
| |
| static void error_out(const char *zText){ |
| fprintf(stderr, "%s: %s\n", zText, strerror(errno)); |
| exit(-1); |
| } |
|
|
| |
| |
| |
| |
| static void sqlite_error_out(const char *zText, sqlite3 *db){ |
| fprintf(stderr, "%s: %s\n", zText, sqlite3_errmsg(db)); |
| exit(-1); |
| } |
|
|
| |
| |
| |
| typedef struct VisitContext VisitContext; |
| struct VisitContext { |
| int nRowPerTrans; |
| sqlite3 *db; |
| sqlite3_stmt *pInsert; |
| }; |
|
|
| |
| |
| |
| |
| |
| void visit_file(void *pCtx, const char *zPath){ |
| int rc; |
| VisitContext *p = (VisitContext*)pCtx; |
| |
| sqlite3_bind_text(p->pInsert, 1, zPath, -1, SQLITE_STATIC); |
| sqlite3_step(p->pInsert); |
| rc = sqlite3_reset(p->pInsert); |
| if( rc!=SQLITE_OK ){ |
| sqlite_error_out("insert", p->db); |
| }else if( p->nRowPerTrans>0 |
| && (sqlite3_last_insert_rowid(p->db) % p->nRowPerTrans)==0 |
| ){ |
| sqlite3_exec(p->db, "COMMIT ; BEGIN", 0, 0, 0); |
| } |
| } |
|
|
| |
| |
| |
| |
| static void traverse( |
| const char *zDir, |
| void *pCtx, |
| void (*xCallback)(void*, const char *zPath) |
| ){ |
| DIR *d; |
| struct dirent *e; |
|
|
| d = opendir(zDir); |
| if( d==0 ) error_out("opendir()"); |
|
|
| for(e=readdir(d); e; e=readdir(d)){ |
| if( strcmp(e->d_name, ".")==0 || strcmp(e->d_name, "..")==0 ) continue; |
| char *zPath = sqlite3_mprintf("%s/%s", zDir, e->d_name); |
| if( zPath==0 ){ fprintf(stderr, "out of memory\n"); abort(); } |
| if (e->d_type & DT_DIR) { |
| traverse(zPath, pCtx, xCallback); |
| }else{ |
| xCallback(pCtx, zPath); |
| } |
| sqlite3_free(zPath); |
| } |
|
|
| closedir(d); |
| } |
|
|
| int main(int argc, char **argv){ |
| int iFts = 5; |
| int bMap = 0; |
| const char *zDir = "."; |
| int i; |
| int rc; |
| int nRowPerTrans = 0; |
| sqlite3 *db; |
| char *zSql; |
| VisitContext sCtx; |
|
|
| int nCmd = 0; |
| char **aCmd = 0; |
|
|
| if( argc % 2 ) showHelp(argv[0]); |
|
|
| for(i=1; i<(argc-1); i+=2){ |
| char *zOpt = argv[i]; |
| char *zArg = argv[i+1]; |
| if( strcmp(zOpt, "-fts")==0 ){ |
| iFts = atoi(zArg); |
| if( iFts!=3 && iFts!=4 && iFts!= 5) showHelp(argv[0]); |
| } |
| else if( strcmp(zOpt, "-trans")==0 ){ |
| nRowPerTrans = atoi(zArg); |
| } |
| else if( strcmp(zOpt, "-idx")==0 ){ |
| bMap = atoi(zArg); |
| if( bMap!=0 && bMap!=1 ) showHelp(argv[0]); |
| } |
| else if( strcmp(zOpt, "-dir")==0 ){ |
| zDir = zArg; |
| } |
| else if( strcmp(zOpt, "-special")==0 ){ |
| nCmd++; |
| aCmd = sqlite3_realloc(aCmd, sizeof(char*) * nCmd); |
| aCmd[nCmd-1] = zArg; |
| } |
| else{ |
| showHelp(argv[0]); |
| } |
| } |
|
|
| |
| rc = sqlite3_open(argv[argc-1], &db); |
| if( rc!=SQLITE_OK ) sqlite_error_out("sqlite3_open()", db); |
|
|
| rc = sqlite3_create_function(db, "readtext", 1, SQLITE_UTF8, 0, |
| readfileFunc, 0, 0); |
| if( rc!=SQLITE_OK ) sqlite_error_out("sqlite3_create_function()", db); |
|
|
| |
| zSql = sqlite3_mprintf("CREATE VIRTUAL TABLE fts USING fts%d(content)", iFts); |
| rc = sqlite3_exec(db, zSql, 0, 0, 0); |
| if( rc!=SQLITE_OK ) sqlite_error_out("sqlite3_exec(1)", db); |
| sqlite3_free(zSql); |
|
|
| for(i=0; i<nCmd; i++){ |
| zSql = sqlite3_mprintf("INSERT INTO fts(fts) VALUES(%Q)", aCmd[i]); |
| rc = sqlite3_exec(db, zSql, 0, 0, 0); |
| if( rc!=SQLITE_OK ) sqlite_error_out("sqlite3_exec(1)", db); |
| sqlite3_free(zSql); |
| } |
|
|
| |
| memset(&sCtx, 0, sizeof(VisitContext)); |
| sCtx.db = db; |
| sCtx.nRowPerTrans = nRowPerTrans; |
| rc = sqlite3_prepare_v2(db, |
| "INSERT INTO fts VALUES(readtext(?))", -1, &sCtx.pInsert, 0 |
| ); |
| if( rc!=SQLITE_OK ) sqlite_error_out("sqlite3_prepare_v2(1)", db); |
|
|
| |
| if( sCtx.nRowPerTrans>0 ) sqlite3_exec(db, "BEGIN", 0, 0, 0); |
| traverse(zDir, (void*)&sCtx, visit_file); |
| if( sCtx.nRowPerTrans>0 ) sqlite3_exec(db, "COMMIT", 0, 0, 0); |
|
|
| |
| sqlite3_finalize(sCtx.pInsert); |
| sqlite3_close(db); |
| sqlite3_free(aCmd); |
| return 0; |
| } |
|
|