/ Check-in [28ab930436]
Login

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

Overview
Comment:Implement the ".ar --create" command using a single "REPLACE INTO sqlar SELECT ... FROM fsdir();" statement. Add the shell_putsnl() SQL function for providing --verbose output.
Timelines: family | ancestors | descendants | both | archive-improvements
Files: files | file ages | folders
SHA3-256: 28ab930436fea33c79073e84f39d9e381fa60b4702a5dcbfaaed72baeeae8431
User & Date: drh 2018-01-10 16:50:18
Context
2018-01-10
17:19
In the fileio.c extension, change the filetype(MODE) function into lsmode(MODE). Use the new lsmode(MODE) function in shell.c. (check-in: 52d12ba9f3 user: drh tags: archive-improvements)
16:50
Implement the ".ar --create" command using a single "REPLACE INTO sqlar SELECT ... FROM fsdir();" statement. Add the shell_putsnl() SQL function for providing --verbose output. (check-in: 28ab930436 user: drh tags: archive-improvements)
15:53
Add the "filetype()" SQL function for interpreting file modes to the fileio.c extension. (check-in: 58c0c74c40 user: drh tags: archive-improvements)
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/shell.c.in.

1130
1131
1132
1133
1134
1135
1136
















1137
1138
1139
1140
1141
1142
1143
*/
static void shellLog(void *pArg, int iErrCode, const char *zMsg){
  ShellState *p = (ShellState*)pArg;
  if( p->pLog==0 ) return;
  utf8_printf(p->pLog, "(%d) %s\n", iErrCode, zMsg);
  fflush(p->pLog);
}

















/*
** Output the given string as a hex-encoded blob (eg. X'1234' )
*/
static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){
  int i;
  char *zBlob = (char *)pBlob;







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







1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
*/
static void shellLog(void *pArg, int iErrCode, const char *zMsg){
  ShellState *p = (ShellState*)pArg;
  if( p->pLog==0 ) return;
  utf8_printf(p->pLog, "(%d) %s\n", iErrCode, zMsg);
  fflush(p->pLog);
}

/*
** SQL function:  shell_putsnl(X)
**
** Write the text X to the screen (or whatever output is being directed)
** adding a newline at the end, and then return X.
*/
static void shellPutsFunc(
  sqlite3_context *pCtx,
  int nVal,
  sqlite3_value **apVal
){
  ShellState *p = (ShellState*)sqlite3_user_data(pCtx);
  utf8_printf(p->out, "%s\n", sqlite3_value_text(apVal[0]));
  sqlite3_result_value(pCtx, apVal[0]);
}

/*
** Output the given string as a hex-encoded blob (eg. X'1234' )
*/
static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){
  int i;
  char *zBlob = (char *)pBlob;
3303
3304
3305
3306
3307
3308
3309


3310
3311
3312
3313
3314
3315
3316
    sqlite3_zipfile_init(p->db, 0, 0);
    sqlite3_sqlar_init(p->db, 0, 0);
#endif
    sqlite3_create_function(p->db, "shell_add_schema", 3, SQLITE_UTF8, 0,
                            shellAddSchemaName, 0, 0);
    sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, 0,
                            shellModuleSchema, 0, 0);


    if( p->openMode==SHELL_OPEN_ZIPFILE ){
      char *zSql = sqlite3_mprintf(
         "CREATE VIRTUAL TABLE zip USING zipfile(%Q);", p->zDbFilename);
      sqlite3_exec(p->db, zSql, 0, 0, 0);
      sqlite3_free(zSql);
    }
  }







>
>







3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
    sqlite3_zipfile_init(p->db, 0, 0);
    sqlite3_sqlar_init(p->db, 0, 0);
#endif
    sqlite3_create_function(p->db, "shell_add_schema", 3, SQLITE_UTF8, 0,
                            shellAddSchemaName, 0, 0);
    sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, 0,
                            shellModuleSchema, 0, 0);
    sqlite3_create_function(p->db, "shell_putsnl", 1, SQLITE_UTF8, p,
                            shellPutsFunc, 0, 0);
    if( p->openMode==SHELL_OPEN_ZIPFILE ){
      char *zSql = sqlite3_mprintf(
         "CREATE VIRTUAL TABLE zip USING zipfile(%Q);", p->zDbFilename);
      sqlite3_exec(p->db, zSql, 0, 0, 0);
      sqlite3_free(zSql);
    }
  }
5004
5005
5006
5007
5008
5009
5010
5011
5012
5013
5014
5015
5016
5017
5018
5019
5020
5021
5022






5023
5024



5025
5026
5027
5028
5029
5030
5031
5032
5033
5034
5035
5036
5037
5038
5039
5040
5041
5042
5043
5044
5045
5046
5047
5048
5049
5050
5051
5052
5053
5054
5055
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
5067
5068
5069
5070
5071
5072
5073
5074
5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
5097
5098
5099
5100
5101
5102
5103
5104
5105
5106
5107
** The create command is the same as update, except that it drops
** any existing "sqlar" table before beginning.
*/
static int arCreateOrUpdateCommand(
  ArCommand *pAr,                 /* Command arguments and options */
  int bUpdate                     /* true for a --create.  false for --update */
){
  const char *zSql = "SELECT name, mode, mtime, data FROM fsdir($name, $dir)";
  const char *zCreate = 
      "CREATE TABLE IF NOT EXISTS sqlar(\n"
      "  name TEXT PRIMARY KEY,  -- name of the file\n"
      "  mode INT,               -- access permissions\n"
      "  mtime INT,              -- last modification time\n"
      "  sz INT,                 -- original file size\n"
      "  data BLOB               -- compressed content\n"
      ")";
  const char *zDrop = "DROP TABLE IF EXISTS sqlar";
  const char *zInsert = "REPLACE INTO sqlar VALUES(?,?,?,?,sqlar_compress(?))";







  sqlite3_stmt *pStmt = 0;        /* Directory traverser */
  sqlite3_stmt *pInsert = 0;      /* Compilation of zInsert */



  int i;                          /* For iterating through azFile[] */
  int j;                          /* Parameter index */
  int rc;                         /* Return code */

  assert( pAr->bZip==0 );

  rc = arExecSql(pAr, "SAVEPOINT ar;");
  if( rc!=SQLITE_OK ) return rc;

  if( bUpdate==0 ){
    rc = arExecSql(pAr, zDrop);
    if( rc!=SQLITE_OK ) return rc;
  }

  rc = arExecSql(pAr, zCreate);
  if( !pAr->bDryRun ){
    shellPrepare(pAr->db, &rc, zInsert, &pInsert);
  }
  shellPrepare(pAr->db, &rc, zSql, &pStmt);
  j = sqlite3_bind_parameter_index(pStmt, "$dir");
  sqlite3_bind_text(pStmt, j, pAr->zDir, -1, SQLITE_STATIC);
  if( pAr->bDryRun ){
    utf8_printf(pAr->p->out, "%s;\n", sqlite3_sql(pStmt));
  }

  for(i=0; i<pAr->nArg && rc==SQLITE_OK; i++){
    j = sqlite3_bind_parameter_index(pStmt, "$name");
    sqlite3_bind_text(pStmt, j, pAr->azArg[i], -1, SQLITE_STATIC);
    while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
      int sz;
      const char *zName = (const char*)sqlite3_column_text(pStmt, 0);
      int mode = sqlite3_column_int(pStmt, 1);
      unsigned int mtime = sqlite3_column_int(pStmt, 2);

      if( pAr->bVerbose ){
        utf8_printf(pAr->p->out, "%s\n", zName);
      }
      if( pAr->bDryRun ){
        utf8_printf(pAr->p->out, "%s;\n", zInsert);
        continue;
      }

      sqlite3_bind_text(pInsert, 1, zName, -1, SQLITE_STATIC);
      sqlite3_bind_int(pInsert, 2, mode);
      sqlite3_bind_int64(pInsert, 3, (sqlite3_int64)mtime);

      if( S_ISDIR(mode) ){
        sz = 0;
        sqlite3_bind_null(pInsert, 5);
      }else{
        sqlite3_bind_value(pInsert, 5, sqlite3_column_value(pStmt, 3));
        if( S_ISLNK(mode) ){
          sz = -1;
        }else{
          sz = sqlite3_column_bytes(pStmt, 3);
        }
      }

      sqlite3_bind_int(pInsert, 4, sz);
      if( pAr->bDryRun ){
        utf8_printf(pAr->p->out, "%s\n", sqlite3_sql(pInsert));
      }else{
        sqlite3_step(pInsert);
      }
      rc = sqlite3_reset(pInsert);
    }
    shellReset(&rc, pStmt);
  }

  if( rc!=SQLITE_OK ){
    arExecSql(pAr, "ROLLBACK TO ar; RELEASE ar;");
  }else{
    rc = arExecSql(pAr, "RELEASE ar;");
  }
  shellFinalize(&rc, pStmt);
  shellFinalize(&rc, pInsert);
  return rc;
}

/*
** Implementation of ".ar" dot command.
*/
static int arDotCommand(







<









|
|
>
>
>
>
>
>
|
|
>
>
>

<


<
<


<




<

<
<
<
<
<
<
<
<
<
<

<
<
<
<
<
<
<
|
|
<
<
<
<
<
<
|
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|
<
<
<
<
<





<
<







5022
5023
5024
5025
5026
5027
5028

5029
5030
5031
5032
5033
5034
5035
5036
5037
5038
5039
5040
5041
5042
5043
5044
5045
5046
5047
5048
5049
5050
5051

5052
5053


5054
5055

5056
5057
5058
5059

5060










5061







5062
5063






5064



5065
















5066
5067





5068
5069
5070
5071
5072


5073
5074
5075
5076
5077
5078
5079
** The create command is the same as update, except that it drops
** any existing "sqlar" table before beginning.
*/
static int arCreateOrUpdateCommand(
  ArCommand *pAr,                 /* Command arguments and options */
  int bUpdate                     /* true for a --create.  false for --update */
){

  const char *zCreate = 
      "CREATE TABLE IF NOT EXISTS sqlar(\n"
      "  name TEXT PRIMARY KEY,  -- name of the file\n"
      "  mode INT,               -- access permissions\n"
      "  mtime INT,              -- last modification time\n"
      "  sz INT,                 -- original file size\n"
      "  data BLOB               -- compressed content\n"
      ")";
  const char *zDrop = "DROP TABLE IF EXISTS sqlar";
  const char *zInsertFmt = 
     "REPLACE INTO sqlar(name,mode,mtime,sz,data)\n"
     "  SELECT\n"
     "    %s,\n"
     "    mode,\n"
     "    mtime,\n"
     "    CASE filetype(mode)\n"
     "      WHEN 'file' THEN length(data)\n"
     "      WHEN 'directory' THEN 0\n"
     "      ELSE -1 END,\n"
     "    CASE WHEN filetype(mode)<>'directory' THEN data ELSE NULL END\n"
     "  FROM fsdir(%Q,%Q)\n"
     "  WHERE filetype(mode)<>'unknown'";
  int i;                          /* For iterating through azFile[] */

  int rc;                         /* Return code */



  rc = arExecSql(pAr, "SAVEPOINT ar;");
  if( rc!=SQLITE_OK ) return rc;

  if( bUpdate==0 ){
    rc = arExecSql(pAr, zDrop);
    if( rc!=SQLITE_OK ) return rc;
  }

  rc = arExecSql(pAr, zCreate);










  for(i=0; i<pAr->nArg && rc==SQLITE_OK; i++){







    char *zSql = sqlite3_mprintf(zInsertFmt,
        pAr->bVerbose ? "shell_putsnl(name)" : "name",






        pAr->azArg[i], pAr->zDir);



    rc = arExecSql(pAr, zSql);
















    sqlite3_free(zSql);
  }





  if( rc!=SQLITE_OK ){
    arExecSql(pAr, "ROLLBACK TO ar; RELEASE ar;");
  }else{
    rc = arExecSql(pAr, "RELEASE ar;");
  }


  return rc;
}

/*
** Implementation of ".ar" dot command.
*/
static int arDotCommand(
5157
5158
5159
5160
5161
5162
5163

5164

5165
5166
5167
5168
5169
5170
5171
      }
      sqlite3_fileio_init(cmd.db, 0, 0);
#ifdef SQLITE_HAVE_ZLIB
      sqlite3_sqlar_init(cmd.db, 0, 0);
#endif
    }
    if( cmd.zSrcTable==0 ){

      if( sqlite3_table_column_metadata(cmd.db,0,"sqlar","name",0,0,0,0,0) ){

        utf8_printf(stderr, "database does not contain an 'sqlar' table\n");
        rc = SQLITE_ERROR;
        goto end_ar_command;
      }
      cmd.zSrcTable = sqlite3_mprintf("sqlar");
    }








>
|
>







5129
5130
5131
5132
5133
5134
5135
5136
5137
5138
5139
5140
5141
5142
5143
5144
5145
      }
      sqlite3_fileio_init(cmd.db, 0, 0);
#ifdef SQLITE_HAVE_ZLIB
      sqlite3_sqlar_init(cmd.db, 0, 0);
#endif
    }
    if( cmd.zSrcTable==0 ){
      if( cmd.eCmd!=AR_CMD_CREATE
       && sqlite3_table_column_metadata(cmd.db,0,"sqlar","name",0,0,0,0,0)
      ){
        utf8_printf(stderr, "database does not contain an 'sqlar' table\n");
        rc = SQLITE_ERROR;
        goto end_ar_command;
      }
      cmd.zSrcTable = sqlite3_mprintf("sqlar");
    }