Per-column collation support
authorPeter Eisentraut
Tue, 8 Feb 2011 21:04:18 +0000 (23:04 +0200)
committerPeter Eisentraut
Tue, 8 Feb 2011 21:04:18 +0000 (23:04 +0200)
This adds collation support for columns and domains, a COLLATE clause
to override it per expression, and B-tree index support.

Peter Eisentraut
reviewed by Pavel Stehule, Itagaki Takahiro, Robert Haas, Noah Misch

156 files changed:
config/c-library.m4
configure
configure.in
contrib/btree_gin/btree_gin.c
contrib/btree_gist/btree_text.c
contrib/btree_gist/btree_utils_var.c
contrib/citext/citext.c
contrib/citext/citext.sql.in
contrib/ltree/lquery_op.c
doc/src/sgml/catalogs.sgml
doc/src/sgml/charset.sgml
doc/src/sgml/func.sgml
doc/src/sgml/indices.sgml
doc/src/sgml/ref/create_domain.sgml
doc/src/sgml/ref/create_index.sgml
doc/src/sgml/ref/create_table.sgml
doc/src/sgml/ref/create_type.sgml
doc/src/sgml/regress.sgml
doc/src/sgml/syntax.sgml
src/backend/access/common/scankey.c
src/backend/access/common/tupdesc.c
src/backend/access/gin/ginutil.c
src/backend/access/index/indexam.c
src/backend/access/nbtree/nbtsearch.c
src/backend/bootstrap/bootstrap.c
src/backend/catalog/Makefile
src/backend/catalog/genbki.pl
src/backend/catalog/heap.c
src/backend/catalog/index.c
src/backend/catalog/namespace.c
src/backend/catalog/pg_type.c
src/backend/catalog/system_views.sql
src/backend/catalog/toasting.c
src/backend/commands/analyze.c
src/backend/commands/dbcommands.c
src/backend/commands/functioncmds.c
src/backend/commands/indexcmds.c
src/backend/commands/seclabel.c
src/backend/commands/sequence.c
src/backend/commands/tablecmds.c
src/backend/commands/typecmds.c
src/backend/commands/view.c
src/backend/executor/execQual.c
src/backend/executor/execTuples.c
src/backend/executor/nodeAgg.c
src/backend/executor/nodeFunctionscan.c
src/backend/executor/nodeIndexscan.c
src/backend/executor/nodeMergeAppend.c
src/backend/executor/nodeMergejoin.c
src/backend/executor/nodeSort.c
src/backend/executor/nodeSubplan.c
src/backend/executor/nodeWindowAgg.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/makefuncs.c
src/backend/nodes/nodeFuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/path/costsize.c
src/backend/optimizer/path/indxpath.c
src/backend/optimizer/path/pathkeys.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/planagg.c
src/backend/optimizer/plan/setrefs.c
src/backend/optimizer/plan/subselect.c
src/backend/optimizer/prep/prepjointree.c
src/backend/optimizer/prep/preptlist.c
src/backend/optimizer/prep/prepunion.c
src/backend/optimizer/util/clauses.c
src/backend/optimizer/util/plancat.c
src/backend/optimizer/util/predtest.c
src/backend/parser/analyze.c
src/backend/parser/gram.y
src/backend/parser/parse_agg.c
src/backend/parser/parse_clause.c
src/backend/parser/parse_coerce.c
src/backend/parser/parse_cte.c
src/backend/parser/parse_expr.c
src/backend/parser/parse_func.c
src/backend/parser/parse_node.c
src/backend/parser/parse_oper.c
src/backend/parser/parse_param.c
src/backend/parser/parse_relation.c
src/backend/parser/parse_target.c
src/backend/parser/parse_type.c
src/backend/parser/parse_utilcmd.c
src/backend/rewrite/rewriteHandler.c
src/backend/tsearch/ts_locale.c
src/backend/tsearch/wparser_def.c
src/backend/utils/adt/arrayfuncs.c
src/backend/utils/adt/format_type.c
src/backend/utils/adt/formatting.c
src/backend/utils/adt/like.c
src/backend/utils/adt/oracle_compat.c
src/backend/utils/adt/pg_locale.c
src/backend/utils/adt/ruleutils.c
src/backend/utils/adt/selfuncs.c
src/backend/utils/adt/varchar.c
src/backend/utils/adt/varlena.c
src/backend/utils/cache/lsyscache.c
src/backend/utils/cache/relcache.c
src/backend/utils/cache/syscache.c
src/backend/utils/errcodes.txt
src/backend/utils/fmgr/fmgr.c
src/backend/utils/fmgr/funcapi.c
src/backend/utils/mb/mbutils.c
src/backend/utils/sort/tuplesort.c
src/bin/initdb/initdb.c
src/bin/pg_dump/pg_dump.c
src/bin/psql/describe.c
src/include/access/skey.h
src/include/access/tupdesc.h
src/include/catalog/catversion.h
src/include/catalog/index.h
src/include/catalog/indexing.h
src/include/catalog/namespace.h
src/include/catalog/pg_attribute.h
src/include/catalog/pg_class.h
src/include/catalog/pg_collation.h [new file with mode: 0644]
src/include/catalog/pg_index.h
src/include/catalog/pg_proc.h
src/include/catalog/pg_type.h
src/include/catalog/pg_type_fn.h
src/include/commands/vacuum.h
src/include/fmgr.h
src/include/mb/pg_wchar.h
src/include/nodes/makefuncs.h
src/include/nodes/nodeFuncs.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/nodes/pg_list.h
src/include/nodes/plannodes.h
src/include/nodes/primnodes.h
src/include/nodes/relation.h
src/include/optimizer/subselect.h
src/include/parser/parse_agg.h
src/include/parser/parse_coerce.h
src/include/parser/parse_node.h
src/include/parser/parse_type.h
src/include/parser/parsetree.h
src/include/pg_config.h.in
src/include/port.h
src/include/utils/builtins.h
src/include/utils/formatting.h
src/include/utils/lsyscache.h
src/include/utils/pg_locale.h
src/include/utils/rel.h
src/include/utils/selfuncs.h
src/include/utils/syscache.h
src/include/utils/tuplesort.h
src/pl/plpgsql/src/pl_comp.c
src/port/chklocale.c
src/test/regress/GNUmakefile
src/test/regress/expected/collate.linux.utf8.out [new file with mode: 0644]
src/test/regress/expected/sanity_check.out
src/test/regress/sql/collate.linux.utf8.sql [new file with mode: 0644]

index 98e03e3d182c588ca081808b0ba64c60416d6c30..cddeafaec232fa0a0f37410f33145b88cb833162 100644 (file)
@@ -297,3 +297,32 @@ int main()
 ])dnl AC_CACHE_VAL
 AC_MSG_RESULT([$pgac_cv_printf_arg_control])
 ])# PGAC_FUNC_PRINTF_ARG_CONTROL
+
+
+# PGAC_TYPE_LOCALE_T
+# ------------------
+# Check for the locale_t type and find the right header file.  Mac OS
+# X needs xlocale.h; standard is locale.h, but glibc also has an
+# xlocale.h file that we should not use.
+#
+AC_DEFUN([PGAC_TYPE_LOCALE_T],
+[AC_CACHE_CHECK([for locale_t], pgac_cv_type_locale_t,
+[AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+[#include 
+locale_t x;],
+[])],
+[pgac_cv_type_locale_t=yes],
+[AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+[#include 
+locale_t x;],
+[])],
+[pgac_cv_type_locale_t='yes (in xlocale.h)'],
+[pgac_cv_type_locale_t=no])])])
+if test "$pgac_cv_type_locale_t" != no; then
+  AC_DEFINE(HAVE_LOCALE_T, 1,
+            [Define to 1 if the system has the type `locale_t'.])
+fi
+if test "$pgac_cv_type_locale_t" = 'yes (in xlocale.h)'; then
+  AC_DEFINE(LOCALE_T_IN_XLOCALE, 1,
+            [Define to 1 if `locale_t' requires .])
+fi])])# PGAC_HEADER_XLOCALE
index cc809acd959146ab31102acae2a4099b81267e23..f4d11abc7e1cfd446f733e20c9aa3124721f8d1e 100755 (executable)
--- a/configure
+++ b/configure
@@ -16830,6 +16830,114 @@ _ACEOF
   fi
 
 
+{ $as_echo "$as_me:$LINENO: checking for locale_t" >&5
+$as_echo_n "checking for locale_t... " >&6; }
+if test "${pgac_cv_type_locale_t+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include 
+locale_t x;
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+    test -z "$ac_c_werror_flag" ||
+    test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  pgac_cv_type_locale_t=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+   cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include 
+locale_t x;
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_compile") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+    test -z "$ac_c_werror_flag" ||
+    test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then
+  pgac_cv_type_locale_t='yes (in xlocale.h)'
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+   pgac_cv_type_locale_t=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $pgac_cv_type_locale_t" >&5
+$as_echo "$pgac_cv_type_locale_t" >&6; }
+if test "$pgac_cv_type_locale_t" != no; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_LOCALE_T 1
+_ACEOF
+
+fi
+if test "$pgac_cv_type_locale_t" = 'yes (in xlocale.h)'; then
+
+cat >>confdefs.h <<\_ACEOF
+#define LOCALE_T_IN_XLOCALE 1
+_ACEOF
+
+fi
+
 { $as_echo "$as_me:$LINENO: checking for struct cmsgcred" >&5
 $as_echo_n "checking for struct cmsgcred... " >&6; }
 if test "${ac_cv_type_struct_cmsgcred+set}" = set; then
index e9a1b2dcba7671c31348d664d991836fc555df42..0dffc51d0bfd9599f7a59d57dbf446075cb46e76 100644 (file)
@@ -1118,6 +1118,8 @@ AC_TYPE_INTPTR_T
 AC_TYPE_UINTPTR_T
 AC_TYPE_LONG_LONG_INT
 
+PGAC_TYPE_LOCALE_T
+
 AC_CHECK_TYPES([struct cmsgcred, struct fcred, struct sockcred], [], [],
 [#include 
 #include 
index 486a662c780cc3bec3493cc3779053bf9cac0039..144f6db18466ae02c097bf84d2002104c3b666de 100644 (file)
@@ -7,6 +7,7 @@
 
 #include "fmgr.h"
 #include "access/skey.h"
+#include "catalog/pg_collation.h"
 #include "utils/builtins.h"
 #include "utils/bytea.h"
 #include "utils/cash.h"
@@ -120,8 +121,9 @@ gin_compare_prefix_##type(PG_FUNCTION_ARGS)                                 \
    int32       res,                                                        \
                cmp;                                                        \
                                                                            \
-   cmp = DatumGetInt32(DirectFunctionCall2(                                \
+   cmp = DatumGetInt32(DirectFunctionCall2WithCollation(                   \
                TypeInfo_##type.typecmp,                                    \
+               DEFAULT_COLLATION_OID,                                      \
                (data->strategy == BTLessStrategyNumber ||                  \
                 data->strategy == BTLessEqualStrategyNumber)               \
                 ? data->datum : a,                                         \
index 40d0b9ad79c5211ec627e94f8853b3ac3551bf3b..665dfe78b449eb340e85817eff8640db5bbb5a5b 100644 (file)
@@ -3,6 +3,7 @@
  */
 #include "btree_gist.h"
 #include "btree_utils_var.h"
+#include "catalog/pg_collation.h"
 #include "utils/builtins.h"
 
 /*
@@ -32,37 +33,37 @@ Datum       gbt_text_same(PG_FUNCTION_ARGS);
 static bool
 gbt_textgt(const void *a, const void *b)
 {
-   return (DatumGetBool(DirectFunctionCall2(text_gt, PointerGetDatum(a), PointerGetDatum(b))));
+   return (DatumGetBool(DirectFunctionCall2WithCollation(text_gt, DEFAULT_COLLATION_OID, PointerGetDatum(a), PointerGetDatum(b))));
 }
 
 static bool
 gbt_textge(const void *a, const void *b)
 {
-   return (DatumGetBool(DirectFunctionCall2(text_ge, PointerGetDatum(a), PointerGetDatum(b))));
+   return (DatumGetBool(DirectFunctionCall2WithCollation(text_ge, DEFAULT_COLLATION_OID, PointerGetDatum(a), PointerGetDatum(b))));
 }
 
 static bool
 gbt_texteq(const void *a, const void *b)
 {
-   return (DatumGetBool(DirectFunctionCall2(texteq, PointerGetDatum(a), PointerGetDatum(b))));
+   return (DatumGetBool(DirectFunctionCall2WithCollation(texteq, DEFAULT_COLLATION_OID, PointerGetDatum(a), PointerGetDatum(b))));
 }
 
 static bool
 gbt_textle(const void *a, const void *b)
 {
-   return (DatumGetBool(DirectFunctionCall2(text_le, PointerGetDatum(a), PointerGetDatum(b))));
+   return (DatumGetBool(DirectFunctionCall2WithCollation(text_le, DEFAULT_COLLATION_OID, PointerGetDatum(a), PointerGetDatum(b))));
 }
 
 static bool
 gbt_textlt(const void *a, const void *b)
 {
-   return (DatumGetBool(DirectFunctionCall2(text_lt, PointerGetDatum(a), PointerGetDatum(b))));
+   return (DatumGetBool(DirectFunctionCall2WithCollation(text_lt, DEFAULT_COLLATION_OID, PointerGetDatum(a), PointerGetDatum(b))));
 }
 
 static int32
 gbt_textcmp(const bytea *a, const bytea *b)
 {
-   return DatumGetInt32(DirectFunctionCall2(bttextcmp, PointerGetDatum(a), PointerGetDatum(b)));
+   return DatumGetInt32(DirectFunctionCall2WithCollation(bttextcmp, DEFAULT_COLLATION_OID, PointerGetDatum(a), PointerGetDatum(b)));
 }
 
 static gbtree_vinfo tinfo =
index 5fc93cfbffed5688563f4baaf6fce8e2d51945e6..8f3173e499789c02f7d756ff6db42220b5a6cd06 100644 (file)
@@ -8,6 +8,7 @@
 #include 
 
 #include "btree_utils_var.h"
+#include "catalog/pg_collation.h"
 #include "utils/pg_locale.h"
 #include "utils/builtins.h"
 #include "utils/rel.h"
@@ -156,7 +157,7 @@ gbt_bytea_pf_match(const bytea *pf, const bytea *query, const gbtree_vinfo *tinf
 
        if (tinfo->eml > 1)
        {
-           out = (varstr_cmp(q, nlen, n, nlen) == 0);
+           out = (varstr_cmp(q, nlen, n, nlen, DEFAULT_COLLATION_OID) == 0);
        }
        else
        {
index 99918258533b9f98798f3e46fbefc1f868db0540..25e4dd3999b6f181b9d195bf83c473cb3866c5aa 100644 (file)
@@ -18,7 +18,7 @@ PG_MODULE_MAGIC;
  *     ====================
  */
 
-static int32 citextcmp(text *left, text *right);
+static int32 citextcmp(text *left, text *right, Oid collid);
 extern Datum citext_cmp(PG_FUNCTION_ARGS);
 extern Datum citext_hash(PG_FUNCTION_ARGS);
 extern Datum citext_eq(PG_FUNCTION_ARGS);
@@ -42,17 +42,18 @@ extern Datum citext_larger(PG_FUNCTION_ARGS);
  * Returns int32 negative, zero, or positive.
  */
 static int32
-citextcmp(text *left, text *right)
+citextcmp(text *left, text *right, Oid collid)
 {
    char       *lcstr,
               *rcstr;
    int32       result;
 
-   lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left));
-   rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right));
+   lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), collid);
+   rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), collid);
 
    result = varstr_cmp(lcstr, strlen(lcstr),
-                       rcstr, strlen(rcstr));
+                       rcstr, strlen(rcstr),
+                       collid);
 
    pfree(lcstr);
    pfree(rcstr);
@@ -75,7 +76,7 @@ citext_cmp(PG_FUNCTION_ARGS)
    text       *right = PG_GETARG_TEXT_PP(1);
    int32       result;
 
-   result = citextcmp(left, right);
+   result = citextcmp(left, right, PG_GET_COLLATION());
 
    PG_FREE_IF_COPY(left, 0);
    PG_FREE_IF_COPY(right, 1);
@@ -92,7 +93,7 @@ citext_hash(PG_FUNCTION_ARGS)
    char       *str;
    Datum       result;
 
-   str = str_tolower(VARDATA_ANY(txt), VARSIZE_ANY_EXHDR(txt));
+   str = str_tolower(VARDATA_ANY(txt), VARSIZE_ANY_EXHDR(txt), PG_GET_COLLATION());
    result = hash_any((unsigned char *) str, strlen(str));
    pfree(str);
 
@@ -121,8 +122,8 @@ citext_eq(PG_FUNCTION_ARGS)
 
    /* We can't compare lengths in advance of downcasing ... */
 
-   lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left));
-   rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right));
+   lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), PG_GET_COLLATION());
+   rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), PG_GET_COLLATION());
 
    /*
     * Since we only care about equality or not-equality, we can avoid all the
@@ -151,8 +152,8 @@ citext_ne(PG_FUNCTION_ARGS)
 
    /* We can't compare lengths in advance of downcasing ... */
 
-   lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left));
-   rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right));
+   lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), PG_GET_COLLATION());
+   rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), PG_GET_COLLATION());
 
    /*
     * Since we only care about equality or not-equality, we can avoid all the
@@ -177,7 +178,7 @@ citext_lt(PG_FUNCTION_ARGS)
    text       *right = PG_GETARG_TEXT_PP(1);
    bool        result;
 
-   result = citextcmp(left, right) < 0;
+   result = citextcmp(left, right, PG_GET_COLLATION()) < 0;
 
    PG_FREE_IF_COPY(left, 0);
    PG_FREE_IF_COPY(right, 1);
@@ -194,7 +195,7 @@ citext_le(PG_FUNCTION_ARGS)
    text       *right = PG_GETARG_TEXT_PP(1);
    bool        result;
 
-   result = citextcmp(left, right) <= 0;
+   result = citextcmp(left, right, PG_GET_COLLATION()) <= 0;
 
    PG_FREE_IF_COPY(left, 0);
    PG_FREE_IF_COPY(right, 1);
@@ -211,7 +212,7 @@ citext_gt(PG_FUNCTION_ARGS)
    text       *right = PG_GETARG_TEXT_PP(1);
    bool        result;
 
-   result = citextcmp(left, right) > 0;
+   result = citextcmp(left, right, PG_GET_COLLATION()) > 0;
 
    PG_FREE_IF_COPY(left, 0);
    PG_FREE_IF_COPY(right, 1);
@@ -228,7 +229,7 @@ citext_ge(PG_FUNCTION_ARGS)
    text       *right = PG_GETARG_TEXT_PP(1);
    bool        result;
 
-   result = citextcmp(left, right) >= 0;
+   result = citextcmp(left, right, PG_GET_COLLATION()) >= 0;
 
    PG_FREE_IF_COPY(left, 0);
    PG_FREE_IF_COPY(right, 1);
@@ -251,7 +252,7 @@ citext_smaller(PG_FUNCTION_ARGS)
    text       *right = PG_GETARG_TEXT_PP(1);
    text       *result;
 
-   result = citextcmp(left, right) < 0 ? left : right;
+   result = citextcmp(left, right, PG_GET_COLLATION()) < 0 ? left : right;
    PG_RETURN_TEXT_P(result);
 }
 
@@ -264,6 +265,6 @@ citext_larger(PG_FUNCTION_ARGS)
    text       *right = PG_GETARG_TEXT_PP(1);
    text       *result;
 
-   result = citextcmp(left, right) > 0 ? left : right;
+   result = citextcmp(left, right, PG_GET_COLLATION()) > 0 ? left : right;
    PG_RETURN_TEXT_P(result);
 }
index 1e75b55397a401de6e70b897f45c953e1eca8b59..7056d0ead370e61ed82eb0e0a28739d30eb77592 100644 (file)
@@ -52,7 +52,8 @@ CREATE TYPE citext (
     STORAGE        = extended,
     -- make it a non-preferred member of string type category
     CATEGORY       = 'S',
-    PREFERRED      = false
+    PREFERRED      = false,
+    COLLATABLE     = true
 );
 
 --
index fe9ecb118ae0d0c98faf89fb5a64f86fee64af78..da1086a8ebf8aa886b36146675f08fcfd5d67155 100644 (file)
@@ -7,6 +7,7 @@
 
 #include 
 
+#include "catalog/pg_collation.h"
 #include "utils/array.h"
 #include "utils/formatting.h"
 #include "ltree.h"
@@ -90,8 +91,8 @@ bool
 int
 ltree_strncasecmp(const char *a, const char *b, size_t s)
 {
-   char       *al = str_tolower(a, s);
-   char       *bl = str_tolower(b, s);
+   char       *al = str_tolower(a, s, DEFAULT_COLLATION_OID);
+   char       *bl = str_tolower(b, s, DEFAULT_COLLATION_OID);
    int         res;
 
    res = strncmp(al, bl, s);
index be132f2eb7b0ba610f74e96789a7314acb5e87ca..f31662c2720f7b6ef10c3b4a46a077d53d7b98e1 100644 (file)
       check constraints, unique constraints, primary key constraints, foreign key constraints
      
 
+     
+      pg_collation
+      collations (locale information)
+     
+
      
       pg_conversion
       encoding conversion information
       
      
 
+     
+      attcollation
+      oid
+      pg_collation.oid
+      
+       The defined collation of the column, zero if the column does
+       not have a collatable type.
+      
+     
+
      
       attacl
       aclitem[]
 
  
 
+  <structname>pg_collation</structname>
+
+  
+   pg_collation
+  
+
+  
+   The catalog pg_collation describes the
+   available collations, which are essentially mappings from an SQL
+   name to operating system locale categories.
+   See  for more information.
+  
+
+  
+   <structname>pg_collation</> Columns
+
+   
+    
+     
+      Name
+      Type
+      References
+      Description
+     
+    
+
+    
+     
+      collname
+      name
+      
+      Collation name (unique per namespace and encoding)
+     
+
+     
+      collnamespace
+      oid
+      pg_namespace.oid
+      
+       The OID of the namespace that contains this collation
+      
+     
+
+     
+      collencoding
+      int4
+      
+      Encoding to which the collation is applicable
+     
+
+     
+      collcollate
+      name
+      
+      LC_COLLATE for this collation object
+     
+
+     
+      collctype
+      name
+      
+      LC_CTYPE for this collation object
+     
+    
+   
+  
+
+
  
   <structname>pg_conversion</structname>
 
       
      
 
+     
+      indcollation
+      oidvector
+      pg_collation.oid
+      
+       For each column in the index key, this contains the OID of the
+       collation to use for the index.
+      
+     
+
      
       indclass
       oidvector
        
      
 
+     
+      typcollation
+      oid
+      pg_collation.oid
+      
+       typcollation specifies the collation
+       of the type.  If a type does not support collations, this will
+       be zero, collation analysis at parse time is skipped, and
+       the use of COLLATE clauses with the type is
+       invalid.  A base type that supports collations will have
+       DEFAULT_COLLATION_OID here.  A domain can have
+       another collation OID, if one was defined for the domain.
+      
+     
+
      
       typdefaultbin
       pg_node_tree
index 644c711dcccfb7efa349e95e5f8326cc3545bff2..49e1bd25b43fbbe57ae37b737163c27c199a6f27 100644 (file)
@@ -304,6 +304,170 @@ initdb --locale=sv_SE
  
 
 
+  Collation Support
+
+  
+   The collation support allows specifying the sort order and certain
+   other locale aspects of data per column or per operation at run
+   time.  This alleviates the problem that the
+   LC_COLLATE and LC_CTYPE settings
+   of a database cannot be changed after its creation.
+  
+
+  
+   
+    The collation support feature is currently only known to work on
+    Linux/glibc and Mac OS X platforms.
+   
+  
+
+  
+   Concepts
+
+   
+    Conceptually, every datum of a collatable data type has a
+    collation.  (Collatable data types in the base system are
+    textvarchar, and char.
+    User-defined base types can also be marked collatable.)  If the
+    datum is a column reference, the collation of the datum is the
+    defined collation of the column.  If the datum is a constant, the
+    collation is the default collation of the data type of the
+    constant.  The collation of more complex expressions is derived
+    from the input collations as described below.
+   
+
+   
+    The collation of a datum can also be the default
+    collation, which reverts to the locale settings defined for the
+    database.  In some cases, a datum can also have no known
+    collation.  In such cases, ordering operations and other
+    operations that need to know the collation will fail.
+   
+
+   
+    When the database system has to perform an ordering or a
+    comparison, it considers the collation of the input data.  This
+    happens in two situations: an ORDER BY clause
+    and a function or operator call such as <.
+    The collation to apply for the performance of the ORDER
+    BY clause is simply the collation of the sort key.  The
+    collation to apply for a function or operator call is derived from
+    the arguments, as described below.  Additionally, collations are
+    taken into account by functions that convert between lower and
+    upper case letters, that is, lower,
+    upper, and initcap.
+   
+
+   
+    For a function call, the collation that is derived from combining
+    the argument collations is both used for performing any
+    comparisons or ordering and for the collation of the function
+    result, if the result type is collatable.
+   
+
+   
+    The collation derivation of a datum can be
+    implicit or explicit.  This distinction affects how collations are
+    combined when multiple different collations appear in an
+    expression.  An explicit collation derivation arises when a
+    COLLATE clause is used; all other collation
+    derivations are implicit.  When multiple collations need to be
+    combined, for example in a function call, the following rules are
+    used:
+
+    
+     
+      
+       If any input item has an explicit collation derivation, then
+       all explicitly derived collations among the input items must be
+       the same, otherwise an error is raised.  If an explicitly
+       derived collation is present, that is the result of the
+       collation combination.
+      
+     
+
+     
+      
+       Otherwise, all input items must have the same implicit
+       collation derivation or the default collation.  If an
+       implicitly derived collation is present, that is the result of
+       the collation combination.  Otherwise, the result is the
+       default collation.
+      
+     
+    
+
+    For example, take this table definition:
+
+CREATE TABLE test1 (
+    a text COLLATE "x",
+    ...
+);
+
+
+    Then in
+
+SELECT a || 'foo' FROM test1;
+
+    the result collation of the || operator is
+    "x" because it combines an implicitly derived
+    collation with the default collation.  But in
+
+SELECT a || ('foo' COLLATE "y") FROM test1;
+
+    the result collation is "y" because the explicit
+    collation derivation overrides the implicit one.
+   
+  
+
+  
+   Managing Collations
+
+   
+    A collation is an SQL schema object that maps an SQL name to
+    operating system locales.  In particular, it maps to a combination
+    of LC_COLLATE and LC_CTYPE.  (As
+    the name would indicate, the main purpose of a collation is to set
+    LC_COLLATE, which controls the sort order.  But
+    it is rarely necessary in practice to have an
+    LC_CTYPE setting that is different from
+    LC_COLLATE, so it is more convenient to collect
+    these under one concept than to create another infrastructure for
+    setting LC_CTYPE per datum.)  Also, a collation
+    is tied to a character encoding.  The same collation name may
+    exist for different encodings.
+   
+
+   
+    When a database system is initialized, initdb
+    populates the system catalog pg_collation with
+    collations based on all the locales it finds on the operating
+    system at the time.  For example, the operating system might
+    provide a locale named de_DE.utf8.
+    initdb would then create a collation named
+    de_DE.utf8 for encoding UTF8
+    that has both LC_COLLATE and
+    LC_CTYPE set to de_DE.utf8.
+    It will also create a collation with the .utf8
+    tag stripped off the name.  So you could also use the collation
+    under the name de_DE, which is less cumbersome
+    to write and makes the name less encoding-dependent.  Note that,
+    nevertheless, the initial set of collation names is
+    platform-dependent.
+   
+
+   
+    In case a collation is needed that has different values for
+    LC_COLLATE and LC_CTYPE, or a
+    different name is needed for a collation (for example, for
+    compatibility with existing applications), a new collation may be
+    created.  But there is currently no SQL-level support for creating
+    or changing collations.
+   
+  
+
  
   Character Set Support
 
index 897078ac3e637757611a05c90457b40cbb462c61..70a1bd9ee9fb9559cefaf5573bc73d4237525e70 100644 (file)
@@ -13059,6 +13059,12 @@ SELECT relname FROM pg_class WHERE pg_table_is_visible(oid);
      
 
      
+      
+       pg_collation_is_visible(collation_oid)
+       
+       boolean
+       is collation visible in search path
+      
       
        pg_conversion_is_visible(conversion_oid)
        
@@ -13123,6 +13129,9 @@ SELECT relname FROM pg_class WHERE pg_table_is_visible(oid);
     
    
 
+   
+    pg_collation_is_visible
+   
    
     pg_conversion_is_visible
    
@@ -13256,7 +13265,7 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
 
      
       
-       format_type(type_oidtypemod)
+       format_type(type_oid [, typemod [, collation_oid ]])
        text
        get SQL name of a data type
       
@@ -13392,7 +13401,9 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
   
    format_type returns the SQL name of a data type that
    is identified by its type OID and possibly a type modifier.  Pass NULL
-   for the type modifier if no specific modifier is known.
+   for the type modifier or omit the argument if no specific modifier is known.
+   If a collation is given as third argument, a COLLATE clause
+   followed by a formatted collation name is appended.
   
 
   
index 8fec13ee1d8905f23a04e661df008796995d7d0a..15fbe0d614e7b8854c524c9334dbe8f7321b9877 100644 (file)
@@ -921,6 +921,7 @@ CREATE INDEX name ON table
    defining two operator classes for the data type and then selecting
    the proper class when making an index.  The operator class determines
    the basic sort ordering (which can then be modified by adding sort options
+   COLLATE,
    ASC/DESC and/or
    NULLS FIRST/NULLS LAST).
   
@@ -1002,6 +1003,47 @@ SELECT am.amname AS index_method,
  
 
 
+  Collations and Indexes
+
+  
+   An index can only support one collation for one column or
+   expression.  If multiple collations are of interest, multiple
+   indexes may be created.
+  
+
+  
+   Consider these statements:
+
+CREATE TABLE test1c (
+    id integer,
+    content varchar COLLATE "x"
+);
+
+CREATE INDEX test1c_content_index ON test1c (content);
+
+   The created index automatically follows the collation of the
+   underlying column, and so a query of the form
+
+SELECT * FROM test1c WHERE content = constant;
+
+   could use the index.
+  
+
+  
+   If in addition, a query of the form, say,
+
+SELECT * FROM test1c WHERE content > constant COLLATE "y";
+
+   is of interest, an additional index could be created that supports
+   the "y" collation, like so:
+
+CREATE INDEX test1c_content_index ON test1c (content COLLATE "y");
+
+  
+
+
  
   Examining Index Usage
 
index 87a7654d6ce349425bcc429fb28e4000dd6a383b..83be889c6d9110cc44fa7918dcf274d6825b0f22 100644 (file)
@@ -22,6 +22,7 @@ PostgreSQL documentation
  
 
 CREATE DOMAIN name [ AS ] data_type
+    [ COLLATE collation ]
     [ DEFAULT expression ]
     [ constraint [ ... ] ]
 
@@ -83,6 +84,17 @@ CREATE DOMAIN name [ AS ] 
       
      
 
+     
+      collation
+      
+       
+        An optional collation for the domain.  If no collation is
+        specified, the database default collation is used (which can
+        be overridden when the domain is used to define a column).
+       
+      
+     
+
      
       DEFAULT expression
 
index 45c298e371e5884c9e5240f9986037f4f095f8a4..8ec7abbbd495231552751063a72925a73bee94df 100644 (file)
@@ -22,7 +22,7 @@ PostgreSQL documentation
  
 
 CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ name ] ON table [ USING method ]
-    ( { column | ( expression ) } [ opclass ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
+    ( { column | ( expression ) } [ COLLATE collation ] [ opclass ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
     [ WITH ( storage_parameter = value [, ... ] ) ]
     [ TABLESPACE tablespace ]
     [ WHERE predicate ]
@@ -181,6 +181,20 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ name
       
      
 
+     
+      collation
+      
+       
+        The name of the collation to use for the index.  By default,
+        the index uses the collation declared for the column to be
+        indexed or the result collation of the expression to be
+        indexed.  Indexes with nondefault collations are
+        available for use by queries that involve expressions using
+        nondefault collations.
+       
+      
+     
+
      
       opclass
       
index 5bffe303364b749a0e72ce07e8ca1808d1ac9de4..9d2d99ad2e3774654ff9acd3d496de1e6f9b34ee 100644 (file)
@@ -22,7 +22,7 @@ PostgreSQL documentation
  
 
 CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] table_name ( [
-  { column_name data_type [ column_constraint [ ... ] ]
+  { column_name data_type [ COLLATE collation ] [ column_constraint [ ... ] ]
     | table_constraint
     | LIKE parent_table [ like_option ... ] }
     [, ... ]
@@ -244,6 +244,17 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
     
    
 
+   
+    COLLATE collation
+    
+     
+      The COLLATE clause assigns a nondefault collation to
+      the column.  By default, the locale settings of the database are
+      used.
+     
+    
+   
+
    
     INHERITS ( parent_table [, ... ] )
     
index a3c75b51d0fb3f461005249b9fbfc65a12adf35a..805fc2cf76051a996e580eb5870ca466305df043 100644 (file)
@@ -45,6 +45,7 @@ CREATE TYPE name (
     [ , DEFAULT = default ]
     [ , ELEMENT = element ]
     [ , DELIMITER = delimiter ]
+    [ , COLLATABLE = collatable ]
 )
 
 CREATE TYPE name
@@ -352,6 +353,16 @@ CREATE TYPE name
    with the array element type, not the array type itself.
   
 
+  
+   If the optional
+   parameter collatable
+   is true, column definitions and expressions of the type may carry
+   collation information and allow the use of
+   the COLLATE clause.  It is up to the
+   implementations of the functions operating on the type to actually
+   make use of the collation information; this does not happen
+   automatically merely by marking the type collatable.
+  
   
 
   
index 61a87ff74d26827c3a7ca6fe7ca105d8f86228b0..6f5e2b1ab888326572456c378bfa0f149e86651c 100644 (file)
@@ -236,6 +236,27 @@ gmake check LANG=C MULTIBYTE=EUC_JP
     existing installation.
    
   
+
+  
+   Extra tests
+
+   
+    The regression test suite contains a few test files that are not
+    run by default, because they might be platform-dependent or take a
+    very long time to run.  You can run these or other extra test
+    files by setting the variable EXTRA_TESTS.  For
+    example, to run the numeric_big test:
+
+gmake check EXTRA_TESTS=numeric_big
+
+    To run the collation tests:
+
+gmake check EXTRA_TESTS=collate.linux.utf8 LANG=en_US.utf8
+
+    This test works only on Linux/glibc platforms and when run in a
+    UTF-8 locale.
+   
+  
   
 
   
index 004205f1265dcd89b467fc61ae012b91e0bf714e..00f665ae5444290f999cd0f1bc8078eff78ac007 100644 (file)
@@ -1899,6 +1899,54 @@ CAST ( expression AS type
    
   
 
+  
+   COLLATE Clause
+
+   
+    COLLATE
+   
+
+   
+    The COLLATE clause overrides the collation of
+    an expression.  It is appended to the expression it applies to:
+
+expr COLLATE collation
+
+    where collation is a possibly
+    schema-qualified identifier.  The COLLATE
+    clause binds tighter than operators; parentheses can be used when
+    necessary.
+   
+
+   
+    If no collation is explicitly specified, the database system
+    either derives a collation from the columns involved in the
+    expression, or it defaults to the default collation of the
+    database if no column is involved in the expression.
+   
+
+   
+    The two typical uses of the COLLATE clause are
+    overriding the sort order in an ORDER BY clause, for
+    example:
+
+SELECT a, b, c FROM tbl WHERE ... ORDER BY a COLLATE "C";
+
+    and overriding the collation of a function or operator call that
+    has locale-sensitive results, for example:
+
+SELECT * FROM tbl WHERE a > 'foo' COLLATE "C";
+
+    In the latter case it doesn't matter which argument of the
+    operator of function call the COLLATE clause is
+    attached to, because the collation that is applied by the operator
+    or function is derived from all arguments, and
+    the COLLATE clause will override the collations of all
+    other arguments.  Attaching nonmatching COLLATE
+    clauses to more than one argument, however, is an error.
+   
+  
+
   
    Scalar Subqueries
 
index 232322a91e0737d3fa57d13b1237803d5420be4b..41cd36fce927e62e9a65742eec1bfa97c9415a23 100644 (file)
@@ -103,3 +103,16 @@ ScanKeyEntryInitializeWithInfo(ScanKey entry,
    entry->sk_argument = argument;
    fmgr_info_copy(&entry->sk_func, finfo, CurrentMemoryContext);
 }
+
+/*
+ * ScanKeyEntryInitializeCollation
+ *
+ * Initialize the collation of a scan key.  This is just a notational
+ * convenience and small abstraction.
+ */
+void
+ScanKeyEntryInitializeCollation(ScanKey entry,
+                               Oid collation)
+{
+   entry->sk_func.fn_collation = collation;
+}
index cfc95b93126bab7c1e1d526bdb41ceb10b430bc3..0cd1f941e3fe9bb146190aaaedc79c922bb3448c 100644 (file)
@@ -488,10 +488,32 @@ TupleDescInitEntry(TupleDesc desc,
    att->attbyval = typeForm->typbyval;
    att->attalign = typeForm->typalign;
    att->attstorage = typeForm->typstorage;
+   att->attcollation = typeForm->typcollation;
 
    ReleaseSysCache(tuple);
 }
 
+/*
+ * TupleDescInitEntryCollation
+ *
+ * Fill in the collation for an attribute in a previously initialized
+ * tuple descriptor.
+ */
+void
+TupleDescInitEntryCollation(TupleDesc desc,
+                           AttrNumber attributeNumber,
+                           Oid collationid)
+{
+   /*
+    * sanity checks
+    */
+   AssertArg(PointerIsValid(desc));
+   AssertArg(attributeNumber >= 1);
+   AssertArg(attributeNumber <= desc->natts);
+
+   desc->attrs[attributeNumber - 1]->attcollation = collationid;
+}
+
 
 /*
  * BuildDescForRelation
@@ -513,6 +535,7 @@ BuildDescForRelation(List *schema)
    char       *attname;
    Oid         atttypid;
    int32       atttypmod;
+   Oid         attcollation;
    int         attdim;
 
    /*
@@ -536,7 +559,7 @@ BuildDescForRelation(List *schema)
        attnum++;
 
        attname = entry->colname;
-       typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
+       typenameTypeIdModColl(NULL, entry->typeName, &atttypid, &atttypmod, &attcollation);
        attdim = list_length(entry->typeName->arrayBounds);
 
        if (entry->typeName->setof)
@@ -547,6 +570,7 @@ BuildDescForRelation(List *schema)
 
        TupleDescInitEntry(desc, attnum, attname,
                           atttypid, atttypmod, attdim);
+       TupleDescInitEntryCollation(desc, attnum, attcollation);
 
        /* Override TupleDescInitEntry's settings as requested */
        if (entry->storage)
@@ -588,18 +612,20 @@ BuildDescForRelation(List *schema)
  * with functions returning RECORD.
  */
 TupleDesc
-BuildDescFromLists(List *names, List *types, List *typmods)
+BuildDescFromLists(List *names, List *types, List *typmods, List *collations)
 {
    int         natts;
    AttrNumber  attnum;
    ListCell   *l1;
    ListCell   *l2;
    ListCell   *l3;
+   ListCell   *l4;
    TupleDesc   desc;
 
    natts = list_length(names);
    Assert(natts == list_length(types));
    Assert(natts == list_length(typmods));
+   Assert(natts == list_length(collations));
 
    /*
     * allocate a new tuple descriptor
@@ -610,20 +636,25 @@ BuildDescFromLists(List *names, List *types, List *typmods)
 
    l2 = list_head(types);
    l3 = list_head(typmods);
+   l4 = list_head(collations);
    foreach(l1, names)
    {
        char       *attname = strVal(lfirst(l1));
        Oid         atttypid;
        int32       atttypmod;
+       Oid         attcollation;
 
        atttypid = lfirst_oid(l2);
        l2 = lnext(l2);
        atttypmod = lfirst_int(l3);
        l3 = lnext(l3);
+       attcollation = lfirst_oid(l4);
+       l4 = lnext(l4);
 
        attnum++;
 
        TupleDescInitEntry(desc, attnum, attname, atttypid, atttypmod, 0);
+       TupleDescInitEntryCollation(desc, attnum, attcollation);
    }
 
    return desc;
index 0a7c1c521fcb774c221b845325d22d761737fd95..35f71c843e1a41b94435a405e6a43eb1f80909f6 100644 (file)
@@ -22,6 +22,7 @@
 #include "storage/freespace.h"
 #include "storage/indexfsm.h"
 #include "storage/lmgr.h"
+#include "utils/lsyscache.h"
 
 
 /*
@@ -60,6 +61,8 @@ initGinState(GinState *state, Relation index)
        fmgr_info_copy(&(state->compareFn[i]),
                       index_getprocinfo(index, i + 1, GIN_COMPARE_PROC),
                       CurrentMemoryContext);
+       fmgr_info_collation(get_typcollation(index->rd_att->attrs[i]->atttypid),
+                           &(state->compareFn[i]));
        fmgr_info_copy(&(state->extractValueFn[i]),
                       index_getprocinfo(index, i + 1, GIN_EXTRACTVALUE_PROC),
                       CurrentMemoryContext);
index 6e0db79517615a1dc13f6287fecbc17bc1e587da..6e6af18d4717dc317444bf2866fadd913a28fefd 100644 (file)
@@ -872,6 +872,8 @@ index_getprocinfo(Relation irel,
                 procnum, attnum, RelationGetRelationName(irel));
 
        fmgr_info_cxt(procId, locinfo, irel->rd_indexcxt);
+       fmgr_info_collation(irel->rd_index->indcollation.values[attnum-1],
+                           locinfo);
    }
 
    return locinfo;
index cf74f7776ce4569738867a0bdbd212f517708ae7..be8d958352b8d18adb6ce594a45439143503a98a 100644 (file)
@@ -723,6 +723,8 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
                                               cur->sk_subtype,
                                               procinfo,
                                               cur->sk_argument);
+               ScanKeyEntryInitializeCollation(scankeys + i,
+                                               cur->sk_func.fn_collation);
            }
            else
            {
@@ -743,6 +745,8 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
                                       cur->sk_subtype,
                                       cmp_proc,
                                       cur->sk_argument);
+               ScanKeyEntryInitializeCollation(scankeys + i,
+                                               cur->sk_func.fn_collation);
            }
        }
    }
index dbcc55c822e7f72e9a19b45a07f07be0d36f3f22..528ea23d4c3fcd023dfc11f98149f01b7e4a80a7 100644 (file)
@@ -26,6 +26,7 @@
 #include "access/xact.h"
 #include "bootstrap/bootstrap.h"
 #include "catalog/index.h"
+#include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
@@ -88,56 +89,57 @@ struct typinfo
    bool        byval;
    char        align;
    char        storage;
+   Oid         collation;
    Oid         inproc;
    Oid         outproc;
 };
 
 static const struct typinfo TypInfo[] = {
-   {"bool", BOOLOID, 0, 1, true, 'c', 'p',
+   {"bool", BOOLOID, 0, 1, true, 'c', 'p', InvalidOid,
    F_BOOLIN, F_BOOLOUT},
-   {"bytea", BYTEAOID, 0, -1, false, 'i', 'x',
+   {"bytea", BYTEAOID, 0, -1, false, 'i', 'x', InvalidOid,
    F_BYTEAIN, F_BYTEAOUT},
-   {"char", CHAROID, 0, 1, true, 'c', 'p',
+   {"char", CHAROID, 0, 1, true, 'c', 'p', InvalidOid,
    F_CHARIN, F_CHAROUT},
-   {"int2", INT2OID, 0, 2, true, 's', 'p',
+   {"int2", INT2OID, 0, 2, true, 's', 'p', InvalidOid,
    F_INT2IN, F_INT2OUT},
-   {"int4", INT4OID, 0, 4, true, 'i', 'p',
+   {"int4", INT4OID, 0, 4, true, 'i', 'p', InvalidOid,
    F_INT4IN, F_INT4OUT},
-   {"float4", FLOAT4OID, 0, 4, FLOAT4PASSBYVAL, 'i', 'p',
+   {"float4", FLOAT4OID, 0, 4, FLOAT4PASSBYVAL, 'i', 'p', InvalidOid,
    F_FLOAT4IN, F_FLOAT4OUT},
-   {"name", NAMEOID, CHAROID, NAMEDATALEN, false, 'c', 'p',
+   {"name", NAMEOID, CHAROID, NAMEDATALEN, false, 'c', 'p', InvalidOid,
    F_NAMEIN, F_NAMEOUT},
-   {"regclass", REGCLASSOID, 0, 4, true, 'i', 'p',
+   {"regclass", REGCLASSOID, 0, 4, true, 'i', 'p', InvalidOid,
    F_REGCLASSIN, F_REGCLASSOUT},
-   {"regproc", REGPROCOID, 0, 4, true, 'i', 'p',
+   {"regproc", REGPROCOID, 0, 4, true, 'i', 'p', InvalidOid,
    F_REGPROCIN, F_REGPROCOUT},
-   {"regtype", REGTYPEOID, 0, 4, true, 'i', 'p',
+   {"regtype", REGTYPEOID, 0, 4, true, 'i', 'p', InvalidOid,
    F_REGTYPEIN, F_REGTYPEOUT},
-   {"text", TEXTOID, 0, -1, false, 'i', 'x',
+   {"text", TEXTOID, 0, -1, false, 'i', 'x', DEFAULT_COLLATION_OID,
    F_TEXTIN, F_TEXTOUT},
-   {"oid", OIDOID, 0, 4, true, 'i', 'p',
+   {"oid", OIDOID, 0, 4, true, 'i', 'p', InvalidOid,
    F_OIDIN, F_OIDOUT},
-   {"tid", TIDOID, 0, 6, false, 's', 'p',
+   {"tid", TIDOID, 0, 6, false, 's', 'p', InvalidOid,
    F_TIDIN, F_TIDOUT},
-   {"xid", XIDOID, 0, 4, true, 'i', 'p',
+   {"xid", XIDOID, 0, 4, true, 'i', 'p', InvalidOid,
    F_XIDIN, F_XIDOUT},
-   {"cid", CIDOID, 0, 4, true, 'i', 'p',
+   {"cid", CIDOID, 0, 4, true, 'i', 'p', InvalidOid,
    F_CIDIN, F_CIDOUT},
-   {"pg_node_tree", PGNODETREEOID, 0, -1, false, 'i', 'x',
+   {"pg_node_tree", PGNODETREEOID, 0, -1, false, 'i', 'x', DEFAULT_COLLATION_OID,
    F_PG_NODE_TREE_IN, F_PG_NODE_TREE_OUT},
-   {"int2vector", INT2VECTOROID, INT2OID, -1, false, 'i', 'p',
+   {"int2vector", INT2VECTOROID, INT2OID, -1, false, 'i', 'p', InvalidOid,
    F_INT2VECTORIN, F_INT2VECTOROUT},
-   {"oidvector", OIDVECTOROID, OIDOID, -1, false, 'i', 'p',
+   {"oidvector", OIDVECTOROID, OIDOID, -1, false, 'i', 'p', InvalidOid,
    F_OIDVECTORIN, F_OIDVECTOROUT},
-   {"_int4", INT4ARRAYOID, INT4OID, -1, false, 'i', 'x',
+   {"_int4", INT4ARRAYOID, INT4OID, -1, false, 'i', 'x', InvalidOid,
    F_ARRAY_IN, F_ARRAY_OUT},
-   {"_text", 1009, TEXTOID, -1, false, 'i', 'x',
+   {"_text", 1009, TEXTOID, -1, false, 'i', 'x', DEFAULT_COLLATION_OID,
    F_ARRAY_IN, F_ARRAY_OUT},
-   {"_oid", 1028, OIDOID, -1, false, 'i', 'x',
+   {"_oid", 1028, OIDOID, -1, false, 'i', 'x', InvalidOid,
    F_ARRAY_IN, F_ARRAY_OUT},
-   {"_char", 1002, CHAROID, -1, false, 'i', 'x',
+   {"_char", 1002, CHAROID, -1, false, 'i', 'x', InvalidOid,
    F_ARRAY_IN, F_ARRAY_OUT},
-   {"_aclitem", 1034, ACLITEMOID, -1, false, 'i', 'x',
+   {"_aclitem", 1034, ACLITEMOID, -1, false, 'i', 'x', InvalidOid,
    F_ARRAY_IN, F_ARRAY_OUT}
 };
 
@@ -710,6 +712,7 @@ DefineAttr(char *name, char *type, int attnum)
        attrtypes[attnum]->attbyval = Ap->am_typ.typbyval;
        attrtypes[attnum]->attstorage = Ap->am_typ.typstorage;
        attrtypes[attnum]->attalign = Ap->am_typ.typalign;
+       attrtypes[attnum]->attcollation = Ap->am_typ.typcollation;
        /* if an array type, assume 1-dimensional attribute */
        if (Ap->am_typ.typelem != InvalidOid && Ap->am_typ.typlen < 0)
            attrtypes[attnum]->attndims = 1;
@@ -723,6 +726,7 @@ DefineAttr(char *name, char *type, int attnum)
        attrtypes[attnum]->attbyval = TypInfo[typeoid].byval;
        attrtypes[attnum]->attstorage = TypInfo[typeoid].storage;
        attrtypes[attnum]->attalign = TypInfo[typeoid].align;
+       attrtypes[attnum]->attcollation = TypInfo[typeoid].collation;
        /* if an array type, assume 1-dimensional attribute */
        if (TypInfo[typeoid].elem != InvalidOid &&
            attrtypes[attnum]->attlen < 0)
index 17f4cc6cfc979fe3820a57a666908fc27f6e1ea5..7cffde1769231c122be3a48f30c7b411da289386 100644 (file)
@@ -39,7 +39,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
    pg_ts_parser.h pg_ts_template.h \
    pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
    pg_foreign_table.h \
-   pg_default_acl.h pg_seclabel.h \
+   pg_default_acl.h pg_seclabel.h pg_collation.h \
    toasting.h indexing.h \
     )
 
index d07e8925257b027e45cd4a427d52123e8c183e12..0aeaf5bfd76c7e34f01f96fb35bbd1d3f2c4bf40 100644 (file)
@@ -340,6 +340,7 @@ sub emit_pgattr_row
             $row{attalign}    = $type->{typalign};
             # set attndims if it's an array type
             $row{attndims}    = $type->{typcategory} eq 'A' ? '1' : '0';
+            $row{attcollation} = $type->{typcollation};
             # attnotnull must be set true if the type is fixed-width and
             # prior columns are too --- compare DefineAttr in bootstrap.c.
             # oidvector and int2vector are also treated as not-nullable.
index c2c7a3d8f4cd56292bd66c36e80669635893fd1e..14c69f3faa88a574ed0afef4ca04d7ae940ea816 100644 (file)
@@ -542,6 +542,7 @@ InsertPgAttributeTuple(Relation pg_attribute_rel,
    values[Anum_pg_attribute_attisdropped - 1] = BoolGetDatum(new_attribute->attisdropped);
    values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal);
    values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount);
+   values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(new_attribute->attcollation);
 
    /* start out with empty permissions and empty options */
    nulls[Anum_pg_attribute_attacl - 1] = true;
@@ -859,7 +860,8 @@ AddNewRelationType(const char *typeName,
                   'x',         /* fully TOASTable */
                   -1,          /* typmod */
                   0,           /* array dimensions for typBaseType */
-                  false);      /* Type NOT NULL */
+                  false,       /* Type NOT NULL */
+                  InvalidOid); /* typcollation */
 }
 
 /* --------------------------------
@@ -1120,7 +1122,8 @@ heap_create_with_catalog(const char *relname,
                   'x',         /* fully TOASTable */
                   -1,          /* typmod */
                   0,           /* array dimensions for typBaseType */
-                  false);      /* Type NOT NULL */
+                  false,       /* Type NOT NULL */
+                  InvalidOid); /* typcollation */
 
        pfree(relarrayname);
    }
index 92672bb73388399faff6b3d4a0cc6e1da349d2bb..9e6012125fc46feb1099fbb2417fe36fe7152acb 100644 (file)
@@ -87,12 +87,14 @@ static TupleDesc ConstructTupleDescriptor(Relation heapRelation,
                         IndexInfo *indexInfo,
                         List *indexColNames,
                         Oid accessMethodObjectId,
+                        Oid *collationObjectId,
                         Oid *classObjectId);
 static void InitializeAttributeOids(Relation indexRelation,
                        int numatts, Oid indexoid);
 static void AppendAttributeTuples(Relation indexRelation, int numatts);
 static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
                    IndexInfo *indexInfo,
+                   Oid *collationOids,
                    Oid *classOids,
                    int16 *coloptions,
                    bool primary,
@@ -264,6 +266,7 @@ ConstructTupleDescriptor(Relation heapRelation,
                         IndexInfo *indexInfo,
                         List *indexColNames,
                         Oid accessMethodObjectId,
+                        Oid *collationObjectId,
                         Oid *classObjectId)
 {
    int         numatts = indexInfo->ii_NumIndexAttrs;
@@ -398,6 +401,8 @@ ConstructTupleDescriptor(Relation heapRelation,
            CheckAttributeType(NameStr(to->attname), to->atttypid, false);
        }
 
+       to->attcollation = collationObjectId[i];
+
        /*
         * We do not yet have the correct relation OID for the index, so just
         * set it invalid for now.  InitializeAttributeOids() will fix it
@@ -521,6 +526,7 @@ static void
 UpdateIndexRelation(Oid indexoid,
                    Oid heapoid,
                    IndexInfo *indexInfo,
+                   Oid *collationOids,
                    Oid *classOids,
                    int16 *coloptions,
                    bool primary,
@@ -529,6 +535,7 @@ UpdateIndexRelation(Oid indexoid,
                    bool isvalid)
 {
    int2vector *indkey;
+   oidvector  *indcollation;
    oidvector  *indclass;
    int2vector *indoption;
    Datum       exprsDatum;
@@ -546,6 +553,7 @@ UpdateIndexRelation(Oid indexoid,
    indkey = buildint2vector(NULL, indexInfo->ii_NumIndexAttrs);
    for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
        indkey->values[i] = indexInfo->ii_KeyAttrNumbers[i];
+   indcollation = buildoidvector(collationOids, indexInfo->ii_NumIndexAttrs);
    indclass = buildoidvector(classOids, indexInfo->ii_NumIndexAttrs);
    indoption = buildint2vector(coloptions, indexInfo->ii_NumIndexAttrs);
 
@@ -601,6 +609,7 @@ UpdateIndexRelation(Oid indexoid,
    /* we set isvalid and isready the same way */
    values[Anum_pg_index_indisready - 1] = BoolGetDatum(isvalid);
    values[Anum_pg_index_indkey - 1] = PointerGetDatum(indkey);
+   values[Anum_pg_index_indcollation - 1] = PointerGetDatum(indcollation);
    values[Anum_pg_index_indclass - 1] = PointerGetDatum(indclass);
    values[Anum_pg_index_indoption - 1] = PointerGetDatum(indoption);
    values[Anum_pg_index_indexprs - 1] = exprsDatum;
@@ -664,6 +673,7 @@ index_create(Relation heapRelation,
             List *indexColNames,
             Oid accessMethodObjectId,
             Oid tableSpaceId,
+            Oid *collationObjectId,
             Oid *classObjectId,
             int16 *coloptions,
             Datum reloptions,
@@ -761,6 +771,7 @@ index_create(Relation heapRelation,
                                            indexInfo,
                                            indexColNames,
                                            accessMethodObjectId,
+                                           collationObjectId,
                                            classObjectId);
 
    /*
@@ -856,7 +867,7 @@ index_create(Relation heapRelation,
     * ----------------
     */
    UpdateIndexRelation(indexRelationId, heapRelationId, indexInfo,
-                       classObjectId, coloptions, isprimary, is_exclusion,
+                       collationObjectId, classObjectId, coloptions, isprimary, is_exclusion,
                        !deferrable,
                        !concurrent);
 
@@ -2370,7 +2381,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot)
    ivinfo.strategy = NULL;
 
    state.tuplesort = tuplesort_begin_datum(TIDOID,
-                                           TIDLessOperator, false,
+                                           TIDLessOperator, InvalidOid, false,
                                            maintenance_work_mem,
                                            false);
    state.htups = state.itups = state.tups_inserted = 0;
index d8bb4e39a8cfe57f5f0a9992133d30ad7ed14c8e..8b04b9fd9b381d1b8c924d7c0abb18c01cff6a6c 100644 (file)
@@ -23,6 +23,7 @@
 #include "catalog/dependency.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_authid.h"
+#include "catalog/pg_collation.h"
 #include "catalog/pg_conversion.h"
 #include "catalog/pg_conversion_fn.h"
 #include "catalog/pg_namespace.h"
@@ -37,6 +38,7 @@
 #include "catalog/pg_type.h"
 #include "commands/dbcommands.h"
 #include "funcapi.h"
+#include "mb/pg_wchar.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "parser/parse_func.h"
@@ -198,6 +200,7 @@ Datum       pg_type_is_visible(PG_FUNCTION_ARGS);
 Datum      pg_function_is_visible(PG_FUNCTION_ARGS);
 Datum      pg_operator_is_visible(PG_FUNCTION_ARGS);
 Datum      pg_opclass_is_visible(PG_FUNCTION_ARGS);
+Datum      pg_collation_is_visible(PG_FUNCTION_ARGS);
 Datum      pg_conversion_is_visible(PG_FUNCTION_ARGS);
 Datum      pg_ts_parser_is_visible(PG_FUNCTION_ARGS);
 Datum      pg_ts_dict_is_visible(PG_FUNCTION_ARGS);
@@ -1610,6 +1613,89 @@ OpfamilyIsVisible(Oid opfid)
    return visible;
 }
 
+/*
+ * CollationGetCollid
+ *     Try to resolve an unqualified collation name.
+ *     Returns OID if collation found in search path, else InvalidOid.
+ *
+ * This is essentially the same as RelnameGetRelid.
+ */
+Oid
+CollationGetCollid(const char *collname)
+{
+   Oid         collid;
+   ListCell   *l;
+
+   recomputeNamespacePath();
+
+   foreach(l, activeSearchPath)
+   {
+       Oid         namespaceId = lfirst_oid(l);
+
+       if (namespaceId == myTempNamespace)
+           continue;           /* do not look in temp namespace */
+
+       collid = GetSysCacheOid3(COLLNAMEENCNSP,
+                                PointerGetDatum(collname),
+                                Int32GetDatum(GetDatabaseEncoding()),
+                                ObjectIdGetDatum(namespaceId));
+       if (OidIsValid(collid))
+           return collid;
+   }
+
+   /* Not found in path */
+   return InvalidOid;
+}
+
+/*
+ * CollationIsVisible
+ *     Determine whether a collation (identified by OID) is visible in the
+ *     current search path.  Visible means "would be found by searching
+ *     for the unqualified collation name".
+ */
+bool
+CollationIsVisible(Oid collid)
+{
+   HeapTuple   colltup;
+   Form_pg_collation collform;
+   Oid         collnamespace;
+   bool        visible;
+
+   colltup = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
+   if (!HeapTupleIsValid(colltup))
+       elog(ERROR, "cache lookup failed for collation %u", collid);
+   collform = (Form_pg_collation) GETSTRUCT(colltup);
+
+   recomputeNamespacePath();
+
+   /*
+    * Quick check: if it ain't in the path at all, it ain't visible. Items in
+    * the system namespace are surely in the path and so we needn't even do
+    * list_member_oid() for them.
+    */
+   collnamespace = collform->collnamespace;
+   if (collnamespace != PG_CATALOG_NAMESPACE &&
+       !list_member_oid(activeSearchPath, collnamespace))
+       visible = false;
+   else
+   {
+       /*
+        * If it is in the path, it might still not be visible; it could be
+        * hidden by another conversion of the same name earlier in the path.
+        * So we must do a slow check to see if this conversion would be found
+        * by CollationGetCollid.
+        */
+       char       *collname = NameStr(collform->collname);
+
+       visible = (CollationGetCollid(collname) == collid);
+   }
+
+   ReleaseSysCache(colltup);
+
+   return visible;
+}
+
+
 /*
  * ConversionGetConid
  *     Try to resolve an unqualified conversion name.
@@ -2807,6 +2893,63 @@ PopOverrideSearchPath(void)
 }
 
 
+/*
+ * get_collation_oid - find a collation by possibly qualified name
+ */
+Oid
+get_collation_oid(List *name, bool missing_ok)
+{
+   char       *schemaname;
+   char       *collation_name;
+   Oid         namespaceId;
+   Oid         colloid = InvalidOid;
+   ListCell   *l;
+   int         encoding;
+
+   encoding = GetDatabaseEncoding();
+
+   /* deconstruct the name list */
+   DeconstructQualifiedName(name, &schemaname, &collation_name);
+
+   if (schemaname)
+   {
+       /* use exact schema given */
+       namespaceId = LookupExplicitNamespace(schemaname);
+       colloid = GetSysCacheOid3(COLLNAMEENCNSP,
+                                 PointerGetDatum(collation_name),
+                                 Int32GetDatum(encoding),
+                                 ObjectIdGetDatum(namespaceId));
+   }
+   else
+   {
+       /* search for it in search path */
+       recomputeNamespacePath();
+
+       foreach(l, activeSearchPath)
+       {
+           namespaceId = lfirst_oid(l);
+
+           if (namespaceId == myTempNamespace)
+               continue;       /* do not look in temp namespace */
+
+           colloid = GetSysCacheOid3(COLLNAMEENCNSP,
+                                     PointerGetDatum(collation_name),
+                                     Int32GetDatum(encoding),
+                                     ObjectIdGetDatum(namespaceId));
+           if (OidIsValid(colloid))
+               return colloid;
+       }
+   }
+
+   /* Not found in path */
+   if (!OidIsValid(colloid) && !missing_ok)
+       ereport(ERROR,
+               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                errmsg("collation \"%s\" for current database encoding \"%s\" does not exist",
+                       NameListToString(name), GetDatabaseEncodingName())));
+   return colloid;
+}
+
 /*
  * get_conversion_oid - find a conversion by possibly qualified name
  */
@@ -3566,6 +3709,17 @@ pg_opclass_is_visible(PG_FUNCTION_ARGS)
    PG_RETURN_BOOL(OpclassIsVisible(oid));
 }
 
+Datum
+pg_collation_is_visible(PG_FUNCTION_ARGS)
+{
+   Oid         oid = PG_GETARG_OID(0);
+
+   if (!SearchSysCacheExists1(COLLOID, ObjectIdGetDatum(oid)))
+       PG_RETURN_NULL();
+
+   PG_RETURN_BOOL(CollationIsVisible(oid));
+}
+
 Datum
 pg_conversion_is_visible(PG_FUNCTION_ARGS)
 {
index 9c249a7ff754e507a147ab9e2128da8a67dd95b6..8ceaab1fb12d939229515d3a4b8171b62e983c40 100644 (file)
@@ -114,6 +114,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
    values[i++] = ObjectIdGetDatum(InvalidOid); /* typbasetype */
    values[i++] = Int32GetDatum(-1);    /* typtypmod */
    values[i++] = Int32GetDatum(0);     /* typndims */
+   values[i++] = ObjectIdGetDatum(InvalidOid); /* typcollation */
    nulls[i++] = true;          /* typdefaultbin */
    nulls[i++] = true;          /* typdefault */
 
@@ -210,7 +211,8 @@ TypeCreate(Oid newTypeOid,
           char storage,
           int32 typeMod,
           int32 typNDims,      /* Array dimensions for baseType */
-          bool typeNotNull)
+          bool typeNotNull,
+          Oid typeCollation)
 {
    Relation    pg_type_desc;
    Oid         typeObjectId;
@@ -348,6 +350,7 @@ TypeCreate(Oid newTypeOid,
    values[i++] = ObjectIdGetDatum(baseType);   /* typbasetype */
    values[i++] = Int32GetDatum(typeMod);       /* typtypmod */
    values[i++] = Int32GetDatum(typNDims);      /* typndims */
+   values[i++] = ObjectIdGetDatum(typeCollation);  /* typcollation */
 
    /*
     * initialize the default binary value for this type.  Check for nulls of
index 718e996e6b8d7dd26e2b0616f5a89090001bd244..4fa1453b14f7135644315636bbdb5c59a83144b1 100644 (file)
@@ -671,6 +671,10 @@ COMMENT ON FUNCTION ts_debug(text) IS
 -- to get filled in.)
 --
 
+CREATE OR REPLACE FUNCTION
+  format_type(oid, int DEFAULT NULL, oid DEFAULT NULL)
+  RETURNS text STABLE LANGUAGE internal AS 'format_type';
+
 CREATE OR REPLACE FUNCTION
   pg_start_backup(label text, fast boolean DEFAULT false)
   RETURNS text STRICT VOLATILE LANGUAGE internal AS 'pg_start_backup';
index c4be3a9ae367bcceb9954b8716c84e21765558c4..5d5496df98963ccb0e808234f9e6667acafd65dd 100644 (file)
@@ -124,6 +124,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
    char        toast_relname[NAMEDATALEN];
    char        toast_idxname[NAMEDATALEN];
    IndexInfo  *indexInfo;
+   Oid         collationObjectId[2];
    Oid         classObjectId[2];
    int16       coloptions[2];
    ObjectAddress baseobject,
@@ -264,6 +265,9 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
    indexInfo->ii_Concurrent = false;
    indexInfo->ii_BrokenHotChain = false;
 
+   collationObjectId[0] = InvalidOid;
+   collationObjectId[1] = InvalidOid;
+
    classObjectId[0] = OID_BTREE_OPS_OID;
    classObjectId[1] = INT4_BTREE_OPS_OID;
 
@@ -275,7 +279,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
                               list_make2("chunk_id", "chunk_seq"),
                               BTREE_AM_OID,
                               rel->rd_rel->reltablespace,
-                              classObjectId, coloptions, (Datum) 0,
+                              collationObjectId, classObjectId, coloptions, (Datum) 0,
                               true, false, false, false,
                               true, false, false);
 
index 4c106dd8c572fc51ec9d06bfd04ee1e70ae6efa7..bafdc80d5847e43842b51591ad4f21bce1bbff74 100644 (file)
@@ -862,11 +862,13 @@ examine_attribute(Relation onerel, int attnum, Node *index_expr)
    {
        stats->attrtypid = exprType(index_expr);
        stats->attrtypmod = exprTypmod(index_expr);
+       stats->attrcollation = exprCollation(index_expr);
    }
    else
    {
        stats->attrtypid = attr->atttypid;
        stats->attrtypmod = attr->atttypmod;
+       stats->attrcollation = attr->attcollation;
    }
 
    typtuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(stats->attrtypid));
@@ -1929,6 +1931,7 @@ compute_minimal_stats(VacAttrStatsP stats,
    track_cnt = 0;
 
    fmgr_info(mystats->eqfunc, &f_cmpeq);
+   fmgr_info_collation(stats->attrcollation, &f_cmpeq);
 
    for (i = 0; i < samplerows; i++)
    {
@@ -2250,6 +2253,7 @@ compute_scalar_stats(VacAttrStatsP stats,
 
    SelectSortFunction(mystats->ltopr, false, &cmpFn, &cmpFlags);
    fmgr_info(cmpFn, &f_cmpfn);
+   fmgr_info_collation(stats->attrcollation, &f_cmpfn);
 
    /* Initial scan to find sortable values */
    for (i = 0; i < samplerows; i++)
index 9a9b4cbf3d96082c39bd51431691b4f276de5e24..c7e0c6a877873248be5acb36d5f6e8c0a67fa371 100644 (file)
@@ -356,8 +356,8 @@ createdb(const CreatedbStmt *stmt)
     *
     * Note: if you change this policy, fix initdb to match.
     */
-   ctype_encoding = pg_get_encoding_from_locale(dbctype);
-   collate_encoding = pg_get_encoding_from_locale(dbcollate);
+   ctype_encoding = pg_get_encoding_from_locale(dbctype, true);
+   collate_encoding = pg_get_encoding_from_locale(dbcollate, true);
 
    if (!(ctype_encoding == encoding ||
          ctype_encoding == PG_SQL_ASCII ||
index 2a2b7c732e84dbf36f7109f6b7f2e6164137e12a..dad65ee8ffaf06eab4b98aa17d732f65dde037f7 100644 (file)
@@ -87,7 +87,7 @@ compute_return_type(TypeName *returnType, Oid languageOid,
    Oid         rettype;
    Type        typtup;
 
-   typtup = LookupTypeName(NULL, returnType, NULL);
+   typtup = LookupTypeName(NULL, returnType, NULL, NULL);
 
    if (typtup)
    {
@@ -207,7 +207,7 @@ examine_parameter_list(List *parameters, Oid languageOid,
        Oid         toid;
        Type        typtup;
 
-       typtup = LookupTypeName(NULL, t, NULL);
+       typtup = LookupTypeName(NULL, t, NULL, NULL);
        if (typtup)
        {
            if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined)
index 94ed43700234f5021b486c1c0cb23050762af4ad..c8e21b68f58928967057bd39dd0d946dece8a990 100644 (file)
@@ -58,6 +58,7 @@
 /* non-export function prototypes */
 static void CheckPredicate(Expr *predicate);
 static void ComputeIndexAttrs(IndexInfo *indexInfo,
+                 Oid *collationOidP,
                  Oid *classOidP,
                  int16 *colOptionP,
                  List *attList,
@@ -124,6 +125,7 @@ DefineIndex(RangeVar *heapRelation,
            bool quiet,
            bool concurrent)
 {
+   Oid        *collationObjectId;
    Oid        *classObjectId;
    Oid         accessMethodId;
    Oid         relationId;
@@ -345,9 +347,10 @@ DefineIndex(RangeVar *heapRelation,
    indexInfo->ii_Concurrent = concurrent;
    indexInfo->ii_BrokenHotChain = false;
 
+   collationObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
    classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
    coloptions = (int16 *) palloc(numberOfAttributes * sizeof(int16));
-   ComputeIndexAttrs(indexInfo, classObjectId, coloptions, attributeList,
+   ComputeIndexAttrs(indexInfo, collationObjectId, classObjectId, coloptions, attributeList,
                      exclusionOpNames, relationId,
                      accessMethodName, accessMethodId,
                      amcanorder, isconstraint);
@@ -392,7 +395,7 @@ DefineIndex(RangeVar *heapRelation,
    indexRelationId =
        index_create(rel, indexRelationName, indexRelationId,
                     indexInfo, indexColNames,
-                    accessMethodId, tablespaceId, classObjectId,
+                    accessMethodId, tablespaceId, collationObjectId, classObjectId,
                     coloptions, reloptions, primary,
                     isconstraint, deferrable, initdeferred,
                     allowSystemTableMods,
@@ -764,6 +767,7 @@ CheckPredicate(Expr *predicate)
  */
 static void
 ComputeIndexAttrs(IndexInfo *indexInfo,
+                 Oid *collationOidP,
                  Oid *classOidP,
                  int16 *colOptionP,
                  List *attList,    /* list of IndexElem's */
@@ -800,6 +804,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
    {
        IndexElem  *attribute = (IndexElem *) lfirst(lc);
        Oid         atttype;
+       Oid         attcollation;
 
        /*
         * Process the column-or-expression to be indexed.
@@ -829,6 +834,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
            attform = (Form_pg_attribute) GETSTRUCT(atttuple);
            indexInfo->ii_KeyAttrNumbers[attn] = attform->attnum;
            atttype = attform->atttypid;
+           attcollation = attform->attcollation;
            ReleaseSysCache(atttuple);
        }
        else if (attribute->expr && IsA(attribute->expr, Var) &&
@@ -839,6 +845,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
 
            indexInfo->ii_KeyAttrNumbers[attn] = var->varattno;
            atttype = get_atttype(relId, var->varattno);
+           attcollation = var->varcollid;
        }
        else
        {
@@ -848,6 +855,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
            indexInfo->ii_Expressions = lappend(indexInfo->ii_Expressions,
                                                attribute->expr);
            atttype = exprType(attribute->expr);
+           attcollation = exprCollation(attribute->expr);
 
            /*
             * We don't currently support generation of an actual query plan
@@ -874,6 +882,20 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
                         errmsg("functions in index expression must be marked IMMUTABLE")));
        }
 
+       /*
+        * Collation override
+        */
+       if (attribute->collation)
+       {
+           if (!type_is_collatable(atttype))
+               ereport(ERROR,
+                       (errcode(ERRCODE_SYNTAX_ERROR),
+                        errmsg("collations are not supported by type %s",
+                               format_type_be(atttype))));
+           attcollation = get_collation_oid(attribute->collation, false);
+       }
+       collationOidP[attn] = attcollation;
+
        /*
         * Identify the opclass to use.
         */
index b927e76abd29dda23215b5fbc77bb24b57b8d9da..27917fc6c00e7221c2648fc474831e8a52687222 100644 (file)
@@ -15,6 +15,7 @@
 #include "catalog/catalog.h"
 #include "catalog/indexing.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_collation.h"
 #include "catalog/pg_seclabel.h"
 #include "commands/seclabel.h"
 #include "miscadmin.h"
@@ -194,6 +195,7 @@ GetSecurityLabel(const ObjectAddress *object, const char *provider)
                Anum_pg_seclabel_provider,
                BTEqualStrategyNumber, F_TEXTEQ,
                CStringGetTextDatum(provider));
+   ScanKeyEntryInitializeCollation(&keys[3], DEFAULT_COLLATION_OID);
 
    pg_seclabel = heap_open(SecLabelRelationId, AccessShareLock);
 
@@ -263,6 +265,7 @@ SetSecurityLabel(const ObjectAddress *object,
                Anum_pg_seclabel_provider,
                BTEqualStrategyNumber, F_TEXTEQ,
                CStringGetTextDatum(provider));
+   ScanKeyEntryInitializeCollation(&keys[3], DEFAULT_COLLATION_OID);
 
    pg_seclabel = heap_open(SecLabelRelationId, RowExclusiveLock);
 
index 0ff722d6f8a9b3a993953ef0610895c3212cc9b5..80ad516de1f17b4b517ffdf8e43d6c96cc1fede6 100644 (file)
@@ -143,53 +143,53 @@ DefineSequence(CreateSeqStmt *seq)
        switch (i)
        {
            case SEQ_COL_NAME:
-               coldef->typeName = makeTypeNameFromOid(NAMEOID, -1);
+               coldef->typeName = makeTypeNameFromOid(NAMEOID, -1, InvalidOid);
                coldef->colname = "sequence_name";
                namestrcpy(&name, seq->sequence->relname);
                value[i - 1] = NameGetDatum(&name);
                break;
            case SEQ_COL_LASTVAL:
-               coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+               coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid);
                coldef->colname = "last_value";
                value[i - 1] = Int64GetDatumFast(new.last_value);
                break;
            case SEQ_COL_STARTVAL:
-               coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+               coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid);
                coldef->colname = "start_value";
                value[i - 1] = Int64GetDatumFast(new.start_value);
                break;
            case SEQ_COL_INCBY:
-               coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+               coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid);
                coldef->colname = "increment_by";
                value[i - 1] = Int64GetDatumFast(new.increment_by);
                break;
            case SEQ_COL_MAXVALUE:
-               coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+               coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid);
                coldef->colname = "max_value";
                value[i - 1] = Int64GetDatumFast(new.max_value);
                break;
            case SEQ_COL_MINVALUE:
-               coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+               coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid);
                coldef->colname = "min_value";
                value[i - 1] = Int64GetDatumFast(new.min_value);
                break;
            case SEQ_COL_CACHE:
-               coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+               coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid);
                coldef->colname = "cache_value";
                value[i - 1] = Int64GetDatumFast(new.cache_value);
                break;
            case SEQ_COL_LOG:
-               coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
+               coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid);
                coldef->colname = "log_cnt";
                value[i - 1] = Int64GetDatum((int64) 1);
                break;
            case SEQ_COL_CYCLE:
-               coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
+               coldef->typeName = makeTypeNameFromOid(BOOLOID, -1, InvalidOid);
                coldef->colname = "is_cycled";
                value[i - 1] = BoolGetDatum(new.is_cycled);
                break;
            case SEQ_COL_CALLED:
-               coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
+               coldef->typeName = makeTypeNameFromOid(BOOLOID, -1, InvalidOid);
                coldef->colname = "is_called";
                value[i - 1] = BoolGetDatum(false);
                break;
index eac72106fdc8613314e4e3c9383485b224d3537e..c0a4e6f954a28fee2c1177d658b23d766f18275d 100644 (file)
@@ -1422,6 +1422,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
            {
                Oid         defTypeId;
                int32       deftypmod;
+               Oid         defCollId;
 
                /*
                 * Yes, try to merge the two column definitions. They must
@@ -1431,7 +1432,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
                        (errmsg("merging multiple inherited definitions of column \"%s\"",
                                attributeName)));
                def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
-               typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod);
+               typenameTypeIdModColl(NULL, def->typeName, &defTypeId, &deftypmod, &defCollId);
                if (defTypeId != attribute->atttypid ||
                    deftypmod != attribute->atttypmod)
                    ereport(ERROR,
@@ -1441,6 +1442,14 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
                             errdetail("%s versus %s",
                                       TypeNameToString(def->typeName),
                                       format_type_be(attribute->atttypid))));
+               if (defCollId != attribute->attcollation)
+                   ereport(ERROR,
+                           (errcode(ERRCODE_COLLATION_MISMATCH),
+                            errmsg("inherited column \"%s\" has a collation conflict",
+                                   attributeName),
+                            errdetail("\"%s\" versus \"%s\"",
+                                      get_collation_name(defCollId),
+                                      get_collation_name(attribute->attcollation))));
 
                /* Copy storage parameter */
                if (def->storage == 0)
@@ -1468,7 +1477,8 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
                def = makeNode(ColumnDef);
                def->colname = pstrdup(attributeName);
                def->typeName = makeTypeNameFromOid(attribute->atttypid,
-                                                   attribute->atttypmod);
+                                                   attribute->atttypmod,
+                                                   attribute->attcollation);
                def->inhcount = 1;
                def->is_local = false;
                def->is_not_null = attribute->attnotnull;
@@ -1594,6 +1604,8 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
                            newTypeId;
                int32       deftypmod,
                            newtypmod;
+               Oid         defcollid,
+                           newcollid;
 
                /*
                 * Yes, try to merge the two column definitions. They must
@@ -1603,8 +1615,8 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
                   (errmsg("merging column \"%s\" with inherited definition",
                           attributeName)));
                def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
-               typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod);
-               typenameTypeIdAndMod(NULL, newdef->typeName, &newTypeId, &newtypmod);
+               typenameTypeIdModColl(NULL, def->typeName, &defTypeId, &deftypmod, &defcollid);
+               typenameTypeIdModColl(NULL, newdef->typeName, &newTypeId, &newtypmod, &newcollid);
                if (defTypeId != newTypeId || deftypmod != newtypmod)
                    ereport(ERROR,
                            (errcode(ERRCODE_DATATYPE_MISMATCH),
@@ -1613,6 +1625,14 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
                             errdetail("%s versus %s",
                                       TypeNameToString(def->typeName),
                                       TypeNameToString(newdef->typeName))));
+               if (defcollid != newcollid)
+                   ereport(ERROR,
+                           (errcode(ERRCODE_COLLATION_MISMATCH),
+                            errmsg("column \"%s\" has a collation conflict",
+                                   attributeName),
+                            errdetail("\"%s\" versus \"%s\"",
+                                      get_collation_name(defcollid),
+                                      get_collation_name(newcollid))));
 
                /* Copy storage parameter */
                if (def->storage == 0)
@@ -4065,6 +4085,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
    HeapTuple   typeTuple;
    Oid         typeOid;
    int32       typmod;
+   Oid         collOid;
    Form_pg_type tform;
    Expr       *defval;
 
@@ -4085,15 +4106,24 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
            Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
            Oid         ctypeId;
            int32       ctypmod;
+           Oid         ccollid;
 
            /* Child column must match by type */
-           typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
+           typenameTypeIdModColl(NULL, colDef->typeName, &ctypeId, &ctypmod, &ccollid);
            if (ctypeId != childatt->atttypid ||
                ctypmod != childatt->atttypmod)
                ereport(ERROR,
                        (errcode(ERRCODE_DATATYPE_MISMATCH),
                         errmsg("child table \"%s\" has different type for column \"%s\"",
                            RelationGetRelationName(rel), colDef->colname)));
+           if (ccollid != childatt->attcollation)
+               ereport(ERROR,
+                       (errcode(ERRCODE_COLLATION_MISMATCH),
+                        errmsg("child table \"%s\" has different collation for column \"%s\"",
+                               RelationGetRelationName(rel), colDef->colname),
+                        errdetail("\"%s\" versus \"%s\"",
+                                  get_collation_name(ccollid),
+                                  get_collation_name(childatt->attcollation))));
 
            /* If it's OID, child column must actually be OID */
            if (isOid && childatt->attnum != ObjectIdAttributeNumber)
@@ -4151,7 +4181,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
                            MaxHeapAttributeNumber)));
    }
 
-   typeTuple = typenameType(NULL, colDef->typeName, &typmod);
+   typeTuple = typenameType(NULL, colDef->typeName, &typmod, &collOid);
    tform = (Form_pg_type) GETSTRUCT(typeTuple);
    typeOid = HeapTupleGetOid(typeTuple);
 
@@ -4176,6 +4206,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
    attribute.attisdropped = false;
    attribute.attislocal = colDef->is_local;
    attribute.attinhcount = colDef->inhcount;
+   attribute.attcollation = collOid;
    /* attribute.attacl is handled by InsertPgAttributeTuple */
 
    ReleaseSysCache(typeTuple);
@@ -4353,7 +4384,7 @@ ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd, LOC
        ColumnDef  *cdef = makeNode(ColumnDef);
 
        cdef->colname = pstrdup("oid");
-       cdef->typeName = makeTypeNameFromOid(OIDOID, -1);
+       cdef->typeName = makeTypeNameFromOid(OIDOID, -1, InvalidOid);
        cdef->inhcount = 0;
        cdef->is_local = true;
        cdef->is_not_null = true;
@@ -6415,6 +6446,7 @@ ATPrepAlterColumnType(List **wqueue,
    AttrNumber  attnum;
    Oid         targettype;
    int32       targettypmod;
+   Oid         targetcollid;
    Node       *transform;
    NewColumnValue *newval;
    ParseState *pstate = make_parsestate(NULL);
@@ -6449,7 +6481,7 @@ ATPrepAlterColumnType(List **wqueue,
                        colName)));
 
    /* Look up the target type */
-   typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
+   typenameTypeIdModColl(NULL, typeName, &targettype, &targettypmod, &targetcollid);
 
    /* make sure datatype is legal for a column */
    CheckAttributeType(colName, targettype, false);
@@ -6501,7 +6533,7 @@ ATPrepAlterColumnType(List **wqueue,
        else
        {
            transform = (Node *) makeVar(1, attnum,
-                                        attTup->atttypid, attTup->atttypmod,
+                                        attTup->atttypid, attTup->atttypmod, attTup->attcollation,
                                         0);
        }
 
@@ -6578,6 +6610,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
    Form_pg_type tform;
    Oid         targettype;
    int32       targettypmod;
+   Oid         targetcollid;
    Node       *defaultexpr;
    Relation    attrelation;
    Relation    depRel;
@@ -6606,7 +6639,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
                        colName)));
 
    /* Look up the target type (should not fail, since prep found it) */
-   typeTuple = typenameType(NULL, typeName, &targettypmod);
+   typeTuple = typenameType(NULL, typeName, &targettypmod, &targetcollid);
    tform = (Form_pg_type) GETSTRUCT(typeTuple);
    targettype = HeapTupleGetOid(typeTuple);
 
@@ -6880,6 +6913,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
     */
    attTup->atttypid = targettype;
    attTup->atttypmod = targettypmod;
+   attTup->attcollation = targetcollid;
    attTup->attndims = list_length(typeName->arrayBounds);
    attTup->attlen = tform->typlen;
    attTup->attbyval = tform->typbyval;
index 5500df03ab4be14cf13962ff117e8b2ca084f777..25d0f3596e14c47da1775b431897d5f4f167a369 100644 (file)
@@ -38,6 +38,7 @@
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/indexing.h"
+#include "catalog/pg_collation.h"
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_depend.h"
 #include "catalog/pg_enum.h"
@@ -118,6 +119,7 @@ DefineType(List *names, List *parameters)
    bool        byValue = false;
    char        alignment = 'i';    /* default alignment */
    char        storage = 'p';  /* default TOAST storage method */
+   Oid         collation = InvalidOid;
    DefElem    *likeTypeEl = NULL;
    DefElem    *internalLengthEl = NULL;
    DefElem    *inputNameEl = NULL;
@@ -135,6 +137,7 @@ DefineType(List *names, List *parameters)
    DefElem    *byValueEl = NULL;
    DefElem    *alignmentEl = NULL;
    DefElem    *storageEl = NULL;
+   DefElem    *collatableEl = NULL;
    Oid         inputOid;
    Oid         outputOid;
    Oid         receiveOid = InvalidOid;
@@ -261,6 +264,8 @@ DefineType(List *names, List *parameters)
            defelp = &alignmentEl;
        else if (pg_strcasecmp(defel->defname, "storage") == 0)
            defelp = &storageEl;
+       else if (pg_strcasecmp(defel->defname, "collatable") == 0)
+           defelp = &collatableEl;
        else
        {
            /* WARNING, not ERROR, for historical backwards-compatibility */
@@ -287,7 +292,7 @@ DefineType(List *names, List *parameters)
        Type        likeType;
        Form_pg_type likeForm;
 
-       likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL);
+       likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL, NULL);
        likeForm = (Form_pg_type) GETSTRUCT(likeType);
        internalLength = likeForm->typlen;
        byValue = likeForm->typbyval;
@@ -390,6 +395,8 @@ DefineType(List *names, List *parameters)
                    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                     errmsg("storage \"%s\" not recognized", a)));
    }
+   if (collatableEl)
+       collation = defGetBoolean(collatableEl) ? DEFAULT_COLLATION_OID : InvalidOid;
 
    /*
     * make sure we have our required definitions
@@ -562,7 +569,8 @@ DefineType(List *names, List *parameters)
                   storage,     /* TOAST strategy */
                   -1,          /* typMod (Domains only) */
                   0,           /* Array Dimensions of typbasetype */
-                  false);      /* Type NOT NULL */
+                  false,       /* Type NOT NULL */
+                  collation);
 
    /*
     * Create the array type that goes with it.
@@ -601,7 +609,8 @@ DefineType(List *names, List *parameters)
               'x',             /* ARRAY is always toastable */
               -1,              /* typMod (Domains only) */
               0,               /* Array dimensions of typbasetype */
-              false);          /* Type NOT NULL */
+              false,           /* Type NOT NULL */
+              collation);
 
    pfree(array_type);
 }
@@ -640,7 +649,7 @@ RemoveTypes(DropStmt *drop)
        typename = makeTypeNameFromNameList(names);
 
        /* Use LookupTypeName here so that shell types can be removed. */
-       tup = LookupTypeName(NULL, typename, NULL);
+       tup = LookupTypeName(NULL, typename, NULL, NULL);
        if (tup == NULL)
        {
            if (!drop->missing_ok)
@@ -767,6 +776,7 @@ DefineDomain(CreateDomainStmt *stmt)
    Oid         old_type_oid;
    Form_pg_type baseType;
    int32       basetypeMod;
+   Oid         baseColl;
 
    /* Convert list of names to a name and namespace */
    domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
@@ -797,7 +807,7 @@ DefineDomain(CreateDomainStmt *stmt)
    /*
     * Look up the base type.
     */
-   typeTup = typenameType(NULL, stmt->typeName, &basetypeMod);
+   typeTup = typenameType(NULL, stmt->typeName, &basetypeMod, &baseColl);
    baseType = (Form_pg_type) GETSTRUCT(typeTup);
    basetypeoid = HeapTupleGetOid(typeTup);
 
@@ -1040,7 +1050,8 @@ DefineDomain(CreateDomainStmt *stmt)
                   storage,     /* TOAST strategy */
                   basetypeMod, /* typeMod value */
                   typNDims,    /* Array dimensions for base type */
-                  typNotNull); /* Type NOT NULL */
+                  typNotNull,  /* Type NOT NULL */
+                  baseColl);
 
    /*
     * Process constraints which refer to the domain ID returned by TypeCreate
@@ -1149,7 +1160,8 @@ DefineEnum(CreateEnumStmt *stmt)
                   'p',         /* TOAST strategy always plain */
                   -1,          /* typMod (Domains only) */
                   0,           /* Array dimensions of typbasetype */
-                  false);      /* Type NOT NULL */
+                  false,       /* Type NOT NULL */
+                  InvalidOid); /* typcollation */
 
    /* Enter the enum's values into pg_enum */
    EnumValuesCreate(enumTypeOid, stmt->vals);
@@ -1188,7 +1200,8 @@ DefineEnum(CreateEnumStmt *stmt)
               'x',             /* ARRAY is always toastable */
               -1,              /* typMod (Domains only) */
               0,               /* Array dimensions of typbasetype */
-              false);          /* Type NOT NULL */
+              false,           /* Type NOT NULL */
+              InvalidOid);     /* typcollation */
 
    pfree(enumArrayName);
 }
@@ -2615,7 +2628,7 @@ AlterTypeOwner(List *names, Oid newOwnerId)
    typename = makeTypeNameFromNameList(names);
 
    /* Use LookupTypeName here so that shell types can be processed */
-   tup = LookupTypeName(NULL, typename, NULL);
+   tup = LookupTypeName(NULL, typename, NULL, NULL);
    if (tup == NULL)
        ereport(ERROR,
                (errcode(ERRCODE_UNDEFINED_OBJECT),
index a684172c8a5d2a33b021b41a33b217f038a6957e..22dfc923cf606fa8914caea87c76cca6b0f928c4 100644 (file)
@@ -120,7 +120,8 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
 
            def->colname = pstrdup(tle->resname);
            def->typeName = makeTypeNameFromOid(exprType((Node *) tle->expr),
-                                            exprTypmod((Node *) tle->expr));
+                                               exprTypmod((Node *) tle->expr),
+                                               exprCollation((Node *) tle->expr));
            def->inhcount = 0;
            def->is_local = true;
            def->is_not_null = false;
index 5e38c20ca6805f8be18d384cb9ff0c5b6403ef1f..2b5dd2dbf85e46efd7f48db684256656d91983f4 100644 (file)
@@ -166,6 +166,9 @@ static Datum ExecEvalFieldStore(FieldStoreState *fstate,
 static Datum ExecEvalRelabelType(GenericExprState *exprstate,
                    ExprContext *econtext,
                    bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalCollateClause(GenericExprState *exprstate,
+                   ExprContext *econtext,
+                   bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalCoerceViaIO(CoerceViaIOState *iostate,
                    ExprContext *econtext,
                    bool *isNull, ExprDoneCond *isDone);
@@ -1202,7 +1205,7 @@ init_fcache(Oid foid, FuncExprState *fcache,
 
    /* Set up the primary fmgr lookup information */
    fmgr_info_cxt(foid, &(fcache->func), fcacheCxt);
-   fcache->func.fn_expr = (Node *) fcache->xprstate.expr;
+   fmgr_info_expr((Node *) fcache->xprstate.expr, &(fcache->func));
 
    /* Initialize the function call parameter struct as well */
    InitFunctionCallInfoData(fcache->fcinfo_data, &(fcache->func),
@@ -4025,6 +4028,20 @@ ExecEvalRelabelType(GenericExprState *exprstate,
    return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone);
 }
 
+/* ----------------------------------------------------------------
+ *     ExecEvalCollateClause
+ *
+ *     Evaluate a CollateClause node.
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalCollateClause(GenericExprState *exprstate,
+                   ExprContext *econtext,
+                   bool *isNull, ExprDoneCond *isDone)
+{
+   return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone);
+}
+
 /* ----------------------------------------------------------------
  *     ExecEvalCoerceViaIO
  *
@@ -4114,7 +4131,7 @@ ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
                      econtext->ecxt_per_query_memory);
 
        /* Initialize additional info */
-       astate->elemfunc.fn_expr = (Node *) acoerce;
+       fmgr_info_expr((Node *) acoerce, &(astate->elemfunc));
    }
 
    /*
@@ -4484,6 +4501,16 @@ ExecInitExpr(Expr *node, PlanState *parent)
                state = (ExprState *) gstate;
            }
            break;
+       case T_CollateClause:
+           {
+               CollateClause *collate = (CollateClause *) node;
+               GenericExprState *gstate = makeNode(GenericExprState);
+
+               gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCollateClause;
+               gstate->arg = ExecInitExpr(collate->arg, parent);
+               state = (ExprState *) gstate;
+           }
+           break;
        case T_CoerceViaIO:
            {
                CoerceViaIO *iocoerce = (CoerceViaIO *) node;
@@ -4657,6 +4684,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
                List       *outlist;
                ListCell   *l;
                ListCell   *l2;
+               ListCell   *l3;
                int         i;
 
                rstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRowCompare;
@@ -4685,10 +4713,11 @@ ExecInitExpr(Expr *node, PlanState *parent)
                Assert(list_length(rcexpr->opfamilies) == nopers);
                rstate->funcs = (FmgrInfo *) palloc(nopers * sizeof(FmgrInfo));
                i = 0;
-               forboth(l, rcexpr->opnos, l2, rcexpr->opfamilies)
+               forthree(l, rcexpr->opnos, l2, rcexpr->opfamilies, l3, rcexpr->collids)
                {
                    Oid         opno = lfirst_oid(l);
                    Oid         opfamily = lfirst_oid(l2);
+                   Oid         collid = lfirst_oid(l3);
                    int         strategy;
                    Oid         lefttype;
                    Oid         righttype;
@@ -4710,6 +4739,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
                     * does this code.
                     */
                    fmgr_info(proc, &(rstate->funcs[i]));
+                   fmgr_info_collation(collid, &(rstate->funcs[i]));
                    i++;
                }
                state = (ExprState *) rstate;
@@ -4769,6 +4799,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
                 * code.
                 */
                fmgr_info(typentry->cmp_proc, &(mstate->cfunc));
+               fmgr_info_collation(minmaxexpr->collid, &(mstate->cfunc));
                state = (ExprState *) mstate;
            }
            break;
index 810ade23f65acd30797a8aaa34f3a0af1e0f8dd0..3f44ef0186ace8434ceaaf2ef37c14ca0fec632d 100644 (file)
@@ -937,11 +937,15 @@ ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk)
        if (skipjunk && tle->resjunk)
            continue;
        TupleDescInitEntry(typeInfo,
-                          cur_resno++,
+                          cur_resno,
                           tle->resname,
                           exprType((Node *) tle->expr),
                           exprTypmod((Node *) tle->expr),
                           0);
+       TupleDescInitEntryCollation(typeInfo,
+                                   cur_resno,
+                                   exprCollation((Node *) tle->expr));
+       cur_resno++;
    }
 
    return typeInfo;
@@ -969,11 +973,15 @@ ExecTypeFromExprList(List *exprList)
        sprintf(fldname, "f%d", cur_resno);
 
        TupleDescInitEntry(typeInfo,
-                          cur_resno++,
+                          cur_resno,
                           fldname,
                           exprType(e),
                           exprTypmod(e),
                           0);
+       TupleDescInitEntryCollation(typeInfo,
+                                   cur_resno,
+                                   exprCollation(e));
+       cur_resno++;
    }
 
    return typeInfo;
index cb652862edd895e63885b3e034dc8364b2047e2d..d9bed220e4af31d05300c7de1509ea33005ab2a1 100644 (file)
@@ -140,6 +140,7 @@ typedef struct AggStatePerAggData
    /* deconstructed sorting information (arrays of length numSortCols) */
    AttrNumber *sortColIdx;
    Oid        *sortOperators;
+   Oid        *sortCollations;
    bool       *sortNullsFirst;
 
    /*
@@ -315,12 +316,14 @@ initialize_aggregates(AggState *aggstate,
                (peraggstate->numInputs == 1) ?
                tuplesort_begin_datum(peraggstate->evaldesc->attrs[0]->atttypid,
                                      peraggstate->sortOperators[0],
+                                     peraggstate->sortCollations[0],
                                      peraggstate->sortNullsFirst[0],
                                      work_mem, false) :
                tuplesort_begin_heap(peraggstate->evaldesc,
                                     peraggstate->numSortCols,
                                     peraggstate->sortColIdx,
                                     peraggstate->sortOperators,
+                                    peraggstate->sortCollations,
                                     peraggstate->sortNullsFirst,
                                     work_mem, false);
        }
@@ -1668,16 +1671,17 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
                                aggref->aggtype,
                                transfn_oid,
                                finalfn_oid,
+                               aggref->collid,
                                &transfnexpr,
                                &finalfnexpr);
 
        fmgr_info(transfn_oid, &peraggstate->transfn);
-       peraggstate->transfn.fn_expr = (Node *) transfnexpr;
+       fmgr_info_expr((Node *) transfnexpr, &peraggstate->transfn);
 
        if (OidIsValid(finalfn_oid))
        {
            fmgr_info(finalfn_oid, &peraggstate->finalfn);
-           peraggstate->finalfn.fn_expr = (Node *) finalfnexpr;
+           fmgr_info_expr((Node *) finalfnexpr, &peraggstate->finalfn);
        }
 
        get_typlenbyval(aggref->aggtype,
@@ -1786,6 +1790,8 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
                (AttrNumber *) palloc(numSortCols * sizeof(AttrNumber));
            peraggstate->sortOperators =
                (Oid *) palloc(numSortCols * sizeof(Oid));
+           peraggstate->sortCollations =
+               (Oid *) palloc(numSortCols * sizeof(Oid));
            peraggstate->sortNullsFirst =
                (bool *) palloc(numSortCols * sizeof(bool));
 
@@ -1801,6 +1807,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 
                peraggstate->sortColIdx[i] = tle->resno;
                peraggstate->sortOperators[i] = sortcl->sortop;
+               peraggstate->sortCollations[i] = exprCollation((Node *) tle->expr);
                peraggstate->sortNullsFirst[i] = sortcl->nulls_first;
                i++;
            }
index 142f81767aa285ea929ac7a7bd73177839ab1545..dedd25501021682558df802207ad65e8cd8c1d3d 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "executor/nodeFunctionscan.h"
 #include "funcapi.h"
+#include "nodes/nodeFuncs.h"
 #include "utils/builtins.h"
 
 
@@ -185,12 +186,16 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
                           funcrettype,
                           -1,
                           0);
+       TupleDescInitEntryCollation(tupdesc,
+                                   (AttrNumber) 1,
+                                   exprCollation(node->funcexpr));
    }
    else if (functypclass == TYPEFUNC_RECORD)
    {
        tupdesc = BuildDescFromLists(node->funccolnames,
                                     node->funccoltypes,
-                                    node->funccoltypmods);
+                                    node->funccoltypmods,
+                                    node->funccolcollations);
    }
    else
    {
index bbf894e3ac13ebe8080c3a880a712376f3403936..55fce94b3215809823f607ec8e0b1f4b53d19032 100644 (file)
@@ -732,6 +732,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
        int         op_strategy;    /* operator's strategy number */
        Oid         op_lefttype;    /* operator's declared input types */
        Oid         op_righttype;
+       Oid         collation;
        Expr       *leftop;     /* expr on lhs of operator */
        Expr       *rightop;    /* expr on rhs ... */
        AttrNumber  varattno;   /* att number used in scan */
@@ -831,6 +832,8 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
                                   op_righttype,        /* strategy subtype */
                                   opfuncid,    /* reg proc to use */
                                   scanvalue);  /* constant */
+           ScanKeyEntryInitializeCollation(this_scan_key,
+                                           ((OpExpr *) clause)->collid);
        }
        else if (IsA(clause, RowCompareExpr))
        {
@@ -839,6 +842,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
            ListCell   *largs_cell = list_head(rc->largs);
            ListCell   *rargs_cell = list_head(rc->rargs);
            ListCell   *opnos_cell = list_head(rc->opnos);
+           ListCell   *collids_cell = list_head(rc->collids);
            ScanKey     first_sub_key;
            int         n_sub_key;
 
@@ -897,6 +901,9 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
                                             op_righttype,
                                             BTORDER_PROC);
 
+               collation = lfirst_oid(collids_cell);
+               collids_cell = lnext(collids_cell);
+
                /*
                 * rightop is the constant or variable comparison value
                 */
@@ -952,6 +959,8 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
                                       op_righttype,    /* strategy subtype */
                                       opfuncid,        /* reg proc to use */
                                       scanvalue);      /* constant */
+               ScanKeyEntryInitializeCollation(this_sub_key,
+                                               collation);
                n_sub_key++;
            }
 
@@ -1035,6 +1044,8 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
                                   op_righttype,        /* strategy subtype */
                                   opfuncid,    /* reg proc to use */
                                   (Datum) 0);  /* constant */
+           ScanKeyEntryInitializeCollation(this_scan_key,
+                                           saop->collid);
        }
        else if (IsA(clause, NullTest))
        {
index 24c5cd8a5b804211a022e50d96a1a5fedbc6ed62..e46af8cff93f351abcd75c1d8c2f649005e1e76b 100644 (file)
@@ -150,6 +150,9 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags)
                    sortFunction,
                    (Datum) 0);
 
+       ScanKeyEntryInitializeCollation(&mergestate->ms_scankeys[i],
+                                       node->collations[i]);
+
        /* However, we use btree's conventions for encoding directionality */
        if (reverse)
            mergestate->ms_scankeys[i].sk_flags |= SK_BT_DESC;
index 6f6645687f667c3468e386e157bd8677b4b78de5..c0b9f2308556e634b73a4b61ba10894afa8279d3 100644 (file)
@@ -180,6 +180,7 @@ typedef enum
 static MergeJoinClause
 MJExamineQuals(List *mergeclauses,
               Oid *mergefamilies,
+              Oid *mergecollations,
               int *mergestrategies,
               bool *mergenullsfirst,
               PlanState *parent)
@@ -197,6 +198,7 @@ MJExamineQuals(List *mergeclauses,
        OpExpr     *qual = (OpExpr *) lfirst(cl);
        MergeJoinClause clause = &clauses[iClause];
        Oid         opfamily = mergefamilies[iClause];
+       Oid         collation = mergecollations[iClause];
        StrategyNumber opstrategy = mergestrategies[iClause];
        bool        nulls_first = mergenullsfirst[iClause];
        int         op_strategy;
@@ -240,6 +242,7 @@ MJExamineQuals(List *mergeclauses,
 
        /* Set up the fmgr lookup information */
        fmgr_info(cmpproc, &(clause->cmpfinfo));
+       fmgr_info_collation(collation, &(clause->cmpfinfo));
 
        /* Fill the additional comparison-strategy flags */
        if (opstrategy == BTLessStrategyNumber)
@@ -1636,6 +1639,7 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
    mergestate->mj_NumClauses = list_length(node->mergeclauses);
    mergestate->mj_Clauses = MJExamineQuals(node->mergeclauses,
                                            node->mergeFamilies,
+                                           node->mergeCollations,
                                            node->mergeStrategies,
                                            node->mergeNullsFirst,
                                            (PlanState *) mergestate);
index 6a03d9ce823315e94403c03644e5f36cb0eed371..e4b28c59b45956acaf6c29d7f97ab6b2014eff8a 100644 (file)
@@ -86,6 +86,7 @@ ExecSort(SortState *node)
                                              plannode->numCols,
                                              plannode->sortColIdx,
                                              plannode->sortOperators,
+                                             plannode->collations,
                                              plannode->nullsFirst,
                                              work_mem,
                                              node->randomAccess);
index 8c263181fd63048f1096ffab03fa849ca6fbeae2..e9b3d76df1ca47fd7dd5b5d19a369950cee3d128 100644 (file)
@@ -831,7 +831,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 
            /* Lookup the equality function (potentially cross-type) */
            fmgr_info(opexpr->opfuncid, &sstate->cur_eq_funcs[i - 1]);
-           sstate->cur_eq_funcs[i - 1].fn_expr = (Node *) opexpr;
+           fmgr_info_expr((Node *) opexpr, &sstate->cur_eq_funcs[i - 1]);
 
            /* Look up the equality function for the RHS type */
            if (!get_compatible_hash_operators(opexpr->opno,
index f37ab39de01d148a40389a255671068971123af1..372262ad7f603c6492f897d9bf65602f2c205f73 100644 (file)
@@ -1561,7 +1561,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 
        fmgr_info_cxt(wfunc->winfnoid, &perfuncstate->flinfo,
                      econtext->ecxt_per_query_memory);
-       perfuncstate->flinfo.fn_expr = (Node *) wfunc;
+       fmgr_info_expr((Node *) wfunc, &perfuncstate->flinfo);
        get_typlenbyval(wfunc->wintype,
                        &perfuncstate->resulttypeLen,
                        &perfuncstate->resulttypeByVal);
@@ -1794,16 +1794,17 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
                            wfunc->wintype,
                            transfn_oid,
                            finalfn_oid,
+                           wfunc->collid,
                            &transfnexpr,
                            &finalfnexpr);
 
    fmgr_info(transfn_oid, &peraggstate->transfn);
-   peraggstate->transfn.fn_expr = (Node *) transfnexpr;
+   fmgr_info_expr((Node *) transfnexpr, &peraggstate->transfn);
 
    if (OidIsValid(finalfn_oid))
    {
        fmgr_info(finalfn_oid, &peraggstate->finalfn);
-       peraggstate->finalfn.fn_expr = (Node *) finalfnexpr;
+       fmgr_info_expr((Node *) finalfnexpr, &peraggstate->finalfn);
    }
 
    get_typlenbyval(wfunc->wintype,
index 662916d2108438cde39c90708b6d1623e7b2a6ca..9b2c874d6d0001b0c8246cb74f14679d351513ae 100644 (file)
@@ -223,6 +223,7 @@ _copyMergeAppend(MergeAppend *from)
    COPY_SCALAR_FIELD(numCols);
    COPY_POINTER_FIELD(sortColIdx, from->numCols * sizeof(AttrNumber));
    COPY_POINTER_FIELD(sortOperators, from->numCols * sizeof(Oid));
+   COPY_POINTER_FIELD(collations, from->numCols * sizeof(Oid));
    COPY_POINTER_FIELD(nullsFirst, from->numCols * sizeof(bool));
 
    return newnode;
@@ -479,6 +480,7 @@ _copyFunctionScan(FunctionScan *from)
    COPY_NODE_FIELD(funccolnames);
    COPY_NODE_FIELD(funccoltypes);
    COPY_NODE_FIELD(funccoltypmods);
+   COPY_NODE_FIELD(funccolcollations);
 
    return newnode;
 }
@@ -622,6 +624,7 @@ _copyMergeJoin(MergeJoin *from)
    COPY_NODE_FIELD(mergeclauses);
    numCols = list_length(from->mergeclauses);
    COPY_POINTER_FIELD(mergeFamilies, numCols * sizeof(Oid));
+   COPY_POINTER_FIELD(mergeCollations, numCols * sizeof(Oid));
    COPY_POINTER_FIELD(mergeStrategies, numCols * sizeof(int));
    COPY_POINTER_FIELD(mergeNullsFirst, numCols * sizeof(bool));
 
@@ -683,6 +686,7 @@ _copySort(Sort *from)
    COPY_SCALAR_FIELD(numCols);
    COPY_POINTER_FIELD(sortColIdx, from->numCols * sizeof(AttrNumber));
    COPY_POINTER_FIELD(sortOperators, from->numCols * sizeof(Oid));
+   COPY_POINTER_FIELD(collations, from->numCols * sizeof(Oid));
    COPY_POINTER_FIELD(nullsFirst, from->numCols * sizeof(bool));
 
    return newnode;
@@ -998,6 +1002,7 @@ _copyVar(Var *from)
    COPY_SCALAR_FIELD(varattno);
    COPY_SCALAR_FIELD(vartype);
    COPY_SCALAR_FIELD(vartypmod);
+   COPY_SCALAR_FIELD(varcollid);
    COPY_SCALAR_FIELD(varlevelsup);
    COPY_SCALAR_FIELD(varnoold);
    COPY_SCALAR_FIELD(varoattno);
@@ -1016,6 +1021,7 @@ _copyConst(Const *from)
 
    COPY_SCALAR_FIELD(consttype);
    COPY_SCALAR_FIELD(consttypmod);
+   COPY_SCALAR_FIELD(constcollid);
    COPY_SCALAR_FIELD(constlen);
 
    if (from->constbyval || from->constisnull)
@@ -1055,6 +1061,7 @@ _copyParam(Param *from)
    COPY_SCALAR_FIELD(paramid);
    COPY_SCALAR_FIELD(paramtype);
    COPY_SCALAR_FIELD(paramtypmod);
+   COPY_SCALAR_FIELD(paramcollation);
    COPY_LOCATION_FIELD(location);
 
    return newnode;
@@ -1075,6 +1082,7 @@ _copyAggref(Aggref *from)
    COPY_NODE_FIELD(aggdistinct);
    COPY_SCALAR_FIELD(aggstar);
    COPY_SCALAR_FIELD(agglevelsup);
+   COPY_SCALAR_FIELD(collid);
    COPY_LOCATION_FIELD(location);
 
    return newnode;
@@ -1094,6 +1102,7 @@ _copyWindowFunc(WindowFunc *from)
    COPY_SCALAR_FIELD(winref);
    COPY_SCALAR_FIELD(winstar);
    COPY_SCALAR_FIELD(winagg);
+   COPY_SCALAR_FIELD(collid);
    COPY_LOCATION_FIELD(location);
 
    return newnode;
@@ -1110,6 +1119,7 @@ _copyArrayRef(ArrayRef *from)
    COPY_SCALAR_FIELD(refarraytype);
    COPY_SCALAR_FIELD(refelemtype);
    COPY_SCALAR_FIELD(reftypmod);
+   COPY_SCALAR_FIELD(refcollid);
    COPY_NODE_FIELD(refupperindexpr);
    COPY_NODE_FIELD(reflowerindexpr);
    COPY_NODE_FIELD(refexpr);
@@ -1131,6 +1141,7 @@ _copyFuncExpr(FuncExpr *from)
    COPY_SCALAR_FIELD(funcretset);
    COPY_SCALAR_FIELD(funcformat);
    COPY_NODE_FIELD(args);
+   COPY_SCALAR_FIELD(collid);
    COPY_LOCATION_FIELD(location);
 
    return newnode;
@@ -1165,6 +1176,7 @@ _copyOpExpr(OpExpr *from)
    COPY_SCALAR_FIELD(opresulttype);
    COPY_SCALAR_FIELD(opretset);
    COPY_NODE_FIELD(args);
+   COPY_SCALAR_FIELD(collid);
    COPY_LOCATION_FIELD(location);
 
    return newnode;
@@ -1183,6 +1195,7 @@ _copyDistinctExpr(DistinctExpr *from)
    COPY_SCALAR_FIELD(opresulttype);
    COPY_SCALAR_FIELD(opretset);
    COPY_NODE_FIELD(args);
+   COPY_SCALAR_FIELD(collid);
    COPY_LOCATION_FIELD(location);
 
    return newnode;
@@ -1200,6 +1213,7 @@ _copyScalarArrayOpExpr(ScalarArrayOpExpr *from)
    COPY_SCALAR_FIELD(opfuncid);
    COPY_SCALAR_FIELD(useOr);
    COPY_NODE_FIELD(args);
+   COPY_SCALAR_FIELD(collid);
    COPY_LOCATION_FIELD(location);
 
    return newnode;
@@ -1252,6 +1266,7 @@ _copySubPlan(SubPlan *from)
    COPY_STRING_FIELD(plan_name);
    COPY_SCALAR_FIELD(firstColType);
    COPY_SCALAR_FIELD(firstColTypmod);
+   COPY_SCALAR_FIELD(firstColCollation);
    COPY_SCALAR_FIELD(useHashTable);
    COPY_SCALAR_FIELD(unknownEqFalse);
    COPY_NODE_FIELD(setParam);
@@ -1288,6 +1303,7 @@ _copyFieldSelect(FieldSelect *from)
    COPY_SCALAR_FIELD(fieldnum);
    COPY_SCALAR_FIELD(resulttype);
    COPY_SCALAR_FIELD(resulttypmod);
+   COPY_SCALAR_FIELD(resultcollation);
 
    return newnode;
 }
@@ -1385,6 +1401,7 @@ _copyCaseExpr(CaseExpr *from)
    CaseExpr   *newnode = makeNode(CaseExpr);
 
    COPY_SCALAR_FIELD(casetype);
+   COPY_SCALAR_FIELD(casecollation);
    COPY_NODE_FIELD(arg);
    COPY_NODE_FIELD(args);
    COPY_NODE_FIELD(defresult);
@@ -1418,6 +1435,7 @@ _copyCaseTestExpr(CaseTestExpr *from)
 
    COPY_SCALAR_FIELD(typeId);
    COPY_SCALAR_FIELD(typeMod);
+   COPY_SCALAR_FIELD(collation);
 
    return newnode;
 }
@@ -1467,6 +1485,7 @@ _copyRowCompareExpr(RowCompareExpr *from)
    COPY_SCALAR_FIELD(rctype);
    COPY_NODE_FIELD(opnos);
    COPY_NODE_FIELD(opfamilies);
+   COPY_NODE_FIELD(collids);
    COPY_NODE_FIELD(largs);
    COPY_NODE_FIELD(rargs);
 
@@ -1482,6 +1501,7 @@ _copyCoalesceExpr(CoalesceExpr *from)
    CoalesceExpr *newnode = makeNode(CoalesceExpr);
 
    COPY_SCALAR_FIELD(coalescetype);
+   COPY_SCALAR_FIELD(coalescecollation);
    COPY_NODE_FIELD(args);
    COPY_LOCATION_FIELD(location);
 
@@ -1499,6 +1519,7 @@ _copyMinMaxExpr(MinMaxExpr *from)
    COPY_SCALAR_FIELD(minmaxtype);
    COPY_SCALAR_FIELD(op);
    COPY_NODE_FIELD(args);
+   COPY_SCALAR_FIELD(collid);
    COPY_LOCATION_FIELD(location);
 
    return newnode;
@@ -1614,6 +1635,7 @@ _copySetToDefault(SetToDefault *from)
 
    COPY_SCALAR_FIELD(typeId);
    COPY_SCALAR_FIELD(typeMod);
+   COPY_SCALAR_FIELD(collid);
    COPY_LOCATION_FIELD(location);
 
    return newnode;
@@ -1719,6 +1741,7 @@ _copyPathKey(PathKey *from)
    /* EquivalenceClasses are never moved, so just shallow-copy the pointer */
    COPY_SCALAR_FIELD(pk_eclass);
    COPY_SCALAR_FIELD(pk_opfamily);
+   COPY_SCALAR_FIELD(pk_collation);
    COPY_SCALAR_FIELD(pk_strategy);
    COPY_SCALAR_FIELD(pk_nulls_first);
 
@@ -1871,12 +1894,14 @@ _copyRangeTblEntry(RangeTblEntry *from)
    COPY_NODE_FIELD(funcexpr);
    COPY_NODE_FIELD(funccoltypes);
    COPY_NODE_FIELD(funccoltypmods);
+   COPY_NODE_FIELD(funccolcollations);
    COPY_NODE_FIELD(values_lists);
    COPY_STRING_FIELD(ctename);
    COPY_SCALAR_FIELD(ctelevelsup);
    COPY_SCALAR_FIELD(self_reference);
    COPY_NODE_FIELD(ctecoltypes);
    COPY_NODE_FIELD(ctecoltypmods);
+   COPY_NODE_FIELD(ctecolcollations);
    COPY_NODE_FIELD(alias);
    COPY_NODE_FIELD(eref);
    COPY_SCALAR_FIELD(inh);
@@ -1960,6 +1985,7 @@ _copyCommonTableExpr(CommonTableExpr *from)
    COPY_NODE_FIELD(ctecolnames);
    COPY_NODE_FIELD(ctecoltypes);
    COPY_NODE_FIELD(ctecoltypmods);
+   COPY_NODE_FIELD(ctecolcollations);
 
    return newnode;
 }
@@ -2114,6 +2140,8 @@ _copyTypeName(TypeName *from)
    COPY_NODE_FIELD(typmods);
    COPY_SCALAR_FIELD(typemod);
    COPY_NODE_FIELD(arrayBounds);
+   COPY_NODE_FIELD(collnames);
+   COPY_SCALAR_FIELD(collOid);
    COPY_LOCATION_FIELD(location);
 
    return newnode;
@@ -2185,6 +2213,19 @@ _copyTypeCast(TypeCast *from)
    return newnode;
 }
 
+static CollateClause *
+_copyCollateClause(CollateClause *from)
+{
+   CollateClause   *newnode = makeNode(CollateClause);
+
+   COPY_NODE_FIELD(arg);
+   COPY_NODE_FIELD(collnames);
+   COPY_SCALAR_FIELD(collOid);
+   COPY_LOCATION_FIELD(location);
+
+   return newnode;
+}
+
 static IndexElem *
 _copyIndexElem(IndexElem *from)
 {
@@ -2193,6 +2234,7 @@ _copyIndexElem(IndexElem *from)
    COPY_STRING_FIELD(name);
    COPY_NODE_FIELD(expr);
    COPY_STRING_FIELD(indexcolname);
+   COPY_NODE_FIELD(collation);
    COPY_NODE_FIELD(opclass);
    COPY_SCALAR_FIELD(ordering);
    COPY_SCALAR_FIELD(nulls_ordering);
@@ -2403,6 +2445,7 @@ _copySetOperationStmt(SetOperationStmt *from)
    COPY_NODE_FIELD(rarg);
    COPY_NODE_FIELD(colTypes);
    COPY_NODE_FIELD(colTypmods);
+   COPY_NODE_FIELD(colCollations);
    COPY_NODE_FIELD(groupClauses);
 
    return newnode;
@@ -4328,6 +4371,9 @@ copyObject(void *from)
        case T_TypeCast:
            retval = _copyTypeCast(from);
            break;
+       case T_CollateClause:
+           retval = _copyCollateClause(from);
+           break;
        case T_SortBy:
            retval = _copySortBy(from);
            break;
index b7dc450447367e395b2fdfdbc328476a5a912bed..837eafaaccb673b046cea646b36d367c11246282 100644 (file)
@@ -137,6 +137,7 @@ _equalVar(Var *a, Var *b)
    COMPARE_SCALAR_FIELD(varattno);
    COMPARE_SCALAR_FIELD(vartype);
    COMPARE_SCALAR_FIELD(vartypmod);
+   COMPARE_SCALAR_FIELD(varcollid);
    COMPARE_SCALAR_FIELD(varlevelsup);
    COMPARE_SCALAR_FIELD(varnoold);
    COMPARE_SCALAR_FIELD(varoattno);
@@ -150,6 +151,7 @@ _equalConst(Const *a, Const *b)
 {
    COMPARE_SCALAR_FIELD(consttype);
    COMPARE_SCALAR_FIELD(consttypmod);
+   COMPARE_SCALAR_FIELD(constcollid);
    COMPARE_SCALAR_FIELD(constlen);
    COMPARE_SCALAR_FIELD(constisnull);
    COMPARE_SCALAR_FIELD(constbyval);
@@ -172,6 +174,7 @@ _equalParam(Param *a, Param *b)
    COMPARE_SCALAR_FIELD(paramid);
    COMPARE_SCALAR_FIELD(paramtype);
    COMPARE_SCALAR_FIELD(paramtypmod);
+   COMPARE_SCALAR_FIELD(paramcollation);
    COMPARE_LOCATION_FIELD(location);
 
    return true;
@@ -187,6 +190,7 @@ _equalAggref(Aggref *a, Aggref *b)
    COMPARE_NODE_FIELD(aggdistinct);
    COMPARE_SCALAR_FIELD(aggstar);
    COMPARE_SCALAR_FIELD(agglevelsup);
+   COMPARE_SCALAR_FIELD(collid);
    COMPARE_LOCATION_FIELD(location);
 
    return true;
@@ -201,6 +205,7 @@ _equalWindowFunc(WindowFunc *a, WindowFunc *b)
    COMPARE_SCALAR_FIELD(winref);
    COMPARE_SCALAR_FIELD(winstar);
    COMPARE_SCALAR_FIELD(winagg);
+   COMPARE_SCALAR_FIELD(collid);
    COMPARE_LOCATION_FIELD(location);
 
    return true;
@@ -212,6 +217,7 @@ _equalArrayRef(ArrayRef *a, ArrayRef *b)
    COMPARE_SCALAR_FIELD(refarraytype);
    COMPARE_SCALAR_FIELD(refelemtype);
    COMPARE_SCALAR_FIELD(reftypmod);
+   COMPARE_SCALAR_FIELD(refcollid);
    COMPARE_NODE_FIELD(refupperindexpr);
    COMPARE_NODE_FIELD(reflowerindexpr);
    COMPARE_NODE_FIELD(refexpr);
@@ -237,6 +243,7 @@ _equalFuncExpr(FuncExpr *a, FuncExpr *b)
        return false;
 
    COMPARE_NODE_FIELD(args);
+   COMPARE_SCALAR_FIELD(collid);
    COMPARE_LOCATION_FIELD(location);
 
    return true;
@@ -272,6 +279,7 @@ _equalOpExpr(OpExpr *a, OpExpr *b)
    COMPARE_SCALAR_FIELD(opresulttype);
    COMPARE_SCALAR_FIELD(opretset);
    COMPARE_NODE_FIELD(args);
+   COMPARE_SCALAR_FIELD(collid);
    COMPARE_LOCATION_FIELD(location);
 
    return true;
@@ -296,6 +304,7 @@ _equalDistinctExpr(DistinctExpr *a, DistinctExpr *b)
    COMPARE_SCALAR_FIELD(opresulttype);
    COMPARE_SCALAR_FIELD(opretset);
    COMPARE_NODE_FIELD(args);
+   COMPARE_SCALAR_FIELD(collid);
    COMPARE_LOCATION_FIELD(location);
 
    return true;
@@ -319,6 +328,7 @@ _equalScalarArrayOpExpr(ScalarArrayOpExpr *a, ScalarArrayOpExpr *b)
 
    COMPARE_SCALAR_FIELD(useOr);
    COMPARE_NODE_FIELD(args);
+   COMPARE_SCALAR_FIELD(collid);
    COMPARE_LOCATION_FIELD(location);
 
    return true;
@@ -356,6 +366,7 @@ _equalSubPlan(SubPlan *a, SubPlan *b)
    COMPARE_STRING_FIELD(plan_name);
    COMPARE_SCALAR_FIELD(firstColType);
    COMPARE_SCALAR_FIELD(firstColTypmod);
+   COMPARE_SCALAR_FIELD(firstColCollation);
    COMPARE_SCALAR_FIELD(useHashTable);
    COMPARE_SCALAR_FIELD(unknownEqFalse);
    COMPARE_NODE_FIELD(setParam);
@@ -382,6 +393,7 @@ _equalFieldSelect(FieldSelect *a, FieldSelect *b)
    COMPARE_SCALAR_FIELD(fieldnum);
    COMPARE_SCALAR_FIELD(resulttype);
    COMPARE_SCALAR_FIELD(resulttypmod);
+   COMPARE_SCALAR_FIELD(resultcollation);
 
    return true;
 }
@@ -485,6 +497,7 @@ static bool
 _equalCaseExpr(CaseExpr *a, CaseExpr *b)
 {
    COMPARE_SCALAR_FIELD(casetype);
+   COMPARE_SCALAR_FIELD(casecollation);
    COMPARE_NODE_FIELD(arg);
    COMPARE_NODE_FIELD(args);
    COMPARE_NODE_FIELD(defresult);
@@ -508,6 +521,7 @@ _equalCaseTestExpr(CaseTestExpr *a, CaseTestExpr *b)
 {
    COMPARE_SCALAR_FIELD(typeId);
    COMPARE_SCALAR_FIELD(typeMod);
+   COMPARE_SCALAR_FIELD(collation);
 
    return true;
 }
@@ -551,6 +565,7 @@ _equalRowCompareExpr(RowCompareExpr *a, RowCompareExpr *b)
    COMPARE_SCALAR_FIELD(rctype);
    COMPARE_NODE_FIELD(opnos);
    COMPARE_NODE_FIELD(opfamilies);
+   COMPARE_NODE_FIELD(collids);
    COMPARE_NODE_FIELD(largs);
    COMPARE_NODE_FIELD(rargs);
 
@@ -561,6 +576,7 @@ static bool
 _equalCoalesceExpr(CoalesceExpr *a, CoalesceExpr *b)
 {
    COMPARE_SCALAR_FIELD(coalescetype);
+   COMPARE_SCALAR_FIELD(coalescecollation);
    COMPARE_NODE_FIELD(args);
    COMPARE_LOCATION_FIELD(location);
 
@@ -573,6 +589,7 @@ _equalMinMaxExpr(MinMaxExpr *a, MinMaxExpr *b)
    COMPARE_SCALAR_FIELD(minmaxtype);
    COMPARE_SCALAR_FIELD(op);
    COMPARE_NODE_FIELD(args);
+   COMPARE_SCALAR_FIELD(collid);
    COMPARE_LOCATION_FIELD(location);
 
    return true;
@@ -673,6 +690,7 @@ _equalSetToDefault(SetToDefault *a, SetToDefault *b)
 {
    COMPARE_SCALAR_FIELD(typeId);
    COMPARE_SCALAR_FIELD(typeMod);
+   COMPARE_SCALAR_FIELD(collid);
    COMPARE_LOCATION_FIELD(location);
 
    return true;
@@ -759,6 +777,7 @@ _equalPathKey(PathKey *a, PathKey *b)
    if (a_eclass != b_eclass)
        return false;
    COMPARE_SCALAR_FIELD(pk_opfamily);
+   COMPARE_SCALAR_FIELD(pk_collation);
    COMPARE_SCALAR_FIELD(pk_strategy);
    COMPARE_SCALAR_FIELD(pk_nulls_first);
 
@@ -965,6 +984,7 @@ _equalSetOperationStmt(SetOperationStmt *a, SetOperationStmt *b)
    COMPARE_NODE_FIELD(rarg);
    COMPARE_NODE_FIELD(colTypes);
    COMPARE_NODE_FIELD(colTypmods);
+   COMPARE_NODE_FIELD(colCollations);
    COMPARE_NODE_FIELD(groupClauses);
 
    return true;
@@ -2079,6 +2099,8 @@ _equalTypeName(TypeName *a, TypeName *b)
    COMPARE_NODE_FIELD(typmods);
    COMPARE_SCALAR_FIELD(typemod);
    COMPARE_NODE_FIELD(arrayBounds);
+   COMPARE_NODE_FIELD(collnames);
+   COMPARE_SCALAR_FIELD(collOid);
    COMPARE_LOCATION_FIELD(location);
 
    return true;
@@ -2094,6 +2116,17 @@ _equalTypeCast(TypeCast *a, TypeCast *b)
    return true;
 }
 
+static bool
+_equalCollateClause(CollateClause *a, CollateClause *b)
+{
+   COMPARE_NODE_FIELD(arg);
+   COMPARE_NODE_FIELD(collnames);
+   COMPARE_SCALAR_FIELD(collOid);
+   COMPARE_LOCATION_FIELD(location);
+
+   return true;
+}
+
 static bool
 _equalSortBy(SortBy *a, SortBy *b)
 {
@@ -2146,6 +2179,7 @@ _equalIndexElem(IndexElem *a, IndexElem *b)
    COMPARE_STRING_FIELD(name);
    COMPARE_NODE_FIELD(expr);
    COMPARE_STRING_FIELD(indexcolname);
+   COMPARE_NODE_FIELD(collation);
    COMPARE_NODE_FIELD(opclass);
    COMPARE_SCALAR_FIELD(ordering);
    COMPARE_SCALAR_FIELD(nulls_ordering);
@@ -2229,12 +2263,14 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
    COMPARE_NODE_FIELD(funcexpr);
    COMPARE_NODE_FIELD(funccoltypes);
    COMPARE_NODE_FIELD(funccoltypmods);
+   COMPARE_NODE_FIELD(funccolcollations);
    COMPARE_NODE_FIELD(values_lists);
    COMPARE_STRING_FIELD(ctename);
    COMPARE_SCALAR_FIELD(ctelevelsup);
    COMPARE_SCALAR_FIELD(self_reference);
    COMPARE_NODE_FIELD(ctecoltypes);
    COMPARE_NODE_FIELD(ctecoltypmods);
+   COMPARE_NODE_FIELD(ctecolcollations);
    COMPARE_NODE_FIELD(alias);
    COMPARE_NODE_FIELD(eref);
    COMPARE_SCALAR_FIELD(inh);
@@ -2308,6 +2344,7 @@ _equalCommonTableExpr(CommonTableExpr *a, CommonTableExpr *b)
    COMPARE_NODE_FIELD(ctecolnames);
    COMPARE_NODE_FIELD(ctecoltypes);
    COMPARE_NODE_FIELD(ctecoltypmods);
+   COMPARE_NODE_FIELD(ctecolcollations);
 
    return true;
 }
@@ -2941,6 +2978,9 @@ equal(void *a, void *b)
        case T_TypeCast:
            retval = _equalTypeCast(a, b);
            break;
+       case T_CollateClause:
+           retval = _equalCollateClause(a, b);
+           break;
        case T_SortBy:
            retval = _equalSortBy(a, b);
            break;
index 79da1853c385026a31aae2b917ac9231da4cc3cb..0225f19382a583e6dd0659a9f23d9605961f496a 100644 (file)
@@ -67,6 +67,7 @@ makeVar(Index varno,
        AttrNumber varattno,
        Oid vartype,
        int32 vartypmod,
+       Oid varcollid,
        Index varlevelsup)
 {
    Var        *var = makeNode(Var);
@@ -75,6 +76,7 @@ makeVar(Index varno,
    var->varattno = varattno;
    var->vartype = vartype;
    var->vartypmod = vartypmod;
+   var->varcollid = varcollid;
    var->varlevelsup = varlevelsup;
 
    /*
@@ -105,6 +107,7 @@ makeVarFromTargetEntry(Index varno,
                   tle->resno,
                   exprType((Node *) tle->expr),
                   exprTypmod((Node *) tle->expr),
+                  exprCollation((Node *) tle->expr),
                   0);
 }
 
@@ -139,6 +142,7 @@ makeWholeRowVar(RangeTblEntry *rte,
                             InvalidAttrNumber,
                             toid,
                             -1,
+                            InvalidOid,
                             varlevelsup);
            break;
        case RTE_FUNCTION:
@@ -150,6 +154,7 @@ makeWholeRowVar(RangeTblEntry *rte,
                                 InvalidAttrNumber,
                                 toid,
                                 -1,
+                                InvalidOid,
                                 varlevelsup);
            }
            else
@@ -164,6 +169,7 @@ makeWholeRowVar(RangeTblEntry *rte,
                                 1,
                                 toid,
                                 -1,
+                                InvalidOid,
                                 varlevelsup);
            }
            break;
@@ -174,6 +180,7 @@ makeWholeRowVar(RangeTblEntry *rte,
                             InvalidAttrNumber,
                             toid,
                             -1,
+                            InvalidOid,
                             varlevelsup);
            break;
        default:
@@ -188,6 +195,7 @@ makeWholeRowVar(RangeTblEntry *rte,
                             InvalidAttrNumber,
                             RECORDOID,
                             -1,
+                            InvalidOid,
                             varlevelsup);
            break;
    }
@@ -272,6 +280,7 @@ makeConst(Oid consttype,
 
    cnst->consttype = consttype;
    cnst->consttypmod = consttypmod;
+   cnst->constcollid = get_typcollation(consttype);
    cnst->constlen = constlen;
    cnst->constvalue = constvalue;
    cnst->constisnull = constisnull;
@@ -418,15 +427,16 @@ makeTypeNameFromNameList(List *names)
 
 /*
  * makeTypeNameFromOid -
- * build a TypeName node to represent a type already known by OID/typmod.
+ * build a TypeName node to represent a type already known by OID/typmod/collation.
  */
 TypeName *
-makeTypeNameFromOid(Oid typeOid, int32 typmod)
+makeTypeNameFromOid(Oid typeOid, int32 typmod, Oid collOid)
 {
    TypeName   *n = makeNode(TypeName);
 
    n->typeOid = typeOid;
    n->typemod = typmod;
+   n->collOid = collOid;
    n->location = -1;
    return n;
 }
@@ -438,7 +448,7 @@ makeTypeNameFromOid(Oid typeOid, int32 typmod)
  * The argument expressions must have been transformed already.
  */
 FuncExpr *
-makeFuncExpr(Oid funcid, Oid rettype, List *args, CoercionForm fformat)
+makeFuncExpr(Oid funcid, Oid rettype, List *args, Oid collid, CoercionForm fformat)
 {
    FuncExpr   *funcexpr;
 
@@ -448,6 +458,7 @@ makeFuncExpr(Oid funcid, Oid rettype, List *args, CoercionForm fformat)
    funcexpr->funcretset = false;       /* only allowed case here */
    funcexpr->funcformat = fformat;
    funcexpr->args = args;
+   funcexpr->collid = collid;
    funcexpr->location = -1;
 
    return funcexpr;
index d17b347e45c03e65a4e630094745d1e29b0cf7f7..8a23047d382e9474aa3eea0bffa89ac2259a3a81 100644 (file)
@@ -14,6 +14,7 @@
  */
 #include "postgres.h"
 
+#include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
 #include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
@@ -161,6 +162,9 @@ exprType(Node *expr)
        case T_RelabelType:
            type = ((RelabelType *) expr)->resulttype;
            break;
+       case T_CollateClause:
+           type = exprType((Node *) ((CollateClause *) expr)->arg);
+           break;
        case T_CoerceViaIO:
            type = ((CoerceViaIO *) expr)->resulttype;
            break;
@@ -459,6 +463,215 @@ exprTypmod(Node *expr)
    return -1;
 }
 
+/*
+ * exprCollation -
+ *   returns the Oid of the collation of the expression's result.
+ */
+Oid
+exprCollation(Node *expr)
+{
+   Oid         coll;
+
+   if (!expr)
+       return InvalidOid;
+
+   switch (nodeTag(expr))
+   {
+       case T_Var:
+           coll = ((Var *) expr)->varcollid;
+           break;
+       case T_Const:
+           coll = ((Const *) expr)->constcollid;
+           break;
+       case T_Param:
+           coll = ((Param *) expr)->paramcollation;
+           break;
+       case T_Aggref:
+           coll = ((Aggref *) expr)->collid;
+           break;
+       case T_WindowFunc:
+           coll = ((WindowFunc *) expr)->collid;
+           break;
+       case T_ArrayRef:
+           coll = ((ArrayRef *) expr)->refcollid;
+           break;
+       case T_FuncExpr:
+           coll = ((FuncExpr *) expr)->collid;
+           break;
+       case T_NamedArgExpr:
+           coll = exprCollation((Node *) ((NamedArgExpr *) expr)->arg);
+           break;
+       case T_OpExpr:
+           coll = ((OpExpr *) expr)->collid;
+           break;
+       case T_DistinctExpr:
+           coll = ((DistinctExpr *) expr)->collid;
+           break;
+       case T_ScalarArrayOpExpr:
+           coll = ((ScalarArrayOpExpr *) expr)->collid;
+           break;
+       case T_BoolExpr:
+           coll = InvalidOid; /* not applicable */
+           break;
+       case T_SubLink:
+           {
+               SubLink    *sublink = (SubLink *) expr;
+
+               if (sublink->subLinkType == EXPR_SUBLINK ||
+                   sublink->subLinkType == ARRAY_SUBLINK)
+               {
+                   /* get the collation of the subselect's first target column */
+                   Query      *qtree = (Query *) sublink->subselect;
+                   TargetEntry *tent;
+
+                   if (!qtree || !IsA(qtree, Query))
+                       elog(ERROR, "cannot get collation for untransformed sublink");
+                   tent = (TargetEntry *) linitial(qtree->targetList);
+                   Assert(IsA(tent, TargetEntry));
+                   Assert(!tent->resjunk);
+                   coll = exprCollation((Node *) tent->expr);
+                   /* note we don't need to care if it's an array */
+               }
+               else
+                   coll = InvalidOid;
+           }
+           break;
+       case T_SubPlan:
+           {
+               SubPlan    *subplan = (SubPlan *) expr;
+
+               if (subplan->subLinkType == EXPR_SUBLINK ||
+                   subplan->subLinkType == ARRAY_SUBLINK)
+               {
+                   /* get the collation of the subselect's first target column */
+                   /* note we don't need to care if it's an array */
+                   coll = subplan->firstColCollation;
+               }
+               else
+               {
+                   /* for all other subplan types, result is boolean */
+                   coll = InvalidOid;
+               }
+           }
+           break;
+       case T_AlternativeSubPlan:
+           {
+               AlternativeSubPlan *asplan = (AlternativeSubPlan *) expr;
+
+               /* subplans should all return the same thing */
+               coll = exprCollation((Node *) linitial(asplan->subplans));
+           }
+           break;
+       case T_FieldSelect:
+           coll = ((FieldSelect *) expr)->resultcollation;
+           break;
+       case T_FieldStore:
+           coll = InvalidOid; /* not applicable */
+           break;
+       case T_RelabelType:
+           coll = exprCollation((Node *) ((RelabelType *) expr)->arg);
+           break;
+       case T_CollateClause:
+           coll = ((CollateClause *) expr)->collOid;
+           break;
+       case T_CoerceViaIO:
+       {
+           CoerceViaIO *cvio = (CoerceViaIO *) expr;
+           coll = coercion_expression_result_collation(cvio->resulttype, (Node *) cvio->arg);
+           break;
+       }
+       case T_ArrayCoerceExpr:
+       {
+           ArrayCoerceExpr *ace = (ArrayCoerceExpr *) expr;
+           coll = coercion_expression_result_collation(ace->resulttype, (Node *) ace->arg);
+           break;
+       }
+       case T_ConvertRowtypeExpr:
+       {
+           ConvertRowtypeExpr *cre = (ConvertRowtypeExpr *) expr;
+           coll = coercion_expression_result_collation(cre->resulttype, (Node *) cre->arg);
+           break;
+       }
+       case T_CaseExpr:
+           coll = ((CaseExpr *) expr)->casecollation;
+           break;
+       case T_CaseTestExpr:
+           coll = ((CaseTestExpr *) expr)->collation;
+           break;
+       case T_ArrayExpr:
+           coll = get_typcollation(((ArrayExpr *) expr)->array_typeid);
+           break;
+       case T_RowExpr:
+           coll = InvalidOid; /* not applicable */
+           break;
+       case T_RowCompareExpr:
+           coll = InvalidOid; /* not applicable */
+           break;
+       case T_CoalesceExpr:
+           coll = ((CoalesceExpr *) expr)->coalescecollation;
+           break;
+       case T_MinMaxExpr:
+           coll = ((MinMaxExpr *) expr)->collid;
+           break;
+       case T_XmlExpr:
+           if (((XmlExpr *) expr)->op == IS_XMLSERIALIZE)
+               coll = DEFAULT_COLLATION_OID;
+           else
+               coll = InvalidOid;
+           break;
+       case T_NullIfExpr:
+           coll = exprCollation((Node *) linitial(((NullIfExpr *) expr)->args));
+           break;
+       case T_NullTest:
+           coll = InvalidOid; /* not applicable */
+           break;
+       case T_BooleanTest:
+           coll = InvalidOid; /* not applicable */
+           break;
+       case T_CoerceToDomain:
+           coll = get_typcollation(((CoerceToDomain *) expr)->resulttype);
+           if (coll == DEFAULT_COLLATION_OID)
+               coll = exprCollation((Node *) ((CoerceToDomain *) expr)->arg);
+           break;
+       case T_CoerceToDomainValue:
+           coll = get_typcollation(((CoerceToDomainValue *) expr)->typeId);
+           break;
+       case T_SetToDefault:
+           coll = ((SetToDefault *) expr)->collid;
+           break;
+       case T_CurrentOfExpr:
+           coll = InvalidOid; /* not applicable */
+           break;
+       case T_PlaceHolderVar:
+           coll = exprCollation((Node *) ((PlaceHolderVar *) expr)->phexpr);
+           break;
+       default:
+           elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
+           coll = InvalidOid;  /* keep compiler quiet */
+           break;
+   }
+
+   return coll;
+}
+
+/*
+ * Compute the result collation of a coercion-like expression that
+ * converts arg to resulttype.
+ */
+Oid
+coercion_expression_result_collation(Oid resulttype, Node *arg)
+{
+   if (type_is_collatable(resulttype))
+   {
+       if (type_is_collatable(exprType(arg)))
+           return exprCollation(arg);
+       else
+           return DEFAULT_COLLATION_OID;
+   }
+   else
+       return InvalidOid;
+}
+
 /*
  * exprIsLengthCoercion
  *     Detect whether an expression tree is an application of a datatype's
@@ -908,6 +1121,9 @@ exprLocation(Node *expr)
                loc = leftmostLoc(loc, tc->location);
            }
            break;
+       case T_CollateClause:
+           loc = ((CollateClause *) expr)->location;
+           break;
        case T_SortBy:
            /* just use argument's location (ignore operator, if any) */
            loc = exprLocation(((SortBy *) expr)->node);
@@ -1220,6 +1436,8 @@ expression_tree_walker(Node *node,
            break;
        case T_RelabelType:
            return walker(((RelabelType *) node)->arg, context);
+       case T_CollateClause:
+           return walker(((CollateClause *) node)->arg, context);
        case T_CoerceViaIO:
            return walker(((CoerceViaIO *) node)->arg, context);
        case T_ArrayCoerceExpr:
@@ -1776,6 +1994,16 @@ expression_tree_mutator(Node *node,
                return (Node *) newnode;
            }
            break;
+       case T_CollateClause:
+           {
+               CollateClause *collate = (CollateClause *) node;
+               CollateClause *newnode;
+
+               FLATCOPY(newnode, collate, CollateClause);
+               MUTATE(newnode->arg, collate->arg, Expr *);
+               return (Node *) newnode;
+           }
+           break;
        case T_CoerceViaIO:
            {
                CoerceViaIO *iocoerce = (CoerceViaIO *) node;
@@ -2471,6 +2699,8 @@ bool
                    return true;
            }
            break;
+       case T_CollateClause:
+           return walker(((CollateClause *) node)->arg, context);
        case T_SortBy:
            return walker(((SortBy *) node)->node, context);
        case T_WindowDef:
index c8eccce5a7a3d22d187b05d8e60eb19d5fd59fe5..3b3e5448fd50eca66d8b8c36f8fa8855c0a68fee 100644 (file)
@@ -365,6 +365,10 @@ _outMergeAppend(StringInfo str, MergeAppend *node)
    for (i = 0; i < node->numCols; i++)
        appendStringInfo(str, " %u", node->sortOperators[i]);
 
+   appendStringInfo(str, " :collations");
+   for (i = 0; i < node->numCols; i++)
+       appendStringInfo(str, " %u", node->collations[i]);
+
    appendStringInfo(str, " :nullsFirst");
    for (i = 0; i < node->numCols; i++)
        appendStringInfo(str, " %s", booltostr(node->nullsFirst[i]));
@@ -499,6 +503,7 @@ _outFunctionScan(StringInfo str, FunctionScan *node)
    WRITE_NODE_FIELD(funccolnames);
    WRITE_NODE_FIELD(funccoltypes);
    WRITE_NODE_FIELD(funccoltypmods);
+   WRITE_NODE_FIELD(funccolcollations);
 }
 
 static void
@@ -568,6 +573,10 @@ _outMergeJoin(StringInfo str, MergeJoin *node)
    for (i = 0; i < numCols; i++)
        appendStringInfo(str, " %u", node->mergeFamilies[i]);
 
+   appendStringInfo(str, " :mergeCollations");
+   for (i = 0; i < numCols; i++)
+       appendStringInfo(str, " %u", node->mergeCollations[i]);
+
    appendStringInfo(str, " :mergeStrategies");
    for (i = 0; i < numCols; i++)
        appendStringInfo(str, " %d", node->mergeStrategies[i]);
@@ -692,6 +701,10 @@ _outSort(StringInfo str, Sort *node)
    for (i = 0; i < node->numCols; i++)
        appendStringInfo(str, " %u", node->sortOperators[i]);
 
+   appendStringInfo(str, " :collations");
+   for (i = 0; i < node->numCols; i++)
+       appendStringInfo(str, " %u", node->collations[i]);
+
    appendStringInfo(str, " :nullsFirst");
    for (i = 0; i < node->numCols; i++)
        appendStringInfo(str, " %s", booltostr(node->nullsFirst[i]));
@@ -864,6 +877,7 @@ _outVar(StringInfo str, Var *node)
    WRITE_INT_FIELD(varattno);
    WRITE_OID_FIELD(vartype);
    WRITE_INT_FIELD(vartypmod);
+   WRITE_OID_FIELD(varcollid);
    WRITE_UINT_FIELD(varlevelsup);
    WRITE_UINT_FIELD(varnoold);
    WRITE_INT_FIELD(varoattno);
@@ -877,6 +891,7 @@ _outConst(StringInfo str, Const *node)
 
    WRITE_OID_FIELD(consttype);
    WRITE_INT_FIELD(consttypmod);
+   WRITE_OID_FIELD(constcollid);
    WRITE_INT_FIELD(constlen);
    WRITE_BOOL_FIELD(constbyval);
    WRITE_BOOL_FIELD(constisnull);
@@ -898,6 +913,7 @@ _outParam(StringInfo str, Param *node)
    WRITE_INT_FIELD(paramid);
    WRITE_OID_FIELD(paramtype);
    WRITE_INT_FIELD(paramtypmod);
+   WRITE_OID_FIELD(paramcollation);
    WRITE_LOCATION_FIELD(location);
 }
 
@@ -913,6 +929,7 @@ _outAggref(StringInfo str, Aggref *node)
    WRITE_NODE_FIELD(aggdistinct);
    WRITE_BOOL_FIELD(aggstar);
    WRITE_UINT_FIELD(agglevelsup);
+   WRITE_OID_FIELD(collid);
    WRITE_LOCATION_FIELD(location);
 }
 
@@ -927,6 +944,7 @@ _outWindowFunc(StringInfo str, WindowFunc *node)
    WRITE_UINT_FIELD(winref);
    WRITE_BOOL_FIELD(winstar);
    WRITE_BOOL_FIELD(winagg);
+   WRITE_OID_FIELD(collid);
    WRITE_LOCATION_FIELD(location);
 }
 
@@ -938,6 +956,7 @@ _outArrayRef(StringInfo str, ArrayRef *node)
    WRITE_OID_FIELD(refarraytype);
    WRITE_OID_FIELD(refelemtype);
    WRITE_INT_FIELD(reftypmod);
+   WRITE_INT_FIELD(refcollid);
    WRITE_NODE_FIELD(refupperindexpr);
    WRITE_NODE_FIELD(reflowerindexpr);
    WRITE_NODE_FIELD(refexpr);
@@ -954,6 +973,7 @@ _outFuncExpr(StringInfo str, FuncExpr *node)
    WRITE_BOOL_FIELD(funcretset);
    WRITE_ENUM_FIELD(funcformat, CoercionForm);
    WRITE_NODE_FIELD(args);
+   WRITE_OID_FIELD(collid);
    WRITE_LOCATION_FIELD(location);
 }
 
@@ -978,6 +998,7 @@ _outOpExpr(StringInfo str, OpExpr *node)
    WRITE_OID_FIELD(opresulttype);
    WRITE_BOOL_FIELD(opretset);
    WRITE_NODE_FIELD(args);
+   WRITE_OID_FIELD(collid);
    WRITE_LOCATION_FIELD(location);
 }
 
@@ -991,6 +1012,7 @@ _outDistinctExpr(StringInfo str, DistinctExpr *node)
    WRITE_OID_FIELD(opresulttype);
    WRITE_BOOL_FIELD(opretset);
    WRITE_NODE_FIELD(args);
+   WRITE_OID_FIELD(collid);
    WRITE_LOCATION_FIELD(location);
 }
 
@@ -1003,6 +1025,7 @@ _outScalarArrayOpExpr(StringInfo str, ScalarArrayOpExpr *node)
    WRITE_OID_FIELD(opfuncid);
    WRITE_BOOL_FIELD(useOr);
    WRITE_NODE_FIELD(args);
+   WRITE_OID_FIELD(collid);
    WRITE_LOCATION_FIELD(location);
 }
 
@@ -1057,6 +1080,7 @@ _outSubPlan(StringInfo str, SubPlan *node)
    WRITE_STRING_FIELD(plan_name);
    WRITE_OID_FIELD(firstColType);
    WRITE_INT_FIELD(firstColTypmod);
+   WRITE_OID_FIELD(firstColCollation);
    WRITE_BOOL_FIELD(useHashTable);
    WRITE_BOOL_FIELD(unknownEqFalse);
    WRITE_NODE_FIELD(setParam);
@@ -1083,6 +1107,7 @@ _outFieldSelect(StringInfo str, FieldSelect *node)
    WRITE_INT_FIELD(fieldnum);
    WRITE_OID_FIELD(resulttype);
    WRITE_INT_FIELD(resulttypmod);
+   WRITE_OID_FIELD(resultcollation);
 }
 
 static void
@@ -1150,6 +1175,7 @@ _outCaseExpr(StringInfo str, CaseExpr *node)
    WRITE_NODE_TYPE("CASE");
 
    WRITE_OID_FIELD(casetype);
+   WRITE_OID_FIELD(casecollation);
    WRITE_NODE_FIELD(arg);
    WRITE_NODE_FIELD(args);
    WRITE_NODE_FIELD(defresult);
@@ -1173,6 +1199,7 @@ _outCaseTestExpr(StringInfo str, CaseTestExpr *node)
 
    WRITE_OID_FIELD(typeId);
    WRITE_INT_FIELD(typeMod);
+   WRITE_OID_FIELD(collation);
 }
 
 static void
@@ -1207,6 +1234,7 @@ _outRowCompareExpr(StringInfo str, RowCompareExpr *node)
    WRITE_ENUM_FIELD(rctype, RowCompareType);
    WRITE_NODE_FIELD(opnos);
    WRITE_NODE_FIELD(opfamilies);
+   WRITE_NODE_FIELD(collids);
    WRITE_NODE_FIELD(largs);
    WRITE_NODE_FIELD(rargs);
 }
@@ -1217,6 +1245,7 @@ _outCoalesceExpr(StringInfo str, CoalesceExpr *node)
    WRITE_NODE_TYPE("COALESCE");
 
    WRITE_OID_FIELD(coalescetype);
+   WRITE_OID_FIELD(coalescecollation);
    WRITE_NODE_FIELD(args);
    WRITE_LOCATION_FIELD(location);
 }
@@ -1229,6 +1258,7 @@ _outMinMaxExpr(StringInfo str, MinMaxExpr *node)
    WRITE_OID_FIELD(minmaxtype);
    WRITE_ENUM_FIELD(op, MinMaxOp);
    WRITE_NODE_FIELD(args);
+   WRITE_OID_FIELD(collid);
    WRITE_LOCATION_FIELD(location);
 }
 
@@ -1309,6 +1339,7 @@ _outSetToDefault(StringInfo str, SetToDefault *node)
 
    WRITE_OID_FIELD(typeId);
    WRITE_INT_FIELD(typeMod);
+   WRITE_OID_FIELD(collid);
    WRITE_LOCATION_FIELD(location);
 }
 
@@ -1716,6 +1747,7 @@ _outPathKey(StringInfo str, PathKey *node)
 
    WRITE_NODE_FIELD(pk_eclass);
    WRITE_OID_FIELD(pk_opfamily);
+   WRITE_OID_FIELD(pk_collation);
    WRITE_INT_FIELD(pk_strategy);
    WRITE_BOOL_FIELD(pk_nulls_first);
 }
@@ -2014,6 +2046,8 @@ _outTypeName(StringInfo str, TypeName *node)
    WRITE_NODE_FIELD(typmods);
    WRITE_INT_FIELD(typemod);
    WRITE_NODE_FIELD(arrayBounds);
+   WRITE_NODE_FIELD(collnames);
+   WRITE_OID_FIELD(collOid);
    WRITE_LOCATION_FIELD(location);
 }
 
@@ -2027,6 +2061,17 @@ _outTypeCast(StringInfo str, TypeCast *node)
    WRITE_LOCATION_FIELD(location);
 }
 
+static void
+_outCollateClause(StringInfo str, CollateClause *node)
+{
+   WRITE_NODE_TYPE("COLLATE");
+
+   WRITE_NODE_FIELD(arg);
+   WRITE_NODE_FIELD(collnames);
+   WRITE_OID_FIELD(collOid);
+   WRITE_LOCATION_FIELD(location);
+}
+
 static void
 _outIndexElem(StringInfo str, IndexElem *node)
 {
@@ -2035,6 +2080,7 @@ _outIndexElem(StringInfo str, IndexElem *node)
    WRITE_STRING_FIELD(name);
    WRITE_NODE_FIELD(expr);
    WRITE_STRING_FIELD(indexcolname);
+   WRITE_NODE_FIELD(collation);
    WRITE_NODE_FIELD(opclass);
    WRITE_ENUM_FIELD(ordering, SortByDir);
    WRITE_ENUM_FIELD(nulls_ordering, SortByNulls);
@@ -2162,6 +2208,7 @@ _outCommonTableExpr(StringInfo str, CommonTableExpr *node)
    WRITE_NODE_FIELD(ctecolnames);
    WRITE_NODE_FIELD(ctecoltypes);
    WRITE_NODE_FIELD(ctecoltypmods);
+   WRITE_NODE_FIELD(ctecolcollations);
 }
 
 static void
@@ -2175,6 +2222,7 @@ _outSetOperationStmt(StringInfo str, SetOperationStmt *node)
    WRITE_NODE_FIELD(rarg);
    WRITE_NODE_FIELD(colTypes);
    WRITE_NODE_FIELD(colTypmods);
+   WRITE_NODE_FIELD(colCollations);
    WRITE_NODE_FIELD(groupClauses);
 }
 
@@ -2205,6 +2253,7 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
            WRITE_NODE_FIELD(funcexpr);
            WRITE_NODE_FIELD(funccoltypes);
            WRITE_NODE_FIELD(funccoltypmods);
+           WRITE_NODE_FIELD(funccolcollations);
            break;
        case RTE_VALUES:
            WRITE_NODE_FIELD(values_lists);
@@ -2215,6 +2264,7 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
            WRITE_BOOL_FIELD(self_reference);
            WRITE_NODE_FIELD(ctecoltypes);
            WRITE_NODE_FIELD(ctecoltypmods);
+           WRITE_NODE_FIELD(ctecolcollations);
            break;
        default:
            elog(ERROR, "unrecognized RTE kind: %d", (int) node->rtekind);
@@ -2732,6 +2782,9 @@ _outNode(StringInfo str, void *obj)
            case T_RelabelType:
                _outRelabelType(str, obj);
                break;
+           case T_CollateClause:
+               _outCollateClause(str, obj);
+               break;
            case T_CoerceViaIO:
                _outCoerceViaIO(str, obj);
                break;
index 99d0576e5ecae2293d55582849a8d192c6ddf8cb..b007caeee3e1ecb4238632e53c7de6c2b3328c8e 100644 (file)
@@ -323,6 +323,7 @@ _readCommonTableExpr(void)
    READ_NODE_FIELD(ctecolnames);
    READ_NODE_FIELD(ctecoltypes);
    READ_NODE_FIELD(ctecoltypmods);
+   READ_NODE_FIELD(ctecolcollations);
 
    READ_DONE();
 }
@@ -341,6 +342,7 @@ _readSetOperationStmt(void)
    READ_NODE_FIELD(rarg);
    READ_NODE_FIELD(colTypes);
    READ_NODE_FIELD(colTypmods);
+   READ_NODE_FIELD(colCollations);
    READ_NODE_FIELD(groupClauses);
 
    READ_DONE();
@@ -406,6 +408,7 @@ _readVar(void)
    READ_INT_FIELD(varattno);
    READ_OID_FIELD(vartype);
    READ_INT_FIELD(vartypmod);
+   READ_OID_FIELD(varcollid);
    READ_UINT_FIELD(varlevelsup);
    READ_UINT_FIELD(varnoold);
    READ_INT_FIELD(varoattno);
@@ -424,6 +427,7 @@ _readConst(void)
 
    READ_OID_FIELD(consttype);
    READ_INT_FIELD(consttypmod);
+   READ_OID_FIELD(constcollid);
    READ_INT_FIELD(constlen);
    READ_BOOL_FIELD(constbyval);
    READ_BOOL_FIELD(constisnull);
@@ -450,6 +454,7 @@ _readParam(void)
    READ_INT_FIELD(paramid);
    READ_OID_FIELD(paramtype);
    READ_INT_FIELD(paramtypmod);
+   READ_OID_FIELD(paramcollation);
    READ_LOCATION_FIELD(location);
 
    READ_DONE();
@@ -470,6 +475,7 @@ _readAggref(void)
    READ_NODE_FIELD(aggdistinct);
    READ_BOOL_FIELD(aggstar);
    READ_UINT_FIELD(agglevelsup);
+   READ_OID_FIELD(collid);
    READ_LOCATION_FIELD(location);
 
    READ_DONE();
@@ -489,6 +495,7 @@ _readWindowFunc(void)
    READ_UINT_FIELD(winref);
    READ_BOOL_FIELD(winstar);
    READ_BOOL_FIELD(winagg);
+   READ_OID_FIELD(collid);
    READ_LOCATION_FIELD(location);
 
    READ_DONE();
@@ -505,6 +512,7 @@ _readArrayRef(void)
    READ_OID_FIELD(refarraytype);
    READ_OID_FIELD(refelemtype);
    READ_INT_FIELD(reftypmod);
+   READ_INT_FIELD(refcollid);
    READ_NODE_FIELD(refupperindexpr);
    READ_NODE_FIELD(reflowerindexpr);
    READ_NODE_FIELD(refexpr);
@@ -526,6 +534,7 @@ _readFuncExpr(void)
    READ_BOOL_FIELD(funcretset);
    READ_ENUM_FIELD(funcformat, CoercionForm);
    READ_NODE_FIELD(args);
+   READ_OID_FIELD(collid);
    READ_LOCATION_FIELD(location);
 
    READ_DONE();
@@ -571,6 +580,7 @@ _readOpExpr(void)
    READ_OID_FIELD(opresulttype);
    READ_BOOL_FIELD(opretset);
    READ_NODE_FIELD(args);
+   READ_OID_FIELD(collid);
    READ_LOCATION_FIELD(location);
 
    READ_DONE();
@@ -600,6 +610,7 @@ _readDistinctExpr(void)
    READ_OID_FIELD(opresulttype);
    READ_BOOL_FIELD(opretset);
    READ_NODE_FIELD(args);
+   READ_OID_FIELD(collid);
    READ_LOCATION_FIELD(location);
 
    READ_DONE();
@@ -628,6 +639,7 @@ _readScalarArrayOpExpr(void)
 
    READ_BOOL_FIELD(useOr);
    READ_NODE_FIELD(args);
+   READ_OID_FIELD(collid);
    READ_LOCATION_FIELD(location);
 
    READ_DONE();
@@ -692,6 +704,7 @@ _readFieldSelect(void)
    READ_INT_FIELD(fieldnum);
    READ_OID_FIELD(resulttype);
    READ_INT_FIELD(resulttypmod);
+   READ_OID_FIELD(resultcollation);
 
    READ_DONE();
 }
@@ -729,6 +742,22 @@ _readRelabelType(void)
    READ_DONE();
 }
 
+/*
+ * _readCollateClause
+ */
+static CollateClause *
+_readCollateClause(void)
+{
+   READ_LOCALS(CollateClause);
+
+   READ_NODE_FIELD(arg);
+   READ_NODE_FIELD(collnames);
+   READ_OID_FIELD(collOid);
+   READ_LOCATION_FIELD(location);
+
+   READ_DONE();
+}
+
 /*
  * _readCoerceViaIO
  */
@@ -789,6 +818,7 @@ _readCaseExpr(void)
    READ_LOCALS(CaseExpr);
 
    READ_OID_FIELD(casetype);
+   READ_OID_FIELD(casecollation);
    READ_NODE_FIELD(arg);
    READ_NODE_FIELD(args);
    READ_NODE_FIELD(defresult);
@@ -822,6 +852,7 @@ _readCaseTestExpr(void)
 
    READ_OID_FIELD(typeId);
    READ_INT_FIELD(typeMod);
+   READ_OID_FIELD(collation);
 
    READ_DONE();
 }
@@ -871,6 +902,7 @@ _readRowCompareExpr(void)
    READ_ENUM_FIELD(rctype, RowCompareType);
    READ_NODE_FIELD(opnos);
    READ_NODE_FIELD(opfamilies);
+   READ_NODE_FIELD(collids);
    READ_NODE_FIELD(largs);
    READ_NODE_FIELD(rargs);
 
@@ -886,6 +918,7 @@ _readCoalesceExpr(void)
    READ_LOCALS(CoalesceExpr);
 
    READ_OID_FIELD(coalescetype);
+   READ_OID_FIELD(coalescecollation);
    READ_NODE_FIELD(args);
    READ_LOCATION_FIELD(location);
 
@@ -903,6 +936,7 @@ _readMinMaxExpr(void)
    READ_OID_FIELD(minmaxtype);
    READ_ENUM_FIELD(op, MinMaxOp);
    READ_NODE_FIELD(args);
+   READ_OID_FIELD(collid);
    READ_LOCATION_FIELD(location);
 
    READ_DONE();
@@ -1029,6 +1063,7 @@ _readSetToDefault(void)
 
    READ_OID_FIELD(typeId);
    READ_INT_FIELD(typeMod);
+   READ_OID_FIELD(collid);
    READ_LOCATION_FIELD(location);
 
    READ_DONE();
@@ -1150,6 +1185,7 @@ _readRangeTblEntry(void)
            READ_NODE_FIELD(funcexpr);
            READ_NODE_FIELD(funccoltypes);
            READ_NODE_FIELD(funccoltypmods);
+           READ_NODE_FIELD(funccolcollations);
            break;
        case RTE_VALUES:
            READ_NODE_FIELD(values_lists);
@@ -1160,6 +1196,7 @@ _readRangeTblEntry(void)
            READ_BOOL_FIELD(self_reference);
            READ_NODE_FIELD(ctecoltypes);
            READ_NODE_FIELD(ctecoltypmods);
+           READ_NODE_FIELD(ctecolcollations);
            break;
        default:
            elog(ERROR, "unrecognized RTE kind: %d",
@@ -1248,6 +1285,8 @@ parseNodeString(void)
        return_value = _readFieldStore();
    else if (MATCH("RELABELTYPE", 11))
        return_value = _readRelabelType();
+   else if (MATCH("COLLATE", 7))
+       return_value = _readCollateClause();
    else if (MATCH("COERCEVIAIO", 11))
        return_value = _readCoerceViaIO();
    else if (MATCH("ARRAYCOERCEEXPR", 15))
index 6c98c49bff858d47c1ee5a7b174693916e0504d3..ffb066283f2176251fd3b9a5acb34395f5a422be 100644 (file)
@@ -1795,6 +1795,7 @@ cost_mergejoin(MergePath *path, PlannerInfo *root, SpecialJoinInfo *sjinfo)
        ipathkey = (PathKey *) linitial(ipathkeys);
        /* debugging check */
        if (opathkey->pk_opfamily != ipathkey->pk_opfamily ||
+           opathkey->pk_collation != ipathkey->pk_collation ||
            opathkey->pk_strategy != ipathkey->pk_strategy ||
            opathkey->pk_nulls_first != ipathkey->pk_nulls_first)
            elog(ERROR, "left and right pathkeys do not match in mergejoin");
@@ -2045,6 +2046,7 @@ cached_scansel(PlannerInfo *root, RestrictInfo *rinfo, PathKey *pathkey)
    {
        cache = (MergeScanSelCache *) lfirst(lc);
        if (cache->opfamily == pathkey->pk_opfamily &&
+           cache->collation == pathkey->pk_collation &&
            cache->strategy == pathkey->pk_strategy &&
            cache->nulls_first == pathkey->pk_nulls_first)
            return cache;
@@ -2054,6 +2056,7 @@ cached_scansel(PlannerInfo *root, RestrictInfo *rinfo, PathKey *pathkey)
    mergejoinscansel(root,
                     (Node *) rinfo->clause,
                     pathkey->pk_opfamily,
+                    pathkey->pk_collation,
                     pathkey->pk_strategy,
                     pathkey->pk_nulls_first,
                     &leftstartsel,
@@ -2066,6 +2069,7 @@ cached_scansel(PlannerInfo *root, RestrictInfo *rinfo, PathKey *pathkey)
 
    cache = (MergeScanSelCache *) palloc(sizeof(MergeScanSelCache));
    cache->opfamily = pathkey->pk_opfamily;
+   cache->collation = pathkey->pk_collation;
    cache->strategy = pathkey->pk_strategy;
    cache->nulls_first = pathkey->pk_nulls_first;
    cache->leftstartsel = leftstartsel;
index a3101d7ea73bf5c487b51c2a6dd5311068c258a4..65bc9be8da805a20670769ece58e111a6ce13055 100644 (file)
@@ -23,6 +23,7 @@
 #include "catalog/pg_opfamily.h"
 #include "catalog/pg_type.h"
 #include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
 #include "optimizer/clauses.h"
 #include "optimizer/cost.h"
 #include "optimizer/pathnode.h"
@@ -99,15 +100,15 @@ static List *find_clauses_for_join(PlannerInfo *root, RelOptInfo *rel,
                      Relids outer_relids, bool isouterjoin);
 static bool match_boolean_index_clause(Node *clause, int indexcol,
                           IndexOptInfo *index);
-static bool match_special_index_operator(Expr *clause, Oid opfamily,
+static bool match_special_index_operator(Expr *clause, Oid idxcolcollation, Oid opfamily,
                             bool indexkey_on_left);
 static Expr *expand_boolean_index_clause(Node *clause, int indexcol,
                            IndexOptInfo *index);
-static List *expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily);
+static List *expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid collation);
 static RestrictInfo *expand_indexqual_rowcompare(RestrictInfo *rinfo,
                            IndexOptInfo *index,
                            int indexcol);
-static List *prefix_quals(Node *leftop, Oid opfamily,
+static List *prefix_quals(Node *leftop, Oid opfamily, Oid collation,
             Const *prefix, Pattern_Prefix_Status pstatus);
 static List *network_prefix_quals(Node *leftop, Oid expr_op, Oid opfamily,
                     Datum rightop);
@@ -1142,7 +1143,9 @@ group_clauses_by_indexkey(IndexOptInfo *index,
  *        and
  *   (2)  must contain an operator which is in the same family as the index
  *        operator for this column, or is a "special" operator as recognized
- *        by match_special_index_operator().
+ *        by match_special_index_operator();
+ *         and
+ *    (3)  must match the collation of the index.
  *
  *   Our definition of "const" is pretty liberal: we allow Vars belonging
  *   to the caller-specified outer_relids relations (which had better not
@@ -1198,6 +1201,7 @@ match_clause_to_indexcol(IndexOptInfo *index,
                         SaOpControl saop_control)
 {
    Expr       *clause = rinfo->clause;
+   Oid         collation = index->indexcollations[indexcol];
    Oid         opfamily = index->opfamily[indexcol];
    Node       *leftop,
               *rightop;
@@ -1280,7 +1284,8 @@ match_clause_to_indexcol(IndexOptInfo *index,
        bms_is_subset(right_relids, outer_relids) &&
        !contain_volatile_functions(rightop))
    {
-       if (is_indexable_operator(expr_op, opfamily, true))
+       if (is_indexable_operator(expr_op, opfamily, true) &&
+           (!collation || collation == exprCollation((Node *) clause)))
            return true;
 
        /*
@@ -1288,7 +1293,7 @@ match_clause_to_indexcol(IndexOptInfo *index,
         * is a "special" indexable operator.
         */
        if (plain_op &&
-           match_special_index_operator(clause, opfamily, true))
+           match_special_index_operator(clause, collation, opfamily, true))
            return true;
        return false;
    }
@@ -1298,14 +1303,15 @@ match_clause_to_indexcol(IndexOptInfo *index,
        bms_is_subset(left_relids, outer_relids) &&
        !contain_volatile_functions(leftop))
    {
-       if (is_indexable_operator(expr_op, opfamily, false))
+       if (is_indexable_operator(expr_op, opfamily, false) &&
+           (!collation || collation == exprCollation((Node *) clause)))
            return true;
 
        /*
         * If we didn't find a member of the index's opfamily, see whether it
         * is a "special" indexable operator.
         */
-       if (match_special_index_operator(clause, opfamily, false))
+       if (match_special_index_operator(clause, collation, opfamily, false))
            return true;
        return false;
    }
@@ -1391,6 +1397,9 @@ match_rowcompare_to_indexcol(IndexOptInfo *index,
    else
        return false;
 
+   if (index->indexcollations[indexcol] != linitial_oid(clause->collids))
+       return false;
+
    /* We're good if the operator is the right type of opfamily member */
    switch (get_op_opfamily_strategy(expr_op, opfamily))
    {
@@ -2380,7 +2389,7 @@ match_boolean_index_clause(Node *clause,
  * Return 'true' if we can do something with it anyway.
  */
 static bool
-match_special_index_operator(Expr *clause, Oid opfamily,
+match_special_index_operator(Expr *clause, Oid idxcolcollation, Oid opfamily,
                             bool indexkey_on_left)
 {
    bool        isIndexable = false;
@@ -2495,7 +2504,7 @@ match_special_index_operator(Expr *clause, Oid opfamily,
            isIndexable =
                (opfamily == TEXT_PATTERN_BTREE_FAM_OID) ||
                (opfamily == TEXT_BTREE_FAM_OID &&
-                (pstatus == Pattern_Prefix_Exact || lc_collate_is_c()));
+                (pstatus == Pattern_Prefix_Exact || lc_collate_is_c(idxcolcollation)));
            break;
 
        case OID_BPCHAR_LIKE_OP:
@@ -2505,7 +2514,7 @@ match_special_index_operator(Expr *clause, Oid opfamily,
            isIndexable =
                (opfamily == BPCHAR_PATTERN_BTREE_FAM_OID) ||
                (opfamily == BPCHAR_BTREE_FAM_OID &&
-                (pstatus == Pattern_Prefix_Exact || lc_collate_is_c()));
+                (pstatus == Pattern_Prefix_Exact || lc_collate_is_c(idxcolcollation)));
            break;
 
        case OID_NAME_LIKE_OP:
@@ -2526,6 +2535,25 @@ match_special_index_operator(Expr *clause, Oid opfamily,
            break;
    }
 
+   if (!isIndexable)
+       return false;
+
+   /*
+    * For case-insensitive matching, we also need to check that the
+    * collations match.
+    */
+   switch (expr_op)
+   {
+       case OID_TEXT_ICLIKE_OP:
+       case OID_TEXT_ICREGEXEQ_OP:
+       case OID_BPCHAR_ICLIKE_OP:
+       case OID_BPCHAR_ICREGEXEQ_OP:
+       case OID_NAME_ICLIKE_OP:
+       case OID_NAME_ICREGEXEQ_OP:
+           isIndexable = (idxcolcollation == exprCollation((Node *) clause));
+           break;
+   }
+
    return isIndexable;
 }
 
@@ -2561,6 +2589,7 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
    {
        List       *clausegroup = (List *) lfirst(lc);
        Oid         curFamily = index->opfamily[indexcol];
+       Oid         curCollation = index->indexcollations[indexcol];
        ListCell   *lc2;
 
        foreach(lc2, clausegroup)
@@ -2592,7 +2621,8 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
            {
                resultquals = list_concat(resultquals,
                                          expand_indexqual_opclause(rinfo,
-                                                                curFamily));
+                                                                   curFamily,
+                                                                   curCollation));
            }
            else if (IsA(clause, ScalarArrayOpExpr))
            {
@@ -2693,7 +2723,7 @@ expand_boolean_index_clause(Node *clause,
  * expand special cases that were accepted by match_special_index_operator().
  */
 static List *
-expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily)
+expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid collation)
 {
    Expr       *clause = rinfo->clause;
 
@@ -2724,7 +2754,7 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily)
            {
                pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like,
                                               &prefix, &rest);
-               return prefix_quals(leftop, opfamily, prefix, pstatus);
+               return prefix_quals(leftop, opfamily, collation, prefix, pstatus);
            }
            break;
 
@@ -2736,7 +2766,7 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily)
                /* the right-hand const is type text for all of these */
                pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like_IC,
                                               &prefix, &rest);
-               return prefix_quals(leftop, opfamily, prefix, pstatus);
+               return prefix_quals(leftop, opfamily, collation, prefix, pstatus);
            }
            break;
 
@@ -2748,7 +2778,7 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily)
                /* the right-hand const is type text for all of these */
                pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex,
                                               &prefix, &rest);
-               return prefix_quals(leftop, opfamily, prefix, pstatus);
+               return prefix_quals(leftop, opfamily, collation, prefix, pstatus);
            }
            break;
 
@@ -2760,7 +2790,7 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily)
                /* the right-hand const is type text for all of these */
                pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex_IC,
                                               &prefix, &rest);
-               return prefix_quals(leftop, opfamily, prefix, pstatus);
+               return prefix_quals(leftop, opfamily, collation, prefix, pstatus);
            }
            break;
 
@@ -2814,6 +2844,7 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
    ListCell   *largs_cell;
    ListCell   *rargs_cell;
    ListCell   *opnos_cell;
+   ListCell   *collids_cell;
 
    /* We have to figure out (again) how the first col matches */
    var_on_left = match_index_to_operand((Node *) linitial(clause->largs),
@@ -2845,6 +2876,7 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
    largs_cell = lnext(list_head(clause->largs));
    rargs_cell = lnext(list_head(clause->rargs));
    opnos_cell = lnext(list_head(clause->opnos));
+   collids_cell = lnext(list_head(clause->collids));
 
    while (largs_cell != NULL)
    {
@@ -2891,6 +2923,10 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
            != op_strategy)
            break;
 
+       /* Does collation match? */
+       if (lfirst_oid(collids_cell) != index->indexcollations[i])
+           break;
+
        /* Add opfamily and datatypes to lists */
        get_op_opfamily_properties(expr_op, index->opfamily[i], false,
                                   &op_strategy,
@@ -2974,6 +3010,8 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
        rc->opnos = new_ops;
        rc->opfamilies = list_truncate(list_copy(clause->opfamilies),
                                       matching_cols);
+       rc->collids = list_truncate(list_copy(clause->collids),
+                                   matching_cols);
        rc->largs = list_truncate((List *) copyObject(clause->largs),
                                  matching_cols);
        rc->rargs = list_truncate((List *) copyObject(clause->rargs),
@@ -2998,7 +3036,7 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
  * operators and operand datatypes.
  */
 static List *
-prefix_quals(Node *leftop, Oid opfamily,
+prefix_quals(Node *leftop, Oid opfamily, Oid collation,
             Const *prefix_const, Pattern_Prefix_Status pstatus)
 {
    List       *result;
@@ -3100,6 +3138,7 @@ prefix_quals(Node *leftop, Oid opfamily,
    if (oproid == InvalidOid)
        elog(ERROR, "no < operator for opfamily %u", opfamily);
    fmgr_info(get_opcode(oproid), <proc);
+   fmgr_info_collation(collation, <proc);
    greaterstr = make_greater_string(prefix_const, <proc);
    if (greaterstr)
    {
index d5536fc2b360843f9cef95bf9a0eb7e8061c040d..fd759281ed52649b7dd205ab2ef95fd132690802 100644 (file)
@@ -18,6 +18,7 @@
 #include "postgres.h"
 
 #include "access/skey.h"
+#include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
 #include "utils/lsyscache.h"
 
 
-static PathKey *makePathKey(EquivalenceClass *eclass, Oid opfamily,
+static PathKey *makePathKey(EquivalenceClass *eclass, Oid opfamily, Oid collation,
            int strategy, bool nulls_first);
 static PathKey *make_canonical_pathkey(PlannerInfo *root,
-                      EquivalenceClass *eclass, Oid opfamily,
+                      EquivalenceClass *eclass, Oid opfamily, Oid collation,
                       int strategy, bool nulls_first);
 static bool pathkey_is_redundant(PathKey *new_pathkey, List *pathkeys);
 static Var *find_indexkey_var(PlannerInfo *root, RelOptInfo *rel,
@@ -53,13 +54,14 @@ static bool right_merge_direction(PlannerInfo *root, PathKey *pathkey);
  * convenience routine to build the specified node.
  */
 static PathKey *
-makePathKey(EquivalenceClass *eclass, Oid opfamily,
+makePathKey(EquivalenceClass *eclass, Oid opfamily, Oid collation,
            int strategy, bool nulls_first)
 {
    PathKey    *pk = makeNode(PathKey);
 
    pk->pk_eclass = eclass;
    pk->pk_opfamily = opfamily;
+   pk->pk_collation = collation;
    pk->pk_strategy = strategy;
    pk->pk_nulls_first = nulls_first;
 
@@ -77,7 +79,7 @@ makePathKey(EquivalenceClass *eclass, Oid opfamily,
  */
 static PathKey *
 make_canonical_pathkey(PlannerInfo *root,
-                      EquivalenceClass *eclass, Oid opfamily,
+                      EquivalenceClass *eclass, Oid opfamily, Oid collation,
                       int strategy, bool nulls_first)
 {
    PathKey    *pk;
@@ -93,6 +95,7 @@ make_canonical_pathkey(PlannerInfo *root,
        pk = (PathKey *) lfirst(lc);
        if (eclass == pk->pk_eclass &&
            opfamily == pk->pk_opfamily &&
+           collation == pk->pk_collation &&
            strategy == pk->pk_strategy &&
            nulls_first == pk->pk_nulls_first)
            return pk;
@@ -104,7 +107,7 @@ make_canonical_pathkey(PlannerInfo *root,
     */
    oldcontext = MemoryContextSwitchTo(root->planner_cxt);
 
-   pk = makePathKey(eclass, opfamily, strategy, nulls_first);
+   pk = makePathKey(eclass, opfamily, collation, strategy, nulls_first);
    root->canon_pathkeys = lappend(root->canon_pathkeys, pk);
 
    MemoryContextSwitchTo(oldcontext);
@@ -206,6 +209,7 @@ canonicalize_pathkeys(PlannerInfo *root, List *pathkeys)
        cpathkey = make_canonical_pathkey(root,
                                          eclass,
                                          pathkey->pk_opfamily,
+                                         pathkey->pk_collation,
                                          pathkey->pk_strategy,
                                          pathkey->pk_nulls_first);
 
@@ -247,6 +251,7 @@ make_pathkey_from_sortinfo(PlannerInfo *root,
    Oid         equality_op;
    List       *opfamilies;
    EquivalenceClass *eclass;
+   Oid         collation;
 
    strategy = reverse_sort ? BTGreaterStrategyNumber : BTLessStrategyNumber;
 
@@ -301,12 +306,14 @@ make_pathkey_from_sortinfo(PlannerInfo *root,
    if (!eclass)
        return NULL;
 
+   collation = exprCollation((Node *) expr);
+
    /* And finally we can find or create a PathKey node */
    if (canonicalize)
-       return make_canonical_pathkey(root, eclass, opfamily,
+       return make_canonical_pathkey(root, eclass, opfamily, collation,
                                      strategy, nulls_first);
    else
-       return makePathKey(eclass, opfamily, strategy, nulls_first);
+       return makePathKey(eclass, opfamily, collation, strategy, nulls_first);
 }
 
 /*
@@ -605,7 +612,8 @@ find_indexkey_var(PlannerInfo *root, RelOptInfo *rel, AttrNumber varattno)
    ListCell   *temp;
    Index       relid;
    Oid         reloid,
-               vartypeid;
+               vartypeid,
+               varcollid;
    int32       type_mod;
 
    foreach(temp, rel->reltargetlist)
@@ -620,8 +628,9 @@ find_indexkey_var(PlannerInfo *root, RelOptInfo *rel, AttrNumber varattno)
    relid = rel->relid;
    reloid = getrelid(relid, root->parse->rtable);
    get_atttypetypmod(reloid, varattno, &vartypeid, &type_mod);
+   varcollid = get_attcollation(reloid, varattno);
 
-   return makeVar(relid, varattno, vartypeid, type_mod, 0);
+   return makeVar(relid, varattno, vartypeid, type_mod, varcollid, 0);
 }
 
 /*
@@ -703,6 +712,7 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
                        make_canonical_pathkey(root,
                                               outer_ec,
                                               sub_pathkey->pk_opfamily,
+                                              sub_pathkey->pk_collation,
                                               sub_pathkey->pk_strategy,
                                               sub_pathkey->pk_nulls_first);
            }
@@ -805,6 +815,7 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
                    outer_pk = make_canonical_pathkey(root,
                                                      outer_ec,
                                                    sub_pathkey->pk_opfamily,
+                                                   sub_pathkey->pk_collation,
                                                    sub_pathkey->pk_strategy,
                                                sub_pathkey->pk_nulls_first);
                    /* score = # of equivalence peers */
@@ -1326,6 +1337,7 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
        pathkey = make_canonical_pathkey(root,
                                         ec,
                                         linitial_oid(ec->ec_opfamilies),
+                                        DEFAULT_COLLATION_OID,
                                         BTLessStrategyNumber,
                                         false);
        /* can't be redundant because no duplicate ECs */
@@ -1419,6 +1431,7 @@ make_inner_pathkeys_for_merge(PlannerInfo *root,
            pathkey = make_canonical_pathkey(root,
                                             ieclass,
                                             opathkey->pk_opfamily,
+                                            opathkey->pk_collation,
                                             opathkey->pk_strategy,
                                             opathkey->pk_nulls_first);
 
@@ -1539,6 +1552,7 @@ right_merge_direction(PlannerInfo *root, PathKey *pathkey)
        PathKey    *query_pathkey = (PathKey *) lfirst(l);
 
        if (pathkey->pk_eclass == query_pathkey->pk_eclass &&
+           pathkey->pk_collation == query_pathkey->pk_collation &&
            pathkey->pk_opfamily == query_pathkey->pk_opfamily)
        {
            /*
index c74125f1f75c19bf2d6899666c2404db2f7d1361..f01114c673a3104a3cc65afb9842c704bdb45d9e 100644 (file)
@@ -105,7 +105,7 @@ static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
             List *tidquals);
 static FunctionScan *make_functionscan(List *qptlist, List *qpqual,
                  Index scanrelid, Node *funcexpr, List *funccolnames,
-                 List *funccoltypes, List *funccoltypmods);
+                 List *funccoltypes, List *funccoltypmods, List *funccolcollations);
 static ValuesScan *make_valuesscan(List *qptlist, List *qpqual,
                Index scanrelid, List *values_lists);
 static CteScan *make_ctescan(List *qptlist, List *qpqual,
@@ -133,12 +133,13 @@ static MergeJoin *make_mergejoin(List *tlist,
               List *joinclauses, List *otherclauses,
               List *mergeclauses,
               Oid *mergefamilies,
+              Oid *mergecollations,
               int *mergestrategies,
               bool *mergenullsfirst,
               Plan *lefttree, Plan *righttree,
               JoinType jointype);
 static Sort *make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
-         AttrNumber *sortColIdx, Oid *sortOperators, bool *nullsFirst,
+         AttrNumber *sortColIdx, Oid *sortOperators, Oid *collations, bool *nullsFirst,
          double limit_tuples);
 static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
                                        Plan *lefttree, List *pathkeys,
@@ -146,6 +147,7 @@ static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
                                        int *p_numsortkeys,
                                        AttrNumber **p_sortColIdx,
                                        Oid **p_sortOperators,
+                                       Oid **p_collations,
                                        bool **p_nullsFirst);
 static Material *make_material(Plan *lefttree);
 
@@ -671,6 +673,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
                                      &node->numCols,
                                      &node->sortColIdx,
                                      &node->sortOperators,
+                                     &node->collations,
                                      &node->nullsFirst);
 
    /*
@@ -685,6 +688,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
        int         numsortkeys;
        AttrNumber *sortColIdx;
        Oid        *sortOperators;
+       Oid        *collations;
        bool       *nullsFirst;
 
        /* Build the child plan */
@@ -696,6 +700,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
                                             &numsortkeys,
                                             &sortColIdx,
                                             &sortOperators,
+                                            &collations,
                                             &nullsFirst);
 
        /*
@@ -710,13 +715,15 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
            elog(ERROR, "MergeAppend child's targetlist doesn't match MergeAppend");
        Assert(memcmp(sortOperators, node->sortOperators,
                      numsortkeys * sizeof(Oid)) == 0);
+       Assert(memcmp(collations, node->collations,
+                     numsortkeys * sizeof(Oid)) == 0);
        Assert(memcmp(nullsFirst, node->nullsFirst,
                      numsortkeys * sizeof(bool)) == 0);
 
        /* Now, insert a Sort node if subplan isn't sufficiently ordered */
        if (!pathkeys_contained_in(pathkeys, subpath->pathkeys))
            subplan = (Plan *) make_sort(root, subplan, numsortkeys,
-                                        sortColIdx, sortOperators, nullsFirst,
+                                        sortColIdx, sortOperators, collations, nullsFirst,
                                         best_path->limit_tuples);
 
        subplans = lappend(subplans, subplan);
@@ -1569,7 +1576,8 @@ create_functionscan_plan(PlannerInfo *root, Path *best_path,
                                  rte->funcexpr,
                                  rte->eref->colnames,
                                  rte->funccoltypes,
-                                 rte->funccoltypmods);
+                                 rte->funccoltypmods,
+                                 rte->funccolcollations);
 
    copy_path_costsize(&scan_plan->scan.plan, best_path);
 
@@ -1847,6 +1855,7 @@ create_mergejoin_plan(PlannerInfo *root,
    List       *innerpathkeys;
    int         nClauses;
    Oid        *mergefamilies;
+   Oid        *mergecollations;
    int        *mergestrategies;
    bool       *mergenullsfirst;
    MergeJoin  *join_plan;
@@ -1946,6 +1955,7 @@ create_mergejoin_plan(PlannerInfo *root,
    nClauses = list_length(mergeclauses);
    Assert(nClauses == list_length(best_path->path_mergeclauses));
    mergefamilies = (Oid *) palloc(nClauses * sizeof(Oid));
+   mergecollations = (Oid *) palloc(nClauses * sizeof(Oid));
    mergestrategies = (int *) palloc(nClauses * sizeof(int));
    mergenullsfirst = (bool *) palloc(nClauses * sizeof(bool));
 
@@ -2074,12 +2084,14 @@ create_mergejoin_plan(PlannerInfo *root,
 
        /* pathkeys should match each other too (more debugging) */
        if (opathkey->pk_opfamily != ipathkey->pk_opfamily ||
+           opathkey->pk_collation != ipathkey->pk_collation ||
            opathkey->pk_strategy != ipathkey->pk_strategy ||
            opathkey->pk_nulls_first != ipathkey->pk_nulls_first)
            elog(ERROR, "left and right pathkeys do not match in mergejoin");
 
        /* OK, save info for executor */
        mergefamilies[i] = opathkey->pk_opfamily;
+       mergecollations[i] = opathkey->pk_collation;
        mergestrategies[i] = opathkey->pk_strategy;
        mergenullsfirst[i] = opathkey->pk_nulls_first;
        i++;
@@ -2099,6 +2111,7 @@ create_mergejoin_plan(PlannerInfo *root,
                               otherclauses,
                               mergeclauses,
                               mergefamilies,
+                              mergecollations,
                               mergestrategies,
                               mergenullsfirst,
                               outer_plan,
@@ -2528,6 +2541,7 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index)
                /* Found a match */
                result = makeVar(index->rel->relid, pos + 1,
                                 exprType(lfirst(indexpr_item)), -1,
+                                exprCollation(lfirst(indexpr_item)),
                                 0);
                return (Node *) result;
            }
@@ -2881,7 +2895,8 @@ make_functionscan(List *qptlist,
                  Node *funcexpr,
                  List *funccolnames,
                  List *funccoltypes,
-                 List *funccoltypmods)
+                 List *funccoltypmods,
+                 List *funccolcollations)
 {
    FunctionScan *node = makeNode(FunctionScan);
    Plan       *plan = &node->scan.plan;
@@ -2896,6 +2911,7 @@ make_functionscan(List *qptlist,
    node->funccolnames = funccolnames;
    node->funccoltypes = funccoltypes;
    node->funccoltypmods = funccoltypmods;
+   node->funccolcollations = funccolcollations;
 
    return node;
 }
@@ -3181,6 +3197,7 @@ make_mergejoin(List *tlist,
               List *otherclauses,
               List *mergeclauses,
               Oid *mergefamilies,
+              Oid *mergecollations,
               int *mergestrategies,
               bool *mergenullsfirst,
               Plan *lefttree,
@@ -3197,6 +3214,7 @@ make_mergejoin(List *tlist,
    plan->righttree = righttree;
    node->mergeclauses = mergeclauses;
    node->mergeFamilies = mergefamilies;
+   node->mergeCollations = mergecollations;
    node->mergeStrategies = mergestrategies;
    node->mergeNullsFirst = mergenullsfirst;
    node->join.jointype = jointype;
@@ -3214,7 +3232,7 @@ make_mergejoin(List *tlist,
  */
 static Sort *
 make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
-         AttrNumber *sortColIdx, Oid *sortOperators, bool *nullsFirst,
+         AttrNumber *sortColIdx, Oid *sortOperators, Oid *collations, bool *nullsFirst,
          double limit_tuples)
 {
    Sort       *node = makeNode(Sort);
@@ -3238,6 +3256,7 @@ make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
    node->numCols = numCols;
    node->sortColIdx = sortColIdx;
    node->sortOperators = sortOperators;
+   node->collations = collations;
    node->nullsFirst = nullsFirst;
 
    return node;
@@ -3253,9 +3272,9 @@ make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
  * max possible number of columns. Return value is the new column count.
  */
 static int
-add_sort_column(AttrNumber colIdx, Oid sortOp, bool nulls_first,
+add_sort_column(AttrNumber colIdx, Oid sortOp, Oid coll, bool nulls_first,
                int numCols, AttrNumber *sortColIdx,
-               Oid *sortOperators, bool *nullsFirst)
+               Oid *sortOperators, Oid *collations, bool *nullsFirst)
 {
    int         i;
 
@@ -3271,7 +3290,8 @@ add_sort_column(AttrNumber colIdx, Oid sortOp, bool nulls_first,
         * opposite nulls direction is redundant.
         */
        if (sortColIdx[i] == colIdx &&
-           sortOperators[numCols] == sortOp)
+           sortOperators[numCols] == sortOp &&
+           collations[numCols] == coll)
        {
            /* Already sorting by this col, so extra sort key is useless */
            return numCols;
@@ -3281,6 +3301,7 @@ add_sort_column(AttrNumber colIdx, Oid sortOp, bool nulls_first,
    /* Add the column */
    sortColIdx[numCols] = colIdx;
    sortOperators[numCols] = sortOp;
+   collations[numCols] = coll;
    nullsFirst[numCols] = nulls_first;
    return numCols + 1;
 }
@@ -3320,6 +3341,7 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
                           int *p_numsortkeys,
                           AttrNumber **p_sortColIdx,
                           Oid **p_sortOperators,
+                          Oid **p_collations,
                           bool **p_nullsFirst)
 {
    List       *tlist = lefttree->targetlist;
@@ -3327,6 +3349,7 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
    int         numsortkeys;
    AttrNumber *sortColIdx;
    Oid        *sortOperators;
+   Oid        *collations;
    bool       *nullsFirst;
 
    /*
@@ -3335,6 +3358,7 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
    numsortkeys = list_length(pathkeys);
    sortColIdx = (AttrNumber *) palloc(numsortkeys * sizeof(AttrNumber));
    sortOperators = (Oid *) palloc(numsortkeys * sizeof(Oid));
+   collations = (Oid *) palloc(numsortkeys * sizeof(Oid));
    nullsFirst = (bool *) palloc(numsortkeys * sizeof(bool));
 
    numsortkeys = 0;
@@ -3493,9 +3517,10 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
         */
        numsortkeys = add_sort_column(tle->resno,
                                      sortop,
+                                     pathkey->pk_collation,
                                      pathkey->pk_nulls_first,
                                      numsortkeys,
-                                     sortColIdx, sortOperators, nullsFirst);
+                                     sortColIdx, sortOperators, collations, nullsFirst);
    }
 
    Assert(numsortkeys > 0);
@@ -3504,6 +3529,7 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
    *p_numsortkeys = numsortkeys;
    *p_sortColIdx = sortColIdx;
    *p_sortOperators = sortOperators;
+   *p_collations = collations;
    *p_nullsFirst = nullsFirst;
 
    return lefttree;
@@ -3525,6 +3551,7 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
    int         numsortkeys;
    AttrNumber *sortColIdx;
    Oid        *sortOperators;
+   Oid        *collations;
    bool       *nullsFirst;
 
    /* Compute sort column info, and adjust lefttree as needed */
@@ -3533,11 +3560,12 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
                                          &numsortkeys,
                                          &sortColIdx,
                                          &sortOperators,
+                                         &collations,
                                          &nullsFirst);
 
    /* Now build the Sort node */
    return make_sort(root, lefttree, numsortkeys,
-                    sortColIdx, sortOperators, nullsFirst, limit_tuples);
+                    sortColIdx, sortOperators, collations, nullsFirst, limit_tuples);
 }
 
 /*
@@ -3555,6 +3583,7 @@ make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree)
    int         numsortkeys;
    AttrNumber *sortColIdx;
    Oid        *sortOperators;
+   Oid        *collations;
    bool       *nullsFirst;
 
    /*
@@ -3563,6 +3592,7 @@ make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree)
    numsortkeys = list_length(sortcls);
    sortColIdx = (AttrNumber *) palloc(numsortkeys * sizeof(AttrNumber));
    sortOperators = (Oid *) palloc(numsortkeys * sizeof(Oid));
+   collations = (Oid *) palloc(numsortkeys * sizeof(Oid));
    nullsFirst = (bool *) palloc(numsortkeys * sizeof(bool));
 
    numsortkeys = 0;
@@ -3578,15 +3608,16 @@ make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree)
         * redundantly.
         */
        numsortkeys = add_sort_column(tle->resno, sortcl->sortop,
+                                     exprCollation((Node *) tle->expr),
                                      sortcl->nulls_first,
                                      numsortkeys,
-                                     sortColIdx, sortOperators, nullsFirst);
+                                     sortColIdx, sortOperators, collations, nullsFirst);
    }
 
    Assert(numsortkeys > 0);
 
    return make_sort(root, lefttree, numsortkeys,
-                    sortColIdx, sortOperators, nullsFirst, -1.0);
+                    sortColIdx, sortOperators, collations, nullsFirst, -1.0);
 }
 
 /*
@@ -3614,6 +3645,7 @@ make_sort_from_groupcols(PlannerInfo *root,
    int         numsortkeys;
    AttrNumber *sortColIdx;
    Oid        *sortOperators;
+   Oid        *collations;
    bool       *nullsFirst;
 
    /*
@@ -3622,6 +3654,7 @@ make_sort_from_groupcols(PlannerInfo *root,
    numsortkeys = list_length(groupcls);
    sortColIdx = (AttrNumber *) palloc(numsortkeys * sizeof(AttrNumber));
    sortOperators = (Oid *) palloc(numsortkeys * sizeof(Oid));
+   collations = (Oid *) palloc(numsortkeys * sizeof(Oid));
    nullsFirst = (bool *) palloc(numsortkeys * sizeof(bool));
 
    numsortkeys = 0;
@@ -3637,16 +3670,17 @@ make_sort_from_groupcols(PlannerInfo *root,
         * redundantly.
         */
        numsortkeys = add_sort_column(tle->resno, grpcl->sortop,
+                                     exprCollation((Node *) tle->expr),
                                      grpcl->nulls_first,
                                      numsortkeys,
-                                     sortColIdx, sortOperators, nullsFirst);
+                                     sortColIdx, sortOperators, collations, nullsFirst);
        grpno++;
    }
 
    Assert(numsortkeys > 0);
 
    return make_sort(root, lefttree, numsortkeys,
-                    sortColIdx, sortOperators, nullsFirst, -1.0);
+                    sortColIdx, sortOperators, collations, nullsFirst, -1.0);
 }
 
 static Material *
index dfbc624aa8b356dc31be04e29f90b450b45ed903..f885385296e1941784207480cf0b3a1beadf5610 100644 (file)
@@ -561,7 +561,8 @@ make_agg_subplan(PlannerInfo *root, RelOptInfo *rel, PrivateMMAggInfo *info)
     */
    info->param = SS_make_initplan_from_plan(&subroot, plan,
                                             exprType((Node *) tle->expr),
-                                            -1);
+                                            -1,
+                                            exprCollation((Node *) tle->expr));
 
    /*
     * Put the updated list of InitPlans back into the outer PlannerInfo.
index 02f5cabd25c07f96de633e5fff9a0f896e5b9797..867238ecc8b62b533f226e957a51ba88657416f5 100644 (file)
@@ -213,9 +213,11 @@ set_plan_references(PlannerGlobal *glob, Plan *plan,
        newrte->funcexpr = NULL;
        newrte->funccoltypes = NIL;
        newrte->funccoltypmods = NIL;
+       newrte->funccolcollations = NIL;
        newrte->values_lists = NIL;
        newrte->ctecoltypes = NIL;
        newrte->ctecoltypmods = NIL;
+       newrte->ctecolcollations = NIL;
 
        glob->finalrtable = lappend(glob->finalrtable, newrte);
 
@@ -1119,6 +1121,7 @@ set_dummy_tlist_references(Plan *plan, int rtoffset)
                         tle->resno,
                         exprType((Node *) oldvar),
                         exprTypmod((Node *) oldvar),
+                        exprCollation((Node *) oldvar),
                         0);
        if (IsA(oldvar, Var))
        {
index febec1e15f0d10b8a777885e25667b5e66baef3e..29eb9dced455c2f929cde7498cbe2047e55aef29 100644 (file)
@@ -157,6 +157,7 @@ replace_outer_var(PlannerInfo *root, Var *var)
    retval->paramid = i;
    retval->paramtype = var->vartype;
    retval->paramtypmod = var->vartypmod;
+   retval->paramcollation = var->varcollid;
    retval->location = -1;
 
    return retval;
@@ -185,6 +186,7 @@ assign_nestloop_param(PlannerInfo *root, Var *var)
    retval->paramid = i;
    retval->paramtype = var->vartype;
    retval->paramtypmod = var->vartypmod;
+   retval->paramcollation = var->varcollid;
    retval->location = -1;
 
    return retval;
@@ -225,6 +227,7 @@ replace_outer_agg(PlannerInfo *root, Aggref *agg)
    retval->paramid = i;
    retval->paramtype = agg->aggtype;
    retval->paramtypmod = -1;
+   retval->paramcollation = agg->collid;
    retval->location = -1;
 
    return retval;
@@ -236,7 +239,7 @@ replace_outer_agg(PlannerInfo *root, Aggref *agg)
  * This is used to allocate PARAM_EXEC slots for subplan outputs.
  */
 static Param *
-generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod)
+generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod, Oid paramcollation)
 {
    Param      *retval;
    PlannerParamItem *pitem;
@@ -246,6 +249,7 @@ generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod)
    retval->paramid = list_length(root->glob->paramlist);
    retval->paramtype = paramtype;
    retval->paramtypmod = paramtypmod;
+   retval->paramcollation = paramcollation;
    retval->location = -1;
 
    pitem = makeNode(PlannerParamItem);
@@ -270,7 +274,7 @@ SS_assign_special_param(PlannerInfo *root)
    Param      *param;
 
    /* We generate a Param of datatype INTERNAL */
-   param = generate_new_param(root, INTERNALOID, -1);
+   param = generate_new_param(root, INTERNALOID, -1, InvalidOid);
    /* ... but the caller only cares about its ID */
    return param->paramid;
 }
@@ -278,13 +282,13 @@ SS_assign_special_param(PlannerInfo *root)
 /*
  * Get the datatype of the first column of the plan's output.
  *
- * This is stored for ARRAY_SUBLINK execution and for exprType()/exprTypmod(),
+ * This is stored for ARRAY_SUBLINK execution and for exprType()/exprTypmod()/exprCollation(),
  * which have no way to get at the plan associated with a SubPlan node.
  * We really only need the info for EXPR_SUBLINK and ARRAY_SUBLINK subplans,
  * but for consistency we save it always.
  */
 static void
-get_first_col_type(Plan *plan, Oid *coltype, int32 *coltypmod)
+get_first_col_type(Plan *plan, Oid *coltype, int32 *coltypmod, Oid *colcollation)
 {
    /* In cases such as EXISTS, tlist might be empty; arbitrarily use VOID */
    if (plan->targetlist)
@@ -296,11 +300,13 @@ get_first_col_type(Plan *plan, Oid *coltype, int32 *coltypmod)
        {
            *coltype = exprType((Node *) tent->expr);
            *coltypmod = exprTypmod((Node *) tent->expr);
+           *colcollation = exprCollation((Node *) tent->expr);
            return;
        }
    }
    *coltype = VOIDOID;
    *coltypmod = -1;
+   *colcollation = InvalidOid;
 }
 
 /*
@@ -470,7 +476,7 @@ build_subplan(PlannerInfo *root, Plan *plan, List *rtable, List *rowmarks,
    splan->subLinkType = subLinkType;
    splan->testexpr = NULL;
    splan->paramIds = NIL;
-   get_first_col_type(plan, &splan->firstColType, &splan->firstColTypmod);
+   get_first_col_type(plan, &splan->firstColType, &splan->firstColTypmod, &splan->firstColCollation);
    splan->useHashTable = false;
    splan->unknownEqFalse = unknownEqFalse;
    splan->setParam = NIL;
@@ -523,7 +529,7 @@ build_subplan(PlannerInfo *root, Plan *plan, List *rtable, List *rowmarks,
        Param      *prm;
 
        Assert(testexpr == NULL);
-       prm = generate_new_param(root, BOOLOID, -1);
+       prm = generate_new_param(root, BOOLOID, -1, InvalidOid);
        splan->setParam = list_make1_int(prm->paramid);
        isInitPlan = true;
        result = (Node *) prm;
@@ -537,7 +543,8 @@ build_subplan(PlannerInfo *root, Plan *plan, List *rtable, List *rowmarks,
        Assert(testexpr == NULL);
        prm = generate_new_param(root,
                                 exprType((Node *) te->expr),
-                                exprTypmod((Node *) te->expr));
+                                exprTypmod((Node *) te->expr),
+                                exprCollation((Node *) te->expr));
        splan->setParam = list_make1_int(prm->paramid);
        isInitPlan = true;
        result = (Node *) prm;
@@ -556,7 +563,8 @@ build_subplan(PlannerInfo *root, Plan *plan, List *rtable, List *rowmarks,
                 format_type_be(exprType((Node *) te->expr)));
        prm = generate_new_param(root,
                                 arraytype,
-                                exprTypmod((Node *) te->expr));
+                                exprTypmod((Node *) te->expr),
+                                exprCollation((Node *) te->expr));
        splan->setParam = list_make1_int(prm->paramid);
        isInitPlan = true;
        result = (Node *) prm;
@@ -708,7 +716,8 @@ generate_subquery_params(PlannerInfo *root, List *tlist, List **paramIds)
 
        param = generate_new_param(root,
                                   exprType((Node *) tent->expr),
-                                  exprTypmod((Node *) tent->expr));
+                                  exprTypmod((Node *) tent->expr),
+                                  exprCollation((Node *) tent->expr));
        result = lappend(result, param);
        ids = lappend_int(ids, param->paramid);
    }
@@ -964,7 +973,7 @@ SS_process_ctes(PlannerInfo *root)
        splan->subLinkType = CTE_SUBLINK;
        splan->testexpr = NULL;
        splan->paramIds = NIL;
-       get_first_col_type(plan, &splan->firstColType, &splan->firstColTypmod);
+       get_first_col_type(plan, &splan->firstColType, &splan->firstColTypmod, &splan->firstColCollation);
        splan->useHashTable = false;
        splan->unknownEqFalse = false;
        splan->setParam = NIL;
@@ -999,7 +1008,7 @@ SS_process_ctes(PlannerInfo *root)
         * Assign a param to represent the query output.  We only really care
         * about reserving a parameter ID number.
         */
-       prm = generate_new_param(root, INTERNALOID, -1);
+       prm = generate_new_param(root, INTERNALOID, -1, InvalidOid);
        splan->setParam = list_make1_int(prm->paramid);
 
        /*
@@ -1565,7 +1574,8 @@ convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect,
        oc = lnext(oc);
        param = generate_new_param(root,
                                   exprType(rightarg),
-                                  exprTypmod(rightarg));
+                                  exprTypmod(rightarg),
+                                  exprCollation(rightarg));
        tlist = lappend(tlist,
                        makeTargetEntry((Expr *) rightarg,
                                        resno++,
@@ -2352,7 +2362,7 @@ finalize_primnode(Node *node, finalize_primnode_context *context)
  */
 Param *
 SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan,
-                          Oid resulttype, int32 resulttypmod)
+                          Oid resulttype, int32 resulttypmod, Oid resultcollation)
 {
    SubPlan    *node;
    Param      *prm;
@@ -2388,7 +2398,7 @@ SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan,
     */
    node = makeNode(SubPlan);
    node->subLinkType = EXPR_SUBLINK;
-   get_first_col_type(plan, &node->firstColType, &node->firstColTypmod);
+   get_first_col_type(plan, &node->firstColType, &node->firstColTypmod, &node->firstColCollation);
    node->plan_id = list_length(root->glob->subplans);
 
    root->init_plans = lappend(root->init_plans, node);
@@ -2403,7 +2413,7 @@ SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan,
    /*
     * Make a Param that will be the subplan's output.
     */
-   prm = generate_new_param(root, resulttype, resulttypmod);
+   prm = generate_new_param(root, resulttype, resulttypmod, resultcollation);
    node->setParam = list_make1_int(prm->paramid);
 
    /* Label the subplan for EXPLAIN purposes */
index f92bcd41b1aa9c63624403d5185f09767fdde70b..bd678ac7edea153d191ca546c478384149c0c530 100644 (file)
@@ -445,6 +445,7 @@ inline_set_returning_functions(PlannerInfo *root)
                rte->funcexpr = NULL;
                rte->funccoltypes = NIL;
                rte->funccoltypmods = NIL;
+               rte->funccolcollations = NIL;
            }
        }
    }
index 36c19438c04592132494d2dad631d7ce2e1141c3..34b38eb329843bd8744245a9c4e5a87197463dc2 100644 (file)
@@ -100,6 +100,7 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
                          SelfItemPointerAttributeNumber,
                          TIDOID,
                          -1,
+                         InvalidOid,
                          0);
            snprintf(resname, sizeof(resname), "ctid%u", rc->rti);
            tle = makeTargetEntry((Expr *) var,
@@ -115,6 +116,7 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
                              TableOidAttributeNumber,
                              OIDOID,
                              -1,
+                             InvalidOid,
                              0);
                snprintf(resname, sizeof(resname), "tableoid%u", rc->rti);
                tle = makeTargetEntry((Expr *) var,
@@ -257,6 +259,7 @@ expand_targetlist(List *tlist, int command_type,
             */
            Oid         atttype = att_tup->atttypid;
            int32       atttypmod = att_tup->atttypmod;
+           Oid         attcollation = att_tup->attcollation;
            Node       *new_expr;
 
            switch (command_type)
@@ -296,6 +299,7 @@ expand_targetlist(List *tlist, int command_type,
                                                    attrno,
                                                    atttype,
                                                    atttypmod,
+                                                   attcollation,
                                                    0);
                    }
                    else
index 449c8dab50139b17efcbca4ddfd3f07b85844fe1..f62af6c37daca9756f7d75fb4e5bf206027de416 100644 (file)
@@ -86,7 +86,7 @@ static List *generate_setop_tlist(List *colTypes, int flag,
                     bool hack_constants,
                     List *input_tlist,
                     List *refnames_tlist);
-static List *generate_append_tlist(List *colTypes, bool flag,
+static List *generate_append_tlist(List *colTypes, List *colCollations, bool flag,
                      List *input_plans,
                      List *refnames_tlist);
 static List *generate_setop_grouplist(SetOperationStmt *op, List *targetlist);
@@ -348,7 +348,7 @@ generate_recursion_plan(SetOperationStmt *setOp, PlannerInfo *root,
    /*
     * Generate tlist for RecursiveUnion plan node --- same as in Append cases
     */
-   tlist = generate_append_tlist(setOp->colTypes, false,
+   tlist = generate_append_tlist(setOp->colTypes, setOp->colCollations, false,
                                  list_make2(lplan, rplan),
                                  refnames_tlist);
 
@@ -443,7 +443,7 @@ generate_union_plan(SetOperationStmt *op, PlannerInfo *root,
     * concerned, but we must make it look real anyway for the benefit of the
     * next plan level up.
     */
-   tlist = generate_append_tlist(op->colTypes, false,
+   tlist = generate_append_tlist(op->colTypes, op->colCollations, false,
                                  planlist, refnames_tlist);
 
    /*
@@ -534,7 +534,7 @@ generate_nonunion_plan(SetOperationStmt *op, PlannerInfo *root,
     * column is shown as a variable not a constant, else setrefs.c will get
     * confused.
     */
-   tlist = generate_append_tlist(op->colTypes, true,
+   tlist = generate_append_tlist(op->colTypes, op->colCollations, true,
                                  planlist, refnames_tlist);
 
    /*
@@ -885,6 +885,7 @@ generate_setop_tlist(List *colTypes, int flag,
                                    inputtle->resno,
                                    exprType((Node *) inputtle->expr),
                                    exprTypmod((Node *) inputtle->expr),
+                                   exprCollation((Node *) inputtle->expr),
                                    0);
        if (exprType(expr) != colType)
        {
@@ -936,13 +937,14 @@ generate_setop_tlist(List *colTypes, int flag,
  * The Vars are always generated with varno 0.
  */
 static List *
-generate_append_tlist(List *colTypes, bool flag,
+generate_append_tlist(List *colTypes, List*colCollations, bool flag,
                      List *input_plans,
                      List *refnames_tlist)
 {
    List       *tlist = NIL;
    int         resno = 1;
    ListCell   *curColType;
+   ListCell   *curColCollation;
    ListCell   *ref_tl_item;
    int         colindex;
    TargetEntry *tle;
@@ -997,10 +999,11 @@ generate_append_tlist(List *colTypes, bool flag,
     * Now we can build the tlist for the Append.
     */
    colindex = 0;
-   forboth(curColType, colTypes, ref_tl_item, refnames_tlist)
+   forthree(curColType, colTypes, curColCollation, colCollations, ref_tl_item, refnames_tlist)
    {
        Oid         colType = lfirst_oid(curColType);
        int32       colTypmod = colTypmods[colindex++];
+       Oid         colColl = lfirst_oid(curColCollation);
        TargetEntry *reftle = (TargetEntry *) lfirst(ref_tl_item);
 
        Assert(reftle->resno == resno);
@@ -1009,6 +1012,7 @@ generate_append_tlist(List *colTypes, bool flag,
                                resno,
                                colType,
                                colTypmod,
+                               colColl,
                                0);
        tle = makeTargetEntry((Expr *) expr,
                              (AttrNumber) resno++,
@@ -1025,6 +1029,7 @@ generate_append_tlist(List *colTypes, bool flag,
                                resno,
                                INT4OID,
                                -1,
+                               InvalidOid,
                                0);
        tle = makeTargetEntry((Expr *) expr,
                              (AttrNumber) resno++,
@@ -1344,6 +1349,7 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
        char       *attname;
        Oid         atttypid;
        int32       atttypmod;
+       Oid         attcollation;
        int         new_attno;
 
        att = old_tupdesc->attrs[old_attno];
@@ -1356,6 +1362,7 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
        attname = NameStr(att->attname);
        atttypid = att->atttypid;
        atttypmod = att->atttypmod;
+       attcollation = att->attcollation;
 
        /*
         * When we are generating the "translation list" for the parent table
@@ -1367,6 +1374,7 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
                                         (AttrNumber) (old_attno + 1),
                                         atttypid,
                                         atttypmod,
+                                        attcollation,
                                         0));
            continue;
        }
@@ -1409,6 +1417,7 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
                                     (AttrNumber) (new_attno + 1),
                                     atttypid,
                                     atttypmod,
+                                    attcollation,
                                     0));
    }
 
index 86990c872529db91b7e5ee3d7c79b515fbaf3fde..fa0952618b13367029be0ea21712c972d58237b8 100644 (file)
@@ -100,7 +100,7 @@ static List *simplify_and_arguments(List *args,
                       bool *haveNull, bool *forceFalse);
 static Node *simplify_boolean_equality(Oid opno, List *args);
 static Expr *simplify_function(Oid funcid,
-                 Oid result_type, int32 result_typmod, List **args,
+                 Oid result_type, int32 result_typmod, Oid collid, List **args,
                  bool has_named_args,
                  bool allow_inline,
                  eval_const_expressions_context *context);
@@ -114,7 +114,7 @@ static List *fetch_function_defaults(HeapTuple func_tuple);
 static void recheck_cast_function_args(List *args, Oid result_type,
                           HeapTuple func_tuple);
 static Expr *evaluate_function(Oid funcid,
-                 Oid result_type, int32 result_typmod, List *args,
+                 Oid result_type, int32 result_typmod, Oid collid, List *args,
                  HeapTuple func_tuple,
                  eval_const_expressions_context *context);
 static Expr *inline_function(Oid funcid, Oid result_type, List *args,
@@ -156,6 +156,7 @@ make_opclause(Oid opno, Oid opresulttype, bool opretset,
        expr->args = list_make2(leftop, rightop);
    else
        expr->args = list_make1(leftop);
+   expr->collid = select_common_collation(NULL, expr->args, false);
    expr->location = -1;
    return (Expr *) expr;
 }
@@ -1973,7 +1974,7 @@ set_coercionform_dontcare_walker(Node *node, void *context)
  */
 static bool
 rowtype_field_matches(Oid rowtypeid, int fieldnum,
-                     Oid expectedtype, int32 expectedtypmod)
+                     Oid expectedtype, int32 expectedtypmod, Oid expectedcollation)
 {
    TupleDesc   tupdesc;
    Form_pg_attribute attr;
@@ -1990,7 +1991,8 @@ rowtype_field_matches(Oid rowtypeid, int fieldnum,
    attr = tupdesc->attrs[fieldnum - 1];
    if (attr->attisdropped ||
        attr->atttypid != expectedtype ||
-       attr->atttypmod != expectedtypmod)
+       attr->atttypmod != expectedtypmod ||
+       attr->attcollation != expectedcollation)
    {
        ReleaseTupleDesc(tupdesc);
        return false;
@@ -2121,6 +2123,7 @@ eval_const_expressions_mutator(Node *node,
                    int16       typLen;
                    bool        typByVal;
                    Datum       pval;
+                   Const      *cnst;
 
                    Assert(prm->ptype == param->paramtype);
                    get_typlenbyval(param->paramtype, &typLen, &typByVal);
@@ -2128,12 +2131,14 @@ eval_const_expressions_mutator(Node *node,
                        pval = prm->value;
                    else
                        pval = datumCopy(prm->value, typByVal, typLen);
-                   return (Node *) makeConst(param->paramtype,
+                   cnst = makeConst(param->paramtype,
                                              param->paramtypmod,
                                              (int) typLen,
                                              pval,
                                              prm->isnull,
                                              typByVal);
+                   cnst->constcollid = param->paramcollation;
+                   return (Node *) cnst;
                }
            }
        }
@@ -2173,6 +2178,7 @@ eval_const_expressions_mutator(Node *node,
         */
        simple = simplify_function(expr->funcid,
                                   expr->funcresulttype, exprTypmod(node),
+                                  expr->collid,
                                   &args,
                                   has_named_args, true, context);
        if (simple)             /* successfully simplified it */
@@ -2190,6 +2196,7 @@ eval_const_expressions_mutator(Node *node,
        newexpr->funcretset = expr->funcretset;
        newexpr->funcformat = expr->funcformat;
        newexpr->args = args;
+       newexpr->collid = expr->collid;
        newexpr->location = expr->location;
        return (Node *) newexpr;
    }
@@ -2221,6 +2228,7 @@ eval_const_expressions_mutator(Node *node,
         */
        simple = simplify_function(expr->opfuncid,
                                   expr->opresulttype, -1,
+                                  expr->collid,
                                   &args,
                                   false, true, context);
        if (simple)             /* successfully simplified it */
@@ -2250,6 +2258,7 @@ eval_const_expressions_mutator(Node *node,
        newexpr->opresulttype = expr->opresulttype;
        newexpr->opretset = expr->opretset;
        newexpr->args = args;
+       newexpr->collid = expr->collid;
        newexpr->location = expr->location;
        return (Node *) newexpr;
    }
@@ -2314,6 +2323,7 @@ eval_const_expressions_mutator(Node *node,
             */
            simple = simplify_function(expr->opfuncid,
                                       expr->opresulttype, -1,
+                                      expr->collid,
                                       &args,
                                       false, false, context);
            if (simple)         /* successfully simplified it */
@@ -2342,6 +2352,7 @@ eval_const_expressions_mutator(Node *node,
        newexpr->opresulttype = expr->opresulttype;
        newexpr->opretset = expr->opretset;
        newexpr->args = args;
+       newexpr->collid = expr->collid;
        newexpr->location = expr->location;
        return (Node *) newexpr;
    }
@@ -2493,7 +2504,7 @@ eval_const_expressions_mutator(Node *node,
        getTypeInputInfo(expr->resulttype, &infunc, &intypioparam);
 
        simple = simplify_function(outfunc,
-                                  CSTRINGOID, -1,
+                                  CSTRINGOID, -1, InvalidOid,
                                   &args,
                                   false, true, context);
        if (simple)             /* successfully simplified output fn */
@@ -2510,8 +2521,11 @@ eval_const_expressions_mutator(Node *node,
                                        Int32GetDatum(-1),
                                        false, true));
 
+           /* preserve collation of input expression */
            simple = simplify_function(infunc,
-                                      expr->resulttype, -1,
+                                      expr->resulttype,
+                                      -1,
+                                      exprCollation((Node *) arg),
                                       &args,
                                       false, true, context);
            if (simple)         /* successfully simplified input fn */
@@ -2690,6 +2704,7 @@ eval_const_expressions_mutator(Node *node,
        /* Otherwise we need a new CASE node */
        newcase = makeNode(CaseExpr);
        newcase->casetype = caseexpr->casetype;
+       newcase->casecollation = caseexpr->casecollation;
        newcase->arg = (Expr *) newarg;
        newcase->args = newargs;
        newcase->defresult = (Expr *) defresult;
@@ -2782,6 +2797,7 @@ eval_const_expressions_mutator(Node *node,
 
        newcoalesce = makeNode(CoalesceExpr);
        newcoalesce->coalescetype = coalesceexpr->coalescetype;
+       newcoalesce->coalescecollation = coalesceexpr->coalescecollation;
        newcoalesce->args = newargs;
        newcoalesce->location = coalesceexpr->location;
        return (Node *) newcoalesce;
@@ -2811,11 +2827,13 @@ eval_const_expressions_mutator(Node *node,
            if (rowtype_field_matches(((Var *) arg)->vartype,
                                      fselect->fieldnum,
                                      fselect->resulttype,
-                                     fselect->resulttypmod))
+                                     fselect->resulttypmod,
+                                     fselect->resultcollation))
                return (Node *) makeVar(((Var *) arg)->varno,
                                        fselect->fieldnum,
                                        fselect->resulttype,
                                        fselect->resulttypmod,
+                                       fselect->resultcollation,
                                        ((Var *) arg)->varlevelsup);
        }
        if (arg && IsA(arg, RowExpr))
@@ -2831,9 +2849,11 @@ eval_const_expressions_mutator(Node *node,
                if (rowtype_field_matches(rowexpr->row_typeid,
                                          fselect->fieldnum,
                                          fselect->resulttype,
-                                         fselect->resulttypmod) &&
+                                         fselect->resulttypmod,
+                                         fselect->resultcollation) &&
                    fselect->resulttype == exprType(fld) &&
-                   fselect->resulttypmod == exprTypmod(fld))
+                   fselect->resulttypmod == exprTypmod(fld) &&
+                   fselect->resultcollation == exprCollation(fld))
                    return fld;
            }
        }
@@ -2842,6 +2862,7 @@ eval_const_expressions_mutator(Node *node,
        newfselect->fieldnum = fselect->fieldnum;
        newfselect->resulttype = fselect->resulttype;
        newfselect->resulttypmod = fselect->resulttypmod;
+       newfselect->resultcollation = fselect->resultcollation;
        return (Node *) newfselect;
    }
    if (IsA(node, NullTest))
@@ -3299,7 +3320,7 @@ simplify_boolean_equality(Oid opno, List *args)
  * pass-by-reference, and it may get modified even if simplification fails.
  */
 static Expr *
-simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
+simplify_function(Oid funcid, Oid result_type, int32 result_typmod, Oid collid,
                  List **args,
                  bool has_named_args,
                  bool allow_inline,
@@ -3330,7 +3351,7 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
    else if (((Form_pg_proc) GETSTRUCT(func_tuple))->pronargs > list_length(*args))
        *args = add_function_defaults(*args, result_type, func_tuple, context);
 
-   newexpr = evaluate_function(funcid, result_type, result_typmod, *args,
+   newexpr = evaluate_function(funcid, result_type, result_typmod, collid, *args,
                                func_tuple, context);
 
    if (!newexpr && allow_inline)
@@ -3581,7 +3602,8 @@ recheck_cast_function_args(List *args, Oid result_type, HeapTuple func_tuple)
  * simplify the function.
  */
 static Expr *
-evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, List *args,
+evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, Oid collid,
+                 List *args,
                  HeapTuple func_tuple,
                  eval_const_expressions_context *context)
 {
@@ -3664,6 +3686,7 @@ evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, List *args,
    newexpr->funcretset = false;
    newexpr->funcformat = COERCE_DONTCARE;      /* doesn't matter */
    newexpr->args = args;
+   newexpr->collid = collid;
    newexpr->location = -1;
 
    return evaluate_expr((Expr *) newexpr, result_type, result_typmod);
index adbe45caec49e7189684debfc00cd4c2e739f08b..1f79ba24fb3fdfb3534048afbe5afd667d14a696 100644 (file)
@@ -198,10 +198,12 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
            info->indexkeys = (int *) palloc(sizeof(int) * ncolumns);
            info->opfamily = (Oid *) palloc(sizeof(Oid) * ncolumns);
            info->opcintype = (Oid *) palloc(sizeof(Oid) * ncolumns);
+           info->indexcollations = (Oid *) palloc(sizeof(Oid) * ncolumns);
 
            for (i = 0; i < ncolumns; i++)
            {
                info->indexkeys[i] = index->indkey.values[i];
+               info->indexcollations[i] = indexRelation->rd_indcollation[i];
                info->opfamily[i] = indexRelation->rd_opfamily[i];
                info->opcintype[i] = indexRelation->rd_opcintype[i];
            }
@@ -634,6 +636,7 @@ get_relation_constraints(PlannerInfo *root,
                                                  i,
                                                  att->atttypid,
                                                  att->atttypmod,
+                                                 att->attcollation,
                                                  0);
                    ntest->nulltesttype = IS_NOT_NULL;
                    ntest->argisrow = type_is_rowtype(att->atttypid);
@@ -797,6 +800,7 @@ build_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
                              attrno,
                              att_tup->atttypid,
                              att_tup->atttypmod,
+                             att_tup->attcollation,
                              0);
 
                tlist = lappend(tlist,
index 6e80b4c0c7b66bd7cb24584c08ebfd34afe85950..4561e8e92f136e6527d06980f5dae39b55eaa334 100644 (file)
@@ -912,6 +912,7 @@ arrayconst_startup_fn(Node *clause, PredIterInfo info)
    state->constexpr.xpr.type = T_Const;
    state->constexpr.consttype = ARR_ELEMTYPE(arrayval);
    state->constexpr.consttypmod = -1;
+   state->constexpr.constcollid = arrayconst->constcollid;
    state->constexpr.constlen = elmlen;
    state->constexpr.constbyval = elmbyval;
    lsecond(state->opexpr.args) = &state->constexpr;
index 070e4c177a60e56d005c30dd6258c2ebb9b177bf..22447f92a2e1647d21e4ca0ec8f9eef4a45e826b 100644 (file)
@@ -1203,6 +1203,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
    ListCell   *left_tlist,
               *lct,
               *lcm,
+              *lcc,
               *l;
    List       *targetvars,
               *targetnames,
@@ -1296,10 +1297,11 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
    targetnames = NIL;
    left_tlist = list_head(leftmostQuery->targetList);
 
-   forboth(lct, sostmt->colTypes, lcm, sostmt->colTypmods)
+   forthree(lct, sostmt->colTypes, lcm, sostmt->colTypmods, lcc, sostmt->colCollations)
    {
        Oid         colType = lfirst_oid(lct);
        int32       colTypmod = lfirst_int(lcm);
+       Oid         colCollation = lfirst_oid(lcc);
        TargetEntry *lefttle = (TargetEntry *) lfirst(left_tlist);
        char       *colName;
        TargetEntry *tle;
@@ -1311,6 +1313,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
                      lefttle->resno,
                      colType,
                      colTypmod,
+                     colCollation,
                      0);
        var->location = exprLocation((Node *) lefttle->expr);
        tle = makeTargetEntry((Expr *) var,
@@ -1418,7 +1421,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
  *     Recursively transform leaves and internal nodes of a set-op tree
  *
  * In addition to returning the transformed node, we return a list of
- * expression nodes showing the type, typmod, and location (for error messages)
+ * expression nodes showing the type, typmod, collation, and location (for error messages)
  * of each output column of the set-op node.  This is used only during the
  * internal recursion of this function.  At the upper levels we use
  * SetToDefault nodes for this purpose, since they carry exactly the fields
@@ -1591,6 +1594,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
        *colInfo = NIL;
        op->colTypes = NIL;
        op->colTypmods = NIL;
+       op->colCollations = NIL;
        op->groupClauses = NIL;
        forboth(lci, lcolinfo, rci, rcolinfo)
        {
@@ -1604,6 +1608,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
            SetToDefault *rescolnode;
            Oid         rescoltype;
            int32       rescoltypmod;
+           Oid         rescolcoll;
 
            /* select common type, same as CASE et al */
            rescoltype = select_common_type(pstate,
@@ -1615,6 +1620,12 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
                rescoltypmod = lcoltypmod;
            else
                rescoltypmod = -1;
+           /* Select common collation.  A common collation is
+            * required for all set operators except UNION ALL; see
+            * SQL:2008-2 7.13 SR 15c. */
+           rescolcoll = select_common_collation(pstate,
+                                                list_make2(lcolnode, rcolnode),
+                                                (op->op == SETOP_UNION && op->all));
 
            /*
             * Verify the coercions are actually possible.  If not, we'd fail
@@ -1643,11 +1654,13 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
            rescolnode = makeNode(SetToDefault);
            rescolnode->typeId = rescoltype;
            rescolnode->typeMod = rescoltypmod;
+           rescolnode->collid = rescolcoll;
            rescolnode->location = exprLocation(bestexpr);
            *colInfo = lappend(*colInfo, rescolnode);
 
            op->colTypes = lappend_oid(op->colTypes, rescoltype);
            op->colTypmods = lappend_int(op->colTypmods, rescoltypmod);
+           op->colCollations = lappend_oid(op->colCollations, rescolcoll);
 
            /*
             * For all cases except UNION ALL, identify the grouping operators
index ced78734bbf386c7bea19bc49d238aa1838f6636..a1bcf02f5be530bec49ccf689b06b1afd761d598 100644 (file)
@@ -261,6 +261,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
 
 %type    func_name handler_name qual_Op qual_all_Op subquery_Op
                opt_class opt_inline_handler opt_validator validator_clause
+               opt_collate
 
 %type   qualified_name OptConstrFromTable
 
@@ -394,7 +395,8 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
 %type    copy_generic_opt_list copy_generic_opt_arg_list
 %type    copy_options
 
-%type  Typename SimpleTypename ConstTypename
+%type  Typename SimpleTypename SimpleTypenameWithoutCollation
+               ConstTypename
                GenericType Numeric opt_float
                Character ConstCharacter
                CharacterWithLength CharacterWithoutLength
@@ -5323,38 +5325,45 @@ index_params:   index_elem                          { $$ = list_make1($1); }
  * expressions in parens.  For backwards-compatibility reasons, we allow
  * an expression that's just a function call to be written without parens.
  */
-index_elem:    ColId opt_class opt_asc_desc opt_nulls_order
+index_elem:    ColId opt_collate opt_class opt_asc_desc opt_nulls_order
                {
                    $$ = makeNode(IndexElem);
                    $$->name = $1;
                    $$->expr = NULL;
                    $$->indexcolname = NULL;
-                   $$->opclass = $2;
-                   $$->ordering = $3;
-                   $$->nulls_ordering = $4;
+                   $$->collation = $2;
+                   $$->opclass = $3;
+                   $$->ordering = $4;
+                   $$->nulls_ordering = $5;
                }
-           | func_expr opt_class opt_asc_desc opt_nulls_order
+           | func_expr opt_collate opt_class opt_asc_desc opt_nulls_order
                {
                    $$ = makeNode(IndexElem);
                    $$->name = NULL;
                    $$->expr = $1;
                    $$->indexcolname = NULL;
-                   $$->opclass = $2;
-                   $$->ordering = $3;
-                   $$->nulls_ordering = $4;
+                   $$->collation = $2;
+                   $$->opclass = $3;
+                   $$->ordering = $4;
+                   $$->nulls_ordering = $5;
                }
-           | '(' a_expr ')' opt_class opt_asc_desc opt_nulls_order
+           | '(' a_expr ')' opt_collate opt_class opt_asc_desc opt_nulls_order
                {
                    $$ = makeNode(IndexElem);
                    $$->name = NULL;
                    $$->expr = $2;
                    $$->indexcolname = NULL;
-                   $$->opclass = $4;
-                   $$->ordering = $5;
-                   $$->nulls_ordering = $6;
+                   $$->collation = $4;
+                   $$->opclass = $5;
+                   $$->ordering = $6;
+                   $$->nulls_ordering = $7;
                }
        ;
 
+opt_collate: COLLATE any_name                      { $$ = $2; }
+           | /*EMPTY*/                             { $$ = NIL; }
+       ;
+
 opt_class: any_name                                { $$ = $1; }
            | USING any_name                        { $$ = $2; }
            | /*EMPTY*/                             { $$ = NIL; }
@@ -8776,6 +8785,13 @@ opt_array_bounds:
        ;
 
 SimpleTypename:
+           SimpleTypenameWithoutCollation opt_collate
+           {
+               $$ = $1;
+               $$->collnames = $2;
+           }
+
+SimpleTypenameWithoutCollation:
            GenericType                             { $$ = $1; }
            | Numeric                               { $$ = $1; }
            | Bit                                   { $$ = $1; }
@@ -9811,6 +9827,14 @@ c_expr:      columnref                               { $$ = $1; }
                    r->location = @1;
                    $$ = (Node *)r;
                }
+           | c_expr COLLATE any_name
+               {
+                   CollateClause *n = makeNode(CollateClause);
+                   n->arg = (Expr *) $1;
+                   n->collnames = $3;
+                   n->location = @2;
+                   $$ = (Node *)n;
+               }
        ;
 
 /*
index e43bc54b3fd426ce038448082ee714a8364a692e..8267627c42fd943e33930d631a375b67af648bb4 100644 (file)
@@ -723,6 +723,7 @@ build_aggregate_fnexprs(Oid *agg_input_types,
                        Oid agg_result_type,
                        Oid transfn_oid,
                        Oid finalfn_oid,
+                       Oid collation,
                        Expr **transfnexpr,
                        Expr **finalfnexpr)
 {
@@ -741,6 +742,7 @@ build_aggregate_fnexprs(Oid *agg_input_types,
    argp->paramid = -1;
    argp->paramtype = agg_state_type;
    argp->paramtypmod = -1;
+   argp->paramcollation = collation;
    argp->location = -1;
 
    args = list_make1(argp);
@@ -752,6 +754,7 @@ build_aggregate_fnexprs(Oid *agg_input_types,
        argp->paramid = -1;
        argp->paramtype = agg_input_types[i];
        argp->paramtypmod = -1;
+       argp->paramcollation = collation;
        argp->location = -1;
        args = lappend(args, argp);
    }
@@ -759,6 +762,7 @@ build_aggregate_fnexprs(Oid *agg_input_types,
    *transfnexpr = (Expr *) makeFuncExpr(transfn_oid,
                                         agg_state_type,
                                         args,
+                                        collation,
                                         COERCE_DONTCARE);
 
    /* see if we have a final function */
@@ -776,11 +780,13 @@ build_aggregate_fnexprs(Oid *agg_input_types,
    argp->paramid = -1;
    argp->paramtype = agg_state_type;
    argp->paramtypmod = -1;
+   argp->paramcollation = collation;
    argp->location = -1;
    args = list_make1(argp);
 
    *finalfnexpr = (Expr *) makeFuncExpr(finalfn_oid,
                                         agg_result_type,
                                         args,
+                                        collation,
                                         COERCE_DONTCARE);
 }
index f9560d07b9c55059f7d294a790e53ec7ca1b2863..d250e0c8598abb1508a70d6c4cf5b2459bf595ef 100644 (file)
@@ -613,7 +613,8 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
 
        tupdesc = BuildDescFromLists(rte->eref->colnames,
                                     rte->funccoltypes,
-                                    rte->funccoltypmods);
+                                    rte->funccoltypmods,
+                                    rte->funccolcollations);
        CheckAttributeNamesTypes(tupdesc, RELKIND_COMPOSITE_TYPE, false);
    }
 
@@ -1935,6 +1936,7 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle,
                    bool resolveUnknown)
 {
    Oid         restype = exprType((Node *) tle->expr);
+   Oid         rescollation = exprCollation((Node *) tle->expr);
    Oid         sortop;
    Oid         eqop;
    bool        hashable;
@@ -2018,6 +2020,12 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle,
            break;
    }
 
+   if (type_is_collatable(restype) && !OidIsValid(rescollation))
+       ereport(ERROR,
+               (errcode(ERRCODE_INDETERMINATE_COLLATION),
+                errmsg("no collation was derived for the sort expression"),
+                errhint("Use the COLLATE clause to set the collation explicitly.")));
+
    cancel_parser_errposition_callback(&pcbstate);
 
    /* avoid making duplicate sortlist entries */
index 5b0dc1420d0476c78c1565c4ee129c07003adeae..2fd808d26b200acaace440b464b5cfee4c975c1b 100644 (file)
@@ -16,6 +16,7 @@
 
 #include "catalog/pg_cast.h"
 #include "catalog/pg_class.h"
+#include "catalog/pg_collation.h"
 #include "catalog/pg_inherits_fn.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
@@ -216,6 +217,7 @@ coerce_type(ParseState *pstate, Node *node,
 
        newcon->consttype = baseTypeId;
        newcon->consttypmod = inputTypeMod;
+       newcon->constcollid = get_typcollation(newcon->consttype);
        newcon->constlen = typeLen(targetType);
        newcon->constbyval = typeByVal(targetType);
        newcon->constisnull = con->constisnull;
@@ -277,6 +279,14 @@ coerce_type(ParseState *pstate, Node *node,
        if (result)
            return result;
    }
+   if (IsA(node, CollateClause))
+   {
+       CollateClause *cc = (CollateClause *) node;
+
+       cc->arg = (Expr *) coerce_type(pstate, (Node *) cc->arg, inputTypeId, targetTypeId, targetTypeMod,
+                                      ccontext, cformat, location);
+       return (Node *) cc;
+   }
    pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext,
                                     &funcId);
    if (pathtype != COERCION_PATH_NONE)
@@ -718,6 +728,7 @@ build_coercion_expression(Node *node,
        FuncExpr   *fexpr;
        List       *args;
        Const      *cons;
+       Oid         collation;
 
        Assert(OidIsValid(funcId));
 
@@ -749,7 +760,9 @@ build_coercion_expression(Node *node,
            args = lappend(args, cons);
        }
 
-       fexpr = makeFuncExpr(funcId, targetTypeId, args, cformat);
+       collation = coercion_expression_result_collation(targetTypeId, node);
+
+       fexpr = makeFuncExpr(funcId, targetTypeId, args, collation, cformat);
        fexpr->location = location;
        return (Node *) fexpr;
    }
@@ -2081,3 +2094,120 @@ typeIsOfTypedTable(Oid reltypeId, Oid reloftypeId)
 
    return result;
 }
+
+
+/*
+ * select_common_collation() -- determine one collation to apply for
+ * an expression node, for evaluating the expression itself or to
+ * label the result of the expression node.
+ *
+ * none_ok means that it is permitted to return "no" collation.  It is
+ * then not possible to sort the result value of whatever expression
+ * is applying this.  none_ok = true reflects the rules of SQL
+ * standard clause "Result of data type combinations", none_ok = false
+ * reflects the rules of clause "Collation determination" (in some
+ * cases invoked via "Grouping operations").
+ */
+Oid
+select_common_collation(ParseState *pstate, List *exprs, bool none_ok)
+{
+   ListCell   *lc;
+
+   /*
+    * Check if there are any explicit collation derivations.  If so,
+    * they must all be the same.
+    */
+   foreach(lc, exprs)
+   {
+       Node       *pexpr = (Node *) lfirst(lc);
+       Oid         pcoll = exprCollation(pexpr);
+       bool        pexplicit = IsA(pexpr, CollateClause);
+
+       if (pcoll && pexplicit)
+       {
+           ListCell    *lc2;
+           for_each_cell(lc2, lnext(lc))
+           {
+               Node       *nexpr = (Node *) lfirst(lc2);
+               Oid         ncoll = exprCollation(nexpr);
+               bool        nexplicit = IsA(nexpr, CollateClause);
+
+               if (!ncoll || !nexplicit)
+                   continue;
+
+               if (ncoll != pcoll)
+                   ereport(ERROR,
+                           (errcode(ERRCODE_COLLATION_MISMATCH),
+                            errmsg("collation mismatch between explicit collations \"%s\" and \"%s\"",
+                                   get_collation_name(pcoll),
+                                   get_collation_name(ncoll)),
+                            parser_errposition(pstate, exprLocation(nexpr))));
+           }
+
+           return pcoll;
+       }
+   }
+
+   /*
+    * Check if there are any implicit collation derivations.
+    */
+   foreach(lc, exprs)
+   {
+       Node       *pexpr = (Node *) lfirst(lc);
+       Oid         pcoll = exprCollation(pexpr);
+
+       if (pcoll && pcoll != DEFAULT_COLLATION_OID)
+       {
+           ListCell    *lc2;
+           for_each_cell(lc2, lnext(lc))
+           {
+               Node       *nexpr = (Node *) lfirst(lc2);
+               Oid         ncoll = exprCollation(nexpr);
+
+               if (!ncoll || ncoll == DEFAULT_COLLATION_OID)
+                   continue;
+
+               if (ncoll != pcoll)
+               {
+                   if (none_ok)
+                       return InvalidOid;
+                   ereport(ERROR,
+                           (errcode(ERRCODE_COLLATION_MISMATCH),
+                            errmsg("collation mismatch between implicit collations \"%s\" and \"%s\"",
+                                   get_collation_name(pcoll),
+                                   get_collation_name(ncoll)),
+                            errhint("You can override the collation by applying the COLLATE clause to one or both expressions."),
+                            parser_errposition(pstate, exprLocation(nexpr))));
+               }
+           }
+
+           return pcoll;
+       }
+   }
+
+   foreach(lc, exprs)
+   {
+       Node       *pexpr = (Node *) lfirst(lc);
+       Oid         pcoll = exprCollation(pexpr);
+
+       if (pcoll == DEFAULT_COLLATION_OID)
+       {
+           ListCell    *lc2;
+           for_each_cell(lc2, lnext(lc))
+           {
+               Node       *nexpr = (Node *) lfirst(lc2);
+               Oid         ncoll = exprCollation(nexpr);
+
+               if (ncoll != pcoll)
+                   break;
+           }
+
+           return pcoll;
+       }
+   }
+
+   /*
+    * Else use default
+    */
+   return InvalidOid;
+}
index 24ba008f9eeff3d45e860030efd11a8d23725092..4d3d33eb079ca04fbd3841285a8938b80485675f 100644 (file)
  */
 #include "postgres.h"
 
+#include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
 #include "nodes/nodeFuncs.h"
 #include "parser/analyze.h"
 #include "parser/parse_cte.h"
 #include "utils/builtins.h"
+#include "utils/lsyscache.h"
 
 
 /* Enumeration of contexts in which a self-reference is disallowed */
@@ -263,11 +265,13 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
         */
        ListCell   *lctlist,
                   *lctyp,
-                  *lctypmod;
+                  *lctypmod,
+                  *lccoll;
        int         varattno;
 
        lctyp = list_head(cte->ctecoltypes);
        lctypmod = list_head(cte->ctecoltypmods);
+       lccoll = list_head(cte->ctecolcollations);
        varattno = 0;
        foreach(lctlist, query->targetList)
        {
@@ -278,7 +282,7 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
                continue;
            varattno++;
            Assert(varattno == te->resno);
-           if (lctyp == NULL || lctypmod == NULL)      /* shouldn't happen */
+           if (lctyp == NULL || lctypmod == NULL || lccoll == NULL)        /* shouldn't happen */
                elog(ERROR, "wrong number of output columns in WITH");
            texpr = (Node *) te->expr;
            if (exprType(texpr) != lfirst_oid(lctyp) ||
@@ -293,10 +297,20 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
                                                         exprTypmod(texpr))),
                         errhint("Cast the output of the non-recursive term to the correct type."),
                         parser_errposition(pstate, exprLocation(texpr))));
+           if (exprCollation(texpr) != lfirst_oid(lccoll))
+               ereport(ERROR,
+                       (errcode(ERRCODE_COLLATION_MISMATCH),
+                        errmsg("recursive query \"%s\" column %d has collation \"%s\" in non-recursive term but collation \"%s\" overall",
+                               cte->ctename, varattno,
+                               get_collation_name(lfirst_oid(lccoll)),
+                               get_collation_name(exprCollation(texpr))),
+                        errhint("Use the COLLATE clause to set the collation of the non-recursive term."),
+                        parser_errposition(pstate, exprLocation(texpr))));
            lctyp = lnext(lctyp);
            lctypmod = lnext(lctypmod);
+           lccoll = lnext(lccoll);
        }
-       if (lctyp != NULL || lctypmod != NULL)  /* shouldn't happen */
+       if (lctyp != NULL || lctypmod != NULL || lccoll != NULL)    /* shouldn't happen */
            elog(ERROR, "wrong number of output columns in WITH");
    }
 }
@@ -331,7 +345,7 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist)
     * handling.)
     */
    cte->ctecolnames = copyObject(cte->aliascolnames);
-   cte->ctecoltypes = cte->ctecoltypmods = NIL;
+   cte->ctecoltypes = cte->ctecoltypmods = cte->ctecolcollations = NIL;
    numaliases = list_length(cte->aliascolnames);
    varattno = 0;
    foreach(tlistitem, tlist)
@@ -339,6 +353,7 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist)
        TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
        Oid         coltype;
        int32       coltypmod;
+       Oid         colcoll;
 
        if (te->resjunk)
            continue;
@@ -353,6 +368,7 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist)
        }
        coltype = exprType((Node *) te->expr);
        coltypmod = exprTypmod((Node *) te->expr);
+       colcoll = exprCollation((Node *) te->expr);
 
        /*
         * If the CTE is recursive, force the exposed column type of any
@@ -366,9 +382,11 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist)
        {
            coltype = TEXTOID;
            coltypmod = -1;     /* should be -1 already, but be sure */
+           colcoll = DEFAULT_COLLATION_OID;
        }
        cte->ctecoltypes = lappend_oid(cte->ctecoltypes, coltype);
        cte->ctecoltypmods = lappend_int(cte->ctecoltypmods, coltypmod);
+       cte->ctecolcollations = lappend_oid(cte->ctecolcollations, colcoll);
    }
    if (varattno < numaliases)
        ereport(ERROR,
index 129b39cb26fd10c03296efb7181301934aef5327..ae5653259280b140aef1cc03bdbf107d6d91457c 100644 (file)
@@ -65,6 +65,7 @@ static Node *transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte,
 static Node *transformIndirection(ParseState *pstate, Node *basenode,
                     List *indirection);
 static Node *transformTypeCast(ParseState *pstate, TypeCast *tc);
+static Node *transformCollateClause(ParseState *pstate, CollateClause *c);
 static Node *make_row_comparison_op(ParseState *pstate, List *opname,
                       List *largs, List *rargs, int location);
 static Node *make_row_distinct_op(ParseState *pstate, List *opname,
@@ -146,6 +147,12 @@ transformExpr(ParseState *pstate, Node *expr)
            {
                TypeCast   *tc = (TypeCast *) expr;
 
+               if (tc->typeName->collnames)
+                   ereport(ERROR,
+                           (errcode(ERRCODE_SYNTAX_ERROR),
+                            errmsg("COLLATE clause not allowed in cast target"),
+                            parser_errposition(pstate, tc->typeName->location)));
+
                /*
                 * If the subject of the typecast is an ARRAY[] construct and
                 * the target type is an array type, we invoke
@@ -185,6 +192,10 @@ transformExpr(ParseState *pstate, Node *expr)
                break;
            }
 
+       case T_CollateClause:
+           result = transformCollateClause(pstate, (CollateClause *) expr);
+           break;
+
        case T_A_Expr:
            {
                A_Expr     *a = (A_Expr *) expr;
@@ -423,6 +434,7 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
                                                           exprType(result),
                                                           InvalidOid,
                                                           exprTypmod(result),
+                                                          exprCollation(result),
                                                           subscripts,
                                                           NULL);
            subscripts = NIL;
@@ -444,6 +456,7 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
                                                   exprType(result),
                                                   InvalidOid,
                                                   exprTypmod(result),
+                                                  exprCollation(result),
                                                   subscripts,
                                                   NULL);
 
@@ -1267,6 +1280,7 @@ transformCaseExpr(ParseState *pstate, CaseExpr *c)
        placeholder = makeNode(CaseTestExpr);
        placeholder->typeId = exprType(arg);
        placeholder->typeMod = exprTypmod(arg);
+       placeholder->collation = exprCollation(arg);
    }
    else
        placeholder = NULL;
@@ -1351,6 +1365,8 @@ transformCaseExpr(ParseState *pstate, CaseExpr *c)
                                  "CASE/WHEN");
    }
 
+   newc->casecollation = select_common_collation(pstate, resultexprs, true);
+
    newc->location = c->location;
 
    return (Node *) newc;
@@ -1461,6 +1477,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
            param->paramid = tent->resno;
            param->paramtype = exprType((Node *) tent->expr);
            param->paramtypmod = exprTypmod((Node *) tent->expr);
+           param->paramcollation = exprCollation((Node *) tent->expr);
            param->location = -1;
 
            right_list = lappend(right_list, param);
@@ -1704,6 +1721,7 @@ transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c)
    }
 
    newc->args = newcoercedargs;
+   newc->coalescecollation = select_common_collation(pstate, newcoercedargs, true);
    newc->location = c->location;
    return (Node *) newc;
 }
@@ -1728,6 +1746,7 @@ transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m)
    }
 
    newm->minmaxtype = select_common_type(pstate, newargs, funcname, NULL);
+   newm->collid = select_common_collation(pstate, newargs, false);
 
    /* Convert arguments if necessary */
    foreach(args, newargs)
@@ -2082,6 +2101,36 @@ transformTypeCast(ParseState *pstate, TypeCast *tc)
    return result;
 }
 
+/*
+ * Handle an explicit COLLATE clause.
+ *
+ * Transform the argument, and look up the collation name.
+ */
+static Node *
+transformCollateClause(ParseState *pstate, CollateClause *c)
+{
+   CollateClause *newc;
+   Oid     argtype;
+
+   newc = makeNode(CollateClause);
+   newc->arg = (Expr *) transformExpr(pstate, (Node *) c->arg);
+
+   argtype = exprType((Node *) newc->arg);
+   /* The unknown type is not collatable, but coerce_type() takes
+    * care of it separately, so we'll let it go here. */
+   if (!type_is_collatable(argtype) && argtype != UNKNOWNOID)
+       ereport(ERROR,
+               (errcode(ERRCODE_SYNTAX_ERROR),
+                errmsg("collations are not supported by type %s",
+                       format_type_be(argtype))));
+
+   newc->collOid = LookupCollation(pstate, c->collnames, c->location);
+   newc->collnames = c->collnames;
+   newc->location = c->location;
+
+   return (Node *) newc;
+}
+
 /*
  * Transform a "row compare-op row" construct
  *
@@ -2103,6 +2152,7 @@ make_row_comparison_op(ParseState *pstate, List *opname,
    List       *opexprs;
    List       *opnos;
    List       *opfamilies;
+   List       *collids;
    ListCell   *l,
               *r;
    List      **opfamily_lists;
@@ -2273,6 +2323,7 @@ make_row_comparison_op(ParseState *pstate, List *opname,
     * possibility that make_op inserted coercion operations.
     */
    opnos = NIL;
+   collids = NIL;
    largs = NIL;
    rargs = NIL;
    foreach(l, opexprs)
@@ -2280,6 +2331,7 @@ make_row_comparison_op(ParseState *pstate, List *opname,
        OpExpr     *cmp = (OpExpr *) lfirst(l);
 
        opnos = lappend_oid(opnos, cmp->opno);
+       collids = lappend_oid(collids, cmp->collid);
        largs = lappend(largs, linitial(cmp->args));
        rargs = lappend(rargs, lsecond(cmp->args));
    }
@@ -2288,6 +2340,7 @@ make_row_comparison_op(ParseState *pstate, List *opname,
    rcexpr->rctype = rctype;
    rcexpr->opnos = opnos;
    rcexpr->opfamilies = opfamilies;
+   rcexpr->collids = collids;
    rcexpr->largs = largs;
    rcexpr->rargs = rargs;
 
index f1a4f9b959e8b39a1270aff85b25f9639888330f..0af9cbd92b3dc1364cdefd656cd4e13937ff4b40 100644 (file)
@@ -78,6 +78,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
    bool        retset;
    int         nvargs;
    FuncDetailCode fdresult;
+   Oid         funccollid;
 
    /*
     * Most of the rest of the parser just assumes that functions do not have
@@ -343,6 +344,12 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
    /* perform the necessary typecasting of arguments */
    make_fn_arguments(pstate, fargs, actual_arg_types, declared_arg_types);
 
+   /* XXX: If we knew which functions required collation information,
+    * we could selectively set the last argument to true here. */
+   funccollid = select_common_collation(pstate, fargs, false);
+   if (!OidIsValid(funccollid))
+       funccollid = get_typcollation(rettype);
+
    /*
     * If it's a variadic function call, transform the last nvargs arguments
     * into an array --- unless it's an "any" variadic.
@@ -383,6 +390,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
        funcexpr->funcretset = retset;
        funcexpr->funcformat = COERCE_EXPLICIT_CALL;
        funcexpr->args = fargs;
+       funcexpr->collid = funccollid;
        funcexpr->location = location;
 
        retval = (Node *) funcexpr;
@@ -396,6 +404,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
        aggref->aggtype = rettype;
        /* args, aggorder, aggdistinct will be set by transformAggregateCall */
        aggref->aggstar = agg_star;
+       aggref->collid = funccollid;
        /* agglevelsup will be set by transformAggregateCall */
        aggref->location = location;
 
@@ -453,6 +462,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
        /* winref will be set by transformWindowFuncCall */
        wfunc->winstar = agg_star;
        wfunc->winagg = (fdresult == FUNCDETAIL_AGGREGATE);
+       wfunc->collid = funccollid;
        wfunc->location = location;
 
        /*
@@ -1303,7 +1313,7 @@ FuncNameAsType(List *funcname)
    Oid         result;
    Type        typtup;
 
-   typtup = LookupTypeName(NULL, makeTypeNameFromNameList(funcname), NULL);
+   typtup = LookupTypeName(NULL, makeTypeNameFromNameList(funcname), NULL, NULL);
    if (typtup == NULL)
        return InvalidOid;
 
@@ -1380,6 +1390,7 @@ ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg,
            fselect->fieldnum = i + 1;
            fselect->resulttype = att->atttypid;
            fselect->resulttypmod = att->atttypmod;
+           fselect->resultcollation = att->attcollation;
            return (Node *) fselect;
        }
    }
@@ -1489,7 +1500,7 @@ LookupTypeNameOid(const TypeName *typename)
    Oid         result;
    Type        typtup;
 
-   typtup = LookupTypeName(NULL, typename, NULL);
+   typtup = LookupTypeName(NULL, typename, NULL, NULL);
    if (typtup == NULL)
        ereport(ERROR,
                (errcode(ERRCODE_UNDEFINED_OBJECT),
index aed404eb1dcac305d969bf6351056b6a14bed71b..163fc891799b1bbca409f835ea23bc8477f61f14 100644 (file)
@@ -189,10 +189,11 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno, int location)
                sublevels_up;
    Oid         vartypeid;
    int32       type_mod;
+   Oid         varcollid;
 
    vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
-   get_rte_attribute_type(rte, attrno, &vartypeid, &type_mod);
-   result = makeVar(vnum, attrno, vartypeid, type_mod, sublevels_up);
+   get_rte_attribute_type(rte, attrno, &vartypeid, &type_mod, &varcollid);
+   result = makeVar(vnum, attrno, vartypeid, type_mod, varcollid, sublevels_up);
    result->location = location;
    return result;
 }
@@ -269,6 +270,7 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod)
  * elementType OID of array's element type (fetch with transformArrayType,
  *             or pass InvalidOid to do it here)
  * arrayTypMod typmod for the array (which is also typmod for the elements)
+ * arrayColl   OID of collation of array and array's elements
  * indirection Untransformed list of subscripts (must not be NIL)
  * assignFrom  NULL for array fetch, else transformed expression for source.
  */
@@ -278,6 +280,7 @@ transformArraySubscripts(ParseState *pstate,
                         Oid arrayType,
                         Oid elementType,
                         int32 arrayTypMod,
+                        Oid arrayColl,
                         List *indirection,
                         Node *assignFrom)
 {
@@ -404,6 +407,7 @@ transformArraySubscripts(ParseState *pstate,
    aref->refarraytype = arrayType;
    aref->refelemtype = elementType;
    aref->reftypmod = arrayTypMod;
+   aref->refcollid = arrayColl;
    aref->refupperindexpr = upperIndexpr;
    aref->reflowerindexpr = lowerIndexpr;
    aref->refexpr = (Expr *) arrayBase;
index 1f50bdcc34210d81313c60481d4b9789adaba08b..cad41d46f09e180958620f46b2a1931583cbd442 100644 (file)
@@ -782,6 +782,7 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
    List       *args;
    Oid         rettype;
    OpExpr     *result;
+   Oid         opcollid;
 
    /* Select the operator */
    if (rtree == NULL)
@@ -861,6 +862,12 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
    /* perform the necessary typecasting of arguments */
    make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
 
+   /* XXX: If we knew which functions required collation information,
+    * we could selectively set the last argument to true here. */
+   opcollid = select_common_collation(pstate, args, false);
+   if (!OidIsValid(opcollid))
+       opcollid = get_typcollation(rettype);
+
    /* and build the expression node */
    result = makeNode(OpExpr);
    result->opno = oprid(tup);
@@ -868,6 +875,7 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
    result->opresulttype = rettype;
    result->opretset = get_func_retset(opform->oprcode);
    result->args = args;
+   result->collid = opcollid;
    result->location = location;
 
    ReleaseSysCache(tup);
@@ -896,6 +904,7 @@ make_scalar_array_op(ParseState *pstate, List *opname,
    List       *args;
    Oid         rettype;
    ScalarArrayOpExpr *result;
+   Oid         opcollid;
 
    ltypeId = exprType(ltree);
    atypeId = exprType(rtree);
@@ -990,12 +999,19 @@ make_scalar_array_op(ParseState *pstate, List *opname,
    /* perform the necessary typecasting of arguments */
    make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
 
+   /* XXX: If we knew which functions required collation information,
+    * we could selectively set the last argument to true here. */
+   opcollid = select_common_collation(pstate, args, false);
+   if (!OidIsValid(opcollid))
+       opcollid = get_typcollation(rettype);
+
    /* and build the expression node */
    result = makeNode(ScalarArrayOpExpr);
    result->opno = oprid(tup);
    result->opfuncid = opform->oprcode;
    result->useOr = useOr;
    result->args = args;
+   result->collid = opcollid;
    result->location = location;
 
    ReleaseSysCache(tup);
index c9987d27234c5b630f17ecbe675e5b03708c4dc2..9e9f2e3ca0b353dd7e5ee556761ca7e21c993f86 100644 (file)
@@ -30,6 +30,7 @@
 #include "nodes/nodeFuncs.h"
 #include "parser/parse_param.h"
 #include "utils/builtins.h"
+#include "utils/lsyscache.h"
 
 
 typedef struct FixedParamState
@@ -113,6 +114,7 @@ fixed_paramref_hook(ParseState *pstate, ParamRef *pref)
    param->paramid = paramno;
    param->paramtype = parstate->paramTypes[paramno - 1];
    param->paramtypmod = -1;
+   param->paramcollation = get_typcollation(param->paramtype);
    param->location = pref->location;
 
    return (Node *) param;
@@ -165,6 +167,7 @@ variable_paramref_hook(ParseState *pstate, ParamRef *pref)
    param->paramid = paramno;
    param->paramtype = *pptype;
    param->paramtypmod = -1;
+   param->paramcollation = get_typcollation(param->paramtype);
    param->location = pref->location;
 
    return (Node *) param;
index 331ac670ff8e557137c1fd804adb10c65366a85d..497c726f31455fc6f24a0533bc3c411ce545e87b 100644 (file)
@@ -1098,6 +1098,7 @@ addRangeTableEntryForFunction(ParseState *pstate,
    rte->funcexpr = funcexpr;
    rte->funccoltypes = NIL;
    rte->funccoltypmods = NIL;
+   rte->funccolcollations = NIL;
    rte->alias = alias;
 
    eref = makeAlias(alias ? alias->aliasname : funcname, NIL);
@@ -1157,6 +1158,7 @@ addRangeTableEntryForFunction(ParseState *pstate,
            char       *attrname;
            Oid         attrtype;
            int32       attrtypmod;
+           Oid         attrcollation;
 
            attrname = pstrdup(n->colname);
            if (n->typeName->setof)
@@ -1165,10 +1167,11 @@ addRangeTableEntryForFunction(ParseState *pstate,
                         errmsg("column \"%s\" cannot be declared SETOF",
                                attrname),
                         parser_errposition(pstate, n->typeName->location)));
-           typenameTypeIdAndMod(pstate, n->typeName, &attrtype, &attrtypmod);
+           typenameTypeIdModColl(pstate, n->typeName, &attrtype, &attrtypmod, &attrcollation);
            eref->colnames = lappend(eref->colnames, makeString(attrname));
            rte->funccoltypes = lappend_oid(rte->funccoltypes, attrtype);
            rte->funccoltypmods = lappend_int(rte->funccoltypmods, attrtypmod);
+           rte->funccolcollations = lappend_oid(rte->funccolcollations, attrcollation);
        }
    }
    else
@@ -1381,6 +1384,7 @@ addRangeTableEntryForCTE(ParseState *pstate,
 
    rte->ctecoltypes = cte->ctecoltypes;
    rte->ctecoltypmods = cte->ctecoltypmods;
+   rte->ctecolcollations = cte->ctecolcollations;
 
    rte->alias = alias;
    if (alias)
@@ -1573,6 +1577,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
                        varnode = makeVar(rtindex, varattno,
                                          exprType((Node *) te->expr),
                                          exprTypmod((Node *) te->expr),
+                                         exprCollation((Node *) te->expr),
                                          sublevels_up);
                        varnode->location = location;
 
@@ -1612,6 +1617,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
 
                        varnode = makeVar(rtindex, 1,
                                          funcrettype, -1,
+                                         exprCollation(rte->funcexpr),
                                          sublevels_up);
                        varnode->location = location;
 
@@ -1626,12 +1632,14 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
                    {
                        ListCell   *l1;
                        ListCell   *l2;
+                       ListCell   *l3;
                        int         attnum = 0;
 
-                       forboth(l1, rte->funccoltypes, l2, rte->funccoltypmods)
+                       forthree(l1, rte->funccoltypes, l2, rte->funccoltypmods, l3, rte->funccolcollations)
                        {
                            Oid         attrtype = lfirst_oid(l1);
                            int32       attrtypmod = lfirst_int(l2);
+                           Oid         attrcollation = lfirst_oid(l3);
                            Var        *varnode;
 
                            attnum++;
@@ -1639,6 +1647,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
                                              attnum,
                                              attrtype,
                                              attrtypmod,
+                                             attrcollation,
                                              sublevels_up);
                            varnode->location = location;
                            *colvars = lappend(*colvars, varnode);
@@ -1681,6 +1690,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
                        varnode = makeVar(rtindex, varattno,
                                          exprType(col),
                                          exprTypmod(col),
+                                         exprCollation(col),
                                          sublevels_up);
                        varnode->location = location;
                        *colvars = lappend(*colvars, varnode);
@@ -1740,6 +1750,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
                        varnode = makeVar(rtindex, varattno,
                                          exprType(avar),
                                          exprTypmod(avar),
+                                         exprCollation(avar),
                                          sublevels_up);
                        varnode->location = location;
 
@@ -1753,12 +1764,14 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
                ListCell   *aliasp_item = list_head(rte->eref->colnames);
                ListCell   *lct;
                ListCell   *lcm;
+               ListCell   *lcc;
 
                varattno = 0;
-               forboth(lct, rte->ctecoltypes, lcm, rte->ctecoltypmods)
+               forthree(lct, rte->ctecoltypes, lcm, rte->ctecoltypmods, lcc, rte->ctecolcollations)
                {
                    Oid         coltype = lfirst_oid(lct);
                    int32       coltypmod = lfirst_int(lcm);
+                   Oid         colcoll = lfirst_oid(lcc);
 
                    varattno++;
 
@@ -1776,7 +1789,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
                        Var        *varnode;
 
                        varnode = makeVar(rtindex, varattno,
-                                         coltype, coltypmod,
+                                         coltype, coltypmod, colcoll,
                                          sublevels_up);
                        *colvars = lappend(*colvars, varnode);
                    }
@@ -1857,7 +1870,7 @@ expandTupleDesc(TupleDesc tupdesc, Alias *eref,
            Var        *varnode;
 
            varnode = makeVar(rtindex, attr->attnum,
-                             attr->atttypid, attr->atttypmod,
+                             attr->atttypid, attr->atttypmod, attr->attcollation,
                              sublevels_up);
            varnode->location = location;
 
@@ -1968,7 +1981,7 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
  */
 void
 get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
-                      Oid *vartype, int32 *vartypmod)
+                      Oid *vartype, int32 *vartypmod, Oid *varcollid)
 {
    switch (rte->rtekind)
    {
@@ -1998,6 +2011,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
                           get_rel_name(rte->relid))));
                *vartype = att_tup->atttypid;
                *vartypmod = att_tup->atttypmod;
+               *varcollid = att_tup->attcollation;
                ReleaseSysCache(tp);
            }
            break;
@@ -2012,6 +2026,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
                         rte->eref->aliasname, attnum);
                *vartype = exprType((Node *) te->expr);
                *vartypmod = exprTypmod((Node *) te->expr);
+               *varcollid = exprCollation((Node *) te->expr);
            }
            break;
        case RTE_FUNCTION:
@@ -2053,17 +2068,20 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
                                        rte->eref->aliasname)));
                    *vartype = att_tup->atttypid;
                    *vartypmod = att_tup->atttypmod;
+                   *varcollid = att_tup->attcollation;
                }
                else if (functypclass == TYPEFUNC_SCALAR)
                {
                    /* Base data type, i.e. scalar */
                    *vartype = funcrettype;
                    *vartypmod = -1;
+                   *varcollid = exprCollation(rte->funcexpr);
                }
                else if (functypclass == TYPEFUNC_RECORD)
                {
                    *vartype = list_nth_oid(rte->funccoltypes, attnum - 1);
                    *vartypmod = list_nth_int(rte->funccoltypmods, attnum - 1);
+                   *varcollid = list_nth_oid(rte->funccolcollations, attnum - 1);
                }
                else
                {
@@ -2084,6 +2102,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
                col = (Node *) list_nth(collist, attnum - 1);
                *vartype = exprType(col);
                *vartypmod = exprTypmod(col);
+               *varcollid = exprCollation(col);
            }
            break;
        case RTE_JOIN:
@@ -2097,6 +2116,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
                aliasvar = (Node *) list_nth(rte->joinaliasvars, attnum - 1);
                *vartype = exprType(aliasvar);
                *vartypmod = exprTypmod(aliasvar);
+               *varcollid = exprCollation(aliasvar);
            }
            break;
        case RTE_CTE:
@@ -2105,6 +2125,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
                Assert(attnum > 0 && attnum <= list_length(rte->ctecoltypes));
                *vartype = list_nth_oid(rte->ctecoltypes, attnum - 1);
                *vartypmod = list_nth_int(rte->ctecoltypmods, attnum - 1);
+               *varcollid = list_nth_oid(rte->ctecolcollations, attnum - 1);
            }
            break;
        default:
index 7d77e0b63f14451938e482a9615f5ad7d1905d8f..a0761da875be78c7387bc0053e6b68f2852eab9b 100644 (file)
@@ -374,6 +374,7 @@ transformAssignedExpr(ParseState *pstate,
    Oid         type_id;        /* type of value provided */
    Oid         attrtype;       /* type of target column */
    int32       attrtypmod;
+   Oid         attrcollation;
    Relation    rd = pstate->p_target_relation;
 
    Assert(rd != NULL);
@@ -385,6 +386,7 @@ transformAssignedExpr(ParseState *pstate,
                 parser_errposition(pstate, location)));
    attrtype = attnumTypeId(rd, attrno);
    attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod;
+   attrcollation = rd->rd_att->attrs[attrno - 1]->attcollation;
 
    /*
     * If the expression is a DEFAULT placeholder, insert the attribute's
@@ -400,6 +402,7 @@ transformAssignedExpr(ParseState *pstate,
 
        def->typeId = attrtype;
        def->typeMod = attrtypmod;
+       def->collid = attrcollation;
        if (indirection)
        {
            if (IsA(linitial(indirection), A_Indices))
@@ -786,6 +789,7 @@ transformAssignmentSubscripts(ParseState *pstate,
                                               arrayType,
                                               elementTypeId,
                                               arrayTypMod,
+                                              InvalidOid,
                                               subscripts,
                                               rhs);
 
@@ -1267,6 +1271,7 @@ ExpandRowReference(ParseState *pstate, Node *expr,
        fselect->fieldnum = i + 1;
        fselect->resulttype = att->atttypid;
        fselect->resulttypmod = att->atttypmod;
+       fselect->resultcollation = att->attcollation;
 
        if (targetlist)
        {
@@ -1338,6 +1343,8 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup)
                               exprType(varnode),
                               exprTypmod(varnode),
                               0);
+           TupleDescInitEntryCollation(tupleDesc, i,
+                                       exprCollation(varnode));
            i++;
        }
        Assert(lname == NULL && lvar == NULL);  /* lists same length? */
@@ -1583,6 +1590,8 @@ FigureColnameInternal(Node *node, char **name)
                }
            }
            break;
+       case T_CollateClause:
+           return FigureColnameInternal((Node *) ((CollateClause *) node)->arg, name);
        case T_CaseExpr:
            strength = FigureColnameInternal((Node *) ((CaseExpr *) node)->defresult,
                                             name);
index 0b601c8b75df9ef9228379680b2cc596037d1daa..02c1c68827ff37eddd621803e2c99048780dcb77 100644 (file)
@@ -29,6 +29,8 @@
 
 static int32 typenameTypeMod(ParseState *pstate, const TypeName *typeName,
                Type typ);
+static Oid typenameCollation(ParseState *pstate, const TypeName *typeName,
+               Type typ);
 
 
 /*
@@ -36,7 +38,8 @@ static int32 typenameTypeMod(ParseState *pstate, const TypeName *typeName,
  *     Given a TypeName object, lookup the pg_type syscache entry of the type.
  *     Returns NULL if no such type can be found.  If the type is found,
  *     the typmod value represented in the TypeName struct is computed and
- *     stored into *typmod_p.
+ *     stored into *typmod_p, and the collation is looked up and stored into
+ *      *colloid_p.
  *
  * NB: on success, the caller must ReleaseSysCache the type tuple when done
  * with it.
@@ -51,15 +54,18 @@ static int32 typenameTypeMod(ParseState *pstate, const TypeName *typeName,
  * found but is a shell, and there is typmod decoration, an error will be
  * thrown --- this is intentional.
  *
+ * colloid_p can also be null.
+ *
  * pstate is only used for error location info, and may be NULL.
  */
 Type
 LookupTypeName(ParseState *pstate, const TypeName *typeName,
-              int32 *typmod_p)
+              int32 *typmod_p, Oid *collid_p)
 {
    Oid         typoid;
    HeapTuple   tup;
    int32       typmod;
+   Oid         collid;
 
    if (typeName->names == NIL)
    {
@@ -174,6 +180,11 @@ LookupTypeName(ParseState *pstate, const TypeName *typeName,
    if (typmod_p)
        *typmod_p = typmod;
 
+   collid = typenameCollation(pstate, typeName, (Type) tup);
+
+   if (collid_p)
+       *collid_p = collid;
+
    return (Type) tup;
 }
 
@@ -185,11 +196,11 @@ LookupTypeName(ParseState *pstate, const TypeName *typeName,
  * Callers of this can therefore assume the result is a fully valid type.
  */
 Type
-typenameType(ParseState *pstate, const TypeName *typeName, int32 *typmod_p)
+typenameType(ParseState *pstate, const TypeName *typeName, int32 *typmod_p, Oid *collid_p)
 {
    Type        tup;
 
-   tup = LookupTypeName(pstate, typeName, typmod_p);
+   tup = LookupTypeName(pstate, typeName, typmod_p, collid_p);
    if (tup == NULL)
        ereport(ERROR,
                (errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -217,7 +228,7 @@ typenameTypeId(ParseState *pstate, const TypeName *typeName)
    Oid         typoid;
    Type        tup;
 
-   tup = typenameType(pstate, typeName, NULL);
+   tup = typenameType(pstate, typeName, NULL, NULL);
    typoid = HeapTupleGetOid(tup);
    ReleaseSysCache(tup);
 
@@ -236,7 +247,25 @@ typenameTypeIdAndMod(ParseState *pstate, const TypeName *typeName,
 {
    Type        tup;
 
-   tup = typenameType(pstate, typeName, typmod_p);
+   tup = typenameType(pstate, typeName, typmod_p, NULL);
+   *typeid_p = HeapTupleGetOid(tup);
+   ReleaseSysCache(tup);
+}
+
+/*
+ * typenameTypeIdModColl - given a TypeName, return the type's OID,
+ * typmod, and collation
+ *
+ * This is equivalent to typenameType, but we only hand back the type OID,
+ * typmod, and collation, not the syscache entry.
+ */
+void
+typenameTypeIdModColl(ParseState *pstate, const TypeName *typeName,
+                     Oid *typeid_p, int32 *typmod_p, Oid *collid_p)
+{
+   Type        tup;
+
+   tup = typenameType(pstate, typeName, typmod_p, collid_p);
    *typeid_p = HeapTupleGetOid(tup);
    ReleaseSysCache(tup);
 }
@@ -350,6 +379,62 @@ typenameTypeMod(ParseState *pstate, const TypeName *typeName, Type typ)
    return result;
 }
 
+/*
+ * typenameCollation - given a TypeName, return the collation OID
+ *
+ * This will throw an error if the TypeName includes a collation but
+ * the data type does not support collations.
+ *
+ * The actual type OID represented by the TypeName must already have been
+ * looked up, and is passed as "typ".
+ *
+ * pstate is only used for error location info, and may be NULL.
+ */
+static Oid
+typenameCollation(ParseState *pstate, const TypeName *typeName, Type typ)
+{
+   Oid typcollation = ((Form_pg_type) GETSTRUCT(typ))->typcollation;
+
+   /* return prespecified collation OID if no collation name specified */
+   if (typeName->collnames == NIL)
+   {
+       if (typeName->collOid == InvalidOid)
+           return typcollation;
+       else
+           return typeName->collOid;
+   }
+
+   if (!OidIsValid(typcollation))
+       ereport(ERROR,
+               (errcode(ERRCODE_SYNTAX_ERROR),
+                errmsg("collations are not supported by type %s",
+                       format_type_be(HeapTupleGetOid(typ))),
+                parser_errposition(pstate, typeName->location)));
+
+   return LookupCollation(pstate, typeName->collnames, typeName->location);
+}
+
+/*
+ * LookupCollation
+ *
+ * Look up collation by name, return OID, with support for error
+ * location.
+ */
+Oid
+LookupCollation(ParseState *pstate, List *collnames, int location)
+{
+   Oid         colloid;
+   ParseCallbackState pcbstate;
+
+   setup_parser_errposition_callback(&pcbstate, pstate, location);
+
+   colloid = get_collation_oid(collnames, false);
+
+   cancel_parser_errposition_callback(&pcbstate);
+
+   return colloid;
+}
+
 /*
  * appendTypeNameToBuffer
  *     Append a string representing the name of a TypeName to a StringInfo.
index e0ab88232b18f619c34c18d98d55ca021597277e..61ce840a5e59395cbfe1e72d4b04235cd6f230e5 100644 (file)
@@ -627,7 +627,8 @@ transformInhRelation(CreateStmtContext *cxt, InhRelation *inhRelation)
        def = makeNode(ColumnDef);
        def->colname = pstrdup(attributeName);
        def->typeName = makeTypeNameFromOid(attribute->atttypid,
-                                           attribute->atttypmod);
+                                           attribute->atttypmod,
+                                           attribute->attcollation);
        def->inhcount = 0;
        def->is_local = true;
        def->is_not_null = attribute->attnotnull;
@@ -821,7 +822,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
 
    AssertArg(ofTypename);
 
-   tuple = typenameType(NULL, ofTypename, NULL);
+   tuple = typenameType(NULL, ofTypename, NULL, NULL);
    typ = (Form_pg_type) GETSTRUCT(tuple);
    ofTypeId = HeapTupleGetOid(tuple);
    ofTypename->typeOid = ofTypeId;     /* cached for later */
@@ -842,7 +843,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
            continue;
 
        n->colname = pstrdup(NameStr(attr->attname));
-       n->typeName = makeTypeNameFromOid(attr->atttypid, attr->atttypmod);
+       n->typeName = makeTypeNameFromOid(attr->atttypid, attr->atttypmod, attr->attcollation);
        n->constraints = NULL;
        n->is_local = true;
        n->is_from_type = true;
@@ -2446,7 +2447,7 @@ transformColumnType(CreateStmtContext *cxt, ColumnDef *column)
    /*
     * All we really need to do here is verify that the type is valid.
     */
-   Type        ctype = typenameType(cxt->pstate, column->typeName, NULL);
+   Type        ctype = typenameType(cxt->pstate, column->typeName, NULL, NULL);
 
    ReleaseSysCache(ctype);
 }
index c12d4cb6a3ff21c14ff5b8116bc55fc9adf2d334..c6491e15194d53d57b169a24cc13e59edad458f0 100644 (file)
@@ -747,6 +747,7 @@ rewriteTargetListIU(Query *parsetree, Relation target_relation,
                                        attrno,
                                        att_tup->atttypid,
                                        att_tup->atttypmod,
+                                       att_tup->attcollation,
                                        0);
 
            new_tle = makeTargetEntry((Expr *) new_expr,
@@ -1127,6 +1128,7 @@ rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte,
                      SelfItemPointerAttributeNumber,
                      TIDOID,
                      -1,
+                     InvalidOid,
                      0);
 
        attrname = "ctid";
index 2b6a6cb946f73c37c7dc2ec1df1140759146be80..e42c4734c74ffb81f7a007cf9aea3bbe48d0a4b6 100644 (file)
@@ -13,6 +13,7 @@
  */
 #include "postgres.h"
 
+#include "catalog/pg_collation.h"
 #include "storage/fd.h"
 #include "tsearch/ts_locale.h"
 #include "tsearch/ts_public.h"
@@ -27,11 +28,12 @@ t_isdigit(const char *ptr)
 {
    int         clen = pg_mblen(ptr);
    wchar_t     character[2];
+   Oid         collation = DEFAULT_COLLATION_OID; /*TODO*/
 
-   if (clen == 1 || lc_ctype_is_c())
+   if (clen == 1 || lc_ctype_is_c(collation))
        return isdigit(TOUCHAR(ptr));
 
-   char2wchar(character, 2, ptr, clen);
+   char2wchar(character, 2, ptr, clen, collation);
 
    return iswdigit((wint_t) character[0]);
 }
@@ -41,11 +43,12 @@ t_isspace(const char *ptr)
 {
    int         clen = pg_mblen(ptr);
    wchar_t     character[2];
+   Oid         collation = DEFAULT_COLLATION_OID; /*TODO*/
 
-   if (clen == 1 || lc_ctype_is_c())
+   if (clen == 1 || lc_ctype_is_c(collation))
        return isspace(TOUCHAR(ptr));
 
-   char2wchar(character, 2, ptr, clen);
+   char2wchar(character, 2, ptr, clen, collation);
 
    return iswspace((wint_t) character[0]);
 }
@@ -55,11 +58,12 @@ t_isalpha(const char *ptr)
 {
    int         clen = pg_mblen(ptr);
    wchar_t     character[2];
+   Oid         collation = DEFAULT_COLLATION_OID; /*TODO*/
 
-   if (clen == 1 || lc_ctype_is_c())
+   if (clen == 1 || lc_ctype_is_c(collation))
        return isalpha(TOUCHAR(ptr));
 
-   char2wchar(character, 2, ptr, clen);
+   char2wchar(character, 2, ptr, clen, collation);
 
    return iswalpha((wint_t) character[0]);
 }
@@ -69,11 +73,12 @@ t_isprint(const char *ptr)
 {
    int         clen = pg_mblen(ptr);
    wchar_t     character[2];
+   Oid         collation = DEFAULT_COLLATION_OID; /*TODO*/
 
-   if (clen == 1 || lc_ctype_is_c())
+   if (clen == 1 || lc_ctype_is_c(collation))
        return isprint(TOUCHAR(ptr));
 
-   char2wchar(character, 2, ptr, clen);
+   char2wchar(character, 2, ptr, clen, collation);
 
    return iswprint((wint_t) character[0]);
 }
@@ -238,6 +243,7 @@ char *
 lowerstr_with_len(const char *str, int len)
 {
    char       *out;
+   Oid         collation = DEFAULT_COLLATION_OID; /*TODO*/
 
    if (len == 0)
        return pstrdup("");
@@ -250,7 +256,7 @@ lowerstr_with_len(const char *str, int len)
     * Also, for a C locale there is no need to process as multibyte. From
     * backend/utils/adt/oracle_compat.c Teodor
     */
-   if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c())
+   if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c(collation))
    {
        wchar_t    *wstr,
                   *wptr;
@@ -263,7 +269,7 @@ lowerstr_with_len(const char *str, int len)
         */
        wptr = wstr = (wchar_t *) palloc(sizeof(wchar_t) * (len + 1));
 
-       wlen = char2wchar(wstr, len + 1, str, len);
+       wlen = char2wchar(wstr, len + 1, str, len, collation);
        Assert(wlen <= len);
 
        while (*wptr)
@@ -278,7 +284,7 @@ lowerstr_with_len(const char *str, int len)
        len = pg_database_encoding_max_length() * wlen + 1;
        out = (char *) palloc(len);
 
-       wlen = wchar2char(out, wstr, len);
+       wlen = wchar2char(out, wstr, len, collation);
 
        pfree(wstr);
 
index 40eca64895ebe84a5b7d61c922ddcb18ccd899e1..65d0632f9a1307509d70fe871e7952ad715e02ce 100644 (file)
@@ -14,6 +14,7 @@
 
 #include "postgres.h"
 
+#include "catalog/pg_collation.h"
 #include "commands/defrem.h"
 #include "tsearch/ts_locale.h"
 #include "tsearch/ts_public.h"
@@ -286,6 +287,7 @@ static TParser *
 TParserInit(char *str, int len)
 {
    TParser    *prs = (TParser *) palloc0(sizeof(TParser));
+   Oid         collation = DEFAULT_COLLATION_OID; /*TODO*/
 
    prs->charmaxlen = pg_database_encoding_max_length();
    prs->str = str;
@@ -299,7 +301,7 @@ TParserInit(char *str, int len)
    if (prs->charmaxlen > 1)
    {
        prs->usewide = true;
-       if ( lc_ctype_is_c() )
+       if ( lc_ctype_is_c(collation) )
        {
            /*
             * char2wchar doesn't work for C-locale and
@@ -311,7 +313,7 @@ TParserInit(char *str, int len)
        else
        {
            prs->wstr = (wchar_t *) palloc(sizeof(wchar_t) * (prs->lenstr + 1));
-           char2wchar(prs->wstr, prs->lenstr + 1, prs->str, prs->lenstr);
+           char2wchar(prs->wstr, prs->lenstr + 1, prs->str, prs->lenstr, collation);
        }
    }
    else
index 931c6953cb3cb9b8961aebe5f7d58b2644a076e1..4ac9830878923ffeabf58247b2aca6266fb89fbc 100644 (file)
@@ -3307,6 +3307,7 @@ array_cmp(FunctionCallInfo fcinfo)
 {
    ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
    ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
+   Oid         collation = PG_GET_COLLATION();
    int         ndims1 = ARR_NDIM(array1);
    int         ndims2 = ARR_NDIM(array2);
    int        *dims1 = ARR_DIMS(array1);
@@ -3341,7 +3342,8 @@ array_cmp(FunctionCallInfo fcinfo)
     */
    typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
    if (typentry == NULL ||
-       typentry->type_id != element_type)
+       typentry->type_id != element_type ||
+       typentry->cmp_proc_finfo.fn_collation != collation)
    {
        typentry = lookup_type_cache(element_type,
                                     TYPECACHE_CMP_PROC_FINFO);
@@ -3351,6 +3353,7 @@ array_cmp(FunctionCallInfo fcinfo)
               errmsg("could not identify a comparison function for type %s",
                      format_type_be(element_type))));
        fcinfo->flinfo->fn_extra = (void *) typentry;
+       typentry->cmp_proc_finfo.fn_collation = collation;
    }
    typlen = typentry->typlen;
    typbyval = typentry->typbyval;
index b56bb74bdc6db437f8155ba10ca5e5360bb23ff4..f85e0bbd00ca7bd9c4992cfbc8379174e1939a75 100644 (file)
@@ -18,6 +18,7 @@
 #include 
 
 #include "catalog/namespace.h"
+#include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
@@ -28,7 +29,8 @@
 #define MAX_INT32_LEN 11
 
 static char *format_type_internal(Oid type_oid, int32 typemod,
-                    bool typemod_given, bool allow_invalid);
+                    bool typemod_given, bool allow_invalid,
+                                 Oid collation_oid);
 static char *printTypmod(const char *typname, int32 typmod, Oid typmodout);
 static char *
 psnprintf(size_t len, const char *fmt,...)
@@ -67,6 +69,7 @@ format_type(PG_FUNCTION_ARGS)
 {
    Oid         type_oid;
    int32       typemod;
+   Oid         collation_oid;
    char       *result;
 
    /* Since this function is not strict, we must test for null args */
@@ -74,13 +77,14 @@ format_type(PG_FUNCTION_ARGS)
        PG_RETURN_NULL();
 
    type_oid = PG_GETARG_OID(0);
+   collation_oid = PG_ARGISNULL(2) ? InvalidOid : PG_GETARG_OID(2);
 
    if (PG_ARGISNULL(1))
-       result = format_type_internal(type_oid, -1, false, true);
+       result = format_type_internal(type_oid, -1, false, true, collation_oid);
    else
    {
        typemod = PG_GETARG_INT32(1);
-       result = format_type_internal(type_oid, typemod, true, true);
+       result = format_type_internal(type_oid, typemod, true, true, collation_oid);
    }
 
    PG_RETURN_TEXT_P(cstring_to_text(result));
@@ -95,7 +99,7 @@ format_type(PG_FUNCTION_ARGS)
 char *
 format_type_be(Oid type_oid)
 {
-   return format_type_internal(type_oid, -1, false, false);
+   return format_type_internal(type_oid, -1, false, false, InvalidOid);
 }
 
 /*
@@ -104,14 +108,15 @@ format_type_be(Oid type_oid)
 char *
 format_type_with_typemod(Oid type_oid, int32 typemod)
 {
-   return format_type_internal(type_oid, typemod, true, false);
+   return format_type_internal(type_oid, typemod, true, false, InvalidOid);
 }
 
 
 
 static char *
 format_type_internal(Oid type_oid, int32 typemod,
-                    bool typemod_given, bool allow_invalid)
+                    bool typemod_given, bool allow_invalid,
+                    Oid collation_oid)
 {
    bool        with_typemod = typemod_given && (typemod >= 0);
    HeapTuple   tuple;
@@ -317,6 +322,12 @@ format_type_internal(Oid type_oid, int32 typemod,
 
    ReleaseSysCache(tuple);
 
+   if (collation_oid && collation_oid != DEFAULT_COLLATION_OID)
+   {
+       char *collstr = generate_collation_name(collation_oid);
+       buf = psnprintf(strlen(buf) + 10 + strlen(collstr), "%s COLLATE %s", buf, collstr);
+   }
+
    return buf;
 }
 
@@ -420,7 +431,7 @@ oidvectortypes(PG_FUNCTION_ARGS)
    for (num = 0; num < numargs; num++)
    {
        char       *typename = format_type_internal(oidArray->values[num], -1,
-                                                   false, true);
+                                                   false, true, InvalidOid);
        size_t      slen = strlen(typename);
 
        if (left < (slen + 2))
index 4855bac41d8daaf8c2582173c5331750edff6233..f90d36d24cc6d0dbc7d95efd93a8f4ff2f9e653e 100644 (file)
@@ -82,6 +82,7 @@
 #include 
 #endif
 
+#include "catalog/pg_collation.h"
 #include "mb/pg_wchar.h"
 #include "utils/builtins.h"
 #include "utils/date.h"
@@ -953,7 +954,7 @@ static void parse_format(FormatNode *node, char *str, const KeyWord *kw,
             KeySuffix *suf, const int *index, int ver, NUMDesc *Num);
 
 static void DCH_to_char(FormatNode *node, bool is_interval,
-           TmToChar *in, char *out);
+           TmToChar *in, char *out, Oid collid);
 static void DCH_from_char(FormatNode *node, char *in, TmFromChar *out);
 
 #ifdef DEBUG_TO_FROM_CHAR
@@ -981,7 +982,7 @@ static char *get_last_relevant_decnum(char *num);
 static void NUM_numpart_from_char(NUMProc *Np, int id, int plen);
 static void NUM_numpart_to_char(NUMProc *Np, int id);
 static char *NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
-             int plen, int sign, bool is_to_char);
+             int plen, int sign, bool is_to_char, Oid collid);
 static DCHCacheEntry *DCH_cache_search(char *str);
 static DCHCacheEntry *DCH_cache_getnew(char *str);
 
@@ -1470,15 +1471,19 @@ str_numth(char *dest, char *num, int type)
  * to this function.  The result is a palloc'd, null-terminated string.
  */
 char *
-str_tolower(const char *buff, size_t nbytes)
+str_tolower(const char *buff, size_t nbytes, Oid collid)
 {
    char       *result;
+   pg_locale_t mylocale = 0;
 
    if (!buff)
        return NULL;
 
+   if (collid != DEFAULT_COLLATION_OID)
+       mylocale = pg_newlocale_from_collation(collid);
+
 #ifdef USE_WIDE_UPPER_LOWER
-   if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c())
+   if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c(collid))
    {
        wchar_t    *workspace;
        size_t      curr_char;
@@ -1493,16 +1498,21 @@ str_tolower(const char *buff, size_t nbytes)
        /* Output workspace cannot have more codes than input bytes */
        workspace = (wchar_t *) palloc((nbytes + 1) * sizeof(wchar_t));
 
-       char2wchar(workspace, nbytes + 1, buff, nbytes);
+       char2wchar(workspace, nbytes + 1, buff, nbytes, collid);
 
        for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
+#ifdef HAVE_LOCALE_T
+           if (mylocale)
+               workspace[curr_char] = towlower_l(workspace[curr_char], mylocale);
+           else
+#endif
            workspace[curr_char] = towlower(workspace[curr_char]);
 
        /* Make result large enough; case change might change number of bytes */
        result_size = curr_char * pg_database_encoding_max_length() + 1;
        result = palloc(result_size);
 
-       wchar2char(result, workspace, result_size);
+       wchar2char(result, workspace, result_size, collid);
        pfree(workspace);
    }
    else
@@ -1526,15 +1536,19 @@ str_tolower(const char *buff, size_t nbytes)
  * to this function.  The result is a palloc'd, null-terminated string.
  */
 char *
-str_toupper(const char *buff, size_t nbytes)
+str_toupper(const char *buff, size_t nbytes, Oid collid)
 {
    char       *result;
+   pg_locale_t mylocale = 0;
 
    if (!buff)
        return NULL;
 
+   if (collid != DEFAULT_COLLATION_OID)
+       mylocale = pg_newlocale_from_collation(collid);
+
 #ifdef USE_WIDE_UPPER_LOWER
-   if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c())
+   if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c(collid))
    {
        wchar_t    *workspace;
        size_t      curr_char;
@@ -1549,16 +1563,21 @@ str_toupper(const char *buff, size_t nbytes)
        /* Output workspace cannot have more codes than input bytes */
        workspace = (wchar_t *) palloc((nbytes + 1) * sizeof(wchar_t));
 
-       char2wchar(workspace, nbytes + 1, buff, nbytes);
+       char2wchar(workspace, nbytes + 1, buff, nbytes, collid);
 
        for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
+#ifdef HAVE_LOCALE_T
+           if (mylocale)
+               workspace[curr_char] = towupper_l(workspace[curr_char], mylocale);
+           else
+#endif
            workspace[curr_char] = towupper(workspace[curr_char]);
 
        /* Make result large enough; case change might change number of bytes */
        result_size = curr_char * pg_database_encoding_max_length() + 1;
        result = palloc(result_size);
 
-       wchar2char(result, workspace, result_size);
+       wchar2char(result, workspace, result_size, collid);
        pfree(workspace);
    }
    else
@@ -1582,16 +1601,20 @@ str_toupper(const char *buff, size_t nbytes)
  * to this function.  The result is a palloc'd, null-terminated string.
  */
 char *
-str_initcap(const char *buff, size_t nbytes)
+str_initcap(const char *buff, size_t nbytes, Oid collid)
 {
    char       *result;
    int         wasalnum = false;
+   pg_locale_t mylocale = 0;
 
    if (!buff)
        return NULL;
 
+   if (collid != DEFAULT_COLLATION_OID)
+       mylocale = pg_newlocale_from_collation(collid);
+
 #ifdef USE_WIDE_UPPER_LOWER
-   if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c())
+   if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c(collid))
    {
        wchar_t    *workspace;
        size_t      curr_char;
@@ -1606,22 +1629,35 @@ str_initcap(const char *buff, size_t nbytes)
        /* Output workspace cannot have more codes than input bytes */
        workspace = (wchar_t *) palloc((nbytes + 1) * sizeof(wchar_t));
 
-       char2wchar(workspace, nbytes + 1, buff, nbytes);
+       char2wchar(workspace, nbytes + 1, buff, nbytes, collid);
 
        for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
        {
-           if (wasalnum)
-               workspace[curr_char] = towlower(workspace[curr_char]);
+#ifdef HAVE_LOCALE_T
+           if (mylocale)
+           {
+               if (wasalnum)
+                   workspace[curr_char] = towlower_l(workspace[curr_char], mylocale);
+               else
+                   workspace[curr_char] = towupper_l(workspace[curr_char], mylocale);
+               wasalnum = iswalnum_l(workspace[curr_char], mylocale);
+           }
            else
-               workspace[curr_char] = towupper(workspace[curr_char]);
-           wasalnum = iswalnum(workspace[curr_char]);
+#endif
+           {
+               if (wasalnum)
+                   workspace[curr_char] = towlower(workspace[curr_char]);
+               else
+                   workspace[curr_char] = towupper(workspace[curr_char]);
+               wasalnum = iswalnum(workspace[curr_char]);
+           }
        }
 
        /* Make result large enough; case change might change number of bytes */
        result_size = curr_char * pg_database_encoding_max_length() + 1;
        result = palloc(result_size);
 
-       wchar2char(result, workspace, result_size);
+       wchar2char(result, workspace, result_size, collid);
        pfree(workspace);
    }
    else
@@ -1647,21 +1683,21 @@ str_initcap(const char *buff, size_t nbytes)
 /* convenience routines for when the input is null-terminated */
 
 static char *
-str_tolower_z(const char *buff)
+str_tolower_z(const char *buff, Oid collid)
 {
-   return str_tolower(buff, strlen(buff));
+   return str_tolower(buff, strlen(buff), collid);
 }
 
 static char *
-str_toupper_z(const char *buff)
+str_toupper_z(const char *buff, Oid collid)
 {
-   return str_toupper(buff, strlen(buff));
+   return str_toupper(buff, strlen(buff), collid);
 }
 
 static char *
-str_initcap_z(const char *buff)
+str_initcap_z(const char *buff, Oid collid)
 {
-   return str_initcap(buff, strlen(buff));
+   return str_initcap(buff, strlen(buff), collid);
 }
 
 
@@ -2039,7 +2075,7 @@ from_char_seq_search(int *dest, char **src, char **array, int type, int max,
  * ----------
  */
 static void
-DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
+DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid collid)
 {
    FormatNode *n;
    char       *s;
@@ -2151,7 +2187,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
                INVALID_FOR_INTERVAL;
                if (tmtcTzn(in))
                {
-                   char       *p = str_tolower_z(tmtcTzn(in));
+                   char       *p = str_tolower_z(tmtcTzn(in), collid);
 
                    strcpy(s, p);
                    pfree(p);
@@ -2195,10 +2231,10 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
                if (!tm->tm_mon)
                    break;
                if (S_TM(n->suffix))
-                   strcpy(s, str_toupper_z(localized_full_months[tm->tm_mon - 1]));
+                   strcpy(s, str_toupper_z(localized_full_months[tm->tm_mon - 1], collid));
                else
                    sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9,
-                           str_toupper_z(months_full[tm->tm_mon - 1]));
+                           str_toupper_z(months_full[tm->tm_mon - 1], collid));
                s += strlen(s);
                break;
            case DCH_Month:
@@ -2206,7 +2242,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
                if (!tm->tm_mon)
                    break;
                if (S_TM(n->suffix))
-                   strcpy(s, str_initcap_z(localized_full_months[tm->tm_mon - 1]));
+                   strcpy(s, str_initcap_z(localized_full_months[tm->tm_mon - 1], collid));
                else
                    sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, months_full[tm->tm_mon - 1]);
                s += strlen(s);
@@ -2216,7 +2252,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
                if (!tm->tm_mon)
                    break;
                if (S_TM(n->suffix))
-                   strcpy(s, str_tolower_z(localized_full_months[tm->tm_mon - 1]));
+                   strcpy(s, str_tolower_z(localized_full_months[tm->tm_mon - 1], collid));
                else
                {
                    sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, months_full[tm->tm_mon - 1]);
@@ -2229,9 +2265,9 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
                if (!tm->tm_mon)
                    break;
                if (S_TM(n->suffix))
-                   strcpy(s, str_toupper_z(localized_abbrev_months[tm->tm_mon - 1]));
+                   strcpy(s, str_toupper_z(localized_abbrev_months[tm->tm_mon - 1], collid));
                else
-                   strcpy(s, str_toupper_z(months[tm->tm_mon - 1]));
+                   strcpy(s, str_toupper_z(months[tm->tm_mon - 1], collid));
                s += strlen(s);
                break;
            case DCH_Mon:
@@ -2239,7 +2275,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
                if (!tm->tm_mon)
                    break;
                if (S_TM(n->suffix))
-                   strcpy(s, str_initcap_z(localized_abbrev_months[tm->tm_mon - 1]));
+                   strcpy(s, str_initcap_z(localized_abbrev_months[tm->tm_mon - 1], collid));
                else
                    strcpy(s, months[tm->tm_mon - 1]);
                s += strlen(s);
@@ -2249,7 +2285,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
                if (!tm->tm_mon)
                    break;
                if (S_TM(n->suffix))
-                   strcpy(s, str_tolower_z(localized_abbrev_months[tm->tm_mon - 1]));
+                   strcpy(s, str_tolower_z(localized_abbrev_months[tm->tm_mon - 1], collid));
                else
                {
                    strcpy(s, months[tm->tm_mon - 1]);
@@ -2266,16 +2302,16 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
            case DCH_DAY:
                INVALID_FOR_INTERVAL;
                if (S_TM(n->suffix))
-                   strcpy(s, str_toupper_z(localized_full_days[tm->tm_wday]));
+                   strcpy(s, str_toupper_z(localized_full_days[tm->tm_wday], collid));
                else
                    sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9,
-                           str_toupper_z(days[tm->tm_wday]));
+                           str_toupper_z(days[tm->tm_wday], collid));
                s += strlen(s);
                break;
            case DCH_Day:
                INVALID_FOR_INTERVAL;
                if (S_TM(n->suffix))
-                   strcpy(s, str_initcap_z(localized_full_days[tm->tm_wday]));
+                   strcpy(s, str_initcap_z(localized_full_days[tm->tm_wday], collid));
                else
                    sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, days[tm->tm_wday]);
                s += strlen(s);
@@ -2283,7 +2319,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
            case DCH_day:
                INVALID_FOR_INTERVAL;
                if (S_TM(n->suffix))
-                   strcpy(s, str_tolower_z(localized_full_days[tm->tm_wday]));
+                   strcpy(s, str_tolower_z(localized_full_days[tm->tm_wday], collid));
                else
                {
                    sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, days[tm->tm_wday]);
@@ -2294,15 +2330,15 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
            case DCH_DY:
                INVALID_FOR_INTERVAL;
                if (S_TM(n->suffix))
-                   strcpy(s, str_toupper_z(localized_abbrev_days[tm->tm_wday]));
+                   strcpy(s, str_toupper_z(localized_abbrev_days[tm->tm_wday], collid));
                else
-                   strcpy(s, str_toupper_z(days_short[tm->tm_wday]));
+                   strcpy(s, str_toupper_z(days_short[tm->tm_wday], collid));
                s += strlen(s);
                break;
            case DCH_Dy:
                INVALID_FOR_INTERVAL;
                if (S_TM(n->suffix))
-                   strcpy(s, str_initcap_z(localized_abbrev_days[tm->tm_wday]));
+                   strcpy(s, str_initcap_z(localized_abbrev_days[tm->tm_wday], collid));
                else
                    strcpy(s, days_short[tm->tm_wday]);
                s += strlen(s);
@@ -2310,7 +2346,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
            case DCH_dy:
                INVALID_FOR_INTERVAL;
                if (S_TM(n->suffix))
-                   strcpy(s, str_tolower_z(localized_abbrev_days[tm->tm_wday]));
+                   strcpy(s, str_tolower_z(localized_abbrev_days[tm->tm_wday], collid));
                else
                {
                    strcpy(s, days_short[tm->tm_wday]);
@@ -2846,7 +2882,7 @@ DCH_cache_search(char *str)
  * for formatting.
  */
 static text *
-datetime_to_char_body(TmToChar *tmtc, text *fmt, bool is_interval)
+datetime_to_char_body(TmToChar *tmtc, text *fmt, bool is_interval, Oid collid)
 {
    FormatNode *format;
    char       *fmt_str,
@@ -2912,7 +2948,7 @@ datetime_to_char_body(TmToChar *tmtc, text *fmt, bool is_interval)
    }
 
    /* The real work is here */
-   DCH_to_char(format, is_interval, tmtc, result);
+   DCH_to_char(format, is_interval, tmtc, result, collid);
 
    if (!incache)
        pfree(format);
@@ -2959,7 +2995,7 @@ timestamp_to_char(PG_FUNCTION_ARGS)
    tm->tm_wday = (thisdate + 1) % 7;
    tm->tm_yday = thisdate - date2j(tm->tm_year, 1, 1) + 1;
 
-   if (!(res = datetime_to_char_body(&tmtc, fmt, false)))
+   if (!(res = datetime_to_char_body(&tmtc, fmt, false, PG_GET_COLLATION())))
        PG_RETURN_NULL();
 
    PG_RETURN_TEXT_P(res);
@@ -2991,7 +3027,7 @@ timestamptz_to_char(PG_FUNCTION_ARGS)
    tm->tm_wday = (thisdate + 1) % 7;
    tm->tm_yday = thisdate - date2j(tm->tm_year, 1, 1) + 1;
 
-   if (!(res = datetime_to_char_body(&tmtc, fmt, false)))
+   if (!(res = datetime_to_char_body(&tmtc, fmt, false, PG_GET_COLLATION())))
        PG_RETURN_NULL();
 
    PG_RETURN_TEXT_P(res);
@@ -3023,7 +3059,7 @@ interval_to_char(PG_FUNCTION_ARGS)
    /* wday is meaningless, yday approximates the total span in days */
    tm->tm_yday = (tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon) * DAYS_PER_MONTH + tm->tm_mday;
 
-   if (!(res = datetime_to_char_body(&tmtc, fmt, true)))
+   if (!(res = datetime_to_char_body(&tmtc, fmt, true, PG_GET_COLLATION())))
        PG_RETURN_NULL();
 
    PG_RETURN_TEXT_P(res);
@@ -4123,7 +4159,7 @@ NUM_numpart_to_char(NUMProc *Np, int id)
  */
 static char *
 NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
-             int plen, int sign, bool is_to_char)
+             int plen, int sign, bool is_to_char, Oid collid)
 {
    FormatNode *n;
    NUMProc     _Np,
@@ -4403,12 +4439,12 @@ NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
                case NUM_rn:
                    if (IS_FILLMODE(Np->Num))
                    {
-                       strcpy(Np->inout_p, str_tolower_z(Np->number_p));
+                       strcpy(Np->inout_p, str_tolower_z(Np->number_p, collid));
                        Np->inout_p += strlen(Np->inout_p) - 1;
                    }
                    else
                    {
-                       sprintf(Np->inout_p, "%15s", str_tolower_z(Np->number_p));
+                       sprintf(Np->inout_p, "%15s", str_tolower_z(Np->number_p, collid));
                        Np->inout_p += strlen(Np->inout_p) - 1;
                    }
                    break;
@@ -4541,7 +4577,7 @@ do { \
  */
 #define NUM_TOCHAR_finish \
 do { \
-   NUM_processor(format, &Num, VARDATA(result), numstr, plen, sign, true); \
+   NUM_processor(format, &Num, VARDATA(result), numstr, plen, sign, true, PG_GET_COLLATION()); \
                                    \
    if (shouldFree)                 \
        pfree(format);              \
@@ -4583,7 +4619,7 @@ numeric_to_number(PG_FUNCTION_ARGS)
    numstr = (char *) palloc((len * NUM_MAX_ITEM_SIZ) + 1);
 
    NUM_processor(format, &Num, VARDATA(value), numstr,
-                 VARSIZE(value) - VARHDRSZ, 0, false);
+                 VARSIZE(value) - VARHDRSZ, 0, false, PG_GET_COLLATION());
 
    scale = Num.post;
    precision = Max(0, Num.pre) + scale;
index 1e7a6f32ea39d44d19f77a1259d3405e0d6f9d34..1edbe88b74cb232e91f523337ad7ab491fbbb03f 100644 (file)
@@ -39,7 +39,7 @@ static int    UTF8_MatchText(char *t, int tlen, char *p, int plen);
 static int SB_IMatchText(char *t, int tlen, char *p, int plen);
 
 static int GenericMatchText(char *s, int slen, char *p, int plen);
-static int Generic_Text_IC_like(text *str, text *pat);
+static int Generic_Text_IC_like(text *str, text *pat, Oid collation);
 
 /*--------------------
  * Support routine for MatchText. Compares given multibyte streams
@@ -133,7 +133,7 @@ GenericMatchText(char *s, int slen, char *p, int plen)
 }
 
 static inline int
-Generic_Text_IC_like(text *str, text *pat)
+Generic_Text_IC_like(text *str, text *pat, Oid collation)
 {
    char       *s,
               *p;
@@ -149,10 +149,10 @@ Generic_Text_IC_like(text *str, text *pat)
    if (pg_database_encoding_max_length() > 1)
    {
        /* lower's result is never packed, so OK to use old macros here */
-       pat = DatumGetTextP(DirectFunctionCall1(lower, PointerGetDatum(pat)));
+       pat = DatumGetTextP(DirectFunctionCall1WithCollation(lower, collation, PointerGetDatum(pat)));
        p = VARDATA(pat);
        plen = (VARSIZE(pat) - VARHDRSZ);
-       str = DatumGetTextP(DirectFunctionCall1(lower, PointerGetDatum(str)));
+       str = DatumGetTextP(DirectFunctionCall1WithCollation(lower, collation, PointerGetDatum(str)));
        s = VARDATA(str);
        slen = (VARSIZE(str) - VARHDRSZ);
        if (GetDatabaseEncoding() == PG_UTF8)
@@ -314,7 +314,7 @@ nameiclike(PG_FUNCTION_ARGS)
 
    strtext = DatumGetTextP(DirectFunctionCall1(name_text,
                                                NameGetDatum(str)));
-   result = (Generic_Text_IC_like(strtext, pat) == LIKE_TRUE);
+   result = (Generic_Text_IC_like(strtext, pat, PG_GET_COLLATION()) == LIKE_TRUE);
 
    PG_RETURN_BOOL(result);
 }
@@ -329,7 +329,7 @@ nameicnlike(PG_FUNCTION_ARGS)
 
    strtext = DatumGetTextP(DirectFunctionCall1(name_text,
                                                NameGetDatum(str)));
-   result = (Generic_Text_IC_like(strtext, pat) != LIKE_TRUE);
+   result = (Generic_Text_IC_like(strtext, pat, PG_GET_COLLATION()) != LIKE_TRUE);
 
    PG_RETURN_BOOL(result);
 }
@@ -341,7 +341,7 @@ texticlike(PG_FUNCTION_ARGS)
    text       *pat = PG_GETARG_TEXT_PP(1);
    bool        result;
 
-   result = (Generic_Text_IC_like(str, pat) == LIKE_TRUE);
+   result = (Generic_Text_IC_like(str, pat, PG_GET_COLLATION()) == LIKE_TRUE);
 
    PG_RETURN_BOOL(result);
 }
@@ -353,7 +353,7 @@ texticnlike(PG_FUNCTION_ARGS)
    text       *pat = PG_GETARG_TEXT_PP(1);
    bool        result;
 
-   result = (Generic_Text_IC_like(str, pat) != LIKE_TRUE);
+   result = (Generic_Text_IC_like(str, pat, PG_GET_COLLATION()) != LIKE_TRUE);
 
    PG_RETURN_BOOL(result);
 }
index 65559dff5873eaccc7d68dbbf9dcadae388b83d8..4487b0a1816e07eee36bd390eda5f641ca037fe4 100644 (file)
@@ -47,7 +47,8 @@ lower(PG_FUNCTION_ARGS)
    text       *result;
 
    out_string = str_tolower(VARDATA_ANY(in_string),
-                            VARSIZE_ANY_EXHDR(in_string));
+                            VARSIZE_ANY_EXHDR(in_string),
+                            PG_GET_COLLATION());
    result = cstring_to_text(out_string);
    pfree(out_string);
 
@@ -77,7 +78,8 @@ upper(PG_FUNCTION_ARGS)
    text       *result;
 
    out_string = str_toupper(VARDATA_ANY(in_string),
-                            VARSIZE_ANY_EXHDR(in_string));
+                            VARSIZE_ANY_EXHDR(in_string),
+                            PG_GET_COLLATION());
    result = cstring_to_text(out_string);
    pfree(out_string);
 
@@ -110,7 +112,8 @@ initcap(PG_FUNCTION_ARGS)
    text       *result;
 
    out_string = str_initcap(VARDATA_ANY(in_string),
-                            VARSIZE_ANY_EXHDR(in_string));
+                            VARSIZE_ANY_EXHDR(in_string),
+                            PG_GET_COLLATION());
    result = cstring_to_text(out_string);
    pfree(out_string);
 
index f76305a219bf385ffb93eeea977aa85d22e86f98..2b9b321b2637cf2d845d9e1460df445b5be2baa8 100644 (file)
 #include 
 #include 
 
+#include "catalog/pg_collation.h"
 #include "catalog/pg_control.h"
 #include "mb/pg_wchar.h"
+#include "utils/hsearch.h"
 #include "utils/memutils.h"
 #include "utils/pg_locale.h"
+#include "utils/syscache.h"
 
 #ifdef WIN32
 #include 
@@ -100,6 +103,11 @@ static char lc_time_envbuf[LC_ENV_BUFSIZE];
 static char *IsoLocaleName(const char *);      /* MSVC specific */
 #endif
 
+static HTAB *locale_cness_cache = NULL;
+#ifdef HAVE_LOCALE_T
+static HTAB *locale_t_cache = NULL;
+#endif
+
 
 /*
  * pg_perm_setlocale
@@ -305,16 +313,90 @@ locale_messages_assign(const char *value, bool doit, GucSource source)
 
 
 /*
- * We'd like to cache whether LC_COLLATE is C (or POSIX), so we can
- * optimize a few code paths in various places.
+ * We'd like to cache whether LC_COLLATE or LC_CTYPE is C (or POSIX),
+ * so we can optimize a few code paths in various places.
+ *
+ * Note that some code relies on this not reporting false negatives
+ * (that is, saying it's not C when it is).  For example, char2wchar()
+ * could fail if the locale is C, so str_tolower() shouldn't call it
+ * in that case.
  */
+
+struct locale_cness_cache_entry
+{
+   Oid         collid;
+   bool        collate_is_c;
+   bool        ctype_is_c;
+};
+
+static void
+init_locale_cness_cache(void)
+{
+   HASHCTL     ctl;
+
+   memset(&ctl, 0, sizeof(ctl));
+   ctl.keysize = sizeof(Oid);
+   ctl.entrysize = sizeof(struct locale_cness_cache_entry);
+   ctl.hash = oid_hash;
+   locale_cness_cache = hash_create("locale C-ness cache", 1000, &ctl, HASH_ELEM | HASH_FUNCTION);
+}
+
+/*
+ * Handle caching of locale "C-ness" for nondefault collation objects.
+ * Relying on the system cache directly isn't fast enough.
+ */
+static bool
+lookup_collation_cness(Oid collation, int category)
+{
+   struct locale_cness_cache_entry *cache_entry;
+   bool        found;
+   HeapTuple   tp;
+   char       *localeptr;
+
+   Assert(OidIsValid(collation));
+   Assert(category == LC_COLLATE || category == LC_CTYPE);
+
+   if (!locale_cness_cache)
+       init_locale_cness_cache();
+
+   cache_entry = hash_search(locale_cness_cache, &collation, HASH_ENTER, &found);
+   if (found)
+   {
+       if (category == LC_COLLATE)
+           return cache_entry->collate_is_c;
+       else
+           return cache_entry->ctype_is_c;
+   }
+
+   tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collation));
+   if (!HeapTupleIsValid(tp))
+       elog(ERROR, "cache lookup failed for collation %u", collation);
+
+   localeptr = NameStr(((Form_pg_collation) GETSTRUCT(tp))->collcollate);
+   cache_entry->collate_is_c = (strcmp(localeptr, "C") == 0) || (strcmp(localeptr, "POSIX") == 0);
+
+   localeptr = NameStr(((Form_pg_collation) GETSTRUCT(tp))->collctype);
+   cache_entry->ctype_is_c = (strcmp(localeptr, "C") == 0) || (strcmp(localeptr, "POSIX") == 0);
+
+   ReleaseSysCache(tp);
+
+   return category == LC_COLLATE ? cache_entry->collate_is_c : cache_entry->ctype_is_c;
+}
+
+
 bool
-lc_collate_is_c(void)
+lc_collate_is_c(Oid collation)
 {
    /* Cache result so we only have to compute it once */
    static int  result = -1;
    char       *localeptr;
 
+   if (!OidIsValid(collation))
+       return false;
+
+   if (collation != DEFAULT_COLLATION_OID)
+       return lookup_collation_cness(collation, LC_COLLATE);
+
    if (result >= 0)
        return (bool) result;
    localeptr = setlocale(LC_COLLATE, NULL);
@@ -331,17 +413,19 @@ lc_collate_is_c(void)
 }
 
 
-/*
- * We'd like to cache whether LC_CTYPE is C (or POSIX), so we can
- * optimize a few code paths in various places.
- */
 bool
-lc_ctype_is_c(void)
+lc_ctype_is_c(Oid collation)
 {
    /* Cache result so we only have to compute it once */
    static int  result = -1;
    char       *localeptr;
 
+   if (!OidIsValid(collation))
+       return false;
+
+   if (collation != DEFAULT_COLLATION_OID)
+       return lookup_collation_cness(collation, LC_CTYPE);
+
    if (result >= 0)
        return (bool) result;
    localeptr = setlocale(LC_CTYPE, NULL);
@@ -483,7 +567,7 @@ PGLC_localeconv(void)
    /* Get formatting information for numeric */
    setlocale(LC_NUMERIC, locale_numeric);
    extlconv = localeconv();
-   encoding = pg_get_encoding_from_locale(locale_numeric);
+   encoding = pg_get_encoding_from_locale(locale_numeric, true);
 
    decimal_point = db_encoding_strdup(encoding, extlconv->decimal_point);
    thousands_sep = db_encoding_strdup(encoding, extlconv->thousands_sep);
@@ -497,7 +581,7 @@ PGLC_localeconv(void)
    /* Get formatting information for monetary */
    setlocale(LC_MONETARY, locale_monetary);
    extlconv = localeconv();
-   encoding = pg_get_encoding_from_locale(locale_monetary);
+   encoding = pg_get_encoding_from_locale(locale_monetary, true);
 
    /*
     * Must copy all values since restoring internal settings may overwrite
@@ -758,3 +842,118 @@ IsoLocaleName(const char *winlocname)
 }
 
 #endif   /* WIN32 && LC_MESSAGES */
+
+
+#ifdef HAVE_LOCALE_T
+struct locale_t_cache_entry
+{
+   Oid         collid;
+   locale_t    locale;
+};
+
+static void
+init_locale_t_cache(void)
+{
+   HASHCTL     ctl;
+
+   memset(&ctl, 0, sizeof(ctl));
+   ctl.keysize = sizeof(Oid);
+   ctl.entrysize = sizeof(struct locale_t_cache_entry);
+   ctl.hash = oid_hash;
+   locale_t_cache = hash_create("locale_t cache", 1000, &ctl, HASH_ELEM | HASH_FUNCTION);
+}
+#endif /* HAVE_LOCALE_T */
+
+/*
+ * Create a locale_t from a collation OID.  Results are cached for the
+ * lifetime of the backend.  Thus, do not free the result with
+ * freelocale().
+ *
+ * As a special optimization, the default/database collation returns
+ * 0.  Callers should then revert to the non-locale_t-enabled code
+ * path.  In fact, they shouldn't call this function at all when they
+ * are dealing with the default locale.  That can save quite a bit in
+ * hotspots.
+ *
+ * For simplicity, we always generate COLLATE + CTYPE even though we
+ * might only need one of them.  Since this is called only once per
+ * session, it shouldn't cost much.
+ */
+pg_locale_t
+pg_newlocale_from_collation(Oid collid)
+{
+#ifdef HAVE_LOCALE_T
+   HeapTuple   tp;
+   const char *collcollate;
+   const char *collctype;
+   locale_t    result;
+   struct locale_t_cache_entry *cache_entry;
+   bool        found;
+
+   if (collid == DEFAULT_COLLATION_OID)
+       return (locale_t) 0;
+
+   if (!OidIsValid(collid))
+       elog(ERROR, "locale operation to be invoked, but no collation was derived");
+
+   if (!locale_t_cache)
+       init_locale_t_cache();
+
+   cache_entry = hash_search(locale_t_cache, &collid, HASH_ENTER, &found);
+   if (found)
+       return cache_entry->locale;
+
+   tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
+   if (!HeapTupleIsValid(tp))
+       elog(ERROR, "cache lookup failed for collation %u", collid);
+
+   collcollate = NameStr(((Form_pg_collation) GETSTRUCT(tp))->collcollate);
+   collctype = NameStr(((Form_pg_collation) GETSTRUCT(tp))->collctype);
+
+   if (strcmp(collcollate, collctype) == 0)
+   {
+       result = newlocale(LC_COLLATE_MASK | LC_CTYPE_MASK, collcollate, NULL);
+       if (!result)
+           ereport(ERROR,
+                   (errcode_for_file_access(),
+                    errmsg("could not create locale \"%s\": %m", collcollate)));
+   }
+   else
+   {
+       locale_t loc1;
+
+       loc1 = newlocale(LC_COLLATE_MASK, collcollate, NULL);
+       if (!loc1)
+           ereport(ERROR,
+                   (errcode_for_file_access(),
+                    errmsg("could not create locale \"%s\": %m", collcollate)));
+       result = newlocale(LC_CTYPE_MASK, collctype, loc1);
+       if (!result)
+           ereport(ERROR,
+                   (errcode_for_file_access(),
+                    errmsg("could not create locale \"%s\": %m", collctype)));
+   }
+
+   ReleaseSysCache(tp);
+
+   cache_entry->locale = result;
+
+   return result;
+#else /* not HAVE_LOCALE_T */
+   /*
+    * For platforms that don't support locale_t, check that we are
+    * dealing with the default locale.  It's unlikely that we'll get
+    * here, but it's possible if users are creating collations even
+    * though they are not supported, or they are mixing builds in odd
+    * ways.
+    */
+   if (!OidIsValid(collid))
+       elog(ERROR, "locale operation to be invoked, but no collation was derived");
+   else if (collid != DEFAULT_COLLATION_OID)
+       ereport(ERROR,
+               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                errmsg("nondefault collations are not supported on this platform")));
+
+   return 0;
+#endif /* not HAVE_LOCALE_T */
+}
index b8259febb86cbf2e457a2d9d0d75176934f71766..cd64235438e9f3b89a5e7eb40f08be09c4889a91 100644 (file)
@@ -23,6 +23,7 @@
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_authid.h"
+#include "catalog/pg_collation.h"
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_depend.h"
 #include "catalog/pg_language.h"
@@ -233,7 +234,7 @@ static void get_from_clause_item(Node *jtnode, Query *query,
                     deparse_context *context);
 static void get_from_clause_alias(Alias *alias, RangeTblEntry *rte,
                      deparse_context *context);
-static void get_from_clause_coldeflist(List *names, List *types, List *typmods,
+static void get_from_clause_coldeflist(List *names, List *types, List *typmods, List *collations,
                           deparse_context *context);
 static void get_opclass_name(Oid opclass, Oid actual_datatype,
                 StringInfo buf);
@@ -788,9 +789,11 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
    Oid         indrelid;
    int         keyno;
    Oid         keycoltype;
+   Datum       indcollDatum;
    Datum       indclassDatum;
    Datum       indoptionDatum;
    bool        isnull;
+   oidvector  *indcollation;
    oidvector  *indclass;
    int2vector *indoption;
    StringInfoData buf;
@@ -808,11 +811,17 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
    indrelid = idxrec->indrelid;
    Assert(indexrelid == idxrec->indexrelid);
 
-   /* Must get indclass and indoption the hard way */
+   /* Must get indcollation, indclass, and indoption the hard way */
+   indcollDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
+                                  Anum_pg_index_indcollation, &isnull);
+   Assert(!isnull);
+   indcollation = (oidvector *) DatumGetPointer(indcollDatum);
+
    indclassDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
                                    Anum_pg_index_indclass, &isnull);
    Assert(!isnull);
    indclass = (oidvector *) DatumGetPointer(indclassDatum);
+
    indoptionDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
                                     Anum_pg_index_indoption, &isnull);
    Assert(!isnull);
@@ -928,6 +937,13 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 
        if (!attrsOnly && (!colno || colno == keyno + 1))
        {
+           Oid coll;
+
+           /* Add collation, if not default */
+           coll = indcollation->values[keyno];
+           if (coll && coll != DEFAULT_COLLATION_OID && coll != get_attcollation(indrelid, attnum))
+               appendStringInfo(&buf, " COLLATE %s", generate_collation_name((indcollation->values[keyno])));
+
            /* Add the operator class name, if not default */
            get_opclass_name(indclass->values[keyno], keycoltype, &buf);
 
@@ -5054,6 +5070,20 @@ get_rule_expr(Node *node, deparse_context *context,
            }
            break;
 
+       case T_CollateClause:
+           {
+               CollateClause *collate = (CollateClause *) node;
+               Node       *arg = (Node *) collate->arg;
+
+               if (!PRETTY_PAREN(context))
+                   appendStringInfoChar(buf, '(');
+               get_rule_expr_paren(arg, context, false, node);
+               appendStringInfo(buf, " COLLATE %s", generate_collation_name(collate->collOid));
+               if (!PRETTY_PAREN(context))
+                   appendStringInfoChar(buf, ')');
+           }
+           break;
+
        case T_CoerceViaIO:
            {
                CoerceViaIO *iocoerce = (CoerceViaIO *) node;
@@ -6345,6 +6375,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
                get_from_clause_coldeflist(rte->eref->colnames,
                                           rte->funccoltypes,
                                           rte->funccoltypmods,
+                                          rte->funccolcollations,
                                           context);
            }
            else
@@ -6543,35 +6574,42 @@ get_from_clause_alias(Alias *alias, RangeTblEntry *rte,
  * responsible for ensuring that an alias or AS is present before it.
  */
 static void
-get_from_clause_coldeflist(List *names, List *types, List *typmods,
+get_from_clause_coldeflist(List *names, List *types, List *typmods, List *collations,
                           deparse_context *context)
 {
    StringInfo  buf = context->buf;
    ListCell   *l1;
    ListCell   *l2;
    ListCell   *l3;
+   ListCell   *l4;
    int         i = 0;
 
    appendStringInfoChar(buf, '(');
 
    l2 = list_head(types);
    l3 = list_head(typmods);
+   l4 = list_head(collations);
    foreach(l1, names)
    {
        char       *attname = strVal(lfirst(l1));
        Oid         atttypid;
        int32       atttypmod;
+       Oid         attcollation;
 
        atttypid = lfirst_oid(l2);
        l2 = lnext(l2);
        atttypmod = lfirst_int(l3);
        l3 = lnext(l3);
+       attcollation = lfirst_oid(l4);
+       l4 = lnext(l4);
 
        if (i > 0)
            appendStringInfo(buf, ", ");
        appendStringInfo(buf, "%s %s",
                         quote_identifier(attname),
                         format_type_with_typemod(atttypid, atttypmod));
+       if (attcollation && attcollation != DEFAULT_COLLATION_OID)
+           appendStringInfo(buf, " COLLATE %s", generate_collation_name(attcollation));
        i++;
    }
 
@@ -7038,6 +7076,39 @@ generate_operator_name(Oid operid, Oid arg1, Oid arg2)
    return buf.data;
 }
 
+/*
+ * generate_collation_name
+ *     Compute the name to display for a collation specified by OID
+ *
+ * The result includes all necessary quoting and schema-prefixing.
+ */
+char *
+generate_collation_name(Oid collid)
+{
+   HeapTuple   tp;
+   Form_pg_collation colltup;
+   char       *collname;
+   char       *nspname;
+   char       *result;
+
+   tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
+   if (!HeapTupleIsValid(tp))
+       elog(ERROR, "cache lookup failed for collation %u", collid);
+   colltup = (Form_pg_collation) GETSTRUCT(tp);
+   collname = NameStr(colltup->collname);
+
+   if (!CollationIsVisible(collid))
+       nspname = get_namespace_name(colltup->collnamespace);
+   else
+       nspname = NULL;
+
+   result = quote_qualified_identifier(nspname, collname);
+
+   ReleaseSysCache(tp);
+
+   return result;
+}
+
 /*
  * Given a C string, produce a TEXT datum.
  *
index 7e3ff864c83f48cddeb8ccc64227a882257842bd..b3299b56d836cb0e6c2ae8d00e5f1afc362894c7 100644 (file)
@@ -94,6 +94,7 @@
 #include "access/gin.h"
 #include "access/sysattr.h"
 #include "catalog/index.h"
+#include "catalog/pg_collation.h"
 #include "catalog/pg_opfamily.h"
 #include "catalog/pg_statistic.h"
 #include "catalog/pg_type.h"
@@ -144,7 +145,7 @@ static double eqjoinsel_inner(Oid operator,
 static double eqjoinsel_semi(Oid operator,
               VariableStatData *vardata1, VariableStatData *vardata2);
 static bool convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue,
-                 Datum lobound, Datum hibound, Oid boundstypid,
+                 Datum lobound, Datum hibound, Oid boundstypid, Oid boundscollid,
                  double *scaledlobound, double *scaledhibound);
 static double convert_numeric_to_scalar(Datum value, Oid typid);
 static void convert_string_to_scalar(char *value,
@@ -163,10 +164,10 @@ static double convert_one_string_to_scalar(char *value,
                             int rangelo, int rangehi);
 static double convert_one_bytea_to_scalar(unsigned char *value, int valuelen,
                            int rangelo, int rangehi);
-static char *convert_string_datum(Datum value, Oid typid);
+static char *convert_string_datum(Datum value, Oid typid, Oid collid);
 static double convert_timevalue_to_scalar(Datum value, Oid typid);
 static bool get_variable_range(PlannerInfo *root, VariableStatData *vardata,
-                  Oid sortop, Datum *min, Datum *max);
+                  Oid sortop, Oid collation, Datum *min, Datum *max);
 static bool get_actual_variable_range(PlannerInfo *root,
                          VariableStatData *vardata,
                          Oid sortop,
@@ -513,6 +514,7 @@ scalarineqsel(PlannerInfo *root, Oid operator, bool isgt,
    stats = (Form_pg_statistic) GETSTRUCT(vardata->statsTuple);
 
    fmgr_info(get_opcode(operator), &opproc);
+   fmgr_info_collation(vardata->attcollation, &opproc);
 
    /*
     * If we have most-common-values info, add up the fractions of the MCV
@@ -837,7 +839,7 @@ ineq_histogram_selectivity(PlannerInfo *root,
                 */
                if (convert_to_scalar(constval, consttype, &val,
                                      values[i - 1], values[i],
-                                     vardata->vartype,
+                                     vardata->vartype, vardata->attcollation,
                                      &low, &high))
                {
                    if (high <= low)
@@ -1249,6 +1251,7 @@ patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype, bool negate)
 
        /* Try to use the histogram entries to get selectivity */
        fmgr_info(get_opcode(operator), &opproc);
+       fmgr_info_collation(DEFAULT_COLLATION_OID, &opproc);
 
        selec = histogram_selectivity(&vardata, &opproc, constval, true,
                                      10, 1, &hist_size);
@@ -2585,7 +2588,7 @@ icnlikejoinsel(PG_FUNCTION_ARGS)
  */
 void
 mergejoinscansel(PlannerInfo *root, Node *clause,
-                Oid opfamily, int strategy, bool nulls_first,
+                Oid opfamily, Oid collation, int strategy, bool nulls_first,
                 Selectivity *leftstart, Selectivity *leftend,
                 Selectivity *rightstart, Selectivity *rightend)
 {
@@ -2754,20 +2757,20 @@ mergejoinscansel(PlannerInfo *root, Node *clause,
    /* Try to get ranges of both inputs */
    if (!isgt)
    {
-       if (!get_variable_range(root, &leftvar, lstatop,
+       if (!get_variable_range(root, &leftvar, lstatop, collation,
                                &leftmin, &leftmax))
            goto fail;          /* no range available from stats */
-       if (!get_variable_range(root, &rightvar, rstatop,
+       if (!get_variable_range(root, &rightvar, rstatop, collation,
                                &rightmin, &rightmax))
            goto fail;          /* no range available from stats */
    }
    else
    {
        /* need to swap the max and min */
-       if (!get_variable_range(root, &leftvar, lstatop,
+       if (!get_variable_range(root, &leftvar, lstatop, collation,
                                &leftmax, &leftmin))
            goto fail;          /* no range available from stats */
-       if (!get_variable_range(root, &rightvar, rstatop,
+       if (!get_variable_range(root, &rightvar, rstatop, collation,
                                &rightmax, &rightmin))
            goto fail;          /* no range available from stats */
    }
@@ -3368,7 +3371,7 @@ estimate_hash_bucketsize(PlannerInfo *root, Node *hashkey, double nbuckets)
  */
 static bool
 convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue,
-                 Datum lobound, Datum hibound, Oid boundstypid,
+                 Datum lobound, Datum hibound, Oid boundstypid, Oid boundscollid,
                  double *scaledlobound, double *scaledhibound)
 {
    /*
@@ -3421,9 +3424,9 @@ convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue,
        case TEXTOID:
        case NAMEOID:
            {
-               char       *valstr = convert_string_datum(value, valuetypid);
-               char       *lostr = convert_string_datum(lobound, boundstypid);
-               char       *histr = convert_string_datum(hibound, boundstypid);
+               char       *valstr = convert_string_datum(value, valuetypid, boundscollid);
+               char       *lostr = convert_string_datum(lobound, boundstypid, boundscollid);
+               char       *histr = convert_string_datum(hibound, boundstypid, boundscollid);
 
                convert_string_to_scalar(valstr, scaledvalue,
                                         lostr, scaledlobound,
@@ -3667,7 +3670,7 @@ convert_one_string_to_scalar(char *value, int rangelo, int rangehi)
  * before continuing, so as to generate correct locale-specific results.
  */
 static char *
-convert_string_datum(Datum value, Oid typid)
+convert_string_datum(Datum value, Oid typid, Oid collid)
 {
    char       *val;
 
@@ -3700,7 +3703,7 @@ convert_string_datum(Datum value, Oid typid)
            return NULL;
    }
 
-   if (!lc_collate_is_c())
+   if (!lc_collate_is_c(collid))
    {
        char       *xfrmstr;
        size_t      xfrmlen;
@@ -4099,6 +4102,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
        vardata->rel = find_base_rel(root, var->varno);
        vardata->atttype = var->vartype;
        vardata->atttypmod = var->vartypmod;
+       vardata->attcollation = var->varcollid;
        vardata->isunique = has_unique_index(vardata->rel, var->varattno);
 
        rte = root->simple_rte_array[var->varno];
@@ -4184,6 +4188,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
    vardata->var = node;
    vardata->atttype = exprType(node);
    vardata->atttypmod = exprTypmod(node);
+   vardata->attcollation = exprCollation(node);
 
    if (onerel)
    {
@@ -4392,7 +4397,7 @@ get_variable_numdistinct(VariableStatData *vardata)
  * be "<" not ">", as only the former is likely to be found in pg_statistic.
  */
 static bool
-get_variable_range(PlannerInfo *root, VariableStatData *vardata, Oid sortop,
+get_variable_range(PlannerInfo *root, VariableStatData *vardata, Oid sortop, Oid collation,
                   Datum *min, Datum *max)
 {
    Datum       tmin = 0;
@@ -4477,6 +4482,7 @@ get_variable_range(PlannerInfo *root, VariableStatData *vardata, Oid sortop,
        FmgrInfo    opproc;
 
        fmgr_info(get_opcode(sortop), &opproc);
+       fmgr_info_collation(collation, &opproc);
 
        for (i = 0; i < nvalues; i++)
        {
@@ -5482,7 +5488,7 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc)
    {
        workstr = TextDatumGetCString(str_const->constvalue);
        len = strlen(workstr);
-       if (lc_collate_is_c() || len == 0)
+       if (lc_collate_is_c(ltproc->fn_collation) || len == 0)
            cmpstr = str_const->constvalue;
        else
        {
@@ -5494,11 +5500,11 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc)
                char       *best;
 
                best = "Z";
-               if (varstr_cmp(best, 1, "z", 1) < 0)
+               if (varstr_cmp(best, 1, "z", 1, DEFAULT_COLLATION_OID) < 0)
                    best = "z";
-               if (varstr_cmp(best, 1, "y", 1) < 0)
+               if (varstr_cmp(best, 1, "y", 1, DEFAULT_COLLATION_OID) < 0)
                    best = "y";
-               if (varstr_cmp(best, 1, "9", 1) < 0)
+               if (varstr_cmp(best, 1, "9", 1, DEFAULT_COLLATION_OID) < 0)
                    best = "9";
                suffixchar = *best;
            }
index 08be966249055eef99aea3cd09d85607920f09ba..1c0ef921a7116be96db3ec15efa8d7300c4564d5 100644 (file)
@@ -737,7 +737,8 @@ bpcharlt(PG_FUNCTION_ARGS)
    len1 = bcTruelen(arg1);
    len2 = bcTruelen(arg2);
 
-   cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2);
+   cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2,
+                    PG_GET_COLLATION());
 
    PG_FREE_IF_COPY(arg1, 0);
    PG_FREE_IF_COPY(arg2, 1);
@@ -757,7 +758,8 @@ bpcharle(PG_FUNCTION_ARGS)
    len1 = bcTruelen(arg1);
    len2 = bcTruelen(arg2);
 
-   cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2);
+   cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2,
+                    PG_GET_COLLATION());
 
    PG_FREE_IF_COPY(arg1, 0);
    PG_FREE_IF_COPY(arg2, 1);
@@ -777,7 +779,8 @@ bpchargt(PG_FUNCTION_ARGS)
    len1 = bcTruelen(arg1);
    len2 = bcTruelen(arg2);
 
-   cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2);
+   cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2,
+                    PG_GET_COLLATION());
 
    PG_FREE_IF_COPY(arg1, 0);
    PG_FREE_IF_COPY(arg2, 1);
@@ -797,7 +800,8 @@ bpcharge(PG_FUNCTION_ARGS)
    len1 = bcTruelen(arg1);
    len2 = bcTruelen(arg2);
 
-   cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2);
+   cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2,
+                    PG_GET_COLLATION());
 
    PG_FREE_IF_COPY(arg1, 0);
    PG_FREE_IF_COPY(arg2, 1);
@@ -817,7 +821,8 @@ bpcharcmp(PG_FUNCTION_ARGS)
    len1 = bcTruelen(arg1);
    len2 = bcTruelen(arg2);
 
-   cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2);
+   cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2,
+                    PG_GET_COLLATION());
 
    PG_FREE_IF_COPY(arg1, 0);
    PG_FREE_IF_COPY(arg2, 1);
@@ -837,7 +842,8 @@ bpchar_larger(PG_FUNCTION_ARGS)
    len1 = bcTruelen(arg1);
    len2 = bcTruelen(arg2);
 
-   cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2);
+   cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2,
+                    PG_GET_COLLATION());
 
    PG_RETURN_BPCHAR_P((cmp >= 0) ? arg1 : arg2);
 }
@@ -854,7 +860,8 @@ bpchar_smaller(PG_FUNCTION_ARGS)
    len1 = bcTruelen(arg1);
    len2 = bcTruelen(arg2);
 
-   cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2);
+   cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2,
+                    PG_GET_COLLATION());
 
    PG_RETURN_BPCHAR_P((cmp <= 0) ? arg1 : arg2);
 }
index e111d2650b4d5b4c25bd7d4735bdca665cadf4f9..8a7a3cf45bfa79b9f19668711a43fbd8378fa286 100644 (file)
@@ -18,6 +18,7 @@
 #include 
 
 #include "access/tuptoaster.h"
+#include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
 #include "libpq/md5.h"
 #include "libpq/pqformat.h"
@@ -55,7 +56,7 @@ typedef struct
 #define PG_GETARG_UNKNOWN_P_COPY(n) DatumGetUnknownPCopy(PG_GETARG_DATUM(n))
 #define PG_RETURN_UNKNOWN_P(x)     PG_RETURN_POINTER(x)
 
-static int text_cmp(text *arg1, text *arg2);
+static int text_cmp(text *arg1, text *arg2, Oid collid);
 static int32 text_length(Datum str);
 static int text_position(text *t1, text *t2);
 static void text_position_setup(text *t1, text *t2, TextPositionState *state);
@@ -1274,7 +1275,7 @@ text_position_cleanup(TextPositionState *state)
  * whether arg1 is less than, equal to, or greater than arg2.
  */
 int
-varstr_cmp(char *arg1, int len1, char *arg2, int len2)
+varstr_cmp(char *arg1, int len1, char *arg2, int len2, Oid collid)
 {
    int         result;
 
@@ -1284,7 +1285,7 @@ varstr_cmp(char *arg1, int len1, char *arg2, int len2)
     * slower, so we optimize the case where LC_COLLATE is C.  We also try to
     * optimize relatively-short strings by avoiding palloc/pfree overhead.
     */
-   if (lc_collate_is_c())
+   if (lc_collate_is_c(collid))
    {
        result = memcmp(arg1, arg2, Min(len1, len2));
        if ((result == 0) && (len1 != len2))
@@ -1298,6 +1299,10 @@ varstr_cmp(char *arg1, int len1, char *arg2, int len2)
        char        a2buf[STACKBUFLEN];
        char       *a1p,
                   *a2p;
+       pg_locale_t mylocale = 0;
+
+       if (collid != DEFAULT_COLLATION_OID)
+           mylocale = pg_newlocale_from_collation(collid);
 
 #ifdef WIN32
        /* Win32 does not have UTF-8, so we need to map to UTF-16 */
@@ -1398,6 +1403,11 @@ varstr_cmp(char *arg1, int len1, char *arg2, int len2)
        memcpy(a2p, arg2, len2);
        a2p[len2] = '\0';
 
+#ifdef HAVE_LOCALE_T
+       if (mylocale)
+           result = strcoll_l(a1p, a2p, mylocale);
+       else
+#endif
        result = strcoll(a1p, a2p);
 
        /*
@@ -1424,7 +1434,7 @@ varstr_cmp(char *arg1, int len1, char *arg2, int len2)
  * Returns -1, 0 or 1
  */
 static int
-text_cmp(text *arg1, text *arg2)
+text_cmp(text *arg1, text *arg2, Oid collid)
 {
    char       *a1p,
               *a2p;
@@ -1437,7 +1447,7 @@ text_cmp(text *arg1, text *arg2)
    len1 = VARSIZE_ANY_EXHDR(arg1);
    len2 = VARSIZE_ANY_EXHDR(arg2);
 
-   return varstr_cmp(a1p, len1, a2p, len2);
+   return varstr_cmp(a1p, len1, a2p, len2, collid);
 }
 
 /*
@@ -1519,7 +1529,7 @@ text_lt(PG_FUNCTION_ARGS)
    text       *arg2 = PG_GETARG_TEXT_PP(1);
    bool        result;
 
-   result = (text_cmp(arg1, arg2) < 0);
+   result = (text_cmp(arg1, arg2, PG_GET_COLLATION()) < 0);
 
    PG_FREE_IF_COPY(arg1, 0);
    PG_FREE_IF_COPY(arg2, 1);
@@ -1534,7 +1544,7 @@ text_le(PG_FUNCTION_ARGS)
    text       *arg2 = PG_GETARG_TEXT_PP(1);
    bool        result;
 
-   result = (text_cmp(arg1, arg2) <= 0);
+   result = (text_cmp(arg1, arg2, PG_GET_COLLATION()) <= 0);
 
    PG_FREE_IF_COPY(arg1, 0);
    PG_FREE_IF_COPY(arg2, 1);
@@ -1549,7 +1559,7 @@ text_gt(PG_FUNCTION_ARGS)
    text       *arg2 = PG_GETARG_TEXT_PP(1);
    bool        result;
 
-   result = (text_cmp(arg1, arg2) > 0);
+   result = (text_cmp(arg1, arg2, PG_GET_COLLATION()) > 0);
 
    PG_FREE_IF_COPY(arg1, 0);
    PG_FREE_IF_COPY(arg2, 1);
@@ -1564,7 +1574,7 @@ text_ge(PG_FUNCTION_ARGS)
    text       *arg2 = PG_GETARG_TEXT_PP(1);
    bool        result;
 
-   result = (text_cmp(arg1, arg2) >= 0);
+   result = (text_cmp(arg1, arg2, PG_GET_COLLATION()) >= 0);
 
    PG_FREE_IF_COPY(arg1, 0);
    PG_FREE_IF_COPY(arg2, 1);
@@ -1579,7 +1589,7 @@ bttextcmp(PG_FUNCTION_ARGS)
    text       *arg2 = PG_GETARG_TEXT_PP(1);
    int32       result;
 
-   result = text_cmp(arg1, arg2);
+   result = text_cmp(arg1, arg2, PG_GET_COLLATION());
 
    PG_FREE_IF_COPY(arg1, 0);
    PG_FREE_IF_COPY(arg2, 1);
@@ -1595,7 +1605,7 @@ text_larger(PG_FUNCTION_ARGS)
    text       *arg2 = PG_GETARG_TEXT_PP(1);
    text       *result;
 
-   result = ((text_cmp(arg1, arg2) > 0) ? arg1 : arg2);
+   result = ((text_cmp(arg1, arg2, PG_GET_COLLATION()) > 0) ? arg1 : arg2);
 
    PG_RETURN_TEXT_P(result);
 }
@@ -1607,7 +1617,7 @@ text_smaller(PG_FUNCTION_ARGS)
    text       *arg2 = PG_GETARG_TEXT_PP(1);
    text       *result;
 
-   result = ((text_cmp(arg1, arg2) < 0) ? arg1 : arg2);
+   result = ((text_cmp(arg1, arg2, PG_GET_COLLATION()) < 0) ? arg1 : arg2);
 
    PG_RETURN_TEXT_P(result);
 }
index 0a4144ba547d8b74a384d29006d60d5ca7f5265e..6af23429ad8aaa75cbdec760943e02a475226ce6 100644 (file)
@@ -20,6 +20,7 @@
 #include "bootstrap/bootstrap.h"
 #include "catalog/pg_amop.h"
 #include "catalog/pg_amproc.h"
+#include "catalog/pg_collation.h"
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_opclass.h"
@@ -902,6 +903,33 @@ get_atttypmod(Oid relid, AttrNumber attnum)
        return -1;
 }
 
+/*
+ * get_attcollation
+ *
+ *     Given the relation id and the attribute number,
+ *     return the "attcollation" field from the attribute relation.
+ */
+Oid
+get_attcollation(Oid relid, AttrNumber attnum)
+{
+   HeapTuple   tp;
+
+   tp = SearchSysCache2(ATTNUM,
+                        ObjectIdGetDatum(relid),
+                        Int16GetDatum(attnum));
+   if (HeapTupleIsValid(tp))
+   {
+       Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp);
+       Oid     result;
+
+       result = att_tup->attcollation;
+       ReleaseSysCache(tp);
+       return result;
+   }
+   else
+       return InvalidOid;
+}
+
 /*
  * get_atttypetypmod
  *
@@ -931,6 +959,36 @@ get_atttypetypmod(Oid relid, AttrNumber attnum,
    ReleaseSysCache(tp);
 }
 
+/*             ---------- COLLATION CACHE ----------                    */
+
+/*
+ * get_collation_name
+ *     Returns the name of a given pg_collation entry.
+ *
+ * Returns a palloc'd copy of the string, or NULL if no such constraint.
+ *
+ * NOTE: since collation name is not unique, be wary of code that uses this
+ * for anything except preparing error messages.
+ */
+char *
+get_collation_name(Oid colloid)
+{
+   HeapTuple   tp;
+
+   tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(colloid));
+   if (HeapTupleIsValid(tp))
+   {
+       Form_pg_collation colltup = (Form_pg_collation) GETSTRUCT(tp);
+       char       *result;
+
+       result = pstrdup(NameStr(colltup->collname));
+       ReleaseSysCache(tp);
+       return result;
+   }
+   else
+       return NULL;
+}
+
 /*             ---------- CONSTRAINT CACHE ----------                   */
 
 /*
@@ -2523,6 +2581,42 @@ get_typmodout(Oid typid)
 }
 #endif   /* NOT_USED */
 
+/*
+ * get_typcollation
+ *
+ *     Given the type OID, return the type's typcollation attribute.
+ */
+Oid
+get_typcollation(Oid typid)
+{
+   HeapTuple   tp;
+
+   tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+   if (HeapTupleIsValid(tp))
+   {
+       Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
+       Oid         result;
+
+       result = typtup->typcollation;
+       ReleaseSysCache(tp);
+       return result;
+   }
+   else
+       return InvalidOid;
+}
+
+
+/*
+ * type_is_collatable
+ *
+ *     Return whether the type cares about collations
+ */
+bool
+type_is_collatable(Oid typid)
+{
+   return OidIsValid(get_typcollation(typid));
+}
+
 
 /*             ---------- STATISTICS CACHE ----------                   */
 
index 3b40acf4dfe6b8ed61ce7d67fa69e7f416420399..90464fd06631f87992e421bea82c4222329c3c83 100644 (file)
@@ -976,9 +976,11 @@ RelationInitIndexAccessInfo(Relation relation)
 {
    HeapTuple   tuple;
    Form_pg_am  aform;
+   Datum       indcollDatum;
    Datum       indclassDatum;
    Datum       indoptionDatum;
    bool        isnull;
+   oidvector  *indcoll;
    oidvector  *indclass;
    int2vector *indoption;
    MemoryContext indexcxt;
@@ -1061,9 +1063,25 @@ RelationInitIndexAccessInfo(Relation relation)
        relation->rd_supportinfo = NULL;
    }
 
+   relation->rd_indcollation = (Oid *)
+       MemoryContextAllocZero(indexcxt, natts * sizeof(Oid));
+
    relation->rd_indoption = (int16 *)
        MemoryContextAllocZero(indexcxt, natts * sizeof(int16));
 
+   /*
+    * indcollation cannot be referenced directly through the C struct, because it
+    * comes after the variable-width indkey field.  Must extract the datum
+    * the hard way...
+    */
+   indcollDatum = fastgetattr(relation->rd_indextuple,
+                              Anum_pg_index_indcollation,
+                              GetPgIndexDescriptor(),
+                              &isnull);
+   Assert(!isnull);
+   indcoll = (oidvector *) DatumGetPointer(indcollDatum);
+   memcpy(relation->rd_indcollation, indcoll->values, natts * sizeof(Oid));
+
    /*
     * indclass cannot be referenced directly through the C struct, because it
     * comes after the variable-width indkey field.  Must extract the datum
@@ -3988,6 +4006,7 @@ load_relcache_init_file(bool shared)
            RegProcedure *support;
            int         nsupport;
            int16      *indoption;
+           Oid        *indcollation;
 
            /* Count nailed indexes to ensure we have 'em all */
            if (rel->rd_isnailed)
@@ -4054,6 +4073,16 @@ load_relcache_init_file(bool shared)
 
            rel->rd_support = support;
 
+           /* next, read the vector of collation OIDs */
+           if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
+               goto read_failed;
+
+           indcollation = (Oid *) MemoryContextAlloc(indexcxt, len);
+           if (fread(indcollation, 1, len, fp) != len)
+               goto read_failed;
+
+           rel->rd_indcollation = indcollation;
+
            /* finally, read the vector of indoption values */
            if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
                goto read_failed;
@@ -4087,6 +4116,7 @@ load_relcache_init_file(bool shared)
            Assert(rel->rd_support == NULL);
            Assert(rel->rd_supportinfo == NULL);
            Assert(rel->rd_indoption == NULL);
+           Assert(rel->rd_indcollation == NULL);
        }
 
        /*
@@ -4305,6 +4335,11 @@ write_relcache_init_file(bool shared)
                  relform->relnatts * (am->amsupport * sizeof(RegProcedure)),
                       fp);
 
+           /* next, write the vector of collation OIDs */
+           write_item(rel->rd_indcollation,
+                      relform->relnatts * sizeof(Oid),
+                      fp);
+
            /* finally, write the vector of indoption values */
            write_item(rel->rd_indoption,
                       relform->relnatts * sizeof(int16),
index 191953b972c1cbfa09b68b283a4288f966cb68b6..715341f8420b461e50a7b5dc7ffd78fb8431225e 100644 (file)
@@ -28,6 +28,7 @@
 #include "catalog/pg_auth_members.h"
 #include "catalog/pg_authid.h"
 #include "catalog/pg_cast.h"
+#include "catalog/pg_collation.h"
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_conversion.h"
 #include "catalog/pg_database.h"
@@ -267,6 +268,28 @@ static const struct cachedesc cacheinfo[] = {
        },
        64
    },
+   {CollationRelationId,       /* COLLNAMEENCNSP */
+       CollationNameEncNspIndexId,
+       3,
+       {
+           Anum_pg_collation_collname,
+           Anum_pg_collation_collencoding,
+           Anum_pg_collation_collnamespace,
+           0
+       },
+       256
+   },
+   {CollationRelationId,       /* COLLOID */
+       CollationOidIndexId,
+       1,
+       {
+           ObjectIdAttributeNumber,
+           0,
+           0,
+           0
+       },
+       256
+   },
    {ConversionRelationId,      /* CONDEFAULT */
        ConversionDefaultIndexId,
        4,
index 6f1d76685919734bd83c197db5c5ea637a127133..0315f6b6f0f08e7e35b80d710d64990eaf837a4c 100644 (file)
@@ -310,6 +310,8 @@ Section: Class 42 - Syntax Error or Access Rule Violation
 42939    E    ERRCODE_RESERVED_NAME                                          reserved_name
 42804    E    ERRCODE_DATATYPE_MISMATCH                                      datatype_mismatch
 42P18    E    ERRCODE_INDETERMINATE_DATATYPE                                 indeterminate_datatype
+42P21    E    ERRCODE_COLLATION_MISMATCH                                     collation_mismatch
+42P22    E    ERRCODE_INDETERMINATE_COLLATION                                indeterminate_collation
 42809    E    ERRCODE_WRONG_OBJECT_TYPE                                      wrong_object_type
 
 # Note: for ERRCODE purposes, we divide namable objects into these categories:
index 54d50e9637763aed4b4fbc37f64ff11076fc542e..d05e4d2dd8f907e85a8400cf3a7ddb8a84cd18d3 100644 (file)
@@ -192,6 +192,7 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt,
     * elogs.
     */
    finfo->fn_oid = InvalidOid;
+   finfo->fn_collation = InvalidOid;
    finfo->fn_extra = NULL;
    finfo->fn_mcxt = mcxt;
    finfo->fn_expr = NULL;      /* caller may set this later */
@@ -419,6 +420,25 @@ fmgr_info_other_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple)
    ReleaseSysCache(languageTuple);
 }
 
+/*
+ * Initialize the fn_collation field
+ */
+void
+fmgr_info_collation(Oid collationId, FmgrInfo *finfo)
+{
+   finfo->fn_collation = collationId;
+}
+
+/*
+ * Initialize the fn_expr field and set the collation based on it
+ */
+void
+fmgr_info_expr(Node *expr, FmgrInfo *finfo)
+{
+   finfo->fn_expr = expr;
+   finfo->fn_collation = exprCollation(expr);
+}
+
 /*
  * Fetch and validate the information record for the given external function.
  * The function is specified by a handle for the containing library
@@ -1273,6 +1293,52 @@ DirectFunctionCall9(PGFunction func, Datum arg1, Datum arg2,
    return result;
 }
 
+Datum
+DirectFunctionCall1WithCollation(PGFunction func, Oid collation, Datum arg1)
+{
+   FunctionCallInfoData fcinfo;
+   FmgrInfo    flinfo;
+   Datum       result;
+
+   InitFunctionCallInfoData(fcinfo, &flinfo, 1, NULL, NULL);
+   fcinfo.flinfo->fn_collation = collation;
+
+   fcinfo.arg[0] = arg1;
+   fcinfo.argnull[0] = false;
+
+   result = (*func) (&fcinfo);
+
+   /* Check for null result, since caller is clearly not expecting one */
+   if (fcinfo.isnull)
+       elog(ERROR, "function %p returned NULL", (void *) func);
+
+   return result;
+}
+
+Datum
+DirectFunctionCall2WithCollation(PGFunction func, Oid collation, Datum arg1, Datum arg2)
+{
+   FunctionCallInfoData fcinfo;
+   FmgrInfo    flinfo;
+   Datum       result;
+
+   InitFunctionCallInfoData(fcinfo, &flinfo, 2, NULL, NULL);
+   fcinfo.flinfo->fn_collation = collation;
+
+   fcinfo.arg[0] = arg1;
+   fcinfo.arg[1] = arg2;
+   fcinfo.argnull[0] = false;
+   fcinfo.argnull[1] = false;
+
+   result = (*func) (&fcinfo);
+
+   /* Check for null result, since caller is clearly not expecting one */
+   if (fcinfo.isnull)
+       elog(ERROR, "function %p returned NULL", (void *) func);
+
+   return result;
+}
+
 
 /*
  * These are for invocation of a previously-looked-up function with a
index e32c716392c704980be6d9a76fcecdc7175ac4de..321b4e7f8ffb5f978dc0eb75917212d791b7944d 100644 (file)
@@ -468,7 +468,6 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
    /* If nothing found, parser messed up */
    if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type))
        return false;
-
    /* If needed, deduce one polymorphic type from the other */
    if (have_anyelement_result && !OidIsValid(anyelement_type))
        anyelement_type = resolve_generic_type(ANYELEMENTOID,
@@ -511,6 +510,9 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
            default:
                break;
        }
+       /* Set collation based on actual argument types */
+       TupleDescInitEntryCollation(tupdesc, i + 1,
+                                   exprCollation(call_expr));
    }
 
    return true;
index a04181286af62d90ce983eb5056f3e7e00941642..5ee74f747d06e010ad109aa4a77ab894486a3519 100644 (file)
@@ -629,7 +629,7 @@ perform_default_encoding_conversion(const char *src, int len, bool is_client_to_
  * zero-terminated.  The output will be zero-terminated iff there is room.
  */
 size_t
-wchar2char(char *to, const wchar_t *from, size_t tolen)
+wchar2char(char *to, const wchar_t *from, size_t tolen, Oid collation)
 {
    size_t      result;
 
@@ -660,7 +660,7 @@ wchar2char(char *to, const wchar_t *from, size_t tolen)
    else
 #endif   /* WIN32 */
    {
-       Assert(!lc_ctype_is_c());
+       Assert(!lc_ctype_is_c(collation));
        result = wcstombs(to, from, tolen);
    }
    return result;
@@ -676,7 +676,7 @@ wchar2char(char *to, const wchar_t *from, size_t tolen)
  * The output will be zero-terminated iff there is room.
  */
 size_t
-char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen)
+char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen, Oid collation)
 {
    size_t      result;
 
@@ -711,7 +711,7 @@ char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen)
        /* mbstowcs requires ending '\0' */
        char       *str = pnstrdup(from, fromlen);
 
-       Assert(!lc_ctype_is_c());
+       Assert(!lc_ctype_is_c(collation));
        result = mbstowcs(to, str, tolen);
        pfree(str);
    }
@@ -983,7 +983,7 @@ GetPlatformEncoding(void)
    if (PlatformEncoding == NULL)
    {
        /* try to determine encoding of server's environment locale */
-       int         encoding = pg_get_encoding_from_locale("");
+       int         encoding = pg_get_encoding_from_locale("", true);
 
        if (encoding < 0)
            encoding = PG_SQL_ASCII;
index d20a3b3739d3f5a92e26b48678b8f88a7a13085a..f2449ea6b13d7acc50dd06fd3ac62a0fe74764dc 100644 (file)
@@ -582,7 +582,7 @@ tuplesort_begin_common(int workMem, bool randomAccess)
 Tuplesortstate *
 tuplesort_begin_heap(TupleDesc tupDesc,
                     int nkeys, AttrNumber *attNums,
-                    Oid *sortOperators, bool *nullsFirstFlags,
+                    Oid *sortOperators, Oid *collations, bool *nullsFirstFlags,
                     int workMem, bool randomAccess)
 {
    Tuplesortstate *state = tuplesort_begin_common(workMem, randomAccess);
@@ -640,6 +640,10 @@ tuplesort_begin_heap(TupleDesc tupDesc,
                    sortFunction,
                    (Datum) 0);
 
+       if (collations)
+           ScanKeyEntryInitializeCollation(&state->scanKeys[i],
+                                           collations[i]);
+
        /* However, we use btree's conventions for encoding directionality */
        if (reverse)
            state->scanKeys[i].sk_flags |= SK_BT_DESC;
@@ -791,7 +795,7 @@ tuplesort_begin_index_hash(Relation indexRel,
 
 Tuplesortstate *
 tuplesort_begin_datum(Oid datumType,
-                     Oid sortOperator, bool nullsFirstFlag,
+                     Oid sortOperator, Oid sortCollation, bool nullsFirstFlag,
                      int workMem, bool randomAccess)
 {
    Tuplesortstate *state = tuplesort_begin_common(workMem, randomAccess);
@@ -832,6 +836,7 @@ tuplesort_begin_datum(Oid datumType,
        elog(ERROR, "operator %u is not a valid ordering operator",
             sortOperator);
    fmgr_info(sortFunction, &state->sortOpFn);
+   fmgr_info_collation(sortCollation, &state->sortOpFn);
 
    /* set ordering flags */
    state->sortFnFlags = reverse ? SK_BT_DESC : 0;
index b903b7b0575af4be79f3e773083f12b198a176ef..b90fd865b30320a6a2245f4f1366bcb6d05d3cbc 100644 (file)
@@ -167,6 +167,7 @@ static void get_set_pwd(void);
 static void setup_depend(void);
 static void setup_sysviews(void);
 static void setup_description(void);
+static void setup_collation(void);
 static void setup_conversion(void);
 static void setup_dictionary(void);
 static void setup_privileges(void);
@@ -226,6 +227,12 @@ do { \
        output_failed = true, output_errno = errno; \
 } while (0)
 
+#define PG_CMD_PRINTF3(fmt, arg1, arg2, arg3)      \
+do { \
+   if (fprintf(cmdfd, fmt, arg1, arg2, arg3) < 0 || fflush(cmdfd) < 0) \
+       output_failed = true, output_errno = errno; \
+} while (0)
+
 #ifndef WIN32
 #define QUOTE_PATH ""
 #define DIR_SEP "/"
@@ -1492,6 +1499,182 @@ setup_description(void)
    check_ok();
 }
 
+#ifdef HAVE_LOCALE_T
+/*
+ * "Normalize" a locale name, stripping off encoding tags such as
+ * ".utf8" (e.g., "en_US.utf8" -> "en_US", but "br_FR.iso885915@euro"
+ * -> "br_FR@euro").  Return true if a new, different name was
+ * generated.
+ */
+static bool
+normalize_locale_name(char *new, const char *old)
+{
+   char   *n = new;
+   const char *o = old;
+   bool    changed = false;
+
+   while (*o)
+   {
+       if (*o == '.')
+       {
+           /* skip over encoding tag such as ".utf8" or ".UTF-8" */
+           o++;
+           while ((*o >= 'A' && *o <= 'Z')
+                  || (*o >= 'a' && *o <= 'z')
+                  || (*o >= '0' && *o <= '9')
+                  || (*o == '-'))
+               o++;
+           changed = true;
+       }
+       else
+           *n++ = *o++;
+   }
+   *n = '\0';
+
+   return changed;
+}
+#endif /* HAVE_LOCALE_T */
+
+/*
+ * populate pg_collation
+ */
+static void
+setup_collation(void)
+{
+#ifdef HAVE_LOCALE_T
+   int i;
+   FILE   *locale_a_handle;
+   char    localebuf[NAMEDATALEN];
+   int     skipped = 0;
+   PG_CMD_DECL;
+#endif
+
+   fputs(_("creating collations ... "), stdout);
+   fflush(stdout);
+
+#ifdef HAVE_LOCALE_T
+   snprintf(cmd, sizeof(cmd),
+            "\"%s\" %s template1 >%s",
+            backend_exec, backend_options,
+            DEVNULL);
+
+   locale_a_handle = popen_check("locale -a", "r");
+   if (!locale_a_handle)
+       return;
+
+   PG_CMD_OPEN;
+
+   PG_CMD_PUTS("CREATE TEMP TABLE tmp_pg_collation ( "
+               "   collname name, "
+               "   locale name, "
+               "   encoding int) WITHOUT OIDS;\n");
+
+   while (fgets(localebuf, sizeof(localebuf), locale_a_handle))
+   {
+       size_t  len;
+       int     enc;
+       bool    skip;
+       char    alias[NAMEDATALEN];
+
+       len = strlen(localebuf);
+
+       if (localebuf[len - 1] != '\n')
+       {
+           if (debug)
+               fprintf(stderr, _("%s: locale name too long, skipped: %s\n"),
+                       progname, localebuf);
+           skipped++;
+           continue;
+       }
+       localebuf[len - 1] = '\0';
+
+       /*
+        * Some systems have locale names that don't consist entirely
+        * of ASCII letters (such as "bokmål" or
+        * "français").  This is pretty silly, since we need
+        * the locale itself to interpret the non-ASCII characters.
+        * We can't do much with those, so we filter them out.
+        */
+       skip = false;
+       for (i = 0; i < len; i++)
+           if (IS_HIGHBIT_SET(localebuf[i]))
+           {
+               if (debug)
+                   fprintf(stderr, _("%s: locale name has non-ASCII characters, skipped: %s\n"),
+                           progname, localebuf);
+               skipped++;
+               skip = true;
+               break;
+           }
+       if (skip)
+           continue;
+
+       enc = pg_get_encoding_from_locale(localebuf, debug);
+       if (enc < 0)
+       {
+           skipped++;
+           continue;           /* error message printed by pg_get_encoding_from_locale() */
+       }
+       if (enc == PG_SQL_ASCII)
+           continue;           /* SQL_ASCII is handled separately */
+
+       PG_CMD_PRINTF2("INSERT INTO tmp_pg_collation (locale, encoding) VALUES ('%s', %d);",
+                      escape_quotes(localebuf), enc);
+
+       /*
+        * Generate aliases such as "en_US" in addition to
+        * "en_US.utf8" for ease of use.  Note that collation names
+        * are unique per encoding only, so this doesn't clash with
+        * "en_US" for LATIN1, say.
+        */
+       if (normalize_locale_name(alias, localebuf))
+           PG_CMD_PRINTF3("INSERT INTO tmp_pg_collation (collname, locale, encoding) VALUES ('%s', '%s', %d);",
+                          escape_quotes(alias), escape_quotes(localebuf), enc);
+   }
+
+   for (i = PG_SQL_ASCII; i <= PG_ENCODING_BE_LAST; i++)
+       PG_CMD_PRINTF2("INSERT INTO tmp_pg_collation (locale, encoding) VALUES ('C', %d), ('POSIX', %d);",
+                      i, i);
+
+   /* Add an SQL-standard name */
+   PG_CMD_PRINTF1("INSERT INTO tmp_pg_collation (collname, locale, encoding) VALUES ('ucs_basic', 'C', %d);", PG_UTF8);
+
+   /*
+    * When copying collations to the final location, eliminate
+    * aliases that conflict with an existing locale name for the same
+    * encoding.  For example, "br_FR.iso88591" is normalized to
+    * "br_FR", both for encoding LATIN1.  But the unnormalized locale
+    * "br_FR" already exists for LATIN1.  Prefer the collation that
+    * matches the OS locale name, else the first name by sort order
+    * (arbitrary choice to be deterministic).
+    */
+   PG_CMD_PUTS("INSERT INTO pg_collation (collname, collnamespace, collencoding, collcollate, collctype) "
+               " SELECT DISTINCT ON (final_collname, collnamespace, encoding)"
+               "   COALESCE(collname, locale) AS final_collname, "
+               "   (SELECT oid FROM pg_namespace WHERE nspname = 'pg_catalog') AS collnamespace, "
+               "   encoding, "
+               "   locale, locale "
+               "  FROM tmp_pg_collation"
+               "  ORDER BY final_collname, collnamespace, encoding, (collname = locale) DESC, locale;\n");
+
+   pclose(locale_a_handle);
+   PG_CMD_CLOSE;
+
+   check_ok();
+   if (skipped && !debug)
+   {
+       printf(ngettext("%d system locale has been omitted because it cannot supported by PostgreSQL.\n",
+                       "%d system locales have been omitted because they cannot be supported by PostgreSQL.\n",
+                       skipped),
+              skipped);
+       printf(_("Use the option \"--debug\" to see details.\n"));
+   }
+#else /* not HAVE_LOCALE_T */
+   printf(_("not supported on this platform\n"));
+   fflush(stdout);
+#endif /* not HAVE_LOCALE_T */
+}
+
 /*
  * load conversion functions
  */
@@ -2021,7 +2204,7 @@ check_locale_encoding(const char *locale, int user_enc)
 {
    int         locale_enc;
 
-   locale_enc = pg_get_encoding_from_locale(locale);
+   locale_enc = pg_get_encoding_from_locale(locale, true);
 
    /* See notes in createdb() to understand these tests */
    if (!(locale_enc == user_enc ||
@@ -2675,7 +2858,7 @@ main(int argc, char *argv[])
    {
        int         ctype_enc;
 
-       ctype_enc = pg_get_encoding_from_locale(lc_ctype);
+       ctype_enc = pg_get_encoding_from_locale(lc_ctype, true);
 
        if (ctype_enc == -1)
        {
@@ -2952,6 +3135,8 @@ main(int argc, char *argv[])
 
    setup_description();
 
+   setup_collation();
+
    setup_conversion();
 
    setup_dictionary();
index d3eb7662880b3dda57f052d7fbbd22392a8cb562..49c570016ad092699ce913e903a760a07ba26f7f 100644 (file)
@@ -5257,7 +5257,23 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
 
        resetPQExpBuffer(q);
 
-       if (g_fout->remoteVersion >= 90000)
+       if (g_fout->remoteVersion >= 90100)
+       {
+           /* attcollation is new in 9.1 */
+           appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, "
+                             "a.attstattarget, a.attstorage, t.typstorage, "
+                             "a.attnotnull, a.atthasdef, a.attisdropped, "
+                             "a.attlen, a.attalign, a.attislocal, "
+                 "pg_catalog.format_type(t.oid,a.atttypmod,a.attcollation) AS atttypname, "
+                          "array_to_string(attoptions, ', ') AS attoptions "
+            "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
+                             "ON a.atttypid = t.oid "
+                             "WHERE a.attrelid = '%u'::pg_catalog.oid "
+                             "AND a.attnum > 0::pg_catalog.int2 "
+                             "ORDER BY a.attrelid, a.attnum",
+                             tbinfo->dobj.catId.oid);
+       }
+       else if (g_fout->remoteVersion >= 90000)
        {
            /* attoptions is new in 9.0 */
            appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, "
@@ -7258,13 +7274,28 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo)
    selectSourceSchema(tyinfo->dobj.namespace->dobj.name);
 
    /* Fetch domain specific details */
-   /* We assume here that remoteVersion must be at least 70300 */
-   appendPQExpBuffer(query, "SELECT typnotnull, "
-               "pg_catalog.format_type(typbasetype, typtypmod) AS typdefn, "
-                     "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, typdefault "
-                     "FROM pg_catalog.pg_type "
-                     "WHERE oid = '%u'::pg_catalog.oid",
-                     tyinfo->dobj.catId.oid);
+   if (g_fout->remoteVersion >= 90100)
+   {
+       /* typcollation is new in 9.1 */
+       appendPQExpBuffer(query, "SELECT typnotnull, "
+                         "pg_catalog.format_type(typbasetype, typtypmod, typcollation) AS typdefn, "
+                         "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
+                         "typdefault "
+                         "FROM pg_catalog.pg_type t "
+                         "WHERE t.oid = '%u'::pg_catalog.oid",
+                         tyinfo->dobj.catId.oid);
+   }
+   else
+   {
+       /* We assume here that remoteVersion must be at least 70300 */
+       appendPQExpBuffer(query, "SELECT typnotnull, "
+                         "pg_catalog.format_type(typbasetype, typtypmod) AS typdefn, "
+                         "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
+                         "typdefault "
+                         "FROM pg_catalog.pg_type "
+                         "WHERE oid = '%u'::pg_catalog.oid",
+                         tyinfo->dobj.catId.oid);
+   }
 
    res = PQexec(g_conn, query->data);
    check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
index 8d4c390933f464994e95542f787bcd54d30c2e90..d6c6cf1f15e6109c577fc28499d2ad3cda91e528 100644 (file)
@@ -1274,6 +1274,10 @@ describeOneTableDetails(const char *schemaname,
                      "\n   FROM pg_catalog.pg_attrdef d"
                      "\n   WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef),"
                      "\n  a.attnotnull, a.attnum");
+   if (pset.sversion >= 90100)
+       appendPQExpBuffer(&buf, ",\n  (SELECT collname FROM pg_collation WHERE oid = a.attcollation AND collname <> 'default') AS attcollation");
+   else
+       appendPQExpBuffer(&buf, ",\n  NULL AS attcollation");
    if (tableinfo.relkind == 'i')
        appendPQExpBuffer(&buf, ",\n  pg_catalog.pg_get_indexdef(a.attrelid, a.attnum, TRUE) AS indexdef");
    if (verbose)
@@ -1396,12 +1400,25 @@ describeOneTableDetails(const char *schemaname,
        /* Type */
        printTableAddCell(&cont, PQgetvalue(res, i, 1), false, false);
 
-       /* Modifiers: not null and default */
+       /* Modifiers: collate, not null, default */
        if (show_modifiers)
        {
            resetPQExpBuffer(&tmpbuf);
+
+           if (!PQgetisnull(res, i, 5))
+           {
+               if (tmpbuf.len > 0)
+                   appendPQExpBufferStr(&tmpbuf, " ");
+               appendPQExpBuffer(&tmpbuf, _("collate %s"),
+                                 PQgetvalue(res, i, 5));
+           }
+
            if (strcmp(PQgetvalue(res, i, 3), "t") == 0)
+           {
+               if (tmpbuf.len > 0)
+                   appendPQExpBufferStr(&tmpbuf, " ");
                appendPQExpBufferStr(&tmpbuf, _("not null"));
+           }
 
            /* handle "default" here */
            /* (note: above we cut off the 'default' string at 128) */
@@ -1424,12 +1441,12 @@ describeOneTableDetails(const char *schemaname,
 
        /* Expression for index column */
        if (tableinfo.relkind == 'i')
-           printTableAddCell(&cont, PQgetvalue(res, i, 5), false, false);
+           printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false);
 
        /* Storage and Description */
        if (verbose)
        {
-           int         firstvcol = (tableinfo.relkind == 'i' ? 6 : 5);
+           int         firstvcol = (tableinfo.relkind == 'i' ? 7 : 6);
            char       *storage = PQgetvalue(res, i, firstvcol);
 
            /* these strings are literal in our syntax, so not translated. */
index 5a498956c9575fd2a1525a9748fefda964abba48..3d2956c93534c23fb7c1fb413060232b36e818a4 100644 (file)
@@ -152,5 +152,7 @@ extern void ScanKeyEntryInitializeWithInfo(ScanKey entry,
                               Oid subtype,
                               FmgrInfo *finfo,
                               Datum argument);
+extern void ScanKeyEntryInitializeCollation(ScanKey entry,
+                                           Oid collation);
 
 #endif   /* SKEY_H */
index 3b0710e2dccd3b7115f654f8b5fff94b9add2a2a..fb8ee0ad4fd92e5e85b993586f8f2423cf100929 100644 (file)
@@ -114,8 +114,12 @@ extern void TupleDescInitEntry(TupleDesc desc,
                   int32 typmod,
                   int attdim);
 
+extern void TupleDescInitEntryCollation(TupleDesc desc,
+                                       AttrNumber attributeNumber,
+                                       Oid collationid);
+
 extern TupleDesc BuildDescForRelation(List *schema);
 
-extern TupleDesc BuildDescFromLists(List *names, List *types, List *typmods);
+extern TupleDesc BuildDescFromLists(List *names, List *types, List *typmods, List *collations);
 
 #endif   /* TUPDESC_H */
index 98086c91489f1cdd343bf1dc861dd00c329be587..3defe0641f2f54e0bc0758cabebba6b05e75a4a2 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 201102082
+#define CATALOG_VERSION_NO 201102083
 
 #endif
index 60387cca01b044c360dfc0c41d857d9a0c2d9789..2ce6806e5051ca275e25c77c16e447d6299e0f59 100644 (file)
@@ -39,6 +39,7 @@ extern Oid index_create(Relation heapRelation,
             List *indexColNames,
             Oid accessMethodObjectId,
             Oid tableSpaceId,
+            Oid *collationObjectId,
             Oid *classObjectId,
             int16 *coloptions,
             Datum reloptions,
index a3fb916903be25efcb002cfa35207bb81f76856d..866942cf9a310740392f72f6ad44eba1d84e9013 100644 (file)
@@ -107,6 +107,11 @@ DECLARE_UNIQUE_INDEX(pg_class_oid_index, 2662, on pg_class using btree(oid oid_o
 DECLARE_UNIQUE_INDEX(pg_class_relname_nsp_index, 2663, on pg_class using btree(relname name_ops, relnamespace oid_ops));
 #define ClassNameNspIndexId  2663
 
+DECLARE_UNIQUE_INDEX(pg_collation_name_enc_nsp_index, 3164, on pg_collation using btree(collname name_ops, collencoding int4_ops, collnamespace oid_ops));
+#define CollationNameEncNspIndexId 3164
+DECLARE_UNIQUE_INDEX(pg_collation_oid_index, 3085, on pg_collation using btree(oid oid_ops));
+#define CollationOidIndexId  3085
+
 /* This following index is not used for a cache and is not unique */
 DECLARE_INDEX(pg_constraint_conname_nsp_index, 2664, on pg_constraint using btree(conname name_ops, connamespace oid_ops));
 #define ConstraintNameNspIndexId  2664
index d2381fbadb2ff92c2cfafef1c11cd1720e3cf464..e72d82eb16638232ca2078e36a43a745c7db5a1a 100644 (file)
@@ -71,6 +71,9 @@ extern bool OpclassIsVisible(Oid opcid);
 extern Oid OpfamilynameGetOpfid(Oid amid, const char *opfname);
 extern bool OpfamilyIsVisible(Oid opfid);
 
+extern Oid CollationGetCollid(const char *collname);
+extern bool CollationIsVisible(Oid collid);
+
 extern Oid ConversionGetConid(const char *conname);
 extern bool ConversionIsVisible(Oid conid);
 
@@ -114,6 +117,7 @@ extern OverrideSearchPath *GetOverrideSearchPath(MemoryContext context);
 extern void PushOverrideSearchPath(OverrideSearchPath *newpath);
 extern void PopOverrideSearchPath(void);
 
+extern Oid get_collation_oid(List *collname, bool missing_ok);
 extern Oid get_conversion_oid(List *conname, bool missing_ok);
 extern Oid FindDefaultConversionProc(int4 for_encoding, int4 to_encoding);
 
index 1845c8d37c4cb0b8c1be0e9f08e0fdd590f53b15..409d6ea3e7e1781d69fbeafc8d427675a9beb390 100644 (file)
@@ -142,6 +142,9 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
    /* Number of times inherited from direct parent relation(s) */
    int4        attinhcount;
 
+   /* attribute's collation */
+   Oid         attcollation;
+
    /*
     * VARIABLE LENGTH FIELDS start here.  These fields may be NULL, too.
     *
@@ -159,10 +162,10 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
  * ATTRIBUTE_FIXED_PART_SIZE is the size of the fixed-layout,
  * guaranteed-not-null part of a pg_attribute row. This is in fact as much
  * of the row as gets copied into tuple descriptors, so don't expect you
- * can access fields beyond attinhcount except in a real tuple!
+ * can access fields beyond attcollation except in a real tuple!
  */
 #define ATTRIBUTE_FIXED_PART_SIZE \
-   (offsetof(FormData_pg_attribute,attinhcount) + sizeof(int4))
+   (offsetof(FormData_pg_attribute,attcollation) + sizeof(Oid))
 
 /* ----------------
  *     Form_pg_attribute corresponds to a pointer to a tuple with
@@ -176,7 +179,7 @@ typedef FormData_pg_attribute *Form_pg_attribute;
  * ----------------
  */
 
-#define Natts_pg_attribute             19
+#define Natts_pg_attribute             20
 #define Anum_pg_attribute_attrelid     1
 #define Anum_pg_attribute_attname      2
 #define Anum_pg_attribute_atttypid     3
@@ -194,8 +197,9 @@ typedef FormData_pg_attribute *Form_pg_attribute;
 #define Anum_pg_attribute_attisdropped 15
 #define Anum_pg_attribute_attislocal   16
 #define Anum_pg_attribute_attinhcount  17
-#define Anum_pg_attribute_attacl       18
-#define Anum_pg_attribute_attoptions   19
+#define Anum_pg_attribute_attcollation 18
+#define Anum_pg_attribute_attacl       19
+#define Anum_pg_attribute_attoptions   20
 
 
 /* ----------------
index 33d34d55a4c4ca13da8cb211a2dbe619c61fd4fb..42bc8635a249558b4d70222ee70f6fa342a90fb1 100644 (file)
@@ -130,9 +130,9 @@ typedef FormData_pg_class *Form_pg_class;
  */
 
 /* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */
-DATA(insert OID = 1247 (  pg_type      PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 28 0 t f f f f 3 _null_ _null_ ));
+DATA(insert OID = 1247 (  pg_type      PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 29 0 t f f f f 3 _null_ _null_ ));
 DESCR("");
-DATA(insert OID = 1249 (  pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 19 0 f f f f f 3 _null_ _null_ ));
+DATA(insert OID = 1249 (  pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 20 0 f f f f f 3 _null_ _null_ ));
 DESCR("");
 DATA(insert OID = 1255 (  pg_proc      PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f p r 25 0 t f f f f 3 _null_ _null_ ));
 DESCR("");
diff --git a/src/include/catalog/pg_collation.h b/src/include/catalog/pg_collation.h
new file mode 100644 (file)
index 0000000..9883b4d
--- /dev/null
@@ -0,0 +1,62 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_collation.h
+ *   definition of the system "collation" relation (pg_collation)
+ *   along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL$
+ *
+ * NOTES
+ *   the genbki.pl script reads this file and generates .bki
+ *   information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_COLLATION_H
+#define PG_COLLATION_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ *     pg_collation definition.  cpp turns this into
+ *     typedef struct FormData_pg_collation
+ * ----------------
+ */
+#define CollationRelationId  3456
+
+CATALOG(pg_collation,3456)
+{
+   NameData    collname;       /* collation name */
+   Oid         collnamespace;  /* OID of namespace containing this collation */
+   int4        collencoding;   /* encoding that this collation applies to */
+   NameData    collcollate;    /* LC_COLLATE setting */
+   NameData    collctype;      /* LC_CTYPE setting */
+} FormData_pg_collation;
+
+/* ----------------
+ *     Form_pg_collation corresponds to a pointer to a row with
+ *     the format of pg_collation relation.
+ * ----------------
+ */
+typedef FormData_pg_collation *Form_pg_collation;
+
+/* ----------------
+ *     compiler constants for pg_collation
+ * ----------------
+ */
+#define Natts_pg_collation             5
+#define Anum_pg_collation_collname     1
+#define Anum_pg_collation_collnamespace    2
+#define Anum_pg_collation_collencoding 3
+#define Anum_pg_collation_collcollate  4
+#define Anum_pg_collation_collctype        5
+
+DATA(insert OID = 100 ( default PGNSP 0 "" "" ));
+DESCR("placeholder for default collation");
+#define DEFAULT_COLLATION_OID          100
+
+#endif   /* PG_COLLATION_H */
index 0dcae6963c16624b24d6834784ae606168d2d09d..d8142e12bc88232e9a64e77926e98f0a83b1aa60 100644 (file)
@@ -44,6 +44,7 @@ CATALOG(pg_index,2610) BKI_WITHOUT_OIDS BKI_SCHEMA_MACRO
 
    /* VARIABLE LENGTH FIELDS: */
    int2vector  indkey;         /* column numbers of indexed cols, or 0 */
+   oidvector   indcollation;   /* collation identifiers */
    oidvector   indclass;       /* opclass identifiers */
    int2vector  indoption;      /* per-column flags (AM-specific meanings) */
    pg_node_tree indexprs;      /* expression trees for index attributes that
@@ -64,7 +65,7 @@ typedef FormData_pg_index *Form_pg_index;
  *     compiler constants for pg_index
  * ----------------
  */
-#define Natts_pg_index                 16
+#define Natts_pg_index                 17
 #define Anum_pg_index_indexrelid       1
 #define Anum_pg_index_indrelid         2
 #define Anum_pg_index_indnatts         3
@@ -77,10 +78,11 @@ typedef FormData_pg_index *Form_pg_index;
 #define Anum_pg_index_indcheckxmin     10
 #define Anum_pg_index_indisready       11
 #define Anum_pg_index_indkey           12
-#define Anum_pg_index_indclass         13
-#define Anum_pg_index_indoption            14
-#define Anum_pg_index_indexprs         15
-#define Anum_pg_index_indpred          16
+#define Anum_pg_index_indcollation     13
+#define Anum_pg_index_indclass         14
+#define Anum_pg_index_indoption            15
+#define Anum_pg_index_indexprs         16
+#define Anum_pg_index_indpred          17
 
 /*
  * Index AMs that support ordered scans must support these two indoption
index 30ff1b5bb9f38029c603f45a34fb17026b56cb33..836574355cd476839e96a805ae0c3deb481a82b0 100644 (file)
@@ -1386,8 +1386,8 @@ DATA(insert OID = 1078 (  bpcharcmp          PGNSP PGUID 12 1 0 0 f f f t f i 2 0 23
 DESCR("less-equal-greater");
 DATA(insert OID = 1080 (  hashbpchar      PGNSP PGUID 12 1 0 0 f f f t f i 1 0 23 "1042" _null_ _null_ _null_ _null_   hashbpchar _null_ _null_ _null_ ));
 DESCR("hash");
-DATA(insert OID = 1081 (  format_type     PGNSP PGUID 12 1 0 0 f f f f f s 2 0 25 "26 23" _null_ _null_ _null_ _null_ format_type _null_ _null_ _null_ ));
-DESCR("format a type oid and atttypmod to canonical SQL");
+DATA(insert OID = 1081 (  format_type     PGNSP PGUID 12 1 0 0 f f f f f s 3 0 25 "26 23 26" _null_ _null_ _null_ _null_ format_type _null_ _null_ _null_ ));
+DESCR("format a type OID, atttypmod, and collation OID to canonical SQL");
 DATA(insert OID = 1084 (  date_in         PGNSP PGUID 12 1 0 0 f f f t f s 1 0 1082 "2275" _null_ _null_ _null_ _null_ date_in _null_ _null_ _null_ ));
 DESCR("I/O");
 DATA(insert OID = 1085 (  date_out        PGNSP PGUID 12 1 0 0 f f f t f s 1 0 2275 "1082" _null_ _null_ _null_ _null_ date_out _null_ _null_ _null_ ));
@@ -3381,6 +3381,8 @@ DATA(insert OID = 3768 (  pg_ts_template_is_visible PGNSP PGUID 12 1 0 0 f f f t
 DESCR("is text search template visible in search path?");
 DATA(insert OID = 3758 (  pg_ts_config_is_visible  PGNSP PGUID 12 1 0 0 f f f t f s 1 0 16 "26" _null_ _null_ _null_ _null_ pg_ts_config_is_visible _null_ _null_ _null_ ));
 DESCR("is text search configuration visible in search path?");
+DATA(insert OID = 3815 (  pg_collation_is_visible  PGNSP PGUID 12 1 0 0 f f f t f s 1 0 16 "26" _null_ _null_ _null_ _null_ pg_collation_is_visible _null_ _null_ _null_ ));
+DESCR("is collation visible in search path?");
 
 DATA(insert OID = 2854 (  pg_my_temp_schema            PGNSP PGUID 12 1 0 0 f f f t f s 0 0 26 "" _null_ _null_ _null_ _null_ pg_my_temp_schema _null_ _null_ _null_ ));
 DESCR("get OID of current session's temp schema, if any");
index 620f7b4612b61746df2f0121e20c329886d0e044..d82078ec0bad915974b077e8feba4515698d7121 100644 (file)
@@ -193,6 +193,13 @@ CATALOG(pg_type,1247) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71) BKI_SCHEMA_MACRO
     */
    int4        typndims;
 
+   /*
+    * Collation: 0 if type cannot use collations,
+    * DEFAULT_COLLATION_OID for collatable base types, possibly other
+    * OID for domains
+    */
+   Oid         typcollation;
+
    /*
     * If typdefaultbin is not NULL, it is the nodeToString representation of
     * a default expression for the type.  Currently this is only used for
@@ -223,7 +230,7 @@ typedef FormData_pg_type *Form_pg_type;
  *     compiler constants for pg_type
  * ----------------
  */
-#define Natts_pg_type                  28
+#define Natts_pg_type                  29
 #define Anum_pg_type_typname           1
 #define Anum_pg_type_typnamespace      2
 #define Anum_pg_type_typowner          3
@@ -250,8 +257,9 @@ typedef FormData_pg_type *Form_pg_type;
 #define Anum_pg_type_typbasetype       24
 #define Anum_pg_type_typtypmod         25
 #define Anum_pg_type_typndims          26
-#define Anum_pg_type_typdefaultbin     27
-#define Anum_pg_type_typdefault            28
+#define Anum_pg_type_typcollation      27
+#define Anum_pg_type_typdefaultbin     28
+#define Anum_pg_type_typdefault            29
 
 
 /* ----------------
@@ -268,87 +276,87 @@ typedef FormData_pg_type *Form_pg_type;
  */
 
 /* OIDS 1 - 99 */
-DATA(insert OID = 16 ( bool       PGNSP PGUID  1 t b B t t \054 0   0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 16 ( bool       PGNSP PGUID  1 t b B t t \054 0   0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 _null_ _null_ ));
 DESCR("boolean, 'true'/'false'");
 #define BOOLOID            16
 
-DATA(insert OID = 17 ( bytea      PGNSP PGUID -1 f b U f t \054 0  0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 17 ( bytea      PGNSP PGUID -1 f b U f t \054 0  0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 _null_ _null_ ));
 DESCR("variable-length string, binary values escaped");
 #define BYTEAOID       17
 
-DATA(insert OID = 18 ( char       PGNSP PGUID  1 t b S f t \054 0   0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 18 ( char       PGNSP PGUID  1 t b S f t \054 0   0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 _null_ _null_ ));
 DESCR("single character");
 #define CHAROID            18
 
-DATA(insert OID = 19 ( name       PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 19 ( name       PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 _null_ _null_ ));
 DESCR("63-character type for storing system identifiers");
 #define NAMEOID            19
 
-DATA(insert OID = 20 ( int8       PGNSP PGUID  8 FLOAT8PASSBYVAL b N f t \054 0     0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 20 ( int8       PGNSP PGUID  8 FLOAT8PASSBYVAL b N f t \054 0     0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 _null_ _null_ ));
 DESCR("~18 digit integer, 8-byte storage");
 #define INT8OID            20
 
-DATA(insert OID = 21 ( int2       PGNSP PGUID  2 t b N f t \054 0   0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 21 ( int2       PGNSP PGUID  2 t b N f t \054 0   0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 _null_ _null_ ));
 DESCR("-32 thousand to 32 thousand, 2-byte storage");
 #define INT2OID            21
 
-DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0  21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0  21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("array of int2, used in system tables");
 #define INT2VECTOROID  22
 
-DATA(insert OID = 23 ( int4       PGNSP PGUID  4 t b N f t \054 0   0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 23 ( int4       PGNSP PGUID  4 t b N f t \054 0   0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("-2 billion to 2 billion integer, 4-byte storage");
 #define INT4OID            23
 
-DATA(insert OID = 24 ( regproc    PGNSP PGUID  4 t b N f t \054 0   0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 24 ( regproc    PGNSP PGUID  4 t b N f t \054 0   0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("registered procedure");
 #define REGPROCOID     24
 
-DATA(insert OID = 25 ( text       PGNSP PGUID -1 f b S t t \054 0  0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 25 ( text       PGNSP PGUID -1 f b S t t \054 0  0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 100 _null_ _null_ ));
 DESCR("variable-length string, no limit specified");
 #define TEXTOID            25
 
-DATA(insert OID = 26 ( oid        PGNSP PGUID  4 t b N t t \054 0   0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 26 ( oid        PGNSP PGUID  4 t b N t t \054 0   0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("object identifier(oid), maximum 4 billion");
 #define OIDOID         26
 
-DATA(insert OID = 27 ( tid        PGNSP PGUID  6 f b U f t \054 0   0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 27 ( tid        PGNSP PGUID  6 f b U f t \054 0   0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 _null_ _null_ ));
 DESCR("(block, offset), physical location of tuple");
 #define TIDOID     27
 
-DATA(insert OID = 28 ( xid        PGNSP PGUID  4 t b U f t \054 0   0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 28 ( xid        PGNSP PGUID  4 t b U f t \054 0   0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("transaction id");
 #define XIDOID 28
 
-DATA(insert OID = 29 ( cid        PGNSP PGUID  4 t b U f t \054 0   0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 29 ( cid        PGNSP PGUID  4 t b U f t \054 0   0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("command identifier type, sequence in transaction id");
 #define CIDOID 29
 
-DATA(insert OID = 30 ( oidvector  PGNSP PGUID -1 f b A f t \054 0  26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 30 ( oidvector  PGNSP PGUID -1 f b A f t \054 0  26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("array of oids, used in system tables");
 #define OIDVECTOROID   30
 
 /* hand-built rowtype entries for bootstrapped catalogs */
 /* NB: OIDs assigned here must match the BKI_ROWTYPE_OID declarations */
 
-DATA(insert OID = 71 ( pg_type         PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 75 ( pg_attribute    PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 81 ( pg_proc         PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 83 ( pg_class        PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 71 ( pg_type         PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 75 ( pg_attribute    PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 81 ( pg_proc         PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 83 ( pg_class        PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
 
 /* OIDS 100 - 199 */
-DATA(insert OID = 142 ( xml           PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 142 ( xml           PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 _null_ _null_ ));
 DESCR("XML content");
 #define XMLOID 142
-DATA(insert OID = 143 ( _xml      PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 143 ( _xml      PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
 
-DATA(insert OID = 194 (    pg_node_tree    PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 194 (    pg_node_tree    PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 100 _null_ _null_ ));
 DESCR("string representing an internal node tree");
 #define PGNODETREEOID  194
 
 /* OIDS 200 - 299 */
 
-DATA(insert OID = 210 (  smgr     PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 210 (  smgr     PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 _null_ _null_ ));
 DESCR("storage manager");
 
 /* OIDS 300 - 399 */
@@ -358,231 +366,231 @@ DESCR("storage manager");
 /* OIDS 500 - 599 */
 
 /* OIDS 600 - 699 */
-DATA(insert OID = 600 (  point    PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 600 (  point    PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 _null_ _null_ ));
 DESCR("geometric point '(x, y)'");
 #define POINTOID       600
-DATA(insert OID = 601 (  lseg     PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 601 (  lseg     PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 _null_ _null_ ));
 DESCR("geometric line segment '(pt1,pt2)'");
 #define LSEGOID            601
-DATA(insert OID = 602 (  path     PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 602 (  path     PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 _null_ _null_ ));
 DESCR("geometric path '(pt1,...)'");
 #define PATHOID            602
-DATA(insert OID = 603 (  box      PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 603 (  box      PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 _null_ _null_ ));
 DESCR("geometric box '(lower left,upper right)'");
 #define BOXOID         603
-DATA(insert OID = 604 (  polygon   PGNSP PGUID -1 f b G f t \054 0  0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 604 (  polygon   PGNSP PGUID -1 f b G f t \054 0  0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 _null_ _null_ ));
 DESCR("geometric polygon '(pt1,...)'");
 #define POLYGONOID     604
 
-DATA(insert OID = 628 (  line     PGNSP PGUID 32 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 628 (  line     PGNSP PGUID 32 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 _null_ _null_ ));
 DESCR("geometric line (not implemented)");
 #define LINEOID            628
-DATA(insert OID = 629 (  _line    PGNSP PGUID  -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 629 (  _line    PGNSP PGUID  -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
 DESCR("");
 
 /* OIDS 700 - 799 */
 
-DATA(insert OID = 700 (  float4    PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0     0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 700 (  float4    PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0     0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("single-precision floating point number, 4-byte storage");
 #define FLOAT4OID 700
-DATA(insert OID = 701 (  float8    PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0     0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 701 (  float8    PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0     0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 _null_ _null_ ));
 DESCR("double-precision floating point number, 8-byte storage");
 #define FLOAT8OID 701
-DATA(insert OID = 702 (  abstime   PGNSP PGUID 4 t b D f t \054 0   0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 702 (  abstime   PGNSP PGUID 4 t b D f t \054 0   0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("absolute, limited-range date and time (Unix system time)");
 #define ABSTIMEOID     702
-DATA(insert OID = 703 (  reltime   PGNSP PGUID 4 t b T f t \054 0   0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 703 (  reltime   PGNSP PGUID 4 t b T f t \054 0   0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("relative, limited-range time interval (Unix delta time)");
 #define RELTIMEOID     703
-DATA(insert OID = 704 (  tinterval PGNSP PGUID 12 f b T f t \054 0  0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 704 (  tinterval PGNSP PGUID 12 f b T f t \054 0  0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("(abstime,abstime), time interval");
 #define TINTERVALOID   704
-DATA(insert OID = 705 (  unknown   PGNSP PGUID -2 f b X f t \054 0  0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 705 (  unknown   PGNSP PGUID -2 f b X f t \054 0  0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 _null_ _null_ ));
 DESCR("");
 #define UNKNOWNOID     705
 
-DATA(insert OID = 718 (  circle    PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 718 (  circle    PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 _null_ _null_ ));
 DESCR("geometric circle '(center,radius)'");
 #define CIRCLEOID      718
-DATA(insert OID = 719 (  _circle   PGNSP PGUID -1 f b A f t \054 0  718 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 790 (  money    PGNSP PGUID   8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 719 (  _circle   PGNSP PGUID -1 f b A f t \054 0  718 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 790 (  money    PGNSP PGUID   8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 _null_ _null_ ));
 DESCR("monetary amounts, $d,ddd.cc");
 #define CASHOID 790
-DATA(insert OID = 791 (  _money    PGNSP PGUID -1 f b A f t \054 0  790 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 791 (  _money    PGNSP PGUID -1 f b A f t \054 0  790 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
 
 /* OIDS 800 - 899 */
-DATA(insert OID = 829 ( macaddr    PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 829 ( macaddr    PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("XX:XX:XX:XX:XX:XX, MAC address");
 #define MACADDROID 829
-DATA(insert OID = 869 ( inet      PGNSP PGUID  -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 869 ( inet      PGNSP PGUID  -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 _null_ _null_ ));
 DESCR("IP address/netmask, host address, netmask optional");
 #define INETOID 869
-DATA(insert OID = 650 ( cidr      PGNSP PGUID  -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 650 ( cidr      PGNSP PGUID  -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 _null_ _null_ ));
 DESCR("network IP address/netmask, network address");
 #define CIDROID 650
 
 /* OIDS 900 - 999 */
 
 /* OIDS 1000 - 1099 */
-DATA(insert OID = 1000 (  _bool         PGNSP PGUID -1 f b A f t \054 0    16 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1001 (  _bytea    PGNSP PGUID -1 f b A f t \054 0    17 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1002 (  _char         PGNSP PGUID -1 f b A f t \054 0    18 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1003 (  _name         PGNSP PGUID -1 f b A f t \054 0    19 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1005 (  _int2         PGNSP PGUID -1 f b A f t \054 0    21 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1006 (  _int2vector PGNSP PGUID -1 f b A f t \054 0  22 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1007 (  _int4         PGNSP PGUID -1 f b A f t \054 0    23 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1000 (  _bool         PGNSP PGUID -1 f b A f t \054 0    16 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1001 (  _bytea    PGNSP PGUID -1 f b A f t \054 0    17 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1002 (  _char         PGNSP PGUID -1 f b A f t \054 0    18 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1003 (  _name         PGNSP PGUID -1 f b A f t \054 0    19 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1005 (  _int2         PGNSP PGUID -1 f b A f t \054 0    21 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1006 (  _int2vector PGNSP PGUID -1 f b A f t \054 0  22 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1007 (  _int4         PGNSP PGUID -1 f b A f t \054 0    23 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
 #define INT4ARRAYOID       1007
-DATA(insert OID = 1008 (  _regproc  PGNSP PGUID -1 f b A f t \054 0    24 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1009 (  _text         PGNSP PGUID -1 f b A f t \054 0    25 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1008 (  _regproc  PGNSP PGUID -1 f b A f t \054 0    24 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1009 (  _text         PGNSP PGUID -1 f b A f t \054 0    25 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 100 _null_ _null_ ));
 #define TEXTARRAYOID       1009
-DATA(insert OID = 1028 (  _oid      PGNSP PGUID -1 f b A f t \054 0    26 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1010 (  _tid      PGNSP PGUID -1 f b A f t \054 0    27 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1011 (  _xid      PGNSP PGUID -1 f b A f t \054 0    28 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1012 (  _cid      PGNSP PGUID -1 f b A f t \054 0    29 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1013 (  _oidvector PGNSP PGUID -1 f b A f t \054 0   30 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1014 (  _bpchar   PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1015 (  _varchar  PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1016 (  _int8         PGNSP PGUID -1 f b A f t \054 0    20 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1017 (  _point    PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1018 (  _lseg         PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1019 (  _path         PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1020 (  _box      PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1021 (  _float4   PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1028 (  _oid      PGNSP PGUID -1 f b A f t \054 0    26 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1010 (  _tid      PGNSP PGUID -1 f b A f t \054 0    27 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1011 (  _xid      PGNSP PGUID -1 f b A f t \054 0    28 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1012 (  _cid      PGNSP PGUID -1 f b A f t \054 0    29 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1013 (  _oidvector PGNSP PGUID -1 f b A f t \054 0   30 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1014 (  _bpchar   PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout - i x f 0 -1 0 100 _null_ _null_ ));
+DATA(insert OID = 1015 (  _varchar  PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout - i x f 0 -1 0 100 _null_ _null_ ));
+DATA(insert OID = 1016 (  _int8         PGNSP PGUID -1 f b A f t \054 0    20 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1017 (  _point    PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1018 (  _lseg         PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1019 (  _path         PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1020 (  _box      PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1021 (  _float4   PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
 #define FLOAT4ARRAYOID 1021
-DATA(insert OID = 1022 (  _float8   PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1023 (  _abstime  PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1024 (  _reltime  PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1025 (  _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1027 (  _polygon  PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1033 (  aclitem   PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1022 (  _float8   PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1023 (  _abstime  PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1024 (  _reltime  PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1025 (  _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1027 (  _polygon  PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1033 (  aclitem   PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("access control list");
 #define ACLITEMOID     1033
-DATA(insert OID = 1034 (  _aclitem  PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1040 (  _macaddr  PGNSP PGUID -1 f b A f t \054 0  829 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1041 (  _inet         PGNSP PGUID -1 f b A f t \054 0  869 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 651  (  _cidr         PGNSP PGUID -1 f b A f t \054 0  650 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1263 (  _cstring  PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1034 (  _aclitem  PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1040 (  _macaddr  PGNSP PGUID -1 f b A f t \054 0  829 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1041 (  _inet         PGNSP PGUID -1 f b A f t \054 0  869 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 651  (  _cidr         PGNSP PGUID -1 f b A f t \054 0  650 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1263 (  _cstring  PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
 #define CSTRINGARRAYOID        1263
 
-DATA(insert OID = 1042 ( bpchar         PGNSP PGUID -1 f b S f t \054 0    0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1042 ( bpchar         PGNSP PGUID -1 f b S f t \054 0    0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 100 _null_ _null_ ));
 DESCR("char(length), blank-padded string, fixed storage length");
 #define BPCHAROID      1042
-DATA(insert OID = 1043 ( varchar    PGNSP PGUID -1 f b S f t \054 0    0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1043 ( varchar    PGNSP PGUID -1 f b S f t \054 0    0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 100 _null_ _null_ ));
 DESCR("varchar(length), non-blank-padded string, variable storage length");
 #define VARCHAROID     1043
 
-DATA(insert OID = 1082 ( date       PGNSP PGUID    4 t b D f t \054 0  0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1082 ( date       PGNSP PGUID    4 t b D f t \054 0  0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("date");
 #define DATEOID            1082
-DATA(insert OID = 1083 ( time       PGNSP PGUID    8 FLOAT8PASSBYVAL b D f t \054 0    0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1083 ( time       PGNSP PGUID    8 FLOAT8PASSBYVAL b D f t \054 0    0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 _null_ _null_ ));
 DESCR("time of day");
 #define TIMEOID            1083
 
 /* OIDS 1100 - 1199 */
-DATA(insert OID = 1114 ( timestamp  PGNSP PGUID    8 FLOAT8PASSBYVAL b D f t \054 0    0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1114 ( timestamp  PGNSP PGUID    8 FLOAT8PASSBYVAL b D f t \054 0    0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 _null_ _null_ ));
 DESCR("date and time");
 #define TIMESTAMPOID   1114
-DATA(insert OID = 1115 ( _timestamp  PGNSP PGUID   -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1182 ( _date      PGNSP PGUID    -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1183 ( _time      PGNSP PGUID    -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1184 ( timestamptz PGNSP PGUID   8 FLOAT8PASSBYVAL b D t t \054 0    0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1115 ( _timestamp  PGNSP PGUID   -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1182 ( _date      PGNSP PGUID    -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1183 ( _time      PGNSP PGUID    -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1184 ( timestamptz PGNSP PGUID   8 FLOAT8PASSBYVAL b D t t \054 0    0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 _null_ _null_ ));
 DESCR("date and time with time zone");
 #define TIMESTAMPTZOID 1184
-DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0  1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1186 ( interval   PGNSP PGUID 16 f b T t t \054 0    0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0  1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1186 ( interval   PGNSP PGUID 16 f b T t t \054 0    0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 _null_ _null_ ));
 DESCR("@  , time interval");
 #define INTERVALOID        1186
-DATA(insert OID = 1187 ( _interval  PGNSP PGUID    -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1187 ( _interval  PGNSP PGUID    -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout - d x f 0 -1 0 _null_ _null_ ));
 
 /* OIDS 1200 - 1299 */
-DATA(insert OID = 1231 (  _numeric  PGNSP PGUID -1 f b A f t \054 0    1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1266 ( timetz         PGNSP PGUID 12 f b D f t \054 0    0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1231 (  _numeric  PGNSP PGUID -1 f b A f t \054 0    1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1266 ( timetz         PGNSP PGUID 12 f b D f t \054 0    0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 _null_ _null_ ));
 DESCR("time of day with time zone");
 #define TIMETZOID      1266
-DATA(insert OID = 1270 ( _timetz    PGNSP PGUID -1 f b A f t \054 0    1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1270 ( _timetz    PGNSP PGUID -1 f b A f t \054 0    1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout - d x f 0 -1 0 _null_ _null_ ));
 
 /* OIDS 1500 - 1599 */
-DATA(insert OID = 1560 ( bit        PGNSP PGUID -1 f b V f t \054 0    0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1560 ( bit        PGNSP PGUID -1 f b V f t \054 0    0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 _null_ _null_ ));
 DESCR("fixed-length bit string");
 #define BITOID  1560
-DATA(insert OID = 1561 ( _bit       PGNSP PGUID -1 f b A f t \054 0    1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1562 ( varbit         PGNSP PGUID -1 f b V t t \054 0    0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1561 ( _bit       PGNSP PGUID -1 f b A f t \054 0    1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1562 ( varbit         PGNSP PGUID -1 f b V t t \054 0    0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 _null_ _null_ ));
 DESCR("variable-length bit string");
 #define VARBITOID    1562
-DATA(insert OID = 1563 ( _varbit    PGNSP PGUID -1 f b A f t \054 0    1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1563 ( _varbit    PGNSP PGUID -1 f b A f t \054 0    1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout - i x f 0 -1 0 _null_ _null_ ));
 
 /* OIDS 1600 - 1699 */
 
 /* OIDS 1700 - 1799 */
-DATA(insert OID = 1700 ( numeric      PGNSP PGUID -1 f b N f t \054 0  0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1700 ( numeric      PGNSP PGUID -1 f b N f t \054 0  0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 _null_ _null_ ));
 DESCR("numeric(precision, decimal), arbitrary precision number");
 #define NUMERICOID     1700
 
-DATA(insert OID = 1790 ( refcursor    PGNSP PGUID -1 f b U f t \054 0  0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1790 ( refcursor    PGNSP PGUID -1 f b U f t \054 0  0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 _null_ _null_ ));
 DESCR("reference to cursor (portal name)");
 #define REFCURSOROID   1790
 
 /* OIDS 2200 - 2299 */
-DATA(insert OID = 2201 ( _refcursor    PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2201 ( _refcursor    PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
 
-DATA(insert OID = 2202 ( regprocedure  PGNSP PGUID 4 t b N f t \054 0   0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2202 ( regprocedure  PGNSP PGUID 4 t b N f t \054 0   0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("registered procedure (with args)");
 #define REGPROCEDUREOID 2202
 
-DATA(insert OID = 2203 ( regoper      PGNSP PGUID  4 t b N f t \054 0   0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2203 ( regoper      PGNSP PGUID  4 t b N f t \054 0   0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("registered operator");
 #define REGOPEROID     2203
 
-DATA(insert OID = 2204 ( regoperator   PGNSP PGUID 4 t b N f t \054 0   0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2204 ( regoperator   PGNSP PGUID 4 t b N f t \054 0   0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("registered operator (with args)");
 #define REGOPERATOROID 2204
 
-DATA(insert OID = 2205 ( regclass     PGNSP PGUID  4 t b N f t \054 0   0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2205 ( regclass     PGNSP PGUID  4 t b N f t \054 0   0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("registered class");
 #define REGCLASSOID        2205
 
-DATA(insert OID = 2206 ( regtype      PGNSP PGUID  4 t b N f t \054 0   0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2206 ( regtype      PGNSP PGUID  4 t b N f t \054 0   0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("registered type");
 #define REGTYPEOID     2206
 
-DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 2208 ( _regoper     PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 2209 ( _regoperator  PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 2210 ( _regclass    PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 2211 ( _regtype     PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2208 ( _regoper     PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2209 ( _regoperator  PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2210 ( _regclass    PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2211 ( _regtype     PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
 #define REGTYPEARRAYOID 2211
 
 /* uuid */
-DATA(insert OID = 2950 ( uuid          PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2950 ( uuid          PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 _null_ _null_ ));
 DESCR("UUID datatype");
-DATA(insert OID = 2951 ( _uuid         PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2951 ( _uuid         PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
 
 /* text search */
-DATA(insert OID = 3614 ( tsvector      PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3614 ( tsvector      PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 _null_ _null_ ));
 DESCR("text representation for text search");
 #define TSVECTOROID        3614
-DATA(insert OID = 3642 ( gtsvector     PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3642 ( gtsvector     PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("GiST index internal text representation for text search");
 #define GTSVECTOROID   3642
-DATA(insert OID = 3615 ( tsquery       PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3615 ( tsquery       PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("query representation for text search");
 #define TSQUERYOID     3615
-DATA(insert OID = 3734 ( regconfig     PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3734 ( regconfig     PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("registered text search configuration");
 #define REGCONFIGOID   3734
-DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("registered text search dictionary");
 #define REGDICTIONARYOID   3769
 
-DATA(insert OID = 3643 ( _tsvector     PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 3644 ( _gtsvector        PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 3645 ( _tsquery      PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 3735 ( _regconfig        PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3643 ( _tsvector     PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3644 ( _gtsvector        PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3645 ( _tsquery      PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3735 ( _regconfig        PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
 
-DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 _null_ _null_ ));
 DESCR("txid snapshot");
-DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
 
 /*
  * pseudo-types
@@ -597,31 +605,31 @@ DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 a
  * but there is now support for it in records and arrays.  Perhaps we should
  * just treat it as a regular base type?
  */
-DATA(insert OID = 2249 ( record            PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2249 ( record            PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
 #define RECORDOID      2249
-DATA(insert OID = 2287 ( _record       PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2287 ( _record       PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
 #define RECORDARRAYOID 2287
-DATA(insert OID = 2275 ( cstring       PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2275 ( cstring       PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 _null_ _null_ ));
 #define CSTRINGOID     2275
-DATA(insert OID = 2276 ( any           PGNSP PGUID  4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2276 ( any           PGNSP PGUID  4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 _null_ _null_ ));
 #define ANYOID         2276
-DATA(insert OID = 2277 ( anyarray      PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2277 ( anyarray      PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 _null_ _null_ ));
 #define ANYARRAYOID        2277
-DATA(insert OID = 2278 ( void          PGNSP PGUID  4 t p P f t \054 0 0 0 void_in void_out - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2278 ( void          PGNSP PGUID  4 t p P f t \054 0 0 0 void_in void_out - - - - - i p f 0 -1 0 _null_ _null_ ));
 #define VOIDOID            2278
-DATA(insert OID = 2279 ( trigger       PGNSP PGUID  4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2279 ( trigger       PGNSP PGUID  4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 _null_ _null_ ));
 #define TRIGGEROID     2279
-DATA(insert OID = 2280 ( language_handler  PGNSP PGUID  4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2280 ( language_handler  PGNSP PGUID  4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 _null_ _null_ ));
 #define LANGUAGE_HANDLEROID        2280
-DATA(insert OID = 2281 ( internal      PGNSP PGUID  SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2281 ( internal      PGNSP PGUID  SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 _null_ _null_ ));
 #define INTERNALOID        2281
-DATA(insert OID = 2282 ( opaque            PGNSP PGUID  4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2282 ( opaque            PGNSP PGUID  4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 _null_ _null_ ));
 #define OPAQUEOID      2282
-DATA(insert OID = 2283 ( anyelement        PGNSP PGUID  4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2283 ( anyelement        PGNSP PGUID  4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 _null_ _null_ ));
 #define ANYELEMENTOID  2283
-DATA(insert OID = 2776 ( anynonarray   PGNSP PGUID  4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2776 ( anynonarray   PGNSP PGUID  4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 _null_ _null_ ));
 #define ANYNONARRAYOID 2776
-DATA(insert OID = 3500 ( anyenum       PGNSP PGUID  4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3500 ( anyenum       PGNSP PGUID  4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 _null_ _null_ ));
 #define ANYENUMOID     3500
 
 
index 836e21617818eb04fa8ae11f1495e67acc34d9d9..81508698db3455524b995bfa1d83dd91cc8faad0 100644 (file)
@@ -50,7 +50,8 @@ extern Oid TypeCreate(Oid newTypeOid,
           char storage,
           int32 typeMod,
           int32 typNDims,
-          bool typeNotNull);
+          bool typeNotNull,
+          Oid typeCollation);
 
 extern void GenerateTypeDependencies(Oid typeNamespace,
                         Oid typeObjectId,
index 93f894e639d8a7372006011a418aee10d2f61a46..3ad7b4bd05495c465925ae8cd5067efa37c57eb9 100644 (file)
@@ -66,12 +66,13 @@ typedef struct VacAttrStats
     * Note: do not assume that the data being analyzed has the same datatype
     * shown in attr, ie do not trust attr->atttypid, attlen, etc.  This is
     * because some index opclasses store a different type than the underlying
-    * column/expression.  Instead use attrtypid, attrtypmod, and attrtype for
+    * column/expression.  Instead use attrtypid, attrtypmod, attrcollation, and attrtype for
     * information about the datatype being fed to the typanalyze function.
     */
    Form_pg_attribute attr;     /* copy of pg_attribute row for column */
    Oid         attrtypid;      /* type of data being analyzed */
    int32       attrtypmod;     /* typmod of data being analyzed */
+   Oid         attrcollation;  /* collation of the data being analyzed */
    Form_pg_type attrtype;      /* copy of pg_type row for attrtypid */
    MemoryContext anl_context;  /* where to save long-lived data */
 
index 33369b02bc11d132c8e72acec8891d57427bbd1d..9720117a7f54aba6d1f403259ccffd23ec6fffd6 100644 (file)
@@ -50,6 +50,7 @@ typedef struct FmgrInfo
    bool        fn_strict;      /* function is "strict" (NULL in => NULL out) */
    bool        fn_retset;      /* function returns a set */
    unsigned char fn_stats;     /* collect stats if track_functions > this */
+   Oid         fn_collation;   /* collation to use */
    void       *fn_extra;       /* extra space for use by handler */
    MemoryContext fn_mcxt;      /* memory context to store fn_extra in */
    fmNodePtr   fn_expr;        /* expression parse tree for call, or NULL */
@@ -83,6 +84,16 @@ extern void fmgr_info(Oid functionId, FmgrInfo *finfo);
 extern void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo,
              MemoryContext mcxt);
 
+/*
+ * Initialize the fn_collation field
+ */
+extern void fmgr_info_collation(Oid collationId, FmgrInfo *finfo);
+
+/*
+ * Initialize the fn_expr field and set the collation based on it
+ */
+extern void fmgr_info_expr(fmNodePtr expr, FmgrInfo *finfo);
+
 /*
  * Copy an FmgrInfo struct
  */
@@ -296,6 +307,7 @@ extern struct varlena *pg_detoast_datum_packed(struct varlena * datum);
 #define PG_RETURN_VARCHAR_P(x) PG_RETURN_POINTER(x)
 #define PG_RETURN_HEAPTUPLEHEADER(x)  PG_RETURN_POINTER(x)
 
+#define PG_GET_COLLATION()     (fcinfo->flinfo ? fcinfo->flinfo->fn_collation : InvalidOid)
 
 /*-------------------------------------------------------------------------
  *     Support for detecting call convention of dynamically-loaded functions
@@ -438,6 +450,12 @@ extern Datum DirectFunctionCall9(PGFunction func, Datum arg1, Datum arg2,
                    Datum arg6, Datum arg7, Datum arg8,
                    Datum arg9);
 
+/* the same but passing a collation */
+extern Datum DirectFunctionCall1WithCollation(PGFunction func, Oid collation,
+                                             Datum arg1);
+extern Datum DirectFunctionCall2WithCollation(PGFunction func, Oid collation,
+                                             Datum arg1, Datum arg2);
+
 /* These are for invocation of a previously-looked-up function with a
  * directly-computed parameter list.  Note that neither arguments nor result
  * are allowed to be NULL.
index f110723da167be1c599a63ba9d61edae9d281497..565b53b3e6ecaa89d362bd3f28b760c97115ce2a 100644 (file)
@@ -393,8 +393,8 @@ extern int  pg_encoding_max_length(int encoding);
 extern int pg_database_encoding_max_length(void);
 
 #ifdef USE_WIDE_UPPER_LOWER
-extern size_t wchar2char(char *to, const wchar_t *from, size_t tolen);
-extern size_t char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen);
+extern size_t wchar2char(char *to, const wchar_t *from, size_t tolen, Oid collation);
+extern size_t char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen, Oid collation);
 #endif
 
 extern int SetClientEncoding(int encoding, bool doit);
index 7c41312587708bba5652d2b68038c9a48940a695..8b7db798b9cd0d087d4960c9557891f5033990c7 100644 (file)
@@ -27,6 +27,7 @@ extern Var *makeVar(Index varno,
        AttrNumber varattno,
        Oid vartype,
        int32 vartypmod,
+       Oid varcollid,
        Index varlevelsup);
 
 extern Var *makeVarFromTargetEntry(Index varno,
@@ -67,10 +68,10 @@ extern RangeVar *makeRangeVar(char *schemaname, char *relname, int location);
 
 extern TypeName *makeTypeName(char *typnam);
 extern TypeName *makeTypeNameFromNameList(List *names);
-extern TypeName *makeTypeNameFromOid(Oid typeOid, int32 typmod);
+extern TypeName *makeTypeNameFromOid(Oid typeOid, int32 typmod, Oid collOid);
 
 extern FuncExpr *makeFuncExpr(Oid funcid, Oid rettype,
-            List *args, CoercionForm fformat);
+            List *args, Oid collid, CoercionForm fformat);
 
 extern DefElem *makeDefElem(char *name, Node *arg);
 extern DefElem *makeDefElemExtended(char *nameSpace, char *name, Node *arg,
index 433fbfe857e722fa4067c117a0e3a0f4cec7bcd1..32f09f68ad1e68cbd735a78af02d30d492a6241f 100644 (file)
@@ -27,6 +27,8 @@
 
 extern Oid exprType(Node *expr);
 extern int32 exprTypmod(Node *expr);
+extern Oid exprCollation(Node *expr);
+extern Oid coercion_expression_result_collation(Oid resulttype, Node *arg);
 extern bool exprIsLengthCoercion(Node *expr, int32 *coercedTypmod);
 extern bool expression_returns_set(Node *clause);
 
index 456e8e216aa8a2a10bdf94b2aa272cc3af2d5b89..63b9dd80be9ab20e2b601e5313aa42ea97eb99fd 100644 (file)
@@ -166,6 +166,7 @@ typedef enum NodeTag
    T_JoinExpr,
    T_FromExpr,
    T_IntoClause,
+   T_CollateClause,
 
    /*
     * TAGS FOR EXPRESSION STATE NODES (execnodes.h)
index 978e7b7921291d507b1e129fa19bcdff6dfbe9cc..b68fe10990a114d9319649bbdc7a50c6d53040d7 100644 (file)
@@ -167,7 +167,8 @@ typedef struct Query
  * specify the type by OID than by name.  If "names" is NIL then the
  * actual type OID is given by typeOid, otherwise typeOid is unused.
  * Similarly, if "typmods" is NIL then the actual typmod is expected to
- * be prespecified in typemod, otherwise typemod is unused.
+ * be prespecified in typemod, otherwise typemod is unused.  Similarly
+ * for collnames/collOid.
  *
  * If pct_type is TRUE, then names is actually a field name and we look up
  * the type of that field. Otherwise (the normal case), names is a type
@@ -183,6 +184,8 @@ typedef struct TypeName
    List       *typmods;        /* type modifier expression(s) */
    int32       typemod;        /* prespecified type modifier */
    List       *arrayBounds;    /* array bounds */
+   List       *collnames;      /* collation name */
+   Oid         collOid;        /* collation by OID */
    int         location;       /* token location, or -1 if unknown */
 } TypeName;
 
@@ -517,6 +520,7 @@ typedef struct IndexElem
    char       *name;           /* name of attribute to index, or NULL */
    Node       *expr;           /* expression to index, or NULL */
    char       *indexcolname;   /* name for index column; NULL = default */
+   List       *collation;      /* name of collation; NIL = default */
    List       *opclass;        /* name of desired opclass; NIL = default */
    SortByDir   ordering;       /* ASC/DESC/default */
    SortByNulls nulls_ordering; /* FIRST/LAST/default */
@@ -702,12 +706,14 @@ typedef struct RangeTblEntry
     * Fields valid for a function RTE (else NULL):
     *
     * If the function returns RECORD, funccoltypes lists the column types
-    * declared in the RTE's column type specification, and funccoltypmods
-    * lists their declared typmods.  Otherwise, both fields are NIL.
+    * declared in the RTE's column type specification, funccoltypmods
+    * lists their declared typmods, funccolcollations their collations.
+    * Otherwise, those fields are NIL.
     */
    Node       *funcexpr;       /* expression tree for func call */
    List       *funccoltypes;   /* OID list of column type OIDs */
    List       *funccoltypmods; /* integer list of column typmods */
+   List       *funccolcollations; /* OID list of column collation OIDs */
 
    /*
     * Fields valid for a values RTE (else NIL):
@@ -722,6 +728,7 @@ typedef struct RangeTblEntry
    bool        self_reference; /* is this a recursive self-reference? */
    List       *ctecoltypes;    /* OID list of column type OIDs */
    List       *ctecoltypmods;  /* integer list of column typmods */
+   List       *ctecolcollations; /* OID list of column collation OIDs */
 
    /*
     * Fields valid in all RTEs:
@@ -883,6 +890,7 @@ typedef struct CommonTableExpr
    List       *ctecolnames;    /* list of output column names */
    List       *ctecoltypes;    /* OID list of output column type OIDs */
    List       *ctecoltypmods;  /* integer list of output column typmods */
+   List       *ctecolcollations; /* OID list of column collation OIDs */
 } CommonTableExpr;
 
 /*****************************************************************************
@@ -1032,6 +1040,7 @@ typedef struct SetOperationStmt
    /* Fields derived during parse analysis: */
    List       *colTypes;       /* OID list of output column type OIDs */
    List       *colTypmods;     /* integer list of output column typmods */
+   List       *colCollations;  /* OID list of output column collation OIDs */
    List       *groupClauses;   /* a list of SortGroupClause's */
    /* groupClauses is NIL if UNION ALL, but must be set otherwise */
 } SetOperationStmt;
index 2d4ef52b13ba9cf8a845dbc2d67b1ec4c68e5d94..e5dd04bd1cebfdd9d19fdc6a09014148d1b0ac0d 100644 (file)
@@ -182,6 +182,15 @@ extern int list_length(List *l);
         (cell1) != NULL && (cell2) != NULL;                        \
         (cell1) = lnext(cell1), (cell2) = lnext(cell2))
 
+/*
+ * forthree -
+ *    the same for three lists
+ */
+#define forthree(cell1, list1, cell2, list2, cell3, list3)         \
+   for ((cell1) = list_head(list1), (cell2) = list_head(list2), (cell3) = list_head(list3); \
+        (cell1) != NULL && (cell2) != NULL && (cell3) != NULL;     \
+        (cell1) = lnext(cell1), (cell2) = lnext(cell2), (cell3) = lnext(cell3))
+
 extern List *lappend(List *list, void *datum);
 extern List *lappend_int(List *list, int datum);
 extern List *lappend_oid(List *list, Oid datum);
index 011c6869f27f27de8fc1ab2fc4326aac2f8530d0..6ba2f6005a2cad52cb6318ee2e9fbbfd8ea66824 100644 (file)
@@ -198,6 +198,7 @@ typedef struct MergeAppend
    int         numCols;        /* number of sort-key columns */
    AttrNumber *sortColIdx;     /* their indexes in the target list */
    Oid        *sortOperators;  /* OIDs of operators to sort them by */
+   Oid        *collations;     /* OIDs of collations */
    bool       *nullsFirst;     /* NULLS FIRST/LAST directions */
 } MergeAppend;
 
@@ -401,6 +402,7 @@ typedef struct FunctionScan
    List       *funccolnames;   /* output column names (string Value nodes) */
    List       *funccoltypes;   /* OID list of column type OIDs */
    List       *funccoltypmods; /* integer list of column typmods */
+   List       *funccolcollations;  /* OID list of column collation OIDs */
 } FunctionScan;
 
 /* ----------------
@@ -503,6 +505,7 @@ typedef struct MergeJoin
    List       *mergeclauses;   /* mergeclauses as expression trees */
    /* these are arrays, but have the same length as the mergeclauses list: */
    Oid        *mergeFamilies;  /* per-clause OIDs of btree opfamilies */
+   Oid        *mergeCollations;    /* per-clause OIDs of collations */
    int        *mergeStrategies;    /* per-clause ordering (ASC or DESC) */
    bool       *mergeNullsFirst;    /* per-clause nulls ordering */
 } MergeJoin;
@@ -536,6 +539,7 @@ typedef struct Sort
    int         numCols;        /* number of sort-key columns */
    AttrNumber *sortColIdx;     /* their indexes in the target list */
    Oid        *sortOperators;  /* OIDs of operators to sort them by */
+   Oid        *collations;     /* OIDs of collations */
    bool       *nullsFirst;     /* NULLS FIRST/LAST directions */
 } Sort;
 
index f8a398dc83079605658af072c2c586a84caefba1..8c366df5f5619d27e80fb99011fc448425d202aa 100644 (file)
@@ -139,6 +139,7 @@ typedef struct Var
                                 * all */
    Oid         vartype;        /* pg_type OID for the type of this var */
    int32       vartypmod;      /* pg_attribute typmod value */
+   Oid         varcollid;      /* collation */
    Index       varlevelsup;    /* for subquery variables referencing outer
                                 * relations; 0 in a normal var, >0 means N
                                 * levels up */
@@ -155,6 +156,7 @@ typedef struct Const
    Expr        xpr;
    Oid         consttype;      /* pg_type OID of the constant's datatype */
    int32       consttypmod;    /* typmod value, if any */
+   Oid         constcollid;    /* collation */
    int         constlen;       /* typlen of the constant's datatype */
    Datum       constvalue;     /* the constant's value */
    bool        constisnull;    /* whether the constant is null (if true,
@@ -205,6 +207,7 @@ typedef struct Param
    int         paramid;        /* numeric ID for parameter */
    Oid         paramtype;      /* pg_type OID of parameter's datatype */
    int32       paramtypmod;    /* typmod value, if known */
+   Oid         paramcollation; /* parameter's collation */
    int         location;       /* token location, or -1 if unknown */
 } Param;
 
@@ -233,6 +236,7 @@ typedef struct Aggref
    List       *aggdistinct;    /* DISTINCT (list of SortGroupClause) */
    bool        aggstar;        /* TRUE if argument list was really '*' */
    Index       agglevelsup;    /* > 0 if agg belongs to outer query */
+   Oid         collid;         /* collation OID to use by function */
    int         location;       /* token location, or -1 if unknown */
 } Aggref;
 
@@ -248,6 +252,7 @@ typedef struct WindowFunc
    Index       winref;         /* index of associated WindowClause */
    bool        winstar;        /* TRUE if argument list was really '*' */
    bool        winagg;         /* is function a simple aggregate? */
+   Oid         collid;         /* collation OID to use by function */
    int         location;       /* token location, or -1 if unknown */
 } WindowFunc;
 
@@ -279,6 +284,7 @@ typedef struct ArrayRef
    Oid         refarraytype;   /* type of the array proper */
    Oid         refelemtype;    /* type of the array elements */
    int32       reftypmod;      /* typmod of the array (and elements too) */
+   Oid         refcollid;      /* collation */
    List       *refupperindexpr;/* expressions that evaluate to upper array
                                 * indexes */
    List       *reflowerindexpr;/* expressions that evaluate to lower array
@@ -324,6 +330,7 @@ typedef struct FuncExpr
    bool        funcretset;     /* true if function returns set */
    CoercionForm funcformat;    /* how to display this function call */
    List       *args;           /* arguments to the function */
+   Oid         collid;         /* collation OID to use by function */
    int         location;       /* token location, or -1 if unknown */
 } FuncExpr;
 
@@ -367,6 +374,7 @@ typedef struct OpExpr
    Oid         opresulttype;   /* PG_TYPE OID of result value */
    bool        opretset;       /* true if operator returns set */
    List       *args;           /* arguments to the operator (1 or 2) */
+   Oid         collid;         /* collation OID to use by operator */
    int         location;       /* token location, or -1 if unknown */
 } OpExpr;
 
@@ -399,6 +407,7 @@ typedef struct ScalarArrayOpExpr
    Oid         opfuncid;       /* PG_PROC OID of underlying function */
    bool        useOr;          /* true for ANY, false for ALL */
    List       *args;           /* the scalar and array operands */
+   Oid         collid;         /* collation OID to use by operator */
    int         location;       /* token location, or -1 if unknown */
 } ScalarArrayOpExpr;
 
@@ -544,6 +553,7 @@ typedef struct SubPlan
    /* Extra data useful for determining subplan's output type: */
    Oid         firstColType;   /* Type of first column of subplan result */
    int32       firstColTypmod; /* Typmod of first column of subplan result */
+   Oid         firstColCollation;  /* Collation of first column of subplan result */
    /* Information about execution strategy: */
    bool        useHashTable;   /* TRUE to store subselect output in a hash
                                 * table (implies we are doing "IN") */
@@ -592,6 +602,7 @@ typedef struct FieldSelect
    Oid         resulttype;     /* type of the field (result type of this
                                 * node) */
    int32       resulttypmod;   /* output typmod (usually -1) */
+   Oid         resultcollation;/* collation of the field */
 } FieldSelect;
 
 /* ----------------
@@ -642,6 +653,18 @@ typedef struct RelabelType
    int         location;       /* token location, or -1 if unknown */
 } RelabelType;
 
+/*
+ * CollateClause - COLLATE
+ */
+typedef struct CollateClause
+{
+   Expr        xpr;
+   Expr       *arg;            /* original expression */
+   List       *collnames;      /* assigned collation */
+   Oid         collOid;        /* resolved collation OID */
+   int         location;       /* token location, or -1 if unknown */
+} CollateClause;
+
 /* ----------------
  * CoerceViaIO
  *
@@ -733,6 +756,7 @@ typedef struct CaseExpr
 {
    Expr        xpr;
    Oid         casetype;       /* type of expression result */
+   Oid         casecollation;  /* collation of expression result */
    Expr       *arg;            /* implicit equality comparison argument */
    List       *args;           /* the arguments (list of WHEN clauses) */
    Expr       *defresult;      /* the default result (ELSE clause) */
@@ -763,6 +787,7 @@ typedef struct CaseTestExpr
    Expr        xpr;
    Oid         typeId;         /* type for substituted value */
    int32       typeMod;        /* typemod for substituted value */
+   Oid         collation;      /* collation for the substituted value */
 } CaseTestExpr;
 
 /*
@@ -850,6 +875,7 @@ typedef struct RowCompareExpr
    RowCompareType rctype;      /* LT LE GE or GT, never EQ or NE */
    List       *opnos;          /* OID list of pairwise comparison ops */
    List       *opfamilies;     /* OID list of containing operator families */
+   List       *collids;        /* OID list of collations for the comparisons */
    List       *largs;          /* the left-hand input arguments */
    List       *rargs;          /* the right-hand input arguments */
 } RowCompareExpr;
@@ -861,6 +887,7 @@ typedef struct CoalesceExpr
 {
    Expr        xpr;
    Oid         coalescetype;   /* type of expression result */
+   Oid         coalescecollation;  /* collation of expression result */
    List       *args;           /* the arguments */
    int         location;       /* token location, or -1 if unknown */
 } CoalesceExpr;
@@ -880,6 +907,7 @@ typedef struct MinMaxExpr
    Oid         minmaxtype;     /* common type of arguments and result */
    MinMaxOp    op;             /* function to execute */
    List       *args;           /* the arguments */
+   Oid         collid;         /* collation to use */
    int         location;       /* token location, or -1 if unknown */
 } MinMaxExpr;
 
@@ -1023,6 +1051,7 @@ typedef struct SetToDefault
    Expr        xpr;
    Oid         typeId;         /* type for substituted value */
    int32       typeMod;        /* typemod for substituted value */
+   Oid         collid;         /* collation for the substituted value */
    int         location;       /* token location, or -1 if unknown */
 } SetToDefault;
 
index 8b14838625a99d07b0bf53bd4abf8dfe6e9167ff..b3ca5b5e329f42551ab5c355d3a85ae3edeb6de6 100644 (file)
@@ -453,6 +453,7 @@ typedef struct IndexOptInfo
    int         ncolumns;       /* number of columns in index */
    Oid        *opfamily;       /* OIDs of operator families for columns */
    int        *indexkeys;      /* column numbers of index's keys, or 0 */
+   Oid        *indexcollations;/* OIDs of the collations of the index columns */
    Oid        *opcintype;      /* OIDs of opclass declared input data types */
    Oid        *sortopfamily;   /* OIDs of btree opfamilies, if orderable */
    bool       *reverse_sort;   /* is sort order descending? */
@@ -585,6 +586,7 @@ typedef struct PathKey
 
    EquivalenceClass *pk_eclass;    /* the value that is ordered */
    Oid         pk_opfamily;    /* btree opfamily defining the ordering */
+   Oid         pk_collation;   /* collation */
    int         pk_strategy;    /* sort direction (ASC or DESC) */
    bool        pk_nulls_first; /* do NULLs come before normal values? */
 } PathKey;
@@ -1102,6 +1104,7 @@ typedef struct MergeScanSelCache
 {
    /* Ordering details (cache lookup key) */
    Oid         opfamily;       /* btree opfamily defining the ordering */
+   Oid         collation;
    int         strategy;       /* sort direction (ASC or DESC) */
    bool        nulls_first;    /* do NULLs come before normal values? */
    /* Results */
index 31a5d5f186aebedceb2d0b38f59507de4d81b9f5..c4374121496d53e4cec04b56cb8342cdde0a895b 100644 (file)
@@ -28,7 +28,7 @@ extern Node *SS_process_sublinks(PlannerInfo *root, Node *expr, bool isQual);
 extern void SS_finalize_plan(PlannerInfo *root, Plan *plan,
                 bool attach_initplans);
 extern Param *SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan,
-                          Oid resulttype, int32 resulttypmod);
+                          Oid resulttype, int32 resulttypmod, Oid resultcollation);
 extern Param *assign_nestloop_param(PlannerInfo *root, Var *var);
 extern int SS_assign_special_param(PlannerInfo *root);
 
index 716c3d2d13b4ebf0294e0c4a1ff7cfe9b7546eed..76d806dd40e068ad2e59c6b6675911ca98b54499 100644 (file)
@@ -30,6 +30,7 @@ extern void build_aggregate_fnexprs(Oid *agg_input_types,
                        Oid agg_result_type,
                        Oid transfn_oid,
                        Oid finalfn_oid,
+                       Oid collation,
                        Expr **transfnexpr,
                        Expr **finalfnexpr);
 
index ceaff2f9a92b994961d34e3b953ee47d8cdbbeeb..9f79ad89d41a3f1480e1d9f38602b116ec97a810 100644 (file)
@@ -87,4 +87,6 @@ extern CoercionPathType find_coercion_pathway(Oid targetTypeId,
 extern CoercionPathType find_typmod_coercion_function(Oid typeId,
                              Oid *funcid);
 
+extern Oid select_common_collation(ParseState *pstate, List *exprs, bool none_ok);
+
 #endif   /* PARSE_COERCE_H */
index edae0527e6eccf1a0dfeef655596d7577b7dfaad..a1511cbe64c7aa183a9c500c5879144034f06825 100644 (file)
@@ -145,6 +145,7 @@ extern ArrayRef *transformArraySubscripts(ParseState *pstate,
                         Oid arrayType,
                         Oid elementType,
                         int32 arrayTypMod,
+                        Oid arrayColl,
                         List *indirection,
                         Node *assignFrom);
 extern Const *make_const(ParseState *pstate, Value *value, int location);
index dadcde790c91ca6d16fb5fb6e5ab7550885215cf..8621ab678d0d7ef46102efc28953c592a74914c9 100644 (file)
 typedef HeapTuple Type;
 
 extern Type LookupTypeName(ParseState *pstate, const TypeName *typeName,
-              int32 *typmod_p);
+                          int32 *typmod_p, Oid *collid_p);
 extern Type typenameType(ParseState *pstate, const TypeName *typeName,
-            int32 *typmod_p);
+                        int32 *typmod_p, Oid *collid_p);
+
+extern Oid LookupCollation(ParseState *pstate, List *collnames, int location);
+
 extern Oid typenameTypeId(ParseState *pstate, const TypeName *typeName);
 extern void typenameTypeIdAndMod(ParseState *pstate, const TypeName *typeName,
-                                Oid *typeid_p, int32 *typmod_p);
+                                 Oid *typeid_p, int32 *typmod_p);
+extern void typenameTypeIdModColl(ParseState *pstate, const TypeName *typeName,
+                                 Oid *typeid_p, int32 *typmod_p, Oid *collid_p);
 
 extern char *TypeNameToString(const TypeName *typeName);
 extern char *TypeNameListToString(List *typenames);
index efdbc1ab81ce5edceb6546e9d7de89ac7f56da62..b0aebd5ef8121e54e3c3823b25158759f13a3092 100644 (file)
@@ -52,7 +52,7 @@ extern char *get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum);
  * type and typemod info for that attribute of that RTE.
  */
 extern void get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
-                      Oid *vartype, int32 *vartypmod);
+                      Oid *vartype, int32 *vartypmod, Oid *varcollid);
 
 /*
  * Check whether an attribute of an RTE has been dropped (note that
index 5d84350c45c5c9abbaa55cf0a8cf21911359decb..3ee1d077a54477dff202a70ad4f1dd0da01909fe 100644 (file)
    */
 #undef HAVE_LL_CONSTANTS
 
+/* Define to 1 if the system has the type `locale_t'. */
+#undef HAVE_LOCALE_T
+
 /* Define to 1 if `long int' works and is 64 bits. */
 #undef HAVE_LONG_INT_64
 
 /* Define to build with Kerberos 5 support. (--with-krb5) */
 #undef KRB5
 
+/* Define to 1 if `locale_t' requires . */
+#undef LOCALE_T_IN_XLOCALE
+
 /* Define as the maximum alignment requirement of any C data type. */
 #undef MAXIMUM_ALIGNOF
 
index 2020a260607168e3c6ce3dabf36a737b5fab7e40..f6d6d2e444bd535701f200fabe38954f63bdba6c 100644 (file)
@@ -461,7 +461,7 @@ extern void qsort_arg(void *base, size_t nel, size_t elsize,
          qsort_arg_comparator cmp, void *arg);
 
 /* port/chklocale.c */
-extern int pg_get_encoding_from_locale(const char *ctype);
+extern int pg_get_encoding_from_locale(const char *ctype, bool write_message);
 
 /* port/inet_net_ntop.c */
 extern char *inet_net_ntop(int af, const void *src, int bits,
index 4cb6c5c17a57d3e91cd9128fd3c9bf32e85f33f2..540d16b844a9e85410a326f1148a3bdfa52bb397 100644 (file)
@@ -623,6 +623,8 @@ extern List *deparse_context_for_planstate(Node *planstate, List *ancestors,
 extern const char *quote_identifier(const char *ident);
 extern char *quote_qualified_identifier(const char *qualifier,
                           const char *ident);
+extern char *generate_collation_name(Oid collid);
+
 
 /* tid.c */
 extern Datum tidin(PG_FUNCTION_ARGS);
@@ -714,7 +716,7 @@ extern Datum textoverlay(PG_FUNCTION_ARGS);
 extern Datum textoverlay_no_len(PG_FUNCTION_ARGS);
 extern Datum name_text(PG_FUNCTION_ARGS);
 extern Datum text_name(PG_FUNCTION_ARGS);
-extern int varstr_cmp(char *arg1, int len1, char *arg2, int len2);
+extern int varstr_cmp(char *arg1, int len1, char *arg2, int len2, Oid collid);
 extern List *textToQualifiedNameList(text *textval);
 extern bool SplitIdentifierString(char *rawstring, char separator,
                      List **namelist);
index 7cd3be1131739f76dad3d4adb16dc92d0293a7f6..175d396297cd39cbbd5da979699c1092b4e1d8e8 100644 (file)
@@ -20,9 +20,9 @@
 #include "fmgr.h"
 
 
-extern char *str_tolower(const char *buff, size_t nbytes);
-extern char *str_toupper(const char *buff, size_t nbytes);
-extern char *str_initcap(const char *buff, size_t nbytes);
+extern char *str_tolower(const char *buff, size_t nbytes, Oid collid);
+extern char *str_toupper(const char *buff, size_t nbytes, Oid collid);
+extern char *str_initcap(const char *buff, size_t nbytes, Oid collid);
 
 extern Datum timestamp_to_char(PG_FUNCTION_ARGS);
 extern Datum timestamptz_to_char(PG_FUNCTION_ARGS);
index 26c241c348439ae12b646609c4b527a048726c50..22dfd58f0aa0a0e98c3367e3ae687e4fbddee0ca 100644 (file)
@@ -60,8 +60,10 @@ extern char *get_relid_attribute_name(Oid relid, AttrNumber attnum);
 extern AttrNumber get_attnum(Oid relid, const char *attname);
 extern Oid get_atttype(Oid relid, AttrNumber attnum);
 extern int32 get_atttypmod(Oid relid, AttrNumber attnum);
+extern Oid get_attcollation(Oid relid, AttrNumber attnum);
 extern void get_atttypetypmod(Oid relid, AttrNumber attnum,
                  Oid *typid, int32 *typmod);
+extern char *get_collation_name(Oid colloid);
 extern char *get_constraint_name(Oid conoid);
 extern Oid get_opclass_family(Oid opclass);
 extern Oid get_opclass_input_type(Oid opclass);
@@ -124,6 +126,8 @@ extern void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena);
 extern void getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typIOParam);
 extern void getTypeBinaryOutputInfo(Oid type, Oid *typSend, bool *typIsVarlena);
 extern Oid get_typmodin(Oid typid);
+extern Oid get_typcollation(Oid typid);
+extern bool type_is_collatable(Oid typid);
 extern Oid getBaseType(Oid typid);
 extern Oid getBaseTypeAndTypmod(Oid typid, int32 *typmod);
 extern int32 get_typavgwidth(Oid typid, int32 typmod);
index d56bf38d2d12b105a75f9d034845c6267e6ecbac..4c72fd0dc6c3137757175713fc5169c22e1aeed6 100644 (file)
@@ -13,6 +13,9 @@
 #define _PG_LOCALE_
 
 #include 
+#ifdef LOCALE_T_IN_XLOCALE
+#include 
+#endif
 
 #include "utils/guc.h"
 
@@ -42,8 +45,8 @@ extern const char *locale_time_assign(const char *value,
 extern bool check_locale(int category, const char *locale);
 extern char *pg_perm_setlocale(int category, const char *locale);
 
-extern bool lc_collate_is_c(void);
-extern bool lc_ctype_is_c(void);
+extern bool lc_collate_is_c(Oid collation);
+extern bool lc_ctype_is_c(Oid collation);
 
 /*
  * Return the POSIX lconv struct (contains number/money formatting
@@ -53,4 +56,20 @@ extern struct lconv *PGLC_localeconv(void);
 
 extern void cache_locale_time(void);
 
+
+/*
+ * We define our own wrapper around locale_t so we can keep the same
+ * function signatures for all builds, while not having to create a
+ * fake version of the standard type locale_t in the global namespace.
+ * The fake version of pg_locale_t can be checked for truth; that's
+ * about all it will be needed for.
+ */
+#ifdef HAVE_LOCALE_T
+typedef locale_t pg_locale_t;
+#else
+typedef int pg_locale_t;
+#endif
+
+extern pg_locale_t pg_newlocale_from_collation(Oid collid);
+
 #endif   /* _PG_LOCALE_ */
index 5498df93ca78dca4add01c5a8a311881550cd41c..d47c4beaa5cac5ed37fc968488b2ab1519c1d18a 100644 (file)
@@ -203,6 +203,7 @@ typedef struct RelationData
    Oid        *rd_exclprocs;   /* OIDs of exclusion ops' procs, if any */
    uint16     *rd_exclstrats;  /* exclusion ops' strategy numbers, if any */
    void       *rd_amcache;     /* available for use by index AM */
+   Oid        *rd_indcollation; /* OIDs of index collations */
 
    /*
     * Hack for CLUSTER, rewriting ALTER TABLE, etc: when writing a new
index e9913aa049f28da92efe296e9925e21cf7719f21..baf6d8caf86fdf118889428e140aa0b87d81e15d 100644 (file)
@@ -74,6 +74,7 @@ typedef struct VariableStatData
    Oid         vartype;        /* exposed type of expression */
    Oid         atttype;        /* type to pass to get_attstatsslot */
    int32       atttypmod;      /* typmod to pass to get_attstatsslot */
+   Oid         attcollation;   /* collation of the variable */
    bool        isunique;       /* true if matched to a unique index */
 } VariableStatData;
 
@@ -178,7 +179,7 @@ extern Selectivity rowcomparesel(PlannerInfo *root,
              int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo);
 
 extern void mergejoinscansel(PlannerInfo *root, Node *clause,
-                Oid opfamily, int strategy, bool nulls_first,
+                Oid opfamily, Oid collation, int strategy, bool nulls_first,
                 Selectivity *leftstart, Selectivity *leftend,
                 Selectivity *rightstart, Selectivity *rightend);
 
index 66515a8f82a8d7f5b02794ba3636dc8a7b643887..55d22303a7ac1c166a15dfea8d4554554b5d8cb8 100644 (file)
@@ -44,6 +44,8 @@ enum SysCacheIdentifier
    CASTSOURCETARGET,
    CLAAMNAMENSP,
    CLAOID,
+   COLLNAMEENCNSP,
+   COLLOID,
    CONDEFAULT,
    CONNAMENSP,
    CONSTROID,
index b38c260d5057d047603c6fced7bc03c7f0c3377c..59505d4b577e0fc2c4eef537a162bf2545be78d2 100644 (file)
@@ -60,7 +60,7 @@ typedef struct Tuplesortstate Tuplesortstate;
 
 extern Tuplesortstate *tuplesort_begin_heap(TupleDesc tupDesc,
                     int nkeys, AttrNumber *attNums,
-                    Oid *sortOperators, bool *nullsFirstFlags,
+                    Oid *sortOperators, Oid *collations, bool *nullsFirstFlags,
                     int workMem, bool randomAccess);
 extern Tuplesortstate *tuplesort_begin_cluster(TupleDesc tupDesc,
                                               Relation indexRel,
@@ -72,7 +72,7 @@ extern Tuplesortstate *tuplesort_begin_index_hash(Relation indexRel,
                           uint32 hash_mask,
                           int workMem, bool randomAccess);
 extern Tuplesortstate *tuplesort_begin_datum(Oid datumType,
-                     Oid sortOperator, bool nullsFirstFlag,
+                     Oid sortOperator, Oid sortCollation, bool nullsFirstFlag,
                      int workMem, bool randomAccess);
 
 extern void tuplesort_set_bound(Tuplesortstate *state, int64 bound);
index 41188a2369fd3632adbb7496c02d80e9ef964075..d2b3862ec722a6c30ad24096af79f04ba7f316a7 100644 (file)
@@ -1565,7 +1565,7 @@ plpgsql_parse_wordtype(char *ident)
     * Word wasn't found in the namespace stack. Try to find a data type with
     * that name, but ignore shell types and complex types.
     */
-   typeTup = LookupTypeName(NULL, makeTypeName(ident), NULL);
+   typeTup = LookupTypeName(NULL, makeTypeName(ident), NULL, NULL);
    if (typeTup)
    {
        Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
index 8fd092e2d6f169f8dac6892b66dd014ac6187a2b..fcd3c6bb5b4037691e0af4d23e4295f489499f79 100644 (file)
@@ -227,7 +227,7 @@ win32_langinfo(const char *ctype)
  * with any desired encoding.
  */
 int
-pg_get_encoding_from_locale(const char *ctype)
+pg_get_encoding_from_locale(const char *ctype, bool write_message)
 {
    char       *sys;
    int         i;
@@ -322,17 +322,20 @@ pg_get_encoding_from_locale(const char *ctype)
     * We print a warning if we got a CODESET string but couldn't recognize
     * it.  This means we need another entry in the table.
     */
+   if (write_message)
+   {
 #ifdef FRONTEND
-   fprintf(stderr, _("could not determine encoding for locale \"%s\": codeset is \"%s\""),
-           ctype, sys);
-   /* keep newline separate so there's only one translatable string */
-   fputc('\n', stderr);
+       fprintf(stderr, _("could not determine encoding for locale \"%s\": codeset is \"%s\""),
+               ctype, sys);
+       /* keep newline separate so there's only one translatable string */
+       fputc('\n', stderr);
 #else
-   ereport(WARNING,
-           (errmsg("could not determine encoding for locale \"%s\": codeset is \"%s\"",
-                   ctype, sys),
-          errdetail("Please report this to .")));
+       ereport(WARNING,
+               (errmsg("could not determine encoding for locale \"%s\": codeset is \"%s\"",
+                       ctype, sys),
+                errdetail("Please report this to .")));
 #endif
+   }
 
    free(sys);
    return -1;
index 15b9ec49f716729ee65a84ee570dc3ef358313ba..4c8af0f1b66d33ac98557f11acc13c370225ea6a 100644 (file)
@@ -141,13 +141,13 @@ tablespace-setup:
 pg_regress_call = ./pg_regress --inputdir=$(srcdir) --dlpath=. $(if $(MULTIBYTE),--multibyte=$(MULTIBYTE)) $(NOLOCALE)
 
 check: all tablespace-setup
-   $(pg_regress_call) --temp-install=./tmp_check --top-builddir=$(top_builddir) --schedule=$(srcdir)/parallel_schedule $(MAXCONNOPT) $(TEMP_CONF)
+   $(pg_regress_call) --temp-install=./tmp_check --top-builddir=$(top_builddir) --schedule=$(srcdir)/parallel_schedule $(MAXCONNOPT) $(TEMP_CONF) $(EXTRA_TESTS)
 
 installcheck: all tablespace-setup
-   $(pg_regress_call) --psqldir=$(PSQLDIR) --schedule=$(srcdir)/serial_schedule
+   $(pg_regress_call) --psqldir=$(PSQLDIR) --schedule=$(srcdir)/serial_schedule $(EXTRA_TESTS)
 
 installcheck-parallel: all tablespace-setup
-   $(pg_regress_call) --psqldir=$(PSQLDIR) --schedule=$(srcdir)/parallel_schedule $(MAXCONNOPT)
+   $(pg_regress_call) --psqldir=$(PSQLDIR) --schedule=$(srcdir)/parallel_schedule $(MAXCONNOPT) $(EXTRA_TESTS)
 
 standbycheck: all
    $(pg_regress_call) --psqldir=$(PSQLDIR) --schedule=$(srcdir)/standby_schedule --use-existing
diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out
new file mode 100644 (file)
index 0000000..e6b2d32
--- /dev/null
@@ -0,0 +1,734 @@
+/*
+ * This test is for Linux/glibc systems and assumes that a full set of
+ * locales is installed.  It must be run in a UTF-8 locale.
+ */
+SET client_encoding TO UTF8;
+CREATE TABLE collate_test1 (
+    a int,
+    b text COLLATE "en_US.utf8" NOT NULL
+);
+\d collate_test1
+          Table "public.collate_test1"
+ Column |  Type   |          Modifiers          
+--------+---------+-----------------------------
+ a      | integer | 
+ b      | text    | collate en_US.utf8 not null
+
+CREATE TABLE collate_test_fail (
+    a int,
+    b text COLLATE "ja_JP.eucjp"
+);
+ERROR:  collation "ja_JP.eucjp" for current database encoding "UTF8" does not exist
+LINE 3:     b text COLLATE "ja_JP.eucjp"
+              ^
+CREATE TABLE collate_test_fail (
+    a int,
+    b text COLLATE "foo"
+);
+ERROR:  collation "foo" for current database encoding "UTF8" does not exist
+LINE 3:     b text COLLATE "foo"
+              ^
+CREATE TABLE collate_test_fail (
+    a int COLLATE "en_US.utf8",
+    b text
+);
+ERROR:  collations are not supported by type integer
+LINE 2:     a int COLLATE "en_US.utf8",
+              ^
+CREATE TABLE collate_test_like (
+    LIKE collate_test1
+);
+\d collate_test_like
+        Table "public.collate_test_like"
+ Column |  Type   |          Modifiers          
+--------+---------+-----------------------------
+ a      | integer | 
+ b      | text    | collate en_US.utf8 not null
+
+CREATE TABLE collate_test2 (
+    a int,
+    b text COLLATE "sv_SE.utf8"
+);
+CREATE TABLE collate_test3 (
+    a int,
+    b text COLLATE "C"
+);
+INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC');
+INSERT INTO collate_test2 SELECT * FROM collate_test1;
+INSERT INTO collate_test3 SELECT * FROM collate_test1;
+SELECT * FROM collate_test1 WHERE b >= 'bbc';
+ a |  b  
+---+-----
+ 3 | bbc
+(1 row)
+
+SELECT * FROM collate_test2 WHERE b >= 'bbc';
+ a |  b  
+---+-----
+ 2 | äbc
+ 3 | bbc
+(2 rows)
+
+SELECT * FROM collate_test3 WHERE b >= 'bbc';
+ a |  b  
+---+-----
+ 2 | äbc
+ 3 | bbc
+(2 rows)
+
+SELECT * FROM collate_test3 WHERE b >= 'BBC';
+ a |  b  
+---+-----
+ 1 | abc
+ 2 | äbc
+ 3 | bbc
+(3 rows)
+
+SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc';
+ a |  b  
+---+-----
+ 2 | äbc
+ 3 | bbc
+(2 rows)
+
+SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C";
+ a |  b  
+---+-----
+ 2 | äbc
+ 3 | bbc
+(2 rows)
+
+SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C";
+ a |  b  
+---+-----
+ 2 | äbc
+ 3 | bbc
+(2 rows)
+
+SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US.utf8";
+ERROR:  collation mismatch between explicit collations "C" and "en_US.utf8"
+LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e...
+                                                             ^
+SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US";
+ERROR:  collation mismatch between explicit collations "C" and "en_US"
+LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e...
+                                                             ^
+CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE.utf8";
+CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE.utf8"; -- fails
+ERROR:  collations are not supported by type integer
+CREATE TABLE collate_test4 (
+    a int,
+    b testdomain_sv
+);
+INSERT INTO collate_test4 SELECT * FROM collate_test1;
+SELECT a, b FROM collate_test4 ORDER BY b;
+ a |  b  
+---+-----
+ 1 | abc
+ 4 | ABC
+ 3 | bbc
+ 2 | äbc
+(4 rows)
+
+CREATE TABLE collate_test5 (
+    a int,
+    b testdomain_sv COLLATE "en_US.utf8"
+);
+INSERT INTO collate_test5 SELECT * FROM collate_test1;
+SELECT a, b FROM collate_test5 ORDER BY b;
+ a |  b  
+---+-----
+ 1 | abc
+ 4 | ABC
+ 2 | äbc
+ 3 | bbc
+(4 rows)
+
+SELECT a, b FROM collate_test1 ORDER BY b;
+ a |  b  
+---+-----
+ 1 | abc
+ 4 | ABC
+ 2 | äbc
+ 3 | bbc
+(4 rows)
+
+SELECT a, b FROM collate_test2 ORDER BY b;
+ a |  b  
+---+-----
+ 1 | abc
+ 4 | ABC
+ 3 | bbc
+ 2 | äbc
+(4 rows)
+
+SELECT a, b FROM collate_test3 ORDER BY b;
+ a |  b  
+---+-----
+ 4 | ABC
+ 1 | abc
+ 3 | bbc
+ 2 | äbc
+(4 rows)
+
+SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C";
+ a |  b  
+---+-----
+ 4 | ABC
+ 1 | abc
+ 3 | bbc
+ 2 | äbc
+(4 rows)
+
+-- star expansion
+SELECT * FROM collate_test1 ORDER BY b;
+ a |  b  
+---+-----
+ 1 | abc
+ 4 | ABC
+ 2 | äbc
+ 3 | bbc
+(4 rows)
+
+SELECT * FROM collate_test2 ORDER BY b;
+ a |  b  
+---+-----
+ 1 | abc
+ 4 | ABC
+ 3 | bbc
+ 2 | äbc
+(4 rows)
+
+SELECT * FROM collate_test3 ORDER BY b;
+ a |  b  
+---+-----
+ 4 | ABC
+ 1 | abc
+ 3 | bbc
+ 2 | äbc
+(4 rows)
+
+-- constant expression folding
+SELECT 'bbc' COLLATE "en_US.utf8" > 'äbc' COLLATE "en_US.utf8" AS "true";
+ true 
+------
+ t
+(1 row)
+
+SELECT 'bbc' COLLATE "sv_SE.utf8" > 'äbc' COLLATE "sv_SE.utf8" AS "false";
+ false 
+-------
+ f
+(1 row)
+
+-- upper/lower
+CREATE TABLE collate_test10 (
+    a int,
+    x text COLLATE "en_US.utf8",
+    y text COLLATE "tr_TR.utf8"
+);
+INSERT INTO collate_test10 VALUES (1, 'hij', 'hij'), (2, 'HIJ', 'HIJ');
+SELECT a, lower(x), lower(y), upper(x), upper(y), initcap(x), initcap(y) FROM collate_test10;
+ a | lower | lower | upper | upper | initcap | initcap 
+---+-------+-------+-------+-------+---------+---------
+ 1 | hij   | hij   | HIJ   | HİJ   | Hij     | Hij
+ 2 | hij   | hıj   | HIJ   | HIJ   | Hij     | Hıj
+(2 rows)
+
+SELECT a, lower(x COLLATE "C"), lower(y COLLATE "C") FROM collate_test10;
+ a | lower | lower 
+---+-------+-------
+ 1 | hij   | hij
+ 2 | hij   | hij
+(2 rows)
+
+SELECT a, x, y FROM collate_test10 ORDER BY lower(y), a;
+ a |  x  |  y  
+---+-----+-----
+ 2 | HIJ | HIJ
+ 1 | hij | hij
+(2 rows)
+
+-- LIKE/ILIKE
+SELECT * FROM collate_test1 WHERE b LIKE 'abc';
+ a |  b  
+---+-----
+ 1 | abc
+(1 row)
+
+SELECT * FROM collate_test1 WHERE b LIKE 'abc%';
+ a |  b  
+---+-----
+ 1 | abc
+(1 row)
+
+SELECT * FROM collate_test1 WHERE b LIKE '%bc%';
+ a |  b  
+---+-----
+ 1 | abc
+ 2 | äbc
+ 3 | bbc
+(3 rows)
+
+SELECT * FROM collate_test1 WHERE b ILIKE 'abc';
+ a |  b  
+---+-----
+ 1 | abc
+ 4 | ABC
+(2 rows)
+
+SELECT * FROM collate_test1 WHERE b ILIKE 'abc%';
+ a |  b  
+---+-----
+ 1 | abc
+ 4 | ABC
+(2 rows)
+
+SELECT * FROM collate_test1 WHERE b ILIKE '%bc%';
+ a |  b  
+---+-----
+ 1 | abc
+ 2 | äbc
+ 3 | bbc
+ 4 | ABC
+(4 rows)
+
+SELECT 'Türkiye' COLLATE "en_US.utf8" ILIKE '%KI%' AS "true";
+ true 
+------
+ t
+(1 row)
+
+SELECT 'Türkiye' COLLATE "tr_TR.utf8" ILIKE '%KI%' AS "false";
+ false 
+-------
+ f
+(1 row)
+
+-- The following actually exercises the selectivity estimation for ILIKE.
+SELECT relname FROM pg_class WHERE relname ILIKE 'abc%';
+ relname 
+---------
+(0 rows)
+
+-- to_char
+SET lc_time TO 'tr_TR.utf8';
+SELECT to_char(date '2010-04-01', 'DD TMMON YYYY');
+   to_char   
+-------------
+ 01 NIS 2010
+(1 row)
+
+SELECT to_char(date '2010-04-01', 'DD TMMON YYYY' COLLATE "tr_TR.utf8");
+   to_char   
+-------------
+ 01 NİS 2010
+(1 row)
+
+-- backwards parsing
+CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc';
+CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C";
+CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10;
+SELECT table_name, view_definition FROM information_schema.views
+  WHERE table_name LIKE 'collview%' ORDER BY 1;
+ table_name |                                                  view_definition                                                   
+------------+--------------------------------------------------------------------------------------------------------------------
+ collview1  | SELECT collate_test1.a, collate_test1.b FROM collate_test1 WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text);
+ collview2  | SELECT collate_test1.a, collate_test1.b FROM collate_test1 ORDER BY (collate_test1.b COLLATE "C");
+ collview3  | SELECT collate_test10.a, lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower FROM collate_test10;
+(3 rows)
+
+-- collation propagation in various expression type
+SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2;
+ a | coalesce 
+---+----------
+ 1 | abc
+ 4 | ABC
+ 2 | äbc
+ 3 | bbc
+(4 rows)
+
+SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2;
+ a | coalesce 
+---+----------
+ 1 | abc
+ 4 | ABC
+ 3 | bbc
+ 2 | äbc
+(4 rows)
+
+SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2;
+ a | coalesce 
+---+----------
+ 4 | ABC
+ 1 | abc
+ 3 | bbc
+ 2 | äbc
+(4 rows)
+
+SELECT a, lower(coalesce(x, 'foo')), lower(coalesce(y, 'foo')) FROM collate_test10;
+ a | lower | lower 
+---+-------+-------
+ 1 | hij   | hij
+ 2 | hij   | hıj
+(2 rows)
+
+SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3;
+ a |  b  | greatest 
+---+-----+----------
+ 1 | abc | CCC
+ 2 | äbc | CCC
+ 3 | bbc | CCC
+ 4 | ABC | CCC
+(4 rows)
+
+SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3;
+ a |  b  | greatest 
+---+-----+----------
+ 1 | abc | CCC
+ 3 | bbc | CCC
+ 4 | ABC | CCC
+ 2 | äbc | äbc
+(4 rows)
+
+SELECT a, b, greatest(b, 'CCC') FROM collate_test3 ORDER BY 3;
+ a |  b  | greatest 
+---+-----+----------
+ 4 | ABC | CCC
+ 1 | abc | abc
+ 3 | bbc | bbc
+ 2 | äbc | äbc
+(4 rows)
+
+SELECT a, x, y, lower(greatest(x, 'foo')), lower(greatest(y, 'foo')) FROM collate_test10;
+ a |  x  |  y  | lower | lower 
+---+-----+-----+-------+-------
+ 1 | hij | hij | hij   | hij
+ 2 | HIJ | HIJ | hij   | hıj
+(2 rows)
+
+SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2;
+ a | nullif 
+---+--------
+ 4 | ABC
+ 2 | äbc
+ 3 | bbc
+ 1 | 
+(4 rows)
+
+SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2;
+ a | nullif 
+---+--------
+ 4 | ABC
+ 3 | bbc
+ 2 | äbc
+ 1 | 
+(4 rows)
+
+SELECT a, nullif(b, 'abc') FROM collate_test3 ORDER BY 2;
+ a | nullif 
+---+--------
+ 4 | ABC
+ 3 | bbc
+ 2 | äbc
+ 1 | 
+(4 rows)
+
+SELECT a, lower(nullif(x, 'foo')), lower(nullif(y, 'foo')) FROM collate_test10;
+ a | lower | lower 
+---+-------+-------
+ 1 | hij   | hij
+ 2 | hij   | hıj
+(2 rows)
+
+SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2;
+ a |  b   
+---+------
+ 4 | ABC
+ 2 | äbc
+ 1 | abcd
+ 3 | bbc
+(4 rows)
+
+SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2;
+ a |  b   
+---+------
+ 4 | ABC
+ 1 | abcd
+ 3 | bbc
+ 2 | äbc
+(4 rows)
+
+SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2;
+ a |  b   
+---+------
+ 4 | ABC
+ 1 | abcd
+ 3 | bbc
+ 2 | äbc
+(4 rows)
+
+CREATE DOMAIN testdomain AS text;
+SELECT a, b::testdomain FROM collate_test1 ORDER BY 2;
+ a |  b  
+---+-----
+ 1 | abc
+ 4 | ABC
+ 2 | äbc
+ 3 | bbc
+(4 rows)
+
+SELECT a, b::testdomain FROM collate_test2 ORDER BY 2;
+ a |  b  
+---+-----
+ 1 | abc
+ 4 | ABC
+ 3 | bbc
+ 2 | äbc
+(4 rows)
+
+SELECT a, b::testdomain FROM collate_test3 ORDER BY 2;
+ a |  b  
+---+-----
+ 4 | ABC
+ 1 | abc
+ 3 | bbc
+ 2 | äbc
+(4 rows)
+
+SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2;
+ a |  b  
+---+-----
+ 1 | abc
+ 4 | ABC
+ 3 | bbc
+ 2 | äbc
+(4 rows)
+
+SELECT a, lower(x::testdomain), lower(y::testdomain) FROM collate_test10;
+ a | lower | lower 
+---+-------+-------
+ 1 | hij   | hij
+ 2 | hij   | hıj
+(2 rows)
+
+SELECT min(b), max(b) FROM collate_test1;
+ min | max 
+-----+-----
+ abc | bbc
+(1 row)
+
+SELECT min(b), max(b) FROM collate_test2;
+ min | max 
+-----+-----
+ abc | äbc
+(1 row)
+
+SELECT min(b), max(b) FROM collate_test3;
+ min | max 
+-----+-----
+ ABC | äbc
+(1 row)
+
+SELECT array_agg(b ORDER BY b) FROM collate_test1;
+     array_agg     
+-------------------
+ {abc,ABC,äbc,bbc}
+(1 row)
+
+SELECT array_agg(b ORDER BY b) FROM collate_test2;
+     array_agg     
+-------------------
+ {abc,ABC,bbc,äbc}
+(1 row)
+
+SELECT array_agg(b ORDER BY b) FROM collate_test3;
+     array_agg     
+-------------------
+ {ABC,abc,bbc,äbc}
+(1 row)
+
+SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2;
+ a |  b  
+---+-----
+ 1 | abc
+ 1 | abc
+ 4 | ABC
+ 4 | ABC
+ 2 | äbc
+ 2 | äbc
+ 3 | bbc
+ 3 | bbc
+(8 rows)
+
+SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2;
+ a |  b  
+---+-----
+ 1 | abc
+ 4 | ABC
+ 3 | bbc
+ 2 | äbc
+(4 rows)
+
+SELECT a, b FROM collate_test3 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test3 WHERE a > 1 ORDER BY 2;
+ a |  b  
+---+-----
+ 3 | bbc
+ 2 | äbc
+(2 rows)
+
+SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2 ORDER BY 2;
+ a |  b  
+---+-----
+ 4 | ABC
+ 3 | bbc
+ 2 | äbc
+(3 rows)
+
+SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail
+ERROR:  no collation was derived for the sort expression
+LINE 1: ...e_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2;
+                                                                     ^
+HINT:  Use the COLLATE clause to set the collation explicitly.
+SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok
+ a |  b  
+---+-----
+ 1 | abc
+ 2 | äbc
+ 3 | bbc
+ 4 | ABC
+ 1 | abc
+ 2 | äbc
+ 3 | bbc
+ 4 | ABC
+(8 rows)
+
+SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail
+ERROR:  collation mismatch between implicit collations "en_US.utf8" and "C"
+LINE 1: SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collat...
+                                                       ^
+HINT:  You can override the collation by applying the COLLATE clause to one or both expressions.
+SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok
+ a |  b  
+---+-----
+ 4 | ABC
+ 1 | abc
+ 3 | bbc
+ 2 | äbc
+(4 rows)
+
+SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail
+ERROR:  collation mismatch between implicit collations "en_US.utf8" and "C"
+LINE 1: ...ELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM col...
+                                                             ^
+HINT:  You can override the collation by applying the COLLATE clause to one or both expressions.
+SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail
+ERROR:  collation mismatch between implicit collations "en_US.utf8" and "C"
+LINE 1: SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM colla...
+                                                        ^
+HINT:  You can override the collation by applying the COLLATE clause to one or both expressions.
+-- casting
+SELECT CAST('42' AS text COLLATE "C");
+ERROR:  COLLATE clause not allowed in cast target
+LINE 1: SELECT CAST('42' AS text COLLATE "C");
+                            ^
+SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2;
+ a |  b  
+---+-----
+ 1 | abc
+ 4 | ABC
+ 2 | äbc
+ 3 | bbc
+(4 rows)
+
+SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2;
+ a |  b  
+---+-----
+ 1 | abc
+ 4 | ABC
+ 3 | bbc
+ 2 | äbc
+(4 rows)
+
+SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2;
+ a |  b  
+---+-----
+ 4 | ABC
+ 1 | abc
+ 3 | bbc
+ 2 | äbc
+(4 rows)
+
+-- polymorphism
+SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1;
+ unnest 
+--------
+ abc
+ ABC
+ äbc
+ bbc
+(4 rows)
+
+SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1;
+ unnest 
+--------
+ abc
+ ABC
+ bbc
+ äbc
+(4 rows)
+
+SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test3)) ORDER BY 1;
+ unnest 
+--------
+ ABC
+ abc
+ bbc
+ äbc
+(4 rows)
+
+CREATE FUNCTION dup (f1 anyelement, f2 out anyelement, f3 out anyarray)
+    AS 'select $1, array[$1,$1]' LANGUAGE sql;
+SELECT a, (dup(b)).* FROM collate_test1 ORDER BY 2;
+ a | f2  |    f3     
+---+-----+-----------
+ 1 | abc | {abc,abc}
+ 4 | ABC | {ABC,ABC}
+ 2 | äbc | {äbc,äbc}
+ 3 | bbc | {bbc,bbc}
+(4 rows)
+
+SELECT a, (dup(b)).* FROM collate_test2 ORDER BY 2;
+ a | f2  |    f3     
+---+-----+-----------
+ 1 | abc | {abc,abc}
+ 4 | ABC | {ABC,ABC}
+ 3 | bbc | {bbc,bbc}
+ 2 | äbc | {äbc,äbc}
+(4 rows)
+
+SELECT a, (dup(b)).* FROM collate_test3 ORDER BY 2;
+ a | f2  |    f3     
+---+-----+-----------
+ 4 | ABC | {ABC,ABC}
+ 1 | abc | {abc,abc}
+ 3 | bbc | {bbc,bbc}
+ 2 | äbc | {äbc,äbc}
+(4 rows)
+
+-- indexes
+CREATE INDEX collate_test1_idx1 ON collate_test1 (b);
+CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C");
+CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically
+CREATE INDEX collate_test1_idx4 ON collate_test1 (a COLLATE "C"); -- fail
+ERROR:  collations are not supported by type integer
+CREATE INDEX collate_test1_idx5 ON collate_test1 ((a COLLATE "C")); -- fail
+ERROR:  collations are not supported by type integer
+SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%';
+      relname       |                                       pg_get_indexdef                                        
+--------------------+----------------------------------------------------------------------------------------------
+ collate_test1_idx1 | CREATE INDEX collate_test1_idx1 ON collate_test1 USING btree (b)
+ collate_test1_idx2 | CREATE INDEX collate_test1_idx2 ON collate_test1 USING btree (b COLLATE "C")
+ collate_test1_idx3 | CREATE INDEX collate_test1_idx3 ON collate_test1 USING btree (((b COLLATE "C")) COLLATE "C")
+(3 rows)
+
index 1ee820fd7c9400103adc436ca5c36304a6cc181d..d378e2641fabc6241d5e8948a6b84dd6f612ba47 100644 (file)
@@ -92,6 +92,7 @@ SELECT relname, relhasindex
  pg_authid               | t
  pg_cast                 | t
  pg_class                | t
+ pg_collation            | t
  pg_constraint           | t
  pg_conversion           | t
  pg_database             | t
@@ -155,7 +156,7 @@ SELECT relname, relhasindex
  timetz_tbl              | f
  tinterval_tbl           | f
  varchar_tbl             | f
-(144 rows)
+(145 rows)
 
 --
 -- another sanity check: every system catalog that has OIDs should have
diff --git a/src/test/regress/sql/collate.linux.utf8.sql b/src/test/regress/sql/collate.linux.utf8.sql
new file mode 100644 (file)
index 0000000..747428e
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * This test is for Linux/glibc systems and assumes that a full set of
+ * locales is installed.  It must be run in a UTF-8 locale.
+ */
+
+SET client_encoding TO UTF8;
+
+
+CREATE TABLE collate_test1 (
+    a int,
+    b text COLLATE "en_US.utf8" NOT NULL
+);
+
+\d collate_test1
+
+CREATE TABLE collate_test_fail (
+    a int,
+    b text COLLATE "ja_JP.eucjp"
+);
+
+CREATE TABLE collate_test_fail (
+    a int,
+    b text COLLATE "foo"
+);
+
+CREATE TABLE collate_test_fail (
+    a int COLLATE "en_US.utf8",
+    b text
+);
+
+CREATE TABLE collate_test_like (
+    LIKE collate_test1
+);
+
+\d collate_test_like
+
+CREATE TABLE collate_test2 (
+    a int,
+    b text COLLATE "sv_SE.utf8"
+);
+
+CREATE TABLE collate_test3 (
+    a int,
+    b text COLLATE "C"
+);
+
+INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC');
+INSERT INTO collate_test2 SELECT * FROM collate_test1;
+INSERT INTO collate_test3 SELECT * FROM collate_test1;
+
+SELECT * FROM collate_test1 WHERE b >= 'bbc';
+SELECT * FROM collate_test2 WHERE b >= 'bbc';
+SELECT * FROM collate_test3 WHERE b >= 'bbc';
+SELECT * FROM collate_test3 WHERE b >= 'BBC';
+
+SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc';
+SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C";
+SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C";
+SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US.utf8";
+SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US";
+
+
+CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE.utf8";
+CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE.utf8"; -- fails
+CREATE TABLE collate_test4 (
+    a int,
+    b testdomain_sv
+);
+INSERT INTO collate_test4 SELECT * FROM collate_test1;
+SELECT a, b FROM collate_test4 ORDER BY b;
+
+CREATE TABLE collate_test5 (
+    a int,
+    b testdomain_sv COLLATE "en_US.utf8"
+);
+INSERT INTO collate_test5 SELECT * FROM collate_test1;
+SELECT a, b FROM collate_test5 ORDER BY b;
+
+
+SELECT a, b FROM collate_test1 ORDER BY b;
+SELECT a, b FROM collate_test2 ORDER BY b;
+SELECT a, b FROM collate_test3 ORDER BY b;
+
+SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C";
+
+-- star expansion
+SELECT * FROM collate_test1 ORDER BY b;
+SELECT * FROM collate_test2 ORDER BY b;
+SELECT * FROM collate_test3 ORDER BY b;
+
+-- constant expression folding
+SELECT 'bbc' COLLATE "en_US.utf8" > 'äbc' COLLATE "en_US.utf8" AS "true";
+SELECT 'bbc' COLLATE "sv_SE.utf8" > 'äbc' COLLATE "sv_SE.utf8" AS "false";
+
+-- upper/lower
+
+CREATE TABLE collate_test10 (
+    a int,
+    x text COLLATE "en_US.utf8",
+    y text COLLATE "tr_TR.utf8"
+);
+
+INSERT INTO collate_test10 VALUES (1, 'hij', 'hij'), (2, 'HIJ', 'HIJ');
+
+SELECT a, lower(x), lower(y), upper(x), upper(y), initcap(x), initcap(y) FROM collate_test10;
+SELECT a, lower(x COLLATE "C"), lower(y COLLATE "C") FROM collate_test10;
+
+SELECT a, x, y FROM collate_test10 ORDER BY lower(y), a;
+
+-- LIKE/ILIKE
+
+SELECT * FROM collate_test1 WHERE b LIKE 'abc';
+SELECT * FROM collate_test1 WHERE b LIKE 'abc%';
+SELECT * FROM collate_test1 WHERE b LIKE '%bc%';
+SELECT * FROM collate_test1 WHERE b ILIKE 'abc';
+SELECT * FROM collate_test1 WHERE b ILIKE 'abc%';
+SELECT * FROM collate_test1 WHERE b ILIKE '%bc%';
+
+SELECT 'Türkiye' COLLATE "en_US.utf8" ILIKE '%KI%' AS "true";
+SELECT 'Türkiye' COLLATE "tr_TR.utf8" ILIKE '%KI%' AS "false";
+
+-- The following actually exercises the selectivity estimation for ILIKE.
+SELECT relname FROM pg_class WHERE relname ILIKE 'abc%';
+
+
+-- to_char
+
+SET lc_time TO 'tr_TR.utf8';
+SELECT to_char(date '2010-04-01', 'DD TMMON YYYY');
+SELECT to_char(date '2010-04-01', 'DD TMMON YYYY' COLLATE "tr_TR.utf8");
+
+
+-- backwards parsing
+
+CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc';
+CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C";
+CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10;
+
+SELECT table_name, view_definition FROM information_schema.views
+  WHERE table_name LIKE 'collview%' ORDER BY 1;
+
+
+-- collation propagation in various expression type
+
+SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2;
+SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2;
+SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2;
+SELECT a, lower(coalesce(x, 'foo')), lower(coalesce(y, 'foo')) FROM collate_test10;
+
+SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3;
+SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3;
+SELECT a, b, greatest(b, 'CCC') FROM collate_test3 ORDER BY 3;
+SELECT a, x, y, lower(greatest(x, 'foo')), lower(greatest(y, 'foo')) FROM collate_test10;
+
+SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2;
+SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2;
+SELECT a, nullif(b, 'abc') FROM collate_test3 ORDER BY 2;
+SELECT a, lower(nullif(x, 'foo')), lower(nullif(y, 'foo')) FROM collate_test10;
+
+SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2;
+SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2;
+SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2;
+
+CREATE DOMAIN testdomain AS text;
+SELECT a, b::testdomain FROM collate_test1 ORDER BY 2;
+SELECT a, b::testdomain FROM collate_test2 ORDER BY 2;
+SELECT a, b::testdomain FROM collate_test3 ORDER BY 2;
+SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2;
+SELECT a, lower(x::testdomain), lower(y::testdomain) FROM collate_test10;
+
+SELECT min(b), max(b) FROM collate_test1;
+SELECT min(b), max(b) FROM collate_test2;
+SELECT min(b), max(b) FROM collate_test3;
+
+SELECT array_agg(b ORDER BY b) FROM collate_test1;
+SELECT array_agg(b ORDER BY b) FROM collate_test2;
+SELECT array_agg(b ORDER BY b) FROM collate_test3;
+
+SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2;
+SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2;
+SELECT a, b FROM collate_test3 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test3 WHERE a > 1 ORDER BY 2;
+SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2 ORDER BY 2;
+
+SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail
+SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok
+SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail
+SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok
+SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail
+SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail
+
+
+-- casting
+
+SELECT CAST('42' AS text COLLATE "C");
+
+SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2;
+SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2;
+SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2;
+
+
+-- polymorphism
+
+SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1;
+SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1;
+SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test3)) ORDER BY 1;
+
+CREATE FUNCTION dup (f1 anyelement, f2 out anyelement, f3 out anyarray)
+    AS 'select $1, array[$1,$1]' LANGUAGE sql;
+
+SELECT a, (dup(b)).* FROM collate_test1 ORDER BY 2;
+SELECT a, (dup(b)).* FROM collate_test2 ORDER BY 2;
+SELECT a, (dup(b)).* FROM collate_test3 ORDER BY 2;
+
+
+-- indexes
+
+CREATE INDEX collate_test1_idx1 ON collate_test1 (b);
+CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C");
+CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically
+
+CREATE INDEX collate_test1_idx4 ON collate_test1 (a COLLATE "C"); -- fail
+CREATE INDEX collate_test1_idx5 ON collate_test1 ((a COLLATE "C")); -- fail
+
+SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%';