LCOV - differential code coverage report
Current view: top level - src/backend/parser - parse_merge.c (source / functions) Coverage Total Hit LBC UBC GIC GNC CBC EUB ECB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 96.8 % 125 121 1 3 38 4 79 1 40 1
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 3 3 1 1 1 1
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

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

Generated by: LCOV version v1.16-55-g56c0a2a