*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
newnode->outerjoincost = from->outerjoincost;
newnode->joinid = listCopy(from->joinid);
- Node_Copy(from, newnode, loc_restrictinfo);
}
/* ----------------
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
freeObject(node->pathkeys);
freeList(node->joinid);
- freeObject(node->loc_restrictinfo);
}
/* ----------------
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.65 1999/07/25 23:07:24 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.66 1999/07/27 03:51:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
+#include <ctype.h>
#include <math.h>
#include "postgres.h"
#include "access/nbtree.h"
#include "catalog/catname.h"
#include "catalog/pg_amop.h"
+#include "catalog/pg_operator.h"
#include "executor/executor.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_expr.h"
#include "parser/parse_oper.h"
#include "parser/parsetree.h"
+#include "utils/builtins.h"
#include "utils/lsyscache.h"
+#include "utils/syscache.h"
+typedef enum {
+ Prefix_None, Prefix_Partial, Prefix_Exact
+} Prefix_Status;
static void match_index_orclauses(RelOptInfo *rel, RelOptInfo *index, int indexkey,
int xclass, List *restrictinfo_list);
static bool match_index_to_operand(int indexkey, Expr *operand,
RelOptInfo *rel, RelOptInfo *index);
static bool function_index_operand(Expr *funcOpnd, RelOptInfo *rel, RelOptInfo *index);
+static bool match_special_index_operator(Expr *clause, bool indexkey_on_left);
+static Prefix_Status like_fixed_prefix(char *patt, char **prefix);
+static Prefix_Status regex_fixed_prefix(char *patt, bool case_insensitive,
+ char **prefix);
+static List *prefix_quals(Var *leftop, Oid expr_op,
+ char *prefix, Prefix_Status pstatus);
/*
* or (var op var) for a join clause, where the var or one
* of the vars matches the index key; and
* (2) contain an operator which is in the same class as the index
- * operator for this key.
+ * operator for this key, or is a "special" operator as recognized
+ * by match_special_index_operator().
*
* In the restriction case, we can cope with (const op var) by commuting
* the clause to (var op const), if there is a commutator operator.
bool isIndexable = false;
Var *leftop,
*rightop;
+ Oid expr_op;
if (! is_opclause((Node *) clause))
return false;
rightop = get_rightop(clause);
if (! leftop || ! rightop)
return false;
+ expr_op = ((Oper *) clause->oper)->opno;
if (!join)
{
* Not considering joins, so check for clauses of the form:
* (var/func operator constant) and (constant operator var/func)
*/
- Oid restrict_op = InvalidOid;
/*
* Check for standard s-argable clause
*/
- if (IsA(rightop, Const) || IsA(rightop, Param))
+ if ((IsA(rightop, Const) || IsA(rightop, Param)) &&
+ match_index_to_operand(indexkey, (Expr *) leftop,
+ rel, index))
{
- restrict_op = ((Oper *) ((Expr *) clause)->oper)->opno;
-
- isIndexable = (op_class(restrict_op, xclass, index->relam) &&
- match_index_to_operand(indexkey,
- (Expr *) leftop,
- rel,
- index));
+ isIndexable = op_class(expr_op, xclass, index->relam);
#ifndef IGNORE_BINARY_COMPATIBLE_INDICES
-
/*
* Didn't find an index? Then maybe we can find another
* binary-compatible index instead... thomas 1998-08-14
* make sure we have two different binary-compatible
* types...
*/
- if ((ltype != rtype)
- && IS_BINARY_COMPATIBLE(ltype, rtype))
+ if (ltype != rtype && IS_BINARY_COMPATIBLE(ltype, rtype))
{
- char *opname;
- Operator newop;
+ char *opname = get_opname(expr_op);
+ Operator newop = NULL;
- opname = get_opname(restrict_op);
if (opname != NULL)
newop = oper(opname, ltype, ltype, TRUE);
- else
- newop = NULL;
/* actually have a different operator to try? */
- if (HeapTupleIsValid(newop) &&
- (oprid(newop) != restrict_op))
+ if (HeapTupleIsValid(newop) && oprid(newop) != expr_op)
{
- restrict_op = oprid(newop);
-
- isIndexable = (op_class(restrict_op, xclass, index->relam) &&
- match_index_to_operand(indexkey,
- (Expr *) leftop,
- rel,
- index));
-
+ expr_op = oprid(newop);
+ isIndexable = op_class(expr_op, xclass, index->relam);
if (isIndexable)
- ((Oper *) ((Expr *) clause)->oper)->opno = restrict_op;
+ ((Oper *) clause->oper)->opno = expr_op;
}
}
}
#endif
+
+ /*
+ * If we didn't find a member of the index's opclass,
+ * see whether it is a "special" indexable operator.
+ */
+ if (!isIndexable)
+ isIndexable = match_special_index_operator(clause, true);
+
}
/*
* Must try to commute the clause to standard s-arg format.
+ * XXX do we really have to commute it? The executor doesn't care!
*/
- else if (IsA(leftop, Const) || IsA(leftop, Param))
+ else if ((IsA(leftop, Const) || IsA(leftop, Param)) &&
+ match_index_to_operand(indexkey, (Expr *) rightop,
+ rel, index))
{
- restrict_op = get_commutator(((Oper *) ((Expr *) clause)->oper)->opno);
+ Oid commuted_op = get_commutator(expr_op);
- isIndexable = ((restrict_op != InvalidOid) &&
- op_class(restrict_op, xclass, index->relam) &&
- match_index_to_operand(indexkey,
- (Expr *) rightop,
- rel,
- index));
+ isIndexable = ((commuted_op != InvalidOid) &&
+ op_class(commuted_op, xclass, index->relam));
#ifndef IGNORE_BINARY_COMPATIBLE_INDICES
if (!isIndexable)
{
- Oid ltype;
- Oid rtype;
-
- ltype = exprType((Node *) leftop);
- rtype = exprType((Node *) rightop);
+ Oid ltype = exprType((Node *) leftop);
+ Oid rtype = exprType((Node *) rightop);
- if ((ltype != rtype)
- && IS_BINARY_COMPATIBLE(ltype, rtype))
+ if (ltype != rtype && IS_BINARY_COMPATIBLE(ltype, rtype))
{
- char *opname;
- Operator newop;
+ char *opname = get_opname(expr_op);
+ Operator newop = NULL;
- restrict_op = ((Oper *) ((Expr *) clause)->oper)->opno;
-
- opname = get_opname(restrict_op);
+ /* note we use rtype, ie, the indexkey's type */
if (opname != NULL)
newop = oper(opname, rtype, rtype, TRUE);
- else
- newop = NULL;
- if (HeapTupleIsValid(newop) && (oprid(newop) != restrict_op))
+ if (HeapTupleIsValid(newop) && oprid(newop) != expr_op)
{
- restrict_op = get_commutator(oprid(newop));
-
- isIndexable = ((restrict_op != InvalidOid) &&
- op_class(restrict_op, xclass, index->relam) &&
- match_index_to_operand(indexkey,
- (Expr *) rightop,
- rel,
- index));
-
+ expr_op = get_commutator(oprid(newop));
+ isIndexable = (expr_op != InvalidOid) &&
+ op_class(expr_op, xclass, index->relam);
if (isIndexable)
- ((Oper *) ((Expr *) clause)->oper)->opno = oprid(newop);
+ ((Oper *) clause->oper)->opno = oprid(newop);
}
}
}
if (isIndexable)
{
-
/*
* In place list modification. (op const var/func) -> (op
* var/func const)
*/
CommuteClause((Node *) clause);
}
+ else
+ {
+ /*
+ * If we didn't find a member of the index's opclass,
+ * see whether it is a "special" indexable operator.
+ * (match_special_index_operator must commute the
+ * clause itself, if it wants to.)
+ */
+ isIndexable = match_special_index_operator(clause, false);
+ }
}
}
else
if (match_index_to_operand(indexkey, (Expr *) leftop,
rel, index))
- join_op = ((Oper *) ((Expr *) clause)->oper)->opno;
+ join_op = expr_op;
else if (match_index_to_operand(indexkey, (Expr *) rightop,
rel, index))
- join_op = get_commutator(((Oper *) ((Expr *) clause)->oper)->opno);
+ join_op = get_commutator(expr_op);
if (join_op && op_class(join_op, xclass, index->relam) &&
is_joinable((Node *) clause))
float selec;
indexquals = get_actual_clauses(clausegroup);
+ /* expand special operators to indexquals the executor can handle */
+ indexquals = expand_indexqual_conditions(indexquals);
index_selectivity(root,
lfirsti(rel->relids),
index->tuples,
true);
- /*
- * copy restrictinfo list into path for expensive function
- * processing -- JMH, 7/7/92
- */
- pathnode->path.loc_restrictinfo = set_difference(copyObject((Node *) rel->restrictinfo),
- clausegroup);
-
-#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);
outerrelids_list = lnext(outerrelids_list);
}
return true;
}
+
+/****************************************************************************
+ * ---- 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
+ * indexscan condition(s). Then we can use the indexscan machinery
+ * 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
+ * we deliver only the tuples we want. (In essence, we're using a regular
+ * 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
+ * just an auxiliary function for match_clause_to_indexkey(); after
+ * the latter fails to recognize a restriction opclause's operator
+ * as a member of an index's opclass, it asks match_special_index_operator()
+ * whether the clause should be considered an indexqual anyway.
+ * expand_indexqual_conditions() converts a list of "raw" indexqual
+ * conditions (with implicit AND semantics across list elements) into
+ * a list that the executor can actually handle. For operators that
+ * are members of the index's opclass this transformation is a no-op,
+ * 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
+ * the form (indexkey OP const/param) or (const/param OP indexkey),
+ * 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
+match_special_index_operator(Expr *clause, bool indexkey_on_left)
+{
+ bool isIndexable = false;
+ Var *leftop,
+ *rightop;
+ Oid expr_op;
+ Datum constvalue;
+ char *patt;
+ char *prefix;
+
+ /* 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...
+ */
+ if (! indexkey_on_left)
+ return false;
+
+ /* we know these will succeed */
+ leftop = get_leftop(clause);
+ rightop = get_rightop(clause);
+ expr_op = ((Oper *) clause->oper)->opno;
+
+ /* again, required for all current special ops: */
+ if (! IsA(rightop, Const) ||
+ ((Const *) rightop)->constisnull)
+ return false;
+ constvalue = ((Const *) rightop)->constvalue;
+
+ switch (expr_op)
+ {
+ case OID_TEXT_LIKE_OP:
+ case OID_BPCHAR_LIKE_OP:
+ case OID_VARCHAR_LIKE_OP:
+ case OID_NAME_LIKE_OP:
+ /* the right-hand const is type text for all of these */
+ patt = textout((text *) DatumGetPointer(constvalue));
+ isIndexable = like_fixed_prefix(patt, &prefix) != Prefix_None;
+ if (prefix) pfree(prefix);
+ pfree(patt);
+ break;
+
+ case OID_TEXT_REGEXEQ_OP:
+ case OID_BPCHAR_REGEXEQ_OP:
+ case OID_VARCHAR_REGEXEQ_OP:
+ case OID_NAME_REGEXEQ_OP:
+ /* the right-hand const is type text for all of these */
+ patt = textout((text *) DatumGetPointer(constvalue));
+ isIndexable = regex_fixed_prefix(patt, false, &prefix) != Prefix_None;
+ if (prefix) pfree(prefix);
+ pfree(patt);
+ break;
+
+ case OID_TEXT_ICREGEXEQ_OP:
+ case OID_BPCHAR_ICREGEXEQ_OP:
+ case OID_VARCHAR_ICREGEXEQ_OP:
+ case OID_NAME_ICREGEXEQ_OP:
+ /* the right-hand const is type text for all of these */
+ patt = textout((text *) DatumGetPointer(constvalue));
+ isIndexable = regex_fixed_prefix(patt, true, &prefix) != Prefix_None;
+ if (prefix) pfree(prefix);
+ pfree(patt);
+ break;
+ }
+
+ return isIndexable;
+}
+
+/*
+ * expand_indexqual_conditions
+ * Given a list of (implicitly ANDed) indexqual clauses,
+ * expand any "special" index operators into clauses that the indexscan
+ * machinery will know what to do with. Clauses that were not
+ * recognized by match_special_index_operator() must be passed through
+ * unchanged.
+ */
+List *
+expand_indexqual_conditions(List *indexquals)
+{
+ List *resultquals = NIL;
+ List *q;
+
+ foreach(q, indexquals)
+ {
+ Expr *clause = (Expr *) lfirst(q);
+ /* we know these will succeed */
+ Var *leftop = get_leftop(clause);
+ Var *rightop = get_rightop(clause);
+ Oid expr_op = ((Oper *) clause->oper)->opno;
+ Datum constvalue;
+ char *patt;
+ char *prefix;
+ Prefix_Status pstatus;
+
+ switch (expr_op)
+ {
+ /*
+ * 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().
+ */
+ case OID_TEXT_LIKE_OP:
+ case OID_BPCHAR_LIKE_OP:
+ case OID_VARCHAR_LIKE_OP:
+ case OID_NAME_LIKE_OP:
+ /* the right-hand const is type text for all of these */
+ constvalue = ((Const *) rightop)->constvalue;
+ patt = textout((text *) DatumGetPointer(constvalue));
+ pstatus = like_fixed_prefix(patt, &prefix);
+ resultquals = nconc(resultquals,
+ prefix_quals(leftop, expr_op,
+ prefix, pstatus));
+ if (prefix) pfree(prefix);
+ pfree(patt);
+ break;
+
+ case OID_TEXT_REGEXEQ_OP:
+ case OID_BPCHAR_REGEXEQ_OP:
+ case OID_VARCHAR_REGEXEQ_OP:
+ case OID_NAME_REGEXEQ_OP:
+ /* the right-hand const is type text for all of these */
+ constvalue = ((Const *) rightop)->constvalue;
+ patt = textout((text *) DatumGetPointer(constvalue));
+ pstatus = regex_fixed_prefix(patt, false, &prefix);
+ resultquals = nconc(resultquals,
+ prefix_quals(leftop, expr_op,
+ prefix, pstatus));
+ if (prefix) pfree(prefix);
+ pfree(patt);
+ break;
+
+ case OID_TEXT_ICREGEXEQ_OP:
+ case OID_BPCHAR_ICREGEXEQ_OP:
+ case OID_VARCHAR_ICREGEXEQ_OP:
+ case OID_NAME_ICREGEXEQ_OP:
+ /* the right-hand const is type text for all of these */
+ constvalue = ((Const *) rightop)->constvalue;
+ patt = textout((text *) DatumGetPointer(constvalue));
+ pstatus = regex_fixed_prefix(patt, true, &prefix);
+ resultquals = nconc(resultquals,
+ prefix_quals(leftop, expr_op,
+ prefix, pstatus));
+ if (prefix) pfree(prefix);
+ pfree(patt);
+ break;
+
+ default:
+ resultquals = lappend(resultquals, clause);
+ break;
+ }
+ }
+
+ return resultquals;
+}
+
+/*
+ * Extract the fixed prefix, if any, for a LIKE pattern.
+ * *prefix is set to a palloc'd prefix string with 1 spare byte,
+ * or to NULL if no fixed prefix exists for the pattern.
+ * The return value distinguishes no fixed prefix, a partial prefix,
+ * or an exact-match-only pattern.
+ */
+static Prefix_Status
+like_fixed_prefix(char *patt, char **prefix)
+{
+ char *match;
+ int pos,
+ match_pos;
+
+ *prefix = match = palloc(strlen(patt)+2);
+ match_pos = 0;
+
+ for (pos = 0; patt[pos]; pos++)
+ {
+ /* % and _ are wildcard characters in LIKE */
+ if (patt[pos] == '%' ||
+ patt[pos] == '_')
+ break;
+ /* Backslash quotes the next character */
+ if (patt[pos] == '\\')
+ {
+ pos++;
+ if (patt[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[match_pos++] = patt[pos];
+ }
+
+ match[match_pos] = '\0';
+
+ /* in LIKE, an empty pattern is an exact match! */
+ if (patt[pos] == '\0')
+ return Prefix_Exact; /* reached end of pattern, so exact */
+
+ if (match_pos > 0)
+ return Prefix_Partial;
+ return Prefix_None;
+}
+
+/*
+ * Extract the fixed prefix, if any, for a regex pattern.
+ * *prefix is set to a palloc'd prefix string with 1 spare byte,
+ * or to NULL if no fixed prefix exists for the pattern.
+ * The return value distinguishes no fixed prefix, a partial prefix,
+ * or an exact-match-only pattern.
+ */
+static Prefix_Status
+regex_fixed_prefix(char *patt, bool case_insensitive,
+ char **prefix)
+{
+ char *match;
+ int pos,
+ match_pos;
+
+ *prefix = NULL;
+
+ /* Pattern must be anchored left */
+ if (patt[0] != '^')
+ return Prefix_None;
+
+ /* Cannot optimize if unquoted | { } is present in pattern */
+ for (pos = 1; patt[pos]; pos++)
+ {
+ if (patt[pos] == '|' ||
+ patt[pos] == '{' ||
+ patt[pos] == '}')
+ return Prefix_None;
+ if (patt[pos] == '\\')
+ {
+ pos++;
+ if (patt[pos] == '\0')
+ break;
+ }
+ }
+
+ /* OK, allocate space for pattern */
+ *prefix = match = palloc(strlen(patt)+2);
+ match_pos = 0;
+
+ /* note start at pos 1 to skip leading ^ */
+ for (pos = 1; patt[pos]; pos++)
+ {
+ if (patt[pos] == '.' ||
+ patt[pos] == '?' ||
+ patt[pos] == '*' ||
+ patt[pos] == '[' ||
+ patt[pos] == '$' ||
+ /* XXX I suspect isalpha() is not an adequately locale-sensitive
+ * test for characters that can vary under case folding?
+ */
+ (case_insensitive && isalpha(patt[pos])))
+ break;
+ if (patt[pos] == '\\')
+ {
+ pos++;
+ if (patt[pos] == '\0')
+ break;
+ }
+ match[match_pos++] = patt[pos];
+ }
+
+ match[match_pos] = '\0';
+
+ if (patt[pos] == '$' && patt[pos+1] == '\0')
+ return Prefix_Exact; /* pattern specifies exact match */
+
+ if (match_pos > 0)
+ return Prefix_Partial;
+ return Prefix_None;
+}
+
+/*
+ * Given a fixed prefix that all the "leftop" values must have,
+ * generate suitable indexqual condition(s). expr_op is the original
+ * LIKE or regex operator; we use it to deduce the appropriate comparison
+ * operators.
+ */
+static List *
+prefix_quals(Var *leftop, Oid expr_op,
+ char *prefix, Prefix_Status pstatus)
+{
+ List *result;
+ Oid datatype;
+ HeapTuple optup;
+ void *conval;
+ Const *con;
+ Oper *op;
+ Expr *expr;
+ int prefixlen;
+
+ Assert(pstatus != Prefix_None);
+
+ switch (expr_op)
+ {
+ case OID_TEXT_LIKE_OP:
+ case OID_TEXT_REGEXEQ_OP:
+ case OID_TEXT_ICREGEXEQ_OP:
+ datatype = TEXTOID;
+ break;
+
+ case OID_BPCHAR_LIKE_OP:
+ case OID_BPCHAR_REGEXEQ_OP:
+ case OID_BPCHAR_ICREGEXEQ_OP:
+ datatype = BPCHAROID;
+ break;
+
+ case OID_VARCHAR_LIKE_OP:
+ case OID_VARCHAR_REGEXEQ_OP:
+ case OID_VARCHAR_ICREGEXEQ_OP:
+ datatype = VARCHAROID;
+ break;
+
+ case OID_NAME_LIKE_OP:
+ case OID_NAME_REGEXEQ_OP:
+ case OID_NAME_ICREGEXEQ_OP:
+ datatype = NAMEOID;
+ break;
+
+ default:
+ elog(ERROR, "prefix_quals: unexpected operator %u", expr_op);
+ return NIL;
+ }
+
+ /*
+ * If we found an exact-match pattern, generate an "=" indexqual.
+ */
+ if (pstatus == Prefix_Exact)
+ {
+ optup = SearchSysCacheTuple(OPRNAME,
+ PointerGetDatum("="),
+ ObjectIdGetDatum(datatype),
+ ObjectIdGetDatum(datatype),
+ CharGetDatum('b'));
+ if (!HeapTupleIsValid(optup))
+ elog(ERROR, "prefix_quals: no = operator for type %u", datatype);
+ /* Note: we cheat a little by assuming that textin() will do for
+ * bpchar and varchar constants too...
+ */
+ conval = (datatype == NAMEOID) ?
+ (void*) namein(prefix) : (void*) textin(prefix);
+ con = makeConst(datatype, ((datatype == NAMEOID) ? NAMEDATALEN : -1),
+ PointerGetDatum(conval),
+ false, false, false, false);
+ op = makeOper(optup->t_data->t_oid, InvalidOid, BOOLOID, 0, NULL);
+ expr = make_opclause(op, leftop, (Var *) con);
+ result = lcons(expr, NIL);
+ return result;
+ }
+
+ /*
+ * Otherwise, we have a nonempty required prefix of the values.
+ *
+ * We can always say "x >= prefix".
+ */
+ optup = SearchSysCacheTuple(OPRNAME,
+ PointerGetDatum(">="),
+ ObjectIdGetDatum(datatype),
+ ObjectIdGetDatum(datatype),
+ CharGetDatum('b'));
+ if (!HeapTupleIsValid(optup))
+ elog(ERROR, "prefix_quals: no >= operator for type %u", datatype);
+ conval = (datatype == NAMEOID) ?
+ (void*) namein(prefix) : (void*) textin(prefix);
+ con = makeConst(datatype, ((datatype == NAMEOID) ? NAMEDATALEN : -1),
+ PointerGetDatum(conval),
+ false, false, false, false);
+ op = makeOper(optup->t_data->t_oid, InvalidOid, BOOLOID, 0, NULL);
+ expr = make_opclause(op, leftop, (Var *) con);
+ result = lcons(expr, NIL);
+
+ /*
+ * In ASCII locale we say "x <= prefix\377". This does not
+ * work for non-ASCII collation orders, and it's not really
+ * right even for ASCII. FIX ME!
+ * Note we assume the passed prefix string is workspace with
+ * an extra byte, as created by the xxx_fixed_prefix routines above.
+ */
+#ifndef USE_LOCALE
+ prefixlen = strlen(prefix);
+ prefix[prefixlen] = '\377';
+ prefix[prefixlen+1] = '\0';
+
+ optup = SearchSysCacheTuple(OPRNAME,
+ PointerGetDatum("<="),
+ ObjectIdGetDatum(datatype),
+ ObjectIdGetDatum(datatype),
+ CharGetDatum('b'));
+ if (!HeapTupleIsValid(optup))
+ elog(ERROR, "prefix_quals: no <= operator for type %u", datatype);
+ conval = (datatype == NAMEOID) ?
+ (void*) namein(prefix) : (void*) textin(prefix);
+ con = makeConst(datatype, ((datatype == NAMEOID) ? NAMEDATALEN : -1),
+ PointerGetDatum(conval),
+ false, false, false, false);
+ op = makeOper(optup->t_data->t_oid, InvalidOid, BOOLOID, 0, NULL);
+ expr = make_opclause(op, leftop, (Var *) con);
+ result = lappend(result, expr);
+#endif
+
+ return result;
+}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
/*
* best available index for each subclause.
*/
IndexPath *pathnode = makeNode(IndexPath);
+ List *indexquals;
List *indexids;
- List *orclause;
Cost cost;
Cost selec;
rel,
clausenode->clause->args,
clausenode->indexids,
+ &indexquals,
&indexids,
&cost,
&selec);
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);
}
}
* 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.
*/
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;
* 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
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 */
*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);
index_selectivity(root,
lfirsti(rel->relids),
indexid,
- indexquals,
+ indexqual,
&npages,
&selec);
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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)
{
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. *
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,
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)
{
* 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)
{
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);
*
*
* 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
/*
* 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)
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
*****************************************************************************/
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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"
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;
}
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.
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),
* 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;
}
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)
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;
}
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,
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;
}
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;
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;
}
*
*
* 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
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);
| '(' 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
| '(' 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
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.
*
* 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
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 ));
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 ));
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 - - ));
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 ));
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 ));
/* 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 ));
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* information. */
Cost outerjoincost;
Relids joinid;
- List *loc_restrictinfo;
} Path;
/*----------
* 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
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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