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