1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-15 19:21:59 +03:00

Cache the result of converting now() to a struct pg_tm.

SQL operations such as CURRENT_DATE, CURRENT_TIME, LOCALTIME, and
conversion of "now" in a datetime input string have to obtain the
transaction start timestamp ("now()") as a broken-down struct pg_tm.
This is a remarkably expensive conversion, and since now() does not
change intra-transaction, it doesn't really need to be done more than
once per transaction.  Introducing a simple cache provides visible
speedups in queries that compute these values many times, for example
insertion of many rows that use a default value of CURRENT_DATE.

Peter Smith, with a bit of kibitzing by me

Discussion: https://postgr.es/m/CAHut+Pu89TWjq530V2gY5O6SWi=OEJMQ_VHMt8bdZB_9JFna5A@mail.gmail.com
This commit is contained in:
Tom Lane
2020-09-28 12:05:03 -04:00
parent e21cbb4b89
commit 0a87ddff5c
2 changed files with 80 additions and 36 deletions

View File

@ -339,35 +339,80 @@ j2day(int date)
/*
* GetCurrentDateTime()
*
* Get the transaction start time ("now()") broken down as a struct pg_tm.
* Get the transaction start time ("now()") broken down as a struct pg_tm,
* converted according to the session timezone setting.
*
* This is just a convenience wrapper for GetCurrentTimeUsec, to cover the
* case where caller doesn't need either fractional seconds or tz offset.
*/
void
GetCurrentDateTime(struct pg_tm *tm)
{
int tz;
fsec_t fsec;
timestamp2tm(GetCurrentTransactionStartTimestamp(), &tz, tm, &fsec,
NULL, NULL);
/* Note: don't pass NULL tzp to timestamp2tm; affects behavior */
GetCurrentTimeUsec(tm, &fsec, NULL);
}
/*
* GetCurrentTimeUsec()
*
* Get the transaction start time ("now()") broken down as a struct pg_tm,
* including fractional seconds and timezone offset.
* including fractional seconds and timezone offset. The time is converted
* according to the session timezone setting.
*
* Callers may pass tzp = NULL if they don't need the offset, but this does
* not affect the conversion behavior (unlike timestamp2tm()).
*
* Internally, we cache the result, since this could be called many times
* in a transaction, within which now() doesn't change.
*/
void
GetCurrentTimeUsec(struct pg_tm *tm, fsec_t *fsec, int *tzp)
{
int tz;
TimestampTz cur_ts = GetCurrentTransactionStartTimestamp();
timestamp2tm(GetCurrentTransactionStartTimestamp(), &tz, tm, fsec,
NULL, NULL);
/* Note: don't pass NULL tzp to timestamp2tm; affects behavior */
/*
* The cache key must include both current time and current timezone. By
* representing the timezone by just a pointer, we're assuming that
* distinct timezone settings could never have the same pointer value.
* This is true by virtue of the hashtable used inside pg_tzset();
* however, it might need another look if we ever allow entries in that
* hash to be recycled.
*/
static TimestampTz cache_ts = 0;
static pg_tz *cache_timezone = NULL;
static struct pg_tm cache_tm;
static fsec_t cache_fsec;
static int cache_tz;
if (cur_ts != cache_ts || session_timezone != cache_timezone)
{
/*
* Make sure cache is marked invalid in case of error after partial
* update within timestamp2tm.
*/
cache_timezone = NULL;
/*
* Perform the computation, storing results into cache. We do not
* really expect any error here, since current time surely ought to be
* within range, but check just for sanity's sake.
*/
if (timestamp2tm(cur_ts, &cache_tz, &cache_tm, &cache_fsec,
NULL, session_timezone) != 0)
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
/* OK, so mark the cache valid. */
cache_ts = cur_ts;
cache_timezone = session_timezone;
}
*tm = cache_tm;
*fsec = cache_fsec;
if (tzp != NULL)
*tzp = tz;
*tzp = cache_tz;
}