1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* aclchk.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* Routines to check access control permissions.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2016-01-02 19:33:40 +01:00
|
|
|
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/backend/catalog/aclchk.c
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* NOTES
|
1997-09-07 07:04:48 +02:00
|
|
|
* See acl.h.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
1996-11-03 07:54:38 +01:00
|
|
|
#include "postgres.h"
|
|
|
|
|
2005-11-21 13:49:33 +01:00
|
|
|
#include "access/genam.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "access/heapam.h"
|
2012-08-30 22:15:44 +02:00
|
|
|
#include "access/htup_details.h"
|
2008-05-12 02:00:54 +02:00
|
|
|
#include "access/sysattr.h"
|
2006-07-13 18:49:20 +02:00
|
|
|
#include "access/xact.h"
|
2016-04-07 03:45:32 +02:00
|
|
|
#include "catalog/binary_upgrade.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "catalog/catalog.h"
|
2005-07-07 22:40:02 +02:00
|
|
|
#include "catalog/dependency.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "catalog/indexing.h"
|
2013-03-18 03:55:14 +01:00
|
|
|
#include "catalog/objectaccess.h"
|
2005-06-28 07:09:14 +02:00
|
|
|
#include "catalog/pg_authid.h"
|
2011-02-12 14:54:13 +01:00
|
|
|
#include "catalog/pg_collation.h"
|
2003-11-21 23:32:49 +01:00
|
|
|
#include "catalog/pg_conversion.h"
|
2002-04-21 02:26:44 +02:00
|
|
|
#include "catalog/pg_database.h"
|
2009-10-05 21:24:49 +02:00
|
|
|
#include "catalog/pg_default_acl.h"
|
2012-07-18 16:16:16 +02:00
|
|
|
#include "catalog/pg_event_trigger.h"
|
2011-03-04 22:08:24 +01:00
|
|
|
#include "catalog/pg_extension.h"
|
2008-12-19 17:25:19 +01:00
|
|
|
#include "catalog/pg_foreign_data_wrapper.h"
|
|
|
|
#include "catalog/pg_foreign_server.h"
|
2016-04-07 03:45:32 +02:00
|
|
|
#include "catalog/pg_init_privs.h"
|
2002-02-19 00:11:58 +01:00
|
|
|
#include "catalog/pg_language.h"
|
2009-12-11 04:34:57 +01:00
|
|
|
#include "catalog/pg_largeobject.h"
|
|
|
|
#include "catalog/pg_largeobject_metadata.h"
|
2002-04-21 02:26:44 +02:00
|
|
|
#include "catalog/pg_namespace.h"
|
2002-07-30 00:14:11 +02:00
|
|
|
#include "catalog/pg_opclass.h"
|
1996-10-31 06:55:24 +01:00
|
|
|
#include "catalog/pg_operator.h"
|
2007-01-23 06:07:18 +01:00
|
|
|
#include "catalog/pg_opfamily.h"
|
1996-11-08 07:02:30 +01:00
|
|
|
#include "catalog/pg_proc.h"
|
2004-06-18 08:14:31 +02:00
|
|
|
#include "catalog/pg_tablespace.h"
|
1998-04-27 06:08:07 +02:00
|
|
|
#include "catalog/pg_type.h"
|
2007-08-21 03:11:32 +02:00
|
|
|
#include "catalog/pg_ts_config.h"
|
|
|
|
#include "catalog/pg_ts_dict.h"
|
2006-05-04 00:45:26 +02:00
|
|
|
#include "commands/dbcommands.h"
|
Allow on-the-fly capture of DDL event details
This feature lets user code inspect and take action on DDL events.
Whenever a ddl_command_end event trigger is installed, DDL actions
executed are saved to a list which can be inspected during execution of
a function attached to ddl_command_end.
The set-returning function pg_event_trigger_ddl_commands can be used to
list actions so captured; it returns data about the type of command
executed, as well as the affected object. This is sufficient for many
uses of this feature. For the cases where it is not, we also provide a
"command" column of a new pseudo-type pg_ddl_command, which is a
pointer to a C structure that can be accessed by C code. The struct
contains all the info necessary to completely inspect and even
reconstruct the executed command.
There is no actual deparse code here; that's expected to come later.
What we have is enough infrastructure that the deparsing can be done in
an external extension. The intention is that we will add some deparsing
code in a later release, as an in-core extension.
A new test module is included. It's probably insufficient as is, but it
should be sufficient as a starting point for a more complete and
future-proof approach.
Authors: Álvaro Herrera, with some help from Andres Freund, Ian Barwick,
Abhijit Menon-Sen.
Reviews by Andres Freund, Robert Haas, Amit Kapila, Michael Paquier,
Craig Ringer, David Steele.
Additional input from Chris Browne, Dimitri Fontaine, Stephen Frost,
Petr Jelínek, Tom Lane, Jim Nasby, Steven Singer, Pavel Stěhule.
Based on original work by Dimitri Fontaine, though I didn't use his
code.
Discussion:
https://www.postgresql.org/message-id/m2txrsdzxa.fsf@2ndQuadrant.fr
https://www.postgresql.org/message-id/20131108153322.GU5809@eldon.alvh.no-ip.org
https://www.postgresql.org/message-id/20150215044814.GL3391@alvh.no-ip.org
2015-05-12 00:14:31 +02:00
|
|
|
#include "commands/event_trigger.h"
|
2016-04-07 03:45:32 +02:00
|
|
|
#include "commands/extension.h"
|
2010-08-05 16:45:09 +02:00
|
|
|
#include "commands/proclang.h"
|
|
|
|
#include "commands/tablespace.h"
|
2008-12-19 17:25:19 +01:00
|
|
|
#include "foreign/foreign.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "miscadmin.h"
|
2011-12-19 23:05:19 +01:00
|
|
|
#include "nodes/makefuncs.h"
|
1997-11-25 23:07:18 +01:00
|
|
|
#include "parser/parse_func.h"
|
2011-12-19 23:05:19 +01:00
|
|
|
#include "parser/parse_type.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "utils/acl.h"
|
Allow on-the-fly capture of DDL event details
This feature lets user code inspect and take action on DDL events.
Whenever a ddl_command_end event trigger is installed, DDL actions
executed are saved to a list which can be inspected during execution of
a function attached to ddl_command_end.
The set-returning function pg_event_trigger_ddl_commands can be used to
list actions so captured; it returns data about the type of command
executed, as well as the affected object. This is sufficient for many
uses of this feature. For the cases where it is not, we also provide a
"command" column of a new pseudo-type pg_ddl_command, which is a
pointer to a C structure that can be accessed by C code. The struct
contains all the info necessary to completely inspect and even
reconstruct the executed command.
There is no actual deparse code here; that's expected to come later.
What we have is enough infrastructure that the deparsing can be done in
an external extension. The intention is that we will add some deparsing
code in a later release, as an in-core extension.
A new test module is included. It's probably insufficient as is, but it
should be sufficient as a starting point for a more complete and
future-proof approach.
Authors: Álvaro Herrera, with some help from Andres Freund, Ian Barwick,
Abhijit Menon-Sen.
Reviews by Andres Freund, Robert Haas, Amit Kapila, Michael Paquier,
Craig Ringer, David Steele.
Additional input from Chris Browne, Dimitri Fontaine, Stephen Frost,
Petr Jelínek, Tom Lane, Jim Nasby, Steven Singer, Pavel Stěhule.
Based on original work by Dimitri Fontaine, though I didn't use his
code.
Discussion:
https://www.postgresql.org/message-id/m2txrsdzxa.fsf@2ndQuadrant.fr
https://www.postgresql.org/message-id/20131108153322.GU5809@eldon.alvh.no-ip.org
https://www.postgresql.org/message-id/20150215044814.GL3391@alvh.no-ip.org
2015-05-12 00:14:31 +02:00
|
|
|
#include "utils/aclchk_internal.h"
|
2010-06-13 19:43:13 +02:00
|
|
|
#include "utils/builtins.h"
|
2002-04-21 02:26:44 +02:00
|
|
|
#include "utils/fmgroids.h"
|
|
|
|
#include "utils/lsyscache.h"
|
2008-06-19 02:46:06 +02:00
|
|
|
#include "utils/rel.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "utils/syscache.h"
|
2008-03-26 22:10:39 +01:00
|
|
|
#include "utils/tqual.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2002-02-19 00:11:58 +01:00
|
|
|
|
2009-10-05 21:24:49 +02:00
|
|
|
/*
|
|
|
|
* Internal format used by ALTER DEFAULT PRIVILEGES.
|
|
|
|
*/
|
|
|
|
typedef struct
|
|
|
|
{
|
2010-02-26 03:01:40 +01:00
|
|
|
Oid roleid; /* owning role */
|
|
|
|
Oid nspid; /* namespace, or InvalidOid if none */
|
2009-10-05 21:24:49 +02:00
|
|
|
/* remaining fields are same as in InternalGrant: */
|
|
|
|
bool is_grant;
|
|
|
|
GrantObjectType objtype;
|
|
|
|
bool all_privs;
|
|
|
|
AclMode privileges;
|
|
|
|
List *grantees;
|
|
|
|
bool grant_option;
|
|
|
|
DropBehavior behavior;
|
|
|
|
} InternalDefaultACL;
|
|
|
|
|
2016-04-07 03:45:32 +02:00
|
|
|
/*
|
|
|
|
* When performing a binary-upgrade, pg_dump will call a function to set
|
|
|
|
* this variable to let us know that we need to populate the pg_init_privs
|
|
|
|
* table for the GRANT/REVOKE commands while this variable is set to true.
|
|
|
|
*/
|
|
|
|
bool binary_upgrade_record_init_privs = false;
|
2009-10-05 21:24:49 +02:00
|
|
|
|
|
|
|
static void ExecGrantStmt_oids(InternalGrant *istmt);
|
2005-12-01 03:03:01 +01:00
|
|
|
static void ExecGrant_Relation(InternalGrant *grantStmt);
|
|
|
|
static void ExecGrant_Database(InternalGrant *grantStmt);
|
2008-12-19 17:25:19 +01:00
|
|
|
static void ExecGrant_Fdw(InternalGrant *grantStmt);
|
|
|
|
static void ExecGrant_ForeignServer(InternalGrant *grantStmt);
|
2005-12-01 03:03:01 +01:00
|
|
|
static void ExecGrant_Function(InternalGrant *grantStmt);
|
|
|
|
static void ExecGrant_Language(InternalGrant *grantStmt);
|
2009-12-11 04:34:57 +01:00
|
|
|
static void ExecGrant_Largeobject(InternalGrant *grantStmt);
|
2005-12-01 03:03:01 +01:00
|
|
|
static void ExecGrant_Namespace(InternalGrant *grantStmt);
|
|
|
|
static void ExecGrant_Tablespace(InternalGrant *grantStmt);
|
2011-12-19 23:05:19 +01:00
|
|
|
static void ExecGrant_Type(InternalGrant *grantStmt);
|
2002-02-19 00:11:58 +01:00
|
|
|
|
2009-10-05 21:24:49 +02:00
|
|
|
static void SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames);
|
|
|
|
static void SetDefaultACL(InternalDefaultACL *iacls);
|
|
|
|
|
2005-12-01 03:03:01 +01:00
|
|
|
static List *objectNamesToOids(GrantObjectType objtype, List *objnames);
|
2009-10-12 22:39:42 +02:00
|
|
|
static List *objectsInSchemaToOids(GrantObjectType objtype, List *nspnames);
|
|
|
|
static List *getRelationsInNamespace(Oid namespaceId, char relkind);
|
2009-01-22 21:16:10 +01:00
|
|
|
static void expand_col_privileges(List *colnames, Oid table_oid,
|
2009-06-11 16:49:15 +02:00
|
|
|
AclMode this_privileges,
|
|
|
|
AclMode *col_privileges,
|
|
|
|
int num_col_privileges);
|
2009-01-22 21:16:10 +01:00
|
|
|
static void expand_all_col_privileges(Oid table_oid, Form_pg_class classForm,
|
2009-06-11 16:49:15 +02:00
|
|
|
AclMode this_privileges,
|
|
|
|
AclMode *col_privileges,
|
|
|
|
int num_col_privileges);
|
2005-06-28 21:51:26 +02:00
|
|
|
static AclMode string_to_privilege(const char *privname);
|
2002-04-21 02:26:44 +02:00
|
|
|
static const char *privilege_to_string(AclMode privilege);
|
2005-12-01 03:03:01 +01:00
|
|
|
static AclMode restrict_and_check_grant(bool is_grant, AclMode avail_goptions,
|
2006-10-04 02:30:14 +02:00
|
|
|
bool all_privs, AclMode privileges,
|
|
|
|
Oid objectId, Oid grantorId,
|
2009-01-22 21:16:10 +01:00
|
|
|
AclObjectKind objkind, const char *objname,
|
|
|
|
AttrNumber att_number, const char *colname);
|
|
|
|
static AclMode pg_aclmask(AclObjectKind objkind, Oid table_oid, AttrNumber attnum,
|
2009-06-11 16:49:15 +02:00
|
|
|
Oid roleid, AclMode mask, AclMaskHow how);
|
2016-04-07 03:45:32 +02:00
|
|
|
static void recordExtensionInitPriv(Oid objoid, Oid classoid, int objsubid,
|
|
|
|
Acl *new_acl);
|
2002-02-19 00:11:58 +01:00
|
|
|
|
2000-10-02 06:49:28 +02:00
|
|
|
|
2001-05-27 11:59:30 +02:00
|
|
|
#ifdef ACLDEBUG
|
2005-08-17 21:45:51 +02:00
|
|
|
static void
|
1997-09-08 22:59:27 +02:00
|
|
|
dumpacl(Acl *acl)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-08-01 00:39:17 +02:00
|
|
|
int i;
|
1997-09-08 04:41:22 +02:00
|
|
|
AclItem *aip;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-05-27 19:49:47 +02:00
|
|
|
elog(DEBUG2, "acl size = %d, # acls = %d",
|
1997-09-07 07:04:48 +02:00
|
|
|
ACL_SIZE(acl), ACL_NUM(acl));
|
2000-08-01 00:39:17 +02:00
|
|
|
aip = ACL_DAT(acl);
|
1997-09-07 07:04:48 +02:00
|
|
|
for (i = 0; i < ACL_NUM(acl); ++i)
|
2003-05-27 19:49:47 +02:00
|
|
|
elog(DEBUG2, " acl[%d]: %s", i,
|
2000-08-01 00:39:17 +02:00
|
|
|
DatumGetCString(DirectFunctionCall1(aclitemout,
|
2005-10-15 04:49:52 +02:00
|
|
|
PointerGetDatum(aip + i))));
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
2001-11-05 18:46:40 +01:00
|
|
|
#endif /* ACLDEBUG */
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2001-06-10 01:21:55 +02:00
|
|
|
|
2002-02-19 00:11:58 +01:00
|
|
|
/*
|
|
|
|
* If is_grant is true, adds the given privileges for the list of
|
|
|
|
* grantees to the existing old_acl. If is_grant is false, the
|
|
|
|
* privileges for the given grantees are removed from old_acl.
|
2003-09-04 17:53:04 +02:00
|
|
|
*
|
|
|
|
* NB: the original old_acl is pfree'd.
|
2002-02-19 00:11:58 +01:00
|
|
|
*/
|
2002-04-21 02:26:44 +02:00
|
|
|
static Acl *
|
|
|
|
merge_acl_with_grant(Acl *old_acl, bool is_grant,
|
2003-10-05 23:49:12 +02:00
|
|
|
bool grant_option, DropBehavior behavior,
|
2003-10-31 21:00:49 +01:00
|
|
|
List *grantees, AclMode privileges,
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid grantorId, Oid ownerId)
|
2002-02-19 00:11:58 +01:00
|
|
|
{
|
2002-04-21 02:26:44 +02:00
|
|
|
unsigned modechg;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *j;
|
2002-02-19 00:11:58 +01:00
|
|
|
Acl *new_acl;
|
|
|
|
|
2002-04-21 02:26:44 +02:00
|
|
|
modechg = is_grant ? ACL_MODECHG_ADD : ACL_MODECHG_DEL;
|
|
|
|
|
2002-02-19 00:11:58 +01:00
|
|
|
#ifdef ACLDEBUG
|
|
|
|
dumpacl(old_acl);
|
|
|
|
#endif
|
|
|
|
new_acl = old_acl;
|
|
|
|
|
|
|
|
foreach(j, grantees)
|
|
|
|
{
|
2012-06-10 21:20:04 +02:00
|
|
|
AclItem aclitem;
|
2003-09-04 17:53:04 +02:00
|
|
|
Acl *newer_acl;
|
2002-02-19 00:11:58 +01:00
|
|
|
|
2012-06-10 21:20:04 +02:00
|
|
|
aclitem.ai_grantee = lfirst_oid(j);
|
2002-02-19 00:11:58 +01:00
|
|
|
|
2003-01-24 00:39:07 +01:00
|
|
|
/*
|
2005-06-28 07:09:14 +02:00
|
|
|
* Grant options can only be granted to individual roles, not PUBLIC.
|
|
|
|
* The reason is that if a user would re-grant a privilege that he
|
|
|
|
* held through PUBLIC, and later the user is removed, the situation
|
|
|
|
* is impossible to clean up.
|
2003-01-24 00:39:07 +01:00
|
|
|
*/
|
2005-06-28 07:09:14 +02:00
|
|
|
if (is_grant && grant_option && aclitem.ai_grantee == ACL_ID_PUBLIC)
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
|
2005-06-28 07:09:14 +02:00
|
|
|
errmsg("grant options can only be granted to roles")));
|
2003-01-24 00:39:07 +01:00
|
|
|
|
2012-06-10 21:20:04 +02:00
|
|
|
aclitem.ai_grantor = grantorId;
|
2003-01-24 00:39:07 +01:00
|
|
|
|
2004-06-01 23:49:23 +02:00
|
|
|
/*
|
|
|
|
* The asymmetry in the conditions here comes from the spec. In
|
2005-10-15 04:49:52 +02:00
|
|
|
* GRANT, the grant_option flag signals WITH GRANT OPTION, which means
|
|
|
|
* to grant both the basic privilege and its grant option. But in
|
|
|
|
* REVOKE, plain revoke revokes both the basic privilege and its grant
|
|
|
|
* option, while REVOKE GRANT OPTION revokes only the option.
|
2004-06-01 23:49:23 +02:00
|
|
|
*/
|
2005-06-28 07:09:14 +02:00
|
|
|
ACLITEM_SET_PRIVS_GOPTIONS(aclitem,
|
2005-10-15 04:49:52 +02:00
|
|
|
(is_grant || !grant_option) ? privileges : ACL_NO_RIGHTS,
|
|
|
|
(!is_grant || grant_option) ? privileges : ACL_NO_RIGHTS);
|
2002-02-19 00:11:58 +01:00
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
newer_acl = aclupdate(new_acl, &aclitem, modechg, ownerId, behavior);
|
2003-09-04 17:53:04 +02:00
|
|
|
|
|
|
|
/* avoid memory leak when there are many grantees */
|
|
|
|
pfree(new_acl);
|
|
|
|
new_acl = newer_acl;
|
2002-02-19 00:11:58 +01:00
|
|
|
|
|
|
|
#ifdef ACLDEBUG
|
|
|
|
dumpacl(new_acl);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
return new_acl;
|
|
|
|
}
|
|
|
|
|
2005-12-01 03:03:01 +01:00
|
|
|
/*
|
|
|
|
* Restrict the privileges to what we can actually grant, and emit
|
|
|
|
* the standards-mandated warning and error messages.
|
|
|
|
*/
|
|
|
|
static AclMode
|
|
|
|
restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
|
|
|
|
AclMode privileges, Oid objectId, Oid grantorId,
|
2009-01-22 21:16:10 +01:00
|
|
|
AclObjectKind objkind, const char *objname,
|
|
|
|
AttrNumber att_number, const char *colname)
|
2005-12-01 03:03:01 +01:00
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
AclMode this_privileges;
|
|
|
|
AclMode whole_mask;
|
2005-12-01 03:03:01 +01:00
|
|
|
|
|
|
|
switch (objkind)
|
|
|
|
{
|
2009-01-22 21:16:10 +01:00
|
|
|
case ACL_KIND_COLUMN:
|
|
|
|
whole_mask = ACL_ALL_RIGHTS_COLUMN;
|
|
|
|
break;
|
2005-12-01 03:03:01 +01:00
|
|
|
case ACL_KIND_CLASS:
|
|
|
|
whole_mask = ACL_ALL_RIGHTS_RELATION;
|
|
|
|
break;
|
2006-01-21 03:16:21 +01:00
|
|
|
case ACL_KIND_SEQUENCE:
|
|
|
|
whole_mask = ACL_ALL_RIGHTS_SEQUENCE;
|
|
|
|
break;
|
2005-12-01 03:03:01 +01:00
|
|
|
case ACL_KIND_DATABASE:
|
|
|
|
whole_mask = ACL_ALL_RIGHTS_DATABASE;
|
|
|
|
break;
|
|
|
|
case ACL_KIND_PROC:
|
|
|
|
whole_mask = ACL_ALL_RIGHTS_FUNCTION;
|
|
|
|
break;
|
|
|
|
case ACL_KIND_LANGUAGE:
|
|
|
|
whole_mask = ACL_ALL_RIGHTS_LANGUAGE;
|
|
|
|
break;
|
2009-12-11 04:34:57 +01:00
|
|
|
case ACL_KIND_LARGEOBJECT:
|
|
|
|
whole_mask = ACL_ALL_RIGHTS_LARGEOBJECT;
|
|
|
|
break;
|
2005-12-01 03:03:01 +01:00
|
|
|
case ACL_KIND_NAMESPACE:
|
|
|
|
whole_mask = ACL_ALL_RIGHTS_NAMESPACE;
|
|
|
|
break;
|
|
|
|
case ACL_KIND_TABLESPACE:
|
|
|
|
whole_mask = ACL_ALL_RIGHTS_TABLESPACE;
|
|
|
|
break;
|
2008-12-19 17:25:19 +01:00
|
|
|
case ACL_KIND_FDW:
|
|
|
|
whole_mask = ACL_ALL_RIGHTS_FDW;
|
|
|
|
break;
|
|
|
|
case ACL_KIND_FOREIGN_SERVER:
|
|
|
|
whole_mask = ACL_ALL_RIGHTS_FOREIGN_SERVER;
|
|
|
|
break;
|
2012-07-18 16:16:16 +02:00
|
|
|
case ACL_KIND_EVENT_TRIGGER:
|
|
|
|
elog(ERROR, "grantable rights not supported for event triggers");
|
|
|
|
/* not reached, but keep compiler quiet */
|
|
|
|
return ACL_NO_RIGHTS;
|
2011-12-19 23:05:19 +01:00
|
|
|
case ACL_KIND_TYPE:
|
|
|
|
whole_mask = ACL_ALL_RIGHTS_TYPE;
|
|
|
|
break;
|
2005-12-01 03:03:01 +01:00
|
|
|
default:
|
|
|
|
elog(ERROR, "unrecognized object kind: %d", objkind);
|
|
|
|
/* not reached, but keep compiler quiet */
|
|
|
|
return ACL_NO_RIGHTS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2006-10-04 02:30:14 +02:00
|
|
|
* If we found no grant options, consider whether to issue a hard error.
|
|
|
|
* Per spec, having any privilege at all on the object will get you by
|
|
|
|
* here.
|
2005-12-01 03:03:01 +01:00
|
|
|
*/
|
|
|
|
if (avail_goptions == ACL_NO_RIGHTS)
|
|
|
|
{
|
2009-01-22 21:16:10 +01:00
|
|
|
if (pg_aclmask(objkind, objectId, att_number, grantorId,
|
2005-12-01 03:03:01 +01:00
|
|
|
whole_mask | ACL_GRANT_OPTION_FOR(whole_mask),
|
|
|
|
ACLMASK_ANY) == ACL_NO_RIGHTS)
|
2009-01-22 21:16:10 +01:00
|
|
|
{
|
|
|
|
if (objkind == ACL_KIND_COLUMN && colname)
|
|
|
|
aclcheck_error_col(ACLCHECK_NO_PRIV, objkind, objname, colname);
|
|
|
|
else
|
|
|
|
aclcheck_error(ACLCHECK_NO_PRIV, objkind, objname);
|
|
|
|
}
|
2005-12-01 03:03:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Restrict the operation to what we can actually grant or revoke, and
|
2014-05-06 18:12:18 +02:00
|
|
|
* issue a warning if appropriate. (For REVOKE this isn't quite what the
|
2006-10-04 02:30:14 +02:00
|
|
|
* spec says to do: the spec seems to want a warning only if no privilege
|
|
|
|
* bits actually change in the ACL. In practice that behavior seems much
|
|
|
|
* too noisy, as well as inconsistent with the GRANT case.)
|
2005-12-01 03:03:01 +01:00
|
|
|
*/
|
|
|
|
this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions);
|
|
|
|
if (is_grant)
|
|
|
|
{
|
|
|
|
if (this_privileges == 0)
|
2010-07-06 21:19:02 +02:00
|
|
|
{
|
2010-03-07 00:10:42 +01:00
|
|
|
if (objkind == ACL_KIND_COLUMN && colname)
|
|
|
|
ereport(WARNING,
|
|
|
|
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
|
|
|
|
errmsg("no privileges were granted for column \"%s\" of relation \"%s\"",
|
|
|
|
colname, objname)));
|
|
|
|
else
|
|
|
|
ereport(WARNING,
|
|
|
|
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
|
|
|
|
errmsg("no privileges were granted for \"%s\"",
|
|
|
|
objname)));
|
|
|
|
}
|
2005-12-01 03:03:01 +01:00
|
|
|
else if (!all_privs && this_privileges != privileges)
|
2010-03-07 00:10:42 +01:00
|
|
|
{
|
|
|
|
if (objkind == ACL_KIND_COLUMN && colname)
|
|
|
|
ereport(WARNING,
|
|
|
|
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
|
|
|
|
errmsg("not all privileges were granted for column \"%s\" of relation \"%s\"",
|
|
|
|
colname, objname)));
|
|
|
|
else
|
|
|
|
ereport(WARNING,
|
|
|
|
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
|
|
|
|
errmsg("not all privileges were granted for \"%s\"",
|
|
|
|
objname)));
|
|
|
|
}
|
2005-12-01 03:03:01 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (this_privileges == 0)
|
2010-03-07 00:10:42 +01:00
|
|
|
{
|
|
|
|
if (objkind == ACL_KIND_COLUMN && colname)
|
|
|
|
ereport(WARNING,
|
|
|
|
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
|
|
|
|
errmsg("no privileges could be revoked for column \"%s\" of relation \"%s\"",
|
|
|
|
colname, objname)));
|
|
|
|
else
|
|
|
|
ereport(WARNING,
|
|
|
|
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
|
|
|
|
errmsg("no privileges could be revoked for \"%s\"",
|
|
|
|
objname)));
|
|
|
|
}
|
2005-12-01 03:03:01 +01:00
|
|
|
else if (!all_privs && this_privileges != privileges)
|
2010-03-07 00:10:42 +01:00
|
|
|
{
|
|
|
|
if (objkind == ACL_KIND_COLUMN && colname)
|
|
|
|
ereport(WARNING,
|
|
|
|
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
|
|
|
|
errmsg("not all privileges could be revoked for column \"%s\" of relation \"%s\"",
|
|
|
|
colname, objname)));
|
|
|
|
else
|
|
|
|
ereport(WARNING,
|
|
|
|
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
|
2010-07-06 21:19:02 +02:00
|
|
|
errmsg("not all privileges could be revoked for \"%s\"",
|
|
|
|
objname)));
|
2010-03-07 00:10:42 +01:00
|
|
|
}
|
2005-12-01 03:03:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return this_privileges;
|
|
|
|
}
|
2002-02-19 00:11:58 +01:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/*
|
2001-06-10 01:21:55 +02:00
|
|
|
* Called to execute the utility commands GRANT and REVOKE
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
void
|
2001-06-10 01:21:55 +02:00
|
|
|
ExecuteGrantStmt(GrantStmt *stmt)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2005-12-01 03:03:01 +01:00
|
|
|
InternalGrant istmt;
|
2005-11-21 13:49:33 +01:00
|
|
|
ListCell *cell;
|
2008-03-24 20:12:49 +01:00
|
|
|
const char *errormsg;
|
2005-12-01 03:03:01 +01:00
|
|
|
AclMode all_privileges;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Turn the regular GrantStmt into the InternalGrant form.
|
|
|
|
*/
|
|
|
|
istmt.is_grant = stmt->is_grant;
|
|
|
|
istmt.objtype = stmt->objtype;
|
2009-10-12 22:39:42 +02:00
|
|
|
|
|
|
|
/* Collect the OIDs of the target objects */
|
|
|
|
switch (stmt->targtype)
|
|
|
|
{
|
|
|
|
case ACL_TARGET_OBJECT:
|
|
|
|
istmt.objects = objectNamesToOids(stmt->objtype, stmt->objects);
|
|
|
|
break;
|
|
|
|
case ACL_TARGET_ALL_IN_SCHEMA:
|
|
|
|
istmt.objects = objectsInSchemaToOids(stmt->objtype, stmt->objects);
|
|
|
|
break;
|
2010-02-26 03:01:40 +01:00
|
|
|
/* ACL_TARGET_DEFAULTS should not be seen here */
|
2009-10-12 22:39:42 +02:00
|
|
|
default:
|
|
|
|
elog(ERROR, "unrecognized GrantStmt.targtype: %d",
|
|
|
|
(int) stmt->targtype);
|
|
|
|
}
|
|
|
|
|
2005-12-01 03:03:01 +01:00
|
|
|
/* all_privs to be filled below */
|
|
|
|
/* privileges to be filled below */
|
2009-01-22 21:16:10 +01:00
|
|
|
istmt.col_privs = NIL; /* may get filled below */
|
|
|
|
istmt.grantees = NIL; /* filled below */
|
2005-12-01 03:03:01 +01:00
|
|
|
istmt.grant_option = stmt->grant_option;
|
|
|
|
istmt.behavior = stmt->behavior;
|
|
|
|
|
2005-11-21 13:49:33 +01:00
|
|
|
/*
|
2015-05-24 03:35:49 +02:00
|
|
|
* Convert the RoleSpec list into an Oid list. Note that at this point we
|
|
|
|
* insert an ACL_ID_PUBLIC into the list if appropriate, so downstream
|
Allow CURRENT/SESSION_USER to be used in certain commands
Commands such as ALTER USER, ALTER GROUP, ALTER ROLE, GRANT, and the
various ALTER OBJECT / OWNER TO, as well as ad-hoc clauses related to
roles such as the AUTHORIZATION clause of CREATE SCHEMA, the FOR clause
of CREATE USER MAPPING, and the FOR ROLE clause of ALTER DEFAULT
PRIVILEGES can now take the keywords CURRENT_USER and SESSION_USER as
user specifiers in place of an explicit user name.
This commit also fixes some quite ugly handling of special standards-
mandated syntax in CREATE USER MAPPING, which in particular would fail
to work in presence of a role named "current_user".
The special role specifiers PUBLIC and NONE also have more consistent
handling now.
Also take the opportunity to add location tracking to user specifiers.
Authors: Kyotaro Horiguchi. Heavily reworked by Álvaro Herrera.
Reviewed by: Rushabh Lathia, Adam Brightwell, Marti Raudsepp.
2015-03-09 19:41:54 +01:00
|
|
|
* there shouldn't be any additional work needed to support this case.
|
2005-11-21 13:49:33 +01:00
|
|
|
*/
|
|
|
|
foreach(cell, stmt->grantees)
|
|
|
|
{
|
2015-05-24 03:35:49 +02:00
|
|
|
RoleSpec *grantee = (RoleSpec *) lfirst(cell);
|
|
|
|
Oid grantee_uid;
|
2005-11-21 13:49:33 +01:00
|
|
|
|
Allow CURRENT/SESSION_USER to be used in certain commands
Commands such as ALTER USER, ALTER GROUP, ALTER ROLE, GRANT, and the
various ALTER OBJECT / OWNER TO, as well as ad-hoc clauses related to
roles such as the AUTHORIZATION clause of CREATE SCHEMA, the FOR clause
of CREATE USER MAPPING, and the FOR ROLE clause of ALTER DEFAULT
PRIVILEGES can now take the keywords CURRENT_USER and SESSION_USER as
user specifiers in place of an explicit user name.
This commit also fixes some quite ugly handling of special standards-
mandated syntax in CREATE USER MAPPING, which in particular would fail
to work in presence of a role named "current_user".
The special role specifiers PUBLIC and NONE also have more consistent
handling now.
Also take the opportunity to add location tracking to user specifiers.
Authors: Kyotaro Horiguchi. Heavily reworked by Álvaro Herrera.
Reviewed by: Rushabh Lathia, Adam Brightwell, Marti Raudsepp.
2015-03-09 19:41:54 +01:00
|
|
|
switch (grantee->roletype)
|
|
|
|
{
|
|
|
|
case ROLESPEC_PUBLIC:
|
|
|
|
grantee_uid = ACL_ID_PUBLIC;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
grantee_uid = get_rolespec_oid((Node *) grantee, false);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
istmt.grantees = lappend_oid(istmt.grantees, grantee_uid);
|
2005-11-21 13:49:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2009-06-11 16:49:15 +02:00
|
|
|
* Convert stmt->privileges, a list of AccessPriv nodes, into an AclMode
|
|
|
|
* bitmask. Note: objtype can't be ACL_OBJECT_COLUMN.
|
2005-11-21 13:49:33 +01:00
|
|
|
*/
|
2002-04-21 02:26:44 +02:00
|
|
|
switch (stmt->objtype)
|
2002-02-19 00:11:58 +01:00
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
/*
|
|
|
|
* Because this might be a sequence, we test both relation and
|
|
|
|
* sequence bits, and later do a more limited test when we know
|
|
|
|
* the object type.
|
|
|
|
*/
|
2002-04-21 02:26:44 +02:00
|
|
|
case ACL_OBJECT_RELATION:
|
2006-01-21 03:16:21 +01:00
|
|
|
all_privileges = ACL_ALL_RIGHTS_RELATION | ACL_ALL_RIGHTS_SEQUENCE;
|
2008-03-24 20:12:49 +01:00
|
|
|
errormsg = gettext_noop("invalid privilege type %s for relation");
|
2006-01-21 03:16:21 +01:00
|
|
|
break;
|
|
|
|
case ACL_OBJECT_SEQUENCE:
|
|
|
|
all_privileges = ACL_ALL_RIGHTS_SEQUENCE;
|
2008-03-24 20:12:49 +01:00
|
|
|
errormsg = gettext_noop("invalid privilege type %s for sequence");
|
2002-04-21 02:26:44 +02:00
|
|
|
break;
|
|
|
|
case ACL_OBJECT_DATABASE:
|
2005-11-21 13:49:33 +01:00
|
|
|
all_privileges = ACL_ALL_RIGHTS_DATABASE;
|
2008-03-24 20:12:49 +01:00
|
|
|
errormsg = gettext_noop("invalid privilege type %s for database");
|
2002-02-19 00:11:58 +01:00
|
|
|
break;
|
2011-12-19 23:05:19 +01:00
|
|
|
case ACL_OBJECT_DOMAIN:
|
|
|
|
all_privileges = ACL_ALL_RIGHTS_TYPE;
|
|
|
|
errormsg = gettext_noop("invalid privilege type %s for domain");
|
|
|
|
break;
|
2002-04-21 02:26:44 +02:00
|
|
|
case ACL_OBJECT_FUNCTION:
|
2005-11-21 13:49:33 +01:00
|
|
|
all_privileges = ACL_ALL_RIGHTS_FUNCTION;
|
2008-03-24 20:12:49 +01:00
|
|
|
errormsg = gettext_noop("invalid privilege type %s for function");
|
2002-02-19 00:11:58 +01:00
|
|
|
break;
|
2002-04-21 02:26:44 +02:00
|
|
|
case ACL_OBJECT_LANGUAGE:
|
2005-11-21 13:49:33 +01:00
|
|
|
all_privileges = ACL_ALL_RIGHTS_LANGUAGE;
|
2008-03-24 20:12:49 +01:00
|
|
|
errormsg = gettext_noop("invalid privilege type %s for language");
|
2002-04-21 02:26:44 +02:00
|
|
|
break;
|
2009-12-11 04:34:57 +01:00
|
|
|
case ACL_OBJECT_LARGEOBJECT:
|
|
|
|
all_privileges = ACL_ALL_RIGHTS_LARGEOBJECT;
|
|
|
|
errormsg = gettext_noop("invalid privilege type %s for large object");
|
|
|
|
break;
|
2002-04-21 02:26:44 +02:00
|
|
|
case ACL_OBJECT_NAMESPACE:
|
2005-11-21 13:49:33 +01:00
|
|
|
all_privileges = ACL_ALL_RIGHTS_NAMESPACE;
|
2008-03-24 20:12:49 +01:00
|
|
|
errormsg = gettext_noop("invalid privilege type %s for schema");
|
2002-02-19 00:11:58 +01:00
|
|
|
break;
|
2004-06-18 08:14:31 +02:00
|
|
|
case ACL_OBJECT_TABLESPACE:
|
2005-11-21 13:49:33 +01:00
|
|
|
all_privileges = ACL_ALL_RIGHTS_TABLESPACE;
|
2008-03-24 20:12:49 +01:00
|
|
|
errormsg = gettext_noop("invalid privilege type %s for tablespace");
|
2004-06-18 08:14:31 +02:00
|
|
|
break;
|
2011-12-19 23:05:19 +01:00
|
|
|
case ACL_OBJECT_TYPE:
|
|
|
|
all_privileges = ACL_ALL_RIGHTS_TYPE;
|
|
|
|
errormsg = gettext_noop("invalid privilege type %s for type");
|
|
|
|
break;
|
2008-12-19 17:25:19 +01:00
|
|
|
case ACL_OBJECT_FDW:
|
|
|
|
all_privileges = ACL_ALL_RIGHTS_FDW;
|
|
|
|
errormsg = gettext_noop("invalid privilege type %s for foreign-data wrapper");
|
|
|
|
break;
|
|
|
|
case ACL_OBJECT_FOREIGN_SERVER:
|
|
|
|
all_privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
|
|
|
|
errormsg = gettext_noop("invalid privilege type %s for foreign server");
|
|
|
|
break;
|
2002-02-19 00:11:58 +01:00
|
|
|
default:
|
2009-10-05 21:24:49 +02:00
|
|
|
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
|
|
|
|
(int) stmt->objtype);
|
2005-12-01 03:03:01 +01:00
|
|
|
/* keep compiler quiet */
|
|
|
|
all_privileges = ACL_NO_RIGHTS;
|
|
|
|
errormsg = NULL;
|
2002-02-19 00:11:58 +01:00
|
|
|
}
|
|
|
|
|
2005-06-28 21:51:26 +02:00
|
|
|
if (stmt->privileges == NIL)
|
2004-06-01 23:49:23 +02:00
|
|
|
{
|
2005-12-01 03:03:01 +01:00
|
|
|
istmt.all_privs = true;
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2005-12-01 03:03:01 +01:00
|
|
|
/*
|
|
|
|
* will be turned into ACL_ALL_RIGHTS_* by the internal routines
|
|
|
|
* depending on the object type
|
|
|
|
*/
|
|
|
|
istmt.privileges = ACL_NO_RIGHTS;
|
2004-06-01 23:49:23 +02:00
|
|
|
}
|
2002-02-19 00:11:58 +01:00
|
|
|
else
|
|
|
|
{
|
2005-12-01 03:03:01 +01:00
|
|
|
istmt.all_privs = false;
|
|
|
|
istmt.privileges = ACL_NO_RIGHTS;
|
2006-01-21 03:16:21 +01:00
|
|
|
|
2005-11-21 13:49:33 +01:00
|
|
|
foreach(cell, stmt->privileges)
|
2002-02-19 00:11:58 +01:00
|
|
|
{
|
2009-01-22 21:16:10 +01:00
|
|
|
AccessPriv *privnode = (AccessPriv *) lfirst(cell);
|
|
|
|
AclMode priv;
|
|
|
|
|
|
|
|
/*
|
2009-06-11 16:49:15 +02:00
|
|
|
* If it's a column-level specification, we just set it aside in
|
|
|
|
* col_privs for the moment; but insist it's for a relation.
|
2009-01-22 21:16:10 +01:00
|
|
|
*/
|
|
|
|
if (privnode->cols)
|
|
|
|
{
|
|
|
|
if (stmt->objtype != ACL_OBJECT_RELATION)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
|
|
|
|
errmsg("column privileges are only valid for relations")));
|
|
|
|
istmt.col_privs = lappend(istmt.col_privs, privnode);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-06-11 16:49:15 +02:00
|
|
|
if (privnode->priv_name == NULL) /* parser mistake? */
|
2009-01-22 21:16:10 +01:00
|
|
|
elog(ERROR, "AccessPriv node must specify privilege or columns");
|
|
|
|
priv = string_to_privilege(privnode->priv_name);
|
2002-02-19 00:11:58 +01:00
|
|
|
|
2005-11-21 13:49:33 +01:00
|
|
|
if (priv & ~((AclMode) all_privileges))
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
|
2008-03-24 20:12:49 +01:00
|
|
|
errmsg(errormsg, privilege_to_string(priv))));
|
2005-11-21 13:49:33 +01:00
|
|
|
|
2005-12-01 03:03:01 +01:00
|
|
|
istmt.privileges |= priv;
|
2002-02-19 00:11:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-12-01 03:03:01 +01:00
|
|
|
ExecGrantStmt_oids(&istmt);
|
2005-11-21 13:49:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ExecGrantStmt_oids
|
|
|
|
*
|
2009-10-05 21:24:49 +02:00
|
|
|
* Internal entry point for granting and revoking privileges.
|
2005-11-21 13:49:33 +01:00
|
|
|
*/
|
2009-10-05 21:24:49 +02:00
|
|
|
static void
|
2005-12-01 03:03:01 +01:00
|
|
|
ExecGrantStmt_oids(InternalGrant *istmt)
|
2005-11-21 13:49:33 +01:00
|
|
|
{
|
2005-12-01 03:03:01 +01:00
|
|
|
switch (istmt->objtype)
|
2000-08-01 00:39:17 +02:00
|
|
|
{
|
2005-11-21 13:49:33 +01:00
|
|
|
case ACL_OBJECT_RELATION:
|
2006-01-21 03:16:21 +01:00
|
|
|
case ACL_OBJECT_SEQUENCE:
|
2005-12-01 03:03:01 +01:00
|
|
|
ExecGrant_Relation(istmt);
|
2005-11-21 13:49:33 +01:00
|
|
|
break;
|
|
|
|
case ACL_OBJECT_DATABASE:
|
2005-12-01 03:03:01 +01:00
|
|
|
ExecGrant_Database(istmt);
|
2005-11-21 13:49:33 +01:00
|
|
|
break;
|
2011-12-19 23:05:19 +01:00
|
|
|
case ACL_OBJECT_DOMAIN:
|
|
|
|
case ACL_OBJECT_TYPE:
|
|
|
|
ExecGrant_Type(istmt);
|
|
|
|
break;
|
2008-12-19 17:25:19 +01:00
|
|
|
case ACL_OBJECT_FDW:
|
|
|
|
ExecGrant_Fdw(istmt);
|
|
|
|
break;
|
|
|
|
case ACL_OBJECT_FOREIGN_SERVER:
|
|
|
|
ExecGrant_ForeignServer(istmt);
|
|
|
|
break;
|
2005-11-21 13:49:33 +01:00
|
|
|
case ACL_OBJECT_FUNCTION:
|
2005-12-01 03:03:01 +01:00
|
|
|
ExecGrant_Function(istmt);
|
2005-11-21 13:49:33 +01:00
|
|
|
break;
|
|
|
|
case ACL_OBJECT_LANGUAGE:
|
2005-12-01 03:03:01 +01:00
|
|
|
ExecGrant_Language(istmt);
|
2005-11-21 13:49:33 +01:00
|
|
|
break;
|
2009-12-11 04:34:57 +01:00
|
|
|
case ACL_OBJECT_LARGEOBJECT:
|
|
|
|
ExecGrant_Largeobject(istmt);
|
|
|
|
break;
|
2005-11-21 13:49:33 +01:00
|
|
|
case ACL_OBJECT_NAMESPACE:
|
2005-12-01 03:03:01 +01:00
|
|
|
ExecGrant_Namespace(istmt);
|
2005-11-21 13:49:33 +01:00
|
|
|
break;
|
|
|
|
case ACL_OBJECT_TABLESPACE:
|
2005-12-01 03:03:01 +01:00
|
|
|
ExecGrant_Tablespace(istmt);
|
2005-11-21 13:49:33 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
|
2005-12-01 03:03:01 +01:00
|
|
|
(int) istmt->objtype);
|
2005-11-21 13:49:33 +01:00
|
|
|
}
|
Allow on-the-fly capture of DDL event details
This feature lets user code inspect and take action on DDL events.
Whenever a ddl_command_end event trigger is installed, DDL actions
executed are saved to a list which can be inspected during execution of
a function attached to ddl_command_end.
The set-returning function pg_event_trigger_ddl_commands can be used to
list actions so captured; it returns data about the type of command
executed, as well as the affected object. This is sufficient for many
uses of this feature. For the cases where it is not, we also provide a
"command" column of a new pseudo-type pg_ddl_command, which is a
pointer to a C structure that can be accessed by C code. The struct
contains all the info necessary to completely inspect and even
reconstruct the executed command.
There is no actual deparse code here; that's expected to come later.
What we have is enough infrastructure that the deparsing can be done in
an external extension. The intention is that we will add some deparsing
code in a later release, as an in-core extension.
A new test module is included. It's probably insufficient as is, but it
should be sufficient as a starting point for a more complete and
future-proof approach.
Authors: Álvaro Herrera, with some help from Andres Freund, Ian Barwick,
Abhijit Menon-Sen.
Reviews by Andres Freund, Robert Haas, Amit Kapila, Michael Paquier,
Craig Ringer, David Steele.
Additional input from Chris Browne, Dimitri Fontaine, Stephen Frost,
Petr Jelínek, Tom Lane, Jim Nasby, Steven Singer, Pavel Stěhule.
Based on original work by Dimitri Fontaine, though I didn't use his
code.
Discussion:
https://www.postgresql.org/message-id/m2txrsdzxa.fsf@2ndQuadrant.fr
https://www.postgresql.org/message-id/20131108153322.GU5809@eldon.alvh.no-ip.org
https://www.postgresql.org/message-id/20150215044814.GL3391@alvh.no-ip.org
2015-05-12 00:14:31 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Pass the info to event triggers about the just-executed GRANT. Note
|
|
|
|
* that we prefer to do it after actually executing it, because that gives
|
|
|
|
* the functions a chance to adjust the istmt with privileges actually
|
|
|
|
* granted.
|
|
|
|
*/
|
|
|
|
if (EventTriggerSupportsGrantObjectType(istmt->objtype))
|
|
|
|
EventTriggerCollectGrant(istmt);
|
2005-11-21 13:49:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* objectNamesToOids
|
|
|
|
*
|
|
|
|
* Turn a list of object names of a given type into an Oid list.
|
2011-07-09 04:19:30 +02:00
|
|
|
*
|
|
|
|
* XXX: This function doesn't take any sort of locks on the objects whose
|
|
|
|
* names it looks up. In the face of concurrent DDL, we might easily latch
|
|
|
|
* onto an old version of an object, causing the GRANT or REVOKE statement
|
|
|
|
* to fail.
|
2005-11-21 13:49:33 +01:00
|
|
|
*/
|
|
|
|
static List *
|
|
|
|
objectNamesToOids(GrantObjectType objtype, List *objnames)
|
|
|
|
{
|
2005-11-22 19:17:34 +01:00
|
|
|
List *objects = NIL;
|
|
|
|
ListCell *cell;
|
2005-11-21 13:49:33 +01:00
|
|
|
|
|
|
|
Assert(objnames != NIL);
|
|
|
|
|
|
|
|
switch (objtype)
|
|
|
|
{
|
|
|
|
case ACL_OBJECT_RELATION:
|
2006-01-21 03:16:21 +01:00
|
|
|
case ACL_OBJECT_SEQUENCE:
|
2005-11-21 13:49:33 +01:00
|
|
|
foreach(cell, objnames)
|
|
|
|
{
|
|
|
|
RangeVar *relvar = (RangeVar *) lfirst(cell);
|
2006-05-04 00:45:26 +02:00
|
|
|
Oid relOid;
|
2005-11-21 13:49:33 +01:00
|
|
|
|
Improve table locking behavior in the face of current DDL.
In the previous coding, callers were faced with an awkward choice:
look up the name, do permissions checks, and then lock the table; or
look up the name, lock the table, and then do permissions checks.
The first choice was wrong because the results of the name lookup
and permissions checks might be out-of-date by the time the table
lock was acquired, while the second allowed a user with no privileges
to interfere with access to a table by users who do have privileges
(e.g. if a malicious backend queues up for an AccessExclusiveLock on
a table on which AccessShareLock is already held, further attempts
to access the table will be blocked until the AccessExclusiveLock
is obtained and the malicious backend's transaction rolls back).
To fix, allow callers of RangeVarGetRelid() to pass a callback which
gets executed after performing the name lookup but before acquiring
the relation lock. If the name lookup is retried (because
invalidation messages are received), the callback will be re-executed
as well, so we get the best of both worlds. RangeVarGetRelid() is
renamed to RangeVarGetRelidExtended(); callers not wishing to supply
a callback can continue to invoke it as RangeVarGetRelid(), which is
now a macro. Since the only one caller that uses nowait = true now
passes a callback anyway, the RangeVarGetRelid() macro defaults nowait
as well. The callback can also be used for supplemental locking - for
example, REINDEX INDEX needs to acquire the table lock before the index
lock to reduce deadlock possibilities.
There's a lot more work to be done here to fix all the cases where this
can be a problem, but this commit provides the general infrastructure
and fixes the following specific cases: REINDEX INDEX, REINDEX TABLE,
LOCK TABLE, and and DROP TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE.
Per discussion with Noah Misch and Alvaro Herrera.
2011-11-30 16:12:27 +01:00
|
|
|
relOid = RangeVarGetRelid(relvar, NoLock, false);
|
2005-11-21 13:49:33 +01:00
|
|
|
objects = lappend_oid(objects, relOid);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ACL_OBJECT_DATABASE:
|
|
|
|
foreach(cell, objnames)
|
|
|
|
{
|
|
|
|
char *dbname = strVal(lfirst(cell));
|
2006-05-04 00:45:26 +02:00
|
|
|
Oid dbid;
|
2005-11-21 13:49:33 +01:00
|
|
|
|
2010-08-05 16:45:09 +02:00
|
|
|
dbid = get_database_oid(dbname, false);
|
2006-05-04 00:45:26 +02:00
|
|
|
objects = lappend_oid(objects, dbid);
|
2005-11-21 13:49:33 +01:00
|
|
|
}
|
|
|
|
break;
|
2011-12-19 23:05:19 +01:00
|
|
|
case ACL_OBJECT_DOMAIN:
|
|
|
|
case ACL_OBJECT_TYPE:
|
|
|
|
foreach(cell, objnames)
|
|
|
|
{
|
|
|
|
List *typname = (List *) lfirst(cell);
|
|
|
|
Oid oid;
|
|
|
|
|
|
|
|
oid = typenameTypeId(NULL, makeTypeNameFromNameList(typname));
|
|
|
|
objects = lappend_oid(objects, oid);
|
|
|
|
}
|
|
|
|
break;
|
2005-11-21 13:49:33 +01:00
|
|
|
case ACL_OBJECT_FUNCTION:
|
|
|
|
foreach(cell, objnames)
|
|
|
|
{
|
|
|
|
FuncWithArgs *func = (FuncWithArgs *) lfirst(cell);
|
|
|
|
Oid funcid;
|
|
|
|
|
|
|
|
funcid = LookupFuncNameTypeNames(func->funcname,
|
|
|
|
func->funcargs, false);
|
|
|
|
objects = lappend_oid(objects, funcid);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ACL_OBJECT_LANGUAGE:
|
|
|
|
foreach(cell, objnames)
|
|
|
|
{
|
2005-11-22 19:17:34 +01:00
|
|
|
char *langname = strVal(lfirst(cell));
|
2010-08-05 16:45:09 +02:00
|
|
|
Oid oid;
|
2005-11-21 13:49:33 +01:00
|
|
|
|
2010-08-05 16:45:09 +02:00
|
|
|
oid = get_language_oid(langname, false);
|
|
|
|
objects = lappend_oid(objects, oid);
|
2005-11-21 13:49:33 +01:00
|
|
|
}
|
|
|
|
break;
|
2009-12-11 04:34:57 +01:00
|
|
|
case ACL_OBJECT_LARGEOBJECT:
|
|
|
|
foreach(cell, objnames)
|
|
|
|
{
|
2010-06-13 19:43:13 +02:00
|
|
|
Oid lobjOid = oidparse(lfirst(cell));
|
2009-12-11 04:34:57 +01:00
|
|
|
|
|
|
|
if (!LargeObjectExists(lobjOid))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("large object %u does not exist",
|
|
|
|
lobjOid)));
|
|
|
|
|
|
|
|
objects = lappend_oid(objects, lobjOid);
|
|
|
|
}
|
|
|
|
break;
|
2005-11-21 13:49:33 +01:00
|
|
|
case ACL_OBJECT_NAMESPACE:
|
2005-11-22 19:17:34 +01:00
|
|
|
foreach(cell, objnames)
|
2005-11-21 13:49:33 +01:00
|
|
|
{
|
|
|
|
char *nspname = strVal(lfirst(cell));
|
2010-08-05 16:45:09 +02:00
|
|
|
Oid oid;
|
2005-11-21 13:49:33 +01:00
|
|
|
|
2010-08-05 16:45:09 +02:00
|
|
|
oid = get_namespace_oid(nspname, false);
|
|
|
|
objects = lappend_oid(objects, oid);
|
2005-11-21 13:49:33 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ACL_OBJECT_TABLESPACE:
|
2005-11-22 19:17:34 +01:00
|
|
|
foreach(cell, objnames)
|
2005-11-21 13:49:33 +01:00
|
|
|
{
|
2005-11-22 19:17:34 +01:00
|
|
|
char *spcname = strVal(lfirst(cell));
|
2010-08-05 16:45:09 +02:00
|
|
|
Oid spcoid;
|
2005-11-21 13:49:33 +01:00
|
|
|
|
2010-08-05 16:45:09 +02:00
|
|
|
spcoid = get_tablespace_oid(spcname, false);
|
|
|
|
objects = lappend_oid(objects, spcoid);
|
2005-11-21 13:49:33 +01:00
|
|
|
}
|
|
|
|
break;
|
2008-12-19 17:25:19 +01:00
|
|
|
case ACL_OBJECT_FDW:
|
|
|
|
foreach(cell, objnames)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
char *fdwname = strVal(lfirst(cell));
|
2011-04-01 17:28:28 +02:00
|
|
|
Oid fdwid = get_foreign_data_wrapper_oid(fdwname, false);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
objects = lappend_oid(objects, fdwid);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ACL_OBJECT_FOREIGN_SERVER:
|
|
|
|
foreach(cell, objnames)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
char *srvname = strVal(lfirst(cell));
|
2011-04-01 17:28:28 +02:00
|
|
|
Oid srvid = get_foreign_server_oid(srvname, false);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
objects = lappend_oid(objects, srvid);
|
|
|
|
}
|
|
|
|
break;
|
2005-11-21 13:49:33 +01:00
|
|
|
default:
|
|
|
|
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
|
|
|
|
(int) objtype);
|
|
|
|
}
|
|
|
|
|
|
|
|
return objects;
|
|
|
|
}
|
|
|
|
|
2009-10-12 22:39:42 +02:00
|
|
|
/*
|
|
|
|
* objectsInSchemaToOids
|
|
|
|
*
|
|
|
|
* Find all objects of a given type in specified schemas, and make a list
|
|
|
|
* of their Oids. We check USAGE privilege on the schemas, but there is
|
|
|
|
* no privilege checking on the individual objects here.
|
|
|
|
*/
|
|
|
|
static List *
|
|
|
|
objectsInSchemaToOids(GrantObjectType objtype, List *nspnames)
|
|
|
|
{
|
|
|
|
List *objects = NIL;
|
|
|
|
ListCell *cell;
|
|
|
|
|
|
|
|
foreach(cell, nspnames)
|
|
|
|
{
|
|
|
|
char *nspname = strVal(lfirst(cell));
|
|
|
|
Oid namespaceId;
|
|
|
|
List *objs;
|
|
|
|
|
2013-01-26 19:24:50 +01:00
|
|
|
namespaceId = LookupExplicitNamespace(nspname, false);
|
2009-10-12 22:39:42 +02:00
|
|
|
|
|
|
|
switch (objtype)
|
|
|
|
{
|
|
|
|
case ACL_OBJECT_RELATION:
|
|
|
|
objs = getRelationsInNamespace(namespaceId, RELKIND_RELATION);
|
|
|
|
objects = list_concat(objects, objs);
|
|
|
|
objs = getRelationsInNamespace(namespaceId, RELKIND_VIEW);
|
|
|
|
objects = list_concat(objects, objs);
|
2013-03-04 01:23:31 +01:00
|
|
|
objs = getRelationsInNamespace(namespaceId, RELKIND_MATVIEW);
|
|
|
|
objects = list_concat(objects, objs);
|
2011-01-02 05:48:11 +01:00
|
|
|
objs = getRelationsInNamespace(namespaceId, RELKIND_FOREIGN_TABLE);
|
|
|
|
objects = list_concat(objects, objs);
|
Implement table partitioning.
Table partitioning is like table inheritance and reuses much of the
existing infrastructure, but there are some important differences.
The parent is called a partitioned table and is always empty; it may
not have indexes or non-inherited constraints, since those make no
sense for a relation with no data of its own. The children are called
partitions and contain all of the actual data. Each partition has an
implicit partitioning constraint. Multiple inheritance is not
allowed, and partitioning and inheritance can't be mixed. Partitions
can't have extra columns and may not allow nulls unless the parent
does. Tuples inserted into the parent are automatically routed to the
correct partition, so tuple-routing ON INSERT triggers are not needed.
Tuple routing isn't yet supported for partitions which are foreign
tables, and it doesn't handle updates that cross partition boundaries.
Currently, tables can be range-partitioned or list-partitioned. List
partitioning is limited to a single column, but range partitioning can
involve multiple columns. A partitioning "column" can be an
expression.
Because table partitioning is less general than table inheritance, it
is hoped that it will be easier to reason about properties of
partitions, and therefore that this will serve as a better foundation
for a variety of possible optimizations, including query planner
optimizations. The tuple routing based which this patch does based on
the implicit partitioning constraints is an example of this, but it
seems likely that many other useful optimizations are also possible.
Amit Langote, reviewed and tested by Robert Haas, Ashutosh Bapat,
Amit Kapila, Rajkumar Raghuwanshi, Corey Huinker, Jaime Casanova,
Rushabh Lathia, Erik Rijkers, among others. Minor revisions by me.
2016-12-07 19:17:43 +01:00
|
|
|
objs = getRelationsInNamespace(namespaceId, RELKIND_PARTITIONED_TABLE);
|
|
|
|
objects = list_concat(objects, objs);
|
2009-10-12 22:39:42 +02:00
|
|
|
break;
|
|
|
|
case ACL_OBJECT_SEQUENCE:
|
|
|
|
objs = getRelationsInNamespace(namespaceId, RELKIND_SEQUENCE);
|
|
|
|
objects = list_concat(objects, objs);
|
|
|
|
break;
|
|
|
|
case ACL_OBJECT_FUNCTION:
|
|
|
|
{
|
|
|
|
ScanKeyData key[1];
|
|
|
|
Relation rel;
|
|
|
|
HeapScanDesc scan;
|
|
|
|
HeapTuple tuple;
|
|
|
|
|
|
|
|
ScanKeyInit(&key[0],
|
|
|
|
Anum_pg_proc_pronamespace,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(namespaceId));
|
|
|
|
|
|
|
|
rel = heap_open(ProcedureRelationId, AccessShareLock);
|
Use an MVCC snapshot, rather than SnapshotNow, for catalog scans.
SnapshotNow scans have the undesirable property that, in the face of
concurrent updates, the scan can fail to see either the old or the new
versions of the row. In many cases, we work around this by requiring
DDL operations to hold AccessExclusiveLock on the object being
modified; in some cases, the existing locking is inadequate and random
failures occur as a result. This commit doesn't change anything
related to locking, but will hopefully pave the way to allowing lock
strength reductions in the future.
The major issue has held us back from making this change in the past
is that taking an MVCC snapshot is significantly more expensive than
using a static special snapshot such as SnapshotNow. However, testing
of various worst-case scenarios reveals that this problem is not
severe except under fairly extreme workloads. To mitigate those
problems, we avoid retaking the MVCC snapshot for each new scan;
instead, we take a new snapshot only when invalidation messages have
been processed. The catcache machinery already requires that
invalidation messages be sent before releasing the related heavyweight
lock; else other backends might rely on locally-cached data rather
than scanning the catalog at all. Thus, making snapshot reuse
dependent on the same guarantees shouldn't break anything that wasn't
already subtly broken.
Patch by me. Review by Michael Paquier and Andres Freund.
2013-07-02 15:47:01 +02:00
|
|
|
scan = heap_beginscan_catalog(rel, 1, key);
|
2009-10-12 22:39:42 +02:00
|
|
|
|
|
|
|
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
|
|
|
|
{
|
|
|
|
objects = lappend_oid(objects, HeapTupleGetOid(tuple));
|
|
|
|
}
|
|
|
|
|
|
|
|
heap_endscan(scan);
|
|
|
|
heap_close(rel, AccessShareLock);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* should not happen */
|
|
|
|
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
|
|
|
|
(int) objtype);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return objects;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* getRelationsInNamespace
|
|
|
|
*
|
|
|
|
* Return Oid list of relations in given namespace filtered by relation kind
|
|
|
|
*/
|
|
|
|
static List *
|
|
|
|
getRelationsInNamespace(Oid namespaceId, char relkind)
|
|
|
|
{
|
|
|
|
List *relations = NIL;
|
|
|
|
ScanKeyData key[2];
|
|
|
|
Relation rel;
|
|
|
|
HeapScanDesc scan;
|
|
|
|
HeapTuple tuple;
|
|
|
|
|
|
|
|
ScanKeyInit(&key[0],
|
|
|
|
Anum_pg_class_relnamespace,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(namespaceId));
|
|
|
|
ScanKeyInit(&key[1],
|
|
|
|
Anum_pg_class_relkind,
|
|
|
|
BTEqualStrategyNumber, F_CHAREQ,
|
|
|
|
CharGetDatum(relkind));
|
|
|
|
|
|
|
|
rel = heap_open(RelationRelationId, AccessShareLock);
|
Use an MVCC snapshot, rather than SnapshotNow, for catalog scans.
SnapshotNow scans have the undesirable property that, in the face of
concurrent updates, the scan can fail to see either the old or the new
versions of the row. In many cases, we work around this by requiring
DDL operations to hold AccessExclusiveLock on the object being
modified; in some cases, the existing locking is inadequate and random
failures occur as a result. This commit doesn't change anything
related to locking, but will hopefully pave the way to allowing lock
strength reductions in the future.
The major issue has held us back from making this change in the past
is that taking an MVCC snapshot is significantly more expensive than
using a static special snapshot such as SnapshotNow. However, testing
of various worst-case scenarios reveals that this problem is not
severe except under fairly extreme workloads. To mitigate those
problems, we avoid retaking the MVCC snapshot for each new scan;
instead, we take a new snapshot only when invalidation messages have
been processed. The catcache machinery already requires that
invalidation messages be sent before releasing the related heavyweight
lock; else other backends might rely on locally-cached data rather
than scanning the catalog at all. Thus, making snapshot reuse
dependent on the same guarantees shouldn't break anything that wasn't
already subtly broken.
Patch by me. Review by Michael Paquier and Andres Freund.
2013-07-02 15:47:01 +02:00
|
|
|
scan = heap_beginscan_catalog(rel, 2, key);
|
2009-10-12 22:39:42 +02:00
|
|
|
|
|
|
|
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
|
|
|
|
{
|
|
|
|
relations = lappend_oid(relations, HeapTupleGetOid(tuple));
|
|
|
|
}
|
|
|
|
|
|
|
|
heap_endscan(scan);
|
|
|
|
heap_close(rel, AccessShareLock);
|
|
|
|
|
|
|
|
return relations;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-05 21:24:49 +02:00
|
|
|
/*
|
|
|
|
* ALTER DEFAULT PRIVILEGES statement
|
|
|
|
*/
|
|
|
|
void
|
2016-09-06 18:00:00 +02:00
|
|
|
ExecAlterDefaultPrivilegesStmt(ParseState *pstate, AlterDefaultPrivilegesStmt *stmt)
|
2009-10-05 21:24:49 +02:00
|
|
|
{
|
|
|
|
GrantStmt *action = stmt->action;
|
|
|
|
InternalDefaultACL iacls;
|
|
|
|
ListCell *cell;
|
2015-03-09 21:00:43 +01:00
|
|
|
List *rolespecs = NIL;
|
2009-10-05 21:24:49 +02:00
|
|
|
List *nspnames = NIL;
|
2015-03-09 21:00:43 +01:00
|
|
|
DefElem *drolespecs = NULL;
|
2009-10-05 21:24:49 +02:00
|
|
|
DefElem *dnspnames = NULL;
|
|
|
|
AclMode all_privileges;
|
|
|
|
const char *errormsg;
|
|
|
|
|
|
|
|
/* Deconstruct the "options" part of the statement */
|
|
|
|
foreach(cell, stmt->options)
|
|
|
|
{
|
|
|
|
DefElem *defel = (DefElem *) lfirst(cell);
|
|
|
|
|
|
|
|
if (strcmp(defel->defname, "schemas") == 0)
|
|
|
|
{
|
|
|
|
if (dnspnames)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
2016-09-06 18:00:00 +02:00
|
|
|
errmsg("conflicting or redundant options"),
|
|
|
|
parser_errposition(pstate, defel->location)));
|
2009-10-05 21:24:49 +02:00
|
|
|
dnspnames = defel;
|
|
|
|
}
|
|
|
|
else if (strcmp(defel->defname, "roles") == 0)
|
|
|
|
{
|
2015-03-09 21:00:43 +01:00
|
|
|
if (drolespecs)
|
2009-10-05 21:24:49 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
2016-09-06 18:00:00 +02:00
|
|
|
errmsg("conflicting or redundant options"),
|
|
|
|
parser_errposition(pstate, defel->location)));
|
2015-03-09 21:00:43 +01:00
|
|
|
drolespecs = defel;
|
2009-10-05 21:24:49 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
elog(ERROR, "option \"%s\" not recognized", defel->defname);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dnspnames)
|
|
|
|
nspnames = (List *) dnspnames->arg;
|
2015-03-09 21:00:43 +01:00
|
|
|
if (drolespecs)
|
|
|
|
rolespecs = (List *) drolespecs->arg;
|
2009-10-05 21:24:49 +02:00
|
|
|
|
|
|
|
/* Prepare the InternalDefaultACL representation of the statement */
|
|
|
|
/* roleid to be filled below */
|
|
|
|
/* nspid to be filled in SetDefaultACLsInSchemas */
|
|
|
|
iacls.is_grant = action->is_grant;
|
|
|
|
iacls.objtype = action->objtype;
|
|
|
|
/* all_privs to be filled below */
|
|
|
|
/* privileges to be filled below */
|
|
|
|
iacls.grantees = NIL; /* filled below */
|
|
|
|
iacls.grant_option = action->grant_option;
|
|
|
|
iacls.behavior = action->behavior;
|
|
|
|
|
|
|
|
/*
|
2015-05-24 03:35:49 +02:00
|
|
|
* Convert the RoleSpec list into an Oid list. Note that at this point we
|
|
|
|
* insert an ACL_ID_PUBLIC into the list if appropriate, so downstream
|
Allow CURRENT/SESSION_USER to be used in certain commands
Commands such as ALTER USER, ALTER GROUP, ALTER ROLE, GRANT, and the
various ALTER OBJECT / OWNER TO, as well as ad-hoc clauses related to
roles such as the AUTHORIZATION clause of CREATE SCHEMA, the FOR clause
of CREATE USER MAPPING, and the FOR ROLE clause of ALTER DEFAULT
PRIVILEGES can now take the keywords CURRENT_USER and SESSION_USER as
user specifiers in place of an explicit user name.
This commit also fixes some quite ugly handling of special standards-
mandated syntax in CREATE USER MAPPING, which in particular would fail
to work in presence of a role named "current_user".
The special role specifiers PUBLIC and NONE also have more consistent
handling now.
Also take the opportunity to add location tracking to user specifiers.
Authors: Kyotaro Horiguchi. Heavily reworked by Álvaro Herrera.
Reviewed by: Rushabh Lathia, Adam Brightwell, Marti Raudsepp.
2015-03-09 19:41:54 +01:00
|
|
|
* there shouldn't be any additional work needed to support this case.
|
2009-10-05 21:24:49 +02:00
|
|
|
*/
|
|
|
|
foreach(cell, action->grantees)
|
|
|
|
{
|
2015-05-24 03:35:49 +02:00
|
|
|
RoleSpec *grantee = (RoleSpec *) lfirst(cell);
|
|
|
|
Oid grantee_uid;
|
2009-10-05 21:24:49 +02:00
|
|
|
|
Allow CURRENT/SESSION_USER to be used in certain commands
Commands such as ALTER USER, ALTER GROUP, ALTER ROLE, GRANT, and the
various ALTER OBJECT / OWNER TO, as well as ad-hoc clauses related to
roles such as the AUTHORIZATION clause of CREATE SCHEMA, the FOR clause
of CREATE USER MAPPING, and the FOR ROLE clause of ALTER DEFAULT
PRIVILEGES can now take the keywords CURRENT_USER and SESSION_USER as
user specifiers in place of an explicit user name.
This commit also fixes some quite ugly handling of special standards-
mandated syntax in CREATE USER MAPPING, which in particular would fail
to work in presence of a role named "current_user".
The special role specifiers PUBLIC and NONE also have more consistent
handling now.
Also take the opportunity to add location tracking to user specifiers.
Authors: Kyotaro Horiguchi. Heavily reworked by Álvaro Herrera.
Reviewed by: Rushabh Lathia, Adam Brightwell, Marti Raudsepp.
2015-03-09 19:41:54 +01:00
|
|
|
switch (grantee->roletype)
|
|
|
|
{
|
|
|
|
case ROLESPEC_PUBLIC:
|
|
|
|
grantee_uid = ACL_ID_PUBLIC;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
grantee_uid = get_rolespec_oid((Node *) grantee, false);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
iacls.grantees = lappend_oid(iacls.grantees, grantee_uid);
|
2009-10-05 21:24:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-02-26 03:01:40 +01:00
|
|
|
* Convert action->privileges, a list of privilege strings, into an
|
|
|
|
* AclMode bitmask.
|
2009-10-05 21:24:49 +02:00
|
|
|
*/
|
|
|
|
switch (action->objtype)
|
|
|
|
{
|
|
|
|
case ACL_OBJECT_RELATION:
|
|
|
|
all_privileges = ACL_ALL_RIGHTS_RELATION;
|
|
|
|
errormsg = gettext_noop("invalid privilege type %s for relation");
|
|
|
|
break;
|
|
|
|
case ACL_OBJECT_SEQUENCE:
|
|
|
|
all_privileges = ACL_ALL_RIGHTS_SEQUENCE;
|
|
|
|
errormsg = gettext_noop("invalid privilege type %s for sequence");
|
|
|
|
break;
|
|
|
|
case ACL_OBJECT_FUNCTION:
|
|
|
|
all_privileges = ACL_ALL_RIGHTS_FUNCTION;
|
|
|
|
errormsg = gettext_noop("invalid privilege type %s for function");
|
|
|
|
break;
|
2011-12-19 23:05:19 +01:00
|
|
|
case ACL_OBJECT_TYPE:
|
|
|
|
all_privileges = ACL_ALL_RIGHTS_TYPE;
|
|
|
|
errormsg = gettext_noop("invalid privilege type %s for type");
|
|
|
|
break;
|
2009-10-05 21:24:49 +02:00
|
|
|
default:
|
|
|
|
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
|
|
|
|
(int) action->objtype);
|
|
|
|
/* keep compiler quiet */
|
|
|
|
all_privileges = ACL_NO_RIGHTS;
|
|
|
|
errormsg = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (action->privileges == NIL)
|
|
|
|
{
|
|
|
|
iacls.all_privs = true;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* will be turned into ACL_ALL_RIGHTS_* by the internal routines
|
|
|
|
* depending on the object type
|
|
|
|
*/
|
|
|
|
iacls.privileges = ACL_NO_RIGHTS;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
iacls.all_privs = false;
|
|
|
|
iacls.privileges = ACL_NO_RIGHTS;
|
|
|
|
|
|
|
|
foreach(cell, action->privileges)
|
|
|
|
{
|
|
|
|
AccessPriv *privnode = (AccessPriv *) lfirst(cell);
|
|
|
|
AclMode priv;
|
|
|
|
|
|
|
|
if (privnode->cols)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
|
2010-02-26 03:01:40 +01:00
|
|
|
errmsg("default privileges cannot be set for columns")));
|
2009-10-05 21:24:49 +02:00
|
|
|
|
|
|
|
if (privnode->priv_name == NULL) /* parser mistake? */
|
|
|
|
elog(ERROR, "AccessPriv node must specify privilege");
|
|
|
|
priv = string_to_privilege(privnode->priv_name);
|
|
|
|
|
|
|
|
if (priv & ~((AclMode) all_privileges))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
|
|
|
|
errmsg(errormsg, privilege_to_string(priv))));
|
|
|
|
|
|
|
|
iacls.privileges |= priv;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-09 21:00:43 +01:00
|
|
|
if (rolespecs == NIL)
|
2009-10-05 21:24:49 +02:00
|
|
|
{
|
|
|
|
/* Set permissions for myself */
|
|
|
|
iacls.roleid = GetUserId();
|
|
|
|
|
|
|
|
SetDefaultACLsInSchemas(&iacls, nspnames);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Look up the role OIDs and do permissions checks */
|
|
|
|
ListCell *rolecell;
|
|
|
|
|
2015-03-09 21:00:43 +01:00
|
|
|
foreach(rolecell, rolespecs)
|
2009-10-05 21:24:49 +02:00
|
|
|
{
|
2015-03-09 21:00:43 +01:00
|
|
|
RoleSpec *rolespec = lfirst(rolecell);
|
2009-10-05 21:24:49 +02:00
|
|
|
|
2015-03-09 21:00:43 +01:00
|
|
|
iacls.roleid = get_rolespec_oid((Node *) rolespec, false);
|
2009-10-05 21:24:49 +02:00
|
|
|
|
|
|
|
/*
|
2010-02-26 03:01:40 +01:00
|
|
|
* We insist that calling user be a member of each target role. If
|
|
|
|
* he has that, he could become that role anyway via SET ROLE, so
|
|
|
|
* FOR ROLE is just a syntactic convenience and doesn't give any
|
|
|
|
* special privileges.
|
2009-10-05 21:24:49 +02:00
|
|
|
*/
|
|
|
|
check_is_member_of_role(GetUserId(), iacls.roleid);
|
|
|
|
|
|
|
|
SetDefaultACLsInSchemas(&iacls, nspnames);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Process ALTER DEFAULT PRIVILEGES for a list of target schemas
|
|
|
|
*
|
|
|
|
* All fields of *iacls except nspid were filled already
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames)
|
|
|
|
{
|
|
|
|
if (nspnames == NIL)
|
|
|
|
{
|
|
|
|
/* Set database-wide permissions if no schema was specified */
|
|
|
|
iacls->nspid = InvalidOid;
|
|
|
|
|
|
|
|
SetDefaultACL(iacls);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-06-09 21:26:40 +02:00
|
|
|
/* Look up the schema OIDs and set permissions for each one */
|
2009-10-05 21:24:49 +02:00
|
|
|
ListCell *nspcell;
|
|
|
|
|
|
|
|
foreach(nspcell, nspnames)
|
|
|
|
{
|
|
|
|
char *nspname = strVal(lfirst(nspcell));
|
|
|
|
|
2010-08-05 16:45:09 +02:00
|
|
|
iacls->nspid = get_namespace_oid(nspname, false);
|
2009-10-05 21:24:49 +02:00
|
|
|
|
2013-06-09 21:26:40 +02:00
|
|
|
/*
|
|
|
|
* We used to insist that the target role have CREATE privileges
|
|
|
|
* on the schema, since without that it wouldn't be able to create
|
|
|
|
* an object for which these default privileges would apply.
|
|
|
|
* However, this check proved to be more confusing than helpful,
|
|
|
|
* and it also caused certain database states to not be
|
|
|
|
* dumpable/restorable, since revoking CREATE doesn't cause
|
|
|
|
* default privileges for the schema to go away. So now, we just
|
|
|
|
* allow the ALTER; if the user lacks CREATE he'll find out when
|
|
|
|
* he tries to create an object.
|
|
|
|
*/
|
2009-10-05 21:24:49 +02:00
|
|
|
|
|
|
|
SetDefaultACL(iacls);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create or update a pg_default_acl entry
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
SetDefaultACL(InternalDefaultACL *iacls)
|
|
|
|
{
|
|
|
|
AclMode this_privileges = iacls->privileges;
|
|
|
|
char objtype;
|
|
|
|
Relation rel;
|
|
|
|
HeapTuple tuple;
|
|
|
|
bool isNew;
|
2010-04-05 03:58:03 +02:00
|
|
|
Acl *def_acl;
|
2009-10-05 21:24:49 +02:00
|
|
|
Acl *old_acl;
|
|
|
|
Acl *new_acl;
|
|
|
|
HeapTuple newtuple;
|
|
|
|
Datum values[Natts_pg_default_acl];
|
|
|
|
bool nulls[Natts_pg_default_acl];
|
|
|
|
bool replaces[Natts_pg_default_acl];
|
|
|
|
int noldmembers;
|
|
|
|
int nnewmembers;
|
|
|
|
Oid *oldmembers;
|
|
|
|
Oid *newmembers;
|
|
|
|
|
|
|
|
rel = heap_open(DefaultAclRelationId, RowExclusiveLock);
|
|
|
|
|
2010-04-05 03:58:03 +02:00
|
|
|
/*
|
|
|
|
* The default for a global entry is the hard-wired default ACL for the
|
2014-05-06 18:12:18 +02:00
|
|
|
* particular object type. The default for non-global entries is an empty
|
2010-04-05 03:58:03 +02:00
|
|
|
* ACL. This must be so because global entries replace the hard-wired
|
|
|
|
* defaults, while others are added on.
|
|
|
|
*/
|
|
|
|
if (!OidIsValid(iacls->nspid))
|
|
|
|
def_acl = acldefault(iacls->objtype, iacls->roleid);
|
|
|
|
else
|
|
|
|
def_acl = make_empty_acl();
|
|
|
|
|
2009-10-05 21:24:49 +02:00
|
|
|
/*
|
2010-02-26 03:01:40 +01:00
|
|
|
* Convert ACL object type to pg_default_acl object type and handle
|
|
|
|
* all_privs option
|
2009-10-05 21:24:49 +02:00
|
|
|
*/
|
|
|
|
switch (iacls->objtype)
|
|
|
|
{
|
|
|
|
case ACL_OBJECT_RELATION:
|
|
|
|
objtype = DEFACLOBJ_RELATION;
|
|
|
|
if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
|
|
|
|
this_privileges = ACL_ALL_RIGHTS_RELATION;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ACL_OBJECT_SEQUENCE:
|
|
|
|
objtype = DEFACLOBJ_SEQUENCE;
|
|
|
|
if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
|
|
|
|
this_privileges = ACL_ALL_RIGHTS_SEQUENCE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ACL_OBJECT_FUNCTION:
|
|
|
|
objtype = DEFACLOBJ_FUNCTION;
|
|
|
|
if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
|
|
|
|
this_privileges = ACL_ALL_RIGHTS_FUNCTION;
|
|
|
|
break;
|
|
|
|
|
2011-12-19 23:05:19 +01:00
|
|
|
case ACL_OBJECT_TYPE:
|
|
|
|
objtype = DEFACLOBJ_TYPE;
|
|
|
|
if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
|
|
|
|
this_privileges = ACL_ALL_RIGHTS_TYPE;
|
|
|
|
break;
|
|
|
|
|
2009-10-05 21:24:49 +02:00
|
|
|
default:
|
|
|
|
elog(ERROR, "unrecognized objtype: %d",
|
|
|
|
(int) iacls->objtype);
|
|
|
|
objtype = 0; /* keep compiler quiet */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Search for existing row for this object type in catalog */
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache3(DEFACLROLENSPOBJ,
|
|
|
|
ObjectIdGetDatum(iacls->roleid),
|
|
|
|
ObjectIdGetDatum(iacls->nspid),
|
2010-02-26 03:01:40 +01:00
|
|
|
CharGetDatum(objtype));
|
2009-10-05 21:24:49 +02:00
|
|
|
|
|
|
|
if (HeapTupleIsValid(tuple))
|
|
|
|
{
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
|
|
|
|
|
|
|
aclDatum = SysCacheGetAttr(DEFACLROLENSPOBJ, tuple,
|
|
|
|
Anum_pg_default_acl_defaclacl,
|
|
|
|
&isNull);
|
|
|
|
if (!isNull)
|
|
|
|
old_acl = DatumGetAclPCopy(aclDatum);
|
|
|
|
else
|
2010-04-05 03:58:03 +02:00
|
|
|
old_acl = NULL; /* this case shouldn't happen, probably */
|
2009-10-05 21:24:49 +02:00
|
|
|
isNew = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
old_acl = NULL;
|
|
|
|
isNew = true;
|
|
|
|
}
|
|
|
|
|
2010-04-05 03:09:53 +02:00
|
|
|
if (old_acl != NULL)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* We need the members of both old and new ACLs so we can correct the
|
|
|
|
* shared dependency information. Collect data before
|
|
|
|
* merge_acl_with_grant throws away old_acl.
|
|
|
|
*/
|
|
|
|
noldmembers = aclmembers(old_acl, &oldmembers);
|
|
|
|
}
|
|
|
|
else
|
2009-10-05 21:24:49 +02:00
|
|
|
{
|
2010-04-05 03:58:03 +02:00
|
|
|
/* If no or null entry, start with the default ACL value */
|
|
|
|
old_acl = aclcopy(def_acl);
|
2010-04-05 03:09:53 +02:00
|
|
|
/* There are no old member roles according to the catalogs */
|
|
|
|
noldmembers = 0;
|
|
|
|
oldmembers = NULL;
|
|
|
|
}
|
2009-10-05 21:24:49 +02:00
|
|
|
|
|
|
|
/*
|
2010-02-26 03:01:40 +01:00
|
|
|
* Generate new ACL. Grantor of rights is always the same as the target
|
|
|
|
* role.
|
2009-10-05 21:24:49 +02:00
|
|
|
*/
|
|
|
|
new_acl = merge_acl_with_grant(old_acl,
|
|
|
|
iacls->is_grant,
|
|
|
|
iacls->grant_option,
|
|
|
|
iacls->behavior,
|
|
|
|
iacls->grantees,
|
|
|
|
this_privileges,
|
|
|
|
iacls->roleid,
|
|
|
|
iacls->roleid);
|
|
|
|
|
2010-04-05 03:58:03 +02:00
|
|
|
/*
|
|
|
|
* If the result is the same as the default value, we do not need an
|
2010-07-06 21:19:02 +02:00
|
|
|
* explicit pg_default_acl entry, and should in fact remove the entry if
|
|
|
|
* it exists. Must sort both arrays to compare properly.
|
2010-04-05 03:58:03 +02:00
|
|
|
*/
|
|
|
|
aclitemsort(new_acl);
|
|
|
|
aclitemsort(def_acl);
|
|
|
|
if (aclequal(new_acl, def_acl))
|
2009-10-05 21:24:49 +02:00
|
|
|
{
|
2010-04-05 03:58:03 +02:00
|
|
|
/* delete old entry, if indeed there is one */
|
|
|
|
if (!isNew)
|
|
|
|
{
|
|
|
|
ObjectAddress myself;
|
2009-10-05 21:24:49 +02:00
|
|
|
|
2010-04-05 03:58:03 +02:00
|
|
|
/*
|
|
|
|
* The dependency machinery will take care of removing all
|
|
|
|
* associated dependency entries. We use DROP_RESTRICT since
|
|
|
|
* there shouldn't be anything depending on this entry.
|
|
|
|
*/
|
|
|
|
myself.classId = DefaultAclRelationId;
|
|
|
|
myself.objectId = HeapTupleGetOid(tuple);
|
|
|
|
myself.objectSubId = 0;
|
|
|
|
|
2012-01-26 15:24:54 +01:00
|
|
|
performDeletion(&myself, DROP_RESTRICT, 0);
|
2010-04-05 03:58:03 +02:00
|
|
|
}
|
2009-10-05 21:24:49 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-04-05 03:58:03 +02:00
|
|
|
/* Prepare to insert or update pg_default_acl entry */
|
|
|
|
MemSet(values, 0, sizeof(values));
|
|
|
|
MemSet(nulls, false, sizeof(nulls));
|
|
|
|
MemSet(replaces, false, sizeof(replaces));
|
2009-10-05 21:24:49 +02:00
|
|
|
|
2010-04-05 03:58:03 +02:00
|
|
|
if (isNew)
|
|
|
|
{
|
|
|
|
/* insert new entry */
|
|
|
|
values[Anum_pg_default_acl_defaclrole - 1] = ObjectIdGetDatum(iacls->roleid);
|
|
|
|
values[Anum_pg_default_acl_defaclnamespace - 1] = ObjectIdGetDatum(iacls->nspid);
|
|
|
|
values[Anum_pg_default_acl_defaclobjtype - 1] = CharGetDatum(objtype);
|
|
|
|
values[Anum_pg_default_acl_defaclacl - 1] = PointerGetDatum(new_acl);
|
|
|
|
|
|
|
|
newtuple = heap_form_tuple(RelationGetDescr(rel), values, nulls);
|
|
|
|
simple_heap_insert(rel, newtuple);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* update existing entry */
|
|
|
|
values[Anum_pg_default_acl_defaclacl - 1] = PointerGetDatum(new_acl);
|
|
|
|
replaces[Anum_pg_default_acl_defaclacl - 1] = true;
|
2009-10-05 21:24:49 +02:00
|
|
|
|
2010-04-05 03:58:03 +02:00
|
|
|
newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel),
|
|
|
|
values, nulls, replaces);
|
|
|
|
simple_heap_update(rel, &newtuple->t_self, newtuple);
|
|
|
|
}
|
2009-10-05 21:24:49 +02:00
|
|
|
|
2010-04-05 03:58:03 +02:00
|
|
|
/* keep the catalog indexes up to date */
|
|
|
|
CatalogUpdateIndexes(rel, newtuple);
|
2009-10-05 21:24:49 +02:00
|
|
|
|
2010-04-05 03:58:03 +02:00
|
|
|
/* these dependencies don't change in an update */
|
|
|
|
if (isNew)
|
2009-10-05 21:24:49 +02:00
|
|
|
{
|
2010-04-05 03:58:03 +02:00
|
|
|
/* dependency on role */
|
|
|
|
recordDependencyOnOwner(DefaultAclRelationId,
|
|
|
|
HeapTupleGetOid(newtuple),
|
|
|
|
iacls->roleid);
|
2009-10-05 21:24:49 +02:00
|
|
|
|
2010-04-05 03:58:03 +02:00
|
|
|
/* dependency on namespace */
|
|
|
|
if (OidIsValid(iacls->nspid))
|
|
|
|
{
|
|
|
|
ObjectAddress myself,
|
2010-07-06 21:19:02 +02:00
|
|
|
referenced;
|
2009-10-05 21:24:49 +02:00
|
|
|
|
2010-04-05 03:58:03 +02:00
|
|
|
myself.classId = DefaultAclRelationId;
|
|
|
|
myself.objectId = HeapTupleGetOid(newtuple);
|
|
|
|
myself.objectSubId = 0;
|
2009-10-05 21:24:49 +02:00
|
|
|
|
2010-04-05 03:58:03 +02:00
|
|
|
referenced.classId = NamespaceRelationId;
|
|
|
|
referenced.objectId = iacls->nspid;
|
|
|
|
referenced.objectSubId = 0;
|
2009-10-05 21:24:49 +02:00
|
|
|
|
2010-04-05 03:58:03 +02:00
|
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
|
|
|
|
}
|
|
|
|
}
|
2009-10-05 21:24:49 +02:00
|
|
|
|
2010-04-05 03:58:03 +02:00
|
|
|
/*
|
|
|
|
* Update the shared dependency ACL info
|
|
|
|
*/
|
|
|
|
nnewmembers = aclmembers(new_acl, &newmembers);
|
2009-10-05 21:24:49 +02:00
|
|
|
|
2010-04-05 03:58:03 +02:00
|
|
|
updateAclDependencies(DefaultAclRelationId,
|
|
|
|
HeapTupleGetOid(newtuple), 0,
|
|
|
|
iacls->roleid,
|
|
|
|
noldmembers, oldmembers,
|
|
|
|
nnewmembers, newmembers);
|
2013-03-18 03:55:14 +01:00
|
|
|
|
|
|
|
if (isNew)
|
|
|
|
InvokeObjectPostCreateHook(DefaultAclRelationId,
|
|
|
|
HeapTupleGetOid(newtuple), 0);
|
|
|
|
else
|
|
|
|
InvokeObjectPostAlterHook(DefaultAclRelationId,
|
|
|
|
HeapTupleGetOid(newtuple), 0);
|
2010-04-05 03:58:03 +02:00
|
|
|
}
|
2009-10-05 21:24:49 +02:00
|
|
|
|
|
|
|
if (HeapTupleIsValid(tuple))
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
|
|
|
heap_close(rel, RowExclusiveLock);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* RemoveRoleFromObjectACL
|
|
|
|
*
|
|
|
|
* Used by shdepDropOwned to remove mentions of a role in ACLs
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
|
|
|
|
{
|
|
|
|
if (classid == DefaultAclRelationId)
|
|
|
|
{
|
|
|
|
InternalDefaultACL iacls;
|
|
|
|
Form_pg_default_acl pg_default_acl_tuple;
|
|
|
|
Relation rel;
|
|
|
|
ScanKeyData skey[1];
|
|
|
|
SysScanDesc scan;
|
|
|
|
HeapTuple tuple;
|
|
|
|
|
|
|
|
/* first fetch info needed by SetDefaultACL */
|
|
|
|
rel = heap_open(DefaultAclRelationId, AccessShareLock);
|
|
|
|
|
|
|
|
ScanKeyInit(&skey[0],
|
|
|
|
ObjectIdAttributeNumber,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(objid));
|
|
|
|
|
|
|
|
scan = systable_beginscan(rel, DefaultAclOidIndexId, true,
|
Use an MVCC snapshot, rather than SnapshotNow, for catalog scans.
SnapshotNow scans have the undesirable property that, in the face of
concurrent updates, the scan can fail to see either the old or the new
versions of the row. In many cases, we work around this by requiring
DDL operations to hold AccessExclusiveLock on the object being
modified; in some cases, the existing locking is inadequate and random
failures occur as a result. This commit doesn't change anything
related to locking, but will hopefully pave the way to allowing lock
strength reductions in the future.
The major issue has held us back from making this change in the past
is that taking an MVCC snapshot is significantly more expensive than
using a static special snapshot such as SnapshotNow. However, testing
of various worst-case scenarios reveals that this problem is not
severe except under fairly extreme workloads. To mitigate those
problems, we avoid retaking the MVCC snapshot for each new scan;
instead, we take a new snapshot only when invalidation messages have
been processed. The catcache machinery already requires that
invalidation messages be sent before releasing the related heavyweight
lock; else other backends might rely on locally-cached data rather
than scanning the catalog at all. Thus, making snapshot reuse
dependent on the same guarantees shouldn't break anything that wasn't
already subtly broken.
Patch by me. Review by Michael Paquier and Andres Freund.
2013-07-02 15:47:01 +02:00
|
|
|
NULL, 1, skey);
|
2009-10-05 21:24:49 +02:00
|
|
|
|
|
|
|
tuple = systable_getnext(scan);
|
|
|
|
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
elog(ERROR, "could not find tuple for default ACL %u", objid);
|
|
|
|
|
|
|
|
pg_default_acl_tuple = (Form_pg_default_acl) GETSTRUCT(tuple);
|
|
|
|
|
|
|
|
iacls.roleid = pg_default_acl_tuple->defaclrole;
|
|
|
|
iacls.nspid = pg_default_acl_tuple->defaclnamespace;
|
|
|
|
|
|
|
|
switch (pg_default_acl_tuple->defaclobjtype)
|
|
|
|
{
|
|
|
|
case DEFACLOBJ_RELATION:
|
|
|
|
iacls.objtype = ACL_OBJECT_RELATION;
|
|
|
|
break;
|
2011-04-21 04:23:58 +02:00
|
|
|
case DEFACLOBJ_SEQUENCE:
|
2009-10-05 21:24:49 +02:00
|
|
|
iacls.objtype = ACL_OBJECT_SEQUENCE;
|
|
|
|
break;
|
|
|
|
case DEFACLOBJ_FUNCTION:
|
|
|
|
iacls.objtype = ACL_OBJECT_FUNCTION;
|
|
|
|
break;
|
2012-12-09 06:08:23 +01:00
|
|
|
case DEFACLOBJ_TYPE:
|
|
|
|
iacls.objtype = ACL_OBJECT_TYPE;
|
|
|
|
break;
|
2009-10-05 21:24:49 +02:00
|
|
|
default:
|
|
|
|
/* Shouldn't get here */
|
2012-12-09 06:08:23 +01:00
|
|
|
elog(ERROR, "unexpected default ACL type: %d",
|
|
|
|
(int) pg_default_acl_tuple->defaclobjtype);
|
2009-10-05 21:24:49 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
systable_endscan(scan);
|
|
|
|
heap_close(rel, AccessShareLock);
|
|
|
|
|
|
|
|
iacls.is_grant = false;
|
|
|
|
iacls.all_privs = true;
|
|
|
|
iacls.privileges = ACL_NO_RIGHTS;
|
|
|
|
iacls.grantees = list_make1_oid(roleid);
|
|
|
|
iacls.grant_option = false;
|
|
|
|
iacls.behavior = DROP_CASCADE;
|
|
|
|
|
|
|
|
/* Do it */
|
|
|
|
SetDefaultACL(&iacls);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
InternalGrant istmt;
|
|
|
|
|
|
|
|
switch (classid)
|
|
|
|
{
|
|
|
|
case RelationRelationId:
|
|
|
|
/* it's OK to use RELATION for a sequence */
|
|
|
|
istmt.objtype = ACL_OBJECT_RELATION;
|
|
|
|
break;
|
|
|
|
case DatabaseRelationId:
|
|
|
|
istmt.objtype = ACL_OBJECT_DATABASE;
|
|
|
|
break;
|
2011-12-19 23:05:19 +01:00
|
|
|
case TypeRelationId:
|
|
|
|
istmt.objtype = ACL_OBJECT_TYPE;
|
|
|
|
break;
|
2009-10-05 21:24:49 +02:00
|
|
|
case ProcedureRelationId:
|
|
|
|
istmt.objtype = ACL_OBJECT_FUNCTION;
|
|
|
|
break;
|
|
|
|
case LanguageRelationId:
|
|
|
|
istmt.objtype = ACL_OBJECT_LANGUAGE;
|
|
|
|
break;
|
2009-12-11 04:34:57 +01:00
|
|
|
case LargeObjectRelationId:
|
|
|
|
istmt.objtype = ACL_OBJECT_LARGEOBJECT;
|
|
|
|
break;
|
2009-10-05 21:24:49 +02:00
|
|
|
case NamespaceRelationId:
|
|
|
|
istmt.objtype = ACL_OBJECT_NAMESPACE;
|
|
|
|
break;
|
|
|
|
case TableSpaceRelationId:
|
|
|
|
istmt.objtype = ACL_OBJECT_TABLESPACE;
|
|
|
|
break;
|
2010-11-12 14:19:14 +01:00
|
|
|
case ForeignServerRelationId:
|
|
|
|
istmt.objtype = ACL_OBJECT_FOREIGN_SERVER;
|
|
|
|
break;
|
|
|
|
case ForeignDataWrapperRelationId:
|
|
|
|
istmt.objtype = ACL_OBJECT_FDW;
|
|
|
|
break;
|
2009-10-05 21:24:49 +02:00
|
|
|
default:
|
|
|
|
elog(ERROR, "unexpected object class %u", classid);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
istmt.is_grant = false;
|
|
|
|
istmt.objects = list_make1_oid(objid);
|
|
|
|
istmt.all_privs = true;
|
|
|
|
istmt.privileges = ACL_NO_RIGHTS;
|
|
|
|
istmt.col_privs = NIL;
|
|
|
|
istmt.grantees = list_make1_oid(roleid);
|
|
|
|
istmt.grant_option = false;
|
|
|
|
istmt.behavior = DROP_CASCADE;
|
|
|
|
|
|
|
|
ExecGrantStmt_oids(&istmt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove a pg_default_acl entry
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
RemoveDefaultACLById(Oid defaclOid)
|
|
|
|
{
|
|
|
|
Relation rel;
|
|
|
|
ScanKeyData skey[1];
|
|
|
|
SysScanDesc scan;
|
|
|
|
HeapTuple tuple;
|
|
|
|
|
|
|
|
rel = heap_open(DefaultAclRelationId, RowExclusiveLock);
|
|
|
|
|
|
|
|
ScanKeyInit(&skey[0],
|
|
|
|
ObjectIdAttributeNumber,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(defaclOid));
|
|
|
|
|
|
|
|
scan = systable_beginscan(rel, DefaultAclOidIndexId, true,
|
Use an MVCC snapshot, rather than SnapshotNow, for catalog scans.
SnapshotNow scans have the undesirable property that, in the face of
concurrent updates, the scan can fail to see either the old or the new
versions of the row. In many cases, we work around this by requiring
DDL operations to hold AccessExclusiveLock on the object being
modified; in some cases, the existing locking is inadequate and random
failures occur as a result. This commit doesn't change anything
related to locking, but will hopefully pave the way to allowing lock
strength reductions in the future.
The major issue has held us back from making this change in the past
is that taking an MVCC snapshot is significantly more expensive than
using a static special snapshot such as SnapshotNow. However, testing
of various worst-case scenarios reveals that this problem is not
severe except under fairly extreme workloads. To mitigate those
problems, we avoid retaking the MVCC snapshot for each new scan;
instead, we take a new snapshot only when invalidation messages have
been processed. The catcache machinery already requires that
invalidation messages be sent before releasing the related heavyweight
lock; else other backends might rely on locally-cached data rather
than scanning the catalog at all. Thus, making snapshot reuse
dependent on the same guarantees shouldn't break anything that wasn't
already subtly broken.
Patch by me. Review by Michael Paquier and Andres Freund.
2013-07-02 15:47:01 +02:00
|
|
|
NULL, 1, skey);
|
2009-10-05 21:24:49 +02:00
|
|
|
|
|
|
|
tuple = systable_getnext(scan);
|
|
|
|
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
elog(ERROR, "could not find tuple for default ACL %u", defaclOid);
|
|
|
|
|
|
|
|
simple_heap_delete(rel, &tuple->t_self);
|
|
|
|
|
|
|
|
systable_endscan(scan);
|
|
|
|
heap_close(rel, RowExclusiveLock);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-22 21:16:10 +01:00
|
|
|
/*
|
|
|
|
* expand_col_privileges
|
|
|
|
*
|
|
|
|
* OR the specified privilege(s) into per-column array entries for each
|
|
|
|
* specified attribute. The per-column array is indexed starting at
|
|
|
|
* FirstLowInvalidHeapAttributeNumber, up to relation's last attribute.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
expand_col_privileges(List *colnames, Oid table_oid,
|
|
|
|
AclMode this_privileges,
|
|
|
|
AclMode *col_privileges,
|
|
|
|
int num_col_privileges)
|
|
|
|
{
|
|
|
|
ListCell *cell;
|
|
|
|
|
|
|
|
foreach(cell, colnames)
|
|
|
|
{
|
|
|
|
char *colname = strVal(lfirst(cell));
|
|
|
|
AttrNumber attnum;
|
|
|
|
|
|
|
|
attnum = get_attnum(table_oid, colname);
|
|
|
|
if (attnum == InvalidAttrNumber)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
|
|
|
errmsg("column \"%s\" of relation \"%s\" does not exist",
|
|
|
|
colname, get_rel_name(table_oid))));
|
|
|
|
attnum -= FirstLowInvalidHeapAttributeNumber;
|
|
|
|
if (attnum <= 0 || attnum >= num_col_privileges)
|
2009-06-11 16:49:15 +02:00
|
|
|
elog(ERROR, "column number out of range"); /* safety check */
|
2009-01-22 21:16:10 +01:00
|
|
|
col_privileges[attnum] |= this_privileges;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* expand_all_col_privileges
|
|
|
|
*
|
|
|
|
* OR the specified privilege(s) into per-column array entries for each valid
|
|
|
|
* attribute of a relation. The per-column array is indexed starting at
|
|
|
|
* FirstLowInvalidHeapAttributeNumber, up to relation's last attribute.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
expand_all_col_privileges(Oid table_oid, Form_pg_class classForm,
|
|
|
|
AclMode this_privileges,
|
|
|
|
AclMode *col_privileges,
|
|
|
|
int num_col_privileges)
|
|
|
|
{
|
|
|
|
AttrNumber curr_att;
|
|
|
|
|
|
|
|
Assert(classForm->relnatts - FirstLowInvalidHeapAttributeNumber < num_col_privileges);
|
|
|
|
for (curr_att = FirstLowInvalidHeapAttributeNumber + 1;
|
|
|
|
curr_att <= classForm->relnatts;
|
|
|
|
curr_att++)
|
|
|
|
{
|
|
|
|
HeapTuple attTuple;
|
|
|
|
bool isdropped;
|
|
|
|
|
|
|
|
if (curr_att == InvalidAttrNumber)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Skip OID column if it doesn't exist */
|
|
|
|
if (curr_att == ObjectIdAttributeNumber && !classForm->relhasoids)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Views don't have any system columns at all */
|
|
|
|
if (classForm->relkind == RELKIND_VIEW && curr_att < 0)
|
|
|
|
continue;
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
attTuple = SearchSysCache2(ATTNUM,
|
|
|
|
ObjectIdGetDatum(table_oid),
|
|
|
|
Int16GetDatum(curr_att));
|
2009-01-22 21:16:10 +01:00
|
|
|
if (!HeapTupleIsValid(attTuple))
|
|
|
|
elog(ERROR, "cache lookup failed for attribute %d of relation %u",
|
|
|
|
curr_att, table_oid);
|
|
|
|
|
|
|
|
isdropped = ((Form_pg_attribute) GETSTRUCT(attTuple))->attisdropped;
|
|
|
|
|
|
|
|
ReleaseSysCache(attTuple);
|
|
|
|
|
|
|
|
/* ignore dropped columns */
|
|
|
|
if (isdropped)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
col_privileges[curr_att - FirstLowInvalidHeapAttributeNumber] |= this_privileges;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This processes attributes, but expects to be called from
|
|
|
|
* ExecGrant_Relation, not directly from ExecGrantStmt.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
ExecGrant_Attribute(InternalGrant *istmt, Oid relOid, const char *relname,
|
|
|
|
AttrNumber attnum, Oid ownerId, AclMode col_privileges,
|
|
|
|
Relation attRelation, const Acl *old_rel_acl)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
HeapTuple attr_tuple;
|
|
|
|
Form_pg_attribute pg_attribute_tuple;
|
|
|
|
Acl *old_acl;
|
|
|
|
Acl *new_acl;
|
|
|
|
Acl *merged_acl;
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
|
|
|
Oid grantorId;
|
|
|
|
AclMode avail_goptions;
|
|
|
|
bool need_update;
|
|
|
|
HeapTuple newtuple;
|
|
|
|
Datum values[Natts_pg_attribute];
|
|
|
|
bool nulls[Natts_pg_attribute];
|
|
|
|
bool replaces[Natts_pg_attribute];
|
|
|
|
int noldmembers;
|
|
|
|
int nnewmembers;
|
|
|
|
Oid *oldmembers;
|
|
|
|
Oid *newmembers;
|
2009-01-22 21:16:10 +01:00
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
attr_tuple = SearchSysCache2(ATTNUM,
|
|
|
|
ObjectIdGetDatum(relOid),
|
|
|
|
Int16GetDatum(attnum));
|
2009-01-22 21:16:10 +01:00
|
|
|
if (!HeapTupleIsValid(attr_tuple))
|
|
|
|
elog(ERROR, "cache lookup failed for attribute %d of relation %u",
|
|
|
|
attnum, relOid);
|
|
|
|
pg_attribute_tuple = (Form_pg_attribute) GETSTRUCT(attr_tuple);
|
|
|
|
|
|
|
|
/*
|
2009-06-11 16:49:15 +02:00
|
|
|
* Get working copy of existing ACL. If there's no ACL, substitute the
|
|
|
|
* proper default.
|
2009-01-22 21:16:10 +01:00
|
|
|
*/
|
|
|
|
aclDatum = SysCacheGetAttr(ATTNUM, attr_tuple, Anum_pg_attribute_attacl,
|
|
|
|
&isNull);
|
|
|
|
if (isNull)
|
2010-04-05 03:09:53 +02:00
|
|
|
{
|
2009-01-22 21:16:10 +01:00
|
|
|
old_acl = acldefault(ACL_OBJECT_COLUMN, ownerId);
|
2010-04-05 03:09:53 +02:00
|
|
|
/* There are no old member roles according to the catalogs */
|
|
|
|
noldmembers = 0;
|
|
|
|
oldmembers = NULL;
|
|
|
|
}
|
2009-01-22 21:16:10 +01:00
|
|
|
else
|
2010-04-05 03:09:53 +02:00
|
|
|
{
|
2009-01-22 21:16:10 +01:00
|
|
|
old_acl = DatumGetAclPCopy(aclDatum);
|
2010-04-05 03:09:53 +02:00
|
|
|
/* Get the roles mentioned in the existing ACL */
|
|
|
|
noldmembers = aclmembers(old_acl, &oldmembers);
|
|
|
|
}
|
2009-01-22 21:16:10 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* In select_best_grantor we should consider existing table-level ACL bits
|
|
|
|
* as well as the per-column ACL. Build a new ACL that is their
|
2009-06-11 16:49:15 +02:00
|
|
|
* concatenation. (This is a bit cheap and dirty compared to merging them
|
|
|
|
* properly with no duplications, but it's all we need here.)
|
2009-01-22 21:16:10 +01:00
|
|
|
*/
|
|
|
|
merged_acl = aclconcat(old_rel_acl, old_acl);
|
|
|
|
|
|
|
|
/* Determine ID to do the grant as, and available grant options */
|
|
|
|
select_best_grantor(GetUserId(), col_privileges,
|
|
|
|
merged_acl, ownerId,
|
|
|
|
&grantorId, &avail_goptions);
|
|
|
|
|
|
|
|
pfree(merged_acl);
|
|
|
|
|
|
|
|
/*
|
2009-06-11 16:49:15 +02:00
|
|
|
* Restrict the privileges to what we can actually grant, and emit the
|
|
|
|
* standards-mandated warning and error messages. Note: we don't track
|
|
|
|
* whether the user actually used the ALL PRIVILEGES(columns) syntax for
|
|
|
|
* each column; we just approximate it by whether all the possible
|
|
|
|
* privileges are specified now. Since the all_privs flag only determines
|
|
|
|
* whether a warning is issued, this seems close enough.
|
2009-01-22 21:16:10 +01:00
|
|
|
*/
|
|
|
|
col_privileges =
|
|
|
|
restrict_and_check_grant(istmt->is_grant, avail_goptions,
|
|
|
|
(col_privileges == ACL_ALL_RIGHTS_COLUMN),
|
|
|
|
col_privileges,
|
|
|
|
relOid, grantorId, ACL_KIND_COLUMN,
|
|
|
|
relname, attnum,
|
|
|
|
NameStr(pg_attribute_tuple->attname));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generate new ACL.
|
|
|
|
*/
|
|
|
|
new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
|
|
|
|
istmt->grant_option,
|
|
|
|
istmt->behavior, istmt->grantees,
|
|
|
|
col_privileges, grantorId,
|
|
|
|
ownerId);
|
|
|
|
|
2010-04-05 03:09:53 +02:00
|
|
|
/*
|
|
|
|
* We need the members of both old and new ACLs so we can correct the
|
|
|
|
* shared dependency information.
|
|
|
|
*/
|
2009-01-22 21:16:10 +01:00
|
|
|
nnewmembers = aclmembers(new_acl, &newmembers);
|
|
|
|
|
|
|
|
/* finished building new ACL value, now insert it */
|
|
|
|
MemSet(values, 0, sizeof(values));
|
|
|
|
MemSet(nulls, false, sizeof(nulls));
|
|
|
|
MemSet(replaces, false, sizeof(replaces));
|
|
|
|
|
|
|
|
/*
|
2009-06-11 16:49:15 +02:00
|
|
|
* If the updated ACL is empty, we can set attacl to null, and maybe even
|
|
|
|
* avoid an update of the pg_attribute row. This is worth testing because
|
|
|
|
* we'll come through here multiple times for any relation-level REVOKE,
|
2014-05-06 18:12:18 +02:00
|
|
|
* even if there were never any column GRANTs. Note we are assuming that
|
2009-06-11 16:49:15 +02:00
|
|
|
* the "default" ACL state for columns is empty.
|
2009-01-22 21:16:10 +01:00
|
|
|
*/
|
|
|
|
if (ACL_NUM(new_acl) > 0)
|
|
|
|
{
|
|
|
|
values[Anum_pg_attribute_attacl - 1] = PointerGetDatum(new_acl);
|
|
|
|
need_update = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
nulls[Anum_pg_attribute_attacl - 1] = true;
|
|
|
|
need_update = !isNull;
|
|
|
|
}
|
|
|
|
replaces[Anum_pg_attribute_attacl - 1] = true;
|
|
|
|
|
|
|
|
if (need_update)
|
|
|
|
{
|
|
|
|
newtuple = heap_modify_tuple(attr_tuple, RelationGetDescr(attRelation),
|
|
|
|
values, nulls, replaces);
|
|
|
|
|
|
|
|
simple_heap_update(attRelation, &newtuple->t_self, newtuple);
|
|
|
|
|
|
|
|
/* keep the catalog indexes up to date */
|
|
|
|
CatalogUpdateIndexes(attRelation, newtuple);
|
|
|
|
|
2016-04-07 03:45:32 +02:00
|
|
|
/* Update initial privileges for extensions */
|
|
|
|
recordExtensionInitPriv(relOid, RelationRelationId, attnum,
|
|
|
|
ACL_NUM(new_acl) > 0 ? new_acl : NULL);
|
|
|
|
|
2009-01-22 21:16:10 +01:00
|
|
|
/* Update the shared dependency ACL info */
|
|
|
|
updateAclDependencies(RelationRelationId, relOid, attnum,
|
2010-04-05 03:09:53 +02:00
|
|
|
ownerId,
|
2009-01-22 21:16:10 +01:00
|
|
|
noldmembers, oldmembers,
|
|
|
|
nnewmembers, newmembers);
|
|
|
|
}
|
|
|
|
|
|
|
|
pfree(new_acl);
|
|
|
|
|
|
|
|
ReleaseSysCache(attr_tuple);
|
|
|
|
}
|
|
|
|
|
2006-01-21 03:16:21 +01:00
|
|
|
/*
|
|
|
|
* This processes both sequences and non-sequences.
|
|
|
|
*/
|
2005-11-21 13:49:33 +01:00
|
|
|
static void
|
2005-12-01 03:03:01 +01:00
|
|
|
ExecGrant_Relation(InternalGrant *istmt)
|
2005-11-21 13:49:33 +01:00
|
|
|
{
|
|
|
|
Relation relation;
|
2009-01-22 21:16:10 +01:00
|
|
|
Relation attRelation;
|
2005-11-21 13:49:33 +01:00
|
|
|
ListCell *cell;
|
|
|
|
|
|
|
|
relation = heap_open(RelationRelationId, RowExclusiveLock);
|
2009-01-22 21:16:10 +01:00
|
|
|
attRelation = heap_open(AttributeRelationId, RowExclusiveLock);
|
2005-11-21 13:49:33 +01:00
|
|
|
|
2005-12-01 03:03:01 +01:00
|
|
|
foreach(cell, istmt->objects)
|
2005-11-21 13:49:33 +01:00
|
|
|
{
|
|
|
|
Oid relOid = lfirst_oid(cell);
|
2001-06-10 01:21:55 +02:00
|
|
|
Datum aclDatum;
|
2005-11-21 13:49:33 +01:00
|
|
|
Form_pg_class pg_class_tuple;
|
2001-06-10 01:21:55 +02:00
|
|
|
bool isNull;
|
2004-06-01 23:49:23 +02:00
|
|
|
AclMode this_privileges;
|
2009-06-11 16:49:15 +02:00
|
|
|
AclMode *col_privileges;
|
2009-01-22 21:16:10 +01:00
|
|
|
int num_col_privileges;
|
|
|
|
bool have_col_privileges;
|
2001-06-10 01:21:55 +02:00
|
|
|
Acl *old_acl;
|
2009-01-22 21:16:10 +01:00
|
|
|
Acl *old_rel_acl;
|
2010-04-05 03:09:53 +02:00
|
|
|
int noldmembers;
|
|
|
|
Oid *oldmembers;
|
2005-10-15 04:49:52 +02:00
|
|
|
Oid ownerId;
|
2005-11-21 13:49:33 +01:00
|
|
|
HeapTuple tuple;
|
2009-01-22 21:16:10 +01:00
|
|
|
ListCell *cell_colprivs;
|
2001-06-10 01:21:55 +02:00
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
|
2001-06-10 01:21:55 +02:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2003-07-21 03:59:11 +02:00
|
|
|
elog(ERROR, "cache lookup failed for relation %u", relOid);
|
2001-06-10 01:21:55 +02:00
|
|
|
pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
|
|
|
|
|
2003-07-21 03:59:11 +02:00
|
|
|
/* Not sensible to grant on an index */
|
2001-06-10 01:21:55 +02:00
|
|
|
if (pg_class_tuple->relkind == RELKIND_INDEX)
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
|
|
|
errmsg("\"%s\" is an index",
|
2005-11-21 13:49:33 +01:00
|
|
|
NameStr(pg_class_tuple->relname))));
|
2001-06-10 01:21:55 +02:00
|
|
|
|
2004-06-01 23:49:23 +02:00
|
|
|
/* Composite types aren't tables either */
|
|
|
|
if (pg_class_tuple->relkind == RELKIND_COMPOSITE_TYPE)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
|
|
|
errmsg("\"%s\" is a composite type",
|
2005-11-21 13:49:33 +01:00
|
|
|
NameStr(pg_class_tuple->relname))));
|
2005-11-22 19:17:34 +01:00
|
|
|
|
2006-01-21 03:16:21 +01:00
|
|
|
/* Used GRANT SEQUENCE on a non-sequence? */
|
|
|
|
if (istmt->objtype == ACL_OBJECT_SEQUENCE &&
|
|
|
|
pg_class_tuple->relkind != RELKIND_SEQUENCE)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
|
|
|
errmsg("\"%s\" is not a sequence",
|
|
|
|
NameStr(pg_class_tuple->relname))));
|
|
|
|
|
2011-01-02 05:48:11 +01:00
|
|
|
/* Adjust the default permissions based on object type */
|
2006-01-21 03:16:21 +01:00
|
|
|
if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
|
|
|
|
{
|
|
|
|
if (pg_class_tuple->relkind == RELKIND_SEQUENCE)
|
|
|
|
this_privileges = ACL_ALL_RIGHTS_SEQUENCE;
|
|
|
|
else
|
|
|
|
this_privileges = ACL_ALL_RIGHTS_RELATION;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
this_privileges = istmt->privileges;
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2006-01-21 03:16:21 +01:00
|
|
|
/*
|
2006-10-04 02:30:14 +02:00
|
|
|
* The GRANT TABLE syntax can be used for sequences and non-sequences,
|
|
|
|
* so we have to look at the relkind to determine the supported
|
|
|
|
* permissions. The OR of table and sequence permissions were already
|
|
|
|
* checked.
|
2006-01-21 03:16:21 +01:00
|
|
|
*/
|
|
|
|
if (istmt->objtype == ACL_OBJECT_RELATION)
|
|
|
|
{
|
|
|
|
if (pg_class_tuple->relkind == RELKIND_SEQUENCE)
|
|
|
|
{
|
|
|
|
/*
|
2009-01-22 21:16:10 +01:00
|
|
|
* For backward compatibility, just throw a warning for
|
2006-10-04 02:30:14 +02:00
|
|
|
* invalid sequence permissions when using the non-sequence
|
2009-01-22 21:16:10 +01:00
|
|
|
* GRANT syntax.
|
2006-01-21 03:16:21 +01:00
|
|
|
*/
|
|
|
|
if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_SEQUENCE))
|
|
|
|
{
|
|
|
|
/*
|
2006-10-04 02:30:14 +02:00
|
|
|
* Mention the object name because the user needs to know
|
2014-05-06 18:12:18 +02:00
|
|
|
* which operations succeeded. This is required because
|
2006-10-04 02:30:14 +02:00
|
|
|
* WARNING allows the command to continue.
|
2006-01-21 03:16:21 +01:00
|
|
|
*/
|
|
|
|
ereport(WARNING,
|
|
|
|
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
|
2009-01-22 21:16:10 +01:00
|
|
|
errmsg("sequence \"%s\" only supports USAGE, SELECT, and UPDATE privileges",
|
2006-01-21 03:16:21 +01:00
|
|
|
NameStr(pg_class_tuple->relname))));
|
|
|
|
this_privileges &= (AclMode) ACL_ALL_RIGHTS_SEQUENCE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_RELATION))
|
2009-01-22 21:16:10 +01:00
|
|
|
{
|
2006-01-21 03:16:21 +01:00
|
|
|
/*
|
2006-10-04 02:30:14 +02:00
|
|
|
* USAGE is the only permission supported by sequences but
|
|
|
|
* not by non-sequences. Don't mention the object name
|
|
|
|
* because we didn't in the combined TABLE | SEQUENCE
|
|
|
|
* check.
|
2006-01-21 03:16:21 +01:00
|
|
|
*/
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
|
2006-10-04 02:30:14 +02:00
|
|
|
errmsg("invalid privilege type USAGE for table")));
|
2009-01-22 21:16:10 +01:00
|
|
|
}
|
2006-01-21 03:16:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-22 21:16:10 +01:00
|
|
|
/*
|
|
|
|
* Set up array in which we'll accumulate any column privilege bits
|
2014-05-06 18:12:18 +02:00
|
|
|
* that need modification. The array is indexed such that entry [0]
|
2009-01-22 21:16:10 +01:00
|
|
|
* corresponds to FirstLowInvalidHeapAttributeNumber.
|
|
|
|
*/
|
|
|
|
num_col_privileges = pg_class_tuple->relnatts - FirstLowInvalidHeapAttributeNumber + 1;
|
|
|
|
col_privileges = (AclMode *) palloc0(num_col_privileges * sizeof(AclMode));
|
|
|
|
have_col_privileges = false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we are revoking relation privileges that are also column
|
|
|
|
* privileges, we must implicitly revoke them from each column too,
|
|
|
|
* per SQL spec. (We don't need to implicitly add column privileges
|
|
|
|
* during GRANT because the permissions-checking code always checks
|
|
|
|
* both relation and per-column privileges.)
|
|
|
|
*/
|
|
|
|
if (!istmt->is_grant &&
|
|
|
|
(this_privileges & ACL_ALL_RIGHTS_COLUMN) != 0)
|
|
|
|
{
|
|
|
|
expand_all_col_privileges(relOid, pg_class_tuple,
|
|
|
|
this_privileges & ACL_ALL_RIGHTS_COLUMN,
|
|
|
|
col_privileges,
|
|
|
|
num_col_privileges);
|
|
|
|
have_col_privileges = true;
|
|
|
|
}
|
|
|
|
|
2005-10-10 20:49:04 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Get owner ID and working copy of existing ACL. If there's no ACL,
|
|
|
|
* substitute the proper default.
|
2005-10-10 20:49:04 +02:00
|
|
|
*/
|
2004-06-01 23:49:23 +02:00
|
|
|
ownerId = pg_class_tuple->relowner;
|
2005-10-10 20:49:04 +02:00
|
|
|
aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl,
|
|
|
|
&isNull);
|
|
|
|
if (isNull)
|
2010-04-05 03:09:53 +02:00
|
|
|
{
|
2011-01-02 05:48:11 +01:00
|
|
|
switch (pg_class_tuple->relkind)
|
|
|
|
{
|
|
|
|
case RELKIND_SEQUENCE:
|
|
|
|
old_acl = acldefault(ACL_OBJECT_SEQUENCE, ownerId);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
old_acl = acldefault(ACL_OBJECT_RELATION, ownerId);
|
|
|
|
break;
|
|
|
|
}
|
2010-04-05 03:09:53 +02:00
|
|
|
/* There are no old member roles according to the catalogs */
|
|
|
|
noldmembers = 0;
|
|
|
|
oldmembers = NULL;
|
|
|
|
}
|
2005-10-10 20:49:04 +02:00
|
|
|
else
|
2010-04-05 03:09:53 +02:00
|
|
|
{
|
2005-10-10 20:49:04 +02:00
|
|
|
old_acl = DatumGetAclPCopy(aclDatum);
|
2010-04-05 03:09:53 +02:00
|
|
|
/* Get the roles mentioned in the existing ACL */
|
|
|
|
noldmembers = aclmembers(old_acl, &oldmembers);
|
|
|
|
}
|
2005-10-10 20:49:04 +02:00
|
|
|
|
2009-01-22 21:16:10 +01:00
|
|
|
/* Need an extra copy of original rel ACL for column handling */
|
|
|
|
old_rel_acl = aclcopy(old_acl);
|
2004-06-01 23:49:23 +02:00
|
|
|
|
|
|
|
/*
|
2009-01-22 21:16:10 +01:00
|
|
|
* Handle relation-level privileges, if any were specified
|
2004-06-01 23:49:23 +02:00
|
|
|
*/
|
2009-01-22 21:16:10 +01:00
|
|
|
if (this_privileges != ACL_NO_RIGHTS)
|
|
|
|
{
|
|
|
|
AclMode avail_goptions;
|
|
|
|
Acl *new_acl;
|
|
|
|
Oid grantorId;
|
|
|
|
HeapTuple newtuple;
|
|
|
|
Datum values[Natts_pg_class];
|
|
|
|
bool nulls[Natts_pg_class];
|
|
|
|
bool replaces[Natts_pg_class];
|
|
|
|
int nnewmembers;
|
|
|
|
Oid *newmembers;
|
2011-01-02 05:48:11 +01:00
|
|
|
AclObjectKind aclkind;
|
2009-01-22 21:16:10 +01:00
|
|
|
|
|
|
|
/* Determine ID to do the grant as, and available grant options */
|
|
|
|
select_best_grantor(GetUserId(), this_privileges,
|
|
|
|
old_acl, ownerId,
|
|
|
|
&grantorId, &avail_goptions);
|
|
|
|
|
2011-01-02 05:48:11 +01:00
|
|
|
switch (pg_class_tuple->relkind)
|
|
|
|
{
|
|
|
|
case RELKIND_SEQUENCE:
|
|
|
|
aclkind = ACL_KIND_SEQUENCE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
aclkind = ACL_KIND_CLASS;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-01-22 21:16:10 +01:00
|
|
|
/*
|
|
|
|
* Restrict the privileges to what we can actually grant, and emit
|
|
|
|
* the standards-mandated warning and error messages.
|
|
|
|
*/
|
|
|
|
this_privileges =
|
|
|
|
restrict_and_check_grant(istmt->is_grant, avail_goptions,
|
|
|
|
istmt->all_privs, this_privileges,
|
2011-01-02 05:48:11 +01:00
|
|
|
relOid, grantorId, aclkind,
|
2009-01-22 21:16:10 +01:00
|
|
|
NameStr(pg_class_tuple->relname),
|
|
|
|
0, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generate new ACL.
|
|
|
|
*/
|
|
|
|
new_acl = merge_acl_with_grant(old_acl,
|
|
|
|
istmt->is_grant,
|
|
|
|
istmt->grant_option,
|
|
|
|
istmt->behavior,
|
|
|
|
istmt->grantees,
|
|
|
|
this_privileges,
|
|
|
|
grantorId,
|
|
|
|
ownerId);
|
|
|
|
|
2010-04-05 03:09:53 +02:00
|
|
|
/*
|
|
|
|
* We need the members of both old and new ACLs so we can correct
|
|
|
|
* the shared dependency information.
|
|
|
|
*/
|
2009-01-22 21:16:10 +01:00
|
|
|
nnewmembers = aclmembers(new_acl, &newmembers);
|
|
|
|
|
|
|
|
/* finished building new ACL value, now insert it */
|
|
|
|
MemSet(values, 0, sizeof(values));
|
|
|
|
MemSet(nulls, false, sizeof(nulls));
|
|
|
|
MemSet(replaces, false, sizeof(replaces));
|
|
|
|
|
|
|
|
replaces[Anum_pg_class_relacl - 1] = true;
|
|
|
|
values[Anum_pg_class_relacl - 1] = PointerGetDatum(new_acl);
|
|
|
|
|
|
|
|
newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation),
|
|
|
|
values, nulls, replaces);
|
|
|
|
|
|
|
|
simple_heap_update(relation, &newtuple->t_self, newtuple);
|
|
|
|
|
|
|
|
/* keep the catalog indexes up to date */
|
|
|
|
CatalogUpdateIndexes(relation, newtuple);
|
|
|
|
|
2016-04-07 03:45:32 +02:00
|
|
|
/* Update initial privileges for extensions */
|
|
|
|
recordExtensionInitPriv(relOid, RelationRelationId, 0, new_acl);
|
|
|
|
|
2009-01-22 21:16:10 +01:00
|
|
|
/* Update the shared dependency ACL info */
|
|
|
|
updateAclDependencies(RelationRelationId, relOid, 0,
|
2010-04-05 03:09:53 +02:00
|
|
|
ownerId,
|
2009-01-22 21:16:10 +01:00
|
|
|
noldmembers, oldmembers,
|
|
|
|
nnewmembers, newmembers);
|
|
|
|
|
|
|
|
pfree(new_acl);
|
|
|
|
}
|
2004-06-01 23:49:23 +02:00
|
|
|
|
2001-06-10 01:21:55 +02:00
|
|
|
/*
|
2009-01-22 21:16:10 +01:00
|
|
|
* Handle column-level privileges, if any were specified or implied.
|
|
|
|
* We first expand the user-specified column privileges into the
|
|
|
|
* array, and then iterate over all nonempty array entries.
|
2005-07-07 22:40:02 +02:00
|
|
|
*/
|
2009-01-22 21:16:10 +01:00
|
|
|
foreach(cell_colprivs, istmt->col_privs)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
AccessPriv *col_privs = (AccessPriv *) lfirst(cell_colprivs);
|
2005-07-07 22:40:02 +02:00
|
|
|
|
2009-01-22 21:16:10 +01:00
|
|
|
if (col_privs->priv_name == NULL)
|
|
|
|
this_privileges = ACL_ALL_RIGHTS_COLUMN;
|
|
|
|
else
|
|
|
|
this_privileges = string_to_privilege(col_privs->priv_name);
|
2001-06-10 01:21:55 +02:00
|
|
|
|
2009-01-22 21:16:10 +01:00
|
|
|
if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_COLUMN))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
|
|
|
|
errmsg("invalid privilege type %s for column",
|
|
|
|
privilege_to_string(this_privileges))));
|
2005-07-07 22:40:02 +02:00
|
|
|
|
2009-01-22 21:16:10 +01:00
|
|
|
if (pg_class_tuple->relkind == RELKIND_SEQUENCE &&
|
|
|
|
this_privileges & ~((AclMode) ACL_SELECT))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* The only column privilege allowed on sequences is SELECT.
|
2009-06-11 16:49:15 +02:00
|
|
|
* This is a warning not error because we do it that way for
|
|
|
|
* relation-level privileges.
|
2009-01-22 21:16:10 +01:00
|
|
|
*/
|
|
|
|
ereport(WARNING,
|
|
|
|
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
|
|
|
|
errmsg("sequence \"%s\" only supports SELECT column privileges",
|
|
|
|
NameStr(pg_class_tuple->relname))));
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2009-01-22 21:16:10 +01:00
|
|
|
this_privileges &= (AclMode) ACL_SELECT;
|
|
|
|
}
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2009-01-22 21:16:10 +01:00
|
|
|
expand_col_privileges(col_privs->cols, relOid,
|
|
|
|
this_privileges,
|
|
|
|
col_privileges,
|
|
|
|
num_col_privileges);
|
|
|
|
have_col_privileges = true;
|
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2009-01-22 21:16:10 +01:00
|
|
|
if (have_col_privileges)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
AttrNumber i;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2009-01-22 21:16:10 +01:00
|
|
|
for (i = 0; i < num_col_privileges; i++)
|
|
|
|
{
|
|
|
|
if (col_privileges[i] == ACL_NO_RIGHTS)
|
|
|
|
continue;
|
|
|
|
ExecGrant_Attribute(istmt,
|
|
|
|
relOid,
|
|
|
|
NameStr(pg_class_tuple->relname),
|
|
|
|
i + FirstLowInvalidHeapAttributeNumber,
|
|
|
|
ownerId,
|
|
|
|
col_privileges[i],
|
|
|
|
attRelation,
|
|
|
|
old_rel_acl);
|
|
|
|
}
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2009-01-22 21:16:10 +01:00
|
|
|
pfree(old_rel_acl);
|
|
|
|
pfree(col_privileges);
|
2005-07-07 22:40:02 +02:00
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
2005-08-12 23:20:24 +02:00
|
|
|
/* prevent error when processing duplicate objects */
|
|
|
|
CommandCounterIncrement();
|
2001-06-10 01:21:55 +02:00
|
|
|
}
|
2005-11-21 13:49:33 +01:00
|
|
|
|
2009-01-22 21:16:10 +01:00
|
|
|
heap_close(attRelation, RowExclusiveLock);
|
2005-11-21 13:49:33 +01:00
|
|
|
heap_close(relation, RowExclusiveLock);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2002-04-21 02:26:44 +02:00
|
|
|
static void
|
2005-12-01 03:03:01 +01:00
|
|
|
ExecGrant_Database(InternalGrant *istmt)
|
2002-04-21 02:26:44 +02:00
|
|
|
{
|
2005-11-21 13:49:33 +01:00
|
|
|
Relation relation;
|
|
|
|
ListCell *cell;
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2005-12-01 03:03:01 +01:00
|
|
|
if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
|
|
|
|
istmt->privileges = ACL_ALL_RIGHTS_DATABASE;
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2005-11-21 13:49:33 +01:00
|
|
|
relation = heap_open(DatabaseRelationId, RowExclusiveLock);
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2005-12-01 03:03:01 +01:00
|
|
|
foreach(cell, istmt->objects)
|
2002-04-21 02:26:44 +02:00
|
|
|
{
|
2005-11-21 13:49:33 +01:00
|
|
|
Oid datId = lfirst_oid(cell);
|
2002-04-21 02:26:44 +02:00
|
|
|
Form_pg_database pg_database_tuple;
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
2005-10-10 20:49:04 +02:00
|
|
|
AclMode avail_goptions;
|
2004-06-01 23:49:23 +02:00
|
|
|
AclMode this_privileges;
|
2002-04-21 02:26:44 +02:00
|
|
|
Acl *old_acl;
|
|
|
|
Acl *new_acl;
|
2005-10-15 04:49:52 +02:00
|
|
|
Oid grantorId;
|
|
|
|
Oid ownerId;
|
2002-04-21 02:26:44 +02:00
|
|
|
HeapTuple newtuple;
|
|
|
|
Datum values[Natts_pg_database];
|
2008-11-02 02:45:28 +01:00
|
|
|
bool nulls[Natts_pg_database];
|
|
|
|
bool replaces[Natts_pg_database];
|
2005-07-07 22:40:02 +02:00
|
|
|
int noldmembers;
|
|
|
|
int nnewmembers;
|
|
|
|
Oid *oldmembers;
|
|
|
|
Oid *newmembers;
|
2005-11-21 13:49:33 +01:00
|
|
|
HeapTuple tuple;
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(datId));
|
2002-04-21 02:26:44 +02:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2006-05-04 00:45:26 +02:00
|
|
|
elog(ERROR, "cache lookup failed for database %u", datId);
|
2005-11-21 13:49:33 +01:00
|
|
|
|
2002-04-21 02:26:44 +02:00
|
|
|
pg_database_tuple = (Form_pg_database) GETSTRUCT(tuple);
|
|
|
|
|
2005-10-10 20:49:04 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Get owner ID and working copy of existing ACL. If there's no ACL,
|
|
|
|
* substitute the proper default.
|
2005-10-10 20:49:04 +02:00
|
|
|
*/
|
2003-10-31 21:00:49 +01:00
|
|
|
ownerId = pg_database_tuple->datdba;
|
2005-10-10 20:49:04 +02:00
|
|
|
aclDatum = heap_getattr(tuple, Anum_pg_database_datacl,
|
|
|
|
RelationGetDescr(relation), &isNull);
|
|
|
|
if (isNull)
|
2010-04-05 03:09:53 +02:00
|
|
|
{
|
2005-10-10 20:49:04 +02:00
|
|
|
old_acl = acldefault(ACL_OBJECT_DATABASE, ownerId);
|
2010-04-05 03:09:53 +02:00
|
|
|
/* There are no old member roles according to the catalogs */
|
|
|
|
noldmembers = 0;
|
|
|
|
oldmembers = NULL;
|
|
|
|
}
|
2005-10-10 20:49:04 +02:00
|
|
|
else
|
2010-04-05 03:09:53 +02:00
|
|
|
{
|
2005-10-10 20:49:04 +02:00
|
|
|
old_acl = DatumGetAclPCopy(aclDatum);
|
2010-04-05 03:09:53 +02:00
|
|
|
/* Get the roles mentioned in the existing ACL */
|
|
|
|
noldmembers = aclmembers(old_acl, &oldmembers);
|
|
|
|
}
|
2005-10-10 20:49:04 +02:00
|
|
|
|
|
|
|
/* Determine ID to do the grant as, and available grant options */
|
2005-12-01 03:03:01 +01:00
|
|
|
select_best_grantor(GetUserId(), istmt->privileges,
|
2005-10-10 20:49:04 +02:00
|
|
|
old_acl, ownerId,
|
|
|
|
&grantorId, &avail_goptions);
|
2003-10-31 21:00:49 +01:00
|
|
|
|
2004-06-01 23:49:23 +02:00
|
|
|
/*
|
2006-10-04 02:30:14 +02:00
|
|
|
* Restrict the privileges to what we can actually grant, and emit the
|
|
|
|
* standards-mandated warning and error messages.
|
2004-06-01 23:49:23 +02:00
|
|
|
*/
|
2005-12-01 03:03:01 +01:00
|
|
|
this_privileges =
|
|
|
|
restrict_and_check_grant(istmt->is_grant, avail_goptions,
|
|
|
|
istmt->all_privs, istmt->privileges,
|
|
|
|
datId, grantorId, ACL_KIND_DATABASE,
|
2009-01-22 21:16:10 +01:00
|
|
|
NameStr(pg_database_tuple->datname),
|
|
|
|
0, NULL);
|
2002-04-21 02:26:44 +02:00
|
|
|
|
|
|
|
/*
|
2005-10-10 20:49:04 +02:00
|
|
|
* Generate new ACL.
|
2005-07-07 22:40:02 +02:00
|
|
|
*/
|
2005-12-01 03:03:01 +01:00
|
|
|
new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
|
|
|
|
istmt->grant_option, istmt->behavior,
|
|
|
|
istmt->grantees, this_privileges,
|
2003-10-31 21:00:49 +01:00
|
|
|
grantorId, ownerId);
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2010-04-05 03:09:53 +02:00
|
|
|
/*
|
|
|
|
* We need the members of both old and new ACLs so we can correct the
|
|
|
|
* shared dependency information.
|
|
|
|
*/
|
2005-07-07 22:40:02 +02:00
|
|
|
nnewmembers = aclmembers(new_acl, &newmembers);
|
|
|
|
|
2002-04-21 02:26:44 +02:00
|
|
|
/* finished building new ACL value, now insert it */
|
|
|
|
MemSet(values, 0, sizeof(values));
|
2008-11-02 02:45:28 +01:00
|
|
|
MemSet(nulls, false, sizeof(nulls));
|
|
|
|
MemSet(replaces, false, sizeof(replaces));
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2008-11-02 02:45:28 +01:00
|
|
|
replaces[Anum_pg_database_datacl - 1] = true;
|
2002-04-21 02:26:44 +02:00
|
|
|
values[Anum_pg_database_datacl - 1] = PointerGetDatum(new_acl);
|
|
|
|
|
2008-11-02 02:45:28 +01:00
|
|
|
newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
|
|
|
|
nulls, replaces);
|
2002-04-21 02:26:44 +02:00
|
|
|
|
|
|
|
simple_heap_update(relation, &newtuple->t_self, newtuple);
|
|
|
|
|
2002-08-05 05:29:17 +02:00
|
|
|
/* keep the catalog indexes up to date */
|
|
|
|
CatalogUpdateIndexes(relation, newtuple);
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2005-07-07 22:40:02 +02:00
|
|
|
/* Update the shared dependency ACL info */
|
2009-01-22 21:16:10 +01:00
|
|
|
updateAclDependencies(DatabaseRelationId, HeapTupleGetOid(tuple), 0,
|
2010-04-05 03:09:53 +02:00
|
|
|
ownerId,
|
2005-07-07 22:40:02 +02:00
|
|
|
noldmembers, oldmembers,
|
|
|
|
nnewmembers, newmembers);
|
|
|
|
|
2006-05-04 00:45:26 +02:00
|
|
|
ReleaseSysCache(tuple);
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2005-11-21 13:49:33 +01:00
|
|
|
pfree(new_acl);
|
2005-08-12 23:20:24 +02:00
|
|
|
|
|
|
|
/* prevent error when processing duplicate objects */
|
|
|
|
CommandCounterIncrement();
|
2002-04-21 02:26:44 +02:00
|
|
|
}
|
2005-11-21 13:49:33 +01:00
|
|
|
|
|
|
|
heap_close(relation, RowExclusiveLock);
|
2002-04-21 02:26:44 +02:00
|
|
|
}
|
2001-06-10 01:21:55 +02:00
|
|
|
|
2008-12-19 17:25:19 +01:00
|
|
|
static void
|
|
|
|
ExecGrant_Fdw(InternalGrant *istmt)
|
|
|
|
{
|
|
|
|
Relation relation;
|
|
|
|
ListCell *cell;
|
|
|
|
|
|
|
|
if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
|
|
|
|
istmt->privileges = ACL_ALL_RIGHTS_FDW;
|
|
|
|
|
|
|
|
relation = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
|
|
|
|
|
|
|
|
foreach(cell, istmt->objects)
|
|
|
|
{
|
|
|
|
Oid fdwid = lfirst_oid(cell);
|
|
|
|
Form_pg_foreign_data_wrapper pg_fdw_tuple;
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
|
|
|
AclMode avail_goptions;
|
|
|
|
AclMode this_privileges;
|
|
|
|
Acl *old_acl;
|
|
|
|
Acl *new_acl;
|
|
|
|
Oid grantorId;
|
|
|
|
Oid ownerId;
|
|
|
|
HeapTuple tuple;
|
|
|
|
HeapTuple newtuple;
|
|
|
|
Datum values[Natts_pg_foreign_data_wrapper];
|
|
|
|
bool nulls[Natts_pg_foreign_data_wrapper];
|
|
|
|
bool replaces[Natts_pg_foreign_data_wrapper];
|
|
|
|
int noldmembers;
|
|
|
|
int nnewmembers;
|
|
|
|
Oid *oldmembers;
|
|
|
|
Oid *newmembers;
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(FOREIGNDATAWRAPPEROID,
|
2010-02-26 03:01:40 +01:00
|
|
|
ObjectIdGetDatum(fdwid));
|
2008-12-19 17:25:19 +01:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwid);
|
|
|
|
|
|
|
|
pg_fdw_tuple = (Form_pg_foreign_data_wrapper) GETSTRUCT(tuple);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get owner ID and working copy of existing ACL. If there's no ACL,
|
|
|
|
* substitute the proper default.
|
|
|
|
*/
|
|
|
|
ownerId = pg_fdw_tuple->fdwowner;
|
|
|
|
aclDatum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID, tuple,
|
|
|
|
Anum_pg_foreign_data_wrapper_fdwacl,
|
|
|
|
&isNull);
|
|
|
|
if (isNull)
|
2010-04-05 03:09:53 +02:00
|
|
|
{
|
2008-12-19 17:25:19 +01:00
|
|
|
old_acl = acldefault(ACL_OBJECT_FDW, ownerId);
|
2010-04-05 03:09:53 +02:00
|
|
|
/* There are no old member roles according to the catalogs */
|
|
|
|
noldmembers = 0;
|
|
|
|
oldmembers = NULL;
|
|
|
|
}
|
2008-12-19 17:25:19 +01:00
|
|
|
else
|
2010-04-05 03:09:53 +02:00
|
|
|
{
|
2008-12-19 17:25:19 +01:00
|
|
|
old_acl = DatumGetAclPCopy(aclDatum);
|
2010-04-05 03:09:53 +02:00
|
|
|
/* Get the roles mentioned in the existing ACL */
|
|
|
|
noldmembers = aclmembers(old_acl, &oldmembers);
|
|
|
|
}
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
/* Determine ID to do the grant as, and available grant options */
|
|
|
|
select_best_grantor(GetUserId(), istmt->privileges,
|
|
|
|
old_acl, ownerId,
|
|
|
|
&grantorId, &avail_goptions);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Restrict the privileges to what we can actually grant, and emit the
|
|
|
|
* standards-mandated warning and error messages.
|
|
|
|
*/
|
|
|
|
this_privileges =
|
|
|
|
restrict_and_check_grant(istmt->is_grant, avail_goptions,
|
|
|
|
istmt->all_privs, istmt->privileges,
|
|
|
|
fdwid, grantorId, ACL_KIND_FDW,
|
2009-01-22 21:16:10 +01:00
|
|
|
NameStr(pg_fdw_tuple->fdwname),
|
|
|
|
0, NULL);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Generate new ACL.
|
|
|
|
*/
|
|
|
|
new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
|
|
|
|
istmt->grant_option, istmt->behavior,
|
|
|
|
istmt->grantees, this_privileges,
|
|
|
|
grantorId, ownerId);
|
|
|
|
|
2010-04-05 03:09:53 +02:00
|
|
|
/*
|
|
|
|
* We need the members of both old and new ACLs so we can correct the
|
|
|
|
* shared dependency information.
|
|
|
|
*/
|
2008-12-19 17:25:19 +01:00
|
|
|
nnewmembers = aclmembers(new_acl, &newmembers);
|
|
|
|
|
|
|
|
/* finished building new ACL value, now insert it */
|
|
|
|
MemSet(values, 0, sizeof(values));
|
|
|
|
MemSet(nulls, false, sizeof(nulls));
|
|
|
|
MemSet(replaces, false, sizeof(replaces));
|
|
|
|
|
|
|
|
replaces[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
|
|
|
|
values[Anum_pg_foreign_data_wrapper_fdwacl - 1] = PointerGetDatum(new_acl);
|
|
|
|
|
|
|
|
newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
|
|
|
|
nulls, replaces);
|
|
|
|
|
|
|
|
simple_heap_update(relation, &newtuple->t_self, newtuple);
|
|
|
|
|
|
|
|
/* keep the catalog indexes up to date */
|
|
|
|
CatalogUpdateIndexes(relation, newtuple);
|
|
|
|
|
2016-04-07 03:45:32 +02:00
|
|
|
/* Update initial privileges for extensions */
|
|
|
|
recordExtensionInitPriv(fdwid, ForeignDataWrapperRelationId, 0,
|
|
|
|
new_acl);
|
|
|
|
|
2008-12-19 17:25:19 +01:00
|
|
|
/* Update the shared dependency ACL info */
|
2009-01-22 21:16:10 +01:00
|
|
|
updateAclDependencies(ForeignDataWrapperRelationId,
|
|
|
|
HeapTupleGetOid(tuple), 0,
|
2010-04-05 03:09:53 +02:00
|
|
|
ownerId,
|
2008-12-19 17:25:19 +01:00
|
|
|
noldmembers, oldmembers,
|
|
|
|
nnewmembers, newmembers);
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
|
|
|
pfree(new_acl);
|
|
|
|
|
|
|
|
/* prevent error when processing duplicate objects */
|
|
|
|
CommandCounterIncrement();
|
|
|
|
}
|
|
|
|
|
|
|
|
heap_close(relation, RowExclusiveLock);
|
|
|
|
}
|
|
|
|
|
2009-06-11 16:49:15 +02:00
|
|
|
static void
|
|
|
|
ExecGrant_ForeignServer(InternalGrant *istmt)
|
2008-12-19 17:25:19 +01:00
|
|
|
{
|
|
|
|
Relation relation;
|
|
|
|
ListCell *cell;
|
|
|
|
|
|
|
|
if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
|
|
|
|
istmt->privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
|
|
|
|
|
|
|
|
relation = heap_open(ForeignServerRelationId, RowExclusiveLock);
|
|
|
|
|
|
|
|
foreach(cell, istmt->objects)
|
|
|
|
{
|
|
|
|
Oid srvid = lfirst_oid(cell);
|
|
|
|
Form_pg_foreign_server pg_server_tuple;
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
|
|
|
AclMode avail_goptions;
|
|
|
|
AclMode this_privileges;
|
|
|
|
Acl *old_acl;
|
|
|
|
Acl *new_acl;
|
|
|
|
Oid grantorId;
|
|
|
|
Oid ownerId;
|
|
|
|
HeapTuple tuple;
|
|
|
|
HeapTuple newtuple;
|
|
|
|
Datum values[Natts_pg_foreign_server];
|
|
|
|
bool nulls[Natts_pg_foreign_server];
|
|
|
|
bool replaces[Natts_pg_foreign_server];
|
|
|
|
int noldmembers;
|
|
|
|
int nnewmembers;
|
|
|
|
Oid *oldmembers;
|
|
|
|
Oid *newmembers;
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(srvid));
|
2008-12-19 17:25:19 +01:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
elog(ERROR, "cache lookup failed for foreign server %u", srvid);
|
|
|
|
|
|
|
|
pg_server_tuple = (Form_pg_foreign_server) GETSTRUCT(tuple);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get owner ID and working copy of existing ACL. If there's no ACL,
|
|
|
|
* substitute the proper default.
|
|
|
|
*/
|
|
|
|
ownerId = pg_server_tuple->srvowner;
|
|
|
|
aclDatum = SysCacheGetAttr(FOREIGNSERVEROID, tuple,
|
|
|
|
Anum_pg_foreign_server_srvacl,
|
|
|
|
&isNull);
|
|
|
|
if (isNull)
|
2010-04-05 03:09:53 +02:00
|
|
|
{
|
2008-12-19 17:25:19 +01:00
|
|
|
old_acl = acldefault(ACL_OBJECT_FOREIGN_SERVER, ownerId);
|
2010-04-05 03:09:53 +02:00
|
|
|
/* There are no old member roles according to the catalogs */
|
|
|
|
noldmembers = 0;
|
|
|
|
oldmembers = NULL;
|
|
|
|
}
|
2008-12-19 17:25:19 +01:00
|
|
|
else
|
2010-04-05 03:09:53 +02:00
|
|
|
{
|
2008-12-19 17:25:19 +01:00
|
|
|
old_acl = DatumGetAclPCopy(aclDatum);
|
2010-04-05 03:09:53 +02:00
|
|
|
/* Get the roles mentioned in the existing ACL */
|
|
|
|
noldmembers = aclmembers(old_acl, &oldmembers);
|
|
|
|
}
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
/* Determine ID to do the grant as, and available grant options */
|
|
|
|
select_best_grantor(GetUserId(), istmt->privileges,
|
|
|
|
old_acl, ownerId,
|
|
|
|
&grantorId, &avail_goptions);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Restrict the privileges to what we can actually grant, and emit the
|
|
|
|
* standards-mandated warning and error messages.
|
|
|
|
*/
|
|
|
|
this_privileges =
|
|
|
|
restrict_and_check_grant(istmt->is_grant, avail_goptions,
|
|
|
|
istmt->all_privs, istmt->privileges,
|
2009-06-11 16:49:15 +02:00
|
|
|
srvid, grantorId, ACL_KIND_FOREIGN_SERVER,
|
2009-01-22 21:16:10 +01:00
|
|
|
NameStr(pg_server_tuple->srvname),
|
|
|
|
0, NULL);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Generate new ACL.
|
|
|
|
*/
|
|
|
|
new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
|
|
|
|
istmt->grant_option, istmt->behavior,
|
|
|
|
istmt->grantees, this_privileges,
|
|
|
|
grantorId, ownerId);
|
|
|
|
|
2010-04-05 03:09:53 +02:00
|
|
|
/*
|
|
|
|
* We need the members of both old and new ACLs so we can correct the
|
|
|
|
* shared dependency information.
|
|
|
|
*/
|
2008-12-19 17:25:19 +01:00
|
|
|
nnewmembers = aclmembers(new_acl, &newmembers);
|
|
|
|
|
|
|
|
/* finished building new ACL value, now insert it */
|
|
|
|
MemSet(values, 0, sizeof(values));
|
|
|
|
MemSet(nulls, false, sizeof(nulls));
|
|
|
|
MemSet(replaces, false, sizeof(replaces));
|
|
|
|
|
|
|
|
replaces[Anum_pg_foreign_server_srvacl - 1] = true;
|
|
|
|
values[Anum_pg_foreign_server_srvacl - 1] = PointerGetDatum(new_acl);
|
|
|
|
|
|
|
|
newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
|
|
|
|
nulls, replaces);
|
|
|
|
|
|
|
|
simple_heap_update(relation, &newtuple->t_self, newtuple);
|
|
|
|
|
|
|
|
/* keep the catalog indexes up to date */
|
|
|
|
CatalogUpdateIndexes(relation, newtuple);
|
|
|
|
|
2016-04-07 03:45:32 +02:00
|
|
|
/* Update initial privileges for extensions */
|
|
|
|
recordExtensionInitPriv(srvid, ForeignServerRelationId, 0, new_acl);
|
|
|
|
|
2008-12-19 17:25:19 +01:00
|
|
|
/* Update the shared dependency ACL info */
|
2009-01-22 21:16:10 +01:00
|
|
|
updateAclDependencies(ForeignServerRelationId,
|
|
|
|
HeapTupleGetOid(tuple), 0,
|
2010-04-05 03:09:53 +02:00
|
|
|
ownerId,
|
2008-12-19 17:25:19 +01:00
|
|
|
noldmembers, oldmembers,
|
|
|
|
nnewmembers, newmembers);
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
|
|
|
pfree(new_acl);
|
|
|
|
|
|
|
|
/* prevent error when processing duplicate objects */
|
|
|
|
CommandCounterIncrement();
|
|
|
|
}
|
|
|
|
|
|
|
|
heap_close(relation, RowExclusiveLock);
|
|
|
|
}
|
|
|
|
|
2002-02-19 00:11:58 +01:00
|
|
|
static void
|
2005-12-01 03:03:01 +01:00
|
|
|
ExecGrant_Function(InternalGrant *istmt)
|
2002-02-19 00:11:58 +01:00
|
|
|
{
|
2005-11-21 13:49:33 +01:00
|
|
|
Relation relation;
|
|
|
|
ListCell *cell;
|
2002-02-19 00:11:58 +01:00
|
|
|
|
2005-12-01 03:03:01 +01:00
|
|
|
if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
|
|
|
|
istmt->privileges = ACL_ALL_RIGHTS_FUNCTION;
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2005-11-21 13:49:33 +01:00
|
|
|
relation = heap_open(ProcedureRelationId, RowExclusiveLock);
|
2002-02-19 00:11:58 +01:00
|
|
|
|
2005-12-01 03:03:01 +01:00
|
|
|
foreach(cell, istmt->objects)
|
2002-02-19 00:11:58 +01:00
|
|
|
{
|
2005-11-21 13:49:33 +01:00
|
|
|
Oid funcId = lfirst_oid(cell);
|
2002-02-19 00:11:58 +01:00
|
|
|
Form_pg_proc pg_proc_tuple;
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
2005-10-10 20:49:04 +02:00
|
|
|
AclMode avail_goptions;
|
2004-06-01 23:49:23 +02:00
|
|
|
AclMode this_privileges;
|
2002-02-19 00:11:58 +01:00
|
|
|
Acl *old_acl;
|
|
|
|
Acl *new_acl;
|
2005-10-15 04:49:52 +02:00
|
|
|
Oid grantorId;
|
|
|
|
Oid ownerId;
|
2005-11-21 13:49:33 +01:00
|
|
|
HeapTuple tuple;
|
2002-02-19 00:11:58 +01:00
|
|
|
HeapTuple newtuple;
|
|
|
|
Datum values[Natts_pg_proc];
|
2008-11-02 02:45:28 +01:00
|
|
|
bool nulls[Natts_pg_proc];
|
|
|
|
bool replaces[Natts_pg_proc];
|
2005-07-07 22:40:02 +02:00
|
|
|
int noldmembers;
|
|
|
|
int nnewmembers;
|
|
|
|
Oid *oldmembers;
|
|
|
|
Oid *newmembers;
|
2002-02-19 00:11:58 +01:00
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcId));
|
2002-02-19 00:11:58 +01:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2005-11-21 13:49:33 +01:00
|
|
|
elog(ERROR, "cache lookup failed for function %u", funcId);
|
|
|
|
|
2002-02-19 00:11:58 +01:00
|
|
|
pg_proc_tuple = (Form_pg_proc) GETSTRUCT(tuple);
|
|
|
|
|
2005-10-10 20:49:04 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Get owner ID and working copy of existing ACL. If there's no ACL,
|
|
|
|
* substitute the proper default.
|
2005-10-10 20:49:04 +02:00
|
|
|
*/
|
2003-10-31 21:00:49 +01:00
|
|
|
ownerId = pg_proc_tuple->proowner;
|
2005-10-10 20:49:04 +02:00
|
|
|
aclDatum = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_proacl,
|
|
|
|
&isNull);
|
|
|
|
if (isNull)
|
2010-04-05 03:09:53 +02:00
|
|
|
{
|
2005-10-10 20:49:04 +02:00
|
|
|
old_acl = acldefault(ACL_OBJECT_FUNCTION, ownerId);
|
2010-04-05 03:09:53 +02:00
|
|
|
/* There are no old member roles according to the catalogs */
|
|
|
|
noldmembers = 0;
|
|
|
|
oldmembers = NULL;
|
|
|
|
}
|
2005-10-10 20:49:04 +02:00
|
|
|
else
|
2010-04-05 03:09:53 +02:00
|
|
|
{
|
2005-10-10 20:49:04 +02:00
|
|
|
old_acl = DatumGetAclPCopy(aclDatum);
|
2010-04-05 03:09:53 +02:00
|
|
|
/* Get the roles mentioned in the existing ACL */
|
|
|
|
noldmembers = aclmembers(old_acl, &oldmembers);
|
|
|
|
}
|
2005-10-10 20:49:04 +02:00
|
|
|
|
|
|
|
/* Determine ID to do the grant as, and available grant options */
|
2005-12-01 03:03:01 +01:00
|
|
|
select_best_grantor(GetUserId(), istmt->privileges,
|
2005-10-10 20:49:04 +02:00
|
|
|
old_acl, ownerId,
|
|
|
|
&grantorId, &avail_goptions);
|
2003-10-31 21:00:49 +01:00
|
|
|
|
2004-06-01 23:49:23 +02:00
|
|
|
/*
|
2006-10-04 02:30:14 +02:00
|
|
|
* Restrict the privileges to what we can actually grant, and emit the
|
|
|
|
* standards-mandated warning and error messages.
|
2004-06-01 23:49:23 +02:00
|
|
|
*/
|
2005-12-01 03:03:01 +01:00
|
|
|
this_privileges =
|
|
|
|
restrict_and_check_grant(istmt->is_grant, avail_goptions,
|
|
|
|
istmt->all_privs, istmt->privileges,
|
|
|
|
funcId, grantorId, ACL_KIND_PROC,
|
2009-01-22 21:16:10 +01:00
|
|
|
NameStr(pg_proc_tuple->proname),
|
|
|
|
0, NULL);
|
2002-02-19 00:11:58 +01:00
|
|
|
|
|
|
|
/*
|
2005-10-10 20:49:04 +02:00
|
|
|
* Generate new ACL.
|
2005-07-07 22:40:02 +02:00
|
|
|
*/
|
2005-12-01 03:03:01 +01:00
|
|
|
new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
|
|
|
|
istmt->grant_option, istmt->behavior,
|
|
|
|
istmt->grantees, this_privileges,
|
2003-10-31 21:00:49 +01:00
|
|
|
grantorId, ownerId);
|
2002-02-19 00:11:58 +01:00
|
|
|
|
2010-04-05 03:09:53 +02:00
|
|
|
/*
|
|
|
|
* We need the members of both old and new ACLs so we can correct the
|
|
|
|
* shared dependency information.
|
|
|
|
*/
|
2005-07-07 22:40:02 +02:00
|
|
|
nnewmembers = aclmembers(new_acl, &newmembers);
|
|
|
|
|
2002-02-19 00:11:58 +01:00
|
|
|
/* finished building new ACL value, now insert it */
|
2002-04-21 02:26:44 +02:00
|
|
|
MemSet(values, 0, sizeof(values));
|
2008-11-02 02:45:28 +01:00
|
|
|
MemSet(nulls, false, sizeof(nulls));
|
|
|
|
MemSet(replaces, false, sizeof(replaces));
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2008-11-02 02:45:28 +01:00
|
|
|
replaces[Anum_pg_proc_proacl - 1] = true;
|
2002-02-19 00:11:58 +01:00
|
|
|
values[Anum_pg_proc_proacl - 1] = PointerGetDatum(new_acl);
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2008-11-02 02:45:28 +01:00
|
|
|
newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
|
|
|
|
nulls, replaces);
|
2002-02-19 00:11:58 +01:00
|
|
|
|
|
|
|
simple_heap_update(relation, &newtuple->t_self, newtuple);
|
|
|
|
|
2002-08-05 05:29:17 +02:00
|
|
|
/* keep the catalog indexes up to date */
|
|
|
|
CatalogUpdateIndexes(relation, newtuple);
|
2002-02-19 00:11:58 +01:00
|
|
|
|
2016-04-07 03:45:32 +02:00
|
|
|
/* Update initial privileges for extensions */
|
|
|
|
recordExtensionInitPriv(funcId, ProcedureRelationId, 0, new_acl);
|
|
|
|
|
2005-07-07 22:40:02 +02:00
|
|
|
/* Update the shared dependency ACL info */
|
2009-01-22 21:16:10 +01:00
|
|
|
updateAclDependencies(ProcedureRelationId, funcId, 0,
|
2010-04-05 03:09:53 +02:00
|
|
|
ownerId,
|
2005-07-07 22:40:02 +02:00
|
|
|
noldmembers, oldmembers,
|
|
|
|
nnewmembers, newmembers);
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
2002-02-19 00:11:58 +01:00
|
|
|
pfree(new_acl);
|
|
|
|
|
2005-08-12 23:20:24 +02:00
|
|
|
/* prevent error when processing duplicate objects */
|
|
|
|
CommandCounterIncrement();
|
2002-02-19 00:11:58 +01:00
|
|
|
}
|
2005-11-21 13:49:33 +01:00
|
|
|
|
|
|
|
heap_close(relation, RowExclusiveLock);
|
2002-02-19 00:11:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2005-12-01 03:03:01 +01:00
|
|
|
ExecGrant_Language(InternalGrant *istmt)
|
2002-02-19 00:11:58 +01:00
|
|
|
{
|
2005-11-21 13:49:33 +01:00
|
|
|
Relation relation;
|
|
|
|
ListCell *cell;
|
2002-02-19 00:11:58 +01:00
|
|
|
|
2005-12-01 03:03:01 +01:00
|
|
|
if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
|
|
|
|
istmt->privileges = ACL_ALL_RIGHTS_LANGUAGE;
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2005-11-21 13:49:33 +01:00
|
|
|
relation = heap_open(LanguageRelationId, RowExclusiveLock);
|
2002-02-19 00:11:58 +01:00
|
|
|
|
2005-12-01 03:03:01 +01:00
|
|
|
foreach(cell, istmt->objects)
|
2002-02-19 00:11:58 +01:00
|
|
|
{
|
2005-12-01 03:03:01 +01:00
|
|
|
Oid langId = lfirst_oid(cell);
|
2002-02-19 00:11:58 +01:00
|
|
|
Form_pg_language pg_language_tuple;
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
2005-10-10 20:49:04 +02:00
|
|
|
AclMode avail_goptions;
|
2004-06-01 23:49:23 +02:00
|
|
|
AclMode this_privileges;
|
2002-02-19 00:11:58 +01:00
|
|
|
Acl *old_acl;
|
|
|
|
Acl *new_acl;
|
2005-10-15 04:49:52 +02:00
|
|
|
Oid grantorId;
|
|
|
|
Oid ownerId;
|
2005-11-21 13:49:33 +01:00
|
|
|
HeapTuple tuple;
|
2002-02-19 00:11:58 +01:00
|
|
|
HeapTuple newtuple;
|
|
|
|
Datum values[Natts_pg_language];
|
2008-11-02 02:45:28 +01:00
|
|
|
bool nulls[Natts_pg_language];
|
|
|
|
bool replaces[Natts_pg_language];
|
2005-07-07 22:40:02 +02:00
|
|
|
int noldmembers;
|
|
|
|
int nnewmembers;
|
|
|
|
Oid *oldmembers;
|
|
|
|
Oid *newmembers;
|
2002-02-19 00:11:58 +01:00
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(LANGOID, ObjectIdGetDatum(langId));
|
2002-02-19 00:11:58 +01:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2005-12-01 03:03:01 +01:00
|
|
|
elog(ERROR, "cache lookup failed for language %u", langId);
|
2005-11-21 13:49:33 +01:00
|
|
|
|
2002-02-19 00:11:58 +01:00
|
|
|
pg_language_tuple = (Form_pg_language) GETSTRUCT(tuple);
|
|
|
|
|
2004-06-01 23:49:23 +02:00
|
|
|
if (!pg_language_tuple->lanpltrusted)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
2005-11-21 13:49:33 +01:00
|
|
|
errmsg("language \"%s\" is not trusted",
|
|
|
|
NameStr(pg_language_tuple->lanname)),
|
2016-06-19 01:38:59 +02:00
|
|
|
errdetail("GRANT and REVOKE are not allowed on untrusted languages, "
|
|
|
|
"because only superusers can use untrusted languages.")));
|
2004-06-01 23:49:23 +02:00
|
|
|
|
2003-10-31 21:00:49 +01:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Get owner ID and working copy of existing ACL. If there's no ACL,
|
|
|
|
* substitute the proper default.
|
2003-10-31 21:00:49 +01:00
|
|
|
*/
|
2007-03-26 18:58:41 +02:00
|
|
|
ownerId = pg_language_tuple->lanowner;
|
2005-10-10 20:49:04 +02:00
|
|
|
aclDatum = SysCacheGetAttr(LANGNAME, tuple, Anum_pg_language_lanacl,
|
|
|
|
&isNull);
|
|
|
|
if (isNull)
|
2010-04-05 03:09:53 +02:00
|
|
|
{
|
2005-10-10 20:49:04 +02:00
|
|
|
old_acl = acldefault(ACL_OBJECT_LANGUAGE, ownerId);
|
2010-04-05 03:09:53 +02:00
|
|
|
/* There are no old member roles according to the catalogs */
|
|
|
|
noldmembers = 0;
|
|
|
|
oldmembers = NULL;
|
|
|
|
}
|
2005-10-10 20:49:04 +02:00
|
|
|
else
|
2010-04-05 03:09:53 +02:00
|
|
|
{
|
2005-10-10 20:49:04 +02:00
|
|
|
old_acl = DatumGetAclPCopy(aclDatum);
|
2010-04-05 03:09:53 +02:00
|
|
|
/* Get the roles mentioned in the existing ACL */
|
|
|
|
noldmembers = aclmembers(old_acl, &oldmembers);
|
|
|
|
}
|
2005-10-10 20:49:04 +02:00
|
|
|
|
|
|
|
/* Determine ID to do the grant as, and available grant options */
|
2005-12-01 03:03:01 +01:00
|
|
|
select_best_grantor(GetUserId(), istmt->privileges,
|
2005-10-10 20:49:04 +02:00
|
|
|
old_acl, ownerId,
|
|
|
|
&grantorId, &avail_goptions);
|
2002-02-19 00:11:58 +01:00
|
|
|
|
2004-06-01 23:49:23 +02:00
|
|
|
/*
|
2006-10-04 02:30:14 +02:00
|
|
|
* Restrict the privileges to what we can actually grant, and emit the
|
|
|
|
* standards-mandated warning and error messages.
|
2004-06-01 23:49:23 +02:00
|
|
|
*/
|
2005-12-01 03:03:01 +01:00
|
|
|
this_privileges =
|
|
|
|
restrict_and_check_grant(istmt->is_grant, avail_goptions,
|
|
|
|
istmt->all_privs, istmt->privileges,
|
|
|
|
langId, grantorId, ACL_KIND_LANGUAGE,
|
2009-01-22 21:16:10 +01:00
|
|
|
NameStr(pg_language_tuple->lanname),
|
|
|
|
0, NULL);
|
2003-10-31 21:00:49 +01:00
|
|
|
|
2002-02-19 00:11:58 +01:00
|
|
|
/*
|
2005-10-10 20:49:04 +02:00
|
|
|
* Generate new ACL.
|
2005-07-07 22:40:02 +02:00
|
|
|
*/
|
2005-12-01 03:03:01 +01:00
|
|
|
new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
|
|
|
|
istmt->grant_option, istmt->behavior,
|
|
|
|
istmt->grantees, this_privileges,
|
2003-10-31 21:00:49 +01:00
|
|
|
grantorId, ownerId);
|
2002-02-19 00:11:58 +01:00
|
|
|
|
2010-04-05 03:09:53 +02:00
|
|
|
/*
|
|
|
|
* We need the members of both old and new ACLs so we can correct the
|
|
|
|
* shared dependency information.
|
|
|
|
*/
|
2005-07-07 22:40:02 +02:00
|
|
|
nnewmembers = aclmembers(new_acl, &newmembers);
|
|
|
|
|
2002-02-19 00:11:58 +01:00
|
|
|
/* finished building new ACL value, now insert it */
|
2002-04-21 02:26:44 +02:00
|
|
|
MemSet(values, 0, sizeof(values));
|
2008-11-02 02:45:28 +01:00
|
|
|
MemSet(nulls, false, sizeof(nulls));
|
|
|
|
MemSet(replaces, false, sizeof(replaces));
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2008-11-02 02:45:28 +01:00
|
|
|
replaces[Anum_pg_language_lanacl - 1] = true;
|
2002-02-19 00:11:58 +01:00
|
|
|
values[Anum_pg_language_lanacl - 1] = PointerGetDatum(new_acl);
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2008-11-02 02:45:28 +01:00
|
|
|
newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
|
|
|
|
nulls, replaces);
|
2002-02-19 00:11:58 +01:00
|
|
|
|
|
|
|
simple_heap_update(relation, &newtuple->t_self, newtuple);
|
|
|
|
|
2002-08-05 05:29:17 +02:00
|
|
|
/* keep the catalog indexes up to date */
|
|
|
|
CatalogUpdateIndexes(relation, newtuple);
|
2002-02-19 00:11:58 +01:00
|
|
|
|
2016-04-07 03:45:32 +02:00
|
|
|
/* Update initial privileges for extensions */
|
|
|
|
recordExtensionInitPriv(langId, LanguageRelationId, 0, new_acl);
|
|
|
|
|
2005-07-07 22:40:02 +02:00
|
|
|
/* Update the shared dependency ACL info */
|
2009-01-22 21:16:10 +01:00
|
|
|
updateAclDependencies(LanguageRelationId, HeapTupleGetOid(tuple), 0,
|
2010-04-05 03:09:53 +02:00
|
|
|
ownerId,
|
2005-07-07 22:40:02 +02:00
|
|
|
noldmembers, oldmembers,
|
|
|
|
nnewmembers, newmembers);
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
2002-02-19 00:11:58 +01:00
|
|
|
pfree(new_acl);
|
|
|
|
|
2005-08-12 23:20:24 +02:00
|
|
|
/* prevent error when processing duplicate objects */
|
|
|
|
CommandCounterIncrement();
|
2002-02-19 00:11:58 +01:00
|
|
|
}
|
2005-11-21 13:49:33 +01:00
|
|
|
|
|
|
|
heap_close(relation, RowExclusiveLock);
|
2002-02-19 00:11:58 +01:00
|
|
|
}
|
|
|
|
|
2009-12-11 04:34:57 +01:00
|
|
|
static void
|
|
|
|
ExecGrant_Largeobject(InternalGrant *istmt)
|
|
|
|
{
|
|
|
|
Relation relation;
|
|
|
|
ListCell *cell;
|
|
|
|
|
|
|
|
if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
|
|
|
|
istmt->privileges = ACL_ALL_RIGHTS_LARGEOBJECT;
|
|
|
|
|
|
|
|
relation = heap_open(LargeObjectMetadataRelationId,
|
|
|
|
RowExclusiveLock);
|
|
|
|
|
|
|
|
foreach(cell, istmt->objects)
|
|
|
|
{
|
|
|
|
Oid loid = lfirst_oid(cell);
|
2010-02-26 03:01:40 +01:00
|
|
|
Form_pg_largeobject_metadata form_lo_meta;
|
2009-12-11 04:34:57 +01:00
|
|
|
char loname[NAMEDATALEN];
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
|
|
|
AclMode avail_goptions;
|
|
|
|
AclMode this_privileges;
|
|
|
|
Acl *old_acl;
|
|
|
|
Acl *new_acl;
|
|
|
|
Oid grantorId;
|
|
|
|
Oid ownerId;
|
|
|
|
HeapTuple newtuple;
|
|
|
|
Datum values[Natts_pg_largeobject_metadata];
|
|
|
|
bool nulls[Natts_pg_largeobject_metadata];
|
|
|
|
bool replaces[Natts_pg_largeobject_metadata];
|
|
|
|
int noldmembers;
|
|
|
|
int nnewmembers;
|
|
|
|
Oid *oldmembers;
|
|
|
|
Oid *newmembers;
|
2010-02-26 03:01:40 +01:00
|
|
|
ScanKeyData entry[1];
|
|
|
|
SysScanDesc scan;
|
2009-12-11 04:34:57 +01:00
|
|
|
HeapTuple tuple;
|
|
|
|
|
|
|
|
/* There's no syscache for pg_largeobject_metadata */
|
|
|
|
ScanKeyInit(&entry[0],
|
|
|
|
ObjectIdAttributeNumber,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(loid));
|
|
|
|
|
|
|
|
scan = systable_beginscan(relation,
|
|
|
|
LargeObjectMetadataOidIndexId, true,
|
Use an MVCC snapshot, rather than SnapshotNow, for catalog scans.
SnapshotNow scans have the undesirable property that, in the face of
concurrent updates, the scan can fail to see either the old or the new
versions of the row. In many cases, we work around this by requiring
DDL operations to hold AccessExclusiveLock on the object being
modified; in some cases, the existing locking is inadequate and random
failures occur as a result. This commit doesn't change anything
related to locking, but will hopefully pave the way to allowing lock
strength reductions in the future.
The major issue has held us back from making this change in the past
is that taking an MVCC snapshot is significantly more expensive than
using a static special snapshot such as SnapshotNow. However, testing
of various worst-case scenarios reveals that this problem is not
severe except under fairly extreme workloads. To mitigate those
problems, we avoid retaking the MVCC snapshot for each new scan;
instead, we take a new snapshot only when invalidation messages have
been processed. The catcache machinery already requires that
invalidation messages be sent before releasing the related heavyweight
lock; else other backends might rely on locally-cached data rather
than scanning the catalog at all. Thus, making snapshot reuse
dependent on the same guarantees shouldn't break anything that wasn't
already subtly broken.
Patch by me. Review by Michael Paquier and Andres Freund.
2013-07-02 15:47:01 +02:00
|
|
|
NULL, 1, entry);
|
2009-12-11 04:34:57 +01:00
|
|
|
|
|
|
|
tuple = systable_getnext(scan);
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
elog(ERROR, "cache lookup failed for large object %u", loid);
|
|
|
|
|
|
|
|
form_lo_meta = (Form_pg_largeobject_metadata) GETSTRUCT(tuple);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get owner ID and working copy of existing ACL. If there's no ACL,
|
|
|
|
* substitute the proper default.
|
|
|
|
*/
|
|
|
|
ownerId = form_lo_meta->lomowner;
|
|
|
|
aclDatum = heap_getattr(tuple,
|
|
|
|
Anum_pg_largeobject_metadata_lomacl,
|
|
|
|
RelationGetDescr(relation), &isNull);
|
|
|
|
if (isNull)
|
2010-04-05 03:09:53 +02:00
|
|
|
{
|
2009-12-11 04:34:57 +01:00
|
|
|
old_acl = acldefault(ACL_OBJECT_LARGEOBJECT, ownerId);
|
2010-04-05 03:09:53 +02:00
|
|
|
/* There are no old member roles according to the catalogs */
|
|
|
|
noldmembers = 0;
|
|
|
|
oldmembers = NULL;
|
|
|
|
}
|
2009-12-11 04:34:57 +01:00
|
|
|
else
|
2010-04-05 03:09:53 +02:00
|
|
|
{
|
2009-12-11 04:34:57 +01:00
|
|
|
old_acl = DatumGetAclPCopy(aclDatum);
|
2010-04-05 03:09:53 +02:00
|
|
|
/* Get the roles mentioned in the existing ACL */
|
|
|
|
noldmembers = aclmembers(old_acl, &oldmembers);
|
|
|
|
}
|
2009-12-11 04:34:57 +01:00
|
|
|
|
|
|
|
/* Determine ID to do the grant as, and available grant options */
|
|
|
|
select_best_grantor(GetUserId(), istmt->privileges,
|
|
|
|
old_acl, ownerId,
|
|
|
|
&grantorId, &avail_goptions);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Restrict the privileges to what we can actually grant, and emit the
|
|
|
|
* standards-mandated warning and error messages.
|
|
|
|
*/
|
|
|
|
snprintf(loname, sizeof(loname), "large object %u", loid);
|
|
|
|
this_privileges =
|
|
|
|
restrict_and_check_grant(istmt->is_grant, avail_goptions,
|
|
|
|
istmt->all_privs, istmt->privileges,
|
|
|
|
loid, grantorId, ACL_KIND_LARGEOBJECT,
|
|
|
|
loname, 0, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generate new ACL.
|
|
|
|
*/
|
|
|
|
new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
|
|
|
|
istmt->grant_option, istmt->behavior,
|
|
|
|
istmt->grantees, this_privileges,
|
|
|
|
grantorId, ownerId);
|
|
|
|
|
2010-04-05 03:09:53 +02:00
|
|
|
/*
|
|
|
|
* We need the members of both old and new ACLs so we can correct the
|
|
|
|
* shared dependency information.
|
|
|
|
*/
|
2009-12-11 04:34:57 +01:00
|
|
|
nnewmembers = aclmembers(new_acl, &newmembers);
|
|
|
|
|
|
|
|
/* finished building new ACL value, now insert it */
|
|
|
|
MemSet(values, 0, sizeof(values));
|
|
|
|
MemSet(nulls, false, sizeof(nulls));
|
|
|
|
MemSet(replaces, false, sizeof(replaces));
|
|
|
|
|
|
|
|
replaces[Anum_pg_largeobject_metadata_lomacl - 1] = true;
|
|
|
|
values[Anum_pg_largeobject_metadata_lomacl - 1]
|
|
|
|
= PointerGetDatum(new_acl);
|
|
|
|
|
|
|
|
newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation),
|
|
|
|
values, nulls, replaces);
|
|
|
|
|
|
|
|
simple_heap_update(relation, &newtuple->t_self, newtuple);
|
|
|
|
|
|
|
|
/* keep the catalog indexes up to date */
|
|
|
|
CatalogUpdateIndexes(relation, newtuple);
|
|
|
|
|
2016-04-07 03:45:32 +02:00
|
|
|
/* Update initial privileges for extensions */
|
|
|
|
recordExtensionInitPriv(loid, LargeObjectRelationId, 0, new_acl);
|
|
|
|
|
2009-12-11 04:34:57 +01:00
|
|
|
/* Update the shared dependency ACL info */
|
|
|
|
updateAclDependencies(LargeObjectRelationId,
|
|
|
|
HeapTupleGetOid(tuple), 0,
|
2010-04-05 03:09:53 +02:00
|
|
|
ownerId,
|
2009-12-11 04:34:57 +01:00
|
|
|
noldmembers, oldmembers,
|
|
|
|
nnewmembers, newmembers);
|
|
|
|
|
|
|
|
systable_endscan(scan);
|
|
|
|
|
|
|
|
pfree(new_acl);
|
|
|
|
|
|
|
|
/* prevent error when processing duplicate objects */
|
|
|
|
CommandCounterIncrement();
|
|
|
|
}
|
|
|
|
|
|
|
|
heap_close(relation, RowExclusiveLock);
|
|
|
|
}
|
|
|
|
|
2002-04-21 02:26:44 +02:00
|
|
|
static void
|
2005-12-01 03:03:01 +01:00
|
|
|
ExecGrant_Namespace(InternalGrant *istmt)
|
2002-02-19 00:11:58 +01:00
|
|
|
{
|
2005-11-21 13:49:33 +01:00
|
|
|
Relation relation;
|
|
|
|
ListCell *cell;
|
2002-02-19 00:11:58 +01:00
|
|
|
|
2005-12-01 03:03:01 +01:00
|
|
|
if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
|
|
|
|
istmt->privileges = ACL_ALL_RIGHTS_NAMESPACE;
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2005-11-21 13:49:33 +01:00
|
|
|
relation = heap_open(NamespaceRelationId, RowExclusiveLock);
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2005-12-01 03:03:01 +01:00
|
|
|
foreach(cell, istmt->objects)
|
2002-04-21 02:26:44 +02:00
|
|
|
{
|
2005-11-21 13:49:33 +01:00
|
|
|
Oid nspid = lfirst_oid(cell);
|
2002-04-21 02:26:44 +02:00
|
|
|
Form_pg_namespace pg_namespace_tuple;
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
2005-10-10 20:49:04 +02:00
|
|
|
AclMode avail_goptions;
|
2004-06-01 23:49:23 +02:00
|
|
|
AclMode this_privileges;
|
2002-04-21 02:26:44 +02:00
|
|
|
Acl *old_acl;
|
|
|
|
Acl *new_acl;
|
2005-10-15 04:49:52 +02:00
|
|
|
Oid grantorId;
|
|
|
|
Oid ownerId;
|
2005-11-21 13:49:33 +01:00
|
|
|
HeapTuple tuple;
|
2002-04-21 02:26:44 +02:00
|
|
|
HeapTuple newtuple;
|
|
|
|
Datum values[Natts_pg_namespace];
|
2008-11-02 02:45:28 +01:00
|
|
|
bool nulls[Natts_pg_namespace];
|
|
|
|
bool replaces[Natts_pg_namespace];
|
2005-07-07 22:40:02 +02:00
|
|
|
int noldmembers;
|
|
|
|
int nnewmembers;
|
|
|
|
Oid *oldmembers;
|
|
|
|
Oid *newmembers;
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(nspid));
|
2002-04-21 02:26:44 +02:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2005-11-21 13:49:33 +01:00
|
|
|
elog(ERROR, "cache lookup failed for namespace %u", nspid);
|
|
|
|
|
2002-04-21 02:26:44 +02:00
|
|
|
pg_namespace_tuple = (Form_pg_namespace) GETSTRUCT(tuple);
|
|
|
|
|
2005-10-10 20:49:04 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Get owner ID and working copy of existing ACL. If there's no ACL,
|
|
|
|
* substitute the proper default.
|
2005-10-10 20:49:04 +02:00
|
|
|
*/
|
2003-10-31 21:00:49 +01:00
|
|
|
ownerId = pg_namespace_tuple->nspowner;
|
2005-10-10 20:49:04 +02:00
|
|
|
aclDatum = SysCacheGetAttr(NAMESPACENAME, tuple,
|
|
|
|
Anum_pg_namespace_nspacl,
|
|
|
|
&isNull);
|
|
|
|
if (isNull)
|
2010-04-05 03:09:53 +02:00
|
|
|
{
|
2005-10-10 20:49:04 +02:00
|
|
|
old_acl = acldefault(ACL_OBJECT_NAMESPACE, ownerId);
|
2010-04-05 03:09:53 +02:00
|
|
|
/* There are no old member roles according to the catalogs */
|
|
|
|
noldmembers = 0;
|
|
|
|
oldmembers = NULL;
|
|
|
|
}
|
2005-10-10 20:49:04 +02:00
|
|
|
else
|
2010-04-05 03:09:53 +02:00
|
|
|
{
|
2005-10-10 20:49:04 +02:00
|
|
|
old_acl = DatumGetAclPCopy(aclDatum);
|
2010-04-05 03:09:53 +02:00
|
|
|
/* Get the roles mentioned in the existing ACL */
|
|
|
|
noldmembers = aclmembers(old_acl, &oldmembers);
|
|
|
|
}
|
2005-10-10 20:49:04 +02:00
|
|
|
|
|
|
|
/* Determine ID to do the grant as, and available grant options */
|
2005-12-01 03:03:01 +01:00
|
|
|
select_best_grantor(GetUserId(), istmt->privileges,
|
2005-10-10 20:49:04 +02:00
|
|
|
old_acl, ownerId,
|
|
|
|
&grantorId, &avail_goptions);
|
2003-10-31 21:00:49 +01:00
|
|
|
|
2004-06-01 23:49:23 +02:00
|
|
|
/*
|
2006-10-04 02:30:14 +02:00
|
|
|
* Restrict the privileges to what we can actually grant, and emit the
|
|
|
|
* standards-mandated warning and error messages.
|
2004-06-01 23:49:23 +02:00
|
|
|
*/
|
2005-12-01 03:03:01 +01:00
|
|
|
this_privileges =
|
|
|
|
restrict_and_check_grant(istmt->is_grant, avail_goptions,
|
|
|
|
istmt->all_privs, istmt->privileges,
|
|
|
|
nspid, grantorId, ACL_KIND_NAMESPACE,
|
2009-01-22 21:16:10 +01:00
|
|
|
NameStr(pg_namespace_tuple->nspname),
|
|
|
|
0, NULL);
|
2002-04-21 02:26:44 +02:00
|
|
|
|
|
|
|
/*
|
2005-10-10 20:49:04 +02:00
|
|
|
* Generate new ACL.
|
2005-07-07 22:40:02 +02:00
|
|
|
*/
|
2005-12-01 03:03:01 +01:00
|
|
|
new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
|
|
|
|
istmt->grant_option, istmt->behavior,
|
|
|
|
istmt->grantees, this_privileges,
|
2003-10-31 21:00:49 +01:00
|
|
|
grantorId, ownerId);
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2010-04-05 03:09:53 +02:00
|
|
|
/*
|
|
|
|
* We need the members of both old and new ACLs so we can correct the
|
|
|
|
* shared dependency information.
|
|
|
|
*/
|
2005-07-07 22:40:02 +02:00
|
|
|
nnewmembers = aclmembers(new_acl, &newmembers);
|
|
|
|
|
2002-04-21 02:26:44 +02:00
|
|
|
/* finished building new ACL value, now insert it */
|
|
|
|
MemSet(values, 0, sizeof(values));
|
2008-11-02 02:45:28 +01:00
|
|
|
MemSet(nulls, false, sizeof(nulls));
|
|
|
|
MemSet(replaces, false, sizeof(replaces));
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2008-11-02 02:45:28 +01:00
|
|
|
replaces[Anum_pg_namespace_nspacl - 1] = true;
|
2002-04-21 02:26:44 +02:00
|
|
|
values[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(new_acl);
|
|
|
|
|
2008-11-02 02:45:28 +01:00
|
|
|
newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
|
|
|
|
nulls, replaces);
|
2002-04-21 02:26:44 +02:00
|
|
|
|
|
|
|
simple_heap_update(relation, &newtuple->t_self, newtuple);
|
|
|
|
|
2002-08-05 05:29:17 +02:00
|
|
|
/* keep the catalog indexes up to date */
|
|
|
|
CatalogUpdateIndexes(relation, newtuple);
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2016-04-07 03:45:32 +02:00
|
|
|
/* Update initial privileges for extensions */
|
|
|
|
recordExtensionInitPriv(nspid, NamespaceRelationId, 0, new_acl);
|
|
|
|
|
2005-07-07 22:40:02 +02:00
|
|
|
/* Update the shared dependency ACL info */
|
2009-01-22 21:16:10 +01:00
|
|
|
updateAclDependencies(NamespaceRelationId, HeapTupleGetOid(tuple), 0,
|
2010-04-05 03:09:53 +02:00
|
|
|
ownerId,
|
2005-07-07 22:40:02 +02:00
|
|
|
noldmembers, oldmembers,
|
|
|
|
nnewmembers, newmembers);
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
2002-04-21 02:26:44 +02:00
|
|
|
pfree(new_acl);
|
|
|
|
|
2005-08-12 23:20:24 +02:00
|
|
|
/* prevent error when processing duplicate objects */
|
|
|
|
CommandCounterIncrement();
|
2002-04-21 02:26:44 +02:00
|
|
|
}
|
2005-11-21 13:49:33 +01:00
|
|
|
|
|
|
|
heap_close(relation, RowExclusiveLock);
|
2002-02-19 00:11:58 +01:00
|
|
|
}
|
|
|
|
|
2004-06-18 08:14:31 +02:00
|
|
|
static void
|
2005-12-01 03:03:01 +01:00
|
|
|
ExecGrant_Tablespace(InternalGrant *istmt)
|
2004-06-18 08:14:31 +02:00
|
|
|
{
|
2005-11-21 13:49:33 +01:00
|
|
|
Relation relation;
|
|
|
|
ListCell *cell;
|
2004-06-18 08:14:31 +02:00
|
|
|
|
2005-12-01 03:03:01 +01:00
|
|
|
if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
|
|
|
|
istmt->privileges = ACL_ALL_RIGHTS_TABLESPACE;
|
2004-06-18 08:14:31 +02:00
|
|
|
|
2005-11-21 13:49:33 +01:00
|
|
|
relation = heap_open(TableSpaceRelationId, RowExclusiveLock);
|
2004-06-18 08:14:31 +02:00
|
|
|
|
2005-12-01 03:03:01 +01:00
|
|
|
foreach(cell, istmt->objects)
|
2004-06-18 08:14:31 +02:00
|
|
|
{
|
2005-11-21 13:49:33 +01:00
|
|
|
Oid tblId = lfirst_oid(cell);
|
2004-06-18 08:14:31 +02:00
|
|
|
Form_pg_tablespace pg_tablespace_tuple;
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
2005-10-10 20:49:04 +02:00
|
|
|
AclMode avail_goptions;
|
2004-06-18 08:14:31 +02:00
|
|
|
AclMode this_privileges;
|
|
|
|
Acl *old_acl;
|
|
|
|
Acl *new_acl;
|
2005-10-15 04:49:52 +02:00
|
|
|
Oid grantorId;
|
|
|
|
Oid ownerId;
|
2004-06-18 08:14:31 +02:00
|
|
|
HeapTuple newtuple;
|
|
|
|
Datum values[Natts_pg_tablespace];
|
2008-11-02 02:45:28 +01:00
|
|
|
bool nulls[Natts_pg_tablespace];
|
|
|
|
bool replaces[Natts_pg_tablespace];
|
2005-07-07 22:40:02 +02:00
|
|
|
int noldmembers;
|
|
|
|
int nnewmembers;
|
|
|
|
Oid *oldmembers;
|
|
|
|
Oid *newmembers;
|
2005-11-21 13:49:33 +01:00
|
|
|
HeapTuple tuple;
|
2004-06-18 08:14:31 +02:00
|
|
|
|
2010-01-05 22:54:00 +01:00
|
|
|
/* Search syscache for pg_tablespace */
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(TABLESPACEOID, ObjectIdGetDatum(tblId));
|
2004-06-18 08:14:31 +02:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2005-11-21 13:49:33 +01:00
|
|
|
elog(ERROR, "cache lookup failed for tablespace %u", tblId);
|
|
|
|
|
2004-06-18 08:14:31 +02:00
|
|
|
pg_tablespace_tuple = (Form_pg_tablespace) GETSTRUCT(tuple);
|
|
|
|
|
2005-10-10 20:49:04 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Get owner ID and working copy of existing ACL. If there's no ACL,
|
|
|
|
* substitute the proper default.
|
2005-10-10 20:49:04 +02:00
|
|
|
*/
|
2004-06-18 08:14:31 +02:00
|
|
|
ownerId = pg_tablespace_tuple->spcowner;
|
2005-10-10 20:49:04 +02:00
|
|
|
aclDatum = heap_getattr(tuple, Anum_pg_tablespace_spcacl,
|
|
|
|
RelationGetDescr(relation), &isNull);
|
|
|
|
if (isNull)
|
2010-04-05 03:09:53 +02:00
|
|
|
{
|
2005-10-10 20:49:04 +02:00
|
|
|
old_acl = acldefault(ACL_OBJECT_TABLESPACE, ownerId);
|
2010-04-05 03:09:53 +02:00
|
|
|
/* There are no old member roles according to the catalogs */
|
|
|
|
noldmembers = 0;
|
|
|
|
oldmembers = NULL;
|
|
|
|
}
|
2005-10-10 20:49:04 +02:00
|
|
|
else
|
2010-04-05 03:09:53 +02:00
|
|
|
{
|
2005-10-10 20:49:04 +02:00
|
|
|
old_acl = DatumGetAclPCopy(aclDatum);
|
2010-04-05 03:09:53 +02:00
|
|
|
/* Get the roles mentioned in the existing ACL */
|
|
|
|
noldmembers = aclmembers(old_acl, &oldmembers);
|
|
|
|
}
|
2005-10-10 20:49:04 +02:00
|
|
|
|
|
|
|
/* Determine ID to do the grant as, and available grant options */
|
2005-12-01 03:03:01 +01:00
|
|
|
select_best_grantor(GetUserId(), istmt->privileges,
|
2005-10-10 20:49:04 +02:00
|
|
|
old_acl, ownerId,
|
|
|
|
&grantorId, &avail_goptions);
|
2004-06-18 08:14:31 +02:00
|
|
|
|
|
|
|
/*
|
2006-10-04 02:30:14 +02:00
|
|
|
* Restrict the privileges to what we can actually grant, and emit the
|
|
|
|
* standards-mandated warning and error messages.
|
2004-06-18 08:14:31 +02:00
|
|
|
*/
|
2005-12-01 03:03:01 +01:00
|
|
|
this_privileges =
|
|
|
|
restrict_and_check_grant(istmt->is_grant, avail_goptions,
|
|
|
|
istmt->all_privs, istmt->privileges,
|
|
|
|
tblId, grantorId, ACL_KIND_TABLESPACE,
|
2009-01-22 21:16:10 +01:00
|
|
|
NameStr(pg_tablespace_tuple->spcname),
|
|
|
|
0, NULL);
|
2004-06-18 08:14:31 +02:00
|
|
|
|
|
|
|
/*
|
2005-10-10 20:49:04 +02:00
|
|
|
* Generate new ACL.
|
2005-07-07 22:40:02 +02:00
|
|
|
*/
|
2005-12-01 03:03:01 +01:00
|
|
|
new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
|
|
|
|
istmt->grant_option, istmt->behavior,
|
|
|
|
istmt->grantees, this_privileges,
|
2004-06-18 08:14:31 +02:00
|
|
|
grantorId, ownerId);
|
|
|
|
|
2010-04-05 03:09:53 +02:00
|
|
|
/*
|
|
|
|
* We need the members of both old and new ACLs so we can correct the
|
|
|
|
* shared dependency information.
|
|
|
|
*/
|
2005-07-07 22:40:02 +02:00
|
|
|
nnewmembers = aclmembers(new_acl, &newmembers);
|
|
|
|
|
2004-06-18 08:14:31 +02:00
|
|
|
/* finished building new ACL value, now insert it */
|
|
|
|
MemSet(values, 0, sizeof(values));
|
2008-11-02 02:45:28 +01:00
|
|
|
MemSet(nulls, false, sizeof(nulls));
|
|
|
|
MemSet(replaces, false, sizeof(replaces));
|
2004-06-18 08:14:31 +02:00
|
|
|
|
2008-11-02 02:45:28 +01:00
|
|
|
replaces[Anum_pg_tablespace_spcacl - 1] = true;
|
2004-06-18 08:14:31 +02:00
|
|
|
values[Anum_pg_tablespace_spcacl - 1] = PointerGetDatum(new_acl);
|
|
|
|
|
2008-11-02 02:45:28 +01:00
|
|
|
newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
|
|
|
|
nulls, replaces);
|
2004-06-18 08:14:31 +02:00
|
|
|
|
|
|
|
simple_heap_update(relation, &newtuple->t_self, newtuple);
|
|
|
|
|
|
|
|
/* keep the catalog indexes up to date */
|
|
|
|
CatalogUpdateIndexes(relation, newtuple);
|
|
|
|
|
2005-07-07 22:40:02 +02:00
|
|
|
/* Update the shared dependency ACL info */
|
2009-01-22 21:16:10 +01:00
|
|
|
updateAclDependencies(TableSpaceRelationId, tblId, 0,
|
2010-04-05 03:09:53 +02:00
|
|
|
ownerId,
|
2005-07-07 22:40:02 +02:00
|
|
|
noldmembers, oldmembers,
|
|
|
|
nnewmembers, newmembers);
|
|
|
|
|
2010-01-05 22:54:00 +01:00
|
|
|
ReleaseSysCache(tuple);
|
2005-11-21 13:49:33 +01:00
|
|
|
pfree(new_acl);
|
2005-08-12 23:20:24 +02:00
|
|
|
|
|
|
|
/* prevent error when processing duplicate objects */
|
|
|
|
CommandCounterIncrement();
|
2004-06-18 08:14:31 +02:00
|
|
|
}
|
2005-11-21 13:49:33 +01:00
|
|
|
|
|
|
|
heap_close(relation, RowExclusiveLock);
|
2004-06-18 08:14:31 +02:00
|
|
|
}
|
|
|
|
|
2011-12-19 23:05:19 +01:00
|
|
|
static void
|
|
|
|
ExecGrant_Type(InternalGrant *istmt)
|
|
|
|
{
|
|
|
|
Relation relation;
|
|
|
|
ListCell *cell;
|
|
|
|
|
|
|
|
if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
|
|
|
|
istmt->privileges = ACL_ALL_RIGHTS_TYPE;
|
|
|
|
|
|
|
|
relation = heap_open(TypeRelationId, RowExclusiveLock);
|
|
|
|
|
|
|
|
foreach(cell, istmt->objects)
|
|
|
|
{
|
|
|
|
Oid typId = lfirst_oid(cell);
|
|
|
|
Form_pg_type pg_type_tuple;
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
|
|
|
AclMode avail_goptions;
|
|
|
|
AclMode this_privileges;
|
|
|
|
Acl *old_acl;
|
|
|
|
Acl *new_acl;
|
|
|
|
Oid grantorId;
|
|
|
|
Oid ownerId;
|
|
|
|
HeapTuple newtuple;
|
|
|
|
Datum values[Natts_pg_type];
|
|
|
|
bool nulls[Natts_pg_type];
|
|
|
|
bool replaces[Natts_pg_type];
|
|
|
|
int noldmembers;
|
|
|
|
int nnewmembers;
|
|
|
|
Oid *oldmembers;
|
|
|
|
Oid *newmembers;
|
|
|
|
HeapTuple tuple;
|
|
|
|
|
|
|
|
/* Search syscache for pg_type */
|
|
|
|
tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typId));
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
elog(ERROR, "cache lookup failed for type %u", typId);
|
|
|
|
|
|
|
|
pg_type_tuple = (Form_pg_type) GETSTRUCT(tuple);
|
|
|
|
|
|
|
|
if (pg_type_tuple->typelem != 0 && pg_type_tuple->typlen == -1)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
|
|
|
|
errmsg("cannot set privileges of array types"),
|
2012-06-10 21:20:04 +02:00
|
|
|
errhint("Set the privileges of the element type instead.")));
|
2011-12-19 23:05:19 +01:00
|
|
|
|
|
|
|
/* Used GRANT DOMAIN on a non-domain? */
|
|
|
|
if (istmt->objtype == ACL_OBJECT_DOMAIN &&
|
|
|
|
pg_type_tuple->typtype != TYPTYPE_DOMAIN)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
|
|
|
errmsg("\"%s\" is not a domain",
|
|
|
|
NameStr(pg_type_tuple->typname))));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get owner ID and working copy of existing ACL. If there's no ACL,
|
|
|
|
* substitute the proper default.
|
|
|
|
*/
|
|
|
|
ownerId = pg_type_tuple->typowner;
|
|
|
|
aclDatum = heap_getattr(tuple, Anum_pg_type_typacl,
|
|
|
|
RelationGetDescr(relation), &isNull);
|
|
|
|
if (isNull)
|
|
|
|
{
|
|
|
|
old_acl = acldefault(istmt->objtype, ownerId);
|
|
|
|
/* There are no old member roles according to the catalogs */
|
|
|
|
noldmembers = 0;
|
|
|
|
oldmembers = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
old_acl = DatumGetAclPCopy(aclDatum);
|
|
|
|
/* Get the roles mentioned in the existing ACL */
|
|
|
|
noldmembers = aclmembers(old_acl, &oldmembers);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Determine ID to do the grant as, and available grant options */
|
|
|
|
select_best_grantor(GetUserId(), istmt->privileges,
|
|
|
|
old_acl, ownerId,
|
|
|
|
&grantorId, &avail_goptions);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Restrict the privileges to what we can actually grant, and emit the
|
|
|
|
* standards-mandated warning and error messages.
|
|
|
|
*/
|
|
|
|
this_privileges =
|
|
|
|
restrict_and_check_grant(istmt->is_grant, avail_goptions,
|
|
|
|
istmt->all_privs, istmt->privileges,
|
|
|
|
typId, grantorId, ACL_KIND_TYPE,
|
|
|
|
NameStr(pg_type_tuple->typname),
|
|
|
|
0, NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generate new ACL.
|
|
|
|
*/
|
|
|
|
new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
|
|
|
|
istmt->grant_option, istmt->behavior,
|
|
|
|
istmt->grantees, this_privileges,
|
|
|
|
grantorId, ownerId);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We need the members of both old and new ACLs so we can correct the
|
|
|
|
* shared dependency information.
|
|
|
|
*/
|
|
|
|
nnewmembers = aclmembers(new_acl, &newmembers);
|
|
|
|
|
|
|
|
/* finished building new ACL value, now insert it */
|
|
|
|
MemSet(values, 0, sizeof(values));
|
|
|
|
MemSet(nulls, false, sizeof(nulls));
|
|
|
|
MemSet(replaces, false, sizeof(replaces));
|
|
|
|
|
|
|
|
replaces[Anum_pg_type_typacl - 1] = true;
|
|
|
|
values[Anum_pg_type_typacl - 1] = PointerGetDatum(new_acl);
|
|
|
|
|
|
|
|
newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
|
|
|
|
nulls, replaces);
|
|
|
|
|
|
|
|
simple_heap_update(relation, &newtuple->t_self, newtuple);
|
|
|
|
|
|
|
|
/* keep the catalog indexes up to date */
|
|
|
|
CatalogUpdateIndexes(relation, newtuple);
|
|
|
|
|
2016-04-07 03:45:32 +02:00
|
|
|
/* Update initial privileges for extensions */
|
|
|
|
recordExtensionInitPriv(typId, TypeRelationId, 0, new_acl);
|
|
|
|
|
2011-12-19 23:05:19 +01:00
|
|
|
/* Update the shared dependency ACL info */
|
|
|
|
updateAclDependencies(TypeRelationId, typId, 0,
|
|
|
|
ownerId,
|
|
|
|
noldmembers, oldmembers,
|
|
|
|
nnewmembers, newmembers);
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
pfree(new_acl);
|
|
|
|
|
|
|
|
/* prevent error when processing duplicate objects */
|
|
|
|
CommandCounterIncrement();
|
|
|
|
}
|
|
|
|
|
|
|
|
heap_close(relation, RowExclusiveLock);
|
|
|
|
}
|
|
|
|
|
2002-02-19 00:11:58 +01:00
|
|
|
|
2005-06-28 21:51:26 +02:00
|
|
|
static AclMode
|
|
|
|
string_to_privilege(const char *privname)
|
|
|
|
{
|
|
|
|
if (strcmp(privname, "insert") == 0)
|
|
|
|
return ACL_INSERT;
|
|
|
|
if (strcmp(privname, "select") == 0)
|
|
|
|
return ACL_SELECT;
|
|
|
|
if (strcmp(privname, "update") == 0)
|
|
|
|
return ACL_UPDATE;
|
|
|
|
if (strcmp(privname, "delete") == 0)
|
|
|
|
return ACL_DELETE;
|
2008-09-08 02:47:41 +02:00
|
|
|
if (strcmp(privname, "truncate") == 0)
|
|
|
|
return ACL_TRUNCATE;
|
2005-06-28 21:51:26 +02:00
|
|
|
if (strcmp(privname, "references") == 0)
|
|
|
|
return ACL_REFERENCES;
|
|
|
|
if (strcmp(privname, "trigger") == 0)
|
|
|
|
return ACL_TRIGGER;
|
|
|
|
if (strcmp(privname, "execute") == 0)
|
|
|
|
return ACL_EXECUTE;
|
|
|
|
if (strcmp(privname, "usage") == 0)
|
|
|
|
return ACL_USAGE;
|
|
|
|
if (strcmp(privname, "create") == 0)
|
|
|
|
return ACL_CREATE;
|
|
|
|
if (strcmp(privname, "temporary") == 0)
|
|
|
|
return ACL_CREATE_TEMP;
|
|
|
|
if (strcmp(privname, "temp") == 0)
|
|
|
|
return ACL_CREATE_TEMP;
|
2006-04-30 23:15:33 +02:00
|
|
|
if (strcmp(privname, "connect") == 0)
|
2006-04-30 04:09:07 +02:00
|
|
|
return ACL_CONNECT;
|
2006-09-05 23:08:36 +02:00
|
|
|
if (strcmp(privname, "rule") == 0)
|
|
|
|
return 0; /* ignore old RULE privileges */
|
2005-06-28 21:51:26 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
|
|
errmsg("unrecognized privilege type \"%s\"", privname)));
|
|
|
|
return 0; /* appease compiler */
|
|
|
|
}
|
|
|
|
|
2002-04-21 02:26:44 +02:00
|
|
|
static const char *
|
|
|
|
privilege_to_string(AclMode privilege)
|
|
|
|
{
|
|
|
|
switch (privilege)
|
|
|
|
{
|
|
|
|
case ACL_INSERT:
|
|
|
|
return "INSERT";
|
|
|
|
case ACL_SELECT:
|
|
|
|
return "SELECT";
|
|
|
|
case ACL_UPDATE:
|
|
|
|
return "UPDATE";
|
|
|
|
case ACL_DELETE:
|
|
|
|
return "DELETE";
|
2008-09-08 02:47:41 +02:00
|
|
|
case ACL_TRUNCATE:
|
|
|
|
return "TRUNCATE";
|
2002-04-21 02:26:44 +02:00
|
|
|
case ACL_REFERENCES:
|
|
|
|
return "REFERENCES";
|
|
|
|
case ACL_TRIGGER:
|
|
|
|
return "TRIGGER";
|
|
|
|
case ACL_EXECUTE:
|
|
|
|
return "EXECUTE";
|
|
|
|
case ACL_USAGE:
|
|
|
|
return "USAGE";
|
|
|
|
case ACL_CREATE:
|
|
|
|
return "CREATE";
|
|
|
|
case ACL_CREATE_TEMP:
|
|
|
|
return "TEMP";
|
2006-04-30 04:09:07 +02:00
|
|
|
case ACL_CONNECT:
|
2006-04-30 23:15:33 +02:00
|
|
|
return "CONNECT";
|
2002-04-21 02:26:44 +02:00
|
|
|
default:
|
2003-07-21 03:59:11 +02:00
|
|
|
elog(ERROR, "unrecognized privilege: %d", (int) privilege);
|
2002-04-21 02:26:44 +02:00
|
|
|
}
|
|
|
|
return NULL; /* appease compiler */
|
|
|
|
}
|
|
|
|
|
2002-04-27 05:45:03 +02:00
|
|
|
/*
|
|
|
|
* Standardized reporting of aclcheck permissions failures.
|
2003-08-01 02:15:26 +02:00
|
|
|
*
|
|
|
|
* Note: we do not double-quote the %s's below, because many callers
|
|
|
|
* supply strings that might be already quoted.
|
2002-04-27 05:45:03 +02:00
|
|
|
*/
|
2003-08-01 02:15:26 +02:00
|
|
|
|
2003-08-04 02:43:34 +02:00
|
|
|
static const char *const no_priv_msg[MAX_ACL_KIND] =
|
2003-08-01 02:15:26 +02:00
|
|
|
{
|
2009-01-22 21:16:10 +01:00
|
|
|
/* ACL_KIND_COLUMN */
|
|
|
|
gettext_noop("permission denied for column %s"),
|
2003-08-01 02:15:26 +02:00
|
|
|
/* ACL_KIND_CLASS */
|
|
|
|
gettext_noop("permission denied for relation %s"),
|
2006-01-21 03:16:21 +01:00
|
|
|
/* ACL_KIND_SEQUENCE */
|
|
|
|
gettext_noop("permission denied for sequence %s"),
|
2003-08-01 02:15:26 +02:00
|
|
|
/* ACL_KIND_DATABASE */
|
|
|
|
gettext_noop("permission denied for database %s"),
|
|
|
|
/* ACL_KIND_PROC */
|
|
|
|
gettext_noop("permission denied for function %s"),
|
|
|
|
/* ACL_KIND_OPER */
|
|
|
|
gettext_noop("permission denied for operator %s"),
|
|
|
|
/* ACL_KIND_TYPE */
|
|
|
|
gettext_noop("permission denied for type %s"),
|
|
|
|
/* ACL_KIND_LANGUAGE */
|
|
|
|
gettext_noop("permission denied for language %s"),
|
2009-12-11 04:34:57 +01:00
|
|
|
/* ACL_KIND_LARGEOBJECT */
|
|
|
|
gettext_noop("permission denied for large object %s"),
|
2003-08-01 02:15:26 +02:00
|
|
|
/* ACL_KIND_NAMESPACE */
|
|
|
|
gettext_noop("permission denied for schema %s"),
|
|
|
|
/* ACL_KIND_OPCLASS */
|
|
|
|
gettext_noop("permission denied for operator class %s"),
|
2007-01-23 06:07:18 +01:00
|
|
|
/* ACL_KIND_OPFAMILY */
|
|
|
|
gettext_noop("permission denied for operator family %s"),
|
2011-02-12 14:54:13 +01:00
|
|
|
/* ACL_KIND_COLLATION */
|
|
|
|
gettext_noop("permission denied for collation %s"),
|
2003-08-01 02:15:26 +02:00
|
|
|
/* ACL_KIND_CONVERSION */
|
2004-06-18 08:14:31 +02:00
|
|
|
gettext_noop("permission denied for conversion %s"),
|
|
|
|
/* ACL_KIND_TABLESPACE */
|
2007-08-21 03:11:32 +02:00
|
|
|
gettext_noop("permission denied for tablespace %s"),
|
|
|
|
/* ACL_KIND_TSDICTIONARY */
|
|
|
|
gettext_noop("permission denied for text search dictionary %s"),
|
|
|
|
/* ACL_KIND_TSCONFIGURATION */
|
2008-12-19 17:25:19 +01:00
|
|
|
gettext_noop("permission denied for text search configuration %s"),
|
|
|
|
/* ACL_KIND_FDW */
|
|
|
|
gettext_noop("permission denied for foreign-data wrapper %s"),
|
|
|
|
/* ACL_KIND_FOREIGN_SERVER */
|
2011-01-02 05:48:11 +01:00
|
|
|
gettext_noop("permission denied for foreign server %s"),
|
2012-07-18 16:16:16 +02:00
|
|
|
/* ACL_KIND_EVENT_TRIGGER */
|
|
|
|
gettext_noop("permission denied for event trigger %s"),
|
2011-03-04 22:08:24 +01:00
|
|
|
/* ACL_KIND_EXTENSION */
|
|
|
|
gettext_noop("permission denied for extension %s"),
|
2003-08-01 02:15:26 +02:00
|
|
|
};
|
|
|
|
|
2003-08-04 02:43:34 +02:00
|
|
|
static const char *const not_owner_msg[MAX_ACL_KIND] =
|
2003-08-01 02:15:26 +02:00
|
|
|
{
|
2009-01-22 21:16:10 +01:00
|
|
|
/* ACL_KIND_COLUMN */
|
|
|
|
gettext_noop("must be owner of relation %s"),
|
2003-08-01 02:15:26 +02:00
|
|
|
/* ACL_KIND_CLASS */
|
|
|
|
gettext_noop("must be owner of relation %s"),
|
2006-01-21 03:16:21 +01:00
|
|
|
/* ACL_KIND_SEQUENCE */
|
|
|
|
gettext_noop("must be owner of sequence %s"),
|
2003-08-01 02:15:26 +02:00
|
|
|
/* ACL_KIND_DATABASE */
|
|
|
|
gettext_noop("must be owner of database %s"),
|
|
|
|
/* ACL_KIND_PROC */
|
|
|
|
gettext_noop("must be owner of function %s"),
|
|
|
|
/* ACL_KIND_OPER */
|
|
|
|
gettext_noop("must be owner of operator %s"),
|
|
|
|
/* ACL_KIND_TYPE */
|
|
|
|
gettext_noop("must be owner of type %s"),
|
|
|
|
/* ACL_KIND_LANGUAGE */
|
|
|
|
gettext_noop("must be owner of language %s"),
|
2009-12-11 04:34:57 +01:00
|
|
|
/* ACL_KIND_LARGEOBJECT */
|
|
|
|
gettext_noop("must be owner of large object %s"),
|
2003-08-01 02:15:26 +02:00
|
|
|
/* ACL_KIND_NAMESPACE */
|
|
|
|
gettext_noop("must be owner of schema %s"),
|
|
|
|
/* ACL_KIND_OPCLASS */
|
|
|
|
gettext_noop("must be owner of operator class %s"),
|
2007-01-23 06:07:18 +01:00
|
|
|
/* ACL_KIND_OPFAMILY */
|
|
|
|
gettext_noop("must be owner of operator family %s"),
|
2011-02-12 14:54:13 +01:00
|
|
|
/* ACL_KIND_COLLATION */
|
|
|
|
gettext_noop("must be owner of collation %s"),
|
2003-08-01 02:15:26 +02:00
|
|
|
/* ACL_KIND_CONVERSION */
|
2004-06-18 08:14:31 +02:00
|
|
|
gettext_noop("must be owner of conversion %s"),
|
|
|
|
/* ACL_KIND_TABLESPACE */
|
2007-08-21 03:11:32 +02:00
|
|
|
gettext_noop("must be owner of tablespace %s"),
|
|
|
|
/* ACL_KIND_TSDICTIONARY */
|
|
|
|
gettext_noop("must be owner of text search dictionary %s"),
|
|
|
|
/* ACL_KIND_TSCONFIGURATION */
|
2008-12-19 17:25:19 +01:00
|
|
|
gettext_noop("must be owner of text search configuration %s"),
|
|
|
|
/* ACL_KIND_FDW */
|
|
|
|
gettext_noop("must be owner of foreign-data wrapper %s"),
|
|
|
|
/* ACL_KIND_FOREIGN_SERVER */
|
2011-01-02 05:48:11 +01:00
|
|
|
gettext_noop("must be owner of foreign server %s"),
|
2012-07-18 16:16:16 +02:00
|
|
|
/* ACL_KIND_EVENT_TRIGGER */
|
|
|
|
gettext_noop("must be owner of event trigger %s"),
|
2011-03-04 22:08:24 +01:00
|
|
|
/* ACL_KIND_EXTENSION */
|
|
|
|
gettext_noop("must be owner of extension %s"),
|
2003-08-01 02:15:26 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2002-04-27 05:45:03 +02:00
|
|
|
void
|
2003-08-01 02:15:26 +02:00
|
|
|
aclcheck_error(AclResult aclerr, AclObjectKind objectkind,
|
|
|
|
const char *objectname)
|
2002-04-27 05:45:03 +02:00
|
|
|
{
|
2003-07-21 03:59:11 +02:00
|
|
|
switch (aclerr)
|
2002-04-27 05:45:03 +02:00
|
|
|
{
|
|
|
|
case ACLCHECK_OK:
|
|
|
|
/* no error, so return to caller */
|
|
|
|
break;
|
|
|
|
case ACLCHECK_NO_PRIV:
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
2003-08-01 02:15:26 +02:00
|
|
|
errmsg(no_priv_msg[objectkind], objectname)));
|
2002-04-27 05:45:03 +02:00
|
|
|
break;
|
|
|
|
case ACLCHECK_NOT_OWNER:
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
2003-08-01 02:15:26 +02:00
|
|
|
errmsg(not_owner_msg[objectkind], objectname)));
|
2002-04-27 05:45:03 +02:00
|
|
|
break;
|
|
|
|
default:
|
2003-07-21 03:59:11 +02:00
|
|
|
elog(ERROR, "unrecognized AclResult: %d", (int) aclerr);
|
2002-04-27 05:45:03 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-22 21:16:10 +01:00
|
|
|
void
|
|
|
|
aclcheck_error_col(AclResult aclerr, AclObjectKind objectkind,
|
|
|
|
const char *objectname, const char *colname)
|
|
|
|
{
|
|
|
|
switch (aclerr)
|
|
|
|
{
|
|
|
|
case ACLCHECK_OK:
|
|
|
|
/* no error, so return to caller */
|
|
|
|
break;
|
|
|
|
case ACLCHECK_NO_PRIV:
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
2010-07-06 21:19:02 +02:00
|
|
|
errmsg("permission denied for column \"%s\" of relation \"%s\"",
|
|
|
|
colname, objectname)));
|
2009-01-22 21:16:10 +01:00
|
|
|
break;
|
|
|
|
case ACLCHECK_NOT_OWNER:
|
|
|
|
/* relation msg is OK since columns don't have separate owners */
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
|
|
errmsg(not_owner_msg[objectkind], objectname)));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
elog(ERROR, "unrecognized AclResult: %d", (int) aclerr);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-06-15 21:55:03 +02:00
|
|
|
/*
|
|
|
|
* Special common handling for types: use element type instead of array type,
|
|
|
|
* and format nicely
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
aclcheck_error_type(AclResult aclerr, Oid typeOid)
|
|
|
|
{
|
2013-05-29 22:58:43 +02:00
|
|
|
Oid element_type = get_element_type(typeOid);
|
2012-06-15 21:55:03 +02:00
|
|
|
|
|
|
|
aclcheck_error(aclerr, ACL_KIND_TYPE, format_type_be(element_type ? element_type : typeOid));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-12-01 03:03:01 +01:00
|
|
|
/*
|
|
|
|
* Relay for the various pg_*_mask routines depending on object kind
|
|
|
|
*/
|
|
|
|
static AclMode
|
2009-01-22 21:16:10 +01:00
|
|
|
pg_aclmask(AclObjectKind objkind, Oid table_oid, AttrNumber attnum, Oid roleid,
|
2005-12-01 03:03:01 +01:00
|
|
|
AclMode mask, AclMaskHow how)
|
|
|
|
{
|
|
|
|
switch (objkind)
|
|
|
|
{
|
2009-01-22 21:16:10 +01:00
|
|
|
case ACL_KIND_COLUMN:
|
|
|
|
return
|
|
|
|
pg_class_aclmask(table_oid, roleid, mask, how) |
|
|
|
|
pg_attribute_aclmask(table_oid, attnum, roleid, mask, how);
|
2005-12-01 03:03:01 +01:00
|
|
|
case ACL_KIND_CLASS:
|
2006-01-21 03:16:21 +01:00
|
|
|
case ACL_KIND_SEQUENCE:
|
2005-12-01 03:03:01 +01:00
|
|
|
return pg_class_aclmask(table_oid, roleid, mask, how);
|
|
|
|
case ACL_KIND_DATABASE:
|
|
|
|
return pg_database_aclmask(table_oid, roleid, mask, how);
|
|
|
|
case ACL_KIND_PROC:
|
|
|
|
return pg_proc_aclmask(table_oid, roleid, mask, how);
|
|
|
|
case ACL_KIND_LANGUAGE:
|
|
|
|
return pg_language_aclmask(table_oid, roleid, mask, how);
|
2009-12-11 04:34:57 +01:00
|
|
|
case ACL_KIND_LARGEOBJECT:
|
|
|
|
return pg_largeobject_aclmask_snapshot(table_oid, roleid,
|
Use an MVCC snapshot, rather than SnapshotNow, for catalog scans.
SnapshotNow scans have the undesirable property that, in the face of
concurrent updates, the scan can fail to see either the old or the new
versions of the row. In many cases, we work around this by requiring
DDL operations to hold AccessExclusiveLock on the object being
modified; in some cases, the existing locking is inadequate and random
failures occur as a result. This commit doesn't change anything
related to locking, but will hopefully pave the way to allowing lock
strength reductions in the future.
The major issue has held us back from making this change in the past
is that taking an MVCC snapshot is significantly more expensive than
using a static special snapshot such as SnapshotNow. However, testing
of various worst-case scenarios reveals that this problem is not
severe except under fairly extreme workloads. To mitigate those
problems, we avoid retaking the MVCC snapshot for each new scan;
instead, we take a new snapshot only when invalidation messages have
been processed. The catcache machinery already requires that
invalidation messages be sent before releasing the related heavyweight
lock; else other backends might rely on locally-cached data rather
than scanning the catalog at all. Thus, making snapshot reuse
dependent on the same guarantees shouldn't break anything that wasn't
already subtly broken.
Patch by me. Review by Michael Paquier and Andres Freund.
2013-07-02 15:47:01 +02:00
|
|
|
mask, how, NULL);
|
2005-12-01 03:03:01 +01:00
|
|
|
case ACL_KIND_NAMESPACE:
|
|
|
|
return pg_namespace_aclmask(table_oid, roleid, mask, how);
|
|
|
|
case ACL_KIND_TABLESPACE:
|
|
|
|
return pg_tablespace_aclmask(table_oid, roleid, mask, how);
|
2008-12-19 17:25:19 +01:00
|
|
|
case ACL_KIND_FDW:
|
|
|
|
return pg_foreign_data_wrapper_aclmask(table_oid, roleid, mask, how);
|
|
|
|
case ACL_KIND_FOREIGN_SERVER:
|
|
|
|
return pg_foreign_server_aclmask(table_oid, roleid, mask, how);
|
2012-07-18 16:16:16 +02:00
|
|
|
case ACL_KIND_EVENT_TRIGGER:
|
|
|
|
elog(ERROR, "grantable rights not supported for event triggers");
|
|
|
|
/* not reached, but keep compiler quiet */
|
|
|
|
return ACL_NO_RIGHTS;
|
2011-12-19 23:05:19 +01:00
|
|
|
case ACL_KIND_TYPE:
|
|
|
|
return pg_type_aclmask(table_oid, roleid, mask, how);
|
2005-12-01 03:03:01 +01:00
|
|
|
default:
|
|
|
|
elog(ERROR, "unrecognized objkind: %d",
|
|
|
|
(int) objkind);
|
|
|
|
/* not reached, but keep compiler quiet */
|
|
|
|
return ACL_NO_RIGHTS;
|
|
|
|
}
|
|
|
|
}
|
2005-05-30 01:38:05 +02:00
|
|
|
|
2009-01-22 21:16:10 +01:00
|
|
|
|
|
|
|
/* ****************************************************************
|
|
|
|
* Exported routines for examining a user's privileges for various objects
|
2004-05-11 19:36:13 +02:00
|
|
|
*
|
2009-01-22 21:16:10 +01:00
|
|
|
* See aclmask() for a description of the common API for these functions.
|
2003-07-21 03:59:11 +02:00
|
|
|
*
|
|
|
|
* Note: we give lookup failure the full ereport treatment because the
|
2009-01-22 21:16:10 +01:00
|
|
|
* has_xxx_privilege() family of functions allow users to pass any random
|
|
|
|
* OID to these functions.
|
|
|
|
* ****************************************************************
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Exported routine for examining a user's privileges for a column
|
|
|
|
*
|
|
|
|
* Note: this considers only privileges granted specifically on the column.
|
|
|
|
* It is caller's responsibility to take relation-level privileges into account
|
2014-05-06 18:12:18 +02:00
|
|
|
* as appropriate. (For the same reason, we have no special case for
|
2009-01-22 21:16:10 +01:00
|
|
|
* superuser-ness here.)
|
|
|
|
*/
|
|
|
|
AclMode
|
|
|
|
pg_attribute_aclmask(Oid table_oid, AttrNumber attnum, Oid roleid,
|
|
|
|
AclMode mask, AclMaskHow how)
|
|
|
|
{
|
|
|
|
AclMode result;
|
|
|
|
HeapTuple classTuple;
|
|
|
|
HeapTuple attTuple;
|
|
|
|
Form_pg_class classForm;
|
|
|
|
Form_pg_attribute attributeForm;
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
|
|
|
Acl *acl;
|
|
|
|
Oid ownerId;
|
|
|
|
|
|
|
|
/*
|
2009-02-06 22:15:12 +01:00
|
|
|
* First, get the column's ACL from its pg_attribute entry
|
2009-01-22 21:16:10 +01:00
|
|
|
*/
|
2010-02-14 19:42:19 +01:00
|
|
|
attTuple = SearchSysCache2(ATTNUM,
|
|
|
|
ObjectIdGetDatum(table_oid),
|
|
|
|
Int16GetDatum(attnum));
|
2009-01-22 21:16:10 +01:00
|
|
|
if (!HeapTupleIsValid(attTuple))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
|
|
|
errmsg("attribute %d of relation with OID %u does not exist",
|
|
|
|
attnum, table_oid)));
|
|
|
|
attributeForm = (Form_pg_attribute) GETSTRUCT(attTuple);
|
|
|
|
|
|
|
|
/* Throw error on dropped columns, too */
|
|
|
|
if (attributeForm->attisdropped)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
|
|
|
errmsg("attribute %d of relation with OID %u does not exist",
|
|
|
|
attnum, table_oid)));
|
|
|
|
|
|
|
|
aclDatum = SysCacheGetAttr(ATTNUM, attTuple, Anum_pg_attribute_attacl,
|
|
|
|
&isNull);
|
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
/*
|
2009-06-11 16:49:15 +02:00
|
|
|
* Here we hard-wire knowledge that the default ACL for a column grants no
|
|
|
|
* privileges, so that we can fall out quickly in the very common case
|
|
|
|
* where attacl is null.
|
2009-02-06 22:15:12 +01:00
|
|
|
*/
|
2009-01-22 21:16:10 +01:00
|
|
|
if (isNull)
|
|
|
|
{
|
2009-02-06 22:15:12 +01:00
|
|
|
ReleaseSysCache(attTuple);
|
|
|
|
return 0;
|
2009-01-22 21:16:10 +01:00
|
|
|
}
|
2009-02-06 22:15:12 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Must get the relation's ownerId from pg_class. Since we already found
|
2009-06-11 16:49:15 +02:00
|
|
|
* a pg_attribute entry, the only likely reason for this to fail is that a
|
|
|
|
* concurrent DROP of the relation committed since then (which could only
|
|
|
|
* happen if we don't have lock on the relation). We prefer to report "no
|
|
|
|
* privileges" rather than failing in such a case, so as to avoid unwanted
|
|
|
|
* failures in has_column_privilege() tests.
|
2009-02-06 22:15:12 +01:00
|
|
|
*/
|
2010-02-14 19:42:19 +01:00
|
|
|
classTuple = SearchSysCache1(RELOID, ObjectIdGetDatum(table_oid));
|
2009-02-06 22:15:12 +01:00
|
|
|
if (!HeapTupleIsValid(classTuple))
|
2009-01-22 21:16:10 +01:00
|
|
|
{
|
2009-02-06 22:15:12 +01:00
|
|
|
ReleaseSysCache(attTuple);
|
|
|
|
return 0;
|
2009-01-22 21:16:10 +01:00
|
|
|
}
|
2009-02-06 22:15:12 +01:00
|
|
|
classForm = (Form_pg_class) GETSTRUCT(classTuple);
|
|
|
|
|
|
|
|
ownerId = classForm->relowner;
|
|
|
|
|
|
|
|
ReleaseSysCache(classTuple);
|
|
|
|
|
|
|
|
/* detoast column's ACL if necessary */
|
|
|
|
acl = DatumGetAclP(aclDatum);
|
2009-01-22 21:16:10 +01:00
|
|
|
|
|
|
|
result = aclmask(acl, roleid, ownerId, mask, how);
|
|
|
|
|
|
|
|
/* if we have a detoasted copy, free it */
|
|
|
|
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
|
|
|
|
pfree(acl);
|
|
|
|
|
|
|
|
ReleaseSysCache(attTuple);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Exported routine for examining a user's privileges for a table
|
2001-06-05 21:34:56 +02:00
|
|
|
*/
|
2004-05-11 19:36:13 +02:00
|
|
|
AclMode
|
2005-06-28 07:09:14 +02:00
|
|
|
pg_class_aclmask(Oid table_oid, Oid roleid,
|
2004-05-11 19:36:13 +02:00
|
|
|
AclMode mask, AclMaskHow how)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2004-05-11 19:36:13 +02:00
|
|
|
AclMode result;
|
2000-10-02 06:49:28 +02:00
|
|
|
HeapTuple tuple;
|
2004-01-14 04:44:53 +01:00
|
|
|
Form_pg_class classForm;
|
2000-10-02 06:49:28 +02:00
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
|
|
|
Acl *acl;
|
2005-10-15 04:49:52 +02:00
|
|
|
Oid ownerId;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-06-05 21:34:56 +02:00
|
|
|
/*
|
2005-05-30 01:38:05 +02:00
|
|
|
* Must get the relation's tuple from pg_class
|
2002-03-22 00:27:25 +01:00
|
|
|
*/
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(table_oid));
|
2002-03-22 00:27:25 +01:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_TABLE),
|
2004-05-11 19:36:13 +02:00
|
|
|
errmsg("relation with OID %u does not exist",
|
|
|
|
table_oid)));
|
2004-01-14 04:44:53 +01:00
|
|
|
classForm = (Form_pg_class) GETSTRUCT(tuple);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Deny anyone permission to update a system catalog unless
|
2015-03-07 05:42:38 +01:00
|
|
|
* pg_authid.rolsuper is set. Also allow it if allowSystemTableMods.
|
2004-01-14 04:44:53 +01:00
|
|
|
*
|
2004-08-29 07:07:03 +02:00
|
|
|
* As of 7.4 we have some updatable system views; those shouldn't be
|
|
|
|
* protected in this way. Assume the view rules can take care of
|
2014-05-06 18:12:18 +02:00
|
|
|
* themselves. ACL_USAGE is if we ever have system sequences.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2008-09-08 02:47:41 +02:00
|
|
|
if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE)) &&
|
Refine our definition of what constitutes a system relation.
Although user-defined relations can't be directly created in
pg_catalog, it's possible for them to end up there, because you can
create them in some other schema and then use ALTER TABLE .. SET SCHEMA
to move them there. Previously, such relations couldn't afterwards
be manipulated, because IsSystemRelation()/IsSystemClass() rejected
all attempts to modify objects in the pg_catalog schema, regardless
of their origin. With this patch, they now reject only those
objects in pg_catalog which were created at initdb-time, allowing
most operations on user-created tables in pg_catalog to proceed
normally.
This patch also adds new functions IsCatalogRelation() and
IsCatalogClass(), which is similar to IsSystemRelation() and
IsSystemClass() but with a slightly narrower definition: only TOAST
tables of system catalogs are included, rather than *all* TOAST tables.
This is currently used only for making decisions about when
invalidation messages need to be sent, but upcoming logical decoding
patches will find other uses for this information.
Andres Freund, with some modifications by me.
2013-11-29 02:57:20 +01:00
|
|
|
IsSystemClass(table_oid, classForm) &&
|
2004-01-14 04:44:53 +01:00
|
|
|
classForm->relkind != RELKIND_VIEW &&
|
2015-03-07 05:42:38 +01:00
|
|
|
!superuser_arg(roleid) &&
|
2004-01-14 04:44:53 +01:00
|
|
|
!allowSystemTableMods)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2001-06-05 21:34:56 +02:00
|
|
|
#ifdef ACLDEBUG
|
2003-07-21 03:59:11 +02:00
|
|
|
elog(DEBUG2, "permission denied for system catalog update");
|
2001-06-05 21:34:56 +02:00
|
|
|
#endif
|
2008-09-08 02:47:41 +02:00
|
|
|
mask &= ~(ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Otherwise, superusers bypass all permission-checking.
|
|
|
|
*/
|
2005-06-28 07:09:14 +02:00
|
|
|
if (superuser_arg(roleid))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2001-05-27 11:59:30 +02:00
|
|
|
#ifdef ACLDEBUG
|
2005-06-28 07:09:14 +02:00
|
|
|
elog(DEBUG2, "OID %u is superuser, home free", roleid);
|
1996-07-09 08:22:35 +02:00
|
|
|
#endif
|
2000-11-16 23:30:52 +01:00
|
|
|
ReleaseSysCache(tuple);
|
2004-05-11 19:36:13 +02:00
|
|
|
return mask;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2000-10-02 06:49:28 +02:00
|
|
|
/*
|
|
|
|
* Normal case: get the relation's ACL from pg_class
|
|
|
|
*/
|
2004-06-01 23:49:23 +02:00
|
|
|
ownerId = classForm->relowner;
|
|
|
|
|
2002-03-22 00:27:25 +01:00
|
|
|
aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl,
|
2000-10-02 06:49:28 +02:00
|
|
|
&isNull);
|
|
|
|
if (isNull)
|
|
|
|
{
|
2003-10-31 21:00:49 +01:00
|
|
|
/* No ACL, so build default ACL */
|
2011-01-02 05:48:11 +01:00
|
|
|
switch (classForm->relkind)
|
|
|
|
{
|
|
|
|
case RELKIND_SEQUENCE:
|
|
|
|
acl = acldefault(ACL_OBJECT_SEQUENCE, ownerId);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
acl = acldefault(ACL_OBJECT_RELATION, ownerId);
|
|
|
|
break;
|
|
|
|
}
|
2001-06-05 21:34:56 +02:00
|
|
|
aclDatum = (Datum) 0;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2000-10-02 06:49:28 +02:00
|
|
|
else
|
1999-09-18 21:08:25 +02:00
|
|
|
{
|
2001-06-05 21:34:56 +02:00
|
|
|
/* detoast rel's ACL if necessary */
|
|
|
|
acl = DatumGetAclP(aclDatum);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
2000-10-02 06:49:28 +02:00
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
result = aclmask(acl, roleid, ownerId, mask, how);
|
2000-11-16 23:30:52 +01:00
|
|
|
|
2001-06-05 21:34:56 +02:00
|
|
|
/* if we have a detoasted copy, free it */
|
|
|
|
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
|
1997-09-07 07:04:48 +02:00
|
|
|
pfree(acl);
|
2001-06-05 21:34:56 +02:00
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return result;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2002-04-21 02:26:44 +02:00
|
|
|
/*
|
2004-05-11 19:36:13 +02:00
|
|
|
* Exported routine for examining a user's privileges for a database
|
2002-04-21 02:26:44 +02:00
|
|
|
*/
|
2004-05-11 19:36:13 +02:00
|
|
|
AclMode
|
2005-06-28 07:09:14 +02:00
|
|
|
pg_database_aclmask(Oid db_oid, Oid roleid,
|
2004-05-11 19:36:13 +02:00
|
|
|
AclMode mask, AclMaskHow how)
|
2002-04-21 02:26:44 +02:00
|
|
|
{
|
2004-05-11 19:36:13 +02:00
|
|
|
AclMode result;
|
2002-04-21 02:26:44 +02:00
|
|
|
HeapTuple tuple;
|
2006-05-04 00:45:26 +02:00
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
|
|
|
Acl *acl;
|
|
|
|
Oid ownerId;
|
2002-04-21 02:26:44 +02:00
|
|
|
|
|
|
|
/* Superusers bypass all permission checking. */
|
2005-06-28 07:09:14 +02:00
|
|
|
if (superuser_arg(roleid))
|
2004-05-11 19:36:13 +02:00
|
|
|
return mask;
|
2002-04-21 02:26:44 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the database's ACL from pg_database
|
|
|
|
*/
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(db_oid));
|
2002-04-21 02:26:44 +02:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_DATABASE),
|
|
|
|
errmsg("database with OID %u does not exist", db_oid)));
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2006-05-04 00:45:26 +02:00
|
|
|
ownerId = ((Form_pg_database) GETSTRUCT(tuple))->datdba;
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2006-05-04 00:45:26 +02:00
|
|
|
aclDatum = SysCacheGetAttr(DATABASEOID, tuple, Anum_pg_database_datacl,
|
|
|
|
&isNull);
|
2002-04-21 02:26:44 +02:00
|
|
|
if (isNull)
|
|
|
|
{
|
|
|
|
/* No ACL, so build default ACL */
|
|
|
|
acl = acldefault(ACL_OBJECT_DATABASE, ownerId);
|
|
|
|
aclDatum = (Datum) 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* detoast ACL if necessary */
|
|
|
|
acl = DatumGetAclP(aclDatum);
|
|
|
|
}
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
result = aclmask(acl, roleid, ownerId, mask, how);
|
2002-04-21 02:26:44 +02:00
|
|
|
|
|
|
|
/* if we have a detoasted copy, free it */
|
|
|
|
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
|
|
|
|
pfree(acl);
|
|
|
|
|
2006-05-04 00:45:26 +02:00
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
2002-04-21 02:26:44 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2002-02-19 00:11:58 +01:00
|
|
|
/*
|
2004-05-11 19:36:13 +02:00
|
|
|
* Exported routine for examining a user's privileges for a function
|
2002-02-19 00:11:58 +01:00
|
|
|
*/
|
2004-05-11 19:36:13 +02:00
|
|
|
AclMode
|
2005-06-28 07:09:14 +02:00
|
|
|
pg_proc_aclmask(Oid proc_oid, Oid roleid,
|
2004-05-11 19:36:13 +02:00
|
|
|
AclMode mask, AclMaskHow how)
|
2002-02-19 00:11:58 +01:00
|
|
|
{
|
2004-05-11 19:36:13 +02:00
|
|
|
AclMode result;
|
2002-02-19 00:11:58 +01:00
|
|
|
HeapTuple tuple;
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
|
|
|
Acl *acl;
|
2005-10-15 04:49:52 +02:00
|
|
|
Oid ownerId;
|
2002-02-19 00:11:58 +01:00
|
|
|
|
2002-03-22 00:27:25 +01:00
|
|
|
/* Superusers bypass all permission checking. */
|
2005-06-28 07:09:14 +02:00
|
|
|
if (superuser_arg(roleid))
|
2004-05-11 19:36:13 +02:00
|
|
|
return mask;
|
2002-02-19 00:11:58 +01:00
|
|
|
|
|
|
|
/*
|
2002-03-22 00:27:25 +01:00
|
|
|
* Get the function's ACL from pg_proc
|
2002-02-19 00:11:58 +01:00
|
|
|
*/
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(proc_oid));
|
2002-02-19 00:11:58 +01:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_FUNCTION),
|
2005-10-15 04:49:52 +02:00
|
|
|
errmsg("function with OID %u does not exist", proc_oid)));
|
2002-02-19 00:11:58 +01:00
|
|
|
|
2004-06-01 23:49:23 +02:00
|
|
|
ownerId = ((Form_pg_proc) GETSTRUCT(tuple))->proowner;
|
|
|
|
|
2002-02-19 00:11:58 +01:00
|
|
|
aclDatum = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_proacl,
|
|
|
|
&isNull);
|
|
|
|
if (isNull)
|
|
|
|
{
|
|
|
|
/* No ACL, so build default ACL */
|
2002-04-21 02:26:44 +02:00
|
|
|
acl = acldefault(ACL_OBJECT_FUNCTION, ownerId);
|
2002-02-19 00:11:58 +01:00
|
|
|
aclDatum = (Datum) 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* detoast ACL if necessary */
|
|
|
|
acl = DatumGetAclP(aclDatum);
|
|
|
|
}
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
result = aclmask(acl, roleid, ownerId, mask, how);
|
2002-02-19 00:11:58 +01:00
|
|
|
|
|
|
|
/* if we have a detoasted copy, free it */
|
|
|
|
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
|
|
|
|
pfree(acl);
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2004-05-11 19:36:13 +02:00
|
|
|
* Exported routine for examining a user's privileges for a language
|
2002-02-19 00:11:58 +01:00
|
|
|
*/
|
2004-05-11 19:36:13 +02:00
|
|
|
AclMode
|
2005-06-28 07:09:14 +02:00
|
|
|
pg_language_aclmask(Oid lang_oid, Oid roleid,
|
2004-05-11 19:36:13 +02:00
|
|
|
AclMode mask, AclMaskHow how)
|
2002-02-19 00:11:58 +01:00
|
|
|
{
|
2004-05-11 19:36:13 +02:00
|
|
|
AclMode result;
|
2002-02-19 00:11:58 +01:00
|
|
|
HeapTuple tuple;
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
|
|
|
Acl *acl;
|
2005-10-15 04:49:52 +02:00
|
|
|
Oid ownerId;
|
2002-02-19 00:11:58 +01:00
|
|
|
|
2002-03-22 00:27:25 +01:00
|
|
|
/* Superusers bypass all permission checking. */
|
2005-06-28 07:09:14 +02:00
|
|
|
if (superuser_arg(roleid))
|
2004-05-11 19:36:13 +02:00
|
|
|
return mask;
|
2002-02-19 00:11:58 +01:00
|
|
|
|
|
|
|
/*
|
2003-07-21 03:59:11 +02:00
|
|
|
* Get the language's ACL from pg_language
|
2002-02-19 00:11:58 +01:00
|
|
|
*/
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(LANGOID, ObjectIdGetDatum(lang_oid));
|
2002-02-19 00:11:58 +01:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
2005-10-15 04:49:52 +02:00
|
|
|
errmsg("language with OID %u does not exist", lang_oid)));
|
2002-02-19 00:11:58 +01:00
|
|
|
|
2007-03-26 18:58:41 +02:00
|
|
|
ownerId = ((Form_pg_language) GETSTRUCT(tuple))->lanowner;
|
2004-06-01 23:49:23 +02:00
|
|
|
|
2002-02-19 00:11:58 +01:00
|
|
|
aclDatum = SysCacheGetAttr(LANGOID, tuple, Anum_pg_language_lanacl,
|
|
|
|
&isNull);
|
|
|
|
if (isNull)
|
|
|
|
{
|
|
|
|
/* No ACL, so build default ACL */
|
2004-06-01 23:49:23 +02:00
|
|
|
acl = acldefault(ACL_OBJECT_LANGUAGE, ownerId);
|
2002-02-19 00:11:58 +01:00
|
|
|
aclDatum = (Datum) 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* detoast ACL if necessary */
|
|
|
|
acl = DatumGetAclP(aclDatum);
|
|
|
|
}
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
result = aclmask(acl, roleid, ownerId, mask, how);
|
2002-04-21 02:26:44 +02:00
|
|
|
|
|
|
|
/* if we have a detoasted copy, free it */
|
|
|
|
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
|
|
|
|
pfree(acl);
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2009-12-11 04:34:57 +01:00
|
|
|
/*
|
|
|
|
* Exported routine for examining a user's privileges for a largeobject
|
|
|
|
*
|
2010-01-07 03:41:16 +01:00
|
|
|
* When a large object is opened for reading, it is opened relative to the
|
Use an MVCC snapshot, rather than SnapshotNow, for catalog scans.
SnapshotNow scans have the undesirable property that, in the face of
concurrent updates, the scan can fail to see either the old or the new
versions of the row. In many cases, we work around this by requiring
DDL operations to hold AccessExclusiveLock on the object being
modified; in some cases, the existing locking is inadequate and random
failures occur as a result. This commit doesn't change anything
related to locking, but will hopefully pave the way to allowing lock
strength reductions in the future.
The major issue has held us back from making this change in the past
is that taking an MVCC snapshot is significantly more expensive than
using a static special snapshot such as SnapshotNow. However, testing
of various worst-case scenarios reveals that this problem is not
severe except under fairly extreme workloads. To mitigate those
problems, we avoid retaking the MVCC snapshot for each new scan;
instead, we take a new snapshot only when invalidation messages have
been processed. The catcache machinery already requires that
invalidation messages be sent before releasing the related heavyweight
lock; else other backends might rely on locally-cached data rather
than scanning the catalog at all. Thus, making snapshot reuse
dependent on the same guarantees shouldn't break anything that wasn't
already subtly broken.
Patch by me. Review by Michael Paquier and Andres Freund.
2013-07-02 15:47:01 +02:00
|
|
|
* caller's snapshot, but when it is opened for writing, a current
|
|
|
|
* MVCC snapshot will be used. See doc/src/sgml/lobj.sgml. This function
|
|
|
|
* takes a snapshot argument so that the permissions check can be made
|
|
|
|
* relative to the same snapshot that will be used to read the underlying
|
|
|
|
* data. The caller will actually pass NULL for an instantaneous MVCC
|
|
|
|
* snapshot, since all we do with the snapshot argument is pass it through
|
2013-11-10 15:20:52 +01:00
|
|
|
* to systable_beginscan().
|
2009-12-11 04:34:57 +01:00
|
|
|
*/
|
|
|
|
AclMode
|
|
|
|
pg_largeobject_aclmask_snapshot(Oid lobj_oid, Oid roleid,
|
|
|
|
AclMode mask, AclMaskHow how,
|
|
|
|
Snapshot snapshot)
|
|
|
|
{
|
|
|
|
AclMode result;
|
|
|
|
Relation pg_lo_meta;
|
2010-02-26 03:01:40 +01:00
|
|
|
ScanKeyData entry[1];
|
|
|
|
SysScanDesc scan;
|
2009-12-11 04:34:57 +01:00
|
|
|
HeapTuple tuple;
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
|
|
|
Acl *acl;
|
|
|
|
Oid ownerId;
|
|
|
|
|
|
|
|
/* Superusers bypass all permission checking. */
|
|
|
|
if (superuser_arg(roleid))
|
|
|
|
return mask;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the largeobject's ACL from pg_language_metadata
|
|
|
|
*/
|
|
|
|
pg_lo_meta = heap_open(LargeObjectMetadataRelationId,
|
|
|
|
AccessShareLock);
|
|
|
|
|
|
|
|
ScanKeyInit(&entry[0],
|
|
|
|
ObjectIdAttributeNumber,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(lobj_oid));
|
|
|
|
|
|
|
|
scan = systable_beginscan(pg_lo_meta,
|
|
|
|
LargeObjectMetadataOidIndexId, true,
|
|
|
|
snapshot, 1, entry);
|
|
|
|
|
|
|
|
tuple = systable_getnext(scan);
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("large object %u does not exist", lobj_oid)));
|
|
|
|
|
|
|
|
ownerId = ((Form_pg_largeobject_metadata) GETSTRUCT(tuple))->lomowner;
|
|
|
|
|
|
|
|
aclDatum = heap_getattr(tuple, Anum_pg_largeobject_metadata_lomacl,
|
|
|
|
RelationGetDescr(pg_lo_meta), &isNull);
|
|
|
|
|
|
|
|
if (isNull)
|
|
|
|
{
|
|
|
|
/* No ACL, so build default ACL */
|
|
|
|
acl = acldefault(ACL_OBJECT_LARGEOBJECT, ownerId);
|
|
|
|
aclDatum = (Datum) 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* detoast ACL if necessary */
|
|
|
|
acl = DatumGetAclP(aclDatum);
|
|
|
|
}
|
|
|
|
|
|
|
|
result = aclmask(acl, roleid, ownerId, mask, how);
|
|
|
|
|
|
|
|
/* if we have a detoasted copy, free it */
|
|
|
|
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
|
|
|
|
pfree(acl);
|
|
|
|
|
|
|
|
systable_endscan(scan);
|
|
|
|
|
|
|
|
heap_close(pg_lo_meta, AccessShareLock);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2002-04-21 02:26:44 +02:00
|
|
|
/*
|
2004-05-11 19:36:13 +02:00
|
|
|
* Exported routine for examining a user's privileges for a namespace
|
2002-04-21 02:26:44 +02:00
|
|
|
*/
|
2004-05-11 19:36:13 +02:00
|
|
|
AclMode
|
2005-06-28 07:09:14 +02:00
|
|
|
pg_namespace_aclmask(Oid nsp_oid, Oid roleid,
|
2004-05-11 19:36:13 +02:00
|
|
|
AclMode mask, AclMaskHow how)
|
2002-04-21 02:26:44 +02:00
|
|
|
{
|
2004-05-11 19:36:13 +02:00
|
|
|
AclMode result;
|
2002-04-21 02:26:44 +02:00
|
|
|
HeapTuple tuple;
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
|
|
|
Acl *acl;
|
2005-10-15 04:49:52 +02:00
|
|
|
Oid ownerId;
|
2002-04-21 02:26:44 +02:00
|
|
|
|
|
|
|
/* Superusers bypass all permission checking. */
|
2005-06-28 07:09:14 +02:00
|
|
|
if (superuser_arg(roleid))
|
2004-05-11 19:36:13 +02:00
|
|
|
return mask;
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2004-05-26 20:35:51 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* If we have been assigned this namespace as a temp namespace, check to
|
|
|
|
* make sure we have CREATE TEMP permission on the database, and if so act
|
|
|
|
* as though we have all standard (but not GRANT OPTION) permissions on
|
|
|
|
* the namespace. If we don't have CREATE TEMP, act as though we have
|
|
|
|
* only USAGE (and not CREATE) rights.
|
2004-05-26 20:35:51 +02:00
|
|
|
*
|
2005-11-22 19:17:34 +01:00
|
|
|
* This may seem redundant given the check in InitTempTableNamespace, but
|
|
|
|
* it really isn't since current user ID may have changed since then. The
|
2005-10-15 04:49:52 +02:00
|
|
|
* upshot of this behavior is that a SECURITY DEFINER function can create
|
|
|
|
* temp tables that can then be accessed (if permission is granted) by
|
|
|
|
* code in the same session that doesn't have permissions to create temp
|
|
|
|
* tables.
|
2004-05-28 18:17:14 +02:00
|
|
|
*
|
|
|
|
* XXX Would it be safe to ereport a special error message as
|
|
|
|
* InitTempTableNamespace does? Returning zero here means we'll get a
|
2005-10-15 04:49:52 +02:00
|
|
|
* generic "permission denied for schema pg_temp_N" message, which is not
|
|
|
|
* remarkably user-friendly.
|
2004-05-26 20:35:51 +02:00
|
|
|
*/
|
2004-05-28 18:17:14 +02:00
|
|
|
if (isTempNamespace(nsp_oid))
|
|
|
|
{
|
2007-04-20 04:37:38 +02:00
|
|
|
if (pg_database_aclcheck(MyDatabaseId, roleid,
|
2004-05-28 18:17:14 +02:00
|
|
|
ACL_CREATE_TEMP) == ACLCHECK_OK)
|
|
|
|
return mask & ACL_ALL_RIGHTS_NAMESPACE;
|
|
|
|
else
|
|
|
|
return mask & ACL_USAGE;
|
2004-05-26 20:35:51 +02:00
|
|
|
}
|
|
|
|
|
2002-02-19 00:11:58 +01:00
|
|
|
/*
|
2003-07-21 03:59:11 +02:00
|
|
|
* Get the schema's ACL from pg_namespace
|
2002-02-19 00:11:58 +01:00
|
|
|
*/
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(nsp_oid));
|
2002-04-21 02:26:44 +02:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_SCHEMA),
|
|
|
|
errmsg("schema with OID %u does not exist", nsp_oid)));
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2004-06-01 23:49:23 +02:00
|
|
|
ownerId = ((Form_pg_namespace) GETSTRUCT(tuple))->nspowner;
|
|
|
|
|
2002-04-21 02:26:44 +02:00
|
|
|
aclDatum = SysCacheGetAttr(NAMESPACEOID, tuple, Anum_pg_namespace_nspacl,
|
|
|
|
&isNull);
|
|
|
|
if (isNull)
|
|
|
|
{
|
|
|
|
/* No ACL, so build default ACL */
|
|
|
|
acl = acldefault(ACL_OBJECT_NAMESPACE, ownerId);
|
|
|
|
aclDatum = (Datum) 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* detoast ACL if necessary */
|
|
|
|
acl = DatumGetAclP(aclDatum);
|
|
|
|
}
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
result = aclmask(acl, roleid, ownerId, mask, how);
|
2002-02-19 00:11:58 +01:00
|
|
|
|
|
|
|
/* if we have a detoasted copy, free it */
|
|
|
|
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
|
|
|
|
pfree(acl);
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2002-03-22 00:27:25 +01:00
|
|
|
|
2004-06-18 08:14:31 +02:00
|
|
|
/*
|
|
|
|
* Exported routine for examining a user's privileges for a tablespace
|
|
|
|
*/
|
|
|
|
AclMode
|
2005-06-28 07:09:14 +02:00
|
|
|
pg_tablespace_aclmask(Oid spc_oid, Oid roleid,
|
2004-06-18 08:14:31 +02:00
|
|
|
AclMode mask, AclMaskHow how)
|
|
|
|
{
|
|
|
|
AclMode result;
|
|
|
|
HeapTuple tuple;
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
|
|
|
Acl *acl;
|
2005-10-15 04:49:52 +02:00
|
|
|
Oid ownerId;
|
2004-06-18 08:14:31 +02:00
|
|
|
|
2007-10-12 20:55:12 +02:00
|
|
|
/* Superusers bypass all permission checking. */
|
2005-06-28 07:09:14 +02:00
|
|
|
if (superuser_arg(roleid))
|
2004-06-18 08:14:31 +02:00
|
|
|
return mask;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the tablespace's ACL from pg_tablespace
|
|
|
|
*/
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(TABLESPACEOID, ObjectIdGetDatum(spc_oid));
|
2004-06-18 08:14:31 +02:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
2005-10-15 04:49:52 +02:00
|
|
|
errmsg("tablespace with OID %u does not exist", spc_oid)));
|
2004-06-18 08:14:31 +02:00
|
|
|
|
|
|
|
ownerId = ((Form_pg_tablespace) GETSTRUCT(tuple))->spcowner;
|
|
|
|
|
2010-01-05 22:54:00 +01:00
|
|
|
aclDatum = SysCacheGetAttr(TABLESPACEOID, tuple,
|
2010-02-26 03:01:40 +01:00
|
|
|
Anum_pg_tablespace_spcacl,
|
|
|
|
&isNull);
|
2004-06-18 08:14:31 +02:00
|
|
|
|
|
|
|
if (isNull)
|
|
|
|
{
|
|
|
|
/* No ACL, so build default ACL */
|
|
|
|
acl = acldefault(ACL_OBJECT_TABLESPACE, ownerId);
|
|
|
|
aclDatum = (Datum) 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* detoast ACL if necessary */
|
|
|
|
acl = DatumGetAclP(aclDatum);
|
|
|
|
}
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
result = aclmask(acl, roleid, ownerId, mask, how);
|
2004-06-18 08:14:31 +02:00
|
|
|
|
|
|
|
/* if we have a detoasted copy, free it */
|
|
|
|
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
|
|
|
|
pfree(acl);
|
|
|
|
|
2010-01-05 22:54:00 +01:00
|
|
|
ReleaseSysCache(tuple);
|
2004-06-18 08:14:31 +02:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2008-12-19 17:25:19 +01:00
|
|
|
/*
|
|
|
|
* Exported routine for examining a user's privileges for a foreign
|
|
|
|
* data wrapper
|
|
|
|
*/
|
|
|
|
AclMode
|
|
|
|
pg_foreign_data_wrapper_aclmask(Oid fdw_oid, Oid roleid,
|
|
|
|
AclMode mask, AclMaskHow how)
|
|
|
|
{
|
|
|
|
AclMode result;
|
|
|
|
HeapTuple tuple;
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
|
|
|
Acl *acl;
|
|
|
|
Oid ownerId;
|
|
|
|
|
|
|
|
Form_pg_foreign_data_wrapper fdwForm;
|
|
|
|
|
|
|
|
/* Bypass permission checks for superusers */
|
|
|
|
if (superuser_arg(roleid))
|
|
|
|
return mask;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Must get the FDW's tuple from pg_foreign_data_wrapper
|
|
|
|
*/
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdw_oid));
|
2008-12-19 17:25:19 +01:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
ereport(ERROR,
|
2016-06-13 19:53:10 +02:00
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("foreign-data wrapper with OID %u does not exist",
|
2008-12-19 17:25:19 +01:00
|
|
|
fdw_oid)));
|
|
|
|
fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tuple);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Normal case: get the FDW's ACL from pg_foreign_data_wrapper
|
|
|
|
*/
|
|
|
|
ownerId = fdwForm->fdwowner;
|
|
|
|
|
|
|
|
aclDatum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID, tuple,
|
|
|
|
Anum_pg_foreign_data_wrapper_fdwacl, &isNull);
|
|
|
|
if (isNull)
|
|
|
|
{
|
|
|
|
/* No ACL, so build default ACL */
|
|
|
|
acl = acldefault(ACL_OBJECT_FDW, ownerId);
|
|
|
|
aclDatum = (Datum) 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* detoast rel's ACL if necessary */
|
|
|
|
acl = DatumGetAclP(aclDatum);
|
|
|
|
}
|
|
|
|
|
|
|
|
result = aclmask(acl, roleid, ownerId, mask, how);
|
|
|
|
|
|
|
|
/* if we have a detoasted copy, free it */
|
|
|
|
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
|
|
|
|
pfree(acl);
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Exported routine for examining a user's privileges for a foreign
|
|
|
|
* server.
|
|
|
|
*/
|
|
|
|
AclMode
|
|
|
|
pg_foreign_server_aclmask(Oid srv_oid, Oid roleid,
|
2009-06-11 16:49:15 +02:00
|
|
|
AclMode mask, AclMaskHow how)
|
2008-12-19 17:25:19 +01:00
|
|
|
{
|
|
|
|
AclMode result;
|
|
|
|
HeapTuple tuple;
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
|
|
|
Acl *acl;
|
|
|
|
Oid ownerId;
|
|
|
|
|
|
|
|
Form_pg_foreign_server srvForm;
|
|
|
|
|
|
|
|
/* Bypass permission checks for superusers */
|
|
|
|
if (superuser_arg(roleid))
|
|
|
|
return mask;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Must get the FDW's tuple from pg_foreign_data_wrapper
|
|
|
|
*/
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(srv_oid));
|
2008-12-19 17:25:19 +01:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
ereport(ERROR,
|
2016-06-13 19:53:10 +02:00
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("foreign server with OID %u does not exist",
|
2008-12-19 17:25:19 +01:00
|
|
|
srv_oid)));
|
|
|
|
srvForm = (Form_pg_foreign_server) GETSTRUCT(tuple);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Normal case: get the foreign server's ACL from pg_foreign_server
|
|
|
|
*/
|
|
|
|
ownerId = srvForm->srvowner;
|
|
|
|
|
|
|
|
aclDatum = SysCacheGetAttr(FOREIGNSERVEROID, tuple,
|
|
|
|
Anum_pg_foreign_server_srvacl, &isNull);
|
|
|
|
if (isNull)
|
|
|
|
{
|
|
|
|
/* No ACL, so build default ACL */
|
|
|
|
acl = acldefault(ACL_OBJECT_FOREIGN_SERVER, ownerId);
|
|
|
|
aclDatum = (Datum) 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* detoast rel's ACL if necessary */
|
|
|
|
acl = DatumGetAclP(aclDatum);
|
|
|
|
}
|
|
|
|
|
|
|
|
result = aclmask(acl, roleid, ownerId, mask, how);
|
|
|
|
|
|
|
|
/* if we have a detoasted copy, free it */
|
|
|
|
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
|
|
|
|
pfree(acl);
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2002-03-22 00:27:25 +01:00
|
|
|
|
2011-12-19 23:05:19 +01:00
|
|
|
/*
|
|
|
|
* Exported routine for examining a user's privileges for a type.
|
|
|
|
*/
|
|
|
|
AclMode
|
|
|
|
pg_type_aclmask(Oid type_oid, Oid roleid, AclMode mask, AclMaskHow how)
|
|
|
|
{
|
|
|
|
AclMode result;
|
|
|
|
HeapTuple tuple;
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
|
|
|
Acl *acl;
|
|
|
|
Oid ownerId;
|
|
|
|
|
|
|
|
Form_pg_type typeForm;
|
|
|
|
|
|
|
|
/* Bypass permission checks for superusers */
|
|
|
|
if (superuser_arg(roleid))
|
|
|
|
return mask;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Must get the type's tuple from pg_type
|
|
|
|
*/
|
|
|
|
tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_oid));
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
ereport(ERROR,
|
2016-06-13 19:53:10 +02:00
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("type with OID %u does not exist",
|
2011-12-19 23:05:19 +01:00
|
|
|
type_oid)));
|
|
|
|
typeForm = (Form_pg_type) GETSTRUCT(tuple);
|
|
|
|
|
2016-06-13 19:53:10 +02:00
|
|
|
/*
|
|
|
|
* "True" array types don't manage permissions of their own; consult the
|
|
|
|
* element type instead.
|
|
|
|
*/
|
|
|
|
if (OidIsValid(typeForm->typelem) && typeForm->typlen == -1)
|
2011-12-19 23:05:19 +01:00
|
|
|
{
|
2012-06-10 21:20:04 +02:00
|
|
|
Oid elttype_oid = typeForm->typelem;
|
2011-12-19 23:05:19 +01:00
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
|
|
|
tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(elttype_oid));
|
2016-06-13 19:53:10 +02:00
|
|
|
/* this case is not a user-facing error, so elog not ereport */
|
2011-12-19 23:05:19 +01:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2016-06-13 19:53:10 +02:00
|
|
|
elog(ERROR, "cache lookup failed for type %u", elttype_oid);
|
2011-12-19 23:05:19 +01:00
|
|
|
typeForm = (Form_pg_type) GETSTRUCT(tuple);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2016-06-13 19:53:10 +02:00
|
|
|
* Now get the type's owner and ACL from the tuple
|
2011-12-19 23:05:19 +01:00
|
|
|
*/
|
|
|
|
ownerId = typeForm->typowner;
|
|
|
|
|
|
|
|
aclDatum = SysCacheGetAttr(TYPEOID, tuple,
|
|
|
|
Anum_pg_type_typacl, &isNull);
|
|
|
|
if (isNull)
|
|
|
|
{
|
|
|
|
/* No ACL, so build default ACL */
|
|
|
|
acl = acldefault(ACL_OBJECT_TYPE, ownerId);
|
|
|
|
aclDatum = (Datum) 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* detoast rel's ACL if necessary */
|
|
|
|
acl = DatumGetAclP(aclDatum);
|
|
|
|
}
|
|
|
|
|
|
|
|
result = aclmask(acl, roleid, ownerId, mask, how);
|
|
|
|
|
|
|
|
/* if we have a detoasted copy, free it */
|
|
|
|
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
|
|
|
|
pfree(acl);
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2009-01-22 21:16:10 +01:00
|
|
|
/*
|
|
|
|
* Exported routine for checking a user's access privileges to a column
|
|
|
|
*
|
|
|
|
* Returns ACLCHECK_OK if the user has any of the privileges identified by
|
|
|
|
* 'mode'; otherwise returns a suitable error code (in practice, always
|
|
|
|
* ACLCHECK_NO_PRIV).
|
|
|
|
*
|
|
|
|
* As with pg_attribute_aclmask, only privileges granted directly on the
|
|
|
|
* column are considered here.
|
|
|
|
*/
|
|
|
|
AclResult
|
|
|
|
pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum,
|
|
|
|
Oid roleid, AclMode mode)
|
|
|
|
{
|
|
|
|
if (pg_attribute_aclmask(table_oid, attnum, roleid, mode, ACLMASK_ANY) != 0)
|
|
|
|
return ACLCHECK_OK;
|
|
|
|
else
|
|
|
|
return ACLCHECK_NO_PRIV;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Exported routine for checking a user's access privileges to any/all columns
|
|
|
|
*
|
|
|
|
* If 'how' is ACLMASK_ANY, then returns ACLCHECK_OK if user has any of the
|
|
|
|
* privileges identified by 'mode' on any non-dropped column in the relation;
|
|
|
|
* otherwise returns a suitable error code (in practice, always
|
|
|
|
* ACLCHECK_NO_PRIV).
|
|
|
|
*
|
|
|
|
* If 'how' is ACLMASK_ALL, then returns ACLCHECK_OK if user has any of the
|
2009-02-06 22:15:12 +01:00
|
|
|
* privileges identified by 'mode' on each non-dropped column in the relation
|
2009-01-22 21:16:10 +01:00
|
|
|
* (and there must be at least one such column); otherwise returns a suitable
|
|
|
|
* error code (in practice, always ACLCHECK_NO_PRIV).
|
|
|
|
*
|
|
|
|
* As with pg_attribute_aclmask, only privileges granted directly on the
|
|
|
|
* column(s) are considered here.
|
|
|
|
*
|
|
|
|
* Note: system columns are not considered here; there are cases where that
|
|
|
|
* might be appropriate but there are also cases where it wouldn't.
|
|
|
|
*/
|
|
|
|
AclResult
|
|
|
|
pg_attribute_aclcheck_all(Oid table_oid, Oid roleid, AclMode mode,
|
|
|
|
AclMaskHow how)
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
AclResult result;
|
|
|
|
HeapTuple classTuple;
|
|
|
|
Form_pg_class classForm;
|
|
|
|
AttrNumber nattrs;
|
|
|
|
AttrNumber curr_att;
|
2009-01-22 21:16:10 +01:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
/*
|
|
|
|
* Must fetch pg_class row to check number of attributes. As in
|
2009-06-11 16:49:15 +02:00
|
|
|
* pg_attribute_aclmask, we prefer to return "no privileges" instead of
|
|
|
|
* throwing an error if we get any unexpected lookup errors.
|
2009-02-06 22:15:12 +01:00
|
|
|
*/
|
2010-02-14 19:42:19 +01:00
|
|
|
classTuple = SearchSysCache1(RELOID, ObjectIdGetDatum(table_oid));
|
2009-01-22 21:16:10 +01:00
|
|
|
if (!HeapTupleIsValid(classTuple))
|
2009-02-06 22:15:12 +01:00
|
|
|
return ACLCHECK_NO_PRIV;
|
2009-01-22 21:16:10 +01:00
|
|
|
classForm = (Form_pg_class) GETSTRUCT(classTuple);
|
|
|
|
|
|
|
|
nattrs = classForm->relnatts;
|
|
|
|
|
|
|
|
ReleaseSysCache(classTuple);
|
|
|
|
|
|
|
|
/*
|
2014-05-06 18:12:18 +02:00
|
|
|
* Initialize result in case there are no non-dropped columns. We want to
|
2009-06-11 16:49:15 +02:00
|
|
|
* report failure in such cases for either value of 'how'.
|
2009-01-22 21:16:10 +01:00
|
|
|
*/
|
|
|
|
result = ACLCHECK_NO_PRIV;
|
|
|
|
|
|
|
|
for (curr_att = 1; curr_att <= nattrs; curr_att++)
|
|
|
|
{
|
|
|
|
HeapTuple attTuple;
|
2009-02-06 22:15:12 +01:00
|
|
|
AclMode attmask;
|
2009-01-22 21:16:10 +01:00
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
attTuple = SearchSysCache2(ATTNUM,
|
|
|
|
ObjectIdGetDatum(table_oid),
|
|
|
|
Int16GetDatum(curr_att));
|
2009-01-22 21:16:10 +01:00
|
|
|
if (!HeapTupleIsValid(attTuple))
|
2009-02-06 22:15:12 +01:00
|
|
|
continue;
|
2009-01-22 21:16:10 +01:00
|
|
|
|
|
|
|
/* ignore dropped columns */
|
2009-02-06 22:15:12 +01:00
|
|
|
if (((Form_pg_attribute) GETSTRUCT(attTuple))->attisdropped)
|
|
|
|
{
|
|
|
|
ReleaseSysCache(attTuple);
|
2009-01-22 21:16:10 +01:00
|
|
|
continue;
|
2009-02-06 22:15:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Here we hard-wire knowledge that the default ACL for a column
|
2009-06-11 16:49:15 +02:00
|
|
|
* grants no privileges, so that we can fall out quickly in the very
|
|
|
|
* common case where attacl is null.
|
2009-02-06 22:15:12 +01:00
|
|
|
*/
|
|
|
|
if (heap_attisnull(attTuple, Anum_pg_attribute_attacl))
|
|
|
|
attmask = 0;
|
|
|
|
else
|
|
|
|
attmask = pg_attribute_aclmask(table_oid, curr_att, roleid,
|
|
|
|
mode, ACLMASK_ANY);
|
|
|
|
|
|
|
|
ReleaseSysCache(attTuple);
|
2009-01-22 21:16:10 +01:00
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
if (attmask != 0)
|
2009-01-22 21:16:10 +01:00
|
|
|
{
|
|
|
|
result = ACLCHECK_OK;
|
|
|
|
if (how == ACLMASK_ANY)
|
|
|
|
break; /* succeed on any success */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result = ACLCHECK_NO_PRIV;
|
|
|
|
if (how == ACLMASK_ALL)
|
|
|
|
break; /* fail on any failure */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2004-05-11 19:36:13 +02:00
|
|
|
/*
|
|
|
|
* Exported routine for checking a user's access privileges to a table
|
|
|
|
*
|
|
|
|
* Returns ACLCHECK_OK if the user has any of the privileges identified by
|
|
|
|
* 'mode'; otherwise returns a suitable error code (in practice, always
|
|
|
|
* ACLCHECK_NO_PRIV).
|
|
|
|
*/
|
|
|
|
AclResult
|
2005-06-28 07:09:14 +02:00
|
|
|
pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
|
2004-05-11 19:36:13 +02:00
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
if (pg_class_aclmask(table_oid, roleid, mode, ACLMASK_ANY) != 0)
|
2004-05-11 19:36:13 +02:00
|
|
|
return ACLCHECK_OK;
|
|
|
|
else
|
|
|
|
return ACLCHECK_NO_PRIV;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Exported routine for checking a user's access privileges to a database
|
|
|
|
*/
|
|
|
|
AclResult
|
2005-06-28 07:09:14 +02:00
|
|
|
pg_database_aclcheck(Oid db_oid, Oid roleid, AclMode mode)
|
2004-05-11 19:36:13 +02:00
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
if (pg_database_aclmask(db_oid, roleid, mode, ACLMASK_ANY) != 0)
|
2004-05-11 19:36:13 +02:00
|
|
|
return ACLCHECK_OK;
|
|
|
|
else
|
|
|
|
return ACLCHECK_NO_PRIV;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Exported routine for checking a user's access privileges to a function
|
|
|
|
*/
|
|
|
|
AclResult
|
2005-06-28 07:09:14 +02:00
|
|
|
pg_proc_aclcheck(Oid proc_oid, Oid roleid, AclMode mode)
|
2004-05-11 19:36:13 +02:00
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
if (pg_proc_aclmask(proc_oid, roleid, mode, ACLMASK_ANY) != 0)
|
2004-05-11 19:36:13 +02:00
|
|
|
return ACLCHECK_OK;
|
|
|
|
else
|
|
|
|
return ACLCHECK_NO_PRIV;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Exported routine for checking a user's access privileges to a language
|
|
|
|
*/
|
|
|
|
AclResult
|
2005-06-28 07:09:14 +02:00
|
|
|
pg_language_aclcheck(Oid lang_oid, Oid roleid, AclMode mode)
|
2004-05-11 19:36:13 +02:00
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
if (pg_language_aclmask(lang_oid, roleid, mode, ACLMASK_ANY) != 0)
|
2004-05-11 19:36:13 +02:00
|
|
|
return ACLCHECK_OK;
|
|
|
|
else
|
|
|
|
return ACLCHECK_NO_PRIV;
|
|
|
|
}
|
|
|
|
|
2009-12-11 04:34:57 +01:00
|
|
|
/*
|
|
|
|
* Exported routine for checking a user's access privileges to a largeobject
|
|
|
|
*/
|
|
|
|
AclResult
|
|
|
|
pg_largeobject_aclcheck_snapshot(Oid lobj_oid, Oid roleid, AclMode mode,
|
|
|
|
Snapshot snapshot)
|
|
|
|
{
|
|
|
|
if (pg_largeobject_aclmask_snapshot(lobj_oid, roleid, mode,
|
|
|
|
ACLMASK_ANY, snapshot) != 0)
|
|
|
|
return ACLCHECK_OK;
|
|
|
|
else
|
|
|
|
return ACLCHECK_NO_PRIV;
|
|
|
|
}
|
|
|
|
|
2004-05-11 19:36:13 +02:00
|
|
|
/*
|
|
|
|
* Exported routine for checking a user's access privileges to a namespace
|
|
|
|
*/
|
|
|
|
AclResult
|
2005-06-28 07:09:14 +02:00
|
|
|
pg_namespace_aclcheck(Oid nsp_oid, Oid roleid, AclMode mode)
|
2004-05-11 19:36:13 +02:00
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
if (pg_namespace_aclmask(nsp_oid, roleid, mode, ACLMASK_ANY) != 0)
|
2004-05-11 19:36:13 +02:00
|
|
|
return ACLCHECK_OK;
|
|
|
|
else
|
|
|
|
return ACLCHECK_NO_PRIV;
|
|
|
|
}
|
|
|
|
|
2004-06-18 08:14:31 +02:00
|
|
|
/*
|
|
|
|
* Exported routine for checking a user's access privileges to a tablespace
|
|
|
|
*/
|
|
|
|
AclResult
|
2005-06-28 07:09:14 +02:00
|
|
|
pg_tablespace_aclcheck(Oid spc_oid, Oid roleid, AclMode mode)
|
2004-06-18 08:14:31 +02:00
|
|
|
{
|
2005-06-28 07:09:14 +02:00
|
|
|
if (pg_tablespace_aclmask(spc_oid, roleid, mode, ACLMASK_ANY) != 0)
|
2004-06-18 08:14:31 +02:00
|
|
|
return ACLCHECK_OK;
|
|
|
|
else
|
|
|
|
return ACLCHECK_NO_PRIV;
|
|
|
|
}
|
|
|
|
|
2008-12-19 17:25:19 +01:00
|
|
|
/*
|
|
|
|
* Exported routine for checking a user's access privileges to a foreign
|
|
|
|
* data wrapper
|
|
|
|
*/
|
|
|
|
AclResult
|
|
|
|
pg_foreign_data_wrapper_aclcheck(Oid fdw_oid, Oid roleid, AclMode mode)
|
|
|
|
{
|
|
|
|
if (pg_foreign_data_wrapper_aclmask(fdw_oid, roleid, mode, ACLMASK_ANY) != 0)
|
|
|
|
return ACLCHECK_OK;
|
|
|
|
else
|
|
|
|
return ACLCHECK_NO_PRIV;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Exported routine for checking a user's access privileges to a foreign
|
|
|
|
* server
|
|
|
|
*/
|
|
|
|
AclResult
|
|
|
|
pg_foreign_server_aclcheck(Oid srv_oid, Oid roleid, AclMode mode)
|
|
|
|
{
|
|
|
|
if (pg_foreign_server_aclmask(srv_oid, roleid, mode, ACLMASK_ANY) != 0)
|
|
|
|
return ACLCHECK_OK;
|
|
|
|
else
|
|
|
|
return ACLCHECK_NO_PRIV;
|
|
|
|
}
|
2004-05-11 19:36:13 +02:00
|
|
|
|
2011-12-19 23:05:19 +01:00
|
|
|
/*
|
|
|
|
* Exported routine for checking a user's access privileges to a type
|
|
|
|
*/
|
|
|
|
AclResult
|
|
|
|
pg_type_aclcheck(Oid type_oid, Oid roleid, AclMode mode)
|
|
|
|
{
|
|
|
|
if (pg_type_aclmask(type_oid, roleid, mode, ACLMASK_ANY) != 0)
|
|
|
|
return ACLCHECK_OK;
|
|
|
|
else
|
|
|
|
return ACLCHECK_NO_PRIV;
|
|
|
|
}
|
|
|
|
|
2002-03-22 00:27:25 +01:00
|
|
|
/*
|
|
|
|
* Ownership check for a relation (specified by OID).
|
|
|
|
*/
|
|
|
|
bool
|
2005-06-28 07:09:14 +02:00
|
|
|
pg_class_ownercheck(Oid class_oid, Oid roleid)
|
2002-03-22 00:27:25 +01:00
|
|
|
{
|
|
|
|
HeapTuple tuple;
|
2005-10-15 04:49:52 +02:00
|
|
|
Oid ownerId;
|
2002-03-22 00:27:25 +01:00
|
|
|
|
|
|
|
/* Superusers bypass all permission checking. */
|
2005-06-28 07:09:14 +02:00
|
|
|
if (superuser_arg(roleid))
|
2002-03-22 00:27:25 +01:00
|
|
|
return true;
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(class_oid));
|
2002-03-22 00:27:25 +01:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_TABLE),
|
2005-10-15 04:49:52 +02:00
|
|
|
errmsg("relation with OID %u does not exist", class_oid)));
|
2002-03-22 00:27:25 +01:00
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
ownerId = ((Form_pg_class) GETSTRUCT(tuple))->relowner;
|
2002-03-22 00:27:25 +01:00
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
2005-07-26 18:38:29 +02:00
|
|
|
return has_privs_of_role(roleid, ownerId);
|
2002-03-22 00:27:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ownership check for a type (specified by OID).
|
|
|
|
*/
|
|
|
|
bool
|
2005-06-28 07:09:14 +02:00
|
|
|
pg_type_ownercheck(Oid type_oid, Oid roleid)
|
2002-03-22 00:27:25 +01:00
|
|
|
{
|
|
|
|
HeapTuple tuple;
|
2005-10-15 04:49:52 +02:00
|
|
|
Oid ownerId;
|
2002-03-22 00:27:25 +01:00
|
|
|
|
|
|
|
/* Superusers bypass all permission checking. */
|
2005-06-28 07:09:14 +02:00
|
|
|
if (superuser_arg(roleid))
|
2002-03-22 00:27:25 +01:00
|
|
|
return true;
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_oid));
|
2002-03-22 00:27:25 +01:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("type with OID %u does not exist", type_oid)));
|
2002-03-22 00:27:25 +01:00
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
ownerId = ((Form_pg_type) GETSTRUCT(tuple))->typowner;
|
2002-03-22 00:27:25 +01:00
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
2005-07-26 18:38:29 +02:00
|
|
|
return has_privs_of_role(roleid, ownerId);
|
2002-03-22 00:27:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ownership check for an operator (specified by OID).
|
|
|
|
*/
|
|
|
|
bool
|
2005-06-28 07:09:14 +02:00
|
|
|
pg_oper_ownercheck(Oid oper_oid, Oid roleid)
|
2002-03-22 00:27:25 +01:00
|
|
|
{
|
|
|
|
HeapTuple tuple;
|
2005-10-15 04:49:52 +02:00
|
|
|
Oid ownerId;
|
2002-03-22 00:27:25 +01:00
|
|
|
|
|
|
|
/* Superusers bypass all permission checking. */
|
2005-06-28 07:09:14 +02:00
|
|
|
if (superuser_arg(roleid))
|
2002-03-22 00:27:25 +01:00
|
|
|
return true;
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(oper_oid));
|
2002-03-22 00:27:25 +01:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_FUNCTION),
|
2005-10-15 04:49:52 +02:00
|
|
|
errmsg("operator with OID %u does not exist", oper_oid)));
|
2002-03-22 00:27:25 +01:00
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
ownerId = ((Form_pg_operator) GETSTRUCT(tuple))->oprowner;
|
2002-03-22 00:27:25 +01:00
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
2005-07-26 18:38:29 +02:00
|
|
|
return has_privs_of_role(roleid, ownerId);
|
2002-03-22 00:27:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ownership check for a function (specified by OID).
|
|
|
|
*/
|
|
|
|
bool
|
2005-06-28 07:09:14 +02:00
|
|
|
pg_proc_ownercheck(Oid proc_oid, Oid roleid)
|
2002-03-22 00:27:25 +01:00
|
|
|
{
|
|
|
|
HeapTuple tuple;
|
2005-10-15 04:49:52 +02:00
|
|
|
Oid ownerId;
|
2002-03-22 00:27:25 +01:00
|
|
|
|
|
|
|
/* Superusers bypass all permission checking. */
|
2005-06-28 07:09:14 +02:00
|
|
|
if (superuser_arg(roleid))
|
2002-03-22 00:27:25 +01:00
|
|
|
return true;
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(proc_oid));
|
2002-03-22 00:27:25 +01:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_FUNCTION),
|
2005-10-15 04:49:52 +02:00
|
|
|
errmsg("function with OID %u does not exist", proc_oid)));
|
2002-03-22 00:27:25 +01:00
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
ownerId = ((Form_pg_proc) GETSTRUCT(tuple))->proowner;
|
2002-03-22 00:27:25 +01:00
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
2005-07-26 18:38:29 +02:00
|
|
|
return has_privs_of_role(roleid, ownerId);
|
2002-03-22 00:27:25 +01:00
|
|
|
}
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2007-03-26 18:58:41 +02:00
|
|
|
/*
|
|
|
|
* Ownership check for a procedural language (specified by OID)
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
pg_language_ownercheck(Oid lan_oid, Oid roleid)
|
|
|
|
{
|
|
|
|
HeapTuple tuple;
|
|
|
|
Oid ownerId;
|
|
|
|
|
|
|
|
/* Superusers bypass all permission checking. */
|
|
|
|
if (superuser_arg(roleid))
|
|
|
|
return true;
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(LANGOID, ObjectIdGetDatum(lan_oid));
|
2007-03-26 18:58:41 +02:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_FUNCTION),
|
|
|
|
errmsg("language with OID %u does not exist", lan_oid)));
|
|
|
|
|
|
|
|
ownerId = ((Form_pg_language) GETSTRUCT(tuple))->lanowner;
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
|
|
|
return has_privs_of_role(roleid, ownerId);
|
|
|
|
}
|
|
|
|
|
2009-12-11 04:34:57 +01:00
|
|
|
/*
|
|
|
|
* Ownership check for a largeobject (specified by OID)
|
|
|
|
*
|
2009-12-21 02:34:11 +01:00
|
|
|
* This is only used for operations like ALTER LARGE OBJECT that are always
|
Use an MVCC snapshot, rather than SnapshotNow, for catalog scans.
SnapshotNow scans have the undesirable property that, in the face of
concurrent updates, the scan can fail to see either the old or the new
versions of the row. In many cases, we work around this by requiring
DDL operations to hold AccessExclusiveLock on the object being
modified; in some cases, the existing locking is inadequate and random
failures occur as a result. This commit doesn't change anything
related to locking, but will hopefully pave the way to allowing lock
strength reductions in the future.
The major issue has held us back from making this change in the past
is that taking an MVCC snapshot is significantly more expensive than
using a static special snapshot such as SnapshotNow. However, testing
of various worst-case scenarios reveals that this problem is not
severe except under fairly extreme workloads. To mitigate those
problems, we avoid retaking the MVCC snapshot for each new scan;
instead, we take a new snapshot only when invalidation messages have
been processed. The catcache machinery already requires that
invalidation messages be sent before releasing the related heavyweight
lock; else other backends might rely on locally-cached data rather
than scanning the catalog at all. Thus, making snapshot reuse
dependent on the same guarantees shouldn't break anything that wasn't
already subtly broken.
Patch by me. Review by Michael Paquier and Andres Freund.
2013-07-02 15:47:01 +02:00
|
|
|
* relative to an up-to-date snapshot.
|
2009-12-11 04:34:57 +01:00
|
|
|
*/
|
|
|
|
bool
|
|
|
|
pg_largeobject_ownercheck(Oid lobj_oid, Oid roleid)
|
|
|
|
{
|
|
|
|
Relation pg_lo_meta;
|
2010-02-26 03:01:40 +01:00
|
|
|
ScanKeyData entry[1];
|
|
|
|
SysScanDesc scan;
|
2009-12-11 04:34:57 +01:00
|
|
|
HeapTuple tuple;
|
|
|
|
Oid ownerId;
|
|
|
|
|
|
|
|
/* Superusers bypass all permission checking. */
|
|
|
|
if (superuser_arg(roleid))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/* There's no syscache for pg_largeobject_metadata */
|
|
|
|
pg_lo_meta = heap_open(LargeObjectMetadataRelationId,
|
|
|
|
AccessShareLock);
|
|
|
|
|
|
|
|
ScanKeyInit(&entry[0],
|
|
|
|
ObjectIdAttributeNumber,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(lobj_oid));
|
|
|
|
|
|
|
|
scan = systable_beginscan(pg_lo_meta,
|
|
|
|
LargeObjectMetadataOidIndexId, true,
|
Use an MVCC snapshot, rather than SnapshotNow, for catalog scans.
SnapshotNow scans have the undesirable property that, in the face of
concurrent updates, the scan can fail to see either the old or the new
versions of the row. In many cases, we work around this by requiring
DDL operations to hold AccessExclusiveLock on the object being
modified; in some cases, the existing locking is inadequate and random
failures occur as a result. This commit doesn't change anything
related to locking, but will hopefully pave the way to allowing lock
strength reductions in the future.
The major issue has held us back from making this change in the past
is that taking an MVCC snapshot is significantly more expensive than
using a static special snapshot such as SnapshotNow. However, testing
of various worst-case scenarios reveals that this problem is not
severe except under fairly extreme workloads. To mitigate those
problems, we avoid retaking the MVCC snapshot for each new scan;
instead, we take a new snapshot only when invalidation messages have
been processed. The catcache machinery already requires that
invalidation messages be sent before releasing the related heavyweight
lock; else other backends might rely on locally-cached data rather
than scanning the catalog at all. Thus, making snapshot reuse
dependent on the same guarantees shouldn't break anything that wasn't
already subtly broken.
Patch by me. Review by Michael Paquier and Andres Freund.
2013-07-02 15:47:01 +02:00
|
|
|
NULL, 1, entry);
|
2009-12-11 04:34:57 +01:00
|
|
|
|
|
|
|
tuple = systable_getnext(scan);
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("large object %u does not exist", lobj_oid)));
|
|
|
|
|
|
|
|
ownerId = ((Form_pg_largeobject_metadata) GETSTRUCT(tuple))->lomowner;
|
|
|
|
|
|
|
|
systable_endscan(scan);
|
|
|
|
heap_close(pg_lo_meta, AccessShareLock);
|
|
|
|
|
|
|
|
return has_privs_of_role(roleid, ownerId);
|
|
|
|
}
|
|
|
|
|
2002-04-21 02:26:44 +02:00
|
|
|
/*
|
|
|
|
* Ownership check for a namespace (specified by OID).
|
|
|
|
*/
|
|
|
|
bool
|
2005-06-28 07:09:14 +02:00
|
|
|
pg_namespace_ownercheck(Oid nsp_oid, Oid roleid)
|
2002-04-21 02:26:44 +02:00
|
|
|
{
|
|
|
|
HeapTuple tuple;
|
2005-10-15 04:49:52 +02:00
|
|
|
Oid ownerId;
|
2002-04-21 02:26:44 +02:00
|
|
|
|
|
|
|
/* Superusers bypass all permission checking. */
|
2005-06-28 07:09:14 +02:00
|
|
|
if (superuser_arg(roleid))
|
2002-04-21 02:26:44 +02:00
|
|
|
return true;
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(nsp_oid));
|
2002-04-21 02:26:44 +02:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_SCHEMA),
|
|
|
|
errmsg("schema with OID %u does not exist", nsp_oid)));
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
ownerId = ((Form_pg_namespace) GETSTRUCT(tuple))->nspowner;
|
2002-04-21 02:26:44 +02:00
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
2005-07-26 18:38:29 +02:00
|
|
|
return has_privs_of_role(roleid, ownerId);
|
2002-04-21 02:26:44 +02:00
|
|
|
}
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2004-06-18 08:14:31 +02:00
|
|
|
/*
|
|
|
|
* Ownership check for a tablespace (specified by OID).
|
|
|
|
*/
|
|
|
|
bool
|
2005-06-28 07:09:14 +02:00
|
|
|
pg_tablespace_ownercheck(Oid spc_oid, Oid roleid)
|
2004-06-18 08:14:31 +02:00
|
|
|
{
|
|
|
|
HeapTuple spctuple;
|
2005-10-15 04:49:52 +02:00
|
|
|
Oid spcowner;
|
2004-06-18 08:14:31 +02:00
|
|
|
|
|
|
|
/* Superusers bypass all permission checking. */
|
2005-06-28 07:09:14 +02:00
|
|
|
if (superuser_arg(roleid))
|
2004-06-18 08:14:31 +02:00
|
|
|
return true;
|
|
|
|
|
2010-01-05 22:54:00 +01:00
|
|
|
/* Search syscache for pg_tablespace */
|
2010-02-14 19:42:19 +01:00
|
|
|
spctuple = SearchSysCache1(TABLESPACEOID, ObjectIdGetDatum(spc_oid));
|
2004-06-18 08:14:31 +02:00
|
|
|
if (!HeapTupleIsValid(spctuple))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
2005-10-15 04:49:52 +02:00
|
|
|
errmsg("tablespace with OID %u does not exist", spc_oid)));
|
2004-06-18 08:14:31 +02:00
|
|
|
|
|
|
|
spcowner = ((Form_pg_tablespace) GETSTRUCT(spctuple))->spcowner;
|
|
|
|
|
2010-01-05 22:54:00 +01:00
|
|
|
ReleaseSysCache(spctuple);
|
2004-06-18 08:14:31 +02:00
|
|
|
|
2005-07-26 18:38:29 +02:00
|
|
|
return has_privs_of_role(roleid, spcowner);
|
2004-06-18 08:14:31 +02:00
|
|
|
}
|
|
|
|
|
2002-07-30 00:14:11 +02:00
|
|
|
/*
|
|
|
|
* Ownership check for an operator class (specified by OID).
|
|
|
|
*/
|
|
|
|
bool
|
2005-06-28 07:09:14 +02:00
|
|
|
pg_opclass_ownercheck(Oid opc_oid, Oid roleid)
|
2002-07-30 00:14:11 +02:00
|
|
|
{
|
|
|
|
HeapTuple tuple;
|
2005-10-15 04:49:52 +02:00
|
|
|
Oid ownerId;
|
2002-07-30 00:14:11 +02:00
|
|
|
|
|
|
|
/* Superusers bypass all permission checking. */
|
2005-06-28 07:09:14 +02:00
|
|
|
if (superuser_arg(roleid))
|
2002-07-30 00:14:11 +02:00
|
|
|
return true;
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opc_oid));
|
2002-07-30 00:14:11 +02:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("operator class with OID %u does not exist",
|
|
|
|
opc_oid)));
|
2002-07-30 00:14:11 +02:00
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
ownerId = ((Form_pg_opclass) GETSTRUCT(tuple))->opcowner;
|
2002-07-30 00:14:11 +02:00
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
2005-07-26 18:38:29 +02:00
|
|
|
return has_privs_of_role(roleid, ownerId);
|
2002-07-30 00:14:11 +02:00
|
|
|
}
|
2003-06-27 16:45:32 +02:00
|
|
|
|
2007-01-23 06:07:18 +01:00
|
|
|
/*
|
|
|
|
* Ownership check for an operator family (specified by OID).
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
pg_opfamily_ownercheck(Oid opf_oid, Oid roleid)
|
|
|
|
{
|
|
|
|
HeapTuple tuple;
|
|
|
|
Oid ownerId;
|
|
|
|
|
|
|
|
/* Superusers bypass all permission checking. */
|
|
|
|
if (superuser_arg(roleid))
|
|
|
|
return true;
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opf_oid));
|
2007-01-23 06:07:18 +01:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("operator family with OID %u does not exist",
|
|
|
|
opf_oid)));
|
|
|
|
|
|
|
|
ownerId = ((Form_pg_opfamily) GETSTRUCT(tuple))->opfowner;
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
|
|
|
return has_privs_of_role(roleid, ownerId);
|
|
|
|
}
|
|
|
|
|
2007-08-21 03:11:32 +02:00
|
|
|
/*
|
|
|
|
* Ownership check for a text search dictionary (specified by OID).
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
pg_ts_dict_ownercheck(Oid dict_oid, Oid roleid)
|
|
|
|
{
|
|
|
|
HeapTuple tuple;
|
|
|
|
Oid ownerId;
|
|
|
|
|
|
|
|
/* Superusers bypass all permission checking. */
|
|
|
|
if (superuser_arg(roleid))
|
|
|
|
return true;
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dict_oid));
|
2007-08-21 03:11:32 +02:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("text search dictionary with OID %u does not exist",
|
|
|
|
dict_oid)));
|
|
|
|
|
|
|
|
ownerId = ((Form_pg_ts_dict) GETSTRUCT(tuple))->dictowner;
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
|
|
|
return has_privs_of_role(roleid, ownerId);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ownership check for a text search configuration (specified by OID).
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
pg_ts_config_ownercheck(Oid cfg_oid, Oid roleid)
|
|
|
|
{
|
|
|
|
HeapTuple tuple;
|
|
|
|
Oid ownerId;
|
|
|
|
|
|
|
|
/* Superusers bypass all permission checking. */
|
|
|
|
if (superuser_arg(roleid))
|
|
|
|
return true;
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfg_oid));
|
2007-08-21 03:11:32 +02:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
2007-11-15 22:14:46 +01:00
|
|
|
errmsg("text search configuration with OID %u does not exist",
|
|
|
|
cfg_oid)));
|
2007-08-21 03:11:32 +02:00
|
|
|
|
|
|
|
ownerId = ((Form_pg_ts_config) GETSTRUCT(tuple))->cfgowner;
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
|
|
|
return has_privs_of_role(roleid, ownerId);
|
|
|
|
}
|
|
|
|
|
2011-04-01 17:28:28 +02:00
|
|
|
/*
|
|
|
|
* Ownership check for a foreign-data wrapper (specified by OID).
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
pg_foreign_data_wrapper_ownercheck(Oid srv_oid, Oid roleid)
|
|
|
|
{
|
|
|
|
HeapTuple tuple;
|
|
|
|
Oid ownerId;
|
|
|
|
|
|
|
|
/* Superusers bypass all permission checking. */
|
|
|
|
if (superuser_arg(roleid))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
tuple = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(srv_oid));
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("foreign-data wrapper with OID %u does not exist",
|
|
|
|
srv_oid)));
|
|
|
|
|
|
|
|
ownerId = ((Form_pg_foreign_data_wrapper) GETSTRUCT(tuple))->fdwowner;
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
|
|
|
return has_privs_of_role(roleid, ownerId);
|
|
|
|
}
|
|
|
|
|
2008-12-19 17:25:19 +01:00
|
|
|
/*
|
|
|
|
* Ownership check for a foreign server (specified by OID).
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid)
|
|
|
|
{
|
|
|
|
HeapTuple tuple;
|
|
|
|
Oid ownerId;
|
|
|
|
|
|
|
|
/* Superusers bypass all permission checking. */
|
|
|
|
if (superuser_arg(roleid))
|
|
|
|
return true;
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(srv_oid));
|
2008-12-19 17:25:19 +01:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
2009-06-11 16:49:15 +02:00
|
|
|
errmsg("foreign server with OID %u does not exist",
|
|
|
|
srv_oid)));
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
ownerId = ((Form_pg_foreign_server) GETSTRUCT(tuple))->srvowner;
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
|
|
|
return has_privs_of_role(roleid, ownerId);
|
|
|
|
}
|
2007-08-21 03:11:32 +02:00
|
|
|
|
2012-07-18 16:16:16 +02:00
|
|
|
/*
|
|
|
|
* Ownership check for an event trigger (specified by OID).
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
pg_event_trigger_ownercheck(Oid et_oid, Oid roleid)
|
|
|
|
{
|
|
|
|
HeapTuple tuple;
|
|
|
|
Oid ownerId;
|
|
|
|
|
|
|
|
/* Superusers bypass all permission checking. */
|
|
|
|
if (superuser_arg(roleid))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
tuple = SearchSysCache1(EVENTTRIGGEROID, ObjectIdGetDatum(et_oid));
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("event trigger with OID %u does not exist",
|
|
|
|
et_oid)));
|
|
|
|
|
|
|
|
ownerId = ((Form_pg_event_trigger) GETSTRUCT(tuple))->evtowner;
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
|
|
|
return has_privs_of_role(roleid, ownerId);
|
|
|
|
}
|
|
|
|
|
2003-06-27 16:45:32 +02:00
|
|
|
/*
|
2004-06-18 08:14:31 +02:00
|
|
|
* Ownership check for a database (specified by OID).
|
2003-06-27 16:45:32 +02:00
|
|
|
*/
|
|
|
|
bool
|
2005-06-28 07:09:14 +02:00
|
|
|
pg_database_ownercheck(Oid db_oid, Oid roleid)
|
2003-06-27 16:45:32 +02:00
|
|
|
{
|
2006-05-04 00:45:26 +02:00
|
|
|
HeapTuple tuple;
|
2005-10-15 04:49:52 +02:00
|
|
|
Oid dba;
|
2003-06-27 16:45:32 +02:00
|
|
|
|
|
|
|
/* Superusers bypass all permission checking. */
|
2005-06-28 07:09:14 +02:00
|
|
|
if (superuser_arg(roleid))
|
2003-06-27 16:45:32 +02:00
|
|
|
return true;
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(db_oid));
|
2006-05-04 00:45:26 +02:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_DATABASE),
|
|
|
|
errmsg("database with OID %u does not exist", db_oid)));
|
2003-06-27 16:45:32 +02:00
|
|
|
|
2006-05-04 00:45:26 +02:00
|
|
|
dba = ((Form_pg_database) GETSTRUCT(tuple))->datdba;
|
2003-06-27 16:45:32 +02:00
|
|
|
|
2006-05-04 00:45:26 +02:00
|
|
|
ReleaseSysCache(tuple);
|
2003-06-27 16:45:32 +02:00
|
|
|
|
2005-07-26 18:38:29 +02:00
|
|
|
return has_privs_of_role(roleid, dba);
|
2003-06-27 16:45:32 +02:00
|
|
|
}
|
2003-11-21 23:32:49 +01:00
|
|
|
|
2011-02-12 14:54:13 +01:00
|
|
|
/*
|
|
|
|
* Ownership check for a collation (specified by OID).
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
pg_collation_ownercheck(Oid coll_oid, Oid roleid)
|
|
|
|
{
|
|
|
|
HeapTuple tuple;
|
|
|
|
Oid ownerId;
|
|
|
|
|
|
|
|
/* Superusers bypass all permission checking. */
|
|
|
|
if (superuser_arg(roleid))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
tuple = SearchSysCache1(COLLOID, ObjectIdGetDatum(coll_oid));
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("collation with OID %u does not exist", coll_oid)));
|
|
|
|
|
|
|
|
ownerId = ((Form_pg_collation) GETSTRUCT(tuple))->collowner;
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
|
|
|
return has_privs_of_role(roleid, ownerId);
|
|
|
|
}
|
|
|
|
|
2003-11-21 23:32:49 +01:00
|
|
|
/*
|
|
|
|
* Ownership check for a conversion (specified by OID).
|
|
|
|
*/
|
|
|
|
bool
|
2005-06-28 07:09:14 +02:00
|
|
|
pg_conversion_ownercheck(Oid conv_oid, Oid roleid)
|
2003-11-21 23:32:49 +01:00
|
|
|
{
|
|
|
|
HeapTuple tuple;
|
2005-10-15 04:49:52 +02:00
|
|
|
Oid ownerId;
|
2003-11-21 23:32:49 +01:00
|
|
|
|
|
|
|
/* Superusers bypass all permission checking. */
|
2005-06-28 07:09:14 +02:00
|
|
|
if (superuser_arg(roleid))
|
2003-11-21 23:32:49 +01:00
|
|
|
return true;
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(CONVOID, ObjectIdGetDatum(conv_oid));
|
2003-11-21 23:32:49 +01:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
2005-10-15 04:49:52 +02:00
|
|
|
errmsg("conversion with OID %u does not exist", conv_oid)));
|
2003-11-21 23:32:49 +01:00
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
ownerId = ((Form_pg_conversion) GETSTRUCT(tuple))->conowner;
|
2003-11-21 23:32:49 +01:00
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
2005-07-26 18:38:29 +02:00
|
|
|
return has_privs_of_role(roleid, ownerId);
|
2003-11-21 23:32:49 +01:00
|
|
|
}
|
2009-10-05 21:24:49 +02:00
|
|
|
|
2011-03-04 22:08:24 +01:00
|
|
|
/*
|
|
|
|
* Ownership check for an extension (specified by OID).
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
pg_extension_ownercheck(Oid ext_oid, Oid roleid)
|
|
|
|
{
|
|
|
|
Relation pg_extension;
|
|
|
|
ScanKeyData entry[1];
|
|
|
|
SysScanDesc scan;
|
|
|
|
HeapTuple tuple;
|
|
|
|
Oid ownerId;
|
|
|
|
|
|
|
|
/* Superusers bypass all permission checking. */
|
|
|
|
if (superuser_arg(roleid))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/* There's no syscache for pg_extension, so do it the hard way */
|
|
|
|
pg_extension = heap_open(ExtensionRelationId, AccessShareLock);
|
|
|
|
|
|
|
|
ScanKeyInit(&entry[0],
|
|
|
|
ObjectIdAttributeNumber,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(ext_oid));
|
|
|
|
|
|
|
|
scan = systable_beginscan(pg_extension,
|
|
|
|
ExtensionOidIndexId, true,
|
Use an MVCC snapshot, rather than SnapshotNow, for catalog scans.
SnapshotNow scans have the undesirable property that, in the face of
concurrent updates, the scan can fail to see either the old or the new
versions of the row. In many cases, we work around this by requiring
DDL operations to hold AccessExclusiveLock on the object being
modified; in some cases, the existing locking is inadequate and random
failures occur as a result. This commit doesn't change anything
related to locking, but will hopefully pave the way to allowing lock
strength reductions in the future.
The major issue has held us back from making this change in the past
is that taking an MVCC snapshot is significantly more expensive than
using a static special snapshot such as SnapshotNow. However, testing
of various worst-case scenarios reveals that this problem is not
severe except under fairly extreme workloads. To mitigate those
problems, we avoid retaking the MVCC snapshot for each new scan;
instead, we take a new snapshot only when invalidation messages have
been processed. The catcache machinery already requires that
invalidation messages be sent before releasing the related heavyweight
lock; else other backends might rely on locally-cached data rather
than scanning the catalog at all. Thus, making snapshot reuse
dependent on the same guarantees shouldn't break anything that wasn't
already subtly broken.
Patch by me. Review by Michael Paquier and Andres Freund.
2013-07-02 15:47:01 +02:00
|
|
|
NULL, 1, entry);
|
2011-03-04 22:08:24 +01:00
|
|
|
|
|
|
|
tuple = systable_getnext(scan);
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("extension with OID %u does not exist", ext_oid)));
|
|
|
|
|
|
|
|
ownerId = ((Form_pg_extension) GETSTRUCT(tuple))->extowner;
|
|
|
|
|
|
|
|
systable_endscan(scan);
|
|
|
|
heap_close(pg_extension, AccessShareLock);
|
|
|
|
|
|
|
|
return has_privs_of_role(roleid, ownerId);
|
|
|
|
}
|
|
|
|
|
2011-03-09 17:28:20 +01:00
|
|
|
/*
|
2014-12-23 19:35:49 +01:00
|
|
|
* Check whether specified role has CREATEROLE privilege (or is a superuser)
|
2011-03-09 17:28:20 +01:00
|
|
|
*
|
2014-12-23 19:35:49 +01:00
|
|
|
* Note: roles do not have owners per se; instead we use this test in
|
|
|
|
* places where an ownership-like permissions test is needed for a role.
|
|
|
|
* Be sure to apply it to the role trying to do the operation, not the
|
|
|
|
* role being operated on! Also note that this generally should not be
|
|
|
|
* considered enough privilege if the target role is a superuser.
|
|
|
|
* (We don't handle that consideration here because we want to give a
|
|
|
|
* separate error message for such cases, so the caller has to deal with it.)
|
2011-03-09 17:28:20 +01:00
|
|
|
*/
|
|
|
|
bool
|
2014-12-23 19:35:49 +01:00
|
|
|
has_createrole_privilege(Oid roleid)
|
2011-03-09 17:28:20 +01:00
|
|
|
{
|
2014-12-23 19:35:49 +01:00
|
|
|
bool result = false;
|
|
|
|
HeapTuple utup;
|
2011-03-09 17:28:20 +01:00
|
|
|
|
2014-12-23 19:35:49 +01:00
|
|
|
/* Superusers bypass all permission checking. */
|
|
|
|
if (superuser_arg(roleid))
|
|
|
|
return true;
|
2014-12-23 14:22:09 +01:00
|
|
|
|
2014-12-23 19:35:49 +01:00
|
|
|
utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
|
|
|
|
if (HeapTupleIsValid(utup))
|
|
|
|
{
|
|
|
|
result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreaterole;
|
|
|
|
ReleaseSysCache(utup);
|
|
|
|
}
|
|
|
|
return result;
|
2011-03-09 17:28:20 +01:00
|
|
|
}
|
|
|
|
|
Row-Level Security Policies (RLS)
Building on the updatable security-barrier views work, add the
ability to define policies on tables to limit the set of rows
which are returned from a query and which are allowed to be added
to a table. Expressions defined by the policy for filtering are
added to the security barrier quals of the query, while expressions
defined to check records being added to a table are added to the
with-check options of the query.
New top-level commands are CREATE/ALTER/DROP POLICY and are
controlled by the table owner. Row Security is able to be enabled
and disabled by the owner on a per-table basis using
ALTER TABLE .. ENABLE/DISABLE ROW SECURITY.
Per discussion, ROW SECURITY is disabled on tables by default and
must be enabled for policies on the table to be used. If no
policies exist on a table with ROW SECURITY enabled, a default-deny
policy is used and no records will be visible.
By default, row security is applied at all times except for the
table owner and the superuser. A new GUC, row_security, is added
which can be set to ON, OFF, or FORCE. When set to FORCE, row
security will be applied even for the table owner and superusers.
When set to OFF, row security will be disabled when allowed and an
error will be thrown if the user does not have rights to bypass row
security.
Per discussion, pg_dump sets row_security = OFF by default to ensure
that exports and backups will have all data in the table or will
error if there are insufficient privileges to bypass row security.
A new option has been added to pg_dump, --enable-row-security, to
ask pg_dump to export with row security enabled.
A new role capability, BYPASSRLS, which can only be set by the
superuser, is added to allow other users to be able to bypass row
security using row_security = OFF.
Many thanks to the various individuals who have helped with the
design, particularly Robert Haas for his feedback.
Authors include Craig Ringer, KaiGai Kohei, Adam Brightwell, Dean
Rasheed, with additional changes and rework by me.
Reviewers have included all of the above, Greg Smith,
Jeff McCormick, and Robert Haas.
2014-09-19 17:18:35 +02:00
|
|
|
bool
|
2014-12-23 19:35:49 +01:00
|
|
|
has_bypassrls_privilege(Oid roleid)
|
Row-Level Security Policies (RLS)
Building on the updatable security-barrier views work, add the
ability to define policies on tables to limit the set of rows
which are returned from a query and which are allowed to be added
to a table. Expressions defined by the policy for filtering are
added to the security barrier quals of the query, while expressions
defined to check records being added to a table are added to the
with-check options of the query.
New top-level commands are CREATE/ALTER/DROP POLICY and are
controlled by the table owner. Row Security is able to be enabled
and disabled by the owner on a per-table basis using
ALTER TABLE .. ENABLE/DISABLE ROW SECURITY.
Per discussion, ROW SECURITY is disabled on tables by default and
must be enabled for policies on the table to be used. If no
policies exist on a table with ROW SECURITY enabled, a default-deny
policy is used and no records will be visible.
By default, row security is applied at all times except for the
table owner and the superuser. A new GUC, row_security, is added
which can be set to ON, OFF, or FORCE. When set to FORCE, row
security will be applied even for the table owner and superusers.
When set to OFF, row security will be disabled when allowed and an
error will be thrown if the user does not have rights to bypass row
security.
Per discussion, pg_dump sets row_security = OFF by default to ensure
that exports and backups will have all data in the table or will
error if there are insufficient privileges to bypass row security.
A new option has been added to pg_dump, --enable-row-security, to
ask pg_dump to export with row security enabled.
A new role capability, BYPASSRLS, which can only be set by the
superuser, is added to allow other users to be able to bypass row
security using row_security = OFF.
Many thanks to the various individuals who have helped with the
design, particularly Robert Haas for his feedback.
Authors include Craig Ringer, KaiGai Kohei, Adam Brightwell, Dean
Rasheed, with additional changes and rework by me.
Reviewers have included all of the above, Greg Smith,
Jeff McCormick, and Robert Haas.
2014-09-19 17:18:35 +02:00
|
|
|
{
|
2014-12-23 19:35:49 +01:00
|
|
|
bool result = false;
|
|
|
|
HeapTuple utup;
|
2014-12-23 14:22:09 +01:00
|
|
|
|
2014-12-23 19:35:49 +01:00
|
|
|
/* Superusers bypass all permission checking. */
|
|
|
|
if (superuser_arg(roleid))
|
|
|
|
return true;
|
2014-12-23 14:22:09 +01:00
|
|
|
|
2014-12-23 19:35:49 +01:00
|
|
|
utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
|
|
|
|
if (HeapTupleIsValid(utup))
|
|
|
|
{
|
|
|
|
result = ((Form_pg_authid) GETSTRUCT(utup))->rolbypassrls;
|
|
|
|
ReleaseSysCache(utup);
|
|
|
|
}
|
|
|
|
return result;
|
Row-Level Security Policies (RLS)
Building on the updatable security-barrier views work, add the
ability to define policies on tables to limit the set of rows
which are returned from a query and which are allowed to be added
to a table. Expressions defined by the policy for filtering are
added to the security barrier quals of the query, while expressions
defined to check records being added to a table are added to the
with-check options of the query.
New top-level commands are CREATE/ALTER/DROP POLICY and are
controlled by the table owner. Row Security is able to be enabled
and disabled by the owner on a per-table basis using
ALTER TABLE .. ENABLE/DISABLE ROW SECURITY.
Per discussion, ROW SECURITY is disabled on tables by default and
must be enabled for policies on the table to be used. If no
policies exist on a table with ROW SECURITY enabled, a default-deny
policy is used and no records will be visible.
By default, row security is applied at all times except for the
table owner and the superuser. A new GUC, row_security, is added
which can be set to ON, OFF, or FORCE. When set to FORCE, row
security will be applied even for the table owner and superusers.
When set to OFF, row security will be disabled when allowed and an
error will be thrown if the user does not have rights to bypass row
security.
Per discussion, pg_dump sets row_security = OFF by default to ensure
that exports and backups will have all data in the table or will
error if there are insufficient privileges to bypass row security.
A new option has been added to pg_dump, --enable-row-security, to
ask pg_dump to export with row security enabled.
A new role capability, BYPASSRLS, which can only be set by the
superuser, is added to allow other users to be able to bypass row
security using row_security = OFF.
Many thanks to the various individuals who have helped with the
design, particularly Robert Haas for his feedback.
Authors include Craig Ringer, KaiGai Kohei, Adam Brightwell, Dean
Rasheed, with additional changes and rework by me.
Reviewers have included all of the above, Greg Smith,
Jeff McCormick, and Robert Haas.
2014-09-19 17:18:35 +02:00
|
|
|
}
|
|
|
|
|
2009-10-05 21:24:49 +02:00
|
|
|
/*
|
|
|
|
* Fetch pg_default_acl entry for given role, namespace and object type
|
|
|
|
* (object type must be given in pg_default_acl's encoding).
|
|
|
|
* Returns NULL if no such entry.
|
|
|
|
*/
|
|
|
|
static Acl *
|
|
|
|
get_default_acl_internal(Oid roleId, Oid nsp_oid, char objtype)
|
|
|
|
{
|
|
|
|
Acl *result = NULL;
|
|
|
|
HeapTuple tuple;
|
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache3(DEFACLROLENSPOBJ,
|
|
|
|
ObjectIdGetDatum(roleId),
|
|
|
|
ObjectIdGetDatum(nsp_oid),
|
|
|
|
CharGetDatum(objtype));
|
2009-10-05 21:24:49 +02:00
|
|
|
|
|
|
|
if (HeapTupleIsValid(tuple))
|
|
|
|
{
|
2010-02-26 03:01:40 +01:00
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
2009-10-05 21:24:49 +02:00
|
|
|
|
|
|
|
aclDatum = SysCacheGetAttr(DEFACLROLENSPOBJ, tuple,
|
|
|
|
Anum_pg_default_acl_defaclacl,
|
|
|
|
&isNull);
|
|
|
|
if (!isNull)
|
|
|
|
result = DatumGetAclPCopy(aclDatum);
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get default permissions for newly created object within given schema
|
|
|
|
*
|
|
|
|
* Returns NULL if built-in system defaults should be used
|
|
|
|
*/
|
|
|
|
Acl *
|
|
|
|
get_user_default_acl(GrantObjectType objtype, Oid ownerId, Oid nsp_oid)
|
|
|
|
{
|
|
|
|
Acl *result;
|
|
|
|
Acl *glob_acl;
|
|
|
|
Acl *schema_acl;
|
|
|
|
Acl *def_acl;
|
|
|
|
char defaclobjtype;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Use NULL during bootstrap, since pg_default_acl probably isn't there
|
|
|
|
* yet.
|
|
|
|
*/
|
|
|
|
if (IsBootstrapProcessingMode())
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* Check if object type is supported in pg_default_acl */
|
|
|
|
switch (objtype)
|
|
|
|
{
|
|
|
|
case ACL_OBJECT_RELATION:
|
|
|
|
defaclobjtype = DEFACLOBJ_RELATION;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ACL_OBJECT_SEQUENCE:
|
|
|
|
defaclobjtype = DEFACLOBJ_SEQUENCE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ACL_OBJECT_FUNCTION:
|
|
|
|
defaclobjtype = DEFACLOBJ_FUNCTION;
|
|
|
|
break;
|
|
|
|
|
2011-12-19 23:05:19 +01:00
|
|
|
case ACL_OBJECT_TYPE:
|
|
|
|
defaclobjtype = DEFACLOBJ_TYPE;
|
|
|
|
break;
|
|
|
|
|
2009-10-05 21:24:49 +02:00
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Look up the relevant pg_default_acl entries */
|
|
|
|
glob_acl = get_default_acl_internal(ownerId, InvalidOid, defaclobjtype);
|
|
|
|
schema_acl = get_default_acl_internal(ownerId, nsp_oid, defaclobjtype);
|
|
|
|
|
|
|
|
/* Quick out if neither entry exists */
|
|
|
|
if (glob_acl == NULL && schema_acl == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* We need to know the hard-wired default value, too */
|
|
|
|
def_acl = acldefault(objtype, ownerId);
|
|
|
|
|
|
|
|
/* If there's no global entry, substitute the hard-wired default */
|
|
|
|
if (glob_acl == NULL)
|
|
|
|
glob_acl = def_acl;
|
|
|
|
|
|
|
|
/* Merge in any per-schema privileges */
|
|
|
|
result = aclmerge(glob_acl, schema_acl, ownerId);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For efficiency, we want to return NULL if the result equals default.
|
|
|
|
* This requires sorting both arrays to get an accurate comparison.
|
|
|
|
*/
|
|
|
|
aclitemsort(result);
|
|
|
|
aclitemsort(def_acl);
|
|
|
|
if (aclequal(result, def_acl))
|
|
|
|
result = NULL;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2016-04-07 03:45:32 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Record initial ACL for an extension object
|
|
|
|
*
|
|
|
|
* This will perform a wholesale replacement of the entire ACL for the object
|
|
|
|
* passed in, therefore be sure to pass in the complete new ACL to use.
|
|
|
|
*
|
|
|
|
* Can be called at any time, we check if 'creating_extension' is set and, if
|
|
|
|
* not, exit immediately.
|
|
|
|
*
|
|
|
|
* Pass in the object OID, the OID of the class (the OID of the table which
|
|
|
|
* the object is defined in) and the 'sub' id of the object (objsubid), if
|
|
|
|
* any. If there is no 'sub' id (they are currently only used for columns of
|
|
|
|
* tables) then pass in '0'. Finally, pass in the complete ACL to store.
|
|
|
|
*
|
|
|
|
* If an ACL already exists for this object/sub-object then we will replace
|
|
|
|
* it with what is passed in.
|
|
|
|
*
|
|
|
|
* Passing in NULL for 'new_acl' will result in the entry for the object being
|
|
|
|
* removed, if one is found.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
recordExtensionInitPriv(Oid objoid, Oid classoid, int objsubid, Acl *new_acl)
|
|
|
|
{
|
|
|
|
Relation relation;
|
|
|
|
ScanKeyData key[3];
|
|
|
|
SysScanDesc scan;
|
|
|
|
HeapTuple tuple;
|
|
|
|
HeapTuple oldtuple;
|
|
|
|
|
2016-04-07 03:45:32 +02:00
|
|
|
/*
|
|
|
|
* Generally, we only record the initial privileges when an extension is
|
|
|
|
* being created, but because we don't actually use CREATE EXTENSION
|
|
|
|
* during binary upgrades with pg_upgrade, there is a variable to let us
|
|
|
|
* know that the GRANT and REVOKE statements being issued, while this
|
|
|
|
* variable is true, are for the initial privileges of the extension
|
|
|
|
* object and therefore we need to record them.
|
|
|
|
*/
|
|
|
|
if (!creating_extension && !binary_upgrade_record_init_privs)
|
2016-04-07 03:45:32 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
relation = heap_open(InitPrivsRelationId, RowExclusiveLock);
|
|
|
|
|
|
|
|
ScanKeyInit(&key[0],
|
|
|
|
Anum_pg_init_privs_objoid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(objoid));
|
|
|
|
ScanKeyInit(&key[1],
|
|
|
|
Anum_pg_init_privs_classoid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(classoid));
|
|
|
|
ScanKeyInit(&key[2],
|
|
|
|
Anum_pg_init_privs_objsubid,
|
|
|
|
BTEqualStrategyNumber, F_INT4EQ,
|
|
|
|
Int32GetDatum(objsubid));
|
|
|
|
|
|
|
|
scan = systable_beginscan(relation, InitPrivsObjIndexId, true,
|
|
|
|
NULL, 3, key);
|
|
|
|
|
|
|
|
/* There should exist only one entry or none. */
|
|
|
|
oldtuple = systable_getnext(scan);
|
|
|
|
|
|
|
|
/* If we find an entry, update it with the latest ACL. */
|
|
|
|
if (HeapTupleIsValid(oldtuple))
|
|
|
|
{
|
|
|
|
Datum values[Natts_pg_init_privs];
|
|
|
|
bool nulls[Natts_pg_init_privs];
|
|
|
|
bool replace[Natts_pg_init_privs];
|
|
|
|
|
|
|
|
/* If we have a new ACL to set, then update the row with it. */
|
|
|
|
if (new_acl)
|
|
|
|
{
|
|
|
|
MemSet(values, 0, sizeof(values));
|
|
|
|
MemSet(nulls, false, sizeof(nulls));
|
|
|
|
MemSet(replace, false, sizeof(replace));
|
|
|
|
|
|
|
|
values[Anum_pg_init_privs_privs - 1] = PointerGetDatum(new_acl);
|
|
|
|
replace[Anum_pg_init_privs_privs - 1] = true;
|
|
|
|
|
|
|
|
oldtuple = heap_modify_tuple(oldtuple, RelationGetDescr(relation),
|
|
|
|
values, nulls, replace);
|
|
|
|
|
|
|
|
simple_heap_update(relation, &oldtuple->t_self, oldtuple);
|
|
|
|
|
|
|
|
/* keep the catalog indexes up to date */
|
|
|
|
CatalogUpdateIndexes(relation, oldtuple);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
/* new_acl is NULL, so delete the entry we found. */
|
|
|
|
simple_heap_delete(relation, &oldtuple->t_self);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* No entry found, so add it. */
|
|
|
|
Datum values[Natts_pg_init_privs];
|
|
|
|
bool nulls[Natts_pg_init_privs];
|
|
|
|
|
|
|
|
MemSet(nulls, false, sizeof(nulls));
|
|
|
|
|
|
|
|
values[Anum_pg_init_privs_objoid - 1] = ObjectIdGetDatum(objoid);
|
|
|
|
values[Anum_pg_init_privs_classoid - 1] = ObjectIdGetDatum(classoid);
|
|
|
|
values[Anum_pg_init_privs_objsubid - 1] = Int32GetDatum(objsubid);
|
|
|
|
|
|
|
|
/* This function only handles initial privileges of extensions */
|
|
|
|
values[Anum_pg_init_privs_privtype - 1] =
|
|
|
|
CharGetDatum(INITPRIVS_EXTENSION);
|
|
|
|
|
|
|
|
values[Anum_pg_init_privs_privs - 1] = PointerGetDatum(new_acl);
|
|
|
|
|
|
|
|
tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls);
|
|
|
|
|
|
|
|
simple_heap_insert(relation, tuple);
|
|
|
|
|
|
|
|
/* keep the catalog indexes up to date */
|
|
|
|
CatalogUpdateIndexes(relation, tuple);
|
|
|
|
}
|
|
|
|
|
2016-04-16 03:57:15 +02:00
|
|
|
systable_endscan(scan);
|
|
|
|
|
2016-04-07 03:45:32 +02:00
|
|
|
/* prevent error when processing objects multiple times */
|
|
|
|
CommandCounterIncrement();
|
|
|
|
|
|
|
|
heap_close(relation, RowExclusiveLock);
|
|
|
|
}
|