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 | } |
571 | |