|
32 | 32 | #include "common/keywords.h"
|
33 | 33 | #include "funcapi.h"
|
34 | 34 | #include "miscadmin.h"
|
| 35 | +#include "nodes/miscnodes.h" |
| 36 | +#include "parser/parse_type.h" |
35 | 37 | #include "parser/scansup.h"
|
36 | 38 | #include "pgstat.h"
|
37 | 39 | #include "postmaster/syslogger.h"
|
|
45 | 47 | #include "utils/ruleutils.h"
|
46 | 48 | #include "utils/timestamp.h"
|
47 | 49 |
|
| 50 | + |
| 51 | +/* |
| 52 | + * structure to cache metadata needed in pg_input_is_valid_common |
| 53 | + */ |
| 54 | +typedef struct ValidIOData |
| 55 | +{ |
| 56 | + Oid typoid; |
| 57 | + int32 typmod; |
| 58 | + bool typname_constant; |
| 59 | + Oid typiofunc; |
| 60 | + Oid typioparam; |
| 61 | + FmgrInfo inputproc; |
| 62 | +} ValidIOData; |
| 63 | + |
| 64 | +static bool pg_input_is_valid_common(FunctionCallInfo fcinfo, |
| 65 | + text *txt, text *typname, |
| 66 | + ErrorSaveContext *escontext); |
| 67 | + |
| 68 | + |
48 | 69 | /*
|
49 | 70 | * Common subroutine for num_nulls() and num_nonnulls().
|
50 | 71 | * Returns true if successful, false if function should return NULL.
|
@@ -640,6 +661,114 @@ pg_column_is_updatable(PG_FUNCTION_ARGS)
|
640 | 661 | }
|
641 | 662 |
|
642 | 663 |
|
| 664 | +/* |
| 665 | + * pg_input_is_valid - test whether string is valid input for datatype. |
| 666 | + * |
| 667 | + * Returns true if OK, false if not. |
| 668 | + * |
| 669 | + * This will only work usefully if the datatype's input function has been |
| 670 | + * updated to return "soft" errors via errsave/ereturn. |
| 671 | + */ |
| 672 | +Datum |
| 673 | +pg_input_is_valid(PG_FUNCTION_ARGS) |
| 674 | +{ |
| 675 | + text *txt = PG_GETARG_TEXT_PP(0); |
| 676 | + text *typname = PG_GETARG_TEXT_PP(1); |
| 677 | + ErrorSaveContext escontext = {T_ErrorSaveContext}; |
| 678 | + |
| 679 | + PG_RETURN_BOOL(pg_input_is_valid_common(fcinfo, txt, typname, |
| 680 | + &escontext)); |
| 681 | +} |
| 682 | + |
| 683 | +/* |
| 684 | + * pg_input_error_message - test whether string is valid input for datatype. |
| 685 | + * |
| 686 | + * Returns NULL if OK, else the primary message string from the error. |
| 687 | + * |
| 688 | + * This will only work usefully if the datatype's input function has been |
| 689 | + * updated to return "soft" errors via errsave/ereturn. |
| 690 | + */ |
| 691 | +Datum |
| 692 | +pg_input_error_message(PG_FUNCTION_ARGS) |
| 693 | +{ |
| 694 | + text *txt = PG_GETARG_TEXT_PP(0); |
| 695 | + text *typname = PG_GETARG_TEXT_PP(1); |
| 696 | + ErrorSaveContext escontext = {T_ErrorSaveContext}; |
| 697 | + |
| 698 | + /* Enable details_wanted */ |
| 699 | + escontext.details_wanted = true; |
| 700 | + |
| 701 | + if (pg_input_is_valid_common(fcinfo, txt, typname, |
| 702 | + &escontext)) |
| 703 | + PG_RETURN_NULL(); |
| 704 | + |
| 705 | + Assert(escontext.error_occurred); |
| 706 | + Assert(escontext.error_data != NULL); |
| 707 | + Assert(escontext.error_data->message != NULL); |
| 708 | + |
| 709 | + PG_RETURN_TEXT_P(cstring_to_text(escontext.error_data->message)); |
| 710 | +} |
| 711 | + |
| 712 | +/* Common subroutine for the above */ |
| 713 | +static bool |
| 714 | +pg_input_is_valid_common(FunctionCallInfo fcinfo, |
| 715 | + text *txt, text *typname, |
| 716 | + ErrorSaveContext *escontext) |
| 717 | +{ |
| 718 | + char *str = text_to_cstring(txt); |
| 719 | + ValidIOData *my_extra; |
| 720 | + Datum converted; |
| 721 | + |
| 722 | + /* |
| 723 | + * We arrange to look up the needed I/O info just once per series of |
| 724 | + * calls, assuming the data type doesn't change underneath us. |
| 725 | + */ |
| 726 | + my_extra = (ValidIOData *) fcinfo->flinfo->fn_extra; |
| 727 | + if (my_extra == NULL) |
| 728 | + { |
| 729 | + fcinfo->flinfo->fn_extra = |
| 730 | + MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, |
| 731 | + sizeof(ValidIOData)); |
| 732 | + my_extra = (ValidIOData *) fcinfo->flinfo->fn_extra; |
| 733 | + my_extra->typoid = InvalidOid; |
| 734 | + /* Detect whether typname argument is constant. */ |
| 735 | + my_extra->typname_constant = get_fn_expr_arg_stable(fcinfo->flinfo, 1); |
| 736 | + } |
| 737 | + |
| 738 | + /* |
| 739 | + * If the typname argument is constant, we only need to parse it the first |
| 740 | + * time through. |
| 741 | + */ |
| 742 | + if (my_extra->typoid == InvalidOid || !my_extra->typname_constant) |
| 743 | + { |
| 744 | + char *typnamestr = text_to_cstring(typname); |
| 745 | + Oid typoid; |
| 746 | + |
| 747 | + /* Parse type-name argument to obtain type OID and encoded typmod. */ |
| 748 | + parseTypeString(typnamestr, &typoid, &my_extra->typmod, false); |
| 749 | + |
| 750 | + /* Update type-specific info if typoid changed. */ |
| 751 | + if (my_extra->typoid != typoid) |
| 752 | + { |
| 753 | + getTypeInputInfo(typoid, |
| 754 | + &my_extra->typiofunc, |
| 755 | + &my_extra->typioparam); |
| 756 | + fmgr_info_cxt(my_extra->typiofunc, &my_extra->inputproc, |
| 757 | + fcinfo->flinfo->fn_mcxt); |
| 758 | + my_extra->typoid = typoid; |
| 759 | + } |
| 760 | + } |
| 761 | + |
| 762 | + /* Now we can try to perform the conversion. */ |
| 763 | + return InputFunctionCallSafe(&my_extra->inputproc, |
| 764 | + str, |
| 765 | + my_extra->typioparam, |
| 766 | + my_extra->typmod, |
| 767 | + (Node *) escontext, |
| 768 | + &converted); |
| 769 | +} |
| 770 | + |
| 771 | + |
643 | 772 | /*
|
644 | 773 | * Is character a valid identifier start?
|
645 | 774 | * Must match scan.l's {ident_start} character class.
|
|
0 commit comments