mirror of
https://github.com/postgres/postgres.git
synced 2025-11-19 13:42:17 +03:00
Revert commit 66c0185a3 and follow-on patches.
This reverts66c0185a3(Allow planner to use Merge Append to efficiently implement UNION) as well as the follow-on commitsd5d2205c8,3b1a7eb28,7487044d6. In addition to those,07746a8efhad to be removed then re-applied in a different place, because66c0185a3moved the relevant code. The reason for this last-minute thrashing is that depesz found a case in which the patched code creates a completely wrong plan that silently gives incorrect query results. It's unclear what the cause is or how many cases are affected, but with beta1 wrap staring us in the face, there's no time for closer investigation. After we figure that out, we can decide whether to un-revert this for beta2 or hold it for v18. Discussion: https://postgr.es/m/Zktzf926vslR35Fv@depesz.com (also some private discussion among pgsql-release)
This commit is contained in:
@@ -54,7 +54,6 @@
|
||||
#include "optimizer/tlist.h"
|
||||
#include "parser/analyze.h"
|
||||
#include "parser/parse_agg.h"
|
||||
#include "parser/parse_clause.h"
|
||||
#include "parser/parse_relation.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "partitioning/partdesc.h"
|
||||
@@ -120,15 +119,12 @@ typedef struct
|
||||
{
|
||||
List *activeWindows; /* active windows, if any */
|
||||
grouping_sets_data *gset_data; /* grouping sets data, if any */
|
||||
SetOperationStmt *setop; /* parent set operation or NULL if not a
|
||||
* subquery belonging to a set operation */
|
||||
} standard_qp_extra;
|
||||
|
||||
/* Local functions */
|
||||
static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind);
|
||||
static void preprocess_qual_conditions(PlannerInfo *root, Node *jtnode);
|
||||
static void grouping_planner(PlannerInfo *root, double tuple_fraction,
|
||||
SetOperationStmt *setops);
|
||||
static void grouping_planner(PlannerInfo *root, double tuple_fraction);
|
||||
static grouping_sets_data *preprocess_grouping_sets(PlannerInfo *root);
|
||||
static List *remap_to_groupclause_idx(List *groupClause, List *gsets,
|
||||
int *tleref_to_colnum_map);
|
||||
@@ -253,8 +249,6 @@ static bool group_by_has_partkey(RelOptInfo *input_rel,
|
||||
List *targetList,
|
||||
List *groupClause);
|
||||
static int common_prefix_cmp(const void *a, const void *b);
|
||||
static List *generate_setop_child_grouplist(SetOperationStmt *op,
|
||||
List *targetlist);
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
@@ -412,7 +406,8 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
|
||||
}
|
||||
|
||||
/* primary planning entry point (may recurse for subqueries) */
|
||||
root = subquery_planner(glob, parse, NULL, false, tuple_fraction, NULL);
|
||||
root = subquery_planner(glob, parse, NULL,
|
||||
false, tuple_fraction);
|
||||
|
||||
/* Select best Path and turn it into a Plan */
|
||||
final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL);
|
||||
@@ -603,10 +598,6 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
|
||||
* hasRecursion is true if this is a recursive WITH query.
|
||||
* tuple_fraction is the fraction of tuples we expect will be retrieved.
|
||||
* tuple_fraction is interpreted as explained for grouping_planner, below.
|
||||
* setops is used for set operation subqueries to provide the subquery with
|
||||
* the context in which it's being used so that Paths correctly sorted for the
|
||||
* set operation can be generated. NULL when not planning a set operation
|
||||
* child.
|
||||
*
|
||||
* Basically, this routine does the stuff that should only be done once
|
||||
* per Query object. It then calls grouping_planner. At one time,
|
||||
@@ -625,9 +616,9 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
|
||||
*--------------------
|
||||
*/
|
||||
PlannerInfo *
|
||||
subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root,
|
||||
bool hasRecursion, double tuple_fraction,
|
||||
SetOperationStmt *setops)
|
||||
subquery_planner(PlannerGlobal *glob, Query *parse,
|
||||
PlannerInfo *parent_root,
|
||||
bool hasRecursion, double tuple_fraction)
|
||||
{
|
||||
PlannerInfo *root;
|
||||
List *newWithCheckOptions;
|
||||
@@ -1086,7 +1077,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root,
|
||||
/*
|
||||
* Do the main planning.
|
||||
*/
|
||||
grouping_planner(root, tuple_fraction, setops);
|
||||
grouping_planner(root, tuple_fraction);
|
||||
|
||||
/*
|
||||
* Capture the set of outer-level param IDs we have access to, for use in
|
||||
@@ -1284,11 +1275,7 @@ preprocess_phv_expression(PlannerInfo *root, Expr *expr)
|
||||
* 0 < tuple_fraction < 1: expect the given fraction of tuples available
|
||||
* from the plan to be retrieved
|
||||
* tuple_fraction >= 1: tuple_fraction is the absolute number of tuples
|
||||
* expected to be retrieved (ie, a LIMIT specification).
|
||||
* setops is used for set operation subqueries to provide the subquery with
|
||||
* the context in which it's being used so that Paths correctly sorted for the
|
||||
* set operation can be generated. NULL when not planning a set operation
|
||||
* child.
|
||||
* expected to be retrieved (ie, a LIMIT specification)
|
||||
*
|
||||
* Returns nothing; the useful output is in the Paths we attach to the
|
||||
* (UPPERREL_FINAL, NULL) upperrel in *root. In addition,
|
||||
@@ -1299,8 +1286,7 @@ preprocess_phv_expression(PlannerInfo *root, Expr *expr)
|
||||
*--------------------
|
||||
*/
|
||||
static void
|
||||
grouping_planner(PlannerInfo *root, double tuple_fraction,
|
||||
SetOperationStmt *setops)
|
||||
grouping_planner(PlannerInfo *root, double tuple_fraction)
|
||||
{
|
||||
Query *parse = root->parse;
|
||||
int64 offset_est = 0;
|
||||
@@ -1335,6 +1321,17 @@ grouping_planner(PlannerInfo *root, double tuple_fraction,
|
||||
|
||||
if (parse->setOperations)
|
||||
{
|
||||
/*
|
||||
* If there's a top-level ORDER BY, assume we have to fetch all the
|
||||
* tuples. This might be too simplistic given all the hackery below
|
||||
* to possibly avoid the sort; but the odds of accurate estimates here
|
||||
* are pretty low anyway. XXX try to get rid of this in favor of
|
||||
* letting plan_set_operations generate both fast-start and
|
||||
* cheapest-total paths.
|
||||
*/
|
||||
if (parse->sortClause)
|
||||
root->tuple_fraction = 0.0;
|
||||
|
||||
/*
|
||||
* Construct Paths for set operations. The results will not need any
|
||||
* work except perhaps a top-level sort and/or LIMIT. Note that any
|
||||
@@ -1504,12 +1501,6 @@ grouping_planner(PlannerInfo *root, double tuple_fraction,
|
||||
qp_extra.activeWindows = activeWindows;
|
||||
qp_extra.gset_data = gset_data;
|
||||
|
||||
/*
|
||||
* If we're a subquery for a set operation, store the SetOperationStmt
|
||||
* in qp_extra.
|
||||
*/
|
||||
qp_extra.setop = setops;
|
||||
|
||||
/*
|
||||
* Generate the best unsorted and presorted paths for the scan/join
|
||||
* portion of this Query, ie the processing represented by the
|
||||
@@ -3462,27 +3453,6 @@ standard_qp_callback(PlannerInfo *root, void *extra)
|
||||
parse->sortClause,
|
||||
tlist);
|
||||
|
||||
/* setting setop_pathkeys might be useful to the union planner */
|
||||
if (qp_extra->setop != NULL &&
|
||||
set_operation_ordered_results_useful(qp_extra->setop))
|
||||
{
|
||||
List *groupClauses;
|
||||
bool sortable;
|
||||
|
||||
groupClauses = generate_setop_child_grouplist(qp_extra->setop, tlist);
|
||||
|
||||
root->setop_pathkeys =
|
||||
make_pathkeys_for_sortclauses_extended(root,
|
||||
&groupClauses,
|
||||
tlist,
|
||||
false,
|
||||
&sortable);
|
||||
if (!sortable)
|
||||
root->setop_pathkeys = NIL;
|
||||
}
|
||||
else
|
||||
root->setop_pathkeys = NIL;
|
||||
|
||||
/*
|
||||
* Figure out whether we want a sorted result from query_planner.
|
||||
*
|
||||
@@ -3492,9 +3462,7 @@ standard_qp_callback(PlannerInfo *root, void *extra)
|
||||
* sortable DISTINCT clause that's more rigorous than the ORDER BY clause,
|
||||
* we try to produce output that's sufficiently well sorted for the
|
||||
* DISTINCT. Otherwise, if there is an ORDER BY clause, we want to sort
|
||||
* by the ORDER BY clause. Otherwise, if we're a subquery being planned
|
||||
* for a set operation which can benefit from presorted results and have a
|
||||
* sortable targetlist, we want to sort by the target list.
|
||||
* by the ORDER BY clause.
|
||||
*
|
||||
* Note: if we have both ORDER BY and GROUP BY, and ORDER BY is a superset
|
||||
* of GROUP BY, it would be tempting to request sort by ORDER BY --- but
|
||||
@@ -3512,8 +3480,6 @@ standard_qp_callback(PlannerInfo *root, void *extra)
|
||||
root->query_pathkeys = root->distinct_pathkeys;
|
||||
else if (root->sort_pathkeys)
|
||||
root->query_pathkeys = root->sort_pathkeys;
|
||||
else if (root->setop_pathkeys != NIL)
|
||||
root->query_pathkeys = root->setop_pathkeys;
|
||||
else
|
||||
root->query_pathkeys = NIL;
|
||||
}
|
||||
@@ -7957,43 +7923,3 @@ group_by_has_partkey(RelOptInfo *input_rel,
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* generate_setop_child_grouplist
|
||||
* Build a SortGroupClause list defining the sort/grouping properties
|
||||
* of the child of a set operation.
|
||||
*
|
||||
* This is similar to generate_setop_grouplist() but differs as the setop
|
||||
* child query's targetlist entries may already have a tleSortGroupRef
|
||||
* assigned for other purposes, such as GROUP BYs. Here we keep the
|
||||
* SortGroupClause list in the same order as 'op' groupClauses and just adjust
|
||||
* the tleSortGroupRef to reference the TargetEntry's 'ressortgroupref'.
|
||||
*/
|
||||
static List *
|
||||
generate_setop_child_grouplist(SetOperationStmt *op, List *targetlist)
|
||||
{
|
||||
List *grouplist = copyObject(op->groupClauses);
|
||||
ListCell *lg;
|
||||
ListCell *lt;
|
||||
|
||||
lg = list_head(grouplist);
|
||||
foreach(lt, targetlist)
|
||||
{
|
||||
TargetEntry *tle = (TargetEntry *) lfirst(lt);
|
||||
SortGroupClause *sgc;
|
||||
|
||||
/* resjunk columns could have sortgrouprefs. Leave these alone */
|
||||
if (tle->resjunk)
|
||||
continue;
|
||||
|
||||
/* we expect every non-resjunk target to have a SortGroupClause */
|
||||
Assert(lg != NULL);
|
||||
sgc = (SortGroupClause *) lfirst(lg);
|
||||
lg = lnext(grouplist, lg);
|
||||
|
||||
/* assign a tleSortGroupRef, or reuse the existing one */
|
||||
sgc->tleSortGroupRef = assignSortGroupRef(tle, targetlist);
|
||||
}
|
||||
Assert(lg == NULL);
|
||||
return grouplist;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user