diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 3a04358df3ec38b6d7b78918029bb2d2c6287212..04e6c8cfdda9584095527f2bfae95a773e88bb33 100644
--- a/contrib/postgres_fdw/deparse.c
+++ b/contrib/postgres_fdw/deparse.c
@@ -14,6 +14,15 @@
  * We assume that the remote session's search_path is exactly "pg_catalog",
  * and thus we need schema-qualify all and only names outside pg_catalog.
  *
+ * We do not consider that it is ever safe to send COLLATE expressions to
+ * the remote server: it might not have the same collation names we do.
+ * (Later we might consider it safe to send COLLATE "C", but even that would
+ * fail on old remote servers.)  An expression is considered safe to send only
+ * if all collations used in it are traceable to Var(s) of the foreign table.
+ * That implies that if the remote server gets a different answer than we do,
+ * the foreign table's columns are not marked with collations that match the
+ * remote table's columns, which we can consider to be user error.
+ *
  * Portions Copyright (c) 2012-2013, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
@@ -29,6 +38,7 @@
 #include "access/htup_details.h"
 #include "access/sysattr.h"
 #include "access/transam.h"
+#include "catalog/pg_collation.h"
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_operator.h"
 #include "catalog/pg_proc.h"
@@ -44,16 +54,33 @@
 
 
 /*
- * Context for foreign_expr_walker's search of an expression tree.
+ * Global context for foreign_expr_walker's search of an expression tree.
  */
-typedef struct foreign_expr_cxt
+typedef struct foreign_glob_cxt
 {
 	/* Input values */
 	PlannerInfo *root;
 	RelOptInfo *foreignrel;
 	/* Result values */
 	List	   *param_numbers;	/* Param IDs of PARAM_EXTERN Params */
-} foreign_expr_cxt;
+} foreign_glob_cxt;
+
+/*
+ * Local (per-tree-level) context for foreign_expr_walker's search.
+ * This is concerned with identifying collations used in the expression.
+ */
+typedef enum
+{
+	FDW_COLLATE_NONE,			/* expression is of a noncollatable type */
+	FDW_COLLATE_SAFE,			/* collation derives from a foreign Var */
+	FDW_COLLATE_UNSAFE			/* collation derives from something else */
+} FDWCollateState;
+
+typedef struct foreign_loc_cxt
+{
+	Oid			collation;		/* OID of current collation, if any */
+	FDWCollateState state;		/* state of current collation choice */
+} foreign_loc_cxt;
 
 /*
  * Functions to determine whether an expression can be evaluated safely on
@@ -61,7 +88,9 @@ typedef struct foreign_expr_cxt
  */
 static bool is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel,
 				Expr *expr, List **param_numbers);
-static bool foreign_expr_walker(Node *node, foreign_expr_cxt *context);
+static bool foreign_expr_walker(Node *node,
+					foreign_glob_cxt *glob_cxt,
+					foreign_loc_cxt *outer_cxt);
 static bool is_builtin(Oid procid);
 
 /*
@@ -166,7 +195,8 @@ is_foreign_expr(PlannerInfo *root,
 				Expr *expr,
 				List **param_numbers)
 {
-	foreign_expr_cxt context;
+	foreign_glob_cxt glob_cxt;
+	foreign_loc_cxt loc_cxt;
 
 	*param_numbers = NIL;		/* default result */
 
@@ -174,12 +204,18 @@ is_foreign_expr(PlannerInfo *root,
 	 * Check that the expression consists of nodes that are safe to execute
 	 * remotely.
 	 */
-	context.root = root;
-	context.foreignrel = baserel;
-	context.param_numbers = NIL;
-	if (foreign_expr_walker((Node *) expr, &context))
+	glob_cxt.root = root;
+	glob_cxt.foreignrel = baserel;
+	glob_cxt.param_numbers = NIL;
+	loc_cxt.collation = InvalidOid;
+	loc_cxt.state = FDW_COLLATE_NONE;
+	if (!foreign_expr_walker((Node *) expr, &glob_cxt, &loc_cxt))
 		return false;
 
+	/* Expressions examined here should be boolean, ie noncollatable */
+	Assert(loc_cxt.collation == InvalidOid);
+	Assert(loc_cxt.state == FDW_COLLATE_NONE);
+
 	/*
 	 * An expression which includes any mutable functions can't be sent over
 	 * because its result is not stable.  For example, sending now() remote
@@ -193,42 +229,80 @@ is_foreign_expr(PlannerInfo *root,
 	/*
 	 * OK, so return list of param IDs too.
 	 */
-	*param_numbers = context.param_numbers;
+	*param_numbers = glob_cxt.param_numbers;
 
 	return true;
 }
 
 /*
- * Return true if expression includes any node that is not safe to execute
- * remotely.  (We use this convention because expression_tree_walker is
- * designed to abort the tree walk as soon as a TRUE result is detected.)
+ * Check if expression is safe to execute remotely, and return true if so.
+ *
+ * In addition, glob_cxt->param_numbers and *outer_cxt are updated.
+ *
+ * We must check that the expression contains only node types we can deparse,
+ * that all types/functions/operators are safe to send (which we approximate
+ * as being built-in), and that all collations used in the expression derive
+ * from Vars of the foreign table.	Because of the latter, the logic is
+ * pretty close to assign_collations_walker() in parse_collate.c, though we
+ * can assume here that the given expression is valid.
  */
 static bool
-foreign_expr_walker(Node *node, foreign_expr_cxt *context)
+foreign_expr_walker(Node *node,
+					foreign_glob_cxt *glob_cxt,
+					foreign_loc_cxt *outer_cxt)
 {
 	bool		check_type = true;
+	foreign_loc_cxt inner_cxt;
+	Oid			collation;
+	FDWCollateState state;
 
+	/* Need do nothing for empty subexpressions */
 	if (node == NULL)
-		return false;
+		return true;
+
+	/* Set up inner_cxt for possible recursion to child nodes */
+	inner_cxt.collation = InvalidOid;
+	inner_cxt.state = FDW_COLLATE_NONE;
 
 	switch (nodeTag(node))
 	{
 		case T_Var:
 			{
+				Var		   *var = (Var *) node;
+
 				/*
 				 * Var can be used if it is in the foreign table (we shouldn't
 				 * really see anything else in baserestrict clauses, but let's
 				 * check anyway).
 				 */
-				Var		   *var = (Var *) node;
-
-				if (var->varno != context->foreignrel->relid ||
+				if (var->varno != glob_cxt->foreignrel->relid ||
 					var->varlevelsup != 0)
-					return true;
+					return false;
+
+				/*
+				 * If Var has a collation, consider that safe to use.
+				 */
+				collation = var->varcollid;
+				state = OidIsValid(collation) ? FDW_COLLATE_SAFE : FDW_COLLATE_NONE;
 			}
 			break;
 		case T_Const:
-			/* OK */
+			{
+				Const	   *c = (Const *) node;
+
+				/*
+				 * If the constant has nondefault collation, either it's of a
+				 * non-builtin type, or it reflects folding of a CollateExpr;
+				 * either way, it's unsafe to send to the remote.
+				 */
+				if (c->constcollid != InvalidOid &&
+					c->constcollid != DEFAULT_COLLATION_OID)
+					return false;
+
+				/* Otherwise, we can consider that it doesn't set collation */
+				collation = InvalidOid;
+				state = FDW_COLLATE_NONE;
+			}
 			break;
 		case T_Param:
 			{
@@ -240,15 +314,24 @@ foreign_expr_walker(Node *node, foreign_expr_cxt *context)
 				 * runs, we should only see PARAM_EXTERN Params anyway.)
 				 */
 				if (p->paramkind != PARAM_EXTERN)
-					return true;
+					return false;
+
+				/*
+				 * Collation handling is same as for Consts.
+				 */
+				if (p->paramcollid != InvalidOid &&
+					p->paramcollid != DEFAULT_COLLATION_OID)
+					return false;
+				collation = InvalidOid;
+				state = FDW_COLLATE_NONE;
 
 				/*
 				 * Report IDs of PARAM_EXTERN Params.  We don't bother to
 				 * eliminate duplicate list elements here; classifyConditions
 				 * will do that.
 				 */
-				context->param_numbers = lappend_int(context->param_numbers,
-													 p->paramid);
+				glob_cxt->param_numbers = lappend_int(glob_cxt->param_numbers,
+													  p->paramid);
 			}
 			break;
 		case T_ArrayRef:
@@ -257,60 +340,262 @@ foreign_expr_walker(Node *node, foreign_expr_cxt *context)
 
 				/* Assignment should not be in restrictions. */
 				if (ar->refassgnexpr != NULL)
-					return true;
+					return false;
+
+				/*
+				 * Recurse to remaining subexpressions.  Since the array
+				 * subscripts must yield (noncollatable) integers, they won't
+				 * affect the inner_cxt state.
+				 */
+				if (!foreign_expr_walker((Node *) ar->refupperindexpr,
+										 glob_cxt, &inner_cxt))
+					return false;
+				if (!foreign_expr_walker((Node *) ar->reflowerindexpr,
+										 glob_cxt, &inner_cxt))
+					return false;
+				if (!foreign_expr_walker((Node *) ar->refexpr,
+										 glob_cxt, &inner_cxt))
+					return false;
+
+				/*
+				 * Array subscripting should yield same collation as input,
+				 * but for safety use same logic as for function nodes.
+				 */
+				collation = ar->refcollid;
+				if (collation == InvalidOid)
+					state = FDW_COLLATE_NONE;
+				else if (inner_cxt.state == FDW_COLLATE_SAFE &&
+						 collation == inner_cxt.collation)
+					state = FDW_COLLATE_SAFE;
+				else
+					state = FDW_COLLATE_UNSAFE;
 			}
 			break;
 		case T_FuncExpr:
 			{
+				FuncExpr   *fe = (FuncExpr *) node;
+
 				/*
 				 * If function used by the expression is not built-in, it
 				 * can't be sent to remote because it might have incompatible
 				 * semantics on remote side.
 				 */
-				FuncExpr   *fe = (FuncExpr *) node;
-
 				if (!is_builtin(fe->funcid))
-					return true;
+					return false;
+
+				/*
+				 * Recurse to input subexpressions.
+				 */
+				if (!foreign_expr_walker((Node *) fe->args,
+										 glob_cxt, &inner_cxt))
+					return false;
+
+				/*
+				 * If function's input collation is not derived from a foreign
+				 * Var, it can't be sent to remote.
+				 */
+				if (fe->inputcollid == InvalidOid)
+					 /* OK, inputs are all noncollatable */ ;
+				else if (inner_cxt.state != FDW_COLLATE_SAFE ||
+						 fe->inputcollid != inner_cxt.collation)
+					return false;
+
+				/*
+				 * Detect whether node is introducing a collation not derived
+				 * from a foreign Var.	(If so, we just mark it unsafe for now
+				 * rather than immediately returning false, since the parent
+				 * node might not care.)
+				 */
+				collation = fe->funccollid;
+				if (collation == InvalidOid)
+					state = FDW_COLLATE_NONE;
+				else if (inner_cxt.state == FDW_COLLATE_SAFE &&
+						 collation == inner_cxt.collation)
+					state = FDW_COLLATE_SAFE;
+				else
+					state = FDW_COLLATE_UNSAFE;
 			}
 			break;
 		case T_OpExpr:
 		case T_DistinctExpr:	/* struct-equivalent to OpExpr */
 			{
+				OpExpr	   *oe = (OpExpr *) node;
+
 				/*
 				 * Similarly, only built-in operators can be sent to remote.
 				 * (If the operator is, surely its underlying function is
 				 * too.)
 				 */
-				OpExpr	   *oe = (OpExpr *) node;
-
 				if (!is_builtin(oe->opno))
-					return true;
+					return false;
+
+				/*
+				 * Recurse to input subexpressions.
+				 */
+				if (!foreign_expr_walker((Node *) oe->args,
+										 glob_cxt, &inner_cxt))
+					return false;
+
+				/*
+				 * If operator's input collation is not derived from a foreign
+				 * Var, it can't be sent to remote.
+				 */
+				if (oe->inputcollid == InvalidOid)
+					 /* OK, inputs are all noncollatable */ ;
+				else if (inner_cxt.state != FDW_COLLATE_SAFE ||
+						 oe->inputcollid != inner_cxt.collation)
+					return false;
+
+				/* Result-collation handling is same as for functions */
+				collation = oe->opcollid;
+				if (collation == InvalidOid)
+					state = FDW_COLLATE_NONE;
+				else if (inner_cxt.state == FDW_COLLATE_SAFE &&
+						 collation == inner_cxt.collation)
+					state = FDW_COLLATE_SAFE;
+				else
+					state = FDW_COLLATE_UNSAFE;
 			}
 			break;
 		case T_ScalarArrayOpExpr:
 			{
+				ScalarArrayOpExpr *oe = (ScalarArrayOpExpr *) node;
+
 				/*
 				 * Again, only built-in operators can be sent to remote.
 				 */
-				ScalarArrayOpExpr *oe = (ScalarArrayOpExpr *) node;
-
 				if (!is_builtin(oe->opno))
-					return true;
+					return false;
+
+				/*
+				 * Recurse to input subexpressions.
+				 */
+				if (!foreign_expr_walker((Node *) oe->args,
+										 glob_cxt, &inner_cxt))
+					return false;
+
+				/*
+				 * If operator's input collation is not derived from a foreign
+				 * Var, it can't be sent to remote.
+				 */
+				if (oe->inputcollid == InvalidOid)
+					 /* OK, inputs are all noncollatable */ ;
+				else if (inner_cxt.state != FDW_COLLATE_SAFE ||
+						 oe->inputcollid != inner_cxt.collation)
+					return false;
+
+				/* Output is always boolean and so noncollatable. */
+				collation = InvalidOid;
+				state = FDW_COLLATE_NONE;
 			}
 			break;
 		case T_RelabelType:
+			{
+				RelabelType *r = (RelabelType *) node;
+
+				/*
+				 * Recurse to input subexpression.
+				 */
+				if (!foreign_expr_walker((Node *) r->arg,
+										 glob_cxt, &inner_cxt))
+					return false;
+
+				/*
+				 * RelabelType must not introduce a collation not derived from
+				 * an input foreign Var.
+				 */
+				collation = r->resultcollid;
+				if (collation == InvalidOid)
+					state = FDW_COLLATE_NONE;
+				else if (inner_cxt.state == FDW_COLLATE_SAFE &&
+						 collation == inner_cxt.collation)
+					state = FDW_COLLATE_SAFE;
+				else
+					state = FDW_COLLATE_UNSAFE;
+			}
+			break;
 		case T_BoolExpr:
+			{
+				BoolExpr   *b = (BoolExpr *) node;
+
+				/*
+				 * Recurse to input subexpressions.
+				 */
+				if (!foreign_expr_walker((Node *) b->args,
+										 glob_cxt, &inner_cxt))
+					return false;
+
+				/* Output is always boolean and so noncollatable. */
+				collation = InvalidOid;
+				state = FDW_COLLATE_NONE;
+			}
+			break;
 		case T_NullTest:
+			{
+				NullTest   *nt = (NullTest *) node;
+
+				/*
+				 * Recurse to input subexpressions.
+				 */
+				if (!foreign_expr_walker((Node *) nt->arg,
+										 glob_cxt, &inner_cxt))
+					return false;
+
+				/* Output is always boolean and so noncollatable. */
+				collation = InvalidOid;
+				state = FDW_COLLATE_NONE;
+			}
+			break;
 		case T_ArrayExpr:
-			/* OK */
+			{
+				ArrayExpr  *a = (ArrayExpr *) node;
+
+				/*
+				 * Recurse to input subexpressions.
+				 */
+				if (!foreign_expr_walker((Node *) a->elements,
+										 glob_cxt, &inner_cxt))
+					return false;
+
+				/*
+				 * ArrayExpr must not introduce a collation not derived from
+				 * an input foreign Var.
+				 */
+				collation = a->array_collid;
+				if (collation == InvalidOid)
+					state = FDW_COLLATE_NONE;
+				else if (inner_cxt.state == FDW_COLLATE_SAFE &&
+						 collation == inner_cxt.collation)
+					state = FDW_COLLATE_SAFE;
+				else
+					state = FDW_COLLATE_UNSAFE;
+			}
 			break;
 		case T_List:
+			{
+				List	   *l = (List *) node;
+				ListCell   *lc;
 
-			/*
-			 * We need only fall through to let expression_tree_walker scan
-			 * the list elements --- but don't apply exprType() to the list.
-			 */
-			check_type = false;
+				/*
+				 * Recurse to component subexpressions.
+				 */
+				foreach(lc, l)
+				{
+					if (!foreign_expr_walker((Node *) lfirst(lc),
+											 glob_cxt, &inner_cxt))
+						return false;
+				}
+
+				/*
+				 * When processing a list, collation state just bubbles up
+				 * from the list elements.
+				 */
+				collation = inner_cxt.collation;
+				state = inner_cxt.state;
+
+				/* Don't apply exprType() to the list. */
+				check_type = false;
+			}
 			break;
 		default:
 
@@ -318,7 +603,7 @@ foreign_expr_walker(Node *node, foreign_expr_cxt *context)
 			 * If it's anything else, assume it's unsafe.  This list can be
 			 * expanded later, but don't forget to add deparse support below.
 			 */
-			return true;
+			return false;
 	}
 
 	/*
@@ -326,10 +611,55 @@ foreign_expr_walker(Node *node, foreign_expr_cxt *context)
 	 * remote because it might have incompatible semantics on remote side.
 	 */
 	if (check_type && !is_builtin(exprType(node)))
-		return true;
+		return false;
+
+	/*
+	 * Now, merge my collation information into my parent's state.
+	 */
+	if (state > outer_cxt->state)
+	{
+		/* Override previous parent state */
+		outer_cxt->collation = collation;
+		outer_cxt->state = state;
+	}
+	else if (state == outer_cxt->state)
+	{
+		/* Merge, or detect error if there's a collation conflict */
+		switch (state)
+		{
+			case FDW_COLLATE_NONE:
+				/* Nothing + nothing is still nothing */
+				break;
+			case FDW_COLLATE_SAFE:
+				if (collation != outer_cxt->collation)
+				{
+					/*
+					 * Non-default collation always beats default.
+					 */
+					if (outer_cxt->collation == DEFAULT_COLLATION_OID)
+					{
+						/* Override previous parent state */
+						outer_cxt->collation = collation;
+					}
+					else if (collation != DEFAULT_COLLATION_OID)
+					{
+						/*
+						 * Conflict; show state as indeterminate.  We don't
+						 * want to "return false" right away, since parent
+						 * node might not care about collation.
+						 */
+						outer_cxt->state = FDW_COLLATE_UNSAFE;
+					}
+				}
+				break;
+			case FDW_COLLATE_UNSAFE:
+				/* We're still conflicted ... */
+				break;
+		}
+	}
 
-	/* Recurse to examine sub-nodes */
-	return expression_tree_walker(node, foreign_expr_walker, context);
+	/* It looks OK */
+	return true;
 }
 
 /*
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index d2913a9ae6ef8c86e0f546b8a79b636fd38b477f..706a37c685a48e768f881d01af0517364757b932 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -477,7 +477,7 @@ EXECUTE st1(101, 101);
 (1 row)
 
 -- subquery using stable function (can't be sent to remote)
-PREPARE st2(int) AS SELECT * FROM ft1 t1 WHERE t1.c1 < $2 AND t1.c3 IN (SELECT c3 FROM ft2 t2 WHERE c1 > $1 AND EXTRACT(dow FROM c4) = 6) ORDER BY c1;
+PREPARE st2(int) AS SELECT * FROM ft1 t1 WHERE t1.c1 < $2 AND t1.c3 IN (SELECT c3 FROM ft2 t2 WHERE c1 > $1 AND date(c4) = '1970-01-17'::date) ORDER BY c1;
 EXPLAIN (VERBOSE, COSTS false) EXECUTE st2(10, 20);
                                                        QUERY PLAN                                                        
 -------------------------------------------------------------------------------------------------------------------------
@@ -494,7 +494,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st2(10, 20);
                Output: t2.c3
                ->  Foreign Scan on public.ft2 t2
                      Output: t2.c3
-                     Filter: (date_part('dow'::text, t2.c4) = 6::double precision)
+                     Filter: (date(t2.c4) = '01-17-1970'::date)
                      Remote SQL: SELECT NULL, NULL, c3, c4, NULL, NULL, NULL, NULL FROM "S 1"."T 1" WHERE (("C 1" > 10))
 (15 rows)
 
@@ -504,17 +504,17 @@ EXECUTE st2(10, 20);
  16 |  6 | 00016 | Sat Jan 17 00:00:00 1970 PST | Sat Jan 17 00:00:00 1970 | 6  | 6          | foo
 (1 row)
 
-EXECUTE st1(101, 101);
-  c3   |  c3   
--------+-------
- 00101 | 00101
+EXECUTE st2(101, 121);
+ c1  | c2 |  c3   |              c4              |            c5            | c6 |     c7     | c8  
+-----+----+-------+------------------------------+--------------------------+----+------------+-----
+ 116 |  6 | 00116 | Sat Jan 17 00:00:00 1970 PST | Sat Jan 17 00:00:00 1970 | 6  | 6          | foo
 (1 row)
 
 -- subquery using immutable function (can be sent to remote)
-PREPARE st3(int) AS SELECT * FROM ft1 t1 WHERE t1.c1 < $2 AND t1.c3 IN (SELECT c3 FROM ft2 t2 WHERE c1 > $1 AND EXTRACT(dow FROM c5) = 6) ORDER BY c1;
+PREPARE st3(int) AS SELECT * FROM ft1 t1 WHERE t1.c1 < $2 AND t1.c3 IN (SELECT c3 FROM ft2 t2 WHERE c1 > $1 AND date(c5) = '1970-01-17'::date) ORDER BY c1;
 EXPLAIN (VERBOSE, COSTS false) EXECUTE st3(10, 20);
-                                                                                     QUERY PLAN                                                                                     
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+                                                                           QUERY PLAN                                                                            
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------
  Sort
    Output: t1.c1, t1.c2, t1.c3, t1.c4, t1.c5, t1.c6, t1.c7, t1.c8
    Sort Key: t1.c1
@@ -528,7 +528,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st3(10, 20);
                Output: t2.c3
                ->  Foreign Scan on public.ft2 t2
                      Output: t2.c3
-                     Remote SQL: SELECT NULL, NULL, c3, NULL, NULL, NULL, NULL, NULL FROM "S 1"."T 1" WHERE (("C 1" > 10)) AND ((date_part('dow'::text, c5) = 6::double precision))
+                     Remote SQL: SELECT NULL, NULL, c3, NULL, NULL, NULL, NULL, NULL FROM "S 1"."T 1" WHERE (("C 1" > 10)) AND ((date(c5) = '1970-01-17'::date))
 (14 rows)
 
 EXECUTE st3(10, 20);
@@ -538,10 +538,9 @@ EXECUTE st3(10, 20);
 (1 row)
 
 EXECUTE st3(20, 30);
- c1 | c2 |  c3   |              c4              |            c5            | c6 |     c7     | c8  
-----+----+-------+------------------------------+--------------------------+----+------------+-----
- 23 |  3 | 00023 | Sat Jan 24 00:00:00 1970 PST | Sat Jan 24 00:00:00 1970 | 3  | 3          | foo
-(1 row)
+ c1 | c2 | c3 | c4 | c5 | c6 | c7 | c8 
+----+----+----+----+----+----+----+----
+(0 rows)
 
 -- custom plan should be chosen initially
 PREPARE st4(int) AS SELECT * FROM ft1 t1 WHERE t1.c1 = $1;
@@ -731,6 +730,74 @@ SELECT * FROM ft1 ORDER BY c1 LIMIT 1;
 (1 row)
 
 COMMIT;
+-- ===================================================================
+-- test handling of collations
+-- ===================================================================
+create table loct3 (f1 text collate "C", f2 text);
+create foreign table ft3 (f1 text collate "C", f2 text)
+  server loopback options (table_name 'loct3');
+-- can be sent to remote
+explain (verbose, costs off) select * from ft3 where f1 = 'foo';
+                                QUERY PLAN                                
+--------------------------------------------------------------------------
+ Foreign Scan on public.ft3
+   Output: f1, f2
+   Remote SQL: SELECT f1, f2 FROM public.loct3 WHERE ((f1 = 'foo'::text))
+(3 rows)
+
+explain (verbose, costs off) select * from ft3 where f1 COLLATE "C" = 'foo';
+                                QUERY PLAN                                
+--------------------------------------------------------------------------
+ Foreign Scan on public.ft3
+   Output: f1, f2
+   Remote SQL: SELECT f1, f2 FROM public.loct3 WHERE ((f1 = 'foo'::text))
+(3 rows)
+
+explain (verbose, costs off) select * from ft3 where f2 = 'foo';
+                                QUERY PLAN                                
+--------------------------------------------------------------------------
+ Foreign Scan on public.ft3
+   Output: f1, f2
+   Remote SQL: SELECT f1, f2 FROM public.loct3 WHERE ((f2 = 'foo'::text))
+(3 rows)
+
+-- can't be sent to remote
+explain (verbose, costs off) select * from ft3 where f1 COLLATE "POSIX" = 'foo';
+                  QUERY PLAN                   
+-----------------------------------------------
+ Foreign Scan on public.ft3
+   Output: f1, f2
+   Filter: ((ft3.f1)::text = 'foo'::text)
+   Remote SQL: SELECT f1, f2 FROM public.loct3
+(4 rows)
+
+explain (verbose, costs off) select * from ft3 where f1 = 'foo' COLLATE "C";
+                  QUERY PLAN                   
+-----------------------------------------------
+ Foreign Scan on public.ft3
+   Output: f1, f2
+   Filter: (ft3.f1 = 'foo'::text COLLATE "C")
+   Remote SQL: SELECT f1, f2 FROM public.loct3
+(4 rows)
+
+explain (verbose, costs off) select * from ft3 where f2 COLLATE "C" = 'foo';
+                  QUERY PLAN                   
+-----------------------------------------------
+ Foreign Scan on public.ft3
+   Output: f1, f2
+   Filter: ((ft3.f2)::text = 'foo'::text)
+   Remote SQL: SELECT f1, f2 FROM public.loct3
+(4 rows)
+
+explain (verbose, costs off) select * from ft3 where f2 = 'foo' COLLATE "C";
+                  QUERY PLAN                   
+-----------------------------------------------
+ Foreign Scan on public.ft3
+   Output: f1, f2
+   Filter: (ft3.f2 = 'foo'::text COLLATE "C")
+   Remote SQL: SELECT f1, f2 FROM public.loct3
+(4 rows)
+
 -- ===================================================================
 -- test writable foreign table stuff
 -- ===================================================================
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 70c1e85f32503f2b00743071e24a46a2cf6e3135..6dc50e4a2a7b32522c7551b430f4f3a0470223dd 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -199,12 +199,12 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st1(1, 2);
 EXECUTE st1(1, 1);
 EXECUTE st1(101, 101);
 -- subquery using stable function (can't be sent to remote)
-PREPARE st2(int) AS SELECT * FROM ft1 t1 WHERE t1.c1 < $2 AND t1.c3 IN (SELECT c3 FROM ft2 t2 WHERE c1 > $1 AND EXTRACT(dow FROM c4) = 6) ORDER BY c1;
+PREPARE st2(int) AS SELECT * FROM ft1 t1 WHERE t1.c1 < $2 AND t1.c3 IN (SELECT c3 FROM ft2 t2 WHERE c1 > $1 AND date(c4) = '1970-01-17'::date) ORDER BY c1;
 EXPLAIN (VERBOSE, COSTS false) EXECUTE st2(10, 20);
 EXECUTE st2(10, 20);
-EXECUTE st1(101, 101);
+EXECUTE st2(101, 121);
 -- subquery using immutable function (can be sent to remote)
-PREPARE st3(int) AS SELECT * FROM ft1 t1 WHERE t1.c1 < $2 AND t1.c3 IN (SELECT c3 FROM ft2 t2 WHERE c1 > $1 AND EXTRACT(dow FROM c5) = 6) ORDER BY c1;
+PREPARE st3(int) AS SELECT * FROM ft1 t1 WHERE t1.c1 < $2 AND t1.c3 IN (SELECT c3 FROM ft2 t2 WHERE c1 > $1 AND date(c5) = '1970-01-17'::date) ORDER BY c1;
 EXPLAIN (VERBOSE, COSTS false) EXECUTE st3(10, 20);
 EXECUTE st3(10, 20);
 EXECUTE st3(20, 30);
@@ -274,6 +274,23 @@ FETCH c;
 SELECT * FROM ft1 ORDER BY c1 LIMIT 1;
 COMMIT;
 
+-- ===================================================================
+-- test handling of collations
+-- ===================================================================
+create table loct3 (f1 text collate "C", f2 text);
+create foreign table ft3 (f1 text collate "C", f2 text)
+  server loopback options (table_name 'loct3');
+
+-- can be sent to remote
+explain (verbose, costs off) select * from ft3 where f1 = 'foo';
+explain (verbose, costs off) select * from ft3 where f1 COLLATE "C" = 'foo';
+explain (verbose, costs off) select * from ft3 where f2 = 'foo';
+-- can't be sent to remote
+explain (verbose, costs off) select * from ft3 where f1 COLLATE "POSIX" = 'foo';
+explain (verbose, costs off) select * from ft3 where f1 = 'foo' COLLATE "C";
+explain (verbose, costs off) select * from ft3 where f2 COLLATE "C" = 'foo';
+explain (verbose, costs off) select * from ft3 where f2 = 'foo' COLLATE "C";
+
 -- ===================================================================
 -- test writable foreign table stuff
 -- ===================================================================