123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 |
- /*
- ** 2014-07-28
- **
- ** The author disclaims copyright to this source code. In place of
- ** a legal notice, here is a blessing:
- **
- ** May you do good and not evil.
- ** May you find forgiveness for yourself and forgive others.
- ** May you share freely, never taking more than you give.
- **
- *************************************************************************
- **
- ** This file implements a utility program that will load many disk
- ** files (all files under a given directory) into a FTS table. This is
- ** used for performance testing of FTS3, FTS4, and FTS5.
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <ctype.h>
- #include <assert.h>
- #include <string.h>
- #include <errno.h>
- #include <dirent.h>
- #include "sqlite3.h"
- /*
- ** Implementation of the "readtext(X)" SQL function. The entire content
- ** of the file named X is read and returned as a TEXT value. It is assumed
- ** the file contains UTF-8 text. NULL is returned if the file does not
- ** exist or is unreadable.
- */
- 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);
- }
- /*
- ** Print usage text for this program and exit.
- */
- 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);
- }
- /*
- ** Exit with a message based on the argument and the current value of errno.
- */
- static void error_out(const char *zText){
- fprintf(stderr, "%s: %s\n", zText, strerror(errno));
- exit(-1);
- }
- /*
- ** Exit with a message based on the first argument and the error message
- ** currently stored in database handle db.
- */
- static void sqlite_error_out(const char *zText, sqlite3 *db){
- fprintf(stderr, "%s: %s\n", zText, sqlite3_errmsg(db));
- exit(-1);
- }
- /*
- ** Context object for visit_file().
- */
- typedef struct VisitContext VisitContext;
- struct VisitContext {
- int nRowPerTrans;
- sqlite3 *db; /* Database handle */
- sqlite3_stmt *pInsert; /* INSERT INTO fts VALUES(readtext(:1)) */
- };
- /*
- ** Callback used with traverse(). The first argument points to an object
- ** of type VisitContext. This function inserts the contents of the text
- ** file zPath into the FTS table.
- */
- void visit_file(void *pCtx, const char *zPath){
- int rc;
- VisitContext *p = (VisitContext*)pCtx;
- /* printf("%s\n", zPath); */
- 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);
- }
- }
- /*
- ** Recursively traverse directory zDir. For each file that is not a
- ** directory, invoke the supplied callback with its path.
- */
- static void traverse(
- const char *zDir, /* Directory to traverse */
- void *pCtx, /* First argument passed to callback */
- 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 (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; /* Value of -fts option */
- int bMap = 0; /* True to create mapping table */
- const char *zDir = "."; /* Directory to scan */
- 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]);
- }
- }
- /* Open the database file */
- 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);
- /* Create the FTS table */
- 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);
- }
- /* Compile the INSERT statement to write data to the FTS table. */
- 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);
- /* Load all files in the directory hierarchy into the FTS table. */
- 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);
- /* Clean up and exit. */
- sqlite3_finalize(sCtx.pInsert);
- sqlite3_close(db);
- sqlite3_free(aCmd);
- return 0;
- }
|