]> granicus.if.org Git - postgresql/blob - src/backend/catalog/pg_conversion.c
d0ec8c699f4bb22b67817e449f05f37387b29404
[postgresql] / src / backend / catalog / pg_conversion.c
1 /*-------------------------------------------------------------------------
2  *
3  * pg_conversion.c
4  *        routines to support manipulation of the pg_conversion relation
5  *
6  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $PostgreSQL: pgsql/src/backend/catalog/pg_conversion.c,v 1.28 2005/10/18 01:06:23 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_proc.h"
22 #include "catalog/namespace.h"
23 #include "utils/builtins.h"
24 #include "utils/lsyscache.h"
25 #include "utils/syscache.h"
26 #include "utils/catcache.h"
27 #include "mb/pg_wchar.h"
28 #include "utils/fmgroids.h"
29 #include "utils/acl.h"
30 #include "miscadmin.h"
31
32 /*
33  * ConversionCreate
34  *
35  * Add a new tuple to pg_conversion.
36  */
37 Oid
38 ConversionCreate(const char *conname, Oid connamespace,
39                                  Oid conowner,
40                                  int32 conforencoding, int32 contoencoding,
41                                  Oid conproc, bool def)
42 {
43         int                     i;
44         Relation        rel;
45         TupleDesc       tupDesc;
46         HeapTuple       tup;
47         char            nulls[Natts_pg_conversion];
48         Datum           values[Natts_pg_conversion];
49         NameData        cname;
50         Oid                     oid;
51         ObjectAddress myself,
52                                 referenced;
53
54         /* sanity checks */
55         if (!conname)
56                 elog(ERROR, "no conversion name supplied");
57
58         /* make sure there is no existing conversion of same name */
59         if (SearchSysCacheExists(CONNAMENSP,
60                                                          PointerGetDatum(conname),
61                                                          ObjectIdGetDatum(connamespace),
62                                                          0, 0))
63                 ereport(ERROR,
64                                 (errcode(ERRCODE_DUPLICATE_OBJECT),
65                                  errmsg("conversion \"%s\" already exists", conname)));
66
67         if (def)
68         {
69                 /*
70                  * make sure there is no existing default <for encoding><to encoding>
71                  * pair in this name space
72                  */
73                 if (FindDefaultConversion(connamespace,
74                                                                   conforencoding,
75                                                                   contoencoding))
76                         ereport(ERROR,
77                                         (errcode(ERRCODE_DUPLICATE_OBJECT),
78                                          errmsg("default conversion for %s to %s already exists",
79                                                         pg_encoding_to_char(conforencoding),
80                                                         pg_encoding_to_char(contoencoding))));
81         }
82
83         /* open pg_conversion */
84         rel = heap_open(ConversionRelationId, RowExclusiveLock);
85         tupDesc = rel->rd_att;
86
87         /* initialize nulls and values */
88         for (i = 0; i < Natts_pg_conversion; i++)
89         {
90                 nulls[i] = ' ';
91                 values[i] = (Datum) NULL;
92         }
93
94         /* form a tuple */
95         namestrcpy(&cname, conname);
96         values[Anum_pg_conversion_conname - 1] = NameGetDatum(&cname);
97         values[Anum_pg_conversion_connamespace - 1] = ObjectIdGetDatum(connamespace);
98         values[Anum_pg_conversion_conowner - 1] = ObjectIdGetDatum(conowner);
99         values[Anum_pg_conversion_conforencoding - 1] = Int32GetDatum(conforencoding);
100         values[Anum_pg_conversion_contoencoding - 1] = Int32GetDatum(contoencoding);
101         values[Anum_pg_conversion_conproc - 1] = ObjectIdGetDatum(conproc);
102         values[Anum_pg_conversion_condefault - 1] = BoolGetDatum(def);
103
104         tup = heap_formtuple(tupDesc, values, nulls);
105
106         /* insert a new tuple */
107         oid = simple_heap_insert(rel, tup);
108         Assert(OidIsValid(oid));
109
110         /* update the index if any */
111         CatalogUpdateIndexes(rel, tup);
112
113         myself.classId = ConversionRelationId;
114         myself.objectId = HeapTupleGetOid(tup);
115         myself.objectSubId = 0;
116
117         /* create dependency on conversion procedure */
118         referenced.classId = ProcedureRelationId;
119         referenced.objectId = conproc;
120         referenced.objectSubId = 0;
121         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
122
123         /* create dependency on owner */
124         recordDependencyOnOwner(ConversionRelationId, HeapTupleGetOid(tup),
125                                                         conowner);
126
127         heap_freetuple(tup);
128         heap_close(rel, RowExclusiveLock);
129
130         return oid;
131 }
132
133 /*
134  * ConversionDrop
135  *
136  * Drop a conversion after doing permission checks.
137  */
138 void
139 ConversionDrop(Oid conversionOid, DropBehavior behavior)
140 {
141         HeapTuple       tuple;
142         ObjectAddress object;
143
144         tuple = SearchSysCache(CONOID,
145                                                    ObjectIdGetDatum(conversionOid),
146                                                    0, 0, 0);
147         if (!HeapTupleIsValid(tuple))
148                 elog(ERROR, "cache lookup failed for conversion %u", conversionOid);
149
150         if (!superuser() &&
151                 ((Form_pg_conversion) GETSTRUCT(tuple))->conowner != GetUserId())
152                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION,
153                                   NameStr(((Form_pg_conversion) GETSTRUCT(tuple))->conname));
154
155         ReleaseSysCache(tuple);
156
157         /*
158          * Do the deletion
159          */
160         object.classId = ConversionRelationId;
161         object.objectId = conversionOid;
162         object.objectSubId = 0;
163
164         performDeletion(&object, behavior);
165 }
166
167 /*
168  * RemoveConversionById
169  *
170  * Remove a tuple from pg_conversion by Oid. This function is solely
171  * called inside catalog/dependency.c
172  */
173 void
174 RemoveConversionById(Oid conversionOid)
175 {
176         Relation        rel;
177         HeapTuple       tuple;
178         HeapScanDesc scan;
179         ScanKeyData scanKeyData;
180
181         ScanKeyInit(&scanKeyData,
182                                 ObjectIdAttributeNumber,
183                                 BTEqualStrategyNumber, F_OIDEQ,
184                                 ObjectIdGetDatum(conversionOid));
185
186         /* open pg_conversion */
187         rel = heap_open(ConversionRelationId, RowExclusiveLock);
188
189         scan = heap_beginscan(rel, SnapshotNow,
190                                                   1, &scanKeyData);
191
192         /* search for the target tuple */
193         if (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection)))
194                 simple_heap_delete(rel, &tuple->t_self);
195         else
196                 elog(ERROR, "could not find tuple for conversion %u", conversionOid);
197         heap_endscan(scan);
198         heap_close(rel, RowExclusiveLock);
199 }
200
201 /*
202  * FindDefaultConversion
203  *
204  * Find "default" conversion proc by for_encoding and to_encoding in the
205  * given namespace.
206  *
207  * If found, returns the procedure's oid, otherwise InvalidOid.  Note that
208  * you get the procedure's OID not the conversion's OID!
209  */
210 Oid
211 FindDefaultConversion(Oid name_space, int32 for_encoding, int32 to_encoding)
212 {
213         CatCList   *catlist;
214         HeapTuple       tuple;
215         Form_pg_conversion body;
216         Oid                     proc = InvalidOid;
217         int                     i;
218
219         catlist = SearchSysCacheList(CONDEFAULT, 3,
220                                                                  ObjectIdGetDatum(name_space),
221                                                                  Int32GetDatum(for_encoding),
222                                                                  Int32GetDatum(to_encoding),
223                                                                  0);
224
225         for (i = 0; i < catlist->n_members; i++)
226         {
227                 tuple = &catlist->members[i]->tuple;
228                 body = (Form_pg_conversion) GETSTRUCT(tuple);
229                 if (body->condefault)
230                 {
231                         proc = body->conproc;
232                         break;
233                 }
234         }
235         ReleaseSysCacheList(catlist);
236         return proc;
237 }
238
239 /*
240  * FindConversion
241  *
242  * Find conversion by namespace and conversion name.
243  * Returns conversion OID.
244  */
245 Oid
246 FindConversion(const char *conname, Oid connamespace)
247 {
248         HeapTuple       tuple;
249         Oid                     procoid;
250         Oid                     conoid;
251         AclResult       aclresult;
252
253         /* search pg_conversion by connamespace and conversion name */
254         tuple = SearchSysCache(CONNAMENSP,
255                                                    PointerGetDatum(conname),
256                                                    ObjectIdGetDatum(connamespace),
257                                                    0, 0);
258         if (!HeapTupleIsValid(tuple))
259                 return InvalidOid;
260
261         procoid = ((Form_pg_conversion) GETSTRUCT(tuple))->conproc;
262         conoid = HeapTupleGetOid(tuple);
263
264         ReleaseSysCache(tuple);
265
266         /* Check we have execute rights for the function */
267         aclresult = pg_proc_aclcheck(procoid, GetUserId(), ACL_EXECUTE);
268         if (aclresult != ACLCHECK_OK)
269                 return InvalidOid;
270
271         return conoid;
272 }
273
274 /*
275  * Execute SQL99's CONVERT function.
276  *
277  * CONVERT <left paren> <character value expression>
278  * USING <form-of-use conversion name> <right paren>
279  *
280  * TEXT convert_using(TEXT string, TEXT conversion_name)
281  */
282 Datum
283 pg_convert_using(PG_FUNCTION_ARGS)
284 {
285         text       *string = PG_GETARG_TEXT_P(0);
286         text       *conv_name = PG_GETARG_TEXT_P(1);
287         text       *retval;
288         List       *parsed_name;
289         Oid                     convoid;
290         HeapTuple       tuple;
291         Form_pg_conversion body;
292         char       *str;
293         char       *result;
294         int                     len;
295
296         /* Convert input string to null-terminated form */
297         len = VARSIZE(string) - VARHDRSZ;
298         str = palloc(len + 1);
299         memcpy(str, VARDATA(string), len);
300         *(str + len) = '\0';
301
302         /* Look up the conversion name */
303         parsed_name = textToQualifiedNameList(conv_name);
304         convoid = FindConversionByName(parsed_name);
305         if (!OidIsValid(convoid))
306                 ereport(ERROR,
307                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
308                                  errmsg("conversion \"%s\" does not exist",
309                                                 NameListToString(parsed_name))));
310
311         tuple = SearchSysCache(CONOID,
312                                                    ObjectIdGetDatum(convoid),
313                                                    0, 0, 0);
314         if (!HeapTupleIsValid(tuple))
315                 elog(ERROR, "cache lookup failed for conversion %u", convoid);
316         body = (Form_pg_conversion) GETSTRUCT(tuple);
317
318         /* Temporary result area should be more than big enough */
319         result = palloc(len * 4 + 1);
320
321         OidFunctionCall5(body->conproc,
322                                          Int32GetDatum(body->conforencoding),
323                                          Int32GetDatum(body->contoencoding),
324                                          CStringGetDatum(str),
325                                          CStringGetDatum(result),
326                                          Int32GetDatum(len));
327
328         ReleaseSysCache(tuple);
329
330         /*
331          * build text result structure. we cannot use textin() here, since textin
332          * assumes that input string encoding is same as database encoding.
333          */
334         len = strlen(result) + VARHDRSZ;
335         retval = palloc(len);
336         VARATT_SIZEP(retval) = len;
337         memcpy(VARDATA(retval), result, len - VARHDRSZ);
338
339         pfree(result);
340         pfree(str);
341
342         PG_RETURN_TEXT_P(retval);
343 }