Line data Source code
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 3 : 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 3 : 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 1 : if (stmt->plhandler)
86 0 : ereport(NOTICE,
87 : (errmsg("using pg_pltemplate information instead of CREATE LANGUAGE parameters")));
88 :
89 : /*
90 : * Check permission
91 : */
92 1 : if (!superuser())
93 : {
94 0 : if (!pltemplate->tmpldbacreate)
95 0 : ereport(ERROR,
96 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
97 : errmsg("must be superuser to create procedural language \"%s\"",
98 : stmt->plname)));
99 0 : if (!pg_database_ownercheck(MyDatabaseId, GetUserId()))
100 0 : aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
101 0 : 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 1 : funcname = SystemFuncName(pltemplate->tmplhandler);
110 1 : handlerOid = LookupFuncName(funcname, 0, funcargtypes, true);
111 1 : if (OidIsValid(handlerOid))
112 : {
113 0 : funcrettype = get_func_rettype(handlerOid);
114 0 : if (funcrettype != LANGUAGE_HANDLEROID)
115 0 : 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 2 : 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 1 : pltemplate->tmplhandler,
131 1 : 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 1 : 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 1 : if (pltemplate->tmplinline)
156 : {
157 1 : funcname = SystemFuncName(pltemplate->tmplinline);
158 1 : funcargtypes[0] = INTERNALOID;
159 1 : inlineOid = LookupFuncName(funcname, 1, funcargtypes, true);
160 1 : if (!OidIsValid(inlineOid))
161 : {
162 2 : 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 1 : pltemplate->tmplinline,
171 1 : 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 1 : inlineOid = tmpAddr.objectId;
189 : }
190 : }
191 : else
192 0 : inlineOid = InvalidOid;
193 :
194 : /*
195 : * Likewise for the validator, if required; but we don't care about
196 : * its return type.
197 : */
198 1 : if (pltemplate->tmplvalidator)
199 : {
200 1 : funcname = SystemFuncName(pltemplate->tmplvalidator);
201 1 : funcargtypes[0] = OIDOID;
202 1 : valOid = LookupFuncName(funcname, 1, funcargtypes, true);
203 1 : if (!OidIsValid(valOid))
204 : {
205 2 : 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 1 : pltemplate->tmplvalidator,
214 1 : 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 1 : valOid = tmpAddr.objectId;
232 : }
233 : }
234 : else
235 0 : valOid = InvalidOid;
236 :
237 : /* ok, create it */
238 1 : return create_proc_lang(stmt->plname, stmt->replace, GetUserId(),
239 : handlerOid, inlineOid,
240 1 : 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 2 : if (!stmt->plhandler)
250 0 : 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 2 : if (!superuser())
260 0 : 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 2 : handlerOid = LookupFuncName(stmt->plhandler, 0, funcargtypes, false);
269 2 : funcrettype = get_func_rettype(handlerOid);
270 2 : 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 0 : if (funcrettype == OPAQUEOID)
278 : {
279 0 : 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 0 : SetFunctionReturnType(handlerOid, LANGUAGE_HANDLEROID);
285 : }
286 : else
287 0 : 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 2 : if (stmt->plinline)
295 : {
296 0 : funcargtypes[0] = INTERNALOID;
297 0 : inlineOid = LookupFuncName(stmt->plinline, 1, funcargtypes, false);
298 : /* return value is ignored, so we don't check the type */
299 : }
300 : else
301 2 : inlineOid = InvalidOid;
302 :
303 : /* validate the validator function */
304 2 : if (stmt->plvalidator)
305 : {
306 0 : funcargtypes[0] = OIDOID;
307 0 : valOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes, false);
308 : /* return value is ignored, so we don't check the type */
309 : }
310 : else
311 2 : valOid = InvalidOid;
312 :
313 : /* ok, create it */
314 2 : return create_proc_lang(stmt->plname, stmt->replace, GetUserId(),
315 : handlerOid, inlineOid,
316 2 : valOid, stmt->pltrusted);
317 : }
318 : }
319 :
320 : /*
321 : * Guts of language creation.
322 : */
323 : static ObjectAddress
324 3 : 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 3 : rel = heap_open(LanguageRelationId, RowExclusiveLock);
341 3 : tupDesc = RelationGetDescr(rel);
342 :
343 : /* Prepare data to be inserted */
344 3 : memset(values, 0, sizeof(values));
345 3 : memset(nulls, false, sizeof(nulls));
346 3 : memset(replaces, true, sizeof(replaces));
347 :
348 3 : namestrcpy(&langname, languageName);
349 3 : values[Anum_pg_language_lanname - 1] = NameGetDatum(&langname);
350 3 : values[Anum_pg_language_lanowner - 1] = ObjectIdGetDatum(languageOwner);
351 3 : values[Anum_pg_language_lanispl - 1] = BoolGetDatum(true);
352 3 : values[Anum_pg_language_lanpltrusted - 1] = BoolGetDatum(trusted);
353 3 : values[Anum_pg_language_lanplcallfoid - 1] = ObjectIdGetDatum(handlerOid);
354 3 : values[Anum_pg_language_laninline - 1] = ObjectIdGetDatum(inlineOid);
355 3 : values[Anum_pg_language_lanvalidator - 1] = ObjectIdGetDatum(valOid);
356 3 : nulls[Anum_pg_language_lanacl - 1] = true;
357 :
358 : /* Check for pre-existing definition */
359 3 : oldtup = SearchSysCache1(LANGNAME, PointerGetDatum(languageName));
360 :
361 3 : if (HeapTupleIsValid(oldtup))
362 : {
363 : /* There is one; okay to replace it? */
364 0 : if (!replace)
365 0 : ereport(ERROR,
366 : (errcode(ERRCODE_DUPLICATE_OBJECT),
367 : errmsg("language \"%s\" already exists", languageName)));
368 0 : if (!pg_language_ownercheck(HeapTupleGetOid(oldtup), languageOwner))
369 0 : 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 0 : replaces[Anum_pg_language_lanowner - 1] = false;
377 0 : replaces[Anum_pg_language_lanacl - 1] = false;
378 :
379 : /* Okay, do it... */
380 0 : tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces);
381 0 : CatalogTupleUpdate(rel, &tup->t_self, tup);
382 :
383 0 : ReleaseSysCache(oldtup);
384 0 : is_update = true;
385 : }
386 : else
387 : {
388 : /* Creating a new language */
389 3 : tup = heap_form_tuple(tupDesc, values, nulls);
390 3 : CatalogTupleInsert(rel, tup);
391 3 : 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 3 : myself.classId = LanguageRelationId;
401 3 : myself.objectId = HeapTupleGetOid(tup);
402 3 : myself.objectSubId = 0;
403 :
404 3 : if (is_update)
405 0 : deleteDependencyRecordsFor(myself.classId, myself.objectId, true);
406 :
407 : /* dependency on owner of language */
408 3 : if (!is_update)
409 3 : recordDependencyOnOwner(myself.classId, myself.objectId,
410 : languageOwner);
411 :
412 : /* dependency on extension */
413 3 : recordDependencyOnCurrentExtension(&myself, is_update);
414 :
415 : /* dependency on the PL handler function */
416 3 : referenced.classId = ProcedureRelationId;
417 3 : referenced.objectId = handlerOid;
418 3 : referenced.objectSubId = 0;
419 3 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
420 :
421 : /* dependency on the inline handler function, if any */
422 3 : if (OidIsValid(inlineOid))
423 : {
424 1 : referenced.classId = ProcedureRelationId;
425 1 : referenced.objectId = inlineOid;
426 1 : referenced.objectSubId = 0;
427 1 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
428 : }
429 :
430 : /* dependency on the validator function, if any */
431 3 : if (OidIsValid(valOid))
432 : {
433 1 : referenced.classId = ProcedureRelationId;
434 1 : referenced.objectId = valOid;
435 1 : referenced.objectSubId = 0;
436 1 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
437 : }
438 :
439 : /* Post creation hook for new procedural language */
440 3 : InvokeObjectPostCreateHook(LanguageRelationId, myself.objectId, 0);
441 :
442 3 : heap_close(rel, RowExclusiveLock);
443 :
444 3 : return myself;
445 : }
446 :
447 : /*
448 : * Look to see if we have template information for the given language name.
449 : */
450 : static PLTemplate *
451 3 : find_language_template(const char *languageName)
452 : {
453 : PLTemplate *result;
454 : Relation rel;
455 : SysScanDesc scan;
456 : ScanKeyData key;
457 : HeapTuple tup;
458 :
459 3 : rel = heap_open(PLTemplateRelationId, AccessShareLock);
460 :
461 3 : ScanKeyInit(&key,
462 : Anum_pg_pltemplate_tmplname,
463 : BTEqualStrategyNumber, F_NAMEEQ,
464 : CStringGetDatum(languageName));
465 3 : scan = systable_beginscan(rel, PLTemplateNameIndexId, true,
466 : NULL, 1, &key);
467 :
468 3 : tup = systable_getnext(scan);
469 3 : if (HeapTupleIsValid(tup))
470 : {
471 1 : Form_pg_pltemplate tmpl = (Form_pg_pltemplate) GETSTRUCT(tup);
472 : Datum datum;
473 : bool isnull;
474 :
475 1 : result = (PLTemplate *) palloc0(sizeof(PLTemplate));
476 1 : result->tmpltrusted = tmpl->tmpltrusted;
477 1 : result->tmpldbacreate = tmpl->tmpldbacreate;
478 :
479 : /* Remaining fields are variable-width so we need heap_getattr */
480 1 : datum = heap_getattr(tup, Anum_pg_pltemplate_tmplhandler,
481 : RelationGetDescr(rel), &isnull);
482 1 : if (!isnull)
483 1 : result->tmplhandler = TextDatumGetCString(datum);
484 :
485 1 : datum = heap_getattr(tup, Anum_pg_pltemplate_tmplinline,
486 : RelationGetDescr(rel), &isnull);
487 1 : if (!isnull)
488 1 : result->tmplinline = TextDatumGetCString(datum);
489 :
490 1 : datum = heap_getattr(tup, Anum_pg_pltemplate_tmplvalidator,
491 : RelationGetDescr(rel), &isnull);
492 1 : if (!isnull)
493 1 : result->tmplvalidator = TextDatumGetCString(datum);
494 :
495 1 : datum = heap_getattr(tup, Anum_pg_pltemplate_tmpllibrary,
496 : RelationGetDescr(rel), &isnull);
497 1 : if (!isnull)
498 1 : result->tmpllibrary = TextDatumGetCString(datum);
499 :
500 : /* Ignore template if handler or library info is missing */
501 1 : if (!result->tmplhandler || !result->tmpllibrary)
502 0 : result = NULL;
503 : }
504 : else
505 2 : result = NULL;
506 :
507 3 : systable_endscan(scan);
508 :
509 3 : heap_close(rel, AccessShareLock);
510 :
511 3 : 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 0 : PLTemplateExists(const char *languageName)
520 : {
521 0 : return (find_language_template(languageName) != NULL);
522 : }
523 :
524 : /*
525 : * Guts of language dropping.
526 : */
527 : void
528 2 : DropProceduralLanguageById(Oid langOid)
529 : {
530 : Relation rel;
531 : HeapTuple langTup;
532 :
533 2 : rel = heap_open(LanguageRelationId, RowExclusiveLock);
534 :
535 2 : langTup = SearchSysCache1(LANGOID, ObjectIdGetDatum(langOid));
536 2 : if (!HeapTupleIsValid(langTup)) /* should not happen */
537 0 : elog(ERROR, "cache lookup failed for language %u", langOid);
538 :
539 2 : CatalogTupleDelete(rel, &langTup->t_self);
540 :
541 2 : ReleaseSysCache(langTup);
542 :
543 2 : heap_close(rel, RowExclusiveLock);
544 2 : }
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 26 : get_language_oid(const char *langname, bool missing_ok)
554 : {
555 : Oid oid;
556 :
557 26 : oid = GetSysCacheOid1(LANGNAME, CStringGetDatum(langname));
558 26 : if (!OidIsValid(oid) && !missing_ok)
559 2 : ereport(ERROR,
560 : (errcode(ERRCODE_UNDEFINED_OBJECT),
561 : errmsg("language \"%s\" does not exist", langname)));
562 24 : return oid;
563 : }
|