]> granicus.if.org Git - postgresql/blob - src/backend/catalog/pg_conversion.c
Update copyright to 2004.
[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-2004, 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.19 2004/08/29 04:12:28 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         ScanKeyInit(&scanKeyData,
180                                 ObjectIdAttributeNumber,
181                                 BTEqualStrategyNumber, F_OIDEQ,
182                                 ObjectIdGetDatum(conversionOid));
183
184         /* open pg_conversion */
185         rel = heap_openr(ConversionRelationName, RowExclusiveLock);
186         tupDesc = rel->rd_att;
187
188         scan = heap_beginscan(rel, SnapshotNow,
189                                                   1, &scanKeyData);
190
191         /* search for the target tuple */
192         if (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection)))
193                 simple_heap_delete(rel, &tuple->t_self);
194         else
195                 elog(ERROR, "could not find tuple for conversion %u", conversionOid);
196         heap_endscan(scan);
197         heap_close(rel, RowExclusiveLock);
198 }
199
200 /*
201  * FindDefaultConversion
202  *
203  * Find "default" conversion proc by for_encoding and to_encoding in the
204  * given namespace.
205  *
206  * If found, returns the procedure's oid, otherwise InvalidOid.  Note that
207  * you get the procedure's OID not the conversion's OID!
208  */
209 Oid
210 FindDefaultConversion(Oid name_space, int32 for_encoding, int32 to_encoding)
211 {
212         CatCList   *catlist;
213         HeapTuple       tuple;
214         Form_pg_conversion body;
215         Oid                     proc = InvalidOid;
216         int                     i;
217
218         catlist = SearchSysCacheList(CONDEFAULT, 3,
219                                                                  ObjectIdGetDatum(name_space),
220                                                                  Int32GetDatum(for_encoding),
221                                                                  Int32GetDatum(to_encoding),
222                                                                  0);
223
224         for (i = 0; i < catlist->n_members; i++)
225         {
226                 tuple = &catlist->members[i]->tuple;
227                 body = (Form_pg_conversion) GETSTRUCT(tuple);
228                 if (body->condefault)
229                 {
230                         proc = body->conproc;
231                         break;
232                 }
233         }
234         ReleaseSysCacheList(catlist);
235         return proc;
236 }
237
238 /*
239  * FindConversion
240  *
241  * Find conversion by namespace and conversion name.
242  * Returns conversion OID.
243  */
244 Oid
245 FindConversion(const char *conname, Oid connamespace)
246 {
247         HeapTuple       tuple;
248         Oid                     procoid;
249         Oid                     conoid;
250         AclResult       aclresult;
251
252         /* search pg_conversion by connamespace and conversion name */
253         tuple = SearchSysCache(CONNAMENSP,
254                                                    PointerGetDatum(conname),
255                                                    ObjectIdGetDatum(connamespace),
256                                                    0, 0);
257         if (!HeapTupleIsValid(tuple))
258                 return InvalidOid;
259
260         procoid = ((Form_pg_conversion) GETSTRUCT(tuple))->conproc;
261         conoid = HeapTupleGetOid(tuple);
262
263         ReleaseSysCache(tuple);
264
265         /* Check we have execute rights for the function */
266         aclresult = pg_proc_aclcheck(procoid, GetUserId(), ACL_EXECUTE);
267         if (aclresult != ACLCHECK_OK)
268                 return InvalidOid;
269
270         return conoid;
271 }
272
273 /*
274  * Execute SQL99's CONVERT function.
275  *
276  * CONVERT <left paren> <character value expression>
277  * USING <form-of-use conversion name> <right paren>
278  *
279  * TEXT convert_using(TEXT string, TEXT conversion_name)
280  */
281 Datum
282 pg_convert_using(PG_FUNCTION_ARGS)
283 {
284         text       *string = PG_GETARG_TEXT_P(0);
285         text       *conv_name = PG_GETARG_TEXT_P(1);
286         text       *retval;
287         List       *parsed_name;
288         Oid                     convoid;
289         HeapTuple       tuple;
290         Form_pg_conversion body;
291         unsigned char *str;
292         unsigned char *result;
293         int                     len;
294
295         /* Convert input string to null-terminated form */
296         len = VARSIZE(string) - VARHDRSZ;
297         str = palloc(len + 1);
298         memcpy(str, VARDATA(string), len);
299         *(str + len) = '\0';
300
301         /* Look up the conversion name */
302         parsed_name = textToQualifiedNameList(conv_name, "convert_using");
303         convoid = FindConversionByName(parsed_name);
304         if (!OidIsValid(convoid))
305                 ereport(ERROR,
306                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
307                                  errmsg("conversion \"%s\" does not exist",
308                                                 NameListToString(parsed_name))));
309
310         tuple = SearchSysCache(CONOID,
311                                                    ObjectIdGetDatum(convoid),
312                                                    0, 0, 0);
313         if (!HeapTupleIsValid(tuple))
314                 elog(ERROR, "cache lookup failed for conversion %u", convoid);
315         body = (Form_pg_conversion) GETSTRUCT(tuple);
316
317         /* Temporary result area should be more than big enough */
318         result = palloc(len * 4 + 1);
319
320         OidFunctionCall5(body->conproc,
321                                          Int32GetDatum(body->conforencoding),
322                                          Int32GetDatum(body->contoencoding),
323                                          CStringGetDatum(str),
324                                          CStringGetDatum(result),
325                                          Int32GetDatum(len));
326
327         ReleaseSysCache(tuple);
328
329         /*
330          * build text result structure. we cannot use textin() here, since
331          * textin assumes that input string encoding is same as database
332          * 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 }