/ Check-in [c9827a01]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Begin adding support for the sqlar archive format to shell.c. There is no "extract" command so far, only "create".
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | sqlar-shell-support
Files: files | file ages | folders
SHA3-256:c9827a01a6e107f38f85c2b2c1c7a599e443067b106217e965b6936441ca619d
User & Date: dan 2017-12-07 15:44:29
Context
2017-12-07
21:03
Add the ".ar x" command to the shell. For extracting the contents of sqlar archives. check-in: 0cc699d1 user: dan tags: sqlar-shell-support
15:44
Begin adding support for the sqlar archive format to shell.c. There is no "extract" command so far, only "create". check-in: c9827a01 user: dan tags: sqlar-shell-support
2017-12-05
19:07
For MSVC, simplify default locations for Tcl and ICU by using directories inside 'compat'. check-in: 8155b5ac user: mistachkin tags: sqlar-shell-support
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/misc/fileio.c.

     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   ******************************************************************************
    12     12   **
    13     13   ** This SQLite extension implements SQL functions readfile() and
    14     14   ** writefile().
           15  +**
           16  +** Also, an eponymous virtual table type "fsdir". Used as follows:
           17  +**
           18  +**   SELECT * FROM fsdir($dirname);
           19  +**
           20  +** Returns one row for each entry in the directory $dirname. No row is
           21  +** returned for "." or "..". Row columns are as follows:
           22  +**
           23  +**   name:  Name of directory entry.
           24  +**   mode:  Value of stat.st_mode for directory entry.
           25  +**   mtime: Value of stat.st_mtime for directory entry.
           26  +**   data:  For a regular file, a blob containing the file data. For a
           27  +**          symlink, a text value containing the text of the link. For a
           28  +**          directory, NULL.
    15     29   */
    16     30   #include "sqlite3ext.h"
    17     31   SQLITE_EXTENSION_INIT1
    18     32   #include <stdio.h>
           33  +
           34  +#define FSDIR_SCHEMA "CREATE TABLE x(name,mode,mtime,data,dir HIDDEN)"
           35  +
           36  +static void readFileContents(sqlite3_context *ctx, const char *zName){
           37  +  FILE *in;
           38  +  long nIn;
           39  +  void *pBuf;
           40  +
           41  +  in = fopen(zName, "rb");
           42  +  if( in==0 ) return;
           43  +  fseek(in, 0, SEEK_END);
           44  +  nIn = ftell(in);
           45  +  rewind(in);
           46  +  pBuf = sqlite3_malloc( nIn );
           47  +  if( pBuf && 1==fread(pBuf, nIn, 1, in) ){
           48  +    sqlite3_result_blob(ctx, pBuf, nIn, sqlite3_free);
           49  +  }else{
           50  +    sqlite3_free(pBuf);
           51  +  }
           52  +  fclose(in);
           53  +}
    19     54   
    20     55   /*
    21     56   ** Implementation of the "readfile(X)" SQL function.  The entire content
    22     57   ** of the file named X is read and returned as a BLOB.  NULL is returned
    23     58   ** if the file does not exist or is unreadable.
    24     59   */
    25     60   static void readfileFunc(
    26     61     sqlite3_context *context,
    27     62     int argc,
    28     63     sqlite3_value **argv
    29     64   ){
    30     65     const char *zName;
    31         -  FILE *in;
    32         -  long nIn;
    33         -  void *pBuf;
    34         -
    35     66     (void)(argc);  /* Unused parameter */
    36     67     zName = (const char*)sqlite3_value_text(argv[0]);
    37     68     if( zName==0 ) return;
    38         -  in = fopen(zName, "rb");
    39         -  if( in==0 ) return;
    40         -  fseek(in, 0, SEEK_END);
    41         -  nIn = ftell(in);
    42         -  rewind(in);
    43         -  pBuf = sqlite3_malloc( nIn );
    44         -  if( pBuf && 1==fread(pBuf, nIn, 1, in) ){
    45         -    sqlite3_result_blob(context, pBuf, nIn, sqlite3_free);
    46         -  }else{
    47         -    sqlite3_free(pBuf);
    48         -  }
    49         -  fclose(in);
           69  +  readFileContents(context, zName);
    50     70   }
    51     71   
    52     72   /*
    53     73   ** Implementation of the "writefile(X,Y)" SQL function.  The argument Y
    54     74   ** is written into file X.  The number of bytes written is returned.  Or
    55     75   ** NULL is returned if something goes wrong, such as being unable to open
    56     76   ** file X for writing.
................................................................................
    76     96     }else{
    77     97       rc = fwrite(z, 1, sqlite3_value_bytes(argv[1]), out);
    78     98     }
    79     99     fclose(out);
    80    100     sqlite3_result_int64(context, rc);
    81    101   }
    82    102   
          103  +#ifndef SQLITE_OMIT_VIRTUALTABLE
          104  +
          105  +#include <sys/types.h>
          106  +#include <sys/stat.h>
          107  +#include <unistd.h>
          108  +#include <dirent.h>
          109  +
          110  +/* 
          111  +*/
          112  +typedef struct fsdir_cursor fsdir_cursor;
          113  +struct fsdir_cursor {
          114  +  sqlite3_vtab_cursor base;  /* Base class - must be first */
          115  +  int eType;                 /* One of FSDIR_DIR or FSDIR_ENTRY */
          116  +  DIR *pDir;                 /* From opendir() */
          117  +  struct stat sStat;         /* Current lstat() results */
          118  +  char *zDir;                /* Directory to read */
          119  +  int nDir;                  /* Value of strlen(zDir) */
          120  +  char *zPath;               /* Path to current entry */
          121  +  int bEof;
          122  +  sqlite3_int64 iRowid;      /* Current rowid */
          123  +};
          124  +
          125  +typedef struct fsdir_tab fsdir_tab;
          126  +struct fsdir_tab {
          127  +  sqlite3_vtab base;         /* Base class - must be first */
          128  +  int eType;                 /* One of FSDIR_DIR or FSDIR_ENTRY */
          129  +};
          130  +
          131  +#define FSDIR_DIR   0
          132  +#define FSDIR_ENTRY 1
          133  +
          134  +/*
          135  +** Construct a new fsdir virtual table object.
          136  +*/
          137  +static int fsdirConnect(
          138  +  sqlite3 *db,
          139  +  void *pAux,
          140  +  int argc, const char *const*argv,
          141  +  sqlite3_vtab **ppVtab,
          142  +  char **pzErr
          143  +){
          144  +  fsdir_tab *pNew = 0;
          145  +  int rc;
          146  +
          147  +  rc = sqlite3_declare_vtab(db, FSDIR_SCHEMA);
          148  +  if( rc==SQLITE_OK ){
          149  +    pNew = (fsdir_tab*)sqlite3_malloc( sizeof(*pNew) );
          150  +    if( pNew==0 ) return SQLITE_NOMEM;
          151  +    memset(pNew, 0, sizeof(*pNew));
          152  +    pNew->eType = (pAux==0 ? FSDIR_DIR : FSDIR_ENTRY);
          153  +  }
          154  +  *ppVtab = (sqlite3_vtab*)pNew;
          155  +  return rc;
          156  +}
          157  +
          158  +/*
          159  +** This method is the destructor for fsdir vtab objects.
          160  +*/
          161  +static int fsdirDisconnect(sqlite3_vtab *pVtab){
          162  +  sqlite3_free(pVtab);
          163  +  return SQLITE_OK;
          164  +}
          165  +
          166  +/*
          167  +** Constructor for a new fsdir_cursor object.
          168  +*/
          169  +static int fsdirOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
          170  +  fsdir_cursor *pCur;
          171  +  pCur = sqlite3_malloc( sizeof(*pCur) );
          172  +  if( pCur==0 ) return SQLITE_NOMEM;
          173  +  memset(pCur, 0, sizeof(*pCur));
          174  +  pCur->eType = ((fsdir_tab*)p)->eType;
          175  +  *ppCursor = &pCur->base;
          176  +  return SQLITE_OK;
          177  +}
          178  +
          179  +/*
          180  +** Destructor for an fsdir_cursor.
          181  +*/
          182  +static int fsdirClose(sqlite3_vtab_cursor *cur){
          183  +  fsdir_cursor *pCur = (fsdir_cursor*)cur;
          184  +  if( pCur->pDir ) closedir(pCur->pDir);
          185  +  sqlite3_free(pCur->zDir);
          186  +  sqlite3_free(pCur->zPath);
          187  +  sqlite3_free(pCur);
          188  +  return SQLITE_OK;
          189  +}
          190  +
          191  +/*
          192  +** Advance an fsdir_cursor to its next row of output.
          193  +*/
          194  +static int fsdirNext(sqlite3_vtab_cursor *cur){
          195  +  fsdir_cursor *pCur = (fsdir_cursor*)cur;
          196  +  struct dirent *pEntry;
          197  +
          198  +  if( pCur->eType==FSDIR_ENTRY ){
          199  +    pCur->bEof = 1;
          200  +    return SQLITE_OK;
          201  +  }
          202  +
          203  +  sqlite3_free(pCur->zPath);
          204  +  pCur->zPath = 0;
          205  +
          206  +  while( 1 ){
          207  +    pEntry = readdir(pCur->pDir);
          208  +    if( pEntry ){
          209  +      if( strcmp(pEntry->d_name, ".") 
          210  +       && strcmp(pEntry->d_name, "..") 
          211  +      ){
          212  +        pCur->zPath = sqlite3_mprintf("%s/%s", pCur->zDir, pEntry->d_name);
          213  +        if( pCur->zPath==0 ) return SQLITE_NOMEM;
          214  +        lstat(pCur->zPath, &pCur->sStat);
          215  +        break;
          216  +      }
          217  +    }else{
          218  +      pCur->bEof = 1;
          219  +      break;
          220  +    }
          221  +  }
          222  +
          223  +  pCur->iRowid++;
          224  +  return SQLITE_OK;
          225  +}
          226  +
          227  +/*
          228  +** Return values of columns for the row at which the series_cursor
          229  +** is currently pointing.
          230  +*/
          231  +static int fsdirColumn(
          232  +  sqlite3_vtab_cursor *cur,   /* The cursor */
          233  +  sqlite3_context *ctx,       /* First argument to sqlite3_result_...() */
          234  +  int i                       /* Which column to return */
          235  +){
          236  +  fsdir_cursor *pCur = (fsdir_cursor*)cur;
          237  +  switch( i ){
          238  +    case 0: { /* name */
          239  +      const char *zName;
          240  +      if( pCur->eType==FSDIR_DIR ){
          241  +        zName = &pCur->zPath[pCur->nDir+1];
          242  +      }else{
          243  +        zName = pCur->zPath;
          244  +      }
          245  +      sqlite3_result_text(ctx, zName, -1, SQLITE_TRANSIENT);
          246  +      break;
          247  +    }
          248  +
          249  +    case 1: /* mode */
          250  +      sqlite3_result_int64(ctx, pCur->sStat.st_mode);
          251  +      break;
          252  +
          253  +    case 2: /* mode */
          254  +      sqlite3_result_int64(ctx, pCur->sStat.st_mtime);
          255  +      break;
          256  +
          257  +    case 3: {
          258  +      mode_t m = pCur->sStat.st_mode;
          259  +      if( S_ISDIR(m) ){
          260  +        sqlite3_result_null(ctx);
          261  +      }else if( S_ISLNK(m) ){
          262  +        char aStatic[64];
          263  +        char *aBuf = aStatic;
          264  +        int nBuf = 64;
          265  +        int n;
          266  +
          267  +        while( 1 ){
          268  +          n = readlink(pCur->zPath, aBuf, nBuf);
          269  +          if( n<nBuf ) break;
          270  +          if( aBuf!=aStatic ) sqlite3_free(aBuf);
          271  +          nBuf = nBuf*2;
          272  +          aBuf = sqlite3_malloc(nBuf);
          273  +          if( aBuf==0 ){
          274  +            sqlite3_result_error_nomem(ctx);
          275  +            return SQLITE_NOMEM;
          276  +          }
          277  +        }
          278  +
          279  +        sqlite3_result_text(ctx, aBuf, n, SQLITE_TRANSIENT);
          280  +        if( aBuf!=aStatic ) sqlite3_free(aBuf);
          281  +      }else{
          282  +        readFileContents(ctx, pCur->zPath);
          283  +      }
          284  +    }
          285  +  }
          286  +  return SQLITE_OK;
          287  +}
          288  +
          289  +/*
          290  +** Return the rowid for the current row. In this implementation, the
          291  +** first row returned is assigned rowid value 1, and each subsequent
          292  +** row a value 1 more than that of the previous.
          293  +*/
          294  +static int fsdirRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
          295  +  fsdir_cursor *pCur = (fsdir_cursor*)cur;
          296  +  *pRowid = pCur->iRowid;
          297  +  return SQLITE_OK;
          298  +}
          299  +
          300  +/*
          301  +** Return TRUE if the cursor has been moved off of the last
          302  +** row of output.
          303  +*/
          304  +static int fsdirEof(sqlite3_vtab_cursor *cur){
          305  +  fsdir_cursor *pCur = (fsdir_cursor*)cur;
          306  +  return pCur->bEof;
          307  +}
          308  +
          309  +static void fsdirSetErrmsg(fsdir_cursor *pCur, const char *zFmt, ...){
          310  +  va_list ap;
          311  +  va_start(ap, zFmt);
          312  +  pCur->base.pVtab->zErrMsg = sqlite3_vmprintf(zFmt, ap);
          313  +  va_end(ap);
          314  +}
          315  +
          316  +/*
          317  +** xFilter callback.
          318  +*/
          319  +static int fsdirFilter(
          320  +  sqlite3_vtab_cursor *cur, 
          321  +  int idxNum, const char *idxStr,
          322  +  int argc, sqlite3_value **argv
          323  +){
          324  +  const char *zDir = 0;
          325  +  fsdir_cursor *pCur = (fsdir_cursor*)cur;
          326  +
          327  +  sqlite3_free(pCur->zDir);
          328  +  pCur->iRowid = 0;
          329  +  pCur->zDir = 0;
          330  +  pCur->bEof = 0;
          331  +  if( pCur->pDir ){
          332  +    closedir(pCur->pDir);
          333  +    pCur->pDir = 0;
          334  +  }
          335  +
          336  +  if( idxNum==0 ){
          337  +    fsdirSetErrmsg(pCur, "table function fsdir requires an argument");
          338  +    return SQLITE_ERROR;
          339  +  }
          340  +
          341  +  assert( argc==1 );
          342  +  zDir = (const char*)sqlite3_value_text(argv[0]);
          343  +  if( zDir==0 ){
          344  +    fsdirSetErrmsg(pCur, "table function fsdir requires a non-NULL argument");
          345  +    return SQLITE_ERROR;
          346  +  }
          347  +
          348  +  pCur->zDir = sqlite3_mprintf("%s", zDir);
          349  +  if( pCur->zDir==0 ){
          350  +    return SQLITE_NOMEM;
          351  +  }
          352  +
          353  +  if( pCur->eType==FSDIR_ENTRY ){
          354  +    int rc = lstat(pCur->zDir, &pCur->sStat);
          355  +    if( rc ){
          356  +      fsdirSetErrmsg(pCur, "cannot stat file: %s", pCur->zDir);
          357  +    }else{
          358  +      pCur->zPath = sqlite3_mprintf("%s", pCur->zDir);
          359  +      if( pCur->zPath==0 ) return SQLITE_NOMEM;
          360  +    }
          361  +    return SQLITE_OK;
          362  +  }else{
          363  +    pCur->nDir = strlen(pCur->zDir);
          364  +    pCur->pDir = opendir(zDir);
          365  +    if( pCur->pDir==0 ){
          366  +      fsdirSetErrmsg(pCur, "error in opendir(\"%s\")", zDir);
          367  +      return SQLITE_ERROR;
          368  +    }
          369  +
          370  +    return fsdirNext(cur);
          371  +  }
          372  +}
          373  +
          374  +/*
          375  +** SQLite will invoke this method one or more times while planning a query
          376  +** that uses the generate_series virtual table.  This routine needs to create
          377  +** a query plan for each invocation and compute an estimated cost for that
          378  +** plan.
          379  +**
          380  +** In this implementation idxNum is used to represent the
          381  +** query plan.  idxStr is unused.
          382  +**
          383  +** The query plan is represented by bits in idxNum:
          384  +**
          385  +**  (1)  start = $value  -- constraint exists
          386  +**  (2)  stop = $value   -- constraint exists
          387  +**  (4)  step = $value   -- constraint exists
          388  +**  (8)  output in descending order
          389  +*/
          390  +static int fsdirBestIndex(
          391  +  sqlite3_vtab *tab,
          392  +  sqlite3_index_info *pIdxInfo
          393  +){
          394  +  int i;                 /* Loop over constraints */
          395  +
          396  +  const struct sqlite3_index_constraint *pConstraint;
          397  +  pConstraint = pIdxInfo->aConstraint;
          398  +  for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
          399  +    if( pConstraint->usable==0 ) continue;
          400  +    if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
          401  +    if( pConstraint->iColumn!=4 ) continue;
          402  +    break;
          403  +  }
          404  +
          405  +  if( i<pIdxInfo->nConstraint ){
          406  +    pIdxInfo->aConstraintUsage[i].omit = 1;
          407  +    pIdxInfo->aConstraintUsage[i].argvIndex = 1;
          408  +    pIdxInfo->idxNum = 1;
          409  +    pIdxInfo->estimatedCost = 10.0;
          410  +  }else{
          411  +    pIdxInfo->idxNum = 0;
          412  +    pIdxInfo->estimatedCost = (double)(((sqlite3_int64)1) << 50);
          413  +  }
          414  +
          415  +  return SQLITE_OK;
          416  +}
          417  +
          418  +static int fsdirRegister(sqlite3 *db){
          419  +  static sqlite3_module fsdirModule = {
          420  +    0,                         /* iVersion */
          421  +    0,                         /* xCreate */
          422  +    fsdirConnect,              /* xConnect */
          423  +    fsdirBestIndex,            /* xBestIndex */
          424  +    fsdirDisconnect,           /* xDisconnect */
          425  +    0,                         /* xDestroy */
          426  +    fsdirOpen,                 /* xOpen - open a cursor */
          427  +    fsdirClose,                /* xClose - close a cursor */
          428  +    fsdirFilter,               /* xFilter - configure scan constraints */
          429  +    fsdirNext,                 /* xNext - advance a cursor */
          430  +    fsdirEof,                  /* xEof - check for end of scan */
          431  +    fsdirColumn,               /* xColumn - read data */
          432  +    fsdirRowid,                /* xRowid - read data */
          433  +    0,                         /* xUpdate */
          434  +    0,                         /* xBegin */
          435  +    0,                         /* xSync */
          436  +    0,                         /* xCommit */
          437  +    0,                         /* xRollback */
          438  +    0,                         /* xFindMethod */
          439  +    0,                         /* xRename */
          440  +  };
          441  +
          442  +  int rc = sqlite3_create_module(db, "fsdir", &fsdirModule, 0);
          443  +  if( rc==SQLITE_OK ){
          444  +    rc = sqlite3_create_module(db, "fsentry", &fsdirModule, (void*)1);
          445  +  }
          446  +  return rc;
          447  +}
          448  +#else         /* SQLITE_OMIT_VIRTUALTABLE */
          449  +# define fsdirRegister(x) SQLITE_OK
          450  +#endif
    83    451   
    84    452   #ifdef _WIN32
    85    453   __declspec(dllexport)
    86    454   #endif
    87    455   int sqlite3_fileio_init(
    88    456     sqlite3 *db, 
    89    457     char **pzErrMsg, 
................................................................................
    94    462     (void)pzErrMsg;  /* Unused parameter */
    95    463     rc = sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0,
    96    464                                  readfileFunc, 0, 0);
    97    465     if( rc==SQLITE_OK ){
    98    466       rc = sqlite3_create_function(db, "writefile", 2, SQLITE_UTF8, 0,
    99    467                                    writefileFunc, 0, 0);
   100    468     }
          469  +  if( rc==SQLITE_OK ){
          470  +    rc = fsdirRegister(db);
          471  +  }
   101    472     return rc;
   102    473   }

Changes to src/shell.c.in.

  4070   4070    usage:
  4071   4071     raw_printf(stderr, "Usage %s sub-command ?switches...?\n", azArg[0]);
  4072   4072     raw_printf(stderr, "Where sub-commands are:\n");
  4073   4073     raw_printf(stderr, "    fkey-indexes\n");
  4074   4074     return SQLITE_ERROR;
  4075   4075   }
  4076   4076   
         4077  +static void shellPrepare(
         4078  +  ShellState *p, 
         4079  +  int *pRc, 
         4080  +  const char *zSql, 
         4081  +  sqlite3_stmt **ppStmt
         4082  +){
         4083  +  *ppStmt = 0;
         4084  +  if( *pRc==SQLITE_OK ){
         4085  +    int rc = sqlite3_prepare_v2(p->db, zSql, -1, ppStmt, 0);
         4086  +    if( rc!=SQLITE_OK ){
         4087  +      raw_printf(stderr, "sql error: %s (%d)\n", 
         4088  +          sqlite3_errmsg(p->db), sqlite3_errcode(p->db)
         4089  +      );
         4090  +      *pRc = rc;
         4091  +    }
         4092  +  }
         4093  +}
         4094  +
         4095  +static void shellFinalize(
         4096  +  int *pRc, 
         4097  +  sqlite3_stmt *pStmt
         4098  +){
         4099  +  int rc = sqlite3_finalize(pStmt);
         4100  +  if( *pRc==SQLITE_OK ) *pRc = rc;
         4101  +}
         4102  +
         4103  +static void shellReset(
         4104  +  int *pRc, 
         4105  +  sqlite3_stmt *pStmt
         4106  +){
         4107  +  int rc = sqlite3_reset(pStmt);
         4108  +  if( *pRc==SQLITE_OK ) *pRc = rc;
         4109  +}
         4110  +
         4111  +/*
         4112  +** Implementation of .ar "eXtract" command. 
         4113  +*/
         4114  +static int arExtractCommand(ShellState *p, int bVerbose){
         4115  +  const char *zSql = 
         4116  +    "SELECT name, mode, mtime, sz, data FROM sqlar";
         4117  +  sqlite3_stmt *pSql = 0;
         4118  +  int rc = SQLITE_OK;
         4119  +
         4120  +  shellPrepare(p, &rc, zSql, &pSql);
         4121  +  while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
         4122  +  }
         4123  +
         4124  +  shellFinalize(&rc, pSql);
         4125  +  return rc;
         4126  +}
         4127  +
         4128  +/*
         4129  +** Implementation of .ar "Create" command. 
         4130  +**
         4131  +** Create the "sqlar" table in the database if it does not already exist.
         4132  +** Then add each file in the azFile[] array to the archive. Directories
         4133  +** are added recursively. If argument bVerbose is non-zero, a message is
         4134  +** printed on stdout for each file archived.
         4135  +*/
         4136  +static int arCreateCommand(
         4137  +  ShellState *p,                  /* Shell state pointer */
         4138  +  char **azFile,                  /* Array of files to add to archive */
         4139  +  int nFile,                      /* Number of entries in azFile[] */
         4140  +  int bVerbose                    /* True to be verbose on stdout */
         4141  +){
         4142  +  const char *zSql = 
         4143  +    "WITH f(n, m, t, d) AS ("
         4144  +    "  SELECT name, mode, mtime, data FROM fsentry(:1) UNION ALL "
         4145  +    "  SELECT n || '/' || name, mode, mtime, data "
         4146  +    "      FROM f, fsdir(n) WHERE (m&?)"
         4147  +    ") SELECT * FROM f";
         4148  +
         4149  +  const char *zSqlar = 
         4150  +      "CREATE TABLE IF NOT EXISTS sqlar("
         4151  +      "name TEXT PRIMARY KEY,  -- name of the file\n"
         4152  +      "mode INT,               -- access permissions\n"
         4153  +      "mtime INT,              -- last modification time\n"
         4154  +      "sz INT,                 -- original file size\n"
         4155  +      "data BLOB               -- compressed content\n"
         4156  +      ")";
         4157  +
         4158  +  const char *zInsert = "REPLACE INTO sqlar VALUES(?, ?, ?, ?, ?)";
         4159  +
         4160  +  sqlite3_stmt *pStmt = 0;        /* Directory traverser */
         4161  +  sqlite3_stmt *pInsert = 0;      /* Compilation of zInsert */
         4162  +  int i;                          /* For iterating through azFile[] */
         4163  +  int rc;                         /* Return code */
         4164  +
         4165  +  Bytef *aCompress = 0;           /* Compression buffer */
         4166  +  int nCompress = 0;              /* Size of compression buffer */
         4167  +  
         4168  +  rc = sqlite3_exec(p->db, "SAVEPOINT ar;", 0, 0, 0);
         4169  +  if( rc!=SQLITE_OK ) return rc;
         4170  +
         4171  +  rc = sqlite3_exec(p->db, zSqlar, 0, 0, 0);
         4172  +  shellPrepare(p, &rc, zInsert, &pInsert);
         4173  +  shellPrepare(p, &rc, zSql, &pStmt);
         4174  +
         4175  +  for(i=0; i<nFile && rc==SQLITE_OK; i++){
         4176  +    sqlite3_bind_text(pStmt, 1, azFile[i], -1, SQLITE_STATIC);
         4177  +    sqlite3_bind_int(pStmt, 2, S_IFDIR);
         4178  +    while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
         4179  +      int sz;
         4180  +      const char *zName = (const char*)sqlite3_column_text(pStmt, 0);
         4181  +      int mode = sqlite3_column_int(pStmt, 1);
         4182  +      unsigned int mtime = sqlite3_column_int(pStmt, 2);
         4183  +
         4184  +      if( bVerbose ){
         4185  +        raw_printf(stdout, "%s\n", zName);
         4186  +      }
         4187  +
         4188  +      sqlite3_bind_text(pInsert, 1, zName, -1, SQLITE_STATIC);
         4189  +      sqlite3_bind_int(pInsert, 2, mode);
         4190  +      sqlite3_bind_int64(pInsert, 3, (sqlite3_int64)mtime);
         4191  +
         4192  +      if( S_ISDIR(mode) ){
         4193  +        sz = 0;
         4194  +        sqlite3_bind_null(pInsert, 5);
         4195  +      }else if( S_ISLNK(mode) ){
         4196  +        sz = -1;
         4197  +        sqlite3_bind_value(pInsert, 5, sqlite3_column_value(pStmt, 3));
         4198  +      }else{
         4199  +        uLongf nReq;              /* Required size of compression buffer */
         4200  +        const Bytef *pData = (const Bytef*)sqlite3_column_blob(pStmt, 3);
         4201  +        sz = sqlite3_column_bytes(pStmt, 3);
         4202  +        nReq = compressBound(sz);
         4203  +        if( aCompress==0 || nReq>nCompress ){
         4204  +          Bytef *aNew = sqlite3_realloc(aCompress, nReq);
         4205  +          if( aNew==0 ){
         4206  +            rc = SQLITE_NOMEM;
         4207  +          }else{
         4208  +            aCompress = aNew;
         4209  +            nCompress = nReq;
         4210  +          }
         4211  +        }
         4212  +
         4213  +        if( Z_OK!=compress(aCompress, &nReq, pData, sz) ){
         4214  +          rc = SQLITE_ERROR;
         4215  +        }
         4216  +        if( nReq<sz ){
         4217  +          sqlite3_bind_blob(pInsert, 5, aCompress, nReq, SQLITE_STATIC);
         4218  +        }else{
         4219  +          sqlite3_bind_blob(pInsert, 5, pData, sz, SQLITE_STATIC);
         4220  +        }
         4221  +      }
         4222  +
         4223  +      if( rc==SQLITE_OK ){
         4224  +        sqlite3_bind_int(pInsert, 4, sz);
         4225  +        sqlite3_step(pInsert);
         4226  +        rc = sqlite3_reset(pInsert);
         4227  +      }
         4228  +    }
         4229  +    shellReset(&rc, pStmt);
         4230  +  }
         4231  +
         4232  +  if( rc!=SQLITE_OK ){
         4233  +    sqlite3_exec(p->db, "ROLLBACK TO ar; RELEASE ar;", 0, 0, 0);
         4234  +  }else{
         4235  +    rc = sqlite3_exec(p->db, "RELEASE ar;", 0, 0, 0);
         4236  +  }
         4237  +  shellFinalize(&rc, pStmt);
         4238  +  shellFinalize(&rc, pInsert);
         4239  +  sqlite3_free(aCompress);
         4240  +  return rc;
         4241  +}
         4242  +
         4243  +/*
         4244  +** Implementation of ".ar" dot command.
         4245  +*/
         4246  +static int arDotCommand(
         4247  +  ShellState *pState,             /* Current shell tool state */
         4248  +  char **azArg,                   /* Array of arguments passed to dot command */
         4249  +  int nArg                        /* Number of entries in azArg[] */
         4250  +){
         4251  +  int bVerbose = 0;
         4252  +  char cmd = 0;
         4253  +  int i;
         4254  +  int n1;
         4255  +  if( nArg<=1 ) goto usage;
         4256  +
         4257  +  n1 = strlen(azArg[1]);
         4258  +  for(i=0; i<n1; i++){
         4259  +    char c = azArg[1][i];
         4260  +    if( c=='c' || c=='x' ){
         4261  +      if( cmd ) goto usage;
         4262  +      cmd = c;
         4263  +    }
         4264  +    else if( c=='v' ){
         4265  +      bVerbose = 1;
         4266  +    }else{
         4267  +      goto usage;
         4268  +    }
         4269  +  }
         4270  +
         4271  +  if( cmd=='c' ){
         4272  +    return arCreateCommand(pState, &azArg[2], nArg-2, bVerbose);
         4273  +  }
         4274  +
         4275  +  if( cmd=='x' ){
         4276  +    if( nArg!=2 ) goto usage;
         4277  +    return arExtractCommand(pState, bVerbose);
         4278  +  }
         4279  +
         4280  + usage:
         4281  +  raw_printf(stderr, "Usage %s sub-command ?args...?\n", azArg[0]);
         4282  +  return SQLITE_ERROR;
         4283  +}
         4284  +
  4077   4285   
  4078   4286   /*
  4079   4287   ** If an input line begins with "." then invoke this routine to
  4080   4288   ** process that line.
  4081   4289   **
  4082   4290   ** Return 1 on error, 2 to exit, and 0 otherwise.
  4083   4291   */
................................................................................
  4129   4337       if( booleanValue(azArg[1]) ){
  4130   4338         sqlite3_set_authorizer(p->db, shellAuth, p);
  4131   4339       }else{
  4132   4340         sqlite3_set_authorizer(p->db, 0, 0);
  4133   4341       }
  4134   4342     }else
  4135   4343   #endif
         4344  +
         4345  +#ifndef SQLITE_OMIT_VIRTUALTABLE
         4346  +  if( c=='a' && strncmp(azArg[0], "ar", n)==0 ){
         4347  +    open_db(p, 0);
         4348  +    rc = arDotCommand(p, azArg, nArg);
         4349  +  }else
         4350  +#endif
  4136   4351   
  4137   4352     if( (c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0)
  4138   4353      || (c=='s' && n>=3 && strncmp(azArg[0], "save", n)==0)
  4139   4354     ){
  4140   4355       const char *zDestFile = 0;
  4141   4356       const char *zDb = 0;
  4142   4357       sqlite3 *pDest;