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
|
|
|
*
|
2013-01-01 23:15:01 +01:00
|
|
|
* Portions Copyright (c) 1996-2013, 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"
|
|
|
|
|
|
|
|
#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"
|
|
|
|
#include "catalog/pg_collation.h"
|
|
|
|
#include "catalog/pg_collation_fn.h"
|
|
|
|
#include "commands/alter.h"
|
|
|
|
#include "commands/collationcmds.h"
|
|
|
|
#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"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* CREATE COLLATION
|
|
|
|
*/
|
2012-12-24 00:25:03 +01:00
|
|
|
Oid
|
2011-02-12 14:54:13 +01:00
|
|
|
DefineCollation(List *names, List *parameters)
|
|
|
|
{
|
|
|
|
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;
|
2011-02-12 14:54:13 +01:00
|
|
|
char *collcollate = NULL;
|
|
|
|
char *collctype = NULL;
|
2011-03-04 21:14:37 +01:00
|
|
|
Oid newoid;
|
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)
|
|
|
|
{
|
2011-04-10 17:42:00 +02:00
|
|
|
DefElem *defel = (DefElem *) lfirst(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;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
|
|
errmsg("collation attribute \"%s\" not recognized",
|
|
|
|
defel->defname)));
|
|
|
|
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));
|
|
|
|
|
|
|
|
ReleaseSysCache(tp);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (localeEl)
|
|
|
|
{
|
|
|
|
collcollate = defGetString(localeEl);
|
|
|
|
collctype = defGetString(localeEl);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lccollateEl)
|
|
|
|
collcollate = defGetString(lccollateEl);
|
|
|
|
|
|
|
|
if (lcctypeEl)
|
|
|
|
collctype = defGetString(lcctypeEl);
|
|
|
|
|
|
|
|
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")));
|
|
|
|
|
|
|
|
check_encoding_locale_matches(GetDatabaseEncoding(), collcollate, collctype);
|
|
|
|
|
2011-03-04 21:14:37 +01:00
|
|
|
newoid = CollationCreate(collName,
|
2011-03-11 19:20:11 +01:00
|
|
|
collNamespace,
|
|
|
|
GetUserId(),
|
|
|
|
GetDatabaseEncoding(),
|
|
|
|
collcollate,
|
|
|
|
collctype);
|
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
|
|
|
|
|
|
|
return newoid;
|
2011-02-12 14:54:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Rename collation
|
|
|
|
*/
|
2012-12-24 00:25:03 +01:00
|
|
|
Oid
|
2011-02-12 14:54:13 +01:00
|
|
|
RenameCollation(List *name, const char *newname)
|
|
|
|
{
|
|
|
|
Oid collationOid;
|
|
|
|
Oid namespaceOid;
|
|
|
|
HeapTuple tup;
|
|
|
|
Relation rel;
|
|
|
|
AclResult aclresult;
|
|
|
|
|
|
|
|
rel = heap_open(CollationRelationId, RowExclusiveLock);
|
|
|
|
|
|
|
|
collationOid = get_collation_oid(name, false);
|
|
|
|
|
|
|
|
tup = SearchSysCacheCopy1(COLLOID, ObjectIdGetDatum(collationOid));
|
|
|
|
if (!HeapTupleIsValid(tup)) /* should not happen */
|
|
|
|
elog(ERROR, "cache lookup failed for collation %u", collationOid);
|
|
|
|
|
|
|
|
namespaceOid = ((Form_pg_collation) GETSTRUCT(tup))->collnamespace;
|
|
|
|
|
|
|
|
/* make sure the new name doesn't exist */
|
|
|
|
if (SearchSysCacheExists3(COLLNAMEENCNSP,
|
|
|
|
CStringGetDatum(newname),
|
|
|
|
Int32GetDatum(GetDatabaseEncoding()),
|
|
|
|
ObjectIdGetDatum(namespaceOid)))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
2011-03-11 19:20:11 +01:00
|
|
|
errmsg("collation \"%s\" for encoding \"%s\" already exists in schema \"%s\"",
|
2011-02-12 14:54:13 +01:00
|
|
|
newname,
|
|
|
|
GetDatabaseEncodingName(),
|
|
|
|
get_namespace_name(namespaceOid))));
|
|
|
|
|
2011-03-11 19:20:11 +01:00
|
|
|
/* mustn't match an any-encoding entry, either */
|
|
|
|
if (SearchSysCacheExists3(COLLNAMEENCNSP,
|
|
|
|
CStringGetDatum(newname),
|
|
|
|
Int32GetDatum(-1),
|
|
|
|
ObjectIdGetDatum(namespaceOid)))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
|
|
|
errmsg("collation \"%s\" already exists in schema \"%s\"",
|
|
|
|
newname,
|
|
|
|
get_namespace_name(namespaceOid))));
|
|
|
|
|
2011-02-12 14:54:13 +01:00
|
|
|
/* must be owner */
|
|
|
|
if (!pg_collation_ownercheck(collationOid, GetUserId()))
|
|
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_COLLATION,
|
|
|
|
NameListToString(name));
|
|
|
|
|
|
|
|
/* must have CREATE privilege on namespace */
|
|
|
|
aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE);
|
|
|
|
if (aclresult != ACLCHECK_OK)
|
|
|
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
|
|
|
get_namespace_name(namespaceOid));
|
|
|
|
|
|
|
|
/* rename */
|
|
|
|
namestrcpy(&(((Form_pg_collation) GETSTRUCT(tup))->collname), newname);
|
|
|
|
simple_heap_update(rel, &tup->t_self, tup);
|
|
|
|
CatalogUpdateIndexes(rel, tup);
|
|
|
|
|
|
|
|
heap_freetuple(tup);
|
2011-03-11 19:20:11 +01:00
|
|
|
|
|
|
|
heap_close(rel, RowExclusiveLock);
|
2012-12-24 00:25:03 +01:00
|
|
|
|
|
|
|
return collationOid;
|
2011-02-12 14:54:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Execute ALTER COLLATION SET SCHEMA
|
|
|
|
*/
|
2012-12-24 00:25:03 +01:00
|
|
|
Oid
|
2011-02-12 14:54:13 +01:00
|
|
|
AlterCollationNamespace(List *name, const char *newschema)
|
|
|
|
{
|
2011-03-11 19:20:11 +01:00
|
|
|
Oid collOid,
|
|
|
|
nspOid;
|
2011-02-12 14:54:13 +01:00
|
|
|
|
|
|
|
collOid = get_collation_oid(name, false);
|
|
|
|
|
|
|
|
nspOid = LookupCreationNamespace(newschema);
|
|
|
|
|
2011-03-11 19:20:11 +01:00
|
|
|
AlterCollationNamespace_oid(collOid, nspOid);
|
2012-12-24 00:25:03 +01:00
|
|
|
|
|
|
|
return collOid;
|
2011-02-12 14:54:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Change collation schema, by oid
|
|
|
|
*/
|
|
|
|
Oid
|
|
|
|
AlterCollationNamespace_oid(Oid collOid, Oid newNspOid)
|
|
|
|
{
|
2011-04-10 17:42:00 +02:00
|
|
|
Oid oldNspOid;
|
2011-02-12 14:54:13 +01:00
|
|
|
Relation rel;
|
2011-03-11 19:20:11 +01:00
|
|
|
char *collation_name;
|
2011-02-12 14:54:13 +01:00
|
|
|
|
|
|
|
rel = heap_open(CollationRelationId, RowExclusiveLock);
|
|
|
|
|
2011-03-11 19:20:11 +01:00
|
|
|
/*
|
|
|
|
* We have to check for name collision ourselves, because
|
2012-09-27 23:13:09 +02:00
|
|
|
* AlterObjectNamespace_internal doesn't know how to deal with the encoding
|
2011-03-11 19:20:11 +01:00
|
|
|
* considerations.
|
|
|
|
*/
|
|
|
|
collation_name = get_collation_name(collOid);
|
|
|
|
if (!collation_name)
|
|
|
|
elog(ERROR, "cache lookup failed for collation %u", collOid);
|
|
|
|
|
|
|
|
/* make sure the name doesn't already exist in new schema */
|
|
|
|
if (SearchSysCacheExists3(COLLNAMEENCNSP,
|
|
|
|
CStringGetDatum(collation_name),
|
|
|
|
Int32GetDatum(GetDatabaseEncoding()),
|
|
|
|
ObjectIdGetDatum(newNspOid)))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
|
|
|
errmsg("collation \"%s\" for encoding \"%s\" already exists in schema \"%s\"",
|
|
|
|
collation_name,
|
|
|
|
GetDatabaseEncodingName(),
|
|
|
|
get_namespace_name(newNspOid))));
|
|
|
|
|
|
|
|
/* mustn't match an any-encoding entry, either */
|
|
|
|
if (SearchSysCacheExists3(COLLNAMEENCNSP,
|
|
|
|
CStringGetDatum(collation_name),
|
|
|
|
Int32GetDatum(-1),
|
|
|
|
ObjectIdGetDatum(newNspOid)))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
|
|
|
errmsg("collation \"%s\" already exists in schema \"%s\"",
|
|
|
|
collation_name,
|
|
|
|
get_namespace_name(newNspOid))));
|
|
|
|
|
|
|
|
/* OK, do the work */
|
2012-09-27 23:13:09 +02:00
|
|
|
oldNspOid = AlterObjectNamespace_internal(rel, collOid, newNspOid);
|
2011-02-12 14:54:13 +01:00
|
|
|
|
|
|
|
heap_close(rel, RowExclusiveLock);
|
|
|
|
|
|
|
|
return oldNspOid;
|
|
|
|
}
|