Re: [HACKERS] Race between SELECT and ALTER TABLE NO INHERIT

Поиск
Список
Период
Сортировка
От Kyotaro HORIGUCHI
Тема Re: [HACKERS] Race between SELECT and ALTER TABLE NO INHERIT
Дата
Msg-id 20170913.112543.158969740.horiguchi.kyotaro@lab.ntt.co.jp
обсуждение исходный текст
Ответ на Re: [HACKERS] Race between SELECT and ALTER TABLE NO INHERIT  (Kyotaro HORIGUCHI <horiguchi.kyotaro@lab.ntt.co.jp>)
Список pgsql-hackers
At Mon, 28 Aug 2017 18:28:07 +0900 (Tokyo Standard Time), Kyotaro HORIGUCHI <horiguchi.kyotaro@lab.ntt.co.jp> wrote in
<20170828.182807.98097766.horiguchi.kyotaro@lab.ntt.co.jp>
> I'll add this to CF2017-09.

This patch got deadly atack from the commit 30833ba. I changed
the signature of expand_single_inheritance_child in addition to
make_inh_translation_list to notify that the specified child is
no longer a child of the parent.

This passes regular regression test and fixed the the problem.

regards,

-- 
Kyotaro Horiguchi
NTT Open Source Software Center
*** a/src/backend/catalog/pg_inherits.c
--- b/src/backend/catalog/pg_inherits.c
***************
*** 42,47 **** typedef struct SeenRelsEntry
--- 42,49 ----     ListCell   *numparents_cell;    /* corresponding list cell */ } SeenRelsEntry; 
+ static bool is_descendent_of_internal(Oid parentId, Oid childId,
+                                       HTAB *seen_rels); /*  * find_inheritance_children  *
***************
*** 400,402 **** typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId)
--- 402,472 ----      return result; }
+ 
+ /*
+  * Check if the child is really a descendent of the parent
+  */
+ bool
+ is_descendent_of(Oid parentId, Oid childId)
+ {
+     HTAB       *seen_rels;
+     HASHCTL        ctl;
+     bool        ischild = false;
+ 
+     memset(&ctl, 0, sizeof(ctl));
+     ctl.keysize = sizeof(Oid);
+     ctl.entrysize = sizeof(Oid);
+     ctl.hcxt = CurrentMemoryContext;
+ 
+     seen_rels = hash_create("is_descendent_of temporary table",
+                             32, /* start small and extend */
+                             &ctl,
+                             HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
+ 
+     ischild = is_descendent_of_internal(parentId, childId, seen_rels);
+ 
+     hash_destroy(seen_rels);
+ 
+     return ischild;
+ }
+ 
+ static bool
+ is_descendent_of_internal(Oid parentId, Oid childId, HTAB *seen_rels)
+ {
+     Relation    inhrel;
+     SysScanDesc scan;
+     ScanKeyData key[1];
+     bool        ischild = false;
+     HeapTuple    inheritsTuple;
+ 
+     inhrel = heap_open(InheritsRelationId, AccessShareLock);
+     ScanKeyInit(&key[0], Anum_pg_inherits_inhparent,
+                 BTEqualStrategyNumber, F_OIDEQ,    ObjectIdGetDatum(parentId));
+     scan = systable_beginscan(inhrel, InheritsParentIndexId, true,
+                               NULL, 1, key);
+ 
+     while ((inheritsTuple = systable_getnext(scan)) != NULL)
+     {
+         bool found;
+         Oid inhrelid = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhrelid;
+ 
+         hash_search(seen_rels, &inhrelid, HASH_ENTER, &found);
+ 
+         /*
+          * Recursively check into children. Although there can't theoretically
+          * be any cycles in the inheritance graph, check the cycles following
+          * find_all_inheritors.
+          */
+         if (inhrelid == childId ||
+             (!found && is_descendent_of_internal(inhrelid, childId, seen_rels)))
+         {
+             ischild = true;
+             break;
+         }
+     }
+ 
+     systable_endscan(scan);
+     heap_close(inhrel, AccessShareLock);
+ 
+     return ischild;
+ }
*** a/src/backend/optimizer/prep/prepunion.c
--- b/src/backend/optimizer/prep/prepunion.c
***************
*** 108,123 **** static void expand_partitioned_rtentry(PlannerInfo *root,                            LOCKMODE
lockmode,                           bool *has_child, List **appinfos,                            List
**partitioned_child_rels);
! static void expand_single_inheritance_child(PlannerInfo *root,                                 RangeTblEntry
*parentrte,                                Index parentRTindex, Relation parentrel,
PlanRowMark*parentrc, Relation childrel,                                 bool *has_child, List **appinfos,
                  List **partitioned_child_rels);
 
! static void make_inh_translation_list(Relation oldrelation,                           Relation newrelation,
!                           Index newvarno,
!                           List **translated_vars); static Bitmapset *translate_col_privs(const Bitmapset
*parent_privs,                    List *translated_vars); static Node *adjust_appendrel_attrs_mutator(Node *node,
 
--- 108,122 ----                            LOCKMODE lockmode,                            bool *has_child, List
**appinfos,                           List **partitioned_child_rels);
 
! static bool expand_single_inheritance_child(PlannerInfo *root,                                 RangeTblEntry
*parentrte,                                Index parentRTindex, Relation parentrel,
PlanRowMark*parentrc, Relation childrel,                                 bool *has_child, List **appinfos,
                  List **partitioned_child_rels);
 
! static List *make_inh_translation_list(Relation oldrelation,                           Relation newrelation,
!                           Index newvarno); static Bitmapset *translate_col_privs(const Bitmapset *parent_privs,
             List *translated_vars); static Node *adjust_appendrel_attrs_mutator(Node *node,
 
***************
*** 1476,1481 **** expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
--- 1475,1482 ----          * in which they appear in the PartitionDesc.  But first, expand the          * parent
itself.         */
 
+ 
+         /* ignore the return value since this doesn't exclude the parent */
expand_single_inheritance_child(root,rte, rti, oldrelation, oldrc,                                         oldrelation,
                                       &has_child, &appinfos,
 
***************
*** 1497,1502 **** expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
--- 1498,1504 ----         {             Oid            childOID = lfirst_oid(l);             Relation    newrelation;
+             bool        expanded = false;              /* Open rel if needed; we already have required locks */
     if (childOID != parentOID)
 
***************
*** 1516,1529 **** expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)                 continue;
           } 
 
!             expand_single_inheritance_child(root, rte, rti, oldrelation, oldrc,
     newrelation,                                             &has_child, &appinfos,
        &partitioned_child_rels); 
 
!             /* Close child relations, but keep locks */             if (childOID != parentOID)
!                 heap_close(newrelation, NoLock);         }     } 
--- 1518,1532 ----                 continue;             } 
!             expanded = expand_single_inheritance_child(root,
!                                             rte, rti, oldrelation, oldrc,
newrelation,                                            &has_child, &appinfos,
  &partitioned_child_rels); 
 
!             /* Close child relations, but keep locks if expanded */             if (childOID != parentOID)
!                 heap_close(newrelation, expanded ? NoLock : lockmode);         }     } 
***************
*** 1581,1586 **** expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
--- 1584,1590 ----     {         Oid            childOID = partdesc->oids[i];         Relation    childrel;
+         bool        expanded = false;          /* Open rel; we already have required locks */         childrel =
heap_open(childOID,NoLock);
 
***************
*** 1592,1598 **** expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,             continue;
} 
 
!         expand_single_inheritance_child(root, parentrte, parentRTindex,
parentrel,parentrc, childrel,                                         has_child, appinfos,
          partitioned_child_rels);
 
--- 1596,1603 ----             continue;         } 
!         expanded = expand_single_inheritance_child(root,
!                                         parentrte, parentRTindex,                                         parentrel,
parentrc,childrel,                                         has_child, appinfos,
partitioned_child_rels);
***************
*** 1606,1613 **** expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
            has_child, appinfos,                                           partitioned_child_rels); 
 
!         /* Close child relation, but keep locks */
!         heap_close(childrel, NoLock);     } } 
--- 1611,1618 ----                                           has_child, appinfos,
   partitioned_child_rels); 
 
!         /* Close child relation, but keep locks if expanded */
!         heap_close(childrel, expanded ? NoLock : lockmode);     } } 
***************
*** 1619,1626 **** expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,  * anything at all.
Otherwise,we'll set "has_child" to true, build a  * RangeTblEntry and either a PartitionedChildRelInfo or AppendRelInfo
as * appropriate, plus maybe a PlanRowMark.  */
 
! static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
  Index parentRTindex, Relation parentrel,                                 PlanRowMark *parentrc, Relation childrel,
 
--- 1624,1634 ----  * anything at all.  Otherwise, we'll set "has_child" to true, build a  * RangeTblEntry and either a
PartitionedChildRelInfoor AppendRelInfo as  * appropriate, plus maybe a PlanRowMark.
 
+  *
+  * Returns false if the child has been found that no longer a child of the
+  * parent.  */
! static bool expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
  Index parentRTindex, Relation parentrel,                                 PlanRowMark *parentrc, Relation childrel,
 
***************
*** 1661,1666 **** expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
--- 1669,1692 ----      */     if (childrte->relkind != RELKIND_PARTITIONED_TABLE)     {
+         List *translated_vars =
+             make_inh_translation_list(parentrel, childrel, childRTindex);
+ 
+         /*
+          * It may happen that the childOID is no longer a child of the
+          * parent.
+          */
+         if (!translated_vars)
+         {
+             /*
+              * This childrel is no longer a child of the parent.  Return doing
+              * nothing. Halfway built childrte is abandoned but this happens
+              * really rarely.
+              */
+             Assert (childOID != parentOID);
+             return false;
+         }
+          /* Remember if we saw a real child. */         if (childOID != parentOID)             *has_child = true;
***************
*** 1670,1677 **** expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
appinfo->child_relid= childRTindex;         appinfo->parent_reltype = parentrel->rd_rel->reltype;
appinfo->child_reltype= childrel->rd_rel->reltype;
 
!         make_inh_translation_list(parentrel, childrel, childRTindex,
!                                   &appinfo->translated_vars);         appinfo->parent_reloid = parentOID;
*appinfos= lappend(*appinfos, appinfo); 
 
--- 1696,1702 ----         appinfo->child_relid = childRTindex;         appinfo->parent_reltype =
parentrel->rd_rel->reltype;        appinfo->child_reltype = childrel->rd_rel->reltype;
 
!         appinfo->translated_vars = translated_vars;         appinfo->parent_reloid = parentOID;         *appinfos =
lappend(*appinfos,appinfo); 
 
***************
*** 1726,1744 **** expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,          root->rowMarks
=lappend(root->rowMarks, childrc);     } }  /*  * make_inh_translation_list  *      Build the list of translations from
parentVars to child Vars for
 
!  *      an inheritance child.  *  * For paranoia's sake, we match type/collation as well as attribute name.  */
! static void make_inh_translation_list(Relation oldrelation, Relation newrelation,
!                           Index newvarno,
!                           List **translated_vars) {     List       *vars = NIL;     TupleDesc    old_tupdesc =
RelationGetDescr(oldrelation);
--- 1751,1771 ----          root->rowMarks = lappend(root->rowMarks, childrc);     }
+ 
+     return true; }  /*  * make_inh_translation_list  *      Build the list of translations from parent Vars to child
Varsfor
 
!  *      an inheritance child. Returns NULL if the two relations are no longer in
!  *      a inheritance relationship.  *  * For paranoia's sake, we match type/collation as well as attribute name.
*/
! static List * make_inh_translation_list(Relation oldrelation, Relation newrelation,
!                           Index newvarno) {     List       *vars = NIL;     TupleDesc    old_tupdesc =
RelationGetDescr(oldrelation);
***************
*** 1808,1815 **** make_inh_translation_list(Relation oldrelation, Relation newrelation,
--- 1835,1852 ----                     break;             }             if (new_attno >= newnatts)
+             {
+                 /*
+                  * It's possible that newrelation is no longer a child of the
+                  * oldrelation. We just ingore it for the case.
+                  */
+                 if (!is_descendent_of(oldrelation->rd_id, newrelation->rd_id))
+                     return NULL;
+ 
+                 /* If still a child, something's wrong. */                 elog(ERROR, "could not find inherited
attribute\"%s\" of relation \"%s\"",                      attname, RelationGetRelationName(newrelation));
 
+             }         }          /* Found it, check type and collation match */
***************
*** 1828,1834 **** make_inh_translation_list(Relation oldrelation, Relation newrelation,
     0));     } 
 
!     *translated_vars = vars; }  /*
--- 1865,1871 ----                                      0));     } 
!     return vars; }  /*
*** a/src/include/catalog/pg_inherits_fn.h
--- b/src/include/catalog/pg_inherits_fn.h
***************
*** 23,27 **** extern List *find_all_inheritors(Oid parentrelId, LOCKMODE lockmode,
--- 23,28 ---- extern bool has_subclass(Oid relationId); extern bool has_superclass(Oid relationId); extern bool
typeInheritsFrom(OidsubclassTypeId, Oid superclassTypeId);
 
+ extern bool is_descendent_of(Oid parentId, Oid childId);  #endif                            /* PG_INHERITS_FN_H */

-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

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

Предыдущее
От: Amit Langote
Дата:
Сообщение: Re: [HACKERS] no test coverage for ALTER FOREIGN DATA WRAPPER nameHANDLER ...
Следующее
От: xiaolongc
Дата:
Сообщение: Re: [HACKERS] Some subscriptions fail (while some succeed) with pglogical