diff options
Diffstat (limited to 'src/backend/utils')
-rw-r--r-- | src/backend/utils/adt/misc.c | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c index 9c132512315..d678d286009 100644 --- a/src/backend/utils/adt/misc.c +++ b/src/backend/utils/adt/misc.c @@ -32,6 +32,8 @@ #include "common/keywords.h" #include "funcapi.h" #include "miscadmin.h" +#include "nodes/miscnodes.h" +#include "parser/parse_type.h" #include "parser/scansup.h" #include "pgstat.h" #include "postmaster/syslogger.h" @@ -45,6 +47,25 @@ #include "utils/ruleutils.h" #include "utils/timestamp.h" + +/* + * structure to cache metadata needed in pg_input_is_valid_common + */ +typedef struct ValidIOData +{ + Oid typoid; + int32 typmod; + bool typname_constant; + Oid typiofunc; + Oid typioparam; + FmgrInfo inputproc; +} ValidIOData; + +static bool pg_input_is_valid_common(FunctionCallInfo fcinfo, + text *txt, text *typname, + ErrorSaveContext *escontext); + + /* * Common subroutine for num_nulls() and num_nonnulls(). * Returns true if successful, false if function should return NULL. @@ -641,6 +662,114 @@ pg_column_is_updatable(PG_FUNCTION_ARGS) /* + * pg_input_is_valid - test whether string is valid input for datatype. + * + * Returns true if OK, false if not. + * + * This will only work usefully if the datatype's input function has been + * updated to return "soft" errors via errsave/ereturn. + */ +Datum +pg_input_is_valid(PG_FUNCTION_ARGS) +{ + text *txt = PG_GETARG_TEXT_PP(0); + text *typname = PG_GETARG_TEXT_PP(1); + ErrorSaveContext escontext = {T_ErrorSaveContext}; + + PG_RETURN_BOOL(pg_input_is_valid_common(fcinfo, txt, typname, + &escontext)); +} + +/* + * pg_input_error_message - test whether string is valid input for datatype. + * + * Returns NULL if OK, else the primary message string from the error. + * + * This will only work usefully if the datatype's input function has been + * updated to return "soft" errors via errsave/ereturn. + */ +Datum +pg_input_error_message(PG_FUNCTION_ARGS) +{ + text *txt = PG_GETARG_TEXT_PP(0); + text *typname = PG_GETARG_TEXT_PP(1); + ErrorSaveContext escontext = {T_ErrorSaveContext}; + + /* Enable details_wanted */ + escontext.details_wanted = true; + + if (pg_input_is_valid_common(fcinfo, txt, typname, + &escontext)) + PG_RETURN_NULL(); + + Assert(escontext.error_occurred); + Assert(escontext.error_data != NULL); + Assert(escontext.error_data->message != NULL); + + PG_RETURN_TEXT_P(cstring_to_text(escontext.error_data->message)); +} + +/* Common subroutine for the above */ +static bool +pg_input_is_valid_common(FunctionCallInfo fcinfo, + text *txt, text *typname, + ErrorSaveContext *escontext) +{ + char *str = text_to_cstring(txt); + ValidIOData *my_extra; + Datum converted; + + /* + * We arrange to look up the needed I/O info just once per series of + * calls, assuming the data type doesn't change underneath us. + */ + my_extra = (ValidIOData *) fcinfo->flinfo->fn_extra; + if (my_extra == NULL) + { + fcinfo->flinfo->fn_extra = + MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, + sizeof(ValidIOData)); + my_extra = (ValidIOData *) fcinfo->flinfo->fn_extra; + my_extra->typoid = InvalidOid; + /* Detect whether typname argument is constant. */ + my_extra->typname_constant = get_fn_expr_arg_stable(fcinfo->flinfo, 1); + } + + /* + * If the typname argument is constant, we only need to parse it the first + * time through. + */ + if (my_extra->typoid == InvalidOid || !my_extra->typname_constant) + { + char *typnamestr = text_to_cstring(typname); + Oid typoid; + + /* Parse type-name argument to obtain type OID and encoded typmod. */ + parseTypeString(typnamestr, &typoid, &my_extra->typmod, false); + + /* Update type-specific info if typoid changed. */ + if (my_extra->typoid != typoid) + { + getTypeInputInfo(typoid, + &my_extra->typiofunc, + &my_extra->typioparam); + fmgr_info_cxt(my_extra->typiofunc, &my_extra->inputproc, + fcinfo->flinfo->fn_mcxt); + my_extra->typoid = typoid; + } + } + + /* Now we can try to perform the conversion. */ + return InputFunctionCallSafe(&my_extra->inputproc, + str, + my_extra->typioparam, + my_extra->typmod, + (Node *) escontext, + &converted); +} + + +/* * Is character a valid identifier start? * Must match scan.l's {ident_start} character class. */ |