1 /*-------------------------------------------------------------------------
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
13 *-------------------------------------------------------------------------
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
,
40 CreateConversionCommand(CreateConversionStmt
*stmt
)
43 char *conversion_name
;
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
,
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)
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
);
74 (errcode(ERRCODE_UNDEFINED_OBJECT
),
75 errmsg("destination encoding \"%s\" does not exist",
79 * Check the existence of the conversion function. Function name could be
82 funcoid
= LookupFuncName(func_name
, sizeof(funcargs
) / sizeof(Oid
),
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
95 ConversionCreate(conversion_name
, namespaceId
, GetUserId(),
96 from_encoding
, to_encoding
, funcoid
, stmt
->def
);
103 DropConversionsCommand(DropStmt
*drop
)
105 ObjectAddresses
*objects
;
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
);
121 Form_pg_conversion con
;
122 ObjectAddress object
;
124 conversionOid
= FindConversionByName(name
);
126 if (!OidIsValid(conversionOid
))
128 if (!drop
->missing_ok
)
131 (errcode(ERRCODE_UNDEFINED_OBJECT
),
132 errmsg("conversion \"%s\" does not exist",
133 NameListToString(name
))));
138 (errmsg("conversion \"%s\" does not exist, skipping",
139 NameListToString(name
))));
144 tuple
= SearchSysCache(CONVOID
,
145 ObjectIdGetDatum(conversionOid
),
147 if (!HeapTupleIsValid(tuple
))
148 elog(ERROR
, "cache lookup failed for conversion %u",
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
);
176 RenameConversion(List
*name
, const char *newname
)
184 rel
= heap_open(ConversionRelationId
, RowExclusiveLock
);
186 conversionOid
= FindConversionByName(name
);
187 if (!OidIsValid(conversionOid
))
189 (errcode(ERRCODE_UNDEFINED_OBJECT
),
190 errmsg("conversion \"%s\" does not exist",
191 NameListToString(name
))));
193 tup
= SearchSysCacheCopy(CONVOID
,
194 ObjectIdGetDatum(conversionOid
),
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
),
207 (errcode(ERRCODE_DUPLICATE_OBJECT
),
208 errmsg("conversion \"%s\" already exists in schema \"%s\"",
209 newname
, get_namespace_name(namespaceOid
))));
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
));
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
);
232 * Change conversion owner, by name
235 AlterConversionOwner(List
*name
, Oid newOwnerId
)
240 rel
= heap_open(ConversionRelationId
, RowExclusiveLock
);
242 conversionOid
= FindConversionByName(name
);
243 if (!OidIsValid(conversionOid
))
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
258 AlterConversionOwner_oid(Oid conversionOid
, Oid newOwnerId
)
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.
276 AlterConversionOwner_internal(Relation rel
, Oid conversionOid
, Oid newOwnerId
)
278 Form_pg_conversion convForm
;
281 Assert(RelationGetRelid(rel
) == ConversionRelationId
);
283 tup
= SearchSysCacheCopy(CONVOID
,
284 ObjectIdGetDatum(conversionOid
),
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
)
299 /* Superusers can always do it */
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
,
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
,