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
|
|
|
*
|
2003-08-04 04:40:20 +02:00
|
|
|
* Portions Copyright (c) 1996-2003, 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
|
2004-03-27 01:24:28 +01:00
|
|
|
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.158 2004/03/27 00:24:28 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
|
|
|
|
2000-07-27 01:46:22 +02:00
|
|
|
|
2002-11-24 22:52:15 +01:00
|
|
|
static List *group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index);
|
2003-06-16 00:51:45 +02:00
|
|
|
static List *group_clauses_by_indexkey_for_join(Query *root,
|
|
|
|
RelOptInfo *rel, IndexOptInfo *index,
|
|
|
|
Relids outer_relids,
|
|
|
|
JoinType jointype, bool isouterjoin);
|
2003-05-28 18:04:02 +02:00
|
|
|
static bool match_clause_to_indexcol(RelOptInfo *rel, IndexOptInfo *index,
|
2003-12-31 00:53:15 +01:00
|
|
|
int indexcol, Oid opclass,
|
2004-01-04 01:07:32 +01:00
|
|
|
RestrictInfo *rinfo);
|
2003-08-04 02:43:34 +02:00
|
|
|
static bool match_join_clause_to_indexcol(RelOptInfo *rel, IndexOptInfo *index,
|
2003-12-31 00:53:15 +01: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);
|
2004-01-05 06:07:36 +01:00
|
|
|
static bool pred_test(List *predicate_list, List *restrictinfo_list);
|
2001-08-06 20:09:45 +02:00
|
|
|
static bool pred_test_restrict_list(Expr *predicate, List *restrictinfo_list);
|
|
|
|
static bool pred_test_recurse_clause(Expr *predicate, Node *clause);
|
|
|
|
static bool pred_test_recurse_pred(Expr *predicate, Node *clause);
|
|
|
|
static bool pred_test_simple_clause(Expr *predicate, Node *clause);
|
2002-11-24 22:52:15 +01:00
|
|
|
static Relids indexable_outerrelids(RelOptInfo *rel, IndexOptInfo *index);
|
|
|
|
static Path *make_innerjoin_index_path(Query *root,
|
2003-08-04 02:43:34 +02:00
|
|
|
RelOptInfo *rel, IndexOptInfo *index,
|
|
|
|
List *clausegroups);
|
2003-05-28 18:04:02 +02:00
|
|
|
static bool match_index_to_operand(Node *operand, int indexcol,
|
2000-04-12 19:17:23 +02:00
|
|
|
RelOptInfo *rel, 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);
|
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;
|
1998-08-02 00:12:13 +02:00
|
|
|
List *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-01-04 01:07:32 +01: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
|
|
|
*/
|
2002-11-24 22:52:15 +01:00
|
|
|
restrictclauses = group_clauses_by_indexkey(rel, 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
|
|
|
*/
|
2000-12-14 23:30:45 +01:00
|
|
|
index_pathkeys = build_index_pathkeys(root, rel, index,
|
|
|
|
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 *)
|
|
|
|
create_index_path(root, rel, 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)
|
|
|
|
{
|
|
|
|
index_pathkeys = build_index_pathkeys(root, rel, index,
|
|
|
|
BackwardScanDirection);
|
|
|
|
useful_pathkeys = truncate_useless_pathkeys(root, rel,
|
|
|
|
index_pathkeys);
|
|
|
|
if (useful_pathkeys != NIL)
|
|
|
|
add_path(rel, (Path *)
|
|
|
|
create_index_path(root, rel, index,
|
|
|
|
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
|
|
|
*/
|
2002-11-24 22:52:15 +01:00
|
|
|
join_outerrelids = indexable_outerrelids(rel, index);
|
|
|
|
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
|
|
|
*
|
1999-07-23 05:34:49 +02:00
|
|
|
* 'rel' is the node of the relation itself.
|
|
|
|
* 'index' is a index on 'rel'.
|
1997-03-18 19:41:37 +01: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 *
|
2002-11-24 22:52:15 +01:00
|
|
|
group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2003-05-29 00:32:50 +02:00
|
|
|
FastList clausegroup_list;
|
2002-11-24 22:52:15 +01:00
|
|
|
List *restrictinfo_list = 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
|
|
|
|
2003-05-29 00:32:50 +02:00
|
|
|
FastListInit(&clausegroup_list);
|
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];
|
2003-05-29 00:32:50 +02:00
|
|
|
FastList clausegroup;
|
2002-11-24 22:52:15 +01:00
|
|
|
List *i;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-05-29 00:32:50 +02:00
|
|
|
FastListInit(&clausegroup);
|
2002-11-24 22:52:15 +01:00
|
|
|
foreach(i, restrictinfo_list)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2002-11-24 22:52:15 +01:00
|
|
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(i);
|
1999-07-23 05:34:49 +02:00
|
|
|
|
2003-05-28 18:04:02 +02:00
|
|
|
if (match_clause_to_indexcol(rel,
|
1999-07-23 05:34:49 +02:00
|
|
|
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))
|
2003-05-29 00:32:50 +02:00
|
|
|
FastAppend(&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
|
|
|
*/
|
2003-05-29 00:32:50 +02:00
|
|
|
if (FastListValue(&clausegroup) == NIL)
|
1997-09-07 07:04:48 +02:00
|
|
|
break;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2003-05-29 00:32:50 +02:00
|
|
|
FastAppend(&clausegroup_list, FastListValue(&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
|
|
|
|
2003-05-29 00:32:50 +02:00
|
|
|
return FastListValue(&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 *
|
2003-06-16 00:51:45 +02:00
|
|
|
group_clauses_by_indexkey_for_join(Query *root,
|
|
|
|
RelOptInfo *rel, IndexOptInfo *index,
|
|
|
|
Relids outer_relids,
|
|
|
|
JoinType jointype, bool isouterjoin)
|
1997-03-18 19:41:37 +01:00
|
|
|
{
|
2003-05-29 00:32:50 +02:00
|
|
|
FastList clausegroup_list;
|
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
|
|
|
|
2003-05-29 00:32:50 +02:00
|
|
|
FastListInit(&clausegroup_list);
|
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];
|
2003-05-29 00:32:50 +02:00
|
|
|
FastList clausegroup;
|
2003-12-18 01:22:12 +01:00
|
|
|
int numsources;
|
2002-11-24 22:52:15 +01:00
|
|
|
List *i;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-05-29 00:32:50 +02:00
|
|
|
FastListInit(&clausegroup);
|
|
|
|
|
2003-12-18 01:22:12 +01: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.
|
|
|
|
*/
|
|
|
|
foreach(i, rel->baserestrictinfo)
|
|
|
|
{
|
|
|
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(i);
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
if (match_clause_to_indexcol(rel,
|
|
|
|
index,
|
|
|
|
indexcol,
|
|
|
|
curClass,
|
2003-12-31 00:53:15 +01:00
|
|
|
rinfo))
|
2003-12-18 01:22:12 +01:00
|
|
|
FastAppend(&clausegroup, rinfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* found anything in base restrict list? */
|
|
|
|
numsources = (FastListValue(&clausegroup) != NIL) ? 1 : 0;
|
|
|
|
|
2002-11-24 22:52:15 +01:00
|
|
|
/* Look for joinclauses that are usable with given outer_relids */
|
|
|
|
foreach(i, rel->joininfo)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2002-11-24 22:52:15 +01:00
|
|
|
JoinInfo *joininfo = (JoinInfo *) lfirst(i);
|
2003-12-18 01:22:12 +01:00
|
|
|
bool jfoundhere = false;
|
2002-11-24 22:52:15 +01:00
|
|
|
List *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;
|
|
|
|
|
2003-05-28 18:04:02 +02:00
|
|
|
if (match_join_clause_to_indexcol(rel,
|
2002-11-24 22:52:15 +01:00
|
|
|
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
|
|
|
{
|
2003-05-29 00:32:50 +02:00
|
|
|
FastAppend(&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
|
|
|
/*
|
2003-12-18 01:22:12 +01: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
|
|
|
{
|
2003-08-04 02:43:34 +02:00
|
|
|
List *nl;
|
2003-06-16 00:51:45 +02:00
|
|
|
|
|
|
|
nl = remove_redundant_join_clauses(root,
|
2003-08-04 02:43:34 +02:00
|
|
|
FastListValue(&clausegroup),
|
2003-06-16 00:51:45 +02:00
|
|
|
jointype);
|
|
|
|
FastListFromList(&clausegroup, nl);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
*/
|
2003-05-29 00:32:50 +02:00
|
|
|
if (FastListValue(&clausegroup) == NIL)
|
1997-09-07 07:04:48 +02:00
|
|
|
break;
|
|
|
|
|
2003-05-29 00:32:50 +02:00
|
|
|
FastAppend(&clausegroup_list, FastListValue(&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;
|
|
|
|
|
2003-05-29 00:32:50 +02:00
|
|
|
return FastListValue(&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
|
|
|
|
* indexscans. Compare also group_clauses_by_indexkey_for_join.)
|
|
|
|
*
|
|
|
|
* 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 *
|
|
|
|
group_clauses_by_indexkey_for_or(RelOptInfo *rel,
|
|
|
|
IndexOptInfo *index,
|
|
|
|
Expr *orsubclause)
|
|
|
|
{
|
|
|
|
FastList clausegroup_list;
|
|
|
|
bool matched = false;
|
|
|
|
int indexcol = 0;
|
|
|
|
Oid *classes = index->classlist;
|
|
|
|
|
|
|
|
FastListInit(&clausegroup_list);
|
|
|
|
do
|
|
|
|
{
|
|
|
|
Oid curClass = classes[0];
|
|
|
|
FastList clausegroup;
|
|
|
|
List *item;
|
|
|
|
|
|
|
|
FastListInit(&clausegroup);
|
|
|
|
|
|
|
|
/* Try to match the OR subclause to the index key */
|
|
|
|
if (IsA(orsubclause, RestrictInfo))
|
|
|
|
{
|
|
|
|
if (match_clause_to_indexcol(rel, index,
|
|
|
|
indexcol, curClass,
|
|
|
|
(RestrictInfo *) orsubclause))
|
|
|
|
{
|
|
|
|
FastAppend(&clausegroup, orsubclause);
|
|
|
|
matched = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (and_clause((Node *) orsubclause))
|
|
|
|
{
|
|
|
|
foreach(item, ((BoolExpr *) orsubclause)->args)
|
|
|
|
{
|
|
|
|
RestrictInfo *subsubclause = (RestrictInfo *) lfirst(item);
|
|
|
|
|
|
|
|
if (IsA(subsubclause, RestrictInfo) &&
|
|
|
|
match_clause_to_indexcol(rel, index,
|
|
|
|
indexcol, curClass,
|
|
|
|
subsubclause))
|
|
|
|
{
|
|
|
|
FastAppend(&clausegroup, subsubclause);
|
|
|
|
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.
|
|
|
|
*
|
|
|
|
* XXX should we always search the top-level list? Slower but
|
|
|
|
* could sometimes yield a better plan.
|
|
|
|
*/
|
|
|
|
if (FastListValue(&clausegroup) == NIL)
|
|
|
|
{
|
|
|
|
foreach(item, rel->baserestrictinfo)
|
|
|
|
{
|
|
|
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(item);
|
|
|
|
|
|
|
|
if (match_clause_to_indexcol(rel, index,
|
|
|
|
indexcol, curClass,
|
|
|
|
rinfo))
|
|
|
|
FastAppend(&clausegroup, rinfo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If still no clauses match this key, we're done; we don't want
|
|
|
|
* to look at keys to its right.
|
|
|
|
*/
|
|
|
|
if (FastListValue(&clausegroup) == NIL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
FastAppend(&clausegroup_list, FastListValue(&clausegroup));
|
|
|
|
|
|
|
|
indexcol++;
|
|
|
|
classes++;
|
|
|
|
|
|
|
|
} while (!DoneMatchingIndexKeys(classes));
|
|
|
|
|
|
|
|
/* if OR clause was not used then forget it, per comments above */
|
|
|
|
if (!matched)
|
|
|
|
return NIL;
|
|
|
|
|
|
|
|
return FastListValue(&clausegroup_list);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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
|
|
|
*
|
1999-08-12 06:32:54 +02:00
|
|
|
* To match, 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
|
|
|
*
|
1999-07-23 05:34:49 +02:00
|
|
|
* 'rel' is the relation of interest.
|
|
|
|
* 'index' is an index on 'rel'.
|
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
|
2003-05-28 18:04:02 +02:00
|
|
|
match_clause_to_indexcol(RelOptInfo *rel,
|
2000-01-09 01:26:47 +01:00
|
|
|
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
|
|
|
|
1999-08-12 06:32:54 +02:00
|
|
|
/* 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
|
|
|
*/
|
2003-05-28 18:04:02 +02:00
|
|
|
if (match_index_to_operand(leftop, indexcol, rel, 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
|
|
|
|
2003-05-28 18:04:02 +02:00
|
|
|
if (match_index_to_operand(rightop, indexcol, rel, 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().
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* 'rel' is the relation of interest.
|
|
|
|
* 'index' is an index on 'rel'.
|
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
|
2003-05-28 18:04:02 +02:00
|
|
|
match_join_clause_to_indexcol(RelOptInfo *rel,
|
2002-11-24 22:52:15 +01:00
|
|
|
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
|
|
|
*/
|
2003-05-28 18:04:02 +02:00
|
|
|
if (match_index_to_operand(leftop, indexcol, rel, 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 =
|
2003-02-08 21:20:55 +01:00
|
|
|
!bms_overlap(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
|
|
|
|
2003-05-28 18:04:02 +02:00
|
|
|
if (match_index_to_operand(rightop, indexcol, rel, 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 =
|
2003-02-08 21:20:55 +01:00
|
|
|
!bms_overlap(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;
|
|
|
|
List *ilist;
|
|
|
|
|
|
|
|
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
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* This routine (together with the routines it calls) iterates over
|
|
|
|
* ANDs in the predicate first, then reduces the qualification
|
|
|
|
* clauses down to their constituent terms, and iterates over ORs
|
|
|
|
* in the predicate last. This order is important to make the test
|
2001-08-06 20:09:45 +02:00
|
|
|
* succeed whenever possible (assuming the predicate has been converted
|
|
|
|
* to CNF format). --Nels, Jan '93
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
static 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
|
|
|
{
|
2001-08-06 20:09:45 +02:00
|
|
|
List *pred;
|
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 */
|
|
|
|
|
|
|
|
foreach(pred, predicate_list)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* if any clause is not implied, the whole predicate is not
|
2001-08-06 20:09:45 +02:00
|
|
|
* implied. Note we assume that any sub-ANDs have been flattened
|
|
|
|
* when the predicate was fed through canonicalize_qual().
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2001-08-06 20:09:45 +02:00
|
|
|
if (!pred_test_restrict_list(lfirst(pred), restrictinfo_list))
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
2001-08-06 20:09:45 +02:00
|
|
|
* pred_test_restrict_list
|
1997-09-07 07:04:48 +02:00
|
|
|
* Does the "predicate inclusion test" for one conjunct of a predicate
|
|
|
|
* expression.
|
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_restrict_list(Expr *predicate, List *restrictinfo_list)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
List *item;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-02-03 21:15:53 +01:00
|
|
|
foreach(item, restrictinfo_list)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2001-07-16 07:07:00 +02:00
|
|
|
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(item);
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* if any clause implies the predicate, return true */
|
2001-08-06 20:09:45 +02:00
|
|
|
if (pred_test_recurse_clause(predicate,
|
|
|
|
(Node *) restrictinfo->clause))
|
1997-09-07 07:04:48 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
2001-08-06 20:09:45 +02:00
|
|
|
* pred_test_recurse_clause
|
1997-09-07 07:04:48 +02:00
|
|
|
* Does the "predicate inclusion test" for a general restriction-clause
|
2001-08-06 20:09:45 +02:00
|
|
|
* expression. Here we recursively deal with the possibility that the
|
|
|
|
* restriction clause is itself an AND or OR structure.
|
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_recurse_clause(Expr *predicate, Node *clause)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
List *items,
|
|
|
|
*item;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-08-06 20:09:45 +02:00
|
|
|
Assert(clause != NULL);
|
|
|
|
if (or_clause(clause))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2002-12-12 16:49:42 +01:00
|
|
|
items = ((BoolExpr *) clause)->args;
|
1997-09-07 07:04:48 +02:00
|
|
|
foreach(item, items)
|
|
|
|
{
|
|
|
|
/* if any OR item doesn't imply the predicate, clause doesn't */
|
2001-08-06 20:09:45 +02:00
|
|
|
if (!pred_test_recurse_clause(predicate, lfirst(item)))
|
1997-09-07 07:04:48 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if (and_clause(clause))
|
|
|
|
{
|
2002-12-12 16:49:42 +01:00
|
|
|
items = ((BoolExpr *) clause)->args;
|
1997-09-07 07:04:48 +02:00
|
|
|
foreach(item, items)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* if any AND item implies the predicate, the whole clause
|
|
|
|
* does
|
|
|
|
*/
|
2001-08-06 20:09:45 +02:00
|
|
|
if (pred_test_recurse_clause(predicate, lfirst(item)))
|
1997-09-07 07:04:48 +02:00
|
|
|
return true;
|
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
return false;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
2001-08-06 20:09:45 +02:00
|
|
|
return pred_test_recurse_pred(predicate, clause);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
2001-08-06 20:09:45 +02:00
|
|
|
* pred_test_recurse_pred
|
1997-09-07 07:04:48 +02:00
|
|
|
* Does the "predicate inclusion test" for one conjunct of a predicate
|
2001-08-06 20:09:45 +02:00
|
|
|
* expression for a simple restriction clause. Here we recursively deal
|
|
|
|
* with the possibility that the predicate conjunct is itself an AND or
|
|
|
|
* OR structure.
|
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_recurse_pred(Expr *predicate, Node *clause)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
List *items,
|
|
|
|
*item;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-08-06 20:09:45 +02:00
|
|
|
Assert(predicate != NULL);
|
|
|
|
if (or_clause((Node *) predicate))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2002-12-12 16:49:42 +01:00
|
|
|
items = ((BoolExpr *) predicate)->args;
|
1997-09-07 07:04:48 +02:00
|
|
|
foreach(item, items)
|
|
|
|
{
|
|
|
|
/* if any item is implied, the whole predicate is implied */
|
2001-08-06 20:09:45 +02:00
|
|
|
if (pred_test_recurse_pred(lfirst(item), clause))
|
1997-09-07 07:04:48 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else if (and_clause((Node *) predicate))
|
|
|
|
{
|
2002-12-12 16:49:42 +01:00
|
|
|
items = ((BoolExpr *) predicate)->args;
|
1997-09-07 07:04:48 +02:00
|
|
|
foreach(item, items)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* if any item is not implied, the whole predicate is not
|
|
|
|
* implied
|
|
|
|
*/
|
2001-08-06 20:09:45 +02:00
|
|
|
if (!pred_test_recurse_pred(lfirst(item), clause))
|
1997-09-07 07:04:48 +02:00
|
|
|
return false;
|
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
return true;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
2001-08-06 20:09:45 +02:00
|
|
|
return pred_test_simple_clause(predicate, clause);
|
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) >
|
|
|
|
* and in addition we use (6) to represent <>. <> is not a btree-indexable
|
|
|
|
* 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:
|
|
|
|
*
|
|
|
|
* LT LE EQ GE GT NE
|
|
|
|
*/
|
|
|
|
{BTGE, BTGE, 0, 0, 0, BTGE}, /* LT */
|
|
|
|
{BTGT, BTGE, 0, 0, 0, BTGT}, /* LE */
|
|
|
|
{BTGT, BTGE, BTEQ, BTLE, BTLT, BTNE}, /* EQ */
|
|
|
|
{ 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
|
|
|
|
* kind of expression. (Actually, there is an implied assumption that the
|
|
|
|
* 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
|
|
|
|
* that has "foo" as an input. In this case the clause must yield NULL when
|
|
|
|
* "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
|
|
|
|
* "foo op constant", where "foo" is the same in both clauses. The operators
|
|
|
|
* and constants can be different but the operators must be in the same btree
|
|
|
|
* operator class. We use the above operator implication table to be able to
|
|
|
|
* 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
|
|
|
|
* the operators are too. As of 7.5 it's possible for opclasses to contain
|
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
Expr *nonnullarg = ((NullTest *) predicate)->arg;
|
|
|
|
|
|
|
|
if (is_opclause(clause) &&
|
|
|
|
member(nonnullarg, ((OpExpr *) clause)->args) &&
|
|
|
|
op_strict(((OpExpr *) clause)->opno))
|
|
|
|
return true;
|
|
|
|
if (is_funcclause(clause) &&
|
|
|
|
member(nonnullarg, ((FuncExpr *) clause)->args) &&
|
|
|
|
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.
|
|
|
|
* Note we don't have to think about binary relabeling of the Const node,
|
|
|
|
* since that would have been folded right into the Const.
|
|
|
|
*
|
|
|
|
* 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-01-07 23:02:48 +01: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
|
|
|
|
* 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
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
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
|
|
|
|
* 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).
|
|
|
|
*
|
|
|
|
* If there are multiple matching opclasses, assume we can use any one to
|
|
|
|
* 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,
|
|
|
|
ObjectIdGetDatum(pred_op_negator),
|
|
|
|
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
|
|
|
/*
|
2003-11-12 22:15:59 +01:00
|
|
|
* From the same opclass, find a strategy number for the clause_op,
|
2003-05-13 06:38:58 +02:00
|
|
|
* if possible
|
|
|
|
*/
|
|
|
|
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,
|
|
|
|
ObjectIdGetDatum(clause_op_negator),
|
|
|
|
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.
|
|
|
|
*
|
|
|
|
* Note that we require only the test_op to be immutable, not
|
|
|
|
* the original clause_op. (pred_op must be immutable, else it
|
|
|
|
* 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-01-07 23:02:48 +01: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.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
1999-07-25 19:53:27 +02:00
|
|
|
* 'rel' is the relation for which 'index' is defined
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
2002-11-24 22:52:15 +01:00
|
|
|
static Relids
|
|
|
|
indexable_outerrelids(RelOptInfo *rel, IndexOptInfo *index)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2003-02-08 21:20:55 +01:00
|
|
|
Relids outer_relids = NULL;
|
1999-07-23 05:34:49 +02:00
|
|
|
List *i;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-11-24 22:52:15 +01:00
|
|
|
foreach(i, rel->joininfo)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-07-23 05:34:49 +02:00
|
|
|
JoinInfo *joininfo = (JoinInfo *) lfirst(i);
|
2002-11-24 22:52:15 +01:00
|
|
|
bool match_found = false;
|
|
|
|
List *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];
|
|
|
|
|
2003-05-28 18:04:02 +02:00
|
|
|
if (match_join_clause_to_indexcol(rel,
|
2002-11-24 22:52:15 +01:00
|
|
|
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;
|
|
|
|
List *ilist;
|
|
|
|
List *jlist;
|
|
|
|
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
|
|
|
|
2002-11-24 22:52:15 +01:00
|
|
|
if (jlist == NIL) /* 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,
|
|
|
|
rel,
|
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 */
|
|
|
|
path = make_innerjoin_index_path(root, rel, index,
|
2003-05-26 02:11:29 +02:00
|
|
|
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.
|
|
|
|
*
|
|
|
|
* 'rel' is the relation for which 'index' is defined
|
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,
|
|
|
|
RelOptInfo *rel, IndexOptInfo *index,
|
2003-05-26 02:11:29 +02:00
|
|
|
List *clausegroups)
|
2002-11-24 22:52:15 +01:00
|
|
|
{
|
|
|
|
IndexPath *pathnode = makeNode(IndexPath);
|
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 */
|
2003-05-26 02:11:29 +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
|
|
|
*/
|
|
|
|
pathnode->indexinfo = makeList1(index);
|
2004-01-06 00:39:54 +01:00
|
|
|
pathnode->indexclauses = makeList1(allclauses);
|
|
|
|
pathnode->indexquals = makeList1(indexquals);
|
|
|
|
|
|
|
|
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
|
|
|
*/
|
2003-05-26 02:11:29 +02:00
|
|
|
allclauses = set_ptrUnion(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
|
|
|
|
|
|
|
cost_index(&pathnode->path, root, rel, index, indexquals, true);
|
|
|
|
|
|
|
|
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;
|
|
|
|
List *l;
|
|
|
|
|
|
|
|
foreach(l, clausegroups)
|
|
|
|
{
|
|
|
|
allclauses = nconc(allclauses, listCopy((List *) lfirst(l)));
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
List *orlist;
|
|
|
|
|
|
|
|
/* There's no such thing as an indexpath with zero scans */
|
|
|
|
Assert(indexclauses != NIL);
|
|
|
|
|
|
|
|
foreach(orlist, indexclauses)
|
|
|
|
{
|
|
|
|
List *andlist = (List *) lfirst(orlist);
|
|
|
|
|
|
|
|
/* Strip RestrictInfos */
|
|
|
|
andlist = get_actual_clauses(andlist);
|
|
|
|
/* Insert AND node if needed, and add to orclauses list */
|
|
|
|
orclauses = lappend(orclauses, make_ands_explicit(andlist));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (length(orclauses) > 1)
|
|
|
|
return make_orclause(orclauses);
|
|
|
|
else
|
|
|
|
return (Expr *) lfirst(orclauses);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
* rel: the parent relation
|
|
|
|
* 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,
|
1999-07-23 05:34:49 +02:00
|
|
|
RelOptInfo *rel,
|
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) &&
|
2003-02-08 21:20:55 +01:00
|
|
|
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
|
|
|
*/
|
|
|
|
List *indexprs;
|
|
|
|
int i;
|
|
|
|
Node *indexkey;
|
1999-08-12 06:32:54 +02:00
|
|
|
|
2003-05-28 18:04:02 +02:00
|
|
|
indexprs = index->indexprs;
|
|
|
|
for (i = 0; i < indexcol; i++)
|
|
|
|
{
|
|
|
|
if (index->indexkeys[i] == 0)
|
|
|
|
{
|
|
|
|
if (indexprs == NIL)
|
|
|
|
elog(ERROR, "wrong number of index expressions");
|
|
|
|
indexprs = lnext(indexprs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (indexprs == NIL)
|
|
|
|
elog(ERROR, "wrong number of index expressions");
|
|
|
|
indexkey = (Node *) lfirst(indexprs);
|
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.)
|
|
|
|
*
|
|
|
|
* Two routines are provided here, match_special_index_operator() and
|
|
|
|
* expand_indexqual_conditions(). match_special_index_operator() is
|
2003-05-28 18:04:02 +02:00
|
|
|
* just an auxiliary function for match_clause_to_indexcol(); after
|
1999-07-27 05:51:11 +02:00
|
|
|
* 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.
|
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,
|
1999-07-27 05:51:11 +02:00
|
|
|
* but operators recognized by match_special_index_operator() must be
|
|
|
|
* converted into one or more "regular" indexqual conditions.
|
|
|
|
*----------
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
* opclass) are passed through unchanged. "Special" index operators
|
|
|
|
* are expanded into clauses that the indexscan machinery will know
|
|
|
|
* what to do with.
|
|
|
|
*
|
|
|
|
* 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
|
2003-11-12 22:15:59 +01: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 *
|
2003-05-26 02:11:29 +02:00
|
|
|
expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
|
1999-07-27 05:51:11 +02:00
|
|
|
{
|
2003-05-29 00:32:50 +02:00
|
|
|
FastList resultquals;
|
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
|
|
|
|
2003-05-29 00:32:50 +02:00
|
|
|
FastListInit(&resultquals);
|
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];
|
|
|
|
List *i;
|
|
|
|
|
|
|
|
foreach(i, (List *) lfirst(clausegroups))
|
1999-07-27 05:51:11 +02:00
|
|
|
{
|
2003-05-26 02:11:29 +02:00
|
|
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(i);
|
1999-07-27 05:51:11 +02:00
|
|
|
|
2003-05-29 00:32:50 +02:00
|
|
|
FastConc(&resultquals,
|
2004-01-06 00:39:54 +01:00
|
|
|
expand_indexqual_condition(rinfo, curClass));
|
2003-05-26 02:11:29 +02:00
|
|
|
}
|
2000-09-15 20:45:31 +02:00
|
|
|
|
2003-05-26 02:11:29 +02:00
|
|
|
clausegroups = lnext(clausegroups);
|
1999-07-27 05:51:11 +02:00
|
|
|
|
2003-05-26 02:11:29 +02:00
|
|
|
classes++;
|
1999-07-27 05:51:11 +02:00
|
|
|
|
2003-05-28 18:04:02 +02:00
|
|
|
} while (clausegroups != NIL && !DoneMatchingIndexKeys(classes));
|
2001-06-17 04:05:20 +02:00
|
|
|
|
2003-08-04 02:43:34 +02:00
|
|
|
Assert(clausegroups == NIL); /* else more groups than indexkeys... */
|
1999-07-27 05:51:11 +02:00
|
|
|
|
2003-05-29 00:32:50 +02:00
|
|
|
return FastListValue(&resultquals);
|
1999-07-27 05:51:11 +02:00
|
|
|
}
|
|
|
|
|
2003-05-26 02:11:29 +02:00
|
|
|
/*
|
|
|
|
* expand_indexqual_condition --- expand a single indexqual condition
|
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-01-06 00:39:54 +01:00
|
|
|
result = makeList1(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-01-06 00:39:54 +01:00
|
|
|
result = makeList1(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-01-06 00:39:54 +01:00
|
|
|
result = makeList1(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-01-06 00:39:54 +01:00
|
|
|
result = makeList1(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
|
|
|
}
|