From: Tom Lane <tgl@sss.pgh.pa.us>
To: Peter Eisentraut <peter.eisentraut@2ndquadrant.com>
Cc: Stephen Frost <sfrost@snowman.net>
Cc: pgsql-hackers@lists.postgresql.org
Subject: Re: Removing pg_pltemplate and creating "trustable" extensions
Date: Sun, 05 Jan 2020 17:51:37 -0500
Message-ID: <28717.1578264697@sss.pgh.pa.us> (raw)
In-Reply-To: <10953.1573852937@sss.pgh.pa.us>
References: <5889.1566415762@sss.pgh.pa.us>
<c15c0f58-79a9-4051-1f43-8fc06564df69@2ndquadrant.com>
<27727.1573052802@sss.pgh.pa.us>
<20191107183856.GY6962@tamriel.snowman.net>
<31658.1573152852@sss.pgh.pa.us>
<20191107191320.GZ6962@tamriel.snowman.net>
<2101.1573155932@sss.pgh.pa.us>
<28987.1573331052@sss.pgh.pa.us>
<10953.1573852937@sss.pgh.pa.us>
I wrote:
> The cfbot noticed that a couple of patches committed this week
> created (trivial) conflicts with this patchset. Here's a v3
> rebased up to HEAD; no interesting changes.
The 2020 copyright update broke this patchset again. Here's a rebase.
No changes except for some minor rearrangement of the CREATE LANGUAGE
man page in 0003.
regards, tom lane
Attachments:
[text/x-diff] 0001-invent-trusted-extensions-4.patch (17.9K, ../28717.1578264697@sss.pgh.pa.us/2-0001-invent-trusted-extensions-4.patch)
download | inline diff:diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgmlindex 55694c4..a573dfb 100644--- a/doc/src/sgml/catalogs.sgml+++ b/doc/src/sgml/catalogs.sgml@@ -8514,7 +8514,15 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
<row>
<entry><structfield>superuser</structfield></entry>
<entry><type>bool</type></entry>
- <entry>True if only superusers are allowed to install this extension</entry>+ <entry>True if only superusers are allowed to install this extension+ (but see <structfield>trusted</structfield>)</entry>+ </row>++ <row>+ <entry><structfield>trusted</structfield></entry>+ <entry><type>bool</type></entry>+ <entry>True if the extension can be installed by non-superusers+ with appropriate privileges</entry>
</row>
<row>
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgmlindex a3046f2..e2807d0 100644--- a/doc/src/sgml/extend.sgml+++ b/doc/src/sgml/extend.sgml@@ -576,6 +576,32 @@
version. If it is set to <literal>false</literal>, just the privileges
required to execute the commands in the installation or update script
are required.
+ This should normally be set to <literal>true</literal> if any of the+ script commands require superuser privileges. (Such commands would+ fail anyway, but it's more user-friendly to give the error up front.)+ </para>+ </listitem>+ </varlistentry>++ <varlistentry>+ <term><varname>trusted</varname> (<type>boolean</type>)</term>+ <listitem>+ <para>+ This parameter, if set to <literal>true</literal> (which is not the+ default), allows some non-superusers to install an extension that+ has <varname>superuser</varname> set to <literal>true</literal>.+ Specifically, installation will be permitted for the owner of the+ current database, and for anyone who has been granted+ the <literal>pg_install_trusted_extension</literal> role.+ When the user executing <command>CREATE EXTENSION</command> is not+ a superuser but is allowed to install by virtue of this parameter,+ then the installation or update script is run as the bootstrap+ superuser, not as the calling user.+ This parameter is irrelevant if <varname>superuser</varname> is+ <literal>false</literal>.+ Generally, this should not be set true for extensions that could+ allow access to otherwise-superuser-only abilities, such as+ filesystem access.
</para>
</listitem>
</varlistentry>
@@ -642,6 +668,18 @@
</para>
<para>
+ If the extension script contains the+ string <literal>@extowner@</literal>, that string is replaced with the+ (suitably quoted) name of the user calling <command>CREATE+ EXTENSION</command> or <command>ALTER EXTENSION</command>. Typically+ this feature is used by extensions that are marked trusted to assign+ ownership of selected objects to the calling user rather than the+ bootstrap superuser. (One should be careful about doing so, however.+ For example, assigning ownership of a C-language function to a+ non-superuser would create a privilege escalation path for that user.)+ </para>++ <para>
While the script files can contain any characters allowed by the specified
encoding, control files should contain only plain ASCII, because there
is no way for <productname>PostgreSQL</productname> to know what encoding a
diff --git a/doc/src/sgml/ref/create_extension.sgml b/doc/src/sgml/ref/create_extension.sgmlindex 36837f9..7cd4346 100644--- a/doc/src/sgml/ref/create_extension.sgml+++ b/doc/src/sgml/ref/create_extension.sgml@@ -47,14 +47,26 @@ CREATE EXTENSION [ IF NOT EXISTS ] <replaceable class="parameter">extension_name
</para>
<para>
- Loading an extension requires the same privileges that would be- required to create its component objects. For most extensions this- means superuser or database owner privileges are needed.
The user who runs <command>CREATE EXTENSION</command> becomes the
owner of the extension for purposes of later privilege checks, as well
as the owner of any objects created by the extension's script.
</para>
+ <para>+ Loading an extension ordinarily requires the same privileges that would+ be required to create its component objects. For many extensions this+ means superuser privileges are needed.+ However, if the extension is marked <firstterm>trusted</firstterm> in+ its control file, then it can be installed by a non-superuser who has+ suitable privileges (that is, owns the current database or has been+ granted the <literal>pg_install_trusted_extension</literal> role). In+ this case the extension object itself will be owned by the calling user,+ but the contained objects will be owned by the bootstrap superuser+ (unless the extension's script explicitly assigns them to the calling+ user). This configuration gives the calling user the right to drop the+ extension, but not to modify individual objects within it.+ </para>+
</refsect1>
<refsect1>
diff --git a/doc/src/sgml/user-manag.sgml b/doc/src/sgml/user-manag.sgmlindex 66f1627..90f637f 100644--- a/doc/src/sgml/user-manag.sgml+++ b/doc/src/sgml/user-manag.sgml@@ -556,6 +556,10 @@ DROP ROLE doomed_role;
<entry>Allow executing programs on the database server as the user the database runs as with
COPY and other functions which allow executing a server-side program.</entry>
</row>
+ <row>+ <entry>pg_install_trusted_extension</entry>+ <entry>Allow installation of <quote>trusted</quote> extensions.</entry>+ </row>
</tbody>
</tgroup>
</table>
@@ -589,6 +593,17 @@ DROP ROLE doomed_role;
</para>
<para>
+ The <literal>pg_install_trusted_extension</literal> role allows grantees+ to install <link linkend="extend-extensions">extensions</link> that are+ marked <quote>trusted</quote> in their control files. This is a+ privilege that is available automatically to database owners, but+ granting this role allows administrators to let other non-superuser roles+ do it too. Generally, unless the <quote>trusted</quote> marking has been+ applied to extensions incautiously, granting this role carries no large+ security risk.+ </para>++ <para>
Care should be taken when granting these roles to ensure they are only used where
needed and with the understanding that these roles grant access to privileged
information.
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sqlindex 2fc3e3f..12619d1 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.cindex 01de398..339ba5b 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)+{+ /* Never trust unless extension's control file says it's okay */+ if (!control->trusted)+ return false;+ /* Database owner can install */+ if (pg_database_ownercheck(MyDatabaseId, GetUserId()))+ return true;+ /* Members of pg_install_trusted_extension can install */+ if (has_privs_of_role(GetUserId(), DEFAULT_ROLE_INSTALL_TRUSTED_EXTENSION))+ 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 be database owner or member of pg_install_trusted_extension 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 be database owner or member of pg_install_trusted_extension 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 */
diff --git a/src/include/catalog/pg_authid.dat b/src/include/catalog/pg_authid.datindex 7c08851..a0df297 100644--- a/src/include/catalog/pg_authid.dat+++ b/src/include/catalog/pg_authid.dat@@ -60,5 +60,10 @@
rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f',
rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1',
rolpassword => '_null_', rolvaliduntil => '_null_' },
+{ oid => '9495', oid_symbol => 'DEFAULT_ROLE_INSTALL_TRUSTED_EXTENSION',+ rolname => 'pg_install_trusted_extension', rolsuper => 'f', rolinherit => 't',+ rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f',+ rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1',+ rolpassword => '_null_', rolvaliduntil => '_null_' },
]
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.datindex 0b6045a..d394e8d 100644--- a/src/include/catalog/pg_proc.dat+++ b/src/include/catalog/pg_proc.dat@@ -9457,9 +9457,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/test/regress/expected/rules.out b/src/test/regress/expected/rules.outindex 80a0782..adb2070 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,
[text/x-diff] 0002-make-pls-pure-extensions-4.patch (13.0K, ../28717.1578264697@sss.pgh.pa.us/3-0002-make-pls-pure-extensions-4.patch)
download | inline diff:diff --git a/src/pl/plperl/GNUmakefile b/src/pl/plperl/GNUmakefileindex 9b1c514..e4d0a0b 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.outnew file mode 100644index 0000000..7cb1f25--- /dev/null+++ b/src/pl/plperl/expected/plperl_setup.out@@ -0,0 +1,60 @@+--+-- 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 be database owner or member of pg_install_trusted_extension 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;+GRANT pg_install_trusted_extension TO regress_user1;+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 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.sqlindex f716ba1..5ff31e7 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.controlindex 6faace1..3a2230a 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 = truediff --git a/src/pl/plperl/plperlu--1.0.sql b/src/pl/plperl/plperlu--1.0.sqlindex 7efb4fb..10d7594 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.sqlnew file mode 100644index 0000000..9c7b52c--- /dev/null+++ b/src/pl/plperl/sql/plperl_setup.sql@@ -0,0 +1,58 @@+--+-- 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;++GRANT pg_install_trusted_extension TO regress_user1;++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 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.sqlindex ab6fa84..6e5b990 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.controlindex b320227..42e764b 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 = truediff --git a/src/pl/plpython/plpython2u--1.0.sql b/src/pl/plpython/plpython2u--1.0.sqlindex 661cc66..69f7477 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.sqlindex c0d6ea8..ba2e6ac 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.sqlindex 4a3e64a..4c6f7c3 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.sqlindex 34a68c8..2ed2b92 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.controlindex b9dc1b8..1568c17 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 = truediff --git a/src/pl/tcl/pltclu--1.0.sql b/src/pl/tcl/pltclu--1.0.sqlindex e05b470..fca869f 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';
[text/x-diff] 0003-interpret-create-lang-as-create-ext-4.patch (28.9K, ../28717.1578264697@sss.pgh.pa.us/4-0003-interpret-create-lang-as-create-ext-4.patch)
download | inline diff:diff --git a/doc/src/sgml/ref/create_language.sgml b/doc/src/sgml/ref/create_language.sgmlindex 13b28b1..af9d115 100644--- a/doc/src/sgml/ref/create_language.sgml+++ b/doc/src/sgml/ref/create_language.sgml@@ -21,9 +21,9 @@ PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
-CREATE [ OR REPLACE ] [ PROCEDURAL ] LANGUAGE <replaceable class="parameter">name</replaceable>
CREATE [ OR REPLACE ] [ TRUSTED ] [ PROCEDURAL ] LANGUAGE <replaceable class="parameter">name</replaceable>
HANDLER <replaceable class="parameter">call_handler</replaceable> [ INLINE <replaceable class="parameter">inline_handler</replaceable> ] [ VALIDATOR <replaceable>valfunction</replaceable> ]
+CREATE [ OR REPLACE ] [ TRUSTED ] [ PROCEDURAL ] LANGUAGE <replaceable class="parameter">name</replaceable>
</synopsis>
</refsynopsisdiv>
@@ -37,21 +37,6 @@ CREATE [ OR REPLACE ] [ TRUSTED ] [ PROCEDURAL ] LANGUAGE <replaceable class="pa
defined in this new language.
</para>
- <note>- <para>- As of <productname>PostgreSQL</productname> 9.1, most procedural- languages have been made into <quote>extensions</quote>, and should- therefore be installed with <xref linkend="sql-createextension"/>- not <command>CREATE LANGUAGE</command>. Direct use of- <command>CREATE LANGUAGE</command> should now be confined to- extension installation scripts. If you have a <quote>bare</quote>- language in your database, perhaps as a result of an upgrade,- you can convert it to an extension using- <literal>CREATE EXTENSION <replaceable>langname</replaceable> FROM- unpackaged</literal>.- </para>- </note>-
<para>
<command>CREATE LANGUAGE</command> effectively associates the
language name with handler function(s) that are responsible for executing
@@ -60,53 +45,32 @@ CREATE [ OR REPLACE ] [ TRUSTED ] [ PROCEDURAL ] LANGUAGE <replaceable class="pa
</para>
<para>
- There are two forms of the <command>CREATE LANGUAGE</command> command.- In the first form, the user supplies just the name of the desired- language, and the <productname>PostgreSQL</productname> server consults- the <link linkend="catalog-pg-pltemplate"><structname>pg_pltemplate</structname></link>- system catalog to determine the correct parameters. In the second form,- the user supplies the language parameters along with the language name.- The second form can be used to create a language that is not defined in- <structname>pg_pltemplate</structname>, but this approach is considered obsolescent.- </para>-- <para>- When the server finds an entry in the <structname>pg_pltemplate</structname> catalog- for the given language name, it will use the catalog data even if the- command includes language parameters. This behavior simplifies loading of- old dump files, which are likely to contain out-of-date information- about language support functions.+ <command>CREATE OR REPLACE LANGUAGE</command> will either create a+ new language, or replace an existing definition. If the language+ already exists, its parameters are updated according to the command,+ but the language's ownership and permissions settings do not change,+ and any existing functions written in the language are assumed to still+ be valid.
</para>
<para>
- Ordinarily, the user must have the+ One must have the
<productname>PostgreSQL</productname> superuser privilege to
- register a new language. However, the owner of a database can register- a new language within that database if the language is listed in- the <structname>pg_pltemplate</structname> catalog and is marked- as allowed to be created by database owners (<structfield>tmpldbacreate</structfield>- is true). The default is that trusted languages can be created- by database owners, but this can be adjusted by superusers by modifying- the contents of <structname>pg_pltemplate</structname>.- The creator of a language becomes its owner and can later- drop it, rename it, or assign it to a new owner.+ register a new language or change an existing language's parameters.+ However, once the language is created it is valid to assign ownership of+ it to a non-superuser, who may then drop it, change its permissions,+ rename it, or assign it to a new owner. (Do not, however, assign+ ownership of the underlying C functions to a non-superuser; that would+ create a privilege escalation path for that user.)
</para>
<para>
- <command>CREATE OR REPLACE LANGUAGE</command> will either create a- new language, or replace an existing definition. If the language- already exists, its parameters are updated according to the values- specified or taken from <structname>pg_pltemplate</structname>,- but the language's ownership and permissions settings do not change,- and any existing functions written in the language are assumed to still- be valid. In addition to the normal privilege requirements for creating- a language, the user must be superuser or owner of the existing language.- The <literal>REPLACE</literal> case is mainly meant to be used to- ensure that the language exists. If the language has a- <structname>pg_pltemplate</structname> entry then <literal>REPLACE</literal>- will not actually change anything about an existing definition, except in- the unusual case where the <structname>pg_pltemplate</structname> entry- has been modified since the language was created.+ The form of <command>CREATE LANGUAGE</command> that does not supply+ any handler function is obsolete. For backwards compatibility with+ old dump files, it is interpreted as <command>CREATE EXTENSION</command>.+ That will work if the language has been packaged into an extension of+ the same name, which is the conventional way to set up procedural+ languages.
</para>
</refsect1>
@@ -218,12 +182,6 @@ CREATE [ OR REPLACE ] [ TRUSTED ] [ PROCEDURAL ] LANGUAGE <replaceable class="pa
</listitem>
</varlistentry>
</variablelist>
-- <para>- The <literal>TRUSTED</literal> option and the support function name(s) are- ignored if the server has an entry for the specified language- name in <structname>pg_pltemplate</structname>.- </para>
</refsect1>
<refsect1 id="sql-createlanguage-notes">
@@ -255,18 +213,6 @@ CREATE [ OR REPLACE ] [ TRUSTED ] [ PROCEDURAL ] LANGUAGE <replaceable class="pa
</para>
<para>
- The call handler function, the inline handler function (if any),- and the validator function (if any)- must already exist if the server does not have an entry for the language- in <structname>pg_pltemplate</structname>. But when there is an entry,- the functions need not already exist;- they will be automatically defined if not present in the database.- (This might result in <command>CREATE LANGUAGE</command> failing, if the- shared library that implements the language is not available in- the installation.)- </para>-- <para>
In <productname>PostgreSQL</productname> versions before 7.3, it was
necessary to declare handler functions as returning the placeholder
type <type>opaque</type>, rather than <type>language_handler</type>.
@@ -281,23 +227,20 @@ CREATE [ OR REPLACE ] [ TRUSTED ] [ PROCEDURAL ] LANGUAGE <replaceable class="pa
<title>Examples</title>
<para>
- The preferred way of creating any of the standard procedural languages- is just:-<programlisting>-CREATE LANGUAGE plperl;-</programlisting>- </para>-- <para>- For a language not known in the <structname>pg_pltemplate</structname> catalog, a- sequence such as this is needed:+ A minimal sequence for creating a new procedural language is:
<programlisting>
CREATE FUNCTION plsample_call_handler() RETURNS language_handler
AS '$libdir/plsample'
LANGUAGE C;
CREATE LANGUAGE plsample
HANDLER plsample_call_handler;
-</programlisting></para>+</programlisting>+ Typically that would be written in an extension's creation script,+ and users would do this to install the extension:+<programlisting>+CREATE EXTENSION plsample;+</programlisting>+ </para>
</refsect1>
<refsect1 id="sql-createlanguage-compat">
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.cindex 339ba5b..a63a39f 100644--- a/src/backend/commands/extension.c+++ b/src/backend/commands/extension.c@@ -2277,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.cindex c31c57e..0f40c9e 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.cindex cdff43d..9d72edb 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.yindex ad5be90..1bd5ed4 100644--- a/src/backend/parser/gram.y+++ b/src/backend/parser/gram.y@@ -4304,14 +4304,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/include/commands/extension.h b/src/include/commands/extension.hindex 6f10707..7923cdc 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.hindex 9a4bc75..c70f8ec 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 */
[text/x-diff] 0004-remove-pg_pltemplate-4.patch (16.6K, ../28717.1578264697@sss.pgh.pa.us/5-0004-remove-pg_pltemplate-4.patch)
download | inline diff:diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgmlindex a573dfb..03ad677 100644--- a/doc/src/sgml/catalogs.sgml+++ b/doc/src/sgml/catalogs.sgml@@ -226,11 +226,6 @@
</row>
<row>
- <entry><link linkend="catalog-pg-pltemplate"><structname>pg_pltemplate</structname></link></entry>- <entry>template data for procedural languages</entry>- </row>-- <row>
<entry><link linkend="catalog-pg-policy"><structname>pg_policy</structname></link></entry>
<entry>row-security policies</entry>
</row>
@@ -4911,113 +4906,6 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
</sect1>
- <sect1 id="catalog-pg-pltemplate">- <title><structname>pg_pltemplate</structname></title>-- <indexterm zone="catalog-pg-pltemplate">- <primary>pg_pltemplate</primary>- </indexterm>-- <para>- The catalog <structname>pg_pltemplate</structname> stores- <quote>template</quote> information for procedural languages.- A template for a language allows the language to be created in a- particular database by a simple <command>CREATE LANGUAGE</command> command,- with no need to specify implementation details.- </para>-- <para>- Unlike most system catalogs, <structname>pg_pltemplate</structname>- is shared across all databases of a cluster: there is only one- copy of <structname>pg_pltemplate</structname> per cluster, not- one per database. This allows the information to be accessible in- each database as it is needed.- </para>-- <table>- <title><structname>pg_pltemplate</structname> Columns</title>-- <tgroup cols="3">- <thead>- <row>- <entry>Name</entry>- <entry>Type</entry>- <entry>Description</entry>- </row>- </thead>-- <tbody>- <row>- <entry><structfield>tmplname</structfield></entry>- <entry><type>name</type></entry>- <entry>Name of the language this template is for</entry>- </row>-- <row>- <entry><structfield>tmpltrusted</structfield></entry>- <entry><type>boolean</type></entry>- <entry>True if language is considered trusted</entry>- </row>-- <row>- <entry><structfield>tmpldbacreate</structfield></entry>- <entry><type>boolean</type></entry>- <entry>True if language may be created by a database owner</entry>- </row>-- <row>- <entry><structfield>tmplhandler</structfield></entry>- <entry><type>text</type></entry>- <entry>Name of call handler function</entry>- </row>-- <row>- <entry><structfield>tmplinline</structfield></entry>- <entry><type>text</type></entry>- <entry>Name of anonymous-block handler function, or null if none</entry>- </row>-- <row>- <entry><structfield>tmplvalidator</structfield></entry>- <entry><type>text</type></entry>- <entry>Name of validator function, or null if none</entry>- </row>-- <row>- <entry><structfield>tmpllibrary</structfield></entry>- <entry><type>text</type></entry>- <entry>Path of shared library that implements language</entry>- </row>-- <row>- <entry><structfield>tmplacl</structfield></entry>- <entry><type>aclitem[]</type></entry>- <entry>Access privileges for template (not actually used)</entry>- </row>-- </tbody>- </tgroup>- </table>-- <para>- There are not currently any commands that manipulate procedural language- templates; to change the built-in information, a superuser must modify- the table using ordinary <command>INSERT</command>, <command>DELETE</command>,- or <command>UPDATE</command> commands.- </para>-- <note>- <para>- It is likely that <structname>pg_pltemplate</structname> will be removed in some- future release of <productname>PostgreSQL</productname>, in favor of- keeping this knowledge about procedural languages in their respective- extension installation scripts.- </para>- </note>-- </sect1>--
<sect1 id="catalog-pg-policy">
<title><structname>pg_policy</structname></title>
diff --git a/doc/src/sgml/plpython.sgml b/doc/src/sgml/plpython.sgmlindex c421092..19ecb56 100644--- a/doc/src/sgml/plpython.sgml+++ b/doc/src/sgml/plpython.sgml@@ -153,7 +153,7 @@
<para>
Daredevils, who want to build a Python-3-only operating system
environment, can change the contents of
- <link linkend="catalog-pg-pltemplate"><structname>pg_pltemplate</structname></link>+ <literal>plpythonu</literal>'s extension control and script files
to make <literal>plpythonu</literal> be equivalent
to <literal>plpython3u</literal>, keeping in mind that this
would make their installation incompatible with most of the rest
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefileindex 61db650..f6ab3ea 100644--- a/src/backend/catalog/Makefile+++ b/src/backend/catalog/Makefile@@ -60,7 +60,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 \
@@ -86,7 +86,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.cindex 16cb6d8..7d6acae 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/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.cindex 799b698..ec3e2c6 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.cindex 28638e9..d163cb2 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/indexing.h b/src/include/catalog/indexing.hindex 13e07fc..8be3038 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.datdeleted file mode 100644index 1c96b30..0000000--- 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.hdeleted file mode 100644index b930730..0000000--- 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/toasting.h b/src/include/catalog/toasting.hindex 3281dbd..51491c4 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/pl/plpython/plpy_main.c b/src/pl/plpython/plpy_main.cindex faaec55..882d69e 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/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.outindex 070de78..f0cd40b 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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: tgl@sss.pgh.pa.us, peter.eisentraut@2ndquadrant.com, sfrost@snowman.net, pgsql-hackers@lists.postgresql.org
Subject: Re: Removing pg_pltemplate and creating "trustable" extensions
In-Reply-To: <28717.1578264697@sss.pgh.pa.us>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox