diff --git a/doc/src/sgml/queries.sgml b/doc/src/sgml/queries.sgml
index 30792f4..3f207e5 100644
--- a/doc/src/sgml/queries.sgml
+++ b/doc/src/sgml/queries.sgml
@@ -1601,6 +1601,9 @@ SELECT DISTINCT ON (<replaceable>expression</replaceable> <optional>, <replaceab
    <primary>EXCEPT</primary>
   </indexterm>
   <indexterm zone="queries-union">
+   <primary>CORRESPONDING</primary>
+  </indexterm>
+  <indexterm zone="queries-union">
    <primary>set union</primary>
   </indexterm>
   <indexterm zone="queries-union">
@@ -1617,9 +1620,9 @@ SELECT DISTINCT ON (<replaceable>expression</replaceable> <optional>, <replaceab
    The results of two queries can be combined using the set operations
    union, intersection, and difference.  The syntax is
 <synopsis>
-<replaceable>query1</replaceable> UNION <optional>ALL</optional> <replaceable>query2</replaceable>
-<replaceable>query1</replaceable> INTERSECT <optional>ALL</optional> <replaceable>query2</replaceable>
-<replaceable>query1</replaceable> EXCEPT <optional>ALL</optional> <replaceable>query2</replaceable>
+<replaceable>query1</replaceable> UNION <optional>ALL</optional> <optional>CORRESPONDING <optional>BY <replaceable>query2</replaceable>
+<replaceable>query1</replaceable> INTERSECT <optional>ALL</optional> <optional>CORRESPONDING <optional>BY <replaceable>query2</replaceable>
+<replaceable>query1</replaceable> EXCEPT <optional>ALL</optional> <optional>CORRESPONDING <optional>BY <replaceable>query2</replaceable>
 </synopsis>
    <replaceable>query1</replaceable> and
    <replaceable>query2</replaceable> are queries that can use any of
@@ -1659,11 +1662,22 @@ SELECT DISTINCT ON (<replaceable>expression</replaceable> <optional>, <replaceab
   </para>
 
   <para>
-   In order to calculate the union, intersection, or difference of two
-   queries, the two queries must be <quote>union compatible</quote>,
-   which means that they return the same number of columns and
-   the corresponding columns have compatible data types, as
-   described in <xref linkend="typeconv-union-case">.
+   <literal>EXCEPT</> returns all rows that are in the result of
+   <replaceable>query1</replaceable> but not in the result of
+   <replaceable>query2</replaceable>.  (This is sometimes called the
+   <firstterm>difference</> between two queries.)  Again, duplicates
+   are eliminated unless <literal>EXCEPT ALL</> is used.
+  </para>
+
+  <para>
+   <literal>CORRESPONDING</> returns all columns that are in both 
+   <replaceable>query1</> and <replaceable>query2</> with the same name.
+  </para>
+
+  <para>
+   <literal>CORRESPONDING BY</> returns all columns in the column list 
+   that are also in both <replaceable>query1</> and 
+   <replaceable>query2</> with the same name.
   </para>
  </sect1>
 
diff --git a/doc/src/sgml/sql.sgml b/doc/src/sgml/sql.sgml
index 57396d7..f98c22e 100644
--- a/doc/src/sgml/sql.sgml
+++ b/doc/src/sgml/sql.sgml
@@ -859,7 +859,7 @@ SELECT [ ALL | DISTINCT [ ON ( <replaceable class="PARAMETER">expression</replac
     [ WHERE <replaceable class="PARAMETER">condition</replaceable> ]
     [ GROUP BY <replaceable class="PARAMETER">expression</replaceable> [, ...] ]
     [ HAVING <replaceable class="PARAMETER">condition</replaceable> [, ...] ]
-    [ { UNION | INTERSECT | EXCEPT } [ ALL ] <replaceable class="PARAMETER">select</replaceable> ]
+    [ { UNION | INTERSECT | EXCEPT } [ ALL ] [ CORRESPONDING [ BY ( <replaceable class="PARAMETER">expression</replaceable> ) ] ] <replaceable class="PARAMETER">select</replaceable> ]
     [ ORDER BY <replaceable class="parameter">expression</replaceable> [ ASC | DESC | USING <replaceable class="parameter">operator</replaceable> ] [ NULLS { FIRST | LAST } ] [, ...] ]
     [ LIMIT { <replaceable class="PARAMETER">count</replaceable> | ALL } ]
     [ OFFSET <replaceable class="PARAMETER">start</replaceable> ]
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 30d733e..30874e0 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2870,6 +2870,7 @@ _copySelectStmt(const SelectStmt *from)
 	COPY_NODE_FIELD(withClause);
 	COPY_SCALAR_FIELD(op);
 	COPY_SCALAR_FIELD(all);
+	COPY_NODE_FIELD(correspondingClause);
 	COPY_NODE_FIELD(larg);
 	COPY_NODE_FIELD(rarg);
 
@@ -2885,6 +2886,7 @@ _copySetOperationStmt(const SetOperationStmt *from)
 	COPY_SCALAR_FIELD(all);
 	COPY_NODE_FIELD(larg);
 	COPY_NODE_FIELD(rarg);
+	COPY_NODE_FIELD(correspondingColumns);
 	COPY_NODE_FIELD(colTypes);
 	COPY_NODE_FIELD(colTypmods);
 	COPY_NODE_FIELD(colCollations);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 55c73b7..f375255 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1020,6 +1020,7 @@ _equalSelectStmt(const SelectStmt *a, const SelectStmt *b)
 	COMPARE_NODE_FIELD(withClause);
 	COMPARE_SCALAR_FIELD(op);
 	COMPARE_SCALAR_FIELD(all);
+	COMPARE_NODE_FIELD(correspondingClause);
 	COMPARE_NODE_FIELD(larg);
 	COMPARE_NODE_FIELD(rarg);
 
@@ -1033,6 +1034,7 @@ _equalSetOperationStmt(const SetOperationStmt *a, const SetOperationStmt *b)
 	COMPARE_SCALAR_FIELD(all);
 	COMPARE_NODE_FIELD(larg);
 	COMPARE_NODE_FIELD(rarg);
+	COMPARE_NODE_FIELD(correspondingColumns);
 	COMPARE_NODE_FIELD(colTypes);
 	COMPARE_NODE_FIELD(colTypmods);
 	COMPARE_NODE_FIELD(colCollations);
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index 7586cce..4962845 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -610,3 +610,24 @@ makeGroupingSet(GroupingSetKind kind, List *content, int location)
 	n->location = location;
 	return n;
 }
+
+/*
+ * changeTargetEntry -
+ *	  change a TargetEntry node
+ */
+TargetEntry *
+changeTargetEntry(TargetEntry *target, AttrNumber resno, AttrNumber loc)
+{
+	TargetEntry *tle = makeNode(TargetEntry);
+
+	tle->expr = target->expr;
+	tle->resno = resno;
+	tle->resname = target->resname;
+	tle->ressortgroupref = 0;
+	tle->resorigtbl = InvalidOid;
+	tle->resorigcol = loc;
+
+	tle->resjunk = target->resjunk;
+
+	return tle;
+}
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index c2f03b7..ca39c1c 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -3411,6 +3411,8 @@ raw_expression_tree_walker(Node *node,
 					return true;
 				if (walker(stmt->lockingClause, context))
 					return true;
+				if (walker(stmt->correspondingClause, context))
+					return true;
 				if (walker(stmt->withClause, context))
 					return true;
 				if (walker(stmt->larg, context))
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 1560ac3..6e96018 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2526,6 +2526,7 @@ _outSelectStmt(StringInfo str, const SelectStmt *node)
 	WRITE_NODE_FIELD(withClause);
 	WRITE_ENUM_FIELD(op, SetOperation);
 	WRITE_BOOL_FIELD(all);
+	WRITE_NODE_FIELD(correspondingClause);
 	WRITE_NODE_FIELD(larg);
 	WRITE_NODE_FIELD(rarg);
 }
@@ -2833,6 +2834,7 @@ _outSetOperationStmt(StringInfo str, const SetOperationStmt *node)
 	WRITE_BOOL_FIELD(all);
 	WRITE_NODE_FIELD(larg);
 	WRITE_NODE_FIELD(rarg);
+	WRITE_NODE_FIELD(correspondingColumns);
 	WRITE_NODE_FIELD(colTypes);
 	WRITE_NODE_FIELD(colTypmods);
 	WRITE_NODE_FIELD(colCollations);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index dcfa6ee..94b724b 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -418,6 +418,7 @@ _readSetOperationStmt(void)
 	READ_BOOL_FIELD(all);
 	READ_NODE_FIELD(larg);
 	READ_NODE_FIELD(rarg);
+	READ_NODE_FIELD(correspondingColumns);
 	READ_NODE_FIELD(colTypes);
 	READ_NODE_FIELD(colTypmods);
 	READ_NODE_FIELD(colCollations);
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 06e843d..4626d4e 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -110,6 +110,7 @@ static Node *adjust_appendrel_attrs_mutator(Node *node,
 static Relids adjust_relid_set(Relids relids, Index oldrelid, Index newrelid);
 static List *adjust_inherited_tlist(List *tlist,
 					   AppendRelInfo *context);
+static List *make_corresponding_target(List *ltargetlist, List *rtargetlist);
 
 
 /*
@@ -187,6 +188,24 @@ plan_set_operations(PlannerInfo *root)
 									   leftmostQuery->targetList,
 									   &top_tlist);
 	}
+	/*
+	 * If corresponding column specified, we take column names from it.
+	 */
+	else if (topop->correspondingColumns != NIL )
+	{
+		/*
+		 * Recurse on setOperations tree to generate paths for set ops. The
+		 * final output path should have just the column types shown as the
+		 * output from the top-level node, plus possibly resjunk working
+		 * columns (we can rely on upper-level nodes to deal with that).
+		 */
+		path = recurse_set_operations((Node *) topop, root,
+									  topop->colTypes, topop->colCollations,
+									  true, -1,
+									  topop->correspondingColumns,
+									  &top_tlist,
+									  NULL);
+	}
 	else
 	{
 		/*
@@ -252,6 +271,8 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
 					   List **pTargetList,
 					   double *pNumGroups)
 {
+	SetOperationStmt *topop = (SetOperationStmt *) root->parse->setOperations;
+
 	if (IsA(setOp, RangeTblRef))
 	{
 		RangeTblRef *rtr = (RangeTblRef *) setOp;
@@ -316,23 +337,45 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
 		path = (Path *) create_subqueryscan_path(root, rel, subpath,
 												 NIL, NULL);
 
-		/*
-		 * Figure out the appropriate target list, and update the
-		 * SubqueryScanPath with the PathTarget form of that.
-		 */
-		tlist = generate_setop_tlist(colTypes, colCollations,
+		if (topop->correspondingColumns != NIL )
+		{
+			List *correspondingTarget;
+			/* make target list that only contains corresponding column from sub-queries list ito use it for projection */
+			correspondingTarget = make_corresponding_target(topop->correspondingColumns,
+					subroot->processed_tlist);
+			/*
+			 * Figure out the appropriate target list, and update the
+			 * SubqueryScanPath with the PathTarget form of that.
+			 */
+			tlist = generate_setop_tlist(colTypes, colCollations, flag,
+					rtr->rtindex, true, correspondingTarget, refnames_tlist);
+
+			path = apply_projection_to_path(root, rel, path,
+					create_pathtarget(root, tlist));
+
+			/* Return the fully-fledged tlist to caller, too */
+			*pTargetList = tlist;
+
+		}
+		else
+		{
+			/*
+			* Figure out the appropriate target list, and update the
+			* SubqueryScanPath with the PathTarget form of that.
+			*/
+			tlist = generate_setop_tlist(colTypes, colCollations,
 									 flag,
 									 rtr->rtindex,
 									 true,
 									 subroot->processed_tlist,
 									 refnames_tlist);
 
-		path = apply_projection_to_path(root, rel, path,
+			path = apply_projection_to_path(root, rel, path,
 										create_pathtarget(root, tlist));
 
-		/* Return the fully-fledged tlist to caller, too */
-		*pTargetList = tlist;
-
+			/* Return the fully-fledged tlist to caller, too */
+			*pTargetList = tlist;
+		}
 		/*
 		 * Estimate number of groups if caller wants it.  If the subquery used
 		 * grouping or aggregation, its output is probably mostly unique
@@ -1026,9 +1069,7 @@ generate_setop_tlist(List *colTypes, List *colCollations,
 
 		rtlc = lnext(rtlc);
 
-		Assert(inputtle->resno == resno);
-		Assert(reftle->resno == resno);
-		Assert(!inputtle->resjunk);
+		Assert(!reftle->resjunk);
 		Assert(!reftle->resjunk);
 
 		/*
@@ -2112,3 +2153,75 @@ adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,
 	/* Now translate for this child */
 	return adjust_appendrel_attrs(root, node, appinfo);
 }
+
+/*
+ * generate target list from left target list with the order
+ * of right target list
+ */
+static List *make_corresponding_target(List *ltargetlist, List *rtargetlist)
+{
+	Index internal = 0;
+	ListCell *ltl;
+	ListCell *rtl;
+	int size;
+	int i;
+	List *matchingColumns = NIL;
+	TargetEntry *simple_te_array;
+
+	size = list_length(ltargetlist) + 1;
+
+	/* Use array to find the order of corresponding columen */
+	simple_te_array = (TargetEntry *) palloc0(size * sizeof(TargetEntry));
+	foreach(ltl, ltargetlist)
+	{
+
+		foreach(rtl, rtargetlist)
+		{
+
+			TargetEntry *ltle = (TargetEntry *) lfirst(ltl);
+			TargetEntry *rtle = (TargetEntry *) lfirst(rtl);
+
+			elog(DEBUG4, "%s", ltle->resname);
+
+			/* Names of the columns must be resolved before calling this method. */
+			Assert(ltle->resname != NULL);
+			Assert(rtle->resname != NULL);
+
+			/* If column names are the same, add it to array. */
+			if (strcmp(ltle->resname, rtle->resname) == 0)
+			{
+
+				simple_te_array[internal].xpr = rtle->xpr;
+				simple_te_array[internal].expr = rtle->expr;
+				simple_te_array[internal].resno = rtle->resno;
+				simple_te_array[internal].resname = rtle->resname;
+				simple_te_array[internal].ressortgroupref =
+						rtle->ressortgroupref;
+				simple_te_array[internal].resorigtbl = rtle->resorigtbl;
+				simple_te_array[internal].resorigcol = rtle->resorigcol;
+				simple_te_array[internal].resjunk = rtle->resjunk;
+				internal++;
+				continue;
+			}
+		}
+	}
+	/* traverse the array and make targetlist */
+	for (i = 0; i < internal; i++)
+	{
+
+		TargetEntry *tle = makeNode(TargetEntry);
+
+		tle->xpr = simple_te_array[i].xpr;
+		tle->expr = simple_te_array[i].expr;
+		tle->resno = simple_te_array[i].resno;
+		tle->resname = simple_te_array[i].resname;
+		tle->ressortgroupref = simple_te_array[i].ressortgroupref;
+		tle->resorigtbl = simple_te_array[i].resorigtbl;
+		tle->resorigcol = simple_te_array[i].resorigcol;
+		tle->resjunk = simple_te_array[i].resjunk;
+
+		matchingColumns = lappend(matchingColumns, tle);
+
+	}
+	return matchingColumns;
+}
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 0f7659b..c82cb32 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -76,6 +76,10 @@ static Query *transformCreateTableAsStmt(ParseState *pstate,
 						   CreateTableAsStmt *stmt);
 static void transformLockingClause(ParseState *pstate, Query *qry,
 					   LockingClause *lc, bool pushedDown);
+static List *determineMatchingColumns(List *ltargetlist, List *rtargetlist);
+static void *makeUnionDatatype(List *ltargetlist, List *rtargetlist,
+		SetOperationStmt *op, List **targetlist, ParseState *parentParseState,
+		const char *context);
 #ifdef RAW_EXPRESSION_COVERAGE_TEST
 static bool test_raw_expression_coverage(Node *node, void *context);
 #endif
@@ -1664,7 +1668,13 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
 	qry->targetList = NIL;
 	targetvars = NIL;
 	targetnames = NIL;
-	left_tlist = list_head(leftmostQuery->targetList);
+	/* for corresponding clause limits top-level query targetlist to those
+	 * corresponding column list only
+	 */
+	if (sostmt->correspondingColumns != NIL )
+		left_tlist = list_head(sostmt->correspondingColumns);
+	else
+		left_tlist = list_head(leftmostQuery->targetList);
 
 	forthree(lct, sostmt->colTypes,
 			 lcm, sostmt->colTypmods,
@@ -1924,8 +1934,6 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
 		SetOperationStmt *op = makeNode(SetOperationStmt);
 		List	   *ltargetlist;
 		List	   *rtargetlist;
-		ListCell   *ltl;
-		ListCell   *rtl;
 		const char *context;
 
 		context = (stmt->op == SETOP_UNION ? "UNION" :
@@ -1935,6 +1943,205 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
 		op->op = stmt->op;
 		op->all = stmt->all;
 
+		/* If CORRESPONDING is specified, syntax and column name validities checked,
+		 * column filtering is done by a subquery later on.
+		 */
+		if (stmt->correspondingClause == NIL )
+		{
+			/* No CORRESPONDING clause, no operation needed for column filtering */
+			op->correspondingColumns = stmt->correspondingClause;
+		}
+		else if (linitial(stmt->correspondingClause) == NULL )
+		{
+			/* CORRESPONDING clause, find matching column names from both tables.
+			 * If there are none then it is a syntax error.
+			 */
+			Query *largQuery;
+			Query *rargQuery;
+			List *matchingColumns;
+			List *rightCorrespondingColumns;
+
+			/* Analyze left query to resolve column names. */
+			largQuery = parse_sub_analyze((Node *) stmt->larg, pstate, NULL, false, false );
+
+			/* Analyze right query to resolve column names. */
+			rargQuery = parse_sub_analyze((Node *) stmt->rarg, pstate, NULL, false, false );
+
+			/* Find matching columns from both queries. */
+			matchingColumns = determineMatchingColumns(largQuery->targetList,rargQuery->targetList);
+			/*
+			 * there may be out-of-order resnos in corresponding target list
+			 */
+			op->correspondingColumns = orderCorrespondingLsit(matchingColumns);
+
+			/* If matchingColumns is empty, there is an error.
+			 * At least one column in the select lists must have the same name.
+			 */
+			if (list_length(matchingColumns) == 0)
+			{
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR), errmsg("%s queries with a CORRESPONDING clause must have at least one column with the same name", context)));
+			}
+
+			/* matchingColumns contain target list as it appear in left query targetList
+			 * we need matching column as it appear in right query targetlist inorder
+			 * to make output column type for corresponding columns
+			 */
+			rightCorrespondingColumns = determineMatchingColumns(rargQuery->targetList, largQuery->targetList);
+
+			/* make union'd datatype of output column
+			 */
+			makeUnionDatatype(matchingColumns, rightCorrespondingColumns, op, targetlist, pstate,
+					context);
+		}
+		else
+		{
+			/*
+			 * CORRESPONDING BY clause, find matching column names from both tables
+			 * and intersect them with BY(...) column list. If there are none
+			 * then it is a syntax error.
+			 */
+
+			Query *largQuery;
+			Query *rargQuery;
+			List *matchingColumns;
+			List *matchingColumnsFiltered;
+			List *rightCorrespondingColumns;
+			ListCell *corrtl;
+			ListCell *mctl;
+
+			/* Analyze left query to resolve column names. */
+			largQuery = parse_sub_analyze((Node *) stmt->larg, pstate, NULL,false, false );
+
+			/* Analyze right query to resolve column names. */
+			rargQuery = parse_sub_analyze((Node *) stmt->rarg, pstate, NULL, false, false );
+
+			/*
+			 * Find matching columns from both queries.
+			 * In CORRESPONDING BY, column names will be removed from
+			 * matchingColumns if they are not in the BY clause.
+			 * All columns in the BY clause must be in matchingColumns,
+			 * otherwise raise syntax error in BY clause.
+			 */
+
+			matchingColumns = determineMatchingColumns(largQuery->targetList,rargQuery->targetList);
+
+			/*
+			 * Every column name in correspondingClause must be in matchingColumns,
+			 * otherwise it is a syntax error.
+			 */
+			foreach(corrtl, stmt->correspondingClause)
+			{
+				Node* corrtle = lfirst(corrtl);
+				if (IsA(corrtle, ColumnRef)
+						&& list_length(((ColumnRef *) corrtle)->fields)
+								== 1&&
+								IsA(linitial(((ColumnRef *) corrtle)->fields), String))
+				{
+					/* Get column name from correspondingClause. */
+					char *name =
+							strVal(linitial(((ColumnRef *) corrtle)->fields));
+					bool hasMatch = false;
+
+					foreach(mctl, matchingColumns)
+					{
+						TargetEntry *mctle = (TargetEntry *) lfirst(mctl);
+
+						Assert(mctle->resname != NULL);
+						Assert(name != NULL);
+
+						/* Compare correspondingClause column name with matchingColumns column names. */
+						if (strcmp(mctle->resname, name) == 0)
+						{
+							/* we have a match. */
+							hasMatch = true;
+							break;
+						}
+					}
+
+					if (!hasMatch)
+					{
+						/* CORRESPONDING BY clause contains a column name that is not in both tables. */
+						ereport(ERROR,
+								(errcode(ERRCODE_SYNTAX_ERROR), errmsg("CORRESPONDING BY clause must only contain column names from both tables.")));
+					}
+
+				}
+				else
+				{
+					/* Only column names are supported, constants are syntax error in CORRESPONDING BY clause. */
+					ereport(ERROR,
+							(errcode(ERRCODE_SYNTAX_ERROR), errmsg( "%s queries with CORRESPONDING BY clause must have only column names and not constants or ordinals in the column name list.", context)));
+				}
+			}
+
+			/* To preserve column ordering from correspondingClause and to remove
+			 * columns from matchingColumns if they are not in correspondingClause,
+			 * create a new list and finalize our column list for the
+			 * CORRESPONDING BY clause.
+			 */
+
+			matchingColumnsFiltered = NIL;
+
+			/* For each column in CORRESPONDING BY column list, check
+			 * column existence in matchingColumns.
+			 */
+			foreach(corrtl, stmt->correspondingClause)
+			{
+				Node* corrtle = lfirst(corrtl);
+
+				if (IsA(corrtle, ColumnRef)
+						&& list_length(((ColumnRef *) corrtle)->fields)
+								== 1&&
+								IsA(linitial(((ColumnRef *) corrtle)->fields), String))
+				{
+					char *name =
+							strVal(linitial(((ColumnRef *) corrtle)->fields));
+
+					foreach(mctl, matchingColumns)
+					{
+						TargetEntry *mctle = (TargetEntry *) lfirst(mctl);
+
+						Assert(mctle->resname != NULL);
+						Assert(name != NULL);
+
+						if (strcmp(mctle->resname, name) == 0)
+						{
+							/* we have a match.*/
+							matchingColumnsFiltered = lappend(
+									matchingColumnsFiltered, mctle);
+							break;
+						}
+					}
+				}
+			}
+
+			/*
+			 * If matchingColumnsFiltered is empty, there is a semantic error.
+			 * At least one column in the select lists must have the same name.
+			 */
+			if (list_length(matchingColumnsFiltered) == 0)
+			{
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR), errmsg("%s queries with CORRESPONDING BY clause must have at least one column name in BY clause and in both of the queries.", context)));
+			}
+			/*
+			 * there may be out-of-order resnos in corresponding target list
+			 */
+			op->correspondingColumns = orderCorrespondingLsit(matchingColumnsFiltered);
+
+			/*
+			 * matchingColumns contain target list as it appear in left query targetList
+			 * we need matching column as it appear in right query targetlist inorder to
+			 * make output column type for corresponding columns
+			 */
+			rightCorrespondingColumns = determineMatchingColumns(rargQuery->targetList,matchingColumnsFiltered);
+			/*
+			 * make union'd datatype of output columns
+			 */
+			makeUnionDatatype(matchingColumnsFiltered, rightCorrespondingColumns, op, targetlist, pstate,
+					context);
+		}
 		/*
 		 * Recursively transform the left child node.
 		 */
@@ -1971,165 +2178,206 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
 						context),
 					 parser_errposition(pstate,
 										exprLocation((Node *) rtargetlist))));
+		if (op->correspondingColumns == NIL)
+			makeUnionDatatype(ltargetlist, rtargetlist, op, targetlist, pstate,
+					context);
 
-		if (targetlist)
-			*targetlist = NIL;
-		op->colTypes = NIL;
-		op->colTypmods = NIL;
-		op->colCollations = NIL;
-		op->groupClauses = NIL;
-		forboth(ltl, ltargetlist, rtl, rtargetlist)
+		return (Node *) op;
+	}
+}
+
+/*
+ * Processes targetlists of two queries for column equivalence to use
+ * with UNION/INTERSECT/EXCEPT CORRESPONDING.
+ */
+static List *
+determineMatchingColumns(List *ltargetlist, List *rtargetlist)
+{
+	List *matchingColumns = NIL;
+	ListCell *ltl;
+	ListCell *rtl;
+
+	foreach(ltl, ltargetlist)
+	{
+		foreach(rtl, rtargetlist)
 		{
 			TargetEntry *ltle = (TargetEntry *) lfirst(ltl);
 			TargetEntry *rtle = (TargetEntry *) lfirst(rtl);
-			Node	   *lcolnode = (Node *) ltle->expr;
-			Node	   *rcolnode = (Node *) rtle->expr;
-			Oid			lcoltype = exprType(lcolnode);
-			Oid			rcoltype = exprType(rcolnode);
-			int32		lcoltypmod = exprTypmod(lcolnode);
-			int32		rcoltypmod = exprTypmod(rcolnode);
-			Node	   *bestexpr;
-			int			bestlocation;
-			Oid			rescoltype;
-			int32		rescoltypmod;
-			Oid			rescolcoll;
-
-			/* select common type, same as CASE et al */
-			rescoltype = select_common_type(pstate,
-											list_make2(lcolnode, rcolnode),
-											context,
-											&bestexpr);
-			bestlocation = exprLocation(bestexpr);
-			/* if same type and same typmod, use typmod; else default */
-			if (lcoltype == rcoltype && lcoltypmod == rcoltypmod)
-				rescoltypmod = lcoltypmod;
-			else
-				rescoltypmod = -1;
-
-			/*
-			 * Verify the coercions are actually possible.  If not, we'd fail
-			 * later anyway, but we want to fail now while we have sufficient
-			 * context to produce an error cursor position.
-			 *
-			 * For all non-UNKNOWN-type cases, we verify coercibility but we
-			 * don't modify the child's expression, for fear of changing the
-			 * child query's semantics.
-			 *
-			 * If a child expression is an UNKNOWN-type Const or Param, we
-			 * want to replace it with the coerced expression.  This can only
-			 * happen when the child is a leaf set-op node.  It's safe to
-			 * replace the expression because if the child query's semantics
-			 * depended on the type of this output column, it'd have already
-			 * coerced the UNKNOWN to something else.  We want to do this
-			 * because (a) we want to verify that a Const is valid for the
-			 * target type, or resolve the actual type of an UNKNOWN Param,
-			 * and (b) we want to avoid unnecessary discrepancies between the
-			 * output type of the child query and the resolved target type.
-			 * Such a discrepancy would disable optimization in the planner.
-			 *
-			 * If it's some other UNKNOWN-type node, eg a Var, we do nothing
-			 * (knowing that coerce_to_common_type would fail).  The planner
-			 * is sometimes able to fold an UNKNOWN Var to a constant before
-			 * it has to coerce the type, so failing now would just break
-			 * cases that might work.
-			 */
-			if (lcoltype != UNKNOWNOID)
-				lcolnode = coerce_to_common_type(pstate, lcolnode,
-												 rescoltype, context);
-			else if (IsA(lcolnode, Const) ||
-					 IsA(lcolnode, Param))
-			{
-				lcolnode = coerce_to_common_type(pstate, lcolnode,
-												 rescoltype, context);
-				ltle->expr = (Expr *) lcolnode;
-			}
 
-			if (rcoltype != UNKNOWNOID)
-				rcolnode = coerce_to_common_type(pstate, rcolnode,
-												 rescoltype, context);
-			else if (IsA(rcolnode, Const) ||
-					 IsA(rcolnode, Param))
-			{
-				rcolnode = coerce_to_common_type(pstate, rcolnode,
-												 rescoltype, context);
-				rtle->expr = (Expr *) rcolnode;
-			}
+			elog(DEBUG4, "%s", ltle->resname);
 
-			/*
-			 * Select common collation.  A common collation is required for
-			 * all set operators except UNION ALL; see SQL:2008 7.13 <query
-			 * expression> Syntax Rule 15c.  (If we fail to identify a common
-			 * collation for a UNION ALL column, the curCollations element
-			 * will be set to InvalidOid, which may result in a runtime error
-			 * if something at a higher query level wants to use the column's
-			 * collation.)
-			 */
-			rescolcoll = select_common_collation(pstate,
-											  list_make2(lcolnode, rcolnode),
-										 (op->op == SETOP_UNION && op->all));
-
-			/* emit results */
-			op->colTypes = lappend_oid(op->colTypes, rescoltype);
-			op->colTypmods = lappend_int(op->colTypmods, rescoltypmod);
-			op->colCollations = lappend_oid(op->colCollations, rescolcoll);
-
-			/*
-			 * For all cases except UNION ALL, identify the grouping operators
-			 * (and, if available, sorting operators) that will be used to
-			 * eliminate duplicates.
-			 */
-			if (op->op != SETOP_UNION || !op->all)
-			{
-				SortGroupClause *grpcl = makeNode(SortGroupClause);
-				Oid			sortop;
-				Oid			eqop;
-				bool		hashable;
-				ParseCallbackState pcbstate;
-
-				setup_parser_errposition_callback(&pcbstate, pstate,
-												  bestlocation);
-
-				/* determine the eqop and optional sortop */
-				get_sort_group_operators(rescoltype,
-										 false, true, false,
-										 &sortop, &eqop, NULL,
-										 &hashable);
-
-				cancel_parser_errposition_callback(&pcbstate);
-
-				/* we don't have a tlist yet, so can't assign sortgrouprefs */
-				grpcl->tleSortGroupRef = 0;
-				grpcl->eqop = eqop;
-				grpcl->sortop = sortop;
-				grpcl->nulls_first = false;		/* OK with or without sortop */
-				grpcl->hashable = hashable;
-
-				op->groupClauses = lappend(op->groupClauses, grpcl);
-			}
+			/* Names of the columns must be resolved before calling this method. */
+			Assert(ltle->resname != NULL);
+			Assert(rtle->resname != NULL);
 
-			/*
-			 * Construct a dummy tlist entry to return.  We use a SetToDefault
-			 * node for the expression, since it carries exactly the fields
-			 * needed, but any other expression node type would do as well.
-			 */
-			if (targetlist)
+			/* If column names are the same, append it to the result. */
+			if (strcmp(ltle->resname, rtle->resname) == 0)
 			{
-				SetToDefault *rescolnode = makeNode(SetToDefault);
-				TargetEntry *restle;
-
-				rescolnode->typeId = rescoltype;
-				rescolnode->typeMod = rescoltypmod;
-				rescolnode->collation = rescolcoll;
-				rescolnode->location = bestlocation;
-				restle = makeTargetEntry((Expr *) rescolnode,
-										 0,		/* no need to set resno */
-										 NULL,
-										 false);
-				*targetlist = lappend(*targetlist, restle);
+				matchingColumns = lappend(matchingColumns, ltle);
+				continue;
 			}
 		}
+	}
+
+	return matchingColumns;
+}
 
-		return (Node *) op;
+/*
+ * process right and left target list to set up union'd datatype
+ */
+static void *
+makeUnionDatatype(List *ltargetlist, List *rtargetlist, SetOperationStmt *op,
+		List **targetlist, ParseState *pstate, const char *context)
+{
+	ListCell *ltl;
+	ListCell *rtl;
+	if (targetlist)
+		*targetlist = NIL;
+	op->colTypes = NIL;
+	op->colTypmods = NIL;
+	op->colCollations = NIL;
+	op->groupClauses = NIL;
+
+	forboth(ltl, ltargetlist, rtl, rtargetlist)
+	{
+		TargetEntry *ltle = (TargetEntry *) lfirst(ltl);
+		TargetEntry *rtle = (TargetEntry *) lfirst(rtl);
+		Node *lcolnode = (Node *) ltle->expr;
+		Node *rcolnode = (Node *) rtle->expr;
+		Oid lcoltype = exprType(lcolnode);
+		Oid rcoltype = exprType(rcolnode);
+		int32 lcoltypmod = exprTypmod(lcolnode);
+		int32 rcoltypmod = exprTypmod(rcolnode);
+		Node *bestexpr;
+		int bestlocation;
+		Oid rescoltype;
+		int32 rescoltypmod;
+		Oid rescolcoll;
+
+		/* select common type, same as CASE et al */
+		rescoltype = select_common_type(pstate, list_make2(lcolnode, rcolnode),
+				context, &bestexpr);
+		bestlocation = exprLocation(bestexpr);
+		/* if same type and same typmod, use typmod; else default */
+		if (lcoltype == rcoltype && lcoltypmod == rcoltypmod)
+			rescoltypmod = lcoltypmod;
+		else
+			rescoltypmod = -1;
+
+		/*
+		 * Verify the coercions are actually possible.  If not, we'd fail
+		 * later anyway, but we want to fail now while we have sufficient
+		 * context to produce an error cursor position.
+		 *
+		 * For all non-UNKNOWN-type cases, we verify coercibility but we
+		 * don't modify the child's expression, for fear of changing the
+		 * child query's semantics.
+		 *
+		 * If a child expression is an UNKNOWN-type Const or Param, we
+		 * want to replace it with the coerced expression.  This can only
+		 * happen when the child is a leaf set-op node.  It's safe to
+		 * replace the expression because if the child query's semantics
+		 * depended on the type of this output column, it'd have already
+		 * coerced the UNKNOWN to something else.  We want to do this
+		 * because (a) we want to verify that a Const is valid for the
+		 * target type, or resolve the actual type of an UNKNOWN Param,
+		 * and (b) we want to avoid unnecessary discrepancies between the
+		 * output type of the child query and the resolved target type.
+		 * Such a discrepancy would disable optimization in the planner.
+		 *
+		 * If it's some other UNKNOWN-type node, eg a Var, we do nothing
+		 * (knowing that coerce_to_common_type would fail).  The planner
+		 * is sometimes able to fold an UNKNOWN Var to a constant before
+		 * it has to coerce the type, so failing now would just break
+		 * cases that might work.
+		 */
+		if (lcoltype != UNKNOWNOID)
+			lcolnode = coerce_to_common_type(pstate, lcolnode, rescoltype,
+					context);
+		else if (IsA(lcolnode, Const) || IsA(lcolnode, Param))
+		{
+			lcolnode = coerce_to_common_type(pstate, lcolnode, rescoltype,
+					context);
+			ltle->expr = (Expr *) lcolnode;
+		}
+
+		if (rcoltype != UNKNOWNOID)
+			rcolnode = coerce_to_common_type(pstate, rcolnode, rescoltype,
+					context);
+		else if (IsA(rcolnode, Const) || IsA(rcolnode, Param))
+		{
+			rcolnode = coerce_to_common_type(pstate, rcolnode, rescoltype,
+					context);
+			rtle->expr = (Expr *) rcolnode;
+		}
+
+		/*
+		 * Select common collation.  A common collation is required for
+		 * all set operators except UNION ALL; see SQL:2008 7.13 <query
+		 * expression> Syntax Rule 15c.  (If we fail to identify a common
+		 * collation for a UNION ALL column, the curCollations element
+		 * will be set to InvalidOid, which may result in a runtime error
+		 * if something at a higher query level wants to use the column's
+		 * collation.)
+		 */
+		rescolcoll = select_common_collation(pstate,
+				list_make2(lcolnode, rcolnode),
+				(op->op == SETOP_UNION && op->all));
+
+		/* emit results */
+		op->colTypes = lappend_oid(op->colTypes, rescoltype);
+		op->colTypmods = lappend_int(op->colTypmods, rescoltypmod);
+		op->colCollations = lappend_oid(op->colCollations, rescolcoll);
+
+		/*
+		 * For all cases except UNION ALL, identify the grouping operators
+		 * (and, if available, sorting operators) that will be used to
+		 * eliminate duplicates.
+		 */
+		if (op->op != SETOP_UNION || !op->all)
+		{
+			SortGroupClause *grpcl = makeNode(SortGroupClause);
+			Oid sortop;
+			Oid eqop;
+			bool hashable;
+			ParseCallbackState pcbstate;
+
+			setup_parser_errposition_callback(&pcbstate, pstate, bestlocation);
+
+			/* determine the eqop and optional sortop */
+			get_sort_group_operators(rescoltype, false, true, false, &sortop,
+					&eqop, NULL, &hashable);
+
+			cancel_parser_errposition_callback(&pcbstate);
+
+			/* we don't have a tlist yet, so can't assign sortgrouprefs */
+			grpcl->tleSortGroupRef = 0;
+			grpcl->eqop = eqop;
+			grpcl->sortop = sortop;
+			grpcl->nulls_first = false; /* OK with or without sortop */
+			grpcl->hashable = hashable;
+
+			op->groupClauses = lappend(op->groupClauses, grpcl);
+		}
+
+		/*
+		 * Construct a dummy tlist entry to return.  We use a SetToDefault
+		 * node for the expression, since it carries exactly the fields
+		 * needed, but any other expression node type would do as well.
+		 */
+		if (targetlist)
+		{
+			SetToDefault *rescolnode = makeNode(SetToDefault);
+			TargetEntry *restle;
+
+			rescolnode->typeId = rescoltype;
+			rescolnode->typeMod = rescoltypmod;
+			rescolnode->collation = rescolcoll;
+			rescolnode->location = bestlocation;
+			restle = makeTargetEntry((Expr *) rescolnode, 0, /* no need to set resno */
+			NULL, false );
+			*targetlist = lappend(*targetlist, restle);
+		}
 	}
 }
 
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 174773b..22aa911 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -166,7 +166,7 @@ static void insertSelectOptions(SelectStmt *stmt,
 								Node *limitOffset, Node *limitCount,
 								WithClause *withClause,
 								core_yyscan_t yyscanner);
-static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg);
+static Node *makeSetOp(SetOperation op, bool all, List *correspondingClause, Node *larg, Node *rarg);
 static Node *doNegate(Node *n, int location);
 static void doNegateFloat(Value *v);
 static Node *makeAndExpr(Node *lexpr, Node *rexpr, int location);
@@ -394,7 +394,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 				relation_expr_list dostmt_opt_list
 				transform_element_list transform_type_list
 				TriggerTransitions TriggerReferencing
-				publication_name_list
+				publication_name_list opt_corresponding_clause
 
 %type <list>	group_by_list
 %type <node>	group_by_item empty_grouping_set rollup_clause cube_clause
@@ -608,7 +608,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
 	CLUSTER COALESCE COLLATE COLLATION COLUMN COMMENT COMMENTS COMMIT
 	COMMITTED CONCURRENTLY CONFIGURATION CONFLICT CONNECTION CONSTRAINT
-	CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE
+	CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY CORRESPONDING COST CREATE
 	CROSS CSV CUBE CURRENT_P
 	CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA
 	CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
@@ -10808,20 +10808,26 @@ simple_select:
 					n->fromClause = list_make1($2);
 					$$ = (Node *)n;
 				}
-			| select_clause UNION all_or_distinct select_clause
+			| select_clause UNION all_or_distinct opt_corresponding_clause select_clause
 				{
-					$$ = makeSetOp(SETOP_UNION, $3, $1, $4);
+					$$ = makeSetOp(SETOP_UNION, $3, $4, $1, $5);
 				}
-			| select_clause INTERSECT all_or_distinct select_clause
+			| select_clause INTERSECT all_or_distinct opt_corresponding_clause select_clause
 				{
-					$$ = makeSetOp(SETOP_INTERSECT, $3, $1, $4);
+					$$ = makeSetOp(SETOP_INTERSECT, $3, $4, $1, $5);
 				}
-			| select_clause EXCEPT all_or_distinct select_clause
+			| select_clause EXCEPT all_or_distinct opt_corresponding_clause select_clause
 				{
-					$$ = makeSetOp(SETOP_EXCEPT, $3, $1, $4);
+					$$ = makeSetOp(SETOP_EXCEPT, $3, $4, $1, $5);
 				}
 		;
 
+opt_corresponding_clause:
+			CORRESPONDING BY '(' expr_list ')'		{ $$ = $4; }
+			| CORRESPONDING							{ $$ = list_make1(NIL); }
+			| /*EMPTY*/								{ $$ = NIL; }
+			;
+
 /*
  * SQL standard WITH clause looks like:
  *
@@ -14315,6 +14321,7 @@ unreserved_keyword:
 			| CONTINUE_P
 			| CONVERSION_P
 			| COPY
+			| CORRESPONDING
 			| COST
 			| CSV
 			| CUBE
@@ -15145,7 +15152,7 @@ insertSelectOptions(SelectStmt *stmt,
 }
 
 static Node *
-makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg)
+makeSetOp(SetOperation op, bool all, List *correspondingClause, Node *larg, Node *rarg)
 {
 	SelectStmt *n = makeNode(SelectStmt);
 
@@ -15153,6 +15160,7 @@ makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg)
 	n->all = all;
 	n->larg = (SelectStmt *) larg;
 	n->rarg = (SelectStmt *) rarg;
+	n->correspondingClause = correspondingClause;
 	return (Node *) n;
 }
 
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 2576e31..d45a88e 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -1903,3 +1903,26 @@ FigureColnameInternal(Node *node, char **name)
 
 	return strength;
 }
+
+/*
+ * changeCorrespondingList()
+ * order target list resno .
+ */
+List *
+orderCorrespondingLsit(List *targetlist)
+{
+	List *p_target = NIL;
+	ListCell *o_target;
+	int pos = 1;
+	int loc = 1;
+
+	foreach(o_target, targetlist)
+	{
+		TargetEntry *tar = (TargetEntry *) lfirst(o_target);
+
+		p_target = lappend(p_target,
+				changeTargetEntry(tar, (AttrNumber) pos++, (AttrNumber) loc++));
+	}
+
+	return p_target;
+}
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index 8feec0b..ac23b1b 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -736,7 +736,8 @@ typeStringToTypeName(const char *str)
 		stmt->limitCount != NULL ||
 		stmt->lockingClause != NIL ||
 		stmt->withClause != NULL ||
-		stmt->op != SETOP_NONE)
+		stmt->op != SETOP_NONE ||
+		stmt->correspondingClause != NULL)
 		goto fail;
 	if (list_length(stmt->targetList) != 1)
 		goto fail;
diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h
index 1f4bad7..777c3e2 100644
--- a/src/include/nodes/makefuncs.h
+++ b/src/include/nodes/makefuncs.h
@@ -86,4 +86,6 @@ extern DefElem *makeDefElemExtended(char *nameSpace, char *name, Node *arg,
 
 extern GroupingSet *makeGroupingSet(GroupingSetKind kind, List *content, int location);
 
+extern TargetEntry *changeTargetEntry(TargetEntry *target, AttrNumber resno, AttrNumber loc);
+
 #endif   /* MAKEFUNC_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 07a8436..1dfff8f 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1420,7 +1420,9 @@ typedef struct SelectStmt
 	 * These fields are used only in "leaf" SelectStmts.
 	 */
 	List	   *distinctClause; /* NULL, list of DISTINCT ON exprs, or
-								 * lcons(NIL,NIL) for all (SELECT DISTINCT) */
+								* lcons(NIL,NIL) for all (SELECT DISTINCT) */
+	List	   *correspondingClause; /* NULL, list of CORRESPONDING BY exprs, or */
+								/* lcons(NIL, NIL) for CORRESPONDING */
 	IntoClause *intoClause;		/* target for SELECT INTO */
 	List	   *targetList;		/* the target list (of ResTarget) */
 	List	   *fromClause;		/* the FROM clause */
@@ -1486,8 +1488,9 @@ typedef struct SetOperationStmt
 	bool		all;			/* ALL specified? */
 	Node	   *larg;			/* left child */
 	Node	   *rarg;			/* right child */
-	/* Eventually add fields for CORRESPONDING spec here */
-
+	/* CORRESPONDING clause fields */
+	List	   *correspondingColumns;	/* NIL: No corresponding, else: CORRESPONDING or CORRESPONDIN
+						* BY matching columns. Not the original clause. */
 	/* Fields derived during parse analysis: */
 	List	   *colTypes;		/* OID list of output column type OIDs */
 	List	   *colTypmods;		/* integer list of output column typmods */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 985d650..77e0396 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -96,6 +96,7 @@ PG_KEYWORD("content", CONTENT_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("continue", CONTINUE_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("conversion", CONVERSION_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("copy", COPY, UNRESERVED_KEYWORD)
+PG_KEYWORD("corresponding", CORRESPONDING, UNRESERVED_KEYWORD)
 PG_KEYWORD("cost", COST, UNRESERVED_KEYWORD)
 PG_KEYWORD("create", CREATE, RESERVED_KEYWORD)
 PG_KEYWORD("cross", CROSS, TYPE_FUNC_NAME_KEYWORD)
diff --git a/src/include/parser/parse_target.h b/src/include/parser/parse_target.h
index d06a235..a4cfe31 100644
--- a/src/include/parser/parse_target.h
+++ b/src/include/parser/parse_target.h
@@ -42,5 +42,6 @@ extern TupleDesc expandRecordVariable(ParseState *pstate, Var *var,
 					 int levelsup);
 extern char *FigureColname(Node *node);
 extern char *FigureIndexColname(Node *node);
+extern List *orderCorrespondingLsit(List *targetlist);
 
 #endif   /* PARSE_TARGET_H */
diff --git a/src/test/regress/expected/union.out b/src/test/regress/expected/union.out
index 4d697ba..3d60b23 100644
--- a/src/test/regress/expected/union.out
+++ b/src/test/regress/expected/union.out
@@ -59,6 +59,198 @@ SELECT 1.1 AS two UNION SELECT 2.2 ORDER BY 1;
  2.2
 (2 rows)
 
+SELECT 1 AS two UNION CORRESPONDING SELECT 2 two;
+ two 
+-----
+   1
+   2
+(2 rows)
+
+SELECT 1 AS one UNION CORRESPONDING SELECT 1 one;
+ one 
+-----
+   1
+(1 row)
+
+SELECT 1 AS two UNION ALL CORRESPONDING SELECT 2 two;
+ two 
+-----
+   1
+   2
+(2 rows)
+
+SELECT 1 AS two UNION ALL CORRESPONDING SELECT 1 two;
+ two 
+-----
+   1
+   1
+(2 rows)
+
+SELECT 1 AS two UNION CORRESPONDING SELECT 2 two UNION CORRESPONDING SELECT 2 two;
+ two 
+-----
+   2
+   1
+(2 rows)
+
+SELECT 1 AS three UNION CORRESPONDING SELECT 2 three UNION ALL CORRESPONDING SELECT 2 three;
+ three 
+-------
+     1
+     2
+     2
+(3 rows)
+
+SELECT 1 AS three UNION CORRESPONDING SELECT 2 three UNION CORRESPONDING SELECT 3 three;
+ three 
+-------
+     3
+     2
+     1
+(3 rows)
+
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING SELECT 4 a, 5 b, 6 c;
+ a | b | c 
+---+---+---
+ 1 | 2 | 3
+ 4 | 5 | 6
+(2 rows)
+
+SELECT 3 c, 2 b, 1 a UNION CORRESPONDING SELECT 6 c, 4 a, 5 b;
+ c | b | a 
+---+---+---
+ 3 | 2 | 1
+ 6 | 5 | 4
+(2 rows)
+
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING BY(a) SELECT 4 a, 5 b, 6 c;
+ a 
+---
+ 1
+ 4
+(2 rows)
+
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING BY(b) SELECT 4 a, 5 b, 6 c;
+ b 
+---
+ 2
+ 5
+(2 rows)
+
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING BY(c) SELECT 4 a, 5 b, 6 c;
+ c 
+---
+ 3
+ 6
+(2 rows)
+
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING BY(a, b) SELECT 4 a, 5 b, 6 c;
+ a | b 
+---+---
+ 1 | 2
+ 4 | 5
+(2 rows)
+
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING BY(b, c) SELECT 4 a, 5 b, 6 c;
+ b | c 
+---+---
+ 2 | 3
+ 5 | 6
+(2 rows)
+
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING BY(a, c) SELECT 4 a, 5 b, 6 c;
+ a | c 
+---+---
+ 1 | 3
+ 4 | 6
+(2 rows)
+
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING BY(a, b, c) SELECT 4 a, 5 b, 6 c;
+ a | b | c 
+---+---+---
+ 1 | 2 | 3
+ 4 | 5 | 6
+(2 rows)
+
+-- CORRESPONDING column ordering, left clause's column ordering must be preserved.
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING SELECT 4 a, 5 b, 6 c;
+ a | b | c 
+---+---+---
+ 1 | 2 | 3
+ 4 | 5 | 6
+(2 rows)
+
+SELECT 2 b, 1 a, 3 c UNION CORRESPONDING SELECT 4 a, 5 b, 6 c;
+ b | a | c 
+---+---+---
+ 2 | 1 | 3
+ 5 | 4 | 6
+(2 rows)
+
+SELECT 1 a, 3 c, 2 b UNION CORRESPONDING SELECT 4 a, 5 b, 6 c;
+ a | c | b 
+---+---+---
+ 1 | 3 | 2
+ 4 | 6 | 5
+(2 rows)
+
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING SELECT 5 b, 6 c, 4 a;
+ a | b | c 
+---+---+---
+ 1 | 2 | 3
+ 4 | 5 | 6
+(2 rows)
+
+SELECT 2 b, 1 a, 3 c UNION CORRESPONDING SELECT 5 b, 6 c, 4 a;
+ b | a | c 
+---+---+---
+ 2 | 1 | 3
+ 5 | 4 | 6
+(2 rows)
+
+-- CORRESPONDING BY column ordering, BY clause column ordering must be preserved.
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING BY(a, b, c) SELECT 4 a, 5 b, 6 c;
+ a | b | c 
+---+---+---
+ 1 | 2 | 3
+ 4 | 5 | 6
+(2 rows)
+
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING BY(b, c, a) SELECT 4 a, 5 b, 6 c;
+ b | c | a 
+---+---+---
+ 2 | 3 | 1
+ 5 | 6 | 4
+(2 rows)
+
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING BY(c, a, b) SELECT 4 a, 5 b, 6 c;
+ c | a | b 
+---+---+---
+ 3 | 1 | 2
+ 6 | 4 | 5
+(2 rows)
+
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING BY(a, b, c) SELECT 5 b, 6 c, 4 a;
+ a | b | c 
+---+---+---
+ 1 | 2 | 3
+ 4 | 5 | 6
+(2 rows)
+
+SELECT 2 b, 3 c, 1 a UNION CORRESPONDING BY(b, c, a) SELECT 4 a, 5 b, 6 c;
+ b | c | a 
+---+---+---
+ 2 | 3 | 1
+ 5 | 6 | 4
+(2 rows)
+
+SELECT 3 c, 2 b, 1 a UNION CORRESPONDING BY(c, a, b) SELECT 4 a, 5 b, 6 c;
+ c | a | b 
+---+---+---
+ 3 | 1 | 2
+ 6 | 4 | 5
+(2 rows)
+
 -- Mixed types
 SELECT 1.1 AS two UNION SELECT 2 ORDER BY 1;
  two 
@@ -258,6 +450,97 @@ ORDER BY 1;
  hi de ho neighbor
 (5 rows)
 
+SELECT f1 AS five FROM FLOAT8_TBL
+UNION CORRESPONDING
+SELECT f1 AS five FROM FLOAT8_TBL
+ORDER BY 1;
+         five          
+-----------------------
+ -1.2345678901234e+200
+               -1004.3
+                -34.84
+ -1.2345678901234e-200
+                     0
+(5 rows)
+
+SELECT f1 AS five FROM FLOAT8_TBL
+UNION CORRESPONDING BY(five)
+SELECT f1 AS five FROM FLOAT8_TBL
+ORDER BY 1;
+         five          
+-----------------------
+ -1.2345678901234e+200
+               -1004.3
+                -34.84
+ -1.2345678901234e-200
+                     0
+(5 rows)
+
+SELECT f1 AS ten FROM FLOAT8_TBL
+UNION ALL CORRESPONDING
+SELECT f1 AS ten FROM FLOAT8_TBL;
+          ten          
+-----------------------
+                     0
+                -34.84
+               -1004.3
+ -1.2345678901234e+200
+ -1.2345678901234e-200
+                     0
+                -34.84
+               -1004.3
+ -1.2345678901234e+200
+ -1.2345678901234e-200
+(10 rows)
+
+SELECT f1 AS nine FROM FLOAT8_TBL
+UNION CORRESPONDING
+SELECT f1 AS nine FROM INT4_TBL
+ORDER BY 1;
+         nine          
+-----------------------
+ -1.2345678901234e+200
+           -2147483647
+               -123456
+               -1004.3
+                -34.84
+ -1.2345678901234e-200
+                     0
+                123456
+            2147483647
+(9 rows)
+
+SELECT f1 AS ten FROM FLOAT8_TBL
+UNION ALL CORRESPONDING
+SELECT f1 AS ten FROM INT4_TBL;
+          ten          
+-----------------------
+                     0
+                -34.84
+               -1004.3
+ -1.2345678901234e+200
+ -1.2345678901234e-200
+                     0
+                123456
+               -123456
+            2147483647
+           -2147483647
+(10 rows)
+
+SELECT f1 AS five FROM FLOAT8_TBL
+  WHERE f1 BETWEEN -1e6 AND 1e6
+UNION CORRESPONDING
+SELECT f1 AS five FROM INT4_TBL
+  WHERE f1 BETWEEN 0 AND 1000000;
+         five          
+-----------------------
+               -1004.3
+                -34.84
+ -1.2345678901234e-200
+                     0
+                123456
+(5 rows)
+
 --
 -- INTERSECT and EXCEPT
 --
@@ -320,6 +603,63 @@ SELECT q1 FROM int8_tbl EXCEPT ALL SELECT DISTINCT q2 FROM int8_tbl ORDER BY 1;
 
 SELECT q1 FROM int8_tbl EXCEPT ALL SELECT q1 FROM int8_tbl FOR NO KEY UPDATE;
 ERROR:  FOR NO KEY UPDATE is not allowed with UNION/INTERSECT/EXCEPT
+SELECT q2 FROM int8_tbl INTERSECT CORRESPONDING SELECT q1 AS q2 FROM int8_tbl;
+        q2        
+------------------
+              123
+ 4567890123456789
+(2 rows)
+
+SELECT q2 FROM int8_tbl INTERSECT ALL CORRESPONDING SELECT q1 AS q2 FROM int8_tbl;
+        q2        
+------------------
+              123
+ 4567890123456789
+ 4567890123456789
+(3 rows)
+
+SELECT q2 FROM int8_tbl EXCEPT CORRESPONDING SELECT q1 AS q2 FROM int8_tbl ORDER BY 1;
+        q2         
+-------------------
+ -4567890123456789
+               456
+(2 rows)
+
+SELECT q2 FROM int8_tbl EXCEPT ALL CORRESPONDING SELECT q1 AS q2 FROM int8_tbl ORDER BY 1;
+        q2         
+-------------------
+ -4567890123456789
+               456
+(2 rows)
+
+SELECT q2 FROM int8_tbl EXCEPT ALL CORRESPONDING SELECT DISTINCT q1 AS q2 FROM int8_tbl ORDER BY 1;
+        q2         
+-------------------
+ -4567890123456789
+               456
+  4567890123456789
+(3 rows)
+
+SELECT q1 FROM int8_tbl EXCEPT CORRESPONDING SELECT q2 AS q1 FROM int8_tbl;
+ q1 
+----
+(0 rows)
+
+SELECT q1 FROM int8_tbl EXCEPT ALL CORRESPONDING SELECT q2 AS q1 FROM int8_tbl;
+        q1        
+------------------
+              123
+ 4567890123456789
+(2 rows)
+
+SELECT q1 FROM int8_tbl EXCEPT ALL CORRESPONDING SELECT DISTINCT q2 AS q1 FROM int8_tbl;
+        q1        
+------------------
+              123
+ 4567890123456789
+ 4567890123456789
+(3 rows)
+
 --
 -- Mixed types
 --
@@ -338,6 +678,21 @@ SELECT f1 FROM float8_tbl EXCEPT SELECT f1 FROM int4_tbl ORDER BY 1;
  -1.2345678901234e-200
 (4 rows)
 
+SELECT f1 FROM float8_tbl INTERSECT CORRESPONDING SELECT f1 FROM int4_tbl;
+ f1 
+----
+  0
+(1 row)
+
+SELECT f1 FROM float8_tbl EXCEPT CORRESPONDING SELECT f1 FROM int4_tbl ORDER BY 1;
+          f1           
+-----------------------
+ -1.2345678901234e+200
+               -1004.3
+                -34.84
+ -1.2345678901234e-200
+(4 rows)
+
 --
 -- Operator precedence and (((((extra))))) parentheses
 --
@@ -424,6 +779,24 @@ SELECT q1 FROM int8_tbl EXCEPT (((SELECT q2 FROM int8_tbl ORDER BY q2 LIMIT 1)))
  4567890123456789
 (2 rows)
 
+SELECT q1,q2 FROM int8_tbl EXCEPT CORRESPONDING SELECT q2,q1 FROM int8_tbl
+ORDER BY q2,q1;
+ q1 | q2 
+----+----
+(0 rows)
+
+SELECT q1,q2 FROM int8_tbl EXCEPT CORRESPONDING BY(q1) SELECT q2,q1 FROM int8_tbl
+ORDER BY q1;
+ q1 
+----
+(0 rows)
+
+SELECT q1,q2 FROM int8_tbl EXCEPT CORRESPONDING BY(q2) SELECT q2,q1 FROM int8_tbl
+ORDER BY q2;
+ q2 
+----
+(0 rows)
+
 --
 -- New syntaxes (7.1) permit new tests
 --
@@ -459,6 +832,22 @@ SELECT '3.4'::numeric UNION SELECT 'foo';
 ERROR:  invalid input syntax for type numeric: "foo"
 LINE 1: SELECT '3.4'::numeric UNION SELECT 'foo';
                                            ^
+SELECT '3.4'::numeric AS a UNION CORRESPONDING SELECT 'foo' AS a;
+ERROR:  invalid input syntax for type numeric: "foo"
+LINE 1: ...CT '3.4'::numeric AS a UNION CORRESPONDING SELECT 'foo' AS a...
+                                                             ^
+SELECT a.f1 FROM (SELECT 'test' AS f1 FROM varchar_tbl) a
+UNION CORRESPONDING
+SELECT b.f1 FROM (SELECT f1 FROM varchar_tbl) b
+ORDER BY 1;
+  f1  
+------
+ a
+ ab
+ abcd
+ test
+(4 rows)
+
 --
 -- Test that expression-index constraints can be pushed down through
 -- UNION or UNION ALL
diff --git a/src/test/regress/sql/union.sql b/src/test/regress/sql/union.sql
index 48e6850..bae0df2 100644
--- a/src/test/regress/sql/union.sql
+++ b/src/test/regress/sql/union.sql
@@ -20,6 +20,64 @@ SELECT 1 AS three UNION SELECT 2 UNION ALL SELECT 2 ORDER BY 1;
 
 SELECT 1.1 AS two UNION SELECT 2.2 ORDER BY 1;
 
+SELECT 1 AS two UNION CORRESPONDING SELECT 2 two;
+
+SELECT 1 AS one UNION CORRESPONDING SELECT 1 one;
+
+SELECT 1 AS two UNION ALL CORRESPONDING SELECT 2 two;
+
+SELECT 1 AS two UNION ALL CORRESPONDING SELECT 1 two;
+
+SELECT 1 AS two UNION CORRESPONDING SELECT 2 two UNION CORRESPONDING SELECT 2 two;
+
+SELECT 1 AS three UNION CORRESPONDING SELECT 2 three UNION ALL CORRESPONDING SELECT 2 three;
+
+SELECT 1 AS three UNION CORRESPONDING SELECT 2 three UNION CORRESPONDING SELECT 3 three;
+
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING SELECT 4 a, 5 b, 6 c;
+
+SELECT 3 c, 2 b, 1 a UNION CORRESPONDING SELECT 6 c, 4 a, 5 b;
+
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING BY(a) SELECT 4 a, 5 b, 6 c;
+
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING BY(b) SELECT 4 a, 5 b, 6 c;
+
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING BY(c) SELECT 4 a, 5 b, 6 c;
+
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING BY(a, b) SELECT 4 a, 5 b, 6 c;
+
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING BY(b, c) SELECT 4 a, 5 b, 6 c;
+
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING BY(a, c) SELECT 4 a, 5 b, 6 c;
+
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING BY(a, b, c) SELECT 4 a, 5 b, 6 c;
+
+-- CORRESPONDING column ordering, left clause's column ordering must be preserved.
+
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING SELECT 4 a, 5 b, 6 c;
+
+SELECT 2 b, 1 a, 3 c UNION CORRESPONDING SELECT 4 a, 5 b, 6 c;
+
+SELECT 1 a, 3 c, 2 b UNION CORRESPONDING SELECT 4 a, 5 b, 6 c;
+
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING SELECT 5 b, 6 c, 4 a;
+
+SELECT 2 b, 1 a, 3 c UNION CORRESPONDING SELECT 5 b, 6 c, 4 a;
+
+-- CORRESPONDING BY column ordering, BY clause column ordering must be preserved.
+
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING BY(a, b, c) SELECT 4 a, 5 b, 6 c;
+
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING BY(b, c, a) SELECT 4 a, 5 b, 6 c;
+
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING BY(c, a, b) SELECT 4 a, 5 b, 6 c;
+
+SELECT 1 a, 2 b, 3 c UNION CORRESPONDING BY(a, b, c) SELECT 5 b, 6 c, 4 a;
+
+SELECT 2 b, 3 c, 1 a UNION CORRESPONDING BY(b, c, a) SELECT 4 a, 5 b, 6 c;
+
+SELECT 3 c, 2 b, 1 a UNION CORRESPONDING BY(c, a, b) SELECT 4 a, 5 b, 6 c;
+
 -- Mixed types
 
 SELECT 1.1 AS two UNION SELECT 2 ORDER BY 1;
@@ -90,6 +148,35 @@ UNION
 SELECT TRIM(TRAILING FROM f1) FROM CHAR_TBL
 ORDER BY 1;
 
+SELECT f1 AS five FROM FLOAT8_TBL
+UNION CORRESPONDING
+SELECT f1 AS five FROM FLOAT8_TBL
+ORDER BY 1;
+
+SELECT f1 AS five FROM FLOAT8_TBL
+UNION CORRESPONDING BY(five)
+SELECT f1 AS five FROM FLOAT8_TBL
+ORDER BY 1;
+
+SELECT f1 AS ten FROM FLOAT8_TBL
+UNION ALL CORRESPONDING
+SELECT f1 AS ten FROM FLOAT8_TBL;
+
+SELECT f1 AS nine FROM FLOAT8_TBL
+UNION CORRESPONDING
+SELECT f1 AS nine FROM INT4_TBL
+ORDER BY 1;
+
+SELECT f1 AS ten FROM FLOAT8_TBL
+UNION ALL CORRESPONDING
+SELECT f1 AS ten FROM INT4_TBL;
+
+SELECT f1 AS five FROM FLOAT8_TBL
+  WHERE f1 BETWEEN -1e6 AND 1e6
+UNION CORRESPONDING
+SELECT f1 AS five FROM INT4_TBL
+  WHERE f1 BETWEEN 0 AND 1000000;
+
 --
 -- INTERSECT and EXCEPT
 --
@@ -112,6 +199,22 @@ SELECT q1 FROM int8_tbl EXCEPT ALL SELECT DISTINCT q2 FROM int8_tbl ORDER BY 1;
 
 SELECT q1 FROM int8_tbl EXCEPT ALL SELECT q1 FROM int8_tbl FOR NO KEY UPDATE;
 
+SELECT q2 FROM int8_tbl INTERSECT CORRESPONDING SELECT q1 AS q2 FROM int8_tbl;
+
+SELECT q2 FROM int8_tbl INTERSECT ALL CORRESPONDING SELECT q1 AS q2 FROM int8_tbl;
+
+SELECT q2 FROM int8_tbl EXCEPT CORRESPONDING SELECT q1 AS q2 FROM int8_tbl ORDER BY 1;
+
+SELECT q2 FROM int8_tbl EXCEPT ALL CORRESPONDING SELECT q1 AS q2 FROM int8_tbl ORDER BY 1;
+
+SELECT q2 FROM int8_tbl EXCEPT ALL CORRESPONDING SELECT DISTINCT q1 AS q2 FROM int8_tbl ORDER BY 1;
+
+SELECT q1 FROM int8_tbl EXCEPT CORRESPONDING SELECT q2 AS q1 FROM int8_tbl;
+
+SELECT q1 FROM int8_tbl EXCEPT ALL CORRESPONDING SELECT q2 AS q1 FROM int8_tbl;
+
+SELECT q1 FROM int8_tbl EXCEPT ALL CORRESPONDING SELECT DISTINCT q2 AS q1 FROM int8_tbl;
+
 --
 -- Mixed types
 --
@@ -120,6 +223,10 @@ SELECT f1 FROM float8_tbl INTERSECT SELECT f1 FROM int4_tbl ORDER BY 1;
 
 SELECT f1 FROM float8_tbl EXCEPT SELECT f1 FROM int4_tbl ORDER BY 1;
 
+SELECT f1 FROM float8_tbl INTERSECT CORRESPONDING SELECT f1 FROM int4_tbl;
+
+SELECT f1 FROM float8_tbl EXCEPT CORRESPONDING SELECT f1 FROM int4_tbl ORDER BY 1;
+
 --
 -- Operator precedence and (((((extra))))) parentheses
 --
@@ -150,6 +257,15 @@ SELECT q1 FROM int8_tbl EXCEPT SELECT q2 FROM int8_tbl ORDER BY q2 LIMIT 1;
 -- But this should work:
 SELECT q1 FROM int8_tbl EXCEPT (((SELECT q2 FROM int8_tbl ORDER BY q2 LIMIT 1))) ORDER BY 1;
 
+SELECT q1,q2 FROM int8_tbl EXCEPT CORRESPONDING SELECT q2,q1 FROM int8_tbl
+ORDER BY q2,q1;
+
+SELECT q1,q2 FROM int8_tbl EXCEPT CORRESPONDING BY(q1) SELECT q2,q1 FROM int8_tbl
+ORDER BY q1;
+
+SELECT q1,q2 FROM int8_tbl EXCEPT CORRESPONDING BY(q2) SELECT q2,q1 FROM int8_tbl
+ORDER BY q2;
+
 --
 -- New syntaxes (7.1) permit new tests
 --
@@ -170,6 +286,13 @@ ORDER BY 1;
 -- This should fail, but it should produce an error cursor
 SELECT '3.4'::numeric UNION SELECT 'foo';
 
+SELECT '3.4'::numeric AS a UNION CORRESPONDING SELECT 'foo' AS a;
+
+SELECT a.f1 FROM (SELECT 'test' AS f1 FROM varchar_tbl) a
+UNION CORRESPONDING
+SELECT b.f1 FROM (SELECT f1 FROM varchar_tbl) b
+ORDER BY 1;
+
 --
 -- Test that expression-index constraints can be pushed down through
 -- UNION or UNION ALL
