]> granicus.if.org Git - postgresql/blob - src/backend/commands/proclang.c
Fix reporting of violations in ExecConstraints, again.
[postgresql] / src / backend / commands / proclang.c
1 /*-------------------------------------------------------------------------
2  *
3  * proclang.c
4  *        PostgreSQL PROCEDURAL LANGUAGE support code.
5  *
6  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  * IDENTIFICATION
10  *        src/backend/commands/proclang.c
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15
16 #include "access/genam.h"
17 #include "access/heapam.h"
18 #include "access/htup_details.h"
19 #include "catalog/dependency.h"
20 #include "catalog/indexing.h"
21 #include "catalog/objectaccess.h"
22 #include "catalog/pg_authid.h"
23 #include "catalog/pg_language.h"
24 #include "catalog/pg_namespace.h"
25 #include "catalog/pg_pltemplate.h"
26 #include "catalog/pg_proc.h"
27 #include "catalog/pg_proc_fn.h"
28 #include "catalog/pg_type.h"
29 #include "commands/dbcommands.h"
30 #include "commands/defrem.h"
31 #include "commands/proclang.h"
32 #include "miscadmin.h"
33 #include "parser/parse_func.h"
34 #include "parser/parser.h"
35 #include "utils/acl.h"
36 #include "utils/builtins.h"
37 #include "utils/fmgroids.h"
38 #include "utils/lsyscache.h"
39 #include "utils/rel.h"
40 #include "utils/syscache.h"
41 #include "utils/tqual.h"
42
43
44 typedef struct
45 {
46         bool            tmpltrusted;    /* trusted? */
47         bool            tmpldbacreate;  /* db owner allowed to create? */
48         char       *tmplhandler;        /* name of handler function */
49         char       *tmplinline;         /* name of anonymous-block handler, or NULL */
50         char       *tmplvalidator;      /* name of validator function, or NULL */
51         char       *tmpllibrary;        /* path of shared library */
52 } PLTemplate;
53
54 static ObjectAddress create_proc_lang(const char *languageName, bool replace,
55                                  Oid languageOwner, Oid handlerOid, Oid inlineOid,
56                                  Oid valOid, bool trusted);
57 static PLTemplate *find_language_template(const char *languageName);
58
59 /* ---------------------------------------------------------------------
60  * CREATE PROCEDURAL LANGUAGE
61  * ---------------------------------------------------------------------
62  */
63 ObjectAddress
64 CreateProceduralLanguage(CreatePLangStmt *stmt)
65 {
66         PLTemplate *pltemplate;
67         ObjectAddress tmpAddr;
68         Oid                     handlerOid,
69                                 inlineOid,
70                                 valOid;
71         Oid                     funcrettype;
72         Oid                     funcargtypes[1];
73
74         /*
75          * If we have template information for the language, ignore the supplied
76          * parameters (if any) and use the template information.
77          */
78         if ((pltemplate = find_language_template(stmt->plname)) != NULL)
79         {
80                 List       *funcname;
81
82                 /*
83                  * Give a notice if we are ignoring supplied parameters.
84                  */
85                 if (stmt->plhandler)
86                         ereport(NOTICE,
87                                         (errmsg("using pg_pltemplate information instead of CREATE LANGUAGE parameters")));
88
89                 /*
90                  * Check permission
91                  */
92                 if (!superuser())
93                 {
94                         if (!pltemplate->tmpldbacreate)
95                                 ereport(ERROR,
96                                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
97                                                  errmsg("must be superuser to create procedural language \"%s\"",
98                                                                 stmt->plname)));
99                         if (!pg_database_ownercheck(MyDatabaseId, GetUserId()))
100                                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
101                                                            get_database_name(MyDatabaseId));
102                 }
103
104                 /*
105                  * Find or create the handler function, which we force to be in the
106                  * pg_catalog schema.  If already present, it must have the correct
107                  * return type.
108                  */
109                 funcname = SystemFuncName(pltemplate->tmplhandler);
110                 handlerOid = LookupFuncName(funcname, 0, funcargtypes, true);
111                 if (OidIsValid(handlerOid))
112                 {
113                         funcrettype = get_func_rettype(handlerOid);
114                         if (funcrettype != LANGUAGE_HANDLEROID)
115                                 ereport(ERROR,
116                                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
117                                                  errmsg("function %s must return type %s",
118                                                    NameListToString(funcname), "language_handler")));
119                 }
120                 else
121                 {
122                         tmpAddr = ProcedureCreate(pltemplate->tmplhandler,
123                                                                           PG_CATALOG_NAMESPACE,
124                                                                           false,        /* replace */
125                                                                           false,        /* returnsSet */
126                                                                           LANGUAGE_HANDLEROID,
127                                                                           BOOTSTRAP_SUPERUSERID,
128                                                                           ClanguageId,
129                                                                           F_FMGR_C_VALIDATOR,
130                                                                           pltemplate->tmplhandler,
131                                                                           pltemplate->tmpllibrary,
132                                                                           false,        /* isAgg */
133                                                                           false,        /* isWindowFunc */
134                                                                           false,        /* security_definer */
135                                                                           false,        /* isLeakProof */
136                                                                           false,        /* isStrict */
137                                                                           PROVOLATILE_VOLATILE,
138                                                                           PROPARALLEL_UNSAFE,
139                                                                           buildoidvector(funcargtypes, 0),
140                                                                           PointerGetDatum(NULL),
141                                                                           PointerGetDatum(NULL),
142                                                                           PointerGetDatum(NULL),
143                                                                           NIL,
144                                                                           PointerGetDatum(NULL),
145                                                                           PointerGetDatum(NULL),
146                                                                           1,
147                                                                           0);
148                         handlerOid = tmpAddr.objectId;
149                 }
150
151                 /*
152                  * Likewise for the anonymous block handler, if required; but we don't
153                  * care about its return type.
154                  */
155                 if (pltemplate->tmplinline)
156                 {
157                         funcname = SystemFuncName(pltemplate->tmplinline);
158                         funcargtypes[0] = INTERNALOID;
159                         inlineOid = LookupFuncName(funcname, 1, funcargtypes, true);
160                         if (!OidIsValid(inlineOid))
161                         {
162                                 tmpAddr = ProcedureCreate(pltemplate->tmplinline,
163                                                                                   PG_CATALOG_NAMESPACE,
164                                                                                   false,                /* replace */
165                                                                                   false,                /* returnsSet */
166                                                                                   VOIDOID,
167                                                                                   BOOTSTRAP_SUPERUSERID,
168                                                                                   ClanguageId,
169                                                                                   F_FMGR_C_VALIDATOR,
170                                                                                   pltemplate->tmplinline,
171                                                                                   pltemplate->tmpllibrary,
172                                                                                   false,                /* isAgg */
173                                                                                   false,                /* isWindowFunc */
174                                                                                   false,                /* security_definer */
175                                                                                   false,                /* isLeakProof */
176                                                                                   true, /* isStrict */
177                                                                                   PROVOLATILE_VOLATILE,
178                                                                                   PROPARALLEL_UNSAFE,
179                                                                                   buildoidvector(funcargtypes, 1),
180                                                                                   PointerGetDatum(NULL),
181                                                                                   PointerGetDatum(NULL),
182                                                                                   PointerGetDatum(NULL),
183                                                                                   NIL,
184                                                                                   PointerGetDatum(NULL),
185                                                                                   PointerGetDatum(NULL),
186                                                                                   1,
187                                                                                   0);
188                                 inlineOid = tmpAddr.objectId;
189                         }
190                 }
191                 else
192                         inlineOid = InvalidOid;
193
194                 /*
195                  * Likewise for the validator, if required; but we don't care about
196                  * its return type.
197                  */
198                 if (pltemplate->tmplvalidator)
199                 {
200                         funcname = SystemFuncName(pltemplate->tmplvalidator);
201                         funcargtypes[0] = OIDOID;
202                         valOid = LookupFuncName(funcname, 1, funcargtypes, true);
203                         if (!OidIsValid(valOid))
204                         {
205                                 tmpAddr = ProcedureCreate(pltemplate->tmplvalidator,
206                                                                                   PG_CATALOG_NAMESPACE,
207                                                                                   false,                /* replace */
208                                                                                   false,                /* returnsSet */
209                                                                                   VOIDOID,
210                                                                                   BOOTSTRAP_SUPERUSERID,
211                                                                                   ClanguageId,
212                                                                                   F_FMGR_C_VALIDATOR,
213                                                                                   pltemplate->tmplvalidator,
214                                                                                   pltemplate->tmpllibrary,
215                                                                                   false,                /* isAgg */
216                                                                                   false,                /* isWindowFunc */
217                                                                                   false,                /* security_definer */
218                                                                                   false,                /* isLeakProof */
219                                                                                   true, /* isStrict */
220                                                                                   PROVOLATILE_VOLATILE,
221                                                                                   PROPARALLEL_UNSAFE,
222                                                                                   buildoidvector(funcargtypes, 1),
223                                                                                   PointerGetDatum(NULL),
224                                                                                   PointerGetDatum(NULL),
225                                                                                   PointerGetDatum(NULL),
226                                                                                   NIL,
227                                                                                   PointerGetDatum(NULL),
228                                                                                   PointerGetDatum(NULL),
229                                                                                   1,
230                                                                                   0);
231                                 valOid = tmpAddr.objectId;
232                         }
233                 }
234                 else
235                         valOid = InvalidOid;
236
237                 /* ok, create it */
238                 return create_proc_lang(stmt->plname, stmt->replace, GetUserId(),
239                                                                 handlerOid, inlineOid,
240                                                                 valOid, pltemplate->tmpltrusted);
241         }
242         else
243         {
244                 /*
245                  * No template, so use the provided information.  If there's no
246                  * handler clause, the user is trying to rely on a template that we
247                  * don't have, so complain accordingly.
248                  */
249                 if (!stmt->plhandler)
250                         ereport(ERROR,
251                                         (errcode(ERRCODE_UNDEFINED_OBJECT),
252                                          errmsg("unsupported language \"%s\"",
253                                                         stmt->plname),
254                                          errhint("The supported languages are listed in the pg_pltemplate system catalog.")));
255
256                 /*
257                  * Check permission
258                  */
259                 if (!superuser())
260                         ereport(ERROR,
261                                         (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
262                                          errmsg("must be superuser to create custom procedural language")));
263
264                 /*
265                  * Lookup the PL handler function and check that it is of the expected
266                  * return type
267                  */
268                 handlerOid = LookupFuncName(stmt->plhandler, 0, funcargtypes, false);
269                 funcrettype = get_func_rettype(handlerOid);
270                 if (funcrettype != LANGUAGE_HANDLEROID)
271                 {
272                         /*
273                          * We allow OPAQUE just so we can load old dump files.  When we
274                          * see a handler function declared OPAQUE, change it to
275                          * LANGUAGE_HANDLER.  (This is probably obsolete and removable?)
276                          */
277                         if (funcrettype == OPAQUEOID)
278                         {
279                                 ereport(WARNING,
280                                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
281                                   errmsg("changing return type of function %s from %s to %s",
282                                                  NameListToString(stmt->plhandler),
283                                                  "opaque", "language_handler")));
284                                 SetFunctionReturnType(handlerOid, LANGUAGE_HANDLEROID);
285                         }
286                         else
287                                 ereport(ERROR,
288                                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
289                                                  errmsg("function %s must return type %s",
290                                         NameListToString(stmt->plhandler), "language_handler")));
291                 }
292
293                 /* validate the inline function */
294                 if (stmt->plinline)
295                 {
296                         funcargtypes[0] = INTERNALOID;
297                         inlineOid = LookupFuncName(stmt->plinline, 1, funcargtypes, false);
298                         /* return value is ignored, so we don't check the type */
299                 }
300                 else
301                         inlineOid = InvalidOid;
302
303                 /* validate the validator function */
304                 if (stmt->plvalidator)
305                 {
306                         funcargtypes[0] = OIDOID;
307                         valOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes, false);
308                         /* return value is ignored, so we don't check the type */
309                 }
310                 else
311                         valOid = InvalidOid;
312
313                 /* ok, create it */
314                 return create_proc_lang(stmt->plname, stmt->replace, GetUserId(),
315                                                                 handlerOid, inlineOid,
316                                                                 valOid, stmt->pltrusted);
317         }
318 }
319
320 /*
321  * Guts of language creation.
322  */
323 static ObjectAddress
324 create_proc_lang(const char *languageName, bool replace,
325                                  Oid languageOwner, Oid handlerOid, Oid inlineOid,
326                                  Oid valOid, bool trusted)
327 {
328         Relation        rel;
329         TupleDesc       tupDesc;
330         Datum           values[Natts_pg_language];
331         bool            nulls[Natts_pg_language];
332         bool            replaces[Natts_pg_language];
333         NameData        langname;
334         HeapTuple       oldtup;
335         HeapTuple       tup;
336         bool            is_update;
337         ObjectAddress myself,
338                                 referenced;
339
340         rel = heap_open(LanguageRelationId, RowExclusiveLock);
341         tupDesc = RelationGetDescr(rel);
342
343         /* Prepare data to be inserted */
344         memset(values, 0, sizeof(values));
345         memset(nulls, false, sizeof(nulls));
346         memset(replaces, true, sizeof(replaces));
347
348         namestrcpy(&langname, languageName);
349         values[Anum_pg_language_lanname - 1] = NameGetDatum(&langname);
350         values[Anum_pg_language_lanowner - 1] = ObjectIdGetDatum(languageOwner);
351         values[Anum_pg_language_lanispl - 1] = BoolGetDatum(true);
352         values[Anum_pg_language_lanpltrusted - 1] = BoolGetDatum(trusted);
353         values[Anum_pg_language_lanplcallfoid - 1] = ObjectIdGetDatum(handlerOid);
354         values[Anum_pg_language_laninline - 1] = ObjectIdGetDatum(inlineOid);
355         values[Anum_pg_language_lanvalidator - 1] = ObjectIdGetDatum(valOid);
356         nulls[Anum_pg_language_lanacl - 1] = true;
357
358         /* Check for pre-existing definition */
359         oldtup = SearchSysCache1(LANGNAME, PointerGetDatum(languageName));
360
361         if (HeapTupleIsValid(oldtup))
362         {
363                 /* There is one; okay to replace it? */
364                 if (!replace)
365                         ereport(ERROR,
366                                         (errcode(ERRCODE_DUPLICATE_OBJECT),
367                                          errmsg("language \"%s\" already exists", languageName)));
368                 if (!pg_language_ownercheck(HeapTupleGetOid(oldtup), languageOwner))
369                         aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE,
370                                                    languageName);
371
372                 /*
373                  * Do not change existing ownership or permissions.  Note
374                  * dependency-update code below has to agree with this decision.
375                  */
376                 replaces[Anum_pg_language_lanowner - 1] = false;
377                 replaces[Anum_pg_language_lanacl - 1] = false;
378
379                 /* Okay, do it... */
380                 tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces);
381                 CatalogTupleUpdate(rel, &tup->t_self, tup);
382
383                 ReleaseSysCache(oldtup);
384                 is_update = true;
385         }
386         else
387         {
388                 /* Creating a new language */
389                 tup = heap_form_tuple(tupDesc, values, nulls);
390                 CatalogTupleInsert(rel, tup);
391                 is_update = false;
392         }
393
394         /*
395          * Create dependencies for the new language.  If we are updating an
396          * existing language, first delete any existing pg_depend entries.
397          * (However, since we are not changing ownership or permissions, the
398          * shared dependencies do *not* need to change, and we leave them alone.)
399          */
400         myself.classId = LanguageRelationId;
401         myself.objectId = HeapTupleGetOid(tup);
402         myself.objectSubId = 0;
403
404         if (is_update)
405                 deleteDependencyRecordsFor(myself.classId, myself.objectId, true);
406
407         /* dependency on owner of language */
408         if (!is_update)
409                 recordDependencyOnOwner(myself.classId, myself.objectId,
410                                                                 languageOwner);
411
412         /* dependency on extension */
413         recordDependencyOnCurrentExtension(&myself, is_update);
414
415         /* dependency on the PL handler function */
416         referenced.classId = ProcedureRelationId;
417         referenced.objectId = handlerOid;
418         referenced.objectSubId = 0;
419         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
420
421         /* dependency on the inline handler function, if any */
422         if (OidIsValid(inlineOid))
423         {
424                 referenced.classId = ProcedureRelationId;
425                 referenced.objectId = inlineOid;
426                 referenced.objectSubId = 0;
427                 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
428         }
429
430         /* dependency on the validator function, if any */
431         if (OidIsValid(valOid))
432         {
433                 referenced.classId = ProcedureRelationId;
434                 referenced.objectId = valOid;
435                 referenced.objectSubId = 0;
436                 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
437         }
438
439         /* Post creation hook for new procedural language */
440         InvokeObjectPostCreateHook(LanguageRelationId, myself.objectId, 0);
441
442         heap_close(rel, RowExclusiveLock);
443
444         return myself;
445 }
446
447 /*
448  * Look to see if we have template information for the given language name.
449  */
450 static PLTemplate *
451 find_language_template(const char *languageName)
452 {
453         PLTemplate *result;
454         Relation        rel;
455         SysScanDesc scan;
456         ScanKeyData key;
457         HeapTuple       tup;
458
459         rel = heap_open(PLTemplateRelationId, AccessShareLock);
460
461         ScanKeyInit(&key,
462                                 Anum_pg_pltemplate_tmplname,
463                                 BTEqualStrategyNumber, F_NAMEEQ,
464                                 CStringGetDatum(languageName));
465         scan = systable_beginscan(rel, PLTemplateNameIndexId, true,
466                                                           NULL, 1, &key);
467
468         tup = systable_getnext(scan);
469         if (HeapTupleIsValid(tup))
470         {
471                 Form_pg_pltemplate tmpl = (Form_pg_pltemplate) GETSTRUCT(tup);
472                 Datum           datum;
473                 bool            isnull;
474
475                 result = (PLTemplate *) palloc0(sizeof(PLTemplate));
476                 result->tmpltrusted = tmpl->tmpltrusted;
477                 result->tmpldbacreate = tmpl->tmpldbacreate;
478
479                 /* Remaining fields are variable-width so we need heap_getattr */
480                 datum = heap_getattr(tup, Anum_pg_pltemplate_tmplhandler,
481                                                          RelationGetDescr(rel), &isnull);
482                 if (!isnull)
483                         result->tmplhandler = TextDatumGetCString(datum);
484
485                 datum = heap_getattr(tup, Anum_pg_pltemplate_tmplinline,
486                                                          RelationGetDescr(rel), &isnull);
487                 if (!isnull)
488                         result->tmplinline = TextDatumGetCString(datum);
489
490                 datum = heap_getattr(tup, Anum_pg_pltemplate_tmplvalidator,
491                                                          RelationGetDescr(rel), &isnull);
492                 if (!isnull)
493                         result->tmplvalidator = TextDatumGetCString(datum);
494
495                 datum = heap_getattr(tup, Anum_pg_pltemplate_tmpllibrary,
496                                                          RelationGetDescr(rel), &isnull);
497                 if (!isnull)
498                         result->tmpllibrary = TextDatumGetCString(datum);
499
500                 /* Ignore template if handler or library info is missing */
501                 if (!result->tmplhandler || !result->tmpllibrary)
502                         result = NULL;
503         }
504         else
505                 result = NULL;
506
507         systable_endscan(scan);
508
509         heap_close(rel, AccessShareLock);
510
511         return result;
512 }
513
514
515 /*
516  * This just returns TRUE if we have a valid template for a given language
517  */
518 bool
519 PLTemplateExists(const char *languageName)
520 {
521         return (find_language_template(languageName) != NULL);
522 }
523
524 /*
525  * Guts of language dropping.
526  */
527 void
528 DropProceduralLanguageById(Oid langOid)
529 {
530         Relation        rel;
531         HeapTuple       langTup;
532
533         rel = heap_open(LanguageRelationId, RowExclusiveLock);
534
535         langTup = SearchSysCache1(LANGOID, ObjectIdGetDatum(langOid));
536         if (!HeapTupleIsValid(langTup))         /* should not happen */
537                 elog(ERROR, "cache lookup failed for language %u", langOid);
538
539         CatalogTupleDelete(rel, &langTup->t_self);
540
541         ReleaseSysCache(langTup);
542
543         heap_close(rel, RowExclusiveLock);
544 }
545
546 /*
547  * get_language_oid - given a language name, look up the OID
548  *
549  * If missing_ok is false, throw an error if language name not found.  If
550  * true, just return InvalidOid.
551  */
552 Oid
553 get_language_oid(const char *langname, bool missing_ok)
554 {
555         Oid                     oid;
556
557         oid = GetSysCacheOid1(LANGNAME, CStringGetDatum(langname));
558         if (!OidIsValid(oid) && !missing_ok)
559                 ereport(ERROR,
560                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
561                                  errmsg("language \"%s\" does not exist", langname)));
562         return oid;
563 }