OLD | NEW |
(Empty) | |
| 1 /* |
| 2 ** 2014-07-28 |
| 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 ** |
| 13 ** This file implements a utility program that will load many disk |
| 14 ** files (all files under a given directory) into a FTS table. This is |
| 15 ** used for performance testing of FTS3, FTS4, and FTS5. |
| 16 */ |
| 17 |
| 18 #include <stdio.h> |
| 19 #include <stdlib.h> |
| 20 #include <ctype.h> |
| 21 #include <assert.h> |
| 22 #include <string.h> |
| 23 #include <errno.h> |
| 24 #include <dirent.h> |
| 25 #include "sqlite3.h" |
| 26 |
| 27 /* |
| 28 ** Implementation of the "readtext(X)" SQL function. The entire content |
| 29 ** of the file named X is read and returned as a TEXT value. It is assumed |
| 30 ** the file contains UTF-8 text. NULL is returned if the file does not |
| 31 ** exist or is unreadable. |
| 32 */ |
| 33 static void readfileFunc( |
| 34 sqlite3_context *context, |
| 35 int argc, |
| 36 sqlite3_value **argv |
| 37 ){ |
| 38 const char *zName; |
| 39 FILE *in; |
| 40 long nIn; |
| 41 void *pBuf; |
| 42 |
| 43 zName = (const char*)sqlite3_value_text(argv[0]); |
| 44 if( zName==0 ) return; |
| 45 in = fopen(zName, "rb"); |
| 46 if( in==0 ) return; |
| 47 fseek(in, 0, SEEK_END); |
| 48 nIn = ftell(in); |
| 49 rewind(in); |
| 50 pBuf = sqlite3_malloc( nIn ); |
| 51 if( pBuf && 1==fread(pBuf, nIn, 1, in) ){ |
| 52 sqlite3_result_text(context, pBuf, nIn, sqlite3_free); |
| 53 }else{ |
| 54 sqlite3_free(pBuf); |
| 55 } |
| 56 fclose(in); |
| 57 } |
| 58 |
| 59 /* |
| 60 ** Print usage text for this program and exit. |
| 61 */ |
| 62 static void showHelp(const char *zArgv0){ |
| 63 printf("\n" |
| 64 "Usage: %s SWITCHES... DB\n" |
| 65 "\n" |
| 66 " This program opens the database named on the command line and attempts to\n" |
| 67 " create an FTS table named \"fts\" with a single column. If successful, it\n" |
| 68 " recursively traverses the directory named by the -dir option and inserts\n" |
| 69 " the contents of each file into the fts table. All files are assumed to\n" |
| 70 " contain UTF-8 text.\n" |
| 71 "\n" |
| 72 "Switches are:\n" |
| 73 " -fts [345] FTS version to use (default=5)\n" |
| 74 " -idx [01] Create a mapping from filename to rowid (default=0)\n" |
| 75 " -dir <path> Root of directory tree to load data from (default=.)\n" |
| 76 " -trans <integer> Number of inserts per transaction (default=1)\n" |
| 77 , zArgv0 |
| 78 ); |
| 79 exit(1); |
| 80 } |
| 81 |
| 82 /* |
| 83 ** Exit with a message based on the argument and the current value of errno. |
| 84 */ |
| 85 static void error_out(const char *zText){ |
| 86 fprintf(stderr, "%s: %s\n", zText, strerror(errno)); |
| 87 exit(-1); |
| 88 } |
| 89 |
| 90 /* |
| 91 ** Exit with a message based on the first argument and the error message |
| 92 ** currently stored in database handle db. |
| 93 */ |
| 94 static void sqlite_error_out(const char *zText, sqlite3 *db){ |
| 95 fprintf(stderr, "%s: %s\n", zText, sqlite3_errmsg(db)); |
| 96 exit(-1); |
| 97 } |
| 98 |
| 99 /* |
| 100 ** Context object for visit_file(). |
| 101 */ |
| 102 typedef struct VisitContext VisitContext; |
| 103 struct VisitContext { |
| 104 int nRowPerTrans; |
| 105 sqlite3 *db; /* Database handle */ |
| 106 sqlite3_stmt *pInsert; /* INSERT INTO fts VALUES(readtext(:1)) */ |
| 107 }; |
| 108 |
| 109 /* |
| 110 ** Callback used with traverse(). The first argument points to an object |
| 111 ** of type VisitContext. This function inserts the contents of the text |
| 112 ** file zPath into the FTS table. |
| 113 */ |
| 114 void visit_file(void *pCtx, const char *zPath){ |
| 115 int rc; |
| 116 VisitContext *p = (VisitContext*)pCtx; |
| 117 /* printf("%s\n", zPath); */ |
| 118 sqlite3_bind_text(p->pInsert, 1, zPath, -1, SQLITE_STATIC); |
| 119 sqlite3_step(p->pInsert); |
| 120 rc = sqlite3_reset(p->pInsert); |
| 121 if( rc!=SQLITE_OK ){ |
| 122 sqlite_error_out("insert", p->db); |
| 123 }else if( p->nRowPerTrans>0 |
| 124 && (sqlite3_last_insert_rowid(p->db) % p->nRowPerTrans)==0 |
| 125 ){ |
| 126 sqlite3_exec(p->db, "COMMIT ; BEGIN", 0, 0, 0); |
| 127 } |
| 128 } |
| 129 |
| 130 /* |
| 131 ** Recursively traverse directory zDir. For each file that is not a |
| 132 ** directory, invoke the supplied callback with its path. |
| 133 */ |
| 134 static void traverse( |
| 135 const char *zDir, /* Directory to traverse */ |
| 136 void *pCtx, /* First argument passed to callback */ |
| 137 void (*xCallback)(void*, const char *zPath) |
| 138 ){ |
| 139 DIR *d; |
| 140 struct dirent *e; |
| 141 |
| 142 d = opendir(zDir); |
| 143 if( d==0 ) error_out("opendir()"); |
| 144 |
| 145 for(e=readdir(d); e; e=readdir(d)){ |
| 146 if( strcmp(e->d_name, ".")==0 || strcmp(e->d_name, "..")==0 ) continue; |
| 147 char *zPath = sqlite3_mprintf("%s/%s", zDir, e->d_name); |
| 148 if (e->d_type & DT_DIR) { |
| 149 traverse(zPath, pCtx, xCallback); |
| 150 }else{ |
| 151 xCallback(pCtx, zPath); |
| 152 } |
| 153 sqlite3_free(zPath); |
| 154 } |
| 155 |
| 156 closedir(d); |
| 157 } |
| 158 |
| 159 int main(int argc, char **argv){ |
| 160 int iFts = 5; /* Value of -fts option */ |
| 161 int bMap = 0; /* True to create mapping table */ |
| 162 const char *zDir = "."; /* Directory to scan */ |
| 163 int i; |
| 164 int rc; |
| 165 int nRowPerTrans = 0; |
| 166 sqlite3 *db; |
| 167 char *zSql; |
| 168 VisitContext sCtx; |
| 169 |
| 170 int nCmd = 0; |
| 171 char **aCmd = 0; |
| 172 |
| 173 if( argc % 2 ) showHelp(argv[0]); |
| 174 |
| 175 for(i=1; i<(argc-1); i+=2){ |
| 176 char *zOpt = argv[i]; |
| 177 char *zArg = argv[i+1]; |
| 178 if( strcmp(zOpt, "-fts")==0 ){ |
| 179 iFts = atoi(zArg); |
| 180 if( iFts!=3 && iFts!=4 && iFts!= 5) showHelp(argv[0]); |
| 181 } |
| 182 else if( strcmp(zOpt, "-trans")==0 ){ |
| 183 nRowPerTrans = atoi(zArg); |
| 184 } |
| 185 else if( strcmp(zOpt, "-idx")==0 ){ |
| 186 bMap = atoi(zArg); |
| 187 if( bMap!=0 && bMap!=1 ) showHelp(argv[0]); |
| 188 } |
| 189 else if( strcmp(zOpt, "-dir")==0 ){ |
| 190 zDir = zArg; |
| 191 } |
| 192 else if( strcmp(zOpt, "-special")==0 ){ |
| 193 nCmd++; |
| 194 aCmd = sqlite3_realloc(aCmd, sizeof(char*) * nCmd); |
| 195 aCmd[nCmd-1] = zArg; |
| 196 } |
| 197 else{ |
| 198 showHelp(argv[0]); |
| 199 } |
| 200 } |
| 201 |
| 202 /* Open the database file */ |
| 203 rc = sqlite3_open(argv[argc-1], &db); |
| 204 if( rc!=SQLITE_OK ) sqlite_error_out("sqlite3_open()", db); |
| 205 |
| 206 rc = sqlite3_create_function(db, "readtext", 1, SQLITE_UTF8, 0, |
| 207 readfileFunc, 0, 0); |
| 208 if( rc!=SQLITE_OK ) sqlite_error_out("sqlite3_create_function()", db); |
| 209 |
| 210 /* Create the FTS table */ |
| 211 zSql = sqlite3_mprintf("CREATE VIRTUAL TABLE fts USING fts%d(content)", iFts); |
| 212 rc = sqlite3_exec(db, zSql, 0, 0, 0); |
| 213 if( rc!=SQLITE_OK ) sqlite_error_out("sqlite3_exec(1)", db); |
| 214 sqlite3_free(zSql); |
| 215 |
| 216 for(i=0; i<nCmd; i++){ |
| 217 zSql = sqlite3_mprintf("INSERT INTO fts(fts) VALUES(%Q)", aCmd[i]); |
| 218 rc = sqlite3_exec(db, zSql, 0, 0, 0); |
| 219 if( rc!=SQLITE_OK ) sqlite_error_out("sqlite3_exec(1)", db); |
| 220 sqlite3_free(zSql); |
| 221 } |
| 222 |
| 223 /* Compile the INSERT statement to write data to the FTS table. */ |
| 224 memset(&sCtx, 0, sizeof(VisitContext)); |
| 225 sCtx.db = db; |
| 226 sCtx.nRowPerTrans = nRowPerTrans; |
| 227 rc = sqlite3_prepare_v2(db, |
| 228 "INSERT INTO fts VALUES(readtext(?))", -1, &sCtx.pInsert, 0 |
| 229 ); |
| 230 if( rc!=SQLITE_OK ) sqlite_error_out("sqlite3_prepare_v2(1)", db); |
| 231 |
| 232 /* Load all files in the directory hierarchy into the FTS table. */ |
| 233 if( sCtx.nRowPerTrans>0 ) sqlite3_exec(db, "BEGIN", 0, 0, 0); |
| 234 traverse(zDir, (void*)&sCtx, visit_file); |
| 235 if( sCtx.nRowPerTrans>0 ) sqlite3_exec(db, "COMMIT", 0, 0, 0); |
| 236 |
| 237 /* Clean up and exit. */ |
| 238 sqlite3_finalize(sCtx.pInsert); |
| 239 sqlite3_close(db); |
| 240 sqlite3_free(aCmd); |
| 241 return 0; |
| 242 } |
OLD | NEW |