<entry>information about partition key of tables</entry>
</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>
</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>
<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>
<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>
<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>
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>
extension, a state of affairs that's a bit tedious to recover from.
</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
<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
</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>
<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>
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
</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>
</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">
all subsequently-created databases.
</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
<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">
<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;
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 \
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 \
)
#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"
if (relationId == AuthIdRelationId ||
relationId == AuthMemRelationId ||
relationId == DatabaseRelationId ||
- relationId == PLTemplateRelationId ||
relationId == SharedDescriptionRelationId ||
relationId == SharedDependRelationId ||
relationId == SharedSecLabelRelationId ||
relationId == AuthMemMemRoleIndexId ||
relationId == DatabaseNameIndexId ||
relationId == DatabaseOidIndexId ||
- relationId == PLTemplateNameIndexId ||
relationId == SharedDescriptionObjIndexId ||
relationId == SharedDependDependerIndexId ||
relationId == SharedDependReferenceIndexId ||
relationId == PgDatabaseToastIndex ||
relationId == PgDbRoleSettingToastTable ||
relationId == PgDbRoleSettingToastIndex ||
- relationId == PgPlTemplateToastTable ||
- relationId == PgPlTemplateToastIndex ||
relationId == PgReplicationOriginToastTable ||
relationId == PgReplicationOriginToastIndex ||
relationId == PgShdescriptionToastTable ||
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;
#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"
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;
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);
control->name = pstrdup(extname);
control->relocatable = false;
control->superuser = true;
+ control->trusted = false;
control->encoding = -1;
/*
CommandCounterIncrement();
}
+/*
+ * 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
*
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
CStringGetTextDatum(""),
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@.
* 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);
}
/*
{
ExtensionVersionInfo *evi = (ExtensionVersionInfo *) lfirst(lc);
ExtensionControlFile *control;
- Datum values[7];
- bool nulls[7];
+ Datum values[8];
+ bool nulls[8];
ListCell *lc2;
if (!evi->installable)
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);
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 */
}
}
+/*
+ * 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
*/
#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"
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);
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);
*/
#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);
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);
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
return myself;
}
-/*
- * 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.
*/
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
}
/*
- * 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)) &&
/*
* 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);
* 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)
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 202001251
+#define CATALOG_VERSION_NO 202001291
#endif
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));
+++ /dev/null
-#----------------------------------------------------------------------
-#
-# 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_' },
-
-]
+++ /dev/null
-/*-------------------------------------------------------------------------
- *
- * 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 */
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',
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
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);
-/*
- * 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
*
*-------------------------------------------------------------------------
*/
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 */
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),)
--- /dev/null
+--
+-- 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;
/* 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';
module_pathname = '$libdir/plperl'
relocatable = false
schema = pg_catalog
-superuser = false
+superuser = true
+trusted = true
/* 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';
--- /dev/null
+--
+-- 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;
/* 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';
module_pathname = '$libdir/plpgsql'
relocatable = false
schema = pg_catalog
-superuser = false
+superuser = true
+trusted = true
*/
#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
/* 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';
/* 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';
/* 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';
/* 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';
module_pathname = '$libdir/pltcl'
relocatable = false
schema = pg_catalog
-superuser = false
+superuser = true
+trusted = true
/* 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';
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,
pg_operator|t
pg_opfamily|t
pg_partitioned_table|t
-pg_pltemplate|t
pg_policy|t
pg_proc|t
pg_publication|t