/* ---------- *Maximallengthofonenode *----------
*/ #define DCH_MAX_ITEM_SIZ 12/* max localized day name */ #define NUM_MAX_ITEM_SIZ 8/* roman number (RN has 15 chars) */
/* ---------- *FromCharDateMode *---------- * *Thisvalueisusedtonominateoneofseveraldistinct(andmutually *exclusive)dateconventionsthatakeywordcanbelongto.
*/ typedefenum
{
FROM_CHAR_DATE_NONE = 0, /* Value does not affect date mode. */
FROM_CHAR_DATE_GREGORIAN, /* Gregorian (day, month, year) style date */
FROM_CHAR_DATE_ISOWEEK, /* ISO 8601 week date */
} FromCharDateMode;
typedefstruct
{ constchar *name; int len; int id; bool is_digit;
FromCharDateMode date_mode;
} KeyWord;
typedefstruct
{
uint8 type; /* NODE_TYPE_XXX, see below */ char character[MAX_MULTIBYTE_CHAR_LEN + 1]; /* if type is CHAR */
uint8 suffix; /* keyword prefix/suffix code, if any */ const KeyWord *key; /* if type is ACTION */
} FormatNode;
/* ---------- *Numberdescriptionstruct *----------
*/ typedefstruct
{ int pre, /* (count) numbers before decimal */
post, /* (count) numbers after decimal */
lsign, /* want locales sign */
flag, /* number parameters */
pre_lsign_num, /* tmp value for lsign */
multi, /* multiplier for 'V' */
zero_start, /* position of first zero */
zero_end, /* position of last zero */
need_locale; /* needs it locale */
} NUMDesc;
/* global cache for date/time format pictures */ static DCHCacheEntry *DCHCache[DCH_CACHE_ENTRIES]; staticint n_DCHCache = 0; /* current number of entries */ staticint DCHCounter = 0; /* aging-event counter */
/* global cache for number format pictures */ static NUMCacheEntry *NUMCache[NUM_CACHE_ENTRIES]; staticint n_NUMCache = 0; /* current number of entries */ staticint NUMCounter = 0; /* aging-event counter */
/* ---------- *Forchar->date/timeconversion *----------
*/ typedefstruct
{
FromCharDateMode mode; int hh,
pm,
mi,
ss,
ssss,
d, /* stored as 1-7, Sunday = 1, 0 means missing */
dd,
ddd,
mm,
ms,
year,
bc,
ww,
w,
cc,
j,
us,
yysz, /* is it YY or YYYY ? */
clock, /* 12 or 24 hour clock? */
tzsign, /* +1, -1, or 0 if no TZH/TZM fields */
tzh,
tzm,
ff; /* fractional precision */ bool has_tz; /* was there a TZ field? */ int gmtoffset; /* GMT offset of fixed-offset zone abbrev */
pg_tz *tzp; /* pg_tz for dynamic abbrev */ char *abbrev; /* dynamic abbrev */
} TmFromChar;
struct fmt_tz /* do_to_timestamp's timezone info output */
{ bool has_tz; /* was there any TZ/TZH/TZM field? */ int gmtoffset; /* GMT offset in seconds */
};
/* ---------- *Datetimetocharconversion * *Tosupportintervalsaswellastimestamps,weuseacustom"tm"struct *thatisalmostlikestructpg_tm,buthasa64-bittm_hourfield. *Weomitthetm_isdstandtm_zonefields,whicharenotusedhere. *----------
*/ struct fmt_tm
{ int tm_sec; int tm_min;
int64 tm_hour; int tm_mday; int tm_mon; int tm_year; int tm_wday; int tm_yday; longint tm_gmtoff;
};
/* Note: this is used to copy pg_tm to fmt_tm, so not quite a bitwise copy */ #define COPY_tm(_DST, _SRC) \ do { \
(_DST)->tm_sec = (_SRC)->tm_sec; \
(_DST)->tm_min = (_SRC)->tm_min; \
(_DST)->tm_hour = (_SRC)->tm_hour; \
(_DST)->tm_mday = (_SRC)->tm_mday; \
(_DST)->tm_mon = (_SRC)->tm_mon; \
(_DST)->tm_year = (_SRC)->tm_year; \
(_DST)->tm_wday = (_SRC)->tm_wday; \
(_DST)->tm_yday = (_SRC)->tm_yday; \
(_DST)->tm_gmtoff = (_SRC)->tm_gmtoff; \
} while(0)
/* Caution: this is used to zero both pg_tm and fmt_tm structs */ #define ZERO_tm(_X) \ do { \
memset(_X, 0, sizeof(*(_X))); \
(_X)->tm_mday = (_X)->tm_mon = 1; \
} while(0)
/* *to_char(time)appearstoto_char()asaninterval,sothischeck *isreallyforintervalandtimedatatypes.
*/ #define INVALID_FOR_INTERVAL \ do { \ if (is_interval) \
ereport(ERROR, \
(errcode(ERRCODE_INVALID_DATETIME_FORMAT), \
errmsg("invalid format specification for an interval value"), \
errhint("Intervals are not tied to specific calendar dates."))); \
} while(0)
int sign, /* '-' or '+' */
sign_wrote, /* was sign write */
num_count, /* number of write digits */
num_in, /* is inside number */
num_curr, /* current position in number */
out_pre_spaces, /* spaces before first digit */
read_dec, /* to_number - was read dec. point */
read_post, /* to_number - number of dec. digit */
read_pre; /* to_number - number non-dec. digit */
char *number, /* string with number */
*number_p, /* pointer to current number position */
*inout, /* in / out buffer */
*inout_p; /* pointer to current inout position */
constchar *last_relevant, /* last relevant number after decimal point */
if (IS_EEEE(num) && n->key->id != NUM_E)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"EEEE\" must be the last pattern used")));
switch (n->key->id)
{ case NUM_9: if (IS_BRACKET(num))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"9\" must be ahead of \"PR\""))); if (IS_MULTI(num))
{
++num->multi; break;
} if (IS_DECIMAL(num))
++num->post; else
++num->pre; break;
case NUM_0: if (IS_BRACKET(num))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"0\" must be ahead of \"PR\""))); if (!IS_ZERO(num) && !IS_DECIMAL(num))
{
num->flag |= NUM_F_ZERO;
num->zero_start = num->pre + 1;
} if (!IS_DECIMAL(num))
++num->pre; else
++num->post;
num->zero_end = num->pre + num->post; break;
case NUM_B: if (num->pre == 0 && num->post == 0 && (!IS_ZERO(num)))
num->flag |= NUM_F_BLANK; break;
case NUM_D:
num->flag |= NUM_F_LDECIMAL;
num->need_locale = true; /* FALLTHROUGH */ case NUM_DEC: if (IS_DECIMAL(num))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("multiple decimal points"))); if (IS_MULTI(num))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cannot use \"V\" and decimal point together")));
num->flag |= NUM_F_DECIMAL; break;
case NUM_FM:
num->flag |= NUM_F_FILLMODE; break;
case NUM_S: if (IS_LSIGN(num))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cannot use \"S\" twice"))); if (IS_PLUS(num) || IS_MINUS(num) || IS_BRACKET(num))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cannot use \"S\" and \"PL\"/\"MI\"/\"SG\"/\"PR\" together"))); if (!IS_DECIMAL(num))
{
num->lsign = NUM_LSIGN_PRE;
num->pre_lsign_num = num->pre;
num->need_locale = true;
num->flag |= NUM_F_LSIGN;
} elseif (num->lsign == NUM_LSIGN_NONE)
{
num->lsign = NUM_LSIGN_POST;
num->need_locale = true;
num->flag |= NUM_F_LSIGN;
} break;
case NUM_MI: if (IS_LSIGN(num))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cannot use \"S\" and \"MI\" together")));
num->flag |= NUM_F_MINUS; if (IS_DECIMAL(num))
num->flag |= NUM_F_MINUS_POST; break;
case NUM_PL: if (IS_LSIGN(num))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cannot use \"S\" and \"PL\" together")));
num->flag |= NUM_F_PLUS; if (IS_DECIMAL(num))
num->flag |= NUM_F_PLUS_POST; break;
case NUM_SG: if (IS_LSIGN(num))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cannot use \"S\" and \"SG\" together")));
num->flag |= NUM_F_MINUS;
num->flag |= NUM_F_PLUS; break;
case NUM_PR: if (IS_LSIGN(num) || IS_PLUS(num) || IS_MINUS(num))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cannot use \"PR\" and \"S\"/\"PL\"/\"MI\"/\"SG\" together")));
num->flag |= NUM_F_BRACKET; break;
case NUM_rn: case NUM_RN: if (IS_ROMAN(num))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cannot use \"RN\" twice")));
num->flag |= NUM_F_ROMAN; break;
case NUM_L: case NUM_G:
num->need_locale = true; break;
case NUM_V: if (IS_DECIMAL(num))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cannot use \"V\" and decimal point together")));
num->flag |= NUM_F_MULTI; break;
case NUM_E: if (IS_EEEE(num))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cannot use \"EEEE\" twice"))); if (IS_BLANK(num) || IS_FILLMODE(num) || IS_LSIGN(num) ||
IS_BRACKET(num) || IS_MINUS(num) || IS_PLUS(num) ||
IS_ROMAN(num) || IS_MULTI(num))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"EEEE\" is incompatible with other formats"),
errdetail("\"EEEE\" may only be used together with digit and decimal point patterns.")));
num->flag |= NUM_F_EEEE; break;
}
if (IS_ROMAN(num) &&
(num->flag & ~(NUM_F_ROMAN | NUM_F_FILLMODE)) != 0)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"RN\" is incompatible with other formats"),
errdetail("\"RN\" may only be used together with \"FM\".")));
}
/* ---------- *ReturnST/ND/RD/THforsimple(1..9)numbers *type-->0upper,1lower *----------
*/ staticconstchar *
get_th(char *num, int type)
{ int len = strlen(num),
last;
last = *(num + (len - 1)); if (!isdigit((unsignedchar) last))
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("\"%s\" is not a number", num)));
/* *All"teens"(<x>1[0-9])get'TH/th',while<x>[02-9][123]stillget *'ST/st','ND/nd','RD/rd',respectively
*/ if ((len > 1) && (num[len - 2] == '1'))
last = 0;
if (!OidIsValid(collid))
{ /* *Thistypicallymeansthattheparsercouldnotresolveaconflict *ofimplicitcollations,soreportitthatway.
*/
ereport(ERROR,
(errcode(ERRCODE_INDETERMINATE_COLLATION),
errmsg("could not determine which collation to use for %s function", "lower()"),
errhint("Use the COLLATE clause to set the collation explicitly.")));
}
mylocale = pg_newlocale_from_collation(collid);
/* C/POSIX collations use this path regardless of database encoding */ if (mylocale->ctype_is_c)
{
result = asc_tolower(buff, nbytes);
} else
{ constchar *src = buff;
size_t srclen = nbytes;
size_t dstsize; char *dst;
size_t needed;
/* first try buffer of equal size plus terminating NUL */
dstsize = srclen + 1;
dst = palloc(dstsize);
if (!OidIsValid(collid))
{ /* *Thistypicallymeansthattheparsercouldnotresolveaconflict *ofimplicitcollations,soreportitthatway.
*/
ereport(ERROR,
(errcode(ERRCODE_INDETERMINATE_COLLATION),
errmsg("could not determine which collation to use for %s function", "upper()"),
errhint("Use the COLLATE clause to set the collation explicitly.")));
}
mylocale = pg_newlocale_from_collation(collid);
/* C/POSIX collations use this path regardless of database encoding */ if (mylocale->ctype_is_c)
{
result = asc_toupper(buff, nbytes);
} else
{ constchar *src = buff;
size_t srclen = nbytes;
size_t dstsize; char *dst;
size_t needed;
/* first try buffer of equal size plus terminating NUL */
dstsize = srclen + 1;
dst = palloc(dstsize);
if (!OidIsValid(collid))
{ /* *Thistypicallymeansthattheparsercouldnotresolveaconflict *ofimplicitcollations,soreportitthatway.
*/
ereport(ERROR,
(errcode(ERRCODE_INDETERMINATE_COLLATION),
errmsg("could not determine which collation to use for %s function", "initcap()"),
errhint("Use the COLLATE clause to set the collation explicitly.")));
}
mylocale = pg_newlocale_from_collation(collid);
/* C/POSIX collations use this path regardless of database encoding */ if (mylocale->ctype_is_c)
{
result = asc_initcap(buff, nbytes);
} else
{ constchar *src = buff;
size_t srclen = nbytes;
size_t dstsize; char *dst;
size_t needed;
/* first try buffer of equal size plus terminating NUL */
dstsize = srclen + 1;
dst = palloc(dstsize);
if (!OidIsValid(collid))
{ /* *Thistypicallymeansthattheparsercouldnotresolveaconflict *ofimplicitcollations,soreportitthatway.
*/
ereport(ERROR,
(errcode(ERRCODE_INDETERMINATE_COLLATION),
errmsg("could not determine which collation to use for %s function", "lower()"),
errhint("Use the COLLATE clause to set the collation explicitly.")));
}
if (GetDatabaseEncoding() != PG_UTF8)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("Unicode case folding can only be performed if server encoding is UTF8")));
mylocale = pg_newlocale_from_collation(collid);
/* C/POSIX collations use this path regardless of database encoding */ if (mylocale->ctype_is_c)
{
result = asc_tolower(buff, nbytes);
} else
{ constchar *src = buff;
size_t srclen = nbytes;
size_t dstsize; char *dst;
size_t needed;
/* first try buffer of equal size plus terminating NUL */
dstsize = srclen + 1;
dst = palloc(dstsize);
returntrue; /* some non-digit input (separator) */
}
staticint
adjust_partial_year_to_2020(int year)
{ /* *Adjustalldatestoward2020;thisiseffectivelywhathappenswhenwe *assume'70'is1970and'69'is2069.
*/ /* Force 0-69 into the 2000's */ if (year < 70) return year + 2000; /* Force 70-99 into the 1900's */ elseif (year < 100) return year + 1900; /* Force 100-519 into the 2000's */ elseif (year < 520) return year + 2000; /* Force 520-999 into the 1000's */ elseif (year < 1000) return year + 1000; else return year;
}
staticint
strspace_len(constchar *str)
{ int len = 0;
if (used < len)
ereturn(escontext, -1,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("source string too short for \"%s\" formatting field",
node->key->name),
errdetail("Field requires %d characters, but only %d remain.",
len, used),
errhint("If your source string is not fixed-width, " "try using the \"FM\" modifier.")));
errno = 0;
result = strtol(copy, &last, 10);
used = last - copy;
if (used > 0 && used < len)
ereturn(escontext, -1,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("invalid value \"%s\" for \"%s\"",
copy, node->key->name),
errdetail("Field requires %d characters, but only %d could be parsed.",
len, used),
errhint("If your source string is not fixed-width, " "try using the \"FM\" modifier.")));
*src += used;
}
if (*src == init)
ereturn(escontext, -1,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("invalid value \"%s\" for \"%s\"",
copy, node->key->name),
errdetail("Value must be an integer.")));
if (errno == ERANGE || result < INT_MIN || result > INT_MAX)
ereturn(escontext, -1,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("value for \"%s\" in source string is out of range",
node->key->name),
errdetail("Value must be in the range %d to %d.",
INT_MIN, INT_MAX)));
if (dest != NULL)
{ if (!from_char_set_int(dest, (int) result, node, escontext)) return -1;
}
/* empty string can't match anything */ if (!*name) return -1;
/* we handle first char specially to gain some speed */
firstc = pg_ascii_tolower((unsignedchar) *name);
for (a = array; *a != NULL; a++)
{ constchar *p; constchar *n;
/* compare first chars */ if (pg_ascii_tolower((unsignedchar) **a) != firstc) continue;
/* compare rest of string */ for (p = *a + 1, n = name + 1;; p++, n++)
{ /* return success if we matched whole array entry */ if (*p == '\0')
{
*len = n - name; return a - array;
} /* else, must have another character in "name" ... */ if (*n == '\0') break; /* ... and it must match */ if (pg_ascii_tolower((unsignedchar) *p) !=
pg_ascii_tolower((unsignedchar) *n)) break;
}
}
for (c = copy; *c; c++)
{ if (scanner_isspace(*c))
{
*c = '\0'; break;
}
}
ereturn(escontext, false,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("invalid value \"%s\" for \"%s\"",
copy, node->key->name),
errdetail("The given value did not match any of " "the allowed values for this field.")));
}
*src += len; returntrue;
}
#define DCH_to_char_fsec(frac_fmt, frac_val) \
sprintf(s, frac_fmt, (int) (frac_val)); \ if (S_THth(n->suffix)) \
str_numth(s, s, S_TH_TYPE(n->suffix)); \
s += strlen(s)
case DCH_FF1: /* tenth of second */
DCH_to_char_fsec("%01d", in->fsec / 100000); break; case DCH_FF2: /* hundredth of second */
DCH_to_char_fsec("%02d", in->fsec / 10000); break; case DCH_FF3: case DCH_MS: /* millisecond */
DCH_to_char_fsec("%03d", in->fsec / 1000); break; case DCH_FF4: /* tenth of a millisecond */
DCH_to_char_fsec("%04d", in->fsec / 100); break; case DCH_FF5: /* hundredth of a millisecond */
DCH_to_char_fsec("%05d", in->fsec / 10); break; case DCH_FF6: case DCH_US: /* microsecond */
DCH_to_char_fsec("%06d", in->fsec); break; #undef DCH_to_char_fsec case DCH_SSSS:
sprintf(s, "%lld",
(longlong) (tm->tm_hour * SECS_PER_HOUR +
tm->tm_min * SECS_PER_MINUTE +
tm->tm_sec)); if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s); break; case DCH_tz:
INVALID_FOR_INTERVAL; if (tmtcTzn(in))
{ /* We assume here that timezone names aren't localized */ char *p = asc_tolower_z(tmtcTzn(in));
strcpy(s, p);
pfree(p);
s += strlen(s);
} break; case DCH_TZ:
INVALID_FOR_INTERVAL; if (tmtcTzn(in))
{
strcpy(s, tmtcTzn(in));
s += strlen(s);
} break; case DCH_TZH:
INVALID_FOR_INTERVAL;
sprintf(s, "%c%02d",
(tm->tm_gmtoff >= 0) ? '+' : '-',
abs((int) tm->tm_gmtoff) / SECS_PER_HOUR);
s += strlen(s); break; case DCH_TZM:
INVALID_FOR_INTERVAL;
sprintf(s, "%02d",
(abs((int) tm->tm_gmtoff) % SECS_PER_HOUR) / SECS_PER_MINUTE);
s += strlen(s); break; case DCH_OF:
INVALID_FOR_INTERVAL;
sprintf(s, "%c%0*d",
(tm->tm_gmtoff >= 0) ? '+' : '-',
S_FM(n->suffix) ? 0 : 2,
abs((int) tm->tm_gmtoff) / SECS_PER_HOUR);
s += strlen(s); if (abs((int) tm->tm_gmtoff) % SECS_PER_HOUR != 0)
{
sprintf(s, ":%02d",
(abs((int) tm->tm_gmtoff) % SECS_PER_HOUR) / SECS_PER_MINUTE);
s += strlen(s);
} break; case DCH_A_D: case DCH_B_C:
INVALID_FOR_INTERVAL;
strcpy(s, (tm->tm_year <= 0 ? B_C_STR : A_D_STR));
s += strlen(s); break; case DCH_AD: case DCH_BC:
INVALID_FOR_INTERVAL;
strcpy(s, (tm->tm_year <= 0 ? BC_STR : AD_STR));
s += strlen(s); break; case DCH_a_d: case DCH_b_c:
INVALID_FOR_INTERVAL;
strcpy(s, (tm->tm_year <= 0 ? b_c_STR : a_d_STR));
s += strlen(s); break; case DCH_ad: case DCH_bc:
INVALID_FOR_INTERVAL;
strcpy(s, (tm->tm_year <= 0 ? bc_STR : ad_STR));
s += strlen(s); break; case DCH_MONTH:
INVALID_FOR_INTERVAL; if (!tm->tm_mon) break; if (S_TM(n->suffix))
{ char *str = str_toupper_z(localized_full_months[tm->tm_mon - 1], collid);
if (strlen(str) <= (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
strcpy(s, str); else
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("localized string format value too long")));
} else
sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9,
asc_toupper_z(months_full[tm->tm_mon - 1]));
s += strlen(s); break; case DCH_Month:
INVALID_FOR_INTERVAL; if (!tm->tm_mon) break; if (S_TM(n->suffix))
{ char *str = str_initcap_z(localized_full_months[tm->tm_mon - 1], collid);
if (strlen(str) <= (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
strcpy(s, str); else
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("localized string format value too long")));
} else
sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9,
months_full[tm->tm_mon - 1]);
s += strlen(s); break; case DCH_month:
INVALID_FOR_INTERVAL; if (!tm->tm_mon) break; if (S_TM(n->suffix))
{ char *str = str_tolower_z(localized_full_months[tm->tm_mon - 1], collid);
if (strlen(str) <= (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
strcpy(s, str); else
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("localized string format value too long")));
} else
sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9,
asc_tolower_z(months_full[tm->tm_mon - 1]));
s += strlen(s); break; case DCH_MON:
INVALID_FOR_INTERVAL; if (!tm->tm_mon) break; if (S_TM(n->suffix))
{ char *str = str_toupper_z(localized_abbrev_months[tm->tm_mon - 1], collid);
if (strlen(str) <= (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
strcpy(s, str); else
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("localized string format value too long")));
} else
strcpy(s, asc_toupper_z(months[tm->tm_mon - 1]));
s += strlen(s); break; case DCH_Mon:
INVALID_FOR_INTERVAL; if (!tm->tm_mon) break; if (S_TM(n->suffix))
{ char *str = str_initcap_z(localized_abbrev_months[tm->tm_mon - 1], collid);
if (strlen(str) <= (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
strcpy(s, str); else
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("localized string format value too long")));
} else
strcpy(s, months[tm->tm_mon - 1]);
s += strlen(s); break; case DCH_mon:
INVALID_FOR_INTERVAL; if (!tm->tm_mon) break; if (S_TM(n->suffix))
{ char *str = str_tolower_z(localized_abbrev_months[tm->tm_mon - 1], collid);
if (strlen(str) <= (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
strcpy(s, str); else
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("localized string format value too long")));
} else
strcpy(s, asc_tolower_z(months[tm->tm_mon - 1]));
s += strlen(s); break; case DCH_MM:
sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : (tm->tm_mon >= 0) ? 2 : 3,
tm->tm_mon); if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s); break; case DCH_DAY:
INVALID_FOR_INTERVAL; if (S_TM(n->suffix))
{ char *str = str_toupper_z(localized_full_days[tm->tm_wday], collid);
if (strlen(str) <= (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
strcpy(s, str); else
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("localized string format value too long")));
} else
sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9,
asc_toupper_z(days[tm->tm_wday]));
s += strlen(s); break; case DCH_Day:
INVALID_FOR_INTERVAL; if (S_TM(n->suffix))
{ char *str = str_initcap_z(localized_full_days[tm->tm_wday], collid);
if (strlen(str) <= (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
strcpy(s, str); else
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("localized string format value too long")));
} else
sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9,
days[tm->tm_wday]);
s += strlen(s); break; case DCH_day:
INVALID_FOR_INTERVAL; if (S_TM(n->suffix))
{ char *str = str_tolower_z(localized_full_days[tm->tm_wday], collid);
if (strlen(str) <= (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
strcpy(s, str); else
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("localized string format value too long")));
} else
sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9,
asc_tolower_z(days[tm->tm_wday]));
s += strlen(s); break; case DCH_DY:
INVALID_FOR_INTERVAL; if (S_TM(n->suffix))
{ char *str = str_toupper_z(localized_abbrev_days[tm->tm_wday], collid);
if (strlen(str) <= (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
strcpy(s, str); else
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("localized string format value too long")));
} else
strcpy(s, asc_toupper_z(days_short[tm->tm_wday]));
s += strlen(s); break; case DCH_Dy:
INVALID_FOR_INTERVAL; if (S_TM(n->suffix))
{ char *str = str_initcap_z(localized_abbrev_days[tm->tm_wday], collid);
if (strlen(str) <= (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
strcpy(s, str); else
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("localized string format value too long")));
} else
strcpy(s, days_short[tm->tm_wday]);
s += strlen(s); break; case DCH_dy:
INVALID_FOR_INTERVAL; if (S_TM(n->suffix))
{ char *str = str_tolower_z(localized_abbrev_days[tm->tm_wday], collid);
if (strlen(str) <= (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
strcpy(s, str); else
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("localized string format value too long")));
} else
strcpy(s, asc_tolower_z(days_short[tm->tm_wday]));
s += strlen(s); break; case DCH_DDD: case DCH_IDDD:
sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 3,
(n->key->id == DCH_DDD) ?
tm->tm_yday :
date2isoyearday(tm->tm_year, tm->tm_mon, tm->tm_mday)); if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s); break; case DCH_DD:
sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2, tm->tm_mday); if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s); break; case DCH_D:
INVALID_FOR_INTERVAL;
sprintf(s, "%d", tm->tm_wday + 1); if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s); break; case DCH_ID:
INVALID_FOR_INTERVAL;
sprintf(s, "%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday); if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s); break; case DCH_WW:
sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2,
(tm->tm_yday - 1) / 7 + 1); if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s); break; case DCH_IW:
sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2,
date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday)); if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s); break; case DCH_Q: if (!tm->tm_mon) break;
sprintf(s, "%d", (tm->tm_mon - 1) / 3 + 1); if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s); break; case DCH_CC: if (is_interval) /* straight calculation */
i = tm->tm_year / 100; else
{ if (tm->tm_year > 0) /* Century 20 == 1901 - 2000 */
i = (tm->tm_year - 1) / 100 + 1; else /* Century 6BC == 600BC - 501BC */
i = tm->tm_year / 100 - 1;
} if (i <= 99 && i >= -99)
sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : (i >= 0) ? 2 : 3, i); else
sprintf(s, "%d", i); if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s); break; case DCH_Y_YYY:
i = ADJUST_YEAR(tm->tm_year, is_interval) / 1000;
sprintf(s, "%d,%03d", i,
ADJUST_YEAR(tm->tm_year, is_interval) - (i * 1000)); if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s); break; case DCH_YYYY: case DCH_IYYY:
sprintf(s, "%0*d",
S_FM(n->suffix) ? 0 :
(ADJUST_YEAR(tm->tm_year, is_interval) >= 0) ? 4 : 5,
(n->key->id == DCH_YYYY ?
ADJUST_YEAR(tm->tm_year, is_interval) :
ADJUST_YEAR(date2isoyear(tm->tm_year,
tm->tm_mon,
tm->tm_mday),
is_interval))); if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s); break; case DCH_YYY: case DCH_IYY:
sprintf(s, "%0*d",
S_FM(n->suffix) ? 0 :
(ADJUST_YEAR(tm->tm_year, is_interval) >= 0) ? 3 : 4,
(n->key->id == DCH_YYY ?
ADJUST_YEAR(tm->tm_year, is_interval) :
ADJUST_YEAR(date2isoyear(tm->tm_year,
tm->tm_mon,
tm->tm_mday),
is_interval)) % 1000); if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s); break; case DCH_YY: case DCH_IY:
sprintf(s, "%0*d",
S_FM(n->suffix) ? 0 :
(ADJUST_YEAR(tm->tm_year, is_interval) >= 0) ? 2 : 3,
(n->key->id == DCH_YY ?
ADJUST_YEAR(tm->tm_year, is_interval) :
ADJUST_YEAR(date2isoyear(tm->tm_year,
tm->tm_mon,
tm->tm_mday),
is_interval)) % 100); if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s); break; case DCH_Y: case DCH_I:
sprintf(s, "%1d",
(n->key->id == DCH_Y ?
ADJUST_YEAR(tm->tm_year, is_interval) :
ADJUST_YEAR(date2isoyear(tm->tm_year,
tm->tm_mon,
tm->tm_mday),
is_interval)) % 10); if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s); break; case DCH_RM: /* FALLTHROUGH */ case DCH_rm:
/* *Forintervals,valueslike'12month'willbereducedto0 *monthandsomeyears.Theseshouldbeprocessed.
*/ if (!tm->tm_mon && !tm->tm_year) break; else
{ int mon = 0; constchar *const *months;
/* *Standardmoderequiresstrictmatchofformatcharacters.
*/ if (std && n->type == NODE_TYPE_CHAR &&
strncmp(s, n->character, chlen) != 0)
ereturn(escontext,,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("unmatched format character \"%s\"",
n->character)));
s += chlen;
} continue;
}
if (!from_char_set_mode(out, n->key->date_mode, escontext)) return;
switch (n->key->id)
{ case DCH_FX:
fx_mode = true; break; case DCH_A_M: case DCH_P_M: case DCH_a_m: case DCH_p_m: if (!from_char_seq_search(&value, &s, ampm_strings_long,
NULL, InvalidOid,
n, escontext)) return; if (!from_char_set_int(&out->pm, value % 2, n, escontext)) return;
out->clock = CLOCK_12_HOUR; break; case DCH_AM: case DCH_PM: case DCH_am: case DCH_pm: if (!from_char_seq_search(&value, &s, ampm_strings,
NULL, InvalidOid,
n, escontext)) return; if (!from_char_set_int(&out->pm, value % 2, n, escontext)) return;
out->clock = CLOCK_12_HOUR; break; case DCH_HH: case DCH_HH12: if (from_char_parse_int_len(&out->hh, &s, 2, n, escontext) < 0) return;
out->clock = CLOCK_12_HOUR;
SKIP_THth(s, n->suffix); break; case DCH_HH24: if (from_char_parse_int_len(&out->hh, &s, 2, n, escontext) < 0) return;
SKIP_THth(s, n->suffix); break; case DCH_MI: if (from_char_parse_int(&out->mi, &s, n, escontext) < 0) return;
SKIP_THth(s, n->suffix); break; case DCH_SS: if (from_char_parse_int(&out->ss, &s, n, escontext) < 0) return;
SKIP_THth(s, n->suffix); break; case DCH_MS: /* millisecond */
len = from_char_parse_int_len(&out->ms, &s, 3, n, escontext); if (len < 0) return;
/* *25is0.25and250is0.25too;025is0.025andnot0.25
*/
out->ms *= len == 1 ? 100 :
len == 2 ? 10 : 1;
SKIP_THth(s, n->suffix); break; case DCH_FF1: case DCH_FF2: case DCH_FF3: case DCH_FF4: case DCH_FF5: case DCH_FF6:
out->ff = n->key->id - DCH_FF1 + 1; /* FALLTHROUGH */ case DCH_US: /* microsecond */
len = from_char_parse_int_len(&out->us, &s,
n->key->id == DCH_US ? 6 :
out->ff, n, escontext); if (len < 0) return;
out->us *= len == 1 ? 100000 :
len == 2 ? 10000 :
len == 3 ? 1000 :
len == 4 ? 100 :
len == 5 ? 10 : 1;
SKIP_THth(s, n->suffix); break; case DCH_SSSS: if (from_char_parse_int(&out->ssss, &s, n, escontext) < 0) return;
SKIP_THth(s, n->suffix); break; case DCH_tz: case DCH_TZ:
{ int tzlen;
tzlen = DecodeTimezoneAbbrevPrefix(s,
&out->gmtoffset,
&out->tzp); if (tzlen > 0)
{
out->has_tz = true; /* we only need the zone abbrev for DYNTZ case */ if (out->tzp)
out->abbrev = pnstrdup(s, tzlen);
out->tzsign = 0; /* drop any earlier TZH/TZM info */
s += tzlen; break;
} elseif (isalpha((unsignedchar) *s))
{ /* *Itdoesn'tmatchanyabbreviation,butitstarts *withaletter.OFformatcertainlywon'tsucceed; *assumeit'samisspelledabbreviationandcomplain *accordingly.
*/
ereturn(escontext,,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("invalid value \"%s\" for \"%s\"",
s, n->key->name),
errdetail("Time zone abbreviation is not recognized.")));
} /* otherwise parse it like OF */
} /* FALLTHROUGH */ case DCH_OF: /* OF is equivalent to TZH or TZH:TZM */ /* see TZH comments below */ if (*s == '+' || *s == '-' || *s == ' ')
{
out->tzsign = *s == '-' ? -1 : +1;
s++;
} else
{ if (extra_skip > 0 && *(s - 1) == '-')
out->tzsign = -1; else
out->tzsign = +1;
} if (from_char_parse_int_len(&out->tzh, &s, 2, n, escontext) < 0) return; if (*s == ':')
{
s++; if (from_char_parse_int_len(&out->tzm, &s, 2, n,
escontext) < 0) return;
} break; case DCH_TZH:
if (from_char_parse_int_len(&out->tzh, &s, 2, n, escontext) < 0) return; break; case DCH_TZM: /* assign positive timezone sign if TZH was not seen before */ if (!out->tzsign)
out->tzsign = +1; if (from_char_parse_int_len(&out->tzm, &s, 2, n, escontext) < 0) return; break; case DCH_A_D: case DCH_B_C: case DCH_a_d: case DCH_b_c: if (!from_char_seq_search(&value, &s, adbc_strings_long,
NULL, InvalidOid,
n, escontext)) return; if (!from_char_set_int(&out->bc, value % 2, n, escontext)) return; break; case DCH_AD: case DCH_BC: case DCH_ad: case DCH_bc: if (!from_char_seq_search(&value, &s, adbc_strings,
NULL, InvalidOid,
n, escontext)) return; if (!from_char_set_int(&out->bc, value % 2, n, escontext)) return; break; case DCH_MONTH: case DCH_Month: case DCH_month: if (!from_char_seq_search(&value, &s, months_full,
S_TM(n->suffix) ? localized_full_months : NULL,
collid,
n, escontext)) return; if (!from_char_set_int(&out->mm, value + 1, n, escontext)) return; break; case DCH_MON: case DCH_Mon: case DCH_mon: if (!from_char_seq_search(&value, &s, months,
S_TM(n->suffix) ? localized_abbrev_months : NULL,
collid,
n, escontext)) return; if (!from_char_set_int(&out->mm, value + 1, n, escontext)) return; break; case DCH_MM: if (from_char_parse_int(&out->mm, &s, n, escontext) < 0) return;
SKIP_THth(s, n->suffix); break; case DCH_DAY: case DCH_Day: case DCH_day: if (!from_char_seq_search(&value, &s, days,
S_TM(n->suffix) ? localized_full_days : NULL,
collid,
n, escontext)) return; if (!from_char_set_int(&out->d, value, n, escontext)) return;
out->d++; break; case DCH_DY: case DCH_Dy: case DCH_dy: if (!from_char_seq_search(&value, &s, days_short,
S_TM(n->suffix) ? localized_abbrev_days : NULL,
collid,
n, escontext)) return; if (!from_char_set_int(&out->d, value, n, escontext)) return;
out->d++; break; case DCH_DDD: if (from_char_parse_int(&out->ddd, &s, n, escontext) < 0) return;
SKIP_THth(s, n->suffix); break; case DCH_IDDD: if (from_char_parse_int_len(&out->ddd, &s, 3, n, escontext) < 0) return;
SKIP_THth(s, n->suffix); break; case DCH_DD: if (from_char_parse_int(&out->dd, &s, n, escontext) < 0) return;
SKIP_THth(s, n->suffix); break; case DCH_D: if (from_char_parse_int(&out->d, &s, n, escontext) < 0) return;
SKIP_THth(s, n->suffix); break; case DCH_ID: if (from_char_parse_int_len(&out->d, &s, 1, n, escontext) < 0) return; /* Shift numbering to match Gregorian where Sunday = 1 */ if (++out->d > 7)
out->d = 1;
SKIP_THth(s, n->suffix); break; case DCH_WW: case DCH_IW: if (from_char_parse_int(&out->ww, &s, n, escontext) < 0) return;
SKIP_THth(s, n->suffix); break; case DCH_Q:
/* *Weignore'Q'whenconvertingtodatebecauseitisunclear *whichdateinthequartertouse,andsomepeoplespecify *bothquarterandmonth,soifitwashonoreditmight *conflictwiththesuppliedmonth.Thatisalsowhywedon't *throwanerror. * *Westillparsethesourcestringforaninteger,butit *isn'tstoredanywherein'out'.
*/ if (from_char_parse_int((int *) NULL, &s, n, escontext) < 0) return;
SKIP_THth(s, n->suffix); break; case DCH_CC: if (from_char_parse_int(&out->cc, &s, n, escontext) < 0) return;
SKIP_THth(s, n->suffix); break; case DCH_Y_YYY:
{ int matched,
years,
millennia,
nch;
matched = sscanf(s, "%d,%03d%n", &millennia, &years, &nch); if (matched < 2)
ereturn(escontext,,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("invalid value \"%s\" for \"%s\"",
s, "Y,YYY")));
/* years += (millennia * 1000); */ if (pg_mul_s32_overflow(millennia, 1000, &millennia) ||
pg_add_s32_overflow(years, millennia, &years))
ereturn(escontext,,
(errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
errmsg("value for \"%s\" in source string is out of range", "Y,YYY")));
if (!from_char_set_int(&out->year, years, n, escontext)) return;
out->yysz = 4;
s += nch;
SKIP_THth(s, n->suffix);
} break; case DCH_YYYY: case DCH_IYYY: if (from_char_parse_int(&out->year, &s, n, escontext) < 0) return;
out->yysz = 4;
SKIP_THth(s, n->suffix); break; case DCH_YYY: case DCH_IYY:
len = from_char_parse_int(&out->year, &s, n, escontext); if (len < 0) return; if (len < 4)
out->year = adjust_partial_year_to_2020(out->year);
out->yysz = 3;
SKIP_THth(s, n->suffix); break; case DCH_YY: case DCH_IY:
len = from_char_parse_int(&out->year, &s, n, escontext); if (len < 0) return; if (len < 4)
out->year = adjust_partial_year_to_2020(out->year);
out->yysz = 2;
SKIP_THth(s, n->suffix); break; case DCH_Y: case DCH_I:
len = from_char_parse_int(&out->year, &s, n, escontext); if (len < 0) return; if (len < 4)
out->year = adjust_partial_year_to_2020(out->year);
out->yysz = 1;
SKIP_THth(s, n->suffix); break; case DCH_RM: case DCH_rm: if (!from_char_seq_search(&value, &s, rm_months_lower,
NULL, InvalidOid,
n, escontext)) return; if (!from_char_set_int(&out->mm, MONTHS_PER_YEAR - value, n,
escontext)) return; break; case DCH_W: if (from_char_parse_int(&out->w, &s, n, escontext) < 0) return;
SKIP_THth(s, n->suffix); break; case DCH_J: if (from_char_parse_int(&out->j, &s, n, escontext) < 0) return;
SKIP_THth(s, n->suffix); break;
}
/* Ignore all spaces after fields */ if (!fx_mode)
{
extra_skip = 0; while (*s != '\0' && isspace((unsignedchar) *s))
{
s++;
extra_skip++;
}
}
}
/* *Standardparsingmodedoesn'tallowunmatchedformatpatternsor *trailingcharactersintheinputstring.
*/ if (std)
{ if (n->type != NODE_TYPE_END)
ereturn(escontext,,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("input string is too short for datetime format")));
while (*s != '\0' && isspace((unsignedchar) *s))
s++;
if (*s != '\0')
ereturn(escontext,,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("trailing characters remain in input string after datetime format")));
}
}
/* *TheinvariantforDCHcacheentrymanagementisthatDCHCounterisequal *tothemaximumagevalueamongtheexistingentries,andweincrementit *wheneveranaccessoccurs.Ifweapproachoverflow,dealwiththatby *halvingalltheagevalues,sothatweretainafairlyaccurateideaof *whichentriesareoldest.
*/ staticinlinevoid
DCH_prevent_counter_overflow(void)
{ if (DCHCounter >= (INT_MAX - 1))
{ for (int i = 0; i < n_DCHCache; i++)
DCHCache[i]->age >>= 1;
DCHCounter >>= 1;
}
}
for (n = node; n->type != NODE_TYPE_END; n++)
{ if (n->type != NODE_TYPE_ACTION) continue;
switch (n->key->id)
{ case DCH_FX: break; case DCH_A_M: case DCH_P_M: case DCH_a_m: case DCH_p_m: case DCH_AM: case DCH_PM: case DCH_am: case DCH_pm: case DCH_HH: case DCH_HH12: case DCH_HH24: case DCH_MI: case DCH_SS: case DCH_MS: /* millisecond */ case DCH_US: /* microsecond */ case DCH_FF1: case DCH_FF2: case DCH_FF3: case DCH_FF4: case DCH_FF5: case DCH_FF6: case DCH_SSSS:
flags |= DCH_TIMED; break; case DCH_tz: case DCH_TZ: case DCH_OF: case DCH_TZH: case DCH_TZM:
flags |= DCH_ZONED; break; case DCH_A_D: case DCH_B_C: case DCH_a_d: case DCH_b_c: case DCH_AD: case DCH_BC: case DCH_ad: case DCH_bc: case DCH_MONTH: case DCH_Month: case DCH_month: case DCH_MON: case DCH_Mon: case DCH_mon: case DCH_MM: case DCH_DAY: case DCH_Day: case DCH_day: case DCH_DY: case DCH_Dy: case DCH_dy: case DCH_DDD: case DCH_IDDD: case DCH_DD: case DCH_D: case DCH_ID: case DCH_WW: case DCH_Q: case DCH_CC: case DCH_Y_YYY: case DCH_YYYY: case DCH_IYYY: case DCH_YYY: case DCH_IYY: case DCH_YY: case DCH_IY: case DCH_Y: case DCH_I: case DCH_RM: case DCH_rm: case DCH_W: case DCH_J:
flags |= DCH_DATED; break;
}
}
return flags;
}
/* select a DCHCacheEntry to hold the given format picture */ static DCHCacheEntry *
DCH_cache_getnew(constchar *str, bool std)
{
DCHCacheEntry *ent;
/* Ensure we can advance DCHCounter below */
DCH_prevent_counter_overflow();
#ifdef DEBUG_TO_FROM_CHAR
elog(DEBUG_elog_output, "cache is full (%d)", n_DCHCache); #endif if (old->valid)
{ for (int i = 1; i < DCH_CACHE_ENTRIES; i++)
{
ent = DCHCache[i]; if (!ent->valid)
{
old = ent; break;
} if (ent->age < old->age)
old = ent;
}
} #ifdef DEBUG_TO_FROM_CHAR
elog(DEBUG_elog_output, "OLD: '%s' AGE: %d", old->str, old->age); #endif
old->valid = false;
strlcpy(old->str, str, DCH_CACHE_SIZE + 1);
old->age = (++DCHCounter); /* caller is expected to fill format, then set valid */ return old;
} else
{ #ifdef DEBUG_TO_FROM_CHAR
elog(DEBUG_elog_output, "NEW (%d)", n_DCHCache); #endif
Assert(DCHCache[n_DCHCache] == NULL);
DCHCache[n_DCHCache] = ent = (DCHCacheEntry *)
MemoryContextAllocZero(TopMemoryContext, sizeof(DCHCacheEntry));
ent->valid = false;
strlcpy(ent->str, str, DCH_CACHE_SIZE + 1);
ent->std = std;
ent->age = (++DCHCounter); /* caller is expected to fill format, then set valid */
++n_DCHCache; return ent;
}
}
/* look for an existing DCHCacheEntry matching the given format picture */ static DCHCacheEntry *
DCH_cache_search(constchar *str, bool std)
{ /* Ensure we can advance DCHCounter below */
DCH_prevent_counter_overflow();
for (int i = 0; i < n_DCHCache; i++)
{
DCHCacheEntry *ent = DCHCache[i];
/* Find or create a DCHCacheEntry for the given format picture */ static DCHCacheEntry *
DCH_cache_fetch(constchar *str, bool std)
{
DCHCacheEntry *ent;
if ((ent = DCH_cache_search(str, std)) == NULL)
{ /* *Notinthecache,mustrunparserandsaveanewformat-pictureto *thecache.Donotmarkthecacheentryvaliduntilparsing *succeeds.
*/
ent = DCH_cache_getnew(str, std);
/* ------------------- *TIMESTAMPto_char() *-------------------
*/
Datum
timestamp_to_char(PG_FUNCTION_ARGS)
{
Timestamp dt = PG_GETARG_TIMESTAMP(0);
text *fmt = PG_GETARG_TEXT_PP(1),
*res;
TmToChar tmtc; struct pg_tm tt; struct fmt_tm *tm; int thisdate;
if (VARSIZE_ANY_EXHDR(fmt) <= 0 || TIMESTAMP_NOT_FINITE(dt))
PG_RETURN_NULL();
ZERO_tmtc(&tmtc);
tm = tmtcTm(&tmtc);
if (timestamp2tm(dt, NULL, &tt, &tmtcFsec(&tmtc), NULL, NULL) != 0)
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
if (!(res = datetime_to_char_body(&tmtc, fmt, false, PG_GET_COLLATION())))
PG_RETURN_NULL();
PG_RETURN_TEXT_P(res);
}
Datum
timestamptz_to_char(PG_FUNCTION_ARGS)
{
TimestampTz dt = PG_GETARG_TIMESTAMP(0);
text *fmt = PG_GETARG_TEXT_PP(1),
*res;
TmToChar tmtc; int tz; struct pg_tm tt; struct fmt_tm *tm; int thisdate;
if (VARSIZE_ANY_EXHDR(fmt) <= 0 || TIMESTAMP_NOT_FINITE(dt))
PG_RETURN_NULL();
ZERO_tmtc(&tmtc);
tm = tmtcTm(&tmtc);
if (timestamp2tm(dt, &tz, &tt, &tmtcFsec(&tmtc), &tmtcTzn(&tmtc), NULL) != 0)
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
/* wday is meaningless, yday approximates the total span in days */
tm->tm_yday = (tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon) * DAYS_PER_MONTH + tm->tm_mday;
if (!(res = datetime_to_char_body(&tmtc, fmt, true, PG_GET_COLLATION())))
PG_RETURN_NULL();
PG_RETURN_TEXT_P(res);
}
/* --------------------- *TO_TIMESTAMP() * *MakeTimestampfromdate_strwhichisformattedatargument'fmt' *(to_timestampisreverseto_char()) *---------------------
*/
Datum
to_timestamp(PG_FUNCTION_ARGS)
{
text *date_txt = PG_GETARG_TEXT_PP(0);
text *fmt = PG_GETARG_TEXT_PP(1);
Oid collid = PG_GET_COLLATION();
Timestamp result; int tz; struct pg_tm tm; struct fmt_tz ftz;
fsec_t fsec; int fprec;
/* Prevent overflow in Julian-day routines */ if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday))
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("date out of range: \"%s\"",
text_to_cstring(date_txt))));
result = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - POSTGRES_EPOCH_JDATE;
/* Now check for just-out-of-range dates */ if (!IS_VALID_DATE(result))
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("date out of range: \"%s\"",
text_to_cstring(date_txt))));
PG_RETURN_DATEADT(result);
}
/* *Convertthe'date_txt'inputtoadatetimetypeusingargument'fmt' *asaformatstring.Thecollation'collid'maybeusedforcase-folding *rulesinsomecases.'strict'specifiesstandardparsingmode. * *Theactualdatatype(returnedin'typid','typmod')isdeterminedby *thepresenceofdate/time/zonecomponentsintheformatstring. * *Whenatimezonecomponentispresent,thecorrespondingoffsetis *returnedin'*tz'. * *IfescontextpointstoanErrorSaveContext,dataerrorswillbereported *byfillingthatstruct;thecallermusttestSOFT_ERROR_OCCURRED()tosee *whetheranerroroccurred.Otherwise,errorsarethrown.
*/
Datum
parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict,
Oid *typid, int32 *typmod, int *tz,
Node *escontext)
{ struct pg_tm tm; struct fmt_tz ftz;
fsec_t fsec; int fprec;
uint32 flags;
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("missing time zone in input string for type timestamptz")));
}
if (tm2timestamp(&tm, fsec, tz, &result) != 0)
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamptz out of range")));
if (tm2timestamp(&tm, fsec, NULL, &result) != 0)
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
*typid = TIMESTAMPOID; return TimestampGetDatum(result);
}
} else
{ if (flags & DCH_ZONED)
{
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("datetime format is zoned but not timed")));
} else
{
DateADT result;
/* Prevent overflow in Julian-day routines */ if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday))
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("date out of range: \"%s\"",
text_to_cstring(date_txt))));
result = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) -
POSTGRES_EPOCH_JDATE;
/* Now check for just-out-of-range dates */ if (!IS_VALID_DATE(result))
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("date out of range: \"%s\"",
text_to_cstring(date_txt))));
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("missing time zone in input string for type timetz")));
}
if (tm2timetz(&tm, fsec, *tz, result) != 0)
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timetz out of range")));
if (tm2time(&tm, fsec, &result) != 0)
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("time out of range")));
AdjustTimeForTypmod(&result, *typmod);
*typid = TIMEOID; return TimeADTGetDatum(result);
}
} else
{
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("datetime format is not dated and not timed")));
}
}
/* *Parsesthedatetimeformatstringin'fmt_str'andreturnstrueifit *containsatimezonespecifier,falseifnot.
*/ bool
datetime_format_has_tz(constchar *fmt_str)
{ bool incache; int fmt_len = strlen(fmt_str); int result;
FormatNode *format;
/* *Convertto_date/to_timestampinputfieldstostandard'tm'
*/ if (tmfc.ssss)
{ int x = tmfc.ssss;
tm->tm_hour = x / SECS_PER_HOUR;
x %= SECS_PER_HOUR;
tm->tm_min = x / SECS_PER_MINUTE;
x %= SECS_PER_MINUTE;
tm->tm_sec = x;
}
if (tmfc.ss)
tm->tm_sec = tmfc.ss; if (tmfc.mi)
tm->tm_min = tmfc.mi; if (tmfc.hh)
tm->tm_hour = tmfc.hh;
if (tmfc.clock == CLOCK_12_HOUR)
{ if (tm->tm_hour < 1 || tm->tm_hour > HOURS_PER_DAY / 2)
{
errsave(escontext,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("hour \"%d\" is invalid for the 12-hour clock",
tm->tm_hour),
errhint("Use the 24-hour clock, or give an hour between 1 and 12."))); goto fail;
}
if (!tm->tm_year && !tmfc.bc)
{
errsave(escontext,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("cannot calculate day of year without year information"))); goto fail;
}
if (tmfc.mode == FROM_CHAR_DATE_ISOWEEK)
{ int j0; /* zeroth day of the ISO year, in Julian */
/* Range-check date fields according to bit mask computed above */ if (fmask != 0)
{ /* We already dealt with AD/BC, so pass isjulian = true */ int dterr = ValidateDate(fmask, true, false, false, tm);
/* This works the same as DCH_prevent_counter_overflow */ staticinlinevoid
NUM_prevent_counter_overflow(void)
{ if (NUMCounter >= (INT_MAX - 1))
{ for (int i = 0; i < n_NUMCache; i++)
NUMCache[i]->age >>= 1;
NUMCounter >>= 1;
}
}
/* select a NUMCacheEntry to hold the given format picture */ static NUMCacheEntry *
NUM_cache_getnew(constchar *str)
{
NUMCacheEntry *ent;
/* Ensure we can advance NUMCounter below */
NUM_prevent_counter_overflow();
#ifdef DEBUG_TO_FROM_CHAR
elog(DEBUG_elog_output, "Cache is full (%d)", n_NUMCache); #endif if (old->valid)
{ for (int i = 1; i < NUM_CACHE_ENTRIES; i++)
{
ent = NUMCache[i]; if (!ent->valid)
{
old = ent; break;
} if (ent->age < old->age)
old = ent;
}
} #ifdef DEBUG_TO_FROM_CHAR
elog(DEBUG_elog_output, "OLD: \"%s\" AGE: %d", old->str, old->age); #endif
old->valid = false;
strlcpy(old->str, str, NUM_CACHE_SIZE + 1);
old->age = (++NUMCounter); /* caller is expected to fill format and Num, then set valid */ return old;
} else
{ #ifdef DEBUG_TO_FROM_CHAR
elog(DEBUG_elog_output, "NEW (%d)", n_NUMCache); #endif
Assert(NUMCache[n_NUMCache] == NULL);
NUMCache[n_NUMCache] = ent = (NUMCacheEntry *)
MemoryContextAllocZero(TopMemoryContext, sizeof(NUMCacheEntry));
ent->valid = false;
strlcpy(ent->str, str, NUM_CACHE_SIZE + 1);
ent->age = (++NUMCounter); /* caller is expected to fill format and Num, then set valid */
++n_NUMCache; return ent;
}
}
/* look for an existing NUMCacheEntry matching the given format picture */ static NUMCacheEntry *
NUM_cache_search(constchar *str)
{ /* Ensure we can advance NUMCounter below */
NUM_prevent_counter_overflow();
for (int i = 0; i < n_NUMCache; i++)
{
NUMCacheEntry *ent = NUMCache[i];
if (len > NUM_CACHE_SIZE)
{ /* *Allocatenewmemoryifformatpictureisbiggerthanstaticcache *anddonotusecache(callparseralways)
*/
format = palloc_array(FormatNode, len + 1);
result = (char *) palloc(MAX_ROMAN_LEN + 1);
*result = '\0';
/* *ThisrangelimitisthesameasinOracle(TM).Thedifficultywith *handling4000ormoreisthatwe'dneedtousemorethan3"M"'s,and *morethan3ofthesamedigitisn'tconsideredavalidRomanstring.
*/ if (number > 3999 || number < 1)
{
fill_str(result, '#', MAX_ROMAN_LEN); return result;
}
/* Convert to decimal, then examine each digit */
len = snprintf(numstr, sizeof(numstr), "%d", number);
Assert(len > 0 && len <= 4);
for (p = numstr; *p != '\0'; p++, --len)
{
num = *p - ('0' + 1); if (num < 0) continue; /* ignore zeroes */ /* switch on current column position */ switch (len)
{ case4: while (num-- >= 0)
strcat(result, "M"); break; case3:
strcat(result, rm100[num]); break; case2:
strcat(result, rm10[num]); break; case1:
strcat(result, rm1[num]); break;
}
} return result;
}
/* *Convertaromannumeral(standardform)toaninteger. *Resultisanintegerbetween1and3999. *Np->inout_pisadvancedpastthecharactersconsumed. * *Ifinputisinvalid,return-1.
*/ staticint
roman_to_int(NUMProc *Np, int input_len)
{ int result = 0; int len; char romanChars[MAX_ROMAN_LEN]; int romanValues[MAX_ROMAN_LEN]; int repeatCount = 1; int vCount = 0,
lCount = 0,
dCount = 0; bool subtractionEncountered = false; int lastSubtractedValue = 0;
/* *Skipanyleadingwhitespace.Perhapsweshouldlimittheamountof *spaceskippedtoMAX_ROMAN_LEN,butthatseemsunnecessarilypicky.
*/ while (!OVERLOAD_TEST && isspace((unsignedchar) *Np->inout_p))
Np->inout_p++;
/* *Collectanddecodevalidromannumerals,consumingatmost *MAX_ROMAN_LENcharacters.Wedothisinaseparatelooptoavoid *repeateddecodingandbecausethemainloopneedstoknowwhenit'sat *thelastnumeral.
*/ for (len = 0; len < MAX_ROMAN_LEN && !OVERLOAD_TEST; len++)
{ char currChar = pg_ascii_toupper(*Np->inout_p); int currValue = ROMAN_VAL(currChar);
if (currValue == 0) break; /* Not a valid roman numeral. */
romanChars[len] = currChar;
romanValues[len] = currValue;
Np->inout_p++;
}
if (len == 0) return -1; /* No valid roman numerals. */
/* Check for valid combinations and compute the represented value. */ for (int i = 0; i < len; i++)
{ char currChar = romanChars[i]; int currValue = romanValues[i];
/* Update state. */
repeatCount = 1;
subtractionEncountered = true;
lastSubtractedValue = currValue;
result += (nextValue - currValue);
} else
{ /* For same numerals, check for repetition. */ if (currChar == nextChar)
{
repeatCount++; if (repeatCount > 3) return -1;
} else
repeatCount = 1;
result += currValue;
}
} else
{ /* This is the last numeral; just add it to the result. */
result += currValue;
}
}
/* Truncate symbol if it's potentially too long */ if (unlikely(pattern_len > NUM_MAX_ITEM_SIZ))
pattern_len = pg_mbcliplen(pattern, pattern_len,
NUM_MAX_ITEM_SIZ);
memcpy(Np->inout_p, pattern, pattern_len);
Np->inout_p += pattern_len;
}
/* *Skipover"n"inputcharacters,butonlyiftheyaren'tnumericdata
*/ staticvoid
NUM_eat_non_data_chars(NUMProc *Np, int n, int input_len)
{ constchar *end = Np->inout + input_len;
while (n-- > 0)
{ if (OVERLOAD_TEST) break; /* end of input */ if (strchr("0123456789.,+-", *Np->inout_p) != NULL) break; /* it's a data character */
Np->inout_p += pg_mblen_range(Np->inout_p, end);
}
}
staticchar *
NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number, int input_len, int to_char_out_pre_spaces, int sign, bool is_to_char, Oid collid)
{
FormatNode *n;
NUMProc _Np,
*Np = &_Np; constchar *pattern; int pattern_len;
if (IS_EEEE(Np->Num))
{ if (!Np->is_to_char)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("\"EEEE\" not supported for input"))); return strcpy(inout, number);
}
/* *Sign
*/ if (is_to_char)
{
Np->sign = sign;
/* MI/PL/SG - write sign itself and not in number */ if (IS_PLUS(Np->Num) || IS_MINUS(Np->Num))
{ if (IS_PLUS(Np->Num) && IS_MINUS(Np->Num) == false)
Np->sign_wrote = false; /* need sign */ else
Np->sign_wrote = true; /* needn't sign */
} else
{ if (Np->sign != '-')
{ if (IS_FILLMODE(Np->Num))
Np->Num->flag &= ~NUM_F_BRACKET;
}
/* *Processordirectcycle
*/ if (Np->is_to_char)
Np->number_p = Np->number; else
Np->number_p = Np->number + 1; /* first char is space for sign */
for (n = node, Np->inout_p = Np->inout; n->type != NODE_TYPE_END; n++)
{ if (!Np->is_to_char)
{ /* *Checkatleastonebyteremainstobescanned.(Inactions *below,mustuseAMOUNT_TESTifwewanttoreadmorebytesthan *that.)
*/ if (OVERLOAD_TEST) break;
}
/* *Formatpicturesactions
*/ if (n->type == NODE_TYPE_ACTION)
{ /* *Create/readdigit/zero/blank/sign/special-case * *'NUM_S'note:Thelocalesignisanchoredtonumberandwe *read/writeitwhenweworkwithfirstorlastnumber *(NUM_0/NUM_9).ThisiswhyNUM_Sismissinginswitch(). * *Noticethe"Np->inout_p++"atthebottomoftheloop.Thisis *whymostoftheactionsadvanceinout_ponelessthanyoumight *expect.Incaseswherewedon'twantthatincrementtohappen, *aswitchcaseendswith"continue"not"break".
*/ switch (n->key->id)
{ case NUM_9: case NUM_0: case NUM_DEC: case NUM_D: if (Np->is_to_char)
{
NUM_numpart_to_char(Np, n->key->id); continue; /* for() */
} else
{
NUM_numpart_from_char(Np, n->key->id, input_len); break; /* switch() case: */
}
case NUM_COMMA: if (Np->is_to_char)
{ if (!Np->num_in)
{ if (IS_FILLMODE(Np->Num)) continue; else
*Np->inout_p = ' ';
} else
*Np->inout_p = ',';
} else
{ if (!Np->num_in)
{ if (IS_FILLMODE(Np->Num)) continue;
} if (*Np->inout_p != ',') continue;
} break;
case NUM_G:
pattern = Np->L_thousands_sep;
pattern_len = strlen(pattern); if (Np->is_to_char)
{ /* Truncate symbol if it's potentially too long */ if (unlikely(pattern_len > NUM_MAX_ITEM_SIZ))
pattern_len = pg_mbcliplen(pattern, pattern_len,
NUM_MAX_ITEM_SIZ); if (!Np->num_in)
{ if (IS_FILLMODE(Np->Num)) continue; else
{ /* just in case there are MB chars */
pattern_len = pg_mbstrlen_with_len(pattern,
pattern_len);
memset(Np->inout_p, ' ', pattern_len);
Np->inout_p += pattern_len - 1;
}
} else
{
memcpy(Np->inout_p, pattern, pattern_len);
Np->inout_p += pattern_len - 1;
}
} else
{ /* Here we do not truncate the symbol ... */ if (!Np->num_in)
{ if (IS_FILLMODE(Np->Num)) continue;
}
case NUM_L:
pattern = Np->L_currency_symbol; if (Np->is_to_char)
{ /* Truncate symbol if it's potentially too long */
pattern_len = strlen(pattern); if (unlikely(pattern_len > NUM_MAX_ITEM_SIZ))
pattern_len = pg_mbcliplen(pattern, pattern_len,
NUM_MAX_ITEM_SIZ);
memcpy(Np->inout_p, pattern, pattern_len);
Np->inout_p += pattern_len - 1;
} else
{ /* Here we do not truncate the symbol ... */
NUM_eat_non_data_chars(Np, pg_mbstrlen(pattern), input_len); continue;
} break;
case NUM_RN: case NUM_rn: if (Np->is_to_char)
{ constchar *number_p;
if (n->key->id == NUM_rn)
number_p = asc_tolower_z(Np->number_p); else
number_p = Np->number_p; if (IS_FILLMODE(Np->Num))
strcpy(Np->inout_p, number_p); else
sprintf(Np->inout_p, "%15s", number_p);
Np->inout_p += strlen(Np->inout_p) - 1;
} else
{ int roman_result = roman_to_int(Np, input_len); int numlen;
if (roman_result < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid Roman numeral")));
numlen = sprintf(Np->number_p, "%d", roman_result);
Np->number_p += numlen;
Np->Num->pre = numlen;
Np->Num->post = 0; continue; /* roman_to_int ate all the chars */
} break;
case NUM_th: if (IS_ROMAN(Np->Num) || *Np->number == '#' ||
Np->sign == '-' || IS_DECIMAL(Np->Num)) continue;
if (Np->is_to_char)
{
strcpy(Np->inout_p, get_th(Np->number, TH_LOWER));
Np->inout_p += 1;
} else
{ /* All variants of 'th' occupy 2 characters */
NUM_eat_non_data_chars(Np, 2, input_len); continue;
} break;
case NUM_TH: if (IS_ROMAN(Np->Num) || *Np->number == '#' ||
Np->sign == '-' || IS_DECIMAL(Np->Num)) continue;
if (Np->is_to_char)
{
strcpy(Np->inout_p, get_th(Np->number, TH_UPPER));
Np->inout_p += 1;
} else
{ /* All variants of 'TH' occupy 2 characters */
NUM_eat_non_data_chars(Np, 2, input_len); continue;
} break;
case NUM_MI: if (Np->is_to_char)
{ if (Np->sign == '-')
*Np->inout_p = '-'; elseif (IS_FILLMODE(Np->Num)) continue; else
*Np->inout_p = ' ';
} else
{ if (*Np->inout_p == '-')
*Np->number = '-'; else
{
NUM_eat_non_data_chars(Np, 1, input_len); continue;
}
} break;
case NUM_PL: if (Np->is_to_char)
{ if (Np->sign == '+')
*Np->inout_p = '+'; elseif (IS_FILLMODE(Np->Num)) continue; else
*Np->inout_p = ' ';
} else
{ if (*Np->inout_p == '+')
*Np->number = '+'; else
{
NUM_eat_non_data_chars(Np, 1, input_len); continue;
}
} break;
/* ---------- *MACRO:StartpartofNUM-forallNUM'sto_charvariants *(sorry,butIhatecopysamecode-macroisbetter..) *----------
*/ #define NUM_TOCHAR_prepare \ do { \ int len = VARSIZE_ANY_EXHDR(fmt); \ if (len <= 0 || len >= (INT_MAX-VARHDRSZ)/NUM_MAX_ITEM_SIZ) \
PG_RETURN_TEXT_P(cstring_to_text("")); \
result = (text *) palloc0((len * NUM_MAX_ITEM_SIZ) + 1 + VARHDRSZ); \
format = NUM_cache(len, &Num, fmt, &shouldFree); \
} while (0)
/* ---------- *MACRO:FinishpartofNUM *----------
*/ #define NUM_TOCHAR_finish \ do { \ int len; \
\
NUM_processor(format, &Num, VARDATA(result), numstr, 0, out_pre_spaces, sign, true, PG_GET_COLLATION()); \
\ if (shouldFree) \
pfree(format); \
\ /* \ *Convertnull-terminatedrepresentationofresulttostandardtext.\ *Theresultisusuallymuchbiggerthanitneedstobe,butthere\ *seemslittlepointinrealloc'ingitsmaller.\
*/ \
len = strlen(VARDATA(result)); \
SET_VARSIZE(result, len + VARHDRSZ); \
} while (0)
/* ------------------- *NUMERICto_number()(convertstringtonumeric) *-------------------
*/
Datum
numeric_to_number(PG_FUNCTION_ARGS)
{
text *value = PG_GETARG_TEXT_PP(0);
text *fmt = PG_GETARG_TEXT_PP(1);
NUMDesc Num;
Datum result;
FormatNode *format; char *numstr; bool shouldFree; int len = 0; int scale,
precision;
len = VARSIZE_ANY_EXHDR(fmt);
if (len <= 0 || len >= INT_MAX / NUM_MAX_ITEM_SIZ)
PG_RETURN_NULL();
if (IS_MULTI(&Num))
{
Numeric x;
Numeric a = int64_to_numeric(10);
Numeric b = int64_to_numeric(-Num.multi);
x = DatumGetNumeric(DirectFunctionCall2(numeric_power,
NumericGetDatum(a),
NumericGetDatum(b)));
result = DirectFunctionCall2(numeric_mul,
result,
NumericGetDatum(x));
}
pfree(numstr); return result;
}
/* ------------------ *NUMERICto_char() *------------------
*/
Datum
numeric_to_char(PG_FUNCTION_ARGS)
{
Numeric value = PG_GETARG_NUMERIC(0);
text *fmt = PG_GETARG_TEXT_PP(1);
NUMDesc Num;
FormatNode *format;
text *result; bool shouldFree; int out_pre_spaces = 0,
sign = 0; char *numstr,
*orgnum,
*p;
NUM_TOCHAR_prepare;
/* *OnDateTypedependpart(numeric)
*/ if (IS_ROMAN(&Num))
{
int32 intvalue; bool err;
/* Round and convert to int */
intvalue = numeric_int4_opt_error(value, &err); /* On overflow, just use PG_INT32_MAX; int_to_roman will cope */ if (err)
intvalue = PG_INT32_MAX;
numstr = int_to_roman(intvalue);
} elseif (IS_EEEE(&Num))
{
orgnum = numeric_out_sci(value, Num.post);
/* --------------- *INT4to_char() *---------------
*/
Datum
int4_to_char(PG_FUNCTION_ARGS)
{
int32 value = PG_GETARG_INT32(0);
text *fmt = PG_GETARG_TEXT_PP(1);
NUMDesc Num;
FormatNode *format;
text *result; bool shouldFree; int out_pre_spaces = 0,
sign = 0; char *numstr,
*orgnum;
NUM_TOCHAR_prepare;
/* *OnDateTypedependpart(int32)
*/ if (IS_ROMAN(&Num))
numstr = int_to_roman(value); elseif (IS_EEEE(&Num))
{ /* we can do it easily because float8 won't lose any precision */
float8 val = (float8) value;
/* --------------- *INT8to_char() *---------------
*/
Datum
int8_to_char(PG_FUNCTION_ARGS)
{
int64 value = PG_GETARG_INT64(0);
text *fmt = PG_GETARG_TEXT_PP(1);
NUMDesc Num;
FormatNode *format;
text *result; bool shouldFree; int out_pre_spaces = 0,
sign = 0; char *numstr,
*orgnum;
NUM_TOCHAR_prepare;
/* *OnDateTypedependpart(int64)
*/ if (IS_ROMAN(&Num))
{
int32 intvalue;
/* On overflow, just use PG_INT32_MAX; int_to_roman will cope */ if (value <= PG_INT32_MAX && value >= PG_INT32_MIN)
intvalue = (int32) value; else
intvalue = PG_INT32_MAX;
numstr = int_to_roman(intvalue);
} elseif (IS_EEEE(&Num))
{ /* to avoid loss of precision, must go via numeric not float8 */
orgnum = numeric_out_sci(int64_to_numeric(value),
Num.post);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.