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