]> granicus.if.org Git - postgresql/blob - src/backend/catalog/pg_conversion.c
Move conversion dependency patch to the proper branch, out of 8.1.X,
[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-2006, 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.30 2006/05/30 13:36:30 momjian 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_namespace.h"
22 #include "catalog/pg_proc.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                                  Oid 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 encoding>
72                  * 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_open(ConversionRelationId, 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] = ObjectIdGetDatum(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 = ConversionRelationId;
115         myself.objectId = HeapTupleGetOid(tup);
116         myself.objectSubId = 0;
117
118         /* create dependency on conversion procedure */
119         referenced.classId = ProcedureRelationId;
120         referenced.objectId = conproc;
121         referenced.objectSubId = 0;
122         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
123
124         /* create dependency on owner */
125         recordDependencyOnOwner(ConversionRelationId, HeapTupleGetOid(tup),
126                                                         conowner);
127
128         /* create dependency on namespace */
129         myself.classId = ConversionRelationId;
130         referenced.classId = NamespaceRelationId;
131         referenced.objectId = connamespace;
132         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
133
134         heap_freetuple(tup);
135         heap_close(rel, RowExclusiveLock);
136
137         return oid;
138 }
139
140 /*
141  * ConversionDrop
142  *
143  * Drop a conversion after doing permission checks.
144  */
145 void
146 ConversionDrop(Oid conversionOid, DropBehavior behavior)
147 {
148         HeapTuple       tuple;
149         ObjectAddress object;
150
151         tuple = SearchSysCache(CONOID,
152                                                    ObjectIdGetDatum(conversionOid),
153                                                    0, 0, 0);
154         if (!HeapTupleIsValid(tuple))
155                 elog(ERROR, "cache lookup failed for conversion %u", conversionOid);
156
157         if (!superuser() &&
158                 ((Form_pg_conversion) GETSTRUCT(tuple))->conowner != GetUserId())
159                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION,
160                                   NameStr(((Form_pg_conversion) GETSTRUCT(tuple))->conname));
161
162         ReleaseSysCache(tuple);
163
164         /*
165          * Do the deletion
166          */
167         object.classId = ConversionRelationId;
168         object.objectId = conversionOid;
169         object.objectSubId = 0;
170
171         performDeletion(&object, behavior);
172 }
173
174 /*
175  * RemoveConversionById
176  *
177  * Remove a tuple from pg_conversion by Oid. This function is solely
178  * called inside catalog/dependency.c
179  */
180 void
181 RemoveConversionById(Oid conversionOid)
182 {
183         Relation        rel;
184         HeapTuple       tuple;
185         HeapScanDesc scan;
186         ScanKeyData scanKeyData;
187
188         ScanKeyInit(&scanKeyData,
189                                 ObjectIdAttributeNumber,
190                                 BTEqualStrategyNumber, F_OIDEQ,
191                                 ObjectIdGetDatum(conversionOid));
192
193         /* open pg_conversion */
194         rel = heap_open(ConversionRelationId, RowExclusiveLock);
195
196         scan = heap_beginscan(rel, SnapshotNow,
197                                                   1, &scanKeyData);
198
199         /* search for the target tuple */
200         if (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection)))
201                 simple_heap_delete(rel, &tuple->t_self);
202         else
203                 elog(ERROR, "could not find tuple for conversion %u", conversionOid);
204         heap_endscan(scan);
205         heap_close(rel, RowExclusiveLock);
206 }
207
208 /*
209  * FindDefaultConversion
210  *
211  * Find "default" conversion proc by for_encoding and to_encoding in the
212  * given namespace.
213  *
214  * If found, returns the procedure's oid, otherwise InvalidOid.  Note that
215  * you get the procedure's OID not the conversion's OID!
216  */
217 Oid
218 FindDefaultConversion(Oid name_space, int32 for_encoding, int32 to_encoding)
219 {
220         CatCList   *catlist;
221         HeapTuple       tuple;
222         Form_pg_conversion body;
223         Oid                     proc = InvalidOid;
224         int                     i;
225
226         catlist = SearchSysCacheList(CONDEFAULT, 3,
227                                                                  ObjectIdGetDatum(name_space),
228                                                                  Int32GetDatum(for_encoding),
229                                                                  Int32GetDatum(to_encoding),
230                                                                  0);
231
232         for (i = 0; i < catlist->n_members; i++)
233         {
234                 tuple = &catlist->members[i]->tuple;
235                 body = (Form_pg_conversion) GETSTRUCT(tuple);
236                 if (body->condefault)
237                 {
238                         proc = body->conproc;
239                         break;
240                 }
241         }
242         ReleaseSysCacheList(catlist);
243         return proc;
244 }
245
246 /*
247  * FindConversion
248  *
249  * Find conversion by namespace and conversion name.
250  * Returns conversion OID.
251  */
252 Oid
253 FindConversion(const char *conname, Oid connamespace)
254 {
255         HeapTuple       tuple;
256         Oid                     procoid;
257         Oid                     conoid;
258         AclResult       aclresult;
259
260         /* search pg_conversion by connamespace and conversion name */
261         tuple = SearchSysCache(CONNAMENSP,
262                                                    PointerGetDatum(conname),
263                                                    ObjectIdGetDatum(connamespace),
264                                                    0, 0);
265         if (!HeapTupleIsValid(tuple))
266                 return InvalidOid;
267
268         procoid = ((Form_pg_conversion) GETSTRUCT(tuple))->conproc;
269         conoid = HeapTupleGetOid(tuple);
270
271         ReleaseSysCache(tuple);
272
273         /* Check we have execute rights for the function */
274         aclresult = pg_proc_aclcheck(procoid, GetUserId(), ACL_EXECUTE);
275         if (aclresult != ACLCHECK_OK)
276                 return InvalidOid;
277
278         return conoid;
279 }
280
281 /*
282  * Execute SQL99's CONVERT function.
283  *
284  * CONVERT <left paren> <character value expression>
285  * USING <form-of-use conversion name> <right paren>
286  *
287  * TEXT convert_using(TEXT string, TEXT conversion_name)
288  */
289 Datum
290 pg_convert_using(PG_FUNCTION_ARGS)
291 {
292         text       *string = PG_GETARG_TEXT_P(0);
293         text       *conv_name = PG_GETARG_TEXT_P(1);
294         text       *retval;
295         List       *parsed_name;
296         Oid                     convoid;
297         HeapTuple       tuple;
298         Form_pg_conversion body;
299         char       *str;
300         char       *result;
301         int                     len;
302
303         /* Convert input string to null-terminated form */
304         len = VARSIZE(string) - VARHDRSZ;
305         str = palloc(len + 1);
306         memcpy(str, VARDATA(string), len);
307         *(str + len) = '\0';
308
309         /* Look up the conversion name */
310         parsed_name = textToQualifiedNameList(conv_name);
311         convoid = FindConversionByName(parsed_name);
312         if (!OidIsValid(convoid))
313                 ereport(ERROR,
314                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
315                                  errmsg("conversion \"%s\" does not exist",
316                                                 NameListToString(parsed_name))));
317
318         tuple = SearchSysCache(CONOID,
319                                                    ObjectIdGetDatum(convoid),
320                                                    0, 0, 0);
321         if (!HeapTupleIsValid(tuple))
322                 elog(ERROR, "cache lookup failed for conversion %u", convoid);
323         body = (Form_pg_conversion) GETSTRUCT(tuple);
324
325         /* Temporary result area should be more than big enough */
326         result = palloc(len * 4 + 1);
327
328         OidFunctionCall5(body->conproc,
329                                          Int32GetDatum(body->conforencoding),
330                                          Int32GetDatum(body->contoencoding),
331                                          CStringGetDatum(str),
332                                          CStringGetDatum(result),
333                                          Int32GetDatum(len));
334
335         ReleaseSysCache(tuple);
336
337         /*
338          * build text result structure. we cannot use textin() here, since textin
339          * assumes that input string encoding is same as database encoding.
340          */
341         len = strlen(result) + VARHDRSZ;
342         retval = palloc(len);
343         VARATT_SIZEP(retval) = len;
344         memcpy(VARDATA(retval), result, len - VARHDRSZ);
345
346         pfree(result);
347         pfree(str);
348
349         PG_RETURN_TEXT_P(retval);
350 }