LCOV - code coverage report
Current view: top level - src/backend/parser - parse_merge.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 118 122 96.7 %
Date: 2024-04-19 22:11:39 Functions: 3 3 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * parse_merge.c
       4             :  *    handle merge-statement in parser
       5             :  *
       6             :  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/parser/parse_merge.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : 
      16             : #include "postgres.h"
      17             : 
      18             : #include "access/sysattr.h"
      19             : #include "nodes/makefuncs.h"
      20             : #include "parser/analyze.h"
      21             : #include "parser/parse_clause.h"
      22             : #include "parser/parse_collate.h"
      23             : #include "parser/parse_cte.h"
      24             : #include "parser/parse_expr.h"
      25             : #include "parser/parse_merge.h"
      26             : #include "parser/parse_relation.h"
      27             : #include "parser/parse_target.h"
      28             : #include "parser/parsetree.h"
      29             : #include "utils/rel.h"
      30             : 
      31             : static void setNamespaceForMergeWhen(ParseState *pstate,
      32             :                                      MergeWhenClause *mergeWhenClause,
      33             :                                      Index targetRTI,
      34             :                                      Index sourceRTI);
      35             : static void setNamespaceVisibilityForRTE(List *namespace, RangeTblEntry *rte,
      36             :                                          bool rel_visible,
      37             :                                          bool cols_visible);
      38             : 
      39             : /*
      40             :  * Make appropriate changes to the namespace visibility while transforming
      41             :  * individual action's quals and targetlist expressions. In particular, for
      42             :  * INSERT actions we must only see the source relation (since INSERT action is
      43             :  * invoked for NOT MATCHED [BY TARGET] tuples and hence there is no target
      44             :  * tuple to deal with). On the other hand, UPDATE and DELETE actions can see
      45             :  * both source and target relations, unless invoked for NOT MATCHED BY SOURCE.
      46             :  *
      47             :  * Also, since the internal join node can hide the source and target
      48             :  * relations, we must explicitly make the respective relation as visible so
      49             :  * that columns can be referenced unqualified from these relations.
      50             :  */
      51             : static void
      52        2840 : setNamespaceForMergeWhen(ParseState *pstate, MergeWhenClause *mergeWhenClause,
      53             :                          Index targetRTI, Index sourceRTI)
      54             : {
      55             :     RangeTblEntry *targetRelRTE,
      56             :                *sourceRelRTE;
      57             : 
      58        2840 :     targetRelRTE = rt_fetch(targetRTI, pstate->p_rtable);
      59        2840 :     sourceRelRTE = rt_fetch(sourceRTI, pstate->p_rtable);
      60             : 
      61        2840 :     if (mergeWhenClause->matchKind == MERGE_WHEN_MATCHED)
      62             :     {
      63             :         Assert(mergeWhenClause->commandType == CMD_UPDATE ||
      64             :                mergeWhenClause->commandType == CMD_DELETE ||
      65             :                mergeWhenClause->commandType == CMD_NOTHING);
      66             : 
      67             :         /* MATCHED actions can see both target and source relations. */
      68        1760 :         setNamespaceVisibilityForRTE(pstate->p_namespace,
      69             :                                      targetRelRTE, true, true);
      70        1760 :         setNamespaceVisibilityForRTE(pstate->p_namespace,
      71             :                                      sourceRelRTE, true, true);
      72             :     }
      73        1080 :     else if (mergeWhenClause->matchKind == MERGE_WHEN_NOT_MATCHED_BY_SOURCE)
      74             :     {
      75             :         /*
      76             :          * NOT MATCHED BY SOURCE actions can see the target relation, but they
      77             :          * can't see the source relation.
      78             :          */
      79             :         Assert(mergeWhenClause->commandType == CMD_UPDATE ||
      80             :                mergeWhenClause->commandType == CMD_DELETE ||
      81             :                mergeWhenClause->commandType == CMD_NOTHING);
      82         132 :         setNamespaceVisibilityForRTE(pstate->p_namespace,
      83             :                                      targetRelRTE, true, true);
      84         132 :         setNamespaceVisibilityForRTE(pstate->p_namespace,
      85             :                                      sourceRelRTE, false, false);
      86             :     }
      87             :     else                        /* MERGE_WHEN_NOT_MATCHED_BY_TARGET */
      88             :     {
      89             :         /*
      90             :          * NOT MATCHED [BY TARGET] actions can't see target relation, but they
      91             :          * can see source relation.
      92             :          */
      93             :         Assert(mergeWhenClause->commandType == CMD_INSERT ||
      94             :                mergeWhenClause->commandType == CMD_NOTHING);
      95         948 :         setNamespaceVisibilityForRTE(pstate->p_namespace,
      96             :                                      targetRelRTE, false, false);
      97         948 :         setNamespaceVisibilityForRTE(pstate->p_namespace,
      98             :                                      sourceRelRTE, true, true);
      99             :     }
     100        2840 : }
     101             : 
     102             : /*
     103             :  * transformMergeStmt -
     104             :  *    transforms a MERGE statement
     105             :  */
     106             : Query *
     107        1860 : transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
     108             : {
     109        1860 :     Query      *qry = makeNode(Query);
     110             :     ListCell   *l;
     111        1860 :     AclMode     targetPerms = ACL_NO_RIGHTS;
     112             :     bool        is_terminal[NUM_MERGE_MATCH_KINDS];
     113             :     Index       sourceRTI;
     114             :     List       *mergeActionList;
     115             :     ParseNamespaceItem *nsitem;
     116             : 
     117             :     /* There can't be any outer WITH to worry about */
     118             :     Assert(pstate->p_ctenamespace == NIL);
     119             : 
     120        1860 :     qry->commandType = CMD_MERGE;
     121        1860 :     qry->hasRecursive = false;
     122             : 
     123             :     /* process the WITH clause independently of all else */
     124        1860 :     if (stmt->withClause)
     125             :     {
     126          48 :         if (stmt->withClause->recursive)
     127           6 :             ereport(ERROR,
     128             :                     (errcode(ERRCODE_SYNTAX_ERROR),
     129             :                      errmsg("WITH RECURSIVE is not supported for MERGE statement")));
     130             : 
     131          42 :         qry->cteList = transformWithClause(pstate, stmt->withClause);
     132          42 :         qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
     133             :     }
     134             : 
     135             :     /*
     136             :      * Check WHEN clauses for permissions and sanity
     137             :      */
     138        1854 :     is_terminal[MERGE_WHEN_MATCHED] = false;
     139        1854 :     is_terminal[MERGE_WHEN_NOT_MATCHED_BY_SOURCE] = false;
     140        1854 :     is_terminal[MERGE_WHEN_NOT_MATCHED_BY_TARGET] = false;
     141        4730 :     foreach(l, stmt->mergeWhenClauses)
     142             :     {
     143        2882 :         MergeWhenClause *mergeWhenClause = (MergeWhenClause *) lfirst(l);
     144             : 
     145             :         /*
     146             :          * Collect permissions to check, according to action types. We require
     147             :          * SELECT privileges for DO NOTHING because it'd be irregular to have
     148             :          * a target relation with zero privileges checked, in case DO NOTHING
     149             :          * is the only action.  There's no damage from that: any meaningful
     150             :          * MERGE command requires at least some access to the table anyway.
     151             :          */
     152        2882 :         switch (mergeWhenClause->commandType)
     153             :         {
     154         952 :             case CMD_INSERT:
     155         952 :                 targetPerms |= ACL_INSERT;
     156         952 :                 break;
     157        1412 :             case CMD_UPDATE:
     158        1412 :                 targetPerms |= ACL_UPDATE;
     159        1412 :                 break;
     160         458 :             case CMD_DELETE:
     161         458 :                 targetPerms |= ACL_DELETE;
     162         458 :                 break;
     163          60 :             case CMD_NOTHING:
     164          60 :                 targetPerms |= ACL_SELECT;
     165          60 :                 break;
     166           0 :             default:
     167           0 :                 elog(ERROR, "unknown action in MERGE WHEN clause");
     168             :         }
     169             : 
     170             :         /*
     171             :          * Check for unreachable WHEN clauses
     172             :          */
     173        2882 :         if (is_terminal[mergeWhenClause->matchKind])
     174           6 :             ereport(ERROR,
     175             :                     (errcode(ERRCODE_SYNTAX_ERROR),
     176             :                      errmsg("unreachable WHEN clause specified after unconditional WHEN clause")));
     177        2876 :         if (mergeWhenClause->condition == NULL)
     178        2098 :             is_terminal[mergeWhenClause->matchKind] = true;
     179             :     }
     180             : 
     181             :     /*
     182             :      * Set up the MERGE target table.  The target table is added to the
     183             :      * namespace below and to joinlist in transform_MERGE_to_join, so don't do
     184             :      * it here.
     185             :      *
     186             :      * Initially mergeTargetRelation is the same as resultRelation, so data is
     187             :      * read from the table being updated.  However, that might be changed by
     188             :      * the rewriter, if the target is a trigger-updatable view, to allow
     189             :      * target data to be read from the expanded view query while updating the
     190             :      * original view relation.
     191             :      */
     192        3696 :     qry->resultRelation = setTargetTable(pstate, stmt->relation,
     193        1848 :                                          stmt->relation->inh,
     194             :                                          false, targetPerms);
     195        1848 :     qry->mergeTargetRelation = qry->resultRelation;
     196             : 
     197             :     /* The target relation must be a table or a view */
     198        1848 :     if (pstate->p_target_relation->rd_rel->relkind != RELKIND_RELATION &&
     199         762 :         pstate->p_target_relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
     200         630 :         pstate->p_target_relation->rd_rel->relkind != RELKIND_VIEW)
     201           6 :         ereport(ERROR,
     202             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     203             :                  errmsg("cannot execute MERGE on relation \"%s\"",
     204             :                         RelationGetRelationName(pstate->p_target_relation)),
     205             :                  errdetail_relkind_not_supported(pstate->p_target_relation->rd_rel->relkind)));
     206             : 
     207             :     /* Now transform the source relation to produce the source RTE. */
     208        1842 :     transformFromClause(pstate,
     209        1842 :                         list_make1(stmt->sourceRelation));
     210        1836 :     sourceRTI = list_length(pstate->p_rtable);
     211        1836 :     nsitem = GetNSItemByRangeTablePosn(pstate, sourceRTI, 0);
     212             : 
     213             :     /*
     214             :      * Check that the target table doesn't conflict with the source table.
     215             :      * This would typically be a checkNameSpaceConflicts call, but we want a
     216             :      * more specific error message.
     217             :      */
     218        1836 :     if (strcmp(pstate->p_target_nsitem->p_names->aliasname,
     219        1836 :                nsitem->p_names->aliasname) == 0)
     220           6 :         ereport(ERROR,
     221             :                 errcode(ERRCODE_DUPLICATE_ALIAS),
     222             :                 errmsg("name \"%s\" specified more than once",
     223             :                        pstate->p_target_nsitem->p_names->aliasname),
     224             :                 errdetail("The name is used both as MERGE target table and data source."));
     225             : 
     226             :     /*
     227             :      * There's no need for a targetlist here; it'll be set up by
     228             :      * preprocess_targetlist later.
     229             :      */
     230        1830 :     qry->targetList = NIL;
     231        1830 :     qry->rtable = pstate->p_rtable;
     232        1830 :     qry->rteperminfos = pstate->p_rteperminfos;
     233             : 
     234             :     /*
     235             :      * Transform the join condition.  This includes references to the target
     236             :      * side, so add that to the namespace.
     237             :      */
     238        1830 :     addNSItemToQuery(pstate, pstate->p_target_nsitem, false, true, true);
     239        1830 :     qry->mergeJoinCondition = transformExpr(pstate, stmt->joinCondition,
     240             :                                             EXPR_KIND_JOIN_ON);
     241             : 
     242             :     /*
     243             :      * Create the temporary query's jointree using the joinlist we built using
     244             :      * just the source relation; the target relation is not included. The join
     245             :      * will be constructed fully by transform_MERGE_to_join.
     246             :      */
     247        1830 :     qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
     248             : 
     249             :     /* Transform the RETURNING list, if any */
     250        1830 :     qry->returningList = transformReturningList(pstate, stmt->returningList,
     251             :                                                 EXPR_KIND_MERGE_RETURNING);
     252             : 
     253             :     /*
     254             :      * We now have a good query shape, so now look at the WHEN conditions and
     255             :      * action targetlists.
     256             :      *
     257             :      * Overall, the MERGE Query's targetlist is NIL.
     258             :      *
     259             :      * Each individual action has its own targetlist that needs separate
     260             :      * transformation. These transforms don't do anything to the overall
     261             :      * targetlist, since that is only used for resjunk columns.
     262             :      *
     263             :      * We can reference any column in Target or Source, which is OK because
     264             :      * both of those already have RTEs. There is nothing like the EXCLUDED
     265             :      * pseudo-relation for INSERT ON CONFLICT.
     266             :      */
     267        1830 :     mergeActionList = NIL;
     268        4634 :     foreach(l, stmt->mergeWhenClauses)
     269             :     {
     270        2840 :         MergeWhenClause *mergeWhenClause = lfirst_node(MergeWhenClause, l);
     271             :         MergeAction *action;
     272             : 
     273        2840 :         action = makeNode(MergeAction);
     274        2840 :         action->commandType = mergeWhenClause->commandType;
     275        2840 :         action->matchKind = mergeWhenClause->matchKind;
     276             : 
     277             :         /*
     278             :          * Set namespace for the specific action. This must be done before
     279             :          * analyzing the WHEN quals and the action targetlist.
     280             :          */
     281        2840 :         setNamespaceForMergeWhen(pstate, mergeWhenClause,
     282        2840 :                                  qry->resultRelation,
     283             :                                  sourceRTI);
     284             : 
     285             :         /*
     286             :          * Transform the WHEN condition.
     287             :          *
     288             :          * Note that these quals are NOT added to the join quals; instead they
     289             :          * are evaluated separately during execution to decide which of the
     290             :          * WHEN MATCHED or WHEN NOT MATCHED actions to execute.
     291             :          */
     292        2840 :         action->qual = transformWhereClause(pstate, mergeWhenClause->condition,
     293             :                                             EXPR_KIND_MERGE_WHEN, "WHEN");
     294             : 
     295             :         /*
     296             :          * Transform target lists for each INSERT and UPDATE action stmt
     297             :          */
     298        2822 :         switch (action->commandType)
     299             :         {
     300         928 :             case CMD_INSERT:
     301             :                 {
     302         928 :                     List       *exprList = NIL;
     303             :                     ListCell   *lc;
     304             :                     RTEPermissionInfo *perminfo;
     305             :                     ListCell   *icols;
     306             :                     ListCell   *attnos;
     307             :                     List       *icolumns;
     308             :                     List       *attrnos;
     309             : 
     310         928 :                     pstate->p_is_insert = true;
     311             : 
     312         928 :                     icolumns = checkInsertTargets(pstate,
     313             :                                                   mergeWhenClause->targetList,
     314             :                                                   &attrnos);
     315             :                     Assert(list_length(icolumns) == list_length(attrnos));
     316             : 
     317         928 :                     action->override = mergeWhenClause->override;
     318             : 
     319             :                     /*
     320             :                      * Handle INSERT much like in transformInsertStmt
     321             :                      */
     322         928 :                     if (mergeWhenClause->values == NIL)
     323             :                     {
     324             :                         /*
     325             :                          * We have INSERT ... DEFAULT VALUES.  We can handle
     326             :                          * this case by emitting an empty targetlist --- all
     327             :                          * columns will be defaulted when the planner expands
     328             :                          * the targetlist.
     329             :                          */
     330          24 :                         exprList = NIL;
     331             :                     }
     332             :                     else
     333             :                     {
     334             :                         /*
     335             :                          * Process INSERT ... VALUES with a single VALUES
     336             :                          * sublist.  We treat this case separately for
     337             :                          * efficiency.  The sublist is just computed directly
     338             :                          * as the Query's targetlist, with no VALUES RTE.  So
     339             :                          * it works just like a SELECT without any FROM.
     340             :                          */
     341             : 
     342             :                         /*
     343             :                          * Do basic expression transformation (same as a ROW()
     344             :                          * expr, but allow SetToDefault at top level)
     345             :                          */
     346         904 :                         exprList = transformExpressionList(pstate,
     347             :                                                            mergeWhenClause->values,
     348             :                                                            EXPR_KIND_VALUES_SINGLE,
     349             :                                                            true);
     350             : 
     351             :                         /* Prepare row for assignment to target table */
     352         892 :                         exprList = transformInsertRow(pstate, exprList,
     353             :                                                       mergeWhenClause->targetList,
     354             :                                                       icolumns, attrnos,
     355             :                                                       false);
     356             :                     }
     357             : 
     358             :                     /*
     359             :                      * Generate action's target list using the computed list
     360             :                      * of expressions. Also, mark all the target columns as
     361             :                      * needing insert permissions.
     362             :                      */
     363         916 :                     perminfo = pstate->p_target_nsitem->p_perminfo;
     364        2880 :                     forthree(lc, exprList, icols, icolumns, attnos, attrnos)
     365             :                     {
     366        1964 :                         Expr       *expr = (Expr *) lfirst(lc);
     367        1964 :                         ResTarget  *col = lfirst_node(ResTarget, icols);
     368        1964 :                         AttrNumber  attr_num = (AttrNumber) lfirst_int(attnos);
     369             :                         TargetEntry *tle;
     370             : 
     371        1964 :                         tle = makeTargetEntry(expr,
     372             :                                               attr_num,
     373             :                                               col->name,
     374             :                                               false);
     375        1964 :                         action->targetList = lappend(action->targetList, tle);
     376             : 
     377        1964 :                         perminfo->insertedCols =
     378        1964 :                             bms_add_member(perminfo->insertedCols,
     379             :                                            attr_num - FirstLowInvalidHeapAttributeNumber);
     380             :                     }
     381             :                 }
     382         916 :                 break;
     383        1400 :             case CMD_UPDATE:
     384             :                 {
     385        1400 :                     pstate->p_is_insert = false;
     386        1394 :                     action->targetList =
     387        1400 :                         transformUpdateTargetList(pstate,
     388             :                                                   mergeWhenClause->targetList);
     389             :                 }
     390        1394 :                 break;
     391         440 :             case CMD_DELETE:
     392         440 :                 break;
     393             : 
     394          54 :             case CMD_NOTHING:
     395          54 :                 action->targetList = NIL;
     396          54 :                 break;
     397           0 :             default:
     398           0 :                 elog(ERROR, "unknown action in MERGE WHEN clause");
     399             :         }
     400             : 
     401        2804 :         mergeActionList = lappend(mergeActionList, action);
     402             :     }
     403             : 
     404        1794 :     qry->mergeActionList = mergeActionList;
     405             : 
     406        1794 :     qry->hasTargetSRFs = false;
     407        1794 :     qry->hasSubLinks = pstate->p_hasSubLinks;
     408             : 
     409        1794 :     assign_query_collations(pstate, qry);
     410             : 
     411        1794 :     return qry;
     412             : }
     413             : 
     414             : static void
     415        5680 : setNamespaceVisibilityForRTE(List *namespace, RangeTblEntry *rte,
     416             :                              bool rel_visible,
     417             :                              bool cols_visible)
     418             : {
     419             :     ListCell   *lc;
     420             : 
     421        8712 :     foreach(lc, namespace)
     422             :     {
     423        8712 :         ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(lc);
     424             : 
     425        8712 :         if (nsitem->p_rte == rte)
     426             :         {
     427        5680 :             nsitem->p_rel_visible = rel_visible;
     428        5680 :             nsitem->p_cols_visible = cols_visible;
     429        5680 :             break;
     430             :         }
     431             :     }
     432        5680 : }

Generated by: LCOV version 1.14