1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* orindxpath.c
|
2004-01-04 01:07:32 +01:00
|
|
|
* Routines to find index paths that match a set of OR clauses
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2004-12-31 23:04:05 +01:00
|
|
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2005-06-06 00:32:58 +02:00
|
|
|
* $PostgreSQL: pgsql/src/backend/optimizer/path/orindxpath.c,v 1.71 2005/06/05 22:32:55 tgl Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
2000-02-05 19:26:09 +01:00
|
|
|
#include "postgres.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "optimizer/cost.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "optimizer/paths.h"
|
2005-04-25 03:30:14 +02:00
|
|
|
#include "optimizer/planmain.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "optimizer/restrictinfo.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
|
2004-01-05 06:07:36 +01:00
|
|
|
/*----------
|
|
|
|
* create_or_index_quals
|
|
|
|
* Examine join OR-of-AND quals to see if any useful restriction OR
|
|
|
|
* clauses can be extracted. If so, add them to the query.
|
|
|
|
*
|
|
|
|
* Although a join clause must reference other relations overall,
|
|
|
|
* an OR of ANDs clause might contain sub-clauses that reference just this
|
|
|
|
* relation and can be used to build a restriction clause.
|
|
|
|
* For example consider
|
|
|
|
* WHERE ((a.x = 42 AND b.y = 43) OR (a.x = 44 AND b.z = 45));
|
|
|
|
* We can transform this into
|
|
|
|
* WHERE ((a.x = 42 AND b.y = 43) OR (a.x = 44 AND b.z = 45))
|
|
|
|
* AND (a.x = 42 OR a.x = 44)
|
|
|
|
* AND (b.y = 43 OR b.z = 45);
|
|
|
|
* which opens the potential to build OR indexscans on a and b. In essence
|
|
|
|
* this is a partial transformation to CNF (AND of ORs format). It is not
|
|
|
|
* complete, however, because we do not unravel the original OR --- doing so
|
|
|
|
* would usually bloat the qualification expression to little gain.
|
|
|
|
*
|
|
|
|
* The added quals are partially redundant with the original OR, and therefore
|
|
|
|
* will cause the size of the joinrel to be underestimated when it is finally
|
2004-08-29 07:07:03 +02:00
|
|
|
* formed. (This would be true of a full transformation to CNF as well; the
|
2004-01-05 06:07:36 +01:00
|
|
|
* fault is not really in the transformation, but in clauselist_selectivity's
|
|
|
|
* inability to recognize redundant conditions.) To minimize the collateral
|
|
|
|
* damage, we want to minimize the number of quals added. Therefore we do
|
|
|
|
* not add every possible extracted restriction condition to the query.
|
|
|
|
* Instead, we search for the single restriction condition that generates
|
|
|
|
* the most useful (cheapest) OR indexscan, and add only that condition.
|
|
|
|
* This is a pretty ad-hoc heuristic, but quite useful.
|
|
|
|
*
|
|
|
|
* We can then compensate for the redundancy of the added qual by poking
|
|
|
|
* the recorded selectivity of the original OR clause, thereby ensuring
|
|
|
|
* the added qual doesn't change the estimated size of the joinrel when
|
|
|
|
* it is finally formed. This is a MAJOR HACK: it depends on the fact
|
|
|
|
* that clause selectivities are cached and on the fact that the same
|
|
|
|
* RestrictInfo node will appear in every joininfo list that might be used
|
2004-08-29 07:07:03 +02:00
|
|
|
* when the joinrel is formed. And it probably isn't right in cases where
|
2004-01-05 06:07:36 +01:00
|
|
|
* the size estimation is nonlinear (i.e., outer and IN joins). But it
|
|
|
|
* beats not doing anything.
|
|
|
|
*
|
|
|
|
* NOTE: one might think this messiness could be worked around by generating
|
|
|
|
* the indexscan path with a small path->rows value, and not touching the
|
|
|
|
* rel's baserestrictinfo or rel->rows. However, that does not work.
|
|
|
|
* The optimizer's fundamental design assumes that every general-purpose
|
|
|
|
* Path for a given relation generates the same number of rows. Without
|
|
|
|
* this assumption we'd not be able to optimize solely on the cost of Paths,
|
|
|
|
* but would have to take number of output rows into account as well.
|
|
|
|
* (Perhaps someday that'd be worth doing, but it's a pretty big change...)
|
|
|
|
*
|
|
|
|
* 'rel' is the relation entry for which quals are to be created
|
|
|
|
*
|
|
|
|
* If successful, adds qual(s) to rel->baserestrictinfo and returns TRUE.
|
|
|
|
* If no quals available, returns FALSE and doesn't change rel.
|
|
|
|
*
|
|
|
|
* Note: check_partial_indexes() must have been run previously.
|
|
|
|
*----------
|
|
|
|
*/
|
|
|
|
bool
|
2005-06-06 00:32:58 +02:00
|
|
|
create_or_index_quals(PlannerInfo *root, RelOptInfo *rel)
|
2004-01-05 06:07:36 +01:00
|
|
|
{
|
2005-04-25 03:30:14 +02:00
|
|
|
BitmapOrPath *bestpath = NULL;
|
2004-01-05 06:07:36 +01:00
|
|
|
RestrictInfo *bestrinfo = NULL;
|
2004-01-06 00:39:54 +01:00
|
|
|
List *newrinfos;
|
2004-01-05 06:07:36 +01:00
|
|
|
RestrictInfo *or_rinfo;
|
|
|
|
Selectivity or_selec,
|
|
|
|
orig_selec;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *i;
|
2004-01-05 06:07:36 +01:00
|
|
|
|
|
|
|
/*
|
2005-04-25 03:30:14 +02:00
|
|
|
* Find potentially interesting OR joinclauses. We must ignore any
|
2004-08-29 07:07:03 +02:00
|
|
|
* joinclauses that are not marked valid_everywhere, because they
|
2004-01-05 06:07:36 +01:00
|
|
|
* cannot be pushed down due to outer-join rules.
|
|
|
|
*/
|
|
|
|
foreach(i, rel->joininfo)
|
|
|
|
{
|
|
|
|
JoinInfo *joininfo = (JoinInfo *) lfirst(i);
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *j;
|
2004-01-05 06:07:36 +01:00
|
|
|
|
|
|
|
foreach(j, joininfo->jinfo_restrictinfo)
|
|
|
|
{
|
|
|
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(j);
|
|
|
|
|
|
|
|
if (restriction_is_or_clause(rinfo) &&
|
|
|
|
rinfo->valid_everywhere)
|
|
|
|
{
|
2005-04-25 03:30:14 +02:00
|
|
|
/*
|
|
|
|
* Use the generate_bitmap_or_paths() machinery to estimate
|
|
|
|
* the value of each OR clause. We can use regular
|
|
|
|
* restriction clauses along with the OR clause contents to
|
|
|
|
* generate indexquals. We pass outer_relids = NULL so that
|
|
|
|
* sub-clauses that are actually joins will be ignored.
|
|
|
|
*/
|
|
|
|
List *orpaths;
|
|
|
|
ListCell *k;
|
|
|
|
|
|
|
|
orpaths = generate_bitmap_or_paths(root, rel,
|
|
|
|
list_make1(rinfo),
|
|
|
|
rel->baserestrictinfo,
|
|
|
|
false, NULL);
|
|
|
|
|
|
|
|
/* Locate the cheapest OR path */
|
|
|
|
foreach(k, orpaths)
|
2004-01-05 06:07:36 +01:00
|
|
|
{
|
2005-04-25 03:30:14 +02:00
|
|
|
BitmapOrPath *path = (BitmapOrPath *) lfirst(k);
|
|
|
|
|
|
|
|
Assert(IsA(path, BitmapOrPath));
|
2004-01-05 06:07:36 +01:00
|
|
|
if (bestpath == NULL ||
|
2005-04-25 03:30:14 +02:00
|
|
|
path->path.total_cost < bestpath->path.total_cost)
|
2004-01-05 06:07:36 +01:00
|
|
|
{
|
2005-04-25 03:30:14 +02:00
|
|
|
bestpath = path;
|
2004-01-05 06:07:36 +01:00
|
|
|
bestrinfo = rinfo;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Fail if no suitable clauses found */
|
|
|
|
if (bestpath == NULL)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
2005-04-25 03:30:14 +02:00
|
|
|
* Convert the path's indexclauses structure to a RestrictInfo tree,
|
|
|
|
* and add it to the rel's restriction list.
|
2004-01-05 06:07:36 +01:00
|
|
|
*/
|
2005-04-25 04:14:48 +02:00
|
|
|
newrinfos = make_restrictinfo_from_bitmapqual((Path *) bestpath,
|
|
|
|
true, true);
|
2004-05-31 01:40:41 +02:00
|
|
|
Assert(list_length(newrinfos) == 1);
|
2004-05-26 06:41:50 +02:00
|
|
|
or_rinfo = (RestrictInfo *) linitial(newrinfos);
|
2005-04-25 03:30:14 +02:00
|
|
|
Assert(IsA(or_rinfo, RestrictInfo));
|
|
|
|
|
2004-05-31 01:40:41 +02:00
|
|
|
rel->baserestrictinfo = list_concat(rel->baserestrictinfo, newrinfos);
|
2004-01-05 06:07:36 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Adjust the original OR clause's cached selectivity to compensate
|
|
|
|
* for the selectivity of the added (but redundant) lower-level qual.
|
|
|
|
* This should result in the join rel getting approximately the same
|
2004-08-29 07:07:03 +02:00
|
|
|
* rows estimate as it would have gotten without all these
|
|
|
|
* shenanigans. (XXX major hack alert ... this depends on the
|
|
|
|
* assumption that the selectivity will stay cached ...)
|
2004-01-05 06:07:36 +01:00
|
|
|
*/
|
|
|
|
or_selec = clause_selectivity(root, (Node *) or_rinfo,
|
|
|
|
0, JOIN_INNER);
|
|
|
|
if (or_selec > 0 && or_selec < 1)
|
|
|
|
{
|
|
|
|
orig_selec = clause_selectivity(root, (Node *) bestrinfo,
|
|
|
|
0, JOIN_INNER);
|
|
|
|
bestrinfo->this_selec = orig_selec / or_selec;
|
|
|
|
/* clamp result to sane range */
|
|
|
|
if (bestrinfo->this_selec > 1)
|
|
|
|
bestrinfo->this_selec = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Tell caller to recompute rel's rows estimate */
|
|
|
|
return true;
|
|
|
|
}
|