|
37 | 37 | #include "libpq-fe.h"
|
38 | 38 |
|
39 | 39 | #include "access/htup_details.h"
|
| 40 | +#include "access/reloptions.h" |
40 | 41 | #include "catalog/indexing.h"
|
41 | 42 | #include "catalog/namespace.h"
|
| 43 | +#include "catalog/pg_foreign_server.h" |
42 | 44 | #include "catalog/pg_type.h"
|
| 45 | +#include "catalog/pg_user_mapping.h" |
43 | 46 | #include "executor/spi.h"
|
44 | 47 | #include "foreign/foreign.h"
|
45 | 48 | #include "funcapi.h"
|
@@ -113,6 +116,8 @@ static char *escape_param_str(const char *from);
|
113 | 116 | static void validate_pkattnums(Relation rel,
|
114 | 117 | int2vector *pkattnums_arg, int32 pknumatts_arg,
|
115 | 118 | int **pkattnums, int *pknumatts);
|
| 119 | +static bool is_valid_dblink_option(const PQconninfoOption *options, |
| 120 | + const char *option, Oid context); |
116 | 121 |
|
117 | 122 | /* Global */
|
118 | 123 | static remoteConn *pconn = NULL;
|
@@ -1912,6 +1917,75 @@ dblink_get_notify(PG_FUNCTION_ARGS)
|
1912 | 1917 | return (Datum) 0;
|
1913 | 1918 | }
|
1914 | 1919 |
|
| 1920 | +/* |
| 1921 | + * Validate the options given to a dblink foreign server or user mapping. |
| 1922 | + * Raise an error if any option is invalid. |
| 1923 | + * |
| 1924 | + * We just check the names of options here, so semantic errors in options, |
| 1925 | + * such as invalid numeric format, will be detected at the attempt to connect. |
| 1926 | + */ |
| 1927 | +PG_FUNCTION_INFO_V1(dblink_fdw_validator); |
| 1928 | +Datum |
| 1929 | +dblink_fdw_validator(PG_FUNCTION_ARGS) |
| 1930 | +{ |
| 1931 | + List *options_list = untransformRelOptions(PG_GETARG_DATUM(0)); |
| 1932 | + Oid context = PG_GETARG_OID(1); |
| 1933 | + ListCell *cell; |
| 1934 | + |
| 1935 | + static const PQconninfoOption *options = NULL; |
| 1936 | + |
| 1937 | + /* |
| 1938 | + * Get list of valid libpq options. |
| 1939 | + * |
| 1940 | + * To avoid unnecessary work, we get the list once and use it throughout |
| 1941 | + * the lifetime of this backend process. We don't need to care about |
| 1942 | + * memory context issues, because PQconndefaults allocates with malloc. |
| 1943 | + */ |
| 1944 | + if (!options) |
| 1945 | + { |
| 1946 | + options = PQconndefaults(); |
| 1947 | + if (!options) /* assume reason for failure is OOM */ |
| 1948 | + ereport(ERROR, |
| 1949 | + (errcode(ERRCODE_FDW_OUT_OF_MEMORY), |
| 1950 | + errmsg("out of memory"), |
| 1951 | + errdetail("could not get libpq's default connection options"))); |
| 1952 | + } |
| 1953 | + |
| 1954 | + /* Validate each supplied option. */ |
| 1955 | + foreach(cell, options_list) |
| 1956 | + { |
| 1957 | + DefElem *def = (DefElem *) lfirst(cell); |
| 1958 | + |
| 1959 | + if (!is_valid_dblink_option(options, def->defname, context)) |
| 1960 | + { |
| 1961 | + /* |
| 1962 | + * Unknown option, or invalid option for the context specified, |
| 1963 | + * so complain about it. Provide a hint with list of valid |
| 1964 | + * options for the context. |
| 1965 | + */ |
| 1966 | + StringInfoData buf; |
| 1967 | + const PQconninfoOption *opt; |
| 1968 | + |
| 1969 | + initStringInfo(&buf); |
| 1970 | + for (opt = options; opt->keyword; opt++) |
| 1971 | + { |
| 1972 | + if (is_valid_dblink_option(options, opt->keyword, context)) |
| 1973 | + appendStringInfo(&buf, "%s%s", |
| 1974 | + (buf.len > 0) ? ", " : "", |
| 1975 | + opt->keyword); |
| 1976 | + } |
| 1977 | + ereport(ERROR, |
| 1978 | + (errcode(ERRCODE_FDW_OPTION_NAME_NOT_FOUND), |
| 1979 | + errmsg("invalid option \"%s\"", def->defname), |
| 1980 | + errhint("Valid options in this context are: %s", |
| 1981 | + buf.data))); |
| 1982 | + } |
| 1983 | + } |
| 1984 | + |
| 1985 | + PG_RETURN_VOID(); |
| 1986 | +} |
| 1987 | + |
| 1988 | + |
1915 | 1989 | /*************************************************************
|
1916 | 1990 | * internal functions
|
1917 | 1991 | */
|
@@ -2768,3 +2842,59 @@ validate_pkattnums(Relation rel,
|
2768 | 2842 | errmsg("invalid attribute number %d", pkattnum)));
|
2769 | 2843 | }
|
2770 | 2844 | }
|
| 2845 | + |
| 2846 | +/* |
| 2847 | + * Check if the specified connection option is valid. |
| 2848 | + * |
| 2849 | + * We basically allow whatever libpq thinks is an option, with these |
| 2850 | + * restrictions: |
| 2851 | + * debug options: disallowed |
| 2852 | + * "client_encoding": disallowed |
| 2853 | + * "user": valid only in USER MAPPING options |
| 2854 | + * secure options (eg password): valid only in USER MAPPING options |
| 2855 | + * others: valid only in FOREIGN SERVER options |
| 2856 | + * |
| 2857 | + * We disallow client_encoding because it would be overridden anyway via |
| 2858 | + * PQclientEncoding; allowing it to be specified would merely promote |
| 2859 | + * confusion. |
| 2860 | + */ |
| 2861 | +static bool |
| 2862 | +is_valid_dblink_option(const PQconninfoOption *options, const char *option, |
| 2863 | + Oid context) |
| 2864 | +{ |
| 2865 | + const PQconninfoOption *opt; |
| 2866 | + |
| 2867 | + /* Look up the option in libpq result */ |
| 2868 | + for (opt = options; opt->keyword; opt++) |
| 2869 | + { |
| 2870 | + if (strcmp(opt->keyword, option) == 0) |
| 2871 | + break; |
| 2872 | + } |
| 2873 | + if (opt->keyword == NULL) |
| 2874 | + return false; |
| 2875 | + |
| 2876 | + /* Disallow debug options (particularly "replication") */ |
| 2877 | + if (strchr(opt->dispchar, 'D')) |
| 2878 | + return false; |
| 2879 | + |
| 2880 | + /* Disallow "client_encoding" */ |
| 2881 | + if (strcmp(opt->keyword, "client_encoding") == 0) |
| 2882 | + return false; |
| 2883 | + |
| 2884 | + /* |
| 2885 | + * If the option is "user" or marked secure, it should be specified only |
| 2886 | + * in USER MAPPING. Others should be specified only in SERVER. |
| 2887 | + */ |
| 2888 | + if (strcmp(opt->keyword, "user") == 0 || strchr(opt->dispchar, '*')) |
| 2889 | + { |
| 2890 | + if (context != UserMappingRelationId) |
| 2891 | + return false; |
| 2892 | + } |
| 2893 | + else |
| 2894 | + { |
| 2895 | + if (context != ForeignServerRelationId) |
| 2896 | + return false; |
| 2897 | + } |
| 2898 | + |
| 2899 | + return true; |
| 2900 | +} |
0 commit comments