dblink_fdw_validator.patch

text/plain

Filename: dblink_fdw_validator.patch
Type: text/plain
Part: 0
Message: Move postgresql_fdw_validator into dblink

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
File+
contrib/dblink/dblink--1.0--1.1.sql 14 0
contrib/dblink/dblink--1.0.sql 0 223
contrib/dblink/dblink--1.1.sql 234 0
contrib/dblink/dblink.c 124 0
contrib/dblink/dblink.control 1 1
contrib/dblink/dblink.h 1 0
contrib/dblink/expected/dblink.out 10 3
contrib/dblink/Makefile 1 1
contrib/dblink/sql/dblink.sql 4 3
doc/src/sgml/dblink.sgml 4 8
doc/src/sgml/ref/create_foreign_data_wrapper.sgml 0 8
src/backend/foreign/foreign.c 0 100
src/include/catalog/pg_proc.h 0 3
src/test/regress/expected/foreign_data.out 35 0
src/test/regress/sql/foreign_data.sql 36 0
diff --git a/contrib/dblink/Makefile b/contrib/dblink/Makefile
index ac63748..a27db88 100644
--- a/contrib/dblink/Makefile
+++ b/contrib/dblink/Makefile
@@ -7,7 +7,7 @@ SHLIB_LINK = $(libpq)
 SHLIB_PREREQS = submake-libpq
 
 EXTENSION = dblink
-DATA = dblink--1.0.sql dblink--unpackaged--1.0.sql
+DATA = dblink--1.1.sql dblink--1.0--1.1.sql dblink--unpackaged--1.0.sql
 
 REGRESS = dblink
 
diff --git a/contrib/dblink/dblink--1.0--1.1.sql b/contrib/dblink/dblink--1.0--1.1.sql
new file mode 100644
index 0000000..f224d3d
--- /dev/null
+++ b/contrib/dblink/dblink--1.0--1.1.sql
@@ -0,0 +1,14 @@
+/* contrib/dblink/dblink--1.0--1.1.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "ALTER EXTENSION dblink UPDATE TO '1.1'" to load this file. \quit
+
+CREATE FUNCTION dblink_fdw_validator(
+    options text[],
+    catalog oid
+)
+RETURNS void
+AS 'MODULE_PATHNAME', 'dblink_fdw_validator'
+LANGUAGE C IMMUTABLE;
+
+CREATE FOREIGN DATA WRAPPER dblink_fdw VALIDATOR dblink_fdw_validator;
diff --git a/contrib/dblink/dblink--1.0.sql b/contrib/dblink/dblink--1.0.sql
deleted file mode 100644
index 1fec9e3..0000000
--- a/contrib/dblink/dblink--1.0.sql
+++ /dev/null
@@ -1,223 +0,0 @@
-/* contrib/dblink/dblink--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION dblink" to load this file. \quit
-
--- dblink_connect now restricts non-superusers to password
--- authenticated connections
-CREATE FUNCTION dblink_connect (text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_connect'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_connect (text, text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_connect'
-LANGUAGE C STRICT;
-
--- dblink_connect_u allows non-superusers to use
--- non-password authenticated connections, but initially
--- privileges are revoked from public
-CREATE FUNCTION dblink_connect_u (text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_connect'
-LANGUAGE C STRICT SECURITY DEFINER;
-
-CREATE FUNCTION dblink_connect_u (text, text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_connect'
-LANGUAGE C STRICT SECURITY DEFINER;
-
-REVOKE ALL ON FUNCTION dblink_connect_u (text) FROM public;
-REVOKE ALL ON FUNCTION dblink_connect_u (text, text) FROM public;
-
-CREATE FUNCTION dblink_disconnect ()
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_disconnect'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_disconnect (text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_disconnect'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_open (text, text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_open'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_open (text, text, boolean)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_open'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_open (text, text, text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_open'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_open (text, text, text, boolean)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_open'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_fetch (text, int)
-RETURNS setof record
-AS 'MODULE_PATHNAME','dblink_fetch'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_fetch (text, int, boolean)
-RETURNS setof record
-AS 'MODULE_PATHNAME','dblink_fetch'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_fetch (text, text, int)
-RETURNS setof record
-AS 'MODULE_PATHNAME','dblink_fetch'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_fetch (text, text, int, boolean)
-RETURNS setof record
-AS 'MODULE_PATHNAME','dblink_fetch'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_close (text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_close'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_close (text, boolean)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_close'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_close (text, text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_close'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_close (text, text, boolean)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_close'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink (text, text)
-RETURNS setof record
-AS 'MODULE_PATHNAME','dblink_record'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink (text, text, boolean)
-RETURNS setof record
-AS 'MODULE_PATHNAME','dblink_record'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink (text)
-RETURNS setof record
-AS 'MODULE_PATHNAME','dblink_record'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink (text, boolean)
-RETURNS setof record
-AS 'MODULE_PATHNAME','dblink_record'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_exec (text, text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_exec'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_exec (text, text, boolean)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_exec'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_exec (text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_exec'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_exec (text,boolean)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_exec'
-LANGUAGE C STRICT;
-
-CREATE TYPE dblink_pkey_results AS (position int, colname text);
-
-CREATE FUNCTION dblink_get_pkey (text)
-RETURNS setof dblink_pkey_results
-AS 'MODULE_PATHNAME','dblink_get_pkey'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_build_sql_insert (text, int2vector, int, _text, _text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_build_sql_insert'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_build_sql_delete (text, int2vector, int, _text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_build_sql_delete'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_build_sql_update (text, int2vector, int, _text, _text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_build_sql_update'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_current_query ()
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_current_query'
-LANGUAGE C;
-
-CREATE FUNCTION dblink_send_query(text, text)
-RETURNS int4
-AS 'MODULE_PATHNAME', 'dblink_send_query'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_is_busy(text)
-RETURNS int4
-AS 'MODULE_PATHNAME', 'dblink_is_busy'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_get_result(text)
-RETURNS SETOF record
-AS 'MODULE_PATHNAME', 'dblink_get_result'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_get_result(text, bool)
-RETURNS SETOF record
-AS 'MODULE_PATHNAME', 'dblink_get_result'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_get_connections()
-RETURNS text[]
-AS 'MODULE_PATHNAME', 'dblink_get_connections'
-LANGUAGE C;
-
-CREATE FUNCTION dblink_cancel_query(text)
-RETURNS text
-AS 'MODULE_PATHNAME', 'dblink_cancel_query'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_error_message(text)
-RETURNS text
-AS 'MODULE_PATHNAME', 'dblink_error_message'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_get_notify(
-    OUT notify_name TEXT,
-    OUT be_pid INT4,
-    OUT extra TEXT
-)
-RETURNS setof record
-AS 'MODULE_PATHNAME', 'dblink_get_notify'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_get_notify(
-    conname TEXT,
-    OUT notify_name TEXT,
-    OUT be_pid INT4,
-    OUT extra TEXT
-)
-RETURNS setof record
-AS 'MODULE_PATHNAME', 'dblink_get_notify'
-LANGUAGE C STRICT;
diff --git a/contrib/dblink/dblink--1.1.sql b/contrib/dblink/dblink--1.1.sql
new file mode 100644
index 0000000..e23b5c9
--- /dev/null
+++ b/contrib/dblink/dblink--1.1.sql
@@ -0,0 +1,234 @@
+/* contrib/dblink/dblink--1.1.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION dblink" to load this file. \quit
+
+-- dblink_connect now restricts non-superusers to password
+-- authenticated connections
+CREATE FUNCTION dblink_connect (text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_connect'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_connect (text, text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_connect'
+LANGUAGE C STRICT;
+
+-- dblink_connect_u allows non-superusers to use
+-- non-password authenticated connections, but initially
+-- privileges are revoked from public
+CREATE FUNCTION dblink_connect_u (text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_connect'
+LANGUAGE C STRICT SECURITY DEFINER;
+
+CREATE FUNCTION dblink_connect_u (text, text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_connect'
+LANGUAGE C STRICT SECURITY DEFINER;
+
+REVOKE ALL ON FUNCTION dblink_connect_u (text) FROM public;
+REVOKE ALL ON FUNCTION dblink_connect_u (text, text) FROM public;
+
+CREATE FUNCTION dblink_disconnect ()
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_disconnect'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_disconnect (text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_disconnect'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_open (text, text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_open'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_open (text, text, boolean)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_open'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_open (text, text, text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_open'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_open (text, text, text, boolean)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_open'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_fetch (text, int)
+RETURNS setof record
+AS 'MODULE_PATHNAME','dblink_fetch'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_fetch (text, int, boolean)
+RETURNS setof record
+AS 'MODULE_PATHNAME','dblink_fetch'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_fetch (text, text, int)
+RETURNS setof record
+AS 'MODULE_PATHNAME','dblink_fetch'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_fetch (text, text, int, boolean)
+RETURNS setof record
+AS 'MODULE_PATHNAME','dblink_fetch'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_close (text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_close'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_close (text, boolean)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_close'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_close (text, text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_close'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_close (text, text, boolean)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_close'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink (text, text)
+RETURNS setof record
+AS 'MODULE_PATHNAME','dblink_record'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink (text, text, boolean)
+RETURNS setof record
+AS 'MODULE_PATHNAME','dblink_record'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink (text)
+RETURNS setof record
+AS 'MODULE_PATHNAME','dblink_record'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink (text, boolean)
+RETURNS setof record
+AS 'MODULE_PATHNAME','dblink_record'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_exec (text, text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_exec'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_exec (text, text, boolean)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_exec'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_exec (text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_exec'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_exec (text,boolean)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_exec'
+LANGUAGE C STRICT;
+
+CREATE TYPE dblink_pkey_results AS (position int, colname text);
+
+CREATE FUNCTION dblink_get_pkey (text)
+RETURNS setof dblink_pkey_results
+AS 'MODULE_PATHNAME','dblink_get_pkey'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_build_sql_insert (text, int2vector, int, _text, _text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_build_sql_insert'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_build_sql_delete (text, int2vector, int, _text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_build_sql_delete'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_build_sql_update (text, int2vector, int, _text, _text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_build_sql_update'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_current_query ()
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_current_query'
+LANGUAGE C;
+
+CREATE FUNCTION dblink_send_query(text, text)
+RETURNS int4
+AS 'MODULE_PATHNAME', 'dblink_send_query'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_is_busy(text)
+RETURNS int4
+AS 'MODULE_PATHNAME', 'dblink_is_busy'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_get_result(text)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'dblink_get_result'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_get_result(text, bool)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'dblink_get_result'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_get_connections()
+RETURNS text[]
+AS 'MODULE_PATHNAME', 'dblink_get_connections'
+LANGUAGE C;
+
+CREATE FUNCTION dblink_cancel_query(text)
+RETURNS text
+AS 'MODULE_PATHNAME', 'dblink_cancel_query'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_error_message(text)
+RETURNS text
+AS 'MODULE_PATHNAME', 'dblink_error_message'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_get_notify(
+    OUT notify_name TEXT,
+    OUT be_pid INT4,
+    OUT extra TEXT
+)
+RETURNS setof record
+AS 'MODULE_PATHNAME', 'dblink_get_notify'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_get_notify(
+    conname TEXT,
+    OUT notify_name TEXT,
+    OUT be_pid INT4,
+    OUT extra TEXT
+)
+RETURNS setof record
+AS 'MODULE_PATHNAME', 'dblink_get_notify'
+LANGUAGE C STRICT;
+
+/* stuffs added since 1.1 */
+CREATE FUNCTION dblink_fdw_validator(
+    options text[],
+    catalog oid
+)
+RETURNS void
+AS 'MODULE_PATHNAME', 'dblink_fdw_validator'
+LANGUAGE C IMMUTABLE;
+
+CREATE FOREIGN DATA WRAPPER dblink_fdw VALIDATOR dblink_fdw_validator;
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index 9f1dac8..6545cd2 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -37,9 +37,12 @@
 #include "libpq-fe.h"
 
 #include "access/htup_details.h"
+#include "access/reloptions.h"
 #include "catalog/indexing.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_foreign_server.h"
 #include "catalog/pg_type.h"
+#include "catalog/pg_user_mapping.h"
 #include "executor/spi.h"
 #include "foreign/foreign.h"
 #include "funcapi.h"
@@ -113,6 +116,8 @@ static char *escape_param_str(const char *from);
 static void validate_pkattnums(Relation rel,
 				   int2vector *pkattnums_arg, int32 pknumatts_arg,
 				   int **pkattnums, int *pknumatts);
+static bool is_valid_dblink_option(PQconninfoOption *options,
+					   const char *option, Oid context);
 
 /* Global */
 static remoteConn *pconn = NULL;
@@ -1912,6 +1917,76 @@ dblink_get_notify(PG_FUNCTION_ARGS)
 	return (Datum) 0;
 }
 
+/*
+ * Validate the generic option given to SERVER or USER MAPPING.
+ * Raise an ERROR if the option is considered invalid.
+ *
+ * We just validate the name of options here, so semantic errors of options,
+ * such as invalid numeric format, will be detected at the attempt to connect.
+ */
+PG_FUNCTION_INFO_V1(dblink_fdw_validator);
+Datum
+dblink_fdw_validator(PG_FUNCTION_ARGS)
+{
+	List	   *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+	Oid			context = PG_GETARG_OID(1);
+	ListCell   *cell;
+	PQconninfoOption *options = NULL;
+
+	/*
+	 * Get list of valid libpq options.  It contains default values too, but we
+	 * don't care the values.  Obtained list is allocated with malloc, so we
+	 * must free it before leaving this function.
+	 */
+	options = PQconndefaults();
+	if (options == NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_FDW_OUT_OF_MEMORY),
+				 errmsg("out of memory"),
+				 errdetail("could not get libpq default connection options")));
+	PG_TRY();
+	{
+		/* Validate each supplied option. */
+		foreach(cell, options_list)
+		{
+			PQconninfoOption *opt;
+			DefElem    *def = lfirst(cell);
+			StringInfoData buf;
+
+			if (!is_valid_dblink_option(options, def->defname, context))
+			{
+				/*
+				 * Unknown option, or invalid option for the context specified,
+				 * complain about it.  Provide a hint with list of valid
+				 * options for the object.
+				 */
+				initStringInfo(&buf);
+				for (opt = options; opt->keyword; opt++)
+				{
+					if (is_valid_dblink_option(options, opt->keyword, context))
+						appendStringInfo(&buf, "%s%s",
+										 (buf.len > 0) ? ", " : "",
+										 opt->keyword);
+				}
+				ereport(ERROR,
+						(errcode(ERRCODE_FDW_OPTION_NAME_NOT_FOUND),
+						 errmsg("invalid option \"%s\"", def->defname),
+						 errhint("Valid options in this context are: %s",
+								 buf.data)));
+			}
+		}
+	}
+	PG_CATCH();
+	{
+		free(options);
+		PG_RE_THROW();
+	}
+	PG_END_TRY();
+
+	free(options);
+	PG_RETURN_VOID();
+}
+
 /*************************************************************
  * internal functions
  */
@@ -2768,3 +2843,52 @@ validate_pkattnums(Relation rel,
 					 errmsg("invalid attribute number %d", pkattnum)));
 	}
 }
+
+/*
+ * Check if the provided option is one of valid dblink options.  Valid libpq
+ * conninfo options are classified as below:
+ *
+ * every debug options : invalid in every context
+ * every secure options: valid in only USER MAPPING options
+ * "user"              : valid in only USER MAPPING options
+ * Others              : valid in both SERVER options and USER MAPPING options
+ *
+ * Note that we accept client_encoding as options, but it is always ignored
+ * because we override it with calling PQclientEncoding to ensure that proper
+ * conversion is used.
+ */
+static bool
+is_valid_dblink_option(PQconninfoOption *options, const char *option,
+					   Oid context)
+{
+	PQconninfoOption *opt;
+
+	for (opt = options; opt->keyword; opt++)
+		if (strcmp(opt->keyword, option) == 0)
+			break;
+	if (opt->keyword == NULL)
+		return false;
+
+	/*
+	 * All debug options "replication" is used in walreceiver.
+	 */
+	if (strcmp(opt->dispchar, "D") == 0)
+		return false;
+
+	/*
+	 * If the options is secure one or "user", it should be specified in only
+	 * USER MAPPING options.  Others should be specified in only SERVER options.
+	 */
+	if (strcmp(opt->keyword, "user") == 0 || strcmp(opt->dispchar, "*") == 0)
+	{
+		if (context != UserMappingRelationId)
+			return false;
+	}
+	else
+	{
+		if (context != ForeignServerRelationId)
+			return false;
+	}
+
+	return true;
+}
diff --git a/contrib/dblink/dblink.control b/contrib/dblink/dblink.control
index 4333a9b..39f439a 100644
--- a/contrib/dblink/dblink.control
+++ b/contrib/dblink/dblink.control
@@ -1,5 +1,5 @@
 # dblink extension
 comment = 'connect to other PostgreSQL databases from within a database'
-default_version = '1.0'
+default_version = '1.1'
 module_pathname = '$libdir/dblink'
 relocatable = true
diff --git a/contrib/dblink/dblink.h b/contrib/dblink/dblink.h
index 935d283..fd11658 100644
--- a/contrib/dblink/dblink.h
+++ b/contrib/dblink/dblink.h
@@ -58,5 +58,6 @@ extern Datum dblink_build_sql_delete(PG_FUNCTION_ARGS);
 extern Datum dblink_build_sql_update(PG_FUNCTION_ARGS);
 extern Datum dblink_current_query(PG_FUNCTION_ARGS);
 extern Datum dblink_get_notify(PG_FUNCTION_ARGS);
+extern Datum dblink_fdw_validator(PG_FUNCTION_ARGS);
 
 #endif   /* DBLINK_H */
diff --git a/contrib/dblink/expected/dblink.out b/contrib/dblink/expected/dblink.out
index a1899ed..1cc06e3 100644
--- a/contrib/dblink/expected/dblink.out
+++ b/contrib/dblink/expected/dblink.out
@@ -783,8 +783,16 @@ SELECT dblink_disconnect('dtest1');
 
 -- test foreign data wrapper functionality
 CREATE USER dblink_regression_test;
-CREATE FOREIGN DATA WRAPPER postgresql;
-CREATE SERVER fdtest FOREIGN DATA WRAPPER postgresql OPTIONS (dbname 'contrib_regression');
+CREATE SERVER fdtest FOREIGN DATA WRAPPER dblink_fdw OPTIONS (invalid 'val');   -- ERROR
+ERROR:  invalid option "invalid"
+HINT:  Valid options in this context are: service, connect_timeout, dbname, host, hostaddr, port, client_encoding, application_name, fallback_application_name, keepalives, keepalives_idle, keepalives_interval, keepalives_count, sslmode, sslcompression, sslcert, sslkey, sslrootcert, sslcrl, requirepeer
+CREATE SERVER fdtest FOREIGN DATA WRAPPER dblink_fdw OPTIONS (password 'val');  -- ERROR
+ERROR:  invalid option "password"
+HINT:  Valid options in this context are: service, connect_timeout, dbname, host, hostaddr, port, client_encoding, application_name, fallback_application_name, keepalives, keepalives_idle, keepalives_interval, keepalives_count, sslmode, sslcompression, sslcert, sslkey, sslrootcert, sslcrl, requirepeer
+CREATE SERVER fdtest FOREIGN DATA WRAPPER dblink_fdw OPTIONS (dbname 'contrib_regression');
+CREATE USER MAPPING FOR public SERVER fdtest OPTIONS (server 'localhost'); -- ERROR
+ERROR:  invalid option "server"
+HINT:  Valid options in this context are: user, password
 CREATE USER MAPPING FOR public SERVER fdtest;
 GRANT USAGE ON FOREIGN SERVER fdtest TO dblink_regression_test;
 GRANT EXECUTE ON FUNCTION dblink_connect_u(text, text) TO dblink_regression_test;
@@ -823,7 +831,6 @@ REVOKE EXECUTE ON FUNCTION dblink_connect_u(text, text) FROM dblink_regression_t
 DROP USER dblink_regression_test;
 DROP USER MAPPING FOR public SERVER fdtest;
 DROP SERVER fdtest;
-DROP FOREIGN DATA WRAPPER postgresql;
 -- test asynchronous notifications
 SELECT dblink_connect('dbname=contrib_regression');
  dblink_connect 
diff --git a/contrib/dblink/sql/dblink.sql b/contrib/dblink/sql/dblink.sql
index 8c8ffe2..171ee50 100644
--- a/contrib/dblink/sql/dblink.sql
+++ b/contrib/dblink/sql/dblink.sql
@@ -361,8 +361,10 @@ SELECT dblink_disconnect('dtest1');
 -- test foreign data wrapper functionality
 CREATE USER dblink_regression_test;
 
-CREATE FOREIGN DATA WRAPPER postgresql;
-CREATE SERVER fdtest FOREIGN DATA WRAPPER postgresql OPTIONS (dbname 'contrib_regression');
+CREATE SERVER fdtest FOREIGN DATA WRAPPER dblink_fdw OPTIONS (invalid 'val');   -- ERROR
+CREATE SERVER fdtest FOREIGN DATA WRAPPER dblink_fdw OPTIONS (password 'val');  -- ERROR
+CREATE SERVER fdtest FOREIGN DATA WRAPPER dblink_fdw OPTIONS (dbname 'contrib_regression');
+CREATE USER MAPPING FOR public SERVER fdtest OPTIONS (server 'localhost'); -- ERROR
 CREATE USER MAPPING FOR public SERVER fdtest;
 GRANT USAGE ON FOREIGN SERVER fdtest TO dblink_regression_test;
 GRANT EXECUTE ON FUNCTION dblink_connect_u(text, text) TO dblink_regression_test;
@@ -381,7 +383,6 @@ REVOKE EXECUTE ON FUNCTION dblink_connect_u(text, text) FROM dblink_regression_t
 DROP USER dblink_regression_test;
 DROP USER MAPPING FOR public SERVER fdtest;
 DROP SERVER fdtest;
-DROP FOREIGN DATA WRAPPER postgresql;
 
 -- test asynchronous notifications
 SELECT dblink_connect('dbname=contrib_regression');
diff --git a/doc/src/sgml/dblink.sgml b/doc/src/sgml/dblink.sgml
index 72ca765..186ab86 100644
--- a/doc/src/sgml/dblink.sgml
+++ b/doc/src/sgml/dblink.sgml
@@ -46,12 +46,10 @@ dblink_connect(text connname, text connstr) returns text
 
    <para>
     The connection string may also be the name of an existing foreign
-    server.  It is recommended to use
-    the <function>postgresql_fdw_validator</function> when defining
-    the corresponding foreign-data wrapper.  See the example below, as
-    well as the following:
+    server.  It is recommended to use the foreign-data wrapper
+    <literal>dblink_fdw</literal> when defining the corresponding foreign
+    server.  See the example below, as well as the following:
     <simplelist type="inline">
-     <member><xref linkend="sql-createforeigndatawrapper"></member>
      <member><xref linkend="sql-createserver"></member>
      <member><xref linkend="sql-createusermapping"></member>
     </simplelist>
@@ -136,8 +134,7 @@ SELECT dblink_connect('myconn', 'dbname=postgres');
 --       DETAIL:  Non-superuser cannot connect if the server does not request a password.
 --       HINT:  Target server's authentication method must be changed.
 CREATE USER dblink_regression_test WITH PASSWORD 'secret';
-CREATE FOREIGN DATA WRAPPER postgresql VALIDATOR postgresql_fdw_validator;
-CREATE SERVER fdtest FOREIGN DATA WRAPPER postgresql OPTIONS (hostaddr '127.0.0.1', dbname 'contrib_regression');
+CREATE SERVER fdtest FOREIGN DATA WRAPPER dblink_fdw OPTIONS (hostaddr '127.0.0.1', dbname 'contrib_regression');
 
 CREATE USER MAPPING FOR dblink_regression_test SERVER fdtest OPTIONS (user 'dblink_regression_test', password 'secret');
 GRANT USAGE ON FOREIGN SERVER fdtest TO dblink_regression_test;
@@ -173,7 +170,6 @@ REVOKE SELECT ON TABLE foo FROM  dblink_regression_test;
 DROP USER MAPPING FOR dblink_regression_test SERVER fdtest;
 DROP USER dblink_regression_test;
 DROP SERVER fdtest;
-DROP FOREIGN DATA WRAPPER postgresql;
 </screen>
   </refsect1>
  </refentry>
diff --git a/doc/src/sgml/ref/create_foreign_data_wrapper.sgml b/doc/src/sgml/ref/create_foreign_data_wrapper.sgml
index 804fb47..d9936e8 100644
--- a/doc/src/sgml/ref/create_foreign_data_wrapper.sgml
+++ b/doc/src/sgml/ref/create_foreign_data_wrapper.sgml
@@ -121,14 +121,6 @@ CREATE FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable>
    There is no support for updating a foreign table, and optimization of
    queries is primitive (and mostly left to the wrapper, too).
   </para>
-
-  <para>
-   There is one built-in foreign-data wrapper validator function
-   provided:
-   <filename>postgresql_fdw_validator</filename>, which accepts
-   options corresponding to <application>libpq</> connection
-   parameters.
-  </para>
  </refsect1>
 
  <refsect1>
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index d8845aa..8400db4 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -27,7 +27,6 @@
 
 
 extern Datum pg_options_to_table(PG_FUNCTION_ARGS);
-extern Datum postgresql_fdw_validator(PG_FUNCTION_ARGS);
 
 
 /*
@@ -434,105 +433,6 @@ pg_options_to_table(PG_FUNCTION_ARGS)
 
 
 /*
- * Describes the valid options for postgresql FDW, server, and user mapping.
- */
-struct ConnectionOption
-{
-	const char *optname;
-	Oid			optcontext;		/* Oid of catalog in which option may appear */
-};
-
-/*
- * Copied from fe-connect.c PQconninfoOptions.
- *
- * The list is small - don't bother with bsearch if it stays so.
- */
-static struct ConnectionOption libpq_conninfo_options[] = {
-	{"authtype", ForeignServerRelationId},
-	{"service", ForeignServerRelationId},
-	{"user", UserMappingRelationId},
-	{"password", UserMappingRelationId},
-	{"connect_timeout", ForeignServerRelationId},
-	{"dbname", ForeignServerRelationId},
-	{"host", ForeignServerRelationId},
-	{"hostaddr", ForeignServerRelationId},
-	{"port", ForeignServerRelationId},
-	{"tty", ForeignServerRelationId},
-	{"options", ForeignServerRelationId},
-	{"requiressl", ForeignServerRelationId},
-	{"sslmode", ForeignServerRelationId},
-	{"gsslib", ForeignServerRelationId},
-	{NULL, InvalidOid}
-};
-
-
-/*
- * Check if the provided option is one of libpq conninfo options.
- * context is the Oid of the catalog the option came from, or 0 if we
- * don't care.
- */
-static bool
-is_conninfo_option(const char *option, Oid context)
-{
-	struct ConnectionOption *opt;
-
-	for (opt = libpq_conninfo_options; opt->optname; opt++)
-		if (context == opt->optcontext && strcmp(opt->optname, option) == 0)
-			return true;
-	return false;
-}
-
-
-/*
- * Validate the generic option given to SERVER or USER MAPPING.
- * Raise an ERROR if the option or its value is considered
- * invalid.
- *
- * Valid server options are all libpq conninfo options except
- * user and password -- these may only appear in USER MAPPING options.
- */
-Datum
-postgresql_fdw_validator(PG_FUNCTION_ARGS)
-{
-	List	   *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
-	Oid			catalog = PG_GETARG_OID(1);
-
-	ListCell   *cell;
-
-	foreach(cell, options_list)
-	{
-		DefElem    *def = lfirst(cell);
-
-		if (!is_conninfo_option(def->defname, catalog))
-		{
-			struct ConnectionOption *opt;
-			StringInfoData buf;
-
-			/*
-			 * Unknown option specified, complain about it. Provide a hint
-			 * with list of valid options for the object.
-			 */
-			initStringInfo(&buf);
-			for (opt = libpq_conninfo_options; opt->optname; opt++)
-				if (catalog == opt->optcontext)
-					appendStringInfo(&buf, "%s%s", (buf.len > 0) ? ", " : "",
-									 opt->optname);
-
-			ereport(ERROR,
-					(errcode(ERRCODE_SYNTAX_ERROR),
-					 errmsg("invalid option \"%s\"", def->defname),
-					 errhint("Valid options in this context are: %s",
-							 buf.data)));
-
-			PG_RETURN_BOOL(false);
-		}
-	}
-
-	PG_RETURN_BOOL(true);
-}
-
-
-/*
  * get_foreign_data_wrapper_oid - given a FDW name, look up the OID
  *
  * If missing_ok is false, throw an error if name not found.  If true, just
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 77a3b41..67a488e8 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3433,9 +3433,6 @@ DESCR("filenode identifier of relation");
 DATA(insert OID = 3034 ( pg_relation_filepath	PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 25 "2205" _null_ _null_ _null_ _null_ pg_relation_filepath _null_ _null_ _null_ ));
 DESCR("file path of relation");
 
-DATA(insert OID = 2316 ( postgresql_fdw_validator PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "1009 26" _null_ _null_ _null_ _null_ postgresql_fdw_validator _null_ _null_ _null_));
-DESCR("(internal)");
-
 DATA(insert OID = 2290 (  record_in			PGNSP PGUID 12 1 0 0 0 f f f f t f s 3 0 2249 "2275 26 23" _null_ _null_ _null_ _null_	record_in _null_ _null_ _null_ ));
 DESCR("I/O");
 DATA(insert OID = 2291 (  record_out		PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 2275 "2249" _null_ _null_ _null_ _null_ record_out _null_ _null_ _null_ ));
diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out
index 1bfdca1..0a5b525 100644
--- a/src/test/regress/expected/foreign_data.out
+++ b/src/test/regress/expected/foreign_data.out
@@ -13,6 +13,40 @@ CREATE ROLE regress_test_role2;
 CREATE ROLE regress_test_role_super SUPERUSER;
 CREATE ROLE regress_test_indirect;
 CREATE ROLE unprivileged_role;
+-- create validator for this test
+CREATE OR REPLACE FUNCTION postgresql_fdw_validator(params text[], oid oid) RETURNS bool AS $$
+DECLARE
+	c text;
+	val text[];
+BEGIN
+	-- validate each option
+	FOR c IN SELECT unnest(params) LOOP
+		-- separate key and value
+		SELECT regexp_split_to_array(c, '=') INTO val;
+		RAISE DEBUG 'record %=%', val[1], val[2];
+
+		IF oid = 'pg_catalog.pg_foreign_server'::regclass THEN
+			IF val[1] NOT IN ('authtype', 'service', 'connect_timeout',
+				'dbname', 'host', 'hostaddr', 'port', 'tty', 'options',
+				'requiressl', 'sslmode', 'gsslib') THEN
+				RAISE 'invalid option "%"', val[1]
+					USING HINT = 'Valid options in this context are: authtype, service, connect_timeout, dbname, host, hostaddr, port, tty, options, requiressl, sslmode, gsslib';
+			END IF;
+		ELSIF oid = 'pg_catalog.pg_user_mapping'::regclass THEN
+			IF val[1] NOT IN ('user', 'password') THEN
+				RAISE 'invalid option "%"', val[1]
+					USING HINT = 'Valid options in this context are: user, password';
+			END IF;
+		ELSE
+			RAISE 'invalid option "%"', val[1]
+				USING HINT = 'Valid options in this context are: ';
+		END IF;
+
+	END LOOP;
+
+	RETURN true;
+END;
+$$ language plpgsql;
 CREATE FOREIGN DATA WRAPPER dummy;
 COMMENT ON FOREIGN DATA WRAPPER dummy IS 'useless';
 CREATE FOREIGN DATA WRAPPER postgresql VALIDATOR postgresql_fdw_validator;
@@ -1202,6 +1236,7 @@ DROP ROLE regress_test_role2;
 DROP FOREIGN DATA WRAPPER postgresql CASCADE;
 DROP FOREIGN DATA WRAPPER dummy CASCADE;
 NOTICE:  drop cascades to server s0
+DROP FUNCTION postgresql_fdw_validator(text[], oid);
 \c
 DROP ROLE foreign_data_user;
 -- At this point we should have no wrappers, no servers, and no mappings.
diff --git a/src/test/regress/sql/foreign_data.sql b/src/test/regress/sql/foreign_data.sql
index 38057db..46e358b 100644
--- a/src/test/regress/sql/foreign_data.sql
+++ b/src/test/regress/sql/foreign_data.sql
@@ -20,6 +20,41 @@ CREATE ROLE regress_test_role_super SUPERUSER;
 CREATE ROLE regress_test_indirect;
 CREATE ROLE unprivileged_role;
 
+-- create validator for this test
+CREATE OR REPLACE FUNCTION postgresql_fdw_validator(params text[], oid oid) RETURNS bool AS $$
+DECLARE
+	c text;
+	val text[];
+BEGIN
+	-- validate each option
+	FOR c IN SELECT unnest(params) LOOP
+		-- separate key and value
+		SELECT regexp_split_to_array(c, '=') INTO val;
+		RAISE DEBUG 'record %=%', val[1], val[2];
+
+		IF oid = 'pg_catalog.pg_foreign_server'::regclass THEN
+			IF val[1] NOT IN ('authtype', 'service', 'connect_timeout',
+				'dbname', 'host', 'hostaddr', 'port', 'tty', 'options',
+				'requiressl', 'sslmode', 'gsslib') THEN
+				RAISE 'invalid option "%"', val[1]
+					USING HINT = 'Valid options in this context are: authtype, service, connect_timeout, dbname, host, hostaddr, port, tty, options, requiressl, sslmode, gsslib';
+			END IF;
+		ELSIF oid = 'pg_catalog.pg_user_mapping'::regclass THEN
+			IF val[1] NOT IN ('user', 'password') THEN
+				RAISE 'invalid option "%"', val[1]
+					USING HINT = 'Valid options in this context are: user, password';
+			END IF;
+		ELSE
+			RAISE 'invalid option "%"', val[1]
+				USING HINT = 'Valid options in this context are: ';
+		END IF;
+
+	END LOOP;
+
+	RETURN true;
+END;
+$$ language plpgsql;
+
 CREATE FOREIGN DATA WRAPPER dummy;
 COMMENT ON FOREIGN DATA WRAPPER dummy IS 'useless';
 CREATE FOREIGN DATA WRAPPER postgresql VALIDATOR postgresql_fdw_validator;
@@ -497,6 +532,7 @@ DROP ROLE unprivileged_role;
 DROP ROLE regress_test_role2;
 DROP FOREIGN DATA WRAPPER postgresql CASCADE;
 DROP FOREIGN DATA WRAPPER dummy CASCADE;
+DROP FUNCTION postgresql_fdw_validator(text[], oid);
 \c
 DROP ROLE foreign_data_user;