diff options
Diffstat (limited to 'src')
34 files changed, 491 insertions, 581 deletions
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile index d5da81c8016..f8f0b4841c3 100644 --- a/src/backend/catalog/Makefile +++ b/src/backend/catalog/Makefile @@ -58,7 +58,7 @@ CATALOG_HEADERS := \ pg_statistic_ext.h pg_statistic_ext_data.h \ pg_statistic.h pg_rewrite.h pg_trigger.h pg_event_trigger.h pg_description.h \ pg_cast.h pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \ - pg_database.h pg_db_role_setting.h pg_tablespace.h pg_pltemplate.h \ + pg_database.h pg_db_role_setting.h pg_tablespace.h \ pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \ pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ pg_ts_parser.h pg_ts_template.h pg_extension.h \ @@ -84,7 +84,7 @@ POSTGRES_BKI_DATA = $(addprefix $(top_srcdir)/src/include/catalog/,\ pg_cast.dat pg_class.dat pg_collation.dat pg_conversion.dat \ pg_database.dat pg_language.dat \ pg_namespace.dat pg_opclass.dat pg_operator.dat pg_opfamily.dat \ - pg_pltemplate.dat pg_proc.dat pg_range.dat pg_tablespace.dat \ + pg_proc.dat pg_range.dat pg_tablespace.dat \ pg_ts_config.dat pg_ts_config_map.dat pg_ts_dict.dat pg_ts_parser.dat \ pg_ts_template.dat pg_type.dat \ ) diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c index 16cb6d88714..7d6acaed92c 100644 --- a/src/backend/catalog/catalog.c +++ b/src/backend/catalog/catalog.c @@ -33,7 +33,6 @@ #include "catalog/pg_database.h" #include "catalog/pg_db_role_setting.h" #include "catalog/pg_namespace.h" -#include "catalog/pg_pltemplate.h" #include "catalog/pg_replication_origin.h" #include "catalog/pg_shdepend.h" #include "catalog/pg_shdescription.h" @@ -242,7 +241,6 @@ IsSharedRelation(Oid relationId) if (relationId == AuthIdRelationId || relationId == AuthMemRelationId || relationId == DatabaseRelationId || - relationId == PLTemplateRelationId || relationId == SharedDescriptionRelationId || relationId == SharedDependRelationId || relationId == SharedSecLabelRelationId || @@ -258,7 +256,6 @@ IsSharedRelation(Oid relationId) relationId == AuthMemMemRoleIndexId || relationId == DatabaseNameIndexId || relationId == DatabaseOidIndexId || - relationId == PLTemplateNameIndexId || relationId == SharedDescriptionObjIndexId || relationId == SharedDependDependerIndexId || relationId == SharedDependReferenceIndexId || @@ -278,8 +275,6 @@ IsSharedRelation(Oid relationId) relationId == PgDatabaseToastIndex || relationId == PgDbRoleSettingToastTable || relationId == PgDbRoleSettingToastIndex || - relationId == PgPlTemplateToastTable || - relationId == PgPlTemplateToastIndex || relationId == PgReplicationOriginToastTable || relationId == PgReplicationOriginToastIndex || relationId == PgShdescriptionToastTable || diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index c9e75f43705..c9e60600357 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -317,7 +317,8 @@ CREATE VIEW pg_available_extensions AS CREATE VIEW pg_available_extension_versions AS SELECT E.name, E.version, (X.extname IS NOT NULL) AS installed, - E.superuser, E.relocatable, E.schema, E.requires, E.comment + E.superuser, E.trusted, E.relocatable, + E.schema, E.requires, E.comment FROM pg_available_extension_versions() AS E LEFT JOIN pg_extension AS X ON E.name = X.extname AND E.version = X.extversion; 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 diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index ba5916b4d27..1b0edf5d3d1 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -4324,14 +4324,17 @@ NumericOnly_list: NumericOnly { $$ = list_make1($1); } CreatePLangStmt: CREATE opt_or_replace opt_trusted opt_procedural LANGUAGE NonReservedWord_or_Sconst { - CreatePLangStmt *n = makeNode(CreatePLangStmt); - n->replace = $2; - n->plname = $6; - /* parameters are all to be supplied by system */ - n->plhandler = NIL; - n->plinline = NIL; - n->plvalidator = NIL; - n->pltrusted = false; + /* + * We now interpret parameterless CREATE LANGUAGE as + * CREATE EXTENSION. "OR REPLACE" is silently translated + * to "IF NOT EXISTS", which isn't quite the same, but + * seems more useful than throwing an error. We just + * ignore TRUSTED, as the previous code would have too. + */ + CreateExtensionStmt *n = makeNode(CreateExtensionStmt); + n->if_not_exists = $2; + n->extname = $6; + n->options = NIL; $$ = (Node *)n; } | CREATE opt_or_replace opt_trusted opt_procedural LANGUAGE NonReservedWord_or_Sconst diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 799b6988b7a..ec3e2c63b0c 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -11396,9 +11396,9 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang) } /* - * If the functions are dumpable then emit a traditional CREATE LANGUAGE - * with parameters. Otherwise, we'll write a parameterless command, which - * will rely on data from pg_pltemplate. + * If the functions are dumpable then emit a complete CREATE LANGUAGE with + * parameters. Otherwise, we'll write a parameterless command, which will + * be interpreted as CREATE EXTENSION. */ useParams = (funcInfo != NULL && (inlineInfo != NULL || !OidIsValid(plang->laninline)) && @@ -11431,11 +11431,11 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang) /* * If not dumping parameters, then use CREATE OR REPLACE so that the * command will not fail if the language is preinstalled in the target - * database. We restrict the use of REPLACE to this case so as to - * eliminate the risk of replacing a language with incompatible - * parameter settings: this command will only succeed at all if there - * is a pg_pltemplate entry, and if there is one, the existing entry - * must match it too. + * database. + * + * Modern servers will interpret this as CREATE EXTENSION IF NOT + * EXISTS; perhaps we should emit that instead? But it might just add + * confusion. */ appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s", qlanname); diff --git a/src/bin/pg_upgrade/function.c b/src/bin/pg_upgrade/function.c index 28638e951ee..d163cb2dde7 100644 --- a/src/bin/pg_upgrade/function.c +++ b/src/bin/pg_upgrade/function.c @@ -214,13 +214,9 @@ check_loadable_libraries(void) * plpython2u language was created with library name plpython2.so * as a symbolic link to plpython.so. In Postgres 9.1, only the * plpython2.so library was created, and both plpythonu and - * plpython2u pointing to it. For this reason, any reference to + * plpython2u point to it. For this reason, any reference to * library name "plpython" in an old PG <= 9.1 cluster must look * for "plpython2" in the new cluster. - * - * For this case, we could check pg_pltemplate, but that only - * works for languages, and does not help with function shared - * objects, so we just do a general fix. */ if (GET_MAJOR_VERSION(old_cluster.major_version) < 901 && strcmp(lib, "$libdir/plpython") == 0) diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 249b1f5a341..b520c050839 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 202001251 +#define CATALOG_VERSION_NO 202001291 #endif diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h index 13e07fc314a..8be303870f8 100644 --- a/src/include/catalog/indexing.h +++ b/src/include/catalog/indexing.h @@ -206,9 +206,6 @@ DECLARE_UNIQUE_INDEX(pg_opfamily_am_name_nsp_index, 2754, on pg_opfamily using b DECLARE_UNIQUE_INDEX(pg_opfamily_oid_index, 2755, on pg_opfamily using btree(oid oid_ops)); #define OpfamilyOidIndexId 2755 -DECLARE_UNIQUE_INDEX(pg_pltemplate_name_index, 1137, on pg_pltemplate using btree(tmplname name_ops)); -#define PLTemplateNameIndexId 1137 - DECLARE_UNIQUE_INDEX(pg_proc_oid_index, 2690, on pg_proc using btree(oid oid_ops)); #define ProcedureOidIndexId 2690 DECLARE_UNIQUE_INDEX(pg_proc_proname_args_nsp_index, 2691, on pg_proc using btree(proname name_ops, proargtypes oidvector_ops, pronamespace oid_ops)); diff --git a/src/include/catalog/pg_pltemplate.dat b/src/include/catalog/pg_pltemplate.dat deleted file mode 100644 index 1c96b304319..00000000000 --- a/src/include/catalog/pg_pltemplate.dat +++ /dev/null @@ -1,51 +0,0 @@ -#---------------------------------------------------------------------- -# -# pg_pltemplate.dat -# Initial contents of the pg_pltemplate system catalog. -# -# Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group -# Portions Copyright (c) 1994, Regents of the University of California -# -# src/include/catalog/pg_pltemplate.dat -# -#---------------------------------------------------------------------- - -[ - -{ tmplname => 'plpgsql', tmpltrusted => 't', tmpldbacreate => 't', - tmplhandler => 'plpgsql_call_handler', tmplinline => 'plpgsql_inline_handler', - tmplvalidator => 'plpgsql_validator', tmpllibrary => '$libdir/plpgsql', - tmplacl => '_null_' }, -{ tmplname => 'pltcl', tmpltrusted => 't', tmpldbacreate => 't', - tmplhandler => 'pltcl_call_handler', tmplinline => '_null_', - tmplvalidator => '_null_', tmpllibrary => '$libdir/pltcl', - tmplacl => '_null_' }, -{ tmplname => 'pltclu', tmpltrusted => 'f', tmpldbacreate => 'f', - tmplhandler => 'pltclu_call_handler', tmplinline => '_null_', - tmplvalidator => '_null_', tmpllibrary => '$libdir/pltcl', - tmplacl => '_null_' }, -{ tmplname => 'plperl', tmpltrusted => 't', tmpldbacreate => 't', - tmplhandler => 'plperl_call_handler', tmplinline => 'plperl_inline_handler', - tmplvalidator => 'plperl_validator', tmpllibrary => '$libdir/plperl', - tmplacl => '_null_' }, -{ tmplname => 'plperlu', tmpltrusted => 'f', tmpldbacreate => 'f', - tmplhandler => 'plperlu_call_handler', tmplinline => 'plperlu_inline_handler', - tmplvalidator => 'plperlu_validator', tmpllibrary => '$libdir/plperl', - tmplacl => '_null_' }, -{ tmplname => 'plpythonu', tmpltrusted => 'f', tmpldbacreate => 'f', - tmplhandler => 'plpython_call_handler', - tmplinline => 'plpython_inline_handler', - tmplvalidator => 'plpython_validator', tmpllibrary => '$libdir/plpython2', - tmplacl => '_null_' }, -{ tmplname => 'plpython2u', tmpltrusted => 'f', tmpldbacreate => 'f', - tmplhandler => 'plpython2_call_handler', - tmplinline => 'plpython2_inline_handler', - tmplvalidator => 'plpython2_validator', tmpllibrary => '$libdir/plpython2', - tmplacl => '_null_' }, -{ tmplname => 'plpython3u', tmpltrusted => 'f', tmpldbacreate => 'f', - tmplhandler => 'plpython3_call_handler', - tmplinline => 'plpython3_inline_handler', - tmplvalidator => 'plpython3_validator', tmpllibrary => '$libdir/plpython3', - tmplacl => '_null_' }, - -] diff --git a/src/include/catalog/pg_pltemplate.h b/src/include/catalog/pg_pltemplate.h deleted file mode 100644 index b930730e481..00000000000 --- a/src/include/catalog/pg_pltemplate.h +++ /dev/null @@ -1,52 +0,0 @@ -/*------------------------------------------------------------------------- - * - * pg_pltemplate.h - * definition of the "PL template" system catalog (pg_pltemplate) - * - * - * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * src/include/catalog/pg_pltemplate.h - * - * NOTES - * The Catalog.pm module reads this file and derives schema - * information. - * - *------------------------------------------------------------------------- - */ -#ifndef PG_PLTEMPLATE_H -#define PG_PLTEMPLATE_H - -#include "catalog/genbki.h" -#include "catalog/pg_pltemplate_d.h" - -/* ---------------- - * pg_pltemplate definition. cpp turns this into - * typedef struct FormData_pg_pltemplate - * ---------------- - */ -CATALOG(pg_pltemplate,1136,PLTemplateRelationId) BKI_SHARED_RELATION -{ - NameData tmplname; /* name of PL */ - bool tmpltrusted; /* PL is trusted? */ - bool tmpldbacreate; /* PL is installable by db owner? */ - -#ifdef CATALOG_VARLEN /* variable-length fields start here */ - text tmplhandler BKI_FORCE_NOT_NULL; /* name of call handler - * function */ - text tmplinline; /* name of anonymous-block handler, or NULL */ - text tmplvalidator; /* name of validator function, or NULL */ - text tmpllibrary BKI_FORCE_NOT_NULL; /* path of shared library */ - aclitem tmplacl[1]; /* access privileges for template */ -#endif -} FormData_pg_pltemplate; - -/* ---------------- - * Form_pg_pltemplate corresponds to a pointer to a row with - * the format of pg_pltemplate relation. - * ---------------- - */ -typedef FormData_pg_pltemplate *Form_pg_pltemplate; - -#endif /* PG_PLTEMPLATE_H */ diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index bef50c76d9c..22282569075 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -9496,9 +9496,9 @@ proname => 'pg_available_extension_versions', procost => '10', prorows => '100', proretset => 't', provolatile => 's', prorettype => 'record', proargtypes => '', - proallargtypes => '{name,text,bool,bool,name,_name,text}', - proargmodes => '{o,o,o,o,o,o,o}', - proargnames => '{name,version,superuser,relocatable,schema,requires,comment}', + proallargtypes => '{name,text,bool,bool,bool,name,_name,text}', + proargmodes => '{o,o,o,o,o,o,o,o}', + proargnames => '{name,version,superuser,trusted,relocatable,schema,requires,comment}', prosrc => 'pg_available_extension_versions' }, { oid => '3084', descr => 'list an extension\'s version update paths', proname => 'pg_extension_update_paths', procost => '10', prorows => '100', diff --git a/src/include/catalog/toasting.h b/src/include/catalog/toasting.h index 3281dbd7aaf..51491c45131 100644 --- a/src/include/catalog/toasting.h +++ b/src/include/catalog/toasting.h @@ -86,9 +86,6 @@ DECLARE_TOAST(pg_database, 4177, 4178); DECLARE_TOAST(pg_db_role_setting, 2966, 2967); #define PgDbRoleSettingToastTable 2966 #define PgDbRoleSettingToastIndex 2967 -DECLARE_TOAST(pg_pltemplate, 4179, 4180); -#define PgPlTemplateToastTable 4179 -#define PgPlTemplateToastIndex 4180 DECLARE_TOAST(pg_replication_origin, 4181, 4182); #define PgReplicationOriginToastTable 4181 #define PgReplicationOriginToastIndex 4182 diff --git a/src/include/commands/extension.h b/src/include/commands/extension.h index 6f10707b00c..7923cdc250d 100644 --- a/src/include/commands/extension.h +++ b/src/include/commands/extension.h @@ -47,6 +47,7 @@ extern ObjectAddress ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt * extern Oid get_extension_oid(const char *extname, bool missing_ok); extern char *get_extension_name(Oid ext_oid); +extern bool extension_file_exists(const char *extensionName); extern ObjectAddress AlterExtensionNamespace(const char *extensionName, const char *newschema, Oid *oldschema); diff --git a/src/include/commands/proclang.h b/src/include/commands/proclang.h index 9a4bc75d120..c70f8ec928e 100644 --- a/src/include/commands/proclang.h +++ b/src/include/commands/proclang.h @@ -1,11 +1,12 @@ -/* - * src/include/commands/proclang.h - * - *------------------------------------------------------------------------- +/*------------------------------------------------------------------------- * * proclang.h * prototypes for proclang.c. * + * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/commands/proclang.h * *------------------------------------------------------------------------- */ @@ -17,7 +18,7 @@ extern ObjectAddress CreateProceduralLanguage(CreatePLangStmt *stmt); extern void DropProceduralLanguageById(Oid langOid); -extern bool PLTemplateExists(const char *languageName); + extern Oid get_language_oid(const char *langname, bool missing_ok); #endif /* PROCLANG_H */ diff --git a/src/pl/plperl/GNUmakefile b/src/pl/plperl/GNUmakefile index 9b1c5141014..e4d0a0bf32e 100644 --- a/src/pl/plperl/GNUmakefile +++ b/src/pl/plperl/GNUmakefile @@ -55,8 +55,10 @@ endif # win32 SHLIB_LINK = $(perl_embed_ldflags) -REGRESS_OPTS = --dbname=$(PL_TESTDB) --load-extension=plperl --load-extension=plperlu -REGRESS = plperl plperl_lc plperl_trigger plperl_shared plperl_elog plperl_util plperl_init plperlu plperl_array plperl_call plperl_transaction +REGRESS_OPTS = --dbname=$(PL_TESTDB) +REGRESS = plperl_setup plperl plperl_lc plperl_trigger plperl_shared \ + plperl_elog plperl_util plperl_init plperlu plperl_array \ + plperl_call plperl_transaction # if Perl can support two interpreters in one backend, # test plperl-and-plperlu cases ifneq ($(PERL),) diff --git a/src/pl/plperl/expected/plperl_setup.out b/src/pl/plperl/expected/plperl_setup.out new file mode 100644 index 00000000000..faeb645c5f8 --- /dev/null +++ b/src/pl/plperl/expected/plperl_setup.out @@ -0,0 +1,66 @@ +-- +-- Install the plperl and plperlu extensions +-- +-- Before going ahead with the to-be-tested installations, verify that +-- a non-superuser is allowed to install plperl (but not plperlu) when +-- suitable permissions have been granted. +CREATE USER regress_user1; +CREATE USER regress_user2; +SET ROLE regress_user1; +CREATE EXTENSION plperl; -- fail +ERROR: permission denied to create extension "plperl" +HINT: Must have CREATE privilege on current database to create this extension. +CREATE EXTENSION plperlu; -- fail +ERROR: permission denied to create extension "plperlu" +HINT: Must be superuser to create this extension. +RESET ROLE; +DO $$ +begin + execute format('grant create on database %I to regress_user1', + current_database()); +end; +$$; +SET ROLE regress_user1; +CREATE EXTENSION plperl; +CREATE EXTENSION plperlu; -- fail +ERROR: permission denied to create extension "plperlu" +HINT: Must be superuser to create this extension. +CREATE FUNCTION foo1() returns int language plperl as '1;'; +SELECT foo1(); + foo1 +------ + 1 +(1 row) + +-- Should be able to change privileges on the language +revoke all on language plperl from public; +SET ROLE regress_user2; +CREATE FUNCTION foo2() returns int language plperl as '2;'; -- fail +ERROR: permission denied for language plperl +SET ROLE regress_user1; +grant usage on language plperl to regress_user2; +SET ROLE regress_user2; +CREATE FUNCTION foo2() returns int language plperl as '2;'; +SELECT foo2(); + foo2 +------ + 2 +(1 row) + +SET ROLE regress_user1; +-- Should be able to drop the extension, but not the language per se +DROP LANGUAGE plperl CASCADE; +ERROR: cannot drop language plperl because extension plperl requires it +HINT: You can drop extension plperl instead. +DROP EXTENSION plperl CASCADE; +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to function foo1() +drop cascades to function foo2() +-- Clean up +RESET ROLE; +DROP OWNED BY regress_user1; +DROP USER regress_user1; +DROP USER regress_user2; +-- Now install the versions that will be used by subsequent test scripts. +CREATE EXTENSION plperl; +CREATE EXTENSION plperlu; diff --git a/src/pl/plperl/plperl--1.0.sql b/src/pl/plperl/plperl--1.0.sql index f716ba1c563..5ff31e74e55 100644 --- a/src/pl/plperl/plperl--1.0.sql +++ b/src/pl/plperl/plperl--1.0.sql @@ -1,11 +1,20 @@ /* src/pl/plperl/plperl--1.0.sql */ -/* - * Currently, all the interesting stuff is done by CREATE LANGUAGE. - * Later we will probably "dumb down" that command and put more of the - * knowledge into this script. - */ +CREATE FUNCTION plperl_call_handler() RETURNS language_handler + LANGUAGE c AS 'MODULE_PATHNAME'; -CREATE LANGUAGE plperl; +CREATE FUNCTION plperl_inline_handler(internal) RETURNS void + STRICT LANGUAGE c AS 'MODULE_PATHNAME'; + +CREATE FUNCTION plperl_validator(oid) RETURNS void + STRICT LANGUAGE c AS 'MODULE_PATHNAME'; + +CREATE TRUSTED LANGUAGE plperl + HANDLER plperl_call_handler + INLINE plperl_inline_handler + VALIDATOR plperl_validator; + +-- The language object, but not the functions, can be owned by a non-superuser. +ALTER LANGUAGE plperl OWNER TO @extowner@; COMMENT ON LANGUAGE plperl IS 'PL/Perl procedural language'; diff --git a/src/pl/plperl/plperl.control b/src/pl/plperl/plperl.control index 6faace12fa5..3a2230a151d 100644 --- a/src/pl/plperl/plperl.control +++ b/src/pl/plperl/plperl.control @@ -4,4 +4,5 @@ default_version = '1.0' module_pathname = '$libdir/plperl' relocatable = false schema = pg_catalog -superuser = false +superuser = true +trusted = true diff --git a/src/pl/plperl/plperlu--1.0.sql b/src/pl/plperl/plperlu--1.0.sql index 7efb4fbc5bf..10d75945a16 100644 --- a/src/pl/plperl/plperlu--1.0.sql +++ b/src/pl/plperl/plperlu--1.0.sql @@ -1,11 +1,17 @@ /* src/pl/plperl/plperlu--1.0.sql */ -/* - * Currently, all the interesting stuff is done by CREATE LANGUAGE. - * Later we will probably "dumb down" that command and put more of the - * knowledge into this script. - */ +CREATE FUNCTION plperlu_call_handler() RETURNS language_handler + LANGUAGE c AS 'MODULE_PATHNAME'; -CREATE LANGUAGE plperlu; +CREATE FUNCTION plperlu_inline_handler(internal) RETURNS void + STRICT LANGUAGE c AS 'MODULE_PATHNAME'; + +CREATE FUNCTION plperlu_validator(oid) RETURNS void + STRICT LANGUAGE c AS 'MODULE_PATHNAME'; + +CREATE LANGUAGE plperlu + HANDLER plperlu_call_handler + INLINE plperlu_inline_handler + VALIDATOR plperlu_validator; COMMENT ON LANGUAGE plperlu IS 'PL/PerlU untrusted procedural language'; diff --git a/src/pl/plperl/sql/plperl_setup.sql b/src/pl/plperl/sql/plperl_setup.sql new file mode 100644 index 00000000000..ae48feae863 --- /dev/null +++ b/src/pl/plperl/sql/plperl_setup.sql @@ -0,0 +1,64 @@ +-- +-- Install the plperl and plperlu extensions +-- + +-- Before going ahead with the to-be-tested installations, verify that +-- a non-superuser is allowed to install plperl (but not plperlu) when +-- suitable permissions have been granted. + +CREATE USER regress_user1; +CREATE USER regress_user2; + +SET ROLE regress_user1; + +CREATE EXTENSION plperl; -- fail +CREATE EXTENSION plperlu; -- fail + +RESET ROLE; + +DO $$ +begin + execute format('grant create on database %I to regress_user1', + current_database()); +end; +$$; + +SET ROLE regress_user1; + +CREATE EXTENSION plperl; +CREATE EXTENSION plperlu; -- fail + +CREATE FUNCTION foo1() returns int language plperl as '1;'; +SELECT foo1(); + +-- Should be able to change privileges on the language +revoke all on language plperl from public; + +SET ROLE regress_user2; + +CREATE FUNCTION foo2() returns int language plperl as '2;'; -- fail + +SET ROLE regress_user1; + +grant usage on language plperl to regress_user2; + +SET ROLE regress_user2; + +CREATE FUNCTION foo2() returns int language plperl as '2;'; +SELECT foo2(); + +SET ROLE regress_user1; + +-- Should be able to drop the extension, but not the language per se +DROP LANGUAGE plperl CASCADE; +DROP EXTENSION plperl CASCADE; + +-- Clean up +RESET ROLE; +DROP OWNED BY regress_user1; +DROP USER regress_user1; +DROP USER regress_user2; + +-- Now install the versions that will be used by subsequent test scripts. +CREATE EXTENSION plperl; +CREATE EXTENSION plperlu; diff --git a/src/pl/plpgsql/src/plpgsql--1.0.sql b/src/pl/plpgsql/src/plpgsql--1.0.sql index ab6fa84ab0d..6e5b990fccc 100644 --- a/src/pl/plpgsql/src/plpgsql--1.0.sql +++ b/src/pl/plpgsql/src/plpgsql--1.0.sql @@ -1,11 +1,20 @@ /* src/pl/plpgsql/src/plpgsql--1.0.sql */ -/* - * Currently, all the interesting stuff is done by CREATE LANGUAGE. - * Later we will probably "dumb down" that command and put more of the - * knowledge into this script. - */ +CREATE FUNCTION plpgsql_call_handler() RETURNS language_handler + LANGUAGE c AS 'MODULE_PATHNAME'; -CREATE LANGUAGE plpgsql; +CREATE FUNCTION plpgsql_inline_handler(internal) RETURNS void + STRICT LANGUAGE c AS 'MODULE_PATHNAME'; + +CREATE FUNCTION plpgsql_validator(oid) RETURNS void + STRICT LANGUAGE c AS 'MODULE_PATHNAME'; + +CREATE TRUSTED LANGUAGE plpgsql + HANDLER plpgsql_call_handler + INLINE plpgsql_inline_handler + VALIDATOR plpgsql_validator; + +-- The language object, but not the functions, can be owned by a non-superuser. +ALTER LANGUAGE plpgsql OWNER TO @extowner@; COMMENT ON LANGUAGE plpgsql IS 'PL/pgSQL procedural language'; diff --git a/src/pl/plpgsql/src/plpgsql.control b/src/pl/plpgsql/src/plpgsql.control index b320227b120..42e764bf36b 100644 --- a/src/pl/plpgsql/src/plpgsql.control +++ b/src/pl/plpgsql/src/plpgsql.control @@ -4,4 +4,5 @@ default_version = '1.0' module_pathname = '$libdir/plpgsql' relocatable = false schema = pg_catalog -superuser = false +superuser = true +trusted = true diff --git a/src/pl/plpython/plpy_main.c b/src/pl/plpython/plpy_main.c index faaec559a22..882d69e14a1 100644 --- a/src/pl/plpython/plpy_main.c +++ b/src/pl/plpython/plpy_main.c @@ -29,7 +29,7 @@ */ #if PY_MAJOR_VERSION >= 3 -/* Use separate names to avoid clash in pg_pltemplate */ +/* Use separate names to reduce confusion */ #define plpython_validator plpython3_validator #define plpython_call_handler plpython3_call_handler #define plpython_inline_handler plpython3_inline_handler diff --git a/src/pl/plpython/plpython2u--1.0.sql b/src/pl/plpython/plpython2u--1.0.sql index 661cc66a891..69f74775678 100644 --- a/src/pl/plpython/plpython2u--1.0.sql +++ b/src/pl/plpython/plpython2u--1.0.sql @@ -1,11 +1,17 @@ /* src/pl/plpython/plpython2u--1.0.sql */ -/* - * Currently, all the interesting stuff is done by CREATE LANGUAGE. - * Later we will probably "dumb down" that command and put more of the - * knowledge into this script. - */ +CREATE FUNCTION plpython2_call_handler() RETURNS language_handler + LANGUAGE c AS 'MODULE_PATHNAME'; -CREATE LANGUAGE plpython2u; +CREATE FUNCTION plpython2_inline_handler(internal) RETURNS void + STRICT LANGUAGE c AS 'MODULE_PATHNAME'; + +CREATE FUNCTION plpython2_validator(oid) RETURNS void + STRICT LANGUAGE c AS 'MODULE_PATHNAME'; + +CREATE LANGUAGE plpython2u + HANDLER plpython2_call_handler + INLINE plpython2_inline_handler + VALIDATOR plpython2_validator; COMMENT ON LANGUAGE plpython2u IS 'PL/Python2U untrusted procedural language'; diff --git a/src/pl/plpython/plpython3u--1.0.sql b/src/pl/plpython/plpython3u--1.0.sql index c0d6ea82c2d..ba2e6ac076b 100644 --- a/src/pl/plpython/plpython3u--1.0.sql +++ b/src/pl/plpython/plpython3u--1.0.sql @@ -1,11 +1,17 @@ /* src/pl/plpython/plpython3u--1.0.sql */ -/* - * Currently, all the interesting stuff is done by CREATE LANGUAGE. - * Later we will probably "dumb down" that command and put more of the - * knowledge into this script. - */ +CREATE FUNCTION plpython3_call_handler() RETURNS language_handler + LANGUAGE c AS 'MODULE_PATHNAME'; -CREATE LANGUAGE plpython3u; +CREATE FUNCTION plpython3_inline_handler(internal) RETURNS void + STRICT LANGUAGE c AS 'MODULE_PATHNAME'; + +CREATE FUNCTION plpython3_validator(oid) RETURNS void + STRICT LANGUAGE c AS 'MODULE_PATHNAME'; + +CREATE LANGUAGE plpython3u + HANDLER plpython3_call_handler + INLINE plpython3_inline_handler + VALIDATOR plpython3_validator; COMMENT ON LANGUAGE plpython3u IS 'PL/Python3U untrusted procedural language'; diff --git a/src/pl/plpython/plpythonu--1.0.sql b/src/pl/plpython/plpythonu--1.0.sql index 4a3e64aac50..4c6f7c3f140 100644 --- a/src/pl/plpython/plpythonu--1.0.sql +++ b/src/pl/plpython/plpythonu--1.0.sql @@ -1,11 +1,17 @@ /* src/pl/plpython/plpythonu--1.0.sql */ -/* - * Currently, all the interesting stuff is done by CREATE LANGUAGE. - * Later we will probably "dumb down" that command and put more of the - * knowledge into this script. - */ +CREATE FUNCTION plpython_call_handler() RETURNS language_handler + LANGUAGE c AS 'MODULE_PATHNAME'; -CREATE LANGUAGE plpythonu; +CREATE FUNCTION plpython_inline_handler(internal) RETURNS void + STRICT LANGUAGE c AS 'MODULE_PATHNAME'; + +CREATE FUNCTION plpython_validator(oid) RETURNS void + STRICT LANGUAGE c AS 'MODULE_PATHNAME'; + +CREATE LANGUAGE plpythonu + HANDLER plpython_call_handler + INLINE plpython_inline_handler + VALIDATOR plpython_validator; COMMENT ON LANGUAGE plpythonu IS 'PL/PythonU untrusted procedural language'; diff --git a/src/pl/tcl/pltcl--1.0.sql b/src/pl/tcl/pltcl--1.0.sql index 34a68c8471f..2ed2b920c8e 100644 --- a/src/pl/tcl/pltcl--1.0.sql +++ b/src/pl/tcl/pltcl--1.0.sql @@ -1,11 +1,12 @@ /* src/pl/tcl/pltcl--1.0.sql */ -/* - * Currently, all the interesting stuff is done by CREATE LANGUAGE. - * Later we will probably "dumb down" that command and put more of the - * knowledge into this script. - */ +CREATE FUNCTION pltcl_call_handler() RETURNS language_handler + LANGUAGE c AS 'MODULE_PATHNAME'; -CREATE LANGUAGE pltcl; +CREATE TRUSTED LANGUAGE pltcl + HANDLER pltcl_call_handler; + +-- The language object, but not the functions, can be owned by a non-superuser. +ALTER LANGUAGE pltcl OWNER TO @extowner@; COMMENT ON LANGUAGE pltcl IS 'PL/Tcl procedural language'; diff --git a/src/pl/tcl/pltcl.control b/src/pl/tcl/pltcl.control index b9dc1b8a138..1568c17e09d 100644 --- a/src/pl/tcl/pltcl.control +++ b/src/pl/tcl/pltcl.control @@ -4,4 +4,5 @@ default_version = '1.0' module_pathname = '$libdir/pltcl' relocatable = false schema = pg_catalog -superuser = false +superuser = true +trusted = true diff --git a/src/pl/tcl/pltclu--1.0.sql b/src/pl/tcl/pltclu--1.0.sql index e05b470aaf2..fca869f7f2d 100644 --- a/src/pl/tcl/pltclu--1.0.sql +++ b/src/pl/tcl/pltclu--1.0.sql @@ -1,11 +1,9 @@ /* src/pl/tcl/pltclu--1.0.sql */ -/* - * Currently, all the interesting stuff is done by CREATE LANGUAGE. - * Later we will probably "dumb down" that command and put more of the - * knowledge into this script. - */ +CREATE FUNCTION pltclu_call_handler() RETURNS language_handler + LANGUAGE c AS 'MODULE_PATHNAME'; -CREATE LANGUAGE pltclu; +CREATE LANGUAGE pltclu + HANDLER pltclu_call_handler; COMMENT ON LANGUAGE pltclu IS 'PL/TclU untrusted procedural language'; diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 70e1e2f78d1..2ab2115fa1a 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1311,11 +1311,12 @@ pg_available_extension_versions| SELECT e.name, e.version, (x.extname IS NOT NULL) AS installed, e.superuser, + e.trusted, e.relocatable, e.schema, e.requires, e.comment - FROM (pg_available_extension_versions() e(name, version, superuser, relocatable, schema, requires, comment) + FROM (pg_available_extension_versions() e(name, version, superuser, trusted, relocatable, schema, requires, comment) LEFT JOIN pg_extension x ON (((e.name = x.extname) AND (e.version = x.extversion)))); pg_available_extensions| SELECT e.name, e.default_version, diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out index 070de78e85c..f0cd40b6607 100644 --- a/src/test/regress/expected/sanity_check.out +++ b/src/test/regress/expected/sanity_check.out @@ -134,7 +134,6 @@ pg_opclass|t pg_operator|t pg_opfamily|t pg_partitioned_table|t -pg_pltemplate|t pg_policy|t pg_proc|t pg_publication|t |