1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-30 19:03:16 +03:00

Have the writefile() function optionally set the modification-time of the

files it writes or creates. And many small fixes to the new code on this
branch.

FossilOrigin-Name: 7b51269caebe1492885fe9b965892f49a3f8bdb1d666b0203d594c30f9e83938
This commit is contained in:
dan
2017-12-14 19:15:07 +00:00
parent 0d0547fec6
commit ac15e2d7cc
5 changed files with 177 additions and 83 deletions

View File

@ -11,21 +11,67 @@
****************************************************************************** ******************************************************************************
** **
** This SQLite extension implements SQL functions readfile() and ** This SQLite extension implements SQL functions readfile() and
** writefile(). ** writefile(), and eponymous virtual type "fsdir".
** **
** Also, an eponymous virtual table type "fsdir". Used as follows: ** WRITEFILE(FILE, DATA [, MODE [, MTIME]]):
** **
** SELECT * FROM fsdir($dirname); ** If neither of the optional arguments is present, then this UDF
** function writes blob DATA to file FILE. If successful, the number
** of bytes written is returned. If an error occurs, NULL is returned.
** **
** Returns one row for each entry in the directory $dirname. No row is ** If the first option argument - MODE - is present, then it must
** returned for "." or "..". Row columns are as follows: ** be passed an integer value that corresponds to a POSIX mode
** value (file type + permissions, as returned in the stat.st_mode
** field by the stat() system call). Three types of files may
** be written/created:
** **
** name: Name of directory entry. ** regular files: (mode & 0170000)==0100000
** mode: Value of stat.st_mode for directory entry. ** symbolic links: (mode & 0170000)==0120000
** mtime: Value of stat.st_mtime for directory entry. ** directories: (mode & 0170000)==0040000
** 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 ** For a directory, the DATA is ignored. For a symbolic link, it is
** directory, NULL. ** interpreted as text and used as the target of the link. For a
** regular file, it is interpreted as a blob and written into the
** named file. Regardless of the type of file, its permissions are
** set to (mode & 0777) before returning.
**
** If the optional MTIME argument is present, then it is interpreted
** as an integer - the number of seconds since the unix epoch. The
** modification-time of the target file is set to this value before
** returning.
**
** If three or more arguments are passed to this function and an
** error is encountered, an exception is raised.
**
** READFILE(FILE):
**
** Read and return the contents of file FILE (type blob) from disk.
**
** FSDIR:
**
** Used as follows:
**
** SELECT * FROM fsdir($path [, $dir]);
**
** Parameter $path is an absolute or relative pathname. If the file that it
** refers to does not exist, it is an error. If the path refers to a regular
** file or symbolic link, it returns a single row. Or, if the path refers
** to a directory, it returns one row for the directory, and one row for each
** file within the hierarchy rooted at $path.
**
** Each row has the following columns:
**
** name: Path to file or directory (text value).
** mode: Value of stat.st_mode for directory entry (an integer).
** mtime: Value of stat.st_mtime for directory entry (an integer).
** 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.
**
** If a non-NULL value is specified for the optional $dir parameter and
** $path is a relative path, then $path is interpreted relative to $dir.
** And the paths returned in the "name" column of the table are also
** relative to directory $dir.
*/ */
#include "sqlite3ext.h" #include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1 SQLITE_EXTENSION_INIT1
@ -45,6 +91,10 @@ SQLITE_EXTENSION_INIT1
#define FSDIR_SCHEMA "(name,mode,mtime,data,path HIDDEN,dir HIDDEN)" #define FSDIR_SCHEMA "(name,mode,mtime,data,path HIDDEN,dir HIDDEN)"
/*
** Set the result stored by context ctx to a blob containing the
** contents of file zName.
*/
static void readFileContents(sqlite3_context *ctx, const char *zName){ static void readFileContents(sqlite3_context *ctx, const char *zName){
FILE *in; FILE *in;
long nIn; long nIn;
@ -81,6 +131,10 @@ static void readfileFunc(
readFileContents(context, zName); readFileContents(context, zName);
} }
/*
** Set the error message contained in context ctx to the results of
** vprintf(zFmt, ...).
*/
static void ctxErrorMsg(sqlite3_context *ctx, const char *zFmt, ...){ static void ctxErrorMsg(sqlite3_context *ctx, const char *zFmt, ...){
char *zMsg = 0; char *zMsg = 0;
va_list ap; va_list ap;
@ -138,11 +192,16 @@ static int makeDirectory(
return rc; return rc;
} }
/*
** This function does the work for the writefile() UDF. Refer to
** header comments at the top of this file for details.
*/
static int writeFile( static int writeFile(
sqlite3_context *pCtx, sqlite3_context *pCtx, /* Context to return bytes written in */
const char *zFile, const char *zFile, /* File to write */
mode_t mode, sqlite3_value *pData, /* Data to write */
sqlite3_value *pData mode_t mode, /* MODE parameter passed to writefile() */
sqlite3_int64 mtime /* MTIME parameter (or -1 to not set time) */
){ ){
if( S_ISLNK(mode) ){ if( S_ISLNK(mode) ){
const char *zTo = (const char*)sqlite3_value_text(pData); const char *zTo = (const char*)sqlite3_value_text(pData);
@ -178,22 +237,30 @@ static int writeFile(
} }
} }
fclose(out); fclose(out);
if( rc==0 && chmod(zFile, mode & 0777) ){ if( rc==0 && mode && chmod(zFile, mode & 0777) ){
rc = 1; rc = 1;
} }
if( rc ) return 2; if( rc ) return 2;
sqlite3_result_int64(pCtx, nWrite); sqlite3_result_int64(pCtx, nWrite);
} }
} }
if( mtime>=0 ){
struct timespec times[2];
times[0].tv_nsec = times[1].tv_nsec = 0;
times[0].tv_sec = time(0);
times[1].tv_sec = mtime;
if( utimensat(AT_FDCWD, zFile, times, AT_SYMLINK_NOFOLLOW) ){
return 1;
}
}
return 0; return 0;
} }
/* /*
** Implementation of the "writefile(W,X[,Y]])" SQL function. ** Implementation of the "writefile(W,X[,Y[,Z]]])" SQL function.
** ** Refer to header comments at the top of this file for details.
** 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( static void writefileFunc(
sqlite3_context *context, sqlite3_context *context,
@ -203,8 +270,9 @@ static void writefileFunc(
const char *zFile; const char *zFile;
mode_t mode = 0; mode_t mode = 0;
int res; int res;
sqlite3_int64 mtime = -1;
if( argc<2 || argc>3 ){ if( argc<2 || argc>4 ){
sqlite3_result_error(context, sqlite3_result_error(context,
"wrong number of arguments to function writefile()", -1 "wrong number of arguments to function writefile()", -1
); );
@ -214,18 +282,20 @@ static void writefileFunc(
zFile = (const char*)sqlite3_value_text(argv[0]); zFile = (const char*)sqlite3_value_text(argv[0]);
if( zFile==0 ) return; if( zFile==0 ) return;
if( argc>=3 ){ if( argc>=3 ){
sqlite3_result_int(context, 0);
mode = sqlite3_value_int(argv[2]); mode = sqlite3_value_int(argv[2]);
} }
if( argc==4 ){
mtime = sqlite3_value_int64(argv[3]);
}
res = writeFile(context, zFile, mode, argv[1]); res = writeFile(context, zFile, argv[1], mode, mtime);
if( res==1 && errno==ENOENT ){ if( res==1 && errno==ENOENT ){
if( makeDirectory(zFile, mode)==SQLITE_OK ){ if( makeDirectory(zFile, mode)==SQLITE_OK ){
res = writeFile(context, zFile, mode, argv[1]); res = writeFile(context, zFile, argv[1], mode, mtime);
} }
} }
if( res!=0 ){ if( argc>2 && res!=0 ){
if( S_ISLNK(mode) ){ if( S_ISLNK(mode) ){
ctxErrorMsg(context, "failed to create symlink: %s", zFile); ctxErrorMsg(context, "failed to create symlink: %s", zFile);
}else if( S_ISDIR(mode) ){ }else if( S_ISDIR(mode) ){
@ -313,6 +383,10 @@ static int fsdirOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
return SQLITE_OK; return SQLITE_OK;
} }
/*
** Reset a cursor back to the state it was in when first returned
** by fsdirOpen().
*/
static void fsdirResetCursor(fsdir_cursor *pCur){ static void fsdirResetCursor(fsdir_cursor *pCur){
int i; int i;
for(i=0; i<=pCur->iLvl; i++){ for(i=0; i<=pCur->iLvl; i++){
@ -341,6 +415,10 @@ static int fsdirClose(sqlite3_vtab_cursor *cur){
return SQLITE_OK; return SQLITE_OK;
} }
/*
** Set the error message for the virtual table associated with cursor
** pCur to the results of vprintf(zFmt, ...).
*/
static void fsdirSetErrmsg(fsdir_cursor *pCur, const char *zFmt, ...){ static void fsdirSetErrmsg(fsdir_cursor *pCur, const char *zFmt, ...){
va_list ap; va_list ap;
va_start(ap, zFmt); va_start(ap, zFmt);
@ -586,6 +664,9 @@ static int fsdirBestIndex(
return SQLITE_OK; return SQLITE_OK;
} }
/*
** Register the "fsdir" virtual table.
*/
static int fsdirRegister(sqlite3 *db){ static int fsdirRegister(sqlite3 *db){
static sqlite3_module fsdirModule = { static sqlite3_module fsdirModule = {
0, /* iVersion */ 0, /* iVersion */
@ -611,9 +692,6 @@ static int fsdirRegister(sqlite3 *db){
}; };
int rc = sqlite3_create_module(db, "fsdir", &fsdirModule, 0); int rc = sqlite3_create_module(db, "fsdir", &fsdirModule, 0);
if( rc==SQLITE_OK ){
rc = sqlite3_create_module(db, "fsentry", &fsdirModule, (void*)1);
}
return rc; return rc;
} }
#else /* SQLITE_OMIT_VIRTUALTABLE */ #else /* SQLITE_OMIT_VIRTUALTABLE */

View File

@ -1,5 +1,5 @@
C Improve\serror\sand\susage\smessages\soutput\sby\sthe\sshell\s".ar"\scommand. C Have\sthe\swritefile()\sfunction\soptionally\sset\sthe\smodification-time\sof\sthe\nfiles\sit\swrites\sor\screates.\sAnd\smany\ssmall\sfixes\sto\sthe\snew\scode\son\sthis\nbranch.
D 2017-12-14T15:40:42.931 D 2017-12-14T19:15:07.381
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F Makefile.in 6a879cbf01e37f9eac131414955f71774b566502d9a57ded1b8585b507503cb8 F Makefile.in 6a879cbf01e37f9eac131414955f71774b566502d9a57ded1b8585b507503cb8
@ -270,7 +270,7 @@ F ext/misc/compress.c 122faa92d25033d6c3f07c39231de074ab3d2e83
F ext/misc/csv.c 1a009b93650732e22334edc92459c4630b9fa703397cbb3c8ca279921a36ca11 F ext/misc/csv.c 1a009b93650732e22334edc92459c4630b9fa703397cbb3c8ca279921a36ca11
F ext/misc/dbdump.c 3509fa6b8932d04e932d6b6b827b6a82ca362781b8e8f3c77336f416793e215e F ext/misc/dbdump.c 3509fa6b8932d04e932d6b6b827b6a82ca362781b8e8f3c77336f416793e215e
F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2 F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2
F ext/misc/fileio.c 29b7fc94752fff6245cf4a81455f98cf6778ec1102ca7e67bf693d41a7db4307 F ext/misc/fileio.c 014152d4133e7b29eab8eb39d0c640659c23a6d23d882b4778f487ae7d1a457b
F ext/misc/fuzzer.c 7c64b8197bb77b7d64eff7cac7848870235d4c25 F ext/misc/fuzzer.c 7c64b8197bb77b7d64eff7cac7848870235d4c25
F ext/misc/ieee754.c f190d0cc5182529acb15babd177781be1ac1718c F ext/misc/ieee754.c f190d0cc5182529acb15babd177781be1ac1718c
F ext/misc/json1.c dbe086615b9546c156bf32b9378fc09383b58bd17513b866cfd24c1e15281984 F ext/misc/json1.c dbe086615b9546c156bf32b9378fc09383b58bd17513b866cfd24c1e15281984
@ -475,7 +475,7 @@ F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
F src/resolve.c bbee7e31d369a18a2f4836644769882e9c5d40ef4a3af911db06410b65cb3730 F src/resolve.c bbee7e31d369a18a2f4836644769882e9c5d40ef4a3af911db06410b65cb3730
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
F src/select.c 17e220191860a64a18c084141e1a8b7309e166a6f2d42c02021af27ea080d157 F src/select.c 17e220191860a64a18c084141e1a8b7309e166a6f2d42c02021af27ea080d157
F src/shell.c.in 4bdd2efe722005180365698f2a3de51e22ae1e9bb61c868006bc8f2c5e02eb98 F src/shell.c.in 074b2129559a0aa712a367317f7e7daf4740925ec2c123b529800628eb10dc73
F src/sqlite.h.in 364515dd186285f3c01f5cab42e7db7edc47c70e87b6a25de389a2e6b8c413fd F src/sqlite.h.in 364515dd186285f3c01f5cab42e7db7edc47c70e87b6a25de389a2e6b8c413fd
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h c02d628cca67f3889c689d82d25c3eb45e2c155db08e4c6089b5840d64687d34 F src/sqlite3ext.h c02d628cca67f3889c689d82d25c3eb45e2c155db08e4c6089b5840d64687d34
@ -1215,7 +1215,7 @@ F test/shell4.test 89ad573879a745974ff2df20ff97c5d6ffffbd5d
F test/shell5.test 23939a4c51f0421330ea61dbd3c74f9c215f5f8d3d1a94846da6ffc777a35458 F test/shell5.test 23939a4c51f0421330ea61dbd3c74f9c215f5f8d3d1a94846da6ffc777a35458
F test/shell6.test 1ceb51b2678c472ba6cf1e5da96679ce8347889fe2c3bf93a0e0fa73f00b00d3 F test/shell6.test 1ceb51b2678c472ba6cf1e5da96679ce8347889fe2c3bf93a0e0fa73f00b00d3
F test/shell7.test 115132f66d0463417f408562cc2cf534f6bbc6d83a6d50f0072a9eb171bae97f F test/shell7.test 115132f66d0463417f408562cc2cf534f6bbc6d83a6d50f0072a9eb171bae97f
F test/shell8.test 0f7dfc5b33bde7143df8e37cbb4ae6ccc7e91f87232dc8e5e02be03117cdebb8 F test/shell8.test 96f35965fe84d633fb2338696f5cbc1bcf6bdbdd79677244bc617a8452851dc7
F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3 F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3
F test/show_speedtest1_rtree.tcl 32e6c5f073d7426148a6936a0408f4b5b169aba5 F test/show_speedtest1_rtree.tcl 32e6c5f073d7426148a6936a0408f4b5b169aba5
F test/shrink.test 1b4330b1fd9e818c04726d45cb28db73087535ce F test/shrink.test 1b4330b1fd9e818c04726d45cb28db73087535ce
@ -1683,7 +1683,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 803156cba8b056a1cb8d1bb186a57454afe72341abe7de1dfe529234c3415cd2 P b9d2d5d97291bf3d1392232e3705cca89dc7b918db2b08067b2b013ea39320e0
R 69895ee1194373ab4ba11578d798fe26 R 975f9981c88e18e3ddfa1fe4bb6e7fae
U dan U dan
Z 5ea750b0cf239f9442948a3eda934166 Z 1e84576de6ef44efce8abf1d868c1119

View File

@ -1 +1 @@
b9d2d5d97291bf3d1392232e3705cca89dc7b918db2b08067b2b013ea39320e0 7b51269caebe1492885fe9b965892f49a3f8bdb1d666b0203d594c30f9e83938

View File

@ -4092,23 +4092,25 @@ static void shellPrepare(
} }
} }
static void shellPrepare2( static void shellPreparePrintf(
sqlite3 *db, sqlite3 *db,
int *pRc, int *pRc,
const char *zSql, sqlite3_stmt **ppStmt,
const char *zTail, const char *zFmt,
sqlite3_stmt **ppStmt ...
){ ){
if( *pRc==SQLITE_OK && zTail ){ *ppStmt = 0;
char *z = sqlite3_mprintf("%s %s", zSql, zTail); if( *pRc==SQLITE_OK ){
va_list ap;
char *z;
va_start(ap, zFmt);
z = sqlite3_vmprintf(zFmt, ap);
if( z==0 ){ if( z==0 ){
*pRc = SQLITE_NOMEM; *pRc = SQLITE_NOMEM;
}else{ }else{
shellPrepare(db, pRc, z, ppStmt); shellPrepare(db, pRc, z, ppStmt);
sqlite3_free(z); sqlite3_free(z);
} }
}else{
shellPrepare(db, pRc, zSql, ppStmt);
} }
} }
@ -4429,23 +4431,27 @@ static int arCheckEntries(sqlite3 *db, ArCommand *pAr){
static void arWhereClause( static void arWhereClause(
int *pRc, int *pRc,
ArCommand *pAr, ArCommand *pAr,
char **pzWhere /* OUT: New WHERE clause (or NULL) */ char **pzWhere /* OUT: New WHERE clause */
){ ){
char *zWhere = 0; char *zWhere = 0;
if( *pRc==SQLITE_OK ){ if( *pRc==SQLITE_OK ){
int i; if( pAr->nArg==0 ){
const char *zSep = "WHERE "; zWhere = sqlite3_mprintf("1");
for(i=0; i<pAr->nArg; i++){ }else{
const char *z = pAr->azArg[i]; int i;
zWhere = sqlite3_mprintf( const char *zSep = "";
"%z%s name = '%q' OR name BETWEEN '%q/' AND '%q0'", for(i=0; i<pAr->nArg; i++){
zWhere, zSep, z, z, z const char *z = pAr->azArg[i];
); zWhere = sqlite3_mprintf(
if( zWhere==0 ){ "%z%s name = '%q' OR name BETWEEN '%q/' AND '%q0'",
*pRc = SQLITE_NOMEM; zWhere, zSep, z, z, z
break; );
if( zWhere==0 ){
*pRc = SQLITE_NOMEM;
break;
}
zSep = " OR ";
} }
zSep = " OR ";
} }
} }
*pzWhere = zWhere; *pzWhere = zWhere;
@ -4455,7 +4461,7 @@ static void arWhereClause(
** Implementation of .ar "lisT" command. ** Implementation of .ar "lisT" command.
*/ */
static int arListCommand(ShellState *p, sqlite3 *db, ArCommand *pAr){ static int arListCommand(ShellState *p, sqlite3 *db, ArCommand *pAr){
const char *zSql = "SELECT name FROM sqlar"; const char *zSql = "SELECT name FROM sqlar WHERE %s";
char *zWhere = 0; char *zWhere = 0;
sqlite3_stmt *pSql = 0; sqlite3_stmt *pSql = 0;
int rc; int rc;
@ -4463,7 +4469,7 @@ static int arListCommand(ShellState *p, sqlite3 *db, ArCommand *pAr){
rc = arCheckEntries(db, pAr); rc = arCheckEntries(db, pAr);
arWhereClause(&rc, pAr, &zWhere); arWhereClause(&rc, pAr, &zWhere);
shellPrepare2(db, &rc, zSql, zWhere, &pSql); shellPreparePrintf(db, &rc, &pSql, zSql, zWhere);
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
raw_printf(p->out, "%s\n", sqlite3_column_text(pSql, 0)); raw_printf(p->out, "%s\n", sqlite3_column_text(pSql, 0));
} }
@ -4482,14 +4488,14 @@ static int arExtractCommand(ShellState *p, sqlite3 *db, ArCommand *pAr){
"ELSE" "ELSE"
" data " " data "
"END, " "END, "
"mode) FROM sqlar"; "mode, mtime) FROM sqlar WHERE (%s) AND (data IS NULL OR :2 = 0)";
const char *zSql2 = "SELECT :1 || name, mtime FROM sqlar";
struct timespec times[2]; struct timespec times[2];
sqlite3_stmt *pSql = 0; sqlite3_stmt *pSql = 0;
int rc = SQLITE_OK; int rc = SQLITE_OK;
char *zDir = 0; char *zDir = 0;
char *zWhere = 0; char *zWhere = 0;
int i;
/* If arguments are specified, check that they actually exist within /* If arguments are specified, check that they actually exist within
** the archive before proceeding. And formulate a WHERE clause to ** the archive before proceeding. And formulate a WHERE clause to
@ -4509,31 +4515,26 @@ static int arExtractCommand(ShellState *p, sqlite3 *db, ArCommand *pAr){
memset(times, 0, sizeof(times)); memset(times, 0, sizeof(times));
times[0].tv_sec = time(0); times[0].tv_sec = time(0);
shellPrepare2(db, &rc, zSql1, zWhere, &pSql); shellPreparePrintf(db, &rc, &pSql, zSql1, zWhere);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
sqlite3_bind_text(pSql, 1, zDir, -1, SQLITE_STATIC); sqlite3_bind_text(pSql, 1, zDir, -1, SQLITE_STATIC);
}
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
if( pAr->bVerbose ){
raw_printf(p->out, "%s\n", sqlite3_column_text(pSql, 0));
}
}
shellFinalize(&rc, pSql);
shellPrepare2(db, &rc, zSql2, zWhere, &pSql); /* Run the SELECT statement twice. The first time, writefile() is called
if( rc==SQLITE_OK ){ ** for all archive members that should be extracted. The second time,
sqlite3_bind_text(pSql, 1, zDir, -1, SQLITE_STATIC); ** only for the directories. This is because the timestamps for
} ** extracted directories must be reset after they are populated (as
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ ** populating them changes the timestamp). */
const char *zPath = (const char*)sqlite3_column_text(pSql, 0); for(i=0; i<2; i++){
times[1].tv_sec = (time_t)sqlite3_column_int64(pSql, 1); sqlite3_bind_int(pSql, 2, i);
if( utimensat(AT_FDCWD, zPath, times, AT_SYMLINK_NOFOLLOW) ){ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
raw_printf(stderr, "failed to set timestamp for %s\n", zPath); if( i==0 && pAr->bVerbose ){
rc = SQLITE_ERROR; raw_printf(p->out, "%s\n", sqlite3_column_text(pSql, 0));
break; }
}
shellReset(&rc, pSql);
} }
shellFinalize(&rc, pSql);
} }
shellFinalize(&rc, pSql);
sqlite3_free(zDir); sqlite3_free(zDir);
sqlite3_free(zWhere); sqlite3_free(zWhere);

View File

@ -139,6 +139,21 @@ foreach {tn tcl} {
catchcmd ":memory:" $x3 catchcmd ":memory:" $x3
dir_to_list ar3 dir_to_list ar3
} $expected } $expected
# This is a repeat of test 1.$tn.1, except that there is a 2 second
# pause between creating the archive and extracting its contents.
# This is to test that timestamps are set correctly.
#
# Because it is slow, only do this for $tn==1.
if {$tn==1} {
do_test 1.$tn.1 {
catchcmd test_ar.db $c1
file delete -force ar1
after 2000
catchcmd test_ar.db $x1
dir_to_list ar1
} $expected
}
} }
finish_test finish_test