diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 30f398069703c16fbade444311cc123b7eff38a8..88eaca0bea082f5e800d08402d7468c5c2b6b578 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -3031,16 +3031,27 @@
       <entry>Owner of the foreign-data wrapper</entry>
      </row>
 
+     <row>
+      <entry><structfield>fdwhandler</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>
+       References a handler function that is responsible for
+       supplying execution routines for the foreign-data wrapper.
+       Zero if no handler is provided
+      </entry>
+     </row>
+
      <row>
       <entry><structfield>fdwvalidator</structfield></entry>
       <entry><type>oid</type></entry>
       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
       <entry>
        References a validator function that is responsible for
-       checking the validity of the generic options given to the
-       foreign-data wrapper, as well as to foreign servers and user
+       checking the validity of the options given to the
+       foreign-data wrapper, as well as options for foreign servers and user
        mappings using the foreign-data wrapper.  Zero if no validator
-       is provided.
+       is provided
       </entry>
      </row>
 
@@ -3079,8 +3090,8 @@
 
   <para>
    The catalog <structname>pg_foreign_server</structname> stores
-   foreign server definitions.  A foreign server describes the
-   connection to a remote server, managing external data.  Foreign
+   foreign server definitions.  A foreign server describes a source
+   of external data, such as a remote server.  Foreign
    servers are accessed via foreign-data wrappers.
   </para>
 
@@ -3116,7 +3127,7 @@
       <entry><structfield>srvfdw</structfield></entry>
       <entry><type>oid</type></entry>
       <entry><literal><link linkend="catalog-pg-foreign-data-wrapper"><structname>pg_foreign_data_wrapper</structname></link>.oid</literal></entry>
-      <entry>The OID of the foreign-data wrapper of this foreign server</entry>
+      <entry>OID of the foreign-data wrapper of this foreign server</entry>
      </row>
 
      <row>
@@ -3167,9 +3178,12 @@
   </indexterm>
 
   <para>
-   The catalog <structname>pg_foreign_table</structname> contains part
-   of the information about foreign tables.
-   The rest is mostly in <structname>pg_class</structname>.
+   The catalog <structname>pg_foreign_table</structname> contains
+   auxiliary information about foreign tables.  A foreign table is
+   primarily represented by a <structname>pg_class</structname> entry,
+   just like a regular table.  Its <structname>pg_foreign_table</structname>
+   entry contains the information that is pertinent only to foreign tables
+   and not any other kind of relation.
   </para>
 
   <table>
@@ -3190,14 +3204,14 @@
       <entry><structfield>ftrelid</structfield></entry>
       <entry><type>oid</type></entry>
       <entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
-      <entry>The OID of the <structname>pg_class</> entry for this foreign table</entry>
+      <entry>OID of the <structname>pg_class</> entry for this foreign table</entry>
      </row>
 
      <row>
       <entry><structfield>ftserver</structfield></entry>
       <entry><type>oid</type></entry>
       <entry><literal><link linkend="catalog-pg-foreign-server"><structname>pg_foreign_server</structname></link>.oid</literal></entry>
-      <entry>The OID of the foreign server for this foreign table</entry>
+      <entry>OID of the foreign server for this foreign table</entry>
      </row>
 
      <row>
@@ -3205,7 +3219,7 @@
       <entry><type>text[]</type></entry>
       <entry></entry>
       <entry>
-       Foreign table options, as <quote>keyword=value</> strings.
+       Foreign table options, as <quote>keyword=value</> strings
       </entry>
      </row>
     </tbody>
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 253b7578985eaab013bfafd7046da1d9e5d556b2..b8f6e238f0b547e3cbebd5569a8a3ccd58024140 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -4431,6 +4431,10 @@ SELECT * FROM pg_attribute
     <primary>language_handler</primary>
    </indexterm>
 
+   <indexterm zone="datatype-pseudo">
+    <primary>fdw_handler</primary>
+   </indexterm>
+
    <indexterm zone="datatype-pseudo">
     <primary>cstring</primary>
    </indexterm>
@@ -4513,6 +4517,11 @@ SELECT * FROM pg_attribute
         <entry>A procedural language call handler is declared to return <type>language_handler</>.</entry>
        </row>
 
+       <row>
+        <entry><type>fdw_handler</></entry>
+        <entry>A foreign-data wrapper handler is declared to return <type>fdw_handler</>.</entry>
+       </row>
+
        <row>
         <entry><type>record</></entry>
         <entry>Identifies a function returning an unspecified row type.</entry>
diff --git a/doc/src/sgml/ref/alter_foreign_data_wrapper.sgml b/doc/src/sgml/ref/alter_foreign_data_wrapper.sgml
index 4e9e8a2e28a323557c2da07ce1c50eda0f0f64c4..af56ed7561ddfeac66951f3874bfea18da3cff19 100644
--- a/doc/src/sgml/ref/alter_foreign_data_wrapper.sgml
+++ b/doc/src/sgml/ref/alter_foreign_data_wrapper.sgml
@@ -22,7 +22,8 @@ PostgreSQL documentation
  <refsynopsisdiv>
 <synopsis>
 ALTER FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable>
-    [ VALIDATOR <replaceable class="parameter">valfunction</replaceable> | NO VALIDATOR ]
+    [ HANDLER <replaceable class="parameter">handler_function</replaceable> | NO HANDLER ]
+    [ VALIDATOR <replaceable class="parameter">validator_function</replaceable> | NO VALIDATOR ]
     [ OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ]) ]
 ALTER FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable> OWNER TO <replaceable>new_owner</replaceable>
 </synopsis>
@@ -34,7 +35,7 @@ ALTER FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable> OWN
   <para>
    <command>ALTER FOREIGN DATA WRAPPER</command> changes the
    definition of a foreign-data wrapper.  The first form of the
-   command changes the library or the generic options of the
+   command changes the support functions or the generic options of the
    foreign-data wrapper (at least one clause is required).  The second
    form changes the owner of the foreign-data wrapper.
   </para>
@@ -59,10 +60,33 @@ ALTER FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable> OWN
    </varlistentry>
 
    <varlistentry>
-    <term><literal>VALIDATOR <replaceable class="parameter">valfunction</replaceable></literal></term>
+    <term><literal>HANDLER <replaceable class="parameter">handler_function</replaceable></literal></term>
     <listitem>
      <para>
-      Specifies a new foreign-data wrapper validator function.
+      Specifies a new handler function for the foreign-data wrapper.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>NO HANDLER</literal></term>
+    <listitem>
+     <para>
+      This is used to specify that the foreign-data wrapper should no
+      longer have a handler function.
+     </para>
+     <para>
+      Note that foreign tables that use a foreign-data wrapper with no
+      handler cannot be accessed.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>VALIDATOR <replaceable class="parameter">validator_function</replaceable></literal></term>
+    <listitem>
+     <para>
+      Specifies a new validator function for the foreign-data wrapper.
      </para>
 
      <para>
@@ -94,7 +118,7 @@ ALTER FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable> OWN
       specify the action to be performed.  <literal>ADD</> is assumed
       if no operation is explicitly specified.  Option names must be
       unique; names and values are also validated using the foreign
-      data wrapper library.
+      data wrapper's validator function, if any.
      </para>
     </listitem>
    </varlistentry>
@@ -126,9 +150,8 @@ ALTER FOREIGN DATA WRAPPER dbi VALIDATOR bob.myvalidator;
 
   <para>
    <command>ALTER FOREIGN DATA WRAPPER</command> conforms to ISO/IEC
-   9075-9 (SQL/MED).  The standard does not specify the <literal>
-   VALIDATOR</literal> and <literal>OWNER TO</> variants of the
-   command.
+   9075-9 (SQL/MED), except that the <literal>HANDLER</literal>,
+   <literal>VALIDATOR</> and <literal>OWNER TO</> clauses are extensions.
   </para>
  </refsect1>
 
diff --git a/doc/src/sgml/ref/create_foreign_data_wrapper.sgml b/doc/src/sgml/ref/create_foreign_data_wrapper.sgml
index f626d56665b25722987107b8c6effb6ca0657de1..711f32b118b31d5c4b04c100d0d23c6939f1bdb9 100644
--- a/doc/src/sgml/ref/create_foreign_data_wrapper.sgml
+++ b/doc/src/sgml/ref/create_foreign_data_wrapper.sgml
@@ -22,7 +22,8 @@ PostgreSQL documentation
  <refsynopsisdiv>
 <synopsis>
 CREATE FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable>
-    [ VALIDATOR <replaceable class="parameter">valfunction</replaceable> | NO VALIDATOR ]
+    [ HANDLER <replaceable class="parameter">handler_function</replaceable> | NO HANDLER ]
+    [ VALIDATOR <replaceable class="parameter">validator_function</replaceable> | NO VALIDATOR ]
     [ OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [, ... ] ) ]
 </synopsis>
  </refsynopsisdiv>
@@ -59,13 +60,32 @@ CREATE FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable>
    </varlistentry>
 
    <varlistentry>
-    <term><literal>VALIDATOR <replaceable class="parameter">valfunction</replaceable></literal></term>
+    <term><literal>HANDLER <replaceable class="parameter">handler_function</replaceable></literal></term>
     <listitem>
      <para>
-      <replaceable class="parameter">valfunction</replaceable> is the
+      <replaceable class="parameter">handler_function</replaceable> is the
+      name of a previously registered function that will be called to
+      retrieve the execution functions for foreign tables.
+      The handler function must take no arguments, and
+      its return type must be <type>fdw_handler</type>.
+     </para>
+
+     <para>
+      It is possible to create a foreign-data wrapper with no handler
+      function, but foreign tables using such a wrapper can only be declared,
+      not accessed.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>VALIDATOR <replaceable class="parameter">validator_function</replaceable></literal></term>
+    <listitem>
+     <para>
+      <replaceable class="parameter">validator_function</replaceable> is the
       name of a previously registered function that will be called to
       check the generic options given to the foreign-data wrapper, as
-      well as to foreign servers and user mappings using the
+      well as options for foreign servers and user mappings using the
       foreign-data wrapper.  If no validator function or <literal>NO
       VALIDATOR</literal> is specified, then options will not be
       checked at creation time.  (Foreign-data wrappers will possibly
@@ -75,8 +95,8 @@ CREATE FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable>
       contain the array of options as stored in the system catalogs,
       and one of type <type>oid</type>, which will be the OID of the
       system catalog containing the options. The return type is ignored;
-      the function should indicate invalid options using the
-      <function>ereport()</function> function.
+      the function should report invalid options using the
+      <function>ereport(ERROR)</function> function.
      </para>
     </listitem>
    </varlistentry>
@@ -87,8 +107,8 @@ CREATE FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable>
      <para>
       This clause specifies options for the new foreign-data wrapper.
       The allowed option names and values are specific to each foreign
-      data wrapper and are validated using the foreign-data wrapper
-      library.  Option names must be unique.
+      data wrapper and are validated using the foreign-data wrapper's
+      validator function.  Option names must be unique.
      </para>
     </listitem>
    </varlistentry>
@@ -122,17 +142,17 @@ CREATE FOREIGN DATA WRAPPER <replaceable class="parameter">name</replaceable>
   <title>Examples</title>
 
   <para>
-   Create a foreign-data wrapper <literal>dummy</>:
+   Create a useless foreign-data wrapper <literal>dummy</>:
 <programlisting>
 CREATE FOREIGN DATA WRAPPER dummy;
 </programlisting>
   </para>
 
   <para>
-   Create a foreign-data wrapper <literal>postgresql</> with
-   validator function <literal>postgresql_fdw_validator</>:
+   Create a foreign-data wrapper <literal>file</> with
+   handler function <literal>file_fdw_handler</>:
 <programlisting>
-CREATE FOREIGN DATA WRAPPER postgresql VALIDATOR postgresql_fdw_validator;
+CREATE FOREIGN DATA WRAPPER file HANDLER file_fdw_handler;
 </programlisting>
   </para>
 
@@ -151,10 +171,10 @@ CREATE FOREIGN DATA WRAPPER mywrapper
 
   <para>
    <command>CREATE FOREIGN DATA WRAPPER</command> conforms to ISO/IEC
-   9075-9 (SQL/MED), with the exception that
-   the <literal>VALIDATOR</literal> clause is an extension and the
+   9075-9 (SQL/MED), with the exception that the <literal>HANDLER</literal>
+   and <literal>VALIDATOR</literal> clauses are extensions and the standard
    clauses <literal>LIBRARY</literal> and <literal>LANGUAGE</literal>
-   are not yet implemented in PostgreSQL.
+   are not implemented in PostgreSQL.
   </para>
 
   <para>
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index a2b5358e16fe7e9daf178fcd4f9d2b2115a9360c..acd40c1f4e9edc3a12644efc8f90c85b88806e2c 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -165,7 +165,7 @@ transformGenericOptions(Oid catalogId,
 
 	result = optionListToArray(resultOptions);
 
-	if (fdwvalidator)
+	if (OidIsValid(fdwvalidator) && DatumGetPointer(result) != NULL)
 		OidFunctionCall2(fdwvalidator, result, ObjectIdGetDatum(catalogId));
 
 	return result;
@@ -246,8 +246,9 @@ AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId)
 								newOwnerId);
 	}
 
-	heap_close(rel, NoLock);
 	heap_freetuple(tup);
+
+	heap_close(rel, RowExclusiveLock);
 }
 
 
@@ -308,25 +309,98 @@ AlterForeignServerOwner(const char *name, Oid newOwnerId)
 								newOwnerId);
 	}
 
-	heap_close(rel, NoLock);
 	heap_freetuple(tup);
+
+	heap_close(rel, RowExclusiveLock);
 }
 
 
+/*
+ * Convert a handler function name passed from the parser to an Oid.
+ */
+static Oid
+lookup_fdw_handler_func(DefElem *handler)
+{
+	Oid			handlerOid;
+
+	if (handler == NULL || handler->arg == NULL)
+		return InvalidOid;
+
+	/* handlers have no arguments */
+	handlerOid = LookupFuncName((List *) handler->arg, 0, NULL, false);
+
+	/* check that handler has correct return type */
+	if (get_func_rettype(handlerOid) != FDW_HANDLEROID)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("function %s must return type \"fdw_handler\"",
+						NameListToString((List *) handler->arg))));
+
+	return handlerOid;
+}
+
 /*
  * Convert a validator function name passed from the parser to an Oid.
  */
 static Oid
-lookup_fdw_validator_func(List *validator)
+lookup_fdw_validator_func(DefElem *validator)
 {
 	Oid			funcargtypes[2];
 
+	if (validator == NULL || validator->arg == NULL)
+		return InvalidOid;
+
+	/* validators take text[], oid */
 	funcargtypes[0] = TEXTARRAYOID;
 	funcargtypes[1] = OIDOID;
-	return LookupFuncName(validator, 2, funcargtypes, false);
-	/* return value is ignored, so we don't check the type */
+
+	return LookupFuncName((List *) validator->arg, 2, funcargtypes, false);
+	/* validator's return value is ignored, so we don't check the type */
 }
 
+/*
+ * Process function options of CREATE/ALTER FDW
+ */
+static void
+parse_func_options(List *func_options,
+				   bool *handler_given, Oid *fdwhandler,
+				   bool *validator_given, Oid *fdwvalidator)
+{
+	ListCell   *cell;
+
+	*handler_given = false;
+	*validator_given = false;
+	/* return InvalidOid if not given */
+	*fdwhandler = InvalidOid;
+	*fdwvalidator = InvalidOid;
+
+	foreach(cell, func_options)
+	{
+		DefElem    *def = (DefElem *) lfirst(cell);
+
+		if (strcmp(def->defname, "handler") == 0)
+		{
+			if (*handler_given)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("conflicting or redundant options")));
+			*handler_given = true;
+			*fdwhandler = lookup_fdw_handler_func(def);
+		}
+		else if (strcmp(def->defname, "validator") == 0)
+		{
+			if (*validator_given)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("conflicting or redundant options")));
+			*validator_given = true;
+			*fdwvalidator = lookup_fdw_validator_func(def);
+		}
+		else
+			elog(ERROR, "option \"%s\" not recognized",
+				 def->defname);
+	}
+}
 
 /*
  * Create a foreign-data wrapper
@@ -339,6 +413,9 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
 	bool		nulls[Natts_pg_foreign_data_wrapper];
 	HeapTuple	tuple;
 	Oid			fdwId;
+	bool		handler_given;
+	bool		validator_given;
+	Oid			fdwhandler;
 	Oid			fdwvalidator;
 	Datum		fdwoptions;
 	Oid			ownerId;
@@ -377,12 +454,13 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
 		DirectFunctionCall1(namein, CStringGetDatum(stmt->fdwname));
 	values[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(ownerId);
 
-	if (stmt->validator)
-		fdwvalidator = lookup_fdw_validator_func(stmt->validator);
-	else
-		fdwvalidator = InvalidOid;
+	/* Lookup handler and validator functions, if given */
+	parse_func_options(stmt->func_options,
+					   &handler_given, &fdwhandler,
+					   &validator_given, &fdwvalidator);
 
-	values[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = fdwvalidator;
+	values[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
+	values[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
 
 	nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
 
@@ -408,7 +486,15 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
 	myself.objectId = fdwId;
 	myself.objectSubId = 0;
 
-	if (fdwvalidator)
+	if (OidIsValid(fdwhandler))
+	{
+		referenced.classId = ProcedureRelationId;
+		referenced.objectId = fdwhandler;
+		referenced.objectSubId = 0;
+		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+	}
+
+	if (OidIsValid(fdwvalidator))
 	{
 		referenced.classId = ProcedureRelationId;
 		referenced.objectId = fdwvalidator;
@@ -425,7 +511,7 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
 	InvokeObjectAccessHook(OAT_POST_CREATE,
 						   ForeignDataWrapperRelationId, fdwId, 0);
 
-	heap_close(rel, NoLock);
+	heap_close(rel, RowExclusiveLock);
 }
 
 
@@ -437,12 +523,16 @@ AlterForeignDataWrapper(AlterFdwStmt *stmt)
 {
 	Relation	rel;
 	HeapTuple	tp;
+	Form_pg_foreign_data_wrapper fdwForm;
 	Datum		repl_val[Natts_pg_foreign_data_wrapper];
 	bool		repl_null[Natts_pg_foreign_data_wrapper];
 	bool		repl_repl[Natts_pg_foreign_data_wrapper];
 	Oid			fdwId;
 	bool		isnull;
 	Datum		datum;
+	bool		handler_given;
+	bool		validator_given;
+	Oid			fdwhandler;
 	Oid			fdwvalidator;
 
 	/* Must be super user */
@@ -461,15 +551,32 @@ AlterForeignDataWrapper(AlterFdwStmt *stmt)
 				(errcode(ERRCODE_UNDEFINED_OBJECT),
 		errmsg("foreign-data wrapper \"%s\" does not exist", stmt->fdwname)));
 
+	fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
 	fdwId = HeapTupleGetOid(tp);
 
 	memset(repl_val, 0, sizeof(repl_val));
 	memset(repl_null, false, sizeof(repl_null));
 	memset(repl_repl, false, sizeof(repl_repl));
 
-	if (stmt->change_validator)
+	parse_func_options(stmt->func_options,
+					   &handler_given, &fdwhandler,
+					   &validator_given, &fdwvalidator);
+
+	if (handler_given)
+	{
+		repl_val[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
+		repl_repl[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = true;
+
+		/*
+		 * It could be that the behavior of accessing foreign table changes
+		 * with the new handler.  Warn about this.
+		 */
+		ereport(WARNING,
+				(errmsg("changing the foreign-data wrapper handler can change behavior of existing foreign tables")));
+	}
+
+	if (validator_given)
 	{
-		fdwvalidator = stmt->validator ? lookup_fdw_validator_func(stmt->validator) : InvalidOid;
 		repl_val[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
 		repl_repl[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = true;
 
@@ -477,26 +584,21 @@ AlterForeignDataWrapper(AlterFdwStmt *stmt)
 		 * It could be that the options for the FDW, SERVER and USER MAPPING
 		 * are no longer valid with the new validator.	Warn about this.
 		 */
-		if (stmt->validator)
+		if (OidIsValid(fdwvalidator))
 			ereport(WARNING,
-			 (errmsg("changing the foreign-data wrapper validator can cause "
-					 "the options for dependent objects to become invalid")));
+					(errmsg("changing the foreign-data wrapper validator can cause "
+							"the options for dependent objects to become invalid")));
 	}
 	else
 	{
 		/*
 		 * Validator is not changed, but we need it for validating options.
 		 */
-		datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
-								tp,
-								Anum_pg_foreign_data_wrapper_fdwvalidator,
-								&isnull);
-		Assert(!isnull);
-		fdwvalidator = DatumGetObjectId(datum);
+		fdwvalidator = fdwForm->fdwvalidator;
 	}
 
 	/*
-	 * Options specified, validate and update.
+	 * If options specified, validate and update.
 	 */
 	if (stmt->options)
 	{
@@ -532,8 +634,46 @@ AlterForeignDataWrapper(AlterFdwStmt *stmt)
 	simple_heap_update(rel, &tp->t_self, tp);
 	CatalogUpdateIndexes(rel, tp);
 
-	heap_close(rel, RowExclusiveLock);
 	heap_freetuple(tp);
+
+	/* Update function dependencies if we changed them */
+	if (handler_given || validator_given)
+	{
+		ObjectAddress myself;
+		ObjectAddress referenced;
+
+		/*
+		 * Flush all existing dependency records of this FDW on functions;
+		 * we assume there can be none other than the ones we are fixing.
+		 */
+		deleteDependencyRecordsForClass(ForeignDataWrapperRelationId,
+										fdwId,
+										ProcedureRelationId,
+										DEPENDENCY_NORMAL);
+
+		/* And build new ones. */
+		myself.classId = ForeignDataWrapperRelationId;
+		myself.objectId = fdwId;
+		myself.objectSubId = 0;
+
+		if (OidIsValid(fdwhandler))
+		{
+			referenced.classId = ProcedureRelationId;
+			referenced.objectId = fdwhandler;
+			referenced.objectSubId = 0;
+			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+		}
+
+		if (OidIsValid(fdwvalidator))
+		{
+			referenced.classId = ProcedureRelationId;
+			referenced.objectId = fdwvalidator;
+			referenced.objectSubId = 0;
+			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+		}
+	}
+
+	heap_close(rel, RowExclusiveLock);
 }
 
 
@@ -712,7 +852,7 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
 	/* Post creation hook for new foreign server */
 	InvokeObjectAccessHook(OAT_POST_CREATE, ForeignServerRelationId, srvId, 0);
 
-	heap_close(rel, NoLock);
+	heap_close(rel, RowExclusiveLock);
 }
 
 
@@ -804,8 +944,9 @@ AlterForeignServer(AlterForeignServerStmt *stmt)
 	simple_heap_update(rel, &tp->t_self, tp);
 	CatalogUpdateIndexes(rel, tp);
 
-	heap_close(rel, RowExclusiveLock);
 	heap_freetuple(tp);
+
+	heap_close(rel, RowExclusiveLock);
 }
 
 
@@ -991,7 +1132,7 @@ CreateUserMapping(CreateUserMappingStmt *stmt)
 	/* Post creation hook for new user mapping */
 	InvokeObjectAccessHook(OAT_POST_CREATE, UserMappingRelationId, umId, 0);
 
-	heap_close(rel, NoLock);
+	heap_close(rel, RowExclusiveLock);
 }
 
 
@@ -1076,8 +1217,9 @@ AlterUserMapping(AlterUserMappingStmt *stmt)
 	simple_heap_update(rel, &tp->t_self, tp);
 	CatalogUpdateIndexes(rel, tp);
 
-	heap_close(rel, RowExclusiveLock);
 	heap_freetuple(tp);
+
+	heap_close(rel, RowExclusiveLock);
 }
 
 
@@ -1184,7 +1326,6 @@ CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
 	Datum		values[Natts_pg_foreign_table];
 	bool		nulls[Natts_pg_foreign_table];
 	HeapTuple	tuple;
-	Oid			ftId;
 	AclResult	aclresult;
 	ObjectAddress myself;
 	ObjectAddress referenced;
@@ -1237,9 +1378,7 @@ CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
 
 	tuple = heap_form_tuple(ftrel->rd_att, values, nulls);
 
-	/* pg_foreign_table don't have OID */
-	ftId = simple_heap_insert(ftrel, tuple);
-
+	simple_heap_insert(ftrel, tuple);
 	CatalogUpdateIndexes(ftrel, tuple);
 
 	heap_freetuple(tuple);
@@ -1254,5 +1393,5 @@ CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
 	referenced.objectSubId = 0;
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
-	heap_close(ftrel, NoLock);
+	heap_close(ftrel, RowExclusiveLock);
 }
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index 9a0f847f93ccfe3b6b43f4bdac0238763051fe50..6e391025ffa6f522f3ae645c20d1602557464a6b 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -58,6 +58,7 @@ GetForeignDataWrapper(Oid fdwid)
 	fdw->fdwid = fdwid;
 	fdw->owner = fdwform->fdwowner;
 	fdw->fdwname = pstrdup(NameStr(fdwform->fdwname));
+	fdw->fdwhandler = fdwform->fdwhandler;
 	fdw->fdwvalidator = fdwform->fdwvalidator;
 
 	/* Extract the options */
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 57d580208102c116323ffd9ff74dcba21fcd3ecc..9a8a97c4bec1eef854df3815ca7ba6260fa4683c 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3282,7 +3282,7 @@ _copyCreateFdwStmt(CreateFdwStmt *from)
 	CreateFdwStmt *newnode = makeNode(CreateFdwStmt);
 
 	COPY_STRING_FIELD(fdwname);
-	COPY_NODE_FIELD(validator);
+	COPY_NODE_FIELD(func_options);
 	COPY_NODE_FIELD(options);
 
 	return newnode;
@@ -3294,8 +3294,7 @@ _copyAlterFdwStmt(AlterFdwStmt *from)
 	AlterFdwStmt *newnode = makeNode(AlterFdwStmt);
 
 	COPY_STRING_FIELD(fdwname);
-	COPY_NODE_FIELD(validator);
-	COPY_SCALAR_FIELD(change_validator);
+	COPY_NODE_FIELD(func_options);
 	COPY_NODE_FIELD(options);
 
 	return newnode;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index f57cf99ba2c1f473cbef55c7c46f78765ceb1ae3..dd332f19f00d91baec544229a6b98772028a9f5b 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1679,7 +1679,7 @@ static bool
 _equalCreateFdwStmt(CreateFdwStmt *a, CreateFdwStmt *b)
 {
 	COMPARE_STRING_FIELD(fdwname);
-	COMPARE_NODE_FIELD(validator);
+	COMPARE_NODE_FIELD(func_options);
 	COMPARE_NODE_FIELD(options);
 
 	return true;
@@ -1689,8 +1689,7 @@ static bool
 _equalAlterFdwStmt(AlterFdwStmt *a, AlterFdwStmt *b)
 {
 	COMPARE_STRING_FIELD(fdwname);
-	COMPARE_NODE_FIELD(validator);
-	COMPARE_SCALAR_FIELD(change_validator);
+	COMPARE_NODE_FIELD(func_options);
 	COMPARE_NODE_FIELD(options);
 
 	return true;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 077a10532eb7fb86a466e252bea3df403a03ff75..c6811a11bd141b44eb84716fa0e2108d9bc30b3e 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -309,6 +309,9 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
 				create_generic_options alter_generic_options
 				relation_expr_list dostmt_opt_list
 
+%type <list>	opt_fdw_options fdw_options
+%type <defelt>	fdw_option
+
 %type <range>	OptTempTableName
 %type <into>	into_clause create_as_target
 
@@ -3505,20 +3508,37 @@ AlterExtensionContentsStmt:
 /*****************************************************************************
  *
  * 		QUERY:
- *             CREATE FOREIGN DATA WRAPPER name [ VALIDATOR name ]
+ *             CREATE FOREIGN DATA WRAPPER name options
  *
  *****************************************************************************/
 
-CreateFdwStmt: CREATE FOREIGN DATA_P WRAPPER name opt_validator create_generic_options
+CreateFdwStmt: CREATE FOREIGN DATA_P WRAPPER name opt_fdw_options create_generic_options
 				{
 					CreateFdwStmt *n = makeNode(CreateFdwStmt);
 					n->fdwname = $5;
-					n->validator = $6;
+					n->func_options = $6;
 					n->options = $7;
 					$$ = (Node *) n;
 				}
 		;
 
+fdw_option:
+			HANDLER handler_name				{ $$ = makeDefElem("handler", (Node *)$2); }
+			| NO HANDLER						{ $$ = makeDefElem("handler", NULL); }
+			| VALIDATOR handler_name			{ $$ = makeDefElem("validator", (Node *)$2); }
+			| NO VALIDATOR						{ $$ = makeDefElem("validator", NULL); }
+		;
+
+fdw_options:
+			fdw_option							{ $$ = list_make1($1); }
+			| fdw_options fdw_option			{ $$ = lappend($1, $2); }
+		;
+
+opt_fdw_options:
+			fdw_options							{ $$ = $1; }
+			| /*EMPTY*/							{ $$ = NIL; }
+		;
+
 /*****************************************************************************
  *
  * 		QUERY :
@@ -3547,32 +3567,24 @@ DropFdwStmt: DROP FOREIGN DATA_P WRAPPER name opt_drop_behavior
 /*****************************************************************************
  *
  * 		QUERY :
- *				ALTER FOREIGN DATA WRAPPER name
+ *				ALTER FOREIGN DATA WRAPPER name options
  *
  ****************************************************************************/
 
-AlterFdwStmt: ALTER FOREIGN DATA_P WRAPPER name validator_clause alter_generic_options
+AlterFdwStmt: ALTER FOREIGN DATA_P WRAPPER name opt_fdw_options alter_generic_options
 				{
 					AlterFdwStmt *n = makeNode(AlterFdwStmt);
 					n->fdwname = $5;
-					n->validator = $6;
-					n->change_validator = true;
+					n->func_options = $6;
 					n->options = $7;
 					$$ = (Node *) n;
 				}
-			| ALTER FOREIGN DATA_P WRAPPER name validator_clause
+			| ALTER FOREIGN DATA_P WRAPPER name fdw_options
 				{
 					AlterFdwStmt *n = makeNode(AlterFdwStmt);
 					n->fdwname = $5;
-					n->validator = $6;
-					n->change_validator = true;
-					$$ = (Node *) n;
-				}
-			| ALTER FOREIGN DATA_P WRAPPER name alter_generic_options
-				{
-					AlterFdwStmt *n = makeNode(AlterFdwStmt);
-					n->fdwname = $5;
-					n->options = $6;
+					n->func_options = $6;
+					n->options = NIL;
 					$$ = (Node *) n;
 				}
 		;
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index 2be0696f6093890fffdb4cd49d9b6bc487c1bfab..d9329f834287b7472688c80417a37934a4668abb 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -267,6 +267,33 @@ language_handler_out(PG_FUNCTION_ARGS)
 }
 
 
+/*
+ * fdw_handler_in		- input routine for pseudo-type FDW_HANDLER.
+ */
+Datum
+fdw_handler_in(PG_FUNCTION_ARGS)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("cannot accept a value of type fdw_handler")));
+
+	PG_RETURN_VOID();			/* keep compiler quiet */
+}
+
+/*
+ * fdw_handler_out		- output routine for pseudo-type FDW_HANDLER.
+ */
+Datum
+fdw_handler_out(PG_FUNCTION_ARGS)
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("cannot display a value of type fdw_handler")));
+
+	PG_RETURN_VOID();			/* keep compiler quiet */
+}
+
+
 /*
  * internal_in		- input routine for pseudo-type INTERNAL.
  */
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 60609b6b3593a038afc09366ad05ef8e4c0bd3f5..a9fa3357e795db484dbf2e6124dc97ea7e5593d2 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -6260,6 +6260,7 @@ getForeignDataWrappers(int *numForeignDataWrappers)
 	int			i_oid;
 	int			i_fdwname;
 	int			i_rolname;
+	int			i_fdwhandler;
 	int			i_fdwvalidator;
 	int			i_fdwacl;
 	int			i_fdwoptions;
@@ -6274,13 +6275,30 @@ getForeignDataWrappers(int *numForeignDataWrappers)
 	/* Make sure we are in proper schema */
 	selectSourceSchema("pg_catalog");
 
-	appendPQExpBuffer(query, "SELECT tableoid, oid, fdwname, "
-		"(%s fdwowner) AS rolname, fdwvalidator::pg_catalog.regproc, fdwacl,"
-					  "array_to_string(ARRAY("
-		 "		SELECT option_name || ' ' || quote_literal(option_value) "
-	   "		FROM pg_options_to_table(fdwoptions)), ', ') AS fdwoptions "
-					  "FROM pg_foreign_data_wrapper",
-					  username_subquery);
+	if (g_fout->remoteVersion >= 90100)
+	{
+		appendPQExpBuffer(query, "SELECT tableoid, oid, fdwname, "
+						  "(%s fdwowner) AS rolname, "
+						  "fdwhandler::pg_catalog.regproc, "
+						  "fdwvalidator::pg_catalog.regproc, fdwacl, "
+						  "array_to_string(ARRAY("
+						  "		SELECT option_name || ' ' || quote_literal(option_value) "
+						  "		FROM pg_options_to_table(fdwoptions)), ', ') AS fdwoptions "
+						  "FROM pg_foreign_data_wrapper",
+						  username_subquery);
+	}
+	else
+	{
+		appendPQExpBuffer(query, "SELECT tableoid, oid, fdwname, "
+						  "(%s fdwowner) AS rolname, "
+						  "'-' AS fdwhandler, "
+						  "fdwvalidator::pg_catalog.regproc, fdwacl, "
+						  "array_to_string(ARRAY("
+						  "		SELECT option_name || ' ' || quote_literal(option_value) "
+						  "		FROM pg_options_to_table(fdwoptions)), ', ') AS fdwoptions "
+						  "FROM pg_foreign_data_wrapper",
+						  username_subquery);
+	}
 
 	res = PQexec(g_conn, query->data);
 	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
@@ -6294,6 +6312,7 @@ getForeignDataWrappers(int *numForeignDataWrappers)
 	i_oid = PQfnumber(res, "oid");
 	i_fdwname = PQfnumber(res, "fdwname");
 	i_rolname = PQfnumber(res, "rolname");
+	i_fdwhandler = PQfnumber(res, "fdwhandler");
 	i_fdwvalidator = PQfnumber(res, "fdwvalidator");
 	i_fdwacl = PQfnumber(res, "fdwacl");
 	i_fdwoptions = PQfnumber(res, "fdwoptions");
@@ -6307,11 +6326,11 @@ getForeignDataWrappers(int *numForeignDataWrappers)
 		fdwinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_fdwname));
 		fdwinfo[i].dobj.namespace = NULL;
 		fdwinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
+		fdwinfo[i].fdwhandler = strdup(PQgetvalue(res, i, i_fdwhandler));
 		fdwinfo[i].fdwvalidator = strdup(PQgetvalue(res, i, i_fdwvalidator));
 		fdwinfo[i].fdwoptions = strdup(PQgetvalue(res, i, i_fdwoptions));
 		fdwinfo[i].fdwacl = strdup(PQgetvalue(res, i, i_fdwacl));
 
-
 		/* Decide whether we want to dump it */
 		selectDumpableObject(&(fdwinfo[i].dobj));
 	}
@@ -10929,11 +10948,13 @@ dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo)
 	appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
 					  qfdwname);
 
-	if (fdwinfo->fdwvalidator && strcmp(fdwinfo->fdwvalidator, "-") != 0)
-		appendPQExpBuffer(q, " VALIDATOR %s",
-						  fdwinfo->fdwvalidator);
+	if (strcmp(fdwinfo->fdwhandler, "-") != 0)
+		appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
+
+	if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
+		appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
 
-	if (fdwinfo->fdwoptions && strlen(fdwinfo->fdwoptions) > 0)
+	if (strlen(fdwinfo->fdwoptions) > 0)
 		appendPQExpBuffer(q, " OPTIONS (%s)", fdwinfo->fdwoptions);
 
 	appendPQExpBuffer(q, ";\n");
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 3c02af44b1329b33a55ad1ad977a1785c1458029..94b7a6bf928a343d02478f48d09d754c9382db6f 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -440,6 +440,7 @@ typedef struct _fdwInfo
 {
 	DumpableObject dobj;
 	char	   *rolname;
+	char	   *fdwhandler;
 	char	   *fdwvalidator;
 	char	   *fdwoptions;
 	char	   *fdwacl;
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 735eef786b4d911b6565248d8197175ef79098b1..94396b191062bdc05fa272c19c250ebfe5cf23a0 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3562,10 +3562,15 @@ listForeignDataWrappers(const char *pattern, bool verbose)
 	initPQExpBuffer(&buf);
 	printfPQExpBuffer(&buf,
 					  "SELECT fdwname AS \"%s\",\n"
-					  "  pg_catalog.pg_get_userbyid(fdwowner) AS \"%s\",\n"
-					  "  fdwvalidator::pg_catalog.regproc AS \"%s\"",
+					  "  pg_catalog.pg_get_userbyid(fdwowner) AS \"%s\",\n",
 					  gettext_noop("Name"),
-					  gettext_noop("Owner"),
+					  gettext_noop("Owner"));
+	if (pset.sversion >= 90100)
+		appendPQExpBuffer(&buf,
+						  "  fdwhandler::pg_catalog.regproc AS \"%s\",\n",
+						  gettext_noop("Handler"));
+	appendPQExpBuffer(&buf,
+					  "  fdwvalidator::pg_catalog.regproc AS \"%s\"",
 					  gettext_noop("Validator"));
 
 	if (verbose)
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 1a7be47fab8609def269011a120b1a2a3e431540..989138169a922f76317362a99f8e33231eee6940 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	201102181
+#define CATALOG_VERSION_NO	201102191
 
 #endif
diff --git a/src/include/catalog/pg_foreign_data_wrapper.h b/src/include/catalog/pg_foreign_data_wrapper.h
index a83556df151e62dddf50064afb58707e43c1cb12..10afb370fd79f2b2d942a09ad197216c59cca721 100644
--- a/src/include/catalog/pg_foreign_data_wrapper.h
+++ b/src/include/catalog/pg_foreign_data_wrapper.h
@@ -32,7 +32,8 @@ CATALOG(pg_foreign_data_wrapper,2328)
 {
 	NameData	fdwname;		/* foreign-data wrapper name */
 	Oid			fdwowner;		/* FDW owner */
-	Oid			fdwvalidator;	/* optional validation function */
+	Oid			fdwhandler;		/* handler function, or 0 if none */
+	Oid			fdwvalidator;	/* option validation function, or 0 if none */
 
 	/* VARIABLE LENGTH FIELDS start here. */
 
@@ -52,11 +53,12 @@ typedef FormData_pg_foreign_data_wrapper *Form_pg_foreign_data_wrapper;
  * ----------------
  */
 
-#define Natts_pg_foreign_data_wrapper				5
+#define Natts_pg_foreign_data_wrapper				6
 #define Anum_pg_foreign_data_wrapper_fdwname		1
 #define Anum_pg_foreign_data_wrapper_fdwowner		2
-#define Anum_pg_foreign_data_wrapper_fdwvalidator	3
-#define Anum_pg_foreign_data_wrapper_fdwacl			4
-#define Anum_pg_foreign_data_wrapper_fdwoptions		5
+#define Anum_pg_foreign_data_wrapper_fdwhandler		3
+#define Anum_pg_foreign_data_wrapper_fdwvalidator	4
+#define Anum_pg_foreign_data_wrapper_fdwacl			5
+#define Anum_pg_foreign_data_wrapper_fdwoptions		6
 
 #endif   /* PG_FOREIGN_DATA_WRAPPER_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index b3458ed56eb5ae7e51a0cb5a56293bb4b25e240a..08949853f190d3190f6ed8afcd5e947581e926d6 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3913,6 +3913,10 @@ DATA(insert OID = 2777 (  anynonarray_in	PGNSP PGUID 12 1 0 0 f f f t f i 1 0 27
 DESCR("I/O");
 DATA(insert OID = 2778 (  anynonarray_out	PGNSP PGUID 12 1 0 0 f f f t f i 1 0 2275 "2776" _null_ _null_ _null_ _null_ anynonarray_out _null_ _null_ _null_ ));
 DESCR("I/O");
+DATA(insert OID = 3116 (  fdw_handler_in	PGNSP PGUID 12 1 0 0 f f f f f i 1 0 3115 "2275" _null_ _null_ _null_ _null_ fdw_handler_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 3117 (  fdw_handler_out	PGNSP PGUID 12 1 0 0 f f f t f i 1 0 2275 "3115" _null_ _null_ _null_ _null_ fdw_handler_out _null_ _null_ _null_ ));
+DESCR("I/O");
 
 /* cryptographic */
 DATA(insert OID =  2311 (  md5	   PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "25" _null_ _null_ _null_ _null_ md5_text _null_ _null_ _null_ ));
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index d82078ec0bad915974b077e8feba4515698d7121..0f7312e495d4c74accecf4825060638ea159000b 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -631,6 +631,8 @@ DATA(insert OID = 2776 ( anynonarray	PGNSP PGUID  4 t p P f t \054 0 0 0 anynona
 #define ANYNONARRAYOID	2776
 DATA(insert OID = 3500 ( anyenum		PGNSP PGUID  4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 0 _null_ _null_ ));
 #define ANYENUMOID		3500
+DATA(insert OID = 3115 ( fdw_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ ));
+#define FDW_HANDLEROID		3115
 
 
 /*
diff --git a/src/include/foreign/foreign.h b/src/include/foreign/foreign.h
index 2d1495cfe133e079d1ee03500c7c335c367bbd8d..2cf0eaa09cc8c6515002dbe732e9a26df0878051 100644
--- a/src/include/foreign/foreign.h
+++ b/src/include/foreign/foreign.h
@@ -37,7 +37,8 @@ typedef struct ForeignDataWrapper
 	Oid			fdwid;			/* FDW Oid */
 	Oid			owner;			/* FDW owner user Oid */
 	char	   *fdwname;		/* Name of the FDW */
-	Oid			fdwvalidator;
+	Oid			fdwhandler;		/* Oid of handler function, or 0 */
+	Oid			fdwvalidator;	/* Oid of validator function, or 0 */
 	List	   *options;		/* fdwoptions as DefElem list */
 } ForeignDataWrapper;
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 8aaa8c1d2f7f322c20c67b457cdb5eea87c7417d..63a61e3da248b2a1677d35eb56ff2b82b9c53b64 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1574,7 +1574,7 @@ typedef struct CreateFdwStmt
 {
 	NodeTag		type;
 	char	   *fdwname;		/* foreign-data wrapper name */
-	List	   *validator;		/* optional validator function (qual. name) */
+	List	   *func_options;	/* HANDLER/VALIDATOR options */
 	List	   *options;		/* generic options to FDW */
 } CreateFdwStmt;
 
@@ -1582,8 +1582,7 @@ typedef struct AlterFdwStmt
 {
 	NodeTag		type;
 	char	   *fdwname;		/* foreign-data wrapper name */
-	List	   *validator;		/* optional validator function (qual. name) */
-	bool		change_validator;
+	List	   *func_options;	/* HANDLER/VALIDATOR options */
 	List	   *options;		/* generic options to FDW */
 } AlterFdwStmt;
 
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 277aec414c38100546ca1a488213fef66568f76f..8392be6208a89666ecd55121e401fcd496389c90 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -508,6 +508,8 @@ extern Datum trigger_in(PG_FUNCTION_ARGS);
 extern Datum trigger_out(PG_FUNCTION_ARGS);
 extern Datum language_handler_in(PG_FUNCTION_ARGS);
 extern Datum language_handler_out(PG_FUNCTION_ARGS);
+extern Datum fdw_handler_in(PG_FUNCTION_ARGS);
+extern Datum fdw_handler_out(PG_FUNCTION_ARGS);
 extern Datum internal_in(PG_FUNCTION_ARGS);
 extern Datum internal_out(PG_FUNCTION_ARGS);
 extern Datum opaque_in(PG_FUNCTION_ARGS);
diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out
index d6c650be14d0488ffc0260e73e45a8a45e513425..780098c2373548bdcb049b2e40c7fdf837e4aca1 100644
--- a/src/test/regress/expected/foreign_data.out
+++ b/src/test/regress/expected/foreign_data.out
@@ -16,11 +16,11 @@ CREATE ROLE unprivileged_role;
 CREATE FOREIGN DATA WRAPPER dummy;
 CREATE FOREIGN DATA WRAPPER postgresql VALIDATOR postgresql_fdw_validator;
 -- At this point we should have 2 built-in wrappers and no servers.
-SELECT fdwname, fdwvalidator::regproc, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3;
-  fdwname   |       fdwvalidator       | fdwoptions 
-------------+--------------------------+------------
- dummy      | -                        | 
- postgresql | postgresql_fdw_validator | 
+SELECT fdwname, fdwhandler::regproc, fdwvalidator::regproc, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3;
+  fdwname   | fdwhandler |       fdwvalidator       | fdwoptions 
+------------+------------+--------------------------+------------
+ dummy      | -          | -                        | 
+ postgresql | -          | postgresql_fdw_validator | 
 (2 rows)
 
 SELECT srvname, srvoptions FROM pg_foreign_server;
@@ -38,12 +38,12 @@ CREATE FOREIGN DATA WRAPPER foo VALIDATOR bar;            -- ERROR
 ERROR:  function bar(text[], oid) does not exist
 CREATE FOREIGN DATA WRAPPER foo;
 \dew
-               List of foreign-data wrappers
-    Name    |       Owner       |        Validator         
-------------+-------------------+--------------------------
- dummy      | foreign_data_user | -
- foo        | foreign_data_user | -
- postgresql | foreign_data_user | postgresql_fdw_validator
+                    List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         
+------------+-------------------+---------+--------------------------
+ dummy      | foreign_data_user | -       | -
+ foo        | foreign_data_user | -       | -
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator
 (3 rows)
 
 CREATE FOREIGN DATA WRAPPER foo; -- duplicate
@@ -51,12 +51,12 @@ ERROR:  foreign-data wrapper "foo" already exists
 DROP FOREIGN DATA WRAPPER foo;
 CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1');
 \dew+
-                                List of foreign-data wrappers
-    Name    |       Owner       |        Validator         | Access privileges |   Options   
-------------+-------------------+--------------------------+-------------------+-------------
- dummy      | foreign_data_user | -                        |                   | 
- foo        | foreign_data_user | -                        |                   | {testing=1}
- postgresql | foreign_data_user | postgresql_fdw_validator |                   | 
+                                     List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges |   Options   
+------------+-------------------+---------+--------------------------+-------------------+-------------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ foo        | foreign_data_user | -       | -                        |                   | {testing=1}
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
 (3 rows)
 
 DROP FOREIGN DATA WRAPPER foo;
@@ -64,12 +64,12 @@ CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1', testing '2');   -- ERROR
 ERROR:  option "testing" provided more than once
 CREATE FOREIGN DATA WRAPPER foo OPTIONS (testing '1', another '2');
 \dew+
-                                     List of foreign-data wrappers
-    Name    |       Owner       |        Validator         | Access privileges |        Options        
-------------+-------------------+--------------------------+-------------------+-----------------------
- dummy      | foreign_data_user | -                        |                   | 
- foo        | foreign_data_user | -                        |                   | {testing=1,another=2}
- postgresql | foreign_data_user | postgresql_fdw_validator |                   | 
+                                          List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges |        Options        
+------------+-------------------+---------+--------------------------+-------------------+-----------------------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ foo        | foreign_data_user | -       | -                        |                   | {testing=1,another=2}
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
 (3 rows)
 
 DROP FOREIGN DATA WRAPPER foo;
@@ -80,12 +80,12 @@ HINT:  Must be superuser to create a foreign-data wrapper.
 RESET ROLE;
 CREATE FOREIGN DATA WRAPPER foo VALIDATOR postgresql_fdw_validator;
 \dew+
-                              List of foreign-data wrappers
-    Name    |       Owner       |        Validator         | Access privileges | Options 
-------------+-------------------+--------------------------+-------------------+---------
- dummy      | foreign_data_user | -                        |                   | 
- foo        | foreign_data_user | postgresql_fdw_validator |                   | 
- postgresql | foreign_data_user | postgresql_fdw_validator |                   | 
+                                   List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
+------------+-------------------+---------+--------------------------+-------------------+---------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ foo        | foreign_data_user | -       | postgresql_fdw_validator |                   | 
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
 (3 rows)
 
 -- ALTER FOREIGN DATA WRAPPER
@@ -97,12 +97,12 @@ ALTER FOREIGN DATA WRAPPER foo VALIDATOR bar;               -- ERROR
 ERROR:  function bar(text[], oid) does not exist
 ALTER FOREIGN DATA WRAPPER foo NO VALIDATOR;
 \dew+
-                              List of foreign-data wrappers
-    Name    |       Owner       |        Validator         | Access privileges | Options 
-------------+-------------------+--------------------------+-------------------+---------
- dummy      | foreign_data_user | -                        |                   | 
- foo        | foreign_data_user | -                        |                   | 
- postgresql | foreign_data_user | postgresql_fdw_validator |                   | 
+                                   List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
+------------+-------------------+---------+--------------------------+-------------------+---------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ foo        | foreign_data_user | -       | -                        |                   | 
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
 (3 rows)
 
 ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '1', b '2');
@@ -112,34 +112,34 @@ ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP c);            -- ERROR
 ERROR:  option "c" not found
 ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD x '1', DROP x);
 \dew+
-                               List of foreign-data wrappers
-    Name    |       Owner       |        Validator         | Access privileges |  Options  
-------------+-------------------+--------------------------+-------------------+-----------
- dummy      | foreign_data_user | -                        |                   | 
- foo        | foreign_data_user | -                        |                   | {a=1,b=2}
- postgresql | foreign_data_user | postgresql_fdw_validator |                   | 
+                                    List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges |  Options  
+------------+-------------------+---------+--------------------------+-------------------+-----------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ foo        | foreign_data_user | -       | -                        |                   | {a=1,b=2}
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
 (3 rows)
 
 ALTER FOREIGN DATA WRAPPER foo OPTIONS (DROP a, SET b '3', ADD c '4');
 \dew+
-                               List of foreign-data wrappers
-    Name    |       Owner       |        Validator         | Access privileges |  Options  
-------------+-------------------+--------------------------+-------------------+-----------
- dummy      | foreign_data_user | -                        |                   | 
- foo        | foreign_data_user | -                        |                   | {b=3,c=4}
- postgresql | foreign_data_user | postgresql_fdw_validator |                   | 
+                                    List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges |  Options  
+------------+-------------------+---------+--------------------------+-------------------+-----------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ foo        | foreign_data_user | -       | -                        |                   | {b=3,c=4}
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
 (3 rows)
 
 ALTER FOREIGN DATA WRAPPER foo OPTIONS (a '2');
 ALTER FOREIGN DATA WRAPPER foo OPTIONS (b '4');             -- ERROR
 ERROR:  option "b" provided more than once
 \dew+
-                                 List of foreign-data wrappers
-    Name    |       Owner       |        Validator         | Access privileges |    Options    
-------------+-------------------+--------------------------+-------------------+---------------
- dummy      | foreign_data_user | -                        |                   | 
- foo        | foreign_data_user | -                        |                   | {b=3,c=4,a=2}
- postgresql | foreign_data_user | postgresql_fdw_validator |                   | 
+                                      List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges |    Options    
+------------+-------------------+---------+--------------------------+-------------------+---------------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ foo        | foreign_data_user | -       | -                        |                   | {b=3,c=4,a=2}
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
 (3 rows)
 
 SET ROLE regress_test_role;
@@ -149,12 +149,12 @@ HINT:  Must be superuser to alter a foreign-data wrapper.
 SET ROLE regress_test_role_super;
 ALTER FOREIGN DATA WRAPPER foo OPTIONS (ADD d '5');
 \dew+
-                                   List of foreign-data wrappers
-    Name    |       Owner       |        Validator         | Access privileges |      Options      
-------------+-------------------+--------------------------+-------------------+-------------------
- dummy      | foreign_data_user | -                        |                   | 
- foo        | foreign_data_user | -                        |                   | {b=3,c=4,a=2,d=5}
- postgresql | foreign_data_user | postgresql_fdw_validator |                   | 
+                                        List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges |      Options      
+------------+-------------------+---------+--------------------------+-------------------+-------------------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ foo        | foreign_data_user | -       | -                        |                   | {b=3,c=4,a=2,d=5}
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
 (3 rows)
 
 ALTER FOREIGN DATA WRAPPER foo OWNER TO regress_test_role;  -- ERROR
@@ -168,12 +168,12 @@ ERROR:  permission denied to alter foreign-data wrapper "foo"
 HINT:  Must be superuser to alter a foreign-data wrapper.
 RESET ROLE;
 \dew+
-                                      List of foreign-data wrappers
-    Name    |          Owner          |        Validator         | Access privileges |      Options      
-------------+-------------------------+--------------------------+-------------------+-------------------
- dummy      | foreign_data_user       | -                        |                   | 
- foo        | regress_test_role_super | -                        |                   | {b=3,c=4,a=2,d=5}
- postgresql | foreign_data_user       | postgresql_fdw_validator |                   | 
+                                           List of foreign-data wrappers
+    Name    |          Owner          | Handler |        Validator         | Access privileges |      Options      
+------------+-------------------------+---------+--------------------------+-------------------+-------------------
+ dummy      | foreign_data_user       | -       | -                        |                   | 
+ foo        | regress_test_role_super | -       | -                        |                   | {b=3,c=4,a=2,d=5}
+ postgresql | foreign_data_user       | -       | postgresql_fdw_validator |                   | 
 (3 rows)
 
 -- DROP FOREIGN DATA WRAPPER
@@ -182,12 +182,12 @@ ERROR:  foreign-data wrapper "nonexistent" does not exist
 DROP FOREIGN DATA WRAPPER IF EXISTS nonexistent;
 NOTICE:  foreign-data wrapper "nonexistent" does not exist, skipping
 \dew+
-                                      List of foreign-data wrappers
-    Name    |          Owner          |        Validator         | Access privileges |      Options      
-------------+-------------------------+--------------------------+-------------------+-------------------
- dummy      | foreign_data_user       | -                        |                   | 
- foo        | regress_test_role_super | -                        |                   | {b=3,c=4,a=2,d=5}
- postgresql | foreign_data_user       | postgresql_fdw_validator |                   | 
+                                           List of foreign-data wrappers
+    Name    |          Owner          | Handler |        Validator         | Access privileges |      Options      
+------------+-------------------------+---------+--------------------------+-------------------+-------------------
+ dummy      | foreign_data_user       | -       | -                        |                   | 
+ foo        | regress_test_role_super | -       | -                        |                   | {b=3,c=4,a=2,d=5}
+ postgresql | foreign_data_user       | -       | postgresql_fdw_validator |                   | 
 (3 rows)
 
 DROP ROLE regress_test_role_super;                          -- ERROR
@@ -202,23 +202,23 @@ ALTER ROLE regress_test_role_super SUPERUSER;
 DROP FOREIGN DATA WRAPPER foo;
 DROP ROLE regress_test_role_super;
 \dew+
-                              List of foreign-data wrappers
-    Name    |       Owner       |        Validator         | Access privileges | Options 
-------------+-------------------+--------------------------+-------------------+---------
- dummy      | foreign_data_user | -                        |                   | 
- postgresql | foreign_data_user | postgresql_fdw_validator |                   | 
+                                   List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
+------------+-------------------+---------+--------------------------+-------------------+---------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
 (2 rows)
 
 CREATE FOREIGN DATA WRAPPER foo;
 CREATE SERVER s1 FOREIGN DATA WRAPPER foo;
 CREATE USER MAPPING FOR current_user SERVER s1;
 \dew+
-                              List of foreign-data wrappers
-    Name    |       Owner       |        Validator         | Access privileges | Options 
-------------+-------------------+--------------------------+-------------------+---------
- dummy      | foreign_data_user | -                        |                   | 
- foo        | foreign_data_user | -                        |                   | 
- postgresql | foreign_data_user | postgresql_fdw_validator |                   | 
+                                   List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
+------------+-------------------+---------+--------------------------+-------------------+---------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ foo        | foreign_data_user | -       | -                        |                   | 
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
 (3 rows)
 
 \des+
@@ -250,11 +250,11 @@ NOTICE:  drop cascades to 2 other objects
 DETAIL:  drop cascades to server s1
 drop cascades to user mapping for foreign_data_user
 \dew+
-                              List of foreign-data wrappers
-    Name    |       Owner       |        Validator         | Access privileges | Options 
-------------+-------------------+--------------------------+-------------------+---------
- dummy      | foreign_data_user | -                        |                   | 
- postgresql | foreign_data_user | postgresql_fdw_validator |                   | 
+                                   List of foreign-data wrappers
+    Name    |       Owner       | Handler |        Validator         | Access privileges | Options 
+------------+-------------------+---------+--------------------------+-------------------+---------
+ dummy      | foreign_data_user | -       | -                        |                   | 
+ postgresql | foreign_data_user | -       | postgresql_fdw_validator |                   | 
 (2 rows)
 
 \des+
@@ -669,6 +669,10 @@ Has OIDs: no
 
 CREATE INDEX id_ft1_c2 ON ft1 (c2);                             -- ERROR
 ERROR:  "ft1" is not a table
+SELECT * FROM ft1;                                              -- ERROR
+ERROR:  foreign table scans are not yet supported
+EXPLAIN SELECT * FROM ft1;                                      -- ERROR
+ERROR:  foreign table scans are not yet supported
 -- ALTER FOREIGN TABLE
 COMMENT ON FOREIGN TABLE ft1 IS 'foreign table';
 COMMENT ON FOREIGN TABLE ft1 IS NULL;
@@ -1105,9 +1109,9 @@ NOTICE:  drop cascades to server sc
 \c
 DROP ROLE foreign_data_user;
 -- At this point we should have no wrappers, no servers, and no mappings.
-SELECT fdwname, fdwvalidator, fdwoptions FROM pg_foreign_data_wrapper;
- fdwname | fdwvalidator | fdwoptions 
----------+--------------+------------
+SELECT fdwname, fdwhandler, fdwvalidator, fdwoptions FROM pg_foreign_data_wrapper;
+ fdwname | fdwhandler | fdwvalidator | fdwoptions 
+---------+------------+--------------+------------
 (0 rows)
 
 SELECT srvname, srvoptions FROM pg_foreign_server;
diff --git a/src/test/regress/sql/foreign_data.sql b/src/test/regress/sql/foreign_data.sql
index 86b698a1b616420946138a08d84b4c81a8642a7e..3f3978590319583ca9fe173160c287d3655fdc87 100644
--- a/src/test/regress/sql/foreign_data.sql
+++ b/src/test/regress/sql/foreign_data.sql
@@ -24,7 +24,7 @@ CREATE FOREIGN DATA WRAPPER dummy;
 CREATE FOREIGN DATA WRAPPER postgresql VALIDATOR postgresql_fdw_validator;
 
 -- At this point we should have 2 built-in wrappers and no servers.
-SELECT fdwname, fdwvalidator::regproc, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3;
+SELECT fdwname, fdwhandler::regproc, fdwvalidator::regproc, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3;
 SELECT srvname, srvoptions FROM pg_foreign_server;
 SELECT * FROM pg_user_mapping;
 
@@ -271,6 +271,8 @@ COMMENT ON COLUMN ft1.c1 IS 'ft1.c1';
 \d+ ft1
 \det+
 CREATE INDEX id_ft1_c2 ON ft1 (c2);                             -- ERROR
+SELECT * FROM ft1;                                              -- ERROR
+EXPLAIN SELECT * FROM ft1;                                      -- ERROR
 
 -- ALTER FOREIGN TABLE
 COMMENT ON FOREIGN TABLE ft1 IS 'foreign table';
@@ -453,6 +455,6 @@ DROP FOREIGN DATA WRAPPER dummy CASCADE;
 DROP ROLE foreign_data_user;
 
 -- At this point we should have no wrappers, no servers, and no mappings.
-SELECT fdwname, fdwvalidator, fdwoptions FROM pg_foreign_data_wrapper;
+SELECT fdwname, fdwhandler, fdwvalidator, fdwoptions FROM pg_foreign_data_wrapper;
 SELECT srvname, srvoptions FROM pg_foreign_server;
 SELECT * FROM pg_user_mapping;