]> granicus.if.org Git - postgresql/blob - src/backend/catalog/pg_conversion.c
pgindent run.
[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-2002, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/catalog/pg_conversion.c,v 1.14 2003/08/04 00:43:16 momjian Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include "access/heapam.h"
18 #include "catalog/catname.h"
19 #include "catalog/dependency.h"
20 #include "catalog/indexing.h"
21 #include "catalog/pg_class.h"
22 #include "catalog/pg_conversion.h"
23 #include "catalog/namespace.h"
24 #include "utils/builtins.h"
25 #include "utils/lsyscache.h"
26 #include "utils/syscache.h"
27 #include "utils/catcache.h"
28 #include "mb/pg_wchar.h"
29 #include "utils/fmgroids.h"
30 #include "utils/acl.h"
31 #include "miscadmin.h"
32
33 /*
34  * ConversionCreate
35  *
36  * Add a new tuple to pg_conversion.
37  */
38 Oid
39 ConversionCreate(const char *conname, Oid connamespace,
40                                  AclId conowner,
41                                  int32 conforencoding, int32 contoencoding,
42                                  Oid conproc, bool def)
43 {
44         int                     i;
45         Relation        rel;
46         TupleDesc       tupDesc;
47         HeapTuple       tup;
48         char            nulls[Natts_pg_conversion];
49         Datum           values[Natts_pg_conversion];
50         NameData        cname;
51         Oid                     oid;
52         ObjectAddress myself,
53                                 referenced;
54
55         /* sanity checks */
56         if (!conname)
57                 elog(ERROR, "no conversion name supplied");
58
59         /* make sure there is no existing conversion of same name */
60         if (SearchSysCacheExists(CONNAMENSP,
61                                                          PointerGetDatum(conname),
62                                                          ObjectIdGetDatum(connamespace),
63                                                          0, 0))
64                 ereport(ERROR,
65                                 (errcode(ERRCODE_DUPLICATE_OBJECT),
66                                  errmsg("conversion \"%s\" already exists", conname)));
67
68         if (def)
69         {
70                 /*
71                  * make sure there is no existing default <for encoding><to
72                  * encoding> pair in this name space
73                  */
74                 if (FindDefaultConversion(connamespace,
75                                                                   conforencoding,
76                                                                   contoencoding))
77                         ereport(ERROR,
78                                         (errcode(ERRCODE_DUPLICATE_OBJECT),
79                                  errmsg("default conversion for %s to %s already exists",
80                                                 pg_encoding_to_char(conforencoding),
81                                                 pg_encoding_to_char(contoencoding))));
82         }
83
84         /* open pg_conversion */
85         rel = heap_openr(ConversionRelationName, RowExclusiveLock);
86         tupDesc = rel->rd_att;
87
88         /* initialize nulls and values */
89         for (i = 0; i < Natts_pg_conversion; i++)
90         {
91                 nulls[i] = ' ';
92                 values[i] = (Datum) NULL;
93         }
94
95         /* form a tuple */
96         namestrcpy(&cname, conname);
97         values[Anum_pg_conversion_conname - 1] = NameGetDatum(&cname);
98         values[Anum_pg_conversion_connamespace - 1] = ObjectIdGetDatum(connamespace);
99         values[Anum_pg_conversion_conowner - 1] = Int32GetDatum(conowner);
100         values[Anum_pg_conversion_conforencoding - 1] = Int32GetDatum(conforencoding);
101         values[Anum_pg_conversion_contoencoding - 1] = Int32GetDatum(contoencoding);
102         values[Anum_pg_conversion_conproc - 1] = ObjectIdGetDatum(conproc);
103         values[Anum_pg_conversion_condefault - 1] = BoolGetDatum(def);
104
105         tup = heap_formtuple(tupDesc, values, nulls);
106
107         /* insert a new tuple */
108         oid = simple_heap_insert(rel, tup);
109         Assert(OidIsValid(oid));
110
111         /* update the index if any */
112         CatalogUpdateIndexes(rel, tup);
113
114         myself.classId = RelationGetRelid(rel);
115         myself.objectId = HeapTupleGetOid(tup);
116         myself.objectSubId = 0;
117
118         /* create dependency on conversion procedure */
119         referenced.classId = RelOid_pg_proc;
120         referenced.objectId = conproc;
121         referenced.objectSubId = 0;
122         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
123
124         heap_freetuple(tup);
125         heap_close(rel, RowExclusiveLock);
126
127         return oid;
128 }
129
130 /*
131  * ConversionDrop
132  *
133  * Drop a conversion after doing permission checks.
134  */
135 void
136 ConversionDrop(Oid conversionOid, DropBehavior behavior)
137 {
138         HeapTuple       tuple;
139         ObjectAddress object;
140
141         tuple = SearchSysCache(CONOID,
142                                                    ObjectIdGetDatum(conversionOid),
143                                                    0, 0, 0);
144         if (!HeapTupleIsValid(tuple))
145                 elog(ERROR, "cache lookup failed for conversion %u", conversionOid);
146
147         if (!superuser() &&
148                 ((Form_pg_conversion) GETSTRUCT(tuple))->conowner != GetUserId())
149                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION,
150                           NameStr(((Form_pg_conversion) GETSTRUCT(tuple))->conname));
151
152         ReleaseSysCache(tuple);
153
154         /*
155          * Do the deletion
156          */
157         object.classId = get_system_catalog_relid(ConversionRelationName);
158         object.objectId = conversionOid;
159         object.objectSubId = 0;
160
161         performDeletion(&object, behavior);
162 }
163
164 /*
165  * RemoveConversionById
166  *
167  * Remove a tuple from pg_conversion by Oid. This function is solely
168  * called inside catalog/dependency.c
169  */
170 void
171 RemoveConversionById(Oid conversionOid)
172 {
173         Relation        rel;
174         TupleDesc       tupDesc;
175         HeapTuple       tuple;
176         HeapScanDesc scan;
177         ScanKeyData scanKeyData;
178
179         ScanKeyEntryInitialize(&scanKeyData,
180                                                    0,
181                                                    ObjectIdAttributeNumber,
182                                                    F_OIDEQ,
183                                                    ObjectIdGetDatum(conversionOid));
184
185         /* open pg_conversion */
186         rel = heap_openr(ConversionRelationName, RowExclusiveLock);
187         tupDesc = rel->rd_att;
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         unsigned char *str;
293         unsigned 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, "convert_using");
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
332          * textin assumes that input string encoding is same as database
333          * encoding.
334          */
335         len = strlen(result) + VARHDRSZ;
336         retval = palloc(len);
337         VARATT_SIZEP(retval) = len;
338         memcpy(VARDATA(retval), result, len - VARHDRSZ);
339
340         pfree(result);
341         pfree(str);
342
343         PG_RETURN_TEXT_P(retval);
344 }