I wrote: > Stephen Frost <sfr...@snowman.net> writes: >> On Tue, Jan 28, 2020 at 16:17 Tom Lane <t...@sss.pgh.pa.us> wrote: >>> On the other hand, there's the point that lots of people have probably >>> given out schema-CREATE privilege to users whom they wouldn't necessarily >>> wish to trust with INSTALL privilege. Schema-CREATE is a pretty harmless >>> privilege, INSTALL much less so.
>> CREATE doesn't just control the ability to create schemas these days- it >> was extended to cover publications also not that long ago. > Oh really ... hm, that does make it a much bigger deal than I was > thinking. Given that, I don't think there's any huge objection to > attaching this to CREATE, at least till we get around to a more > significant redesign. Here's a v5 that drops the new predefined role and allows trusted-extension installation when you have CREATE on the current database. There's no other changes except a bit of documentation wordsmithing. Barring further complaints, I'm going to push this fairly soon. regards, tom lane
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 85ac79f..a10b665 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -8519,7 +8519,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/ddl.sgml b/doc/src/sgml/ddl.sgml index 3546e39..8d3a0d1 100644 --- a/doc/src/sgml/ddl.sgml +++ b/doc/src/sgml/ddl.sgml @@ -1742,6 +1742,7 @@ REVOKE ALL ON accounts FROM PUBLIC; <listitem> <para> For databases, allows new schemas and publications to be created within + the database, and allows trusted extensions to be installed within the database. </para> <para> @@ -1753,8 +1754,11 @@ REVOKE ALL ON accounts FROM PUBLIC; <para> For tablespaces, allows tables, indexes, and temporary files to be created within the tablespace, and allows databases to be created that - have the tablespace as their default tablespace. (Note that revoking - this privilege will not alter the placement of existing objects.) + have the tablespace as their default tablespace. + </para> + <para> + Note that revoking this privilege will not alter the existence or + location of existing objects. </para> </listitem> </varlistentry> diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml index a3046f2..ffe068b 100644 --- a/doc/src/sgml/extend.sgml +++ b/doc/src/sgml/extend.sgml @@ -576,6 +576,31 @@ 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 anyone who has + <literal>CREATE</literal> privilege on the current database. + 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 +667,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.sgml index 36837f9..d76ac3e 100644 --- a/doc/src/sgml/ref/create_extension.sgml +++ b/doc/src/sgml/ref/create_extension.sgml @@ -47,14 +47,25 @@ 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 any user who has + <literal>CREATE</literal> privilege on the current database. + 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/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index c9e75f4..c9e6060 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 01de398..ddd46f4 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 */ diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index bef50c7..2228256 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/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 70e1e2f..2ab2115 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/pl/plperl/GNUmakefile b/src/pl/plperl/GNUmakefile index 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.out new file mode 100644 index 0000000..faeb645 --- /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 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.control index 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 = true diff --git a/src/pl/plperl/plperlu--1.0.sql b/src/pl/plperl/plperlu--1.0.sql index 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.sql new file mode 100644 index 0000000..ae48fea --- /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 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.control index 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 = true diff --git a/src/pl/plpython/plpython2u--1.0.sql b/src/pl/plpython/plpython2u--1.0.sql index 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.sql index 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.sql index 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.sql index 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.control index 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 = true diff --git a/src/pl/tcl/pltclu--1.0.sql b/src/pl/tcl/pltclu--1.0.sql index 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';
diff --git a/doc/src/sgml/ref/create_language.sgml b/doc/src/sgml/ref/create_language.sgml index 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.c index 01de398..ddd46f4 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.c index 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.c index 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.y index ba5916b..1b0edf5 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/include/commands/extension.h b/src/include/commands/extension.h index 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.h index 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 */
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 85ac79f..a10b665 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.sgml index 7bdaf76..1921915 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/doc/src/sgml/xplang.sgml b/doc/src/sgml/xplang.sgml index 60e0430..e647e2d 100644 --- a/doc/src/sgml/xplang.sgml +++ b/doc/src/sgml/xplang.sgml @@ -190,7 +190,7 @@ CREATE FUNCTION plperl_call_handler() RETURNS language_handler AS <programlisting> CREATE FUNCTION plperl_inline_handler(internal) RETURNS void AS - '$libdir/plperl' LANGUAGE C; + '$libdir/plperl' LANGUAGE C STRICT; CREATE FUNCTION plperl_validator(oid) RETURNS void AS '$libdir/plperl' LANGUAGE C STRICT; diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile index d5da81c..f8f0b48 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 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.c index 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.c index 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.h index 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.dat deleted file mode 100644 index 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.h deleted file mode 100644 index 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.h index 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.c index 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.out index 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