/ Check-in [0cc699d1]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

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

Overview
Comment:Add the ".ar x" command to the shell. For extracting the contents of sqlar archives.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | sqlar-shell-support
Files: files | file ages | folders
SHA3-256: 0cc699d14adfe8c7b7be50c180186562861806c47425c80c935bce43ee5c5c12
User & Date: dan 2017-12-07 21:03:33
Context
2017-12-09
17:58
Improve parsing of ".ar" commands. Add new test file for the same. check-in: 840401cc user: dan tags: sqlar-shell-support
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
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to ext/misc/fileio.c.

26
27
28
29
30
31
32











33
34
35
36
37
38
39
..
64
65
66
67
68
69
70
71










72
73

74
75
76
77
78
79
80
81
82
83
84
85
86

87


88




89
90





















91
92





93
94
95
96
97




98

99

100
101
102
103



104
105
106
107
108




109
110
111
112
113
114
115
...
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
**   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>












#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;
................................................................................
){
  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
** file X for writing.
*/
static void writefileFunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  FILE *out;
  const char *z;
  sqlite3_int64 rc;
  const char *zFile;




  (void)(argc);  /* Unused parameter */




  zFile = (const char*)sqlite3_value_text(argv[0]);
  if( zFile==0 ) return;





















  out = fopen(zFile, "wb");
  if( out==0 ) return;





  z = (const char*)sqlite3_value_blob(argv[1]);
  if( z==0 ){
    rc = 0;
  }else{
    rc = fwrite(z, 1, sqlite3_value_bytes(argv[1]), out);




  }

  fclose(out);

  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 */
................................................................................
){
  int rc = SQLITE_OK;
  SQLITE_EXTENSION_INIT2(pApi);
  (void)pzErrMsg;  /* Unused parameter */
  rc = sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0,
                               readfileFunc, 0, 0);
  if( rc==SQLITE_OK ){
    rc = sqlite3_create_function(db, "writefile", 2, SQLITE_UTF8, 0,
                                 writefileFunc, 0, 0);
  }
  if( rc==SQLITE_OK ){
    rc = fsdirRegister(db);
  }
  return rc;
}







>
>
>
>
>
>
>
>
>
>
>







 








>
>
>
>
>
>
>
>
>
>

|
>
|
|
|






<
<
<

>

>
>
|
>
>
>
>


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
>
>
>
>
>
|
|
<
<
|
>
>
>
>
|
>
|
>
|
|

<
>
>
>
|
<
<
<
<
>
>
>
>







 







|







26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
..
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146


147
148
149
150
151
152
153
154
155
156
157
158

159
160
161
162




163
164
165
166
167
168
169
170
171
172
173
...
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
**   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>
#include <string.h>
#include <assert.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <time.h>
#include <utime.h>


#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;
................................................................................
){
  const char *zName;
  (void)(argc);  /* Unused parameter */
  zName = (const char*)sqlite3_value_text(argv[0]);
  if( zName==0 ) return;
  readFileContents(context, zName);
}

static void ctxErrorMsg(sqlite3_context *ctx, const char *zFmt, ...){
  char *zMsg = 0;
  va_list ap;
  va_start(ap, zFmt);
  zMsg = sqlite3_vmprintf(zFmt, ap);
  sqlite3_result_error(ctx, zMsg, -1);
  sqlite3_free(zMsg);
  va_end(ap);
}

/*
** Implementation of the "writefile(W,X[,Y]])" SQL function.  
**
** The argument X is written into file W.  The number of bytes written is
** returned. Or NULL is returned if something goes wrong, such as being unable
** to open file X for writing.
*/
static void writefileFunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){



  const char *zFile;
  mode_t mode = 0;

  if( argc<2 || argc>3 ){
    sqlite3_result_error(context, 
        "wrong number of arguments to function writefile()", -1
    );
    return;
  }

  zFile = (const char*)sqlite3_value_text(argv[0]);
  if( zFile==0 ) return;
  if( argc>=3 ){
    sqlite3_result_int(context, 0);
    mode = sqlite3_value_int(argv[2]);
  }

  if( S_ISLNK(mode) ){
    const char *zTo = (const char*)sqlite3_value_text(argv[1]);
    if( symlink(zTo, zFile)<0 ){
      ctxErrorMsg(context, "failed to create symlink: %s", zFile);
      return;
    }
  }else{
    if( S_ISDIR(mode) ){
      if( mkdir(zFile, mode) ){
        ctxErrorMsg(context, "failed to create directory: %s", zFile);
        return;
      }
    }else{
      sqlite3_int64 nWrite = 0;
      const char *z;
      int rc = 0;
      FILE *out = fopen(zFile, "wb");
      if( out==0 ){
        if( argc>2 ){
          ctxErrorMsg(context, "failed to open file for writing: %s", zFile);
        }
        return;
      }
      z = (const char*)sqlite3_value_blob(argv[1]);
      if( z ){


        sqlite3_int64 n = fwrite(z, 1, sqlite3_value_bytes(argv[1]), out);
        nWrite = sqlite3_value_bytes(argv[1]);
        if( nWrite!=n ){
          ctxErrorMsg(context, "failed to write file: %s", zFile);
          rc = 1;
        }
      }
      fclose(out);
      if( rc ) return;
      sqlite3_result_int64(context, nWrite);
    }


    if( argc>2 && chmod(zFile, mode & 0777) ){
      ctxErrorMsg(context, "failed to chmod file: %s", zFile);
      return;
    }




  }
}

#ifndef SQLITE_OMIT_VIRTUALTABLE

/* 
*/
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 */
................................................................................
){
  int rc = SQLITE_OK;
  SQLITE_EXTENSION_INIT2(pApi);
  (void)pzErrMsg;  /* Unused parameter */
  rc = sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0,
                               readfileFunc, 0, 0);
  if( rc==SQLITE_OK ){
    rc = sqlite3_create_function(db, "writefile", -1, SQLITE_UTF8, 0,
                                 writefileFunc, 0, 0);
  }
  if( rc==SQLITE_OK ){
    rc = fsdirRegister(db);
  }
  return rc;
}

Changes to src/shell.c.in.

4092
4093
4094
4095
4096
4097
4098


4099
4100






4101
4102
4103
4104
4105
4106
4107
....
4108
4109
4110
4111
4112
4113
4114
4115



4116



4117
4118
4119



4120
4121


4122
4123
4124













4125
4126
4127
4128
4129
4130
4131
  }
}

static void shellFinalize(
  int *pRc, 
  sqlite3_stmt *pStmt
){


  int rc = sqlite3_finalize(pStmt);
  if( *pRc==SQLITE_OK ) *pRc = rc;






}

static void shellReset(
  int *pRc, 
  sqlite3_stmt *pStmt
){
  int rc = sqlite3_reset(pStmt);
................................................................................
  if( *pRc==SQLITE_OK ) *pRc = rc;
}

/*
** Implementation of .ar "eXtract" command. 
*/
static int arExtractCommand(ShellState *p, int bVerbose){
  const char *zSql = 



    "SELECT name, mode, mtime, sz, data FROM sqlar";



  sqlite3_stmt *pSql = 0;
  int rc = SQLITE_OK;




  shellPrepare(p, &rc, zSql, &pSql);
  while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){


  }

  shellFinalize(&rc, pSql);













  return rc;
}

/*
** Implementation of .ar "Create" command. 
**
** Create the "sqlar" table in the database if it does not already exist.







>
>
|
|
>
>
>
>
>
>







 







|
>
>
>
|
>
>
>



>
>
>
|

>
>
|
|

>
>
>
>
>
>
>
>
>
>
>
>
>







4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
....
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
  }
}

static void shellFinalize(
  int *pRc, 
  sqlite3_stmt *pStmt
){
  if( pStmt ){
    sqlite3 *db = sqlite3_db_handle(pStmt);
    int rc = sqlite3_finalize(pStmt);
    if( *pRc==SQLITE_OK ){
      if( rc!=SQLITE_OK ){
        raw_printf(stderr, "SQL error: %s\n", sqlite3_errmsg(db));
      }
      *pRc = rc;
    }
  }
}

static void shellReset(
  int *pRc, 
  sqlite3_stmt *pStmt
){
  int rc = sqlite3_reset(pStmt);
................................................................................
  if( *pRc==SQLITE_OK ) *pRc = rc;
}

/*
** Implementation of .ar "eXtract" command. 
*/
static int arExtractCommand(ShellState *p, int bVerbose){
  const char *zSql1 = 
    "SELECT name, writefile(name, "
    "CASE WHEN (data AND sz>=0 AND sz!=length(data)) THEN uncompress(data) "
    "   ELSE data END, "
    "mode) FROM sqlar";
  const char *zSql2 = "SELECT name, mtime FROM sqlar"; 

  struct timespec times[2];
  sqlite3_stmt *pSql = 0;
  int rc = SQLITE_OK;

  memset(times, 0, sizeof(times));
  times[0].tv_sec = time(0);

  shellPrepare(p, &rc, zSql1, &pSql);
  while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
    if( bVerbose ){
      raw_printf(stdout, "%s\n", sqlite3_column_text(pSql, 0));
    }
  }
  shellFinalize(&rc, pSql);

  shellPrepare(p, &rc, zSql2, &pSql);
  while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
    const char *zPath = (const char*)sqlite3_column_text(pSql, 0);
    times[1].tv_sec = (time_t)sqlite3_column_int64(pSql, 1);
    if( utimensat(AT_FDCWD, zPath, times, AT_SYMLINK_NOFOLLOW) ){
      raw_printf(stderr, "failed to set timestamp for %s\n", zPath);
      rc = SQLITE_ERROR;
      break;
    }
  }
  shellFinalize(&rc, pSql);

  return rc;
}

/*
** Implementation of .ar "Create" command. 
**
** Create the "sqlar" table in the database if it does not already exist.