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 17:13:01 Functions: 100.0 % 3 3 1 1 1 1
Baseline: 15 Line coverage date bins:
Baseline Date: 2023-04-08 15:09:40 (60,120] days: 100.0 % 3 3 3
Legend: Lines: hit not hit (120,180] days: 100.0 % 6 6 4 2
(240..) days: 96.6 % 116 112 1 3 38 74 1 39
Function coverage date bins:
(240..) days: 75.0 % 4 3 1 1 1 1

 Age         Owner                  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
  377 alvherre                   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;
  377 alvherre                  151 UBC           0 :             default:
                                152               0 :                 elog(ERROR, "unknown action in MERGE WHEN clause");
                                153                 :         }
                                154                 : 
                                155                 :         /*
                                156                 :          * Check for unreachable WHEN clauses
                                157                 :          */
   89 dean.a.rasheed            158 CBC         762 :         if (is_terminal[when_type])
  377 alvherre                  159               3 :             ereport(ERROR,
                                160                 :                     (errcode(ERRCODE_SYNTAX_ERROR),
                                161                 :                      errmsg("unreachable WHEN clause specified after unconditional WHEN clause")));
   89 dean.a.rasheed            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                 :      */
  377 alvherre                  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)));
  135 dean.a.rasheed            185             492 :     if (pstate->p_target_relation->rd_rules != NULL &&
                                186               3 :         pstate->p_target_relation->rd_rules->numLocks > 0)
  377 alvherre                  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                 :      */
  362                           216             483 :     qry->targetList = NIL;
  377                           217             483 :     qry->rtable = pstate->p_rtable;
  124 alvherre                  218 GNC         483 :     qry->rteperminfos = pstate->p_rteperminfos;
  377 alvherre                  219 ECB             : 
                                220                 :     /*
                                221                 :      * Transform the join condition.  This includes references to the target
                                222                 :      * side, so add that to the namespace.
                                223                 :      */
  377 alvherre                  224 GIC         483 :     addNSItemToQuery(pstate, pstate->p_target_nsitem, false, true, true);
  377 alvherre                  225 CBC         483 :     joinExpr = transformExpr(pstate, stmt->joinCondition,
  377 alvherre                  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                 :      */
  377 alvherre                  234 GIC         483 :     qry->jointree = makeFromExpr(pstate->p_joinlist, joinExpr);
  377 alvherre                  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                 :      */
  377 alvherre                  250 GIC         483 :     mergeActionList = NIL;
  377 alvherre                  251 CBC        1197 :     foreach(l, stmt->mergeWhenClauses)
  377 alvherre                  252 ECB             :     {
  377 alvherre                  253 GIC         729 :         MergeWhenClause *mergeWhenClause = lfirst_node(MergeWhenClause, l);
  377 alvherre                  254 ECB             :         MergeAction *action;
                                255                 : 
  377 alvherre                  256 GIC         729 :         action = makeNode(MergeAction);
  377 alvherre                  257 CBC         729 :         action->commandType = mergeWhenClause->commandType;
                                258             729 :         action->matched = mergeWhenClause->matched;
  377 alvherre                  259 ECB             : 
                                260                 :         /* Use an outer join if any INSERT actions exist in the command. */
  377 alvherre                  261 GIC         729 :         if (action->commandType == CMD_INSERT)
  377 alvherre                  262 CBC         289 :             qry->mergeUseOuterJoin = true;
  377 alvherre                  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                 :          */
  377 alvherre                  268 GIC         729 :         setNamespaceForMergeWhen(pstate, mergeWhenClause,
  377 alvherre                  269 CBC         729 :                                  qry->resultRelation,
  377 alvherre                  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                 :          */
  377 alvherre                  279 GIC         729 :         action->qual = transformWhereClause(pstate, mergeWhenClause->condition,
  377 alvherre                  280 ECB             :                                             EXPR_KIND_MERGE_WHEN, "WHEN");
                                281                 : 
                                282                 :         /*
                                283                 :          * Transform target lists for each INSERT and UPDATE action stmt
                                284                 :          */
  377 alvherre                  285 GIC         723 :         switch (action->commandType)
  377 alvherre                  286 ECB             :         {
  377 alvherre                  287 GIC         286 :             case CMD_INSERT:
  377 alvherre                  288 ECB             :                 {
  377 alvherre                  289 GIC         286 :                     List       *exprList = NIL;
  377 alvherre                  290 ECB             :                     ListCell   *lc;
                                291                 :                     RTEPermissionInfo *perminfo;
                                292                 :                     ListCell   *icols;
                                293                 :                     ListCell   *attnos;
                                294                 :                     List       *icolumns;
                                295                 :                     List       *attrnos;
                                296                 : 
  377 alvherre                  297 GIC         286 :                     pstate->p_is_insert = true;
  377 alvherre                  298 ECB             : 
  377 alvherre                  299 GIC         286 :                     icolumns = checkInsertTargets(pstate,
  377 alvherre                  300 ECB             :                                                   mergeWhenClause->targetList,
                                301                 :                                                   &attrnos);
  377 alvherre                  302 GIC         286 :                     Assert(list_length(icolumns) == list_length(attrnos));
  377 alvherre                  303 ECB             : 
  377 alvherre                  304 GIC         286 :                     action->override = mergeWhenClause->override;
  377 alvherre                  305 ECB             : 
                                306                 :                     /*
                                307                 :                      * Handle INSERT much like in transformInsertStmt
                                308                 :                      */
  377 alvherre                  309 GIC         286 :                     if (mergeWhenClause->values == NIL)
  377 alvherre                  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                 :                          */
  377 alvherre                  317 GIC           9 :                         exprList = NIL;
  377 alvherre                  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                 :                          */
  377 alvherre                  333 GIC         277 :                         exprList = transformExpressionList(pstate,
  377 alvherre                  334 ECB             :                                                            mergeWhenClause->values,
                                335                 :                                                            EXPR_KIND_VALUES_SINGLE,
                                336                 :                                                            true);
                                337                 : 
                                338                 :                         /* Prepare row for assignment to target table */
  377 alvherre                  339 GIC         271 :                         exprList = transformInsertRow(pstate, exprList,
  377 alvherre                  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                 :                      */
  124 alvherre                  350 GNC         280 :                     perminfo = pstate->p_target_nsitem->p_perminfo;
  377 alvherre                  351 CBC         888 :                     forthree(lc, exprList, icols, icolumns, attnos, attrnos)
  377 alvherre                  352 ECB             :                     {
  377 alvherre                  353 GIC         608 :                         Expr       *expr = (Expr *) lfirst(lc);
  377 alvherre                  354 CBC         608 :                         ResTarget  *col = lfirst_node(ResTarget, icols);
                                355             608 :                         AttrNumber  attr_num = (AttrNumber) lfirst_int(attnos);
  377 alvherre                  356 ECB             :                         TargetEntry *tle;
                                357                 : 
  377 alvherre                  358 GIC         608 :                         tle = makeTargetEntry(expr,
  377 alvherre                  359 ECB             :                                               attr_num,
                                360                 :                                               col->name,
                                361                 :                                               false);
  377 alvherre                  362 GIC         608 :                         action->targetList = lappend(action->targetList, tle);
  377 alvherre                  363 ECB             : 
  124 alvherre                  364 GNC         608 :                         perminfo->insertedCols =
                                365             608 :                             bms_add_member(perminfo->insertedCols,
  377 alvherre                  366 ECB             :                                            attr_num - FirstLowInvalidHeapAttributeNumber);
                                367                 :                     }
                                368                 :                 }
  377 alvherre                  369 GIC         280 :                 break;
  377 alvherre                  370 CBC         325 :             case CMD_UPDATE:
  377 alvherre                  371 ECB             :                 {
  377 alvherre                  372 GIC         325 :                     pstate->p_is_insert = false;
  377 alvherre                  373 CBC         322 :                     action->targetList =
                                374             325 :                         transformUpdateTargetList(pstate,
  377 alvherre                  375 ECB             :                                                   mergeWhenClause->targetList);
                                376                 :                 }
  377 alvherre                  377 GIC         322 :                 break;
  377 alvherre                  378 CBC         100 :             case CMD_DELETE:
                                379             100 :                 break;
  377 alvherre                  380 ECB             : 
  377 alvherre                  381 GIC          12 :             case CMD_NOTHING:
  377 alvherre                  382 CBC          12 :                 action->targetList = NIL;
                                383              12 :                 break;
  377 alvherre                  384 LBC           0 :             default:
  377 alvherre                  385 UBC           0 :                 elog(ERROR, "unknown action in MERGE WHEN clause");
  377 alvherre                  386 EUB             :         }
                                387                 : 
  377 alvherre                  388 GIC         714 :         mergeActionList = lappend(mergeActionList, action);
  377 alvherre                  389 ECB             :     }
                                390                 : 
  377 alvherre                  391 GIC         468 :     qry->mergeActionList = mergeActionList;
  377 alvherre                  392 ECB             : 
                                393                 :     /* RETURNING could potentially be added in the future, but not in SQL std */
  377 alvherre                  394 GIC         468 :     qry->returningList = NULL;
  377 alvherre                  395 ECB             : 
  377 alvherre                  396 GIC         468 :     qry->hasTargetSRFs = false;
  377 alvherre                  397 CBC         468 :     qry->hasSubLinks = pstate->p_hasSubLinks;
  377 alvherre                  398 ECB             : 
  377 alvherre                  399 GIC         468 :     assign_query_collations(pstate, qry);
  377 alvherre                  400 ECB             : 
  377 alvherre                  401 GIC         468 :     return qry;
  377 alvherre                  402 ECB             : }
                                403                 : 
                                404                 : static void
  377 alvherre                  405 GIC        1458 : setNamespaceVisibilityForRTE(List *namespace, RangeTblEntry *rte,
  377 alvherre                  406 ECB             :                              bool rel_visible,
                                407                 :                              bool cols_visible)
                                408                 : {
                                409                 :     ListCell   *lc;
                                410                 : 
  377 alvherre                  411 GIC        2283 :     foreach(lc, namespace)
  377 alvherre                  412 ECB             :     {
  377 alvherre                  413 GIC        2283 :         ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(lc);
  377 alvherre                  414 ECB             : 
  377 alvherre                  415 GIC        2283 :         if (nsitem->p_rte == rte)
  377 alvherre                  416 ECB             :         {
  377 alvherre                  417 GIC        1458 :             nsitem->p_rel_visible = rel_visible;
  377 alvherre                  418 CBC        1458 :             nsitem->p_cols_visible = cols_visible;
                                419            1458 :             break;
  377 alvherre                  420 ECB             :         }
                                421                 :     }
  377 alvherre                  422 GIC        1458 : }
        

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