Improve performance of find_all_inheritors()

Previous coding uses three nested loops which obviously were a pain for
large number of table's children. Patch replaces inner loop with
a hashmap.

Author: Aleksander Alekseev
Reviewed-by: me

https://commitfest.postgresql.org/13/1058/
This commit is contained in:
Teodor Sigaev 2017-03-27 19:07:48 +03:00
parent 5196f13b27
commit 827d6f9779
1 changed files with 44 additions and 14 deletions

View File

@ -31,7 +31,16 @@
#include "utils/fmgroids.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
#include "utils/memutils.h"
/*
* Entry of a hash table used in find_all_inheritors. See below.
*/
typedef struct SeenRelsEntry
{
Oid rel_id; /* relation oid */
ListCell *numparents_cell; /* corresponding list cell */
} SeenRelsEntry;
/*
* find_inheritance_children
@ -157,10 +166,33 @@ find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
List *
find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
{
/* hash table for O(1) rel_oid -> rel_numparents cell lookup */
HTAB *seen_rels;
HASHCTL ctl;
MemoryContext new_ctx;
List *rels_list,
*rel_numparents;
ListCell *l;
/*
* We need a separate memory context for a hash table. This is because
* hash table is used only in this procedure. To free a memory we need to
* call hash_destroy which is just a wrapper around MemoryContextDelete.
*/
new_ctx = AllocSetContextCreate(CurrentMemoryContext,
"FindAllInheritorsSeenRelsContext",
ALLOCSET_DEFAULT_SIZES);
memset(&ctl, 0, sizeof(ctl));
ctl.keysize = sizeof(Oid);
ctl.entrysize = sizeof(SeenRelsEntry);
ctl.hcxt = new_ctx;
seen_rels = hash_create(
"find_all_inheritors temporary table",
32, /* start small and extend */
&ctl, HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
/*
* 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
@ -190,26 +222,21 @@ find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
foreach(lc, currentchildren)
{
Oid child_oid = lfirst_oid(lc);
bool found = false;
ListCell *lo;
ListCell *li;
bool found;
SeenRelsEntry *hash_entry;
/* if the rel is already there, bump number-of-parents counter */
forboth(lo, rels_list, li, rel_numparents)
hash_entry = hash_search(seen_rels, &child_oid, HASH_ENTER, &found);
if (found)
{
if (lfirst_oid(lo) == child_oid)
{
lfirst_int(li)++;
found = true;
break;
}
/* if the rel is already there, bump number-of-parents counter */
lfirst_int(hash_entry->numparents_cell)++;
}
/* if it's not there, add it. expect 1 parent, initially. */
if (!found)
else
{
/* if it's not there, add it. expect 1 parent, initially. */
rels_list = lappend_oid(rels_list, child_oid);
rel_numparents = lappend_int(rel_numparents, 1);
hash_entry->numparents_cell = rel_numparents->tail;
}
}
}
@ -218,6 +245,9 @@ find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
*numparents = rel_numparents;
else
list_free(rel_numparents);
hash_destroy(seen_rels);
return rels_list;
}