summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Eisentraut2011-02-08 21:04:18 +0000
committerPeter Eisentraut2011-02-08 21:04:18 +0000
commit414c5a2ea65cbd38d79ffdf9b1fde7cc75c134e0 (patch)
tree016efd0c7108f659ea4f3c52ea54d78e1e5449e1
parent1703f0e8da2e8e3eccb6e12879c011ba106f8a62 (diff)
Per-column collation support
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
-rw-r--r--config/c-library.m429
-rwxr-xr-xconfigure108
-rw-r--r--configure.in2
-rw-r--r--contrib/btree_gin/btree_gin.c4
-rw-r--r--contrib/btree_gist/btree_text.c13
-rw-r--r--contrib/btree_gist/btree_utils_var.c3
-rw-r--r--contrib/citext/citext.c35
-rw-r--r--contrib/citext/citext.sql.in3
-rw-r--r--contrib/ltree/lquery_op.c5
-rw-r--r--doc/src/sgml/catalogs.sgml110
-rw-r--r--doc/src/sgml/charset.sgml164
-rw-r--r--doc/src/sgml/func.sgml15
-rw-r--r--doc/src/sgml/indices.sgml42
-rw-r--r--doc/src/sgml/ref/create_domain.sgml12
-rw-r--r--doc/src/sgml/ref/create_index.sgml16
-rw-r--r--doc/src/sgml/ref/create_table.sgml13
-rw-r--r--doc/src/sgml/ref/create_type.sgml11
-rw-r--r--doc/src/sgml/regress.sgml21
-rw-r--r--doc/src/sgml/syntax.sgml48
-rw-r--r--src/backend/access/common/scankey.c13
-rw-r--r--src/backend/access/common/tupdesc.c35
-rw-r--r--src/backend/access/gin/ginutil.c3
-rw-r--r--src/backend/access/index/indexam.c2
-rw-r--r--src/backend/access/nbtree/nbtsearch.c4
-rw-r--r--src/backend/bootstrap/bootstrap.c50
-rw-r--r--src/backend/catalog/Makefile2
-rw-r--r--src/backend/catalog/genbki.pl1
-rw-r--r--src/backend/catalog/heap.c7
-rw-r--r--src/backend/catalog/index.c15
-rw-r--r--src/backend/catalog/namespace.c154
-rw-r--r--src/backend/catalog/pg_type.c5
-rw-r--r--src/backend/catalog/system_views.sql4
-rw-r--r--src/backend/catalog/toasting.c6
-rw-r--r--src/backend/commands/analyze.c4
-rw-r--r--src/backend/commands/dbcommands.c4
-rw-r--r--src/backend/commands/functioncmds.c4
-rw-r--r--src/backend/commands/indexcmds.c26
-rw-r--r--src/backend/commands/seclabel.c3
-rw-r--r--src/backend/commands/sequence.c20
-rw-r--r--src/backend/commands/tablecmds.c54
-rw-r--r--src/backend/commands/typecmds.c31
-rw-r--r--src/backend/commands/view.c3
-rw-r--r--src/backend/executor/execQual.c37
-rw-r--r--src/backend/executor/execTuples.c12
-rw-r--r--src/backend/executor/nodeAgg.c11
-rw-r--r--src/backend/executor/nodeFunctionscan.c7
-rw-r--r--src/backend/executor/nodeIndexscan.c11
-rw-r--r--src/backend/executor/nodeMergeAppend.c3
-rw-r--r--src/backend/executor/nodeMergejoin.c4
-rw-r--r--src/backend/executor/nodeSort.c1
-rw-r--r--src/backend/executor/nodeSubplan.c2
-rw-r--r--src/backend/executor/nodeWindowAgg.c7
-rw-r--r--src/backend/nodes/copyfuncs.c46
-rw-r--r--src/backend/nodes/equalfuncs.c40
-rw-r--r--src/backend/nodes/makefuncs.c17
-rw-r--r--src/backend/nodes/nodeFuncs.c230
-rw-r--r--src/backend/nodes/outfuncs.c53
-rw-r--r--src/backend/nodes/readfuncs.c39
-rw-r--r--src/backend/optimizer/path/costsize.c4
-rw-r--r--src/backend/optimizer/path/indxpath.c75
-rw-r--r--src/backend/optimizer/path/pathkeys.c32
-rw-r--r--src/backend/optimizer/plan/createplan.c64
-rw-r--r--src/backend/optimizer/plan/planagg.c3
-rw-r--r--src/backend/optimizer/plan/setrefs.c3
-rw-r--r--src/backend/optimizer/plan/subselect.c40
-rw-r--r--src/backend/optimizer/prep/prepjointree.c1
-rw-r--r--src/backend/optimizer/prep/preptlist.c4
-rw-r--r--src/backend/optimizer/prep/prepunion.c21
-rw-r--r--src/backend/optimizer/util/clauses.c49
-rw-r--r--src/backend/optimizer/util/plancat.c4
-rw-r--r--src/backend/optimizer/util/predtest.c1
-rw-r--r--src/backend/parser/analyze.c17
-rw-r--r--src/backend/parser/gram.y50
-rw-r--r--src/backend/parser/parse_agg.c6
-rw-r--r--src/backend/parser/parse_clause.c10
-rw-r--r--src/backend/parser/parse_coerce.c132
-rw-r--r--src/backend/parser/parse_cte.c26
-rw-r--r--src/backend/parser/parse_expr.c53
-rw-r--r--src/backend/parser/parse_func.c15
-rw-r--r--src/backend/parser/parse_node.c8
-rw-r--r--src/backend/parser/parse_oper.c16
-rw-r--r--src/backend/parser/parse_param.c3
-rw-r--r--src/backend/parser/parse_relation.c33
-rw-r--r--src/backend/parser/parse_target.c9
-rw-r--r--src/backend/parser/parse_type.c97
-rw-r--r--src/backend/parser/parse_utilcmd.c9
-rw-r--r--src/backend/rewrite/rewriteHandler.c2
-rw-r--r--src/backend/tsearch/ts_locale.c28
-rw-r--r--src/backend/tsearch/wparser_def.c6
-rw-r--r--src/backend/utils/adt/arrayfuncs.c5
-rw-r--r--src/backend/utils/adt/format_type.c25
-rw-r--r--src/backend/utils/adt/formatting.c140
-rw-r--r--src/backend/utils/adt/like.c16
-rw-r--r--src/backend/utils/adt/oracle_compat.c9
-rw-r--r--src/backend/utils/adt/pg_locale.c219
-rw-r--r--src/backend/utils/adt/ruleutils.c77
-rw-r--r--src/backend/utils/adt/selfuncs.c46
-rw-r--r--src/backend/utils/adt/varchar.c21
-rw-r--r--src/backend/utils/adt/varlena.c34
-rw-r--r--src/backend/utils/cache/lsyscache.c94
-rw-r--r--src/backend/utils/cache/relcache.c35
-rw-r--r--src/backend/utils/cache/syscache.c23
-rw-r--r--src/backend/utils/errcodes.txt2
-rw-r--r--src/backend/utils/fmgr/fmgr.c66
-rw-r--r--src/backend/utils/fmgr/funcapi.c4
-rw-r--r--src/backend/utils/mb/mbutils.c10
-rw-r--r--src/backend/utils/sort/tuplesort.c9
-rw-r--r--src/bin/initdb/initdb.c189
-rw-r--r--src/bin/pg_dump/pg_dump.c47
-rw-r--r--src/bin/psql/describe.c23
-rw-r--r--src/include/access/skey.h2
-rw-r--r--src/include/access/tupdesc.h6
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/catalog/index.h1
-rw-r--r--src/include/catalog/indexing.h5
-rw-r--r--src/include/catalog/namespace.h4
-rw-r--r--src/include/catalog/pg_attribute.h14
-rw-r--r--src/include/catalog/pg_class.h4
-rw-r--r--src/include/catalog/pg_collation.h62
-rw-r--r--src/include/catalog/pg_index.h12
-rw-r--r--src/include/catalog/pg_proc.h6
-rw-r--r--src/include/catalog/pg_type.h284
-rw-r--r--src/include/catalog/pg_type_fn.h3
-rw-r--r--src/include/commands/vacuum.h3
-rw-r--r--src/include/fmgr.h18
-rw-r--r--src/include/mb/pg_wchar.h4
-rw-r--r--src/include/nodes/makefuncs.h5
-rw-r--r--src/include/nodes/nodeFuncs.h2
-rw-r--r--src/include/nodes/nodes.h1
-rw-r--r--src/include/nodes/parsenodes.h15
-rw-r--r--src/include/nodes/pg_list.h9
-rw-r--r--src/include/nodes/plannodes.h4
-rw-r--r--src/include/nodes/primnodes.h29
-rw-r--r--src/include/nodes/relation.h3
-rw-r--r--src/include/optimizer/subselect.h2
-rw-r--r--src/include/parser/parse_agg.h1
-rw-r--r--src/include/parser/parse_coerce.h2
-rw-r--r--src/include/parser/parse_node.h1
-rw-r--r--src/include/parser/parse_type.h11
-rw-r--r--src/include/parser/parsetree.h2
-rw-r--r--src/include/pg_config.h.in6
-rw-r--r--src/include/port.h2
-rw-r--r--src/include/utils/builtins.h4
-rw-r--r--src/include/utils/formatting.h6
-rw-r--r--src/include/utils/lsyscache.h4
-rw-r--r--src/include/utils/pg_locale.h23
-rw-r--r--src/include/utils/rel.h1
-rw-r--r--src/include/utils/selfuncs.h3
-rw-r--r--src/include/utils/syscache.h2
-rw-r--r--src/include/utils/tuplesort.h4
-rw-r--r--src/pl/plpgsql/src/pl_comp.c2
-rw-r--r--src/port/chklocale.c21
-rw-r--r--src/test/regress/GNUmakefile6
-rw-r--r--src/test/regress/expected/collate.linux.utf8.out734
-rw-r--r--src/test/regress/expected/sanity_check.out3
-rw-r--r--src/test/regress/sql/collate.linux.utf8.sql224
156 files changed, 4519 insertions, 582 deletions
diff --git a/config/c-library.m4 b/config/c-library.m4
index 98e03e3d18..cddeafaec2 100644
--- a/config/c-library.m4
+++ b/config/c-library.m4
@@ -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
diff --git a/configure b/configure
index cc809acd95..f4d11abc7e 100755
--- 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
diff --git a/configure.in b/configure.in
index e9a1b2dcba..0dffc51d0b 100644
--- a/configure.in
+++ b/configure.in
@@ -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>
diff --git a/contrib/btree_gin/btree_gin.c b/contrib/btree_gin/btree_gin.c
index 486a662c78..144f6db184 100644
--- a/contrib/btree_gin/btree_gin.c
+++ b/contrib/btree_gin/btree_gin.c
@@ -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, \
diff --git a/contrib/btree_gist/btree_text.c b/contrib/btree_gist/btree_text.c
index 40d0b9ad79..665dfe78b4 100644
--- a/contrib/btree_gist/btree_text.c
+++ b/contrib/btree_gist/btree_text.c
@@ -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 =
diff --git a/contrib/btree_gist/btree_utils_var.c b/contrib/btree_gist/btree_utils_var.c
index 5fc93cfbff..8f3173e499 100644
--- a/contrib/btree_gist/btree_utils_var.c
+++ b/contrib/btree_gist/btree_utils_var.c
@@ -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
{
diff --git a/contrib/citext/citext.c b/contrib/citext/citext.c
index 9991825853..25e4dd3999 100644
--- a/contrib/citext/citext.c
+++ b/contrib/citext/citext.c
@@ -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);
}
diff --git a/contrib/citext/citext.sql.in b/contrib/citext/citext.sql.in
index 1e75b55397..7056d0ead3 100644
--- a/contrib/citext/citext.sql.in
+++ b/contrib/citext/citext.sql.in
@@ -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
);
--
diff --git a/contrib/ltree/lquery_op.c b/contrib/ltree/lquery_op.c
index fe9ecb118a..da1086a8eb 100644
--- a/contrib/ltree/lquery_op.c
+++ b/contrib/ltree/lquery_op.c
@@ -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);
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index be132f2eb7..f31662c272 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -104,6 +104,11 @@
</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>
</row>
@@ -1114,6 +1119,16 @@
</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>
<entry></entry>
@@ -2050,6 +2065,76 @@
</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>
@@ -3126,6 +3211,16 @@
</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>
<entry><literal><link linkend="catalog-pg-opclass"><structname>pg_opclass</structname></link>.oid</literal></entry>
@@ -5867,6 +5962,21 @@
</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>
<entry></entry>
diff --git a/doc/src/sgml/charset.sgml b/doc/src/sgml/charset.sgml
index 644c711dcc..49e1bd25b4 100644
--- a/doc/src/sgml/charset.sgml
+++ b/doc/src/sgml/charset.sgml
@@ -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>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 897078ac3e..70a1bd9ee9 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -13060,6 +13060,12 @@ SELECT relname FROM pg_class WHERE pg_table_is_visible(oid);
<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>
<entry><type>boolean</type></entry>
@@ -13124,6 +13130,9 @@ SELECT relname FROM pg_class WHERE pg_table_is_visible(oid);
</table>
<indexterm>
+ <primary>pg_collation_is_visible</primary>
+ </indexterm>
+ <indexterm>
<primary>pg_conversion_is_visible</primary>
</indexterm>
<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>
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index 8fec13ee1d..15fbe0d614 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -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>
diff --git a/doc/src/sgml/ref/create_domain.sgml b/doc/src/sgml/ref/create_domain.sgml
index 87a7654d6c..83be889c6d 100644
--- a/doc/src/sgml/ref/create_domain.sgml
+++ b/doc/src/sgml/ref/create_domain.sgml
@@ -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> [ ... ] ]
@@ -84,6 +85,17 @@ CREATE DOMAIN <replaceable class="parameter">name</replaceable> [ AS ] <replacea
</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>
<listitem>
diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml
index 45c298e371..8ec7abbbd4 100644
--- a/doc/src/sgml/ref/create_index.sgml
+++ b/doc/src/sgml/ref/create_index.sgml
@@ -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> ]
@@ -182,6 +182,20 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ <replaceable class="parameter">name</
</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>
<para>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 5bffe30336..9d2d99ad2e 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -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> ... ] }
[, ... ]
@@ -245,6 +245,17 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
</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>
<para>
diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml
index a3c75b51d0..805fc2cf76 100644
--- a/doc/src/sgml/ref/create_type.sgml
+++ b/doc/src/sgml/ref/create_type.sgml
@@ -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>
diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml
index 61a87ff74d..6f5e2b1ab8 100644
--- a/doc/src/sgml/regress.sgml
+++ b/doc/src/sgml/regress.sgml
@@ -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">
diff --git a/doc/src/sgml/syntax.sgml b/doc/src/sgml/syntax.sgml
index 004205f126..00f665ae54 100644
--- a/doc/src/sgml/syntax.sgml
+++ b/doc/src/sgml/syntax.sgml
@@ -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>
diff --git a/src/backend/access/common/scankey.c b/src/backend/access/common/scankey.c
index 232322a91e..41cd36fce9 100644
--- a/src/backend/access/common/scankey.c
+++ b/src/backend/access/common/scankey.c
@@ -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;
+}
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index cfc95b9312..0cd1f941e3 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -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;
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index 0a7c1c521f..35f71c843e 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -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);
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index 6e0db79517..6e6af18d47 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -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;
diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c
index cf74f7776c..be8d958352 100644
--- a/src/backend/access/nbtree/nbtsearch.c
+++ b/src/backend/access/nbtree/nbtsearch.c
@@ -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);
}
}
}
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index dbcc55c822..528ea23d4c 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -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)
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 17f4cc6cfc..7cffde1769 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -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 \
)
diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl
index d07e892525..0aeaf5bfd7 100644
--- a/src/backend/catalog/genbki.pl
+++ b/src/backend/catalog/genbki.pl
@@ -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.
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index c2c7a3d8f4..14c69f3faa 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -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);
}
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 92672bb733..9e6012125f 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -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;
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index d8bb4e39a8..8b04b9fd9b 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -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);
@@ -1611,6 +1614,89 @@ OpfamilyIsVisible(Oid opfid)
}
/*
+ * 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.
* Returns OID if conversion found in search path, else InvalidOid.
@@ -2808,6 +2894,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
*/
Oid
@@ -3567,6 +3710,17 @@ pg_opclass_is_visible(PG_FUNCTION_ARGS)
}
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)
{
Oid oid = PG_GETARG_OID(0);
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 9c249a7ff7..8ceaab1fb1 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -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
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 718e996e6b..4fa1453b14 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -672,5 +672,9 @@ COMMENT ON FUNCTION ts_debug(text) IS
--
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';
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index c4be3a9ae3..5d5496df98 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -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);
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 4c106dd8c5..bafdc80d58 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -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++)
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 9a9b4cbf3d..c7e0c6a877 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -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 ||
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 2a2b7c732e..dad65ee8ff 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -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)
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 94ed437002..c8e21b68f5 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -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
@@ -875,6 +883,20 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
}
/*
+ * 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.
*/
classOidP[attn] = GetIndexOpClass(attribute->opclass,
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index b927e76abd..27917fc6c0 100644
--- a/src/backend/commands/seclabel.c
+++ b/src/backend/commands/seclabel.c
@@ -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);
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 0ff722d6f8..80ad516de1 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -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;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index eac72106fd..c0a4e6f954 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -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;
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 5500df03ab..25d0f3596e 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -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),
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index a684172c8a..22dfc923cf 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -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;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 5e38c20ca6..2b5dd2dbf8 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -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),
@@ -4026,6 +4029,20 @@ ExecEvalRelabelType(GenericExprState *exprstate,
}
/* ----------------------------------------------------------------
+ * ExecEvalCollateClause
+ *
+ * Evaluate a CollateClause node.
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalCollateClause(GenericExprState *exprstate,
+ ExprContext *econtext,
+ bool *isNull, ExprDoneCond *isDone)
+{
+ return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone);
+}
+
+/* ----------------------------------------------------------------
* ExecEvalCoerceViaIO
*
* Evaluate a CoerceViaIO node.
@@ -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;
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 810ade23f6..3f44ef0186 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -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;
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index cb652862ed..d9bed220e4 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -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++;
}
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index 142f81767a..dedd255010 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -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
{
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index bbf894e3ac..55fce94b32 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -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))
{
diff --git a/src/backend/executor/nodeMergeAppend.c b/src/backend/executor/nodeMergeAppend.c
index 24c5cd8a5b..e46af8cff9 100644
--- a/src/backend/executor/nodeMergeAppend.c
+++ b/src/backend/executor/nodeMergeAppend.c
@@ -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;
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 6f6645687f..c0b9f23085 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -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);
diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c
index 6a03d9ce82..e4b28c59b4 100644
--- a/src/backend/executor/nodeSort.c
+++ b/src/backend/executor/nodeSort.c
@@ -86,6 +86,7 @@ ExecSort(SortState *node)
plannode->numCols,
plannode->sortColIdx,
plannode->sortOperators,
+ plannode->collations,
plannode->nullsFirst,
work_mem,
node->randomAccess);
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index 8c263181fd..e9b3d76df1 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -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,
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index f37ab39de0..372262ad7f 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -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,
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 662916d210..9b2c874d6d 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -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;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index b7dc450447..837eafaacc 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -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;
@@ -2095,6 +2117,17 @@ _equalTypeCast(TypeCast *a, TypeCast *b)
}
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)
{
COMPARE_NODE_FIELD(node);
@@ -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;
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index 79da1853c3..0225f19382 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -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;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index d17b347e45..8a23047d38 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -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;
@@ -460,6 +464,215 @@ exprTypmod(Node *expr)
}
/*
+ * 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
* typmod-coercion function. Optionally extract the result's typmod.
@@ -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:
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index c8eccce5a7..3b3e5448fd 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -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);
}
@@ -2028,6 +2062,17 @@ _outTypeCast(StringInfo str, TypeCast *node)
}
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)
{
WRITE_NODE_TYPE("INDEXELEM");
@@ -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;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 99d0576e5e..b007caeee3 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -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();
}
@@ -730,6 +743,22 @@ _readRelabelType(void)
}
/*
+ * _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
*/
static CoerceViaIO *
@@ -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))
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 6c98c49bff..ffb066283f 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -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;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index a3101d7ea7..65bc9be8da 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -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)
{
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index d5536fc2b3..fd759281ed 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -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"
@@ -30,10 +31,10 @@
#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)
{
/*
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index c74125f1f7..f01114c673 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -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 *
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index dfbc624aa8..f885385296 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -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.
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 02f5cabd25..867238ecc8 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -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))
{
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index febec1e15f..29eb9dced4 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -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 */
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index f92bcd41b1..bd678ac7ed 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -445,6 +445,7 @@ inline_set_returning_functions(PlannerInfo *root)
rte->funcexpr = NULL;
rte->funccoltypes = NIL;
rte->funccoltypmods = NIL;
+ rte->funccolcollations = NIL;
}
}
}
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index 36c19438c0..34b38eb329 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -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 a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 449c8dab50..f62af6c37d 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -86,7 +86,7 @@ static List *generate_setop_tlist(List *colTypes, int flag,
bool hack_constants,
List *input_tlist,
List *refnames_tlist);
-static List *generate_append_tlist(List *colTypes, bool flag,
+static List *generate_append_tlist(List *colTypes, List *colCollations, bool flag,
List *input_plans,
List *refnames_tlist);
static List *generate_setop_grouplist(SetOperationStmt *op, List *targetlist);
@@ -348,7 +348,7 @@ generate_recursion_plan(SetOperationStmt *setOp, PlannerInfo *root,
/*
* Generate tlist for RecursiveUnion plan node --- same as in Append cases
*/
- tlist = generate_append_tlist(setOp->colTypes, false,
+ tlist = generate_append_tlist(setOp->colTypes, setOp->colCollations, false,
list_make2(lplan, rplan),
refnames_tlist);
@@ -443,7 +443,7 @@ generate_union_plan(SetOperationStmt *op, PlannerInfo *root,
* concerned, but we must make it look real anyway for the benefit of the
* next plan level up.
*/
- tlist = generate_append_tlist(op->colTypes, false,
+ tlist = generate_append_tlist(op->colTypes, op->colCollations, false,
planlist, refnames_tlist);
/*
@@ -534,7 +534,7 @@ generate_nonunion_plan(SetOperationStmt *op, PlannerInfo *root,
* column is shown as a variable not a constant, else setrefs.c will get
* confused.
*/
- tlist = generate_append_tlist(op->colTypes, true,
+ tlist = generate_append_tlist(op->colTypes, op->colCollations, true,
planlist, refnames_tlist);
/*
@@ -885,6 +885,7 @@ generate_setop_tlist(List *colTypes, int flag,
inputtle->resno,
exprType((Node *) inputtle->expr),
exprTypmod((Node *) inputtle->expr),
+ exprCollation((Node *) inputtle->expr),
0);
if (exprType(expr) != colType)
{
@@ -936,13 +937,14 @@ generate_setop_tlist(List *colTypes, int flag,
* The Vars are always generated with varno 0.
*/
static List *
-generate_append_tlist(List *colTypes, bool flag,
+generate_append_tlist(List *colTypes, List*colCollations, bool flag,
List *input_plans,
List *refnames_tlist)
{
List *tlist = NIL;
int resno = 1;
ListCell *curColType;
+ ListCell *curColCollation;
ListCell *ref_tl_item;
int colindex;
TargetEntry *tle;
@@ -997,10 +999,11 @@ generate_append_tlist(List *colTypes, bool flag,
* Now we can build the tlist for the Append.
*/
colindex = 0;
- forboth(curColType, colTypes, ref_tl_item, refnames_tlist)
+ forthree(curColType, colTypes, curColCollation, colCollations, ref_tl_item, refnames_tlist)
{
Oid colType = lfirst_oid(curColType);
int32 colTypmod = colTypmods[colindex++];
+ Oid colColl = lfirst_oid(curColCollation);
TargetEntry *reftle = (TargetEntry *) lfirst(ref_tl_item);
Assert(reftle->resno == resno);
@@ -1009,6 +1012,7 @@ generate_append_tlist(List *colTypes, bool flag,
resno,
colType,
colTypmod,
+ colColl,
0);
tle = makeTargetEntry((Expr *) expr,
(AttrNumber) resno++,
@@ -1025,6 +1029,7 @@ generate_append_tlist(List *colTypes, bool flag,
resno,
INT4OID,
-1,
+ InvalidOid,
0);
tle = makeTargetEntry((Expr *) expr,
(AttrNumber) resno++,
@@ -1344,6 +1349,7 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
char *attname;
Oid atttypid;
int32 atttypmod;
+ Oid attcollation;
int new_attno;
att = old_tupdesc->attrs[old_attno];
@@ -1356,6 +1362,7 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
attname = NameStr(att->attname);
atttypid = att->atttypid;
atttypmod = att->atttypmod;
+ attcollation = att->attcollation;
/*
* When we are generating the "translation list" for the parent table
@@ -1367,6 +1374,7 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
(AttrNumber) (old_attno + 1),
atttypid,
atttypmod,
+ attcollation,
0));
continue;
}
@@ -1409,6 +1417,7 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
(AttrNumber) (new_attno + 1),
atttypid,
atttypmod,
+ attcollation,
0));
}
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 86990c8725..fa0952618b 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -100,7 +100,7 @@ static List *simplify_and_arguments(List *args,
bool *haveNull, bool *forceFalse);
static Node *simplify_boolean_equality(Oid opno, List *args);
static Expr *simplify_function(Oid funcid,
- Oid result_type, int32 result_typmod, List **args,
+ Oid result_type, int32 result_typmod, Oid collid, List **args,
bool has_named_args,
bool allow_inline,
eval_const_expressions_context *context);
@@ -114,7 +114,7 @@ static List *fetch_function_defaults(HeapTuple func_tuple);
static void recheck_cast_function_args(List *args, Oid result_type,
HeapTuple func_tuple);
static Expr *evaluate_function(Oid funcid,
- Oid result_type, int32 result_typmod, List *args,
+ Oid result_type, int32 result_typmod, Oid collid, List *args,
HeapTuple func_tuple,
eval_const_expressions_context *context);
static Expr *inline_function(Oid funcid, Oid result_type, List *args,
@@ -156,6 +156,7 @@ make_opclause(Oid opno, Oid opresulttype, bool opretset,
expr->args = list_make2(leftop, rightop);
else
expr->args = list_make1(leftop);
+ expr->collid = select_common_collation(NULL, expr->args, false);
expr->location = -1;
return (Expr *) expr;
}
@@ -1973,7 +1974,7 @@ set_coercionform_dontcare_walker(Node *node, void *context)
*/
static bool
rowtype_field_matches(Oid rowtypeid, int fieldnum,
- Oid expectedtype, int32 expectedtypmod)
+ Oid expectedtype, int32 expectedtypmod, Oid expectedcollation)
{
TupleDesc tupdesc;
Form_pg_attribute attr;
@@ -1990,7 +1991,8 @@ rowtype_field_matches(Oid rowtypeid, int fieldnum,
attr = tupdesc->attrs[fieldnum - 1];
if (attr->attisdropped ||
attr->atttypid != expectedtype ||
- attr->atttypmod != expectedtypmod)
+ attr->atttypmod != expectedtypmod ||
+ attr->attcollation != expectedcollation)
{
ReleaseTupleDesc(tupdesc);
return false;
@@ -2121,6 +2123,7 @@ eval_const_expressions_mutator(Node *node,
int16 typLen;
bool typByVal;
Datum pval;
+ Const *cnst;
Assert(prm->ptype == param->paramtype);
get_typlenbyval(param->paramtype, &typLen, &typByVal);
@@ -2128,12 +2131,14 @@ eval_const_expressions_mutator(Node *node,
pval = prm->value;
else
pval = datumCopy(prm->value, typByVal, typLen);
- return (Node *) makeConst(param->paramtype,
+ cnst = makeConst(param->paramtype,
param->paramtypmod,
(int) typLen,
pval,
prm->isnull,
typByVal);
+ cnst->constcollid = param->paramcollation;
+ return (Node *) cnst;
}
}
}
@@ -2173,6 +2178,7 @@ eval_const_expressions_mutator(Node *node,
*/
simple = simplify_function(expr->funcid,
expr->funcresulttype, exprTypmod(node),
+ expr->collid,
&args,
has_named_args, true, context);
if (simple) /* successfully simplified it */
@@ -2190,6 +2196,7 @@ eval_const_expressions_mutator(Node *node,
newexpr->funcretset = expr->funcretset;
newexpr->funcformat = expr->funcformat;
newexpr->args = args;
+ newexpr->collid = expr->collid;
newexpr->location = expr->location;
return (Node *) newexpr;
}
@@ -2221,6 +2228,7 @@ eval_const_expressions_mutator(Node *node,
*/
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
+ expr->collid,
&args,
false, true, context);
if (simple) /* successfully simplified it */
@@ -2250,6 +2258,7 @@ eval_const_expressions_mutator(Node *node,
newexpr->opresulttype = expr->opresulttype;
newexpr->opretset = expr->opretset;
newexpr->args = args;
+ newexpr->collid = expr->collid;
newexpr->location = expr->location;
return (Node *) newexpr;
}
@@ -2314,6 +2323,7 @@ eval_const_expressions_mutator(Node *node,
*/
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
+ expr->collid,
&args,
false, false, context);
if (simple) /* successfully simplified it */
@@ -2342,6 +2352,7 @@ eval_const_expressions_mutator(Node *node,
newexpr->opresulttype = expr->opresulttype;
newexpr->opretset = expr->opretset;
newexpr->args = args;
+ newexpr->collid = expr->collid;
newexpr->location = expr->location;
return (Node *) newexpr;
}
@@ -2493,7 +2504,7 @@ eval_const_expressions_mutator(Node *node,
getTypeInputInfo(expr->resulttype, &infunc, &intypioparam);
simple = simplify_function(outfunc,
- CSTRINGOID, -1,
+ CSTRINGOID, -1, InvalidOid,
&args,
false, true, context);
if (simple) /* successfully simplified output fn */
@@ -2510,8 +2521,11 @@ eval_const_expressions_mutator(Node *node,
Int32GetDatum(-1),
false, true));
+ /* preserve collation of input expression */
simple = simplify_function(infunc,
- expr->resulttype, -1,
+ expr->resulttype,
+ -1,
+ exprCollation((Node *) arg),
&args,
false, true, context);
if (simple) /* successfully simplified input fn */
@@ -2690,6 +2704,7 @@ eval_const_expressions_mutator(Node *node,
/* Otherwise we need a new CASE node */
newcase = makeNode(CaseExpr);
newcase->casetype = caseexpr->casetype;
+ newcase->casecollation = caseexpr->casecollation;
newcase->arg = (Expr *) newarg;
newcase->args = newargs;
newcase->defresult = (Expr *) defresult;
@@ -2782,6 +2797,7 @@ eval_const_expressions_mutator(Node *node,
newcoalesce = makeNode(CoalesceExpr);
newcoalesce->coalescetype = coalesceexpr->coalescetype;
+ newcoalesce->coalescecollation = coalesceexpr->coalescecollation;
newcoalesce->args = newargs;
newcoalesce->location = coalesceexpr->location;
return (Node *) newcoalesce;
@@ -2811,11 +2827,13 @@ eval_const_expressions_mutator(Node *node,
if (rowtype_field_matches(((Var *) arg)->vartype,
fselect->fieldnum,
fselect->resulttype,
- fselect->resulttypmod))
+ fselect->resulttypmod,
+ fselect->resultcollation))
return (Node *) makeVar(((Var *) arg)->varno,
fselect->fieldnum,
fselect->resulttype,
fselect->resulttypmod,
+ fselect->resultcollation,
((Var *) arg)->varlevelsup);
}
if (arg && IsA(arg, RowExpr))
@@ -2831,9 +2849,11 @@ eval_const_expressions_mutator(Node *node,
if (rowtype_field_matches(rowexpr->row_typeid,
fselect->fieldnum,
fselect->resulttype,
- fselect->resulttypmod) &&
+ fselect->resulttypmod,
+ fselect->resultcollation) &&
fselect->resulttype == exprType(fld) &&
- fselect->resulttypmod == exprTypmod(fld))
+ fselect->resulttypmod == exprTypmod(fld) &&
+ fselect->resultcollation == exprCollation(fld))
return fld;
}
}
@@ -2842,6 +2862,7 @@ eval_const_expressions_mutator(Node *node,
newfselect->fieldnum = fselect->fieldnum;
newfselect->resulttype = fselect->resulttype;
newfselect->resulttypmod = fselect->resulttypmod;
+ newfselect->resultcollation = fselect->resultcollation;
return (Node *) newfselect;
}
if (IsA(node, NullTest))
@@ -3299,7 +3320,7 @@ simplify_boolean_equality(Oid opno, List *args)
* pass-by-reference, and it may get modified even if simplification fails.
*/
static Expr *
-simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
+simplify_function(Oid funcid, Oid result_type, int32 result_typmod, Oid collid,
List **args,
bool has_named_args,
bool allow_inline,
@@ -3330,7 +3351,7 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
else if (((Form_pg_proc) GETSTRUCT(func_tuple))->pronargs > list_length(*args))
*args = add_function_defaults(*args, result_type, func_tuple, context);
- newexpr = evaluate_function(funcid, result_type, result_typmod, *args,
+ newexpr = evaluate_function(funcid, result_type, result_typmod, collid, *args,
func_tuple, context);
if (!newexpr && allow_inline)
@@ -3581,7 +3602,8 @@ recheck_cast_function_args(List *args, Oid result_type, HeapTuple func_tuple)
* simplify the function.
*/
static Expr *
-evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, List *args,
+evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, Oid collid,
+ List *args,
HeapTuple func_tuple,
eval_const_expressions_context *context)
{
@@ -3664,6 +3686,7 @@ evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, List *args,
newexpr->funcretset = false;
newexpr->funcformat = COERCE_DONTCARE; /* doesn't matter */
newexpr->args = args;
+ newexpr->collid = collid;
newexpr->location = -1;
return evaluate_expr((Expr *) newexpr, result_type, result_typmod);
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index adbe45caec..1f79ba24fb 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -198,10 +198,12 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
info->indexkeys = (int *) palloc(sizeof(int) * ncolumns);
info->opfamily = (Oid *) palloc(sizeof(Oid) * ncolumns);
info->opcintype = (Oid *) palloc(sizeof(Oid) * ncolumns);
+ info->indexcollations = (Oid *) palloc(sizeof(Oid) * ncolumns);
for (i = 0; i < ncolumns; i++)
{
info->indexkeys[i] = index->indkey.values[i];
+ info->indexcollations[i] = indexRelation->rd_indcollation[i];
info->opfamily[i] = indexRelation->rd_opfamily[i];
info->opcintype[i] = indexRelation->rd_opcintype[i];
}
@@ -634,6 +636,7 @@ get_relation_constraints(PlannerInfo *root,
i,
att->atttypid,
att->atttypmod,
+ att->attcollation,
0);
ntest->nulltesttype = IS_NOT_NULL;
ntest->argisrow = type_is_rowtype(att->atttypid);
@@ -797,6 +800,7 @@ build_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
attrno,
att_tup->atttypid,
att_tup->atttypmod,
+ att_tup->attcollation,
0);
tlist = lappend(tlist,
diff --git a/src/backend/optimizer/util/predtest.c b/src/backend/optimizer/util/predtest.c
index 6e80b4c0c7..4561e8e92f 100644
--- a/src/backend/optimizer/util/predtest.c
+++ b/src/backend/optimizer/util/predtest.c
@@ -912,6 +912,7 @@ arrayconst_startup_fn(Node *clause, PredIterInfo info)
state->constexpr.xpr.type = T_Const;
state->constexpr.consttype = ARR_ELEMTYPE(arrayval);
state->constexpr.consttypmod = -1;
+ state->constexpr.constcollid = arrayconst->constcollid;
state->constexpr.constlen = elmlen;
state->constexpr.constbyval = elmbyval;
lsecond(state->opexpr.args) = &state->constexpr;
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 070e4c177a..22447f92a2 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -1203,6 +1203,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
ListCell *left_tlist,
*lct,
*lcm,
+ *lcc,
*l;
List *targetvars,
*targetnames,
@@ -1296,10 +1297,11 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
targetnames = NIL;
left_tlist = list_head(leftmostQuery->targetList);
- forboth(lct, sostmt->colTypes, lcm, sostmt->colTypmods)
+ forthree(lct, sostmt->colTypes, lcm, sostmt->colTypmods, lcc, sostmt->colCollations)
{
Oid colType = lfirst_oid(lct);
int32 colTypmod = lfirst_int(lcm);
+ Oid colCollation = lfirst_oid(lcc);
TargetEntry *lefttle = (TargetEntry *) lfirst(left_tlist);
char *colName;
TargetEntry *tle;
@@ -1311,6 +1313,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
lefttle->resno,
colType,
colTypmod,
+ colCollation,
0);
var->location = exprLocation((Node *) lefttle->expr);
tle = makeTargetEntry((Expr *) var,
@@ -1418,7 +1421,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
* Recursively transform leaves and internal nodes of a set-op tree
*
* In addition to returning the transformed node, we return a list of
- * expression nodes showing the type, typmod, and location (for error messages)
+ * expression nodes showing the type, typmod, collation, and location (for error messages)
* of each output column of the set-op node. This is used only during the
* internal recursion of this function. At the upper levels we use
* SetToDefault nodes for this purpose, since they carry exactly the fields
@@ -1591,6 +1594,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
*colInfo = NIL;
op->colTypes = NIL;
op->colTypmods = NIL;
+ op->colCollations = NIL;
op->groupClauses = NIL;
forboth(lci, lcolinfo, rci, rcolinfo)
{
@@ -1604,6 +1608,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
SetToDefault *rescolnode;
Oid rescoltype;
int32 rescoltypmod;
+ Oid rescolcoll;
/* select common type, same as CASE et al */
rescoltype = select_common_type(pstate,
@@ -1615,6 +1620,12 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
rescoltypmod = lcoltypmod;
else
rescoltypmod = -1;
+ /* Select common collation. A common collation is
+ * required for all set operators except UNION ALL; see
+ * SQL:2008-2 7.13 SR 15c. */
+ rescolcoll = select_common_collation(pstate,
+ list_make2(lcolnode, rcolnode),
+ (op->op == SETOP_UNION && op->all));
/*
* Verify the coercions are actually possible. If not, we'd fail
@@ -1643,11 +1654,13 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
rescolnode = makeNode(SetToDefault);
rescolnode->typeId = rescoltype;
rescolnode->typeMod = rescoltypmod;
+ rescolnode->collid = rescolcoll;
rescolnode->location = exprLocation(bestexpr);
*colInfo = lappend(*colInfo, rescolnode);
op->colTypes = lappend_oid(op->colTypes, rescoltype);
op->colTypmods = lappend_int(op->colTypmods, rescoltypmod);
+ op->colCollations = lappend_oid(op->colCollations, rescolcoll);
/*
* For all cases except UNION ALL, identify the grouping operators
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index ced78734bb..a1bcf02f5b 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -261,6 +261,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
%type <list> func_name handler_name qual_Op qual_all_Op subquery_Op
opt_class opt_inline_handler opt_validator validator_clause
+ opt_collate
%type <range> qualified_name OptConstrFromTable
@@ -394,7 +395,8 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
%type <list> copy_generic_opt_list copy_generic_opt_arg_list
%type <list> copy_options
-%type <typnam> Typename SimpleTypename ConstTypename
+%type <typnam> Typename SimpleTypename SimpleTypenameWithoutCollation
+ ConstTypename
GenericType Numeric opt_float
Character ConstCharacter
CharacterWithLength CharacterWithoutLength
@@ -5323,38 +5325,45 @@ index_params: index_elem { $$ = list_make1($1); }
* expressions in parens. For backwards-compatibility reasons, we allow
* an expression that's just a function call to be written without parens.
*/
-index_elem: ColId opt_class opt_asc_desc opt_nulls_order
+index_elem: ColId opt_collate opt_class opt_asc_desc opt_nulls_order
{
$$ = makeNode(IndexElem);
$$->name = $1;
$$->expr = NULL;
$$->indexcolname = NULL;
- $$->opclass = $2;
- $$->ordering = $3;
- $$->nulls_ordering = $4;
+ $$->collation = $2;
+ $$->opclass = $3;
+ $$->ordering = $4;
+ $$->nulls_ordering = $5;
}
- | func_expr opt_class opt_asc_desc opt_nulls_order
+ | func_expr opt_collate opt_class opt_asc_desc opt_nulls_order
{
$$ = makeNode(IndexElem);
$$->name = NULL;
$$->expr = $1;
$$->indexcolname = NULL;
- $$->opclass = $2;
- $$->ordering = $3;
- $$->nulls_ordering = $4;
+ $$->collation = $2;
+ $$->opclass = $3;
+ $$->ordering = $4;
+ $$->nulls_ordering = $5;
}
- | '(' a_expr ')' opt_class opt_asc_desc opt_nulls_order
+ | '(' a_expr ')' opt_collate opt_class opt_asc_desc opt_nulls_order
{
$$ = makeNode(IndexElem);
$$->name = NULL;
$$->expr = $2;
$$->indexcolname = NULL;
- $$->opclass = $4;
- $$->ordering = $5;
- $$->nulls_ordering = $6;
+ $$->collation = $4;
+ $$->opclass = $5;
+ $$->ordering = $6;
+ $$->nulls_ordering = $7;
}
;
+opt_collate: COLLATE any_name { $$ = $2; }
+ | /*EMPTY*/ { $$ = NIL; }
+ ;
+
opt_class: any_name { $$ = $1; }
| USING any_name { $$ = $2; }
| /*EMPTY*/ { $$ = NIL; }
@@ -8776,6 +8785,13 @@ opt_array_bounds:
;
SimpleTypename:
+ SimpleTypenameWithoutCollation opt_collate
+ {
+ $$ = $1;
+ $$->collnames = $2;
+ }
+
+SimpleTypenameWithoutCollation:
GenericType { $$ = $1; }
| Numeric { $$ = $1; }
| Bit { $$ = $1; }
@@ -9811,6 +9827,14 @@ c_expr: columnref { $$ = $1; }
r->location = @1;
$$ = (Node *)r;
}
+ | c_expr COLLATE any_name
+ {
+ CollateClause *n = makeNode(CollateClause);
+ n->arg = (Expr *) $1;
+ n->collnames = $3;
+ n->location = @2;
+ $$ = (Node *)n;
+ }
;
/*
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index e43bc54b3f..8267627c42 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -723,6 +723,7 @@ build_aggregate_fnexprs(Oid *agg_input_types,
Oid agg_result_type,
Oid transfn_oid,
Oid finalfn_oid,
+ Oid collation,
Expr **transfnexpr,
Expr **finalfnexpr)
{
@@ -741,6 +742,7 @@ build_aggregate_fnexprs(Oid *agg_input_types,
argp->paramid = -1;
argp->paramtype = agg_state_type;
argp->paramtypmod = -1;
+ argp->paramcollation = collation;
argp->location = -1;
args = list_make1(argp);
@@ -752,6 +754,7 @@ build_aggregate_fnexprs(Oid *agg_input_types,
argp->paramid = -1;
argp->paramtype = agg_input_types[i];
argp->paramtypmod = -1;
+ argp->paramcollation = collation;
argp->location = -1;
args = lappend(args, argp);
}
@@ -759,6 +762,7 @@ build_aggregate_fnexprs(Oid *agg_input_types,
*transfnexpr = (Expr *) makeFuncExpr(transfn_oid,
agg_state_type,
args,
+ collation,
COERCE_DONTCARE);
/* see if we have a final function */
@@ -776,11 +780,13 @@ build_aggregate_fnexprs(Oid *agg_input_types,
argp->paramid = -1;
argp->paramtype = agg_state_type;
argp->paramtypmod = -1;
+ argp->paramcollation = collation;
argp->location = -1;
args = list_make1(argp);
*finalfnexpr = (Expr *) makeFuncExpr(finalfn_oid,
agg_result_type,
args,
+ collation,
COERCE_DONTCARE);
}
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index f9560d07b9..d250e0c859 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -613,7 +613,8 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
tupdesc = BuildDescFromLists(rte->eref->colnames,
rte->funccoltypes,
- rte->funccoltypmods);
+ rte->funccoltypmods,
+ rte->funccolcollations);
CheckAttributeNamesTypes(tupdesc, RELKIND_COMPOSITE_TYPE, false);
}
@@ -1935,6 +1936,7 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle,
bool resolveUnknown)
{
Oid restype = exprType((Node *) tle->expr);
+ Oid rescollation = exprCollation((Node *) tle->expr);
Oid sortop;
Oid eqop;
bool hashable;
@@ -2018,6 +2020,12 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle,
break;
}
+ if (type_is_collatable(restype) && !OidIsValid(rescollation))
+ ereport(ERROR,
+ (errcode(ERRCODE_INDETERMINATE_COLLATION),
+ errmsg("no collation was derived for the sort expression"),
+ errhint("Use the COLLATE clause to set the collation explicitly.")));
+
cancel_parser_errposition_callback(&pcbstate);
/* avoid making duplicate sortlist entries */
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 5b0dc1420d..2fd808d26b 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -16,6 +16,7 @@
#include "catalog/pg_cast.h"
#include "catalog/pg_class.h"
+#include "catalog/pg_collation.h"
#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
@@ -216,6 +217,7 @@ coerce_type(ParseState *pstate, Node *node,
newcon->consttype = baseTypeId;
newcon->consttypmod = inputTypeMod;
+ newcon->constcollid = get_typcollation(newcon->consttype);
newcon->constlen = typeLen(targetType);
newcon->constbyval = typeByVal(targetType);
newcon->constisnull = con->constisnull;
@@ -277,6 +279,14 @@ coerce_type(ParseState *pstate, Node *node,
if (result)
return result;
}
+ if (IsA(node, CollateClause))
+ {
+ CollateClause *cc = (CollateClause *) node;
+
+ cc->arg = (Expr *) coerce_type(pstate, (Node *) cc->arg, inputTypeId, targetTypeId, targetTypeMod,
+ ccontext, cformat, location);
+ return (Node *) cc;
+ }
pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext,
&funcId);
if (pathtype != COERCION_PATH_NONE)
@@ -718,6 +728,7 @@ build_coercion_expression(Node *node,
FuncExpr *fexpr;
List *args;
Const *cons;
+ Oid collation;
Assert(OidIsValid(funcId));
@@ -749,7 +760,9 @@ build_coercion_expression(Node *node,
args = lappend(args, cons);
}
- fexpr = makeFuncExpr(funcId, targetTypeId, args, cformat);
+ collation = coercion_expression_result_collation(targetTypeId, node);
+
+ fexpr = makeFuncExpr(funcId, targetTypeId, args, collation, cformat);
fexpr->location = location;
return (Node *) fexpr;
}
@@ -2081,3 +2094,120 @@ typeIsOfTypedTable(Oid reltypeId, Oid reloftypeId)
return result;
}
+
+
+/*
+ * select_common_collation() -- determine one collation to apply for
+ * an expression node, for evaluating the expression itself or to
+ * label the result of the expression node.
+ *
+ * none_ok means that it is permitted to return "no" collation. It is
+ * then not possible to sort the result value of whatever expression
+ * is applying this. none_ok = true reflects the rules of SQL
+ * standard clause "Result of data type combinations", none_ok = false
+ * reflects the rules of clause "Collation determination" (in some
+ * cases invoked via "Grouping operations").
+ */
+Oid
+select_common_collation(ParseState *pstate, List *exprs, bool none_ok)
+{
+ ListCell *lc;
+
+ /*
+ * Check if there are any explicit collation derivations. If so,
+ * they must all be the same.
+ */
+ foreach(lc, exprs)
+ {
+ Node *pexpr = (Node *) lfirst(lc);
+ Oid pcoll = exprCollation(pexpr);
+ bool pexplicit = IsA(pexpr, CollateClause);
+
+ if (pcoll && pexplicit)
+ {
+ ListCell *lc2;
+ for_each_cell(lc2, lnext(lc))
+ {
+ Node *nexpr = (Node *) lfirst(lc2);
+ Oid ncoll = exprCollation(nexpr);
+ bool nexplicit = IsA(nexpr, CollateClause);
+
+ if (!ncoll || !nexplicit)
+ continue;
+
+ if (ncoll != pcoll)
+ ereport(ERROR,
+ (errcode(ERRCODE_COLLATION_MISMATCH),
+ errmsg("collation mismatch between explicit collations \"%s\" and \"%s\"",
+ get_collation_name(pcoll),
+ get_collation_name(ncoll)),
+ parser_errposition(pstate, exprLocation(nexpr))));
+ }
+
+ return pcoll;
+ }
+ }
+
+ /*
+ * Check if there are any implicit collation derivations.
+ */
+ foreach(lc, exprs)
+ {
+ Node *pexpr = (Node *) lfirst(lc);
+ Oid pcoll = exprCollation(pexpr);
+
+ if (pcoll && pcoll != DEFAULT_COLLATION_OID)
+ {
+ ListCell *lc2;
+ for_each_cell(lc2, lnext(lc))
+ {
+ Node *nexpr = (Node *) lfirst(lc2);
+ Oid ncoll = exprCollation(nexpr);
+
+ if (!ncoll || ncoll == DEFAULT_COLLATION_OID)
+ continue;
+
+ if (ncoll != pcoll)
+ {
+ if (none_ok)
+ return InvalidOid;
+ ereport(ERROR,
+ (errcode(ERRCODE_COLLATION_MISMATCH),
+ errmsg("collation mismatch between implicit collations \"%s\" and \"%s\"",
+ get_collation_name(pcoll),
+ get_collation_name(ncoll)),
+ errhint("You can override the collation by applying the COLLATE clause to one or both expressions."),
+ parser_errposition(pstate, exprLocation(nexpr))));
+ }
+ }
+
+ return pcoll;
+ }
+ }
+
+ foreach(lc, exprs)
+ {
+ Node *pexpr = (Node *) lfirst(lc);
+ Oid pcoll = exprCollation(pexpr);
+
+ if (pcoll == DEFAULT_COLLATION_OID)
+ {
+ ListCell *lc2;
+ for_each_cell(lc2, lnext(lc))
+ {
+ Node *nexpr = (Node *) lfirst(lc2);
+ Oid ncoll = exprCollation(nexpr);
+
+ if (ncoll != pcoll)
+ break;
+ }
+
+ return pcoll;
+ }
+ }
+
+ /*
+ * Else use default
+ */
+ return InvalidOid;
+}
diff --git a/src/backend/parser/parse_cte.c b/src/backend/parser/parse_cte.c
index 24ba008f9e..4d3d33eb07 100644
--- a/src/backend/parser/parse_cte.c
+++ b/src/backend/parser/parse_cte.c
@@ -14,11 +14,13 @@
*/
#include "postgres.h"
+#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
#include "nodes/nodeFuncs.h"
#include "parser/analyze.h"
#include "parser/parse_cte.h"
#include "utils/builtins.h"
+#include "utils/lsyscache.h"
/* Enumeration of contexts in which a self-reference is disallowed */
@@ -263,11 +265,13 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
*/
ListCell *lctlist,
*lctyp,
- *lctypmod;
+ *lctypmod,
+ *lccoll;
int varattno;
lctyp = list_head(cte->ctecoltypes);
lctypmod = list_head(cte->ctecoltypmods);
+ lccoll = list_head(cte->ctecolcollations);
varattno = 0;
foreach(lctlist, query->targetList)
{
@@ -278,7 +282,7 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
continue;
varattno++;
Assert(varattno == te->resno);
- if (lctyp == NULL || lctypmod == NULL) /* shouldn't happen */
+ if (lctyp == NULL || lctypmod == NULL || lccoll == NULL) /* shouldn't happen */
elog(ERROR, "wrong number of output columns in WITH");
texpr = (Node *) te->expr;
if (exprType(texpr) != lfirst_oid(lctyp) ||
@@ -293,10 +297,20 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
exprTypmod(texpr))),
errhint("Cast the output of the non-recursive term to the correct type."),
parser_errposition(pstate, exprLocation(texpr))));
+ if (exprCollation(texpr) != lfirst_oid(lccoll))
+ ereport(ERROR,
+ (errcode(ERRCODE_COLLATION_MISMATCH),
+ errmsg("recursive query \"%s\" column %d has collation \"%s\" in non-recursive term but collation \"%s\" overall",
+ cte->ctename, varattno,
+ get_collation_name(lfirst_oid(lccoll)),
+ get_collation_name(exprCollation(texpr))),
+ errhint("Use the COLLATE clause to set the collation of the non-recursive term."),
+ parser_errposition(pstate, exprLocation(texpr))));
lctyp = lnext(lctyp);
lctypmod = lnext(lctypmod);
+ lccoll = lnext(lccoll);
}
- if (lctyp != NULL || lctypmod != NULL) /* shouldn't happen */
+ if (lctyp != NULL || lctypmod != NULL || lccoll != NULL) /* shouldn't happen */
elog(ERROR, "wrong number of output columns in WITH");
}
}
@@ -331,7 +345,7 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist)
* handling.)
*/
cte->ctecolnames = copyObject(cte->aliascolnames);
- cte->ctecoltypes = cte->ctecoltypmods = NIL;
+ cte->ctecoltypes = cte->ctecoltypmods = cte->ctecolcollations = NIL;
numaliases = list_length(cte->aliascolnames);
varattno = 0;
foreach(tlistitem, tlist)
@@ -339,6 +353,7 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist)
TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
Oid coltype;
int32 coltypmod;
+ Oid colcoll;
if (te->resjunk)
continue;
@@ -353,6 +368,7 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist)
}
coltype = exprType((Node *) te->expr);
coltypmod = exprTypmod((Node *) te->expr);
+ colcoll = exprCollation((Node *) te->expr);
/*
* If the CTE is recursive, force the exposed column type of any
@@ -366,9 +382,11 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist)
{
coltype = TEXTOID;
coltypmod = -1; /* should be -1 already, but be sure */
+ colcoll = DEFAULT_COLLATION_OID;
}
cte->ctecoltypes = lappend_oid(cte->ctecoltypes, coltype);
cte->ctecoltypmods = lappend_int(cte->ctecoltypmods, coltypmod);
+ cte->ctecolcollations = lappend_oid(cte->ctecolcollations, colcoll);
}
if (varattno < numaliases)
ereport(ERROR,
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 129b39cb26..ae56532592 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -65,6 +65,7 @@ static Node *transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte,
static Node *transformIndirection(ParseState *pstate, Node *basenode,
List *indirection);
static Node *transformTypeCast(ParseState *pstate, TypeCast *tc);
+static Node *transformCollateClause(ParseState *pstate, CollateClause *c);
static Node *make_row_comparison_op(ParseState *pstate, List *opname,
List *largs, List *rargs, int location);
static Node *make_row_distinct_op(ParseState *pstate, List *opname,
@@ -146,6 +147,12 @@ transformExpr(ParseState *pstate, Node *expr)
{
TypeCast *tc = (TypeCast *) expr;
+ if (tc->typeName->collnames)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("COLLATE clause not allowed in cast target"),
+ parser_errposition(pstate, tc->typeName->location)));
+
/*
* If the subject of the typecast is an ARRAY[] construct and
* the target type is an array type, we invoke
@@ -185,6 +192,10 @@ transformExpr(ParseState *pstate, Node *expr)
break;
}
+ case T_CollateClause:
+ result = transformCollateClause(pstate, (CollateClause *) expr);
+ break;
+
case T_A_Expr:
{
A_Expr *a = (A_Expr *) expr;
@@ -423,6 +434,7 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
exprType(result),
InvalidOid,
exprTypmod(result),
+ exprCollation(result),
subscripts,
NULL);
subscripts = NIL;
@@ -444,6 +456,7 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
exprType(result),
InvalidOid,
exprTypmod(result),
+ exprCollation(result),
subscripts,
NULL);
@@ -1267,6 +1280,7 @@ transformCaseExpr(ParseState *pstate, CaseExpr *c)
placeholder = makeNode(CaseTestExpr);
placeholder->typeId = exprType(arg);
placeholder->typeMod = exprTypmod(arg);
+ placeholder->collation = exprCollation(arg);
}
else
placeholder = NULL;
@@ -1351,6 +1365,8 @@ transformCaseExpr(ParseState *pstate, CaseExpr *c)
"CASE/WHEN");
}
+ newc->casecollation = select_common_collation(pstate, resultexprs, true);
+
newc->location = c->location;
return (Node *) newc;
@@ -1461,6 +1477,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
param->paramid = tent->resno;
param->paramtype = exprType((Node *) tent->expr);
param->paramtypmod = exprTypmod((Node *) tent->expr);
+ param->paramcollation = exprCollation((Node *) tent->expr);
param->location = -1;
right_list = lappend(right_list, param);
@@ -1704,6 +1721,7 @@ transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c)
}
newc->args = newcoercedargs;
+ newc->coalescecollation = select_common_collation(pstate, newcoercedargs, true);
newc->location = c->location;
return (Node *) newc;
}
@@ -1728,6 +1746,7 @@ transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m)
}
newm->minmaxtype = select_common_type(pstate, newargs, funcname, NULL);
+ newm->collid = select_common_collation(pstate, newargs, false);
/* Convert arguments if necessary */
foreach(args, newargs)
@@ -2083,6 +2102,36 @@ transformTypeCast(ParseState *pstate, TypeCast *tc)
}
/*
+ * Handle an explicit COLLATE clause.
+ *
+ * Transform the argument, and look up the collation name.
+ */
+static Node *
+transformCollateClause(ParseState *pstate, CollateClause *c)
+{
+ CollateClause *newc;
+ Oid argtype;
+
+ newc = makeNode(CollateClause);
+ newc->arg = (Expr *) transformExpr(pstate, (Node *) c->arg);
+
+ argtype = exprType((Node *) newc->arg);
+ /* The unknown type is not collatable, but coerce_type() takes
+ * care of it separately, so we'll let it go here. */
+ if (!type_is_collatable(argtype) && argtype != UNKNOWNOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("collations are not supported by type %s",
+ format_type_be(argtype))));
+
+ newc->collOid = LookupCollation(pstate, c->collnames, c->location);
+ newc->collnames = c->collnames;
+ newc->location = c->location;
+
+ return (Node *) newc;
+}
+
+/*
* Transform a "row compare-op row" construct
*
* The inputs are lists of already-transformed expressions.
@@ -2103,6 +2152,7 @@ make_row_comparison_op(ParseState *pstate, List *opname,
List *opexprs;
List *opnos;
List *opfamilies;
+ List *collids;
ListCell *l,
*r;
List **opfamily_lists;
@@ -2273,6 +2323,7 @@ make_row_comparison_op(ParseState *pstate, List *opname,
* possibility that make_op inserted coercion operations.
*/
opnos = NIL;
+ collids = NIL;
largs = NIL;
rargs = NIL;
foreach(l, opexprs)
@@ -2280,6 +2331,7 @@ make_row_comparison_op(ParseState *pstate, List *opname,
OpExpr *cmp = (OpExpr *) lfirst(l);
opnos = lappend_oid(opnos, cmp->opno);
+ collids = lappend_oid(collids, cmp->collid);
largs = lappend(largs, linitial(cmp->args));
rargs = lappend(rargs, lsecond(cmp->args));
}
@@ -2288,6 +2340,7 @@ make_row_comparison_op(ParseState *pstate, List *opname,
rcexpr->rctype = rctype;
rcexpr->opnos = opnos;
rcexpr->opfamilies = opfamilies;
+ rcexpr->collids = collids;
rcexpr->largs = largs;
rcexpr->rargs = rargs;
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index f1a4f9b959..0af9cbd92b 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -78,6 +78,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
bool retset;
int nvargs;
FuncDetailCode fdresult;
+ Oid funccollid;
/*
* Most of the rest of the parser just assumes that functions do not have
@@ -343,6 +344,12 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
/* perform the necessary typecasting of arguments */
make_fn_arguments(pstate, fargs, actual_arg_types, declared_arg_types);
+ /* XXX: If we knew which functions required collation information,
+ * we could selectively set the last argument to true here. */
+ funccollid = select_common_collation(pstate, fargs, false);
+ if (!OidIsValid(funccollid))
+ funccollid = get_typcollation(rettype);
+
/*
* If it's a variadic function call, transform the last nvargs arguments
* into an array --- unless it's an "any" variadic.
@@ -383,6 +390,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
funcexpr->funcretset = retset;
funcexpr->funcformat = COERCE_EXPLICIT_CALL;
funcexpr->args = fargs;
+ funcexpr->collid = funccollid;
funcexpr->location = location;
retval = (Node *) funcexpr;
@@ -396,6 +404,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
aggref->aggtype = rettype;
/* args, aggorder, aggdistinct will be set by transformAggregateCall */
aggref->aggstar = agg_star;
+ aggref->collid = funccollid;
/* agglevelsup will be set by transformAggregateCall */
aggref->location = location;
@@ -453,6 +462,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
/* winref will be set by transformWindowFuncCall */
wfunc->winstar = agg_star;
wfunc->winagg = (fdresult == FUNCDETAIL_AGGREGATE);
+ wfunc->collid = funccollid;
wfunc->location = location;
/*
@@ -1303,7 +1313,7 @@ FuncNameAsType(List *funcname)
Oid result;
Type typtup;
- typtup = LookupTypeName(NULL, makeTypeNameFromNameList(funcname), NULL);
+ typtup = LookupTypeName(NULL, makeTypeNameFromNameList(funcname), NULL, NULL);
if (typtup == NULL)
return InvalidOid;
@@ -1380,6 +1390,7 @@ ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg,
fselect->fieldnum = i + 1;
fselect->resulttype = att->atttypid;
fselect->resulttypmod = att->atttypmod;
+ fselect->resultcollation = att->attcollation;
return (Node *) fselect;
}
}
@@ -1489,7 +1500,7 @@ LookupTypeNameOid(const TypeName *typename)
Oid result;
Type typtup;
- typtup = LookupTypeName(NULL, typename, NULL);
+ typtup = LookupTypeName(NULL, typename, NULL, NULL);
if (typtup == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index aed404eb1d..163fc89179 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -189,10 +189,11 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno, int location)
sublevels_up;
Oid vartypeid;
int32 type_mod;
+ Oid varcollid;
vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
- get_rte_attribute_type(rte, attrno, &vartypeid, &type_mod);
- result = makeVar(vnum, attrno, vartypeid, type_mod, sublevels_up);
+ get_rte_attribute_type(rte, attrno, &vartypeid, &type_mod, &varcollid);
+ result = makeVar(vnum, attrno, vartypeid, type_mod, varcollid, sublevels_up);
result->location = location;
return result;
}
@@ -269,6 +270,7 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod)
* elementType OID of array's element type (fetch with transformArrayType,
* or pass InvalidOid to do it here)
* arrayTypMod typmod for the array (which is also typmod for the elements)
+ * arrayColl OID of collation of array and array's elements
* indirection Untransformed list of subscripts (must not be NIL)
* assignFrom NULL for array fetch, else transformed expression for source.
*/
@@ -278,6 +280,7 @@ transformArraySubscripts(ParseState *pstate,
Oid arrayType,
Oid elementType,
int32 arrayTypMod,
+ Oid arrayColl,
List *indirection,
Node *assignFrom)
{
@@ -404,6 +407,7 @@ transformArraySubscripts(ParseState *pstate,
aref->refarraytype = arrayType;
aref->refelemtype = elementType;
aref->reftypmod = arrayTypMod;
+ aref->refcollid = arrayColl;
aref->refupperindexpr = upperIndexpr;
aref->reflowerindexpr = lowerIndexpr;
aref->refexpr = (Expr *) arrayBase;
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c
index 1f50bdcc34..cad41d46f0 100644
--- a/src/backend/parser/parse_oper.c
+++ b/src/backend/parser/parse_oper.c
@@ -782,6 +782,7 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
List *args;
Oid rettype;
OpExpr *result;
+ Oid opcollid;
/* Select the operator */
if (rtree == NULL)
@@ -861,6 +862,12 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
/* perform the necessary typecasting of arguments */
make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
+ /* XXX: If we knew which functions required collation information,
+ * we could selectively set the last argument to true here. */
+ opcollid = select_common_collation(pstate, args, false);
+ if (!OidIsValid(opcollid))
+ opcollid = get_typcollation(rettype);
+
/* and build the expression node */
result = makeNode(OpExpr);
result->opno = oprid(tup);
@@ -868,6 +875,7 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
result->opresulttype = rettype;
result->opretset = get_func_retset(opform->oprcode);
result->args = args;
+ result->collid = opcollid;
result->location = location;
ReleaseSysCache(tup);
@@ -896,6 +904,7 @@ make_scalar_array_op(ParseState *pstate, List *opname,
List *args;
Oid rettype;
ScalarArrayOpExpr *result;
+ Oid opcollid;
ltypeId = exprType(ltree);
atypeId = exprType(rtree);
@@ -990,12 +999,19 @@ make_scalar_array_op(ParseState *pstate, List *opname,
/* perform the necessary typecasting of arguments */
make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
+ /* XXX: If we knew which functions required collation information,
+ * we could selectively set the last argument to true here. */
+ opcollid = select_common_collation(pstate, args, false);
+ if (!OidIsValid(opcollid))
+ opcollid = get_typcollation(rettype);
+
/* and build the expression node */
result = makeNode(ScalarArrayOpExpr);
result->opno = oprid(tup);
result->opfuncid = opform->oprcode;
result->useOr = useOr;
result->args = args;
+ result->collid = opcollid;
result->location = location;
ReleaseSysCache(tup);
diff --git a/src/backend/parser/parse_param.c b/src/backend/parser/parse_param.c
index c9987d2723..9e9f2e3ca0 100644
--- a/src/backend/parser/parse_param.c
+++ b/src/backend/parser/parse_param.c
@@ -30,6 +30,7 @@
#include "nodes/nodeFuncs.h"
#include "parser/parse_param.h"
#include "utils/builtins.h"
+#include "utils/lsyscache.h"
typedef struct FixedParamState
@@ -113,6 +114,7 @@ fixed_paramref_hook(ParseState *pstate, ParamRef *pref)
param->paramid = paramno;
param->paramtype = parstate->paramTypes[paramno - 1];
param->paramtypmod = -1;
+ param->paramcollation = get_typcollation(param->paramtype);
param->location = pref->location;
return (Node *) param;
@@ -165,6 +167,7 @@ variable_paramref_hook(ParseState *pstate, ParamRef *pref)
param->paramid = paramno;
param->paramtype = *pptype;
param->paramtypmod = -1;
+ param->paramcollation = get_typcollation(param->paramtype);
param->location = pref->location;
return (Node *) param;
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 331ac670ff..497c726f31 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -1098,6 +1098,7 @@ addRangeTableEntryForFunction(ParseState *pstate,
rte->funcexpr = funcexpr;
rte->funccoltypes = NIL;
rte->funccoltypmods = NIL;
+ rte->funccolcollations = NIL;
rte->alias = alias;
eref = makeAlias(alias ? alias->aliasname : funcname, NIL);
@@ -1157,6 +1158,7 @@ addRangeTableEntryForFunction(ParseState *pstate,
char *attrname;
Oid attrtype;
int32 attrtypmod;
+ Oid attrcollation;
attrname = pstrdup(n->colname);
if (n->typeName->setof)
@@ -1165,10 +1167,11 @@ addRangeTableEntryForFunction(ParseState *pstate,
errmsg("column \"%s\" cannot be declared SETOF",
attrname),
parser_errposition(pstate, n->typeName->location)));
- typenameTypeIdAndMod(pstate, n->typeName, &attrtype, &attrtypmod);
+ typenameTypeIdModColl(pstate, n->typeName, &attrtype, &attrtypmod, &attrcollation);
eref->colnames = lappend(eref->colnames, makeString(attrname));
rte->funccoltypes = lappend_oid(rte->funccoltypes, attrtype);
rte->funccoltypmods = lappend_int(rte->funccoltypmods, attrtypmod);
+ rte->funccolcollations = lappend_oid(rte->funccolcollations, attrcollation);
}
}
else
@@ -1381,6 +1384,7 @@ addRangeTableEntryForCTE(ParseState *pstate,
rte->ctecoltypes = cte->ctecoltypes;
rte->ctecoltypmods = cte->ctecoltypmods;
+ rte->ctecolcollations = cte->ctecolcollations;
rte->alias = alias;
if (alias)
@@ -1573,6 +1577,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
varnode = makeVar(rtindex, varattno,
exprType((Node *) te->expr),
exprTypmod((Node *) te->expr),
+ exprCollation((Node *) te->expr),
sublevels_up);
varnode->location = location;
@@ -1612,6 +1617,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
varnode = makeVar(rtindex, 1,
funcrettype, -1,
+ exprCollation(rte->funcexpr),
sublevels_up);
varnode->location = location;
@@ -1626,12 +1632,14 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
{
ListCell *l1;
ListCell *l2;
+ ListCell *l3;
int attnum = 0;
- forboth(l1, rte->funccoltypes, l2, rte->funccoltypmods)
+ forthree(l1, rte->funccoltypes, l2, rte->funccoltypmods, l3, rte->funccolcollations)
{
Oid attrtype = lfirst_oid(l1);
int32 attrtypmod = lfirst_int(l2);
+ Oid attrcollation = lfirst_oid(l3);
Var *varnode;
attnum++;
@@ -1639,6 +1647,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
attnum,
attrtype,
attrtypmod,
+ attrcollation,
sublevels_up);
varnode->location = location;
*colvars = lappend(*colvars, varnode);
@@ -1681,6 +1690,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
varnode = makeVar(rtindex, varattno,
exprType(col),
exprTypmod(col),
+ exprCollation(col),
sublevels_up);
varnode->location = location;
*colvars = lappend(*colvars, varnode);
@@ -1740,6 +1750,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
varnode = makeVar(rtindex, varattno,
exprType(avar),
exprTypmod(avar),
+ exprCollation(avar),
sublevels_up);
varnode->location = location;
@@ -1753,12 +1764,14 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
ListCell *aliasp_item = list_head(rte->eref->colnames);
ListCell *lct;
ListCell *lcm;
+ ListCell *lcc;
varattno = 0;
- forboth(lct, rte->ctecoltypes, lcm, rte->ctecoltypmods)
+ forthree(lct, rte->ctecoltypes, lcm, rte->ctecoltypmods, lcc, rte->ctecolcollations)
{
Oid coltype = lfirst_oid(lct);
int32 coltypmod = lfirst_int(lcm);
+ Oid colcoll = lfirst_oid(lcc);
varattno++;
@@ -1776,7 +1789,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
Var *varnode;
varnode = makeVar(rtindex, varattno,
- coltype, coltypmod,
+ coltype, coltypmod, colcoll,
sublevels_up);
*colvars = lappend(*colvars, varnode);
}
@@ -1857,7 +1870,7 @@ expandTupleDesc(TupleDesc tupdesc, Alias *eref,
Var *varnode;
varnode = makeVar(rtindex, attr->attnum,
- attr->atttypid, attr->atttypmod,
+ attr->atttypid, attr->atttypmod, attr->attcollation,
sublevels_up);
varnode->location = location;
@@ -1968,7 +1981,7 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
*/
void
get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
- Oid *vartype, int32 *vartypmod)
+ Oid *vartype, int32 *vartypmod, Oid *varcollid)
{
switch (rte->rtekind)
{
@@ -1998,6 +2011,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
get_rel_name(rte->relid))));
*vartype = att_tup->atttypid;
*vartypmod = att_tup->atttypmod;
+ *varcollid = att_tup->attcollation;
ReleaseSysCache(tp);
}
break;
@@ -2012,6 +2026,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
rte->eref->aliasname, attnum);
*vartype = exprType((Node *) te->expr);
*vartypmod = exprTypmod((Node *) te->expr);
+ *varcollid = exprCollation((Node *) te->expr);
}
break;
case RTE_FUNCTION:
@@ -2053,17 +2068,20 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
rte->eref->aliasname)));
*vartype = att_tup->atttypid;
*vartypmod = att_tup->atttypmod;
+ *varcollid = att_tup->attcollation;
}
else if (functypclass == TYPEFUNC_SCALAR)
{
/* Base data type, i.e. scalar */
*vartype = funcrettype;
*vartypmod = -1;
+ *varcollid = exprCollation(rte->funcexpr);
}
else if (functypclass == TYPEFUNC_RECORD)
{
*vartype = list_nth_oid(rte->funccoltypes, attnum - 1);
*vartypmod = list_nth_int(rte->funccoltypmods, attnum - 1);
+ *varcollid = list_nth_oid(rte->funccolcollations, attnum - 1);
}
else
{
@@ -2084,6 +2102,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
col = (Node *) list_nth(collist, attnum - 1);
*vartype = exprType(col);
*vartypmod = exprTypmod(col);
+ *varcollid = exprCollation(col);
}
break;
case RTE_JOIN:
@@ -2097,6 +2116,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
aliasvar = (Node *) list_nth(rte->joinaliasvars, attnum - 1);
*vartype = exprType(aliasvar);
*vartypmod = exprTypmod(aliasvar);
+ *varcollid = exprCollation(aliasvar);
}
break;
case RTE_CTE:
@@ -2105,6 +2125,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
Assert(attnum > 0 && attnum <= list_length(rte->ctecoltypes));
*vartype = list_nth_oid(rte->ctecoltypes, attnum - 1);
*vartypmod = list_nth_int(rte->ctecoltypmods, attnum - 1);
+ *varcollid = list_nth_oid(rte->ctecolcollations, attnum - 1);
}
break;
default:
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 7d77e0b63f..a0761da875 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -374,6 +374,7 @@ transformAssignedExpr(ParseState *pstate,
Oid type_id; /* type of value provided */
Oid attrtype; /* type of target column */
int32 attrtypmod;
+ Oid attrcollation;
Relation rd = pstate->p_target_relation;
Assert(rd != NULL);
@@ -385,6 +386,7 @@ transformAssignedExpr(ParseState *pstate,
parser_errposition(pstate, location)));
attrtype = attnumTypeId(rd, attrno);
attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod;
+ attrcollation = rd->rd_att->attrs[attrno - 1]->attcollation;
/*
* If the expression is a DEFAULT placeholder, insert the attribute's
@@ -400,6 +402,7 @@ transformAssignedExpr(ParseState *pstate,
def->typeId = attrtype;
def->typeMod = attrtypmod;
+ def->collid = attrcollation;
if (indirection)
{
if (IsA(linitial(indirection), A_Indices))
@@ -786,6 +789,7 @@ transformAssignmentSubscripts(ParseState *pstate,
arrayType,
elementTypeId,
arrayTypMod,
+ InvalidOid,
subscripts,
rhs);
@@ -1267,6 +1271,7 @@ ExpandRowReference(ParseState *pstate, Node *expr,
fselect->fieldnum = i + 1;
fselect->resulttype = att->atttypid;
fselect->resulttypmod = att->atttypmod;
+ fselect->resultcollation = att->attcollation;
if (targetlist)
{
@@ -1338,6 +1343,8 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup)
exprType(varnode),
exprTypmod(varnode),
0);
+ TupleDescInitEntryCollation(tupleDesc, i,
+ exprCollation(varnode));
i++;
}
Assert(lname == NULL && lvar == NULL); /* lists same length? */
@@ -1583,6 +1590,8 @@ FigureColnameInternal(Node *node, char **name)
}
}
break;
+ case T_CollateClause:
+ return FigureColnameInternal((Node *) ((CollateClause *) node)->arg, name);
case T_CaseExpr:
strength = FigureColnameInternal((Node *) ((CaseExpr *) node)->defresult,
name);
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index 0b601c8b75..02c1c68827 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -29,6 +29,8 @@
static int32 typenameTypeMod(ParseState *pstate, const TypeName *typeName,
Type typ);
+static Oid typenameCollation(ParseState *pstate, const TypeName *typeName,
+ Type typ);
/*
@@ -36,7 +38,8 @@ static int32 typenameTypeMod(ParseState *pstate, const TypeName *typeName,
* Given a TypeName object, lookup the pg_type syscache entry of the type.
* Returns NULL if no such type can be found. If the type is found,
* the typmod value represented in the TypeName struct is computed and
- * stored into *typmod_p.
+ * stored into *typmod_p, and the collation is looked up and stored into
+ * *colloid_p.
*
* NB: on success, the caller must ReleaseSysCache the type tuple when done
* with it.
@@ -51,15 +54,18 @@ static int32 typenameTypeMod(ParseState *pstate, const TypeName *typeName,
* found but is a shell, and there is typmod decoration, an error will be
* thrown --- this is intentional.
*
+ * colloid_p can also be null.
+ *
* pstate is only used for error location info, and may be NULL.
*/
Type
LookupTypeName(ParseState *pstate, const TypeName *typeName,
- int32 *typmod_p)
+ int32 *typmod_p, Oid *collid_p)
{
Oid typoid;
HeapTuple tup;
int32 typmod;
+ Oid collid;
if (typeName->names == NIL)
{
@@ -174,6 +180,11 @@ LookupTypeName(ParseState *pstate, const TypeName *typeName,
if (typmod_p)
*typmod_p = typmod;
+ collid = typenameCollation(pstate, typeName, (Type) tup);
+
+ if (collid_p)
+ *collid_p = collid;
+
return (Type) tup;
}
@@ -185,11 +196,11 @@ LookupTypeName(ParseState *pstate, const TypeName *typeName,
* Callers of this can therefore assume the result is a fully valid type.
*/
Type
-typenameType(ParseState *pstate, const TypeName *typeName, int32 *typmod_p)
+typenameType(ParseState *pstate, const TypeName *typeName, int32 *typmod_p, Oid *collid_p)
{
Type tup;
- tup = LookupTypeName(pstate, typeName, typmod_p);
+ tup = LookupTypeName(pstate, typeName, typmod_p, collid_p);
if (tup == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -217,7 +228,7 @@ typenameTypeId(ParseState *pstate, const TypeName *typeName)
Oid typoid;
Type tup;
- tup = typenameType(pstate, typeName, NULL);
+ tup = typenameType(pstate, typeName, NULL, NULL);
typoid = HeapTupleGetOid(tup);
ReleaseSysCache(tup);
@@ -236,7 +247,25 @@ typenameTypeIdAndMod(ParseState *pstate, const TypeName *typeName,
{
Type tup;
- tup = typenameType(pstate, typeName, typmod_p);
+ tup = typenameType(pstate, typeName, typmod_p, NULL);
+ *typeid_p = HeapTupleGetOid(tup);
+ ReleaseSysCache(tup);
+}
+
+/*
+ * typenameTypeIdModColl - given a TypeName, return the type's OID,
+ * typmod, and collation
+ *
+ * This is equivalent to typenameType, but we only hand back the type OID,
+ * typmod, and collation, not the syscache entry.
+ */
+void
+typenameTypeIdModColl(ParseState *pstate, const TypeName *typeName,
+ Oid *typeid_p, int32 *typmod_p, Oid *collid_p)
+{
+ Type tup;
+
+ tup = typenameType(pstate, typeName, typmod_p, collid_p);
*typeid_p = HeapTupleGetOid(tup);
ReleaseSysCache(tup);
}
@@ -351,6 +380,62 @@ typenameTypeMod(ParseState *pstate, const TypeName *typeName, Type typ)
}
/*
+ * typenameCollation - given a TypeName, return the collation OID
+ *
+ * This will throw an error if the TypeName includes a collation but
+ * the data type does not support collations.
+ *
+ * The actual type OID represented by the TypeName must already have been
+ * looked up, and is passed as "typ".
+ *
+ * pstate is only used for error location info, and may be NULL.
+ */
+static Oid
+typenameCollation(ParseState *pstate, const TypeName *typeName, Type typ)
+{
+ Oid typcollation = ((Form_pg_type) GETSTRUCT(typ))->typcollation;
+
+ /* return prespecified collation OID if no collation name specified */
+ if (typeName->collnames == NIL)
+ {
+ if (typeName->collOid == InvalidOid)
+ return typcollation;
+ else
+ return typeName->collOid;
+ }
+
+ if (!OidIsValid(typcollation))
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("collations are not supported by type %s",
+ format_type_be(HeapTupleGetOid(typ))),
+ parser_errposition(pstate, typeName->location)));
+
+ return LookupCollation(pstate, typeName->collnames, typeName->location);
+}
+
+/*
+ * LookupCollation
+ *
+ * Look up collation by name, return OID, with support for error
+ * location.
+ */
+Oid
+LookupCollation(ParseState *pstate, List *collnames, int location)
+{
+ Oid colloid;
+ ParseCallbackState pcbstate;
+
+ setup_parser_errposition_callback(&pcbstate, pstate, location);
+
+ colloid = get_collation_oid(collnames, false);
+
+ cancel_parser_errposition_callback(&pcbstate);
+
+ return colloid;
+}
+
+/*
* appendTypeNameToBuffer
* Append a string representing the name of a TypeName to a StringInfo.
* This is the shared guts of TypeNameToString and TypeNameListToString.
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index e0ab88232b..61ce840a5e 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -627,7 +627,8 @@ transformInhRelation(CreateStmtContext *cxt, InhRelation *inhRelation)
def = makeNode(ColumnDef);
def->colname = pstrdup(attributeName);
def->typeName = makeTypeNameFromOid(attribute->atttypid,
- attribute->atttypmod);
+ attribute->atttypmod,
+ attribute->attcollation);
def->inhcount = 0;
def->is_local = true;
def->is_not_null = attribute->attnotnull;
@@ -821,7 +822,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
AssertArg(ofTypename);
- tuple = typenameType(NULL, ofTypename, NULL);
+ tuple = typenameType(NULL, ofTypename, NULL, NULL);
typ = (Form_pg_type) GETSTRUCT(tuple);
ofTypeId = HeapTupleGetOid(tuple);
ofTypename->typeOid = ofTypeId; /* cached for later */
@@ -842,7 +843,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
continue;
n->colname = pstrdup(NameStr(attr->attname));
- n->typeName = makeTypeNameFromOid(attr->atttypid, attr->atttypmod);
+ n->typeName = makeTypeNameFromOid(attr->atttypid, attr->atttypmod, attr->attcollation);
n->constraints = NULL;
n->is_local = true;
n->is_from_type = true;
@@ -2446,7 +2447,7 @@ transformColumnType(CreateStmtContext *cxt, ColumnDef *column)
/*
* All we really need to do here is verify that the type is valid.
*/
- Type ctype = typenameType(cxt->pstate, column->typeName, NULL);
+ Type ctype = typenameType(cxt->pstate, column->typeName, NULL, NULL);
ReleaseSysCache(ctype);
}
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index c12d4cb6a3..c6491e1519 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -747,6 +747,7 @@ rewriteTargetListIU(Query *parsetree, Relation target_relation,
attrno,
att_tup->atttypid,
att_tup->atttypmod,
+ att_tup->attcollation,
0);
new_tle = makeTargetEntry((Expr *) new_expr,
@@ -1127,6 +1128,7 @@ rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte,
SelfItemPointerAttributeNumber,
TIDOID,
-1,
+ InvalidOid,
0);
attrname = "ctid";
diff --git a/src/backend/tsearch/ts_locale.c b/src/backend/tsearch/ts_locale.c
index 2b6a6cb946..e42c4734c7 100644
--- a/src/backend/tsearch/ts_locale.c
+++ b/src/backend/tsearch/ts_locale.c
@@ -13,6 +13,7 @@
*/
#include "postgres.h"
+#include "catalog/pg_collation.h"
#include "storage/fd.h"
#include "tsearch/ts_locale.h"
#include "tsearch/ts_public.h"
@@ -27,11 +28,12 @@ t_isdigit(const char *ptr)
{
int clen = pg_mblen(ptr);
wchar_t character[2];
+ Oid collation = DEFAULT_COLLATION_OID; /*TODO*/
- if (clen == 1 || lc_ctype_is_c())
+ if (clen == 1 || lc_ctype_is_c(collation))
return isdigit(TOUCHAR(ptr));
- char2wchar(character, 2, ptr, clen);
+ char2wchar(character, 2, ptr, clen, collation);
return iswdigit((wint_t) character[0]);
}
@@ -41,11 +43,12 @@ t_isspace(const char *ptr)
{
int clen = pg_mblen(ptr);
wchar_t character[2];
+ Oid collation = DEFAULT_COLLATION_OID; /*TODO*/
- if (clen == 1 || lc_ctype_is_c())
+ if (clen == 1 || lc_ctype_is_c(collation))
return isspace(TOUCHAR(ptr));
- char2wchar(character, 2, ptr, clen);
+ char2wchar(character, 2, ptr, clen, collation);
return iswspace((wint_t) character[0]);
}
@@ -55,11 +58,12 @@ t_isalpha(const char *ptr)
{
int clen = pg_mblen(ptr);
wchar_t character[2];
+ Oid collation = DEFAULT_COLLATION_OID; /*TODO*/
- if (clen == 1 || lc_ctype_is_c())
+ if (clen == 1 || lc_ctype_is_c(collation))
return isalpha(TOUCHAR(ptr));
- char2wchar(character, 2, ptr, clen);
+ char2wchar(character, 2, ptr, clen, collation);
return iswalpha((wint_t) character[0]);
}
@@ -69,11 +73,12 @@ t_isprint(const char *ptr)
{
int clen = pg_mblen(ptr);
wchar_t character[2];
+ Oid collation = DEFAULT_COLLATION_OID; /*TODO*/
- if (clen == 1 || lc_ctype_is_c())
+ if (clen == 1 || lc_ctype_is_c(collation))
return isprint(TOUCHAR(ptr));
- char2wchar(character, 2, ptr, clen);
+ char2wchar(character, 2, ptr, clen, collation);
return iswprint((wint_t) character[0]);
}
@@ -238,6 +243,7 @@ char *
lowerstr_with_len(const char *str, int len)
{
char *out;
+ Oid collation = DEFAULT_COLLATION_OID; /*TODO*/
if (len == 0)
return pstrdup("");
@@ -250,7 +256,7 @@ lowerstr_with_len(const char *str, int len)
* Also, for a C locale there is no need to process as multibyte. From
* backend/utils/adt/oracle_compat.c Teodor
*/
- if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c())
+ if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c(collation))
{
wchar_t *wstr,
*wptr;
@@ -263,7 +269,7 @@ lowerstr_with_len(const char *str, int len)
*/
wptr = wstr = (wchar_t *) palloc(sizeof(wchar_t) * (len + 1));
- wlen = char2wchar(wstr, len + 1, str, len);
+ wlen = char2wchar(wstr, len + 1, str, len, collation);
Assert(wlen <= len);
while (*wptr)
@@ -278,7 +284,7 @@ lowerstr_with_len(const char *str, int len)
len = pg_database_encoding_max_length() * wlen + 1;
out = (char *) palloc(len);
- wlen = wchar2char(out, wstr, len);
+ wlen = wchar2char(out, wstr, len, collation);
pfree(wstr);
diff --git a/src/backend/tsearch/wparser_def.c b/src/backend/tsearch/wparser_def.c
index 40eca64895..65d0632f9a 100644
--- a/src/backend/tsearch/wparser_def.c
+++ b/src/backend/tsearch/wparser_def.c
@@ -14,6 +14,7 @@
#include "postgres.h"
+#include "catalog/pg_collation.h"
#include "commands/defrem.h"
#include "tsearch/ts_locale.h"
#include "tsearch/ts_public.h"
@@ -286,6 +287,7 @@ static TParser *
TParserInit(char *str, int len)
{
TParser *prs = (TParser *) palloc0(sizeof(TParser));
+ Oid collation = DEFAULT_COLLATION_OID; /*TODO*/
prs->charmaxlen = pg_database_encoding_max_length();
prs->str = str;
@@ -299,7 +301,7 @@ TParserInit(char *str, int len)
if (prs->charmaxlen > 1)
{
prs->usewide = true;
- if ( lc_ctype_is_c() )
+ if ( lc_ctype_is_c(collation) )
{
/*
* char2wchar doesn't work for C-locale and
@@ -311,7 +313,7 @@ TParserInit(char *str, int len)
else
{
prs->wstr = (wchar_t *) palloc(sizeof(wchar_t) * (prs->lenstr + 1));
- char2wchar(prs->wstr, prs->lenstr + 1, prs->str, prs->lenstr);
+ char2wchar(prs->wstr, prs->lenstr + 1, prs->str, prs->lenstr, collation);
}
}
else
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 931c6953cb..4ac9830878 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -3307,6 +3307,7 @@ array_cmp(FunctionCallInfo fcinfo)
{
ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
+ Oid collation = PG_GET_COLLATION();
int ndims1 = ARR_NDIM(array1);
int ndims2 = ARR_NDIM(array2);
int *dims1 = ARR_DIMS(array1);
@@ -3341,7 +3342,8 @@ array_cmp(FunctionCallInfo fcinfo)
*/
typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
if (typentry == NULL ||
- typentry->type_id != element_type)
+ typentry->type_id != element_type ||
+ typentry->cmp_proc_finfo.fn_collation != collation)
{
typentry = lookup_type_cache(element_type,
TYPECACHE_CMP_PROC_FINFO);
@@ -3351,6 +3353,7 @@ array_cmp(FunctionCallInfo fcinfo)
errmsg("could not identify a comparison function for type %s",
format_type_be(element_type))));
fcinfo->flinfo->fn_extra = (void *) typentry;
+ typentry->cmp_proc_finfo.fn_collation = collation;
}
typlen = typentry->typlen;
typbyval = typentry->typbyval;
diff --git a/src/backend/utils/adt/format_type.c b/src/backend/utils/adt/format_type.c
index b56bb74bdc..f85e0bbd00 100644
--- a/src/backend/utils/adt/format_type.c
+++ b/src/backend/utils/adt/format_type.c
@@ -18,6 +18,7 @@
#include <ctype.h>
#include "catalog/namespace.h"
+#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
@@ -28,7 +29,8 @@
#define MAX_INT32_LEN 11
static char *format_type_internal(Oid type_oid, int32 typemod,
- bool typemod_given, bool allow_invalid);
+ bool typemod_given, bool allow_invalid,
+ Oid collation_oid);
static char *printTypmod(const char *typname, int32 typmod, Oid typmodout);
static char *
psnprintf(size_t len, const char *fmt,...)
@@ -67,6 +69,7 @@ format_type(PG_FUNCTION_ARGS)
{
Oid type_oid;
int32 typemod;
+ Oid collation_oid;
char *result;
/* Since this function is not strict, we must test for null args */
@@ -74,13 +77,14 @@ format_type(PG_FUNCTION_ARGS)
PG_RETURN_NULL();
type_oid = PG_GETARG_OID(0);
+ collation_oid = PG_ARGISNULL(2) ? InvalidOid : PG_GETARG_OID(2);
if (PG_ARGISNULL(1))
- result = format_type_internal(type_oid, -1, false, true);
+ result = format_type_internal(type_oid, -1, false, true, collation_oid);
else
{
typemod = PG_GETARG_INT32(1);
- result = format_type_internal(type_oid, typemod, true, true);
+ result = format_type_internal(type_oid, typemod, true, true, collation_oid);
}
PG_RETURN_TEXT_P(cstring_to_text(result));
@@ -95,7 +99,7 @@ format_type(PG_FUNCTION_ARGS)
char *
format_type_be(Oid type_oid)
{
- return format_type_internal(type_oid, -1, false, false);
+ return format_type_internal(type_oid, -1, false, false, InvalidOid);
}
/*
@@ -104,14 +108,15 @@ format_type_be(Oid type_oid)
char *
format_type_with_typemod(Oid type_oid, int32 typemod)
{
- return format_type_internal(type_oid, typemod, true, false);
+ return format_type_internal(type_oid, typemod, true, false, InvalidOid);
}
static char *
format_type_internal(Oid type_oid, int32 typemod,
- bool typemod_given, bool allow_invalid)
+ bool typemod_given, bool allow_invalid,
+ Oid collation_oid)
{
bool with_typemod = typemod_given && (typemod >= 0);
HeapTuple tuple;
@@ -317,6 +322,12 @@ format_type_internal(Oid type_oid, int32 typemod,
ReleaseSysCache(tuple);
+ if (collation_oid && collation_oid != DEFAULT_COLLATION_OID)
+ {
+ char *collstr = generate_collation_name(collation_oid);
+ buf = psnprintf(strlen(buf) + 10 + strlen(collstr), "%s COLLATE %s", buf, collstr);
+ }
+
return buf;
}
@@ -420,7 +431,7 @@ oidvectortypes(PG_FUNCTION_ARGS)
for (num = 0; num < numargs; num++)
{
char *typename = format_type_internal(oidArray->values[num], -1,
- false, true);
+ false, true, InvalidOid);
size_t slen = strlen(typename);
if (left < (slen + 2))
diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
index 4855bac41d..f90d36d24c 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -82,6 +82,7 @@
#include <wctype.h>
#endif
+#include "catalog/pg_collation.h"
#include "mb/pg_wchar.h"
#include "utils/builtins.h"
#include "utils/date.h"
@@ -953,7 +954,7 @@ static void parse_format(FormatNode *node, char *str, const KeyWord *kw,
KeySuffix *suf, const int *index, int ver, NUMDesc *Num);
static void DCH_to_char(FormatNode *node, bool is_interval,
- TmToChar *in, char *out);
+ TmToChar *in, char *out, Oid collid);
static void DCH_from_char(FormatNode *node, char *in, TmFromChar *out);
#ifdef DEBUG_TO_FROM_CHAR
@@ -981,7 +982,7 @@ static char *get_last_relevant_decnum(char *num);
static void NUM_numpart_from_char(NUMProc *Np, int id, int plen);
static void NUM_numpart_to_char(NUMProc *Np, int id);
static char *NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
- int plen, int sign, bool is_to_char);
+ int plen, int sign, bool is_to_char, Oid collid);
static DCHCacheEntry *DCH_cache_search(char *str);
static DCHCacheEntry *DCH_cache_getnew(char *str);
@@ -1470,15 +1471,19 @@ str_numth(char *dest, char *num, int type)
* to this function. The result is a palloc'd, null-terminated string.
*/
char *
-str_tolower(const char *buff, size_t nbytes)
+str_tolower(const char *buff, size_t nbytes, Oid collid)
{
char *result;
+ pg_locale_t mylocale = 0;
if (!buff)
return NULL;
+ if (collid != DEFAULT_COLLATION_OID)
+ mylocale = pg_newlocale_from_collation(collid);
+
#ifdef USE_WIDE_UPPER_LOWER
- if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c())
+ if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c(collid))
{
wchar_t *workspace;
size_t curr_char;
@@ -1493,16 +1498,21 @@ str_tolower(const char *buff, size_t nbytes)
/* Output workspace cannot have more codes than input bytes */
workspace = (wchar_t *) palloc((nbytes + 1) * sizeof(wchar_t));
- char2wchar(workspace, nbytes + 1, buff, nbytes);
+ char2wchar(workspace, nbytes + 1, buff, nbytes, collid);
for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
+#ifdef HAVE_LOCALE_T
+ if (mylocale)
+ workspace[curr_char] = towlower_l(workspace[curr_char], mylocale);
+ else
+#endif
workspace[curr_char] = towlower(workspace[curr_char]);
/* Make result large enough; case change might change number of bytes */
result_size = curr_char * pg_database_encoding_max_length() + 1;
result = palloc(result_size);
- wchar2char(result, workspace, result_size);
+ wchar2char(result, workspace, result_size, collid);
pfree(workspace);
}
else
@@ -1526,15 +1536,19 @@ str_tolower(const char *buff, size_t nbytes)
* to this function. The result is a palloc'd, null-terminated string.
*/
char *
-str_toupper(const char *buff, size_t nbytes)
+str_toupper(const char *buff, size_t nbytes, Oid collid)
{
char *result;
+ pg_locale_t mylocale = 0;
if (!buff)
return NULL;
+ if (collid != DEFAULT_COLLATION_OID)
+ mylocale = pg_newlocale_from_collation(collid);
+
#ifdef USE_WIDE_UPPER_LOWER
- if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c())
+ if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c(collid))
{
wchar_t *workspace;
size_t curr_char;
@@ -1549,16 +1563,21 @@ str_toupper(const char *buff, size_t nbytes)
/* Output workspace cannot have more codes than input bytes */
workspace = (wchar_t *) palloc((nbytes + 1) * sizeof(wchar_t));
- char2wchar(workspace, nbytes + 1, buff, nbytes);
+ char2wchar(workspace, nbytes + 1, buff, nbytes, collid);
for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
+#ifdef HAVE_LOCALE_T
+ if (mylocale)
+ workspace[curr_char] = towupper_l(workspace[curr_char], mylocale);
+ else
+#endif
workspace[curr_char] = towupper(workspace[curr_char]);
/* Make result large enough; case change might change number of bytes */
result_size = curr_char * pg_database_encoding_max_length() + 1;
result = palloc(result_size);
- wchar2char(result, workspace, result_size);
+ wchar2char(result, workspace, result_size, collid);
pfree(workspace);
}
else
@@ -1582,16 +1601,20 @@ str_toupper(const char *buff, size_t nbytes)
* to this function. The result is a palloc'd, null-terminated string.
*/
char *
-str_initcap(const char *buff, size_t nbytes)
+str_initcap(const char *buff, size_t nbytes, Oid collid)
{
char *result;
int wasalnum = false;
+ pg_locale_t mylocale = 0;
if (!buff)
return NULL;
+ if (collid != DEFAULT_COLLATION_OID)
+ mylocale = pg_newlocale_from_collation(collid);
+
#ifdef USE_WIDE_UPPER_LOWER
- if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c())
+ if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c(collid))
{
wchar_t *workspace;
size_t curr_char;
@@ -1606,22 +1629,35 @@ str_initcap(const char *buff, size_t nbytes)
/* Output workspace cannot have more codes than input bytes */
workspace = (wchar_t *) palloc((nbytes + 1) * sizeof(wchar_t));
- char2wchar(workspace, nbytes + 1, buff, nbytes);
+ char2wchar(workspace, nbytes + 1, buff, nbytes, collid);
for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
{
- if (wasalnum)
- workspace[curr_char] = towlower(workspace[curr_char]);
+#ifdef HAVE_LOCALE_T
+ if (mylocale)
+ {
+ if (wasalnum)
+ workspace[curr_char] = towlower_l(workspace[curr_char], mylocale);
+ else
+ workspace[curr_char] = towupper_l(workspace[curr_char], mylocale);
+ wasalnum = iswalnum_l(workspace[curr_char], mylocale);
+ }
else
- workspace[curr_char] = towupper(workspace[curr_char]);
- wasalnum = iswalnum(workspace[curr_char]);
+#endif
+ {
+ if (wasalnum)
+ workspace[curr_char] = towlower(workspace[curr_char]);
+ else
+ workspace[curr_char] = towupper(workspace[curr_char]);
+ wasalnum = iswalnum(workspace[curr_char]);
+ }
}
/* Make result large enough; case change might change number of bytes */
result_size = curr_char * pg_database_encoding_max_length() + 1;
result = palloc(result_size);
- wchar2char(result, workspace, result_size);
+ wchar2char(result, workspace, result_size, collid);
pfree(workspace);
}
else
@@ -1647,21 +1683,21 @@ str_initcap(const char *buff, size_t nbytes)
/* convenience routines for when the input is null-terminated */
static char *
-str_tolower_z(const char *buff)
+str_tolower_z(const char *buff, Oid collid)
{
- return str_tolower(buff, strlen(buff));
+ return str_tolower(buff, strlen(buff), collid);
}
static char *
-str_toupper_z(const char *buff)
+str_toupper_z(const char *buff, Oid collid)
{
- return str_toupper(buff, strlen(buff));
+ return str_toupper(buff, strlen(buff), collid);
}
static char *
-str_initcap_z(const char *buff)
+str_initcap_z(const char *buff, Oid collid)
{
- return str_initcap(buff, strlen(buff));
+ return str_initcap(buff, strlen(buff), collid);
}
@@ -2039,7 +2075,7 @@ from_char_seq_search(int *dest, char **src, char **array, int type, int max,
* ----------
*/
static void
-DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
+DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid collid)
{
FormatNode *n;
char *s;
@@ -2151,7 +2187,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
INVALID_FOR_INTERVAL;
if (tmtcTzn(in))
{
- char *p = str_tolower_z(tmtcTzn(in));
+ char *p = str_tolower_z(tmtcTzn(in), collid);
strcpy(s, p);
pfree(p);
@@ -2195,10 +2231,10 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
if (!tm->tm_mon)
break;
if (S_TM(n->suffix))
- strcpy(s, str_toupper_z(localized_full_months[tm->tm_mon - 1]));
+ strcpy(s, str_toupper_z(localized_full_months[tm->tm_mon - 1], collid));
else
sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9,
- str_toupper_z(months_full[tm->tm_mon - 1]));
+ str_toupper_z(months_full[tm->tm_mon - 1], collid));
s += strlen(s);
break;
case DCH_Month:
@@ -2206,7 +2242,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
if (!tm->tm_mon)
break;
if (S_TM(n->suffix))
- strcpy(s, str_initcap_z(localized_full_months[tm->tm_mon - 1]));
+ strcpy(s, str_initcap_z(localized_full_months[tm->tm_mon - 1], collid));
else
sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, months_full[tm->tm_mon - 1]);
s += strlen(s);
@@ -2216,7 +2252,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
if (!tm->tm_mon)
break;
if (S_TM(n->suffix))
- strcpy(s, str_tolower_z(localized_full_months[tm->tm_mon - 1]));
+ strcpy(s, str_tolower_z(localized_full_months[tm->tm_mon - 1], collid));
else
{
sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, months_full[tm->tm_mon - 1]);
@@ -2229,9 +2265,9 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
if (!tm->tm_mon)
break;
if (S_TM(n->suffix))
- strcpy(s, str_toupper_z(localized_abbrev_months[tm->tm_mon - 1]));
+ strcpy(s, str_toupper_z(localized_abbrev_months[tm->tm_mon - 1], collid));
else
- strcpy(s, str_toupper_z(months[tm->tm_mon - 1]));
+ strcpy(s, str_toupper_z(months[tm->tm_mon - 1], collid));
s += strlen(s);
break;
case DCH_Mon:
@@ -2239,7 +2275,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
if (!tm->tm_mon)
break;
if (S_TM(n->suffix))
- strcpy(s, str_initcap_z(localized_abbrev_months[tm->tm_mon - 1]));
+ strcpy(s, str_initcap_z(localized_abbrev_months[tm->tm_mon - 1], collid));
else
strcpy(s, months[tm->tm_mon - 1]);
s += strlen(s);
@@ -2249,7 +2285,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
if (!tm->tm_mon)
break;
if (S_TM(n->suffix))
- strcpy(s, str_tolower_z(localized_abbrev_months[tm->tm_mon - 1]));
+ strcpy(s, str_tolower_z(localized_abbrev_months[tm->tm_mon - 1], collid));
else
{
strcpy(s, months[tm->tm_mon - 1]);
@@ -2266,16 +2302,16 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
case DCH_DAY:
INVALID_FOR_INTERVAL;
if (S_TM(n->suffix))
- strcpy(s, str_toupper_z(localized_full_days[tm->tm_wday]));
+ strcpy(s, str_toupper_z(localized_full_days[tm->tm_wday], collid));
else
sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9,
- str_toupper_z(days[tm->tm_wday]));
+ str_toupper_z(days[tm->tm_wday], collid));
s += strlen(s);
break;
case DCH_Day:
INVALID_FOR_INTERVAL;
if (S_TM(n->suffix))
- strcpy(s, str_initcap_z(localized_full_days[tm->tm_wday]));
+ strcpy(s, str_initcap_z(localized_full_days[tm->tm_wday], collid));
else
sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, days[tm->tm_wday]);
s += strlen(s);
@@ -2283,7 +2319,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
case DCH_day:
INVALID_FOR_INTERVAL;
if (S_TM(n->suffix))
- strcpy(s, str_tolower_z(localized_full_days[tm->tm_wday]));
+ strcpy(s, str_tolower_z(localized_full_days[tm->tm_wday], collid));
else
{
sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, days[tm->tm_wday]);
@@ -2294,15 +2330,15 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
case DCH_DY:
INVALID_FOR_INTERVAL;
if (S_TM(n->suffix))
- strcpy(s, str_toupper_z(localized_abbrev_days[tm->tm_wday]));
+ strcpy(s, str_toupper_z(localized_abbrev_days[tm->tm_wday], collid));
else
- strcpy(s, str_toupper_z(days_short[tm->tm_wday]));
+ strcpy(s, str_toupper_z(days_short[tm->tm_wday], collid));
s += strlen(s);
break;
case DCH_Dy:
INVALID_FOR_INTERVAL;
if (S_TM(n->suffix))
- strcpy(s, str_initcap_z(localized_abbrev_days[tm->tm_wday]));
+ strcpy(s, str_initcap_z(localized_abbrev_days[tm->tm_wday], collid));
else
strcpy(s, days_short[tm->tm_wday]);
s += strlen(s);
@@ -2310,7 +2346,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
case DCH_dy:
INVALID_FOR_INTERVAL;
if (S_TM(n->suffix))
- strcpy(s, str_tolower_z(localized_abbrev_days[tm->tm_wday]));
+ strcpy(s, str_tolower_z(localized_abbrev_days[tm->tm_wday], collid));
else
{
strcpy(s, days_short[tm->tm_wday]);
@@ -2846,7 +2882,7 @@ DCH_cache_search(char *str)
* for formatting.
*/
static text *
-datetime_to_char_body(TmToChar *tmtc, text *fmt, bool is_interval)