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:
parent
1703f0e8da
commit
414c5a2ea6
@ -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
108
configure
vendored
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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, \
|
||||
|
@ -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 =
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
);
|
||||
|
||||
--
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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><</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>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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 > <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>
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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">
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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 \
|
||||
)
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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';
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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++)
|
||||
|
@ -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 ||
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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++;
|
||||
}
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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))
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -86,6 +86,7 @@ ExecSort(SortState *node)
|
||||
plannode->numCols,
|
||||
plannode->sortColIdx,
|
||||
plannode->sortOperators,
|
||||
plannode->collations,
|
||||
plannode->nullsFirst,
|
||||
work_mem,
|
||||
node->randomAccess);
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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))
|
||||
|
@ -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;
|
||||
|
@ -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), <proc);
|
||||
fmgr_info_collation(collation, <proc);
|
||||
greaterstr = make_greater_string(prefix_const, <proc);
|
||||
if (greaterstr)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
/*
|
||||
|
@ -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 *
|
||||
|
@ -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.
|
||||
|
@ -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))
|
||||
{
|
||||
|
@ -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 */
|
||||
|
@ -445,6 +445,7 @@ inline_set_returning_functions(PlannerInfo *root)
|
||||
rte->funcexpr = NULL;
|
||||
rte->funccoltypes = NIL;
|
||||
rte->funccoltypmods = NIL;
|
||||
rte->funccolcollations = NIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
;
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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";
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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))
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 */
|
||||
}
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
94
src/backend/utils/cache/lsyscache.c
vendored
94
src/backend/utils/cache/lsyscache.c
vendored
@ -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
Loading…
x
Reference in New Issue
Block a user