1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* aclchk.c
|
1996-07-09 08:22:35 +02:00
|
|
|
* Routines to check access control permissions.
|
|
|
|
*
|
2021-01-02 19:06:25 +01:00
|
|
|
* Portions Copyright (c) 1996-2021, 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
|
|
|
|
* See acl.h.
|
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
1996-11-03 07:54:38 +01:00
|
|
|
#include "postgres.h"
|
|
|
|
|
2019-12-27 00:09:00 +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"
|
tableam: Add and use scan APIs.
Too allow table accesses to be not directly dependent on heap, several
new abstractions are needed. Specifically:
1) Heap scans need to be generalized into table scans. Do this by
introducing TableScanDesc, which will be the "base class" for
individual AMs. This contains the AM independent fields from
HeapScanDesc.
The previous heap_{beginscan,rescan,endscan} et al. have been
replaced with a table_ version.
There's no direct replacement for heap_getnext(), as that returned
a HeapTuple, which is undesirable for a other AMs. Instead there's
table_scan_getnextslot(). But note that heap_getnext() lives on,
it's still used widely to access catalog tables.
This is achieved by new scan_begin, scan_end, scan_rescan,
scan_getnextslot callbacks.
2) The portion of parallel scans that's shared between backends need
to be able to do so without the user doing per-AM work. To achieve
that new parallelscan_{estimate, initialize, reinitialize}
callbacks are introduced, which operate on a new
ParallelTableScanDesc, which again can be subclassed by AMs.
As it is likely that several AMs are going to be block oriented,
block oriented callbacks that can be shared between such AMs are
provided and used by heap. table_block_parallelscan_{estimate,
intiialize, reinitialize} as callbacks, and
table_block_parallelscan_{nextpage, init} for use in AMs. These
operate on a ParallelBlockTableScanDesc.
3) Index scans need to be able to access tables to return a tuple, and
there needs to be state across individual accesses to the heap to
store state like buffers. That's now handled by introducing a
sort-of-scan IndexFetchTable, which again is intended to be
subclassed by individual AMs (for heap IndexFetchHeap).
The relevant callbacks for an AM are index_fetch_{end, begin,
reset} to create the necessary state, and index_fetch_tuple to
retrieve an indexed tuple. Note that index_fetch_tuple
implementations need to be smarter than just blindly fetching the
tuples for AMs that have optimizations similar to heap's HOT - the
currently alive tuple in the update chain needs to be fetched if
appropriate.
Similar to table_scan_getnextslot(), it's undesirable to continue
to return HeapTuples. Thus index_fetch_heap (might want to rename
that later) now accepts a slot as an argument. Core code doesn't
have a lot of call sites performing index scans without going
through the systable_* API (in contrast to loads of heap_getnext
calls and working directly with HeapTuples).
Index scans now store the result of a search in
IndexScanDesc->xs_heaptid, rather than xs_ctup->t_self. As the
target is not generally a HeapTuple anymore that seems cleaner.
To be able to sensible adapt code to use the above, two further
callbacks have been introduced:
a) slot_callbacks returns a TupleTableSlotOps* suitable for creating
slots capable of holding a tuple of the AMs
type. table_slot_callbacks() and table_slot_create() are based
upon that, but have additional logic to deal with views, foreign
tables, etc.
While this change could have been done separately, nearly all the
call sites that needed to be adapted for the rest of this commit
also would have been needed to be adapted for
table_slot_callbacks(), making separation not worthwhile.
b) tuple_satisfies_snapshot checks whether the tuple in a slot is
currently visible according to a snapshot. That's required as a few
places now don't have a buffer + HeapTuple around, but a
slot (which in heap's case internally has that information).
Additionally a few infrastructure changes were needed:
I) SysScanDesc, as used by systable_{beginscan, getnext} et al. now
internally uses a slot to keep track of tuples. While
systable_getnext() still returns HeapTuples, and will so for the
foreseeable future, the index API (see 1) above) now only deals with
slots.
The remainder, and largest part, of this commit is then adjusting all
scans in postgres to use the new APIs.
Author: Andres Freund, Haribabu Kommi, Alvaro Herrera
Discussion:
https://postgr.es/m/20180703070645.wchpu5muyto5n647@alap3.anarazel.de
https://postgr.es/m/20160812231527.GA690404@alvherre.pgsql
2019-03-11 20:46:41 +01:00
|
|
|
#include "access/tableam.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"
|
2017-01-30 05:05:07 +01:00
|
|
|
#include "catalog/pg_aggregate.h"
|
|
|
|
#include "catalog/pg_am.h"
|
2005-06-28 07:09:14 +02:00
|
|
|
#include "catalog/pg_authid.h"
|
2017-01-30 05:05:07 +01:00
|
|
|
#include "catalog/pg_cast.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"
|
Implement multivariate n-distinct coefficients
Add support for explicitly declared statistic objects (CREATE
STATISTICS), allowing collection of statistics on more complex
combinations that individual table columns. Companion commands DROP
STATISTICS and ALTER STATISTICS ... OWNER TO / SET SCHEMA / RENAME are
added too. All this DDL has been designed so that more statistic types
can be added later on, such as multivariate most-common-values and
multivariate histograms between columns of a single table, leaving room
for permitting columns on multiple tables, too, as well as expressions.
This commit only adds support for collection of n-distinct coefficient
on user-specified sets of columns in a single table. This is useful to
estimate number of distinct groups in GROUP BY and DISTINCT clauses;
estimation errors there can cause over-allocation of memory in hashed
aggregates, for instance, so it's a worthwhile problem to solve. A new
special pseudo-type pg_ndistinct is used.
(num-distinct estimation was deemed sufficiently useful by itself that
this is worthwhile even if no further statistic types are added
immediately; so much so that another version of essentially the same
functionality was submitted by Kyotaro Horiguchi:
https://postgr.es/m/20150828.173334.114731693.horiguchi.kyotaro@lab.ntt.co.jp
though this commit does not use that code.)
Author: Tomas Vondra. Some code rework by Álvaro.
Reviewed-by: Dean Rasheed, David Rowley, Kyotaro Horiguchi, Jeff Janes,
Ideriha Takeshi
Discussion: https://postgr.es/m/543AFA15.4080608@fuzzy.cz
https://postgr.es/m/20170320190220.ixlaueanxegqd5gr@alvherre.pgsql
2017-03-24 18:06:10 +01:00
|
|
|
#include "catalog/pg_statistic_ext.h"
|
2017-01-19 18:00:00 +01:00
|
|
|
#include "catalog/pg_subscription.h"
|
2004-06-18 08:14:31 +02:00
|
|
|
#include "catalog/pg_tablespace.h"
|
2019-11-12 04:00:16 +01:00
|
|
|
#include "catalog/pg_transform.h"
|
2007-08-21 03:11:32 +02:00
|
|
|
#include "catalog/pg_ts_config.h"
|
|
|
|
#include "catalog/pg_ts_dict.h"
|
2017-01-30 05:05:07 +01:00
|
|
|
#include "catalog/pg_ts_parser.h"
|
|
|
|
#include "catalog/pg_ts_template.h"
|
2019-11-12 04:00:16 +01:00
|
|
|
#include "catalog/pg_type.h"
|
2006-05-04 00:45:26 +02:00
|
|
|
#include "commands/dbcommands.h"
|
Improve reporting of "conflicting or redundant options" errors.
When reporting "conflicting or redundant options" errors, try to
ensure that errposition() is used, to help the user identify the
offending option.
Formerly, errposition() was invoked in less than 60% of cases. This
patch raises that to over 90%, but there remain a few places where the
ParseState is not readily available. Using errdetail() might improve
the error in such cases, but that is left as a task for the future.
Additionally, since this error is thrown from over 100 places in the
codebase, introduce a dedicated function to throw it, reducing code
duplication.
Extracted from a slightly larger patch by Vignesh C. Reviewed by
Bharath Rupireddy, Alvaro Herrera, Dilip Kumar, Hou Zhijie, Peter
Smith, Daniel Gustafsson, Julien Rouhaud and me.
Discussion: https://postgr.es/m/CALDaNm33FFSS5tVyvmkoK2cCMuDVxcui=gFrjti9ROfynqSAGA@mail.gmail.com
2021-07-15 09:49:45 +02:00
|
|
|
#include "commands/defrem.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"
|
|
|
|
|
2009-10-05 21:24:49 +02:00
|
|
|
/*
|
|
|
|
* Internal format used by ALTER DEFAULT PRIVILEGES.
|
|
|
|
*/
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
Oid roleid; /* owning role */
|
|
|
|
Oid nspid; /* namespace, or InvalidOid if none */
|
|
|
|
/* remaining fields are same as in InternalGrant: */
|
|
|
|
bool is_grant;
|
2017-10-12 00:35:19 +02:00
|
|
|
ObjectType objtype;
|
2009-10-05 21:24:49 +02:00
|
|
|
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);
|
|
|
|
|
2017-10-12 00:35:19 +02:00
|
|
|
static List *objectNamesToOids(ObjectType objtype, List *objnames);
|
|
|
|
static List *objectsInSchemaToOids(ObjectType objtype, List *nspnames);
|
2009-10-12 22:39:42 +02:00
|
|
|
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,
|
|
|
|
AclMode this_privileges,
|
|
|
|
AclMode *col_privileges,
|
|
|
|
int num_col_privileges);
|
|
|
|
static void expand_all_col_privileges(Oid table_oid, Form_pg_class classForm,
|
|
|
|
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,
|
|
|
|
bool all_privs, AclMode privileges,
|
|
|
|
Oid objectId, Oid grantorId,
|
2017-12-02 15:26:34 +01:00
|
|
|
ObjectType objtype, const char *objname,
|
2009-01-22 21:16:10 +01:00
|
|
|
AttrNumber att_number, const char *colname);
|
2017-12-02 15:26:34 +01:00
|
|
|
static AclMode pg_aclmask(ObjectType objtype, Oid table_oid, AttrNumber attnum,
|
2009-01-22 21:16:10 +01: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);
|
2017-01-30 05:05:07 +01:00
|
|
|
static void recordExtensionInitPrivWorker(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
|
|
|
|
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
|
|
|
new_acl = old_acl;
|
|
|
|
|
|
|
|
foreach(j, grantees)
|
|
|
|
{
|
2005-11-21 13:49:33 +01:00
|
|
|
AclItem aclitem;
|
2003-09-04 17:53:04 +02:00
|
|
|
Acl *newer_acl;
|
2002-02-19 00:11:58 +01:00
|
|
|
|
2009-01-22 21:16:10 +01: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
|
|
|
|
2009-01-22 21:16:10 +01: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
|
|
|
|
* 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.
|
|
|
|
*/
|
2005-06-28 07:09:14 +02:00
|
|
|
ACLITEM_SET_PRIVS_GOPTIONS(aclitem,
|
2003-10-05 23:49:12 +02:00
|
|
|
(is_grant || !grant_option) ? privileges : ACL_NO_RIGHTS,
|
2005-06-28 07:09:14 +02:00
|
|
|
(!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
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2017-12-02 15:26:34 +01:00
|
|
|
ObjectType objtype, const char *objname,
|
2009-01-22 21:16:10 +01:00
|
|
|
AttrNumber att_number, const char *colname)
|
2005-12-01 03:03:01 +01:00
|
|
|
{
|
|
|
|
AclMode this_privileges;
|
|
|
|
AclMode whole_mask;
|
|
|
|
|
2017-12-02 15:26:34 +01:00
|
|
|
switch (objtype)
|
2005-12-01 03:03:01 +01:00
|
|
|
{
|
2017-12-02 15:26:34 +01:00
|
|
|
case OBJECT_COLUMN:
|
2009-01-22 21:16:10 +01:00
|
|
|
whole_mask = ACL_ALL_RIGHTS_COLUMN;
|
|
|
|
break;
|
2017-12-02 15:26:34 +01:00
|
|
|
case OBJECT_TABLE:
|
2005-12-01 03:03:01 +01:00
|
|
|
whole_mask = ACL_ALL_RIGHTS_RELATION;
|
|
|
|
break;
|
2017-12-02 15:26:34 +01:00
|
|
|
case OBJECT_SEQUENCE:
|
2006-01-21 03:16:21 +01:00
|
|
|
whole_mask = ACL_ALL_RIGHTS_SEQUENCE;
|
|
|
|
break;
|
2017-12-02 15:26:34 +01:00
|
|
|
case OBJECT_DATABASE:
|
2005-12-01 03:03:01 +01:00
|
|
|
whole_mask = ACL_ALL_RIGHTS_DATABASE;
|
|
|
|
break;
|
2017-12-02 15:26:34 +01:00
|
|
|
case OBJECT_FUNCTION:
|
2005-12-01 03:03:01 +01:00
|
|
|
whole_mask = ACL_ALL_RIGHTS_FUNCTION;
|
|
|
|
break;
|
2017-12-02 15:26:34 +01:00
|
|
|
case OBJECT_LANGUAGE:
|
2005-12-01 03:03:01 +01:00
|
|
|
whole_mask = ACL_ALL_RIGHTS_LANGUAGE;
|
|
|
|
break;
|
2017-12-02 15:26:34 +01:00
|
|
|
case OBJECT_LARGEOBJECT:
|
2009-12-11 04:34:57 +01:00
|
|
|
whole_mask = ACL_ALL_RIGHTS_LARGEOBJECT;
|
|
|
|
break;
|
2017-12-02 15:26:34 +01:00
|
|
|
case OBJECT_SCHEMA:
|
2017-10-12 00:35:19 +02:00
|
|
|
whole_mask = ACL_ALL_RIGHTS_SCHEMA;
|
2005-12-01 03:03:01 +01:00
|
|
|
break;
|
2017-12-02 15:26:34 +01:00
|
|
|
case OBJECT_TABLESPACE:
|
2005-12-01 03:03:01 +01:00
|
|
|
whole_mask = ACL_ALL_RIGHTS_TABLESPACE;
|
|
|
|
break;
|
2017-12-02 15:26:34 +01:00
|
|
|
case OBJECT_FDW:
|
2008-12-19 17:25:19 +01:00
|
|
|
whole_mask = ACL_ALL_RIGHTS_FDW;
|
|
|
|
break;
|
2017-12-02 15:26:34 +01:00
|
|
|
case OBJECT_FOREIGN_SERVER:
|
2008-12-19 17:25:19 +01:00
|
|
|
whole_mask = ACL_ALL_RIGHTS_FOREIGN_SERVER;
|
|
|
|
break;
|
2017-12-02 15:26:34 +01:00
|
|
|
case OBJECT_EVENT_TRIGGER:
|
2012-07-18 16:16:16 +02:00
|
|
|
elog(ERROR, "grantable rights not supported for event triggers");
|
|
|
|
/* not reached, but keep compiler quiet */
|
|
|
|
return ACL_NO_RIGHTS;
|
2017-12-02 15:26:34 +01:00
|
|
|
case OBJECT_TYPE:
|
2011-12-19 23:05:19 +01:00
|
|
|
whole_mask = ACL_ALL_RIGHTS_TYPE;
|
|
|
|
break;
|
2005-12-01 03:03:01 +01:00
|
|
|
default:
|
2017-12-02 15:26:34 +01:00
|
|
|
elog(ERROR, "unrecognized object type: %d", objtype);
|
2005-12-01 03:03:01 +01:00
|
|
|
/* not reached, but keep compiler quiet */
|
|
|
|
return ACL_NO_RIGHTS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
if (avail_goptions == ACL_NO_RIGHTS)
|
|
|
|
{
|
2017-12-02 15:26:34 +01:00
|
|
|
if (pg_aclmask(objtype, 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
|
|
|
{
|
2017-12-02 15:26:34 +01:00
|
|
|
if (objtype == OBJECT_COLUMN && colname)
|
|
|
|
aclcheck_error_col(ACLCHECK_NO_PRIV, objtype, objname, colname);
|
2009-01-22 21:16:10 +01:00
|
|
|
else
|
2017-12-02 15:26:34 +01:00
|
|
|
aclcheck_error(ACLCHECK_NO_PRIV, objtype, objname);
|
2009-01-22 21:16:10 +01:00
|
|
|
}
|
2005-12-01 03:03:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Restrict the operation to what we can actually grant or revoke, and
|
|
|
|
* issue a warning if appropriate. (For REVOKE this isn't quite what the
|
|
|
|
* 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.)
|
|
|
|
*/
|
|
|
|
this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions);
|
|
|
|
if (is_grant)
|
|
|
|
{
|
|
|
|
if (this_privileges == 0)
|
2010-03-07 00:10:42 +01:00
|
|
|
{
|
2017-12-02 15:26:34 +01:00
|
|
|
if (objtype == OBJECT_COLUMN && colname)
|
2010-03-07 00:10:42 +01:00
|
|
|
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
|
|
|
{
|
2017-12-02 15:26:34 +01:00
|
|
|
if (objtype == OBJECT_COLUMN && colname)
|
2010-03-07 00:10:42 +01:00
|
|
|
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
|
|
|
{
|
2017-12-02 15:26:34 +01:00
|
|
|
if (objtype == OBJECT_COLUMN && colname)
|
2010-03-07 00:10:42 +01:00
|
|
|
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
|
|
|
{
|
2017-12-02 15:26:34 +01:00
|
|
|
if (objtype == OBJECT_COLUMN && colname)
|
2010-03-07 00:10:42 +01:00
|
|
|
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),
|
|
|
|
errmsg("not all privileges could be revoked for \"%s\"",
|
|
|
|
objname)));
|
|
|
|
}
|
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;
|
|
|
|
|
2021-01-30 09:41:44 +01:00
|
|
|
if (stmt->grantor)
|
|
|
|
{
|
|
|
|
Oid grantor;
|
|
|
|
|
|
|
|
grantor = get_rolespec_oid(stmt->grantor, false);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Currently, this clause is only for SQL compatibility, not very
|
|
|
|
* interesting otherwise.
|
|
|
|
*/
|
|
|
|
if (grantor != GetUserId())
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
|
|
errmsg("grantor must be current user")));
|
|
|
|
}
|
|
|
|
|
2005-12-01 03:03:01 +01:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
/* ACL_TARGET_DEFAULTS should not be seen here */
|
|
|
|
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
|
|
|
/*
|
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
|
|
|
* 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
|
|
|
|
* there shouldn't be any additional work needed to support this case.
|
2005-11-21 13:49:33 +01:00
|
|
|
*/
|
|
|
|
foreach(cell, stmt->grantees)
|
|
|
|
{
|
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
|
|
|
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:
|
2016-12-28 18:00:00 +01:00
|
|
|
grantee_uid = get_rolespec_oid(grantee, false);
|
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
|
|
|
break;
|
|
|
|
}
|
|
|
|
istmt.grantees = lappend_oid(istmt.grantees, grantee_uid);
|
2005-11-21 13:49:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2009-01-22 21:16:10 +01:00
|
|
|
* Convert stmt->privileges, a list of AccessPriv nodes, into an AclMode
|
2017-10-12 00:35:19 +02:00
|
|
|
* bitmask. Note: objtype can't be 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
|
|
|
{
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_TABLE:
|
2018-04-26 20:47:16 +02:00
|
|
|
|
2006-01-21 03:16:21 +01: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.
|
|
|
|
*/
|
|
|
|
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;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_SEQUENCE:
|
2006-01-21 03:16:21 +01:00
|
|
|
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;
|
2017-10-12 00:35:19 +02:00
|
|
|
case 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;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_DOMAIN:
|
2011-12-19 23:05:19 +01:00
|
|
|
all_privileges = ACL_ALL_RIGHTS_TYPE;
|
|
|
|
errormsg = gettext_noop("invalid privilege type %s for domain");
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case 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;
|
2017-10-12 00:35:19 +02:00
|
|
|
case 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;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_LARGEOBJECT:
|
2009-12-11 04:34:57 +01:00
|
|
|
all_privileges = ACL_ALL_RIGHTS_LARGEOBJECT;
|
|
|
|
errormsg = gettext_noop("invalid privilege type %s for large object");
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_SCHEMA:
|
|
|
|
all_privileges = ACL_ALL_RIGHTS_SCHEMA;
|
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;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_PROCEDURE:
|
2017-11-30 14:46:13 +01:00
|
|
|
all_privileges = ACL_ALL_RIGHTS_FUNCTION;
|
|
|
|
errormsg = gettext_noop("invalid privilege type %s for procedure");
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_ROUTINE:
|
2017-11-30 14:46:13 +01:00
|
|
|
all_privileges = ACL_ALL_RIGHTS_FUNCTION;
|
|
|
|
errormsg = gettext_noop("invalid privilege type %s for routine");
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case 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;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_TYPE:
|
2011-12-19 23:05:19 +01:00
|
|
|
all_privileges = ACL_ALL_RIGHTS_TYPE;
|
|
|
|
errormsg = gettext_noop("invalid privilege type %s for type");
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_FDW:
|
2008-12-19 17:25:19 +01:00
|
|
|
all_privileges = ACL_ALL_RIGHTS_FDW;
|
|
|
|
errormsg = gettext_noop("invalid privilege type %s for foreign-data wrapper");
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_FOREIGN_SERVER:
|
2008-12-19 17:25:19 +01:00
|
|
|
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;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
if (privnode->cols)
|
|
|
|
{
|
2017-10-12 00:35:19 +02:00
|
|
|
if (stmt->objtype != OBJECT_TABLE)
|
2009-01-22 21:16:10 +01:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
|
|
|
|
errmsg("column privileges are only valid for relations")));
|
|
|
|
istmt.col_privs = lappend(istmt.col_privs, privnode);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (privnode->priv_name == NULL) /* parser mistake? */
|
|
|
|
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
|
|
|
{
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_TABLE:
|
|
|
|
case OBJECT_SEQUENCE:
|
2005-12-01 03:03:01 +01:00
|
|
|
ExecGrant_Relation(istmt);
|
2005-11-21 13:49:33 +01:00
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_DATABASE:
|
2005-12-01 03:03:01 +01:00
|
|
|
ExecGrant_Database(istmt);
|
2005-11-21 13:49:33 +01:00
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_DOMAIN:
|
|
|
|
case OBJECT_TYPE:
|
2011-12-19 23:05:19 +01:00
|
|
|
ExecGrant_Type(istmt);
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_FDW:
|
2008-12-19 17:25:19 +01:00
|
|
|
ExecGrant_Fdw(istmt);
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_FOREIGN_SERVER:
|
2008-12-19 17:25:19 +01:00
|
|
|
ExecGrant_ForeignServer(istmt);
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_FUNCTION:
|
|
|
|
case OBJECT_PROCEDURE:
|
|
|
|
case OBJECT_ROUTINE:
|
2005-12-01 03:03:01 +01:00
|
|
|
ExecGrant_Function(istmt);
|
2005-11-21 13:49:33 +01:00
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_LANGUAGE:
|
2005-12-01 03:03:01 +01:00
|
|
|
ExecGrant_Language(istmt);
|
2005-11-21 13:49:33 +01:00
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_LARGEOBJECT:
|
2009-12-11 04:34:57 +01:00
|
|
|
ExecGrant_Largeobject(istmt);
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_SCHEMA:
|
2005-12-01 03:03:01 +01:00
|
|
|
ExecGrant_Namespace(istmt);
|
2005-11-21 13:49:33 +01:00
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case 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.
|
|
|
|
*/
|
2017-10-12 00:35:19 +02:00
|
|
|
if (EventTriggerSupportsObjectType(istmt->objtype))
|
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
|
|
|
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 *
|
2017-10-12 00:35:19 +02:00
|
|
|
objectNamesToOids(ObjectType objtype, List *objnames)
|
2005-11-21 13:49:33 +01:00
|
|
|
{
|
|
|
|
List *objects = NIL;
|
|
|
|
ListCell *cell;
|
|
|
|
|
|
|
|
Assert(objnames != NIL);
|
|
|
|
|
|
|
|
switch (objtype)
|
|
|
|
{
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_TABLE:
|
|
|
|
case 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;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_DATABASE:
|
2005-11-21 13:49:33 +01:00
|
|
|
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;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_DOMAIN:
|
|
|
|
case OBJECT_TYPE:
|
2011-12-19 23:05:19 +01:00
|
|
|
foreach(cell, objnames)
|
|
|
|
{
|
|
|
|
List *typname = (List *) lfirst(cell);
|
|
|
|
Oid oid;
|
|
|
|
|
|
|
|
oid = typenameTypeId(NULL, makeTypeNameFromNameList(typname));
|
|
|
|
objects = lappend_oid(objects, oid);
|
|
|
|
}
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_FUNCTION:
|
2005-11-21 13:49:33 +01:00
|
|
|
foreach(cell, objnames)
|
|
|
|
{
|
2016-12-28 18:00:00 +01:00
|
|
|
ObjectWithArgs *func = (ObjectWithArgs *) lfirst(cell);
|
2005-11-21 13:49:33 +01:00
|
|
|
Oid funcid;
|
|
|
|
|
2017-11-30 14:46:13 +01:00
|
|
|
funcid = LookupFuncWithArgs(OBJECT_FUNCTION, func, false);
|
2005-11-21 13:49:33 +01:00
|
|
|
objects = lappend_oid(objects, funcid);
|
|
|
|
}
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_LANGUAGE:
|
2005-11-21 13:49:33 +01:00
|
|
|
foreach(cell, objnames)
|
|
|
|
{
|
|
|
|
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;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_LARGEOBJECT:
|
2009-12-11 04:34:57 +01:00
|
|
|
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;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_SCHEMA:
|
2005-11-21 13:49:33 +01:00
|
|
|
foreach(cell, objnames)
|
|
|
|
{
|
|
|
|
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;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_PROCEDURE:
|
2017-11-30 14:46:13 +01:00
|
|
|
foreach(cell, objnames)
|
|
|
|
{
|
|
|
|
ObjectWithArgs *func = (ObjectWithArgs *) lfirst(cell);
|
|
|
|
Oid procid;
|
|
|
|
|
|
|
|
procid = LookupFuncWithArgs(OBJECT_PROCEDURE, func, false);
|
|
|
|
objects = lappend_oid(objects, procid);
|
|
|
|
}
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_ROUTINE:
|
2017-11-30 14:46:13 +01:00
|
|
|
foreach(cell, objnames)
|
|
|
|
{
|
|
|
|
ObjectWithArgs *func = (ObjectWithArgs *) lfirst(cell);
|
|
|
|
Oid routid;
|
|
|
|
|
|
|
|
routid = LookupFuncWithArgs(OBJECT_ROUTINE, func, false);
|
|
|
|
objects = lappend_oid(objects, routid);
|
|
|
|
}
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_TABLESPACE:
|
2005-11-21 13:49:33 +01:00
|
|
|
foreach(cell, objnames)
|
|
|
|
{
|
|
|
|
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;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_FDW:
|
2008-12-19 17:25:19 +01:00
|
|
|
foreach(cell, objnames)
|
|
|
|
{
|
|
|
|
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;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_FOREIGN_SERVER:
|
2008-12-19 17:25:19 +01:00
|
|
|
foreach(cell, objnames)
|
|
|
|
{
|
|
|
|
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 *
|
2017-10-12 00:35:19 +02:00
|
|
|
objectsInSchemaToOids(ObjectType objtype, List *nspnames)
|
2009-10-12 22:39:42 +02:00
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_TABLE:
|
2009-10-12 22:39:42 +02:00
|
|
|
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;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_SEQUENCE:
|
2009-10-12 22:39:42 +02:00
|
|
|
objs = getRelationsInNamespace(namespaceId, RELKIND_SEQUENCE);
|
|
|
|
objects = list_concat(objects, objs);
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_FUNCTION:
|
|
|
|
case OBJECT_PROCEDURE:
|
|
|
|
case OBJECT_ROUTINE:
|
2009-10-12 22:39:42 +02:00
|
|
|
{
|
2017-11-30 14:46:13 +01:00
|
|
|
ScanKeyData key[2];
|
|
|
|
int keycount;
|
2009-10-12 22:39:42 +02:00
|
|
|
Relation rel;
|
tableam: Add and use scan APIs.
Too allow table accesses to be not directly dependent on heap, several
new abstractions are needed. Specifically:
1) Heap scans need to be generalized into table scans. Do this by
introducing TableScanDesc, which will be the "base class" for
individual AMs. This contains the AM independent fields from
HeapScanDesc.
The previous heap_{beginscan,rescan,endscan} et al. have been
replaced with a table_ version.
There's no direct replacement for heap_getnext(), as that returned
a HeapTuple, which is undesirable for a other AMs. Instead there's
table_scan_getnextslot(). But note that heap_getnext() lives on,
it's still used widely to access catalog tables.
This is achieved by new scan_begin, scan_end, scan_rescan,
scan_getnextslot callbacks.
2) The portion of parallel scans that's shared between backends need
to be able to do so without the user doing per-AM work. To achieve
that new parallelscan_{estimate, initialize, reinitialize}
callbacks are introduced, which operate on a new
ParallelTableScanDesc, which again can be subclassed by AMs.
As it is likely that several AMs are going to be block oriented,
block oriented callbacks that can be shared between such AMs are
provided and used by heap. table_block_parallelscan_{estimate,
intiialize, reinitialize} as callbacks, and
table_block_parallelscan_{nextpage, init} for use in AMs. These
operate on a ParallelBlockTableScanDesc.
3) Index scans need to be able to access tables to return a tuple, and
there needs to be state across individual accesses to the heap to
store state like buffers. That's now handled by introducing a
sort-of-scan IndexFetchTable, which again is intended to be
subclassed by individual AMs (for heap IndexFetchHeap).
The relevant callbacks for an AM are index_fetch_{end, begin,
reset} to create the necessary state, and index_fetch_tuple to
retrieve an indexed tuple. Note that index_fetch_tuple
implementations need to be smarter than just blindly fetching the
tuples for AMs that have optimizations similar to heap's HOT - the
currently alive tuple in the update chain needs to be fetched if
appropriate.
Similar to table_scan_getnextslot(), it's undesirable to continue
to return HeapTuples. Thus index_fetch_heap (might want to rename
that later) now accepts a slot as an argument. Core code doesn't
have a lot of call sites performing index scans without going
through the systable_* API (in contrast to loads of heap_getnext
calls and working directly with HeapTuples).
Index scans now store the result of a search in
IndexScanDesc->xs_heaptid, rather than xs_ctup->t_self. As the
target is not generally a HeapTuple anymore that seems cleaner.
To be able to sensible adapt code to use the above, two further
callbacks have been introduced:
a) slot_callbacks returns a TupleTableSlotOps* suitable for creating
slots capable of holding a tuple of the AMs
type. table_slot_callbacks() and table_slot_create() are based
upon that, but have additional logic to deal with views, foreign
tables, etc.
While this change could have been done separately, nearly all the
call sites that needed to be adapted for the rest of this commit
also would have been needed to be adapted for
table_slot_callbacks(), making separation not worthwhile.
b) tuple_satisfies_snapshot checks whether the tuple in a slot is
currently visible according to a snapshot. That's required as a few
places now don't have a buffer + HeapTuple around, but a
slot (which in heap's case internally has that information).
Additionally a few infrastructure changes were needed:
I) SysScanDesc, as used by systable_{beginscan, getnext} et al. now
internally uses a slot to keep track of tuples. While
systable_getnext() still returns HeapTuples, and will so for the
foreseeable future, the index API (see 1) above) now only deals with
slots.
The remainder, and largest part, of this commit is then adjusting all
scans in postgres to use the new APIs.
Author: Andres Freund, Haribabu Kommi, Alvaro Herrera
Discussion:
https://postgr.es/m/20180703070645.wchpu5muyto5n647@alap3.anarazel.de
https://postgr.es/m/20160812231527.GA690404@alvherre.pgsql
2019-03-11 20:46:41 +01:00
|
|
|
TableScanDesc scan;
|
2009-10-12 22:39:42 +02:00
|
|
|
HeapTuple tuple;
|
|
|
|
|
2017-11-30 14:46:13 +01:00
|
|
|
keycount = 0;
|
|
|
|
ScanKeyInit(&key[keycount++],
|
2009-10-12 22:39:42 +02:00
|
|
|
Anum_pg_proc_pronamespace,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(namespaceId));
|
|
|
|
|
2017-10-12 00:35:19 +02:00
|
|
|
if (objtype == OBJECT_FUNCTION)
|
2018-03-02 14:57:38 +01:00
|
|
|
/* includes aggregates and window functions */
|
2017-11-30 14:46:13 +01:00
|
|
|
ScanKeyInit(&key[keycount++],
|
2018-03-02 14:57:38 +01:00
|
|
|
Anum_pg_proc_prokind,
|
|
|
|
BTEqualStrategyNumber, F_CHARNE,
|
|
|
|
CharGetDatum(PROKIND_PROCEDURE));
|
2017-10-12 00:35:19 +02:00
|
|
|
else if (objtype == OBJECT_PROCEDURE)
|
2017-11-30 14:46:13 +01:00
|
|
|
ScanKeyInit(&key[keycount++],
|
2018-03-02 14:57:38 +01:00
|
|
|
Anum_pg_proc_prokind,
|
|
|
|
BTEqualStrategyNumber, F_CHAREQ,
|
|
|
|
CharGetDatum(PROKIND_PROCEDURE));
|
2017-11-30 14:46:13 +01:00
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
rel = table_open(ProcedureRelationId, AccessShareLock);
|
tableam: Add and use scan APIs.
Too allow table accesses to be not directly dependent on heap, several
new abstractions are needed. Specifically:
1) Heap scans need to be generalized into table scans. Do this by
introducing TableScanDesc, which will be the "base class" for
individual AMs. This contains the AM independent fields from
HeapScanDesc.
The previous heap_{beginscan,rescan,endscan} et al. have been
replaced with a table_ version.
There's no direct replacement for heap_getnext(), as that returned
a HeapTuple, which is undesirable for a other AMs. Instead there's
table_scan_getnextslot(). But note that heap_getnext() lives on,
it's still used widely to access catalog tables.
This is achieved by new scan_begin, scan_end, scan_rescan,
scan_getnextslot callbacks.
2) The portion of parallel scans that's shared between backends need
to be able to do so without the user doing per-AM work. To achieve
that new parallelscan_{estimate, initialize, reinitialize}
callbacks are introduced, which operate on a new
ParallelTableScanDesc, which again can be subclassed by AMs.
As it is likely that several AMs are going to be block oriented,
block oriented callbacks that can be shared between such AMs are
provided and used by heap. table_block_parallelscan_{estimate,
intiialize, reinitialize} as callbacks, and
table_block_parallelscan_{nextpage, init} for use in AMs. These
operate on a ParallelBlockTableScanDesc.
3) Index scans need to be able to access tables to return a tuple, and
there needs to be state across individual accesses to the heap to
store state like buffers. That's now handled by introducing a
sort-of-scan IndexFetchTable, which again is intended to be
subclassed by individual AMs (for heap IndexFetchHeap).
The relevant callbacks for an AM are index_fetch_{end, begin,
reset} to create the necessary state, and index_fetch_tuple to
retrieve an indexed tuple. Note that index_fetch_tuple
implementations need to be smarter than just blindly fetching the
tuples for AMs that have optimizations similar to heap's HOT - the
currently alive tuple in the update chain needs to be fetched if
appropriate.
Similar to table_scan_getnextslot(), it's undesirable to continue
to return HeapTuples. Thus index_fetch_heap (might want to rename
that later) now accepts a slot as an argument. Core code doesn't
have a lot of call sites performing index scans without going
through the systable_* API (in contrast to loads of heap_getnext
calls and working directly with HeapTuples).
Index scans now store the result of a search in
IndexScanDesc->xs_heaptid, rather than xs_ctup->t_self. As the
target is not generally a HeapTuple anymore that seems cleaner.
To be able to sensible adapt code to use the above, two further
callbacks have been introduced:
a) slot_callbacks returns a TupleTableSlotOps* suitable for creating
slots capable of holding a tuple of the AMs
type. table_slot_callbacks() and table_slot_create() are based
upon that, but have additional logic to deal with views, foreign
tables, etc.
While this change could have been done separately, nearly all the
call sites that needed to be adapted for the rest of this commit
also would have been needed to be adapted for
table_slot_callbacks(), making separation not worthwhile.
b) tuple_satisfies_snapshot checks whether the tuple in a slot is
currently visible according to a snapshot. That's required as a few
places now don't have a buffer + HeapTuple around, but a
slot (which in heap's case internally has that information).
Additionally a few infrastructure changes were needed:
I) SysScanDesc, as used by systable_{beginscan, getnext} et al. now
internally uses a slot to keep track of tuples. While
systable_getnext() still returns HeapTuples, and will so for the
foreseeable future, the index API (see 1) above) now only deals with
slots.
The remainder, and largest part, of this commit is then adjusting all
scans in postgres to use the new APIs.
Author: Andres Freund, Haribabu Kommi, Alvaro Herrera
Discussion:
https://postgr.es/m/20180703070645.wchpu5muyto5n647@alap3.anarazel.de
https://postgr.es/m/20160812231527.GA690404@alvherre.pgsql
2019-03-11 20:46:41 +01:00
|
|
|
scan = table_beginscan_catalog(rel, keycount, key);
|
2009-10-12 22:39:42 +02:00
|
|
|
|
|
|
|
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
|
|
|
|
{
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
Oid oid = ((Form_pg_proc) GETSTRUCT(tuple))->oid;
|
|
|
|
|
|
|
|
objects = lappend_oid(objects, oid);
|
2009-10-12 22:39:42 +02:00
|
|
|
}
|
|
|
|
|
tableam: Add and use scan APIs.
Too allow table accesses to be not directly dependent on heap, several
new abstractions are needed. Specifically:
1) Heap scans need to be generalized into table scans. Do this by
introducing TableScanDesc, which will be the "base class" for
individual AMs. This contains the AM independent fields from
HeapScanDesc.
The previous heap_{beginscan,rescan,endscan} et al. have been
replaced with a table_ version.
There's no direct replacement for heap_getnext(), as that returned
a HeapTuple, which is undesirable for a other AMs. Instead there's
table_scan_getnextslot(). But note that heap_getnext() lives on,
it's still used widely to access catalog tables.
This is achieved by new scan_begin, scan_end, scan_rescan,
scan_getnextslot callbacks.
2) The portion of parallel scans that's shared between backends need
to be able to do so without the user doing per-AM work. To achieve
that new parallelscan_{estimate, initialize, reinitialize}
callbacks are introduced, which operate on a new
ParallelTableScanDesc, which again can be subclassed by AMs.
As it is likely that several AMs are going to be block oriented,
block oriented callbacks that can be shared between such AMs are
provided and used by heap. table_block_parallelscan_{estimate,
intiialize, reinitialize} as callbacks, and
table_block_parallelscan_{nextpage, init} for use in AMs. These
operate on a ParallelBlockTableScanDesc.
3) Index scans need to be able to access tables to return a tuple, and
there needs to be state across individual accesses to the heap to
store state like buffers. That's now handled by introducing a
sort-of-scan IndexFetchTable, which again is intended to be
subclassed by individual AMs (for heap IndexFetchHeap).
The relevant callbacks for an AM are index_fetch_{end, begin,
reset} to create the necessary state, and index_fetch_tuple to
retrieve an indexed tuple. Note that index_fetch_tuple
implementations need to be smarter than just blindly fetching the
tuples for AMs that have optimizations similar to heap's HOT - the
currently alive tuple in the update chain needs to be fetched if
appropriate.
Similar to table_scan_getnextslot(), it's undesirable to continue
to return HeapTuples. Thus index_fetch_heap (might want to rename
that later) now accepts a slot as an argument. Core code doesn't
have a lot of call sites performing index scans without going
through the systable_* API (in contrast to loads of heap_getnext
calls and working directly with HeapTuples).
Index scans now store the result of a search in
IndexScanDesc->xs_heaptid, rather than xs_ctup->t_self. As the
target is not generally a HeapTuple anymore that seems cleaner.
To be able to sensible adapt code to use the above, two further
callbacks have been introduced:
a) slot_callbacks returns a TupleTableSlotOps* suitable for creating
slots capable of holding a tuple of the AMs
type. table_slot_callbacks() and table_slot_create() are based
upon that, but have additional logic to deal with views, foreign
tables, etc.
While this change could have been done separately, nearly all the
call sites that needed to be adapted for the rest of this commit
also would have been needed to be adapted for
table_slot_callbacks(), making separation not worthwhile.
b) tuple_satisfies_snapshot checks whether the tuple in a slot is
currently visible according to a snapshot. That's required as a few
places now don't have a buffer + HeapTuple around, but a
slot (which in heap's case internally has that information).
Additionally a few infrastructure changes were needed:
I) SysScanDesc, as used by systable_{beginscan, getnext} et al. now
internally uses a slot to keep track of tuples. While
systable_getnext() still returns HeapTuples, and will so for the
foreseeable future, the index API (see 1) above) now only deals with
slots.
The remainder, and largest part, of this commit is then adjusting all
scans in postgres to use the new APIs.
Author: Andres Freund, Haribabu Kommi, Alvaro Herrera
Discussion:
https://postgr.es/m/20180703070645.wchpu5muyto5n647@alap3.anarazel.de
https://postgr.es/m/20160812231527.GA690404@alvherre.pgsql
2019-03-11 20:46:41 +01:00
|
|
|
table_endscan(scan);
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(rel, AccessShareLock);
|
2009-10-12 22:39:42 +02:00
|
|
|
}
|
|
|
|
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;
|
tableam: Add and use scan APIs.
Too allow table accesses to be not directly dependent on heap, several
new abstractions are needed. Specifically:
1) Heap scans need to be generalized into table scans. Do this by
introducing TableScanDesc, which will be the "base class" for
individual AMs. This contains the AM independent fields from
HeapScanDesc.
The previous heap_{beginscan,rescan,endscan} et al. have been
replaced with a table_ version.
There's no direct replacement for heap_getnext(), as that returned
a HeapTuple, which is undesirable for a other AMs. Instead there's
table_scan_getnextslot(). But note that heap_getnext() lives on,
it's still used widely to access catalog tables.
This is achieved by new scan_begin, scan_end, scan_rescan,
scan_getnextslot callbacks.
2) The portion of parallel scans that's shared between backends need
to be able to do so without the user doing per-AM work. To achieve
that new parallelscan_{estimate, initialize, reinitialize}
callbacks are introduced, which operate on a new
ParallelTableScanDesc, which again can be subclassed by AMs.
As it is likely that several AMs are going to be block oriented,
block oriented callbacks that can be shared between such AMs are
provided and used by heap. table_block_parallelscan_{estimate,
intiialize, reinitialize} as callbacks, and
table_block_parallelscan_{nextpage, init} for use in AMs. These
operate on a ParallelBlockTableScanDesc.
3) Index scans need to be able to access tables to return a tuple, and
there needs to be state across individual accesses to the heap to
store state like buffers. That's now handled by introducing a
sort-of-scan IndexFetchTable, which again is intended to be
subclassed by individual AMs (for heap IndexFetchHeap).
The relevant callbacks for an AM are index_fetch_{end, begin,
reset} to create the necessary state, and index_fetch_tuple to
retrieve an indexed tuple. Note that index_fetch_tuple
implementations need to be smarter than just blindly fetching the
tuples for AMs that have optimizations similar to heap's HOT - the
currently alive tuple in the update chain needs to be fetched if
appropriate.
Similar to table_scan_getnextslot(), it's undesirable to continue
to return HeapTuples. Thus index_fetch_heap (might want to rename
that later) now accepts a slot as an argument. Core code doesn't
have a lot of call sites performing index scans without going
through the systable_* API (in contrast to loads of heap_getnext
calls and working directly with HeapTuples).
Index scans now store the result of a search in
IndexScanDesc->xs_heaptid, rather than xs_ctup->t_self. As the
target is not generally a HeapTuple anymore that seems cleaner.
To be able to sensible adapt code to use the above, two further
callbacks have been introduced:
a) slot_callbacks returns a TupleTableSlotOps* suitable for creating
slots capable of holding a tuple of the AMs
type. table_slot_callbacks() and table_slot_create() are based
upon that, but have additional logic to deal with views, foreign
tables, etc.
While this change could have been done separately, nearly all the
call sites that needed to be adapted for the rest of this commit
also would have been needed to be adapted for
table_slot_callbacks(), making separation not worthwhile.
b) tuple_satisfies_snapshot checks whether the tuple in a slot is
currently visible according to a snapshot. That's required as a few
places now don't have a buffer + HeapTuple around, but a
slot (which in heap's case internally has that information).
Additionally a few infrastructure changes were needed:
I) SysScanDesc, as used by systable_{beginscan, getnext} et al. now
internally uses a slot to keep track of tuples. While
systable_getnext() still returns HeapTuples, and will so for the
foreseeable future, the index API (see 1) above) now only deals with
slots.
The remainder, and largest part, of this commit is then adjusting all
scans in postgres to use the new APIs.
Author: Andres Freund, Haribabu Kommi, Alvaro Herrera
Discussion:
https://postgr.es/m/20180703070645.wchpu5muyto5n647@alap3.anarazel.de
https://postgr.es/m/20160812231527.GA690404@alvherre.pgsql
2019-03-11 20:46:41 +01:00
|
|
|
TableScanDesc scan;
|
2009-10-12 22:39:42 +02:00
|
|
|
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));
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
rel = table_open(RelationRelationId, AccessShareLock);
|
tableam: Add and use scan APIs.
Too allow table accesses to be not directly dependent on heap, several
new abstractions are needed. Specifically:
1) Heap scans need to be generalized into table scans. Do this by
introducing TableScanDesc, which will be the "base class" for
individual AMs. This contains the AM independent fields from
HeapScanDesc.
The previous heap_{beginscan,rescan,endscan} et al. have been
replaced with a table_ version.
There's no direct replacement for heap_getnext(), as that returned
a HeapTuple, which is undesirable for a other AMs. Instead there's
table_scan_getnextslot(). But note that heap_getnext() lives on,
it's still used widely to access catalog tables.
This is achieved by new scan_begin, scan_end, scan_rescan,
scan_getnextslot callbacks.
2) The portion of parallel scans that's shared between backends need
to be able to do so without the user doing per-AM work. To achieve
that new parallelscan_{estimate, initialize, reinitialize}
callbacks are introduced, which operate on a new
ParallelTableScanDesc, which again can be subclassed by AMs.
As it is likely that several AMs are going to be block oriented,
block oriented callbacks that can be shared between such AMs are
provided and used by heap. table_block_parallelscan_{estimate,
intiialize, reinitialize} as callbacks, and
table_block_parallelscan_{nextpage, init} for use in AMs. These
operate on a ParallelBlockTableScanDesc.
3) Index scans need to be able to access tables to return a tuple, and
there needs to be state across individual accesses to the heap to
store state like buffers. That's now handled by introducing a
sort-of-scan IndexFetchTable, which again is intended to be
subclassed by individual AMs (for heap IndexFetchHeap).
The relevant callbacks for an AM are index_fetch_{end, begin,
reset} to create the necessary state, and index_fetch_tuple to
retrieve an indexed tuple. Note that index_fetch_tuple
implementations need to be smarter than just blindly fetching the
tuples for AMs that have optimizations similar to heap's HOT - the
currently alive tuple in the update chain needs to be fetched if
appropriate.
Similar to table_scan_getnextslot(), it's undesirable to continue
to return HeapTuples. Thus index_fetch_heap (might want to rename
that later) now accepts a slot as an argument. Core code doesn't
have a lot of call sites performing index scans without going
through the systable_* API (in contrast to loads of heap_getnext
calls and working directly with HeapTuples).
Index scans now store the result of a search in
IndexScanDesc->xs_heaptid, rather than xs_ctup->t_self. As the
target is not generally a HeapTuple anymore that seems cleaner.
To be able to sensible adapt code to use the above, two further
callbacks have been introduced:
a) slot_callbacks returns a TupleTableSlotOps* suitable for creating
slots capable of holding a tuple of the AMs
type. table_slot_callbacks() and table_slot_create() are based
upon that, but have additional logic to deal with views, foreign
tables, etc.
While this change could have been done separately, nearly all the
call sites that needed to be adapted for the rest of this commit
also would have been needed to be adapted for
table_slot_callbacks(), making separation not worthwhile.
b) tuple_satisfies_snapshot checks whether the tuple in a slot is
currently visible according to a snapshot. That's required as a few
places now don't have a buffer + HeapTuple around, but a
slot (which in heap's case internally has that information).
Additionally a few infrastructure changes were needed:
I) SysScanDesc, as used by systable_{beginscan, getnext} et al. now
internally uses a slot to keep track of tuples. While
systable_getnext() still returns HeapTuples, and will so for the
foreseeable future, the index API (see 1) above) now only deals with
slots.
The remainder, and largest part, of this commit is then adjusting all
scans in postgres to use the new APIs.
Author: Andres Freund, Haribabu Kommi, Alvaro Herrera
Discussion:
https://postgr.es/m/20180703070645.wchpu5muyto5n647@alap3.anarazel.de
https://postgr.es/m/20160812231527.GA690404@alvherre.pgsql
2019-03-11 20:46:41 +01:00
|
|
|
scan = table_beginscan_catalog(rel, 2, key);
|
2009-10-12 22:39:42 +02:00
|
|
|
|
|
|
|
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
|
|
|
|
{
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
Oid oid = ((Form_pg_class) GETSTRUCT(tuple))->oid;
|
|
|
|
|
|
|
|
relations = lappend_oid(relations, oid);
|
2009-10-12 22:39:42 +02:00
|
|
|
}
|
|
|
|
|
tableam: Add and use scan APIs.
Too allow table accesses to be not directly dependent on heap, several
new abstractions are needed. Specifically:
1) Heap scans need to be generalized into table scans. Do this by
introducing TableScanDesc, which will be the "base class" for
individual AMs. This contains the AM independent fields from
HeapScanDesc.
The previous heap_{beginscan,rescan,endscan} et al. have been
replaced with a table_ version.
There's no direct replacement for heap_getnext(), as that returned
a HeapTuple, which is undesirable for a other AMs. Instead there's
table_scan_getnextslot(). But note that heap_getnext() lives on,
it's still used widely to access catalog tables.
This is achieved by new scan_begin, scan_end, scan_rescan,
scan_getnextslot callbacks.
2) The portion of parallel scans that's shared between backends need
to be able to do so without the user doing per-AM work. To achieve
that new parallelscan_{estimate, initialize, reinitialize}
callbacks are introduced, which operate on a new
ParallelTableScanDesc, which again can be subclassed by AMs.
As it is likely that several AMs are going to be block oriented,
block oriented callbacks that can be shared between such AMs are
provided and used by heap. table_block_parallelscan_{estimate,
intiialize, reinitialize} as callbacks, and
table_block_parallelscan_{nextpage, init} for use in AMs. These
operate on a ParallelBlockTableScanDesc.
3) Index scans need to be able to access tables to return a tuple, and
there needs to be state across individual accesses to the heap to
store state like buffers. That's now handled by introducing a
sort-of-scan IndexFetchTable, which again is intended to be
subclassed by individual AMs (for heap IndexFetchHeap).
The relevant callbacks for an AM are index_fetch_{end, begin,
reset} to create the necessary state, and index_fetch_tuple to
retrieve an indexed tuple. Note that index_fetch_tuple
implementations need to be smarter than just blindly fetching the
tuples for AMs that have optimizations similar to heap's HOT - the
currently alive tuple in the update chain needs to be fetched if
appropriate.
Similar to table_scan_getnextslot(), it's undesirable to continue
to return HeapTuples. Thus index_fetch_heap (might want to rename
that later) now accepts a slot as an argument. Core code doesn't
have a lot of call sites performing index scans without going
through the systable_* API (in contrast to loads of heap_getnext
calls and working directly with HeapTuples).
Index scans now store the result of a search in
IndexScanDesc->xs_heaptid, rather than xs_ctup->t_self. As the
target is not generally a HeapTuple anymore that seems cleaner.
To be able to sensible adapt code to use the above, two further
callbacks have been introduced:
a) slot_callbacks returns a TupleTableSlotOps* suitable for creating
slots capable of holding a tuple of the AMs
type. table_slot_callbacks() and table_slot_create() are based
upon that, but have additional logic to deal with views, foreign
tables, etc.
While this change could have been done separately, nearly all the
call sites that needed to be adapted for the rest of this commit
also would have been needed to be adapted for
table_slot_callbacks(), making separation not worthwhile.
b) tuple_satisfies_snapshot checks whether the tuple in a slot is
currently visible according to a snapshot. That's required as a few
places now don't have a buffer + HeapTuple around, but a
slot (which in heap's case internally has that information).
Additionally a few infrastructure changes were needed:
I) SysScanDesc, as used by systable_{beginscan, getnext} et al. now
internally uses a slot to keep track of tuples. While
systable_getnext() still returns HeapTuples, and will so for the
foreseeable future, the index API (see 1) above) now only deals with
slots.
The remainder, and largest part, of this commit is then adjusting all
scans in postgres to use the new APIs.
Author: Andres Freund, Haribabu Kommi, Alvaro Herrera
Discussion:
https://postgr.es/m/20180703070645.wchpu5muyto5n647@alap3.anarazel.de
https://postgr.es/m/20160812231527.GA690404@alvherre.pgsql
2019-03-11 20:46:41 +01:00
|
|
|
table_endscan(scan);
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(rel, AccessShareLock);
|
2009-10-12 22:39:42 +02:00
|
|
|
|
|
|
|
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)
|
Improve reporting of "conflicting or redundant options" errors.
When reporting "conflicting or redundant options" errors, try to
ensure that errposition() is used, to help the user identify the
offending option.
Formerly, errposition() was invoked in less than 60% of cases. This
patch raises that to over 90%, but there remain a few places where the
ParseState is not readily available. Using errdetail() might improve
the error in such cases, but that is left as a task for the future.
Additionally, since this error is thrown from over 100 places in the
codebase, introduce a dedicated function to throw it, reducing code
duplication.
Extracted from a slightly larger patch by Vignesh C. Reviewed by
Bharath Rupireddy, Alvaro Herrera, Dilip Kumar, Hou Zhijie, Peter
Smith, Daniel Gustafsson, Julien Rouhaud and me.
Discussion: https://postgr.es/m/CALDaNm33FFSS5tVyvmkoK2cCMuDVxcui=gFrjti9ROfynqSAGA@mail.gmail.com
2021-07-15 09:49:45 +02:00
|
|
|
errorConflictingDefElem(defel, pstate);
|
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)
|
Improve reporting of "conflicting or redundant options" errors.
When reporting "conflicting or redundant options" errors, try to
ensure that errposition() is used, to help the user identify the
offending option.
Formerly, errposition() was invoked in less than 60% of cases. This
patch raises that to over 90%, but there remain a few places where the
ParseState is not readily available. Using errdetail() might improve
the error in such cases, but that is left as a task for the future.
Additionally, since this error is thrown from over 100 places in the
codebase, introduce a dedicated function to throw it, reducing code
duplication.
Extracted from a slightly larger patch by Vignesh C. Reviewed by
Bharath Rupireddy, Alvaro Herrera, Dilip Kumar, Hou Zhijie, Peter
Smith, Daniel Gustafsson, Julien Rouhaud and me.
Discussion: https://postgr.es/m/CALDaNm33FFSS5tVyvmkoK2cCMuDVxcui=gFrjti9ROfynqSAGA@mail.gmail.com
2021-07-15 09:49:45 +02:00
|
|
|
errorConflictingDefElem(defel, pstate);
|
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;
|
|
|
|
|
|
|
|
/*
|
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
|
|
|
* 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
|
|
|
|
* there shouldn't be any additional work needed to support this case.
|
2009-10-05 21:24:49 +02:00
|
|
|
*/
|
|
|
|
foreach(cell, action->grantees)
|
|
|
|
{
|
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
|
|
|
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:
|
2016-12-28 18:00:00 +01:00
|
|
|
grantee_uid = get_rolespec_oid(grantee, false);
|
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
|
|
|
break;
|
|
|
|
}
|
|
|
|
iacls.grantees = lappend_oid(iacls.grantees, grantee_uid);
|
2009-10-05 21:24:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert action->privileges, a list of privilege strings, into an
|
|
|
|
* AclMode bitmask.
|
|
|
|
*/
|
|
|
|
switch (action->objtype)
|
|
|
|
{
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_TABLE:
|
2009-10-05 21:24:49 +02:00
|
|
|
all_privileges = ACL_ALL_RIGHTS_RELATION;
|
|
|
|
errormsg = gettext_noop("invalid privilege type %s for relation");
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_SEQUENCE:
|
2009-10-05 21:24:49 +02:00
|
|
|
all_privileges = ACL_ALL_RIGHTS_SEQUENCE;
|
|
|
|
errormsg = gettext_noop("invalid privilege type %s for sequence");
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_FUNCTION:
|
2009-10-05 21:24:49 +02:00
|
|
|
all_privileges = ACL_ALL_RIGHTS_FUNCTION;
|
|
|
|
errormsg = gettext_noop("invalid privilege type %s for function");
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_PROCEDURE:
|
2017-11-30 14:46:13 +01:00
|
|
|
all_privileges = ACL_ALL_RIGHTS_FUNCTION;
|
|
|
|
errormsg = gettext_noop("invalid privilege type %s for procedure");
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_ROUTINE:
|
2017-11-30 14:46:13 +01:00
|
|
|
all_privileges = ACL_ALL_RIGHTS_FUNCTION;
|
|
|
|
errormsg = gettext_noop("invalid privilege type %s for routine");
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_TYPE:
|
2011-12-19 23:05:19 +01:00
|
|
|
all_privileges = ACL_ALL_RIGHTS_TYPE;
|
|
|
|
errormsg = gettext_noop("invalid privilege type %s for type");
|
|
|
|
break;
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_SCHEMA:
|
|
|
|
all_privileges = ACL_ALL_RIGHTS_SCHEMA;
|
2017-03-28 17:58:55 +02:00
|
|
|
errormsg = gettext_noop("invalid privilege type %s for schema");
|
|
|
|
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),
|
|
|
|
errmsg("default privileges cannot be set for columns")));
|
|
|
|
|
|
|
|
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
|
|
|
|
2016-12-28 18:00:00 +01:00
|
|
|
iacls.roleid = get_rolespec_oid(rolespec, false);
|
2009-10-05 21:24:49 +02: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.
|
|
|
|
*/
|
|
|
|
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;
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
rel = table_open(DefaultAclRelationId, RowExclusiveLock);
|
2009-10-05 21:24:49 +02:00
|
|
|
|
2010-04-05 03:58:03 +02:00
|
|
|
/*
|
|
|
|
* The default for a global entry is the hard-wired default ACL for the
|
|
|
|
* particular object type. The default for non-global entries is an empty
|
|
|
|
* 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
|
|
|
/*
|
|
|
|
* Convert ACL object type to pg_default_acl object type and handle
|
|
|
|
* all_privs option
|
|
|
|
*/
|
|
|
|
switch (iacls->objtype)
|
|
|
|
{
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_TABLE:
|
2009-10-05 21:24:49 +02:00
|
|
|
objtype = DEFACLOBJ_RELATION;
|
|
|
|
if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
|
|
|
|
this_privileges = ACL_ALL_RIGHTS_RELATION;
|
|
|
|
break;
|
|
|
|
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_SEQUENCE:
|
2009-10-05 21:24:49 +02:00
|
|
|
objtype = DEFACLOBJ_SEQUENCE;
|
|
|
|
if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
|
|
|
|
this_privileges = ACL_ALL_RIGHTS_SEQUENCE;
|
|
|
|
break;
|
|
|
|
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_FUNCTION:
|
2009-10-05 21:24:49 +02:00
|
|
|
objtype = DEFACLOBJ_FUNCTION;
|
|
|
|
if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
|
|
|
|
this_privileges = ACL_ALL_RIGHTS_FUNCTION;
|
|
|
|
break;
|
|
|
|
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_TYPE:
|
2011-12-19 23:05:19 +01:00
|
|
|
objtype = DEFACLOBJ_TYPE;
|
|
|
|
if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
|
|
|
|
this_privileges = ACL_ALL_RIGHTS_TYPE;
|
|
|
|
break;
|
|
|
|
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_SCHEMA:
|
2017-03-28 17:58:55 +02:00
|
|
|
if (OidIsValid(iacls->nspid))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
|
|
|
|
errmsg("cannot use IN SCHEMA clause when using GRANT/REVOKE ON SCHEMAS")));
|
|
|
|
objtype = DEFACLOBJ_NAMESPACE;
|
|
|
|
if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
|
2017-10-12 00:35:19 +02:00
|
|
|
this_privileges = ACL_ALL_RIGHTS_SCHEMA;
|
2017-03-28 17:58:55 +02:00
|
|
|
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),
|
|
|
|
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
|
|
|
|
|
|
|
/*
|
|
|
|
* Generate new ACL. Grantor of rights is always the same as the target
|
|
|
|
* role.
|
|
|
|
*/
|
|
|
|
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
|
|
|
|
* explicit pg_default_acl entry, and should in fact remove the entry if
|
|
|
|
* it exists. Must sort both arrays to compare properly.
|
|
|
|
*/
|
|
|
|
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;
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
myself.objectId = ((Form_pg_default_acl) GETSTRUCT(tuple))->oid;
|
2010-04-05 03:58:03 +02:00
|
|
|
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
|
|
|
|
{
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
Oid defAclOid;
|
|
|
|
|
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 */
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
defAclOid = GetNewOidWithIndex(rel, DefaultAclOidIndexId,
|
|
|
|
Anum_pg_default_acl_oid);
|
|
|
|
values[Anum_pg_default_acl_oid - 1] = ObjectIdGetDatum(defAclOid);
|
2010-04-05 03:58:03 +02:00
|
|
|
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);
|
2017-01-31 22:42:24 +01:00
|
|
|
CatalogTupleInsert(rel, newtuple);
|
2010-04-05 03:58:03 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
defAclOid = ((Form_pg_default_acl) GETSTRUCT(tuple))->oid;
|
|
|
|
|
2010-04-05 03:58:03 +02:00
|
|
|
/* 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);
|
2017-01-31 22:42:24 +01:00
|
|
|
CatalogTupleUpdate(rel, &newtuple->t_self, newtuple);
|
2010-04-05 03:58:03 +02:00
|
|
|
}
|
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 */
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
recordDependencyOnOwner(DefaultAclRelationId, defAclOid,
|
2010-04-05 03:58:03 +02:00
|
|
|
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,
|
|
|
|
referenced;
|
2009-10-05 21:24:49 +02:00
|
|
|
|
2010-04-05 03:58:03 +02:00
|
|
|
myself.classId = DefaultAclRelationId;
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
myself.objectId = defAclOid;
|
2010-04-05 03:58:03 +02:00
|
|
|
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,
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
defAclOid, 0,
|
2010-04-05 03:58:03 +02:00
|
|
|
iacls->roleid,
|
|
|
|
noldmembers, oldmembers,
|
|
|
|
nnewmembers, newmembers);
|
2013-03-18 03:55:14 +01:00
|
|
|
|
|
|
|
if (isNew)
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
InvokeObjectPostCreateHook(DefaultAclRelationId, defAclOid, 0);
|
2013-03-18 03:55:14 +01:00
|
|
|
else
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
InvokeObjectPostAlterHook(DefaultAclRelationId, defAclOid, 0);
|
2010-04-05 03:58:03 +02:00
|
|
|
}
|
2009-10-05 21:24:49 +02:00
|
|
|
|
|
|
|
if (HeapTupleIsValid(tuple))
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(rel, RowExclusiveLock);
|
2021-01-20 03:38:17 +01:00
|
|
|
|
|
|
|
/* prevent error when processing duplicate objects */
|
|
|
|
CommandCounterIncrement();
|
2009-10-05 21:24:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 */
|
2019-01-21 19:32:19 +01:00
|
|
|
rel = table_open(DefaultAclRelationId, AccessShareLock);
|
2009-10-05 21:24:49 +02:00
|
|
|
|
|
|
|
ScanKeyInit(&skey[0],
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
Anum_pg_default_acl_oid,
|
2009-10-05 21:24:49 +02:00
|
|
|
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:
|
2017-10-12 00:35:19 +02:00
|
|
|
iacls.objtype = OBJECT_TABLE;
|
2009-10-05 21:24:49 +02:00
|
|
|
break;
|
2011-04-21 04:23:58 +02:00
|
|
|
case DEFACLOBJ_SEQUENCE:
|
2017-10-12 00:35:19 +02:00
|
|
|
iacls.objtype = OBJECT_SEQUENCE;
|
2009-10-05 21:24:49 +02:00
|
|
|
break;
|
|
|
|
case DEFACLOBJ_FUNCTION:
|
2017-10-12 00:35:19 +02:00
|
|
|
iacls.objtype = OBJECT_FUNCTION;
|
2009-10-05 21:24:49 +02:00
|
|
|
break;
|
2012-12-09 06:08:23 +01:00
|
|
|
case DEFACLOBJ_TYPE:
|
2017-10-12 00:35:19 +02:00
|
|
|
iacls.objtype = OBJECT_TYPE;
|
2012-12-09 06:08:23 +01:00
|
|
|
break;
|
2017-03-28 17:58:55 +02:00
|
|
|
case DEFACLOBJ_NAMESPACE:
|
2017-10-12 00:35:19 +02:00
|
|
|
iacls.objtype = OBJECT_SCHEMA;
|
2017-03-28 17:58:55 +02:00
|
|
|
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);
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(rel, AccessShareLock);
|
2009-10-05 21:24:49 +02:00
|
|
|
|
|
|
|
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:
|
2017-10-12 00:35:19 +02:00
|
|
|
/* it's OK to use TABLE for a sequence */
|
|
|
|
istmt.objtype = OBJECT_TABLE;
|
2009-10-05 21:24:49 +02:00
|
|
|
break;
|
|
|
|
case DatabaseRelationId:
|
2017-10-12 00:35:19 +02:00
|
|
|
istmt.objtype = OBJECT_DATABASE;
|
2009-10-05 21:24:49 +02:00
|
|
|
break;
|
2011-12-19 23:05:19 +01:00
|
|
|
case TypeRelationId:
|
2017-10-12 00:35:19 +02:00
|
|
|
istmt.objtype = OBJECT_TYPE;
|
2011-12-19 23:05:19 +01:00
|
|
|
break;
|
2009-10-05 21:24:49 +02:00
|
|
|
case ProcedureRelationId:
|
2017-10-12 00:35:19 +02:00
|
|
|
istmt.objtype = OBJECT_ROUTINE;
|
2009-10-05 21:24:49 +02:00
|
|
|
break;
|
|
|
|
case LanguageRelationId:
|
2017-10-12 00:35:19 +02:00
|
|
|
istmt.objtype = OBJECT_LANGUAGE;
|
2009-10-05 21:24:49 +02:00
|
|
|
break;
|
2009-12-11 04:34:57 +01:00
|
|
|
case LargeObjectRelationId:
|
2017-10-12 00:35:19 +02:00
|
|
|
istmt.objtype = OBJECT_LARGEOBJECT;
|
2009-12-11 04:34:57 +01:00
|
|
|
break;
|
2009-10-05 21:24:49 +02:00
|
|
|
case NamespaceRelationId:
|
2017-10-12 00:35:19 +02:00
|
|
|
istmt.objtype = OBJECT_SCHEMA;
|
2009-10-05 21:24:49 +02:00
|
|
|
break;
|
|
|
|
case TableSpaceRelationId:
|
2017-10-12 00:35:19 +02:00
|
|
|
istmt.objtype = OBJECT_TABLESPACE;
|
2009-10-05 21:24:49 +02:00
|
|
|
break;
|
2010-11-12 14:19:14 +01:00
|
|
|
case ForeignServerRelationId:
|
2017-10-12 00:35:19 +02:00
|
|
|
istmt.objtype = OBJECT_FOREIGN_SERVER;
|
2010-11-12 14:19:14 +01:00
|
|
|
break;
|
|
|
|
case ForeignDataWrapperRelationId:
|
2017-10-12 00:35:19 +02:00
|
|
|
istmt.objtype = OBJECT_FDW;
|
2010-11-12 14:19:14 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
elog(ERROR, "column number out of range"); /* safety check */
|
|
|
|
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;
|
|
|
|
|
|
|
|
/* 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
|
2019-07-08 06:15:09 +02:00
|
|
|
* ExecGrant_Relation, not directly from ExecuteGrantStmt.
|
2009-01-22 21:16:10 +01:00
|
|
|
*/
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get working copy of existing ACL. If there's no ACL, substitute the
|
|
|
|
* proper default.
|
|
|
|
*/
|
|
|
|
aclDatum = SysCacheGetAttr(ATTNUM, attr_tuple, Anum_pg_attribute_attacl,
|
|
|
|
&isNull);
|
|
|
|
if (isNull)
|
2010-04-05 03:09:53 +02:00
|
|
|
{
|
2017-10-12 00:35:19 +02:00
|
|
|
old_acl = acldefault(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
|
|
|
|
* concatenation. (This is a bit cheap and dirty compared to merging them
|
|
|
|
* properly with no duplications, but it's all we need here.)
|
|
|
|
*/
|
|
|
|
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);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
col_privileges =
|
|
|
|
restrict_and_check_grant(istmt->is_grant, avail_goptions,
|
|
|
|
(col_privileges == ACL_ALL_RIGHTS_COLUMN),
|
|
|
|
col_privileges,
|
2017-12-02 15:26:34 +01:00
|
|
|
relOid, grantorId, OBJECT_COLUMN,
|
2009-01-22 21:16:10 +01:00
|
|
|
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));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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,
|
|
|
|
* even if there were never any column GRANTs. Note we are assuming that
|
|
|
|
* the "default" ACL state for columns is empty.
|
|
|
|
*/
|
|
|
|
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);
|
|
|
|
|
2017-01-31 22:42:24 +01:00
|
|
|
CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
|
2009-01-22 21:16:10 +01:00
|
|
|
|
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;
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
relation = table_open(RelationRelationId, RowExclusiveLock);
|
|
|
|
attRelation = table_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-01-22 21:16:10 +01:00
|
|
|
AclMode *col_privileges;
|
|
|
|
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-06-28 07:09:14 +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 */
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
if (pg_class_tuple->relkind == RELKIND_INDEX ||
|
|
|
|
pg_class_tuple->relkind == RELKIND_PARTITIONED_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? */
|
2017-10-12 00:35:19 +02:00
|
|
|
if (istmt->objtype == OBJECT_SEQUENCE &&
|
2006-01-21 03:16:21 +01:00
|
|
|
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
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2017-10-12 00:35:19 +02:00
|
|
|
if (istmt->objtype == OBJECT_TABLE)
|
2006-01-21 03:16:21 +01:00
|
|
|
{
|
|
|
|
if (pg_class_tuple->relkind == RELKIND_SEQUENCE)
|
|
|
|
{
|
|
|
|
/*
|
2009-01-22 21:16:10 +01:00
|
|
|
* For backward compatibility, just throw a warning for
|
2006-01-21 03:16:21 +01: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))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Mention the object name because the user needs to know
|
|
|
|
* which operations succeeded. This is required because
|
|
|
|
* WARNING allows the command to continue.
|
|
|
|
*/
|
|
|
|
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
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
|
2017-01-18 20:08:20 +01:00
|
|
|
errmsg("invalid privilege type %s for table",
|
|
|
|
"USAGE")));
|
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
|
|
|
|
* that need modification. The array is indexed such that entry [0]
|
|
|
|
* 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
|
|
|
/*
|
|
|
|
* Get owner ID and working copy of existing ACL. If there's no ACL,
|
|
|
|
* substitute the proper default.
|
|
|
|
*/
|
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:
|
2017-10-12 00:35:19 +02:00
|
|
|
old_acl = acldefault(OBJECT_SEQUENCE, ownerId);
|
2011-01-02 05:48:11 +01:00
|
|
|
break;
|
|
|
|
default:
|
2017-10-12 00:35:19 +02:00
|
|
|
old_acl = acldefault(OBJECT_TABLE, ownerId);
|
2011-01-02 05:48:11 +01:00
|
|
|
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;
|
2017-12-02 15:26:34 +01:00
|
|
|
ObjectType objtype;
|
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:
|
2017-12-02 15:26:34 +01:00
|
|
|
objtype = OBJECT_SEQUENCE;
|
2011-01-02 05:48:11 +01:00
|
|
|
break;
|
|
|
|
default:
|
2017-12-02 15:26:34 +01:00
|
|
|
objtype = OBJECT_TABLE;
|
2011-01-02 05:48:11 +01:00
|
|
|
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,
|
2017-12-02 15:26:34 +01:00
|
|
|
relOid, grantorId, objtype,
|
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);
|
|
|
|
|
2017-01-31 22:42:24 +01:00
|
|
|
CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
|
2009-01-22 21:16:10 +01:00
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
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.
|
|
|
|
* This is a warning not error because we do it that way for
|
|
|
|
* relation-level privileges.
|
|
|
|
*/
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
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
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(attRelation, RowExclusiveLock);
|
|
|
|
table_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
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
relation = table_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-06-28 07:09:14 +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
|
|
|
/*
|
|
|
|
* 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
|
|
|
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
|
|
|
{
|
2017-10-12 00:35:19 +02:00
|
|
|
old_acl = acldefault(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
|
|
|
/*
|
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.
|
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,
|
2017-12-02 15:26:34 +01:00
|
|
|
datId, grantorId, OBJECT_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
|
|
|
|
2017-01-31 22:42:24 +01:00
|
|
|
CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2005-07-07 22:40:02 +02:00
|
|
|
/* Update the shared dependency ACL info */
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
updateAclDependencies(DatabaseRelationId, pg_database_tuple->oid, 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
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
table_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;
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
relation = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
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,
|
|
|
|
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
|
|
|
{
|
2017-10-12 00:35:19 +02:00
|
|
|
old_acl = acldefault(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,
|
2017-12-02 15:26:34 +01:00
|
|
|
fdwid, grantorId, OBJECT_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);
|
|
|
|
|
2017-01-31 22:42:24 +01:00
|
|
|
CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
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,
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
pg_fdw_tuple->oid, 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();
|
|
|
|
}
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(relation, RowExclusiveLock);
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ExecGrant_ForeignServer(InternalGrant *istmt)
|
|
|
|
{
|
|
|
|
Relation relation;
|
|
|
|
ListCell *cell;
|
|
|
|
|
|
|
|
if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
|
|
|
|
istmt->privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
relation = table_open(ForeignServerRelationId, RowExclusiveLock);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
|
|
|
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
|
|
|
{
|
2017-10-12 00:35:19 +02:00
|
|
|
old_acl = acldefault(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,
|
2017-12-02 15:26:34 +01:00
|
|
|
srvid, grantorId, OBJECT_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);
|
|
|
|
|
2017-01-31 22:42:24 +01:00
|
|
|
CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
|
2008-12-19 17:25:19 +01:00
|
|
|
|
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,
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
pg_server_tuple->oid, 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();
|
|
|
|
}
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(relation, RowExclusiveLock);
|
2008-12-19 17:25:19 +01:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
relation = table_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-06-28 07:09:14 +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
|
|
|
/*
|
|
|
|
* 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
|
|
|
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
|
|
|
{
|
2017-10-12 00:35:19 +02:00
|
|
|
old_acl = acldefault(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
|
|
|
/*
|
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.
|
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,
|
2017-12-02 15:26:34 +01:00
|
|
|
funcId, grantorId, OBJECT_FUNCTION,
|
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
|
|
|
|
2017-01-31 22:42:24 +01:00
|
|
|
CatalogTupleUpdate(relation, &newtuple->t_self, 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
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
table_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
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
relation = table_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-06-28 07:09:14 +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-10 20:49:04 +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
|
|
|
{
|
2017-10-12 00:35:19 +02:00
|
|
|
old_acl = acldefault(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
|
|
|
/*
|
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.
|
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,
|
2017-12-02 15:26:34 +01:00
|
|
|
langId, grantorId, OBJECT_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
|
|
|
|
2017-01-31 22:42:24 +01:00
|
|
|
CatalogTupleUpdate(relation, &newtuple->t_self, 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 */
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
updateAclDependencies(LanguageRelationId, pg_language_tuple->oid, 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
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
table_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;
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
relation = table_open(LargeObjectMetadataRelationId,
|
|
|
|
RowExclusiveLock);
|
2009-12-11 04:34:57 +01:00
|
|
|
|
|
|
|
foreach(cell, istmt->objects)
|
|
|
|
{
|
|
|
|
Oid loid = lfirst_oid(cell);
|
|
|
|
Form_pg_largeobject_metadata form_lo_meta;
|
|
|
|
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;
|
|
|
|
ScanKeyData entry[1];
|
|
|
|
SysScanDesc scan;
|
|
|
|
HeapTuple tuple;
|
|
|
|
|
|
|
|
/* There's no syscache for pg_largeobject_metadata */
|
|
|
|
ScanKeyInit(&entry[0],
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
Anum_pg_largeobject_metadata_oid,
|
2009-12-11 04:34:57 +01:00
|
|
|
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))
|
2017-06-04 22:20:03 +02:00
|
|
|
elog(ERROR, "could not find tuple for large object %u", loid);
|
2009-12-11 04:34:57 +01:00
|
|
|
|
|
|
|
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
|
|
|
{
|
2017-10-12 00:35:19 +02:00
|
|
|
old_acl = acldefault(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,
|
2017-12-02 15:26:34 +01:00
|
|
|
loid, grantorId, OBJECT_LARGEOBJECT,
|
2009-12-11 04:34:57 +01:00
|
|
|
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);
|
|
|
|
|
2017-01-31 22:42:24 +01:00
|
|
|
CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
|
2009-12-11 04:34:57 +01:00
|
|
|
|
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,
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
form_lo_meta->oid, 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();
|
|
|
|
}
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(relation, RowExclusiveLock);
|
2009-12-11 04:34:57 +01:00
|
|
|
}
|
|
|
|
|
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)
|
2017-10-12 00:35:19 +02:00
|
|
|
istmt->privileges = ACL_ALL_RIGHTS_SCHEMA;
|
2002-04-21 02:26:44 +02:00
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
relation = table_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-06-28 07:09:14 +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
|
|
|
/*
|
|
|
|
* 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
|
|
|
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
|
|
|
{
|
2017-10-12 00:35:19 +02:00
|
|
|
old_acl = acldefault(OBJECT_SCHEMA, 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
|
|
|
/*
|
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.
|
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,
|
2017-12-02 15:26:34 +01:00
|
|
|
nspid, grantorId, OBJECT_SCHEMA,
|
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
|
|
|
|
2017-01-31 22:42:24 +01:00
|
|
|
CatalogTupleUpdate(relation, &newtuple->t_self, 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 */
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
updateAclDependencies(NamespaceRelationId, pg_namespace_tuple->oid, 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
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
table_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
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
relation = table_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-06-28 07:09:14 +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
|
|
|
/*
|
|
|
|
* Get owner ID and working copy of existing ACL. If there's no ACL,
|
|
|
|
* substitute the proper default.
|
|
|
|
*/
|
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
|
|
|
{
|
2017-10-12 00:35:19 +02:00
|
|
|
old_acl = acldefault(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
|
|
|
|
|
|
|
/*
|
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.
|
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,
|
2017-12-02 15:26:34 +01:00
|
|
|
tblId, grantorId, OBJECT_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
|
|
|
|
2017-01-31 22:42:24 +01:00
|
|
|
CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
|
2004-06-18 08:14:31 +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(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
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
table_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;
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
relation = table_open(TypeRelationId, RowExclusiveLock);
|
2011-12-19 23:05:19 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
Support subscripting of arbitrary types, not only arrays.
This patch generalizes the subscripting infrastructure so that any
data type can be subscripted, if it provides a handler function to
define what that means. Traditional variable-length (varlena) arrays
all use array_subscript_handler(), while the existing fixed-length
types that support subscripting use raw_array_subscript_handler().
It's expected that other types that want to use subscripting notation
will define their own handlers. (This patch provides no such new
features, though; it only lays the foundation for them.)
To do this, move the parser's semantic processing of subscripts
(including coercion to whatever data type is required) into a
method callback supplied by the handler. On the execution side,
replace the ExecEvalSubscriptingRef* layer of functions with direct
calls to callback-supplied execution routines. (Thus, essentially
no new run-time overhead should be caused by this patch. Indeed,
there is room to remove some overhead by supplying specialized
execution routines. This patch does a little bit in that line,
but more could be done.)
Additional work is required here and there to remove formerly
hard-wired assumptions about the result type, collation, etc
of a SubscriptingRef expression node; and to remove assumptions
that the subscript values must be integers.
One useful side-effect of this is that we now have a less squishy
mechanism for identifying whether a data type is a "true" array:
instead of wiring in weird rules about typlen, we can look to see
if pg_type.typsubscript == F_ARRAY_SUBSCRIPT_HANDLER. For this
to be bulletproof, we have to forbid user-defined types from using
that handler directly; but there seems no good reason for them to
do so.
This patch also removes assumptions that the number of subscripts
is limited to MAXDIM (6), or indeed has any hard-wired limit.
That limit still applies to types handled by array_subscript_handler
or raw_array_subscript_handler, but to discourage other dependencies
on this constant, I've moved it from c.h to utils/array.h.
Dmitry Dolgov, reviewed at various times by Tom Lane, Arthur Zakirov,
Peter Eisentraut, Pavel Stehule
Discussion: https://postgr.es/m/CA+q6zcVDuGBv=M0FqBYX8DPebS3F_0KQ6OVFobGJPM507_SZ_w@mail.gmail.com
Discussion: https://postgr.es/m/CA+q6zcVovR+XY4mfk-7oNk-rF91gH0PebnNfuUjuuDsyHjOcVA@mail.gmail.com
2020-12-09 18:40:37 +01:00
|
|
|
if (IsTrueArrayType(pg_type_tuple))
|
2011-12-19 23:05:19 +01:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
|
|
|
|
errmsg("cannot set privileges of array types"),
|
|
|
|
errhint("Set the privileges of the element type instead.")));
|
|
|
|
|
|
|
|
/* Used GRANT DOMAIN on a non-domain? */
|
2017-10-12 00:35:19 +02:00
|
|
|
if (istmt->objtype == OBJECT_DOMAIN &&
|
2011-12-19 23:05:19 +01:00
|
|
|
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,
|
2017-12-02 15:26:34 +01:00
|
|
|
typId, grantorId, OBJECT_TYPE,
|
2011-12-19 23:05:19 +01:00
|
|
|
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);
|
|
|
|
|
2017-01-31 22:42:24 +01:00
|
|
|
CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
|
2011-12-19 23:05:19 +01:00
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(relation, RowExclusiveLock);
|
2011-12-19 23:05:19 +01:00
|
|
|
}
|
|
|
|
|
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
|
|
|
*/
|
|
|
|
void
|
2017-12-02 15:26:34 +01:00
|
|
|
aclcheck_error(AclResult aclerr, ObjectType objtype,
|
2003-08-01 02:15:26 +02:00
|
|
|
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:
|
2017-12-02 15:26:34 +01:00
|
|
|
{
|
2018-01-20 04:16:25 +01:00
|
|
|
const char *msg = "???";
|
2017-12-02 15:26:34 +01:00
|
|
|
|
|
|
|
switch (objtype)
|
|
|
|
{
|
|
|
|
case OBJECT_AGGREGATE:
|
|
|
|
msg = gettext_noop("permission denied for aggregate %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_COLLATION:
|
|
|
|
msg = gettext_noop("permission denied for collation %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_COLUMN:
|
|
|
|
msg = gettext_noop("permission denied for column %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_CONVERSION:
|
|
|
|
msg = gettext_noop("permission denied for conversion %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_DATABASE:
|
|
|
|
msg = gettext_noop("permission denied for database %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_DOMAIN:
|
|
|
|
msg = gettext_noop("permission denied for domain %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_EVENT_TRIGGER:
|
|
|
|
msg = gettext_noop("permission denied for event trigger %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_EXTENSION:
|
|
|
|
msg = gettext_noop("permission denied for extension %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_FDW:
|
|
|
|
msg = gettext_noop("permission denied for foreign-data wrapper %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_FOREIGN_SERVER:
|
|
|
|
msg = gettext_noop("permission denied for foreign server %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_FOREIGN_TABLE:
|
|
|
|
msg = gettext_noop("permission denied for foreign table %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_FUNCTION:
|
|
|
|
msg = gettext_noop("permission denied for function %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_INDEX:
|
|
|
|
msg = gettext_noop("permission denied for index %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_LANGUAGE:
|
|
|
|
msg = gettext_noop("permission denied for language %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_LARGEOBJECT:
|
|
|
|
msg = gettext_noop("permission denied for large object %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_MATVIEW:
|
|
|
|
msg = gettext_noop("permission denied for materialized view %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_OPCLASS:
|
|
|
|
msg = gettext_noop("permission denied for operator class %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_OPERATOR:
|
|
|
|
msg = gettext_noop("permission denied for operator %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_OPFAMILY:
|
|
|
|
msg = gettext_noop("permission denied for operator family %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_POLICY:
|
|
|
|
msg = gettext_noop("permission denied for policy %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_PROCEDURE:
|
|
|
|
msg = gettext_noop("permission denied for procedure %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_PUBLICATION:
|
|
|
|
msg = gettext_noop("permission denied for publication %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_ROUTINE:
|
|
|
|
msg = gettext_noop("permission denied for routine %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_SCHEMA:
|
|
|
|
msg = gettext_noop("permission denied for schema %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_SEQUENCE:
|
|
|
|
msg = gettext_noop("permission denied for sequence %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_STATISTIC_EXT:
|
|
|
|
msg = gettext_noop("permission denied for statistics object %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_SUBSCRIPTION:
|
|
|
|
msg = gettext_noop("permission denied for subscription %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_TABLE:
|
|
|
|
msg = gettext_noop("permission denied for table %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_TABLESPACE:
|
|
|
|
msg = gettext_noop("permission denied for tablespace %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_TSCONFIGURATION:
|
|
|
|
msg = gettext_noop("permission denied for text search configuration %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_TSDICTIONARY:
|
|
|
|
msg = gettext_noop("permission denied for text search dictionary %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_TYPE:
|
|
|
|
msg = gettext_noop("permission denied for type %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_VIEW:
|
|
|
|
msg = gettext_noop("permission denied for view %s");
|
|
|
|
break;
|
|
|
|
/* these currently aren't used */
|
|
|
|
case OBJECT_ACCESS_METHOD:
|
|
|
|
case OBJECT_AMOP:
|
|
|
|
case OBJECT_AMPROC:
|
|
|
|
case OBJECT_ATTRIBUTE:
|
|
|
|
case OBJECT_CAST:
|
|
|
|
case OBJECT_DEFAULT:
|
|
|
|
case OBJECT_DEFACL:
|
|
|
|
case OBJECT_DOMCONSTRAINT:
|
|
|
|
case OBJECT_PUBLICATION_REL:
|
|
|
|
case OBJECT_ROLE:
|
|
|
|
case OBJECT_RULE:
|
|
|
|
case OBJECT_TABCONSTRAINT:
|
|
|
|
case OBJECT_TRANSFORM:
|
|
|
|
case OBJECT_TRIGGER:
|
|
|
|
case OBJECT_TSPARSER:
|
|
|
|
case OBJECT_TSTEMPLATE:
|
|
|
|
case OBJECT_USER_MAPPING:
|
|
|
|
elog(ERROR, "unsupported object type %d", objtype);
|
|
|
|
}
|
|
|
|
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
|
|
errmsg(msg, objectname)));
|
|
|
|
break;
|
|
|
|
}
|
2002-04-27 05:45:03 +02:00
|
|
|
case ACLCHECK_NOT_OWNER:
|
2017-12-02 15:26:34 +01:00
|
|
|
{
|
2018-01-20 04:16:25 +01:00
|
|
|
const char *msg = "???";
|
2017-12-02 15:26:34 +01:00
|
|
|
|
|
|
|
switch (objtype)
|
|
|
|
{
|
|
|
|
case OBJECT_AGGREGATE:
|
|
|
|
msg = gettext_noop("must be owner of aggregate %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_COLLATION:
|
|
|
|
msg = gettext_noop("must be owner of collation %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_CONVERSION:
|
|
|
|
msg = gettext_noop("must be owner of conversion %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_DATABASE:
|
|
|
|
msg = gettext_noop("must be owner of database %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_DOMAIN:
|
|
|
|
msg = gettext_noop("must be owner of domain %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_EVENT_TRIGGER:
|
|
|
|
msg = gettext_noop("must be owner of event trigger %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_EXTENSION:
|
|
|
|
msg = gettext_noop("must be owner of extension %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_FDW:
|
|
|
|
msg = gettext_noop("must be owner of foreign-data wrapper %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_FOREIGN_SERVER:
|
|
|
|
msg = gettext_noop("must be owner of foreign server %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_FOREIGN_TABLE:
|
|
|
|
msg = gettext_noop("must be owner of foreign table %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_FUNCTION:
|
|
|
|
msg = gettext_noop("must be owner of function %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_INDEX:
|
|
|
|
msg = gettext_noop("must be owner of index %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_LANGUAGE:
|
|
|
|
msg = gettext_noop("must be owner of language %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_LARGEOBJECT:
|
|
|
|
msg = gettext_noop("must be owner of large object %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_MATVIEW:
|
|
|
|
msg = gettext_noop("must be owner of materialized view %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_OPCLASS:
|
|
|
|
msg = gettext_noop("must be owner of operator class %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_OPERATOR:
|
|
|
|
msg = gettext_noop("must be owner of operator %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_OPFAMILY:
|
|
|
|
msg = gettext_noop("must be owner of operator family %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_PROCEDURE:
|
|
|
|
msg = gettext_noop("must be owner of procedure %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_PUBLICATION:
|
|
|
|
msg = gettext_noop("must be owner of publication %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_ROUTINE:
|
|
|
|
msg = gettext_noop("must be owner of routine %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_SEQUENCE:
|
|
|
|
msg = gettext_noop("must be owner of sequence %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_SUBSCRIPTION:
|
|
|
|
msg = gettext_noop("must be owner of subscription %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_TABLE:
|
|
|
|
msg = gettext_noop("must be owner of table %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_TYPE:
|
|
|
|
msg = gettext_noop("must be owner of type %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_VIEW:
|
|
|
|
msg = gettext_noop("must be owner of view %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_SCHEMA:
|
|
|
|
msg = gettext_noop("must be owner of schema %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_STATISTIC_EXT:
|
|
|
|
msg = gettext_noop("must be owner of statistics object %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_TABLESPACE:
|
|
|
|
msg = gettext_noop("must be owner of tablespace %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_TSCONFIGURATION:
|
|
|
|
msg = gettext_noop("must be owner of text search configuration %s");
|
|
|
|
break;
|
|
|
|
case OBJECT_TSDICTIONARY:
|
|
|
|
msg = gettext_noop("must be owner of text search dictionary %s");
|
|
|
|
break;
|
2018-04-26 20:47:16 +02:00
|
|
|
|
2017-12-02 15:26:34 +01:00
|
|
|
/*
|
|
|
|
* Special cases: For these, the error message talks
|
|
|
|
* about "relation", because that's where the
|
|
|
|
* ownership is attached. See also
|
|
|
|
* check_object_ownership().
|
|
|
|
*/
|
|
|
|
case OBJECT_COLUMN:
|
|
|
|
case OBJECT_POLICY:
|
|
|
|
case OBJECT_RULE:
|
|
|
|
case OBJECT_TABCONSTRAINT:
|
|
|
|
case OBJECT_TRIGGER:
|
|
|
|
msg = gettext_noop("must be owner of relation %s");
|
|
|
|
break;
|
|
|
|
/* these currently aren't used */
|
|
|
|
case OBJECT_ACCESS_METHOD:
|
|
|
|
case OBJECT_AMOP:
|
|
|
|
case OBJECT_AMPROC:
|
|
|
|
case OBJECT_ATTRIBUTE:
|
|
|
|
case OBJECT_CAST:
|
|
|
|
case OBJECT_DEFAULT:
|
|
|
|
case OBJECT_DEFACL:
|
|
|
|
case OBJECT_DOMCONSTRAINT:
|
|
|
|
case OBJECT_PUBLICATION_REL:
|
|
|
|
case OBJECT_ROLE:
|
|
|
|
case OBJECT_TRANSFORM:
|
|
|
|
case OBJECT_TSPARSER:
|
|
|
|
case OBJECT_TSTEMPLATE:
|
|
|
|
case OBJECT_USER_MAPPING:
|
|
|
|
elog(ERROR, "unsupported object type %d", objtype);
|
|
|
|
}
|
|
|
|
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
|
|
errmsg(msg, objectname)));
|
|
|
|
break;
|
|
|
|
}
|
2002-04-27 05:45:03 +02:00
|
|
|
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
|
2017-12-02 15:26:34 +01:00
|
|
|
aclcheck_error_col(AclResult aclerr, ObjectType objtype,
|
2009-01-22 21:16:10 +01:00
|
|
|
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-03-07 00:10:42 +01:00
|
|
|
errmsg("permission denied for column \"%s\" of relation \"%s\"",
|
2009-01-22 21:16:10 +01:00
|
|
|
colname, objectname)));
|
|
|
|
break;
|
|
|
|
case ACLCHECK_NOT_OWNER:
|
|
|
|
/* relation msg is OK since columns don't have separate owners */
|
2017-12-02 15:26:34 +01:00
|
|
|
aclcheck_error(aclerr, objtype, objectname);
|
2009-01-22 21:16:10 +01:00
|
|
|
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)
|
|
|
|
{
|
|
|
|
Oid element_type = get_element_type(typeOid);
|
|
|
|
|
2017-12-02 15:26:34 +01:00
|
|
|
aclcheck_error(aclerr, OBJECT_TYPE, format_type_be(element_type ? element_type : typeOid));
|
2012-06-15 21:55:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-12-01 03:03:01 +01:00
|
|
|
/*
|
|
|
|
* Relay for the various pg_*_mask routines depending on object kind
|
|
|
|
*/
|
|
|
|
static AclMode
|
2017-12-02 15:26:34 +01:00
|
|
|
pg_aclmask(ObjectType objtype, Oid table_oid, AttrNumber attnum, Oid roleid,
|
2005-12-01 03:03:01 +01:00
|
|
|
AclMode mask, AclMaskHow how)
|
|
|
|
{
|
2017-12-02 15:26:34 +01:00
|
|
|
switch (objtype)
|
2005-12-01 03:03:01 +01:00
|
|
|
{
|
2017-12-02 15:26:34 +01:00
|
|
|
case OBJECT_COLUMN:
|
2009-01-22 21:16:10 +01:00
|
|
|
return
|
|
|
|
pg_class_aclmask(table_oid, roleid, mask, how) |
|
|
|
|
pg_attribute_aclmask(table_oid, attnum, roleid, mask, how);
|
2017-12-02 15:26:34 +01:00
|
|
|
case OBJECT_TABLE:
|
|
|
|
case OBJECT_SEQUENCE:
|
2005-12-01 03:03:01 +01:00
|
|
|
return pg_class_aclmask(table_oid, roleid, mask, how);
|
2017-12-02 15:26:34 +01:00
|
|
|
case OBJECT_DATABASE:
|
2005-12-01 03:03:01 +01:00
|
|
|
return pg_database_aclmask(table_oid, roleid, mask, how);
|
2017-12-02 15:26:34 +01:00
|
|
|
case OBJECT_FUNCTION:
|
2005-12-01 03:03:01 +01:00
|
|
|
return pg_proc_aclmask(table_oid, roleid, mask, how);
|
2017-12-02 15:26:34 +01:00
|
|
|
case OBJECT_LANGUAGE:
|
2005-12-01 03:03:01 +01:00
|
|
|
return pg_language_aclmask(table_oid, roleid, mask, how);
|
2017-12-02 15:26:34 +01:00
|
|
|
case OBJECT_LARGEOBJECT:
|
2009-12-11 04:34:57 +01:00
|
|
|
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);
|
2017-12-02 15:26:34 +01:00
|
|
|
case OBJECT_SCHEMA:
|
2005-12-01 03:03:01 +01:00
|
|
|
return pg_namespace_aclmask(table_oid, roleid, mask, how);
|
2017-12-02 15:26:34 +01:00
|
|
|
case OBJECT_STATISTIC_EXT:
|
2017-05-14 16:54:47 +02:00
|
|
|
elog(ERROR, "grantable rights not supported for statistics objects");
|
Implement multivariate n-distinct coefficients
Add support for explicitly declared statistic objects (CREATE
STATISTICS), allowing collection of statistics on more complex
combinations that individual table columns. Companion commands DROP
STATISTICS and ALTER STATISTICS ... OWNER TO / SET SCHEMA / RENAME are
added too. All this DDL has been designed so that more statistic types
can be added later on, such as multivariate most-common-values and
multivariate histograms between columns of a single table, leaving room
for permitting columns on multiple tables, too, as well as expressions.
This commit only adds support for collection of n-distinct coefficient
on user-specified sets of columns in a single table. This is useful to
estimate number of distinct groups in GROUP BY and DISTINCT clauses;
estimation errors there can cause over-allocation of memory in hashed
aggregates, for instance, so it's a worthwhile problem to solve. A new
special pseudo-type pg_ndistinct is used.
(num-distinct estimation was deemed sufficiently useful by itself that
this is worthwhile even if no further statistic types are added
immediately; so much so that another version of essentially the same
functionality was submitted by Kyotaro Horiguchi:
https://postgr.es/m/20150828.173334.114731693.horiguchi.kyotaro@lab.ntt.co.jp
though this commit does not use that code.)
Author: Tomas Vondra. Some code rework by Álvaro.
Reviewed-by: Dean Rasheed, David Rowley, Kyotaro Horiguchi, Jeff Janes,
Ideriha Takeshi
Discussion: https://postgr.es/m/543AFA15.4080608@fuzzy.cz
https://postgr.es/m/20170320190220.ixlaueanxegqd5gr@alvherre.pgsql
2017-03-24 18:06:10 +01:00
|
|
|
/* not reached, but keep compiler quiet */
|
|
|
|
return ACL_NO_RIGHTS;
|
2017-12-02 15:26:34 +01:00
|
|
|
case OBJECT_TABLESPACE:
|
2005-12-01 03:03:01 +01:00
|
|
|
return pg_tablespace_aclmask(table_oid, roleid, mask, how);
|
2017-12-02 15:26:34 +01:00
|
|
|
case OBJECT_FDW:
|
2008-12-19 17:25:19 +01:00
|
|
|
return pg_foreign_data_wrapper_aclmask(table_oid, roleid, mask, how);
|
2017-12-02 15:26:34 +01:00
|
|
|
case OBJECT_FOREIGN_SERVER:
|
2008-12-19 17:25:19 +01:00
|
|
|
return pg_foreign_server_aclmask(table_oid, roleid, mask, how);
|
2017-12-02 15:26:34 +01:00
|
|
|
case OBJECT_EVENT_TRIGGER:
|
2012-07-18 16:16:16 +02:00
|
|
|
elog(ERROR, "grantable rights not supported for event triggers");
|
|
|
|
/* not reached, but keep compiler quiet */
|
|
|
|
return ACL_NO_RIGHTS;
|
2017-12-02 15:26:34 +01:00
|
|
|
case OBJECT_TYPE:
|
2011-12-19 23:05:19 +01:00
|
|
|
return pg_type_aclmask(table_oid, roleid, mask, how);
|
2005-12-01 03:03:01 +01:00
|
|
|
default:
|
2017-12-02 15:26:34 +01:00
|
|
|
elog(ERROR, "unrecognized objtype: %d",
|
|
|
|
(int) objtype);
|
2005-12-01 03:03:01 +01:00
|
|
|
/* 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
|
|
|
|
* as appropriate. (For the same reason, we have no special case for
|
|
|
|
* superuser-ness here.)
|
|
|
|
*/
|
|
|
|
AclMode
|
|
|
|
pg_attribute_aclmask(Oid table_oid, AttrNumber attnum, Oid roleid,
|
|
|
|
AclMode mask, AclMaskHow how)
|
Fix has_column_privilege function corner case
According to the comments, when an invalid or dropped column oid is passed
to has_column_privilege(), the intention has always been to return NULL.
However, when the caller had table level privilege the invalid/missing
column was never discovered, because table permissions were checked first.
Fix that by introducing extended versions of pg_attribute_acl(check|mask)
and pg_class_acl(check|mask) which take a new argument, is_missing. When
is_missing is NULL, the old behavior is preserved. But when is_missing is
passed by the caller, no ERROR is thrown for dropped or missing
columns/relations, and is_missing is flipped to true. This in turn allows
has_column_privilege to check for column privileges first, providing the
desired semantics.
Not backpatched since it is a user visible behavioral change with no previous
complaints, and the fix is a bit on the invasive side.
Author: Joe Conway
Reviewed-By: Tom Lane
Reported by: Ian Barwick
Discussion: https://postgr.es/m/flat/9b5f4311-157b-4164-7fe7-077b4fe8ed84%40joeconway.com
2021-03-31 19:55:25 +02:00
|
|
|
{
|
|
|
|
return pg_attribute_aclmask_ext(table_oid, attnum, roleid,
|
|
|
|
mask, how, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Exported routine for examining a user's privileges for a column
|
|
|
|
*
|
|
|
|
* Does the bulk of the work for pg_attribute_aclmask(), and allows other
|
|
|
|
* callers to avoid the missing attribute ERROR when is_missing is non-NULL.
|
|
|
|
*/
|
|
|
|
AclMode
|
|
|
|
pg_attribute_aclmask_ext(Oid table_oid, AttrNumber attnum, Oid roleid,
|
|
|
|
AclMode mask, AclMaskHow how, bool *is_missing)
|
2009-01-22 21:16:10 +01:00
|
|
|
{
|
|
|
|
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))
|
Fix has_column_privilege function corner case
According to the comments, when an invalid or dropped column oid is passed
to has_column_privilege(), the intention has always been to return NULL.
However, when the caller had table level privilege the invalid/missing
column was never discovered, because table permissions were checked first.
Fix that by introducing extended versions of pg_attribute_acl(check|mask)
and pg_class_acl(check|mask) which take a new argument, is_missing. When
is_missing is NULL, the old behavior is preserved. But when is_missing is
passed by the caller, no ERROR is thrown for dropped or missing
columns/relations, and is_missing is flipped to true. This in turn allows
has_column_privilege to check for column privileges first, providing the
desired semantics.
Not backpatched since it is a user visible behavioral change with no previous
complaints, and the fix is a bit on the invasive side.
Author: Joe Conway
Reviewed-By: Tom Lane
Reported by: Ian Barwick
Discussion: https://postgr.es/m/flat/9b5f4311-157b-4164-7fe7-077b4fe8ed84%40joeconway.com
2021-03-31 19:55:25 +02:00
|
|
|
{
|
|
|
|
if (is_missing != NULL)
|
|
|
|
{
|
|
|
|
/* return "no privileges" instead of throwing an error */
|
|
|
|
*is_missing = true;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
|
|
|
errmsg("attribute %d of relation with OID %u does not exist",
|
|
|
|
attnum, table_oid)));
|
|
|
|
}
|
|
|
|
|
2009-01-22 21:16:10 +01:00
|
|
|
attributeForm = (Form_pg_attribute) GETSTRUCT(attTuple);
|
|
|
|
|
Fix has_column_privilege function corner case
According to the comments, when an invalid or dropped column oid is passed
to has_column_privilege(), the intention has always been to return NULL.
However, when the caller had table level privilege the invalid/missing
column was never discovered, because table permissions were checked first.
Fix that by introducing extended versions of pg_attribute_acl(check|mask)
and pg_class_acl(check|mask) which take a new argument, is_missing. When
is_missing is NULL, the old behavior is preserved. But when is_missing is
passed by the caller, no ERROR is thrown for dropped or missing
columns/relations, and is_missing is flipped to true. This in turn allows
has_column_privilege to check for column privileges first, providing the
desired semantics.
Not backpatched since it is a user visible behavioral change with no previous
complaints, and the fix is a bit on the invasive side.
Author: Joe Conway
Reviewed-By: Tom Lane
Reported by: Ian Barwick
Discussion: https://postgr.es/m/flat/9b5f4311-157b-4164-7fe7-077b4fe8ed84%40joeconway.com
2021-03-31 19:55:25 +02:00
|
|
|
/* Check dropped columns, too */
|
2009-01-22 21:16:10 +01:00
|
|
|
if (attributeForm->attisdropped)
|
Fix has_column_privilege function corner case
According to the comments, when an invalid or dropped column oid is passed
to has_column_privilege(), the intention has always been to return NULL.
However, when the caller had table level privilege the invalid/missing
column was never discovered, because table permissions were checked first.
Fix that by introducing extended versions of pg_attribute_acl(check|mask)
and pg_class_acl(check|mask) which take a new argument, is_missing. When
is_missing is NULL, the old behavior is preserved. But when is_missing is
passed by the caller, no ERROR is thrown for dropped or missing
columns/relations, and is_missing is flipped to true. This in turn allows
has_column_privilege to check for column privileges first, providing the
desired semantics.
Not backpatched since it is a user visible behavioral change with no previous
complaints, and the fix is a bit on the invasive side.
Author: Joe Conway
Reviewed-By: Tom Lane
Reported by: Ian Barwick
Discussion: https://postgr.es/m/flat/9b5f4311-157b-4164-7fe7-077b4fe8ed84%40joeconway.com
2021-03-31 19:55:25 +02:00
|
|
|
{
|
|
|
|
if (is_missing != NULL)
|
|
|
|
{
|
|
|
|
/* return "no privileges" instead of throwing an error */
|
|
|
|
*is_missing = true;
|
|
|
|
ReleaseSysCache(attTuple);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
|
|
|
errmsg("attribute %d of relation with OID %u does not exist",
|
|
|
|
attnum, table_oid)));
|
|
|
|
}
|
2009-01-22 21:16:10 +01:00
|
|
|
|
|
|
|
aclDatum = SysCacheGetAttr(ATTNUM, attTuple, Anum_pg_attribute_attacl,
|
|
|
|
&isNull);
|
|
|
|
|
2009-02-06 22:15:12 +01: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-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
|
|
|
|
* 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.
|
|
|
|
*/
|
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)
|
Fix has_column_privilege function corner case
According to the comments, when an invalid or dropped column oid is passed
to has_column_privilege(), the intention has always been to return NULL.
However, when the caller had table level privilege the invalid/missing
column was never discovered, because table permissions were checked first.
Fix that by introducing extended versions of pg_attribute_acl(check|mask)
and pg_class_acl(check|mask) which take a new argument, is_missing. When
is_missing is NULL, the old behavior is preserved. But when is_missing is
passed by the caller, no ERROR is thrown for dropped or missing
columns/relations, and is_missing is flipped to true. This in turn allows
has_column_privilege to check for column privileges first, providing the
desired semantics.
Not backpatched since it is a user visible behavioral change with no previous
complaints, and the fix is a bit on the invasive side.
Author: Joe Conway
Reviewed-By: Tom Lane
Reported by: Ian Barwick
Discussion: https://postgr.es/m/flat/9b5f4311-157b-4164-7fe7-077b4fe8ed84%40joeconway.com
2021-03-31 19:55:25 +02:00
|
|
|
{
|
|
|
|
return pg_class_aclmask_ext(table_oid, roleid, mask, how, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Exported routine for examining a user's privileges for a table
|
|
|
|
*
|
|
|
|
* Does the bulk of the work for pg_class_aclmask(), and allows other
|
|
|
|
* callers to avoid the missing relation ERROR when is_missing is non-NULL.
|
|
|
|
*/
|
|
|
|
AclMode
|
|
|
|
pg_class_aclmask_ext(Oid table_oid, Oid roleid, AclMode mask,
|
|
|
|
AclMaskHow how, bool *is_missing)
|
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-06-28 07:09:14 +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))
|
Fix has_column_privilege function corner case
According to the comments, when an invalid or dropped column oid is passed
to has_column_privilege(), the intention has always been to return NULL.
However, when the caller had table level privilege the invalid/missing
column was never discovered, because table permissions were checked first.
Fix that by introducing extended versions of pg_attribute_acl(check|mask)
and pg_class_acl(check|mask) which take a new argument, is_missing. When
is_missing is NULL, the old behavior is preserved. But when is_missing is
passed by the caller, no ERROR is thrown for dropped or missing
columns/relations, and is_missing is flipped to true. This in turn allows
has_column_privilege to check for column privileges first, providing the
desired semantics.
Not backpatched since it is a user visible behavioral change with no previous
complaints, and the fix is a bit on the invasive side.
Author: Joe Conway
Reviewed-By: Tom Lane
Reported by: Ian Barwick
Discussion: https://postgr.es/m/flat/9b5f4311-157b-4164-7fe7-077b4fe8ed84%40joeconway.com
2021-03-31 19:55:25 +02:00
|
|
|
{
|
|
|
|
if (is_missing != NULL)
|
|
|
|
{
|
|
|
|
/* return "no privileges" instead of throwing an error */
|
|
|
|
*is_missing = true;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_TABLE),
|
|
|
|
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
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/*
|
|
|
|
* Deny anyone permission to update a system catalog unless
|
2019-11-29 10:04:45 +01:00
|
|
|
* pg_authid.rolsuper is set.
|
2004-01-14 04:44:53 +01: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
|
2006-01-21 03:16:21 +01:00
|
|
|
* themselves. ACL_USAGE is if we ever have system sequences.
|
1996-07-09 08:22:35 +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 &&
|
2019-11-29 10:04:45 +01:00
|
|
|
!superuser_arg(roleid))
|
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
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/*
|
|
|
|
* Otherwise, superusers bypass all permission-checking.
|
|
|
|
*/
|
2005-06-28 07:09:14 +02:00
|
|
|
if (superuser_arg(roleid))
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-11-16 23:30:52 +01:00
|
|
|
ReleaseSysCache(tuple);
|
2004-05-11 19:36:13 +02:00
|
|
|
return mask;
|
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:
|
2017-10-12 00:35:19 +02:00
|
|
|
acl = acldefault(OBJECT_SEQUENCE, ownerId);
|
2011-01-02 05:48:11 +01:00
|
|
|
break;
|
|
|
|
default:
|
2017-10-12 00:35:19 +02:00
|
|
|
acl = acldefault(OBJECT_TABLE, ownerId);
|
2011-01-02 05:48:11 +01:00
|
|
|
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))
|
1996-07-09 08:22:35 +02:00
|
|
|
pfree(acl);
|
2001-06-05 21:34:56 +02:00
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
2021-04-05 19:42:52 +02:00
|
|
|
/*
|
|
|
|
* Check if ACL_SELECT is being checked and, if so, and not set already as
|
|
|
|
* part of the result, then check if the user is a member of the
|
|
|
|
* pg_read_all_data role, which allows read access to all relations.
|
|
|
|
*/
|
|
|
|
if (mask & ACL_SELECT && !(result & ACL_SELECT) &&
|
2021-04-10 21:01:41 +02:00
|
|
|
has_privs_of_role(roleid, ROLE_PG_READ_ALL_DATA))
|
2021-04-05 19:42:52 +02:00
|
|
|
result |= ACL_SELECT;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if ACL_INSERT, ACL_UPDATE, or ACL_DELETE is being checked and, if
|
|
|
|
* so, and not set already as part of the result, then check if the user
|
|
|
|
* is a member of the pg_write_all_data role, which allows
|
|
|
|
* INSERT/UPDATE/DELETE access to all relations (except system catalogs,
|
|
|
|
* which requires superuser, see above).
|
|
|
|
*/
|
|
|
|
if (mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE) &&
|
|
|
|
!(result & (ACL_INSERT | ACL_UPDATE | ACL_DELETE)) &&
|
2021-04-10 21:01:41 +02:00
|
|
|
has_privs_of_role(roleid, ROLE_PG_WRITE_ALL_DATA))
|
2021-04-05 19:42:52 +02:00
|
|
|
result |= (mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE));
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
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 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 */
|
2017-10-12 00:35:19 +02:00
|
|
|
acl = acldefault(OBJECT_DATABASE, ownerId);
|
2002-04-21 02:26:44 +02: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);
|
|
|
|
|
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-06-28 07:09:14 +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),
|
|
|
|
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 */
|
2017-10-12 00:35:19 +02:00
|
|
|
acl = acldefault(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-06-28 07:09:14 +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),
|
|
|
|
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 */
|
2017-10-12 00:35:19 +02:00
|
|
|
acl = acldefault(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;
|
|
|
|
ScanKeyData entry[1];
|
|
|
|
SysScanDesc scan;
|
|
|
|
HeapTuple tuple;
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
|
|
|
Acl *acl;
|
|
|
|
Oid ownerId;
|
|
|
|
|
|
|
|
/* Superusers bypass all permission checking. */
|
|
|
|
if (superuser_arg(roleid))
|
|
|
|
return mask;
|
|
|
|
|
|
|
|
/*
|
2019-08-05 05:14:58 +02:00
|
|
|
* Get the largeobject's ACL from pg_largeobject_metadata
|
2009-12-11 04:34:57 +01:00
|
|
|
*/
|
2019-01-21 19:32:19 +01:00
|
|
|
pg_lo_meta = table_open(LargeObjectMetadataRelationId,
|
|
|
|
AccessShareLock);
|
2009-12-11 04:34:57 +01:00
|
|
|
|
|
|
|
ScanKeyInit(&entry[0],
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
Anum_pg_largeobject_metadata_oid,
|
2009-12-11 04:34:57 +01:00
|
|
|
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 */
|
2017-10-12 00:35:19 +02:00
|
|
|
acl = acldefault(OBJECT_LARGEOBJECT, ownerId);
|
2009-12-11 04:34:57 +01:00
|
|
|
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);
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(pg_lo_meta, AccessShareLock);
|
2009-12-11 04:34:57 +01:00
|
|
|
|
|
|
|
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-06-28 07:09:14 +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
|
|
|
/*
|
2004-05-28 18:17:14 +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
|
|
|
*
|
2004-05-28 18:17:14 +02: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
|
2004-05-28 18:37:11 +02:00
|
|
|
* upshot of this behavior is that a SECURITY DEFINER function can create
|
2004-05-28 18:17:14 +02:00
|
|
|
* temp tables that can then be accessed (if permission is granted) by
|
2004-05-28 18:37:11 +02:00
|
|
|
* 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
|
|
|
|
* 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)
|
2017-10-12 00:35:19 +02:00
|
|
|
return mask & ACL_ALL_RIGHTS_SCHEMA;
|
2004-05-28 18:17:14 +02:00
|
|
|
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 */
|
2017-10-12 00:35:19 +02:00
|
|
|
acl = acldefault(OBJECT_SCHEMA, ownerId);
|
2002-04-21 02:26:44 +02: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);
|
|
|
|
|
2021-04-05 19:42:52 +02:00
|
|
|
/*
|
|
|
|
* Check if ACL_USAGE is being checked and, if so, and not set already as
|
|
|
|
* part of the result, then check if the user is a member of the
|
|
|
|
* pg_read_all_data or pg_write_all_data roles, which allow usage access
|
|
|
|
* to all schemas.
|
|
|
|
*/
|
|
|
|
if (mask & ACL_USAGE && !(result & ACL_USAGE) &&
|
2021-04-10 21:01:41 +02:00
|
|
|
(has_privs_of_role(roleid, ROLE_PG_READ_ALL_DATA) ||
|
|
|
|
has_privs_of_role(roleid, ROLE_PG_WRITE_ALL_DATA)))
|
2021-04-05 19:42:52 +02:00
|
|
|
result |= ACL_USAGE;
|
2002-02-19 00:11:58 +01:00
|
|
|
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-06-28 07:09:14 +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),
|
|
|
|
errmsg("tablespace with OID %u does not exist", spc_oid)));
|
|
|
|
|
|
|
|
ownerId = ((Form_pg_tablespace) GETSTRUCT(tuple))->spcowner;
|
|
|
|
|
2010-01-05 22:54:00 +01:00
|
|
|
aclDatum = SysCacheGetAttr(TABLESPACEOID, tuple,
|
|
|
|
Anum_pg_tablespace_spcacl,
|
|
|
|
&isNull);
|
2004-06-18 08:14:31 +02:00
|
|
|
|
|
|
|
if (isNull)
|
|
|
|
{
|
|
|
|
/* No ACL, so build default ACL */
|
2017-10-12 00:35:19 +02:00
|
|
|
acl = acldefault(OBJECT_TABLESPACE, ownerId);
|
2004-06-18 08:14:31 +02: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);
|
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 */
|
2017-10-12 00:35:19 +02:00
|
|
|
acl = acldefault(OBJECT_FDW, ownerId);
|
2008-12-19 17:25:19 +01:00
|
|
|
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,
|
|
|
|
AclMode mask, AclMaskHow how)
|
|
|
|
{
|
|
|
|
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 */
|
2017-10-12 00:35:19 +02:00
|
|
|
acl = acldefault(OBJECT_FOREIGN_SERVER, ownerId);
|
2008-12-19 17:25:19 +01:00
|
|
|
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.
|
|
|
|
*/
|
Support subscripting of arbitrary types, not only arrays.
This patch generalizes the subscripting infrastructure so that any
data type can be subscripted, if it provides a handler function to
define what that means. Traditional variable-length (varlena) arrays
all use array_subscript_handler(), while the existing fixed-length
types that support subscripting use raw_array_subscript_handler().
It's expected that other types that want to use subscripting notation
will define their own handlers. (This patch provides no such new
features, though; it only lays the foundation for them.)
To do this, move the parser's semantic processing of subscripts
(including coercion to whatever data type is required) into a
method callback supplied by the handler. On the execution side,
replace the ExecEvalSubscriptingRef* layer of functions with direct
calls to callback-supplied execution routines. (Thus, essentially
no new run-time overhead should be caused by this patch. Indeed,
there is room to remove some overhead by supplying specialized
execution routines. This patch does a little bit in that line,
but more could be done.)
Additional work is required here and there to remove formerly
hard-wired assumptions about the result type, collation, etc
of a SubscriptingRef expression node; and to remove assumptions
that the subscript values must be integers.
One useful side-effect of this is that we now have a less squishy
mechanism for identifying whether a data type is a "true" array:
instead of wiring in weird rules about typlen, we can look to see
if pg_type.typsubscript == F_ARRAY_SUBSCRIPT_HANDLER. For this
to be bulletproof, we have to forbid user-defined types from using
that handler directly; but there seems no good reason for them to
do so.
This patch also removes assumptions that the number of subscripts
is limited to MAXDIM (6), or indeed has any hard-wired limit.
That limit still applies to types handled by array_subscript_handler
or raw_array_subscript_handler, but to discourage other dependencies
on this constant, I've moved it from c.h to utils/array.h.
Dmitry Dolgov, reviewed at various times by Tom Lane, Arthur Zakirov,
Peter Eisentraut, Pavel Stehule
Discussion: https://postgr.es/m/CA+q6zcVDuGBv=M0FqBYX8DPebS3F_0KQ6OVFobGJPM507_SZ_w@mail.gmail.com
Discussion: https://postgr.es/m/CA+q6zcVovR+XY4mfk-7oNk-rF91gH0PebnNfuUjuuDsyHjOcVA@mail.gmail.com
2020-12-09 18:40:37 +01:00
|
|
|
if (IsTrueArrayType(typeForm))
|
2011-12-19 23:05:19 +01:00
|
|
|
{
|
|
|
|
Oid elttype_oid = typeForm->typelem;
|
|
|
|
|
|
|
|
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 */
|
2017-10-12 00:35:19 +02:00
|
|
|
acl = acldefault(OBJECT_TYPE, ownerId);
|
2011-12-19 23:05:19 +01:00
|
|
|
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)
|
|
|
|
{
|
Fix has_column_privilege function corner case
According to the comments, when an invalid or dropped column oid is passed
to has_column_privilege(), the intention has always been to return NULL.
However, when the caller had table level privilege the invalid/missing
column was never discovered, because table permissions were checked first.
Fix that by introducing extended versions of pg_attribute_acl(check|mask)
and pg_class_acl(check|mask) which take a new argument, is_missing. When
is_missing is NULL, the old behavior is preserved. But when is_missing is
passed by the caller, no ERROR is thrown for dropped or missing
columns/relations, and is_missing is flipped to true. This in turn allows
has_column_privilege to check for column privileges first, providing the
desired semantics.
Not backpatched since it is a user visible behavioral change with no previous
complaints, and the fix is a bit on the invasive side.
Author: Joe Conway
Reviewed-By: Tom Lane
Reported by: Ian Barwick
Discussion: https://postgr.es/m/flat/9b5f4311-157b-4164-7fe7-077b4fe8ed84%40joeconway.com
2021-03-31 19:55:25 +02:00
|
|
|
return pg_attribute_aclcheck_ext(table_oid, attnum, roleid, mode, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Exported routine for checking a user's access privileges to a column
|
|
|
|
*
|
|
|
|
* Does the bulk of the work for pg_attribute_aclcheck(), and allows other
|
|
|
|
* callers to avoid the missing attribute ERROR when is_missing is non-NULL.
|
|
|
|
*/
|
|
|
|
AclResult
|
|
|
|
pg_attribute_aclcheck_ext(Oid table_oid, AttrNumber attnum,
|
|
|
|
Oid roleid, AclMode mode, bool *is_missing)
|
|
|
|
{
|
|
|
|
if (pg_attribute_aclmask_ext(table_oid, attnum, roleid, mode,
|
|
|
|
ACLMASK_ANY, is_missing) != 0)
|
2009-01-22 21:16:10 +01:00
|
|
|
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)
|
|
|
|
{
|
|
|
|
AclResult result;
|
|
|
|
HeapTuple classTuple;
|
|
|
|
Form_pg_class classForm;
|
|
|
|
AttrNumber nattrs;
|
|
|
|
AttrNumber curr_att;
|
|
|
|
|
2009-02-06 22:15:12 +01:00
|
|
|
/*
|
|
|
|
* Must fetch pg_class row to check number of attributes. As in
|
|
|
|
* pg_attribute_aclmask, we prefer to return "no privileges" instead of
|
|
|
|
* throwing an error if we get any unexpected lookup errors.
|
|
|
|
*/
|
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);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize result in case there are no non-dropped columns. We want to
|
|
|
|
* report failure in such cases for either value of 'how'.
|
|
|
|
*/
|
|
|
|
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
|
|
|
|
* grants no privileges, so that we can fall out quickly in the very
|
|
|
|
* common case where attacl is null.
|
|
|
|
*/
|
2018-03-28 02:13:52 +02:00
|
|
|
if (heap_attisnull(attTuple, Anum_pg_attribute_attacl, NULL))
|
2009-02-06 22:15:12 +01:00
|
|
|
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
|
|
|
{
|
Fix has_column_privilege function corner case
According to the comments, when an invalid or dropped column oid is passed
to has_column_privilege(), the intention has always been to return NULL.
However, when the caller had table level privilege the invalid/missing
column was never discovered, because table permissions were checked first.
Fix that by introducing extended versions of pg_attribute_acl(check|mask)
and pg_class_acl(check|mask) which take a new argument, is_missing. When
is_missing is NULL, the old behavior is preserved. But when is_missing is
passed by the caller, no ERROR is thrown for dropped or missing
columns/relations, and is_missing is flipped to true. This in turn allows
has_column_privilege to check for column privileges first, providing the
desired semantics.
Not backpatched since it is a user visible behavioral change with no previous
complaints, and the fix is a bit on the invasive side.
Author: Joe Conway
Reviewed-By: Tom Lane
Reported by: Ian Barwick
Discussion: https://postgr.es/m/flat/9b5f4311-157b-4164-7fe7-077b4fe8ed84%40joeconway.com
2021-03-31 19:55:25 +02:00
|
|
|
return pg_class_aclcheck_ext(table_oid, roleid, mode, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Exported routine for checking a user's access privileges to a table
|
|
|
|
*
|
|
|
|
* Does the bulk of the work for pg_class_aclcheck(), and allows other
|
|
|
|
* callers to avoid the missing relation ERROR when is_missing is non-NULL.
|
|
|
|
*/
|
|
|
|
AclResult
|
|
|
|
pg_class_aclcheck_ext(Oid table_oid, Oid roleid,
|
|
|
|
AclMode mode, bool *is_missing)
|
|
|
|
{
|
|
|
|
if (pg_class_aclmask_ext(table_oid, roleid, mode,
|
|
|
|
ACLMASK_ANY, is_missing) != 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-06-28 07:09:14 +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),
|
|
|
|
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-06-28 07:09:14 +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-06-28 07:09:14 +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),
|
|
|
|
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-06-28 07:09:14 +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),
|
|
|
|
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;
|
|
|
|
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_largeobject_metadata */
|
2019-01-21 19:32:19 +01:00
|
|
|
pg_lo_meta = table_open(LargeObjectMetadataRelationId,
|
|
|
|
AccessShareLock);
|
2009-12-11 04:34:57 +01:00
|
|
|
|
|
|
|
ScanKeyInit(&entry[0],
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
Anum_pg_largeobject_metadata_oid,
|
2009-12-11 04:34:57 +01:00
|
|
|
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);
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(pg_lo_meta, AccessShareLock);
|
2009-12-11 04:34:57 +01:00
|
|
|
|
|
|
|
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-06-28 07:09:14 +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-06-28 07:09:14 +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),
|
|
|
|
errmsg("tablespace with OID %u does not exist", spc_oid)));
|
|
|
|
|
|
|
|
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-06-28 07:09:14 +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),
|
|
|
|
errmsg("text search configuration with OID %u does not exist",
|
|
|
|
cfg_oid)));
|
|
|
|
|
|
|
|
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),
|
|
|
|
errmsg("foreign server with OID %u does not exist",
|
|
|
|
srv_oid)));
|
|
|
|
|
|
|
|
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-06-28 07:09:14 +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-06-28 07:09:14 +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),
|
|
|
|
errmsg("conversion with OID %u does not exist", conv_oid)));
|
|
|
|
|
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 */
|
2019-01-21 19:32:19 +01:00
|
|
|
pg_extension = table_open(ExtensionRelationId, AccessShareLock);
|
2011-03-04 22:08:24 +01:00
|
|
|
|
|
|
|
ScanKeyInit(&entry[0],
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
Anum_pg_extension_oid,
|
2011-03-04 22:08:24 +01:00
|
|
|
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);
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(pg_extension, AccessShareLock);
|
2011-03-04 22:08:24 +01:00
|
|
|
|
|
|
|
return has_privs_of_role(roleid, ownerId);
|
|
|
|
}
|
|
|
|
|
2017-01-19 18:00:00 +01:00
|
|
|
/*
|
2018-03-29 21:18:53 +02:00
|
|
|
* Ownership check for a publication (specified by OID).
|
2017-01-19 18:00:00 +01:00
|
|
|
*/
|
|
|
|
bool
|
|
|
|
pg_publication_ownercheck(Oid pub_oid, Oid roleid)
|
|
|
|
{
|
|
|
|
HeapTuple tuple;
|
|
|
|
Oid ownerId;
|
|
|
|
|
|
|
|
/* Superusers bypass all permission checking. */
|
|
|
|
if (superuser_arg(roleid))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
tuple = SearchSysCache1(PUBLICATIONOID, ObjectIdGetDatum(pub_oid));
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("publication with OID %u does not exist", pub_oid)));
|
|
|
|
|
|
|
|
ownerId = ((Form_pg_publication) GETSTRUCT(tuple))->pubowner;
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
|
|
|
return has_privs_of_role(roleid, ownerId);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-04-17 20:19:39 +02:00
|
|
|
* Ownership check for a subscription (specified by OID).
|
2017-01-19 18:00:00 +01:00
|
|
|
*/
|
|
|
|
bool
|
|
|
|
pg_subscription_ownercheck(Oid sub_oid, Oid roleid)
|
|
|
|
{
|
|
|
|
HeapTuple tuple;
|
|
|
|
Oid ownerId;
|
|
|
|
|
|
|
|
/* Superusers bypass all permission checking. */
|
|
|
|
if (superuser_arg(roleid))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
tuple = SearchSysCache1(SUBSCRIPTIONOID, ObjectIdGetDatum(sub_oid));
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("subscription with OID %u does not exist", sub_oid)));
|
|
|
|
|
|
|
|
ownerId = ((Form_pg_subscription) GETSTRUCT(tuple))->subowner;
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
|
|
|
return has_privs_of_role(roleid, ownerId);
|
|
|
|
}
|
|
|
|
|
Implement multivariate n-distinct coefficients
Add support for explicitly declared statistic objects (CREATE
STATISTICS), allowing collection of statistics on more complex
combinations that individual table columns. Companion commands DROP
STATISTICS and ALTER STATISTICS ... OWNER TO / SET SCHEMA / RENAME are
added too. All this DDL has been designed so that more statistic types
can be added later on, such as multivariate most-common-values and
multivariate histograms between columns of a single table, leaving room
for permitting columns on multiple tables, too, as well as expressions.
This commit only adds support for collection of n-distinct coefficient
on user-specified sets of columns in a single table. This is useful to
estimate number of distinct groups in GROUP BY and DISTINCT clauses;
estimation errors there can cause over-allocation of memory in hashed
aggregates, for instance, so it's a worthwhile problem to solve. A new
special pseudo-type pg_ndistinct is used.
(num-distinct estimation was deemed sufficiently useful by itself that
this is worthwhile even if no further statistic types are added
immediately; so much so that another version of essentially the same
functionality was submitted by Kyotaro Horiguchi:
https://postgr.es/m/20150828.173334.114731693.horiguchi.kyotaro@lab.ntt.co.jp
though this commit does not use that code.)
Author: Tomas Vondra. Some code rework by Álvaro.
Reviewed-by: Dean Rasheed, David Rowley, Kyotaro Horiguchi, Jeff Janes,
Ideriha Takeshi
Discussion: https://postgr.es/m/543AFA15.4080608@fuzzy.cz
https://postgr.es/m/20170320190220.ixlaueanxegqd5gr@alvherre.pgsql
2017-03-24 18:06:10 +01:00
|
|
|
/*
|
2017-05-14 16:54:47 +02:00
|
|
|
* Ownership check for a statistics object (specified by OID).
|
Implement multivariate n-distinct coefficients
Add support for explicitly declared statistic objects (CREATE
STATISTICS), allowing collection of statistics on more complex
combinations that individual table columns. Companion commands DROP
STATISTICS and ALTER STATISTICS ... OWNER TO / SET SCHEMA / RENAME are
added too. All this DDL has been designed so that more statistic types
can be added later on, such as multivariate most-common-values and
multivariate histograms between columns of a single table, leaving room
for permitting columns on multiple tables, too, as well as expressions.
This commit only adds support for collection of n-distinct coefficient
on user-specified sets of columns in a single table. This is useful to
estimate number of distinct groups in GROUP BY and DISTINCT clauses;
estimation errors there can cause over-allocation of memory in hashed
aggregates, for instance, so it's a worthwhile problem to solve. A new
special pseudo-type pg_ndistinct is used.
(num-distinct estimation was deemed sufficiently useful by itself that
this is worthwhile even if no further statistic types are added
immediately; so much so that another version of essentially the same
functionality was submitted by Kyotaro Horiguchi:
https://postgr.es/m/20150828.173334.114731693.horiguchi.kyotaro@lab.ntt.co.jp
though this commit does not use that code.)
Author: Tomas Vondra. Some code rework by Álvaro.
Reviewed-by: Dean Rasheed, David Rowley, Kyotaro Horiguchi, Jeff Janes,
Ideriha Takeshi
Discussion: https://postgr.es/m/543AFA15.4080608@fuzzy.cz
https://postgr.es/m/20170320190220.ixlaueanxegqd5gr@alvherre.pgsql
2017-03-24 18:06:10 +01:00
|
|
|
*/
|
|
|
|
bool
|
2017-05-14 16:54:47 +02:00
|
|
|
pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid)
|
Implement multivariate n-distinct coefficients
Add support for explicitly declared statistic objects (CREATE
STATISTICS), allowing collection of statistics on more complex
combinations that individual table columns. Companion commands DROP
STATISTICS and ALTER STATISTICS ... OWNER TO / SET SCHEMA / RENAME are
added too. All this DDL has been designed so that more statistic types
can be added later on, such as multivariate most-common-values and
multivariate histograms between columns of a single table, leaving room
for permitting columns on multiple tables, too, as well as expressions.
This commit only adds support for collection of n-distinct coefficient
on user-specified sets of columns in a single table. This is useful to
estimate number of distinct groups in GROUP BY and DISTINCT clauses;
estimation errors there can cause over-allocation of memory in hashed
aggregates, for instance, so it's a worthwhile problem to solve. A new
special pseudo-type pg_ndistinct is used.
(num-distinct estimation was deemed sufficiently useful by itself that
this is worthwhile even if no further statistic types are added
immediately; so much so that another version of essentially the same
functionality was submitted by Kyotaro Horiguchi:
https://postgr.es/m/20150828.173334.114731693.horiguchi.kyotaro@lab.ntt.co.jp
though this commit does not use that code.)
Author: Tomas Vondra. Some code rework by Álvaro.
Reviewed-by: Dean Rasheed, David Rowley, Kyotaro Horiguchi, Jeff Janes,
Ideriha Takeshi
Discussion: https://postgr.es/m/543AFA15.4080608@fuzzy.cz
https://postgr.es/m/20170320190220.ixlaueanxegqd5gr@alvherre.pgsql
2017-03-24 18:06:10 +01:00
|
|
|
{
|
|
|
|
HeapTuple tuple;
|
|
|
|
Oid ownerId;
|
|
|
|
|
|
|
|
/* Superusers bypass all permission checking. */
|
|
|
|
if (superuser_arg(roleid))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
tuple = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(stat_oid));
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
2017-05-14 16:54:47 +02:00
|
|
|
errmsg("statistics object with OID %u does not exist",
|
|
|
|
stat_oid)));
|
Implement multivariate n-distinct coefficients
Add support for explicitly declared statistic objects (CREATE
STATISTICS), allowing collection of statistics on more complex
combinations that individual table columns. Companion commands DROP
STATISTICS and ALTER STATISTICS ... OWNER TO / SET SCHEMA / RENAME are
added too. All this DDL has been designed so that more statistic types
can be added later on, such as multivariate most-common-values and
multivariate histograms between columns of a single table, leaving room
for permitting columns on multiple tables, too, as well as expressions.
This commit only adds support for collection of n-distinct coefficient
on user-specified sets of columns in a single table. This is useful to
estimate number of distinct groups in GROUP BY and DISTINCT clauses;
estimation errors there can cause over-allocation of memory in hashed
aggregates, for instance, so it's a worthwhile problem to solve. A new
special pseudo-type pg_ndistinct is used.
(num-distinct estimation was deemed sufficiently useful by itself that
this is worthwhile even if no further statistic types are added
immediately; so much so that another version of essentially the same
functionality was submitted by Kyotaro Horiguchi:
https://postgr.es/m/20150828.173334.114731693.horiguchi.kyotaro@lab.ntt.co.jp
though this commit does not use that code.)
Author: Tomas Vondra. Some code rework by Álvaro.
Reviewed-by: Dean Rasheed, David Rowley, Kyotaro Horiguchi, Jeff Janes,
Ideriha Takeshi
Discussion: https://postgr.es/m/543AFA15.4080608@fuzzy.cz
https://postgr.es/m/20170320190220.ixlaueanxegqd5gr@alvherre.pgsql
2017-03-24 18:06:10 +01:00
|
|
|
|
2017-04-17 23:34:29 +02:00
|
|
|
ownerId = ((Form_pg_statistic_ext) GETSTRUCT(tuple))->stxowner;
|
Implement multivariate n-distinct coefficients
Add support for explicitly declared statistic objects (CREATE
STATISTICS), allowing collection of statistics on more complex
combinations that individual table columns. Companion commands DROP
STATISTICS and ALTER STATISTICS ... OWNER TO / SET SCHEMA / RENAME are
added too. All this DDL has been designed so that more statistic types
can be added later on, such as multivariate most-common-values and
multivariate histograms between columns of a single table, leaving room
for permitting columns on multiple tables, too, as well as expressions.
This commit only adds support for collection of n-distinct coefficient
on user-specified sets of columns in a single table. This is useful to
estimate number of distinct groups in GROUP BY and DISTINCT clauses;
estimation errors there can cause over-allocation of memory in hashed
aggregates, for instance, so it's a worthwhile problem to solve. A new
special pseudo-type pg_ndistinct is used.
(num-distinct estimation was deemed sufficiently useful by itself that
this is worthwhile even if no further statistic types are added
immediately; so much so that another version of essentially the same
functionality was submitted by Kyotaro Horiguchi:
https://postgr.es/m/20150828.173334.114731693.horiguchi.kyotaro@lab.ntt.co.jp
though this commit does not use that code.)
Author: Tomas Vondra. Some code rework by Álvaro.
Reviewed-by: Dean Rasheed, David Rowley, Kyotaro Horiguchi, Jeff Janes,
Ideriha Takeshi
Discussion: https://postgr.es/m/543AFA15.4080608@fuzzy.cz
https://postgr.es/m/20170320190220.ixlaueanxegqd5gr@alvherre.pgsql
2017-03-24 18:06:10 +01:00
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
|
|
|
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))
|
|
|
|
{
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
|
|
|
|
|
|
|
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
|
|
|
|
*
|
Fix missing role dependencies for some schema and type ACLs.
This patch fixes several related cases in which pg_shdepend entries were
never made, or were lost, for references to roles appearing in the ACLs of
schemas and/or types. While that did no immediate harm, if a referenced
role were later dropped, the drop would be allowed and would leave a
dangling reference in the object's ACL. That still wasn't a big problem
for normal database usage, but it would cause obscure failures in
subsequent dump/reload or pg_upgrade attempts, taking the form of
attempts to grant privileges to all-numeric role names. (I think I've
seen field reports matching that symptom, but can't find any right now.)
Several cases are fixed here:
1. ALTER DOMAIN SET/DROP DEFAULT would lose the dependencies for any
existing ACL entries for the domain. This case is ancient, dating
back as far as we've had pg_shdepend tracking at all.
2. If a default type privilege applies, CREATE TYPE recorded the
ACL properly but forgot to install dependency entries for it.
This dates to the addition of default privileges for types in 9.2.
3. If a default schema privilege applies, CREATE SCHEMA recorded the
ACL properly but forgot to install dependency entries for it.
This dates to the addition of default privileges for schemas in v10
(commit ab89e465c).
Another somewhat-related problem is that when creating a relation
rowtype or implicit array type, TypeCreate would apply any available
default type privileges to that type, which we don't really want
since such an object isn't supposed to have privileges of its own.
(You can't, for example, drop such privileges once they've been added
to an array type.)
ab89e465c is also to blame for a race condition in the regression tests:
privileges.sql transiently installed globally-applicable default
privileges on schemas, which sometimes got absorbed into the ACLs of
schemas created by concurrent test scripts. This should have resulted
in failures when privileges.sql tried to drop the role holding such
privileges; but thanks to the bug fixed here, it instead led to dangling
ACLs in the final state of the regression database. We'd managed not to
notice that, but it became obvious in the wake of commit da906766c, which
allowed the race condition to occur in pg_upgrade tests.
To fix, add a function recordDependencyOnNewAcl to encapsulate what
callers of get_user_default_acl need to do; while the original call
sites got that right via ad-hoc code, none of the later-added ones
have. Also change GenerateTypeDependencies to generate these
dependencies, which requires adding the typacl to its parameter list.
(That might be annoying if there are any extensions calling that
function directly; but if there are, they're most likely buggy in the
same way as the core callers were, so they need work anyway.) While
I was at it, I changed GenerateTypeDependencies to accept most of its
parameters in the form of a Form_pg_type pointer, making its parameter
list a bit less unwieldy and mistake-prone.
The test race condition is fixed just by wrapping the addition and
removal of default privileges into a single transaction, so that that
state is never visible externally. We might eventually prefer to
separate out tests of default privileges into a script that runs by
itself, but that would be a bigger change and would make the tests
run slower overall.
Back-patch relevant parts to all supported branches.
Discussion: https://postgr.es/m/15719.1541725287@sss.pgh.pa.us
2018-11-10 02:42:03 +01:00
|
|
|
* Returns NULL if built-in system defaults should be used.
|
|
|
|
*
|
|
|
|
* If the result is not NULL, caller must call recordDependencyOnNewAcl
|
|
|
|
* once the OID of the new object is known.
|
2009-10-05 21:24:49 +02:00
|
|
|
*/
|
|
|
|
Acl *
|
2017-10-12 00:35:19 +02:00
|
|
|
get_user_default_acl(ObjectType objtype, Oid ownerId, Oid nsp_oid)
|
2009-10-05 21:24:49 +02:00
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_TABLE:
|
2009-10-05 21:24:49 +02:00
|
|
|
defaclobjtype = DEFACLOBJ_RELATION;
|
|
|
|
break;
|
|
|
|
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_SEQUENCE:
|
2009-10-05 21:24:49 +02:00
|
|
|
defaclobjtype = DEFACLOBJ_SEQUENCE;
|
|
|
|
break;
|
|
|
|
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_FUNCTION:
|
2009-10-05 21:24:49 +02:00
|
|
|
defaclobjtype = DEFACLOBJ_FUNCTION;
|
|
|
|
break;
|
|
|
|
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_TYPE:
|
2011-12-19 23:05:19 +01:00
|
|
|
defaclobjtype = DEFACLOBJ_TYPE;
|
|
|
|
break;
|
|
|
|
|
2017-10-12 00:35:19 +02:00
|
|
|
case OBJECT_SCHEMA:
|
2017-03-28 17:58:55 +02:00
|
|
|
defaclobjtype = DEFACLOBJ_NAMESPACE;
|
|
|
|
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
|
|
|
|
Fix missing role dependencies for some schema and type ACLs.
This patch fixes several related cases in which pg_shdepend entries were
never made, or were lost, for references to roles appearing in the ACLs of
schemas and/or types. While that did no immediate harm, if a referenced
role were later dropped, the drop would be allowed and would leave a
dangling reference in the object's ACL. That still wasn't a big problem
for normal database usage, but it would cause obscure failures in
subsequent dump/reload or pg_upgrade attempts, taking the form of
attempts to grant privileges to all-numeric role names. (I think I've
seen field reports matching that symptom, but can't find any right now.)
Several cases are fixed here:
1. ALTER DOMAIN SET/DROP DEFAULT would lose the dependencies for any
existing ACL entries for the domain. This case is ancient, dating
back as far as we've had pg_shdepend tracking at all.
2. If a default type privilege applies, CREATE TYPE recorded the
ACL properly but forgot to install dependency entries for it.
This dates to the addition of default privileges for types in 9.2.
3. If a default schema privilege applies, CREATE SCHEMA recorded the
ACL properly but forgot to install dependency entries for it.
This dates to the addition of default privileges for schemas in v10
(commit ab89e465c).
Another somewhat-related problem is that when creating a relation
rowtype or implicit array type, TypeCreate would apply any available
default type privileges to that type, which we don't really want
since such an object isn't supposed to have privileges of its own.
(You can't, for example, drop such privileges once they've been added
to an array type.)
ab89e465c is also to blame for a race condition in the regression tests:
privileges.sql transiently installed globally-applicable default
privileges on schemas, which sometimes got absorbed into the ACLs of
schemas created by concurrent test scripts. This should have resulted
in failures when privileges.sql tried to drop the role holding such
privileges; but thanks to the bug fixed here, it instead led to dangling
ACLs in the final state of the regression database. We'd managed not to
notice that, but it became obvious in the wake of commit da906766c, which
allowed the race condition to occur in pg_upgrade tests.
To fix, add a function recordDependencyOnNewAcl to encapsulate what
callers of get_user_default_acl need to do; while the original call
sites got that right via ad-hoc code, none of the later-added ones
have. Also change GenerateTypeDependencies to generate these
dependencies, which requires adding the typacl to its parameter list.
(That might be annoying if there are any extensions calling that
function directly; but if there are, they're most likely buggy in the
same way as the core callers were, so they need work anyway.) While
I was at it, I changed GenerateTypeDependencies to accept most of its
parameters in the form of a Form_pg_type pointer, making its parameter
list a bit less unwieldy and mistake-prone.
The test race condition is fixed just by wrapping the addition and
removal of default privileges into a single transaction, so that that
state is never visible externally. We might eventually prefer to
separate out tests of default privileges into a script that runs by
itself, but that would be a bigger change and would make the tests
run slower overall.
Back-patch relevant parts to all supported branches.
Discussion: https://postgr.es/m/15719.1541725287@sss.pgh.pa.us
2018-11-10 02:42:03 +01:00
|
|
|
/*
|
|
|
|
* Record dependencies on roles mentioned in a new object's ACL.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
recordDependencyOnNewAcl(Oid classId, Oid objectId, int32 objsubId,
|
|
|
|
Oid ownerId, Acl *acl)
|
|
|
|
{
|
|
|
|
int nmembers;
|
|
|
|
Oid *members;
|
|
|
|
|
|
|
|
/* Nothing to do if ACL is defaulted */
|
|
|
|
if (acl == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Extract roles mentioned in ACL */
|
|
|
|
nmembers = aclmembers(acl, &members);
|
|
|
|
|
|
|
|
/* Update the shared dependency ACL info */
|
|
|
|
updateAclDependencies(classId, objectId, objsubId,
|
|
|
|
ownerId,
|
|
|
|
0, NULL,
|
|
|
|
nmembers, members);
|
|
|
|
}
|
|
|
|
|
2016-04-07 03:45:32 +02:00
|
|
|
/*
|
2017-01-30 05:05:07 +01:00
|
|
|
* Record initial privileges for the top-level object passed in.
|
2016-04-07 03:45:32 +02:00
|
|
|
*
|
2017-01-30 05:05:07 +01:00
|
|
|
* For the object passed in, this will record its ACL (if any) and the ACLs of
|
|
|
|
* any sub-objects (eg: columns) into pg_init_privs.
|
|
|
|
*
|
|
|
|
* Any new kinds of objects which have ACLs associated with them and can be
|
|
|
|
* added to an extension should be added to the if-else tree below.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
recordExtObjInitPriv(Oid objoid, Oid classoid)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* pg_class / pg_attribute
|
|
|
|
*
|
|
|
|
* If this is a relation then we need to see if there are any sub-objects
|
|
|
|
* (eg: columns) for it and, if so, be sure to call
|
|
|
|
* recordExtensionInitPrivWorker() for each one.
|
|
|
|
*/
|
|
|
|
if (classoid == RelationRelationId)
|
|
|
|
{
|
|
|
|
Form_pg_class pg_class_tuple;
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
|
|
|
HeapTuple tuple;
|
|
|
|
|
|
|
|
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(objoid));
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
elog(ERROR, "cache lookup failed for relation %u", objoid);
|
|
|
|
pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
|
|
|
|
|
2020-04-17 19:41:59 +02:00
|
|
|
/*
|
|
|
|
* Indexes don't have permissions, neither do the pg_class rows for
|
|
|
|
* composite types. (These cases are unreachable given the
|
|
|
|
* restrictions in ALTER EXTENSION ADD, but let's check anyway.)
|
|
|
|
*/
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
if (pg_class_tuple->relkind == RELKIND_INDEX ||
|
2020-04-17 19:41:59 +02:00
|
|
|
pg_class_tuple->relkind == RELKIND_PARTITIONED_INDEX ||
|
|
|
|
pg_class_tuple->relkind == RELKIND_COMPOSITE_TYPE)
|
|
|
|
{
|
|
|
|
ReleaseSysCache(tuple);
|
2017-01-30 05:05:07 +01:00
|
|
|
return;
|
2020-04-17 19:41:59 +02:00
|
|
|
}
|
2017-01-30 05:05:07 +01:00
|
|
|
|
|
|
|
/*
|
2020-04-17 19:41:59 +02:00
|
|
|
* If this isn't a sequence then it's possibly going to have
|
|
|
|
* column-level ACLs associated with it.
|
2017-01-30 05:05:07 +01:00
|
|
|
*/
|
|
|
|
if (pg_class_tuple->relkind != RELKIND_SEQUENCE)
|
|
|
|
{
|
|
|
|
AttrNumber curr_att;
|
|
|
|
AttrNumber nattrs = pg_class_tuple->relnatts;
|
|
|
|
|
|
|
|
for (curr_att = 1; curr_att <= nattrs; curr_att++)
|
|
|
|
{
|
|
|
|
HeapTuple attTuple;
|
|
|
|
Datum attaclDatum;
|
|
|
|
|
|
|
|
attTuple = SearchSysCache2(ATTNUM,
|
|
|
|
ObjectIdGetDatum(objoid),
|
|
|
|
Int16GetDatum(curr_att));
|
|
|
|
|
|
|
|
if (!HeapTupleIsValid(attTuple))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* ignore dropped columns */
|
|
|
|
if (((Form_pg_attribute) GETSTRUCT(attTuple))->attisdropped)
|
|
|
|
{
|
|
|
|
ReleaseSysCache(attTuple);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
attaclDatum = SysCacheGetAttr(ATTNUM, attTuple,
|
|
|
|
Anum_pg_attribute_attacl,
|
|
|
|
&isNull);
|
|
|
|
|
|
|
|
/* no need to do anything for a NULL ACL */
|
|
|
|
if (isNull)
|
|
|
|
{
|
|
|
|
ReleaseSysCache(attTuple);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
recordExtensionInitPrivWorker(objoid, classoid, curr_att,
|
|
|
|
DatumGetAclP(attaclDatum));
|
|
|
|
|
|
|
|
ReleaseSysCache(attTuple);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl,
|
|
|
|
&isNull);
|
|
|
|
|
|
|
|
/* Add the record, if any, for the top-level object */
|
|
|
|
if (!isNull)
|
|
|
|
recordExtensionInitPrivWorker(objoid, classoid, 0,
|
|
|
|
DatumGetAclP(aclDatum));
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
}
|
|
|
|
/* pg_foreign_data_wrapper */
|
|
|
|
else if (classoid == ForeignDataWrapperRelationId)
|
|
|
|
{
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
|
|
|
HeapTuple tuple;
|
|
|
|
|
|
|
|
tuple = SearchSysCache1(FOREIGNDATAWRAPPEROID,
|
|
|
|
ObjectIdGetDatum(objoid));
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
elog(ERROR, "cache lookup failed for foreign data wrapper %u",
|
|
|
|
objoid);
|
|
|
|
|
|
|
|
aclDatum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID, tuple,
|
|
|
|
Anum_pg_foreign_data_wrapper_fdwacl,
|
|
|
|
&isNull);
|
|
|
|
|
|
|
|
/* Add the record, if any, for the top-level object */
|
|
|
|
if (!isNull)
|
|
|
|
recordExtensionInitPrivWorker(objoid, classoid, 0,
|
|
|
|
DatumGetAclP(aclDatum));
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
}
|
|
|
|
/* pg_foreign_server */
|
|
|
|
else if (classoid == ForeignServerRelationId)
|
|
|
|
{
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
|
|
|
HeapTuple tuple;
|
|
|
|
|
|
|
|
tuple = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(objoid));
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
elog(ERROR, "cache lookup failed for foreign data wrapper %u",
|
|
|
|
objoid);
|
|
|
|
|
|
|
|
aclDatum = SysCacheGetAttr(FOREIGNSERVEROID, tuple,
|
|
|
|
Anum_pg_foreign_server_srvacl,
|
|
|
|
&isNull);
|
|
|
|
|
|
|
|
/* Add the record, if any, for the top-level object */
|
|
|
|
if (!isNull)
|
|
|
|
recordExtensionInitPrivWorker(objoid, classoid, 0,
|
|
|
|
DatumGetAclP(aclDatum));
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
}
|
|
|
|
/* pg_language */
|
|
|
|
else if (classoid == LanguageRelationId)
|
|
|
|
{
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
|
|
|
HeapTuple tuple;
|
|
|
|
|
|
|
|
tuple = SearchSysCache1(LANGOID, ObjectIdGetDatum(objoid));
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
elog(ERROR, "cache lookup failed for language %u", objoid);
|
|
|
|
|
|
|
|
aclDatum = SysCacheGetAttr(LANGOID, tuple, Anum_pg_language_lanacl,
|
|
|
|
&isNull);
|
|
|
|
|
|
|
|
/* Add the record, if any, for the top-level object */
|
|
|
|
if (!isNull)
|
|
|
|
recordExtensionInitPrivWorker(objoid, classoid, 0,
|
|
|
|
DatumGetAclP(aclDatum));
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
}
|
|
|
|
/* pg_largeobject_metadata */
|
|
|
|
else if (classoid == LargeObjectMetadataRelationId)
|
|
|
|
{
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
|
|
|
HeapTuple tuple;
|
|
|
|
ScanKeyData entry[1];
|
|
|
|
SysScanDesc scan;
|
|
|
|
Relation relation;
|
|
|
|
|
2020-04-17 19:41:59 +02:00
|
|
|
/*
|
|
|
|
* Note: this is dead code, given that we don't allow large objects to
|
|
|
|
* be made extension members. But it seems worth carrying in case
|
|
|
|
* some future caller of this function has need for it.
|
|
|
|
*/
|
2019-01-21 19:32:19 +01:00
|
|
|
relation = table_open(LargeObjectMetadataRelationId, RowExclusiveLock);
|
2017-01-30 05:05:07 +01:00
|
|
|
|
|
|
|
/* There's no syscache for pg_largeobject_metadata */
|
|
|
|
ScanKeyInit(&entry[0],
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
Anum_pg_largeobject_metadata_oid,
|
2017-01-30 05:05:07 +01:00
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(objoid));
|
|
|
|
|
|
|
|
scan = systable_beginscan(relation,
|
|
|
|
LargeObjectMetadataOidIndexId, true,
|
|
|
|
NULL, 1, entry);
|
|
|
|
|
|
|
|
tuple = systable_getnext(scan);
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
2017-06-04 22:20:03 +02:00
|
|
|
elog(ERROR, "could not find tuple for large object %u", objoid);
|
2017-01-30 05:05:07 +01:00
|
|
|
|
|
|
|
aclDatum = heap_getattr(tuple,
|
|
|
|
Anum_pg_largeobject_metadata_lomacl,
|
|
|
|
RelationGetDescr(relation), &isNull);
|
|
|
|
|
|
|
|
/* Add the record, if any, for the top-level object */
|
|
|
|
if (!isNull)
|
|
|
|
recordExtensionInitPrivWorker(objoid, classoid, 0,
|
|
|
|
DatumGetAclP(aclDatum));
|
|
|
|
|
|
|
|
systable_endscan(scan);
|
|
|
|
}
|
|
|
|
/* pg_namespace */
|
|
|
|
else if (classoid == NamespaceRelationId)
|
|
|
|
{
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
|
|
|
HeapTuple tuple;
|
|
|
|
|
|
|
|
tuple = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(objoid));
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
elog(ERROR, "cache lookup failed for function %u", objoid);
|
|
|
|
|
|
|
|
aclDatum = SysCacheGetAttr(NAMESPACEOID, tuple,
|
|
|
|
Anum_pg_namespace_nspacl, &isNull);
|
|
|
|
|
|
|
|
/* Add the record, if any, for the top-level object */
|
|
|
|
if (!isNull)
|
|
|
|
recordExtensionInitPrivWorker(objoid, classoid, 0,
|
|
|
|
DatumGetAclP(aclDatum));
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
}
|
|
|
|
/* pg_proc */
|
|
|
|
else if (classoid == ProcedureRelationId)
|
|
|
|
{
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
|
|
|
HeapTuple tuple;
|
|
|
|
|
|
|
|
tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(objoid));
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
elog(ERROR, "cache lookup failed for function %u", objoid);
|
|
|
|
|
|
|
|
aclDatum = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_proacl,
|
|
|
|
&isNull);
|
|
|
|
|
|
|
|
/* Add the record, if any, for the top-level object */
|
|
|
|
if (!isNull)
|
|
|
|
recordExtensionInitPrivWorker(objoid, classoid, 0,
|
|
|
|
DatumGetAclP(aclDatum));
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
}
|
|
|
|
/* pg_type */
|
|
|
|
else if (classoid == TypeRelationId)
|
|
|
|
{
|
|
|
|
Datum aclDatum;
|
|
|
|
bool isNull;
|
|
|
|
HeapTuple tuple;
|
|
|
|
|
|
|
|
tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(objoid));
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
elog(ERROR, "cache lookup failed for function %u", objoid);
|
|
|
|
|
|
|
|
aclDatum = SysCacheGetAttr(TYPEOID, tuple, Anum_pg_type_typacl,
|
|
|
|
&isNull);
|
|
|
|
|
|
|
|
/* Add the record, if any, for the top-level object */
|
|
|
|
if (!isNull)
|
|
|
|
recordExtensionInitPrivWorker(objoid, classoid, 0,
|
|
|
|
DatumGetAclP(aclDatum));
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
}
|
|
|
|
else if (classoid == AccessMethodRelationId ||
|
|
|
|
classoid == AggregateRelationId ||
|
|
|
|
classoid == CastRelationId ||
|
|
|
|
classoid == CollationRelationId ||
|
|
|
|
classoid == ConversionRelationId ||
|
|
|
|
classoid == EventTriggerRelationId ||
|
|
|
|
classoid == OperatorRelationId ||
|
|
|
|
classoid == OperatorClassRelationId ||
|
|
|
|
classoid == OperatorFamilyRelationId ||
|
|
|
|
classoid == NamespaceRelationId ||
|
|
|
|
classoid == TSConfigRelationId ||
|
|
|
|
classoid == TSDictionaryRelationId ||
|
|
|
|
classoid == TSParserRelationId ||
|
|
|
|
classoid == TSTemplateRelationId ||
|
|
|
|
classoid == TransformRelationId
|
|
|
|
)
|
|
|
|
{
|
|
|
|
/* no ACL for these object types, so do nothing. */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* complain if we are given a class OID for a class that extensions don't
|
|
|
|
* support or that we don't recognize.
|
|
|
|
*/
|
|
|
|
else
|
|
|
|
{
|
|
|
|
elog(ERROR, "unrecognized or unsupported class OID: %u", classoid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For the object passed in, remove its ACL and the ACLs of any object subIds
|
|
|
|
* from pg_init_privs (via recordExtensionInitPrivWorker()).
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
removeExtObjInitPriv(Oid objoid, Oid classoid)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If this is a relation then we need to see if there are any sub-objects
|
|
|
|
* (eg: columns) for it and, if so, be sure to call
|
|
|
|
* recordExtensionInitPrivWorker() for each one.
|
|
|
|
*/
|
|
|
|
if (classoid == RelationRelationId)
|
|
|
|
{
|
|
|
|
Form_pg_class pg_class_tuple;
|
|
|
|
HeapTuple tuple;
|
|
|
|
|
|
|
|
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(objoid));
|
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
elog(ERROR, "cache lookup failed for relation %u", objoid);
|
|
|
|
pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
|
|
|
|
|
2020-04-17 19:41:59 +02:00
|
|
|
/*
|
|
|
|
* Indexes don't have permissions, neither do the pg_class rows for
|
|
|
|
* composite types. (These cases are unreachable given the
|
|
|
|
* restrictions in ALTER EXTENSION DROP, but let's check anyway.)
|
|
|
|
*/
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
if (pg_class_tuple->relkind == RELKIND_INDEX ||
|
2020-04-17 19:41:59 +02:00
|
|
|
pg_class_tuple->relkind == RELKIND_PARTITIONED_INDEX ||
|
|
|
|
pg_class_tuple->relkind == RELKIND_COMPOSITE_TYPE)
|
|
|
|
{
|
|
|
|
ReleaseSysCache(tuple);
|
2017-01-30 05:05:07 +01:00
|
|
|
return;
|
2020-04-17 19:41:59 +02:00
|
|
|
}
|
2017-01-30 05:05:07 +01:00
|
|
|
|
|
|
|
/*
|
2020-04-17 19:41:59 +02:00
|
|
|
* If this isn't a sequence then it's possibly going to have
|
|
|
|
* column-level ACLs associated with it.
|
2017-01-30 05:05:07 +01:00
|
|
|
*/
|
|
|
|
if (pg_class_tuple->relkind != RELKIND_SEQUENCE)
|
|
|
|
{
|
|
|
|
AttrNumber curr_att;
|
|
|
|
AttrNumber nattrs = pg_class_tuple->relnatts;
|
|
|
|
|
|
|
|
for (curr_att = 1; curr_att <= nattrs; curr_att++)
|
|
|
|
{
|
|
|
|
HeapTuple attTuple;
|
|
|
|
|
|
|
|
attTuple = SearchSysCache2(ATTNUM,
|
|
|
|
ObjectIdGetDatum(objoid),
|
|
|
|
Int16GetDatum(curr_att));
|
|
|
|
|
|
|
|
if (!HeapTupleIsValid(attTuple))
|
|
|
|
continue;
|
|
|
|
|
2017-06-30 21:54:14 +02:00
|
|
|
/* when removing, remove all entries, even dropped columns */
|
2017-01-30 05:05:07 +01:00
|
|
|
|
|
|
|
recordExtensionInitPrivWorker(objoid, classoid, curr_att, NULL);
|
|
|
|
|
|
|
|
ReleaseSysCache(attTuple);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove the record, if any, for the top-level object */
|
|
|
|
recordExtensionInitPrivWorker(objoid, classoid, 0, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Record initial ACL for an extension object
|
2016-04-07 03:45:32 +02:00
|
|
|
*
|
|
|
|
* 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)
|
|
|
|
{
|
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;
|
|
|
|
|
2017-01-30 05:05:07 +01:00
|
|
|
recordExtensionInitPrivWorker(objoid, classoid, objsubid, new_acl);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Record initial ACL for an extension object, worker.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* Generally speaking, do *not* use this function directly but instead use
|
|
|
|
* recordExtensionInitPriv(), which checks if 'creating_extension' is set.
|
|
|
|
* This function does *not* check if 'creating_extension' is set as it is also
|
|
|
|
* used when an object is added to or removed from an extension via ALTER
|
|
|
|
* EXTENSION ... ADD/DROP.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
recordExtensionInitPrivWorker(Oid objoid, Oid classoid, int objsubid, Acl *new_acl)
|
|
|
|
{
|
|
|
|
Relation relation;
|
|
|
|
ScanKeyData key[3];
|
|
|
|
SysScanDesc scan;
|
|
|
|
HeapTuple tuple;
|
|
|
|
HeapTuple oldtuple;
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
relation = table_open(InitPrivsRelationId, RowExclusiveLock);
|
2016-04-07 03:45:32 +02:00
|
|
|
|
|
|
|
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));
|
|
|
|
|
2018-03-03 17:23:33 +01:00
|
|
|
values[Anum_pg_init_privs_initprivs - 1] = PointerGetDatum(new_acl);
|
|
|
|
replace[Anum_pg_init_privs_initprivs - 1] = true;
|
2016-04-07 03:45:32 +02:00
|
|
|
|
|
|
|
oldtuple = heap_modify_tuple(oldtuple, RelationGetDescr(relation),
|
|
|
|
values, nulls, replace);
|
|
|
|
|
2017-01-31 22:42:24 +01:00
|
|
|
CatalogTupleUpdate(relation, &oldtuple->t_self, oldtuple);
|
2016-04-07 03:45:32 +02:00
|
|
|
}
|
|
|
|
else
|
2017-02-01 22:13:30 +01:00
|
|
|
{
|
2016-04-07 03:45:32 +02:00
|
|
|
/* new_acl is NULL, so delete the entry we found. */
|
2017-02-01 22:13:30 +01:00
|
|
|
CatalogTupleDelete(relation, &oldtuple->t_self);
|
|
|
|
}
|
2016-04-07 03:45:32 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Datum values[Natts_pg_init_privs];
|
|
|
|
bool nulls[Natts_pg_init_privs];
|
|
|
|
|
2017-01-30 05:05:07 +01:00
|
|
|
/*
|
|
|
|
* Only add a new entry if the new ACL is non-NULL.
|
|
|
|
*
|
|
|
|
* If we are passed in a NULL ACL and no entry exists, we can just
|
|
|
|
* fall through and do nothing.
|
|
|
|
*/
|
|
|
|
if (new_acl)
|
|
|
|
{
|
|
|
|
/* No entry found, so add it. */
|
|
|
|
MemSet(nulls, false, sizeof(nulls));
|
2016-04-07 03:45:32 +02:00
|
|
|
|
2017-01-30 05:05:07 +01:00
|
|
|
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);
|
2016-04-07 03:45:32 +02:00
|
|
|
|
2017-01-30 05:05:07 +01:00
|
|
|
/* This function only handles initial privileges of extensions */
|
|
|
|
values[Anum_pg_init_privs_privtype - 1] =
|
|
|
|
CharGetDatum(INITPRIVS_EXTENSION);
|
2016-04-07 03:45:32 +02:00
|
|
|
|
2018-03-03 17:23:33 +01:00
|
|
|
values[Anum_pg_init_privs_initprivs - 1] = PointerGetDatum(new_acl);
|
2016-04-07 03:45:32 +02:00
|
|
|
|
2017-01-30 05:05:07 +01:00
|
|
|
tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls);
|
2016-04-07 03:45:32 +02:00
|
|
|
|
2017-01-31 22:42:24 +01:00
|
|
|
CatalogTupleInsert(relation, tuple);
|
2017-01-30 05:05:07 +01:00
|
|
|
}
|
2016-04-07 03:45:32 +02:00
|
|
|
}
|
|
|
|
|
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();
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(relation, RowExclusiveLock);
|
2016-04-07 03:45:32 +02:00
|
|
|
}
|