aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/catalog/Makefile4
-rw-r--r--src/backend/catalog/catalog.c5
-rw-r--r--src/backend/catalog/system_views.sql3
-rw-r--r--src/backend/commands/extension.c175
-rw-r--r--src/backend/commands/functioncmds.c5
-rw-r--r--src/backend/commands/proclang.c422
-rw-r--r--src/backend/parser/gram.y19
7 files changed, 239 insertions, 394 deletions
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index d5da81c8016..f8f0b4841c3 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -58,7 +58,7 @@ CATALOG_HEADERS := \
pg_statistic_ext.h pg_statistic_ext_data.h \
pg_statistic.h pg_rewrite.h pg_trigger.h pg_event_trigger.h pg_description.h \
pg_cast.h pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \
- pg_database.h pg_db_role_setting.h pg_tablespace.h pg_pltemplate.h \
+ pg_database.h pg_db_role_setting.h pg_tablespace.h \
pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
pg_ts_parser.h pg_ts_template.h pg_extension.h \
@@ -84,7 +84,7 @@ POSTGRES_BKI_DATA = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_cast.dat pg_class.dat pg_collation.dat pg_conversion.dat \
pg_database.dat pg_language.dat \
pg_namespace.dat pg_opclass.dat pg_operator.dat pg_opfamily.dat \
- pg_pltemplate.dat pg_proc.dat pg_range.dat pg_tablespace.dat \
+ pg_proc.dat pg_range.dat pg_tablespace.dat \
pg_ts_config.dat pg_ts_config_map.dat pg_ts_dict.dat pg_ts_parser.dat \
pg_ts_template.dat pg_type.dat \
)
diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
index 16cb6d88714..7d6acaed92c 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -33,7 +33,6 @@
#include "catalog/pg_database.h"
#include "catalog/pg_db_role_setting.h"
#include "catalog/pg_namespace.h"
-#include "catalog/pg_pltemplate.h"
#include "catalog/pg_replication_origin.h"
#include "catalog/pg_shdepend.h"
#include "catalog/pg_shdescription.h"
@@ -242,7 +241,6 @@ IsSharedRelation(Oid relationId)
if (relationId == AuthIdRelationId ||
relationId == AuthMemRelationId ||
relationId == DatabaseRelationId ||
- relationId == PLTemplateRelationId ||
relationId == SharedDescriptionRelationId ||
relationId == SharedDependRelationId ||
relationId == SharedSecLabelRelationId ||
@@ -258,7 +256,6 @@ IsSharedRelation(Oid relationId)
relationId == AuthMemMemRoleIndexId ||
relationId == DatabaseNameIndexId ||
relationId == DatabaseOidIndexId ||
- relationId == PLTemplateNameIndexId ||
relationId == SharedDescriptionObjIndexId ||
relationId == SharedDependDependerIndexId ||
relationId == SharedDependReferenceIndexId ||
@@ -278,8 +275,6 @@ IsSharedRelation(Oid relationId)
relationId == PgDatabaseToastIndex ||
relationId == PgDbRoleSettingToastTable ||
relationId == PgDbRoleSettingToastIndex ||
- relationId == PgPlTemplateToastTable ||
- relationId == PgPlTemplateToastIndex ||
relationId == PgReplicationOriginToastTable ||
relationId == PgReplicationOriginToastIndex ||
relationId == PgShdescriptionToastTable ||
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index c9e75f43705..c9e60600357 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -317,7 +317,8 @@ CREATE VIEW pg_available_extensions AS
CREATE VIEW pg_available_extension_versions AS
SELECT E.name, E.version, (X.extname IS NOT NULL) AS installed,
- E.superuser, E.relocatable, E.schema, E.requires, E.comment
+ E.superuser, E.trusted, E.relocatable,
+ E.schema, E.requires, E.comment
FROM pg_available_extension_versions() AS E
LEFT JOIN pg_extension AS X
ON E.name = X.extname AND E.version = X.extversion;
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 01de398dcb3..ddd46f4e2f5 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -40,6 +40,7 @@
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
+#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_extension.h"
@@ -84,6 +85,7 @@ typedef struct ExtensionControlFile
char *schema; /* target schema (allowed if !relocatable) */
bool relocatable; /* is ALTER EXTENSION SET SCHEMA supported? */
bool superuser; /* must be superuser to install? */
+ bool trusted; /* allow becoming superuser on the fly? */
int encoding; /* encoding of the script file, or -1 */
List *requires; /* names of prerequisite extensions */
} ExtensionControlFile;
@@ -558,6 +560,14 @@ parse_extension_control_file(ExtensionControlFile *control,
errmsg("parameter \"%s\" requires a Boolean value",
item->name)));
}
+ else if (strcmp(item->name, "trusted") == 0)
+ {
+ if (!parse_bool(item->value, &control->trusted))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("parameter \"%s\" requires a Boolean value",
+ item->name)));
+ }
else if (strcmp(item->name, "encoding") == 0)
{
control->encoding = pg_valid_server_encoding(item->value);
@@ -614,6 +624,7 @@ read_extension_control_file(const char *extname)
control->name = pstrdup(extname);
control->relocatable = false;
control->superuser = true;
+ control->trusted = false;
control->encoding = -1;
/*
@@ -795,6 +806,27 @@ execute_sql_string(const char *sql)
}
/*
+ * Policy function: is the given extension trusted for installation by a
+ * non-superuser?
+ *
+ * (Update the errhint logic below if you change this.)
+ */
+static bool
+extension_is_trusted(ExtensionControlFile *control)
+{
+ AclResult aclresult;
+
+ /* Never trust unless extension's control file says it's okay */
+ if (!control->trusted)
+ return false;
+ /* Allow if user has CREATE privilege on current database */
+ aclresult = pg_database_aclcheck(MyDatabaseId, GetUserId(), ACL_CREATE);
+ if (aclresult == ACLCHECK_OK)
+ return true;
+ return false;
+}
+
+/*
* Execute the appropriate script file for installing or updating the extension
*
* If from_version isn't NULL, it's an update
@@ -806,35 +838,56 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
List *requiredSchemas,
const char *schemaName, Oid schemaOid)
{
+ bool switch_to_superuser = false;
char *filename;
+ Oid save_userid = 0;
+ int save_sec_context = 0;
int save_nestlevel;
StringInfoData pathbuf;
ListCell *lc;
/*
- * Enforce superuser-ness if appropriate. We postpone this check until
- * here so that the flag is correctly associated with the right script(s)
- * if it's set in secondary control files.
+ * Enforce superuser-ness if appropriate. We postpone these checks until
+ * here so that the control flags are correctly associated with the right
+ * script(s) if they happen to be set in secondary control files.
*/
if (control->superuser && !superuser())
{
- if (from_version == NULL)
+ if (extension_is_trusted(control))
+ switch_to_superuser = true;
+ else if (from_version == NULL)
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to create extension \"%s\"",
control->name),
- errhint("Must be superuser to create this extension.")));
+ control->trusted
+ ? errhint("Must have CREATE privilege on current database to create this extension.")
+ : errhint("Must be superuser to create this extension.")));
else
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to update extension \"%s\"",
control->name),
- errhint("Must be superuser to update this extension.")));
+ control->trusted
+ ? errhint("Must have CREATE privilege on current database to update this extension.")
+ : errhint("Must be superuser to update this extension.")));
}
filename = get_extension_script_filename(control, from_version, version);
/*
+ * If installing a trusted extension on behalf of a non-superuser, become
+ * the bootstrap superuser. (This switch will be cleaned up automatically
+ * if the transaction aborts, as will the GUC changes below.)
+ */
+ if (switch_to_superuser)
+ {
+ GetUserIdAndSecContext(&save_userid, &save_sec_context);
+ SetUserIdAndSecContext(BOOTSTRAP_SUPERUSERID,
+ save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
+ }
+
+ /*
* Force client_min_messages and log_min_messages to be at least WARNING,
* so that we won't spam the user with useless NOTICE messages from common
* script actions like creating shell types.
@@ -907,6 +960,22 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
CStringGetTextDatum("ng"));
/*
+ * If the script uses @extowner@, substitute the calling username.
+ */
+ if (strstr(c_sql, "@extowner@"))
+ {
+ Oid uid = switch_to_superuser ? save_userid : GetUserId();
+ const char *userName = GetUserNameFromId(uid, false);
+ const char *qUserName = quote_identifier(userName);
+
+ t_sql = DirectFunctionCall3Coll(replace_text,
+ C_COLLATION_OID,
+ t_sql,
+ CStringGetTextDatum("@extowner@"),
+ CStringGetTextDatum(qUserName));
+ }
+
+ /*
* If it's not relocatable, substitute the target schema name for
* occurrences of @extschema@.
*
@@ -953,6 +1022,12 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
* Restore the GUC variables we set above.
*/
AtEOXact_GUC(true, save_nestlevel);
+
+ /*
+ * Restore authentication state if needed.
+ */
+ if (switch_to_superuser)
+ SetUserIdAndSecContext(save_userid, save_sec_context);
}
/*
@@ -2111,8 +2186,8 @@ get_available_versions_for_extension(ExtensionControlFile *pcontrol,
{
ExtensionVersionInfo *evi = (ExtensionVersionInfo *) lfirst(lc);
ExtensionControlFile *control;
- Datum values[7];
- bool nulls[7];
+ Datum values[8];
+ bool nulls[8];
ListCell *lc2;
if (!evi->installable)
@@ -2133,24 +2208,26 @@ get_available_versions_for_extension(ExtensionControlFile *pcontrol,
values[1] = CStringGetTextDatum(evi->name);
/* superuser */
values[2] = BoolGetDatum(control->superuser);
+ /* trusted */
+ values[3] = BoolGetDatum(control->trusted);
/* relocatable */
- values[3] = BoolGetDatum(control->relocatable);
+ values[4] = BoolGetDatum(control->relocatable);
/* schema */
if (control->schema == NULL)
- nulls[4] = true;
+ nulls[5] = true;
else
- values[4] = DirectFunctionCall1(namein,
+ values[5] = DirectFunctionCall1(namein,
CStringGetDatum(control->schema));
/* requires */
if (control->requires == NIL)
- nulls[5] = true;
+ nulls[6] = true;
else
- values[5] = convert_requires_to_datum(control->requires);
+ values[6] = convert_requires_to_datum(control->requires);
/* comment */
if (control->comment == NULL)
- nulls[6] = true;
+ nulls[7] = true;
else
- values[6] = CStringGetTextDatum(control->comment);
+ values[7] = CStringGetTextDatum(control->comment);
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
@@ -2178,16 +2255,18 @@ get_available_versions_for_extension(ExtensionControlFile *pcontrol,
values[1] = CStringGetTextDatum(evi2->name);
/* superuser */
values[2] = BoolGetDatum(control->superuser);
+ /* trusted */
+ values[3] = BoolGetDatum(control->trusted);
/* relocatable */
- values[3] = BoolGetDatum(control->relocatable);
+ values[4] = BoolGetDatum(control->relocatable);
/* schema stays the same */
/* requires */
if (control->requires == NIL)
- nulls[5] = true;
+ nulls[6] = true;
else
{
- values[5] = convert_requires_to_datum(control->requires);
- nulls[5] = false;
+ values[6] = convert_requires_to_datum(control->requires);
+ nulls[6] = false;
}
/* comment stays the same */
@@ -2198,6 +2277,64 @@ get_available_versions_for_extension(ExtensionControlFile *pcontrol,
}
/*
+ * Test whether the given extension exists (not whether it's installed)
+ *
+ * This checks for the existence of a matching control file in the extension
+ * directory. That's not a bulletproof check, since the file might be
+ * invalid, but this is only used for hints so it doesn't have to be 100%
+ * right.
+ */
+bool
+extension_file_exists(const char *extensionName)
+{
+ bool result = false;
+ char *location;
+ DIR *dir;
+ struct dirent *de;
+
+ location = get_extension_control_directory();
+ dir = AllocateDir(location);
+
+ /*
+ * If the control directory doesn't exist, we want to silently return
+ * false. Any other error will be reported by ReadDir.
+ */
+ if (dir == NULL && errno == ENOENT)
+ {
+ /* do nothing */
+ }
+ else
+ {
+ while ((de = ReadDir(dir, location)) != NULL)
+ {
+ char *extname;
+
+ if (!is_extension_control_filename(de->d_name))
+ continue;
+
+ /* extract extension name from 'name.control' filename */
+ extname = pstrdup(de->d_name);
+ *strrchr(extname, '.') = '\0';
+
+ /* ignore it if it's an auxiliary control file */
+ if (strstr(extname, "--"))
+ continue;
+
+ /* done if it matches request */
+ if (strcmp(extname, extensionName) == 0)
+ {
+ result = true;
+ break;
+ }
+ }
+
+ FreeDir(dir);
+ }
+
+ return result;
+}
+
+/*
* Convert a list of extension names to a name[] Datum
*/
static Datum
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index c31c57e5e9a..0f40c9ee27b 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -49,6 +49,7 @@
#include "catalog/pg_type.h"
#include "commands/alter.h"
#include "commands/defrem.h"
+#include "commands/extension.h"
#include "commands/proclang.h"
#include "executor/execdesc.h"
#include "executor/executor.h"
@@ -991,7 +992,7 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("language \"%s\" does not exist", language),
- (PLTemplateExists(language) ?
+ (extension_file_exists(language) ?
errhint("Use CREATE EXTENSION to load the language into the database.") : 0)));
languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
@@ -2225,7 +2226,7 @@ ExecuteDoStmt(DoStmt *stmt, bool atomic)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("language \"%s\" does not exist", language),
- (PLTemplateExists(language) ?
+ (extension_file_exists(language) ?
errhint("Use CREATE EXTENSION to load the language into the database.") : 0)));
languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c
index cdff43d3ce7..9d72edbfec5 100644
--- a/src/backend/commands/proclang.c
+++ b/src/backend/commands/proclang.c
@@ -13,329 +13,110 @@
*/
#include "postgres.h"
-#include "access/genam.h"
-#include "access/htup_details.h"
#include "access/table.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
-#include "catalog/pg_authid.h"
#include "catalog/pg_language.h"
#include "catalog/pg_namespace.h"
-#include "catalog/pg_pltemplate.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
-#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/proclang.h"
#include "miscadmin.h"
#include "parser/parse_func.h"
-#include "parser/parser.h"
-#include "utils/acl.h"
#include "utils/builtins.h"
-#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
-typedef struct
-{
- bool tmpltrusted; /* trusted? */
- bool tmpldbacreate; /* db owner allowed to create? */
- char *tmplhandler; /* name of handler function */
- char *tmplinline; /* name of anonymous-block handler, or NULL */
- char *tmplvalidator; /* name of validator function, or NULL */
- char *tmpllibrary; /* path of shared library */
-} PLTemplate;
-
-static ObjectAddress create_proc_lang(const char *languageName, bool replace,
- Oid languageOwner, Oid handlerOid, Oid inlineOid,
- Oid valOid, bool trusted);
-static PLTemplate *find_language_template(const char *languageName);
-
/*
* CREATE LANGUAGE
*/
ObjectAddress
CreateProceduralLanguage(CreatePLangStmt *stmt)
{
- PLTemplate *pltemplate;
- ObjectAddress tmpAddr;
+ const char *languageName = stmt->plname;
+ Oid languageOwner = GetUserId();
Oid handlerOid,
inlineOid,
valOid;
Oid funcrettype;
Oid funcargtypes[1];
+ Relation rel;
+ TupleDesc tupDesc;
+ Datum values[Natts_pg_language];
+ bool nulls[Natts_pg_language];
+ bool replaces[Natts_pg_language];
+ NameData langname;
+ HeapTuple oldtup;
+ HeapTuple tup;
+ Oid langoid;
+ bool is_update;
+ ObjectAddress myself,
+ referenced;
/*
- * If we have template information for the language, ignore the supplied
- * parameters (if any) and use the template information.
+ * Check permission
*/
- if ((pltemplate = find_language_template(stmt->plname)) != NULL)
- {
- List *funcname;
-
- /*
- * Give a notice if we are ignoring supplied parameters.
- */
- if (stmt->plhandler)
- ereport(NOTICE,
- (errmsg("using pg_pltemplate information instead of CREATE LANGUAGE parameters")));
-
- /*
- * Check permission
- */
- if (!superuser())
- {
- if (!pltemplate->tmpldbacreate)
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to create procedural language \"%s\"",
- stmt->plname)));
- if (!pg_database_ownercheck(MyDatabaseId, GetUserId()))
- aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_DATABASE,
- get_database_name(MyDatabaseId));
- }
-
- /*
- * Find or create the handler function, which we force to be in the
- * pg_catalog schema. If already present, it must have the correct
- * return type.
- */
- funcname = SystemFuncName(pltemplate->tmplhandler);
- handlerOid = LookupFuncName(funcname, 0, NULL, true);
- if (OidIsValid(handlerOid))
- {
- funcrettype = get_func_rettype(handlerOid);
- if (funcrettype != LANGUAGE_HANDLEROID)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("function %s must return type %s",
- NameListToString(funcname), "language_handler")));
- }
- else
- {
- tmpAddr = ProcedureCreate(pltemplate->tmplhandler,
- PG_CATALOG_NAMESPACE,
- false, /* replace */
- false, /* returnsSet */
- LANGUAGE_HANDLEROID,
- BOOTSTRAP_SUPERUSERID,
- ClanguageId,
- F_FMGR_C_VALIDATOR,
- pltemplate->tmplhandler,
- pltemplate->tmpllibrary,
- PROKIND_FUNCTION,
- false, /* security_definer */
- false, /* isLeakProof */
- false, /* isStrict */
- PROVOLATILE_VOLATILE,
- PROPARALLEL_UNSAFE,
- buildoidvector(funcargtypes, 0),
- PointerGetDatum(NULL),
- PointerGetDatum(NULL),
- PointerGetDatum(NULL),
- NIL,
- PointerGetDatum(NULL),
- PointerGetDatum(NULL),
- InvalidOid,
- 1,
- 0);
- handlerOid = tmpAddr.objectId;
- }
-
- /*
- * Likewise for the anonymous block handler, if required; but we don't
- * care about its return type.
- */
- if (pltemplate->tmplinline)
- {
- funcname = SystemFuncName(pltemplate->tmplinline);
- funcargtypes[0] = INTERNALOID;
- inlineOid = LookupFuncName(funcname, 1, funcargtypes, true);
- if (!OidIsValid(inlineOid))
- {
- tmpAddr = ProcedureCreate(pltemplate->tmplinline,
- PG_CATALOG_NAMESPACE,
- false, /* replace */
- false, /* returnsSet */
- VOIDOID,
- BOOTSTRAP_SUPERUSERID,
- ClanguageId,
- F_FMGR_C_VALIDATOR,
- pltemplate->tmplinline,
- pltemplate->tmpllibrary,
- PROKIND_FUNCTION,
- false, /* security_definer */
- false, /* isLeakProof */
- true, /* isStrict */
- PROVOLATILE_VOLATILE,
- PROPARALLEL_UNSAFE,
- buildoidvector(funcargtypes, 1),
- PointerGetDatum(NULL),
- PointerGetDatum(NULL),
- PointerGetDatum(NULL),
- NIL,
- PointerGetDatum(NULL),
- PointerGetDatum(NULL),
- InvalidOid,
- 1,
- 0);
- inlineOid = tmpAddr.objectId;
- }
- }
- else
- inlineOid = InvalidOid;
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to create custom procedural language")));
+ /*
+ * Lookup the PL handler function and check that it is of the expected
+ * return type
+ */
+ Assert(stmt->plhandler);
+ handlerOid = LookupFuncName(stmt->plhandler, 0, NULL, false);
+ funcrettype = get_func_rettype(handlerOid);
+ if (funcrettype != LANGUAGE_HANDLEROID)
+ {
/*
- * Likewise for the validator, if required; but we don't care about
- * its return type.
+ * We allow OPAQUE just so we can load old dump files. When we see a
+ * handler function declared OPAQUE, change it to LANGUAGE_HANDLER.
+ * (This is probably obsolete and removable?)
*/
- if (pltemplate->tmplvalidator)
+ if (funcrettype == OPAQUEOID)
{
- funcname = SystemFuncName(pltemplate->tmplvalidator);
- funcargtypes[0] = OIDOID;
- valOid = LookupFuncName(funcname, 1, funcargtypes, true);
- if (!OidIsValid(valOid))
- {
- tmpAddr = ProcedureCreate(pltemplate->tmplvalidator,
- PG_CATALOG_NAMESPACE,
- false, /* replace */
- false, /* returnsSet */
- VOIDOID,
- BOOTSTRAP_SUPERUSERID,
- ClanguageId,
- F_FMGR_C_VALIDATOR,
- pltemplate->tmplvalidator,
- pltemplate->tmpllibrary,
- PROKIND_FUNCTION,
- false, /* security_definer */
- false, /* isLeakProof */
- true, /* isStrict */
- PROVOLATILE_VOLATILE,
- PROPARALLEL_UNSAFE,
- buildoidvector(funcargtypes, 1),
- PointerGetDatum(NULL),
- PointerGetDatum(NULL),
- PointerGetDatum(NULL),
- NIL,
- PointerGetDatum(NULL),
- PointerGetDatum(NULL),
- InvalidOid,
- 1,
- 0);
- valOid = tmpAddr.objectId;
- }
+ ereport(WARNING,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("changing return type of function %s from %s to %s",
+ NameListToString(stmt->plhandler),
+ "opaque", "language_handler")));
+ SetFunctionReturnType(handlerOid, LANGUAGE_HANDLEROID);
}
else
- valOid = InvalidOid;
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("function %s must return type %s",
+ NameListToString(stmt->plhandler), "language_handler")));
+ }
- /* ok, create it */
- return create_proc_lang(stmt->plname, stmt->replace, GetUserId(),
- handlerOid, inlineOid,
- valOid, pltemplate->tmpltrusted);
+ /* validate the inline function */
+ if (stmt->plinline)
+ {
+ funcargtypes[0] = INTERNALOID;
+ inlineOid = LookupFuncName(stmt->plinline, 1, funcargtypes, false);
+ /* return value is ignored, so we don't check the type */
}
else
- {
- /*
- * No template, so use the provided information. If there's no
- * handler clause, the user is trying to rely on a template that we
- * don't have, so complain accordingly.
- */
- if (!stmt->plhandler)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("unsupported language \"%s\"",
- stmt->plname),
- errhint("The supported languages are listed in the pg_pltemplate system catalog.")));
+ inlineOid = InvalidOid;
- /*
- * Check permission
- */
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to create custom procedural language")));
-
- /*
- * Lookup the PL handler function and check that it is of the expected
- * return type
- */
- handlerOid = LookupFuncName(stmt->plhandler, 0, NULL, false);
- funcrettype = get_func_rettype(handlerOid);
- if (funcrettype != LANGUAGE_HANDLEROID)
- {
- /*
- * We allow OPAQUE just so we can load old dump files. When we
- * see a handler function declared OPAQUE, change it to
- * LANGUAGE_HANDLER. (This is probably obsolete and removable?)
- */
- if (funcrettype == OPAQUEOID)
- {
- ereport(WARNING,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("changing return type of function %s from %s to %s",
- NameListToString(stmt->plhandler),
- "opaque", "language_handler")));
- SetFunctionReturnType(handlerOid, LANGUAGE_HANDLEROID);
- }
- else
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("function %s must return type %s",
- NameListToString(stmt->plhandler), "language_handler")));
- }
-
- /* validate the inline function */
- if (stmt->plinline)
- {
- funcargtypes[0] = INTERNALOID;
- inlineOid = LookupFuncName(stmt->plinline, 1, funcargtypes, false);
- /* return value is ignored, so we don't check the type */
- }
- else
- inlineOid = InvalidOid;
-
- /* validate the validator function */
- if (stmt->plvalidator)
- {
- funcargtypes[0] = OIDOID;
- valOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes, false);
- /* return value is ignored, so we don't check the type */
- }
- else
- valOid = InvalidOid;
-
- /* ok, create it */
- return create_proc_lang(stmt->plname, stmt->replace, GetUserId(),
- handlerOid, inlineOid,
- valOid, stmt->pltrusted);
+ /* validate the validator function */
+ if (stmt->plvalidator)
+ {
+ funcargtypes[0] = OIDOID;
+ valOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes, false);
+ /* return value is ignored, so we don't check the type */
}
-}
-
-/*
- * Guts of language creation.
- */
-static ObjectAddress
-create_proc_lang(const char *languageName, bool replace,
- Oid languageOwner, Oid handlerOid, Oid inlineOid,
- Oid valOid, bool trusted)
-{
- Relation rel;
- TupleDesc tupDesc;
- Datum values[Natts_pg_language];
- bool nulls[Natts_pg_language];
- bool replaces[Natts_pg_language];
- NameData langname;
- HeapTuple oldtup;
- HeapTuple tup;
- Oid langoid;
- bool is_update;
- ObjectAddress myself,
- referenced;
+ else
+ valOid = InvalidOid;
+ /* ok to create it */
rel = table_open(LanguageRelationId, RowExclusiveLock);
tupDesc = RelationGetDescr(rel);
@@ -348,7 +129,7 @@ create_proc_lang(const char *languageName, bool replace,
values[Anum_pg_language_lanname - 1] = NameGetDatum(&langname);
values[Anum_pg_language_lanowner - 1] = ObjectIdGetDatum(languageOwner);
values[Anum_pg_language_lanispl - 1] = BoolGetDatum(true);
- values[Anum_pg_language_lanpltrusted - 1] = BoolGetDatum(trusted);
+ values[Anum_pg_language_lanpltrusted - 1] = BoolGetDatum(stmt->pltrusted);
values[Anum_pg_language_lanplcallfoid - 1] = ObjectIdGetDatum(handlerOid);
values[Anum_pg_language_laninline - 1] = ObjectIdGetDatum(inlineOid);
values[Anum_pg_language_lanvalidator - 1] = ObjectIdGetDatum(valOid);
@@ -362,13 +143,17 @@ create_proc_lang(const char *languageName, bool replace,
Form_pg_language oldform = (Form_pg_language) GETSTRUCT(oldtup);
/* There is one; okay to replace it? */
- if (!replace)
+ if (!stmt->replace)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("language \"%s\" already exists", languageName)));
+
+ /* This is currently pointless, since we already checked superuser */
+#ifdef NOT_USED
if (!pg_language_ownercheck(oldform->oid, languageOwner))
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_LANGUAGE,
languageName);
+#endif
/*
* Do not change existing oid, ownership or permissions. Note
@@ -451,83 +236,6 @@ create_proc_lang(const char *languageName, bool replace,
}
/*
- * Look to see if we have template information for the given language name.
- */
-static PLTemplate *
-find_language_template(const char *languageName)
-{
- PLTemplate *result;
- Relation rel;
- SysScanDesc scan;
- ScanKeyData key;
- HeapTuple tup;
-
- rel = table_open(PLTemplateRelationId, AccessShareLock);
-
- ScanKeyInit(&key,
- Anum_pg_pltemplate_tmplname,
- BTEqualStrategyNumber, F_NAMEEQ,
- CStringGetDatum(languageName));
- scan = systable_beginscan(rel, PLTemplateNameIndexId, true,
- NULL, 1, &key);
-
- tup = systable_getnext(scan);
- if (HeapTupleIsValid(tup))
- {
- Form_pg_pltemplate tmpl = (Form_pg_pltemplate) GETSTRUCT(tup);
- Datum datum;
- bool isnull;
-
- result = (PLTemplate *) palloc0(sizeof(PLTemplate));
- result->tmpltrusted = tmpl->tmpltrusted;
- result->tmpldbacreate = tmpl->tmpldbacreate;
-
- /* Remaining fields are variable-width so we need heap_getattr */
- datum = heap_getattr(tup, Anum_pg_pltemplate_tmplhandler,
- RelationGetDescr(rel), &isnull);
- if (!isnull)
- result->tmplhandler = TextDatumGetCString(datum);
-
- datum = heap_getattr(tup, Anum_pg_pltemplate_tmplinline,
- RelationGetDescr(rel), &isnull);
- if (!isnull)
- result->tmplinline = TextDatumGetCString(datum);
-
- datum = heap_getattr(tup, Anum_pg_pltemplate_tmplvalidator,
- RelationGetDescr(rel), &isnull);
- if (!isnull)
- result->tmplvalidator = TextDatumGetCString(datum);
-
- datum = heap_getattr(tup, Anum_pg_pltemplate_tmpllibrary,
- RelationGetDescr(rel), &isnull);
- if (!isnull)
- result->tmpllibrary = TextDatumGetCString(datum);
-
- /* Ignore template if handler or library info is missing */
- if (!result->tmplhandler || !result->tmpllibrary)
- result = NULL;
- }
- else
- result = NULL;
-
- systable_endscan(scan);
-
- table_close(rel, AccessShareLock);
-
- return result;
-}
-
-
-/*
- * This just returns true if we have a valid template for a given language
- */
-bool
-PLTemplateExists(const char *languageName)
-{
- return (find_language_template(languageName) != NULL);
-}
-
-/*
* Guts of language dropping.
*/
void
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index ba5916b4d27..1b0edf5d3d1 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4324,14 +4324,17 @@ NumericOnly_list: NumericOnly { $$ = list_make1($1); }
CreatePLangStmt:
CREATE opt_or_replace opt_trusted opt_procedural LANGUAGE NonReservedWord_or_Sconst
{
- CreatePLangStmt *n = makeNode(CreatePLangStmt);
- n->replace = $2;
- n->plname = $6;
- /* parameters are all to be supplied by system */
- n->plhandler = NIL;
- n->plinline = NIL;
- n->plvalidator = NIL;
- n->pltrusted = false;
+ /*
+ * We now interpret parameterless CREATE LANGUAGE as
+ * CREATE EXTENSION. "OR REPLACE" is silently translated
+ * to "IF NOT EXISTS", which isn't quite the same, but
+ * seems more useful than throwing an error. We just
+ * ignore TRUSTED, as the previous code would have too.
+ */
+ CreateExtensionStmt *n = makeNode(CreateExtensionStmt);
+ n->if_not_exists = $2;
+ n->extname = $6;
+ n->options = NIL;
$$ = (Node *)n;
}
| CREATE opt_or_replace opt_trusted opt_procedural LANGUAGE NonReservedWord_or_Sconst