aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2009-11-16 18:04:47 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2009-11-16 18:04:47 +0000
commit1ec65488c08e7b8681b37e42eb056ea3e8cef382 (patch)
tree4863c1b824a6e11811d56287108e86baccea3be9 /src
parent4272c8724fb065b54e34007de5bd82996c0be692 (diff)
downloadpostgresql-1ec65488c08e7b8681b37e42eb056ea3e8cef382.tar.gz
postgresql-1ec65488c08e7b8681b37e42eb056ea3e8cef382.zip
While doing the final setrefs.c pass over a plan tree, try to match up
non-Var sort/group expressions using ressortgroupref labels instead of depending entirely on equal()-ity of the upper node's tlist expressions to the lower node's. This avoids emitting the wrong outputs in cases where there are textually identical volatile sort/group expressions, as for example select distinct random(),random() from generate_series(1,10); Per report from Andrew Gierth. Backpatch to 8.4. Arguably this is wrong all the way back, but the only known case where there's an observable problem is when using hash aggregation to implement DISTINCT, which is new as of 8.4. So for the moment I'll refrain from backpatching further.
Diffstat (limited to 'src')
-rw-r--r--src/backend/optimizer/plan/setrefs.c72
1 files changed, 67 insertions, 5 deletions
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 11e14f96c55..e006633b397 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.150 2009/06/11 14:48:59 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.150.2.1 2009/11/16 18:04:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -101,6 +101,10 @@ static Var *search_indexed_tlist_for_var(Var *var,
static Var *search_indexed_tlist_for_non_var(Node *node,
indexed_tlist *itlist,
Index newvarno);
+static Var *search_indexed_tlist_for_sortgroupref(Node *node,
+ Index sortgroupref,
+ indexed_tlist *itlist,
+ Index newvarno);
static List *fix_join_expr(PlannerGlobal *glob,
List *clauses,
indexed_tlist *outer_itlist,
@@ -1119,10 +1123,25 @@ set_upper_references(PlannerGlobal *glob, Plan *plan, int rtoffset)
TargetEntry *tle = (TargetEntry *) lfirst(l);
Node *newexpr;
- newexpr = fix_upper_expr(glob,
- (Node *) tle->expr,
- subplan_itlist,
- rtoffset);
+ /* If it's a non-Var sort/group item, first try to match by sortref */
+ if (tle->ressortgroupref != 0 && !IsA(tle->expr, Var))
+ {
+ newexpr = (Node *)
+ search_indexed_tlist_for_sortgroupref((Node *) tle->expr,
+ tle->ressortgroupref,
+ subplan_itlist,
+ OUTER);
+ if (!newexpr)
+ newexpr = fix_upper_expr(glob,
+ (Node *) tle->expr,
+ subplan_itlist,
+ rtoffset);
+ }
+ else
+ newexpr = fix_upper_expr(glob,
+ (Node *) tle->expr,
+ subplan_itlist,
+ rtoffset);
tle = flatCopyTargetEntry(tle);
tle->expr = (Expr *) newexpr;
output_targetlist = lappend(output_targetlist, tle);
@@ -1367,6 +1386,49 @@ search_indexed_tlist_for_non_var(Node *node,
}
/*
+ * search_indexed_tlist_for_sortgroupref --- find a sort/group expression
+ * (which is assumed not to be just a Var)
+ *
+ * If a match is found, return a Var constructed to reference the tlist item.
+ * If no match, return NULL.
+ *
+ * This is needed to ensure that we select the right subplan TLE in cases
+ * where there are multiple textually-equal()-but-volatile sort expressions.
+ * And it's also faster than search_indexed_tlist_for_non_var.
+ */
+static Var *
+search_indexed_tlist_for_sortgroupref(Node *node,
+ Index sortgroupref,
+ indexed_tlist *itlist,
+ Index newvarno)
+{
+ ListCell *lc;
+
+ foreach(lc, itlist->tlist)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(lc);
+
+ /* The equal() check should be redundant, but let's be paranoid */
+ if (tle->ressortgroupref == sortgroupref &&
+ equal(node, tle->expr))
+ {
+ /* Found a matching subplan output expression */
+ Var *newvar;
+
+ newvar = makeVar(newvarno,
+ tle->resno,
+ exprType((Node *) tle->expr),
+ exprTypmod((Node *) tle->expr),
+ 0);
+ newvar->varnoold = 0; /* wasn't ever a plain Var */
+ newvar->varoattno = 0;
+ return newvar;
+ }
+ }
+ return NULL; /* no match */
+}
+
+/*
* fix_join_expr
* Create a new set of targetlist entries or join qual clauses by
* changing the varno/varattno values of variables in the clauses