diff options
author | Peter Eisentraut <peter_e@gmx.net> | 2011-02-12 15:54:13 +0200 |
---|---|---|
committer | Peter Eisentraut <peter_e@gmx.net> | 2011-02-12 15:55:18 +0200 |
commit | b313bca0afce3ab9dab0a77c64c0982835854b9a (patch) | |
tree | 862203ffd9adbc62684bec05fa32b2de4713e6b9 /src/backend | |
parent | d31e2a495b6f2127afc31b4da2e5f4e89aa2cdfe (diff) | |
download | postgresql-b313bca0afce3ab9dab0a77c64c0982835854b9a.tar.gz postgresql-b313bca0afce3ab9dab0a77c64c0982835854b9a.zip |
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')
-rw-r--r-- | src/backend/catalog/Makefile | 2 | ||||
-rw-r--r-- | src/backend/catalog/aclchk.c | 31 | ||||
-rw-r--r-- | src/backend/catalog/dependency.c | 46 | ||||
-rw-r--r-- | src/backend/catalog/heap.c | 9 | ||||
-rw-r--r-- | src/backend/catalog/index.c | 14 | ||||
-rw-r--r-- | src/backend/catalog/objectaddress.c | 9 | ||||
-rw-r--r-- | src/backend/catalog/pg_collation.c | 163 | ||||
-rw-r--r-- | src/backend/catalog/pg_shdepend.c | 6 | ||||
-rw-r--r-- | src/backend/catalog/pg_type.c | 13 | ||||
-rw-r--r-- | src/backend/commands/Makefile | 2 | ||||
-rw-r--r-- | src/backend/commands/alter.c | 17 | ||||
-rw-r--r-- | src/backend/commands/collationcmds.c | 401 | ||||
-rw-r--r-- | src/backend/commands/comment.c | 5 | ||||
-rw-r--r-- | src/backend/commands/dbcommands.c | 116 | ||||
-rw-r--r-- | src/backend/commands/tablecmds.c | 26 | ||||
-rw-r--r-- | src/backend/commands/typecmds.c | 1 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 57 | ||||
-rw-r--r-- | src/backend/tcop/utility.c | 18 |
18 files changed, 869 insertions, 67 deletions
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile index 45aca8dd7f7..3a834618d28 100644 --- a/src/backend/catalog/Makefile +++ b/src/backend/catalog/Makefile @@ -11,7 +11,7 @@ top_builddir = ../../.. include $(top_builddir)/src/Makefile.global OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \ - objectaddress.o pg_aggregate.o pg_constraint.o pg_conversion.o \ + objectaddress.o pg_aggregate.o pg_collation.o pg_constraint.o pg_conversion.o \ pg_depend.o pg_enum.o pg_inherits.o pg_largeobject.o pg_namespace.o \ pg_operator.o pg_proc.o pg_db_role_setting.o pg_shdepend.o pg_type.o \ storage.o toasting.o diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index e07db507c09..db1d092796e 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -25,6 +25,7 @@ #include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/pg_authid.h" +#include "catalog/pg_collation.h" #include "catalog/pg_conversion.h" #include "catalog/pg_database.h" #include "catalog/pg_default_acl.h" @@ -3131,6 +3132,8 @@ static const char *const no_priv_msg[MAX_ACL_KIND] = gettext_noop("permission denied for operator class %s"), /* ACL_KIND_OPFAMILY */ gettext_noop("permission denied for operator family %s"), + /* ACL_KIND_COLLATION */ + gettext_noop("permission denied for collation %s"), /* ACL_KIND_CONVERSION */ gettext_noop("permission denied for conversion %s"), /* ACL_KIND_TABLESPACE */ @@ -3173,6 +3176,8 @@ static const char *const not_owner_msg[MAX_ACL_KIND] = gettext_noop("must be owner of operator class %s"), /* ACL_KIND_OPFAMILY */ gettext_noop("must be owner of operator family %s"), + /* ACL_KIND_COLLATION */ + gettext_noop("must be owner of collation %s"), /* ACL_KIND_CONVERSION */ gettext_noop("must be owner of conversion %s"), /* ACL_KIND_TABLESPACE */ @@ -4632,6 +4637,32 @@ pg_database_ownercheck(Oid db_oid, Oid roleid) } /* + * Ownership check for a collation (specified by OID). + */ +bool +pg_collation_ownercheck(Oid coll_oid, Oid roleid) +{ + HeapTuple tuple; + Oid ownerId; + + /* Superusers bypass all permission checking. */ + if (superuser_arg(roleid)) + return true; + + tuple = SearchSysCache1(COLLOID, ObjectIdGetDatum(coll_oid)); + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("collation with OID %u does not exist", coll_oid))); + + ownerId = ((Form_pg_collation) GETSTRUCT(tuple))->collowner; + + ReleaseSysCache(tuple); + + return has_privs_of_role(roleid, ownerId); +} + +/* * Ownership check for a conversion (specified by OID). */ bool diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index 5c5f750a069..1679776f019 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -28,6 +28,8 @@ #include "catalog/pg_attrdef.h" #include "catalog/pg_authid.h" #include "catalog/pg_cast.h" +#include "catalog/pg_collation.h" +#include "catalog/pg_collation_fn.h" #include "catalog/pg_constraint.h" #include "catalog/pg_conversion.h" #include "catalog/pg_conversion_fn.h" @@ -133,6 +135,7 @@ static const Oid object_classes[MAX_OCLASS] = { ProcedureRelationId, /* OCLASS_PROC */ TypeRelationId, /* OCLASS_TYPE */ CastRelationId, /* OCLASS_CAST */ + CollationRelationId, /* OCLASS_COLLATION */ ConstraintRelationId, /* OCLASS_CONSTRAINT */ ConversionRelationId, /* OCLASS_CONVERSION */ AttrDefaultRelationId, /* OCLASS_DEFAULT */ @@ -1075,6 +1078,10 @@ doDeletion(const ObjectAddress *object) DropCastById(object->objectId); break; + case OCLASS_COLLATION: + RemoveCollationById(object->objectId); + break; + case OCLASS_CONSTRAINT: RemoveConstraintById(object->objectId); break; @@ -1417,6 +1424,9 @@ find_expr_references_walker(Node *node, /* A constant must depend on the constant's datatype */ add_object_address(OCLASS_TYPE, con->consttype, 0, context->addrs); + if (OidIsValid(con->constcollid)) + add_object_address(OCLASS_COLLATION, con->constcollid, 0, + context->addrs); /* * If it's a regclass or similar literal referring to an existing @@ -1483,6 +1493,9 @@ find_expr_references_walker(Node *node, /* A parameter must depend on the parameter's datatype */ add_object_address(OCLASS_TYPE, param->paramtype, 0, context->addrs); + if (OidIsValid(param->paramcollation)) + add_object_address(OCLASS_COLLATION, param->paramcollation, 0, + context->addrs); } else if (IsA(node, FuncExpr)) { @@ -1553,6 +1566,13 @@ find_expr_references_walker(Node *node, add_object_address(OCLASS_TYPE, relab->resulttype, 0, context->addrs); } + else if (IsA(node, CollateClause)) + { + CollateClause *coll = (CollateClause *) node; + + add_object_address(OCLASS_COLLATION, coll->collOid, 0, + context->addrs); + } else if (IsA(node, CoerceViaIO)) { CoerceViaIO *iocoerce = (CoerceViaIO *) node; @@ -1653,6 +1673,14 @@ find_expr_references_walker(Node *node, add_object_address(OCLASS_TYPE, lfirst_oid(ct), 0, context->addrs); } + foreach(ct, rte->funccolcollations) + { + Oid collid = lfirst_oid(ct); + + if (OidIsValid(collid)) + add_object_address(OCLASS_COLLATION, collid, 0, + context->addrs); + } break; default: break; @@ -2019,6 +2047,9 @@ getObjectClass(const ObjectAddress *object) case CastRelationId: return OCLASS_CAST; + case CollationRelationId: + return OCLASS_COLLATION; + case ConstraintRelationId: return OCLASS_CONSTRAINT; @@ -2167,6 +2198,21 @@ getObjectDescription(const ObjectAddress *object) break; } + case OCLASS_COLLATION: + { + HeapTuple collTup; + + collTup = SearchSysCache1(COLLOID, + ObjectIdGetDatum(object->objectId)); + if (!HeapTupleIsValid(collTup)) + elog(ERROR, "cache lookup failed for collation %u", + object->objectId); + appendStringInfo(&buffer, _("collation %s"), + NameStr(((Form_pg_collation) GETSTRUCT(collTup))->collname)); + ReleaseSysCache(collTup); + break; + } + case OCLASS_CONSTRAINT: { HeapTuple conTup; diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index d9b272a7122..2cf210d82c2 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -42,6 +42,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_attrdef.h" +#include "catalog/pg_collation.h" #include "catalog/pg_constraint.h" #include "catalog/pg_foreign_table.h" #include "catalog/pg_inherits.h" @@ -613,6 +614,14 @@ AddNewAttributeTuples(Oid new_rel_oid, referenced.objectId = attr->atttypid; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + if (OidIsValid(attr->attcollation)) + { + referenced.classId = CollationRelationId; + referenced.objectId = attr->attcollation; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } } /* diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 452ced6644e..5979a650921 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -36,6 +36,7 @@ #include "catalog/index.h" #include "catalog/indexing.h" #include "catalog/namespace.h" +#include "catalog/pg_collation.h" #include "catalog/pg_constraint.h" #include "catalog/pg_operator.h" #include "catalog/pg_opclass.h" @@ -960,6 +961,19 @@ index_create(Relation heapRelation, Assert(!initdeferred); } + /* Store dependency on collations */ + for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) + { + if (OidIsValid(collationObjectId[i])) + { + referenced.classId = CollationRelationId; + referenced.objectId = collationObjectId[i]; + referenced.objectSubId = 0; + + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + } + /* Store dependency on operator classes */ for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) { diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c index 505bc35f6d3..aeb07710e84 100644 --- a/src/backend/catalog/objectaddress.c +++ b/src/backend/catalog/objectaddress.c @@ -25,6 +25,7 @@ #include "catalog/pg_authid.h" #include "catalog/pg_cast.h" #include "catalog/pg_class.h" +#include "catalog/pg_collation.h" #include "catalog/pg_constraint.h" #include "catalog/pg_conversion.h" #include "catalog/pg_database.h" @@ -165,6 +166,11 @@ get_object_address(ObjectType objtype, List *objname, List *objargs, false, -1); address.objectSubId = 0; break; + case OBJECT_COLLATION: + address.classId = CollationRelationId; + address.objectId = get_collation_oid(objname, false); + address.objectSubId = 0; + break; case OBJECT_CONVERSION: address.classId = ConversionRelationId; address.objectId = get_conversion_oid(objname, false); @@ -621,6 +627,9 @@ object_exists(ObjectAddress address) case OperatorRelationId: cache = OPEROID; break; + case CollationRelationId: + cache = COLLOID; + break; case ConversionRelationId: cache = CONVOID; break; diff --git a/src/backend/catalog/pg_collation.c b/src/backend/catalog/pg_collation.c new file mode 100644 index 00000000000..54a75a6f623 --- /dev/null +++ b/src/backend/catalog/pg_collation.c @@ -0,0 +1,163 @@ +/*------------------------------------------------------------------------- + * + * pg_collation.c + * routines to support manipulation of the pg_collation relation + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/catalog/pg_collation.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/heapam.h" +#include "access/sysattr.h" +#include "catalog/dependency.h" +#include "catalog/indexing.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_collation.h" +#include "catalog/pg_collation_fn.h" +#include "catalog/pg_namespace.h" +#include "catalog/pg_proc.h" +#include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/fmgroids.h" +#include "utils/rel.h" +#include "utils/syscache.h" +#include "utils/tqual.h" + +/* + * CollationCreate + * + * Add a new tuple to pg_collation. + */ +Oid +CollationCreate(const char *collname, Oid collnamespace, + Oid collowner, + int32 collencoding, + const char *collcollate, const char *collctype) +{ + int i; + Relation rel; + TupleDesc tupDesc; + HeapTuple tup; + bool nulls[Natts_pg_collation]; + Datum values[Natts_pg_collation]; + NameData name_name, name_collate, name_ctype; + Oid oid; + ObjectAddress myself, + referenced; + + AssertArg(collname); + AssertArg(collnamespace); + AssertArg(collowner); + AssertArg(collcollate); + AssertArg(collctype); + + /* make sure there is no existing collation of same name */ + if (SearchSysCacheExists3(COLLNAMEENCNSP, + PointerGetDatum(collname), + Int32GetDatum(collencoding), + ObjectIdGetDatum(collnamespace))) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("collation \"%s\" for encoding \"%s\" already exists", + collname, pg_encoding_to_char(collencoding)))); + + /* open pg_collation */ + rel = heap_open(CollationRelationId, RowExclusiveLock); + tupDesc = rel->rd_att; + + /* initialize nulls and values */ + for (i = 0; i < Natts_pg_collation; i++) + { + nulls[i] = false; + values[i] = (Datum) NULL; + } + + /* form a tuple */ + namestrcpy(&name_name, collname); + values[Anum_pg_collation_collname - 1] = NameGetDatum(&name_name); + values[Anum_pg_collation_collnamespace - 1] = ObjectIdGetDatum(collnamespace); + values[Anum_pg_collation_collowner - 1] = ObjectIdGetDatum(collowner); + values[Anum_pg_collation_collencoding - 1] = Int32GetDatum(collencoding); + namestrcpy(&name_collate, collcollate); + values[Anum_pg_collation_collcollate - 1] = NameGetDatum(&name_collate); + namestrcpy(&name_ctype, collctype); + values[Anum_pg_collation_collctype - 1] = NameGetDatum(&name_ctype); + + tup = heap_form_tuple(tupDesc, values, nulls); + + /* insert a new tuple */ + oid = simple_heap_insert(rel, tup); + Assert(OidIsValid(oid)); + + /* update the index if any */ + CatalogUpdateIndexes(rel, tup); + + myself.classId = CollationRelationId; + myself.objectId = HeapTupleGetOid(tup); + myself.objectSubId = 0; + + /* create dependency on namespace */ + referenced.classId = NamespaceRelationId; + referenced.objectId = collnamespace; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + /* create dependency on owner */ + recordDependencyOnOwner(CollationRelationId, HeapTupleGetOid(tup), + collowner); + + /* dependency on extension */ + recordDependencyOnCurrentExtension(&myself); + + /* Post creation hook for new collation */ + InvokeObjectAccessHook(OAT_POST_CREATE, + CollationRelationId, HeapTupleGetOid(tup), 0); + + heap_freetuple(tup); + heap_close(rel, RowExclusiveLock); + + return oid; +} + +/* + * RemoveCollationById + * + * Remove a tuple from pg_collation by Oid. This function is solely + * called inside catalog/dependency.c + */ +void +RemoveCollationById(Oid collationOid) +{ + Relation rel; + HeapTuple tuple; + HeapScanDesc scan; + ScanKeyData scanKeyData; + + ScanKeyInit(&scanKeyData, + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(collationOid)); + + /* open pg_collation */ + rel = heap_open(CollationRelationId, RowExclusiveLock); + + scan = heap_beginscan(rel, SnapshotNow, + 1, &scanKeyData); + + /* search for the target tuple */ + if (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection))) + simple_heap_delete(rel, &tuple->t_self); + else + elog(ERROR, "could not find tuple for collation %u", collationOid); + heap_endscan(scan); + heap_close(rel, RowExclusiveLock); +} diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c index 040f777b022..8c8e7b276d7 100644 --- a/src/backend/catalog/pg_shdepend.c +++ b/src/backend/catalog/pg_shdepend.c @@ -21,6 +21,7 @@ #include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/pg_authid.h" +#include "catalog/pg_collation.h" #include "catalog/pg_conversion.h" #include "catalog/pg_database.h" #include "catalog/pg_default_acl.h" @@ -35,6 +36,7 @@ #include "catalog/pg_tablespace.h" #include "catalog/pg_type.h" #include "commands/dbcommands.h" +#include "commands/collationcmds.h" #include "commands/conversioncmds.h" #include "commands/defrem.h" #include "commands/proclang.h" @@ -1323,6 +1325,10 @@ shdepReassignOwned(List *roleids, Oid newrole) /* Issue the appropriate ALTER OWNER call */ switch (sdepForm->classid) { + case CollationRelationId: + AlterCollationOwner_oid(sdepForm->objid, newrole); + break; + case ConversionRelationId: AlterConversionOwner_oid(sdepForm->objid, newrole); break; diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c index 9b574179ff9..06301c075bb 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -19,6 +19,7 @@ #include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/objectaccess.h" +#include "catalog/pg_collation.h" #include "catalog/pg_namespace.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" @@ -156,6 +157,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId) InvalidOid, false, InvalidOid, + InvalidOid, NULL, false); @@ -460,6 +462,7 @@ TypeCreate(Oid newTypeOid, elementType, isImplicitArray, baseType, + typeCollation, (defaultTypeBin ? stringToNode(defaultTypeBin) : NULL), @@ -499,6 +502,7 @@ GenerateTypeDependencies(Oid typeNamespace, Oid elementType, bool isImplicitArray, Oid baseType, + Oid typeCollation, Node *defaultExpr, bool rebuild) { @@ -639,6 +643,15 @@ GenerateTypeDependencies(Oid typeNamespace, recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } + /* Normal dependency from a domain to its base type's collation. */ + if (OidIsValid(typeCollation)) + { + referenced.classId = CollationRelationId; + referenced.objectId = typeCollation; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + /* Normal dependency on the default expression. */ if (defaultExpr) recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL); diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile index 0aadbc56adb..81fd6581f32 100644 --- a/src/backend/commands/Makefile +++ b/src/backend/commands/Makefile @@ -13,7 +13,7 @@ top_builddir = ../../.. include $(top_builddir)/src/Makefile.global OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \ - constraint.o conversioncmds.o copy.o \ + collationcmds.o constraint.o conversioncmds.o copy.o \ dbcommands.o define.o discard.o explain.o extension.o \ foreigncmds.o functioncmds.o \ indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \ diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c index 2c9340accf1..99fdd7dba30 100644 --- a/src/backend/commands/alter.c +++ b/src/backend/commands/alter.c @@ -20,6 +20,7 @@ #include "catalog/pg_largeobject.h" #include "catalog/pg_namespace.h" #include "commands/alter.h" +#include "commands/collationcmds.h" #include "commands/conversioncmds.h" #include "commands/dbcommands.h" #include "commands/defrem.h" @@ -53,6 +54,10 @@ ExecRenameStmt(RenameStmt *stmt) RenameAggregate(stmt->object, stmt->objarg, stmt->newname); break; + case OBJECT_COLLATION: + RenameCollation(stmt->object, stmt->newname); + break; + case OBJECT_CONVERSION: RenameConversion(stmt->object, stmt->newname); break; @@ -185,6 +190,10 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt) stmt->newschema); break; + case OBJECT_COLLATION: + AlterCollationNamespace(stmt->object, stmt->newschema); + break; + case OBJECT_CONVERSION: AlterConversionNamespace(stmt->object, stmt->newschema); break; @@ -302,6 +311,10 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid) oldNspOid = AlterTypeNamespace_oid(objid, nspOid); break; + case OCLASS_COLLATION: + oldNspOid = AlterCollationNamespace_oid(objid, nspOid); + break; + case OCLASS_CONVERSION: oldNspOid = AlterConversionNamespace_oid(objid, nspOid); break; @@ -478,6 +491,10 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt) AlterAggregateOwner(stmt->object, stmt->objarg, newowner); break; + case OBJECT_COLLATION: + AlterCollationOwner(stmt->object, newowner); + break; + case OBJECT_CONVERSION: AlterConversionOwner(stmt->object, newowner); break; 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; +} diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c index faef256b1d8..a0a561c144d 100644 --- a/src/backend/commands/comment.c +++ b/src/backend/commands/comment.c @@ -133,6 +133,11 @@ CommentObject(CommentStmt *stmt) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, strVal(linitial(stmt->objname))); break; + case OBJECT_COLLATION: + if (!pg_collation_ownercheck(address.objectId, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_COLLATION, + NameListToString(stmt->objname)); + break; case OBJECT_CONVERSION: if (!pg_conversion_ownercheck(address.objectId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION, diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index c7e0c6a8778..87d9e545b4f 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -129,8 +129,6 @@ createdb(const CreatedbStmt *stmt) char *dbctype = NULL; int encoding = -1; int dbconnlimit = -1; - int ctype_encoding; - int collate_encoding; int notherbackends; int npreparedxacts; createdb_failure_params fparms; @@ -334,60 +332,7 @@ createdb(const CreatedbStmt *stmt) (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("invalid locale name %s", dbctype))); - /* - * Check whether chosen encoding matches chosen locale settings. This - * restriction is necessary because libc's locale-specific code usually - * fails when presented with data in an encoding it's not expecting. We - * allow mismatch in four cases: - * - * 1. locale encoding = SQL_ASCII, which means that the locale is C/POSIX - * which works with any encoding. - * - * 2. locale encoding = -1, which means that we couldn't determine the - * locale's encoding and have to trust the user to get it right. - * - * 3. selected encoding is UTF8 and platform is win32. This is because - * UTF8 is a pseudo codepage that is supported in all locales since it's - * converted to UTF16 before being used. - * - * 4. selected encoding is SQL_ASCII, but only if you're a superuser. This - * is risky but we have historically allowed it --- notably, the - * regression tests require it. - * - * Note: if you change this policy, fix initdb to match. - */ - 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 || - ctype_encoding == -1 || -#ifdef WIN32 - encoding == PG_UTF8 || -#endif - (encoding == PG_SQL_ASCII && superuser()))) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("encoding %s does not match locale %s", - pg_encoding_to_char(encoding), - dbctype), - errdetail("The chosen LC_CTYPE setting requires encoding %s.", - pg_encoding_to_char(ctype_encoding)))); - - if (!(collate_encoding == encoding || - collate_encoding == PG_SQL_ASCII || - collate_encoding == -1 || -#ifdef WIN32 - encoding == PG_UTF8 || -#endif - (encoding == PG_SQL_ASCII && superuser()))) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("encoding %s does not match locale %s", - pg_encoding_to_char(encoding), - dbcollate), - errdetail("The chosen LC_COLLATE setting requires encoding %s.", - pg_encoding_to_char(collate_encoding)))); + check_encoding_locale_matches(encoding, dbcollate, dbctype); /* * Check that the new encoding and locale settings match the source @@ -710,6 +655,65 @@ createdb(const CreatedbStmt *stmt) PointerGetDatum(&fparms)); } +/* + * Check whether chosen encoding matches chosen locale settings. This + * restriction is necessary because libc's locale-specific code usually + * fails when presented with data in an encoding it's not expecting. We + * allow mismatch in four cases: + * + * 1. locale encoding = SQL_ASCII, which means that the locale is C/POSIX + * which works with any encoding. + * + * 2. locale encoding = -1, which means that we couldn't determine the + * locale's encoding and have to trust the user to get it right. + * + * 3. selected encoding is UTF8 and platform is win32. This is because + * UTF8 is a pseudo codepage that is supported in all locales since it's + * converted to UTF16 before being used. + * + * 4. selected encoding is SQL_ASCII, but only if you're a superuser. This + * is risky but we have historically allowed it --- notably, the + * regression tests require it. + * + * Note: if you change this policy, fix initdb to match. + */ +void +check_encoding_locale_matches(int encoding, const char *collate, const char *ctype) +{ + int ctype_encoding = pg_get_encoding_from_locale(ctype, true); + int collate_encoding = pg_get_encoding_from_locale(collate, true); + + if (!(ctype_encoding == encoding || + ctype_encoding == PG_SQL_ASCII || + ctype_encoding == -1 || +#ifdef WIN32 + encoding == PG_UTF8 || +#endif + (encoding == PG_SQL_ASCII && superuser()))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("encoding %s does not match locale %s", + pg_encoding_to_char(encoding), + ctype), + errdetail("The chosen LC_CTYPE setting requires encoding %s.", + pg_encoding_to_char(ctype_encoding)))); + + if (!(collate_encoding == encoding || + collate_encoding == PG_SQL_ASCII || + collate_encoding == -1 || +#ifdef WIN32 + encoding == PG_UTF8 || +#endif + (encoding == PG_SQL_ASCII && superuser()))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("encoding %s does not match locale %s", + pg_encoding_to_char(encoding), + collate), + errdetail("The chosen LC_COLLATE setting requires encoding %s.", + pg_encoding_to_char(collate_encoding)))); +} + /* Error cleanup callback for createdb */ static void createdb_failure_callback(int code, Datum arg) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 1db42d044ac..324d9ff9ea1 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -27,6 +27,7 @@ #include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/objectaccess.h" +#include "catalog/pg_collation.h" #include "catalog/pg_constraint.h" #include "catalog/pg_depend.h" #include "catalog/pg_foreign_table.h" @@ -293,7 +294,7 @@ static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recu AlterTableCmd *cmd, LOCKMODE lockmode); static void ATExecAddColumn(AlteredTableInfo *tab, Relation rel, ColumnDef *colDef, bool isOid, LOCKMODE lockmode); -static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid); +static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid, Oid collid); static void ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd, LOCKMODE lockmode); static void ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode); @@ -4369,14 +4370,14 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, /* * Add needed dependency entries for the new column. */ - add_column_datatype_dependency(myrelid, newattnum, attribute.atttypid); + add_column_datatype_dependency(myrelid, newattnum, attribute.atttypid, attribute.attcollation); } /* * Install a column's dependency on its datatype. */ static void -add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid) +add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid, Oid collid) { ObjectAddress myself, referenced; @@ -4388,6 +4389,14 @@ add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid) referenced.objectId = typid; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + if (collid) + { + referenced.classId = CollationRelationId; + referenced.objectId = collid; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } } /* @@ -6877,6 +6886,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, case OCLASS_PROC: case OCLASS_TYPE: case OCLASS_CAST: + case OCLASS_COLLATION: case OCLASS_CONVERSION: case OCLASS_LANGUAGE: case OCLASS_LARGEOBJECT: @@ -6918,7 +6928,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, /* * Now scan for dependencies of this column on other things. The only * thing we should find is the dependency on the column datatype, which we - * want to remove. + * want to remove, and possibly an associated collation. */ ScanKeyInit(&key[0], Anum_pg_depend_classid, @@ -6943,8 +6953,10 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, if (foundDep->deptype != DEPENDENCY_NORMAL) elog(ERROR, "found unexpected dependency type '%c'", foundDep->deptype); - if (foundDep->refclassid != TypeRelationId || - foundDep->refobjid != attTup->atttypid) + if (!(foundDep->refclassid == TypeRelationId && + foundDep->refobjid == attTup->atttypid) && + !(foundDep->refclassid == CollationRelationId && + foundDep->refobjid == attTup->attcollation)) elog(ERROR, "found unexpected dependency for column"); simple_heap_delete(depRel, &depTup->t_self); @@ -6977,7 +6989,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, heap_close(attrelation, RowExclusiveLock); /* Install dependency on new datatype */ - add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype); + add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype, targetcollid); /* * Drop any pg_statistic entry for the column, since it's now wrong type diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index f9da7816b25..be1f1d791fd 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -1736,6 +1736,7 @@ AlterDomainDefault(List *names, Node *defaultRaw) InvalidOid, false, /* a domain isn't an implicit array */ typTup->typbasetype, + typTup->typcollation, defaultExpr, true); /* Rebuild is true */ diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a99f8c6ca24..3857205ef9b 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -482,7 +482,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_ CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE - CLUSTER COALESCE COLLATE COLUMN COMMENT COMMENTS COMMIT + CLUSTER COALESCE COLLATE COLLATION COLUMN COMMENT COMMENTS COMMIT COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE CREATEDB CREATEROLE CREATEUSER CROSS CSV CURRENT_P @@ -3316,6 +3316,15 @@ AlterExtensionContentsStmt: n->objargs = list_make1($9); $$ = (Node *) n; } + | ALTER EXTENSION name add_drop COLLATION any_name + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_COLLATION; + n->objname = $6; + $$ = (Node *)n; + } | ALTER EXTENSION name add_drop CONVERSION_P any_name { AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); @@ -4248,6 +4257,24 @@ DefineStmt: n->definition = $6; $$ = (Node *)n; } + | CREATE COLLATION any_name definition + { + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_COLLATION; + n->args = NIL; + n->defnames = $3; + n->definition = $4; + $$ = (Node *)n; + } + | CREATE COLLATION any_name FROM any_name + { + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_COLLATION; + n->args = NIL; + n->defnames = $3; + n->definition = list_make1(makeDefElem("from", (Node *) $5)); + $$ = (Node *)n; + } ; definition: '(' def_list ')' { $$ = $2; } @@ -4621,6 +4648,7 @@ drop_type: TABLE { $$ = OBJECT_TABLE; } | FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; } | TYPE_P { $$ = OBJECT_TYPE; } | DOMAIN_P { $$ = OBJECT_DOMAIN; } + | COLLATION { $$ = OBJECT_COLLATION; } | CONVERSION_P { $$ = OBJECT_CONVERSION; } | SCHEMA { $$ = OBJECT_SCHEMA; } | EXTENSION { $$ = OBJECT_EXTENSION; } @@ -4676,7 +4704,7 @@ opt_restart_seqs: * the object associated with the comment. The form of the statement is: * * COMMENT ON [ [ DATABASE | DOMAIN | INDEX | SEQUENCE | TABLE | TYPE | VIEW | - * CONVERSION | LANGUAGE | OPERATOR CLASS | LARGE OBJECT | + * COLLATION | CONVERSION | LANGUAGE | OPERATOR CLASS | LARGE OBJECT | * CAST | COLUMN | SCHEMA | TABLESPACE | EXTENSION | ROLE | * TEXT SEARCH PARSER | TEXT SEARCH DICTIONARY | * TEXT SEARCH TEMPLATE | TEXT SEARCH CONFIGURATION | @@ -4854,6 +4882,7 @@ comment_type: | DOMAIN_P { $$ = OBJECT_DOMAIN; } | TYPE_P { $$ = OBJECT_TYPE; } | VIEW { $$ = OBJECT_VIEW; } + | COLLATION { $$ = OBJECT_COLLATION; } | CONVERSION_P { $$ = OBJECT_CONVERSION; } | TABLESPACE { $$ = OBJECT_TABLESPACE; } | EXTENSION { $$ = OBJECT_EXTENSION; } @@ -6275,6 +6304,14 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name n->newname = $7; $$ = (Node *)n; } + | ALTER COLLATION any_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_COLLATION; + n->object = $3; + n->newname = $6; + $$ = (Node *)n; + } | ALTER CONVERSION_P any_name RENAME TO name { RenameStmt *n = makeNode(RenameStmt); @@ -6535,6 +6572,14 @@ AlterObjectSchemaStmt: n->newschema = $7; $$ = (Node *)n; } + | ALTER COLLATION any_name SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_COLLATION; + n->object = $3; + n->newschema = $6; + $$ = (Node *)n; + } | ALTER CONVERSION_P any_name SET SCHEMA name { AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); @@ -6684,6 +6729,14 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId n->newowner = $7; $$ = (Node *)n; } + | ALTER COLLATION any_name OWNER TO RoleId + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_COLLATION; + n->object = $3; + n->newowner = $6; + $$ = (Node *)n; + } | ALTER CONVERSION_P any_name OWNER TO RoleId { AlterOwnerStmt *n = makeNode(AlterOwnerStmt); diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 8ca042024f3..67aa5e2ab63 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -26,6 +26,7 @@ #include "commands/async.h" #include "commands/cluster.h" #include "commands/comment.h" +#include "commands/collationcmds.h" #include "commands/conversioncmds.h" #include "commands/copy.h" #include "commands/dbcommands.h" @@ -665,6 +666,10 @@ standard_ProcessUtility(Node *parsetree, RemoveTypes(stmt); break; + case OBJECT_COLLATION: + DropCollationsCommand(stmt); + break; + case OBJECT_CONVERSION: DropConversionsCommand(stmt); break; @@ -884,6 +889,10 @@ standard_ProcessUtility(Node *parsetree, Assert(stmt->args == NIL); DefineTSConfiguration(stmt->defnames, stmt->definition); break; + case OBJECT_COLLATION: + Assert(stmt->args == NIL); + DefineCollation(stmt->defnames, stmt->definition); + break; default: elog(ERROR, "unrecognized define stmt type: %d", (int) stmt->kind); @@ -1453,6 +1462,9 @@ AlterObjectTypeCommandTag(ObjectType objtype) case OBJECT_CAST: tag = "ALTER CAST"; break; + case OBJECT_COLLATION: + tag = "ALTER COLLATION"; + break; case OBJECT_COLUMN: tag = "ALTER TABLE"; break; @@ -1754,6 +1766,9 @@ CreateCommandTag(Node *parsetree) case OBJECT_DOMAIN: tag = "DROP DOMAIN"; break; + case OBJECT_COLLATION: + tag = "DROP COLLATION"; + break; case OBJECT_CONVERSION: tag = "DROP CONVERSION"; break; @@ -1867,6 +1882,9 @@ CreateCommandTag(Node *parsetree) case OBJECT_TSCONFIGURATION: tag = "CREATE TEXT SEARCH CONFIGURATION"; break; + case OBJECT_COLLATION: + tag = "CREATE COLLATION"; + break; default: tag = "???"; } |