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/syscache.h"
32 static void AlterConversionOwner_internal(Relation rel
, Oid conversionOid
,
39 CreateConversionCommand(CreateConversionStmt
*stmt
)
42 char *conversion_name
;
47 const char *from_encoding_name
= stmt
->for_encoding_name
;
48 const char *to_encoding_name
= stmt
->to_encoding_name
;
49 List
*func_name
= stmt
->func_name
;
50 static Oid funcargs
[] = {INT4OID
, INT4OID
, CSTRINGOID
, INTERNALOID
, INT4OID
};
52 /* Convert list of names to a name and namespace */
53 namespaceId
= QualifiedNameGetCreationNamespace(stmt
->conversion_name
,
56 /* Check we have creation rights in target namespace */
57 aclresult
= pg_namespace_aclcheck(namespaceId
, GetUserId(), ACL_CREATE
);
58 if (aclresult
!= ACLCHECK_OK
)
59 aclcheck_error(aclresult
, ACL_KIND_NAMESPACE
,
60 get_namespace_name(namespaceId
));
62 /* Check the encoding names */
63 from_encoding
= pg_char_to_encoding(from_encoding_name
);
64 if (from_encoding
< 0)
66 (errcode(ERRCODE_UNDEFINED_OBJECT
),
67 errmsg("source encoding \"%s\" does not exist",
68 from_encoding_name
)));
70 to_encoding
= pg_char_to_encoding(to_encoding_name
);
73 (errcode(ERRCODE_UNDEFINED_OBJECT
),
74 errmsg("destination encoding \"%s\" does not exist",
78 * Check the existence of the conversion function. Function name could be
81 funcoid
= LookupFuncName(func_name
, sizeof(funcargs
) / sizeof(Oid
),
84 /* Check we have EXECUTE rights for the function */
85 aclresult
= pg_proc_aclcheck(funcoid
, GetUserId(), ACL_EXECUTE
);
86 if (aclresult
!= ACLCHECK_OK
)
87 aclcheck_error(aclresult
, ACL_KIND_PROC
,
88 NameListToString(func_name
));
91 * All seem ok, go ahead (possible failure would be a duplicate conversion
94 ConversionCreate(conversion_name
, namespaceId
, GetUserId(),
95 from_encoding
, to_encoding
, funcoid
, stmt
->def
);
102 DropConversionsCommand(DropStmt
*drop
)
104 ObjectAddresses
*objects
;
108 * First we identify all the conversions, then we delete them in a single
109 * performMultipleDeletions() call. This is to avoid unwanted
110 * DROP RESTRICT errors if one of the conversions depends on another.
111 * (Not that that is very likely, but we may as well do this consistently.)
113 objects
= new_object_addresses();
115 foreach(cell
, drop
->objects
)
117 List
*name
= (List
*) lfirst(cell
);
120 Form_pg_conversion con
;
121 ObjectAddress object
;
123 conversionOid
= FindConversionByName(name
);
125 if (!OidIsValid(conversionOid
))
127 if (!drop
->missing_ok
)
130 (errcode(ERRCODE_UNDEFINED_OBJECT
),
131 errmsg("conversion \"%s\" does not exist",
132 NameListToString(name
))));
137 (errmsg("conversion \"%s\" does not exist, skipping",
138 NameListToString(name
))));
143 tuple
= SearchSysCache(CONVOID
,
144 ObjectIdGetDatum(conversionOid
),
146 if (!HeapTupleIsValid(tuple
))
147 elog(ERROR
, "cache lookup failed for conversion %u",
149 con
= (Form_pg_conversion
) GETSTRUCT(tuple
);
151 /* Permission check: must own conversion or its namespace */
152 if (!pg_conversion_ownercheck(conversionOid
, GetUserId()) &&
153 !pg_namespace_ownercheck(con
->connamespace
, GetUserId()))
154 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_CONVERSION
,
155 NameStr(con
->conname
));
157 object
.classId
= ConversionRelationId
;
158 object
.objectId
= conversionOid
;
159 object
.objectSubId
= 0;
161 add_exact_object_address(&object
, objects
);
163 ReleaseSysCache(tuple
);
166 performMultipleDeletions(objects
, drop
->behavior
);
168 free_object_addresses(objects
);
175 RenameConversion(List
*name
, const char *newname
)
183 rel
= heap_open(ConversionRelationId
, RowExclusiveLock
);
185 conversionOid
= FindConversionByName(name
);
186 if (!OidIsValid(conversionOid
))
188 (errcode(ERRCODE_UNDEFINED_OBJECT
),
189 errmsg("conversion \"%s\" does not exist",
190 NameListToString(name
))));
192 tup
= SearchSysCacheCopy(CONVOID
,
193 ObjectIdGetDatum(conversionOid
),
195 if (!HeapTupleIsValid(tup
)) /* should not happen */
196 elog(ERROR
, "cache lookup failed for conversion %u", conversionOid
);
198 namespaceOid
= ((Form_pg_conversion
) GETSTRUCT(tup
))->connamespace
;
200 /* make sure the new name doesn't exist */
201 if (SearchSysCacheExists(CONNAMENSP
,
202 CStringGetDatum(newname
),
203 ObjectIdGetDatum(namespaceOid
),
206 (errcode(ERRCODE_DUPLICATE_OBJECT
),
207 errmsg("conversion \"%s\" already exists in schema \"%s\"",
208 newname
, get_namespace_name(namespaceOid
))));
211 if (!pg_conversion_ownercheck(conversionOid
, GetUserId()))
212 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_CONVERSION
,
213 NameListToString(name
));
215 /* must have CREATE privilege on namespace */
216 aclresult
= pg_namespace_aclcheck(namespaceOid
, GetUserId(), ACL_CREATE
);
217 if (aclresult
!= ACLCHECK_OK
)
218 aclcheck_error(aclresult
, ACL_KIND_NAMESPACE
,
219 get_namespace_name(namespaceOid
));
222 namestrcpy(&(((Form_pg_conversion
) GETSTRUCT(tup
))->conname
), newname
);
223 simple_heap_update(rel
, &tup
->t_self
, tup
);
224 CatalogUpdateIndexes(rel
, tup
);
226 heap_close(rel
, NoLock
);
231 * Change conversion owner, by name
234 AlterConversionOwner(List
*name
, Oid newOwnerId
)
239 rel
= heap_open(ConversionRelationId
, RowExclusiveLock
);
241 conversionOid
= FindConversionByName(name
);
242 if (!OidIsValid(conversionOid
))
244 (errcode(ERRCODE_UNDEFINED_OBJECT
),
245 errmsg("conversion \"%s\" does not exist",
246 NameListToString(name
))));
248 AlterConversionOwner_internal(rel
, conversionOid
, newOwnerId
);
250 heap_close(rel
, NoLock
);
254 * Change conversion owner, by oid
257 AlterConversionOwner_oid(Oid conversionOid
, Oid newOwnerId
)
261 rel
= heap_open(ConversionRelationId
, RowExclusiveLock
);
263 AlterConversionOwner_internal(rel
, conversionOid
, newOwnerId
);
265 heap_close(rel
, NoLock
);
269 * AlterConversionOwner_internal
271 * Internal routine for changing the owner. rel must be pg_conversion, already
272 * open and suitably locked; it will not be closed.
275 AlterConversionOwner_internal(Relation rel
, Oid conversionOid
, Oid newOwnerId
)
277 Form_pg_conversion convForm
;
280 Assert(RelationGetRelid(rel
) == ConversionRelationId
);
282 tup
= SearchSysCacheCopy(CONVOID
,
283 ObjectIdGetDatum(conversionOid
),
285 if (!HeapTupleIsValid(tup
)) /* should not happen */
286 elog(ERROR
, "cache lookup failed for conversion %u", conversionOid
);
288 convForm
= (Form_pg_conversion
) GETSTRUCT(tup
);
291 * If the new owner is the same as the existing owner, consider the
292 * command to have succeeded. This is for dump restoration purposes.
294 if (convForm
->conowner
!= newOwnerId
)
298 /* Superusers can always do it */
301 /* Otherwise, must be owner of the existing object */
302 if (!pg_conversion_ownercheck(HeapTupleGetOid(tup
), GetUserId()))
303 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_CONVERSION
,
304 NameStr(convForm
->conname
));
306 /* Must be able to become new owner */
307 check_is_member_of_role(GetUserId(), newOwnerId
);
309 /* New owner must have CREATE privilege on namespace */
310 aclresult
= pg_namespace_aclcheck(convForm
->connamespace
,
313 if (aclresult
!= ACLCHECK_OK
)
314 aclcheck_error(aclresult
, ACL_KIND_NAMESPACE
,
315 get_namespace_name(convForm
->connamespace
));
319 * Modify the owner --- okay to scribble on tup because it's a copy
321 convForm
->conowner
= newOwnerId
;
323 simple_heap_update(rel
, &tup
->t_self
, tup
);
325 CatalogUpdateIndexes(rel
, tup
);
327 /* Update owner dependency reference */
328 changeDependencyOnOwner(ConversionRelationId
, conversionOid
,