| From: | jian he <jian(dot)universality(at)gmail(dot)com> |
|---|---|
| To: | Corey Huinker <corey(dot)huinker(at)gmail(dot)com> |
| Cc: | assam258(at)gmail(dot)com, peter(at)eisentraut(dot)org, vik(at)postgresfriends(dot)org, sulamul(at)gmail(dot)com, reshkekirill(at)gmail(dot)com, isaac(dot)morland(at)gmail(dot)com, pgsql-hackers(at)lists(dot)postgresql(dot)org |
| Subject: | Re: CAST(... ON DEFAULT) - WIP build on top of Error-Safe User Functions |
| Date: | 2026-09-16 07:47:52 |
| Message-ID: | CACJufxF86NMYChEXLDWBhDKRrD8qM1Vs-uoqrH-AjLGScrJVqA@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
The attached v33 has nontrivial changes compared with v32.
previously v32 I did:
1. add coerce_to_target_type_extended _extended function seems not
future-proof enough.
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -43,15 +43,29 @@ extern Node *coerce_to_target_type(ParseState *pstate,
CoercionContext ccontext,
CoercionForm cformat,
int location);
+extern Node *coerce_to_target_type_extended(ParseState *pstate,
+ Node *expr,
+ Oid exprtype,
+ Oid targettype,
+ int32 targettypmod,
+ CoercionContext ccontext,
+ CoercionForm cformat,
+ int location,
+ Node *escontext);
To support CAST('error' AS integer DEFAULT 1 ON CONVERSION ERROR), the
conversion of an unknown literal to a Const, done inside coerce_to_target_type,
must be able to fail softly. Passing an ErrorSaveContext pointer down through
those functions is ugly and not future-proof, so adding a p_escontext field to
struct ParseState. This also lets other parse-analysis code do error-safe
conversions the same way, if they want.
2.
+/*
+ * SafeTypeCastExpr -
+ * Transformed representation of
+ * CAST(expr AS typename DEFAULT expr ON CONVERSION ERROR)
+ */
+typedef struct SafeTypeCastExpr
+{
+ Expr xpr;
+
+ /*
+ * The transformed source expression.
+ *
+ * Cases like ``CAST(1 AS date DEFAULT NULL ON CONVERSION ERROR)`` where
+ * the castexpr evaluates to NULL, we need this field to reconstruct the
+ * original query.
+ */
+ Expr *source;
+
+ /*
+ * transformed cast expression, NULL means cannot coerce to target type.
+ *
+ * For query jumbling, ignore this node because castexpr expression may
+ * contain the "source" expression. Including it would cause the same
+ * expression to be jumbled twice.
+ */
+ Expr *castexpr pg_node_attr(query_jumble_ignore);
"castexpr" could already contain the "source" expression, so every
tree walker would see the same subtree twice.
That has unintended ramification, for example a
SubLink or an Aggref in the argument would be processed twice, and avoiding
that requires special care at each such place, which is fragile.
So instead "castexpr" is built over a CaseTestExpr placeholder, and
the argument is stored once.
3.
For FuncExpr, we should do something equivalent to ExecEvalCoerceViaIOSafe.
As I mentioned before, our error safe type cast evaluation mainly involves
processing/evaluating FuncExpr and CoerceViaIO nodes. For CoerceViaIO,
ExecEvalCoerceViaIOSafe already sets resvalue and resnull on a soft error.
Function calls (FuncExpr) need the same treatment. A function that reports an
error softly returns a dummy datum with isnull *still* false! and the next step
would consume it. After a soft error the step must instead produce resvalue = 0
*and* resnull = true. That has to happen at the call site, not in the consumer.
CAST('\x01'::bytea::uuid AS text DEFAULT 'x1' ON CONVERSION ERROR)
Here, function bytea_uuid() reports the bad length with ereturn(), which records
the error and returns Datum 0 without setting fcinfo->isnull. The next step
would then call uuid_out() on a null pointer. By the errsave convention the
return value is garbage and the caller must check the context, so the
function-call step does that and returns NULL instead.
So four ExprEvalOp opcodes are added:
EEOP_FUNCEXPR_SAFE,
EEOP_FUNCEXPR_STRICT_SAFE,
EEOP_FUNCEXPR_SAFE_FUSAGE,
EEOP_FUNCEXPR_STRICT_SAFE_FUSAGE
4. extensive changes in clauses.c also required.
Rationale:
CAST(('abc'::text)::int + abs(NULL) AS int DEFAULT 3 ON CONVERSION ERROR)
To make the above query does not fail, which is what we want (at least, I think
so), we need to make evaluate_expr handle soft errors.
This means evaluate_expr needs an ErrorSaveContext node. As a result, we’ll need
to refactor every occurrence of ece_evaluate_expr.
| Attachment | Content-Type | Size |
|---|---|---|
| v33-0002-introduce-pg_proc.proerrorsafe.patch | application/x-patch | 67.0 KB |
| v33-0003-CAST-expr-AS-newtype-DEFAULT-expr-ON-CONVERSION-ERROR.patch | application/x-patch | 151.6 KB |
| v33-0001-error-safe-for-casting-text-to-other-types-per-pg_cast.patch | application/x-patch | 9.3 KB |
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Peter Smith | 2026-09-16 07:51:12 | Re: Distinguish publication exclusions in object addresses |
| Previous Message | Ewan Young | 2026-09-16 07:43:58 | Re: ERROR: no relation entry for relid 3 |