Age Owner TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * proclang.c
4 : * PostgreSQL LANGUAGE support code.
5 : *
6 : * Portions Copyright (c) 1996-2023, 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/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"
33 :
34 :
35 : /*
36 : * CREATE LANGUAGE
37 : */
38 : ObjectAddress
9173 bruce 39 CBC 326 : CreateProceduralLanguage(CreatePLangStmt *stmt)
40 : {
1166 tgl 41 326 : const char *languageName = stmt->plname;
42 326 : Oid languageOwner = GetUserId();
43 : Oid handlerOid,
44 : inlineOid,
45 : valOid;
46 : Oid funcrettype;
47 : Oid funcargtypes[1];
48 : Relation rel;
49 : TupleDesc tupDesc;
50 : Datum values[Natts_pg_language];
51 : bool nulls[Natts_pg_language];
52 : bool replaces[Natts_pg_language];
53 : NameData langname;
54 : HeapTuple oldtup;
55 : HeapTuple tup;
56 : Oid langoid;
57 : bool is_update;
58 : ObjectAddress myself,
59 : referenced;
60 : ObjectAddresses *addrs;
61 :
62 : /*
63 : * Check permission
64 : */
65 326 : if (!superuser())
1166 tgl 66 UBC 0 : ereport(ERROR,
67 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
68 : errmsg("must be superuser to create custom procedural language")));
69 :
70 : /*
71 : * Lookup the PL handler function and check that it is of the expected
72 : * return type
73 : */
1166 tgl 74 CBC 326 : Assert(stmt->plhandler);
75 326 : handlerOid = LookupFuncName(stmt->plhandler, 0, NULL, false);
76 326 : funcrettype = get_func_rettype(handlerOid);
77 326 : if (funcrettype != LANGUAGE_HANDLEROID)
1130 tgl 78 UBC 0 : ereport(ERROR,
79 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
80 : errmsg("function %s must return type %s",
81 : NameListToString(stmt->plhandler), "language_handler")));
82 :
83 : /* validate the inline function */
1166 tgl 84 CBC 326 : if (stmt->plinline)
85 : {
86 317 : funcargtypes[0] = INTERNALOID;
87 317 : inlineOid = LookupFuncName(stmt->plinline, 1, funcargtypes, false);
88 : /* return value is ignored, so we don't check the type */
89 : }
90 : else
91 9 : inlineOid = InvalidOid;
92 :
93 : /* validate the validator function */
94 326 : if (stmt->plvalidator)
95 : {
96 317 : funcargtypes[0] = OIDOID;
97 317 : valOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes, false);
98 : /* return value is ignored, so we don't check the type */
99 : }
100 : else
101 9 : valOid = InvalidOid;
102 :
103 : /* ok to create it */
1539 andres 104 326 : rel = table_open(LanguageRelationId, RowExclusiveLock);
4793 tgl 105 326 : tupDesc = RelationGetDescr(rel);
106 :
107 : /* Prepare data to be inserted */
6425 108 326 : memset(values, 0, sizeof(values));
5271 109 326 : memset(nulls, false, sizeof(nulls));
4793 110 326 : memset(replaces, true, sizeof(replaces));
111 :
6425 112 326 : namestrcpy(&langname, languageName);
113 326 : values[Anum_pg_language_lanname - 1] = NameGetDatum(&langname);
5858 114 326 : values[Anum_pg_language_lanowner - 1] = ObjectIdGetDatum(languageOwner);
6425 115 326 : values[Anum_pg_language_lanispl - 1] = BoolGetDatum(true);
1166 116 326 : values[Anum_pg_language_lanpltrusted - 1] = BoolGetDatum(stmt->pltrusted);
6425 117 326 : values[Anum_pg_language_lanplcallfoid - 1] = ObjectIdGetDatum(handlerOid);
4947 118 326 : values[Anum_pg_language_laninline - 1] = ObjectIdGetDatum(inlineOid);
6425 119 326 : values[Anum_pg_language_lanvalidator - 1] = ObjectIdGetDatum(valOid);
5271 120 326 : nulls[Anum_pg_language_lanacl - 1] = true;
121 :
122 : /* Check for pre-existing definition */
4793 123 326 : oldtup = SearchSysCache1(LANGNAME, PointerGetDatum(languageName));
124 :
125 326 : if (HeapTupleIsValid(oldtup))
126 : {
1601 andres 127 UBC 0 : Form_pg_language oldform = (Form_pg_language) GETSTRUCT(oldtup);
128 :
129 : /* There is one; okay to replace it? */
1166 tgl 130 0 : if (!stmt->replace)
4793 131 0 : ereport(ERROR,
132 : (errcode(ERRCODE_DUPLICATE_OBJECT),
133 : errmsg("language \"%s\" already exists", languageName)));
134 :
135 : /* This is currently pointless, since we already checked superuser */
136 : #ifdef NOT_USED
137 : if (!object_ownercheck(LanguageRelationId, oldform->oid, languageOwner))
138 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_LANGUAGE,
139 : languageName);
140 : #endif
141 :
142 : /*
143 : * Do not change existing oid, ownership or permissions. Note
144 : * dependency-update code below has to agree with this decision.
145 : */
1601 andres 146 0 : replaces[Anum_pg_language_oid - 1] = false;
4793 tgl 147 0 : replaces[Anum_pg_language_lanowner - 1] = false;
148 0 : replaces[Anum_pg_language_lanacl - 1] = false;
149 :
150 : /* Okay, do it... */
151 0 : tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces);
2259 alvherre 152 0 : CatalogTupleUpdate(rel, &tup->t_self, tup);
153 :
1601 andres 154 0 : langoid = oldform->oid;
4793 tgl 155 0 : ReleaseSysCache(oldtup);
156 0 : is_update = true;
157 : }
158 : else
159 : {
160 : /* Creating a new language */
1601 andres 161 CBC 326 : langoid = GetNewOidWithIndex(rel, LanguageOidIndexId,
162 : Anum_pg_language_oid);
163 326 : values[Anum_pg_language_oid - 1] = ObjectIdGetDatum(langoid);
4793 tgl 164 326 : tup = heap_form_tuple(tupDesc, values, nulls);
2259 alvherre 165 326 : CatalogTupleInsert(rel, tup);
4793 tgl 166 326 : is_update = false;
167 : }
168 :
169 : /*
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.)
174 : */
6569 175 326 : myself.classId = LanguageRelationId;
1601 andres 176 326 : myself.objectId = langoid;
7572 tgl 177 326 : myself.objectSubId = 0;
178 :
4793 179 326 : if (is_update)
4443 tgl 180 UBC 0 : deleteDependencyRecordsFor(myself.classId, myself.objectId, true);
181 :
182 : /* dependency on owner of language */
4793 tgl 183 CBC 326 : if (!is_update)
184 326 : recordDependencyOnOwner(myself.classId, myself.objectId,
185 : languageOwner);
186 :
187 : /* dependency on extension */
4278 188 326 : recordDependencyOnCurrentExtension(&myself, is_update);
189 :
946 michael 190 326 : addrs = new_object_addresses();
191 :
192 : /* dependency on the PL handler function */
193 326 : ObjectAddressSet(referenced, ProcedureRelationId, handlerOid);
194 326 : add_exact_object_address(&referenced, addrs);
195 :
196 : /* dependency on the inline handler function, if any */
4947 tgl 197 326 : if (OidIsValid(inlineOid))
198 : {
946 michael 199 317 : ObjectAddressSet(referenced, ProcedureRelationId, inlineOid);
200 317 : add_exact_object_address(&referenced, addrs);
201 : }
202 :
203 : /* dependency on the validator function, if any */
6425 tgl 204 326 : if (OidIsValid(valOid))
205 : {
946 michael 206 317 : ObjectAddressSet(referenced, ProcedureRelationId, valOid);
207 317 : add_exact_object_address(&referenced, addrs);
208 : }
209 :
210 326 : record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
211 326 : free_object_addresses(addrs);
212 :
213 : /* Post creation hook for new procedural language */
3686 rhaas 214 326 : InvokeObjectPostCreateHook(LanguageRelationId, myself.objectId, 0);
215 :
1539 andres 216 326 : table_close(rel, RowExclusiveLock);
217 :
2959 alvherre 218 326 : return myself;
219 : }
220 :
221 : /*
222 : * get_language_oid - given a language name, look up the OID
223 : *
224 : * If missing_ok is false, throw an error if language name not found. If
225 : * true, just return InvalidOid.
226 : */
227 : Oid
4630 rhaas 228 744 : get_language_oid(const char *langname, bool missing_ok)
229 : {
230 : Oid oid;
231 :
1601 andres 232 744 : oid = GetSysCacheOid1(LANGNAME, Anum_pg_language_oid,
233 : CStringGetDatum(langname));
4630 rhaas 234 744 : if (!OidIsValid(oid) && !missing_ok)
235 8 : ereport(ERROR,
236 : (errcode(ERRCODE_UNDEFINED_OBJECT),
237 : errmsg("language \"%s\" does not exist", langname)));
238 736 : return oid;
239 : }
|