Skip to content

Commit 9140cf8

Browse files
committed
Associate partitioning information with each RelOptInfo.
This is not used for anything yet, but it is necessary infrastructure for partition-wise join and for partition pruning without constraint exclusion. Ashutosh Bapat, reviewed by Amit Langote and with quite a few changes, mostly cosmetic, by me. Additional review and testing of this patch series by Antonin Houska, Amit Khandekar, Rafia Sabih, Rajkumar Raghuwanshi, Thomas Munro, and Dilip Kumar. Discussion: http://postgr.es/m/CAFjFpRfneFG3H+F6BaiXemMrKF+FY-POpx3Ocy+RiH3yBmXSNw@mail.gmail.com
1 parent 7b86c2a commit 9140cf8

File tree

3 files changed

+249
-3
lines changed

3 files changed

+249
-3
lines changed

src/backend/optimizer/util/plancat.c

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ static List *get_relation_constraints(PlannerInfo *root,
6868
static List *build_index_tlist(PlannerInfo *root, IndexOptInfo *index,
6969
Relation heapRelation);
7070
static List *get_relation_statistics(RelOptInfo *rel, Relation relation);
71+
static void set_relation_partition_info(PlannerInfo *root, RelOptInfo *rel,
72+
Relation relation);
73+
static PartitionScheme find_partition_scheme(PlannerInfo *root, Relation rel);
74+
static List **build_baserel_partition_key_exprs(Relation relation, Index varno);
7175

7276
/*
7377
* get_relation_info -
@@ -420,6 +424,13 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
420424
/* Collect info about relation's foreign keys, if relevant */
421425
get_relation_foreign_keys(root, rel, relation, inhparent);
422426

427+
/*
428+
* Collect info about relation's partitioning scheme, if any. Only
429+
* inheritance parents may be partitioned.
430+
*/
431+
if (inhparent && relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
432+
set_relation_partition_info(root, rel, relation);
433+
423434
heap_close(relation, NoLock);
424435

425436
/*
@@ -1802,3 +1813,151 @@ has_row_triggers(PlannerInfo *root, Index rti, CmdType event)
18021813
heap_close(relation, NoLock);
18031814
return result;
18041815
}
1816+
1817+
/*
1818+
* set_relation_partition_info
1819+
*
1820+
* Set partitioning scheme and related information for a partitioned table.
1821+
*/
1822+
static void
1823+
set_relation_partition_info(PlannerInfo *root, RelOptInfo *rel,
1824+
Relation relation)
1825+
{
1826+
PartitionDesc partdesc;
1827+
1828+
Assert(relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
1829+
1830+
partdesc = RelationGetPartitionDesc(relation);
1831+
rel->part_scheme = find_partition_scheme(root, relation);
1832+
Assert(partdesc != NULL && rel->part_scheme != NULL);
1833+
rel->boundinfo = partdesc->boundinfo;
1834+
rel->nparts = partdesc->nparts;
1835+
rel->partexprs = build_baserel_partition_key_exprs(relation, rel->relid);
1836+
}
1837+
1838+
/*
1839+
* find_partition_scheme
1840+
*
1841+
* Find or create a PartitionScheme for this Relation.
1842+
*/
1843+
static PartitionScheme
1844+
find_partition_scheme(PlannerInfo *root, Relation relation)
1845+
{
1846+
PartitionKey partkey = RelationGetPartitionKey(relation);
1847+
ListCell *lc;
1848+
int partnatts;
1849+
PartitionScheme part_scheme;
1850+
1851+
/* A partitioned table should have a partition key. */
1852+
Assert(partkey != NULL);
1853+
1854+
partnatts = partkey->partnatts;
1855+
1856+
/* Search for a matching partition scheme and return if found one. */
1857+
foreach(lc, root->part_schemes)
1858+
{
1859+
part_scheme = lfirst(lc);
1860+
1861+
/* Match partitioning strategy and number of keys. */
1862+
if (partkey->strategy != part_scheme->strategy ||
1863+
partnatts != part_scheme->partnatts)
1864+
continue;
1865+
1866+
/* Match the partition key types. */
1867+
if (memcmp(partkey->partopfamily, part_scheme->partopfamily,
1868+
sizeof(Oid) * partnatts) != 0 ||
1869+
memcmp(partkey->partopcintype, part_scheme->partopcintype,
1870+
sizeof(Oid) * partnatts) != 0 ||
1871+
memcmp(partkey->parttypcoll, part_scheme->parttypcoll,
1872+
sizeof(Oid) * partnatts) != 0)
1873+
continue;
1874+
1875+
/*
1876+
* Length and byval information should match when partopcintype
1877+
* matches.
1878+
*/
1879+
Assert(memcmp(partkey->parttyplen, part_scheme->parttyplen,
1880+
sizeof(int16) * partnatts) == 0);
1881+
Assert(memcmp(partkey->parttypbyval, part_scheme->parttypbyval,
1882+
sizeof(bool) * partnatts) == 0);
1883+
1884+
/* Found matching partition scheme. */
1885+
return part_scheme;
1886+
}
1887+
1888+
/*
1889+
* Did not find matching partition scheme. Create one copying relevant
1890+
* information from the relcache. Instead of copying whole arrays, copy
1891+
* the pointers in relcache. It's safe to do so since
1892+
* RelationClearRelation() wouldn't change it while planner is using it.
1893+
*/
1894+
part_scheme = (PartitionScheme) palloc0(sizeof(PartitionSchemeData));
1895+
part_scheme->strategy = partkey->strategy;
1896+
part_scheme->partnatts = partkey->partnatts;
1897+
part_scheme->partopfamily = partkey->partopfamily;
1898+
part_scheme->partopcintype = partkey->partopcintype;
1899+
part_scheme->parttypcoll = partkey->parttypcoll;
1900+
part_scheme->parttyplen = partkey->parttyplen;
1901+
part_scheme->parttypbyval = partkey->parttypbyval;
1902+
1903+
/* Add the partitioning scheme to PlannerInfo. */
1904+
root->part_schemes = lappend(root->part_schemes, part_scheme);
1905+
1906+
return part_scheme;
1907+
}
1908+
1909+
/*
1910+
* build_baserel_partition_key_exprs
1911+
*
1912+
* Collects partition key expressions for a given base relation. Any single
1913+
* column partition keys are converted to Var nodes. All Var nodes are set
1914+
* to the given varno. The partition key expressions are returned as an array
1915+
* of single element lists to be stored in RelOptInfo of the base relation.
1916+
*/
1917+
static List **
1918+
build_baserel_partition_key_exprs(Relation relation, Index varno)
1919+
{
1920+
PartitionKey partkey = RelationGetPartitionKey(relation);
1921+
int partnatts;
1922+
int cnt;
1923+
List **partexprs;
1924+
ListCell *lc;
1925+
1926+
/* A partitioned table should have a partition key. */
1927+
Assert(partkey != NULL);
1928+
1929+
partnatts = partkey->partnatts;
1930+
partexprs = (List **) palloc(sizeof(List *) * partnatts);
1931+
lc = list_head(partkey->partexprs);
1932+
1933+
for (cnt = 0; cnt < partnatts; cnt++)
1934+
{
1935+
Expr *partexpr;
1936+
AttrNumber attno = partkey->partattrs[cnt];
1937+
1938+
if (attno != InvalidAttrNumber)
1939+
{
1940+
/* Single column partition key is stored as a Var node. */
1941+
Assert(attno > 0);
1942+
1943+
partexpr = (Expr *) makeVar(varno, attno,
1944+
partkey->parttypid[cnt],
1945+
partkey->parttypmod[cnt],
1946+
partkey->parttypcoll[cnt], 0);
1947+
}
1948+
else
1949+
{
1950+
if (lc == NULL)
1951+
elog(ERROR, "wrong number of partition key expressions");
1952+
1953+
/* Re-stamp the expression with given varno. */
1954+
partexpr = (Expr *) copyObject(lfirst(lc));
1955+
ChangeVarNodes((Node *) partexpr, 1, varno, 0);
1956+
lc = lnext(lc);
1957+
}
1958+
1959+
partexprs[cnt] = list_make1(partexpr);
1960+
}
1961+
1962+
return partexprs;
1963+
}

src/backend/optimizer/util/relnode.c

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
146146
rel->baserestrict_min_security = UINT_MAX;
147147
rel->joininfo = NIL;
148148
rel->has_eclass_joins = false;
149+
rel->part_scheme = NULL;
150+
rel->nparts = 0;
151+
rel->boundinfo = NULL;
152+
rel->part_rels = NULL;
153+
rel->partexprs = NULL;
149154

150155
/*
151156
* Pass top parent's relids down the inheritance hierarchy. If the parent
@@ -218,18 +223,41 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
218223
if (rte->inh)
219224
{
220225
ListCell *l;
226+
int nparts = rel->nparts;
227+
int cnt_parts = 0;
228+
229+
if (nparts > 0)
230+
rel->part_rels = (RelOptInfo **)
231+
palloc(sizeof(RelOptInfo *) * nparts);
221232

222233
foreach(l, root->append_rel_list)
223234
{
224235
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
236+
RelOptInfo *childrel;
225237

226238
/* append_rel_list contains all append rels; ignore others */
227239
if (appinfo->parent_relid != relid)
228240
continue;
229241

230-
(void) build_simple_rel(root, appinfo->child_relid,
231-
rel);
242+
childrel = build_simple_rel(root, appinfo->child_relid,
243+
rel);
244+
245+
/* Nothing more to do for an unpartitioned table. */
246+
if (!rel->part_scheme)
247+
continue;
248+
249+
/*
250+
* The order of partition OIDs in append_rel_list is the same as
251+
* the order in the PartitionDesc, so the order of part_rels will
252+
* also match the PartitionDesc. See expand_partitioned_rtentry.
253+
*/
254+
Assert(cnt_parts < nparts);
255+
rel->part_rels[cnt_parts] = childrel;
256+
cnt_parts++;
232257
}
258+
259+
/* We should have seen all the child partitions. */
260+
Assert(cnt_parts == nparts);
233261
}
234262

235263
return rel;
@@ -527,6 +555,11 @@ build_join_rel(PlannerInfo *root,
527555
joinrel->joininfo = NIL;
528556
joinrel->has_eclass_joins = false;
529557
joinrel->top_parent_relids = NULL;
558+
joinrel->part_scheme = NULL;
559+
joinrel->nparts = 0;
560+
joinrel->boundinfo = NULL;
561+
joinrel->part_rels = NULL;
562+
joinrel->partexprs = NULL;
530563

531564
/* Compute information relevant to the foreign relations. */
532565
set_foreign_rel_properties(joinrel, outer_rel, inner_rel);

src/include/nodes/relation.h

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,9 @@ typedef struct PlannerInfo
266266
List *distinct_pathkeys; /* distinctClause pathkeys, if any */
267267
List *sort_pathkeys; /* sortClause pathkeys, if any */
268268

269+
List *part_schemes; /* Canonicalised partition schemes used in the
270+
* query. */
271+
269272
List *initial_rels; /* RelOptInfos we are now trying to join */
270273

271274
/* Use fetch_upper_rel() to get any particular upper rel */
@@ -326,6 +329,34 @@ typedef struct PlannerInfo
326329
((root)->simple_rte_array ? (root)->simple_rte_array[rti] : \
327330
rt_fetch(rti, (root)->parse->rtable))
328331

332+
/*
333+
* If multiple relations are partitioned the same way, all such partitions
334+
* will have a pointer to the same PartitionScheme. A list of PartitionScheme
335+
* objects is attached to the PlannerInfo. By design, the partition scheme
336+
* incorporates only the general properties of the partition method (LIST vs.
337+
* RANGE, number of partitioning columns and the type information for each)
338+
* and not the specific bounds.
339+
*
340+
* We store the opclass-declared input data types instead of the partition key
341+
* datatypes since the former rather than the latter are used to compare
342+
* partition bounds. Since partition key data types and the opclass declared
343+
* input data types are expected to be binary compatible (per ResolveOpClass),
344+
* both of those should have same byval and length properties.
345+
*/
346+
typedef struct PartitionSchemeData
347+
{
348+
char strategy; /* partition strategy */
349+
int16 partnatts; /* number of partition attributes */
350+
Oid *partopfamily; /* OIDs of operator families */
351+
Oid *partopcintype; /* OIDs of opclass declared input data types */
352+
Oid *parttypcoll; /* OIDs of collations of partition keys. */
353+
354+
/* Cached information about partition key data types. */
355+
int16 *parttyplen;
356+
bool *parttypbyval;
357+
} PartitionSchemeData;
358+
359+
typedef struct PartitionSchemeData *PartitionScheme;
329360

330361
/*----------
331362
* RelOptInfo
@@ -456,7 +487,7 @@ typedef struct PlannerInfo
456487
* other rels for which we have tried and failed to prove
457488
* this one unique
458489
*
459-
* The presence of the remaining fields depends on the restrictions
490+
* The presence of the following fields depends on the restrictions
460491
* and joins that the relation participates in:
461492
*
462493
* baserestrictinfo - List of RestrictInfo nodes, containing info about
@@ -487,6 +518,21 @@ typedef struct PlannerInfo
487518
* We store baserestrictcost in the RelOptInfo (for base relations) because
488519
* we know we will need it at least once (to price the sequential scan)
489520
* and may need it multiple times to price index scans.
521+
*
522+
* If the relation is partitioned, these fields will be set:
523+
*
524+
* part_scheme - Partitioning scheme of the relation
525+
* boundinfo - Partition bounds
526+
* nparts - Number of partitions
527+
* part_rels - RelOptInfos for each partition
528+
* partexprs - Partition key expressions
529+
*
530+
* Note: A base relation always has only one set of partition keys, but a join
531+
* relation may have as many sets of partition keys as the number of relations
532+
* being joined. partexprs is an array containing part_scheme->partnatts
533+
* elements, each of which is a list of partition key expressions. For a base
534+
* relation each list contains only one expression, but for a join relation
535+
* there can be one per baserel.
490536
*----------
491537
*/
492538
typedef enum RelOptKind
@@ -592,6 +638,14 @@ typedef struct RelOptInfo
592638

593639
/* used by "other" relations */
594640
Relids top_parent_relids; /* Relids of topmost parents */
641+
642+
/* used for partitioned relations */
643+
PartitionScheme part_scheme; /* Partitioning scheme. */
644+
int nparts; /* number of partitions */
645+
struct PartitionBoundInfoData *boundinfo; /* Partition bounds */
646+
struct RelOptInfo **part_rels; /* Array of RelOptInfos of partitions,
647+
* stored in the same order of bounds */
648+
List **partexprs; /* Partition key expressions. */
595649
} RelOptInfo;
596650

597651
/*

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy