Make type "name" collation-aware.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 19 Dec 2018 22:35:12 +0000 (17:35 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 19 Dec 2018 22:46:25 +0000 (17:46 -0500)
The "name" comparison operators now all support collations, making them
functionally equivalent to "text" comparisons, except for the different
physical representation of the datatype.  They do, in fact, mostly share
the varstr_cmp and varstr_sortsupport infrastructure, which has been
slightly enlarged to handle the case.

To avoid changes in the default behavior of the datatype, set name's
typcollation to C_COLLATION_OID not DEFAULT_COLLATION_OID, so that
by default comparisons to a name value will continue to use strcmp
semantics.  (This would have been the case for system catalog columns
anyway, because of commit 6b0faf723, but doing this makes it true for
user-created name columns as well.  In particular, this avoids
locale-dependent changes in our regression test results.)

In consequence, tweak a couple of places that made assumptions about
collatable base types always having typcollation DEFAULT_COLLATION_OID.
I have not, however, attempted to relax the restriction that user-
defined collatable types must have that.  Hence, "name" doesn't
behave quite like a user-defined type; it acts more like a domain
with COLLATE "C".  (Conceivably, if we ever get rid of the need for
catalog name columns to be fixed-length, "name" could actually become
such a domain over text.  But that'd be a pretty massive undertaking,
and I'm not volunteering.)

Discussion: https://postgr.es/m/15938.1544377821@sss.pgh.pa.us

16 files changed:
doc/src/sgml/catalogs.sgml
src/backend/access/nbtree/nbtcompare.c
src/backend/bootstrap/bootstrap.c
src/backend/commands/explain.c
src/backend/nodes/nodeFuncs.c
src/backend/optimizer/path/indxpath.c
src/backend/parser/parse_clause.c
src/backend/utils/adt/name.c
src/backend/utils/adt/selfuncs.c
src/backend/utils/adt/varchar.c
src/backend/utils/adt/varlena.c
src/include/catalog/catversion.h
src/include/catalog/pg_type.dat
src/include/catalog/pg_type.h
src/include/utils/varlena.h
src/pl/plpgsql/src/pl_comp.c

index 8d0cab5da69a29d5c99fafac763b42da687150ca..af4d0625eab6c51eb4b36295c16953f88bd2fe0d 100644 (file)
@@ -7866,10 +7866,10 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
       <entry><para>
        <structfield>typcollation</structfield> specifies the collation
        of the type.  If the type does not support collations, this will
-       be zero.  A base type that supports collations will have
-       <symbol>DEFAULT_COLLATION_OID</symbol> here.  A domain over a
-       collatable type can have some other collation OID, if one was
-       specified for the domain.
+       be zero.  A base type that supports collations will have a nonzero
+       value here, typically <symbol>DEFAULT_COLLATION_OID</symbol>.
+       A domain over a collatable type can have a collation OID different
+       from its base type's, if one was specified for the domain.
       </para></entry>
      </row>
 
index 6f2ad23b5d7f1d89d1b211a429e35c5bbff49600..94ef6eaf90e42f27935192588b2c138db03cd809 100644 (file)
@@ -333,30 +333,3 @@ btcharcmp(PG_FUNCTION_ARGS)
    /* Be careful to compare chars as unsigned */
    PG_RETURN_INT32((int32) ((uint8) a) - (int32) ((uint8) b));
 }
-
-Datum
-btnamecmp(PG_FUNCTION_ARGS)
-{
-   Name        a = PG_GETARG_NAME(0);
-   Name        b = PG_GETARG_NAME(1);
-
-   PG_RETURN_INT32(strncmp(NameStr(*a), NameStr(*b), NAMEDATALEN));
-}
-
-static int
-btnamefastcmp(Datum x, Datum y, SortSupport ssup)
-{
-   Name        a = DatumGetName(x);
-   Name        b = DatumGetName(y);
-
-   return strncmp(NameStr(*a), NameStr(*b), NAMEDATALEN);
-}
-
-Datum
-btnamesortsupport(PG_FUNCTION_ARGS)
-{
-   SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
-
-   ssup->comparator = btnamefastcmp;
-   PG_RETURN_VOID();
-}
index 8e42255e82f6b97837bef8d857d0e0c171bc195c..fc1927c537b1da252e265b502ebb9c142f68d2f5 100644 (file)
@@ -111,7 +111,7 @@ static const struct typinfo TypInfo[] = {
    F_INT4IN, F_INT4OUT},
    {"float4", FLOAT4OID, 0, 4, FLOAT4PASSBYVAL, 'i', 'p', InvalidOid,
    F_FLOAT4IN, F_FLOAT4OUT},
-   {"name", NAMEOID, CHAROID, NAMEDATALEN, false, 'c', 'p', InvalidOid,
+   {"name", NAMEOID, CHAROID, NAMEDATALEN, false, 'c', 'p', C_COLLATION_OID,
    F_NAMEIN, F_NAMEOUT},
    {"regclass", REGCLASSOID, 0, 4, true, 'i', 'p', InvalidOid,
    F_REGCLASSIN, F_REGCLASSOUT},
index de09ded65b90630e6ca4351cfe457eefcf5a144e..094e977fb5c1f38fcb90cf67af2b6fd848eac78c 100644 (file)
@@ -14,7 +14,6 @@
 #include "postgres.h"
 
 #include "access/xact.h"
-#include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
 #include "commands/createas.h"
 #include "commands/defrem.h"
@@ -2347,11 +2346,13 @@ show_sortorder_options(StringInfo buf, Node *sortexpr,
                                 TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
 
    /*
-    * Print COLLATE if it's not default.  There are some cases where this is
-    * redundant, eg if expression is a column whose declared collation is
-    * that collation, but it's hard to distinguish that here.
+    * Print COLLATE if it's not default for the column's type.  There are
+    * some cases where this is redundant, eg if expression is a column whose
+    * declared collation is that collation, but it's hard to distinguish that
+    * here (and arguably, printing COLLATE explicitly is a good idea anyway
+    * in such cases).
     */
-   if (OidIsValid(collation) && collation != DEFAULT_COLLATION_OID)
+   if (OidIsValid(collation) && collation != get_typcollation(sortcoltype))
    {
        char       *collname = get_collation_name(collation);
 
index 54b3dcff3ec3c91a112710efd3117314752039d5..a813e3879171a875dc8caeae1fd2f176228c68e1 100644 (file)
@@ -862,7 +862,11 @@ exprCollation(const Node *expr)
            coll = ((const MinMaxExpr *) expr)->minmaxcollid;
            break;
        case T_SQLValueFunction:
-           coll = InvalidOid;  /* all cases return non-collatable types */
+           /* Returns either NAME or a non-collatable type */
+           if (((const SQLValueFunction *) expr)->type == NAMEOID)
+               coll = C_COLLATION_OID;
+           else
+               coll = InvalidOid;
            break;
        case T_XmlExpr:
 
@@ -1075,7 +1079,9 @@ exprSetCollation(Node *expr, Oid collation)
            ((MinMaxExpr *) expr)->minmaxcollid = collation;
            break;
        case T_SQLValueFunction:
-           Assert(!OidIsValid(collation)); /* no collatable results */
+           Assert((((SQLValueFunction *) expr)->type == NAMEOID) ?
+                  (collation == C_COLLATION_OID) :
+                  (collation == InvalidOid));
            break;
        case T_XmlExpr:
            Assert((((XmlExpr *) expr)->op == IS_XMLSERIALIZE) ?
index 5f464153942314731a93dc909c4c7bcbaa51ffab..965d9648e5d35a6b54f431ff9357cb29bede05dc 100644 (file)
@@ -4344,7 +4344,7 @@ string_to_const(const char *str, Oid datatype)
            break;
 
        case NAMEOID:
-           collation = InvalidOid;
+           collation = C_COLLATION_OID;
            constlen = NAMEDATALEN;
            break;
 
index 4ba51203a6a505b45b71e10ca7787a2ac08c0a44..20997241bac1e01c937fe4ef8630b38c4d353681 100644 (file)
@@ -788,7 +788,7 @@ transformRangeTableFunc(ParseState *pstate, RangeTableFunc *rtf)
        tf->coltypes = lappend_oid(tf->coltypes, typid);
        tf->coltypmods = lappend_int(tf->coltypmods, typmod);
        tf->colcollations = lappend_oid(tf->colcollations,
-                                       type_is_collatable(typid) ? DEFAULT_COLLATION_OID : InvalidOid);
+                                       get_typcollation(typid));
 
        /* Transform the PATH and DEFAULT expressions */
        if (rawc->colexpr)
index c266da2de1ce1b015eafe382d2e6dbb234a351f4..0cf8b9d46b5a6532626372dc0faa07c4a4959820 100644 (file)
@@ -21,6 +21,7 @@
 #include "postgres.h"
 
 #include "catalog/namespace.h"
+#include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
 #include "libpq/pqformat.h"
 #include "mb/pg_wchar.h"
@@ -28,6 +29,7 @@
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
+#include "utils/varlena.h"
 
 
 /*****************************************************************************
@@ -113,22 +115,21 @@ namesend(PG_FUNCTION_ARGS)
 
 
 /*****************************************************************************
- *  PUBLIC ROUTINES                                                         *
+ *  COMPARISON/SORTING ROUTINES                                             *
  *****************************************************************************/
 
 /*
  *     nameeq  - returns 1 iff arguments are equal
  *     namene  - returns 1 iff arguments are not equal
- *
- *     BUGS:
- *             Assumes that "xy\0\0a" should be equal to "xy\0b".
- *             If not, can do the comparison backwards for efficiency.
- *
  *     namelt  - returns 1 iff a < b
  *     namele  - returns 1 iff a <= b
  *     namegt  - returns 1 iff a > b
  *     namege  - returns 1 iff a >= b
  *
+ * Note that the use of strncmp with NAMEDATALEN limit is mostly historical;
+ * strcmp would do as well, because we do not allow NAME values that don't
+ * have a '\0' terminator.  Whatever might be past the terminator is not
+ * considered relevant to comparisons.
  */
 Datum
 nameeq(PG_FUNCTION_ARGS)
@@ -136,6 +137,7 @@ nameeq(PG_FUNCTION_ARGS)
    Name        arg1 = PG_GETARG_NAME(0);
    Name        arg2 = PG_GETARG_NAME(1);
 
+   /* Collation doesn't matter: equal only if bitwise-equal */
    PG_RETURN_BOOL(strncmp(NameStr(*arg1), NameStr(*arg2), NAMEDATALEN) == 0);
 }
 
@@ -145,16 +147,30 @@ namene(PG_FUNCTION_ARGS)
    Name        arg1 = PG_GETARG_NAME(0);
    Name        arg2 = PG_GETARG_NAME(1);
 
+   /* Collation doesn't matter: equal only if bitwise-equal */
    PG_RETURN_BOOL(strncmp(NameStr(*arg1), NameStr(*arg2), NAMEDATALEN) != 0);
 }
 
+static int
+namecmp(Name arg1, Name arg2, Oid collid)
+{
+   /* Fast path for common case used in system catalogs */
+   if (collid == C_COLLATION_OID)
+       return strncmp(NameStr(*arg1), NameStr(*arg2), NAMEDATALEN);
+
+   /* Else rely on the varstr infrastructure */
+   return varstr_cmp(NameStr(*arg1), strlen(NameStr(*arg1)),
+                     NameStr(*arg2), strlen(NameStr(*arg2)),
+                     collid);
+}
+
 Datum
 namelt(PG_FUNCTION_ARGS)
 {
    Name        arg1 = PG_GETARG_NAME(0);
    Name        arg2 = PG_GETARG_NAME(1);
 
-   PG_RETURN_BOOL(strncmp(NameStr(*arg1), NameStr(*arg2), NAMEDATALEN) < 0);
+   PG_RETURN_BOOL(namecmp(arg1, arg2, PG_GET_COLLATION()) < 0);
 }
 
 Datum
@@ -163,7 +179,7 @@ namele(PG_FUNCTION_ARGS)
    Name        arg1 = PG_GETARG_NAME(0);
    Name        arg2 = PG_GETARG_NAME(1);
 
-   PG_RETURN_BOOL(strncmp(NameStr(*arg1), NameStr(*arg2), NAMEDATALEN) <= 0);
+   PG_RETURN_BOOL(namecmp(arg1, arg2, PG_GET_COLLATION()) <= 0);
 }
 
 Datum
@@ -172,7 +188,7 @@ namegt(PG_FUNCTION_ARGS)
    Name        arg1 = PG_GETARG_NAME(0);
    Name        arg2 = PG_GETARG_NAME(1);
 
-   PG_RETURN_BOOL(strncmp(NameStr(*arg1), NameStr(*arg2), NAMEDATALEN) > 0);
+   PG_RETURN_BOOL(namecmp(arg1, arg2, PG_GET_COLLATION()) > 0);
 }
 
 Datum
@@ -181,11 +197,39 @@ namege(PG_FUNCTION_ARGS)
    Name        arg1 = PG_GETARG_NAME(0);
    Name        arg2 = PG_GETARG_NAME(1);
 
-   PG_RETURN_BOOL(strncmp(NameStr(*arg1), NameStr(*arg2), NAMEDATALEN) >= 0);
+   PG_RETURN_BOOL(namecmp(arg1, arg2, PG_GET_COLLATION()) >= 0);
+}
+
+Datum
+btnamecmp(PG_FUNCTION_ARGS)
+{
+   Name        arg1 = PG_GETARG_NAME(0);
+   Name        arg2 = PG_GETARG_NAME(1);
+
+   PG_RETURN_INT32(namecmp(arg1, arg2, PG_GET_COLLATION()));
 }
 
+Datum
+btnamesortsupport(PG_FUNCTION_ARGS)
+{
+   SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
+   Oid         collid = ssup->ssup_collation;
+   MemoryContext oldcontext;
+
+   oldcontext = MemoryContextSwitchTo(ssup->ssup_cxt);
+
+   /* Use generic string SortSupport */
+   varstr_sortsupport(ssup, NAMEOID, collid);
 
-/* (see char.c for comparison/operation routines) */
+   MemoryContextSwitchTo(oldcontext);
+
+   PG_RETURN_VOID();
+}
+
+
+/*****************************************************************************
+ *  MISCELLANEOUS PUBLIC ROUTINES                                           *
+ *****************************************************************************/
 
 int
 namecpy(Name n1, const NameData *n2)
@@ -204,14 +248,6 @@ namecat(Name n1, Name n2)
 }
 #endif
 
-#ifdef NOT_USED
-int
-namecmp(Name n1, Name n2)
-{
-   return strncmp(NameStr(*n1), NameStr(*n2), NAMEDATALEN);
-}
-#endif
-
 int
 namestrcpy(Name name, const char *str)
 {
@@ -243,6 +279,12 @@ namestrcat(Name name, const char *str)
 }
 #endif
 
+/*
+ * Compare a NAME to a C string
+ *
+ * Assumes C collation always; be careful when using this for
+ * anything but equality checks!
+ */
 int
 namestrcmp(Name name, const char *str)
 {
index c3db9ea070b340797432686547e9567c87ac07a1..ebedb269832bf662a0f253d565800508923010aa 100644 (file)
@@ -6341,23 +6341,16 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc, Oid collation)
    char       *workstr;
    int         len;
    Datum       cmpstr;
-   text       *cmptxt = NULL;
+   char       *cmptxt = NULL;
    mbcharacter_incrementer charinc;
 
    /*
     * Get a modifiable copy of the prefix string in C-string format, and set
     * up the string we will compare to as a Datum.  In C locale this can just
-    * be the given prefix string, otherwise we need to add a suffix.  Types
-    * NAME and BYTEA sort bytewise so they don't need a suffix either.
+    * be the given prefix string, otherwise we need to add a suffix.  Type
+    * BYTEA sorts bytewise so it never needs a suffix either.
     */
-   if (datatype == NAMEOID)
-   {
-       workstr = DatumGetCString(DirectFunctionCall1(nameout,
-                                                     str_const->constvalue));
-       len = strlen(workstr);
-       cmpstr = str_const->constvalue;
-   }
-   else if (datatype == BYTEAOID)
+   if (datatype == BYTEAOID)
    {
        bytea      *bstr = DatumGetByteaPP(str_const->constvalue);
 
@@ -6369,7 +6362,11 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc, Oid collation)
    }
    else
    {
-       workstr = TextDatumGetCString(str_const->constvalue);
+       if (datatype == NAMEOID)
+           workstr = DatumGetCString(DirectFunctionCall1(nameout,
+                                                         str_const->constvalue));
+       else
+           workstr = TextDatumGetCString(str_const->constvalue);
        len = strlen(workstr);
        if (lc_collate_is_c(collation) || len == 0)
            cmpstr = str_const->constvalue;
@@ -6395,11 +6392,22 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc, Oid collation)
            }
 
            /* And build the string to compare to */
-           cmptxt = (text *) palloc(VARHDRSZ + len + 1);
-           SET_VARSIZE(cmptxt, VARHDRSZ + len + 1);
-           memcpy(VARDATA(cmptxt), workstr, len);
-           *(VARDATA(cmptxt) + len) = suffixchar;
-           cmpstr = PointerGetDatum(cmptxt);
+           if (datatype == NAMEOID)
+           {
+               cmptxt = palloc(len + 2);
+               memcpy(cmptxt, workstr, len);
+               cmptxt[len] = suffixchar;
+               cmptxt[len + 1] = '\0';
+               cmpstr = PointerGetDatum(cmptxt);
+           }
+           else
+           {
+               cmptxt = palloc(VARHDRSZ + len + 1);
+               SET_VARSIZE(cmptxt, VARHDRSZ + len + 1);
+               memcpy(VARDATA(cmptxt), workstr, len);
+               *(VARDATA(cmptxt) + len) = suffixchar;
+               cmpstr = PointerGetDatum(cmptxt);
+           }
        }
    }
 
@@ -6518,7 +6526,7 @@ string_to_const(const char *str, Oid datatype)
            break;
 
        case NAMEOID:
-           collation = InvalidOid;
+           collation = C_COLLATION_OID;
            constlen = NAMEDATALEN;
            break;
 
index 8f07b1e2729b3a611fcda4f2955e9afb0e7f24af..d5a5800cb2f0210073956b996ce6f0629e7a4142 100644 (file)
@@ -18,6 +18,7 @@
 #include "access/hash.h"
 #include "access/tuptoaster.h"
 #include "catalog/pg_collation.h"
+#include "catalog/pg_type.h"
 #include "libpq/pqformat.h"
 #include "nodes/nodeFuncs.h"
 #include "utils/array.h"
@@ -876,7 +877,7 @@ bpchar_sortsupport(PG_FUNCTION_ARGS)
    oldcontext = MemoryContextSwitchTo(ssup->ssup_cxt);
 
    /* Use generic string SortSupport */
-   varstr_sortsupport(ssup, collid, true);
+   varstr_sortsupport(ssup, BPCHAROID, collid);
 
    MemoryContextSwitchTo(oldcontext);
 
@@ -1085,7 +1086,7 @@ btbpchar_pattern_sortsupport(PG_FUNCTION_ARGS)
    oldcontext = MemoryContextSwitchTo(ssup->ssup_cxt);
 
    /* Use generic string SortSupport, forcing "C" collation */
-   varstr_sortsupport(ssup, C_COLLATION_OID, true);
+   varstr_sortsupport(ssup, BPCHAROID, C_COLLATION_OID);
 
    MemoryContextSwitchTo(oldcontext);
 
index 0fd3b157482d8ae09a0a03de51f3e8556f287306..a4fb5885c7e93e05d31750e844a749ae80fc6324 100644 (file)
@@ -69,7 +69,7 @@ typedef struct
    int         last_returned;  /* Last comparison result (cache) */
    bool        cache_blob;     /* Does buf2 contain strxfrm() blob, etc? */
    bool        collate_c;
-   bool        bpchar;         /* Sorting bpchar, not varchar/text/bytea? */
+   Oid         typeid;         /* Actual datatype (text/bpchar/bytea/name) */
    hyperLogLogState abbr_card; /* Abbreviated key cardinality state */
    hyperLogLogState full_card; /* Full key cardinality state */
    double      prop_card;      /* Required cardinality proportion */
@@ -93,7 +93,10 @@ typedef struct
 
 static int varstrfastcmp_c(Datum x, Datum y, SortSupport ssup);
 static int bpcharfastcmp_c(Datum x, Datum y, SortSupport ssup);
-static int varstrfastcmp_locale(Datum x, Datum y, SortSupport ssup);
+static int namefastcmp_c(Datum x, Datum y, SortSupport ssup);
+static int varlenafastcmp_locale(Datum x, Datum y, SortSupport ssup);
+static int namefastcmp_locale(Datum x, Datum y, SortSupport ssup);
+static int varstrfastcmp_locale(char *a1p, int len1, char *a2p, int len2, SortSupport ssup);
 static int varstrcmp_abbrev(Datum x, Datum y, SortSupport ssup);
 static Datum varstr_abbrev_convert(Datum original, SortSupport ssup);
 static bool varstr_abbrev_abort(int memtupcount, SortSupport ssup);
@@ -1814,7 +1817,7 @@ bttextsortsupport(PG_FUNCTION_ARGS)
    oldcontext = MemoryContextSwitchTo(ssup->ssup_cxt);
 
    /* Use generic string SortSupport */
-   varstr_sortsupport(ssup, collid, false);
+   varstr_sortsupport(ssup, TEXTOID, collid);
 
    MemoryContextSwitchTo(oldcontext);
 
@@ -1832,7 +1835,7 @@ bttextsortsupport(PG_FUNCTION_ARGS)
  * this will not work with any other collation, though.
  */
 void
-varstr_sortsupport(SortSupport ssup, Oid collid, bool bpchar)
+varstr_sortsupport(SortSupport ssup, Oid typeid, Oid collid)
 {
    bool        abbreviate = ssup->abbreviate;
    bool        collate_c = false;
@@ -1845,18 +1848,25 @@ varstr_sortsupport(SortSupport ssup, Oid collid, bool bpchar)
     * overhead of a trip through the fmgr layer for every comparison, which
     * can be substantial.
     *
-    * Most typically, we'll set the comparator to varstrfastcmp_locale, which
-    * uses strcoll() to perform comparisons and knows about the special
-    * requirements of BpChar callers.  However, if LC_COLLATE = C, we can
-    * make things quite a bit faster with varstrfastcmp_c or bpcharfastcmp_c,
-    * both of which use memcmp() rather than strcoll().
+    * Most typically, we'll set the comparator to varlenafastcmp_locale,
+    * which uses strcoll() to perform comparisons.  We use that for the
+    * BpChar case too, but type NAME uses namefastcmp_locale. However, if
+    * LC_COLLATE = C, we can make things quite a bit faster with
+    * varstrfastcmp_c, bpcharfastcmp_c, or namefastcmp_c, all of which use
+    * memcmp() rather than strcoll().
     */
    if (lc_collate_is_c(collid))
    {
-       if (!bpchar)
-           ssup->comparator = varstrfastcmp_c;
-       else
+       if (typeid == BPCHAROID)
            ssup->comparator = bpcharfastcmp_c;
+       else if (typeid == NAMEOID)
+       {
+           ssup->comparator = namefastcmp_c;
+           /* Not supporting abbreviation with type NAME, for now */
+           abbreviate = false;
+       }
+       else
+           ssup->comparator = varstrfastcmp_c;
 
        collate_c = true;
    }
@@ -1897,7 +1907,17 @@ varstr_sortsupport(SortSupport ssup, Oid collid, bool bpchar)
            return;
 #endif
 
-       ssup->comparator = varstrfastcmp_locale;
+       /*
+        * We use varlenafastcmp_locale except for type NAME.
+        */
+       if (typeid == NAMEOID)
+       {
+           ssup->comparator = namefastcmp_locale;
+           /* Not supporting abbreviation with type NAME, for now */
+           abbreviate = false;
+       }
+       else
+           ssup->comparator = varlenafastcmp_locale;
    }
 
    /*
@@ -1963,7 +1983,7 @@ varstr_sortsupport(SortSupport ssup, Oid collid, bool bpchar)
         */
        sss->cache_blob = true;
        sss->collate_c = collate_c;
-       sss->bpchar = bpchar;
+       sss->typeid = typeid;
        ssup->ssup_extra = sss;
 
        /*
@@ -2055,17 +2075,25 @@ bpcharfastcmp_c(Datum x, Datum y, SortSupport ssup)
 }
 
 /*
- * sortsupport comparison func (for locale case)
+ * sortsupport comparison func (for NAME C locale case)
+ */
+static int
+namefastcmp_c(Datum x, Datum y, SortSupport ssup)
+{
+   Name        arg1 = DatumGetName(x);
+   Name        arg2 = DatumGetName(y);
+
+   return strncmp(NameStr(*arg1), NameStr(*arg2), NAMEDATALEN);
+}
+
+/*
+ * sortsupport comparison func (for locale case with all varlena types)
  */
 static int
-varstrfastcmp_locale(Datum x, Datum y, SortSupport ssup)
+varlenafastcmp_locale(Datum x, Datum y, SortSupport ssup)
 {
    VarString  *arg1 = DatumGetVarStringPP(x);
    VarString  *arg2 = DatumGetVarStringPP(y);
-   bool        arg1_match;
-   VarStringSortSupport *sss = (VarStringSortSupport *) ssup->ssup_extra;
-
-   /* working state */
    char       *a1p,
               *a2p;
    int         len1,
@@ -2078,6 +2106,41 @@ varstrfastcmp_locale(Datum x, Datum y, SortSupport ssup)
    len1 = VARSIZE_ANY_EXHDR(arg1);
    len2 = VARSIZE_ANY_EXHDR(arg2);
 
+   result = varstrfastcmp_locale(a1p, len1, a2p, len2, ssup);
+
+   /* We can't afford to leak memory here. */
+   if (PointerGetDatum(arg1) != x)
+       pfree(arg1);
+   if (PointerGetDatum(arg2) != y)
+       pfree(arg2);
+
+   return result;
+}
+
+/*
+ * sortsupport comparison func (for locale case with NAME type)
+ */
+static int
+namefastcmp_locale(Datum x, Datum y, SortSupport ssup)
+{
+   Name        arg1 = DatumGetName(x);
+   Name        arg2 = DatumGetName(y);
+
+   return varstrfastcmp_locale(NameStr(*arg1), strlen(NameStr(*arg1)),
+                               NameStr(*arg2), strlen(NameStr(*arg2)),
+                               ssup);
+}
+
+/*
+ * sortsupport comparison func for locale cases
+ */
+static int
+varstrfastcmp_locale(char *a1p, int len1, char *a2p, int len2, SortSupport ssup)
+{
+   VarStringSortSupport *sss = (VarStringSortSupport *) ssup->ssup_extra;
+   int         result;
+   bool        arg1_match;
+
    /* Fast pre-check for equality, as discussed in varstr_cmp() */
    if (len1 == len2 && memcmp(a1p, a2p, len1) == 0)
    {
@@ -2094,11 +2157,10 @@ varstrfastcmp_locale(Datum x, Datum y, SortSupport ssup)
         * (not limited to padding), so we need make no distinction between
         * padding space characters and "real" space characters.
         */
-       result = 0;
-       goto done;
+       return 0;
    }
 
-   if (sss->bpchar)
+   if (sss->typeid == BPCHAROID)
    {
        /* Get true number of bytes, ignoring trailing spaces */
        len1 = bpchartruelen(a1p, len1);
@@ -2152,8 +2214,7 @@ varstrfastcmp_locale(Datum x, Datum y, SortSupport ssup)
    else if (arg1_match && !sss->cache_blob)
    {
        /* Use result cached following last actual strcoll() call */
-       result = sss->last_returned;
-       goto done;
+       return sss->last_returned;
    }
 
    if (sss->locale)
@@ -2222,13 +2283,6 @@ varstrfastcmp_locale(Datum x, Datum y, SortSupport ssup)
    /* Cache result, perhaps saving an expensive strcoll() call next time */
    sss->cache_blob = false;
    sss->last_returned = result;
-done:
-   /* We can't afford to leak memory here. */
-   if (PointerGetDatum(arg1) != x)
-       pfree(arg1);
-   if (PointerGetDatum(arg2) != y)
-       pfree(arg2);
-
    return result;
 }
 
@@ -2240,7 +2294,7 @@ varstrcmp_abbrev(Datum x, Datum y, SortSupport ssup)
 {
    /*
     * When 0 is returned, the core system will call varstrfastcmp_c()
-    * (bpcharfastcmp_c() in BpChar case) or varstrfastcmp_locale().  Even a
+    * (bpcharfastcmp_c() in BpChar case) or varlenafastcmp_locale().  Even a
     * strcmp() on two non-truncated strxfrm() blobs cannot indicate *equality*
     * authoritatively, for the same reason that there is a strcoll()
     * tie-breaker call to strcmp() in varstr_cmp().
@@ -2279,7 +2333,7 @@ varstr_abbrev_convert(Datum original, SortSupport ssup)
    len = VARSIZE_ANY_EXHDR(authoritative);
 
    /* Get number of bytes, ignoring trailing spaces */
-   if (sss->bpchar)
+   if (sss->typeid == BPCHAROID)
        len = bpchartruelen(authoritative_data, len);
 
    /*
@@ -2758,7 +2812,7 @@ bttext_pattern_sortsupport(PG_FUNCTION_ARGS)
    oldcontext = MemoryContextSwitchTo(ssup->ssup_cxt);
 
    /* Use generic string SortSupport, forcing "C" collation */
-   varstr_sortsupport(ssup, C_COLLATION_OID, false);
+   varstr_sortsupport(ssup, TEXTOID, C_COLLATION_OID);
 
    MemoryContextSwitchTo(oldcontext);
 
@@ -3798,7 +3852,7 @@ bytea_sortsupport(PG_FUNCTION_ARGS)
    oldcontext = MemoryContextSwitchTo(ssup->ssup_cxt);
 
    /* Use generic string SortSupport, forcing "C" collation */
-   varstr_sortsupport(ssup, C_COLLATION_OID, false);
+   varstr_sortsupport(ssup, BYTEAOID, C_COLLATION_OID);
 
    MemoryContextSwitchTo(oldcontext);
 
index aa27471437a7c0a17ea7c7e757f9164b84cdd440..887a620d33f065024318638761924a601537093d 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 201812182
+#define CATALOG_VERSION_NO 201812191
 
 #endif
index d295eae1b99aa3994034f60b9e7a0cf8c2bdd752..7bca400ec239a73d2c89113bc6cf734a7391453e 100644 (file)
@@ -54,7 +54,7 @@
   typname => 'name', typlen => 'NAMEDATALEN', typbyval => 'f',
   typcategory => 'S', typelem => 'char', typinput => 'namein',
   typoutput => 'nameout', typreceive => 'namerecv', typsend => 'namesend',
-  typalign => 'c' },
+  typalign => 'c', typcollation => '950' },
 { oid => '20', array_type_oid => '1016',
   descr => '~18 digit integer, 8-byte storage',
   typname => 'int8', typlen => '8', typbyval => 'FLOAT8PASSBYVAL',
index 05185dd809dc4cecbea8627230255719c45b3501..43bc08ee5280f824429fb51a1e94d699fe3b022f 100644 (file)
@@ -211,8 +211,9 @@ CATALOG(pg_type,1247,TypeRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71,TypeRelati
    int32       typndims BKI_DEFAULT(0);
 
    /*
-    * Collation: 0 if type cannot use collations, DEFAULT_COLLATION_OID for
-    * collatable base types, possibly other OID for domains
+    * Collation: 0 if type cannot use collations, nonzero (typically
+    * DEFAULT_COLLATION_OID) for collatable base types, possibly some other
+    * OID for domains over collatable types
     */
    Oid         typcollation BKI_DEFAULT(0);
 
index c776931bc4823322577648116e58e42a8cf48ff4..ee3e205b7a4c95a1b8f3ea5d879a4277891c0dac 100644 (file)
@@ -17,7 +17,7 @@
 #include "utils/sortsupport.h"
 
 extern int varstr_cmp(const char *arg1, int len1, const char *arg2, int len2, Oid collid);
-extern void varstr_sortsupport(SortSupport ssup, Oid collid, bool bpchar);
+extern void varstr_sortsupport(SortSupport ssup, Oid typeid, Oid collid);
 extern int varstr_levenshtein(const char *source, int slen,
                   const char *target, int tlen,
                   int ins_c, int del_c, int sub_c,
index 8bacc74cce1e05b319711b557c21e76ea5b9a583..fb3fb5b8164cdd6e9fc83446e00f1b30175854c8 100644 (file)
@@ -607,7 +607,7 @@ do_compile(FunctionCallInfo fcinfo,
            var = plpgsql_build_variable("tg_name", 0,
                                         plpgsql_build_datatype(NAMEOID,
                                                                -1,
-                                                               InvalidOid),
+                                                               function->fn_input_collation),
                                         true);
            Assert(var->dtype == PLPGSQL_DTYPE_VAR);
            var->dtype = PLPGSQL_DTYPE_PROMISE;
@@ -657,7 +657,7 @@ do_compile(FunctionCallInfo fcinfo,
            var = plpgsql_build_variable("tg_relname", 0,
                                         plpgsql_build_datatype(NAMEOID,
                                                                -1,
-                                                               InvalidOid),
+                                                               function->fn_input_collation),
                                         true);
            Assert(var->dtype == PLPGSQL_DTYPE_VAR);
            var->dtype = PLPGSQL_DTYPE_PROMISE;
@@ -667,7 +667,7 @@ do_compile(FunctionCallInfo fcinfo,
            var = plpgsql_build_variable("tg_table_name", 0,
                                         plpgsql_build_datatype(NAMEOID,
                                                                -1,
-                                                               InvalidOid),
+                                                               function->fn_input_collation),
                                         true);
            Assert(var->dtype == PLPGSQL_DTYPE_VAR);
            var->dtype = PLPGSQL_DTYPE_PROMISE;
@@ -677,7 +677,7 @@ do_compile(FunctionCallInfo fcinfo,
            var = plpgsql_build_variable("tg_table_schema", 0,
                                         plpgsql_build_datatype(NAMEOID,
                                                                -1,
-                                                               InvalidOid),
+                                                               function->fn_input_collation),
                                         true);
            Assert(var->dtype == PLPGSQL_DTYPE_VAR);
            var->dtype = PLPGSQL_DTYPE_PROMISE;