]> granicus.if.org Git - postgresql/commitdiff
First cut at doing LIKE/regex indexing optimization in
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 27 Jul 1999 03:51:11 +0000 (03:51 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 27 Jul 1999 03:51:11 +0000 (03:51 +0000)
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.

12 files changed:
src/backend/nodes/copyfuncs.c
src/backend/nodes/freefuncs.c
src/backend/optimizer/path/indxpath.c
src/backend/optimizer/path/orindxpath.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/util/clauses.c
src/backend/optimizer/util/pathnode.c
src/backend/parser/gram.y
src/include/catalog/pg_operator.h
src/include/nodes/relation.h
src/include/optimizer/clauses.h
src/include/optimizer/paths.h

index ca17faf8a5f71b14fe4571059c4a6d5996bf6e5f..db97c2ed39bb57ef47f85492ca96d625286f55fa 100644 (file)
@@ -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);
 }
 
 /* ----------------
index 8bedf17fdb971187f065518a78aab0d566521eff..b06536d19777c04f78f18995a8bf8a62dc23b9ce 100644 (file)
@@ -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);
 }
 
 /* ----------------
index 2f1002c78f5b5e1eb31045e386f877b110869b84..670a41c60adcbcf39d7e227fe7497f03af07d6a3 100644 (file)
@@ -8,10 +8,11 @@
  *
  *
  * 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"
@@ -20,6 +21,7 @@
 #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);
@@ -65,6 +72,12 @@ static List *create_index_path_group(Query *root, RelOptInfo *rel, RelOptInfo *i
 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);
 
 
 /*
@@ -502,7 +515,8 @@ group_clauses_by_ikey_for_joins(RelOptInfo *rel,
  *               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.
@@ -539,6 +553,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
        bool            isIndexable = false;
        Var                *leftop,
                           *rightop;
+       Oid                     expr_op;
 
        if (! is_opclause((Node *) clause))
                return false;
@@ -546,6 +561,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
        rightop = get_rightop(clause);
        if (! leftop || ! rightop)
                return false;
+       expr_op = ((Oper *) clause->oper)->opno;
 
        if (!join)
        {
@@ -553,23 +569,17 @@ match_clause_to_indexkey(RelOptInfo *rel,
                 * 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
@@ -583,88 +593,70 @@ match_clause_to_indexkey(RelOptInfo *rel,
                                 * 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);
                                        }
                                }
                        }
@@ -672,13 +664,22 @@ match_clause_to_indexkey(RelOptInfo *rel,
 
                        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
@@ -694,10 +695,10 @@ match_clause_to_indexkey(RelOptInfo *rel,
 
                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))
@@ -1221,6 +1222,8 @@ index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
                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),
@@ -1258,18 +1261,6 @@ index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
                                                                                          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);
        }
@@ -1419,3 +1410,460 @@ function_index_operand(Expr *funcOpnd, RelOptInfo *rel, RelOptInfo *index)
 
        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;
+}
index 39fa69cdef818940258d555bd42a8aa528306b43..450b8d7b2dc53c847036bccd7e58c14fe7517a86 100644 (file)
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
 
 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);
 
index 0148d4912e198feca8c57c9dfa6470d42f3111ee..18837cf2824095c1fd29a6800648c1677f5fc5ac 100644 (file)
@@ -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);
 
index 442ebad1e7b5582a323ffaa2116826a0d29957ba..de9ef509382a72cf0274565d6da5c9fc42c21360 100644 (file)
@@ -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
  *****************************************************************************/
index 658ee57baaa667e78eb9daa296d942696863634f..aa0aedb453055209ddd9a8db4749af00bff1fd7e 100644 (file)
@@ -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;
 }
index ec41d7efce900b4549b3dc0cdfd6b1390f215aa2..60ff7a6ef259f266e7e10db93f41e244f3c04a55 100644 (file)
@@ -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.
index 1dec2cfcd97156f5c302f976c9ef0a7a933372a3..e22e1e5afb7fda3c91903593ff578c4de684da8b 100644 (file)
@@ -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 ));
index 0402fb8fb9ee1314cc7d46f81fea01aac1cfab43..4db2e9cea445b74b70f52ecd69d78fbf39d0f6cc 100644 (file)
@@ -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
index 748c0d46e9fd9818eb79aae2e35e08771b0c411b..ad6976760a5f5877972642b955f88de66bb85bfc 100644 (file)
@@ -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);
 
index a26085d05e26265a3abca7b2a7dcf145f7cecf72..75d9e328458afb81ec7b32d4732e5fa214b4bb8f 100644 (file)
@@ -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