Fix pg_dump bug in the database-level collation patch. "datcollate" and
[PostgreSQL.git] / src / backend / commands / conversioncmds.c
blob2ab0759b4dcad62dc712517f837ea1b850fc8754
1 /*-------------------------------------------------------------------------
3 * conversioncmds.c
4 * conversion creation command support code
6 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
10 * IDENTIFICATION
11 * $PostgreSQL$
13 *-------------------------------------------------------------------------
15 #include "postgres.h"
17 #include "access/heapam.h"
18 #include "catalog/dependency.h"
19 #include "catalog/indexing.h"
20 #include "catalog/pg_conversion.h"
21 #include "catalog/pg_conversion_fn.h"
22 #include "catalog/pg_type.h"
23 #include "commands/conversioncmds.h"
24 #include "mb/pg_wchar.h"
25 #include "miscadmin.h"
26 #include "parser/parse_func.h"
27 #include "utils/acl.h"
28 #include "utils/builtins.h"
29 #include "utils/lsyscache.h"
30 #include "utils/rel.h"
31 #include "utils/syscache.h"
33 static void AlterConversionOwner_internal(Relation rel, Oid conversionOid,
34 Oid newOwnerId);
37 * CREATE CONVERSION
39 void
40 CreateConversionCommand(CreateConversionStmt *stmt)
42 Oid namespaceId;
43 char *conversion_name;
44 AclResult aclresult;
45 int from_encoding;
46 int to_encoding;
47 Oid funcoid;
48 const char *from_encoding_name = stmt->for_encoding_name;
49 const char *to_encoding_name = stmt->to_encoding_name;
50 List *func_name = stmt->func_name;
51 static Oid funcargs[] = {INT4OID, INT4OID, CSTRINGOID, INTERNALOID, INT4OID};
53 /* Convert list of names to a name and namespace */
54 namespaceId = QualifiedNameGetCreationNamespace(stmt->conversion_name,
55 &conversion_name);
57 /* Check we have creation rights in target namespace */
58 aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE);
59 if (aclresult != ACLCHECK_OK)
60 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
61 get_namespace_name(namespaceId));
63 /* Check the encoding names */
64 from_encoding = pg_char_to_encoding(from_encoding_name);
65 if (from_encoding < 0)
66 ereport(ERROR,
67 (errcode(ERRCODE_UNDEFINED_OBJECT),
68 errmsg("source encoding \"%s\" does not exist",
69 from_encoding_name)));
71 to_encoding = pg_char_to_encoding(to_encoding_name);
72 if (to_encoding < 0)
73 ereport(ERROR,
74 (errcode(ERRCODE_UNDEFINED_OBJECT),
75 errmsg("destination encoding \"%s\" does not exist",
76 to_encoding_name)));
79 * Check the existence of the conversion function. Function name could be
80 * a qualified name.
82 funcoid = LookupFuncName(func_name, sizeof(funcargs) / sizeof(Oid),
83 funcargs, false);
85 /* Check we have EXECUTE rights for the function */
86 aclresult = pg_proc_aclcheck(funcoid, GetUserId(), ACL_EXECUTE);
87 if (aclresult != ACLCHECK_OK)
88 aclcheck_error(aclresult, ACL_KIND_PROC,
89 NameListToString(func_name));
92 * All seem ok, go ahead (possible failure would be a duplicate conversion
93 * name)
95 ConversionCreate(conversion_name, namespaceId, GetUserId(),
96 from_encoding, to_encoding, funcoid, stmt->def);
100 * DROP CONVERSION
102 void
103 DropConversionsCommand(DropStmt *drop)
105 ObjectAddresses *objects;
106 ListCell *cell;
109 * First we identify all the conversions, then we delete them in a single
110 * performMultipleDeletions() call. This is to avoid unwanted
111 * DROP RESTRICT errors if one of the conversions depends on another.
112 * (Not that that is very likely, but we may as well do this consistently.)
114 objects = new_object_addresses();
116 foreach(cell, drop->objects)
118 List *name = (List *) lfirst(cell);
119 Oid conversionOid;
120 HeapTuple tuple;
121 Form_pg_conversion con;
122 ObjectAddress object;
124 conversionOid = FindConversionByName(name);
126 if (!OidIsValid(conversionOid))
128 if (!drop->missing_ok)
130 ereport(ERROR,
131 (errcode(ERRCODE_UNDEFINED_OBJECT),
132 errmsg("conversion \"%s\" does not exist",
133 NameListToString(name))));
135 else
137 ereport(NOTICE,
138 (errmsg("conversion \"%s\" does not exist, skipping",
139 NameListToString(name))));
141 continue;
144 tuple = SearchSysCache(CONVOID,
145 ObjectIdGetDatum(conversionOid),
146 0, 0, 0);
147 if (!HeapTupleIsValid(tuple))
148 elog(ERROR, "cache lookup failed for conversion %u",
149 conversionOid);
150 con = (Form_pg_conversion) GETSTRUCT(tuple);
152 /* Permission check: must own conversion or its namespace */
153 if (!pg_conversion_ownercheck(conversionOid, GetUserId()) &&
154 !pg_namespace_ownercheck(con->connamespace, GetUserId()))
155 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION,
156 NameStr(con->conname));
158 object.classId = ConversionRelationId;
159 object.objectId = conversionOid;
160 object.objectSubId = 0;
162 add_exact_object_address(&object, objects);
164 ReleaseSysCache(tuple);
167 performMultipleDeletions(objects, drop->behavior);
169 free_object_addresses(objects);
173 * Rename conversion
175 void
176 RenameConversion(List *name, const char *newname)
178 Oid conversionOid;
179 Oid namespaceOid;
180 HeapTuple tup;
181 Relation rel;
182 AclResult aclresult;
184 rel = heap_open(ConversionRelationId, RowExclusiveLock);
186 conversionOid = FindConversionByName(name);
187 if (!OidIsValid(conversionOid))
188 ereport(ERROR,
189 (errcode(ERRCODE_UNDEFINED_OBJECT),
190 errmsg("conversion \"%s\" does not exist",
191 NameListToString(name))));
193 tup = SearchSysCacheCopy(CONVOID,
194 ObjectIdGetDatum(conversionOid),
195 0, 0, 0);
196 if (!HeapTupleIsValid(tup)) /* should not happen */
197 elog(ERROR, "cache lookup failed for conversion %u", conversionOid);
199 namespaceOid = ((Form_pg_conversion) GETSTRUCT(tup))->connamespace;
201 /* make sure the new name doesn't exist */
202 if (SearchSysCacheExists(CONNAMENSP,
203 CStringGetDatum(newname),
204 ObjectIdGetDatum(namespaceOid),
205 0, 0))
206 ereport(ERROR,
207 (errcode(ERRCODE_DUPLICATE_OBJECT),
208 errmsg("conversion \"%s\" already exists in schema \"%s\"",
209 newname, get_namespace_name(namespaceOid))));
211 /* must be owner */
212 if (!pg_conversion_ownercheck(conversionOid, GetUserId()))
213 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION,
214 NameListToString(name));
216 /* must have CREATE privilege on namespace */
217 aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE);
218 if (aclresult != ACLCHECK_OK)
219 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
220 get_namespace_name(namespaceOid));
222 /* rename */
223 namestrcpy(&(((Form_pg_conversion) GETSTRUCT(tup))->conname), newname);
224 simple_heap_update(rel, &tup->t_self, tup);
225 CatalogUpdateIndexes(rel, tup);
227 heap_close(rel, NoLock);
228 heap_freetuple(tup);
232 * Change conversion owner, by name
234 void
235 AlterConversionOwner(List *name, Oid newOwnerId)
237 Oid conversionOid;
238 Relation rel;
240 rel = heap_open(ConversionRelationId, RowExclusiveLock);
242 conversionOid = FindConversionByName(name);
243 if (!OidIsValid(conversionOid))
244 ereport(ERROR,
245 (errcode(ERRCODE_UNDEFINED_OBJECT),
246 errmsg("conversion \"%s\" does not exist",
247 NameListToString(name))));
249 AlterConversionOwner_internal(rel, conversionOid, newOwnerId);
251 heap_close(rel, NoLock);
255 * Change conversion owner, by oid
257 void
258 AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId)
260 Relation rel;
262 rel = heap_open(ConversionRelationId, RowExclusiveLock);
264 AlterConversionOwner_internal(rel, conversionOid, newOwnerId);
266 heap_close(rel, NoLock);
270 * AlterConversionOwner_internal
272 * Internal routine for changing the owner. rel must be pg_conversion, already
273 * open and suitably locked; it will not be closed.
275 static void
276 AlterConversionOwner_internal(Relation rel, Oid conversionOid, Oid newOwnerId)
278 Form_pg_conversion convForm;
279 HeapTuple tup;
281 Assert(RelationGetRelid(rel) == ConversionRelationId);
283 tup = SearchSysCacheCopy(CONVOID,
284 ObjectIdGetDatum(conversionOid),
285 0, 0, 0);
286 if (!HeapTupleIsValid(tup)) /* should not happen */
287 elog(ERROR, "cache lookup failed for conversion %u", conversionOid);
289 convForm = (Form_pg_conversion) GETSTRUCT(tup);
292 * If the new owner is the same as the existing owner, consider the
293 * command to have succeeded. This is for dump restoration purposes.
295 if (convForm->conowner != newOwnerId)
297 AclResult aclresult;
299 /* Superusers can always do it */
300 if (!superuser())
302 /* Otherwise, must be owner of the existing object */
303 if (!pg_conversion_ownercheck(HeapTupleGetOid(tup), GetUserId()))
304 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION,
305 NameStr(convForm->conname));
307 /* Must be able to become new owner */
308 check_is_member_of_role(GetUserId(), newOwnerId);
310 /* New owner must have CREATE privilege on namespace */
311 aclresult = pg_namespace_aclcheck(convForm->connamespace,
312 newOwnerId,
313 ACL_CREATE);
314 if (aclresult != ACLCHECK_OK)
315 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
316 get_namespace_name(convForm->connamespace));
320 * Modify the owner --- okay to scribble on tup because it's a copy
322 convForm->conowner = newOwnerId;
324 simple_heap_update(rel, &tup->t_self, tup);
326 CatalogUpdateIndexes(rel, tup);
328 /* Update owner dependency reference */
329 changeDependencyOnOwner(ConversionRelationId, conversionOid,
330 newOwnerId);
333 heap_freetuple(tup);