Re: Fixing findDependentObjects()'s dependency on scan order (regressions in DROP diagnostic messages)

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Re: Fixing findDependentObjects()'s dependency on scan order (regressions in DROP diagnostic messages)
Дата
Msg-id 24613.1549817419@sss.pgh.pa.us
обсуждение исходный текст
Ответ на Re: Fixing findDependentObjects()'s dependency on scan order (regressions in DROP diagnostic messages)  (Tom Lane <tgl@sss.pgh.pa.us>)
Ответы Re: Fixing findDependentObjects()'s dependency on scan order(regressions in DROP diagnostic messages)  (Peter Geoghegan <pg@bowt.ie>)
Список pgsql-hackers
I wrote:
> Peter Geoghegan <pg@bowt.ie> writes:
>>> [ invent separate primary and secondary partition dependencies? ]

>> I lean towards changing these on HEAD, ...

> Me too.

Here's a version of the patch that does it that way.

            regards, tom lane

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 6dd0700..ae69a9e 100644
*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
*************** SCRAM-SHA-256$<replaceable><iteration
*** 2970,2976 ****
         referenced object, and should be automatically dropped
         (regardless of <literal>RESTRICT</literal> or <literal>CASCADE</literal>
         mode) if the referenced object is dropped.  Example: a named
!        constraint on a table is made autodependent on the table, so
         that it will go away if the table is dropped.
        </para>
       </listitem>
--- 2970,2976 ----
         referenced object, and should be automatically dropped
         (regardless of <literal>RESTRICT</literal> or <literal>CASCADE</literal>
         mode) if the referenced object is dropped.  Example: a named
!        constraint on a table is made auto-dependent on the table, so
         that it will go away if the table is dropped.
        </para>
       </listitem>
*************** SCRAM-SHA-256$<replaceable><iteration
*** 2982,3019 ****
        <para>
         The dependent object was created as part of creation of the
         referenced object, and is really just a part of its internal
!        implementation.  A <command>DROP</command> of the dependent object
!        will be disallowed outright (we'll tell the user to issue a
!        <command>DROP</command> against the referenced object, instead).  A
!        <command>DROP</command> of the referenced object will be propagated
!        through to drop the dependent object whether
!        <command>CASCADE</command> is specified or not.  Example: a trigger
!        that's created to enforce a foreign-key constraint is made
!        internally dependent on the constraint's
!        <structname>pg_constraint</structname> entry.
        </para>
       </listitem>
      </varlistentry>

      <varlistentry>
!      <term><symbol>DEPENDENCY_INTERNAL_AUTO</symbol> (<literal>I</literal>)</term>
       <listitem>
        <para>
         The dependent object was created as part of creation of the
         referenced object, and is really just a part of its internal
!        implementation.  A <command>DROP</command> of the dependent object
!        will be disallowed outright (we'll tell the user to issue a
!        <command>DROP</command> against the referenced object, instead).
!        While a regular internal dependency will prevent
!        the dependent object from being dropped while any such dependencies
!        remain, <literal>DEPENDENCY_INTERNAL_AUTO</literal> will allow such
!        a drop as long as the object can be found by following any of such
!        dependencies.
!        Example: an index on a partition is made internal-auto-dependent on
!        both the partition itself as well as on the index on the parent
!        partitioned table; so the partition index is dropped together with
!        either the partition it indexes, or with the parent index it is
!        attached to.
        </para>
       </listitem>
      </varlistentry>
--- 2982,3052 ----
        <para>
         The dependent object was created as part of creation of the
         referenced object, and is really just a part of its internal
!        implementation.  A direct <command>DROP</command> of the dependent
!        object will be disallowed outright (we'll tell the user to issue
!        a <command>DROP</command> against the referenced object, instead).
!        A <command>DROP</command> of the referenced object will result in
!        automatically dropping the dependent object
!        whether <literal>CASCADE</literal> is specified or not.  If the
!        dependent object is reached due to a dependency on some other object,
!        the drop is converted to a drop of the referenced object, so
!        that <literal>NORMAL</literal> and <literal>AUTO</literal>
!        dependencies of the dependent object behave much like they were
!        dependencies of the referenced object.
!        Example: a view's <literal>ON SELECT</literal> rule is made
!        internally dependent on the view, preventing it from being dropped
!        while the view remains.  Dependencies of the rule (such as tables it
!        refers to) act as if they were dependencies of the view.
        </para>
       </listitem>
      </varlistentry>

      <varlistentry>
!      <term><symbol>DEPENDENCY_PARTITION_PRI</symbol> (<literal>P</literal>)</term>
       <listitem>
        <para>
         The dependent object was created as part of creation of the
         referenced object, and is really just a part of its internal
!        implementation; however, unlike <literal>INTERNAL</literal>,
!        there is more than one such referenced object.  The dependent object
!        must not be dropped unless at least one of these referenced objects
!        is dropped; if any one is, the dependent object should be dropped
!        whether or not <literal>CASCADE</literal> is specified.  Also
!        unlike <literal>INTERNAL</literal>, a drop of some other object
!        that the dependent object depends on does not result in automatic
!        deletion of any partition-referenced object.  Hence, if the drop
!        does not cascade to at least one of these objects via some other
!        path, it will be refused.  (In most cases, the dependent object
!        shares all its non-partition dependencies with at least one
!        partition-referenced object, so that this restriction does not
!        result in blocking any cascaded delete.)
!        Note that partition dependencies are made in addition to, not
!        instead of, any dependencies the object would normally have.  This
!        simplifies <command>ATTACH/DETACH PARTITION</command> operations:
!        the partition dependencies need only be added or removed.
!        Example: a child partitioned index is made partition-dependent
!        on both the partition table it is on and the parent partitioned
!        index, so that it goes away if either of those is dropped, but
!        not otherwise.
!       </para>
!      </listitem>
!     </varlistentry>
!
!     <varlistentry>
!      <term><symbol>DEPENDENCY_PARTITION_SEC</symbol> (<literal>S</literal>)</term>
!      <listitem>
!       <para>
!        A <quote>secondary</quote> partition dependency acts identically to
!        a primary one, except that the primary dependency is preferentially
!        referenced in error messages.  An object should have at most one
!        primary partition dependency, but there could perhaps be multiple
!        secondary dependencies.
!        Example: actually, we'll set up a child partitioned index with the
!        parent partitioned index as primary partition dependency and the
!        partition table as secondary partition dependency.  In this way,
!        if the user tries to drop the child partitioned index, the error
!        message will suggest dropping the parent partitioned index instead
!        (not the table).
        </para>
       </listitem>
      </varlistentry>
*************** SCRAM-SHA-256$<replaceable><iteration
*** 3026,3034 ****
         the referenced object (see
         <link linkend="catalog-pg-extension"><structname>pg_extension</structname></link>).
         The dependent object can be dropped only via
!        <command>DROP EXTENSION</command> on the referenced object.  Functionally
!        this dependency type acts the same as an internal dependency, but
!        it's kept separate for clarity and to simplify <application>pg_dump</application>.
        </para>
       </listitem>
      </varlistentry>
--- 3059,3068 ----
         the referenced object (see
         <link linkend="catalog-pg-extension"><structname>pg_extension</structname></link>).
         The dependent object can be dropped only via
!        <command>DROP EXTENSION</command> on the referenced object.
!        Functionally this dependency type acts the same as
!        an <literal>INTERNAL</literal> dependency, but it's kept separate for
!        clarity and to simplify <application>pg_dump</application>.
        </para>
       </listitem>
      </varlistentry>
*************** SCRAM-SHA-256$<replaceable><iteration
*** 3038,3047 ****
       <listitem>
        <para>
         The dependent object is not a member of the extension that is the
!        referenced object (and so should not be ignored by pg_dump), but
!        cannot function without it and should be dropped when the
!        extension itself is. The dependent object may be dropped on its
!        own as well.
        </para>
       </listitem>
      </varlistentry>
--- 3072,3084 ----
       <listitem>
        <para>
         The dependent object is not a member of the extension that is the
!        referenced object (and so it should not be ignored
!        by <application>pg_dump</application>), but it cannot function
!        without the extension and should be auto-dropped if the extension is.
!        The dependent object may be dropped on its own as well.
!        Functionally this dependency type acts the same as
!        an <literal>AUTO</literal> dependency, but it's kept separate for
!        clarity and to simplify <application>pg_dump</application>.
        </para>
       </listitem>
      </varlistentry>
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 2c54895..e001334 100644
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
*************** typedef struct
*** 99,107 ****
  #define DEPFLAG_NORMAL        0x0002    /* reached via normal dependency */
  #define DEPFLAG_AUTO        0x0004    /* reached via auto dependency */
  #define DEPFLAG_INTERNAL    0x0008    /* reached via internal dependency */
! #define DEPFLAG_EXTENSION    0x0010    /* reached via extension dependency */
! #define DEPFLAG_REVERSE        0x0020    /* reverse internal/extension link */
! #define DEPFLAG_SUBOBJECT    0x0040    /* subobject of another deletable object */


  /* expansible list of ObjectAddresses */
--- 99,109 ----
  #define DEPFLAG_NORMAL        0x0002    /* reached via normal dependency */
  #define DEPFLAG_AUTO        0x0004    /* reached via auto dependency */
  #define DEPFLAG_INTERNAL    0x0008    /* reached via internal dependency */
! #define DEPFLAG_PARTITION    0x0010    /* reached via partition dependency */
! #define DEPFLAG_EXTENSION    0x0020    /* reached via extension dependency */
! #define DEPFLAG_REVERSE        0x0040    /* reverse internal/extension link */
! #define DEPFLAG_IS_PART        0x0080    /* has a partition dependency */
! #define DEPFLAG_SUBOBJECT    0x0100    /* subobject of another deletable object */


  /* expansible list of ObjectAddresses */
*************** findDependentObjects(const ObjectAddress
*** 478,483 ****
--- 480,487 ----
      SysScanDesc scan;
      HeapTuple    tup;
      ObjectAddress otherObject;
+     ObjectAddress owningObject;
+     ObjectAddress partitionObject;
      ObjectAddressAndFlags *dependentObjects;
      int            numDependentObjects;
      int            maxDependentObjects;
*************** findDependentObjects(const ObjectAddress
*** 547,552 ****
--- 551,560 ----
      scan = systable_beginscan(*depRel, DependDependerIndexId, true,
                                NULL, nkeys, key);

+     /* initialize variables that loop may fill */
+     memset(&owningObject, 0, sizeof(owningObject));
+     memset(&partitionObject, 0, sizeof(partitionObject));
+
      while (HeapTupleIsValid(tup = systable_getnext(scan)))
      {
          Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);
*************** findDependentObjects(const ObjectAddress
*** 591,614 ****
                  /* FALL THRU */

              case DEPENDENCY_INTERNAL:
-             case DEPENDENCY_INTERNAL_AUTO:

                  /*
                   * This object is part of the internal implementation of
                   * another object, or is part of the extension that is the
                   * other object.  We have three cases:
                   *
!                  * 1. At the outermost recursion level, disallow the DROP. (We
!                  * just ereport here, rather than proceeding, since no other
!                  * dependencies are likely to be interesting.)    However, if
!                  * the owning object is listed in pendingObjects, just release
!                  * the caller's lock and return; we'll eventually complete the
!                  * DROP when we reach that entry in the pending list.
                   */
                  if (stack == NULL)
                  {
-                     char       *otherObjDesc;
-
                      if (pendingObjects &&
                          object_address_present(&otherObject, pendingObjects))
                      {
--- 599,624 ----
                  /* FALL THRU */

              case DEPENDENCY_INTERNAL:

                  /*
                   * This object is part of the internal implementation of
                   * another object, or is part of the extension that is the
                   * other object.  We have three cases:
                   *
!                  * 1. At the outermost recursion level, we must disallow the
!                  * DROP.  However, if the owning object is listed in
!                  * pendingObjects, just release the caller's lock and return;
!                  * we'll eventually complete the DROP when we reach that entry
!                  * in the pending list.
!                  *
!                  * Note: the above statement is true only if this pg_depend
!                  * entry still exists by then; in principle, therefore, we
!                  * could miss deleting an item the user told us to delete.
!                  * However, no inconsistency can result: since we're at outer
!                  * level, there is no object depending on this one.
                   */
                  if (stack == NULL)
                  {
                      if (pendingObjects &&
                          object_address_present(&otherObject, pendingObjects))
                      {
*************** findDependentObjects(const ObjectAddress
*** 617,630 ****
                          ReleaseDeletionLock(object);
                          return;
                      }
!                     otherObjDesc = getObjectDescription(&otherObject);
!                     ereport(ERROR,
!                             (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
!                              errmsg("cannot drop %s because %s requires it",
!                                     getObjectDescription(object),
!                                     otherObjDesc),
!                              errhint("You can drop %s instead.",
!                                      otherObjDesc)));
                  }

                  /*
--- 627,643 ----
                          ReleaseDeletionLock(object);
                          return;
                      }
!
!                     /*
!                      * We postpone actually issuing the error message until
!                      * after this loop, so that we can make the behavior
!                      * independent of the ordering of pg_depend entries, at
!                      * least so long as there's just one INTERNAL + EXTENSION
!                      * dependency.  (If there's more, we'll complain about a
!                      * random one of them.)
!                      */
!                     owningObject = otherObject;
!                     break;
                  }

                  /*
*************** findDependentObjects(const ObjectAddress
*** 643,656 ****
                   * transform this deletion request into a delete of this
                   * owning object.
                   *
-                  * For INTERNAL_AUTO dependencies, we don't enforce this; in
-                  * other words, we don't follow the links back to the owning
-                  * object.
-                  */
-                 if (foundDep->deptype == DEPENDENCY_INTERNAL_AUTO)
-                     break;
-
-                 /*
                   * First, release caller's lock on this object and get
                   * deletion lock on the owning object.  (We must release
                   * caller's lock to avoid deadlock against a concurrent
--- 656,661 ----
*************** findDependentObjects(const ObjectAddress
*** 673,678 ****
--- 678,690 ----
                  }

                  /*
+                  * One way or the other, we're done with the scan; might as
+                  * well close it down before recursing, to reduce peak
+                  * resource consumption.
+                  */
+                 systable_endscan(scan);
+
+                 /*
                   * Okay, recurse to the owning object instead of proceeding.
                   *
                   * We do not need to stack the current object; we want the
*************** findDependentObjects(const ObjectAddress
*** 690,699 ****
                                       targetObjects,
                                       pendingObjects,
                                       depRel);
                  /* And we're done here. */
-                 systable_endscan(scan);
                  return;

              case DEPENDENCY_PIN:

                  /*
--- 702,767 ----
                                       targetObjects,
                                       pendingObjects,
                                       depRel);
+
+                 /*
+                  * The current target object should have been added to
+                  * targetObjects while processing the owning object; but it
+                  * probably got only the flag bits associated with the
+                  * dependency we're looking at.  We need to add the objflags
+                  * that were passed to this recursion level, too, else we may
+                  * get a bogus failure in reportDependentObjects (if, for
+                  * example, we were called due to a partition dependency).
+                  *
+                  * If somehow the current object didn't get scheduled for
+                  * deletion, bleat.  (That would imply that somebody deleted
+                  * this dependency record before the recursion got to it.)
+                  * Another idea would be to reacquire lock on the current
+                  * object and resume trying to delete it, but it seems not
+                  * worth dealing with the race conditions inherent in that.
+                  */
+                 if (!object_address_present_add_flags(object, objflags,
+                                                       targetObjects))
+                     elog(ERROR, "deletion of owning object %s failed to delete %s",
+                          getObjectDescription(&otherObject),
+                          getObjectDescription(object));
+
                  /* And we're done here. */
                  return;

+             case DEPENDENCY_PARTITION_PRI:
+
+                 /*
+                  * Remember that this object has a partition-type dependency.
+                  * After the dependency scan, we'll complain if we didn't find
+                  * a reason to delete one of its partition dependencies.
+                  */
+                 objflags |= DEPFLAG_IS_PART;
+
+                 /*
+                  * Also remember the primary partition owner, for error
+                  * messages.  If there are multiple primary owners (which
+                  * there should not be), we'll report a random one of them.
+                  */
+                 partitionObject = otherObject;
+                 break;
+
+             case DEPENDENCY_PARTITION_SEC:
+
+                 /*
+                  * Only use secondary partition owners in error messages if we
+                  * find no primary owner (which probably shouldn't happen).
+                  */
+                 if (!(objflags & DEPFLAG_IS_PART))
+                     partitionObject = otherObject;
+
+                 /*
+                  * Remember that this object has a partition-type dependency.
+                  * After the dependency scan, we'll complain if we didn't find
+                  * a reason to delete one of its partition dependencies.
+                  */
+                 objflags |= DEPFLAG_IS_PART;
+                 break;
+
              case DEPENDENCY_PIN:

                  /*
*************** findDependentObjects(const ObjectAddress
*** 713,718 ****
--- 781,808 ----
      systable_endscan(scan);

      /*
+      * If we found an INTERNAL or EXTENSION dependency when we're at outer
+      * level, complain about it now.  If we also found a PARTITION dependency,
+      * we prefer to report the PARTITION dependency.  This is arbitrary but
+      * seems to be more useful in practice.
+      */
+     if (OidIsValid(owningObject.classId))
+     {
+         char       *otherObjDesc;
+
+         if (OidIsValid(partitionObject.classId))
+             otherObjDesc = getObjectDescription(&partitionObject);
+         else
+             otherObjDesc = getObjectDescription(&owningObject);
+
+         ereport(ERROR,
+                 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
+                  errmsg("cannot drop %s because %s requires it",
+                         getObjectDescription(object), otherObjDesc),
+                  errhint("You can drop %s instead.", otherObjDesc)));
+     }
+
+     /*
       * Next, identify all objects that directly depend on the current object.
       * To ensure predictable deletion order, we collect them up in
       * dependentObjects and sort the list before actually recursing.  (The
*************** findDependentObjects(const ObjectAddress
*** 789,798 ****
              case DEPENDENCY_AUTO_EXTENSION:
                  subflags = DEPFLAG_AUTO;
                  break;
-             case DEPENDENCY_INTERNAL_AUTO:
              case DEPENDENCY_INTERNAL:
                  subflags = DEPFLAG_INTERNAL;
                  break;
              case DEPENDENCY_EXTENSION:
                  subflags = DEPFLAG_EXTENSION;
                  break;
--- 879,891 ----
              case DEPENDENCY_AUTO_EXTENSION:
                  subflags = DEPFLAG_AUTO;
                  break;
              case DEPENDENCY_INTERNAL:
                  subflags = DEPFLAG_INTERNAL;
                  break;
+             case DEPENDENCY_PARTITION_PRI:
+             case DEPENDENCY_PARTITION_SEC:
+                 subflags = DEPFLAG_PARTITION;
+                 break;
              case DEPENDENCY_EXTENSION:
                  subflags = DEPFLAG_EXTENSION;
                  break;
*************** findDependentObjects(const ObjectAddress
*** 868,877 ****
      /*
       * Finally, we can add the target object to targetObjects.  Be careful to
       * include any flags that were passed back down to us from inner recursion
!      * levels.
       */
      extra.flags = mystack.flags;
!     if (stack)
          extra.dependee = *stack->object;
      else
          memset(&extra.dependee, 0, sizeof(extra.dependee));
--- 961,975 ----
      /*
       * Finally, we can add the target object to targetObjects.  Be careful to
       * include any flags that were passed back down to us from inner recursion
!      * levels.  Record the "dependee" as being either the most important
!      * partition owner if there is one, else the object we recursed from, if
!      * any.  (The logic in reportDependentObjects() is such that it can only
!      * need one of those objects.)
       */
      extra.flags = mystack.flags;
!     if (extra.flags & DEPFLAG_IS_PART)
!         extra.dependee = partitionObject;
!     else if (stack)
          extra.dependee = *stack->object;
      else
          memset(&extra.dependee, 0, sizeof(extra.dependee));
*************** reportDependentObjects(const ObjectAddre
*** 906,913 ****
      int            i;

      /*
       * If no error is to be thrown, and the msglevel is too low to be shown to
!      * either client or server log, there's no need to do any of the work.
       *
       * Note: this code doesn't know all there is to be known about elog
       * levels, but it works for NOTICE and DEBUG2, which are the only values
--- 1004,1040 ----
      int            i;

      /*
+      * If we need to delete any partition-dependent objects, make sure that
+      * we're deleting at least one of their partition dependencies, too. That
+      * can be detected by checking that we reached them by a PARTITION
+      * dependency at some point.
+      *
+      * We just report the first such object, as in most cases the only way to
+      * trigger this complaint is to explicitly try to delete one partition of
+      * a partitioned object.
+      */
+     for (i = 0; i < targetObjects->numrefs; i++)
+     {
+         const ObjectAddressExtra *extra = &targetObjects->extras[i];
+
+         if ((extra->flags & DEPFLAG_IS_PART) &&
+             !(extra->flags & DEPFLAG_PARTITION))
+         {
+             const ObjectAddress *object = &targetObjects->refs[i];
+             char       *otherObjDesc = getObjectDescription(&extra->dependee);
+
+             ereport(ERROR,
+                     (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
+                      errmsg("cannot drop %s because %s requires it",
+                             getObjectDescription(object), otherObjDesc),
+                      errhint("You can drop %s instead.", otherObjDesc)));
+         }
+     }
+
+     /*
       * If no error is to be thrown, and the msglevel is too low to be shown to
!      * either client or server log, there's no need to do any of the rest of
!      * the work.
       *
       * Note: this code doesn't know all there is to be known about elog
       * levels, but it works for NOTICE and DEBUG2, which are the only values
*************** reportDependentObjects(const ObjectAddre
*** 951,961 ****

          /*
           * If, at any stage of the recursive search, we reached the object via
!          * an AUTO, INTERNAL, or EXTENSION dependency, then it's okay to
!          * delete it even in RESTRICT mode.
           */
          if (extra->flags & (DEPFLAG_AUTO |
                              DEPFLAG_INTERNAL |
                              DEPFLAG_EXTENSION))
          {
              /*
--- 1078,1089 ----

          /*
           * If, at any stage of the recursive search, we reached the object via
!          * an AUTO, INTERNAL, PARTITION, or EXTENSION dependency, then it's
!          * okay to delete it even in RESTRICT mode.
           */
          if (extra->flags & (DEPFLAG_AUTO |
                              DEPFLAG_INTERNAL |
+                             DEPFLAG_PARTITION |
                              DEPFLAG_EXTENSION))
          {
              /*
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index faf6956..d16c3d0 100644
*** a/src/backend/catalog/index.c
--- b/src/backend/catalog/index.c
*************** index_create(Relation heapRelation,
*** 1041,1049 ****
          else
          {
              bool        have_simple_col = false;
-             DependencyType deptype;
-
-             deptype = OidIsValid(parentIndexRelid) ? DEPENDENCY_INTERNAL_AUTO : DEPENDENCY_AUTO;

              /* Create auto dependencies on simply-referenced columns */
              for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
--- 1041,1046 ----
*************** index_create(Relation heapRelation,
*** 1054,1060 ****
                      referenced.objectId = heapRelationId;
                      referenced.objectSubId = indexInfo->ii_IndexAttrNumbers[i];

!                     recordDependencyOn(&myself, &referenced, deptype);

                      have_simple_col = true;
                  }
--- 1051,1057 ----
                      referenced.objectId = heapRelationId;
                      referenced.objectSubId = indexInfo->ii_IndexAttrNumbers[i];

!                     recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);

                      have_simple_col = true;
                  }
*************** index_create(Relation heapRelation,
*** 1072,1089 ****
                  referenced.objectId = heapRelationId;
                  referenced.objectSubId = 0;

!                 recordDependencyOn(&myself, &referenced, deptype);
              }
          }

!         /* Store dependency on parent index, if any */
          if (OidIsValid(parentIndexRelid))
          {
              referenced.classId = RelationRelationId;
              referenced.objectId = parentIndexRelid;
              referenced.objectSubId = 0;

!             recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL_AUTO);
          }

          /* Store dependency on collations */
--- 1069,1097 ----
                  referenced.objectId = heapRelationId;
                  referenced.objectSubId = 0;

!                 recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
              }
          }

!         /*
!          * If this is an index partition, create partition dependencies on
!          * both the parent index and the table.  (Note: these must be *in
!          * addition to*, not instead of, all other dependencies.  Otherwise
!          * we'll be short some dependencies after DETACH PARTITION.)
!          */
          if (OidIsValid(parentIndexRelid))
          {
              referenced.classId = RelationRelationId;
              referenced.objectId = parentIndexRelid;
              referenced.objectSubId = 0;

!             recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI);
!
!             referenced.classId = RelationRelationId;
!             referenced.objectId = heapRelationId;
!             referenced.objectSubId = 0;
!
!             recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
          }

          /* Store dependency on collations */
*************** index_constraint_create(Relation heapRel
*** 1342,1356 ****
      recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);

      /*
!      * Also, if this is a constraint on a partition, mark it as depending on
!      * the constraint in the parent.
       */
      if (OidIsValid(parentConstraintId))
      {
!         ObjectAddress parentConstr;
!
!         ObjectAddressSet(parentConstr, ConstraintRelationId, parentConstraintId);
!         recordDependencyOn(&referenced, &parentConstr, DEPENDENCY_INTERNAL_AUTO);
      }

      /*
--- 1350,1366 ----
      recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);

      /*
!      * Also, if this is a constraint on a partition, give it partition-type
!      * dependencies on the parent constraint as well as the table.
       */
      if (OidIsValid(parentConstraintId))
      {
!         ObjectAddressSet(myself, ConstraintRelationId, conOid);
!         ObjectAddressSet(referenced, ConstraintRelationId, parentConstraintId);
!         recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI);
!         ObjectAddressSet(referenced, RelationRelationId,
!                          RelationGetRelid(heapRelation));
!         recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
      }

      /*
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 698b493..ad836e0 100644
*** a/src/backend/catalog/pg_constraint.c
--- b/src/backend/catalog/pg_constraint.c
*************** AlterConstraintNamespaces(Oid ownerId, O
*** 760,772 ****

  /*
   * ConstraintSetParentConstraint
!  *        Set a partition's constraint as child of its parent table's
   *
   * This updates the constraint's pg_constraint row to show it as inherited, and
!  * add a dependency to the parent so that it cannot be removed on its own.
   */
  void
! ConstraintSetParentConstraint(Oid childConstrId, Oid parentConstrId)
  {
      Relation    constrRel;
      Form_pg_constraint constrForm;
--- 760,776 ----

  /*
   * ConstraintSetParentConstraint
!  *        Set a partition's constraint as child of its parent constraint,
!  *        or remove the linkage if parentConstrId is InvalidOid.
   *
   * This updates the constraint's pg_constraint row to show it as inherited, and
!  * adds PARTITION dependencies to prevent the constraint from being deleted
!  * on its own.  Alternatively, reverse that.
   */
  void
! ConstraintSetParentConstraint(Oid childConstrId,
!                               Oid parentConstrId,
!                               Oid childTableId)
  {
      Relation    constrRel;
      Form_pg_constraint constrForm;
*************** ConstraintSetParentConstraint(Oid childC
*** 795,804 ****

          CatalogTupleUpdate(constrRel, &tuple->t_self, newtup);

-         ObjectAddressSet(referenced, ConstraintRelationId, parentConstrId);
          ObjectAddressSet(depender, ConstraintRelationId, childConstrId);

!         recordDependencyOn(&depender, &referenced, DEPENDENCY_INTERNAL_AUTO);
      }
      else
      {
--- 799,811 ----

          CatalogTupleUpdate(constrRel, &tuple->t_self, newtup);

          ObjectAddressSet(depender, ConstraintRelationId, childConstrId);

!         ObjectAddressSet(referenced, ConstraintRelationId, parentConstrId);
!         recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_PRI);
!
!         ObjectAddressSet(referenced, RelationRelationId, childTableId);
!         recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_SEC);
      }
      else
      {
*************** ConstraintSetParentConstraint(Oid childC
*** 809,818 ****
          /* Make sure there's no further inheritance. */
          Assert(constrForm->coninhcount == 0);

          deleteDependencyRecordsForClass(ConstraintRelationId, childConstrId,
                                          ConstraintRelationId,
!                                         DEPENDENCY_INTERNAL_AUTO);
!         CatalogTupleUpdate(constrRel, &tuple->t_self, newtup);
      }

      ReleaseSysCache(tuple);
--- 816,829 ----
          /* Make sure there's no further inheritance. */
          Assert(constrForm->coninhcount == 0);

+         CatalogTupleUpdate(constrRel, &tuple->t_self, newtup);
+
          deleteDependencyRecordsForClass(ConstraintRelationId, childConstrId,
                                          ConstraintRelationId,
!                                         DEPENDENCY_PARTITION_PRI);
!         deleteDependencyRecordsForClass(ConstraintRelationId, childConstrId,
!                                         RelationRelationId,
!                                         DEPENDENCY_PARTITION_SEC);
      }

      ReleaseSysCache(tuple);
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index bd85099..7352b9e 100644
*** a/src/backend/commands/indexcmds.c
--- b/src/backend/commands/indexcmds.c
*************** DefineIndex(Oid relationId,
*** 971,977 ****
                          IndexSetParentIndex(cldidx, indexRelationId);
                          if (createdConstraintId != InvalidOid)
                              ConstraintSetParentConstraint(cldConstrOid,
!                                                           createdConstraintId);

                          if (!cldidx->rd_index->indisvalid)
                              invalidate_parent = true;
--- 971,978 ----
                          IndexSetParentIndex(cldidx, indexRelationId);
                          if (createdConstraintId != InvalidOid)
                              ConstraintSetParentConstraint(cldConstrOid,
!                                                           createdConstraintId,
!                                                           childRelid);

                          if (!cldidx->rd_index->indisvalid)
                              invalidate_parent = true;
*************** IndexSetParentIndex(Relation partitionId
*** 2622,2656 ****

      if (fix_dependencies)
      {
-         ObjectAddress partIdx;
-
          /*
!          * Insert/delete pg_depend rows.  If setting a parent, add an
!          * INTERNAL_AUTO dependency to the parent index; if making standalone,
!          * remove all existing rows and put back the regular dependency on the
!          * table.
           */
-         ObjectAddressSet(partIdx, RelationRelationId, partRelid);
-
          if (OidIsValid(parentOid))
          {
              ObjectAddress parentIdx;

              ObjectAddressSet(parentIdx, RelationRelationId, parentOid);
!             recordDependencyOn(&partIdx, &parentIdx, DEPENDENCY_INTERNAL_AUTO);
          }
          else
          {
-             ObjectAddress partitionTbl;
-
-             ObjectAddressSet(partitionTbl, RelationRelationId,
-                              partitionIdx->rd_index->indrelid);
-
              deleteDependencyRecordsForClass(RelationRelationId, partRelid,
                                              RelationRelationId,
!                                             DEPENDENCY_INTERNAL_AUTO);
!
!             recordDependencyOn(&partIdx, &partitionTbl, DEPENDENCY_AUTO);
          }

          /* make our updates visible */
--- 2623,2656 ----

      if (fix_dependencies)
      {
          /*
!          * Insert/delete pg_depend rows.  If setting a parent, add PARTITION
!          * dependencies on the parent index and the table; if removing a
!          * parent, delete PARTITION dependencies.
           */
          if (OidIsValid(parentOid))
          {
+             ObjectAddress partIdx;
              ObjectAddress parentIdx;
+             ObjectAddress partitionTbl;

+             ObjectAddressSet(partIdx, RelationRelationId, partRelid);
              ObjectAddressSet(parentIdx, RelationRelationId, parentOid);
!             ObjectAddressSet(partitionTbl, RelationRelationId,
!                              partitionIdx->rd_index->indrelid);
!             recordDependencyOn(&partIdx, &parentIdx,
!                                DEPENDENCY_PARTITION_PRI);
!             recordDependencyOn(&partIdx, &partitionTbl,
!                                DEPENDENCY_PARTITION_SEC);
          }
          else
          {
              deleteDependencyRecordsForClass(RelationRelationId, partRelid,
                                              RelationRelationId,
!                                             DEPENDENCY_PARTITION_PRI);
!             deleteDependencyRecordsForClass(RelationRelationId, partRelid,
!                                             RelationRelationId,
!                                             DEPENDENCY_PARTITION_SEC);
          }

          /* make our updates visible */
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index b66d194..715c6a2 100644
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
*************** CloneFkReferencing(Relation pg_constrain
*** 7825,7831 ****
          bool        attach_it;
          Oid            constrOid;
          ObjectAddress parentAddr,
!                     childAddr;
          ListCell   *cell;
          int            i;

--- 7825,7832 ----
          bool        attach_it;
          Oid            constrOid;
          ObjectAddress parentAddr,
!                     childAddr,
!                     childTableAddr;
          ListCell   *cell;
          int            i;

*************** CloneFkReferencing(Relation pg_constrain
*** 7966,7972 ****
              systable_endscan(scan);
              table_close(trigrel, RowExclusiveLock);

!             ConstraintSetParentConstraint(fk->conoid, parentConstrOid);
              CommandCounterIncrement();
              attach_it = true;
              break;
--- 7967,7974 ----
              systable_endscan(scan);
              table_close(trigrel, RowExclusiveLock);

!             ConstraintSetParentConstraint(fk->conoid, parentConstrOid,
!                                           RelationGetRelid(partRel));
              CommandCounterIncrement();
              attach_it = true;
              break;
*************** CloneFkReferencing(Relation pg_constrain
*** 8013,8020 ****
                                    1, false, true);
          subclone = lappend_oid(subclone, constrOid);

          ObjectAddressSet(childAddr, ConstraintRelationId, constrOid);
!         recordDependencyOn(&childAddr, &parentAddr, DEPENDENCY_INTERNAL_AUTO);

          fkconstraint = makeNode(Constraint);
          /* for now this is all we need */
--- 8015,8028 ----
                                    1, false, true);
          subclone = lappend_oid(subclone, constrOid);

+         /* Set up partition dependencies for the new constraint */
          ObjectAddressSet(childAddr, ConstraintRelationId, constrOid);
!         recordDependencyOn(&childAddr, &parentAddr,
!                            DEPENDENCY_PARTITION_PRI);
!         ObjectAddressSet(childTableAddr, RelationRelationId,
!                          RelationGetRelid(partRel));
!         recordDependencyOn(&childAddr, &childTableAddr,
!                            DEPENDENCY_PARTITION_SEC);

          fkconstraint = makeNode(Constraint);
          /* for now this is all we need */
*************** AttachPartitionEnsureIndexes(Relation re
*** 14893,14899 ****
                  /* bingo. */
                  IndexSetParentIndex(attachrelIdxRels[i], idx);
                  if (OidIsValid(constraintOid))
!                     ConstraintSetParentConstraint(cldConstrOid, constraintOid);
                  update_relispartition(NULL, cldIdxId, true);
                  found = true;
                  break;
--- 14901,14908 ----
                  /* bingo. */
                  IndexSetParentIndex(attachrelIdxRels[i], idx);
                  if (OidIsValid(constraintOid))
!                     ConstraintSetParentConstraint(cldConstrOid, constraintOid,
!                                                   RelationGetRelid(attachrel));
                  update_relispartition(NULL, cldIdxId, true);
                  found = true;
                  break;
*************** ATExecDetachPartition(Relation rel, Rang
*** 15151,15157 ****
          constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
                                                      idxid);
          if (OidIsValid(constrOid))
!             ConstraintSetParentConstraint(constrOid, InvalidOid);

          index_close(idx, NoLock);
      }
--- 15160,15166 ----
          constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
                                                      idxid);
          if (OidIsValid(constrOid))
!             ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);

          index_close(idx, NoLock);
      }
*************** ATExecDetachPartition(Relation rel, Rang
*** 15183,15189 ****
          }

          /* unset conparentid and adjust conislocal, coninhcount, etc. */
!         ConstraintSetParentConstraint(fk->conoid, InvalidOid);

          /*
           * Make the action triggers on the referenced relation.  When this was
--- 15192,15198 ----
          }

          /* unset conparentid and adjust conislocal, coninhcount, etc. */
!         ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);

          /*
           * Make the action triggers on the referenced relation.  When this was
*************** ATExecAttachPartitionIdx(List **wqueue,
*** 15419,15425 ****
          /* All good -- do it */
          IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
          if (OidIsValid(constraintOid))
!             ConstraintSetParentConstraint(cldConstrId, constraintOid);
          update_relispartition(NULL, partIdxId, true);

          pfree(attmap);
--- 15428,15435 ----
          /* All good -- do it */
          IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
          if (OidIsValid(constraintOid))
!             ConstraintSetParentConstraint(cldConstrId, constraintOid,
!                                           RelationGetRelid(partTbl));
          update_relispartition(NULL, partIdxId, true);

          pfree(attmap);
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 0b245a6..409bee2 100644
*** a/src/backend/commands/trigger.c
--- b/src/backend/commands/trigger.c
*************** CreateTrigger(CreateTrigStmt *stmt, cons
*** 1012,1028 ****
           * User CREATE TRIGGER, so place dependencies.  We make trigger be
           * auto-dropped if its relation is dropped or if the FK relation is
           * dropped.  (Auto drop is compatible with our pre-7.3 behavior.)
-          *
-          * Exception: if this trigger comes from a parent partitioned table,
-          * then it's not separately drop-able, but goes away if the partition
-          * does.
           */
          referenced.classId = RelationRelationId;
          referenced.objectId = RelationGetRelid(rel);
          referenced.objectSubId = 0;
!         recordDependencyOn(&myself, &referenced, OidIsValid(parentTriggerOid) ?
!                            DEPENDENCY_INTERNAL_AUTO :
!                            DEPENDENCY_AUTO);

          if (OidIsValid(constrrelid))
          {
--- 1012,1022 ----
           * User CREATE TRIGGER, so place dependencies.  We make trigger be
           * auto-dropped if its relation is dropped or if the FK relation is
           * dropped.  (Auto drop is compatible with our pre-7.3 behavior.)
           */
          referenced.classId = RelationRelationId;
          referenced.objectId = RelationGetRelid(rel);
          referenced.objectSubId = 0;
!         recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);

          if (OidIsValid(constrrelid))
          {
*************** CreateTrigger(CreateTrigStmt *stmt, cons
*** 1046,1056 ****
              recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
          }

!         /* Depends on the parent trigger, if there is one. */
          if (OidIsValid(parentTriggerOid))
          {
              ObjectAddressSet(referenced, TriggerRelationId, parentTriggerOid);
!             recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL_AUTO);
          }
      }

--- 1040,1054 ----
              recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
          }

!         /*
!          * If it's a partition trigger, create the partition dependencies.
!          */
          if (OidIsValid(parentTriggerOid))
          {
              ObjectAddressSet(referenced, TriggerRelationId, parentTriggerOid);
!             recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI);
!             ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
!             recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
          }
      }

diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 5dea270..b235a23 100644
*** a/src/include/catalog/dependency.h
--- b/src/include/catalog/dependency.h
***************
*** 24,87 ****
   *
   * In all cases, a dependency relationship indicates that the referenced
   * object may not be dropped without also dropping the dependent object.
!  * However, there are several subflavors:
!  *
!  * DEPENDENCY_NORMAL ('n'): normal relationship between separately-created
!  * objects.  The dependent object may be dropped without affecting the
!  * referenced object.  The referenced object may only be dropped by
!  * specifying CASCADE, in which case the dependent object is dropped too.
!  * Example: a table column has a normal dependency on its datatype.
!  *
!  * DEPENDENCY_AUTO ('a'): the dependent object can be dropped separately
!  * from the referenced object, and should be automatically dropped
!  * (regardless of RESTRICT or CASCADE mode) if the referenced object
!  * is dropped.
!  * Example: a named constraint on a table is made auto-dependent on
!  * the table, so that it will go away if the table is dropped.
!  *
!  * DEPENDENCY_INTERNAL ('i'): the dependent object was created as part
!  * of creation of the referenced object, and is really just a part of
!  * its internal implementation.  A DROP of the dependent object will be
!  * disallowed outright (we'll tell the user to issue a DROP against the
!  * referenced object, instead).  A DROP of the referenced object will be
!  * propagated through to drop the dependent object whether CASCADE is
!  * specified or not.
!  * Example: a trigger that's created to enforce a foreign-key constraint
!  * is made internally dependent on the constraint's pg_constraint entry.
!  *
!  * DEPENDENCY_INTERNAL_AUTO ('I'): the dependent object was created as
!  * part of creation of the referenced object, and is really just a part
!  * of its internal implementation.  A DROP of the dependent object will
!  * be disallowed outright (we'll tell the user to issue a DROP against the
!  * referenced object, instead).  While a regular internal dependency will
!  * prevent the dependent object from being dropped while any such
!  * dependencies remain, DEPENDENCY_INTERNAL_AUTO will allow such a drop as
!  * long as the object can be found by following any of such dependencies.
!  * Example: an index on a partition is made internal-auto-dependent on
!  * both the partition itself as well as on the index on the parent
!  * partitioned table; so the partition index is dropped together with
!  * either the partition it indexes, or with the parent index it is attached
!  * to.
!
!  * DEPENDENCY_EXTENSION ('e'): the dependent object is a member of the
!  * extension that is the referenced object.  The dependent object can be
!  * dropped only via DROP EXTENSION on the referenced object.  Functionally
!  * this dependency type acts the same as an internal dependency, but it's
!  * kept separate for clarity and to simplify pg_dump.
!  *
!  * DEPENDENCY_AUTO_EXTENSION ('x'): the dependent object is not a member
!  * of the extension that is the referenced object (and so should not be
!  * ignored by pg_dump), but cannot function without the extension and
!  * should be dropped when the extension itself is.  The dependent object
!  * may be dropped on its own as well.
!  *
!  * DEPENDENCY_PIN ('p'): there is no dependent object; this type of entry
!  * is a signal that the system itself depends on the referenced object,
!  * and so that object must never be deleted.  Entries of this type are
!  * created only during initdb.  The fields for the dependent object
!  * contain zeroes.
!  *
!  * Other dependency flavors may be needed in future.
   */

  typedef enum DependencyType
--- 24,31 ----
   *
   * In all cases, a dependency relationship indicates that the referenced
   * object may not be dropped without also dropping the dependent object.
!  * However, there are several subflavors; see the description of pg_depend
!  * in catalogs.sgml for details.
   */

  typedef enum DependencyType
*************** typedef enum DependencyType
*** 89,95 ****
      DEPENDENCY_NORMAL = 'n',
      DEPENDENCY_AUTO = 'a',
      DEPENDENCY_INTERNAL = 'i',
!     DEPENDENCY_INTERNAL_AUTO = 'I',
      DEPENDENCY_EXTENSION = 'e',
      DEPENDENCY_AUTO_EXTENSION = 'x',
      DEPENDENCY_PIN = 'p'
--- 33,40 ----
      DEPENDENCY_NORMAL = 'n',
      DEPENDENCY_AUTO = 'a',
      DEPENDENCY_INTERNAL = 'i',
!     DEPENDENCY_PARTITION_PRI = 'P',
!     DEPENDENCY_PARTITION_SEC = 'S',
      DEPENDENCY_EXTENSION = 'e',
      DEPENDENCY_AUTO_EXTENSION = 'x',
      DEPENDENCY_PIN = 'p'
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index 55a2694..c87bded 100644
*** a/src/include/catalog/pg_constraint.h
--- b/src/include/catalog/pg_constraint.h
*************** extern char *ChooseConstraintName(const
*** 239,245 ****
  extern void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
                            Oid newNspId, bool isType, ObjectAddresses *objsMoved);
  extern void ConstraintSetParentConstraint(Oid childConstrId,
!                               Oid parentConstrId);
  extern Oid    get_relation_constraint_oid(Oid relid, const char *conname, bool missing_ok);
  extern Bitmapset *get_relation_constraint_attnos(Oid relid, const char *conname,
                                 bool missing_ok, Oid *constraintOid);
--- 239,246 ----
  extern void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
                            Oid newNspId, bool isType, ObjectAddresses *objsMoved);
  extern void ConstraintSetParentConstraint(Oid childConstrId,
!                               Oid parentConstrId,
!                               Oid childTableId);
  extern Oid    get_relation_constraint_oid(Oid relid, const char *conname, bool missing_ok);
  extern Bitmapset *get_relation_constraint_attnos(Oid relid, const char *conname,
                                 bool missing_ok, Oid *constraintOid);

В списке pgsql-hackers по дате отправления:

Предыдущее
От: Justin Pryzby
Дата:
Сообщение: Re: dsa_allocate() faliure
Следующее
От: Chapman Flack
Дата:
Сообщение: Re: PostgreSQL vs SQL/XML Standards