mirror of
https://github.com/postgres/postgres.git
synced 2025-04-24 10:47:04 +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:
parent
e21cbb4b89
commit
0a87ddff5c
@ -299,20 +299,31 @@ EncodeSpecialDate(DateADT dt, char *str)
|
|||||||
DateADT
|
DateADT
|
||||||
GetSQLCurrentDate(void)
|
GetSQLCurrentDate(void)
|
||||||
{
|
{
|
||||||
TimestampTz ts;
|
struct pg_tm tm;
|
||||||
struct pg_tm tt,
|
|
||||||
*tm = &tt;
|
|
||||||
fsec_t fsec;
|
|
||||||
int tz;
|
|
||||||
|
|
||||||
ts = GetCurrentTransactionStartTimestamp();
|
static int cache_year = 0;
|
||||||
|
static int cache_mon = 0;
|
||||||
|
static int cache_mday = 0;
|
||||||
|
static DateADT cache_date;
|
||||||
|
|
||||||
if (timestamp2tm(ts, &tz, tm, &fsec, NULL, NULL) != 0)
|
GetCurrentDateTime(&tm);
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
|
||||||
errmsg("timestamp out of range")));
|
|
||||||
|
|
||||||
return date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
|
/*
|
||||||
|
* date2j involves several integer divisions; moreover, unless our session
|
||||||
|
* lives across local midnight, we don't really have to do it more than
|
||||||
|
* once. So it seems worth having a separate cache here.
|
||||||
|
*/
|
||||||
|
if (tm.tm_year != cache_year ||
|
||||||
|
tm.tm_mon != cache_mon ||
|
||||||
|
tm.tm_mday != cache_mday)
|
||||||
|
{
|
||||||
|
cache_date = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - POSTGRES_EPOCH_JDATE;
|
||||||
|
cache_year = tm.tm_year;
|
||||||
|
cache_mon = tm.tm_mon;
|
||||||
|
cache_mday = tm.tm_mday;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cache_date;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -322,18 +333,12 @@ TimeTzADT *
|
|||||||
GetSQLCurrentTime(int32 typmod)
|
GetSQLCurrentTime(int32 typmod)
|
||||||
{
|
{
|
||||||
TimeTzADT *result;
|
TimeTzADT *result;
|
||||||
TimestampTz ts;
|
|
||||||
struct pg_tm tt,
|
struct pg_tm tt,
|
||||||
*tm = &tt;
|
*tm = &tt;
|
||||||
fsec_t fsec;
|
fsec_t fsec;
|
||||||
int tz;
|
int tz;
|
||||||
|
|
||||||
ts = GetCurrentTransactionStartTimestamp();
|
GetCurrentTimeUsec(tm, &fsec, &tz);
|
||||||
|
|
||||||
if (timestamp2tm(ts, &tz, tm, &fsec, NULL, NULL) != 0)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
|
||||||
errmsg("timestamp out of range")));
|
|
||||||
|
|
||||||
result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
|
result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
|
||||||
tm2timetz(tm, fsec, tz, result);
|
tm2timetz(tm, fsec, tz, result);
|
||||||
@ -348,18 +353,12 @@ TimeADT
|
|||||||
GetSQLLocalTime(int32 typmod)
|
GetSQLLocalTime(int32 typmod)
|
||||||
{
|
{
|
||||||
TimeADT result;
|
TimeADT result;
|
||||||
TimestampTz ts;
|
|
||||||
struct pg_tm tt,
|
struct pg_tm tt,
|
||||||
*tm = &tt;
|
*tm = &tt;
|
||||||
fsec_t fsec;
|
fsec_t fsec;
|
||||||
int tz;
|
int tz;
|
||||||
|
|
||||||
ts = GetCurrentTransactionStartTimestamp();
|
GetCurrentTimeUsec(tm, &fsec, &tz);
|
||||||
|
|
||||||
if (timestamp2tm(ts, &tz, tm, &fsec, NULL, NULL) != 0)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
|
||||||
errmsg("timestamp out of range")));
|
|
||||||
|
|
||||||
tm2time(tm, fsec, &result);
|
tm2time(tm, fsec, &result);
|
||||||
AdjustTimeForTypmod(&result, typmod);
|
AdjustTimeForTypmod(&result, typmod);
|
||||||
|
@ -339,35 +339,80 @@ j2day(int date)
|
|||||||
/*
|
/*
|
||||||
* GetCurrentDateTime()
|
* 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
|
void
|
||||||
GetCurrentDateTime(struct pg_tm *tm)
|
GetCurrentDateTime(struct pg_tm *tm)
|
||||||
{
|
{
|
||||||
int tz;
|
|
||||||
fsec_t fsec;
|
fsec_t fsec;
|
||||||
|
|
||||||
timestamp2tm(GetCurrentTransactionStartTimestamp(), &tz, tm, &fsec,
|
GetCurrentTimeUsec(tm, &fsec, NULL);
|
||||||
NULL, NULL);
|
|
||||||
/* Note: don't pass NULL tzp to timestamp2tm; affects behavior */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* GetCurrentTimeUsec()
|
* GetCurrentTimeUsec()
|
||||||
*
|
*
|
||||||
* 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,
|
||||||
* 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
|
void
|
||||||
GetCurrentTimeUsec(struct pg_tm *tm, fsec_t *fsec, int *tzp)
|
GetCurrentTimeUsec(struct pg_tm *tm, fsec_t *fsec, int *tzp)
|
||||||
{
|
{
|
||||||
int tz;
|
TimestampTz cur_ts = GetCurrentTransactionStartTimestamp();
|
||||||
|
|
||||||
timestamp2tm(GetCurrentTransactionStartTimestamp(), &tz, tm, fsec,
|
/*
|
||||||
NULL, NULL);
|
* The cache key must include both current time and current timezone. By
|
||||||
/* Note: don't pass NULL tzp to timestamp2tm; affects behavior */
|
* 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)
|
if (tzp != NULL)
|
||||||
*tzp = tz;
|
*tzp = cache_tz;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user