summaryrefslogtreecommitdiffstats
path: root/src/backend/optimizer
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r--src/backend/optimizer/path/equivclass.c15
-rw-r--r--src/backend/optimizer/plan/planner.c27
-rw-r--r--src/backend/optimizer/prep/prepjointree.c4
-rw-r--r--src/backend/optimizer/util/clauses.c43
-rw-r--r--src/backend/optimizer/util/pathnode.c9
-rw-r--r--src/backend/optimizer/util/relnode.c26
6 files changed, 90 insertions, 34 deletions
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 7fa502d..e79c7e4 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -1885,6 +1885,21 @@ create_join_clause(PlannerInfo *root,
rightem->em_relids),
ec->ec_min_security);
+ /*
+ * If either EM is a child, force the clause's clause_relids to include
+ * the relid(s) of the child rel. In normal cases it would already, but
+ * not if we are considering appendrel child relations with pseudoconstant
+ * translated variables (i.e., UNION ALL sub-selects with constant output
+ * items). We must do this so that join_clause_is_movable_into() will
+ * think that the clause should be evaluated at the correct place.
+ */
+ if (leftem->em_is_child)
+ rinfo->clause_relids = bms_add_members(rinfo->clause_relids,
+ leftem->em_relids);
+ if (rightem->em_is_child)
+ rinfo->clause_relids = bms_add_members(rinfo->clause_relids,
+ rightem->em_relids);
+
/* If it's a child clause, copy the parent's rinfo_serial */
if (parent_rinfo)
rinfo->rinfo_serial = parent_rinfo->rinfo_serial;
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 80ad6bf..2ffef1b 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -7349,13 +7349,24 @@ gather_grouping_paths(PlannerInfo *root, RelOptInfo *rel)
{
ListCell *lc;
Path *cheapest_partial_path;
+ List *groupby_pathkeys;
+
+ /*
+ * This occurs after any partial aggregation has taken place, so trim off
+ * any pathkeys added for ORDER BY / DISTINCT aggregates.
+ */
+ if (list_length(root->group_pathkeys) > root->num_groupby_pathkeys)
+ groupby_pathkeys = list_copy_head(root->group_pathkeys,
+ root->num_groupby_pathkeys);
+ else
+ groupby_pathkeys = root->group_pathkeys;
/* Try Gather for unordered paths and Gather Merge for ordered ones. */
generate_useful_gather_paths(root, rel, true);
/* Try cheapest partial path + explicit Sort + Gather Merge. */
cheapest_partial_path = linitial(rel->partial_pathlist);
- if (!pathkeys_contained_in(root->group_pathkeys,
+ if (!pathkeys_contained_in(groupby_pathkeys,
cheapest_partial_path->pathkeys))
{
Path *path;
@@ -7364,14 +7375,14 @@ gather_grouping_paths(PlannerInfo *root, RelOptInfo *rel)
total_groups =
cheapest_partial_path->rows * cheapest_partial_path->parallel_workers;
path = (Path *) create_sort_path(root, rel, cheapest_partial_path,
- root->group_pathkeys,
+ groupby_pathkeys,
-1.0);
path = (Path *)
create_gather_merge_path(root,
rel,
path,
rel->reltarget,
- root->group_pathkeys,
+ groupby_pathkeys,
NULL,
&total_groups);
@@ -7382,10 +7393,10 @@ gather_grouping_paths(PlannerInfo *root, RelOptInfo *rel)
* Consider incremental sort on all partial paths, if enabled.
*
* We can also skip the entire loop when we only have a single-item
- * group_pathkeys because then we can't possibly have a presorted prefix
+ * groupby_pathkeys because then we can't possibly have a presorted prefix
* of the list without having the list be fully sorted.
*/
- if (!enable_incremental_sort || list_length(root->group_pathkeys) == 1)
+ if (!enable_incremental_sort || list_length(groupby_pathkeys) == 1)
return;
/* also consider incremental sort on partial paths, if enabled */
@@ -7396,7 +7407,7 @@ gather_grouping_paths(PlannerInfo *root, RelOptInfo *rel)
int presorted_keys;
double total_groups;
- is_sorted = pathkeys_count_contained_in(root->group_pathkeys,
+ is_sorted = pathkeys_count_contained_in(groupby_pathkeys,
path->pathkeys,
&presorted_keys);
@@ -7409,7 +7420,7 @@ gather_grouping_paths(PlannerInfo *root, RelOptInfo *rel)
path = (Path *) create_incremental_sort_path(root,
rel,
path,
- root->group_pathkeys,
+ groupby_pathkeys,
presorted_keys,
-1.0);
@@ -7418,7 +7429,7 @@ gather_grouping_paths(PlannerInfo *root, RelOptInfo *rel)
rel,
path,
rel->reltarget,
- root->group_pathkeys,
+ groupby_pathkeys,
NULL,
&total_groups);
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 57262f9..1171097 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -1805,6 +1805,10 @@ pull_up_constant_function(PlannerInfo *root, Node *jtnode,
if (rtf->funccolcount != 1)
return jtnode; /* definitely composite */
+ /* If it has a coldeflist, it certainly returns RECORD */
+ if (rtf->funccolnames != NIL)
+ return jtnode; /* must be a one-column RECORD type */
+
functypclass = get_expr_result_type(rtf->funcexpr,
&funcrettype,
&tupdesc);
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 507c101..6265099 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -2423,6 +2423,10 @@ static Node *
eval_const_expressions_mutator(Node *node,
eval_const_expressions_context *context)
{
+
+ /* since this function recurses, it could be driven to stack overflow */
+ check_stack_depth();
+
if (node == NULL)
return NULL;
switch (nodeTag(node))
@@ -4410,12 +4414,11 @@ evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
* Can't simplify if it returns RECORD. The immediate problem is that it
* will be needing an expected tupdesc which we can't supply here.
*
- * In the case where it has OUT parameters, it could get by without an
- * expected tupdesc, but we still have issues: get_expr_result_type()
- * doesn't know how to extract type info from a RECORD constant, and in
- * the case of a NULL function result there doesn't seem to be any clean
- * way to fix that. In view of the likelihood of there being still other
- * gotchas, seems best to leave the function call unreduced.
+ * In the case where it has OUT parameters, we could build an expected
+ * tupdesc from those, but there may be other gotchas lurking. In
+ * particular, if the function were to return NULL, we would produce a
+ * null constant with no remaining indication of which concrete record
+ * type it is. For now, seems best to leave the function call unreduced.
*/
if (funcform->prorettype == RECORDOID)
return NULL;
@@ -4704,9 +4707,10 @@ inline_function(Oid funcid, Oid result_type, Oid result_collid,
* needed; that's probably not important, but let's be careful.
*/
querytree_list = list_make1(querytree);
- if (check_sql_fn_retval(list_make1(querytree_list),
- result_type, rettupdesc,
- false, NULL))
+ if (check_sql_fn_retval_ext(list_make1(querytree_list),
+ result_type, rettupdesc,
+ funcform->prokind,
+ false, NULL))
goto fail; /* reject whole-tuple-result cases */
/*
@@ -5215,16 +5219,20 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
}
/*
- * Also resolve the actual function result tupdesc, if composite. If the
- * function is just declared to return RECORD, dig the info out of the AS
- * clause.
+ * Also resolve the actual function result tupdesc, if composite. If we
+ * have a coldeflist, believe that; otherwise use get_expr_result_type.
+ * (This logic should match ExecInitFunctionScan.)
*/
- functypclass = get_expr_result_type((Node *) fexpr, NULL, &rettupdesc);
- if (functypclass == TYPEFUNC_RECORD)
+ if (rtfunc->funccolnames != NIL)
+ {
+ functypclass = TYPEFUNC_RECORD;
rettupdesc = BuildDescFromLists(rtfunc->funccolnames,
rtfunc->funccoltypes,
rtfunc->funccoltypmods,
rtfunc->funccolcollations);
+ }
+ else
+ functypclass = get_expr_result_type((Node *) fexpr, NULL, &rettupdesc);
/*
* The single command must be a plain SELECT.
@@ -5246,9 +5254,10 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
* shows it's returning a whole tuple result; otherwise what it's
* returning is a single composite column which is not what we need.
*/
- if (!check_sql_fn_retval(list_make1(querytree_list),
- fexpr->funcresulttype, rettupdesc,
- true, NULL) &&
+ if (!check_sql_fn_retval_ext(list_make1(querytree_list),
+ fexpr->funcresulttype, rettupdesc,
+ funcform->prokind,
+ true, NULL) &&
(functypclass == TYPEFUNC_COMPOSITE ||
functypclass == TYPEFUNC_COMPOSITE_DOMAIN ||
functypclass == TYPEFUNC_RECORD))
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 841be37..bd23226 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -1708,8 +1708,13 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
pathnode->path.pathkeys = NIL;
pathnode->subpath = subpath;
- pathnode->in_operators = sjinfo->semi_operators;
- pathnode->uniq_exprs = sjinfo->semi_rhs_exprs;
+
+ /*
+ * Under GEQO, the sjinfo might be short-lived, so we'd better make copies
+ * of data structures we extract from it.
+ */
+ pathnode->in_operators = copyObject(sjinfo->semi_operators);
+ pathnode->uniq_exprs = copyObject(sjinfo->semi_rhs_exprs);
/*
* If the input is a relation and it has a unique index that proves the
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 76dad17..e6d5cd8 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -1535,6 +1535,7 @@ get_baserel_parampathinfo(PlannerInfo *root, RelOptInfo *baserel,
ParamPathInfo *ppi;
Relids joinrelids;
List *pclauses;
+ List *eqclauses;
Bitmapset *pserials;
double rows;
ListCell *lc;
@@ -1570,14 +1571,25 @@ get_baserel_parampathinfo(PlannerInfo *root, RelOptInfo *baserel,
/*
* Add in joinclauses generated by EquivalenceClasses, too. (These
- * necessarily satisfy join_clause_is_movable_into.)
+ * necessarily satisfy join_clause_is_movable_into; but in assert-enabled
+ * builds, let's verify that.)
*/
- pclauses = list_concat(pclauses,
- generate_join_implied_equalities(root,
- joinrelids,
- required_outer,
- baserel,
- NULL));
+ eqclauses = generate_join_implied_equalities(root,
+ joinrelids,
+ required_outer,
+ baserel,
+ NULL);
+#ifdef USE_ASSERT_CHECKING
+ foreach(lc, eqclauses)
+ {
+ RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+
+ Assert(join_clause_is_movable_into(rinfo,
+ baserel->relids,
+ joinrelids));
+ }
+#endif
+ pclauses = list_concat(pclauses, eqclauses);
/* Compute set of serial numbers of the enforced clauses */
pserials = NULL;