#include "utils/numeric.h"
#include "utils/pg_locale.h"
+/* ----------
+ * Convenience macros for error handling
+ * ----------
+ *
+ * Two macros below help to handle errors in functions that take
+ * 'bool *have_error' argument. When this argument is not NULL, it's expected
+ * that function will suppress ereports when possible. Instead it should
+ * return some default value and set *have_error flag.
+ *
+ * RETURN_ERROR() macro intended to wrap ereport() calls. When have_error
+ * function argument is not NULL, then instead of ereport'ing we set
+ * *have_error flag and go to on_error label. It's supposed that jump
+ * resources will be freed and some 'default' value returned.
+ *
+ * CHECK_ERROR() jumps on_error label when *have_error flag is defined and set.
+ * It's supposed to be used for immediate exit from the function on error
+ * after call of another function with 'bool *have_error' argument.
+ */
+#define RETURN_ERROR(throw_error) \
+do { \
+ if (have_error) \
+ { \
+ *have_error = true; \
+ goto on_error; \
+ } \
+ else \
+ { \
+ throw_error; \
+ } \
+} while (0)
+
+#define CHECK_ERROR \
+do { \
+ if (have_error && *have_error) \
+ goto on_error; \
+} while (0)
+
/* ----------
* Routines flags
* ----------
static void DCH_to_char(FormatNode *node, bool is_interval,
TmToChar *in, char *out, Oid collid);
static void DCH_from_char(FormatNode *node, char *in, TmFromChar *out,
- bool std);
+ bool std, bool *have_error);
#ifdef DEBUG_TO_FROM_CHAR
static void dump_index(const KeyWord *k, const int *index);
static char *str_numth(char *dest, char *num, int type);
static int adjust_partial_year_to_2020(int year);
static int strspace_len(char *str);
-static void from_char_set_mode(TmFromChar *tmfc, const FromCharDateMode mode);
-static void from_char_set_int(int *dest, const int value, const FormatNode *node);
-static int from_char_parse_int_len(int *dest, char **src, const int len, FormatNode *node);
-static int from_char_parse_int(int *dest, char **src, FormatNode *node);
+static void from_char_set_mode(TmFromChar *tmfc, const FromCharDateMode mode,
+ bool *have_error);
+static void from_char_set_int(int *dest, const int value, const FormatNode *node,
+ bool *have_error);
+static int from_char_parse_int_len(int *dest, char **src, const int len,
+ FormatNode *node, bool *have_error);
+static int from_char_parse_int(int *dest, char **src, FormatNode *node,
+ bool *have_error);
static int seq_search(char *name, const char *const *array, int type, int max, int *len);
-static int from_char_seq_search(int *dest, char **src, const char *const *array, int type, int max, FormatNode *node);
+static int from_char_seq_search(int *dest, char **src,
+ const char *const *array, int type, int max,
+ FormatNode *node, bool *have_error);
static void do_to_timestamp(text *date_txt, text *fmt, bool std,
struct pg_tm *tm, fsec_t *fsec, int *fprec,
- uint32 *flags);
+ uint32 *flags, bool *have_error);
static char *fill_str(char *str, int c, int max);
static FormatNode *NUM_cache(int len, NUMDesc *Num, text *pars_str, bool *shouldFree);
static char *int_to_roman(int number);
*
* Puke if the date mode has already been set, and the caller attempts to set
* it to a conflicting mode.
+ *
+ * If 'have_error' is NULL, then errors are thrown, else '*have_error' is set.
*/
static void
-from_char_set_mode(TmFromChar *tmfc, const FromCharDateMode mode)
+from_char_set_mode(TmFromChar *tmfc, const FromCharDateMode mode, bool *have_error)
{
if (mode != FROM_CHAR_DATE_NONE)
{
if (tmfc->mode == FROM_CHAR_DATE_NONE)
tmfc->mode = mode;
else if (tmfc->mode != mode)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
- errmsg("invalid combination of date conventions"),
- errhint("Do not mix Gregorian and ISO week date "
- "conventions in a formatting template.")));
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+ errmsg("invalid combination of date conventions"),
+ errhint("Do not mix Gregorian and ISO week date "
+ "conventions in a formatting template."))));
}
+
+on_error:
+ return;
}
/*
*
* Puke if the destination integer has previously been set to some other
* non-zero value.
+ *
+ * If 'have_error' is NULL, then errors are thrown, else '*have_error' is set.
*/
static void
-from_char_set_int(int *dest, const int value, const FormatNode *node)
+from_char_set_int(int *dest, const int value, const FormatNode *node,
+ bool *have_error)
{
if (*dest != 0 && *dest != value)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
- errmsg("conflicting values for \"%s\" field in formatting string",
- node->key->name),
- errdetail("This value contradicts a previous setting for "
- "the same field type.")));
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+ errmsg("conflicting values for \"%s\" field in "
+ "formatting string",
+ node->key->name),
+ errdetail("This value contradicts a previous setting "
+ "for the same field type."))));
*dest = value;
+
+on_error:
+ return;
}
/*
* Note that from_char_parse_int() provides a more convenient wrapper where
* the length of the field is the same as the length of the format keyword (as
* with DD and MI).
+ *
+ * If 'have_error' is NULL, then errors are thrown, else '*have_error' is set
+ * and -1 is returned.
*/
static int
-from_char_parse_int_len(int *dest, char **src, const int len, FormatNode *node)
+from_char_parse_int_len(int *dest, char **src, const int len, FormatNode *node,
+ bool *have_error)
{
long result;
char copy[DCH_MAX_ITEM_SIZ + 1];
char *last;
if (used < len)
- ereport(ERROR,
- (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.")));
+ RETURN_ERROR(ereport(ERROR,
+ (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)
- ereport(ERROR,
- (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.")));
+ RETURN_ERROR(ereport(ERROR,
+ (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)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
- errmsg("invalid value \"%s\" for \"%s\"",
- copy, node->key->name),
- errdetail("Value must be an integer.")));
+ RETURN_ERROR(ereport(ERROR,
+ (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)
- ereport(ERROR,
- (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)));
+ RETURN_ERROR(ereport(ERROR,
+ (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)
- from_char_set_int(dest, (int) result, node);
+ {
+ from_char_set_int(dest, (int) result, node, have_error);
+ CHECK_ERROR;
+ }
+
return *src - init;
+
+on_error:
+ return -1;
}
/*
* required length explicitly.
*/
static int
-from_char_parse_int(int *dest, char **src, FormatNode *node)
+from_char_parse_int(int *dest, char **src, FormatNode *node, bool *have_error)
{
- return from_char_parse_int_len(dest, src, node->key->len, node);
+ return from_char_parse_int_len(dest, src, node->key->len, node, have_error);
}
/* ----------
* pointed to by 'dest', advance 'src' to the end of the part of the string
* which matched, and return the number of characters consumed.
*
- * If the string doesn't match, throw an error.
+ * If the string doesn't match, throw an error if 'have_error' is NULL,
+ * otherwise set '*have_error' and return -1.
*/
static int
-from_char_seq_search(int *dest, char **src, const char *const *array, int type, int max,
- FormatNode *node)
+from_char_seq_search(int *dest, char **src, const char *const *array, int type,
+ int max, FormatNode *node, bool *have_error)
{
int len;
Assert(max <= DCH_MAX_ITEM_SIZ);
strlcpy(copy, *src, max + 1);
- ereport(ERROR,
- (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.")));
+ RETURN_ERROR(ereport(ERROR,
+ (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;
return len;
+
+on_error:
+ return -1;
}
/* ----------
*
* Note: we currently don't have any to_interval() function, so there
* is no need here for INVALID_FOR_INTERVAL checks.
+ *
+ * If 'have_error' is NULL, then errors are thrown, else '*have_error' is set.
* ----------
*/
static void
-DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool std)
+DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool std,
+ bool *have_error)
{
FormatNode *n;
char *s;
if (*s == n->character[0])
s++;
else
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
- errmsg("unmatched format separator \"%c\"",
- n->character[0])));
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+ errmsg("unmatched format separator \"%c\"",
+ n->character[0]))));
}
else if (!fx_mode)
{
continue;
}
- from_char_set_mode(out, n->key->date_mode);
+ from_char_set_mode(out, n->key->date_mode, have_error);
+ CHECK_ERROR;
switch (n->key->id)
{
case DCH_a_m:
case DCH_p_m:
from_char_seq_search(&value, &s, ampm_strings_long,
- ALL_UPPER, n->key->len, n);
- from_char_set_int(&out->pm, value % 2, n);
+ ALL_UPPER, n->key->len, n, have_error);
+ CHECK_ERROR;
+ from_char_set_int(&out->pm, value % 2, n, have_error);
+ CHECK_ERROR;
out->clock = CLOCK_12_HOUR;
break;
case DCH_AM:
case DCH_am:
case DCH_pm:
from_char_seq_search(&value, &s, ampm_strings,
- ALL_UPPER, n->key->len, n);
- from_char_set_int(&out->pm, value % 2, n);
+ ALL_UPPER, n->key->len, n, have_error);
+ CHECK_ERROR;
+ from_char_set_int(&out->pm, value % 2, n, have_error);
+ CHECK_ERROR;
out->clock = CLOCK_12_HOUR;
break;
case DCH_HH:
case DCH_HH12:
- from_char_parse_int_len(&out->hh, &s, 2, n);
+ from_char_parse_int_len(&out->hh, &s, 2, n, have_error);
+ CHECK_ERROR;
out->clock = CLOCK_12_HOUR;
SKIP_THth(s, n->suffix);
break;
case DCH_HH24:
- from_char_parse_int_len(&out->hh, &s, 2, n);
+ from_char_parse_int_len(&out->hh, &s, 2, n, have_error);
+ CHECK_ERROR;
SKIP_THth(s, n->suffix);
break;
case DCH_MI:
- from_char_parse_int(&out->mi, &s, n);
+ from_char_parse_int(&out->mi, &s, n, have_error);
+ CHECK_ERROR;
SKIP_THth(s, n->suffix);
break;
case DCH_SS:
- from_char_parse_int(&out->ss, &s, n);
+ from_char_parse_int(&out->ss, &s, n, have_error);
+ CHECK_ERROR;
SKIP_THth(s, n->suffix);
break;
case DCH_MS: /* millisecond */
- len = from_char_parse_int_len(&out->ms, &s, 3, n);
+ len = from_char_parse_int_len(&out->ms, &s, 3, n, have_error);
+ CHECK_ERROR;
/*
* 25 is 0.25 and 250 is 0.25 too; 025 is 0.025 and not 0.25
case DCH_US: /* microsecond */
len = from_char_parse_int_len(&out->us, &s,
n->key->id == DCH_US ? 6 :
- out->ff, n);
+ out->ff, n, have_error);
+ CHECK_ERROR;
out->us *= len == 1 ? 100000 :
len == 2 ? 10000 :
SKIP_THth(s, n->suffix);
break;
case DCH_SSSS:
- from_char_parse_int(&out->ssss, &s, n);
+ from_char_parse_int(&out->ssss, &s, n, have_error);
+ CHECK_ERROR;
SKIP_THth(s, n->suffix);
break;
case DCH_tz:
case DCH_TZ:
case DCH_OF:
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("formatting field \"%s\" is only supported in to_char",
- n->key->name)));
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("formatting field \"%s\" is only supported in to_char",
+ n->key->name))));
+ CHECK_ERROR;
break;
case DCH_TZH:
out->tzsign = +1;
}
- from_char_parse_int_len(&out->tzh, &s, 2, n);
+ from_char_parse_int_len(&out->tzh, &s, 2, n, have_error);
+ CHECK_ERROR;
break;
case DCH_TZM:
/* assign positive timezone sign if TZH was not seen before */
if (!out->tzsign)
out->tzsign = +1;
- from_char_parse_int_len(&out->tzm, &s, 2, n);
+ from_char_parse_int_len(&out->tzm, &s, 2, n, have_error);
+ CHECK_ERROR;
break;
case DCH_A_D:
case DCH_B_C:
case DCH_a_d:
case DCH_b_c:
from_char_seq_search(&value, &s, adbc_strings_long,
- ALL_UPPER, n->key->len, n);
- from_char_set_int(&out->bc, value % 2, n);
+ ALL_UPPER, n->key->len, n, have_error);
+ CHECK_ERROR;
+ from_char_set_int(&out->bc, value % 2, n, have_error);
+ CHECK_ERROR;
break;
case DCH_AD:
case DCH_BC:
case DCH_ad:
case DCH_bc:
from_char_seq_search(&value, &s, adbc_strings,
- ALL_UPPER, n->key->len, n);
- from_char_set_int(&out->bc, value % 2, n);
+ ALL_UPPER, n->key->len, n, have_error);
+ CHECK_ERROR;
+ from_char_set_int(&out->bc, value % 2, n, have_error);
+ CHECK_ERROR;
break;
case DCH_MONTH:
case DCH_Month:
case DCH_month:
from_char_seq_search(&value, &s, months_full, ONE_UPPER,
- MAX_MONTH_LEN, n);
- from_char_set_int(&out->mm, value + 1, n);
+ MAX_MONTH_LEN, n, have_error);
+ CHECK_ERROR;
+ from_char_set_int(&out->mm, value + 1, n, have_error);
+ CHECK_ERROR;
break;
case DCH_MON:
case DCH_Mon:
case DCH_mon:
from_char_seq_search(&value, &s, months, ONE_UPPER,
- MAX_MON_LEN, n);
- from_char_set_int(&out->mm, value + 1, n);
+ MAX_MON_LEN, n, have_error);
+ CHECK_ERROR;
+ from_char_set_int(&out->mm, value + 1, n, have_error);
+ CHECK_ERROR;
break;
case DCH_MM:
- from_char_parse_int(&out->mm, &s, n);
+ from_char_parse_int(&out->mm, &s, n, have_error);
+ CHECK_ERROR;
SKIP_THth(s, n->suffix);
break;
case DCH_DAY:
case DCH_Day:
case DCH_day:
from_char_seq_search(&value, &s, days, ONE_UPPER,
- MAX_DAY_LEN, n);
- from_char_set_int(&out->d, value, n);
+ MAX_DAY_LEN, n, have_error);
+ CHECK_ERROR;
+ from_char_set_int(&out->d, value, n, have_error);
+ CHECK_ERROR;
out->d++;
break;
case DCH_DY:
case DCH_Dy:
case DCH_dy:
from_char_seq_search(&value, &s, days, ONE_UPPER,
- MAX_DY_LEN, n);
- from_char_set_int(&out->d, value, n);
+ MAX_DY_LEN, n, have_error);
+ CHECK_ERROR;
+ from_char_set_int(&out->d, value, n, have_error);
+ CHECK_ERROR;
out->d++;
break;
case DCH_DDD:
- from_char_parse_int(&out->ddd, &s, n);
+ from_char_parse_int(&out->ddd, &s, n, have_error);
+ CHECK_ERROR;
SKIP_THth(s, n->suffix);
break;
case DCH_IDDD:
- from_char_parse_int_len(&out->ddd, &s, 3, n);
+ from_char_parse_int_len(&out->ddd, &s, 3, n, have_error);
+ CHECK_ERROR;
SKIP_THth(s, n->suffix);
break;
case DCH_DD:
- from_char_parse_int(&out->dd, &s, n);
+ from_char_parse_int(&out->dd, &s, n, have_error);
+ CHECK_ERROR;
SKIP_THth(s, n->suffix);
break;
case DCH_D:
- from_char_parse_int(&out->d, &s, n);
+ from_char_parse_int(&out->d, &s, n, have_error);
+ CHECK_ERROR;
SKIP_THth(s, n->suffix);
break;
case DCH_ID:
- from_char_parse_int_len(&out->d, &s, 1, n);
+ from_char_parse_int_len(&out->d, &s, 1, n, have_error);
+ CHECK_ERROR;
/* Shift numbering to match Gregorian where Sunday = 1 */
if (++out->d > 7)
out->d = 1;
break;
case DCH_WW:
case DCH_IW:
- from_char_parse_int(&out->ww, &s, n);
+ from_char_parse_int(&out->ww, &s, n, have_error);
+ CHECK_ERROR;
SKIP_THth(s, n->suffix);
break;
case DCH_Q:
* We still parse the source string for an integer, but it
* isn't stored anywhere in 'out'.
*/
- from_char_parse_int((int *) NULL, &s, n);
+ from_char_parse_int((int *) NULL, &s, n, have_error);
+ CHECK_ERROR;
SKIP_THth(s, n->suffix);
break;
case DCH_CC:
- from_char_parse_int(&out->cc, &s, n);
+ from_char_parse_int(&out->cc, &s, n, have_error);
+ CHECK_ERROR;
SKIP_THth(s, n->suffix);
break;
case DCH_Y_YYY:
matched = sscanf(s, "%d,%03d%n", &millennia, &years, &nch);
if (matched < 2)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
- errmsg("invalid input string for \"Y,YYY\"")));
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+ errmsg("invalid input string for \"Y,YYY\""))));
years += (millennia * 1000);
- from_char_set_int(&out->year, years, n);
+ from_char_set_int(&out->year, years, n, have_error);
+ CHECK_ERROR;
out->yysz = 4;
s += nch;
SKIP_THth(s, n->suffix);
break;
case DCH_YYYY:
case DCH_IYYY:
- from_char_parse_int(&out->year, &s, n);
+ from_char_parse_int(&out->year, &s, n, have_error);
+ CHECK_ERROR;
out->yysz = 4;
SKIP_THth(s, n->suffix);
break;
case DCH_YYY:
case DCH_IYY:
- if (from_char_parse_int(&out->year, &s, n) < 4)
+ len = from_char_parse_int(&out->year, &s, n, have_error);
+ CHECK_ERROR;
+ 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:
- if (from_char_parse_int(&out->year, &s, n) < 4)
+ len = from_char_parse_int(&out->year, &s, n, have_error);
+ CHECK_ERROR;
+ 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:
- if (from_char_parse_int(&out->year, &s, n) < 4)
+ len = from_char_parse_int(&out->year, &s, n, have_error);
+ CHECK_ERROR;
+ if (len < 4)
out->year = adjust_partial_year_to_2020(out->year);
out->yysz = 1;
SKIP_THth(s, n->suffix);
break;
case DCH_RM:
from_char_seq_search(&value, &s, rm_months_upper,
- ALL_UPPER, MAX_RM_LEN, n);
- from_char_set_int(&out->mm, MONTHS_PER_YEAR - value, n);
+ ALL_UPPER, MAX_RM_LEN, n, have_error);
+ CHECK_ERROR;
+ from_char_set_int(&out->mm, MONTHS_PER_YEAR - value,
+ n, have_error);
+ CHECK_ERROR;
break;
case DCH_rm:
from_char_seq_search(&value, &s, rm_months_lower,
- ALL_LOWER, MAX_RM_LEN, n);
- from_char_set_int(&out->mm, MONTHS_PER_YEAR - value, n);
+ ALL_LOWER, MAX_RM_LEN, n, have_error);
+ CHECK_ERROR;
+ from_char_set_int(&out->mm, MONTHS_PER_YEAR - value,
+ n, have_error);
+ CHECK_ERROR;
break;
case DCH_W:
- from_char_parse_int(&out->w, &s, n);
+ from_char_parse_int(&out->w, &s, n, have_error);
+ CHECK_ERROR;
SKIP_THth(s, n->suffix);
break;
case DCH_J:
- from_char_parse_int(&out->j, &s, n);
+ from_char_parse_int(&out->j, &s, n, have_error);
+ CHECK_ERROR;
SKIP_THth(s, n->suffix);
break;
}
if (std)
{
if (n->type != NODE_TYPE_END)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
- errmsg("input string is too short for datetime format")));
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+ errmsg("input string is too short for datetime format"))));
while (*s != '\0' && isspace((unsigned char) *s))
s++;
if (*s != '\0')
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
- errmsg("trailing characters remain in input string after "
- "datetime format")));
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+ errmsg("trailing characters remain in input string "
+ "after datetime format"))));
}
+
+on_error:
+ return;
}
/*
}
}
-/* Get mask of date/time/zone components present in format nodes. */
+/*
+ * Get mask of date/time/zone components present in format nodes.
+ *
+ * If 'have_error' is NULL, then errors are thrown, else '*have_error' is set.
+ */
static int
-DCH_datetime_type(FormatNode *node)
+DCH_datetime_type(FormatNode *node, bool *have_error)
{
FormatNode *n;
int flags = 0;
case DCH_tz:
case DCH_TZ:
case DCH_OF:
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("formatting field \"%s\" is only supported in to_char",
- n->key->name)));
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("formatting field \"%s\" is only supported in to_char",
+ n->key->name))));
flags |= DCH_ZONED;
break;
case DCH_TZH:
}
}
+on_error:
return flags;
}
fsec_t fsec;
int fprec;
- do_to_timestamp(date_txt, fmt, false, &tm, &fsec, &fprec, NULL);
+ do_to_timestamp(date_txt, fmt, false, &tm, &fsec, &fprec, NULL, NULL);
/* Use the specified time zone, if any. */
if (tm.tm_zone)
struct pg_tm tm;
fsec_t fsec;
- do_to_timestamp(date_txt, fmt, false, &tm, &fsec, NULL, NULL);
+ do_to_timestamp(date_txt, fmt, false, &tm, &fsec, NULL, NULL, NULL);
/* Prevent overflow in Julian-day routines */
if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday))
* the presence of date/time/zone components in the format string.
*
* When timezone component is present, the corresponding offset is set to '*tz'.
+ *
+ * If 'have_error' is NULL, then errors are thrown, else '*have_error' is set
+ * and zero value is returned.
*/
Datum
parse_datetime(text *date_txt, text *fmt, bool strict, Oid *typid,
- int32 *typmod, int *tz)
+ int32 *typmod, int *tz, bool *have_error)
{
struct pg_tm tm;
fsec_t fsec;
int fprec = 0;
uint32 flags;
- do_to_timestamp(date_txt, fmt, strict, &tm, &fsec, &fprec, &flags);
+ do_to_timestamp(date_txt, fmt, strict, &tm, &fsec, &fprec, &flags, have_error);
+ CHECK_ERROR;
*typmod = fprec ? fprec : -1; /* fractional part precision */
*/
Assert(!strict);
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
- errmsg("missing time zone in input string for type timestamptz")));
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+ errmsg("missing time zone in input string for type timestamptz"))));
}
if (tm2timestamp(&tm, fsec, tz, &result) != 0)
- ereport(ERROR,
- (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
- errmsg("timestamptz out of range")));
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("timestamptz out of range"))));
AdjustTimestampForTypmod(&result, *typmod);
Timestamp result;
if (tm2timestamp(&tm, fsec, NULL, &result) != 0)
- ereport(ERROR,
- (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
- errmsg("timestamp out of range")));
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("timestamp out of range"))));
AdjustTimestampForTypmod(&result, *typmod);
{
if (flags & DCH_ZONED)
{
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
- errmsg("datetime format is zoned but not timed")));
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+ errmsg("datetime format is zoned but not timed"))));
}
else
{
/* 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))));
+ RETURN_ERROR(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))));
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("date out of range: \"%s\"",
+ text_to_cstring(date_txt)))));
*typid = DATEOID;
return DateADTGetDatum(result);
int dterr = DecodeTimezone(unconstify(char *, tm.tm_zone), tz);
if (dterr)
- DateTimeParseError(dterr, text_to_cstring(date_txt), "timetz");
+ RETURN_ERROR(DateTimeParseError(dterr, text_to_cstring(date_txt), "timetz"));
}
else
{
*/
Assert(!strict);
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
- errmsg("missing time zone in input string for type timetz")));
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+ errmsg("missing time zone in input string for type timetz"))));
}
if (tm2timetz(&tm, fsec, *tz, result) != 0)
- ereport(ERROR,
- (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
- errmsg("timetz out of range")));
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("timetz out of range"))));
AdjustTimeForTypmod(&result->time, *typmod);
TimeADT result;
if (tm2time(&tm, fsec, &result) != 0)
- ereport(ERROR,
- (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
- errmsg("time out of range")));
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("time out of range"))));
AdjustTimeForTypmod(&result, *typmod);
}
else
{
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
- errmsg("datetime format is not dated and not timed")));
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+ errmsg("datetime format is not dated and not timed"))));
}
+on_error:
return (Datum) 0;
}
*
* The TmFromChar is then analysed and converted into the final results in
* struct 'tm' and 'fsec'.
+ *
+ * Bit mask of date/time/zone components found in 'fmt' is returned in 'flags'.
+ *
+ * 'std' specifies standard parsing mode.
+ *
+ * If 'have_error' is NULL, then errors are thrown, else '*have_error' is set.
*/
static void
do_to_timestamp(text *date_txt, text *fmt, bool std,
struct pg_tm *tm, fsec_t *fsec, int *fprec,
- uint32 *flags)
+ uint32 *flags, bool *have_error)
{
- FormatNode *format;
+ FormatNode *format = NULL;
TmFromChar tmfc;
int fmt_len;
char *date_str;
int fmask;
+ bool incache = false;
date_str = text_to_cstring(date_txt);
if (fmt_len)
{
char *fmt_str;
- bool incache;
fmt_str = text_to_cstring(fmt);
* Allocate new memory if format picture is bigger than static
* cache and do not use cache (call parser always)
*/
- incache = false;
-
format = (FormatNode *) palloc((fmt_len + 1) * sizeof(FormatNode));
parse_format(format, fmt_str, DCH_keywords, DCH_suff, DCH_index,
/* dump_index(DCH_keywords, DCH_index); */
#endif
- DCH_from_char(format, date_str, &tmfc, std);
+ DCH_from_char(format, date_str, &tmfc, std, have_error);
+ CHECK_ERROR;
pfree(fmt_str);
if (flags)
- *flags = DCH_datetime_type(format);
+ *flags = DCH_datetime_type(format, have_error);
if (!incache)
+ {
pfree(format);
+ format = NULL;
+ }
+
+ CHECK_ERROR;
}
DEBUG_TMFC(&tmfc);
if (tmfc.clock == CLOCK_12_HOUR)
{
if (tm->tm_hour < 1 || tm->tm_hour > HOURS_PER_DAY / 2)
- ereport(ERROR,
- (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.")));
+ {
+ RETURN_ERROR(ereport(ERROR,
+ (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."))));
+ }
if (tmfc.pm && tm->tm_hour < HOURS_PER_DAY / 2)
tm->tm_hour += HOURS_PER_DAY / 2;
*/
if (!tm->tm_year && !tmfc.bc)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
- errmsg("cannot calculate day of year without year information")));
+ {
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+ errmsg("cannot calculate day of year without year information"))));
+ }
if (tmfc.mode == FROM_CHAR_DATE_ISOWEEK)
{
* said DTERR_MD_FIELD_OVERFLOW, because we don't want to print an
* irrelevant hint about datestyle.
*/
- DateTimeParseError(DTERR_FIELD_OVERFLOW, date_str, "timestamp");
+ RETURN_ERROR(DateTimeParseError(DTERR_FIELD_OVERFLOW, date_str, "timestamp"));
}
}
tm->tm_min < 0 || tm->tm_min >= MINS_PER_HOUR ||
tm->tm_sec < 0 || tm->tm_sec >= SECS_PER_MINUTE ||
*fsec < INT64CONST(0) || *fsec >= USECS_PER_SEC)
- DateTimeParseError(DTERR_FIELD_OVERFLOW, date_str, "timestamp");
+ {
+ RETURN_ERROR(DateTimeParseError(DTERR_FIELD_OVERFLOW, date_str, "timestamp"));
+ }
/* Save parsed time-zone into tm->tm_zone if it was specified */
if (tmfc.tzsign)
if (tmfc.tzh < 0 || tmfc.tzh > MAX_TZDISP_HOUR ||
tmfc.tzm < 0 || tmfc.tzm >= MINS_PER_HOUR)
- DateTimeParseError(DTERR_TZDISP_OVERFLOW, date_str, "timestamp");
+ {
+ RETURN_ERROR(DateTimeParseError(DTERR_TZDISP_OVERFLOW, date_str, "timestamp"));
+ }
tz = psprintf("%c%02d:%02d",
tmfc.tzsign > 0 ? '+' : '-', tmfc.tzh, tmfc.tzm);
DEBUG_TM(tm);
+on_error:
+
+ if (format && !incache)
+ pfree(format);
+
pfree(date_str);
}