diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:15:05 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:15:05 +0000 |
commit | 46651ce6fe013220ed397add242004d764fc0153 (patch) | |
tree | 6e5299f990f88e60174a1d3ae6e48eedd2688b2b /src/backend/commands/proclang.c | |
parent | Initial commit. (diff) | |
download | postgresql-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.c | 239 |
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; +} |