diff --git a/doc/src/sgml/ref/create_table_as.sgml b/doc/src/sgml/ref/create_table_as.sgml
index 593b1c7a8f83a739e6778d6b1251a2b521fa3e58..1be9f4d1f6a1e78f0834fa38ffff12dec447be7a 100644
--- a/doc/src/sgml/ref/create_table_as.sgml
+++ b/doc/src/sgml/ref/create_table_as.sgml
@@ -105,10 +105,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE <replaceable
     <listitem>
      <para>
       The name of a column in the new table.  If column names are not
-      provided, they are taken from the output column names of the
-      query.  If the table is created from an
-      <command>EXECUTE</command> command, a column name list cannot be
-      specified.
+      provided, they are taken from the output column names of the query.
      </para>
     </listitem>
    </varlistentry>
@@ -252,7 +249,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE <replaceable
    identical to pre-8.0 releases.  Applications that
    require OIDs in the table created by <command>CREATE TABLE
    AS</command> should explicitly specify <literal>WITH (OIDS)</literal>
-   to ensure proper behavior.
+   to ensure desired behavior.
   </para>
  </refsect1>
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 708831a5c34d1ee126994ca808cc3cda4c93814d..d19e0978e4ea45f7f198ca841c5903bd8098e388 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -56,6 +56,7 @@
 #include "storage/smgr.h"
 #include "tcop/utility.h"
 #include "utils/acl.h"
+#include "utils/builtins.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/snapmgr.h"
@@ -305,6 +306,13 @@ standard_ExecutorRun(QueryDesc *queryDesc,
 	if (sendTuples)
 		(*dest->rStartup) (dest, operation, queryDesc->tupDesc);
 
+	/*
+	 * if it's CREATE TABLE AS ... WITH NO DATA, skip plan execution
+	 */
+	if (estate->es_select_into &&
+		queryDesc->plannedstmt->intoClause->skipData)
+		direction = NoMovementScanDirection;
+
 	/*
 	 * run plan
 	 */
@@ -2388,6 +2396,7 @@ OpenIntoRel(QueryDesc *queryDesc)
 {
 	IntoClause *into = queryDesc->plannedstmt->intoClause;
 	EState	   *estate = queryDesc->estate;
+	TupleDesc	intoTupDesc = queryDesc->tupDesc;
 	Relation	intoRelationDesc;
 	char	   *intoName;
 	Oid			namespaceId;
@@ -2415,6 +2424,31 @@ OpenIntoRel(QueryDesc *queryDesc)
 				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
 				 errmsg("ON COMMIT can only be used on temporary tables")));
 
+	/*
+	 * If a column name list was specified in CREATE TABLE AS, override the
+	 * column names derived from the query.  (Too few column names are OK, too
+	 * many are not.)  It would probably be all right to scribble directly on
+	 * the query's result tupdesc, but let's be safe and make a copy.
+	 */
+	if (into->colNames)
+	{
+		ListCell   *lc;
+
+		intoTupDesc = CreateTupleDescCopy(intoTupDesc);
+		attnum = 1;
+		foreach(lc, into->colNames)
+		{
+			char	   *colname = strVal(lfirst(lc));
+
+			if (attnum > intoTupDesc->natts)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("CREATE TABLE AS specifies too many column names")));
+			namestrcpy(&(intoTupDesc->attrs[attnum - 1]->attname), colname);
+			attnum++;
+		}
+	}
+
 	/*
 	 * Find namespace to create in, check its permissions
 	 */
@@ -2477,7 +2511,7 @@ OpenIntoRel(QueryDesc *queryDesc)
 											  InvalidOid,
 											  InvalidOid,
 											  GetUserId(),
-											  queryDesc->tupDesc,
+											  intoTupDesc,
 											  NIL,
 											  RELKIND_RELATION,
 											  into->rel->relpersistence,
@@ -2519,7 +2553,7 @@ OpenIntoRel(QueryDesc *queryDesc)
 	intoRelationDesc = heap_open(intoRelationId, AccessExclusiveLock);
 
 	/*
-	 * check INSERT permission on the constructed table.
+	 * Check INSERT permission on the constructed table.
 	 */
 	rte = makeNode(RangeTblEntry);
 	rte->rtekind = RTE_RELATION;
@@ -2527,7 +2561,7 @@ OpenIntoRel(QueryDesc *queryDesc)
 	rte->relkind = RELKIND_RELATION;
 	rte->requiredPerms = ACL_INSERT;
 
-	for (attnum = 1; attnum <= queryDesc->tupDesc->natts; attnum++)
+	for (attnum = 1; attnum <= intoTupDesc->natts; attnum++)
 		rte->modifiedCols = bms_add_member(rte->modifiedCols,
 				attnum - FirstLowInvalidHeapAttributeNumber);
 
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index b8f047a9a58bc7db0ffb38b5fa4c9f3a5183071a..c70a5bdf38898e7f76fa40322fb973b269f9698a 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1041,6 +1041,7 @@ _copyIntoClause(IntoClause *from)
 	COPY_NODE_FIELD(options);
 	COPY_SCALAR_FIELD(onCommit);
 	COPY_STRING_FIELD(tableSpaceName);
+	COPY_SCALAR_FIELD(skipData);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index d1af48a30da7e892fc994f8a785728ad774e89ee..f490a7ab2ef2a653c8270034820fa7bd957feff0 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -119,6 +119,7 @@ _equalIntoClause(IntoClause *a, IntoClause *b)
 	COMPARE_NODE_FIELD(options);
 	COMPARE_SCALAR_FIELD(onCommit);
 	COMPARE_STRING_FIELD(tableSpaceName);
+	COMPARE_SCALAR_FIELD(skipData);
 
 	return true;
 }
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index f7d39edf6fd24961f88dd92383ea87151b97acc2..31af47fdd2a2567db957a6fd881e6241c94b7127 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -899,6 +899,7 @@ _outIntoClause(StringInfo str, IntoClause *node)
 	WRITE_NODE_FIELD(options);
 	WRITE_ENUM_FIELD(onCommit, OnCommitAction);
 	WRITE_STRING_FIELD(tableSpaceName);
+	WRITE_BOOL_FIELD(skipData);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 29a0e8fe3baebf73cf76f8cf606805e731022e8c..3de20ad93a96a5749194791c700517907f8816d6 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -394,6 +394,7 @@ _readIntoClause(void)
 	READ_NODE_FIELD(options);
 	READ_ENUM_FIELD(onCommit, OnCommitAction);
 	READ_STRING_FIELD(tableSpaceName);
+	READ_BOOL_FIELD(skipData);
 
 	READ_DONE();
 }
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index e4a4e3a5e48005a62b02e79f83277ce758c43b28..dae547839319ae00abd0767f8fbbae0c6bd916fd 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -56,7 +56,6 @@ static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
 						  bool isTopLevel, List **targetlist);
 static void determineRecursiveColTypes(ParseState *pstate,
 						   Node *larg, List *nrtargetlist);
-static void applyColumnNames(List *dst, List *src);
 static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
 static List *transformReturningList(ParseState *pstate, List *returningList);
 static Query *transformDeclareCursorStmt(ParseState *pstate,
@@ -964,13 +963,8 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 												   pstate->p_windowdefs,
 												   &qry->targetList);
 
-	/* handle any SELECT INTO/CREATE TABLE AS spec */
-	if (stmt->intoClause)
-	{
-		qry->intoClause = stmt->intoClause;
-		if (stmt->intoClause->colNames)
-			applyColumnNames(qry->targetList, stmt->intoClause->colNames);
-	}
+	/* SELECT INTO/CREATE TABLE AS spec is just passed through */
+	qry->intoClause = stmt->intoClause;
 
 	qry->rtable = pstate->p_rtable;
 	qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
@@ -1191,13 +1185,8 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 			 errmsg("SELECT FOR UPDATE/SHARE cannot be applied to VALUES")));
 
-	/* handle any CREATE TABLE AS spec */
-	if (stmt->intoClause)
-	{
-		qry->intoClause = stmt->intoClause;
-		if (stmt->intoClause->colNames)
-			applyColumnNames(qry->targetList, stmt->intoClause->colNames);
-	}
+	/* CREATE TABLE AS spec is just passed through */
+	qry->intoClause = stmt->intoClause;
 
 	/*
 	 * There mustn't have been any table references in the expressions, else
@@ -1268,7 +1257,6 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
 	int			leftmostRTI;
 	Query	   *leftmostQuery;
 	SetOperationStmt *sostmt;
-	List	   *intoColNames = NIL;
 	List	   *sortClause;
 	Node	   *limitOffset;
 	Node	   *limitCount;
@@ -1306,11 +1294,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
 		leftmostSelect = leftmostSelect->larg;
 	Assert(leftmostSelect && IsA(leftmostSelect, SelectStmt) &&
 		   leftmostSelect->larg == NULL);
-	if (leftmostSelect->intoClause)
-	{
-		qry->intoClause = leftmostSelect->intoClause;
-		intoColNames = leftmostSelect->intoClause->colNames;
-	}
+	qry->intoClause = leftmostSelect->intoClause;
 
 	/* clear this to prevent complaints in transformSetOperationTree() */
 	leftmostSelect->intoClause = NULL;
@@ -1460,19 +1444,6 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
 	qry->limitCount = transformLimitClause(pstate, limitCount,
 										   "LIMIT");
 
-	/*
-	 * Handle SELECT INTO/CREATE TABLE AS.
-	 *
-	 * Any column names from CREATE TABLE AS need to be attached to both the
-	 * top level and the leftmost subquery.  We do not do this earlier because
-	 * we do *not* want sortClause processing to be affected.
-	 */
-	if (intoColNames)
-	{
-		applyColumnNames(qry->targetList, intoColNames);
-		applyColumnNames(leftmostQuery->targetList, intoColNames);
-	}
-
 	qry->rtable = pstate->p_rtable;
 	qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
 
@@ -1892,44 +1863,6 @@ determineRecursiveColTypes(ParseState *pstate, Node *larg, List *nrtargetlist)
 	analyzeCTETargetList(pstate, pstate->p_parent_cte, targetList);
 }
 
-/*
- * Attach column names from a ColumnDef list to a TargetEntry list
- * (for CREATE TABLE AS)
- */
-static void
-applyColumnNames(List *dst, List *src)
-{
-	ListCell   *dst_item;
-	ListCell   *src_item;
-
-	src_item = list_head(src);
-
-	foreach(dst_item, dst)
-	{
-		TargetEntry *d = (TargetEntry *) lfirst(dst_item);
-		ColumnDef  *s;
-
-		/* junk targets don't count */
-		if (d->resjunk)
-			continue;
-
-		/* fewer ColumnDefs than target entries is OK */
-		if (src_item == NULL)
-			break;
-
-		s = (ColumnDef *) lfirst(src_item);
-		src_item = lnext(src_item);
-
-		d->resname = pstrdup(s->colname);
-	}
-
-	/* more ColumnDefs than target entries is not OK */
-	if (src_item != NULL)
-		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
-				 errmsg("CREATE TABLE AS specifies too many column names")));
-}
-
 
 /*
  * transformUpdateStmt -
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7ea38e4663e850e9831a4d08df644c29848ae6d6..2a497d1b79dd57d92b2f78655baa3edbddc6a5ac 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -387,8 +387,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType,
 %type <node>	case_expr case_arg when_clause case_default
 %type <list>	when_clause_list
 %type <ival>	sub_type
-%type <list>	OptCreateAs CreateAsList
-%type <node>	CreateAsElement ctext_expr
+%type <node>	ctext_expr
 %type <value>	NumericOnly
 %type <list>	NumericOnly_list
 %type <alias>	alias_clause
@@ -3015,8 +3014,7 @@ CreateAsStmt:
 					 * When the SelectStmt is a set-operation tree, we must
 					 * stuff the INTO information into the leftmost component
 					 * Select, because that's where analyze.c will expect
-					 * to find it.	Similarly, the output column names must
-					 * be attached to that Select's target list.
+					 * to find it.
 					 */
 					SelectStmt *n = findLeftmostSelect((SelectStmt *) $6);
 					if (n->intoClause != NULL)
@@ -3024,17 +3022,16 @@ CreateAsStmt:
 								(errcode(ERRCODE_SYNTAX_ERROR),
 								 errmsg("CREATE TABLE AS cannot specify INTO"),
 								 parser_errposition(exprLocation((Node *) n->intoClause))));
-					$4->rel->relpersistence = $2;
 					n->intoClause = $4;
-					/* Implement WITH NO DATA by forcing top-level LIMIT 0 */
-					if (!$7)
-						((SelectStmt *) $6)->limitCount = makeIntConst(0, -1);
+					/* cram additional flags into the IntoClause */
+					$4->rel->relpersistence = $2;
+					$4->skipData = !($7);
 					$$ = $6;
 				}
 		;
 
 create_as_target:
-			qualified_name OptCreateAs OptWith OnCommitOption OptTableSpace
+			qualified_name opt_column_list OptWith OnCommitOption OptTableSpace
 				{
 					$$ = makeNode(IntoClause);
 					$$->rel = $1;
@@ -3042,36 +3039,7 @@ create_as_target:
 					$$->options = $3;
 					$$->onCommit = $4;
 					$$->tableSpaceName = $5;
-				}
-		;
-
-OptCreateAs:
-			'(' CreateAsList ')'					{ $$ = $2; }
-			| /*EMPTY*/								{ $$ = NIL; }
-		;
-
-CreateAsList:
-			CreateAsElement							{ $$ = list_make1($1); }
-			| CreateAsList ',' CreateAsElement		{ $$ = lappend($1, $3); }
-		;
-
-CreateAsElement:
-			ColId
-				{
-					ColumnDef *n = makeNode(ColumnDef);
-					n->colname = $1;
-					n->typeName = NULL;
-					n->inhcount = 0;
-					n->is_local = true;
-					n->is_not_null = false;
-					n->is_from_type = false;
-					n->storage = 0;
-					n->raw_default = NULL;
-					n->cooked_default = NULL;
-					n->collClause = NULL;
-					n->collOid = InvalidOid;
-					n->constraints = NIL;
-					$$ = (Node *)n;
+					$$->skipData = false;		/* might get changed later */
 				}
 		;
 
@@ -8030,18 +7998,15 @@ ExecuteStmt: EXECUTE name execute_param_clause
 					$$ = (Node *) n;
 				}
 			| CREATE OptTemp TABLE create_as_target AS
-				EXECUTE name execute_param_clause
+				EXECUTE name execute_param_clause opt_with_data
 				{
 					ExecuteStmt *n = makeNode(ExecuteStmt);
 					n->name = $7;
 					n->params = $8;
-					$4->rel->relpersistence = $2;
 					n->into = $4;
-					if ($4->colNames)
-						ereport(ERROR,
-								(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-								 errmsg("column name list not allowed in CREATE TABLE / AS EXECUTE")));
-					/* ... because it's not implemented, but it could be */
+					/* cram additional flags into the IntoClause */
+					$4->rel->relpersistence = $2;
+					$4->skipData = !($9);
 					$$ = (Node *) n;
 				}
 		;
@@ -8583,6 +8548,7 @@ into_clause:
 					$$->options = NIL;
 					$$->onCommit = ONCOMMIT_NOOP;
 					$$->tableSpaceName = NULL;
+					$$->skipData = false;
 				}
 			| /*EMPTY*/
 				{ $$ = NULL; }
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 02f60939bfeed46669069159d0e994f83d1a0607..088c307660798d3f66afc230562b0450301a9668 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	201111231
+#define CATALOG_VERSION_NO	201111241
 
 #endif
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index cedf022e174a0cd9ebc4955b432f1ac1fce8d034..28a2b120f612789bbc539d13aa107f7af445cc70 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -91,6 +91,7 @@ typedef struct IntoClause
 	List	   *options;		/* options from WITH clause */
 	OnCommitAction onCommit;	/* what do we do at COMMIT? */
 	char	   *tableSpaceName; /* table space to use, or NULL */
+	bool		skipData;		/* true for WITH NO DATA */
 } IntoClause;