])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
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
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>
#include "fmgr.h"
#include "access/skey.h"
+#include "catalog/pg_collation.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
#include "utils/cash.h"
int32 res, \
cmp; \
\
- cmp = DatumGetInt32(DirectFunctionCall2( \
+ cmp = DatumGetInt32(DirectFunctionCall2WithCollation( \
TypeInfo_##type.typecmp, \
+ DEFAULT_COLLATION_OID, \
(data->strategy == BTLessStrategyNumber || \
data->strategy == BTLessEqualStrategyNumber) \
? data->datum : a, \
*/
#include "btree_gist.h"
#include "btree_utils_var.h"
+#include "catalog/pg_collation.h"
#include "utils/builtins.h"
/*
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 =
#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"
if (tinfo->eml > 1)
{
- out = (varstr_cmp(q, nlen, n, nlen) == 0);
+ out = (varstr_cmp(q, nlen, n, nlen, DEFAULT_COLLATION_OID) == 0);
}
else
{
* ====================
*/
-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);
* 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);
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);
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);
/* 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
/* 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
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);
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);
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);
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);
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);
}
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);
}
STORAGE = extended,
-- make it a non-preferred member of string type category
CATEGORY = 'S',
- PREFERRED = false
+ PREFERRED = false,
+ COLLATABLE = true
);
--
#include <ctype.h>
+#include "catalog/pg_collation.h"
#include "utils/array.h"
#include "utils/formatting.h"
#include "ltree.h"
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);
<entry>check constraints, unique constraints, primary key constraints, foreign key constraints</entry>
</row>
+ <row>
+ <entry><link linkend="catalog-pg-collation"><structname>pg_collation</structname></link></entry>
+ <entry>collations (locale information)</entry>
+ </row>
+
<row>
<entry><link linkend="catalog-pg-conversion"><structname>pg_conversion</structname></link></entry>
<entry>encoding conversion information</entry>
</entry>
</row>
+ <row>
+ <entry><structfield>attcollation</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-collation"><structname>pg_collation</structname></link>.oid</literal></entry>
+ <entry>
+ The defined collation of the column, zero if the column does
+ not have a collatable type.
+ </entry>
+ </row>
+
<row>
<entry><structfield>attacl</structfield></entry>
<entry><type>aclitem[]</type></entry>
</sect1>
+ <sect1 id="catalog-pg-collation">
+ <title><structname>pg_collation</structname></title>
+
+ <indexterm zone="catalog-pg-collation">
+ <primary>pg_collation</primary>
+ </indexterm>
+
+ <para>
+ The catalog <structname>pg_collation</structname> describes the
+ available collations, which are essentially mappings from an SQL
+ name to operating system locale categories.
+ See <xref linkend="locale"> for more information.
+ </para>
+
+ <table>
+ <title><structname>pg_collation</> Columns</title>
+
+ <tgroup cols="4">
+ <thead>
+ <row>
+ <entry>Name</entry>
+ <entry>Type</entry>
+ <entry>References</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><structfield>collname</structfield></entry>
+ <entry><type>name</type></entry>
+ <entry></entry>
+ <entry>Collation name (unique per namespace and encoding)</entry>
+ </row>
+
+ <row>
+ <entry><structfield>collnamespace</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-namespace"><structname>pg_namespace</structname></link>.oid</literal></entry>
+ <entry>
+ The OID of the namespace that contains this collation
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>collencoding</structfield></entry>
+ <entry><type>int4</type></entry>
+ <entry></entry>
+ <entry>Encoding to which the collation is applicable</entry>
+ </row>
+
+ <row>
+ <entry><structfield>collcollate</structfield></entry>
+ <entry><type>name</type></entry>
+ <entry></entry>
+ <entry>LC_COLLATE for this collation object</entry>
+ </row>
+
+ <row>
+ <entry><structfield>collctype</structfield></entry>
+ <entry><type>name</type></entry>
+ <entry></entry>
+ <entry>LC_CTYPE for this collation object</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect1>
+
<sect1 id="catalog-pg-conversion">
<title><structname>pg_conversion</structname></title>
</entry>
</row>
+ <row>
+ <entry><structfield>indcollation</structfield></entry>
+ <entry><type>oidvector</type></entry>
+ <entry><literal><link linkend="catalog-pg-collation"><structname>pg_collation</structname></link>.oid</literal></entry>
+ <entry>
+ For each column in the index key, this contains the OID of the
+ collation to use for the index.
+ </entry>
+ </row>
+
<row>
<entry><structfield>indclass</structfield></entry>
<entry><type>oidvector</type></entry>
</para></entry>
</row>
+ <row>
+ <entry><structfield>typcollation</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-collation"><structname>pg_collation</structname></link>.oid</literal></entry>
+ <entry><para>
+ <structfield>typcollation</structfield> specifies the collation
+ of the type. If a type does not support collations, this will
+ be zero, collation analysis at parse time is skipped, and
+ the use of <literal>COLLATE</literal> clauses with the type is
+ invalid. A base type that supports collations will have
+ <symbol>DEFAULT_COLLATION_OID</symbol> here. A domain can have
+ another collation OID, if one was defined for the domain.
+ </para></entry>
+ </row>
+
<row>
<entry><structfield>typdefaultbin</structfield></entry>
<entry><type>pg_node_tree</type></entry>
</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><</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>
</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>
</tgroup>
</table>
+ <indexterm>
+ <primary>pg_collation_is_visible</primary>
+ </indexterm>
<indexterm>
<primary>pg_conversion_is_visible</primary>
</indexterm>
<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>
<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>
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>
</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 > <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>
<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> [ ... ] ]
</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>
<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> ]
</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>
<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> ... ] }
[, ... ]
</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>
[ , 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>
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>
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">
</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>
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;
+}
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
char *attname;
Oid atttypid;
int32 atttypmod;
+ Oid attcollation;
int attdim;
/*
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)
TupleDescInitEntry(desc, attnum, attname,
atttypid, atttypmod, attdim);
+ TupleDescInitEntryCollation(desc, attnum, attcollation);
/* Override TupleDescInitEntry's settings as requested */
if (entry->storage)
* 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
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;
#include "storage/freespace.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
+#include "utils/lsyscache.h"
/*
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);
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;
cur->sk_subtype,
procinfo,
cur->sk_argument);
+ ScanKeyEntryInitializeCollation(scankeys + i,
+ cur->sk_func.fn_collation);
}
else
{
cur->sk_subtype,
cmp_proc,
cur->sk_argument);
+ ScanKeyEntryInitializeCollation(scankeys + i,
+ cur->sk_func.fn_collation);
}
}
}
#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"
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}
};
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;
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)
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 \
)
$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.
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;
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
- false); /* Type NOT NULL */
+ false, /* Type NOT NULL */
+ InvalidOid); /* typcollation */
}
/* --------------------------------
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
- false); /* Type NOT NULL */
+ false, /* Type NOT NULL */
+ InvalidOid); /* typcollation */
pfree(relarrayname);
}
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,
IndexInfo *indexInfo,
List *indexColNames,
Oid accessMethodObjectId,
+ Oid *collationObjectId,
Oid *classObjectId)
{
int numatts = indexInfo->ii_NumIndexAttrs;
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
UpdateIndexRelation(Oid indexoid,
Oid heapoid,
IndexInfo *indexInfo,
+ Oid *collationOids,
Oid *classOids,
int16 *coloptions,
bool primary,
bool isvalid)
{
int2vector *indkey;
+ oidvector *indcollation;
oidvector *indclass;
int2vector *indoption;
Datum exprsDatum;
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);
/* 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;
List *indexColNames,
Oid accessMethodObjectId,
Oid tableSpaceId,
+ Oid *collationObjectId,
Oid *classObjectId,
int16 *coloptions,
Datum reloptions,
indexInfo,
indexColNames,
accessMethodObjectId,
+ collationObjectId,
classObjectId);
/*
* ----------------
*/
UpdateIndexRelation(indexRelationId, heapRelationId, indexInfo,
- classObjectId, coloptions, isprimary, is_exclusion,
+ collationObjectId, classObjectId, coloptions, isprimary, is_exclusion,
!deferrable,
!concurrent);
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;
#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"
#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"
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);
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.
}
+/*
+ * 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
*/
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)
{
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 */
char storage,
int32 typeMod,
int32 typNDims, /* Array dimensions for baseType */
- bool typeNotNull)
+ bool typeNotNull,
+ Oid typeCollation)
{
Relation pg_type_desc;
Oid typeObjectId;
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
-- 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';
char toast_relname[NAMEDATALEN];
char toast_idxname[NAMEDATALEN];
IndexInfo *indexInfo;
+ Oid collationObjectId[2];
Oid classObjectId[2];
int16 coloptions[2];
ObjectAddress baseobject,
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;
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);
{
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));
track_cnt = 0;
fmgr_info(mystats->eqfunc, &f_cmpeq);
+ fmgr_info_collation(stats->attrcollation, &f_cmpeq);
for (i = 0; i < samplerows; i++)
{
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++)
*
* 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 ||
Oid rettype;
Type typtup;
- typtup = LookupTypeName(NULL, returnType, NULL);
+ typtup = LookupTypeName(NULL, returnType, NULL, NULL);
if (typtup)
{
Oid toid;
Type typtup;
- typtup = LookupTypeName(NULL, t, NULL);
+ typtup = LookupTypeName(NULL, t, NULL, NULL);
if (typtup)
{
if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined)
/* non-export function prototypes */
static void CheckPredicate(Expr *predicate);
static void ComputeIndexAttrs(IndexInfo *indexInfo,
+ Oid *collationOidP,
Oid *classOidP,
int16 *colOptionP,
List *attList,
bool quiet,
bool concurrent)
{
+ Oid *collationObjectId;
Oid *classObjectId;
Oid accessMethodId;
Oid relationId;
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);
indexRelationId =
index_create(rel, indexRelationName, indexRelationId,
indexInfo, indexColNames,
- accessMethodId, tablespaceId, classObjectId,
+ accessMethodId, tablespaceId, collationObjectId, classObjectId,
coloptions, reloptions, primary,
isconstraint, deferrable, initdeferred,
allowSystemTableMods,
*/
static void
ComputeIndexAttrs(IndexInfo *indexInfo,
+ Oid *collationOidP,
Oid *classOidP,
int16 *colOptionP,
List *attList, /* list of IndexElem's */
{
IndexElem *attribute = (IndexElem *) lfirst(lc);
Oid atttype;
+ Oid attcollation;
/*
* Process the column-or-expression to be indexed.
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) &&
indexInfo->ii_KeyAttrNumbers[attn] = var->varattno;
atttype = get_atttype(relId, var->varattno);
+ attcollation = var->varcollid;
}
else
{
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
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.
*/
#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"
Anum_pg_seclabel_provider,
BTEqualStrategyNumber, F_TEXTEQ,
CStringGetTextDatum(provider));
+ ScanKeyEntryInitializeCollation(&keys[3], DEFAULT_COLLATION_OID);
pg_seclabel = heap_open(SecLabelRelationId, AccessShareLock);
Anum_pg_seclabel_provider,
BTEqualStrategyNumber, F_TEXTEQ,
CStringGetTextDatum(provider));
+ ScanKeyEntryInitializeCollation(&keys[3], DEFAULT_COLLATION_OID);
pg_seclabel = heap_open(SecLabelRelationId, RowExclusiveLock);
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;
{
Oid defTypeId;
int32 deftypmod;
+ Oid defCollId;
/*
* Yes, try to merge the two column definitions. They must
(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,
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)
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;
newTypeId;
int32 deftypmod,
newtypmod;
+ Oid defcollid,
+ newcollid;
/*
* Yes, try to merge the two column definitions. They must
(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),
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)
HeapTuple typeTuple;
Oid typeOid;
int32 typmod;
+ Oid collOid;
Form_pg_type tform;
Expr *defval;
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)
MaxHeapAttributeNumber)));
}
- typeTuple = typenameType(NULL, colDef->typeName, &typmod);
+ typeTuple = typenameType(NULL, colDef->typeName, &typmod, &collOid);
tform = (Form_pg_type) GETSTRUCT(typeTuple);
typeOid = HeapTupleGetOid(typeTuple);
attribute.attisdropped = false;
attribute.attislocal = colDef->is_local;
attribute.attinhcount = colDef->inhcount;
+ attribute.attcollation = collOid;
/* attribute.attacl is handled by InsertPgAttributeTuple */
ReleaseSysCache(typeTuple);
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;
AttrNumber attnum;
Oid targettype;
int32 targettypmod;
+ Oid targetcollid;
Node *transform;
NewColumnValue *newval;
ParseState *pstate = make_parsestate(NULL);
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);
else
{
transform = (Node *) makeVar(1, attnum,
- attTup->atttypid, attTup->atttypmod,
+ attTup->atttypid, attTup->atttypmod, attTup->attcollation,
0);
}
Form_pg_type tform;
Oid targettype;
int32 targettypmod;
+ Oid targetcollid;
Node *defaultexpr;
Relation attrelation;
Relation depRel;
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);
*/
attTup->atttypid = targettype;
attTup->atttypmod = targettypmod;
+ attTup->attcollation = targetcollid;
attTup->attndims = list_length(typeName->arrayBounds);
attTup->attlen = tform->typlen;
attTup->attbyval = tform->typbyval;
#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"
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;
DefElem *byValueEl = NULL;
DefElem *alignmentEl = NULL;
DefElem *storageEl = NULL;
+ DefElem *collatableEl = NULL;
Oid inputOid;
Oid outputOid;
Oid receiveOid = InvalidOid;
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 */
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;
(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
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.
'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);
}
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)
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,
/*
* 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);
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
'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);
'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);
}
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),
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;
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);
/* 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),
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
*
econtext->ecxt_per_query_memory);
/* Initialize additional info */
- astate->elemfunc.fn_expr = (Node *) acoerce;
+ fmgr_info_expr((Node *) acoerce, &(astate->elemfunc));
}
/*
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;
List *outlist;
ListCell *l;
ListCell *l2;
+ ListCell *l3;
int i;
rstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRowCompare;
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;
* does this code.
*/
fmgr_info(proc, &(rstate->funcs[i]));
+ fmgr_info_collation(collid, &(rstate->funcs[i]));
i++;
}
state = (ExprState *) rstate;
* code.
*/
fmgr_info(typentry->cmp_proc, &(mstate->cfunc));
+ fmgr_info_collation(minmaxexpr->collid, &(mstate->cfunc));
state = (ExprState *) mstate;
}
break;
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;
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;
/* deconstructed sorting information (arrays of length numSortCols) */
AttrNumber *sortColIdx;
Oid *sortOperators;
+ Oid *sortCollations;
bool *sortNullsFirst;
/*
(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);
}
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,
(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));
peraggstate->sortColIdx[i] = tle->resno;
peraggstate->sortOperators[i] = sortcl->sortop;
+ peraggstate->sortCollations[i] = exprCollation((Node *) tle->expr);
peraggstate->sortNullsFirst[i] = sortcl->nulls_first;
i++;
}
#include "executor/nodeFunctionscan.h"
#include "funcapi.h"
+#include "nodes/nodeFuncs.h"
#include "utils/builtins.h"
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
{
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 */
op_righttype, /* strategy subtype */
opfuncid, /* reg proc to use */
scanvalue); /* constant */
+ ScanKeyEntryInitializeCollation(this_scan_key,
+ ((OpExpr *) clause)->collid);
}
else if (IsA(clause, RowCompareExpr))
{
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;
op_righttype,
BTORDER_PROC);
+ collation = lfirst_oid(collids_cell);
+ collids_cell = lnext(collids_cell);
+
/*
* rightop is the constant or variable comparison value
*/
op_righttype, /* strategy subtype */
opfuncid, /* reg proc to use */
scanvalue); /* constant */
+ ScanKeyEntryInitializeCollation(this_sub_key,
+ collation);
n_sub_key++;
}
op_righttype, /* strategy subtype */
opfuncid, /* reg proc to use */
(Datum) 0); /* constant */
+ ScanKeyEntryInitializeCollation(this_scan_key,
+ saop->collid);
}
else if (IsA(clause, NullTest))
{
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;
static MergeJoinClause
MJExamineQuals(List *mergeclauses,
Oid *mergefamilies,
+ Oid *mergecollations,
int *mergestrategies,
bool *mergenullsfirst,
PlanState *parent)
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;
/* 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)
mergestate->mj_NumClauses = list_length(node->mergeclauses);
mergestate->mj_Clauses = MJExamineQuals(node->mergeclauses,
node->mergeFamilies,
+ node->mergeCollations,
node->mergeStrategies,
node->mergeNullsFirst,
(PlanState *) mergestate);
plannode->numCols,
plannode->sortColIdx,
plannode->sortOperators,
+ plannode->collations,
plannode->nullsFirst,
work_mem,
node->randomAccess);
/* 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,
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);
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,
COPY_SCALAR_FIELD(numCols);
COPY_POINTER_FIELD(sortColIdx, from->numCols * sizeof(AttrNumber));
COPY_POINTER_FIELD(sortOperators, fr