summaryrefslogtreecommitdiff
path: root/src/backend/commands/collationcmds.c
diff options
context:
space:
mode:
authorPeter Eisentraut <peter_e@gmx.net>2011-02-12 15:54:13 +0200
committerPeter Eisentraut <peter_e@gmx.net>2011-02-12 15:55:18 +0200
commitb313bca0afce3ab9dab0a77c64c0982835854b9a (patch)
tree862203ffd9adbc62684bec05fa32b2de4713e6b9 /src/backend/commands/collationcmds.c
parentd31e2a495b6f2127afc31b4da2e5f4e89aa2cdfe (diff)
DDL support for collations
- collowner field - CREATE COLLATION - ALTER COLLATION - DROP COLLATION - COMMENT ON COLLATION - integration with extensions - pg_dump support for the above - dependency management - psql tab completion - psql \dO command
Diffstat (limited to 'src/backend/commands/collationcmds.c')
-rw-r--r--src/backend/commands/collationcmds.c401
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;
+}