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 LEFT JOIN ON ;" + 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);