From cec29953e21d79259e5d88cd7fb929ce1aaa6a8d Mon Sep 17 00:00:00 2001 From: Vladimir Rachkin Date: Thu, 31 Oct 2024 09:33:47 +0300 Subject: [PATCH] Add safe cast for function cast --- src/backend/parser/parse_coerce.c | 59 ++++++++++++++++++++++++++++++- src/backend/parser/parse_expr.c | 6 +++- src/backend/utils/misc/Makefile | 2 +- src/backend/utils/misc/try_cast.c | 31 ++++++++++++++++ src/include/catalog/pg_proc.h | 4 +++ src/include/nodes/primnodes.h | 3 +- src/include/parser/parse_coerce.h | 1 + src/include/utils/try_cast.h | 8 +++++ 8 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 src/backend/utils/misc/try_cast.c create mode 100644 src/include/utils/try_cast.h diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 14648037baa..c88ed6119ac 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -1250,6 +1250,60 @@ build_coercion_expression(Node *node, fexpr->location = location; return (Node *) fexpr; } + else if (pathtype == COERCION_PATH_FUNC_SAFE) + { + /* We build an ordinary FuncExpr with special arguments */ + FuncExpr *fexpr; + List *args; + Const *cons; + + Assert(OidIsValid(funcId)); + + cons = makeConst(REGPROCEDUREOID, + -1, + InvalidOid, + sizeof(regproc), + funcId, + false, + true); + + args = list_make2(node, cons); + + + + if (nargs >= 2) + { + /* Pass target typmod as an int4 constant */ + cons = makeConst(INT4OID, + -1, + InvalidOid, + sizeof(int32), + Int32GetDatum(targetTypMod), + false, + true); + + args = lappend(args, cons); + } + + if (nargs == 3) + { + /* Pass it a boolean isExplicit parameter, too */ + cons = makeConst(BOOLOID, + -1, + InvalidOid, + sizeof(bool), + BoolGetDatum(isExplicit), + false, + true); + + args = lappend(args, cons); + } + + fexpr = makeFuncExpr(TRY_CAST_FUNCTION_OID, targetTypeId, args, + InvalidOid, InvalidOid, cformat); + fexpr->location = location; + return (Node *) fexpr; + } else if (pathtype == COERCION_PATH_ARRAYCOERCE) { /* We need to build an ArrayCoerceExpr */ @@ -2563,7 +2617,10 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, switch (castForm->castmethod) { case COERCION_METHOD_FUNCTION: - result = COERCION_PATH_FUNC; + if (ccontext == COERCION_EXPLICIT_SAFE) + result = COERCION_PATH_FUNC_SAFE; + else + result = COERCION_PATH_FUNC; *funcid = castForm->castfunc; break; case COERCION_METHOD_INOUT: diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 765f4e36afa..c591be32193 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -2467,9 +2467,13 @@ transformTypeCast(ParseState *pstate, TypeCast *tc) if (location < 0) location = tc->typeName->location; + CoercionContext ccontext = COERCION_EXPLICIT; + if (tc->is_trycast) + ccontext = COERCION_EXPLICIT_SAFE; + result = coerce_to_target_type(pstate, expr, inputType, targetType, targetTypmod, - COERCION_EXPLICIT, + ccontext, COERCE_EXPLICIT_CAST, location); if (result == NULL) diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile index 329eb99809d..6489411b036 100644 --- a/src/backend/utils/misc/Makefile +++ b/src/backend/utils/misc/Makefile @@ -21,7 +21,7 @@ OBJS = guc.o help_config.o pg_rusage.o ps_status.o rbtree.o \ gpexpand.o \ faultinjector.o uriparser.o \ bitstream.o bitmap_compression.o guc_gp.o backend_cancel.o \ - superuser.o timeout.o tzparser.o + superuser.o timeout.o try_cast.o tzparser.o # This location might depend on the installation directories. Therefore # we can't substitute it into pg_config.h. diff --git a/src/backend/utils/misc/try_cast.c b/src/backend/utils/misc/try_cast.c new file mode 100644 index 00000000000..f3bc18c5cfe --- /dev/null +++ b/src/backend/utils/misc/try_cast.c @@ -0,0 +1,31 @@ +#include "postgres.h" + +#include +#include +#include + +#include "funcapi.h" +#include "libpq/pqformat.h" +#include "utils/try_cast.h" +#include "utils/builtins.h" + + +Datum +try_cast(PG_FUNCTION_ARGS) +{ + Datum value = PG_GETARG_DATUM(0); + + Datum funcId = PG_GETARG_DATUM(1); + + PG_TRY(); + { + Datum res = OidFunctionCall1(funcId, value); + + PG_RETURN_DATUM(res); + } + PG_CATCH(); + { + PG_RETURN_NULL(); + } + PG_END_TRY(); +} \ No newline at end of file diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index dec7eebc5a0..bccc146d704 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -5234,6 +5234,10 @@ DESCR("import collations from operating system"); DATA(insert OID = 3994 ( mdb_locale_enabled PGNSP PGUID 12 1 0 0 0 f f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ mdb_locale_enabled _null_ _null_ _null_ )); DESCR("true if mdb locales enabled"); +DATA(insert OID = 3995 ( try_cast_func PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2283 "2283 24" _null_ _null_ _null_ _null_ try_cast _null_ _null_ _null_ )); +DESCR("try_cast function"); +#define TRY_CAST_FUNCTION_OID 3995 + /* * Include more definitions from pg_proc_gp.h, for GPDB-added functions. They * are kept in a separate file to make diffing and merging with upstream diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index caed77f1c29..bbd8202937d 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -437,7 +437,8 @@ typedef enum CoercionContext { COERCION_IMPLICIT, /* coercion in context of expression */ COERCION_ASSIGNMENT, /* coercion in context of assignment */ - COERCION_EXPLICIT /* explicit cast operation */ + COERCION_EXPLICIT, /* explicit cast operation */ + COERCION_EXPLICIT_SAFE /* explicit cast operation */ } CoercionContext; /* diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h index 87caf4a18c6..87142dbfce4 100644 --- a/src/include/parser/parse_coerce.h +++ b/src/include/parser/parse_coerce.h @@ -25,6 +25,7 @@ typedef enum CoercionPathType { COERCION_PATH_NONE, /* failed to find any coercion pathway */ COERCION_PATH_FUNC, /* apply the specified coercion function */ + COERCION_PATH_FUNC_SAFE, /* apply the specified coercion function safe */ COERCION_PATH_RELABELTYPE, /* binary-compatible cast, no function */ COERCION_PATH_ARRAYCOERCE, /* need an ArrayCoerceExpr node */ COERCION_PATH_COERCEVIAIO /* need a CoerceViaIO node */ diff --git a/src/include/utils/try_cast.h b/src/include/utils/try_cast.h new file mode 100644 index 00000000000..6286abd6a68 --- /dev/null +++ b/src/include/utils/try_cast.h @@ -0,0 +1,8 @@ +#ifndef TRY_CAST_H +#define TRY_CAST_H + +#include "fmgr.h" + +extern Datum try_cast(PG_FUNCTION_ARGS); + +#endif \ No newline at end of file