Per-column collation support
authorPeter Eisentraut <[email protected]>
Tue, 8 Feb 2011 21:04:18 +0000 (23:04 +0200)
committerPeter Eisentraut <[email protected]>
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.h>
+locale_t x;],
+[])],
+[pgac_cv_type_locale_t=yes],
+[AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+[#include <xlocale.h>
+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 <xlocale.h>.])
+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.h>
+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 <xlocale.h>
+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 <sys/param.h>
 #include <sys/types.h>
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 <float.h>
 
 #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 <ctype.h>
 
+#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)
       <entry>check constraints, unique constraints, primary key constraints, foreign key constraints</entry>
      </row>
 
+     <row>
+      <entry><link linkend="catalog-pg-collation"><structname>pg_collation</structname></link></entry>
+      <entry>collations (locale information)</entry>
+     </row>
+
      <row>
       <entry><link linkend="catalog-pg-conversion"><structname>pg_conversion</structname></link></entry>
       <entry>encoding conversion information</entry>
       </entry>
      </row>
 
+     <row>
+      <entry><structfield>attcollation</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-collation"><structname>pg_collation</structname></link>.oid</literal></entry>
+      <entry>
+       The defined collation of the column, zero if the column does
+       not have a collatable type.
+      </entry>
+     </row>
+
      <row>
       <entry><structfield>attacl</structfield></entry>
       <entry><type>aclitem[]</type></entry>
 
  </sect1>
 
+ <sect1 id="catalog-pg-collation">
+  <title><structname>pg_collation</structname></title>
+
+  <indexterm zone="catalog-pg-collation">
+   <primary>pg_collation</primary>
+  </indexterm>
+
+  <para>
+   The catalog <structname>pg_collation</structname> describes the
+   available collations, which are essentially mappings from an SQL
+   name to operating system locale categories.
+   See <xref linkend="locale"> for more information.
+  </para>
+
+  <table>
+   <title><structname>pg_collation</> Columns</title>
+
+   <tgroup cols="4">
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Type</entry>
+      <entry>References</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><structfield>collname</structfield></entry>
+      <entry><type>name</type></entry>
+      <entry></entry>
+      <entry>Collation name (unique per namespace and encoding)</entry>
+     </row>
+
+     <row>
+      <entry><structfield>collnamespace</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-namespace"><structname>pg_namespace</structname></link>.oid</literal></entry>
+      <entry>
+       The OID of the namespace that contains this collation
+      </entry>
+     </row>
+
+     <row>
+      <entry><structfield>collencoding</structfield></entry>
+      <entry><type>int4</type></entry>
+      <entry></entry>
+      <entry>Encoding to which the collation is applicable</entry>
+     </row>
+
+     <row>
+      <entry><structfield>collcollate</structfield></entry>
+      <entry><type>name</type></entry>
+      <entry></entry>
+      <entry>LC_COLLATE for this collation object</entry>
+     </row>
+
+     <row>
+      <entry><structfield>collctype</structfield></entry>
+      <entry><type>name</type></entry>
+      <entry></entry>
+      <entry>LC_CTYPE for this collation object</entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+ </sect1>
+
  <sect1 id="catalog-pg-conversion">
   <title><structname>pg_conversion</structname></title>
 
       </entry>
      </row>
 
+     <row>
+      <entry><structfield>indcollation</structfield></entry>
+      <entry><type>oidvector</type></entry>
+      <entry><literal><link linkend="catalog-pg-collation"><structname>pg_collation</structname></link>.oid</literal></entry>
+      <entry>
+       For each column in the index key, this contains the OID of the
+       collation to use for the index.
+      </entry>
+     </row>
+
      <row>
       <entry><structfield>indclass</structfield></entry>
       <entry><type>oidvector</type></entry>
        </para></entry>
      </row>
 
+     <row>
+      <entry><structfield>typcollation</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-collation"><structname>pg_collation</structname></link>.oid</literal></entry>
+      <entry><para>
+       <structfield>typcollation</structfield> 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 <literal>COLLATE</literal> clauses with the type is
+       invalid.  A base type that supports collations will have
+       <symbol>DEFAULT_COLLATION_OID</symbol> here.  A domain can have
+       another collation OID, if one was defined for the domain.
+      </para></entry>
+     </row>
+
      <row>
       <entry><structfield>typdefaultbin</structfield></entry>
       <entry><type>pg_node_tree</type></entry>
index 644c711dcccfb7efa349e95e5f8326cc3545bff2..49e1bd25b43fbbe57ae37b737163c27c199a6f27 100644 (file)
@@ -304,6 +304,170 @@ initdb --locale=sv_SE
  </sect1>
 
 
+ <sect1 id="collation">
+  <title>Collation Support</title>
+
+  <para>
+   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
+   <symbol>LC_COLLATE</symbol> and <symbol>LC_CTYPE</symbol> settings
+   of a database cannot be changed after its creation.
+  </para>
+
+  <note>
+   <para>
+    The collation support feature is currently only known to work on
+    Linux/glibc and Mac OS X platforms.
+   </para>
+  </note>
+
+  <sect2>
+   <title>Concepts</title>
+
+   <para>
+    Conceptually, every datum of a collatable data type has a
+    collation.  (Collatable data types in the base system are
+    <type>text</type>, <type>varchar</type>, and <type>char</type>.
+    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.
+   </para>
+
+   <para>
+    The collation of a datum can also be the <quote>default</quote>
+    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.
+   </para>
+
+   <para>
+    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 <literal>ORDER BY</literal> clause
+    and a function or operator call such as <literal>&lt;</literal>.
+    The collation to apply for the performance of the <literal>ORDER
+    BY</literal> 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, <function>lower</function>,
+    <function>upper</function>, and <function>initcap</function>.
+   </para>
+
+   <para>
+    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.
+   </para>
+
+   <para>
+    The <firstterm>collation derivation</firstterm> 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
+    <literal>COLLATE</literal> 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:
+
+    <orderedlist>
+     <listitem>
+      <para>
+       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.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       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.
+      </para>
+     </listitem>
+    </orderedlist>
+
+    For example, take this table definition:
+<programlisting>
+CREATE TABLE test1 (
+    a text COLLATE "x",
+    ...
+);
+</programlisting>
+
+    Then in
+<programlisting>
+SELECT a || 'foo' FROM test1;
+</programlisting>
+    the result collation of the <literal>||</literal> operator is
+    <literal>"x"</literal> because it combines an implicitly derived
+    collation with the default collation.  But in
+<programlisting>
+SELECT a || ('foo' COLLATE "y") FROM test1;
+</programlisting>
+    the result collation is <literal>"y"</literal> because the explicit
+    collation derivation overrides the implicit one.
+   </para>
+  </sect2>
+
+  <sect2>
+   <title>Managing Collations</title>
+
+   <para>
+    A collation is an SQL schema object that maps an SQL name to
+    operating system locales.  In particular, it maps to a combination
+    of <symbol>LC_COLLATE</symbol> and <symbol>LC_CTYPE</symbol>.  (As
+    the name would indicate, the main purpose of a collation is to set
+    <symbol>LC_COLLATE</symbol>, which controls the sort order.  But
+    it is rarely necessary in practice to have an
+    <symbol>LC_CTYPE</symbol> setting that is different from
+    <symbol>LC_COLLATE</symbol>, so it is more convenient to collect
+    these under one concept than to create another infrastructure for
+    setting <symbol>LC_CTYPE</symbol> per datum.)  Also, a collation
+    is tied to a character encoding.  The same collation name may
+    exist for different encodings.
+   </para>
+
+   <para>
+    When a database system is initialized, <command>initdb</command>
+    populates the system catalog <literal>pg_collation</literal> 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 <literal>de_DE.utf8</literal>.
+    <command>initdb</command> would then create a collation named
+    <literal>de_DE.utf8</literal> for encoding <literal>UTF8</literal>
+    that has both <symbol>LC_COLLATE</symbol> and
+    <symbol>LC_CTYPE</symbol> set to <literal>de_DE.utf8</literal>.
+    It will also create a collation with the <literal>.utf8</literal>
+    tag stripped off the name.  So you could also use the collation
+    under the name <literal>de_DE</literal>, 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.
+   </para>
+
+   <para>
+    In case a collation is needed that has different values for
+    <symbol>LC_COLLATE</symbol> and <symbol>LC_CTYPE</symbol>, 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.
+   </para>
+  </sect2>
+ </sect1>
+
  <sect1 id="multibyte">
   <title>Character Set Support</title>
 
index 897078ac3e637757611a05c90457b40cbb462c61..70a1bd9ee9fb9559cefaf5573bc73d4237525e70 100644 (file)
@@ -13059,6 +13059,12 @@ SELECT relname FROM pg_class WHERE pg_table_is_visible(oid);
      </thead>
 
      <tbody>
+      <row>
+       <entry><literal><function>pg_collation_is_visible(<parameter>collation_oid</parameter>)</function></literal>
+       </entry>
+       <entry><type>boolean</type></entry>
+       <entry>is collation visible in search path</entry>
+      </row>
       <row>
        <entry><literal><function>pg_conversion_is_visible(<parameter>conversion_oid</parameter>)</function></literal>
        </entry>
@@ -13123,6 +13129,9 @@ SELECT relname FROM pg_class WHERE pg_table_is_visible(oid);
     </tgroup>
    </table>
 
+   <indexterm>
+    <primary>pg_collation_is_visible</primary>
+   </indexterm>
    <indexterm>
     <primary>pg_conversion_is_visible</primary>
    </indexterm>
@@ -13256,7 +13265,7 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
 
      <tbody>
       <row>
-       <entry><literal><function>format_type(<parameter>type_oid</parameter>, <parameter>typemod</>)</function></literal></entry>
+       <entry><literal><function>format_type(<parameter>type_oid</parameter> [, <parameter>typemod</> [, <parameter>collation_oid</> ]])</function></literal></entry>
        <entry><type>text</type></entry>
        <entry>get SQL name of a data type</entry>
       </row>
@@ -13392,7 +13401,9 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
   <para>
    <function>format_type</function> 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 <literal>COLLATE</> clause
+   followed by a formatted collation name is appended.
   </para>
 
   <para>
index 8fec13ee1d8905f23a04e661df008796995d7d0a..15fbe0d614e7b8854c524c9334dbe8f7321b9877 100644 (file)
@@ -921,6 +921,7 @@ CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable>
    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
+   <literal>COLLATE</literal>,
    <literal>ASC</>/<literal>DESC</> and/or
    <literal>NULLS FIRST</>/<literal>NULLS LAST</>).
   </para>
@@ -1002,6 +1003,47 @@ SELECT am.amname AS index_method,
  </sect1>
 
 
+ <sect1 id="indexes-collations">
+  <title>Collations and Indexes</title>
+
+  <para>
+   An index can only support one collation for one column or
+   expression.  If multiple collations are of interest, multiple
+   indexes may be created.
+  </para>
+
+  <para>
+   Consider these statements:
+<programlisting>
+CREATE TABLE test1c (
+    id integer,
+    content varchar COLLATE "x"
+);
+
+CREATE INDEX test1c_content_index ON test1c (content);
+</programlisting>
+   The created index automatically follows the collation of the
+   underlying column, and so a query of the form
+<programlisting>
+SELECT * FROM test1c WHERE content = <replaceable>constant</replaceable>;
+</programlisting>
+   could use the index.
+  </para>
+
+  <para>
+   If in addition, a query of the form, say,
+<programlisting>
+SELECT * FROM test1c WHERE content &gt; <replaceable>constant</replaceable> COLLATE "y";
+</programlisting>
+   is of interest, an additional index could be created that supports
+   the <literal>"y"</literal> collation, like so:
+<programlisting>
+CREATE INDEX test1c_content_index ON test1c (content COLLATE "y");
+</programlisting>
+  </para>
+ </sect1>
+
+
  <sect1 id="indexes-examine">
   <title>Examining Index Usage</title>
 
index 87a7654d6ce349425bcc429fb28e4000dd6a383b..83be889c6d9110cc44fa7918dcf274d6825b0f22 100644 (file)
@@ -22,6 +22,7 @@ PostgreSQL documentation
  <refsynopsisdiv>
 <synopsis>
 CREATE DOMAIN <replaceable class="parameter">name</replaceable> [ AS ] <replaceable class="parameter">data_type</replaceable>
+    [ COLLATE <replaceable>collation</replaceable> ]
     [ DEFAULT <replaceable>expression</replaceable> ]
     [ <replaceable class="PARAMETER">constraint</replaceable> [ ... ] ]
 
@@ -83,6 +84,17 @@ CREATE DOMAIN <replaceable class="parameter">name</replaceable> [ AS ] <replacea
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><replaceable>collation</replaceable></term>
+      <listitem>
+       <para>
+        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).
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><literal>DEFAULT <replaceable>expression</replaceable></literal></term>
 
index 45c298e371e5884c9e5240f9986037f4f095f8a4..8ec7abbbd495231552751063a72925a73bee94df 100644 (file)
@@ -22,7 +22,7 @@ PostgreSQL documentation
  <refsynopsisdiv>
 <synopsis>
 CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ <replaceable class="parameter">name</replaceable> ] ON <replaceable class="parameter">table</replaceable> [ USING <replaceable class="parameter">method</replaceable> ]
-    ( { <replaceable class="parameter">column</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ <replaceable class="parameter">opclass</replaceable> ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
+    ( { <replaceable class="parameter">column</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] [ <replaceable class="parameter">opclass</replaceable> ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
     [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace</replaceable> ]
     [ WHERE <replaceable class="parameter">predicate</replaceable> ]
@@ -181,6 +181,20 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ <replaceable class="parameter">name</
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><replaceable class="parameter">collation</replaceable></term>
+      <listitem>
+       <para>
+        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.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><replaceable class="parameter">opclass</replaceable></term>
       <listitem>
index 5bffe303364b749a0e72ce07e8ca1808d1ac9de4..9d2d99ad2e3774654ff9acd3d496de1e6f9b34ee 100644 (file)
@@ -22,7 +22,7 @@ PostgreSQL documentation
  <refsynopsisdiv>
 <synopsis>
 CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name</replaceable> ( [
-  { <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ]
+  { <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [ COLLATE <replaceable>collation</replaceable> ] [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ]
     | <replaceable>table_constraint</replaceable>
     | LIKE <replaceable>parent_table</replaceable> [ <replaceable>like_option</replaceable> ... ] }
     [, ... ]
@@ -244,6 +244,17 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>COLLATE <replaceable>collation</replaceable></literal></term>
+    <listitem>
+     <para>
+      The <literal>COLLATE</> clause assigns a nondefault collation to
+      the column.  By default, the locale settings of the database are
+      used.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><literal>INHERITS ( <replaceable>parent_table</replaceable> [, ... ] )</literal></term>
     <listitem>
index a3c75b51d0fb3f461005249b9fbfc65a12adf35a..805fc2cf76051a996e580eb5870ca466305df043 100644 (file)
@@ -45,6 +45,7 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> (
     [ , DEFAULT = <replaceable class="parameter">default</replaceable> ]
     [ , ELEMENT = <replaceable class="parameter">element</replaceable> ]
     [ , DELIMITER = <replaceable class="parameter">delimiter</replaceable> ]
+    [ , COLLATABLE = <replaceable class="parameter">collatable</replaceable> ]
 )
 
 CREATE TYPE <replaceable class="parameter">name</replaceable>
@@ -352,6 +353,16 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
    with the array element type, not the array type itself.
   </para>
 
+  <para>
+   If the optional
+   parameter <replaceable class="parameter">collatable</replaceable>
+   is true, column definitions and expressions of the type may carry
+   collation information and allow the use of
+   the <literal>COLLATE</literal> 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.
+  </para>
   </refsect2>
 
   <refsect2>
index 61a87ff74d26827c3a7ca6fe7ca105d8f86228b0..6f5e2b1ab888326572456c378bfa0f149e86651c 100644 (file)
@@ -236,6 +236,27 @@ gmake check LANG=C MULTIBYTE=EUC_JP
     existing installation.
    </para>
   </sect2>
+
+  <sect2>
+   <title>Extra tests</title>
+
+   <para>
+    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 <envar>EXTRA_TESTS</envar>.  For
+    example, to run the <literal>numeric_big</literal> test:
+<screen>
+gmake check EXTRA_TESTS=numeric_big
+</screen>
+    To run the collation tests:
+<screen>
+gmake check EXTRA_TESTS=collate.linux.utf8 LANG=en_US.utf8
+</screen>
+    This test works only on Linux/glibc platforms and when run in a
+    UTF-8 locale.
+   </para>
+  </sect2>
   </sect1>
 
   <sect1 id="regress-evaluation">
index 004205f1265dcd89b467fc61ae012b91e0bf714e..00f665ae5444290f999cd0f1bc8078eff78ac007 100644 (file)
@@ -1899,6 +1899,54 @@ CAST ( <replaceable>expression</replaceable> AS <replaceable>type</replaceable>
    </note>
   </sect2>
 
+  <sect2 id="sql-syntax-collate-clause">
+   <title>COLLATE Clause</title>
+
+   <indexterm>
+    <primary>COLLATE</primary>
+   </indexterm>
+
+   <para>
+    The <literal>COLLATE</literal> clause overrides the collation of
+    an expression.  It is appended to the expression it applies to:
+<synopsis>
+<replaceable>expr</replaceable> COLLATE <replaceable>collation</replaceable>
+</synopsis>
+    where <replaceable>collation</replaceable> is a possibly
+    schema-qualified identifier.  The <literal>COLLATE</literal>
+    clause binds tighter than operators; parentheses can be used when
+    necessary.
+   </para>
+
+   <para>
+    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.
+   </para>
+
+   <para>
+    The two typical uses of the <literal>COLLATE</literal> clause are
+    overriding the sort order in an <literal>ORDER BY</> clause, for
+    example:
+<programlisting>
+SELECT a, b, c FROM tbl WHERE ... ORDER BY a COLLATE "C";
+</programlisting>
+    and overriding the collation of a function or operator call that
+    has locale-sensitive results, for example:
+<programlisting>
+SELECT * FROM tbl WHERE a > 'foo' COLLATE "C";
+</programlisting>
+    In the latter case it doesn't matter which argument of the
+    operator of function call the <literal>COLLATE</> clause is
+    attached to, because the collation that is applied by the operator
+    or function is derived from all arguments, and
+    the <literal>COLLATE</> clause will override the collations of all
+    other arguments.  Attaching nonmatching <literal>COLLATE</>
+    clauses to more than one argument, however, is an error.
+   </para>
+  </sect2>
+
   <sect2 id="sql-syntax-scalar-subqueries">
    <title>Scalar Subqueries</title>
 
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), &ltproc);
+       fmgr_info_collation(collation, &ltproc);
        greaterstr = make_greater_string(prefix_const, &ltproc);
        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
diff --git