diff --git a/src/common/backend/utils/adt/datetime.cpp b/src/common/backend/utils/adt/datetime.cpp index f8ae0f43c..39bfbced5 100644 --- a/src/common/backend/utils/adt/datetime.cpp +++ b/src/common/backend/utils/adt/datetime.cpp @@ -348,15 +348,52 @@ void GetCurrentDateTime(struct pg_tm* tm) * * Get the transaction start time ("now()") broken down as a struct pg_tm, * including fractional seconds and timezone offset. + * + * 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 */ - if (tzp != NULL) - *tzp = tz; + /* + * 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. + */ + if (cur_ts != u_sess->cache_ts || session_timezone != u_sess->cache_timezone) { + /* + * Make sure cache is marked invalid in case of error after partial + * update within timestamp2tm. + */ + u_sess->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, + &u_sess->cache_tz, &u_sess->cache_tm, &u_sess->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. */ + u_sess->cache_ts = cur_ts; + u_sess->cache_timezone = session_timezone; + } + + *tm = u_sess->cache_tm; + *fsec = u_sess->cache_fsec; + if (tzp != NULL) { + *tzp = u_sess->cache_tz; + } } /* TrimTrailingZeros() diff --git a/src/include/knl/knl_session.h b/src/include/knl/knl_session.h index 57c98dc46..8363217f0 100644 --- a/src/include/knl/knl_session.h +++ b/src/include/knl/knl_session.h @@ -2772,6 +2772,13 @@ typedef struct knl_session_context { struct knl_u_clientConnTime_context clientConnTime_cxt; knl_u_hook_context hook_cxt; + + /* The datetime cache in current transaction. */ + TimestampTz cache_ts = 0; + pg_tz* cache_timezone = NULL; + struct pg_tm cache_tm; + fsec_t cache_fsec; + int cache_tz; } knl_session_context; enum stp_xact_err_type {