mirror of
https://github.com/postgres/postgres.git
synced 2025-06-13 07:41:39 +03:00
Allow non-superuser database owners to create procedural languages.
A DBA is allowed to create a language in his database if it's marked "tmpldbacreate" in pg_pltemplate. The factory default is that this is set for all standard trusted languages, but of course a superuser may adjust the settings. In service of this, add the long-foreseen owner column to pg_language; renaming, dropping, and altering owner of a PL now follow normal ownership rules instead of being superuser-only. Jeremy Drake, with some editorialization by Tom Lane.
This commit is contained in:
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/proclang.c,v 1.71 2007/01/22 01:35:20 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/proclang.c,v 1.72 2007/03/26 16:58:38 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -17,16 +17,19 @@
|
||||
#include "access/heapam.h"
|
||||
#include "catalog/dependency.h"
|
||||
#include "catalog/indexing.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/gramparse.h"
|
||||
#include "parser/parse_func.h"
|
||||
#include "utils/acl.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/fmgroids.h"
|
||||
#include "utils/lsyscache.h"
|
||||
@ -36,13 +39,14 @@
|
||||
typedef struct
|
||||
{
|
||||
bool tmpltrusted; /* trusted? */
|
||||
bool tmpldbacreate; /* db owner allowed to create? */
|
||||
char *tmplhandler; /* name of handler function */
|
||||
char *tmplvalidator; /* name of validator function, or NULL */
|
||||
char *tmpllibrary; /* path of shared library */
|
||||
} PLTemplate;
|
||||
|
||||
static void create_proc_lang(const char *languageName,
|
||||
Oid handlerOid, Oid valOid, bool trusted);
|
||||
Oid languageOwner, Oid handlerOid, Oid valOid, bool trusted);
|
||||
static PLTemplate *find_language_template(const char *languageName);
|
||||
|
||||
|
||||
@ -60,14 +64,6 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
||||
Oid funcrettype;
|
||||
Oid funcargtypes[1];
|
||||
|
||||
/*
|
||||
* Check permission
|
||||
*/
|
||||
if (!superuser())
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
errmsg("must be superuser to create procedural language")));
|
||||
|
||||
/*
|
||||
* Translate the language name and check that this language doesn't
|
||||
* already exist
|
||||
@ -96,6 +92,21 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
||||
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\"",
|
||||
languageName)));
|
||||
if (!pg_database_ownercheck(MyDatabaseId, GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_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
|
||||
@ -171,7 +182,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
||||
valOid = InvalidOid;
|
||||
|
||||
/* ok, create it */
|
||||
create_proc_lang(languageName, handlerOid, valOid,
|
||||
create_proc_lang(languageName, GetUserId(), handlerOid, valOid,
|
||||
pltemplate->tmpltrusted);
|
||||
}
|
||||
else
|
||||
@ -188,6 +199,14 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
||||
languageName),
|
||||
errhint("The supported languages are listed in the pg_pltemplate system catalog.")));
|
||||
|
||||
/*
|
||||
* 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
|
||||
@ -227,7 +246,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
||||
valOid = InvalidOid;
|
||||
|
||||
/* ok, create it */
|
||||
create_proc_lang(languageName, handlerOid, valOid, stmt->pltrusted);
|
||||
create_proc_lang(languageName, GetUserId(), handlerOid, valOid,
|
||||
stmt->pltrusted);
|
||||
}
|
||||
}
|
||||
|
||||
@ -236,7 +256,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
||||
*/
|
||||
static void
|
||||
create_proc_lang(const char *languageName,
|
||||
Oid handlerOid, Oid valOid, bool trusted)
|
||||
Oid languageOwner, Oid handlerOid, Oid valOid, bool trusted)
|
||||
{
|
||||
Relation rel;
|
||||
TupleDesc tupDesc;
|
||||
@ -258,6 +278,7 @@ create_proc_lang(const char *languageName,
|
||||
|
||||
namestrcpy(&langname, languageName);
|
||||
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_lanplcallfoid - 1] = ObjectIdGetDatum(handlerOid);
|
||||
@ -277,6 +298,12 @@ create_proc_lang(const char *languageName,
|
||||
myself.objectId = HeapTupleGetOid(tup);
|
||||
myself.objectSubId = 0;
|
||||
|
||||
/* dependency on owner of language */
|
||||
referenced.classId = AuthIdRelationId;
|
||||
referenced.objectId = languageOwner;
|
||||
referenced.objectSubId = 0;
|
||||
recordSharedDependencyOn(&myself, &referenced, SHARED_DEPENDENCY_OWNER);
|
||||
|
||||
/* dependency on the PL handler function */
|
||||
referenced.classId = ProcedureRelationId;
|
||||
referenced.objectId = handlerOid;
|
||||
@ -325,6 +352,7 @@ find_language_template(const char *languageName)
|
||||
|
||||
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,
|
||||
@ -381,14 +409,6 @@ DropProceduralLanguage(DropPLangStmt *stmt)
|
||||
HeapTuple langTup;
|
||||
ObjectAddress object;
|
||||
|
||||
/*
|
||||
* Check permission
|
||||
*/
|
||||
if (!superuser())
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
errmsg("must be superuser to drop procedural language")));
|
||||
|
||||
/*
|
||||
* Translate the language name, check that the language exists
|
||||
*/
|
||||
@ -411,6 +431,13 @@ DropProceduralLanguage(DropPLangStmt *stmt)
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check permission
|
||||
*/
|
||||
if (!pg_language_ownercheck(HeapTupleGetOid(langTup), GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE,
|
||||
languageName);
|
||||
|
||||
object.classId = LanguageRelationId;
|
||||
object.objectId = HeapTupleGetOid(langTup);
|
||||
object.objectSubId = 0;
|
||||
@ -478,11 +505,10 @@ RenameLanguage(const char *oldname, const char *newname)
|
||||
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
||||
errmsg("language \"%s\" already exists", newname)));
|
||||
|
||||
/* must be superuser, since we do not have owners for PLs */
|
||||
if (!superuser())
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
errmsg("must be superuser to rename procedural language")));
|
||||
/* must be owner of PL */
|
||||
if (!pg_language_ownercheck(HeapTupleGetOid(tup), GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE,
|
||||
oldname);
|
||||
|
||||
/* rename */
|
||||
namestrcpy(&(((Form_pg_language) GETSTRUCT(tup))->lanname), newname);
|
||||
@ -492,3 +518,87 @@ RenameLanguage(const char *oldname, const char *newname)
|
||||
heap_close(rel, NoLock);
|
||||
heap_freetuple(tup);
|
||||
}
|
||||
|
||||
/*
|
||||
* Change language owner
|
||||
*/
|
||||
void
|
||||
AlterLanguageOwner(const char *name, Oid newOwnerId)
|
||||
{
|
||||
HeapTuple tup;
|
||||
Relation rel;
|
||||
Form_pg_language lanForm;
|
||||
|
||||
/* Translate name for consistency with CREATE */
|
||||
name = case_translate_language_name(name);
|
||||
|
||||
rel = heap_open(LanguageRelationId, RowExclusiveLock);
|
||||
|
||||
tup = SearchSysCache(LANGNAME,
|
||||
CStringGetDatum(name),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tup))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("language \"%s\" does not exist", name)));
|
||||
lanForm = (Form_pg_language) GETSTRUCT(tup);
|
||||
|
||||
/*
|
||||
* If the new owner is the same as the existing owner, consider the
|
||||
* command to have succeeded. This is for dump restoration purposes.
|
||||
*/
|
||||
if (lanForm->lanowner != newOwnerId)
|
||||
{
|
||||
Datum repl_val[Natts_pg_language];
|
||||
char repl_null[Natts_pg_language];
|
||||
char repl_repl[Natts_pg_language];
|
||||
Acl *newAcl;
|
||||
Datum aclDatum;
|
||||
bool isNull;
|
||||
HeapTuple newtuple;
|
||||
|
||||
/* Otherwise, must be owner of the existing object */
|
||||
if (!pg_language_ownercheck(HeapTupleGetOid(tup), GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE,
|
||||
NameStr(lanForm->lanname));
|
||||
|
||||
/* Must be able to become new owner */
|
||||
check_is_member_of_role(GetUserId(), newOwnerId);
|
||||
|
||||
memset(repl_null, ' ', sizeof(repl_null));
|
||||
memset(repl_repl, ' ', sizeof(repl_repl));
|
||||
|
||||
repl_repl[Anum_pg_language_lanowner - 1] = 'r';
|
||||
repl_val[Anum_pg_language_lanowner - 1] = ObjectIdGetDatum(newOwnerId);
|
||||
|
||||
/*
|
||||
* Determine the modified ACL for the new owner. This is only
|
||||
* necessary when the ACL is non-null.
|
||||
*/
|
||||
aclDatum = SysCacheGetAttr(LANGNAME, tup,
|
||||
Anum_pg_language_lanacl,
|
||||
&isNull);
|
||||
if (!isNull)
|
||||
{
|
||||
newAcl = aclnewowner(DatumGetAclP(aclDatum),
|
||||
lanForm->lanowner, newOwnerId);
|
||||
repl_repl[Anum_pg_language_lanacl - 1] = 'r';
|
||||
repl_val[Anum_pg_language_lanacl - 1] = PointerGetDatum(newAcl);
|
||||
}
|
||||
|
||||
newtuple = heap_modifytuple(tup, RelationGetDescr(rel),
|
||||
repl_val, repl_null, repl_repl);
|
||||
|
||||
simple_heap_update(rel, &newtuple->t_self, newtuple);
|
||||
CatalogUpdateIndexes(rel, newtuple);
|
||||
|
||||
heap_freetuple(newtuple);
|
||||
|
||||
/* Update owner dependency reference */
|
||||
changeDependencyOnOwner(LanguageRelationId, HeapTupleGetOid(tup),
|
||||
newOwnerId);
|
||||
}
|
||||
|
||||
ReleaseSysCache(tup);
|
||||
heap_close(rel, RowExclusiveLock);
|
||||
}
|
||||
|
Reference in New Issue
Block a user