]> granicus.if.org Git - postgresql/blob - src/backend/commands/conversioncmds.c
Make CREATE CONVERSION verify that a putative encoding conversion function
[postgresql] / src / backend / commands / conversioncmds.c
1 /*-------------------------------------------------------------------------
2  *
3  * conversioncmds.c
4  *        conversion creation command support code
5  *
6  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $PostgreSQL: pgsql/src/backend/commands/conversioncmds.c,v 1.36 2008/11/14 17:40:56 tgl Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
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"
32
33 static void AlterConversionOwner_internal(Relation rel, Oid conversionOid,
34                                                           Oid newOwnerId);
35
36 /*
37  * CREATE CONVERSION
38  */
39 void
40 CreateConversionCommand(CreateConversionStmt *stmt)
41 {
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};
52
53         /* Convert list of names to a name and namespace */
54         namespaceId = QualifiedNameGetCreationNamespace(stmt->conversion_name,
55                                                                                                         &conversion_name);
56
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));
62
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)));
70
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)));
77
78         /*
79          * Check the existence of the conversion function. Function name could be
80          * a qualified name.
81          */
82         funcoid = LookupFuncName(func_name, sizeof(funcargs) / sizeof(Oid),
83                                                          funcargs, false);
84
85         /* Check it returns VOID, else it's probably the wrong function */
86         if (get_func_rettype(funcoid) != VOIDOID)
87                 ereport(ERROR,
88                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
89                                  errmsg("encoding conversion function %s must return type \"void\"",
90                                                 NameListToString(func_name))));
91
92         /* Check we have EXECUTE rights for the function */
93         aclresult = pg_proc_aclcheck(funcoid, GetUserId(), ACL_EXECUTE);
94         if (aclresult != ACLCHECK_OK)
95                 aclcheck_error(aclresult, ACL_KIND_PROC,
96                                            NameListToString(func_name));
97
98         /*
99          * All seem ok, go ahead (possible failure would be a duplicate conversion
100          * name)
101          */
102         ConversionCreate(conversion_name, namespaceId, GetUserId(),
103                                          from_encoding, to_encoding, funcoid, stmt->def);
104 }
105
106 /*
107  * DROP CONVERSION
108  */
109 void
110 DropConversionsCommand(DropStmt *drop)
111 {
112         ObjectAddresses *objects;
113         ListCell *cell;
114
115         /*
116          * First we identify all the conversions, then we delete them in a single
117          * performMultipleDeletions() call.  This is to avoid unwanted
118          * DROP RESTRICT errors if one of the conversions depends on another.
119          * (Not that that is very likely, but we may as well do this consistently.)
120          */
121         objects = new_object_addresses();
122
123         foreach(cell, drop->objects)
124         {
125                 List            *name = (List *) lfirst(cell);
126                 Oid                     conversionOid;
127                 HeapTuple       tuple;
128                 Form_pg_conversion con;
129                 ObjectAddress object;
130
131                 conversionOid = FindConversionByName(name);
132
133                 if (!OidIsValid(conversionOid))
134                 {
135                         if (!drop->missing_ok)
136                         {
137                                 ereport(ERROR,
138                                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
139                                                  errmsg("conversion \"%s\" does not exist",
140                                                                 NameListToString(name))));
141                         }
142                         else
143                         {
144                                 ereport(NOTICE,
145                                                 (errmsg("conversion \"%s\" does not exist, skipping",
146                                                                 NameListToString(name))));
147                         }
148                         continue;
149                 }
150
151                 tuple = SearchSysCache(CONVOID,
152                                                            ObjectIdGetDatum(conversionOid),
153                                                            0, 0, 0);
154                 if (!HeapTupleIsValid(tuple))
155                         elog(ERROR, "cache lookup failed for conversion %u",
156                                  conversionOid);
157                 con = (Form_pg_conversion) GETSTRUCT(tuple);
158
159                 /* Permission check: must own conversion or its namespace */
160                 if (!pg_conversion_ownercheck(conversionOid, GetUserId()) &&
161                         !pg_namespace_ownercheck(con->connamespace, GetUserId()))
162                         aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION,
163                                                    NameStr(con->conname));
164
165                 object.classId = ConversionRelationId;
166                 object.objectId = conversionOid;
167                 object.objectSubId = 0;
168
169                 add_exact_object_address(&object, objects);
170
171                 ReleaseSysCache(tuple);
172         }
173
174         performMultipleDeletions(objects, drop->behavior);
175
176         free_object_addresses(objects);
177 }
178
179 /*
180  * Rename conversion
181  */
182 void
183 RenameConversion(List *name, const char *newname)
184 {
185         Oid                     conversionOid;
186         Oid                     namespaceOid;
187         HeapTuple       tup;
188         Relation        rel;
189         AclResult       aclresult;
190
191         rel = heap_open(ConversionRelationId, RowExclusiveLock);
192
193         conversionOid = FindConversionByName(name);
194         if (!OidIsValid(conversionOid))
195                 ereport(ERROR,
196                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
197                                  errmsg("conversion \"%s\" does not exist",
198                                                 NameListToString(name))));
199
200         tup = SearchSysCacheCopy(CONVOID,
201                                                          ObjectIdGetDatum(conversionOid),
202                                                          0, 0, 0);
203         if (!HeapTupleIsValid(tup)) /* should not happen */
204                 elog(ERROR, "cache lookup failed for conversion %u", conversionOid);
205
206         namespaceOid = ((Form_pg_conversion) GETSTRUCT(tup))->connamespace;
207
208         /* make sure the new name doesn't exist */
209         if (SearchSysCacheExists(CONNAMENSP,
210                                                          CStringGetDatum(newname),
211                                                          ObjectIdGetDatum(namespaceOid),
212                                                          0, 0))
213                 ereport(ERROR,
214                                 (errcode(ERRCODE_DUPLICATE_OBJECT),
215                                  errmsg("conversion \"%s\" already exists in schema \"%s\"",
216                                                 newname, get_namespace_name(namespaceOid))));
217
218         /* must be owner */
219         if (!pg_conversion_ownercheck(conversionOid, GetUserId()))
220                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION,
221                                            NameListToString(name));
222
223         /* must have CREATE privilege on namespace */
224         aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE);
225         if (aclresult != ACLCHECK_OK)
226                 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
227                                            get_namespace_name(namespaceOid));
228
229         /* rename */
230         namestrcpy(&(((Form_pg_conversion) GETSTRUCT(tup))->conname), newname);
231         simple_heap_update(rel, &tup->t_self, tup);
232         CatalogUpdateIndexes(rel, tup);
233
234         heap_close(rel, NoLock);
235         heap_freetuple(tup);
236 }
237
238 /*
239  * Change conversion owner, by name
240  */
241 void
242 AlterConversionOwner(List *name, Oid newOwnerId)
243 {
244         Oid                     conversionOid;
245         Relation        rel;
246
247         rel = heap_open(ConversionRelationId, RowExclusiveLock);
248
249         conversionOid = FindConversionByName(name);
250         if (!OidIsValid(conversionOid))
251                 ereport(ERROR,
252                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
253                                  errmsg("conversion \"%s\" does not exist",
254                                                 NameListToString(name))));
255
256         AlterConversionOwner_internal(rel, conversionOid, newOwnerId);
257
258         heap_close(rel, NoLock);
259 }
260
261 /*
262  * Change conversion owner, by oid
263  */
264 void
265 AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId)
266 {
267         Relation        rel;
268
269         rel = heap_open(ConversionRelationId, RowExclusiveLock);
270
271         AlterConversionOwner_internal(rel, conversionOid, newOwnerId);
272
273         heap_close(rel, NoLock);
274 }
275
276 /*
277  * AlterConversionOwner_internal
278  *
279  * Internal routine for changing the owner.  rel must be pg_conversion, already
280  * open and suitably locked; it will not be closed.
281  */
282 static void
283 AlterConversionOwner_internal(Relation rel, Oid conversionOid, Oid newOwnerId)
284 {
285         Form_pg_conversion convForm;
286         HeapTuple       tup;
287
288         Assert(RelationGetRelid(rel) == ConversionRelationId);
289
290         tup = SearchSysCacheCopy(CONVOID,
291                                                          ObjectIdGetDatum(conversionOid),
292                                                          0, 0, 0);
293         if (!HeapTupleIsValid(tup)) /* should not happen */
294                 elog(ERROR, "cache lookup failed for conversion %u", conversionOid);
295
296         convForm = (Form_pg_conversion) GETSTRUCT(tup);
297
298         /*
299          * If the new owner is the same as the existing owner, consider the
300          * command to have succeeded.  This is for dump restoration purposes.
301          */
302         if (convForm->conowner != newOwnerId)
303         {
304                 AclResult       aclresult;
305
306                 /* Superusers can always do it */
307                 if (!superuser())
308                 {
309                         /* Otherwise, must be owner of the existing object */
310                         if (!pg_conversion_ownercheck(HeapTupleGetOid(tup), GetUserId()))
311                                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION,
312                                                            NameStr(convForm->conname));
313
314                         /* Must be able to become new owner */
315                         check_is_member_of_role(GetUserId(), newOwnerId);
316
317                         /* New owner must have CREATE privilege on namespace */
318                         aclresult = pg_namespace_aclcheck(convForm->connamespace,
319                                                                                           newOwnerId,
320                                                                                           ACL_CREATE);
321                         if (aclresult != ACLCHECK_OK)
322                                 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
323                                                            get_namespace_name(convForm->connamespace));
324                 }
325
326                 /*
327                  * Modify the owner --- okay to scribble on tup because it's a copy
328                  */
329                 convForm->conowner = newOwnerId;
330
331                 simple_heap_update(rel, &tup->t_self, tup);
332
333                 CatalogUpdateIndexes(rel, tup);
334
335                 /* Update owner dependency reference */
336                 changeDependencyOnOwner(ConversionRelationId, conversionOid,
337                                                                 newOwnerId);
338         }
339
340         heap_freetuple(tup);
341 }