Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Android/art/art/dexopt_chroot_setup/binder/   (Android Betriebssystem Version 17©)  Datei vom 26.5.2026 mit Größe 1 kB image not shown  

Quelle  parse_merge.c  Sprache: unbekannt

 
/*-------------------------------------------------------------------------
 *
 * parse_merge.c
 *   handle merge-statement in parser
 *
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *   src/backend/parser/parse_merge.c
 *
 *-------------------------------------------------------------------------
 */


#include "postgres.h"

#include "access/sysattr.h"
#include "nodes/makefuncs.h"
#include "parser/analyze.h"
#include "parser/parse_clause.h"
#include "parser/parse_collate.h"
#include "parser/parse_cte.h"
#include "parser/parse_expr.h"
#include "parser/parse_merge.h"
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
#include "parser/parsetree.h"
#include "utils/rel.h"

static void setNamespaceForMergeWhen(ParseState *pstate,
          MergeWhenClause *mergeWhenClause,
          Index targetRTI,
          Index sourceRTI);
static void setNamespaceVisibilityForRTE(List *namespace, RangeTblEntry *rte,
           bool rel_visible,
           bool cols_visible);

/*
 * Make appropriate changes to the namespace visibility while transforming
 * individual action's quals and targetlist expressions. In particular, for
 * INSERT actions we must only see the source relation (since INSERT action is
 * invoked for NOT MATCHED [BY TARGET] tuples and hence there is no target
 * tuple to deal with). On the other hand, UPDATE and DELETE actions can see
 * both source and target relations, unless invoked for NOT MATCHED BY SOURCE.
 *
 * Also, since the internal join node can hide the source and target
 * relations, we must explicitly make the respective relation as visible so
 * that columns can be referenced unqualified from these relations.
 */

static void
setNamespaceForMergeWhen(ParseState *pstate, MergeWhenClause *mergeWhenClause,
       Index targetRTI, Index sourceRTI)
{
 RangeTblEntry *targetRelRTE,
      *sourceRelRTE;

 targetRelRTE = rt_fetch(targetRTI, pstate->p_rtable);
 sourceRelRTE = rt_fetch(sourceRTI, pstate->p_rtable);

 if (mergeWhenClause->matchKind == MERGE_WHEN_MATCHED)
 {
  Assert(mergeWhenClause->commandType == CMD_UPDATE ||
      mergeWhenClause->commandType == CMD_DELETE ||
      mergeWhenClause->commandType == CMD_NOTHING);

  /* MATCHED actions can see both target and source relations. */
  setNamespaceVisibilityForRTE(pstate->p_namespace,
          targetRelRTE, true, true);
  setNamespaceVisibilityForRTE(pstate->p_namespace,
          sourceRelRTE, true, true);
 }
 else if (mergeWhenClause->matchKind == MERGE_WHEN_NOT_MATCHED_BY_SOURCE)
 {
  /*
   * NOT MATCHED BY SOURCE actions can see the target relation, but they
   * can't see the source relation.
 */

  Assert(mergeWhenClause->commandType == CMD_UPDATE ||
      mergeWhenClause->commandType == CMD_DELETE ||
      mergeWhenClause->commandType == CMD_NOTHING);
  setNamespaceVisibilityForRTE(pstate->p_namespace,
          targetRelRTE, true, true);
  setNamespaceVisibilityForRTE(pstate->p_namespace,
          sourceRelRTE, false, false);
 }
 else      /* MERGE_WHEN_NOT_MATCHED_BY_TARGET */
 {
  /*
   * NOT MATCHED [BY TARGET] actions can't see target relation, but they
   * can see source relation.
 */

  Assert(mergeWhenClause->commandType == CMD_INSERT ||
      mergeWhenClause->commandType == CMD_NOTHING);
  setNamespaceVisibilityForRTE(pstate->p_namespace,
          targetRelRTE, false, false);
  setNamespaceVisibilityForRTE(pstate->p_namespace,
          sourceRelRTE, true, true);
 }
}

/*
 * transformMergeStmt -
 *   transforms a MERGE statement
 */

Query *
transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
{
 Query    *qry = makeNode(Query);
 ListCell   *l;
 AclMode  targetPerms = ACL_NO_RIGHTS;
 bool  is_terminal[NUM_MERGE_MATCH_KINDS];
 Index  sourceRTI;
 List    *mergeActionList;
 ParseNamespaceItem *nsitem;

 /* There can't be any outer WITH to worry about */
 Assert(pstate->p_ctenamespace == NIL);

 qry->commandType = CMD_MERGE;
 qry->hasRecursive = false;

 /* process the WITH clause independently of all else */
 if (stmt->withClause)
 {
  if (stmt->withClause->recursive)
   ereport(ERROR,
     (errcode(ERRCODE_SYNTAX_ERROR),
      errmsg("WITH RECURSIVE is not supported for MERGE statement")));

  qry->cteList = transformWithClause(pstate, stmt->withClause);
  qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
 }

 /*
  * Check WHEN clauses for permissions and sanity
 */

 is_terminal[MERGE_WHEN_MATCHED] = false;
 is_terminal[MERGE_WHEN_NOT_MATCHED_BY_SOURCE] = false;
 is_terminal[MERGE_WHEN_NOT_MATCHED_BY_TARGET] = false;
 foreach(l, stmt->mergeWhenClauses)
 {
  MergeWhenClause *mergeWhenClause = (MergeWhenClause *) lfirst(l);

  /*
   * Collect permissions to check, according to action types. We require
   * SELECT privileges for DO NOTHING because it'd be irregular to have
   * a target relation with zero privileges checked, in case DO NOTHING
   * is the only action.  There's no damage from that: any meaningful
   * MERGE command requires at least some access to the table anyway.
 */

  switch (mergeWhenClause->commandType)
  {
   case CMD_INSERT:
    targetPerms |= ACL_INSERT;
    break;
   case CMD_UPDATE:
    targetPerms |= ACL_UPDATE;
    break;
   case CMD_DELETE:
    targetPerms |= ACL_DELETE;
    break;
   case CMD_NOTHING:
    targetPerms |= ACL_SELECT;
    break;
   default:
    elog(ERROR, "unknown action in MERGE WHEN clause");
  }

  /*
   * Check for unreachable WHEN clauses
 */

  if (is_terminal[mergeWhenClause->matchKind])
   ereport(ERROR,
     (errcode(ERRCODE_SYNTAX_ERROR),
      errmsg("unreachable WHEN clause specified after unconditional WHEN clause")));
  if (mergeWhenClause->condition == NULL)
   is_terminal[mergeWhenClause->matchKind] = true;
 }

 /*
  * Set up the MERGE target table.  The target table is added to the
  * namespace below and to joinlist in transform_MERGE_to_join, so don't do
  * it here.
  *
  * Initially mergeTargetRelation is the same as resultRelation, so data is
  * read from the table being updated.  However, that might be changed by
  * the rewriter, if the target is a trigger-updatable view, to allow
  * target data to be read from the expanded view query while updating the
  * original view relation.
 */

 qry->resultRelation = setTargetTable(pstate, stmt->relation,
           stmt->relation->inh,
           false, targetPerms);
 qry->mergeTargetRelation = qry->resultRelation;

 /* The target relation must be a table or a view */
 if (pstate->p_target_relation->rd_rel->relkind != RELKIND_RELATION &&
  pstate->p_target_relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
  pstate->p_target_relation->rd_rel->relkind != RELKIND_VIEW)
  ereport(ERROR,
    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     errmsg("cannot execute MERGE on relation \"%s\"",
      RelationGetRelationName(pstate->p_target_relation)),
     errdetail_relkind_not_supported(pstate->p_target_relation->rd_rel->relkind)));

 /* Now transform the source relation to produce the source RTE. */
 transformFromClause(pstate,
      list_make1(stmt->sourceRelation));
 sourceRTI = list_length(pstate->p_rtable);
 nsitem = GetNSItemByRangeTablePosn(pstate, sourceRTI, 0);

 /*
  * Check that the target table doesn't conflict with the source table.
  * This would typically be a checkNameSpaceConflicts call, but we want a
  * more specific error message.
 */

 if (strcmp(pstate->p_target_nsitem->p_names->aliasname,
      nsitem->p_names->aliasname) == 0)
  ereport(ERROR,
    errcode(ERRCODE_DUPLICATE_ALIAS),
    errmsg("name \"%s\" specified more than once",
        pstate->p_target_nsitem->p_names->aliasname),
    errdetail("The name is used both as MERGE target table and data source."));

 /*
  * There's no need for a targetlist here; it'll be set up by
  * preprocess_targetlist later.
 */

 qry->targetList = NIL;
 qry->rtable = pstate->p_rtable;
 qry->rteperminfos = pstate->p_rteperminfos;

 /*
  * Transform the join condition.  This includes references to the target
  * side, so add that to the namespace.
 */

 addNSItemToQuery(pstate, pstate->p_target_nsitem, false, true, true);
 qry->mergeJoinCondition = transformExpr(pstate, stmt->joinCondition,
           EXPR_KIND_JOIN_ON);

 /*
  * Create the temporary query's jointree using the joinlist we built using
  * just the source relation; the target relation is not included. The join
  * will be constructed fully by transform_MERGE_to_join.
 */

 qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);

 /* Transform the RETURNING list, if any */
 transformReturningClause(pstate, qry, stmt->returningClause,
        EXPR_KIND_MERGE_RETURNING);

 /*
  * We now have a good query shape, so now look at the WHEN conditions and
  * action targetlists.
  *
  * Overall, the MERGE Query's targetlist is NIL.
  *
  * Each individual action has its own targetlist that needs separate
  * transformation. These transforms don't do anything to the overall
  * targetlist, since that is only used for resjunk columns.
  *
  * We can reference any column in Target or Source, which is OK because
  * both of those already have RTEs. There is nothing like the EXCLUDED
  * pseudo-relation for INSERT ON CONFLICT.
 */

 mergeActionList = NIL;
 foreach(l, stmt->mergeWhenClauses)
 {
  MergeWhenClause *mergeWhenClause = lfirst_node(MergeWhenClause, l);
  MergeAction *action;

  action = makeNode(MergeAction);
  action->commandType = mergeWhenClause->commandType;
  action->matchKind = mergeWhenClause->matchKind;

  /*
   * Set namespace for the specific action. This must be done before
   * analyzing the WHEN quals and the action targetlist.
 */

  setNamespaceForMergeWhen(pstate, mergeWhenClause,
         qry->resultRelation,
         sourceRTI);

  /*
   * Transform the WHEN condition.
   *
   * Note that these quals are NOT added to the join quals; instead they
   * are evaluated separately during execution to decide which of the
   * WHEN MATCHED or WHEN NOT MATCHED actions to execute.
 */

  action->qual = transformWhereClause(pstate, mergeWhenClause->condition,
           EXPR_KIND_MERGE_WHEN, "WHEN");

  /*
   * Transform target lists for each INSERT and UPDATE action stmt
 */

  switch (action->commandType)
  {
   case CMD_INSERT:
    {
     List    *exprList = NIL;
     ListCell   *lc;
     RTEPermissionInfo *perminfo;
     ListCell   *icols;
     ListCell   *attnos;
     List    *icolumns;
     List    *attrnos;

     pstate->p_is_insert = true;

     icolumns = checkInsertTargets(pstate,
              mergeWhenClause->targetList,
              &attrnos);
     Assert(list_length(icolumns) == list_length(attrnos));

     action->override = mergeWhenClause->override;

     /*
      * Handle INSERT much like in transformInsertStmt
 */

     if (mergeWhenClause->values == NIL)
     {
      /*
       * We have INSERT ... DEFAULT VALUES.  We can handle
       * this case by emitting an empty targetlist --- all
       * columns will be defaulted when the planner expands
       * the targetlist.
 */

      exprList = NIL;
     }
     else
     {
      /*
       * Process INSERT ... VALUES with a single VALUES
       * sublist.  We treat this case separately for
       * efficiency.  The sublist is just computed directly
       * as the Query's targetlist, with no VALUES RTE.  So
       * it works just like a SELECT without any FROM.
 */


      /*
       * Do basic expression transformation (same as a ROW()
       * expr, but allow SetToDefault at top level)
 */

      exprList = transformExpressionList(pstate,
                 mergeWhenClause->values,
                 EXPR_KIND_VALUES_SINGLE,
                 true);

      /* Prepare row for assignment to target table */
      exprList = transformInsertRow(pstate, exprList,
               mergeWhenClause->targetList,
               icolumns, attrnos,
               false);
     }

     /*
      * Generate action's target list using the computed list
      * of expressions. Also, mark all the target columns as
      * needing insert permissions.
 */

     perminfo = pstate->p_target_nsitem->p_perminfo;
     forthree(lc, exprList, icols, icolumns, attnos, attrnos)
     {
      Expr    *expr = (Expr *) lfirst(lc);
      ResTarget  *col = lfirst_node(ResTarget, icols);
      AttrNumber attr_num = (AttrNumber) lfirst_int(attnos);
      TargetEntry *tle;

      tle = makeTargetEntry(expr,
             attr_num,
             col->name,
             false);
      action->targetList = lappend(action->targetList, tle);

      perminfo->insertedCols =
       bms_add_member(perminfo->insertedCols,
             attr_num - FirstLowInvalidHeapAttributeNumber);
     }
    }
    break;
   case CMD_UPDATE:
    {
     pstate->p_is_insert = false;
     action->targetList =
      transformUpdateTargetList(pstate,
              mergeWhenClause->targetList);
    }
    break;
   case CMD_DELETE:
    break;

   case CMD_NOTHING:
    action->targetList = NIL;
    break;
   default:
    elog(ERROR, "unknown action in MERGE WHEN clause");
  }

  mergeActionList = lappend(mergeActionList, action);
 }

 qry->mergeActionList = mergeActionList;

 qry->hasTargetSRFs = false;
 qry->hasSubLinks = pstate->p_hasSubLinks;

 assign_query_collations(pstate, qry);

 return qry;
}

static void
setNamespaceVisibilityForRTE(List *namespace, RangeTblEntry *rte,
        bool rel_visible,
        bool cols_visible)
{
 ListCell   *lc;

 foreach(lc, namespace)
 {
  ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(lc);

  if (nsitem->p_rte == rte)
  {
   nsitem->p_rel_visible = rel_visible;
   nsitem->p_cols_visible = cols_visible;
   break;
  }
 }
}

Messung V0.5 in Prozent
C=88 H=95 G=91

[Verzeichnis aufwärts0.14unsichere VerbindungÜbersetzung europäischer Sprachen durch Browser2026-09-28]