diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2020-01-29 18:42:43 -0500 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2020-01-29 18:42:43 -0500 |
commit | 50fc694e43742ce3d04a5e9f708432cb022c5f0d (patch) | |
tree | 590d1724a529f8a80294c1f9d0bfe3c7ae9ec942 /src/backend/commands | |
parent | 166ab9c8d343b51e6838d7b59194d32a0019242f (diff) | |
download | postgresql-50fc694e43742ce3d04a5e9f708432cb022c5f0d.tar.gz postgresql-50fc694e43742ce3d04a5e9f708432cb022c5f0d.zip |
Invent "trusted" extensions, and remove the pg_pltemplate catalog.
This patch creates a new extension property, "trusted". An extension
that's marked that way in its control file can be installed by a
non-superuser who has the CREATE privilege on the current database,
even if the extension contains objects that normally would have to be
created by a superuser. The objects within the extension will (by
default) be owned by the bootstrap superuser, but the extension itself
will be owned by the calling user. This allows replicating the old
behavior around trusted procedural languages, without all the
special-case logic in CREATE LANGUAGE. We have, however, chosen to
loosen the rules slightly: formerly, only a database owner could take
advantage of the special case that allowed installation of a trusted
language, but now anyone who has CREATE privilege can do so.
Having done that, we can delete the pg_pltemplate catalog, moving the
knowledge it contained into the extension script files for the various
PLs. This ends up being no change at all for the in-core PLs, but it is
a large step forward for external PLs: they can now have the same ease
of installation as core PLs do. The old "trusted PL" behavior was only
available to PLs that had entries in pg_pltemplate, but now any
extension can be marked trusted if appropriate.
This also removes one of the stumbling blocks for our Python 2 -> 3
migration, since the association of "plpythonu" with Python 2 is no
longer hard-wired into pg_pltemplate's initial contents. Exactly where
we go from here on that front remains to be settled, but one problem
is fixed.
Patch by me, reviewed by Peter Eisentraut, Stephen Frost, and others.
Discussion: https://postgr.es/m/5889.1566415762@sss.pgh.pa.us
Diffstat (limited to 'src/backend/commands')
-rw-r--r-- | src/backend/commands/extension.c | 175 | ||||
-rw-r--r-- | src/backend/commands/functioncmds.c | 5 | ||||
-rw-r--r-- | src/backend/commands/proclang.c | 422 |
3 files changed, 224 insertions, 378 deletions
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c index 01de398dcb3..ddd46f4e2f5 100644 --- a/src/backend/commands/extension.c +++ b/src/backend/commands/extension.c @@ -40,6 +40,7 @@ #include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/objectaccess.h" +#include "catalog/pg_authid.h" #include "catalog/pg_collation.h" #include "catalog/pg_depend.h" #include "catalog/pg_extension.h" @@ -84,6 +85,7 @@ typedef struct ExtensionControlFile char *schema; /* target schema (allowed if !relocatable) */ bool relocatable; /* is ALTER EXTENSION SET SCHEMA supported? */ bool superuser; /* must be superuser to install? */ + bool trusted; /* allow becoming superuser on the fly? */ int encoding; /* encoding of the script file, or -1 */ List *requires; /* names of prerequisite extensions */ } ExtensionControlFile; @@ -558,6 +560,14 @@ parse_extension_control_file(ExtensionControlFile *control, errmsg("parameter \"%s\" requires a Boolean value", item->name))); } + else if (strcmp(item->name, "trusted") == 0) + { + if (!parse_bool(item->value, &control->trusted)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("parameter \"%s\" requires a Boolean value", + item->name))); + } else if (strcmp(item->name, "encoding") == 0) { control->encoding = pg_valid_server_encoding(item->value); @@ -614,6 +624,7 @@ read_extension_control_file(const char *extname) control->name = pstrdup(extname); control->relocatable = false; control->superuser = true; + control->trusted = false; control->encoding = -1; /* @@ -795,6 +806,27 @@ execute_sql_string(const char *sql) } /* + * Policy function: is the given extension trusted for installation by a + * non-superuser? + * + * (Update the errhint logic below if you change this.) + */ +static bool +extension_is_trusted(ExtensionControlFile *control) +{ + AclResult aclresult; + + /* Never trust unless extension's control file says it's okay */ + if (!control->trusted) + return false; + /* Allow if user has CREATE privilege on current database */ + aclresult = pg_database_aclcheck(MyDatabaseId, GetUserId(), ACL_CREATE); + if (aclresult == ACLCHECK_OK) + return true; + return false; +} + +/* * Execute the appropriate script file for installing or updating the extension * * If from_version isn't NULL, it's an update @@ -806,35 +838,56 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control, List *requiredSchemas, const char *schemaName, Oid schemaOid) { + bool switch_to_superuser = false; char *filename; + Oid save_userid = 0; + int save_sec_context = 0; int save_nestlevel; StringInfoData pathbuf; ListCell *lc; /* - * Enforce superuser-ness if appropriate. We postpone this check until - * here so that the flag is correctly associated with the right script(s) - * if it's set in secondary control files. + * Enforce superuser-ness if appropriate. We postpone these checks until + * here so that the control flags are correctly associated with the right + * script(s) if they happen to be set in secondary control files. */ if (control->superuser && !superuser()) { - if (from_version == NULL) + if (extension_is_trusted(control)) + switch_to_superuser = true; + else if (from_version == NULL) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to create extension \"%s\"", control->name), - errhint("Must be superuser to create this extension."))); + control->trusted + ? errhint("Must have CREATE privilege on current database to create this extension.") + : errhint("Must be superuser to create this extension."))); else ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to update extension \"%s\"", control->name), - errhint("Must be superuser to update this extension."))); + control->trusted + ? errhint("Must have CREATE privilege on current database to update this extension.") + : errhint("Must be superuser to update this extension."))); } filename = get_extension_script_filename(control, from_version, version); /* + * If installing a trusted extension on behalf of a non-superuser, become + * the bootstrap superuser. (This switch will be cleaned up automatically + * if the transaction aborts, as will the GUC changes below.) + */ + if (switch_to_superuser) + { + GetUserIdAndSecContext(&save_userid, &save_sec_context); + SetUserIdAndSecContext(BOOTSTRAP_SUPERUSERID, + save_sec_context | SECURITY_LOCAL_USERID_CHANGE); + } + + /* * Force client_min_messages and log_min_messages to be at least WARNING, * so that we won't spam the user with useless NOTICE messages from common * script actions like creating shell types. @@ -907,6 +960,22 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control, CStringGetTextDatum("ng")); /* + * If the script uses @extowner@, substitute the calling username. + */ + if (strstr(c_sql, "@extowner@")) + { + Oid uid = switch_to_superuser ? save_userid : GetUserId(); + const char *userName = GetUserNameFromId(uid, false); + const char *qUserName = quote_identifier(userName); + + t_sql = DirectFunctionCall3Coll(replace_text, + C_COLLATION_OID, + t_sql, + CStringGetTextDatum("@extowner@"), + CStringGetTextDatum(qUserName)); + } + + /* * If it's not relocatable, substitute the target schema name for * occurrences of @extschema@. * @@ -953,6 +1022,12 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control, * Restore the GUC variables we set above. */ AtEOXact_GUC(true, save_nestlevel); + + /* + * Restore authentication state if needed. + */ + if (switch_to_superuser) + SetUserIdAndSecContext(save_userid, save_sec_context); } /* @@ -2111,8 +2186,8 @@ get_available_versions_for_extension(ExtensionControlFile *pcontrol, { ExtensionVersionInfo *evi = (ExtensionVersionInfo *) lfirst(lc); ExtensionControlFile *control; - Datum values[7]; - bool nulls[7]; + Datum values[8]; + bool nulls[8]; ListCell *lc2; if (!evi->installable) @@ -2133,24 +2208,26 @@ get_available_versions_for_extension(ExtensionControlFile *pcontrol, values[1] = CStringGetTextDatum(evi->name); /* superuser */ values[2] = BoolGetDatum(control->superuser); + /* trusted */ + values[3] = BoolGetDatum(control->trusted); /* relocatable */ - values[3] = BoolGetDatum(control->relocatable); + values[4] = BoolGetDatum(control->relocatable); /* schema */ if (control->schema == NULL) - nulls[4] = true; + nulls[5] = true; else - values[4] = DirectFunctionCall1(namein, + values[5] = DirectFunctionCall1(namein, CStringGetDatum(control->schema)); /* requires */ if (control->requires == NIL) - nulls[5] = true; + nulls[6] = true; else - values[5] = convert_requires_to_datum(control->requires); + values[6] = convert_requires_to_datum(control->requires); /* comment */ if (control->comment == NULL) - nulls[6] = true; + nulls[7] = true; else - values[6] = CStringGetTextDatum(control->comment); + values[7] = CStringGetTextDatum(control->comment); tuplestore_putvalues(tupstore, tupdesc, values, nulls); @@ -2178,16 +2255,18 @@ get_available_versions_for_extension(ExtensionControlFile *pcontrol, values[1] = CStringGetTextDatum(evi2->name); /* superuser */ values[2] = BoolGetDatum(control->superuser); + /* trusted */ + values[3] = BoolGetDatum(control->trusted); /* relocatable */ - values[3] = BoolGetDatum(control->relocatable); + values[4] = BoolGetDatum(control->relocatable); /* schema stays the same */ /* requires */ if (control->requires == NIL) - nulls[5] = true; + nulls[6] = true; else { - values[5] = convert_requires_to_datum(control->requires); - nulls[5] = false; + values[6] = convert_requires_to_datum(control->requires); + nulls[6] = false; } /* comment stays the same */ @@ -2198,6 +2277,64 @@ get_available_versions_for_extension(ExtensionControlFile *pcontrol, } /* + * Test whether the given extension exists (not whether it's installed) + * + * This checks for the existence of a matching control file in the extension + * directory. That's not a bulletproof check, since the file might be + * invalid, but this is only used for hints so it doesn't have to be 100% + * right. + */ +bool +extension_file_exists(const char *extensionName) +{ + bool result = false; + char *location; + DIR *dir; + struct dirent *de; + + location = get_extension_control_directory(); + dir = AllocateDir(location); + + /* + * If the control directory doesn't exist, we want to silently return + * false. Any other error will be reported by ReadDir. + */ + if (dir == NULL && errno == ENOENT) + { + /* do nothing */ + } + else + { + while ((de = ReadDir(dir, location)) != NULL) + { + char *extname; + + if (!is_extension_control_filename(de->d_name)) + continue; + + /* extract extension name from 'name.control' filename */ + extname = pstrdup(de->d_name); + *strrchr(extname, '.') = '\0'; + + /* ignore it if it's an auxiliary control file */ + if (strstr(extname, "--")) + continue; + + /* done if it matches request */ + if (strcmp(extname, extensionName) == 0) + { + result = true; + break; + } + } + + FreeDir(dir); + } + + return result; +} + +/* * Convert a list of extension names to a name[] Datum */ static Datum diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index c31c57e5e9a..0f40c9ee27b 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -49,6 +49,7 @@ #include "catalog/pg_type.h" #include "commands/alter.h" #include "commands/defrem.h" +#include "commands/extension.h" #include "commands/proclang.h" #include "executor/execdesc.h" #include "executor/executor.h" @@ -991,7 +992,7 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("language \"%s\" does not exist", language), - (PLTemplateExists(language) ? + (extension_file_exists(language) ? errhint("Use CREATE EXTENSION to load the language into the database.") : 0))); languageStruct = (Form_pg_language) GETSTRUCT(languageTuple); @@ -2225,7 +2226,7 @@ ExecuteDoStmt(DoStmt *stmt, bool atomic) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("language \"%s\" does not exist", language), - (PLTemplateExists(language) ? + (extension_file_exists(language) ? errhint("Use CREATE EXTENSION to load the language into the database.") : 0))); languageStruct = (Form_pg_language) GETSTRUCT(languageTuple); diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c index cdff43d3ce7..9d72edbfec5 100644 --- a/src/backend/commands/proclang.c +++ b/src/backend/commands/proclang.c @@ -13,329 +13,110 @@ */ #include "postgres.h" -#include "access/genam.h" -#include "access/htup_details.h" #include "access/table.h" #include "catalog/catalog.h" #include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/objectaccess.h" -#include "catalog/pg_authid.h" #include "catalog/pg_language.h" #include "catalog/pg_namespace.h" -#include "catalog/pg_pltemplate.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" -#include "commands/dbcommands.h" #include "commands/defrem.h" #include "commands/proclang.h" #include "miscadmin.h" #include "parser/parse_func.h" -#include "parser/parser.h" -#include "utils/acl.h" #include "utils/builtins.h" -#include "utils/fmgroids.h" #include "utils/lsyscache.h" #include "utils/rel.h" #include "utils/syscache.h" -typedef struct -{ - bool tmpltrusted; /* trusted? */ - bool tmpldbacreate; /* db owner allowed to create? */ - char *tmplhandler; /* name of handler function */ - char *tmplinline; /* name of anonymous-block handler, or NULL */ - char *tmplvalidator; /* name of validator function, or NULL */ - char *tmpllibrary; /* path of shared library */ -} PLTemplate; - -static ObjectAddress create_proc_lang(const char *languageName, bool replace, - Oid languageOwner, Oid handlerOid, Oid inlineOid, - Oid valOid, bool trusted); -static PLTemplate *find_language_template(const char *languageName); - /* * CREATE LANGUAGE */ ObjectAddress CreateProceduralLanguage(CreatePLangStmt *stmt) { - PLTemplate *pltemplate; - ObjectAddress tmpAddr; + 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; /* - * If we have template information for the language, ignore the supplied - * parameters (if any) and use the template information. + * Check permission */ - if ((pltemplate = find_language_template(stmt->plname)) != NULL) - { - List *funcname; - - /* - * Give a notice if we are ignoring supplied parameters. - */ - if (stmt->plhandler) - ereport(NOTICE, - (errmsg("using pg_pltemplate information instead of CREATE LANGUAGE parameters"))); - - /* - * Check permission - */ - if (!superuser()) - { - if (!pltemplate->tmpldbacreate) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to create procedural language \"%s\"", - stmt->plname))); - if (!pg_database_ownercheck(MyDatabaseId, GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_DATABASE, - get_database_name(MyDatabaseId)); - } - - /* - * Find or create the handler function, which we force to be in the - * pg_catalog schema. If already present, it must have the correct - * return type. - */ - funcname = SystemFuncName(pltemplate->tmplhandler); - handlerOid = LookupFuncName(funcname, 0, NULL, true); - if (OidIsValid(handlerOid)) - { - funcrettype = get_func_rettype(handlerOid); - if (funcrettype != LANGUAGE_HANDLEROID) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("function %s must return type %s", - NameListToString(funcname), "language_handler"))); - } - else - { - tmpAddr = ProcedureCreate(pltemplate->tmplhandler, - PG_CATALOG_NAMESPACE, - false, /* replace */ - false, /* returnsSet */ - LANGUAGE_HANDLEROID, - BOOTSTRAP_SUPERUSERID, - ClanguageId, - F_FMGR_C_VALIDATOR, - pltemplate->tmplhandler, - pltemplate->tmpllibrary, - PROKIND_FUNCTION, - false, /* security_definer */ - false, /* isLeakProof */ - false, /* isStrict */ - PROVOLATILE_VOLATILE, - PROPARALLEL_UNSAFE, - buildoidvector(funcargtypes, 0), - PointerGetDatum(NULL), - PointerGetDatum(NULL), - PointerGetDatum(NULL), - NIL, - PointerGetDatum(NULL), - PointerGetDatum(NULL), - InvalidOid, - 1, - 0); - handlerOid = tmpAddr.objectId; - } - - /* - * Likewise for the anonymous block handler, if required; but we don't - * care about its return type. - */ - if (pltemplate->tmplinline) - { - funcname = SystemFuncName(pltemplate->tmplinline); - funcargtypes[0] = INTERNALOID; - inlineOid = LookupFuncName(funcname, 1, funcargtypes, true); - if (!OidIsValid(inlineOid)) - { - tmpAddr = ProcedureCreate(pltemplate->tmplinline, - PG_CATALOG_NAMESPACE, - false, /* replace */ - false, /* returnsSet */ - VOIDOID, - BOOTSTRAP_SUPERUSERID, - ClanguageId, - F_FMGR_C_VALIDATOR, - pltemplate->tmplinline, - pltemplate->tmpllibrary, - PROKIND_FUNCTION, - false, /* security_definer */ - false, /* isLeakProof */ - true, /* isStrict */ - PROVOLATILE_VOLATILE, - PROPARALLEL_UNSAFE, - buildoidvector(funcargtypes, 1), - PointerGetDatum(NULL), - PointerGetDatum(NULL), - PointerGetDatum(NULL), - NIL, - PointerGetDatum(NULL), - PointerGetDatum(NULL), - InvalidOid, - 1, - 0); - inlineOid = tmpAddr.objectId; - } - } - else - inlineOid = InvalidOid; + 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) + { /* - * Likewise for the validator, if required; but we don't care about - * its return type. + * We allow OPAQUE just so we can load old dump files. When we see a + * handler function declared OPAQUE, change it to LANGUAGE_HANDLER. + * (This is probably obsolete and removable?) */ - if (pltemplate->tmplvalidator) + if (funcrettype == OPAQUEOID) { - funcname = SystemFuncName(pltemplate->tmplvalidator); - funcargtypes[0] = OIDOID; - valOid = LookupFuncName(funcname, 1, funcargtypes, true); - if (!OidIsValid(valOid)) - { - tmpAddr = ProcedureCreate(pltemplate->tmplvalidator, - PG_CATALOG_NAMESPACE, - false, /* replace */ - false, /* returnsSet */ - VOIDOID, - BOOTSTRAP_SUPERUSERID, - ClanguageId, - F_FMGR_C_VALIDATOR, - pltemplate->tmplvalidator, - pltemplate->tmpllibrary, - PROKIND_FUNCTION, - false, /* security_definer */ - false, /* isLeakProof */ - true, /* isStrict */ - PROVOLATILE_VOLATILE, - PROPARALLEL_UNSAFE, - buildoidvector(funcargtypes, 1), - PointerGetDatum(NULL), - PointerGetDatum(NULL), - PointerGetDatum(NULL), - NIL, - PointerGetDatum(NULL), - PointerGetDatum(NULL), - InvalidOid, - 1, - 0); - valOid = tmpAddr.objectId; - } + ereport(WARNING, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("changing return type of function %s from %s to %s", + NameListToString(stmt->plhandler), + "opaque", "language_handler"))); + SetFunctionReturnType(handlerOid, LANGUAGE_HANDLEROID); } else - valOid = InvalidOid; + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("function %s must return type %s", + NameListToString(stmt->plhandler), "language_handler"))); + } - /* ok, create it */ - return create_proc_lang(stmt->plname, stmt->replace, GetUserId(), - handlerOid, inlineOid, - valOid, pltemplate->tmpltrusted); + /* 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 - { - /* - * No template, so use the provided information. If there's no - * handler clause, the user is trying to rely on a template that we - * don't have, so complain accordingly. - */ - if (!stmt->plhandler) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("unsupported language \"%s\"", - stmt->plname), - errhint("The supported languages are listed in the pg_pltemplate system catalog."))); + inlineOid = InvalidOid; - /* - * 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 - */ - handlerOid = LookupFuncName(stmt->plhandler, 0, NULL, false); - funcrettype = get_func_rettype(handlerOid); - if (funcrettype != LANGUAGE_HANDLEROID) - { - /* - * We allow OPAQUE just so we can load old dump files. When we - * see a handler function declared OPAQUE, change it to - * LANGUAGE_HANDLER. (This is probably obsolete and removable?) - */ - if (funcrettype == OPAQUEOID) - { - ereport(WARNING, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("changing return type of function %s from %s to %s", - NameListToString(stmt->plhandler), - "opaque", "language_handler"))); - SetFunctionReturnType(handlerOid, LANGUAGE_HANDLEROID); - } - else - 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, create it */ - return create_proc_lang(stmt->plname, stmt->replace, GetUserId(), - handlerOid, inlineOid, - valOid, stmt->pltrusted); + /* 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 */ } -} - -/* - * Guts of language creation. - */ -static ObjectAddress -create_proc_lang(const char *languageName, bool replace, - Oid languageOwner, Oid handlerOid, Oid inlineOid, - Oid valOid, bool trusted) -{ - 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; + else + valOid = InvalidOid; + /* ok to create it */ rel = table_open(LanguageRelationId, RowExclusiveLock); tupDesc = RelationGetDescr(rel); @@ -348,7 +129,7 @@ create_proc_lang(const char *languageName, bool replace, 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(trusted); + 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); @@ -362,13 +143,17 @@ create_proc_lang(const char *languageName, bool replace, Form_pg_language oldform = (Form_pg_language) GETSTRUCT(oldtup); /* There is one; okay to replace it? */ - if (!replace) + 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 @@ -451,83 +236,6 @@ create_proc_lang(const char *languageName, bool replace, } /* - * Look to see if we have template information for the given language name. - */ -static PLTemplate * -find_language_template(const char *languageName) -{ - PLTemplate *result; - Relation rel; - SysScanDesc scan; - ScanKeyData key; - HeapTuple tup; - - rel = table_open(PLTemplateRelationId, AccessShareLock); - - ScanKeyInit(&key, - Anum_pg_pltemplate_tmplname, - BTEqualStrategyNumber, F_NAMEEQ, - CStringGetDatum(languageName)); - scan = systable_beginscan(rel, PLTemplateNameIndexId, true, - NULL, 1, &key); - - tup = systable_getnext(scan); - if (HeapTupleIsValid(tup)) - { - Form_pg_pltemplate tmpl = (Form_pg_pltemplate) GETSTRUCT(tup); - Datum datum; - bool isnull; - - result = (PLTemplate *) palloc0(sizeof(PLTemplate)); - result->tmpltrusted = tmpl->tmpltrusted; - result->tmpldbacreate = tmpl->tmpldbacreate; - - /* Remaining fields are variable-width so we need heap_getattr */ - datum = heap_getattr(tup, Anum_pg_pltemplate_tmplhandler, - RelationGetDescr(rel), &isnull); - if (!isnull) - result->tmplhandler = TextDatumGetCString(datum); - - datum = heap_getattr(tup, Anum_pg_pltemplate_tmplinline, - RelationGetDescr(rel), &isnull); - if (!isnull) - result->tmplinline = TextDatumGetCString(datum); - - datum = heap_getattr(tup, Anum_pg_pltemplate_tmplvalidator, - RelationGetDescr(rel), &isnull); - if (!isnull) - result->tmplvalidator = TextDatumGetCString(datum); - - datum = heap_getattr(tup, Anum_pg_pltemplate_tmpllibrary, - RelationGetDescr(rel), &isnull); - if (!isnull) - result->tmpllibrary = TextDatumGetCString(datum); - - /* Ignore template if handler or library info is missing */ - if (!result->tmplhandler || !result->tmpllibrary) - result = NULL; - } - else - result = NULL; - - systable_endscan(scan); - - table_close(rel, AccessShareLock); - - return result; -} - - -/* - * This just returns true if we have a valid template for a given language - */ -bool -PLTemplateExists(const char *languageName) -{ - return (find_language_template(languageName) != NULL); -} - -/* * Guts of language dropping. */ void |