Skip to content
Snippets Groups Projects
Select Git revision
  • bdca82f44d0e0168dece56cbd53b54ba142f328f
  • master default
  • benchmark-tools
  • postgres-lambda
  • REL9_4_25
  • REL9_5_20
  • REL9_6_16
  • REL_10_11
  • REL_11_6
  • REL_12_1
  • REL_12_0
  • REL_12_RC1
  • REL_12_BETA4
  • REL9_4_24
  • REL9_5_19
  • REL9_6_15
  • REL_10_10
  • REL_11_5
  • REL_12_BETA3
  • REL9_4_23
  • REL9_5_18
  • REL9_6_14
  • REL_10_9
  • REL_11_4
24 results

collationcmds.c

Blame
  • user avatar
    Peter Eisentraut authored
    - 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
    b313bca0
    History
    collationcmds.c 10.21 KiB
    /*-------------------------------------------------------------------------
     *
     * 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;
    }