aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/catalog/dependency.c17
-rw-r--r--src/backend/catalog/pg_constraint.c111
-rw-r--r--src/backend/nodes/copyfuncs.c3
-rw-r--r--src/backend/nodes/equalfuncs.c3
-rw-r--r--src/backend/nodes/outfuncs.c3
-rw-r--r--src/backend/nodes/readfuncs.c3
-rw-r--r--src/backend/parser/parse_agg.c63
7 files changed, 184 insertions, 19 deletions
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 491c402a03b..aeffbf4d74a 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.96 2010/02/26 02:00:36 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.97 2010/08/07 02:44:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1594,7 +1594,7 @@ find_expr_references_walker(Node *node,
{
/* Recurse into RTE subquery or not-yet-planned sublink subquery */
Query *query = (Query *) node;
- ListCell *rtable;
+ ListCell *lc;
bool result;
/*
@@ -1604,9 +1604,9 @@ find_expr_references_walker(Node *node,
* of recursing into RTE_FUNCTION RTEs, subqueries, etc, so no need to
* do that here. But keep it from looking at join alias lists.)
*/
- foreach(rtable, query->rtable)
+ foreach(lc, query->rtable)
{
- RangeTblEntry *rte = (RangeTblEntry *) lfirst(rtable);
+ RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
ListCell *ct;
switch (rte->rtekind)
@@ -1627,6 +1627,15 @@ find_expr_references_walker(Node *node,
}
}
+ /*
+ * Add dependencies on constraints listed in query's constraintDeps
+ */
+ foreach(lc, query->constraintDeps)
+ {
+ add_object_address(OCLASS_CONSTRAINT, lfirst_oid(lc), 0,
+ context->addrs);
+ }
+
/* query_tree_walker ignores ORDER BY etc, but we need those opers */
find_expr_references_walker((Node *) query->sortClause, context);
find_expr_references_walker((Node *) query->groupClause, context);
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 4fb96d21002..106b8df6e4c 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.54 2010/08/05 15:25:35 rhaas Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.55 2010/08/07 02:44:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -783,3 +783,112 @@ get_constraint_oid(Oid relid, const char *conname, bool missing_ok)
return conOid;
}
+
+/*
+ * Determine whether a relation can be proven functionally dependent on
+ * a set of grouping columns. If so, return TRUE and add the pg_constraint
+ * OIDs of the constraints needed for the proof to the *constraintDeps list.
+ *
+ * grouping_columns is a list of grouping expressions, in which columns of
+ * the rel of interest are Vars with the indicated varno/varlevelsup.
+ *
+ * Currently we only check to see if the rel has a primary key that is a
+ * subset of the grouping_columns. We could also use plain unique constraints
+ * if all their columns are known not null, but there's a problem: we need
+ * to be able to represent the not-null-ness as part of the constraints added
+ * to *constraintDeps. FIXME whenever not-null constraints get represented
+ * in pg_constraint.
+ */
+bool
+check_functional_grouping(Oid relid,
+ Index varno, Index varlevelsup,
+ List *grouping_columns,
+ List **constraintDeps)
+{
+ bool result = false;
+ Relation pg_constraint;
+ HeapTuple tuple;
+ SysScanDesc scan;
+ ScanKeyData skey[1];
+
+ /* Scan pg_constraint for constraints of the target rel */
+ pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_constraint_conrelid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(relid));
+
+ scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true,
+ SnapshotNow, 1, skey);
+
+ while (HeapTupleIsValid(tuple = systable_getnext(scan)))
+ {
+ Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
+ Datum adatum;
+ bool isNull;
+ ArrayType *arr;
+ int16 *attnums;
+ int numkeys;
+ int i;
+ bool found_col;
+
+ /* Only PK constraints are of interest for now, see comment above */
+ if (con->contype != CONSTRAINT_PRIMARY)
+ continue;
+
+ /* Extract the conkey array, ie, attnums of PK's columns */
+ adatum = heap_getattr(tuple, Anum_pg_constraint_conkey,
+ RelationGetDescr(pg_constraint), &isNull);
+ if (isNull)
+ elog(ERROR, "null conkey for constraint %u",
+ HeapTupleGetOid(tuple));
+ arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
+ numkeys = ARR_DIMS(arr)[0];
+ if (ARR_NDIM(arr) != 1 ||
+ numkeys < 0 ||
+ ARR_HASNULL(arr) ||
+ ARR_ELEMTYPE(arr) != INT2OID)
+ elog(ERROR, "conkey is not a 1-D smallint array");
+ attnums = (int16 *) ARR_DATA_PTR(arr);
+
+ found_col = false;
+ for (i = 0; i < numkeys; i++)
+ {
+ AttrNumber attnum = attnums[i];
+ ListCell *gl;
+
+ found_col = false;
+ foreach(gl, grouping_columns)
+ {
+ Var *gvar = (Var *) lfirst(gl);
+
+ if (IsA(gvar, Var) &&
+ gvar->varno == varno &&
+ gvar->varlevelsup == varlevelsup &&
+ gvar->varattno == attnum)
+ {
+ found_col = true;
+ break;
+ }
+ }
+ if (!found_col)
+ break;
+ }
+
+ if (found_col)
+ {
+ /* The PK is a subset of grouping_columns, so we win */
+ *constraintDeps = lappend_oid(*constraintDeps,
+ HeapTupleGetOid(tuple));
+ result = true;
+ break;
+ }
+ }
+
+ systable_endscan(scan);
+
+ heap_close(pg_constraint, AccessShareLock);
+
+ return result;
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index a5862771a90..54af8b62cac 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.466 2010/07/25 23:21:21 rhaas Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.467 2010/08/07 02:44:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -2272,6 +2272,7 @@ _copyQuery(Query *from)
COPY_NODE_FIELD(limitCount);
COPY_NODE_FIELD(rowMarks);
COPY_NODE_FIELD(setOperations);
+ COPY_NODE_FIELD(constraintDeps);
return newnode;
}
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 7056287c93b..0cb87015b6e 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -22,7 +22,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.386 2010/07/25 23:21:21 rhaas Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.387 2010/08/07 02:44:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -877,6 +877,7 @@ _equalQuery(Query *a, Query *b)
COMPARE_NODE_FIELD(limitCount);
COMPARE_NODE_FIELD(rowMarks);
COMPARE_NODE_FIELD(setOperations);
+ COMPARE_NODE_FIELD(constraintDeps);
return true;
}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 6089ea3e3aa..8c694350950 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.387 2010/07/25 23:21:21 rhaas Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.388 2010/08/07 02:44:06 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
@@ -2020,6 +2020,7 @@ _outQuery(StringInfo str, Query *node)
WRITE_NODE_FIELD(limitCount);
WRITE_NODE_FIELD(rowMarks);
WRITE_NODE_FIELD(setOperations);
+ WRITE_NODE_FIELD(constraintDeps);
}
static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index f28191d2d00..4d85d676979 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.232 2010/02/16 22:34:43 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.233 2010/08/07 02:44:07 tgl Exp $
*
* NOTES
* Path and Plan nodes do not have any readfuncs support, because we
@@ -218,6 +218,7 @@ _readQuery(void)
READ_NODE_FIELD(limitCount);
READ_NODE_FIELD(rowMarks);
READ_NODE_FIELD(setOperations);
+ READ_NODE_FIELD(constraintDeps);
READ_DONE();
}
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index c36c5fb08c6..a7c34bc8930 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -8,12 +8,13 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_agg.c,v 1.93 2010/03/17 16:52:38 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_agg.c,v 1.94 2010/08/07 02:44:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
+#include "catalog/pg_constraint.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/tlist.h"
@@ -29,13 +30,16 @@
typedef struct
{
ParseState *pstate;
+ Query *qry;
List *groupClauses;
bool have_non_var_grouping;
+ List **func_grouped_rels;
int sublevels_up;
} check_ungrouped_columns_context;
-static void check_ungrouped_columns(Node *node, ParseState *pstate,
- List *groupClauses, bool have_non_var_grouping);
+static void check_ungrouped_columns(Node *node, ParseState *pstate, Query *qry,
+ List *groupClauses, bool have_non_var_grouping,
+ List **func_grouped_rels);
static bool check_ungrouped_columns_walker(Node *node,
check_ungrouped_columns_context *context);
@@ -293,6 +297,7 @@ parseCheckAggregates(ParseState *pstate, Query *qry)
{
List *groupClauses = NIL;
bool have_non_var_grouping;
+ List *func_grouped_rels = NIL;
ListCell *l;
bool hasJoinRTEs;
bool hasSelfRefRTEs;
@@ -408,14 +413,16 @@ parseCheckAggregates(ParseState *pstate, Query *qry)
clause = (Node *) qry->targetList;
if (hasJoinRTEs)
clause = flatten_join_alias_vars(root, clause);
- check_ungrouped_columns(clause, pstate,
- groupClauses, have_non_var_grouping);
+ check_ungrouped_columns(clause, pstate, qry,
+ groupClauses, have_non_var_grouping,
+ &func_grouped_rels);
clause = (Node *) qry->havingQual;
if (hasJoinRTEs)
clause = flatten_join_alias_vars(root, clause);
- check_ungrouped_columns(clause, pstate,
- groupClauses, have_non_var_grouping);
+ check_ungrouped_columns(clause, pstate, qry,
+ groupClauses, have_non_var_grouping,
+ &func_grouped_rels);
/*
* Per spec, aggregates can't appear in a recursive term.
@@ -535,14 +542,17 @@ parseCheckWindowFuncs(ParseState *pstate, Query *qry)
* way more pain than the feature seems worth.
*/
static void
-check_ungrouped_columns(Node *node, ParseState *pstate,
- List *groupClauses, bool have_non_var_grouping)
+check_ungrouped_columns(Node *node, ParseState *pstate, Query *qry,
+ List *groupClauses, bool have_non_var_grouping,
+ List **func_grouped_rels)
{
check_ungrouped_columns_context context;
context.pstate = pstate;
+ context.qry = qry;
context.groupClauses = groupClauses;
context.have_non_var_grouping = have_non_var_grouping;
+ context.func_grouped_rels = func_grouped_rels;
context.sublevels_up = 0;
check_ungrouped_columns_walker(node, &context);
}
@@ -619,10 +629,43 @@ check_ungrouped_columns_walker(Node *node,
}
}
- /* Found an ungrouped local variable; generate error message */
+ /*
+ * Check whether the Var is known functionally dependent on the GROUP
+ * BY columns. If so, we can allow the Var to be used, because the
+ * grouping is really a no-op for this table. However, this deduction
+ * depends on one or more constraints of the table, so we have to add
+ * those constraints to the query's constraintDeps list, because it's
+ * not semantically valid anymore if the constraint(s) get dropped.
+ * (Therefore, this check must be the last-ditch effort before raising
+ * error: we don't want to add dependencies unnecessarily.)
+ *
+ * Because this is a pretty expensive check, and will have the same
+ * outcome for all columns of a table, we remember which RTEs we've
+ * already proven functional dependency for in the func_grouped_rels
+ * list. This test also prevents us from adding duplicate entries
+ * to the constraintDeps list.
+ */
+ if (list_member_int(*context->func_grouped_rels, var->varno))
+ return false; /* previously proven acceptable */
+
Assert(var->varno > 0 &&
(int) var->varno <= list_length(context->pstate->p_rtable));
rte = rt_fetch(var->varno, context->pstate->p_rtable);
+ if (rte->rtekind == RTE_RELATION)
+ {
+ if (check_functional_grouping(rte->relid,
+ var->varno,
+ 0,
+ context->groupClauses,
+ &context->qry->constraintDeps))
+ {
+ *context->func_grouped_rels =
+ lappend_int(*context->func_grouped_rels, var->varno);
+ return false; /* acceptable */
+ }
+ }
+
+ /* Found an ungrouped local variable; generate error message */
attname = get_rte_attribute_name(rte, var->varattno);
if (context->sublevels_up == 0)
ereport(ERROR,