merge.diff

text/plain

Filename: merge.diff
Type: text/plain
Part: 0
Message: Re: gSoC - ADD MERGE COMMAND - code patch submission

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
File+
src/backend/nodes/copyfuncs.c 39 1
src/backend/nodes/equalfuncs.c 2 1
src/backend/nodes/outfuncs.c 51 0
src/backend/parser/analyze.c 365 16
src/backend/parser/parse_clause.c 29 0
src/backend/parser/parse_relation.c 3 3
src/backend/parser/parse_target.c 1 1
src/backend/parser/parse_type.c 1 1
src/backend/rewrite/rewriteHandler.c 51 1
src/backend/tcop/utility.c 7 2
src/include/nodes/nodes.h 11 1
src/include/nodes/parsenodes.h 33 0
src/include/parser/parse_clause.h 3 0
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index e770e89..aad914c 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2231,6 +2231,7 @@ _copyQuery(Query *from)
 	COPY_SCALAR_FIELD(querySource);
 	COPY_SCALAR_FIELD(canSetTag);
 	COPY_NODE_FIELD(utilityStmt);
+	COPY_NODE_FIELD(mergeActQry); //merge actions 
 	COPY_SCALAR_FIELD(resultRelation);
 	COPY_NODE_FIELD(intoClause);
 	COPY_SCALAR_FIELD(hasAggs);
@@ -2324,6 +2325,36 @@ _copySelectStmt(SelectStmt *from)
 	return newnode;
 }
 
+
+static MergeStmt *
+_copyMergeStmt(MergeStmt *from)
+{
+	MergeStmt *newnode = makeNode(MergeStmt);
+
+	COPY_NODE_FIELD(relation);
+	COPY_NODE_FIELD(source);
+	COPY_NODE_FIELD(matchCondition);
+	COPY_NODE_FIELD(actions);
+	
+	return newnode;
+	
+}
+
+
+static MergeConditionAction *
+_copyMergeConditionAction(MergeConditionAction *from)
+{
+	MergeConditionAction *newnode = makeNode(MergeConditionAction);
+
+	COPY_SCALAR_FIELD(match);
+	COPY_NODE_FIELD(condition);
+	COPY_NODE_FIELD(action);
+
+	return newnode;
+}
+
+
+
 static SetOperationStmt *
 _copySetOperationStmt(SetOperationStmt *from)
 {
@@ -4148,7 +4179,14 @@ copyObject(void *from)
 		case T_AlterTSConfigurationStmt:
 			retval = _copyAlterTSConfigurationStmt(from);
 			break;
+		case T_MergeStmt:
+			retval = _copyMergeStmt(from);
+			break;
 
+			
+		case T_MergeConditionAction:
+			retval = _copyMergeConditionAction(from);
+			break;
 		case T_A_Expr:
 			retval = _copyAExpr(from);
 			break;
@@ -4244,7 +4282,7 @@ copyObject(void *from)
 			break;
 
 		default:
-			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(from));
+			elog(ERROR, "unrecognized node type: %d in copyObject() function", (int) nodeTag(from));
 			retval = from;		/* keep compiler quiet */
 			break;
 	}
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 5d83727..8ab3247 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -855,6 +855,7 @@ _equalQuery(Query *a, Query *b)
 	COMPARE_SCALAR_FIELD(querySource);
 	COMPARE_SCALAR_FIELD(canSetTag);
 	COMPARE_NODE_FIELD(utilityStmt);
+	COMPARE_NODE_FIELD(mergeActQry);
 	COMPARE_SCALAR_FIELD(resultRelation);
 	COMPARE_NODE_FIELD(intoClause);
 	COMPARE_SCALAR_FIELD(hasAggs);
@@ -2933,7 +2934,7 @@ equal(void *a, void *b)
 			break;
 
 		default:
-			elog(ERROR, "unrecognized node type: %d",
+			elog(ERROR, "unrecognized node type: %d in equal() function",
 				 (int) nodeTag(a));
 			retval = false;		/* keep compiler quiet */
 			break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index e7dae4b..357de31 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1984,6 +1984,7 @@ _outQuery(StringInfo str, Query *node)
 	else
 		appendStringInfo(str, " :utilityStmt <>");
 
+	WRITE_NODE_FIELD(mergeActQry);
 	WRITE_INT_FIELD(resultRelation);
 	WRITE_NODE_FIELD(intoClause);
 	WRITE_BOOL_FIELD(hasAggs);
@@ -2439,6 +2440,46 @@ _outConstraint(StringInfo str, Constraint *node)
 }
 
 
+
+
+static void
+_outMergeConditionAction(StringInfo str, MergeConditionAction *node)
+{
+	WRITE_NODE_TYPE("MERGECONDITIONACTION");
+
+	WRITE_BOOL_FIELD(match);
+	
+	WRITE_NODE_FIELD(condition);
+	WRITE_NODE_FIELD(action);
+
+
+}
+
+static void
+_outMergeStmt(StringInfo str, MergeStmt *node)
+{
+	WRITE_NODE_TYPE("MERGESTMT");
+
+	WRITE_NODE_FIELD(relation);
+	WRITE_NODE_FIELD(source);
+	WRITE_NODE_FIELD(matchCondition);
+	WRITE_NODE_FIELD(actions);
+
+}
+
+static void 
+_outDeleteStmt(StringInfo str, DeleteStmt *node)
+{
+	WRITE_NODE_TYPE("DELETESTMT");
+
+	WRITE_NODE_FIELD(relation);
+	WRITE_NODE_FIELD(usingClause);
+	WRITE_NODE_FIELD(whereClause);
+	WRITE_NODE_FIELD(returningList);
+
+
+}
+
 /*
  * _outNode -
  *	  converts a Node into ascii string and append it to 'str'
@@ -2889,6 +2930,16 @@ _outNode(StringInfo str, void *obj)
 				_outXmlSerialize(str, obj);
 				break;
 
+			case T_MergeStmt:
+				_outMergeStmt(str, obj);
+				break;
+			case T_MergeConditionAction:
+				_outMergeConditionAction(str,obj);
+				break;
+			case T_DeleteStmt:
+				_outDeleteStmt(str,obj);
+				break;
+					
 			default:
 
 				/*
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 6b99a10..ae9236a 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -64,6 +64,8 @@ static Query *transformExplainStmt(ParseState *pstate,
 static void transformLockingClause(ParseState *pstate, Query *qry,
 					   LockingClause *lc, bool pushedDown);
 
+static Query *
+transformMergeStmt(ParseState *pstate, MergeStmt *stmt);
 
 /*
  * parse_analyze
@@ -164,14 +166,17 @@ transformStmt(ParseState *pstate, Node *parseTree)
 			 * Optimizable statements
 			 */
 		case T_InsertStmt:
+		case T_MergeInsert:
 			result = transformInsertStmt(pstate, (InsertStmt *) parseTree);
 			break;
 
 		case T_DeleteStmt:
+		case T_MergeDelete:
 			result = transformDeleteStmt(pstate, (DeleteStmt *) parseTree);
 			break;
 
 		case T_UpdateStmt:
+		case T_MergeUpdate:
 			result = transformUpdateStmt(pstate, (UpdateStmt *) parseTree);
 			break;
 
@@ -188,6 +193,10 @@ transformStmt(ParseState *pstate, Node *parseTree)
 			}
 			break;
 
+		case T_MergeStmt:
+			result = transformMergeStmt(pstate, (MergeStmt *)parseTree);
+			break;
+
 			/*
 			 * Special cases
 			 */
@@ -282,12 +291,14 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
 
 	qry->commandType = CMD_DELETE;
 
-	/* set up range table with just the result rel */
-	qry->resultRelation = setTargetTable(pstate, stmt->relation,
+	if(IsA(stmt,DeleteStmt)) //for MergeDelete, no need to do this
+	{
+		/* set up range table with just the result rel */
+		qry->resultRelation = setTargetTable(pstate, stmt->relation,
 								  interpretInhOption(stmt->relation->inhOpt),
 										 true,
 										 ACL_DELETE);
-
+	}
 	qry->distinctClause = NIL;
 
 	/*
@@ -296,7 +307,8 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
 	 * USING keyword is used rather than FROM because FROM is already a
 	 * keyword in the DELETE syntax.
 	 */
-	transformFromClause(pstate, stmt->usingClause);
+	if(IsA(stmt,DeleteStmt)) //for MergeDelete, no need to do this
+		transformFromClause(pstate, stmt->usingClause);
 
 	qual = transformWhereClause(pstate, stmt->whereClause, "WHERE");
 
@@ -347,6 +359,9 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 	 * VALUES list, or general SELECT input.  We special-case VALUES, both for
 	 * efficiency and so we can handle DEFAULT specifications.
 	 */
+
+	//a MergeInsert statment is always VALUE clause
+
 	isGeneralSelect = (selectStmt && selectStmt->valuesLists == NIL);
 
 	/*
@@ -382,7 +397,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 	 * mentioned in the SELECT part.  Note that the target table is not added
 	 * to the joinlist or namespace.
 	 */
-	qry->resultRelation = setTargetTable(pstate, stmt->relation,
+	if(IsA(stmt,InsertStmt))//for MergeInsert, no need to do this
+		qry->resultRelation = setTargetTable(pstate, stmt->relation,
 										 false, false, ACL_INSERT);
 
 	/* Validate stmt->cols list, or build default list if no list given */
@@ -695,7 +711,6 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 				 errmsg("cannot use window function in VALUES"),
 				 parser_errposition(pstate,
 									locate_windowfunc((Node *) qry))));
-
 	return qry;
 }
 
@@ -892,6 +907,9 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 							   (LockingClause *) lfirst(l), false);
 	}
 
+
+//printf("%s\n", nodeToString(qry));
+
 	return qry;
 }
 
@@ -1730,17 +1748,23 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
 	qry->commandType = CMD_UPDATE;
 	pstate->p_is_update = true;
 
-	qry->resultRelation = setTargetTable(pstate, stmt->relation,
-								  interpretInhOption(stmt->relation->inhOpt),
-										 true,
-										 ACL_UPDATE);
+	if(IsA(stmt, UpdateStmt))//for MergeUpdate, no need to do this
+	{
+		//for a MergeUpdate node, we have no need to se the target and source rels
+		qry->resultRelation = setTargetTable(pstate, stmt->relation,
+									  interpretInhOption(stmt->relation->inhOpt),
+											 true,
+											 ACL_UPDATE);
+
+		/*
+		 * the FROM clause is non-standard SQL syntax. We used to be able to do
+		 * this with REPLACE in POSTQUEL so we keep the feature.
+		 */
+		transformFromClause(pstate, stmt->fromClause);
+	}
 
-	/*
-	 * the FROM clause is non-standard SQL syntax. We used to be able to do
-	 * this with REPLACE in POSTQUEL so we keep the feature.
-	 */
-	transformFromClause(pstate, stmt->fromClause);
 
+	
 	qry->targetList = transformTargetList(pstate, stmt->targetList);
 
 	qual = transformWhereClause(pstate, stmt->whereClause, "WHERE");
@@ -1806,12 +1830,14 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
 		origTarget = (ResTarget *) lfirst(origTargetList);
 		Assert(IsA(origTarget, ResTarget));
 
+printf("targe entry :%s\n", nodeToString(origTarget));		
+
 		attrno = attnameAttNum(pstate->p_target_relation,
 							   origTarget->name, true);
 		if (attrno == InvalidAttrNumber)
 			ereport(ERROR,
 					(errcode(ERRCODE_UNDEFINED_COLUMN),
-					 errmsg("column \"%s\" of relation \"%s\" does not exist",
+					 errmsg("column \"%s\" of relation \"%s\" does not exist in transformUpdateStmt()",
 							origTarget->name,
 						 RelationGetRelationName(pstate->p_target_relation)),
 					 parser_errposition(pstate, origTarget->location)));
@@ -2241,3 +2267,326 @@ applyLockingClause(Query *qry, Index rtindex,
 	rc->pushedDown = pushedDown;
 	qry->rowMarks = lappend(qry->rowMarks, rc);
 }
+
+//transform a action of merge command into a query. No change of the pstate range table is allowed in this function. 
+static Query *
+transformMergeActions(ParseState *pstate, MergeStmt *stmt, MergeConditionAction *condact)
+{
+	Query *actqry = makeNode(Query);
+	A_Expr *match_expr; //the expr of matched/not matched
+	A_Expr *act_qual_expr;
+
+	//firstly, we need to make sure that DELETE and UPDATE actions are only taken in MATCHED condition 
+	//and INSERTs are only takend when not MATCHED
+
+	if(IsA(condact->action, MergeDelete)) 
+	{	
+		if(!condact->match) 
+			ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+		 		errmsg("The DELETE action in MERGE command is not allowed when NOT MATCHED")));
+	}
+	else if(IsA(condact->action, MergeUpdate))
+	{	
+		if(!condact->match) 
+			ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+		 		errmsg("The UPDATE action in MERGE command is not allowed when NOT MATCHED")));
+	}
+	else if(IsA(condact->action, MergeInsert))
+	{	
+		if(condact->match) 
+			ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+		 		errmsg("The INSERT action in MERGE command is not allowed when MATCHED")));
+	}
+	else
+		ereport(ERROR,
+			(errcode(ERRCODE_SYNTAX_ERROR),
+			 errmsg("UNKONWN action type in MERGE")));
+
+
+		
+	//combine the condtion of this act with the ON qual of the merge command
+	//do a copy of the merge condtion for safety. 
+	if(condact->match) 
+		match_expr = copyObject(stmt->matchCondition);
+	else
+		match_expr = makeA_Expr(AEXPR_NOT, NIL, NULL, 
+									copyObject(stmt->matchCondition), 1);
+											
+
+	if(condact->condition)
+		act_qual_expr = makeA_Expr(AEXPR_AND, NIL, condact->condition, (Node *)match_expr, 2);
+	else
+		act_qual_expr = match_expr;
+
+
+	//use the transfomStmt() to parse all types of actions
+	if(IsA(condact->action, MergeDelete))
+	{
+		//a delete action
+		MergeDelete *deleteact = (MergeDelete *)(condact->action);
+		Assert(IsA(deleteact,DeleteStmt));
+		
+		deleteact->relation = stmt->relation;
+		deleteact->usingClause = stmt->source;
+		deleteact->whereClause = (Node *)act_qual_expr;
+
+		//parse the action query
+		actqry = transformStmt(pstate, (Node *)deleteact);
+
+		if(!IsA(actqry, Query) || actqry->commandType != CMD_DELETE || actqry->utilityStmt != NULL)
+			elog(ERROR, "improper DELETE action in merge stmt");
+
+		return actqry;
+	}
+	else if(IsA(condact->action, MergeUpdate))
+	{
+		//an update action
+		MergeUpdate *updateact = (MergeUpdate *)(condact->action);
+
+		//the "targetlist" of the updateact is filled in the parser 
+		updateact->relation = stmt->relation;
+		updateact->fromClause = stmt->source;
+		updateact->whereClause = (Node *)act_qual_expr;
+
+		//parse the action query
+		actqry = transformStmt(pstate, (Node *)updateact);
+
+		if(!IsA(actqry, Query) || actqry->commandType != CMD_UPDATE|| actqry->utilityStmt != NULL)
+			elog(ERROR, "improper UPDATE action in merge stmt");
+
+		return actqry;
+	}
+	else if(IsA(condact->action, MergeInsert))
+	{
+		//an insert action
+		Node *qual;
+		MergeInsert *insertact;
+
+		insertact = (MergeInsert *)(condact->action);
+		
+		
+		//the "cols" and "selectStmt" of the insertact is filled in the parser 
+		insertact->relation = stmt->relation;
+
+		/*
+		the merge insert action has a strange feature. 
+		In an ordinary INSERT, the VALUES list can only contains constants and DEFAULT. (am I right??)
+		But in the INSERT action of MERGE command, the VALUES list can have expressions with variables(attributes of the targe and source tables).
+		Besides, in the ordinary INSERT, a VALUES list can never be followed by a WHERE clause. But in MERGE INSERT action, there are matching conditions. 
+
+		Thus, the output qry of this function is an INSERT query in the style of "INSERT...VALUES...", except that we have other range tables and a WHERE clause.
+		Note that it is also different from the "INSERT ... SELECT..." query, in which the whole SELECT is a subquery. (We don't have subquery here).
+		We construct this novel query structure in order to keep consitency with other merge action types (DELETE, UPDATE). 
+		In this way, all the merge action queries are in fact share the very same Range Table, They only differs in their target lists and join trees
+		
+		*/
+		
+				
+		//parse the action query, this will call transformInsertStmt() which analyzes the VALUES list.
+		actqry = transformStmt(pstate, (Node *)insertact);
+
+		//do the WHERE clause here, Since the transformInsertStmt() function only analyzes the VALUES list but not the WHERE clause
+		qual = transformWhereClause(pstate,(Node *)act_qual_expr, "WHERE");
+		
+		actqry->jointree = makeFromExpr(pstate->p_joinlist, qual);
+
+		
+		if(!IsA(actqry, Query) || actqry->commandType != CMD_INSERT|| actqry->utilityStmt != NULL)
+			elog(ERROR, "improper INSERT action in merge stmt");
+
+
+		return actqry;
+	}
+	else
+		elog(ERROR, "unknown action type in MERGE");
+
+	//never comes here
+	return NULL;
+}
+
+
+
+static Query *
+transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
+{
+	Query	   *qry; 
+
+	ColumnRef *starRef;
+	ResTarget *starResTarget;
+	ListCell *act;
+	ListCell *l;
+	JoinExpr *joinexp;
+	int 	rtindex;
+
+	//this will never happen, since the garm.y is restricted that only one rel name is allowed to appear in the source table position. 
+	//However, if we extent the command in future, we may need to note this check here. 
+	if(list_length(stmt->source) != 1) 
+		ereport(ERROR,
+			(errcode(ERRCODE_SYNTAX_ERROR),
+			 errmsg("now we only accept merge command with only ONE source table")));
+		
+	//now, do the real tranformation of the merge command. 
+	qry = makeNode(Query);
+	qry->commandType = CMD_MERGE;
+
+	/*
+	What we are doing here is to create a query like 
+		"SELECT * FROM <source_rel> LEFT JOIN <target_rel> ON <match_condition>;"
+	Note:	
+	1. we set the "match condition" as the join qualification. 
+	The left join will scan both the matched and non-matched tuples.
+
+	2. a normal SELECT query has no "target relation". 
+	But here we need to set the targe relation in query, like the UPDATE/DELETE/INSERT queries. 
+	So this is a left join SELECT with a "target table" in its range table. 
+
+	3. We don't have a specific ACL level for Merge, here we just use ACL_SELECT. 
+	But we will add other ACL levels when handle each merge actions.  
+	*/
+
+
+	/*before analyze the FROM clause, we need to set the target table. 
+	We cannot call setTargetTable() function directly. 
+	We only need the lock target relation, without adding it to Range table. 
+	*/
+	
+	
+	setTargetTableLock(pstate, stmt->relation);
+	
+	
+	
+	//create the FROM clause. Make the join expression first
+	joinexp = makeNode(JoinExpr);
+	joinexp->jointype = JOIN_LEFT;
+	joinexp->isNatural = FALSE;
+	joinexp->larg = linitial(stmt->source);//source list has only one element
+	joinexp->rarg = (Node *)stmt->relation;
+	joinexp->quals = stmt->matchCondition; //match condtion
+
+	//transform the FROM clause. The target relation and source relation will be add to Rtable here.	
+	transformFromClause(pstate, list_make1(joinexp));
+
+	//the targetList of the main query is "*"	
+	starRef = makeNode(ColumnRef);	
+	starRef->fields = list_make1(makeNode(A_Star));					
+	starRef->location = 1;					
+
+	starResTarget = makeNode(ResTarget);					
+	starResTarget->name = NULL;					
+	starResTarget->indirection = NIL;					
+	starResTarget->val = (Node *)starRef;					
+	starResTarget->location = 1;
+	
+	qry->targetList = transformTargetList(pstate, list_make1(starResTarget));
+
+	//we don't need the WHERE clause here. Set it null. 
+	qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
+
+	//now , we find out the RTE for the target relation, and do some unfinished jobs
+	rtindex = 1;
+	foreach(l, pstate->p_rtable)
+	{
+		RangeTblEntry *rte = (RangeTblEntry *)lfirst(l);
+		if(rte->relid == pstate->p_target_relation->rd_id) //find the RTE
+		{
+			pstate->p_target_rangetblentry = rte;
+			rte->requiredPerms = ACL_SELECT;	
+			qry->resultRelation = rtindex;
+			break;
+		}
+		rtindex++;
+	}
+
+	if(pstate->p_target_rangetblentry == NULL)
+		elog(ERROR, "cannot find the RTE for target table");
+	
+
+	qry->rtable = pstate->p_rtable;
+
+	qry->hasSubLinks = pstate->p_hasSubLinks;
+
+	/*
+	 * Top-level aggregates are simply disallowed in MERGE
+	 */
+	if (pstate->p_hasAggs)
+		ereport(ERROR,
+				(errcode(ERRCODE_GROUPING_ERROR),
+				 errmsg("cannot use aggregate function in top level of MERGE"),
+				 parser_errposition(pstate,
+									locate_agg_of_level((Node *) qry, 0))));
+	if (pstate->p_hasWindowFuncs)
+		ereport(ERROR,
+				(errcode(ERRCODE_WINDOWING_ERROR),
+				 errmsg("cannot use window function in MERGE"),
+				 parser_errposition(pstate,
+									locate_windowfunc((Node *) qry))));
+
+
+
+
+	/*
+		the main query is done.
+	then for each actions ,we transform it to a seperate query.
+	the action queries shares the exactly same range table with the main query. 
+	in other words, in the extra condtions of the sub actions, we don't allow involvement of new tables
+	*/
+	
+	qry->mergeActQry = NIL;
+
+	foreach(act,stmt->actions)
+	{
+		MergeConditionAction *mca = lfirst(act);
+		Query *actqry;
+
+		switch(mca->action->type)
+		{
+			case T_MergeDelete:
+				pstate->p_target_rangetblentry->requiredPerms |= ACL_DELETE;
+				break;
+			case T_MergeUpdate:
+				pstate->p_target_rangetblentry->requiredPerms |= ACL_UPDATE;
+				break;
+			case T_MergeInsert:
+				pstate->p_target_rangetblentry->requiredPerms |= ACL_INSERT;				
+				break;
+			default:
+				elog(ERROR, "unknown MERGE action type %d", mca->type);
+				break;
+
+		}
+
+
+		//transform the act (and its condition) as a single query. Link it to the top-level query
+		actqry = transformMergeActions(pstate, stmt, mca);
+
+		//since we don't invoke setTargetTable() in transformMergeActions(), we need to set actqry->resultRelation here
+		actqry->resultRelation = qry->resultRelation;
+
+//printf("finish one action qry: \n%s\n", nodeToString(actqry));
+
+		qry->mergeActQry = lappend(qry->mergeActQry, actqry);
+	}
+
+
+	//for a single-action merge, we just stransform it into a orignial update/delete command.
+	//but the insert action cannot take this shortcut.
+	if(list_length(stmt->actions) == 1)
+	{
+		Query *q = linitial(qry->mergeActQry);
+		if(q->commandType == CMD_DELETE || q->commandType == CMD_UPDATE)
+			return q;
+	}
+
+//printf("the content of main qry is : \n%s\n----\n-----", nodeToString(qry));
+	return qry;
+
+	
+}
+
+
+
+
+
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index f30132a..cf24985 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -213,6 +213,35 @@ setTargetTable(ParseState *pstate, RangeVar *relation,
 	return rtindex;
 }
 
+
+
+/*
+ * setTargetTableLock
+ *     only set the lock for targe table, without adding it to range table
+ */
+ 
+void
+setTargetTableLock(ParseState *pstate, RangeVar *relation)
+{
+	
+	/* Close old target; this could only happen for multi-action rules */
+	if (pstate->p_target_relation != NULL)
+		heap_close(pstate->p_target_relation, NoLock);
+
+	/*
+	 * Open target rel and grab suitable lock (which we will hold till end of
+	 * transaction).
+	 *
+	 * free_parsestate() will eventually do the corresponding heap_close(),
+	 * but *not* release the lock.
+	 */
+	pstate->p_target_relation = parserOpenTable(pstate, relation,
+												RowExclusiveLock);
+
+	
+}
+
+
 /*
  * Simplify InhOption (yes/no/default) into boolean yes/no.
  *
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 38c7e91..b2fcd1c 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -1993,7 +1993,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
 				if (att_tup->attisdropped)
 					ereport(ERROR,
 							(errcode(ERRCODE_UNDEFINED_COLUMN),
-					errmsg("column \"%s\" of relation \"%s\" does not exist",
+					errmsg("column \"%s\" of relation \"%s\" does not exist when get the attr type",
 						   NameStr(att_tup->attname),
 						   get_rel_name(rte->relid))));
 				*vartype = att_tup->atttypid;
@@ -2035,7 +2035,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
 					if (attnum < 1 || attnum > tupdesc->natts)
 						ereport(ERROR,
 								(errcode(ERRCODE_UNDEFINED_COLUMN),
-						errmsg("column %d of relation \"%s\" does not exist",
+						errmsg("column %d of relation \"%s\" does not exist when get the attr type1",
 							   attnum,
 							   rte->eref->aliasname)));
 
@@ -2048,7 +2048,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
 					if (att_tup->attisdropped)
 						ereport(ERROR,
 								(errcode(ERRCODE_UNDEFINED_COLUMN),
-								 errmsg("column \"%s\" of relation \"%s\" does not exist",
+								 errmsg("column \"%s\" of relation \"%s\" does not exist when get the attr type2",
 										NameStr(att_tup->attname),
 										rte->eref->aliasname)));
 					*vartype = att_tup->atttypid;
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index e542dc0..282ad8f 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -805,7 +805,7 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
 			if (attrno == InvalidAttrNumber)
 				ereport(ERROR,
 						(errcode(ERRCODE_UNDEFINED_COLUMN),
-					errmsg("column \"%s\" of relation \"%s\" does not exist",
+					errmsg("column \"%s\" of relation \"%s\" does not exist in checkInsertTargets()",
 						   name,
 						 RelationGetRelationName(pstate->p_target_relation)),
 						 parser_errposition(pstate, col->location)));
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index 8d0932b..bd1f9f9 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -114,7 +114,7 @@ LookupTypeName(ParseState *pstate, const TypeName *typeName,
 		if (attnum == InvalidAttrNumber)
 			ereport(ERROR,
 					(errcode(ERRCODE_UNDEFINED_COLUMN),
-					 errmsg("column \"%s\" of relation \"%s\" does not exist",
+					 errmsg("column \"%s\" of relation \"%s\" does not exist in LookupTypeName",
 							field, rel->relname),
 					 parser_errposition(pstate, typeName->location)));
 		typoid = get_atttype(relid, attnum);
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 25b44dd..90c4256 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -1861,7 +1861,57 @@ QueryRewrite(Query *parsetree)
 	 *
 	 * Apply all non-SELECT rules possibly getting 0 or many queries
 	 */
-	querylist = RewriteQuery(parsetree, NIL);
+
+	if(parsetree->commandType == CMD_MERGE)
+	{
+		//for merge query, we have a set of lower-level action queries (not subquery).
+		//each of these action queries should be applied to RewriteQuery(). And, in all cases, the original query should be excuted. 
+		//However, I am not sure how to run the rules for a merge command yet. :(
+
+		ListCell *l;
+		
+		querylist = NIL;
+
+		foreach(l, parsetree->mergeActQry)
+		{
+			List *queryList4action;
+			Query  *q;
+			
+			queryList4action = RewriteQuery((Query *)lfirst(l), NIL);
+
+
+
+
+
+			if(queryList4action == NIL)
+				continue;
+			
+			//we need to remove the orignal query from the list. 
+			//If it is in the list, it must be either the head or the tail.
+
+						
+			q = (Query *)linitial(queryList4action);
+			if(q->querySource == QSRC_ORIGINAL)
+				queryList4action = list_delete_first(queryList4action);
+			else
+			{
+				q = (Query *)llast(queryList4action);
+				if(q->querySource == QSRC_ORIGINAL)
+					queryList4action = list_truncate(queryList4action,list_length(queryList4action)-1);
+			}	
+			
+
+			//append the rule queries of this action to the full querylist
+			querylist = list_concat(querylist,queryList4action);
+		}
+
+		//finally, put the original query at the head.
+		querylist = lcons(parsetree,querylist);
+
+			
+	}
+	else //a plain query
+		querylist = RewriteQuery(parsetree, NIL);
 
 	/*
 	 * Step 2
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 8960246..846c7c4 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1398,6 +1398,10 @@ CreateCommandTag(Node *parsetree)
 			tag = "SELECT";
 			break;
 
+		case T_MergeStmt:
+			tag = "MERGE";
+			break;
+
 			/* utility statements --- same whether raw or cooked */
 		case T_TransactionStmt:
 			{
@@ -2206,7 +2210,7 @@ CreateCommandTag(Node *parsetree)
 			break;
 
 		default:
-			elog(WARNING, "unrecognized node type: %d",
+			elog(WARNING, "unrecognized node type: %d for command tag creation",
 				 (int) nodeTag(parsetree));
 			tag = "???";
 			break;
@@ -2235,6 +2239,7 @@ GetCommandLogLevel(Node *parsetree)
 		case T_InsertStmt:
 		case T_DeleteStmt:
 		case T_UpdateStmt:
+		case T_MergeStmt:
 			lev = LOGSTMT_MOD;
 			break;
 
@@ -2659,7 +2664,7 @@ GetCommandLogLevel(Node *parsetree)
 			break;
 
 		default:
-			elog(WARNING, "unrecognized node type: %d",
+			elog(WARNING, "unrecognized node type: %d when get the command log level",
 				 (int) nodeTag(parsetree));
 			lev = LOGSTMT_ALL;
 			break;
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 1b5e476..0a619d2 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -260,6 +260,14 @@ typedef enum NodeTag
 	T_DeleteStmt,
 	T_UpdateStmt,
 	T_SelectStmt,
+	
+	T_MergeStmt,
+	T_MergeConditionAction,
+	T_MergeUpdate, 
+	T_MergeDelete,  
+	T_MergeInsert,
+
+
 	T_AlterTableStmt,
 	T_AlterTableCmd,
 	T_AlterDomainStmt,
@@ -509,7 +517,9 @@ typedef enum CmdType
 	CMD_SELECT,					/* select stmt */
 	CMD_UPDATE,					/* update stmt */
 	CMD_INSERT,					/* insert stmt */
-	CMD_DELETE,
+	CMD_DELETE,					/*delete stmt*/
+	CMD_MERGE,						/*merge stmt*/
+	
 	CMD_UTILITY,				/* cmds like create, destroy, copy, vacuum,
 								 * etc. */
 	CMD_NOTHING					/* dummy command for instead nothing rules
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index b591073..05e98d9 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -108,6 +108,9 @@ typedef struct Query
 	Node	   *utilityStmt;	/* non-null if this is DECLARE CURSOR or a
 								 * non-optimizable statement */
 
+	List 		*mergeActQry; // the list of all the merge actions. used only for merge query statment
+
+	
 	int			resultRelation; /* rtable index of target relation for
 								 * INSERT/UPDATE/DELETE; 0 for SELECT */
 
@@ -922,6 +925,9 @@ typedef struct UpdateStmt
 	List	   *returningList;	/* list of expressions to return */
 } UpdateStmt;
 
+
+
+
 /* ----------------------
  *		Select Statement
  *
@@ -1023,6 +1029,33 @@ typedef struct SetOperationStmt
 } SetOperationStmt;
 
 
+//ZBX: the structure for MERGE command statement
+typedef struct MergeStmt
+{
+	NodeTag		type;
+	RangeVar   	*relation;		/*targe relation for merge */
+	List		*source;		/* source relations for the merge. Currently, we only allwo single-source merge, so the length of this list should always be 1 */
+	Node	   	*matchCondition;	/* qualifications of the merge*/
+	List	   	*actions;		/*list  of MergeConditionAction structure. It stores all the match / non-matching conditions and the corresponding actions*/
+
+}MergeStmt;
+
+//the structure for the actions of MERGE command. Holds info of the clauses like "... WHEN MATCHED AND ... THEN UPDATE/DELETE/INSERT"
+typedef struct MergeConditionAction
+{
+	NodeTag		type;
+	bool 		match; //match or not match
+	Node		*condition;//the AND condition for this action
+	Node 		*action; //the actions: delete , insert or update
+}MergeConditionAction;
+
+typedef UpdateStmt MergeUpdate;
+typedef DeleteStmt MergeDelete;
+typedef InsertStmt MergeInsert;
+
+
+
+
 /*****************************************************************************
  *		Other Statements (no optimizations required)
  *
diff --git a/src/include/parser/parse_clause.h b/src/include/parser/parse_clause.h
index f3d3ee9..2541f50 100644
--- a/src/include/parser/parse_clause.h
+++ b/src/include/parser/parse_clause.h
@@ -19,6 +19,9 @@
 extern void transformFromClause(ParseState *pstate, List *frmList);
 extern int setTargetTable(ParseState *pstate, RangeVar *relation,
 			   bool inh, bool alsoSource, AclMode requiredPerms);
+
+extern void setTargetTableLock(ParseState *pstate, RangeVar *relation);
+
 extern bool interpretInhOption(InhOption inhOpt);
 extern bool interpretOidsOption(List *defList);