summaryrefslogtreecommitdiff
path: root/src/backend/utils
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2022-12-09 20:15:56 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2022-12-09 20:15:56 -0500
commit4dd687502d9eb0b2984c36579c2fcf5283adfa7c (patch)
treeb4150eb444a7b944308c77b6bceef311fc7cd677 /src/backend/utils
parentc60488b4748b4316f1c92d62457671046e5c8994 (diff)
downloadpostgresql-4dd687502d9eb0b2984c36579c2fcf5283adfa7c.tar.gz
Restructure soft-error handling in formatting.c.
Replace the error trapping scheme introduced in 5bc450629 with our shiny new errsave/ereturn mechanism. This doesn't have any real functional impact (although I think that the new coding is able to report a few more errors softly than v15 did). And I doubt there's any measurable performance difference either. But this gets rid of an ad-hoc, one-of-a-kind design in favor of a mechanism that will be widely used going forward, so it should be a net win for code readability. Discussion: https://postgr.es/m/3bbbb0df-7382-bf87-9737-340ba096e034@postgrespro.ru
Diffstat (limited to 'src/backend/utils')
-rw-r--r--src/backend/utils/adt/formatting.c666
-rw-r--r--src/backend/utils/adt/jsonpath_exec.c12
2 files changed, 321 insertions, 357 deletions
diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
index eba981abd8..65746c48d2 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -78,6 +78,7 @@
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
#include "mb/pg_wchar.h"
+#include "nodes/miscnodes.h"
#include "parser/scansup.h"
#include "utils/builtins.h"
#include "utils/date.h"
@@ -88,42 +89,6 @@
#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
@@ -1065,7 +1030,7 @@ static void parse_format(FormatNode *node, const char *str, const KeyWord *kw,
static void DCH_to_char(FormatNode *node, bool is_interval,
TmToChar *in, char *out, Oid collid);
static void DCH_from_char(FormatNode *node, const char *in, TmFromChar *out,
- Oid collid, bool std, bool *have_error);
+ Oid collid, bool std, Node *escontext);
#ifdef DEBUG_TO_FROM_CHAR
static void dump_index(const KeyWord *k, const int *index);
@@ -1076,24 +1041,24 @@ static const char *get_th(char *num, int type);
static char *str_numth(char *dest, char *num, int type);
static int adjust_partial_year_to_2020(int year);
static int strspace_len(const char *str);
-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 bool from_char_set_mode(TmFromChar *tmfc, const FromCharDateMode mode,
+ Node *escontext);
+static bool from_char_set_int(int *dest, const int value, const FormatNode *node,
+ Node *escontext);
static int from_char_parse_int_len(int *dest, const char **src, const int len,
- FormatNode *node, bool *have_error);
+ FormatNode *node, Node *escontext);
static int from_char_parse_int(int *dest, const char **src, FormatNode *node,
- bool *have_error);
+ Node *escontext);
static int seq_search_ascii(const char *name, const char *const *array, int *len);
static int seq_search_localized(const char *name, char **array, int *len,
Oid collid);
-static int from_char_seq_search(int *dest, const char **src,
+static bool from_char_seq_search(int *dest, const char **src,
const char *const *array,
char **localized_array, Oid collid,
- FormatNode *node, bool *have_error);
-static void do_to_timestamp(text *date_txt, text *fmt, Oid collid, bool std,
+ FormatNode *node, Node *escontext);
+static bool do_to_timestamp(text *date_txt, text *fmt, Oid collid, bool std,
struct pg_tm *tm, fsec_t *fsec, int *fprec,
- uint32 *flags, bool *have_error);
+ uint32 *flags, Node *escontext);
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);
@@ -2291,25 +2256,25 @@ strspace_len(const char *str)
* 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.
+ * Returns true on success, false on failure (if escontext points to an
+ * ErrorSaveContext; otherwise errors are thrown).
*/
-static void
-from_char_set_mode(TmFromChar *tmfc, const FromCharDateMode mode, bool *have_error)
+static bool
+from_char_set_mode(TmFromChar *tmfc, const FromCharDateMode mode,
+ Node *escontext)
{
if (mode != FROM_CHAR_DATE_NONE)
{
if (tmfc->mode == FROM_CHAR_DATE_NONE)
tmfc->mode = mode;
else if (tmfc->mode != mode)
- 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."))));
+ ereturn(escontext, false,
+ (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;
+ return true;
}
/*
@@ -2318,24 +2283,22 @@ on_error:
* 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.
+ * Returns true on success, false on failure (if escontext points to an
+ * ErrorSaveContext; otherwise errors are thrown).
*/
-static void
+static bool
from_char_set_int(int *dest, const int value, const FormatNode *node,
- bool *have_error)
+ Node *escontext)
{
if (*dest != 0 && *dest != value)
- 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."))));
+ ereturn(escontext, false,
+ (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;
+ return true;
}
/*
@@ -2352,18 +2315,16 @@ on_error:
* point at the character immediately following the last character used in the
* conversion.
*
- * Return the number of characters consumed.
+ * Returns the number of characters consumed, or -1 on error (if escontext
+ * points to an ErrorSaveContext; otherwise errors are thrown).
*
* 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, const char **src, const int len, FormatNode *node,
- bool *have_error)
+ Node *escontext)
{
long result;
char copy[DCH_MAX_ITEM_SIZ + 1];
@@ -2399,60 +2360,54 @@ from_char_parse_int_len(int *dest, const char **src, const int len, FormatNode *
char *last;
if (used < len)
- 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."))));
+ 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)
- 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."))));
+ 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)
- 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."))));
+ 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)
- 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))));
+ 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)
{
- from_char_set_int(dest, (int) result, node, have_error);
- CHECK_ERROR;
+ if (!from_char_set_int(dest, (int) result, node, escontext))
+ return -1;
}
return *src - init;
-
-on_error:
- return -1;
}
/*
@@ -2465,9 +2420,10 @@ on_error:
* required length explicitly.
*/
static int
-from_char_parse_int(int *dest, const char **src, FormatNode *node, bool *have_error)
+from_char_parse_int(int *dest, const char **src, FormatNode *node,
+ Node *escontext)
{
- return from_char_parse_int_len(dest, src, node->key->len, node, have_error);
+ return from_char_parse_int_len(dest, src, node->key->len, node, escontext);
}
/*
@@ -2613,19 +2569,19 @@ seq_search_localized(const char *name, char **array, int *len, Oid collid)
* (which will follow the rules of the collation 'collid').
*
* If a match is found, copy the array index of the match into the integer
- * pointed to by 'dest', advance 'src' to the end of the part of the string
- * which matched, and return the number of characters consumed.
+ * pointed to by 'dest' and advance 'src' to the end of the part of the string
+ * which matched.
*
- * If the string doesn't match, throw an error if 'have_error' is NULL,
- * otherwise set '*have_error' and return -1.
+ * Returns true on match, false on failure (if escontext points to an
+ * ErrorSaveContext; otherwise errors are thrown).
*
* 'node' is used only for error reports: node->key->name identifies the
* field type we were searching for.
*/
-static int
+static bool
from_char_seq_search(int *dest, const char **src, const char *const *array,
char **localized_array, Oid collid,
- FormatNode *node, bool *have_error)
+ FormatNode *node, Node *escontext)
{
int len;
@@ -2652,18 +2608,15 @@ from_char_seq_search(int *dest, const char **src, const char *const *array,
}
}
- 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."))));
+ 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;
- return len;
-
-on_error:
- return -1;
+ return true;
}
/* ----------
@@ -3310,14 +3263,17 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
*
* 'collid' identifies the collation to use, if needed.
* 'std' specifies standard parsing mode.
- * If 'have_error' is NULL, then errors are thrown, else '*have_error' is set.
+ *
+ * If escontext points to an ErrorSaveContext, data errors will be reported
+ * by filling that struct; the caller must test SOFT_ERROR_OCCURRED() to see
+ * whether an error occurred. Otherwise, errors are thrown.
*
* Note: we currently don't have any to_interval() function, so there
* is no need here for INVALID_FOR_INTERVAL checks.
*/
static void
DCH_from_char(FormatNode *node, const char *in, TmFromChar *out,
- Oid collid, bool std, bool *have_error)
+ Oid collid, bool std, Node *escontext)
{
FormatNode *n;
const char *s;
@@ -3360,10 +3316,10 @@ DCH_from_char(FormatNode *node, const char *in, TmFromChar *out,
if (*s == n->character[0])
s++;
else
- RETURN_ERROR(ereport(ERROR,
- (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
- errmsg("unmatched format separator \"%c\"",
- n->character[0]))));
+ ereturn(escontext,,
+ (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+ errmsg("unmatched format separator \"%c\"",
+ n->character[0])));
}
else if (!fx_mode)
{
@@ -3421,18 +3377,18 @@ DCH_from_char(FormatNode *node, const char *in, TmFromChar *out,
*/
if (std && n->type == NODE_TYPE_CHAR &&
strncmp(s, n->character, chlen) != 0)
- RETURN_ERROR(ereport(ERROR,
- (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
- errmsg("unmatched format character \"%s\"",
- n->character))));
+ ereturn(escontext,,
+ (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+ errmsg("unmatched format character \"%s\"",
+ n->character)));
s += chlen;
}
continue;
}
- from_char_set_mode(out, n->key->date_mode, have_error);
- CHECK_ERROR;
+ if (!from_char_set_mode(out, n->key->date_mode, escontext))
+ return;
switch (n->key->id)
{
@@ -3443,51 +3399,52 @@ DCH_from_char(FormatNode *node, const char *in, TmFromChar *out,
case DCH_P_M:
case DCH_a_m:
case DCH_p_m:
- from_char_seq_search(&value, &s, ampm_strings_long,
- NULL, InvalidOid,
- n, have_error);
- CHECK_ERROR;
- from_char_set_int(&out->pm, value % 2, n, have_error);
- CHECK_ERROR;
+ 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:
- from_char_seq_search(&value, &s, ampm_strings,
- NULL, InvalidOid,
- n, have_error);
- CHECK_ERROR;
- from_char_set_int(&out->pm, value % 2, n, have_error);
- CHECK_ERROR;
+ 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:
- from_char_parse_int_len(&out->hh, &s, 2, n, have_error);
- CHECK_ERROR;
+ 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:
- from_char_parse_int_len(&out->hh, &s, 2, n, have_error);
- CHECK_ERROR;
+ if (from_char_parse_int_len(&out->hh, &s, 2, n, escontext) < 0)
+ return;
SKIP_THth(s, n->suffix);
break;
case DCH_MI:
- from_char_parse_int(&out->mi, &s, n, have_error);
- CHECK_ERROR;
+ if (from_char_parse_int(&out->mi, &s, n, escontext) < 0)
+ return;
SKIP_THth(s, n->suffix);
break;
case DCH_SS:
- from_char_parse_int(&out->ss, &s, n, have_error);
- CHECK_ERROR;
+ 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, have_error);
- CHECK_ERROR;
+ len = from_char_parse_int_len(&out->ms, &s, 3, n, escontext);
+ if (len < 0)
+ return;
/*
* 25 is 0.25 and 250 is 0.25 too; 025 is 0.025 and not 0.25
@@ -3508,8 +3465,9 @@ DCH_from_char(FormatNode *node, const char *in, TmFromChar *out,
case DCH_US: /* microsecond */
len = from_char_parse_int_len(&out->us, &s,
n->key->id == DCH_US ? 6 :
- out->ff, n, have_error);
- CHECK_ERROR;
+ out->ff, n, escontext);
+ if (len < 0)
+ return;
out->us *= len == 1 ? 100000 :
len == 2 ? 10000 :
@@ -3520,18 +3478,17 @@ DCH_from_char(FormatNode *node, const char *in, TmFromChar *out,
SKIP_THth(s, n->suffix);
break;
case DCH_SSSS:
- from_char_parse_int(&out->ssss, &s, n, have_error);
- CHECK_ERROR;
+ if (from_char_parse_int(&out->ssss, &s, n, escontext) < 0)
+ return;
SKIP_THth(s, n->suffix);
break;
case DCH_tz:
case DCH_TZ:
case DCH_OF:
- RETURN_ERROR(ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("formatting field \"%s\" is only supported in to_char",
- n->key->name))));
- CHECK_ERROR;
+ ereturn(escontext,,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("formatting field \"%s\" is only supported in to_char",
+ n->key->name)));
break;
case DCH_TZH:
@@ -3555,112 +3512,112 @@ DCH_from_char(FormatNode *node, const char *in, TmFromChar *out,
out->tzsign = +1;
}
- from_char_parse_int_len(&out->tzh, &s, 2, n, have_error);
- CHECK_ERROR;
+ 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;
- from_char_parse_int_len(&out->tzm, &s, 2, n, have_error);
- CHECK_ERROR;
+ 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:
- from_char_seq_search(&value, &s, adbc_strings_long,
- NULL, InvalidOid,
- n, have_error);
- CHECK_ERROR;
- from_char_set_int(&out->bc, value % 2, n, have_error);
- CHECK_ERROR;
+ 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:
- from_char_seq_search(&value, &s, adbc_strings,
- NULL, InvalidOid,
- n, have_error);
- CHECK_ERROR;
- from_char_set_int(&out->bc, value % 2, n, have_error);
- CHECK_ERROR;
+ 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:
- from_char_seq_search(&value, &s, months_full,
- S_TM(n->suffix) ? localized_full_months : NULL,
- collid,
- n, have_error);
- CHECK_ERROR;
- from_char_set_int(&out->mm, value + 1, n, have_error);
- CHECK_ERROR;
+ 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:
- from_char_seq_search(&value, &s, months,
- S_TM(n->suffix) ? localized_abbrev_months : NULL,
- collid,
- n, have_error);
- CHECK_ERROR;
- from_char_set_int(&out->mm, value + 1, n, have_error);
- CHECK_ERROR;
+ 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:
- from_char_parse_int(&out->mm, &s, n, have_error);
- CHECK_ERROR;
+ 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:
- from_char_seq_search(&value, &s, days,
- S_TM(n->suffix) ? localized_full_days : NULL,
- collid,
- n, have_error);
- CHECK_ERROR;
- from_char_set_int(&out->d, value, n, have_error);
- CHECK_ERROR;
+ 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:
- from_char_seq_search(&value, &s, days_short,
- S_TM(n->suffix) ? localized_abbrev_days : NULL,
- collid,
- n, have_error);
- CHECK_ERROR;
- from_char_set_int(&out->d, value, n, have_error);
- CHECK_ERROR;
+ 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:
- from_char_parse_int(&out->ddd, &s, n, have_error);
- CHECK_ERROR;
+ if (from_char_parse_int(&out->ddd, &s, n, escontext) < 0)
+ return;
SKIP_THth(s, n->suffix);
break;
case DCH_IDDD:
- from_char_parse_int_len(&out->ddd, &s, 3, n, have_error);
- CHECK_ERROR;
+ if (from_char_parse_int_len(&out->ddd, &s, 3, n, escontext) < 0)
+ return;
SKIP_THth(s, n->suffix);
break;
case DCH_DD:
- from_char_parse_int(&out->dd, &s, n, have_error);
- CHECK_ERROR;
+ if (from_char_parse_int(&out->dd, &s, n, escontext) < 0)
+ return;
SKIP_THth(s, n->suffix);
break;
case DCH_D:
- from_char_parse_int(&out->d, &s, n, have_error);
- CHECK_ERROR;
+ if (from_char_parse_int(&out->d, &s, n, escontext) < 0)
+ return;
SKIP_THth(s, n->suffix);
break;
case DCH_ID:
- from_char_parse_int_len(&out->d, &s, 1, n, have_error);
- CHECK_ERROR;
+ 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;
@@ -3668,8 +3625,8 @@ DCH_from_char(FormatNode *node, const char *in, TmFromChar *out,
break;
case DCH_WW:
case DCH_IW:
- from_char_parse_int(&out->ww, &s, n, have_error);
- CHECK_ERROR;
+ if (from_char_parse_int(&out->ww, &s, n, escontext) < 0)
+ return;
SKIP_THth(s, n->suffix);
break;
case DCH_Q:
@@ -3684,13 +3641,13 @@ DCH_from_char(FormatNode *node, const char *in, TmFromChar *out,
* 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, have_error);
- CHECK_ERROR;
+ if (from_char_parse_int((int *) NULL, &s, n, escontext) < 0)
+ return;
SKIP_THth(s, n->suffix);
break;
case DCH_CC:
- from_char_parse_int(&out->cc, &s, n, have_error);
- CHECK_ERROR;
+ if (from_char_parse_int(&out->cc, &s, n, escontext) < 0)
+ return;
SKIP_THth(s, n->suffix);
break;
case DCH_Y_YYY:
@@ -3702,12 +3659,12 @@ DCH_from_char(FormatNode *node, const char *in, TmFromChar *out,
matched = sscanf(s, "%d,%03d%n", &millennia, &years, &nch);
if (matched < 2)
- RETURN_ERROR(ereport(ERROR,
- (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
- errmsg("invalid input string for \"Y,YYY\""))));
+ ereturn(escontext,,
+ (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+ errmsg("invalid input string for \"Y,YYY\"")));
years += (millennia * 1000);
- from_char_set_int(&out->year, years, n, have_error);
- CHECK_ERROR;
+ if (!from_char_set_int(&out->year, years, n, escontext))
+ return;
out->yysz = 4;
s += nch;
SKIP_THth(s, n->suffix);
@@ -3715,15 +3672,16 @@ DCH_from_char(FormatNode *node, const char *in, TmFromChar *out,
break;
case DCH_YYYY:
case DCH_IYYY:
- from_char_parse_int(&out->year, &s, n, have_error);
- CHECK_ERROR;
+ 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, have_error);
- CHECK_ERROR;
+ 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;
@@ -3731,8 +3689,9 @@ DCH_from_char(FormatNode *node, const char *in, TmFromChar *out,
break;
case DCH_YY:
case DCH_IY:
- len = from_char_parse_int(&out->year, &s, n, have_error);
- CHECK_ERROR;
+ 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;
@@ -3740,8 +3699,9 @@ DCH_from_char(FormatNode *node, const char *in, TmFromChar *out,
break;
case DCH_Y:
case DCH_I:
- len = from_char_parse_int(&out->year, &s, n, have_error);
- CHECK_ERROR;
+ 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;
@@ -3749,22 +3709,22 @@ DCH_from_char(FormatNode *node, const char *in, TmFromChar *out,
break;
case DCH_RM:
case DCH_rm:
- from_char_seq_search(&value, &s, rm_months_lower,
- NULL, InvalidOid,
- n, have_error);
- CHECK_ERROR;
- from_char_set_int(&out->mm, MONTHS_PER_YEAR - value,
- n, have_error);
- CHECK_ERROR;
+ 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:
- from_char_parse_int(&out->w, &s, n, have_error);
- CHECK_ERROR;
+ if (from_char_parse_int(&out->w, &s, n, escontext) < 0)
+ return;
SKIP_THth(s, n->suffix);
break;
case DCH_J:
- from_char_parse_int(&out->j, &s, n, have_error);
- CHECK_ERROR;
+ if (from_char_parse_int(&out->j, &s, n, escontext) < 0)
+ return;
SKIP_THth(s, n->suffix);
break;
}
@@ -3788,22 +3748,18 @@ DCH_from_char(FormatNode *node, const char *in, TmFromChar *out,
if (std)
{
if (n->type != NODE_TYPE_END)
- RETURN_ERROR(ereport(ERROR,
- (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
- errmsg("input string is too short for datetime format"))));
+ ereturn(escontext,,
+ (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')
- RETURN_ERROR(ereport(ERROR,
- (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
- errmsg("trailing characters remain in input string "
- "after datetime format"))));
+ ereturn(escontext,,
+ (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+ errmsg("trailing characters remain in input string after datetime format")));
}
-
-on_error:
- return;
}
/*
@@ -3826,11 +3782,9 @@ DCH_prevent_counter_overflow(void)
/*
* 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, bool *have_error)
+DCH_datetime_type(FormatNode *node)
{
FormatNode *n;
int flags = 0;
@@ -3871,12 +3825,6 @@ DCH_datetime_type(FormatNode *node, bool *have_error)
case DCH_tz:
case DCH_TZ:
case DCH_OF:
- 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:
case DCH_TZM:
flags |= DCH_ZONED;
@@ -3928,7 +3876,6 @@ DCH_datetime_type(FormatNode *node, bool *have_error)
}
}
-on_error:
return flags;
}
@@ -4313,25 +4260,26 @@ to_date(PG_FUNCTION_ARGS)
* The actual data type (returned in 'typid', 'typmod') is determined by
* the presence of date/time/zone components in the format string.
*
- * When timezone component is present, the corresponding offset is
+ * When a timezone component is present, the corresponding offset is
* returned in '*tz'.
*
- * If 'have_error' is NULL, then errors are thrown, else '*have_error' is set
- * and zero value is returned.
+ * If escontext points to an ErrorSaveContext, data errors will be reported
+ * by filling that struct; the caller must test SOFT_ERROR_OCCURRED() to see
+ * whether an error occurred. Otherwise, errors are thrown.
*/
Datum
parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict,
Oid *typid, int32 *typmod, int *tz,
- bool *have_error)
+ Node *escontext)
{
struct pg_tm tm;
fsec_t fsec;
int fprec;
uint32 flags;
- do_to_timestamp(date_txt, fmt, collid, strict,
- &tm, &fsec, &fprec, &flags, have_error);
- CHECK_ERROR;
+ if (!do_to_timestamp(date_txt, fmt, collid, strict,
+ &tm, &fsec, &fprec, &flags, escontext))
+ return (Datum) 0;
*typmod = fprec ? fprec : -1; /* fractional part precision */
@@ -4349,9 +4297,12 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict,
int dterr = DecodeTimezone(tm.tm_zone, tz);
if (dterr)
+ {
DateTimeParseError(dterr, &extra,
text_to_cstring(date_txt),
- "timestamptz", NULL);
+ "timestamptz", escontext);
+ return (Datum) 0;
+ }
}
else
{
@@ -4362,17 +4313,17 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict,
*/
Assert(!strict);
- RETURN_ERROR(ereport(ERROR,
- (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
- errmsg("missing time zone in input string for type timestamptz"))));
+ 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)
- RETURN_ERROR(ereport(ERROR,
- (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
- errmsg("timestamptz out of range"))));
+ ereturn(escontext, (Datum) 0,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("timestamptz out of range")));
- AdjustTimestampForTypmod(&result, *typmod, NULL); /* XXX */
+ AdjustTimestampForTypmod(&result, *typmod, escontext);
*typid = TIMESTAMPTZOID;
return TimestampTzGetDatum(result);
@@ -4382,11 +4333,11 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict,
Timestamp result;
if (tm2timestamp(&tm, fsec, NULL, &result) != 0)
- RETURN_ERROR(ereport(ERROR,
- (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
- errmsg("timestamp out of range"))));
+ ereturn(escontext, (Datum) 0,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("timestamp out of range")));
- AdjustTimestampForTypmod(&result, *typmod, NULL); /* XXX */
+ AdjustTimestampForTypmod(&result, *typmod, escontext);
*typid = TIMESTAMPOID;
return TimestampGetDatum(result);
@@ -4396,9 +4347,9 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict,
{
if (flags & DCH_ZONED)
{
- RETURN_ERROR(ereport(ERROR,
- (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
- errmsg("datetime format is zoned but not timed"))));
+ ereturn(escontext, (Datum) 0,
+ (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+ errmsg("datetime format is zoned but not timed")));
}
else
{
@@ -4406,20 +4357,20 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict,
/* Prevent overflow in Julian-day routines */
if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday))
- RETURN_ERROR(ereport(ERROR,
- (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
- errmsg("date out of range: \"%s\"",
- text_to_cstring(date_txt)))));
+ 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))
- RETURN_ERROR(ereport(ERROR,
- (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
- errmsg("date out of range: \"%s\"",
- text_to_cstring(date_txt)))));
+ ereturn(escontext, (Datum) 0,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("date out of range: \"%s\"",
+ text_to_cstring(date_txt))));
*typid = DATEOID;
return DateADTGetDatum(result);
@@ -4438,9 +4389,12 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict,
int dterr = DecodeTimezone(tm.tm_zone, tz);
if (dterr)
- RETURN_ERROR(DateTimeParseError(dterr, &extra,
- text_to_cstring(date_txt),
- "timetz", NULL));
+ {
+ DateTimeParseError(dterr, &extra,
+ text_to_cstring(date_txt),
+ "timetz", escontext);
+ return (Datum) 0;
+ }
}
else
{
@@ -4451,15 +4405,15 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict,
*/
Assert(!strict);
- RETURN_ERROR(ereport(ERROR,
- (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
- errmsg("missing time zone in input string for type timetz"))));
+ 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)
- RETURN_ERROR(ereport(ERROR,
- (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
- errmsg("timetz out of range"))));
+ ereturn(escontext, (Datum) 0,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("timetz out of range")));
AdjustTimeForTypmod(&result->time, *typmod);
@@ -4471,9 +4425,9 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict,
TimeADT result;
if (tm2time(&tm, fsec, &result) != 0)
- RETURN_ERROR(ereport(ERROR,
- (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
- errmsg("time out of range"))));
+ ereturn(escontext, (Datum) 0,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("time out of range")));
AdjustTimeForTypmod(&result, *typmod);
@@ -4483,13 +4437,10 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict,
}
else
{
- RETURN_ERROR(ereport(ERROR,
- (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
- errmsg("datetime format is not dated and not timed"))));
+ ereturn(escontext, (Datum) 0,
+ (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
+ errmsg("datetime format is not dated and not timed")));
}
-
-on_error:
- return (Datum) 0;
}
/*
@@ -4500,9 +4451,13 @@ on_error:
*
* 'collid' identifies the collation to use, if needed.
* 'std' specifies standard parsing mode.
+ *
* Bit mask of date/time/zone components found in 'fmt' is returned in 'flags',
* if that is not NULL.
- * If 'have_error' is NULL, then errors are thrown, else '*have_error' is set.
+ *
+ * Returns true on success, false on failure (if escontext points to an
+ * ErrorSaveContext; otherwise errors are thrown). Note that currently,
+ * soft-error behavior is provided for bad data but not bad format.
*
* We parse 'fmt' into a list of FormatNodes, which is then passed to
* DCH_from_char to populate a TmFromChar with the parsed contents of
@@ -4511,10 +4466,10 @@ on_error:
* The TmFromChar is then analysed and converted into the final results in
* struct 'tm', 'fsec', and 'fprec'.
*/
-static void
+static bool
do_to_timestamp(text *date_txt, text *fmt, Oid collid, bool std,
struct pg_tm *tm, fsec_t *fsec, int *fprec,
- uint32 *flags, bool *have_error)
+ uint32 *flags, Node *escontext)
{
FormatNode *format = NULL;
TmFromChar tmfc;
@@ -4572,21 +4527,19 @@ do_to_timestamp(text *date_txt, text *fmt, Oid collid, bool std,
/* dump_index(DCH_keywords, DCH_index); */
#endif
- DCH_from_char(format, date_str, &tmfc, collid, std, have_error);
- CHECK_ERROR;
-
+ DCH_from_char(format, date_str, &tmfc, collid, std, escontext);
pfree(fmt_str);
+ if (SOFT_ERROR_OCCURRED(escontext))
+ goto fail;
if (flags)
- *flags = DCH_datetime_type(format, have_error);
+ *flags = DCH_datetime_type(format);
if (!incache)
{
pfree(format);
format = NULL;
}
-
- CHECK_ERROR;
}
DEBUG_TMFC(&tmfc);
@@ -4616,11 +4569,12 @@ do_to_timestamp(text *date_txt, text *fmt, Oid collid, bool std,
{
if (tm->tm_hour < 1 || tm->tm_hour > HOURS_PER_DAY / 2)
{
- 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."))));
+ 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 (tmfc.pm && tm->tm_hour < HOURS_PER_DAY / 2)
@@ -4729,9 +4683,10 @@ do_to_timestamp(text *date_txt, text *fmt, Oid collid, bool std,
if (!tm->tm_year && !tmfc.bc)
{
- RETURN_ERROR(ereport(ERROR,
- (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
- errmsg("cannot calculate day of year without year information"))));
+ 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)
@@ -4789,8 +4744,9 @@ do_to_timestamp(text *date_txt, text *fmt, Oid collid, bool std,
* said DTERR_MD_FIELD_OVERFLOW, because we don't want to print an
* irrelevant hint about datestyle.
*/
- RETURN_ERROR(DateTimeParseError(DTERR_FIELD_OVERFLOW, NULL,
- date_str, "timestamp", NULL));
+ DateTimeParseError(DTERR_FIELD_OVERFLOW, NULL,
+ date_str, "timestamp", escontext);
+ goto fail;
}
}
@@ -4800,8 +4756,9 @@ do_to_timestamp(text *date_txt, text *fmt, Oid collid, bool std,
tm->tm_sec < 0 || tm->tm_sec >= SECS_PER_MINUTE ||
*fsec < INT64CONST(0) || *fsec >= USECS_PER_SEC)
{
- RETURN_ERROR(DateTimeParseError(DTERR_FIELD_OVERFLOW, NULL,
- date_str, "timestamp", NULL));
+ DateTimeParseError(DTERR_FIELD_OVERFLOW, NULL,
+ date_str, "timestamp", escontext);
+ goto fail;
}
/* Save parsed time-zone into tm->tm_zone if it was specified */
@@ -4812,8 +4769,9 @@ do_to_timestamp(text *date_txt, text *fmt, Oid collid, bool std,
if (tmfc.tzh < 0 || tmfc.tzh > MAX_TZDISP_HOUR ||
tmfc.tzm < 0 || tmfc.tzm >= MINS_PER_HOUR)
{
- RETURN_ERROR(DateTimeParseError(DTERR_TZDISP_OVERFLOW, NULL,
- date_str, "timestamp", NULL));
+ DateTimeParseError(DTERR_TZDISP_OVERFLOW, NULL,
+ date_str, "timestamp", escontext);
+ goto fail;
}
tz = psprintf("%c%02d:%02d",
@@ -4824,12 +4782,18 @@ do_to_timestamp(text *date_txt, text *fmt, Oid collid, bool std,
DEBUG_TM(tm);
-on_error:
-
if (format && !incache)
pfree(format);
+ pfree(date_str);
+
+ return true;
+fail:
+ if (format && !incache)
+ pfree(format);
pfree(date_str);
+
+ return false;
}
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index 930bd26584..e758616eb8 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1808,7 +1808,7 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
text *template;
char *template_str;
int template_len;
- bool have_error = false;
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
jspGetArg(jsp, &elem);
@@ -1822,9 +1822,9 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
value = parse_datetime(datetime, template, collid, true,
&typid, &typmod, &tz,
- jspThrowErrors(cxt) ? NULL : &have_error);
+ jspThrowErrors(cxt) ? NULL : (Node *) &escontext);
- if (have_error)
+ if (escontext.error_occurred)
res = jperError;
else
res = jperOk;
@@ -1859,7 +1859,7 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
/* loop until datetime format fits */
for (i = 0; i < lengthof(fmt_str); i++)
{
- bool have_error = false;
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
if (!fmt_txt[i])
{
@@ -1872,9 +1872,9 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
value = parse_datetime(datetime, fmt_txt[i], collid, true,
&typid, &typmod, &tz,
- &have_error);
+ (Node *) &escontext);
- if (!have_error)
+ if (!escontext.error_occurred)
{
res = jperOk;
break;