aboutsummaryrefslogtreecommitdiff
path: root/ext/misc/fileio.c
diff options
context:
space:
mode:
authordan <dan@noemail.net>2017-12-07 15:44:29 +0000
committerdan <dan@noemail.net>2017-12-07 15:44:29 +0000
commitfd0245d771542a60f17fb7aa5c1ab13990d0a92f (patch)
tree699efa89dce45727b8d71dc0585ad358b0cde69b /ext/misc/fileio.c
parenta0fcafe7623efb2bbef52e15b8dbc6ae82e71c0c (diff)
downloadsqlite-fd0245d771542a60f17fb7aa5c1ab13990d0a92f.tar.gz
sqlite-fd0245d771542a60f17fb7aa5c1ab13990d0a92f.zip
Begin adding support for the sqlar archive format to shell.c. There is no
"extract" command so far, only "create". FossilOrigin-Name: c9827a01a6e107f38f85c2b2c1c7a599e443067b106217e965b6936441ca619d
Diffstat (limited to 'ext/misc/fileio.c')
-rw-r--r--ext/misc/fileio.c401
1 files changed, 386 insertions, 15 deletions
diff --git a/ext/misc/fileio.c b/ext/misc/fileio.c
index 2c00ad971..475f3713c 100644
--- a/ext/misc/fileio.c
+++ b/ext/misc/fileio.c
@@ -12,29 +12,32 @@
**
** This SQLite extension implements SQL functions readfile() and
** writefile().
+**
+** Also, an eponymous virtual table type "fsdir". Used as follows:
+**
+** SELECT * FROM fsdir($dirname);
+**
+** Returns one row for each entry in the directory $dirname. No row is
+** returned for "." or "..". Row columns are as follows:
+**
+** name: Name of directory entry.
+** mode: Value of stat.st_mode for directory entry.
+** mtime: Value of stat.st_mtime for directory entry.
+** data: For a regular file, a blob containing the file data. For a
+** symlink, a text value containing the text of the link. For a
+** directory, NULL.
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
#include <stdio.h>
-/*
-** Implementation of the "readfile(X)" SQL function. The entire content
-** of the file named X is read and returned as a BLOB. 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;
+#define FSDIR_SCHEMA "CREATE TABLE x(name,mode,mtime,data,dir HIDDEN)"
+
+static void readFileContents(sqlite3_context *ctx, const char *zName){
FILE *in;
long nIn;
void *pBuf;
- (void)(argc); /* Unused parameter */
- 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);
@@ -42,7 +45,7 @@ static void readfileFunc(
rewind(in);
pBuf = sqlite3_malloc( nIn );
if( pBuf && 1==fread(pBuf, nIn, 1, in) ){
- sqlite3_result_blob(context, pBuf, nIn, sqlite3_free);
+ sqlite3_result_blob(ctx, pBuf, nIn, sqlite3_free);
}else{
sqlite3_free(pBuf);
}
@@ -50,6 +53,23 @@ static void readfileFunc(
}
/*
+** Implementation of the "readfile(X)" SQL function. The entire content
+** of the file named X is read and returned as a BLOB. 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;
+ (void)(argc); /* Unused parameter */
+ zName = (const char*)sqlite3_value_text(argv[0]);
+ if( zName==0 ) return;
+ readFileContents(context, zName);
+}
+
+/*
** Implementation of the "writefile(X,Y)" SQL function. The argument Y
** is written into file X. The number of bytes written is returned. Or
** NULL is returned if something goes wrong, such as being unable to open
@@ -80,6 +100,354 @@ static void writefileFunc(
sqlite3_result_int64(context, rc);
}
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+
+/*
+*/
+typedef struct fsdir_cursor fsdir_cursor;
+struct fsdir_cursor {
+ sqlite3_vtab_cursor base; /* Base class - must be first */
+ int eType; /* One of FSDIR_DIR or FSDIR_ENTRY */
+ DIR *pDir; /* From opendir() */
+ struct stat sStat; /* Current lstat() results */
+ char *zDir; /* Directory to read */
+ int nDir; /* Value of strlen(zDir) */
+ char *zPath; /* Path to current entry */
+ int bEof;
+ sqlite3_int64 iRowid; /* Current rowid */
+};
+
+typedef struct fsdir_tab fsdir_tab;
+struct fsdir_tab {
+ sqlite3_vtab base; /* Base class - must be first */
+ int eType; /* One of FSDIR_DIR or FSDIR_ENTRY */
+};
+
+#define FSDIR_DIR 0
+#define FSDIR_ENTRY 1
+
+/*
+** Construct a new fsdir virtual table object.
+*/
+static int fsdirConnect(
+ sqlite3 *db,
+ void *pAux,
+ int argc, const char *const*argv,
+ sqlite3_vtab **ppVtab,
+ char **pzErr
+){
+ fsdir_tab *pNew = 0;
+ int rc;
+
+ rc = sqlite3_declare_vtab(db, FSDIR_SCHEMA);
+ if( rc==SQLITE_OK ){
+ pNew = (fsdir_tab*)sqlite3_malloc( sizeof(*pNew) );
+ if( pNew==0 ) return SQLITE_NOMEM;
+ memset(pNew, 0, sizeof(*pNew));
+ pNew->eType = (pAux==0 ? FSDIR_DIR : FSDIR_ENTRY);
+ }
+ *ppVtab = (sqlite3_vtab*)pNew;
+ return rc;
+}
+
+/*
+** This method is the destructor for fsdir vtab objects.
+*/
+static int fsdirDisconnect(sqlite3_vtab *pVtab){
+ sqlite3_free(pVtab);
+ return SQLITE_OK;
+}
+
+/*
+** Constructor for a new fsdir_cursor object.
+*/
+static int fsdirOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
+ fsdir_cursor *pCur;
+ pCur = sqlite3_malloc( sizeof(*pCur) );
+ if( pCur==0 ) return SQLITE_NOMEM;
+ memset(pCur, 0, sizeof(*pCur));
+ pCur->eType = ((fsdir_tab*)p)->eType;
+ *ppCursor = &pCur->base;
+ return SQLITE_OK;
+}
+
+/*
+** Destructor for an fsdir_cursor.
+*/
+static int fsdirClose(sqlite3_vtab_cursor *cur){
+ fsdir_cursor *pCur = (fsdir_cursor*)cur;
+ if( pCur->pDir ) closedir(pCur->pDir);
+ sqlite3_free(pCur->zDir);
+ sqlite3_free(pCur->zPath);
+ sqlite3_free(pCur);
+ return SQLITE_OK;
+}
+
+/*
+** Advance an fsdir_cursor to its next row of output.
+*/
+static int fsdirNext(sqlite3_vtab_cursor *cur){
+ fsdir_cursor *pCur = (fsdir_cursor*)cur;
+ struct dirent *pEntry;
+
+ if( pCur->eType==FSDIR_ENTRY ){
+ pCur->bEof = 1;
+ return SQLITE_OK;
+ }
+
+ sqlite3_free(pCur->zPath);
+ pCur->zPath = 0;
+
+ while( 1 ){
+ pEntry = readdir(pCur->pDir);
+ if( pEntry ){
+ if( strcmp(pEntry->d_name, ".")
+ && strcmp(pEntry->d_name, "..")
+ ){
+ pCur->zPath = sqlite3_mprintf("%s/%s", pCur->zDir, pEntry->d_name);
+ if( pCur->zPath==0 ) return SQLITE_NOMEM;
+ lstat(pCur->zPath, &pCur->sStat);
+ break;
+ }
+ }else{
+ pCur->bEof = 1;
+ break;
+ }
+ }
+
+ pCur->iRowid++;
+ return SQLITE_OK;
+}
+
+/*
+** Return values of columns for the row at which the series_cursor
+** is currently pointing.
+*/
+static int fsdirColumn(
+ sqlite3_vtab_cursor *cur, /* The cursor */
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
+ int i /* Which column to return */
+){
+ fsdir_cursor *pCur = (fsdir_cursor*)cur;
+ switch( i ){
+ case 0: { /* name */
+ const char *zName;
+ if( pCur->eType==FSDIR_DIR ){
+ zName = &pCur->zPath[pCur->nDir+1];
+ }else{
+ zName = pCur->zPath;
+ }
+ sqlite3_result_text(ctx, zName, -1, SQLITE_TRANSIENT);
+ break;
+ }
+
+ case 1: /* mode */
+ sqlite3_result_int64(ctx, pCur->sStat.st_mode);
+ break;
+
+ case 2: /* mode */
+ sqlite3_result_int64(ctx, pCur->sStat.st_mtime);
+ break;
+
+ case 3: {
+ mode_t m = pCur->sStat.st_mode;
+ if( S_ISDIR(m) ){
+ sqlite3_result_null(ctx);
+ }else if( S_ISLNK(m) ){
+ char aStatic[64];
+ char *aBuf = aStatic;
+ int nBuf = 64;
+ int n;
+
+ while( 1 ){
+ n = readlink(pCur->zPath, aBuf, nBuf);
+ if( n<nBuf ) break;
+ if( aBuf!=aStatic ) sqlite3_free(aBuf);
+ nBuf = nBuf*2;
+ aBuf = sqlite3_malloc(nBuf);
+ if( aBuf==0 ){
+ sqlite3_result_error_nomem(ctx);
+ return SQLITE_NOMEM;
+ }
+ }
+
+ sqlite3_result_text(ctx, aBuf, n, SQLITE_TRANSIENT);
+ if( aBuf!=aStatic ) sqlite3_free(aBuf);
+ }else{
+ readFileContents(ctx, pCur->zPath);
+ }
+ }
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Return the rowid for the current row. In this implementation, the
+** first row returned is assigned rowid value 1, and each subsequent
+** row a value 1 more than that of the previous.
+*/
+static int fsdirRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
+ fsdir_cursor *pCur = (fsdir_cursor*)cur;
+ *pRowid = pCur->iRowid;
+ return SQLITE_OK;
+}
+
+/*
+** Return TRUE if the cursor has been moved off of the last
+** row of output.
+*/
+static int fsdirEof(sqlite3_vtab_cursor *cur){
+ fsdir_cursor *pCur = (fsdir_cursor*)cur;
+ return pCur->bEof;
+}
+
+static void fsdirSetErrmsg(fsdir_cursor *pCur, const char *zFmt, ...){
+ va_list ap;
+ va_start(ap, zFmt);
+ pCur->base.pVtab->zErrMsg = sqlite3_vmprintf(zFmt, ap);
+ va_end(ap);
+}
+
+/*
+** xFilter callback.
+*/
+static int fsdirFilter(
+ sqlite3_vtab_cursor *cur,
+ int idxNum, const char *idxStr,
+ int argc, sqlite3_value **argv
+){
+ const char *zDir = 0;
+ fsdir_cursor *pCur = (fsdir_cursor*)cur;
+
+ sqlite3_free(pCur->zDir);
+ pCur->iRowid = 0;
+ pCur->zDir = 0;
+ pCur->bEof = 0;
+ if( pCur->pDir ){
+ closedir(pCur->pDir);
+ pCur->pDir = 0;
+ }
+
+ if( idxNum==0 ){
+ fsdirSetErrmsg(pCur, "table function fsdir requires an argument");
+ return SQLITE_ERROR;
+ }
+
+ assert( argc==1 );
+ zDir = (const char*)sqlite3_value_text(argv[0]);
+ if( zDir==0 ){
+ fsdirSetErrmsg(pCur, "table function fsdir requires a non-NULL argument");
+ return SQLITE_ERROR;
+ }
+
+ pCur->zDir = sqlite3_mprintf("%s", zDir);
+ if( pCur->zDir==0 ){
+ return SQLITE_NOMEM;
+ }
+
+ if( pCur->eType==FSDIR_ENTRY ){
+ int rc = lstat(pCur->zDir, &pCur->sStat);
+ if( rc ){
+ fsdirSetErrmsg(pCur, "cannot stat file: %s", pCur->zDir);
+ }else{
+ pCur->zPath = sqlite3_mprintf("%s", pCur->zDir);
+ if( pCur->zPath==0 ) return SQLITE_NOMEM;
+ }
+ return SQLITE_OK;
+ }else{
+ pCur->nDir = strlen(pCur->zDir);
+ pCur->pDir = opendir(zDir);
+ if( pCur->pDir==0 ){
+ fsdirSetErrmsg(pCur, "error in opendir(\"%s\")", zDir);
+ return SQLITE_ERROR;
+ }
+
+ return fsdirNext(cur);
+ }
+}
+
+/*
+** SQLite will invoke this method one or more times while planning a query
+** that uses the generate_series virtual table. This routine needs to create
+** a query plan for each invocation and compute an estimated cost for that
+** plan.
+**
+** In this implementation idxNum is used to represent the
+** query plan. idxStr is unused.
+**
+** The query plan is represented by bits in idxNum:
+**
+** (1) start = $value -- constraint exists
+** (2) stop = $value -- constraint exists
+** (4) step = $value -- constraint exists
+** (8) output in descending order
+*/
+static int fsdirBestIndex(
+ sqlite3_vtab *tab,
+ sqlite3_index_info *pIdxInfo
+){
+ int i; /* Loop over constraints */
+
+ const struct sqlite3_index_constraint *pConstraint;
+ pConstraint = pIdxInfo->aConstraint;
+ for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
+ if( pConstraint->usable==0 ) continue;
+ if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
+ if( pConstraint->iColumn!=4 ) continue;
+ break;
+ }
+
+ if( i<pIdxInfo->nConstraint ){
+ pIdxInfo->aConstraintUsage[i].omit = 1;
+ pIdxInfo->aConstraintUsage[i].argvIndex = 1;
+ pIdxInfo->idxNum = 1;
+ pIdxInfo->estimatedCost = 10.0;
+ }else{
+ pIdxInfo->idxNum = 0;
+ pIdxInfo->estimatedCost = (double)(((sqlite3_int64)1) << 50);
+ }
+
+ return SQLITE_OK;
+}
+
+static int fsdirRegister(sqlite3 *db){
+ static sqlite3_module fsdirModule = {
+ 0, /* iVersion */
+ 0, /* xCreate */
+ fsdirConnect, /* xConnect */
+ fsdirBestIndex, /* xBestIndex */
+ fsdirDisconnect, /* xDisconnect */
+ 0, /* xDestroy */
+ fsdirOpen, /* xOpen - open a cursor */
+ fsdirClose, /* xClose - close a cursor */
+ fsdirFilter, /* xFilter - configure scan constraints */
+ fsdirNext, /* xNext - advance a cursor */
+ fsdirEof, /* xEof - check for end of scan */
+ fsdirColumn, /* xColumn - read data */
+ fsdirRowid, /* xRowid - read data */
+ 0, /* xUpdate */
+ 0, /* xBegin */
+ 0, /* xSync */
+ 0, /* xCommit */
+ 0, /* xRollback */
+ 0, /* xFindMethod */
+ 0, /* xRename */
+ };
+
+ int rc = sqlite3_create_module(db, "fsdir", &fsdirModule, 0);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_module(db, "fsentry", &fsdirModule, (void*)1);
+ }
+ return rc;
+}
+#else /* SQLITE_OMIT_VIRTUALTABLE */
+# define fsdirRegister(x) SQLITE_OK
+#endif
#ifdef _WIN32
__declspec(dllexport)
@@ -98,5 +466,8 @@ int sqlite3_fileio_init(
rc = sqlite3_create_function(db, "writefile", 2, SQLITE_UTF8, 0,
writefileFunc, 0, 0);
}
+ if( rc==SQLITE_OK ){
+ rc = fsdirRegister(db);
+ }
return rc;
}