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-05-06 19:24:55 +02:00
|
|
|
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.180 2005/05/06 17:24:54 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>
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "access/nbtree.h"
|
1998-04-27 06:08:07 +02:00
|
|
|
#include "catalog/pg_amop.h"
|
2002-04-17 01:08:12 +02:00
|
|
|
#include "catalog/pg_namespace.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"
|
2004-03-27 01:24:28 +01:00
|
|
|
#include "catalog/pg_proc.h"
|
2003-05-13 06:38:58 +02:00
|
|
|
#include "catalog/pg_type.h"
|
1998-04-27 06:08:07 +02:00
|
|
|
#include "executor/executor.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"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "optimizer/restrictinfo.h"
|
1999-08-12 06:32:54 +02:00
|
|
|
#include "optimizer/var.h"
|
2003-05-26 02:11:29 +02:00
|
|
|
#include "parser/parse_expr.h"
|
2002-07-13 21:20:34 +02:00
|
|
|
#include "rewrite/rewriteManip.h"
|
1999-07-27 05:51:11 +02:00
|
|
|
#include "utils/builtins.h"
|
2003-05-13 06:38:58 +02:00
|
|
|
#include "utils/catcache.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"
|
1999-07-27 05:51:11 +02:00
|
|
|
#include "utils/syscache.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-04-22 23:58:32 +02:00
|
|
|
static List *find_usable_indexes(Query *root, RelOptInfo *rel,
|
|
|
|
List *clauses, List *outer_clauses,
|
|
|
|
bool istoplevel, bool isjoininner,
|
|
|
|
Relids outer_relids);
|
|
|
|
static Path *choose_bitmap_and(Query *root, RelOptInfo *rel, List *paths);
|
2005-04-23 03:57:34 +02:00
|
|
|
static int bitmap_path_comparator(const void *a, const void *b);
|
|
|
|
static Cost bitmap_and_cost_est(Query *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-03-02 05:10:53 +01:00
|
|
|
static bool pred_test_recurse(Node *clause, Node *predicate);
|
2001-08-06 20:09:45 +02:00
|
|
|
static bool pred_test_simple_clause(Expr *predicate, Node *clause);
|
2005-04-22 23:58:32 +02:00
|
|
|
static Relids indexable_outerrelids(RelOptInfo *rel);
|
|
|
|
static bool list_matches_any_index(List *clauses, RelOptInfo *rel,
|
|
|
|
Relids outer_relids);
|
|
|
|
static bool matches_any_index(RestrictInfo *rinfo, RelOptInfo *rel,
|
|
|
|
Relids outer_relids);
|
|
|
|
static List *find_clauses_for_join(Query *root, RelOptInfo *rel,
|
|
|
|
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,
|
|
|
|
* or match the query's ORDER BY 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
|
2001-05-20 22:28:20 +02:00
|
|
|
create_index_paths(Query *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
|
|
|
|
* '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 *
|
|
|
|
find_usable_indexes(Query *root, RelOptInfo *rel,
|
|
|
|
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);
|
|
|
|
|
|
|
|
if (!pred_test(index->indpred, all_clauses) ||
|
|
|
|
pred_test(index->indpred, outer_clauses))
|
|
|
|
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.
|
|
|
|
* We assume here that the AM does so if and only if it supports
|
|
|
|
* ordered scans. (It would probably be better if there were a
|
|
|
|
* specific flag for this in pg_am, but there's not.)
|
2000-02-15 21:49:31 +01:00
|
|
|
*/
|
2001-07-16 07:07:00 +02:00
|
|
|
if (restrictclauses != NIL ||
|
|
|
|
useful_pathkeys != NIL ||
|
2005-04-20 23:48:04 +02:00
|
|
|
(index->indpred != NIL && index_is_ordered))
|
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-04-22 23:58:32 +02:00
|
|
|
generate_bitmap_or_paths(Query *root, RelOptInfo *rel,
|
|
|
|
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 *
|
|
|
|
choose_bitmap_and(Query *root, RelOptInfo *rel, List *paths)
|
|
|
|
{
|
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
|
|
|
|
* already used by earlier indexes. (We could use pred_test() to have
|
|
|
|
* a more intelligent, but much more expensive, check --- but in most
|
2005-04-25 05:58:30 +02:00
|
|
|
* cases simple pointer equality should suffice, since after all the
|
|
|
|
* 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
|
|
|
|
bitmap_and_cost_est(Query *root, RelOptInfo *rel, List *paths)
|
|
|
|
{
|
|
|
|
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-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().
|
|
|
|
*
|
2003-05-26 02:11:29 +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().)
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1999-07-23 05:34:49 +02:00
|
|
|
* Note that in a multi-key index, we stop if we find a key that cannot be
|
|
|
|
* used with any clause. For example, given an index on (A,B,C), we might
|
2003-05-26 02:11:29 +02:00
|
|
|
* return ((C1 C2) (C3 C4)) if we find that clauses C1 and C2 use column A,
|
1999-07-30 06:07:25 +02:00
|
|
|
* clauses C3 and C4 use column B, and no clauses use column C. But if
|
2003-05-26 02:11:29 +02:00
|
|
|
* no clauses match B we will return ((C1 C2)), whether or not there are
|
1999-07-23 05:34:49 +02:00
|
|
|
* clauses matching column C, because the executor couldn't use them anyway.
|
2003-05-26 02:11:29 +02:00
|
|
|
* Therefore, there are no empty sublists in the result.
|
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
|
|
|
/*
|
|
|
|
* If no clauses match this key, we're done; we don't want to look
|
|
|
|
* at keys to its right.
|
1999-07-23 05:34:49 +02:00
|
|
|
*/
|
2004-06-01 06:47:46 +02:00
|
|
|
if (clausegroup == NIL)
|
1997-09-07 07:04:48 +02:00
|
|
|
break;
|
|
|
|
|
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
|
|
|
|
check_partial_indexes(Query *root, RelOptInfo *rel)
|
|
|
|
{
|
|
|
|
List *restrictinfo_list = rel->baserestrictinfo;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *ilist;
|
2004-01-05 06:07:36 +01:00
|
|
|
|
|
|
|
foreach(ilist, rel->indexlist)
|
|
|
|
{
|
|
|
|
IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If this is a partial index, we can only use it if it passes the
|
|
|
|
* predicate test.
|
|
|
|
*/
|
|
|
|
if (index->indpred == NIL)
|
|
|
|
continue; /* ignore non-partial indexes */
|
|
|
|
|
|
|
|
index->predOK = pred_test(index->indpred, restrictinfo_list);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1999-02-14 00:22:53 +01:00
|
|
|
* pred_test
|
1997-09-07 07:04:48 +02:00
|
|
|
* Does the "predicate inclusion test" for partial indexes.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1999-02-03 21:15:53 +01:00
|
|
|
* Recursively checks whether the clauses in restrictinfo_list imply
|
1997-09-07 07:04:48 +02:00
|
|
|
* that the given predicate is true.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2005-03-01 01:24:52 +01:00
|
|
|
* The top-level List structure of each list corresponds to an AND list.
|
2005-03-28 02:58:26 +02:00
|
|
|
* We assume that eval_const_expressions() has been applied and so there
|
|
|
|
* are no un-flattened ANDs or ORs (e.g., no AND immediately within an AND,
|
2005-03-02 05:10:53 +01:00
|
|
|
* including AND just below the top-level List structure).
|
|
|
|
* If this is not true we might fail to prove an implication that is
|
|
|
|
* valid, but no worse consequences will ensue.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2004-10-12 00:57:00 +02:00
|
|
|
bool
|
2004-01-05 06:07:36 +01:00
|
|
|
pred_test(List *predicate_list, List *restrictinfo_list)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2005-03-02 05:10:53 +01:00
|
|
|
ListCell *item;
|
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
|
|
|
|
* here with joininfo_list to do more complete tests for the usability
|
|
|
|
* of a partial index. For now, the test only uses restriction
|
1999-02-03 21:15:53 +01:00
|
|
|
* clauses (those in restrictinfo_list). --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
|
|
|
*/
|
|
|
|
|
2001-07-16 07:07:00 +02:00
|
|
|
if (predicate_list == NIL)
|
1997-09-07 07:04:48 +02:00
|
|
|
return true; /* no predicate: the index is usable */
|
2001-07-16 07:07:00 +02:00
|
|
|
if (restrictinfo_list == NIL)
|
1997-09-07 07:04:48 +02:00
|
|
|
return false; /* no restriction clauses: the test must
|
|
|
|
* fail */
|
|
|
|
|
2005-03-02 05:10:53 +01:00
|
|
|
/*
|
|
|
|
* In all cases where the predicate is an AND-clause, pred_test_recurse()
|
|
|
|
* will prefer to iterate over the predicate's components. So we can
|
|
|
|
* just do that to start with here, and eliminate the need for
|
|
|
|
* pred_test_recurse() to handle a bare List on the predicate side.
|
|
|
|
*
|
|
|
|
* Logic is: restriction must imply each of the AND'ed predicate items.
|
|
|
|
*/
|
|
|
|
foreach(item, predicate_list)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2005-03-02 05:10:53 +01:00
|
|
|
if (!pred_test_recurse((Node *) restrictinfo_list, lfirst(item)))
|
1997-09-07 07:04:48 +02:00
|
|
|
return false;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
return true;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-03-02 05:10:53 +01:00
|
|
|
/*----------
|
|
|
|
* pred_test_recurse
|
|
|
|
* Does the "predicate inclusion test" for non-NULL restriction and
|
|
|
|
* predicate clauses.
|
|
|
|
*
|
|
|
|
* The logic followed here is ("=>" means "implies"):
|
|
|
|
* atom A => atom B iff: pred_test_simple_clause says so
|
|
|
|
* atom A => AND-expr B iff: A => each of B's components
|
|
|
|
* atom A => OR-expr B iff: A => any of B's components
|
|
|
|
* AND-expr A => atom B iff: any of A's components => B
|
|
|
|
* AND-expr A => AND-expr B iff: A => each of B's components
|
|
|
|
* AND-expr A => OR-expr B iff: A => any of B's components,
|
|
|
|
* *or* any of A's components => B
|
|
|
|
* OR-expr A => atom B iff: each of A's components => B
|
|
|
|
* OR-expr A => AND-expr B iff: A => each of B's components
|
|
|
|
* OR-expr A => OR-expr B iff: each of A's components => any of B's
|
|
|
|
*
|
|
|
|
* An "atom" is anything other than an AND or OR node. Notice that we don't
|
|
|
|
* have any special logic to handle NOT nodes; these should have been pushed
|
|
|
|
* down or eliminated where feasible by prepqual.c.
|
|
|
|
*
|
|
|
|
* We can't recursively expand either side first, but have to interleave
|
|
|
|
* the expansions per the above rules, to be sure we handle all of these
|
|
|
|
* examples:
|
|
|
|
* (x OR y) => (x OR y OR z)
|
|
|
|
* (x AND y AND z) => (x AND y)
|
|
|
|
* (x AND y) => ((x AND y) OR z)
|
|
|
|
* ((x OR y) AND z) => (x OR y)
|
|
|
|
* This is still not an exhaustive test, but it handles most normal cases
|
|
|
|
* under the assumption that both inputs have been AND/OR flattened.
|
|
|
|
*
|
|
|
|
* A bare List node on the restriction side is interpreted as an AND clause,
|
|
|
|
* in order to handle the top-level restriction List properly. However we
|
|
|
|
* need not consider a List on the predicate side since pred_test() already
|
|
|
|
* expanded it.
|
|
|
|
*
|
|
|
|
* We have to be prepared to handle RestrictInfo nodes in the restrictinfo
|
|
|
|
* tree, though not in the predicate tree.
|
|
|
|
*----------
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
static bool
|
2005-03-02 05:10:53 +01:00
|
|
|
pred_test_recurse(Node *clause, Node *predicate)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *item;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-08-06 20:09:45 +02:00
|
|
|
Assert(clause != NULL);
|
2005-03-02 05:10:53 +01:00
|
|
|
/* skip through RestrictInfo */
|
2004-11-05 21:45:10 +01:00
|
|
|
if (IsA(clause, RestrictInfo))
|
|
|
|
{
|
2005-03-02 05:10:53 +01:00
|
|
|
clause = (Node *) ((RestrictInfo *) clause)->clause;
|
|
|
|
Assert(clause != NULL);
|
|
|
|
Assert(!IsA(clause, RestrictInfo));
|
2004-11-05 21:45:10 +01:00
|
|
|
}
|
2005-03-02 05:10:53 +01:00
|
|
|
Assert(predicate != NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Since a restriction List clause is handled the same as an AND clause,
|
|
|
|
* we can avoid duplicate code like this:
|
|
|
|
*/
|
|
|
|
if (and_clause(clause))
|
|
|
|
clause = (Node *) ((BoolExpr *) clause)->args;
|
|
|
|
|
|
|
|
if (IsA(clause, List))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2005-03-02 05:10:53 +01:00
|
|
|
if (and_clause(predicate))
|
|
|
|
{
|
|
|
|
/* AND-clause => AND-clause if A implies each of B's items */
|
|
|
|
foreach(item, ((BoolExpr *) predicate)->args)
|
|
|
|
{
|
|
|
|
if (!pred_test_recurse(clause, lfirst(item)))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if (or_clause(predicate))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2005-03-02 05:10:53 +01:00
|
|
|
/* AND-clause => OR-clause if A implies any of B's items */
|
|
|
|
/* Needed to handle (x AND y) => ((x AND y) OR z) */
|
|
|
|
foreach(item, ((BoolExpr *) predicate)->args)
|
|
|
|
{
|
|
|
|
if (pred_test_recurse(clause, lfirst(item)))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/* Also check if any of A's items implies B */
|
|
|
|
/* Needed to handle ((x OR y) AND z) => (x OR y) */
|
|
|
|
foreach(item, (List *) clause)
|
|
|
|
{
|
|
|
|
if (pred_test_recurse(lfirst(item), predicate))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* AND-clause => atom if any of A's items implies B */
|
|
|
|
foreach(item, (List *) clause)
|
|
|
|
{
|
|
|
|
if (pred_test_recurse(lfirst(item), predicate))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
}
|
2005-03-02 05:10:53 +01:00
|
|
|
else if (or_clause(clause))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2005-03-02 05:10:53 +01:00
|
|
|
if (or_clause(predicate))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
|
|
|
/*
|
2005-03-02 05:10:53 +01:00
|
|
|
* OR-clause => OR-clause if each of A's items implies any of
|
|
|
|
* B's items. Messy but can't do it any more simply.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2005-03-02 05:10:53 +01:00
|
|
|
foreach(item, ((BoolExpr *) clause)->args)
|
|
|
|
{
|
|
|
|
Node *citem = lfirst(item);
|
|
|
|
ListCell *item2;
|
|
|
|
|
|
|
|
foreach(item2, ((BoolExpr *) predicate)->args)
|
|
|
|
{
|
|
|
|
if (pred_test_recurse(citem, lfirst(item2)))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (item2 == NULL)
|
|
|
|
return false; /* doesn't imply any of B's */
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* OR-clause => AND-clause if each of A's items implies B */
|
|
|
|
/* OR-clause => atom if each of A's items implies B */
|
|
|
|
foreach(item, ((BoolExpr *) clause)->args)
|
|
|
|
{
|
|
|
|
if (!pred_test_recurse(lfirst(item), predicate))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
2005-03-01 01:24:52 +01:00
|
|
|
else
|
|
|
|
{
|
2005-03-02 05:10:53 +01:00
|
|
|
if (and_clause(predicate))
|
2005-03-01 01:24:52 +01:00
|
|
|
{
|
2005-03-02 05:10:53 +01:00
|
|
|
/* atom => AND-clause if A implies each of B's items */
|
|
|
|
foreach(item, ((BoolExpr *) predicate)->args)
|
|
|
|
{
|
|
|
|
if (!pred_test_recurse(clause, lfirst(item)))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2005-03-01 01:24:52 +01:00
|
|
|
}
|
2005-03-02 05:10:53 +01:00
|
|
|
else if (or_clause(predicate))
|
2005-03-01 01:24:52 +01:00
|
|
|
{
|
2005-03-02 05:10:53 +01:00
|
|
|
/* atom => OR-clause if A implies any of B's items */
|
|
|
|
foreach(item, ((BoolExpr *) predicate)->args)
|
|
|
|
{
|
|
|
|
if (pred_test_recurse(clause, lfirst(item)))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* atom => atom is the base case */
|
|
|
|
return pred_test_simple_clause((Expr *) predicate, clause);
|
2005-03-01 01:24:52 +01:00
|
|
|
}
|
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Define an "operator implication table" for btree operators ("strategies").
|
2004-01-07 23:02:48 +01:00
|
|
|
*
|
|
|
|
* The strategy numbers defined by btree indexes (see access/skey.h) are:
|
|
|
|
* (1) < (2) <= (3) = (4) >= (5) >
|
2004-08-29 07:07:03 +02:00
|
|
|
* and in addition we use (6) to represent <>. <> is not a btree-indexable
|
2004-01-07 23:02:48 +01:00
|
|
|
* operator, but we assume here that if the equality operator of a btree
|
|
|
|
* opclass has a negator operator, the negator behaves as <> for the opclass.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* The interpretation of:
|
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* test_op = BT_implic_table[given_op-1][target_op-1]
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2004-01-07 23:02:48 +01:00
|
|
|
* where test_op, given_op and target_op are strategy numbers (from 1 to 6)
|
1996-07-09 08:22:35 +02:00
|
|
|
* of btree operators, is as follows:
|
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* If you know, for some ATTR, that "ATTR given_op CONST1" is true, and you
|
|
|
|
* want to determine whether "ATTR target_op CONST2" must also be true, then
|
2003-11-12 22:15:59 +01:00
|
|
|
* you can use "CONST2 test_op CONST1" as a test. If this test returns true,
|
1997-09-07 07:04:48 +02:00
|
|
|
* then the target expression must be true; if the test returns false, then
|
|
|
|
* the target expression may be false.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2004-01-07 23:02:48 +01:00
|
|
|
* An entry where test_op == 0 means the implication cannot be determined,
|
|
|
|
* i.e., this test should always be considered false.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
|
2004-01-07 23:02:48 +01:00
|
|
|
#define BTLT BTLessStrategyNumber
|
|
|
|
#define BTLE BTLessEqualStrategyNumber
|
|
|
|
#define BTEQ BTEqualStrategyNumber
|
|
|
|
#define BTGE BTGreaterEqualStrategyNumber
|
|
|
|
#define BTGT BTGreaterStrategyNumber
|
|
|
|
#define BTNE 6
|
|
|
|
|
2000-07-28 01:16:04 +02:00
|
|
|
static const StrategyNumber
|
2004-01-07 23:02:48 +01:00
|
|
|
BT_implic_table[6][6] = {
|
|
|
|
/*
|
|
|
|
* The target operator:
|
|
|
|
*
|
2004-08-29 07:07:03 +02:00
|
|
|
* LT LE EQ GE GT NE
|
2004-01-07 23:02:48 +01:00
|
|
|
*/
|
2004-08-29 07:07:03 +02:00
|
|
|
{BTGE, BTGE, 0, 0, 0, BTGE}, /* LT */
|
|
|
|
{BTGT, BTGE, 0, 0, 0, BTGT}, /* LE */
|
2004-01-07 23:02:48 +01:00
|
|
|
{BTGT, BTGE, BTEQ, BTLE, BTLT, BTNE}, /* EQ */
|
2004-08-29 07:07:03 +02:00
|
|
|
{0, 0, 0, BTLE, BTLT, BTLT}, /* GE */
|
|
|
|
{0, 0, 0, BTLE, BTLE, BTLE}, /* GT */
|
|
|
|
{0, 0, 0, 0, 0, BTEQ} /* NE */
|
1996-07-09 08:22:35 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2004-03-07 06:43:53 +01:00
|
|
|
/*----------
|
2001-08-06 20:09:45 +02:00
|
|
|
* pred_test_simple_clause
|
1997-09-07 07:04:48 +02:00
|
|
|
* Does the "predicate inclusion test" for a "simple clause" predicate
|
2001-08-06 20:09:45 +02:00
|
|
|
* and a "simple clause" restriction.
|
|
|
|
*
|
2004-03-07 06:43:53 +01:00
|
|
|
* We have three strategies for determining whether one simple clause
|
|
|
|
* implies another:
|
|
|
|
*
|
|
|
|
* A simple and general way is to see if they are equal(); this works for any
|
2004-08-29 07:07:03 +02:00
|
|
|
* kind of expression. (Actually, there is an implied assumption that the
|
2004-03-07 06:43:53 +01:00
|
|
|
* functions in the expression are immutable, ie dependent only on their input
|
|
|
|
* arguments --- but this was checked for the predicate by CheckPredicate().)
|
|
|
|
*
|
|
|
|
* When the predicate is of the form "foo IS NOT NULL", we can conclude that
|
|
|
|
* the predicate is implied if the clause is a strict operator or function
|
2004-08-29 07:07:03 +02:00
|
|
|
* that has "foo" as an input. In this case the clause must yield NULL when
|
2004-03-07 06:43:53 +01:00
|
|
|
* "foo" is NULL, which we can take as equivalent to FALSE because we know
|
|
|
|
* we are within an AND/OR subtree of a WHERE clause. (Again, "foo" is
|
|
|
|
* already known immutable, so the clause will certainly always fail.)
|
2001-08-06 20:09:45 +02:00
|
|
|
*
|
2004-03-07 06:43:53 +01:00
|
|
|
* Our other way works only for binary boolean opclauses of the form
|
2004-08-29 07:07:03 +02:00
|
|
|
* "foo op constant", where "foo" is the same in both clauses. The operators
|
2004-03-07 06:43:53 +01:00
|
|
|
* and constants can be different but the operators must be in the same btree
|
2004-08-29 07:07:03 +02:00
|
|
|
* operator class. We use the above operator implication table to be able to
|
2004-03-07 06:43:53 +01:00
|
|
|
* derive implications between nonidentical clauses. (Note: "foo" is known
|
2004-03-27 01:24:28 +01:00
|
|
|
* immutable, and constants are surely immutable, but we have to check that
|
2004-08-04 23:34:35 +02:00
|
|
|
* the operators are too. As of 8.0 it's possible for opclasses to contain
|
2004-03-27 01:24:28 +01:00
|
|
|
* operators that are merely stable, and we dare not make deductions with
|
|
|
|
* these.)
|
2001-08-06 20:09:45 +02:00
|
|
|
*
|
2004-03-07 06:43:53 +01:00
|
|
|
* Eventually, rtree operators could also be handled by defining an
|
|
|
|
* appropriate "RT_implic_table" array.
|
|
|
|
*----------
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
static bool
|
2001-08-06 20:09:45 +02:00
|
|
|
pred_test_simple_clause(Expr *predicate, Node *clause)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2004-01-07 23:02:48 +01:00
|
|
|
Node *leftop,
|
|
|
|
*rightop;
|
|
|
|
Node *pred_var,
|
1997-09-08 04:41:22 +02:00
|
|
|
*clause_var;
|
|
|
|
Const *pred_const,
|
|
|
|
*clause_const;
|
2004-01-07 23:02:48 +01:00
|
|
|
bool pred_var_on_left,
|
|
|
|
clause_var_on_left,
|
|
|
|
pred_op_negated;
|
1997-09-08 04:41:22 +02:00
|
|
|
Oid pred_op,
|
|
|
|
clause_op,
|
2004-01-07 23:02:48 +01:00
|
|
|
pred_op_negator,
|
|
|
|
clause_op_negator,
|
2003-11-12 22:15:59 +01:00
|
|
|
test_op = InvalidOid;
|
|
|
|
Oid opclass_id;
|
2003-05-13 06:38:58 +02:00
|
|
|
bool found = false;
|
2003-11-12 22:15:59 +01:00
|
|
|
StrategyNumber pred_strategy,
|
|
|
|
clause_strategy,
|
1997-09-08 04:41:22 +02:00
|
|
|
test_strategy;
|
2003-11-12 22:15:59 +01:00
|
|
|
Oid clause_subtype;
|
1997-09-08 04:41:22 +02:00
|
|
|
Expr *test_expr;
|
2002-12-13 20:46:01 +01:00
|
|
|
ExprState *test_exprstate;
|
2001-07-16 07:07:00 +02:00
|
|
|
Datum test_result;
|
|
|
|
bool isNull;
|
2003-05-13 06:38:58 +02:00
|
|
|
CatCList *catlist;
|
|
|
|
int i;
|
2002-12-15 17:17:59 +01:00
|
|
|
EState *estate;
|
|
|
|
MemoryContext oldcontext;
|
2001-07-16 07:07:00 +02:00
|
|
|
|
2001-08-06 20:09:45 +02:00
|
|
|
/* First try the equal() test */
|
|
|
|
if (equal((Node *) predicate, clause))
|
|
|
|
return true;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2004-03-07 06:43:53 +01:00
|
|
|
/* Next try the IS NOT NULL case */
|
|
|
|
if (predicate && IsA(predicate, NullTest) &&
|
|
|
|
((NullTest *) predicate)->nulltesttype == IS_NOT_NULL)
|
|
|
|
{
|
2004-08-29 07:07:03 +02:00
|
|
|
Expr *nonnullarg = ((NullTest *) predicate)->arg;
|
2004-03-07 06:43:53 +01:00
|
|
|
|
|
|
|
if (is_opclause(clause) &&
|
2004-05-31 01:40:41 +02:00
|
|
|
list_member(((OpExpr *) clause)->args, nonnullarg) &&
|
2004-03-07 06:43:53 +01:00
|
|
|
op_strict(((OpExpr *) clause)->opno))
|
|
|
|
return true;
|
|
|
|
if (is_funcclause(clause) &&
|
2004-05-31 01:40:41 +02:00
|
|
|
list_member(((FuncExpr *) clause)->args, nonnullarg) &&
|
2004-03-07 06:43:53 +01:00
|
|
|
func_strict(((FuncExpr *) clause)->funcid))
|
|
|
|
return true;
|
|
|
|
return false; /* we can't succeed below... */
|
|
|
|
}
|
|
|
|
|
2001-08-06 20:09:45 +02:00
|
|
|
/*
|
2001-10-25 07:50:21 +02:00
|
|
|
* Can't do anything more unless they are both binary opclauses with a
|
2004-01-07 23:02:48 +01:00
|
|
|
* Const on one side, and identical subexpressions on the other sides.
|
2004-08-29 07:07:03 +02:00
|
|
|
* Note we don't have to think about binary relabeling of the Const
|
|
|
|
* node, since that would have been folded right into the Const.
|
2004-01-07 23:02:48 +01:00
|
|
|
*
|
|
|
|
* If either Const is null, we also fail right away; this assumes that
|
|
|
|
* the test operator will always be strict.
|
2001-08-06 20:09:45 +02:00
|
|
|
*/
|
2002-12-12 16:49:42 +01:00
|
|
|
if (!is_opclause(predicate))
|
2001-08-06 20:09:45 +02:00
|
|
|
return false;
|
2004-01-07 23:02:48 +01:00
|
|
|
leftop = get_leftop(predicate);
|
|
|
|
rightop = get_rightop(predicate);
|
|
|
|
if (rightop == NULL)
|
|
|
|
return false; /* not a binary opclause */
|
|
|
|
if (IsA(rightop, Const))
|
|
|
|
{
|
|
|
|
pred_var = leftop;
|
|
|
|
pred_const = (Const *) rightop;
|
|
|
|
pred_var_on_left = true;
|
|
|
|
}
|
|
|
|
else if (IsA(leftop, Const))
|
|
|
|
{
|
|
|
|
pred_var = rightop;
|
|
|
|
pred_const = (Const *) leftop;
|
|
|
|
pred_var_on_left = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return false; /* no Const to be found */
|
|
|
|
if (pred_const->constisnull)
|
|
|
|
return false;
|
2001-08-06 20:09:45 +02:00
|
|
|
|
|
|
|
if (!is_opclause(clause))
|
|
|
|
return false;
|
2004-01-07 23:02:48 +01:00
|
|
|
leftop = get_leftop((Expr *) clause);
|
|
|
|
rightop = get_rightop((Expr *) clause);
|
|
|
|
if (rightop == NULL)
|
|
|
|
return false; /* not a binary opclause */
|
|
|
|
if (IsA(rightop, Const))
|
|
|
|
{
|
|
|
|
clause_var = leftop;
|
|
|
|
clause_const = (Const *) rightop;
|
|
|
|
clause_var_on_left = true;
|
|
|
|
}
|
|
|
|
else if (IsA(leftop, Const))
|
|
|
|
{
|
|
|
|
clause_var = rightop;
|
|
|
|
clause_const = (Const *) leftop;
|
|
|
|
clause_var_on_left = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return false; /* no Const to be found */
|
|
|
|
if (clause_const->constisnull)
|
1997-09-07 07:04:48 +02:00
|
|
|
return false;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
2004-08-29 07:07:03 +02:00
|
|
|
* Check for matching subexpressions on the non-Const sides. We used
|
|
|
|
* to only allow a simple Var, but it's about as easy to allow any
|
|
|
|
* expression. Remember we already know that the pred expression does
|
2004-01-07 23:02:48 +01:00
|
|
|
* not contain any non-immutable functions, so identical expressions
|
|
|
|
* should yield identical results.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2004-01-07 23:02:48 +01:00
|
|
|
if (!equal(pred_var, clause_var))
|
1997-09-07 07:04:48 +02:00
|
|
|
return false;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2004-01-07 23:02:48 +01:00
|
|
|
/*
|
2004-08-29 07:07:03 +02:00
|
|
|
* Okay, get the operators in the two clauses we're comparing. Commute
|
|
|
|
* them if needed so that we can assume the variables are on the left.
|
2004-01-07 23:02:48 +01:00
|
|
|
*/
|
2002-12-12 16:49:42 +01:00
|
|
|
pred_op = ((OpExpr *) predicate)->opno;
|
2004-01-07 23:02:48 +01:00
|
|
|
if (!pred_var_on_left)
|
|
|
|
{
|
|
|
|
pred_op = get_commutator(pred_op);
|
|
|
|
if (!OidIsValid(pred_op))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2002-12-12 16:49:42 +01:00
|
|
|
clause_op = ((OpExpr *) clause)->opno;
|
2004-01-07 23:02:48 +01:00
|
|
|
if (!clause_var_on_left)
|
|
|
|
{
|
|
|
|
clause_op = get_commutator(clause_op);
|
|
|
|
if (!OidIsValid(clause_op))
|
|
|
|
return false;
|
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
2003-11-12 22:15:59 +01:00
|
|
|
* Try to find a btree opclass containing the needed operators.
|
2001-07-16 07:07:00 +02:00
|
|
|
*
|
2003-05-13 06:38:58 +02:00
|
|
|
* We must find a btree opclass that contains both operators, else the
|
2003-11-12 22:15:59 +01:00
|
|
|
* implication can't be determined. Also, the pred_op has to be of
|
2004-08-29 07:07:03 +02:00
|
|
|
* default subtype (implying left and right input datatypes are the
|
|
|
|
* same); otherwise it's unsafe to put the pred_const on the left side
|
|
|
|
* of the test. Also, the opclass must contain a suitable test
|
|
|
|
* operator matching the clause_const's type (which we take to mean
|
|
|
|
* that it has the same subtype as the original clause_operator).
|
2003-11-12 22:15:59 +01:00
|
|
|
*
|
|
|
|
* If there are multiple matching opclasses, assume we can use any one to
|
2004-08-29 07:07:03 +02:00
|
|
|
* determine the logical relationship of the two operators and the
|
|
|
|
* correct corresponding test operator. This should work for any
|
|
|
|
* logically consistent opclasses.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2003-05-13 06:38:58 +02:00
|
|
|
catlist = SearchSysCacheList(AMOPOPID, 1,
|
|
|
|
ObjectIdGetDatum(pred_op),
|
|
|
|
0, 0, 0);
|
2001-08-21 18:36:06 +02:00
|
|
|
|
2004-01-07 23:02:48 +01:00
|
|
|
/*
|
|
|
|
* If we couldn't find any opclass containing the pred_op, perhaps it
|
|
|
|
* is a <> operator. See if it has a negator that is in an opclass.
|
|
|
|
*/
|
|
|
|
pred_op_negated = false;
|
|
|
|
if (catlist->n_members == 0)
|
|
|
|
{
|
|
|
|
pred_op_negator = get_negator(pred_op);
|
|
|
|
if (OidIsValid(pred_op_negator))
|
|
|
|
{
|
|
|
|
pred_op_negated = true;
|
|
|
|
ReleaseSysCacheList(catlist);
|
|
|
|
catlist = SearchSysCacheList(AMOPOPID, 1,
|
2004-08-29 07:07:03 +02:00
|
|
|
ObjectIdGetDatum(pred_op_negator),
|
2004-01-07 23:02:48 +01:00
|
|
|
0, 0, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Also may need the clause_op's negator */
|
|
|
|
clause_op_negator = get_negator(clause_op);
|
|
|
|
|
|
|
|
/* Now search the opclasses */
|
2003-05-13 06:38:58 +02:00
|
|
|
for (i = 0; i < catlist->n_members; i++)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2003-05-13 06:38:58 +02:00
|
|
|
HeapTuple pred_tuple = &catlist->members[i]->tuple;
|
|
|
|
Form_pg_amop pred_form = (Form_pg_amop) GETSTRUCT(pred_tuple);
|
|
|
|
HeapTuple clause_tuple;
|
|
|
|
|
2003-11-12 22:15:59 +01:00
|
|
|
opclass_id = pred_form->amopclaid;
|
|
|
|
|
|
|
|
/* must be btree */
|
|
|
|
if (!opclass_is_btree(opclass_id))
|
|
|
|
continue;
|
|
|
|
/* predicate operator must be default within this opclass */
|
|
|
|
if (pred_form->amopsubtype != InvalidOid)
|
2003-05-13 06:38:58 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Get the predicate operator's btree strategy number */
|
|
|
|
pred_strategy = (StrategyNumber) pred_form->amopstrategy;
|
|
|
|
Assert(pred_strategy >= 1 && pred_strategy <= 5);
|
|
|
|
|
2004-01-07 23:02:48 +01:00
|
|
|
if (pred_op_negated)
|
|
|
|
{
|
|
|
|
/* Only consider negators that are = */
|
|
|
|
if (pred_strategy != BTEqualStrategyNumber)
|
|
|
|
continue;
|
|
|
|
pred_strategy = BTNE;
|
|
|
|
}
|
|
|
|
|
2003-05-13 06:38:58 +02:00
|
|
|
/*
|
2004-08-29 07:07:03 +02:00
|
|
|
* From the same opclass, find a strategy number for the
|
|
|
|
* clause_op, if possible
|
2003-05-13 06:38:58 +02:00
|
|
|
*/
|
|
|
|
clause_tuple = SearchSysCache(AMOPOPID,
|
|
|
|
ObjectIdGetDatum(clause_op),
|
|
|
|
ObjectIdGetDatum(opclass_id),
|
|
|
|
0, 0);
|
|
|
|
if (HeapTupleIsValid(clause_tuple))
|
2001-08-21 18:36:06 +02:00
|
|
|
{
|
2003-05-13 06:38:58 +02:00
|
|
|
Form_pg_amop clause_form = (Form_pg_amop) GETSTRUCT(clause_tuple);
|
2001-10-25 07:50:21 +02:00
|
|
|
|
2003-11-12 22:15:59 +01:00
|
|
|
/* Get the restriction clause operator's strategy/subtype */
|
2003-05-13 06:38:58 +02:00
|
|
|
clause_strategy = (StrategyNumber) clause_form->amopstrategy;
|
|
|
|
Assert(clause_strategy >= 1 && clause_strategy <= 5);
|
2003-11-12 22:15:59 +01:00
|
|
|
clause_subtype = clause_form->amopsubtype;
|
2003-05-13 06:38:58 +02:00
|
|
|
ReleaseSysCache(clause_tuple);
|
2004-01-07 23:02:48 +01:00
|
|
|
}
|
|
|
|
else if (OidIsValid(clause_op_negator))
|
|
|
|
{
|
|
|
|
clause_tuple = SearchSysCache(AMOPOPID,
|
2004-08-29 07:07:03 +02:00
|
|
|
ObjectIdGetDatum(clause_op_negator),
|
2004-01-07 23:02:48 +01:00
|
|
|
ObjectIdGetDatum(opclass_id),
|
|
|
|
0, 0);
|
|
|
|
if (HeapTupleIsValid(clause_tuple))
|
2003-11-12 22:15:59 +01:00
|
|
|
{
|
2004-01-07 23:02:48 +01:00
|
|
|
Form_pg_amop clause_form = (Form_pg_amop) GETSTRUCT(clause_tuple);
|
|
|
|
|
|
|
|
/* Get the restriction clause operator's strategy/subtype */
|
|
|
|
clause_strategy = (StrategyNumber) clause_form->amopstrategy;
|
|
|
|
Assert(clause_strategy >= 1 && clause_strategy <= 5);
|
|
|
|
clause_subtype = clause_form->amopsubtype;
|
|
|
|
ReleaseSysCache(clause_tuple);
|
|
|
|
|
|
|
|
/* Only consider negators that are = */
|
|
|
|
if (clause_strategy != BTEqualStrategyNumber)
|
|
|
|
continue;
|
|
|
|
clause_strategy = BTNE;
|
2003-11-12 22:15:59 +01:00
|
|
|
}
|
2004-01-07 23:02:48 +01:00
|
|
|
else
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
continue;
|
2003-11-12 22:15:59 +01:00
|
|
|
|
2004-01-07 23:02:48 +01:00
|
|
|
/*
|
|
|
|
* Look up the "test" strategy number in the implication table
|
|
|
|
*/
|
|
|
|
test_strategy = BT_implic_table[clause_strategy - 1][pred_strategy - 1];
|
|
|
|
if (test_strategy == 0)
|
|
|
|
{
|
|
|
|
/* Can't determine implication using this interpretation */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* See if opclass has an operator for the test strategy and the
|
|
|
|
* clause datatype.
|
|
|
|
*/
|
|
|
|
if (test_strategy == BTNE)
|
|
|
|
{
|
2003-11-12 22:15:59 +01:00
|
|
|
test_op = get_opclass_member(opclass_id, clause_subtype,
|
2004-01-07 23:02:48 +01:00
|
|
|
BTEqualStrategyNumber);
|
2003-11-12 22:15:59 +01:00
|
|
|
if (OidIsValid(test_op))
|
2004-01-07 23:02:48 +01:00
|
|
|
test_op = get_negator(test_op);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
test_op = get_opclass_member(opclass_id, clause_subtype,
|
|
|
|
test_strategy);
|
|
|
|
}
|
|
|
|
if (OidIsValid(test_op))
|
|
|
|
{
|
2004-03-27 01:24:28 +01:00
|
|
|
/*
|
|
|
|
* Last check: test_op must be immutable.
|
|
|
|
*
|
2004-08-29 07:07:03 +02:00
|
|
|
* Note that we require only the test_op to be immutable, not the
|
|
|
|
* original clause_op. (pred_op must be immutable, else it
|
2004-03-27 01:24:28 +01:00
|
|
|
* would not be allowed in an index predicate.) Essentially
|
|
|
|
* we are assuming that the opclass is consistent even if it
|
|
|
|
* contains operators that are merely stable.
|
|
|
|
*/
|
|
|
|
if (op_volatile(test_op) == PROVOLATILE_IMMUTABLE)
|
|
|
|
{
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
2001-08-21 18:36:06 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2003-05-13 06:38:58 +02:00
|
|
|
ReleaseSysCacheList(catlist);
|
2001-08-21 18:36:06 +02:00
|
|
|
|
2003-05-13 06:38:58 +02:00
|
|
|
if (!found)
|
2001-08-21 18:36:06 +02:00
|
|
|
{
|
2003-05-13 06:38:58 +02:00
|
|
|
/* couldn't find a btree opclass to interpret the operators */
|
2001-08-21 18:36:06 +02:00
|
|
|
return false;
|
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
2003-11-12 22:15:59 +01:00
|
|
|
* Evaluate the test. For this we need an EState.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2002-12-15 17:17:59 +01:00
|
|
|
estate = CreateExecutorState();
|
|
|
|
|
|
|
|
/* We can use the estate's working context to avoid memory leaks. */
|
|
|
|
oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
|
|
|
|
|
|
|
|
/* Build expression tree */
|
2002-12-12 16:49:42 +01:00
|
|
|
test_expr = make_opclause(test_op,
|
|
|
|
BOOLOID,
|
|
|
|
false,
|
2003-11-12 22:15:59 +01:00
|
|
|
(Expr *) pred_const,
|
|
|
|
(Expr *) clause_const);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2002-12-15 17:17:59 +01:00
|
|
|
/* Prepare it for execution */
|
|
|
|
test_exprstate = ExecPrepareExpr(test_expr, estate);
|
|
|
|
|
|
|
|
/* And execute it. */
|
|
|
|
test_result = ExecEvalExprSwitchContext(test_exprstate,
|
2004-08-29 07:07:03 +02:00
|
|
|
GetPerTupleExprContext(estate),
|
2001-08-06 20:09:45 +02:00
|
|
|
&isNull, NULL);
|
2002-12-15 17:17:59 +01:00
|
|
|
|
|
|
|
/* Get back to outer memory context */
|
|
|
|
MemoryContextSwitchTo(oldcontext);
|
|
|
|
|
|
|
|
/* Release all the junk we just created */
|
|
|
|
FreeExecutorState(estate);
|
2000-08-24 05:29:15 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if (isNull)
|
|
|
|
{
|
2003-05-13 06:38:58 +02:00
|
|
|
/* Treat a null result as false ... but it's a tad fishy ... */
|
2003-07-25 02:01:09 +02:00
|
|
|
elog(DEBUG2, "null predicate test result");
|
1997-09-07 07:04:48 +02:00
|
|
|
return false;
|
|
|
|
}
|
2001-07-16 07:07:00 +02:00
|
|
|
return DatumGetBool(test_result);
|
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-04-22 23:58:32 +02:00
|
|
|
foreach(l, rel->joininfo)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
JoinInfo *joininfo = (JoinInfo *) lfirst(l);
|
1999-07-30 06:07:25 +02:00
|
|
|
|
2002-11-24 22:52:15 +01:00
|
|
|
/*
|
|
|
|
* Examine each joinclause in the JoinInfo node's list to see if
|
2005-04-22 23:58:32 +02:00
|
|
|
* it matches any key of any index. If so, add the JoinInfo's
|
2003-08-04 02:43:34 +02:00
|
|
|
* otherrels to the result. We can skip examining other
|
2005-04-22 23:58:32 +02:00
|
|
|
* joinclauses in the same list as soon as we find a match, since
|
|
|
|
* by definition they all have the same otherrels.
|
2002-11-24 22:52:15 +01:00
|
|
|
*/
|
2005-04-22 23:58:32 +02:00
|
|
|
if (list_matches_any_index(joininfo->jinfo_restrictinfo,
|
|
|
|
rel,
|
|
|
|
joininfo->unjoined_relids))
|
|
|
|
outer_relids = bms_add_members(outer_relids,
|
|
|
|
joininfo->unjoined_relids);
|
|
|
|
}
|
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
|
|
|
/*
|
|
|
|
* list_matches_any_index
|
|
|
|
* Workhorse for indexable_outerrelids: given a list of RestrictInfos,
|
|
|
|
* see if any of them match any index of the given rel.
|
|
|
|
*
|
|
|
|
* We define it like this so that we can recurse into OR subclauses.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
list_matches_any_index(List *clauses, RelOptInfo *rel, Relids outer_relids)
|
|
|
|
{
|
|
|
|
ListCell *l;
|
2002-11-24 22:52:15 +01:00
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
foreach(l, clauses)
|
|
|
|
{
|
|
|
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
|
|
|
|
ListCell *j;
|
2002-11-24 22:52:15 +01:00
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
Assert(IsA(rinfo, RestrictInfo));
|
2002-11-24 22:52:15 +01:00
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
/* RestrictInfos that aren't ORs are easy */
|
|
|
|
if (!restriction_is_or_clause(rinfo))
|
|
|
|
{
|
|
|
|
if (matches_any_index(rinfo, rel, outer_relids))
|
|
|
|
return true;
|
|
|
|
continue;
|
2002-11-24 22:52:15 +01:00
|
|
|
}
|
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
foreach(j, ((BoolExpr *) rinfo->orclause)->args)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2005-04-22 23:58:32 +02:00
|
|
|
Node *orarg = (Node *) lfirst(j);
|
|
|
|
|
|
|
|
/* OR arguments should be ANDs or sub-RestrictInfos */
|
|
|
|
if (and_clause(orarg))
|
|
|
|
{
|
|
|
|
List *andargs = ((BoolExpr *) orarg)->args;
|
|
|
|
|
|
|
|
/* Recurse to examine AND items and sub-ORs */
|
|
|
|
if (list_matches_any_index(andargs, rel, outer_relids))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Assert(IsA(orarg, RestrictInfo));
|
|
|
|
Assert(!restriction_is_or_clause((RestrictInfo *) orarg));
|
|
|
|
if (matches_any_index((RestrictInfo *) orarg, rel,
|
|
|
|
outer_relids))
|
|
|
|
return true;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1999-07-25 19:53:27 +02:00
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* matches_any_index
|
|
|
|
* Workhorse for indexable_outerrelids: see if a simple joinclause can be
|
|
|
|
* matched to any index of the given rel.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
matches_any_index(RestrictInfo *rinfo, RelOptInfo *rel, Relids outer_relids)
|
|
|
|
{
|
|
|
|
ListCell *l;
|
|
|
|
|
|
|
|
/* 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 *
|
|
|
|
best_inner_indexscan(Query *root, RelOptInfo *rel,
|
|
|
|
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 *
|
|
|
|
find_clauses_for_join(Query *root, RelOptInfo *rel,
|
|
|
|
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;
|
|
|
|
int numsources;
|
|
|
|
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
|
|
|
/* found anything in base restrict list? */
|
|
|
|
numsources = (clause_list != NIL) ? 1 : 0;
|
2003-06-16 00:51:45 +02:00
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
/* Look for joinclauses that are usable with given outer_relids */
|
|
|
|
foreach(l, rel->joininfo)
|
|
|
|
{
|
|
|
|
JoinInfo *joininfo = (JoinInfo *) lfirst(l);
|
|
|
|
bool jfoundhere = false;
|
|
|
|
ListCell *j;
|
2004-01-06 00:39:54 +01:00
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
if (!bms_is_subset(joininfo->unjoined_relids, outer_relids))
|
|
|
|
continue;
|
2002-11-24 22:52:15 +01:00
|
|
|
|
2005-04-22 23:58:32 +02:00
|
|
|
foreach(j, joininfo->jinfo_restrictinfo)
|
|
|
|
{
|
|
|
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(j);
|
|
|
|
|
|
|
|
/* Can't use pushed-down clauses in outer join */
|
|
|
|
if (isouterjoin && rinfo->is_pushed_down)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
clause_list = lappend(clause_list, rinfo);
|
|
|
|
if (!jfoundhere)
|
|
|
|
{
|
|
|
|
jfoundhere = true;
|
|
|
|
jfound = true;
|
|
|
|
numsources++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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-04-22 23:58:32 +02:00
|
|
|
* If we found clauses in more than one list, we may now have
|
|
|
|
* clauses that are known redundant. Get rid of 'em.
|
2002-11-24 22:52:15 +01:00
|
|
|
*/
|
2005-04-22 23:58:32 +02:00
|
|
|
if (numsources > 1)
|
|
|
|
{
|
|
|
|
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.
|
|
|
|
* (The latter is not depended on by any part of the planner, so far as I can
|
2005-04-25 03:30:14 +02:00
|
|
|
* tell; but some parts of the executor do assume that the indexqual list
|
2003-08-04 02:43:34 +02:00
|
|
|
* ultimately delivered to the executor is so ordered. One such place is
|
2004-08-29 07:07:03 +02:00
|
|
|
* _bt_preprocess_keys() in the btree support. Perhaps that ought to be fixed
|
2003-05-26 02:11:29 +02:00
|
|
|
* someday --- tgl 7/00)
|
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,
|
|
|
|
true, true));
|
|
|
|
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);
|
2004-05-31 01:40:41 +02:00
|
|
|
result = list_make1(make_restrictinfo(expr, true, true));
|
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);
|
2004-05-31 01:40:41 +02:00
|
|
|
result = list_make1(make_restrictinfo(expr, true, true));
|
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);
|
2004-01-06 00:39:54 +01:00
|
|
|
result = lappend(result, make_restrictinfo(expr, true, true));
|
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));
|
2004-05-31 01:40:41 +02:00
|
|
|
result = list_make1(make_restrictinfo(expr, true, true));
|
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));
|
2004-01-06 00:39:54 +01:00
|
|
|
result = lappend(result, make_restrictinfo(expr, true, true));
|
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
|
|
|
}
|