Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Postgres/src/backend/catalog/   (Postgres Database Version 18.4©)  Datei vom 11.4.2026 mit Größe 14 kB image not shown  

Quelle  pg_subscription.c

  Sprache: C
 

/*-------------------------------------------------------------------------
 *
 * pg_subscription.c
 *  replication subscriptions
 *
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *  src/backend/catalog/pg_subscription.c
 *
 *-------------------------------------------------------------------------
 */


#include "postgres.h"

#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/tableam.h"
#include "catalog/indexing.h"
#include "catalog/pg_subscription.h"
#include "catalog/pg_subscription_rel.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "storage/lmgr.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/pg_lsn.h"
#include "utils/rel.h"
#include "utils/syscache.h"

static List *textarray_to_stringlist(ArrayType *textarray);

/*
 * Add a comma-separated list of publication names to the 'dest' string.
 */

void
GetPublicationsStr(List *publications, StringInfo dest, bool quote_literal)
{
 ListCell   *lc;
 bool  first = true;

 Assert(publications != NIL);

 foreach(lc, publications)
 {
  char    *pubname = strVal(lfirst(lc));

  if (first)
   first = false;
  else
   appendStringInfoString(dest, ", ");

  if (quote_literal)
   appendStringInfoString(dest, quote_literal_cstr(pubname));
  else
  {
   appendStringInfoChar(dest, '"');
   appendStringInfoString(dest, pubname);
   appendStringInfoChar(dest, '"');
  }
 }
}

/*
 * Fetch the subscription from the syscache.
 */

Subscription *
GetSubscription(Oid subid, bool missing_ok)
{
 HeapTuple tup;
 Subscription *sub;
 Form_pg_subscription subform;
 Datum  datum;
 bool  isnull;

 tup = SearchSysCache1(SUBSCRIPTIONOID, ObjectIdGetDatum(subid));

 if (!HeapTupleIsValid(tup))
 {
  if (missing_ok)
   return NULL;

  elog(ERROR, "cache lookup failed for subscription %u", subid);
 }

 subform = (Form_pg_subscription) GETSTRUCT(tup);

 sub = (Subscription *) palloc(sizeof(Subscription));
 sub->oid = subid;
 sub->dbid = subform->subdbid;
 sub->skiplsn = subform->subskiplsn;
 sub->name = pstrdup(NameStr(subform->subname));
 sub->owner = subform->subowner;
 sub->enabled = subform->subenabled;
 sub->binary = subform->subbinary;
 sub->stream = subform->substream;
 sub->twophasestate = subform->subtwophasestate;
 sub->disableonerr = subform->subdisableonerr;
 sub->passwordrequired = subform->subpasswordrequired;
 sub->runasowner = subform->subrunasowner;
 sub->failover = subform->subfailover;

 /* Get conninfo */
 datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID,
           tup,
           Anum_pg_subscription_subconninfo);
 sub->conninfo = TextDatumGetCString(datum);

 /* Get slotname */
 datum = SysCacheGetAttr(SUBSCRIPTIONOID,
       tup,
       Anum_pg_subscription_subslotname,
       &isnull);
 if (!isnull)
  sub->slotname = pstrdup(NameStr(*DatumGetName(datum)));
 else
  sub->slotname = NULL;

 /* Get synccommit */
 datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID,
           tup,
           Anum_pg_subscription_subsynccommit);
 sub->synccommit = TextDatumGetCString(datum);

 /* Get publications */
 datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID,
           tup,
           Anum_pg_subscription_subpublications);
 sub->publications = textarray_to_stringlist(DatumGetArrayTypeP(datum));

 /* Get origin */
 datum = SysCacheGetAttrNotNull(SUBSCRIPTIONOID,
           tup,
           Anum_pg_subscription_suborigin);
 sub->origin = TextDatumGetCString(datum);

 /* Is the subscription owner a superuser? */
 sub->ownersuperuser = superuser_arg(sub->owner);

 ReleaseSysCache(tup);

 return sub;
}

/*
 * Return number of subscriptions defined in given database.
 * Used by dropdb() to check if database can indeed be dropped.
 */

int
CountDBSubscriptions(Oid dbid)
{
 int   nsubs = 0;
 Relation rel;
 ScanKeyData scankey;
 SysScanDesc scan;
 HeapTuple tup;

 rel = table_open(SubscriptionRelationId, RowExclusiveLock);

 ScanKeyInit(&scankey,
    Anum_pg_subscription_subdbid,
    BTEqualStrategyNumber, F_OIDEQ,
    ObjectIdGetDatum(dbid));

 scan = systable_beginscan(rel, InvalidOid, false,
         NULL, 1, &scankey);

 while (HeapTupleIsValid(tup = systable_getnext(scan)))
  nsubs++;

 systable_endscan(scan);

 table_close(rel, NoLock);

 return nsubs;
}

/*
 * Free memory allocated by subscription struct.
 */

void
FreeSubscription(Subscription *sub)
{
 pfree(sub->name);
 pfree(sub->conninfo);
 if (sub->slotname)
  pfree(sub->slotname);
 list_free_deep(sub->publications);
 pfree(sub);
}

/*
 * Disable the given subscription.
 */

void
DisableSubscription(Oid subid)
{
 Relation rel;
 bool  nulls[Natts_pg_subscription];
 bool  replaces[Natts_pg_subscription];
 Datum  values[Natts_pg_subscription];
 HeapTuple tup;

 /* Look up the subscription in the catalog */
 rel = table_open(SubscriptionRelationId, RowExclusiveLock);
 tup = SearchSysCacheCopy1(SUBSCRIPTIONOID, ObjectIdGetDatum(subid));

 if (!HeapTupleIsValid(tup))
  elog(ERROR, "cache lookup failed for subscription %u", subid);

 LockSharedObject(SubscriptionRelationId, subid, 0, AccessShareLock);

 /* Form a new tuple. */
 memset(values, 0sizeof(values));
 memset(nulls, falsesizeof(nulls));
 memset(replaces, falsesizeof(replaces));

 /* Set the subscription to disabled. */
 values[Anum_pg_subscription_subenabled - 1] = BoolGetDatum(false);
 replaces[Anum_pg_subscription_subenabled - 1] = true;

 /* Update the catalog */
 tup = heap_modify_tuple(tup, RelationGetDescr(rel), values, nulls,
       replaces);
 CatalogTupleUpdate(rel, &tup->t_self, tup);
 heap_freetuple(tup);

 table_close(rel, NoLock);
}

/*
 * Convert text array to list of strings.
 *
 * Note: the resulting list of strings is pallocated here.
 */

static List *
textarray_to_stringlist(ArrayType *textarray)
{
 Datum    *elems;
 int   nelems,
    i;
 List    *res = NIL;

 deconstruct_array_builtin(textarray, TEXTOID, &elems, NULL, &nelems);

 if (nelems == 0)
  return NIL;

 for (i = 0; i < nelems; i++)
  res = lappend(res, makeString(TextDatumGetCString(elems[i])));

 return res;
}

/*
 * Add new state record for a subscription table.
 *
 * If retain_lock is true, then don't release the locks taken in this function.
 * We normally release the locks at the end of transaction but in binary-upgrade
 * mode, we expect to release those immediately.
 */

void
AddSubscriptionRelState(Oid subid, Oid relid, char state,
      XLogRecPtr sublsn, bool retain_lock)
{
 Relation rel;
 HeapTuple tup;
 bool  nulls[Natts_pg_subscription_rel];
 Datum  values[Natts_pg_subscription_rel];

 LockSharedObject(SubscriptionRelationId, subid, 0, AccessShareLock);

 rel = table_open(SubscriptionRelRelationId, RowExclusiveLock);

 /* Try finding existing mapping. */
 tup = SearchSysCacheCopy2(SUBSCRIPTIONRELMAP,
         ObjectIdGetDatum(relid),
         ObjectIdGetDatum(subid));
 if (HeapTupleIsValid(tup))
  elog(ERROR, "subscription table %u in subscription %u already exists",
    relid, subid);

 /* Form the tuple. */
 memset(values, 0sizeof(values));
 memset(nulls, falsesizeof(nulls));
 values[Anum_pg_subscription_rel_srsubid - 1] = ObjectIdGetDatum(subid);
 values[Anum_pg_subscription_rel_srrelid - 1] = ObjectIdGetDatum(relid);
 values[Anum_pg_subscription_rel_srsubstate - 1] = CharGetDatum(state);
 if (sublsn != InvalidXLogRecPtr)
  values[Anum_pg_subscription_rel_srsublsn - 1] = LSNGetDatum(sublsn);
 else
  nulls[Anum_pg_subscription_rel_srsublsn - 1] = true;

 tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);

 /* Insert tuple into catalog. */
 CatalogTupleInsert(rel, tup);

 heap_freetuple(tup);

 /* Cleanup. */
 if (retain_lock)
 {
  table_close(rel, NoLock);
 }
 else
 {
  table_close(rel, RowExclusiveLock);
  UnlockSharedObject(SubscriptionRelationId, subid, 0, AccessShareLock);
 }
}

/*
 * Update the state of a subscription table.
 */

void
UpdateSubscriptionRelState(Oid subid, Oid relid, char state,
         XLogRecPtr sublsn, bool already_locked)
{
 Relation rel;
 HeapTuple tup;
 bool  nulls[Natts_pg_subscription_rel];
 Datum  values[Natts_pg_subscription_rel];
 bool  replaces[Natts_pg_subscription_rel];

 if (already_locked)
 {
#ifdef USE_ASSERT_CHECKING
  LOCKTAG  tag;

  Assert(CheckRelationOidLockedByMe(SubscriptionRelRelationId,
            RowExclusiveLock, true));
  SET_LOCKTAG_OBJECT(tag, InvalidOid, SubscriptionRelationId, subid, 0);
  Assert(LockHeldByMe(&tag, AccessShareLock, true));
#endif

  rel = table_open(SubscriptionRelRelationId, NoLock);
 }
 else
 {
  LockSharedObject(SubscriptionRelationId, subid, 0, AccessShareLock);
  rel = table_open(SubscriptionRelRelationId, RowExclusiveLock);
 }

 /* Try finding existing mapping. */
 tup = SearchSysCacheCopy2(SUBSCRIPTIONRELMAP,
         ObjectIdGetDatum(relid),
         ObjectIdGetDatum(subid));
 if (!HeapTupleIsValid(tup))
  elog(ERROR, "subscription table %u in subscription %u does not exist",
    relid, subid);

 /* Update the tuple. */
 memset(values, 0sizeof(values));
 memset(nulls, falsesizeof(nulls));
 memset(replaces, falsesizeof(replaces));

 replaces[Anum_pg_subscription_rel_srsubstate - 1] = true;
 values[Anum_pg_subscription_rel_srsubstate - 1] = CharGetDatum(state);

 replaces[Anum_pg_subscription_rel_srsublsn - 1] = true;
 if (sublsn != InvalidXLogRecPtr)
  values[Anum_pg_subscription_rel_srsublsn - 1] = LSNGetDatum(sublsn);
 else
  nulls[Anum_pg_subscription_rel_srsublsn - 1] = true;

 tup = heap_modify_tuple(tup, RelationGetDescr(rel), values, nulls,
       replaces);

 /* Update the catalog. */
 CatalogTupleUpdate(rel, &tup->t_self, tup);

 /* Cleanup. */
 table_close(rel, NoLock);
}

/*
 * Get state of subscription table.
 *
 * Returns SUBREL_STATE_UNKNOWN when the table is not in the subscription.
 */

char
GetSubscriptionRelState(Oid subid, Oid relid, XLogRecPtr *sublsn)
{
 HeapTuple tup;
 char  substate;
 bool  isnull;
 Datum  d;
 Relation rel;

 /*
  * This is to avoid the race condition with AlterSubscription which tries
  * to remove this relstate.
 */

 rel = table_open(SubscriptionRelRelationId, AccessShareLock);

 /* Try finding the mapping. */
 tup = SearchSysCache2(SUBSCRIPTIONRELMAP,
        ObjectIdGetDatum(relid),
        ObjectIdGetDatum(subid));

 if (!HeapTupleIsValid(tup))
 {
  table_close(rel, AccessShareLock);
  *sublsn = InvalidXLogRecPtr;
  return SUBREL_STATE_UNKNOWN;
 }

 /* Get the state. */
 substate = ((Form_pg_subscription_rel) GETSTRUCT(tup))->srsubstate;

 /* Get the LSN */
 d = SysCacheGetAttr(SUBSCRIPTIONRELMAP, tup,
      Anum_pg_subscription_rel_srsublsn, &isnull);
 if (isnull)
  *sublsn = InvalidXLogRecPtr;
 else
  *sublsn = DatumGetLSN(d);

 /* Cleanup */
 ReleaseSysCache(tup);

 table_close(rel, AccessShareLock);

 return substate;
}

/*
 * Drop subscription relation mapping. These can be for a particular
 * subscription, or for a particular relation, or both.
 */

void
RemoveSubscriptionRel(Oid subid, Oid relid)
{
 Relation rel;
 TableScanDesc scan;
 ScanKeyData skey[2];
 HeapTuple tup;
 int   nkeys = 0;

 rel = table_open(SubscriptionRelRelationId, RowExclusiveLock);

 if (OidIsValid(subid))
 {
  ScanKeyInit(&skey[nkeys++],
     Anum_pg_subscription_rel_srsubid,
     BTEqualStrategyNumber,
     F_OIDEQ,
     ObjectIdGetDatum(subid));
 }

 if (OidIsValid(relid))
 {
  ScanKeyInit(&skey[nkeys++],
     Anum_pg_subscription_rel_srrelid,
     BTEqualStrategyNumber,
     F_OIDEQ,
     ObjectIdGetDatum(relid));
 }

 /* Do the search and delete what we found. */
 scan = table_beginscan_catalog(rel, nkeys, skey);
 while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
 {
  Form_pg_subscription_rel subrel;

  subrel = (Form_pg_subscription_rel) GETSTRUCT(tup);

  /*
   * We don't allow to drop the relation mapping when the table
   * synchronization is in progress unless the caller updates the
   * corresponding subscription as well. This is to ensure that we don't
   * leave tablesync slots or origins in the system when the
   * corresponding table is dropped.
 */

  if (!OidIsValid(subid) && subrel->srsubstate != SUBREL_STATE_READY)
  {
   ereport(ERROR,
     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
      errmsg("could not drop relation mapping for subscription \"%s\"",
       get_subscription_name(subrel->srsubid, false)),
      errdetail("Table synchronization for relation \"%s\" is in progress and is in state \"%c\".",
          get_rel_name(relid), subrel->srsubstate),

   /*
    * translator: first %s is a SQL ALTER command and second %s is a
    * SQL DROP command
 */

      errhint("Use %s to enable subscription if not already enabled or use %s to drop the subscription.",
        "ALTER SUBSCRIPTION ... ENABLE",
        "DROP SUBSCRIPTION ...")));
  }

  CatalogTupleDelete(rel, &tup->t_self);
 }
 table_endscan(scan);

 table_close(rel, RowExclusiveLock);
}

/*
 * Does the subscription have any relations?
 *
 * Use this function only to know true/false, and when you have no need for the
 * List returned by GetSubscriptionRelations.
 */

bool
HasSubscriptionRelations(Oid subid)
{
 Relation rel;
 ScanKeyData skey[1];
 SysScanDesc scan;
 bool  has_subrels;

 rel = table_open(SubscriptionRelRelationId, AccessShareLock);

 ScanKeyInit(&skey[0],
    Anum_pg_subscription_rel_srsubid,
    BTEqualStrategyNumber, F_OIDEQ,
    ObjectIdGetDatum(subid));

 scan = systable_beginscan(rel, InvalidOid, false,
         NULL, 1, skey);

 /* If even a single tuple exists then the subscription has tables. */
 has_subrels = HeapTupleIsValid(systable_getnext(scan));

 /* Cleanup */
 systable_endscan(scan);
 table_close(rel, AccessShareLock);

 return has_subrels;
}

/*
 * Get the relations for the subscription.
 *
 * If not_ready is true, return only the relations that are not in a ready
 * state, otherwise return all the relations of the subscription.  The
 * returned list is palloc'ed in the current memory context.
 */

List *
GetSubscriptionRelations(Oid subid, bool not_ready)
{
 List    *res = NIL;
 Relation rel;
 HeapTuple tup;
 int   nkeys = 0;
 ScanKeyData skey[2];
 SysScanDesc scan;

 rel = table_open(SubscriptionRelRelationId, AccessShareLock);

 ScanKeyInit(&skey[nkeys++],
    Anum_pg_subscription_rel_srsubid,
    BTEqualStrategyNumber, F_OIDEQ,
    ObjectIdGetDatum(subid));

 if (not_ready)
  ScanKeyInit(&skey[nkeys++],
     Anum_pg_subscription_rel_srsubstate,
     BTEqualStrategyNumber, F_CHARNE,
     CharGetDatum(SUBREL_STATE_READY));

 scan = systable_beginscan(rel, InvalidOid, false,
         NULL, nkeys, skey);

 while (HeapTupleIsValid(tup = systable_getnext(scan)))
 {
  Form_pg_subscription_rel subrel;
  SubscriptionRelState *relstate;
  Datum  d;
  bool  isnull;

  subrel = (Form_pg_subscription_rel) GETSTRUCT(tup);

  relstate = (SubscriptionRelState *) palloc(sizeof(SubscriptionRelState));
  relstate->relid = subrel->srrelid;
  relstate->state = subrel->srsubstate;
  d = SysCacheGetAttr(SUBSCRIPTIONRELMAP, tup,
       Anum_pg_subscription_rel_srsublsn, &isnull);
  if (isnull)
   relstate->lsn = InvalidXLogRecPtr;
  else
   relstate->lsn = DatumGetLSN(d);

  res = lappend(res, relstate);
 }

 /* Cleanup */
 systable_endscan(scan);
 table_close(rel, AccessShareLock);

 return res;
}

Messung V0.5 in Prozent
C=94 H=94 G=93

¤ 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.