1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2024-12-12 10:04:14 +02:00

Replace strftime() with cvtTimeToZP() and strNew/CatTimeP().

These functions produce cleaner code and hide implementation details.
This commit is contained in:
David Steele 2024-06-19 10:33:46 +08:00
parent cec486b6dd
commit a0a5f2300c
16 changed files with 154 additions and 76 deletions

View File

@ -211,6 +211,12 @@ configuration.set(
description: 'Indicate that a function is formatted like printf (and provide format and args position)'
)
# Set FN_STRFTIME macro
configuration.set(
'FN_STRFTIME(fmt)', '__attribute__((format(strftime, fmt, 0)))',
description: 'Indicate that a function is formatted like strftime (and provide format position)'
)
####################################################################################################################################
# Include subdirs
####################################################################################################################################

View File

@ -44,6 +44,9 @@ Build Flags Generated by Configure
// Indicate that a function is formatted like printf (and provide format and args position)
#define FN_PRINTF(fmt, args) __attribute__((format(printf, fmt, args)))
// Indicate that a function is formatted like strftime (and provide format position)
#define FN_STRFTIME(fmt) __attribute__((format(strftime, fmt, 0)))
// Extern function/variable required by other compilation units. Changing these to static is a meson-only feature.
#define FN_EXTERN extern
#define VR_EXTERN_DECLARE extern

View File

@ -58,12 +58,8 @@ backupLabelFormat(const BackupType type, const String *const backupLabelPrior, c
ASSERT(timestamp > 0);
// Format the timestamp
struct tm timePart;
char buffer[DATE_TIME_LEN + 1];
THROW_ON_SYS_ERROR(
strftime(buffer, sizeof(buffer), "%Y%m%d-%H%M%S", localtime_r(&timestamp, &timePart)) == 0, AssertError,
"unable to format time");
cvtTimeToZP("%Y%m%d-%H%M%S", timestamp, buffer, sizeof(buffer));
// If full label
String *result;

View File

@ -471,18 +471,13 @@ cmdManifestRender(void)
strCatFmt(result, "reference: %s\n", strZ(strLstJoin(manifestReferenceList(manifest), ", ")));
strCatFmt(result, "type: %s\n", strZ(strIdToStr(data->backupType)));
struct tm timePart;
char timeBufferStart[20];
char timeBufferStop[20];
int64_t duration = (int64_t)(data->backupTimestampStop - data->backupTimestampStart);
strftime(
timeBufferStart, sizeof(timeBufferStart), "%Y-%m-%d %H:%M:%S", localtime_r(&data->backupTimestampStart, &timePart));
strftime(
timeBufferStop, sizeof(timeBufferStop), "%Y-%m-%d %H:%M:%S", localtime_r(&data->backupTimestampStop, &timePart));
strCatFmt(
result, "time: start: %s, stop: %s, duration: %" PRId64 ":%02" PRId64 ":%02" PRId64 "\n", timeBufferStart,
timeBufferStop, duration / 3600, duration % 3600 / 60, duration % 60);
result, "time: start: %s, stop: %s, duration: %" PRId64 ":%02" PRId64 ":%02" PRId64 "\n",
strZ(strNewTimeP("%Y-%m-%d %H:%M:%S", data->backupTimestampStart)),
strZ(strNewTimeP("%Y-%m-%d %H:%M:%S", data->backupTimestampStop)), duration / 3600, duration % 3600 / 60,
duration % 60);
strCatFmt(result, "bundle: %s\n", cvtBoolToConstZ(data->bundle));
strCatFmt(result, "block: %s\n", cvtBoolToConstZ(data->blockIncr));

View File

@ -1926,12 +1926,7 @@ restoreRecoveryWrite(const Manifest *const manifest, const StorageInfo *const fi
else
{
// Generate a label used to identify this restore in the recovery file
struct tm timePart;
char restoreTimestamp[20];
const time_t timestamp = time(NULL);
strftime(restoreTimestamp, sizeof(restoreTimestamp), "%Y-%m-%d %H:%M:%S", localtime_r(&timestamp, &timePart));
const String *const restoreLabel = STR(restoreTimestamp);
const String *const restoreLabel = strNewTimeP("%Y-%m-%d %H:%M:%S", time(NULL));
// Write recovery file based on PostgreSQL version
if (pgVersion >= PG_VERSION_RECOVERY_GUC)
@ -2494,12 +2489,8 @@ cmdRestore(void)
if (manifestData(jobData.manifest)->backupOptionOnline)
{
struct tm timePart;
char timeBuffer[20];
const time_t backupTimestampStart = manifestData(jobData.manifest)->backupTimestampStart;
strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d %H:%M:%S", localtime_r(&backupTimestampStart, &timePart));
strCatFmt(message, ", recovery will start at %s", timeBuffer);
strCatZ(message, ", recovery will start at ");
strCatTimeP(message, "%Y-%m-%d %H:%M:%S", manifestData(jobData.manifest)->backupTimestampStart);
}
LOG_INFO(strZ(message));

View File

@ -246,7 +246,7 @@ FN_EXTERN size_t typeToLog(const char *typeName, char *buffer, size_t bufferSize
#define FUNCTION_LOG_TIME_TYPE \
time_t
#define FUNCTION_LOG_TIME_FORMAT(value, buffer, bufferSize) \
cvtTimeToZ(value, buffer, bufferSize)
cvtTimeToZP("%s", value, buffer, bufferSize)
#define FUNCTION_LOG_UINT_TYPE \
unsigned int

View File

@ -15,6 +15,7 @@ Log Handler
#include "common/debug.h"
#include "common/log.h"
#include "common/time.h"
#include "common/type/convert.h"
/***********************************************************************************************************************************
Module variables
@ -398,13 +399,11 @@ logPre(
// Add time
if (logTimestamp)
{
struct tm timePart;
const TimeMSec logTimeMSec = timeMSec();
const time_t logTimeSec = (time_t)(logTimeMSec / MSEC_PER_SEC);
result.bufferPos += strftime(
logBuffer + result.bufferPos, sizeof(logBuffer) - result.bufferPos, "%Y-%m-%d %H:%M:%S",
localtime_r(&logTimeSec, &timePart));
result.bufferPos += cvtTimeToZP(
"%Y-%m-%d %H:%M:%S", logTimeSec, logBuffer + result.bufferPos, sizeof(logBuffer) - result.bufferPos);
result.bufferPos += (size_t)snprintf(
logBuffer + result.bufferPos, sizeof(logBuffer) - result.bufferPos, ".%03d ", (int)(logTimeMSec % 1000));
}

View File

@ -366,18 +366,26 @@ cvtSizeToZ(const size_t value, char *const buffer, const size_t bufferSize)
/**********************************************************************************************************************************/
FN_EXTERN size_t
cvtTimeToZ(const time_t value, char *const buffer, const size_t bufferSize)
cvtTimeToZ(const char *const format, const time_t value, char *const buffer, const size_t bufferSize, const CvtTimeToZParam param)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRINGZ, format);
FUNCTION_TEST_PARAM(TIME, value);
FUNCTION_TEST_PARAM_P(CHARDATA, buffer);
FUNCTION_TEST_PARAM(SIZE, bufferSize);
FUNCTION_TEST_PARAM(BOOL, param.utc);
FUNCTION_TEST_END();
ASSERT(buffer != NULL);
struct tm timePart;
const size_t result = strftime(buffer, bufferSize, "%s", localtime_r(&value, &timePart));
// We can ignore this warning here since the format parameter of cvtTimeToZP() is checked
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
const size_t result = strftime(
buffer, bufferSize, format, param.utc ? gmtime_r(&value, &timePart) : localtime_r(&value, &timePart));
#pragma GCC diagnostic pop
if (result == 0)
THROW(AssertError, "buffer overflow");

View File

@ -12,6 +12,7 @@ Contains conversions to/from native C types. Conversions of project types should
#include <sys/types.h>
#include "common/assert.h"
#include "common/type/param.h"
/***********************************************************************************************************************************
Required buffer sizes
@ -97,7 +98,17 @@ FN_EXTERN mode_t cvtZToMode(const char *value);
FN_EXTERN size_t cvtSizeToZ(size_t value, char *buffer, size_t bufferSize);
// Convert time_t to zero-terminated string
FN_EXTERN size_t cvtTimeToZ(time_t value, char *buffer, size_t bufferSize);
typedef struct CvtTimeToZParam
{
VAR_PARAM_HEADER;
bool utc; // Use UTC instead of local time?
} CvtTimeToZParam;
#define cvtTimeToZP(format, value, buffer, bufferSize, ...) \
cvtTimeToZ(format, value, buffer, bufferSize, (CvtTimeToZParam){VAR_PARAM_INIT, __VA_ARGS__})
FN_EXTERN FN_STRFTIME(1) size_t cvtTimeToZ(
const char *format, time_t value, char *buffer, size_t bufferSize, CvtTimeToZParam param);
// Convert uint to zero-terminated string and vice versa
FN_EXTERN size_t cvtUIntToZ(unsigned int value, char *buffer, size_t bufferSize);

View File

@ -8,6 +8,7 @@ String Handler
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "common/debug.h"
#include "common/macro.h"
@ -174,6 +175,27 @@ strNewDbl(const double value)
FUNCTION_TEST_RETURN(STRING, strNewZ(working));
}
/**********************************************************************************************************************************/
FN_EXTERN String *
strNewTime(const char *const format, const time_t timestamp, const StrNewTimeParam param)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRINGZ, format);
FUNCTION_TEST_PARAM(TIME, timestamp);
FUNCTION_TEST_PARAM(BOOL, param.utc);
FUNCTION_TEST_END();
char buffer[64];
// We can ignore this warning here since the format parameter of strNewTimeP() is checked
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
cvtTimeToZP(format, timestamp, buffer, sizeof(buffer), .utc = param.utc);
#pragma GCC diagnostic pop
FUNCTION_TEST_RETURN(STRING, strNewZ(buffer));
}
/**********************************************************************************************************************************/
FN_EXTERN String *
strNewBuf(const Buffer *const buffer)
@ -513,6 +535,28 @@ strCatEncode(String *const this, const EncodingType type, const Buffer *const bu
FUNCTION_TEST_RETURN(STRING, this);
}
/**********************************************************************************************************************************/
FN_EXTERN String *
strCatTime(String *const this, const char *const format, const time_t timestamp, const StrCatTimeParam param)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING, this);
FUNCTION_TEST_PARAM(STRINGZ, format);
FUNCTION_TEST_PARAM(TIME, timestamp);
FUNCTION_TEST_PARAM(BOOL, param.utc);
FUNCTION_TEST_END();
char buffer[64];
// We can ignore this warning here since the format parameter of strCatTimeP() is checked
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
cvtTimeToZP(format, timestamp, buffer, sizeof(buffer), .utc = param.utc);
#pragma GCC diagnostic pop
FUNCTION_TEST_RETURN(STRING, strCatZ(this, buffer));
}
/**********************************************************************************************************************************/
FN_EXTERN String *
strCatFmt(String *const this, const char *const format, ...)

View File

@ -69,6 +69,18 @@ FN_EXTERN String *strNewBuf(const Buffer *buffer);
// Create a new fixed length string by converting the double value
FN_EXTERN String *strNewDbl(double value);
// Create a new fixed length string by converting a timestamp
typedef struct StrNewTimeParam
{
VAR_PARAM_HEADER;
bool utc; // Use UTC instead of local time?
} StrNewTimeParam;
#define strNewTimeP(format, timestamp, ...) \
strNewTime(format, timestamp, (StrNewTimeParam){VAR_PARAM_INIT, __VA_ARGS__})
FN_EXTERN FN_STRFTIME(1) String *strNewTime(const char *format, time_t timestamp, StrNewTimeParam param);
// Create a new fixed length string encoded with the specified type (e.g. encodingBase64) from a buffer
FN_EXTERN String *strNewEncode(EncodingType type, const Buffer *buffer);
@ -129,6 +141,18 @@ FN_EXTERN String *strCatChr(String *this, char cat);
// Append a string encoded with the specified type (e.g. encodingBase64) from a buffer
FN_EXTERN String *strCatEncode(String *this, EncodingType type, const Buffer *buffer);
// Append a timestamp
typedef struct StrCatTimeParam
{
VAR_PARAM_HEADER;
bool utc; // Use UTC instead of local time?
} StrCatTimeParam;
#define strCatTimeP(this, format, timestamp, ...) \
strCatTime(this, format, timestamp, (StrCatTimeParam){VAR_PARAM_INIT, __VA_ARGS__})
FN_EXTERN FN_STRFTIME(2) String *strCatTime(String *this, const char *format, time_t timestamp, StrCatTimeParam param);
// Append a formatted string
FN_EXTERN FN_PRINTF(2, 3) String *strCatFmt(String *this, const char *format, ...);

View File

@ -123,26 +123,6 @@ Expected ISO-8601 data/time size
***********************************************************************************************************************************/
#define ISO_8601_DATE_TIME_SIZE 16
/***********************************************************************************************************************************
Format ISO-8601 date/time for authentication
***********************************************************************************************************************************/
static String *
storageS3DateTime(const time_t authTime)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(TIME, authTime);
FUNCTION_TEST_END();
struct tm timePart;
char buffer[ISO_8601_DATE_TIME_SIZE + 1];
THROW_ON_SYS_ERROR(
strftime(buffer, sizeof(buffer), "%Y%m%dT%H%M%SZ", gmtime_r(&authTime, &timePart)) != ISO_8601_DATE_TIME_SIZE, AssertError,
"unable to format date");
FUNCTION_TEST_RETURN(STRING, strNewZ(buffer));
}
/***********************************************************************************************************************************
Generate authorization header and add it to the supplied header list
@ -552,7 +532,7 @@ storageS3RequestAsync(StorageS3 *const this, const String *const verb, const Str
// Generate authorization header
storageS3Auth(
this, verb, path, param.query, storageS3DateTime(time(NULL)), requestHeader,
this, verb, path, param.query, strNewTimeP("%Y%m%dT%H%M%SZ", time(NULL), .utc = true), requestHeader,
strNewEncode(
encodingHex,
param.content == NULL || bufEmpty(param.content) ?

View File

@ -401,12 +401,7 @@ testRun(void)
hrnTzSet("America/New_York");
time_t testTime = 1573754569;
char timeBuffer[20];
struct tm timePart;
strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d %H:%M:%S", localtime_r(&testTime, &timePart));
TEST_RESULT_Z(timeBuffer, "2019-11-14 13:02:49", "check timezone set");
TEST_RESULT_STR_Z(strNewTimeP("%Y-%m-%d %H:%M:%S", 1573754569), "2019-11-14 13:02:49", "check timezone set");
TEST_RESULT_INT(getEpoch(STRDEF("2019-11-14 13:02:49-0500")), 1573754569, "offset same as local");
TEST_RESULT_INT(getEpoch(STRDEF("2019-11-14 13:02:49")), 1573754569, "GMT-0500 (EST)");
TEST_RESULT_INT(getEpoch(STRDEF("2019-09-14 20:02:49")), 1568505769, "GMT-0400 (EDT)");

View File

@ -125,14 +125,27 @@ testRun(void)
}
// *****************************************************************************************************************************
if (testBegin("cvtTimeToZ()"))
if (testBegin("cvtTimeToZP()"))
{
char buffer[STACK_TRACE_PARAM_MAX];
TEST_ERROR(cvtTimeToZ(9999, buffer, 4), AssertError, "buffer overflow");
hrnTzSet("America/New_York");
TEST_RESULT_UINT(cvtTimeToZ(1573222014, buffer, STACK_TRACE_PARAM_MAX), 10, "convert time to string");
TEST_ERROR(cvtTimeToZP("%s", 9999, buffer, 4), AssertError, "buffer overflow");
TEST_RESULT_UINT(cvtTimeToZP("%s", 1573222014, buffer, STACK_TRACE_PARAM_MAX), 10, "local epoch");
TEST_RESULT_Z(buffer, "1573222014", " check buffer");
TEST_RESULT_UINT(cvtTimeToZP("%s", 1573222014, buffer, STACK_TRACE_PARAM_MAX, .utc = true), 10, "utc epoch");
TEST_RESULT_Z(buffer, "1573240014", " check buffer");
TEST_RESULT_UINT(cvtTimeToZP("%Y%m%d-%H%M%S", 1715930051, buffer, STACK_TRACE_PARAM_MAX), 15, "local string");
TEST_RESULT_Z(buffer, "20240517-031411", " check buffer");
TEST_RESULT_UINT(cvtTimeToZP("%Y%m%d-%H%M%S", 1715930051, buffer, STACK_TRACE_PARAM_MAX, .utc = true), 15, "utc string");
TEST_RESULT_Z(buffer, "20240517-071411", " check buffer");
hrnTzSet("UTC");
}
// *****************************************************************************************************************************

View File

@ -69,6 +69,16 @@ testRun(void)
TEST_RESULT_STR_Z(strNewDbl(999.1), "999.1", "new");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("new from time");
hrnTzSet("America/New_York");
TEST_RESULT_STR_Z(strNewTimeP("%Y%m%d-%H%M%S", 1715930051), "20240517-031411", "local");
TEST_RESULT_STR_Z(strNewTimeP("%Y%m%d-%H%M%S", 1715930051, .utc = true), "20240517-071411", "utc");
hrnTzSet("UTC");
// -------------------------------------------------------------------------------------------------------------------------
string = strNewFmt("formatted %s %04d", "string", 1);
TEST_RESULT_STR_Z(string, "formatted string 0001", "new with formatted string");
@ -141,6 +151,8 @@ testRun(void)
String *string = strCatZ(strNew(), "XXX");
String *string2 = strNewZ("ZZZZ");
hrnTzSet("America/New_York");
TEST_RESULT_STR_Z(strCatN(string, STRDEF("XX"), 1), "XXXX", "cat N chars");
TEST_RESULT_UINT(string->pub.extra, 60, "check extra");
TEST_RESULT_STR_Z(strCatZ(string, ""), "XXXX", "cat empty string");
@ -165,9 +177,19 @@ testRun(void)
strCatEncode(string, encodingBase64, BUFSTRDEF("zzzzz")),
"XXXXYYYY?00777!$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$enp6eno=", "cat encode");
TEST_RESULT_UINT(string->pub.extra, 27, "check extra");
TEST_RESULT_STR_Z(
strCatTimeP(string, "%Y%m%d-%H%M%S", 1715930051),
"XXXXYYYY?00777!$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$enp6eno=20240517-031411", "cat time");
TEST_RESULT_STR_Z(
strCatTimeP(string, "%Y%m%d-%H%M%S", 1715930051, .utc = true),
"XXXXYYYY?00777!$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$enp6eno=20240517-03141120240517-071411",
"cat time");
TEST_RESULT_UINT(string->pub.extra, 54, "check extra");
TEST_RESULT_VOID(strFree(string), "free string");
TEST_RESULT_STR_Z(string2, "ZZZZ", "check unaltered string");
hrnTzSet("UTC");
}
// *****************************************************************************************************************************

View File

@ -242,13 +242,7 @@ testS3DateTime(time_t time)
FUNCTION_HARNESS_PARAM(TIME, time);
FUNCTION_HARNESS_END();
char buffer[21];
THROW_ON_SYS_ERROR(
strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%SZ", gmtime(&time)) != sizeof(buffer) - 1, AssertError,
"unable to format date");
FUNCTION_HARNESS_RETURN(STRING, strNewZ(buffer));
FUNCTION_HARNESS_RETURN(STRING, strNewTimeP("%Y-%m-%dT%H:%M:%SZ", time, .utc = true));
}
/***********************************************************************************************************************************
@ -298,15 +292,12 @@ testRun(void)
hrnCfgEnvRaw(cfgOptRepoS3KeySecret, secretAccessKey);
// *****************************************************************************************************************************
if (testBegin("storageS3DateTime() and storageS3Auth()"))
if (testBegin("storageS3Auth()"))
{
char logBuf[STACK_TRACE_PARAM_MAX];
TEST_RESULT_STR_Z(storageS3DateTime(1491267845), "20170404T010405Z", "static date");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("config without token");
char logBuf[STACK_TRACE_PARAM_MAX];
StringList *argList = strLstDup(commonArgList);
HRN_CFG_LOAD(cfgCmdArchivePush, argList);