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
AC_MSG_RESULT([$pgac_cv_printf_arg_control])
])# PGAC_FUNC_PRINTF_ARG_CONTROL
# PGAC_TYPE_LOCALE_T
# ------------------
# Check for the locale_t type and find the right header file. Mac OS
# X needs xlocale.h; standard is locale.h, but glibc also has an
# xlocale.h file that we should not use.
#
AC_DEFUN([PGAC_TYPE_LOCALE_T],
[AC_CACHE_CHECK([for locale_t], pgac_cv_type_locale_t,
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
[#include <locale.h>
locale_t x;],
[])],
[pgac_cv_type_locale_t=yes],
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
[#include <xlocale.h>
locale_t x;],
[])],
[pgac_cv_type_locale_t='yes (in xlocale.h)'],
[pgac_cv_type_locale_t=no])])])
if test "$pgac_cv_type_locale_t" != no; then
AC_DEFINE(HAVE_LOCALE_T, 1,
[Define to 1 if the system has the type `locale_t'.])
fi
if test "$pgac_cv_type_locale_t" = 'yes (in xlocale.h)'; then
AC_DEFINE(LOCALE_T_IN_XLOCALE, 1,
[Define to 1 if `locale_t' requires <xlocale.h>.])
fi])])# PGAC_HEADER_XLOCALE

108
configure vendored
View File

@ -16830,6 +16830,114 @@ _ACEOF
fi
{ $as_echo "$as_me:$LINENO: checking for locale_t" >&5
$as_echo_n "checking for locale_t... " >&6; }
if test "${pgac_cv_type_locale_t+set}" = set; then
$as_echo_n "(cached) " >&6
else
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
#include <locale.h>
locale_t x;
int
main ()
{
;
return 0;
}
_ACEOF
rm -f conftest.$ac_objext
if { (ac_try="$ac_compile"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
$as_echo "$ac_try_echo") >&5
(eval "$ac_compile") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
$as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && {
test -z "$ac_c_werror_flag" ||
test ! -s conftest.err
} && test -s conftest.$ac_objext; then
pgac_cv_type_locale_t=yes
else
$as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
#include <xlocale.h>
locale_t x;
int
main ()
{
;
return 0;
}
_ACEOF
rm -f conftest.$ac_objext
if { (ac_try="$ac_compile"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
$as_echo "$ac_try_echo") >&5
(eval "$ac_compile") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
$as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && {
test -z "$ac_c_werror_flag" ||
test ! -s conftest.err
} && test -s conftest.$ac_objext; then
pgac_cv_type_locale_t='yes (in xlocale.h)'
else
$as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
pgac_cv_type_locale_t=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
{ $as_echo "$as_me:$LINENO: result: $pgac_cv_type_locale_t" >&5
$as_echo "$pgac_cv_type_locale_t" >&6; }
if test "$pgac_cv_type_locale_t" != no; then
cat >>confdefs.h <<\_ACEOF
#define HAVE_LOCALE_T 1
_ACEOF
fi
if test "$pgac_cv_type_locale_t" = 'yes (in xlocale.h)'; then
cat >>confdefs.h <<\_ACEOF
#define LOCALE_T_IN_XLOCALE 1
_ACEOF
fi
{ $as_echo "$as_me:$LINENO: checking for struct cmsgcred" >&5
$as_echo_n "checking for struct cmsgcred... " >&6; }
if test "${ac_cv_type_struct_cmsgcred+set}" = set; then

View File

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

View File

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

View File

@ -3,6 +3,7 @@
*/
#include "btree_gist.h"
#include "btree_utils_var.h"
#include "catalog/pg_collation.h"
#include "utils/builtins.h"
/*
@ -32,37 +33,37 @@ Datum gbt_text_same(PG_FUNCTION_ARGS);
static bool
gbt_textgt(const void *a, const void *b)
{
return (DatumGetBool(DirectFunctionCall2(text_gt, PointerGetDatum(a), PointerGetDatum(b))));
return (DatumGetBool(DirectFunctionCall2WithCollation(text_gt, DEFAULT_COLLATION_OID, PointerGetDatum(a), PointerGetDatum(b))));
}
static bool
gbt_textge(const void *a, const void *b)
{
return (DatumGetBool(DirectFunctionCall2(text_ge, PointerGetDatum(a), PointerGetDatum(b))));
return (DatumGetBool(DirectFunctionCall2WithCollation(text_ge, DEFAULT_COLLATION_OID, PointerGetDatum(a), PointerGetDatum(b))));
}
static bool
gbt_texteq(const void *a, const void *b)
{
return (DatumGetBool(DirectFunctionCall2(texteq, PointerGetDatum(a), PointerGetDatum(b))));
return (DatumGetBool(DirectFunctionCall2WithCollation(texteq, DEFAULT_COLLATION_OID, PointerGetDatum(a), PointerGetDatum(b))));
}
static bool
gbt_textle(const void *a, const void *b)
{
return (DatumGetBool(DirectFunctionCall2(text_le, PointerGetDatum(a), PointerGetDatum(b))));
return (DatumGetBool(DirectFunctionCall2WithCollation(text_le, DEFAULT_COLLATION_OID, PointerGetDatum(a), PointerGetDatum(b))));
}
static bool
gbt_textlt(const void *a, const void *b)
{
return (DatumGetBool(DirectFunctionCall2(text_lt, PointerGetDatum(a), PointerGetDatum(b))));
return (DatumGetBool(DirectFunctionCall2WithCollation(text_lt, DEFAULT_COLLATION_OID, PointerGetDatum(a), PointerGetDatum(b))));
}
static int32
gbt_textcmp(const bytea *a, const bytea *b)
{
return DatumGetInt32(DirectFunctionCall2(bttextcmp, PointerGetDatum(a), PointerGetDatum(b)));
return DatumGetInt32(DirectFunctionCall2WithCollation(bttextcmp, DEFAULT_COLLATION_OID, PointerGetDatum(a), PointerGetDatum(b)));
}
static gbtree_vinfo tinfo =

View File

@ -8,6 +8,7 @@
#include <float.h>
#include "btree_utils_var.h"
#include "catalog/pg_collation.h"
#include "utils/pg_locale.h"
#include "utils/builtins.h"
#include "utils/rel.h"
@ -156,7 +157,7 @@ gbt_bytea_pf_match(const bytea *pf, const bytea *query, const gbtree_vinfo *tinf
if (tinfo->eml > 1)
{
out = (varstr_cmp(q, nlen, n, nlen) == 0);
out = (varstr_cmp(q, nlen, n, nlen, DEFAULT_COLLATION_OID) == 0);
}
else
{

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

View File

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

View File

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

View File

@ -103,6 +103,11 @@
<entry>check constraints, unique constraints, primary key constraints, foreign key constraints</entry>
</row>
<row>
<entry><link linkend="catalog-pg-collation"><structname>pg_collation</structname></link></entry>
<entry>collations (locale information)</entry>
</row>
<row>
<entry><link linkend="catalog-pg-conversion"><structname>pg_conversion</structname></link></entry>
<entry>encoding conversion information</entry>
@ -1113,6 +1118,16 @@
</entry>
</row>
<row>
<entry><structfield>attcollation</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-collation"><structname>pg_collation</structname></link>.oid</literal></entry>
<entry>
The defined collation of the column, zero if the column does
not have a collatable type.
</entry>
</row>
<row>
<entry><structfield>attacl</structfield></entry>
<entry><type>aclitem[]</type></entry>
@ -2050,6 +2065,76 @@
</sect1>
<sect1 id="catalog-pg-collation">
<title><structname>pg_collation</structname></title>
<indexterm zone="catalog-pg-collation">
<primary>pg_collation</primary>
</indexterm>
<para>
The catalog <structname>pg_collation</structname> describes the
available collations, which are essentially mappings from an SQL
name to operating system locale categories.
See <xref linkend="locale"> for more information.
</para>
<table>
<title><structname>pg_collation</> Columns</title>
<tgroup cols="4">
<thead>
<row>
<entry>Name</entry>
<entry>Type</entry>
<entry>References</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry><structfield>collname</structfield></entry>
<entry><type>name</type></entry>
<entry></entry>
<entry>Collation name (unique per namespace and encoding)</entry>
</row>
<row>
<entry><structfield>collnamespace</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-namespace"><structname>pg_namespace</structname></link>.oid</literal></entry>
<entry>
The OID of the namespace that contains this collation
</entry>
</row>
<row>
<entry><structfield>collencoding</structfield></entry>
<entry><type>int4</type></entry>
<entry></entry>
<entry>Encoding to which the collation is applicable</entry>
</row>
<row>
<entry><structfield>collcollate</structfield></entry>
<entry><type>name</type></entry>
<entry></entry>
<entry>LC_COLLATE for this collation object</entry>
</row>
<row>
<entry><structfield>collctype</structfield></entry>
<entry><type>name</type></entry>
<entry></entry>
<entry>LC_CTYPE for this collation object</entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
<sect1 id="catalog-pg-conversion">
<title><structname>pg_conversion</structname></title>
@ -3125,6 +3210,16 @@
</entry>
</row>
<row>
<entry><structfield>indcollation</structfield></entry>
<entry><type>oidvector</type></entry>
<entry><literal><link linkend="catalog-pg-collation"><structname>pg_collation</structname></link>.oid</literal></entry>
<entry>
For each column in the index key, this contains the OID of the
collation to use for the index.
</entry>
</row>
<row>
<entry><structfield>indclass</structfield></entry>
<entry><type>oidvector</type></entry>
@ -5866,6 +5961,21 @@
</para></entry>
</row>
<row>
<entry><structfield>typcollation</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-collation"><structname>pg_collation</structname></link>.oid</literal></entry>
<entry><para>
<structfield>typcollation</structfield> specifies the collation
of the type. If a type does not support collations, this will
be zero, collation analysis at parse time is skipped, and
the use of <literal>COLLATE</literal> clauses with the type is
invalid. A base type that supports collations will have
<symbol>DEFAULT_COLLATION_OID</symbol> here. A domain can have
another collation OID, if one was defined for the domain.
</para></entry>
</row>
<row>
<entry><structfield>typdefaultbin</structfield></entry>
<entry><type>pg_node_tree</type></entry>

View File

@ -304,6 +304,170 @@ initdb --locale=sv_SE
</sect1>
<sect1 id="collation">
<title>Collation Support</title>
<para>
The collation support allows specifying the sort order and certain
other locale aspects of data per column or per operation at run
time. This alleviates the problem that the
<symbol>LC_COLLATE</symbol> and <symbol>LC_CTYPE</symbol> settings
of a database cannot be changed after its creation.
</para>
<note>
<para>
The collation support feature is currently only known to work on
Linux/glibc and Mac OS X platforms.
</para>
</note>
<sect2>
<title>Concepts</title>
<para>
Conceptually, every datum of a collatable data type has a
collation. (Collatable data types in the base system are
<type>text</type>, <type>varchar</type>, and <type>char</type>.
User-defined base types can also be marked collatable.) If the
datum is a column reference, the collation of the datum is the
defined collation of the column. If the datum is a constant, the
collation is the default collation of the data type of the
constant. The collation of more complex expressions is derived
from the input collations as described below.
</para>
<para>
The collation of a datum can also be the <quote>default</quote>
collation, which reverts to the locale settings defined for the
database. In some cases, a datum can also have no known
collation. In such cases, ordering operations and other
operations that need to know the collation will fail.
</para>
<para>
When the database system has to perform an ordering or a
comparison, it considers the collation of the input data. This
happens in two situations: an <literal>ORDER BY</literal> clause
and a function or operator call such as <literal>&lt;</literal>.
The collation to apply for the performance of the <literal>ORDER
BY</literal> clause is simply the collation of the sort key. The
collation to apply for a function or operator call is derived from
the arguments, as described below. Additionally, collations are
taken into account by functions that convert between lower and
upper case letters, that is, <function>lower</function>,
<function>upper</function>, and <function>initcap</function>.
</para>
<para>
For a function call, the collation that is derived from combining
the argument collations is both used for performing any
comparisons or ordering and for the collation of the function
result, if the result type is collatable.
</para>
<para>
The <firstterm>collation derivation</firstterm> of a datum can be
implicit or explicit. This distinction affects how collations are
combined when multiple different collations appear in an
expression. An explicit collation derivation arises when a
<literal>COLLATE</literal> clause is used; all other collation
derivations are implicit. When multiple collations need to be
combined, for example in a function call, the following rules are
used:
<orderedlist>
<listitem>
<para>
If any input item has an explicit collation derivation, then
all explicitly derived collations among the input items must be
the same, otherwise an error is raised. If an explicitly
derived collation is present, that is the result of the
collation combination.
</para>
</listitem>
<listitem>
<para>
Otherwise, all input items must have the same implicit
collation derivation or the default collation. If an
implicitly derived collation is present, that is the result of
the collation combination. Otherwise, the result is the
default collation.
</para>
</listitem>
</orderedlist>
For example, take this table definition:
<programlisting>
CREATE TABLE test1 (
a text COLLATE "x",
...
);
</programlisting>
Then in
<programlisting>
SELECT a || 'foo' FROM test1;
</programlisting>
the result collation of the <literal>||</literal> operator is
<literal>"x"</literal> because it combines an implicitly derived
collation with the default collation. But in
<programlisting>
SELECT a || ('foo' COLLATE "y") FROM test1;
</programlisting>
the result collation is <literal>"y"</literal> because the explicit
collation derivation overrides the implicit one.
</para>
</sect2>
<sect2>
<title>Managing Collations</title>
<para>
A collation is an SQL schema object that maps an SQL name to
operating system locales. In particular, it maps to a combination
of <symbol>LC_COLLATE</symbol> and <symbol>LC_CTYPE</symbol>. (As
the name would indicate, the main purpose of a collation is to set
<symbol>LC_COLLATE</symbol>, which controls the sort order. But
it is rarely necessary in practice to have an
<symbol>LC_CTYPE</symbol> setting that is different from
<symbol>LC_COLLATE</symbol>, so it is more convenient to collect
these under one concept than to create another infrastructure for
setting <symbol>LC_CTYPE</symbol> per datum.) Also, a collation
is tied to a character encoding. The same collation name may
exist for different encodings.
</para>
<para>
When a database system is initialized, <command>initdb</command>
populates the system catalog <literal>pg_collation</literal> with
collations based on all the locales it finds on the operating
system at the time. For example, the operating system might
provide a locale named <literal>de_DE.utf8</literal>.
<command>initdb</command> would then create a collation named
<literal>de_DE.utf8</literal> for encoding <literal>UTF8</literal>
that has both <symbol>LC_COLLATE</symbol> and
<symbol>LC_CTYPE</symbol> set to <literal>de_DE.utf8</literal>.
It will also create a collation with the <literal>.utf8</literal>
tag stripped off the name. So you could also use the collation
under the name <literal>de_DE</literal>, which is less cumbersome
to write and makes the name less encoding-dependent. Note that,
nevertheless, the initial set of collation names is
platform-dependent.
</para>
<para>
In case a collation is needed that has different values for
<symbol>LC_COLLATE</symbol> and <symbol>LC_CTYPE</symbol>, or a
different name is needed for a collation (for example, for
compatibility with existing applications), a new collation may be
created. But there is currently no SQL-level support for creating
or changing collations.
</para>
</sect2>
</sect1>
<sect1 id="multibyte">
<title>Character Set Support</title>

View File

@ -13059,6 +13059,12 @@ SELECT relname FROM pg_class WHERE pg_table_is_visible(oid);
</thead>
<tbody>
<row>
<entry><literal><function>pg_collation_is_visible(<parameter>collation_oid</parameter>)</function></literal>
</entry>
<entry><type>boolean</type></entry>
<entry>is collation visible in search path</entry>
</row>
<row>
<entry><literal><function>pg_conversion_is_visible(<parameter>conversion_oid</parameter>)</function></literal>
</entry>
@ -13123,6 +13129,9 @@ SELECT relname FROM pg_class WHERE pg_table_is_visible(oid);
</tgroup>
</table>
<indexterm>
<primary>pg_collation_is_visible</primary>
</indexterm>
<indexterm>
<primary>pg_conversion_is_visible</primary>
</indexterm>
@ -13256,7 +13265,7 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
<tbody>
<row>
<entry><literal><function>format_type(<parameter>type_oid</parameter>, <parameter>typemod</>)</function></literal></entry>
<entry><literal><function>format_type(<parameter>type_oid</parameter> [, <parameter>typemod</> [, <parameter>collation_oid</> ]])</function></literal></entry>
<entry><type>text</type></entry>
<entry>get SQL name of a data type</entry>
</row>
@ -13392,7 +13401,9 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
<para>
<function>format_type</function> returns the SQL name of a data type that
is identified by its type OID and possibly a type modifier. Pass NULL
for the type modifier if no specific modifier is known.
for the type modifier or omit the argument if no specific modifier is known.
If a collation is given as third argument, a <literal>COLLATE</> clause
followed by a formatted collation name is appended.
</para>
<para>

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
the proper class when making an index. The operator class determines
the basic sort ordering (which can then be modified by adding sort options
<literal>COLLATE</literal>,
<literal>ASC</>/<literal>DESC</> and/or
<literal>NULLS FIRST</>/<literal>NULLS LAST</>).
</para>
@ -1002,6 +1003,47 @@ SELECT am.amname AS index_method,
</sect1>
<sect1 id="indexes-collations">
<title>Collations and Indexes</title>
<para>
An index can only support one collation for one column or
expression. If multiple collations are of interest, multiple
indexes may be created.
</para>
<para>
Consider these statements:
<programlisting>
CREATE TABLE test1c (
id integer,
content varchar COLLATE "x"
);
CREATE INDEX test1c_content_index ON test1c (content);
</programlisting>
The created index automatically follows the collation of the
underlying column, and so a query of the form
<programlisting>
SELECT * FROM test1c WHERE content = <replaceable>constant</replaceable>;
</programlisting>
could use the index.
</para>
<para>
If in addition, a query of the form, say,
<programlisting>
SELECT * FROM test1c WHERE content &gt; <replaceable>constant</replaceable> COLLATE "y";
</programlisting>
is of interest, an additional index could be created that supports
the <literal>"y"</literal> collation, like so:
<programlisting>
CREATE INDEX test1c_content_index ON test1c (content COLLATE "y");
</programlisting>
</para>
</sect1>
<sect1 id="indexes-examine">
<title>Examining Index Usage</title>

View File

@ -22,6 +22,7 @@ PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
CREATE DOMAIN <replaceable class="parameter">name</replaceable> [ AS ] <replaceable class="parameter">data_type</replaceable>
[ COLLATE <replaceable>collation</replaceable> ]
[ DEFAULT <replaceable>expression</replaceable> ]
[ <replaceable class="PARAMETER">constraint</replaceable> [ ... ] ]
@ -83,6 +84,17 @@ CREATE DOMAIN <replaceable class="parameter">name</replaceable> [ AS ] <replacea
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable>collation</replaceable></term>
<listitem>
<para>
An optional collation for the domain. If no collation is
specified, the database default collation is used (which can
be overridden when the domain is used to define a column).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>DEFAULT <replaceable>expression</replaceable></literal></term>

View File

@ -22,7 +22,7 @@ PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ <replaceable class="parameter">name</replaceable> ] ON <replaceable class="parameter">table</replaceable> [ USING <replaceable class="parameter">method</replaceable> ]
( { <replaceable class="parameter">column</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ <replaceable class="parameter">opclass</replaceable> ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
( { <replaceable class="parameter">column</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] [ <replaceable class="parameter">opclass</replaceable> ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
[ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace</replaceable> ]
[ WHERE <replaceable class="parameter">predicate</replaceable> ]
@ -181,6 +181,20 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ <replaceable class="parameter">name</
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="parameter">collation</replaceable></term>
<listitem>
<para>
The name of the collation to use for the index. By default,
the index uses the collation declared for the column to be
indexed or the result collation of the expression to be
indexed. Indexes with nondefault collations are
available for use by queries that involve expressions using
nondefault collations.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="parameter">opclass</replaceable></term>
<listitem>

View File

@ -22,7 +22,7 @@ PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name</replaceable> ( [
{ <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ]
{ <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [ COLLATE <replaceable>collation</replaceable> ] [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ]
| <replaceable>table_constraint</replaceable>
| LIKE <replaceable>parent_table</replaceable> [ <replaceable>like_option</replaceable> ... ] }
[, ... ]
@ -244,6 +244,17 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
</listitem>
</varlistentry>
<varlistentry>
<term><literal>COLLATE <replaceable>collation</replaceable></literal></term>
<listitem>
<para>
The <literal>COLLATE</> clause assigns a nondefault collation to
the column. By default, the locale settings of the database are
used.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>INHERITS ( <replaceable>parent_table</replaceable> [, ... ] )</literal></term>
<listitem>

View File

@ -45,6 +45,7 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> (
[ , DEFAULT = <replaceable class="parameter">default</replaceable> ]
[ , ELEMENT = <replaceable class="parameter">element</replaceable> ]
[ , DELIMITER = <replaceable class="parameter">delimiter</replaceable> ]
[ , COLLATABLE = <replaceable class="parameter">collatable</replaceable> ]
)
CREATE TYPE <replaceable class="parameter">name</replaceable>
@ -352,6 +353,16 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
with the array element type, not the array type itself.
</para>
<para>
If the optional
parameter <replaceable class="parameter">collatable</replaceable>
is true, column definitions and expressions of the type may carry
collation information and allow the use of
the <literal>COLLATE</literal> clause. It is up to the
implementations of the functions operating on the type to actually
make use of the collation information; this does not happen
automatically merely by marking the type collatable.
</para>
</refsect2>
<refsect2>

View File

@ -236,6 +236,27 @@ gmake check LANG=C MULTIBYTE=EUC_JP
existing installation.
</para>
</sect2>
<sect2>
<title>Extra tests</title>
<para>
The regression test suite contains a few test files that are not
run by default, because they might be platform-dependent or take a
very long time to run. You can run these or other extra test
files by setting the variable <envar>EXTRA_TESTS</envar>. For
example, to run the <literal>numeric_big</literal> test:
<screen>
gmake check EXTRA_TESTS=numeric_big
</screen>
To run the collation tests:
<screen>
gmake check EXTRA_TESTS=collate.linux.utf8 LANG=en_US.utf8
</screen>
This test works only on Linux/glibc platforms and when run in a
UTF-8 locale.
</para>
</sect2>
</sect1>
<sect1 id="regress-evaluation">

View File

@ -1899,6 +1899,54 @@ CAST ( <replaceable>expression</replaceable> AS <replaceable>type</replaceable>
</note>
</sect2>
<sect2 id="sql-syntax-collate-clause">
<title>COLLATE Clause</title>
<indexterm>
<primary>COLLATE</primary>
</indexterm>
<para>
The <literal>COLLATE</literal> clause overrides the collation of
an expression. It is appended to the expression it applies to:
<synopsis>
<replaceable>expr</replaceable> COLLATE <replaceable>collation</replaceable>
</synopsis>
where <replaceable>collation</replaceable> is a possibly
schema-qualified identifier. The <literal>COLLATE</literal>
clause binds tighter than operators; parentheses can be used when
necessary.
</para>
<para>
If no collation is explicitly specified, the database system
either derives a collation from the columns involved in the
expression, or it defaults to the default collation of the
database if no column is involved in the expression.
</para>
<para>
The two typical uses of the <literal>COLLATE</literal> clause are
overriding the sort order in an <literal>ORDER BY</> clause, for
example:
<programlisting>
SELECT a, b, c FROM tbl WHERE ... ORDER BY a COLLATE "C";
</programlisting>
and overriding the collation of a function or operator call that
has locale-sensitive results, for example:
<programlisting>
SELECT * FROM tbl WHERE a > 'foo' COLLATE "C";
</programlisting>
In the latter case it doesn't matter which argument of the
operator of function call the <literal>COLLATE</> clause is
attached to, because the collation that is applied by the operator
or function is derived from all arguments, and
the <literal>COLLATE</> clause will override the collations of all
other arguments. Attaching nonmatching <literal>COLLATE</>
clauses to more than one argument, however, is an error.
</para>
</sect2>
<sect2 id="sql-syntax-scalar-subqueries">
<title>Scalar Subqueries</title>

View File

@ -103,3 +103,16 @@ ScanKeyEntryInitializeWithInfo(ScanKey entry,
entry->sk_argument = argument;
fmgr_info_copy(&entry->sk_func, finfo, CurrentMemoryContext);
}
/*
* ScanKeyEntryInitializeCollation
*
* Initialize the collation of a scan key. This is just a notational
* convenience and small abstraction.
*/
void
ScanKeyEntryInitializeCollation(ScanKey entry,
Oid collation)
{
entry->sk_func.fn_collation = collation;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -39,7 +39,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_ts_parser.h pg_ts_template.h \
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
pg_foreign_table.h \
pg_default_acl.h pg_seclabel.h \
pg_default_acl.h pg_seclabel.h pg_collation.h \
toasting.h indexing.h \
)

View File

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

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_attislocal - 1] = BoolGetDatum(new_attribute->attislocal);
values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount);
values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(new_attribute->attcollation);
/* start out with empty permissions and empty options */
nulls[Anum_pg_attribute_attacl - 1] = true;
@ -859,7 +860,8 @@ AddNewRelationType(const char *typeName,
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
false); /* Type NOT NULL */
false, /* Type NOT NULL */
InvalidOid); /* typcollation */
}
/* --------------------------------
@ -1120,7 +1122,8 @@ heap_create_with_catalog(const char *relname,
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
false); /* Type NOT NULL */
false, /* Type NOT NULL */
InvalidOid); /* typcollation */
pfree(relarrayname);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

@ -613,7 +613,8 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
tupdesc = BuildDescFromLists(rte->eref->colnames,
rte->funccoltypes,
rte->funccoltypmods);
rte->funccoltypmods,
rte->funccolcollations);
CheckAttributeNamesTypes(tupdesc, RELKIND_COMPOSITE_TYPE, false);
}
@ -1935,6 +1936,7 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle,
bool resolveUnknown)
{
Oid restype = exprType((Node *) tle->expr);
Oid rescollation = exprCollation((Node *) tle->expr);
Oid sortop;
Oid eqop;
bool hashable;
@ -2018,6 +2020,12 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle,
break;
}
if (type_is_collatable(restype) && !OidIsValid(rescollation))
ereport(ERROR,
(errcode(ERRCODE_INDETERMINATE_COLLATION),
errmsg("no collation was derived for the sort expression"),
errhint("Use the COLLATE clause to set the collation explicitly.")));
cancel_parser_errposition_callback(&pcbstate);
/* avoid making duplicate sortlist entries */

View File

@ -16,6 +16,7 @@
#include "catalog/pg_cast.h"
#include "catalog/pg_class.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
@ -216,6 +217,7 @@ coerce_type(ParseState *pstate, Node *node,
newcon->consttype = baseTypeId;
newcon->consttypmod = inputTypeMod;
newcon->constcollid = get_typcollation(newcon->consttype);
newcon->constlen = typeLen(targetType);
newcon->constbyval = typeByVal(targetType);
newcon->constisnull = con->constisnull;
@ -277,6 +279,14 @@ coerce_type(ParseState *pstate, Node *node,
if (result)
return result;
}
if (IsA(node, CollateClause))
{
CollateClause *cc = (CollateClause *) node;
cc->arg = (Expr *) coerce_type(pstate, (Node *) cc->arg, inputTypeId, targetTypeId, targetTypeMod,
ccontext, cformat, location);
return (Node *) cc;
}
pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext,
&funcId);
if (pathtype != COERCION_PATH_NONE)
@ -718,6 +728,7 @@ build_coercion_expression(Node *node,
FuncExpr *fexpr;
List *args;
Const *cons;
Oid collation;
Assert(OidIsValid(funcId));
@ -749,7 +760,9 @@ build_coercion_expression(Node *node,
args = lappend(args, cons);
}
fexpr = makeFuncExpr(funcId, targetTypeId, args, cformat);
collation = coercion_expression_result_collation(targetTypeId, node);
fexpr = makeFuncExpr(funcId, targetTypeId, args, collation, cformat);
fexpr->location = location;
return (Node *) fexpr;
}
@ -2081,3 +2094,120 @@ typeIsOfTypedTable(Oid reltypeId, Oid reloftypeId)
return result;
}
/*
* select_common_collation() -- determine one collation to apply for
* an expression node, for evaluating the expression itself or to
* label the result of the expression node.
*
* none_ok means that it is permitted to return "no" collation. It is
* then not possible to sort the result value of whatever expression
* is applying this. none_ok = true reflects the rules of SQL
* standard clause "Result of data type combinations", none_ok = false
* reflects the rules of clause "Collation determination" (in some
* cases invoked via "Grouping operations").
*/
Oid
select_common_collation(ParseState *pstate, List *exprs, bool none_ok)
{
ListCell *lc;
/*
* Check if there are any explicit collation derivations. If so,
* they must all be the same.
*/
foreach(lc, exprs)
{
Node *pexpr = (Node *) lfirst(lc);
Oid pcoll = exprCollation(pexpr);
bool pexplicit = IsA(pexpr, CollateClause);
if (pcoll && pexplicit)
{
ListCell *lc2;
for_each_cell(lc2, lnext(lc))
{
Node *nexpr = (Node *) lfirst(lc2);
Oid ncoll = exprCollation(nexpr);
bool nexplicit = IsA(nexpr, CollateClause);
if (!ncoll || !nexplicit)
continue;
if (ncoll != pcoll)
ereport(ERROR,
(errcode(ERRCODE_COLLATION_MISMATCH),
errmsg("collation mismatch between explicit collations \"%s\" and \"%s\"",
get_collation_name(pcoll),
get_collation_name(ncoll)),
parser_errposition(pstate, exprLocation(nexpr))));
}
return pcoll;
}
}
/*
* Check if there are any implicit collation derivations.
*/
foreach(lc, exprs)
{
Node *pexpr = (Node *) lfirst(lc);
Oid pcoll = exprCollation(pexpr);
if (pcoll && pcoll != DEFAULT_COLLATION_OID)
{
ListCell *lc2;
for_each_cell(lc2, lnext(lc))
{
Node *nexpr = (Node *) lfirst(lc2);
Oid ncoll = exprCollation(nexpr);
if (!ncoll || ncoll == DEFAULT_COLLATION_OID)
continue;
if (ncoll != pcoll)
{
if (none_ok)
return InvalidOid;
ereport(ERROR,
(errcode(ERRCODE_COLLATION_MISMATCH),
errmsg("collation mismatch between implicit collations \"%s\" and \"%s\"",
get_collation_name(pcoll),
get_collation_name(ncoll)),
errhint("You can override the collation by applying the COLLATE clause to one or both expressions."),
parser_errposition(pstate, exprLocation(nexpr))));
}
}
return pcoll;
}
}
foreach(lc, exprs)
{
Node *pexpr = (Node *) lfirst(lc);
Oid pcoll = exprCollation(pexpr);
if (pcoll == DEFAULT_COLLATION_OID)
{
ListCell *lc2;
for_each_cell(lc2, lnext(lc))
{
Node *nexpr = (Node *) lfirst(lc2);
Oid ncoll = exprCollation(nexpr);
if (ncoll != pcoll)
break;
}
return pcoll;
}
}
/*
* Else use default
*/
return InvalidOid;
}

View File

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

View File

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

View File

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

View File

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

View File

@ -782,6 +782,7 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
List *args;
Oid rettype;
OpExpr *result;
Oid opcollid;
/* Select the operator */
if (rtree == NULL)
@ -861,6 +862,12 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
/* perform the necessary typecasting of arguments */
make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
/* XXX: If we knew which functions required collation information,
* we could selectively set the last argument to true here. */
opcollid = select_common_collation(pstate, args, false);
if (!OidIsValid(opcollid))
opcollid = get_typcollation(rettype);
/* and build the expression node */
result = makeNode(OpExpr);
result->opno = oprid(tup);
@ -868,6 +875,7 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
result->opresulttype = rettype;
result->opretset = get_func_retset(opform->oprcode);
result->args = args;
result->collid = opcollid;
result->location = location;
ReleaseSysCache(tup);
@ -896,6 +904,7 @@ make_scalar_array_op(ParseState *pstate, List *opname,
List *args;
Oid rettype;
ScalarArrayOpExpr *result;
Oid opcollid;
ltypeId = exprType(ltree);
atypeId = exprType(rtree);
@ -990,12 +999,19 @@ make_scalar_array_op(ParseState *pstate, List *opname,
/* perform the necessary typecasting of arguments */
make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
/* XXX: If we knew which functions required collation information,
* we could selectively set the last argument to true here. */
opcollid = select_common_collation(pstate, args, false);
if (!OidIsValid(opcollid))
opcollid = get_typcollation(rettype);
/* and build the expression node */
result = makeNode(ScalarArrayOpExpr);
result->opno = oprid(tup);
result->opfuncid = opform->oprcode;
result->useOr = useOr;
result->args = args;
result->collid = opcollid;
result->location = location;
ReleaseSysCache(tup);

View File

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

View File

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

View File

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

View File

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

View File

@ -627,7 +627,8 @@ transformInhRelation(CreateStmtContext *cxt, InhRelation *inhRelation)
def = makeNode(ColumnDef);
def->colname = pstrdup(attributeName);
def->typeName = makeTypeNameFromOid(attribute->atttypid,
attribute->atttypmod);
attribute->atttypmod,
attribute->attcollation);
def->inhcount = 0;
def->is_local = true;
def->is_not_null = attribute->attnotnull;
@ -821,7 +822,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
AssertArg(ofTypename);
tuple = typenameType(NULL, ofTypename, NULL);
tuple = typenameType(NULL, ofTypename, NULL, NULL);
typ = (Form_pg_type) GETSTRUCT(tuple);
ofTypeId = HeapTupleGetOid(tuple);
ofTypename->typeOid = ofTypeId; /* cached for later */
@ -842,7 +843,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
continue;
n->colname = pstrdup(NameStr(attr->attname));
n->typeName = makeTypeNameFromOid(attr->atttypid, attr->atttypmod);
n->typeName = makeTypeNameFromOid(attr->atttypid, attr->atttypmod, attr->attcollation);
n->constraints = NULL;
n->is_local = true;
n->is_from_type = true;
@ -2446,7 +2447,7 @@ transformColumnType(CreateStmtContext *cxt, ColumnDef *column)
/*
* All we really need to do here is verify that the type is valid.
*/
Type ctype = typenameType(cxt->pstate, column->typeName, NULL);
Type ctype = typenameType(cxt->pstate, column->typeName, NULL, NULL);
ReleaseSysCache(ctype);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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