summaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/access/transam/xlog.c9
-rw-r--r--src/backend/catalog/namespace.c3
-rw-r--r--src/backend/optimizer/path/allpaths.c21
-rw-r--r--src/backend/optimizer/prep/prepunion.c59
-rw-r--r--src/backend/parser/analyze.c2
-rw-r--r--src/backend/parser/gram.y14
-rw-r--r--src/backend/parser/parse_clause.c65
-rw-r--r--src/backend/utils/adt/ruleutils.c4
8 files changed, 156 insertions, 21 deletions
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 109713315c0..eceab341255 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -3134,6 +3134,10 @@ XLogNeedsFlush(XLogRecPtr record)
*/
if (!XLogInsertAllowed())
{
+ /* Quick exit if already known to be updated or cannot be updated */
+ if (!updateMinRecoveryPoint || record <= LocalMinRecoveryPoint)
+ return false;
+
/*
* An invalid minRecoveryPoint means that we need to recover all the
* WAL, i.e., we're doing crash recovery. We never modify the control
@@ -3143,11 +3147,10 @@ XLogNeedsFlush(XLogRecPtr record)
* it has not replayed all WAL available when doing crash recovery.
*/
if (XLogRecPtrIsInvalid(LocalMinRecoveryPoint) && InRecovery)
+ {
updateMinRecoveryPoint = false;
-
- /* Quick exit if already known to be updated or cannot be updated */
- if (record <= LocalMinRecoveryPoint || !updateMinRecoveryPoint)
return false;
+ }
/*
* Update local copy of minRecoveryPoint. But if the lock is busy,
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index ed9aeee24bc..d23474da4fb 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -2753,6 +2753,9 @@ StatisticsObjIsVisibleExt(Oid stxid, bool *is_missing)
{
Oid namespaceId = lfirst_oid(l);
+ if (namespaceId == myTempNamespace)
+ continue; /* do not look in temp namespace */
+
if (namespaceId == stxnamespace)
{
/* Found it first in path */
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 132e8a35a39..d7ff36d89be 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -2254,10 +2254,9 @@ set_dummy_rel_pathlist(RelOptInfo *rel)
* return false.
*/
static bool
-find_window_run_conditions(Query *subquery, RangeTblEntry *rte,
- AttrNumber attno, WindowFunc *wfunc, OpExpr *opexpr,
- bool wfunc_left, bool *keep_original,
- Bitmapset **run_cond_attrs)
+find_window_run_conditions(Query *subquery, AttrNumber attno,
+ WindowFunc *wfunc, OpExpr *opexpr, bool wfunc_left,
+ bool *keep_original, Bitmapset **run_cond_attrs)
{
Oid prosupport;
Expr *otherexpr;
@@ -2445,7 +2444,7 @@ find_window_run_conditions(Query *subquery, RangeTblEntry *rte,
* will use the runCondition to stop returning tuples.
*/
static bool
-check_and_push_window_quals(Query *subquery, RangeTblEntry *rte, Node *clause,
+check_and_push_window_quals(Query *subquery, Node *clause,
Bitmapset **run_cond_attrs)
{
OpExpr *opexpr = (OpExpr *) clause;
@@ -2485,9 +2484,8 @@ check_and_push_window_quals(Query *subquery, RangeTblEntry *rte, Node *clause,
TargetEntry *tle = list_nth(subquery->targetList, var1->varattno - 1);
WindowFunc *wfunc = (WindowFunc *) tle->expr;
- if (find_window_run_conditions(subquery, rte, tle->resno, wfunc,
- opexpr, true, &keep_original,
- run_cond_attrs))
+ if (find_window_run_conditions(subquery, tle->resno, wfunc, opexpr,
+ true, &keep_original, run_cond_attrs))
return keep_original;
}
@@ -2498,9 +2496,8 @@ check_and_push_window_quals(Query *subquery, RangeTblEntry *rte, Node *clause,
TargetEntry *tle = list_nth(subquery->targetList, var2->varattno - 1);
WindowFunc *wfunc = (WindowFunc *) tle->expr;
- if (find_window_run_conditions(subquery, rte, tle->resno, wfunc,
- opexpr, false, &keep_original,
- run_cond_attrs))
+ if (find_window_run_conditions(subquery, tle->resno, wfunc, opexpr,
+ false, &keep_original, run_cond_attrs))
return keep_original;
}
@@ -2622,7 +2619,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
* runCondition.
*/
if (!subquery->hasWindowFuncs ||
- check_and_push_window_quals(subquery, rte, clause,
+ check_and_push_window_quals(subquery, clause,
&run_cond_attrs))
{
/*
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 28a4ae64440..6bd0f4a5dc3 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -23,6 +23,8 @@
*/
#include "postgres.h"
+#include <math.h>
+
#include "access/htup_details.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
@@ -74,6 +76,8 @@ static List *generate_append_tlist(List *colTypes, List *colCollations,
List *input_tlists,
List *refnames_tlist);
static List *generate_setop_grouplist(SetOperationStmt *op, List *targetlist);
+static PathTarget *create_setop_pathtarget(PlannerInfo *root, List *tlist,
+ List *child_pathlist);
/*
@@ -803,7 +807,8 @@ generate_union_paths(SetOperationStmt *op, PlannerInfo *root,
/* Build result relation. */
result_rel = fetch_upper_rel(root, UPPERREL_SETOP, relids);
- result_rel->reltarget = create_pathtarget(root, tlist);
+ result_rel->reltarget = create_setop_pathtarget(root, tlist,
+ cheapest_pathlist);
result_rel->consider_parallel = consider_parallel;
result_rel->consider_startup = (root->tuple_fraction > 0);
@@ -892,7 +897,7 @@ generate_union_paths(SetOperationStmt *op, PlannerInfo *root,
path = (Path *) create_agg_path(root,
result_rel,
apath,
- create_pathtarget(root, tlist),
+ result_rel->reltarget,
AGG_HASHED,
AGGSPLIT_SIMPLE,
groupList,
@@ -908,7 +913,7 @@ generate_union_paths(SetOperationStmt *op, PlannerInfo *root,
path = (Path *) create_agg_path(root,
result_rel,
gpath,
- create_pathtarget(root, tlist),
+ result_rel->reltarget,
AGG_HASHED,
AGGSPLIT_SIMPLE,
groupList,
@@ -1130,7 +1135,18 @@ generate_nonunion_paths(SetOperationStmt *op, PlannerInfo *root,
/* Build result relation. */
result_rel = fetch_upper_rel(root, UPPERREL_SETOP,
bms_union(lrel->relids, rrel->relids));
- result_rel->reltarget = create_pathtarget(root, tlist);
+
+ /*
+ * Create the PathTarget and set the width accordingly. For EXCEPT, since
+ * the set op result won't contain rows from the rpath, we only account
+ * for the width of the lpath. For INTERSECT, use both input paths.
+ */
+ if (op->op == SETOP_EXCEPT)
+ result_rel->reltarget = create_setop_pathtarget(root, tlist,
+ list_make1(lpath));
+ else
+ result_rel->reltarget = create_setop_pathtarget(root, tlist,
+ list_make2(lpath, rpath));
/*
* Estimate number of distinct groups that we'll need hashtable entries
@@ -1619,3 +1635,38 @@ generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
Assert(lg == NULL);
return grouplist;
}
+
+/*
+ * create_setop_pathtarget
+ * Do the normal create_pathtarget() work, plus set the resulting
+ * PathTarget's width to the average width of the Paths in child_pathlist
+ * weighted using the estimated row count of each path.
+ *
+ * Note: This is required because set op target lists use varno==0, which
+ * results in a type default width estimate rather than one that's based on
+ * statistics of the columns from the set op children.
+ */
+static PathTarget *
+create_setop_pathtarget(PlannerInfo *root, List *tlist, List *child_pathlist)
+{
+ PathTarget *reltarget;
+ ListCell *lc;
+ double parent_rows = 0;
+ double parent_size = 0;
+
+ reltarget = create_pathtarget(root, tlist);
+
+ /* Calculate the total rows and total size. */
+ foreach(lc, child_pathlist)
+ {
+ Path *path = (Path *) lfirst(lc);
+
+ parent_rows += path->rows;
+ parent_size += path->parent->reltarget->width * path->rows;
+ }
+
+ if (parent_rows > 0)
+ reltarget->width = rint(parent_size / parent_rows);
+
+ return reltarget;
+}
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 5aeb54eb5f6..3b392b084ad 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -1467,12 +1467,14 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt,
qry->groupClause = transformGroupClause(pstate,
stmt->groupClause,
+ stmt->groupByAll,
&qry->groupingSets,
&qry->targetList,
qry->sortClause,
EXPR_KIND_GROUP_BY,
false /* allow SQL92 rules */ );
qry->groupDistinct = stmt->groupDistinct;
+ qry->groupByAll = stmt->groupByAll;
if (stmt->distinctClause == NIL)
{
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 9fd48acb1f8..f1def67ac7c 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -120,6 +120,7 @@ typedef struct SelectLimit
typedef struct GroupClause
{
bool distinct;
+ bool all;
List *list;
} GroupClause;
@@ -12993,6 +12994,7 @@ simple_select:
n->whereClause = $6;
n->groupClause = ($7)->list;
n->groupDistinct = ($7)->distinct;
+ n->groupByAll = ($7)->all;
n->havingClause = $8;
n->windowClause = $9;
$$ = (Node *) n;
@@ -13010,6 +13012,7 @@ simple_select:
n->whereClause = $6;
n->groupClause = ($7)->list;
n->groupDistinct = ($7)->distinct;
+ n->groupByAll = ($7)->all;
n->havingClause = $8;
n->windowClause = $9;
$$ = (Node *) n;
@@ -13507,14 +13510,24 @@ group_clause:
GroupClause *n = (GroupClause *) palloc(sizeof(GroupClause));
n->distinct = $3 == SET_QUANTIFIER_DISTINCT;
+ n->all = false;
n->list = $4;
$$ = n;
}
+ | GROUP_P BY ALL
+ {
+ GroupClause *n = (GroupClause *) palloc(sizeof(GroupClause));
+ n->distinct = false;
+ n->all = true;
+ n->list = NIL;
+ $$ = n;
+ }
| /*EMPTY*/
{
GroupClause *n = (GroupClause *) palloc(sizeof(GroupClause));
n->distinct = false;
+ n->all = false;
n->list = NIL;
$$ = n;
}
@@ -17618,6 +17631,7 @@ PLpgSQL_Expr: opt_distinct_clause opt_target_list
n->whereClause = $4;
n->groupClause = ($5)->list;
n->groupDistinct = ($5)->distinct;
+ n->groupByAll = ($5)->all;
n->havingClause = $6;
n->windowClause = $7;
n->sortClause = $8;
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 9f20a70ce13..ca26f6f61f2 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -2598,6 +2598,9 @@ transformGroupingSet(List **flatresult,
* GROUP BY items will be added to the targetlist (as resjunk columns)
* if not already present, so the targetlist must be passed by reference.
*
+ * If GROUP BY ALL is specified, the groupClause will be inferred to be all
+ * non-aggregate, non-window expressions in the targetlist.
+ *
* This is also used for window PARTITION BY clauses (which act almost the
* same, but are always interpreted per SQL99 rules).
*
@@ -2622,6 +2625,7 @@ transformGroupingSet(List **flatresult,
*
* pstate ParseState
* grouplist clause to transform
+ * groupByAll is this a GROUP BY ALL statement?
* groupingSets reference to list to contain the grouping set tree
* targetlist reference to TargetEntry list
* sortClause ORDER BY clause (SortGroupClause nodes)
@@ -2629,7 +2633,8 @@ transformGroupingSet(List **flatresult,
* useSQL99 SQL99 rather than SQL92 syntax
*/
List *
-transformGroupClause(ParseState *pstate, List *grouplist, List **groupingSets,
+transformGroupClause(ParseState *pstate, List *grouplist, bool groupByAll,
+ List **groupingSets,
List **targetlist, List *sortClause,
ParseExprKind exprKind, bool useSQL99)
{
@@ -2640,6 +2645,63 @@ transformGroupClause(ParseState *pstate, List *grouplist, List **groupingSets,
bool hasGroupingSets = false;
Bitmapset *seen_local = NULL;
+ /* Handle GROUP BY ALL */
+ if (groupByAll)
+ {
+ /* There cannot have been any explicit grouplist items */
+ Assert(grouplist == NIL);
+
+ /* Iterate over targets, adding acceptable ones to the result list */
+ foreach_ptr(TargetEntry, tle, *targetlist)
+ {
+ /* Ignore junk TLEs */
+ if (tle->resjunk)
+ continue;
+
+ /*
+ * TLEs containing aggregates are not okay to add to GROUP BY
+ * (compare checkTargetlistEntrySQL92). But the SQL standard
+ * directs us to skip them, so it's fine.
+ */
+ if (pstate->p_hasAggs &&
+ contain_aggs_of_level((Node *) tle->expr, 0))
+ continue;
+
+ /*
+ * Likewise, TLEs containing window functions are not okay to add
+ * to GROUP BY. At this writing, the SQL standard is silent on
+ * what to do with them, but by analogy to aggregates we'll just
+ * skip them.
+ */
+ if (pstate->p_hasWindowFuncs &&
+ contain_windowfuncs((Node *) tle->expr))
+ continue;
+
+ /*
+ * Otherwise, add the TLE to the result using default sort/group
+ * semantics. We specify the parse location as the TLE's
+ * location, despite the comment for addTargetToGroupList
+ * discouraging that. The only other thing we could point to is
+ * the ALL keyword, which seems unhelpful when there are multiple
+ * TLEs.
+ */
+ result = addTargetToGroupList(pstate, tle,
+ result, *targetlist,
+ exprLocation((Node *) tle->expr));
+ }
+
+ /* If we found any acceptable targets, we're done */
+ if (result != NIL)
+ return result;
+
+ /*
+ * Otherwise, the SQL standard says to treat it like "GROUP BY ()".
+ * Build a representation of that, and let the rest of this function
+ * handle it.
+ */
+ grouplist = list_make1(makeGroupingSet(GROUPING_SET_EMPTY, NIL, -1));
+ }
+
/*
* Recursively flatten implicit RowExprs. (Technically this is only needed
* for GROUP BY, per the syntax rules for grouping sets, but we do it
@@ -2818,6 +2880,7 @@ transformWindowDefinitions(ParseState *pstate,
true /* force SQL99 rules */ );
partitionClause = transformGroupClause(pstate,
windef->partitionClause,
+ false /* not GROUP BY ALL */ ,
NULL,
targetlist,
orderClause,
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index defcdaa8b34..c6d83d67b87 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -6186,7 +6186,9 @@ get_basic_select_query(Query *query, deparse_context *context)
save_ingroupby = context->inGroupBy;
context->inGroupBy = true;
- if (query->groupingSets == NIL)
+ if (query->groupByAll)
+ appendStringInfoString(buf, "ALL");
+ else if (query->groupingSets == NIL)
{
sep = "";
foreach(l, query->groupClause)