aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/catalog/Makefile4
-rw-r--r--src/backend/catalog/catalog.c5
-rw-r--r--src/backend/catalog/system_views.sql3
-rw-r--r--src/backend/commands/extension.c175
-rw-r--r--src/backend/commands/functioncmds.c5
-rw-r--r--src/backend/commands/proclang.c422
-rw-r--r--src/backend/parser/gram.y19
-rw-r--r--src/bin/pg_dump/pg_dump.c16
-rw-r--r--src/bin/pg_upgrade/function.c6
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/catalog/indexing.h3
-rw-r--r--src/include/catalog/pg_pltemplate.dat51
-rw-r--r--src/include/catalog/pg_pltemplate.h52
-rw-r--r--src/include/catalog/pg_proc.dat6
-rw-r--r--src/include/catalog/toasting.h3
-rw-r--r--src/include/commands/extension.h1
-rw-r--r--src/include/commands/proclang.h11
-rw-r--r--src/pl/plperl/GNUmakefile6
-rw-r--r--src/pl/plperl/expected/plperl_setup.out66
-rw-r--r--src/pl/plperl/plperl--1.0.sql21
-rw-r--r--src/pl/plperl/plperl.control3
-rw-r--r--src/pl/plperl/plperlu--1.0.sql18
-rw-r--r--src/pl/plperl/sql/plperl_setup.sql64
-rw-r--r--src/pl/plpgsql/src/plpgsql--1.0.sql21
-rw-r--r--src/pl/plpgsql/src/plpgsql.control3
-rw-r--r--src/pl/plpython/plpy_main.c2
-rw-r--r--src/pl/plpython/plpython2u--1.0.sql18
-rw-r--r--src/pl/plpython/plpython3u--1.0.sql18
-rw-r--r--src/pl/plpython/plpythonu--1.0.sql18
-rw-r--r--src/pl/tcl/pltcl--1.0.sql13
-rw-r--r--src/pl/tcl/pltcl.control3
-rw-r--r--src/pl/tcl/pltclu--1.0.sql10
-rw-r--r--src/test/regress/expected/rules.out3
-rw-r--r--src/test/regress/expected/sanity_check.out1
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