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