Skip to content

Commit

Permalink
Add safe cast for function cast
Browse files Browse the repository at this point in the history
  • Loading branch information
robozmey committed Oct 31, 2024
1 parent 4789dc9 commit cec2995
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 4 deletions.
59 changes: 58 additions & 1 deletion src/backend/parser/parse_coerce.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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:
Expand Down
6 changes: 5 additions & 1 deletion src/backend/parser/parse_expr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion src/backend/utils/misc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
31 changes: 31 additions & 0 deletions src/backend/utils/misc/try_cast.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#include "postgres.h"

#include <ctype.h>
#include <limits.h>
#include <math.h>

#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();
}
4 changes: 4 additions & 0 deletions src/include/catalog/pg_proc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion src/include/nodes/primnodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/*
Expand Down
1 change: 1 addition & 0 deletions src/include/parser/parse_coerce.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
8 changes: 8 additions & 0 deletions src/include/utils/try_cast.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#ifndef TRY_CAST_H
#define TRY_CAST_H

#include "fmgr.h"

extern Datum try_cast(PG_FUNCTION_ARGS);

#endif

0 comments on commit cec2995

Please sign in to comment.