diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2003-05-26 00:11:29 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2003-05-26 00:11:29 +0000 |
commit | f45df8c0144005739d09387cb594baaaa08295a6 (patch) | |
tree | 45bf02ceab43e8eb24ff7c961cff9a89e3db2770 /src/backend/optimizer/path/indxpath.c | |
parent | 297c1658ed35dc0ac4a13c190f29cc5e2ad49a0b (diff) | |
download | postgresql-f45df8c0144005739d09387cb594baaaa08295a6.tar.gz postgresql-f45df8c0144005739d09387cb594baaaa08295a6.zip |
Cause CHAR(n) to TEXT or VARCHAR conversion to automatically strip trailing
blanks, in hopes of reducing the surprise factor for newbies. Remove
redundant operators for VARCHAR (it depends wholly on TEXT operations now).
Clean up resolution of ambiguous operators/functions to avoid surprising
choices for domains: domains are treated as equivalent to their base types
and binary-coercibility is no longer considered a preference item when
choosing among multiple operators/functions. IsBinaryCoercible now correctly
reflects the notion that you need *only* relabel the type to get from type
A to type B: that is, a domain is binary-coercible to its base type, but
not vice versa. Various marginal cleanup, including merging the essentially
duplicate resolution code in parse_func.c and parse_oper.c. Improve opr_sanity
regression test to understand about binary compatibility (using pg_cast),
and fix a couple of small errors in the catalogs revealed thereby.
Restructure "special operator" handling to fetch operators via index opclasses
rather than hardwiring assumptions about names (cleans up the pattern_ops
stuff a little).
Diffstat (limited to 'src/backend/optimizer/path/indxpath.c')
-rw-r--r-- | src/backend/optimizer/path/indxpath.c | 559 |
1 files changed, 273 insertions, 286 deletions
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index b4da4666f1f..4529c30e626 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.139 2003/05/15 19:34:46 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.140 2003/05/26 00:11:27 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -20,6 +20,7 @@ #include "access/nbtree.h" #include "catalog/pg_amop.h" #include "catalog/pg_namespace.h" +#include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" #include "catalog/pg_type.h" #include "executor/executor.h" @@ -30,6 +31,7 @@ #include "optimizer/paths.h" #include "optimizer/restrictinfo.h" #include "optimizer/var.h" +#include "parser/parse_expr.h" #include "rewrite/rewriteManip.h" #include "utils/builtins.h" #include "utils/catcache.h" @@ -80,17 +82,18 @@ static bool pred_test_simple_clause(Expr *predicate, Node *clause); static Relids indexable_outerrelids(RelOptInfo *rel, IndexOptInfo *index); static Path *make_innerjoin_index_path(Query *root, RelOptInfo *rel, IndexOptInfo *index, - List *clausegroup); + List *clausegroups); static bool match_index_to_operand(int indexkey, Node *operand, RelOptInfo *rel, IndexOptInfo *index); static bool function_index_operand(Expr *funcOpnd, RelOptInfo *rel, IndexOptInfo *index); static bool match_special_index_operator(Expr *clause, Oid opclass, bool indexkey_on_left); -static List *prefix_quals(Node *leftop, Oid expr_op, +static List *expand_indexqual_condition(Expr *clause, Oid opclass); +static List *prefix_quals(Node *leftop, Oid opclass, Const *prefix, Pattern_Prefix_Status pstatus); -static List *network_prefix_quals(Node *leftop, Oid expr_op, Datum rightop); -static Oid find_operator(const char *opname, Oid datatype); +static List *network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass, + Datum rightop); static Datum string_to_datum(const char *str, Oid datatype); static Const *string_to_const(const char *str, Oid datatype); @@ -411,7 +414,7 @@ match_or_subclause_to_indexkey(RelOptInfo *rel, * Currently we'll end up rechecking both the OR clause and the transferred * restriction clause as qpquals. FIXME someday.) * - * Also, we apply expand_indexqual_conditions() to convert any special + * Also, we apply expand_indexqual_condition() to convert any special * matching opclauses to indexable operators. * * The passed-in clause is not changed. @@ -430,7 +433,7 @@ extract_or_indexqual_conditions(RelOptInfo *rel, * Extract relevant indexclauses in indexkey order. This is * essentially just like group_clauses_by_indexkey() except that the * input and output are lists of bare clauses, not of RestrictInfo - * nodes. + * nodes, and that we expand special operators immediately. */ do { @@ -448,13 +451,15 @@ extract_or_indexqual_conditions(RelOptInfo *rel, if (match_clause_to_indexkey(rel, index, curIndxKey, curClass, subsubclause)) - clausegroup = lappend(clausegroup, subsubclause); + clausegroup = nconc(clausegroup, + expand_indexqual_condition(subsubclause, + curClass)); } } else if (match_clause_to_indexkey(rel, index, curIndxKey, curClass, orsubclause)) - clausegroup = makeList1(orsubclause); + clausegroup = expand_indexqual_condition(orsubclause, curClass); /* * If we found no clauses for this indexkey in the OR subclause @@ -469,7 +474,9 @@ extract_or_indexqual_conditions(RelOptInfo *rel, if (match_clause_to_indexkey(rel, index, curIndxKey, curClass, rinfo->clause)) - clausegroup = lappend(clausegroup, rinfo->clause); + clausegroup = nconc(clausegroup, + expand_indexqual_condition(rinfo->clause, + curClass)); } } @@ -490,7 +497,7 @@ extract_or_indexqual_conditions(RelOptInfo *rel, if (quals == NIL) elog(ERROR, "extract_or_indexqual_conditions: no matching clause"); - return expand_indexqual_conditions(quals); + return quals; } @@ -501,26 +508,23 @@ extract_or_indexqual_conditions(RelOptInfo *rel, /* * group_clauses_by_indexkey - * Generates a list of restriction clauses that can be used with an index. + * Find restriction clauses that can be used with an index. * * 'rel' is the node of the relation itself. * 'index' is a index on 'rel'. * - * Returns a list of all the RestrictInfo nodes for clauses that can be - * used with this index. - * - * The list is ordered by index key. (This is not depended on by any part - * of the planner, so far as I can tell; but some parts of the executor - * do assume that the indxqual list ultimately delivered to the executor - * is so ordered. One such place is _bt_orderkeys() in the btree support. - * Perhaps that ought to be fixed someday --- tgl 7/00) + * Returns a list of sublists of RestrictInfo nodes for clauses that can be + * used with this index. Each sublist contains clauses that can be used + * with one index key (in no particular order); the top list is ordered by + * index key. (This is depended on by expand_indexqual_conditions().) * * Note that in a multi-key index, we stop if we find a key that cannot be * used with any clause. For example, given an index on (A,B,C), we might - * return (C1 C2 C3 C4) if we find that clauses C1 and C2 use column A, + * return ((C1 C2) (C3 C4)) if we find that clauses C1 and C2 use column A, * clauses C3 and C4 use column B, and no clauses use column C. But if - * no clauses match B we will return (C1 C2), whether or not there are + * no clauses match B we will return ((C1 C2)), whether or not there are * clauses matching column C, because the executor couldn't use them anyway. + * Therefore, there are no empty sublists in the result. */ static List * group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index) @@ -559,20 +563,19 @@ group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index) if (clausegroup == NIL) break; - clausegroup_list = nconc(clausegroup_list, clausegroup); + clausegroup_list = lappend(clausegroup_list, clausegroup); indexkeys++; classes++; } while (!DoneMatchingIndexKeys(indexkeys, classes)); - /* clausegroup_list holds all matched clauses ordered by indexkeys */ return clausegroup_list; } /* * group_clauses_by_indexkey_for_join - * Generates a list of clauses that can be used with an index + * Generate a list of sublists of clauses that can be used with an index * to scan the inner side of a nestloop join. * * This is much like group_clauses_by_indexkey(), but we consider both @@ -652,23 +655,20 @@ group_clauses_by_indexkey_for_join(RelOptInfo *rel, IndexOptInfo *index, if (clausegroup == NIL) break; - clausegroup_list = nconc(clausegroup_list, clausegroup); + clausegroup_list = lappend(clausegroup_list, clausegroup); indexkeys++; classes++; } while (!DoneMatchingIndexKeys(indexkeys, classes)); - /* - * if no join clause was matched then forget it, per comments above. - */ + /* if no join clause was matched then forget it, per comments above */ if (!jfound) { freeList(clausegroup_list); return NIL; } - /* clausegroup_list holds all matched clauses ordered by indexkeys */ return clausegroup_list; } @@ -1124,8 +1124,6 @@ pred_test_simple_clause(Expr *predicate, Node *clause) ExprState *test_exprstate; Datum test_result; bool isNull; - HeapTuple test_tuple; - Form_pg_amop test_form; CatCList *catlist; int i; EState *estate; @@ -1241,22 +1239,13 @@ pred_test_simple_clause(Expr *predicate, Node *clause) /* * 3. From the same opclass, find the operator for the test strategy */ - test_tuple = SearchSysCache(AMOPSTRATEGY, - ObjectIdGetDatum(opclass_id), - Int16GetDatum(test_strategy), - 0, 0); - if (!HeapTupleIsValid(test_tuple)) + test_op = get_opclass_member(opclass_id, test_strategy); + if (!OidIsValid(test_op)) { /* This should not fail, else pg_amop entry is missing */ elog(ERROR, "Missing pg_amop entry for opclass %u strategy %d", opclass_id, test_strategy); } - test_form = (Form_pg_amop) GETSTRUCT(test_tuple); - - /* Get the test operator */ - test_op = test_form->amopopr; - - ReleaseSysCache(test_tuple); /* * 4. Evaluate the test. For this we need an EState. @@ -1488,22 +1477,18 @@ best_inner_indexscan(Query *root, RelOptInfo *rel, if (jlist == NIL) /* failed to find a match? */ { - List *clausegroup; + List *clausegroups; /* find useful clauses for this index and outerjoin set */ - clausegroup = group_clauses_by_indexkey_for_join(rel, - index, - index_outer_relids, - isouterjoin); - if (clausegroup) + clausegroups = group_clauses_by_indexkey_for_join(rel, + index, + index_outer_relids, + isouterjoin); + if (clausegroups) { - /* remove duplicate and redundant clauses */ - clausegroup = remove_redundant_join_clauses(root, - clausegroup, - jointype); /* make the path */ path = make_innerjoin_index_path(root, rel, index, - clausegroup); + clausegroups); } /* Cache the result --- whether positive or negative */ @@ -1542,15 +1527,17 @@ best_inner_indexscan(Query *root, RelOptInfo *rel, * relation in a nestloop join. * * 'rel' is the relation for which 'index' is defined - * 'clausegroup' is a list of restrictinfo nodes that can use 'index' + * 'clausegroups' is a list of lists of RestrictInfos that can use 'index' */ static Path * make_innerjoin_index_path(Query *root, RelOptInfo *rel, IndexOptInfo *index, - List *clausegroup) + List *clausegroups) { IndexPath *pathnode = makeNode(IndexPath); - List *indexquals; + List *indexquals, + *allclauses, + *l; /* XXX this code ought to be merged with create_index_path? */ @@ -1564,11 +1551,8 @@ make_innerjoin_index_path(Query *root, */ pathnode->path.pathkeys = NIL; - /* Extract bare indexqual clauses from restrictinfos */ - indexquals = get_actual_clauses(clausegroup); - - /* expand special operators to indexquals the executor can handle */ - indexquals = expand_indexqual_conditions(indexquals); + /* Convert RestrictInfo nodes to indexquals the executor can handle */ + indexquals = expand_indexqual_conditions(index, clausegroups); /* * Note that we are making a pathnode for a single-scan indexscan; @@ -1583,24 +1567,31 @@ make_innerjoin_index_path(Query *root, /* * We must compute the estimated number of output rows for the * indexscan. This is less than rel->rows because of the - * additional selectivity of the join clauses. Since clausegroup + * additional selectivity of the join clauses. Since clausegroups * may contain both restriction and join clauses, we have to do a * set union to get the full set of clauses that must be - * considered to compute the correct selectivity. (We can't just - * nconc the two lists; then we might have some restriction - * clauses appearing twice, which'd mislead - * restrictlist_selectivity into double-counting their + * considered to compute the correct selectivity. (Without the union + * operation, we might have some restriction clauses appearing twice, + * which'd mislead restrictlist_selectivity into double-counting their * selectivity. However, since RestrictInfo nodes aren't copied when * linking them into different lists, it should be sufficient to use * pointer comparison to remove duplicates.) * + * We assume we can destructively modify the input sublists. + * * Always assume the join type is JOIN_INNER; even if some of the * join clauses come from other contexts, that's not our problem. */ + allclauses = NIL; + foreach(l, clausegroups) + { + /* nconc okay here since same clause couldn't be in two sublists */ + allclauses = nconc(allclauses, (List *) lfirst(l)); + } + allclauses = set_ptrUnion(rel->baserestrictinfo, allclauses); pathnode->rows = rel->tuples * restrictlist_selectivity(root, - set_ptrUnion(rel->baserestrictinfo, - clausegroup), + allclauses, rel->relid, JOIN_INNER); /* Like costsize.c, force estimate to be at least one row */ @@ -1741,10 +1732,10 @@ function_index_operand(Expr *funcOpnd, RelOptInfo *rel, IndexOptInfo *index) * 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, + * expand_indexqual_conditions() converts a list of lists of RestrictInfo + * nodes (with implicit AND semantics across list elements) into + * a list of clauses 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. *---------- @@ -1765,10 +1756,9 @@ match_special_index_operator(Expr *clause, Oid opclass, bool indexkey_on_left) { bool isIndexable = false; - Node *leftop, - *rightop; + Node *rightop; Oid expr_op; - Const *patt = NULL; + Const *patt; Const *prefix = NULL; Const *rest = NULL; @@ -1781,7 +1771,6 @@ match_special_index_operator(Expr *clause, Oid opclass, return false; /* we know these will succeed */ - leftop = get_leftop(clause); rightop = get_rightop(clause); expr_op = ((OpExpr *) clause)->opno; @@ -1795,7 +1784,6 @@ match_special_index_operator(Expr *clause, Oid opclass, { 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 */ isIndexable = pattern_fixed_prefix(patt, Pattern_Type_Like, @@ -1809,7 +1797,6 @@ match_special_index_operator(Expr *clause, Oid opclass, case OID_TEXT_ICLIKE_OP: case OID_BPCHAR_ICLIKE_OP: - case OID_VARCHAR_ICLIKE_OP: case OID_NAME_ICLIKE_OP: /* the right-hand const is type text for all of these */ isIndexable = pattern_fixed_prefix(patt, Pattern_Type_Like_IC, @@ -1818,7 +1805,6 @@ match_special_index_operator(Expr *clause, Oid opclass, 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 */ isIndexable = pattern_fixed_prefix(patt, Pattern_Type_Regex, @@ -1827,7 +1813,6 @@ match_special_index_operator(Expr *clause, Oid opclass, 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 */ isIndexable = pattern_fixed_prefix(patt, Pattern_Type_Regex_IC, @@ -1855,8 +1840,11 @@ match_special_index_operator(Expr *clause, Oid opclass, /* * Must also check that index's opclass supports the operators we will * want to apply. (A hash index, for example, will not support ">=".) - * We cheat a little by not checking for availability of "=" ... any - * index type should support "=", methinks. + * Currently, only btree supports the operators we need. + * + * We insist on the opclass being the specific one we expect, + * else we'd do the wrong thing if someone were to make a reverse-sort + * opclass with the same operators. */ switch (expr_op) { @@ -1864,69 +1852,44 @@ match_special_index_operator(Expr *clause, Oid opclass, case OID_TEXT_ICLIKE_OP: case OID_TEXT_REGEXEQ_OP: case OID_TEXT_ICREGEXEQ_OP: - if (lc_collate_is_c()) - isIndexable = (op_in_opclass(find_operator(">=", TEXTOID), opclass) - && op_in_opclass(find_operator("<", TEXTOID), opclass)); - else - isIndexable = (op_in_opclass(find_operator("~>=~", TEXTOID), opclass) - && op_in_opclass(find_operator("~<~", TEXTOID), opclass)); - break; - - case OID_BYTEA_LIKE_OP: - isIndexable = (op_in_opclass(find_operator(">=", BYTEAOID), opclass) - && op_in_opclass(find_operator("<", BYTEAOID), opclass)); + /* text operators will be used for varchar inputs, too */ + isIndexable = + (opclass == TEXT_PATTERN_BTREE_OPS_OID) || + (opclass == TEXT_BTREE_OPS_OID && lc_collate_is_c()) || + (opclass == VARCHAR_PATTERN_BTREE_OPS_OID) || + (opclass == VARCHAR_BTREE_OPS_OID && lc_collate_is_c()); break; case OID_BPCHAR_LIKE_OP: case OID_BPCHAR_ICLIKE_OP: case OID_BPCHAR_REGEXEQ_OP: case OID_BPCHAR_ICREGEXEQ_OP: - if (lc_collate_is_c()) - isIndexable = (op_in_opclass(find_operator(">=", BPCHAROID), opclass) - && op_in_opclass(find_operator("<", BPCHAROID), opclass)); - else - isIndexable = (op_in_opclass(find_operator("~>=~", BPCHAROID), opclass) - && op_in_opclass(find_operator("~<~", BPCHAROID), opclass)); - break; - - case OID_VARCHAR_LIKE_OP: - case OID_VARCHAR_ICLIKE_OP: - case OID_VARCHAR_REGEXEQ_OP: - case OID_VARCHAR_ICREGEXEQ_OP: - if (lc_collate_is_c()) - isIndexable = (op_in_opclass(find_operator(">=", VARCHAROID), opclass) - && op_in_opclass(find_operator("<", VARCHAROID), opclass)); - else - isIndexable = (op_in_opclass(find_operator("~>=~", VARCHAROID), opclass) - && op_in_opclass(find_operator("~<~", VARCHAROID), opclass)); + isIndexable = + (opclass == BPCHAR_PATTERN_BTREE_OPS_OID) || + (opclass == BPCHAR_BTREE_OPS_OID && lc_collate_is_c()); break; case OID_NAME_LIKE_OP: case OID_NAME_ICLIKE_OP: case OID_NAME_REGEXEQ_OP: case OID_NAME_ICREGEXEQ_OP: - if (lc_collate_is_c()) - isIndexable = (op_in_opclass(find_operator(">=", NAMEOID), opclass) - && op_in_opclass(find_operator("<", NAMEOID), opclass)); - else - isIndexable = (op_in_opclass(find_operator("~>=~", NAMEOID), opclass) - && op_in_opclass(find_operator("~<~", NAMEOID), opclass)); + isIndexable = + (opclass == NAME_PATTERN_BTREE_OPS_OID) || + (opclass == NAME_BTREE_OPS_OID && lc_collate_is_c()); + break; + + case OID_BYTEA_LIKE_OP: + isIndexable = (opclass == BYTEA_BTREE_OPS_OID); break; case OID_INET_SUB_OP: case OID_INET_SUBEQ_OP: - /* for SUB we actually need ">" not ">=", but this should do */ - if (!op_in_opclass(find_operator(">=", INETOID), opclass) || - !op_in_opclass(find_operator("<=", INETOID), opclass)) - isIndexable = false; + isIndexable = (opclass == INET_BTREE_OPS_OID); break; case OID_CIDR_SUB_OP: case OID_CIDR_SUBEQ_OP: - /* for SUB we actually need ">" not ">=", but this should do */ - if (!op_in_opclass(find_operator(">=", CIDROID), opclass) || - !op_in_opclass(find_operator("<=", CIDROID), opclass)) - isIndexable = false; + isIndexable = (opclass == CIDR_BTREE_OPS_OID); break; } @@ -1935,185 +1898,217 @@ match_special_index_operator(Expr *clause, Oid opclass, /* * 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. + * Given a list of sublists of RestrictInfo nodes, produce a flat list + * of index qual clauses. Standard qual clauses (those in the index's + * opclass) are passed through unchanged. "Special" index operators + * are expanded into clauses that the indexscan machinery will know + * what to do with. + * + * The input list is ordered by index key, and so the output list is too. + * (The latter is not depended on by any part of the planner, so far as I can + * tell; but some parts of the executor do assume that the indxqual list + * ultimately delivered to the executor is so ordered. One such place is + * _bt_orderkeys() in the btree support. Perhaps that ought to be fixed + * someday --- tgl 7/00) */ List * -expand_indexqual_conditions(List *indexquals) +expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups) { List *resultquals = NIL; - List *q; + int *indexkeys = index->indexkeys; + Oid *classes = index->classlist; + + if (clausegroups == NIL) + return NIL; - foreach(q, indexquals) + do { - Expr *clause = (Expr *) lfirst(q); - - /* we know these will succeed */ - Node *leftop = get_leftop(clause); - Node *rightop = get_rightop(clause); - Oid expr_op = ((OpExpr *) clause)->opno; - Const *patt = (Const *) rightop; - Const *prefix = NULL; - Const *rest = NULL; - Pattern_Prefix_Status pstatus; - - switch (expr_op) + Oid curClass = classes[0]; + List *i; + + foreach(i, (List *) lfirst(clausegroups)) { - /* - * 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: - case OID_BYTEA_LIKE_OP: - pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like, - &prefix, &rest); - resultquals = nconc(resultquals, - prefix_quals(leftop, expr_op, - prefix, pstatus)); - break; + RestrictInfo *rinfo = (RestrictInfo *) lfirst(i); - case OID_TEXT_ICLIKE_OP: - case OID_BPCHAR_ICLIKE_OP: - case OID_VARCHAR_ICLIKE_OP: - case OID_NAME_ICLIKE_OP: - /* the right-hand const is type text for all of these */ - pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like_IC, - &prefix, &rest); - resultquals = nconc(resultquals, - prefix_quals(leftop, expr_op, - prefix, pstatus)); - break; + resultquals = nconc(resultquals, + expand_indexqual_condition(rinfo->clause, + curClass)); + } - 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 */ - pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex, - &prefix, &rest); - resultquals = nconc(resultquals, - prefix_quals(leftop, expr_op, - prefix, pstatus)); - break; + clausegroups = lnext(clausegroups); - 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 */ - pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex_IC, - &prefix, &rest); - resultquals = nconc(resultquals, - prefix_quals(leftop, expr_op, - prefix, pstatus)); - break; + indexkeys++; + classes++; - case OID_INET_SUB_OP: - case OID_INET_SUBEQ_OP: - case OID_CIDR_SUB_OP: - case OID_CIDR_SUBEQ_OP: - resultquals = nconc(resultquals, - network_prefix_quals(leftop, expr_op, - patt->constvalue)); - break; + } while (clausegroups != NIL && + !DoneMatchingIndexKeys(indexkeys, classes)); - default: - resultquals = lappend(resultquals, clause); - break; - } - } + Assert(clausegroups == NIL); /* else more groups than indexkeys... */ return resultquals; } /* + * expand_indexqual_condition --- expand a single indexqual condition + */ +static List * +expand_indexqual_condition(Expr *clause, Oid opclass) +{ + /* we know these will succeed */ + Node *leftop = get_leftop(clause); + Node *rightop = get_rightop(clause); + Oid expr_op = ((OpExpr *) clause)->opno; + Const *patt = (Const *) rightop; + Const *prefix = NULL; + Const *rest = NULL; + Pattern_Prefix_Status pstatus; + List *result; + + 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_NAME_LIKE_OP: + case OID_BYTEA_LIKE_OP: + pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like, + &prefix, &rest); + result = prefix_quals(leftop, opclass, prefix, pstatus); + break; + + case OID_TEXT_ICLIKE_OP: + case OID_BPCHAR_ICLIKE_OP: + case OID_NAME_ICLIKE_OP: + /* the right-hand const is type text for all of these */ + pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like_IC, + &prefix, &rest); + result = prefix_quals(leftop, opclass, prefix, pstatus); + break; + + case OID_TEXT_REGEXEQ_OP: + case OID_BPCHAR_REGEXEQ_OP: + case OID_NAME_REGEXEQ_OP: + /* the right-hand const is type text for all of these */ + pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex, + &prefix, &rest); + result = prefix_quals(leftop, opclass, prefix, pstatus); + break; + + case OID_TEXT_ICREGEXEQ_OP: + case OID_BPCHAR_ICREGEXEQ_OP: + case OID_NAME_ICREGEXEQ_OP: + /* the right-hand const is type text for all of these */ + pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex_IC, + &prefix, &rest); + result = prefix_quals(leftop, opclass, prefix, pstatus); + break; + + case OID_INET_SUB_OP: + case OID_INET_SUBEQ_OP: + case OID_CIDR_SUB_OP: + case OID_CIDR_SUBEQ_OP: + result = network_prefix_quals(leftop, expr_op, opclass, + patt->constvalue); + break; + + default: + result = makeList1(clause); + break; + } + + return result; +} + +/* * 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 + * generate suitable indexqual condition(s). opclass is the index + * operator class; we use it to deduce the appropriate comparison * operators and operand datatypes. */ static List * -prefix_quals(Node *leftop, Oid expr_op, +prefix_quals(Node *leftop, Oid opclass, Const *prefix_const, Pattern_Prefix_Status pstatus) { List *result; Oid datatype; Oid oproid; - const char *oprname; - char *prefix; - Const *con; Expr *expr; - Const *greaterstr = NULL; + Const *greaterstr; Assert(pstatus != Pattern_Prefix_None); - switch (expr_op) + switch (opclass) { - case OID_TEXT_LIKE_OP: - case OID_TEXT_ICLIKE_OP: - case OID_TEXT_REGEXEQ_OP: - case OID_TEXT_ICREGEXEQ_OP: + case TEXT_BTREE_OPS_OID: + case TEXT_PATTERN_BTREE_OPS_OID: datatype = TEXTOID; break; - case OID_BYTEA_LIKE_OP: - datatype = BYTEAOID; + case VARCHAR_BTREE_OPS_OID: + case VARCHAR_PATTERN_BTREE_OPS_OID: + datatype = VARCHAROID; break; - case OID_BPCHAR_LIKE_OP: - case OID_BPCHAR_ICLIKE_OP: - case OID_BPCHAR_REGEXEQ_OP: - case OID_BPCHAR_ICREGEXEQ_OP: + case BPCHAR_BTREE_OPS_OID: + case BPCHAR_PATTERN_BTREE_OPS_OID: datatype = BPCHAROID; break; - case OID_VARCHAR_LIKE_OP: - case OID_VARCHAR_ICLIKE_OP: - case OID_VARCHAR_REGEXEQ_OP: - case OID_VARCHAR_ICREGEXEQ_OP: - datatype = VARCHAROID; + case NAME_BTREE_OPS_OID: + case NAME_PATTERN_BTREE_OPS_OID: + datatype = NAMEOID; break; - case OID_NAME_LIKE_OP: - case OID_NAME_ICLIKE_OP: - case OID_NAME_REGEXEQ_OP: - case OID_NAME_ICREGEXEQ_OP: - datatype = NAMEOID; + case BYTEA_BTREE_OPS_OID: + datatype = BYTEAOID; break; default: - elog(ERROR, "prefix_quals: unexpected operator %u", expr_op); + elog(ERROR, "prefix_quals: unexpected opclass %u", opclass); return NIL; } - /* Prefix constant is text for all except BYTEA_LIKE */ - if (datatype != BYTEAOID) - prefix = DatumGetCString(DirectFunctionCall1(textout, - prefix_const->constvalue)); - else - prefix = DatumGetCString(DirectFunctionCall1(byteaout, - prefix_const->constvalue)); + /* + * If necessary, coerce the prefix constant to the right type. + * The given prefix constant is either text or bytea type. + */ + if (prefix_const->consttype != datatype) + { + char *prefix; + + switch (prefix_const->consttype) + { + case TEXTOID: + prefix = DatumGetCString(DirectFunctionCall1(textout, + prefix_const->constvalue)); + break; + case BYTEAOID: + prefix = DatumGetCString(DirectFunctionCall1(byteaout, + prefix_const->constvalue)); + break; + default: + elog(ERROR, "prefix_quals: unexpected consttype %u", + prefix_const->consttype); + return NIL; + } + prefix_const = string_to_const(prefix, datatype); + pfree(prefix); + } /* * If we found an exact-match pattern, generate an "=" indexqual. */ if (pstatus == Pattern_Prefix_Exact) { - oprname = (datatype == BYTEAOID || lc_collate_is_c() ? "=" : "~=~"); - oproid = find_operator(oprname, datatype); + oproid = get_opclass_member(opclass, BTEqualStrategyNumber); if (oproid == InvalidOid) - elog(ERROR, "prefix_quals: no operator %s for type %u", oprname, datatype); - con = string_to_const(prefix, datatype); + elog(ERROR, "prefix_quals: no operator = for opclass %u", opclass); expr = make_opclause(oproid, BOOLOID, false, - (Expr *) leftop, (Expr *) con); + (Expr *) leftop, (Expr *) prefix_const); result = makeList1(expr); return result; } @@ -2123,13 +2118,11 @@ prefix_quals(Node *leftop, Oid expr_op, * * We can always say "x >= prefix". */ - oprname = (datatype == BYTEAOID || lc_collate_is_c() ? ">=" : "~>=~"); - oproid = find_operator(oprname, datatype); + oproid = get_opclass_member(opclass, BTGreaterEqualStrategyNumber); if (oproid == InvalidOid) - elog(ERROR, "prefix_quals: no operator %s for type %u", oprname, datatype); - con = string_to_const(prefix, datatype); + elog(ERROR, "prefix_quals: no operator >= for opclass %u", opclass); expr = make_opclause(oproid, BOOLOID, false, - (Expr *) leftop, (Expr *) con); + (Expr *) leftop, (Expr *) prefix_const); result = makeList1(expr); /*------- @@ -2137,13 +2130,12 @@ prefix_quals(Node *leftop, Oid expr_op, * "x < greaterstr". *------- */ - greaterstr = make_greater_string(con); + greaterstr = make_greater_string(prefix_const); if (greaterstr) { - oprname = (datatype == BYTEAOID || lc_collate_is_c() ? "<" : "~<~"); - oproid = find_operator(oprname, datatype); + oproid = get_opclass_member(opclass, BTLessStrategyNumber); if (oproid == InvalidOid) - elog(ERROR, "prefix_quals: no operator %s for type %u", oprname, datatype); + elog(ERROR, "prefix_quals: no operator < for opclass %u", opclass); expr = make_opclause(oproid, BOOLOID, false, (Expr *) leftop, (Expr *) greaterstr); result = lappend(result, expr); @@ -2155,19 +2147,18 @@ prefix_quals(Node *leftop, Oid expr_op, /* * Given a leftop and a rightop, and a inet-class sup/sub operator, * generate suitable indexqual condition(s). expr_op is the original - * operator. + * operator, and opclass is the index opclass. */ static List * -network_prefix_quals(Node *leftop, Oid expr_op, Datum rightop) +network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass, Datum rightop) { bool is_eq; - char *opr1name; - Datum opr1right; - Datum opr2right; + Oid datatype; Oid opr1oid; Oid opr2oid; + Datum opr1right; + Datum opr2right; List *result; - Oid datatype; Expr *expr; switch (expr_op) @@ -2198,12 +2189,20 @@ network_prefix_quals(Node *leftop, Oid expr_op, Datum rightop) * create clause "key >= network_scan_first( rightop )", or ">" if the * operator disallows equality. */ - - opr1name = is_eq ? ">=" : ">"; - opr1oid = find_operator(opr1name, datatype); - if (opr1oid == InvalidOid) - elog(ERROR, "network_prefix_quals: no %s operator for type %u", - opr1name, datatype); + if (is_eq) + { + opr1oid = get_opclass_member(opclass, BTGreaterEqualStrategyNumber); + if (opr1oid == InvalidOid) + elog(ERROR, "network_prefix_quals: no >= operator for opclass %u", + opclass); + } + else + { + opr1oid = get_opclass_member(opclass, BTGreaterStrategyNumber); + if (opr1oid == InvalidOid) + elog(ERROR, "network_prefix_quals: no > operator for opclass %u", + opclass); + } opr1right = network_scan_first(rightop); @@ -2215,10 +2214,10 @@ network_prefix_quals(Node *leftop, Oid expr_op, Datum rightop) /* create clause "key <= network_scan_last( rightop )" */ - opr2oid = find_operator("<=", datatype); + opr2oid = get_opclass_member(opclass, BTLessEqualStrategyNumber); if (opr2oid == InvalidOid) - elog(ERROR, "network_prefix_quals: no <= operator for type %u", - datatype); + elog(ERROR, "network_prefix_quals: no <= operator for opclass %u", + opclass); opr2right = network_scan_last(rightop); @@ -2235,18 +2234,6 @@ network_prefix_quals(Node *leftop, Oid expr_op, Datum rightop) * Handy subroutines for match_special_index_operator() and friends. */ -/* See if there is a binary op of the given name for the given datatype */ -/* NB: we assume that only built-in system operators are searched for */ -static Oid -find_operator(const char *opname, Oid datatype) -{ - return GetSysCacheOid(OPERNAMENSP, - PointerGetDatum(opname), - ObjectIdGetDatum(datatype), - ObjectIdGetDatum(datatype), - ObjectIdGetDatum(PG_CATALOG_NAMESPACE)); -} - /* * Generate a Datum of the appropriate type from a C string. * Note that all of the supported types are pass-by-ref, so the |