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
This commit is contained in:
Peter Eisentraut 2011-02-08 23:04:18 +02:00
parent 1703f0e8da
commit 414c5a2ea6
156 changed files with 4519 additions and 582 deletions

View File

@ -297,3 +297,32 @@ int main()
])dnl AC_CACHE_VAL ])dnl AC_CACHE_VAL
AC_MSG_RESULT([$pgac_cv_printf_arg_control]) AC_MSG_RESULT([$pgac_cv_printf_arg_control])
])# PGAC_FUNC_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

108
configure vendored
View File

@ -16830,6 +16830,114 @@ _ACEOF
fi 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 "$as_me:$LINENO: checking for struct cmsgcred" >&5
$as_echo_n "checking for struct cmsgcred... " >&6; } $as_echo_n "checking for struct cmsgcred... " >&6; }
if test "${ac_cv_type_struct_cmsgcred+set}" = set; then if test "${ac_cv_type_struct_cmsgcred+set}" = set; then

View File

@ -1118,6 +1118,8 @@ AC_TYPE_INTPTR_T
AC_TYPE_UINTPTR_T AC_TYPE_UINTPTR_T
AC_TYPE_LONG_LONG_INT AC_TYPE_LONG_LONG_INT
PGAC_TYPE_LOCALE_T
AC_CHECK_TYPES([struct cmsgcred, struct fcred, struct sockcred], [], [], AC_CHECK_TYPES([struct cmsgcred, struct fcred, struct sockcred], [], [],
[#include <sys/param.h> [#include <sys/param.h>
#include <sys/types.h> #include <sys/types.h>

View File

@ -7,6 +7,7 @@
#include "fmgr.h" #include "fmgr.h"
#include "access/skey.h" #include "access/skey.h"
#include "catalog/pg_collation.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/bytea.h" #include "utils/bytea.h"
#include "utils/cash.h" #include "utils/cash.h"
@ -120,8 +121,9 @@ gin_compare_prefix_##type(PG_FUNCTION_ARGS) \
int32 res, \ int32 res, \
cmp; \ cmp; \
\ \
cmp = DatumGetInt32(DirectFunctionCall2( \ cmp = DatumGetInt32(DirectFunctionCall2WithCollation( \
TypeInfo_##type.typecmp, \ TypeInfo_##type.typecmp, \
DEFAULT_COLLATION_OID, \
(data->strategy == BTLessStrategyNumber || \ (data->strategy == BTLessStrategyNumber || \
data->strategy == BTLessEqualStrategyNumber) \ data->strategy == BTLessEqualStrategyNumber) \
? data->datum : a, \ ? data->datum : a, \

View File

@ -3,6 +3,7 @@
*/ */
#include "btree_gist.h" #include "btree_gist.h"
#include "btree_utils_var.h" #include "btree_utils_var.h"
#include "catalog/pg_collation.h"
#include "utils/builtins.h" #include "utils/builtins.h"
/* /*
@ -32,37 +33,37 @@ Datum gbt_text_same(PG_FUNCTION_ARGS);
static bool static bool
gbt_textgt(const void *a, const void *b) 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 static bool
gbt_textge(const void *a, const void *b) 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 static bool
gbt_texteq(const void *a, const void *b) 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 static bool
gbt_textle(const void *a, const void *b) 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 static bool
gbt_textlt(const void *a, const void *b) 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 static int32
gbt_textcmp(const bytea *a, const bytea *b) 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 = static gbtree_vinfo tinfo =

View File

@ -8,6 +8,7 @@
#include <float.h> #include <float.h>
#include "btree_utils_var.h" #include "btree_utils_var.h"
#include "catalog/pg_collation.h"
#include "utils/pg_locale.h" #include "utils/pg_locale.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/rel.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) if (tinfo->eml > 1)
{ {
out = (varstr_cmp(q, nlen, n, nlen) == 0); out = (varstr_cmp(q, nlen, n, nlen, DEFAULT_COLLATION_OID) == 0);
} }
else else
{ {

View File

@ -18,7 +18,7 @@ PG_MODULE_MAGIC;
* ==================== * ====================
*/ */
static int32 citextcmp(text *left, text *right); static int32 citextcmp(text *left, text *right, Oid collid);
extern Datum citext_cmp(PG_FUNCTION_ARGS); extern Datum citext_cmp(PG_FUNCTION_ARGS);
extern Datum citext_hash(PG_FUNCTION_ARGS); extern Datum citext_hash(PG_FUNCTION_ARGS);
extern Datum citext_eq(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. * Returns int32 negative, zero, or positive.
*/ */
static int32 static int32
citextcmp(text *left, text *right) citextcmp(text *left, text *right, Oid collid)
{ {
char *lcstr, char *lcstr,
*rcstr; *rcstr;
int32 result; int32 result;
lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left)); lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), collid);
rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right)); rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), collid);
result = varstr_cmp(lcstr, strlen(lcstr), result = varstr_cmp(lcstr, strlen(lcstr),
rcstr, strlen(rcstr)); rcstr, strlen(rcstr),
collid);
pfree(lcstr); pfree(lcstr);
pfree(rcstr); pfree(rcstr);
@ -75,7 +76,7 @@ citext_cmp(PG_FUNCTION_ARGS)
text *right = PG_GETARG_TEXT_PP(1); text *right = PG_GETARG_TEXT_PP(1);
int32 result; int32 result;
result = citextcmp(left, right); result = citextcmp(left, right, PG_GET_COLLATION());
PG_FREE_IF_COPY(left, 0); PG_FREE_IF_COPY(left, 0);
PG_FREE_IF_COPY(right, 1); PG_FREE_IF_COPY(right, 1);
@ -92,7 +93,7 @@ citext_hash(PG_FUNCTION_ARGS)
char *str; char *str;
Datum result; 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)); result = hash_any((unsigned char *) str, strlen(str));
pfree(str); pfree(str);
@ -121,8 +122,8 @@ citext_eq(PG_FUNCTION_ARGS)
/* We can't compare lengths in advance of downcasing ... */ /* We can't compare lengths in advance of downcasing ... */
lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left)); lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), PG_GET_COLLATION());
rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right)); 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 * 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 ... */ /* We can't compare lengths in advance of downcasing ... */
lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left)); lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), PG_GET_COLLATION());
rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right)); 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 * 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); text *right = PG_GETARG_TEXT_PP(1);
bool result; 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(left, 0);
PG_FREE_IF_COPY(right, 1); PG_FREE_IF_COPY(right, 1);
@ -194,7 +195,7 @@ citext_le(PG_FUNCTION_ARGS)
text *right = PG_GETARG_TEXT_PP(1); text *right = PG_GETARG_TEXT_PP(1);
bool result; 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(left, 0);
PG_FREE_IF_COPY(right, 1); PG_FREE_IF_COPY(right, 1);
@ -211,7 +212,7 @@ citext_gt(PG_FUNCTION_ARGS)
text *right = PG_GETARG_TEXT_PP(1); text *right = PG_GETARG_TEXT_PP(1);
bool result; 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(left, 0);
PG_FREE_IF_COPY(right, 1); PG_FREE_IF_COPY(right, 1);
@ -228,7 +229,7 @@ citext_ge(PG_FUNCTION_ARGS)
text *right = PG_GETARG_TEXT_PP(1); text *right = PG_GETARG_TEXT_PP(1);
bool result; 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(left, 0);
PG_FREE_IF_COPY(right, 1); PG_FREE_IF_COPY(right, 1);
@ -251,7 +252,7 @@ citext_smaller(PG_FUNCTION_ARGS)
text *right = PG_GETARG_TEXT_PP(1); text *right = PG_GETARG_TEXT_PP(1);
text *result; text *result;
result = citextcmp(left, right) < 0 ? left : right; result = citextcmp(left, right, PG_GET_COLLATION()) < 0 ? left : right;
PG_RETURN_TEXT_P(result); PG_RETURN_TEXT_P(result);
} }
@ -264,6 +265,6 @@ citext_larger(PG_FUNCTION_ARGS)
text *right = PG_GETARG_TEXT_PP(1); text *right = PG_GETARG_TEXT_PP(1);
text *result; text *result;
result = citextcmp(left, right) > 0 ? left : right; result = citextcmp(left, right, PG_GET_COLLATION()) > 0 ? left : right;
PG_RETURN_TEXT_P(result); PG_RETURN_TEXT_P(result);
} }

View File

@ -52,7 +52,8 @@ CREATE TYPE citext (
STORAGE = extended, STORAGE = extended,
-- make it a non-preferred member of string type category -- make it a non-preferred member of string type category
CATEGORY = 'S', CATEGORY = 'S',
PREFERRED = false PREFERRED = false,
COLLATABLE = true
); );
-- --

View File

@ -7,6 +7,7 @@
#include <ctype.h> #include <ctype.h>
#include "catalog/pg_collation.h"
#include "utils/array.h" #include "utils/array.h"
#include "utils/formatting.h" #include "utils/formatting.h"
#include "ltree.h" #include "ltree.h"
@ -90,8 +91,8 @@ bool
int int
ltree_strncasecmp(const char *a, const char *b, size_t s) ltree_strncasecmp(const char *a, const char *b, size_t s)
{ {
char *al = str_tolower(a, s); char *al = str_tolower(a, s, DEFAULT_COLLATION_OID);
char *bl = str_tolower(b, s); char *bl = str_tolower(b, s, DEFAULT_COLLATION_OID);
int res; int res;
res = strncmp(al, bl, s); res = strncmp(al, bl, s);

View File

@ -103,6 +103,11 @@
<entry>check constraints, unique constraints, primary key constraints, foreign key constraints</entry> <entry>check constraints, unique constraints, primary key constraints, foreign key constraints</entry>
</row> </row>
<row>
<entry><link linkend="catalog-pg-collation"><structname>pg_collation</structname></link></entry>
<entry>collations (locale information)</entry>
</row>
<row> <row>
<entry><link linkend="catalog-pg-conversion"><structname>pg_conversion</structname></link></entry> <entry><link linkend="catalog-pg-conversion"><structname>pg_conversion</structname></link></entry>
<entry>encoding conversion information</entry> <entry>encoding conversion information</entry>
@ -1113,6 +1118,16 @@
</entry> </entry>
</row> </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> <row>
<entry><structfield>attacl</structfield></entry> <entry><structfield>attacl</structfield></entry>
<entry><type>aclitem[]</type></entry> <entry><type>aclitem[]</type></entry>
@ -2050,6 +2065,76 @@
</sect1> </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"> <sect1 id="catalog-pg-conversion">
<title><structname>pg_conversion</structname></title> <title><structname>pg_conversion</structname></title>
@ -3125,6 +3210,16 @@
</entry> </entry>
</row> </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> <row>
<entry><structfield>indclass</structfield></entry> <entry><structfield>indclass</structfield></entry>
<entry><type>oidvector</type></entry> <entry><type>oidvector</type></entry>
@ -5866,6 +5961,21 @@
</para></entry> </para></entry>
</row> </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> <row>
<entry><structfield>typdefaultbin</structfield></entry> <entry><structfield>typdefaultbin</structfield></entry>
<entry><type>pg_node_tree</type></entry> <entry><type>pg_node_tree</type></entry>

View File

@ -304,6 +304,170 @@ initdb --locale=sv_SE
</sect1> </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"> <sect1 id="multibyte">
<title>Character Set Support</title> <title>Character Set Support</title>

View File

@ -13059,6 +13059,12 @@ SELECT relname FROM pg_class WHERE pg_table_is_visible(oid);
</thead> </thead>
<tbody> <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> <row>
<entry><literal><function>pg_conversion_is_visible(<parameter>conversion_oid</parameter>)</function></literal> <entry><literal><function>pg_conversion_is_visible(<parameter>conversion_oid</parameter>)</function></literal>
</entry> </entry>
@ -13123,6 +13129,9 @@ SELECT relname FROM pg_class WHERE pg_table_is_visible(oid);
</tgroup> </tgroup>
</table> </table>
<indexterm>
<primary>pg_collation_is_visible</primary>
</indexterm>
<indexterm> <indexterm>
<primary>pg_conversion_is_visible</primary> <primary>pg_conversion_is_visible</primary>
</indexterm> </indexterm>
@ -13256,7 +13265,7 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
<tbody> <tbody>
<row> <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><type>text</type></entry>
<entry>get SQL name of a data type</entry> <entry>get SQL name of a data type</entry>
</row> </row>
@ -13392,7 +13401,9 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
<para> <para>
<function>format_type</function> returns the SQL name of a data type that <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 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>
<para> <para>

View File

@ -921,6 +921,7 @@ CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable>
defining two operator classes for the data type and then selecting defining two operator classes for the data type and then selecting
the proper class when making an index. The operator class determines the proper class when making an index. The operator class determines
the basic sort ordering (which can then be modified by adding sort options the basic sort ordering (which can then be modified by adding sort options
<literal>COLLATE</literal>,
<literal>ASC</>/<literal>DESC</> and/or <literal>ASC</>/<literal>DESC</> and/or
<literal>NULLS FIRST</>/<literal>NULLS LAST</>). <literal>NULLS FIRST</>/<literal>NULLS LAST</>).
</para> </para>
@ -1002,6 +1003,47 @@ SELECT am.amname AS index_method,
</sect1> </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"> <sect1 id="indexes-examine">
<title>Examining Index Usage</title> <title>Examining Index Usage</title>

View File

@ -22,6 +22,7 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
CREATE DOMAIN <replaceable class="parameter">name</replaceable> [ AS ] <replaceable class="parameter">data_type</replaceable> CREATE DOMAIN <replaceable class="parameter">name</replaceable> [ AS ] <replaceable class="parameter">data_type</replaceable>
[ COLLATE <replaceable>collation</replaceable> ]
[ DEFAULT <replaceable>expression</replaceable> ] [ DEFAULT <replaceable>expression</replaceable> ]
[ <replaceable class="PARAMETER">constraint</replaceable> [ ... ] ] [ <replaceable class="PARAMETER">constraint</replaceable> [ ... ] ]
@ -83,6 +84,17 @@ CREATE DOMAIN <replaceable class="parameter">name</replaceable> [ AS ] <replacea
</listitem> </listitem>
</varlistentry> </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> <varlistentry>
<term><literal>DEFAULT <replaceable>expression</replaceable></literal></term> <term><literal>DEFAULT <replaceable>expression</replaceable></literal></term>

View File

@ -22,7 +22,7 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ <replaceable class="parameter">name</replaceable> ] ON <replaceable class="parameter">table</replaceable> [ USING <replaceable class="parameter">method</replaceable> ] 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> [, ... ] ) ] [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace</replaceable> ] [ TABLESPACE <replaceable class="parameter">tablespace</replaceable> ]
[ WHERE <replaceable class="parameter">predicate</replaceable> ] [ WHERE <replaceable class="parameter">predicate</replaceable> ]
@ -181,6 +181,20 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ <replaceable class="parameter">name</
</listitem> </listitem>
</varlistentry> </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> <varlistentry>
<term><replaceable class="parameter">opclass</replaceable></term> <term><replaceable class="parameter">opclass</replaceable></term>
<listitem> <listitem>

View File

@ -22,7 +22,7 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name</replaceable> ( [ 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> | <replaceable>table_constraint</replaceable>
| LIKE <replaceable>parent_table</replaceable> [ <replaceable>like_option</replaceable> ... ] } | LIKE <replaceable>parent_table</replaceable> [ <replaceable>like_option</replaceable> ... ] }
[, ... ] [, ... ]
@ -244,6 +244,17 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
</listitem> </listitem>
</varlistentry> </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> <varlistentry>
<term><literal>INHERITS ( <replaceable>parent_table</replaceable> [, ... ] )</literal></term> <term><literal>INHERITS ( <replaceable>parent_table</replaceable> [, ... ] )</literal></term>
<listitem> <listitem>

View File

@ -45,6 +45,7 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> (
[ , DEFAULT = <replaceable class="parameter">default</replaceable> ] [ , DEFAULT = <replaceable class="parameter">default</replaceable> ]
[ , ELEMENT = <replaceable class="parameter">element</replaceable> ] [ , ELEMENT = <replaceable class="parameter">element</replaceable> ]
[ , DELIMITER = <replaceable class="parameter">delimiter</replaceable> ] [ , DELIMITER = <replaceable class="parameter">delimiter</replaceable> ]
[ , COLLATABLE = <replaceable class="parameter">collatable</replaceable> ]
) )
CREATE TYPE <replaceable class="parameter">name</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. with the array element type, not the array type itself.
</para> </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>
<refsect2> <refsect2>

View File

@ -236,6 +236,27 @@ gmake check LANG=C MULTIBYTE=EUC_JP
existing installation. existing installation.
</para> </para>
</sect2> </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>
<sect1 id="regress-evaluation"> <sect1 id="regress-evaluation">

View File

@ -1899,6 +1899,54 @@ CAST ( <replaceable>expression</replaceable> AS <replaceable>type</replaceable>
</note> </note>
</sect2> </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"> <sect2 id="sql-syntax-scalar-subqueries">
<title>Scalar Subqueries</title> <title>Scalar Subqueries</title>

View File

@ -103,3 +103,16 @@ ScanKeyEntryInitializeWithInfo(ScanKey entry,
entry->sk_argument = argument; entry->sk_argument = argument;
fmgr_info_copy(&entry->sk_func, finfo, CurrentMemoryContext); 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;
}

View File

@ -488,10 +488,32 @@ TupleDescInitEntry(TupleDesc desc,
att->attbyval = typeForm->typbyval; att->attbyval = typeForm->typbyval;
att->attalign = typeForm->typalign; att->attalign = typeForm->typalign;
att->attstorage = typeForm->typstorage; att->attstorage = typeForm->typstorage;
att->attcollation = typeForm->typcollation;
ReleaseSysCache(tuple); 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 * BuildDescForRelation
@ -513,6 +535,7 @@ BuildDescForRelation(List *schema)
char *attname; char *attname;
Oid atttypid; Oid atttypid;
int32 atttypmod; int32 atttypmod;
Oid attcollation;
int attdim; int attdim;
/* /*
@ -536,7 +559,7 @@ BuildDescForRelation(List *schema)
attnum++; attnum++;
attname = entry->colname; attname = entry->colname;
typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod); typenameTypeIdModColl(NULL, entry->typeName, &atttypid, &atttypmod, &attcollation);
attdim = list_length(entry->typeName->arrayBounds); attdim = list_length(entry->typeName->arrayBounds);
if (entry->typeName->setof) if (entry->typeName->setof)
@ -547,6 +570,7 @@ BuildDescForRelation(List *schema)
TupleDescInitEntry(desc, attnum, attname, TupleDescInitEntry(desc, attnum, attname,
atttypid, atttypmod, attdim); atttypid, atttypmod, attdim);
TupleDescInitEntryCollation(desc, attnum, attcollation);
/* Override TupleDescInitEntry's settings as requested */ /* Override TupleDescInitEntry's settings as requested */
if (entry->storage) if (entry->storage)
@ -588,18 +612,20 @@ BuildDescForRelation(List *schema)
* with functions returning RECORD. * with functions returning RECORD.
*/ */
TupleDesc TupleDesc
BuildDescFromLists(List *names, List *types, List *typmods) BuildDescFromLists(List *names, List *types, List *typmods, List *collations)
{ {
int natts; int natts;
AttrNumber attnum; AttrNumber attnum;
ListCell *l1; ListCell *l1;
ListCell *l2; ListCell *l2;
ListCell *l3; ListCell *l3;
ListCell *l4;
TupleDesc desc; TupleDesc desc;
natts = list_length(names); natts = list_length(names);
Assert(natts == list_length(types)); Assert(natts == list_length(types));
Assert(natts == list_length(typmods)); Assert(natts == list_length(typmods));
Assert(natts == list_length(collations));
/* /*
* allocate a new tuple descriptor * allocate a new tuple descriptor
@ -610,20 +636,25 @@ BuildDescFromLists(List *names, List *types, List *typmods)
l2 = list_head(types); l2 = list_head(types);
l3 = list_head(typmods); l3 = list_head(typmods);
l4 = list_head(collations);
foreach(l1, names) foreach(l1, names)
{ {
char *attname = strVal(lfirst(l1)); char *attname = strVal(lfirst(l1));
Oid atttypid; Oid atttypid;
int32 atttypmod; int32 atttypmod;
Oid attcollation;
atttypid = lfirst_oid(l2); atttypid = lfirst_oid(l2);
l2 = lnext(l2); l2 = lnext(l2);
atttypmod = lfirst_int(l3); atttypmod = lfirst_int(l3);
l3 = lnext(l3); l3 = lnext(l3);
attcollation = lfirst_oid(l4);
l4 = lnext(l4);
attnum++; attnum++;
TupleDescInitEntry(desc, attnum, attname, atttypid, atttypmod, 0); TupleDescInitEntry(desc, attnum, attname, atttypid, atttypmod, 0);
TupleDescInitEntryCollation(desc, attnum, attcollation);
} }
return desc; return desc;

View File

@ -22,6 +22,7 @@
#include "storage/freespace.h" #include "storage/freespace.h"
#include "storage/indexfsm.h" #include "storage/indexfsm.h"
#include "storage/lmgr.h" #include "storage/lmgr.h"
#include "utils/lsyscache.h"
/* /*
@ -60,6 +61,8 @@ initGinState(GinState *state, Relation index)
fmgr_info_copy(&(state->compareFn[i]), fmgr_info_copy(&(state->compareFn[i]),
index_getprocinfo(index, i + 1, GIN_COMPARE_PROC), index_getprocinfo(index, i + 1, GIN_COMPARE_PROC),
CurrentMemoryContext); CurrentMemoryContext);
fmgr_info_collation(get_typcollation(index->rd_att->attrs[i]->atttypid),
&(state->compareFn[i]));
fmgr_info_copy(&(state->extractValueFn[i]), fmgr_info_copy(&(state->extractValueFn[i]),
index_getprocinfo(index, i + 1, GIN_EXTRACTVALUE_PROC), index_getprocinfo(index, i + 1, GIN_EXTRACTVALUE_PROC),
CurrentMemoryContext); CurrentMemoryContext);

View File

@ -872,6 +872,8 @@ index_getprocinfo(Relation irel,
procnum, attnum, RelationGetRelationName(irel)); procnum, attnum, RelationGetRelationName(irel));
fmgr_info_cxt(procId, locinfo, irel->rd_indexcxt); fmgr_info_cxt(procId, locinfo, irel->rd_indexcxt);
fmgr_info_collation(irel->rd_index->indcollation.values[attnum-1],
locinfo);
} }
return locinfo; return locinfo;

View File

@ -723,6 +723,8 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
cur->sk_subtype, cur->sk_subtype,
procinfo, procinfo,
cur->sk_argument); cur->sk_argument);
ScanKeyEntryInitializeCollation(scankeys + i,
cur->sk_func.fn_collation);
} }
else else
{ {
@ -743,6 +745,8 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
cur->sk_subtype, cur->sk_subtype,
cmp_proc, cmp_proc,
cur->sk_argument); cur->sk_argument);
ScanKeyEntryInitializeCollation(scankeys + i,
cur->sk_func.fn_collation);
} }
} }
} }

View File

@ -26,6 +26,7 @@
#include "access/xact.h" #include "access/xact.h"
#include "bootstrap/bootstrap.h" #include "bootstrap/bootstrap.h"
#include "catalog/index.h" #include "catalog/index.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "libpq/pqsignal.h" #include "libpq/pqsignal.h"
#include "miscadmin.h" #include "miscadmin.h"
@ -88,56 +89,57 @@ struct typinfo
bool byval; bool byval;
char align; char align;
char storage; char storage;
Oid collation;
Oid inproc; Oid inproc;
Oid outproc; Oid outproc;
}; };
static const struct typinfo TypInfo[] = { static const struct typinfo TypInfo[] = {
{"bool", BOOLOID, 0, 1, true, 'c', 'p', {"bool", BOOLOID, 0, 1, true, 'c', 'p', InvalidOid,
F_BOOLIN, F_BOOLOUT}, F_BOOLIN, F_BOOLOUT},
{"bytea", BYTEAOID, 0, -1, false, 'i', 'x', {"bytea", BYTEAOID, 0, -1, false, 'i', 'x', InvalidOid,
F_BYTEAIN, F_BYTEAOUT}, F_BYTEAIN, F_BYTEAOUT},
{"char", CHAROID, 0, 1, true, 'c', 'p', {"char", CHAROID, 0, 1, true, 'c', 'p', InvalidOid,
F_CHARIN, F_CHAROUT}, F_CHARIN, F_CHAROUT},
{"int2", INT2OID, 0, 2, true, 's', 'p', {"int2", INT2OID, 0, 2, true, 's', 'p', InvalidOid,
F_INT2IN, F_INT2OUT}, F_INT2IN, F_INT2OUT},
{"int4", INT4OID, 0, 4, true, 'i', 'p', {"int4", INT4OID, 0, 4, true, 'i', 'p', InvalidOid,
F_INT4IN, F_INT4OUT}, F_INT4IN, F_INT4OUT},
{"float4", FLOAT4OID, 0, 4, FLOAT4PASSBYVAL, 'i', 'p', {"float4", FLOAT4OID, 0, 4, FLOAT4PASSBYVAL, 'i', 'p', InvalidOid,
F_FLOAT4IN, F_FLOAT4OUT}, F_FLOAT4IN, F_FLOAT4OUT},
{"name", NAMEOID, CHAROID, NAMEDATALEN, false, 'c', 'p', {"name", NAMEOID, CHAROID, NAMEDATALEN, false, 'c', 'p', InvalidOid,
F_NAMEIN, F_NAMEOUT}, F_NAMEIN, F_NAMEOUT},
{"regclass", REGCLASSOID, 0, 4, true, 'i', 'p', {"regclass", REGCLASSOID, 0, 4, true, 'i', 'p', InvalidOid,
F_REGCLASSIN, F_REGCLASSOUT}, F_REGCLASSIN, F_REGCLASSOUT},
{"regproc", REGPROCOID, 0, 4, true, 'i', 'p', {"regproc", REGPROCOID, 0, 4, true, 'i', 'p', InvalidOid,
F_REGPROCIN, F_REGPROCOUT}, F_REGPROCIN, F_REGPROCOUT},
{"regtype", REGTYPEOID, 0, 4, true, 'i', 'p', {"regtype", REGTYPEOID, 0, 4, true, 'i', 'p', InvalidOid,
F_REGTYPEIN, F_REGTYPEOUT}, 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}, F_TEXTIN, F_TEXTOUT},
{"oid", OIDOID, 0, 4, true, 'i', 'p', {"oid", OIDOID, 0, 4, true, 'i', 'p', InvalidOid,
F_OIDIN, F_OIDOUT}, F_OIDIN, F_OIDOUT},
{"tid", TIDOID, 0, 6, false, 's', 'p', {"tid", TIDOID, 0, 6, false, 's', 'p', InvalidOid,
F_TIDIN, F_TIDOUT}, F_TIDIN, F_TIDOUT},
{"xid", XIDOID, 0, 4, true, 'i', 'p', {"xid", XIDOID, 0, 4, true, 'i', 'p', InvalidOid,
F_XIDIN, F_XIDOUT}, F_XIDIN, F_XIDOUT},
{"cid", CIDOID, 0, 4, true, 'i', 'p', {"cid", CIDOID, 0, 4, true, 'i', 'p', InvalidOid,
F_CIDIN, F_CIDOUT}, 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}, 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}, F_INT2VECTORIN, F_INT2VECTOROUT},
{"oidvector", OIDVECTOROID, OIDOID, -1, false, 'i', 'p', {"oidvector", OIDVECTOROID, OIDOID, -1, false, 'i', 'p', InvalidOid,
F_OIDVECTORIN, F_OIDVECTOROUT}, 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}, 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}, 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}, 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}, 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} 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]->attbyval = Ap->am_typ.typbyval;
attrtypes[attnum]->attstorage = Ap->am_typ.typstorage; attrtypes[attnum]->attstorage = Ap->am_typ.typstorage;
attrtypes[attnum]->attalign = Ap->am_typ.typalign; attrtypes[attnum]->attalign = Ap->am_typ.typalign;
attrtypes[attnum]->attcollation = Ap->am_typ.typcollation;
/* if an array type, assume 1-dimensional attribute */ /* if an array type, assume 1-dimensional attribute */
if (Ap->am_typ.typelem != InvalidOid && Ap->am_typ.typlen < 0) if (Ap->am_typ.typelem != InvalidOid && Ap->am_typ.typlen < 0)
attrtypes[attnum]->attndims = 1; attrtypes[attnum]->attndims = 1;
@ -723,6 +726,7 @@ DefineAttr(char *name, char *type, int attnum)
attrtypes[attnum]->attbyval = TypInfo[typeoid].byval; attrtypes[attnum]->attbyval = TypInfo[typeoid].byval;
attrtypes[attnum]->attstorage = TypInfo[typeoid].storage; attrtypes[attnum]->attstorage = TypInfo[typeoid].storage;
attrtypes[attnum]->attalign = TypInfo[typeoid].align; attrtypes[attnum]->attalign = TypInfo[typeoid].align;
attrtypes[attnum]->attcollation = TypInfo[typeoid].collation;
/* if an array type, assume 1-dimensional attribute */ /* if an array type, assume 1-dimensional attribute */
if (TypInfo[typeoid].elem != InvalidOid && if (TypInfo[typeoid].elem != InvalidOid &&
attrtypes[attnum]->attlen < 0) attrtypes[attnum]->attlen < 0)

View File

@ -39,7 +39,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_ts_parser.h pg_ts_template.h \ pg_ts_parser.h pg_ts_template.h \
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
pg_foreign_table.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 \ toasting.h indexing.h \
) )

View File

@ -340,6 +340,7 @@ sub emit_pgattr_row
$row{attalign} = $type->{typalign}; $row{attalign} = $type->{typalign};
# set attndims if it's an array type # set attndims if it's an array type
$row{attndims} = $type->{typcategory} eq 'A' ? '1' : '0'; $row{attndims} = $type->{typcategory} eq 'A' ? '1' : '0';
$row{attcollation} = $type->{typcollation};
# attnotnull must be set true if the type is fixed-width and # attnotnull must be set true if the type is fixed-width and
# prior columns are too --- compare DefineAttr in bootstrap.c. # prior columns are too --- compare DefineAttr in bootstrap.c.
# oidvector and int2vector are also treated as not-nullable. # oidvector and int2vector are also treated as not-nullable.

View File

@ -542,6 +542,7 @@ InsertPgAttributeTuple(Relation pg_attribute_rel,
values[Anum_pg_attribute_attisdropped - 1] = BoolGetDatum(new_attribute->attisdropped); values[Anum_pg_attribute_attisdropped - 1] = BoolGetDatum(new_attribute->attisdropped);
values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal); values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal);
values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount); 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 */ /* start out with empty permissions and empty options */
nulls[Anum_pg_attribute_attacl - 1] = true; nulls[Anum_pg_attribute_attacl - 1] = true;
@ -859,7 +860,8 @@ AddNewRelationType(const char *typeName,
'x', /* fully TOASTable */ 'x', /* fully TOASTable */
-1, /* typmod */ -1, /* typmod */
0, /* array dimensions for typBaseType */ 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 */ 'x', /* fully TOASTable */
-1, /* typmod */ -1, /* typmod */
0, /* array dimensions for typBaseType */ 0, /* array dimensions for typBaseType */
false); /* Type NOT NULL */ false, /* Type NOT NULL */
InvalidOid); /* typcollation */
pfree(relarrayname); pfree(relarrayname);
} }

View File

@ -87,12 +87,14 @@ static TupleDesc ConstructTupleDescriptor(Relation heapRelation,
IndexInfo *indexInfo, IndexInfo *indexInfo,
List *indexColNames, List *indexColNames,
Oid accessMethodObjectId, Oid accessMethodObjectId,
Oid *collationObjectId,
Oid *classObjectId); Oid *classObjectId);
static void InitializeAttributeOids(Relation indexRelation, static void InitializeAttributeOids(Relation indexRelation,
int numatts, Oid indexoid); int numatts, Oid indexoid);
static void AppendAttributeTuples(Relation indexRelation, int numatts); static void AppendAttributeTuples(Relation indexRelation, int numatts);
static void UpdateIndexRelation(Oid indexoid, Oid heapoid, static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
IndexInfo *indexInfo, IndexInfo *indexInfo,
Oid *collationOids,
Oid *classOids, Oid *classOids,
int16 *coloptions, int16 *coloptions,
bool primary, bool primary,
@ -264,6 +266,7 @@ ConstructTupleDescriptor(Relation heapRelation,
IndexInfo *indexInfo, IndexInfo *indexInfo,
List *indexColNames, List *indexColNames,
Oid accessMethodObjectId, Oid accessMethodObjectId,
Oid *collationObjectId,
Oid *classObjectId) Oid *classObjectId)
{ {
int numatts = indexInfo->ii_NumIndexAttrs; int numatts = indexInfo->ii_NumIndexAttrs;
@ -398,6 +401,8 @@ ConstructTupleDescriptor(Relation heapRelation,
CheckAttributeType(NameStr(to->attname), to->atttypid, false); 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 * We do not yet have the correct relation OID for the index, so just
* set it invalid for now. InitializeAttributeOids() will fix it * set it invalid for now. InitializeAttributeOids() will fix it
@ -521,6 +526,7 @@ static void
UpdateIndexRelation(Oid indexoid, UpdateIndexRelation(Oid indexoid,
Oid heapoid, Oid heapoid,
IndexInfo *indexInfo, IndexInfo *indexInfo,
Oid *collationOids,
Oid *classOids, Oid *classOids,
int16 *coloptions, int16 *coloptions,
bool primary, bool primary,
@ -529,6 +535,7 @@ UpdateIndexRelation(Oid indexoid,
bool isvalid) bool isvalid)
{ {
int2vector *indkey; int2vector *indkey;
oidvector *indcollation;
oidvector *indclass; oidvector *indclass;
int2vector *indoption; int2vector *indoption;
Datum exprsDatum; Datum exprsDatum;
@ -546,6 +553,7 @@ UpdateIndexRelation(Oid indexoid,
indkey = buildint2vector(NULL, indexInfo->ii_NumIndexAttrs); indkey = buildint2vector(NULL, indexInfo->ii_NumIndexAttrs);
for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
indkey->values[i] = indexInfo->ii_KeyAttrNumbers[i]; indkey->values[i] = indexInfo->ii_KeyAttrNumbers[i];
indcollation = buildoidvector(collationOids, indexInfo->ii_NumIndexAttrs);
indclass = buildoidvector(classOids, indexInfo->ii_NumIndexAttrs); indclass = buildoidvector(classOids, indexInfo->ii_NumIndexAttrs);
indoption = buildint2vector(coloptions, indexInfo->ii_NumIndexAttrs); indoption = buildint2vector(coloptions, indexInfo->ii_NumIndexAttrs);
@ -601,6 +609,7 @@ UpdateIndexRelation(Oid indexoid,
/* we set isvalid and isready the same way */ /* we set isvalid and isready the same way */
values[Anum_pg_index_indisready - 1] = BoolGetDatum(isvalid); values[Anum_pg_index_indisready - 1] = BoolGetDatum(isvalid);
values[Anum_pg_index_indkey - 1] = PointerGetDatum(indkey); 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_indclass - 1] = PointerGetDatum(indclass);
values[Anum_pg_index_indoption - 1] = PointerGetDatum(indoption); values[Anum_pg_index_indoption - 1] = PointerGetDatum(indoption);
values[Anum_pg_index_indexprs - 1] = exprsDatum; values[Anum_pg_index_indexprs - 1] = exprsDatum;
@ -664,6 +673,7 @@ index_create(Relation heapRelation,
List *indexColNames, List *indexColNames,
Oid accessMethodObjectId, Oid accessMethodObjectId,
Oid tableSpaceId, Oid tableSpaceId,
Oid *collationObjectId,
Oid *classObjectId, Oid *classObjectId,
int16 *coloptions, int16 *coloptions,
Datum reloptions, Datum reloptions,
@ -761,6 +771,7 @@ index_create(Relation heapRelation,
indexInfo, indexInfo,
indexColNames, indexColNames,
accessMethodObjectId, accessMethodObjectId,
collationObjectId,
classObjectId); classObjectId);
/* /*
@ -856,7 +867,7 @@ index_create(Relation heapRelation,
* ---------------- * ----------------
*/ */
UpdateIndexRelation(indexRelationId, heapRelationId, indexInfo, UpdateIndexRelation(indexRelationId, heapRelationId, indexInfo,
classObjectId, coloptions, isprimary, is_exclusion, collationObjectId, classObjectId, coloptions, isprimary, is_exclusion,
!deferrable, !deferrable,
!concurrent); !concurrent);
@ -2370,7 +2381,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot)
ivinfo.strategy = NULL; ivinfo.strategy = NULL;
state.tuplesort = tuplesort_begin_datum(TIDOID, state.tuplesort = tuplesort_begin_datum(TIDOID,
TIDLessOperator, false, TIDLessOperator, InvalidOid, false,
maintenance_work_mem, maintenance_work_mem,
false); false);
state.htups = state.itups = state.tups_inserted = 0; state.htups = state.itups = state.tups_inserted = 0;

View File

@ -23,6 +23,7 @@
#include "catalog/dependency.h" #include "catalog/dependency.h"
#include "catalog/namespace.h" #include "catalog/namespace.h"
#include "catalog/pg_authid.h" #include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_conversion.h" #include "catalog/pg_conversion.h"
#include "catalog/pg_conversion_fn.h" #include "catalog/pg_conversion_fn.h"
#include "catalog/pg_namespace.h" #include "catalog/pg_namespace.h"
@ -37,6 +38,7 @@
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "commands/dbcommands.h" #include "commands/dbcommands.h"
#include "funcapi.h" #include "funcapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
#include "parser/parse_func.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_function_is_visible(PG_FUNCTION_ARGS);
Datum pg_operator_is_visible(PG_FUNCTION_ARGS); Datum pg_operator_is_visible(PG_FUNCTION_ARGS);
Datum pg_opclass_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_conversion_is_visible(PG_FUNCTION_ARGS);
Datum pg_ts_parser_is_visible(PG_FUNCTION_ARGS); Datum pg_ts_parser_is_visible(PG_FUNCTION_ARGS);
Datum pg_ts_dict_is_visible(PG_FUNCTION_ARGS); Datum pg_ts_dict_is_visible(PG_FUNCTION_ARGS);
@ -1610,6 +1613,89 @@ OpfamilyIsVisible(Oid opfid)
return visible; return visible;
} }
/*
* CollationGetCollid
* Try to resolve an unqualified collation name.
* Returns OID if collation found in search path, else InvalidOid.
*
* This is essentially the same as RelnameGetRelid.
*/
Oid
CollationGetCollid(const char *collname)
{
Oid collid;
ListCell *l;
recomputeNamespacePath();
foreach(l, activeSearchPath)
{
Oid namespaceId = lfirst_oid(l);
if (namespaceId == myTempNamespace)
continue; /* do not look in temp namespace */
collid = GetSysCacheOid3(COLLNAMEENCNSP,
PointerGetDatum(collname),
Int32GetDatum(GetDatabaseEncoding()),
ObjectIdGetDatum(namespaceId));
if (OidIsValid(collid))
return collid;
}
/* Not found in path */
return InvalidOid;
}
/*
* CollationIsVisible
* Determine whether a collation (identified by OID) is visible in the
* current search path. Visible means "would be found by searching
* for the unqualified collation name".
*/
bool
CollationIsVisible(Oid collid)
{
HeapTuple colltup;
Form_pg_collation collform;
Oid collnamespace;
bool visible;
colltup = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
if (!HeapTupleIsValid(colltup))
elog(ERROR, "cache lookup failed for collation %u", collid);
collform = (Form_pg_collation) GETSTRUCT(colltup);
recomputeNamespacePath();
/*
* Quick check: if it ain't in the path at all, it ain't visible. Items in
* the system namespace are surely in the path and so we needn't even do
* list_member_oid() for them.
*/
collnamespace = collform->collnamespace;
if (collnamespace != PG_CATALOG_NAMESPACE &&
!list_member_oid(activeSearchPath, collnamespace))
visible = false;
else
{
/*
* If it is in the path, it might still not be visible; it could be
* hidden by another conversion of the same name earlier in the path.
* So we must do a slow check to see if this conversion would be found
* by CollationGetCollid.
*/
char *collname = NameStr(collform->collname);
visible = (CollationGetCollid(collname) == collid);
}
ReleaseSysCache(colltup);
return visible;
}
/* /*
* ConversionGetConid * ConversionGetConid
* Try to resolve an unqualified conversion name. * Try to resolve an unqualified conversion name.
@ -2807,6 +2893,63 @@ PopOverrideSearchPath(void)
} }
/*
* get_collation_oid - find a collation by possibly qualified name
*/
Oid
get_collation_oid(List *name, bool missing_ok)
{
char *schemaname;
char *collation_name;
Oid namespaceId;
Oid colloid = InvalidOid;
ListCell *l;
int encoding;
encoding = GetDatabaseEncoding();
/* deconstruct the name list */
DeconstructQualifiedName(name, &schemaname, &collation_name);
if (schemaname)
{
/* use exact schema given */
namespaceId = LookupExplicitNamespace(schemaname);
colloid = GetSysCacheOid3(COLLNAMEENCNSP,
PointerGetDatum(collation_name),
Int32GetDatum(encoding),
ObjectIdGetDatum(namespaceId));
}
else
{
/* search for it in search path */
recomputeNamespacePath();
foreach(l, activeSearchPath)
{
namespaceId = lfirst_oid(l);
if (namespaceId == myTempNamespace)
continue; /* do not look in temp namespace */
colloid = GetSysCacheOid3(COLLNAMEENCNSP,
PointerGetDatum(collation_name),
Int32GetDatum(encoding),
ObjectIdGetDatum(namespaceId));
if (OidIsValid(colloid))
return colloid;
}
}
/* Not found in path */
if (!OidIsValid(colloid) && !missing_ok)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("collation \"%s\" for current database encoding \"%s\" does not exist",
NameListToString(name), GetDatabaseEncodingName())));
return colloid;
}
/* /*
* get_conversion_oid - find a conversion by possibly qualified name * get_conversion_oid - find a conversion by possibly qualified name
*/ */
@ -3566,6 +3709,17 @@ pg_opclass_is_visible(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(OpclassIsVisible(oid)); PG_RETURN_BOOL(OpclassIsVisible(oid));
} }
Datum
pg_collation_is_visible(PG_FUNCTION_ARGS)
{
Oid oid = PG_GETARG_OID(0);
if (!SearchSysCacheExists1(COLLOID, ObjectIdGetDatum(oid)))
PG_RETURN_NULL();
PG_RETURN_BOOL(CollationIsVisible(oid));
}
Datum Datum
pg_conversion_is_visible(PG_FUNCTION_ARGS) pg_conversion_is_visible(PG_FUNCTION_ARGS)
{ {

View File

@ -114,6 +114,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
values[i++] = ObjectIdGetDatum(InvalidOid); /* typbasetype */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typbasetype */
values[i++] = Int32GetDatum(-1); /* typtypmod */ values[i++] = Int32GetDatum(-1); /* typtypmod */
values[i++] = Int32GetDatum(0); /* typndims */ values[i++] = Int32GetDatum(0); /* typndims */
values[i++] = ObjectIdGetDatum(InvalidOid); /* typcollation */
nulls[i++] = true; /* typdefaultbin */ nulls[i++] = true; /* typdefaultbin */
nulls[i++] = true; /* typdefault */ nulls[i++] = true; /* typdefault */
@ -210,7 +211,8 @@ TypeCreate(Oid newTypeOid,
char storage, char storage,
int32 typeMod, int32 typeMod,
int32 typNDims, /* Array dimensions for baseType */ int32 typNDims, /* Array dimensions for baseType */
bool typeNotNull) bool typeNotNull,
Oid typeCollation)
{ {
Relation pg_type_desc; Relation pg_type_desc;
Oid typeObjectId; Oid typeObjectId;
@ -348,6 +350,7 @@ TypeCreate(Oid newTypeOid,
values[i++] = ObjectIdGetDatum(baseType); /* typbasetype */ values[i++] = ObjectIdGetDatum(baseType); /* typbasetype */
values[i++] = Int32GetDatum(typeMod); /* typtypmod */ values[i++] = Int32GetDatum(typeMod); /* typtypmod */
values[i++] = Int32GetDatum(typNDims); /* typndims */ values[i++] = Int32GetDatum(typNDims); /* typndims */
values[i++] = ObjectIdGetDatum(typeCollation); /* typcollation */
/* /*
* initialize the default binary value for this type. Check for nulls of * initialize the default binary value for this type. Check for nulls of

View File

@ -671,6 +671,10 @@ COMMENT ON FUNCTION ts_debug(text) IS
-- to get filled in.) -- to get filled in.)
-- --
CREATE OR REPLACE FUNCTION
format_type(oid, int DEFAULT NULL, oid DEFAULT NULL)
RETURNS text STABLE LANGUAGE internal AS 'format_type';
CREATE OR REPLACE FUNCTION CREATE OR REPLACE FUNCTION
pg_start_backup(label text, fast boolean DEFAULT false) pg_start_backup(label text, fast boolean DEFAULT false)
RETURNS text STRICT VOLATILE LANGUAGE internal AS 'pg_start_backup'; RETURNS text STRICT VOLATILE LANGUAGE internal AS 'pg_start_backup';

View File

@ -124,6 +124,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
char toast_relname[NAMEDATALEN]; char toast_relname[NAMEDATALEN];
char toast_idxname[NAMEDATALEN]; char toast_idxname[NAMEDATALEN];
IndexInfo *indexInfo; IndexInfo *indexInfo;
Oid collationObjectId[2];
Oid classObjectId[2]; Oid classObjectId[2];
int16 coloptions[2]; int16 coloptions[2];
ObjectAddress baseobject, ObjectAddress baseobject,
@ -264,6 +265,9 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
indexInfo->ii_Concurrent = false; indexInfo->ii_Concurrent = false;
indexInfo->ii_BrokenHotChain = false; indexInfo->ii_BrokenHotChain = false;
collationObjectId[0] = InvalidOid;
collationObjectId[1] = InvalidOid;
classObjectId[0] = OID_BTREE_OPS_OID; classObjectId[0] = OID_BTREE_OPS_OID;
classObjectId[1] = INT4_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"), list_make2("chunk_id", "chunk_seq"),
BTREE_AM_OID, BTREE_AM_OID,
rel->rd_rel->reltablespace, rel->rd_rel->reltablespace,
classObjectId, coloptions, (Datum) 0, collationObjectId, classObjectId, coloptions, (Datum) 0,
true, false, false, false, true, false, false, false,
true, false, false); true, false, false);

View File

@ -862,11 +862,13 @@ examine_attribute(Relation onerel, int attnum, Node *index_expr)
{ {
stats->attrtypid = exprType(index_expr); stats->attrtypid = exprType(index_expr);
stats->attrtypmod = exprTypmod(index_expr); stats->attrtypmod = exprTypmod(index_expr);
stats->attrcollation = exprCollation(index_expr);
} }
else else
{ {
stats->attrtypid = attr->atttypid; stats->attrtypid = attr->atttypid;
stats->attrtypmod = attr->atttypmod; stats->attrtypmod = attr->atttypmod;
stats->attrcollation = attr->attcollation;
} }
typtuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(stats->attrtypid)); typtuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(stats->attrtypid));
@ -1929,6 +1931,7 @@ compute_minimal_stats(VacAttrStatsP stats,
track_cnt = 0; track_cnt = 0;
fmgr_info(mystats->eqfunc, &f_cmpeq); fmgr_info(mystats->eqfunc, &f_cmpeq);
fmgr_info_collation(stats->attrcollation, &f_cmpeq);
for (i = 0; i < samplerows; i++) for (i = 0; i < samplerows; i++)
{ {
@ -2250,6 +2253,7 @@ compute_scalar_stats(VacAttrStatsP stats,
SelectSortFunction(mystats->ltopr, false, &cmpFn, &cmpFlags); SelectSortFunction(mystats->ltopr, false, &cmpFn, &cmpFlags);
fmgr_info(cmpFn, &f_cmpfn); fmgr_info(cmpFn, &f_cmpfn);
fmgr_info_collation(stats->attrcollation, &f_cmpfn);
/* Initial scan to find sortable values */ /* Initial scan to find sortable values */
for (i = 0; i < samplerows; i++) for (i = 0; i < samplerows; i++)

View File

@ -356,8 +356,8 @@ createdb(const CreatedbStmt *stmt)
* *
* Note: if you change this policy, fix initdb to match. * Note: if you change this policy, fix initdb to match.
*/ */
ctype_encoding = pg_get_encoding_from_locale(dbctype); ctype_encoding = pg_get_encoding_from_locale(dbctype, true);
collate_encoding = pg_get_encoding_from_locale(dbcollate); collate_encoding = pg_get_encoding_from_locale(dbcollate, true);
if (!(ctype_encoding == encoding || if (!(ctype_encoding == encoding ||
ctype_encoding == PG_SQL_ASCII || ctype_encoding == PG_SQL_ASCII ||

View File

@ -87,7 +87,7 @@ compute_return_type(TypeName *returnType, Oid languageOid,
Oid rettype; Oid rettype;
Type typtup; Type typtup;
typtup = LookupTypeName(NULL, returnType, NULL); typtup = LookupTypeName(NULL, returnType, NULL, NULL);
if (typtup) if (typtup)
{ {
@ -207,7 +207,7 @@ examine_parameter_list(List *parameters, Oid languageOid,
Oid toid; Oid toid;
Type typtup; Type typtup;
typtup = LookupTypeName(NULL, t, NULL); typtup = LookupTypeName(NULL, t, NULL, NULL);
if (typtup) if (typtup)
{ {
if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined) if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined)

View File

@ -58,6 +58,7 @@
/* non-export function prototypes */ /* non-export function prototypes */
static void CheckPredicate(Expr *predicate); static void CheckPredicate(Expr *predicate);
static void ComputeIndexAttrs(IndexInfo *indexInfo, static void ComputeIndexAttrs(IndexInfo *indexInfo,
Oid *collationOidP,
Oid *classOidP, Oid *classOidP,
int16 *colOptionP, int16 *colOptionP,
List *attList, List *attList,
@ -124,6 +125,7 @@ DefineIndex(RangeVar *heapRelation,
bool quiet, bool quiet,
bool concurrent) bool concurrent)
{ {
Oid *collationObjectId;
Oid *classObjectId; Oid *classObjectId;
Oid accessMethodId; Oid accessMethodId;
Oid relationId; Oid relationId;
@ -345,9 +347,10 @@ DefineIndex(RangeVar *heapRelation,
indexInfo->ii_Concurrent = concurrent; indexInfo->ii_Concurrent = concurrent;
indexInfo->ii_BrokenHotChain = false; indexInfo->ii_BrokenHotChain = false;
collationObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid)); classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
coloptions = (int16 *) palloc(numberOfAttributes * sizeof(int16)); coloptions = (int16 *) palloc(numberOfAttributes * sizeof(int16));
ComputeIndexAttrs(indexInfo, classObjectId, coloptions, attributeList, ComputeIndexAttrs(indexInfo, collationObjectId, classObjectId, coloptions, attributeList,
exclusionOpNames, relationId, exclusionOpNames, relationId,
accessMethodName, accessMethodId, accessMethodName, accessMethodId,
amcanorder, isconstraint); amcanorder, isconstraint);
@ -392,7 +395,7 @@ DefineIndex(RangeVar *heapRelation,
indexRelationId = indexRelationId =
index_create(rel, indexRelationName, indexRelationId, index_create(rel, indexRelationName, indexRelationId,
indexInfo, indexColNames, indexInfo, indexColNames,
accessMethodId, tablespaceId, classObjectId, accessMethodId, tablespaceId, collationObjectId, classObjectId,
coloptions, reloptions, primary, coloptions, reloptions, primary,
isconstraint, deferrable, initdeferred, isconstraint, deferrable, initdeferred,
allowSystemTableMods, allowSystemTableMods,
@ -764,6 +767,7 @@ CheckPredicate(Expr *predicate)
*/ */
static void static void
ComputeIndexAttrs(IndexInfo *indexInfo, ComputeIndexAttrs(IndexInfo *indexInfo,
Oid *collationOidP,
Oid *classOidP, Oid *classOidP,
int16 *colOptionP, int16 *colOptionP,
List *attList, /* list of IndexElem's */ List *attList, /* list of IndexElem's */
@ -800,6 +804,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
{ {
IndexElem *attribute = (IndexElem *) lfirst(lc); IndexElem *attribute = (IndexElem *) lfirst(lc);
Oid atttype; Oid atttype;
Oid attcollation;
/* /*
* Process the column-or-expression to be indexed. * Process the column-or-expression to be indexed.
@ -829,6 +834,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
attform = (Form_pg_attribute) GETSTRUCT(atttuple); attform = (Form_pg_attribute) GETSTRUCT(atttuple);
indexInfo->ii_KeyAttrNumbers[attn] = attform->attnum; indexInfo->ii_KeyAttrNumbers[attn] = attform->attnum;
atttype = attform->atttypid; atttype = attform->atttypid;
attcollation = attform->attcollation;
ReleaseSysCache(atttuple); ReleaseSysCache(atttuple);
} }
else if (attribute->expr && IsA(attribute->expr, Var) && else if (attribute->expr && IsA(attribute->expr, Var) &&
@ -839,6 +845,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
indexInfo->ii_KeyAttrNumbers[attn] = var->varattno; indexInfo->ii_KeyAttrNumbers[attn] = var->varattno;
atttype = get_atttype(relId, var->varattno); atttype = get_atttype(relId, var->varattno);
attcollation = var->varcollid;
} }
else else
{ {
@ -848,6 +855,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
indexInfo->ii_Expressions = lappend(indexInfo->ii_Expressions, indexInfo->ii_Expressions = lappend(indexInfo->ii_Expressions,
attribute->expr); attribute->expr);
atttype = exprType(attribute->expr); atttype = exprType(attribute->expr);
attcollation = exprCollation(attribute->expr);
/* /*
* We don't currently support generation of an actual query plan * We don't currently support generation of an actual query plan
@ -874,6 +882,20 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
errmsg("functions in index expression must be marked IMMUTABLE"))); errmsg("functions in index expression must be marked IMMUTABLE")));
} }
/*
* Collation override
*/
if (attribute->collation)
{
if (!type_is_collatable(atttype))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("collations are not supported by type %s",
format_type_be(atttype))));
attcollation = get_collation_oid(attribute->collation, false);
}
collationOidP[attn] = attcollation;
/* /*
* Identify the opclass to use. * Identify the opclass to use.
*/ */

View File

@ -15,6 +15,7 @@
#include "catalog/catalog.h" #include "catalog/catalog.h"
#include "catalog/indexing.h" #include "catalog/indexing.h"
#include "catalog/namespace.h" #include "catalog/namespace.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_seclabel.h" #include "catalog/pg_seclabel.h"
#include "commands/seclabel.h" #include "commands/seclabel.h"
#include "miscadmin.h" #include "miscadmin.h"
@ -194,6 +195,7 @@ GetSecurityLabel(const ObjectAddress *object, const char *provider)
Anum_pg_seclabel_provider, Anum_pg_seclabel_provider,
BTEqualStrategyNumber, F_TEXTEQ, BTEqualStrategyNumber, F_TEXTEQ,
CStringGetTextDatum(provider)); CStringGetTextDatum(provider));
ScanKeyEntryInitializeCollation(&keys[3], DEFAULT_COLLATION_OID);
pg_seclabel = heap_open(SecLabelRelationId, AccessShareLock); pg_seclabel = heap_open(SecLabelRelationId, AccessShareLock);
@ -263,6 +265,7 @@ SetSecurityLabel(const ObjectAddress *object,
Anum_pg_seclabel_provider, Anum_pg_seclabel_provider,
BTEqualStrategyNumber, F_TEXTEQ, BTEqualStrategyNumber, F_TEXTEQ,
CStringGetTextDatum(provider)); CStringGetTextDatum(provider));
ScanKeyEntryInitializeCollation(&keys[3], DEFAULT_COLLATION_OID);
pg_seclabel = heap_open(SecLabelRelationId, RowExclusiveLock); pg_seclabel = heap_open(SecLabelRelationId, RowExclusiveLock);

View File

@ -143,53 +143,53 @@ DefineSequence(CreateSeqStmt *seq)
switch (i) switch (i)
{ {
case SEQ_COL_NAME: case SEQ_COL_NAME:
coldef->typeName = makeTypeNameFromOid(NAMEOID, -1); coldef->typeName = makeTypeNameFromOid(NAMEOID, -1, InvalidOid);
coldef->colname = "sequence_name"; coldef->colname = "sequence_name";
namestrcpy(&name, seq->sequence->relname); namestrcpy(&name, seq->sequence->relname);
value[i - 1] = NameGetDatum(&name); value[i - 1] = NameGetDatum(&name);
break; break;
case SEQ_COL_LASTVAL: case SEQ_COL_LASTVAL:
coldef->typeName = makeTypeNameFromOid(INT8OID, -1); coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid);
coldef->colname = "last_value"; coldef->colname = "last_value";
value[i - 1] = Int64GetDatumFast(new.last_value); value[i - 1] = Int64GetDatumFast(new.last_value);
break; break;
case SEQ_COL_STARTVAL: case SEQ_COL_STARTVAL:
coldef->typeName = makeTypeNameFromOid(INT8OID, -1); coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid);
coldef->colname = "start_value"; coldef->colname = "start_value";
value[i - 1] = Int64GetDatumFast(new.start_value); value[i - 1] = Int64GetDatumFast(new.start_value);
break; break;
case SEQ_COL_INCBY: case SEQ_COL_INCBY:
coldef->typeName = makeTypeNameFromOid(INT8OID, -1); coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid);
coldef->colname = "increment_by"; coldef->colname = "increment_by";
value[i - 1] = Int64GetDatumFast(new.increment_by); value[i - 1] = Int64GetDatumFast(new.increment_by);
break; break;
case SEQ_COL_MAXVALUE: case SEQ_COL_MAXVALUE:
coldef->typeName = makeTypeNameFromOid(INT8OID, -1); coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid);
coldef->colname = "max_value"; coldef->colname = "max_value";
value[i - 1] = Int64GetDatumFast(new.max_value); value[i - 1] = Int64GetDatumFast(new.max_value);
break; break;
case SEQ_COL_MINVALUE: case SEQ_COL_MINVALUE:
coldef->typeName = makeTypeNameFromOid(INT8OID, -1); coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid);
coldef->colname = "min_value"; coldef->colname = "min_value";
value[i - 1] = Int64GetDatumFast(new.min_value); value[i - 1] = Int64GetDatumFast(new.min_value);
break; break;
case SEQ_COL_CACHE: case SEQ_COL_CACHE:
coldef->typeName = makeTypeNameFromOid(INT8OID, -1); coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid);
coldef->colname = "cache_value"; coldef->colname = "cache_value";
value[i - 1] = Int64GetDatumFast(new.cache_value); value[i - 1] = Int64GetDatumFast(new.cache_value);
break; break;
case SEQ_COL_LOG: case SEQ_COL_LOG:
coldef->typeName = makeTypeNameFromOid(INT8OID, -1); coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid);
coldef->colname = "log_cnt"; coldef->colname = "log_cnt";
value[i - 1] = Int64GetDatum((int64) 1); value[i - 1] = Int64GetDatum((int64) 1);
break; break;
case SEQ_COL_CYCLE: case SEQ_COL_CYCLE:
coldef->typeName = makeTypeNameFromOid(BOOLOID, -1); coldef->typeName = makeTypeNameFromOid(BOOLOID, -1, InvalidOid);
coldef->colname = "is_cycled"; coldef->colname = "is_cycled";
value[i - 1] = BoolGetDatum(new.is_cycled); value[i - 1] = BoolGetDatum(new.is_cycled);
break; break;
case SEQ_COL_CALLED: case SEQ_COL_CALLED:
coldef->typeName = makeTypeNameFromOid(BOOLOID, -1); coldef->typeName = makeTypeNameFromOid(BOOLOID, -1, InvalidOid);
coldef->colname = "is_called"; coldef->colname = "is_called";
value[i - 1] = BoolGetDatum(false); value[i - 1] = BoolGetDatum(false);
break; break;

View File

@ -1422,6 +1422,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
{ {
Oid defTypeId; Oid defTypeId;
int32 deftypmod; int32 deftypmod;
Oid defCollId;
/* /*
* Yes, try to merge the two column definitions. They must * 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\"", (errmsg("merging multiple inherited definitions of column \"%s\"",
attributeName))); attributeName)));
def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1); 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 || if (defTypeId != attribute->atttypid ||
deftypmod != attribute->atttypmod) deftypmod != attribute->atttypmod)
ereport(ERROR, ereport(ERROR,
@ -1441,6 +1442,14 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
errdetail("%s versus %s", errdetail("%s versus %s",
TypeNameToString(def->typeName), TypeNameToString(def->typeName),
format_type_be(attribute->atttypid)))); 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 */ /* Copy storage parameter */
if (def->storage == 0) if (def->storage == 0)
@ -1468,7 +1477,8 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
def = makeNode(ColumnDef); def = makeNode(ColumnDef);
def->colname = pstrdup(attributeName); def->colname = pstrdup(attributeName);
def->typeName = makeTypeNameFromOid(attribute->atttypid, def->typeName = makeTypeNameFromOid(attribute->atttypid,
attribute->atttypmod); attribute->atttypmod,
attribute->attcollation);
def->inhcount = 1; def->inhcount = 1;
def->is_local = false; def->is_local = false;
def->is_not_null = attribute->attnotnull; def->is_not_null = attribute->attnotnull;
@ -1594,6 +1604,8 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
newTypeId; newTypeId;
int32 deftypmod, int32 deftypmod,
newtypmod; newtypmod;
Oid defcollid,
newcollid;
/* /*
* Yes, try to merge the two column definitions. They must * 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", (errmsg("merging column \"%s\" with inherited definition",
attributeName))); attributeName)));
def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1); def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod); typenameTypeIdModColl(NULL, def->typeName, &defTypeId, &deftypmod, &defcollid);
typenameTypeIdAndMod(NULL, newdef->typeName, &newTypeId, &newtypmod); typenameTypeIdModColl(NULL, newdef->typeName, &newTypeId, &newtypmod, &newcollid);
if (defTypeId != newTypeId || deftypmod != newtypmod) if (defTypeId != newTypeId || deftypmod != newtypmod)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH), (errcode(ERRCODE_DATATYPE_MISMATCH),
@ -1613,6 +1625,14 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
errdetail("%s versus %s", errdetail("%s versus %s",
TypeNameToString(def->typeName), TypeNameToString(def->typeName),
TypeNameToString(newdef->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 */ /* Copy storage parameter */
if (def->storage == 0) if (def->storage == 0)
@ -4065,6 +4085,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
HeapTuple typeTuple; HeapTuple typeTuple;
Oid typeOid; Oid typeOid;
int32 typmod; int32 typmod;
Oid collOid;
Form_pg_type tform; Form_pg_type tform;
Expr *defval; Expr *defval;
@ -4085,15 +4106,24 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple); Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
Oid ctypeId; Oid ctypeId;
int32 ctypmod; int32 ctypmod;
Oid ccollid;
/* Child column must match by type */ /* Child column must match by type */
typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod); typenameTypeIdModColl(NULL, colDef->typeName, &ctypeId, &ctypmod, &ccollid);
if (ctypeId != childatt->atttypid || if (ctypeId != childatt->atttypid ||
ctypmod != childatt->atttypmod) ctypmod != childatt->atttypmod)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH), (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("child table \"%s\" has different type for column \"%s\"", errmsg("child table \"%s\" has different type for column \"%s\"",
RelationGetRelationName(rel), colDef->colname))); 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 it's OID, child column must actually be OID */
if (isOid && childatt->attnum != ObjectIdAttributeNumber) if (isOid && childatt->attnum != ObjectIdAttributeNumber)
@ -4151,7 +4181,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
MaxHeapAttributeNumber))); MaxHeapAttributeNumber)));
} }
typeTuple = typenameType(NULL, colDef->typeName, &typmod); typeTuple = typenameType(NULL, colDef->typeName, &typmod, &collOid);
tform = (Form_pg_type) GETSTRUCT(typeTuple); tform = (Form_pg_type) GETSTRUCT(typeTuple);
typeOid = HeapTupleGetOid(typeTuple); typeOid = HeapTupleGetOid(typeTuple);
@ -4176,6 +4206,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
attribute.attisdropped = false; attribute.attisdropped = false;
attribute.attislocal = colDef->is_local; attribute.attislocal = colDef->is_local;
attribute.attinhcount = colDef->inhcount; attribute.attinhcount = colDef->inhcount;
attribute.attcollation = collOid;
/* attribute.attacl is handled by InsertPgAttributeTuple */ /* attribute.attacl is handled by InsertPgAttributeTuple */
ReleaseSysCache(typeTuple); ReleaseSysCache(typeTuple);
@ -4353,7 +4384,7 @@ ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd, LOC
ColumnDef *cdef = makeNode(ColumnDef); ColumnDef *cdef = makeNode(ColumnDef);
cdef->colname = pstrdup("oid"); cdef->colname = pstrdup("oid");
cdef->typeName = makeTypeNameFromOid(OIDOID, -1); cdef->typeName = makeTypeNameFromOid(OIDOID, -1, InvalidOid);
cdef->inhcount = 0; cdef->inhcount = 0;
cdef->is_local = true; cdef->is_local = true;
cdef->is_not_null = true; cdef->is_not_null = true;
@ -6415,6 +6446,7 @@ ATPrepAlterColumnType(List **wqueue,
AttrNumber attnum; AttrNumber attnum;
Oid targettype; Oid targettype;
int32 targettypmod; int32 targettypmod;
Oid targetcollid;
Node *transform; Node *transform;
NewColumnValue *newval; NewColumnValue *newval;
ParseState *pstate = make_parsestate(NULL); ParseState *pstate = make_parsestate(NULL);
@ -6449,7 +6481,7 @@ ATPrepAlterColumnType(List **wqueue,
colName))); colName)));
/* Look up the target type */ /* Look up the target type */
typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod); typenameTypeIdModColl(NULL, typeName, &targettype, &targettypmod, &targetcollid);
/* make sure datatype is legal for a column */ /* make sure datatype is legal for a column */
CheckAttributeType(colName, targettype, false); CheckAttributeType(colName, targettype, false);
@ -6501,7 +6533,7 @@ ATPrepAlterColumnType(List **wqueue,
else else
{ {
transform = (Node *) makeVar(1, attnum, transform = (Node *) makeVar(1, attnum,
attTup->atttypid, attTup->atttypmod, attTup->atttypid, attTup->atttypmod, attTup->attcollation,
0); 0);
} }
@ -6578,6 +6610,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
Form_pg_type tform; Form_pg_type tform;
Oid targettype; Oid targettype;
int32 targettypmod; int32 targettypmod;
Oid targetcollid;
Node *defaultexpr; Node *defaultexpr;
Relation attrelation; Relation attrelation;
Relation depRel; Relation depRel;
@ -6606,7 +6639,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
colName))); colName)));
/* Look up the target type (should not fail, since prep found it) */ /* 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); tform = (Form_pg_type) GETSTRUCT(typeTuple);
targettype = HeapTupleGetOid(typeTuple); targettype = HeapTupleGetOid(typeTuple);
@ -6880,6 +6913,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
*/ */
attTup->atttypid = targettype; attTup->atttypid = targettype;
attTup->atttypmod = targettypmod; attTup->atttypmod = targettypmod;
attTup->attcollation = targetcollid;
attTup->attndims = list_length(typeName->arrayBounds); attTup->attndims = list_length(typeName->arrayBounds);
attTup->attlen = tform->typlen; attTup->attlen = tform->typlen;
attTup->attbyval = tform->typbyval; attTup->attbyval = tform->typbyval;

View File

@ -38,6 +38,7 @@
#include "catalog/dependency.h" #include "catalog/dependency.h"
#include "catalog/heap.h" #include "catalog/heap.h"
#include "catalog/indexing.h" #include "catalog/indexing.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h" #include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h" #include "catalog/pg_depend.h"
#include "catalog/pg_enum.h" #include "catalog/pg_enum.h"
@ -118,6 +119,7 @@ DefineType(List *names, List *parameters)
bool byValue = false; bool byValue = false;
char alignment = 'i'; /* default alignment */ char alignment = 'i'; /* default alignment */
char storage = 'p'; /* default TOAST storage method */ char storage = 'p'; /* default TOAST storage method */
Oid collation = InvalidOid;
DefElem *likeTypeEl = NULL; DefElem *likeTypeEl = NULL;
DefElem *internalLengthEl = NULL; DefElem *internalLengthEl = NULL;
DefElem *inputNameEl = NULL; DefElem *inputNameEl = NULL;
@ -135,6 +137,7 @@ DefineType(List *names, List *parameters)
DefElem *byValueEl = NULL; DefElem *byValueEl = NULL;
DefElem *alignmentEl = NULL; DefElem *alignmentEl = NULL;
DefElem *storageEl = NULL; DefElem *storageEl = NULL;
DefElem *collatableEl = NULL;
Oid inputOid; Oid inputOid;
Oid outputOid; Oid outputOid;
Oid receiveOid = InvalidOid; Oid receiveOid = InvalidOid;
@ -261,6 +264,8 @@ DefineType(List *names, List *parameters)
defelp = &alignmentEl; defelp = &alignmentEl;
else if (pg_strcasecmp(defel->defname, "storage") == 0) else if (pg_strcasecmp(defel->defname, "storage") == 0)
defelp = &storageEl; defelp = &storageEl;
else if (pg_strcasecmp(defel->defname, "collatable") == 0)
defelp = &collatableEl;
else else
{ {
/* WARNING, not ERROR, for historical backwards-compatibility */ /* WARNING, not ERROR, for historical backwards-compatibility */
@ -287,7 +292,7 @@ DefineType(List *names, List *parameters)
Type likeType; Type likeType;
Form_pg_type likeForm; Form_pg_type likeForm;
likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL); likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL, NULL);
likeForm = (Form_pg_type) GETSTRUCT(likeType); likeForm = (Form_pg_type) GETSTRUCT(likeType);
internalLength = likeForm->typlen; internalLength = likeForm->typlen;
byValue = likeForm->typbyval; byValue = likeForm->typbyval;
@ -390,6 +395,8 @@ DefineType(List *names, List *parameters)
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("storage \"%s\" not recognized", a))); errmsg("storage \"%s\" not recognized", a)));
} }
if (collatableEl)
collation = defGetBoolean(collatableEl) ? DEFAULT_COLLATION_OID : InvalidOid;
/* /*
* make sure we have our required definitions * make sure we have our required definitions
@ -562,7 +569,8 @@ DefineType(List *names, List *parameters)
storage, /* TOAST strategy */ storage, /* TOAST strategy */
-1, /* typMod (Domains only) */ -1, /* typMod (Domains only) */
0, /* Array Dimensions of typbasetype */ 0, /* Array Dimensions of typbasetype */
false); /* Type NOT NULL */ false, /* Type NOT NULL */
collation);
/* /*
* Create the array type that goes with it. * Create the array type that goes with it.
@ -601,7 +609,8 @@ DefineType(List *names, List *parameters)
'x', /* ARRAY is always toastable */ 'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */ -1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */ 0, /* Array dimensions of typbasetype */
false); /* Type NOT NULL */ false, /* Type NOT NULL */
collation);
pfree(array_type); pfree(array_type);
} }
@ -640,7 +649,7 @@ RemoveTypes(DropStmt *drop)
typename = makeTypeNameFromNameList(names); typename = makeTypeNameFromNameList(names);
/* Use LookupTypeName here so that shell types can be removed. */ /* 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 (tup == NULL)
{ {
if (!drop->missing_ok) if (!drop->missing_ok)
@ -767,6 +776,7 @@ DefineDomain(CreateDomainStmt *stmt)
Oid old_type_oid; Oid old_type_oid;
Form_pg_type baseType; Form_pg_type baseType;
int32 basetypeMod; int32 basetypeMod;
Oid baseColl;
/* Convert list of names to a name and namespace */ /* Convert list of names to a name and namespace */
domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname, domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
@ -797,7 +807,7 @@ DefineDomain(CreateDomainStmt *stmt)
/* /*
* Look up the base type. * Look up the base type.
*/ */
typeTup = typenameType(NULL, stmt->typeName, &basetypeMod); typeTup = typenameType(NULL, stmt->typeName, &basetypeMod, &baseColl);
baseType = (Form_pg_type) GETSTRUCT(typeTup); baseType = (Form_pg_type) GETSTRUCT(typeTup);
basetypeoid = HeapTupleGetOid(typeTup); basetypeoid = HeapTupleGetOid(typeTup);
@ -1040,7 +1050,8 @@ DefineDomain(CreateDomainStmt *stmt)
storage, /* TOAST strategy */ storage, /* TOAST strategy */
basetypeMod, /* typeMod value */ basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */ 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 * Process constraints which refer to the domain ID returned by TypeCreate
@ -1149,7 +1160,8 @@ DefineEnum(CreateEnumStmt *stmt)
'p', /* TOAST strategy always plain */ 'p', /* TOAST strategy always plain */
-1, /* typMod (Domains only) */ -1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */ 0, /* Array dimensions of typbasetype */
false); /* Type NOT NULL */ false, /* Type NOT NULL */
InvalidOid); /* typcollation */
/* Enter the enum's values into pg_enum */ /* Enter the enum's values into pg_enum */
EnumValuesCreate(enumTypeOid, stmt->vals); EnumValuesCreate(enumTypeOid, stmt->vals);
@ -1188,7 +1200,8 @@ DefineEnum(CreateEnumStmt *stmt)
'x', /* ARRAY is always toastable */ 'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */ -1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */ 0, /* Array dimensions of typbasetype */
false); /* Type NOT NULL */ false, /* Type NOT NULL */
InvalidOid); /* typcollation */
pfree(enumArrayName); pfree(enumArrayName);
} }
@ -2615,7 +2628,7 @@ AlterTypeOwner(List *names, Oid newOwnerId)
typename = makeTypeNameFromNameList(names); typename = makeTypeNameFromNameList(names);
/* Use LookupTypeName here so that shell types can be processed */ /* Use LookupTypeName here so that shell types can be processed */
tup = LookupTypeName(NULL, typename, NULL); tup = LookupTypeName(NULL, typename, NULL, NULL);
if (tup == NULL) if (tup == NULL)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT), (errcode(ERRCODE_UNDEFINED_OBJECT),

View File

@ -120,7 +120,8 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
def->colname = pstrdup(tle->resname); def->colname = pstrdup(tle->resname);
def->typeName = makeTypeNameFromOid(exprType((Node *) tle->expr), def->typeName = makeTypeNameFromOid(exprType((Node *) tle->expr),
exprTypmod((Node *) tle->expr)); exprTypmod((Node *) tle->expr),
exprCollation((Node *) tle->expr));
def->inhcount = 0; def->inhcount = 0;
def->is_local = true; def->is_local = true;
def->is_not_null = false; def->is_not_null = false;

View File

@ -166,6 +166,9 @@ static Datum ExecEvalFieldStore(FieldStoreState *fstate,
static Datum ExecEvalRelabelType(GenericExprState *exprstate, static Datum ExecEvalRelabelType(GenericExprState *exprstate,
ExprContext *econtext, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone); bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalCollateClause(GenericExprState *exprstate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalCoerceViaIO(CoerceViaIOState *iostate, static Datum ExecEvalCoerceViaIO(CoerceViaIOState *iostate,
ExprContext *econtext, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone); bool *isNull, ExprDoneCond *isDone);
@ -1202,7 +1205,7 @@ init_fcache(Oid foid, FuncExprState *fcache,
/* Set up the primary fmgr lookup information */ /* Set up the primary fmgr lookup information */
fmgr_info_cxt(foid, &(fcache->func), fcacheCxt); 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 */ /* Initialize the function call parameter struct as well */
InitFunctionCallInfoData(fcache->fcinfo_data, &(fcache->func), InitFunctionCallInfoData(fcache->fcinfo_data, &(fcache->func),
@ -4025,6 +4028,20 @@ ExecEvalRelabelType(GenericExprState *exprstate,
return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone); return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone);
} }
/* ----------------------------------------------------------------
* ExecEvalCollateClause
*
* Evaluate a CollateClause node.
* ----------------------------------------------------------------
*/
static Datum
ExecEvalCollateClause(GenericExprState *exprstate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone)
{
return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone);
}
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecEvalCoerceViaIO * ExecEvalCoerceViaIO
* *
@ -4114,7 +4131,7 @@ ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
econtext->ecxt_per_query_memory); econtext->ecxt_per_query_memory);
/* Initialize additional info */ /* 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; state = (ExprState *) gstate;
} }
break; 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: case T_CoerceViaIO:
{ {
CoerceViaIO *iocoerce = (CoerceViaIO *) node; CoerceViaIO *iocoerce = (CoerceViaIO *) node;
@ -4657,6 +4684,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
List *outlist; List *outlist;
ListCell *l; ListCell *l;
ListCell *l2; ListCell *l2;
ListCell *l3;
int i; int i;
rstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRowCompare; rstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRowCompare;
@ -4685,10 +4713,11 @@ ExecInitExpr(Expr *node, PlanState *parent)
Assert(list_length(rcexpr->opfamilies) == nopers); Assert(list_length(rcexpr->opfamilies) == nopers);
rstate->funcs = (FmgrInfo *) palloc(nopers * sizeof(FmgrInfo)); rstate->funcs = (FmgrInfo *) palloc(nopers * sizeof(FmgrInfo));
i = 0; 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 opno = lfirst_oid(l);
Oid opfamily = lfirst_oid(l2); Oid opfamily = lfirst_oid(l2);
Oid collid = lfirst_oid(l3);
int strategy; int strategy;
Oid lefttype; Oid lefttype;
Oid righttype; Oid righttype;
@ -4710,6 +4739,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
* does this code. * does this code.
*/ */
fmgr_info(proc, &(rstate->funcs[i])); fmgr_info(proc, &(rstate->funcs[i]));
fmgr_info_collation(collid, &(rstate->funcs[i]));
i++; i++;
} }
state = (ExprState *) rstate; state = (ExprState *) rstate;
@ -4769,6 +4799,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
* code. * code.
*/ */
fmgr_info(typentry->cmp_proc, &(mstate->cfunc)); fmgr_info(typentry->cmp_proc, &(mstate->cfunc));
fmgr_info_collation(minmaxexpr->collid, &(mstate->cfunc));
state = (ExprState *) mstate; state = (ExprState *) mstate;
} }
break; break;

View File

@ -937,11 +937,15 @@ ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk)
if (skipjunk && tle->resjunk) if (skipjunk && tle->resjunk)
continue; continue;
TupleDescInitEntry(typeInfo, TupleDescInitEntry(typeInfo,
cur_resno++, cur_resno,
tle->resname, tle->resname,
exprType((Node *) tle->expr), exprType((Node *) tle->expr),
exprTypmod((Node *) tle->expr), exprTypmod((Node *) tle->expr),
0); 0);
TupleDescInitEntryCollation(typeInfo,
cur_resno,
exprCollation((Node *) tle->expr));
cur_resno++;
} }
return typeInfo; return typeInfo;
@ -969,11 +973,15 @@ ExecTypeFromExprList(List *exprList)
sprintf(fldname, "f%d", cur_resno); sprintf(fldname, "f%d", cur_resno);
TupleDescInitEntry(typeInfo, TupleDescInitEntry(typeInfo,
cur_resno++, cur_resno,
fldname, fldname,
exprType(e), exprType(e),
exprTypmod(e), exprTypmod(e),
0); 0);
TupleDescInitEntryCollation(typeInfo,
cur_resno,
exprCollation(e));
cur_resno++;
} }
return typeInfo; return typeInfo;

View File

@ -140,6 +140,7 @@ typedef struct AggStatePerAggData
/* deconstructed sorting information (arrays of length numSortCols) */ /* deconstructed sorting information (arrays of length numSortCols) */
AttrNumber *sortColIdx; AttrNumber *sortColIdx;
Oid *sortOperators; Oid *sortOperators;
Oid *sortCollations;
bool *sortNullsFirst; bool *sortNullsFirst;
/* /*
@ -315,12 +316,14 @@ initialize_aggregates(AggState *aggstate,
(peraggstate->numInputs == 1) ? (peraggstate->numInputs == 1) ?
tuplesort_begin_datum(peraggstate->evaldesc->attrs[0]->atttypid, tuplesort_begin_datum(peraggstate->evaldesc->attrs[0]->atttypid,
peraggstate->sortOperators[0], peraggstate->sortOperators[0],
peraggstate->sortCollations[0],
peraggstate->sortNullsFirst[0], peraggstate->sortNullsFirst[0],
work_mem, false) : work_mem, false) :
tuplesort_begin_heap(peraggstate->evaldesc, tuplesort_begin_heap(peraggstate->evaldesc,
peraggstate->numSortCols, peraggstate->numSortCols,
peraggstate->sortColIdx, peraggstate->sortColIdx,
peraggstate->sortOperators, peraggstate->sortOperators,
peraggstate->sortCollations,
peraggstate->sortNullsFirst, peraggstate->sortNullsFirst,
work_mem, false); work_mem, false);
} }
@ -1668,16 +1671,17 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
aggref->aggtype, aggref->aggtype,
transfn_oid, transfn_oid,
finalfn_oid, finalfn_oid,
aggref->collid,
&transfnexpr, &transfnexpr,
&finalfnexpr); &finalfnexpr);
fmgr_info(transfn_oid, &peraggstate->transfn); fmgr_info(transfn_oid, &peraggstate->transfn);
peraggstate->transfn.fn_expr = (Node *) transfnexpr; fmgr_info_expr((Node *) transfnexpr, &peraggstate->transfn);
if (OidIsValid(finalfn_oid)) if (OidIsValid(finalfn_oid))
{ {
fmgr_info(finalfn_oid, &peraggstate->finalfn); fmgr_info(finalfn_oid, &peraggstate->finalfn);
peraggstate->finalfn.fn_expr = (Node *) finalfnexpr; fmgr_info_expr((Node *) finalfnexpr, &peraggstate->finalfn);
} }
get_typlenbyval(aggref->aggtype, get_typlenbyval(aggref->aggtype,
@ -1786,6 +1790,8 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
(AttrNumber *) palloc(numSortCols * sizeof(AttrNumber)); (AttrNumber *) palloc(numSortCols * sizeof(AttrNumber));
peraggstate->sortOperators = peraggstate->sortOperators =
(Oid *) palloc(numSortCols * sizeof(Oid)); (Oid *) palloc(numSortCols * sizeof(Oid));
peraggstate->sortCollations =
(Oid *) palloc(numSortCols * sizeof(Oid));
peraggstate->sortNullsFirst = peraggstate->sortNullsFirst =
(bool *) palloc(numSortCols * sizeof(bool)); (bool *) palloc(numSortCols * sizeof(bool));
@ -1801,6 +1807,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
peraggstate->sortColIdx[i] = tle->resno; peraggstate->sortColIdx[i] = tle->resno;
peraggstate->sortOperators[i] = sortcl->sortop; peraggstate->sortOperators[i] = sortcl->sortop;
peraggstate->sortCollations[i] = exprCollation((Node *) tle->expr);
peraggstate->sortNullsFirst[i] = sortcl->nulls_first; peraggstate->sortNullsFirst[i] = sortcl->nulls_first;
i++; i++;
} }

View File

@ -24,6 +24,7 @@
#include "executor/nodeFunctionscan.h" #include "executor/nodeFunctionscan.h"
#include "funcapi.h" #include "funcapi.h"
#include "nodes/nodeFuncs.h"
#include "utils/builtins.h" #include "utils/builtins.h"
@ -185,12 +186,16 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
funcrettype, funcrettype,
-1, -1,
0); 0);
TupleDescInitEntryCollation(tupdesc,
(AttrNumber) 1,
exprCollation(node->funcexpr));
} }
else if (functypclass == TYPEFUNC_RECORD) else if (functypclass == TYPEFUNC_RECORD)
{ {
tupdesc = BuildDescFromLists(node->funccolnames, tupdesc = BuildDescFromLists(node->funccolnames,
node->funccoltypes, node->funccoltypes,
node->funccoltypmods); node->funccoltypmods,
node->funccolcollations);
} }
else else
{ {

View File

@ -732,6 +732,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
int op_strategy; /* operator's strategy number */ int op_strategy; /* operator's strategy number */
Oid op_lefttype; /* operator's declared input types */ Oid op_lefttype; /* operator's declared input types */
Oid op_righttype; Oid op_righttype;
Oid collation;
Expr *leftop; /* expr on lhs of operator */ Expr *leftop; /* expr on lhs of operator */
Expr *rightop; /* expr on rhs ... */ Expr *rightop; /* expr on rhs ... */
AttrNumber varattno; /* att number used in scan */ AttrNumber varattno; /* att number used in scan */
@ -831,6 +832,8 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
op_righttype, /* strategy subtype */ op_righttype, /* strategy subtype */
opfuncid, /* reg proc to use */ opfuncid, /* reg proc to use */
scanvalue); /* constant */ scanvalue); /* constant */
ScanKeyEntryInitializeCollation(this_scan_key,
((OpExpr *) clause)->collid);
} }
else if (IsA(clause, RowCompareExpr)) else if (IsA(clause, RowCompareExpr))
{ {
@ -839,6 +842,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
ListCell *largs_cell = list_head(rc->largs); ListCell *largs_cell = list_head(rc->largs);
ListCell *rargs_cell = list_head(rc->rargs); ListCell *rargs_cell = list_head(rc->rargs);
ListCell *opnos_cell = list_head(rc->opnos); ListCell *opnos_cell = list_head(rc->opnos);
ListCell *collids_cell = list_head(rc->collids);
ScanKey first_sub_key; ScanKey first_sub_key;
int n_sub_key; int n_sub_key;
@ -897,6 +901,9 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
op_righttype, op_righttype,
BTORDER_PROC); BTORDER_PROC);
collation = lfirst_oid(collids_cell);
collids_cell = lnext(collids_cell);
/* /*
* rightop is the constant or variable comparison value * rightop is the constant or variable comparison value
*/ */
@ -952,6 +959,8 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
op_righttype, /* strategy subtype */ op_righttype, /* strategy subtype */
opfuncid, /* reg proc to use */ opfuncid, /* reg proc to use */
scanvalue); /* constant */ scanvalue); /* constant */
ScanKeyEntryInitializeCollation(this_sub_key,
collation);
n_sub_key++; n_sub_key++;
} }
@ -1035,6 +1044,8 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
op_righttype, /* strategy subtype */ op_righttype, /* strategy subtype */
opfuncid, /* reg proc to use */ opfuncid, /* reg proc to use */
(Datum) 0); /* constant */ (Datum) 0); /* constant */
ScanKeyEntryInitializeCollation(this_scan_key,
saop->collid);
} }
else if (IsA(clause, NullTest)) else if (IsA(clause, NullTest))
{ {

View File

@ -150,6 +150,9 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags)
sortFunction, sortFunction,
(Datum) 0); (Datum) 0);
ScanKeyEntryInitializeCollation(&mergestate->ms_scankeys[i],
node->collations[i]);
/* However, we use btree's conventions for encoding directionality */ /* However, we use btree's conventions for encoding directionality */
if (reverse) if (reverse)
mergestate->ms_scankeys[i].sk_flags |= SK_BT_DESC; mergestate->ms_scankeys[i].sk_flags |= SK_BT_DESC;

View File

@ -180,6 +180,7 @@ typedef enum
static MergeJoinClause static MergeJoinClause
MJExamineQuals(List *mergeclauses, MJExamineQuals(List *mergeclauses,
Oid *mergefamilies, Oid *mergefamilies,
Oid *mergecollations,
int *mergestrategies, int *mergestrategies,
bool *mergenullsfirst, bool *mergenullsfirst,
PlanState *parent) PlanState *parent)
@ -197,6 +198,7 @@ MJExamineQuals(List *mergeclauses,
OpExpr *qual = (OpExpr *) lfirst(cl); OpExpr *qual = (OpExpr *) lfirst(cl);
MergeJoinClause clause = &clauses[iClause]; MergeJoinClause clause = &clauses[iClause];
Oid opfamily = mergefamilies[iClause]; Oid opfamily = mergefamilies[iClause];
Oid collation = mergecollations[iClause];
StrategyNumber opstrategy = mergestrategies[iClause]; StrategyNumber opstrategy = mergestrategies[iClause];
bool nulls_first = mergenullsfirst[iClause]; bool nulls_first = mergenullsfirst[iClause];
int op_strategy; int op_strategy;
@ -240,6 +242,7 @@ MJExamineQuals(List *mergeclauses,
/* Set up the fmgr lookup information */ /* Set up the fmgr lookup information */
fmgr_info(cmpproc, &(clause->cmpfinfo)); fmgr_info(cmpproc, &(clause->cmpfinfo));
fmgr_info_collation(collation, &(clause->cmpfinfo));
/* Fill the additional comparison-strategy flags */ /* Fill the additional comparison-strategy flags */
if (opstrategy == BTLessStrategyNumber) if (opstrategy == BTLessStrategyNumber)
@ -1636,6 +1639,7 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
mergestate->mj_NumClauses = list_length(node->mergeclauses); mergestate->mj_NumClauses = list_length(node->mergeclauses);
mergestate->mj_Clauses = MJExamineQuals(node->mergeclauses, mergestate->mj_Clauses = MJExamineQuals(node->mergeclauses,
node->mergeFamilies, node->mergeFamilies,
node->mergeCollations,
node->mergeStrategies, node->mergeStrategies,
node->mergeNullsFirst, node->mergeNullsFirst,
(PlanState *) mergestate); (PlanState *) mergestate);

View File

@ -86,6 +86,7 @@ ExecSort(SortState *node)
plannode->numCols, plannode->numCols,
plannode->sortColIdx, plannode->sortColIdx,
plannode->sortOperators, plannode->sortOperators,
plannode->collations,
plannode->nullsFirst, plannode->nullsFirst,
work_mem, work_mem,
node->randomAccess); node->randomAccess);

View File

@ -831,7 +831,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
/* Lookup the equality function (potentially cross-type) */ /* Lookup the equality function (potentially cross-type) */
fmgr_info(opexpr->opfuncid, &sstate->cur_eq_funcs[i - 1]); 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 */ /* Look up the equality function for the RHS type */
if (!get_compatible_hash_operators(opexpr->opno, if (!get_compatible_hash_operators(opexpr->opno,

View File

@ -1561,7 +1561,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
fmgr_info_cxt(wfunc->winfnoid, &perfuncstate->flinfo, fmgr_info_cxt(wfunc->winfnoid, &perfuncstate->flinfo,
econtext->ecxt_per_query_memory); econtext->ecxt_per_query_memory);
perfuncstate->flinfo.fn_expr = (Node *) wfunc; fmgr_info_expr((Node *) wfunc, &perfuncstate->flinfo);
get_typlenbyval(wfunc->wintype, get_typlenbyval(wfunc->wintype,
&perfuncstate->resulttypeLen, &perfuncstate->resulttypeLen,
&perfuncstate->resulttypeByVal); &perfuncstate->resulttypeByVal);
@ -1794,16 +1794,17 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
wfunc->wintype, wfunc->wintype,
transfn_oid, transfn_oid,
finalfn_oid, finalfn_oid,
wfunc->collid,
&transfnexpr, &transfnexpr,
&finalfnexpr); &finalfnexpr);
fmgr_info(transfn_oid, &peraggstate->transfn); fmgr_info(transfn_oid, &peraggstate->transfn);
peraggstate->transfn.fn_expr = (Node *) transfnexpr; fmgr_info_expr((Node *) transfnexpr, &peraggstate->transfn);
if (OidIsValid(finalfn_oid)) if (OidIsValid(finalfn_oid))
{ {
fmgr_info(finalfn_oid, &peraggstate->finalfn); fmgr_info(finalfn_oid, &peraggstate->finalfn);
peraggstate->finalfn.fn_expr = (Node *) finalfnexpr; fmgr_info_expr((Node *) finalfnexpr, &peraggstate->finalfn);
} }
get_typlenbyval(wfunc->wintype, get_typlenbyval(wfunc->wintype,

View File

@ -223,6 +223,7 @@ _copyMergeAppend(MergeAppend *from)
COPY_SCALAR_FIELD(numCols); COPY_SCALAR_FIELD(numCols);
COPY_POINTER_FIELD(sortColIdx, from->numCols * sizeof(AttrNumber)); COPY_POINTER_FIELD(sortColIdx, from->numCols * sizeof(AttrNumber));
COPY_POINTER_FIELD(sortOperators, from->numCols * sizeof(Oid)); COPY_POINTER_FIELD(sortOperators, from->numCols * sizeof(Oid));
COPY_POINTER_FIELD(collations, from->numCols * sizeof(Oid));
COPY_POINTER_FIELD(nullsFirst, from->numCols * sizeof(bool)); COPY_POINTER_FIELD(nullsFirst, from->numCols * sizeof(bool));
return newnode; return newnode;
@ -479,6 +480,7 @@ _copyFunctionScan(FunctionScan *from)
COPY_NODE_FIELD(funccolnames); COPY_NODE_FIELD(funccolnames);
COPY_NODE_FIELD(funccoltypes); COPY_NODE_FIELD(funccoltypes);
COPY_NODE_FIELD(funccoltypmods); COPY_NODE_FIELD(funccoltypmods);
COPY_NODE_FIELD(funccolcollations);
return newnode; return newnode;
} }
@ -622,6 +624,7 @@ _copyMergeJoin(MergeJoin *from)
COPY_NODE_FIELD(mergeclauses); COPY_NODE_FIELD(mergeclauses);
numCols = list_length(from->mergeclauses); numCols = list_length(from->mergeclauses);
COPY_POINTER_FIELD(mergeFamilies, numCols * sizeof(Oid)); COPY_POINTER_FIELD(mergeFamilies, numCols * sizeof(Oid));
COPY_POINTER_FIELD(mergeCollations, numCols * sizeof(Oid));
COPY_POINTER_FIELD(mergeStrategies, numCols * sizeof(int)); COPY_POINTER_FIELD(mergeStrategies, numCols * sizeof(int));
COPY_POINTER_FIELD(mergeNullsFirst, numCols * sizeof(bool)); COPY_POINTER_FIELD(mergeNullsFirst, numCols * sizeof(bool));
@ -683,6 +686,7 @@ _copySort(Sort *from)
COPY_SCALAR_FIELD(numCols); COPY_SCALAR_FIELD(numCols);
COPY_POINTER_FIELD(sortColIdx, from->numCols * sizeof(AttrNumber)); COPY_POINTER_FIELD(sortColIdx, from->numCols * sizeof(AttrNumber));
COPY_POINTER_FIELD(sortOperators, from->numCols * sizeof(Oid)); COPY_POINTER_FIELD(sortOperators, from->numCols * sizeof(Oid));
COPY_POINTER_FIELD(collations, from->numCols * sizeof(Oid));
COPY_POINTER_FIELD(nullsFirst, from->numCols * sizeof(bool)); COPY_POINTER_FIELD(nullsFirst, from->numCols * sizeof(bool));
return newnode; return newnode;
@ -998,6 +1002,7 @@ _copyVar(Var *from)
COPY_SCALAR_FIELD(varattno); COPY_SCALAR_FIELD(varattno);
COPY_SCALAR_FIELD(vartype); COPY_SCALAR_FIELD(vartype);
COPY_SCALAR_FIELD(vartypmod); COPY_SCALAR_FIELD(vartypmod);
COPY_SCALAR_FIELD(varcollid);
COPY_SCALAR_FIELD(varlevelsup); COPY_SCALAR_FIELD(varlevelsup);
COPY_SCALAR_FIELD(varnoold); COPY_SCALAR_FIELD(varnoold);
COPY_SCALAR_FIELD(varoattno); COPY_SCALAR_FIELD(varoattno);
@ -1016,6 +1021,7 @@ _copyConst(Const *from)
COPY_SCALAR_FIELD(consttype); COPY_SCALAR_FIELD(consttype);
COPY_SCALAR_FIELD(consttypmod); COPY_SCALAR_FIELD(consttypmod);
COPY_SCALAR_FIELD(constcollid);
COPY_SCALAR_FIELD(constlen); COPY_SCALAR_FIELD(constlen);
if (from->constbyval || from->constisnull) if (from->constbyval || from->constisnull)
@ -1055,6 +1061,7 @@ _copyParam(Param *from)
COPY_SCALAR_FIELD(paramid); COPY_SCALAR_FIELD(paramid);
COPY_SCALAR_FIELD(paramtype); COPY_SCALAR_FIELD(paramtype);
COPY_SCALAR_FIELD(paramtypmod); COPY_SCALAR_FIELD(paramtypmod);
COPY_SCALAR_FIELD(paramcollation);
COPY_LOCATION_FIELD(location); COPY_LOCATION_FIELD(location);
return newnode; return newnode;
@ -1075,6 +1082,7 @@ _copyAggref(Aggref *from)
COPY_NODE_FIELD(aggdistinct); COPY_NODE_FIELD(aggdistinct);
COPY_SCALAR_FIELD(aggstar); COPY_SCALAR_FIELD(aggstar);
COPY_SCALAR_FIELD(agglevelsup); COPY_SCALAR_FIELD(agglevelsup);
COPY_SCALAR_FIELD(collid);
COPY_LOCATION_FIELD(location); COPY_LOCATION_FIELD(location);
return newnode; return newnode;
@ -1094,6 +1102,7 @@ _copyWindowFunc(WindowFunc *from)
COPY_SCALAR_FIELD(winref); COPY_SCALAR_FIELD(winref);
COPY_SCALAR_FIELD(winstar); COPY_SCALAR_FIELD(winstar);
COPY_SCALAR_FIELD(winagg); COPY_SCALAR_FIELD(winagg);
COPY_SCALAR_FIELD(collid);
COPY_LOCATION_FIELD(location); COPY_LOCATION_FIELD(location);
return newnode; return newnode;
@ -1110,6 +1119,7 @@ _copyArrayRef(ArrayRef *from)
COPY_SCALAR_FIELD(refarraytype); COPY_SCALAR_FIELD(refarraytype);
COPY_SCALAR_FIELD(refelemtype); COPY_SCALAR_FIELD(refelemtype);
COPY_SCALAR_FIELD(reftypmod); COPY_SCALAR_FIELD(reftypmod);
COPY_SCALAR_FIELD(refcollid);
COPY_NODE_FIELD(refupperindexpr); COPY_NODE_FIELD(refupperindexpr);
COPY_NODE_FIELD(reflowerindexpr); COPY_NODE_FIELD(reflowerindexpr);
COPY_NODE_FIELD(refexpr); COPY_NODE_FIELD(refexpr);
@ -1131,6 +1141,7 @@ _copyFuncExpr(FuncExpr *from)
COPY_SCALAR_FIELD(funcretset); COPY_SCALAR_FIELD(funcretset);
COPY_SCALAR_FIELD(funcformat); COPY_SCALAR_FIELD(funcformat);
COPY_NODE_FIELD(args); COPY_NODE_FIELD(args);
COPY_SCALAR_FIELD(collid);
COPY_LOCATION_FIELD(location); COPY_LOCATION_FIELD(location);
return newnode; return newnode;
@ -1165,6 +1176,7 @@ _copyOpExpr(OpExpr *from)
COPY_SCALAR_FIELD(opresulttype); COPY_SCALAR_FIELD(opresulttype);
COPY_SCALAR_FIELD(opretset); COPY_SCALAR_FIELD(opretset);
COPY_NODE_FIELD(args); COPY_NODE_FIELD(args);
COPY_SCALAR_FIELD(collid);
COPY_LOCATION_FIELD(location); COPY_LOCATION_FIELD(location);
return newnode; return newnode;
@ -1183,6 +1195,7 @@ _copyDistinctExpr(DistinctExpr *from)
COPY_SCALAR_FIELD(opresulttype); COPY_SCALAR_FIELD(opresulttype);
COPY_SCALAR_FIELD(opretset); COPY_SCALAR_FIELD(opretset);
COPY_NODE_FIELD(args); COPY_NODE_FIELD(args);
COPY_SCALAR_FIELD(collid);
COPY_LOCATION_FIELD(location); COPY_LOCATION_FIELD(location);
return newnode; return newnode;
@ -1200,6 +1213,7 @@ _copyScalarArrayOpExpr(ScalarArrayOpExpr *from)
COPY_SCALAR_FIELD(opfuncid); COPY_SCALAR_FIELD(opfuncid);
COPY_SCALAR_FIELD(useOr); COPY_SCALAR_FIELD(useOr);
COPY_NODE_FIELD(args); COPY_NODE_FIELD(args);
COPY_SCALAR_FIELD(collid);
COPY_LOCATION_FIELD(location); COPY_LOCATION_FIELD(location);
return newnode; return newnode;
@ -1252,6 +1266,7 @@ _copySubPlan(SubPlan *from)
COPY_STRING_FIELD(plan_name); COPY_STRING_FIELD(plan_name);
COPY_SCALAR_FIELD(firstColType); COPY_SCALAR_FIELD(firstColType);
COPY_SCALAR_FIELD(firstColTypmod); COPY_SCALAR_FIELD(firstColTypmod);
COPY_SCALAR_FIELD(firstColCollation);
COPY_SCALAR_FIELD(useHashTable); COPY_SCALAR_FIELD(useHashTable);
COPY_SCALAR_FIELD(unknownEqFalse); COPY_SCALAR_FIELD(unknownEqFalse);
COPY_NODE_FIELD(setParam); COPY_NODE_FIELD(setParam);
@ -1288,6 +1303,7 @@ _copyFieldSelect(FieldSelect *from)
COPY_SCALAR_FIELD(fieldnum); COPY_SCALAR_FIELD(fieldnum);
COPY_SCALAR_FIELD(resulttype); COPY_SCALAR_FIELD(resulttype);
COPY_SCALAR_FIELD(resulttypmod); COPY_SCALAR_FIELD(resulttypmod);
COPY_SCALAR_FIELD(resultcollation);
return newnode; return newnode;
} }
@ -1385,6 +1401,7 @@ _copyCaseExpr(CaseExpr *from)
CaseExpr *newnode = makeNode(CaseExpr); CaseExpr *newnode = makeNode(CaseExpr);
COPY_SCALAR_FIELD(casetype); COPY_SCALAR_FIELD(casetype);
COPY_SCALAR_FIELD(casecollation);
COPY_NODE_FIELD(arg); COPY_NODE_FIELD(arg);
COPY_NODE_FIELD(args); COPY_NODE_FIELD(args);
COPY_NODE_FIELD(defresult); COPY_NODE_FIELD(defresult);
@ -1418,6 +1435,7 @@ _copyCaseTestExpr(CaseTestExpr *from)
COPY_SCALAR_FIELD(typeId); COPY_SCALAR_FIELD(typeId);
COPY_SCALAR_FIELD(typeMod); COPY_SCALAR_FIELD(typeMod);
COPY_SCALAR_FIELD(collation);
return newnode; return newnode;
} }
@ -1467,6 +1485,7 @@ _copyRowCompareExpr(RowCompareExpr *from)
COPY_SCALAR_FIELD(rctype); COPY_SCALAR_FIELD(rctype);
COPY_NODE_FIELD(opnos); COPY_NODE_FIELD(opnos);
COPY_NODE_FIELD(opfamilies); COPY_NODE_FIELD(opfamilies);
COPY_NODE_FIELD(collids);
COPY_NODE_FIELD(largs); COPY_NODE_FIELD(largs);
COPY_NODE_FIELD(rargs); COPY_NODE_FIELD(rargs);
@ -1482,6 +1501,7 @@ _copyCoalesceExpr(CoalesceExpr *from)
CoalesceExpr *newnode = makeNode(CoalesceExpr); CoalesceExpr *newnode = makeNode(CoalesceExpr);
COPY_SCALAR_FIELD(coalescetype); COPY_SCALAR_FIELD(coalescetype);
COPY_SCALAR_FIELD(coalescecollation);
COPY_NODE_FIELD(args); COPY_NODE_FIELD(args);
COPY_LOCATION_FIELD(location); COPY_LOCATION_FIELD(location);
@ -1499,6 +1519,7 @@ _copyMinMaxExpr(MinMaxExpr *from)
COPY_SCALAR_FIELD(minmaxtype); COPY_SCALAR_FIELD(minmaxtype);
COPY_SCALAR_FIELD(op); COPY_SCALAR_FIELD(op);
COPY_NODE_FIELD(args); COPY_NODE_FIELD(args);
COPY_SCALAR_FIELD(collid);
COPY_LOCATION_FIELD(location); COPY_LOCATION_FIELD(location);
return newnode; return newnode;
@ -1614,6 +1635,7 @@ _copySetToDefault(SetToDefault *from)
COPY_SCALAR_FIELD(typeId); COPY_SCALAR_FIELD(typeId);
COPY_SCALAR_FIELD(typeMod); COPY_SCALAR_FIELD(typeMod);
COPY_SCALAR_FIELD(collid);
COPY_LOCATION_FIELD(location); COPY_LOCATION_FIELD(location);
return newnode; return newnode;
@ -1719,6 +1741,7 @@ _copyPathKey(PathKey *from)
/* EquivalenceClasses are never moved, so just shallow-copy the pointer */ /* EquivalenceClasses are never moved, so just shallow-copy the pointer */
COPY_SCALAR_FIELD(pk_eclass); COPY_SCALAR_FIELD(pk_eclass);
COPY_SCALAR_FIELD(pk_opfamily); COPY_SCALAR_FIELD(pk_opfamily);
COPY_SCALAR_FIELD(pk_collation);
COPY_SCALAR_FIELD(pk_strategy); COPY_SCALAR_FIELD(pk_strategy);
COPY_SCALAR_FIELD(pk_nulls_first); COPY_SCALAR_FIELD(pk_nulls_first);
@ -1871,12 +1894,14 @@ _copyRangeTblEntry(RangeTblEntry *from)
COPY_NODE_FIELD(funcexpr); COPY_NODE_FIELD(funcexpr);
COPY_NODE_FIELD(funccoltypes); COPY_NODE_FIELD(funccoltypes);
COPY_NODE_FIELD(funccoltypmods); COPY_NODE_FIELD(funccoltypmods);
COPY_NODE_FIELD(funccolcollations);
COPY_NODE_FIELD(values_lists); COPY_NODE_FIELD(values_lists);
COPY_STRING_FIELD(ctename); COPY_STRING_FIELD(ctename);
COPY_SCALAR_FIELD(ctelevelsup); COPY_SCALAR_FIELD(ctelevelsup);
COPY_SCALAR_FIELD(self_reference); COPY_SCALAR_FIELD(self_reference);
COPY_NODE_FIELD(ctecoltypes); COPY_NODE_FIELD(ctecoltypes);
COPY_NODE_FIELD(ctecoltypmods); COPY_NODE_FIELD(ctecoltypmods);
COPY_NODE_FIELD(ctecolcollations);
COPY_NODE_FIELD(alias); COPY_NODE_FIELD(alias);
COPY_NODE_FIELD(eref); COPY_NODE_FIELD(eref);
COPY_SCALAR_FIELD(inh); COPY_SCALAR_FIELD(inh);
@ -1960,6 +1985,7 @@ _copyCommonTableExpr(CommonTableExpr *from)
COPY_NODE_FIELD(ctecolnames); COPY_NODE_FIELD(ctecolnames);
COPY_NODE_FIELD(ctecoltypes); COPY_NODE_FIELD(ctecoltypes);
COPY_NODE_FIELD(ctecoltypmods); COPY_NODE_FIELD(ctecoltypmods);
COPY_NODE_FIELD(ctecolcollations);
return newnode; return newnode;
} }
@ -2114,6 +2140,8 @@ _copyTypeName(TypeName *from)
COPY_NODE_FIELD(typmods); COPY_NODE_FIELD(typmods);
COPY_SCALAR_FIELD(typemod); COPY_SCALAR_FIELD(typemod);
COPY_NODE_FIELD(arrayBounds); COPY_NODE_FIELD(arrayBounds);
COPY_NODE_FIELD(collnames);
COPY_SCALAR_FIELD(collOid);
COPY_LOCATION_FIELD(location); COPY_LOCATION_FIELD(location);
return newnode; return newnode;
@ -2185,6 +2213,19 @@ _copyTypeCast(TypeCast *from)
return newnode; 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 * static IndexElem *
_copyIndexElem(IndexElem *from) _copyIndexElem(IndexElem *from)
{ {
@ -2193,6 +2234,7 @@ _copyIndexElem(IndexElem *from)
COPY_STRING_FIELD(name); COPY_STRING_FIELD(name);
COPY_NODE_FIELD(expr); COPY_NODE_FIELD(expr);
COPY_STRING_FIELD(indexcolname); COPY_STRING_FIELD(indexcolname);
COPY_NODE_FIELD(collation);
COPY_NODE_FIELD(opclass); COPY_NODE_FIELD(opclass);
COPY_SCALAR_FIELD(ordering); COPY_SCALAR_FIELD(ordering);
COPY_SCALAR_FIELD(nulls_ordering); COPY_SCALAR_FIELD(nulls_ordering);
@ -2403,6 +2445,7 @@ _copySetOperationStmt(SetOperationStmt *from)
COPY_NODE_FIELD(rarg); COPY_NODE_FIELD(rarg);
COPY_NODE_FIELD(colTypes); COPY_NODE_FIELD(colTypes);
COPY_NODE_FIELD(colTypmods); COPY_NODE_FIELD(colTypmods);
COPY_NODE_FIELD(colCollations);
COPY_NODE_FIELD(groupClauses); COPY_NODE_FIELD(groupClauses);
return newnode; return newnode;
@ -4328,6 +4371,9 @@ copyObject(void *from)
case T_TypeCast: case T_TypeCast:
retval = _copyTypeCast(from); retval = _copyTypeCast(from);
break; break;
case T_CollateClause:
retval = _copyCollateClause(from);
break;
case T_SortBy: case T_SortBy:
retval = _copySortBy(from); retval = _copySortBy(from);
break; break;

View File

@ -137,6 +137,7 @@ _equalVar(Var *a, Var *b)
COMPARE_SCALAR_FIELD(varattno); COMPARE_SCALAR_FIELD(varattno);
COMPARE_SCALAR_FIELD(vartype); COMPARE_SCALAR_FIELD(vartype);
COMPARE_SCALAR_FIELD(vartypmod); COMPARE_SCALAR_FIELD(vartypmod);
COMPARE_SCALAR_FIELD(varcollid);
COMPARE_SCALAR_FIELD(varlevelsup); COMPARE_SCALAR_FIELD(varlevelsup);
COMPARE_SCALAR_FIELD(varnoold); COMPARE_SCALAR_FIELD(varnoold);
COMPARE_SCALAR_FIELD(varoattno); COMPARE_SCALAR_FIELD(varoattno);
@ -150,6 +151,7 @@ _equalConst(Const *a, Const *b)
{ {
COMPARE_SCALAR_FIELD(consttype); COMPARE_SCALAR_FIELD(consttype);
COMPARE_SCALAR_FIELD(consttypmod); COMPARE_SCALAR_FIELD(consttypmod);
COMPARE_SCALAR_FIELD(constcollid);
COMPARE_SCALAR_FIELD(constlen); COMPARE_SCALAR_FIELD(constlen);
COMPARE_SCALAR_FIELD(constisnull); COMPARE_SCALAR_FIELD(constisnull);
COMPARE_SCALAR_FIELD(constbyval); COMPARE_SCALAR_FIELD(constbyval);
@ -172,6 +174,7 @@ _equalParam(Param *a, Param *b)
COMPARE_SCALAR_FIELD(paramid); COMPARE_SCALAR_FIELD(paramid);
COMPARE_SCALAR_FIELD(paramtype); COMPARE_SCALAR_FIELD(paramtype);
COMPARE_SCALAR_FIELD(paramtypmod); COMPARE_SCALAR_FIELD(paramtypmod);
COMPARE_SCALAR_FIELD(paramcollation);
COMPARE_LOCATION_FIELD(location); COMPARE_LOCATION_FIELD(location);
return true; return true;
@ -187,6 +190,7 @@ _equalAggref(Aggref *a, Aggref *b)
COMPARE_NODE_FIELD(aggdistinct); COMPARE_NODE_FIELD(aggdistinct);
COMPARE_SCALAR_FIELD(aggstar); COMPARE_SCALAR_FIELD(aggstar);
COMPARE_SCALAR_FIELD(agglevelsup); COMPARE_SCALAR_FIELD(agglevelsup);
COMPARE_SCALAR_FIELD(collid);
COMPARE_LOCATION_FIELD(location); COMPARE_LOCATION_FIELD(location);
return true; return true;
@ -201,6 +205,7 @@ _equalWindowFunc(WindowFunc *a, WindowFunc *b)
COMPARE_SCALAR_FIELD(winref); COMPARE_SCALAR_FIELD(winref);
COMPARE_SCALAR_FIELD(winstar); COMPARE_SCALAR_FIELD(winstar);
COMPARE_SCALAR_FIELD(winagg); COMPARE_SCALAR_FIELD(winagg);
COMPARE_SCALAR_FIELD(collid);
COMPARE_LOCATION_FIELD(location); COMPARE_LOCATION_FIELD(location);
return true; return true;
@ -212,6 +217,7 @@ _equalArrayRef(ArrayRef *a, ArrayRef *b)
COMPARE_SCALAR_FIELD(refarraytype); COMPARE_SCALAR_FIELD(refarraytype);
COMPARE_SCALAR_FIELD(refelemtype); COMPARE_SCALAR_FIELD(refelemtype);
COMPARE_SCALAR_FIELD(reftypmod); COMPARE_SCALAR_FIELD(reftypmod);
COMPARE_SCALAR_FIELD(refcollid);
COMPARE_NODE_FIELD(refupperindexpr); COMPARE_NODE_FIELD(refupperindexpr);
COMPARE_NODE_FIELD(reflowerindexpr); COMPARE_NODE_FIELD(reflowerindexpr);
COMPARE_NODE_FIELD(refexpr); COMPARE_NODE_FIELD(refexpr);
@ -237,6 +243,7 @@ _equalFuncExpr(FuncExpr *a, FuncExpr *b)
return false; return false;
COMPARE_NODE_FIELD(args); COMPARE_NODE_FIELD(args);
COMPARE_SCALAR_FIELD(collid);
COMPARE_LOCATION_FIELD(location); COMPARE_LOCATION_FIELD(location);
return true; return true;
@ -272,6 +279,7 @@ _equalOpExpr(OpExpr *a, OpExpr *b)
COMPARE_SCALAR_FIELD(opresulttype); COMPARE_SCALAR_FIELD(opresulttype);
COMPARE_SCALAR_FIELD(opretset); COMPARE_SCALAR_FIELD(opretset);
COMPARE_NODE_FIELD(args); COMPARE_NODE_FIELD(args);
COMPARE_SCALAR_FIELD(collid);
COMPARE_LOCATION_FIELD(location); COMPARE_LOCATION_FIELD(location);
return true; return true;
@ -296,6 +304,7 @@ _equalDistinctExpr(DistinctExpr *a, DistinctExpr *b)
COMPARE_SCALAR_FIELD(opresulttype); COMPARE_SCALAR_FIELD(opresulttype);
COMPARE_SCALAR_FIELD(opretset); COMPARE_SCALAR_FIELD(opretset);
COMPARE_NODE_FIELD(args); COMPARE_NODE_FIELD(args);
COMPARE_SCALAR_FIELD(collid);
COMPARE_LOCATION_FIELD(location); COMPARE_LOCATION_FIELD(location);
return true; return true;
@ -319,6 +328,7 @@ _equalScalarArrayOpExpr(ScalarArrayOpExpr *a, ScalarArrayOpExpr *b)
COMPARE_SCALAR_FIELD(useOr); COMPARE_SCALAR_FIELD(useOr);
COMPARE_NODE_FIELD(args); COMPARE_NODE_FIELD(args);
COMPARE_SCALAR_FIELD(collid);
COMPARE_LOCATION_FIELD(location); COMPARE_LOCATION_FIELD(location);
return true; return true;
@ -356,6 +366,7 @@ _equalSubPlan(SubPlan *a, SubPlan *b)
COMPARE_STRING_FIELD(plan_name); COMPARE_STRING_FIELD(plan_name);
COMPARE_SCALAR_FIELD(firstColType); COMPARE_SCALAR_FIELD(firstColType);
COMPARE_SCALAR_FIELD(firstColTypmod); COMPARE_SCALAR_FIELD(firstColTypmod);
COMPARE_SCALAR_FIELD(firstColCollation);
COMPARE_SCALAR_FIELD(useHashTable); COMPARE_SCALAR_FIELD(useHashTable);
COMPARE_SCALAR_FIELD(unknownEqFalse); COMPARE_SCALAR_FIELD(unknownEqFalse);
COMPARE_NODE_FIELD(setParam); COMPARE_NODE_FIELD(setParam);
@ -382,6 +393,7 @@ _equalFieldSelect(FieldSelect *a, FieldSelect *b)
COMPARE_SCALAR_FIELD(fieldnum); COMPARE_SCALAR_FIELD(fieldnum);
COMPARE_SCALAR_FIELD(resulttype); COMPARE_SCALAR_FIELD(resulttype);
COMPARE_SCALAR_FIELD(resulttypmod); COMPARE_SCALAR_FIELD(resulttypmod);
COMPARE_SCALAR_FIELD(resultcollation);
return true; return true;
} }
@ -485,6 +497,7 @@ static bool
_equalCaseExpr(CaseExpr *a, CaseExpr *b) _equalCaseExpr(CaseExpr *a, CaseExpr *b)
{ {
COMPARE_SCALAR_FIELD(casetype); COMPARE_SCALAR_FIELD(casetype);
COMPARE_SCALAR_FIELD(casecollation);
COMPARE_NODE_FIELD(arg); COMPARE_NODE_FIELD(arg);
COMPARE_NODE_FIELD(args); COMPARE_NODE_FIELD(args);
COMPARE_NODE_FIELD(defresult); COMPARE_NODE_FIELD(defresult);
@ -508,6 +521,7 @@ _equalCaseTestExpr(CaseTestExpr *a, CaseTestExpr *b)
{ {
COMPARE_SCALAR_FIELD(typeId); COMPARE_SCALAR_FIELD(typeId);
COMPARE_SCALAR_FIELD(typeMod); COMPARE_SCALAR_FIELD(typeMod);
COMPARE_SCALAR_FIELD(collation);
return true; return true;
} }
@ -551,6 +565,7 @@ _equalRowCompareExpr(RowCompareExpr *a, RowCompareExpr *b)
COMPARE_SCALAR_FIELD(rctype); COMPARE_SCALAR_FIELD(rctype);
COMPARE_NODE_FIELD(opnos); COMPARE_NODE_FIELD(opnos);
COMPARE_NODE_FIELD(opfamilies); COMPARE_NODE_FIELD(opfamilies);
COMPARE_NODE_FIELD(collids);
COMPARE_NODE_FIELD(largs); COMPARE_NODE_FIELD(largs);
COMPARE_NODE_FIELD(rargs); COMPARE_NODE_FIELD(rargs);
@ -561,6 +576,7 @@ static bool
_equalCoalesceExpr(CoalesceExpr *a, CoalesceExpr *b) _equalCoalesceExpr(CoalesceExpr *a, CoalesceExpr *b)
{ {
COMPARE_SCALAR_FIELD(coalescetype); COMPARE_SCALAR_FIELD(coalescetype);
COMPARE_SCALAR_FIELD(coalescecollation);
COMPARE_NODE_FIELD(args); COMPARE_NODE_FIELD(args);
COMPARE_LOCATION_FIELD(location); COMPARE_LOCATION_FIELD(location);
@ -573,6 +589,7 @@ _equalMinMaxExpr(MinMaxExpr *a, MinMaxExpr *b)
COMPARE_SCALAR_FIELD(minmaxtype); COMPARE_SCALAR_FIELD(minmaxtype);
COMPARE_SCALAR_FIELD(op); COMPARE_SCALAR_FIELD(op);
COMPARE_NODE_FIELD(args); COMPARE_NODE_FIELD(args);
COMPARE_SCALAR_FIELD(collid);
COMPARE_LOCATION_FIELD(location); COMPARE_LOCATION_FIELD(location);
return true; return true;
@ -673,6 +690,7 @@ _equalSetToDefault(SetToDefault *a, SetToDefault *b)
{ {
COMPARE_SCALAR_FIELD(typeId); COMPARE_SCALAR_FIELD(typeId);
COMPARE_SCALAR_FIELD(typeMod); COMPARE_SCALAR_FIELD(typeMod);
COMPARE_SCALAR_FIELD(collid);
COMPARE_LOCATION_FIELD(location); COMPARE_LOCATION_FIELD(location);
return true; return true;
@ -759,6 +777,7 @@ _equalPathKey(PathKey *a, PathKey *b)
if (a_eclass != b_eclass) if (a_eclass != b_eclass)
return false; return false;
COMPARE_SCALAR_FIELD(pk_opfamily); COMPARE_SCALAR_FIELD(pk_opfamily);
COMPARE_SCALAR_FIELD(pk_collation);
COMPARE_SCALAR_FIELD(pk_strategy); COMPARE_SCALAR_FIELD(pk_strategy);
COMPARE_SCALAR_FIELD(pk_nulls_first); COMPARE_SCALAR_FIELD(pk_nulls_first);
@ -965,6 +984,7 @@ _equalSetOperationStmt(SetOperationStmt *a, SetOperationStmt *b)
COMPARE_NODE_FIELD(rarg); COMPARE_NODE_FIELD(rarg);
COMPARE_NODE_FIELD(colTypes); COMPARE_NODE_FIELD(colTypes);
COMPARE_NODE_FIELD(colTypmods); COMPARE_NODE_FIELD(colTypmods);
COMPARE_NODE_FIELD(colCollations);
COMPARE_NODE_FIELD(groupClauses); COMPARE_NODE_FIELD(groupClauses);
return true; return true;
@ -2079,6 +2099,8 @@ _equalTypeName(TypeName *a, TypeName *b)
COMPARE_NODE_FIELD(typmods); COMPARE_NODE_FIELD(typmods);
COMPARE_SCALAR_FIELD(typemod); COMPARE_SCALAR_FIELD(typemod);
COMPARE_NODE_FIELD(arrayBounds); COMPARE_NODE_FIELD(arrayBounds);
COMPARE_NODE_FIELD(collnames);
COMPARE_SCALAR_FIELD(collOid);
COMPARE_LOCATION_FIELD(location); COMPARE_LOCATION_FIELD(location);
return true; return true;
@ -2094,6 +2116,17 @@ _equalTypeCast(TypeCast *a, TypeCast *b)
return true; return true;
} }
static bool
_equalCollateClause(CollateClause *a, CollateClause *b)
{
COMPARE_NODE_FIELD(arg);
COMPARE_NODE_FIELD(collnames);
COMPARE_SCALAR_FIELD(collOid);
COMPARE_LOCATION_FIELD(location);
return true;
}
static bool static bool
_equalSortBy(SortBy *a, SortBy *b) _equalSortBy(SortBy *a, SortBy *b)
{ {
@ -2146,6 +2179,7 @@ _equalIndexElem(IndexElem *a, IndexElem *b)
COMPARE_STRING_FIELD(name); COMPARE_STRING_FIELD(name);
COMPARE_NODE_FIELD(expr); COMPARE_NODE_FIELD(expr);
COMPARE_STRING_FIELD(indexcolname); COMPARE_STRING_FIELD(indexcolname);
COMPARE_NODE_FIELD(collation);
COMPARE_NODE_FIELD(opclass); COMPARE_NODE_FIELD(opclass);
COMPARE_SCALAR_FIELD(ordering); COMPARE_SCALAR_FIELD(ordering);
COMPARE_SCALAR_FIELD(nulls_ordering); COMPARE_SCALAR_FIELD(nulls_ordering);
@ -2229,12 +2263,14 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
COMPARE_NODE_FIELD(funcexpr); COMPARE_NODE_FIELD(funcexpr);
COMPARE_NODE_FIELD(funccoltypes); COMPARE_NODE_FIELD(funccoltypes);
COMPARE_NODE_FIELD(funccoltypmods); COMPARE_NODE_FIELD(funccoltypmods);
COMPARE_NODE_FIELD(funccolcollations);
COMPARE_NODE_FIELD(values_lists); COMPARE_NODE_FIELD(values_lists);
COMPARE_STRING_FIELD(ctename); COMPARE_STRING_FIELD(ctename);
COMPARE_SCALAR_FIELD(ctelevelsup); COMPARE_SCALAR_FIELD(ctelevelsup);
COMPARE_SCALAR_FIELD(self_reference); COMPARE_SCALAR_FIELD(self_reference);
COMPARE_NODE_FIELD(ctecoltypes); COMPARE_NODE_FIELD(ctecoltypes);
COMPARE_NODE_FIELD(ctecoltypmods); COMPARE_NODE_FIELD(ctecoltypmods);
COMPARE_NODE_FIELD(ctecolcollations);
COMPARE_NODE_FIELD(alias); COMPARE_NODE_FIELD(alias);
COMPARE_NODE_FIELD(eref); COMPARE_NODE_FIELD(eref);
COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(inh);
@ -2308,6 +2344,7 @@ _equalCommonTableExpr(CommonTableExpr *a, CommonTableExpr *b)
COMPARE_NODE_FIELD(ctecolnames); COMPARE_NODE_FIELD(ctecolnames);
COMPARE_NODE_FIELD(ctecoltypes); COMPARE_NODE_FIELD(ctecoltypes);
COMPARE_NODE_FIELD(ctecoltypmods); COMPARE_NODE_FIELD(ctecoltypmods);
COMPARE_NODE_FIELD(ctecolcollations);
return true; return true;
} }
@ -2941,6 +2978,9 @@ equal(void *a, void *b)
case T_TypeCast: case T_TypeCast:
retval = _equalTypeCast(a, b); retval = _equalTypeCast(a, b);
break; break;
case T_CollateClause:
retval = _equalCollateClause(a, b);
break;
case T_SortBy: case T_SortBy:
retval = _equalSortBy(a, b); retval = _equalSortBy(a, b);
break; break;

View File

@ -67,6 +67,7 @@ makeVar(Index varno,
AttrNumber varattno, AttrNumber varattno,
Oid vartype, Oid vartype,
int32 vartypmod, int32 vartypmod,
Oid varcollid,
Index varlevelsup) Index varlevelsup)
{ {
Var *var = makeNode(Var); Var *var = makeNode(Var);
@ -75,6 +76,7 @@ makeVar(Index varno,
var->varattno = varattno; var->varattno = varattno;
var->vartype = vartype; var->vartype = vartype;
var->vartypmod = vartypmod; var->vartypmod = vartypmod;
var->varcollid = varcollid;
var->varlevelsup = varlevelsup; var->varlevelsup = varlevelsup;
/* /*
@ -105,6 +107,7 @@ makeVarFromTargetEntry(Index varno,
tle->resno, tle->resno,
exprType((Node *) tle->expr), exprType((Node *) tle->expr),
exprTypmod((Node *) tle->expr), exprTypmod((Node *) tle->expr),
exprCollation((Node *) tle->expr),
0); 0);
} }
@ -139,6 +142,7 @@ makeWholeRowVar(RangeTblEntry *rte,
InvalidAttrNumber, InvalidAttrNumber,
toid, toid,
-1, -1,
InvalidOid,
varlevelsup); varlevelsup);
break; break;
case RTE_FUNCTION: case RTE_FUNCTION:
@ -150,6 +154,7 @@ makeWholeRowVar(RangeTblEntry *rte,
InvalidAttrNumber, InvalidAttrNumber,
toid, toid,
-1, -1,
InvalidOid,
varlevelsup); varlevelsup);
} }
else else
@ -164,6 +169,7 @@ makeWholeRowVar(RangeTblEntry *rte,
1, 1,
toid, toid,
-1, -1,
InvalidOid,
varlevelsup); varlevelsup);
} }
break; break;
@ -174,6 +180,7 @@ makeWholeRowVar(RangeTblEntry *rte,
InvalidAttrNumber, InvalidAttrNumber,
toid, toid,
-1, -1,
InvalidOid,
varlevelsup); varlevelsup);
break; break;
default: default:
@ -188,6 +195,7 @@ makeWholeRowVar(RangeTblEntry *rte,
InvalidAttrNumber, InvalidAttrNumber,
RECORDOID, RECORDOID,
-1, -1,
InvalidOid,
varlevelsup); varlevelsup);
break; break;
} }
@ -272,6 +280,7 @@ makeConst(Oid consttype,
cnst->consttype = consttype; cnst->consttype = consttype;
cnst->consttypmod = consttypmod; cnst->consttypmod = consttypmod;
cnst->constcollid = get_typcollation(consttype);
cnst->constlen = constlen; cnst->constlen = constlen;
cnst->constvalue = constvalue; cnst->constvalue = constvalue;
cnst->constisnull = constisnull; cnst->constisnull = constisnull;
@ -418,15 +427,16 @@ makeTypeNameFromNameList(List *names)
/* /*
* makeTypeNameFromOid - * 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 * TypeName *
makeTypeNameFromOid(Oid typeOid, int32 typmod) makeTypeNameFromOid(Oid typeOid, int32 typmod, Oid collOid)
{ {
TypeName *n = makeNode(TypeName); TypeName *n = makeNode(TypeName);
n->typeOid = typeOid; n->typeOid = typeOid;
n->typemod = typmod; n->typemod = typmod;
n->collOid = collOid;
n->location = -1; n->location = -1;
return n; return n;
} }
@ -438,7 +448,7 @@ makeTypeNameFromOid(Oid typeOid, int32 typmod)
* The argument expressions must have been transformed already. * The argument expressions must have been transformed already.
*/ */
FuncExpr * FuncExpr *
makeFuncExpr(Oid funcid, Oid rettype, List *args, CoercionForm fformat) makeFuncExpr(Oid funcid, Oid rettype, List *args, Oid collid, CoercionForm fformat)
{ {
FuncExpr *funcexpr; FuncExpr *funcexpr;
@ -448,6 +458,7 @@ makeFuncExpr(Oid funcid, Oid rettype, List *args, CoercionForm fformat)
funcexpr->funcretset = false; /* only allowed case here */ funcexpr->funcretset = false; /* only allowed case here */
funcexpr->funcformat = fformat; funcexpr->funcformat = fformat;
funcexpr->args = args; funcexpr->args = args;
funcexpr->collid = collid;
funcexpr->location = -1; funcexpr->location = -1;
return funcexpr; return funcexpr;

View File

@ -14,6 +14,7 @@
*/ */
#include "postgres.h" #include "postgres.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "nodes/nodeFuncs.h" #include "nodes/nodeFuncs.h"
@ -161,6 +162,9 @@ exprType(Node *expr)
case T_RelabelType: case T_RelabelType:
type = ((RelabelType *) expr)->resulttype; type = ((RelabelType *) expr)->resulttype;
break; break;
case T_CollateClause:
type = exprType((Node *) ((CollateClause *) expr)->arg);
break;
case T_CoerceViaIO: case T_CoerceViaIO:
type = ((CoerceViaIO *) expr)->resulttype; type = ((CoerceViaIO *) expr)->resulttype;
break; break;
@ -459,6 +463,215 @@ exprTypmod(Node *expr)
return -1; return -1;
} }
/*
* exprCollation -
* returns the Oid of the collation of the expression's result.
*/
Oid
exprCollation(Node *expr)
{
Oid coll;
if (!expr)
return InvalidOid;
switch (nodeTag(expr))
{
case T_Var:
coll = ((Var *) expr)->varcollid;
break;
case T_Const:
coll = ((Const *) expr)->constcollid;
break;
case T_Param:
coll = ((Param *) expr)->paramcollation;
break;
case T_Aggref:
coll = ((Aggref *) expr)->collid;
break;
case T_WindowFunc:
coll = ((WindowFunc *) expr)->collid;
break;
case T_ArrayRef:
coll = ((ArrayRef *) expr)->refcollid;
break;
case T_FuncExpr:
coll = ((FuncExpr *) expr)->collid;
break;
case T_NamedArgExpr:
coll = exprCollation((Node *) ((NamedArgExpr *) expr)->arg);
break;
case T_OpExpr:
coll = ((OpExpr *) expr)->collid;
break;
case T_DistinctExpr:
coll = ((DistinctExpr *) expr)->collid;
break;
case T_ScalarArrayOpExpr:
coll = ((ScalarArrayOpExpr *) expr)->collid;
break;
case T_BoolExpr:
coll = InvalidOid; /* not applicable */
break;
case T_SubLink:
{
SubLink *sublink = (SubLink *) expr;
if (sublink->subLinkType == EXPR_SUBLINK ||
sublink->subLinkType == ARRAY_SUBLINK)
{
/* get the collation of the subselect's first target column */
Query *qtree = (Query *) sublink->subselect;
TargetEntry *tent;
if (!qtree || !IsA(qtree, Query))
elog(ERROR, "cannot get collation for untransformed sublink");
tent = (TargetEntry *) linitial(qtree->targetList);
Assert(IsA(tent, TargetEntry));
Assert(!tent->resjunk);
coll = exprCollation((Node *) tent->expr);
/* note we don't need to care if it's an array */
}
else
coll = InvalidOid;
}
break;
case T_SubPlan:
{
SubPlan *subplan = (SubPlan *) expr;
if (subplan->subLinkType == EXPR_SUBLINK ||
subplan->subLinkType == ARRAY_SUBLINK)
{
/* get the collation of the subselect's first target column */
/* note we don't need to care if it's an array */
coll = subplan->firstColCollation;
}
else
{
/* for all other subplan types, result is boolean */
coll = InvalidOid;
}
}
break;
case T_AlternativeSubPlan:
{
AlternativeSubPlan *asplan = (AlternativeSubPlan *) expr;
/* subplans should all return the same thing */
coll = exprCollation((Node *) linitial(asplan->subplans));
}
break;
case T_FieldSelect:
coll = ((FieldSelect *) expr)->resultcollation;
break;
case T_FieldStore:
coll = InvalidOid; /* not applicable */
break;
case T_RelabelType:
coll = exprCollation((Node *) ((RelabelType *) expr)->arg);
break;
case T_CollateClause:
coll = ((CollateClause *) expr)->collOid;
break;
case T_CoerceViaIO:
{
CoerceViaIO *cvio = (CoerceViaIO *) expr;
coll = coercion_expression_result_collation(cvio->resulttype, (Node *) cvio->arg);
break;
}
case T_ArrayCoerceExpr:
{
ArrayCoerceExpr *ace = (ArrayCoerceExpr *) expr;
coll = coercion_expression_result_collation(ace->resulttype, (Node *) ace->arg);
break;
}
case T_ConvertRowtypeExpr:
{
ConvertRowtypeExpr *cre = (ConvertRowtypeExpr *) expr;
coll = coercion_expression_result_collation(cre->resulttype, (Node *) cre->arg);
break;
}
case T_CaseExpr:
coll = ((CaseExpr *) expr)->casecollation;
break;
case T_CaseTestExpr:
coll = ((CaseTestExpr *) expr)->collation;
break;
case T_ArrayExpr:
coll = get_typcollation(((ArrayExpr *) expr)->array_typeid);
break;
case T_RowExpr:
coll = InvalidOid; /* not applicable */
break;
case T_RowCompareExpr:
coll = InvalidOid; /* not applicable */
break;
case T_CoalesceExpr:
coll = ((CoalesceExpr *) expr)->coalescecollation;
break;
case T_MinMaxExpr:
coll = ((MinMaxExpr *) expr)->collid;
break;
case T_XmlExpr:
if (((XmlExpr *) expr)->op == IS_XMLSERIALIZE)
coll = DEFAULT_COLLATION_OID;
else
coll = InvalidOid;
break;
case T_NullIfExpr:
coll = exprCollation((Node *) linitial(((NullIfExpr *) expr)->args));
break;
case T_NullTest:
coll = InvalidOid; /* not applicable */
break;
case T_BooleanTest:
coll = InvalidOid; /* not applicable */
break;
case T_CoerceToDomain:
coll = get_typcollation(((CoerceToDomain *) expr)->resulttype);
if (coll == DEFAULT_COLLATION_OID)
coll = exprCollation((Node *) ((CoerceToDomain *) expr)->arg);
break;
case T_CoerceToDomainValue:
coll = get_typcollation(((CoerceToDomainValue *) expr)->typeId);
break;
case T_SetToDefault:
coll = ((SetToDefault *) expr)->collid;
break;
case T_CurrentOfExpr:
coll = InvalidOid; /* not applicable */
break;
case T_PlaceHolderVar:
coll = exprCollation((Node *) ((PlaceHolderVar *) expr)->phexpr);
break;
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
coll = InvalidOid; /* keep compiler quiet */
break;
}
return coll;
}
/*
* Compute the result collation of a coercion-like expression that
* converts arg to resulttype.
*/
Oid
coercion_expression_result_collation(Oid resulttype, Node *arg)
{
if (type_is_collatable(resulttype))
{
if (type_is_collatable(exprType(arg)))
return exprCollation(arg);
else
return DEFAULT_COLLATION_OID;
}
else
return InvalidOid;
}
/* /*
* exprIsLengthCoercion * exprIsLengthCoercion
* Detect whether an expression tree is an application of a datatype's * Detect whether an expression tree is an application of a datatype's
@ -908,6 +1121,9 @@ exprLocation(Node *expr)
loc = leftmostLoc(loc, tc->location); loc = leftmostLoc(loc, tc->location);
} }
break; break;
case T_CollateClause:
loc = ((CollateClause *) expr)->location;
break;
case T_SortBy: case T_SortBy:
/* just use argument's location (ignore operator, if any) */ /* just use argument's location (ignore operator, if any) */
loc = exprLocation(((SortBy *) expr)->node); loc = exprLocation(((SortBy *) expr)->node);
@ -1220,6 +1436,8 @@ expression_tree_walker(Node *node,
break; break;
case T_RelabelType: case T_RelabelType:
return walker(((RelabelType *) node)->arg, context); return walker(((RelabelType *) node)->arg, context);
case T_CollateClause:
return walker(((CollateClause *) node)->arg, context);
case T_CoerceViaIO: case T_CoerceViaIO:
return walker(((CoerceViaIO *) node)->arg, context); return walker(((CoerceViaIO *) node)->arg, context);
case T_ArrayCoerceExpr: case T_ArrayCoerceExpr:
@ -1776,6 +1994,16 @@ expression_tree_mutator(Node *node,
return (Node *) newnode; return (Node *) newnode;
} }
break; 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: case T_CoerceViaIO:
{ {
CoerceViaIO *iocoerce = (CoerceViaIO *) node; CoerceViaIO *iocoerce = (CoerceViaIO *) node;
@ -2471,6 +2699,8 @@ bool
return true; return true;
} }
break; break;
case T_CollateClause:
return walker(((CollateClause *) node)->arg, context);
case T_SortBy: case T_SortBy:
return walker(((SortBy *) node)->node, context); return walker(((SortBy *) node)->node, context);
case T_WindowDef: case T_WindowDef:

View File

@ -365,6 +365,10 @@ _outMergeAppend(StringInfo str, MergeAppend *node)
for (i = 0; i < node->numCols; i++) for (i = 0; i < node->numCols; i++)
appendStringInfo(str, " %u", node->sortOperators[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"); appendStringInfo(str, " :nullsFirst");
for (i = 0; i < node->numCols; i++) for (i = 0; i < node->numCols; i++)
appendStringInfo(str, " %s", booltostr(node->nullsFirst[i])); appendStringInfo(str, " %s", booltostr(node->nullsFirst[i]));
@ -499,6 +503,7 @@ _outFunctionScan(StringInfo str, FunctionScan *node)
WRITE_NODE_FIELD(funccolnames); WRITE_NODE_FIELD(funccolnames);
WRITE_NODE_FIELD(funccoltypes); WRITE_NODE_FIELD(funccoltypes);
WRITE_NODE_FIELD(funccoltypmods); WRITE_NODE_FIELD(funccoltypmods);
WRITE_NODE_FIELD(funccolcollations);
} }
static void static void
@ -568,6 +573,10 @@ _outMergeJoin(StringInfo str, MergeJoin *node)
for (i = 0; i < numCols; i++) for (i = 0; i < numCols; i++)
appendStringInfo(str, " %u", node->mergeFamilies[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"); appendStringInfo(str, " :mergeStrategies");
for (i = 0; i < numCols; i++) for (i = 0; i < numCols; i++)
appendStringInfo(str, " %d", node->mergeStrategies[i]); appendStringInfo(str, " %d", node->mergeStrategies[i]);
@ -692,6 +701,10 @@ _outSort(StringInfo str, Sort *node)
for (i = 0; i < node->numCols; i++) for (i = 0; i < node->numCols; i++)
appendStringInfo(str, " %u", node->sortOperators[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"); appendStringInfo(str, " :nullsFirst");
for (i = 0; i < node->numCols; i++) for (i = 0; i < node->numCols; i++)
appendStringInfo(str, " %s", booltostr(node->nullsFirst[i])); appendStringInfo(str, " %s", booltostr(node->nullsFirst[i]));
@ -864,6 +877,7 @@ _outVar(StringInfo str, Var *node)
WRITE_INT_FIELD(varattno); WRITE_INT_FIELD(varattno);
WRITE_OID_FIELD(vartype); WRITE_OID_FIELD(vartype);
WRITE_INT_FIELD(vartypmod); WRITE_INT_FIELD(vartypmod);
WRITE_OID_FIELD(varcollid);
WRITE_UINT_FIELD(varlevelsup); WRITE_UINT_FIELD(varlevelsup);
WRITE_UINT_FIELD(varnoold); WRITE_UINT_FIELD(varnoold);
WRITE_INT_FIELD(varoattno); WRITE_INT_FIELD(varoattno);
@ -877,6 +891,7 @@ _outConst(StringInfo str, Const *node)
WRITE_OID_FIELD(consttype); WRITE_OID_FIELD(consttype);
WRITE_INT_FIELD(consttypmod); WRITE_INT_FIELD(consttypmod);
WRITE_OID_FIELD(constcollid);
WRITE_INT_FIELD(constlen); WRITE_INT_FIELD(constlen);
WRITE_BOOL_FIELD(constbyval); WRITE_BOOL_FIELD(constbyval);
WRITE_BOOL_FIELD(constisnull); WRITE_BOOL_FIELD(constisnull);
@ -898,6 +913,7 @@ _outParam(StringInfo str, Param *node)
WRITE_INT_FIELD(paramid); WRITE_INT_FIELD(paramid);
WRITE_OID_FIELD(paramtype); WRITE_OID_FIELD(paramtype);
WRITE_INT_FIELD(paramtypmod); WRITE_INT_FIELD(paramtypmod);
WRITE_OID_FIELD(paramcollation);
WRITE_LOCATION_FIELD(location); WRITE_LOCATION_FIELD(location);
} }
@ -913,6 +929,7 @@ _outAggref(StringInfo str, Aggref *node)
WRITE_NODE_FIELD(aggdistinct); WRITE_NODE_FIELD(aggdistinct);
WRITE_BOOL_FIELD(aggstar); WRITE_BOOL_FIELD(aggstar);
WRITE_UINT_FIELD(agglevelsup); WRITE_UINT_FIELD(agglevelsup);
WRITE_OID_FIELD(collid);
WRITE_LOCATION_FIELD(location); WRITE_LOCATION_FIELD(location);
} }
@ -927,6 +944,7 @@ _outWindowFunc(StringInfo str, WindowFunc *node)
WRITE_UINT_FIELD(winref); WRITE_UINT_FIELD(winref);
WRITE_BOOL_FIELD(winstar); WRITE_BOOL_FIELD(winstar);
WRITE_BOOL_FIELD(winagg); WRITE_BOOL_FIELD(winagg);
WRITE_OID_FIELD(collid);
WRITE_LOCATION_FIELD(location); WRITE_LOCATION_FIELD(location);
} }
@ -938,6 +956,7 @@ _outArrayRef(StringInfo str, ArrayRef *node)
WRITE_OID_FIELD(refarraytype); WRITE_OID_FIELD(refarraytype);
WRITE_OID_FIELD(refelemtype); WRITE_OID_FIELD(refelemtype);
WRITE_INT_FIELD(reftypmod); WRITE_INT_FIELD(reftypmod);
WRITE_INT_FIELD(refcollid);
WRITE_NODE_FIELD(refupperindexpr); WRITE_NODE_FIELD(refupperindexpr);
WRITE_NODE_FIELD(reflowerindexpr); WRITE_NODE_FIELD(reflowerindexpr);
WRITE_NODE_FIELD(refexpr); WRITE_NODE_FIELD(refexpr);
@ -954,6 +973,7 @@ _outFuncExpr(StringInfo str, FuncExpr *node)
WRITE_BOOL_FIELD(funcretset); WRITE_BOOL_FIELD(funcretset);
WRITE_ENUM_FIELD(funcformat, CoercionForm); WRITE_ENUM_FIELD(funcformat, CoercionForm);
WRITE_NODE_FIELD(args); WRITE_NODE_FIELD(args);
WRITE_OID_FIELD(collid);
WRITE_LOCATION_FIELD(location); WRITE_LOCATION_FIELD(location);
} }
@ -978,6 +998,7 @@ _outOpExpr(StringInfo str, OpExpr *node)
WRITE_OID_FIELD(opresulttype); WRITE_OID_FIELD(opresulttype);
WRITE_BOOL_FIELD(opretset); WRITE_BOOL_FIELD(opretset);
WRITE_NODE_FIELD(args); WRITE_NODE_FIELD(args);
WRITE_OID_FIELD(collid);
WRITE_LOCATION_FIELD(location); WRITE_LOCATION_FIELD(location);
} }
@ -991,6 +1012,7 @@ _outDistinctExpr(StringInfo str, DistinctExpr *node)
WRITE_OID_FIELD(opresulttype); WRITE_OID_FIELD(opresulttype);
WRITE_BOOL_FIELD(opretset); WRITE_BOOL_FIELD(opretset);
WRITE_NODE_FIELD(args); WRITE_NODE_FIELD(args);
WRITE_OID_FIELD(collid);
WRITE_LOCATION_FIELD(location); WRITE_LOCATION_FIELD(location);
} }
@ -1003,6 +1025,7 @@ _outScalarArrayOpExpr(StringInfo str, ScalarArrayOpExpr *node)
WRITE_OID_FIELD(opfuncid); WRITE_OID_FIELD(opfuncid);
WRITE_BOOL_FIELD(useOr); WRITE_BOOL_FIELD(useOr);
WRITE_NODE_FIELD(args); WRITE_NODE_FIELD(args);
WRITE_OID_FIELD(collid);
WRITE_LOCATION_FIELD(location); WRITE_LOCATION_FIELD(location);
} }
@ -1057,6 +1080,7 @@ _outSubPlan(StringInfo str, SubPlan *node)
WRITE_STRING_FIELD(plan_name); WRITE_STRING_FIELD(plan_name);
WRITE_OID_FIELD(firstColType); WRITE_OID_FIELD(firstColType);
WRITE_INT_FIELD(firstColTypmod); WRITE_INT_FIELD(firstColTypmod);
WRITE_OID_FIELD(firstColCollation);
WRITE_BOOL_FIELD(useHashTable); WRITE_BOOL_FIELD(useHashTable);
WRITE_BOOL_FIELD(unknownEqFalse); WRITE_BOOL_FIELD(unknownEqFalse);
WRITE_NODE_FIELD(setParam); WRITE_NODE_FIELD(setParam);
@ -1083,6 +1107,7 @@ _outFieldSelect(StringInfo str, FieldSelect *node)
WRITE_INT_FIELD(fieldnum); WRITE_INT_FIELD(fieldnum);
WRITE_OID_FIELD(resulttype); WRITE_OID_FIELD(resulttype);
WRITE_INT_FIELD(resulttypmod); WRITE_INT_FIELD(resulttypmod);
WRITE_OID_FIELD(resultcollation);
} }
static void static void
@ -1150,6 +1175,7 @@ _outCaseExpr(StringInfo str, CaseExpr *node)
WRITE_NODE_TYPE("CASE"); WRITE_NODE_TYPE("CASE");
WRITE_OID_FIELD(casetype); WRITE_OID_FIELD(casetype);
WRITE_OID_FIELD(casecollation);
WRITE_NODE_FIELD(arg); WRITE_NODE_FIELD(arg);
WRITE_NODE_FIELD(args); WRITE_NODE_FIELD(args);
WRITE_NODE_FIELD(defresult); WRITE_NODE_FIELD(defresult);
@ -1173,6 +1199,7 @@ _outCaseTestExpr(StringInfo str, CaseTestExpr *node)
WRITE_OID_FIELD(typeId); WRITE_OID_FIELD(typeId);
WRITE_INT_FIELD(typeMod); WRITE_INT_FIELD(typeMod);
WRITE_OID_FIELD(collation);
} }
static void static void
@ -1207,6 +1234,7 @@ _outRowCompareExpr(StringInfo str, RowCompareExpr *node)
WRITE_ENUM_FIELD(rctype, RowCompareType); WRITE_ENUM_FIELD(rctype, RowCompareType);
WRITE_NODE_FIELD(opnos); WRITE_NODE_FIELD(opnos);
WRITE_NODE_FIELD(opfamilies); WRITE_NODE_FIELD(opfamilies);
WRITE_NODE_FIELD(collids);
WRITE_NODE_FIELD(largs); WRITE_NODE_FIELD(largs);
WRITE_NODE_FIELD(rargs); WRITE_NODE_FIELD(rargs);
} }
@ -1217,6 +1245,7 @@ _outCoalesceExpr(StringInfo str, CoalesceExpr *node)
WRITE_NODE_TYPE("COALESCE"); WRITE_NODE_TYPE("COALESCE");
WRITE_OID_FIELD(coalescetype); WRITE_OID_FIELD(coalescetype);
WRITE_OID_FIELD(coalescecollation);
WRITE_NODE_FIELD(args); WRITE_NODE_FIELD(args);
WRITE_LOCATION_FIELD(location); WRITE_LOCATION_FIELD(location);
} }
@ -1229,6 +1258,7 @@ _outMinMaxExpr(StringInfo str, MinMaxExpr *node)
WRITE_OID_FIELD(minmaxtype); WRITE_OID_FIELD(minmaxtype);
WRITE_ENUM_FIELD(op, MinMaxOp); WRITE_ENUM_FIELD(op, MinMaxOp);
WRITE_NODE_FIELD(args); WRITE_NODE_FIELD(args);
WRITE_OID_FIELD(collid);
WRITE_LOCATION_FIELD(location); WRITE_LOCATION_FIELD(location);
} }
@ -1309,6 +1339,7 @@ _outSetToDefault(StringInfo str, SetToDefault *node)
WRITE_OID_FIELD(typeId); WRITE_OID_FIELD(typeId);
WRITE_INT_FIELD(typeMod); WRITE_INT_FIELD(typeMod);
WRITE_OID_FIELD(collid);
WRITE_LOCATION_FIELD(location); WRITE_LOCATION_FIELD(location);
} }
@ -1716,6 +1747,7 @@ _outPathKey(StringInfo str, PathKey *node)
WRITE_NODE_FIELD(pk_eclass); WRITE_NODE_FIELD(pk_eclass);
WRITE_OID_FIELD(pk_opfamily); WRITE_OID_FIELD(pk_opfamily);
WRITE_OID_FIELD(pk_collation);
WRITE_INT_FIELD(pk_strategy); WRITE_INT_FIELD(pk_strategy);
WRITE_BOOL_FIELD(pk_nulls_first); WRITE_BOOL_FIELD(pk_nulls_first);
} }
@ -2014,6 +2046,8 @@ _outTypeName(StringInfo str, TypeName *node)
WRITE_NODE_FIELD(typmods); WRITE_NODE_FIELD(typmods);
WRITE_INT_FIELD(typemod); WRITE_INT_FIELD(typemod);
WRITE_NODE_FIELD(arrayBounds); WRITE_NODE_FIELD(arrayBounds);
WRITE_NODE_FIELD(collnames);
WRITE_OID_FIELD(collOid);
WRITE_LOCATION_FIELD(location); WRITE_LOCATION_FIELD(location);
} }
@ -2027,6 +2061,17 @@ _outTypeCast(StringInfo str, TypeCast *node)
WRITE_LOCATION_FIELD(location); WRITE_LOCATION_FIELD(location);
} }
static void
_outCollateClause(StringInfo str, CollateClause *node)
{
WRITE_NODE_TYPE("COLLATE");
WRITE_NODE_FIELD(arg);
WRITE_NODE_FIELD(collnames);
WRITE_OID_FIELD(collOid);
WRITE_LOCATION_FIELD(location);
}
static void static void
_outIndexElem(StringInfo str, IndexElem *node) _outIndexElem(StringInfo str, IndexElem *node)
{ {
@ -2035,6 +2080,7 @@ _outIndexElem(StringInfo str, IndexElem *node)
WRITE_STRING_FIELD(name); WRITE_STRING_FIELD(name);
WRITE_NODE_FIELD(expr); WRITE_NODE_FIELD(expr);
WRITE_STRING_FIELD(indexcolname); WRITE_STRING_FIELD(indexcolname);
WRITE_NODE_FIELD(collation);
WRITE_NODE_FIELD(opclass); WRITE_NODE_FIELD(opclass);
WRITE_ENUM_FIELD(ordering, SortByDir); WRITE_ENUM_FIELD(ordering, SortByDir);
WRITE_ENUM_FIELD(nulls_ordering, SortByNulls); WRITE_ENUM_FIELD(nulls_ordering, SortByNulls);
@ -2162,6 +2208,7 @@ _outCommonTableExpr(StringInfo str, CommonTableExpr *node)
WRITE_NODE_FIELD(ctecolnames); WRITE_NODE_FIELD(ctecolnames);
WRITE_NODE_FIELD(ctecoltypes); WRITE_NODE_FIELD(ctecoltypes);
WRITE_NODE_FIELD(ctecoltypmods); WRITE_NODE_FIELD(ctecoltypmods);
WRITE_NODE_FIELD(ctecolcollations);
} }
static void static void
@ -2175,6 +2222,7 @@ _outSetOperationStmt(StringInfo str, SetOperationStmt *node)
WRITE_NODE_FIELD(rarg); WRITE_NODE_FIELD(rarg);
WRITE_NODE_FIELD(colTypes); WRITE_NODE_FIELD(colTypes);
WRITE_NODE_FIELD(colTypmods); WRITE_NODE_FIELD(colTypmods);
WRITE_NODE_FIELD(colCollations);
WRITE_NODE_FIELD(groupClauses); WRITE_NODE_FIELD(groupClauses);
} }
@ -2205,6 +2253,7 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
WRITE_NODE_FIELD(funcexpr); WRITE_NODE_FIELD(funcexpr);
WRITE_NODE_FIELD(funccoltypes); WRITE_NODE_FIELD(funccoltypes);
WRITE_NODE_FIELD(funccoltypmods); WRITE_NODE_FIELD(funccoltypmods);
WRITE_NODE_FIELD(funccolcollations);
break; break;
case RTE_VALUES: case RTE_VALUES:
WRITE_NODE_FIELD(values_lists); WRITE_NODE_FIELD(values_lists);
@ -2215,6 +2264,7 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
WRITE_BOOL_FIELD(self_reference); WRITE_BOOL_FIELD(self_reference);
WRITE_NODE_FIELD(ctecoltypes); WRITE_NODE_FIELD(ctecoltypes);
WRITE_NODE_FIELD(ctecoltypmods); WRITE_NODE_FIELD(ctecoltypmods);
WRITE_NODE_FIELD(ctecolcollations);
break; break;
default: default:
elog(ERROR, "unrecognized RTE kind: %d", (int) node->rtekind); elog(ERROR, "unrecognized RTE kind: %d", (int) node->rtekind);
@ -2732,6 +2782,9 @@ _outNode(StringInfo str, void *obj)
case T_RelabelType: case T_RelabelType:
_outRelabelType(str, obj); _outRelabelType(str, obj);
break; break;
case T_CollateClause:
_outCollateClause(str, obj);
break;
case T_CoerceViaIO: case T_CoerceViaIO:
_outCoerceViaIO(str, obj); _outCoerceViaIO(str, obj);
break; break;

View File

@ -323,6 +323,7 @@ _readCommonTableExpr(void)
READ_NODE_FIELD(ctecolnames); READ_NODE_FIELD(ctecolnames);
READ_NODE_FIELD(ctecoltypes); READ_NODE_FIELD(ctecoltypes);
READ_NODE_FIELD(ctecoltypmods); READ_NODE_FIELD(ctecoltypmods);
READ_NODE_FIELD(ctecolcollations);
READ_DONE(); READ_DONE();
} }
@ -341,6 +342,7 @@ _readSetOperationStmt(void)
READ_NODE_FIELD(rarg); READ_NODE_FIELD(rarg);
READ_NODE_FIELD(colTypes); READ_NODE_FIELD(colTypes);
READ_NODE_FIELD(colTypmods); READ_NODE_FIELD(colTypmods);
READ_NODE_FIELD(colCollations);
READ_NODE_FIELD(groupClauses); READ_NODE_FIELD(groupClauses);
READ_DONE(); READ_DONE();
@ -406,6 +408,7 @@ _readVar(void)
READ_INT_FIELD(varattno); READ_INT_FIELD(varattno);
READ_OID_FIELD(vartype); READ_OID_FIELD(vartype);
READ_INT_FIELD(vartypmod); READ_INT_FIELD(vartypmod);
READ_OID_FIELD(varcollid);
READ_UINT_FIELD(varlevelsup); READ_UINT_FIELD(varlevelsup);
READ_UINT_FIELD(varnoold); READ_UINT_FIELD(varnoold);
READ_INT_FIELD(varoattno); READ_INT_FIELD(varoattno);
@ -424,6 +427,7 @@ _readConst(void)
READ_OID_FIELD(consttype); READ_OID_FIELD(consttype);
READ_INT_FIELD(consttypmod); READ_INT_FIELD(consttypmod);
READ_OID_FIELD(constcollid);
READ_INT_FIELD(constlen); READ_INT_FIELD(constlen);
READ_BOOL_FIELD(constbyval); READ_BOOL_FIELD(constbyval);
READ_BOOL_FIELD(constisnull); READ_BOOL_FIELD(constisnull);
@ -450,6 +454,7 @@ _readParam(void)
READ_INT_FIELD(paramid); READ_INT_FIELD(paramid);
READ_OID_FIELD(paramtype); READ_OID_FIELD(paramtype);
READ_INT_FIELD(paramtypmod); READ_INT_FIELD(paramtypmod);
READ_OID_FIELD(paramcollation);
READ_LOCATION_FIELD(location); READ_LOCATION_FIELD(location);
READ_DONE(); READ_DONE();
@ -470,6 +475,7 @@ _readAggref(void)
READ_NODE_FIELD(aggdistinct); READ_NODE_FIELD(aggdistinct);
READ_BOOL_FIELD(aggstar); READ_BOOL_FIELD(aggstar);
READ_UINT_FIELD(agglevelsup); READ_UINT_FIELD(agglevelsup);
READ_OID_FIELD(collid);
READ_LOCATION_FIELD(location); READ_LOCATION_FIELD(location);
READ_DONE(); READ_DONE();
@ -489,6 +495,7 @@ _readWindowFunc(void)
READ_UINT_FIELD(winref); READ_UINT_FIELD(winref);
READ_BOOL_FIELD(winstar); READ_BOOL_FIELD(winstar);
READ_BOOL_FIELD(winagg); READ_BOOL_FIELD(winagg);
READ_OID_FIELD(collid);
READ_LOCATION_FIELD(location); READ_LOCATION_FIELD(location);
READ_DONE(); READ_DONE();
@ -505,6 +512,7 @@ _readArrayRef(void)
READ_OID_FIELD(refarraytype); READ_OID_FIELD(refarraytype);
READ_OID_FIELD(refelemtype); READ_OID_FIELD(refelemtype);
READ_INT_FIELD(reftypmod); READ_INT_FIELD(reftypmod);
READ_INT_FIELD(refcollid);
READ_NODE_FIELD(refupperindexpr); READ_NODE_FIELD(refupperindexpr);
READ_NODE_FIELD(reflowerindexpr); READ_NODE_FIELD(reflowerindexpr);
READ_NODE_FIELD(refexpr); READ_NODE_FIELD(refexpr);
@ -526,6 +534,7 @@ _readFuncExpr(void)
READ_BOOL_FIELD(funcretset); READ_BOOL_FIELD(funcretset);
READ_ENUM_FIELD(funcformat, CoercionForm); READ_ENUM_FIELD(funcformat, CoercionForm);
READ_NODE_FIELD(args); READ_NODE_FIELD(args);
READ_OID_FIELD(collid);
READ_LOCATION_FIELD(location); READ_LOCATION_FIELD(location);
READ_DONE(); READ_DONE();
@ -571,6 +580,7 @@ _readOpExpr(void)
READ_OID_FIELD(opresulttype); READ_OID_FIELD(opresulttype);
READ_BOOL_FIELD(opretset); READ_BOOL_FIELD(opretset);
READ_NODE_FIELD(args); READ_NODE_FIELD(args);
READ_OID_FIELD(collid);
READ_LOCATION_FIELD(location); READ_LOCATION_FIELD(location);
READ_DONE(); READ_DONE();
@ -600,6 +610,7 @@ _readDistinctExpr(void)
READ_OID_FIELD(opresulttype); READ_OID_FIELD(opresulttype);
READ_BOOL_FIELD(opretset); READ_BOOL_FIELD(opretset);
READ_NODE_FIELD(args); READ_NODE_FIELD(args);
READ_OID_FIELD(collid);
READ_LOCATION_FIELD(location); READ_LOCATION_FIELD(location);
READ_DONE(); READ_DONE();
@ -628,6 +639,7 @@ _readScalarArrayOpExpr(void)
READ_BOOL_FIELD(useOr); READ_BOOL_FIELD(useOr);
READ_NODE_FIELD(args); READ_NODE_FIELD(args);
READ_OID_FIELD(collid);
READ_LOCATION_FIELD(location); READ_LOCATION_FIELD(location);
READ_DONE(); READ_DONE();
@ -692,6 +704,7 @@ _readFieldSelect(void)
READ_INT_FIELD(fieldnum); READ_INT_FIELD(fieldnum);
READ_OID_FIELD(resulttype); READ_OID_FIELD(resulttype);
READ_INT_FIELD(resulttypmod); READ_INT_FIELD(resulttypmod);
READ_OID_FIELD(resultcollation);
READ_DONE(); READ_DONE();
} }
@ -729,6 +742,22 @@ _readRelabelType(void)
READ_DONE(); READ_DONE();
} }
/*
* _readCollateClause
*/
static CollateClause *
_readCollateClause(void)
{
READ_LOCALS(CollateClause);
READ_NODE_FIELD(arg);
READ_NODE_FIELD(collnames);
READ_OID_FIELD(collOid);
READ_LOCATION_FIELD(location);
READ_DONE();
}
/* /*
* _readCoerceViaIO * _readCoerceViaIO
*/ */
@ -789,6 +818,7 @@ _readCaseExpr(void)
READ_LOCALS(CaseExpr); READ_LOCALS(CaseExpr);
READ_OID_FIELD(casetype); READ_OID_FIELD(casetype);
READ_OID_FIELD(casecollation);
READ_NODE_FIELD(arg); READ_NODE_FIELD(arg);
READ_NODE_FIELD(args); READ_NODE_FIELD(args);
READ_NODE_FIELD(defresult); READ_NODE_FIELD(defresult);
@ -822,6 +852,7 @@ _readCaseTestExpr(void)
READ_OID_FIELD(typeId); READ_OID_FIELD(typeId);
READ_INT_FIELD(typeMod); READ_INT_FIELD(typeMod);
READ_OID_FIELD(collation);
READ_DONE(); READ_DONE();
} }
@ -871,6 +902,7 @@ _readRowCompareExpr(void)
READ_ENUM_FIELD(rctype, RowCompareType); READ_ENUM_FIELD(rctype, RowCompareType);
READ_NODE_FIELD(opnos); READ_NODE_FIELD(opnos);
READ_NODE_FIELD(opfamilies); READ_NODE_FIELD(opfamilies);
READ_NODE_FIELD(collids);
READ_NODE_FIELD(largs); READ_NODE_FIELD(largs);
READ_NODE_FIELD(rargs); READ_NODE_FIELD(rargs);
@ -886,6 +918,7 @@ _readCoalesceExpr(void)
READ_LOCALS(CoalesceExpr); READ_LOCALS(CoalesceExpr);
READ_OID_FIELD(coalescetype); READ_OID_FIELD(coalescetype);
READ_OID_FIELD(coalescecollation);
READ_NODE_FIELD(args); READ_NODE_FIELD(args);
READ_LOCATION_FIELD(location); READ_LOCATION_FIELD(location);
@ -903,6 +936,7 @@ _readMinMaxExpr(void)
READ_OID_FIELD(minmaxtype); READ_OID_FIELD(minmaxtype);
READ_ENUM_FIELD(op, MinMaxOp); READ_ENUM_FIELD(op, MinMaxOp);
READ_NODE_FIELD(args); READ_NODE_FIELD(args);
READ_OID_FIELD(collid);
READ_LOCATION_FIELD(location); READ_LOCATION_FIELD(location);
READ_DONE(); READ_DONE();
@ -1029,6 +1063,7 @@ _readSetToDefault(void)
READ_OID_FIELD(typeId); READ_OID_FIELD(typeId);
READ_INT_FIELD(typeMod); READ_INT_FIELD(typeMod);
READ_OID_FIELD(collid);
READ_LOCATION_FIELD(location); READ_LOCATION_FIELD(location);
READ_DONE(); READ_DONE();
@ -1150,6 +1185,7 @@ _readRangeTblEntry(void)
READ_NODE_FIELD(funcexpr); READ_NODE_FIELD(funcexpr);
READ_NODE_FIELD(funccoltypes); READ_NODE_FIELD(funccoltypes);
READ_NODE_FIELD(funccoltypmods); READ_NODE_FIELD(funccoltypmods);
READ_NODE_FIELD(funccolcollations);
break; break;
case RTE_VALUES: case RTE_VALUES:
READ_NODE_FIELD(values_lists); READ_NODE_FIELD(values_lists);
@ -1160,6 +1196,7 @@ _readRangeTblEntry(void)
READ_BOOL_FIELD(self_reference); READ_BOOL_FIELD(self_reference);
READ_NODE_FIELD(ctecoltypes); READ_NODE_FIELD(ctecoltypes);
READ_NODE_FIELD(ctecoltypmods); READ_NODE_FIELD(ctecoltypmods);
READ_NODE_FIELD(ctecolcollations);
break; break;
default: default:
elog(ERROR, "unrecognized RTE kind: %d", elog(ERROR, "unrecognized RTE kind: %d",
@ -1248,6 +1285,8 @@ parseNodeString(void)
return_value = _readFieldStore(); return_value = _readFieldStore();
else if (MATCH("RELABELTYPE", 11)) else if (MATCH("RELABELTYPE", 11))
return_value = _readRelabelType(); return_value = _readRelabelType();
else if (MATCH("COLLATE", 7))
return_value = _readCollateClause();
else if (MATCH("COERCEVIAIO", 11)) else if (MATCH("COERCEVIAIO", 11))
return_value = _readCoerceViaIO(); return_value = _readCoerceViaIO();
else if (MATCH("ARRAYCOERCEEXPR", 15)) else if (MATCH("ARRAYCOERCEEXPR", 15))

View File

@ -1795,6 +1795,7 @@ cost_mergejoin(MergePath *path, PlannerInfo *root, SpecialJoinInfo *sjinfo)
ipathkey = (PathKey *) linitial(ipathkeys); ipathkey = (PathKey *) linitial(ipathkeys);
/* debugging check */ /* debugging check */
if (opathkey->pk_opfamily != ipathkey->pk_opfamily || if (opathkey->pk_opfamily != ipathkey->pk_opfamily ||
opathkey->pk_collation != ipathkey->pk_collation ||
opathkey->pk_strategy != ipathkey->pk_strategy || opathkey->pk_strategy != ipathkey->pk_strategy ||
opathkey->pk_nulls_first != ipathkey->pk_nulls_first) opathkey->pk_nulls_first != ipathkey->pk_nulls_first)
elog(ERROR, "left and right pathkeys do not match in mergejoin"); 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); cache = (MergeScanSelCache *) lfirst(lc);
if (cache->opfamily == pathkey->pk_opfamily && if (cache->opfamily == pathkey->pk_opfamily &&
cache->collation == pathkey->pk_collation &&
cache->strategy == pathkey->pk_strategy && cache->strategy == pathkey->pk_strategy &&
cache->nulls_first == pathkey->pk_nulls_first) cache->nulls_first == pathkey->pk_nulls_first)
return cache; return cache;
@ -2054,6 +2056,7 @@ cached_scansel(PlannerInfo *root, RestrictInfo *rinfo, PathKey *pathkey)
mergejoinscansel(root, mergejoinscansel(root,
(Node *) rinfo->clause, (Node *) rinfo->clause,
pathkey->pk_opfamily, pathkey->pk_opfamily,
pathkey->pk_collation,
pathkey->pk_strategy, pathkey->pk_strategy,
pathkey->pk_nulls_first, pathkey->pk_nulls_first,
&leftstartsel, &leftstartsel,
@ -2066,6 +2069,7 @@ cached_scansel(PlannerInfo *root, RestrictInfo *rinfo, PathKey *pathkey)
cache = (MergeScanSelCache *) palloc(sizeof(MergeScanSelCache)); cache = (MergeScanSelCache *) palloc(sizeof(MergeScanSelCache));
cache->opfamily = pathkey->pk_opfamily; cache->opfamily = pathkey->pk_opfamily;
cache->collation = pathkey->pk_collation;
cache->strategy = pathkey->pk_strategy; cache->strategy = pathkey->pk_strategy;
cache->nulls_first = pathkey->pk_nulls_first; cache->nulls_first = pathkey->pk_nulls_first;
cache->leftstartsel = leftstartsel; cache->leftstartsel = leftstartsel;

View File

@ -23,6 +23,7 @@
#include "catalog/pg_opfamily.h" #include "catalog/pg_opfamily.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h" #include "optimizer/clauses.h"
#include "optimizer/cost.h" #include "optimizer/cost.h"
#include "optimizer/pathnode.h" #include "optimizer/pathnode.h"
@ -99,15 +100,15 @@ static List *find_clauses_for_join(PlannerInfo *root, RelOptInfo *rel,
Relids outer_relids, bool isouterjoin); Relids outer_relids, bool isouterjoin);
static bool match_boolean_index_clause(Node *clause, int indexcol, static bool match_boolean_index_clause(Node *clause, int indexcol,
IndexOptInfo *index); 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); bool indexkey_on_left);
static Expr *expand_boolean_index_clause(Node *clause, int indexcol, static Expr *expand_boolean_index_clause(Node *clause, int indexcol,
IndexOptInfo *index); 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, static RestrictInfo *expand_indexqual_rowcompare(RestrictInfo *rinfo,
IndexOptInfo *index, IndexOptInfo *index,
int indexcol); 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); Const *prefix, Pattern_Prefix_Status pstatus);
static List *network_prefix_quals(Node *leftop, Oid expr_op, Oid opfamily, static List *network_prefix_quals(Node *leftop, Oid expr_op, Oid opfamily,
Datum rightop); Datum rightop);
@ -1142,7 +1143,9 @@ group_clauses_by_indexkey(IndexOptInfo *index,
* and * and
* (2) must contain an operator which is in the same family as the index * (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 * 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 * Our definition of "const" is pretty liberal: we allow Vars belonging
* to the caller-specified outer_relids relations (which had better not * to the caller-specified outer_relids relations (which had better not
@ -1198,6 +1201,7 @@ match_clause_to_indexcol(IndexOptInfo *index,
SaOpControl saop_control) SaOpControl saop_control)
{ {
Expr *clause = rinfo->clause; Expr *clause = rinfo->clause;
Oid collation = index->indexcollations[indexcol];
Oid opfamily = index->opfamily[indexcol]; Oid opfamily = index->opfamily[indexcol];
Node *leftop, Node *leftop,
*rightop; *rightop;
@ -1280,7 +1284,8 @@ match_clause_to_indexcol(IndexOptInfo *index,
bms_is_subset(right_relids, outer_relids) && bms_is_subset(right_relids, outer_relids) &&
!contain_volatile_functions(rightop)) !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; return true;
/* /*
@ -1288,7 +1293,7 @@ match_clause_to_indexcol(IndexOptInfo *index,
* is a "special" indexable operator. * is a "special" indexable operator.
*/ */
if (plain_op && if (plain_op &&
match_special_index_operator(clause, opfamily, true)) match_special_index_operator(clause, collation, opfamily, true))
return true; return true;
return false; return false;
} }
@ -1298,14 +1303,15 @@ match_clause_to_indexcol(IndexOptInfo *index,
bms_is_subset(left_relids, outer_relids) && bms_is_subset(left_relids, outer_relids) &&
!contain_volatile_functions(leftop)) !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; return true;
/* /*
* If we didn't find a member of the index's opfamily, see whether it * If we didn't find a member of the index's opfamily, see whether it
* is a "special" indexable operator. * 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 true;
return false; return false;
} }
@ -1391,6 +1397,9 @@ match_rowcompare_to_indexcol(IndexOptInfo *index,
else else
return false; 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 */ /* We're good if the operator is the right type of opfamily member */
switch (get_op_opfamily_strategy(expr_op, opfamily)) 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. * Return 'true' if we can do something with it anyway.
*/ */
static bool 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 indexkey_on_left)
{ {
bool isIndexable = false; bool isIndexable = false;
@ -2495,7 +2504,7 @@ match_special_index_operator(Expr *clause, Oid opfamily,
isIndexable = isIndexable =
(opfamily == TEXT_PATTERN_BTREE_FAM_OID) || (opfamily == TEXT_PATTERN_BTREE_FAM_OID) ||
(opfamily == TEXT_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; break;
case OID_BPCHAR_LIKE_OP: case OID_BPCHAR_LIKE_OP:
@ -2505,7 +2514,7 @@ match_special_index_operator(Expr *clause, Oid opfamily,
isIndexable = isIndexable =
(opfamily == BPCHAR_PATTERN_BTREE_FAM_OID) || (opfamily == BPCHAR_PATTERN_BTREE_FAM_OID) ||
(opfamily == BPCHAR_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; break;
case OID_NAME_LIKE_OP: case OID_NAME_LIKE_OP:
@ -2526,6 +2535,25 @@ match_special_index_operator(Expr *clause, Oid opfamily,
break; 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; return isIndexable;
} }
@ -2561,6 +2589,7 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
{ {
List *clausegroup = (List *) lfirst(lc); List *clausegroup = (List *) lfirst(lc);
Oid curFamily = index->opfamily[indexcol]; Oid curFamily = index->opfamily[indexcol];
Oid curCollation = index->indexcollations[indexcol];
ListCell *lc2; ListCell *lc2;
foreach(lc2, clausegroup) foreach(lc2, clausegroup)
@ -2592,7 +2621,8 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
{ {
resultquals = list_concat(resultquals, resultquals = list_concat(resultquals,
expand_indexqual_opclause(rinfo, expand_indexqual_opclause(rinfo,
curFamily)); curFamily,
curCollation));
} }
else if (IsA(clause, ScalarArrayOpExpr)) 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(). * expand special cases that were accepted by match_special_index_operator().
*/ */
static List * static List *
expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily) expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid collation)
{ {
Expr *clause = rinfo->clause; Expr *clause = rinfo->clause;
@ -2724,7 +2754,7 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily)
{ {
pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like, pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like,
&prefix, &rest); &prefix, &rest);
return prefix_quals(leftop, opfamily, prefix, pstatus); return prefix_quals(leftop, opfamily, collation, prefix, pstatus);
} }
break; break;
@ -2736,7 +2766,7 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily)
/* the right-hand const is type text for all of these */ /* the right-hand const is type text for all of these */
pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like_IC, pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like_IC,
&prefix, &rest); &prefix, &rest);
return prefix_quals(leftop, opfamily, prefix, pstatus); return prefix_quals(leftop, opfamily, collation, prefix, pstatus);
} }
break; break;
@ -2748,7 +2778,7 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily)
/* the right-hand const is type text for all of these */ /* the right-hand const is type text for all of these */
pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex, pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex,
&prefix, &rest); &prefix, &rest);
return prefix_quals(leftop, opfamily, prefix, pstatus); return prefix_quals(leftop, opfamily, collation, prefix, pstatus);
} }
break; break;
@ -2760,7 +2790,7 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily)
/* the right-hand const is type text for all of these */ /* the right-hand const is type text for all of these */
pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex_IC, pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex_IC,
&prefix, &rest); &prefix, &rest);
return prefix_quals(leftop, opfamily, prefix, pstatus); return prefix_quals(leftop, opfamily, collation, prefix, pstatus);
} }
break; break;
@ -2814,6 +2844,7 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
ListCell *largs_cell; ListCell *largs_cell;
ListCell *rargs_cell; ListCell *rargs_cell;
ListCell *opnos_cell; ListCell *opnos_cell;
ListCell *collids_cell;
/* We have to figure out (again) how the first col matches */ /* We have to figure out (again) how the first col matches */
var_on_left = match_index_to_operand((Node *) linitial(clause->largs), 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)); largs_cell = lnext(list_head(clause->largs));
rargs_cell = lnext(list_head(clause->rargs)); rargs_cell = lnext(list_head(clause->rargs));
opnos_cell = lnext(list_head(clause->opnos)); opnos_cell = lnext(list_head(clause->opnos));
collids_cell = lnext(list_head(clause->collids));
while (largs_cell != NULL) while (largs_cell != NULL)
{ {
@ -2891,6 +2923,10 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
!= op_strategy) != op_strategy)
break; break;
/* Does collation match? */
if (lfirst_oid(collids_cell) != index->indexcollations[i])
break;
/* Add opfamily and datatypes to lists */ /* Add opfamily and datatypes to lists */
get_op_opfamily_properties(expr_op, index->opfamily[i], false, get_op_opfamily_properties(expr_op, index->opfamily[i], false,
&op_strategy, &op_strategy,
@ -2974,6 +3010,8 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
rc->opnos = new_ops; rc->opnos = new_ops;
rc->opfamilies = list_truncate(list_copy(clause->opfamilies), rc->opfamilies = list_truncate(list_copy(clause->opfamilies),
matching_cols); matching_cols);
rc->collids = list_truncate(list_copy(clause->collids),
matching_cols);
rc->largs = list_truncate((List *) copyObject(clause->largs), rc->largs = list_truncate((List *) copyObject(clause->largs),
matching_cols); matching_cols);
rc->rargs = list_truncate((List *) copyObject(clause->rargs), rc->rargs = list_truncate((List *) copyObject(clause->rargs),
@ -2998,7 +3036,7 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
* operators and operand datatypes. * operators and operand datatypes.
*/ */
static List * static List *
prefix_quals(Node *leftop, Oid opfamily, prefix_quals(Node *leftop, Oid opfamily, Oid collation,
Const *prefix_const, Pattern_Prefix_Status pstatus) Const *prefix_const, Pattern_Prefix_Status pstatus)
{ {
List *result; List *result;
@ -3100,6 +3138,7 @@ prefix_quals(Node *leftop, Oid opfamily,
if (oproid == InvalidOid) if (oproid == InvalidOid)
elog(ERROR, "no < operator for opfamily %u", opfamily); elog(ERROR, "no < operator for opfamily %u", opfamily);
fmgr_info(get_opcode(oproid), &ltproc); fmgr_info(get_opcode(oproid), &ltproc);
fmgr_info_collation(collation, &ltproc);
greaterstr = make_greater_string(prefix_const, &ltproc); greaterstr = make_greater_string(prefix_const, &ltproc);
if (greaterstr) if (greaterstr)
{ {

View File

@ -18,6 +18,7 @@
#include "postgres.h" #include "postgres.h"
#include "access/skey.h" #include "access/skey.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h" #include "nodes/nodeFuncs.h"
@ -30,10 +31,10 @@
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
static PathKey *makePathKey(EquivalenceClass *eclass, Oid opfamily, static PathKey *makePathKey(EquivalenceClass *eclass, Oid opfamily, Oid collation,
int strategy, bool nulls_first); int strategy, bool nulls_first);
static PathKey *make_canonical_pathkey(PlannerInfo *root, static PathKey *make_canonical_pathkey(PlannerInfo *root,
EquivalenceClass *eclass, Oid opfamily, EquivalenceClass *eclass, Oid opfamily, Oid collation,
int strategy, bool nulls_first); int strategy, bool nulls_first);
static bool pathkey_is_redundant(PathKey *new_pathkey, List *pathkeys); static bool pathkey_is_redundant(PathKey *new_pathkey, List *pathkeys);
static Var *find_indexkey_var(PlannerInfo *root, RelOptInfo *rel, 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. * convenience routine to build the specified node.
*/ */
static PathKey * static PathKey *
makePathKey(EquivalenceClass *eclass, Oid opfamily, makePathKey(EquivalenceClass *eclass, Oid opfamily, Oid collation,
int strategy, bool nulls_first) int strategy, bool nulls_first)
{ {
PathKey *pk = makeNode(PathKey); PathKey *pk = makeNode(PathKey);
pk->pk_eclass = eclass; pk->pk_eclass = eclass;
pk->pk_opfamily = opfamily; pk->pk_opfamily = opfamily;
pk->pk_collation = collation;
pk->pk_strategy = strategy; pk->pk_strategy = strategy;
pk->pk_nulls_first = nulls_first; pk->pk_nulls_first = nulls_first;
@ -77,7 +79,7 @@ makePathKey(EquivalenceClass *eclass, Oid opfamily,
*/ */
static PathKey * static PathKey *
make_canonical_pathkey(PlannerInfo *root, make_canonical_pathkey(PlannerInfo *root,
EquivalenceClass *eclass, Oid opfamily, EquivalenceClass *eclass, Oid opfamily, Oid collation,
int strategy, bool nulls_first) int strategy, bool nulls_first)
{ {
PathKey *pk; PathKey *pk;
@ -93,6 +95,7 @@ make_canonical_pathkey(PlannerInfo *root,
pk = (PathKey *) lfirst(lc); pk = (PathKey *) lfirst(lc);
if (eclass == pk->pk_eclass && if (eclass == pk->pk_eclass &&
opfamily == pk->pk_opfamily && opfamily == pk->pk_opfamily &&
collation == pk->pk_collation &&
strategy == pk->pk_strategy && strategy == pk->pk_strategy &&
nulls_first == pk->pk_nulls_first) nulls_first == pk->pk_nulls_first)
return pk; return pk;
@ -104,7 +107,7 @@ make_canonical_pathkey(PlannerInfo *root,
*/ */
oldcontext = MemoryContextSwitchTo(root->planner_cxt); 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); root->canon_pathkeys = lappend(root->canon_pathkeys, pk);
MemoryContextSwitchTo(oldcontext); MemoryContextSwitchTo(oldcontext);
@ -206,6 +209,7 @@ canonicalize_pathkeys(PlannerInfo *root, List *pathkeys)
cpathkey = make_canonical_pathkey(root, cpathkey = make_canonical_pathkey(root,
eclass, eclass,
pathkey->pk_opfamily, pathkey->pk_opfamily,
pathkey->pk_collation,
pathkey->pk_strategy, pathkey->pk_strategy,
pathkey->pk_nulls_first); pathkey->pk_nulls_first);
@ -247,6 +251,7 @@ make_pathkey_from_sortinfo(PlannerInfo *root,
Oid equality_op; Oid equality_op;
List *opfamilies; List *opfamilies;
EquivalenceClass *eclass; EquivalenceClass *eclass;
Oid collation;
strategy = reverse_sort ? BTGreaterStrategyNumber : BTLessStrategyNumber; strategy = reverse_sort ? BTGreaterStrategyNumber : BTLessStrategyNumber;
@ -301,12 +306,14 @@ make_pathkey_from_sortinfo(PlannerInfo *root,
if (!eclass) if (!eclass)
return NULL; return NULL;
collation = exprCollation((Node *) expr);
/* And finally we can find or create a PathKey node */ /* And finally we can find or create a PathKey node */
if (canonicalize) if (canonicalize)
return make_canonical_pathkey(root, eclass, opfamily, return make_canonical_pathkey(root, eclass, opfamily, collation,
strategy, nulls_first); strategy, nulls_first);
else 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; ListCell *temp;
Index relid; Index relid;
Oid reloid, Oid reloid,
vartypeid; vartypeid,
varcollid;
int32 type_mod; int32 type_mod;
foreach(temp, rel->reltargetlist) foreach(temp, rel->reltargetlist)
@ -620,8 +628,9 @@ find_indexkey_var(PlannerInfo *root, RelOptInfo *rel, AttrNumber varattno)
relid = rel->relid; relid = rel->relid;
reloid = getrelid(relid, root->parse->rtable); reloid = getrelid(relid, root->parse->rtable);
get_atttypetypmod(reloid, varattno, &vartypeid, &type_mod); 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, make_canonical_pathkey(root,
outer_ec, outer_ec,
sub_pathkey->pk_opfamily, sub_pathkey->pk_opfamily,
sub_pathkey->pk_collation,
sub_pathkey->pk_strategy, sub_pathkey->pk_strategy,
sub_pathkey->pk_nulls_first); sub_pathkey->pk_nulls_first);
} }
@ -805,6 +815,7 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
outer_pk = make_canonical_pathkey(root, outer_pk = make_canonical_pathkey(root,
outer_ec, outer_ec,
sub_pathkey->pk_opfamily, sub_pathkey->pk_opfamily,
sub_pathkey->pk_collation,
sub_pathkey->pk_strategy, sub_pathkey->pk_strategy,
sub_pathkey->pk_nulls_first); sub_pathkey->pk_nulls_first);
/* score = # of equivalence peers */ /* score = # of equivalence peers */
@ -1326,6 +1337,7 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
pathkey = make_canonical_pathkey(root, pathkey = make_canonical_pathkey(root,
ec, ec,
linitial_oid(ec->ec_opfamilies), linitial_oid(ec->ec_opfamilies),
DEFAULT_COLLATION_OID,
BTLessStrategyNumber, BTLessStrategyNumber,
false); false);
/* can't be redundant because no duplicate ECs */ /* can't be redundant because no duplicate ECs */
@ -1419,6 +1431,7 @@ make_inner_pathkeys_for_merge(PlannerInfo *root,
pathkey = make_canonical_pathkey(root, pathkey = make_canonical_pathkey(root,
ieclass, ieclass,
opathkey->pk_opfamily, opathkey->pk_opfamily,
opathkey->pk_collation,
opathkey->pk_strategy, opathkey->pk_strategy,
opathkey->pk_nulls_first); opathkey->pk_nulls_first);
@ -1539,6 +1552,7 @@ right_merge_direction(PlannerInfo *root, PathKey *pathkey)
PathKey *query_pathkey = (PathKey *) lfirst(l); PathKey *query_pathkey = (PathKey *) lfirst(l);
if (pathkey->pk_eclass == query_pathkey->pk_eclass && if (pathkey->pk_eclass == query_pathkey->pk_eclass &&
pathkey->pk_collation == query_pathkey->pk_collation &&
pathkey->pk_opfamily == query_pathkey->pk_opfamily) pathkey->pk_opfamily == query_pathkey->pk_opfamily)
{ {
/* /*

View File

@ -105,7 +105,7 @@ static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
List *tidquals); List *tidquals);
static FunctionScan *make_functionscan(List *qptlist, List *qpqual, static FunctionScan *make_functionscan(List *qptlist, List *qpqual,
Index scanrelid, Node *funcexpr, List *funccolnames, Index scanrelid, Node *funcexpr, List *funccolnames,
List *funccoltypes, List *funccoltypmods); List *funccoltypes, List *funccoltypmods, List *funccolcollations);
static ValuesScan *make_valuesscan(List *qptlist, List *qpqual, static ValuesScan *make_valuesscan(List *qptlist, List *qpqual,
Index scanrelid, List *values_lists); Index scanrelid, List *values_lists);
static CteScan *make_ctescan(List *qptlist, List *qpqual, static CteScan *make_ctescan(List *qptlist, List *qpqual,
@ -133,12 +133,13 @@ static MergeJoin *make_mergejoin(List *tlist,
List *joinclauses, List *otherclauses, List *joinclauses, List *otherclauses,
List *mergeclauses, List *mergeclauses,
Oid *mergefamilies, Oid *mergefamilies,
Oid *mergecollations,
int *mergestrategies, int *mergestrategies,
bool *mergenullsfirst, bool *mergenullsfirst,
Plan *lefttree, Plan *righttree, Plan *lefttree, Plan *righttree,
JoinType jointype); JoinType jointype);
static Sort *make_sort(PlannerInfo *root, Plan *lefttree, int numCols, 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); double limit_tuples);
static Plan *prepare_sort_from_pathkeys(PlannerInfo *root, static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
Plan *lefttree, List *pathkeys, Plan *lefttree, List *pathkeys,
@ -146,6 +147,7 @@ static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
int *p_numsortkeys, int *p_numsortkeys,
AttrNumber **p_sortColIdx, AttrNumber **p_sortColIdx,
Oid **p_sortOperators, Oid **p_sortOperators,
Oid **p_collations,
bool **p_nullsFirst); bool **p_nullsFirst);
static Material *make_material(Plan *lefttree); static Material *make_material(Plan *lefttree);
@ -671,6 +673,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
&node->numCols, &node->numCols,
&node->sortColIdx, &node->sortColIdx,
&node->sortOperators, &node->sortOperators,
&node->collations,
&node->nullsFirst); &node->nullsFirst);
/* /*
@ -685,6 +688,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
int numsortkeys; int numsortkeys;
AttrNumber *sortColIdx; AttrNumber *sortColIdx;
Oid *sortOperators; Oid *sortOperators;
Oid *collations;
bool *nullsFirst; bool *nullsFirst;
/* Build the child plan */ /* Build the child plan */
@ -696,6 +700,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
&numsortkeys, &numsortkeys,
&sortColIdx, &sortColIdx,
&sortOperators, &sortOperators,
&collations,
&nullsFirst); &nullsFirst);
/* /*
@ -710,13 +715,15 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
elog(ERROR, "MergeAppend child's targetlist doesn't match MergeAppend"); elog(ERROR, "MergeAppend child's targetlist doesn't match MergeAppend");
Assert(memcmp(sortOperators, node->sortOperators, Assert(memcmp(sortOperators, node->sortOperators,
numsortkeys * sizeof(Oid)) == 0); numsortkeys * sizeof(Oid)) == 0);
Assert(memcmp(collations, node->collations,
numsortkeys * sizeof(Oid)) == 0);
Assert(memcmp(nullsFirst, node->nullsFirst, Assert(memcmp(nullsFirst, node->nullsFirst,
numsortkeys * sizeof(bool)) == 0); numsortkeys * sizeof(bool)) == 0);
/* Now, insert a Sort node if subplan isn't sufficiently ordered */ /* Now, insert a Sort node if subplan isn't sufficiently ordered */
if (!pathkeys_contained_in(pathkeys, subpath->pathkeys)) if (!pathkeys_contained_in(pathkeys, subpath->pathkeys))
subplan = (Plan *) make_sort(root, subplan, numsortkeys, subplan = (Plan *) make_sort(root, subplan, numsortkeys,
sortColIdx, sortOperators, nullsFirst, sortColIdx, sortOperators, collations, nullsFirst,
best_path->limit_tuples); best_path->limit_tuples);
subplans = lappend(subplans, subplan); subplans = lappend(subplans, subplan);
@ -1569,7 +1576,8 @@ create_functionscan_plan(PlannerInfo *root, Path *best_path,
rte->funcexpr, rte->funcexpr,
rte->eref->colnames, rte->eref->colnames,
rte->funccoltypes, rte->funccoltypes,
rte->funccoltypmods); rte->funccoltypmods,
rte->funccolcollations);
copy_path_costsize(&scan_plan->scan.plan, best_path); copy_path_costsize(&scan_plan->scan.plan, best_path);
@ -1847,6 +1855,7 @@ create_mergejoin_plan(PlannerInfo *root,
List *innerpathkeys; List *innerpathkeys;
int nClauses; int nClauses;
Oid *mergefamilies; Oid *mergefamilies;
Oid *mergecollations;
int *mergestrategies; int *mergestrategies;
bool *mergenullsfirst; bool *mergenullsfirst;
MergeJoin *join_plan; MergeJoin *join_plan;
@ -1946,6 +1955,7 @@ create_mergejoin_plan(PlannerInfo *root,
nClauses = list_length(mergeclauses); nClauses = list_length(mergeclauses);
Assert(nClauses == list_length(best_path->path_mergeclauses)); Assert(nClauses == list_length(best_path->path_mergeclauses));
mergefamilies = (Oid *) palloc(nClauses * sizeof(Oid)); mergefamilies = (Oid *) palloc(nClauses * sizeof(Oid));
mergecollations = (Oid *) palloc(nClauses * sizeof(Oid));
mergestrategies = (int *) palloc(nClauses * sizeof(int)); mergestrategies = (int *) palloc(nClauses * sizeof(int));
mergenullsfirst = (bool *) palloc(nClauses * sizeof(bool)); mergenullsfirst = (bool *) palloc(nClauses * sizeof(bool));
@ -2074,12 +2084,14 @@ create_mergejoin_plan(PlannerInfo *root,
/* pathkeys should match each other too (more debugging) */ /* pathkeys should match each other too (more debugging) */
if (opathkey->pk_opfamily != ipathkey->pk_opfamily || if (opathkey->pk_opfamily != ipathkey->pk_opfamily ||
opathkey->pk_collation != ipathkey->pk_collation ||
opathkey->pk_strategy != ipathkey->pk_strategy || opathkey->pk_strategy != ipathkey->pk_strategy ||
opathkey->pk_nulls_first != ipathkey->pk_nulls_first) opathkey->pk_nulls_first != ipathkey->pk_nulls_first)
elog(ERROR, "left and right pathkeys do not match in mergejoin"); elog(ERROR, "left and right pathkeys do not match in mergejoin");
/* OK, save info for executor */ /* OK, save info for executor */
mergefamilies[i] = opathkey->pk_opfamily; mergefamilies[i] = opathkey->pk_opfamily;
mergecollations[i] = opathkey->pk_collation;
mergestrategies[i] = opathkey->pk_strategy; mergestrategies[i] = opathkey->pk_strategy;
mergenullsfirst[i] = opathkey->pk_nulls_first; mergenullsfirst[i] = opathkey->pk_nulls_first;
i++; i++;
@ -2099,6 +2111,7 @@ create_mergejoin_plan(PlannerInfo *root,
otherclauses, otherclauses,
mergeclauses, mergeclauses,
mergefamilies, mergefamilies,
mergecollations,
mergestrategies, mergestrategies,
mergenullsfirst, mergenullsfirst,
outer_plan, outer_plan,
@ -2528,6 +2541,7 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index)
/* Found a match */ /* Found a match */
result = makeVar(index->rel->relid, pos + 1, result = makeVar(index->rel->relid, pos + 1,
exprType(lfirst(indexpr_item)), -1, exprType(lfirst(indexpr_item)), -1,
exprCollation(lfirst(indexpr_item)),
0); 0);
return (Node *) result; return (Node *) result;
} }
@ -2881,7 +2895,8 @@ make_functionscan(List *qptlist,
Node *funcexpr, Node *funcexpr,
List *funccolnames, List *funccolnames,
List *funccoltypes, List *funccoltypes,
List *funccoltypmods) List *funccoltypmods,
List *funccolcollations)
{ {
FunctionScan *node = makeNode(FunctionScan); FunctionScan *node = makeNode(FunctionScan);
Plan *plan = &node->scan.plan; Plan *plan = &node->scan.plan;
@ -2896,6 +2911,7 @@ make_functionscan(List *qptlist,
node->funccolnames = funccolnames; node->funccolnames = funccolnames;
node->funccoltypes = funccoltypes; node->funccoltypes = funccoltypes;
node->funccoltypmods = funccoltypmods; node->funccoltypmods = funccoltypmods;
node->funccolcollations = funccolcollations;
return node; return node;
} }
@ -3181,6 +3197,7 @@ make_mergejoin(List *tlist,
List *otherclauses, List *otherclauses,
List *mergeclauses, List *mergeclauses,
Oid *mergefamilies, Oid *mergefamilies,
Oid *mergecollations,
int *mergestrategies, int *mergestrategies,
bool *mergenullsfirst, bool *mergenullsfirst,
Plan *lefttree, Plan *lefttree,
@ -3197,6 +3214,7 @@ make_mergejoin(List *tlist,
plan->righttree = righttree; plan->righttree = righttree;
node->mergeclauses = mergeclauses; node->mergeclauses = mergeclauses;
node->mergeFamilies = mergefamilies; node->mergeFamilies = mergefamilies;
node->mergeCollations = mergecollations;
node->mergeStrategies = mergestrategies; node->mergeStrategies = mergestrategies;
node->mergeNullsFirst = mergenullsfirst; node->mergeNullsFirst = mergenullsfirst;
node->join.jointype = jointype; node->join.jointype = jointype;
@ -3214,7 +3232,7 @@ make_mergejoin(List *tlist,
*/ */
static Sort * static Sort *
make_sort(PlannerInfo *root, Plan *lefttree, int numCols, 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) double limit_tuples)
{ {
Sort *node = makeNode(Sort); Sort *node = makeNode(Sort);
@ -3238,6 +3256,7 @@ make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
node->numCols = numCols; node->numCols = numCols;
node->sortColIdx = sortColIdx; node->sortColIdx = sortColIdx;
node->sortOperators = sortOperators; node->sortOperators = sortOperators;
node->collations = collations;
node->nullsFirst = nullsFirst; node->nullsFirst = nullsFirst;
return node; 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. * max possible number of columns. Return value is the new column count.
*/ */
static int 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, int numCols, AttrNumber *sortColIdx,
Oid *sortOperators, bool *nullsFirst) Oid *sortOperators, Oid *collations, bool *nullsFirst)
{ {
int i; int i;
@ -3271,7 +3290,8 @@ add_sort_column(AttrNumber colIdx, Oid sortOp, bool nulls_first,
* opposite nulls direction is redundant. * opposite nulls direction is redundant.
*/ */
if (sortColIdx[i] == colIdx && if (sortColIdx[i] == colIdx &&
sortOperators[numCols] == sortOp) sortOperators[numCols] == sortOp &&
collations[numCols] == coll)
{ {
/* Already sorting by this col, so extra sort key is useless */ /* Already sorting by this col, so extra sort key is useless */
return numCols; return numCols;
@ -3281,6 +3301,7 @@ add_sort_column(AttrNumber colIdx, Oid sortOp, bool nulls_first,
/* Add the column */ /* Add the column */
sortColIdx[numCols] = colIdx; sortColIdx[numCols] = colIdx;
sortOperators[numCols] = sortOp; sortOperators[numCols] = sortOp;
collations[numCols] = coll;
nullsFirst[numCols] = nulls_first; nullsFirst[numCols] = nulls_first;
return numCols + 1; return numCols + 1;
} }
@ -3320,6 +3341,7 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
int *p_numsortkeys, int *p_numsortkeys,
AttrNumber **p_sortColIdx, AttrNumber **p_sortColIdx,
Oid **p_sortOperators, Oid **p_sortOperators,
Oid **p_collations,
bool **p_nullsFirst) bool **p_nullsFirst)
{ {
List *tlist = lefttree->targetlist; List *tlist = lefttree->targetlist;
@ -3327,6 +3349,7 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
int numsortkeys; int numsortkeys;
AttrNumber *sortColIdx; AttrNumber *sortColIdx;
Oid *sortOperators; Oid *sortOperators;
Oid *collations;
bool *nullsFirst; bool *nullsFirst;
/* /*
@ -3335,6 +3358,7 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
numsortkeys = list_length(pathkeys); numsortkeys = list_length(pathkeys);
sortColIdx = (AttrNumber *) palloc(numsortkeys * sizeof(AttrNumber)); sortColIdx = (AttrNumber *) palloc(numsortkeys * sizeof(AttrNumber));
sortOperators = (Oid *) palloc(numsortkeys * sizeof(Oid)); sortOperators = (Oid *) palloc(numsortkeys * sizeof(Oid));
collations = (Oid *) palloc(numsortkeys * sizeof(Oid));
nullsFirst = (bool *) palloc(numsortkeys * sizeof(bool)); nullsFirst = (bool *) palloc(numsortkeys * sizeof(bool));
numsortkeys = 0; numsortkeys = 0;
@ -3493,9 +3517,10 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
*/ */
numsortkeys = add_sort_column(tle->resno, numsortkeys = add_sort_column(tle->resno,
sortop, sortop,
pathkey->pk_collation,
pathkey->pk_nulls_first, pathkey->pk_nulls_first,
numsortkeys, numsortkeys,
sortColIdx, sortOperators, nullsFirst); sortColIdx, sortOperators, collations, nullsFirst);
} }
Assert(numsortkeys > 0); Assert(numsortkeys > 0);
@ -3504,6 +3529,7 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
*p_numsortkeys = numsortkeys; *p_numsortkeys = numsortkeys;
*p_sortColIdx = sortColIdx; *p_sortColIdx = sortColIdx;
*p_sortOperators = sortOperators; *p_sortOperators = sortOperators;
*p_collations = collations;
*p_nullsFirst = nullsFirst; *p_nullsFirst = nullsFirst;
return lefttree; return lefttree;
@ -3525,6 +3551,7 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
int numsortkeys; int numsortkeys;
AttrNumber *sortColIdx; AttrNumber *sortColIdx;
Oid *sortOperators; Oid *sortOperators;
Oid *collations;
bool *nullsFirst; bool *nullsFirst;
/* Compute sort column info, and adjust lefttree as needed */ /* Compute sort column info, and adjust lefttree as needed */
@ -3533,11 +3560,12 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
&numsortkeys, &numsortkeys,
&sortColIdx, &sortColIdx,
&sortOperators, &sortOperators,
&collations,
&nullsFirst); &nullsFirst);
/* Now build the Sort node */ /* Now build the Sort node */
return make_sort(root, lefttree, numsortkeys, 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; int numsortkeys;
AttrNumber *sortColIdx; AttrNumber *sortColIdx;
Oid *sortOperators; Oid *sortOperators;
Oid *collations;
bool *nullsFirst; bool *nullsFirst;
/* /*
@ -3563,6 +3592,7 @@ make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree)
numsortkeys = list_length(sortcls); numsortkeys = list_length(sortcls);
sortColIdx = (AttrNumber *) palloc(numsortkeys * sizeof(AttrNumber)); sortColIdx = (AttrNumber *) palloc(numsortkeys * sizeof(AttrNumber));
sortOperators = (Oid *) palloc(numsortkeys * sizeof(Oid)); sortOperators = (Oid *) palloc(numsortkeys * sizeof(Oid));
collations = (Oid *) palloc(numsortkeys * sizeof(Oid));
nullsFirst = (bool *) palloc(numsortkeys * sizeof(bool)); nullsFirst = (bool *) palloc(numsortkeys * sizeof(bool));
numsortkeys = 0; numsortkeys = 0;
@ -3578,15 +3608,16 @@ make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree)
* redundantly. * redundantly.
*/ */
numsortkeys = add_sort_column(tle->resno, sortcl->sortop, numsortkeys = add_sort_column(tle->resno, sortcl->sortop,
exprCollation((Node *) tle->expr),
sortcl->nulls_first, sortcl->nulls_first,
numsortkeys, numsortkeys,
sortColIdx, sortOperators, nullsFirst); sortColIdx, sortOperators, collations, nullsFirst);
} }
Assert(numsortkeys > 0); Assert(numsortkeys > 0);
return make_sort(root, lefttree, numsortkeys, 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; int numsortkeys;
AttrNumber *sortColIdx; AttrNumber *sortColIdx;
Oid *sortOperators; Oid *sortOperators;
Oid *collations;
bool *nullsFirst; bool *nullsFirst;
/* /*
@ -3622,6 +3654,7 @@ make_sort_from_groupcols(PlannerInfo *root,
numsortkeys = list_length(groupcls); numsortkeys = list_length(groupcls);
sortColIdx = (AttrNumber *) palloc(numsortkeys * sizeof(AttrNumber)); sortColIdx = (AttrNumber *) palloc(numsortkeys * sizeof(AttrNumber));
sortOperators = (Oid *) palloc(numsortkeys * sizeof(Oid)); sortOperators = (Oid *) palloc(numsortkeys * sizeof(Oid));
collations = (Oid *) palloc(numsortkeys * sizeof(Oid));
nullsFirst = (bool *) palloc(numsortkeys * sizeof(bool)); nullsFirst = (bool *) palloc(numsortkeys * sizeof(bool));
numsortkeys = 0; numsortkeys = 0;
@ -3637,16 +3670,17 @@ make_sort_from_groupcols(PlannerInfo *root,
* redundantly. * redundantly.
*/ */
numsortkeys = add_sort_column(tle->resno, grpcl->sortop, numsortkeys = add_sort_column(tle->resno, grpcl->sortop,
exprCollation((Node *) tle->expr),
grpcl->nulls_first, grpcl->nulls_first,
numsortkeys, numsortkeys,
sortColIdx, sortOperators, nullsFirst); sortColIdx, sortOperators, collations, nullsFirst);
grpno++; grpno++;
} }
Assert(numsortkeys > 0); Assert(numsortkeys > 0);
return make_sort(root, lefttree, numsortkeys, return make_sort(root, lefttree, numsortkeys,
sortColIdx, sortOperators, nullsFirst, -1.0); sortColIdx, sortOperators, collations, nullsFirst, -1.0);
} }
static Material * static Material *

View File

@ -561,7 +561,8 @@ make_agg_subplan(PlannerInfo *root, RelOptInfo *rel, PrivateMMAggInfo *info)
*/ */
info->param = SS_make_initplan_from_plan(&subroot, plan, info->param = SS_make_initplan_from_plan(&subroot, plan,
exprType((Node *) tle->expr), exprType((Node *) tle->expr),
-1); -1,
exprCollation((Node *) tle->expr));
/* /*
* Put the updated list of InitPlans back into the outer PlannerInfo. * Put the updated list of InitPlans back into the outer PlannerInfo.

View File

@ -213,9 +213,11 @@ set_plan_references(PlannerGlobal *glob, Plan *plan,
newrte->funcexpr = NULL; newrte->funcexpr = NULL;
newrte->funccoltypes = NIL; newrte->funccoltypes = NIL;
newrte->funccoltypmods = NIL; newrte->funccoltypmods = NIL;
newrte->funccolcollations = NIL;
newrte->values_lists = NIL; newrte->values_lists = NIL;
newrte->ctecoltypes = NIL; newrte->ctecoltypes = NIL;
newrte->ctecoltypmods = NIL; newrte->ctecoltypmods = NIL;
newrte->ctecolcollations = NIL;
glob->finalrtable = lappend(glob->finalrtable, newrte); glob->finalrtable = lappend(glob->finalrtable, newrte);
@ -1119,6 +1121,7 @@ set_dummy_tlist_references(Plan *plan, int rtoffset)
tle->resno, tle->resno,
exprType((Node *) oldvar), exprType((Node *) oldvar),
exprTypmod((Node *) oldvar), exprTypmod((Node *) oldvar),
exprCollation((Node *) oldvar),
0); 0);
if (IsA(oldvar, Var)) if (IsA(oldvar, Var))
{ {

View File

@ -157,6 +157,7 @@ replace_outer_var(PlannerInfo *root, Var *var)
retval->paramid = i; retval->paramid = i;
retval->paramtype = var->vartype; retval->paramtype = var->vartype;
retval->paramtypmod = var->vartypmod; retval->paramtypmod = var->vartypmod;
retval->paramcollation = var->varcollid;
retval->location = -1; retval->location = -1;
return retval; return retval;
@ -185,6 +186,7 @@ assign_nestloop_param(PlannerInfo *root, Var *var)
retval->paramid = i; retval->paramid = i;
retval->paramtype = var->vartype; retval->paramtype = var->vartype;
retval->paramtypmod = var->vartypmod; retval->paramtypmod = var->vartypmod;
retval->paramcollation = var->varcollid;
retval->location = -1; retval->location = -1;
return retval; return retval;
@ -225,6 +227,7 @@ replace_outer_agg(PlannerInfo *root, Aggref *agg)
retval->paramid = i; retval->paramid = i;
retval->paramtype = agg->aggtype; retval->paramtype = agg->aggtype;
retval->paramtypmod = -1; retval->paramtypmod = -1;
retval->paramcollation = agg->collid;
retval->location = -1; retval->location = -1;
return retval; return retval;
@ -236,7 +239,7 @@ replace_outer_agg(PlannerInfo *root, Aggref *agg)
* This is used to allocate PARAM_EXEC slots for subplan outputs. * This is used to allocate PARAM_EXEC slots for subplan outputs.
*/ */
static Param * static Param *
generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod) generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod, Oid paramcollation)
{ {
Param *retval; Param *retval;
PlannerParamItem *pitem; PlannerParamItem *pitem;
@ -246,6 +249,7 @@ generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod)
retval->paramid = list_length(root->glob->paramlist); retval->paramid = list_length(root->glob->paramlist);
retval->paramtype = paramtype; retval->paramtype = paramtype;
retval->paramtypmod = paramtypmod; retval->paramtypmod = paramtypmod;
retval->paramcollation = paramcollation;
retval->location = -1; retval->location = -1;
pitem = makeNode(PlannerParamItem); pitem = makeNode(PlannerParamItem);
@ -270,7 +274,7 @@ SS_assign_special_param(PlannerInfo *root)
Param *param; Param *param;
/* We generate a Param of datatype INTERNAL */ /* 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 */ /* ... but the caller only cares about its ID */
return param->paramid; 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. * 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. * 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, * We really only need the info for EXPR_SUBLINK and ARRAY_SUBLINK subplans,
* but for consistency we save it always. * but for consistency we save it always.
*/ */
static void 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 */ /* In cases such as EXISTS, tlist might be empty; arbitrarily use VOID */
if (plan->targetlist) if (plan->targetlist)
@ -296,11 +300,13 @@ get_first_col_type(Plan *plan, Oid *coltype, int32 *coltypmod)
{ {
*coltype = exprType((Node *) tent->expr); *coltype = exprType((Node *) tent->expr);
*coltypmod = exprTypmod((Node *) tent->expr); *coltypmod = exprTypmod((Node *) tent->expr);
*colcollation = exprCollation((Node *) tent->expr);
return; return;
} }
} }
*coltype = VOIDOID; *coltype = VOIDOID;
*coltypmod = -1; *coltypmod = -1;
*colcollation = InvalidOid;
} }
/* /*
@ -470,7 +476,7 @@ build_subplan(PlannerInfo *root, Plan *plan, List *rtable, List *rowmarks,
splan->subLinkType = subLinkType; splan->subLinkType = subLinkType;
splan->testexpr = NULL; splan->testexpr = NULL;
splan->paramIds = NIL; 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->useHashTable = false;
splan->unknownEqFalse = unknownEqFalse; splan->unknownEqFalse = unknownEqFalse;
splan->setParam = NIL; splan->setParam = NIL;
@ -523,7 +529,7 @@ build_subplan(PlannerInfo *root, Plan *plan, List *rtable, List *rowmarks,
Param *prm; Param *prm;
Assert(testexpr == NULL); 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); splan->setParam = list_make1_int(prm->paramid);
isInitPlan = true; isInitPlan = true;
result = (Node *) prm; result = (Node *) prm;
@ -537,7 +543,8 @@ build_subplan(PlannerInfo *root, Plan *plan, List *rtable, List *rowmarks,
Assert(testexpr == NULL); Assert(testexpr == NULL);
prm = generate_new_param(root, prm = generate_new_param(root,
exprType((Node *) te->expr), exprType((Node *) te->expr),
exprTypmod((Node *) te->expr)); exprTypmod((Node *) te->expr),
exprCollation((Node *) te->expr));
splan->setParam = list_make1_int(prm->paramid); splan->setParam = list_make1_int(prm->paramid);
isInitPlan = true; isInitPlan = true;
result = (Node *) prm; result = (Node *) prm;
@ -556,7 +563,8 @@ build_subplan(PlannerInfo *root, Plan *plan, List *rtable, List *rowmarks,
format_type_be(exprType((Node *) te->expr))); format_type_be(exprType((Node *) te->expr)));
prm = generate_new_param(root, prm = generate_new_param(root,
arraytype, arraytype,
exprTypmod((Node *) te->expr)); exprTypmod((Node *) te->expr),
exprCollation((Node *) te->expr));
splan->setParam = list_make1_int(prm->paramid); splan->setParam = list_make1_int(prm->paramid);
isInitPlan = true; isInitPlan = true;
result = (Node *) prm; result = (Node *) prm;
@ -708,7 +716,8 @@ generate_subquery_params(PlannerInfo *root, List *tlist, List **paramIds)
param = generate_new_param(root, param = generate_new_param(root,
exprType((Node *) tent->expr), exprType((Node *) tent->expr),
exprTypmod((Node *) tent->expr)); exprTypmod((Node *) tent->expr),
exprCollation((Node *) tent->expr));
result = lappend(result, param); result = lappend(result, param);
ids = lappend_int(ids, param->paramid); ids = lappend_int(ids, param->paramid);
} }
@ -964,7 +973,7 @@ SS_process_ctes(PlannerInfo *root)
splan->subLinkType = CTE_SUBLINK; splan->subLinkType = CTE_SUBLINK;
splan->testexpr = NULL; splan->testexpr = NULL;
splan->paramIds = NIL; 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->useHashTable = false;
splan->unknownEqFalse = false; splan->unknownEqFalse = false;
splan->setParam = NIL; splan->setParam = NIL;
@ -999,7 +1008,7 @@ SS_process_ctes(PlannerInfo *root)
* Assign a param to represent the query output. We only really care * Assign a param to represent the query output. We only really care
* about reserving a parameter ID number. * 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); splan->setParam = list_make1_int(prm->paramid);
/* /*
@ -1565,7 +1574,8 @@ convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect,
oc = lnext(oc); oc = lnext(oc);
param = generate_new_param(root, param = generate_new_param(root,
exprType(rightarg), exprType(rightarg),
exprTypmod(rightarg)); exprTypmod(rightarg),
exprCollation(rightarg));
tlist = lappend(tlist, tlist = lappend(tlist,
makeTargetEntry((Expr *) rightarg, makeTargetEntry((Expr *) rightarg,
resno++, resno++,
@ -2352,7 +2362,7 @@ finalize_primnode(Node *node, finalize_primnode_context *context)
*/ */
Param * Param *
SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan, SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan,
Oid resulttype, int32 resulttypmod) Oid resulttype, int32 resulttypmod, Oid resultcollation)
{ {
SubPlan *node; SubPlan *node;
Param *prm; Param *prm;
@ -2388,7 +2398,7 @@ SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan,
*/ */
node = makeNode(SubPlan); node = makeNode(SubPlan);
node->subLinkType = EXPR_SUBLINK; 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); node->plan_id = list_length(root->glob->subplans);
root->init_plans = lappend(root->init_plans, node); 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. * 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); node->setParam = list_make1_int(prm->paramid);
/* Label the subplan for EXPLAIN purposes */ /* Label the subplan for EXPLAIN purposes */

View File

@ -445,6 +445,7 @@ inline_set_returning_functions(PlannerInfo *root)
rte->funcexpr = NULL; rte->funcexpr = NULL;
rte->funccoltypes = NIL; rte->funccoltypes = NIL;
rte->funccoltypmods = NIL; rte->funccoltypmods = NIL;
rte->funccolcollations = NIL;
} }
} }
} }

View File

@ -100,6 +100,7 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
SelfItemPointerAttributeNumber, SelfItemPointerAttributeNumber,
TIDOID, TIDOID,
-1, -1,
InvalidOid,
0); 0);
snprintf(resname, sizeof(resname), "ctid%u", rc->rti); snprintf(resname, sizeof(resname), "ctid%u", rc->rti);
tle = makeTargetEntry((Expr *) var, tle = makeTargetEntry((Expr *) var,
@ -115,6 +116,7 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
TableOidAttributeNumber, TableOidAttributeNumber,
OIDOID, OIDOID,
-1, -1,
InvalidOid,
0); 0);
snprintf(resname, sizeof(resname), "tableoid%u", rc->rti); snprintf(resname, sizeof(resname), "tableoid%u", rc->rti);
tle = makeTargetEntry((Expr *) var, tle = makeTargetEntry((Expr *) var,
@ -257,6 +259,7 @@ expand_targetlist(List *tlist, int command_type,
*/ */
Oid atttype = att_tup->atttypid; Oid atttype = att_tup->atttypid;
int32 atttypmod = att_tup->atttypmod; int32 atttypmod = att_tup->atttypmod;
Oid attcollation = att_tup->attcollation;
Node *new_expr; Node *new_expr;
switch (command_type) switch (command_type)
@ -296,6 +299,7 @@ expand_targetlist(List *tlist, int command_type,
attrno, attrno,
atttype, atttype,
atttypmod, atttypmod,
attcollation,
0); 0);
} }
else else

View File

@ -86,7 +86,7 @@ static List *generate_setop_tlist(List *colTypes, int flag,
bool hack_constants, bool hack_constants,
List *input_tlist, List *input_tlist,
List *refnames_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 *input_plans,
List *refnames_tlist); List *refnames_tlist);
static List *generate_setop_grouplist(SetOperationStmt *op, List *targetlist); 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 * 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), list_make2(lplan, rplan),
refnames_tlist); 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 * concerned, but we must make it look real anyway for the benefit of the
* next plan level up. * next plan level up.
*/ */
tlist = generate_append_tlist(op->colTypes, false, tlist = generate_append_tlist(op->colTypes, op->colCollations, false,
planlist, refnames_tlist); 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 * column is shown as a variable not a constant, else setrefs.c will get
* confused. * confused.
*/ */
tlist = generate_append_tlist(op->colTypes, true, tlist = generate_append_tlist(op->colTypes, op->colCollations, true,
planlist, refnames_tlist); planlist, refnames_tlist);
/* /*
@ -885,6 +885,7 @@ generate_setop_tlist(List *colTypes, int flag,
inputtle->resno, inputtle->resno,
exprType((Node *) inputtle->expr), exprType((Node *) inputtle->expr),
exprTypmod((Node *) inputtle->expr), exprTypmod((Node *) inputtle->expr),
exprCollation((Node *) inputtle->expr),
0); 0);
if (exprType(expr) != colType) if (exprType(expr) != colType)
{ {
@ -936,13 +937,14 @@ generate_setop_tlist(List *colTypes, int flag,
* The Vars are always generated with varno 0. * The Vars are always generated with varno 0.
*/ */
static List * static List *
generate_append_tlist(List *colTypes, bool flag, generate_append_tlist(List *colTypes, List*colCollations, bool flag,
List *input_plans, List *input_plans,
List *refnames_tlist) List *refnames_tlist)
{ {
List *tlist = NIL; List *tlist = NIL;
int resno = 1; int resno = 1;
ListCell *curColType; ListCell *curColType;
ListCell *curColCollation;
ListCell *ref_tl_item; ListCell *ref_tl_item;
int colindex; int colindex;
TargetEntry *tle; TargetEntry *tle;
@ -997,10 +999,11 @@ generate_append_tlist(List *colTypes, bool flag,
* Now we can build the tlist for the Append. * Now we can build the tlist for the Append.
*/ */
colindex = 0; 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); Oid colType = lfirst_oid(curColType);
int32 colTypmod = colTypmods[colindex++]; int32 colTypmod = colTypmods[colindex++];
Oid colColl = lfirst_oid(curColCollation);
TargetEntry *reftle = (TargetEntry *) lfirst(ref_tl_item); TargetEntry *reftle = (TargetEntry *) lfirst(ref_tl_item);
Assert(reftle->resno == resno); Assert(reftle->resno == resno);
@ -1009,6 +1012,7 @@ generate_append_tlist(List *colTypes, bool flag,
resno, resno,
colType, colType,
colTypmod, colTypmod,
colColl,
0); 0);
tle = makeTargetEntry((Expr *) expr, tle = makeTargetEntry((Expr *) expr,
(AttrNumber) resno++, (AttrNumber) resno++,
@ -1025,6 +1029,7 @@ generate_append_tlist(List *colTypes, bool flag,
resno, resno,
INT4OID, INT4OID,
-1, -1,
InvalidOid,
0); 0);
tle = makeTargetEntry((Expr *) expr, tle = makeTargetEntry((Expr *) expr,
(AttrNumber) resno++, (AttrNumber) resno++,
@ -1344,6 +1349,7 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
char *attname; char *attname;
Oid atttypid; Oid atttypid;
int32 atttypmod; int32 atttypmod;
Oid attcollation;
int new_attno; int new_attno;
att = old_tupdesc->attrs[old_attno]; att = old_tupdesc->attrs[old_attno];
@ -1356,6 +1362,7 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
attname = NameStr(att->attname); attname = NameStr(att->attname);
atttypid = att->atttypid; atttypid = att->atttypid;
atttypmod = att->atttypmod; atttypmod = att->atttypmod;
attcollation = att->attcollation;
/* /*
* When we are generating the "translation list" for the parent table * 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), (AttrNumber) (old_attno + 1),
atttypid, atttypid,
atttypmod, atttypmod,
attcollation,
0)); 0));
continue; continue;
} }
@ -1409,6 +1417,7 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
(AttrNumber) (new_attno + 1), (AttrNumber) (new_attno + 1),
atttypid, atttypid,
atttypmod, atttypmod,
attcollation,
0)); 0));
} }

View File

@ -100,7 +100,7 @@ static List *simplify_and_arguments(List *args,
bool *haveNull, bool *forceFalse); bool *haveNull, bool *forceFalse);
static Node *simplify_boolean_equality(Oid opno, List *args); static Node *simplify_boolean_equality(Oid opno, List *args);
static Expr *simplify_function(Oid funcid, 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 has_named_args,
bool allow_inline, bool allow_inline,
eval_const_expressions_context *context); 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, static void recheck_cast_function_args(List *args, Oid result_type,
HeapTuple func_tuple); HeapTuple func_tuple);
static Expr *evaluate_function(Oid funcid, 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, HeapTuple func_tuple,
eval_const_expressions_context *context); eval_const_expressions_context *context);
static Expr *inline_function(Oid funcid, Oid result_type, List *args, 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); expr->args = list_make2(leftop, rightop);
else else
expr->args = list_make1(leftop); expr->args = list_make1(leftop);
expr->collid = select_common_collation(NULL, expr->args, false);
expr->location = -1; expr->location = -1;
return (Expr *) expr; return (Expr *) expr;
} }
@ -1973,7 +1974,7 @@ set_coercionform_dontcare_walker(Node *node, void *context)
*/ */
static bool static bool
rowtype_field_matches(Oid rowtypeid, int fieldnum, rowtype_field_matches(Oid rowtypeid, int fieldnum,
Oid expectedtype, int32 expectedtypmod) Oid expectedtype, int32 expectedtypmod, Oid expectedcollation)
{ {
TupleDesc tupdesc; TupleDesc tupdesc;
Form_pg_attribute attr; Form_pg_attribute attr;
@ -1990,7 +1991,8 @@ rowtype_field_matches(Oid rowtypeid, int fieldnum,
attr = tupdesc->attrs[fieldnum - 1]; attr = tupdesc->attrs[fieldnum - 1];
if (attr->attisdropped || if (attr->attisdropped ||
attr->atttypid != expectedtype || attr->atttypid != expectedtype ||
attr->atttypmod != expectedtypmod) attr->atttypmod != expectedtypmod ||
attr->attcollation != expectedcollation)
{ {
ReleaseTupleDesc(tupdesc); ReleaseTupleDesc(tupdesc);
return false; return false;
@ -2121,6 +2123,7 @@ eval_const_expressions_mutator(Node *node,
int16 typLen; int16 typLen;
bool typByVal; bool typByVal;
Datum pval; Datum pval;
Const *cnst;
Assert(prm->ptype == param->paramtype); Assert(prm->ptype == param->paramtype);
get_typlenbyval(param->paramtype, &typLen, &typByVal); get_typlenbyval(param->paramtype, &typLen, &typByVal);
@ -2128,12 +2131,14 @@ eval_const_expressions_mutator(Node *node,
pval = prm->value; pval = prm->value;
else else
pval = datumCopy(prm->value, typByVal, typLen); pval = datumCopy(prm->value, typByVal, typLen);
return (Node *) makeConst(param->paramtype, cnst = makeConst(param->paramtype,
param->paramtypmod, param->paramtypmod,
(int) typLen, (int) typLen,
pval, pval,
prm->isnull, prm->isnull,
typByVal); typByVal);
cnst->constcollid = param->paramcollation;
return (Node *) cnst;
} }
} }
} }
@ -2173,6 +2178,7 @@ eval_const_expressions_mutator(Node *node,
*/ */
simple = simplify_function(expr->funcid, simple = simplify_function(expr->funcid,
expr->funcresulttype, exprTypmod(node), expr->funcresulttype, exprTypmod(node),
expr->collid,
&args, &args,
has_named_args, true, context); has_named_args, true, context);
if (simple) /* successfully simplified it */ if (simple) /* successfully simplified it */
@ -2190,6 +2196,7 @@ eval_const_expressions_mutator(Node *node,
newexpr->funcretset = expr->funcretset; newexpr->funcretset = expr->funcretset;
newexpr->funcformat = expr->funcformat; newexpr->funcformat = expr->funcformat;
newexpr->args = args; newexpr->args = args;
newexpr->collid = expr->collid;
newexpr->location = expr->location; newexpr->location = expr->location;
return (Node *) newexpr; return (Node *) newexpr;
} }
@ -2221,6 +2228,7 @@ eval_const_expressions_mutator(Node *node,
*/ */
simple = simplify_function(expr->opfuncid, simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1, expr->opresulttype, -1,
expr->collid,
&args, &args,
false, true, context); false, true, context);
if (simple) /* successfully simplified it */ if (simple) /* successfully simplified it */
@ -2250,6 +2258,7 @@ eval_const_expressions_mutator(Node *node,
newexpr->opresulttype = expr->opresulttype; newexpr->opresulttype = expr->opresulttype;
newexpr->opretset = expr->opretset; newexpr->opretset = expr->opretset;
newexpr->args = args; newexpr->args = args;
newexpr->collid = expr->collid;
newexpr->location = expr->location; newexpr->location = expr->location;
return (Node *) newexpr; return (Node *) newexpr;
} }
@ -2314,6 +2323,7 @@ eval_const_expressions_mutator(Node *node,
*/ */
simple = simplify_function(expr->opfuncid, simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1, expr->opresulttype, -1,
expr->collid,
&args, &args,
false, false, context); false, false, context);
if (simple) /* successfully simplified it */ if (simple) /* successfully simplified it */
@ -2342,6 +2352,7 @@ eval_const_expressions_mutator(Node *node,
newexpr->opresulttype = expr->opresulttype; newexpr->opresulttype = expr->opresulttype;
newexpr->opretset = expr->opretset; newexpr->opretset = expr->opretset;
newexpr->args = args; newexpr->args = args;
newexpr->collid = expr->collid;
newexpr->location = expr->location; newexpr->location = expr->location;
return (Node *) newexpr; return (Node *) newexpr;
} }
@ -2493,7 +2504,7 @@ eval_const_expressions_mutator(Node *node,
getTypeInputInfo(expr->resulttype, &infunc, &intypioparam); getTypeInputInfo(expr->resulttype, &infunc, &intypioparam);
simple = simplify_function(outfunc, simple = simplify_function(outfunc,
CSTRINGOID, -1, CSTRINGOID, -1, InvalidOid,
&args, &args,
false, true, context); false, true, context);
if (simple) /* successfully simplified output fn */ if (simple) /* successfully simplified output fn */
@ -2510,8 +2521,11 @@ eval_const_expressions_mutator(Node *node,
Int32GetDatum(-1), Int32GetDatum(-1),
false, true)); false, true));
/* preserve collation of input expression */
simple = simplify_function(infunc, simple = simplify_function(infunc,
expr->resulttype, -1, expr->resulttype,
-1,
exprCollation((Node *) arg),
&args, &args,
false, true, context); false, true, context);
if (simple) /* successfully simplified input fn */ if (simple) /* successfully simplified input fn */
@ -2690,6 +2704,7 @@ eval_const_expressions_mutator(Node *node,
/* Otherwise we need a new CASE node */ /* Otherwise we need a new CASE node */
newcase = makeNode(CaseExpr); newcase = makeNode(CaseExpr);
newcase->casetype = caseexpr->casetype; newcase->casetype = caseexpr->casetype;
newcase->casecollation = caseexpr->casecollation;
newcase->arg = (Expr *) newarg; newcase->arg = (Expr *) newarg;
newcase->args = newargs; newcase->args = newargs;
newcase->defresult = (Expr *) defresult; newcase->defresult = (Expr *) defresult;
@ -2782,6 +2797,7 @@ eval_const_expressions_mutator(Node *node,
newcoalesce = makeNode(CoalesceExpr); newcoalesce = makeNode(CoalesceExpr);
newcoalesce->coalescetype = coalesceexpr->coalescetype; newcoalesce->coalescetype = coalesceexpr->coalescetype;
newcoalesce->coalescecollation = coalesceexpr->coalescecollation;
newcoalesce->args = newargs; newcoalesce->args = newargs;
newcoalesce->location = coalesceexpr->location; newcoalesce->location = coalesceexpr->location;
return (Node *) newcoalesce; return (Node *) newcoalesce;
@ -2811,11 +2827,13 @@ eval_const_expressions_mutator(Node *node,
if (rowtype_field_matches(((Var *) arg)->vartype, if (rowtype_field_matches(((Var *) arg)->vartype,
fselect->fieldnum, fselect->fieldnum,
fselect->resulttype, fselect->resulttype,
fselect->resulttypmod)) fselect->resulttypmod,
fselect->resultcollation))
return (Node *) makeVar(((Var *) arg)->varno, return (Node *) makeVar(((Var *) arg)->varno,
fselect->fieldnum, fselect->fieldnum,
fselect->resulttype, fselect->resulttype,
fselect->resulttypmod, fselect->resulttypmod,
fselect->resultcollation,
((Var *) arg)->varlevelsup); ((Var *) arg)->varlevelsup);
} }
if (arg && IsA(arg, RowExpr)) if (arg && IsA(arg, RowExpr))
@ -2831,9 +2849,11 @@ eval_const_expressions_mutator(Node *node,
if (rowtype_field_matches(rowexpr->row_typeid, if (rowtype_field_matches(rowexpr->row_typeid,
fselect->fieldnum, fselect->fieldnum,
fselect->resulttype, fselect->resulttype,
fselect->resulttypmod) && fselect->resulttypmod,
fselect->resultcollation) &&
fselect->resulttype == exprType(fld) && fselect->resulttype == exprType(fld) &&
fselect->resulttypmod == exprTypmod(fld)) fselect->resulttypmod == exprTypmod(fld) &&
fselect->resultcollation == exprCollation(fld))
return fld; return fld;
} }
} }
@ -2842,6 +2862,7 @@ eval_const_expressions_mutator(Node *node,
newfselect->fieldnum = fselect->fieldnum; newfselect->fieldnum = fselect->fieldnum;
newfselect->resulttype = fselect->resulttype; newfselect->resulttype = fselect->resulttype;
newfselect->resulttypmod = fselect->resulttypmod; newfselect->resulttypmod = fselect->resulttypmod;
newfselect->resultcollation = fselect->resultcollation;
return (Node *) newfselect; return (Node *) newfselect;
} }
if (IsA(node, NullTest)) 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. * pass-by-reference, and it may get modified even if simplification fails.
*/ */
static Expr * 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, List **args,
bool has_named_args, bool has_named_args,
bool allow_inline, 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)) else if (((Form_pg_proc) GETSTRUCT(func_tuple))->pronargs > list_length(*args))
*args = add_function_defaults(*args, result_type, func_tuple, context); *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); func_tuple, context);
if (!newexpr && allow_inline) if (!newexpr && allow_inline)
@ -3581,7 +3602,8 @@ recheck_cast_function_args(List *args, Oid result_type, HeapTuple func_tuple)
* simplify the function. * simplify the function.
*/ */
static Expr * 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, HeapTuple func_tuple,
eval_const_expressions_context *context) 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->funcretset = false;
newexpr->funcformat = COERCE_DONTCARE; /* doesn't matter */ newexpr->funcformat = COERCE_DONTCARE; /* doesn't matter */
newexpr->args = args; newexpr->args = args;
newexpr->collid = collid;
newexpr->location = -1; newexpr->location = -1;
return evaluate_expr((Expr *) newexpr, result_type, result_typmod); return evaluate_expr((Expr *) newexpr, result_type, result_typmod);

View File

@ -198,10 +198,12 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
info->indexkeys = (int *) palloc(sizeof(int) * ncolumns); info->indexkeys = (int *) palloc(sizeof(int) * ncolumns);
info->opfamily = (Oid *) palloc(sizeof(Oid) * ncolumns); info->opfamily = (Oid *) palloc(sizeof(Oid) * ncolumns);
info->opcintype = (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++) for (i = 0; i < ncolumns; i++)
{ {
info->indexkeys[i] = index->indkey.values[i]; info->indexkeys[i] = index->indkey.values[i];
info->indexcollations[i] = indexRelation->rd_indcollation[i];
info->opfamily[i] = indexRelation->rd_opfamily[i]; info->opfamily[i] = indexRelation->rd_opfamily[i];
info->opcintype[i] = indexRelation->rd_opcintype[i]; info->opcintype[i] = indexRelation->rd_opcintype[i];
} }
@ -634,6 +636,7 @@ get_relation_constraints(PlannerInfo *root,
i, i,
att->atttypid, att->atttypid,
att->atttypmod, att->atttypmod,
att->attcollation,
0); 0);
ntest->nulltesttype = IS_NOT_NULL; ntest->nulltesttype = IS_NOT_NULL;
ntest->argisrow = type_is_rowtype(att->atttypid); ntest->argisrow = type_is_rowtype(att->atttypid);
@ -797,6 +800,7 @@ build_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
attrno, attrno,
att_tup->atttypid, att_tup->atttypid,
att_tup->atttypmod, att_tup->atttypmod,
att_tup->attcollation,
0); 0);
tlist = lappend(tlist, tlist = lappend(tlist,

View File

@ -912,6 +912,7 @@ arrayconst_startup_fn(Node *clause, PredIterInfo info)
state->constexpr.xpr.type = T_Const; state->constexpr.xpr.type = T_Const;
state->constexpr.consttype = ARR_ELEMTYPE(arrayval); state->constexpr.consttype = ARR_ELEMTYPE(arrayval);
state->constexpr.consttypmod = -1; state->constexpr.consttypmod = -1;
state->constexpr.constcollid = arrayconst->constcollid;
state->constexpr.constlen = elmlen; state->constexpr.constlen = elmlen;
state->constexpr.constbyval = elmbyval; state->constexpr.constbyval = elmbyval;
lsecond(state->opexpr.args) = &state->constexpr; lsecond(state->opexpr.args) = &state->constexpr;

View File

@ -1203,6 +1203,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
ListCell *left_tlist, ListCell *left_tlist,
*lct, *lct,
*lcm, *lcm,
*lcc,
*l; *l;
List *targetvars, List *targetvars,
*targetnames, *targetnames,
@ -1296,10 +1297,11 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
targetnames = NIL; targetnames = NIL;
left_tlist = list_head(leftmostQuery->targetList); 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); Oid colType = lfirst_oid(lct);
int32 colTypmod = lfirst_int(lcm); int32 colTypmod = lfirst_int(lcm);
Oid colCollation = lfirst_oid(lcc);
TargetEntry *lefttle = (TargetEntry *) lfirst(left_tlist); TargetEntry *lefttle = (TargetEntry *) lfirst(left_tlist);
char *colName; char *colName;
TargetEntry *tle; TargetEntry *tle;
@ -1311,6 +1313,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
lefttle->resno, lefttle->resno,
colType, colType,
colTypmod, colTypmod,
colCollation,
0); 0);
var->location = exprLocation((Node *) lefttle->expr); var->location = exprLocation((Node *) lefttle->expr);
tle = makeTargetEntry((Expr *) var, tle = makeTargetEntry((Expr *) var,
@ -1418,7 +1421,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
* Recursively transform leaves and internal nodes of a set-op tree * Recursively transform leaves and internal nodes of a set-op tree
* *
* In addition to returning the transformed node, we return a list of * 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 * 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 * internal recursion of this function. At the upper levels we use
* SetToDefault nodes for this purpose, since they carry exactly the fields * SetToDefault nodes for this purpose, since they carry exactly the fields
@ -1591,6 +1594,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
*colInfo = NIL; *colInfo = NIL;
op->colTypes = NIL; op->colTypes = NIL;
op->colTypmods = NIL; op->colTypmods = NIL;
op->colCollations = NIL;
op->groupClauses = NIL; op->groupClauses = NIL;
forboth(lci, lcolinfo, rci, rcolinfo) forboth(lci, lcolinfo, rci, rcolinfo)
{ {
@ -1604,6 +1608,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
SetToDefault *rescolnode; SetToDefault *rescolnode;
Oid rescoltype; Oid rescoltype;
int32 rescoltypmod; int32 rescoltypmod;
Oid rescolcoll;
/* select common type, same as CASE et al */ /* select common type, same as CASE et al */
rescoltype = select_common_type(pstate, rescoltype = select_common_type(pstate,
@ -1615,6 +1620,12 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
rescoltypmod = lcoltypmod; rescoltypmod = lcoltypmod;
else else
rescoltypmod = -1; 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 * Verify the coercions are actually possible. If not, we'd fail
@ -1643,11 +1654,13 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
rescolnode = makeNode(SetToDefault); rescolnode = makeNode(SetToDefault);
rescolnode->typeId = rescoltype; rescolnode->typeId = rescoltype;
rescolnode->typeMod = rescoltypmod; rescolnode->typeMod = rescoltypmod;
rescolnode->collid = rescolcoll;
rescolnode->location = exprLocation(bestexpr); rescolnode->location = exprLocation(bestexpr);
*colInfo = lappend(*colInfo, rescolnode); *colInfo = lappend(*colInfo, rescolnode);
op->colTypes = lappend_oid(op->colTypes, rescoltype); op->colTypes = lappend_oid(op->colTypes, rescoltype);
op->colTypmods = lappend_int(op->colTypmods, rescoltypmod); op->colTypmods = lappend_int(op->colTypmods, rescoltypmod);
op->colCollations = lappend_oid(op->colCollations, rescolcoll);
/* /*
* For all cases except UNION ALL, identify the grouping operators * For all cases except UNION ALL, identify the grouping operators

View File

@ -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 %type <list> func_name handler_name qual_Op qual_all_Op subquery_Op
opt_class opt_inline_handler opt_validator validator_clause opt_class opt_inline_handler opt_validator validator_clause
opt_collate
%type <range> qualified_name OptConstrFromTable %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_generic_opt_list copy_generic_opt_arg_list
%type <list> copy_options %type <list> copy_options
%type <typnam> Typename SimpleTypename ConstTypename %type <typnam> Typename SimpleTypename SimpleTypenameWithoutCollation
ConstTypename
GenericType Numeric opt_float GenericType Numeric opt_float
Character ConstCharacter Character ConstCharacter
CharacterWithLength CharacterWithoutLength CharacterWithLength CharacterWithoutLength
@ -5323,38 +5325,45 @@ index_params: index_elem { $$ = list_make1($1); }
* expressions in parens. For backwards-compatibility reasons, we allow * expressions in parens. For backwards-compatibility reasons, we allow
* an expression that's just a function call to be written without parens. * 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); $$ = makeNode(IndexElem);
$$->name = $1; $$->name = $1;
$$->expr = NULL; $$->expr = NULL;
$$->indexcolname = NULL; $$->indexcolname = NULL;
$$->opclass = $2; $$->collation = $2;
$$->ordering = $3; $$->opclass = $3;
$$->nulls_ordering = $4; $$->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); $$ = makeNode(IndexElem);
$$->name = NULL; $$->name = NULL;
$$->expr = $1; $$->expr = $1;
$$->indexcolname = NULL; $$->indexcolname = NULL;
$$->opclass = $2; $$->collation = $2;
$$->ordering = $3; $$->opclass = $3;
$$->nulls_ordering = $4; $$->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); $$ = makeNode(IndexElem);
$$->name = NULL; $$->name = NULL;
$$->expr = $2; $$->expr = $2;
$$->indexcolname = NULL; $$->indexcolname = NULL;
$$->opclass = $4; $$->collation = $4;
$$->ordering = $5; $$->opclass = $5;
$$->nulls_ordering = $6; $$->ordering = $6;
$$->nulls_ordering = $7;
} }
; ;
opt_collate: COLLATE any_name { $$ = $2; }
| /*EMPTY*/ { $$ = NIL; }
;
opt_class: any_name { $$ = $1; } opt_class: any_name { $$ = $1; }
| USING any_name { $$ = $2; } | USING any_name { $$ = $2; }
| /*EMPTY*/ { $$ = NIL; } | /*EMPTY*/ { $$ = NIL; }
@ -8776,6 +8785,13 @@ opt_array_bounds:
; ;
SimpleTypename: SimpleTypename:
SimpleTypenameWithoutCollation opt_collate
{
$$ = $1;
$$->collnames = $2;
}
SimpleTypenameWithoutCollation:
GenericType { $$ = $1; } GenericType { $$ = $1; }
| Numeric { $$ = $1; } | Numeric { $$ = $1; }
| Bit { $$ = $1; } | Bit { $$ = $1; }
@ -9811,6 +9827,14 @@ c_expr: columnref { $$ = $1; }
r->location = @1; r->location = @1;
$$ = (Node *)r; $$ = (Node *)r;
} }
| c_expr COLLATE any_name
{
CollateClause *n = makeNode(CollateClause);
n->arg = (Expr *) $1;
n->collnames = $3;
n->location = @2;
$$ = (Node *)n;
}
; ;
/* /*

View File

@ -723,6 +723,7 @@ build_aggregate_fnexprs(Oid *agg_input_types,
Oid agg_result_type, Oid agg_result_type,
Oid transfn_oid, Oid transfn_oid,
Oid finalfn_oid, Oid finalfn_oid,
Oid collation,
Expr **transfnexpr, Expr **transfnexpr,
Expr **finalfnexpr) Expr **finalfnexpr)
{ {
@ -741,6 +742,7 @@ build_aggregate_fnexprs(Oid *agg_input_types,
argp->paramid = -1; argp->paramid = -1;
argp->paramtype = agg_state_type; argp->paramtype = agg_state_type;
argp->paramtypmod = -1; argp->paramtypmod = -1;
argp->paramcollation = collation;
argp->location = -1; argp->location = -1;
args = list_make1(argp); args = list_make1(argp);
@ -752,6 +754,7 @@ build_aggregate_fnexprs(Oid *agg_input_types,
argp->paramid = -1; argp->paramid = -1;
argp->paramtype = agg_input_types[i]; argp->paramtype = agg_input_types[i];
argp->paramtypmod = -1; argp->paramtypmod = -1;
argp->paramcollation = collation;
argp->location = -1; argp->location = -1;
args = lappend(args, argp); args = lappend(args, argp);
} }
@ -759,6 +762,7 @@ build_aggregate_fnexprs(Oid *agg_input_types,
*transfnexpr = (Expr *) makeFuncExpr(transfn_oid, *transfnexpr = (Expr *) makeFuncExpr(transfn_oid,
agg_state_type, agg_state_type,
args, args,
collation,
COERCE_DONTCARE); COERCE_DONTCARE);
/* see if we have a final function */ /* see if we have a final function */
@ -776,11 +780,13 @@ build_aggregate_fnexprs(Oid *agg_input_types,
argp->paramid = -1; argp->paramid = -1;
argp->paramtype = agg_state_type; argp->paramtype = agg_state_type;
argp->paramtypmod = -1; argp->paramtypmod = -1;
argp->paramcollation = collation;
argp->location = -1; argp->location = -1;
args = list_make1(argp); args = list_make1(argp);
*finalfnexpr = (Expr *) makeFuncExpr(finalfn_oid, *finalfnexpr = (Expr *) makeFuncExpr(finalfn_oid,
agg_result_type, agg_result_type,
args, args,
collation,
COERCE_DONTCARE); COERCE_DONTCARE);
} }

View File

@ -613,7 +613,8 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
tupdesc = BuildDescFromLists(rte->eref->colnames, tupdesc = BuildDescFromLists(rte->eref->colnames,
rte->funccoltypes, rte->funccoltypes,
rte->funccoltypmods); rte->funccoltypmods,
rte->funccolcollations);
CheckAttributeNamesTypes(tupdesc, RELKIND_COMPOSITE_TYPE, false); CheckAttributeNamesTypes(tupdesc, RELKIND_COMPOSITE_TYPE, false);
} }
@ -1935,6 +1936,7 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle,
bool resolveUnknown) bool resolveUnknown)
{ {
Oid restype = exprType((Node *) tle->expr); Oid restype = exprType((Node *) tle->expr);
Oid rescollation = exprCollation((Node *) tle->expr);
Oid sortop; Oid sortop;
Oid eqop; Oid eqop;
bool hashable; bool hashable;
@ -2018,6 +2020,12 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle,
break; 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); cancel_parser_errposition_callback(&pcbstate);
/* avoid making duplicate sortlist entries */ /* avoid making duplicate sortlist entries */

View File

@ -16,6 +16,7 @@
#include "catalog/pg_cast.h" #include "catalog/pg_cast.h"
#include "catalog/pg_class.h" #include "catalog/pg_class.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_inherits_fn.h" #include "catalog/pg_inherits_fn.h"
#include "catalog/pg_proc.h" #include "catalog/pg_proc.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
@ -216,6 +217,7 @@ coerce_type(ParseState *pstate, Node *node,
newcon->consttype = baseTypeId; newcon->consttype = baseTypeId;
newcon->consttypmod = inputTypeMod; newcon->consttypmod = inputTypeMod;
newcon->constcollid = get_typcollation(newcon->consttype);
newcon->constlen = typeLen(targetType); newcon->constlen = typeLen(targetType);
newcon->constbyval = typeByVal(targetType); newcon->constbyval = typeByVal(targetType);
newcon->constisnull = con->constisnull; newcon->constisnull = con->constisnull;
@ -277,6 +279,14 @@ coerce_type(ParseState *pstate, Node *node,
if (result) if (result)
return 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, pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext,
&funcId); &funcId);
if (pathtype != COERCION_PATH_NONE) if (pathtype != COERCION_PATH_NONE)
@ -718,6 +728,7 @@ build_coercion_expression(Node *node,
FuncExpr *fexpr; FuncExpr *fexpr;
List *args; List *args;
Const *cons; Const *cons;
Oid collation;
Assert(OidIsValid(funcId)); Assert(OidIsValid(funcId));
@ -749,7 +760,9 @@ build_coercion_expression(Node *node,
args = lappend(args, cons); 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; fexpr->location = location;
return (Node *) fexpr; return (Node *) fexpr;
} }
@ -2081,3 +2094,120 @@ typeIsOfTypedTable(Oid reltypeId, Oid reloftypeId)
return result; 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;
}

View File

@ -14,11 +14,13 @@
*/ */
#include "postgres.h" #include "postgres.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "nodes/nodeFuncs.h" #include "nodes/nodeFuncs.h"
#include "parser/analyze.h" #include "parser/analyze.h"
#include "parser/parse_cte.h" #include "parser/parse_cte.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/lsyscache.h"
/* Enumeration of contexts in which a self-reference is disallowed */ /* Enumeration of contexts in which a self-reference is disallowed */
@ -263,11 +265,13 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
*/ */
ListCell *lctlist, ListCell *lctlist,
*lctyp, *lctyp,
*lctypmod; *lctypmod,
*lccoll;
int varattno; int varattno;
lctyp = list_head(cte->ctecoltypes); lctyp = list_head(cte->ctecoltypes);
lctypmod = list_head(cte->ctecoltypmods); lctypmod = list_head(cte->ctecoltypmods);
lccoll = list_head(cte->ctecolcollations);
varattno = 0; varattno = 0;
foreach(lctlist, query->targetList) foreach(lctlist, query->targetList)
{ {
@ -278,7 +282,7 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
continue; continue;
varattno++; varattno++;
Assert(varattno == te->resno); 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"); elog(ERROR, "wrong number of output columns in WITH");
texpr = (Node *) te->expr; texpr = (Node *) te->expr;
if (exprType(texpr) != lfirst_oid(lctyp) || if (exprType(texpr) != lfirst_oid(lctyp) ||
@ -293,10 +297,20 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
exprTypmod(texpr))), exprTypmod(texpr))),
errhint("Cast the output of the non-recursive term to the correct type."), errhint("Cast the output of the non-recursive term to the correct type."),
parser_errposition(pstate, exprLocation(texpr)))); 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); lctyp = lnext(lctyp);
lctypmod = lnext(lctypmod); 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"); elog(ERROR, "wrong number of output columns in WITH");
} }
} }
@ -331,7 +345,7 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist)
* handling.) * handling.)
*/ */
cte->ctecolnames = copyObject(cte->aliascolnames); cte->ctecolnames = copyObject(cte->aliascolnames);
cte->ctecoltypes = cte->ctecoltypmods = NIL; cte->ctecoltypes = cte->ctecoltypmods = cte->ctecolcollations = NIL;
numaliases = list_length(cte->aliascolnames); numaliases = list_length(cte->aliascolnames);
varattno = 0; varattno = 0;
foreach(tlistitem, tlist) foreach(tlistitem, tlist)
@ -339,6 +353,7 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist)
TargetEntry *te = (TargetEntry *) lfirst(tlistitem); TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
Oid coltype; Oid coltype;
int32 coltypmod; int32 coltypmod;
Oid colcoll;
if (te->resjunk) if (te->resjunk)
continue; continue;
@ -353,6 +368,7 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist)
} }
coltype = exprType((Node *) te->expr); coltype = exprType((Node *) te->expr);
coltypmod = exprTypmod((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 * 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; coltype = TEXTOID;
coltypmod = -1; /* should be -1 already, but be sure */ coltypmod = -1; /* should be -1 already, but be sure */
colcoll = DEFAULT_COLLATION_OID;
} }
cte->ctecoltypes = lappend_oid(cte->ctecoltypes, coltype); cte->ctecoltypes = lappend_oid(cte->ctecoltypes, coltype);
cte->ctecoltypmods = lappend_int(cte->ctecoltypmods, coltypmod); cte->ctecoltypmods = lappend_int(cte->ctecoltypmods, coltypmod);
cte->ctecolcollations = lappend_oid(cte->ctecolcollations, colcoll);
} }
if (varattno < numaliases) if (varattno < numaliases)
ereport(ERROR, ereport(ERROR,

View File

@ -65,6 +65,7 @@ static Node *transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte,
static Node *transformIndirection(ParseState *pstate, Node *basenode, static Node *transformIndirection(ParseState *pstate, Node *basenode,
List *indirection); List *indirection);
static Node *transformTypeCast(ParseState *pstate, TypeCast *tc); static Node *transformTypeCast(ParseState *pstate, TypeCast *tc);
static Node *transformCollateClause(ParseState *pstate, CollateClause *c);
static Node *make_row_comparison_op(ParseState *pstate, List *opname, static Node *make_row_comparison_op(ParseState *pstate, List *opname,
List *largs, List *rargs, int location); List *largs, List *rargs, int location);
static Node *make_row_distinct_op(ParseState *pstate, List *opname, static Node *make_row_distinct_op(ParseState *pstate, List *opname,
@ -146,6 +147,12 @@ transformExpr(ParseState *pstate, Node *expr)
{ {
TypeCast *tc = (TypeCast *) 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 * If the subject of the typecast is an ARRAY[] construct and
* the target type is an array type, we invoke * the target type is an array type, we invoke
@ -185,6 +192,10 @@ transformExpr(ParseState *pstate, Node *expr)
break; break;
} }
case T_CollateClause:
result = transformCollateClause(pstate, (CollateClause *) expr);
break;
case T_A_Expr: case T_A_Expr:
{ {
A_Expr *a = (A_Expr *) expr; A_Expr *a = (A_Expr *) expr;
@ -423,6 +434,7 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
exprType(result), exprType(result),
InvalidOid, InvalidOid,
exprTypmod(result), exprTypmod(result),
exprCollation(result),
subscripts, subscripts,
NULL); NULL);
subscripts = NIL; subscripts = NIL;
@ -444,6 +456,7 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
exprType(result), exprType(result),
InvalidOid, InvalidOid,
exprTypmod(result), exprTypmod(result),
exprCollation(result),
subscripts, subscripts,
NULL); NULL);
@ -1267,6 +1280,7 @@ transformCaseExpr(ParseState *pstate, CaseExpr *c)
placeholder = makeNode(CaseTestExpr); placeholder = makeNode(CaseTestExpr);
placeholder->typeId = exprType(arg); placeholder->typeId = exprType(arg);
placeholder->typeMod = exprTypmod(arg); placeholder->typeMod = exprTypmod(arg);
placeholder->collation = exprCollation(arg);
} }
else else
placeholder = NULL; placeholder = NULL;
@ -1351,6 +1365,8 @@ transformCaseExpr(ParseState *pstate, CaseExpr *c)
"CASE/WHEN"); "CASE/WHEN");
} }
newc->casecollation = select_common_collation(pstate, resultexprs, true);
newc->location = c->location; newc->location = c->location;
return (Node *) newc; return (Node *) newc;
@ -1461,6 +1477,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
param->paramid = tent->resno; param->paramid = tent->resno;
param->paramtype = exprType((Node *) tent->expr); param->paramtype = exprType((Node *) tent->expr);
param->paramtypmod = exprTypmod((Node *) tent->expr); param->paramtypmod = exprTypmod((Node *) tent->expr);
param->paramcollation = exprCollation((Node *) tent->expr);
param->location = -1; param->location = -1;
right_list = lappend(right_list, param); right_list = lappend(right_list, param);
@ -1704,6 +1721,7 @@ transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c)
} }
newc->args = newcoercedargs; newc->args = newcoercedargs;
newc->coalescecollation = select_common_collation(pstate, newcoercedargs, true);
newc->location = c->location; newc->location = c->location;
return (Node *) newc; return (Node *) newc;
} }
@ -1728,6 +1746,7 @@ transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m)
} }
newm->minmaxtype = select_common_type(pstate, newargs, funcname, NULL); newm->minmaxtype = select_common_type(pstate, newargs, funcname, NULL);
newm->collid = select_common_collation(pstate, newargs, false);
/* Convert arguments if necessary */ /* Convert arguments if necessary */
foreach(args, newargs) foreach(args, newargs)
@ -2082,6 +2101,36 @@ transformTypeCast(ParseState *pstate, TypeCast *tc)
return result; return result;
} }
/*
* Handle an explicit COLLATE clause.
*
* Transform the argument, and look up the collation name.
*/
static Node *
transformCollateClause(ParseState *pstate, CollateClause *c)
{
CollateClause *newc;
Oid argtype;
newc = makeNode(CollateClause);
newc->arg = (Expr *) transformExpr(pstate, (Node *) c->arg);
argtype = exprType((Node *) newc->arg);
/* The unknown type is not collatable, but coerce_type() takes
* care of it separately, so we'll let it go here. */
if (!type_is_collatable(argtype) && argtype != UNKNOWNOID)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("collations are not supported by type %s",
format_type_be(argtype))));
newc->collOid = LookupCollation(pstate, c->collnames, c->location);
newc->collnames = c->collnames;
newc->location = c->location;
return (Node *) newc;
}
/* /*
* Transform a "row compare-op row" construct * Transform a "row compare-op row" construct
* *
@ -2103,6 +2152,7 @@ make_row_comparison_op(ParseState *pstate, List *opname,
List *opexprs; List *opexprs;
List *opnos; List *opnos;
List *opfamilies; List *opfamilies;
List *collids;
ListCell *l, ListCell *l,
*r; *r;
List **opfamily_lists; List **opfamily_lists;
@ -2273,6 +2323,7 @@ make_row_comparison_op(ParseState *pstate, List *opname,
* possibility that make_op inserted coercion operations. * possibility that make_op inserted coercion operations.
*/ */
opnos = NIL; opnos = NIL;
collids = NIL;
largs = NIL; largs = NIL;
rargs = NIL; rargs = NIL;
foreach(l, opexprs) foreach(l, opexprs)
@ -2280,6 +2331,7 @@ make_row_comparison_op(ParseState *pstate, List *opname,
OpExpr *cmp = (OpExpr *) lfirst(l); OpExpr *cmp = (OpExpr *) lfirst(l);
opnos = lappend_oid(opnos, cmp->opno); opnos = lappend_oid(opnos, cmp->opno);
collids = lappend_oid(collids, cmp->collid);
largs = lappend(largs, linitial(cmp->args)); largs = lappend(largs, linitial(cmp->args));
rargs = lappend(rargs, lsecond(cmp->args)); rargs = lappend(rargs, lsecond(cmp->args));
} }
@ -2288,6 +2340,7 @@ make_row_comparison_op(ParseState *pstate, List *opname,
rcexpr->rctype = rctype; rcexpr->rctype = rctype;
rcexpr->opnos = opnos; rcexpr->opnos = opnos;
rcexpr->opfamilies = opfamilies; rcexpr->opfamilies = opfamilies;
rcexpr->collids = collids;
rcexpr->largs = largs; rcexpr->largs = largs;
rcexpr->rargs = rargs; rcexpr->rargs = rargs;

View File

@ -78,6 +78,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
bool retset; bool retset;
int nvargs; int nvargs;
FuncDetailCode fdresult; FuncDetailCode fdresult;
Oid funccollid;
/* /*
* Most of the rest of the parser just assumes that functions do not have * 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 */ /* perform the necessary typecasting of arguments */
make_fn_arguments(pstate, fargs, actual_arg_types, declared_arg_types); 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 * If it's a variadic function call, transform the last nvargs arguments
* into an array --- unless it's an "any" variadic. * into an array --- unless it's an "any" variadic.
@ -383,6 +390,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
funcexpr->funcretset = retset; funcexpr->funcretset = retset;
funcexpr->funcformat = COERCE_EXPLICIT_CALL; funcexpr->funcformat = COERCE_EXPLICIT_CALL;
funcexpr->args = fargs; funcexpr->args = fargs;
funcexpr->collid = funccollid;
funcexpr->location = location; funcexpr->location = location;
retval = (Node *) funcexpr; retval = (Node *) funcexpr;
@ -396,6 +404,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
aggref->aggtype = rettype; aggref->aggtype = rettype;
/* args, aggorder, aggdistinct will be set by transformAggregateCall */ /* args, aggorder, aggdistinct will be set by transformAggregateCall */
aggref->aggstar = agg_star; aggref->aggstar = agg_star;
aggref->collid = funccollid;
/* agglevelsup will be set by transformAggregateCall */ /* agglevelsup will be set by transformAggregateCall */
aggref->location = location; aggref->location = location;
@ -453,6 +462,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
/* winref will be set by transformWindowFuncCall */ /* winref will be set by transformWindowFuncCall */
wfunc->winstar = agg_star; wfunc->winstar = agg_star;
wfunc->winagg = (fdresult == FUNCDETAIL_AGGREGATE); wfunc->winagg = (fdresult == FUNCDETAIL_AGGREGATE);
wfunc->collid = funccollid;
wfunc->location = location; wfunc->location = location;
/* /*
@ -1303,7 +1313,7 @@ FuncNameAsType(List *funcname)
Oid result; Oid result;
Type typtup; Type typtup;
typtup = LookupTypeName(NULL, makeTypeNameFromNameList(funcname), NULL); typtup = LookupTypeName(NULL, makeTypeNameFromNameList(funcname), NULL, NULL);
if (typtup == NULL) if (typtup == NULL)
return InvalidOid; return InvalidOid;
@ -1380,6 +1390,7 @@ ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg,
fselect->fieldnum = i + 1; fselect->fieldnum = i + 1;
fselect->resulttype = att->atttypid; fselect->resulttype = att->atttypid;
fselect->resulttypmod = att->atttypmod; fselect->resulttypmod = att->atttypmod;
fselect->resultcollation = att->attcollation;
return (Node *) fselect; return (Node *) fselect;
} }
} }
@ -1489,7 +1500,7 @@ LookupTypeNameOid(const TypeName *typename)
Oid result; Oid result;
Type typtup; Type typtup;
typtup = LookupTypeName(NULL, typename, NULL); typtup = LookupTypeName(NULL, typename, NULL, NULL);
if (typtup == NULL) if (typtup == NULL)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT), (errcode(ERRCODE_UNDEFINED_OBJECT),

View File

@ -189,10 +189,11 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno, int location)
sublevels_up; sublevels_up;
Oid vartypeid; Oid vartypeid;
int32 type_mod; int32 type_mod;
Oid varcollid;
vnum = RTERangeTablePosn(pstate, rte, &sublevels_up); vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
get_rte_attribute_type(rte, attrno, &vartypeid, &type_mod); get_rte_attribute_type(rte, attrno, &vartypeid, &type_mod, &varcollid);
result = makeVar(vnum, attrno, vartypeid, type_mod, sublevels_up); result = makeVar(vnum, attrno, vartypeid, type_mod, varcollid, sublevels_up);
result->location = location; result->location = location;
return result; return result;
} }
@ -269,6 +270,7 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod)
* elementType OID of array's element type (fetch with transformArrayType, * elementType OID of array's element type (fetch with transformArrayType,
* or pass InvalidOid to do it here) * or pass InvalidOid to do it here)
* arrayTypMod typmod for the array (which is also typmod for the elements) * 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) * indirection Untransformed list of subscripts (must not be NIL)
* assignFrom NULL for array fetch, else transformed expression for source. * assignFrom NULL for array fetch, else transformed expression for source.
*/ */
@ -278,6 +280,7 @@ transformArraySubscripts(ParseState *pstate,
Oid arrayType, Oid arrayType,
Oid elementType, Oid elementType,
int32 arrayTypMod, int32 arrayTypMod,
Oid arrayColl,
List *indirection, List *indirection,
Node *assignFrom) Node *assignFrom)
{ {
@ -404,6 +407,7 @@ transformArraySubscripts(ParseState *pstate,
aref->refarraytype = arrayType; aref->refarraytype = arrayType;
aref->refelemtype = elementType; aref->refelemtype = elementType;
aref->reftypmod = arrayTypMod; aref->reftypmod = arrayTypMod;
aref->refcollid = arrayColl;
aref->refupperindexpr = upperIndexpr; aref->refupperindexpr = upperIndexpr;
aref->reflowerindexpr = lowerIndexpr; aref->reflowerindexpr = lowerIndexpr;
aref->refexpr = (Expr *) arrayBase; aref->refexpr = (Expr *) arrayBase;

View File

@ -782,6 +782,7 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
List *args; List *args;
Oid rettype; Oid rettype;
OpExpr *result; OpExpr *result;
Oid opcollid;
/* Select the operator */ /* Select the operator */
if (rtree == NULL) if (rtree == NULL)
@ -861,6 +862,12 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
/* perform the necessary typecasting of arguments */ /* perform the necessary typecasting of arguments */
make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types); 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 */ /* and build the expression node */
result = makeNode(OpExpr); result = makeNode(OpExpr);
result->opno = oprid(tup); result->opno = oprid(tup);
@ -868,6 +875,7 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
result->opresulttype = rettype; result->opresulttype = rettype;
result->opretset = get_func_retset(opform->oprcode); result->opretset = get_func_retset(opform->oprcode);
result->args = args; result->args = args;
result->collid = opcollid;
result->location = location; result->location = location;
ReleaseSysCache(tup); ReleaseSysCache(tup);
@ -896,6 +904,7 @@ make_scalar_array_op(ParseState *pstate, List *opname,
List *args; List *args;
Oid rettype; Oid rettype;
ScalarArrayOpExpr *result; ScalarArrayOpExpr *result;
Oid opcollid;
ltypeId = exprType(ltree); ltypeId = exprType(ltree);
atypeId = exprType(rtree); atypeId = exprType(rtree);
@ -990,12 +999,19 @@ make_scalar_array_op(ParseState *pstate, List *opname,
/* perform the necessary typecasting of arguments */ /* perform the necessary typecasting of arguments */
make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types); 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 */ /* and build the expression node */
result = makeNode(ScalarArrayOpExpr); result = makeNode(ScalarArrayOpExpr);
result->opno = oprid(tup); result->opno = oprid(tup);
result->opfuncid = opform->oprcode; result->opfuncid = opform->oprcode;
result->useOr = useOr; result->useOr = useOr;
result->args = args; result->args = args;
result->collid = opcollid;
result->location = location; result->location = location;
ReleaseSysCache(tup); ReleaseSysCache(tup);

View File

@ -30,6 +30,7 @@
#include "nodes/nodeFuncs.h" #include "nodes/nodeFuncs.h"
#include "parser/parse_param.h" #include "parser/parse_param.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/lsyscache.h"
typedef struct FixedParamState typedef struct FixedParamState
@ -113,6 +114,7 @@ fixed_paramref_hook(ParseState *pstate, ParamRef *pref)
param->paramid = paramno; param->paramid = paramno;
param->paramtype = parstate->paramTypes[paramno - 1]; param->paramtype = parstate->paramTypes[paramno - 1];
param->paramtypmod = -1; param->paramtypmod = -1;
param->paramcollation = get_typcollation(param->paramtype);
param->location = pref->location; param->location = pref->location;
return (Node *) param; return (Node *) param;
@ -165,6 +167,7 @@ variable_paramref_hook(ParseState *pstate, ParamRef *pref)
param->paramid = paramno; param->paramid = paramno;
param->paramtype = *pptype; param->paramtype = *pptype;
param->paramtypmod = -1; param->paramtypmod = -1;
param->paramcollation = get_typcollation(param->paramtype);
param->location = pref->location; param->location = pref->location;
return (Node *) param; return (Node *) param;

View File

@ -1098,6 +1098,7 @@ addRangeTableEntryForFunction(ParseState *pstate,
rte->funcexpr = funcexpr; rte->funcexpr = funcexpr;
rte->funccoltypes = NIL; rte->funccoltypes = NIL;
rte->funccoltypmods = NIL; rte->funccoltypmods = NIL;
rte->funccolcollations = NIL;
rte->alias = alias; rte->alias = alias;
eref = makeAlias(alias ? alias->aliasname : funcname, NIL); eref = makeAlias(alias ? alias->aliasname : funcname, NIL);
@ -1157,6 +1158,7 @@ addRangeTableEntryForFunction(ParseState *pstate,
char *attrname; char *attrname;
Oid attrtype; Oid attrtype;
int32 attrtypmod; int32 attrtypmod;
Oid attrcollation;
attrname = pstrdup(n->colname); attrname = pstrdup(n->colname);
if (n->typeName->setof) if (n->typeName->setof)
@ -1165,10 +1167,11 @@ addRangeTableEntryForFunction(ParseState *pstate,
errmsg("column \"%s\" cannot be declared SETOF", errmsg("column \"%s\" cannot be declared SETOF",
attrname), attrname),
parser_errposition(pstate, n->typeName->location))); 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)); eref->colnames = lappend(eref->colnames, makeString(attrname));
rte->funccoltypes = lappend_oid(rte->funccoltypes, attrtype); rte->funccoltypes = lappend_oid(rte->funccoltypes, attrtype);
rte->funccoltypmods = lappend_int(rte->funccoltypmods, attrtypmod); rte->funccoltypmods = lappend_int(rte->funccoltypmods, attrtypmod);
rte->funccolcollations = lappend_oid(rte->funccolcollations, attrcollation);
} }
} }
else else
@ -1381,6 +1384,7 @@ addRangeTableEntryForCTE(ParseState *pstate,
rte->ctecoltypes = cte->ctecoltypes; rte->ctecoltypes = cte->ctecoltypes;
rte->ctecoltypmods = cte->ctecoltypmods; rte->ctecoltypmods = cte->ctecoltypmods;
rte->ctecolcollations = cte->ctecolcollations;
rte->alias = alias; rte->alias = alias;
if (alias) if (alias)
@ -1573,6 +1577,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
varnode = makeVar(rtindex, varattno, varnode = makeVar(rtindex, varattno,
exprType((Node *) te->expr), exprType((Node *) te->expr),
exprTypmod((Node *) te->expr), exprTypmod((Node *) te->expr),
exprCollation((Node *) te->expr),
sublevels_up); sublevels_up);
varnode->location = location; varnode->location = location;
@ -1612,6 +1617,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
varnode = makeVar(rtindex, 1, varnode = makeVar(rtindex, 1,
funcrettype, -1, funcrettype, -1,
exprCollation(rte->funcexpr),
sublevels_up); sublevels_up);
varnode->location = location; varnode->location = location;
@ -1626,12 +1632,14 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
{ {
ListCell *l1; ListCell *l1;
ListCell *l2; ListCell *l2;
ListCell *l3;
int attnum = 0; 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); Oid attrtype = lfirst_oid(l1);
int32 attrtypmod = lfirst_int(l2); int32 attrtypmod = lfirst_int(l2);
Oid attrcollation = lfirst_oid(l3);
Var *varnode; Var *varnode;
attnum++; attnum++;
@ -1639,6 +1647,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
attnum, attnum,
attrtype, attrtype,
attrtypmod, attrtypmod,
attrcollation,
sublevels_up); sublevels_up);
varnode->location = location; varnode->location = location;
*colvars = lappend(*colvars, varnode); *colvars = lappend(*colvars, varnode);
@ -1681,6 +1690,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
varnode = makeVar(rtindex, varattno, varnode = makeVar(rtindex, varattno,
exprType(col), exprType(col),
exprTypmod(col), exprTypmod(col),
exprCollation(col),
sublevels_up); sublevels_up);
varnode->location = location; varnode->location = location;
*colvars = lappend(*colvars, varnode); *colvars = lappend(*colvars, varnode);
@ -1740,6 +1750,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
varnode = makeVar(rtindex, varattno, varnode = makeVar(rtindex, varattno,
exprType(avar), exprType(avar),
exprTypmod(avar), exprTypmod(avar),
exprCollation(avar),
sublevels_up); sublevels_up);
varnode->location = location; varnode->location = location;
@ -1753,12 +1764,14 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
ListCell *aliasp_item = list_head(rte->eref->colnames); ListCell *aliasp_item = list_head(rte->eref->colnames);
ListCell *lct; ListCell *lct;
ListCell *lcm; ListCell *lcm;
ListCell *lcc;
varattno = 0; varattno = 0;
forboth(lct, rte->ctecoltypes, lcm, rte->ctecoltypmods) forthree(lct, rte->ctecoltypes, lcm, rte->ctecoltypmods, lcc, rte->ctecolcollations)
{ {
Oid coltype = lfirst_oid(lct); Oid coltype = lfirst_oid(lct);
int32 coltypmod = lfirst_int(lcm); int32 coltypmod = lfirst_int(lcm);
Oid colcoll = lfirst_oid(lcc);
varattno++; varattno++;
@ -1776,7 +1789,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
Var *varnode; Var *varnode;
varnode = makeVar(rtindex, varattno, varnode = makeVar(rtindex, varattno,
coltype, coltypmod, coltype, coltypmod, colcoll,
sublevels_up); sublevels_up);
*colvars = lappend(*colvars, varnode); *colvars = lappend(*colvars, varnode);
} }
@ -1857,7 +1870,7 @@ expandTupleDesc(TupleDesc tupdesc, Alias *eref,
Var *varnode; Var *varnode;
varnode = makeVar(rtindex, attr->attnum, varnode = makeVar(rtindex, attr->attnum,
attr->atttypid, attr->atttypmod, attr->atttypid, attr->atttypmod, attr->attcollation,
sublevels_up); sublevels_up);
varnode->location = location; varnode->location = location;
@ -1968,7 +1981,7 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
*/ */
void void
get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
Oid *vartype, int32 *vartypmod) Oid *vartype, int32 *vartypmod, Oid *varcollid)
{ {
switch (rte->rtekind) switch (rte->rtekind)
{ {
@ -1998,6 +2011,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
get_rel_name(rte->relid)))); get_rel_name(rte->relid))));
*vartype = att_tup->atttypid; *vartype = att_tup->atttypid;
*vartypmod = att_tup->atttypmod; *vartypmod = att_tup->atttypmod;
*varcollid = att_tup->attcollation;
ReleaseSysCache(tp); ReleaseSysCache(tp);
} }
break; break;
@ -2012,6 +2026,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
rte->eref->aliasname, attnum); rte->eref->aliasname, attnum);
*vartype = exprType((Node *) te->expr); *vartype = exprType((Node *) te->expr);
*vartypmod = exprTypmod((Node *) te->expr); *vartypmod = exprTypmod((Node *) te->expr);
*varcollid = exprCollation((Node *) te->expr);
} }
break; break;
case RTE_FUNCTION: case RTE_FUNCTION:
@ -2053,17 +2068,20 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
rte->eref->aliasname))); rte->eref->aliasname)));
*vartype = att_tup->atttypid; *vartype = att_tup->atttypid;
*vartypmod = att_tup->atttypmod; *vartypmod = att_tup->atttypmod;
*varcollid = att_tup->attcollation;
} }
else if (functypclass == TYPEFUNC_SCALAR) else if (functypclass == TYPEFUNC_SCALAR)
{ {
/* Base data type, i.e. scalar */ /* Base data type, i.e. scalar */
*vartype = funcrettype; *vartype = funcrettype;
*vartypmod = -1; *vartypmod = -1;
*varcollid = exprCollation(rte->funcexpr);
} }
else if (functypclass == TYPEFUNC_RECORD) else if (functypclass == TYPEFUNC_RECORD)
{ {
*vartype = list_nth_oid(rte->funccoltypes, attnum - 1); *vartype = list_nth_oid(rte->funccoltypes, attnum - 1);
*vartypmod = list_nth_int(rte->funccoltypmods, attnum - 1); *vartypmod = list_nth_int(rte->funccoltypmods, attnum - 1);
*varcollid = list_nth_oid(rte->funccolcollations, attnum - 1);
} }
else else
{ {
@ -2084,6 +2102,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
col = (Node *) list_nth(collist, attnum - 1); col = (Node *) list_nth(collist, attnum - 1);
*vartype = exprType(col); *vartype = exprType(col);
*vartypmod = exprTypmod(col); *vartypmod = exprTypmod(col);
*varcollid = exprCollation(col);
} }
break; break;
case RTE_JOIN: case RTE_JOIN:
@ -2097,6 +2116,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
aliasvar = (Node *) list_nth(rte->joinaliasvars, attnum - 1); aliasvar = (Node *) list_nth(rte->joinaliasvars, attnum - 1);
*vartype = exprType(aliasvar); *vartype = exprType(aliasvar);
*vartypmod = exprTypmod(aliasvar); *vartypmod = exprTypmod(aliasvar);
*varcollid = exprCollation(aliasvar);
} }
break; break;
case RTE_CTE: case RTE_CTE:
@ -2105,6 +2125,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
Assert(attnum > 0 && attnum <= list_length(rte->ctecoltypes)); Assert(attnum > 0 && attnum <= list_length(rte->ctecoltypes));
*vartype = list_nth_oid(rte->ctecoltypes, attnum - 1); *vartype = list_nth_oid(rte->ctecoltypes, attnum - 1);
*vartypmod = list_nth_int(rte->ctecoltypmods, attnum - 1); *vartypmod = list_nth_int(rte->ctecoltypmods, attnum - 1);
*varcollid = list_nth_oid(rte->ctecolcollations, attnum - 1);
} }
break; break;
default: default:

View File

@ -374,6 +374,7 @@ transformAssignedExpr(ParseState *pstate,
Oid type_id; /* type of value provided */ Oid type_id; /* type of value provided */
Oid attrtype; /* type of target column */ Oid attrtype; /* type of target column */
int32 attrtypmod; int32 attrtypmod;
Oid attrcollation;
Relation rd = pstate->p_target_relation; Relation rd = pstate->p_target_relation;
Assert(rd != NULL); Assert(rd != NULL);
@ -385,6 +386,7 @@ transformAssignedExpr(ParseState *pstate,
parser_errposition(pstate, location))); parser_errposition(pstate, location)));
attrtype = attnumTypeId(rd, attrno); attrtype = attnumTypeId(rd, attrno);
attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; 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 * If the expression is a DEFAULT placeholder, insert the attribute's
@ -400,6 +402,7 @@ transformAssignedExpr(ParseState *pstate,
def->typeId = attrtype; def->typeId = attrtype;
def->typeMod = attrtypmod; def->typeMod = attrtypmod;
def->collid = attrcollation;
if (indirection) if (indirection)
{ {
if (IsA(linitial(indirection), A_Indices)) if (IsA(linitial(indirection), A_Indices))
@ -786,6 +789,7 @@ transformAssignmentSubscripts(ParseState *pstate,
arrayType, arrayType,
elementTypeId, elementTypeId,
arrayTypMod, arrayTypMod,
InvalidOid,
subscripts, subscripts,
rhs); rhs);
@ -1267,6 +1271,7 @@ ExpandRowReference(ParseState *pstate, Node *expr,
fselect->fieldnum = i + 1; fselect->fieldnum = i + 1;
fselect->resulttype = att->atttypid; fselect->resulttype = att->atttypid;
fselect->resulttypmod = att->atttypmod; fselect->resulttypmod = att->atttypmod;
fselect->resultcollation = att->attcollation;
if (targetlist) if (targetlist)
{ {
@ -1338,6 +1343,8 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup)
exprType(varnode), exprType(varnode),
exprTypmod(varnode), exprTypmod(varnode),
0); 0);
TupleDescInitEntryCollation(tupleDesc, i,
exprCollation(varnode));
i++; i++;
} }
Assert(lname == NULL && lvar == NULL); /* lists same length? */ Assert(lname == NULL && lvar == NULL); /* lists same length? */
@ -1583,6 +1590,8 @@ FigureColnameInternal(Node *node, char **name)
} }
} }
break; break;
case T_CollateClause:
return FigureColnameInternal((Node *) ((CollateClause *) node)->arg, name);
case T_CaseExpr: case T_CaseExpr:
strength = FigureColnameInternal((Node *) ((CaseExpr *) node)->defresult, strength = FigureColnameInternal((Node *) ((CaseExpr *) node)->defresult,
name); name);

View File

@ -29,6 +29,8 @@
static int32 typenameTypeMod(ParseState *pstate, const TypeName *typeName, static int32 typenameTypeMod(ParseState *pstate, const TypeName *typeName,
Type typ); 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. * 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, * 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 * 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 * NB: on success, the caller must ReleaseSysCache the type tuple when done
* with it. * 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 * found but is a shell, and there is typmod decoration, an error will be
* thrown --- this is intentional. * thrown --- this is intentional.
* *
* colloid_p can also be null.
*
* pstate is only used for error location info, and may be NULL. * pstate is only used for error location info, and may be NULL.
*/ */
Type Type
LookupTypeName(ParseState *pstate, const TypeName *typeName, LookupTypeName(ParseState *pstate, const TypeName *typeName,
int32 *typmod_p) int32 *typmod_p, Oid *collid_p)
{ {
Oid typoid; Oid typoid;
HeapTuple tup; HeapTuple tup;
int32 typmod; int32 typmod;
Oid collid;
if (typeName->names == NIL) if (typeName->names == NIL)
{ {
@ -174,6 +180,11 @@ LookupTypeName(ParseState *pstate, const TypeName *typeName,
if (typmod_p) if (typmod_p)
*typmod_p = typmod; *typmod_p = typmod;
collid = typenameCollation(pstate, typeName, (Type) tup);
if (collid_p)
*collid_p = collid;
return (Type) tup; 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. * Callers of this can therefore assume the result is a fully valid type.
*/ */
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; Type tup;
tup = LookupTypeName(pstate, typeName, typmod_p); tup = LookupTypeName(pstate, typeName, typmod_p, collid_p);
if (tup == NULL) if (tup == NULL)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT), (errcode(ERRCODE_UNDEFINED_OBJECT),
@ -217,7 +228,7 @@ typenameTypeId(ParseState *pstate, const TypeName *typeName)
Oid typoid; Oid typoid;
Type tup; Type tup;
tup = typenameType(pstate, typeName, NULL); tup = typenameType(pstate, typeName, NULL, NULL);
typoid = HeapTupleGetOid(tup); typoid = HeapTupleGetOid(tup);
ReleaseSysCache(tup); ReleaseSysCache(tup);
@ -236,7 +247,25 @@ typenameTypeIdAndMod(ParseState *pstate, const TypeName *typeName,
{ {
Type tup; 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); *typeid_p = HeapTupleGetOid(tup);
ReleaseSysCache(tup); ReleaseSysCache(tup);
} }
@ -350,6 +379,62 @@ typenameTypeMod(ParseState *pstate, const TypeName *typeName, Type typ)
return result; return result;
} }
/*
* typenameCollation - given a TypeName, return the collation OID
*
* This will throw an error if the TypeName includes a collation but
* the data type does not support collations.
*
* The actual type OID represented by the TypeName must already have been
* looked up, and is passed as "typ".
*
* pstate is only used for error location info, and may be NULL.
*/
static Oid
typenameCollation(ParseState *pstate, const TypeName *typeName, Type typ)
{
Oid typcollation = ((Form_pg_type) GETSTRUCT(typ))->typcollation;
/* return prespecified collation OID if no collation name specified */
if (typeName->collnames == NIL)
{
if (typeName->collOid == InvalidOid)
return typcollation;
else
return typeName->collOid;
}
if (!OidIsValid(typcollation))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("collations are not supported by type %s",
format_type_be(HeapTupleGetOid(typ))),
parser_errposition(pstate, typeName->location)));
return LookupCollation(pstate, typeName->collnames, typeName->location);
}
/*
* LookupCollation
*
* Look up collation by name, return OID, with support for error
* location.
*/
Oid
LookupCollation(ParseState *pstate, List *collnames, int location)
{
Oid colloid;
ParseCallbackState pcbstate;
setup_parser_errposition_callback(&pcbstate, pstate, location);
colloid = get_collation_oid(collnames, false);
cancel_parser_errposition_callback(&pcbstate);
return colloid;
}
/* /*
* appendTypeNameToBuffer * appendTypeNameToBuffer
* Append a string representing the name of a TypeName to a StringInfo. * Append a string representing the name of a TypeName to a StringInfo.

View File

@ -627,7 +627,8 @@ transformInhRelation(CreateStmtContext *cxt, InhRelation *inhRelation)
def = makeNode(ColumnDef); def = makeNode(ColumnDef);
def->colname = pstrdup(attributeName); def->colname = pstrdup(attributeName);
def->typeName = makeTypeNameFromOid(attribute->atttypid, def->typeName = makeTypeNameFromOid(attribute->atttypid,
attribute->atttypmod); attribute->atttypmod,
attribute->attcollation);
def->inhcount = 0; def->inhcount = 0;
def->is_local = true; def->is_local = true;
def->is_not_null = attribute->attnotnull; def->is_not_null = attribute->attnotnull;
@ -821,7 +822,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
AssertArg(ofTypename); AssertArg(ofTypename);
tuple = typenameType(NULL, ofTypename, NULL); tuple = typenameType(NULL, ofTypename, NULL, NULL);
typ = (Form_pg_type) GETSTRUCT(tuple); typ = (Form_pg_type) GETSTRUCT(tuple);
ofTypeId = HeapTupleGetOid(tuple); ofTypeId = HeapTupleGetOid(tuple);
ofTypename->typeOid = ofTypeId; /* cached for later */ ofTypename->typeOid = ofTypeId; /* cached for later */
@ -842,7 +843,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
continue; continue;
n->colname = pstrdup(NameStr(attr->attname)); 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->constraints = NULL;
n->is_local = true; n->is_local = true;
n->is_from_type = 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. * 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); ReleaseSysCache(ctype);
} }

View File

@ -747,6 +747,7 @@ rewriteTargetListIU(Query *parsetree, Relation target_relation,
attrno, attrno,
att_tup->atttypid, att_tup->atttypid,
att_tup->atttypmod, att_tup->atttypmod,
att_tup->attcollation,
0); 0);
new_tle = makeTargetEntry((Expr *) new_expr, new_tle = makeTargetEntry((Expr *) new_expr,
@ -1127,6 +1128,7 @@ rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte,
SelfItemPointerAttributeNumber, SelfItemPointerAttributeNumber,
TIDOID, TIDOID,
-1, -1,
InvalidOid,
0); 0);
attrname = "ctid"; attrname = "ctid";

View File

@ -13,6 +13,7 @@
*/ */
#include "postgres.h" #include "postgres.h"
#include "catalog/pg_collation.h"
#include "storage/fd.h" #include "storage/fd.h"
#include "tsearch/ts_locale.h" #include "tsearch/ts_locale.h"
#include "tsearch/ts_public.h" #include "tsearch/ts_public.h"
@ -27,11 +28,12 @@ t_isdigit(const char *ptr)
{ {
int clen = pg_mblen(ptr); int clen = pg_mblen(ptr);
wchar_t character[2]; 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)); return isdigit(TOUCHAR(ptr));
char2wchar(character, 2, ptr, clen); char2wchar(character, 2, ptr, clen, collation);
return iswdigit((wint_t) character[0]); return iswdigit((wint_t) character[0]);
} }
@ -41,11 +43,12 @@ t_isspace(const char *ptr)
{ {
int clen = pg_mblen(ptr); int clen = pg_mblen(ptr);
wchar_t character[2]; 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)); return isspace(TOUCHAR(ptr));
char2wchar(character, 2, ptr, clen); char2wchar(character, 2, ptr, clen, collation);
return iswspace((wint_t) character[0]); return iswspace((wint_t) character[0]);
} }
@ -55,11 +58,12 @@ t_isalpha(const char *ptr)
{ {
int clen = pg_mblen(ptr); int clen = pg_mblen(ptr);
wchar_t character[2]; 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)); return isalpha(TOUCHAR(ptr));
char2wchar(character, 2, ptr, clen); char2wchar(character, 2, ptr, clen, collation);
return iswalpha((wint_t) character[0]); return iswalpha((wint_t) character[0]);
} }
@ -69,11 +73,12 @@ t_isprint(const char *ptr)
{ {
int clen = pg_mblen(ptr); int clen = pg_mblen(ptr);
wchar_t character[2]; 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)); return isprint(TOUCHAR(ptr));
char2wchar(character, 2, ptr, clen); char2wchar(character, 2, ptr, clen, collation);
return iswprint((wint_t) character[0]); return iswprint((wint_t) character[0]);
} }
@ -238,6 +243,7 @@ char *
lowerstr_with_len(const char *str, int len) lowerstr_with_len(const char *str, int len)
{ {
char *out; char *out;
Oid collation = DEFAULT_COLLATION_OID; /*TODO*/
if (len == 0) if (len == 0)
return pstrdup(""); 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 * Also, for a C locale there is no need to process as multibyte. From
* backend/utils/adt/oracle_compat.c Teodor * 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, wchar_t *wstr,
*wptr; *wptr;
@ -263,7 +269,7 @@ lowerstr_with_len(const char *str, int len)
*/ */
wptr = wstr = (wchar_t *) palloc(sizeof(wchar_t) * (len + 1)); 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); Assert(wlen <= len);
while (*wptr) while (*wptr)
@ -278,7 +284,7 @@ lowerstr_with_len(const char *str, int len)
len = pg_database_encoding_max_length() * wlen + 1; len = pg_database_encoding_max_length() * wlen + 1;
out = (char *) palloc(len); out = (char *) palloc(len);
wlen = wchar2char(out, wstr, len); wlen = wchar2char(out, wstr, len, collation);
pfree(wstr); pfree(wstr);

View File

@ -14,6 +14,7 @@
#include "postgres.h" #include "postgres.h"
#include "catalog/pg_collation.h"
#include "commands/defrem.h" #include "commands/defrem.h"
#include "tsearch/ts_locale.h" #include "tsearch/ts_locale.h"
#include "tsearch/ts_public.h" #include "tsearch/ts_public.h"
@ -286,6 +287,7 @@ static TParser *
TParserInit(char *str, int len) TParserInit(char *str, int len)
{ {
TParser *prs = (TParser *) palloc0(sizeof(TParser)); TParser *prs = (TParser *) palloc0(sizeof(TParser));
Oid collation = DEFAULT_COLLATION_OID; /*TODO*/
prs->charmaxlen = pg_database_encoding_max_length(); prs->charmaxlen = pg_database_encoding_max_length();
prs->str = str; prs->str = str;
@ -299,7 +301,7 @@ TParserInit(char *str, int len)
if (prs->charmaxlen > 1) if (prs->charmaxlen > 1)
{ {
prs->usewide = true; prs->usewide = true;
if ( lc_ctype_is_c() ) if ( lc_ctype_is_c(collation) )
{ {
/* /*
* char2wchar doesn't work for C-locale and * char2wchar doesn't work for C-locale and
@ -311,7 +313,7 @@ TParserInit(char *str, int len)
else else
{ {
prs->wstr = (wchar_t *) palloc(sizeof(wchar_t) * (prs->lenstr + 1)); 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 else

View File

@ -3307,6 +3307,7 @@ array_cmp(FunctionCallInfo fcinfo)
{ {
ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0); ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1); ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
Oid collation = PG_GET_COLLATION();
int ndims1 = ARR_NDIM(array1); int ndims1 = ARR_NDIM(array1);
int ndims2 = ARR_NDIM(array2); int ndims2 = ARR_NDIM(array2);
int *dims1 = ARR_DIMS(array1); int *dims1 = ARR_DIMS(array1);
@ -3341,7 +3342,8 @@ array_cmp(FunctionCallInfo fcinfo)
*/ */
typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra; typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
if (typentry == NULL || 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, typentry = lookup_type_cache(element_type,
TYPECACHE_CMP_PROC_FINFO); TYPECACHE_CMP_PROC_FINFO);
@ -3351,6 +3353,7 @@ array_cmp(FunctionCallInfo fcinfo)
errmsg("could not identify a comparison function for type %s", errmsg("could not identify a comparison function for type %s",
format_type_be(element_type)))); format_type_be(element_type))));
fcinfo->flinfo->fn_extra = (void *) typentry; fcinfo->flinfo->fn_extra = (void *) typentry;
typentry->cmp_proc_finfo.fn_collation = collation;
} }
typlen = typentry->typlen; typlen = typentry->typlen;
typbyval = typentry->typbyval; typbyval = typentry->typbyval;

View File

@ -18,6 +18,7 @@
#include <ctype.h> #include <ctype.h>
#include "catalog/namespace.h" #include "catalog/namespace.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
@ -28,7 +29,8 @@
#define MAX_INT32_LEN 11 #define MAX_INT32_LEN 11
static char *format_type_internal(Oid type_oid, int32 typemod, 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 *printTypmod(const char *typname, int32 typmod, Oid typmodout);
static char * static char *
psnprintf(size_t len, const char *fmt,...) psnprintf(size_t len, const char *fmt,...)
@ -67,6 +69,7 @@ format_type(PG_FUNCTION_ARGS)
{ {
Oid type_oid; Oid type_oid;
int32 typemod; int32 typemod;
Oid collation_oid;
char *result; char *result;
/* Since this function is not strict, we must test for null args */ /* Since this function is not strict, we must test for null args */
@ -74,13 +77,14 @@ format_type(PG_FUNCTION_ARGS)
PG_RETURN_NULL(); PG_RETURN_NULL();
type_oid = PG_GETARG_OID(0); type_oid = PG_GETARG_OID(0);
collation_oid = PG_ARGISNULL(2) ? InvalidOid : PG_GETARG_OID(2);
if (PG_ARGISNULL(1)) 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 else
{ {
typemod = PG_GETARG_INT32(1); 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)); PG_RETURN_TEXT_P(cstring_to_text(result));
@ -95,7 +99,7 @@ format_type(PG_FUNCTION_ARGS)
char * char *
format_type_be(Oid type_oid) 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 * char *
format_type_with_typemod(Oid type_oid, int32 typemod) 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 * static char *
format_type_internal(Oid type_oid, int32 typemod, 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); bool with_typemod = typemod_given && (typemod >= 0);
HeapTuple tuple; HeapTuple tuple;
@ -317,6 +322,12 @@ format_type_internal(Oid type_oid, int32 typemod,
ReleaseSysCache(tuple); 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; return buf;
} }
@ -420,7 +431,7 @@ oidvectortypes(PG_FUNCTION_ARGS)
for (num = 0; num < numargs; num++) for (num = 0; num < numargs; num++)
{ {
char *typename = format_type_internal(oidArray->values[num], -1, char *typename = format_type_internal(oidArray->values[num], -1,
false, true); false, true, InvalidOid);
size_t slen = strlen(typename); size_t slen = strlen(typename);
if (left < (slen + 2)) if (left < (slen + 2))

View File

@ -82,6 +82,7 @@
#include <wctype.h> #include <wctype.h>
#endif #endif
#include "catalog/pg_collation.h"
#include "mb/pg_wchar.h" #include "mb/pg_wchar.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/date.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); KeySuffix *suf, const int *index, int ver, NUMDesc *Num);
static void DCH_to_char(FormatNode *node, bool is_interval, 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); static void DCH_from_char(FormatNode *node, char *in, TmFromChar *out);
#ifdef DEBUG_TO_FROM_CHAR #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_from_char(NUMProc *Np, int id, int plen);
static void NUM_numpart_to_char(NUMProc *Np, int id); static void NUM_numpart_to_char(NUMProc *Np, int id);
static char *NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number, 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_search(char *str);
static DCHCacheEntry *DCH_cache_getnew(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. * to this function. The result is a palloc'd, null-terminated string.
*/ */
char * char *
str_tolower(const char *buff, size_t nbytes) str_tolower(const char *buff, size_t nbytes, Oid collid)
{ {
char *result; char *result;
pg_locale_t mylocale = 0;
if (!buff) if (!buff)
return NULL; return NULL;
if (collid != DEFAULT_COLLATION_OID)
mylocale = pg_newlocale_from_collation(collid);
#ifdef USE_WIDE_UPPER_LOWER #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; wchar_t *workspace;
size_t curr_char; 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 */ /* Output workspace cannot have more codes than input bytes */
workspace = (wchar_t *) palloc((nbytes + 1) * sizeof(wchar_t)); 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++) 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]); workspace[curr_char] = towlower(workspace[curr_char]);
/* Make result large enough; case change might change number of bytes */ /* Make result large enough; case change might change number of bytes */
result_size = curr_char * pg_database_encoding_max_length() + 1; result_size = curr_char * pg_database_encoding_max_length() + 1;
result = palloc(result_size); result = palloc(result_size);
wchar2char(result, workspace, result_size); wchar2char(result, workspace, result_size, collid);
pfree(workspace); pfree(workspace);
} }
else 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. * to this function. The result is a palloc'd, null-terminated string.
*/ */
char * char *
str_toupper(const char *buff, size_t nbytes) str_toupper(const char *buff, size_t nbytes, Oid collid)
{ {
char *result; char *result;
pg_locale_t mylocale = 0;
if (!buff) if (!buff)
return NULL; return NULL;
if (collid != DEFAULT_COLLATION_OID)
mylocale = pg_newlocale_from_collation(collid);
#ifdef USE_WIDE_UPPER_LOWER #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; wchar_t *workspace;
size_t curr_char; 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 */ /* Output workspace cannot have more codes than input bytes */
workspace = (wchar_t *) palloc((nbytes + 1) * sizeof(wchar_t)); 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++) 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]); workspace[curr_char] = towupper(workspace[curr_char]);
/* Make result large enough; case change might change number of bytes */ /* Make result large enough; case change might change number of bytes */
result_size = curr_char * pg_database_encoding_max_length() + 1; result_size = curr_char * pg_database_encoding_max_length() + 1;
result = palloc(result_size); result = palloc(result_size);
wchar2char(result, workspace, result_size); wchar2char(result, workspace, result_size, collid);
pfree(workspace); pfree(workspace);
} }
else 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. * to this function. The result is a palloc'd, null-terminated string.
*/ */
char * char *
str_initcap(const char *buff, size_t nbytes) str_initcap(const char *buff, size_t nbytes, Oid collid)
{ {
char *result; char *result;
int wasalnum = false; int wasalnum = false;
pg_locale_t mylocale = 0;
if (!buff) if (!buff)
return NULL; return NULL;
if (collid != DEFAULT_COLLATION_OID)
mylocale = pg_newlocale_from_collation(collid);
#ifdef USE_WIDE_UPPER_LOWER #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; wchar_t *workspace;
size_t curr_char; 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 */ /* Output workspace cannot have more codes than input bytes */
workspace = (wchar_t *) palloc((nbytes + 1) * sizeof(wchar_t)); 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++) for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
{ {
if (wasalnum) #ifdef HAVE_LOCALE_T
workspace[curr_char] = towlower(workspace[curr_char]); 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 else
workspace[curr_char] = towupper(workspace[curr_char]); #endif
wasalnum = iswalnum(workspace[curr_char]); {
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 */ /* Make result large enough; case change might change number of bytes */
result_size = curr_char * pg_database_encoding_max_length() + 1; result_size = curr_char * pg_database_encoding_max_length() + 1;
result = palloc(result_size); result = palloc(result_size);
wchar2char(result, workspace, result_size); wchar2char(result, workspace, result_size, collid);
pfree(workspace); pfree(workspace);
} }
else else
@ -1647,21 +1683,21 @@ str_initcap(const char *buff, size_t nbytes)
/* convenience routines for when the input is null-terminated */ /* convenience routines for when the input is null-terminated */
static char * 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 * 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 * 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 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; FormatNode *n;
char *s; char *s;
@ -2151,7 +2187,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
INVALID_FOR_INTERVAL; INVALID_FOR_INTERVAL;
if (tmtcTzn(in)) if (tmtcTzn(in))
{ {
char *p = str_tolower_z(tmtcTzn(in)); char *p = str_tolower_z(tmtcTzn(in), collid);
strcpy(s, p); strcpy(s, p);
pfree(p); pfree(p);
@ -2195,10 +2231,10 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
if (!tm->tm_mon) if (!tm->tm_mon)
break; break;
if (S_TM(n->suffix)) 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 else
sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, 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); s += strlen(s);
break; break;
case DCH_Month: case DCH_Month:
@ -2206,7 +2242,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
if (!tm->tm_mon) if (!tm->tm_mon)
break; break;
if (S_TM(n->suffix)) 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 else
sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, months_full[tm->tm_mon - 1]); sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, months_full[tm->tm_mon - 1]);
s += strlen(s); s += strlen(s);
@ -2216,7 +2252,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
if (!tm->tm_mon) if (!tm->tm_mon)
break; break;
if (S_TM(n->suffix)) 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 else
{ {
sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, months_full[tm->tm_mon - 1]); 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) if (!tm->tm_mon)
break; break;
if (S_TM(n->suffix)) 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 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); s += strlen(s);
break; break;
case DCH_Mon: case DCH_Mon:
@ -2239,7 +2275,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
if (!tm->tm_mon) if (!tm->tm_mon)
break; break;
if (S_TM(n->suffix)) 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 else
strcpy(s, months[tm->tm_mon - 1]); strcpy(s, months[tm->tm_mon - 1]);
s += strlen(s); s += strlen(s);
@ -2249,7 +2285,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
if (!tm->tm_mon) if (!tm->tm_mon)
break; break;
if (S_TM(n->suffix)) 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 else
{ {
strcpy(s, months[tm->tm_mon - 1]); 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: case DCH_DAY:
INVALID_FOR_INTERVAL; INVALID_FOR_INTERVAL;
if (S_TM(n->suffix)) 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 else
sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, 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); s += strlen(s);
break; break;
case DCH_Day: case DCH_Day:
INVALID_FOR_INTERVAL; INVALID_FOR_INTERVAL;
if (S_TM(n->suffix)) 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 else
sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, days[tm->tm_wday]); sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, days[tm->tm_wday]);
s += strlen(s); s += strlen(s);
@ -2283,7 +2319,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
case DCH_day: case DCH_day:
INVALID_FOR_INTERVAL; INVALID_FOR_INTERVAL;
if (S_TM(n->suffix)) 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 else
{ {
sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, days[tm->tm_wday]); 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: case DCH_DY:
INVALID_FOR_INTERVAL; INVALID_FOR_INTERVAL;
if (S_TM(n->suffix)) 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 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); s += strlen(s);
break; break;
case DCH_Dy: case DCH_Dy:
INVALID_FOR_INTERVAL; INVALID_FOR_INTERVAL;
if (S_TM(n->suffix)) 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 else
strcpy(s, days_short[tm->tm_wday]); strcpy(s, days_short[tm->tm_wday]);
s += strlen(s); s += strlen(s);
@ -2310,7 +2346,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out)
case DCH_dy: case DCH_dy:
INVALID_FOR_INTERVAL; INVALID_FOR_INTERVAL;
if (S_TM(n->suffix)) 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 else
{ {
strcpy(s, days_short[tm->tm_wday]); strcpy(s, days_short[tm->tm_wday]);
@ -2846,7 +2882,7 @@ DCH_cache_search(char *str)
* for formatting. * for formatting.
*/ */
static text * static text *
datetime_to_char_body(TmToChar *tmtc, text *fmt, bool is_interval) datetime_to_char_body(TmToChar *tmtc, text *fmt, bool is_interval, Oid collid)
{ {
FormatNode *format; FormatNode *format;
char *fmt_str, char *fmt_str,
@ -2912,7 +2948,7 @@ datetime_to_char_body(TmToChar *tmtc, text *fmt, bool is_interval)
} }
/* The real work is here */ /* The real work is here */
DCH_to_char(format, is_interval, tmtc, result); DCH_to_char(format, is_interval, tmtc, result, collid);
if (!incache) if (!incache)
pfree(format); pfree(format);
@ -2959,7 +2995,7 @@ timestamp_to_char(PG_FUNCTION_ARGS)
tm->tm_wday = (thisdate + 1) % 7; tm->tm_wday = (thisdate + 1) % 7;
tm->tm_yday = thisdate - date2j(tm->tm_year, 1, 1) + 1; tm->tm_yday = thisdate - date2j(tm->tm_year, 1, 1) + 1;
if (!(res = datetime_to_char_body(&tmtc, fmt, false))) if (!(res = datetime_to_char_body(&tmtc, fmt, false, PG_GET_COLLATION())))
PG_RETURN_NULL(); PG_RETURN_NULL();
PG_RETURN_TEXT_P(res); PG_RETURN_TEXT_P(res);
@ -2991,7 +3027,7 @@ timestamptz_to_char(PG_FUNCTION_ARGS)
tm->tm_wday = (thisdate + 1) % 7; tm->tm_wday = (thisdate + 1) % 7;
tm->tm_yday = thisdate - date2j(tm->tm_year, 1, 1) + 1; tm->tm_yday = thisdate - date2j(tm->tm_year, 1, 1) + 1;
if (!(res = datetime_to_char_body(&tmtc, fmt, false))) if (!(res = datetime_to_char_body(&tmtc, fmt, false, PG_GET_COLLATION())))
PG_RETURN_NULL(); PG_RETURN_NULL();
PG_RETURN_TEXT_P(res); PG_RETURN_TEXT_P(res);
@ -3023,7 +3059,7 @@ interval_to_char(PG_FUNCTION_ARGS)
/* wday is meaningless, yday approximates the total span in days */ /* wday is meaningless, yday approximates the total span in days */
tm->tm_yday = (tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon) * DAYS_PER_MONTH + tm->tm_mday; tm->tm_yday = (tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon) * DAYS_PER_MONTH + tm->tm_mday;
if (!(res = datetime_to_char_body(&tmtc, fmt, true))) if (!(res = datetime_to_char_body(&tmtc, fmt, true, PG_GET_COLLATION())))
PG_RETURN_NULL(); PG_RETURN_NULL();
PG_RETURN_TEXT_P(res); PG_RETURN_TEXT_P(res);
@ -4123,7 +4159,7 @@ NUM_numpart_to_char(NUMProc *Np, int id)
*/ */
static char * static char *
NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number, NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
int plen, int sign, bool is_to_char) int plen, int sign, bool is_to_char, Oid collid)
{ {
FormatNode *n; FormatNode *n;
NUMProc _Np, NUMProc _Np,
@ -4403,12 +4439,12 @@ NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
case NUM_rn: case NUM_rn:
if (IS_FILLMODE(Np->Num)) if (IS_FILLMODE(Np->Num))
{ {
strcpy(Np->inout_p, str_tolower_z(Np->number_p)); strcpy(Np->inout_p, str_tolower_z(Np->number_p, collid));
Np->inout_p += strlen(Np->inout_p) - 1; Np->inout_p += strlen(Np->inout_p) - 1;
} }
else else
{ {
sprintf(Np->inout_p, "%15s", str_tolower_z(Np->number_p)); sprintf(Np->inout_p, "%15s", str_tolower_z(Np->number_p, collid));
Np->inout_p += strlen(Np->inout_p) - 1; Np->inout_p += strlen(Np->inout_p) - 1;
} }
break; break;
@ -4541,7 +4577,7 @@ do { \
*/ */
#define NUM_TOCHAR_finish \ #define NUM_TOCHAR_finish \
do { \ do { \
NUM_processor(format, &Num, VARDATA(result), numstr, plen, sign, true); \ NUM_processor(format, &Num, VARDATA(result), numstr, plen, sign, true, PG_GET_COLLATION()); \
\ \
if (shouldFree) \ if (shouldFree) \
pfree(format); \ pfree(format); \
@ -4583,7 +4619,7 @@ numeric_to_number(PG_FUNCTION_ARGS)
numstr = (char *) palloc((len * NUM_MAX_ITEM_SIZ) + 1); numstr = (char *) palloc((len * NUM_MAX_ITEM_SIZ) + 1);
NUM_processor(format, &Num, VARDATA(value), numstr, NUM_processor(format, &Num, VARDATA(value), numstr,
VARSIZE(value) - VARHDRSZ, 0, false); VARSIZE(value) - VARHDRSZ, 0, false, PG_GET_COLLATION());
scale = Num.post; scale = Num.post;
precision = Max(0, Num.pre) + scale; precision = Max(0, Num.pre) + scale;

View File

@ -39,7 +39,7 @@ static int UTF8_MatchText(char *t, int tlen, char *p, int plen);
static int SB_IMatchText(char *t, int tlen, char *p, int plen); static int SB_IMatchText(char *t, int tlen, char *p, int plen);
static int GenericMatchText(char *s, int slen, char *p, int plen); static int GenericMatchText(char *s, int slen, char *p, int plen);
static int Generic_Text_IC_like(text *str, text *pat); static int Generic_Text_IC_like(text *str, text *pat, Oid collation);
/*-------------------- /*--------------------
* Support routine for MatchText. Compares given multibyte streams * Support routine for MatchText. Compares given multibyte streams
@ -133,7 +133,7 @@ GenericMatchText(char *s, int slen, char *p, int plen)
} }
static inline int static inline int
Generic_Text_IC_like(text *str, text *pat) Generic_Text_IC_like(text *str, text *pat, Oid collation)
{ {
char *s, char *s,
*p; *p;
@ -149,10 +149,10 @@ Generic_Text_IC_like(text *str, text *pat)
if (pg_database_encoding_max_length() > 1) if (pg_database_encoding_max_length() > 1)
{ {
/* lower's result is never packed, so OK to use old macros here */ /* lower's result is never packed, so OK to use old macros here */
pat = DatumGetTextP(DirectFunctionCall1(lower, PointerGetDatum(pat))); pat = DatumGetTextP(DirectFunctionCall1WithCollation(lower, collation, PointerGetDatum(pat)));
p = VARDATA(pat); p = VARDATA(pat);
plen = (VARSIZE(pat) - VARHDRSZ); plen = (VARSIZE(pat) - VARHDRSZ);
str = DatumGetTextP(DirectFunctionCall1(lower, PointerGetDatum(str))); str = DatumGetTextP(DirectFunctionCall1WithCollation(lower, collation, PointerGetDatum(str)));
s = VARDATA(str); s = VARDATA(str);
slen = (VARSIZE(str) - VARHDRSZ); slen = (VARSIZE(str) - VARHDRSZ);
if (GetDatabaseEncoding() == PG_UTF8) if (GetDatabaseEncoding() == PG_UTF8)
@ -314,7 +314,7 @@ nameiclike(PG_FUNCTION_ARGS)
strtext = DatumGetTextP(DirectFunctionCall1(name_text, strtext = DatumGetTextP(DirectFunctionCall1(name_text,
NameGetDatum(str))); NameGetDatum(str)));
result = (Generic_Text_IC_like(strtext, pat) == LIKE_TRUE); result = (Generic_Text_IC_like(strtext, pat, PG_GET_COLLATION()) == LIKE_TRUE);
PG_RETURN_BOOL(result); PG_RETURN_BOOL(result);
} }
@ -329,7 +329,7 @@ nameicnlike(PG_FUNCTION_ARGS)
strtext = DatumGetTextP(DirectFunctionCall1(name_text, strtext = DatumGetTextP(DirectFunctionCall1(name_text,
NameGetDatum(str))); NameGetDatum(str)));
result = (Generic_Text_IC_like(strtext, pat) != LIKE_TRUE); result = (Generic_Text_IC_like(strtext, pat, PG_GET_COLLATION()) != LIKE_TRUE);
PG_RETURN_BOOL(result); PG_RETURN_BOOL(result);
} }
@ -341,7 +341,7 @@ texticlike(PG_FUNCTION_ARGS)
text *pat = PG_GETARG_TEXT_PP(1); text *pat = PG_GETARG_TEXT_PP(1);
bool result; bool result;
result = (Generic_Text_IC_like(str, pat) == LIKE_TRUE); result = (Generic_Text_IC_like(str, pat, PG_GET_COLLATION()) == LIKE_TRUE);
PG_RETURN_BOOL(result); PG_RETURN_BOOL(result);
} }
@ -353,7 +353,7 @@ texticnlike(PG_FUNCTION_ARGS)
text *pat = PG_GETARG_TEXT_PP(1); text *pat = PG_GETARG_TEXT_PP(1);
bool result; bool result;
result = (Generic_Text_IC_like(str, pat) != LIKE_TRUE); result = (Generic_Text_IC_like(str, pat, PG_GET_COLLATION()) != LIKE_TRUE);
PG_RETURN_BOOL(result); PG_RETURN_BOOL(result);
} }

View File

@ -47,7 +47,8 @@ lower(PG_FUNCTION_ARGS)
text *result; text *result;
out_string = str_tolower(VARDATA_ANY(in_string), out_string = str_tolower(VARDATA_ANY(in_string),
VARSIZE_ANY_EXHDR(in_string)); VARSIZE_ANY_EXHDR(in_string),
PG_GET_COLLATION());
result = cstring_to_text(out_string); result = cstring_to_text(out_string);
pfree(out_string); pfree(out_string);
@ -77,7 +78,8 @@ upper(PG_FUNCTION_ARGS)
text *result; text *result;
out_string = str_toupper(VARDATA_ANY(in_string), out_string = str_toupper(VARDATA_ANY(in_string),
VARSIZE_ANY_EXHDR(in_string)); VARSIZE_ANY_EXHDR(in_string),
PG_GET_COLLATION());
result = cstring_to_text(out_string); result = cstring_to_text(out_string);
pfree(out_string); pfree(out_string);
@ -110,7 +112,8 @@ initcap(PG_FUNCTION_ARGS)
text *result; text *result;
out_string = str_initcap(VARDATA_ANY(in_string), out_string = str_initcap(VARDATA_ANY(in_string),
VARSIZE_ANY_EXHDR(in_string)); VARSIZE_ANY_EXHDR(in_string),
PG_GET_COLLATION());
result = cstring_to_text(out_string); result = cstring_to_text(out_string);
pfree(out_string); pfree(out_string);

View File

@ -54,10 +54,13 @@
#include <locale.h> #include <locale.h>
#include <time.h> #include <time.h>
#include "catalog/pg_collation.h"
#include "catalog/pg_control.h" #include "catalog/pg_control.h"
#include "mb/pg_wchar.h" #include "mb/pg_wchar.h"
#include "utils/hsearch.h"
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/pg_locale.h" #include "utils/pg_locale.h"
#include "utils/syscache.h"
#ifdef WIN32 #ifdef WIN32
#include <shlwapi.h> #include <shlwapi.h>
@ -100,6 +103,11 @@ static char lc_time_envbuf[LC_ENV_BUFSIZE];
static char *IsoLocaleName(const char *); /* MSVC specific */ static char *IsoLocaleName(const char *); /* MSVC specific */
#endif #endif
static HTAB *locale_cness_cache = NULL;
#ifdef HAVE_LOCALE_T
static HTAB *locale_t_cache = NULL;
#endif
/* /*
* pg_perm_setlocale * pg_perm_setlocale
@ -305,16 +313,90 @@ locale_messages_assign(const char *value, bool doit, GucSource source)
/* /*
* We'd like to cache whether LC_COLLATE is C (or POSIX), so we can * We'd like to cache whether LC_COLLATE or LC_CTYPE is C (or POSIX),
* optimize a few code paths in various places. * so we can optimize a few code paths in various places.
*
* Note that some code relies on this not reporting false negatives
* (that is, saying it's not C when it is). For example, char2wchar()
* could fail if the locale is C, so str_tolower() shouldn't call it
* in that case.
*/ */
struct locale_cness_cache_entry
{
Oid collid;
bool collate_is_c;
bool ctype_is_c;
};
static void
init_locale_cness_cache(void)
{
HASHCTL ctl;
memset(&ctl, 0, sizeof(ctl));
ctl.keysize = sizeof(Oid);
ctl.entrysize = sizeof(struct locale_cness_cache_entry);
ctl.hash = oid_hash;
locale_cness_cache = hash_create("locale C-ness cache", 1000, &ctl, HASH_ELEM | HASH_FUNCTION);
}
/*
* Handle caching of locale "C-ness" for nondefault collation objects.
* Relying on the system cache directly isn't fast enough.
*/
static bool
lookup_collation_cness(Oid collation, int category)
{
struct locale_cness_cache_entry *cache_entry;
bool found;
HeapTuple tp;
char *localeptr;
Assert(OidIsValid(collation));
Assert(category == LC_COLLATE || category == LC_CTYPE);
if (!locale_cness_cache)
init_locale_cness_cache();
cache_entry = hash_search(locale_cness_cache, &collation, HASH_ENTER, &found);
if (found)
{
if (category == LC_COLLATE)
return cache_entry->collate_is_c;
else
return cache_entry->ctype_is_c;
}
tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collation));
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for collation %u", collation);
localeptr = NameStr(((Form_pg_collation) GETSTRUCT(tp))->collcollate);
cache_entry->collate_is_c = (strcmp(localeptr, "C") == 0) || (strcmp(localeptr, "POSIX") == 0);
localeptr = NameStr(((Form_pg_collation) GETSTRUCT(tp))->collctype);
cache_entry->ctype_is_c = (strcmp(localeptr, "C") == 0) || (strcmp(localeptr, "POSIX") == 0);
ReleaseSysCache(tp);
return category == LC_COLLATE ? cache_entry->collate_is_c : cache_entry->ctype_is_c;
}
bool bool
lc_collate_is_c(void) lc_collate_is_c(Oid collation)
{ {
/* Cache result so we only have to compute it once */ /* Cache result so we only have to compute it once */
static int result = -1; static int result = -1;
char *localeptr; char *localeptr;
if (!OidIsValid(collation))
return false;
if (collation != DEFAULT_COLLATION_OID)
return lookup_collation_cness(collation, LC_COLLATE);
if (result >= 0) if (result >= 0)
return (bool) result; return (bool) result;
localeptr = setlocale(LC_COLLATE, NULL); localeptr = setlocale(LC_COLLATE, NULL);
@ -331,17 +413,19 @@ lc_collate_is_c(void)
} }
/*
* We'd like to cache whether LC_CTYPE is C (or POSIX), so we can
* optimize a few code paths in various places.
*/
bool bool
lc_ctype_is_c(void) lc_ctype_is_c(Oid collation)
{ {
/* Cache result so we only have to compute it once */ /* Cache result so we only have to compute it once */
static int result = -1; static int result = -1;
char *localeptr; char *localeptr;
if (!OidIsValid(collation))
return false;
if (collation != DEFAULT_COLLATION_OID)
return lookup_collation_cness(collation, LC_CTYPE);
if (result >= 0) if (result >= 0)
return (bool) result; return (bool) result;
localeptr = setlocale(LC_CTYPE, NULL); localeptr = setlocale(LC_CTYPE, NULL);
@ -483,7 +567,7 @@ PGLC_localeconv(void)
/* Get formatting information for numeric */ /* Get formatting information for numeric */
setlocale(LC_NUMERIC, locale_numeric); setlocale(LC_NUMERIC, locale_numeric);
extlconv = localeconv(); extlconv = localeconv();
encoding = pg_get_encoding_from_locale(locale_numeric); encoding = pg_get_encoding_from_locale(locale_numeric, true);
decimal_point = db_encoding_strdup(encoding, extlconv->decimal_point); decimal_point = db_encoding_strdup(encoding, extlconv->decimal_point);
thousands_sep = db_encoding_strdup(encoding, extlconv->thousands_sep); thousands_sep = db_encoding_strdup(encoding, extlconv->thousands_sep);
@ -497,7 +581,7 @@ PGLC_localeconv(void)
/* Get formatting information for monetary */ /* Get formatting information for monetary */
setlocale(LC_MONETARY, locale_monetary); setlocale(LC_MONETARY, locale_monetary);
extlconv = localeconv(); extlconv = localeconv();
encoding = pg_get_encoding_from_locale(locale_monetary); encoding = pg_get_encoding_from_locale(locale_monetary, true);
/* /*
* Must copy all values since restoring internal settings may overwrite * Must copy all values since restoring internal settings may overwrite
@ -758,3 +842,118 @@ IsoLocaleName(const char *winlocname)
} }
#endif /* WIN32 && LC_MESSAGES */ #endif /* WIN32 && LC_MESSAGES */
#ifdef HAVE_LOCALE_T
struct locale_t_cache_entry
{
Oid collid;
locale_t locale;
};
static void
init_locale_t_cache(void)
{
HASHCTL ctl;
memset(&ctl, 0, sizeof(ctl));
ctl.keysize = sizeof(Oid);
ctl.entrysize = sizeof(struct locale_t_cache_entry);
ctl.hash = oid_hash;
locale_t_cache = hash_create("locale_t cache", 1000, &ctl, HASH_ELEM | HASH_FUNCTION);
}
#endif /* HAVE_LOCALE_T */
/*
* Create a locale_t from a collation OID. Results are cached for the
* lifetime of the backend. Thus, do not free the result with
* freelocale().
*
* As a special optimization, the default/database collation returns
* 0. Callers should then revert to the non-locale_t-enabled code
* path. In fact, they shouldn't call this function at all when they
* are dealing with the default locale. That can save quite a bit in
* hotspots.
*
* For simplicity, we always generate COLLATE + CTYPE even though we
* might only need one of them. Since this is called only once per
* session, it shouldn't cost much.
*/
pg_locale_t
pg_newlocale_from_collation(Oid collid)
{
#ifdef HAVE_LOCALE_T
HeapTuple tp;
const char *collcollate;
const char *collctype;
locale_t result;
struct locale_t_cache_entry *cache_entry;
bool found;
if (collid == DEFAULT_COLLATION_OID)
return (locale_t) 0;
if (!OidIsValid(collid))
elog(ERROR, "locale operation to be invoked, but no collation was derived");
if (!locale_t_cache)
init_locale_t_cache();
cache_entry = hash_search(locale_t_cache, &collid, HASH_ENTER, &found);
if (found)
return cache_entry->locale;
tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for collation %u", collid);
collcollate = NameStr(((Form_pg_collation) GETSTRUCT(tp))->collcollate);
collctype = NameStr(((Form_pg_collation) GETSTRUCT(tp))->collctype);
if (strcmp(collcollate, collctype) == 0)
{
result = newlocale(LC_COLLATE_MASK | LC_CTYPE_MASK, collcollate, NULL);
if (!result)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not create locale \"%s\": %m", collcollate)));
}
else
{
locale_t loc1;
loc1 = newlocale(LC_COLLATE_MASK, collcollate, NULL);
if (!loc1)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not create locale \"%s\": %m", collcollate)));
result = newlocale(LC_CTYPE_MASK, collctype, loc1);
if (!result)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not create locale \"%s\": %m", collctype)));
}
ReleaseSysCache(tp);
cache_entry->locale = result;
return result;
#else /* not HAVE_LOCALE_T */
/*
* For platforms that don't support locale_t, check that we are
* dealing with the default locale. It's unlikely that we'll get
* here, but it's possible if users are creating collations even
* though they are not supported, or they are mixing builds in odd
* ways.
*/
if (!OidIsValid(collid))
elog(ERROR, "locale operation to be invoked, but no collation was derived");
else if (collid != DEFAULT_COLLATION_OID)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("nondefault collations are not supported on this platform")));
return 0;
#endif /* not HAVE_LOCALE_T */
}

View File

@ -23,6 +23,7 @@
#include "catalog/dependency.h" #include "catalog/dependency.h"
#include "catalog/indexing.h" #include "catalog/indexing.h"
#include "catalog/pg_authid.h" #include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h" #include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h" #include "catalog/pg_depend.h"
#include "catalog/pg_language.h" #include "catalog/pg_language.h"
@ -233,7 +234,7 @@ static void get_from_clause_item(Node *jtnode, Query *query,
deparse_context *context); deparse_context *context);
static void get_from_clause_alias(Alias *alias, RangeTblEntry *rte, static void get_from_clause_alias(Alias *alias, RangeTblEntry *rte,
deparse_context *context); deparse_context *context);
static void get_from_clause_coldeflist(List *names, List *types, List *typmods, static void get_from_clause_coldeflist(List *names, List *types, List *typmods, List *collations,
deparse_context *context); deparse_context *context);
static void get_opclass_name(Oid opclass, Oid actual_datatype, static void get_opclass_name(Oid opclass, Oid actual_datatype,
StringInfo buf); StringInfo buf);
@ -788,9 +789,11 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
Oid indrelid; Oid indrelid;
int keyno; int keyno;
Oid keycoltype; Oid keycoltype;
Datum indcollDatum;
Datum indclassDatum; Datum indclassDatum;
Datum indoptionDatum; Datum indoptionDatum;
bool isnull; bool isnull;
oidvector *indcollation;
oidvector *indclass; oidvector *indclass;
int2vector *indoption; int2vector *indoption;
StringInfoData buf; StringInfoData buf;
@ -808,11 +811,17 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
indrelid = idxrec->indrelid; indrelid = idxrec->indrelid;
Assert(indexrelid == idxrec->indexrelid); Assert(indexrelid == idxrec->indexrelid);
/* Must get indclass and indoption the hard way */ /* Must get indcollation, indclass, and indoption the hard way */
indcollDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
Anum_pg_index_indcollation, &isnull);
Assert(!isnull);
indcollation = (oidvector *) DatumGetPointer(indcollDatum);
indclassDatum = SysCacheGetAttr(INDEXRELID, ht_idx, indclassDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
Anum_pg_index_indclass, &isnull); Anum_pg_index_indclass, &isnull);
Assert(!isnull); Assert(!isnull);
indclass = (oidvector *) DatumGetPointer(indclassDatum); indclass = (oidvector *) DatumGetPointer(indclassDatum);
indoptionDatum = SysCacheGetAttr(INDEXRELID, ht_idx, indoptionDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
Anum_pg_index_indoption, &isnull); Anum_pg_index_indoption, &isnull);
Assert(!isnull); Assert(!isnull);
@ -928,6 +937,13 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
if (!attrsOnly && (!colno || colno == keyno + 1)) if (!attrsOnly && (!colno || colno == keyno + 1))
{ {
Oid coll;
/* Add collation, if not default */
coll = indcollation->values[keyno];
if (coll && coll != DEFAULT_COLLATION_OID && coll != get_attcollation(indrelid, attnum))
appendStringInfo(&buf, " COLLATE %s", generate_collation_name((indcollation->values[keyno])));
/* Add the operator class name, if not default */ /* Add the operator class name, if not default */
get_opclass_name(indclass->values[keyno], keycoltype, &buf); get_opclass_name(indclass->values[keyno], keycoltype, &buf);
@ -5054,6 +5070,20 @@ get_rule_expr(Node *node, deparse_context *context,
} }
break; break;
case T_CollateClause:
{
CollateClause *collate = (CollateClause *) node;
Node *arg = (Node *) collate->arg;
if (!PRETTY_PAREN(context))
appendStringInfoChar(buf, '(');
get_rule_expr_paren(arg, context, false, node);
appendStringInfo(buf, " COLLATE %s", generate_collation_name(collate->collOid));
if (!PRETTY_PAREN(context))
appendStringInfoChar(buf, ')');
}
break;
case T_CoerceViaIO: case T_CoerceViaIO:
{ {
CoerceViaIO *iocoerce = (CoerceViaIO *) node; CoerceViaIO *iocoerce = (CoerceViaIO *) node;
@ -6345,6 +6375,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
get_from_clause_coldeflist(rte->eref->colnames, get_from_clause_coldeflist(rte->eref->colnames,
rte->funccoltypes, rte->funccoltypes,
rte->funccoltypmods, rte->funccoltypmods,
rte->funccolcollations,
context); context);
} }
else else
@ -6543,35 +6574,42 @@ get_from_clause_alias(Alias *alias, RangeTblEntry *rte,
* responsible for ensuring that an alias or AS is present before it. * responsible for ensuring that an alias or AS is present before it.
*/ */
static void static void
get_from_clause_coldeflist(List *names, List *types, List *typmods, get_from_clause_coldeflist(List *names, List *types, List *typmods, List *collations,
deparse_context *context) deparse_context *context)
{ {
StringInfo buf = context->buf; StringInfo buf = context->buf;
ListCell *l1; ListCell *l1;
ListCell *l2; ListCell *l2;
ListCell *l3; ListCell *l3;
ListCell *l4;
int i = 0; int i = 0;
appendStringInfoChar(buf, '('); appendStringInfoChar(buf, '(');
l2 = list_head(types); l2 = list_head(types);
l3 = list_head(typmods); l3 = list_head(typmods);
l4 = list_head(collations);
foreach(l1, names) foreach(l1, names)
{ {
char *attname = strVal(lfirst(l1)); char *attname = strVal(lfirst(l1));
Oid atttypid; Oid atttypid;
int32 atttypmod; int32 atttypmod;
Oid attcollation;
atttypid = lfirst_oid(l2); atttypid = lfirst_oid(l2);
l2 = lnext(l2); l2 = lnext(l2);
atttypmod = lfirst_int(l3); atttypmod = lfirst_int(l3);
l3 = lnext(l3); l3 = lnext(l3);
attcollation = lfirst_oid(l4);
l4 = lnext(l4);
if (i > 0) if (i > 0)
appendStringInfo(buf, ", "); appendStringInfo(buf, ", ");
appendStringInfo(buf, "%s %s", appendStringInfo(buf, "%s %s",
quote_identifier(attname), quote_identifier(attname),
format_type_with_typemod(atttypid, atttypmod)); format_type_with_typemod(atttypid, atttypmod));
if (attcollation && attcollation != DEFAULT_COLLATION_OID)
appendStringInfo(buf, " COLLATE %s", generate_collation_name(attcollation));
i++; i++;
} }
@ -7038,6 +7076,39 @@ generate_operator_name(Oid operid, Oid arg1, Oid arg2)
return buf.data; return buf.data;
} }
/*
* generate_collation_name
* Compute the name to display for a collation specified by OID
*
* The result includes all necessary quoting and schema-prefixing.
*/
char *
generate_collation_name(Oid collid)
{
HeapTuple tp;
Form_pg_collation colltup;
char *collname;
char *nspname;
char *result;
tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for collation %u", collid);
colltup = (Form_pg_collation) GETSTRUCT(tp);
collname = NameStr(colltup->collname);
if (!CollationIsVisible(collid))
nspname = get_namespace_name(colltup->collnamespace);
else
nspname = NULL;
result = quote_qualified_identifier(nspname, collname);
ReleaseSysCache(tp);
return result;
}
/* /*
* Given a C string, produce a TEXT datum. * Given a C string, produce a TEXT datum.
* *

View File

@ -94,6 +94,7 @@
#include "access/gin.h" #include "access/gin.h"
#include "access/sysattr.h" #include "access/sysattr.h"
#include "catalog/index.h" #include "catalog/index.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_opfamily.h" #include "catalog/pg_opfamily.h"
#include "catalog/pg_statistic.h" #include "catalog/pg_statistic.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
@ -144,7 +145,7 @@ static double eqjoinsel_inner(Oid operator,
static double eqjoinsel_semi(Oid operator, static double eqjoinsel_semi(Oid operator,
VariableStatData *vardata1, VariableStatData *vardata2); VariableStatData *vardata1, VariableStatData *vardata2);
static bool convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue, static bool convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue,
Datum lobound, Datum hibound, Oid boundstypid, Datum lobound, Datum hibound, Oid boundstypid, Oid boundscollid,
double *scaledlobound, double *scaledhibound); double *scaledlobound, double *scaledhibound);
static double convert_numeric_to_scalar(Datum value, Oid typid); static double convert_numeric_to_scalar(Datum value, Oid typid);
static void convert_string_to_scalar(char *value, static void convert_string_to_scalar(char *value,
@ -163,10 +164,10 @@ static double convert_one_string_to_scalar(char *value,
int rangelo, int rangehi); int rangelo, int rangehi);
static double convert_one_bytea_to_scalar(unsigned char *value, int valuelen, static double convert_one_bytea_to_scalar(unsigned char *value, int valuelen,
int rangelo, int rangehi); int rangelo, int rangehi);
static char *convert_string_datum(Datum value, Oid typid); static char *convert_string_datum(Datum value, Oid typid, Oid collid);
static double convert_timevalue_to_scalar(Datum value, Oid typid); static double convert_timevalue_to_scalar(Datum value, Oid typid);
static bool get_variable_range(PlannerInfo *root, VariableStatData *vardata, static bool get_variable_range(PlannerInfo *root, VariableStatData *vardata,
Oid sortop, Datum *min, Datum *max); Oid sortop, Oid collation, Datum *min, Datum *max);
static bool get_actual_variable_range(PlannerInfo *root, static bool get_actual_variable_range(PlannerInfo *root,
VariableStatData *vardata, VariableStatData *vardata,
Oid sortop, Oid sortop,
@ -513,6 +514,7 @@ scalarineqsel(PlannerInfo *root, Oid operator, bool isgt,
stats = (Form_pg_statistic) GETSTRUCT(vardata->statsTuple); stats = (Form_pg_statistic) GETSTRUCT(vardata->statsTuple);
fmgr_info(get_opcode(operator), &opproc); fmgr_info(get_opcode(operator), &opproc);
fmgr_info_collation(vardata->attcollation, &opproc);
/* /*
* If we have most-common-values info, add up the fractions of the MCV * If we have most-common-values info, add up the fractions of the MCV
@ -837,7 +839,7 @@ ineq_histogram_selectivity(PlannerInfo *root,
*/ */
if (convert_to_scalar(constval, consttype, &val, if (convert_to_scalar(constval, consttype, &val,
values[i - 1], values[i], values[i - 1], values[i],
vardata->vartype, vardata->vartype, vardata->attcollation,
&low, &high)) &low, &high))
{ {
if (high <= low) if (high <= low)
@ -1249,6 +1251,7 @@ patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype, bool negate)
/* Try to use the histogram entries to get selectivity */ /* Try to use the histogram entries to get selectivity */
fmgr_info(get_opcode(operator), &opproc); fmgr_info(get_opcode(operator), &opproc);
fmgr_info_collation(DEFAULT_COLLATION_OID, &opproc);
selec = histogram_selectivity(&vardata, &opproc, constval, true, selec = histogram_selectivity(&vardata, &opproc, constval, true,
10, 1, &hist_size); 10, 1, &hist_size);
@ -2585,7 +2588,7 @@ icnlikejoinsel(PG_FUNCTION_ARGS)
*/ */
void void
mergejoinscansel(PlannerInfo *root, Node *clause, mergejoinscansel(PlannerInfo *root, Node *clause,
Oid opfamily, int strategy, bool nulls_first, Oid opfamily, Oid collation, int strategy, bool nulls_first,
Selectivity *leftstart, Selectivity *leftend, Selectivity *leftstart, Selectivity *leftend,
Selectivity *rightstart, Selectivity *rightend) Selectivity *rightstart, Selectivity *rightend)
{ {
@ -2754,20 +2757,20 @@ mergejoinscansel(PlannerInfo *root, Node *clause,
/* Try to get ranges of both inputs */ /* Try to get ranges of both inputs */
if (!isgt) if (!isgt)
{ {
if (!get_variable_range(root, &leftvar, lstatop, if (!get_variable_range(root, &leftvar, lstatop, collation,
&leftmin, &leftmax)) &leftmin, &leftmax))
goto fail; /* no range available from stats */ goto fail; /* no range available from stats */
if (!get_variable_range(root, &rightvar, rstatop, if (!get_variable_range(root, &rightvar, rstatop, collation,
&rightmin, &rightmax)) &rightmin, &rightmax))
goto fail; /* no range available from stats */ goto fail; /* no range available from stats */
} }
else else
{ {
/* need to swap the max and min */ /* need to swap the max and min */
if (!get_variable_range(root, &leftvar, lstatop, if (!get_variable_range(root, &leftvar, lstatop, collation,
&leftmax, &leftmin)) &leftmax, &leftmin))
goto fail; /* no range available from stats */ goto fail; /* no range available from stats */
if (!get_variable_range(root, &rightvar, rstatop, if (!get_variable_range(root, &rightvar, rstatop, collation,
&rightmax, &rightmin)) &rightmax, &rightmin))
goto fail; /* no range available from stats */ goto fail; /* no range available from stats */
} }
@ -3368,7 +3371,7 @@ estimate_hash_bucketsize(PlannerInfo *root, Node *hashkey, double nbuckets)
*/ */
static bool static bool
convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue, convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue,
Datum lobound, Datum hibound, Oid boundstypid, Datum lobound, Datum hibound, Oid boundstypid, Oid boundscollid,
double *scaledlobound, double *scaledhibound) double *scaledlobound, double *scaledhibound)
{ {
/* /*
@ -3421,9 +3424,9 @@ convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue,
case TEXTOID: case TEXTOID:
case NAMEOID: case NAMEOID:
{ {
char *valstr = convert_string_datum(value, valuetypid); char *valstr = convert_string_datum(value, valuetypid, boundscollid);
char *lostr = convert_string_datum(lobound, boundstypid); char *lostr = convert_string_datum(lobound, boundstypid, boundscollid);
char *histr = convert_string_datum(hibound, boundstypid); char *histr = convert_string_datum(hibound, boundstypid, boundscollid);
convert_string_to_scalar(valstr, scaledvalue, convert_string_to_scalar(valstr, scaledvalue,
lostr, scaledlobound, lostr, scaledlobound,
@ -3667,7 +3670,7 @@ convert_one_string_to_scalar(char *value, int rangelo, int rangehi)
* before continuing, so as to generate correct locale-specific results. * before continuing, so as to generate correct locale-specific results.
*/ */
static char * static char *
convert_string_datum(Datum value, Oid typid) convert_string_datum(Datum value, Oid typid, Oid collid)
{ {
char *val; char *val;
@ -3700,7 +3703,7 @@ convert_string_datum(Datum value, Oid typid)
return NULL; return NULL;
} }
if (!lc_collate_is_c()) if (!lc_collate_is_c(collid))
{ {
char *xfrmstr; char *xfrmstr;
size_t xfrmlen; size_t xfrmlen;
@ -4099,6 +4102,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
vardata->rel = find_base_rel(root, var->varno); vardata->rel = find_base_rel(root, var->varno);
vardata->atttype = var->vartype; vardata->atttype = var->vartype;
vardata->atttypmod = var->vartypmod; vardata->atttypmod = var->vartypmod;
vardata->attcollation = var->varcollid;
vardata->isunique = has_unique_index(vardata->rel, var->varattno); vardata->isunique = has_unique_index(vardata->rel, var->varattno);
rte = root->simple_rte_array[var->varno]; rte = root->simple_rte_array[var->varno];
@ -4184,6 +4188,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
vardata->var = node; vardata->var = node;
vardata->atttype = exprType(node); vardata->atttype = exprType(node);
vardata->atttypmod = exprTypmod(node); vardata->atttypmod = exprTypmod(node);
vardata->attcollation = exprCollation(node);
if (onerel) if (onerel)
{ {
@ -4392,7 +4397,7 @@ get_variable_numdistinct(VariableStatData *vardata)
* be "<" not ">", as only the former is likely to be found in pg_statistic. * be "<" not ">", as only the former is likely to be found in pg_statistic.
*/ */
static bool static bool
get_variable_range(PlannerInfo *root, VariableStatData *vardata, Oid sortop, get_variable_range(PlannerInfo *root, VariableStatData *vardata, Oid sortop, Oid collation,
Datum *min, Datum *max) Datum *min, Datum *max)
{ {
Datum tmin = 0; Datum tmin = 0;
@ -4477,6 +4482,7 @@ get_variable_range(PlannerInfo *root, VariableStatData *vardata, Oid sortop,
FmgrInfo opproc; FmgrInfo opproc;
fmgr_info(get_opcode(sortop), &opproc); fmgr_info(get_opcode(sortop), &opproc);
fmgr_info_collation(collation, &opproc);
for (i = 0; i < nvalues; i++) for (i = 0; i < nvalues; i++)
{ {
@ -5482,7 +5488,7 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc)
{ {
workstr = TextDatumGetCString(str_const->constvalue); workstr = TextDatumGetCString(str_const->constvalue);
len = strlen(workstr); len = strlen(workstr);
if (lc_collate_is_c() || len == 0) if (lc_collate_is_c(ltproc->fn_collation) || len == 0)
cmpstr = str_const->constvalue; cmpstr = str_const->constvalue;
else else
{ {
@ -5494,11 +5500,11 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc)
char *best; char *best;
best = "Z"; best = "Z";
if (varstr_cmp(best, 1, "z", 1) < 0) if (varstr_cmp(best, 1, "z", 1, DEFAULT_COLLATION_OID) < 0)
best = "z"; best = "z";
if (varstr_cmp(best, 1, "y", 1) < 0) if (varstr_cmp(best, 1, "y", 1, DEFAULT_COLLATION_OID) < 0)
best = "y"; best = "y";
if (varstr_cmp(best, 1, "9", 1) < 0) if (varstr_cmp(best, 1, "9", 1, DEFAULT_COLLATION_OID) < 0)
best = "9"; best = "9";
suffixchar = *best; suffixchar = *best;
} }

View File

@ -737,7 +737,8 @@ bpcharlt(PG_FUNCTION_ARGS)
len1 = bcTruelen(arg1); len1 = bcTruelen(arg1);
len2 = bcTruelen(arg2); len2 = bcTruelen(arg2);
cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2); cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2,
PG_GET_COLLATION());
PG_FREE_IF_COPY(arg1, 0); PG_FREE_IF_COPY(arg1, 0);
PG_FREE_IF_COPY(arg2, 1); PG_FREE_IF_COPY(arg2, 1);
@ -757,7 +758,8 @@ bpcharle(PG_FUNCTION_ARGS)
len1 = bcTruelen(arg1); len1 = bcTruelen(arg1);
len2 = bcTruelen(arg2); len2 = bcTruelen(arg2);
cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2); cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2,
PG_GET_COLLATION());
PG_FREE_IF_COPY(arg1, 0); PG_FREE_IF_COPY(arg1, 0);
PG_FREE_IF_COPY(arg2, 1); PG_FREE_IF_COPY(arg2, 1);
@ -777,7 +779,8 @@ bpchargt(PG_FUNCTION_ARGS)
len1 = bcTruelen(arg1); len1 = bcTruelen(arg1);
len2 = bcTruelen(arg2); len2 = bcTruelen(arg2);
cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2); cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2,
PG_GET_COLLATION());
PG_FREE_IF_COPY(arg1, 0); PG_FREE_IF_COPY(arg1, 0);
PG_FREE_IF_COPY(arg2, 1); PG_FREE_IF_COPY(arg2, 1);
@ -797,7 +800,8 @@ bpcharge(PG_FUNCTION_ARGS)
len1 = bcTruelen(arg1); len1 = bcTruelen(arg1);
len2 = bcTruelen(arg2); len2 = bcTruelen(arg2);
cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2); cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2,
PG_GET_COLLATION());
PG_FREE_IF_COPY(arg1, 0); PG_FREE_IF_COPY(arg1, 0);
PG_FREE_IF_COPY(arg2, 1); PG_FREE_IF_COPY(arg2, 1);
@ -817,7 +821,8 @@ bpcharcmp(PG_FUNCTION_ARGS)
len1 = bcTruelen(arg1); len1 = bcTruelen(arg1);
len2 = bcTruelen(arg2); len2 = bcTruelen(arg2);
cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2); cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2,
PG_GET_COLLATION());
PG_FREE_IF_COPY(arg1, 0); PG_FREE_IF_COPY(arg1, 0);
PG_FREE_IF_COPY(arg2, 1); PG_FREE_IF_COPY(arg2, 1);
@ -837,7 +842,8 @@ bpchar_larger(PG_FUNCTION_ARGS)
len1 = bcTruelen(arg1); len1 = bcTruelen(arg1);
len2 = bcTruelen(arg2); len2 = bcTruelen(arg2);
cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2); cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2,
PG_GET_COLLATION());
PG_RETURN_BPCHAR_P((cmp >= 0) ? arg1 : arg2); PG_RETURN_BPCHAR_P((cmp >= 0) ? arg1 : arg2);
} }
@ -854,7 +860,8 @@ bpchar_smaller(PG_FUNCTION_ARGS)
len1 = bcTruelen(arg1); len1 = bcTruelen(arg1);
len2 = bcTruelen(arg2); len2 = bcTruelen(arg2);
cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2); cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2,
PG_GET_COLLATION());
PG_RETURN_BPCHAR_P((cmp <= 0) ? arg1 : arg2); PG_RETURN_BPCHAR_P((cmp <= 0) ? arg1 : arg2);
} }

View File

@ -18,6 +18,7 @@
#include <limits.h> #include <limits.h>
#include "access/tuptoaster.h" #include "access/tuptoaster.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "libpq/md5.h" #include "libpq/md5.h"
#include "libpq/pqformat.h" #include "libpq/pqformat.h"
@ -55,7 +56,7 @@ typedef struct
#define PG_GETARG_UNKNOWN_P_COPY(n) DatumGetUnknownPCopy(PG_GETARG_DATUM(n)) #define PG_GETARG_UNKNOWN_P_COPY(n) DatumGetUnknownPCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_UNKNOWN_P(x) PG_RETURN_POINTER(x) #define PG_RETURN_UNKNOWN_P(x) PG_RETURN_POINTER(x)
static int text_cmp(text *arg1, text *arg2); static int text_cmp(text *arg1, text *arg2, Oid collid);
static int32 text_length(Datum str); static int32 text_length(Datum str);
static int text_position(text *t1, text *t2); static int text_position(text *t1, text *t2);
static void text_position_setup(text *t1, text *t2, TextPositionState *state); static void text_position_setup(text *t1, text *t2, TextPositionState *state);
@ -1274,7 +1275,7 @@ text_position_cleanup(TextPositionState *state)
* whether arg1 is less than, equal to, or greater than arg2. * whether arg1 is less than, equal to, or greater than arg2.
*/ */
int int
varstr_cmp(char *arg1, int len1, char *arg2, int len2) varstr_cmp(char *arg1, int len1, char *arg2, int len2, Oid collid)
{ {
int result; int result;
@ -1284,7 +1285,7 @@ varstr_cmp(char *arg1, int len1, char *arg2, int len2)
* slower, so we optimize the case where LC_COLLATE is C. We also try to * slower, so we optimize the case where LC_COLLATE is C. We also try to
* optimize relatively-short strings by avoiding palloc/pfree overhead. * optimize relatively-short strings by avoiding palloc/pfree overhead.
*/ */
if (lc_collate_is_c()) if (lc_collate_is_c(collid))
{ {
result = memcmp(arg1, arg2, Min(len1, len2)); result = memcmp(arg1, arg2, Min(len1, len2));
if ((result == 0) && (len1 != len2)) if ((result == 0) && (len1 != len2))
@ -1298,6 +1299,10 @@ varstr_cmp(char *arg1, int len1, char *arg2, int len2)
char a2buf[STACKBUFLEN]; char a2buf[STACKBUFLEN];
char *a1p, char *a1p,
*a2p; *a2p;
pg_locale_t mylocale = 0;
if (collid != DEFAULT_COLLATION_OID)
mylocale = pg_newlocale_from_collation(collid);
#ifdef WIN32 #ifdef WIN32
/* Win32 does not have UTF-8, so we need to map to UTF-16 */ /* Win32 does not have UTF-8, so we need to map to UTF-16 */
@ -1398,6 +1403,11 @@ varstr_cmp(char *arg1, int len1, char *arg2, int len2)
memcpy(a2p, arg2, len2); memcpy(a2p, arg2, len2);
a2p[len2] = '\0'; a2p[len2] = '\0';
#ifdef HAVE_LOCALE_T
if (mylocale)
result = strcoll_l(a1p, a2p, mylocale);
else
#endif
result = strcoll(a1p, a2p); result = strcoll(a1p, a2p);
/* /*
@ -1424,7 +1434,7 @@ varstr_cmp(char *arg1, int len1, char *arg2, int len2)
* Returns -1, 0 or 1 * Returns -1, 0 or 1
*/ */
static int static int
text_cmp(text *arg1, text *arg2) text_cmp(text *arg1, text *arg2, Oid collid)
{ {
char *a1p, char *a1p,
*a2p; *a2p;
@ -1437,7 +1447,7 @@ text_cmp(text *arg1, text *arg2)
len1 = VARSIZE_ANY_EXHDR(arg1); len1 = VARSIZE_ANY_EXHDR(arg1);
len2 = VARSIZE_ANY_EXHDR(arg2); len2 = VARSIZE_ANY_EXHDR(arg2);
return varstr_cmp(a1p, len1, a2p, len2); return varstr_cmp(a1p, len1, a2p, len2, collid);
} }
/* /*
@ -1519,7 +1529,7 @@ text_lt(PG_FUNCTION_ARGS)
text *arg2 = PG_GETARG_TEXT_PP(1); text *arg2 = PG_GETARG_TEXT_PP(1);
bool result; bool result;
result = (text_cmp(arg1, arg2) < 0); result = (text_cmp(arg1, arg2, PG_GET_COLLATION()) < 0);
PG_FREE_IF_COPY(arg1, 0); PG_FREE_IF_COPY(arg1, 0);
PG_FREE_IF_COPY(arg2, 1); PG_FREE_IF_COPY(arg2, 1);
@ -1534,7 +1544,7 @@ text_le(PG_FUNCTION_ARGS)
text *arg2 = PG_GETARG_TEXT_PP(1); text *arg2 = PG_GETARG_TEXT_PP(1);
bool result; bool result;
result = (text_cmp(arg1, arg2) <= 0); result = (text_cmp(arg1, arg2, PG_GET_COLLATION()) <= 0);
PG_FREE_IF_COPY(arg1, 0); PG_FREE_IF_COPY(arg1, 0);
PG_FREE_IF_COPY(arg2, 1); PG_FREE_IF_COPY(arg2, 1);
@ -1549,7 +1559,7 @@ text_gt(PG_FUNCTION_ARGS)
text *arg2 = PG_GETARG_TEXT_PP(1); text *arg2 = PG_GETARG_TEXT_PP(1);
bool result; bool result;
result = (text_cmp(arg1, arg2) > 0); result = (text_cmp(arg1, arg2, PG_GET_COLLATION()) > 0);
PG_FREE_IF_COPY(arg1, 0); PG_FREE_IF_COPY(arg1, 0);
PG_FREE_IF_COPY(arg2, 1); PG_FREE_IF_COPY(arg2, 1);
@ -1564,7 +1574,7 @@ text_ge(PG_FUNCTION_ARGS)
text *arg2 = PG_GETARG_TEXT_PP(1); text *arg2 = PG_GETARG_TEXT_PP(1);
bool result; bool result;
result = (text_cmp(arg1, arg2) >= 0); result = (text_cmp(arg1, arg2, PG_GET_COLLATION()) >= 0);
PG_FREE_IF_COPY(arg1, 0); PG_FREE_IF_COPY(arg1, 0);
PG_FREE_IF_COPY(arg2, 1); PG_FREE_IF_COPY(arg2, 1);
@ -1579,7 +1589,7 @@ bttextcmp(PG_FUNCTION_ARGS)
text *arg2 = PG_GETARG_TEXT_PP(1); text *arg2 = PG_GETARG_TEXT_PP(1);
int32 result; int32 result;
result = text_cmp(arg1, arg2); result = text_cmp(arg1, arg2, PG_GET_COLLATION());
PG_FREE_IF_COPY(arg1, 0); PG_FREE_IF_COPY(arg1, 0);
PG_FREE_IF_COPY(arg2, 1); PG_FREE_IF_COPY(arg2, 1);
@ -1595,7 +1605,7 @@ text_larger(PG_FUNCTION_ARGS)
text *arg2 = PG_GETARG_TEXT_PP(1); text *arg2 = PG_GETARG_TEXT_PP(1);
text *result; text *result;
result = ((text_cmp(arg1, arg2) > 0) ? arg1 : arg2); result = ((text_cmp(arg1, arg2, PG_GET_COLLATION()) > 0) ? arg1 : arg2);
PG_RETURN_TEXT_P(result); PG_RETURN_TEXT_P(result);
} }
@ -1607,7 +1617,7 @@ text_smaller(PG_FUNCTION_ARGS)
text *arg2 = PG_GETARG_TEXT_PP(1); text *arg2 = PG_GETARG_TEXT_PP(1);
text *result; text *result;
result = ((text_cmp(arg1, arg2) < 0) ? arg1 : arg2); result = ((text_cmp(arg1, arg2, PG_GET_COLLATION()) < 0) ? arg1 : arg2);
PG_RETURN_TEXT_P(result); PG_RETURN_TEXT_P(result);
} }

View File

@ -20,6 +20,7 @@
#include "bootstrap/bootstrap.h" #include "bootstrap/bootstrap.h"
#include "catalog/pg_amop.h" #include "catalog/pg_amop.h"
#include "catalog/pg_amproc.h" #include "catalog/pg_amproc.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h" #include "catalog/pg_constraint.h"
#include "catalog/pg_namespace.h" #include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h" #include "catalog/pg_opclass.h"
@ -902,6 +903,33 @@ get_atttypmod(Oid relid, AttrNumber attnum)
return -1; return -1;
} }
/*
* get_attcollation
*
* Given the relation id and the attribute number,
* return the "attcollation" field from the attribute relation.
*/
Oid
get_attcollation(Oid relid, AttrNumber attnum)
{
HeapTuple tp;
tp = SearchSysCache2(ATTNUM,
ObjectIdGetDatum(relid),
Int16GetDatum(attnum));
if (HeapTupleIsValid(tp))
{
Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp);
Oid result;
result = att_tup->attcollation;
ReleaseSysCache(tp);
return result;
}
else
return InvalidOid;
}
/* /*
* get_atttypetypmod * get_atttypetypmod
* *
@ -931,6 +959,36 @@ get_atttypetypmod(Oid relid, AttrNumber attnum,
ReleaseSysCache(tp); ReleaseSysCache(tp);
} }
/* ---------- COLLATION CACHE ---------- */
/*
* get_collation_name
* Returns the name of a given pg_collation entry.
*
* Returns a palloc'd copy of the string, or NULL if no such constraint.
*
* NOTE: since collation name is not unique, be wary of code that uses this
* for anything except preparing error messages.
*/
char *
get_collation_name(Oid colloid)
{
HeapTuple tp;
tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(colloid));
if (HeapTupleIsValid(tp))
{
Form_pg_collation colltup = (Form_pg_collation) GETSTRUCT(tp);
char *result;
result = pstrdup(NameStr(colltup->collname));
ReleaseSysCache(tp);
return result;
}
else
return NULL;
}
/* ---------- CONSTRAINT CACHE ---------- */ /* ---------- CONSTRAINT CACHE ---------- */
/* /*
@ -2523,6 +2581,42 @@ get_typmodout(Oid typid)
} }
#endif /* NOT_USED */ #endif /* NOT_USED */
/*
* get_typcollation
*
* Given the type OID, return the type's typcollation attribute.
*/
Oid
get_typcollation(Oid typid)
{
HeapTuple tp;
tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (HeapTupleIsValid(tp))
{
Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
Oid result;
result = typtup->typcollation;
ReleaseSysCache(tp);
return result;
}
else
return InvalidOid;
}
/*
* type_is_collatable
*
* Return whether the type cares about collations
*/
bool
type_is_collatable(Oid typid)
{
return OidIsValid(get_typcollation(typid));
}
/* ---------- STATISTICS CACHE ---------- */ /* ---------- STATISTICS CACHE ---------- */

Some files were not shown because too many files have changed in this diff Show More