Cause CHAR(n) to TEXT or VARCHAR conversion to automatically strip trailing
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 26 May 2003 00:11:29 +0000 (00:11 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 26 May 2003 00:11:29 +0000 (00:11 +0000)
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).

36 files changed:
contrib/array/array_iterator.c
contrib/array/array_iterator.h
contrib/array/array_iterator.sql.in
doc/src/sgml/release.sgml
doc/src/sgml/typeconv.sgml
src/backend/optimizer/path/indxpath.c
src/backend/optimizer/util/pathnode.c
src/backend/parser/parse_coerce.c
src/backend/parser/parse_func.c
src/backend/parser/parse_oper.c
src/backend/utils/adt/selfuncs.c
src/backend/utils/adt/varchar.c
src/backend/utils/cache/lsyscache.c
src/include/catalog/catversion.h
src/include/catalog/pg_amop.h
src/include/catalog/pg_amproc.h
src/include/catalog/pg_cast.h
src/include/catalog/pg_opclass.h
src/include/catalog/pg_operator.h
src/include/catalog/pg_proc.h
src/include/optimizer/paths.h
src/include/parser/parse_func.h
src/include/utils/builtins.h
src/include/utils/lsyscache.h
src/test/regress/expected/name.out
src/test/regress/expected/opr_sanity.out
src/test/regress/expected/rules.out
src/test/regress/expected/select_having.out
src/test/regress/expected/select_having_1.out
src/test/regress/expected/select_implicit.out
src/test/regress/expected/select_implicit_1.out
src/test/regress/expected/strings.out
src/test/regress/expected/union.out
src/test/regress/sql/opr_sanity.sql
src/test/regress/sql/strings.sql
src/test/regress/sql/union.sql

index d2a9a3271e632232c4e1e3c650b603248461100d..9504f7527795862893ea4f1321c3c52e15c0554c 100644 (file)
@@ -145,43 +145,6 @@ array_all_textregexeq(ArrayType *array, void *value)
                          array, (Datum) value);
 }
 
-/*
- * Iterator functions for type _varchar. Note that the regexp
- * operators take the second argument of type text.
- */
-
-int32
-array_varchareq(ArrayType *array, void *value)
-{
-   return array_iterator(F_VARCHAREQ,
-                         0,    /* logical or */
-                         array, (Datum) value);
-}
-
-int32
-array_all_varchareq(ArrayType *array, void *value)
-{
-   return array_iterator(F_VARCHAREQ,
-                         1,    /* logical and */
-                         array, (Datum) value);
-}
-
-int32
-array_varcharregexeq(ArrayType *array, void *value)
-{
-   return array_iterator(F_TEXTREGEXEQ,
-                         0,    /* logical or */
-                         array, (Datum) value);
-}
-
-int32
-array_all_varcharregexeq(ArrayType *array, void *value)
-{
-   return array_iterator(F_TEXTREGEXEQ,
-                         1,    /* logical and */
-                         array, (Datum) value);
-}
-
 /*
  * Iterator functions for type _bpchar. Note that the regexp
  * operators take the second argument of type text.
index c85d68f27ac60463a97b155520ef0b6a6d0cd453..75cfba07dd7596ac9415f9fb5a0c5ad91f163d28 100644 (file)
@@ -9,11 +9,6 @@ int32      array_all_texteq(ArrayType *array, void *value);
 int32      array_textregexeq(ArrayType *array, void *value);
 int32      array_all_textregexeq(ArrayType *array, void *value);
 
-int32      array_varchareq(ArrayType *array, void *value);
-int32      array_all_varchareq(ArrayType *array, void *value);
-int32      array_varcharregexeq(ArrayType *array, void *value);
-int32      array_all_varcharregexeq(ArrayType *array, void *value);
-
 int32      array_bpchareq(ArrayType *array, void *value);
 int32      array_all_bpchareq(ArrayType *array, void *value);
 int32      array_bpcharregexeq(ArrayType *array, void *value);
index 4108a63eafd524d70108159405db6e70cb0f2ea1..2d89f2c9872d2e2a0784131bf4d135725d0bca6a 100644 (file)
@@ -55,59 +55,6 @@ CREATE OPERATOR **~ (
 );
 
 
--- define the array operators *=, **=, *~ and **~ for type _varchar
---
--- NOTE: "varchar" is also a reserved word and must be quoted.
---
-CREATE OR REPLACE FUNCTION array_varchareq(_varchar, varchar)
-RETURNS bool
-AS 'MODULE_PATHNAME' 
-LANGUAGE 'C' IMMUTABLE STRICT;
-
-CREATE OR REPLACE FUNCTION array_all_varchareq(_varchar, varchar)
-RETURNS bool
-AS 'MODULE_PATHNAME' 
-LANGUAGE 'C' IMMUTABLE STRICT;
-
-CREATE OR REPLACE FUNCTION array_varcharregexeq(_varchar, varchar)
-RETURNS bool
-AS 'MODULE_PATHNAME' 
-LANGUAGE 'C' IMMUTABLE STRICT;
-
-CREATE OR REPLACE FUNCTION array_all_varcharregexeq(_varchar, varchar)
-RETURNS bool
-AS 'MODULE_PATHNAME' 
-LANGUAGE 'C' IMMUTABLE STRICT;
-
-DROP OPERATOR *=(_varchar,"varchar");
-CREATE OPERATOR *= (
-   LEFTARG=_varchar, 
-   RIGHTARG="varchar", 
-   PROCEDURE=array_varchareq
-);
-
-DROP OPERATOR **=(_varchar,"varchar");
-CREATE OPERATOR **= (
-   LEFTARG=_varchar,
-   RIGHTARG="varchar",
-   PROCEDURE=array_all_varchareq
-);
-
-DROP OPERATOR *~(_varchar,"varchar");
-CREATE OPERATOR *~ (
-   LEFTARG=_varchar,
-   RIGHTARG="varchar",
-   PROCEDURE=array_varcharregexeq
-);
-
-DROP OPERATOR **~(_varchar,"varchar");
-CREATE OPERATOR **~ (
-   LEFTARG=_varchar,
-   RIGHTARG="varchar",
-   PROCEDURE=array_all_varcharregexeq
-);
-
-
 -- define the array operators *=, **=, *~ and **~ for type _bpchar
 --
 CREATE OR REPLACE FUNCTION array_bpchareq(_bpchar, bpchar)
index ea6fe8a72174d09080b73501a20e4965e087ab10..3250595964411a173d730e0ce42c554bc0a9b3ef 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.189 2003/05/22 18:31:45 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.190 2003/05/26 00:11:27 tgl Exp $
 -->
 
 <appendix id="release">
@@ -24,6 +24,7 @@ CDATA means the content is "SGML-free", so you can write without
 worries about funny characters.
 -->
 <literallayout><![CDATA[
+CHAR(n) to TEXT conversion automatically strips trailing blanks
 Pattern matching operations can use indexes regardless of locale
 New frontend/backend protocol supports many long-requested features
 SET AUTOCOMMIT TO OFF is no longer supported
index 0a85ea1230f61596760ac0f78649991ab3517a19..fa59aba0fea747f24b00314c41a962bbc0849037 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/typeconv.sgml,v 1.30 2003/03/25 16:15:38 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/typeconv.sgml,v 1.31 2003/05/26 00:11:27 tgl Exp $
 -->
 
 <chapter Id="typeconv">
@@ -45,7 +45,7 @@ mixed-type expressions to be meaningful even with user-defined types.
 <para>
 The <productname>PostgreSQL</productname> scanner/parser decodes lexical
 elements into only five fundamental categories: integers, floating-point numbers, strings,
-names, and key words.  Most extended types are first classified as
+names, and key words.  Constants of most non-numeric types are first classified as
 strings. The <acronym>SQL</acronym> language definition allows specifying type
 names with strings, and this mechanism can be used in
 <productname>PostgreSQL</productname> to start the parser down the correct
@@ -134,8 +134,8 @@ The system catalogs store information about which conversions, called
 perform those conversions.  Additional casts can be added by the user
 with the <command>CREATE CAST</command> command.  (This is usually
 done in conjunction with defining new data types.  The set of casts
-between the built-in types has been carefully crafted and should not
-be altered.)
+between the built-in types has been carefully crafted and is best not
+altered.)
 </para>
 
 <para>
@@ -144,8 +144,8 @@ at proper behavior for <acronym>SQL</acronym> standard types. There are
 several basic <firstterm>type categories</firstterm> defined: <type>boolean</type>,
 <type>numeric</type>, <type>string</type>, <type>bitstring</type>, <type>datetime</type>, <type>timespan</type>, <type>geometric</type>, <type>network</type>,
 and user-defined. Each category, with the exception of user-defined, has
-a <firstterm>preferred type</firstterm> which is preferentially selected
-when there is ambiguity.
+one or more <firstterm>preferred types</firstterm> which are preferentially
+selected when there is ambiguity.
 In the user-defined category, each type is its own preferred type.
 Ambiguous expressions (those with multiple candidate parsing solutions)
 can therefore often be resolved when there are multiple possible built-in types, but
@@ -175,7 +175,8 @@ be converted to a user-defined type (of course, only if conversion is necessary)
 <para>
 User-defined types are not related. Currently, <productname>PostgreSQL</productname>
 does not have information available to it on relationships between types, other than
-hardcoded heuristics for built-in types and implicit relationships based on available functions.
+hardcoded heuristics for built-in types and implicit relationships based on
+available functions and casts.
 </para>
 </listitem>
 
@@ -203,14 +204,15 @@ should use this new function and will no longer do the implicit conversion using
 <title>Operators</title>
 
   <para>
-   The operand types of an operator invocation are resolved following
+   The specific operator to be used in an operator invocation is determined
+   by following
    the procedure below.  Note that this procedure is indirectly affected
    by the precedence of the involved operators.  See <xref
    linkend="sql-precedence"> for more information.
   </para>
 
 <procedure>
-<title>Operand Type Resolution</title>
+<title>Operator Type Resolution</title>
 
 <step performance="required">
 <para>
@@ -271,22 +273,16 @@ candidate remains, use it; else continue to the next step.
 <step performance="required">
 <para>
 Run through all candidates and keep those with the most exact matches
-on input types.  Keep all candidates if none have any exact matches.
+on input types.  (Domain types are considered the same as their base type
+for this purpose.)  Keep all candidates if none have any exact matches.
 If only one candidate remains, use it; else continue to the next step.
 </para>
 </step>
 <step performance="required">
 <para>
-Run through all candidates and keep those with the most exact or
-binary-compatible matches on input types.  Keep all candidates if none have
-any exact or binary-compatible matches.
-If only one candidate remains, use it; else continue to the next step.
-</para>
-</step>
-<step performance="required">
-<para>
-Run through all candidates and keep those that accept preferred types at
-the most positions where type conversion will be required.
+Run through all candidates and keep those that accept preferred types (of the
+input datatype's type category) at the most positions where type conversion
+will be required.
 Keep all candidates if none accept preferred types.
 If only one candidate remains, use it; else continue to the next step.
 </para>
@@ -295,12 +291,13 @@ If only one candidate remains, use it; else continue to the next step.
 <para>
 If any input arguments are <type>unknown</type>, check the type
 categories accepted at those argument positions by the remaining
-candidates.  At each position, select the <literal>string</literal> category if any
+candidates.  At each position, select the <type>string</type> category
+if any
 candidate accepts that category.  (This bias towards string is appropriate
 since an unknown-type literal does look like a string.) Otherwise, if
 all the remaining candidates accept the same type category, select that
 category; otherwise fail because the correct choice cannot be deduced
-without more clues.  Now discard operator
+without more clues.  Now discard
 candidates that do not accept the selected type category.  Furthermore,
 if any candidate accepts a preferred type at a given argument position,
 discard candidates that accept non-preferred types for that argument.
@@ -455,12 +452,12 @@ SELECT CAST('20' AS int8) ! AS "factorial";
 <title>Functions</title>
 
   <para>
-   The argument types of function calls are resolved according to the
-   following steps.
+   The specific function to be used in a function invocation is determined
+   according to the following steps.
   </para>
 
 <procedure>
-<title>Function Argument Type Resolution</title>
+<title>Function Type Resolution</title>
 
 <step performance="required">
 <para>
@@ -523,29 +520,24 @@ candidate remains, use it; else continue to the next step.
 <step performance="required">
 <para>
 Run through all candidates and keep those with the most exact matches
-on input types.  Keep all candidates if none have any exact matches.
-If only one candidate remains, use it; else continue to the next step.
-</para>
-</step>
-<step performance="required">
-<para>
-Run through all candidates and keep those with the most exact or
-binary-compatible matches on input types.  Keep all candidates if none have
-any exact or binary-compatible matches.
+on input types.  (Domain types are considered the same as their base type
+for this purpose.)  Keep all candidates if none have any exact matches.
 If only one candidate remains, use it; else continue to the next step.
 </para>
 </step>
 <step performance="required">
 <para>
-Run through all candidates and keep those that accept preferred types at
-the most positions where type conversion will be required.
+Run through all candidates and keep those that accept preferred types (of the
+input datatype's type category) at the most positions where type conversion
+will be required.
 Keep all candidates if none accept preferred types.
 If only one candidate remains, use it; else continue to the next step.
 </para>
 </step>
 <step performance="required">
 <para>
-If any input arguments are <type>unknown</type>, check the type categories accepted
+If any input arguments are <type>unknown</type>, check the type categories
+accepted 
 at those argument positions by the remaining candidates.  At each position,
 select the <type>string</type> category if any candidate accepts that category.
 (This bias towards string
@@ -553,8 +545,8 @@ is appropriate since an unknown-type literal does look like a string.)
 Otherwise, if all the remaining candidates accept the same type category,
 select that category; otherwise fail because
 the correct choice cannot be deduced without more clues.
-Now discard candidates that do not accept the selected type category;
-furthermore, if any candidate accepts a preferred type at a given argument
+Now discard candidates that do not accept the selected type category.
+Furthermore, if any candidate accepts a preferred type at a given argument
 position, discard candidates that accept non-preferred types for that
 argument.
 </para>
@@ -571,6 +563,8 @@ then fail.
 </procedure>
 
 <para>
+Note that the <quote>best match</> rules are identical for operator and
+function type resolution.
 Some examples follow.
 </para>
 
@@ -649,7 +643,8 @@ SELECT substr(CAST (varchar '1234' AS text), 3);
 <para>
 <note>
 <para>
-The parser is aware that <type>text</type> and <type>varchar</type>
+The parser learns from the <structname>pg_cast</> catalog that
+<type>text</type> and <type>varchar</type>
 are binary-compatible, meaning that one can be passed to a function that
 accepts the other without doing any physical conversion.  Therefore, no
 explicit type conversion call is really inserted in this case.
index b4da4666f1fcd5238e5fdeb708f193412a9aedbc..4529c30e626acacce405612fda52eccde6676c03 100644 (file)
@@ -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
index 3984c666f51242e2a6c6a3fe932c35fbe7db2c9a..25648beed19dd465be50a76610ffe3e5e02bd070 100644 (file)
@@ -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,
index 9dc0c7f1c19ba4f0b66a952676e09ed567dd0dde..fc35c2d6d41579171680dd0bbad6f62749154662 100644 (file)
@@ -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;
 
index 058b9aad73dc8524b162955dfcf601a3b8031ddb..e9a40e03179b31c258918c6ddcce7dee3fcac3b6 100644 (file)
@@ -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,
-                                            &current_function_typeids);
+               ncandidates = func_match_argtypes(nargs,
+                                                 current_input_typeids,
+                                                 raw_candidates,
+                                                 &current_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
index 6238258ed2f6448eea2e8dfa84e32b205460d541..21f73994c42b19965f3e959c70e989d65559a8fa 100644 (file)
@@ -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;
index 5ff4b1931da9616e141caacf4843b0a626ef3701..77ef33b878358670ca47ae553158265a32358e5b 100644 (file)
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
 #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
index 77fb26a480793f5a11d604c7ab7453782aac0f77..5085c6025c2aef05112531a0db10ad45a0500e39 100644 (file)
@@ -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);
-}
index 19178cc5243f3cfa36a0d928a44a5a3b2d9a6b44..fcd9dc2f59bbd19528a88da6979f03b57d4d81f1 100644 (file)
@@ -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 ----------                   */
 
 /*
index c7848af9f62f49cc22a1fca87daf8f4bf17c5860..2c28ffcabc55fefc4e1e1c7c18a072f907acbe59 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: catversion.h,v 1.195 2003/05/23 22:33:22 tgl Exp $
+ * $Id: catversion.h,v 1.196 2003/05/26 00:11:27 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 200305231
+#define CATALOG_VERSION_NO 200305241
 
 #endif
index b373ce15a223d77c3d5e7ec504d710bf16eba065..dbff38b3c392bc3fda7db669da54a32773979341 100644 (file)
@@ -16,7 +16,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_amop.h,v 1.48 2003/05/15 15:50:19 petere Exp $
+ * $Id: pg_amop.h,v 1.49 2003/05/26 00:11:27 tgl Exp $
  *
  * NOTES
  *  the genbki.sh script reads this file and generates .bki
@@ -219,14 +219,14 @@ DATA(insert (  426 4 f 1061 ));
 DATA(insert (   426 5 f 1060 ));
 
 /*
- * btree varchar_ops
+ * btree varchar_ops (same operators as text_ops)
  */
 
-DATA(insert (  2003 1 f 1066 ));
-DATA(insert (  2003 2 f 1067 ));
-DATA(insert (  2003 3 f 1062 ));
-DATA(insert (  2003 4 f 1069 ));
-DATA(insert (  2003 5 f 1068 ));
+DATA(insert (  2003 1 f 664 ));
+DATA(insert (  2003 2 f 665 ));
+DATA(insert (  2003 3 f  98 ));
+DATA(insert (  2003 4 f 667 ));
+DATA(insert (  2003 5 f 666 ));
 
 /*
  * btree bytea_ops
@@ -389,14 +389,14 @@ DATA(insert ( 2095 4 f 2317 ));
 DATA(insert (  2095 5 f 2318 ));
 
 /*
- * btree varchar pattern
+ * btree varchar pattern (same operators as text)
  */
 
-DATA(insert (  2096 1 f 2320 ));
-DATA(insert (  2096 2 f 2321 ));
-DATA(insert (  2096 3 f 2322 ));
-DATA(insert (  2096 4 f 2323 ));
-DATA(insert (  2096 5 f 2324 ));
+DATA(insert (  2096 1 f 2314 ));
+DATA(insert (  2096 2 f 2315 ));
+DATA(insert (  2096 3 f 2316 ));
+DATA(insert (  2096 4 f 2317 ));
+DATA(insert (  2096 5 f 2318 ));
 
 /*
  * btree bpchar pattern
@@ -462,7 +462,7 @@ DATA(insert (   1999 1 f 1320 ));
 /* timetz_ops */
 DATA(insert (  2001 1 f 1550 ));
 /* varchar_ops */
-DATA(insert (  2004 1 f 1062 ));
+DATA(insert (  2004 1 f   98 ));
 /* timestamp_ops */
 DATA(insert (  2040 1 f 2060 ));
 
index ba15ac9a6603ce33dc7527c7c334e2ea252bf87e..0048d000cdf90f18bd97c75234c9f5f50b4d50be 100644 (file)
@@ -14,7 +14,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_amproc.h,v 1.36 2003/05/15 15:50:19 petere Exp $
+ * $Id: pg_amproc.h,v 1.37 2003/05/26 00:11:27 tgl Exp $
  *
  * NOTES
  *   the genbki.sh script reads this file and generates .bki
@@ -103,10 +103,10 @@ DATA(insert ( 1996 1 1107 ));
 DATA(insert (  1998 1 1314 ));
 DATA(insert (  2000 1 1358 ));
 DATA(insert (  2002 1 1672 ));
-DATA(insert (  2003 1 1079 ));
-DATA(insert (  2039 1 1314 ));
+DATA(insert (  2003 1  360 ));
+DATA(insert (  2039 1 2045 ));
 DATA(insert (  2095 1 2166 ));
-DATA(insert (  2096 1 2173 ));
+DATA(insert (  2096 1 2166 ));
 DATA(insert (  2097 1 2180 ));
 DATA(insert (  2098 1 2187 ));
 
index ed6834c6683698e2b32fe674c104490734ffcc3d..6c3d47ba7f149a0a526756ca1a3be9dc4691914d 100644 (file)
@@ -7,7 +7,7 @@
  *
  * Copyright (c) 2002, PostgreSQL Global Development Group
  *
- * $Id: pg_cast.h,v 1.6 2003/05/14 18:08:15 tgl Exp $
+ * $Id: pg_cast.h,v 1.7 2003/05/26 00:11:27 tgl Exp $
  *
  * NOTES
  *   the genbki.sh script reads this file and generates .bki
@@ -161,8 +161,8 @@ DATA(insert ( 2206   23    0 a ));
  */
 DATA(insert (  25 1042    0 i ));
 DATA(insert (  25 1043    0 i ));
-DATA(insert ( 1042  25    0 i ));
-DATA(insert ( 1042 1043    0 i ));
+DATA(insert ( 1042  25  401 i ));
+DATA(insert ( 1042 1043  401 i ));
 DATA(insert ( 1043  25    0 i ));
 DATA(insert ( 1043 1042    0 i ));
 DATA(insert (  18   25  946 i ));
index 29c92f5ddfbdaf0775a147a4b9727e86dd6bac71..809cde3da02ec4052cf8a25340407b59269b3894 100644 (file)
@@ -9,7 +9,7 @@
  * of opclass name and index access method type.  This row specifies the
  * expected input data type for the opclass (the type of the heap column,
  * or the function output type in the case of a functional index). Note
- * that types binary-compatible with the specified type will be accepted too.
+ * that types binary-coercible to the specified type will be accepted too.
  *
  * For a given <opcamid, opcintype> pair, there can be at most one row that
  * has opcdefault = true; this row is the default opclass for such data in
@@ -26,7 +26,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_opclass.h,v 1.48 2003/05/15 15:50:19 petere Exp $
+ * $Id: pg_opclass.h,v 1.49 2003/05/26 00:11:27 tgl Exp $
  *
  * NOTES
  *   the genbki.sh script reads this file and generates .bki
@@ -92,11 +92,14 @@ DATA(insert OID =  423 (    403     bit_ops         PGNSP PGUID 1560 t 0 ));
 DATA(insert OID =  424 (   403     bool_ops        PGNSP PGUID   16 t 0 ));
 DATA(insert OID =  425 (   402     box_ops         PGNSP PGUID  603 t 0 ));
 DATA(insert OID =  426 (   403     bpchar_ops      PGNSP PGUID 1042 t 0 ));
+#define BPCHAR_BTREE_OPS_OID 426
 DATA(insert OID =  427 (   405     bpchar_ops      PGNSP PGUID 1042 t 0 ));
 DATA(insert OID =  428 (   403     bytea_ops       PGNSP PGUID   17 t 0 ));
+#define BYTEA_BTREE_OPS_OID 428
 DATA(insert OID =  429 (   403     char_ops        PGNSP PGUID   18 t 0 ));
 DATA(insert OID =  431 (   405     char_ops        PGNSP PGUID   18 t 0 ));
 DATA(insert OID =  432 (   403     cidr_ops        PGNSP PGUID  650 t 0 ));
+#define CIDR_BTREE_OPS_OID 432
 DATA(insert OID =  433 (   405     cidr_ops        PGNSP PGUID  650 t 0 ));
 DATA(insert OID =  434 (   403     date_ops        PGNSP PGUID 1082 t 0 ));
 DATA(insert OID =  435 (   405     date_ops        PGNSP PGUID 1082 t 0 ));
@@ -105,6 +108,7 @@ DATA(insert OID = 1971 (    405     float4_ops      PGNSP PGUID  700 t 0 ));
 DATA(insert OID = 1972 (   403     float8_ops      PGNSP PGUID  701 t 0 ));
 DATA(insert OID = 1973 (   405     float8_ops      PGNSP PGUID  701 t 0 ));
 DATA(insert OID = 1974 (   403     inet_ops        PGNSP PGUID  869 t 0 ));
+#define INET_BTREE_OPS_OID 1974
 DATA(insert OID = 1975 (   405     inet_ops        PGNSP PGUID  869 t 0 ));
 DATA(insert OID = 1976 (   403     int2_ops        PGNSP PGUID   21 t 0 ));
 #define INT2_BTREE_OPS_OID 1976
@@ -119,6 +123,7 @@ DATA(insert OID = 1983 (    405     interval_ops    PGNSP PGUID 1186 t 0 ));
 DATA(insert OID = 1984 (   403     macaddr_ops     PGNSP PGUID  829 t 0 ));
 DATA(insert OID = 1985 (   405     macaddr_ops     PGNSP PGUID  829 t 0 ));
 DATA(insert OID = 1986 (   403     name_ops        PGNSP PGUID   19 t 0 ));
+#define NAME_BTREE_OPS_OID 1986
 DATA(insert OID = 1987 (   405     name_ops        PGNSP PGUID   19 t 0 ));
 DATA(insert OID = 1988 (   403     numeric_ops     PGNSP PGUID 1700 t 0 ));
 DATA(insert OID = 1989 (   403     oid_ops         PGNSP PGUID   26 t 0 ));
@@ -128,6 +133,7 @@ DATA(insert OID = 1991 (    403     oidvector_ops   PGNSP PGUID   30 t 0 ));
 DATA(insert OID = 1992 (   405     oidvector_ops   PGNSP PGUID   30 t 0 ));
 DATA(insert OID = 1993 (   402     poly_ops        PGNSP PGUID  604 t 0 ));
 DATA(insert OID = 1994 (   403     text_ops        PGNSP PGUID   25 t 0 ));
+#define TEXT_BTREE_OPS_OID 1994
 DATA(insert OID = 1995 (   405     text_ops        PGNSP PGUID   25 t 0 ));
 DATA(insert OID = 1996 (   403     time_ops        PGNSP PGUID 1083 t 0 ));
 DATA(insert OID = 1997 (   405     time_ops        PGNSP PGUID 1083 t 0 ));
@@ -137,12 +143,17 @@ DATA(insert OID = 2000 (  403     timetz_ops      PGNSP PGUID 1266 t 0 ));
 DATA(insert OID = 2001 (   405     timetz_ops      PGNSP PGUID 1266 t 0 ));
 DATA(insert OID = 2002 (   403     varbit_ops      PGNSP PGUID 1562 t 0 ));
 DATA(insert OID = 2003 (   403     varchar_ops     PGNSP PGUID 1043 t 0 ));
+#define VARCHAR_BTREE_OPS_OID 2003
 DATA(insert OID = 2004 (   405     varchar_ops     PGNSP PGUID 1043 t 0 ));
 DATA(insert OID = 2039 (   403     timestamp_ops   PGNSP PGUID 1114 t 0 ));
 DATA(insert OID = 2040 (   405     timestamp_ops   PGNSP PGUID 1114 t 0 ));
 DATA(insert OID = 2095 (   403     text_pattern_ops    PGNSP PGUID   25 f 0 ));
+#define TEXT_PATTERN_BTREE_OPS_OID 2095
 DATA(insert OID = 2096 (   403     varchar_pattern_ops PGNSP PGUID 1043 f 0 ));
+#define VARCHAR_PATTERN_BTREE_OPS_OID 2096
 DATA(insert OID = 2097 (   403     bpchar_pattern_ops  PGNSP PGUID 1042 f 0 ));
+#define BPCHAR_PATTERN_BTREE_OPS_OID 2097
 DATA(insert OID = 2098 (   403     name_pattern_ops    PGNSP PGUID   19 f 0 ));
+#define NAME_PATTERN_BTREE_OPS_OID 2098
 
 #endif   /* PG_OPCLASS_H */
index 2fe0da5fbe9dbf6bd30c74347ddfea70929c337f..ae4fb6e04bb978bbad0b2f2ca5cd9ac6b100f268 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_operator.h,v 1.113 2003/05/15 15:50:19 petere Exp $
+ * $Id: pg_operator.h,v 1.114 2003/05/26 00:11:27 tgl Exp $
  *
  * NOTES
  *   the genbki.sh script reads this file and generates .bki
@@ -122,7 +122,9 @@ DATA(insert OID = 374 (  "||"      PGNSP PGUID b f 2283 2277 2277   0 0 0 0 0 0 ar
 DATA(insert OID = 375 (  "||"     PGNSP PGUID b f 2277 2277 2277   0 0 0 0 0 0 array_cat      -       -     ));
 
 DATA(insert OID = 352 (  "="      PGNSP PGUID b t  28  28  16 352   0   0   0   0   0 xideq eqsel eqjoinsel ));
-DATA(insert OID = 353 (  "="      PGNSP PGUID b t  28  23  16   0   0   0   0   0   0 xideq eqsel eqjoinsel ));
+DATA(insert OID = 353 (  "="      PGNSP PGUID b t  28  23  16   0   0   0   0   0   0 xideqint4 eqsel eqjoinsel ));
+DATA(insert OID = 385 (  "="      PGNSP PGUID b t  29  29  16 385   0   0   0   0   0 cideq eqsel eqjoinsel ));
+DATA(insert OID = 386 (  "="      PGNSP PGUID b t  22  22  16 386   0   0   0   0   0 int2vectoreq eqsel eqjoinsel ));
 DATA(insert OID = 387 (  "="      PGNSP PGUID b t  27  27  16 387   0   0   0   0   0 tideq eqsel eqjoinsel ));
 #define TIDEqualOperator   387
 DATA(insert OID = 388 (  "!"      PGNSP PGUID r f  20   0  20   0   0   0   0   0   0 int8fac - - ));
@@ -170,7 +172,7 @@ DATA(insert OID = 506 (  ">^"      PGNSP PGUID b f 600 600  16   0   0   0   0   0   0 po
 DATA(insert OID = 507 (  "<<"     PGNSP PGUID b f 600 600  16   0   0   0   0   0   0 point_left positionsel positionjoinsel ));
 DATA(insert OID = 508 (  ">>"     PGNSP PGUID b f 600 600  16   0   0   0   0   0   0 point_right positionsel positionjoinsel ));
 DATA(insert OID = 509 (  "<^"     PGNSP PGUID b f 600 600  16   0   0   0   0   0   0 point_below positionsel positionjoinsel ));
-DATA(insert OID = 510 (  "~="     PGNSP PGUID b f 600 600  16 510   0   0   0   0   0 point_eq eqsel eqjoinsel ));
+DATA(insert OID = 510 (  "~="     PGNSP PGUID b f 600 600  16 510 713   0   0   0   0 point_eq eqsel eqjoinsel ));
 DATA(insert OID = 511 (  "@"      PGNSP PGUID b f 600 603  16   0   0   0   0   0   0 on_pb - - ));
 DATA(insert OID = 512 (  "@"      PGNSP PGUID b f 600 602  16 755   0   0   0   0   0 on_ppath - - ));
 DATA(insert OID = 513 (  "@@"     PGNSP PGUID l f   0 603 600   0   0   0   0   0   0 box_center - - ));
@@ -350,6 +352,8 @@ DATA(insert OID = 708 (  "<->"     PGNSP PGUID b f 628 628 701 708   0  0  0   0
 DATA(insert OID = 709 (  "<->"    PGNSP PGUID b f 601 601 701 709   0  0  0   0   0 lseg_distance - - ));
 DATA(insert OID = 712 (  "<->"    PGNSP PGUID b f 604 604 701 712   0  0  0   0   0 poly_distance - - ));
 
+DATA(insert OID = 713 (  "<>"     PGNSP PGUID b f 600 600  16 713 510  0  0   0   0 point_ne neqsel neqjoinsel ));
+
 /* add translation/rotation/scaling operators for geometric types. - thomas 97/05/10 */
 DATA(insert OID = 731 (  "+"      PGNSP PGUID b f  600  600    600  731  0 0 0 0 0 point_add - - ));
 DATA(insert OID = 732 (  "-"      PGNSP PGUID b f  600  600    600    0  0 0 0 0 0 point_sub - - ));
@@ -427,29 +431,16 @@ DATA(insert OID =  969 (  "@@"       PGNSP PGUID l f  0  601  600    0  0 0 0 0 0 lse
 DATA(insert OID =  970 (  "@@"    PGNSP PGUID l f  0  602  600    0  0 0 0 0 0 path_center - - ));
 DATA(insert OID =  971 (  "@@"    PGNSP PGUID l f  0  604  600    0  0 0 0 0 0 poly_center - - ));
 
-DATA(insert OID =  974 (  "||"    PGNSP PGUID b f 1042 1042 1042    0  0 0 0 0 0 textcat - - ));
-DATA(insert OID =  979 (  "||"    PGNSP PGUID b f 1043 1043 1043    0  0 0 0 0 0 textcat - - ));
-
 DATA(insert OID = 1054 ( "="      PGNSP PGUID b f 1042 1042     16 1054 1057 1058 1058 1058 1060 bpchareq eqsel eqjoinsel ));
-DATA(insert OID = 1055 ( "~"      PGNSP PGUID b f 1042 25   16    0 1056  0 0 0 0 textregexeq regexeqsel regexeqjoinsel ));
+DATA(insert OID = 1055 ( "~"      PGNSP PGUID b f 1042 25   16    0 1056  0 0 0 0 bpcharregexeq regexeqsel regexeqjoinsel ));
 #define OID_BPCHAR_REGEXEQ_OP      1055
-DATA(insert OID = 1056 ( "!~"     PGNSP PGUID b f 1042 25   16    0 1055  0 0 0 0 textregexne regexnesel regexnejoinsel ));
+DATA(insert OID = 1056 ( "!~"     PGNSP PGUID b f 1042 25   16    0 1055  0 0 0 0 bpcharregexne regexnesel regexnejoinsel ));
 DATA(insert OID = 1057 ( "<>"     PGNSP PGUID b f 1042 1042     16 1057 1054  0 0 0 0 bpcharne neqsel neqjoinsel ));
 DATA(insert OID = 1058 ( "<"      PGNSP PGUID b f 1042 1042     16 1060 1061  0 0 0 0 bpcharlt scalarltsel scalarltjoinsel ));
 DATA(insert OID = 1059 ( "<="     PGNSP PGUID b f 1042 1042     16 1061 1060  0 0 0 0 bpcharle scalarltsel scalarltjoinsel ));
 DATA(insert OID = 1060 ( ">"      PGNSP PGUID b f 1042 1042     16 1058 1059  0 0 0 0 bpchargt scalargtsel scalargtjoinsel ));
 DATA(insert OID = 1061 ( ">="     PGNSP PGUID b f 1042 1042     16 1059 1058  0 0 0 0 bpcharge scalargtsel scalargtjoinsel ));
 
-DATA(insert OID = 1062 ( "="      PGNSP PGUID b t 1043 1043    16  1062 1065 1066 1066 1066 1068 varchareq eqsel eqjoinsel ));
-DATA(insert OID = 1063 ( "~"      PGNSP PGUID b f 1043 25  16 0 1064  0 0 0 0 textregexeq regexeqsel regexeqjoinsel ));
-#define OID_VARCHAR_REGEXEQ_OP     1063
-DATA(insert OID = 1064 ( "!~"     PGNSP PGUID b f 1043 25  16 0 1063  0 0 0 0 textregexne regexnesel regexnejoinsel ));
-DATA(insert OID = 1065 ( "<>"     PGNSP PGUID b f 1043 1043    16 1065 1062  0 0 0 0 varcharne neqsel neqjoinsel ));
-DATA(insert OID = 1066 ( "<"      PGNSP PGUID b f 1043 1043    16 1068 1069  0 0 0 0 varcharlt scalarltsel scalarltjoinsel ));
-DATA(insert OID = 1067 ( "<="     PGNSP PGUID b f 1043 1043    16 1069 1068  0 0 0 0 varcharle scalarltsel scalarltjoinsel ));
-DATA(insert OID = 1068 ( ">"      PGNSP PGUID b f 1043 1043    16 1066 1067  0 0 0 0 varchargt scalargtsel scalargtjoinsel ));
-DATA(insert OID = 1069 ( ">="     PGNSP PGUID b f 1043 1043    16 1067 1066  0 0 0 0 varcharge scalargtsel scalargtjoinsel ));
-
 /* date operators */
 DATA(insert OID = 1076 ( "+"      PGNSP PGUID b f  1082    1186 1114 0 0 0 0 0 0 date_pl_interval - - ));
 DATA(insert OID = 1077 ( "-"      PGNSP PGUID b f  1082    1186 1114 0 0 0 0 0 0 date_mi_interval - - ));
@@ -515,12 +506,9 @@ DATA(insert OID = 1208 (  "!~~"   PGNSP PGUID b f  19  25  16 0 1207 0 0 0 0 namen
 DATA(insert OID = 1209 (  "~~"   PGNSP PGUID b f  25   25  16 0 1210 0 0 0 0 textlike likesel likejoinsel ));
 #define OID_TEXT_LIKE_OP       1209
 DATA(insert OID = 1210 (  "!~~"   PGNSP PGUID b f  25  25  16 0 1209 0 0 0 0 textnlike nlikesel nlikejoinsel ));
-DATA(insert OID = 1211 (  "~~"   PGNSP PGUID b f  1042 25  16 0 1212 0 0 0 0 textlike likesel likejoinsel ));
+DATA(insert OID = 1211 (  "~~"   PGNSP PGUID b f  1042 25  16 0 1212 0 0 0 0 bpcharlike likesel likejoinsel ));
 #define OID_BPCHAR_LIKE_OP     1211
-DATA(insert OID = 1212 (  "!~~"   PGNSP PGUID b f  1042 25 16 0 1211 0 0 0 0 textnlike nlikesel nlikejoinsel ));
-DATA(insert OID = 1213 (  "~~"   PGNSP PGUID b f  1043 25  16 0 1214 0 0 0 0 textlike likesel likejoinsel ));
-#define OID_VARCHAR_LIKE_OP        1213
-DATA(insert OID = 1214 (  "!~~"   PGNSP PGUID b f  1043 25 16 0 1213 0 0 0 0 textnlike nlikesel nlikejoinsel ));
+DATA(insert OID = 1212 (  "!~~"   PGNSP PGUID b f  1042 25 16 0 1211 0 0 0 0 bpcharnlike nlikesel nlikejoinsel ));
 
 /* case-insensitive regex hacks */
 DATA(insert OID = 1226 (  "~*"      PGNSP PGUID b f    19  25  16 0 1227  0 0 0 0 nameicregexeq icregexeqsel icregexeqjoinsel ));
@@ -529,20 +517,17 @@ DATA(insert OID = 1227 (  "!~*"        PGNSP PGUID b f    19  25  16 0 1226  0 0 0 0 namei
 DATA(insert OID = 1228 (  "~*"      PGNSP PGUID b f    25  25  16 0 1229  0 0 0 0 texticregexeq icregexeqsel icregexeqjoinsel ));
 #define OID_TEXT_ICREGEXEQ_OP      1228
 DATA(insert OID = 1229 (  "!~*"         PGNSP PGUID b f    25  25  16 0 1228  0 0 0 0 texticregexne icregexnesel icregexnejoinsel ));
-DATA(insert OID = 1232 (  "~*"     PGNSP PGUID b f  1043  25  16 0 1233    0 0   0   0 texticregexeq icregexeqsel icregexeqjoinsel ));
-#define OID_VARCHAR_ICREGEXEQ_OP       1232
-DATA(insert OID = 1233 ( "!~*"     PGNSP PGUID b f  1043  25  16 0 1232    0 0   0   0 texticregexne icregexnesel icregexnejoinsel ));
-DATA(insert OID = 1234 (  "~*"     PGNSP PGUID b f  1042  25  16 0 1235    0 0   0   0 texticregexeq icregexeqsel icregexeqjoinsel ));
+DATA(insert OID = 1234 (  "~*"     PGNSP PGUID b f  1042  25  16 0 1235    0 0   0   0 bpcharicregexeq icregexeqsel icregexeqjoinsel ));
 #define OID_BPCHAR_ICREGEXEQ_OP        1234
-DATA(insert OID = 1235 ( "!~*"     PGNSP PGUID b f  1042  25  16 0 1234    0 0   0   0 texticregexne icregexnesel icregexnejoinsel ));
+DATA(insert OID = 1235 ( "!~*"     PGNSP PGUID b f  1042  25  16 0 1234    0 0   0   0 bpcharicregexne icregexnesel icregexnejoinsel ));
 
 /* timestamptz operators */
-DATA(insert OID = 1320 (  "="     PGNSP PGUID b f 1184 1184     16 1320 1321 1322 1322 1322 1324 timestamp_eq eqsel eqjoinsel ));
-DATA(insert OID = 1321 (  "<>"    PGNSP PGUID b f 1184 1184     16 1321 1320 0 0 0 0 timestamp_ne neqsel neqjoinsel ));
-DATA(insert OID = 1322 (  "<"     PGNSP PGUID b f 1184 1184     16 1324 1325 0 0 0 0 timestamp_lt scalarltsel scalarltjoinsel ));
-DATA(insert OID = 1323 (  "<="    PGNSP PGUID b f 1184 1184     16 1325 1324 0 0 0 0 timestamp_le scalarltsel scalarltjoinsel ));
-DATA(insert OID = 1324 (  ">"     PGNSP PGUID b f 1184 1184     16 1322 1323 0 0 0 0 timestamp_gt scalargtsel scalargtjoinsel ));
-DATA(insert OID = 1325 (  ">="    PGNSP PGUID b f 1184 1184     16 1323 1322 0 0 0 0 timestamp_ge scalargtsel scalargtjoinsel ));
+DATA(insert OID = 1320 (  "="     PGNSP PGUID b f 1184 1184     16 1320 1321 1322 1322 1322 1324 timestamptz_eq eqsel eqjoinsel ));
+DATA(insert OID = 1321 (  "<>"    PGNSP PGUID b f 1184 1184     16 1321 1320 0 0 0 0 timestamptz_ne neqsel neqjoinsel ));
+DATA(insert OID = 1322 (  "<"     PGNSP PGUID b f 1184 1184     16 1324 1325 0 0 0 0 timestamptz_lt scalarltsel scalarltjoinsel ));
+DATA(insert OID = 1323 (  "<="    PGNSP PGUID b f 1184 1184     16 1325 1324 0 0 0 0 timestamptz_le scalarltsel scalarltjoinsel ));
+DATA(insert OID = 1324 (  ">"     PGNSP PGUID b f 1184 1184     16 1322 1323 0 0 0 0 timestamptz_gt scalargtsel scalargtjoinsel ));
+DATA(insert OID = 1325 (  ">="    PGNSP PGUID b f 1184 1184     16 1323 1322 0 0 0 0 timestamptz_ge scalargtsel scalargtjoinsel ));
 DATA(insert OID = 1327 (  "+"     PGNSP PGUID b f 1184 1186 1184    0  0 0 0 0 0 timestamptz_pl_span - - ));
 DATA(insert OID = 1328 (  "-"     PGNSP PGUID b f 1184 1184 1186    0  0 0 0 0 0 timestamptz_mi - - ));
 DATA(insert OID = 1329 (  "-"     PGNSP PGUID b f 1184 1186 1184    0  0 0 0 0 0 timestamptz_mi_span - - ));
@@ -691,20 +676,9 @@ DATA(insert OID = 1626 (  "!~~*"  PGNSP PGUID b f  19  25  16 0 1625 0 0 0 0 namei
 DATA(insert OID = 1627 (  "~~*"   PGNSP PGUID b f  25  25  16 0 1628 0 0 0 0 texticlike iclikesel iclikejoinsel ));
 #define OID_TEXT_ICLIKE_OP     1627
 DATA(insert OID = 1628 (  "!~~*"  PGNSP PGUID b f  25  25  16 0 1627 0 0 0 0 texticnlike icnlikesel icnlikejoinsel ));
-DATA(insert OID = 1629 (  "~~*"   PGNSP PGUID b f  1042 25 16 0 1630 0 0 0 0 texticlike iclikesel iclikejoinsel ));
+DATA(insert OID = 1629 (  "~~*"   PGNSP PGUID b f  1042 25 16 0 1630 0 0 0 0 bpchariclike iclikesel iclikejoinsel ));
 #define OID_BPCHAR_ICLIKE_OP   1629
-DATA(insert OID = 1630 (  "!~~*"  PGNSP PGUID b f  1042 25 16 0 1629 0 0 0 0 texticnlike icnlikesel icnlikejoinsel ));
-DATA(insert OID = 1631 (  "~~*"   PGNSP PGUID b f  1043 25 16 0 1632 0 0 0 0 texticlike iclikesel iclikejoinsel ));
-#define OID_VARCHAR_ICLIKE_OP  1631
-DATA(insert OID = 1632 (  "!~~*"  PGNSP PGUID b f  1043 25 16 0 1631 0 0 0 0 texticnlike icnlikesel icnlikejoinsel ));
-
-/* regproc comparisons --- use oid (unsigned) comparison */
-DATA(insert OID = 1656 (  "="     PGNSP PGUID b t  24  24  16 1656 1657 1658 1658 1658 1659 oideq eqsel eqjoinsel ));
-DATA(insert OID = 1657 (  "<>"    PGNSP PGUID b f  24  24  16 1657 1656 0 0 0 0 oidne neqsel neqjoinsel ));
-DATA(insert OID = 1658 (  "<"     PGNSP PGUID b f  24  24  16 1659 1661 0 0 0 0 oidlt scalarltsel scalarltjoinsel ));
-DATA(insert OID = 1659 (  ">"     PGNSP PGUID b f  24  24  16 1658 1660 0 0 0 0 oidgt scalargtsel scalargtjoinsel ));
-DATA(insert OID = 1660 (  "<="    PGNSP PGUID b f  24  24  16 1661 1659 0 0 0 0 oidle scalarltsel scalarltjoinsel ));
-DATA(insert OID = 1661 (  ">="    PGNSP PGUID b f  24  24  16 1660 1658 0 0 0 0 oidge scalargtsel scalargtjoinsel ));
+DATA(insert OID = 1630 (  "!~~*"  PGNSP PGUID b f  1042 25 16 0 1629 0 0 0 0 bpcharicnlike icnlikesel icnlikejoinsel ));
 
 /* NUMERIC type - OID's 1700-1799 */
 DATA(insert OID = 1751 (  "-"     PGNSP PGUID l f  0 1700 1700    0    0 0 0 0 0 numeric_uminus - - ));
@@ -823,13 +797,6 @@ DATA(insert OID = 2317 ( "~>=~"    PGNSP PGUID b f 25 25 16 2315 2314 0 0 0 0 text_
 DATA(insert OID = 2318 ( "~>~" PGNSP PGUID b f 25 25 16 2314 2315 0 0 0 0 text_pattern_gt scalargtsel scalargtjoinsel ));
 DATA(insert OID = 2319 ( "~<>~"    PGNSP PGUID b f 25 25 16 2319 2316 0 0 0 0 text_pattern_ne neqsel neqjoinsel ));
 
-DATA(insert OID = 2320 ( "~<~" PGNSP PGUID b f 1043 1043 16 2324 2323 0 0 0 0 varchar_pattern_lt scalarltsel scalarltjoinsel ));
-DATA(insert OID = 2321 ( "~<=~"    PGNSP PGUID b f 1043 1043 16 2323 2324 0 0 0 0 varchar_pattern_le scalarltsel scalarltjoinsel ));
-DATA(insert OID = 2322 ( "~=~" PGNSP PGUID b t 1043 1043 16 2322 2325 2320 2320 2320 2324 varchar_pattern_eq eqsel eqjoinsel ));
-DATA(insert OID = 2323 ( "~>=~"    PGNSP PGUID b f 1043 1043 16 2321 2320 0 0 0 0 varchar_pattern_ge scalargtsel scalargtjoinsel ));
-DATA(insert OID = 2324 ( "~>~" PGNSP PGUID b f 1043 1043 16 2320 2321 0 0 0 0 varchar_pattern_gt scalargtsel scalargtjoinsel ));
-DATA(insert OID = 2325 ( "~<>~"    PGNSP PGUID b f 1043 1043 16 2325 2322 0 0 0 0 varchar_pattern_ne neqsel neqjoinsel ));
-
 DATA(insert OID = 2326 ( "~<~" PGNSP PGUID b f 1042 1042 16 2330 2329 0 0 0 0 bpchar_pattern_lt scalarltsel scalarltjoinsel ));
 DATA(insert OID = 2327 ( "~<=~"    PGNSP PGUID b f 1042 1042 16 2329 2330 0 0 0 0 bpchar_pattern_le scalarltsel scalarltjoinsel ));
 DATA(insert OID = 2328 ( "~=~" PGNSP PGUID b t 1042 1042 16 2328 2331 2326 2326 2326 2330 bpchar_pattern_eq eqsel eqjoinsel ));
index f2f3e3eff14b0adc91e4ab317296efdb8abb5aba..7d6ff867962ac7f5e08d096d13ba5cdbb03a6876 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_proc.h,v 1.301 2003/05/23 22:33:22 tgl Exp $
+ * $Id: pg_proc.h,v 1.302 2003/05/26 00:11:27 tgl Exp $
  *
  * NOTES
  *   The script catalog/genbki.sh reads this file and generates .bki
@@ -788,14 +788,16 @@ DESCR("intersect?");
 
 /* OIDS 400 - 499 */
 
+DATA(insert OID =  401 (  text            PGNSP PGUID 12 f f t f i 1 25 "1042"  rtrim1 - _null_ ));
+DESCR("convert char(n) to text");
 DATA(insert OID =  406 (  text            PGNSP PGUID 12 f f t f i 1 25 "19" name_text - _null_ ));
 DESCR("convert name to text");
 DATA(insert OID =  407 (  name            PGNSP PGUID 12 f f t f i 1 19 "25" text_name - _null_ ));
 DESCR("convert text to name");
 DATA(insert OID =  408 (  bpchar          PGNSP PGUID 12 f f t f i 1 1042 "19" name_bpchar - _null_ ));
-DESCR("convert name to char()");
+DESCR("convert name to char(n)");
 DATA(insert OID =  409 (  name            PGNSP PGUID 12 f f t f i 1 19 "1042"  bpchar_name - _null_ ));
-DESCR("convert char() to name");
+DESCR("convert char(n) to name");
 
 DATA(insert OID = 440 (  hashgettuple     PGNSP PGUID 12 f f t f v 2 16 "2281 2281"  hashgettuple - _null_ ));
 DESCR("hash(internal)");
@@ -1338,22 +1340,8 @@ DATA(insert OID = 1052 (  bpcharge          PGNSP PGUID 12 f f t f i 2 16 "1042 1042"
 DESCR("greater-than-or-equal");
 DATA(insert OID = 1053 (  bpcharne        PGNSP PGUID 12 f f t f i 2 16 "1042 1042"    bpcharne - _null_ ));
 DESCR("not equal");
-DATA(insert OID = 1070 (  varchareq           PGNSP PGUID 12 f f t f i 2 16 "1043 1043"    varchareq - _null_ ));
-DESCR("equal");
-DATA(insert OID = 1071 (  varcharlt           PGNSP PGUID 12 f f t f i 2 16 "1043 1043"    varcharlt - _null_ ));
-DESCR("less-than");
-DATA(insert OID = 1072 (  varcharle           PGNSP PGUID 12 f f t f i 2 16 "1043 1043"    varcharle - _null_ ));
-DESCR("less-than-or-equal");
-DATA(insert OID = 1073 (  varchargt           PGNSP PGUID 12 f f t f i 2 16 "1043 1043"    varchargt - _null_ ));
-DESCR("greater-than");
-DATA(insert OID = 1074 (  varcharge           PGNSP PGUID 12 f f t f i 2 16 "1043 1043"    varcharge - _null_ ));
-DESCR("greater-than-or-equal");
-DATA(insert OID = 1075 (  varcharne           PGNSP PGUID 12 f f t f i 2 16 "1043 1043"    varcharne - _null_ ));
-DESCR("not equal");
 DATA(insert OID = 1078 (  bpcharcmp           PGNSP PGUID 12 f f t f i 2 23 "1042 1042"    bpcharcmp - _null_ ));
 DESCR("less-equal-greater");
-DATA(insert OID = 1079 (  varcharcmp      PGNSP PGUID 12 f f t f i 2 23 "1043 1043"    varcharcmp - _null_ ));
-DESCR("less-equal-greater");
 DATA(insert OID = 1080 (  hashbpchar      PGNSP PGUID 12 f f t f i 1 23 "1042"  hashbpchar - _null_ ));
 DESCR("hash");
 DATA(insert OID = 1081 (  format_type     PGNSP PGUID 12 f f f f s 2 25 "26 23" format_type - _null_ ));
@@ -1488,7 +1476,7 @@ DESCR("minus");
 DATA(insert OID = 1191 (  timestamptz      PGNSP PGUID 12 f f t f s 1 1184 "25"    text_timestamptz - _null_ ));
 DESCR("convert text to timestamp with time zone");
 DATA(insert OID = 1192 (  text             PGNSP PGUID 12 f f t f s 1   25 "1184"  timestamptz_text - _null_ ));
-DESCR("convert timestamp to text");
+DESCR("convert timestamp with time zone to text");
 DATA(insert OID = 1193 (  text             PGNSP PGUID 12 f f t f i 1   25 "1186"  interval_text - _null_ ));
 DESCR("convert interval to text");
 DATA(insert OID = 1194 (  reltime          PGNSP PGUID 12 f f t f i 1  703 "1186"  interval_reltime - _null_ ));
@@ -1594,7 +1582,7 @@ DESCR("convert time and date to timestamp");
 DATA(insert OID = 1297 (  datetimetz_pl    PGNSP PGUID 12 f f t f i 2 1184 "1082 1266" datetimetz_timestamptz - _null_ ));
 DESCR("convert date and time with time zone to timestamp with time zone");
 DATA(insert OID = 1298 (  timetzdate_pl    PGNSP PGUID 14 f f t f i 2 1184 "1266 1082" "select ($2 + $1)" - _null_ ));
-DESCR("convert time with time zone and date to timestamp");
+DESCR("convert time with time zone and date to timestamp with time zone");
 DATA(insert OID = 1299 (  now             PGNSP PGUID 12 f f t f s 0 1184 ""  now - _null_ ));
 DESCR("current transaction time");
 
@@ -1642,8 +1630,9 @@ DATA(insert OID = 1317 (  length           PGNSP PGUID 12 f f t f i 1 23 "25"  textlen
 DESCR("length");
 DATA(insert OID = 1318 (  length            PGNSP PGUID 12 f f t f i 1 23 "1042"  bpcharlen - _null_ ));
 DESCR("character length");
-DATA(insert OID = 1319 (  length            PGNSP PGUID 12 f f t f i 1 23 "1043"  varcharlen - _null_ ));
-DESCR("character length");
+
+DATA(insert OID = 1319 (  xideqint4             PGNSP PGUID 12 f f t f i 2 16 "28 23"  xideq - _null_ ));
+DESCR("equal");
 
 DATA(insert OID = 1326 (  interval_div      PGNSP PGUID 12 f f t f i 2 1186 "1186 701"  interval_div - _null_ ));
 DESCR("divide");
@@ -1703,8 +1692,6 @@ DESCR("convert abstime to time");
 
 DATA(insert OID = 1367 (  character_length PGNSP PGUID 12 f f t f i 1  23 "1042"  bpcharlen - _null_ ));
 DESCR("character length");
-DATA(insert OID = 1368 (  character_length PGNSP PGUID 12 f f t f i 1  23 "1043"  varcharlen - _null_ ));
-DESCR("character length");
 DATA(insert OID = 1369 (  character_length PGNSP PGUID 12 f f t f i 1  23 "25"  textlen - _null_ ));
 DESCR("character length");
 
@@ -1712,15 +1699,11 @@ DATA(insert OID = 1370 (  interval           PGNSP PGUID 12 f f t f i 1 1186 "1083"  ti
 DESCR("convert time to interval");
 DATA(insert OID = 1372 (  char_length       PGNSP PGUID 12 f f t f i 1 23   "1042"  bpcharlen - _null_ ));
 DESCR("character length");
-DATA(insert OID = 1373 (  char_length       PGNSP PGUID 12 f f t f i 1 23   "1043"  varcharlen - _null_ ));
-DESCR("character length");
 
 DATA(insert OID = 1374 (  octet_length          PGNSP PGUID 12 f f t f i 1 23   "25"  textoctetlen - _null_ ));
 DESCR("octet length");
 DATA(insert OID = 1375 (  octet_length          PGNSP PGUID 12 f f t f i 1 23   "1042"  bpcharoctetlen - _null_ ));
 DESCR("octet length");
-DATA(insert OID = 1376 (  octet_length          PGNSP PGUID 12 f f t f i 1 23   "1043"  varcharoctetlen - _null_ ));
-DESCR("octet length");
 
 DATA(insert OID = 1377 (  time_larger     PGNSP PGUID 12 f f t f i 2 1083 "1083 1083"  time_larger - _null_ ));
 DESCR("larger of two");
@@ -2106,6 +2089,11 @@ DESCR("convert SQL99 regexp pattern to POSIX style");
 
 DATA(insert OID = 1624 (  mul_d_interval   PGNSP PGUID 12 f f t f i 2 1186 "701 1186"  mul_d_interval - _null_ ));
 
+DATA(insert OID = 1631 (  bpcharlike      PGNSP PGUID 12 f f t f i 2 16 "1042 25" textlike - _null_ ));
+DESCR("matches LIKE expression");
+DATA(insert OID = 1632 (  bpcharnlike     PGNSP PGUID 12 f f t f i 2 16 "1042 25" textnlike - _null_ ));
+DESCR("does not match LIKE expression");
+
 DATA(insert OID = 1633 (  texticlike       PGNSP PGUID 12 f f t f i 2 16 "25 25" texticlike - _null_ ));
 DESCR("matches LIKE expression, case-insensitive");
 DATA(insert OID = 1634 (  texticnlike      PGNSP PGUID 12 f f t f i 2 16 "25 25" texticnlike - _null_ ));
@@ -2117,6 +2105,19 @@ DESCR("does not match LIKE expression, case-insensitive");
 DATA(insert OID = 1637 (  like_escape      PGNSP PGUID 12 f f t f i 2 25 "25 25" like_escape - _null_ ));
 DESCR("convert LIKE pattern to use backslash escapes");
 
+DATA(insert OID = 1656 (  bpcharicregexeq    PGNSP PGUID 12 f f t f i 2 16 "1042 25"   texticregexeq - _null_ ));
+DESCR("matches regex., case-insensitive");
+DATA(insert OID = 1657 (  bpcharicregexne    PGNSP PGUID 12 f f t f i 2 16 "1042 25"   texticregexne - _null_ ));
+DESCR("does not match regex., case-insensitive");
+DATA(insert OID = 1658 (  bpcharregexeq       PGNSP PGUID 12 f f t f i 2 16 "1042 25"  textregexeq - _null_ ));
+DESCR("matches regex., case-sensitive");
+DATA(insert OID = 1659 (  bpcharregexne       PGNSP PGUID 12 f f t f i 2 16 "1042 25"  textregexne - _null_ ));
+DESCR("does not match regex., case-sensitive");
+DATA(insert OID = 1660 (  bpchariclike     PGNSP PGUID 12 f f t f i 2 16 "1042 25" texticlike - _null_ ));
+DESCR("matches LIKE expression, case-insensitive");
+DATA(insert OID = 1661 (  bpcharicnlike        PGNSP PGUID 12 f f t f i 2 16 "1042 25" texticnlike - _null_ ));
+DESCR("does not match LIKE expression, case-insensitive");
+
 DATA(insert OID = 1689 (  update_pg_pwd_and_pg_group  PGNSP PGUID 12 f f t f v 0 2279  ""  update_pg_pwd_and_pg_group - _null_ ));
 DESCR("update pg_pwd and pg_group files");
 
@@ -2532,7 +2533,7 @@ DESCR("format float8 to text");
 DATA(insert OID = 1777 ( to_number         PGNSP PGUID 12 f f t f i 2  1700 "25 25"  numeric_to_number - _null_ ));
 DESCR("convert text to numeric");
 DATA(insert OID = 1778 ( to_timestamp      PGNSP PGUID 12 f f t f s 2  1184 "25 25"  to_timestamp - _null_ ));
-DESCR("convert text to timestamp");
+DESCR("convert text to timestamp with time zone");
 DATA(insert OID = 1780 ( to_date           PGNSP PGUID 12 f f t f i 2  1082 "25 25"  to_date - _null_ ));
 DESCR("convert text to date");
 DATA(insert OID = 1768 ( to_char           PGNSP PGUID 12 f f t f i 2  25 "1186 25"  interval_to_char - _null_ ));
@@ -2803,7 +2804,7 @@ DATA(insert OID = 1966 (  oidsmaller     PGNSP PGUID 12 f f t f i 2 26 "26 26"    oi
 DESCR("smaller of two");
 
 DATA(insert OID = 1967 (  timestamptz     PGNSP PGUID 12 f f t f i 2 1184 "1184 23"    timestamptz_scale - _null_ ));
-DESCR("adjust timestamp precision");
+DESCR("adjust timestamptz precision");
 DATA(insert OID = 1968 (  time            PGNSP PGUID 12 f f t f i 2 1083 "1083 23"    time_scale - _null_ ));
 DESCR("adjust time precision");
 DATA(insert OID = 1969 (  timetz          PGNSP PGUID 12 f f t f i 2 1266 "1266 23"    timetz_scale - _null_ ));
@@ -2851,9 +2852,9 @@ DESCR("convert date to timestamp");
 DATA(insert OID = 2025 (  timestamp            PGNSP PGUID 12 f f t f i 2 1114 "1082 1083"  datetime_timestamp - _null_ ));
 DESCR("convert date and time to timestamp");
 DATA(insert OID = 2027 (  timestamp            PGNSP PGUID 12 f f t f s 1 1114 "1184"  timestamptz_timestamp - _null_ ));
-DESCR("convert date and time with time zone to timestamp");
+DESCR("convert timestamp with time zone to timestamp");
 DATA(insert OID = 2028 (  timestamptz      PGNSP PGUID 12 f f t f s 1 1184 "1114"  timestamp_timestamptz - _null_ ));
-DESCR("convert date and time with time zone to timestamp");
+DESCR("convert timestamp to timestamp with time zone");
 DATA(insert OID = 2029 (  date             PGNSP PGUID 12 f f t f i 1 1082 "1114"  timestamp_date - _null_ ));
 DESCR("convert timestamp to date");
 DATA(insert OID = 2030 (  abstime          PGNSP PGUID 12 f f t f s 1  702 "1114"  timestamp_abstime - _null_ ));
@@ -3033,14 +3034,6 @@ DATA(insert OID = 2165 ( text_pattern_ne     PGNSP PGUID 12 f f t f i 2 16 "25 2
 DATA(insert OID = 2166 ( bttext_pattern_cmp  PGNSP PGUID 12 f f t f i 2 23 "25 25" bttext_pattern_cmp - _null_ ));
 
 /* We use the same procedures here as above since the types are binary compatible. */
-DATA(insert OID = 2167 ( varchar_pattern_lt    PGNSP PGUID 12 f f t f i 2 16 "1043 1043" text_pattern_lt - _null_ ));
-DATA(insert OID = 2168 ( varchar_pattern_le    PGNSP PGUID 12 f f t f i 2 16 "1043 1043" text_pattern_le - _null_ ));
-DATA(insert OID = 2169 ( varchar_pattern_eq    PGNSP PGUID 12 f f t f i 2 16 "1043 1043" text_pattern_eq - _null_ ));
-DATA(insert OID = 2170 ( varchar_pattern_ge    PGNSP PGUID 12 f f t f i 2 16 "1043 1043" text_pattern_ge - _null_ ));
-DATA(insert OID = 2171 ( varchar_pattern_gt    PGNSP PGUID 12 f f t f i 2 16 "1043 1043" text_pattern_gt - _null_ ));
-DATA(insert OID = 2172 ( varchar_pattern_ne    PGNSP PGUID 12 f f t f i 2 16 "1043 1043" text_pattern_ne - _null_ ));
-DATA(insert OID = 2173 ( btvarchar_pattern_cmp PGNSP PGUID 12 f f t f i 2 23 "1043 1043" bttext_pattern_cmp - _null_ ));
-
 DATA(insert OID = 2174 ( bpchar_pattern_lt    PGNSP PGUID 12 f f t f i 2 16 "1042 1042" text_pattern_lt - _null_ ));
 DATA(insert OID = 2175 ( bpchar_pattern_le    PGNSP PGUID 12 f f t f i 2 16 "1042 1042" text_pattern_le - _null_ ));
 DATA(insert OID = 2176 ( bpchar_pattern_eq    PGNSP PGUID 12 f f t f i 2 16 "1042 1042" text_pattern_eq - _null_ ));
index 768e493e0c9c0be86ff7d63169519d8beebc7b6d..b36e17f91d7801989f190edc7977f0bf815a7329 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: paths.h,v 1.66 2003/02/15 20:12:41 tgl Exp $
+ * $Id: paths.h,v 1.67 2003/05/26 00:11:28 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -41,7 +41,8 @@ extern Path *best_inner_indexscan(Query *root, RelOptInfo *rel,
 extern List *extract_or_indexqual_conditions(RelOptInfo *rel,
                                IndexOptInfo *index,
                                Expr *orsubclause);
-extern List *expand_indexqual_conditions(List *indexquals);
+extern List *expand_indexqual_conditions(IndexOptInfo *index,
+                                        List *clausegroups);
 
 /*
  * orindxpath.c
index 2b1a1fad4b9734f74ff9008efbc89c74a63e53cd..5f2adf5c4a1d9b15c3239878a46a9cdd6e84923e 100644 (file)
@@ -7,13 +7,14 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_func.h,v 1.45 2003/04/29 22:13:11 tgl Exp $
+ * $Id: parse_func.h,v 1.46 2003/05/26 00:11:28 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef PARSER_FUNC_H
 #define PARSER_FUNC_H
 
+#include "catalog/namespace.h"
 #include "parser/parse_node.h"
 
 
@@ -48,6 +49,15 @@ extern FuncDetailCode func_get_detail(List *funcname, List *fargs,
                Oid *funcid, Oid *rettype,
                bool *retset, Oid **true_typeids);
 
+extern int func_match_argtypes(int nargs,
+                               Oid *input_typeids,
+                               FuncCandidateList raw_candidates,
+                               FuncCandidateList *candidates);
+
+extern FuncCandidateList func_select_candidate(int nargs,
+                                              Oid *input_typeids,
+                                              FuncCandidateList candidates);
+
 extern bool typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId);
 
 extern void make_fn_arguments(ParseState *pstate,
index 539dfbc9de79aea7010f23a79800b2d6554cfc16..42e9a84c77b8bf34f5046e15e8c8954e8a19d75e 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: builtins.h,v 1.218 2003/05/23 22:33:23 tgl Exp $
+ * $Id: builtins.h,v 1.219 2003/05/26 00:11:28 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -496,15 +496,6 @@ extern Datum varcharout(PG_FUNCTION_ARGS);
 extern Datum varcharrecv(PG_FUNCTION_ARGS);
 extern Datum varcharsend(PG_FUNCTION_ARGS);
 extern Datum varchar(PG_FUNCTION_ARGS);
-extern Datum varchareq(PG_FUNCTION_ARGS);
-extern Datum varcharne(PG_FUNCTION_ARGS);
-extern Datum varcharlt(PG_FUNCTION_ARGS);
-extern Datum varcharle(PG_FUNCTION_ARGS);
-extern Datum varchargt(PG_FUNCTION_ARGS);
-extern Datum varcharge(PG_FUNCTION_ARGS);
-extern Datum varcharcmp(PG_FUNCTION_ARGS);
-extern Datum varcharlen(PG_FUNCTION_ARGS);
-extern Datum varcharoctetlen(PG_FUNCTION_ARGS);
 
 /* varlena.c */
 extern Datum textin(PG_FUNCTION_ARGS);
index 848cc9f146e9c4415c2939ec62ee29d790fa8019..66b497a98b14fd3c975f61cd2145f3a9d08a2b69 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: lsyscache.h,v 1.69 2003/05/09 18:08:48 tgl Exp $
+ * $Id: lsyscache.h,v 1.70 2003/05/26 00:11:28 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,6 +17,7 @@
 
 extern bool op_in_opclass(Oid opno, Oid opclass);
 extern bool op_requires_recheck(Oid opno, Oid opclass);
+extern Oid get_opclass_member(Oid opclass, int16 strategy);
 extern char *get_attname(Oid relid, AttrNumber attnum);
 extern AttrNumber get_attnum(Oid relid, const char *attname);
 extern Oid get_atttype(Oid relid, AttrNumber attnum);
index 8cc09dae7442514f465712393c0b17a93e485151..f6c3a17fe51f659d98a572df3913545dfc6aedec 100644 (file)
@@ -27,7 +27,7 @@ INSERT INTO NAME_TBL(f1) VALUES ('d34aaasdf');
 INSERT INTO NAME_TBL(f1) VALUES ('');
 INSERT INTO NAME_TBL(f1) VALUES ('1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ');
 SELECT '' AS seven, NAME_TBL.*;
- seven |               f1                
+ seven |                               f1                                
 -------+-----------------------------------------------------------------
        | 1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQ
        | 1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopq
@@ -39,7 +39,7 @@ SELECT '' AS seven, NAME_TBL.*;
 (7 rows)
 
 SELECT '' AS six, c.f1 FROM NAME_TBL c WHERE c.f1 <> '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQR';
- six |               f1                
+ six |                               f1                                
 -----+-----------------------------------------------------------------
      | 1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopq
      | asdfghjkl;
@@ -49,20 +49,20 @@ SELECT '' AS six, c.f1 FROM NAME_TBL c WHERE c.f1 <> '1234567890ABCDEFGHIJKLMNOP
 (5 rows)
 
 SELECT '' AS one, c.f1 FROM NAME_TBL c WHERE c.f1 = '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQR';
- one |        f1        
+ one |                               f1                                
 -----+-----------------------------------------------------------------
      | 1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQ
      | 1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQ
 (2 rows)
 
 SELECT '' AS three, c.f1 FROM NAME_TBL c WHERE c.f1 < '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQR';
- three |               f1                
+ three | f1 
 -------+----
        | 
 (1 row)
 
 SELECT '' AS four, c.f1 FROM NAME_TBL c WHERE c.f1 <= '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQR';
- four |               f1                
+ four |                               f1                                
 ------+-----------------------------------------------------------------
       | 1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQ
       | 
@@ -70,7 +70,7 @@ SELECT '' AS four, c.f1 FROM NAME_TBL c WHERE c.f1 <= '1234567890ABCDEFGHIJKLMNO
 (3 rows)
 
 SELECT '' AS three, c.f1 FROM NAME_TBL c WHERE c.f1 > '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQR';
- three |        f1        
+ three |                               f1                                
 -------+-----------------------------------------------------------------
        | 1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopq
        | asdfghjkl;
@@ -79,7 +79,7 @@ SELECT '' AS three, c.f1 FROM NAME_TBL c WHERE c.f1 > '1234567890ABCDEFGHIJKLMNO
 (4 rows)
 
 SELECT '' AS four, c.f1 FROM NAME_TBL c WHERE c.f1 >= '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQR';
- four |        f1        
+ four |                               f1                                
 ------+-----------------------------------------------------------------
       | 1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQ
       | 1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopq
@@ -90,7 +90,7 @@ SELECT '' AS four, c.f1 FROM NAME_TBL c WHERE c.f1 >= '1234567890ABCDEFGHIJKLMNO
 (6 rows)
 
 SELECT '' AS seven, c.f1 FROM NAME_TBL c WHERE c.f1 ~ '.*';
- seven |               f1                
+ seven |                               f1                                
 -------+-----------------------------------------------------------------
        | 1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQ
        | 1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopq
@@ -107,7 +107,7 @@ SELECT '' AS zero, c.f1 FROM NAME_TBL c WHERE c.f1 !~ '.*';
 (0 rows)
 
 SELECT '' AS three, c.f1 FROM NAME_TBL c WHERE c.f1 ~ '[0-9]';
- three |               f1                
+ three |                               f1                                
 -------+-----------------------------------------------------------------
        | 1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQ
        | 1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopq
index 2573d2011c3bbcea454b8402d6468ab927247f33..e0504706f3c1c2a5f50d465cb13199b3247a99d1 100644 (file)
 --
 -- NB: run this test earlier than the create_operator test, because
 -- that test creates some bogus operators...
+-- Helper functions to deal with cases where binary-coercible matches are
+-- allowed.
+-- This should match IsBinaryCoercible() in parse_coerce.c.
+create function binary_coercible(oid, oid) returns bool as
+'SELECT ($1 = $2) OR
+ EXISTS(select 1 from pg_cast where
+        castsource = $1 and casttarget = $2 and
+        castfunc = 0 and castcontext = ''i'')'
+language sql;
+-- This one ignores castcontext, so it considers only physical equivalence
+-- and not whether the coercion can be invoked implicitly.
+create function physically_coercible(oid, oid) returns bool as
+'SELECT ($1 = $2) OR
+ EXISTS(select 1 from pg_cast where
+        castsource = $1 and casttarget = $2 and
+        castfunc = 0)'
+language sql;
 -- **************** pg_proc ****************
 -- Look for illegal values in pg_proc fields.
 -- NOTE: in reality pronargs could be more than 10, but I'm too lazy to put
@@ -105,11 +122,10 @@ WHERE p1.oid != p2.oid AND
 -------------+-------------
           25 |        1042
           25 |        1043
-        1042 |        1043
         1114 |        1184
         1560 |        1562
         2277 |        2283
-(6 rows)
+(5 rows)
 
 SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1]
 FROM pg_proc AS p1, pg_proc AS p2
@@ -120,13 +136,12 @@ WHERE p1.oid != p2.oid AND
     (p1.proargtypes[1] < p2.proargtypes[1]);
  proargtypes | proargtypes 
 -------------+-------------
+          23 |          28
           25 |        1042
-          25 |        1043
-        1042 |        1043
         1114 |        1184
         1560 |        1562
         2277 |        2283
-(6 rows)
+(5 rows)
 
 SELECT DISTINCT p1.proargtypes[2], p2.proargtypes[2]
 FROM pg_proc AS p1, pg_proc AS p2
@@ -228,23 +243,17 @@ SELECT c.*
 FROM pg_cast c, pg_proc p
 WHERE c.castfunc = p.oid AND
     (p.pronargs <> 1
-     OR NOT (c.castsource = p.proargtypes[0] OR
-             EXISTS (SELECT 1 FROM pg_cast k
-                     WHERE k.castfunc = 0 AND
-                       k.castsource = c.castsource AND
-                       k.casttarget = p.proargtypes[0]))
-     OR NOT (p.prorettype = c.casttarget OR
-             EXISTS (SELECT 1 FROM pg_cast k
-                     WHERE k.castfunc = 0 AND
-                       k.castsource = p.prorettype AND
-                       k.casttarget = c.casttarget)));
+     OR NOT binary_coercible(c.castsource, p.proargtypes[0])
+     OR NOT binary_coercible(p.prorettype, c.casttarget));
  castsource | casttarget | castfunc | castcontext 
 ------------+------------+----------+-------------
 (0 rows)
 
 -- Look for binary compatible casts that do not have the reverse
 -- direction registered as well, or where the reverse direction is not
--- also binary compatible.  This is legal, but probably not intended.
+-- also binary compatible.  This is legal, but usually not intended.
+-- As of 7.4, this finds the casts from text and varchar to bpchar, because
+-- those are binary-compatible while the reverse way goes through rtrim().
 SELECT *
 FROM pg_cast c
 WHERE c.castfunc = 0 AND
@@ -254,7 +263,9 @@ WHERE c.castfunc = 0 AND
                     k.casttarget = c.castsource);
  castsource | casttarget | castfunc | castcontext 
 ------------+------------+----------+-------------
-(0 rows)
+         25 |       1042 |        0 | i
+       1043 |       1042 |        0 | i
+(2 rows)
 
 -- **************** pg_operator ****************
 -- Look for illegal values in pg_operator fields.
@@ -425,14 +436,15 @@ WHERE p1.oprlsortop != p1.oprrsortop AND
 -- Hashing only works on simple equality operators "type = sametype",
 -- since the hash itself depends on the bitwise representation of the type.
 -- Check that allegedly hashable operators look like they might be "=".
--- NOTE: in 7.3, this search finds xideqint4.
--- Until we have some cleaner way of dealing with binary-equivalent types,
--- just leave that tuple in the expected output.
+-- NOTE: as of 7.3, this search finds xideqint4.  Since we do not mark
+-- xid and int4 as binary-equivalent in pg_cast, there's no easy way to
+-- recognize that case as OK; just leave that tuple in the expected output.
 SELECT p1.oid, p1.oprname
 FROM pg_operator AS p1
 WHERE p1.oprcanhash AND NOT
     (p1.oprkind = 'b' AND p1.oprresult = 'bool'::regtype AND
-     p1.oprleft = p1.oprright AND p1.oprname IN ('=', '~=~') AND p1.oprcom = p1.oid);
+     p1.oprleft = p1.oprright AND p1.oprname IN ('=', '~=~') AND
+     p1.oprcom = p1.oid);
  oid | oprname 
 -----+---------
  353 | =
@@ -464,33 +476,26 @@ WHERE p1.oprcanhash AND p1.oprcode = p2.oid AND p2.proname = 'array_eq';
 
 -- Check that each operator defined in pg_operator matches its oprcode entry
 -- in pg_proc.  Easiest to do this separately for each oprkind.
--- FIXME: want to check that argument/result types match, but how to do that
--- in the face of binary-compatible types?
 SELECT p1.oid, p1.oprname, p2.oid, p2.proname
 FROM pg_operator AS p1, pg_proc AS p2
 WHERE p1.oprcode = p2.oid AND
     p1.oprkind = 'b' AND
     (p2.pronargs != 2
--- diked out until we find a way of marking binary-compatible types
--- OR
---     p1.oprresult != p2.prorettype OR
---     (p1.oprleft != p2.proargtypes[0] AND p2.proargtypes[0] != 0) OR
---     (p1.oprright != p2.proargtypes[1] AND p2.proargtypes[1] != 0)
-);
+     OR NOT binary_coercible(p2.prorettype, p1.oprresult)
+     OR NOT binary_coercible(p1.oprleft, p2.proargtypes[0])
+     OR NOT binary_coercible(p1.oprright, p2.proargtypes[1]));
  oid | oprname | oid | proname 
 -----+---------+-----+---------
 (0 rows)
 
--- These two selects can be left as-is because there are no binary-compatible
--- cases that they trip over, at least in 6.5:
 SELECT p1.oid, p1.oprname, p2.oid, p2.proname
 FROM pg_operator AS p1, pg_proc AS p2
 WHERE p1.oprcode = p2.oid AND
     p1.oprkind = 'l' AND
-    (p2.pronargs != 1 OR
-     p1.oprresult != p2.prorettype OR
-     (p1.oprright != p2.proargtypes[0] AND p2.proargtypes[0] != 0) OR
-     p1.oprleft != 0);
+    (p2.pronargs != 1
+     OR NOT binary_coercible(p2.prorettype, p1.oprresult)
+     OR NOT binary_coercible(p1.oprright, p2.proargtypes[0])
+     OR p1.oprleft != 0);
  oid | oprname | oid | proname 
 -----+---------+-----+---------
 (0 rows)
@@ -499,10 +504,10 @@ SELECT p1.oid, p1.oprname, p2.oid, p2.proname
 FROM pg_operator AS p1, pg_proc AS p2
 WHERE p1.oprcode = p2.oid AND
     p1.oprkind = 'r' AND
-    (p2.pronargs != 1 OR
-     p1.oprresult != p2.prorettype OR
-     (p1.oprleft != p2.proargtypes[0] AND p2.proargtypes[0] != 0) OR
-     p1.oprright != 0);
+    (p2.pronargs != 1
+     OR NOT binary_coercible(p2.prorettype, p1.oprresult)
+     OR NOT binary_coercible(p1.oprleft, p2.proargtypes[0])
+     OR p1.oprright != 0);
  oid | oprname | oid | proname 
 -----+---------+-----+---------
 (0 rows)
@@ -591,48 +596,46 @@ WHERE a.aggfnoid = p.oid AND
 (0 rows)
 
 -- Cross-check transfn against its entry in pg_proc.
--- FIXME: what about binary-compatible types?
--- NOTE: in 7.1, this search finds max and min on abstime, which are
--- implemented using int4larger/int4smaller.  Until we have
--- some cleaner way of dealing with binary-equivalent types, just leave
--- those two tuples in the expected output.
-SELECT a.aggfnoid::oid, p.proname, p2.oid, p2.proname
-FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS p2
+-- NOTE: use physically_coercible here, not binary_coercible, because
+-- max and min on abstime are implemented using int4larger/int4smaller.
+SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname
+FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr
 WHERE a.aggfnoid = p.oid AND
-    a.aggtransfn = p2.oid AND
-    (p2.proretset OR
-     a.aggtranstype != p2.prorettype OR
-     a.aggtranstype != p2.proargtypes[0] OR
-     NOT ((p2.pronargs = 2 AND p.proargtypes[0] = p2.proargtypes[1]) OR
-          (p2.pronargs = 1 AND p.proargtypes[0] = '"any"'::regtype)))
-ORDER BY 1;
- aggfnoid | proname | oid |   proname   
-----------+---------+-----+-------------
-     2121 | max     | 768 | int4larger
-     2137 | min     | 769 | int4smaller
-(2 rows)
+    a.aggtransfn = ptr.oid AND
+    (ptr.proretset
+     OR NOT physically_coercible(ptr.prorettype, a.aggtranstype)
+     OR NOT physically_coercible(a.aggtranstype, ptr.proargtypes[0])
+     OR NOT ((ptr.pronargs = 2 AND
+              physically_coercible(p.proargtypes[0], ptr.proargtypes[1]))
+             OR
+             (ptr.pronargs = 1 AND
+              p.proargtypes[0] = '"any"'::regtype)));
+ aggfnoid | proname | oid | proname 
+----------+---------+-----+---------
+(0 rows)
 
 -- Cross-check finalfn (if present) against its entry in pg_proc.
--- FIXME: what about binary-compatible types?
-SELECT a.aggfnoid::oid, p.proname, p2.oid, p2.proname
-FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS p2
+SELECT a.aggfnoid::oid, p.proname, pfn.oid, pfn.proname
+FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS pfn
 WHERE a.aggfnoid = p.oid AND
-    a.aggfinalfn = p2.oid AND
-    (p2.proretset OR p.prorettype != p2.prorettype OR
-     p2.pronargs != 1 OR
-     a.aggtranstype != p2.proargtypes[0]);
+    a.aggfinalfn = pfn.oid AND
+    (pfn.proretset
+     OR NOT binary_coercible(pfn.prorettype, p.prorettype)
+     OR pfn.pronargs != 1
+     OR NOT binary_coercible(a.aggtranstype, pfn.proargtypes[0]));
  aggfnoid | proname | oid | proname 
 ----------+---------+-----+---------
 (0 rows)
 
 -- If transfn is strict then either initval should be non-NULL, or
--- input type should equal transtype so that the first non-null input
+-- input type should match transtype so that the first non-null input
 -- can be assigned as the state value.
-SELECT a.aggfnoid::oid, p.proname, p2.oid, p2.proname
-FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS p2
+SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname
+FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr
 WHERE a.aggfnoid = p.oid AND
-    a.aggtransfn = p2.oid AND p2.proisstrict AND
-    a.agginitval IS NULL AND p.proargtypes[0] != a.aggtranstype;
+    a.aggtransfn = ptr.oid AND ptr.proisstrict AND
+    a.agginitval IS NULL AND
+    NOT binary_coercible(p.proargtypes[0], a.aggtranstype);
  aggfnoid | proname | oid | proname 
 ----------+---------+-----+---------
 (0 rows)
@@ -714,7 +717,8 @@ WHERE p1.amopopr = p2.oid AND
 SELECT p1.amopclaid, p1.amopopr, p2.oid, p2.oprname, p3.opcname
 FROM pg_amop AS p1, pg_operator AS p2, pg_opclass AS p3
 WHERE p1.amopopr = p2.oid AND p1.amopclaid = p3.oid AND
-    (p3.opcintype != p2.oprleft OR p3.opcintype != p2.oprright);
+    (NOT binary_coercible(p3.opcintype, p2.oprleft) OR
+     p2.oprleft != p2.oprright);
  amopclaid | amopopr | oid | oprname | opcname 
 -----------+---------+-----+---------+---------
 (0 rows)
@@ -752,7 +756,8 @@ WHERE p2.opcamid = p1.oid AND
 -- signature of the function may be different for different support routines
 -- or different base data types.
 -- We can check that all the referenced instances of the same support
--- routine number take the same number of parameters, but that's about it...
+-- routine number take the same number of parameters, but that's about it
+-- for a general check...
 SELECT p1.amopclaid, p1.amprocnum,
    p2.oid, p2.proname,
    p3.opcname,
@@ -769,3 +774,22 @@ WHERE p1.amopclaid = p3.oid AND p4.amopclaid = p6.oid AND
 -----------+-----------+-----+---------+---------+-----------+-----------+-----+---------+---------
 (0 rows)
 
+-- For btree, though, we can do better since we know the support routines
+-- must be of the form cmp(input, input) returns int4.
+SELECT p1.amopclaid, p1.amprocnum,
+   p2.oid, p2.proname,
+   p3.opcname
+FROM pg_amproc AS p1, pg_proc AS p2, pg_opclass AS p3
+WHERE p3.opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree')
+    AND p1.amopclaid = p3.oid AND p1.amproc = p2.oid AND
+    (opckeytype != 0
+     OR amprocnum != 1
+     OR proretset
+     OR prorettype != 23
+     OR pronargs != 2
+     OR NOT binary_coercible(opcintype, proargtypes[0])
+     OR proargtypes[0] != proargtypes[1]);
+ amopclaid | amprocnum | oid | proname | opcname 
+-----------+-----------+-----+---------+---------
+(0 rows)
+
index f173b6cf5440cb40e50e6ae56613ad1c9bc967dc..322b84ff100b4a4ff16b204dfd5290e154cde4d9 100644 (file)
@@ -1317,9 +1317,9 @@ SELECT tablename, rulename, definition FROM pg_rules
 ---------------+-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  pg_settings   | pg_settings_n   | CREATE RULE pg_settings_n AS ON UPDATE TO pg_settings DO INSTEAD NOTHING;
  pg_settings   | pg_settings_u   | CREATE RULE pg_settings_u AS ON UPDATE TO pg_settings WHERE (new.name = old.name) DO SELECT set_config(old.name, new.setting, false) AS set_config;
- rtest_emp     | rtest_emp_del   | CREATE RULE rtest_emp_del AS ON DELETE TO rtest_emp DO INSERT INTO rtest_emplog (ename, who, "action", newsal, oldsal) VALUES (old.ename, "current_user"(), 'fired     '::bpchar, '$0.00'::money, old.salary);
- rtest_emp     | rtest_emp_ins   | CREATE RULE rtest_emp_ins AS ON INSERT TO rtest_emp DO INSERT INTO rtest_emplog (ename, who, "action", newsal, oldsal) VALUES (new.ename, "current_user"(), 'hired     '::bpchar, new.salary, '$0.00'::money);
- rtest_emp     | rtest_emp_upd   | CREATE RULE rtest_emp_upd AS ON UPDATE TO rtest_emp WHERE (new.salary <> old.salary) DO INSERT INTO rtest_emplog (ename, who, "action", newsal, oldsal) VALUES (new.ename, "current_user"(), 'honored   '::bpchar, new.salary, old.salary);
+ rtest_emp     | rtest_emp_del   | CREATE RULE rtest_emp_del AS ON DELETE TO rtest_emp DO INSERT INTO rtest_emplog (ename, who, "action", newsal, oldsal) VALUES (old.ename, "current_user"(), 'fired'::bpchar, '$0.00'::money, old.salary);
+ rtest_emp     | rtest_emp_ins   | CREATE RULE rtest_emp_ins AS ON INSERT TO rtest_emp DO INSERT INTO rtest_emplog (ename, who, "action", newsal, oldsal) VALUES (new.ename, "current_user"(), 'hired'::bpchar, new.salary, '$0.00'::money);
+ rtest_emp     | rtest_emp_upd   | CREATE RULE rtest_emp_upd AS ON UPDATE TO rtest_emp WHERE (new.salary <> old.salary) DO INSERT INTO rtest_emplog (ename, who, "action", newsal, oldsal) VALUES (new.ename, "current_user"(), 'honored'::bpchar, new.salary, old.salary);
  rtest_nothn1  | rtest_nothn_r1  | CREATE RULE rtest_nothn_r1 AS ON INSERT TO rtest_nothn1 WHERE ((new.a >= 10) AND (new.a < 20)) DO INSTEAD NOTHING;
  rtest_nothn1  | rtest_nothn_r2  | CREATE RULE rtest_nothn_r2 AS ON INSERT TO rtest_nothn1 WHERE ((new.a >= 30) AND (new.a < 40)) DO INSTEAD NOTHING;
  rtest_nothn2  | rtest_nothn_r3  | CREATE RULE rtest_nothn_r3 AS ON INSERT TO rtest_nothn2 WHERE (new.a >= 100) DO INSTEAD INSERT INTO rtest_nothn3 (a, b) VALUES (new.a, new.b);
index 4447a9df4d6f804da027e560284ac15b4b09fabc..37793d49b57a47dce0625f9b9a1f2ddf247c1ae2 100644 (file)
@@ -33,11 +33,11 @@ SELECT b, c FROM test_having
 SELECT lower(c), count(c) FROM test_having
    GROUP BY lower(c) HAVING count(*) > 2 OR min(a) = max(a)
    ORDER BY lower(c);
 lower   | count 
-----------+-------
- bbbb     |     3
- cccc     |     4
- xxxx     |     1
lower | count 
+-------+-------
+ bbbb  |     3
+ cccc  |     4
+ xxxx  |     1
 (3 rows)
 
 SELECT c, max(a) FROM test_having
index ffbb591b12533c8a77381c2658b5e894d9cf5f17..6154bcbfe86e42d3dac1794e9d3fd5df47b51531 100644 (file)
@@ -33,11 +33,11 @@ SELECT b, c FROM test_having
 SELECT lower(c), count(c) FROM test_having
    GROUP BY lower(c) HAVING count(*) > 2 OR min(a) = max(a)
    ORDER BY lower(c);
 lower   | count 
-----------+-------
- bbbb     |     3
- cccc     |     4
- xxxx     |     1
lower | count 
+-------+-------
+ bbbb  |     3
+ cccc  |     4
+ xxxx  |     1
 (3 rows)
 
 SELECT c, max(a) FROM test_having
index 47d2e80289eb77db45c00a7047ba41c5ffc7c387..d8a9edbd7810babff70a5ccb9b893b8f0ff8d5b4 100644 (file)
@@ -248,12 +248,12 @@ SELECT count(b) FROM test_missing_target GROUP BY b/2 ORDER BY b/2;
 --   w/ existing GROUP BY target using a relation name in target
 SELECT lower(test_missing_target.c), count(c)
   FROM test_missing_target GROUP BY lower(c) ORDER BY lower(c);
 lower   | count 
-----------+-------
- aaaa     |     2
- bbbb     |     3
- cccc     |     4
- xxxx     |     1
lower | count 
+-------+-------
+ aaaa  |     2
+ bbbb  |     3
+ cccc  |     4
+ xxxx  |     1
 (4 rows)
 
 --   w/o existing GROUP BY target
index 80abfd8f9a271face02cdc35b35bedbfeffa57ee..fa67bf3a5d126a33ef9a0f8e3a50c6f1f32e189d 100644 (file)
@@ -248,12 +248,12 @@ SELECT count(b) FROM test_missing_target GROUP BY b/2 ORDER BY b/2;
 --   w/ existing GROUP BY target using a relation name in target
 SELECT lower(test_missing_target.c), count(c)
   FROM test_missing_target GROUP BY lower(c) ORDER BY lower(c);
 lower   | count 
-----------+-------
- aaaa     |     2
- bbbb     |     3
- cccc     |     4
- xxxx     |     1
lower | count 
+-------+-------
+ aaaa  |     2
+ bbbb  |     3
+ cccc  |     4
+ xxxx  |     1
 (4 rows)
 
 --   w/o existing GROUP BY target
index bb999785eb12b58c2edfe21aabaeef58d0b334a4..26c18d1690b6396847ea43228d065b58511534bc 100644 (file)
@@ -26,8 +26,8 @@ ERROR:  parser: parse error at or near "' - third line'" at character 75
 SELECT CAST(f1 AS text) AS "text(char)" FROM CHAR_TBL;
  text(char) 
 ------------
- a   
- ab  
+ a
+ ab
  abcd
  abcd
 (4 rows)
@@ -88,8 +88,8 @@ SELECT CAST(f1 AS varchar) AS "varchar(text)" FROM TEXT_TBL;
 SELECT CAST(f1 AS varchar) AS "varchar(char)" FROM CHAR_TBL;
  varchar(char) 
 ---------------
- a   
- ab  
+ a
+ ab
  abcd
  abcd
 (4 rows)
@@ -570,16 +570,16 @@ SELECT text 'text' || ' and unknown' AS "Concat text to unknown type";
  text and unknown
 (1 row)
 
-SELECT char(20) 'characters' || 'and text' AS "Concat char to unknown type";
- Concat char to unknown type  
-------------------------------
- characters          and text
+SELECT char(20) 'characters' || ' and text' AS "Concat char to unknown type";
+ Concat char to unknown type 
+-----------------------------
+ characters and text
 (1 row)
 
 SELECT text 'text' || char(20) ' and characters' AS "Concat text to char";
-   Concat text to char    
---------------------------
- text and characters     
+ Concat text to char 
+---------------------
+ text and characters
 (1 row)
 
 SELECT text 'text' || varchar ' and varchar' AS "Concat text to varchar";
index a46a44becab4b2a76792d5871373fe02aeea051e..43d32298a58ff539eb66fd51651914342a805fbe 100644 (file)
@@ -203,21 +203,19 @@ SELECT f1 FROM INT4_TBL
                 123456
 (5 rows)
 
-SELECT f1 AS five FROM VARCHAR_TBL
+SELECT CAST(f1 AS char(4)) AS three FROM VARCHAR_TBL
 UNION
 SELECT f1 FROM CHAR_TBL;
- five 
-------
- a
+ three 
+-------
  a   
- ab
  ab  
  abcd
-(5 rows)
+(3 rows)
 
 SELECT f1 AS three FROM VARCHAR_TBL
 UNION
-SELECT TRIM(TRAILING FROM f1) FROM CHAR_TBL;
+SELECT CAST(f1 AS varchar) FROM CHAR_TBL;
  three 
 -------
  a
@@ -234,8 +232,8 @@ SELECT f1 FROM CHAR_TBL;
  ab
  abcd
  abcd
- a   
- ab  
+ a
+ ab
  abcd
  abcd
 (8 rows)
index 5be76aa3b41fbf1f00e13134ed2bda1643f2805c..4b7bd7b4dd5c7b100cbe7d2aa7eaf5049e4ec226 100644 (file)
 -- NB: run this test earlier than the create_operator test, because
 -- that test creates some bogus operators...
 
+
+-- Helper functions to deal with cases where binary-coercible matches are
+-- allowed.
+
+-- This should match IsBinaryCoercible() in parse_coerce.c.
+create function binary_coercible(oid, oid) returns bool as
+'SELECT ($1 = $2) OR
+ EXISTS(select 1 from pg_cast where
+        castsource = $1 and casttarget = $2 and
+        castfunc = 0 and castcontext = ''i'')'
+language sql;
+
+-- This one ignores castcontext, so it considers only physical equivalence
+-- and not whether the coercion can be invoked implicitly.
+create function physically_coercible(oid, oid) returns bool as
+'SELECT ($1 = $2) OR
+ EXISTS(select 1 from pg_cast where
+        castsource = $1 and casttarget = $2 and
+        castfunc = 0)'
+language sql;
+
 -- **************** pg_proc ****************
 
 -- Look for illegal values in pg_proc fields.
@@ -180,20 +201,15 @@ SELECT c.*
 FROM pg_cast c, pg_proc p
 WHERE c.castfunc = p.oid AND
     (p.pronargs <> 1
-     OR NOT (c.castsource = p.proargtypes[0] OR
-             EXISTS (SELECT 1 FROM pg_cast k
-                     WHERE k.castfunc = 0 AND
-                       k.castsource = c.castsource AND
-                       k.casttarget = p.proargtypes[0]))
-     OR NOT (p.prorettype = c.casttarget OR
-             EXISTS (SELECT 1 FROM pg_cast k
-                     WHERE k.castfunc = 0 AND
-                       k.castsource = p.prorettype AND
-                       k.casttarget = c.casttarget)));
+     OR NOT binary_coercible(c.castsource, p.proargtypes[0])
+     OR NOT binary_coercible(p.prorettype, c.casttarget));
 
 -- Look for binary compatible casts that do not have the reverse
 -- direction registered as well, or where the reverse direction is not
--- also binary compatible.  This is legal, but probably not intended.
+-- also binary compatible.  This is legal, but usually not intended.
+
+-- As of 7.4, this finds the casts from text and varchar to bpchar, because
+-- those are binary-compatible while the reverse way goes through rtrim().
 
 SELECT *
 FROM pg_cast c
@@ -347,15 +363,17 @@ WHERE p1.oprlsortop != p1.oprrsortop AND
 -- Hashing only works on simple equality operators "type = sametype",
 -- since the hash itself depends on the bitwise representation of the type.
 -- Check that allegedly hashable operators look like they might be "=".
--- NOTE: in 7.3, this search finds xideqint4.
--- Until we have some cleaner way of dealing with binary-equivalent types,
--- just leave that tuple in the expected output.
+
+-- NOTE: as of 7.3, this search finds xideqint4.  Since we do not mark
+-- xid and int4 as binary-equivalent in pg_cast, there's no easy way to
+-- recognize that case as OK; just leave that tuple in the expected output.
 
 SELECT p1.oid, p1.oprname
 FROM pg_operator AS p1
 WHERE p1.oprcanhash AND NOT
     (p1.oprkind = 'b' AND p1.oprresult = 'bool'::regtype AND
-     p1.oprleft = p1.oprright AND p1.oprname IN ('=', '~=~') AND p1.oprcom = p1.oid);
+     p1.oprleft = p1.oprright AND p1.oprname IN ('=', '~=~') AND
+     p1.oprcom = p1.oid);
 
 -- In 6.5 we accepted hashable array equality operators when the array element
 -- type is hashable.  However, what we actually need to make hashjoin work on
@@ -382,41 +400,33 @@ WHERE p1.oprcanhash AND p1.oprcode = p2.oid AND p2.proname = 'array_eq';
 
 -- Check that each operator defined in pg_operator matches its oprcode entry
 -- in pg_proc.  Easiest to do this separately for each oprkind.
--- FIXME: want to check that argument/result types match, but how to do that
--- in the face of binary-compatible types?
 
 SELECT p1.oid, p1.oprname, p2.oid, p2.proname
 FROM pg_operator AS p1, pg_proc AS p2
 WHERE p1.oprcode = p2.oid AND
     p1.oprkind = 'b' AND
     (p2.pronargs != 2
--- diked out until we find a way of marking binary-compatible types
--- OR
---     p1.oprresult != p2.prorettype OR
---     (p1.oprleft != p2.proargtypes[0] AND p2.proargtypes[0] != 0) OR
---     (p1.oprright != p2.proargtypes[1] AND p2.proargtypes[1] != 0)
-);
-
--- These two selects can be left as-is because there are no binary-compatible
--- cases that they trip over, at least in 6.5:
+     OR NOT binary_coercible(p2.prorettype, p1.oprresult)
+     OR NOT binary_coercible(p1.oprleft, p2.proargtypes[0])
+     OR NOT binary_coercible(p1.oprright, p2.proargtypes[1]));
 
 SELECT p1.oid, p1.oprname, p2.oid, p2.proname
 FROM pg_operator AS p1, pg_proc AS p2
 WHERE p1.oprcode = p2.oid AND
     p1.oprkind = 'l' AND
-    (p2.pronargs != 1 OR
-     p1.oprresult != p2.prorettype OR
-     (p1.oprright != p2.proargtypes[0] AND p2.proargtypes[0] != 0) OR
-     p1.oprleft != 0);
+    (p2.pronargs != 1
+     OR NOT binary_coercible(p2.prorettype, p1.oprresult)
+     OR NOT binary_coercible(p1.oprright, p2.proargtypes[0])
+     OR p1.oprleft != 0);
 
 SELECT p1.oid, p1.oprname, p2.oid, p2.proname
 FROM pg_operator AS p1, pg_proc AS p2
 WHERE p1.oprcode = p2.oid AND
     p1.oprkind = 'r' AND
-    (p2.pronargs != 1 OR
-     p1.oprresult != p2.prorettype OR
-     (p1.oprleft != p2.proargtypes[0] AND p2.proargtypes[0] != 0) OR
-     p1.oprright != 0);
+    (p2.pronargs != 1
+     OR NOT binary_coercible(p2.prorettype, p1.oprresult)
+     OR NOT binary_coercible(p1.oprleft, p2.proargtypes[0])
+     OR p1.oprright != 0);
 
 -- If the operator is mergejoinable or hashjoinable, its underlying function
 -- should not be volatile.
@@ -489,42 +499,42 @@ WHERE a.aggfnoid = p.oid AND
     a.aggfinalfn = 0 AND p.prorettype != a.aggtranstype;
 
 -- Cross-check transfn against its entry in pg_proc.
--- FIXME: what about binary-compatible types?
--- NOTE: in 7.1, this search finds max and min on abstime, which are
--- implemented using int4larger/int4smaller.  Until we have
--- some cleaner way of dealing with binary-equivalent types, just leave
--- those two tuples in the expected output.
-SELECT a.aggfnoid::oid, p.proname, p2.oid, p2.proname
-FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS p2
+-- NOTE: use physically_coercible here, not binary_coercible, because
+-- max and min on abstime are implemented using int4larger/int4smaller.
+SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname
+FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr
 WHERE a.aggfnoid = p.oid AND
-    a.aggtransfn = p2.oid AND
-    (p2.proretset OR
-     a.aggtranstype != p2.prorettype OR
-     a.aggtranstype != p2.proargtypes[0] OR
-     NOT ((p2.pronargs = 2 AND p.proargtypes[0] = p2.proargtypes[1]) OR
-          (p2.pronargs = 1 AND p.proargtypes[0] = '"any"'::regtype)))
-ORDER BY 1;
+    a.aggtransfn = ptr.oid AND
+    (ptr.proretset
+     OR NOT physically_coercible(ptr.prorettype, a.aggtranstype)
+     OR NOT physically_coercible(a.aggtranstype, ptr.proargtypes[0])
+     OR NOT ((ptr.pronargs = 2 AND
+              physically_coercible(p.proargtypes[0], ptr.proargtypes[1]))
+             OR
+             (ptr.pronargs = 1 AND
+              p.proargtypes[0] = '"any"'::regtype)));
 
 -- Cross-check finalfn (if present) against its entry in pg_proc.
--- FIXME: what about binary-compatible types?
 
-SELECT a.aggfnoid::oid, p.proname, p2.oid, p2.proname
-FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS p2
+SELECT a.aggfnoid::oid, p.proname, pfn.oid, pfn.proname
+FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS pfn
 WHERE a.aggfnoid = p.oid AND
-    a.aggfinalfn = p2.oid AND
-    (p2.proretset OR p.prorettype != p2.prorettype OR
-     p2.pronargs != 1 OR
-     a.aggtranstype != p2.proargtypes[0]);
+    a.aggfinalfn = pfn.oid AND
+    (pfn.proretset
+     OR NOT binary_coercible(pfn.prorettype, p.prorettype)
+     OR pfn.pronargs != 1
+     OR NOT binary_coercible(a.aggtranstype, pfn.proargtypes[0]));
 
 -- If transfn is strict then either initval should be non-NULL, or
--- input type should equal transtype so that the first non-null input
+-- input type should match transtype so that the first non-null input
 -- can be assigned as the state value.
 
-SELECT a.aggfnoid::oid, p.proname, p2.oid, p2.proname
-FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS p2
+SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname
+FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr
 WHERE a.aggfnoid = p.oid AND
-    a.aggtransfn = p2.oid AND p2.proisstrict AND
-    a.agginitval IS NULL AND p.proargtypes[0] != a.aggtranstype;
+    a.aggtransfn = ptr.oid AND ptr.proisstrict AND
+    a.agginitval IS NULL AND
+    NOT binary_coercible(p.proargtypes[0], a.aggtranstype);
 
 -- **************** pg_opclass ****************
 
@@ -592,7 +602,8 @@ WHERE p1.amopopr = p2.oid AND
 SELECT p1.amopclaid, p1.amopopr, p2.oid, p2.oprname, p3.opcname
 FROM pg_amop AS p1, pg_operator AS p2, pg_opclass AS p3
 WHERE p1.amopopr = p2.oid AND p1.amopclaid = p3.oid AND
-    (p3.opcintype != p2.oprleft OR p3.opcintype != p2.oprright);
+    (NOT binary_coercible(p3.opcintype, p2.oprleft) OR
+     p2.oprleft != p2.oprright);
 
 -- **************** pg_amproc ****************
 
@@ -622,7 +633,8 @@ WHERE p2.opcamid = p1.oid AND
 -- signature of the function may be different for different support routines
 -- or different base data types.
 -- We can check that all the referenced instances of the same support
--- routine number take the same number of parameters, but that's about it...
+-- routine number take the same number of parameters, but that's about it
+-- for a general check...
 
 SELECT p1.amopclaid, p1.amprocnum,
    p2.oid, p2.proname,
@@ -636,3 +648,20 @@ WHERE p1.amopclaid = p3.oid AND p4.amopclaid = p6.oid AND
     p3.opcamid = p6.opcamid AND p1.amprocnum = p4.amprocnum AND
     p1.amproc = p2.oid AND p4.amproc = p5.oid AND
     (p2.proretset OR p5.proretset OR p2.pronargs != p5.pronargs);
+
+-- For btree, though, we can do better since we know the support routines
+-- must be of the form cmp(input, input) returns int4.
+
+SELECT p1.amopclaid, p1.amprocnum,
+   p2.oid, p2.proname,
+   p3.opcname
+FROM pg_amproc AS p1, pg_proc AS p2, pg_opclass AS p3
+WHERE p3.opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree')
+    AND p1.amopclaid = p3.oid AND p1.amproc = p2.oid AND
+    (opckeytype != 0
+     OR amprocnum != 1
+     OR proretset
+     OR prorettype != 23
+     OR pronargs != 2
+     OR NOT binary_coercible(opcintype, proargtypes[0])
+     OR proargtypes[0] != proargtypes[1]);
index b84d0cb8adb00d36bb9c479a2798e41fde20d137..d2368d6ba534ab537f868996e5238ed285e684dd 100644 (file)
@@ -199,7 +199,7 @@ SELECT 'unknown' || ' and unknown' AS "Concat unknown types";
 
 SELECT text 'text' || ' and unknown' AS "Concat text to unknown type";
 
-SELECT char(20) 'characters' || 'and text' AS "Concat char to unknown type";
+SELECT char(20) 'characters' || ' and text' AS "Concat char to unknown type";
 
 SELECT text 'text' || char(20) ' and characters' AS "Concat text to char";
 
index 747d281948ff24e215eebd6d43552dd8130c425d..c69e4c3f15271334cb742b7a4c272f3fb160d435 100644 (file)
@@ -66,13 +66,13 @@ UNION
 SELECT f1 FROM INT4_TBL
   WHERE f1 BETWEEN 0 AND 1000000;
 
-SELECT f1 AS five FROM VARCHAR_TBL
+SELECT CAST(f1 AS char(4)) AS three FROM VARCHAR_TBL
 UNION
 SELECT f1 FROM CHAR_TBL;
 
 SELECT f1 AS three FROM VARCHAR_TBL
 UNION
-SELECT TRIM(TRAILING FROM f1) FROM CHAR_TBL;
+SELECT CAST(f1 AS varchar) FROM CHAR_TBL;
 
 SELECT f1 AS eight FROM VARCHAR_TBL
 UNION ALL