1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* indxpath.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* Routines to determine which indices are usable for scanning a
|
1999-07-23 05:34:49 +02:00
|
|
|
* given relation, and create IndexPaths 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-03-28 02:58:26 +02:00
|
|
|
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.172 2005/03/28 00:58:22 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"
|
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-03-27 08:29:49 +02:00
|
|
|
static List *group_clauses_by_indexkey(IndexOptInfo *index);
|
2003-06-16 00:51:45 +02:00
|
|
|
static List *group_clauses_by_indexkey_for_join(Query *root,
|
2005-03-27 08:29:49 +02:00
|
|
|
IndexOptInfo *index,
|
2003-06-16 00:51:45 +02:00
|
|
|
Relids outer_relids,
|
|
|
|
JoinType jointype, bool isouterjoin);
|
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,
|
|
|
|
RestrictInfo *rinfo);
|
2005-03-27 08:29:49 +02:00
|
|
|
static bool match_join_clause_to_indexcol(IndexOptInfo *index,
|
2004-08-29 07:07:03 +02:00
|
|
|
int indexcol, Oid opclass,
|
|
|
|
RestrictInfo *rinfo);
|
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-03-27 08:29:49 +02:00
|
|
|
static Relids indexable_outerrelids(IndexOptInfo *index);
|
|
|
|
static Path *make_innerjoin_index_path(Query *root, IndexOptInfo *index,
|
2003-08-04 02:43:34 +02:00
|
|
|
List *clausegroups);
|
2003-05-28 18:04:02 +02:00
|
|
|
static bool match_index_to_operand(Node *operand, int indexcol,
|
2005-03-27 08:29:49 +02:00
|
|
|
IndexOptInfo *index);
|
|
|
|
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
|
|
|
{
|
2003-02-08 21:20:55 +01:00
|
|
|
Relids all_join_outerrelids = NULL;
|
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);
|
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;
|
2002-11-24 22:52:15 +01:00
|
|
|
Relids join_outerrelids;
|
1998-08-14 18:13:07 +02:00
|
|
|
|
2004-01-05 06:07:36 +01:00
|
|
|
/* Ignore partial indexes that do not match the query */
|
|
|
|
if (index->indpred != NIL && !index->predOK)
|
|
|
|
continue;
|
1998-08-14 18:13:07 +02:00
|
|
|
|
1998-08-02 00:12:13 +02:00
|
|
|
/*
|
2004-08-29 07:07:03 +02:00
|
|
|
* 1. Match the index against non-OR restriction clauses. (OR
|
|
|
|
* clauses will be considered later by orindxpath.c.)
|
1998-08-02 00:12:13 +02:00
|
|
|
*/
|
2005-03-27 08:29:49 +02:00
|
|
|
restrictclauses = group_clauses_by_indexkey(index);
|
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
|
2001-03-22 05:01:46 +01:00
|
|
|
* see how many of them are actually useful for this query.
|
1999-08-16 04:17:58 +02:00
|
|
|
*/
|
2005-03-27 08:29:49 +02:00
|
|
|
index_pathkeys = build_index_pathkeys(root, index,
|
2000-12-14 23:30:45 +01:00
|
|
|
ForwardScanDirection);
|
|
|
|
index_is_ordered = (index_pathkeys != NIL);
|
|
|
|
useful_pathkeys = truncate_useless_pathkeys(root, rel,
|
|
|
|
index_pathkeys);
|
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.
|
2000-02-15 21:49:31 +01:00
|
|
|
*/
|
2001-07-16 07:07:00 +02:00
|
|
|
if (restrictclauses != NIL ||
|
|
|
|
useful_pathkeys != NIL ||
|
|
|
|
index->indpred != NIL)
|
2000-02-15 21:49:31 +01:00
|
|
|
add_path(rel, (Path *)
|
2005-03-27 08:29:49 +02:00
|
|
|
create_index_path(root, index,
|
2000-07-13 07:47:29 +02:00
|
|
|
restrictclauses,
|
2000-12-14 23:30:45 +01:00
|
|
|
useful_pathkeys,
|
|
|
|
index_is_ordered ?
|
|
|
|
ForwardScanDirection :
|
|
|
|
NoMovementScanDirection));
|
|
|
|
|
|
|
|
/*
|
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
|
|
|
*/
|
|
|
|
if (index_is_ordered)
|
|
|
|
{
|
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)
|
|
|
|
add_path(rel, (Path *)
|
2005-03-27 08:29:49 +02:00
|
|
|
create_index_path(root, index,
|
2000-12-14 23:30:45 +01:00
|
|
|
restrictclauses,
|
|
|
|
useful_pathkeys,
|
|
|
|
BackwardScanDirection));
|
|
|
|
}
|
1999-08-16 04:17:58 +02:00
|
|
|
|
|
|
|
/*
|
2004-01-04 01:07:32 +01:00
|
|
|
* 5. Examine join clauses to see which ones are potentially
|
2003-08-04 02:43:34 +02:00
|
|
|
* usable with this index, 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. We compute both per-index and
|
|
|
|
* overall-for-relation sets.
|
1998-08-02 00:12:13 +02:00
|
|
|
*/
|
2005-03-27 08:29:49 +02:00
|
|
|
join_outerrelids = indexable_outerrelids(index);
|
2002-11-24 22:52:15 +01:00
|
|
|
index->outer_relids = join_outerrelids;
|
2003-02-08 21:20:55 +01:00
|
|
|
all_join_outerrelids = bms_add_members(all_join_outerrelids,
|
|
|
|
join_outerrelids);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
2002-11-24 22:52:15 +01:00
|
|
|
|
|
|
|
rel->index_outer_relids = all_join_outerrelids;
|
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
|
|
|
*
|
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
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
static List *
|
2005-03-27 08:29:49 +02:00
|
|
|
group_clauses_by_indexkey(IndexOptInfo *index)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2004-06-01 06:47:46 +02:00
|
|
|
List *clausegroup_list = NIL;
|
2005-03-27 08:29:49 +02:00
|
|
|
List *restrictinfo_list = index->rel->baserestrictinfo;
|
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
|
|
|
|
2002-11-24 22:52:15 +01:00
|
|
|
if (restrictinfo_list == NIL)
|
1997-09-07 07:04:48 +02:00
|
|
|
return NIL;
|
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
|
|
|
|
2004-05-26 06:41:50 +02:00
|
|
|
foreach(l, restrictinfo_list)
|
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-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,
|
2003-12-31 00:53:15 +01:00
|
|
|
rinfo))
|
2004-06-01 06:47:46 +02:00
|
|
|
clausegroup = lappend(clausegroup, rinfo);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1999-07-23 05:34:49 +02: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;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2004-06-01 06:47:46 +02:00
|
|
|
clausegroup_list = lappend(clausegroup_list, clausegroup);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-05-28 18:04:02 +02:00
|
|
|
indexcol++;
|
1997-09-07 07:04:48 +02:00
|
|
|
classes++;
|
|
|
|
|
2003-05-28 18:04:02 +02:00
|
|
|
} while (!DoneMatchingIndexKeys(classes));
|
1997-03-18 19:41:37 +01:00
|
|
|
|
2004-06-01 06:47:46 +02:00
|
|
|
return clausegroup_list;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
2002-11-24 22:52:15 +01:00
|
|
|
* group_clauses_by_indexkey_for_join
|
2003-05-26 02:11:29 +02:00
|
|
|
* Generate a list of sublists of clauses that can be used with an index
|
1999-08-16 04:17:58 +02:00
|
|
|
* to scan the inner side of a nestloop join.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1999-07-23 05:34:49 +02:00
|
|
|
* This is much like group_clauses_by_indexkey(), but we consider both
|
2002-11-24 22:52:15 +01:00
|
|
|
* join and restriction clauses. Any joinclause that uses only otherrels
|
2003-08-04 02:43:34 +02:00
|
|
|
* in the specified outer_relids is fair game. But there must be at least
|
2002-11-24 22:52:15 +01:00
|
|
|
* one such joinclause in the final list, otherwise we return NIL indicating
|
|
|
|
* that this index isn't interesting as an inner indexscan. (A scan using
|
|
|
|
* only restriction clauses shouldn't be created here, because a regular Path
|
|
|
|
* will already have been generated for it.)
|
1997-03-18 19:41:37 +01:00
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
static List *
|
2005-03-27 08:29:49 +02:00
|
|
|
group_clauses_by_indexkey_for_join(Query *root, IndexOptInfo *index,
|
2003-06-16 00:51:45 +02:00
|
|
|
Relids outer_relids,
|
|
|
|
JoinType jointype, bool isouterjoin)
|
1997-03-18 19:41:37 +01:00
|
|
|
{
|
2004-06-01 06:47:46 +02:00
|
|
|
List *clausegroup_list = NIL;
|
1997-09-08 04:41:22 +02:00
|
|
|
bool jfound = false;
|
2003-05-28 18:04:02 +02:00
|
|
|
int indexcol = 0;
|
2002-11-24 22:52:15 +01:00
|
|
|
Oid *classes = index->classlist;
|
1997-09-07 07:04:48 +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;
|
2003-12-18 01:22:12 +01:00
|
|
|
int numsources;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *l;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-12-18 01:22:12 +01:00
|
|
|
/*
|
2004-08-29 07:07:03 +02:00
|
|
|
* We can always use plain restriction clauses for the rel. We
|
|
|
|
* scan these first because we want them first in the clausegroup
|
|
|
|
* 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.
|
2003-12-18 01:22:12 +01:00
|
|
|
*/
|
2005-03-27 08:29:49 +02:00
|
|
|
foreach(l, index->rel->baserestrictinfo)
|
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
|
|
|
|
|
|
|
/* Can't use pushed-down clauses in outer join */
|
2004-01-05 06:07:36 +01:00
|
|
|
if (isouterjoin && rinfo->is_pushed_down)
|
2003-12-18 01:22:12 +01:00
|
|
|
continue;
|
|
|
|
|
2005-03-27 08:29:49 +02:00
|
|
|
if (match_clause_to_indexcol(index,
|
2003-12-18 01:22:12 +01:00
|
|
|
indexcol,
|
|
|
|
curClass,
|
2003-12-31 00:53:15 +01:00
|
|
|
rinfo))
|
2004-06-01 06:47:46 +02:00
|
|
|
clausegroup = lappend(clausegroup, rinfo);
|
2003-12-18 01:22:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* found anything in base restrict list? */
|
2004-06-01 06:47:46 +02:00
|
|
|
numsources = (clausegroup != NIL) ? 1 : 0;
|
2003-12-18 01:22:12 +01:00
|
|
|
|
2002-11-24 22:52:15 +01:00
|
|
|
/* Look for joinclauses that are usable with given outer_relids */
|
2005-03-27 08:29:49 +02:00
|
|
|
foreach(l, index->rel->joininfo)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
JoinInfo *joininfo = (JoinInfo *) lfirst(l);
|
2003-12-18 01:22:12 +01:00
|
|
|
bool jfoundhere = false;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *j;
|
1999-07-23 05:34:49 +02:00
|
|
|
|
2003-02-08 21:20:55 +01:00
|
|
|
if (!bms_is_subset(joininfo->unjoined_relids, outer_relids))
|
2002-11-24 22:52:15 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
foreach(j, joininfo->jinfo_restrictinfo)
|
1999-07-23 05:34:49 +02:00
|
|
|
{
|
2002-11-24 22:52:15 +01:00
|
|
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(j);
|
|
|
|
|
|
|
|
/* Can't use pushed-down clauses in outer join */
|
2004-01-05 06:07:36 +01:00
|
|
|
if (isouterjoin && rinfo->is_pushed_down)
|
2002-11-24 22:52:15 +01:00
|
|
|
continue;
|
|
|
|
|
2005-03-27 08:29:49 +02:00
|
|
|
if (match_join_clause_to_indexcol(index,
|
2003-05-28 18:04:02 +02:00
|
|
|
indexcol,
|
2002-11-24 22:52:15 +01:00
|
|
|
curClass,
|
2003-12-31 00:53:15 +01:00
|
|
|
rinfo))
|
2002-11-24 22:52:15 +01:00
|
|
|
{
|
2004-06-01 06:47:46 +02:00
|
|
|
clausegroup = lappend(clausegroup, rinfo);
|
2003-12-18 01:22:12 +01:00
|
|
|
if (!jfoundhere)
|
|
|
|
{
|
|
|
|
jfoundhere = true;
|
|
|
|
jfound = true;
|
|
|
|
numsources++;
|
|
|
|
}
|
2002-11-24 22:52:15 +01:00
|
|
|
}
|
1999-07-23 05:34:49 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2002-11-24 22:52:15 +01:00
|
|
|
|
2003-06-16 00:51:45 +02:00
|
|
|
/*
|
2004-08-29 07:07:03 +02:00
|
|
|
* If we found clauses in more than one list, we may now have
|
|
|
|
* clauses that are known redundant. Get rid of 'em.
|
2003-06-16 00:51:45 +02:00
|
|
|
*/
|
2003-12-18 01:22:12 +01:00
|
|
|
if (numsources > 1)
|
2003-06-16 00:51:45 +02:00
|
|
|
{
|
2004-06-01 06:47:46 +02:00
|
|
|
clausegroup = remove_redundant_join_clauses(root,
|
|
|
|
clausegroup,
|
|
|
|
jointype);
|
2003-06-16 00:51:45 +02: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
|
|
|
|
2003-05-26 02:11:29 +02:00
|
|
|
/* if no join clause was matched then forget it, per comments above */
|
1999-07-30 06:07:25 +02:00
|
|
|
if (!jfound)
|
|
|
|
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
|
|
|
|
2004-01-04 01:07:32 +01:00
|
|
|
/*
|
|
|
|
* group_clauses_by_indexkey_for_or
|
|
|
|
* Generate a list of sublists of clauses that can be used with an index
|
|
|
|
* to find rows matching an OR subclause.
|
|
|
|
*
|
|
|
|
* This is essentially just like group_clauses_by_indexkey() except that
|
|
|
|
* we can use the given clause (or any AND subclauses of it) as well as
|
|
|
|
* top-level restriction clauses of the relation. Furthermore, we demand
|
|
|
|
* that at least one such use be made, otherwise we fail and return NIL.
|
|
|
|
* (Any path we made without such a use would be redundant with non-OR
|
2004-08-29 07:07:03 +02:00
|
|
|
* indexscans. Compare also group_clauses_by_indexkey_for_join.)
|
2004-01-04 01:07:32 +01:00
|
|
|
*
|
|
|
|
* XXX When we generate an indexqual list that uses both the OR subclause
|
|
|
|
* and top-level restriction clauses, we end up with a slightly inefficient
|
|
|
|
* plan because create_indexscan_plan is not very bright about figuring out
|
|
|
|
* which restriction clauses are implied by the generated indexqual condition.
|
|
|
|
* Currently we'll end up rechecking both the OR clause and the top-level
|
|
|
|
* restriction clause as qpquals. FIXME someday.
|
|
|
|
*/
|
|
|
|
List *
|
2005-03-27 08:29:49 +02:00
|
|
|
group_clauses_by_indexkey_for_or(IndexOptInfo *index, Expr *orsubclause)
|
2004-01-04 01:07:32 +01:00
|
|
|
{
|
2004-06-01 06:47:46 +02:00
|
|
|
List *clausegroup_list = NIL;
|
2004-01-04 01:07:32 +01:00
|
|
|
bool matched = false;
|
|
|
|
int indexcol = 0;
|
|
|
|
Oid *classes = index->classlist;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
Oid curClass = classes[0];
|
2004-06-01 06:47:46 +02:00
|
|
|
List *clausegroup = NIL;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *item;
|
2004-01-04 01:07:32 +01:00
|
|
|
|
|
|
|
/* Try to match the OR subclause to the index key */
|
|
|
|
if (IsA(orsubclause, RestrictInfo))
|
|
|
|
{
|
2005-03-27 08:29:49 +02:00
|
|
|
if (match_clause_to_indexcol(index, indexcol, curClass,
|
2004-01-04 01:07:32 +01:00
|
|
|
(RestrictInfo *) orsubclause))
|
|
|
|
{
|
2004-06-01 06:47:46 +02:00
|
|
|
clausegroup = lappend(clausegroup, orsubclause);
|
2004-01-04 01:07:32 +01:00
|
|
|
matched = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (and_clause((Node *) orsubclause))
|
|
|
|
{
|
|
|
|
foreach(item, ((BoolExpr *) orsubclause)->args)
|
|
|
|
{
|
|
|
|
RestrictInfo *subsubclause = (RestrictInfo *) lfirst(item);
|
|
|
|
|
|
|
|
if (IsA(subsubclause, RestrictInfo) &&
|
2005-03-27 08:29:49 +02:00
|
|
|
match_clause_to_indexcol(index, indexcol, curClass,
|
2004-01-04 01:07:32 +01:00
|
|
|
subsubclause))
|
|
|
|
{
|
2004-06-01 06:47:46 +02:00
|
|
|
clausegroup = lappend(clausegroup, subsubclause);
|
2004-01-04 01:07:32 +01:00
|
|
|
matched = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we found no clauses for this indexkey in the OR subclause
|
|
|
|
* itself, try looking in the rel's top-level restriction list.
|
|
|
|
*
|
2004-08-29 07:07:03 +02:00
|
|
|
* XXX should we always search the top-level list? Slower but could
|
|
|
|
* sometimes yield a better plan.
|
2004-01-04 01:07:32 +01:00
|
|
|
*/
|
2004-06-01 06:47:46 +02:00
|
|
|
if (clausegroup == NIL)
|
2004-01-04 01:07:32 +01:00
|
|
|
{
|
2005-03-27 08:29:49 +02:00
|
|
|
foreach(item, index->rel->baserestrictinfo)
|
2004-01-04 01:07:32 +01:00
|
|
|
{
|
|
|
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(item);
|
|
|
|
|
2005-03-27 08:29:49 +02:00
|
|
|
if (match_clause_to_indexcol(index, indexcol, curClass,
|
2004-01-04 01:07:32 +01:00
|
|
|
rinfo))
|
2004-06-01 06:47:46 +02:00
|
|
|
clausegroup = lappend(clausegroup, rinfo);
|
2004-01-04 01:07:32 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If still no clauses match this key, we're done; we don't want
|
|
|
|
* to look at keys to its right.
|
|
|
|
*/
|
2004-06-01 06:47:46 +02:00
|
|
|
if (clausegroup == NIL)
|
2004-01-04 01:07:32 +01:00
|
|
|
break;
|
|
|
|
|
2004-06-01 06:47:46 +02:00
|
|
|
clausegroup_list = lappend(clausegroup_list, clausegroup);
|
2004-01-04 01:07:32 +01:00
|
|
|
|
|
|
|
indexcol++;
|
|
|
|
classes++;
|
|
|
|
} while (!DoneMatchingIndexKeys(classes));
|
|
|
|
|
|
|
|
/* if OR clause was not used then forget it, per comments above */
|
|
|
|
if (!matched)
|
|
|
|
return NIL;
|
|
|
|
|
2004-06-01 06:47:46 +02:00
|
|
|
return clausegroup_list;
|
2004-01-04 01:07:32 +01: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
|
|
|
*
|
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,
|
2003-12-31 00:53:15 +01:00
|
|
|
RestrictInfo *rinfo)
|
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
|
|
|
|
* (constant operator indexkey). Anything that is a "pseudo constant"
|
|
|
|
* expression will do.
|
2002-11-24 22:52:15 +01:00
|
|
|
*/
|
2005-03-27 08:29:49 +02:00
|
|
|
if (match_index_to_operand(leftop, indexcol, index) &&
|
2004-01-04 01:07:32 +01:00
|
|
|
is_pseudo_constant_clause_relids(rightop, rinfo->right_relids))
|
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) &&
|
2004-01-04 01:07:32 +01:00
|
|
|
is_pseudo_constant_clause_relids(leftop, rinfo->left_relids))
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2003-05-28 18:04:02 +02:00
|
|
|
* match_join_clause_to_indexcol()
|
|
|
|
* Determines whether a join clause matches a column of an index.
|
2002-11-24 22:52:15 +01:00
|
|
|
*
|
|
|
|
* To match, the clause:
|
|
|
|
*
|
|
|
|
* (1) must be in the form (indexkey op others) or (others op indexkey),
|
|
|
|
* where others is an expression involving only vars of the other
|
|
|
|
* relation(s); and
|
|
|
|
* (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
|
2002-11-24 22:52:15 +01:00
|
|
|
* by match_special_index_operator().
|
|
|
|
*
|
2005-03-27 00:29:20 +01:00
|
|
|
* The boolean-index cases don't apply.
|
|
|
|
*
|
2002-11-24 22:52:15 +01:00
|
|
|
* As above, we must be able to commute the clause to put the indexkey
|
|
|
|
* on the left.
|
|
|
|
*
|
|
|
|
* Note that we already know that the clause as a whole uses vars from
|
|
|
|
* the interesting set of relations. But we need to defend against
|
|
|
|
* expressions like (a.f1 OP (b.f2 OP a.f3)); that's not processable by
|
|
|
|
* an indexscan nestloop join, whereas (a.f1 OP (b.f2 OP c.f3)) is.
|
|
|
|
*
|
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).
|
2002-11-24 22:52:15 +01:00
|
|
|
* 'opclass' is the corresponding operator class.
|
2003-12-31 00:53:15 +01:00
|
|
|
* 'rinfo' is the clause to be tested (as a RestrictInfo node).
|
2002-11-24 22:52:15 +01:00
|
|
|
*
|
|
|
|
* Returns true if the clause can be used with this index key.
|
|
|
|
*
|
|
|
|
* NOTE: returns false if clause is an OR or AND clause; it is the
|
|
|
|
* responsibility of higher-level routines to cope with those.
|
|
|
|
*/
|
|
|
|
static bool
|
2005-03-27 08:29:49 +02:00
|
|
|
match_join_clause_to_indexcol(IndexOptInfo *index,
|
2003-05-28 18:04:02 +02:00
|
|
|
int indexcol,
|
2002-11-24 22:52:15 +01:00
|
|
|
Oid opclass,
|
2003-12-31 00:53:15 +01:00
|
|
|
RestrictInfo *rinfo)
|
2002-11-24 22:52:15 +01:00
|
|
|
{
|
2003-12-31 00:53:15 +01:00
|
|
|
Expr *clause = rinfo->clause;
|
2003-01-15 20:35:48 +01:00
|
|
|
Node *leftop,
|
2002-11-24 22:52:15 +01:00
|
|
|
*rightop;
|
|
|
|
|
|
|
|
/* Clause must be a binary opclause. */
|
2002-12-12 16:49:42 +01:00
|
|
|
if (!is_opclause(clause))
|
2002-11-24 22:52:15 +01:00
|
|
|
return false;
|
|
|
|
leftop = get_leftop(clause);
|
|
|
|
rightop = get_rightop(clause);
|
|
|
|
if (!leftop || !rightop)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
2003-08-04 02:43:34 +02:00
|
|
|
* Check for an indexqual that could be handled by a nestloop join. We
|
|
|
|
* need the index key to be compared against an expression that uses
|
|
|
|
* none of the indexed relation's vars and contains no volatile
|
|
|
|
* functions.
|
2002-11-24 22:52:15 +01:00
|
|
|
*/
|
2005-03-27 08:29:49 +02:00
|
|
|
if (match_index_to_operand(leftop, indexcol, index))
|
2002-11-24 22:52:15 +01:00
|
|
|
{
|
2003-12-31 00:53:15 +01:00
|
|
|
Relids othervarnos = rinfo->right_relids;
|
2002-11-24 22:52:15 +01:00
|
|
|
bool isIndexable;
|
|
|
|
|
|
|
|
isIndexable =
|
2005-03-27 08:29:49 +02:00
|
|
|
!bms_overlap(index->rel->relids, othervarnos) &&
|
2003-01-15 20:35:48 +01:00
|
|
|
!contain_volatile_functions(rightop) &&
|
2002-11-24 22:52:15 +01:00
|
|
|
is_indexable_operator(clause, opclass, true);
|
|
|
|
return isIndexable;
|
1999-08-12 06:32:54 +02:00
|
|
|
}
|
2002-11-24 22:52:15 +01:00
|
|
|
|
2005-03-27 08:29:49 +02:00
|
|
|
if (match_index_to_operand(rightop, indexcol, index))
|
1999-08-12 06:32:54 +02:00
|
|
|
{
|
2003-12-31 00:53:15 +01:00
|
|
|
Relids othervarnos = rinfo->left_relids;
|
2002-11-24 22:52:15 +01:00
|
|
|
bool isIndexable;
|
|
|
|
|
|
|
|
isIndexable =
|
2005-03-27 08:29:49 +02:00
|
|
|
!bms_overlap(index->rel->relids, othervarnos) &&
|
2003-01-15 20:35:48 +01:00
|
|
|
!contain_volatile_functions(leftop) &&
|
2002-11-24 22:52:15 +01:00
|
|
|
is_indexable_operator(clause, opclass, false);
|
|
|
|
return isIndexable;
|
1999-08-12 06:32:54 +02:00
|
|
|
}
|
1998-08-14 18:13:07 +02:00
|
|
|
|
1999-08-12 06:32:54 +02:00
|
|
|
return false;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
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
|
2003-08-04 02:43:34 +02:00
|
|
|
* for the specified index. Returns a set of relids.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2002-11-24 22:52:15 +01:00
|
|
|
static Relids
|
2005-03-27 08:29:49 +02:00
|
|
|
indexable_outerrelids(IndexOptInfo *index)
|
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-03-27 08:29:49 +02:00
|
|
|
foreach(l, index->rel->joininfo)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
JoinInfo *joininfo = (JoinInfo *) lfirst(l);
|
2002-11-24 22:52:15 +01:00
|
|
|
bool match_found = false;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *j;
|
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
|
|
|
|
* it matches any key of the index. If so, add the JoinInfo's
|
2003-08-04 02:43:34 +02:00
|
|
|
* otherrels to the result. We can skip examining other
|
|
|
|
* 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
|
|
|
*/
|
|
|
|
foreach(j, joininfo->jinfo_restrictinfo)
|
|
|
|
{
|
|
|
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(j);
|
2003-08-04 02:43:34 +02:00
|
|
|
int indexcol = 0;
|
|
|
|
Oid *classes = index->classlist;
|
1999-07-30 06:07:25 +02:00
|
|
|
|
2002-11-24 22:52:15 +01:00
|
|
|
do
|
|
|
|
{
|
|
|
|
Oid curClass = classes[0];
|
|
|
|
|
2005-03-27 08:29:49 +02:00
|
|
|
if (match_join_clause_to_indexcol(index,
|
2003-05-28 18:04:02 +02:00
|
|
|
indexcol,
|
2002-11-24 22:52:15 +01:00
|
|
|
curClass,
|
2003-12-31 00:53:15 +01:00
|
|
|
rinfo))
|
2002-11-24 22:52:15 +01:00
|
|
|
{
|
|
|
|
match_found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2003-05-28 18:04:02 +02:00
|
|
|
indexcol++;
|
2002-11-24 22:52:15 +01:00
|
|
|
classes++;
|
|
|
|
|
2003-05-28 18:04:02 +02:00
|
|
|
} while (!DoneMatchingIndexKeys(classes));
|
2002-11-24 22:52:15 +01:00
|
|
|
|
|
|
|
if (match_found)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (match_found)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2003-02-08 21:20:55 +01:00
|
|
|
outer_relids = bms_add_members(outer_relids,
|
|
|
|
joininfo->unjoined_relids);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1999-07-25 19:53:27 +02:00
|
|
|
|
2002-11-24 22:52:15 +01:00
|
|
|
return outer_relids;
|
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
|
|
|
{
|
2002-11-24 22:52:15 +01:00
|
|
|
Path *cheapest = NULL;
|
|
|
|
bool isouterjoin;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *ilist;
|
|
|
|
ListCell *jlist;
|
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
|
|
|
|
* the set of outer relids actually relevant for this index. If there
|
|
|
|
* 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
|
|
|
*/
|
|
|
|
foreach(jlist, rel->index_inner_paths)
|
|
|
|
{
|
|
|
|
info = (InnerIndexscanInfo *) lfirst(jlist);
|
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
|
|
|
/*
|
2003-08-04 02:43:34 +02:00
|
|
|
* For each index of the rel, find the best path; then choose the best
|
|
|
|
* overall. We cache the per-index results as well as the overall
|
|
|
|
* result. (This is useful because different indexes may have
|
|
|
|
* different relevant outerrel sets, so different overall outerrel
|
|
|
|
* sets might still map to the same computation for a given index.)
|
2002-11-24 22:52:15 +01:00
|
|
|
*/
|
|
|
|
foreach(ilist, rel->indexlist)
|
|
|
|
{
|
2003-08-04 02:43:34 +02:00
|
|
|
IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
|
2002-11-24 22:52:15 +01:00
|
|
|
Relids index_outer_relids;
|
|
|
|
Path *path = NULL;
|
|
|
|
|
|
|
|
/* identify set of relevant outer relids for this index */
|
2003-02-08 21:20:55 +01:00
|
|
|
index_outer_relids = bms_intersect(index->outer_relids, outer_relids);
|
|
|
|
/* skip if none */
|
|
|
|
if (bms_is_empty(index_outer_relids))
|
|
|
|
{
|
|
|
|
bms_free(index_outer_relids);
|
2002-11-24 22:52:15 +01:00
|
|
|
continue;
|
2003-02-08 21:20:55 +01:00
|
|
|
}
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2000-02-15 21:49:31 +01:00
|
|
|
/*
|
2002-11-24 22:52:15 +01:00
|
|
|
* Look to see if we already computed the result for this index.
|
2000-02-15 21:49:31 +01:00
|
|
|
*/
|
2002-11-24 22:52:15 +01:00
|
|
|
foreach(jlist, index->inner_paths)
|
|
|
|
{
|
|
|
|
info = (InnerIndexscanInfo *) lfirst(jlist);
|
2003-02-08 21:20:55 +01:00
|
|
|
if (bms_equal(info->other_relids, index_outer_relids) &&
|
2002-11-24 22:52:15 +01:00
|
|
|
info->isouterjoin == isouterjoin)
|
|
|
|
{
|
|
|
|
path = info->best_innerpath;
|
2003-08-04 02:43:34 +02:00
|
|
|
bms_free(index_outer_relids); /* not needed anymore */
|
2002-11-24 22:52:15 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
1998-09-21 17:41:28 +02:00
|
|
|
|
2004-05-26 06:41:50 +02:00
|
|
|
if (jlist == NULL) /* failed to find a match? */
|
2000-09-12 23:07:18 +02:00
|
|
|
{
|
2003-05-26 02:11:29 +02:00
|
|
|
List *clausegroups;
|
2002-11-24 22:52:15 +01:00
|
|
|
|
|
|
|
/* find useful clauses for this index and outerjoin set */
|
2003-06-16 00:51:45 +02:00
|
|
|
clausegroups = group_clauses_by_indexkey_for_join(root,
|
2003-05-26 02:11:29 +02:00
|
|
|
index,
|
2003-08-04 02:43:34 +02:00
|
|
|
index_outer_relids,
|
2003-06-16 00:51:45 +02:00
|
|
|
jointype,
|
2003-08-04 02:43:34 +02:00
|
|
|
isouterjoin);
|
2003-05-26 02:11:29 +02:00
|
|
|
if (clausegroups)
|
2002-11-24 22:52:15 +01:00
|
|
|
{
|
|
|
|
/* make the path */
|
2005-03-27 08:29:49 +02:00
|
|
|
path = make_innerjoin_index_path(root, index, clausegroups);
|
2002-11-24 22:52:15 +01:00
|
|
|
}
|
2000-09-12 23:07:18 +02:00
|
|
|
|
2002-11-24 22:52:15 +01:00
|
|
|
/* Cache the result --- whether positive or negative */
|
|
|
|
info = makeNode(InnerIndexscanInfo);
|
|
|
|
info->other_relids = index_outer_relids;
|
|
|
|
info->isouterjoin = isouterjoin;
|
|
|
|
info->best_innerpath = path;
|
|
|
|
index->inner_paths = lcons(info, index->inner_paths);
|
2000-09-12 23:07:18 +02:00
|
|
|
}
|
|
|
|
|
2002-11-24 22:52:15 +01:00
|
|
|
if (path != NULL &&
|
|
|
|
(cheapest == NULL ||
|
|
|
|
compare_path_costs(path, cheapest, TOTAL_COST) < 0))
|
|
|
|
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
|
|
|
/****************************************************************************
|
|
|
|
* ---- PATH CREATION UTILITIES ----
|
|
|
|
****************************************************************************/
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-11-24 22:52:15 +01:00
|
|
|
/*
|
|
|
|
* make_innerjoin_index_path
|
|
|
|
* Create an index path node for a path to be used as an inner
|
|
|
|
* relation in a nestloop join.
|
|
|
|
*
|
2005-03-27 08:29:49 +02:00
|
|
|
* 'index' is the index of interest
|
2003-05-26 02:11:29 +02:00
|
|
|
* 'clausegroups' is a list of lists of RestrictInfos that can use 'index'
|
2002-11-24 22:52:15 +01:00
|
|
|
*/
|
|
|
|
static Path *
|
|
|
|
make_innerjoin_index_path(Query *root,
|
2005-03-27 08:29:49 +02:00
|
|
|
IndexOptInfo *index,
|
2003-05-26 02:11:29 +02:00
|
|
|
List *clausegroups)
|
2002-11-24 22:52:15 +01:00
|
|
|
{
|
|
|
|
IndexPath *pathnode = makeNode(IndexPath);
|
2005-03-27 08:29:49 +02:00
|
|
|
RelOptInfo *rel = index->rel;
|
2003-05-26 02:11:29 +02:00
|
|
|
List *indexquals,
|
2004-01-06 00:39:54 +01:00
|
|
|
*allclauses;
|
2000-09-12 23:07:18 +02:00
|
|
|
|
2003-06-16 00:51:45 +02:00
|
|
|
/* XXX perhaps this code should be merged with create_index_path? */
|
2002-11-24 22:52:15 +01:00
|
|
|
|
|
|
|
pathnode->path.pathtype = T_IndexScan;
|
|
|
|
pathnode->path.parent = rel;
|
|
|
|
|
|
|
|
/*
|
2003-08-04 02:43:34 +02:00
|
|
|
* There's no point in marking the path with any pathkeys, since it
|
|
|
|
* will only ever be used as the inner path of a nestloop, and so its
|
|
|
|
* ordering does not matter.
|
2002-11-24 22:52:15 +01:00
|
|
|
*/
|
|
|
|
pathnode->path.pathkeys = NIL;
|
|
|
|
|
2004-01-06 00:39:54 +01:00
|
|
|
/* Convert clauses to indexquals the executor can handle */
|
2005-03-27 08:29:49 +02:00
|
|
|
indexquals = expand_indexqual_conditions(index, clausegroups);
|
2002-11-24 22:52:15 +01:00
|
|
|
|
2004-01-06 00:39:54 +01:00
|
|
|
/* Flatten the clausegroups list to produce indexclauses list */
|
|
|
|
allclauses = flatten_clausegroups_list(clausegroups);
|
2003-06-16 00:51:45 +02:00
|
|
|
|
2002-11-24 22:52:15 +01:00
|
|
|
/*
|
|
|
|
* Note that we are making a pathnode for a single-scan indexscan;
|
2004-01-06 00:39:54 +01:00
|
|
|
* therefore, indexinfo etc should be single-element lists.
|
2002-11-24 22:52:15 +01:00
|
|
|
*/
|
2004-05-31 01:40:41 +02:00
|
|
|
pathnode->indexinfo = list_make1(index);
|
|
|
|
pathnode->indexclauses = list_make1(allclauses);
|
|
|
|
pathnode->indexquals = list_make1(indexquals);
|
2004-01-06 00:39:54 +01:00
|
|
|
|
|
|
|
pathnode->isjoininner = true;
|
2002-11-24 22:52:15 +01:00
|
|
|
|
|
|
|
/* We don't actually care what order the index scans in ... */
|
|
|
|
pathnode->indexscandir = NoMovementScanDirection;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We must compute the estimated number of output rows for the
|
2003-08-04 02:43:34 +02:00
|
|
|
* indexscan. This is less than rel->rows because of the additional
|
|
|
|
* selectivity of the join clauses. Since clausegroups may contain
|
|
|
|
* both restriction and join clauses, we have to do a set union to get
|
|
|
|
* the full set of clauses that must be considered to compute the
|
|
|
|
* correct selectivity. (Without the union operation, we might have
|
|
|
|
* some restriction clauses appearing twice, which'd mislead
|
2004-01-04 04:51:52 +01:00
|
|
|
* clauselist_selectivity into double-counting their selectivity.
|
2003-08-04 02:43:34 +02:00
|
|
|
* However, since RestrictInfo nodes aren't copied when linking them
|
|
|
|
* into different lists, it should be sufficient to use pointer
|
|
|
|
* comparison to remove duplicates.)
|
2003-01-28 23:13:41 +01:00
|
|
|
*
|
2003-08-04 02:43:34 +02:00
|
|
|
* Always assume the join type is JOIN_INNER; even if some of the join
|
|
|
|
* clauses come from other contexts, that's not our problem.
|
2002-11-24 22:52:15 +01:00
|
|
|
*/
|
2004-05-31 01:40:41 +02:00
|
|
|
allclauses = list_union_ptr(rel->baserestrictinfo, allclauses);
|
2002-11-24 22:52:15 +01:00
|
|
|
pathnode->rows = rel->tuples *
|
2004-01-04 04:51:52 +01:00
|
|
|
clauselist_selectivity(root,
|
|
|
|
allclauses,
|
|
|
|
rel->relid, /* do not use 0! */
|
|
|
|
JOIN_INNER);
|
2002-11-24 22:52:15 +01:00
|
|
|
/* Like costsize.c, force estimate to be at least one row */
|
2004-01-05 06:07:36 +01:00
|
|
|
pathnode->rows = clamp_row_est(pathnode->rows);
|
2002-11-24 22:52:15 +01:00
|
|
|
|
2005-03-27 08:29:49 +02:00
|
|
|
cost_index(&pathnode->path, root, index, indexquals, true);
|
2002-11-24 22:52:15 +01:00
|
|
|
|
|
|
|
return (Path *) pathnode;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
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()
|
|
|
|
* or one of its sibling routines, to produce an indexclauses list.
|
|
|
|
*/
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* make_expr_from_indexclauses()
|
|
|
|
* Given an indexclauses structure, produce an ordinary boolean expression.
|
|
|
|
*
|
|
|
|
* This consists of stripping out the RestrictInfo nodes and inserting
|
|
|
|
* explicit AND and OR nodes as needed. There's not much to it, but
|
|
|
|
* the functionality is needed in a few places, so centralize the logic.
|
|
|
|
*/
|
|
|
|
Expr *
|
|
|
|
make_expr_from_indexclauses(List *indexclauses)
|
|
|
|
{
|
|
|
|
List *orclauses = NIL;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *orlist;
|
2004-01-06 00:39:54 +01:00
|
|
|
|
|
|
|
/* There's no such thing as an indexpath with zero scans */
|
|
|
|
Assert(indexclauses != NIL);
|
|
|
|
|
|
|
|
foreach(orlist, indexclauses)
|
|
|
|
{
|
2004-08-29 07:07:03 +02:00
|
|
|
List *andlist = (List *) lfirst(orlist);
|
2004-01-06 00:39:54 +01:00
|
|
|
|
|
|
|
/* Strip RestrictInfos */
|
|
|
|
andlist = get_actual_clauses(andlist);
|
|
|
|
/* Insert AND node if needed, and add to orclauses list */
|
|
|
|
orclauses = lappend(orclauses, make_ands_explicit(andlist));
|
|
|
|
}
|
|
|
|
|
2004-05-31 01:40:41 +02:00
|
|
|
if (list_length(orclauses) > 1)
|
2004-01-06 00:39:54 +01:00
|
|
|
return make_orclause(orclauses);
|
|
|
|
else
|
2004-05-26 06:41:50 +02:00
|
|
|
return (Expr *) linitial(orclauses);
|
2004-01-06 00:39:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
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
|
|
|
*/
|
|
|
|
static 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
|
|
|
|
* tell; but some parts of the executor do assume that the indxqual 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
|
|
|
}
|