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