Refactor ownercheck functions
[pgsql.git] / src / backend / commands / proclang.c
blob96a524be36aa1d891828ae74a8b862ac02f8ff7e
1 /*-------------------------------------------------------------------------
3 * proclang.c
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
9 * IDENTIFICATION
10 * src/backend/commands/proclang.c
12 *-------------------------------------------------------------------------
14 #include "postgres.h"
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"
36 * CREATE LANGUAGE
38 ObjectAddress
39 CreateProceduralLanguage(CreatePLangStmt *stmt)
41 const char *languageName = stmt->plname;
42 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;
63 * Check permission
65 if (!superuser())
66 ereport(ERROR,
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
72 * return type
74 Assert(stmt->plhandler);
75 handlerOid = LookupFuncName(stmt->plhandler, 0, NULL, false);
76 funcrettype = get_func_rettype(handlerOid);
77 if (funcrettype != LANGUAGE_HANDLEROID)
78 ereport(ERROR,
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 */
84 if (stmt->plinline)
86 funcargtypes[0] = INTERNALOID;
87 inlineOid = LookupFuncName(stmt->plinline, 1, funcargtypes, false);
88 /* return value is ignored, so we don't check the type */
90 else
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 */
100 else
101 valOid = InvalidOid;
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? */
130 if (!stmt->replace)
131 ereport(ERROR,
132 (errcode(ERRCODE_DUPLICATE_OBJECT),
133 errmsg("language \"%s\" already exists", languageName)));
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
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;
150 /* Okay, do it... */
151 tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces);
152 CatalogTupleUpdate(rel, &tup->t_self, tup);
154 langoid = oldform->oid;
155 ReleaseSysCache(oldtup);
156 is_update = true;
158 else
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);
166 is_update = false;
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;
179 if (is_update)
180 deleteDependencyRecordsFor(myself.classId, myself.objectId, true);
182 /* dependency on owner of language */
183 if (!is_update)
184 recordDependencyOnOwner(myself.classId, myself.objectId,
185 languageOwner);
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);
218 return myself;
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)
230 Oid oid;
232 oid = GetSysCacheOid1(LANGNAME, Anum_pg_language_oid,
233 CStringGetDatum(langname));
234 if (!OidIsValid(oid) && !missing_ok)
235 ereport(ERROR,
236 (errcode(ERRCODE_UNDEFINED_OBJECT),
237 errmsg("language \"%s\" does not exist", langname)));
238 return oid;