Implement hypothetical multi-level partitioning.

This commit is contained in:
Julien Rouhaud 2018-06-30 18:57:14 +02:00
parent e64cfedaa3
commit 21ae10ac3c
7 changed files with 419 additions and 227 deletions

View file

@ -93,8 +93,8 @@ CREATE FUNCTION hypopg_reset_table()
AS '$libdir/hypopg', 'hypopg_reset_table';
CREATE FUNCTION hypopg_table(OUT relid oid, OUT tablename text,
OUT parentid oid,
OUT partition_by_clause text, OUT partition_of_clause text)
OUT parentid oid, OUT rootid oid,
OUT partition_by_clause text, OUT partition_bounds text)
RETURNS SETOF record
LANGUAGE c COST 100
AS '$libdir/hypopg', 'hypopg_table';

View file

@ -344,8 +344,8 @@ hypo_get_relation_info_hook(PlannerInfo *root,
}
}
/* Close the relation release the lock now */
heap_close(relation, AccessShareLock);
/* Close the relation and keep the lock, it might be reopened later */
heap_close(relation, NoLock);
#if PG_VERSION_NUM >= 100000
if(hypo_table_oid_is_hypothetical(relationObjectId))

View file

@ -63,7 +63,9 @@ PG_FUNCTION_INFO_V1(hypopg_statistic);
#if PG_VERSION_NUM >= 100000
static void hypo_do_analyze_partition(Relation onerel, Relation pgstats,
hypoTable *entry, float4 fraction, hypoTable *root_entry);
hypoTable *entry, float4 fraction);
static void hypo_do_analyze_tree(Relation onerel, Relation pgstats,
float4 fraction, hypoTable *parent);
static uint32 hypo_hash_fn(const void *key, Size keysize);
static void hypo_update_attstats(hypoTable *part, int natts,
VacAttrStats **vacattrstats, Relation pgstats);
@ -77,7 +79,7 @@ static void hypo_update_attstats(hypoTable *part, int natts,
*/
Selectivity
hypo_clauselist_selectivity(PlannerInfo *root, RelOptInfo *rel, List *clauses,
Oid table_relid)
Oid table_relid, Oid parent_oid)
{
Selectivity selectivity;
PlannerInfo *root_dummy;
@ -120,9 +122,10 @@ hypo_clauselist_selectivity(PlannerInfo *root, RelOptInfo *rel, List *clauses,
/* Are we estimating selectivity for hypothetical partitioning? */
if (clauses == NIL)
{
hypoTable *parent = hypo_find_table(table_relid);
hypoTable *part = hypo_find_table(parent_oid, false);
Assert(root != NULL);
Assert(part->partkey);
/* add the hypothetical partition oid to be able to get the
* constraints */
@ -130,7 +133,7 @@ hypo_clauselist_selectivity(PlannerInfo *root, RelOptInfo *rel, List *clauses,
root_dummy->parse->rtable = list_make1(rte);
/* get the partition constraints, setup for a rel with relid 1 */
clauses = hypo_get_partition_constraints(root_dummy, rel, parent);
clauses = hypo_get_partition_constraints(root_dummy, rel, part);
/*
* and remove the hypothetical oid to avoid computing selectivity with
@ -172,7 +175,7 @@ hypo_clauselist_selectivity(PlannerInfo *root, RelOptInfo *rel, List *clauses,
* before and after this function.
*/
static void hypo_do_analyze_partition(Relation onerel, Relation pgstats,
hypoTable *part, float4 fraction, hypoTable *root_entry)
hypoTable *part, float4 fraction)
{
int attr_cnt,
tcnt,
@ -184,7 +187,7 @@ static void hypo_do_analyze_partition(Relation onerel, Relation pgstats,
Oid save_userid;
int save_sec_context;
int save_nestlevel;
Oid root_tableid = root_entry->oid;
Oid root_tableid = part->rootid;
List *context = deparse_context_for(get_rel_name(root_tableid),
root_tableid);
StringInfoData buf;
@ -237,7 +240,7 @@ static void hypo_do_analyze_partition(Relation onerel, Relation pgstats,
/*
* Acquire the sample rows
*/
constraints = hypo_get_qual_from_partbound(root_entry, part->boundspec);
constraints = hypo_get_partition_quals_inh(part, NULL);
constraints = (List *) make_ands_explicit(constraints);
str = deparse_expression((Node *) constraints, context, false, false);
@ -343,6 +346,28 @@ static void hypo_do_analyze_partition(Relation onerel, Relation pgstats,
anl_context = NULL;
}
static void hypo_do_analyze_tree(Relation onerel, Relation pgstats,
float4 fraction, hypoTable *parent)
{
ListCell *lc;
Assert(hypoTables);
foreach(lc, hypoTables)
{
hypoTable *part = (hypoTable *) lfirst(lc);
if (!OidIsValid(part->parentid))
continue;
if (part->parentid != parent->oid)
continue;
hypo_do_analyze_partition(onerel, pgstats, part, fraction);
hypo_do_analyze_tree(onerel, pgstats, fraction, part);
}
}
static uint32 hypo_hash_fn(const void *key, Size keysize)
{
const hypoStatsKey *k = (const hypoStatsKey *) key;
@ -505,10 +530,9 @@ HYPO_PARTITION_NOT_SUPPORTED();
#else
Oid root_tableid = PG_GETARG_OID(0);
float4 fraction = PG_GETARG_FLOAT4(1);
hypoTable *root_entry = hypo_find_table(root_tableid);
hypoTable *root_entry = hypo_find_table(root_tableid, true);
Relation onerel;
Relation pgstats;
ListCell *lc;
int ret;
if (!root_entry)
@ -541,15 +565,7 @@ HYPO_PARTITION_NOT_SUPPORTED();
onerel = heap_open(root_tableid, AccessShareLock);
pgstats = heap_open(StatisticRelationId, AccessShareLock);
foreach(lc, hypoTables)
{
hypoTable *part = (hypoTable *) lfirst(lc);
if (part->parentid != root_tableid)
continue;
hypo_do_analyze_partition(onerel, pgstats, part, fraction, root_entry);
}
hypo_do_analyze_tree(onerel, pgstats, fraction, root_entry);
/* release SPI related resources (and return to caller's context) */
SPI_finish();

View file

@ -1582,7 +1582,7 @@ hypo_estimate_index(hypoIndex *entry, RelOptInfo *rel)
Selectivity selectivity;
selectivity = hypo_clauselist_selectivity(NULL, rel, entry->indpred,
entry->relid);
entry->relid, InvalidOid);
elog(DEBUG1, "hypopg: selectivity for index \"%s\": %lf",
entry->indexname, selectivity);

View file

@ -75,7 +75,16 @@ PG_FUNCTION_INFO_V1(hypopg_table);
#if PG_VERSION_NUM >= 100000
static void hypo_addTable(hypoTable *entry);
static int hypo_expand_partitioned_entry(PlannerInfo *root, Oid
relationObjectId, RelOptInfo *rel, Relation parentrel,
hypoTable *branch, int firstpos, int parent_rti);
static void hypo_expand_single_inheritance_child(PlannerInfo *root,
Oid relationObjectId, RelOptInfo *rel, Relation parentrel,
hypoTable *branch, RangeTblEntry *rte, hypoTable *child, Oid newrelid,
int parent_rti, bool expandBranch);
static List *hypo_find_inheritance_children(hypoTable *parent);
static List *hypo_get_qual_from_partbound(hypoTable *parent,
PartitionBoundSpec *spec);
static PartitionDesc hypo_generate_partitiondesc(hypoTable *parent);
static void hypo_generate_partkey(CreateStmt *stmt, Oid parentid,
hypoTable *entry);
@ -88,17 +97,15 @@ static Oid hypo_get_default_partition_oid(Oid parentid);
static char *hypo_get_partbounddef(hypoTable *entry);
static char *hypo_get_partkeydef(hypoTable *entry);
static hypoTable *hypo_newTable(Oid parentid);
static Oid hypo_table_find_parent_entry(hypoTable *entry);
static Oid hypo_table_find_parent_oid(Oid parentid);
static Oid hypo_table_find_root_oid(hypoTable *entry);
static bool hypo_table_name_is_hypothetical(const char *name);
static hypoTable *hypo_table_find_parent_oid(Oid parentid);
static hypoTable *hypo_table_name_get_entry(const char *name);
static void hypo_table_pfree(hypoTable *entry);
static bool hypo_table_remove(Oid tableid);
static const hypoTable *hypo_table_store_parsetree(CreateStmt *node,
const char *queryString, Oid parentid);
const char *queryString, Oid parentid, Oid rootid);
static PartitionBoundSpec *hypo_transformPartitionBound(ParseState *pstate,
hypoTable *parent, PartitionBoundSpec *spec);
static void hypo_partition_table(PlannerInfo *root, RelOptInfo *rel,
static void hypo_set_relation_partition_info(PlannerInfo *root, RelOptInfo *rel,
hypoTable *entry);
static List *hypo_get_qual_for_hash(hypoTable *parent, PartitionBoundSpec *spec);
static List *hypo_get_qual_for_list(hypoTable *parent, PartitionBoundSpec *spec);
@ -122,6 +129,191 @@ hypo_addTable(hypoTable *entry)
MemoryContextSwitchTo(oldcontext);
}
/*
* Adaptation of expand_partitioned_rtentry
*/
static int
hypo_expand_partitioned_entry(PlannerInfo *root, Oid relationObjectId,
RelOptInfo *rel, Relation parentrel, hypoTable *branch, int firstpos,
int parent_rti)
{
hypoTable *parent;
List *inhoids;
int nparts;
RangeTblEntry *rte;
ListCell *cell;
int newrelid, oldsize = root->simple_rel_array_size;
int i;
Assert(hypo_table_oid_is_hypothetical(relationObjectId));
if (!branch)
parent = hypo_find_table(relationObjectId, false);
else
parent = branch;
/* get all of the partition oids */
inhoids = hypo_find_inheritance_children(parent);
nparts = list_length(inhoids);
/*
* resize and clean rte and rel arrays. We need a slot for self expansion
* and one per partition
*/
root->simple_rel_array_size += nparts + 1;
root->simple_rel_array = (RelOptInfo **)
repalloc(root->simple_rel_array,
root->simple_rel_array_size *
sizeof(RelOptInfo *));
root->simple_rte_array = (RangeTblEntry **)
repalloc(root->simple_rte_array,
root->simple_rel_array_size *
sizeof(RangeTblEntry *));
for (i=oldsize; i<root->simple_rel_array_size; i++)
{
root->simple_rel_array[i] = NULL;
root->simple_rte_array[i] = NULL;
}
/* Get the rte from the root partition */
rte = root->simple_rte_array[rel->relid];
/* if this is not the root partition, update the basic metadata */
if (!branch)
{
Assert(parent_rti == -1);
rte->relkind = RELKIND_PARTITIONED_TABLE;
rte->inh = true;
HYPO_TAG_RTI(rel->relid, root);
}
else /* branch partition, expand it */
{
/* The rte we retrieved has already been updated */
Assert(rte->relkind == RELKIND_PARTITIONED_TABLE);
rte = copyObject(rte);
if(!rte->alias)
rte->alias = makeNode(Alias);
rte->alias->aliasname = branch->tablename;
hypo_expand_single_inheritance_child(root, relationObjectId, rel,
parentrel, branch, rte, branch, firstpos, parent_rti, true);
firstpos++;
}
/* add the partitioned table itself */
root->simple_rte_array[firstpos] = rte;
root->parse->rtable = lappend(root->parse->rtable,
root->simple_rte_array[firstpos]);
HYPO_TAG_RTI(firstpos, root);
/*
* create RangeTblEntries and AppendRelInfos hypothetically
* for all hypothetical partitions
*/
newrelid = firstpos + 1;
foreach(cell, inhoids)
{
Oid childOid = lfirst_oid(cell);
hypoTable *child;
child = hypo_find_table(childOid, false);
/* Expand the child if it's partitioned */
if (child->partkey)
{
/*
* firstpos-1 is the ancestor rti for expanded children of
* this branch.
*/
newrelid = hypo_expand_partitioned_entry(root, relationObjectId,
rel, parentrel, child, newrelid, firstpos-1);
continue;
}
/* firstpos-1 is the ancestor rti of this children */
hypo_expand_single_inheritance_child(root, relationObjectId, rel,
parentrel, branch, rte, child, newrelid, firstpos-1, false);
newrelid++;
}
/* add partition info for root partition */
if (!branch)
hypo_set_relation_partition_info(root, rel, parent);
return newrelid;
}
/*
* Adaptation of expand_single_inheritance_child
*/
static void
hypo_expand_single_inheritance_child(PlannerInfo *root, Oid relationObjectId,
RelOptInfo *rel, Relation parentrel, hypoTable *branch,
RangeTblEntry *rte, hypoTable *child, Oid newrelid, int parent_rti,
bool expandBranch)
{
RangeTblEntry *childrte;
AppendRelInfo *appinfo;
childrte = copyObject(rte);
if (!expandBranch)
{
childrte->rtekind = RTE_RELATION;
childrte->inh = false;
}
else
{
Assert(branch);
Assert(child == branch);
}
childrte->relid = relationObjectId; //originalOID;
if (child->partkey)
childrte->relkind = RELKIND_PARTITIONED_TABLE;
else
childrte->relkind = RELKIND_RELATION;
if(!childrte->alias)
childrte->alias = makeNode(Alias);
childrte->alias->aliasname = child->tablename;
/* FIXME maybe use a mapping array here instead of rte->values_lists*/
if (expandBranch)
childrte->values_lists = list_make1_oid(branch->oid); //partitionOID
else
childrte->values_lists = list_make1_oid(child->oid); //partitionOID
root->simple_rte_array[newrelid] = childrte;
HYPO_TAG_RTI(newrelid, root);
appinfo = makeNode(AppendRelInfo);
if (expandBranch || branch)
appinfo->parent_relid = parent_rti;
else
appinfo->parent_relid = rel->relid;
appinfo->child_relid = newrelid;
appinfo->parent_reltype = parentrel->rd_rel->reltype;
appinfo->child_reltype = parentrel->rd_rel->reltype;
make_inh_translation_list(parentrel, parentrel, newrelid,
&appinfo->translated_vars);
appinfo->parent_reloid = relationObjectId;
root->append_rel_list = lappend(root->append_rel_list,
appinfo);
root->parse->rtable = lappend(root->parse->rtable,
root->simple_rte_array[newrelid]);
}
/*
* Adaptation of find_inheritance_children().
*
@ -225,6 +417,9 @@ hypo_generate_partitiondesc(hypoTable *parent)
/* Get partition oids from pg_inherits */
inhoids = hypo_find_inheritance_children(parent);
if (!inhoids)
return NULL;
/* Collect bound spec nodes in a list */
i = 0;
partoids = NIL;
@ -1047,7 +1242,7 @@ hypo_generate_partition_key_exprs(hypoTable *entry, RelOptInfo *rel)
static PartitionBoundSpec *
hypo_get_boundspec(Oid tableid)
{
hypoTable *entry = hypo_find_table(tableid);
hypoTable *entry = hypo_find_table(tableid, true);
if (entry)
return entry->boundspec;
@ -1173,7 +1368,7 @@ hypo_get_partkeydef(hypoTable *entry)
elog(ERROR, "hypopg: hypothetical table %s is not partitioned",
quote_identifier(entry->tablename));
relid = hypo_table_find_root_oid(entry);
relid = entry->rootid;
partexpr_item = list_head(partkey->partexprs);
context = deparse_context_for(get_relation_name(relid), relid);
@ -1263,6 +1458,7 @@ static hypoTable *
hypo_newTable(Oid parentid)
{
hypoTable *entry;
hypoTable *parent;
MemoryContext oldcontext;
oldcontext = MemoryContextSwitchTo(HypoMemoryContext);
@ -1279,31 +1475,29 @@ hypo_newTable(Oid parentid)
* If the given root table oid isn't present in hypoTables, we're
* partitioning it, so keep its oid, otherwise generate a new oid
*/
entry->parentid = hypo_table_find_parent_oid(parentid);
parent= hypo_table_find_parent_oid(parentid);
if (entry->parentid != InvalidOid)
entry->oid = hypo_getNewOid(parentid);
if (parent)
{
entry->parentid = parent->oid;
entry->oid = hypo_getNewOid(parent->rootid);
entry->rootid = parent->rootid;
}
else
{
entry->parentid = InvalidOid;
entry->oid = parentid;
entry->rootid = parentid;
}
return entry;
}
/*
* Find the direct parent oid of a hypothetical partition entry. Return NULL
* is it's a top level table.
*/
static Oid
hypo_table_find_parent_entry(hypoTable *entry)
{
return hypo_table_find_parent_oid(entry->parentid);
}
/*
* Find the direct parent oid of a hypothetical partition given it's parentid
* field. Return InvalidOid is it's a top level table.
*/
static Oid
static hypoTable *
hypo_table_find_parent_oid(Oid parentid)
{
ListCell *lc;
@ -1313,31 +1507,10 @@ hypo_table_find_parent_oid(Oid parentid)
hypoTable *entry = (hypoTable *) lfirst(lc);
if (entry->oid == parentid)
return entry->oid;
return entry;
}
return InvalidOid;
}
/*
* Find the root table identifier of an hypothetical table
*/
static Oid
hypo_table_find_root_oid(hypoTable *entry)
{
Oid parent, last;
if (entry->parentid == InvalidOid)
return entry->oid;
parent = hypo_table_find_parent_entry(entry);
while (parent != InvalidOid)
{
last = parent;
parent = hypo_table_find_parent_entry(entry);
}
return last;
return NULL;
}
/*
@ -1345,7 +1518,7 @@ hypo_table_find_root_oid(hypoTable *entry)
* otherwise return NULL
*/
hypoTable *
hypo_find_table(Oid tableid)
hypo_find_table(Oid tableid, bool missing_ok)
{
ListCell *lc;
@ -1359,14 +1532,18 @@ hypo_find_table(Oid tableid)
return entry;
}
if (!missing_ok)
elog(ERROR, "hypopg: could not find parent for %d", tableid);
return NULL;
}
/*
* Is the given name an hypothetical partition ?
* Return the hypothetical oid if the given name is an hypothetical partition,
* otherwise return InvalidOid
*/
static bool
hypo_table_name_is_hypothetical(const char *name)
static hypoTable *
hypo_table_name_get_entry(const char *name)
{
ListCell *lc;
@ -1375,10 +1552,10 @@ hypo_table_name_is_hypothetical(const char *name)
hypoTable *entry = (hypoTable *) lfirst(lc);
if (strcmp(entry->tablename, name) == 0)
return true;
return entry;
}
return false;
return NULL;
}
/*
@ -1494,7 +1671,7 @@ hypo_table_reset(void)
*/
static const hypoTable *
hypo_table_store_parsetree(CreateStmt *node, const char *queryString,
Oid parentid)
Oid parentid, Oid rootid)
{
hypoTable *entry;
List *stmts;
@ -1528,7 +1705,7 @@ hypo_table_store_parsetree(CreateStmt *node, const char *queryString,
if (defaultpart != InvalidOid)
elog(ERROR, "partition \"%s\" conflicts with existing default partition \"%s\"",
quote_identifier(node->relation->relname),
quote_identifier(hypo_find_table(defaultpart)->tablename));
quote_identifier(hypo_find_table(defaultpart, false)->tablename));
}
}
@ -1539,7 +1716,7 @@ hypo_table_store_parsetree(CreateStmt *node, const char *queryString,
/* The CreateStmt specified a PARTITION BY clause, store it */
if (stmt->partspec)
hypo_generate_partkey(stmt, parentid, entry);
hypo_generate_partkey(stmt, rootid, entry);
if (boundspec)
{
@ -1551,7 +1728,7 @@ hypo_table_store_parsetree(CreateStmt *node, const char *queryString,
oldcontext = MemoryContextSwitchTo(HypoMemoryContext);
entry->boundspec = hypo_transformPartitionBound(pstate,
hypo_find_table(parentid), boundspec);
hypo_find_table(parentid, false), boundspec);
MemoryContextSwitchTo(oldcontext);
}
@ -1628,12 +1805,12 @@ hypo_transformPartitionBound(ParseState *pstate, hypoTable *parent,
/* Get the only column's name in case we need to output an error */
if (key->partattrs[0] != 0)
colname = get_attname(parent->oid,
colname = get_attname(parent->rootid,
key->partattrs[0], false);
else
colname = deparse_expression((Node *) linitial(partexprs),
deparse_context_for(parent->tablename,
parent->oid),
parent->rootid),
false, false);
/* Need its type data too */
coltype = get_partition_col_typid(key, 0);
@ -1713,7 +1890,7 @@ hypo_transformPartitionBound(ParseState *pstate, hypoTable *parent,
/* Get the column's name in case we need to output an error */
if (key->partattrs[i] != 0)
colname = get_attname(parent->oid,
colname = get_attname(parent->rootid,
key->partattrs[i], false);
else
{
@ -1779,112 +1956,19 @@ hypo_injectHypotheticalPartitioning(PlannerInfo *root,
Oid relationObjectId,
RelOptInfo *rel)
{
hypoTable *parent;
List *inhoids;
int nparts;
Assert(HYPO_ENABLED());
Assert(hypo_table_oid_is_hypothetical(relationObjectId));
parent = hypo_find_table(relationObjectId);
/* get all of the partition oids */
inhoids = hypo_find_inheritance_children(parent);
nparts = list_length(inhoids);
/*
* if this rel is parent, prepare some structures to inject
* hypothetical partitioning
*/
if(!HYPO_RTI_IS_TAGGED(rel->relid,root))
{
int i;
int oldsize = root->simple_rel_array_size;
RangeTblEntry *rte;
ListCell *cell;
AppendRelInfo *appinfo;
Relation parentrel;
/* resize and clean rte and rel arrays */
root->simple_rel_array_size = oldsize + nparts + 1;
root->simple_rel_array = (RelOptInfo **)
repalloc(root->simple_rel_array,
root->simple_rel_array_size *
sizeof(RelOptInfo *));
root->simple_rte_array = (RangeTblEntry **)
repalloc(root->simple_rte_array,
root->simple_rel_array_size *
sizeof(RangeTblEntry *));
for (i=oldsize; i<root->simple_rel_array_size; i++)
{
root->simple_rel_array[i] = NULL;
root->simple_rte_array[i] = NULL;
}
/* rewrite and restore this rel's rte */
rte = root->simple_rte_array[rel->relid];
rte->relkind = RELKIND_PARTITIONED_TABLE;
rte->inh = true;
root->simple_rte_array[rel->relid] = rte;
root->simple_rte_array[oldsize] = rte;
root->parse->rtable = lappend(root->parse->rtable,
root->simple_rte_array[oldsize]);
HYPO_TAG_RTI(rel->relid, root);
HYPO_TAG_RTI(oldsize, root);
/*
* create RangeTblEntries and AppendRelInfos hypothetically
* for all hypothetical partitions
*/
i = 1;
foreach(cell, inhoids)
{
int newrelid;
Oid childOid = lfirst_oid(cell);
hypoTable *child;
RangeTblEntry *childrte;
Relation parentrel;
child = hypo_find_table(childOid);
newrelid = oldsize + i;
childrte = copyObject(rte);
childrte->rtekind = RTE_RELATION;
childrte->relid = relationObjectId; //originalOID;
childrte->relkind = RELKIND_RELATION;
childrte->inh = false;
if(!childrte->alias)
childrte->alias = makeNode(Alias);
childrte->alias->aliasname = child->tablename;
/* FIXME maybe use a mapping array here instead of rte->values_lists*/
childrte->values_lists = list_make1_oid(child->oid); //partitionOID
root->simple_rte_array[newrelid] = childrte;
HYPO_TAG_RTI(newrelid, root);
appinfo = makeNode(AppendRelInfo);
appinfo->parent_relid = rel->relid;
appinfo->child_relid = newrelid;
parentrel = heap_open(relationObjectId, NoLock);
appinfo->parent_reltype = parentrel->rd_rel->reltype;
appinfo->child_reltype = parentrel->rd_rel->reltype;
make_inh_translation_list(parentrel, parentrel, newrelid,
&appinfo->translated_vars);
heap_close(parentrel, NoLock);
appinfo->parent_reloid = relationObjectId;
root->append_rel_list = lappend(root->append_rel_list,
appinfo);
root->parse->rtable = lappend(root->parse->rtable,
root->simple_rte_array[newrelid]);
i++;
}
/* add partition info to this rel */
hypo_partition_table(root, rel, parent);
parentrel = heap_open(relationObjectId, AccessShareLock);
hypo_expand_partitioned_entry(root, relationObjectId, rel, parentrel, NULL, root->simple_rel_array_size, -1);
heap_close(parentrel, NoLock);
}
/*
@ -1899,57 +1983,87 @@ hypo_injectHypotheticalPartitioning(PlannerInfo *root,
if (rel->reloptkind != RELOPT_BASEREL
&&HYPO_RTI_IS_TAGGED(rel->relid,root))
{
if (parent->partkey->strategy == PARTITION_STRATEGY_HASH)
Oid partoid;
hypoTable *part;
RangeTblEntry *rte = planner_rt_fetch(rel->relid, root);
Selectivity selectivity;
double pages;
Assert(rte->values_lists);
partoid = linitial_oid(rte->values_lists);
part = hypo_find_table(partoid, false);
/*
* there's no need to estimate branch partitions pages and tuples, but
* we have to setup their partitioning data
*/
if (part->partkey)
{
double pages;
pages = ceil(rel->pages / nparts);
rel->pages = (BlockNumber) pages;
rel->tuples = clamp_row_est(rel->tuples / nparts);
hypo_set_relation_partition_info(root, rel, part);
return;
}
else
{
Selectivity selectivity;
double pages;
selectivity = hypo_clauselist_selectivity(root, rel, NIL,
parent->oid);
/*
* hypo_clauselist_selectivity will retrieve the constraints for this
* partition and all its ancestors
*/
selectivity = hypo_clauselist_selectivity(root, rel, NIL,
part->rootid, part->parentid);
elog(DEBUG1, "hypopg: selectivity for partition \"%s\": %lf",
hypo_find_table(linitial_oid(planner_rt_fetch(rel->relid,
root)->values_lists))->tablename,
selectivity);
elog(DEBUG1, "hypopg: selectivity for partition \"%s\": %lf",
hypo_find_table(linitial_oid(planner_rt_fetch(rel->relid,
root)->values_lists), false)->tablename,
selectivity);
pages = ceil(rel->pages * selectivity);
rel->pages = (BlockNumber) pages;
rel->tuples = clamp_row_est(rel->tuples * selectivity);
}
pages = ceil(rel->pages * selectivity);
rel->pages = (BlockNumber) pages;
rel->tuples = clamp_row_est(rel->tuples * selectivity);
}
}
/*
* If this is the table we want to hypothetically partition, modifies its
* metadata to add partitioning information
* Set partitioning scheme and relation information for a hypothetically
* partitioned table.
*
* Heavily inspired on set_relation_partition_info
*/
static void
hypo_partition_table(PlannerInfo *root, RelOptInfo *rel, hypoTable *entry)
hypo_set_relation_partition_info(PlannerInfo *root, RelOptInfo *rel,
hypoTable *entry)
{
PartitionDesc partdesc;
PartitionKey partkey;
Assert(planner_rt_fetch(rel->relid, root)->relkind ==
RELKIND_PARTITIONED_TABLE);
partdesc = hypo_generate_partitiondesc(entry);
if (!partdesc)
return;
partkey = entry->partkey;
rel->part_scheme = hypo_find_partition_scheme(root, partkey);
Assert(partdesc != NULL && rel->part_scheme != NULL);
rel->boundinfo = partition_bounds_copy(partdesc->boundinfo, partkey);
rel->nparts = partdesc->nparts;
hypo_generate_partition_key_exprs(entry, rel);
rel->partition_qual = NIL; //FIX ME for multi-level partition
/* Add the partition_qual if it's not the root partition */
if (OidIsValid(entry->parentid))
{
hypoTable *parent = hypo_find_table(entry->parentid, false);
rel->partition_qual = hypo_get_partition_constraints(root, rel, parent);
}
}
/*
* Returns a List of partition constraints from its partition bound spec
* Given a rel corresponding to a hypothetically partitioned table, returns a
* List of partition constraints for this partition, including all its
* ancestors if any. The main processing is done in
* hypo_get_partition_quals_inh.
*
* It is inspired on get_relation_constraints()
*/
@ -1959,17 +2073,17 @@ hypo_get_partition_constraints(PlannerInfo *root, RelOptInfo *rel, hypoTable *pa
ListCell *lc;
Oid childOid;
hypoTable *child;
PartitionBoundSpec *spec;
List *constraints;
/* FIXME maybe use a mapping array here instead of rte->values_lists*/
lc = list_head(planner_rt_fetch(rel->relid, root)->values_lists);
childOid = lfirst_oid(lc);
child = hypo_find_table(childOid);
spec = child->boundspec;
child = hypo_find_table(childOid, false);
Assert(child->parentid == parent->oid);
/* get its partition constraints */
constraints = hypo_get_qual_from_partbound(parent,spec);
constraints = hypo_get_partition_quals_inh(child, parent);
if (constraints)
{
@ -1984,8 +2098,40 @@ hypo_get_partition_constraints(PlannerInfo *root, RelOptInfo *rel, hypoTable *pa
/* Fix Vars to have the desired varno */
if (rel->relid != 1)
ChangeVarNodes((Node *) constraints, 1, rel->relid, 0);
}
return constraints;
}
/*
* Return the partition constraints belonging to the given partition and all
* of its ancestor. The parent parameter is optional.
*/
List *
hypo_get_partition_quals_inh(hypoTable *part, hypoTable *parent)
{
PartitionBoundSpec *spec;
List *constraints = NIL;
Assert(OidIsValid(part->parentid));
if (parent == NULL)
parent = hypo_find_table(part->parentid, false);
spec = part->boundspec;
constraints = hypo_get_qual_from_partbound(parent, spec);
/* Append parent's constraint if any */
if (OidIsValid(parent->parentid))
{
List *parent_constraints;
parent_constraints = hypo_get_partition_quals_inh(parent, NULL);
constraints = list_concat(parent_constraints, constraints);
}
return constraints;
}
@ -1996,7 +2142,7 @@ hypo_get_partition_constraints(PlannerInfo *root, RelOptInfo *rel, hypoTable *pa
*
* Heavily inspired on get_qual_from_partbound
*/
List *
static List *
hypo_get_qual_from_partbound(hypoTable *parent, PartitionBoundSpec *spec)
{
PartitionKey key = parent->partkey;
@ -2655,8 +2801,9 @@ HYPO_PARTITION_NOT_SUPPORTED();
#else
const char *partname = PG_GETARG_NAME(0)->data;
char *partitionof = TextDatumGetCString(PG_GETARG_TEXT_PP(1));
char *partition_by = NULL;
StringInfoData sql;
Oid parentid;
Oid parentid, rootid;
const hypoTable *entry;
List *parsetree_list;
RawStmt *raw_stmt;
@ -2668,11 +2815,9 @@ HYPO_PARTITION_NOT_SUPPORTED();
int i = 0;
if (!PG_ARGISNULL(2))
{
elog(ERROR, "hypopg: multi-level partitioning is not supported yet");
}
partition_by = TextDatumGetCString(PG_GETARG_TEXT_PP(2));
if (hypo_table_name_is_hypothetical(partname))
if (hypo_table_name_get_entry(partname) != NULL)
elog(ERROR, "hypopg: hypothetical table %s already exists",
quote_identifier(partname));
@ -2693,6 +2838,10 @@ HYPO_PARTITION_NOT_SUPPORTED();
appendStringInfo(&sql, "CREATE TABLE %s %s",
quote_identifier(partname), partitionof);
if (partition_by)
appendStringInfo(&sql, " %s",
partition_by);
parsetree_list = pg_parse_query(sql.data);
raw_stmt = (RawStmt *) linitial(parsetree_list);
stmt = (CreateStmt *) raw_stmt->stmt;
@ -2701,24 +2850,47 @@ HYPO_PARTITION_NOT_SUPPORTED();
if (!stmt->partbound || !stmt->inhRelations)
elog(ERROR, "hypopg: you must specify a PARTITION OF clause");
if (stmt->partspec)
elog(ERROR, "hypopg: multi-level partitioning is not supported yet");
/* Find the parent's oid */
if (list_length(stmt->inhRelations) != 1)
elog(ERROR, "hypopg: unexpected list lenght %d, expected 1",
elog(ERROR, "hypopg: unexpected list length %d, expected 1",
list_length(stmt->inhRelations));
rv = (RangeVar *) linitial(stmt->inhRelations);
/* FIXME change this when handling multi-level partitioning */
parentid = RangeVarGetRelid(rv, AccessShareLock, false);
parentid = RangeVarGetRelid(rv, AccessShareLock, true);
if (!hypo_table_oid_is_hypothetical(parentid))
if (OidIsValid(parentid) && !hypo_table_oid_is_hypothetical(parentid))
elog(ERROR, "hypopg: %s must be hypothetically partitioned first",
quote_identifier(rv->relname));
/* Look for a hypothetical parent if we didn't find a real table */
if (!OidIsValid(parentid))
{
hypoTable *parent;
parent = hypo_table_name_get_entry(rv->relname);
if (parent == NULL)
elog(ERROR, "hypopg: %s does not exists",
quote_identifier(rv->relname));
if(rv->schemaname)
elog(ERROR, "hypopg: cannot use qualified name with hypothetical"
" partition");
parentid = parent->oid;
rootid = parent->rootid;
}
else
{
/*
* if we found a real table, there's no subpartitioning, so the root
* and the parent are the same
*/
rootid = parentid;
}
entry = hypo_table_store_parsetree((CreateStmt *) stmt, sql.data,
parentid);
parentid, rootid);
pfree(sql.data);
@ -2816,7 +2988,7 @@ HYPO_PARTITION_NOT_SUPPORTED();
Assert(IsA(raw_stmt->stmt, CreateStmt));
entry = hypo_table_store_parsetree((CreateStmt *) raw_stmt->stmt, sql.data,
tableid);
tableid, tableid);
/* special case for root table, copy it's original name */
strncpy(entry->tablename, root_name, NAMEDATALEN);
@ -2901,6 +3073,8 @@ HYPO_PARTITION_NOT_SUPPORTED();
else
values[i++] = ObjectIdGetDatum(entry->parentid);
values[i++] = ObjectIdGetDatum(entry->rootid);
if (entry->partkey)
values[i++] = CStringGetTextDatum(hypo_get_partkeydef(entry));
else

View file

@ -40,7 +40,7 @@ extern HTAB *hypoStatsHash;
#endif /* PG_VERSION_NUM >= 100000 */
Selectivity hypo_clauselist_selectivity(PlannerInfo *root, RelOptInfo *rel,
List *clauses, Oid table_relid);
List *clauses, Oid table_relid, Oid parent_oid);
/*--- Functions --- */

View file

@ -18,7 +18,7 @@
#if PG_VERSION_NUM >= 100000
#define HYPO_PARTITION_NOT_SUPPORTED
#define HYPO_TABLE_NB_COLS 5 /* # of column hypopg_table() returns */
#define HYPO_TABLE_NB_COLS 6 /* # of column hypopg_table() returns */
#define HYPO_ADD_PART_COLS 2 /* # of column hypopg_add_partition() returns */
#include "optimizer/paths.h"
@ -35,6 +35,8 @@ typedef struct hypoTable
Oid oid; /* hypothetical table unique identifier */
Oid parentid; /* In case of partition, it's direct parent,
otherwise InvalidOid */
Oid rootid; /* In case of partition, it's root parent,
otherwise InvalidOid */
char *tablename; /* hypothetical partition name, or original
table name for root parititon */
Oid namespace; /* Oid of the hypothetical table's schema */
@ -65,10 +67,10 @@ Datum hypopg_partition_table(PG_FUNCTION_ARGS);
Datum hypopg_reset_table(PG_FUNCTION_ARGS);
#if PG_VERSION_NUM >= 100000
hypoTable *hypo_find_table(Oid tableid);
hypoTable *hypo_find_table(Oid tableid, bool missing_ok);
List *hypo_get_partition_constraints(PlannerInfo *root, RelOptInfo *rel,
hypoTable *parent);
List *hypo_get_qual_from_partbound(hypoTable *parent, PartitionBoundSpec *spec);
List *hypo_get_partition_quals_inh(hypoTable *part, hypoTable *parent);
bool hypo_table_oid_is_hypothetical(Oid relid);
void hypo_injectHypotheticalPartitioning(PlannerInfo *root,
Oid relationObjectId,