postgresql/src/backend/catalog/aclchk.c

6058 lines
162 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* aclchk.c
* Routines to check access control permissions.
*
* Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
2010-09-20 22:08:53 +02:00
* src/backend/catalog/aclchk.c
*
* NOTES
* See acl.h.
*
*-------------------------------------------------------------------------
*/
1996-11-03 07:54:38 +01:00
#include "postgres.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
#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"
#include "access/xact.h"
#include "catalog/binary_upgrade.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
1999-07-16 07:00:38 +02:00
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_am.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
#include "catalog/pg_default_acl.h"
#include "catalog/pg_event_trigger.h"
#include "catalog/pg_extension.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_init_privs.h"
#include "catalog/pg_language.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_largeobject_metadata.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#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"
#include "catalog/pg_subscription.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_transform.h"
#include "catalog/pg_ts_config.h"
#include "catalog/pg_ts_dict.h"
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
Allow on-the-fly capture of DDL event details This feature lets user code inspect and take action on DDL events. Whenever a ddl_command_end event trigger is installed, DDL actions executed are saved to a list which can be inspected during execution of a function attached to ddl_command_end. The set-returning function pg_event_trigger_ddl_commands can be used to list actions so captured; it returns data about the type of command executed, as well as the affected object. This is sufficient for many uses of this feature. For the cases where it is not, we also provide a "command" column of a new pseudo-type pg_ddl_command, which is a pointer to a C structure that can be accessed by C code. The struct contains all the info necessary to completely inspect and even reconstruct the executed command. There is no actual deparse code here; that's expected to come later. What we have is enough infrastructure that the deparsing can be done in an external extension. The intention is that we will add some deparsing code in a later release, as an in-core extension. A new test module is included. It's probably insufficient as is, but it should be sufficient as a starting point for a more complete and future-proof approach. Authors: Álvaro Herrera, with some help from Andres Freund, Ian Barwick, Abhijit Menon-Sen. Reviews by Andres Freund, Robert Haas, Amit Kapila, Michael Paquier, Craig Ringer, David Steele. Additional input from Chris Browne, Dimitri Fontaine, Stephen Frost, Petr Jelínek, Tom Lane, Jim Nasby, Steven Singer, Pavel Stěhule. Based on original work by Dimitri Fontaine, though I didn't use his code. Discussion: https://www.postgresql.org/message-id/m2txrsdzxa.fsf@2ndQuadrant.fr https://www.postgresql.org/message-id/20131108153322.GU5809@eldon.alvh.no-ip.org https://www.postgresql.org/message-id/20150215044814.GL3391@alvh.no-ip.org
2015-05-12 00:14:31 +02:00
#include "commands/event_trigger.h"
#include "commands/extension.h"
#include "commands/proclang.h"
#include "commands/tablespace.h"
#include "foreign/foreign.h"
1999-07-16 07:00:38 +02:00
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "parser/parse_func.h"
#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"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
/*
* Internal format used by ALTER DEFAULT PRIVILEGES.
*/
typedef struct
{
2010-02-26 03:01:40 +01:00
Oid roleid; /* owning role */
Oid nspid; /* namespace, or InvalidOid if none */
/* remaining fields are same as in InternalGrant: */
bool is_grant;
ObjectType objtype;
bool all_privs;
AclMode privileges;
List *grantees;
bool grant_option;
DropBehavior behavior;
} InternalDefaultACL;
/*
* 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;
static void ExecGrantStmt_oids(InternalGrant *istmt);
static void ExecGrant_Relation(InternalGrant *grantStmt);
static void ExecGrant_Database(InternalGrant *grantStmt);
static void ExecGrant_Fdw(InternalGrant *grantStmt);
static void ExecGrant_ForeignServer(InternalGrant *grantStmt);
static void ExecGrant_Function(InternalGrant *grantStmt);
static void ExecGrant_Language(InternalGrant *grantStmt);
static void ExecGrant_Largeobject(InternalGrant *grantStmt);
static void ExecGrant_Namespace(InternalGrant *grantStmt);
static void ExecGrant_Tablespace(InternalGrant *grantStmt);
static void ExecGrant_Type(InternalGrant *grantStmt);
static void SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames);
static void SetDefaultACL(InternalDefaultACL *iacls);
static List *objectNamesToOids(ObjectType objtype, List *objnames);
static List *objectsInSchemaToOids(ObjectType objtype, List *nspnames);
static List *getRelationsInNamespace(Oid namespaceId, char relkind);
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);
static AclMode string_to_privilege(const char *privname);
static const char *privilege_to_string(AclMode privilege);
static AclMode restrict_and_check_grant(bool is_grant, AclMode avail_goptions,
bool all_privs, AclMode privileges,
Oid objectId, Oid grantorId,
ObjectType objtype, const char *objname,
AttrNumber att_number, const char *colname);
static AclMode pg_aclmask(ObjectType objtype, Oid table_oid, AttrNumber attnum,
Oid roleid, AclMode mask, AclMaskHow how);
static void recordExtensionInitPriv(Oid objoid, Oid classoid, int objsubid,
Acl *new_acl);
static void recordExtensionInitPrivWorker(Oid objoid, Oid classoid, int objsubid,
Acl *new_acl);
#ifdef ACLDEBUG
static void
1997-09-08 22:59:27 +02:00
dumpacl(Acl *acl)
{
int i;
AclItem *aip;
elog(DEBUG2, "acl size = %d, # acls = %d",
ACL_SIZE(acl), ACL_NUM(acl));
aip = ACL_DAT(acl);
for (i = 0; i < ACL_NUM(acl); ++i)
elog(DEBUG2, " acl[%d]: %s", i,
DatumGetCString(DirectFunctionCall1(aclitemout,
2005-10-15 04:49:52 +02:00
PointerGetDatum(aip + i))));
}
Phase 2 of pgindent updates. Change pg_bsd_indent to follow upstream rules for placement of comments to the right of code, and remove pgindent hack that caused comments following #endif to not obey the general rule. Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using the published version of pg_bsd_indent, but a hacked-up version that tried to minimize the amount of movement of comments to the right of code. The situation of interest is where such a comment has to be moved to the right of its default placement at column 33 because there's code there. BSD indent has always moved right in units of tab stops in such cases --- but in the previous incarnation, indent was working in 8-space tab stops, while now it knows we use 4-space tabs. So the net result is that in about half the cases, such comments are placed one tab stop left of before. This is better all around: it leaves more room on the line for comment text, and it means that in such cases the comment uniformly starts at the next 4-space tab stop after the code, rather than sometimes one and sometimes two tabs after. Also, ensure that comments following #endif are indented the same as comments following other preprocessor commands such as #else. That inconsistency turns out to have been self-inflicted damage from a poorly-thought-through post-indent "fixup" in pgindent. This patch is much less interesting than the first round of indent changes, but also bulkier, so I thought it best to separate the effects. Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:18:54 +02:00
#endif /* ACLDEBUG */
/*
* 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.
*
* NB: the original old_acl is pfree'd.
*/
static Acl *
merge_acl_with_grant(Acl *old_acl, bool is_grant,
bool grant_option, DropBehavior behavior,
List *grantees, AclMode privileges,
Oid grantorId, Oid ownerId)
{
unsigned modechg;
ListCell *j;
Acl *new_acl;
modechg = is_grant ? ACL_MODECHG_ADD : ACL_MODECHG_DEL;
#ifdef ACLDEBUG
dumpacl(old_acl);
#endif
new_acl = old_acl;
foreach(j, grantees)
{
AclItem aclitem;
Acl *newer_acl;
aclitem.ai_grantee = lfirst_oid(j);
/*
* 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.
*/
if (is_grant && grant_option && aclitem.ai_grantee == ACL_ID_PUBLIC)
ereport(ERROR,
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
errmsg("grant options can only be granted to roles")));
aclitem.ai_grantor = grantorId;
/*
* The asymmetry in the conditions here comes from the spec. In
2005-10-15 04:49:52 +02:00
* GRANT, the grant_option flag signals WITH GRANT OPTION, which means
* to grant both the basic privilege and its grant option. But in
* REVOKE, plain revoke revokes both the basic privilege and its grant
* option, while REVOKE GRANT OPTION revokes only the option.
*/
ACLITEM_SET_PRIVS_GOPTIONS(aclitem,
(is_grant || !grant_option) ? privileges : ACL_NO_RIGHTS,
(!is_grant || grant_option) ? privileges : ACL_NO_RIGHTS);
newer_acl = aclupdate(new_acl, &aclitem, modechg, ownerId, behavior);
/* avoid memory leak when there are many grantees */
pfree(new_acl);
new_acl = newer_acl;
#ifdef ACLDEBUG
dumpacl(new_acl);
#endif
}
return new_acl;
}
/*
* 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,
ObjectType objtype, const char *objname,
AttrNumber att_number, const char *colname)
{
2006-10-04 02:30:14 +02:00
AclMode this_privileges;
AclMode whole_mask;
switch (objtype)
{
case OBJECT_COLUMN:
whole_mask = ACL_ALL_RIGHTS_COLUMN;
break;
case OBJECT_TABLE:
whole_mask = ACL_ALL_RIGHTS_RELATION;
break;
case OBJECT_SEQUENCE:
whole_mask = ACL_ALL_RIGHTS_SEQUENCE;
break;
case OBJECT_DATABASE:
whole_mask = ACL_ALL_RIGHTS_DATABASE;
break;
case OBJECT_FUNCTION:
whole_mask = ACL_ALL_RIGHTS_FUNCTION;
break;
case OBJECT_LANGUAGE:
whole_mask = ACL_ALL_RIGHTS_LANGUAGE;
break;
case OBJECT_LARGEOBJECT:
whole_mask = ACL_ALL_RIGHTS_LARGEOBJECT;
break;
case OBJECT_SCHEMA:
whole_mask = ACL_ALL_RIGHTS_SCHEMA;
break;
case OBJECT_TABLESPACE:
whole_mask = ACL_ALL_RIGHTS_TABLESPACE;
break;
case OBJECT_FDW:
whole_mask = ACL_ALL_RIGHTS_FDW;
break;
case OBJECT_FOREIGN_SERVER:
whole_mask = ACL_ALL_RIGHTS_FOREIGN_SERVER;
break;
case OBJECT_EVENT_TRIGGER:
elog(ERROR, "grantable rights not supported for event triggers");
/* not reached, but keep compiler quiet */
return ACL_NO_RIGHTS;
case OBJECT_TYPE:
whole_mask = ACL_ALL_RIGHTS_TYPE;
break;
default:
elog(ERROR, "unrecognized object type: %d", objtype);
/* not reached, but keep compiler quiet */
return ACL_NO_RIGHTS;
}
/*
2006-10-04 02:30:14 +02:00
* If we found no grant options, consider whether to issue a hard error.
* Per spec, having any privilege at all on the object will get you by
* here.
*/
if (avail_goptions == ACL_NO_RIGHTS)
{
if (pg_aclmask(objtype, objectId, att_number, grantorId,
whole_mask | ACL_GRANT_OPTION_FOR(whole_mask),
ACLMASK_ANY) == ACL_NO_RIGHTS)
{
if (objtype == OBJECT_COLUMN && colname)
aclcheck_error_col(ACLCHECK_NO_PRIV, objtype, objname, colname);
else
aclcheck_error(ACLCHECK_NO_PRIV, objtype, objname);
}
}
/*
* 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
2006-10-04 02:30:14 +02:00
* spec says to do: the spec seems to want a warning only if no privilege
* bits actually change in the ACL. In practice that behavior seems much
* too noisy, as well as inconsistent with the GRANT case.)
*/
this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions);
if (is_grant)
{
if (this_privileges == 0)
2010-07-06 21:19:02 +02:00
{
if (objtype == OBJECT_COLUMN && colname)
ereport(WARNING,
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
errmsg("no privileges were granted for column \"%s\" of relation \"%s\"",
colname, objname)));
else
ereport(WARNING,
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
errmsg("no privileges were granted for \"%s\"",
objname)));
}
else if (!all_privs && this_privileges != privileges)
{
if (objtype == OBJECT_COLUMN && colname)
ereport(WARNING,
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
errmsg("not all privileges were granted for column \"%s\" of relation \"%s\"",
colname, objname)));
else
ereport(WARNING,
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
errmsg("not all privileges were granted for \"%s\"",
objname)));
}
}
else
{
if (this_privileges == 0)
{
if (objtype == OBJECT_COLUMN && colname)
ereport(WARNING,
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
errmsg("no privileges could be revoked for column \"%s\" of relation \"%s\"",
colname, objname)));
else
ereport(WARNING,
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
errmsg("no privileges could be revoked for \"%s\"",
objname)));
}
else if (!all_privs && this_privileges != privileges)
{
if (objtype == OBJECT_COLUMN && colname)
ereport(WARNING,
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
errmsg("not all privileges could be revoked for column \"%s\" of relation \"%s\"",
colname, objname)));
else
ereport(WARNING,
(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
errmsg("not all privileges could be revoked for \"%s\"",
objname)));
}
}
return this_privileges;
}
/*
* Called to execute the utility commands GRANT and REVOKE
*/
void
ExecuteGrantStmt(GrantStmt *stmt)
{
InternalGrant istmt;
ListCell *cell;
const char *errormsg;
AclMode all_privileges;
/*
* Turn the regular GrantStmt into the InternalGrant form.
*/
istmt.is_grant = stmt->is_grant;
istmt.objtype = stmt->objtype;
/* Collect the OIDs of the target objects */
switch (stmt->targtype)
{
case ACL_TARGET_OBJECT:
istmt.objects = objectNamesToOids(stmt->objtype, stmt->objects);
break;
case ACL_TARGET_ALL_IN_SCHEMA:
istmt.objects = objectsInSchemaToOids(stmt->objtype, stmt->objects);
break;
2010-02-26 03:01:40 +01:00
/* ACL_TARGET_DEFAULTS should not be seen here */
default:
elog(ERROR, "unrecognized GrantStmt.targtype: %d",
(int) stmt->targtype);
}
/* all_privs to be filled below */
/* privileges to be filled below */
istmt.col_privs = NIL; /* may get filled below */
istmt.grantees = NIL; /* filled below */
istmt.grant_option = stmt->grant_option;
istmt.behavior = stmt->behavior;
/*
2015-05-24 03:35:49 +02:00
* Convert the RoleSpec list into an Oid list. Note that at this point we
* insert an ACL_ID_PUBLIC into the list if appropriate, so downstream
* there shouldn't be any additional work needed to support this case.
*/
foreach(cell, stmt->grantees)
{
2015-05-24 03:35:49 +02:00
RoleSpec *grantee = (RoleSpec *) lfirst(cell);
Oid grantee_uid;
switch (grantee->roletype)
{
case ROLESPEC_PUBLIC:
grantee_uid = ACL_ID_PUBLIC;
break;
default:
grantee_uid = get_rolespec_oid(grantee, false);
break;
}
istmt.grantees = lappend_oid(istmt.grantees, grantee_uid);
}
/*
* Convert stmt->privileges, a list of AccessPriv nodes, into an AclMode
* bitmask. Note: objtype can't be OBJECT_COLUMN.
*/
switch (stmt->objtype)
{
case OBJECT_TABLE:
2006-10-04 02:30:14 +02:00
/*
* Because this might be a sequence, we test both relation and
* sequence bits, and later do a more limited test when we know
* the object type.
*/
all_privileges = ACL_ALL_RIGHTS_RELATION | ACL_ALL_RIGHTS_SEQUENCE;
errormsg = gettext_noop("invalid privilege type %s for relation");
break;
case OBJECT_SEQUENCE:
all_privileges = ACL_ALL_RIGHTS_SEQUENCE;
errormsg = gettext_noop("invalid privilege type %s for sequence");
break;
case OBJECT_DATABASE:
all_privileges = ACL_ALL_RIGHTS_DATABASE;
errormsg = gettext_noop("invalid privilege type %s for database");
break;
case OBJECT_DOMAIN:
all_privileges = ACL_ALL_RIGHTS_TYPE;
errormsg = gettext_noop("invalid privilege type %s for domain");
break;
case OBJECT_FUNCTION:
all_privileges = ACL_ALL_RIGHTS_FUNCTION;
errormsg = gettext_noop("invalid privilege type %s for function");
break;
case OBJECT_LANGUAGE:
all_privileges = ACL_ALL_RIGHTS_LANGUAGE;
errormsg = gettext_noop("invalid privilege type %s for language");
break;
case OBJECT_LARGEOBJECT:
all_privileges = ACL_ALL_RIGHTS_LARGEOBJECT;
errormsg = gettext_noop("invalid privilege type %s for large object");
break;
case OBJECT_SCHEMA:
all_privileges = ACL_ALL_RIGHTS_SCHEMA;
errormsg = gettext_noop("invalid privilege type %s for schema");
break;
case OBJECT_PROCEDURE:
all_privileges = ACL_ALL_RIGHTS_FUNCTION;
errormsg = gettext_noop("invalid privilege type %s for procedure");
break;
case OBJECT_ROUTINE:
all_privileges = ACL_ALL_RIGHTS_FUNCTION;
errormsg = gettext_noop("invalid privilege type %s for routine");
break;
case OBJECT_TABLESPACE:
all_privileges = ACL_ALL_RIGHTS_TABLESPACE;
errormsg = gettext_noop("invalid privilege type %s for tablespace");
break;
case OBJECT_TYPE:
all_privileges = ACL_ALL_RIGHTS_TYPE;
errormsg = gettext_noop("invalid privilege type %s for type");
break;
case OBJECT_FDW:
all_privileges = ACL_ALL_RIGHTS_FDW;
errormsg = gettext_noop("invalid privilege type %s for foreign-data wrapper");
break;
case OBJECT_FOREIGN_SERVER:
all_privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
errormsg = gettext_noop("invalid privilege type %s for foreign server");
break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) stmt->objtype);
/* keep compiler quiet */
all_privileges = ACL_NO_RIGHTS;
errormsg = NULL;
}
if (stmt->privileges == NIL)
{
istmt.all_privs = true;
2006-10-04 02:30:14 +02:00
/*
* will be turned into ACL_ALL_RIGHTS_* by the internal routines
* depending on the object type
*/
istmt.privileges = ACL_NO_RIGHTS;
}
else
{
istmt.all_privs = false;
istmt.privileges = ACL_NO_RIGHTS;
foreach(cell, stmt->privileges)
{
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)
{
if (stmt->objtype != OBJECT_TABLE)
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);
if (priv & ~((AclMode) all_privileges))
ereport(ERROR,
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
errmsg(errormsg, privilege_to_string(priv))));
istmt.privileges |= priv;
}
}
ExecGrantStmt_oids(&istmt);
}
/*
* ExecGrantStmt_oids
*
* Internal entry point for granting and revoking privileges.
*/
static void
ExecGrantStmt_oids(InternalGrant *istmt)
{
switch (istmt->objtype)
{
case OBJECT_TABLE:
case OBJECT_SEQUENCE:
ExecGrant_Relation(istmt);
break;
case OBJECT_DATABASE:
ExecGrant_Database(istmt);
break;
case OBJECT_DOMAIN:
case OBJECT_TYPE:
ExecGrant_Type(istmt);
break;
case OBJECT_FDW:
ExecGrant_Fdw(istmt);
break;
case OBJECT_FOREIGN_SERVER:
ExecGrant_ForeignServer(istmt);
break;
case OBJECT_FUNCTION:
case OBJECT_PROCEDURE:
case OBJECT_ROUTINE:
ExecGrant_Function(istmt);
break;
case OBJECT_LANGUAGE:
ExecGrant_Language(istmt);
break;
case OBJECT_LARGEOBJECT:
ExecGrant_Largeobject(istmt);
break;
case OBJECT_SCHEMA:
ExecGrant_Namespace(istmt);
break;
case OBJECT_TABLESPACE:
ExecGrant_Tablespace(istmt);
break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) 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
/*
* Pass the info to event triggers about the just-executed GRANT. Note
* that we prefer to do it after actually executing it, because that gives
* the functions a chance to adjust the istmt with privileges actually
* granted.
*/
if (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);
}
/*
* objectNamesToOids
*
* Turn a list of object names of a given type into an Oid list.
*
* 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.
*/
static List *
objectNamesToOids(ObjectType objtype, List *objnames)
{
List *objects = NIL;
ListCell *cell;
Assert(objnames != NIL);
switch (objtype)
{
case OBJECT_TABLE:
case OBJECT_SEQUENCE:
foreach(cell, objnames)
{
RangeVar *relvar = (RangeVar *) lfirst(cell);
Oid relOid;
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);
objects = lappend_oid(objects, relOid);
}
break;
case OBJECT_DATABASE:
foreach(cell, objnames)
{
char *dbname = strVal(lfirst(cell));
Oid dbid;
dbid = get_database_oid(dbname, false);
objects = lappend_oid(objects, dbid);
}
break;
case OBJECT_DOMAIN:
case OBJECT_TYPE:
foreach(cell, objnames)
{
List *typname = (List *) lfirst(cell);
Oid oid;
oid = typenameTypeId(NULL, makeTypeNameFromNameList(typname));
objects = lappend_oid(objects, oid);
}
break;
case OBJECT_FUNCTION:
foreach(cell, objnames)
{
ObjectWithArgs *func = (ObjectWithArgs *) lfirst(cell);
Oid funcid;
funcid = LookupFuncWithArgs(OBJECT_FUNCTION, func, false);
objects = lappend_oid(objects, funcid);
}
break;
case OBJECT_LANGUAGE:
foreach(cell, objnames)
{
char *langname = strVal(lfirst(cell));
Oid oid;
oid = get_language_oid(langname, false);
objects = lappend_oid(objects, oid);
}
break;
case OBJECT_LARGEOBJECT:
foreach(cell, objnames)
{
Oid lobjOid = oidparse(lfirst(cell));
if (!LargeObjectExists(lobjOid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("large object %u does not exist",
lobjOid)));
objects = lappend_oid(objects, lobjOid);
}
break;
case OBJECT_SCHEMA:
foreach(cell, objnames)
{
char *nspname = strVal(lfirst(cell));
Oid oid;
oid = get_namespace_oid(nspname, false);
objects = lappend_oid(objects, oid);
}
break;
case OBJECT_PROCEDURE:
foreach(cell, objnames)
{
ObjectWithArgs *func = (ObjectWithArgs *) lfirst(cell);
Oid procid;
procid = LookupFuncWithArgs(OBJECT_PROCEDURE, func, false);
objects = lappend_oid(objects, procid);
}
break;
case OBJECT_ROUTINE:
foreach(cell, objnames)
{
ObjectWithArgs *func = (ObjectWithArgs *) lfirst(cell);
Oid routid;
routid = LookupFuncWithArgs(OBJECT_ROUTINE, func, false);
objects = lappend_oid(objects, routid);
}
break;
case OBJECT_TABLESPACE:
foreach(cell, objnames)
{
char *spcname = strVal(lfirst(cell));
Oid spcoid;
spcoid = get_tablespace_oid(spcname, false);
objects = lappend_oid(objects, spcoid);
}
break;
case OBJECT_FDW:
foreach(cell, objnames)
{
char *fdwname = strVal(lfirst(cell));
Oid fdwid = get_foreign_data_wrapper_oid(fdwname, false);
objects = lappend_oid(objects, fdwid);
}
break;
case OBJECT_FOREIGN_SERVER:
foreach(cell, objnames)
{
char *srvname = strVal(lfirst(cell));
Oid srvid = get_foreign_server_oid(srvname, false);
objects = lappend_oid(objects, srvid);
}
break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
}
return objects;
}
/*
* objectsInSchemaToOids
*
* Find all objects of a given type in specified schemas, and make a list
* of their Oids. We check USAGE privilege on the schemas, but there is
* no privilege checking on the individual objects here.
*/
static List *
objectsInSchemaToOids(ObjectType objtype, List *nspnames)
{
List *objects = NIL;
ListCell *cell;
foreach(cell, nspnames)
{
char *nspname = strVal(lfirst(cell));
Oid namespaceId;
List *objs;
namespaceId = LookupExplicitNamespace(nspname, false);
switch (objtype)
{
case OBJECT_TABLE:
objs = getRelationsInNamespace(namespaceId, RELKIND_RELATION);
objects = list_concat(objects, objs);
objs = getRelationsInNamespace(namespaceId, RELKIND_VIEW);
objects = list_concat(objects, objs);
objs = getRelationsInNamespace(namespaceId, RELKIND_MATVIEW);
objects = list_concat(objects, objs);
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);
break;
case OBJECT_SEQUENCE:
objs = getRelationsInNamespace(namespaceId, RELKIND_SEQUENCE);
objects = list_concat(objects, objs);
break;
case OBJECT_FUNCTION:
case OBJECT_PROCEDURE:
case OBJECT_ROUTINE:
{
ScanKeyData key[2];
int keycount;
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;
HeapTuple tuple;
keycount = 0;
ScanKeyInit(&key[keycount++],
Anum_pg_proc_pronamespace,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(namespaceId));
if (objtype == OBJECT_FUNCTION)
/* includes aggregates and window functions */
ScanKeyInit(&key[keycount++],
Anum_pg_proc_prokind,
BTEqualStrategyNumber, F_CHARNE,
CharGetDatum(PROKIND_PROCEDURE));
else if (objtype == OBJECT_PROCEDURE)
ScanKeyInit(&key[keycount++],
Anum_pg_proc_prokind,
BTEqualStrategyNumber, F_CHAREQ,
CharGetDatum(PROKIND_PROCEDURE));
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);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid oid = ((Form_pg_proc) GETSTRUCT(tuple))->oid;
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
objects = lappend_oid(objects, oid);
}
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);
table_close(rel, AccessShareLock);
}
break;
default:
/* should not happen */
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
}
}
return objects;
}
/*
* getRelationsInNamespace
*
* Return Oid list of relations in given namespace filtered by relation kind
*/
static List *
getRelationsInNamespace(Oid namespaceId, char relkind)
{
List *relations = NIL;
ScanKeyData key[2];
Relation rel;
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;
HeapTuple tuple;
ScanKeyInit(&key[0],
Anum_pg_class_relnamespace,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(namespaceId));
ScanKeyInit(&key[1],
Anum_pg_class_relkind,
BTEqualStrategyNumber, F_CHAREQ,
CharGetDatum(relkind));
rel = 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);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid oid = ((Form_pg_class) GETSTRUCT(tuple))->oid;
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
relations = lappend_oid(relations, oid);
}
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);
table_close(rel, AccessShareLock);
return relations;
}
/*
* ALTER DEFAULT PRIVILEGES statement
*/
void
ExecAlterDefaultPrivilegesStmt(ParseState *pstate, AlterDefaultPrivilegesStmt *stmt)
{
GrantStmt *action = stmt->action;
InternalDefaultACL iacls;
ListCell *cell;
List *rolespecs = NIL;
List *nspnames = NIL;
DefElem *drolespecs = NULL;
DefElem *dnspnames = NULL;
AclMode all_privileges;
const char *errormsg;
/* Deconstruct the "options" part of the statement */
foreach(cell, stmt->options)
{
DefElem *defel = (DefElem *) lfirst(cell);
if (strcmp(defel->defname, "schemas") == 0)
{
if (dnspnames)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("conflicting or redundant options"),
parser_errposition(pstate, defel->location)));
dnspnames = defel;
}
else if (strcmp(defel->defname, "roles") == 0)
{
if (drolespecs)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("conflicting or redundant options"),
parser_errposition(pstate, defel->location)));
drolespecs = defel;
}
else
elog(ERROR, "option \"%s\" not recognized", defel->defname);
}
if (dnspnames)
nspnames = (List *) dnspnames->arg;
if (drolespecs)
rolespecs = (List *) drolespecs->arg;
/* Prepare the InternalDefaultACL representation of the statement */
/* roleid to be filled below */
/* nspid to be filled in SetDefaultACLsInSchemas */
iacls.is_grant = action->is_grant;
iacls.objtype = action->objtype;
/* all_privs to be filled below */
/* privileges to be filled below */
iacls.grantees = NIL; /* filled below */
iacls.grant_option = action->grant_option;
iacls.behavior = action->behavior;
/*
2015-05-24 03:35:49 +02:00
* Convert the RoleSpec list into an Oid list. Note that at this point we
* insert an ACL_ID_PUBLIC into the list if appropriate, so downstream
* there shouldn't be any additional work needed to support this case.
*/
foreach(cell, action->grantees)
{
2015-05-24 03:35:49 +02:00
RoleSpec *grantee = (RoleSpec *) lfirst(cell);
Oid grantee_uid;
switch (grantee->roletype)
{
case ROLESPEC_PUBLIC:
grantee_uid = ACL_ID_PUBLIC;
break;
default:
grantee_uid = get_rolespec_oid(grantee, false);
break;
}
iacls.grantees = lappend_oid(iacls.grantees, grantee_uid);
}
/*
2010-02-26 03:01:40 +01:00
* Convert action->privileges, a list of privilege strings, into an
* AclMode bitmask.
*/
switch (action->objtype)
{
case OBJECT_TABLE:
all_privileges = ACL_ALL_RIGHTS_RELATION;
errormsg = gettext_noop("invalid privilege type %s for relation");
break;
case OBJECT_SEQUENCE:
all_privileges = ACL_ALL_RIGHTS_SEQUENCE;
errormsg = gettext_noop("invalid privilege type %s for sequence");
break;
case OBJECT_FUNCTION:
all_privileges = ACL_ALL_RIGHTS_FUNCTION;
errormsg = gettext_noop("invalid privilege type %s for function");
break;
case OBJECT_PROCEDURE:
all_privileges = ACL_ALL_RIGHTS_FUNCTION;
errormsg = gettext_noop("invalid privilege type %s for procedure");
break;
case OBJECT_ROUTINE:
all_privileges = ACL_ALL_RIGHTS_FUNCTION;
errormsg = gettext_noop("invalid privilege type %s for routine");
break;
case OBJECT_TYPE:
all_privileges = ACL_ALL_RIGHTS_TYPE;
errormsg = gettext_noop("invalid privilege type %s for type");
break;
case OBJECT_SCHEMA:
all_privileges = ACL_ALL_RIGHTS_SCHEMA;
errormsg = gettext_noop("invalid privilege type %s for schema");
break;
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;
}
}
if (rolespecs == NIL)
{
/* Set permissions for myself */
iacls.roleid = GetUserId();
SetDefaultACLsInSchemas(&iacls, nspnames);
}
else
{
/* Look up the role OIDs and do permissions checks */
ListCell *rolecell;
foreach(rolecell, rolespecs)
{
RoleSpec *rolespec = lfirst(rolecell);
iacls.roleid = get_rolespec_oid(rolespec, false);
/*
2010-02-26 03:01:40 +01:00
* We insist that calling user be a member of each target role. If
* he has that, he could become that role anyway via SET ROLE, so
* FOR ROLE is just a syntactic convenience and doesn't give any
* special privileges.
*/
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
{
/* Look up the schema OIDs and set permissions for each one */
ListCell *nspcell;
foreach(nspcell, nspnames)
{
char *nspname = strVal(lfirst(nspcell));
iacls->nspid = get_namespace_oid(nspname, false);
/*
* 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.
*/
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;
Acl *def_acl;
Acl *old_acl;
Acl *new_acl;
HeapTuple newtuple;
Datum values[Natts_pg_default_acl];
bool nulls[Natts_pg_default_acl];
bool replaces[Natts_pg_default_acl];
int noldmembers;
int nnewmembers;
Oid *oldmembers;
Oid *newmembers;
rel = table_open(DefaultAclRelationId, RowExclusiveLock);
/*
* 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();
/*
2010-02-26 03:01:40 +01:00
* Convert ACL object type to pg_default_acl object type and handle
* all_privs option
*/
switch (iacls->objtype)
{
case OBJECT_TABLE:
objtype = DEFACLOBJ_RELATION;
if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
this_privileges = ACL_ALL_RIGHTS_RELATION;
break;
case OBJECT_SEQUENCE:
objtype = DEFACLOBJ_SEQUENCE;
if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
this_privileges = ACL_ALL_RIGHTS_SEQUENCE;
break;
case OBJECT_FUNCTION:
objtype = DEFACLOBJ_FUNCTION;
if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
this_privileges = ACL_ALL_RIGHTS_FUNCTION;
break;
case OBJECT_TYPE:
objtype = DEFACLOBJ_TYPE;
if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
this_privileges = ACL_ALL_RIGHTS_TYPE;
break;
case OBJECT_SCHEMA:
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)
this_privileges = ACL_ALL_RIGHTS_SCHEMA;
break;
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 */
tuple = SearchSysCache3(DEFACLROLENSPOBJ,
ObjectIdGetDatum(iacls->roleid),
ObjectIdGetDatum(iacls->nspid),
2010-02-26 03:01:40 +01:00
CharGetDatum(objtype));
if (HeapTupleIsValid(tuple))
{
Datum aclDatum;
bool isNull;
aclDatum = SysCacheGetAttr(DEFACLROLENSPOBJ, tuple,
Anum_pg_default_acl_defaclacl,
&isNull);
if (!isNull)
old_acl = DatumGetAclPCopy(aclDatum);
else
old_acl = NULL; /* this case shouldn't happen, probably */
isNew = false;
}
else
{
old_acl = NULL;
isNew = true;
}
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
{
/* If no or null entry, start with the default ACL value */
old_acl = aclcopy(def_acl);
/* There are no old member roles according to the catalogs */
noldmembers = 0;
oldmembers = NULL;
}
/*
2010-02-26 03:01:40 +01: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);
/*
* If the result is the same as the default value, we do not need an
2010-07-06 21:19:02 +02:00
* explicit pg_default_acl entry, and should in fact remove the entry if
* it exists. Must sort both arrays to compare properly.
*/
aclitemsort(new_acl);
aclitemsort(def_acl);
if (aclequal(new_acl, def_acl))
{
/* delete old entry, if indeed there is one */
if (!isNew)
{
ObjectAddress myself;
/*
* 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;
myself.objectSubId = 0;
performDeletion(&myself, DROP_RESTRICT, 0);
}
}
else
{
Oid defAclOid;
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
/* Prepare to insert or update pg_default_acl entry */
MemSet(values, 0, sizeof(values));
MemSet(nulls, false, sizeof(nulls));
MemSet(replaces, false, sizeof(replaces));
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);
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);
CatalogTupleInsert(rel, newtuple);
}
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;
/* update existing entry */
values[Anum_pg_default_acl_defaclacl - 1] = PointerGetDatum(new_acl);
replaces[Anum_pg_default_acl_defaclacl - 1] = true;
newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel),
values, nulls, replaces);
CatalogTupleUpdate(rel, &newtuple->t_self, newtuple);
}
/* these dependencies don't change in an update */
if (isNew)
{
/* 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,
iacls->roleid);
/* dependency on namespace */
if (OidIsValid(iacls->nspid))
{
ObjectAddress myself,
2010-07-06 21:19:02 +02:00
referenced;
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;
myself.objectSubId = 0;
referenced.classId = NamespaceRelationId;
referenced.objectId = iacls->nspid;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
}
}
/*
* Update the shared dependency ACL info
*/
nnewmembers = aclmembers(new_acl, &newmembers);
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,
iacls->roleid,
noldmembers, oldmembers,
nnewmembers, newmembers);
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);
else
InvokeObjectPostAlterHook(DefaultAclRelationId, defAclOid, 0);
}
if (HeapTupleIsValid(tuple))
ReleaseSysCache(tuple);
table_close(rel, RowExclusiveLock);
}
/*
* RemoveRoleFromObjectACL
*
* Used by shdepDropOwned to remove mentions of a role in ACLs
*/
void
RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
{
if (classid == DefaultAclRelationId)
{
InternalDefaultACL iacls;
Form_pg_default_acl pg_default_acl_tuple;
Relation rel;
ScanKeyData skey[1];
SysScanDesc scan;
HeapTuple tuple;
/* first fetch info needed by SetDefaultACL */
rel = table_open(DefaultAclRelationId, AccessShareLock);
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,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(objid));
scan = systable_beginscan(rel, DefaultAclOidIndexId, true,
NULL, 1, skey);
tuple = systable_getnext(scan);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "could not find tuple for default ACL %u", objid);
pg_default_acl_tuple = (Form_pg_default_acl) GETSTRUCT(tuple);
iacls.roleid = pg_default_acl_tuple->defaclrole;
iacls.nspid = pg_default_acl_tuple->defaclnamespace;
switch (pg_default_acl_tuple->defaclobjtype)
{
case DEFACLOBJ_RELATION:
iacls.objtype = OBJECT_TABLE;
break;
case DEFACLOBJ_SEQUENCE:
iacls.objtype = OBJECT_SEQUENCE;
break;
case DEFACLOBJ_FUNCTION:
iacls.objtype = OBJECT_FUNCTION;
break;
case DEFACLOBJ_TYPE:
iacls.objtype = OBJECT_TYPE;
break;
case DEFACLOBJ_NAMESPACE:
iacls.objtype = OBJECT_SCHEMA;
break;
default:
/* Shouldn't get here */
elog(ERROR, "unexpected default ACL type: %d",
(int) pg_default_acl_tuple->defaclobjtype);
break;
}
systable_endscan(scan);
table_close(rel, AccessShareLock);
iacls.is_grant = false;
iacls.all_privs = true;
iacls.privileges = ACL_NO_RIGHTS;
iacls.grantees = list_make1_oid(roleid);
iacls.grant_option = false;
iacls.behavior = DROP_CASCADE;
/* Do it */
SetDefaultACL(&iacls);
}
else
{
InternalGrant istmt;
switch (classid)
{
case RelationRelationId:
/* it's OK to use TABLE for a sequence */
istmt.objtype = OBJECT_TABLE;
break;
case DatabaseRelationId:
istmt.objtype = OBJECT_DATABASE;
break;
case TypeRelationId:
istmt.objtype = OBJECT_TYPE;
break;
case ProcedureRelationId:
istmt.objtype = OBJECT_ROUTINE;
break;
case LanguageRelationId:
istmt.objtype = OBJECT_LANGUAGE;
break;
case LargeObjectRelationId:
istmt.objtype = OBJECT_LARGEOBJECT;
break;
case NamespaceRelationId:
istmt.objtype = OBJECT_SCHEMA;
break;
case TableSpaceRelationId:
istmt.objtype = OBJECT_TABLESPACE;
break;
case ForeignServerRelationId:
istmt.objtype = OBJECT_FOREIGN_SERVER;
break;
case ForeignDataWrapperRelationId:
istmt.objtype = OBJECT_FDW;
break;
default:
elog(ERROR, "unexpected object class %u", classid);
break;
}
istmt.is_grant = false;
istmt.objects = list_make1_oid(objid);
istmt.all_privs = true;
istmt.privileges = ACL_NO_RIGHTS;
istmt.col_privs = NIL;
istmt.grantees = list_make1_oid(roleid);
istmt.grant_option = false;
istmt.behavior = DROP_CASCADE;
ExecGrantStmt_oids(&istmt);
}
}
/*
* Remove a pg_default_acl entry
*/
void
RemoveDefaultACLById(Oid defaclOid)
{
Relation rel;
ScanKeyData skey[1];
SysScanDesc scan;
HeapTuple tuple;
rel = table_open(DefaultAclRelationId, RowExclusiveLock);
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,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(defaclOid));
scan = systable_beginscan(rel, DefaultAclOidIndexId, true,
NULL, 1, skey);
tuple = systable_getnext(scan);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "could not find tuple for default ACL %u", defaclOid);
CatalogTupleDelete(rel, &tuple->t_self);
systable_endscan(scan);
table_close(rel, RowExclusiveLock);
}
/*
* 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;
attTuple = SearchSysCache2(ATTNUM,
ObjectIdGetDatum(table_oid),
Int16GetDatum(curr_att));
if (!HeapTupleIsValid(attTuple))
elog(ERROR, "cache lookup failed for attribute %d of relation %u",
curr_att, table_oid);
isdropped = ((Form_pg_attribute) GETSTRUCT(attTuple))->attisdropped;
ReleaseSysCache(attTuple);
/* ignore dropped columns */
if (isdropped)
continue;
col_privileges[curr_att - FirstLowInvalidHeapAttributeNumber] |= this_privileges;
}
}
/*
* This processes attributes, but expects to be called from
* ExecGrant_Relation, not directly from ExecuteGrantStmt.
*/
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;
attr_tuple = SearchSysCache2(ATTNUM,
ObjectIdGetDatum(relOid),
Int16GetDatum(attnum));
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)
{
old_acl = acldefault(OBJECT_COLUMN, 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);
}
/*
* 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,
relOid, grantorId, OBJECT_COLUMN,
relname, attnum,
NameStr(pg_attribute_tuple->attname));
/*
* Generate new ACL.
*/
new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
istmt->grant_option,
istmt->behavior, istmt->grantees,
col_privileges, grantorId,
ownerId);
/*
* 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));
/*
* 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);
CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
/* Update initial privileges for extensions */
recordExtensionInitPriv(relOid, RelationRelationId, attnum,
ACL_NUM(new_acl) > 0 ? new_acl : NULL);
/* Update the shared dependency ACL info */
updateAclDependencies(RelationRelationId, relOid, attnum,
ownerId,
noldmembers, oldmembers,
nnewmembers, newmembers);
}
pfree(new_acl);
ReleaseSysCache(attr_tuple);
}
/*
* This processes both sequences and non-sequences.
*/
static void
ExecGrant_Relation(InternalGrant *istmt)
{
Relation relation;
Relation attRelation;
ListCell *cell;
relation = table_open(RelationRelationId, RowExclusiveLock);
attRelation = table_open(AttributeRelationId, RowExclusiveLock);
foreach(cell, istmt->objects)
{
Oid relOid = lfirst_oid(cell);
Datum aclDatum;
Form_pg_class pg_class_tuple;
bool isNull;
AclMode this_privileges;
AclMode *col_privileges;
int num_col_privileges;
bool have_col_privileges;
Acl *old_acl;
Acl *old_rel_acl;
int noldmembers;
Oid *oldmembers;
2005-10-15 04:49:52 +02:00
Oid ownerId;
HeapTuple tuple;
ListCell *cell_colprivs;
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for relation %u", relOid);
pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
/* Not sensible to grant on an index */
if (pg_class_tuple->relkind == RELKIND_INDEX ||
pg_class_tuple->relkind == RELKIND_PARTITIONED_INDEX)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is an index",
NameStr(pg_class_tuple->relname))));
/* 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",
NameStr(pg_class_tuple->relname))));
/* Used GRANT SEQUENCE on a non-sequence? */
if (istmt->objtype == OBJECT_SEQUENCE &&
pg_class_tuple->relkind != RELKIND_SEQUENCE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a sequence",
NameStr(pg_class_tuple->relname))));
/* Adjust the default permissions based on object type */
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-10-04 02:30:14 +02:00
* The GRANT TABLE syntax can be used for sequences and non-sequences,
* so we have to look at the relkind to determine the supported
* permissions. The OR of table and sequence permissions were already
* checked.
*/
if (istmt->objtype == OBJECT_TABLE)
{
if (pg_class_tuple->relkind == RELKIND_SEQUENCE)
{
/*
* For backward compatibility, just throw a warning for
2006-10-04 02:30:14 +02:00
* invalid sequence permissions when using the non-sequence
* GRANT syntax.
*/
if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_SEQUENCE))
{
/*
2006-10-04 02:30:14 +02:00
* Mention the object name because the user needs to know
* which operations succeeded. This is required because
2006-10-04 02:30:14 +02:00
* WARNING allows the command to continue.
*/
ereport(WARNING,
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
errmsg("sequence \"%s\" only supports USAGE, SELECT, and UPDATE privileges",
NameStr(pg_class_tuple->relname))));
this_privileges &= (AclMode) ACL_ALL_RIGHTS_SEQUENCE;
}
}
else
{
if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_RELATION))
{
/*
2006-10-04 02:30:14 +02:00
* USAGE is the only permission supported by sequences but
* not by non-sequences. Don't mention the object name
* because we didn't in the combined TABLE | SEQUENCE
* check.
*/
ereport(ERROR,
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
errmsg("invalid privilege type %s for table",
"USAGE")));
}
}
}
/*
* 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-15 04:49:52 +02:00
* Get owner ID and working copy of existing ACL. If there's no ACL,
* substitute the proper default.
*/
ownerId = pg_class_tuple->relowner;
aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl,
&isNull);
if (isNull)
{
switch (pg_class_tuple->relkind)
{
case RELKIND_SEQUENCE:
old_acl = acldefault(OBJECT_SEQUENCE, ownerId);
break;
default:
old_acl = acldefault(OBJECT_TABLE, ownerId);
break;
}
/* 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);
}
/* Need an extra copy of original rel ACL for column handling */
old_rel_acl = aclcopy(old_acl);
/*
* Handle relation-level privileges, if any were specified
*/
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;
ObjectType objtype;
/* Determine ID to do the grant as, and available grant options */
select_best_grantor(GetUserId(), this_privileges,
old_acl, ownerId,
&grantorId, &avail_goptions);
switch (pg_class_tuple->relkind)
{
case RELKIND_SEQUENCE:
objtype = OBJECT_SEQUENCE;
break;
default:
objtype = OBJECT_TABLE;
break;
}
/*
* 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,
relOid, grantorId, objtype,
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);
/*
* 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_class_relacl - 1] = true;
values[Anum_pg_class_relacl - 1] = PointerGetDatum(new_acl);
newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation),
values, nulls, replaces);
CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
/* Update initial privileges for extensions */
recordExtensionInitPriv(relOid, RelationRelationId, 0, new_acl);
/* Update the shared dependency ACL info */
updateAclDependencies(RelationRelationId, relOid, 0,
ownerId,
noldmembers, oldmembers,
nnewmembers, newmembers);
pfree(new_acl);
}
/*
* 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.
*/
foreach(cell_colprivs, istmt->col_privs)
{
AccessPriv *col_privs = (AccessPriv *) lfirst(cell_colprivs);
if (col_privs->priv_name == NULL)
this_privileges = ACL_ALL_RIGHTS_COLUMN;
else
this_privileges = string_to_privilege(col_privs->priv_name);
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))));
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))));
this_privileges &= (AclMode) ACL_SELECT;
}
expand_col_privileges(col_privs->cols, relOid,
this_privileges,
col_privileges,
num_col_privileges);
have_col_privileges = true;
}
if (have_col_privileges)
{
AttrNumber i;
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);
}
}
pfree(old_rel_acl);
pfree(col_privileges);
ReleaseSysCache(tuple);
/* prevent error when processing duplicate objects */
CommandCounterIncrement();
}
table_close(attRelation, RowExclusiveLock);
table_close(relation, RowExclusiveLock);
}
static void
ExecGrant_Database(InternalGrant *istmt)
{
Relation relation;
ListCell *cell;
if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
istmt->privileges = ACL_ALL_RIGHTS_DATABASE;
relation = table_open(DatabaseRelationId, RowExclusiveLock);
foreach(cell, istmt->objects)
{
Oid datId = lfirst_oid(cell);
Form_pg_database pg_database_tuple;
Datum aclDatum;
bool isNull;
AclMode avail_goptions;
AclMode this_privileges;
Acl *old_acl;
Acl *new_acl;
2005-10-15 04:49:52 +02:00
Oid grantorId;
Oid ownerId;
HeapTuple newtuple;
Datum values[Natts_pg_database];
bool nulls[Natts_pg_database];
bool replaces[Natts_pg_database];
int noldmembers;
int nnewmembers;
Oid *oldmembers;
Oid *newmembers;
HeapTuple tuple;
tuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(datId));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for database %u", datId);
pg_database_tuple = (Form_pg_database) GETSTRUCT(tuple);
/*
2005-10-15 04:49:52 +02:00
* Get owner ID and working copy of existing ACL. If there's no ACL,
* substitute the proper default.
*/
ownerId = pg_database_tuple->datdba;
aclDatum = heap_getattr(tuple, Anum_pg_database_datacl,
RelationGetDescr(relation), &isNull);
if (isNull)
{
old_acl = acldefault(OBJECT_DATABASE, 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);
/*
2006-10-04 02:30:14 +02:00
* Restrict the privileges to what we can actually grant, and emit the
* standards-mandated warning and error messages.
*/
this_privileges =
restrict_and_check_grant(istmt->is_grant, avail_goptions,
istmt->all_privs, istmt->privileges,
datId, grantorId, OBJECT_DATABASE,
NameStr(pg_database_tuple->datname),
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_database_datacl - 1] = true;
values[Anum_pg_database_datacl - 1] = PointerGetDatum(new_acl);
newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
nulls, replaces);
CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
/* 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,
ownerId,
noldmembers, oldmembers,
nnewmembers, newmembers);
ReleaseSysCache(tuple);
pfree(new_acl);
/* prevent error when processing duplicate objects */
CommandCounterIncrement();
}
table_close(relation, RowExclusiveLock);
}
static void
ExecGrant_Fdw(InternalGrant *istmt)
{
Relation relation;
ListCell *cell;
if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
istmt->privileges = ACL_ALL_RIGHTS_FDW;
relation = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
foreach(cell, istmt->objects)
{
Oid fdwid = lfirst_oid(cell);
Form_pg_foreign_data_wrapper pg_fdw_tuple;
Datum aclDatum;
bool isNull;
AclMode avail_goptions;
AclMode this_privileges;
Acl *old_acl;
Acl *new_acl;
Oid grantorId;
Oid ownerId;
HeapTuple tuple;
HeapTuple newtuple;
Datum values[Natts_pg_foreign_data_wrapper];
bool nulls[Natts_pg_foreign_data_wrapper];
bool replaces[Natts_pg_foreign_data_wrapper];
int noldmembers;
int nnewmembers;
Oid *oldmembers;
Oid *newmembers;
tuple = SearchSysCache1(FOREIGNDATAWRAPPEROID,
2010-02-26 03:01:40 +01:00
ObjectIdGetDatum(fdwid));
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)
{
old_acl = acldefault(OBJECT_FDW, 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,
fdwid, grantorId, OBJECT_FDW,
NameStr(pg_fdw_tuple->fdwname),
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_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);
CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
/* Update initial privileges for extensions */
recordExtensionInitPriv(fdwid, ForeignDataWrapperRelationId, 0,
new_acl);
/* Update the shared dependency ACL info */
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,
ownerId,
noldmembers, oldmembers,
nnewmembers, newmembers);
ReleaseSysCache(tuple);
pfree(new_acl);
/* prevent error when processing duplicate objects */
CommandCounterIncrement();
}
table_close(relation, RowExclusiveLock);
}
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;
relation = table_open(ForeignServerRelationId, RowExclusiveLock);
foreach(cell, istmt->objects)
{
Oid srvid = lfirst_oid(cell);
Form_pg_foreign_server pg_server_tuple;
Datum aclDatum;
bool isNull;
AclMode avail_goptions;
AclMode this_privileges;
Acl *old_acl;
Acl *new_acl;
Oid grantorId;
Oid ownerId;
HeapTuple tuple;
HeapTuple newtuple;
Datum values[Natts_pg_foreign_server];
bool nulls[Natts_pg_foreign_server];
bool replaces[Natts_pg_foreign_server];
int noldmembers;
int nnewmembers;
Oid *oldmembers;
Oid *newmembers;
tuple = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(srvid));
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)
{
old_acl = acldefault(OBJECT_FOREIGN_SERVER, 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,
srvid, grantorId, OBJECT_FOREIGN_SERVER,
NameStr(pg_server_tuple->srvname),
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_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);
CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
/* Update initial privileges for extensions */
recordExtensionInitPriv(srvid, ForeignServerRelationId, 0, new_acl);
/* Update the shared dependency ACL info */
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,
ownerId,
noldmembers, oldmembers,
nnewmembers, newmembers);
ReleaseSysCache(tuple);
pfree(new_acl);
/* prevent error when processing duplicate objects */
CommandCounterIncrement();
}
table_close(relation, RowExclusiveLock);
}
static void
ExecGrant_Function(InternalGrant *istmt)
{
Relation relation;
ListCell *cell;
if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
istmt->privileges = ACL_ALL_RIGHTS_FUNCTION;
relation = table_open(ProcedureRelationId, RowExclusiveLock);
foreach(cell, istmt->objects)
{
Oid funcId = lfirst_oid(cell);
Form_pg_proc pg_proc_tuple;
Datum aclDatum;
bool isNull;
AclMode avail_goptions;
AclMode this_privileges;
Acl *old_acl;
Acl *new_acl;
2005-10-15 04:49:52 +02:00
Oid grantorId;
Oid ownerId;
HeapTuple tuple;
HeapTuple newtuple;
Datum values[Natts_pg_proc];
bool nulls[Natts_pg_proc];
bool replaces[Natts_pg_proc];
int noldmembers;
int nnewmembers;
Oid *oldmembers;
Oid *newmembers;
tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcId));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for function %u", funcId);
pg_proc_tuple = (Form_pg_proc) GETSTRUCT(tuple);
/*
2005-10-15 04:49:52 +02:00
* Get owner ID and working copy of existing ACL. If there's no ACL,
* substitute the proper default.
*/
ownerId = pg_proc_tuple->proowner;
aclDatum = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_proacl,
&isNull);
if (isNull)
{
old_acl = acldefault(OBJECT_FUNCTION, 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);
/*
2006-10-04 02:30:14 +02:00
* Restrict the privileges to what we can actually grant, and emit the
* standards-mandated warning and error messages.
*/
this_privileges =
restrict_and_check_grant(istmt->is_grant, avail_goptions,
istmt->all_privs, istmt->privileges,
funcId, grantorId, OBJECT_FUNCTION,
NameStr(pg_proc_tuple->proname),
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_proc_proacl - 1] = true;
values[Anum_pg_proc_proacl - 1] = PointerGetDatum(new_acl);
newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
nulls, replaces);
CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
/* Update initial privileges for extensions */
recordExtensionInitPriv(funcId, ProcedureRelationId, 0, new_acl);
/* Update the shared dependency ACL info */
updateAclDependencies(ProcedureRelationId, funcId, 0,
ownerId,
noldmembers, oldmembers,
nnewmembers, newmembers);
ReleaseSysCache(tuple);
pfree(new_acl);
/* prevent error when processing duplicate objects */
CommandCounterIncrement();
}
table_close(relation, RowExclusiveLock);
}
static void
ExecGrant_Language(InternalGrant *istmt)
{
Relation relation;
ListCell *cell;
if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
istmt->privileges = ACL_ALL_RIGHTS_LANGUAGE;
relation = table_open(LanguageRelationId, RowExclusiveLock);
foreach(cell, istmt->objects)
{
Oid langId = lfirst_oid(cell);
Form_pg_language pg_language_tuple;
Datum aclDatum;
bool isNull;
AclMode avail_goptions;
AclMode this_privileges;
Acl *old_acl;
Acl *new_acl;
2005-10-15 04:49:52 +02:00
Oid grantorId;
Oid ownerId;
HeapTuple tuple;
HeapTuple newtuple;
Datum values[Natts_pg_language];
bool nulls[Natts_pg_language];
bool replaces[Natts_pg_language];
int noldmembers;
int nnewmembers;
Oid *oldmembers;
Oid *newmembers;
tuple = SearchSysCache1(LANGOID, ObjectIdGetDatum(langId));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for language %u", langId);
pg_language_tuple = (Form_pg_language) GETSTRUCT(tuple);
if (!pg_language_tuple->lanpltrusted)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("language \"%s\" is not trusted",
NameStr(pg_language_tuple->lanname)),
errdetail("GRANT and REVOKE are not allowed on untrusted languages, "
"because only superusers can use untrusted languages.")));
/*
2005-10-15 04:49:52 +02:00
* Get owner ID and working copy of existing ACL. If there's no ACL,
* substitute the proper default.
*/
ownerId = pg_language_tuple->lanowner;
aclDatum = SysCacheGetAttr(LANGNAME, tuple, Anum_pg_language_lanacl,
&isNull);
if (isNull)
{
old_acl = acldefault(OBJECT_LANGUAGE, 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);
/*
2006-10-04 02:30:14 +02:00
* Restrict the privileges to what we can actually grant, and emit the
* standards-mandated warning and error messages.
*/
this_privileges =
restrict_and_check_grant(istmt->is_grant, avail_goptions,
istmt->all_privs, istmt->privileges,
langId, grantorId, OBJECT_LANGUAGE,
NameStr(pg_language_tuple->lanname),
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_language_lanacl - 1] = true;
values[Anum_pg_language_lanacl - 1] = PointerGetDatum(new_acl);
newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
nulls, replaces);
CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
/* Update initial privileges for extensions */
recordExtensionInitPriv(langId, LanguageRelationId, 0, new_acl);
/* 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,
ownerId,
noldmembers, oldmembers,
nnewmembers, newmembers);
ReleaseSysCache(tuple);
pfree(new_acl);
/* prevent error when processing duplicate objects */
CommandCounterIncrement();
}
table_close(relation, RowExclusiveLock);
}
static void
ExecGrant_Largeobject(InternalGrant *istmt)
{
Relation relation;
ListCell *cell;
if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
istmt->privileges = ACL_ALL_RIGHTS_LARGEOBJECT;
relation = table_open(LargeObjectMetadataRelationId,
RowExclusiveLock);
foreach(cell, istmt->objects)
{
Oid loid = lfirst_oid(cell);
2010-02-26 03:01:40 +01:00
Form_pg_largeobject_metadata form_lo_meta;
char loname[NAMEDATALEN];
Datum aclDatum;
bool isNull;
AclMode avail_goptions;
AclMode this_privileges;
Acl *old_acl;
Acl *new_acl;
Oid grantorId;
Oid ownerId;
HeapTuple newtuple;
Datum values[Natts_pg_largeobject_metadata];
bool nulls[Natts_pg_largeobject_metadata];
bool replaces[Natts_pg_largeobject_metadata];
int noldmembers;
int nnewmembers;
Oid *oldmembers;
Oid *newmembers;
2010-02-26 03:01:40 +01:00
ScanKeyData entry[1];
SysScanDesc scan;
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,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(loid));
scan = systable_beginscan(relation,
LargeObjectMetadataOidIndexId, true,
NULL, 1, entry);
tuple = systable_getnext(scan);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "could not find tuple for large object %u", loid);
form_lo_meta = (Form_pg_largeobject_metadata) GETSTRUCT(tuple);
/*
* Get owner ID and working copy of existing ACL. If there's no ACL,
* substitute the proper default.
*/
ownerId = form_lo_meta->lomowner;
aclDatum = heap_getattr(tuple,
Anum_pg_largeobject_metadata_lomacl,
RelationGetDescr(relation), &isNull);
if (isNull)
{
old_acl = acldefault(OBJECT_LARGEOBJECT, 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.
*/
snprintf(loname, sizeof(loname), "large object %u", loid);
this_privileges =
restrict_and_check_grant(istmt->is_grant, avail_goptions,
istmt->all_privs, istmt->privileges,
loid, grantorId, OBJECT_LARGEOBJECT,
loname, 0, NULL);
/*
* Generate new ACL.
*/
new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
istmt->grant_option, istmt->behavior,
istmt->grantees, this_privileges,
grantorId, ownerId);
/*
* 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_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);
CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
/* Update initial privileges for extensions */
recordExtensionInitPriv(loid, LargeObjectRelationId, 0, new_acl);
/* 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,
ownerId,
noldmembers, oldmembers,
nnewmembers, newmembers);
systable_endscan(scan);
pfree(new_acl);
/* prevent error when processing duplicate objects */
CommandCounterIncrement();
}
table_close(relation, RowExclusiveLock);
}
static void
ExecGrant_Namespace(InternalGrant *istmt)
{
Relation relation;
ListCell *cell;
if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
istmt->privileges = ACL_ALL_RIGHTS_SCHEMA;
relation = table_open(NamespaceRelationId, RowExclusiveLock);
foreach(cell, istmt->objects)
{
Oid nspid = lfirst_oid(cell);
Form_pg_namespace pg_namespace_tuple;
Datum aclDatum;
bool isNull;
AclMode avail_goptions;
AclMode this_privileges;
Acl *old_acl;
Acl *new_acl;
2005-10-15 04:49:52 +02:00
Oid grantorId;
Oid ownerId;
HeapTuple tuple;
HeapTuple newtuple;
Datum values[Natts_pg_namespace];
bool nulls[Natts_pg_namespace];
bool replaces[Natts_pg_namespace];
int noldmembers;
int nnewmembers;
Oid *oldmembers;
Oid *newmembers;
tuple = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(nspid));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for namespace %u", nspid);
pg_namespace_tuple = (Form_pg_namespace) GETSTRUCT(tuple);
/*
2005-10-15 04:49:52 +02:00
* Get owner ID and working copy of existing ACL. If there's no ACL,
* substitute the proper default.
*/
ownerId = pg_namespace_tuple->nspowner;
aclDatum = SysCacheGetAttr(NAMESPACENAME, tuple,
Anum_pg_namespace_nspacl,
&isNull);
if (isNull)
{
old_acl = acldefault(OBJECT_SCHEMA, 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);
/*
2006-10-04 02:30:14 +02:00
* Restrict the privileges to what we can actually grant, and emit the
* standards-mandated warning and error messages.
*/
this_privileges =
restrict_and_check_grant(istmt->is_grant, avail_goptions,
istmt->all_privs, istmt->privileges,
nspid, grantorId, OBJECT_SCHEMA,
NameStr(pg_namespace_tuple->nspname),
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_namespace_nspacl - 1] = true;
values[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(new_acl);
newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
nulls, replaces);
CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
/* Update initial privileges for extensions */
recordExtensionInitPriv(nspid, NamespaceRelationId, 0, new_acl);
/* 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,
ownerId,
noldmembers, oldmembers,
nnewmembers, newmembers);
ReleaseSysCache(tuple);
pfree(new_acl);
/* prevent error when processing duplicate objects */
CommandCounterIncrement();
}
table_close(relation, RowExclusiveLock);
}
static void
ExecGrant_Tablespace(InternalGrant *istmt)
{
Relation relation;
ListCell *cell;
if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
istmt->privileges = ACL_ALL_RIGHTS_TABLESPACE;
relation = table_open(TableSpaceRelationId, RowExclusiveLock);
foreach(cell, istmt->objects)
{
Oid tblId = lfirst_oid(cell);
Form_pg_tablespace pg_tablespace_tuple;
Datum aclDatum;
bool isNull;
AclMode avail_goptions;
AclMode this_privileges;
Acl *old_acl;
Acl *new_acl;
2005-10-15 04:49:52 +02:00
Oid grantorId;
Oid ownerId;
HeapTuple newtuple;
Datum values[Natts_pg_tablespace];
bool nulls[Natts_pg_tablespace];
bool replaces[Natts_pg_tablespace];
int noldmembers;
int nnewmembers;
Oid *oldmembers;
Oid *newmembers;
HeapTuple tuple;
/* Search syscache for pg_tablespace */
tuple = SearchSysCache1(TABLESPACEOID, ObjectIdGetDatum(tblId));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for tablespace %u", tblId);
pg_tablespace_tuple = (Form_pg_tablespace) GETSTRUCT(tuple);
/*
2005-10-15 04:49:52 +02:00
* Get owner ID and working copy of existing ACL. If there's no ACL,
* substitute the proper default.
*/
ownerId = pg_tablespace_tuple->spcowner;
aclDatum = heap_getattr(tuple, Anum_pg_tablespace_spcacl,
RelationGetDescr(relation), &isNull);
if (isNull)
{
old_acl = acldefault(OBJECT_TABLESPACE, 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);
/*
2006-10-04 02:30:14 +02:00
* Restrict the privileges to what we can actually grant, and emit the
* standards-mandated warning and error messages.
*/
this_privileges =
restrict_and_check_grant(istmt->is_grant, avail_goptions,
istmt->all_privs, istmt->privileges,
tblId, grantorId, OBJECT_TABLESPACE,
NameStr(pg_tablespace_tuple->spcname),
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_tablespace_spcacl - 1] = true;
values[Anum_pg_tablespace_spcacl - 1] = PointerGetDatum(new_acl);
newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
nulls, replaces);
CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
/* Update the shared dependency ACL info */
updateAclDependencies(TableSpaceRelationId, tblId, 0,
ownerId,
noldmembers, oldmembers,
nnewmembers, newmembers);
ReleaseSysCache(tuple);
pfree(new_acl);
/* prevent error when processing duplicate objects */
CommandCounterIncrement();
}
table_close(relation, RowExclusiveLock);
}
static void
ExecGrant_Type(InternalGrant *istmt)
{
Relation relation;
ListCell *cell;
if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
istmt->privileges = ACL_ALL_RIGHTS_TYPE;
relation = table_open(TypeRelationId, RowExclusiveLock);
foreach(cell, istmt->objects)
{
Oid typId = lfirst_oid(cell);
Form_pg_type pg_type_tuple;
Datum aclDatum;
bool isNull;
AclMode avail_goptions;
AclMode this_privileges;
Acl *old_acl;
Acl *new_acl;
Oid grantorId;
Oid ownerId;
HeapTuple newtuple;
Datum values[Natts_pg_type];
bool nulls[Natts_pg_type];
bool replaces[Natts_pg_type];
int noldmembers;
int nnewmembers;
Oid *oldmembers;
Oid *newmembers;
HeapTuple tuple;
/* Search syscache for pg_type */
tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typId));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for type %u", typId);
pg_type_tuple = (Form_pg_type) GETSTRUCT(tuple);
if (pg_type_tuple->typelem != 0 && pg_type_tuple->typlen == -1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
errmsg("cannot set privileges of array types"),
errhint("Set the privileges of the element type instead.")));
/* Used GRANT DOMAIN on a non-domain? */
if (istmt->objtype == OBJECT_DOMAIN &&
pg_type_tuple->typtype != TYPTYPE_DOMAIN)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a domain",
NameStr(pg_type_tuple->typname))));
/*
* Get owner ID and working copy of existing ACL. If there's no ACL,
* substitute the proper default.
*/
ownerId = pg_type_tuple->typowner;
aclDatum = heap_getattr(tuple, Anum_pg_type_typacl,
RelationGetDescr(relation), &isNull);
if (isNull)
{
old_acl = acldefault(istmt->objtype, ownerId);
/* There are no old member roles according to the catalogs */
noldmembers = 0;
oldmembers = NULL;
}
else
{
old_acl = DatumGetAclPCopy(aclDatum);
/* Get the roles mentioned in the existing ACL */
noldmembers = aclmembers(old_acl, &oldmembers);
}
/* Determine ID to do the grant as, and available grant options */
select_best_grantor(GetUserId(), istmt->privileges,
old_acl, ownerId,
&grantorId, &avail_goptions);
/*
* Restrict the privileges to what we can actually grant, and emit the
* standards-mandated warning and error messages.
*/
this_privileges =
restrict_and_check_grant(istmt->is_grant, avail_goptions,
istmt->all_privs, istmt->privileges,
typId, grantorId, OBJECT_TYPE,
NameStr(pg_type_tuple->typname),
0, NULL);
/*
* Generate new ACL.
*/
new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
istmt->grant_option, istmt->behavior,
istmt->grantees, this_privileges,
grantorId, ownerId);
/*
* We need the members of both old and new ACLs so we can correct the
* shared dependency information.
*/
nnewmembers = aclmembers(new_acl, &newmembers);
/* finished building new ACL value, now insert it */
MemSet(values, 0, sizeof(values));
MemSet(nulls, false, sizeof(nulls));
MemSet(replaces, false, sizeof(replaces));
replaces[Anum_pg_type_typacl - 1] = true;
values[Anum_pg_type_typacl - 1] = PointerGetDatum(new_acl);
newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
nulls, replaces);
CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
/* Update initial privileges for extensions */
recordExtensionInitPriv(typId, TypeRelationId, 0, new_acl);
/* 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();
}
table_close(relation, RowExclusiveLock);
}
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;
if (strcmp(privname, "truncate") == 0)
return ACL_TRUNCATE;
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;
if (strcmp(privname, "connect") == 0)
return ACL_CONNECT;
if (strcmp(privname, "rule") == 0)
return 0; /* ignore old RULE privileges */
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("unrecognized privilege type \"%s\"", privname)));
return 0; /* appease compiler */
}
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";
case ACL_TRUNCATE:
return "TRUNCATE";
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";
case ACL_CONNECT:
return "CONNECT";
default:
elog(ERROR, "unrecognized privilege: %d", (int) privilege);
}
return NULL; /* appease compiler */
}
/*
* Standardized reporting of aclcheck permissions failures.
*
* Note: we do not double-quote the %s's below, because many callers
* supply strings that might be already quoted.
*/
void
aclcheck_error(AclResult aclerr, ObjectType objtype,
const char *objectname)
{
switch (aclerr)
{
case ACLCHECK_OK:
/* no error, so return to caller */
break;
case ACLCHECK_NO_PRIV:
{
const char *msg = "???";
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;
}
case ACLCHECK_NOT_OWNER:
{
const char *msg = "???";
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;
/*
* 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;
}
default:
elog(ERROR, "unrecognized AclResult: %d", (int) aclerr);
break;
}
}
void
aclcheck_error_col(AclResult aclerr, ObjectType objtype,
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),
errmsg("permission denied for column \"%s\" of relation \"%s\"",
colname, objectname)));
break;
case ACLCHECK_NOT_OWNER:
/* relation msg is OK since columns don't have separate owners */
aclcheck_error(aclerr, objtype, objectname);
break;
default:
elog(ERROR, "unrecognized AclResult: %d", (int) aclerr);
break;
}
}
/*
* 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);
aclcheck_error(aclerr, OBJECT_TYPE, format_type_be(element_type ? element_type : typeOid));
}
/*
* Relay for the various pg_*_mask routines depending on object kind
*/
static AclMode
pg_aclmask(ObjectType objtype, Oid table_oid, AttrNumber attnum, Oid roleid,
AclMode mask, AclMaskHow how)
{
switch (objtype)
{
case OBJECT_COLUMN:
return
pg_class_aclmask(table_oid, roleid, mask, how) |
pg_attribute_aclmask(table_oid, attnum, roleid, mask, how);
case OBJECT_TABLE:
case OBJECT_SEQUENCE:
return pg_class_aclmask(table_oid, roleid, mask, how);
case OBJECT_DATABASE:
return pg_database_aclmask(table_oid, roleid, mask, how);
case OBJECT_FUNCTION:
return pg_proc_aclmask(table_oid, roleid, mask, how);
case OBJECT_LANGUAGE:
return pg_language_aclmask(table_oid, roleid, mask, how);
case OBJECT_LARGEOBJECT:
return pg_largeobject_aclmask_snapshot(table_oid, roleid,
mask, how, NULL);
case OBJECT_SCHEMA:
return pg_namespace_aclmask(table_oid, roleid, mask, how);
case OBJECT_STATISTIC_EXT:
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;
case OBJECT_TABLESPACE:
return pg_tablespace_aclmask(table_oid, roleid, mask, how);
case OBJECT_FDW:
return pg_foreign_data_wrapper_aclmask(table_oid, roleid, mask, how);
case OBJECT_FOREIGN_SERVER:
return pg_foreign_server_aclmask(table_oid, roleid, mask, how);
case OBJECT_EVENT_TRIGGER:
elog(ERROR, "grantable rights not supported for event triggers");
/* not reached, but keep compiler quiet */
return ACL_NO_RIGHTS;
case OBJECT_TYPE:
return pg_type_aclmask(table_oid, roleid, mask, how);
default:
elog(ERROR, "unrecognized objtype: %d",
(int) objtype);
/* not reached, but keep compiler quiet */
return ACL_NO_RIGHTS;
}
}
/* ****************************************************************
* Exported routines for examining a user's privileges for various objects
*
* See aclmask() for a description of the common API for these functions.
*
* Note: we give lookup failure the full ereport treatment because the
* 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)
{
AclMode result;
HeapTuple classTuple;
HeapTuple attTuple;
Form_pg_class classForm;
Form_pg_attribute attributeForm;
Datum aclDatum;
bool isNull;
Acl *acl;
Oid ownerId;
/*
* First, get the column's ACL from its pg_attribute entry
*/
attTuple = SearchSysCache2(ATTNUM,
ObjectIdGetDatum(table_oid),
Int16GetDatum(attnum));
if (!HeapTupleIsValid(attTuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("attribute %d of relation with OID %u does not exist",
attnum, table_oid)));
attributeForm = (Form_pg_attribute) GETSTRUCT(attTuple);
/* Throw error on dropped columns, too */
if (attributeForm->attisdropped)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("attribute %d of relation with OID %u does not exist",
attnum, table_oid)));
aclDatum = SysCacheGetAttr(ATTNUM, attTuple, Anum_pg_attribute_attacl,
&isNull);
/*
* 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.
*/
if (isNull)
{
ReleaseSysCache(attTuple);
return 0;
}
/*
* 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.
*/
classTuple = SearchSysCache1(RELOID, ObjectIdGetDatum(table_oid));
if (!HeapTupleIsValid(classTuple))
{
ReleaseSysCache(attTuple);
return 0;
}
classForm = (Form_pg_class) GETSTRUCT(classTuple);
ownerId = classForm->relowner;
ReleaseSysCache(classTuple);
/* detoast column'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(attTuple);
return result;
}
/*
* Exported routine for examining a user's privileges for a table
*/
AclMode
pg_class_aclmask(Oid table_oid, Oid roleid,
AclMode mask, AclMaskHow how)
{
AclMode result;
HeapTuple tuple;
Form_pg_class classForm;
Datum aclDatum;
bool isNull;
Acl *acl;
2005-10-15 04:49:52 +02:00
Oid ownerId;
/*
* Must get the relation's tuple from pg_class
*/
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(table_oid));
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_TABLE),
errmsg("relation with OID %u does not exist",
table_oid)));
classForm = (Form_pg_class) GETSTRUCT(tuple);
/*
* Deny anyone permission to update a system catalog unless
* pg_authid.rolsuper is set. Also allow it if allowSystemTableMods.
*
2004-08-29 07:07:03 +02:00
* As of 7.4 we have some updatable system views; those shouldn't be
* protected in this way. Assume the view rules can take care of
* themselves. ACL_USAGE is if we ever have system sequences.
*/
if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE)) &&
IsSystemClass(table_oid, classForm) &&
classForm->relkind != RELKIND_VIEW &&
!superuser_arg(roleid) &&
!allowSystemTableMods)
{
#ifdef ACLDEBUG
elog(DEBUG2, "permission denied for system catalog update");
#endif
mask &= ~(ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE);
}
/*
* Otherwise, superusers bypass all permission-checking.
*/
if (superuser_arg(roleid))
{
#ifdef ACLDEBUG
elog(DEBUG2, "OID %u is superuser, home free", roleid);
#endif
ReleaseSysCache(tuple);
return mask;
}
/*
* Normal case: get the relation's ACL from pg_class
*/
ownerId = classForm->relowner;
aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl,
&isNull);
if (isNull)
{
/* No ACL, so build default ACL */
switch (classForm->relkind)
{
case RELKIND_SEQUENCE:
acl = acldefault(OBJECT_SEQUENCE, ownerId);
break;
default:
acl = acldefault(OBJECT_TABLE, ownerId);
break;
}
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);
1998-09-01 05:29:17 +02:00
return result;
}
/*
* Exported routine for examining a user's privileges for a database
*/
AclMode
pg_database_aclmask(Oid db_oid, Oid roleid,
AclMode mask, AclMaskHow how)
{
AclMode result;
HeapTuple tuple;
Datum aclDatum;
bool isNull;
Acl *acl;
Oid ownerId;
/* Superusers bypass all permission checking. */
if (superuser_arg(roleid))
return mask;
/*
* Get the database's ACL from pg_database
*/
tuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(db_oid));
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_DATABASE),
errmsg("database with OID %u does not exist", db_oid)));
ownerId = ((Form_pg_database) GETSTRUCT(tuple))->datdba;
aclDatum = SysCacheGetAttr(DATABASEOID, tuple, Anum_pg_database_datacl,
&isNull);
if (isNull)
{
/* No ACL, so build default ACL */
acl = acldefault(OBJECT_DATABASE, ownerId);
aclDatum = (Datum) 0;
}
else
{
/* detoast ACL if necessary */
acl = DatumGetAclP(aclDatum);
}
result = aclmask(acl, roleid, ownerId, mask, how);
/* if we have a detoasted copy, free it */
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
pfree(acl);
ReleaseSysCache(tuple);
return result;
}
/*
* Exported routine for examining a user's privileges for a function
*/
AclMode
pg_proc_aclmask(Oid proc_oid, Oid roleid,
AclMode mask, AclMaskHow how)
{
AclMode result;
HeapTuple tuple;
Datum aclDatum;
bool isNull;
Acl *acl;
2005-10-15 04:49:52 +02:00
Oid ownerId;
/* Superusers bypass all permission checking. */
if (superuser_arg(roleid))
return mask;
/*
* Get the function's ACL from pg_proc
*/
tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(proc_oid));
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
2005-10-15 04:49:52 +02:00
errmsg("function with OID %u does not exist", proc_oid)));
ownerId = ((Form_pg_proc) GETSTRUCT(tuple))->proowner;
aclDatum = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_proacl,
&isNull);
if (isNull)
{
/* No ACL, so build default ACL */
acl = acldefault(OBJECT_FUNCTION, ownerId);
aclDatum = (Datum) 0;
}
else
{
/* detoast ACL if necessary */
acl = DatumGetAclP(aclDatum);
}
result = aclmask(acl, roleid, ownerId, mask, how);
/* if we have a detoasted copy, free it */
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
pfree(acl);
ReleaseSysCache(tuple);
return result;
}
/*
* Exported routine for examining a user's privileges for a language
*/
AclMode
pg_language_aclmask(Oid lang_oid, Oid roleid,
AclMode mask, AclMaskHow how)
{
AclMode result;
HeapTuple tuple;
Datum aclDatum;
bool isNull;
Acl *acl;
2005-10-15 04:49:52 +02:00
Oid ownerId;
/* Superusers bypass all permission checking. */
if (superuser_arg(roleid))
return mask;
/*
* Get the language's ACL from pg_language
*/
tuple = SearchSysCache1(LANGOID, ObjectIdGetDatum(lang_oid));
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
2005-10-15 04:49:52 +02:00
errmsg("language with OID %u does not exist", lang_oid)));
ownerId = ((Form_pg_language) GETSTRUCT(tuple))->lanowner;
aclDatum = SysCacheGetAttr(LANGOID, tuple, Anum_pg_language_lanacl,
&isNull);
if (isNull)
{
/* No ACL, so build default ACL */
acl = acldefault(OBJECT_LANGUAGE, ownerId);
aclDatum = (Datum) 0;
}
else
{
/* detoast ACL if necessary */
acl = DatumGetAclP(aclDatum);
}
result = aclmask(acl, roleid, ownerId, mask, how);
/* if we have a detoasted copy, free it */
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
pfree(acl);
ReleaseSysCache(tuple);
return result;
}
/*
* Exported routine for examining a user's privileges for a largeobject
*
* When a large object is opened for reading, it is opened relative to the
* 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
* to systable_beginscan().
*/
AclMode
pg_largeobject_aclmask_snapshot(Oid lobj_oid, Oid roleid,
AclMode mask, AclMaskHow how,
Snapshot snapshot)
{
AclMode result;
Relation pg_lo_meta;
2010-02-26 03:01:40 +01:00
ScanKeyData entry[1];
SysScanDesc scan;
HeapTuple tuple;
Datum aclDatum;
bool isNull;
Acl *acl;
Oid ownerId;
/* Superusers bypass all permission checking. */
if (superuser_arg(roleid))
return mask;
/*
* Get the largeobject's ACL from pg_largeobject_metadata
*/
pg_lo_meta = table_open(LargeObjectMetadataRelationId,
AccessShareLock);
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,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(lobj_oid));
scan = systable_beginscan(pg_lo_meta,
LargeObjectMetadataOidIndexId, true,
snapshot, 1, entry);
tuple = systable_getnext(scan);
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("large object %u does not exist", lobj_oid)));
ownerId = ((Form_pg_largeobject_metadata) GETSTRUCT(tuple))->lomowner;
aclDatum = heap_getattr(tuple, Anum_pg_largeobject_metadata_lomacl,
RelationGetDescr(pg_lo_meta), &isNull);
if (isNull)
{
/* No ACL, so build default ACL */
acl = acldefault(OBJECT_LARGEOBJECT, ownerId);
aclDatum = (Datum) 0;
}
else
{
/* detoast ACL if necessary */
acl = DatumGetAclP(aclDatum);
}
result = aclmask(acl, roleid, ownerId, mask, how);
/* if we have a detoasted copy, free it */
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
pfree(acl);
systable_endscan(scan);
table_close(pg_lo_meta, AccessShareLock);
return result;
}
/*
* Exported routine for examining a user's privileges for a namespace
*/
AclMode
pg_namespace_aclmask(Oid nsp_oid, Oid roleid,
AclMode mask, AclMaskHow how)
{
AclMode result;
HeapTuple tuple;
Datum aclDatum;
bool isNull;
Acl *acl;
2005-10-15 04:49:52 +02:00
Oid ownerId;
/* Superusers bypass all permission checking. */
if (superuser_arg(roleid))
return mask;
*) inet_(client|server)_(addr|port)() and necessary documentation for the four functions. > Also, please justify the temp-related changes. I was not aware that we > had any breakage there. patch-tmp-schema.txt contains the following bits: *) Changes pg_namespace_aclmask() so that the superuser is always able to create objects in the temp namespace. *) Changes pg_namespace_aclmask() so that if this is a temp namespace, objects are only allowed to be created in the temp namespace if the user has TEMP privs on the database. This encompasses all object creation, not just TEMP tables. *) InitTempTableNamespace() checks to see if the current user, not the session user, has access to create a temp namespace. The first two changes are necessary to support the third change. Now it's possible to revoke all temp table privs from non-super users and limiting all creation of temp tables/schemas via a function that's executed with elevated privs (security definer). Before this change, it was not possible to have a setuid function to create a temp table/schema if the session user had no TEMP privs. patch-area-path.txt contains: *) Can now determine the area of a closed path. patch-dfmgr.txt contains: *) Small tweak to add the library path that's being expanded. I was using $lib/foo.so and couldn't easily figure out what the error message, "invalid macro name in dynamic library path" meant without looking through the source code. With the path in there, at least I know where to start looking in my config file. Sean Chittenden
2004-05-26 20:35:51 +02:00
/*
2005-10-15 04:49:52 +02:00
* If we have been assigned this namespace as a temp namespace, check to
* make sure we have CREATE TEMP permission on the database, and if so act
* as though we have all standard (but not GRANT OPTION) permissions on
* the namespace. If we don't have CREATE TEMP, act as though we have
* only USAGE (and not CREATE) rights.
*) inet_(client|server)_(addr|port)() and necessary documentation for the four functions. > Also, please justify the temp-related changes. I was not aware that we > had any breakage there. patch-tmp-schema.txt contains the following bits: *) Changes pg_namespace_aclmask() so that the superuser is always able to create objects in the temp namespace. *) Changes pg_namespace_aclmask() so that if this is a temp namespace, objects are only allowed to be created in the temp namespace if the user has TEMP privs on the database. This encompasses all object creation, not just TEMP tables. *) InitTempTableNamespace() checks to see if the current user, not the session user, has access to create a temp namespace. The first two changes are necessary to support the third change. Now it's possible to revoke all temp table privs from non-super users and limiting all creation of temp tables/schemas via a function that's executed with elevated privs (security definer). Before this change, it was not possible to have a setuid function to create a temp table/schema if the session user had no TEMP privs. patch-area-path.txt contains: *) Can now determine the area of a closed path. patch-dfmgr.txt contains: *) Small tweak to add the library path that's being expanded. I was using $lib/foo.so and couldn't easily figure out what the error message, "invalid macro name in dynamic library path" meant without looking through the source code. With the path in there, at least I know where to start looking in my config file. Sean Chittenden
2004-05-26 20:35:51 +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
2005-10-15 04:49:52 +02:00
* upshot of this behavior is that a SECURITY DEFINER function can create
* temp tables that can then be accessed (if permission is granted) by
* code in the same session that doesn't have permissions to create temp
* tables.
*
* XXX Would it be safe to ereport a special error message as
* InitTempTableNamespace does? Returning zero here means we'll get a
2005-10-15 04:49:52 +02:00
* generic "permission denied for schema pg_temp_N" message, which is not
* remarkably user-friendly.
*) inet_(client|server)_(addr|port)() and necessary documentation for the four functions. > Also, please justify the temp-related changes. I was not aware that we > had any breakage there. patch-tmp-schema.txt contains the following bits: *) Changes pg_namespace_aclmask() so that the superuser is always able to create objects in the temp namespace. *) Changes pg_namespace_aclmask() so that if this is a temp namespace, objects are only allowed to be created in the temp namespace if the user has TEMP privs on the database. This encompasses all object creation, not just TEMP tables. *) InitTempTableNamespace() checks to see if the current user, not the session user, has access to create a temp namespace. The first two changes are necessary to support the third change. Now it's possible to revoke all temp table privs from non-super users and limiting all creation of temp tables/schemas via a function that's executed with elevated privs (security definer). Before this change, it was not possible to have a setuid function to create a temp table/schema if the session user had no TEMP privs. patch-area-path.txt contains: *) Can now determine the area of a closed path. patch-dfmgr.txt contains: *) Small tweak to add the library path that's being expanded. I was using $lib/foo.so and couldn't easily figure out what the error message, "invalid macro name in dynamic library path" meant without looking through the source code. With the path in there, at least I know where to start looking in my config file. Sean Chittenden
2004-05-26 20:35:51 +02:00
*/
if (isTempNamespace(nsp_oid))
{
if (pg_database_aclcheck(MyDatabaseId, roleid,
ACL_CREATE_TEMP) == ACLCHECK_OK)
return mask & ACL_ALL_RIGHTS_SCHEMA;
else
return mask & ACL_USAGE;
*) inet_(client|server)_(addr|port)() and necessary documentation for the four functions. > Also, please justify the temp-related changes. I was not aware that we > had any breakage there. patch-tmp-schema.txt contains the following bits: *) Changes pg_namespace_aclmask() so that the superuser is always able to create objects in the temp namespace. *) Changes pg_namespace_aclmask() so that if this is a temp namespace, objects are only allowed to be created in the temp namespace if the user has TEMP privs on the database. This encompasses all object creation, not just TEMP tables. *) InitTempTableNamespace() checks to see if the current user, not the session user, has access to create a temp namespace. The first two changes are necessary to support the third change. Now it's possible to revoke all temp table privs from non-super users and limiting all creation of temp tables/schemas via a function that's executed with elevated privs (security definer). Before this change, it was not possible to have a setuid function to create a temp table/schema if the session user had no TEMP privs. patch-area-path.txt contains: *) Can now determine the area of a closed path. patch-dfmgr.txt contains: *) Small tweak to add the library path that's being expanded. I was using $lib/foo.so and couldn't easily figure out what the error message, "invalid macro name in dynamic library path" meant without looking through the source code. With the path in there, at least I know where to start looking in my config file. Sean Chittenden
2004-05-26 20:35:51 +02:00
}
/*
* Get the schema's ACL from pg_namespace
*/
tuple = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(nsp_oid));
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_SCHEMA),
errmsg("schema with OID %u does not exist", nsp_oid)));
ownerId = ((Form_pg_namespace) GETSTRUCT(tuple))->nspowner;
aclDatum = SysCacheGetAttr(NAMESPACEOID, tuple, Anum_pg_namespace_nspacl,
&isNull);
if (isNull)
{
/* No ACL, so build default ACL */
acl = acldefault(OBJECT_SCHEMA, ownerId);
aclDatum = (Datum) 0;
}
else
{
/* detoast ACL if necessary */
acl = DatumGetAclP(aclDatum);
}
result = aclmask(acl, roleid, ownerId, mask, how);
/* if we have a detoasted copy, free it */
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
pfree(acl);
ReleaseSysCache(tuple);
return result;
}
/*
* Exported routine for examining a user's privileges for a tablespace
*/
AclMode
pg_tablespace_aclmask(Oid spc_oid, Oid roleid,
AclMode mask, AclMaskHow how)
{
AclMode result;
HeapTuple tuple;
Datum aclDatum;
bool isNull;
Acl *acl;
2005-10-15 04:49:52 +02:00
Oid ownerId;
/* Superusers bypass all permission checking. */
if (superuser_arg(roleid))
return mask;
/*
* Get the tablespace's ACL from pg_tablespace
*/
tuple = SearchSysCache1(TABLESPACEOID, ObjectIdGetDatum(spc_oid));
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
2005-10-15 04:49:52 +02:00
errmsg("tablespace with OID %u does not exist", spc_oid)));
ownerId = ((Form_pg_tablespace) GETSTRUCT(tuple))->spcowner;
aclDatum = SysCacheGetAttr(TABLESPACEOID, tuple,
2010-02-26 03:01:40 +01:00
Anum_pg_tablespace_spcacl,
&isNull);
if (isNull)
{
/* No ACL, so build default ACL */
acl = acldefault(OBJECT_TABLESPACE, ownerId);
aclDatum = (Datum) 0;
}
else
{
/* detoast ACL if necessary */
acl = DatumGetAclP(aclDatum);
}
result = aclmask(acl, roleid, ownerId, mask, how);
/* if we have a detoasted copy, free it */
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
pfree(acl);
ReleaseSysCache(tuple);
return result;
}
/*
* 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
*/
tuple = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdw_oid));
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("foreign-data wrapper with OID %u does not exist",
fdw_oid)));
fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tuple);
/*
* Normal case: get the FDW's ACL from pg_foreign_data_wrapper
*/
ownerId = fdwForm->fdwowner;
aclDatum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID, tuple,
Anum_pg_foreign_data_wrapper_fdwacl, &isNull);
if (isNull)
{
/* No ACL, so build default ACL */
acl = acldefault(OBJECT_FDW, ownerId);
aclDatum = (Datum) 0;
}
else
{
/* detoast rel's ACL if necessary */
acl = DatumGetAclP(aclDatum);
}
result = aclmask(acl, roleid, ownerId, mask, how);
/* if we have a detoasted copy, free it */
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
pfree(acl);
ReleaseSysCache(tuple);
return result;
}
/*
* Exported routine for examining a user's privileges for a foreign
* server.
*/
AclMode
pg_foreign_server_aclmask(Oid srv_oid, Oid roleid,
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
*/
tuple = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(srv_oid));
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("foreign server with OID %u does not exist",
srv_oid)));
srvForm = (Form_pg_foreign_server) GETSTRUCT(tuple);
/*
* Normal case: get the foreign server's ACL from pg_foreign_server
*/
ownerId = srvForm->srvowner;
aclDatum = SysCacheGetAttr(FOREIGNSERVEROID, tuple,
Anum_pg_foreign_server_srvacl, &isNull);
if (isNull)
{
/* No ACL, so build default ACL */
acl = acldefault(OBJECT_FOREIGN_SERVER, ownerId);
aclDatum = (Datum) 0;
}
else
{
/* detoast rel's ACL if necessary */
acl = DatumGetAclP(aclDatum);
}
result = aclmask(acl, roleid, ownerId, mask, how);
/* if we have a detoasted copy, free it */
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
pfree(acl);
ReleaseSysCache(tuple);
return result;
}
/*
* 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,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("type with OID %u does not exist",
type_oid)));
typeForm = (Form_pg_type) GETSTRUCT(tuple);
/*
* "True" array types don't manage permissions of their own; consult the
* element type instead.
*/
if (OidIsValid(typeForm->typelem) && typeForm->typlen == -1)
{
Oid elttype_oid = typeForm->typelem;
ReleaseSysCache(tuple);
tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(elttype_oid));
/* this case is not a user-facing error, so elog not ereport */
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for type %u", elttype_oid);
typeForm = (Form_pg_type) GETSTRUCT(tuple);
}
/*
* Now get the type's owner and ACL from the tuple
*/
ownerId = typeForm->typowner;
aclDatum = SysCacheGetAttr(TYPEOID, tuple,
Anum_pg_type_typacl, &isNull);
if (isNull)
{
/* No ACL, so build default ACL */
acl = acldefault(OBJECT_TYPE, ownerId);
aclDatum = (Datum) 0;
}
else
{
/* detoast rel's ACL if necessary */
acl = DatumGetAclP(aclDatum);
}
result = aclmask(acl, roleid, ownerId, mask, how);
/* if we have a detoasted copy, free it */
if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
pfree(acl);
ReleaseSysCache(tuple);
return result;
}
/*
* Exported routine for checking a user's access privileges to a column
*
* Returns ACLCHECK_OK if the user has any of the privileges identified by
* 'mode'; otherwise returns a suitable error code (in practice, always
* ACLCHECK_NO_PRIV).
*
* As with pg_attribute_aclmask, only privileges granted directly on the
* column are considered here.
*/
AclResult
pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum,
Oid roleid, AclMode mode)
{
if (pg_attribute_aclmask(table_oid, attnum, roleid, mode, ACLMASK_ANY) != 0)
return ACLCHECK_OK;
else
return ACLCHECK_NO_PRIV;
}
/*
* Exported routine for checking a user's access privileges to any/all columns
*
* If 'how' is ACLMASK_ANY, then returns ACLCHECK_OK if user has any of the
* privileges identified by 'mode' on any non-dropped column in the relation;
* otherwise returns a suitable error code (in practice, always
* ACLCHECK_NO_PRIV).
*
* If 'how' is ACLMASK_ALL, then returns ACLCHECK_OK if user has any of the
* privileges identified by 'mode' on each non-dropped column in the relation
* (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;
/*
* 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.
*/
classTuple = SearchSysCache1(RELOID, ObjectIdGetDatum(table_oid));
if (!HeapTupleIsValid(classTuple))
return ACLCHECK_NO_PRIV;
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;
AclMode attmask;
attTuple = SearchSysCache2(ATTNUM,
ObjectIdGetDatum(table_oid),
Int16GetDatum(curr_att));
if (!HeapTupleIsValid(attTuple))
continue;
/* ignore dropped columns */
if (((Form_pg_attribute) GETSTRUCT(attTuple))->attisdropped)
{
ReleaseSysCache(attTuple);
continue;
}
/*
* 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.
*/
if (heap_attisnull(attTuple, Anum_pg_attribute_attacl, NULL))
attmask = 0;
else
attmask = pg_attribute_aclmask(table_oid, curr_att, roleid,
mode, ACLMASK_ANY);
ReleaseSysCache(attTuple);
if (attmask != 0)
{
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;
}
/*
* 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
pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
{
if (pg_class_aclmask(table_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 database
*/
AclResult
pg_database_aclcheck(Oid db_oid, Oid roleid, AclMode mode)
{
if (pg_database_aclmask(db_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 function
*/
AclResult
pg_proc_aclcheck(Oid proc_oid, Oid roleid, AclMode mode)
{
if (pg_proc_aclmask(proc_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 language
*/
AclResult
pg_language_aclcheck(Oid lang_oid, Oid roleid, AclMode mode)
{
if (pg_language_aclmask(lang_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 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;
}
/*
* Exported routine for checking a user's access privileges to a namespace
*/
AclResult
pg_namespace_aclcheck(Oid nsp_oid, Oid roleid, AclMode mode)
{
if (pg_namespace_aclmask(nsp_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 tablespace
*/
AclResult
pg_tablespace_aclcheck(Oid spc_oid, Oid roleid, AclMode mode)
{
if (pg_tablespace_aclmask(spc_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
* 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;
}
/*
* 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;
}
/*
* Ownership check for a relation (specified by OID).
*/
bool
pg_class_ownercheck(Oid class_oid, Oid roleid)
{
HeapTuple tuple;
2005-10-15 04:49:52 +02:00
Oid ownerId;
/* Superusers bypass all permission checking. */
if (superuser_arg(roleid))
return true;
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(class_oid));
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_TABLE),
2005-10-15 04:49:52 +02:00
errmsg("relation with OID %u does not exist", class_oid)));
ownerId = ((Form_pg_class) GETSTRUCT(tuple))->relowner;
ReleaseSysCache(tuple);
return has_privs_of_role(roleid, ownerId);
}
/*
* Ownership check for a type (specified by OID).
*/
bool
pg_type_ownercheck(Oid type_oid, Oid roleid)
{
HeapTuple tuple;
2005-10-15 04:49:52 +02:00
Oid ownerId;
/* Superusers bypass all permission checking. */
if (superuser_arg(roleid))
return true;
tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_oid));
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("type with OID %u does not exist", type_oid)));
ownerId = ((Form_pg_type) GETSTRUCT(tuple))->typowner;
ReleaseSysCache(tuple);
return has_privs_of_role(roleid, ownerId);
}
/*
* Ownership check for an operator (specified by OID).
*/
bool
pg_oper_ownercheck(Oid oper_oid, Oid roleid)
{
HeapTuple tuple;
2005-10-15 04:49:52 +02:00
Oid ownerId;
/* Superusers bypass all permission checking. */
if (superuser_arg(roleid))
return true;
tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(oper_oid));
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
2005-10-15 04:49:52 +02:00
errmsg("operator with OID %u does not exist", oper_oid)));
ownerId = ((Form_pg_operator) GETSTRUCT(tuple))->oprowner;
ReleaseSysCache(tuple);
return has_privs_of_role(roleid, ownerId);
}
/*
* Ownership check for a function (specified by OID).
*/
bool
pg_proc_ownercheck(Oid proc_oid, Oid roleid)
{
HeapTuple tuple;
2005-10-15 04:49:52 +02:00
Oid ownerId;
/* Superusers bypass all permission checking. */
if (superuser_arg(roleid))
return true;
tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(proc_oid));
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
2005-10-15 04:49:52 +02:00
errmsg("function with OID %u does not exist", proc_oid)));
ownerId = ((Form_pg_proc) GETSTRUCT(tuple))->proowner;
ReleaseSysCache(tuple);
return has_privs_of_role(roleid, ownerId);
}
/*
* 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;
tuple = SearchSysCache1(LANGOID, ObjectIdGetDatum(lan_oid));
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);
}
/*
* Ownership check for a largeobject (specified by OID)
*
* This is only used for operations like ALTER LARGE OBJECT that are always
* relative to an up-to-date snapshot.
*/
bool
pg_largeobject_ownercheck(Oid lobj_oid, Oid roleid)
{
Relation pg_lo_meta;
2010-02-26 03:01:40 +01:00
ScanKeyData entry[1];
SysScanDesc scan;
HeapTuple tuple;
Oid ownerId;
/* Superusers bypass all permission checking. */
if (superuser_arg(roleid))
return true;
/* There's no syscache for pg_largeobject_metadata */
pg_lo_meta = table_open(LargeObjectMetadataRelationId,
AccessShareLock);
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,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(lobj_oid));
scan = systable_beginscan(pg_lo_meta,
LargeObjectMetadataOidIndexId, true,
NULL, 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;
systable_endscan(scan);
table_close(pg_lo_meta, AccessShareLock);
return has_privs_of_role(roleid, ownerId);
}
/*
* Ownership check for a namespace (specified by OID).
*/
bool
pg_namespace_ownercheck(Oid nsp_oid, Oid roleid)
{
HeapTuple tuple;
2005-10-15 04:49:52 +02:00
Oid ownerId;
/* Superusers bypass all permission checking. */
if (superuser_arg(roleid))
return true;
tuple = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(nsp_oid));
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_SCHEMA),
errmsg("schema with OID %u does not exist", nsp_oid)));
ownerId = ((Form_pg_namespace) GETSTRUCT(tuple))->nspowner;
ReleaseSysCache(tuple);
return has_privs_of_role(roleid, ownerId);
}
/*
* Ownership check for a tablespace (specified by OID).
*/
bool
pg_tablespace_ownercheck(Oid spc_oid, Oid roleid)
{
HeapTuple spctuple;
2005-10-15 04:49:52 +02:00
Oid spcowner;
/* Superusers bypass all permission checking. */
if (superuser_arg(roleid))
return true;
/* Search syscache for pg_tablespace */
spctuple = SearchSysCache1(TABLESPACEOID, ObjectIdGetDatum(spc_oid));
if (!HeapTupleIsValid(spctuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
2005-10-15 04:49:52 +02:00
errmsg("tablespace with OID %u does not exist", spc_oid)));
spcowner = ((Form_pg_tablespace) GETSTRUCT(spctuple))->spcowner;
ReleaseSysCache(spctuple);
return has_privs_of_role(roleid, spcowner);
}
/*
* Ownership check for an operator class (specified by OID).
*/
bool
pg_opclass_ownercheck(Oid opc_oid, Oid roleid)
{
HeapTuple tuple;
2005-10-15 04:49:52 +02:00
Oid ownerId;
/* Superusers bypass all permission checking. */
if (superuser_arg(roleid))
return true;
tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opc_oid));
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("operator class with OID %u does not exist",
opc_oid)));
ownerId = ((Form_pg_opclass) GETSTRUCT(tuple))->opcowner;
ReleaseSysCache(tuple);
return has_privs_of_role(roleid, ownerId);
}
2003-06-27 16:45:32 +02: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;
tuple = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opf_oid));
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);
}
/*
* 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;
tuple = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dict_oid));
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;
tuple = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfg_oid));
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);
}
/*
* 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);
}
/*
* 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;
tuple = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(srv_oid));
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);
}
/*
* 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
/*
* Ownership check for a database (specified by OID).
2003-06-27 16:45:32 +02:00
*/
bool
pg_database_ownercheck(Oid db_oid, Oid roleid)
2003-06-27 16:45:32 +02:00
{
HeapTuple tuple;
2005-10-15 04:49:52 +02:00
Oid dba;
2003-06-27 16:45:32 +02:00
/* Superusers bypass all permission checking. */
if (superuser_arg(roleid))
2003-06-27 16:45:32 +02:00
return true;
tuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(db_oid));
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_DATABASE),
errmsg("database with OID %u does not exist", db_oid)));
2003-06-27 16:45:32 +02:00
dba = ((Form_pg_database) GETSTRUCT(tuple))->datdba;
2003-06-27 16:45:32 +02:00
ReleaseSysCache(tuple);
2003-06-27 16:45:32 +02:00
return has_privs_of_role(roleid, dba);
2003-06-27 16:45:32 +02: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);
}
/*
* Ownership check for a conversion (specified by OID).
*/
bool
pg_conversion_ownercheck(Oid conv_oid, Oid roleid)
{
HeapTuple tuple;
2005-10-15 04:49:52 +02:00
Oid ownerId;
/* Superusers bypass all permission checking. */
if (superuser_arg(roleid))
return true;
tuple = SearchSysCache1(CONVOID, ObjectIdGetDatum(conv_oid));
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
2005-10-15 04:49:52 +02:00
errmsg("conversion with OID %u does not exist", conv_oid)));
ownerId = ((Form_pg_conversion) GETSTRUCT(tuple))->conowner;
ReleaseSysCache(tuple);
return has_privs_of_role(roleid, ownerId);
}
/*
* Ownership check for an extension (specified by OID).
*/
bool
pg_extension_ownercheck(Oid ext_oid, Oid roleid)
{
Relation pg_extension;
ScanKeyData entry[1];
SysScanDesc scan;
HeapTuple tuple;
Oid ownerId;
/* Superusers bypass all permission checking. */
if (superuser_arg(roleid))
return true;
/* There's no syscache for pg_extension, so do it the hard way */
pg_extension = table_open(ExtensionRelationId, AccessShareLock);
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,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(ext_oid));
scan = systable_beginscan(pg_extension,
ExtensionOidIndexId, true,
NULL, 1, entry);
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);
table_close(pg_extension, AccessShareLock);
return has_privs_of_role(roleid, ownerId);
}
/*
* Ownership check for a publication (specified by OID).
*/
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).
*/
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
/*
* 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
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),
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
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);
}
/*
* Check whether specified role has CREATEROLE privilege (or is a superuser)
*
* 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.)
*/
bool
has_createrole_privilege(Oid roleid)
{
bool result = false;
HeapTuple utup;
/* Superusers bypass all permission checking. */
if (superuser_arg(roleid))
return true;
utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
if (HeapTupleIsValid(utup))
{
result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreaterole;
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
bool
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
{
bool result = false;
HeapTuple utup;
/* Superusers bypass all permission checking. */
if (superuser_arg(roleid))
return true;
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
}
/*
* 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;
tuple = SearchSysCache3(DEFACLROLENSPOBJ,
ObjectIdGetDatum(roleId),
ObjectIdGetDatum(nsp_oid),
CharGetDatum(objtype));
if (HeapTupleIsValid(tuple))
{
2010-02-26 03:01:40 +01:00
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.
*/
Acl *
get_user_default_acl(ObjectType objtype, Oid ownerId, Oid nsp_oid)
{
Acl *result;
Acl *glob_acl;
Acl *schema_acl;
Acl *def_acl;
char defaclobjtype;
/*
* Use NULL during bootstrap, since pg_default_acl probably isn't there
* yet.
*/
if (IsBootstrapProcessingMode())
return NULL;
/* Check if object type is supported in pg_default_acl */
switch (objtype)
{
case OBJECT_TABLE:
defaclobjtype = DEFACLOBJ_RELATION;
break;
case OBJECT_SEQUENCE:
defaclobjtype = DEFACLOBJ_SEQUENCE;
break;
case OBJECT_FUNCTION:
defaclobjtype = DEFACLOBJ_FUNCTION;
break;
case OBJECT_TYPE:
defaclobjtype = DEFACLOBJ_TYPE;
break;
case OBJECT_SCHEMA:
defaclobjtype = DEFACLOBJ_NAMESPACE;
break;
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;
}
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);
}
/*
* Record initial privileges for the top-level object passed in.
*
* 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);
/* Indexes don't have permissions */
if (pg_class_tuple->relkind == RELKIND_INDEX ||
pg_class_tuple->relkind == RELKIND_PARTITIONED_INDEX)
return;
/* Composite types don't have permissions either */
if (pg_class_tuple->relkind == RELKIND_COMPOSITE_TYPE)
return;
/*
* If this isn't a sequence, index, or composite type then it's
* possibly going to have columns associated with it that might have
* ACLs.
*/
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;
relation = table_open(LargeObjectMetadataRelationId, RowExclusiveLock);
/* 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,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(objoid));
scan = systable_beginscan(relation,
LargeObjectMetadataOidIndexId, true,
NULL, 1, entry);
tuple = systable_getnext(scan);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "could not find tuple for large object %u", objoid);
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);
/* Indexes don't have permissions */
if (pg_class_tuple->relkind == RELKIND_INDEX ||
pg_class_tuple->relkind == RELKIND_PARTITIONED_INDEX)
return;
/* Composite types don't have permissions either */
if (pg_class_tuple->relkind == RELKIND_COMPOSITE_TYPE)
return;
/*
* If this isn't a sequence, index, or composite type then it's
* possibly going to have columns associated with it that might have
* ACLs.
*/
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;
/* when removing, remove all entries, even dropped columns */
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
*
* 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)
{
/*
* 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)
return;
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;
relation = table_open(InitPrivsRelationId, RowExclusiveLock);
ScanKeyInit(&key[0],
Anum_pg_init_privs_objoid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(objoid));
ScanKeyInit(&key[1],
Anum_pg_init_privs_classoid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(classoid));
ScanKeyInit(&key[2],
Anum_pg_init_privs_objsubid,
BTEqualStrategyNumber, F_INT4EQ,
Int32GetDatum(objsubid));
scan = systable_beginscan(relation, InitPrivsObjIndexId, true,
NULL, 3, key);
/* There should exist only one entry or none. */
oldtuple = systable_getnext(scan);
/* If we find an entry, update it with the latest ACL. */
if (HeapTupleIsValid(oldtuple))
{
Datum values[Natts_pg_init_privs];
bool nulls[Natts_pg_init_privs];
bool replace[Natts_pg_init_privs];
/* If we have a new ACL to set, then update the row with it. */
if (new_acl)
{
MemSet(values, 0, sizeof(values));
MemSet(nulls, false, sizeof(nulls));
MemSet(replace, false, sizeof(replace));
values[Anum_pg_init_privs_initprivs - 1] = PointerGetDatum(new_acl);
replace[Anum_pg_init_privs_initprivs - 1] = true;
oldtuple = heap_modify_tuple(oldtuple, RelationGetDescr(relation),
values, nulls, replace);
CatalogTupleUpdate(relation, &oldtuple->t_self, oldtuple);
}
else
{
/* new_acl is NULL, so delete the entry we found. */
CatalogTupleDelete(relation, &oldtuple->t_self);
}
}
else
{
Datum values[Natts_pg_init_privs];
bool nulls[Natts_pg_init_privs];
/*
* 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));
values[Anum_pg_init_privs_objoid - 1] = ObjectIdGetDatum(objoid);
values[Anum_pg_init_privs_classoid - 1] = ObjectIdGetDatum(classoid);
values[Anum_pg_init_privs_objsubid - 1] = Int32GetDatum(objsubid);
/* This function only handles initial privileges of extensions */
values[Anum_pg_init_privs_privtype - 1] =
CharGetDatum(INITPRIVS_EXTENSION);
values[Anum_pg_init_privs_initprivs - 1] = PointerGetDatum(new_acl);
tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls);
CatalogTupleInsert(relation, tuple);
}
}
systable_endscan(scan);
/* prevent error when processing objects multiple times */
CommandCounterIncrement();
table_close(relation, RowExclusiveLock);
}