1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-21 16:02:15 +03:00

Fix planner and rewriter to follow SQL semantics for tables that are

mentioned in FROM but not elsewhere in the query: such tables should be
joined over anyway.  Aside from being more standards-compliant, this allows
removal of some very ugly hacks for COUNT(*) processing.  Also, allow
HAVING clause without aggregate functions, since SQL does.  Clean up
CREATE RULE statement-list syntax the same way Bruce just fixed the
main stmtmulti production.
CAUTION: addition of a field to RangeTblEntry nodes breaks stored rules;
you will have to initdb if you have any rules.
This commit is contained in:
Tom Lane
1999-10-07 04:23:24 +00:00
parent 4040fcfa78
commit 3eb1c82277
21 changed files with 408 additions and 390 deletions

@ -6,7 +6,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.59 1999/10/02 04:42:04 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.60 1999/10/07 04:23:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -818,12 +818,13 @@ ApplyRetrieveRule(Query *parsetree,
int rt_index,
int relation_level,
Relation relation,
bool relWasInJoinSet,
int *modified)
{
Query *rule_action = NULL;
Node *rule_qual;
List *rtable,
*rt,
*addedrtable,
*l;
int nothing,
rt_length;
@ -844,19 +845,23 @@ ApplyRetrieveRule(Query *parsetree,
nothing = TRUE;
rtable = copyObject(parsetree->rtable);
foreach(rt, rtable)
rt_length = length(rtable); /* original length, not counting rule */
addedrtable = copyObject(rule_action->rtable);
/* If the original rel wasn't in the join set, none of its spawn is.
* If it was, then leave the spawn's flags as they are.
*/
if (! relWasInJoinSet)
{
RangeTblEntry *rte = lfirst(rt);
/*
* this is to prevent add_missing_vars_to_base_rels() from adding
* a bogus entry to the new target list.
*/
rte->inFromCl = false;
foreach(l, addedrtable)
{
RangeTblEntry *rte = lfirst(l);
rte->inJoinSet = false;
}
}
rt_length = length(rtable);
rtable = nconc(rtable, copyObject(rule_action->rtable));
rtable = nconc(rtable, addedrtable);
parsetree->rtable = rtable;
/* FOR UPDATE of view... */
@ -1006,10 +1011,14 @@ fireRIRrules(Query *parsetree)
RuleLock *rules;
RewriteRule *rule;
RewriteRule RIRonly;
bool relWasInJoinSet;
int modified = false;
int i;
List *l;
/* don't try to convert this into a foreach loop, because
* rtable list can get changed each time through...
*/
rt_index = 0;
while (rt_index < length(parsetree->rtable))
{
@ -1017,46 +1026,57 @@ fireRIRrules(Query *parsetree)
rte = nth(rt_index - 1, parsetree->rtable);
if (!rangeTableEntry_used((Node *) parsetree, rt_index, 0))
/*
* If the table is not one named in the original FROM clause
* then it must be referenced in the query, or we ignore it.
* This prevents infinite expansion loop due to new rtable
* entries inserted by expansion of a rule.
*/
if (! rte->inFromCl && rt_index != parsetree->resultRelation &&
! rangeTableEntry_used((Node *) parsetree, rt_index, 0))
{
/*
* Unused range table entries must not be marked as coming
* from a clause. Otherwise the planner will generate joins
* over relations that in fact shouldn't be scanned at all and
* the result will contain duplicates
*
* Jan
*
*/
rte->inFromCl = FALSE;
/* Make sure the planner ignores it too... */
rte->inJoinSet = false;
continue;
}
rel = heap_openr(rte->relname, AccessShareLock);
if (rel->rd_rules == NULL)
rules = rel->rd_rules;
if (rules == NULL)
{
heap_close(rel, AccessShareLock);
continue;
}
rules = rel->rd_rules;
locks = NIL;
relWasInJoinSet = rte->inJoinSet; /* save before possibly clearing */
/*
* Collect the RIR rules that we must apply
*/
locks = NIL;
for (i = 0; i < rules->numLocks; i++)
{
rule = rules->rules[i];
if (rule->event != CMD_SELECT)
continue;
if (rule->attrno > 0 &&
!attribute_used((Node *) parsetree,
rt_index,
rule->attrno, 0))
continue;
if (rule->attrno > 0)
{
/* per-attr rule; do we need it? */
if (! attribute_used((Node *) parsetree,
rt_index,
rule->attrno, 0))
continue;
}
else
{
/* Rel-wide ON SELECT DO INSTEAD means this is a view.
* Remove the view from the planner's join target set,
* or we'll get no rows out because view itself is empty!
*/
if (rule->isInstead)
rte->inJoinSet = false;
}
locks = lappend(locks, rule);
}
@ -1083,6 +1103,7 @@ fireRIRrules(Query *parsetree)
rt_index,
RIRonly.attrno == -1,
rel,
relWasInJoinSet,
&modified);
}
@ -2012,10 +2033,10 @@ Except_Intersect_Rewrite(Query *parsetree)
* If the Select Query node has aggregates in use add all the
* subselects to the HAVING qual else to the WHERE qual
*/
if (intersect_node->hasAggs == false)
AddQual(intersect_node, (Node *) n);
else
if (intersect_node->hasAggs)
AddHavingQual(intersect_node, (Node *) n);
else
AddQual(intersect_node, (Node *) n);
/* Now we got sublinks */
intersect_node->hasSubLinks = true;