Commit ac4913a0 authored by Tom Lane's avatar Tom Lane

Clean up messy clause-selectivity code in clausesel.c; repair bug

identified by Hiroshi (incorrect cost attributed to OR clauses
after multiple passes through set_rest_selec()).  I think the code
was trying to allow selectivities of OR subclauses to be passed in
from outside, but noplace was actually passing any useful data, and
set_rest_selec() was passing wrong data.

Restructure representation of "indexqual" in IndexPath nodes so that
it is the same as for indxqual in completed IndexScan nodes: namely,
a toplevel list with an entry for each pass of the index scan, having
sublists that are implicitly-ANDed index qual conditions for that pass.
You don't want to know what the old representation was :-(

Improve documentation of OR-clause indexscan functions.

Remove useless 'notclause' field from RestrictInfo nodes.  (This might
force an initdb for anyone who has stored rules containing RestrictInfos,
but I do not think that RestrictInfo ever appears in completed plans.)
parent 348bdbce
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.86 1999/07/17 20:17:05 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.87 1999/07/24 23:21:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1317,7 +1317,6 @@ _copyRestrictInfo(RestrictInfo *from) ...@@ -1317,7 +1317,6 @@ _copyRestrictInfo(RestrictInfo *from)
Node_Copy(from, newnode, clause); Node_Copy(from, newnode, clause);
newnode->selectivity = from->selectivity; newnode->selectivity = from->selectivity;
newnode->notclause = from->notclause;
Node_Copy(from, newnode, indexids); Node_Copy(from, newnode, indexids);
Node_Copy(from, newnode, mergejoinorder); Node_Copy(from, newnode, mergejoinorder);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.43 1999/07/17 20:17:05 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.44 1999/07/24 23:21:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -305,8 +305,6 @@ _equalRestrictInfo(RestrictInfo *a, RestrictInfo *b) ...@@ -305,8 +305,6 @@ _equalRestrictInfo(RestrictInfo *a, RestrictInfo *b)
return false; return false;
if (a->selectivity != b->selectivity) if (a->selectivity != b->selectivity)
return false; return false;
if (a->notclause != b->notclause)
return false;
#ifdef EqualMergeOrderExists #ifdef EqualMergeOrderExists
if (!EqualMergeOrder(a->mergejoinorder, b->mergejoinorder)) if (!EqualMergeOrder(a->mergejoinorder, b->mergejoinorder))
return false; return false;
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* $Id: outfuncs.c,v 1.90 1999/07/18 19:02:49 tgl Exp $ * $Id: outfuncs.c,v 1.91 1999/07/24 23:21:07 tgl Exp $
* *
* NOTES * NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which * Every (plan) node in POSTGRES has an associated "out" routine which
...@@ -1110,9 +1110,8 @@ _outRestrictInfo(StringInfo str, RestrictInfo *node) ...@@ -1110,9 +1110,8 @@ _outRestrictInfo(StringInfo str, RestrictInfo *node)
_outNode(str, node->clause); _outNode(str, node->clause);
appendStringInfo(str, appendStringInfo(str,
" :selectivity %f :notclause %s :indexids ", " :selectivity %f :indexids ",
node->selectivity, node->selectivity);
node->notclause ? "true" : "false");
_outNode(str, node->indexids); _outNode(str, node->indexids);
appendStringInfo(str, " :mergejoinorder "); appendStringInfo(str, " :mergejoinorder ");
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.69 1999/07/17 20:17:08 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.70 1999/07/24 23:21:08 tgl Exp $
* *
* NOTES * NOTES
* Most of the read functions for plan nodes are tested. (In fact, they * Most of the read functions for plan nodes are tested. (In fact, they
...@@ -1856,14 +1856,6 @@ _readRestrictInfo() ...@@ -1856,14 +1856,6 @@ _readRestrictInfo()
local_node->selectivity = atof(token); local_node->selectivity = atof(token);
token = lsptok(NULL, &length); /* get :notclause */
token = lsptok(NULL, &length); /* now read it */
if (!strncmp(token, "true", 4))
local_node->notclause = true;
else
local_node->notclause = false;
token = lsptok(NULL, &length); /* get :indexids */ token = lsptok(NULL, &length); /* get :indexids */
local_node->indexids = nodeRead(true); /* now read it */ local_node->indexids = nodeRead(true); /* now read it */
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.50 1999/07/17 20:17:11 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.51 1999/07/24 23:21:08 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -86,8 +86,8 @@ make_one_rel(Query *root, List *rels) ...@@ -86,8 +86,8 @@ make_one_rel(Query *root, List *rels)
* set_base_rel_pathlist * set_base_rel_pathlist
* Finds all paths available for scanning each relation entry in * Finds all paths available for scanning each relation entry in
* 'rels'. Sequential scan and any available indices are considered * 'rels'. Sequential scan and any available indices are considered
* if possible(indices are not considered for lower nesting levels). * if possible (indices are not considered for lower nesting levels).
* All unique paths are attached to the relation's 'pathlist' field. * All useful paths are attached to the relation's 'pathlist' field.
* *
* MODIFIES: rels * MODIFIES: rels
*/ */
...@@ -98,21 +98,32 @@ set_base_rel_pathlist(Query *root, List *rels) ...@@ -98,21 +98,32 @@ set_base_rel_pathlist(Query *root, List *rels)
foreach(temp, rels) foreach(temp, rels)
{ {
RelOptInfo *rel = (RelOptInfo *) lfirst(temp);
List *indices = find_relation_indices(root, rel);
List *sequential_scan_list; List *sequential_scan_list;
List *rel_index_scan_list; List *rel_index_scan_list;
List *or_index_scan_list; List *or_index_scan_list;
RelOptInfo *rel = (RelOptInfo *) lfirst(temp);
sequential_scan_list = lcons(create_seqscan_path(rel), NIL); sequential_scan_list = lcons(create_seqscan_path(rel), NIL);
rel_index_scan_list = create_index_paths(root, rel_index_scan_list = create_index_paths(root,
rel, rel,
find_relation_indices(root, rel), indices,
rel->restrictinfo, rel->restrictinfo,
rel->joininfo); rel->joininfo);
or_index_scan_list = create_or_index_paths(root, rel, rel->restrictinfo); /* Note: create_or_index_paths depends on create_index_paths
* to have marked OR restriction clauses with relevant indices;
* this is why it doesn't need to be given the full list of indices.
*/
or_index_scan_list = create_or_index_paths(root, rel,
rel->restrictinfo);
/* add_pathlist will discard any paths that are dominated by
* another available path, keeping only those paths that are
* superior along at least one dimension of cost or sortedness.
*/
rel->pathlist = add_pathlist(rel, rel->pathlist = add_pathlist(rel,
sequential_scan_list, sequential_scan_list,
nconc(rel_index_scan_list, nconc(rel_index_scan_list,
...@@ -128,7 +139,6 @@ set_base_rel_pathlist(Query *root, List *rels) ...@@ -128,7 +139,6 @@ set_base_rel_pathlist(Query *root, List *rels)
rel->size = compute_rel_size(rel); rel->size = compute_rel_size(rel);
rel->width = compute_rel_width(rel); rel->width = compute_rel_width(rel);
} }
return;
} }
/* /*
......
This diff is collapsed.
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.62 1999/07/23 03:34:49 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.63 1999/07/24 23:21:09 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -109,12 +109,25 @@ create_index_paths(Query *root, ...@@ -109,12 +109,25 @@ create_index_paths(Query *root,
continue; continue;
/* /*
* 1. Try matching the index against subclauses of an 'or' clause. * 1. Try matching the index against subclauses of restriction 'or'
* The fields of the restrictinfo nodes are marked with lists of * clauses (ie, 'or' clauses that reference only this relation).
* the matching indices. No paths are actually created. We * The restrictinfo nodes for the 'or' clauses are marked with lists
* currently only look to match the first key. We don't find * of the matching indices. No paths are actually created now;
* multi-key index cases where an AND matches the first key, and * that will be done in orindxpath.c after all indexes for the rel
* the OR matches the second key. * have been examined. (We need to do it that way because we can
* potentially use a different index for each subclause of an 'or',
* so we can't build a path for an 'or' clause until all indexes have
* been matched against it.)
*
* We currently only look to match the first key of each index against
* 'or' subclauses. There are cases where a later key of a multi-key
* index could be used (if other top-level clauses match earlier keys
* of the index), but our poor brains are hurting already...
*
* We don't even think about special handling of 'or' clauses that
* involve more than one relation, since they can't be processed by
* a single indexscan path anyway. Currently, cnfify() is certain
* to have restructured any such toplevel 'or' clauses anyway.
*/ */
match_index_orclauses(rel, match_index_orclauses(rel,
index, index,
...@@ -123,7 +136,7 @@ create_index_paths(Query *root, ...@@ -123,7 +136,7 @@ create_index_paths(Query *root,
restrictinfo_list); restrictinfo_list);
/* /*
* 2. If the keys of this index match any of the available * 2. If the keys of this index match any of the available non-'or'
* restriction clauses, then create a path using those clauses * restriction clauses, then create a path using those clauses
* as indexquals. * as indexquals.
*/ */
...@@ -179,11 +192,14 @@ create_index_paths(Query *root, ...@@ -179,11 +192,14 @@ create_index_paths(Query *root,
/* /*
* match_index_orclauses * match_index_orclauses
* Attempt to match an index against subclauses within 'or' clauses. * Attempt to match an index against subclauses within 'or' clauses.
* If the index does match, then the clause is marked with information * Each subclause that does match is marked with the index's node.
* about the index.
* *
* Essentially, this adds 'index' to the list of indices in the * Essentially, this adds 'index' to the list of subclause indices in
* RestrictInfo field of each of the clauses which it matches. * the RestrictInfo field of each of the 'or' clauses where it matches.
* NOTE: we can use storage in the RestrictInfo for this purpose because
* this processing is only done on single-relation restriction clauses.
* Therefore, we will never have indexes for more than one relation
* mentioned in the same RestrictInfo node's list.
* *
* 'rel' is the node of the relation on which the index is defined. * 'rel' is the node of the relation on which the index is defined.
* 'index' is the index node. * 'index' is the index node.
...@@ -204,12 +220,11 @@ match_index_orclauses(RelOptInfo *rel, ...@@ -204,12 +220,11 @@ match_index_orclauses(RelOptInfo *rel,
{ {
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i); RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i);
if (valid_or_clause(restrictinfo)) if (restriction_is_or_clause(restrictinfo))
{ {
/* /*
* Mark the 'or' clause with a list of indices which match * Add this index to the subclause index list for each
* each of its subclauses. We add entries to the existing * subclause that it matches.
* list, if any.
*/ */
restrictinfo->indexids = restrictinfo->indexids =
match_index_orclause(rel, index, match_index_orclause(rel, index,
...@@ -253,7 +268,9 @@ match_index_orclause(RelOptInfo *rel, ...@@ -253,7 +268,9 @@ match_index_orclause(RelOptInfo *rel,
List *index_list; List *index_list;
List *clist; List *clist;
/* first time through, we create empty list of same length as OR clause */ /* first time through, we create list of same length as OR clause,
* containing an empty sublist for each subclause.
*/
if (!other_matching_indices) if (!other_matching_indices)
{ {
matching_indices = NIL; matching_indices = NIL;
...@@ -1186,9 +1203,13 @@ index_innerjoin(Query *root, RelOptInfo *rel, List *clausegroup_list, ...@@ -1186,9 +1203,13 @@ index_innerjoin(Query *root, RelOptInfo *rel, List *clausegroup_list,
pathnode->path.pathorder->ord.sortop = index->ordering; pathnode->path.pathorder->ord.sortop = index->ordering;
pathnode->path.pathkeys = NIL; pathnode->path.pathkeys = NIL;
/* Note that we are making a pathnode for a single-scan indexscan;
* therefore, both indexid and indexqual should be single-element
* lists.
*/
pathnode->indexid = index->relids; pathnode->indexid = index->relids;
pathnode->indexkeys = index->indexkeys; pathnode->indexkeys = index->indexkeys;
pathnode->indexqual = clausegroup; pathnode->indexqual = lcons(get_actual_clauses(clausegroup), NIL);
pathnode->path.joinid = ((RestrictInfo *) lfirst(clausegroup))->restrictinfojoinid; pathnode->path.joinid = ((RestrictInfo *) lfirst(clausegroup))->restrictinfojoinid;
......
This diff is collapsed.
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.62 1999/07/17 20:17:13 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.63 1999/07/24 23:21:11 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -301,54 +301,30 @@ create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses) ...@@ -301,54 +301,30 @@ create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses)
* create_indexscan_node * create_indexscan_node
* Returns a indexscan node for the base relation scanned by 'best_path' * Returns a indexscan node for the base relation scanned by 'best_path'
* with restriction clauses 'scan_clauses' and targetlist 'tlist'. * with restriction clauses 'scan_clauses' and targetlist 'tlist'.
*
* If an 'or' clause is to be used with this index, the indxqual field
* will contain a list of the 'or' clause arguments, e.g., the
* clause(OR a b c) will generate: ((a) (b) (c)). Otherwise, the
* indxqual will simply contain one conjunctive qualification: ((a)).
*/ */
static IndexScan * static IndexScan *
create_indexscan_node(IndexPath *best_path, create_indexscan_node(IndexPath *best_path,
List *tlist, List *tlist,
List *scan_clauses) List *scan_clauses)
{ {
List *indxqual = best_path->indexqual;
/* List *qpqual;
* Extract the(first if conjunct, only if disjunct) clause from the List *fixed_indxqual;
* restrictinfo list.
*/
Expr *index_clause = (Expr *) NULL;
List *indxqual = NIL;
List *qpqual = NIL;
List *fixed_indxqual = NIL;
List *ixid; List *ixid;
IndexScan *scan_node = (IndexScan *) NULL; IndexScan *scan_node;
bool lossy = FALSE; bool lossy = false;
HeapTuple indexTuple;
Form_pg_index index;
/*
* If an 'or' clause is to be used with this index, the indxqual field
* will contain a list of the 'or' clause arguments, e.g., the
* clause(OR a b c) will generate: ((a) (b) (c)). Otherwise, the
* indxqual will simply contain one conjunctive qualification: ((a)).
*/
if (best_path->indexqual != NULL)
/* added call to fix_opids, JMH 6/23/92 */
index_clause = (Expr *)
lfirst(fix_opids(get_actual_clauses(best_path->indexqual)));
if (or_clause((Node *) index_clause))
{
List *temp = NIL;
foreach(temp, index_clause->args)
indxqual = lappend(indxqual, lcons(lfirst(temp), NIL));
}
else
{
indxqual = lcons(get_actual_clauses(best_path->indexqual),
NIL);
}
/* check and see if any indices are lossy */ /* check and see if any indices are lossy */
foreach(ixid, best_path->indexid) foreach(ixid, best_path->indexid)
{ {
HeapTuple indexTuple;
Form_pg_index index;
indexTuple = SearchSysCacheTuple(INDEXRELID, indexTuple = SearchSysCacheTuple(INDEXRELID,
ObjectIdGetDatum(lfirsti(ixid)), ObjectIdGetDatum(lfirsti(ixid)),
0, 0, 0); 0, 0, 0);
...@@ -356,34 +332,70 @@ create_indexscan_node(IndexPath *best_path, ...@@ -356,34 +332,70 @@ create_indexscan_node(IndexPath *best_path,
elog(ERROR, "create_plan: index %u not found", lfirsti(ixid)); elog(ERROR, "create_plan: index %u not found", lfirsti(ixid));
index = (Form_pg_index) GETSTRUCT(indexTuple); index = (Form_pg_index) GETSTRUCT(indexTuple);
if (index->indislossy) if (index->indislossy)
lossy = TRUE; {
lossy = true;
break;
}
} }
/* /*
* The qpqual field contains all restrictions not automatically * The qpqual list must contain all restrictions not automatically
* handled by the index. Note that for non-lossy indices, the * handled by the index. Note that for non-lossy indices, the
* predicates in the indxqual are handled by the index, while for * predicates in the indxqual are handled by the index, while for
* lossy indices the indxqual predicates need to be double-checked * lossy indices the indxqual predicates need to be double-checked
* after the index fetches the best-guess tuples. * after the index fetches the best-guess tuples.
*
* There should not be any clauses in scan_clauses that duplicate
* expressions checked by the index, but just in case, we will
* get rid of them via set_difference.
*/ */
if (or_clause((Node *) index_clause)) if (length(indxqual) > 1)
{ {
/*
* Build an expression representation of the indexqual, expanding
* the implicit OR and AND semantics of the first- and second-level
* lists. XXX Is it really necessary to do a deep copy here?
*/
List *orclauses = NIL;
List *orclause;
Expr *indxqual_expr;
foreach(orclause, indxqual)
{
orclauses = lappend(orclauses,
make_ands_explicit((List *) copyObject(lfirst(orclause))));
}
indxqual_expr = make_orclause(orclauses);
/* this set_difference is almost certainly a waste of time... */
qpqual = set_difference(scan_clauses, qpqual = set_difference(scan_clauses,
lcons(index_clause, NIL)); lcons(indxqual_expr, NIL));
if (lossy) if (lossy)
qpqual = lappend(qpqual, (List *) copyObject(index_clause)); qpqual = lappend(qpqual, indxqual_expr);
} }
else else if (indxqual != NIL)
{ {
/* Here, we can simply treat the first sublist as an independent
* set of qual expressions, since there is no top-level OR behavior.
*/
qpqual = set_difference(scan_clauses, lfirst(indxqual)); qpqual = set_difference(scan_clauses, lfirst(indxqual));
if (lossy) if (lossy)
qpqual = nconc(qpqual, qpqual = nconc(qpqual, (List *) copyObject(lfirst(indxqual)));
(List *) copyObject(lfirst(indxqual)));
} }
else
qpqual = NIL;
/*
* Fix opids in the completed indxqual. We don't want to do this sooner
* since it would screw up the set_difference calcs above. Really,
* this ought to only happen at final exit from the planner...
*/
indxqual = fix_opids(indxqual);
fixed_indxqual = (List *) fix_indxqual_references((Node *) indxqual, (Path *) best_path); /* The executor needs a copy with index attrs substituted for table ones */
fixed_indxqual = (List *) fix_indxqual_references((Node *) indxqual,
(Path *) best_path);
scan_node = make_indexscan(tlist, scan_node = make_indexscan(tlist,
qpqual, qpqual,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.34 1999/07/16 04:59:19 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.35 1999/07/24 23:21:12 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -25,12 +25,11 @@ ...@@ -25,12 +25,11 @@
#include "optimizer/var.h" #include "optimizer/var.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
extern int Quiet;
static void add_restrict_and_join_to_rel(Query *root, List *clause); static void add_restrict_and_join_to_rel(Query *root, Node *clause);
static void add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo, static void add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
Relids join_relids); Relids join_relids);
static void add_vars_to_targetlist(Query *root, List *vars, Relids join_relids); static void add_vars_to_targetlist(Query *root, List *vars);
static MergeOrder *mergejoinop(Expr *clause); static MergeOrder *mergejoinop(Expr *clause);
static Oid hashjoinop(Expr *clause); static Oid hashjoinop(Expr *clause);
...@@ -129,7 +128,7 @@ add_missing_vars_to_tlist(Query *root, List *tlist) ...@@ -129,7 +128,7 @@ add_missing_vars_to_tlist(Query *root, List *tlist)
* relations appearing within clauses. Creates new relation entries if * relations appearing within clauses. Creates new relation entries if
* necessary, adding them to *query_relation_list*. * necessary, adding them to *query_relation_list*.
* *
* Returns nothing of interest. * 'clauses': the list of clauses in the cnfify'd query qualification.
*/ */
void void
add_restrict_and_join_to_rels(Query *root, List *clauses) add_restrict_and_join_to_rels(Query *root, List *clauses)
...@@ -137,95 +136,71 @@ add_restrict_and_join_to_rels(Query *root, List *clauses) ...@@ -137,95 +136,71 @@ add_restrict_and_join_to_rels(Query *root, List *clauses)
List *clause; List *clause;
foreach(clause, clauses) foreach(clause, clauses)
add_restrict_and_join_to_rel(root, lfirst(clause)); add_restrict_and_join_to_rel(root, (Node*) lfirst(clause));
return;
} }
/* /*
* add_restrict_and_join_to_rel- * add_restrict_and_join_to_rel-
* Add clause information to either the 'RestrictInfo' or 'JoinInfo' field * Add clause information to either the 'RestrictInfo' or 'JoinInfo' field
* of a relation entry(depending on whether or not the clause is a join) * of a relation entry (depending on whether or not the clause is a join)
* by creating a new RestrictInfo node and setting appropriate fields * by creating a new RestrictInfo node and setting appropriate fields
* within the nodes. * within the nodes.
*
* Returns nothing of interest.
*/ */
static void static void
add_restrict_and_join_to_rel(Query *root, List *clause) add_restrict_and_join_to_rel(Query *root, Node *clause)
{ {
RestrictInfo *restrictinfo = makeNode(RestrictInfo);
Relids relids; Relids relids;
List *vars; List *vars;
RestrictInfo *restrictinfo = makeNode(RestrictInfo);
/*
* Retrieve all relids and vars contained within the clause.
*/
clause_get_relids_vars((Node *) clause, &relids, &vars);
restrictinfo->clause = (Expr *) clause; restrictinfo->clause = (Expr *) clause;
restrictinfo->notclause = contains_not((Node *) clause);
restrictinfo->selectivity = 0;
restrictinfo->indexids = NIL; restrictinfo->indexids = NIL;
restrictinfo->mergejoinorder = (MergeOrder *) NULL; restrictinfo->mergejoinorder = (MergeOrder *) NULL;
restrictinfo->hashjoinoperator = (Oid) 0; restrictinfo->hashjoinoperator = (Oid) 0;
/*
* The selectivity of the clause must be computed regardless of
* whether it's a restriction or a join clause
*/
restrictinfo->selectivity = compute_clause_selec(root, clause);
/*
* Retrieve all relids and vars contained within the clause.
*/
clause_get_relids_vars(clause, &relids, &vars);
if (length(relids) == 1) if (length(relids) == 1)
{ {
/* /*
* There is only one relation participating in 'clause', so * There is only one relation participating in 'clause', so
* 'clause' must be a restriction clause. * 'clause' must be a restriction clause for that relation.
*/ */
RelOptInfo *rel = get_base_rel(root, lfirsti(relids)); RelOptInfo *rel = get_base_rel(root, lfirsti(relids));
/*
* The selectivity of the clause must be computed regardless of
* whether it's a restriction or a join clause
*/
if (is_funcclause((Node *) clause))
/*
* XXX If we have a func clause set selectivity to 1/3, really
* need a true selectivity function.
*/
restrictinfo->selectivity = (Cost) 0.3333333;
else
restrictinfo->selectivity = compute_clause_selec(root, (Node *) clause, NIL);
rel->restrictinfo = lcons(restrictinfo, rel->restrictinfo); rel->restrictinfo = lcons(restrictinfo, rel->restrictinfo);
} }
else else
{ {
/* /*
* 'clause' is a join clause, since there is more than one atom in * 'clause' is a join clause, since there is more than one atom in
* the relid list. * the relid list. Add it to the join lists of all the relevant
* relations. (If, perchance, 'clause' contains NO vars, then
* nothing will happen...)
*/ */
if (is_funcclause((Node *) clause))
/*
* XXX If we have a func clause set selectivity to 1/3, really
* need a true selectivity function.
*/
restrictinfo->selectivity = (Cost) 0.3333333;
else
restrictinfo->selectivity = compute_clause_selec(root, (Node *) clause, NIL);
add_join_info_to_rels(root, restrictinfo, relids); add_join_info_to_rels(root, restrictinfo, relids);
/* we are going to be doing a join, so add var to targetlist */ /* we are going to be doing a join, so add vars to targetlists */
add_vars_to_targetlist(root, vars, relids); add_vars_to_targetlist(root, vars);
} }
} }
/* /*
* add_join_info_to_rels * add_join_info_to_rels
* For every relation participating in a join clause, add 'restrictinfo' to * For every relation participating in a join clause, add 'restrictinfo' to
* the appropriate joininfo node(creating a new one and adding it to the * the appropriate joininfo node (creating a new one and adding it to the
* appropriate rel node if necessary). * appropriate rel node if necessary).
* *
* 'restrictinfo' describes the join clause * 'restrictinfo' describes the join clause
* 'join_relids' is the list of relations participating in the join clause * 'join_relids' is the list of relations participating in the join clause
*
*/ */
static void static void
add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo, add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
...@@ -233,7 +208,7 @@ add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo, ...@@ -233,7 +208,7 @@ add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
{ {
List *join_relid; List *join_relid;
/* For every relid, find the rel, and add the proper join entries */ /* For every relid, find the joininfo, and add the proper join entries */
foreach(join_relid, join_relids) foreach(join_relid, join_relids)
{ {
JoinInfo *joininfo; JoinInfo *joininfo;
...@@ -247,43 +222,39 @@ add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo, ...@@ -247,43 +222,39 @@ add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
unjoined_relids = lappendi(unjoined_relids, lfirsti(rel)); unjoined_relids = lappendi(unjoined_relids, lfirsti(rel));
} }
/*
* Find or make the joininfo node for this combination of rels
*/
joininfo = find_joininfo_node(get_base_rel(root, lfirsti(join_relid)), joininfo = find_joininfo_node(get_base_rel(root, lfirsti(join_relid)),
unjoined_relids); unjoined_relids);
/*
* And add the restrictinfo node to it. NOTE that each joininfo
* gets its own copy of the restrictinfo node! (Is this really
* necessary? Possibly ... later parts of the optimizer destructively
* modify restrict/join clauses...)
*/
joininfo->jinfo_restrictinfo = lcons(copyObject((void *) restrictinfo), joininfo->jinfo_restrictinfo = lcons(copyObject((void *) restrictinfo),
joininfo->jinfo_restrictinfo); joininfo->jinfo_restrictinfo);
} }
} }
/* /*
* add_vars_to_targetlist * add_vars_to_targetlist
* For each variable appearing in a clause, * For each variable appearing in a clause, add it to the relation's
* (1) If a targetlist entry for the variable is not already present in * targetlist if not already present.
* the appropriate relation's target list, add one.
* (2) If a targetlist entry is already present, but the var is part of a
* join clause, add the relids of the join relations to the JoinList
* entry of the targetlist entry.
*
* 'vars' is the list of var nodes
* 'join_relids' is the list of relids appearing in the join clause
* (if this is a join clause)
*
* Returns nothing.
*/ */
static void static void
add_vars_to_targetlist(Query *root, List *vars, Relids join_relids) add_vars_to_targetlist(Query *root, List *vars)
{ {
Var *var; List *temp;
List *temp = NIL;
RelOptInfo *rel = (RelOptInfo *) NULL;
TargetEntry *tlistentry;
foreach(temp, vars) foreach(temp, vars)
{ {
var = (Var *) lfirst(temp); Var *var = (Var *) lfirst(temp);
rel = get_base_rel(root, var->varno); RelOptInfo *rel = get_base_rel(root, var->varno);
tlistentry = tlistentry_member(var, rel->targetlist);
if (tlistentry == NULL) if (tlistentry_member(var, rel->targetlist) == NULL)
/* add a new entry */
add_var_to_tlist(rel, var); add_var_to_tlist(rel, var);
} }
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.40 1999/07/16 04:59:23 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.41 1999/07/24 23:21:13 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -290,6 +290,21 @@ make_andclause(List *andclauses) ...@@ -290,6 +290,21 @@ make_andclause(List *andclauses)
return expr; return expr;
} }
/*
* Sometimes (such as in the result of cnfify), we use lists of expression
* nodes with implicit AND semantics. This function converts back to an
* explicit-AND representation.
*/
Expr *
make_ands_explicit(List *andclauses)
{
if (andclauses == NIL)
return NULL;
else if (length(andclauses) == 1)
return (Expr *) lfirst(andclauses);
else
return make_andclause(andclauses);
}
/***************************************************************************** /*****************************************************************************
* CASE clause functions * CASE clause functions
...@@ -410,38 +425,6 @@ NumRelids(Node *clause) ...@@ -410,38 +425,6 @@ NumRelids(Node *clause)
return length(var_list); return length(var_list);
} }
/*
* contains_not
*
* Returns t iff the clause is a 'not' clause or if any of the
* subclauses within an 'or' clause contain 'not's.
*
* NOTE that only the top-level AND/OR structure is searched for NOTs;
* we are not interested in buried substructure.
*/
bool
contains_not(Node *clause)
{
if (single_node(clause))
return false;
if (not_clause(clause))
return true;
if (or_clause(clause) || and_clause(clause))
{
List *a;
foreach(a, ((Expr *) clause)->args)
{
if (contains_not(lfirst(a)))
return true;
}
}
return false;
}
/* /*
* is_joinable * is_joinable
* *
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.46 1999/07/16 04:59:25 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.47 1999/07/24 23:21:14 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -426,7 +426,8 @@ create_index_path(Query *root, ...@@ -426,7 +426,8 @@ create_index_path(Query *root,
/* each clause gets an equal selectivity */ /* each clause gets an equal selectivity */
clausesel = pow(selec, 1.0 / (double) length(restriction_clauses)); clausesel = pow(selec, 1.0 / (double) length(restriction_clauses));
pathnode->indexqual = restriction_clauses; pathnode->indexqual = lcons(get_actual_clauses(restriction_clauses),
NIL);
pathnode->path.path_cost = cost_index(lfirsti(index->relids), pathnode->path.path_cost = cost_index(lfirsti(index->relids),
(int) npages, (int) npages,
selec, selec,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.6 1999/07/16 04:59:27 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.7 1999/07/24 23:21:14 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -20,17 +20,15 @@ ...@@ -20,17 +20,15 @@
#include "optimizer/restrictinfo.h" #include "optimizer/restrictinfo.h"
/* /*
* valid_or_clause * restriction_is_or_clause
* *
* Returns t iff the restrictinfo node contains a 'normal' 'or' clause. * Returns t iff the restrictinfo node contains an 'or' clause.
* *
*/ */
bool bool
valid_or_clause(RestrictInfo *restrictinfo) restriction_is_or_clause(RestrictInfo *restrictinfo)
{ {
if (restrictinfo != NULL && if (restrictinfo != NULL &&
!single_node((Node *) restrictinfo->clause) &&
!restrictinfo->notclause &&
or_clause((Node *) restrictinfo->clause)) or_clause((Node *) restrictinfo->clause))
return true; return true;
else else
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* $Id: relation.h,v 1.34 1999/07/15 23:03:56 momjian Exp $ * $Id: relation.h,v 1.35 1999/07/24 23:21:04 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -151,6 +151,23 @@ typedef struct Path ...@@ -151,6 +151,23 @@ typedef struct Path
List *loc_restrictinfo; List *loc_restrictinfo;
} Path; } Path;
/*----------
* IndexPath represents an index scan. Although an indexscan can only read
* a single relation, it can scan it more than once, potentially using a
* different index during each scan. The result is the union (OR) of all the
* tuples matched during any scan. (The executor is smart enough not to return
* the same tuple more than once, even if it is matched in multiple scans.)
* 'indexid' is a list of index relation OIDs, one per scan to be performed.
* 'indexqual' is a list of index qualifications, also one per scan.
* Each entry in 'indexqual' is a sublist of qualification expressions with
* implicit AND semantics across the sublist items. Each one of the sublist
* items must be an operator expression of the form (var op something) or
* (something op var), where the var is a field the associated index keys on
* and the op is a member of the operator class of the index.
* NOTE that the semantics of the top-level list in 'indexqual' is OR
* combination, while the sublists are implicitly AND combinations!
*----------
*/
typedef struct IndexPath typedef struct IndexPath
{ {
Path path; Path path;
...@@ -205,16 +222,20 @@ typedef struct JoinKey ...@@ -205,16 +222,20 @@ typedef struct JoinKey
} JoinKey; } JoinKey;
/* /*
* clause info * Restriction clause info.
* We create one of these for each AND sub-clause of a restriction condition
* (WHERE clause). Since the restriction clauses are logically ANDed, we
* can use any one of them or any subset of them to filter out tuples,
* without having to evaluate the rest. The RestrictInfo node itself stores
* data used by the optimizer while choosing the best query plan.
*/ */
typedef struct RestrictInfo typedef struct RestrictInfo
{ {
NodeTag type; NodeTag type;
Expr *clause; /* should be an OP clause */ Expr *clause; /* the represented subclause of WHERE cond */
Cost selectivity; Cost selectivity; /* estimated selectivity */
bool notclause; List *indexids; /* subclause index IDs if clause is an OR */
List *indexids;
/* mergejoin only */ /* mergejoin only */
MergeOrder *mergejoinorder; MergeOrder *mergejoinorder;
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* $Id: clauses.h,v 1.21 1999/07/15 23:03:57 momjian Exp $ * $Id: clauses.h,v 1.22 1999/07/24 23:21:05 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -34,13 +34,13 @@ extern Expr *get_notclausearg(Expr *notclause); ...@@ -34,13 +34,13 @@ extern Expr *get_notclausearg(Expr *notclause);
extern bool and_clause(Node *clause); extern bool and_clause(Node *clause);
extern Expr *make_andclause(List *andclauses); extern Expr *make_andclause(List *andclauses);
extern Expr *make_ands_explicit(List *andclauses);
extern bool case_clause(Node *clause); extern bool case_clause(Node *clause);
extern List *pull_constant_clauses(List *quals, List **constantQual); extern List *pull_constant_clauses(List *quals, List **constantQual);
extern void clause_get_relids_vars(Node *clause, Relids *relids, List **vars); extern void clause_get_relids_vars(Node *clause, Relids *relids, List **vars);
extern int NumRelids(Node *clause); extern int NumRelids(Node *clause);
extern bool contains_not(Node *clause);
extern bool is_joinable(Node *clause); extern bool is_joinable(Node *clause);
extern bool qual_clause_p(Node *clause); extern bool qual_clause_p(Node *clause);
extern void fix_opid(Node *clause); extern void fix_opid(Node *clause);
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* $Id: cost.h,v 1.21 1999/07/15 15:21:20 momjian Exp $ * $Id: cost.h,v 1.22 1999/07/24 23:21:05 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -59,7 +59,6 @@ extern void set_clause_selectivities(List *restrictinfo_list, Cost new_selectivi ...@@ -59,7 +59,6 @@ extern void set_clause_selectivities(List *restrictinfo_list, Cost new_selectivi
extern Cost product_selec(List *restrictinfo_list); extern Cost product_selec(List *restrictinfo_list);
extern void set_rest_relselec(Query *root, List *rel_list); extern void set_rest_relselec(Query *root, List *rel_list);
extern void set_rest_selec(Query *root, List *restrictinfo_list); extern void set_rest_selec(Query *root, List *restrictinfo_list);
extern Cost compute_clause_selec(Query *root, extern Cost compute_clause_selec(Query *root, Node *clause);
Node *clause, List *or_selectivities);
#endif /* COST_H */ #endif /* COST_H */
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* $Id: restrictinfo.h,v 1.5 1999/07/15 15:21:23 momjian Exp $ * $Id: restrictinfo.h,v 1.6 1999/07/24 23:21:05 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
#include "nodes/relation.h" #include "nodes/relation.h"
extern bool valid_or_clause(RestrictInfo *restrictinfo); extern bool restriction_is_or_clause(RestrictInfo *restrictinfo);
extern List *get_actual_clauses(List *restrictinfo_list); extern List *get_actual_clauses(List *restrictinfo_list);
extern void get_relattvals(List *restrictinfo_list, List **attnos, extern void get_relattvals(List *restrictinfo_list, List **attnos,
List **values, List **flags); List **values, List **flags);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment