diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index ca17faf8a5f71b14fe4571059c4a6d5996bf6e5f..db97c2ed39bb57ef47f85492ca96d625286f55fa 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -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); } /* ---------------- diff --git a/src/backend/nodes/freefuncs.c b/src/backend/nodes/freefuncs.c index 8bedf17fdb971187f065518a78aab0d566521eff..b06536d19777c04f78f18995a8bf8a62dc23b9ce 100644 --- a/src/backend/nodes/freefuncs.c +++ b/src/backend/nodes/freefuncs.c @@ -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); } /* ---------------- diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 2f1002c78f5b5e1eb31045e386f877b110869b84..670a41c60adcbcf39d7e227fe7497f03af07d6a3 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -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" @@ -35,8 +37,13 @@ #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; +} diff --git a/src/backend/optimizer/path/orindxpath.c b/src/backend/optimizer/path/orindxpath.c index 39fa69cdef818940258d555bd42a8aa528306b43..450b8d7b2dc53c847036bccd7e58c14fe7517a86 100644 --- a/src/backend/optimizer/path/orindxpath.c +++ b/src/backend/optimizer/path/orindxpath.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.30 1999/07/25 23:07:24 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.31 1999/07/27 03:51:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -28,11 +28,13 @@ static void best_or_subclause_indices(Query *root, RelOptInfo *rel, List *subclauses, List *indices, + List **indexquals, List **indexids, Cost *cost, Cost *selec); static void best_or_subclause_index(Query *root, RelOptInfo *rel, - Expr *subclause, List *indices, - int *indexid, Cost *cost, Cost *selec); + List *indexqual, List *indices, + int *retIndexid, + Cost *retCost, Cost *retSelec); /* @@ -84,8 +86,8 @@ create_or_index_paths(Query *root, * best available index for each subclause. */ IndexPath *pathnode = makeNode(IndexPath); + List *indexquals; List *indexids; - List *orclause; Cost cost; Cost selec; @@ -93,6 +95,7 @@ create_or_index_paths(Query *root, rel, clausenode->clause->args, clausenode->indexids, + &indexquals, &indexids, &cost, &selec); @@ -111,43 +114,11 @@ create_or_index_paths(Query *root, pathnode->path.pathorder->ord.sortop = NULL; pathnode->path.pathkeys = NIL; - /* - * Generate an indexqual list from the OR clause's args. - * We want two levels of sublist: the first is implicit OR - * and the second is implicit AND. (Currently, we will never - * see a sub-AND-clause because of cnfify(), but someday maybe - * the code below will do something useful...) - */ - pathnode->indexqual = NIL; - foreach(orclause, clausenode->clause->args) - { - Expr *subclause = (Expr *) lfirst(orclause); - List *sublist; - - if (and_clause((Node *) subclause)) - sublist = subclause->args; - else - sublist = lcons(subclause, NIL); - /* expansion call... */ - pathnode->indexqual = lappend(pathnode->indexqual, - sublist); - } + pathnode->indexqual = indexquals; pathnode->indexid = indexids; pathnode->path.path_cost = cost; clausenode->selectivity = (Cost) selec; - /* - * copy restrictinfo list into path for expensive function - * processing -- JMH, 7/7/92 - */ - pathnode->path.loc_restrictinfo = set_difference(copyObject((Node *) rel->restrictinfo), - lcons(clausenode, NIL)); - -#ifdef NOT_USED /* fix xfunc */ - /* add in cost for expensive functions! -- JMH, 7/7/92 */ - if (XfuncMode != XFUNC_OFF) - ((Path *) pathnode)->path_cost += xfunc_get_path_cost((Path) pathnode); -#endif path_list = lappend(path_list, pathnode); } } @@ -163,11 +134,21 @@ create_or_index_paths(Query *root, * indices. The cost is the sum of the individual index costs, since * the executor will perform a scan for each subclause of the 'or'. * + * This routine also creates the indexquals and indexids lists that will + * be needed by the executor. The indexquals list has one entry for each + * scan of the base rel, which is a sublist of indexqual conditions to + * apply in that scan. The implicit semantics are AND across each sublist + * of quals, and OR across the toplevel list (note that the executor + * takes care not to return any single tuple more than once). The indexids + * list gives the index to be used in each scan. + * * 'rel' is the node of the relation on which the indexes are defined * 'subclauses' are the subclauses of the 'or' clause * 'indices' is a list of sublists of the index nodes that matched each * subclause of the 'or' clause - * '*indexids' gets a list of the best index ID to use for each subclause + * '*indexquals' gets the constructed indexquals for the path (a list + * of sublists of clauses, one sublist per scan of the base rel) + * '*indexids' gets a list of the index IDs for each scan of the rel * '*cost' gets the total cost of the path * '*selec' gets the total selectivity of the path. */ @@ -176,27 +157,41 @@ best_or_subclause_indices(Query *root, RelOptInfo *rel, List *subclauses, List *indices, + List **indexquals, /* return value */ List **indexids, /* return value */ Cost *cost, /* return value */ Cost *selec) /* return value */ { List *slist; + *indexquals = NIL; *indexids = NIL; - *selec = (Cost) 0.0; *cost = (Cost) 0.0; + *selec = (Cost) 0.0; foreach(slist, subclauses) { + Expr *subclause = lfirst(slist); + List *indexqual; int best_indexid; Cost best_cost; Cost best_selec; - best_or_subclause_index(root, rel, lfirst(slist), lfirst(indices), + /* Convert this 'or' subclause to an indexqual list */ + indexqual = make_ands_implicit(subclause); + /* expand special operators to indexquals the executor can handle */ + indexqual = expand_indexqual_conditions(indexqual); + + best_or_subclause_index(root, rel, indexqual, lfirst(indices), &best_indexid, &best_cost, &best_selec); + *indexquals = lappend(*indexquals, indexqual); *indexids = lappendi(*indexids, best_indexid); *cost += best_cost; + /* We approximate the selectivity as the sum of the clause + * selectivities (but not more than 1). + * XXX This is too pessimistic, isn't it? + */ *selec += best_selec; if (*selec > (Cost) 1.0) *selec = (Cost) 1.0; @@ -212,7 +207,7 @@ best_or_subclause_indices(Query *root, * the least expensive. * * 'rel' is the node of the relation on which the index is defined - * 'subclause' is the subclause + * 'indexqual' is the indexqual list derived from the subclause * 'indices' is a list of index nodes that match the subclause * '*retIndexid' gets the ID of the best index * '*retCost' gets the cost of a scan with that index @@ -221,14 +216,13 @@ best_or_subclause_indices(Query *root, static void best_or_subclause_index(Query *root, RelOptInfo *rel, - Expr *subclause, + List *indexqual, List *indices, int *retIndexid, /* return value */ Cost *retCost, /* return value */ Cost *retSelec) /* return value */ { bool first_run = true; - List *indexquals; List *ilist; /* if we don't match anything, return zeros */ @@ -236,13 +230,6 @@ best_or_subclause_index(Query *root, *retCost = (Cost) 0.0; *retSelec = (Cost) 0.0; - /* convert 'or' subclause to an indexqual list */ - if (and_clause((Node *) subclause)) - indexquals = subclause->args; - else - indexquals = lcons(subclause, NIL); - /* expansion call... */ - foreach(ilist, indices) { RelOptInfo *index = (RelOptInfo *) lfirst(ilist); @@ -254,7 +241,7 @@ best_or_subclause_index(Query *root, index_selectivity(root, lfirsti(rel->relids), indexid, - indexquals, + indexqual, &npages, &selec); diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 0148d4912e198feca8c57c9dfa6470d42f3111ee..18837cf2824095c1fd29a6800648c1677f5fc5ac 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -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); diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 442ebad1e7b5582a323ffaa2116826a0d29957ba..de9ef509382a72cf0274565d6da5c9fc42c21360 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -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 *****************************************************************************/ diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 658ee57baaa667e78eb9daa296d942696863634f..aa0aedb453055209ddd9a8db4749af00bff1fd7e 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -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; } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index ec41d7efce900b4549b3dc0cdfd6b1390f215aa2..60ff7a6ef259f266e7e10db93f41e244f3c04a55 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -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. diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h index 1dec2cfcd97156f5c302f976c9ef0a7a933372a3..e22e1e5afb7fda3c91903593ff578c4de684da8b 100644 --- a/src/include/catalog/pg_operator.h +++ b/src/include/catalog/pg_operator.h @@ -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 )); diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 0402fb8fb9ee1314cc7d46f81fea01aac1cfab43..4db2e9cea445b74b70f52ecd69d78fbf39d0f6cc 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -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 diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h index 748c0d46e9fd9818eb79aae2e35e08771b0c411b..ad6976760a5f5877972642b955f88de66bb85bfc 100644 --- a/src/include/optimizer/clauses.h +++ b/src/include/optimizer/clauses.h @@ -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); diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index a26085d05e26265a3abca7b2a7dcf145f7cecf72..75d9e328458afb81ec7b32d4732e5fa214b4bb8f 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -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