PostgreSQL数据库统计信息——查找继承子表find_all_inheritors

find_inheritance_children函数返回包含继承自OID为parentrelId表的所有子表OID的列表(获取的是直接子表oid)。指定的锁类型需要在每个子表上获取的(但不是在给定的parentrelId表上,调用者应该已经锁定了它)。如果lockmode是NoLock,则不会获取锁,但调用者必须注意竞争条件,以防止可能的子表drop。

    List *find_inheritance_children(Oid parentrelId, LOCKMODE lockmode) {
    List *list = NIL;
    Relation relation;
    SysScanDesc scan;
    ScanKeyData key[1];
    HeapTuple inheritsTuple;
    Oid inhrelid, *oidarr;
    int maxoids, numoids, i;


    /* Can skip the scan if pg_class shows the relation has never had a subclass. */
    if (!has_subclass(parentrelId)) return NIL; // pg_class relhassubclass列为false,也就是没有子表


    // 扫描pg_inherits系统表,获取子表oid的数组
    maxoids = 32; numoids = 0; /* Scan pg_inherits and build a working array of subclass OIDs. */
    oidarr = (Oid *) palloc(maxoids * sizeof(Oid));
    relation = table_open(InheritsRelationId, AccessShareLock);
    ScanKeyInit(&key[0],Anum_pg_inherits_inhparent,BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(parentrelId));
    scan = systable_beginscan(relation, InheritsParentIndexId, true, NULL, 1, key);
    while ((inheritsTuple = systable_getnext(scan)) != NULL) {
    inhrelid = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhrelid; // 获取下一级子表oid
    if (numoids >= maxoids){ maxoids *= 2; oidarr = (Oid *) repalloc(oidarr, maxoids * sizeof(Oid)); } // 空间不够进行扩容
    oidarr[numoids++] = inhrelid;
    }
    systable_endscan(scan);
    table_close(relation, AccessShareLock);


    /* If we found more than one child, sort them by OID. This ensures reasonably consistent behavior regardless of the vagaries of an indexscan. This is important since we need to be sure all backends lock children in the same order to avoid needless deadlocks. */ // 如果我们发现多个孩子,按OID排序。这确保了合理一致的行为,而不管indexscan的变化无常。这一点很重要,因为我们需要确保所有后端都以相同的顺序锁定子级,以避免不必要的死锁。
    if (numoids > 1) qsort(oidarr, numoids, sizeof(Oid), oid_cmp);
    for (i = 0; i < numoids; i++) { /* Acquire locks and build the result list. */ // 循环锁定子表
    inhrelid = oidarr[i];
    if (lockmode != NoLock) {
    LockRelationOid(inhrelid, lockmode); /* Get the lock to synchronize against concurrent drop */
    /* Now that we have the lock, double-check to see if the relation really exists or not. If not, assume it was dropped while we waited to acquire lock, and ignore it. */ // 现在我们有了锁,再次检查关系是否真的存在。若并没有,那个么假设它在我们等待获取锁时被丢弃,并忽略它。
    if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(inhrelid))) {
    UnlockRelationOid(inhrelid, lockmode); /* Release useless lock */
    continue; /* And ignore this relation */
    }
    }
    list = lappend_oid(list, inhrelid);
    }
    pfree(oidarr);
    return list;
    }

    find_all_inheritors函数获取以parentrelId为根的继承树上所有子表的oid,其返回形参numparents存放的是继承树上所有具有表节点的父表数量。继承树是一颗以parentrelId为根的树,但是其子表节点的父表可能有多个(从根节点可能有多条路径访问到该子表节点),因此这里使用改进的广度遍历,用表oid映射该表父表的数量的hash来记录每个节点的父节点数量,这样遍历到所有子表的同时可以获取每个子表的父表数量。

      List *find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents) {      
      HASHCTL ctl; /* hash table for O(1) rel_oid -> rel_numparents cell lookup */ // 表oid映射该表父表的数量
      memset(&ctl, 0, sizeof(ctl));
      ctl.keysize = sizeof(Oid);
      ctl.entrysize = sizeof(SeenRelsEntry);
      ctl.hcxt = CurrentMemoryContext;
      HTAB *seen_rels;
      seen_rels = hash_create("find_all_inheritors temporary table",32, /* start small and extend */&ctl, HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);

      List *rels_list, *rel_numparents; ListCell *l;
      /* We build a list starting with the given rel and adding all direct and indirect children. We can use a single list as both the record of already-found rels and the agenda of rels yet to be scanned for more children. This is a bit tricky but works because the foreach() macro doesn't fetch the next list element until the bottom of the loop. */ // 我们从给定的rel开始构建一个列表,并添加所有直接和间接子项。我们可以使用单个列表作为已找到的表的记录和尚未扫描更多子表的表。这有点棘手,但很有效,因为foreach()宏直到循环底部才获取下一个列表元素
      rels_list = list_make1_oid(parentrelId); // <- 继承树上所有表的oid
      rel_numparents = list_make1_int(0);
      foreach(l, rels_list){
      Oid currentrel = lfirst_oid(l);
      List *currentchildren;
      ListCell *lc; // 调用find_inheritance_children函数获取该表下级子表
      currentchildren = find_inheritance_children(currentrel, lockmode); /* Get the direct children of this rel */


      /* Add to the queue only those children not already seen. This avoids making duplicate entries in case of multiple inheritance paths from the same parent. (It'll also keep us from getting into an infinite loop, though theoretically there can't be any cycles in the inheritance graph anyway.) */ // 仅将那些尚未看到的子表添加到队列中。这避免了在来自同一父级的多个继承路径的情况下创建重复条目。(这也将防止我们进入无限循环,尽管理论上继承图中不可能有任何循环。)
      foreach(lc, currentchildren){
      Oid child_oid = lfirst_oid(lc);
      bool found;
      SeenRelsEntry *hash_entry;
      hash_entry = hash_search(seen_rels, &child_oid, HASH_ENTER, &found);
      if (found) { /* if the rel is already there, bump number-of-parents counter */ // 该表已经被访问过,递增其父表数量
      lfirst_int(hash_entry->numparents_cell)++;
      }else { /* if it's not there, add it. expect 1 parent, initially. */ // 没有被访问过
      rels_list = lappend_oid(rels_list, child_oid); // 加入到存放继承树上所有表的oid的列表中
      rel_numparents = lappend_int(rel_numparents, 1); // 创建父表数量节点
      hash_entry->numparents_cell = rel_numparents->tail; // 建立oid到父表数量节点的映射
      }
      }
      }
      if (numparents) *numparents = rel_numparents; // 如果需要返回则返回
      else list_free(rel_numparents);
      hash_destroy(seen_rels);
      return rels_list;
      }



      免责声明:

      1、本站资源由自动抓取工具收集整理于网络。

      2、本站不承担由于内容的合法性及真实性所引起的一切争议和法律责任。

      3、电子书、小说等仅供网友预览使用,书籍版权归作者或出版社所有。

      4、如作者、出版社认为资源涉及侵权,请联系本站,本站将在收到通知书后尽快删除您认为侵权的作品。

      5、如果您喜欢本资源,请您支持作者,购买正版内容。

      6、资源失效,请下方留言,欢迎分享资源链接

      文章评论

      0条评论