diff options
Diffstat (limited to 'src/backend/commands/collationcmds.c')
-rw-r--r-- | src/backend/commands/collationcmds.c | 401 |
1 files changed, 401 insertions, 0 deletions
diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c new file mode 100644 index 00000000000..6db72d919cc --- /dev/null +++ b/src/backend/commands/collationcmds.c @@ -0,0 +1,401 @@ +/*------------------------------------------------------------------------- + * + * collationcmds.c + * collation creation command support code + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/commands/collationcmds.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/heapam.h" +#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 "parser/parse_type.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/syscache.h" + +static void AlterCollationOwner_internal(Relation rel, Oid collationOid, + Oid newOwnerId); + +/* + * CREATE COLLATION + */ +void +DefineCollation(List *names, List *parameters) +{ + char *collName; + Oid collNamespace; + AclResult aclresult; + ListCell *pl; + DefElem *fromEl = NULL; + DefElem *localeEl = NULL; + DefElem *lccollateEl = NULL; + DefElem *lcctypeEl = NULL; + char *collcollate = NULL; + char *collctype = NULL; + + 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) + { + DefElem *defel = (DefElem *) lfirst(pl); + 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; + + collid = LookupCollation(NULL, defGetQualifiedName(fromEl), -1); + 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), + errmsg("parameter \"lc_collate\" parameter must be specified"))); + + if (!collctype) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("parameter \"lc_ctype\" must be specified"))); + + check_encoding_locale_matches(GetDatabaseEncoding(), collcollate, collctype); + + CollationCreate(collName, + collNamespace, + GetUserId(), + GetDatabaseEncoding(), + collcollate, + collctype); +} + +/* + * DROP COLLATION + */ +void +DropCollationsCommand(DropStmt *drop) +{ + ObjectAddresses *objects; + ListCell *cell; + + /* + * First we identify all the collations, then we delete them in a single + * performMultipleDeletions() call. This is to avoid unwanted DROP + * RESTRICT errors if one of the collations depends on another. (Not that + * that is very likely, but we may as well do this consistently.) + */ + objects = new_object_addresses(); + + foreach(cell, drop->objects) + { + List *name = (List *) lfirst(cell); + Oid collationOid; + HeapTuple tuple; + Form_pg_collation coll; + ObjectAddress object; + + collationOid = get_collation_oid(name, drop->missing_ok); + + if (!OidIsValid(collationOid)) + { + ereport(NOTICE, + (errmsg("collation \"%s\" does not exist, skipping", + NameListToString(name)))); + continue; + } + + tuple = SearchSysCache1(COLLOID, ObjectIdGetDatum(collationOid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for collation %u", + collationOid); + coll = (Form_pg_collation) GETSTRUCT(tuple); + + /* Permission check: must own collation or its namespace */ + if (!pg_collation_ownercheck(collationOid, GetUserId()) && + !pg_namespace_ownercheck(coll->collnamespace, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_COLLATION, + NameStr(coll->collname)); + + object.classId = CollationRelationId; + object.objectId = collationOid; + object.objectSubId = 0; + + add_exact_object_address(&object, objects); + + ReleaseSysCache(tuple); + } + + performMultipleDeletions(objects, drop->behavior); + + free_object_addresses(objects); +} + +/* + * Rename collation + */ +void +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), + errmsg("collation \"%s\" for current database encoding \"%s\" already exists in schema \"%s\"", + newname, + GetDatabaseEncodingName(), + get_namespace_name(namespaceOid)))); + + /* 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_close(rel, NoLock); + heap_freetuple(tup); +} + +/* + * Change collation owner, by name + */ +void +AlterCollationOwner(List *name, Oid newOwnerId) +{ + Oid collationOid; + Relation rel; + + rel = heap_open(CollationRelationId, RowExclusiveLock); + + collationOid = get_collation_oid(name, false); + + AlterCollationOwner_internal(rel, collationOid, newOwnerId); + + heap_close(rel, NoLock); +} + +/* + * Change collation owner, by oid + */ +void +AlterCollationOwner_oid(Oid collationOid, Oid newOwnerId) +{ + Relation rel; + + rel = heap_open(CollationRelationId, RowExclusiveLock); + + AlterCollationOwner_internal(rel, collationOid, newOwnerId); + + heap_close(rel, NoLock); +} + +/* + * AlterCollationOwner_internal + * + * Internal routine for changing the owner. rel must be pg_collation, already + * open and suitably locked; it will not be closed. + */ +static void +AlterCollationOwner_internal(Relation rel, Oid collationOid, Oid newOwnerId) +{ + Form_pg_collation collForm; + HeapTuple tup; + + Assert(RelationGetRelid(rel) == CollationRelationId); + + tup = SearchSysCacheCopy1(COLLOID, ObjectIdGetDatum(collationOid)); + if (!HeapTupleIsValid(tup)) /* should not happen */ + elog(ERROR, "cache lookup failed for collation %u", collationOid); + + collForm = (Form_pg_collation) GETSTRUCT(tup); + + /* + * If the new owner is the same as the existing owner, consider the + * command to have succeeded. This is for dump restoration purposes. + */ + if (collForm->collowner != newOwnerId) + { + AclResult aclresult; + + /* Superusers can always do it */ + if (!superuser()) + { + /* Otherwise, must be owner of the existing object */ + if (!pg_collation_ownercheck(HeapTupleGetOid(tup), GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_COLLATION, + NameStr(collForm->collname)); + + /* Must be able to become new owner */ + check_is_member_of_role(GetUserId(), newOwnerId); + + /* New owner must have CREATE privilege on namespace */ + aclresult = pg_namespace_aclcheck(collForm->collnamespace, + newOwnerId, + ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(collForm->collnamespace)); + } + + /* + * Modify the owner --- okay to scribble on tup because it's a copy + */ + collForm->collowner = newOwnerId; + + simple_heap_update(rel, &tup->t_self, tup); + + CatalogUpdateIndexes(rel, tup); + + /* Update owner dependency reference */ + changeDependencyOnOwner(CollationRelationId, collationOid, + newOwnerId); + } + + heap_freetuple(tup); +} + +/* + * Execute ALTER COLLATION SET SCHEMA + */ +void +AlterCollationNamespace(List *name, const char *newschema) +{ + Oid collOid, nspOid; + Relation rel; + + rel = heap_open(CollationRelationId, RowExclusiveLock); + + collOid = get_collation_oid(name, false); + + /* get schema OID */ + nspOid = LookupCreationNamespace(newschema); + + AlterObjectNamespace(rel, COLLOID, -1, + collOid, nspOid, + Anum_pg_collation_collname, + Anum_pg_collation_collnamespace, + Anum_pg_collation_collowner, + ACL_KIND_COLLATION); + + heap_close(rel, NoLock); +} + +/* + * Change collation schema, by oid + */ +Oid +AlterCollationNamespace_oid(Oid collOid, Oid newNspOid) +{ + Oid oldNspOid; + Relation rel; + + rel = heap_open(CollationRelationId, RowExclusiveLock); + + oldNspOid = AlterObjectNamespace(rel, COLLOID, -1, + collOid, newNspOid, + Anum_pg_collation_collname, + Anum_pg_collation_collnamespace, + Anum_pg_collation_collowner, + ACL_KIND_COLLATION); + + heap_close(rel, RowExclusiveLock); + + return oldNspOid; +} |