#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sqlite3ext.h" SQLITE_EXTENSION_INIT1 struct vtab { sqlite3_vtab base; /* Base class - must be first */ sqlite3 *db; /* Database connection */ char *dbname; /* Name of database holding this table */ char *tablename; /* Name of the virtual table */ char *pattern; /* input to glob(3) */ char dbname_data[1]; /* storage for dbname */ }; struct vtab_cursor { sqlite3_vtab_cursor base; /* Base class - must be first */ struct vtab *pvtab; /* Virtual table of this cursor */ glob_t glob; size_t iname; }; static const char create_table[] = "CREATE TABLE tablename" "( name VARCHAR(512)" ")"; /* * argv[0] -> module name ("glob") * argv[1] -> database name * argv[2] -> table name * argv[3] -> glob pattern */ static int xCreate(sqlite3 *db, void *pAux, int argc, const char * const *argv, sqlite3_vtab **ppVTab, char **pzErr) { const char *dbname = argv[1]; const char *tablename = argv[2]; const char *pattern = argv[3]; struct vtab *pvtab; if( argc < 4 ) return SQLITE_ERROR; /* * Allocate a vtable with room at the end for the dbname. * Initialize dbname pointer to the address of the additional storage. */ if( (pvtab = sqlite3_malloc(1 + sizeof(*pvtab) + strlen(dbname))) == NULL ) { return SQLITE_NOMEM; } memset(pvtab, '\0', sizeof(*pvtab)); pvtab->db = db; strcpy( pvtab->dbname_data, dbname ); pvtab->dbname = pvtab->dbname_data; if( (pvtab->tablename = sqlite3_mprintf("%s", tablename)) == NULL ) { sqlite3_free(pvtab); return SQLITE_NOMEM; } if( (pvtab->pattern = sqlite3_mprintf("%s", pattern)) == NULL ) { sqlite3_free(pvtab->tablename); sqlite3_free(pvtab); return SQLITE_NOMEM; } if( sqlite3_declare_vtab(db, create_table) != SQLITE_OK ) { sqlite3_free(pvtab->tablename); sqlite3_free(pvtab->pattern); sqlite3_free(pvtab); return SQLITE_ERROR; } *ppVTab = (sqlite3_vtab *)pvtab; return SQLITE_OK; } static int xConnect( sqlite3 *db, void *pAux, int argc, const char* const *argv, sqlite3_vtab **ppVTab, char **pzErr ) { return xCreate(db, pAux, argc, argv, ppVTab, pzErr); } static int xBestIndex(sqlite3_vtab *pvtab, sqlite3_index_info* pi) { #if 0 /* Inputs */ pi->nConstraint; *pi->aConstraint; /* Table of WHERE clause constraints */ pi->nOrderBy; *pi->aOrderBy; /* The ORDER BY clause */ /* Outputs */ *pi->aConstraintUsage; pi->idxNum; /* Number used to identify the index */ pi->idxStr; /* String, possibly obtained from sqlite3_malloc */ pi->needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */ pi->orderByConsumed; /* True if output is already ordered */ pi->estimatedCost; /* Estimated cost of using this index */ #endif /* we're only going to iterate over the directory entries each time */ pi->idxNum = 0; pi->idxStr = "any"; pi->needToFreeIdxStr = 0; pi->orderByConsumed = 0; pi->estimatedCost = 1; return SQLITE_OK; } static int xDisconnect(sqlite3_vtab *pVTab) { return SQLITE_OK; } static int xDestroy(sqlite3_vtab *pVTab) { struct vtab *pvtab = (struct vtab *)pVTab; sqlite3_free(pvtab->tablename); sqlite3_free(pvtab->pattern); sqlite3_free(pvtab); return SQLITE_OK; } static int xOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) { struct vtab *pvtab = (struct vtab *)pVTab; struct vtab_cursor *pcursor = sqlite3_malloc( sizeof(struct vtab_cursor) ); if( pcursor == NULL ) return SQLITE_NOMEM; pcursor->pvtab = pvtab; if( glob(pcursor->pvtab->pattern, GLOB_NOSORT, NULL, &pcursor->glob) != 0 ) { sqlite3_free(pcursor); return SQLITE_ERROR; } if( (pcursor->glob.gl_pathv) == NULL ) { sqlite3_free(pcursor); return SQLITE_ERROR; } pcursor->iname = 0; *ppCursor = &pcursor->base; return SQLITE_OK; } static int xClose(sqlite3_vtab_cursor *pCursor) { struct vtab_cursor *pcur = (struct vtab_cursor *)pCursor; globfree(&pcur->glob); sqlite3_free(pcur); return SQLITE_OK; } static int xEof(sqlite3_vtab_cursor *pCursor) { struct vtab_cursor *pcur = (struct vtab_cursor *)pCursor; return pcur->iname >= pcur->glob.gl_matchc; } static int xFilter(sqlite3_vtab_cursor *pCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv) { return SQLITE_OK; } static int xNext(sqlite3_vtab_cursor* pCursor) { struct vtab_cursor *pcur = (struct vtab_cursor *)pCursor; pcur->iname++; return SQLITE_OK; } static int xColumn(sqlite3_vtab_cursor *pCursor, sqlite3_context *pcontext, int N) { struct vtab_cursor *pcur = (struct vtab_cursor *)pCursor; const char *name = pcur->glob.gl_pathv[pcur->iname]; switch(N) { case 0: sqlite3_result_text(pcontext, name, -1, SQLITE_TRANSIENT); break; default: sqlite3_result_error_code(pcontext, SQLITE_ERROR); return SQLITE_ERROR; break; } return SQLITE_OK; } static int xRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid) { struct vtab_cursor *pcur = (struct vtab_cursor *)pCursor; *pRowid = pcur->iname; return SQLITE_OK; } /* * Rename the table. */ static int xRename(sqlite3_vtab *pVTab, const char *zNew) { struct vtab *pvtab = (struct vtab *)pVTab; char *newname = sqlite3_mprintf("%s", zNew); int erc; if( newname == NULL ){ return SQLITE_NOMEM; } char *sql = sqlite3_mprintf("ALTER TABLE \"%w\".\"%w\" RENAME TO \"%w\"", pvtab->dbname, pvtab->tablename, newname); if( sql == NULL ) { return SQLITE_NOMEM; } if( (erc = sqlite3_exec(pvtab->db, sql, 0, 0, 0)) != SQLITE_OK ) { sqlite3_free(sql); return erc; } sqlite3_free(pvtab->tablename); pvtab->tablename = newname; return SQLITE_OK; } /* * A virtual table module that renders a file list from glob(3). */ static sqlite3_module globModule = { 0, /* iVersion */ xCreate, /* xCreate - handle CREATE VIRTUAL TABLE */ xConnect, /* xConnect - reconnected to an existing table */ xBestIndex, /* xBestIndex - figure out how to do a query */ xDisconnect, /* xDisconnect - close a connection */ xDestroy, /* xDestroy - handle DROP TABLE */ xOpen, /* xOpen - open a cursor */ xClose, /* xClose - close a cursor */ xFilter, /* xFilter - configure scan constraints */ xNext, /* xNext - advance a cursor */ xEof, /* xEof - check for end of scan */ xColumn, /* xColumn - read data */ xRowid, /* xRowid - read data */ 0, /* xUpdate */ 0, /* xBegin */ 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ xRename, /* xRename */ }; #if 0 /* * No functions implemented. */ struct func_desc_t { const char *name; int narg; void *papp; void (*xfunc)(sqlite3_context*,int,sqlite3_value**); void (*xstep)(sqlite3_context*,int,sqlite3_value**); void (*xfinal)(sqlite3_context*); }; static struct func_desc_t functions[]; static size_t nfunctions = 0; // sizeof(functions)/sizeof(functions[0]); enum { eTextRep=SQLITE_UTF8 }; #endif /* * Register functions and the virtual table. */ static int register_module(sqlite3 *db, char **pzErrMsg) { int erc; #if 0 struct func_desc_t *pf; for( pf=functions; pf < functions + nfunctions; pf++ ) { erc = sqlite3_create_function( db, pf->name, pf->narg, eTextRep, pf->papp, pf->xfunc, pf->xstep, pf->xfinal ); if( erc != SQLITE_OK ) { sqlite3_log(erc, "error loading UDF '%s'", pf->name); return erc; } } #endif erc = sqlite3_create_module(db, "glob", &globModule, 0); if( erc != SQLITE_OK ) { *pzErrMsg = sqlite3_mprintf("register_module: %d", erc); return erc; } return SQLITE_OK; } /* * Extension load function called by shell.c. */ int sqlite3_extension_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi ) { SQLITE_EXTENSION_INIT2(pApi); return register_module(db, pzErrMsg); }