summaryrefslogtreecommitdiffstats
path: root/src/backend/commands/proclang.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:15:05 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:15:05 +0000
commit46651ce6fe013220ed397add242004d764fc0153 (patch)
tree6e5299f990f88e60174a1d3ae6e48eedd2688b2b /src/backend/commands/proclang.c
parentInitial commit. (diff)
downloadpostgresql-14-46651ce6fe013220ed397add242004d764fc0153.tar.xz
postgresql-14-46651ce6fe013220ed397add242004d764fc0153.zip
Adding upstream version 14.5.upstream/14.5upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/backend/commands/proclang.c')
-rw-r--r--src/backend/commands/proclang.c239
1 files changed, 239 insertions, 0 deletions
diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c
new file mode 100644
index 0000000..81598d3
--- /dev/null
+++ b/src/backend/commands/proclang.c
@@ -0,0 +1,239 @@
+/*-------------------------------------------------------------------------
+ *
+ * proclang.c
+ * PostgreSQL LANGUAGE support code.
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/commands/proclang.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/table.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_language.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "commands/proclang.h"
+#include "miscadmin.h"
+#include "parser/parse_func.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+/*
+ * CREATE LANGUAGE
+ */
+ObjectAddress
+CreateProceduralLanguage(CreatePLangStmt *stmt)
+{
+ const char *languageName = stmt->plname;
+ Oid languageOwner = GetUserId();
+ Oid handlerOid,
+ inlineOid,
+ valOid;
+ Oid funcrettype;
+ Oid funcargtypes[1];
+ Relation rel;
+ TupleDesc tupDesc;
+ Datum values[Natts_pg_language];
+ bool nulls[Natts_pg_language];
+ bool replaces[Natts_pg_language];
+ NameData langname;
+ HeapTuple oldtup;
+ HeapTuple tup;
+ Oid langoid;
+ bool is_update;
+ ObjectAddress myself,
+ referenced;
+ ObjectAddresses *addrs;
+
+ /*
+ * Check permission
+ */
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to create custom procedural language")));
+
+ /*
+ * Lookup the PL handler function and check that it is of the expected
+ * return type
+ */
+ Assert(stmt->plhandler);
+ handlerOid = LookupFuncName(stmt->plhandler, 0, NULL, false);
+ funcrettype = get_func_rettype(handlerOid);
+ if (funcrettype != LANGUAGE_HANDLEROID)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("function %s must return type %s",
+ NameListToString(stmt->plhandler), "language_handler")));
+
+ /* validate the inline function */
+ if (stmt->plinline)
+ {
+ funcargtypes[0] = INTERNALOID;
+ inlineOid = LookupFuncName(stmt->plinline, 1, funcargtypes, false);
+ /* return value is ignored, so we don't check the type */
+ }
+ else
+ inlineOid = InvalidOid;
+
+ /* validate the validator function */
+ if (stmt->plvalidator)
+ {
+ funcargtypes[0] = OIDOID;
+ valOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes, false);
+ /* return value is ignored, so we don't check the type */
+ }
+ else
+ valOid = InvalidOid;
+
+ /* ok to create it */
+ rel = table_open(LanguageRelationId, RowExclusiveLock);
+ tupDesc = RelationGetDescr(rel);
+
+ /* Prepare data to be inserted */
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+ memset(replaces, true, sizeof(replaces));
+
+ namestrcpy(&langname, languageName);
+ values[Anum_pg_language_lanname - 1] = NameGetDatum(&langname);
+ values[Anum_pg_language_lanowner - 1] = ObjectIdGetDatum(languageOwner);
+ values[Anum_pg_language_lanispl - 1] = BoolGetDatum(true);
+ values[Anum_pg_language_lanpltrusted - 1] = BoolGetDatum(stmt->pltrusted);
+ values[Anum_pg_language_lanplcallfoid - 1] = ObjectIdGetDatum(handlerOid);
+ values[Anum_pg_language_laninline - 1] = ObjectIdGetDatum(inlineOid);
+ values[Anum_pg_language_lanvalidator - 1] = ObjectIdGetDatum(valOid);
+ nulls[Anum_pg_language_lanacl - 1] = true;
+
+ /* Check for pre-existing definition */
+ oldtup = SearchSysCache1(LANGNAME, PointerGetDatum(languageName));
+
+ if (HeapTupleIsValid(oldtup))
+ {
+ Form_pg_language oldform = (Form_pg_language) GETSTRUCT(oldtup);
+
+ /* There is one; okay to replace it? */
+ if (!stmt->replace)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("language \"%s\" already exists", languageName)));
+
+ /* This is currently pointless, since we already checked superuser */
+#ifdef NOT_USED
+ if (!pg_language_ownercheck(oldform->oid, languageOwner))
+ aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_LANGUAGE,
+ languageName);
+#endif
+
+ /*
+ * Do not change existing oid, ownership or permissions. Note
+ * dependency-update code below has to agree with this decision.
+ */
+ replaces[Anum_pg_language_oid - 1] = false;
+ replaces[Anum_pg_language_lanowner - 1] = false;
+ replaces[Anum_pg_language_lanacl - 1] = false;
+
+ /* Okay, do it... */
+ tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces);
+ CatalogTupleUpdate(rel, &tup->t_self, tup);
+
+ langoid = oldform->oid;
+ ReleaseSysCache(oldtup);
+ is_update = true;
+ }
+ else
+ {
+ /* Creating a new language */
+ langoid = GetNewOidWithIndex(rel, LanguageOidIndexId,
+ Anum_pg_language_oid);
+ values[Anum_pg_language_oid - 1] = ObjectIdGetDatum(langoid);
+ tup = heap_form_tuple(tupDesc, values, nulls);
+ CatalogTupleInsert(rel, tup);
+ is_update = false;
+ }
+
+ /*
+ * Create dependencies for the new language. If we are updating an
+ * existing language, first delete any existing pg_depend entries.
+ * (However, since we are not changing ownership or permissions, the
+ * shared dependencies do *not* need to change, and we leave them alone.)
+ */
+ myself.classId = LanguageRelationId;
+ myself.objectId = langoid;
+ myself.objectSubId = 0;
+
+ if (is_update)
+ deleteDependencyRecordsFor(myself.classId, myself.objectId, true);
+
+ /* dependency on owner of language */
+ if (!is_update)
+ recordDependencyOnOwner(myself.classId, myself.objectId,
+ languageOwner);
+
+ /* dependency on extension */
+ recordDependencyOnCurrentExtension(&myself, is_update);
+
+ addrs = new_object_addresses();
+
+ /* dependency on the PL handler function */
+ ObjectAddressSet(referenced, ProcedureRelationId, handlerOid);
+ add_exact_object_address(&referenced, addrs);
+
+ /* dependency on the inline handler function, if any */
+ if (OidIsValid(inlineOid))
+ {
+ ObjectAddressSet(referenced, ProcedureRelationId, inlineOid);
+ add_exact_object_address(&referenced, addrs);
+ }
+
+ /* dependency on the validator function, if any */
+ if (OidIsValid(valOid))
+ {
+ ObjectAddressSet(referenced, ProcedureRelationId, valOid);
+ add_exact_object_address(&referenced, addrs);
+ }
+
+ record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
+ free_object_addresses(addrs);
+
+ /* Post creation hook for new procedural language */
+ InvokeObjectPostCreateHook(LanguageRelationId, myself.objectId, 0);
+
+ table_close(rel, RowExclusiveLock);
+
+ return myself;
+}
+
+/*
+ * get_language_oid - given a language name, look up the OID
+ *
+ * If missing_ok is false, throw an error if language name not found. If
+ * true, just return InvalidOid.
+ */
+Oid
+get_language_oid(const char *langname, bool missing_ok)
+{
+ Oid oid;
+
+ oid = GetSysCacheOid1(LANGNAME, Anum_pg_language_oid,
+ CStringGetDatum(langname));
+ if (!OidIsValid(oid) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("language \"%s\" does not exist", langname)));
+ return oid;
+}