1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* indxpath.c
|
2005-04-22 23:58:32 +02:00
|
|
|
* Routines to determine which indexes are usable for scanning a
|
|
|
|
* given relation, and create Paths accordingly.
|
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-14 01:14:49 +02:00
|
|
|
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.184 2005/06/13 23:14:48 tgl Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
1998-04-27 06:08:07 +02:00
|
|
|
|
2000-04-16 06:41:03 +02:00
|
|
|
#include <math.h>
|
|
|
|
|
2005-06-11 00:25:37 +02:00
|
|
|
#include "access/skey.h"
|
2003-05-26 02:11:29 +02:00
|
|
|
#include "catalog/pg_opclass.h"
|
1999-07-27 05:51:11 +02:00
|
|
|
#include "catalog/pg_operator.h"
|
2003-05-13 06:38:58 +02:00
|
|
|
#include "catalog/pg_type.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "nodes/makefuncs.h"
|
|
|
|
#include "optimizer/clauses.h"
|
|
|
|
#include "optimizer/cost.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "optimizer/pathnode.h"
|
1998-04-27 06:08:07 +02:00
|
|
|
#include "optimizer/paths.h"
|
2005-06-11 00:25:37 +02:00
|
|
|
#include "optimizer/predtest.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "optimizer/restrictinfo.h"
|
1999-07-27 05:51:11 +02:00
|
|
|
#include "utils/builtins.h"
|
1998-04-27 06:08:07 +02:00
|
|
|
#include "utils/lsyscache.h"
|
2005-05-06 19:24:55 +02:00
|
|
|
#include "utils/memutils.h"
|
2003-05-15 21:34:46 +02:00
|
|
|
#include "utils/pg_locale.h"
|
2001-06-25 23:11:45 +02:00
|
|
|
#include "utils/selfuncs.h"
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-01-09 01:26:47 +01:00
|
|
|
|
2000-07-27 01:46:22 +02:00
|
|
|
/*
|
|
|
|
* DoneMatchingIndexKeys() - MACRO
|
|
|
|
*/
|
2003-05-28 18:04:02 +02:00
|
|
|
#define DoneMatchingIndexKeys(classes) (classes[0] == InvalidOid)
|
2000-07-27 01:46:22 +02:00
|
|
|
|
2001-08-21 18:36:06 +02:00
|
|
|
#define is_indexable_operator(clause,opclass,indexkey_on_left) \
|
|
|
|
(indexable_operator(clause,opclass,indexkey_on_left) != InvalidOid)
|
2000-01-09 01:26:47 +01:00
|
|
|
|
2005-03-27 00:29:20 +01:00
|
|
|
#define IsBooleanOpclass(opclass) \
|
|
|
|
((opclass) == BOOL_BTREE_OPS_OID || (opclass) == BOOL_HASH_OPS_OID)
|
|
|
|
|
2000-07-27 01:46:22 +02:00
|
|
|
|
2005-06-06 00:32:58 +02:00
|
|
|
static List *find_usable_indexes(PlannerInfo *root, RelOptInfo *rel,
|
2005-04-22 23:58:32 +02:00
|
|
|
List *clauses, List *outer_clauses,
|
|
|
|
bool istoplevel, bool isjoininner,
|
|
|
|
Relids outer_relids);
|
2005-06-06 00:32:58 +02:00
|
|
|
static Path *choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, List *paths);
|
2005-04-23 03:57:34 +02:00
|
|
|
static int bitmap_path_comparator(const void *a, const void *b);
|
2005-06-06 00:32:58 +02:00
|
|
|
static Cost bitmap_and_cost_est(PlannerInfo *root, RelOptInfo *rel, List *paths);
|
2005-03-27 08:29:49 +02:00
|
|
|
static bool match_clause_to_indexcol(IndexOptInfo *index,
|
2004-08-29 07:07:03 +02:00
|
|
|
int indexcol, Oid opclass,
|
2005-04-22 23:58:32 +02:00
|
|
|
RestrictInfo *rinfo,
|
|
|
|
Relids outer_relids);
|
2002-12-12 16:49:42 +01:00
|
|
|
static Oid indexable_operator(Expr *clause, Oid opclass,
|
|
|
|
bool indexkey_on_left);
|
2005-04-22 23:58:32 +02:00
|
|
|
static Relids indexable_outerrelids(RelOptInfo *rel);
|
|
|
|
static bool matches_any_index(RestrictInfo *rinfo, RelOptInfo *rel,
|
|
|
|
Relids outer_relids);
|
2005-06-06 00:32:58 +02:00
|
|
|
static List *find_clauses_for_join(PlannerInfo *root, RelOptInfo *rel,
|
2005-04-22 23:58:32 +02:00
|
|
|
Relids outer_relids, bool isouterjoin);
|
2005-03-27 08:29:49 +02:00
|
|
|
static bool match_boolean_index_clause(Node *clause, int indexcol,
|
2005-03-27 00:29:20 +01:00
|
|
|
IndexOptInfo *index);
|
2001-08-21 18:36:06 +02:00
|
|
|
static bool match_special_index_operator(Expr *clause, Oid opclass,
|
2000-04-12 19:17:23 +02:00
|
|
|
bool indexkey_on_left);
|
2005-03-27 08:29:49 +02:00
|
|
|
static Expr *expand_boolean_index_clause(Node *clause, int indexcol,
|
2005-03-27 00:29:20 +01:00
|
|
|
IndexOptInfo *index);
|
2004-01-06 00:39:54 +01:00
|
|
|
static List *expand_indexqual_condition(RestrictInfo *rinfo, Oid opclass);
|
2003-05-26 02:11:29 +02:00
|
|
|
static List *prefix_quals(Node *leftop, Oid opclass,
|
2002-09-02 08:22:20 +02:00
|
|
|
Const *prefix, Pattern_Prefix_Status pstatus);
|
2003-05-26 02:11:29 +02:00
|
|
|
static List *network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass,
|
2003-08-04 02:43:34 +02:00
|
|
|
Datum rightop);
|
2000-04-12 19:17:23 +02:00
|
|
|
static Datum string_to_datum(const char *str, Oid datatype);
|
|
|
|
static Const *string_to_const(const char *str, Oid datatype);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
|
1999-07-23 05:34:49 +02:00
|
|
|
/*
|
|
|
|
* create_index_paths()
|
|
|
|
* Generate all interesting index paths for the given relation.
|
2000-02-15 21:49:31 +01:00
|
|
|
* Candidate paths are added to the rel's pathlist (using add_path).
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1999-07-30 06:07:25 +02:00
|
|
|
* To be considered for an index scan, an index must match one or more
|
1999-08-21 05:49:17 +02:00
|
|
|
* restriction clauses or join clauses from the query's qual condition,
|
2005-06-14 01:14:49 +02:00
|
|
|
* or match the query's ORDER BY condition, or have a predicate that
|
|
|
|
* matches the query's qual condition.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1999-07-30 06:07:25 +02:00
|
|
|
* There are two basic kinds of index scans. A "plain" index scan uses
|
|
|
|
* only restriction clauses (possibly none at all) in its indexqual,
|
|
|
|
* so it can be applied in any context. An "innerjoin" index scan uses
|
|
|
|
* join clauses (plus restriction clauses, if available) in its indexqual.
|
|
|
|
* Therefore it can only be used as the inner relation of a nestloop
|
|
|
|
* join against an outer rel that includes all the other rels mentioned
|
|
|
|
* in its join clauses. In that context, values for the other rels'
|
|
|
|
* attributes are available and fixed during any one scan of the indexpath.
|
|
|
|
*
|
2002-11-24 22:52:15 +01:00
|
|
|
* An IndexPath is generated and submitted to add_path() for each plain index
|
|
|
|
* scan this routine deems potentially interesting for the current query.
|
|
|
|
*
|
|
|
|
* We also determine the set of other relids that participate in join
|
2003-08-04 02:43:34 +02:00
|
|
|
* clauses that could be used with each index. The actually best innerjoin
|
2002-11-24 22:52:15 +01:00
|
|
|
* path will be generated for each outer relation later on, but knowing the
|
|
|
|
* set of potential otherrels allows us to identify equivalent outer relations
|
|
|
|
* and avoid repeated computation.
|
1999-07-30 06:07:25 +02:00
|
|
|
*
|
1999-07-23 05:34:49 +02:00
|
|
|
* 'rel' is the relation for which we want to generate index paths
|
2004-01-05 06:07:36 +01:00
|
|
|
*
|
|
|
|
* Note: check_partial_indexes() must have been run previously.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2000-02-15 21:49:31 +01:00
|
|
|
void
|
2005-06-06 00:32:58 +02:00
|
|
|
create_index_paths(PlannerInfo *root, RelOptInfo *rel)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2005-04-22 23:58:32 +02:00
|
|
|
List *indexpaths;
|
|
|
|
List *bitindexpaths;
|
|
|
|
ListCell *l;
|
|
|
|
|
|
|
|
/* Skip the whole mess if no indexes */
|
|
|
|
if (rel->indexlist == NIL)
|
|
|
|
{
|
|
|
|
rel->index_outer_relids = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Examine join clauses to see which ones are potentially usable with
|
|
|
|
* indexes of this rel, and generate the set of all other relids that
|
|
|
|
* participate in such join clauses. We'll use this set later to
|
|
|
|
* recognize outer rels that are equivalent for joining purposes.
|
|
|
|
*/
|
|
|
|
rel->index_outer_relids = indexable_outerrelids(rel);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find all the index paths that are directly usable for this relation
|
|
|
|
* (ie, are valid without considering OR or JOIN clauses).
|
|
|
|
*/
|
|
|
|
indexpaths = find_usable_indexes(root, rel,
|
|
|
|
rel->baserestrictinfo, NIL,
|
|
|
|
true, false, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We can submit them all to add_path. (This generates access paths for
|
|
|
|
* plain IndexScan plans.) However, for the next step we will only want
|
|
|
|
* the ones that have some selectivity; we must discard anything that was
|
|
|
|
* generated solely for ordering purposes.
|
|
|
|
*/
|
|
|
|
bitindexpaths = NIL;
|
|
|
|
foreach(l, indexpaths)
|
|
|
|
{
|
|
|
|
IndexPath *ipath = (IndexPath *) lfirst(l);
|
|
|
|
|
|
|
|
add_path(rel, (Path *) ipath);
|
|
|
|
|
|
|
|
if (ipath->indexselectivity < 1.0 &&
|
|
|
|
!ScanDirectionIsBackward(ipath->indexscandir))
|
|
|
|
bitindexpaths = lappend(bitindexpaths, ipath);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generate BitmapOrPaths for any suitable OR-clauses present in the
|
|
|
|
* restriction list. Add these to bitindexpaths.
|
|
|
|
*/
|
|
|
|
indexpaths = generate_bitmap_or_paths(root, rel,
|
|
|
|
rel->baserestrictinfo, NIL,
|
|
|
|
false, NULL);
|
|
|
|
bitindexpaths = list_concat(bitindexpaths, indexpaths);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we found anything usable, generate a BitmapHeapPath for the
|
|
|
|
* most promising combination of bitmap index paths.
|
|
|
|
*/
|
|
|
|
if (bitindexpaths != NIL)
|
|
|
|
{
|
|
|
|
Path *bitmapqual;
|
|
|
|
BitmapHeapPath *bpath;
|
|
|
|
|
|
|
|
bitmapqual = choose_bitmap_and(root, rel, bitindexpaths);
|
|
|
|
bpath = create_bitmap_heap_path(root, rel, bitmapqual, false);
|
|
|
|
add_path(rel, (Path *) bpath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*----------
|
|
|
|
* find_usable_indexes
|
|
|
|
* Given a list of restriction clauses, find all the potentially usable
|
|
|
|
* indexes for the given relation, and return a list of IndexPaths.
|
|
|
|
*
|
|
|
|
* The caller actually supplies two lists of restriction clauses: some
|
|
|
|
* "current" ones and some "outer" ones. Both lists can be used freely
|
|
|
|
* to match keys of the index, but an index must use at least one of the
|
|
|
|
* "current" clauses to be considered usable. The motivation for this is
|
|
|
|
* examples like
|
|
|
|
* WHERE (x = 42) AND (... OR (y = 52 AND z = 77) OR ....)
|
|
|
|
* While we are considering the y/z subclause of the OR, we can use "x = 42"
|
|
|
|
* as one of the available index conditions; but we shouldn't match the
|
|
|
|
* subclause to any index on x alone, because such a Path would already have
|
|
|
|
* been generated at the upper level. So we could use an index on x,y,z
|
|
|
|
* or an index on x,y for the OR subclause, but not an index on just x.
|
|
|
|
*
|
|
|
|
* If istoplevel is true (indicating we are considering the top level of a
|
|
|
|
* rel's restriction clauses), we will include indexes in the result that
|
|
|
|
* have an interesting sort order, even if they have no matching restriction
|
|
|
|
* clauses.
|
|
|
|
*
|
|
|
|
* 'rel' is the relation for which we want to generate index paths
|
|
|
|
* 'clauses' is the current list of clauses (RestrictInfo nodes)
|
|
|
|
* 'outer_clauses' is the list of additional upper-level clauses
|
|
|
|
* 'istoplevel' is true if clauses are the rel's top-level restriction list
|
2005-06-14 01:14:49 +02:00
|
|
|
* (outer_clauses must be NIL when this is true)
|
2005-04-22 23:58:32 +02:00
|
|
|
* 'isjoininner' is true if forming an inner indexscan (so some of the
|
|
|
|
* given clauses are join clauses)
|
|
|
|
* 'outer_relids' identifies the outer side of the join (pass NULL
|
|
|
|
* if not isjoininner)
|
|
|
|
*
|
|
|
|
* Note: check_partial_indexes() must have been run previously.
|
|
|
|
*----------
|
|
|
|
*/
|
|
|
|
static List *
|
2005-06-06 00:32:58 +02:00
|
|
|
find_usable_indexes(PlannerInfo *root, RelOptInfo *rel,
|
2005-04-22 23:58:32 +02:00
|
|
|
List *clauses, List *outer_clauses,
|
|
|
|
bool istoplevel, bool isjoininner,
|
|
|
|
Relids outer_relids)
|
|
|
|
{
|
|
|
|
List *result = NIL;
|
|
|
|
List *all_clauses = NIL; /* not computed till needed */
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *ilist;
|
1998-08-14 18:13:07 +02:00
|
|
|
|
2001-05-20 22:28:20 +02:00
|
|
|
foreach(ilist, rel->indexlist)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-01-09 01:26:47 +01:00
|
|
|
IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
|
2005-04-22 23:58:32 +02:00
|
|
|
IndexPath *ipath;
|
1999-07-30 06:07:25 +02:00
|
|
|
List *restrictclauses;
|
2000-12-14 23:30:45 +01:00
|
|
|
List *index_pathkeys;
|
|
|
|
List *useful_pathkeys;
|
|
|
|
bool index_is_ordered;
|
1998-08-14 18:13:07 +02:00
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
/*
|
|
|
|
* Ignore partial indexes that do not match the query. If a partial
|
|
|
|
* index is marked predOK then we know it's OK; otherwise, if we
|
|
|
|
* are at top level we know it's not OK (since predOK is exactly
|
|
|
|
* whether its predicate could be proven from the toplevel clauses).
|
|
|
|
* Otherwise, we have to test whether the added clauses are
|
|
|
|
* sufficient to imply the predicate. If so, we could use
|
|
|
|
* the index in the current context.
|
|
|
|
*/
|
2004-01-05 06:07:36 +01:00
|
|
|
if (index->indpred != NIL && !index->predOK)
|
2005-04-22 23:58:32 +02:00
|
|
|
{
|
|
|
|
if (istoplevel)
|
|
|
|
continue; /* no point in trying to prove it */
|
|
|
|
|
|
|
|
/* Form all_clauses if not done already */
|
|
|
|
if (all_clauses == NIL)
|
|
|
|
all_clauses = list_concat(list_copy(clauses),
|
|
|
|
outer_clauses);
|
|
|
|
|
2005-06-11 00:25:37 +02:00
|
|
|
if (!predicate_implied_by(index->indpred, all_clauses) ||
|
|
|
|
predicate_implied_by(index->indpred, outer_clauses))
|
2005-04-22 23:58:32 +02:00
|
|
|
continue;
|
|
|
|
}
|
1998-08-14 18:13:07 +02:00
|
|
|
|
1998-08-02 00:12:13 +02:00
|
|
|
/*
|
2005-04-22 23:58:32 +02:00
|
|
|
* 1. Match the index against the available restriction clauses.
|
1998-08-02 00:12:13 +02:00
|
|
|
*/
|
2005-04-22 23:58:32 +02:00
|
|
|
restrictclauses = group_clauses_by_indexkey(index,
|
|
|
|
clauses,
|
|
|
|
outer_clauses,
|
|
|
|
outer_relids);
|
1999-07-30 06:07:25 +02:00
|
|
|
|
1998-08-02 00:12:13 +02:00
|
|
|
/*
|
2004-01-04 01:07:32 +01:00
|
|
|
* 2. Compute pathkeys describing index's ordering, if any, then
|
2005-04-22 23:58:32 +02:00
|
|
|
* see how many of them are actually useful for this query. This
|
|
|
|
* is not relevant unless we are at top level.
|
1999-08-16 04:17:58 +02:00
|
|
|
*/
|
2005-04-20 23:48:04 +02:00
|
|
|
index_is_ordered = OidIsValid(index->ordering[0]);
|
2005-04-22 23:58:32 +02:00
|
|
|
if (istoplevel && index_is_ordered && !isjoininner)
|
2005-04-20 23:48:04 +02:00
|
|
|
{
|
|
|
|
index_pathkeys = build_index_pathkeys(root, index,
|
|
|
|
ForwardScanDirection);
|
|
|
|
useful_pathkeys = truncate_useless_pathkeys(root, rel,
|
|
|
|
index_pathkeys);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
useful_pathkeys = NIL;
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2000-02-15 21:49:31 +01:00
|
|
|
/*
|
2004-01-04 01:07:32 +01:00
|
|
|
* 3. Generate an indexscan path if there are relevant restriction
|
2000-12-14 23:30:45 +01:00
|
|
|
* clauses OR the index ordering is potentially useful for later
|
|
|
|
* merging or final output ordering.
|
2001-07-16 07:07:00 +02:00
|
|
|
*
|
|
|
|
* If there is a predicate, consider it anyway since the index
|
2001-08-06 20:09:45 +02:00
|
|
|
* predicate has already been found to match the query. The
|
|
|
|
* selectivity of the predicate might alone make the index useful.
|
2005-04-20 23:48:04 +02:00
|
|
|
*
|
|
|
|
* Note: not all index AMs support scans with no restriction clauses.
|
2005-06-14 01:14:49 +02:00
|
|
|
* We can't generate a scan over an index with amoptionalkey = false
|
|
|
|
* unless there's at least one restriction clause.
|
2000-02-15 21:49:31 +01:00
|
|
|
*/
|
2001-07-16 07:07:00 +02:00
|
|
|
if (restrictclauses != NIL ||
|
2005-06-14 01:14:49 +02:00
|
|
|
(index->amoptionalkey &&
|
|
|
|
(useful_pathkeys != NIL || index->indpred != NIL)))
|
2005-04-22 23:58:32 +02:00
|
|
|
{
|
|
|
|
ipath = create_index_path(root, index,
|
|
|
|
restrictclauses,
|
|
|
|
useful_pathkeys,
|
|
|
|
index_is_ordered ?
|
|
|
|
ForwardScanDirection :
|
|
|
|
NoMovementScanDirection,
|
|
|
|
isjoininner);
|
|
|
|
result = lappend(result, ipath);
|
|
|
|
}
|
2000-12-14 23:30:45 +01:00
|
|
|
|
|
|
|
/*
|
2004-01-04 01:07:32 +01:00
|
|
|
* 4. If the index is ordered, a backwards scan might be
|
2001-03-22 05:01:46 +01:00
|
|
|
* interesting. Currently this is only possible for a DESC query
|
|
|
|
* result ordering.
|
2000-12-14 23:30:45 +01:00
|
|
|
*/
|
2005-04-22 23:58:32 +02:00
|
|
|
if (istoplevel && index_is_ordered && !isjoininner)
|
2000-12-14 23:30:45 +01:00
|
|
|
{
|
2005-03-27 08:29:49 +02:00
|
|
|
index_pathkeys = build_index_pathkeys(root, index,
|
2000-12-14 23:30:45 +01:00
|
|
|
BackwardScanDirection);
|
|
|
|
useful_pathkeys = truncate_useless_pathkeys(root, rel,
|
|
|
|
index_pathkeys);
|
|
|
|
if (useful_pathkeys != NIL)
|
2005-04-22 23:58:32 +02:00
|
|
|
{
|
|
|
|
ipath = create_index_path(root, index,
|
|
|
|
restrictclauses,
|
|
|
|
useful_pathkeys,
|
|
|
|
BackwardScanDirection,
|
|
|
|
false);
|
|
|
|
result = lappend(result, ipath);
|
|
|
|
}
|
2000-12-14 23:30:45 +01:00
|
|
|
}
|
2005-04-22 23:58:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-08-16 04:17:58 +02:00
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
/*
|
|
|
|
* generate_bitmap_or_paths
|
|
|
|
* Look through the list of clauses to find OR clauses, and generate
|
|
|
|
* a BitmapOrPath for each one we can handle that way. Return a list
|
|
|
|
* of the generated BitmapOrPaths.
|
|
|
|
*
|
|
|
|
* outer_clauses is a list of additional clauses that can be assumed true
|
|
|
|
* for the purpose of generating indexquals, but are not to be searched for
|
|
|
|
* ORs. (See find_usable_indexes() for motivation.)
|
|
|
|
*/
|
2005-04-25 03:30:14 +02:00
|
|
|
List *
|
2005-06-06 00:32:58 +02:00
|
|
|
generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel,
|
2005-04-22 23:58:32 +02:00
|
|
|
List *clauses, List *outer_clauses,
|
|
|
|
bool isjoininner,
|
|
|
|
Relids outer_relids)
|
|
|
|
{
|
|
|
|
List *result = NIL;
|
|
|
|
List *all_clauses;
|
|
|
|
ListCell *l;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We can use both the current and outer clauses as context for
|
|
|
|
* find_usable_indexes
|
|
|
|
*/
|
|
|
|
all_clauses = list_concat(list_copy(clauses), outer_clauses);
|
|
|
|
|
|
|
|
foreach(l, clauses)
|
|
|
|
{
|
|
|
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
|
|
|
|
List *pathlist;
|
|
|
|
Path *bitmapqual;
|
|
|
|
ListCell *j;
|
|
|
|
|
|
|
|
Assert(IsA(rinfo, RestrictInfo));
|
|
|
|
/* Ignore RestrictInfos that aren't ORs */
|
|
|
|
if (!restriction_is_or_clause(rinfo))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We must be able to match at least one index to each of the arms
|
|
|
|
* of the OR, else we can't use it.
|
|
|
|
*/
|
|
|
|
pathlist = NIL;
|
|
|
|
foreach(j, ((BoolExpr *) rinfo->orclause)->args)
|
|
|
|
{
|
|
|
|
Node *orarg = (Node *) lfirst(j);
|
|
|
|
List *indlist;
|
|
|
|
|
|
|
|
/* OR arguments should be ANDs or sub-RestrictInfos */
|
|
|
|
if (and_clause(orarg))
|
|
|
|
{
|
|
|
|
List *andargs = ((BoolExpr *) orarg)->args;
|
|
|
|
|
|
|
|
indlist = find_usable_indexes(root, rel,
|
|
|
|
andargs,
|
|
|
|
all_clauses,
|
|
|
|
false,
|
|
|
|
isjoininner,
|
|
|
|
outer_relids);
|
|
|
|
/* Recurse in case there are sub-ORs */
|
|
|
|
indlist = list_concat(indlist,
|
|
|
|
generate_bitmap_or_paths(root, rel,
|
|
|
|
andargs,
|
|
|
|
all_clauses,
|
|
|
|
isjoininner,
|
|
|
|
outer_relids));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Assert(IsA(orarg, RestrictInfo));
|
|
|
|
Assert(!restriction_is_or_clause((RestrictInfo *) orarg));
|
|
|
|
indlist = find_usable_indexes(root, rel,
|
|
|
|
list_make1(orarg),
|
|
|
|
all_clauses,
|
|
|
|
false,
|
|
|
|
isjoininner,
|
|
|
|
outer_relids);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* If nothing matched this arm, we can't do anything
|
|
|
|
* with this OR clause.
|
|
|
|
*/
|
|
|
|
if (indlist == NIL)
|
|
|
|
{
|
|
|
|
pathlist = NIL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* OK, pick the most promising AND combination,
|
|
|
|
* and add it to pathlist.
|
|
|
|
*/
|
|
|
|
bitmapqual = choose_bitmap_and(root, rel, indlist);
|
|
|
|
pathlist = lappend(pathlist, bitmapqual);
|
|
|
|
}
|
1999-08-16 04:17:58 +02:00
|
|
|
/*
|
2005-04-22 23:58:32 +02:00
|
|
|
* If we have a match for every arm, then turn them
|
|
|
|
* into a BitmapOrPath, and add to result list.
|
1998-08-02 00:12:13 +02:00
|
|
|
*/
|
2005-04-22 23:58:32 +02:00
|
|
|
if (pathlist != NIL)
|
|
|
|
{
|
|
|
|
bitmapqual = (Path *) create_bitmap_or_path(root, rel, pathlist);
|
|
|
|
result = lappend(result, bitmapqual);
|
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
2002-11-24 22:52:15 +01:00
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* choose_bitmap_and
|
|
|
|
* Given a nonempty list of bitmap paths, AND them into one path.
|
|
|
|
*
|
|
|
|
* This is a nontrivial decision since we can legally use any subset of the
|
|
|
|
* given path set. We want to choose a good tradeoff between selectivity
|
|
|
|
* and cost of computing the bitmap.
|
|
|
|
*
|
|
|
|
* The result is either a single one of the inputs, or a BitmapAndPath
|
|
|
|
* combining multiple inputs.
|
|
|
|
*/
|
|
|
|
static Path *
|
2005-06-06 00:32:58 +02:00
|
|
|
choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, List *paths)
|
2005-04-22 23:58:32 +02:00
|
|
|
{
|
2005-04-23 03:57:34 +02:00
|
|
|
int npaths = list_length(paths);
|
|
|
|
Path **patharray;
|
|
|
|
Cost costsofar;
|
|
|
|
List *qualsofar;
|
|
|
|
ListCell *lastcell;
|
|
|
|
int i;
|
|
|
|
ListCell *l;
|
|
|
|
|
|
|
|
Assert(npaths > 0); /* else caller error */
|
|
|
|
if (npaths == 1)
|
|
|
|
return (Path *) linitial(paths); /* easy case */
|
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
/*
|
2005-04-23 03:57:34 +02:00
|
|
|
* In theory we should consider every nonempty subset of the given paths.
|
|
|
|
* In practice that seems like overkill, given the crude nature of the
|
|
|
|
* estimates, not to mention the possible effects of higher-level AND and
|
|
|
|
* OR clauses. As a compromise, we sort the paths by selectivity.
|
|
|
|
* We always take the first, and sequentially add on paths that result
|
|
|
|
* in a lower estimated cost.
|
|
|
|
*
|
|
|
|
* We also make some effort to detect directly redundant input paths,
|
|
|
|
* as can happen if there are multiple possibly usable indexes. For
|
|
|
|
* this we look only at plain IndexPath inputs, not at sub-OR clauses.
|
|
|
|
* And we consider an index redundant if all its index conditions were
|
2005-06-11 00:25:37 +02:00
|
|
|
* already used by earlier indexes. (We could use predicate_implied_by
|
|
|
|
* to have a more intelligent, but much more expensive, check --- but in
|
|
|
|
* most cases simple pointer equality should suffice, since after all the
|
2005-04-25 05:58:30 +02:00
|
|
|
* index conditions are all coming from the same RestrictInfo lists.)
|
|
|
|
*
|
|
|
|
* XXX is there any risk of throwing away a useful partial index here
|
|
|
|
* because we don't explicitly look at indpred? At least in simple
|
|
|
|
* cases, the partial index will sort before competing non-partial
|
|
|
|
* indexes and so it makes the right choice, but perhaps we need to
|
|
|
|
* work harder.
|
2005-04-22 23:58:32 +02:00
|
|
|
*/
|
2005-04-23 03:57:34 +02:00
|
|
|
|
|
|
|
/* Convert list to array so we can apply qsort */
|
|
|
|
patharray = (Path **) palloc(npaths * sizeof(Path *));
|
|
|
|
i = 0;
|
|
|
|
foreach(l, paths)
|
|
|
|
{
|
|
|
|
patharray[i++] = (Path *) lfirst(l);
|
|
|
|
}
|
|
|
|
qsort(patharray, npaths, sizeof(Path *), bitmap_path_comparator);
|
|
|
|
|
|
|
|
paths = list_make1(patharray[0]);
|
|
|
|
costsofar = bitmap_and_cost_est(root, rel, paths);
|
|
|
|
if (IsA(patharray[0], IndexPath))
|
2005-04-25 03:30:14 +02:00
|
|
|
qualsofar = list_copy(((IndexPath *) patharray[0])->indexclauses);
|
2005-04-23 03:57:34 +02:00
|
|
|
else
|
|
|
|
qualsofar = NIL;
|
|
|
|
lastcell = list_head(paths); /* for quick deletions */
|
|
|
|
|
|
|
|
for (i = 1; i < npaths; i++)
|
|
|
|
{
|
|
|
|
Path *newpath = patharray[i];
|
|
|
|
List *newqual = NIL;
|
|
|
|
Cost newcost;
|
|
|
|
|
|
|
|
if (IsA(newpath, IndexPath))
|
|
|
|
{
|
2005-04-25 03:30:14 +02:00
|
|
|
newqual = ((IndexPath *) newpath)->indexclauses;
|
2005-04-25 05:58:30 +02:00
|
|
|
if (list_difference_ptr(newqual, qualsofar) == NIL)
|
2005-04-23 03:57:34 +02:00
|
|
|
continue; /* redundant */
|
|
|
|
}
|
|
|
|
|
|
|
|
paths = lappend(paths, newpath);
|
|
|
|
newcost = bitmap_and_cost_est(root, rel, paths);
|
|
|
|
if (newcost < costsofar)
|
|
|
|
{
|
|
|
|
costsofar = newcost;
|
|
|
|
if (newqual)
|
|
|
|
qualsofar = list_concat(qualsofar, list_copy(newqual));
|
|
|
|
lastcell = lnext(lastcell);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
paths = list_delete_cell(paths, lnext(lastcell), lastcell);
|
|
|
|
}
|
|
|
|
Assert(lnext(lastcell) == NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (list_length(paths) == 1)
|
|
|
|
return (Path *) linitial(paths); /* no need for AND */
|
2005-04-22 23:58:32 +02:00
|
|
|
return (Path *) create_bitmap_and_path(root, rel, paths);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2005-04-23 03:57:34 +02:00
|
|
|
/* qsort comparator to sort in increasing selectivity order */
|
|
|
|
static int
|
|
|
|
bitmap_path_comparator(const void *a, const void *b)
|
|
|
|
{
|
|
|
|
Path *pa = *(Path * const *) a;
|
|
|
|
Path *pb = *(Path * const *) b;
|
|
|
|
Cost acost;
|
|
|
|
Cost bcost;
|
|
|
|
Selectivity aselec;
|
|
|
|
Selectivity bselec;
|
|
|
|
|
|
|
|
cost_bitmap_tree_node(pa, &acost, &aselec);
|
|
|
|
cost_bitmap_tree_node(pb, &bcost, &bselec);
|
|
|
|
|
|
|
|
if (aselec < bselec)
|
|
|
|
return -1;
|
|
|
|
if (aselec > bselec)
|
|
|
|
return 1;
|
|
|
|
/* if identical selectivity, sort by cost */
|
|
|
|
if (acost < bcost)
|
|
|
|
return -1;
|
|
|
|
if (acost > bcost)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Estimate the cost of actually executing a BitmapAnd with the given
|
|
|
|
* inputs.
|
|
|
|
*/
|
|
|
|
static Cost
|
2005-06-06 00:32:58 +02:00
|
|
|
bitmap_and_cost_est(PlannerInfo *root, RelOptInfo *rel, List *paths)
|
2005-04-23 03:57:34 +02:00
|
|
|
{
|
|
|
|
BitmapAndPath apath;
|
|
|
|
Path bpath;
|
|
|
|
|
|
|
|
/* Set up a dummy BitmapAndPath */
|
|
|
|
apath.path.type = T_BitmapAndPath;
|
|
|
|
apath.path.parent = rel;
|
|
|
|
apath.bitmapquals = paths;
|
|
|
|
cost_bitmap_and_node(&apath, root);
|
|
|
|
|
|
|
|
/* Now we can do cost_bitmap_heap_scan */
|
|
|
|
cost_bitmap_heap_scan(&bpath, root, rel, (Path *) &apath, false);
|
|
|
|
|
|
|
|
return bpath.total_cost;
|
|
|
|
}
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
/****************************************************************************
|
1997-09-07 07:04:48 +02:00
|
|
|
* ---- ROUTINES TO CHECK RESTRICTIONS ----
|
1996-07-09 08:22:35 +02:00
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1999-02-14 00:22:53 +01:00
|
|
|
* group_clauses_by_indexkey
|
2003-05-26 02:11:29 +02:00
|
|
|
* Find restriction clauses that can be used with an index.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
2005-06-14 01:14:49 +02:00
|
|
|
* Returns a list of sublists of RestrictInfo nodes for clauses that can be
|
|
|
|
* used with this index. Each sublist contains clauses that can be used
|
|
|
|
* with one index key (in no particular order); the top list is ordered by
|
|
|
|
* index key. (This is depended on by expand_indexqual_conditions().)
|
|
|
|
*
|
2005-04-22 23:58:32 +02:00
|
|
|
* As explained in the comments for find_usable_indexes(), we can use
|
|
|
|
* clauses from either of the given lists, but the result is required to
|
|
|
|
* use at least one clause from the "current clauses" list. We return
|
|
|
|
* NIL if we don't find any such clause.
|
|
|
|
*
|
|
|
|
* outer_relids determines what Vars will be allowed on the other side
|
|
|
|
* of a possible index qual; see match_clause_to_indexcol().
|
|
|
|
*
|
2005-06-14 01:14:49 +02:00
|
|
|
* If the index has amoptionalkey = false, we give up and return NIL when
|
|
|
|
* there are no restriction clauses matching the first index key. Otherwise,
|
|
|
|
* we return NIL if there are no restriction clauses matching any index key.
|
|
|
|
* A non-NIL result will have one (possibly empty) sublist for each index key.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
2005-06-14 01:14:49 +02:00
|
|
|
* Example: given an index on (A,B,C), we would return ((C1 C2) () (C3 C4))
|
|
|
|
* if we find that clauses C1 and C2 use column A, clauses C3 and C4 use
|
|
|
|
* column C, and no clauses use column B.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2005-04-12 01:06:57 +02:00
|
|
|
List *
|
2005-04-22 23:58:32 +02:00
|
|
|
group_clauses_by_indexkey(IndexOptInfo *index,
|
|
|
|
List *clauses, List *outer_clauses,
|
|
|
|
Relids outer_relids)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2004-06-01 06:47:46 +02:00
|
|
|
List *clausegroup_list = NIL;
|
2005-04-22 23:58:32 +02:00
|
|
|
bool found_clause = false;
|
2003-05-28 18:04:02 +02:00
|
|
|
int indexcol = 0;
|
2002-11-24 22:52:15 +01:00
|
|
|
Oid *classes = index->classlist;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
if (clauses == NIL)
|
|
|
|
return NIL; /* cannot succeed */
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1998-07-31 17:10:40 +02:00
|
|
|
do
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-07-23 05:34:49 +02:00
|
|
|
Oid curClass = classes[0];
|
2004-06-01 06:47:46 +02:00
|
|
|
List *clausegroup = NIL;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *l;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
/* check the current clauses */
|
|
|
|
foreach(l, clauses)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
|
1999-07-23 05:34:49 +02:00
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
Assert(IsA(rinfo, RestrictInfo));
|
2005-03-27 08:29:49 +02:00
|
|
|
if (match_clause_to_indexcol(index,
|
2003-05-28 18:04:02 +02:00
|
|
|
indexcol,
|
1999-07-23 05:34:49 +02:00
|
|
|
curClass,
|
2005-04-22 23:58:32 +02:00
|
|
|
rinfo,
|
|
|
|
outer_relids))
|
|
|
|
{
|
2004-06-01 06:47:46 +02:00
|
|
|
clausegroup = lappend(clausegroup, rinfo);
|
2005-04-22 23:58:32 +02:00
|
|
|
found_clause = true;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1999-07-23 05:34:49 +02:00
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
/* check the outer clauses */
|
|
|
|
foreach(l, outer_clauses)
|
2003-12-18 01:22:12 +01:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
|
2003-12-18 01:22:12 +01:00
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
Assert(IsA(rinfo, RestrictInfo));
|
2005-03-27 08:29:49 +02:00
|
|
|
if (match_clause_to_indexcol(index,
|
2003-12-18 01:22:12 +01:00
|
|
|
indexcol,
|
|
|
|
curClass,
|
2005-04-22 23:58:32 +02:00
|
|
|
rinfo,
|
|
|
|
outer_relids))
|
2004-06-01 06:47:46 +02:00
|
|
|
clausegroup = lappend(clausegroup, rinfo);
|
2003-12-18 01:22:12 +01:00
|
|
|
}
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
2005-06-14 01:14:49 +02:00
|
|
|
* If no clauses match this key, check for amoptionalkey restriction.
|
1999-07-23 05:34:49 +02:00
|
|
|
*/
|
2005-06-14 01:14:49 +02:00
|
|
|
if (clausegroup == NIL && !index->amoptionalkey && indexcol == 0)
|
|
|
|
return NIL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2004-06-01 06:47:46 +02:00
|
|
|
clausegroup_list = lappend(clausegroup_list, clausegroup);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-05-28 18:04:02 +02:00
|
|
|
indexcol++;
|
1997-09-07 07:04:48 +02:00
|
|
|
classes++;
|
1997-03-18 19:41:37 +01:00
|
|
|
|
2003-05-28 18:04:02 +02:00
|
|
|
} while (!DoneMatchingIndexKeys(classes));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
if (!found_clause)
|
1999-07-30 06:07:25 +02:00
|
|
|
return NIL;
|
|
|
|
|
2004-06-01 06:47:46 +02:00
|
|
|
return clausegroup_list;
|
1997-03-18 19:41:37 +01:00
|
|
|
}
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-01-22 07:25:42 +01:00
|
|
|
/*
|
2003-05-28 18:04:02 +02:00
|
|
|
* match_clause_to_indexcol()
|
|
|
|
* Determines whether a restriction clause matches a column of an index.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
2005-03-27 00:29:20 +01:00
|
|
|
* To match a normal index, the clause:
|
2000-08-13 04:50:35 +02:00
|
|
|
*
|
2002-11-24 22:52:15 +01:00
|
|
|
* (1) must be in the form (indexkey op const) or (const op indexkey);
|
|
|
|
* and
|
1999-08-12 06:32:54 +02:00
|
|
|
* (2) must contain an operator which is in the same class as the index
|
2003-05-28 18:04:02 +02:00
|
|
|
* operator for this column, or is a "special" operator as recognized
|
1999-08-12 06:32:54 +02:00
|
|
|
* by match_special_index_operator().
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
2005-04-22 23:58:32 +02:00
|
|
|
* Our definition of "const" is pretty liberal: we allow Vars belonging
|
|
|
|
* to the caller-specified outer_relids relations (which had better not
|
|
|
|
* include the relation whose index is being tested). outer_relids should
|
|
|
|
* be NULL when checking simple restriction clauses, and the outer side
|
|
|
|
* of the join when building a join inner scan. Other than that, the
|
|
|
|
* only thing we don't like is volatile functions.
|
|
|
|
*
|
|
|
|
* Note: in most cases we already know that the clause as a whole uses
|
|
|
|
* vars from the interesting set of relations. The reason for the
|
|
|
|
* outer_relids test is to reject clauses like (a.f1 OP (b.f2 OP a.f3));
|
|
|
|
* that's not processable by an indexscan nestloop join on A, whereas
|
|
|
|
* (a.f1 OP (b.f2 OP c.f3)) is.
|
|
|
|
*
|
1999-08-12 06:32:54 +02:00
|
|
|
* Presently, the executor can only deal with indexquals that have the
|
|
|
|
* indexkey on the left, so we can only use clauses that have the indexkey
|
|
|
|
* on the right if we can commute the clause to put the key on the left.
|
|
|
|
* We do not actually do the commuting here, but we check whether a
|
|
|
|
* suitable commutator operator is available.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
2005-03-27 00:29:20 +01:00
|
|
|
* For boolean indexes, it is also possible to match the clause directly
|
|
|
|
* to the indexkey; or perhaps the clause is (NOT indexkey).
|
|
|
|
*
|
2005-03-27 08:29:49 +02:00
|
|
|
* 'index' is the index of interest.
|
2003-05-28 18:04:02 +02:00
|
|
|
* 'indexcol' is a column number of 'index' (counting from 0).
|
1999-12-31 04:41:03 +01:00
|
|
|
* 'opclass' is the corresponding operator class.
|
2004-01-04 01:07:32 +01:00
|
|
|
* 'rinfo' is the clause to be tested (as a RestrictInfo node).
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1999-07-23 05:34:49 +02:00
|
|
|
* Returns true if the clause can be used with this index key.
|
|
|
|
*
|
2000-02-05 19:26:09 +01:00
|
|
|
* NOTE: returns false if clause is an OR or AND clause; it is the
|
|
|
|
* responsibility of higher-level routines to cope with those.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1999-07-23 05:34:49 +02:00
|
|
|
static bool
|
2005-03-27 08:29:49 +02:00
|
|
|
match_clause_to_indexcol(IndexOptInfo *index,
|
2003-05-28 18:04:02 +02:00
|
|
|
int indexcol,
|
1999-12-31 04:41:03 +01:00
|
|
|
Oid opclass,
|
2005-04-22 23:58:32 +02:00
|
|
|
RestrictInfo *rinfo,
|
|
|
|
Relids outer_relids)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2004-01-04 01:07:32 +01:00
|
|
|
Expr *clause = rinfo->clause;
|
2003-01-15 20:35:48 +01:00
|
|
|
Node *leftop,
|
1997-09-08 04:41:22 +02:00
|
|
|
*rightop;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-03-27 00:29:20 +01:00
|
|
|
/* First check for boolean-index cases. */
|
|
|
|
if (IsBooleanOpclass(opclass))
|
|
|
|
{
|
2005-03-27 08:29:49 +02:00
|
|
|
if (match_boolean_index_clause((Node *) clause, indexcol, index))
|
2005-03-27 00:29:20 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Else clause must be a binary opclause. */
|
2002-12-12 16:49:42 +01:00
|
|
|
if (!is_opclause(clause))
|
1999-07-23 05:34:49 +02:00
|
|
|
return false;
|
1997-09-07 07:04:48 +02:00
|
|
|
leftop = get_leftop(clause);
|
|
|
|
rightop = get_rightop(clause);
|
2000-04-12 19:17:23 +02:00
|
|
|
if (!leftop || !rightop)
|
1999-07-23 05:34:49 +02:00
|
|
|
return false;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-11-24 22:52:15 +01:00
|
|
|
/*
|
2003-08-04 02:43:34 +02:00
|
|
|
* Check for clauses of the form: (indexkey operator constant) or
|
2005-04-22 23:58:32 +02:00
|
|
|
* (constant operator indexkey). See above notes about const-ness.
|
2002-11-24 22:52:15 +01:00
|
|
|
*/
|
2005-03-27 08:29:49 +02:00
|
|
|
if (match_index_to_operand(leftop, indexcol, index) &&
|
2005-04-22 23:58:32 +02:00
|
|
|
bms_is_subset(rinfo->right_relids, outer_relids) &&
|
|
|
|
!contain_volatile_functions(rightop))
|
1997-03-18 19:41:37 +01:00
|
|
|
{
|
2002-11-24 22:52:15 +01:00
|
|
|
if (is_indexable_operator(clause, opclass, true))
|
|
|
|
return true;
|
|
|
|
|
1999-07-23 05:34:49 +02:00
|
|
|
/*
|
2003-08-04 02:43:34 +02:00
|
|
|
* If we didn't find a member of the index's opclass, see whether
|
|
|
|
* it is a "special" indexable operator.
|
1999-07-23 05:34:49 +02:00
|
|
|
*/
|
2002-11-24 22:52:15 +01:00
|
|
|
if (match_special_index_operator(clause, opclass, true))
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
1999-05-25 18:15:34 +02:00
|
|
|
|
2005-03-27 08:29:49 +02:00
|
|
|
if (match_index_to_operand(rightop, indexcol, index) &&
|
2005-04-22 23:58:32 +02:00
|
|
|
bms_is_subset(rinfo->left_relids, outer_relids) &&
|
|
|
|
!contain_volatile_functions(leftop))
|
2002-11-24 22:52:15 +01:00
|
|
|
{
|
|
|
|
if (is_indexable_operator(clause, opclass, false))
|
|
|
|
return true;
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2002-11-24 22:52:15 +01:00
|
|
|
/*
|
2003-08-04 02:43:34 +02:00
|
|
|
* If we didn't find a member of the index's opclass, see whether
|
|
|
|
* it is a "special" indexable operator.
|
2002-11-24 22:52:15 +01:00
|
|
|
*/
|
|
|
|
if (match_special_index_operator(clause, opclass, false))
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2002-11-24 22:52:15 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
1999-08-12 06:32:54 +02:00
|
|
|
/*
|
|
|
|
* indexable_operator
|
2001-08-21 18:36:06 +02:00
|
|
|
* Does a binary opclause contain an operator matching the index opclass?
|
1999-08-12 06:32:54 +02:00
|
|
|
*
|
2000-01-09 01:26:47 +01:00
|
|
|
* If the indexkey is on the right, what we actually want to know
|
|
|
|
* is whether the operator has a commutator operator that matches
|
2001-08-21 18:36:06 +02:00
|
|
|
* the index's opclass.
|
1999-08-12 06:32:54 +02:00
|
|
|
*
|
2000-01-09 01:26:47 +01:00
|
|
|
* Returns the OID of the matching operator, or InvalidOid if no match.
|
Extend pg_cast castimplicit column to a three-way value; this allows us
to be flexible about assignment casts without introducing ambiguity in
operator/function resolution. Introduce a well-defined promotion hierarchy
for numeric datatypes (int2->int4->int8->numeric->float4->float8).
Change make_const to initially label numeric literals as int4, int8, or
numeric (never float8 anymore).
Explicitly mark Func and RelabelType nodes to indicate whether they came
from a function call, explicit cast, or implicit cast; use this to do
reverse-listing more accurately and without so many heuristics.
Explicit casts to char, varchar, bit, varbit will truncate or pad without
raising an error (the pre-7.2 behavior), while assigning to a column without
any explicit cast will still raise an error for wrong-length data like 7.3.
This more nearly follows the SQL spec than 7.2 behavior (we should be
reporting a 'completion condition' in the explicit-cast cases, but we have
no mechanism for that, so just do silent truncation).
Fix some problems with enforcement of typmod for array elements;
it didn't work at all in 'UPDATE ... SET array[n] = foo', for example.
Provide a generalized array_length_coerce() function to replace the
specialized per-array-type functions that used to be needed (and were
missing for NUMERIC as well as all the datetime types).
Add missing conversions int8<->float4, text<->numeric, oid<->int8.
initdb forced.
2002-09-18 23:35:25 +02:00
|
|
|
* (Formerly, this routine might return a binary-compatible operator
|
|
|
|
* rather than the original one, but that kluge is history.)
|
1999-08-12 06:32:54 +02:00
|
|
|
*/
|
2002-12-12 16:49:42 +01:00
|
|
|
static Oid
|
2001-08-21 18:36:06 +02:00
|
|
|
indexable_operator(Expr *clause, Oid opclass, bool indexkey_on_left)
|
1999-08-12 06:32:54 +02:00
|
|
|
{
|
2002-12-12 16:49:42 +01:00
|
|
|
Oid expr_op = ((OpExpr *) clause)->opno;
|
Extend pg_cast castimplicit column to a three-way value; this allows us
to be flexible about assignment casts without introducing ambiguity in
operator/function resolution. Introduce a well-defined promotion hierarchy
for numeric datatypes (int2->int4->int8->numeric->float4->float8).
Change make_const to initially label numeric literals as int4, int8, or
numeric (never float8 anymore).
Explicitly mark Func and RelabelType nodes to indicate whether they came
from a function call, explicit cast, or implicit cast; use this to do
reverse-listing more accurately and without so many heuristics.
Explicit casts to char, varchar, bit, varbit will truncate or pad without
raising an error (the pre-7.2 behavior), while assigning to a column without
any explicit cast will still raise an error for wrong-length data like 7.3.
This more nearly follows the SQL spec than 7.2 behavior (we should be
reporting a 'completion condition' in the explicit-cast cases, but we have
no mechanism for that, so just do silent truncation).
Fix some problems with enforcement of typmod for array elements;
it didn't work at all in 'UPDATE ... SET array[n] = foo', for example.
Provide a generalized array_length_coerce() function to replace the
specialized per-array-type functions that used to be needed (and were
missing for NUMERIC as well as all the datetime types).
Add missing conversions int8<->float4, text<->numeric, oid<->int8.
initdb forced.
2002-09-18 23:35:25 +02:00
|
|
|
Oid commuted_op;
|
1999-08-12 06:32:54 +02:00
|
|
|
|
|
|
|
/* Get the commuted operator if necessary */
|
|
|
|
if (indexkey_on_left)
|
|
|
|
commuted_op = expr_op;
|
|
|
|
else
|
|
|
|
commuted_op = get_commutator(expr_op);
|
|
|
|
if (commuted_op == InvalidOid)
|
2000-01-09 01:26:47 +01:00
|
|
|
return InvalidOid;
|
1999-08-12 06:32:54 +02:00
|
|
|
|
Extend pg_cast castimplicit column to a three-way value; this allows us
to be flexible about assignment casts without introducing ambiguity in
operator/function resolution. Introduce a well-defined promotion hierarchy
for numeric datatypes (int2->int4->int8->numeric->float4->float8).
Change make_const to initially label numeric literals as int4, int8, or
numeric (never float8 anymore).
Explicitly mark Func and RelabelType nodes to indicate whether they came
from a function call, explicit cast, or implicit cast; use this to do
reverse-listing more accurately and without so many heuristics.
Explicit casts to char, varchar, bit, varbit will truncate or pad without
raising an error (the pre-7.2 behavior), while assigning to a column without
any explicit cast will still raise an error for wrong-length data like 7.3.
This more nearly follows the SQL spec than 7.2 behavior (we should be
reporting a 'completion condition' in the explicit-cast cases, but we have
no mechanism for that, so just do silent truncation).
Fix some problems with enforcement of typmod for array elements;
it didn't work at all in 'UPDATE ... SET array[n] = foo', for example.
Provide a generalized array_length_coerce() function to replace the
specialized per-array-type functions that used to be needed (and were
missing for NUMERIC as well as all the datetime types).
Add missing conversions int8<->float4, text<->numeric, oid<->int8.
initdb forced.
2002-09-18 23:35:25 +02:00
|
|
|
/* OK if the (commuted) operator is a member of the index's opclass */
|
2001-08-21 18:36:06 +02:00
|
|
|
if (op_in_opclass(commuted_op, opclass))
|
2000-01-09 01:26:47 +01:00
|
|
|
return expr_op;
|
1999-08-12 06:32:54 +02:00
|
|
|
|
2000-01-09 01:26:47 +01:00
|
|
|
return InvalidOid;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
1997-09-07 07:04:48 +02:00
|
|
|
* ---- ROUTINES TO DO PARTIAL INDEX PREDICATE TESTS ----
|
1996-07-09 08:22:35 +02:00
|
|
|
****************************************************************************/
|
|
|
|
|
2004-01-05 06:07:36 +01:00
|
|
|
/*
|
|
|
|
* check_partial_indexes
|
|
|
|
* Check each partial index of the relation, and mark it predOK or not
|
|
|
|
* depending on whether the predicate is satisfied for this query.
|
|
|
|
*/
|
|
|
|
void
|
2005-06-06 00:32:58 +02:00
|
|
|
check_partial_indexes(PlannerInfo *root, RelOptInfo *rel)
|
2004-01-05 06:07:36 +01:00
|
|
|
{
|
|
|
|
List *restrictinfo_list = rel->baserestrictinfo;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *ilist;
|
2004-01-05 06:07:36 +01:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
|
|
|
* Note: if Postgres tried to optimize queries by forming equivalence
|
|
|
|
* classes over equi-joined attributes (i.e., if it recognized that a
|
|
|
|
* qualification such as "where a.b=c.d and a.b=5" could make use of
|
|
|
|
* an index on c.d), then we could use that equivalence class info
|
2005-06-09 06:19:00 +02:00
|
|
|
* here with joininfo lists to do more complete tests for the usability
|
1997-09-07 07:04:48 +02:00
|
|
|
* of a partial index. For now, the test only uses restriction
|
2005-06-11 00:25:37 +02:00
|
|
|
* clauses (those in baserestrictinfo). --Nels, Dec '92
|
2001-08-06 20:09:45 +02:00
|
|
|
*
|
|
|
|
* XXX as of 7.1, equivalence class info *is* available. Consider
|
|
|
|
* improving this code as foreseen by Nels.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
|
2005-06-11 00:25:37 +02:00
|
|
|
foreach(ilist, rel->indexlist)
|
2001-08-21 18:36:06 +02:00
|
|
|
{
|
2005-06-11 00:25:37 +02:00
|
|
|
IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
|
2002-12-15 17:17:59 +01:00
|
|
|
|
2005-06-11 00:25:37 +02:00
|
|
|
if (index->indpred == NIL)
|
|
|
|
continue; /* ignore non-partial indexes */
|
2000-08-24 05:29:15 +02:00
|
|
|
|
2005-06-11 00:25:37 +02:00
|
|
|
index->predOK = predicate_implied_by(index->indpred,
|
|
|
|
restrictinfo_list);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
1997-09-07 07:04:48 +02:00
|
|
|
* ---- ROUTINES TO CHECK JOIN CLAUSES ----
|
1996-07-09 08:22:35 +02:00
|
|
|
****************************************************************************/
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
2002-11-24 22:52:15 +01:00
|
|
|
* indexable_outerrelids
|
|
|
|
* Finds all other relids that participate in any indexable join clause
|
2005-04-22 23:58:32 +02:00
|
|
|
* for the specified table. Returns a set of relids.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2002-11-24 22:52:15 +01:00
|
|
|
static Relids
|
2005-04-22 23:58:32 +02:00
|
|
|
indexable_outerrelids(RelOptInfo *rel)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2003-02-08 21:20:55 +01:00
|
|
|
Relids outer_relids = NULL;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *l;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-06-09 06:19:00 +02:00
|
|
|
/*
|
|
|
|
* Examine each joinclause in the joininfo list to see if it matches any
|
|
|
|
* key of any index. If so, add the clause's other rels to the result.
|
|
|
|
* (Note: we consider only actual participants, not extraneous rels
|
|
|
|
* possibly mentioned in required_relids.)
|
|
|
|
*/
|
2005-04-22 23:58:32 +02:00
|
|
|
foreach(l, rel->joininfo)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2005-06-09 06:19:00 +02:00
|
|
|
RestrictInfo *joininfo = (RestrictInfo *) lfirst(l);
|
|
|
|
Relids other_rels;
|
1999-07-30 06:07:25 +02:00
|
|
|
|
2005-06-09 06:19:00 +02:00
|
|
|
other_rels = bms_difference(joininfo->clause_relids, rel->relids);
|
|
|
|
if (matches_any_index(joininfo, rel, other_rels))
|
|
|
|
outer_relids = bms_join(outer_relids, other_rels);
|
|
|
|
else
|
|
|
|
bms_free(other_rels);
|
2005-04-22 23:58:32 +02:00
|
|
|
}
|
1999-07-30 06:07:25 +02:00
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
return outer_relids;
|
|
|
|
}
|
2002-11-24 22:52:15 +01:00
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
/*
|
2005-06-09 06:19:00 +02:00
|
|
|
* matches_any_index
|
|
|
|
* Workhorse for indexable_outerrelids: see if a joinclause can be
|
|
|
|
* matched to any index of the given rel.
|
2005-04-22 23:58:32 +02:00
|
|
|
*/
|
|
|
|
static bool
|
2005-06-09 06:19:00 +02:00
|
|
|
matches_any_index(RestrictInfo *rinfo, RelOptInfo *rel, Relids outer_relids)
|
2005-04-22 23:58:32 +02:00
|
|
|
{
|
|
|
|
ListCell *l;
|
2002-11-24 22:52:15 +01:00
|
|
|
|
2005-06-09 06:19:00 +02:00
|
|
|
Assert(IsA(rinfo, RestrictInfo));
|
2002-11-24 22:52:15 +01:00
|
|
|
|
2005-06-09 06:19:00 +02:00
|
|
|
if (restriction_is_or_clause(rinfo))
|
|
|
|
{
|
|
|
|
foreach(l, ((BoolExpr *) rinfo->orclause)->args)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2005-06-09 06:19:00 +02:00
|
|
|
Node *orarg = (Node *) lfirst(l);
|
2005-04-22 23:58:32 +02:00
|
|
|
|
|
|
|
/* OR arguments should be ANDs or sub-RestrictInfos */
|
|
|
|
if (and_clause(orarg))
|
|
|
|
{
|
2005-06-09 06:19:00 +02:00
|
|
|
ListCell *j;
|
2005-04-22 23:58:32 +02:00
|
|
|
|
|
|
|
/* Recurse to examine AND items and sub-ORs */
|
2005-06-09 06:19:00 +02:00
|
|
|
foreach(j, ((BoolExpr *) orarg)->args)
|
|
|
|
{
|
|
|
|
RestrictInfo *arinfo = (RestrictInfo *) lfirst(j);
|
|
|
|
|
|
|
|
if (matches_any_index(arinfo, rel, outer_relids))
|
|
|
|
return true;
|
|
|
|
}
|
2005-04-22 23:58:32 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2005-06-09 06:19:00 +02:00
|
|
|
/* Recurse to examine simple clause */
|
2005-04-22 23:58:32 +02:00
|
|
|
Assert(IsA(orarg, RestrictInfo));
|
|
|
|
Assert(!restriction_is_or_clause((RestrictInfo *) orarg));
|
|
|
|
if (matches_any_index((RestrictInfo *) orarg, rel,
|
2005-06-09 06:19:00 +02:00
|
|
|
outer_relids))
|
2005-04-22 23:58:32 +02:00
|
|
|
return true;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2005-04-22 23:58:32 +02:00
|
|
|
|
2005-06-09 06:19:00 +02:00
|
|
|
return false;
|
|
|
|
}
|
2005-04-22 23:58:32 +02:00
|
|
|
|
|
|
|
/* Normal case for a simple restriction clause */
|
|
|
|
foreach(l, rel->indexlist)
|
|
|
|
{
|
|
|
|
IndexOptInfo *index = (IndexOptInfo *) lfirst(l);
|
|
|
|
int indexcol = 0;
|
|
|
|
Oid *classes = index->classlist;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
Oid curClass = classes[0];
|
|
|
|
|
|
|
|
if (match_clause_to_indexcol(index,
|
|
|
|
indexcol,
|
|
|
|
curClass,
|
|
|
|
rinfo,
|
|
|
|
outer_relids))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
indexcol++;
|
|
|
|
classes++;
|
|
|
|
} while (!DoneMatchingIndexKeys(classes));
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
2002-11-24 22:52:15 +01:00
|
|
|
* best_inner_indexscan
|
|
|
|
* Finds the best available inner indexscan for a nestloop join
|
|
|
|
* with the given rel on the inside and the given outer_relids outside.
|
|
|
|
* May return NULL if there are no possible inner indexscans.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2002-11-24 22:52:15 +01:00
|
|
|
* We ignore ordering considerations (since a nestloop's inner scan's order
|
|
|
|
* is uninteresting). Also, we consider only total cost when deciding which
|
|
|
|
* of two possible paths is better --- this assumes that all indexpaths have
|
|
|
|
* negligible startup cost. (True today, but someday we might have to think
|
|
|
|
* harder.) Therefore, there is only one dimension of comparison and so it's
|
|
|
|
* sufficient to return a single "best" path.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2002-11-24 22:52:15 +01:00
|
|
|
Path *
|
2005-06-06 00:32:58 +02:00
|
|
|
best_inner_indexscan(PlannerInfo *root, RelOptInfo *rel,
|
2002-11-24 22:52:15 +01:00
|
|
|
Relids outer_relids, JoinType jointype)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2005-04-22 23:58:32 +02:00
|
|
|
Path *cheapest;
|
2002-11-24 22:52:15 +01:00
|
|
|
bool isouterjoin;
|
2005-04-22 23:58:32 +02:00
|
|
|
List *clause_list;
|
|
|
|
List *indexpaths;
|
|
|
|
List *bitindexpaths;
|
|
|
|
ListCell *l;
|
2002-11-24 22:52:15 +01:00
|
|
|
InnerIndexscanInfo *info;
|
Fix GEQO to work again in CVS tip, by being more careful about memory
allocation in best_inner_indexscan(). While at it, simplify GEQO's
interface to the main planner --- make_join_rel() offers exactly the
API it really wants, whereas calling make_rels_by_clause_joins() and
make_rels_by_clauseless_joins() required jumping through hoops.
Rewrite gimme_tree for clarity (sometimes iteration is much better than
recursion), and approximately halve GEQO's runtime by recognizing that
tours of the forms (a,b,c,d,...) and (b,a,c,d,...) are equivalent
because of symmetry in make_join_rel().
2002-12-16 22:30:30 +01:00
|
|
|
MemoryContext oldcontext;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-11-24 22:52:15 +01:00
|
|
|
/*
|
2003-01-20 19:55:07 +01:00
|
|
|
* Nestloop only supports inner, left, and IN joins.
|
2002-11-24 22:52:15 +01:00
|
|
|
*/
|
|
|
|
switch (jointype)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2002-11-24 22:52:15 +01:00
|
|
|
case JOIN_INNER:
|
2003-01-20 19:55:07 +01:00
|
|
|
case JOIN_IN:
|
|
|
|
case JOIN_UNIQUE_OUTER:
|
2002-11-24 22:52:15 +01:00
|
|
|
isouterjoin = false;
|
|
|
|
break;
|
|
|
|
case JOIN_LEFT:
|
|
|
|
isouterjoin = true;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2002-11-24 22:52:15 +01:00
|
|
|
/*
|
|
|
|
* If there are no indexable joinclauses for this rel, exit quickly.
|
|
|
|
*/
|
2003-02-08 21:20:55 +01:00
|
|
|
if (bms_is_empty(rel->index_outer_relids))
|
2002-11-24 22:52:15 +01:00
|
|
|
return NULL;
|
2003-08-04 02:43:34 +02:00
|
|
|
|
Fix GEQO to work again in CVS tip, by being more careful about memory
allocation in best_inner_indexscan(). While at it, simplify GEQO's
interface to the main planner --- make_join_rel() offers exactly the
API it really wants, whereas calling make_rels_by_clause_joins() and
make_rels_by_clauseless_joins() required jumping through hoops.
Rewrite gimme_tree for clarity (sometimes iteration is much better than
recursion), and approximately halve GEQO's runtime by recognizing that
tours of the forms (a,b,c,d,...) and (b,a,c,d,...) are equivalent
because of symmetry in make_join_rel().
2002-12-16 22:30:30 +01:00
|
|
|
/*
|
|
|
|
* Otherwise, we have to do path selection in the memory context of
|
|
|
|
* the given rel, so that any created path can be safely attached to
|
|
|
|
* the rel's cache of best inner paths. (This is not currently an
|
|
|
|
* issue for normal planning, but it is an issue for GEQO planning.)
|
|
|
|
*/
|
|
|
|
oldcontext = MemoryContextSwitchTo(GetMemoryChunkContext(rel));
|
2003-08-04 02:43:34 +02:00
|
|
|
|
Fix GEQO to work again in CVS tip, by being more careful about memory
allocation in best_inner_indexscan(). While at it, simplify GEQO's
interface to the main planner --- make_join_rel() offers exactly the
API it really wants, whereas calling make_rels_by_clause_joins() and
make_rels_by_clauseless_joins() required jumping through hoops.
Rewrite gimme_tree for clarity (sometimes iteration is much better than
recursion), and approximately halve GEQO's runtime by recognizing that
tours of the forms (a,b,c,d,...) and (b,a,c,d,...) are equivalent
because of symmetry in make_join_rel().
2002-12-16 22:30:30 +01:00
|
|
|
/*
|
2003-08-04 02:43:34 +02:00
|
|
|
* Intersect the given outer_relids with index_outer_relids to find
|
2005-04-22 23:58:32 +02:00
|
|
|
* the set of outer relids actually relevant for this rel. If there
|
2003-08-04 02:43:34 +02:00
|
|
|
* are none, again we can fail immediately.
|
Fix GEQO to work again in CVS tip, by being more careful about memory
allocation in best_inner_indexscan(). While at it, simplify GEQO's
interface to the main planner --- make_join_rel() offers exactly the
API it really wants, whereas calling make_rels_by_clause_joins() and
make_rels_by_clauseless_joins() required jumping through hoops.
Rewrite gimme_tree for clarity (sometimes iteration is much better than
recursion), and approximately halve GEQO's runtime by recognizing that
tours of the forms (a,b,c,d,...) and (b,a,c,d,...) are equivalent
because of symmetry in make_join_rel().
2002-12-16 22:30:30 +01:00
|
|
|
*/
|
2003-02-08 21:20:55 +01:00
|
|
|
outer_relids = bms_intersect(rel->index_outer_relids, outer_relids);
|
|
|
|
if (bms_is_empty(outer_relids))
|
Fix GEQO to work again in CVS tip, by being more careful about memory
allocation in best_inner_indexscan(). While at it, simplify GEQO's
interface to the main planner --- make_join_rel() offers exactly the
API it really wants, whereas calling make_rels_by_clause_joins() and
make_rels_by_clauseless_joins() required jumping through hoops.
Rewrite gimme_tree for clarity (sometimes iteration is much better than
recursion), and approximately halve GEQO's runtime by recognizing that
tours of the forms (a,b,c,d,...) and (b,a,c,d,...) are equivalent
because of symmetry in make_join_rel().
2002-12-16 22:30:30 +01:00
|
|
|
{
|
2003-02-08 21:20:55 +01:00
|
|
|
bms_free(outer_relids);
|
Fix GEQO to work again in CVS tip, by being more careful about memory
allocation in best_inner_indexscan(). While at it, simplify GEQO's
interface to the main planner --- make_join_rel() offers exactly the
API it really wants, whereas calling make_rels_by_clause_joins() and
make_rels_by_clauseless_joins() required jumping through hoops.
Rewrite gimme_tree for clarity (sometimes iteration is much better than
recursion), and approximately halve GEQO's runtime by recognizing that
tours of the forms (a,b,c,d,...) and (b,a,c,d,...) are equivalent
because of symmetry in make_join_rel().
2002-12-16 22:30:30 +01:00
|
|
|
MemoryContextSwitchTo(oldcontext);
|
2002-11-24 22:52:15 +01:00
|
|
|
return NULL;
|
Fix GEQO to work again in CVS tip, by being more careful about memory
allocation in best_inner_indexscan(). While at it, simplify GEQO's
interface to the main planner --- make_join_rel() offers exactly the
API it really wants, whereas calling make_rels_by_clause_joins() and
make_rels_by_clauseless_joins() required jumping through hoops.
Rewrite gimme_tree for clarity (sometimes iteration is much better than
recursion), and approximately halve GEQO's runtime by recognizing that
tours of the forms (a,b,c,d,...) and (b,a,c,d,...) are equivalent
because of symmetry in make_join_rel().
2002-12-16 22:30:30 +01:00
|
|
|
}
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2002-11-24 22:52:15 +01:00
|
|
|
/*
|
|
|
|
* Look to see if we already computed the result for this set of
|
2003-08-04 02:43:34 +02:00
|
|
|
* relevant outerrels. (We include the isouterjoin status in the
|
2002-11-24 22:52:15 +01:00
|
|
|
* cache lookup key for safety. In practice I suspect this is not
|
2003-08-04 02:43:34 +02:00
|
|
|
* necessary because it should always be the same for a given
|
|
|
|
* innerrel.)
|
2002-11-24 22:52:15 +01:00
|
|
|
*/
|
2005-04-22 23:58:32 +02:00
|
|
|
foreach(l, rel->index_inner_paths)
|
2002-11-24 22:52:15 +01:00
|
|
|
{
|
2005-04-22 23:58:32 +02:00
|
|
|
info = (InnerIndexscanInfo *) lfirst(l);
|
2003-02-08 21:20:55 +01:00
|
|
|
if (bms_equal(info->other_relids, outer_relids) &&
|
2002-11-24 22:52:15 +01:00
|
|
|
info->isouterjoin == isouterjoin)
|
|
|
|
{
|
2003-02-08 21:20:55 +01:00
|
|
|
bms_free(outer_relids);
|
Fix GEQO to work again in CVS tip, by being more careful about memory
allocation in best_inner_indexscan(). While at it, simplify GEQO's
interface to the main planner --- make_join_rel() offers exactly the
API it really wants, whereas calling make_rels_by_clause_joins() and
make_rels_by_clauseless_joins() required jumping through hoops.
Rewrite gimme_tree for clarity (sometimes iteration is much better than
recursion), and approximately halve GEQO's runtime by recognizing that
tours of the forms (a,b,c,d,...) and (b,a,c,d,...) are equivalent
because of symmetry in make_join_rel().
2002-12-16 22:30:30 +01:00
|
|
|
MemoryContextSwitchTo(oldcontext);
|
2002-11-24 22:52:15 +01:00
|
|
|
return info->best_innerpath;
|
|
|
|
}
|
|
|
|
}
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2002-11-24 22:52:15 +01:00
|
|
|
/*
|
2005-04-22 23:58:32 +02:00
|
|
|
* Find all the relevant restriction and join clauses.
|
2002-11-24 22:52:15 +01:00
|
|
|
*/
|
2005-04-22 23:58:32 +02:00
|
|
|
clause_list = find_clauses_for_join(root, rel, outer_relids, isouterjoin);
|
2002-11-24 22:52:15 +01:00
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
/*
|
|
|
|
* Find all the index paths that are usable for this join, except for
|
|
|
|
* stuff involving OR clauses.
|
|
|
|
*/
|
|
|
|
indexpaths = find_usable_indexes(root, rel,
|
|
|
|
clause_list, NIL,
|
|
|
|
false, true,
|
|
|
|
outer_relids);
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
/*
|
|
|
|
* Generate BitmapOrPaths for any suitable OR-clauses present in the
|
|
|
|
* clause list.
|
|
|
|
*/
|
|
|
|
bitindexpaths = generate_bitmap_or_paths(root, rel,
|
|
|
|
clause_list, NIL,
|
|
|
|
true,
|
|
|
|
outer_relids);
|
1998-09-21 17:41:28 +02:00
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
/*
|
|
|
|
* Include the regular index paths in bitindexpaths.
|
|
|
|
*/
|
|
|
|
bitindexpaths = list_concat(bitindexpaths, list_copy(indexpaths));
|
2000-09-12 23:07:18 +02:00
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
/*
|
|
|
|
* If we found anything usable, generate a BitmapHeapPath for the
|
|
|
|
* most promising combination of bitmap index paths.
|
|
|
|
*/
|
|
|
|
if (bitindexpaths != NIL)
|
|
|
|
{
|
|
|
|
Path *bitmapqual;
|
|
|
|
BitmapHeapPath *bpath;
|
|
|
|
|
|
|
|
bitmapqual = choose_bitmap_and(root, rel, bitindexpaths);
|
|
|
|
bpath = create_bitmap_heap_path(root, rel, bitmapqual, true);
|
|
|
|
indexpaths = lappend(indexpaths, bpath);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now choose the cheapest member of indexpaths.
|
|
|
|
*/
|
|
|
|
cheapest = NULL;
|
|
|
|
foreach(l, indexpaths)
|
|
|
|
{
|
|
|
|
Path *path = (Path *) lfirst(l);
|
2000-09-12 23:07:18 +02:00
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
if (cheapest == NULL ||
|
|
|
|
compare_path_costs(path, cheapest, TOTAL_COST) < 0)
|
2002-11-24 22:52:15 +01:00
|
|
|
cheapest = path;
|
|
|
|
}
|
2000-01-23 00:50:30 +01:00
|
|
|
|
2002-11-24 22:52:15 +01:00
|
|
|
/* Cache the result --- whether positive or negative */
|
|
|
|
info = makeNode(InnerIndexscanInfo);
|
|
|
|
info->other_relids = outer_relids;
|
|
|
|
info->isouterjoin = isouterjoin;
|
|
|
|
info->best_innerpath = cheapest;
|
|
|
|
rel->index_inner_paths = lcons(info, rel->index_inner_paths);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
Fix GEQO to work again in CVS tip, by being more careful about memory
allocation in best_inner_indexscan(). While at it, simplify GEQO's
interface to the main planner --- make_join_rel() offers exactly the
API it really wants, whereas calling make_rels_by_clause_joins() and
make_rels_by_clauseless_joins() required jumping through hoops.
Rewrite gimme_tree for clarity (sometimes iteration is much better than
recursion), and approximately halve GEQO's runtime by recognizing that
tours of the forms (a,b,c,d,...) and (b,a,c,d,...) are equivalent
because of symmetry in make_join_rel().
2002-12-16 22:30:30 +01:00
|
|
|
MemoryContextSwitchTo(oldcontext);
|
|
|
|
|
2002-11-24 22:52:15 +01:00
|
|
|
return cheapest;
|
|
|
|
}
|
2000-02-15 21:49:31 +01:00
|
|
|
|
2002-11-24 22:52:15 +01:00
|
|
|
/*
|
2005-04-22 23:58:32 +02:00
|
|
|
* find_clauses_for_join
|
|
|
|
* Generate a list of clauses that are potentially useful for
|
|
|
|
* scanning rel as the inner side of a nestloop join.
|
2002-11-24 22:52:15 +01:00
|
|
|
*
|
2005-04-22 23:58:32 +02:00
|
|
|
* We consider both join and restriction clauses. Any joinclause that uses
|
|
|
|
* only otherrels in the specified outer_relids is fair game. But there must
|
|
|
|
* be at least one such joinclause in the final list, otherwise we return NIL
|
|
|
|
* indicating that there isn't any potential win here.
|
2002-11-24 22:52:15 +01:00
|
|
|
*/
|
2005-04-22 23:58:32 +02:00
|
|
|
static List *
|
2005-06-06 00:32:58 +02:00
|
|
|
find_clauses_for_join(PlannerInfo *root, RelOptInfo *rel,
|
2005-04-22 23:58:32 +02:00
|
|
|
Relids outer_relids, bool isouterjoin)
|
2002-11-24 22:52:15 +01:00
|
|
|
{
|
2005-04-22 23:58:32 +02:00
|
|
|
List *clause_list = NIL;
|
|
|
|
bool jfound = false;
|
2005-06-09 06:19:00 +02:00
|
|
|
Relids join_relids;
|
2005-04-22 23:58:32 +02:00
|
|
|
ListCell *l;
|
2002-11-24 22:52:15 +01:00
|
|
|
|
|
|
|
/*
|
2005-04-22 23:58:32 +02:00
|
|
|
* We can always use plain restriction clauses for the rel. We
|
|
|
|
* scan these first because we want them first in the clause
|
|
|
|
* list for the convenience of remove_redundant_join_clauses,
|
|
|
|
* which can never remove non-join clauses and hence won't be able
|
|
|
|
* to get rid of a non-join clause if it appears after a join
|
|
|
|
* clause it is redundant with.
|
2002-11-24 22:52:15 +01:00
|
|
|
*/
|
2005-04-22 23:58:32 +02:00
|
|
|
foreach(l, rel->baserestrictinfo)
|
|
|
|
{
|
|
|
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
|
2002-11-24 22:52:15 +01:00
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
/* Can't use pushed-down clauses in outer join */
|
|
|
|
if (isouterjoin && rinfo->is_pushed_down)
|
|
|
|
continue;
|
|
|
|
clause_list = lappend(clause_list, rinfo);
|
|
|
|
}
|
2002-11-24 22:52:15 +01:00
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
/* Look for joinclauses that are usable with given outer_relids */
|
2005-06-09 06:19:00 +02:00
|
|
|
join_relids = bms_union(rel->relids, outer_relids);
|
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
foreach(l, rel->joininfo)
|
|
|
|
{
|
2005-06-09 06:19:00 +02:00
|
|
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
|
2004-01-06 00:39:54 +01:00
|
|
|
|
2005-06-09 06:19:00 +02:00
|
|
|
/* Can't use pushed-down clauses in outer join */
|
|
|
|
if (isouterjoin && rinfo->is_pushed_down)
|
|
|
|
continue;
|
|
|
|
if (!bms_is_subset(rinfo->required_relids, join_relids))
|
2005-04-22 23:58:32 +02:00
|
|
|
continue;
|
2002-11-24 22:52:15 +01:00
|
|
|
|
2005-06-09 06:19:00 +02:00
|
|
|
clause_list = lappend(clause_list, rinfo);
|
|
|
|
jfound = true;
|
2005-04-22 23:58:32 +02:00
|
|
|
}
|
|
|
|
|
2005-06-09 06:19:00 +02:00
|
|
|
bms_free(join_relids);
|
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
/* if no join clause was matched then forget it, per comments above */
|
|
|
|
if (!jfound)
|
|
|
|
return NIL;
|
2002-11-24 22:52:15 +01:00
|
|
|
|
|
|
|
/*
|
2005-06-09 06:19:00 +02:00
|
|
|
* We may now have clauses that are known redundant. Get rid of 'em.
|
2002-11-24 22:52:15 +01:00
|
|
|
*/
|
2005-06-09 06:19:00 +02:00
|
|
|
if (list_length(clause_list) > 1)
|
2005-04-22 23:58:32 +02:00
|
|
|
{
|
|
|
|
clause_list = remove_redundant_join_clauses(root,
|
|
|
|
clause_list,
|
|
|
|
isouterjoin);
|
|
|
|
}
|
|
|
|
|
|
|
|
return clause_list;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* ---- PATH CREATION UTILITIES ----
|
|
|
|
****************************************************************************/
|
|
|
|
|
2004-01-06 00:39:54 +01:00
|
|
|
/*
|
|
|
|
* flatten_clausegroups_list
|
|
|
|
* Given a list of lists of RestrictInfos, flatten it to a list
|
|
|
|
* of RestrictInfos.
|
|
|
|
*
|
|
|
|
* This is used to flatten out the result of group_clauses_by_indexkey()
|
2005-04-25 03:30:14 +02:00
|
|
|
* to produce an indexclauses list.
|
2004-01-06 00:39:54 +01:00
|
|
|
*/
|
|
|
|
List *
|
|
|
|
flatten_clausegroups_list(List *clausegroups)
|
|
|
|
{
|
|
|
|
List *allclauses = NIL;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *l;
|
2004-01-06 00:39:54 +01:00
|
|
|
|
|
|
|
foreach(l, clausegroups)
|
2004-05-31 01:40:41 +02:00
|
|
|
allclauses = list_concat(allclauses, list_copy((List *) lfirst(l)));
|
2004-01-06 00:39:54 +01:00
|
|
|
return allclauses;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-07-23 05:34:49 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* ---- ROUTINES TO CHECK OPERANDS ----
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* match_index_to_operand()
|
|
|
|
* Generalized test for a match between an index's key
|
|
|
|
* and the operand on one side of a restriction or join clause.
|
2003-05-28 18:04:02 +02:00
|
|
|
*
|
|
|
|
* operand: the nodetree to be compared to the index
|
|
|
|
* indexcol: the column number of the index (counting from 0)
|
|
|
|
* index: the index of interest
|
1999-07-23 05:34:49 +02:00
|
|
|
*/
|
2005-04-12 01:06:57 +02:00
|
|
|
bool
|
2003-05-28 18:04:02 +02:00
|
|
|
match_index_to_operand(Node *operand,
|
|
|
|
int indexcol,
|
2000-01-09 01:26:47 +01:00
|
|
|
IndexOptInfo *index)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2003-05-28 18:04:02 +02:00
|
|
|
int indkey;
|
|
|
|
|
2000-08-13 04:50:35 +02:00
|
|
|
/*
|
2003-05-28 18:04:02 +02:00
|
|
|
* Ignore any RelabelType node above the operand. This is needed to
|
2000-08-13 04:50:35 +02:00
|
|
|
* be able to apply indexscanning in binary-compatible-operator cases.
|
|
|
|
* Note: we can assume there is at most one RelabelType node;
|
|
|
|
* eval_const_expressions() will have simplified if more than one.
|
|
|
|
*/
|
|
|
|
if (operand && IsA(operand, RelabelType))
|
2003-01-15 20:35:48 +01:00
|
|
|
operand = (Node *) ((RelabelType *) operand)->arg;
|
2000-08-13 04:50:35 +02:00
|
|
|
|
2003-05-28 18:04:02 +02:00
|
|
|
indkey = index->indexkeys[indexcol];
|
|
|
|
if (indkey != 0)
|
1999-07-23 05:34:49 +02:00
|
|
|
{
|
|
|
|
/*
|
2003-05-28 18:04:02 +02:00
|
|
|
* Simple index column; operand must be a matching Var.
|
1999-07-23 05:34:49 +02:00
|
|
|
*/
|
2000-08-13 04:50:35 +02:00
|
|
|
if (operand && IsA(operand, Var) &&
|
2005-03-27 08:29:49 +02:00
|
|
|
index->rel->relid == ((Var *) operand)->varno &&
|
2003-05-28 18:04:02 +02:00
|
|
|
indkey == ((Var *) operand)->varattno)
|
1999-08-16 04:17:58 +02:00
|
|
|
return true;
|
1999-07-23 05:34:49 +02:00
|
|
|
}
|
2003-05-28 18:04:02 +02:00
|
|
|
else
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2003-05-28 18:04:02 +02:00
|
|
|
/*
|
2003-08-04 02:43:34 +02:00
|
|
|
* Index expression; find the correct expression. (This search
|
|
|
|
* could be avoided, at the cost of complicating all the callers
|
|
|
|
* of this routine; doesn't seem worth it.)
|
2003-05-28 18:04:02 +02:00
|
|
|
*/
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *indexpr_item;
|
2003-05-28 18:04:02 +02:00
|
|
|
int i;
|
|
|
|
Node *indexkey;
|
1999-08-12 06:32:54 +02:00
|
|
|
|
2004-05-26 06:41:50 +02:00
|
|
|
indexpr_item = list_head(index->indexprs);
|
2003-05-28 18:04:02 +02:00
|
|
|
for (i = 0; i < indexcol; i++)
|
|
|
|
{
|
|
|
|
if (index->indexkeys[i] == 0)
|
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
if (indexpr_item == NULL)
|
2003-05-28 18:04:02 +02:00
|
|
|
elog(ERROR, "wrong number of index expressions");
|
2004-05-26 06:41:50 +02:00
|
|
|
indexpr_item = lnext(indexpr_item);
|
2003-05-28 18:04:02 +02:00
|
|
|
}
|
|
|
|
}
|
2004-05-26 06:41:50 +02:00
|
|
|
if (indexpr_item == NULL)
|
2003-05-28 18:04:02 +02:00
|
|
|
elog(ERROR, "wrong number of index expressions");
|
2004-05-26 06:41:50 +02:00
|
|
|
indexkey = (Node *) lfirst(indexpr_item);
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-05-28 18:04:02 +02:00
|
|
|
/*
|
|
|
|
* Does it match the operand? Again, strip any relabeling.
|
|
|
|
*/
|
|
|
|
if (indexkey && IsA(indexkey, RelabelType))
|
|
|
|
indexkey = (Node *) ((RelabelType *) indexkey)->arg;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-05-28 18:04:02 +02:00
|
|
|
if (equal(indexkey, operand))
|
|
|
|
return true;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
2003-05-28 18:04:02 +02:00
|
|
|
return false;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1999-07-27 05:51:11 +02:00
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* ---- ROUTINES FOR "SPECIAL" INDEXABLE OPERATORS ----
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/*----------
|
|
|
|
* These routines handle special optimization of operators that can be
|
|
|
|
* used with index scans even though they are not known to the executor's
|
|
|
|
* indexscan machinery. The key idea is that these operators allow us
|
|
|
|
* to derive approximate indexscan qual clauses, such that any tuples
|
|
|
|
* that pass the operator clause itself must also satisfy the simpler
|
2000-04-12 19:17:23 +02:00
|
|
|
* indexscan condition(s). Then we can use the indexscan machinery
|
1999-07-27 05:51:11 +02:00
|
|
|
* to avoid scanning as much of the table as we'd otherwise have to,
|
|
|
|
* while applying the original operator as a qpqual condition to ensure
|
2000-04-12 19:17:23 +02:00
|
|
|
* we deliver only the tuples we want. (In essence, we're using a regular
|
1999-07-27 05:51:11 +02:00
|
|
|
* index as if it were a lossy index.)
|
|
|
|
*
|
|
|
|
* An example of what we're doing is
|
|
|
|
* textfield LIKE 'abc%'
|
|
|
|
* from which we can generate the indexscanable conditions
|
|
|
|
* textfield >= 'abc' AND textfield < 'abd'
|
|
|
|
* which allow efficient scanning of an index on textfield.
|
|
|
|
* (In reality, character set and collation issues make the transformation
|
|
|
|
* from LIKE to indexscan limits rather harder than one might think ...
|
|
|
|
* but that's the basic idea.)
|
|
|
|
*
|
2005-03-27 00:29:20 +01:00
|
|
|
* Another thing that we do with this machinery is to provide special
|
|
|
|
* smarts for "boolean" indexes (that is, indexes on boolean columns
|
|
|
|
* that support boolean equality). We can transform a plain reference
|
|
|
|
* to the indexkey into "indexkey = true", or "NOT indexkey" into
|
|
|
|
* "indexkey = false", so as to make the expression indexable using the
|
|
|
|
* regular index operators. (As of Postgres 8.1, we must do this here
|
|
|
|
* because constant simplification does the reverse transformation;
|
|
|
|
* without this code there'd be no way to use such an index at all.)
|
|
|
|
*
|
|
|
|
* Three routines are provided here:
|
|
|
|
*
|
|
|
|
* match_special_index_operator() is just an auxiliary function for
|
|
|
|
* match_clause_to_indexcol(); after the latter fails to recognize a
|
|
|
|
* restriction opclause's operator as a member of an index's opclass,
|
|
|
|
* it asks match_special_index_operator() whether the clause should be
|
|
|
|
* considered an indexqual anyway.
|
|
|
|
*
|
|
|
|
* match_boolean_index_clause() similarly detects clauses that can be
|
|
|
|
* converted into boolean equality operators.
|
|
|
|
*
|
2003-05-26 02:11:29 +02:00
|
|
|
* expand_indexqual_conditions() converts a list of lists of RestrictInfo
|
|
|
|
* nodes (with implicit AND semantics across list elements) into
|
|
|
|
* a list of clauses that the executor can actually handle. For operators
|
|
|
|
* that are members of the index's opclass this transformation is a no-op,
|
2005-03-27 00:29:20 +01:00
|
|
|
* but clauses recognized by match_special_index_operator() or
|
|
|
|
* match_boolean_index_clause() must be converted into one or more "regular"
|
|
|
|
* indexqual conditions.
|
1999-07-27 05:51:11 +02:00
|
|
|
*----------
|
|
|
|
*/
|
|
|
|
|
2005-03-27 00:29:20 +01:00
|
|
|
/*
|
|
|
|
* match_boolean_index_clause
|
|
|
|
* Recognize restriction clauses that can be matched to a boolean index.
|
|
|
|
*
|
|
|
|
* This should be called only when IsBooleanOpclass() recognizes the
|
|
|
|
* index's operator class. We check to see if the clause matches the
|
|
|
|
* index's key.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
match_boolean_index_clause(Node *clause,
|
|
|
|
int indexcol,
|
|
|
|
IndexOptInfo *index)
|
|
|
|
{
|
|
|
|
/* Direct match? */
|
2005-03-27 08:29:49 +02:00
|
|
|
if (match_index_to_operand(clause, indexcol, index))
|
2005-03-27 00:29:20 +01:00
|
|
|
return true;
|
|
|
|
/* NOT clause? */
|
|
|
|
if (not_clause(clause))
|
|
|
|
{
|
|
|
|
if (match_index_to_operand((Node *) get_notclausearg((Expr *) clause),
|
2005-03-27 08:29:49 +02:00
|
|
|
indexcol, index))
|
2005-03-27 00:29:20 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Since we only consider clauses at top level of WHERE, we can convert
|
|
|
|
* indexkey IS TRUE and indexkey IS FALSE to index searches as well.
|
|
|
|
* The different meaning for NULL isn't important.
|
|
|
|
*/
|
|
|
|
else if (clause && IsA(clause, BooleanTest))
|
|
|
|
{
|
|
|
|
BooleanTest *btest = (BooleanTest *) clause;
|
|
|
|
|
|
|
|
if (btest->booltesttype == IS_TRUE ||
|
|
|
|
btest->booltesttype == IS_FALSE)
|
|
|
|
if (match_index_to_operand((Node *) btest->arg,
|
2005-03-27 08:29:49 +02:00
|
|
|
indexcol, index))
|
2005-03-27 00:29:20 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
1999-07-27 05:51:11 +02:00
|
|
|
/*
|
|
|
|
* match_special_index_operator
|
|
|
|
* Recognize restriction clauses that can be used to generate
|
|
|
|
* additional indexscanable qualifications.
|
|
|
|
*
|
|
|
|
* The given clause is already known to be a binary opclause having
|
2000-08-13 04:50:35 +02:00
|
|
|
* the form (indexkey OP pseudoconst) or (pseudoconst OP indexkey),
|
1999-07-27 05:51:11 +02:00
|
|
|
* but the OP proved not to be one of the index's opclass operators.
|
|
|
|
* Return 'true' if we can do something with it anyway.
|
|
|
|
*/
|
|
|
|
static bool
|
2001-08-21 18:36:06 +02:00
|
|
|
match_special_index_operator(Expr *clause, Oid opclass,
|
1999-12-31 04:41:03 +01:00
|
|
|
bool indexkey_on_left)
|
1999-07-27 05:51:11 +02:00
|
|
|
{
|
|
|
|
bool isIndexable = false;
|
2003-05-26 02:11:29 +02:00
|
|
|
Node *rightop;
|
1999-07-27 05:51:11 +02:00
|
|
|
Oid expr_op;
|
2003-05-26 02:11:29 +02:00
|
|
|
Const *patt;
|
2002-09-02 08:22:20 +02:00
|
|
|
Const *prefix = NULL;
|
|
|
|
Const *rest = NULL;
|
1999-07-27 05:51:11 +02:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
|
|
|
* Currently, all known special operators require the indexkey on the
|
|
|
|
* left, but this test could be pushed into the switch statement if
|
|
|
|
* some are added that do not...
|
1999-07-27 05:51:11 +02:00
|
|
|
*/
|
2000-04-12 19:17:23 +02:00
|
|
|
if (!indexkey_on_left)
|
1999-07-27 05:51:11 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
/* we know these will succeed */
|
|
|
|
rightop = get_rightop(clause);
|
2002-12-12 16:49:42 +01:00
|
|
|
expr_op = ((OpExpr *) clause)->opno;
|
1999-07-27 05:51:11 +02:00
|
|
|
|
|
|
|
/* again, required for all current special ops: */
|
2000-04-12 19:17:23 +02:00
|
|
|
if (!IsA(rightop, Const) ||
|
1999-07-27 05:51:11 +02:00
|
|
|
((Const *) rightop)->constisnull)
|
|
|
|
return false;
|
2002-09-02 08:22:20 +02:00
|
|
|
patt = (Const *) rightop;
|
1999-07-27 05:51:11 +02:00
|
|
|
|
|
|
|
switch (expr_op)
|
|
|
|
{
|
|
|
|
case OID_TEXT_LIKE_OP:
|
|
|
|
case OID_BPCHAR_LIKE_OP:
|
|
|
|
case OID_NAME_LIKE_OP:
|
2002-09-02 08:22:20 +02:00
|
|
|
/* the right-hand const is type text for all of these */
|
2003-05-15 17:50:21 +02:00
|
|
|
isIndexable = pattern_fixed_prefix(patt, Pattern_Type_Like,
|
2003-08-04 02:43:34 +02:00
|
|
|
&prefix, &rest) != Pattern_Prefix_None;
|
2002-09-02 08:22:20 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OID_BYTEA_LIKE_OP:
|
|
|
|
isIndexable = pattern_fixed_prefix(patt, Pattern_Type_Like,
|
2003-08-04 02:43:34 +02:00
|
|
|
&prefix, &rest) != Pattern_Prefix_None;
|
1999-07-27 05:51:11 +02:00
|
|
|
break;
|
|
|
|
|
2000-09-15 20:45:31 +02:00
|
|
|
case OID_TEXT_ICLIKE_OP:
|
|
|
|
case OID_BPCHAR_ICLIKE_OP:
|
|
|
|
case OID_NAME_ICLIKE_OP:
|
2002-09-02 08:22:20 +02:00
|
|
|
/* the right-hand const is type text for all of these */
|
2003-05-15 17:50:21 +02:00
|
|
|
isIndexable = pattern_fixed_prefix(patt, Pattern_Type_Like_IC,
|
2003-08-04 02:43:34 +02:00
|
|
|
&prefix, &rest) != Pattern_Prefix_None;
|
2000-09-15 20:45:31 +02:00
|
|
|
break;
|
|
|
|
|
1999-07-27 05:51:11 +02:00
|
|
|
case OID_TEXT_REGEXEQ_OP:
|
|
|
|
case OID_BPCHAR_REGEXEQ_OP:
|
|
|
|
case OID_NAME_REGEXEQ_OP:
|
2002-09-02 08:22:20 +02:00
|
|
|
/* the right-hand const is type text for all of these */
|
2003-05-15 17:50:21 +02:00
|
|
|
isIndexable = pattern_fixed_prefix(patt, Pattern_Type_Regex,
|
2003-08-04 02:43:34 +02:00
|
|
|
&prefix, &rest) != Pattern_Prefix_None;
|
1999-07-27 05:51:11 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OID_TEXT_ICREGEXEQ_OP:
|
|
|
|
case OID_BPCHAR_ICREGEXEQ_OP:
|
|
|
|
case OID_NAME_ICREGEXEQ_OP:
|
2002-09-02 08:22:20 +02:00
|
|
|
/* the right-hand const is type text for all of these */
|
2003-05-15 17:50:21 +02:00
|
|
|
isIndexable = pattern_fixed_prefix(patt, Pattern_Type_Regex_IC,
|
2003-08-04 02:43:34 +02:00
|
|
|
&prefix, &rest) != Pattern_Prefix_None;
|
1999-07-27 05:51:11 +02:00
|
|
|
break;
|
2001-06-17 04:05:20 +02:00
|
|
|
|
|
|
|
case OID_INET_SUB_OP:
|
|
|
|
case OID_INET_SUBEQ_OP:
|
|
|
|
case OID_CIDR_SUB_OP:
|
|
|
|
case OID_CIDR_SUBEQ_OP:
|
|
|
|
isIndexable = true;
|
|
|
|
break;
|
1999-07-27 05:51:11 +02:00
|
|
|
}
|
|
|
|
|
2002-09-02 08:22:20 +02:00
|
|
|
if (prefix)
|
|
|
|
{
|
|
|
|
pfree(DatumGetPointer(prefix->constvalue));
|
|
|
|
pfree(prefix);
|
|
|
|
}
|
|
|
|
|
1999-12-31 04:41:03 +01:00
|
|
|
/* done if the expression doesn't look indexable */
|
2000-04-12 19:17:23 +02:00
|
|
|
if (!isIndexable)
|
1999-12-31 04:41:03 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Must also check that index's opclass supports the operators we will
|
|
|
|
* want to apply. (A hash index, for example, will not support ">=".)
|
2003-05-26 02:11:29 +02:00
|
|
|
* Currently, only btree supports the operators we need.
|
|
|
|
*
|
2003-08-04 02:43:34 +02:00
|
|
|
* We insist on the opclass being the specific one we expect, else we'd
|
|
|
|
* do the wrong thing if someone were to make a reverse-sort opclass
|
|
|
|
* with the same operators.
|
1999-12-31 04:41:03 +01:00
|
|
|
*/
|
|
|
|
switch (expr_op)
|
|
|
|
{
|
|
|
|
case OID_TEXT_LIKE_OP:
|
2000-09-15 20:45:31 +02:00
|
|
|
case OID_TEXT_ICLIKE_OP:
|
1999-12-31 04:41:03 +01:00
|
|
|
case OID_TEXT_REGEXEQ_OP:
|
|
|
|
case OID_TEXT_ICREGEXEQ_OP:
|
2003-05-26 02:11:29 +02:00
|
|
|
/* text operators will be used for varchar inputs, too */
|
|
|
|
isIndexable =
|
|
|
|
(opclass == TEXT_PATTERN_BTREE_OPS_OID) ||
|
|
|
|
(opclass == TEXT_BTREE_OPS_OID && lc_collate_is_c()) ||
|
|
|
|
(opclass == VARCHAR_PATTERN_BTREE_OPS_OID) ||
|
|
|
|
(opclass == VARCHAR_BTREE_OPS_OID && lc_collate_is_c());
|
2002-09-02 08:22:20 +02:00
|
|
|
break;
|
|
|
|
|
1999-12-31 04:41:03 +01:00
|
|
|
case OID_BPCHAR_LIKE_OP:
|
2000-09-15 20:45:31 +02:00
|
|
|
case OID_BPCHAR_ICLIKE_OP:
|
1999-12-31 04:41:03 +01:00
|
|
|
case OID_BPCHAR_REGEXEQ_OP:
|
|
|
|
case OID_BPCHAR_ICREGEXEQ_OP:
|
2003-05-26 02:11:29 +02:00
|
|
|
isIndexable =
|
|
|
|
(opclass == BPCHAR_PATTERN_BTREE_OPS_OID) ||
|
|
|
|
(opclass == BPCHAR_BTREE_OPS_OID && lc_collate_is_c());
|
1999-12-31 04:41:03 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OID_NAME_LIKE_OP:
|
2000-09-15 20:45:31 +02:00
|
|
|
case OID_NAME_ICLIKE_OP:
|
1999-12-31 04:41:03 +01:00
|
|
|
case OID_NAME_REGEXEQ_OP:
|
|
|
|
case OID_NAME_ICREGEXEQ_OP:
|
2003-05-26 02:11:29 +02:00
|
|
|
isIndexable =
|
|
|
|
(opclass == NAME_PATTERN_BTREE_OPS_OID) ||
|
|
|
|
(opclass == NAME_BTREE_OPS_OID && lc_collate_is_c());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OID_BYTEA_LIKE_OP:
|
|
|
|
isIndexable = (opclass == BYTEA_BTREE_OPS_OID);
|
1999-12-31 04:41:03 +01:00
|
|
|
break;
|
2001-06-17 04:05:20 +02:00
|
|
|
|
|
|
|
case OID_INET_SUB_OP:
|
|
|
|
case OID_INET_SUBEQ_OP:
|
2003-05-26 02:11:29 +02:00
|
|
|
isIndexable = (opclass == INET_BTREE_OPS_OID);
|
2001-06-17 04:05:20 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OID_CIDR_SUB_OP:
|
|
|
|
case OID_CIDR_SUBEQ_OP:
|
2003-05-26 02:11:29 +02:00
|
|
|
isIndexable = (opclass == CIDR_BTREE_OPS_OID);
|
2001-06-17 04:05:20 +02:00
|
|
|
break;
|
1999-12-31 04:41:03 +01:00
|
|
|
}
|
|
|
|
|
1999-07-27 05:51:11 +02:00
|
|
|
return isIndexable;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* expand_indexqual_conditions
|
2003-05-26 02:11:29 +02:00
|
|
|
* Given a list of sublists of RestrictInfo nodes, produce a flat list
|
|
|
|
* of index qual clauses. Standard qual clauses (those in the index's
|
2005-03-27 00:29:20 +01:00
|
|
|
* opclass) are passed through unchanged. Boolean clauses and "special"
|
|
|
|
* index operators are expanded into clauses that the indexscan machinery
|
|
|
|
* will know what to do with.
|
2003-05-26 02:11:29 +02:00
|
|
|
*
|
|
|
|
* The input list is ordered by index key, and so the output list is too.
|
2005-06-14 01:14:49 +02:00
|
|
|
* (The latter is not depended on by any part of the core planner, I believe,
|
|
|
|
* but parts of the executor require it, and so do the amcostestimate
|
|
|
|
* functions.)
|
1999-07-27 05:51:11 +02:00
|
|
|
*/
|
|
|
|
List *
|
2005-03-27 08:29:49 +02:00
|
|
|
expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
|
1999-07-27 05:51:11 +02:00
|
|
|
{
|
2004-06-01 06:47:46 +02:00
|
|
|
List *resultquals = NIL;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *clausegroup_item;
|
2005-03-27 00:29:20 +01:00
|
|
|
int indexcol = 0;
|
2003-05-26 02:11:29 +02:00
|
|
|
Oid *classes = index->classlist;
|
|
|
|
|
|
|
|
if (clausegroups == NIL)
|
|
|
|
return NIL;
|
1999-07-27 05:51:11 +02:00
|
|
|
|
2004-05-26 06:41:50 +02:00
|
|
|
clausegroup_item = list_head(clausegroups);
|
2003-05-26 02:11:29 +02:00
|
|
|
do
|
1999-07-27 05:51:11 +02:00
|
|
|
{
|
2003-05-26 02:11:29 +02:00
|
|
|
Oid curClass = classes[0];
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *l;
|
2003-05-26 02:11:29 +02:00
|
|
|
|
2004-05-26 06:41:50 +02:00
|
|
|
foreach(l, (List *) lfirst(clausegroup_item))
|
1999-07-27 05:51:11 +02:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
|
1999-07-27 05:51:11 +02:00
|
|
|
|
2005-03-27 00:29:20 +01:00
|
|
|
/* First check for boolean cases */
|
|
|
|
if (IsBooleanOpclass(curClass))
|
|
|
|
{
|
|
|
|
Expr *boolqual;
|
|
|
|
|
|
|
|
boolqual = expand_boolean_index_clause((Node *) rinfo->clause,
|
|
|
|
indexcol,
|
|
|
|
index);
|
|
|
|
if (boolqual)
|
|
|
|
{
|
|
|
|
resultquals = lappend(resultquals,
|
|
|
|
make_restrictinfo(boolqual,
|
2005-06-09 06:19:00 +02:00
|
|
|
true, true,
|
|
|
|
NULL));
|
2005-03-27 00:29:20 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-06-01 06:47:46 +02:00
|
|
|
resultquals = list_concat(resultquals,
|
|
|
|
expand_indexqual_condition(rinfo,
|
2005-03-27 00:29:20 +01:00
|
|
|
curClass));
|
2003-05-26 02:11:29 +02:00
|
|
|
}
|
2000-09-15 20:45:31 +02:00
|
|
|
|
2004-05-26 06:41:50 +02:00
|
|
|
clausegroup_item = lnext(clausegroup_item);
|
2005-03-27 00:29:20 +01:00
|
|
|
|
|
|
|
indexcol++;
|
2003-05-26 02:11:29 +02:00
|
|
|
classes++;
|
2004-05-26 06:41:50 +02:00
|
|
|
} while (clausegroup_item != NULL && !DoneMatchingIndexKeys(classes));
|
1999-07-27 05:51:11 +02:00
|
|
|
|
2004-06-01 06:47:46 +02:00
|
|
|
Assert(clausegroup_item == NULL); /* else more groups than indexkeys */
|
1999-07-27 05:51:11 +02:00
|
|
|
|
2004-06-01 06:47:46 +02:00
|
|
|
return resultquals;
|
1999-07-27 05:51:11 +02:00
|
|
|
}
|
|
|
|
|
2005-03-27 00:29:20 +01:00
|
|
|
/*
|
|
|
|
* expand_boolean_index_clause
|
|
|
|
* Convert a clause recognized by match_boolean_index_clause into
|
|
|
|
* a boolean equality operator clause.
|
|
|
|
*
|
|
|
|
* Returns NULL if the clause isn't a boolean index qual.
|
|
|
|
*/
|
|
|
|
static Expr *
|
|
|
|
expand_boolean_index_clause(Node *clause,
|
|
|
|
int indexcol,
|
|
|
|
IndexOptInfo *index)
|
|
|
|
{
|
|
|
|
/* Direct match? */
|
2005-03-27 08:29:49 +02:00
|
|
|
if (match_index_to_operand(clause, indexcol, index))
|
2005-03-27 00:29:20 +01:00
|
|
|
{
|
|
|
|
/* convert to indexkey = TRUE */
|
|
|
|
return make_opclause(BooleanEqualOperator, BOOLOID, false,
|
|
|
|
(Expr *) clause,
|
|
|
|
(Expr *) makeBoolConst(true, false));
|
|
|
|
}
|
|
|
|
/* NOT clause? */
|
|
|
|
if (not_clause(clause))
|
|
|
|
{
|
|
|
|
Node *arg = (Node *) get_notclausearg((Expr *) clause);
|
|
|
|
|
|
|
|
/* It must have matched the indexkey */
|
2005-03-27 08:29:49 +02:00
|
|
|
Assert(match_index_to_operand(arg, indexcol, index));
|
2005-03-27 00:29:20 +01:00
|
|
|
/* convert to indexkey = FALSE */
|
|
|
|
return make_opclause(BooleanEqualOperator, BOOLOID, false,
|
|
|
|
(Expr *) arg,
|
|
|
|
(Expr *) makeBoolConst(false, false));
|
|
|
|
}
|
|
|
|
if (clause && IsA(clause, BooleanTest))
|
|
|
|
{
|
|
|
|
BooleanTest *btest = (BooleanTest *) clause;
|
|
|
|
Node *arg = (Node *) btest->arg;
|
|
|
|
|
|
|
|
/* It must have matched the indexkey */
|
2005-03-27 08:29:49 +02:00
|
|
|
Assert(match_index_to_operand(arg, indexcol, index));
|
2005-03-27 00:29:20 +01:00
|
|
|
if (btest->booltesttype == IS_TRUE)
|
|
|
|
{
|
|
|
|
/* convert to indexkey = TRUE */
|
|
|
|
return make_opclause(BooleanEqualOperator, BOOLOID, false,
|
|
|
|
(Expr *) arg,
|
|
|
|
(Expr *) makeBoolConst(true, false));
|
|
|
|
}
|
|
|
|
if (btest->booltesttype == IS_FALSE)
|
|
|
|
{
|
|
|
|
/* convert to indexkey = FALSE */
|
|
|
|
return make_opclause(BooleanEqualOperator, BOOLOID, false,
|
|
|
|
(Expr *) arg,
|
|
|
|
(Expr *) makeBoolConst(false, false));
|
|
|
|
}
|
|
|
|
/* Oops */
|
|
|
|
Assert(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2003-05-26 02:11:29 +02:00
|
|
|
/*
|
|
|
|
* expand_indexqual_condition --- expand a single indexqual condition
|
2005-03-27 00:29:20 +01:00
|
|
|
* (other than a boolean-qual case)
|
2004-01-06 00:39:54 +01:00
|
|
|
*
|
|
|
|
* The input is a single RestrictInfo, the output a list of RestrictInfos
|
2003-05-26 02:11:29 +02:00
|
|
|
*/
|
|
|
|
static List *
|
2004-01-06 00:39:54 +01:00
|
|
|
expand_indexqual_condition(RestrictInfo *rinfo, Oid opclass)
|
2003-05-26 02:11:29 +02:00
|
|
|
{
|
2004-01-06 00:39:54 +01:00
|
|
|
Expr *clause = rinfo->clause;
|
2003-05-26 02:11:29 +02:00
|
|
|
/* we know these will succeed */
|
|
|
|
Node *leftop = get_leftop(clause);
|
|
|
|
Node *rightop = get_rightop(clause);
|
|
|
|
Oid expr_op = ((OpExpr *) clause)->opno;
|
|
|
|
Const *patt = (Const *) rightop;
|
|
|
|
Const *prefix = NULL;
|
|
|
|
Const *rest = NULL;
|
|
|
|
Pattern_Prefix_Status pstatus;
|
|
|
|
List *result;
|
|
|
|
|
|
|
|
switch (expr_op)
|
|
|
|
{
|
2003-08-04 02:43:34 +02:00
|
|
|
/*
|
|
|
|
* LIKE and regex operators are not members of any index
|
|
|
|
* opclass, so if we find one in an indexqual list we can
|
|
|
|
* assume that it was accepted by
|
|
|
|
* match_special_index_operator().
|
|
|
|
*/
|
2003-05-26 02:11:29 +02:00
|
|
|
case OID_TEXT_LIKE_OP:
|
|
|
|
case OID_BPCHAR_LIKE_OP:
|
|
|
|
case OID_NAME_LIKE_OP:
|
|
|
|
case OID_BYTEA_LIKE_OP:
|
|
|
|
pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like,
|
|
|
|
&prefix, &rest);
|
|
|
|
result = prefix_quals(leftop, opclass, prefix, pstatus);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OID_TEXT_ICLIKE_OP:
|
|
|
|
case OID_BPCHAR_ICLIKE_OP:
|
|
|
|
case OID_NAME_ICLIKE_OP:
|
|
|
|
/* the right-hand const is type text for all of these */
|
|
|
|
pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like_IC,
|
|
|
|
&prefix, &rest);
|
|
|
|
result = prefix_quals(leftop, opclass, prefix, pstatus);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OID_TEXT_REGEXEQ_OP:
|
|
|
|
case OID_BPCHAR_REGEXEQ_OP:
|
|
|
|
case OID_NAME_REGEXEQ_OP:
|
|
|
|
/* the right-hand const is type text for all of these */
|
|
|
|
pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex,
|
|
|
|
&prefix, &rest);
|
|
|
|
result = prefix_quals(leftop, opclass, prefix, pstatus);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OID_TEXT_ICREGEXEQ_OP:
|
|
|
|
case OID_BPCHAR_ICREGEXEQ_OP:
|
|
|
|
case OID_NAME_ICREGEXEQ_OP:
|
|
|
|
/* the right-hand const is type text for all of these */
|
|
|
|
pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex_IC,
|
|
|
|
&prefix, &rest);
|
|
|
|
result = prefix_quals(leftop, opclass, prefix, pstatus);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OID_INET_SUB_OP:
|
|
|
|
case OID_INET_SUBEQ_OP:
|
|
|
|
case OID_CIDR_SUB_OP:
|
|
|
|
case OID_CIDR_SUBEQ_OP:
|
|
|
|
result = network_prefix_quals(leftop, expr_op, opclass,
|
|
|
|
patt->constvalue);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2004-05-31 01:40:41 +02:00
|
|
|
result = list_make1(rinfo);
|
2003-05-26 02:11:29 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-07-27 05:51:11 +02:00
|
|
|
/*
|
|
|
|
* Given a fixed prefix that all the "leftop" values must have,
|
2003-05-26 02:11:29 +02:00
|
|
|
* generate suitable indexqual condition(s). opclass is the index
|
|
|
|
* operator class; we use it to deduce the appropriate comparison
|
2003-03-23 02:49:02 +01:00
|
|
|
* operators and operand datatypes.
|
1999-07-27 05:51:11 +02:00
|
|
|
*/
|
|
|
|
static List *
|
2003-05-26 02:11:29 +02:00
|
|
|
prefix_quals(Node *leftop, Oid opclass,
|
2002-09-02 08:22:20 +02:00
|
|
|
Const *prefix_const, Pattern_Prefix_Status pstatus)
|
1999-07-27 05:51:11 +02:00
|
|
|
{
|
|
|
|
List *result;
|
|
|
|
Oid datatype;
|
1999-12-31 04:41:03 +01:00
|
|
|
Oid oproid;
|
1999-07-27 05:51:11 +02:00
|
|
|
Expr *expr;
|
2003-05-26 02:11:29 +02:00
|
|
|
Const *greaterstr;
|
1999-07-27 05:51:11 +02:00
|
|
|
|
2000-04-16 06:41:03 +02:00
|
|
|
Assert(pstatus != Pattern_Prefix_None);
|
1999-07-27 05:51:11 +02:00
|
|
|
|
2003-05-26 02:11:29 +02:00
|
|
|
switch (opclass)
|
1999-07-27 05:51:11 +02:00
|
|
|
{
|
2003-05-26 02:11:29 +02:00
|
|
|
case TEXT_BTREE_OPS_OID:
|
|
|
|
case TEXT_PATTERN_BTREE_OPS_OID:
|
1999-07-27 05:51:11 +02:00
|
|
|
datatype = TEXTOID;
|
|
|
|
break;
|
|
|
|
|
2003-05-26 02:11:29 +02:00
|
|
|
case VARCHAR_BTREE_OPS_OID:
|
|
|
|
case VARCHAR_PATTERN_BTREE_OPS_OID:
|
|
|
|
datatype = VARCHAROID;
|
2002-09-02 08:22:20 +02:00
|
|
|
break;
|
|
|
|
|
2003-05-26 02:11:29 +02:00
|
|
|
case BPCHAR_BTREE_OPS_OID:
|
|
|
|
case BPCHAR_PATTERN_BTREE_OPS_OID:
|
1999-07-27 05:51:11 +02:00
|
|
|
datatype = BPCHAROID;
|
|
|
|
break;
|
|
|
|
|
2003-05-26 02:11:29 +02:00
|
|
|
case NAME_BTREE_OPS_OID:
|
|
|
|
case NAME_PATTERN_BTREE_OPS_OID:
|
|
|
|
datatype = NAMEOID;
|
1999-07-27 05:51:11 +02:00
|
|
|
break;
|
|
|
|
|
2003-05-26 02:11:29 +02:00
|
|
|
case BYTEA_BTREE_OPS_OID:
|
|
|
|
datatype = BYTEAOID;
|
1999-07-27 05:51:11 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2003-07-25 02:01:09 +02:00
|
|
|
/* shouldn't get here */
|
|
|
|
elog(ERROR, "unexpected opclass: %u", opclass);
|
1999-07-27 05:51:11 +02:00
|
|
|
return NIL;
|
|
|
|
}
|
|
|
|
|
2003-05-26 02:11:29 +02:00
|
|
|
/*
|
2003-08-04 02:43:34 +02:00
|
|
|
* If necessary, coerce the prefix constant to the right type. The
|
|
|
|
* given prefix constant is either text or bytea type.
|
2003-05-26 02:11:29 +02:00
|
|
|
*/
|
|
|
|
if (prefix_const->consttype != datatype)
|
|
|
|
{
|
2003-08-04 02:43:34 +02:00
|
|
|
char *prefix;
|
2003-05-26 02:11:29 +02:00
|
|
|
|
|
|
|
switch (prefix_const->consttype)
|
|
|
|
{
|
|
|
|
case TEXTOID:
|
|
|
|
prefix = DatumGetCString(DirectFunctionCall1(textout,
|
2003-08-04 02:43:34 +02:00
|
|
|
prefix_const->constvalue));
|
2003-05-26 02:11:29 +02:00
|
|
|
break;
|
|
|
|
case BYTEAOID:
|
|
|
|
prefix = DatumGetCString(DirectFunctionCall1(byteaout,
|
2003-08-04 02:43:34 +02:00
|
|
|
prefix_const->constvalue));
|
2003-05-26 02:11:29 +02:00
|
|
|
break;
|
|
|
|
default:
|
2003-07-25 02:01:09 +02:00
|
|
|
elog(ERROR, "unexpected const type: %u",
|
2003-05-26 02:11:29 +02:00
|
|
|
prefix_const->consttype);
|
|
|
|
return NIL;
|
|
|
|
}
|
|
|
|
prefix_const = string_to_const(prefix, datatype);
|
|
|
|
pfree(prefix);
|
|
|
|
}
|
2002-09-02 08:22:20 +02:00
|
|
|
|
1999-07-27 05:51:11 +02:00
|
|
|
/*
|
|
|
|
* If we found an exact-match pattern, generate an "=" indexqual.
|
|
|
|
*/
|
2000-04-16 06:41:03 +02:00
|
|
|
if (pstatus == Pattern_Prefix_Exact)
|
1999-07-27 05:51:11 +02:00
|
|
|
{
|
2003-11-12 22:15:59 +01:00
|
|
|
oproid = get_opclass_member(opclass, InvalidOid,
|
|
|
|
BTEqualStrategyNumber);
|
1999-12-31 04:41:03 +01:00
|
|
|
if (oproid == InvalidOid)
|
2003-07-25 02:01:09 +02:00
|
|
|
elog(ERROR, "no = operator for opclass %u", opclass);
|
2002-12-12 16:49:42 +01:00
|
|
|
expr = make_opclause(oproid, BOOLOID, false,
|
2003-05-26 02:11:29 +02:00
|
|
|
(Expr *) leftop, (Expr *) prefix_const);
|
2005-06-09 06:19:00 +02:00
|
|
|
result = list_make1(make_restrictinfo(expr, true, true, NULL));
|
1999-07-27 05:51:11 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Otherwise, we have a nonempty required prefix of the values.
|
|
|
|
*
|
|
|
|
* We can always say "x >= prefix".
|
|
|
|
*/
|
2003-11-12 22:15:59 +01:00
|
|
|
oproid = get_opclass_member(opclass, InvalidOid,
|
|
|
|
BTGreaterEqualStrategyNumber);
|
1999-12-31 04:41:03 +01:00
|
|
|
if (oproid == InvalidOid)
|
2003-07-25 02:01:09 +02:00
|
|
|
elog(ERROR, "no >= operator for opclass %u", opclass);
|
2002-12-12 16:49:42 +01:00
|
|
|
expr = make_opclause(oproid, BOOLOID, false,
|
2003-05-26 02:11:29 +02:00
|
|
|
(Expr *) leftop, (Expr *) prefix_const);
|
2005-06-09 06:19:00 +02:00
|
|
|
result = list_make1(make_restrictinfo(expr, true, true, NULL));
|
1999-07-27 05:51:11 +02:00
|
|
|
|
2001-03-23 05:49:58 +01:00
|
|
|
/*-------
|
|
|
|
* If we can create a string larger than the prefix, we can say
|
|
|
|
* "x < greaterstr".
|
|
|
|
*-------
|
1999-07-27 05:51:11 +02:00
|
|
|
*/
|
2003-05-26 02:11:29 +02:00
|
|
|
greaterstr = make_greater_string(prefix_const);
|
1999-12-31 06:38:25 +01:00
|
|
|
if (greaterstr)
|
|
|
|
{
|
2003-11-12 22:15:59 +01:00
|
|
|
oproid = get_opclass_member(opclass, InvalidOid,
|
|
|
|
BTLessStrategyNumber);
|
1999-12-31 06:38:25 +01:00
|
|
|
if (oproid == InvalidOid)
|
2003-07-25 02:01:09 +02:00
|
|
|
elog(ERROR, "no < operator for opclass %u", opclass);
|
2002-12-12 16:49:42 +01:00
|
|
|
expr = make_opclause(oproid, BOOLOID, false,
|
|
|
|
(Expr *) leftop, (Expr *) greaterstr);
|
2005-06-09 06:19:00 +02:00
|
|
|
result = lappend(result, make_restrictinfo(expr, true, true, NULL));
|
1999-12-31 06:38:25 +01:00
|
|
|
}
|
1999-07-27 05:51:11 +02:00
|
|
|
|
1999-12-31 06:38:25 +01:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2001-06-17 04:05:20 +02:00
|
|
|
/*
|
|
|
|
* Given a leftop and a rightop, and a inet-class sup/sub operator,
|
|
|
|
* generate suitable indexqual condition(s). expr_op is the original
|
2003-05-26 02:11:29 +02:00
|
|
|
* operator, and opclass is the index opclass.
|
2001-06-17 04:05:20 +02:00
|
|
|
*/
|
|
|
|
static List *
|
2003-05-26 02:11:29 +02:00
|
|
|
network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass, Datum rightop)
|
2001-06-17 04:05:20 +02:00
|
|
|
{
|
2001-10-25 07:50:21 +02:00
|
|
|
bool is_eq;
|
2003-05-26 02:11:29 +02:00
|
|
|
Oid datatype;
|
2001-10-25 07:50:21 +02:00
|
|
|
Oid opr1oid;
|
|
|
|
Oid opr2oid;
|
2003-05-26 02:11:29 +02:00
|
|
|
Datum opr1right;
|
|
|
|
Datum opr2right;
|
2001-06-17 04:05:20 +02:00
|
|
|
List *result;
|
|
|
|
Expr *expr;
|
|
|
|
|
|
|
|
switch (expr_op)
|
|
|
|
{
|
|
|
|
case OID_INET_SUB_OP:
|
2001-10-25 07:50:21 +02:00
|
|
|
datatype = INETOID;
|
|
|
|
is_eq = false;
|
2001-06-17 04:05:20 +02:00
|
|
|
break;
|
|
|
|
case OID_INET_SUBEQ_OP:
|
2001-10-25 07:50:21 +02:00
|
|
|
datatype = INETOID;
|
|
|
|
is_eq = true;
|
2001-06-17 04:05:20 +02:00
|
|
|
break;
|
|
|
|
case OID_CIDR_SUB_OP:
|
2001-10-25 07:50:21 +02:00
|
|
|
datatype = CIDROID;
|
|
|
|
is_eq = false;
|
2001-06-17 04:05:20 +02:00
|
|
|
break;
|
|
|
|
case OID_CIDR_SUBEQ_OP:
|
2001-10-25 07:50:21 +02:00
|
|
|
datatype = CIDROID;
|
|
|
|
is_eq = true;
|
2001-06-17 04:05:20 +02:00
|
|
|
break;
|
|
|
|
default:
|
2003-07-25 02:01:09 +02:00
|
|
|
elog(ERROR, "unexpected operator: %u", expr_op);
|
2001-06-17 04:05:20 +02:00
|
|
|
return NIL;
|
2001-10-25 07:50:21 +02:00
|
|
|
}
|
2001-06-17 04:05:20 +02:00
|
|
|
|
|
|
|
/*
|
2001-10-25 07:50:21 +02:00
|
|
|
* create clause "key >= network_scan_first( rightop )", or ">" if the
|
|
|
|
* operator disallows equality.
|
2001-06-17 04:05:20 +02:00
|
|
|
*/
|
2003-05-26 02:11:29 +02:00
|
|
|
if (is_eq)
|
|
|
|
{
|
2003-11-12 22:15:59 +01:00
|
|
|
opr1oid = get_opclass_member(opclass, InvalidOid,
|
|
|
|
BTGreaterEqualStrategyNumber);
|
2003-05-26 02:11:29 +02:00
|
|
|
if (opr1oid == InvalidOid)
|
2003-07-25 02:01:09 +02:00
|
|
|
elog(ERROR, "no >= operator for opclass %u", opclass);
|
2003-05-26 02:11:29 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2003-11-12 22:15:59 +01:00
|
|
|
opr1oid = get_opclass_member(opclass, InvalidOid,
|
|
|
|
BTGreaterStrategyNumber);
|
2003-05-26 02:11:29 +02:00
|
|
|
if (opr1oid == InvalidOid)
|
2003-07-25 02:01:09 +02:00
|
|
|
elog(ERROR, "no > operator for opclass %u", opclass);
|
2003-05-26 02:11:29 +02:00
|
|
|
}
|
2001-06-17 04:05:20 +02:00
|
|
|
|
2001-10-25 07:50:21 +02:00
|
|
|
opr1right = network_scan_first(rightop);
|
2001-06-17 04:05:20 +02:00
|
|
|
|
2002-12-12 16:49:42 +01:00
|
|
|
expr = make_opclause(opr1oid, BOOLOID, false,
|
|
|
|
(Expr *) leftop,
|
|
|
|
(Expr *) makeConst(datatype, -1, opr1right,
|
|
|
|
false, false));
|
2005-06-09 06:19:00 +02:00
|
|
|
result = list_make1(make_restrictinfo(expr, true, true, NULL));
|
2001-06-17 04:05:20 +02:00
|
|
|
|
|
|
|
/* create clause "key <= network_scan_last( rightop )" */
|
|
|
|
|
2003-11-12 22:15:59 +01:00
|
|
|
opr2oid = get_opclass_member(opclass, InvalidOid,
|
|
|
|
BTLessEqualStrategyNumber);
|
2001-06-17 04:05:20 +02:00
|
|
|
if (opr2oid == InvalidOid)
|
2003-07-25 02:01:09 +02:00
|
|
|
elog(ERROR, "no <= operator for opclass %u", opclass);
|
2001-06-17 04:05:20 +02:00
|
|
|
|
2001-10-25 07:50:21 +02:00
|
|
|
opr2right = network_scan_last(rightop);
|
2001-06-17 04:05:20 +02:00
|
|
|
|
2002-12-12 16:49:42 +01:00
|
|
|
expr = make_opclause(opr2oid, BOOLOID, false,
|
|
|
|
(Expr *) leftop,
|
|
|
|
(Expr *) makeConst(datatype, -1, opr2right,
|
|
|
|
false, false));
|
2005-06-09 06:19:00 +02:00
|
|
|
result = lappend(result, make_restrictinfo(expr, true, true, NULL));
|
2001-06-17 04:05:20 +02:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
1999-12-31 06:38:25 +01:00
|
|
|
/*
|
|
|
|
* Handy subroutines for match_special_index_operator() and friends.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generate a Datum of the appropriate type from a C string.
|
|
|
|
* Note that all of the supported types are pass-by-ref, so the
|
|
|
|
* returned value should be pfree'd if no longer needed.
|
|
|
|
*/
|
|
|
|
static Datum
|
2000-04-12 19:17:23 +02:00
|
|
|
string_to_datum(const char *str, Oid datatype)
|
1999-12-31 06:38:25 +01:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
|
|
|
* We cheat a little by assuming that textin() will do for bpchar and
|
|
|
|
* varchar constants too...
|
1999-12-31 06:38:25 +01:00
|
|
|
*/
|
|
|
|
if (datatype == NAMEOID)
|
2000-08-03 18:35:08 +02:00
|
|
|
return DirectFunctionCall1(namein, CStringGetDatum(str));
|
2002-09-02 08:22:20 +02:00
|
|
|
else if (datatype == BYTEAOID)
|
|
|
|
return DirectFunctionCall1(byteain, CStringGetDatum(str));
|
1999-12-31 06:38:25 +01:00
|
|
|
else
|
2000-07-06 01:12:09 +02:00
|
|
|
return DirectFunctionCall1(textin, CStringGetDatum(str));
|
1999-12-31 06:38:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generate a Const node of the appropriate type from a C string.
|
|
|
|
*/
|
|
|
|
static Const *
|
2000-04-12 19:17:23 +02:00
|
|
|
string_to_const(const char *str, Oid datatype)
|
1999-12-31 06:38:25 +01:00
|
|
|
{
|
|
|
|
Datum conval = string_to_datum(str, datatype);
|
|
|
|
|
|
|
|
return makeConst(datatype, ((datatype == NAMEOID) ? NAMEDATALEN : -1),
|
2002-11-25 22:29:42 +01:00
|
|
|
conval, false, false);
|
1999-12-31 06:38:25 +01:00
|
|
|
}
|