Skip to content

Commit afc3f55

Browse files
author
Maksim Milyutin
committed
Apply patches to optimize partition pruning
1 parent 0c39d10 commit afc3f55

File tree

2 files changed

+138
-60
lines changed

2 files changed

+138
-60
lines changed

src/backend/catalog/pg_inherits.c

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,19 @@
2828
#include "parser/parse_type.h"
2929
#include "storage/lmgr.h"
3030
#include "utils/fmgroids.h"
31+
#include "utils/memutils.h"
3132
#include "utils/syscache.h"
3233
#include "utils/tqual.h"
3334

35+
/*
36+
* Entry of a hash table used in find_all_inheritors. See below.
37+
*/
38+
typedef struct SeenRelsEntry
39+
{
40+
Oid rel_id; /* relation oid */
41+
ListCell *numparents_cell; /* corresponding list cell */
42+
} SeenRelsEntry;
43+
3444
static int oid_cmp(const void *p1, const void *p2);
3545

3646

@@ -158,10 +168,33 @@ find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
158168
List *
159169
find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
160170
{
171+
/* hash table for O(1) rel_oid -> rel_numparents cell lookup */
172+
HTAB *seen_rels;
173+
HASHCTL ctl;
174+
MemoryContext new_ctx;
161175
List *rels_list,
162176
*rel_numparents;
163177
ListCell *l;
164178

179+
/*
180+
* We need a separate memory context for a hash table. This is because
181+
* hash table is used only in this procedure. To free a memory we need to
182+
* call hash_destroy which is just a wrapper around MemoryContextDelete.
183+
*/
184+
new_ctx = AllocSetContextCreate(CurrentMemoryContext,
185+
"FindAllInheritorsSeenRelsContext",
186+
ALLOCSET_DEFAULT_SIZES);
187+
188+
memset(&ctl, 0, sizeof(ctl));
189+
ctl.keysize = sizeof(Oid);
190+
ctl.entrysize = sizeof(SeenRelsEntry);
191+
ctl.hcxt = new_ctx;
192+
193+
seen_rels = hash_create(
194+
"find_all_inheritors temporary table",
195+
32, /* start small and extend */
196+
&ctl, HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
197+
165198
/*
166199
* We build a list starting with the given rel and adding all direct and
167200
* indirect children. We can use a single list as both the record of
@@ -191,26 +224,21 @@ find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
191224
foreach(lc, currentchildren)
192225
{
193226
Oid child_oid = lfirst_oid(lc);
194-
bool found = false;
195-
ListCell *lo;
196-
ListCell *li;
227+
bool found;
228+
SeenRelsEntry *hash_entry;
197229

198-
/* if the rel is already there, bump number-of-parents counter */
199-
forboth(lo, rels_list, li, rel_numparents)
230+
hash_entry = hash_search(seen_rels, &child_oid, HASH_ENTER, &found);
231+
if (found)
200232
{
201-
if (lfirst_oid(lo) == child_oid)
202-
{
203-
lfirst_int(li)++;
204-
found = true;
205-
break;
206-
}
233+
/* if the rel is already there, bump number-of-parents counter */
234+
lfirst_int(hash_entry->numparents_cell)++;
207235
}
208-
209-
/* if it's not there, add it. expect 1 parent, initially. */
210-
if (!found)
236+
else
211237
{
238+
/* if it's not there, add it. expect 1 parent, initially. */
212239
rels_list = lappend_oid(rels_list, child_oid);
213240
rel_numparents = lappend_int(rel_numparents, 1);
241+
hash_entry->numparents_cell = rel_numparents->tail;
214242
}
215243
}
216244
}
@@ -219,6 +247,9 @@ find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
219247
*numparents = rel_numparents;
220248
else
221249
list_free(rel_numparents);
250+
251+
hash_destroy(seen_rels);
252+
222253
return rels_list;
223254
}
224255

src/backend/postmaster/pgstat.c

Lines changed: 93 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,20 @@ typedef struct TabStatusArray
160160

161161
static TabStatusArray *pgStatTabList = NULL;
162162

163+
/*
164+
* pgStatTabHash entry
165+
*/
166+
typedef struct TabStatHashEntry
167+
{
168+
Oid t_id;
169+
PgStat_TableStatus* tsa_entry;
170+
} TabStatHashEntry;
171+
172+
/*
173+
* Hash table for O(1) t_id -> tsa_entry lookup
174+
*/
175+
static HTAB *pgStatTabHash = NULL;
176+
163177
/*
164178
* Backends store per-function info that's waiting to be sent to the collector
165179
* in this hash table (indexed by function OID).
@@ -817,6 +831,14 @@ pgstat_report_stat(bool force)
817831
tsa->tsa_used = 0;
818832
}
819833

834+
/*
835+
* pgStatTabHash is outdated on this point so we have to clean it,
836+
* hash_destroy() will remove hash memory context, allocated in
837+
* make_sure_stat_tab_initialized()
838+
*/
839+
hash_destroy(pgStatTabHash);
840+
pgStatTabHash = NULL;
841+
820842
/*
821843
* Send partial messages. Make sure that any pending xact commit/abort
822844
* gets counted, even if there are no table stats to send.
@@ -1661,60 +1683,88 @@ pgstat_initstats(Relation rel)
16611683
rel->pgstat_info = get_tabstat_entry(rel_id, rel->rd_rel->relisshared);
16621684
}
16631685

1686+
/*
1687+
* Make sure pgStatTabList and pgStatTabHash are initialized.
1688+
*/
1689+
static void
1690+
make_sure_stat_tab_initialized()
1691+
{
1692+
HASHCTL ctl;
1693+
MemoryContext new_ctx;
1694+
1695+
if(!pgStatTabList)
1696+
{
1697+
/* This is first time procedure is called */
1698+
pgStatTabList = (TabStatusArray *) MemoryContextAllocZero(TopMemoryContext,
1699+
sizeof(TabStatusArray));
1700+
}
1701+
1702+
if(pgStatTabHash)
1703+
return;
1704+
1705+
/* Hash table was freed or never existed. */
1706+
1707+
new_ctx = AllocSetContextCreate(
1708+
TopMemoryContext,
1709+
"PGStatLookupHashTableContext",
1710+
ALLOCSET_DEFAULT_SIZES);
1711+
1712+
memset(&ctl, 0, sizeof(ctl));
1713+
ctl.keysize = sizeof(Oid);
1714+
ctl.entrysize = sizeof(TabStatHashEntry);
1715+
ctl.hcxt = new_ctx;
1716+
1717+
pgStatTabHash = hash_create("pgstat t_id to tsa_entry lookup hash table",
1718+
TABSTAT_QUANTUM, &ctl, HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
1719+
}
1720+
16641721
/*
16651722
* get_tabstat_entry - find or create a PgStat_TableStatus entry for rel
16661723
*/
16671724
static PgStat_TableStatus *
16681725
get_tabstat_entry(Oid rel_id, bool isshared)
16691726
{
1727+
TabStatHashEntry* hash_entry;
16701728
PgStat_TableStatus *entry;
16711729
TabStatusArray *tsa;
1672-
TabStatusArray *prev_tsa;
1673-
int i;
1730+
bool found;
1731+
1732+
make_sure_stat_tab_initialized();
16741733

16751734
/*
1676-
* Search the already-used tabstat slots for this relation.
1735+
* Find an entry or create a new one.
16771736
*/
1678-
prev_tsa = NULL;
1679-
for (tsa = pgStatTabList; tsa != NULL; prev_tsa = tsa, tsa = tsa->tsa_next)
1737+
hash_entry = hash_search(pgStatTabHash, &rel_id, HASH_ENTER, &found);
1738+
if(found)
1739+
return hash_entry->tsa_entry;
1740+
1741+
/*
1742+
* `hash_entry` was just created and now we have to fill it.
1743+
* First make sure there is a free space in a last element of pgStatTabList.
1744+
*/
1745+
tsa = pgStatTabList;
1746+
while(tsa->tsa_used == TABSTAT_QUANTUM)
16801747
{
1681-
for (i = 0; i < tsa->tsa_used; i++)
1748+
if(tsa->tsa_next == NULL)
16821749
{
1683-
entry = &tsa->tsa_entries[i];
1684-
if (entry->t_id == rel_id)
1685-
return entry;
1750+
tsa->tsa_next = (TabStatusArray *) MemoryContextAllocZero(TopMemoryContext,
1751+
sizeof(TabStatusArray));
16861752
}
16871753

1688-
if (tsa->tsa_used < TABSTAT_QUANTUM)
1689-
{
1690-
/*
1691-
* It must not be present, but we found a free slot instead. Fine,
1692-
* let's use this one. We assume the entry was already zeroed,
1693-
* either at creation or after last use.
1694-
*/
1695-
entry = &tsa->tsa_entries[tsa->tsa_used++];
1696-
entry->t_id = rel_id;
1697-
entry->t_shared = isshared;
1698-
return entry;
1699-
}
1754+
tsa = tsa->tsa_next;
17001755
}
17011756

17021757
/*
1703-
* We ran out of tabstat slots, so allocate more. Be sure they're zeroed.
1704-
*/
1705-
tsa = (TabStatusArray *) MemoryContextAllocZero(TopMemoryContext,
1706-
sizeof(TabStatusArray));
1707-
if (prev_tsa)
1708-
prev_tsa->tsa_next = tsa;
1709-
else
1710-
pgStatTabList = tsa;
1711-
1712-
/*
1713-
* Use the first entry of the new TabStatusArray.
1758+
* Add an entry.
17141759
*/
17151760
entry = &tsa->tsa_entries[tsa->tsa_used++];
17161761
entry->t_id = rel_id;
17171762
entry->t_shared = isshared;
1763+
1764+
/*
1765+
* Add a corresponding entry to pgStatTabHash.
1766+
*/
1767+
hash_entry->tsa_entry = entry;
17181768
return entry;
17191769
}
17201770

@@ -1726,22 +1776,19 @@ get_tabstat_entry(Oid rel_id, bool isshared)
17261776
PgStat_TableStatus *
17271777
find_tabstat_entry(Oid rel_id)
17281778
{
1729-
PgStat_TableStatus *entry;
1730-
TabStatusArray *tsa;
1731-
int i;
1779+
TabStatHashEntry* hash_entry;
17321780

1733-
for (tsa = pgStatTabList; tsa != NULL; tsa = tsa->tsa_next)
1734-
{
1735-
for (i = 0; i < tsa->tsa_used; i++)
1736-
{
1737-
entry = &tsa->tsa_entries[i];
1738-
if (entry->t_id == rel_id)
1739-
return entry;
1740-
}
1741-
}
1781+
/*
1782+
* There are no entries at all.
1783+
*/
1784+
if(!pgStatTabHash)
1785+
return NULL;
17421786

1743-
/* Not present */
1744-
return NULL;
1787+
hash_entry = hash_search(pgStatTabHash, &rel_id, HASH_FIND, NULL);
1788+
if(!hash_entry)
1789+
return NULL;
1790+
1791+
return hash_entry->tsa_entry;
17451792
}
17461793

17471794
/*

0 commit comments

Comments
 (0)