From e75d99818dd3940be997520e64db8c9e3b207e39 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Tue, 21 May 2024 07:05:26 +0200 Subject: Merging upstream version 15.7. Signed-off-by: Daniel Baumann --- src/backend/optimizer/path/equivclass.c | 15 +++++++++++ src/backend/optimizer/prep/prepjointree.c | 4 +++ src/backend/optimizer/util/clauses.c | 43 +++++++++++++++++++------------ src/backend/optimizer/util/pathnode.c | 9 +++++-- src/backend/optimizer/util/relnode.c | 25 +++++++++++++----- 5 files changed, 70 insertions(+), 26 deletions(-) (limited to 'src/backend/optimizer') diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c index 9f39f46..06f89a6 100644 --- a/src/backend/optimizer/path/equivclass.c +++ b/src/backend/optimizer/path/equivclass.c @@ -1851,6 +1851,21 @@ create_join_clause(PlannerInfo *root, rightem->em_nullable_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); + /* Mark the clause as redundant, or not */ rinfo->parent_ec = parent_ec; diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index ea05763..0efcc3b 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -1822,6 +1822,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 e1cedd9..9fcfd55 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -2347,6 +2347,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)) @@ -4319,12 +4323,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; @@ -4618,9 +4621,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 */ /* @@ -5134,16 +5138,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. @@ -5165,9 +5173,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 46fd29b..e5c82bf 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -1696,8 +1696,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 3c75fd5..6e1c87a 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -1300,6 +1300,7 @@ get_baserel_parampathinfo(PlannerInfo *root, RelOptInfo *baserel, ParamPathInfo *ppi; Relids joinrelids; List *pclauses; + List *eqclauses; double rows; ListCell *lc; @@ -1333,14 +1334,24 @@ get_baserel_parampathinfo(PlannerInfo *root, RelOptInfo *baserel, } /* - * Add in joinclauses generated by EquivalenceClasses, too. (These - * necessarily satisfy join_clause_is_movable_into.) + * Add in joinclauses generated by EquivalenceClasses, too. In principle + * these should always satisfy join_clause_is_movable_into; but if we are + * below an outer join the clauses might contain Vars that should only be + * evaluated above the join, so we have to check. */ - pclauses = list_concat(pclauses, - generate_join_implied_equalities(root, - joinrelids, - required_outer, - baserel)); + eqclauses = generate_join_implied_equalities(root, + joinrelids, + required_outer, + baserel); + foreach(lc, eqclauses) + { + RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); + + if (join_clause_is_movable_into(rinfo, + baserel->relids, + joinrelids)) + pclauses = lappend(pclauses, rinfo); + } /* Estimate the number of rows returned by the parameterized scan */ rows = get_parameterized_baserel_size(root, baserel, pclauses); -- cgit v1.2.3