Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  parse_merge.c

  Sprache: C
 

/*-------------------------------------------------------------------------
 *
 * 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, truetrue);
  setNamespaceVisibilityForRTE(pstate->p_namespace,
          sourceRelRTE, truetrue);
 }
 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, truetrue);
  setNamespaceVisibilityForRTE(pstate->p_namespace,
          sourceRelRTE, falsefalse);
 }
 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, falsefalse);
  setNamespaceVisibilityForRTE(pstate->p_namespace,
          sourceRelRTE, truetrue);
 }
}

/*
 * 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, falsetruetrue);
 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=94 H=100 G=96

¤ Dauer der Verarbeitung: 0.14 Sekunden  (vorverarbeitet am  2026-08-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=738142