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