diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 7cfa63f..8a13ac4 100644
*** a/src/backend/executor/execQual.c
--- b/src/backend/executor/execQual.c
***************
*** 50,55 ****
--- 50,56 ----
  #include "nodes/nodeFuncs.h"
  #include "optimizer/planner.h"
  #include "parser/parse_coerce.h"
+ #include "parser/parsetree.h"
  #include "pgstat.h"
  #include "utils/acl.h"
  #include "utils/builtins.h"
*************** ExecEvalWholeRowVar(WholeRowVarExprState
*** 712,717 ****
--- 713,720 ----
  {
  	Var		   *variable = (Var *) wrvstate->xprstate.expr;
  	TupleTableSlot *slot;
+ 	TupleDesc	output_tupdesc;
+ 	MemoryContext oldcontext;
  	bool		needslow = false;
  
  	if (isDone)
*************** ExecEvalWholeRowVar(WholeRowVarExprState
*** 787,794 ****
  			/* If so, build the junkfilter in the query memory context */
  			if (junk_filter_needed)
  			{
- 				MemoryContext oldcontext;
- 
  				oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
  				wrvstate->wrv_junkFilter =
  					ExecInitJunkFilter(subplan->plan->targetlist,
--- 790,795 ----
*************** ExecEvalWholeRowVar(WholeRowVarExprState
*** 860,869 ****
  				needslow = true;	/* need runtime check for null */
  		}
  
  		ReleaseTupleDesc(var_tupdesc);
  	}
  
! 	/* Skip the checking on future executions of node */
  	if (needslow)
  		wrvstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalWholeRowSlow;
  	else
--- 861,921 ----
  				needslow = true;	/* need runtime check for null */
  		}
  
+ 		/*
+ 		 * Use the variable's declared rowtype as the descriptor for the
+ 		 * output values.  In particular, we *must* absorb any attisdropped
+ 		 * markings.
+ 		 */
+ 		oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+ 		output_tupdesc = CreateTupleDescCopy(var_tupdesc);
+ 		MemoryContextSwitchTo(oldcontext);
+ 
  		ReleaseTupleDesc(var_tupdesc);
  	}
+ 	else
+ 	{
+ 		/*
+ 		 * In the RECORD case, we use the input slot's rowtype as the
+ 		 * descriptor for the output values, modulo possibly assigning new
+ 		 * column names below.
+ 		 */
+ 		oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+ 		output_tupdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor);
+ 		MemoryContextSwitchTo(oldcontext);
+ 	}
  
! 	/*
! 	 * Construct a tuple descriptor for the composite values we'll produce,
! 	 * and make sure its record type is "blessed".  The main reason to do this
! 	 * is to be sure that operations such as row_to_json() will see the
! 	 * desired column names when they look up the descriptor from the type
! 	 * information embedded in the composite values.
! 	 *
! 	 * We already got the correct physical datatype info above, but now we
! 	 * should try to find the source RTE and adopt its column aliases, in case
! 	 * they are different from the original rowtype's names.  But to minimize
! 	 * compatibility breakage, don't do this for Vars of named composite
! 	 * types, only for Vars of type RECORD.
! 	 *
! 	 * If we can't locate the RTE, assume the column names we've got are OK.
! 	 * (As of this writing, the only cases where we can't locate the RTE are
! 	 * in execution of trigger WHEN clauses, and then the Var will have the
! 	 * trigger's relation's rowtype, so its names are fine.)
! 	 */
! 	if (variable->vartype == RECORDOID &&
! 		econtext->ecxt_estate &&
! 		variable->varno <= list_length(econtext->ecxt_estate->es_range_table))
! 	{
! 		RangeTblEntry *rte = rt_fetch(variable->varno,
! 									  econtext->ecxt_estate->es_range_table);
! 
! 		ExecTypeSetColNames(output_tupdesc, rte->eref->colnames);
! 	}
! 
! 	/* Bless the tupdesc if needed, and save it in the execution state */
! 	wrvstate->wrv_tupdesc = BlessTupleDesc(output_tupdesc);
! 
! 	/* Skip all the above on future executions of node */
  	if (needslow)
  		wrvstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalWholeRowSlow;
  	else
*************** ExecEvalWholeRowFast(WholeRowVarExprStat
*** 886,892 ****
  {
  	Var		   *variable = (Var *) wrvstate->xprstate.expr;
  	TupleTableSlot *slot;
- 	TupleDesc	slot_tupdesc;
  	HeapTupleHeader dtuple;
  
  	if (isDone)
--- 938,943 ----
*************** ExecEvalWholeRowFast(WholeRowVarExprStat
*** 917,949 ****
  		slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
  
  	/*
- 	 * If it's a RECORD Var, we'll use the slot's type ID info.  It's likely
- 	 * that the slot's type is also RECORD; if so, make sure it's been
- 	 * "blessed", so that the Datum can be interpreted later.  (Note: we must
- 	 * do this here, not in ExecEvalWholeRowVar, because some plan trees may
- 	 * return different slots at different times.  We have to be ready to
- 	 * bless additional slots during the run.)
- 	 */
- 	slot_tupdesc = slot->tts_tupleDescriptor;
- 	if (variable->vartype == RECORDOID &&
- 		slot_tupdesc->tdtypeid == RECORDOID &&
- 		slot_tupdesc->tdtypmod < 0)
- 		assign_record_type_typmod(slot_tupdesc);
- 
- 	/*
  	 * Copy the slot tuple and make sure any toasted fields get detoasted.
  	 */
  	dtuple = DatumGetHeapTupleHeader(ExecFetchSlotTupleDatum(slot));
  
  	/*
! 	 * If the Var identifies a named composite type, label the datum with that
! 	 * type; otherwise we'll use the slot's info.
  	 */
! 	if (variable->vartype != RECORDOID)
! 	{
! 		HeapTupleHeaderSetTypeId(dtuple, variable->vartype);
! 		HeapTupleHeaderSetTypMod(dtuple, variable->vartypmod);
! 	}
  
  	return PointerGetDatum(dtuple);
  }
--- 968,982 ----
  		slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
  
  	/*
  	 * Copy the slot tuple and make sure any toasted fields get detoasted.
  	 */
  	dtuple = DatumGetHeapTupleHeader(ExecFetchSlotTupleDatum(slot));
  
  	/*
! 	 * Label the datum with the composite type info we identified before.
  	 */
! 	HeapTupleHeaderSetTypeId(dtuple, wrvstate->wrv_tupdesc->tdtypeid);
! 	HeapTupleHeaderSetTypMod(dtuple, wrvstate->wrv_tupdesc->tdtypmod);
  
  	return PointerGetDatum(dtuple);
  }
*************** ExecEvalWholeRowSlow(WholeRowVarExprStat
*** 997,1004 ****
  	tuple = ExecFetchSlotTuple(slot);
  	tupleDesc = slot->tts_tupleDescriptor;
  
  	Assert(variable->vartype != RECORDOID);
! 	var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1);
  
  	/* Check to see if any dropped attributes are non-null */
  	for (i = 0; i < var_tupdesc->natts; i++)
--- 1030,1038 ----
  	tuple = ExecFetchSlotTuple(slot);
  	tupleDesc = slot->tts_tupleDescriptor;
  
+ 	/* wrv_tupdesc is a good enough representation of the Var's rowtype */
  	Assert(variable->vartype != RECORDOID);
! 	var_tupdesc = wrvstate->wrv_tupdesc;
  
  	/* Check to see if any dropped attributes are non-null */
  	for (i = 0; i < var_tupdesc->natts; i++)
*************** ExecEvalWholeRowSlow(WholeRowVarExprStat
*** 1025,1036 ****
  	dtuple = DatumGetHeapTupleHeader(ExecFetchSlotTupleDatum(slot));
  
  	/*
! 	 * Reset datum's type ID fields to match the Var.
  	 */
! 	HeapTupleHeaderSetTypeId(dtuple, variable->vartype);
! 	HeapTupleHeaderSetTypMod(dtuple, variable->vartypmod);
! 
! 	ReleaseTupleDesc(var_tupdesc);
  
  	return PointerGetDatum(dtuple);
  }
--- 1059,1068 ----
  	dtuple = DatumGetHeapTupleHeader(ExecFetchSlotTupleDatum(slot));
  
  	/*
! 	 * Label the datum with the composite type info we identified before.
  	 */
! 	HeapTupleHeaderSetTypeId(dtuple, wrvstate->wrv_tupdesc->tdtypeid);
! 	HeapTupleHeaderSetTypMod(dtuple, wrvstate->wrv_tupdesc->tdtypmod);
  
  	return PointerGetDatum(dtuple);
  }
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4375,4380 ****
--- 4407,4413 ----
  				WholeRowVarExprState *wstate = makeNode(WholeRowVarExprState);
  
  				wstate->parent = parent;
+ 				wstate->wrv_tupdesc = NULL;
  				wstate->wrv_junkFilter = NULL;
  				state = (ExprState *) wstate;
  				state->evalfunc = (ExprStateEvalFunc) ExecEvalWholeRowVar;
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 66515f7..6a0792b 100644
*** a/src/backend/executor/execTuples.c
--- b/src/backend/executor/execTuples.c
*************** ExecTypeFromExprList(List *exprList, Lis
*** 985,990 ****
--- 985,1033 ----
  }
  
  /*
+  * ExecTypeSetColNames - set column names in a TupleDesc
+  *
+  * Column names must be provided as an alias list (list of String nodes).
+  * We set names only in TupleDesc columns that lacked one before.
+  */
+ void
+ ExecTypeSetColNames(TupleDesc typeInfo, List *namesList)
+ {
+ 	bool		modified = false;
+ 	int			colno = 0;
+ 	ListCell   *lc;
+ 
+ 	foreach(lc, namesList)
+ 	{
+ 		char	   *cname = strVal(lfirst(lc));
+ 		Form_pg_attribute attr;
+ 
+ 		/* Guard against too-long names list */
+ 		if (colno >= typeInfo->natts)
+ 			break;
+ 		attr = typeInfo->attrs[colno++];
+ 
+ 		/* Ignore empty aliases (these must be for dropped columns) */
+ 		if (cname[0] == '\0')
+ 			continue;
+ 
+ 		/* Change tupdesc only if we didn't have a name before */
+ 		if (NameStr(attr->attname)[0] == '\0')
+ 		{
+ 			namestrcpy(&(attr->attname), cname);
+ 			modified = true;
+ 		}
+ 	}
+ 
+ 	/* If we modified the tupdesc, it's now a new record type */
+ 	if (modified)
+ 	{
+ 		typeInfo->tdtypeid = RECORDOID;
+ 		typeInfo->tdtypmod = -1;
+ 	}
+ }
+ 
+ /*
   * BlessTupleDesc - make a completed tuple descriptor useful for SRFs
   *
   * Rowtype Datums returned by a function must contain valid type information.
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 0266135..21e9e3b 100644
*** a/src/include/executor/executor.h
--- b/src/include/executor/executor.h
*************** extern TupleTableSlot *ExecInitNullTuple
*** 266,271 ****
--- 266,272 ----
  extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid);
  extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid);
  extern TupleDesc ExecTypeFromExprList(List *exprList, List *namesList);
+ extern void ExecTypeSetColNames(TupleDesc typeInfo, List *namesList);
  extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg);
  
  typedef struct TupOutputState
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index fae2811..aaf9a55 100644
*** a/src/include/nodes/execnodes.h
--- b/src/include/nodes/execnodes.h
*************** typedef struct WholeRowVarExprState
*** 578,583 ****
--- 578,584 ----
  	ExprState	xprstate;
  	struct PlanState *parent;	/* parent PlanState, or NULL if none */
  	JunkFilter *wrv_junkFilter; /* JunkFilter to remove resjunk cols */
+ 	TupleDesc	wrv_tupdesc;	/* descriptor for resulting tuples */
  } WholeRowVarExprState;
  
  /* ----------------
diff --git a/src/test/regress/expected/rowtypes.out b/src/test/regress/expected/rowtypes.out
index 88e7bfa..d7f1dd5 100644
*** a/src/test/regress/expected/rowtypes.out
--- b/src/test/regress/expected/rowtypes.out
*************** select (row('Jim', 'Beam')).text;  -- er
*** 474,476 ****
--- 474,636 ----
  ERROR:  could not identify column "text" in record data type
  LINE 1: select (row('Jim', 'Beam')).text;
                  ^
+ --
+ -- Test that composite values are seen to have the correct column names
+ -- (bug #11210 and other reports)
+ --
+ select row_to_json(i) from int8_tbl i;
+                   row_to_json                   
+ ------------------------------------------------
+  {"q1":123,"q2":456}
+  {"q1":123,"q2":4567890123456789}
+  {"q1":4567890123456789,"q2":123}
+  {"q1":4567890123456789,"q2":4567890123456789}
+  {"q1":4567890123456789,"q2":-4567890123456789}
+ (5 rows)
+ 
+ select row_to_json(i) from int8_tbl i(x,y);
+                   row_to_json                   
+ ------------------------------------------------
+  {"q1":123,"q2":456}
+  {"q1":123,"q2":4567890123456789}
+  {"q1":4567890123456789,"q2":123}
+  {"q1":4567890123456789,"q2":4567890123456789}
+  {"q1":4567890123456789,"q2":-4567890123456789}
+ (5 rows)
+ 
+ create temp view vv1 as select * from int8_tbl;
+ select row_to_json(i) from vv1 i;
+                   row_to_json                   
+ ------------------------------------------------
+  {"q1":123,"q2":456}
+  {"q1":123,"q2":4567890123456789}
+  {"q1":4567890123456789,"q2":123}
+  {"q1":4567890123456789,"q2":4567890123456789}
+  {"q1":4567890123456789,"q2":-4567890123456789}
+ (5 rows)
+ 
+ select row_to_json(i) from vv1 i(x,y);
+                   row_to_json                   
+ ------------------------------------------------
+  {"q1":123,"q2":456}
+  {"q1":123,"q2":4567890123456789}
+  {"q1":4567890123456789,"q2":123}
+  {"q1":4567890123456789,"q2":4567890123456789}
+  {"q1":4567890123456789,"q2":-4567890123456789}
+ (5 rows)
+ 
+ select row_to_json(ss) from
+   (select q1, q2 from int8_tbl) as ss;
+                   row_to_json                   
+ ------------------------------------------------
+  {"q1":123,"q2":456}
+  {"q1":123,"q2":4567890123456789}
+  {"q1":4567890123456789,"q2":123}
+  {"q1":4567890123456789,"q2":4567890123456789}
+  {"q1":4567890123456789,"q2":-4567890123456789}
+ (5 rows)
+ 
+ select row_to_json(ss) from
+   (select q1, q2 from int8_tbl offset 0) as ss;
+                   row_to_json                   
+ ------------------------------------------------
+  {"q1":123,"q2":456}
+  {"q1":123,"q2":4567890123456789}
+  {"q1":4567890123456789,"q2":123}
+  {"q1":4567890123456789,"q2":4567890123456789}
+  {"q1":4567890123456789,"q2":-4567890123456789}
+ (5 rows)
+ 
+ select row_to_json(ss) from
+   (select q1 as a, q2 as b from int8_tbl) as ss;
+                  row_to_json                  
+ ----------------------------------------------
+  {"a":123,"b":456}
+  {"a":123,"b":4567890123456789}
+  {"a":4567890123456789,"b":123}
+  {"a":4567890123456789,"b":4567890123456789}
+  {"a":4567890123456789,"b":-4567890123456789}
+ (5 rows)
+ 
+ select row_to_json(ss) from
+   (select q1 as a, q2 as b from int8_tbl offset 0) as ss;
+                   row_to_json                   
+ ------------------------------------------------
+  {"q1":123,"q2":456}
+  {"q1":123,"q2":4567890123456789}
+  {"q1":4567890123456789,"q2":123}
+  {"q1":4567890123456789,"q2":4567890123456789}
+  {"q1":4567890123456789,"q2":-4567890123456789}
+ (5 rows)
+ 
+ select row_to_json(ss) from
+   (select q1 as a, q2 as b from int8_tbl) as ss(x,y);
+                  row_to_json                  
+ ----------------------------------------------
+  {"x":123,"y":456}
+  {"x":123,"y":4567890123456789}
+  {"x":4567890123456789,"y":123}
+  {"x":4567890123456789,"y":4567890123456789}
+  {"x":4567890123456789,"y":-4567890123456789}
+ (5 rows)
+ 
+ select row_to_json(ss) from
+   (select q1 as a, q2 as b from int8_tbl offset 0) as ss(x,y);
+                   row_to_json                   
+ ------------------------------------------------
+  {"q1":123,"q2":456}
+  {"q1":123,"q2":4567890123456789}
+  {"q1":4567890123456789,"q2":123}
+  {"q1":4567890123456789,"q2":4567890123456789}
+  {"q1":4567890123456789,"q2":-4567890123456789}
+ (5 rows)
+ 
+ explain (costs off)
+ select row_to_json(q) from
+   (select thousand, tenthous from tenk1
+    where thousand = 42 and tenthous < 2000 offset 0) q;
+                          QUERY PLAN                          
+ -------------------------------------------------------------
+  Subquery Scan on q
+    ->  Index Only Scan using tenk1_thous_tenthous on tenk1
+          Index Cond: ((thousand = 42) AND (tenthous < 2000))
+ (3 rows)
+ 
+ select row_to_json(q) from
+   (select thousand, tenthous from tenk1
+    where thousand = 42 and tenthous < 2000 offset 0) q;
+            row_to_json           
+ ---------------------------------
+  {"thousand":42,"tenthous":42}
+  {"thousand":42,"tenthous":1042}
+ (2 rows)
+ 
+ select row_to_json(q) from
+   (select thousand as x, tenthous as y from tenk1
+    where thousand = 42 and tenthous < 2000 offset 0) q;
+     row_to_json    
+ -------------------
+  {"x":42,"y":42}
+  {"x":42,"y":1042}
+ (2 rows)
+ 
+ select row_to_json(q) from
+   (select thousand as x, tenthous as y from tenk1
+    where thousand = 42 and tenthous < 2000 offset 0) q(a,b);
+     row_to_json    
+ -------------------
+  {"a":42,"b":42}
+  {"a":42,"b":1042}
+ (2 rows)
+ 
+ create temp table tt1 as select * from int8_tbl limit 2;
+ create temp table tt2 () inherits(tt1);
+ insert into tt2 values(0,0);
+ select row_to_json(r) from (select q2,q1 from tt1 offset 0) r;
+            row_to_json            
+ ----------------------------------
+  {"q2":456,"q1":123}
+  {"q2":4567890123456789,"q1":123}
+  {"q2":0,"q1":0}
+ (3 rows)
+ 
diff --git a/src/test/regress/sql/rowtypes.sql b/src/test/regress/sql/rowtypes.sql
index 65ebdc5..bc3f021 100644
*** a/src/test/regress/sql/rowtypes.sql
--- b/src/test/regress/sql/rowtypes.sql
*************** select cast (row('Jim', 'Beam') as text)
*** 227,229 ****
--- 227,273 ----
  select (row('Jim', 'Beam'))::text;
  select text(row('Jim', 'Beam'));  -- error
  select (row('Jim', 'Beam')).text;  -- error
+ 
+ --
+ -- Test that composite values are seen to have the correct column names
+ -- (bug #11210 and other reports)
+ --
+ 
+ select row_to_json(i) from int8_tbl i;
+ select row_to_json(i) from int8_tbl i(x,y);
+ 
+ create temp view vv1 as select * from int8_tbl;
+ select row_to_json(i) from vv1 i;
+ select row_to_json(i) from vv1 i(x,y);
+ 
+ select row_to_json(ss) from
+   (select q1, q2 from int8_tbl) as ss;
+ select row_to_json(ss) from
+   (select q1, q2 from int8_tbl offset 0) as ss;
+ select row_to_json(ss) from
+   (select q1 as a, q2 as b from int8_tbl) as ss;
+ select row_to_json(ss) from
+   (select q1 as a, q2 as b from int8_tbl offset 0) as ss;
+ select row_to_json(ss) from
+   (select q1 as a, q2 as b from int8_tbl) as ss(x,y);
+ select row_to_json(ss) from
+   (select q1 as a, q2 as b from int8_tbl offset 0) as ss(x,y);
+ 
+ explain (costs off)
+ select row_to_json(q) from
+   (select thousand, tenthous from tenk1
+    where thousand = 42 and tenthous < 2000 offset 0) q;
+ select row_to_json(q) from
+   (select thousand, tenthous from tenk1
+    where thousand = 42 and tenthous < 2000 offset 0) q;
+ select row_to_json(q) from
+   (select thousand as x, tenthous as y from tenk1
+    where thousand = 42 and tenthous < 2000 offset 0) q;
+ select row_to_json(q) from
+   (select thousand as x, tenthous as y from tenk1
+    where thousand = 42 and tenthous < 2000 offset 0) q(a,b);
+ 
+ create temp table tt1 as select * from int8_tbl limit 2;
+ create temp table tt2 () inherits(tt1);
+ insert into tt2 values(0,0);
+ select row_to_json(r) from (select q2,q1 from tt1 offset 0) r;
