1 /*-------------------------------------------------------------------------
4 * PostgreSQL LANGUAGE support code.
6 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
10 * src/backend/commands/proclang.c
12 *-------------------------------------------------------------------------
16 #include "access/table.h"
17 #include "catalog/catalog.h"
18 #include "catalog/dependency.h"
19 #include "catalog/indexing.h"
20 #include "catalog/objectaccess.h"
21 #include "catalog/pg_language.h"
22 #include "catalog/pg_namespace.h"
23 #include "catalog/pg_proc.h"
24 #include "catalog/pg_type.h"
25 #include "commands/defrem.h"
26 #include "commands/proclang.h"
27 #include "miscadmin.h"
28 #include "parser/parse_func.h"
29 #include "utils/builtins.h"
30 #include "utils/lsyscache.h"
31 #include "utils/rel.h"
32 #include "utils/syscache.h"
39 CreateProceduralLanguage(CreatePLangStmt
*stmt
)
41 const char *languageName
= stmt
->plname
;
42 Oid languageOwner
= GetUserId();
50 Datum values
[Natts_pg_language
];
51 bool nulls
[Natts_pg_language
];
52 bool replaces
[Natts_pg_language
];
60 ObjectAddresses
*addrs
;
67 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
68 errmsg("must be superuser to create custom procedural language")));
71 * Lookup the PL handler function and check that it is of the expected
74 Assert(stmt
->plhandler
);
75 handlerOid
= LookupFuncName(stmt
->plhandler
, 0, NULL
, false);
76 funcrettype
= get_func_rettype(handlerOid
);
77 if (funcrettype
!= LANGUAGE_HANDLEROID
)
79 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
80 errmsg("function %s must return type %s",
81 NameListToString(stmt
->plhandler
), "language_handler")));
83 /* validate the inline function */
86 funcargtypes
[0] = INTERNALOID
;
87 inlineOid
= LookupFuncName(stmt
->plinline
, 1, funcargtypes
, false);
88 /* return value is ignored, so we don't check the type */
91 inlineOid
= InvalidOid
;
93 /* validate the validator function */
94 if (stmt
->plvalidator
)
96 funcargtypes
[0] = OIDOID
;
97 valOid
= LookupFuncName(stmt
->plvalidator
, 1, funcargtypes
, false);
98 /* return value is ignored, so we don't check the type */
103 /* ok to create it */
104 rel
= table_open(LanguageRelationId
, RowExclusiveLock
);
105 tupDesc
= RelationGetDescr(rel
);
107 /* Prepare data to be inserted */
108 memset(values
, 0, sizeof(values
));
109 memset(nulls
, false, sizeof(nulls
));
110 memset(replaces
, true, sizeof(replaces
));
112 namestrcpy(&langname
, languageName
);
113 values
[Anum_pg_language_lanname
- 1] = NameGetDatum(&langname
);
114 values
[Anum_pg_language_lanowner
- 1] = ObjectIdGetDatum(languageOwner
);
115 values
[Anum_pg_language_lanispl
- 1] = BoolGetDatum(true);
116 values
[Anum_pg_language_lanpltrusted
- 1] = BoolGetDatum(stmt
->pltrusted
);
117 values
[Anum_pg_language_lanplcallfoid
- 1] = ObjectIdGetDatum(handlerOid
);
118 values
[Anum_pg_language_laninline
- 1] = ObjectIdGetDatum(inlineOid
);
119 values
[Anum_pg_language_lanvalidator
- 1] = ObjectIdGetDatum(valOid
);
120 nulls
[Anum_pg_language_lanacl
- 1] = true;
122 /* Check for pre-existing definition */
123 oldtup
= SearchSysCache1(LANGNAME
, PointerGetDatum(languageName
));
125 if (HeapTupleIsValid(oldtup
))
127 Form_pg_language oldform
= (Form_pg_language
) GETSTRUCT(oldtup
);
129 /* There is one; okay to replace it? */
132 (errcode(ERRCODE_DUPLICATE_OBJECT
),
133 errmsg("language \"%s\" already exists", languageName
)));
135 /* This is currently pointless, since we already checked superuser */
137 if (!object_ownercheck(LanguageRelationId
, oldform
->oid
, languageOwner
))
138 aclcheck_error(ACLCHECK_NOT_OWNER
, OBJECT_LANGUAGE
,
143 * Do not change existing oid, ownership or permissions. Note
144 * dependency-update code below has to agree with this decision.
146 replaces
[Anum_pg_language_oid
- 1] = false;
147 replaces
[Anum_pg_language_lanowner
- 1] = false;
148 replaces
[Anum_pg_language_lanacl
- 1] = false;
151 tup
= heap_modify_tuple(oldtup
, tupDesc
, values
, nulls
, replaces
);
152 CatalogTupleUpdate(rel
, &tup
->t_self
, tup
);
154 langoid
= oldform
->oid
;
155 ReleaseSysCache(oldtup
);
160 /* Creating a new language */
161 langoid
= GetNewOidWithIndex(rel
, LanguageOidIndexId
,
162 Anum_pg_language_oid
);
163 values
[Anum_pg_language_oid
- 1] = ObjectIdGetDatum(langoid
);
164 tup
= heap_form_tuple(tupDesc
, values
, nulls
);
165 CatalogTupleInsert(rel
, tup
);
170 * Create dependencies for the new language. If we are updating an
171 * existing language, first delete any existing pg_depend entries.
172 * (However, since we are not changing ownership or permissions, the
173 * shared dependencies do *not* need to change, and we leave them alone.)
175 myself
.classId
= LanguageRelationId
;
176 myself
.objectId
= langoid
;
177 myself
.objectSubId
= 0;
180 deleteDependencyRecordsFor(myself
.classId
, myself
.objectId
, true);
182 /* dependency on owner of language */
184 recordDependencyOnOwner(myself
.classId
, myself
.objectId
,
187 /* dependency on extension */
188 recordDependencyOnCurrentExtension(&myself
, is_update
);
190 addrs
= new_object_addresses();
192 /* dependency on the PL handler function */
193 ObjectAddressSet(referenced
, ProcedureRelationId
, handlerOid
);
194 add_exact_object_address(&referenced
, addrs
);
196 /* dependency on the inline handler function, if any */
197 if (OidIsValid(inlineOid
))
199 ObjectAddressSet(referenced
, ProcedureRelationId
, inlineOid
);
200 add_exact_object_address(&referenced
, addrs
);
203 /* dependency on the validator function, if any */
204 if (OidIsValid(valOid
))
206 ObjectAddressSet(referenced
, ProcedureRelationId
, valOid
);
207 add_exact_object_address(&referenced
, addrs
);
210 record_object_address_dependencies(&myself
, addrs
, DEPENDENCY_NORMAL
);
211 free_object_addresses(addrs
);
213 /* Post creation hook for new procedural language */
214 InvokeObjectPostCreateHook(LanguageRelationId
, myself
.objectId
, 0);
216 table_close(rel
, RowExclusiveLock
);
222 * get_language_oid - given a language name, look up the OID
224 * If missing_ok is false, throw an error if language name not found. If
225 * true, just return InvalidOid.
228 get_language_oid(const char *langname
, bool missing_ok
)
232 oid
= GetSysCacheOid1(LANGNAME
, Anum_pg_language_oid
,
233 CStringGetDatum(langname
));
234 if (!OidIsValid(oid
) && !missing_ok
)
236 (errcode(ERRCODE_UNDEFINED_OBJECT
),
237 errmsg("language \"%s\" does not exist", langname
)));