2011-02-12 14:54:13 +01:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* collationcmds.c
|
2011-03-11 19:20:11 +01:00
|
|
|
* collation-related commands support code
|
2011-02-12 14:54:13 +01:00
|
|
|
*
|
2017-01-03 19:48:53 +01:00
|
|
|
* Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
|
2011-02-12 14:54:13 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
|
|
|
* src/backend/commands/collationcmds.c
|
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
2017-03-23 20:25:34 +01:00
|
|
|
#include "access/heapam.h"
|
2012-08-30 22:15:44 +02:00
|
|
|
#include "access/htup_details.h"
|
2011-03-04 21:14:37 +01:00
|
|
|
#include "access/xact.h"
|
2011-02-12 14:54:13 +01:00
|
|
|
#include "catalog/dependency.h"
|
|
|
|
#include "catalog/indexing.h"
|
|
|
|
#include "catalog/namespace.h"
|
2017-03-23 20:25:34 +01:00
|
|
|
#include "catalog/objectaccess.h"
|
2011-02-12 14:54:13 +01:00
|
|
|
#include "catalog/pg_collation.h"
|
|
|
|
#include "catalog/pg_collation_fn.h"
|
|
|
|
#include "commands/alter.h"
|
|
|
|
#include "commands/collationcmds.h"
|
2017-03-23 20:25:34 +01:00
|
|
|
#include "commands/comment.h"
|
2011-02-12 14:54:13 +01:00
|
|
|
#include "commands/dbcommands.h"
|
|
|
|
#include "commands/defrem.h"
|
|
|
|
#include "mb/pg_wchar.h"
|
|
|
|
#include "miscadmin.h"
|
|
|
|
#include "utils/builtins.h"
|
|
|
|
#include "utils/lsyscache.h"
|
2011-03-04 21:14:37 +01:00
|
|
|
#include "utils/pg_locale.h"
|
2011-03-26 04:10:07 +01:00
|
|
|
#include "utils/rel.h"
|
2011-02-12 14:54:13 +01:00
|
|
|
#include "utils/syscache.h"
|
|
|
|
|
2017-03-23 20:25:34 +01:00
|
|
|
|
2011-02-12 14:54:13 +01:00
|
|
|
/*
|
|
|
|
* CREATE COLLATION
|
|
|
|
*/
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
ObjectAddress
|
2017-02-09 04:51:09 +01:00
|
|
|
DefineCollation(ParseState *pstate, List *names, List *parameters, bool if_not_exists)
|
2011-02-12 14:54:13 +01:00
|
|
|
{
|
|
|
|
char *collName;
|
|
|
|
Oid collNamespace;
|
|
|
|
AclResult aclresult;
|
|
|
|
ListCell *pl;
|
2011-04-10 17:42:00 +02:00
|
|
|
DefElem *fromEl = NULL;
|
|
|
|
DefElem *localeEl = NULL;
|
|
|
|
DefElem *lccollateEl = NULL;
|
|
|
|
DefElem *lcctypeEl = NULL;
|
2017-03-23 20:25:34 +01:00
|
|
|
DefElem *providerEl = NULL;
|
|
|
|
DefElem *versionEl = NULL;
|
2011-02-12 14:54:13 +01:00
|
|
|
char *collcollate = NULL;
|
|
|
|
char *collctype = NULL;
|
2017-03-23 20:25:34 +01:00
|
|
|
char *collproviderstr = NULL;
|
|
|
|
int collencoding;
|
|
|
|
char collprovider = 0;
|
|
|
|
char *collversion = NULL;
|
2011-03-04 21:14:37 +01:00
|
|
|
Oid newoid;
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
ObjectAddress address;
|
2011-02-12 14:54:13 +01:00
|
|
|
|
|
|
|
collNamespace = QualifiedNameGetCreationNamespace(names, &collName);
|
|
|
|
|
|
|
|
aclresult = pg_namespace_aclcheck(collNamespace, GetUserId(), ACL_CREATE);
|
|
|
|
if (aclresult != ACLCHECK_OK)
|
|
|
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
|
|
|
get_namespace_name(collNamespace));
|
|
|
|
|
|
|
|
foreach(pl, parameters)
|
|
|
|
{
|
Improve castNode notation by introducing list-extraction-specific variants.
This extends the castNode() notation introduced by commit 5bcab1114 to
provide, in one step, extraction of a list cell's pointer and coercion to
a concrete node type. For example, "lfirst_node(Foo, lc)" is the same
as "castNode(Foo, lfirst(lc))". Almost half of the uses of castNode
that have appeared so far include a list extraction call, so this is
pretty widely useful, and it saves a few more keystrokes compared to the
old way.
As with the previous patch, back-patch the addition of these macros to
pg_list.h, so that the notation will be available when back-patching.
Patch by me, after an idea of Andrew Gierth's.
Discussion: https://postgr.es/m/14197.1491841216@sss.pgh.pa.us
2017-04-10 19:51:29 +02:00
|
|
|
DefElem *defel = lfirst_node(DefElem, pl);
|
2011-02-12 14:54:13 +01:00
|
|
|
DefElem **defelp;
|
|
|
|
|
|
|
|
if (pg_strcasecmp(defel->defname, "from") == 0)
|
|
|
|
defelp = &fromEl;
|
|
|
|
else if (pg_strcasecmp(defel->defname, "locale") == 0)
|
|
|
|
defelp = &localeEl;
|
|
|
|
else if (pg_strcasecmp(defel->defname, "lc_collate") == 0)
|
|
|
|
defelp = &lccollateEl;
|
|
|
|
else if (pg_strcasecmp(defel->defname, "lc_ctype") == 0)
|
|
|
|
defelp = &lcctypeEl;
|
2017-03-23 20:25:34 +01:00
|
|
|
else if (pg_strcasecmp(defel->defname, "provider") == 0)
|
|
|
|
defelp = &providerEl;
|
|
|
|
else if (pg_strcasecmp(defel->defname, "version") == 0)
|
|
|
|
defelp = &versionEl;
|
2011-02-12 14:54:13 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
|
|
errmsg("collation attribute \"%s\" not recognized",
|
2016-09-06 18:00:00 +02:00
|
|
|
defel->defname),
|
|
|
|
parser_errposition(pstate, defel->location)));
|
2011-02-12 14:54:13 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
*defelp = defel;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((localeEl && (lccollateEl || lcctypeEl))
|
|
|
|
|| (fromEl && list_length(parameters) != 1))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
|
|
errmsg("conflicting or redundant options")));
|
|
|
|
|
|
|
|
if (fromEl)
|
|
|
|
{
|
|
|
|
Oid collid;
|
|
|
|
HeapTuple tp;
|
|
|
|
|
2011-04-10 17:42:00 +02:00
|
|
|
collid = get_collation_oid(defGetQualifiedName(fromEl), false);
|
2011-02-12 14:54:13 +01:00
|
|
|
tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
|
|
|
|
if (!HeapTupleIsValid(tp))
|
|
|
|
elog(ERROR, "cache lookup failed for collation %u", collid);
|
|
|
|
|
|
|
|
collcollate = pstrdup(NameStr(((Form_pg_collation) GETSTRUCT(tp))->collcollate));
|
|
|
|
collctype = pstrdup(NameStr(((Form_pg_collation) GETSTRUCT(tp))->collctype));
|
2017-03-23 20:25:34 +01:00
|
|
|
collprovider = ((Form_pg_collation) GETSTRUCT(tp))->collprovider;
|
2011-02-12 14:54:13 +01:00
|
|
|
|
|
|
|
ReleaseSysCache(tp);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (localeEl)
|
|
|
|
{
|
|
|
|
collcollate = defGetString(localeEl);
|
|
|
|
collctype = defGetString(localeEl);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lccollateEl)
|
|
|
|
collcollate = defGetString(lccollateEl);
|
|
|
|
|
|
|
|
if (lcctypeEl)
|
|
|
|
collctype = defGetString(lcctypeEl);
|
|
|
|
|
2017-03-23 20:25:34 +01:00
|
|
|
if (providerEl)
|
|
|
|
collproviderstr = defGetString(providerEl);
|
|
|
|
|
|
|
|
if (versionEl)
|
|
|
|
collversion = defGetString(versionEl);
|
|
|
|
|
|
|
|
if (collproviderstr)
|
|
|
|
{
|
|
|
|
if (pg_strcasecmp(collproviderstr, "icu") == 0)
|
|
|
|
collprovider = COLLPROVIDER_ICU;
|
|
|
|
else if (pg_strcasecmp(collproviderstr, "libc") == 0)
|
|
|
|
collprovider = COLLPROVIDER_LIBC;
|
|
|
|
else
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
|
|
|
errmsg("unrecognized collation provider: %s",
|
|
|
|
collproviderstr)));
|
|
|
|
}
|
|
|
|
else if (!fromEl)
|
|
|
|
collprovider = COLLPROVIDER_LIBC;
|
|
|
|
|
2011-02-12 14:54:13 +01:00
|
|
|
if (!collcollate)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
2011-09-07 19:29:26 +02:00
|
|
|
errmsg("parameter \"lc_collate\" must be specified")));
|
2011-02-12 14:54:13 +01:00
|
|
|
|
|
|
|
if (!collctype)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
|
|
|
errmsg("parameter \"lc_ctype\" must be specified")));
|
|
|
|
|
2017-03-23 20:25:34 +01:00
|
|
|
if (collprovider == COLLPROVIDER_ICU)
|
|
|
|
collencoding = -1;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
collencoding = GetDatabaseEncoding();
|
|
|
|
check_encoding_locale_matches(collencoding, collcollate, collctype);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!collversion)
|
|
|
|
collversion = get_collation_actual_version(collprovider, collcollate);
|
2011-02-12 14:54:13 +01:00
|
|
|
|
2011-03-04 21:14:37 +01:00
|
|
|
newoid = CollationCreate(collName,
|
2011-03-11 19:20:11 +01:00
|
|
|
collNamespace,
|
|
|
|
GetUserId(),
|
2017-03-23 20:25:34 +01:00
|
|
|
collprovider,
|
|
|
|
collencoding,
|
2011-03-11 19:20:11 +01:00
|
|
|
collcollate,
|
2017-01-18 18:00:00 +01:00
|
|
|
collctype,
|
2017-03-23 20:25:34 +01:00
|
|
|
collversion,
|
2017-02-09 04:51:09 +01:00
|
|
|
if_not_exists);
|
2017-01-18 18:00:00 +01:00
|
|
|
|
|
|
|
if (!OidIsValid(newoid))
|
|
|
|
return InvalidObjectAddress;
|
2011-03-04 21:14:37 +01:00
|
|
|
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
ObjectAddressSet(address, CollationRelationId, newoid);
|
|
|
|
|
2011-03-04 21:14:37 +01:00
|
|
|
/* check that the locales can be loaded */
|
|
|
|
CommandCounterIncrement();
|
2011-03-22 21:55:32 +01:00
|
|
|
(void) pg_newlocale_from_collation(newoid);
|
2012-12-24 00:25:03 +01:00
|
|
|
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
return address;
|
2011-02-12 14:54:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
Rework order of checks in ALTER / SET SCHEMA
When attempting to move an object into the schema in which it already
was, for most objects classes we were correctly complaining about
exactly that ("object is already in schema"); but for some other object
classes, such as functions, we were instead complaining of a name
collision ("object already exists in schema"). The latter is wrong and
misleading, per complaint from Robert Haas in
CA+TgmoZ0+gNf7RDKRc3u5rHXffP=QjqPZKGxb4BsPz65k7qnHQ@mail.gmail.com
To fix, refactor the way these checks are done. As a bonus, the
resulting code is smaller and can also share some code with Rename
cases.
While at it, remove use of getObjectDescriptionOids() in error messages.
These are normally disallowed because of translatability considerations,
but this one had slipped through since 9.1. (Not sure that this is
worth backpatching, though, as it would create some untranslated
messages in back branches.)
This is loosely based on a patch by KaiGai Kohei, heavily reworked by
me.
2013-01-15 17:23:43 +01:00
|
|
|
* Subroutine for ALTER COLLATION SET SCHEMA and RENAME
|
|
|
|
*
|
|
|
|
* Is there a collation with the same name of the given collation already in
|
|
|
|
* the given namespace? If so, raise an appropriate error message.
|
2011-02-12 14:54:13 +01:00
|
|
|
*/
|
Rework order of checks in ALTER / SET SCHEMA
When attempting to move an object into the schema in which it already
was, for most objects classes we were correctly complaining about
exactly that ("object is already in schema"); but for some other object
classes, such as functions, we were instead complaining of a name
collision ("object already exists in schema"). The latter is wrong and
misleading, per complaint from Robert Haas in
CA+TgmoZ0+gNf7RDKRc3u5rHXffP=QjqPZKGxb4BsPz65k7qnHQ@mail.gmail.com
To fix, refactor the way these checks are done. As a bonus, the
resulting code is smaller and can also share some code with Rename
cases.
While at it, remove use of getObjectDescriptionOids() in error messages.
These are normally disallowed because of translatability considerations,
but this one had slipped through since 9.1. (Not sure that this is
worth backpatching, though, as it would create some untranslated
messages in back branches.)
This is loosely based on a patch by KaiGai Kohei, heavily reworked by
me.
2013-01-15 17:23:43 +01:00
|
|
|
void
|
|
|
|
IsThereCollationInNamespace(const char *collname, Oid nspOid)
|
2011-02-12 14:54:13 +01:00
|
|
|
{
|
2011-03-11 19:20:11 +01:00
|
|
|
/* make sure the name doesn't already exist in new schema */
|
|
|
|
if (SearchSysCacheExists3(COLLNAMEENCNSP,
|
Rework order of checks in ALTER / SET SCHEMA
When attempting to move an object into the schema in which it already
was, for most objects classes we were correctly complaining about
exactly that ("object is already in schema"); but for some other object
classes, such as functions, we were instead complaining of a name
collision ("object already exists in schema"). The latter is wrong and
misleading, per complaint from Robert Haas in
CA+TgmoZ0+gNf7RDKRc3u5rHXffP=QjqPZKGxb4BsPz65k7qnHQ@mail.gmail.com
To fix, refactor the way these checks are done. As a bonus, the
resulting code is smaller and can also share some code with Rename
cases.
While at it, remove use of getObjectDescriptionOids() in error messages.
These are normally disallowed because of translatability considerations,
but this one had slipped through since 9.1. (Not sure that this is
worth backpatching, though, as it would create some untranslated
messages in back branches.)
This is loosely based on a patch by KaiGai Kohei, heavily reworked by
me.
2013-01-15 17:23:43 +01:00
|
|
|
CStringGetDatum(collname),
|
2011-03-11 19:20:11 +01:00
|
|
|
Int32GetDatum(GetDatabaseEncoding()),
|
Rework order of checks in ALTER / SET SCHEMA
When attempting to move an object into the schema in which it already
was, for most objects classes we were correctly complaining about
exactly that ("object is already in schema"); but for some other object
classes, such as functions, we were instead complaining of a name
collision ("object already exists in schema"). The latter is wrong and
misleading, per complaint from Robert Haas in
CA+TgmoZ0+gNf7RDKRc3u5rHXffP=QjqPZKGxb4BsPz65k7qnHQ@mail.gmail.com
To fix, refactor the way these checks are done. As a bonus, the
resulting code is smaller and can also share some code with Rename
cases.
While at it, remove use of getObjectDescriptionOids() in error messages.
These are normally disallowed because of translatability considerations,
but this one had slipped through since 9.1. (Not sure that this is
worth backpatching, though, as it would create some untranslated
messages in back branches.)
This is loosely based on a patch by KaiGai Kohei, heavily reworked by
me.
2013-01-15 17:23:43 +01:00
|
|
|
ObjectIdGetDatum(nspOid)))
|
2011-03-11 19:20:11 +01:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
|
|
|
errmsg("collation \"%s\" for encoding \"%s\" already exists in schema \"%s\"",
|
Rework order of checks in ALTER / SET SCHEMA
When attempting to move an object into the schema in which it already
was, for most objects classes we were correctly complaining about
exactly that ("object is already in schema"); but for some other object
classes, such as functions, we were instead complaining of a name
collision ("object already exists in schema"). The latter is wrong and
misleading, per complaint from Robert Haas in
CA+TgmoZ0+gNf7RDKRc3u5rHXffP=QjqPZKGxb4BsPz65k7qnHQ@mail.gmail.com
To fix, refactor the way these checks are done. As a bonus, the
resulting code is smaller and can also share some code with Rename
cases.
While at it, remove use of getObjectDescriptionOids() in error messages.
These are normally disallowed because of translatability considerations,
but this one had slipped through since 9.1. (Not sure that this is
worth backpatching, though, as it would create some untranslated
messages in back branches.)
This is loosely based on a patch by KaiGai Kohei, heavily reworked by
me.
2013-01-15 17:23:43 +01:00
|
|
|
collname, GetDatabaseEncodingName(),
|
|
|
|
get_namespace_name(nspOid))));
|
2011-03-11 19:20:11 +01:00
|
|
|
|
|
|
|
/* mustn't match an any-encoding entry, either */
|
|
|
|
if (SearchSysCacheExists3(COLLNAMEENCNSP,
|
Rework order of checks in ALTER / SET SCHEMA
When attempting to move an object into the schema in which it already
was, for most objects classes we were correctly complaining about
exactly that ("object is already in schema"); but for some other object
classes, such as functions, we were instead complaining of a name
collision ("object already exists in schema"). The latter is wrong and
misleading, per complaint from Robert Haas in
CA+TgmoZ0+gNf7RDKRc3u5rHXffP=QjqPZKGxb4BsPz65k7qnHQ@mail.gmail.com
To fix, refactor the way these checks are done. As a bonus, the
resulting code is smaller and can also share some code with Rename
cases.
While at it, remove use of getObjectDescriptionOids() in error messages.
These are normally disallowed because of translatability considerations,
but this one had slipped through since 9.1. (Not sure that this is
worth backpatching, though, as it would create some untranslated
messages in back branches.)
This is loosely based on a patch by KaiGai Kohei, heavily reworked by
me.
2013-01-15 17:23:43 +01:00
|
|
|
CStringGetDatum(collname),
|
2011-03-11 19:20:11 +01:00
|
|
|
Int32GetDatum(-1),
|
Rework order of checks in ALTER / SET SCHEMA
When attempting to move an object into the schema in which it already
was, for most objects classes we were correctly complaining about
exactly that ("object is already in schema"); but for some other object
classes, such as functions, we were instead complaining of a name
collision ("object already exists in schema"). The latter is wrong and
misleading, per complaint from Robert Haas in
CA+TgmoZ0+gNf7RDKRc3u5rHXffP=QjqPZKGxb4BsPz65k7qnHQ@mail.gmail.com
To fix, refactor the way these checks are done. As a bonus, the
resulting code is smaller and can also share some code with Rename
cases.
While at it, remove use of getObjectDescriptionOids() in error messages.
These are normally disallowed because of translatability considerations,
but this one had slipped through since 9.1. (Not sure that this is
worth backpatching, though, as it would create some untranslated
messages in back branches.)
This is loosely based on a patch by KaiGai Kohei, heavily reworked by
me.
2013-01-15 17:23:43 +01:00
|
|
|
ObjectIdGetDatum(nspOid)))
|
2011-03-11 19:20:11 +01:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
|
|
|
errmsg("collation \"%s\" already exists in schema \"%s\"",
|
Rework order of checks in ALTER / SET SCHEMA
When attempting to move an object into the schema in which it already
was, for most objects classes we were correctly complaining about
exactly that ("object is already in schema"); but for some other object
classes, such as functions, we were instead complaining of a name
collision ("object already exists in schema"). The latter is wrong and
misleading, per complaint from Robert Haas in
CA+TgmoZ0+gNf7RDKRc3u5rHXffP=QjqPZKGxb4BsPz65k7qnHQ@mail.gmail.com
To fix, refactor the way these checks are done. As a bonus, the
resulting code is smaller and can also share some code with Rename
cases.
While at it, remove use of getObjectDescriptionOids() in error messages.
These are normally disallowed because of translatability considerations,
but this one had slipped through since 9.1. (Not sure that this is
worth backpatching, though, as it would create some untranslated
messages in back branches.)
This is loosely based on a patch by KaiGai Kohei, heavily reworked by
me.
2013-01-15 17:23:43 +01:00
|
|
|
collname, get_namespace_name(nspOid))));
|
2011-02-12 14:54:13 +01:00
|
|
|
}
|
2017-01-18 18:00:00 +01:00
|
|
|
|
2017-03-23 20:25:34 +01:00
|
|
|
/*
|
|
|
|
* ALTER COLLATION
|
|
|
|
*/
|
|
|
|
ObjectAddress
|
|
|
|
AlterCollation(AlterCollationStmt *stmt)
|
|
|
|
{
|
|
|
|
Relation rel;
|
|
|
|
Oid collOid;
|
|
|
|
HeapTuple tup;
|
|
|
|
Form_pg_collation collForm;
|
|
|
|
Datum collversion;
|
|
|
|
bool isnull;
|
|
|
|
char *oldversion;
|
|
|
|
char *newversion;
|
|
|
|
ObjectAddress address;
|
|
|
|
|
|
|
|
rel = heap_open(CollationRelationId, RowExclusiveLock);
|
|
|
|
collOid = get_collation_oid(stmt->collname, false);
|
|
|
|
|
|
|
|
if (!pg_collation_ownercheck(collOid, GetUserId()))
|
|
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_COLLATION,
|
|
|
|
NameListToString(stmt->collname));
|
|
|
|
|
|
|
|
tup = SearchSysCacheCopy1(COLLOID, ObjectIdGetDatum(collOid));
|
|
|
|
if (!HeapTupleIsValid(tup))
|
|
|
|
elog(ERROR, "cache lookup failed for collation %u", collOid);
|
|
|
|
|
|
|
|
collForm = (Form_pg_collation) GETSTRUCT(tup);
|
|
|
|
collversion = SysCacheGetAttr(COLLOID, tup, Anum_pg_collation_collversion,
|
|
|
|
&isnull);
|
|
|
|
oldversion = isnull ? NULL : TextDatumGetCString(collversion);
|
|
|
|
|
|
|
|
newversion = get_collation_actual_version(collForm->collprovider, NameStr(collForm->collcollate));
|
|
|
|
|
|
|
|
/* cannot change from NULL to non-NULL or vice versa */
|
|
|
|
if ((!oldversion && newversion) || (oldversion && !newversion))
|
|
|
|
elog(ERROR, "invalid collation version change");
|
|
|
|
else if (oldversion && newversion && strcmp(newversion, oldversion) != 0)
|
|
|
|
{
|
|
|
|
bool nulls[Natts_pg_collation];
|
|
|
|
bool replaces[Natts_pg_collation];
|
|
|
|
Datum values[Natts_pg_collation];
|
|
|
|
|
|
|
|
ereport(NOTICE,
|
|
|
|
(errmsg("changing version from %s to %s",
|
|
|
|
oldversion, newversion)));
|
|
|
|
|
|
|
|
memset(values, 0, sizeof(values));
|
|
|
|
memset(nulls, false, sizeof(nulls));
|
|
|
|
memset(replaces, false, sizeof(replaces));
|
|
|
|
|
|
|
|
values[Anum_pg_collation_collversion - 1] = CStringGetTextDatum(newversion);
|
|
|
|
replaces[Anum_pg_collation_collversion - 1] = true;
|
|
|
|
|
|
|
|
tup = heap_modify_tuple(tup, RelationGetDescr(rel),
|
|
|
|
values, nulls, replaces);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ereport(NOTICE,
|
|
|
|
(errmsg("version has not changed")));
|
|
|
|
|
|
|
|
CatalogTupleUpdate(rel, &tup->t_self, tup);
|
|
|
|
|
|
|
|
InvokeObjectPostAlterHook(CollationRelationId, collOid, 0);
|
|
|
|
|
|
|
|
ObjectAddressSet(address, CollationRelationId, collOid);
|
|
|
|
|
|
|
|
heap_freetuple(tup);
|
|
|
|
heap_close(rel, NoLock);
|
|
|
|
|
|
|
|
return address;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
pg_collation_actual_version(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
Oid collid = PG_GETARG_OID(0);
|
|
|
|
HeapTuple tp;
|
|
|
|
char *collcollate;
|
|
|
|
char collprovider;
|
|
|
|
char *version;
|
|
|
|
|
|
|
|
tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
|
|
|
|
if (!HeapTupleIsValid(tp))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("collation with OID %u does not exist", collid)));
|
|
|
|
|
|
|
|
collcollate = pstrdup(NameStr(((Form_pg_collation) GETSTRUCT(tp))->collcollate));
|
|
|
|
collprovider = ((Form_pg_collation) GETSTRUCT(tp))->collprovider;
|
|
|
|
|
|
|
|
ReleaseSysCache(tp);
|
|
|
|
|
|
|
|
version = get_collation_actual_version(collprovider, collcollate);
|
|
|
|
|
|
|
|
if (version)
|
|
|
|
PG_RETURN_TEXT_P(cstring_to_text(version));
|
|
|
|
else
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
}
|
|
|
|
|
2017-01-18 18:00:00 +01:00
|
|
|
|
|
|
|
/*
|
2017-03-23 20:25:34 +01:00
|
|
|
* "Normalize" a libc locale name, stripping off encoding tags such as
|
2017-01-18 18:00:00 +01:00
|
|
|
* ".utf8" (e.g., "en_US.utf8" -> "en_US", but "br_FR.iso885915@euro"
|
|
|
|
* -> "br_FR@euro"). Return true if a new, different name was
|
|
|
|
* generated.
|
|
|
|
*/
|
|
|
|
pg_attribute_unused()
|
|
|
|
static bool
|
2017-03-23 20:25:34 +01:00
|
|
|
normalize_libc_locale_name(char *new, const char *old)
|
2017-01-18 18:00:00 +01:00
|
|
|
{
|
|
|
|
char *n = new;
|
|
|
|
const char *o = old;
|
|
|
|
bool changed = false;
|
|
|
|
|
|
|
|
while (*o)
|
|
|
|
{
|
|
|
|
if (*o == '.')
|
|
|
|
{
|
|
|
|
/* skip over encoding tag such as ".utf8" or ".UTF-8" */
|
|
|
|
o++;
|
|
|
|
while ((*o >= 'A' && *o <= 'Z')
|
|
|
|
|| (*o >= 'a' && *o <= 'z')
|
|
|
|
|| (*o >= '0' && *o <= '9')
|
|
|
|
|| (*o == '-'))
|
|
|
|
o++;
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
*n++ = *o++;
|
|
|
|
}
|
|
|
|
*n = '\0';
|
|
|
|
|
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-23 20:25:34 +01:00
|
|
|
#ifdef USE_ICU
|
|
|
|
static char *
|
|
|
|
get_icu_language_tag(const char *localename)
|
|
|
|
{
|
|
|
|
char buf[ULOC_FULLNAME_CAPACITY];
|
|
|
|
UErrorCode status;
|
|
|
|
|
|
|
|
status = U_ZERO_ERROR;
|
|
|
|
uloc_toLanguageTag(localename, buf, sizeof(buf), TRUE, &status);
|
|
|
|
if (U_FAILURE(status))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errmsg("could not convert locale name \"%s\" to language tag: %s",
|
|
|
|
localename, u_errorName(status))));
|
|
|
|
|
|
|
|
return pstrdup(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
get_icu_locale_comment(const char *localename)
|
|
|
|
{
|
|
|
|
UErrorCode status;
|
|
|
|
UChar displayname[128];
|
|
|
|
int32 len_uchar;
|
|
|
|
char *result;
|
|
|
|
|
|
|
|
status = U_ZERO_ERROR;
|
|
|
|
len_uchar = uloc_getDisplayName(localename, "en", &displayname[0], sizeof(displayname), &status);
|
|
|
|
if (U_FAILURE(status))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errmsg("could get display name for locale \"%s\": %s",
|
|
|
|
localename, u_errorName(status))));
|
|
|
|
|
|
|
|
icu_from_uchar(&result, displayname, len_uchar);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
#endif /* USE_ICU */
|
|
|
|
|
|
|
|
|
2017-01-18 18:00:00 +01:00
|
|
|
Datum
|
|
|
|
pg_import_system_collations(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
#if defined(HAVE_LOCALE_T) && !defined(WIN32)
|
|
|
|
bool if_not_exists = PG_GETARG_BOOL(0);
|
2017-01-18 19:44:19 +01:00
|
|
|
Oid nspid = PG_GETARG_OID(1);
|
2017-01-18 18:00:00 +01:00
|
|
|
|
|
|
|
FILE *locale_a_handle;
|
|
|
|
char localebuf[NAMEDATALEN]; /* we assume ASCII so this is fine */
|
|
|
|
int count = 0;
|
2017-01-18 19:44:19 +01:00
|
|
|
List *aliaslist = NIL;
|
|
|
|
List *localelist = NIL;
|
|
|
|
List *enclist = NIL;
|
|
|
|
ListCell *lca,
|
|
|
|
*lcl,
|
|
|
|
*lce;
|
2017-01-18 18:00:00 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!superuser())
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
|
|
(errmsg("must be superuser to import system collations"))));
|
|
|
|
|
|
|
|
#if defined(HAVE_LOCALE_T) && !defined(WIN32)
|
|
|
|
locale_a_handle = OpenPipeStream("locale -a", "r");
|
|
|
|
if (locale_a_handle == NULL)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode_for_file_access(),
|
|
|
|
errmsg("could not execute command \"%s\": %m",
|
|
|
|
"locale -a")));
|
|
|
|
|
|
|
|
while (fgets(localebuf, sizeof(localebuf), locale_a_handle))
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
size_t len;
|
|
|
|
int enc;
|
|
|
|
bool skip;
|
|
|
|
char alias[NAMEDATALEN];
|
|
|
|
|
|
|
|
len = strlen(localebuf);
|
|
|
|
|
|
|
|
if (len == 0 || localebuf[len - 1] != '\n')
|
|
|
|
{
|
|
|
|
elog(DEBUG1, "locale name too long, skipped: \"%s\"", localebuf);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
localebuf[len - 1] = '\0';
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Some systems have locale names that don't consist entirely of ASCII
|
|
|
|
* letters (such as "bokmål" or "français"). This is
|
|
|
|
* pretty silly, since we need the locale itself to interpret the
|
|
|
|
* non-ASCII characters. We can't do much with those, so we filter
|
|
|
|
* them out.
|
|
|
|
*/
|
|
|
|
skip = false;
|
|
|
|
for (i = 0; i < len; i++)
|
|
|
|
{
|
|
|
|
if (IS_HIGHBIT_SET(localebuf[i]))
|
|
|
|
{
|
|
|
|
skip = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (skip)
|
|
|
|
{
|
|
|
|
elog(DEBUG1, "locale name has non-ASCII characters, skipped: \"%s\"", localebuf);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
enc = pg_get_encoding_from_locale(localebuf, false);
|
|
|
|
if (enc < 0)
|
|
|
|
{
|
|
|
|
/* error message printed by pg_get_encoding_from_locale() */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!PG_VALID_BE_ENCODING(enc))
|
|
|
|
continue; /* ignore locales for client-only encodings */
|
|
|
|
if (enc == PG_SQL_ASCII)
|
|
|
|
continue; /* C/POSIX are already in the catalog */
|
|
|
|
|
|
|
|
count++;
|
|
|
|
|
2017-03-23 20:25:34 +01:00
|
|
|
CollationCreate(localebuf, nspid, GetUserId(), COLLPROVIDER_LIBC, enc,
|
|
|
|
localebuf, localebuf,
|
|
|
|
get_collation_actual_version(COLLPROVIDER_LIBC, localebuf),
|
|
|
|
if_not_exists);
|
2017-01-18 18:00:00 +01:00
|
|
|
|
|
|
|
CommandCounterIncrement();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generate aliases such as "en_US" in addition to "en_US.utf8" for
|
|
|
|
* ease of use. Note that collation names are unique per encoding
|
|
|
|
* only, so this doesn't clash with "en_US" for LATIN1, say.
|
|
|
|
*
|
2017-01-18 19:44:19 +01:00
|
|
|
* However, it might conflict with a name we'll see later in the
|
|
|
|
* "locale -a" output. So save up the aliases and try to add them
|
|
|
|
* after we've read all the output.
|
2017-01-18 18:00:00 +01:00
|
|
|
*/
|
2017-03-23 20:25:34 +01:00
|
|
|
if (normalize_libc_locale_name(alias, localebuf))
|
2017-01-18 18:00:00 +01:00
|
|
|
{
|
2017-01-18 19:44:19 +01:00
|
|
|
aliaslist = lappend(aliaslist, pstrdup(alias));
|
|
|
|
localelist = lappend(localelist, pstrdup(localebuf));
|
|
|
|
enclist = lappend_int(enclist, enc);
|
2017-01-18 18:00:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ClosePipeStream(locale_a_handle);
|
|
|
|
|
2017-01-18 19:44:19 +01:00
|
|
|
/* Now try to add any aliases we created */
|
|
|
|
forthree(lca, aliaslist, lcl, localelist, lce, enclist)
|
|
|
|
{
|
|
|
|
char *alias = (char *) lfirst(lca);
|
|
|
|
char *locale = (char *) lfirst(lcl);
|
|
|
|
int enc = lfirst_int(lce);
|
|
|
|
|
2017-03-23 20:25:34 +01:00
|
|
|
CollationCreate(alias, nspid, GetUserId(), COLLPROVIDER_LIBC, enc,
|
|
|
|
locale, locale,
|
|
|
|
get_collation_actual_version(COLLPROVIDER_LIBC, locale),
|
|
|
|
true);
|
2017-01-18 19:44:19 +01:00
|
|
|
CommandCounterIncrement();
|
|
|
|
}
|
|
|
|
|
2017-01-18 18:00:00 +01:00
|
|
|
if (count == 0)
|
2017-01-23 19:45:32 +01:00
|
|
|
ereport(WARNING,
|
2017-01-18 18:00:00 +01:00
|
|
|
(errmsg("no usable system locales were found")));
|
2017-01-18 19:44:19 +01:00
|
|
|
#endif /* not HAVE_LOCALE_T && not WIN32 */
|
2017-01-18 18:00:00 +01:00
|
|
|
|
2017-03-23 20:25:34 +01:00
|
|
|
#ifdef USE_ICU
|
|
|
|
if (!is_encoding_supported_by_icu(GetDatabaseEncoding()))
|
|
|
|
{
|
|
|
|
ereport(NOTICE,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
|
|
errmsg("encoding \"%s\" not supported by ICU",
|
|
|
|
pg_encoding_to_char(GetDatabaseEncoding()))));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Start the loop at -1 to sneak in the root locale without too much
|
|
|
|
* code duplication.
|
|
|
|
*/
|
|
|
|
for (i = -1; i < ucol_countAvailable(); i++)
|
|
|
|
{
|
|
|
|
const char *name;
|
|
|
|
char *langtag;
|
|
|
|
const char *collcollate;
|
|
|
|
UEnumeration *en;
|
|
|
|
UErrorCode status;
|
|
|
|
const char *val;
|
|
|
|
Oid collid;
|
|
|
|
|
|
|
|
if (i == -1)
|
|
|
|
name = ""; /* ICU root locale */
|
|
|
|
else
|
|
|
|
name = ucol_getAvailable(i);
|
|
|
|
|
|
|
|
langtag = get_icu_language_tag(name);
|
|
|
|
collcollate = U_ICU_VERSION_MAJOR_NUM >= 54 ? langtag : name;
|
|
|
|
collid = CollationCreate(psprintf("%s-x-icu", langtag),
|
|
|
|
nspid, GetUserId(), COLLPROVIDER_ICU, -1,
|
|
|
|
collcollate, collcollate,
|
|
|
|
get_collation_actual_version(COLLPROVIDER_ICU, collcollate),
|
|
|
|
if_not_exists);
|
|
|
|
|
|
|
|
CreateComments(collid, CollationRelationId, 0,
|
|
|
|
get_icu_locale_comment(name));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add keyword variants
|
|
|
|
*/
|
|
|
|
status = U_ZERO_ERROR;
|
|
|
|
en = ucol_getKeywordValuesForLocale("collation", name, TRUE, &status);
|
|
|
|
if (U_FAILURE(status))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errmsg("could not get keyword values for locale \"%s\": %s",
|
|
|
|
name, u_errorName(status))));
|
|
|
|
|
|
|
|
status = U_ZERO_ERROR;
|
|
|
|
uenum_reset(en, &status);
|
|
|
|
while ((val = uenum_next(en, NULL, &status)))
|
|
|
|
{
|
|
|
|
char *localeid = psprintf("%s@collation=%s", name, val);
|
|
|
|
|
|
|
|
langtag = get_icu_language_tag(localeid);
|
|
|
|
collcollate = U_ICU_VERSION_MAJOR_NUM >= 54 ? langtag : localeid;
|
|
|
|
collid = CollationCreate(psprintf("%s-x-icu", langtag),
|
|
|
|
nspid, GetUserId(), COLLPROVIDER_ICU, -1,
|
|
|
|
collcollate, collcollate,
|
|
|
|
get_collation_actual_version(COLLPROVIDER_ICU, collcollate),
|
|
|
|
if_not_exists);
|
|
|
|
CreateComments(collid, CollationRelationId, 0,
|
|
|
|
get_icu_locale_comment(localeid));
|
|
|
|
}
|
|
|
|
if (U_FAILURE(status))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errmsg("could not get keyword values for locale \"%s\": %s",
|
|
|
|
name, u_errorName(status))));
|
|
|
|
uenum_close(en);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-01-18 18:00:00 +01:00
|
|
|
PG_RETURN_VOID();
|
|
|
|
}
|