Restructure soft-error handling in formatting.c.
authorTom Lane <[email protected]>
Sat, 10 Dec 2022 01:15:56 +0000 (20:15 -0500)
committerTom Lane <[email protected]>
Sat, 10 Dec 2022 01:15:56 +0000 (20:15 -0500)
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

src/backend/utils/adt/formatting.c
src/backend/utils/adt/jsonpath_exec.c
src/include/utils/formatting.h

index eba981abd858a7a927195ad4b67c720a9519fd40..65746c48d264c9342849be283b1664466cf6bbb9 100644 (file)
@@ -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"
 #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 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;
 }
 
 
index 930bd265842470c75611e2b50a07e68ea1c64d31..e758616eb83687444e8e8e2df6b737988d02aea5 100644 (file)
@@ -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;
index 851e787bfdb89ec855dd10403d3e3b89ce5d3c3a..887e22d6f64abf981bea321a5c2d8dee6cabc920 100644 (file)
@@ -28,6 +28,6 @@ extern char *asc_initcap(const char *buff, size_t nbytes);
 
 extern Datum parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict,
                                                        Oid *typid, int32 *typmod, int *tz,
-                                                       bool *have_error);
+                                                       struct Node *escontext);
 
 #endif