diff --git a/aqo_pg13.patch b/aqo_pg13.patch index 406e3e0e..d7ecb41c 100644 --- a/aqo_pg13.patch +++ b/aqo_pg13.patch @@ -1,5 +1,5 @@ diff --git a/contrib/Makefile b/contrib/Makefile -index 1846d415b6..95519ac11d 100644 +index 1846d415b6f..95519ac11de 100644 --- a/contrib/Makefile +++ b/contrib/Makefile @@ -7,6 +7,7 @@ include $(top_builddir)/src/Makefile.global @@ -11,7 +11,7 @@ index 1846d415b6..95519ac11d 100644 auto_explain \ bloom \ diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c -index bc05c96b4c..b6a3abe0d2 100644 +index bc05c96b4ce..b6a3abe0d2b 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -24,6 +24,7 @@ @@ -57,7 +57,7 @@ index bc05c96b4c..b6a3abe0d2 100644 if (es->format == EXPLAIN_FORMAT_TEXT) appendStringInfoChar(es->str, '\n'); diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c -index 692b6c1559..580d04d784 100644 +index 692b6c1559f..580d04d7844 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -132,6 +132,7 @@ CopyPlanFields(const Plan *from, Plan *newnode) @@ -69,35 +69,31 @@ index 692b6c1559..580d04d784 100644 /* diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c -index 21ececf0c2..a0e7a7ebca 100644 +index 21ececf0c2f..ebfd3ba86de 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -342,6 +342,7 @@ _outPlanInfo(StringInfo str, const Plan *node) WRITE_NODE_FIELD(initPlan); WRITE_BITMAPSET_FIELD(extParam); WRITE_BITMAPSET_FIELD(allParam); -+ /*WRITE_NODE_FIELD(ext_nodes); */ ++ WRITE_NODE_FIELD(ext_nodes); } /* diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c -index 7976b369ba..604314e0b3 100644 +index 7976b369ba8..2e47bd8d950 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c -@@ -1580,6 +1580,11 @@ ReadCommonPlan(Plan *local_node) +@@ -1580,6 +1580,7 @@ ReadCommonPlan(Plan *local_node) READ_NODE_FIELD(initPlan); READ_BITMAPSET_FIELD(extParam); READ_BITMAPSET_FIELD(allParam); -+ local_node->ext_nodes = NIL; -+ /* READ_NODE_FIELD(ext_nodes); -+ * Don't serialize this field. It is required to serialize RestrictInfo and -+ * EqualenceClass. -+ */ ++ READ_NODE_FIELD(ext_nodes); } /* diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c -index 4edc859cb5..988f2e6ab7 100644 +index 4edc859cb57..988f2e6ab75 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -98,6 +98,12 @@ @@ -363,7 +359,7 @@ index 4edc859cb5..988f2e6ab7 100644 { double parallel_divisor = path->parallel_workers; diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c -index 917713c163..5b7bf1cec6 100644 +index 917713c1633..5b7bf1cec69 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -70,6 +70,7 @@ @@ -394,7 +390,7 @@ index 917713c163..5b7bf1cec6 100644 /* diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c -index 27c665ac12..f599fba755 100644 +index 821693c60ee..fa627f472f9 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -145,7 +145,8 @@ static List *extract_rollup_sets(List *groupingSets); @@ -407,7 +403,7 @@ index 27c665ac12..f599fba755 100644 grouping_sets_data *gd, List *target_list); static RelOptInfo *create_grouping_paths(PlannerInfo *root, -@@ -3686,7 +3687,8 @@ standard_qp_callback(PlannerInfo *root, void *extra) +@@ -3682,7 +3683,8 @@ standard_qp_callback(PlannerInfo *root, void *extra) */ static double get_number_of_groups(PlannerInfo *root, @@ -417,7 +413,7 @@ index 27c665ac12..f599fba755 100644 grouping_sets_data *gd, List *target_list) { -@@ -3723,7 +3725,7 @@ get_number_of_groups(PlannerInfo *root, +@@ -3719,7 +3721,7 @@ get_number_of_groups(PlannerInfo *root, GroupingSetData *gs = lfirst_node(GroupingSetData, lc2); double numGroups = estimate_num_groups(root, groupExprs, @@ -426,7 +422,7 @@ index 27c665ac12..f599fba755 100644 &gset); gs->numGroups = numGroups; -@@ -3748,7 +3750,7 @@ get_number_of_groups(PlannerInfo *root, +@@ -3744,7 +3746,7 @@ get_number_of_groups(PlannerInfo *root, GroupingSetData *gs = lfirst_node(GroupingSetData, lc2); double numGroups = estimate_num_groups(root, groupExprs, @@ -435,7 +431,7 @@ index 27c665ac12..f599fba755 100644 &gset); gs->numGroups = numGroups; -@@ -3764,8 +3766,8 @@ get_number_of_groups(PlannerInfo *root, +@@ -3760,8 +3762,8 @@ get_number_of_groups(PlannerInfo *root, groupExprs = get_sortgrouplist_exprs(parse->groupClause, target_list); @@ -446,7 +442,7 @@ index 27c665ac12..f599fba755 100644 } } else if (parse->groupingSets) -@@ -4151,7 +4153,8 @@ create_ordinary_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, +@@ -4147,7 +4149,8 @@ create_ordinary_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, * Estimate number of groups. */ dNumGroups = get_number_of_groups(root, @@ -456,7 +452,7 @@ index 27c665ac12..f599fba755 100644 gd, extra->targetList); -@@ -6935,13 +6938,15 @@ create_partial_grouping_paths(PlannerInfo *root, +@@ -6931,13 +6934,15 @@ create_partial_grouping_paths(PlannerInfo *root, if (cheapest_total_path != NULL) dNumPartialGroups = get_number_of_groups(root, @@ -475,7 +471,7 @@ index 27c665ac12..f599fba755 100644 extra->targetList); diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c -index a203e6f1ff..d31bf5bae6 100644 +index a203e6f1ff5..d31bf5bae63 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -258,6 +258,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) @@ -541,7 +537,7 @@ index a203e6f1ff..d31bf5bae6 100644 return ppi; diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c -index 37458da096..248a1875a1 100644 +index 37458da096d..248a1875a18 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -147,6 +147,7 @@ @@ -573,7 +569,7 @@ index 37458da096..248a1875a1 100644 * estimate_num_groups - Estimate number of groups in a grouped query * diff --git a/src/include/commands/explain.h b/src/include/commands/explain.h -index ba661d32a6..09d0abe58b 100644 +index ba661d32a63..09d0abe58be 100644 --- a/src/include/commands/explain.h +++ b/src/include/commands/explain.h @@ -75,6 +75,18 @@ extern PGDLLIMPORT ExplainOneQuery_hook_type ExplainOneQuery_hook; @@ -596,7 +592,7 @@ index ba661d32a6..09d0abe58b 100644 extern void ExplainQuery(ParseState *pstate, ExplainStmt *stmt, ParamListInfo params, DestReceiver *dest); diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h -index 5ebf070979..5b2acd7de2 100644 +index d2b4271de9d..559b9db7121 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -739,6 +739,10 @@ typedef struct RelOptInfo @@ -635,7 +631,7 @@ index 5ebf070979..5b2acd7de2 100644 diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h -index 90f02ce6fd..88c332164d 100644 +index 90f02ce6fdd..88c332164dd 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -159,6 +159,9 @@ typedef struct Plan @@ -649,7 +645,7 @@ index 90f02ce6fd..88c332164d 100644 /* ---------------- diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h -index 6141654e47..e6b28cbb05 100644 +index 6141654e478..e6b28cbb05f 100644 --- a/src/include/optimizer/cost.h +++ b/src/include/optimizer/cost.h @@ -39,6 +39,37 @@ typedef enum @@ -733,7 +729,7 @@ index 6141654e47..e6b28cbb05 100644 #endif /* COST_H */ diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h -index 3bd7072ae8..21bbaba11c 100644 +index 3bd7072ae8c..21bbaba11c8 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -18,6 +18,10 @@ @@ -748,7 +744,7 @@ index 3bd7072ae8..21bbaba11c 100644 * prototypes for pathnode.c */ diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h -index 8ce60e202e..75415102c2 100644 +index 8ce60e202e5..75415102c2e 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -24,6 +24,12 @@ extern double cursor_tuple_fraction; @@ -765,7 +761,7 @@ index 8ce60e202e..75415102c2 100644 * prototypes for plan/planmain.c */ diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h -index 7ac4a06391..def3522881 100644 +index 7ac4a063915..74fe91b89f1 100644 --- a/src/include/utils/selfuncs.h +++ b/src/include/utils/selfuncs.h @@ -127,6 +127,12 @@ typedef bool (*get_index_stats_hook_type) (PlannerInfo *root, @@ -781,13 +777,13 @@ index 7ac4a06391..def3522881 100644 /* Functions in selfuncs.c */ -@@ -195,6 +201,9 @@ extern void mergejoinscansel(PlannerInfo *root, Node *clause, +@@ -193,6 +199,9 @@ extern void mergejoinscansel(PlannerInfo *root, Node *clause, + Selectivity *leftstart, Selectivity *leftend, + Selectivity *rightstart, Selectivity *rightend); - extern double estimate_num_groups(PlannerInfo *root, List *groupExprs, - double input_rows, List **pgset); +extern double estimate_num_groups_ext(PlannerInfo *root, List *groupExprs, + Path *subpath, RelOptInfo *grouped_rel, + List **pgset); + extern double estimate_num_groups(PlannerInfo *root, List *groupExprs, + double input_rows, List **pgset); - extern void estimate_hash_bucket_stats(PlannerInfo *root, - Node *hashkey, double nbuckets, diff --git a/cardinality_hooks.c b/cardinality_hooks.c index da25e02c..0c23d391 100644 --- a/cardinality_hooks.c +++ b/cardinality_hooks.c @@ -186,8 +186,7 @@ aqo_get_parameterized_baserel_size(PlannerInfo *root, forboth(l, allclauses, l2, selectivities) { - current_hash = get_clause_hash( - ((RestrictInfo *) lfirst(l))->clause, + current_hash = get_clause_hash(((AQOClause *) lfirst(l))->clause, nargs, args_hash, eclass_hash); cache_selectivity(current_hash, rel->relid, rte->relid, *((double *) lfirst(l2))); diff --git a/hash.c b/hash.c index 55ce8b6a..397fe11c 100644 --- a/hash.c +++ b/hash.c @@ -27,6 +27,7 @@ #include "aqo.h" #include "hash.h" +#include "path_utils.h" static int get_str_hash(const char *str); static int get_node_hash(Node *node); @@ -236,11 +237,11 @@ get_fss_for_object(List *relsigns, List *clauselist, i = 0; foreach(lc, clauselist) { - RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc); + AQOClause *clause = (AQOClause *) lfirst(lc); - clause_hashes[i] = get_clause_hash(rinfo->clause, + clause_hashes[i] = get_clause_hash(clause->clause, nargs, args_hash, eclass_hash); - args = get_clause_args_ptr(rinfo->clause); + args = get_clause_args_ptr(clause->clause); clause_has_consts[i] = (args != NULL && has_consts(*args)); i++; } @@ -335,14 +336,14 @@ get_clause_hash(Expr *clause, int nargs, int *args_hash, int *eclass_hash) cclause = copyObject(clause); args = get_clause_args_ptr(cclause); + /* XXX: Why does it work even if this loop is removed? */ foreach(l, *args) { arg_eclass = get_arg_eclass(get_node_hash(lfirst(l)), nargs, args_hash, eclass_hash); if (arg_eclass != 0) { - lfirst(l) = makeNode(Param); - ((Param *) lfirst(l))->paramid = arg_eclass; + lfirst(l) = create_aqo_const_node(AQO_NODE_EXPR, arg_eclass); } } if (!clause_is_eq_clause(clause) || has_consts(*args)) @@ -572,7 +573,7 @@ get_arg_eclass(int arg_hash, int nargs, int *args_hash, int *eclass_hash) static void get_clauselist_args(List *clauselist, int *nargs, int **args_hash) { - RestrictInfo *rinfo; + AQOClause *clause; List **args; ListCell *l; ListCell *l2; @@ -582,9 +583,9 @@ get_clauselist_args(List *clauselist, int *nargs, int **args_hash) foreach(l, clauselist) { - rinfo = (RestrictInfo *) lfirst(l); - args = get_clause_args_ptr(rinfo->clause); - if (args != NULL && clause_is_eq_clause(rinfo->clause)) + clause = (AQOClause *) lfirst(l); + args = get_clause_args_ptr(clause->clause); + if (args != NULL && clause_is_eq_clause(clause->clause)) foreach(l2, *args) if (!IsA(lfirst(l2), Const)) cnt++; @@ -593,9 +594,9 @@ get_clauselist_args(List *clauselist, int *nargs, int **args_hash) *args_hash = palloc(cnt * sizeof(**args_hash)); foreach(l, clauselist) { - rinfo = (RestrictInfo *) lfirst(l); - args = get_clause_args_ptr(rinfo->clause); - if (args != NULL && clause_is_eq_clause(rinfo->clause)) + clause = (AQOClause *) lfirst(l); + args = get_clause_args_ptr(clause->clause); + if (args != NULL && clause_is_eq_clause(clause->clause)) foreach(l2, *args) if (!IsA(lfirst(l2), Const)) (*args_hash)[i++] = get_node_hash(lfirst(l2)); @@ -650,7 +651,7 @@ disjoint_set_merge_eclasses(int *p, int v1, int v2) static int * perform_eclasses_join(List *clauselist, int nargs, int *args_hash) { - RestrictInfo *rinfo; + AQOClause *clause; int *p; ListCell *l, *l2; @@ -664,9 +665,9 @@ perform_eclasses_join(List *clauselist, int nargs, int *args_hash) foreach(l, clauselist) { - rinfo = (RestrictInfo *) lfirst(l); - args = get_clause_args_ptr(rinfo->clause); - if (args != NULL && clause_is_eq_clause(rinfo->clause)) + clause = (AQOClause *) lfirst(l); + args = get_clause_args_ptr(clause->clause); + if (args != NULL && clause_is_eq_clause(clause->clause)) { i3 = -1; foreach(l2, *args) diff --git a/path_utils.c b/path_utils.c index 67f5919b..3f8f2895 100644 --- a/path_utils.c +++ b/path_utils.c @@ -22,6 +22,7 @@ #include "storage/lmgr.h" #include "utils/syscache.h" #include "utils/lsyscache.h" +#include "common/shortest_dec.h" #include "aqo.h" #include "hash.h" @@ -39,7 +40,8 @@ static AQOPlanNode DefaultAQOPlanNode = .node.type = T_ExtensibleNode, .node.extnodename = AQO_PLAN_NODE, .had_path = false, - .rels = NULL, + .rels.hrels = NIL, + .rels.signatures = NIL, .clauses = NIL, .selectivities = NIL, .grouping_exprs = NIL, @@ -47,18 +49,39 @@ static AQOPlanNode DefaultAQOPlanNode = .parallel_divisor = -1., .was_parametrized = false, .fss = INT_MAX, - .prediction = -1 + .prediction = -1. }; /* * Hook on creation of a plan node. We need to store AQO-specific data to * support learning stage. */ -static create_plan_hook_type aqo_create_plan_next = NULL; +static create_plan_hook_type aqo_create_plan_next = NULL; -/*static create_upper_paths_hook_type aqo_create_upper_paths_next = NULL;*/ +/*static create_upper_paths_hook_type aqo_create_upper_paths_next = NULL;*/ +/* Return a copy of the given list of AQOClause structs */ +static List * +copy_aqo_clauses(List *src) +{ + List *result = NIL; + ListCell *lc; + + foreach(lc, src) + { + AQOClause *old = (AQOClause *) lfirst(lc); + AQOClause *new = palloc(sizeof(AQOClause)); + + memcpy(new, old, sizeof(AQOClause)); + new->clause = copyObject(old->clause); + + result = lappend(result, (void *) new); + } + + return result; +} + static AQOPlanNode * create_aqo_plan_node() { @@ -66,12 +89,20 @@ create_aqo_plan_node() T_ExtensibleNode); Assert(node != NULL); memcpy(node, &DefaultAQOPlanNode, sizeof(AQOPlanNode)); - node->rels = palloc(sizeof(RelSortOut)); - node->rels->hrels = NIL; - node->rels->signatures = NIL; return node; } +AQOConstNode * +create_aqo_const_node(AQOConstType type, int fss) +{ + AQOConstNode *node = (AQOConstNode *) newNode(sizeof(AQOConstNode), + T_ExtensibleNode); + Assert(node != NULL); + node->node.extnodename = AQO_CONST_NODE; + node->type = type; + node->fss = fss; + return node; +} /* Ensure that it's postgres_fdw's foreign server oid */ static bool @@ -276,13 +307,8 @@ subplan_hunter(Node *node, void *context) if (IsA(node, SubPlan)) { - A_Const *fss = makeNode(A_Const); - - fss->val.type = T_Integer; - fss->location = -1; - fss->val.val.ival = 0; - return (Node *) fss; - + /* TODO: use fss of SubPlan here */ + return (Node *) create_aqo_const_node(AQO_NODE_SUBPLAN, 0); } return expression_tree_mutator(node, subplan_hunter, context); } @@ -292,8 +318,8 @@ subplan_hunter(Node *node, void *context) * During this operation clauses could be changed and we couldn't walk across * this list next. */ -List * -aqo_get_clauses(PlannerInfo *root, List *restrictlist) +static List * +aqo_get_raw_clauses(PlannerInfo *root, List *restrictlist) { List *clauses = NIL; ListCell *lc; @@ -311,14 +337,49 @@ aqo_get_clauses(PlannerInfo *root, List *restrictlist) return clauses; } +static List * +copy_aqo_clauses_from_rinfo(List *src) +{ + List *result = NIL; + ListCell *lc; + + foreach(lc, src) + { + RestrictInfo *old = (RestrictInfo *) lfirst(lc); + AQOClause *new = palloc(sizeof(AQOClause)); + + new->clause = copyObject(old->clause); + new->norm_selec = old->norm_selec; + new->outer_selec = old->outer_selec; + + result = lappend(result, (void *) new); + } + + return result; +} + /* - * For given path returns the list of all clauses used in it. - * Also returns selectivities for the clauses throw the selectivities variable. - * Both clauses and selectivities returned lists are copies and therefore - * may be modified without corruption of the input data. + * Return copy of clauses returned from the aqo_get_raw_clause() routine + * and convert it into AQOClause struct. */ List * -get_path_clauses(Path *path, PlannerInfo *root, List **selectivities) +aqo_get_clauses(PlannerInfo *root, List *restrictlist) +{ + List *clauses = aqo_get_raw_clauses(root, restrictlist); + List *result = copy_aqo_clauses_from_rinfo(clauses); + + list_free_deep(clauses); + return result; +} + +/* + * Returns a list of all used clauses for the given path. + * Also returns selectivities for the clauses to 'selectivities' variable. + * The returned list of the selectivities is a copy and therefore + * may be modified without corruption of the input data. + */ +static List * +get_path_clauses_recurse(Path *path, PlannerInfo *root, List **selectivities) { List *inner; List *inner_sel = NIL; @@ -338,89 +399,89 @@ get_path_clauses(Path *path, PlannerInfo *root, List **selectivities) case T_NestPath: case T_MergePath: case T_HashPath: - cur = ((JoinPath *) path)->joinrestrictinfo; + cur = list_concat(cur, ((JoinPath *) path)->joinrestrictinfo); /* Not quite correct to avoid sjinfo, but we believe in caching */ cur_sel = get_selectivities(root, cur, 0, ((JoinPath *) path)->jointype, NULL); - outer = get_path_clauses(((JoinPath *) path)->outerjoinpath, root, + outer = get_path_clauses_recurse(((JoinPath *) path)->outerjoinpath, root, &outer_sel); - inner = get_path_clauses(((JoinPath *) path)->innerjoinpath, root, + inner = get_path_clauses_recurse(((JoinPath *) path)->innerjoinpath, root, &inner_sel); *selectivities = list_concat(cur_sel, list_concat(outer_sel, inner_sel)); - return list_concat(list_copy(cur), list_concat(outer, inner)); + return list_concat(cur, list_concat(outer, inner)); break; case T_UniquePath: - return get_path_clauses(((UniquePath *) path)->subpath, root, + return get_path_clauses_recurse(((UniquePath *) path)->subpath, root, selectivities); break; case T_GatherPath: case T_GatherMergePath: - return get_path_clauses(((GatherPath *) path)->subpath, root, + return get_path_clauses_recurse(((GatherPath *) path)->subpath, root, selectivities); break; case T_MaterialPath: - return get_path_clauses(((MaterialPath *) path)->subpath, root, + return get_path_clauses_recurse(((MaterialPath *) path)->subpath, root, selectivities); break; case T_ProjectionPath: - return get_path_clauses(((ProjectionPath *) path)->subpath, root, + return get_path_clauses_recurse(((ProjectionPath *) path)->subpath, root, selectivities); break; case T_ProjectSetPath: - return get_path_clauses(((ProjectSetPath *) path)->subpath, root, + return get_path_clauses_recurse(((ProjectSetPath *) path)->subpath, root, selectivities); break; case T_SortPath: - return get_path_clauses(((SortPath *) path)->subpath, root, + return get_path_clauses_recurse(((SortPath *) path)->subpath, root, selectivities); break; case T_IncrementalSortPath: { IncrementalSortPath *p = (IncrementalSortPath *) path; - return get_path_clauses(p->spath.subpath, root, + return get_path_clauses_recurse(p->spath.subpath, root, selectivities); } break; case T_GroupPath: - return get_path_clauses(((GroupPath *) path)->subpath, root, + return get_path_clauses_recurse(((GroupPath *) path)->subpath, root, selectivities); break; case T_UpperUniquePath: - return get_path_clauses(((UpperUniquePath *) path)->subpath, root, + return get_path_clauses_recurse(((UpperUniquePath *) path)->subpath, root, selectivities); break; case T_AggPath: - return get_path_clauses(((AggPath *) path)->subpath, root, + return get_path_clauses_recurse(((AggPath *) path)->subpath, root, selectivities); break; case T_GroupingSetsPath: - return get_path_clauses(((GroupingSetsPath *) path)->subpath, root, + return get_path_clauses_recurse(((GroupingSetsPath *) path)->subpath, root, selectivities); break; case T_WindowAggPath: - return get_path_clauses(((WindowAggPath *) path)->subpath, root, + return get_path_clauses_recurse(((WindowAggPath *) path)->subpath, root, selectivities); break; case T_SetOpPath: - return get_path_clauses(((SetOpPath *) path)->subpath, root, + return get_path_clauses_recurse(((SetOpPath *) path)->subpath, root, selectivities); break; case T_LockRowsPath: - return get_path_clauses(((LockRowsPath *) path)->subpath, root, + return get_path_clauses_recurse(((LockRowsPath *) path)->subpath, root, selectivities); break; case T_LimitPath: - return get_path_clauses(((LimitPath *) path)->subpath, root, + return get_path_clauses_recurse(((LimitPath *) path)->subpath, root, selectivities); break; case T_SubqueryScanPath: /* Recursing into Subquery we must use subroot */ Assert(path->parent->subroot != NULL); - return get_path_clauses(((SubqueryScanPath *) path)->subpath, + return get_path_clauses_recurse(((SubqueryScanPath *) path)->subpath, path->parent->subroot, selectivities); break; @@ -432,11 +493,11 @@ get_path_clauses(Path *path, PlannerInfo *root, List **selectivities) { Path *subpath = lfirst(lc); - cur = list_concat(cur, list_copy( - get_path_clauses(subpath, root, selectivities))); + cur = list_concat(cur, + get_path_clauses_recurse(subpath, root, selectivities)); cur_sel = list_concat(cur_sel, *selectivities); } - cur = list_concat(cur, aqo_get_clauses(root, + cur = list_concat(cur, aqo_get_raw_clauses(root, path->parent->baserestrictinfo)); *selectivities = list_concat(cur_sel, get_selectivities(root, @@ -459,11 +520,11 @@ get_path_clauses(Path *path, PlannerInfo *root, List **selectivities) { Path *subpath = lfirst(lc); - cur = list_concat(cur, list_copy( - get_path_clauses(subpath, root, selectivities))); + cur = list_concat(cur, + get_path_clauses_recurse(subpath, root, selectivities)); cur_sel = list_concat(cur_sel, *selectivities); } - cur = list_concat(cur, aqo_get_clauses(root, + cur = list_concat(cur, aqo_get_raw_clauses(root, path->parent->baserestrictinfo)); *selectivities = list_concat(cur_sel, get_selectivities(root, @@ -475,7 +536,7 @@ get_path_clauses(Path *path, PlannerInfo *root, List **selectivities) case T_ForeignPath: /* The same as in the default case */ default: - cur = list_concat(list_copy(path->parent->baserestrictinfo), + cur = list_concat(list_concat(cur, path->parent->baserestrictinfo), path->param_info ? path->param_info->ppi_clauses : NIL); if (path->param_info) @@ -484,12 +545,26 @@ get_path_clauses(Path *path, PlannerInfo *root, List **selectivities) else cur_sel = get_selectivities(root, cur, 0, JOIN_INNER, NULL); *selectivities = cur_sel; - cur = aqo_get_clauses(root, cur); + cur = aqo_get_raw_clauses(root, cur); return cur; break; } } +/* + * Returns a list of AQOClauses for the given path, which is a copy + * of the clauses returned from the get_path_clauses_recurse() routine. + * Also returns selectivities for the clauses to 'selectivities' variable. + * Both returned lists are copies and therefore may be modified without + * corruption of the input data. + */ +List * +get_path_clauses(Path *path, PlannerInfo *root, List **selectivities) +{ + return copy_aqo_clauses_from_rinfo( + get_path_clauses_recurse(path, root, selectivities)); +} + /* * Some of paths are kind of utility path. I mean, It isn't corresponding to * specific RelOptInfo node. So, it should be omitted in process of clauses @@ -595,7 +670,7 @@ aqo_create_plan(PlannerInfo *root, Path *src, Plan **dest) (*dest)->lefttree->targetlist); /* Copy bare expressions for further AQO learning case. */ node->grouping_exprs = copyObject(groupExprs); - get_list_of_relids(root, ap->subpath->parent->relids, node->rels); + get_list_of_relids(root, ap->subpath->parent->relids, &node->rels); node->jointype = JOIN_INNER; } else if (is_appropriate_path(src)) @@ -606,7 +681,7 @@ aqo_create_plan(PlannerInfo *root, Path *src, Plan **dest) node->jointype = JOIN_INNER; } - get_list_of_relids(root, src->parent->relids, node->rels); + get_list_of_relids(root, src->parent->relids, &node->rels); if (src->parallel_workers > 0) node->parallel_divisor = get_parallel_divisor(src); @@ -641,15 +716,19 @@ AQOnodeCopy(struct ExtensibleNode *enew, const struct ExtensibleNode *eold) Assert(strcmp(old->node.extnodename, AQO_PLAN_NODE) == 0); Assert(new && old); - /* Copy static fields in one command */ - memcpy(new, old, sizeof(AQOPlanNode)); + /* + * Copy static fields in one command. + * But do not copy fields of the old->node. + * Elsewise, we can use pointers that will be freed. + * For example, it is old->node.extnodename. + */ + memcpy(&new->had_path, &old->had_path, sizeof(AQOPlanNode) - offsetof(AQOPlanNode, had_path)); /* These lists couldn't contain AQO nodes. Use basic machinery */ - new->rels = palloc(sizeof(RelSortOut)); - new->rels->hrels = list_copy(old->rels->hrels); - new->rels->signatures = list_copy(old->rels->signatures); + new->rels.hrels = list_copy(old->rels.hrels); + new->rels.signatures = list_copy(old->rels.signatures); - new->clauses = copyObject(old->clauses); + new->clauses = copy_aqo_clauses(old->clauses); new->grouping_exprs = copyObject(old->grouping_exprs); new->selectivities = copyObject(old->selectivities); enew = (ExtensibleNode *) new; @@ -661,6 +740,39 @@ AQOnodeEqual(const struct ExtensibleNode *a, const struct ExtensibleNode *b) return false; } +static void +AQOconstCopy(struct ExtensibleNode *enew, const struct ExtensibleNode *eold) +{ + AQOConstNode *new = (AQOConstNode *) enew; + AQOConstNode *old = (AQOConstNode *) eold; + + Assert(IsA(old, ExtensibleNode)); + Assert(strcmp(old->node.extnodename, AQO_CONST_NODE) == 0); + Assert(new && old); + + new->type = old->type; + new->fss = old->fss; + enew = (ExtensibleNode *) new; +} + +static bool +AQOconstEqual(const struct ExtensibleNode *a, const struct ExtensibleNode *b) +{ + return false; +} + +/* + * Convert a double value, attempting to ensure the value is preserved exactly. + */ +static void +outDouble(StringInfo str, double d) +{ + char buf[DOUBLE_SHORTEST_DECIMAL_LEN]; + + double_to_shortest_decimal_buf(d, buf); + appendStringInfoString(str, buf); +} + #define WRITE_INT_FIELD(fldname) \ appendStringInfo(str, " :" CppAsString(fldname) " %d", node->fldname) @@ -678,17 +790,57 @@ AQOnodeEqual(const struct ExtensibleNode *a, const struct ExtensibleNode *b) appendStringInfo(str, " :" CppAsString(fldname) " %d", \ (int) node->fldname) -/* Write a float field --- caller must give format to define precision */ -#define WRITE_FLOAT_FIELD(fldname,format) \ - appendStringInfo(str, " :" CppAsString(fldname) " " format, node->fldname) +/* Write a float field */ +#define WRITE_FLOAT_FIELD(fldname) \ + (appendStringInfo(str, " :" CppAsString(fldname) " "), \ + outDouble(str, node->fldname)) + +/* The start part of a custom list writer */ +#define WRITE_CUSTOM_LIST_START(fldname) \ + { \ + appendStringInfo(str, " :N_" CppAsString(fldname) " %d ", \ + list_length(node->fldname)); \ + /* Serialize this list like an array */ \ + if (list_length(node->fldname)) \ + { \ + ListCell *lc; \ + appendStringInfo(str, "("); \ + foreach (lc, node->fldname) + +/* The end part of a custom list writer */ +#define WRITE_CUSTOM_LIST_END() \ + appendStringInfo(str, " )"); \ + } \ + else \ + appendStringInfo(str, "<>"); \ + } + +/* Write a list of int values */ +#define WRITE_INT_LIST(fldname) \ + WRITE_CUSTOM_LIST_START(fldname) \ + { \ + int val = lfirst_int(lc); \ + appendStringInfo(str, " %d", val); \ + } \ + WRITE_CUSTOM_LIST_END() + +/* Write a list of AQOClause values */ +#define WRITE_AQOCLAUSE_LIST(fldname) \ + WRITE_CUSTOM_LIST_START(clauses) \ + { \ + AQOClause *node = lfirst(lc); \ + /* Serialize this struct like a node */ \ + appendStringInfo(str, " {"); \ + WRITE_NODE_FIELD(clause); \ + WRITE_FLOAT_FIELD(norm_selec); \ + WRITE_FLOAT_FIELD(outer_selec); \ + appendStringInfo(str, " }"); \ + } \ + WRITE_CUSTOM_LIST_END() /* * Serialize AQO plan node to a string. * - * Right now we can't correctly serialize all fields of the node. Taking into - * account that this action needed when a plan moves into parallel workers or - * just during debugging, we serialize it only partially, just for debug - * purposes. * Some extensions may manipulate by parts of serialized plan too. */ static void @@ -696,9 +848,36 @@ AQOnodeOut(struct StringInfoData *str, const struct ExtensibleNode *enode) { AQOPlanNode *node = (AQOPlanNode *) enode; - /* For Adaptive optimization DEBUG purposes */ + WRITE_BOOL_FIELD(had_path); + + WRITE_NODE_FIELD(rels.hrels); + WRITE_INT_LIST(rels.signatures); + + WRITE_AQOCLAUSE_LIST(clauses); + + WRITE_NODE_FIELD(selectivities); + WRITE_NODE_FIELD(grouping_exprs); + WRITE_ENUM_FIELD(jointype, JoinType); + + WRITE_FLOAT_FIELD(parallel_divisor); + WRITE_BOOL_FIELD(was_parametrized); + + WRITE_INT_FIELD(fss); + WRITE_FLOAT_FIELD(prediction); +} + +/* + * Serialize AQO const node to a string. + * + * Some extensions may manipulate by parts of serialized plan too. + */ +static void +AQOconstOut(struct StringInfoData *str, const struct ExtensibleNode *enode) +{ + AQOConstNode *node = (AQOConstNode *) enode; + + WRITE_ENUM_FIELD(type, AQOConstType); WRITE_INT_FIELD(fss); - WRITE_FLOAT_FIELD(prediction, "%.0f"); } /* Read an integer field (anything written as ":fldname %d") */ @@ -731,6 +910,54 @@ AQOnodeOut(struct StringInfoData *str, const struct ExtensibleNode *enode) (void) token; /* in case not used elsewhere */ \ local_node->fldname = nodeRead(NULL, 0) +/* The start part of a custom list reader */ +#define READ_CUSTOM_LIST_START() \ + { \ + int counter; \ + token = pg_strtok(&length); /* skip the name */ \ + token = pg_strtok(&length); \ + counter = atoi(token); \ + token = pg_strtok(&length); /* left bracket "(" */ \ + if (length) \ + { \ + for (int i = 0; i < counter; i++) + +/* The end part of a custom list reader */ +#define READ_CUSTOM_LIST_END(fldname) \ + token = pg_strtok(&length); /* right bracket ")" */ \ + } \ + else \ + local_node->fldname = NIL; \ + } + +/* Read a list of int values */ +#define READ_INT_LIST(fldname) \ + READ_CUSTOM_LIST_START() \ + { \ + int val; \ + token = pg_strtok(&length); \ + val = atoi(token); \ + local_node->fldname = lappend_int( \ + local_node->fldname, val); \ + } \ + READ_CUSTOM_LIST_END(fldname) + +/* Read a list of AQOClause values */ +#define READ_AQOCLAUSE_LIST(fldname) \ + READ_CUSTOM_LIST_START() \ + { \ + /* copy to use in the inner blocks of code */ \ + AQOPlanNode *node_copy = local_node; \ + AQOClause *local_node = palloc(sizeof(AQOClause)); \ + token = pg_strtok(&length); /* left bracket "{" */ \ + READ_NODE_FIELD(clause); \ + READ_FLOAT_FIELD(norm_selec); \ + READ_FLOAT_FIELD(outer_selec); \ + token = pg_strtok(&length); /* right bracket "}" */ \ + node_copy->fldname = lappend(node_copy->fldname, local_node); \ + } \ + READ_CUSTOM_LIST_END(fldname) + /* * Deserialize AQO plan node from a string to internal representation. * @@ -743,22 +970,41 @@ AQOnodeRead(struct ExtensibleNode *enode) const char *token; int length; - local_node->had_path = false; - local_node->jointype = 0; - local_node->parallel_divisor = 1.0; - local_node->was_parametrized = false; + READ_BOOL_FIELD(had_path); + + READ_NODE_FIELD(rels.hrels); + READ_INT_LIST(rels.signatures); + + READ_AQOCLAUSE_LIST(clauses); + + READ_NODE_FIELD(selectivities); + READ_NODE_FIELD(grouping_exprs); + READ_ENUM_FIELD(jointype, JoinType); - local_node->rels = palloc0(sizeof(RelSortOut)); - local_node->clauses = NIL; - local_node->selectivities = NIL; - local_node->grouping_exprs = NIL; + READ_FLOAT_FIELD(parallel_divisor); + READ_BOOL_FIELD(was_parametrized); - /* For Adaptive optimization DEBUG purposes */ READ_INT_FIELD(fss); READ_FLOAT_FIELD(prediction); } -static const ExtensibleNodeMethods method = +/* + * Deserialize AQO const node from a string to internal representation. + * + * Should work in coherence with AQOconstOut(). + */ +static void +AQOconstRead(struct ExtensibleNode *enode) +{ + AQOConstNode *local_node = (AQOConstNode *) enode; + const char *token; + int length; + + READ_ENUM_FIELD(type, AQOConstType); + READ_INT_FIELD(fss); +} + +static const ExtensibleNodeMethods aqo_node_method = { .extnodename = AQO_PLAN_NODE, .node_size = sizeof(AQOPlanNode), @@ -768,10 +1014,21 @@ static const ExtensibleNodeMethods method = .nodeRead = AQOnodeRead }; +static const ExtensibleNodeMethods aqo_const_method = +{ + .extnodename = AQO_CONST_NODE, + .node_size = sizeof(AQOConstNode), + .nodeCopy = AQOconstCopy, + .nodeEqual = AQOconstEqual, + .nodeOut = AQOconstOut, + .nodeRead = AQOconstRead +}; + void RegisterAQOPlanNodeMethods(void) { - RegisterExtensibleNodeMethods(&method); + RegisterExtensibleNodeMethods(&aqo_node_method); + RegisterExtensibleNodeMethods(&aqo_const_method); } /* diff --git a/path_utils.h b/path_utils.h index cbe83da0..0d5d68bd 100644 --- a/path_utils.h +++ b/path_utils.h @@ -6,6 +6,7 @@ #include "optimizer/planner.h" #define AQO_PLAN_NODE "AQOPlanNode" +#define AQO_CONST_NODE "AQOConstNode" /* * Find and sort out relations that used in the query: @@ -20,6 +21,20 @@ typedef struct * table or on a table structure for temp table */ } RelSortOut; +/* + * Fields of the RestrictInfo needed in the AQOPlanNode + */ +typedef struct AQOClause +{ + /* the represented clause of WHERE or JOIN */ + Expr *clause; + /* selectivity for "normal" (JOIN_INNER) semantics; -1 if not yet set */ + Selectivity norm_selec; + /* selectivity for outer join semantics; -1 if not yet set */ + Selectivity outer_selec; + +} AQOClause; + /* * information for adaptive query optimization */ @@ -27,7 +42,7 @@ typedef struct AQOPlanNode { ExtensibleNode node; bool had_path; - RelSortOut *rels; + RelSortOut rels; List *clauses; List *selectivities; @@ -43,6 +58,25 @@ typedef struct AQOPlanNode double prediction; } AQOPlanNode; +/* + * The type of a node that is replaced by AQOConstNode. + */ +typedef enum AQOConstType +{ + AQO_NODE_EXPR = 0, + AQO_NODE_SUBPLAN +} AQOConstType; + +/* + * A custom node that is used to calcucate a fss instead of regular node, + * such as SubPlan or Expr. + */ +typedef struct AQOConstNode +{ + ExtensibleNode node; + AQOConstType type; /* The type of the replaced node */ + int fss; /* The fss of the replaced node */ +} AQOConstNode; #define strtobool(x) ((*(x) == 't') ? true : false) @@ -64,6 +98,8 @@ extern List *get_path_clauses(Path *path, PlannerInfo *root, List **selectivities); +extern AQOConstNode *create_aqo_const_node(AQOConstType type, int fss); + extern AQOPlanNode *get_aqo_plan_node(Plan *plan, bool create); extern void RegisterAQOPlanNodeMethods(void); diff --git a/postprocessing.c b/postprocessing.c index 7df0a253..e166f84c 100644 --- a/postprocessing.c +++ b/postprocessing.c @@ -197,12 +197,12 @@ restore_selectivities(List *clauselist, List *relidslist, JoinType join_type, foreach(l, clauselist) { - RestrictInfo *rinfo = (RestrictInfo *) lfirst(l); - Selectivity *cur_sel = NULL; + AQOClause *clause = (AQOClause *) lfirst(l); + Selectivity *cur_sel = NULL; if (parametrized_sel) { - cur_hash = get_clause_hash(rinfo->clause, nargs, + cur_hash = get_clause_hash(clause->clause, nargs, args_hash, eclass_hash); cur_sel = selectivity_cache_find_global_relid(cur_hash, cur_relid); } @@ -212,9 +212,9 @@ restore_selectivities(List *clauselist, List *relidslist, JoinType join_type, cur_sel = palloc(sizeof(double)); if (join_type == JOIN_INNER) - *cur_sel = rinfo->norm_selec; + *cur_sel = clause->norm_selec; else - *cur_sel = rinfo->outer_selec; + *cur_sel = clause->outer_selec; if (*cur_sel < 0) *cur_sel = 0; @@ -500,7 +500,7 @@ learnOnPlanState(PlanState *p, void *context) List *cur_selectivities; cur_selectivities = restore_selectivities(aqo_node->clauses, - aqo_node->rels->hrels, + aqo_node->rels.hrels, aqo_node->jointype, aqo_node->was_parametrized); SubplanCtx.selectivities = list_concat(SubplanCtx.selectivities, @@ -508,14 +508,14 @@ learnOnPlanState(PlanState *p, void *context) SubplanCtx.clauselist = list_concat(SubplanCtx.clauselist, list_copy(aqo_node->clauses)); - if (aqo_node->rels->hrels != NIL) + if (aqo_node->rels.hrels != NIL) { /* * This plan can be stored as a cached plan. In the case we will have * bogus path_relids field (changed by list_concat routine) at the * next usage (and aqo-learn) of this plan. */ - ctx->relidslist = list_copy(aqo_node->rels->hrels); + ctx->relidslist = list_copy(aqo_node->rels.hrels); if (p->instrument) { @@ -527,12 +527,12 @@ learnOnPlanState(PlanState *p, void *context) { if (IsA(p, AggState)) learn_agg_sample(&SubplanCtx, - aqo_node->rels, learn_rows, rfactor, + &aqo_node->rels, learn_rows, rfactor, p->plan, notExecuted); else learn_sample(&SubplanCtx, - aqo_node->rels, learn_rows, rfactor, + &aqo_node->rels, learn_rows, rfactor, p->plan, notExecuted); } }