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 | |
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')
-rw-r--r-- | src/backend/optimizer/path/indxpath.c | 559 | ||||
-rw-r--r-- | src/backend/optimizer/util/pathnode.c | 9 | ||||
-rw-r--r-- | src/backend/parser/parse_coerce.c | 150 | ||||
-rw-r--r-- | src/backend/parser/parse_func.c | 213 | ||||
-rw-r--r-- | src/backend/parser/parse_oper.c | 421 | ||||
-rw-r--r-- | src/backend/utils/adt/selfuncs.c | 144 | ||||
-rw-r--r-- | src/backend/utils/adt/varchar.c | 193 | ||||
-rw-r--r-- | src/backend/utils/cache/lsyscache.c | 29 |
8 files changed, 622 insertions, 1096 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 diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 3984c666f51..25648beed19 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.88 2003/02/15 20:12:40 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.89 2003/05/26 00:11:27 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -328,7 +328,7 @@ create_seqscan_path(Query *root, RelOptInfo *rel) * * 'rel' is the parent rel * 'index' is an index on 'rel' - * 'restriction_clauses' is a list of RestrictInfo nodes + * 'restriction_clauses' is a list of lists of RestrictInfo nodes * to be used as index qual conditions in the scan. * 'pathkeys' describes the ordering of the path. * 'indexscandir' is ForwardScanDirection or BackwardScanDirection @@ -352,9 +352,8 @@ create_index_path(Query *root, pathnode->path.parent = rel; pathnode->path.pathkeys = pathkeys; - indexquals = get_actual_clauses(restriction_clauses); - /* 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, restriction_clauses); /* * We are making a pathnode for a single-scan indexscan; therefore, diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 9dc0c7f1c19..fc35c2d6d41 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.96 2003/04/29 22:13:10 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.97 2003/05/26 00:11:27 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -32,7 +32,6 @@ static Node *coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod, CoercionForm cformat, bool isExplicit); -static Oid PreferredType(CATEGORY category, Oid type); static Node *build_func_call(Oid funcid, Oid rettype, List *args, CoercionForm fformat); @@ -66,28 +65,43 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, if (can_coerce_type(1, &exprtype, &targettype, ccontext)) expr = coerce_type(pstate, expr, exprtype, targettype, ccontext, cformat); - /* - * String hacks to get transparent conversions for char and varchar: - * if a coercion to text is available, use it for forced coercions to - * char(n) or varchar(n). - * - * This is pretty grotty, but seems easier to maintain than providing - * entries in pg_cast that parallel all the ones for text. - */ - else if (ccontext >= COERCION_ASSIGNMENT && - (targettype == BPCHAROID || targettype == VARCHAROID)) + else if (ccontext >= COERCION_ASSIGNMENT) { - Oid text_id = TEXTOID; + /* + * String hacks to get transparent conversions for char and varchar: + * if a coercion to text is available, use it for forced coercions to + * char(n) or varchar(n) or domains thereof. + * + * This is pretty grotty, but seems easier to maintain than providing + * entries in pg_cast that parallel all the ones for text. + */ + Oid targetbasetype = getBaseType(targettype); - if (can_coerce_type(1, &exprtype, &text_id, ccontext)) + if (targetbasetype == BPCHAROID || targetbasetype == VARCHAROID) { - expr = coerce_type(pstate, expr, exprtype, text_id, - ccontext, cformat); - /* Need a RelabelType if no typmod coercion is performed */ - if (targettypmod < 0) - expr = (Node *) makeRelabelType((Expr *) expr, - targettype, -1, - cformat); + Oid text_id = TEXTOID; + + if (can_coerce_type(1, &exprtype, &text_id, ccontext)) + { + expr = coerce_type(pstate, expr, exprtype, text_id, + ccontext, cformat); + if (targetbasetype != targettype) + { + /* need to coerce to domain over char or varchar */ + expr = coerce_to_domain(expr, targetbasetype, targettype, + cformat); + } + else + { + /* need a RelabelType if no typmod coercion will be performed */ + if (targettypmod < 0) + expr = (Node *) makeRelabelType((Expr *) expr, + targettype, -1, + cformat); + } + } + else + expr = NULL; } else expr = NULL; @@ -923,7 +937,10 @@ enforce_generic_type_consistency(Oid *actual_arg_types, /* TypeCategory() - * Assign a category to the specified OID. + * Assign a category to the specified type OID. + * + * NB: this must not return INVALID_TYPE. + * * XXX This should be moved to system catalog lookups * to allow for better type extensibility. * - thomas 2001-09-30 @@ -1026,7 +1043,11 @@ TypeCategory(Oid inType) /* IsPreferredType() - * Check if this type is a preferred type. + * Check if this type is a preferred type for the given category. + * + * If category is INVALID_TYPE, then we'll return TRUE for preferred types + * of any category; otherwise, only for preferred types of that category. + * * XXX This should be moved to system catalog lookups * to allow for better type extensibility. * - thomas 2001-09-30 @@ -1034,39 +1055,34 @@ TypeCategory(Oid inType) bool IsPreferredType(CATEGORY category, Oid type) { - return (type == PreferredType(category, type)); -} /* IsPreferredType() */ + Oid preftype; + if (category == INVALID_TYPE) + category = TypeCategory(type); + else if (category != TypeCategory(type)) + return false; -/* PreferredType() - * Return the preferred type OID for the specified category. - * XXX This should be moved to system catalog lookups - * to allow for better type extensibility. - * - thomas 2001-09-30 - */ -static Oid -PreferredType(CATEGORY category, Oid type) -{ - Oid result; - + /* + * This switch should agree with TypeCategory(), above. Note that + * at this point, category certainly matches the type. + */ switch (category) { - case (INVALID_TYPE): case (UNKNOWN_TYPE): case (GENERIC_TYPE): - result = UNKNOWNOID; + preftype = UNKNOWNOID; break; case (BOOLEAN_TYPE): - result = BOOLOID; + preftype = BOOLOID; break; case (STRING_TYPE): - result = TEXTOID; + preftype = TEXTOID; break; case (BITSTRING_TYPE): - result = VARBITOID; + preftype = VARBITOID; break; case (NUMERIC_TYPE): @@ -1077,52 +1093,59 @@ PreferredType(CATEGORY category, Oid type) type == REGOPERATOROID || type == REGCLASSOID || type == REGTYPEOID) - result = OIDOID; + preftype = OIDOID; else - result = FLOAT8OID; + preftype = FLOAT8OID; break; case (DATETIME_TYPE): if (type == DATEOID) - result = TIMESTAMPOID; + preftype = TIMESTAMPOID; else - result = TIMESTAMPTZOID; + preftype = TIMESTAMPTZOID; break; case (TIMESPAN_TYPE): - result = INTERVALOID; + preftype = INTERVALOID; break; case (GEOMETRIC_TYPE): - result = type; + preftype = type; break; case (NETWORK_TYPE): - result = INETOID; + preftype = INETOID; break; case (USER_TYPE): - result = type; + preftype = type; break; default: - elog(ERROR, "PreferredType: unknown category"); - result = UNKNOWNOID; + elog(ERROR, "IsPreferredType: unknown category"); + preftype = UNKNOWNOID; break; } - return result; -} /* PreferredType() */ + + return (type == preftype); +} /* IsPreferredType() */ /* IsBinaryCoercible() * Check if srctype is binary-coercible to targettype. * * This notion allows us to cheat and directly exchange values without - * going through the trouble of calling a conversion function. + * going through the trouble of calling a conversion function. Note that + * in general, this should only be an implementation shortcut. Before 7.4, + * this was also used as a heuristic for resolving overloaded functions and + * operators, but that's basically a bad idea. * * As of 7.3, binary coercibility isn't hardwired into the code anymore. * We consider two types binary-coercible if there is an implicitly - * invokable, no-function-needed pg_cast entry. + * invokable, no-function-needed pg_cast entry. Also, a domain is always + * binary-coercible to its base type, though *not* vice versa (in the other + * direction, one must apply domain constraint checks before accepting the + * value as legitimate). * * This function replaces IsBinaryCompatible(), which was an inherently * symmetric test. Since the pg_cast entries aren't necessarily symmetric, @@ -1139,13 +1162,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype) if (srctype == targettype) return true; - /* Perhaps the types are domains; if so, look at their base types */ + /* If srctype is a domain, reduce to its base type */ if (OidIsValid(srctype)) srctype = getBaseType(srctype); - if (OidIsValid(targettype)) - targettype = getBaseType(targettype); - /* Somewhat-fast path if same base type */ + /* Somewhat-fast path for domain -> base type case */ if (srctype == targettype) return true; @@ -1174,8 +1195,13 @@ IsBinaryCoercible(Oid srctype, Oid targettype) * ccontext determines the set of available casts. * * If we find a suitable entry in pg_cast, return TRUE, and set *funcid - * to the castfunc value (which may be InvalidOid for a binary-compatible - * coercion). + * to the castfunc value, which may be InvalidOid for a binary-compatible + * coercion. + * + * NOTE: *funcid == InvalidOid does not necessarily mean that no work is + * needed to do the coercion; if the target is a domain then we may need to + * apply domain constraint checking. If you want to check for a zero-effort + * conversion then use IsBinaryCoercible(). */ bool find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, @@ -1193,7 +1219,7 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, if (OidIsValid(targetTypeId)) targetTypeId = getBaseType(targetTypeId); - /* Domains are automatically binary-compatible with their base type */ + /* Domains are always coercible to and from their base type */ if (sourceTypeId == targetTypeId) return true; diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 058b9aad73d..e9a40e03179 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.147 2003/04/29 22:13:10 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.148 2003/05/26 00:11:27 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -16,7 +16,6 @@ #include "access/heapam.h" #include "catalog/catname.h" -#include "catalog/namespace.h" #include "catalog/pg_inherits.h" #include "catalog/pg_proc.h" #include "lib/stringinfo.h" @@ -37,13 +36,7 @@ static Oid **argtype_inherit(int nargs, Oid *argtypes); static int find_inheritors(Oid relid, Oid **supervec); static Oid **gen_cross_product(InhPaths *arginh, int nargs); -static int match_argtypes(int nargs, - Oid *input_typeids, - FuncCandidateList function_typeids, - FuncCandidateList *candidates); static FieldSelect *setup_field_select(Node *input, char *attname, Oid relid); -static FuncCandidateList func_select_candidate(int nargs, Oid *input_typeids, - FuncCandidateList candidates); static void unknown_attribute(const char *schemaname, const char *relname, const char *attname); @@ -355,21 +348,24 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, } -/* match_argtypes() +/* func_match_argtypes() * - * Given a list of possible typeid arrays to a function and an array of - * input typeids, produce a shortlist of those function typeid arrays - * that match the input typeids (either exactly or by coercion), and - * return the number of such arrays. + * Given a list of candidate functions (having the right name and number + * of arguments) and an array of input datatype OIDs, produce a shortlist of + * those candidates that actually accept the input datatypes (either exactly + * or by coercion), and return the number of such candidates. + * + * Note that can_coerce_type will assume that UNKNOWN inputs are coercible to + * anything, so candidates will not be eliminated on that basis. * * NB: okay to modify input list structure, as long as we find at least - * one match. + * one match. If no match at all, the list must remain unmodified. */ -static int -match_argtypes(int nargs, - Oid *input_typeids, - FuncCandidateList function_typeids, - FuncCandidateList *candidates) /* return value */ +int +func_match_argtypes(int nargs, + Oid *input_typeids, + FuncCandidateList raw_candidates, + FuncCandidateList *candidates) /* return value */ { FuncCandidateList current_candidate; FuncCandidateList next_candidate; @@ -377,7 +373,7 @@ match_argtypes(int nargs, *candidates = NULL; - for (current_candidate = function_typeids; + for (current_candidate = raw_candidates; current_candidate != NULL; current_candidate = next_candidate) { @@ -392,21 +388,65 @@ match_argtypes(int nargs, } return ncandidates; -} /* match_argtypes() */ +} /* func_match_argtypes() */ /* func_select_candidate() - * Given the input argtype array and more than one candidate - * for the function, attempt to resolve the conflict. + * Given the input argtype array and more than one candidate + * for the function, attempt to resolve the conflict. + * * Returns the selected candidate if the conflict can be resolved, * otherwise returns NULL. * - * By design, this is pretty similar to oper_select_candidate in parse_oper.c. - * However, the calling convention is a little different: we assume the caller - * already pruned away "candidates" that aren't actually coercion-compatible - * with the input types, whereas oper_select_candidate must do that itself. + * Note that the caller has already determined that there is no candidate + * exactly matching the input argtypes, and has pruned away any "candidates" + * that aren't actually coercion-compatible with the input types. + * + * This is also used for resolving ambiguous operator references. Formerly + * parse_oper.c had its own, essentially duplicate code for the purpose. + * The following comments (formerly in parse_oper.c) are kept to record some + * of the history of these heuristics. + * + * OLD COMMENTS: + * + * This routine is new code, replacing binary_oper_select_candidate() + * which dates from v4.2/v1.0.x days. It tries very hard to match up + * operators with types, including allowing type coercions if necessary. + * The important thing is that the code do as much as possible, + * while _never_ doing the wrong thing, where "the wrong thing" would + * be returning an operator when other better choices are available, + * or returning an operator which is a non-intuitive possibility. + * - thomas 1998-05-21 + * + * The comments below came from binary_oper_select_candidate(), and + * illustrate the issues and choices which are possible: + * - thomas 1998-05-20 + * + * current wisdom holds that the default operator should be one in which + * both operands have the same type (there will only be one such + * operator) + * + * 7.27.93 - I have decided not to do this; it's too hard to justify, and + * it's easy enough to typecast explicitly - avi + * [the rest of this routine was commented out since then - ay] + * + * 6/23/95 - I don't complete agree with avi. In particular, casting + * floats is a pain for users. Whatever the rationale behind not doing + * this is, I need the following special case to work. + * + * In the WHERE clause of a query, if a float is specified without + * quotes, we treat it as float8. I added the float48* operators so + * that we can operate on float4 and float8. But now we have more than + * one matching operator if the right arg is unknown (eg. float + * specified with quotes). This break some stuff in the regression + * test where there are floats in quotes not properly casted. Below is + * the solution. In addition to requiring the operator operates on the + * same type for both operands [as in the code Avi originally + * commented out], we also require that the operators be equivalent in + * some sense. (see equivalentOpersAfterPromotion for details.) + * - ay 6/95 */ -static FuncCandidateList +FuncCandidateList func_select_candidate(int nargs, Oid *input_typeids, FuncCandidateList candidates) @@ -419,59 +459,28 @@ func_select_candidate(int nargs, int ncandidates; int nbestMatch, nmatch; + Oid input_base_typeids[FUNC_MAX_ARGS]; CATEGORY slot_category[FUNC_MAX_ARGS], current_category; bool slot_has_preferred_type[FUNC_MAX_ARGS]; bool resolved_unknowns; /* - * Run through all candidates and keep those with the most matches on - * exact types. Keep all candidates if none match. + * If any input types are domains, reduce them to their base types. + * This ensures that we will consider functions on the base type to be + * "exact matches" in the exact-match heuristic; it also makes it possible + * to do something useful with the type-category heuristics. Note that + * this makes it difficult, but not impossible, to use functions declared + * to take a domain as an input datatype. Such a function will be + * selected over the base-type function only if it is an exact match at + * all argument positions, and so was already chosen by our caller. */ - ncandidates = 0; - nbestMatch = 0; - last_candidate = NULL; - for (current_candidate = candidates; - current_candidate != NULL; - current_candidate = current_candidate->next) - { - current_typeids = current_candidate->args; - nmatch = 0; - for (i = 0; i < nargs; i++) - { - if (input_typeids[i] != UNKNOWNOID && - current_typeids[i] == input_typeids[i]) - nmatch++; - } - - /* take this one as the best choice so far? */ - if ((nmatch > nbestMatch) || (last_candidate == NULL)) - { - nbestMatch = nmatch; - candidates = current_candidate; - last_candidate = current_candidate; - ncandidates = 1; - } - /* no worse than the last choice, so keep this one too? */ - else if (nmatch == nbestMatch) - { - last_candidate->next = current_candidate; - last_candidate = current_candidate; - ncandidates++; - } - /* otherwise, don't bother keeping this one... */ - } - - if (last_candidate) /* terminate rebuilt list */ - last_candidate->next = NULL; - - if (ncandidates == 1) - return candidates; + for (i = 0; i < nargs; i++) + input_base_typeids[i] = getBaseType(input_typeids[i]); /* - * Still too many candidates? Run through all candidates and keep - * those with the most matches on exact types + binary-compatible - * types. Keep all candidates if none match. + * Run through all candidates and keep those with the most matches on + * exact types. Keep all candidates if none match. */ ncandidates = 0; nbestMatch = 0; @@ -484,11 +493,9 @@ func_select_candidate(int nargs, nmatch = 0; for (i = 0; i < nargs; i++) { - if (input_typeids[i] != UNKNOWNOID) - { - if (IsBinaryCoercible(input_typeids[i], current_typeids[i])) - nmatch++; - } + if (input_base_typeids[i] != UNKNOWNOID && + current_typeids[i] == input_base_typeids[i]) + nmatch++; } /* take this one as the best choice so far? */ @@ -516,10 +523,14 @@ func_select_candidate(int nargs, return candidates; /* - * Still too many candidates? Now look for candidates which are - * preferred types at the args that will require coercion. Keep all - * candidates if none match. + * Still too many candidates? Now look for candidates which have either + * exact matches or preferred types at the args that will require coercion. + * (Restriction added in 7.4: preferred type must be of same category as + * input type; give no preference to cross-category conversions to + * preferred types.) Keep all candidates if none match. */ + for (i = 0; i < nargs; i++) /* avoid multiple lookups */ + slot_category[i] = TypeCategory(input_base_typeids[i]); ncandidates = 0; nbestMatch = 0; last_candidate = NULL; @@ -531,11 +542,10 @@ func_select_candidate(int nargs, nmatch = 0; for (i = 0; i < nargs; i++) { - if (input_typeids[i] != UNKNOWNOID) + if (input_base_typeids[i] != UNKNOWNOID) { - current_category = TypeCategory(current_typeids[i]); - if (current_typeids[i] == input_typeids[i] || - IsPreferredType(current_category, current_typeids[i])) + if (current_typeids[i] == input_base_typeids[i] || + IsPreferredType(slot_category[i], current_typeids[i])) nmatch++; } } @@ -565,6 +575,11 @@ func_select_candidate(int nargs, * Still too many candidates? Try assigning types for the unknown * columns. * + * NOTE: for a binary operator with one unknown and one non-unknown input, + * we already tried the heuristic of looking for a candidate with the + * known input type on both sides (see binary_oper_exact()). That's + * essentially a special case of the general algorithm we try next. + * * We do this by examining each unknown argument position to see if we * can determine a "type category" for it. If any candidate has an * input datatype of STRING category, use STRING category (this bias @@ -588,7 +603,7 @@ func_select_candidate(int nargs, { bool have_conflict; - if (input_typeids[i] != UNKNOWNOID) + if (input_base_typeids[i] != UNKNOWNOID) continue; resolved_unknowns = true; /* assume we can do it */ slot_category[i] = INVALID_TYPE; @@ -656,7 +671,7 @@ func_select_candidate(int nargs, current_typeids = current_candidate->args; for (i = 0; i < nargs; i++) { - if (input_typeids[i] != UNKNOWNOID) + if (input_base_typeids[i] != UNKNOWNOID) continue; current_type = current_typeids[i]; current_category = TypeCategory(current_type); @@ -694,7 +709,7 @@ func_select_candidate(int nargs, if (ncandidates == 1) return candidates; - return NULL; /* failed to determine a unique candidate */ + return NULL; /* failed to select a best candidate */ } /* func_select_candidate() */ @@ -734,16 +749,17 @@ func_get_detail(List *funcname, bool *retset, /* return value */ Oid **true_typeids) /* return value */ { - FuncCandidateList function_typeids; + FuncCandidateList raw_candidates; FuncCandidateList best_candidate; /* Get list of possible candidates from namespace search */ - function_typeids = FuncnameGetCandidates(funcname, nargs); + raw_candidates = FuncnameGetCandidates(funcname, nargs); /* - * See if there is an exact match + * Quickly check if there is an exact match to the input datatypes + * (there can be only one) */ - for (best_candidate = function_typeids; + for (best_candidate = raw_candidates; best_candidate != NULL; best_candidate = best_candidate->next) { @@ -815,7 +831,7 @@ func_get_detail(List *funcname, * didn't find an exact match, so now try to match up * candidates... */ - if (function_typeids != NULL) + if (raw_candidates != NULL) { Oid **input_typeid_vector = NULL; Oid *current_input_typeids; @@ -829,17 +845,18 @@ func_get_detail(List *funcname, do { - FuncCandidateList current_function_typeids; + FuncCandidateList current_candidates; int ncandidates; - ncandidates = match_argtypes(nargs, current_input_typeids, - function_typeids, - ¤t_function_typeids); + ncandidates = func_match_argtypes(nargs, + current_input_typeids, + raw_candidates, + ¤t_candidates); /* one match only? then run with it... */ if (ncandidates == 1) { - best_candidate = current_function_typeids; + best_candidate = current_candidates; break; } @@ -851,7 +868,7 @@ func_get_detail(List *funcname, { best_candidate = func_select_candidate(nargs, current_input_typeids, - current_function_typeids); + current_candidates); /* * If we were able to choose a best candidate, we're diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c index 6238258ed2f..21f73994c42 100644 --- a/src/backend/parser/parse_oper.c +++ b/src/backend/parser/parse_oper.c @@ -8,18 +8,13 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.63 2003/04/29 22:13:10 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.64 2003/05/26 00:11:27 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" -#include "access/genam.h" -#include "access/heapam.h" -#include "catalog/catname.h" -#include "catalog/indexing.h" -#include "catalog/namespace.h" #include "catalog/pg_operator.h" #include "parser/parse_coerce.h" #include "parser/parse_expr.h" @@ -289,107 +284,29 @@ binary_oper_exact(Oid arg1, Oid arg2, /* oper_select_candidate() - * Given the input argtype array and one or more candidates - * for the function argtype array, attempt to resolve the conflict. - * Returns the selected argtype array if the conflict can be resolved, - * otherwise returns NULL. + * Given the input argtype array and one or more candidates + * for the operator, attempt to resolve the conflict. * - * By design, this is pretty similar to func_select_candidate in parse_func.c. - * However, we can do a couple of extra things here because we know we can - * have no more than two args to deal with. Also, the calling convention - * is a little different: we must prune away "candidates" that aren't actually - * coercion-compatible with the input types, whereas in parse_func.c that - * gets done by match_argtypes before func_select_candidate is called. + * Returns the OID of the selected operator if the conflict can be resolved, + * otherwise returns InvalidOid. * - * This routine is new code, replacing binary_oper_select_candidate() - * which dates from v4.2/v1.0.x days. It tries very hard to match up - * operators with types, including allowing type coercions if necessary. - * The important thing is that the code do as much as possible, - * while _never_ doing the wrong thing, where "the wrong thing" would - * be returning an operator when other better choices are available, - * or returning an operator which is a non-intuitive possibility. - * - thomas 1998-05-21 - * - * The comments below came from binary_oper_select_candidate(), and - * illustrate the issues and choices which are possible: - * - thomas 1998-05-20 - * - * current wisdom holds that the default operator should be one in which - * both operands have the same type (there will only be one such - * operator) - * - * 7.27.93 - I have decided not to do this; it's too hard to justify, and - * it's easy enough to typecast explicitly - avi - * [the rest of this routine was commented out since then - ay] - * - * 6/23/95 - I don't complete agree with avi. In particular, casting - * floats is a pain for users. Whatever the rationale behind not doing - * this is, I need the following special case to work. - * - * In the WHERE clause of a query, if a float is specified without - * quotes, we treat it as float8. I added the float48* operators so - * that we can operate on float4 and float8. But now we have more than - * one matching operator if the right arg is unknown (eg. float - * specified with quotes). This break some stuff in the regression - * test where there are floats in quotes not properly casted. Below is - * the solution. In addition to requiring the operator operates on the - * same type for both operands [as in the code Avi originally - * commented out], we also require that the operators be equivalent in - * some sense. (see equivalentOpersAfterPromotion for details.) - * - ay 6/95 + * Note that the caller has already determined that there is no candidate + * exactly matching the input argtype(s). Incompatible candidates are not yet + * pruned away, however. */ static Oid oper_select_candidate(int nargs, Oid *input_typeids, FuncCandidateList candidates) { - FuncCandidateList current_candidate; - FuncCandidateList last_candidate; - Oid *current_typeids; - Oid current_type; - int unknownOids; - int i; int ncandidates; - int nbestMatch, - nmatch; - CATEGORY slot_category[FUNC_MAX_ARGS], - current_category; - bool slot_has_preferred_type[FUNC_MAX_ARGS]; - bool resolved_unknowns; /* - * First, delete any candidates that cannot actually accept the given - * input types, whether directly or by coercion. (Note that - * can_coerce_type will assume that UNKNOWN inputs are coercible to - * anything, so candidates will not be eliminated on that basis.) + * Delete any candidates that cannot actually accept the given + * input types, whether directly or by coercion. */ - ncandidates = 0; - last_candidate = NULL; - for (current_candidate = candidates; - current_candidate != NULL; - current_candidate = current_candidate->next) - { - if (can_coerce_type(nargs, input_typeids, current_candidate->args, - COERCION_IMPLICIT)) - { - if (last_candidate == NULL) - { - candidates = current_candidate; - last_candidate = current_candidate; - ncandidates = 1; - } - else - { - last_candidate->next = current_candidate; - last_candidate = current_candidate; - ncandidates++; - } - } - /* otherwise, don't bother keeping this one... */ - } - - if (last_candidate) /* terminate rebuilt list */ - last_candidate->next = NULL; + ncandidates = func_match_argtypes(nargs, input_typeids, + candidates, &candidates); /* Done if no candidate or only one candidate survives */ if (ncandidates == 0) @@ -398,317 +315,15 @@ oper_select_candidate(int nargs, return candidates->oid; /* - * Run through all candidates and keep those with the most matches on - * exact types. Keep all candidates if none match. - */ - ncandidates = 0; - nbestMatch = 0; - last_candidate = NULL; - for (current_candidate = candidates; - current_candidate != NULL; - current_candidate = current_candidate->next) - { - current_typeids = current_candidate->args; - nmatch = 0; - for (i = 0; i < nargs; i++) - { - if (input_typeids[i] != UNKNOWNOID && - current_typeids[i] == input_typeids[i]) - nmatch++; - } - - /* take this one as the best choice so far? */ - if ((nmatch > nbestMatch) || (last_candidate == NULL)) - { - nbestMatch = nmatch; - candidates = current_candidate; - last_candidate = current_candidate; - ncandidates = 1; - } - /* no worse than the last choice, so keep this one too? */ - else if (nmatch == nbestMatch) - { - last_candidate->next = current_candidate; - last_candidate = current_candidate; - ncandidates++; - } - /* otherwise, don't bother keeping this one... */ - } - - if (last_candidate) /* terminate rebuilt list */ - last_candidate->next = NULL; - - if (ncandidates == 1) - return candidates->oid; - - /* - * Still too many candidates? Run through all candidates and keep - * those with the most matches on exact types + binary-compatible - * types. Keep all candidates if none match. - */ - ncandidates = 0; - nbestMatch = 0; - last_candidate = NULL; - for (current_candidate = candidates; - current_candidate != NULL; - current_candidate = current_candidate->next) - { - current_typeids = current_candidate->args; - nmatch = 0; - for (i = 0; i < nargs; i++) - { - if (input_typeids[i] != UNKNOWNOID) - { - if (IsBinaryCoercible(input_typeids[i], current_typeids[i])) - nmatch++; - } - } - - /* take this one as the best choice so far? */ - if ((nmatch > nbestMatch) || (last_candidate == NULL)) - { - nbestMatch = nmatch; - candidates = current_candidate; - last_candidate = current_candidate; - ncandidates = 1; - } - /* no worse than the last choice, so keep this one too? */ - else if (nmatch == nbestMatch) - { - last_candidate->next = current_candidate; - last_candidate = current_candidate; - ncandidates++; - } - /* otherwise, don't bother keeping this one... */ - } - - if (last_candidate) /* terminate rebuilt list */ - last_candidate->next = NULL; - - if (ncandidates == 1) - return candidates->oid; - - /* - * Still too many candidates? Now look for candidates which are - * preferred types at the args that will require coercion. Keep all - * candidates if none match. - */ - ncandidates = 0; - nbestMatch = 0; - last_candidate = NULL; - for (current_candidate = candidates; - current_candidate != NULL; - current_candidate = current_candidate->next) - { - current_typeids = current_candidate->args; - nmatch = 0; - for (i = 0; i < nargs; i++) - { - if (input_typeids[i] != UNKNOWNOID) - { - current_category = TypeCategory(current_typeids[i]); - if (current_typeids[i] == input_typeids[i] || - IsPreferredType(current_category, current_typeids[i])) - nmatch++; - } - } - - if ((nmatch > nbestMatch) || (last_candidate == NULL)) - { - nbestMatch = nmatch; - candidates = current_candidate; - last_candidate = current_candidate; - ncandidates = 1; - } - else if (nmatch == nbestMatch) - { - last_candidate->next = current_candidate; - last_candidate = current_candidate; - ncandidates++; - } - } - - if (last_candidate) /* terminate rebuilt list */ - last_candidate->next = NULL; - - if (ncandidates == 1) - return candidates->oid; - - /* - * Still too many candidates? Try assigning types for the unknown - * columns. - * - * First try: if we have an unknown and a non-unknown input, see whether - * there is a candidate all of whose input types are the same as the - * known input type (there can be at most one such candidate). If so, - * use that candidate. NOTE that this is cool only because operators - * can't have more than 2 args, so taking the last non-unknown as - * current_type can yield only one possibility if there is also an - * unknown. - */ - unknownOids = FALSE; - current_type = UNKNOWNOID; - for (i = 0; i < nargs; i++) - { - if ((input_typeids[i] != UNKNOWNOID) - && (input_typeids[i] != InvalidOid)) - current_type = input_typeids[i]; - else - unknownOids = TRUE; - } - - if (unknownOids && (current_type != UNKNOWNOID)) - { - for (current_candidate = candidates; - current_candidate != NULL; - current_candidate = current_candidate->next) - { - current_typeids = current_candidate->args; - nmatch = 0; - for (i = 0; i < nargs; i++) - { - if (current_type == current_typeids[i]) - nmatch++; - } - if (nmatch == nargs) - return current_candidate->oid; - } - } - - /* - * Second try: same algorithm as for unknown resolution in - * parse_func.c. - * - * We do this by examining each unknown argument position to see if we - * can determine a "type category" for it. If any candidate has an - * input datatype of STRING category, use STRING category (this bias - * towards STRING is appropriate since unknown-type literals look like - * strings). Otherwise, if all the candidates agree on the type - * category of this argument position, use that category. Otherwise, - * fail because we cannot determine a category. - * - * If we are able to determine a type category, also notice whether any - * of the candidates takes a preferred datatype within the category. - * - * Having completed this examination, remove candidates that accept the - * wrong category at any unknown position. Also, if at least one - * candidate accepted a preferred type at a position, remove - * candidates that accept non-preferred types. - * - * If we are down to one candidate at the end, we win. + * Use the same heuristics as for ambiguous functions to resolve + * the conflict. */ - resolved_unknowns = false; - for (i = 0; i < nargs; i++) - { - bool have_conflict; - - if (input_typeids[i] != UNKNOWNOID) - continue; - resolved_unknowns = true; /* assume we can do it */ - slot_category[i] = INVALID_TYPE; - slot_has_preferred_type[i] = false; - have_conflict = false; - for (current_candidate = candidates; - current_candidate != NULL; - current_candidate = current_candidate->next) - { - current_typeids = current_candidate->args; - current_type = current_typeids[i]; - current_category = TypeCategory(current_type); - if (slot_category[i] == INVALID_TYPE) - { - /* first candidate */ - slot_category[i] = current_category; - slot_has_preferred_type[i] = - IsPreferredType(current_category, current_type); - } - else if (current_category == slot_category[i]) - { - /* more candidates in same category */ - slot_has_preferred_type[i] |= - IsPreferredType(current_category, current_type); - } - else - { - /* category conflict! */ - if (current_category == STRING_TYPE) - { - /* STRING always wins if available */ - slot_category[i] = current_category; - slot_has_preferred_type[i] = - IsPreferredType(current_category, current_type); - } - else - { - /* - * Remember conflict, but keep going (might find - * STRING) - */ - have_conflict = true; - } - } - } - if (have_conflict && slot_category[i] != STRING_TYPE) - { - /* Failed to resolve category conflict at this position */ - resolved_unknowns = false; - break; - } - } + candidates = func_select_candidate(nargs, input_typeids, candidates); - if (resolved_unknowns) - { - /* Strip non-matching candidates */ - ncandidates = 0; - last_candidate = NULL; - for (current_candidate = candidates; - current_candidate != NULL; - current_candidate = current_candidate->next) - { - bool keepit = true; - - current_typeids = current_candidate->args; - for (i = 0; i < nargs; i++) - { - if (input_typeids[i] != UNKNOWNOID) - continue; - current_type = current_typeids[i]; - current_category = TypeCategory(current_type); - if (current_category != slot_category[i]) - { - keepit = false; - break; - } - if (slot_has_preferred_type[i] && - !IsPreferredType(current_category, current_type)) - { - keepit = false; - break; - } - } - if (keepit) - { - /* keep this candidate */ - last_candidate = current_candidate; - ncandidates++; - } - else - { - /* forget this candidate */ - if (last_candidate) - last_candidate->next = current_candidate->next; - else - candidates = current_candidate->next; - } - } - if (last_candidate) /* terminate rebuilt list */ - last_candidate->next = NULL; - } - - if (ncandidates == 1) + if (candidates) return candidates->oid; - return InvalidOid; /* failed to determine a unique candidate */ + return InvalidOid; /* failed to select a best candidate */ } /* oper_select_candidate() */ @@ -751,7 +366,7 @@ oper(List *opname, Oid ltypeId, Oid rtypeId, bool noError) /* * Unspecified type for one of the arguments? then use the - * other + * other (XXX this is probably dead code?) */ if (rtypeId == InvalidOid) rtypeId = ltypeId; diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 5ff4b1931da..77ef33b8783 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.137 2003/05/15 15:50:18 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.138 2003/05/26 00:11:27 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -77,9 +77,11 @@ #include <math.h> #include "access/heapam.h" +#include "access/nbtree.h" #include "access/tuptoaster.h" #include "catalog/catname.h" #include "catalog/pg_namespace.h" +#include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" #include "catalog/pg_proc.h" #include "catalog/pg_statistic.h" @@ -177,10 +179,9 @@ static bool get_restriction_var(List *args, int varRelid, Var **var, Node **other, bool *varonleft); static void get_join_vars(List *args, Var **var1, Var **var2); -static Selectivity prefix_selectivity(Query *root, Var *var, Oid vartype, - Const *prefix); +static Selectivity prefix_selectivity(Query *root, Var *var, + Oid opclass, Const *prefix); static Selectivity pattern_selectivity(Const *patt, Pattern_Type ptype); -static Oid find_operator(const char *opname, Oid datatype); static Datum string_to_datum(const char *str, Oid datatype); static Const *string_to_const(const char *str, Oid datatype); @@ -837,6 +838,7 @@ patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype) Datum constval; Oid consttype; Oid vartype; + Oid opclass; Pattern_Prefix_Status pstatus; Const *patt = NULL; Const *prefix = NULL; @@ -884,21 +886,77 @@ patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype) if (vartype != consttype) vartype = getBaseType(vartype); + /* + * We should now be able to recognize the var's datatype. Choose the + * index opclass from which we must draw the comparison operators. + * + * NOTE: It would be more correct to use the PATTERN opclasses than + * the simple ones, but at the moment ANALYZE will not generate statistics + * for the PATTERN operators. But our results are so approximate anyway + * that it probably hardly matters. + */ + switch (vartype) + { + case TEXTOID: + opclass = TEXT_BTREE_OPS_OID; + break; + case VARCHAROID: + opclass = VARCHAR_BTREE_OPS_OID; + break; + case BPCHAROID: + opclass = BPCHAR_BTREE_OPS_OID; + break; + case NAMEOID: + opclass = NAME_BTREE_OPS_OID; + break; + case BYTEAOID: + opclass = BYTEA_BTREE_OPS_OID; + break; + default: + return DEFAULT_MATCH_SEL; + } + /* divide pattern into fixed prefix and remainder */ patt = (Const *) other; pstatus = pattern_fixed_prefix(patt, ptype, &prefix, &rest); + /* + * If necessary, coerce the prefix constant to the right type. + * (The "rest" constant need not be changed.) + */ + if (prefix && prefix->consttype != vartype) + { + char *prefixstr; + + switch (prefix->consttype) + { + case TEXTOID: + prefixstr = DatumGetCString(DirectFunctionCall1(textout, + prefix->constvalue)); + break; + case BYTEAOID: + prefixstr = DatumGetCString(DirectFunctionCall1(byteaout, + prefix->constvalue)); + break; + default: + elog(ERROR, "patternsel: unexpected consttype %u", + prefix->consttype); + return DEFAULT_MATCH_SEL; + } + prefix = string_to_const(prefixstr, vartype); + pfree(prefixstr); + } + if (pstatus == Pattern_Prefix_Exact) { /* * Pattern specifies an exact match, so pretend operator is '=' */ - Oid eqopr = find_operator("=", vartype); + Oid eqopr = get_opclass_member(opclass, BTEqualStrategyNumber); List *eqargs; if (eqopr == InvalidOid) - elog(ERROR, "patternsel: no = operator for type %u", - vartype); + elog(ERROR, "patternsel: no = operator for opclass %u", opclass); eqargs = makeList2(var, prefix); result = DatumGetFloat8(DirectFunctionCall4(eqsel, PointerGetDatum(root), @@ -918,7 +976,7 @@ patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype) Selectivity selec; if (pstatus == Pattern_Prefix_Partial) - prefixsel = prefix_selectivity(root, var, vartype, prefix); + prefixsel = prefix_selectivity(root, var, opclass, prefix); else prefixsel = 1.0; restsel = pattern_selectivity(rest, ptype); @@ -3020,10 +3078,13 @@ get_join_vars(List *args, Var **var1, Var **var2) /* * Extract the fixed prefix, if any, for a pattern. - * *prefix is set to a palloc'd prefix string, - * or to NULL if no fixed prefix exists for the pattern. - * *rest is set to point to the remainder of the pattern after the - * portion describing the fixed prefix. + * + * *prefix is set to a palloc'd prefix string (in the form of a Const node), + * or to NULL if no fixed prefix exists for the pattern. + * *rest is set to a palloc'd Const representing the remainder of the pattern + * after the portion describing the fixed prefix. + * Each of these has the same type (TEXT or BYTEA) as the given pattern Const. + * * The return value distinguishes no fixed prefix, a partial prefix, * or an exact-match-only pattern. */ @@ -3035,7 +3096,6 @@ like_fixed_prefix(Const *patt_const, bool case_insensitive, char *match; char *patt; int pattlen; - char *prefix; char *rest; Oid typeid = patt_const->consttype; int pos, @@ -3058,7 +3118,7 @@ like_fixed_prefix(Const *patt_const, bool case_insensitive, pattlen = toast_raw_datum_size(patt_const->constvalue) - VARHDRSZ; } - prefix = match = palloc(pattlen + 1); + match = palloc(pattlen + 1); match_pos = 0; for (pos = 0; pos < pattlen; pos++) @@ -3093,12 +3153,11 @@ like_fixed_prefix(Const *patt_const, bool case_insensitive, match[match_pos] = '\0'; rest = &patt[pos]; - *prefix_const = string_to_const(prefix, typeid); + *prefix_const = string_to_const(match, typeid); *rest_const = string_to_const(rest, typeid); pfree(patt); pfree(match); - prefix = NULL; /* in LIKE, an empty pattern is an exact match! */ if (pos == pattlen) @@ -3120,7 +3179,6 @@ regex_fixed_prefix(Const *patt_const, bool case_insensitive, match_pos, paren_depth; char *patt; - char *prefix; char *rest; Oid typeid = patt_const->consttype; @@ -3176,7 +3234,7 @@ regex_fixed_prefix(Const *patt_const, bool case_insensitive, } /* OK, allocate space for pattern */ - prefix = match = palloc(strlen(patt) + 1); + match = palloc(strlen(patt) + 1); match_pos = 0; /* note start at pos 1 to skip leading ^ */ @@ -3231,18 +3289,20 @@ regex_fixed_prefix(Const *patt_const, bool case_insensitive, { rest = &patt[pos + 1]; - *prefix_const = string_to_const(prefix, typeid); + *prefix_const = string_to_const(match, typeid); *rest_const = string_to_const(rest, typeid); + pfree(patt); + pfree(match); + return Pattern_Prefix_Exact; /* pattern specifies exact match */ } - *prefix_const = string_to_const(prefix, typeid); + *prefix_const = string_to_const(match, typeid); *rest_const = string_to_const(rest, typeid); pfree(patt); pfree(match); - prefix = NULL; if (match_pos > 0) return Pattern_Prefix_Partial; @@ -3284,10 +3344,8 @@ pattern_fixed_prefix(Const *patt, Pattern_Type ptype, * A fixed prefix "foo" is estimated as the selectivity of the expression * "var >= 'foo' AND var < 'fop'" (see also indxqual.c). * - * Because of constant-folding, we can assume that the prefixcon constant's - * type exactly matches the operator's declared input type; but it's not - * safe to make the same assumption for the Var, so the type to use for the - * Var must be passed in separately. + * We use the >= and < operators from the specified btree opclass to do the + * estimation. The given Var and Const must be of the associated datatype. * * XXX Note: we make use of the upper bound to estimate operator selectivity * even if the locale is such that we cannot rely on the upper-bound string. @@ -3295,27 +3353,17 @@ pattern_fixed_prefix(Const *patt, Pattern_Type ptype, * more useful to use the upper-bound code than not. */ static Selectivity -prefix_selectivity(Query *root, Var *var, Oid vartype, Const *prefixcon) +prefix_selectivity(Query *root, Var *var, Oid opclass, Const *prefixcon) { Selectivity prefixsel; Oid cmpopr; - char *prefix; List *cmpargs; Const *greaterstrcon; - cmpopr = find_operator(">=", vartype); + cmpopr = get_opclass_member(opclass, BTGreaterEqualStrategyNumber); if (cmpopr == InvalidOid) - elog(ERROR, "prefix_selectivity: no >= operator for type %u", - vartype); - if (prefixcon->consttype != BYTEAOID) - prefix = DatumGetCString(DirectFunctionCall1(textout, prefixcon->constvalue)); - else - prefix = DatumGetCString(DirectFunctionCall1(byteaout, prefixcon->constvalue)); - - /* If var is type NAME, must adjust type of comparison constant */ - if (vartype == NAMEOID) - prefixcon = string_to_const(prefix, NAMEOID); - + elog(ERROR, "prefix_selectivity: no >= operator for opclass %u", + opclass); cmpargs = makeList2(var, prefixcon); /* Assume scalargtsel is appropriate for all supported types */ prefixsel = DatumGetFloat8(DirectFunctionCall4(scalargtsel, @@ -3334,10 +3382,10 @@ prefix_selectivity(Query *root, Var *var, Oid vartype, Const *prefixcon) { Selectivity topsel; - cmpopr = find_operator("<", vartype); + cmpopr = get_opclass_member(opclass, BTLessStrategyNumber); if (cmpopr == InvalidOid) - elog(ERROR, "prefix_selectivity: no < operator for type %u", - vartype); + elog(ERROR, "prefix_selectivity: no < operator for opclass %u", + opclass); cmpargs = makeList2(var, greaterstrcon); /* Assume scalarltsel is appropriate for all supported types */ topsel = DatumGetFloat8(DirectFunctionCall4(scalarltsel, @@ -3702,18 +3750,6 @@ make_greater_string(const Const *str_const) return (Const *) NULL; } -/* 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 diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c index 77fb26a4807..5085c6025c2 100644 --- a/src/backend/utils/adt/varchar.c +++ b/src/backend/utils/adt/varchar.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.96 2003/05/12 23:08:50 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.97 2003/05/26 00:11:27 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -343,7 +343,10 @@ name_bpchar(PG_FUNCTION_ARGS) /***************************************************************************** - * varchar - varchar() * + * varchar - varchar(n) + * + * Note: varchar piggybacks on type text for most operations, and so has no + * C-coded functions except for I/O and typmod checking. *****************************************************************************/ /* @@ -700,7 +703,7 @@ bpcharcmp(PG_FUNCTION_ARGS) /* * bpchar needs a specialized hash function because we want to ignore - * trailing blanks in comparisons. (varchar can use plain hashvarlena.) + * trailing blanks in comparisons. */ Datum hashbpchar(PG_FUNCTION_ARGS) @@ -720,187 +723,3 @@ hashbpchar(PG_FUNCTION_ARGS) return result; } - - -/***************************************************************************** - * Functions used for varchar - *****************************************************************************/ - -Datum -varcharlen(PG_FUNCTION_ARGS) -{ - VarChar *arg = PG_GETARG_VARCHAR_P(0); - - /* optimization for single byte encoding */ - if (pg_database_encoding_max_length() <= 1) - PG_RETURN_INT32(VARSIZE(arg) - VARHDRSZ); - - PG_RETURN_INT32( - pg_mbstrlen_with_len(VARDATA(arg), VARSIZE(arg) - VARHDRSZ) - ); -} - -Datum -varcharoctetlen(PG_FUNCTION_ARGS) -{ - VarChar *arg = PG_GETARG_VARCHAR_P(0); - - PG_RETURN_INT32(VARSIZE(arg) - VARHDRSZ); -} - - -/***************************************************************************** - * Comparison Functions used for varchar - * - * Note: btree indexes need these routines not to leak memory; therefore, - * be careful to free working copies of toasted datums. Most places don't - * need to be so careful. - *****************************************************************************/ - -Datum -varchareq(PG_FUNCTION_ARGS) -{ - VarChar *arg1 = PG_GETARG_VARCHAR_P(0); - VarChar *arg2 = PG_GETARG_VARCHAR_P(1); - int len1, - len2; - bool result; - - len1 = VARSIZE(arg1) - VARHDRSZ; - len2 = VARSIZE(arg2) - VARHDRSZ; - - /* fast path for different-length inputs */ - if (len1 != len2) - result = false; - else - result = (varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2) == 0); - - PG_FREE_IF_COPY(arg1, 0); - PG_FREE_IF_COPY(arg2, 1); - - PG_RETURN_BOOL(result); -} - -Datum -varcharne(PG_FUNCTION_ARGS) -{ - VarChar *arg1 = PG_GETARG_VARCHAR_P(0); - VarChar *arg2 = PG_GETARG_VARCHAR_P(1); - int len1, - len2; - bool result; - - len1 = VARSIZE(arg1) - VARHDRSZ; - len2 = VARSIZE(arg2) - VARHDRSZ; - - /* fast path for different-length inputs */ - if (len1 != len2) - result = true; - else - result = (varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2) != 0); - - PG_FREE_IF_COPY(arg1, 0); - PG_FREE_IF_COPY(arg2, 1); - - PG_RETURN_BOOL(result); -} - -Datum -varcharlt(PG_FUNCTION_ARGS) -{ - VarChar *arg1 = PG_GETARG_VARCHAR_P(0); - VarChar *arg2 = PG_GETARG_VARCHAR_P(1); - int len1, - len2; - int cmp; - - len1 = VARSIZE(arg1) - VARHDRSZ; - len2 = VARSIZE(arg2) - VARHDRSZ; - - cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2); - - PG_FREE_IF_COPY(arg1, 0); - PG_FREE_IF_COPY(arg2, 1); - - PG_RETURN_BOOL(cmp < 0); -} - -Datum -varcharle(PG_FUNCTION_ARGS) -{ - VarChar *arg1 = PG_GETARG_VARCHAR_P(0); - VarChar *arg2 = PG_GETARG_VARCHAR_P(1); - int len1, - len2; - int cmp; - - len1 = VARSIZE(arg1) - VARHDRSZ; - len2 = VARSIZE(arg2) - VARHDRSZ; - - cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2); - - PG_FREE_IF_COPY(arg1, 0); - PG_FREE_IF_COPY(arg2, 1); - - PG_RETURN_BOOL(cmp <= 0); -} - -Datum -varchargt(PG_FUNCTION_ARGS) -{ - VarChar *arg1 = PG_GETARG_VARCHAR_P(0); - VarChar *arg2 = PG_GETARG_VARCHAR_P(1); - int len1, - len2; - int cmp; - - len1 = VARSIZE(arg1) - VARHDRSZ; - len2 = VARSIZE(arg2) - VARHDRSZ; - - cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2); - - PG_FREE_IF_COPY(arg1, 0); - PG_FREE_IF_COPY(arg2, 1); - - PG_RETURN_BOOL(cmp > 0); -} - -Datum -varcharge(PG_FUNCTION_ARGS) -{ - VarChar *arg1 = PG_GETARG_VARCHAR_P(0); - VarChar *arg2 = PG_GETARG_VARCHAR_P(1); - int len1, - len2; - int cmp; - - len1 = VARSIZE(arg1) - VARHDRSZ; - len2 = VARSIZE(arg2) - VARHDRSZ; - - cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2); - - PG_FREE_IF_COPY(arg1, 0); - PG_FREE_IF_COPY(arg2, 1); - - PG_RETURN_BOOL(cmp >= 0); -} - -Datum -varcharcmp(PG_FUNCTION_ARGS) -{ - VarChar *arg1 = PG_GETARG_VARCHAR_P(0); - VarChar *arg2 = PG_GETARG_VARCHAR_P(1); - int len1, - len2; - int cmp; - - len1 = VARSIZE(arg1) - VARHDRSZ; - len2 = VARSIZE(arg2) - VARHDRSZ; - - cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2); - - PG_FREE_IF_COPY(arg1, 0); - PG_FREE_IF_COPY(arg2, 1); - - PG_RETURN_INT32(cmp); -} diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 19178cc5243..fcd9dc2f59b 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.94 2003/05/13 04:38:58 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.95 2003/05/26 00:11:27 tgl Exp $ * * NOTES * Eventually, the index information should go through here, too. @@ -80,6 +80,33 @@ op_requires_recheck(Oid opno, Oid opclass) return result; } +/* + * get_opclass_member + * Get the OID of the operator that implements the specified strategy + * for the specified opclass. + * + * Returns InvalidOid if there is no pg_amop entry for the given keys. + */ +Oid +get_opclass_member(Oid opclass, int16 strategy) +{ + HeapTuple tp; + Form_pg_amop amop_tup; + Oid result; + + tp = SearchSysCache(AMOPSTRATEGY, + ObjectIdGetDatum(opclass), + Int16GetDatum(strategy), + 0, 0); + if (!HeapTupleIsValid(tp)) + return InvalidOid; + amop_tup = (Form_pg_amop) GETSTRUCT(tp); + result = amop_tup->amopopr; + ReleaseSysCache(tp); + return result; +} + + /* ---------- ATTRIBUTE CACHES ---------- */ /* |