diff --git a/hypopg.c b/hypopg.c index 3ca82e7..3eed965 100644 --- a/hypopg.c +++ b/hypopg.c @@ -305,7 +305,7 @@ hypo_get_relation_info_hook(PlannerInfo *root, bool inhparent, RelOptInfo *rel) { - if (HYPO_ENABLED()) + if (HYPO_ENABLED()) { Relation relation; @@ -341,8 +341,8 @@ hypo_get_relation_info_hook(PlannerInfo *root, if(hypo_table_oid_is_hypothetical(relationObjectId)) /* - * this relation is table we want to partition hypothetical, - * inject hypothetical partitioning + * this relation is table we want to partition hypothetical, + * inject hypothetical partitioning */ hypo_injectHypotheticalPartitioning(root, relationObjectId, rel); @@ -354,17 +354,18 @@ hypo_get_relation_info_hook(PlannerInfo *root, /* * if this child relation is excluded by constraints, call set_dummy_rel_pathlist */ -static void +static void hypo_set_rel_pathlist_hook(PlannerInfo *root, - RelOptInfo *rel, - Index rti, - RangeTblEntry *rte) + RelOptInfo *rel, + Index rti, + RangeTblEntry *rte) { - if(HYPO_ENABLED() && hypo_table_oid_is_hypothetical(rte->relid) && rte->relkind == 'r') - hypo_markDummyIfExcluded(root,rel,rti,rte); - - if (prev_set_rel_pathlist_hook) - prev_set_rel_pathlist_hook(root, rel, rti, rte); + if(HYPO_ENABLED() && hypo_table_oid_is_hypothetical(rte->relid) + && rte->relkind == 'r') + hypo_setPartitionPathlist(root,rel,rti,rte); + + if (prev_set_rel_pathlist_hook) + prev_set_rel_pathlist_hook(root, rel, rti, rte); } diff --git a/hypopg_import.c b/hypopg_import.c index 3dabeae..c38343d 100644 --- a/hypopg_import.c +++ b/hypopg_import.c @@ -37,6 +37,14 @@ #include "utils/ruleutils.h" #endif #include "optimizer/clauses.h" +#if PG_VERSION_NUM >= 110000 +#include "catalog/partition.h" +#include "optimizer/cost.h" +#include "optimizer/paths.h" +#include "partitioning/partbounds.h" +#include "utils/partcache.h" +#include "partitioning/partdefs.h" +#endif #include "optimizer/planner.h" #include "optimizer/var.h" #include "parser/parse_coerce.h" @@ -49,12 +57,11 @@ #include "utils/rel.h" #include "utils/syscache.h" + #include "include/hypopg_import.h" - - /* Copied from src/backend/optimizer/util/plancat.c, not exported. * * Build a targetlist representing the columns of the specified index. @@ -819,6 +826,7 @@ make_one_range_bound(PartitionKey key, int index, List *datums, bool lower) return bound; } + /* * Copied from src/backend/catalog/partition.c, not exported * @@ -831,74 +839,9 @@ qsort_partition_rbound_cmp(const void *a, const void *b, void *arg) PartitionRangeBound *b2 = (*(PartitionRangeBound *const *) b); PartitionKey key = (PartitionKey) arg; - return partition_rbound_cmp(key, b1->datums, b1->kind, b1->lower, b2); -} - -/* - * Copied from src/backend/catalog/partition.c, not exported - * - * partition_rbound_cmp - * - * Return for two range bounds whether the 1st one (specified in datums1, - * kind1, and lower1) is <, =, or > the bound specified in *b2. - * - * Note that if the values of the two range bounds compare equal, then we take - * into account whether they are upper or lower bounds, and an upper bound is - * considered to be smaller than a lower bound. This is important to the way - * that RelationBuildPartitionDesc() builds the PartitionBoundInfoData - * structure, which only stores the upper bound of a common boundary between - * two contiguous partitions. - */ -int32 -partition_rbound_cmp(PartitionKey key, - Datum *datums1, PartitionRangeDatumKind *kind1, - bool lower1, PartitionRangeBound *b2) -{ - int32 cmpval = 0; /* placate compiler */ - int i; - Datum *datums2 = b2->datums; - PartitionRangeDatumKind *kind2 = b2->kind; - bool lower2 = b2->lower; - - for (i = 0; i < key->partnatts; i++) - { - /* - * First, handle cases where the column is unbounded, which should not - * invoke the comparison procedure, and should not consider any later - * columns. Note that the PartitionRangeDatumKind enum elements - * compare the same way as the values they represent. - */ - if (kind1[i] < kind2[i]) - return -1; - else if (kind1[i] > kind2[i]) - return 1; - else if (kind1[i] != PARTITION_RANGE_DATUM_VALUE) - - /* - * The column bounds are both MINVALUE or both MAXVALUE. No later - * columns should be considered, but we still need to compare - * whether they are upper or lower bounds. - */ - break; - - cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i], - key->partcollation[i], - datums1[i], - datums2[i])); - if (cmpval != 0) - break; - } - - /* - * If the comparison is anything other than equal, we're done. If they - * compare equal though, we still have to consider whether the boundaries - * are inclusive or exclusive. Exclusive one is considered smaller of the - * two. - */ - if (cmpval == 0 && lower1 != lower2) - cmpval = lower1 ? 1 : -1; - - return cmpval; + return partition_rbound_cmp(key->partnatts, key->partsupfunc, + key->partcollation, b1->datums, b1->kind, + b1->lower, b2); } /* ---------- @@ -1254,104 +1197,104 @@ Expr * make_partition_op_expr(PartitionKey key, int keynum, uint16 strategy, Expr *arg1, Expr *arg2) { - Oid operoid; - bool need_relabel = false; - Expr *result = NULL; - - /* Get the correct btree operator for this partitioning column */ - operoid = get_partition_operator(key, keynum, strategy, &need_relabel); - - /* - * Chosen operator may be such that the non-Const operand needs to be - * coerced, so apply the same; see the comment in - * get_partition_operator(). - */ - if (!IsA(arg1, Const) && - (need_relabel || - key->partcollation[keynum] != key->parttypcoll[keynum])) - arg1 = (Expr *) makeRelabelType(arg1, - key->partopcintype[keynum], - -1, - key->partcollation[keynum], - COERCE_EXPLICIT_CAST); + Oid operoid; + bool need_relabel = false; + Expr *result = NULL; - /* Generate the actual expression */ - switch (key->strategy) - { - case PARTITION_STRATEGY_LIST: - { - List *elems = (List *) arg2; - int nelems = list_length(elems); - - Assert(nelems >= 1); - Assert(keynum == 0); - - if (nelems > 1 && - !type_is_array(key->parttypid[keynum])) - { - ArrayExpr *arrexpr; - ScalarArrayOpExpr *saopexpr; - - /* Construct an ArrayExpr for the right-hand inputs */ - arrexpr = makeNode(ArrayExpr); - arrexpr->array_typeid = - get_array_type(key->parttypid[keynum]); - arrexpr->array_collid = key->parttypcoll[keynum]; - arrexpr->element_typeid = key->parttypid[keynum]; - arrexpr->elements = elems; - arrexpr->multidims = false; - arrexpr->location = -1; - - /* Build leftop = ANY (rightop) */ - saopexpr = makeNode(ScalarArrayOpExpr); - saopexpr->opno = operoid; - saopexpr->opfuncid = get_opcode(operoid); - saopexpr->useOr = true; - saopexpr->inputcollid = key->partcollation[keynum]; - saopexpr->args = list_make2(arg1, arrexpr); - saopexpr->location = -1; - - result = (Expr *) saopexpr; - } - else - { - List *elemops = NIL; - ListCell *lc; - - foreach (lc, elems) - { - Expr *elem = lfirst(lc), - *elemop; - - elemop = make_opclause(operoid, - BOOLOID, - false, - arg1, elem, - InvalidOid, - key->partcollation[keynum]); - elemops = lappend(elemops, elemop); - } - - result = nelems > 1 ? makeBoolExpr(OR_EXPR, elemops, -1) : linitial(elemops); - } - break; - } - - case PARTITION_STRATEGY_RANGE: - result = make_opclause(operoid, - BOOLOID, - false, - arg1, arg2, - InvalidOid, - key->partcollation[keynum]); - break; - - default: - elog(ERROR, "invalid partitioning strategy"); - break; - } - - return result; + /* Get the correct btree operator for this partitioning column */ + operoid = get_partition_operator(key, keynum, strategy, &need_relabel); + + /* + * Chosen operator may be such that the non-Const operand needs to be + * coerced, so apply the same; see the comment in + * get_partition_operator(). + */ + if (!IsA(arg1, Const) && + (need_relabel || + key->partcollation[keynum] != key->parttypcoll[keynum])) + arg1 = (Expr *) makeRelabelType(arg1, + key->partopcintype[keynum], + -1, + key->partcollation[keynum], + COERCE_EXPLICIT_CAST); + + /* Generate the actual expression */ + switch (key->strategy) + { + case PARTITION_STRATEGY_LIST: + { + List *elems = (List *) arg2; + int nelems = list_length(elems); + + Assert(nelems >= 1); + Assert(keynum == 0); + + if (nelems > 1 && + !type_is_array(key->parttypid[keynum])) + { + ArrayExpr *arrexpr; + ScalarArrayOpExpr *saopexpr; + + /* Construct an ArrayExpr for the right-hand inputs */ + arrexpr = makeNode(ArrayExpr); + arrexpr->array_typeid = + get_array_type(key->parttypid[keynum]); + arrexpr->array_collid = key->parttypcoll[keynum]; + arrexpr->element_typeid = key->parttypid[keynum]; + arrexpr->elements = elems; + arrexpr->multidims = false; + arrexpr->location = -1; + + /* Build leftop = ANY (rightop) */ + saopexpr = makeNode(ScalarArrayOpExpr); + saopexpr->opno = operoid; + saopexpr->opfuncid = get_opcode(operoid); + saopexpr->useOr = true; + saopexpr->inputcollid = key->partcollation[keynum]; + saopexpr->args = list_make2(arg1, arrexpr); + saopexpr->location = -1; + + result = (Expr *) saopexpr; + } + else + { + List *elemops = NIL; + ListCell *lc; + + foreach (lc, elems) + { + Expr *elem = lfirst(lc), + *elemop; + + elemop = make_opclause(operoid, + BOOLOID, + false, + arg1, elem, + InvalidOid, + key->partcollation[keynum]); + elemops = lappend(elemops, elemop); + } + + result = nelems > 1 ? makeBoolExpr(OR_EXPR, elemops, -1) : linitial(elemops); + } + break; + } + + case PARTITION_STRATEGY_RANGE: + result = make_opclause(operoid, + BOOLOID, + false, + arg1, arg2, + InvalidOid, + key->partcollation[keynum]); + break; + + default: + elog(ERROR, "invalid partitioning strategy"); + break; + } + + return result; } /* @@ -1379,84 +1322,84 @@ get_range_key_properties(PartitionKey key, int keynum, Expr **keyCol, Const **lower_val, Const **upper_val) { - /* Get partition key expression for this column */ - if (key->partattrs[keynum] != 0) - { - *keyCol = (Expr *) makeVar(1, - key->partattrs[keynum], - key->parttypid[keynum], - key->parttypmod[keynum], - key->parttypcoll[keynum], - 0); - } - else - { - if (*partexprs_item == NULL) - elog(ERROR, "wrong number of partition key expressions"); - *keyCol = copyObject(lfirst(*partexprs_item)); - *partexprs_item = lnext(*partexprs_item); - } + /* Get partition key expression for this column */ + if (key->partattrs[keynum] != 0) + { + *keyCol = (Expr *) makeVar(1, + key->partattrs[keynum], + key->parttypid[keynum], + key->parttypmod[keynum], + key->parttypcoll[keynum], + 0); + } + else + { + if (*partexprs_item == NULL) + elog(ERROR, "wrong number of partition key expressions"); + *keyCol = copyObject(lfirst(*partexprs_item)); + *partexprs_item = lnext(*partexprs_item); + } - /* Get appropriate Const nodes for the bounds */ - if (ldatum->kind == PARTITION_RANGE_DATUM_VALUE) - *lower_val = castNode(Const, copyObject(ldatum->value)); - else - *lower_val = NULL; + /* Get appropriate Const nodes for the bounds */ + if (ldatum->kind == PARTITION_RANGE_DATUM_VALUE) + *lower_val = castNode(Const, copyObject(ldatum->value)); + else + *lower_val = NULL; - if (udatum->kind == PARTITION_RANGE_DATUM_VALUE) - *upper_val = castNode(Const, copyObject(udatum->value)); - else - *upper_val = NULL; + if (udatum->kind == PARTITION_RANGE_DATUM_VALUE) + *upper_val = castNode(Const, copyObject(udatum->value)); + else + *upper_val = NULL; } - /* - * Copied from src/backend/catalog/partition.c, not exported - * - * get_range_nulltest - * - * A non-default range partition table does not currently allow partition - * keys to be null, so emit an IS NOT NULL expression for each key column. - */ +/* + * Copied from src/backend/catalog/partition.c, not exported + * + * get_range_nulltest + * + * A non-default range partition table does not currently allow partition + * keys to be null, so emit an IS NOT NULL expression for each key column. + */ List * get_range_nulltest(PartitionKey key) { - List *result = NIL; - NullTest *nulltest; - ListCell *partexprs_item; - int i; - - partexprs_item = list_head(key->partexprs); - for (i = 0; i < key->partnatts; i++) - { - Expr *keyCol; - - if (key->partattrs[i] != 0) + List *result = NIL; + NullTest *nulltest; + ListCell *partexprs_item; + int i; + + partexprs_item = list_head(key->partexprs); + for (i = 0; i < key->partnatts; i++) { - keyCol = (Expr *) makeVar(1, - key->partattrs[i], - key->parttypid[i], - key->parttypmod[i], - key->parttypcoll[i], - 0); - } - else - { - if (partexprs_item == NULL) - elog(ERROR, "wrong number of partition key expressions"); - keyCol = copyObject(lfirst(partexprs_item)); - partexprs_item = lnext(partexprs_item); + Expr *keyCol; + + if (key->partattrs[i] != 0) + { + keyCol = (Expr *) makeVar(1, + key->partattrs[i], + key->parttypid[i], + key->parttypmod[i], + key->parttypcoll[i], + 0); + } + else + { + if (partexprs_item == NULL) + elog(ERROR, "wrong number of partition key expressions"); + keyCol = copyObject(lfirst(partexprs_item)); + partexprs_item = lnext(partexprs_item); + } + + nulltest = makeNode(NullTest); + nulltest->arg = keyCol; + nulltest->nulltesttype = IS_NOT_NULL; + nulltest->argisrow = false; + nulltest->location = -1; + result = lappend(result, nulltest); } - nulltest = makeNode(NullTest); - nulltest->arg = keyCol; - nulltest->nulltesttype = IS_NOT_NULL; - nulltest->argisrow = false; - nulltest->location = -1; - result = lappend(result, nulltest); - } - - return result; + return result; } @@ -1565,4 +1508,60 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation, *translated_vars = vars; } + +/* + * Copied from src/backend/optimizer/path/allpaths.c, not exported + * + * set_plain_rel_pathlist + * Build access paths for a plain relation (no subquery, no inheritance) + */ +void +set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) +{ + Relids required_outer; + + /* + * We don't support pushing join clauses into the quals of a seqscan, but + * it could still have required parameterization due to LATERAL refs in + * its tlist. + */ + required_outer = rel->lateral_relids; + + /* Consider sequential scan */ + add_path(rel, create_seqscan_path(root, rel, required_outer, 0)); + + /* If appropriate, consider parallel sequential scan */ + if (rel->consider_parallel && required_outer == NULL) + create_plain_partial_paths(root, rel); + + /* Consider index scans */ + create_index_paths(root, rel); + + /* Consider TID scans */ + create_tidscan_paths(root, rel); +} + +/* + * Copied from src/backend/optimizer/path/allpaths.c, not exported + * + * create_plain_partial_paths + * Build partial access paths for parallel scan of a plain relation + */ +void +create_plain_partial_paths(PlannerInfo *root, RelOptInfo *rel) +{ + int parallel_workers; + + parallel_workers = compute_parallel_worker(rel, rel->pages, -1, + max_parallel_workers_per_gather); + + /* If any limit was set to zero, the user doesn't want a parallel scan. */ + if (parallel_workers <= 0) + return; + + /* Add an unordered partial path based on a parallel sequential scan. */ + add_partial_path(rel, create_seqscan_path(root, rel, NULL, parallel_workers)); +} + + #endif diff --git a/hypopg_table.c b/hypopg_table.c index 53ccc57..9569ce1 100644 --- a/hypopg_table.c +++ b/hypopg_table.c @@ -26,8 +26,9 @@ #include "access/htup_details.h" #include "access/nbtree.h" #include "catalog/namespace.h" +#include "catalog/partition.h" #include "catalog/pg_class.h" -#include "catalog/pg_inherits_fn.h" +#include "catalog/pg_inherits.h" #include "catalog/pg_opclass.h" #include "catalog/pg_type.h" #include "nodes/makefuncs.h" @@ -35,8 +36,11 @@ #include "nodes/nodes.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/pathnode.h" #include "optimizer/predtest.h" #include "optimizer/prep.h" +#include "optimizer/restrictinfo.h" +#include "optimizer/var.h" #include "parser/parsetree.h" #include "parser/parse_utilcmd.h" #include "rewrite/rewriteManip.h" @@ -45,8 +49,10 @@ #include "utils/datum.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" +#include "utils/partcache.h" #include "utils/ruleutils.h" #include "utils/syscache.h" +#include "partitioning/partbounds.h" #endif #include "include/hypopg.h" @@ -91,14 +97,14 @@ static const hypoTable *hypo_table_store_parsetree(CreateStmt *node, const char *queryString, Oid parentid); static PartitionBoundSpec *hypo_transformPartitionBound(ParseState *pstate, hypoTable *parent, PartitionBoundSpec *spec); -static void hypo_partition_table(PlannerInfo *root, RelOptInfo *rel, +static void hypo_partition_table(PlannerInfo *root, RelOptInfo *rel, hypoTable *entry); -static List *hypo_get_partition_constraints(PlannerInfo *root, RelOptInfo *rel, +static List *hypo_get_partition_constraints(PlannerInfo *root, RelOptInfo *rel, hypoTable *parent); static List *hypo_get_qual_from_partbound(hypoTable *parent, PartitionBoundSpec *spec); static List *hypo_get_qual_for_hash(hypoTable *parent, PartitionBoundSpec *spec); static List *hypo_get_qual_for_list(hypoTable *parent, PartitionBoundSpec *spec); -static List *hypo_get_qual_for_range(hypoTable *parent, PartitionBoundSpec *spec, +static List *hypo_get_qual_for_range(hypoTable *parent, PartitionBoundSpec *spec, bool for_default); #define HYPO_RTI_IS_TAGGED(rti, root) (planner_rt_fetch(rti, root)->security_barrier) @@ -829,7 +835,7 @@ hypo_generate_partkey(CreateStmt *stmt, Oid parentid, hypoTable *entry) key->parttypid[i] = exprType(lfirst(partexprs_item)); key->parttypmod[i] = exprTypmod(lfirst(partexprs_item)); key->parttypcoll[i] = exprCollation(lfirst(partexprs_item)); - + partexprs_item = lnext(partexprs_item); } get_typlenbyvalalign(key->parttypid[i], @@ -909,7 +915,7 @@ hypo_generate_part_scheme(CreateStmt *stmt, PartitionKey partkey, Oid //part_scheme->parttypcoll = (Oid *) palloc(sizeof(Oid) * partnatts); //memcpy(part_scheme->parttypcoll, partkey->parttypcoll, // sizeof(Oid) * partnatts); - + part_scheme->parttyplen = (int16 *) palloc(sizeof(int16) * partnatts); memcpy(part_scheme->parttyplen, partkey->parttyplen, sizeof(int16) * partnatts); @@ -1732,226 +1738,279 @@ hypo_transformPartitionBound(ParseState *pstate, hypoTable *parent, * the hypothetical partitioning to this rel */ void -hypo_injectHypotheticalPartitioning(PlannerInfo *root, - Oid relationObjectId, +hypo_injectHypotheticalPartitioning(PlannerInfo *root, + Oid relationObjectId, RelOptInfo *rel) { - hypoTable *parent; + hypoTable *parent; - Assert(HYPO_ENABLED()); - Assert(hypo_table_oid_is_hypothetical(relationObjectId)); - - parent = hypo_find_table(relationObjectId); + Assert(HYPO_ENABLED()); + Assert(hypo_table_oid_is_hypothetical(relationObjectId)); - /* - * if this rel is parent, prepare some structures to inject - * hypothetical partitioning - */ - if(!HYPO_RTI_IS_TAGGED(rel->relid,root)) - { - List *inhoids; - int nparts, i; - int oldsize = root->simple_rel_array_size; - RangeTblEntry *rte; - List *partitioned_child_rels = NIL; - ListCell *cell; - AppendRelInfo *appinfo; - PartitionedChildRelInfo *pcinfo; - - /* get all of the partition oids */ - inhoids = hypo_find_inheritance_children(parent); - nparts = list_length(inhoids); - - /* 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; isimple_rel_array_size; i++) + parent = hypo_find_table(relationObjectId); + + /* + * if this rel is parent, prepare some structures to inject + * hypothetical partitioning + */ + if(!HYPO_RTI_IS_TAGGED(rel->relid,root)) { - 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; - - partitioned_child_rels = lappend_int(partitioned_child_rels, - oldsize); - root->parse->rtable = lappend(root->parse->rtable, - root->simple_rte_array[oldsize]); - - HYPO_TAG_RTI(rel->relid, root); - HYPO_TAG_RTI(oldsize, root); - + List *inhoids; + int nparts, i; + int oldsize = root->simple_rel_array_size; + RangeTblEntry *rte; + List *partitioned_child_rels = NIL; + ListCell *cell; + AppendRelInfo *appinfo; + PartitionedChildRelInfo *pcinfo; - /* - * 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, + /* get all of the partition oids */ + inhoids = hypo_find_inheritance_children(parent); + nparts = list_length(inhoids); + + /* 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; isimple_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; + + partitioned_child_rels = lappend_int(partitioned_child_rels, + oldsize); + 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++; - } - - /* create pcinfo hypothetically for this rel */ - pcinfo = makeNode(PartitionedChildRelInfo); - pcinfo->parent_relid = oldsize; - pcinfo->child_rels = partitioned_child_rels; - root->pcinfo_list = lappend(root->pcinfo_list, pcinfo); - - /* add partition info to this rel */ - hypo_partition_table(root, rel, parent); - } - - - /* - * if this rel is partition, rewrite tuples and pages using selectivity - * which is computed according to its partition constraints - */ - if (rel->reloptkind != RELOPT_BASEREL - &&HYPO_RTI_IS_TAGGED(rel->relid,root)) - { - List *constraints; - PlannerInfo *root_dummy; - Selectivity selectivity; - double pages; - /* get its partition constraints */ - constraints = hypo_get_partition_constraints(root, rel, parent); - - /* - * to compute selectivity, make dummy PlannerInfo and then rewrite - * tuples and pages using this selectivity - */ - root_dummy = makeNode(PlannerInfo); - root_dummy = root; - root_dummy->simple_rel_array[rel->relid] = rel; - - selectivity = clauselist_selectivity(root_dummy, - constraints, - 0, - JOIN_INNER, - NULL); - - pages = ceil(rel->pages * selectivity); - rel->pages = (BlockNumber)pages; - rel->tuples = clamp_row_est(rel->tuples * selectivity); - } + i++; + } + + /* create pcinfo hypothetically for this rel */ + pcinfo = makeNode(PartitionedChildRelInfo); + pcinfo->parent_relid = oldsize; + pcinfo->child_rels = partitioned_child_rels; + root->pcinfo_list = lappend(root->pcinfo_list, pcinfo); + + /* add partition info to this rel */ + hypo_partition_table(root, rel, parent); + } + + /* + * If this rel is partition, we add the partition constraints to the + * rte->securityQuals so that the relation which is need not be scanned + * is marked as Dummy at the set_append_rel_size() and the rel->rows is + * computed correctly at the set_baserel_size_estimates(). We shouldn't + * rewrite the rel->pages and the rel->tuples here, because they will be + * rewritten at the later hook. + * + * TODO: should comfirm that the tuples will not referred till the + * set_baserel_size_esimates() and think about rel->reltarget->width + * + */ + if (rel->reloptkind != RELOPT_BASEREL + &&HYPO_RTI_IS_TAGGED(rel->relid,root)) + { + List *constraints; + + /* get its partition constraints */ + constraints = hypo_get_partition_constraints(root, rel, parent); + + /* + * to compute rel->rows at set_baserel_size_estimates using parent's + * statistics, parent's tuples and baserestrictinfo, we add the partition + * constraints to its rte->securityQuals + */ + planner_rt_fetch(rel->relid, root)->securityQuals = list_make1(constraints); + } } /* - * If this rel is need not be scanned, we have to mark it as dummy to omit it - * from the appendrel - * - * It is inspired on relation_excluded_by_constraints + * If this rel is partition, we remove the partition constraints from the + * its rel->baserestrictinfo and rewrite some items of its RelOptInfo: + * the rel->pages, the rel->tuples rel->baserestrictcost. After that + * we call the set_plain_rel_pathlist() to re-create its pathlist using + * the new RelOptInfo. + * */ -void hypo_markDummyIfExcluded(PlannerInfo *root, RelOptInfo *rel, - Index rti, RangeTblEntry *rte) +void hypo_setPartitionPathlist(PlannerInfo *root, RelOptInfo *rel, + Index rti, RangeTblEntry *rte) { - hypoTable *parent; - List *constraints; - List *safe_constraints = NIL; - ListCell *lc; + ListCell *l; + Index parentRTindex; + RelOptInfo *parentrel; + hypoTable *parent = hypo_find_table(rte->relid); + List *constraints = hypo_get_partition_constraints(root, rel, parent); + PlannerInfo *root_dummy; + Selectivity selectivity; + double pages; - Assert(HYPO_ENABLED()); - Assert(hypo_table_oid_is_hypothetical(rte->relid)); - Assert(rte->relkind == 'r'); + /* + * get the parent's rel and copy its rel->baserestrictinfo to + * the own rel->baserestrictinfo. + * this part is inspired on set_append_rel_size(). + */ + foreach(l, root->append_rel_list) + { + AppendRelInfo *appinfo = (AppendRelInfo *)lfirst(l); + List *childquals = NIL; + Index cq_min_security = UINT_MAX; + ListCell *lc; - parent = hypo_find_table(rte->relid); - - /* get its partition constraints */ - constraints = hypo_get_partition_constraints(root, rel, parent); - - /* - * We do not currently enforce that CHECK constraints contain only - * immutable functions, so it's necessary to check here. We daren't draw - * conclusions from plan-time evaluation of non-immutable functions. Since - * they're ANDed, we can just ignore any mutable constraints in the list, - * and reason about the rest. - */ - foreach(lc, constraints) - { - Node *pred = (Node *) lfirst(lc); - - if (!contain_mutable_functions(pred)) - safe_constraints = lappend(safe_constraints, pred); - } - - /* if this partition need not be scanned, we call the set_dummy_rel_pathlist() - * to mark it as dummy */ - if (predicate_refuted_by(safe_constraints, rel->baserestrictinfo, false)) - set_dummy_rel_pathlist(rel); - + if(appinfo->child_relid == rti) + { + parentRTindex = appinfo->parent_relid; + parentrel = root->simple_rel_array[parentRTindex]; - /* - TODO: re-estimate parent size just like set_append_rel_size() - */ - - + foreach(lc, parentrel->baserestrictinfo) + { + RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); + Node *childqual; + ListCell *lc2; + + Assert(IsA(rinfo, RestrictInfo)); + childqual = adjust_appendrel_attrs(root, + (Node *) rinfo->clause, + 1, &appinfo); + childqual = eval_const_expressions(root, childqual); + + /* might have gotten an AND clause, if so flatten it */ + foreach(lc2, make_ands_implicit((Expr *) childqual)) + { + Node *onecq = (Node *) lfirst(lc2); + bool pseudoconstant; + + /* check for pseudoconstant (no Vars or volatile functions) */ + pseudoconstant = + !contain_vars_of_level(onecq, 0) && + !contain_volatile_functions(onecq); + if (pseudoconstant) + { + /* tell createplan.c to check for gating quals */ + root->hasPseudoConstantQuals = true; + } + /* reconstitute RestrictInfo with appropriate properties */ + childquals = lappend(childquals, + make_restrictinfo((Expr *) onecq, + rinfo->is_pushed_down, + rinfo->outerjoin_delayed, + pseudoconstant, + rinfo->security_level, + NULL, NULL, NULL)); + /* track minimum security level among child quals */ + cq_min_security = Min(cq_min_security, rinfo->security_level); + } + } + rel->baserestrictinfo = childquals; + rel->baserestrict_min_security = cq_min_security; + break; + } + } + + /* + * make dummy PlannerInfo to compute the selectivity, and then rewrite + * tuples and pages using this selectivity + */ + root_dummy = makeNode(PlannerInfo); + root_dummy = root; + root_dummy->simple_rel_array[rti] = rel; + + selectivity = clauselist_selectivity(root_dummy, + constraints, + 0, + JOIN_INNER, + NULL); + + pages = ceil(rel->pages * selectivity); + rel->pages = (BlockNumber)pages; + rel->tuples = clamp_row_est(rel->tuples * selectivity); + + /* recompute the rel->baserestrictcost*/ + cost_qual_eval(&rel->baserestrictcost, rel->baserestrictinfo, root); + + /* + * call the set_plain_rel_pathlist() to re-create its pathlist using + * the new RelOptInfo + */ + set_plain_rel_pathlist(root, rel, rte); } + /* * If this is the table we want to hypothetically partition, modifies its * metadata to add partitioning information */ static void hypo_partition_table(PlannerInfo *root, RelOptInfo *rel, hypoTable *entry) -{ +{ PartitionDesc partdesc; PartitionKey partkey; - + partdesc = hypo_generate_partitiondesc(entry); partkey = entry->partkey; rel->part_scheme = entry->part_scheme; @@ -1968,8 +2027,8 @@ hypo_partition_table(PlannerInfo *root, RelOptInfo *rel, hypoTable *entry) */ static List * hypo_get_partition_constraints(PlannerInfo *root, RelOptInfo *rel, hypoTable *parent) -{ - ListCell *lc; +{ + ListCell *lc; Oid childOid; hypoTable *child; PartitionBoundSpec *spec; @@ -1980,10 +2039,10 @@ hypo_get_partition_constraints(PlannerInfo *root, RelOptInfo *rel, hypoTable *pa childOid = lfirst_oid(lc); child = hypo_find_table(childOid); spec = child->boundspec; - + /* get its partition constraints */ constraints = hypo_get_qual_from_partbound(parent,spec); - + if (constraints) { /* @@ -1992,20 +2051,20 @@ hypo_get_partition_constraints(PlannerInfo *root, RelOptInfo *rel, hypoTable *pa */ constraints = (List *) eval_const_expressions(root, (Node *) constraints); /* FIXME this func will be modified at pg11 */ - constraints = (List *) canonicalize_qual((Expr *) constraints); - + constraints = (List *) canonicalize_qual((Expr *) constraints, true); + /* Fix Vars to have the desired varno */ if (rel->relid != 1) ChangeVarNodes((Node *) constraints, 1, rel->relid, 0); - } + } return constraints; } /* - * Return the list of executable expressions as partition constraint + * Return the list of executable expressions as partition constraint * * Heavily inspired on get_qual_from_partbound */ @@ -2016,15 +2075,15 @@ hypo_get_qual_from_partbound(hypoTable *parent, PartitionBoundSpec *spec) List *my_qual = NIL; Assert(key != NULL); - + switch (key->strategy) { - + case PARTITION_STRATEGY_HASH: Assert(spec->strategy == PARTITION_STRATEGY_HASH); my_qual = hypo_get_qual_for_hash(parent, spec); break; - + case PARTITION_STRATEGY_LIST: Assert(spec->strategy == PARTITION_STRATEGY_LIST); my_qual = hypo_get_qual_for_list(parent, spec); @@ -2039,7 +2098,7 @@ hypo_get_qual_from_partbound(hypoTable *parent, PartitionBoundSpec *spec) elog(ERROR, "unexpected partition strategy: %d", (int) key->strategy); } - + return my_qual; } @@ -2329,9 +2388,9 @@ hypo_get_qual_for_range(hypoTable *parent, PartitionBoundSpec *spec, bool for_de *upper_or_start_datum; bool need_next_lower_arm, need_next_upper_arm; - + if (spec->is_default) - { + { List *or_expr_args = NIL; PartitionDesc pdesc = hypo_generate_partitiondesc(parent); Oid *inhoids = pdesc->oids; @@ -2340,7 +2399,7 @@ hypo_get_qual_for_range(hypoTable *parent, PartitionBoundSpec *spec, bool for_de for (i = 0; i < nparts; i++) { - Oid inhrelid = inhoids[i]; //is this a parent oid or dummy child oid? + Oid inhrelid = inhoids[i]; //is this a parent oid or dummy child oid? HeapTuple tuple; Datum datum; bool isnull; @@ -2409,7 +2468,7 @@ hypo_get_qual_for_range(hypoTable *parent, PartitionBoundSpec *spec, bool for_de return result; } - + lower_or_start_datum = list_head(spec->lowerdatums); upper_or_start_datum = list_head(spec->upperdatums); num_or_arms = key->partnatts; diff --git a/include/hypopg_import.h b/include/hypopg_import.h index d2a5ba4..74d732e 100644 --- a/include/hypopg_import.h +++ b/include/hypopg_import.h @@ -14,6 +14,8 @@ #include "nodes/pg_list.h" #include "optimizer/planner.h" +#include "optimizer/pathnode.h" +#include "partitioning/partbounds.h" #include "utils/rel.h" @@ -36,6 +38,7 @@ extern char *get_am_name(Oid amOid); extern void get_opclass_name(Oid opclass, Oid actual_datatype, StringInfo buf); #if PG_VERSION_NUM >= 100000 +#if PG_VERSION_NUM < 110000 /* * Imported from src/backend/catalog/partition.c, not exported */ @@ -77,6 +80,7 @@ typedef struct PartitionRangeBound PartitionRangeDatumKind *kind; /* the kind of each datum */ bool lower; /* this is the lower (vs upper) bound */ } PartitionRangeBound; +#endif /* Context info needed for invoking a recursive querytree display routine */ typedef struct @@ -108,9 +112,6 @@ int32 qsort_partition_list_value_cmp(const void *a, const void *b, void *arg); PartitionRangeBound *make_one_range_bound(PartitionKey key, int index, List *datums, bool lower); int32 qsort_partition_rbound_cmp(const void *a, const void *b, void *arg); -int32 partition_rbound_cmp(PartitionKey key, - Datum *datums1, PartitionRangeDatumKind *kind1, - bool lower1, PartitionRangeBound *b2); void get_const_expr(Const *constval, deparse_context *context, int showtype); void get_const_collation(Const *constval, deparse_context *context); @@ -132,7 +133,8 @@ List *get_range_nulltest(PartitionKey key); void make_inh_translation_list(Relation oldrelation, Relation newrelation, Index newvarno, List **translated_vars); - +void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte); +void create_plain_partial_paths(PlannerInfo *root, RelOptInfo *rel); /* Copied from src/backend/catalog/partition.c, not exported */ #define partition_bound_accepts_nulls(bi) ((bi)->null_index != -1) diff --git a/include/hypopg_table.h b/include/hypopg_table.h index bc3a30c..6da8f93 100644 --- a/include/hypopg_table.h +++ b/include/hypopg_table.h @@ -73,5 +73,7 @@ void hypo_injectHypotheticalPartitioning(PlannerInfo *root, RelOptInfo *rel); void hypo_markDummyIfExcluded(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte); +void hypo_setPartitionPathlist(PlannerInfo *root, RelOptInfo *rel, + Index rti, RangeTblEntry *rte); #endif