Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Enhance virtual table "fsdir" in ext/misc/fileio.c. Add support for "-C" to the shell command's ".ar c" command. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | sqlar-shell-support |
Files: | files | file ages | folders |
SHA3-256: |
0394889afed2479773af594e2d9659cf |
User & Date: | dan 2017-12-11 20:22:02.045 |
Context
2017-12-12
| ||
20:04 | Add support for parsing options in non-traditional tar form to the ".ar" command. Have writefile() attempt to create any missing path components. And not to throw an exception if it is called to create a directory that already exists. (check-in: 38dbeb1e77 user: dan tags: sqlar-shell-support) | |
2017-12-11
| ||
20:22 | Enhance virtual table "fsdir" in ext/misc/fileio.c. Add support for "-C" to the shell command's ".ar c" command. (check-in: 0394889afe user: dan tags: sqlar-shell-support) | |
2017-12-09
| ||
18:28 | Add support for -C to ".ar x". (check-in: 8cd70960c5 user: dan tags: sqlar-shell-support) | |
Changes
Changes to ext/misc/fileio.c.
︙ | ︙ | |||
38 39 40 41 42 43 44 | #include <fcntl.h> #include <unistd.h> #include <dirent.h> #include <time.h> #include <utime.h> | | | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | #include <fcntl.h> #include <unistd.h> #include <dirent.h> #include <time.h> #include <utime.h> #define FSDIR_SCHEMA "(name,mode,mtime,data,path HIDDEN,dir HIDDEN)" static void readFileContents(sqlite3_context *ctx, const char *zName){ FILE *in; long nIn; void *pBuf; in = fopen(zName, "rb"); |
︙ | ︙ | |||
162 163 164 165 166 167 168 169 170 171 172 | } } } #ifndef SQLITE_OMIT_VIRTUALTABLE /* */ typedef struct fsdir_cursor fsdir_cursor; struct fsdir_cursor { sqlite3_vtab_cursor base; /* Base class - must be first */ | > > > > > > > > | > | > > > > > < < < < < < < | < | 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | } } } #ifndef SQLITE_OMIT_VIRTUALTABLE /* ** Cursor type for recursively iterating through a directory structure. */ typedef struct fsdir_cursor fsdir_cursor; typedef struct FsdirLevel FsdirLevel; struct FsdirLevel { DIR *pDir; /* From opendir() */ char *zDir; /* Name of directory (nul-terminated) */ }; struct fsdir_cursor { sqlite3_vtab_cursor base; /* Base class - must be first */ int nLvl; /* Number of entries in aLvl[] array */ int iLvl; /* Index of current entry */ FsdirLevel *aLvl; /* Hierarchy of directories being traversed */ const char *zBase; int nBase; struct stat sStat; /* Current lstat() results */ char *zPath; /* Path to current entry */ sqlite3_int64 iRowid; /* Current rowid */ }; typedef struct fsdir_tab fsdir_tab; struct fsdir_tab { sqlite3_vtab base; /* Base class - must be first */ }; /* ** 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, "CREATE TABLE x" FSDIR_SCHEMA); if( rc==SQLITE_OK ){ pNew = (fsdir_tab*)sqlite3_malloc( sizeof(*pNew) ); if( pNew==0 ) return SQLITE_NOMEM; memset(pNew, 0, sizeof(*pNew)); } *ppVtab = (sqlite3_vtab*)pNew; return rc; } /* ** This method is the destructor for fsdir vtab objects. |
︙ | ︙ | |||
225 226 227 228 229 230 231 | ** 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)); | | > > > > > > > > > > > > > > > > | | | > > > > > > > > < < < < < < < < < < < < < < < < < < | < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < | | | | 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 | ** 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->iLvl = -1; *ppCursor = &pCur->base; return SQLITE_OK; } static void fsdirResetCursor(fsdir_cursor *pCur){ int i; for(i=0; i<=pCur->iLvl; i++){ FsdirLevel *pLvl = &pCur->aLvl[i]; if( pLvl->pDir ) closedir(pLvl->pDir); sqlite3_free(pLvl->zDir); } sqlite3_free(pCur->zPath); pCur->aLvl = 0; pCur->zPath = 0; pCur->zBase = 0; pCur->nBase = 0; pCur->iLvl = -1; pCur->iRowid = 1; } /* ** Destructor for an fsdir_cursor. */ static int fsdirClose(sqlite3_vtab_cursor *cur){ fsdir_cursor *pCur = (fsdir_cursor*)cur; fsdirResetCursor(pCur); sqlite3_free(pCur->aLvl); sqlite3_free(pCur); return SQLITE_OK; } 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); } /* ** Advance an fsdir_cursor to its next row of output. */ static int fsdirNext(sqlite3_vtab_cursor *cur){ fsdir_cursor *pCur = (fsdir_cursor*)cur; mode_t m = pCur->sStat.st_mode; pCur->iRowid++; if( S_ISDIR(m) ){ /* Descend into this directory */ int iNew = pCur->iLvl + 1; FsdirLevel *pLvl; if( iNew>=pCur->nLvl ){ int nNew = iNew+1; int nByte = nNew*sizeof(FsdirLevel); FsdirLevel *aNew = (FsdirLevel*)sqlite3_realloc(pCur->aLvl, nByte); if( aNew==0 ) return SQLITE_NOMEM; memset(&aNew[pCur->nLvl], 0, sizeof(FsdirLevel)*(nNew-pCur->nLvl)); pCur->aLvl = aNew; pCur->nLvl = nNew; } pCur->iLvl = iNew; pLvl = &pCur->aLvl[iNew]; pLvl->zDir = pCur->zPath; pCur->zPath = 0; pLvl->pDir = opendir(pLvl->zDir); if( pLvl->pDir==0 ){ fsdirSetErrmsg(pCur, "cannot read directory: %s", pCur->zPath); return SQLITE_ERROR; } } while( pCur->iLvl>=0 ){ FsdirLevel *pLvl = &pCur->aLvl[pCur->iLvl]; struct dirent *pEntry = readdir(pLvl->pDir); if( pEntry ){ if( pEntry->d_name[0]=='.' ){ if( pEntry->d_name[1]=='.' && pEntry->d_name[2]=='\0' ) continue; if( pEntry->d_name[1]=='\0' ) continue; } sqlite3_free(pCur->zPath); pCur->zPath = sqlite3_mprintf("%s/%s", pLvl->zDir, pEntry->d_name); if( pCur->zPath==0 ) return SQLITE_NOMEM; if( lstat(pCur->zPath, &pCur->sStat) ){ fsdirSetErrmsg(pCur, "cannot stat file: %s", pCur->zPath); return SQLITE_ERROR; } return SQLITE_OK; } closedir(pLvl->pDir); sqlite3_free(pLvl->zDir); pLvl->pDir = 0; pLvl->zDir = 0; pCur->iLvl--; } /* EOF */ sqlite3_free(pCur->zPath); pCur->zPath = 0; 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 */ sqlite3_result_text(ctx, &pCur->zPath[pCur->nBase], -1, SQLITE_TRANSIENT); break; } case 1: /* mode */ sqlite3_result_int64(ctx, pCur->sStat.st_mode); break; case 2: /* mtime */ sqlite3_result_int64(ctx, pCur->sStat.st_mtime); break; case 3: { /* data */ 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; |
︙ | ︙ | |||
357 358 359 360 361 362 363 | /* ** 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; | | < < < < < < < < < < < < | < < | | < | < < | | < | | | > > | > | < < < < | | | | | < | 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 | /* ** 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->zPath==0); } /* ** 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; fsdirResetCursor(pCur); if( idxNum==0 ){ fsdirSetErrmsg(pCur, "table function fsdir requires an argument"); return SQLITE_ERROR; } assert( argc==idxNum && (argc==1 || argc==2) ); zDir = (const char*)sqlite3_value_text(argv[0]); if( zDir==0 ){ fsdirSetErrmsg(pCur, "table function fsdir requires a non-NULL argument"); return SQLITE_ERROR; } if( argc==2 ){ pCur->zBase = (const char*)sqlite3_value_text(argv[1]); } if( pCur->zBase ){ pCur->nBase = strlen(pCur->zBase)+1; pCur->zPath = sqlite3_mprintf("%s/%s", pCur->zBase, zDir); }else{ pCur->zPath = sqlite3_mprintf("%s", zDir); } if( pCur->zPath==0 ){ return SQLITE_NOMEM; } if( lstat(pCur->zPath, &pCur->sStat) ){ fsdirSetErrmsg(pCur, "cannot stat file: %s", pCur->zPath); return SQLITE_ERROR; } return SQLITE_OK; } /* ** 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. |
︙ | ︙ | |||
446 447 448 449 450 451 452 453 454 455 456 457 458 | ** (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; | > > | | > | > > | > > | > | | | | | > | 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 | ** (8) output in descending order */ static int fsdirBestIndex( sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo ){ int i; /* Loop over constraints */ int idx4 = -1; int idx5 = -1; 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 ) idx4 = i; if( pConstraint->iColumn==5 ) idx5 = i; } if( idx4<0 ){ pIdxInfo->idxNum = 0; pIdxInfo->estimatedCost = (double)(((sqlite3_int64)1) << 50); }else{ pIdxInfo->aConstraintUsage[idx4].omit = 1; pIdxInfo->aConstraintUsage[idx4].argvIndex = 1; if( idx5>=0 ){ pIdxInfo->aConstraintUsage[idx5].omit = 1; pIdxInfo->aConstraintUsage[idx5].argvIndex = 2; pIdxInfo->idxNum = 2; pIdxInfo->estimatedCost = 10.0; }else{ pIdxInfo->idxNum = 1; pIdxInfo->estimatedCost = 100.0; } } return SQLITE_OK; } static int fsdirRegister(sqlite3 *db){ static sqlite3_module fsdirModule = { |
︙ | ︙ |
Changes to src/shell.c.in.
︙ | ︙ | |||
4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 | } } shellFinalize(&rc, pSql); sqlite3_free(zDir); return rc; } /* ** Implementation of .ar "Create" command. ** ** Create the "sqlar" table in the database if it does not already exist. ** Then add each file in the azFile[] array to the archive. Directories ** are added recursively. If argument bVerbose is non-zero, a message is ** printed on stdout for each file archived. */ static int arCreateCommand( ShellState *p, /* Shell state pointer */ ArCommand *pAr /* Command arguments and options */ ){ const char *zSql = | > < | < < < | > < | 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 | } } shellFinalize(&rc, pSql); sqlite3_free(zDir); return rc; } /* ** Implementation of .ar "Create" command. ** ** Create the "sqlar" table in the database if it does not already exist. ** Then add each file in the azFile[] array to the archive. Directories ** are added recursively. If argument bVerbose is non-zero, a message is ** printed on stdout for each file archived. */ static int arCreateCommand( ShellState *p, /* Shell state pointer */ ArCommand *pAr /* Command arguments and options */ ){ const char *zSql = "SELECT name, mode, mtime, data FROM fsdir(?, ?)"; const char *zSqlar = "CREATE TABLE IF NOT EXISTS sqlar(" "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 *zInsert = "REPLACE INTO sqlar VALUES(?, ?, ?, ?, ?)"; sqlite3_stmt *pStmt = 0; /* Directory traverser */ sqlite3_stmt *pInsert = 0; /* Compilation of zInsert */ int i; /* For iterating through azFile[] */ int rc; /* Return code */ Bytef *aCompress = 0; /* Compression buffer */ int nCompress = 0; /* Size of compression buffer */ rc = sqlite3_exec(p->db, "SAVEPOINT ar;", 0, 0, 0); if( rc!=SQLITE_OK ) return rc; rc = sqlite3_exec(p->db, zSqlar, 0, 0, 0); shellPrepare(p, &rc, zInsert, &pInsert); shellPrepare(p, &rc, zSql, &pStmt); sqlite3_bind_text(pStmt, 2, pAr->zDir, -1, SQLITE_STATIC); for(i=0; i<pAr->nArg && rc==SQLITE_OK; i++){ sqlite3_bind_text(pStmt, 1, 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 ){ |
︙ | ︙ |
Changes to test/shell8.test.
︙ | ︙ | |||
82 83 84 85 86 87 88 89 90 91 92 | do_test 1.2 { file delete -force ar3 file mkdir ar3 catchcmd test_ar.db ".ar xvC ar3" dir_to_list ar3/ar1 } $expected finish_test | > > > > > > > > > > | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | do_test 1.2 { file delete -force ar3 file mkdir ar3 catchcmd test_ar.db ".ar xvC ar3" dir_to_list ar3/ar1 } $expected do_test 1.3 { file delete -force ar3 file mkdir ar3 forcedelete test_ar.db catchcmd test_ar.db ".ar cC ar1 file1 file2 dir1" catchcmd test_ar.db ".ar xC ar3" dir_to_list ar3 } $expected finish_test |