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
43typedef 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
53static ObjectAddress create_proc_lang(const char *languageName, bool replace,
54 Oid languageOwner, Oid handlerOid, Oid inlineOid,
55 Oid valOid, bool trusted);
56static PLTemplate *find_language_template(const char *languageName);
57
58/*
59 * CREATE LANGUAGE
60 */
61ObjectAddress
62CreateProceduralLanguage(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 */
321static ObjectAddress
322create_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 */
456static PLTemplate *
457find_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 */
524bool
525PLTemplateExists(const char *languageName)
526{
527 return (find_language_template(languageName) != NULL);
528}
529
530/*
531 * Guts of language dropping.
532 */
533void
534DropProceduralLanguageById(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 */
558Oid
559get_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}
571