Commit 9e7e29e6 authored by Tom Lane's avatar Tom Lane

First cut at doing LIKE/regex indexing optimization in

optimizer rather than parser.  This has many advantages, such as not
getting fooled by chance uses of operator names ~ and ~~ (the operators
are identified by OID now), and not creating useless comparison operations
in contexts where the comparisons will not actually be used as indexquals.
The new code also recognizes exact-match LIKE and regex patterns, and
produces an = indexqual instead of >= and <=.

This change does NOT fix the problem with non-ASCII locales: the code
still doesn't know how to generate an upper bound indexqual for non-ASCII
collation order.  But it's no worse than before, just the same deficiency
in a different place...

Also, dike out loc_restrictinfo fields in Plan nodes.  These were doing
nothing useful in the absence of 'expensive functions' optimization,
and they took a considerable amount of processing to fill in.
parent 434df3fb
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.88 1999/07/25 17:53:27 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.89 1999/07/27 03:51:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1097,7 +1097,6 @@ CopyPathFields(Path *from, Path *newnode)
newnode->outerjoincost = from->outerjoincost;
newnode->joinid = listCopy(from->joinid);
Node_Copy(from, newnode, loc_restrictinfo);
}
/* ----------------
......
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.23 1999/07/25 17:53:27 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.24 1999/07/27 03:51:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -750,7 +750,6 @@ FreePathFields(Path *node)
freeObject(node->pathkeys);
freeList(node->joinid);
freeObject(node->loc_restrictinfo);
}
/* ----------------
......
This diff is collapsed.
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.30 1999/07/25 23:07:24 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.31 1999/07/27 03:51:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -28,11 +28,13 @@
static void best_or_subclause_indices(Query *root, RelOptInfo *rel,
List *subclauses, List *indices,
List **indexquals,
List **indexids,
Cost *cost, Cost *selec);
static void best_or_subclause_index(Query *root, RelOptInfo *rel,
Expr *subclause, List *indices,
int *indexid, Cost *cost, Cost *selec);
List *indexqual, List *indices,
int *retIndexid,
Cost *retCost, Cost *retSelec);
/*
......@@ -84,8 +86,8 @@ create_or_index_paths(Query *root,
* best available index for each subclause.
*/
IndexPath *pathnode = makeNode(IndexPath);
List *indexquals;
List *indexids;
List *orclause;
Cost cost;
Cost selec;
......@@ -93,6 +95,7 @@ create_or_index_paths(Query *root,
rel,
clausenode->clause->args,
clausenode->indexids,
&indexquals,
&indexids,
&cost,
&selec);
......@@ -111,43 +114,11 @@ create_or_index_paths(Query *root,
pathnode->path.pathorder->ord.sortop = NULL;
pathnode->path.pathkeys = NIL;
/*
* Generate an indexqual list from the OR clause's args.
* We want two levels of sublist: the first is implicit OR
* and the second is implicit AND. (Currently, we will never
* see a sub-AND-clause because of cnfify(), but someday maybe
* the code below will do something useful...)
*/
pathnode->indexqual = NIL;
foreach(orclause, clausenode->clause->args)
{
Expr *subclause = (Expr *) lfirst(orclause);
List *sublist;
if (and_clause((Node *) subclause))
sublist = subclause->args;
else
sublist = lcons(subclause, NIL);
/* expansion call... */
pathnode->indexqual = lappend(pathnode->indexqual,
sublist);
}
pathnode->indexqual = indexquals;
pathnode->indexid = indexids;
pathnode->path.path_cost = cost;
clausenode->selectivity = (Cost) selec;
/*
* copy restrictinfo list into path for expensive function
* processing -- JMH, 7/7/92
*/
pathnode->path.loc_restrictinfo = set_difference(copyObject((Node *) rel->restrictinfo),
lcons(clausenode, NIL));
#ifdef NOT_USED /* fix xfunc */
/* add in cost for expensive functions! -- JMH, 7/7/92 */
if (XfuncMode != XFUNC_OFF)
((Path *) pathnode)->path_cost += xfunc_get_path_cost((Path) pathnode);
#endif
path_list = lappend(path_list, pathnode);
}
}
......@@ -163,11 +134,21 @@ create_or_index_paths(Query *root,
* indices. The cost is the sum of the individual index costs, since
* the executor will perform a scan for each subclause of the 'or'.
*
* This routine also creates the indexquals and indexids lists that will
* be needed by the executor. The indexquals list has one entry for each
* scan of the base rel, which is a sublist of indexqual conditions to
* apply in that scan. The implicit semantics are AND across each sublist
* of quals, and OR across the toplevel list (note that the executor
* takes care not to return any single tuple more than once). The indexids
* list gives the index to be used in each scan.
*
* 'rel' is the node of the relation on which the indexes are defined
* 'subclauses' are the subclauses of the 'or' clause
* 'indices' is a list of sublists of the index nodes that matched each
* subclause of the 'or' clause
* '*indexids' gets a list of the best index ID to use for each subclause
* '*indexquals' gets the constructed indexquals for the path (a list
* of sublists of clauses, one sublist per scan of the base rel)
* '*indexids' gets a list of the index IDs for each scan of the rel
* '*cost' gets the total cost of the path
* '*selec' gets the total selectivity of the path.
*/
......@@ -176,27 +157,41 @@ best_or_subclause_indices(Query *root,
RelOptInfo *rel,
List *subclauses,
List *indices,
List **indexquals, /* return value */
List **indexids, /* return value */
Cost *cost, /* return value */
Cost *selec) /* return value */
{
List *slist;
*indexquals = NIL;
*indexids = NIL;
*selec = (Cost) 0.0;
*cost = (Cost) 0.0;
*selec = (Cost) 0.0;
foreach(slist, subclauses)
{
Expr *subclause = lfirst(slist);
List *indexqual;
int best_indexid;
Cost best_cost;
Cost best_selec;
best_or_subclause_index(root, rel, lfirst(slist), lfirst(indices),
/* Convert this 'or' subclause to an indexqual list */
indexqual = make_ands_implicit(subclause);
/* expand special operators to indexquals the executor can handle */
indexqual = expand_indexqual_conditions(indexqual);
best_or_subclause_index(root, rel, indexqual, lfirst(indices),
&best_indexid, &best_cost, &best_selec);
*indexquals = lappend(*indexquals, indexqual);
*indexids = lappendi(*indexids, best_indexid);
*cost += best_cost;
/* We approximate the selectivity as the sum of the clause
* selectivities (but not more than 1).
* XXX This is too pessimistic, isn't it?
*/
*selec += best_selec;
if (*selec > (Cost) 1.0)
*selec = (Cost) 1.0;
......@@ -212,7 +207,7 @@ best_or_subclause_indices(Query *root,
* the least expensive.
*
* 'rel' is the node of the relation on which the index is defined
* 'subclause' is the subclause
* 'indexqual' is the indexqual list derived from the subclause
* 'indices' is a list of index nodes that match the subclause
* '*retIndexid' gets the ID of the best index
* '*retCost' gets the cost of a scan with that index
......@@ -221,14 +216,13 @@ best_or_subclause_indices(Query *root,
static void
best_or_subclause_index(Query *root,
RelOptInfo *rel,
Expr *subclause,
List *indexqual,
List *indices,
int *retIndexid, /* return value */
Cost *retCost, /* return value */
Cost *retSelec) /* return value */
{
bool first_run = true;
List *indexquals;
List *ilist;
/* if we don't match anything, return zeros */
......@@ -236,13 +230,6 @@ best_or_subclause_index(Query *root,
*retCost = (Cost) 0.0;
*retSelec = (Cost) 0.0;
/* convert 'or' subclause to an indexqual list */
if (and_clause((Node *) subclause))
indexquals = subclause->args;
else
indexquals = lcons(subclause, NIL);
/* expansion call... */
foreach(ilist, indices)
{
RelOptInfo *index = (RelOptInfo *) lfirst(ilist);
......@@ -254,7 +241,7 @@ best_or_subclause_index(Query *root,
index_selectivity(root,
lfirsti(rel->relids),
indexid,
indexquals,
indexqual,
&npages,
&selec);
......
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.63 1999/07/24 23:21:11 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.64 1999/07/27 03:51:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -151,15 +151,11 @@ create_scan_node(Path *best_path, List *tlist)
List *scan_clauses;
/*
* Extract the relevant clauses from the parent relation and replace
* the operator OIDs with the corresponding regproc ids.
*
* now that local predicate clauses are copied into paths in
* find_rel_paths() and then (possibly) pulled up in
* xfunc_trypullup(), we get the relevant clauses from the path
* itself, not its parent relation. --- JMH, 6/15/92
* Extract the relevant restriction clauses from the parent relation;
* the executor must apply all these restrictions during the scan.
* Fix regproc ids in the restriction clauses.
*/
scan_clauses = fix_opids(get_actual_clauses(best_path->loc_restrictinfo));
scan_clauses = fix_opids(get_actual_clauses(best_path->parent->restrictinfo));
switch (best_path->pathtype)
{
......@@ -245,8 +241,7 @@ create_join_node(JoinPath *best_path, List *tlist)
best_path->path.pathtype);
}
#if 0
#ifdef NOT_USED
/*
* * Expensive function pullups may have pulled local predicates *
* into this path node. Put them in the qpqual of the plan node. *
......@@ -282,11 +277,10 @@ create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses)
List *temp;
temp = best_path->parent->relids;
if (temp == NULL)
elog(ERROR, "scanrelid is empty");
else
scan_relid = (Index) lfirsti(temp); /* ??? who takes care of
* lnext? - ay */
/* there should be exactly one base rel involved... */
Assert(length(temp) == 1);
scan_relid = (Index) lfirsti(temp);
scan_node = make_seqscan(tlist,
scan_clauses,
scan_relid,
......@@ -319,6 +313,9 @@ create_indexscan_node(IndexPath *best_path,
IndexScan *scan_node;
bool lossy = false;
/* there should be exactly one base rel involved... */
Assert(length(best_path->path.parent->relids) == 1);
/* check and see if any indices are lossy */
foreach(ixid, best_path->indexid)
{
......@@ -345,9 +342,9 @@ create_indexscan_node(IndexPath *best_path,
* lossy indices the indxqual predicates need to be double-checked
* 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.
* Since the indexquals were generated from the restriction clauses
* given by scan_clauses, there will normally be some duplications
* between the lists. Get rid of the duplicates, then add back if lossy.
*/
if (length(indxqual) > 1)
{
......@@ -387,9 +384,8 @@ create_indexscan_node(IndexPath *best_path,
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...
* Fix opids in the completed indxqual.
* XXX this ought to only happen at final exit from the planner...
*/
indxqual = fix_opids(indxqual);
......
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.42 1999/07/25 23:07:25 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.43 1999/07/27 03:51:04 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
......@@ -293,8 +293,9 @@ make_andclause(List *andclauses)
/*
* 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.
* nodes with implicit AND semantics. These functions convert between an
* AND-semantics expression list and the ordinary representation of a
* boolean expression.
*/
Expr *
make_ands_explicit(List *andclauses)
......@@ -307,6 +308,17 @@ make_ands_explicit(List *andclauses)
return make_andclause(andclauses);
}
List *
make_ands_implicit(Expr *clause)
{
if (clause == NULL)
return NIL;
else if (and_clause((Node *) clause))
return clause->args;
else
return lcons(clause, NIL);
}
/*****************************************************************************
* CASE clause functions
*****************************************************************************/
......
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.48 1999/07/25 23:07:26 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.49 1999/07/27 03:51:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -21,6 +21,7 @@
#include "optimizer/keys.h"
#include "optimizer/ordering.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/plancat.h"
#include "optimizer/restrictinfo.h"
#include "parser/parsetree.h"
......@@ -288,22 +289,12 @@ create_seqscan_path(RelOptInfo *rel)
pathnode->pathorder->ord.sortop = NULL;
pathnode->pathkeys = NIL;
/*
* copy restrictinfo list into path for expensive function processing
* JMH, 7/7/92
*/
pathnode->loc_restrictinfo = (List *) copyObject((Node *) rel->restrictinfo);
if (rel->relids != NULL)
relid = lfirsti(rel->relids);
pathnode->path_cost = cost_seqscan(relid,
rel->pages, rel->tuples);
/* add in expensive functions cost! -- JMH, 7/7/92 */
#ifdef NOT_USED
if (XfuncMode != XFUNC_OFF)
pathnode->path_cost += xfunc_get_path_cost(pathnode);
#endif
return pathnode;
}
......@@ -344,13 +335,6 @@ create_index_path(Query *root,
pathnode->indexkeys = index->indexkeys;
pathnode->indexqual = NIL;
/*
* copy restrictinfo list into path for expensive function processing
* JMH, 7/7/92
*/
pathnode->path.loc_restrictinfo = set_difference((List *) copyObject((Node *) rel->restrictinfo),
restriction_clauses);
/*
* The index must have an ordering for the path to have (ordering)
* keys, and vice versa.
......@@ -403,6 +387,8 @@ create_index_path(Query *root,
Cost clausesel;
indexquals = get_actual_clauses(restriction_clauses);
/* expand special operators to indexquals the executor can handle */
indexquals = expand_indexqual_conditions(indexquals);
index_selectivity(root,
lfirsti(rel->relids),
......@@ -425,20 +411,18 @@ create_index_path(Query *root,
* Set selectivities of clauses used with index to the selectivity
* of this index, subdividing the selectivity equally over each of
* the clauses.
*
* XXX Can this divide the selectivities in a better way?
*
* XXX In fact, why the heck are we doing this at all? We already
* set the cost for the indexpath.
* set the cost for the indexpath, and it's far from obvious that
* the selectivity of the path should have any effect on estimates
* made for other contexts...
*/
clausesel = pow(selec, 1.0 / (double) length(restriction_clauses));
set_clause_selectivities(restriction_clauses, clausesel);
}
#ifdef NOT_USED
/* add in expensive functions cost! -- JMH, 7/7/92 */
if (XfuncMode != XFUNC_OFF)
pathnode->path_cost += xfunc_get_path_cost((Path *) pathnode);
#endif
return pathnode;
}
......@@ -473,7 +457,6 @@ create_nestloop_path(RelOptInfo *joinrel,
pathnode->path.pathkeys = pathkeys;
pathnode->path.joinid = NIL;
pathnode->path.outerjoincost = (Cost) 0.0;
pathnode->path.loc_restrictinfo = NIL;
pathnode->path.pathorder = makeNode(PathOrder);
if (pathkeys)
......@@ -497,11 +480,7 @@ create_nestloop_path(RelOptInfo *joinrel,
page_size(outer_rel->size,
outer_rel->width),
IsA(inner_path, IndexPath));
/* add in expensive function costs -- JMH 7/7/92 */
#ifdef NOT_USED
if (XfuncMode != XFUNC_OFF)
pathnode->path_cost += xfunc_get_path_cost((Path *) pathnode);
#endif
return pathnode;
}
......@@ -550,7 +529,6 @@ create_mergejoin_path(RelOptInfo *joinrel,
pathnode->jpath.path.pathorder->ordtype = MERGE_ORDER;
pathnode->jpath.path.pathorder->ord.merge = order;
pathnode->path_mergeclauses = mergeclauses;
pathnode->jpath.path.loc_restrictinfo = NIL;
pathnode->outersortkeys = outersortkeys;
pathnode->innersortkeys = innersortkeys;
pathnode->jpath.path.path_cost = cost_mergejoin(outer_path->path_cost,
......@@ -561,11 +539,7 @@ create_mergejoin_path(RelOptInfo *joinrel,
innersize,
outerwidth,
innerwidth);
/* add in expensive function costs -- JMH 7/7/92 */
#ifdef NOT_USED
if (XfuncMode != XFUNC_OFF)
pathnode->path_cost += xfunc_get_path_cost((Path *) pathnode);
#endif
return pathnode;
}
......@@ -608,7 +582,6 @@ create_hashjoin_path(RelOptInfo *joinrel,
pathnode->jpath.outerjoinpath = outer_path;
pathnode->jpath.innerjoinpath = inner_path;
pathnode->jpath.pathinfo = joinrel->restrictinfo;
pathnode->jpath.path.loc_restrictinfo = NIL;
pathnode->jpath.path.pathkeys = pathkeys;
pathnode->jpath.path.pathorder = makeNode(PathOrder);
pathnode->jpath.path.pathorder->ordtype = SORTOP_ORDER;
......@@ -625,10 +598,6 @@ create_hashjoin_path(RelOptInfo *joinrel,
innerkeys,
outersize, innersize,
outerwidth, innerwidth);
/* add in expensive function costs -- JMH 7/7/92 */
#ifdef NOT_USED
if (XfuncMode != XFUNC_OFF)
pathnode->path_cost += xfunc_get_path_cost((Path *) pathnode);
#endif
return pathnode;
}
......@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.94 1999/07/20 00:18:01 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.95 1999/07/27 03:51:06 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
......@@ -76,7 +76,6 @@ static void mapTargetColumns(List *source, List *target);
static List *makeConstantList( A_Const *node);
static char *FlattenStringList(List *list);
static char *fmtId(char *rawid);
static Node *makeIndexable(char *opname, Node *lexpr, Node *rexpr);
static void param_type_init(Oid *typev, int nargs);
static Node *doNegate(Node *n);
......@@ -3724,9 +3723,9 @@ a_expr: attr
| '(' a_expr_or_null ')'
{ $$ = $2; }
| a_expr Op a_expr
{ $$ = makeIndexable($2,$1,$3); }
{ $$ = makeA_Expr(OP, $2, $1, $3); }
| a_expr LIKE a_expr
{ $$ = makeIndexable("~~", $1, $3); }
{ $$ = makeA_Expr(OP, "~~", $1, $3); }
| a_expr NOT LIKE a_expr
{ $$ = makeA_Expr(OP, "!~~", $1, $4); }
| Op a_expr
......@@ -4376,7 +4375,7 @@ b_expr: attr
| '(' a_expr ')'
{ $$ = $2; }
| b_expr Op b_expr
{ $$ = makeIndexable($2,$1,$3); }
{ $$ = makeA_Expr(OP, $2,$1,$3); }
| Op b_expr
{ $$ = makeA_Expr(OP, $1, NULL, $2); }
| b_expr Op
......@@ -5197,157 +5196,6 @@ mapTargetColumns(List *src, List *dst)
return;
} /* mapTargetColumns() */
static Node *makeIndexable(char *opname, Node *lexpr, Node *rexpr)
{
Node *result = NULL;
/* we do this so indexes can be used */
if (strcmp(opname,"~") == 0 ||
strcmp(opname,"~*") == 0)
{
if (nodeTag(rexpr) == T_A_Const &&
((A_Const *)rexpr)->val.type == T_String &&
((A_Const *)rexpr)->val.val.str[0] == '^')
{
A_Const *n = (A_Const *)rexpr;
char *match_least = palloc(strlen(n->val.val.str)+2);
char *match_most = palloc(strlen(n->val.val.str)+2);
int pos, match_pos=0;
bool found_special = false;
/* Cannot optimize if unquoted | { } is present in pattern */
for (pos = 1; n->val.val.str[pos]; pos++)
{
if (n->val.val.str[pos] == '|' ||
n->val.val.str[pos] == '{' ||
n->val.val.str[pos] == '}')
{
found_special = true;
break;
}
if (n->val.val.str[pos] == '\\')
{
pos++;
if (n->val.val.str[pos] == '\0')
break;
}
}
if (!found_special)
{
/* note start at pos 1 to skip leading ^ */
for (pos = 1; n->val.val.str[pos]; pos++)
{
if (n->val.val.str[pos] == '.' ||
n->val.val.str[pos] == '?' ||
n->val.val.str[pos] == '*' ||
n->val.val.str[pos] == '[' ||
n->val.val.str[pos] == '$' ||
(strcmp(opname,"~*") == 0 && isalpha(n->val.val.str[pos])))
break;
if (n->val.val.str[pos] == '\\')
{
pos++;
if (n->val.val.str[pos] == '\0')
break;
}
match_least[match_pos] = n->val.val.str[pos];
match_most[match_pos++] = n->val.val.str[pos];
}
if (match_pos != 0)
{
A_Const *least = makeNode(A_Const);
A_Const *most = makeNode(A_Const);
/* make strings to be used in index use */
match_least[match_pos] = '\0';
match_most[match_pos] = '\377';
match_most[match_pos+1] = '\0';
least->val.type = T_String;
least->val.val.str = match_least;
most->val.type = T_String;
most->val.val.str = match_most;
#ifdef USE_LOCALE
result = makeA_Expr(AND, NULL,
makeA_Expr(OP, "~", lexpr, rexpr),
makeA_Expr(OP, ">=", lexpr, (Node *)least));
#else
result = makeA_Expr(AND, NULL,
makeA_Expr(OP, "~", lexpr, rexpr),
makeA_Expr(AND, NULL,
makeA_Expr(OP, ">=", lexpr, (Node *)least),
makeA_Expr(OP, "<=", lexpr, (Node *)most)));
#endif
}
}
}
}
else if (strcmp(opname,"~~") == 0)
{
if (nodeTag(rexpr) == T_A_Const &&
((A_Const *)rexpr)->val.type == T_String)
{
A_Const *n = (A_Const *)rexpr;
char *match_least = palloc(strlen(n->val.val.str)+2);
char *match_most = palloc(strlen(n->val.val.str)+2);
int pos, match_pos=0;
for (pos = 0; n->val.val.str[pos]; pos++)
{
/* % and _ are wildcard characters in LIKE */
if (n->val.val.str[pos] == '%' ||
n->val.val.str[pos] == '_')
break;
/* Backslash quotes the next character */
if (n->val.val.str[pos] == '\\')
{
pos++;
if (n->val.val.str[pos] == '\0')
break;
}
/*
* NOTE: this code used to think that %% meant a literal %,
* but textlike() itself does not think that, and the SQL92
* spec doesn't say any such thing either.
*/
match_least[match_pos] = n->val.val.str[pos];
match_most[match_pos++] = n->val.val.str[pos];
}
if (match_pos != 0)
{
A_Const *least = makeNode(A_Const);
A_Const *most = makeNode(A_Const);
/* make strings to be used in index use */
match_least[match_pos] = '\0';
match_most[match_pos] = '\377';
match_most[match_pos+1] = '\0';
least->val.type = T_String;
least->val.val.str = match_least;
most->val.type = T_String;
most->val.val.str = match_most;
#ifdef USE_LOCALE
result = makeA_Expr(AND, NULL,
makeA_Expr(OP, "~~", lexpr, rexpr),
makeA_Expr(OP, ">=", lexpr, (Node *)least));
#else
result = makeA_Expr(AND, NULL,
makeA_Expr(OP, "~~", lexpr, rexpr),
makeA_Expr(AND, NULL,
makeA_Expr(OP, ">=", lexpr, (Node *)least),
makeA_Expr(OP, "<=", lexpr, (Node *)most)));
#endif
}
}
}
if (result == NULL)
result = makeA_Expr(OP, opname, lexpr, rexpr);
return result;
} /* makeIndexable() */
/* xlateSqlFunc()
* Convert alternate type names to internal Postgres types.
......
......@@ -7,7 +7,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: pg_operator.h,v 1.57 1999/05/25 16:13:46 momjian Exp $
* $Id: pg_operator.h,v 1.58 1999/07/27 03:51:11 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
......@@ -271,7 +271,6 @@ DATA(insert OID = 1283 ( ";" PGUID 0 l t f 0 701 701 0 0 0 0 dlog1 - - ));
DATA(insert OID = 1284 ( "|" PGUID 0 l t f 0 704 702 0 0 0 0 intervalstart - - ));
DATA(insert OID = 606 ( "<#>" PGUID 0 b t f 702 702 704 0 0 0 0 mktinterval - - ));
DATA(insert OID = 607 ( "=" PGUID 0 b t t 26 26 16 607 608 609 609 oideq eqsel eqjoinsel ));
#define OIDEqualOperator 607 /* XXX planner/prep/semanopt.c crock */
DATA(insert OID = 608 ( "<>" PGUID 0 b t f 26 26 16 608 607 0 0 oidne neqsel neqjoinsel ));
DATA(insert OID = 644 ( "<>" PGUID 0 b t f 30 30 16 644 649 0 0 oid8ne neqsel neqjoinsel ));
......@@ -301,7 +300,6 @@ DATA(insert OID = 624 ( "<=" PGUID 0 b t f 700 700 16 625 623 0 0 float4le
DATA(insert OID = 625 ( ">=" PGUID 0 b t f 700 700 16 624 622 0 0 float4ge intgtsel intgtjoinsel ));
DATA(insert OID = 626 ( "!!=" PGUID 0 b t f 23 19 16 0 0 0 0 int4notin - - ));
DATA(insert OID = 627 ( "!!=" PGUID 0 b t f 26 19 16 0 0 0 0 oidnotin - - ));
#define OIDNotInOperator 627 /* XXX planner/prep/semanopt.c crock */
DATA(insert OID = 630 ( "<>" PGUID 0 b t f 18 18 16 630 92 0 0 charne neqsel neqjoinsel ));
DATA(insert OID = 631 ( "<" PGUID 0 b t f 18 18 16 633 634 0 0 charlt intltsel intltjoinsel ));
......@@ -315,8 +313,10 @@ DATA(insert OID = 637 ( "*" PGUID 0 b t f 18 18 18 0 0 0 0 charmul - - )
DATA(insert OID = 638 ( "/" PGUID 0 b t f 18 18 18 0 0 0 0 chardiv - - ));
DATA(insert OID = 639 ( "~" PGUID 0 b t f 19 25 16 0 640 0 0 nameregexeq eqsel eqjoinsel ));
#define OID_NAME_REGEXEQ_OP 639
DATA(insert OID = 640 ( "!~" PGUID 0 b t f 19 25 16 0 639 0 0 nameregexne neqsel neqjoinsel ));
DATA(insert OID = 641 ( "~" PGUID 0 b t f 25 25 16 0 642 0 0 textregexeq eqsel eqjoinsel ));
#define OID_TEXT_REGEXEQ_OP 641
DATA(insert OID = 642 ( "!~" PGUID 0 b t f 25 25 16 0 641 0 0 textregexne neqsel neqjoinsel ));
DATA(insert OID = 643 ( "<>" PGUID 0 b t f 19 19 16 643 93 0 0 namene neqsel neqjoinsel ));
DATA(insert OID = 654 ( "||" PGUID 0 b t f 25 25 25 0 0 0 0 textcat - - ));
......@@ -436,6 +436,7 @@ DATA(insert OID = 979 ( "||" PGUID 0 b t f 1043 1043 1043 0 0 0 0 textc
DATA(insert OID = 1054 ( "=" PGUID 0 b t t 1042 1042 16 1054 1057 1058 1058 bpchareq eqsel eqjoinsel ));
DATA(insert OID = 1055 ( "~" PGUID 0 b t f 1042 25 16 0 1056 0 0 textregexeq eqsel eqjoinsel ));
#define OID_BPCHAR_REGEXEQ_OP 1055
DATA(insert OID = 1056 ( "!~" PGUID 0 b t f 1042 25 16 0 1055 0 0 textregexne neqsel neqjoinsel ));
DATA(insert OID = 1057 ( "<>" PGUID 0 b t f 1042 1042 16 1057 1054 0 0 bpcharne neqsel neqjoinsel ));
DATA(insert OID = 1058 ( "<" PGUID 0 b t f 1042 1042 16 1060 1061 0 0 bpcharlt intltsel intltjoinsel ));
......@@ -445,6 +446,7 @@ DATA(insert OID = 1061 ( ">=" PGUID 0 b t f 1042 1042 16 1059 1058 0 0 bpch
DATA(insert OID = 1062 ( "=" PGUID 0 b t t 1043 1043 16 1062 1065 1066 1066 varchareq eqsel eqjoinsel ));
DATA(insert OID = 1063 ( "~" PGUID 0 b t f 1043 25 16 0 1064 0 0 textregexeq eqsel eqjoinsel ));
#define OID_VARCHAR_REGEXEQ_OP 1063
DATA(insert OID = 1064 ( "!~" PGUID 0 b t f 1043 25 16 0 1063 0 0 textregexne neqsel neqjoinsel ));
DATA(insert OID = 1065 ( "<>" PGUID 0 b t f 1043 1043 16 1065 1062 0 0 varcharne neqsel neqjoinsel ));
DATA(insert OID = 1066 ( "<" PGUID 0 b t f 1043 1043 16 1068 1069 0 0 varcharlt intltsel intltjoinsel ));
......@@ -501,22 +503,30 @@ DATA(insert OID = 1137 ( "=" PGUID 0 b t t 26 23 16 1136 0 0 0 oideqint4 eqsel
/* LIKE hacks by Keith Parks. */
DATA(insert OID = 1207 ( "~~" PGUID 0 b t f 19 25 16 0 1208 0 0 namelike eqsel eqjoinsel ));
#define OID_NAME_LIKE_OP 1207
DATA(insert OID = 1208 ( "!~~" PGUID 0 b t f 19 25 16 0 1207 0 0 namenlike neqsel neqjoinsel ));
DATA(insert OID = 1209 ( "~~" PGUID 0 b t f 25 25 16 0 1210 0 0 textlike eqsel eqjoinsel ));
#define OID_TEXT_LIKE_OP 1209
DATA(insert OID = 1210 ( "!~~" PGUID 0 b t f 25 25 16 0 1209 0 0 textnlike neqsel neqjoinsel ));
DATA(insert OID = 1211 ( "~~" PGUID 0 b t f 1042 25 16 0 1212 0 0 textlike eqsel eqjoinsel ));
#define OID_BPCHAR_LIKE_OP 1211
DATA(insert OID = 1212 ( "!~~" PGUID 0 b t f 1042 25 16 0 1211 0 0 textnlike neqsel neqjoinsel ));
DATA(insert OID = 1213 ( "~~" PGUID 0 b t f 1043 25 16 0 1214 0 0 textlike eqsel eqjoinsel ));
#define OID_VARCHAR_LIKE_OP 1213
DATA(insert OID = 1214 ( "!~~" PGUID 0 b t f 1043 25 16 0 1213 0 0 textnlike neqsel neqjoinsel ));
/* case-insensitive LIKE hacks */
DATA(insert OID = 1226 ( "~*" PGUID 0 b t f 19 25 16 0 1227 0 0 nameicregexeq eqsel eqjoinsel ));
#define OID_NAME_ICREGEXEQ_OP 1226
DATA(insert OID = 1227 ( "!~*" PGUID 0 b t f 19 25 16 0 1226 0 0 nameicregexne neqsel neqjoinsel ));
DATA(insert OID = 1228 ( "~*" PGUID 0 b t f 25 25 16 0 1229 0 0 texticregexeq eqsel eqjoinsel ));
#define OID_TEXT_ICREGEXEQ_OP 1228
DATA(insert OID = 1229 ( "!~*" PGUID 0 b t f 25 25 16 0 1228 0 0 texticregexne neqsel neqjoinsel ));
DATA(insert OID = 1232 ( "~*" PGUID 0 b t f 1043 25 16 0 1233 0 0 texticregexeq eqsel eqjoinsel ));
#define OID_VARCHAR_ICREGEXEQ_OP 1232
DATA(insert OID = 1233 ( "!~*" PGUID 0 b t f 1043 25 16 0 1232 0 0 texticregexne neqsel neqjoinsel ));
DATA(insert OID = 1234 ( "~*" PGUID 0 b t f 1042 25 16 0 1235 0 0 texticregexeq eqsel eqjoinsel ));
#define OID_BPCHAR_ICREGEXEQ_OP 1234
DATA(insert OID = 1235 ( "!~*" PGUID 0 b t f 1042 25 16 0 1234 0 0 texticregexne neqsel neqjoinsel ));
DATA(insert OID = 1300 ( "=" PGUID 0 b t t 1296 1296 16 1300 1301 1302 1302 timestampeq eqsel eqjoinsel ));
......
......@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: relation.h,v 1.36 1999/07/25 17:53:26 tgl Exp $
* $Id: relation.h,v 1.37 1999/07/27 03:51:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -148,7 +148,6 @@ typedef struct Path
* information. */
Cost outerjoincost;
Relids joinid;
List *loc_restrictinfo;
} Path;
/*----------
......@@ -292,7 +291,7 @@ typedef struct Iter
* cinfo -- if NULL, this stream node referes to the path node.
* Otherwise this is a pointer to the current clause.
* clausetype -- whether cinfo is in loc_restrictinfo or pathinfo in the
* path node
* path node (XXX this is now used only by dead code...)
* upstream -- linked list pointer upwards
* downstream -- ditto, downwards
* groupup -- whether or not this node is in a group with the node upstream
......
......@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: clauses.h,v 1.23 1999/07/25 23:07:23 tgl Exp $
* $Id: clauses.h,v 1.24 1999/07/27 03:51:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -35,6 +35,7 @@ extern Expr *get_notclausearg(Expr *notclause);
extern bool and_clause(Node *clause);
extern Expr *make_andclause(List *andclauses);
extern Expr *make_ands_explicit(List *andclauses);
extern List *make_ands_implicit(Expr *clause);
extern bool case_clause(Node *clause);
......
......@@ -7,7 +7,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: paths.h,v 1.31 1999/07/15 15:21:22 momjian Exp $
* $Id: paths.h,v 1.32 1999/07/27 03:51:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -28,6 +28,7 @@ extern RelOptInfo *make_one_rel(Query *root, List *rels);
extern List *create_index_paths(Query *root, RelOptInfo *rel, List *indices,
List *restrictinfo_list,
List *joininfo_list);
extern List *expand_indexqual_conditions(List *indexquals);
/*
* joinpath.h
......
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