1 /*-------------------------------------------------------------------------
4 * PostgreSQL LANGUAGE support code.
6 * Portions Copyright (c) 1996-2024, 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_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"
37 CreateProceduralLanguage(CreatePLangStmt
*stmt
)
39 const char *languageName
= stmt
->plname
;
40 Oid languageOwner
= GetUserId();
48 Datum values
[Natts_pg_language
];
49 bool nulls
[Natts_pg_language
];
50 bool replaces
[Natts_pg_language
];
58 ObjectAddresses
*addrs
;
65 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
66 errmsg("must be superuser to create custom procedural language")));
69 * Lookup the PL handler function and check that it is of the expected
72 Assert(stmt
->plhandler
);
73 handlerOid
= LookupFuncName(stmt
->plhandler
, 0, NULL
, false);
74 funcrettype
= get_func_rettype(handlerOid
);
75 if (funcrettype
!= LANGUAGE_HANDLEROID
)
77 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
78 errmsg("function %s must return type %s",
79 NameListToString(stmt
->plhandler
), "language_handler")));
81 /* validate the inline function */
84 funcargtypes
[0] = INTERNALOID
;
85 inlineOid
= LookupFuncName(stmt
->plinline
, 1, funcargtypes
, false);
86 /* return value is ignored, so we don't check the type */
89 inlineOid
= InvalidOid
;
91 /* validate the validator function */
92 if (stmt
->plvalidator
)
94 funcargtypes
[0] = OIDOID
;
95 valOid
= LookupFuncName(stmt
->plvalidator
, 1, funcargtypes
, false);
96 /* return value is ignored, so we don't check the type */
101 /* ok to create it */
102 rel
= table_open(LanguageRelationId
, RowExclusiveLock
);
103 tupDesc
= RelationGetDescr(rel
);
105 /* Prepare data to be inserted */
106 memset(values
, 0, sizeof(values
));
107 memset(nulls
, false, sizeof(nulls
));
108 memset(replaces
, true, sizeof(replaces
));
110 namestrcpy(&langname
, languageName
);
111 values
[Anum_pg_language_lanname
- 1] = NameGetDatum(&langname
);
112 values
[Anum_pg_language_lanowner
- 1] = ObjectIdGetDatum(languageOwner
);
113 values
[Anum_pg_language_lanispl
- 1] = BoolGetDatum(true);
114 values
[Anum_pg_language_lanpltrusted
- 1] = BoolGetDatum(stmt
->pltrusted
);
115 values
[Anum_pg_language_lanplcallfoid
- 1] = ObjectIdGetDatum(handlerOid
);
116 values
[Anum_pg_language_laninline
- 1] = ObjectIdGetDatum(inlineOid
);
117 values
[Anum_pg_language_lanvalidator
- 1] = ObjectIdGetDatum(valOid
);
118 nulls
[Anum_pg_language_lanacl
- 1] = true;
120 /* Check for pre-existing definition */
121 oldtup
= SearchSysCache1(LANGNAME
, PointerGetDatum(languageName
));
123 if (HeapTupleIsValid(oldtup
))
125 Form_pg_language oldform
= (Form_pg_language
) GETSTRUCT(oldtup
);
127 /* There is one; okay to replace it? */
130 (errcode(ERRCODE_DUPLICATE_OBJECT
),
131 errmsg("language \"%s\" already exists", languageName
)));
133 /* This is currently pointless, since we already checked superuser */
135 if (!object_ownercheck(LanguageRelationId
, oldform
->oid
, languageOwner
))
136 aclcheck_error(ACLCHECK_NOT_OWNER
, OBJECT_LANGUAGE
,
141 * Do not change existing oid, ownership or permissions. Note
142 * dependency-update code below has to agree with this decision.
144 replaces
[Anum_pg_language_oid
- 1] = false;
145 replaces
[Anum_pg_language_lanowner
- 1] = false;
146 replaces
[Anum_pg_language_lanacl
- 1] = false;
149 tup
= heap_modify_tuple(oldtup
, tupDesc
, values
, nulls
, replaces
);
150 CatalogTupleUpdate(rel
, &tup
->t_self
, tup
);
152 langoid
= oldform
->oid
;
153 ReleaseSysCache(oldtup
);
158 /* Creating a new language */
159 langoid
= GetNewOidWithIndex(rel
, LanguageOidIndexId
,
160 Anum_pg_language_oid
);
161 values
[Anum_pg_language_oid
- 1] = ObjectIdGetDatum(langoid
);
162 tup
= heap_form_tuple(tupDesc
, values
, nulls
);
163 CatalogTupleInsert(rel
, tup
);
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.)
173 myself
.classId
= LanguageRelationId
;
174 myself
.objectId
= langoid
;
175 myself
.objectSubId
= 0;
178 deleteDependencyRecordsFor(myself
.classId
, myself
.objectId
, true);
180 /* dependency on owner of language */
182 recordDependencyOnOwner(myself
.classId
, myself
.objectId
,
185 /* dependency on extension */
186 recordDependencyOnCurrentExtension(&myself
, is_update
);
188 addrs
= new_object_addresses();
190 /* dependency on the PL handler function */
191 ObjectAddressSet(referenced
, ProcedureRelationId
, handlerOid
);
192 add_exact_object_address(&referenced
, addrs
);
194 /* dependency on the inline handler function, if any */
195 if (OidIsValid(inlineOid
))
197 ObjectAddressSet(referenced
, ProcedureRelationId
, inlineOid
);
198 add_exact_object_address(&referenced
, addrs
);
201 /* dependency on the validator function, if any */
202 if (OidIsValid(valOid
))
204 ObjectAddressSet(referenced
, ProcedureRelationId
, valOid
);
205 add_exact_object_address(&referenced
, addrs
);
208 record_object_address_dependencies(&myself
, addrs
, DEPENDENCY_NORMAL
);
209 free_object_addresses(addrs
);
211 /* Post creation hook for new procedural language */
212 InvokeObjectPostCreateHook(LanguageRelationId
, myself
.objectId
, 0);
214 table_close(rel
, RowExclusiveLock
);
220 * get_language_oid - given a language name, look up the OID
222 * If missing_ok is false, throw an error if language name not found. If
223 * true, just return InvalidOid.
226 get_language_oid(const char *langname
, bool missing_ok
)
230 oid
= GetSysCacheOid1(LANGNAME
, Anum_pg_language_oid
,
231 CStringGetDatum(langname
));
232 if (!OidIsValid(oid
) && !missing_ok
)
234 (errcode(ERRCODE_UNDEFINED_OBJECT
),
235 errmsg("language \"%s\" does not exist", langname
)));