Fix xslt_process() to ensure that it inserts a NULL terminator after the
[PostgreSQL.git] / src / backend / catalog / aclchk.c
blobec4aaf0bf14ab9c063a469944fcac76ed1241f04
1 /*-------------------------------------------------------------------------
3 * aclchk.c
4 * Routines to check access control permissions.
6 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
10 * IDENTIFICATION
11 * $PostgreSQL$
13 * NOTES
14 * See acl.h.
16 *-------------------------------------------------------------------------
18 #include "postgres.h"
20 #include "access/genam.h"
21 #include "access/heapam.h"
22 #include "access/sysattr.h"
23 #include "access/xact.h"
24 #include "catalog/catalog.h"
25 #include "catalog/dependency.h"
26 #include "catalog/indexing.h"
27 #include "catalog/pg_authid.h"
28 #include "catalog/pg_conversion.h"
29 #include "catalog/pg_database.h"
30 #include "catalog/pg_foreign_data_wrapper.h"
31 #include "catalog/pg_foreign_server.h"
32 #include "catalog/pg_language.h"
33 #include "catalog/pg_namespace.h"
34 #include "catalog/pg_opclass.h"
35 #include "catalog/pg_operator.h"
36 #include "catalog/pg_opfamily.h"
37 #include "catalog/pg_proc.h"
38 #include "catalog/pg_tablespace.h"
39 #include "catalog/pg_type.h"
40 #include "catalog/pg_ts_config.h"
41 #include "catalog/pg_ts_dict.h"
42 #include "commands/dbcommands.h"
43 #include "foreign/foreign.h"
44 #include "miscadmin.h"
45 #include "parser/parse_func.h"
46 #include "utils/acl.h"
47 #include "utils/fmgroids.h"
48 #include "utils/lsyscache.h"
49 #include "utils/rel.h"
50 #include "utils/syscache.h"
51 #include "utils/tqual.h"
54 static void ExecGrant_Relation(InternalGrant *grantStmt);
55 static void ExecGrant_Database(InternalGrant *grantStmt);
56 static void ExecGrant_Fdw(InternalGrant *grantStmt);
57 static void ExecGrant_ForeignServer(InternalGrant *grantStmt);
58 static void ExecGrant_Function(InternalGrant *grantStmt);
59 static void ExecGrant_Language(InternalGrant *grantStmt);
60 static void ExecGrant_Namespace(InternalGrant *grantStmt);
61 static void ExecGrant_Tablespace(InternalGrant *grantStmt);
63 static List *objectNamesToOids(GrantObjectType objtype, List *objnames);
64 static void expand_col_privileges(List *colnames, Oid table_oid,
65 AclMode this_privileges,
66 AclMode *col_privileges,
67 int num_col_privileges);
68 static void expand_all_col_privileges(Oid table_oid, Form_pg_class classForm,
69 AclMode this_privileges,
70 AclMode *col_privileges,
71 int num_col_privileges);
72 static AclMode string_to_privilege(const char *privname);
73 static const char *privilege_to_string(AclMode privilege);
74 static AclMode restrict_and_check_grant(bool is_grant, AclMode avail_goptions,
75 bool all_privs, AclMode privileges,
76 Oid objectId, Oid grantorId,
77 AclObjectKind objkind, const char *objname,
78 AttrNumber att_number, const char *colname);
79 static AclMode pg_aclmask(AclObjectKind objkind, Oid table_oid, AttrNumber attnum,
80 Oid roleid, AclMode mask, AclMaskHow how);
83 #ifdef ACLDEBUG
84 static void
85 dumpacl(Acl *acl)
87 int i;
88 AclItem *aip;
90 elog(DEBUG2, "acl size = %d, # acls = %d",
91 ACL_SIZE(acl), ACL_NUM(acl));
92 aip = ACL_DAT(acl);
93 for (i = 0; i < ACL_NUM(acl); ++i)
94 elog(DEBUG2, " acl[%d]: %s", i,
95 DatumGetCString(DirectFunctionCall1(aclitemout,
96 PointerGetDatum(aip + i))));
98 #endif /* ACLDEBUG */
102 * If is_grant is true, adds the given privileges for the list of
103 * grantees to the existing old_acl. If is_grant is false, the
104 * privileges for the given grantees are removed from old_acl.
106 * NB: the original old_acl is pfree'd.
108 static Acl *
109 merge_acl_with_grant(Acl *old_acl, bool is_grant,
110 bool grant_option, DropBehavior behavior,
111 List *grantees, AclMode privileges,
112 Oid grantorId, Oid ownerId)
114 unsigned modechg;
115 ListCell *j;
116 Acl *new_acl;
118 modechg = is_grant ? ACL_MODECHG_ADD : ACL_MODECHG_DEL;
120 #ifdef ACLDEBUG
121 dumpacl(old_acl);
122 #endif
123 new_acl = old_acl;
125 foreach(j, grantees)
127 AclItem aclitem;
128 Acl *newer_acl;
130 aclitem. ai_grantee = lfirst_oid(j);
133 * Grant options can only be granted to individual roles, not PUBLIC.
134 * The reason is that if a user would re-grant a privilege that he
135 * held through PUBLIC, and later the user is removed, the situation
136 * is impossible to clean up.
138 if (is_grant && grant_option && aclitem.ai_grantee == ACL_ID_PUBLIC)
139 ereport(ERROR,
140 (errcode(ERRCODE_INVALID_GRANT_OPERATION),
141 errmsg("grant options can only be granted to roles")));
143 aclitem. ai_grantor = grantorId;
146 * The asymmetry in the conditions here comes from the spec. In
147 * GRANT, the grant_option flag signals WITH GRANT OPTION, which means
148 * to grant both the basic privilege and its grant option. But in
149 * REVOKE, plain revoke revokes both the basic privilege and its grant
150 * option, while REVOKE GRANT OPTION revokes only the option.
152 ACLITEM_SET_PRIVS_GOPTIONS(aclitem,
153 (is_grant || !grant_option) ? privileges : ACL_NO_RIGHTS,
154 (!is_grant || grant_option) ? privileges : ACL_NO_RIGHTS);
156 newer_acl = aclupdate(new_acl, &aclitem, modechg, ownerId, behavior);
158 /* avoid memory leak when there are many grantees */
159 pfree(new_acl);
160 new_acl = newer_acl;
162 #ifdef ACLDEBUG
163 dumpacl(new_acl);
164 #endif
167 return new_acl;
171 * Restrict the privileges to what we can actually grant, and emit
172 * the standards-mandated warning and error messages.
174 static AclMode
175 restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
176 AclMode privileges, Oid objectId, Oid grantorId,
177 AclObjectKind objkind, const char *objname,
178 AttrNumber att_number, const char *colname)
180 AclMode this_privileges;
181 AclMode whole_mask;
183 switch (objkind)
185 case ACL_KIND_COLUMN:
186 whole_mask = ACL_ALL_RIGHTS_COLUMN;
187 break;
188 case ACL_KIND_CLASS:
189 whole_mask = ACL_ALL_RIGHTS_RELATION;
190 break;
191 case ACL_KIND_SEQUENCE:
192 whole_mask = ACL_ALL_RIGHTS_SEQUENCE;
193 break;
194 case ACL_KIND_DATABASE:
195 whole_mask = ACL_ALL_RIGHTS_DATABASE;
196 break;
197 case ACL_KIND_PROC:
198 whole_mask = ACL_ALL_RIGHTS_FUNCTION;
199 break;
200 case ACL_KIND_LANGUAGE:
201 whole_mask = ACL_ALL_RIGHTS_LANGUAGE;
202 break;
203 case ACL_KIND_NAMESPACE:
204 whole_mask = ACL_ALL_RIGHTS_NAMESPACE;
205 break;
206 case ACL_KIND_TABLESPACE:
207 whole_mask = ACL_ALL_RIGHTS_TABLESPACE;
208 break;
209 case ACL_KIND_FDW:
210 whole_mask = ACL_ALL_RIGHTS_FDW;
211 break;
212 case ACL_KIND_FOREIGN_SERVER:
213 whole_mask = ACL_ALL_RIGHTS_FOREIGN_SERVER;
214 break;
215 default:
216 elog(ERROR, "unrecognized object kind: %d", objkind);
217 /* not reached, but keep compiler quiet */
218 return ACL_NO_RIGHTS;
222 * If we found no grant options, consider whether to issue a hard error.
223 * Per spec, having any privilege at all on the object will get you by
224 * here.
226 if (avail_goptions == ACL_NO_RIGHTS)
228 if (pg_aclmask(objkind, objectId, att_number, grantorId,
229 whole_mask | ACL_GRANT_OPTION_FOR(whole_mask),
230 ACLMASK_ANY) == ACL_NO_RIGHTS)
232 if (objkind == ACL_KIND_COLUMN && colname)
233 aclcheck_error_col(ACLCHECK_NO_PRIV, objkind, objname, colname);
234 else
235 aclcheck_error(ACLCHECK_NO_PRIV, objkind, objname);
240 * Restrict the operation to what we can actually grant or revoke, and
241 * issue a warning if appropriate. (For REVOKE this isn't quite what the
242 * spec says to do: the spec seems to want a warning only if no privilege
243 * bits actually change in the ACL. In practice that behavior seems much
244 * too noisy, as well as inconsistent with the GRANT case.)
246 this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions);
247 if (is_grant)
249 if (this_privileges == 0)
250 ereport(WARNING,
251 (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
252 errmsg("no privileges were granted for \"%s\"", objname)));
253 else if (!all_privs && this_privileges != privileges)
254 ereport(WARNING,
255 (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
256 errmsg("not all privileges were granted for \"%s\"", objname)));
258 else
260 if (this_privileges == 0)
261 ereport(WARNING,
262 (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
263 errmsg("no privileges could be revoked for \"%s\"", objname)));
264 else if (!all_privs && this_privileges != privileges)
265 ereport(WARNING,
266 (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
267 errmsg("not all privileges could be revoked for \"%s\"", objname)));
270 return this_privileges;
274 * Called to execute the utility commands GRANT and REVOKE
276 void
277 ExecuteGrantStmt(GrantStmt *stmt)
279 InternalGrant istmt;
280 ListCell *cell;
281 const char *errormsg;
282 AclMode all_privileges;
285 * Turn the regular GrantStmt into the InternalGrant form.
287 istmt.is_grant = stmt->is_grant;
288 istmt.objtype = stmt->objtype;
289 istmt.objects = objectNamesToOids(stmt->objtype, stmt->objects);
290 /* all_privs to be filled below */
291 /* privileges to be filled below */
292 istmt.col_privs = NIL; /* may get filled below */
293 istmt.grantees = NIL; /* filled below */
294 istmt.grant_option = stmt->grant_option;
295 istmt.behavior = stmt->behavior;
298 * Convert the PrivGrantee list into an Oid list. Note that at this point
299 * we insert an ACL_ID_PUBLIC into the list if an empty role name is
300 * detected (which is what the grammar uses if PUBLIC is found), so
301 * downstream there shouldn't be any additional work needed to support
302 * this case.
304 foreach(cell, stmt->grantees)
306 PrivGrantee *grantee = (PrivGrantee *) lfirst(cell);
308 if (grantee->rolname == NULL)
309 istmt.grantees = lappend_oid(istmt.grantees, ACL_ID_PUBLIC);
310 else
311 istmt.grantees =
312 lappend_oid(istmt.grantees,
313 get_roleid_checked(grantee->rolname));
317 * Convert stmt->privileges, a list of AccessPriv nodes, into an AclMode
318 * bitmask. Note: objtype can't be ACL_OBJECT_COLUMN.
320 switch (stmt->objtype)
323 * Because this might be a sequence, we test both relation and
324 * sequence bits, and later do a more limited test when we know
325 * the object type.
327 case ACL_OBJECT_RELATION:
328 all_privileges = ACL_ALL_RIGHTS_RELATION | ACL_ALL_RIGHTS_SEQUENCE;
329 errormsg = gettext_noop("invalid privilege type %s for relation");
330 break;
331 case ACL_OBJECT_SEQUENCE:
332 all_privileges = ACL_ALL_RIGHTS_SEQUENCE;
333 errormsg = gettext_noop("invalid privilege type %s for sequence");
334 break;
335 case ACL_OBJECT_DATABASE:
336 all_privileges = ACL_ALL_RIGHTS_DATABASE;
337 errormsg = gettext_noop("invalid privilege type %s for database");
338 break;
339 case ACL_OBJECT_FUNCTION:
340 all_privileges = ACL_ALL_RIGHTS_FUNCTION;
341 errormsg = gettext_noop("invalid privilege type %s for function");
342 break;
343 case ACL_OBJECT_LANGUAGE:
344 all_privileges = ACL_ALL_RIGHTS_LANGUAGE;
345 errormsg = gettext_noop("invalid privilege type %s for language");
346 break;
347 case ACL_OBJECT_NAMESPACE:
348 all_privileges = ACL_ALL_RIGHTS_NAMESPACE;
349 errormsg = gettext_noop("invalid privilege type %s for schema");
350 break;
351 case ACL_OBJECT_TABLESPACE:
352 all_privileges = ACL_ALL_RIGHTS_TABLESPACE;
353 errormsg = gettext_noop("invalid privilege type %s for tablespace");
354 break;
355 case ACL_OBJECT_FDW:
356 all_privileges = ACL_ALL_RIGHTS_FDW;
357 errormsg = gettext_noop("invalid privilege type %s for foreign-data wrapper");
358 break;
359 case ACL_OBJECT_FOREIGN_SERVER:
360 all_privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
361 errormsg = gettext_noop("invalid privilege type %s for foreign server");
362 break;
363 default:
364 /* keep compiler quiet */
365 all_privileges = ACL_NO_RIGHTS;
366 errormsg = NULL;
367 elog(ERROR, "unrecognized GrantStmt.objtype: %d",
368 (int) stmt->objtype);
371 if (stmt->privileges == NIL)
373 istmt.all_privs = true;
376 * will be turned into ACL_ALL_RIGHTS_* by the internal routines
377 * depending on the object type
379 istmt.privileges = ACL_NO_RIGHTS;
381 else
383 istmt.all_privs = false;
384 istmt.privileges = ACL_NO_RIGHTS;
386 foreach(cell, stmt->privileges)
388 AccessPriv *privnode = (AccessPriv *) lfirst(cell);
389 AclMode priv;
392 * If it's a column-level specification, we just set it aside in
393 * col_privs for the moment; but insist it's for a relation.
395 if (privnode->cols)
397 if (stmt->objtype != ACL_OBJECT_RELATION)
398 ereport(ERROR,
399 (errcode(ERRCODE_INVALID_GRANT_OPERATION),
400 errmsg("column privileges are only valid for relations")));
401 istmt.col_privs = lappend(istmt.col_privs, privnode);
402 continue;
405 if (privnode->priv_name == NULL) /* parser mistake? */
406 elog(ERROR, "AccessPriv node must specify privilege or columns");
407 priv = string_to_privilege(privnode->priv_name);
409 if (priv & ~((AclMode) all_privileges))
410 ereport(ERROR,
411 (errcode(ERRCODE_INVALID_GRANT_OPERATION),
412 errmsg(errormsg, privilege_to_string(priv))));
414 istmt.privileges |= priv;
418 ExecGrantStmt_oids(&istmt);
422 * ExecGrantStmt_oids
424 * "Internal" entrypoint for granting and revoking privileges. This is
425 * exported for pg_shdepend.c to use in revoking privileges when dropping
426 * a role.
428 void
429 ExecGrantStmt_oids(InternalGrant *istmt)
431 switch (istmt->objtype)
433 case ACL_OBJECT_RELATION:
434 case ACL_OBJECT_SEQUENCE:
435 ExecGrant_Relation(istmt);
436 break;
437 case ACL_OBJECT_DATABASE:
438 ExecGrant_Database(istmt);
439 break;
440 case ACL_OBJECT_FDW:
441 ExecGrant_Fdw(istmt);
442 break;
443 case ACL_OBJECT_FOREIGN_SERVER:
444 ExecGrant_ForeignServer(istmt);
445 break;
446 case ACL_OBJECT_FUNCTION:
447 ExecGrant_Function(istmt);
448 break;
449 case ACL_OBJECT_LANGUAGE:
450 ExecGrant_Language(istmt);
451 break;
452 case ACL_OBJECT_NAMESPACE:
453 ExecGrant_Namespace(istmt);
454 break;
455 case ACL_OBJECT_TABLESPACE:
456 ExecGrant_Tablespace(istmt);
457 break;
458 default:
459 elog(ERROR, "unrecognized GrantStmt.objtype: %d",
460 (int) istmt->objtype);
465 * objectNamesToOids
467 * Turn a list of object names of a given type into an Oid list.
469 static List *
470 objectNamesToOids(GrantObjectType objtype, List *objnames)
472 List *objects = NIL;
473 ListCell *cell;
475 Assert(objnames != NIL);
477 switch (objtype)
479 case ACL_OBJECT_RELATION:
480 case ACL_OBJECT_SEQUENCE:
481 foreach(cell, objnames)
483 RangeVar *relvar = (RangeVar *) lfirst(cell);
484 Oid relOid;
486 relOid = RangeVarGetRelid(relvar, false);
487 objects = lappend_oid(objects, relOid);
489 break;
490 case ACL_OBJECT_DATABASE:
491 foreach(cell, objnames)
493 char *dbname = strVal(lfirst(cell));
494 Oid dbid;
496 dbid = get_database_oid(dbname);
497 if (!OidIsValid(dbid))
498 ereport(ERROR,
499 (errcode(ERRCODE_UNDEFINED_DATABASE),
500 errmsg("database \"%s\" does not exist",
501 dbname)));
502 objects = lappend_oid(objects, dbid);
504 break;
505 case ACL_OBJECT_FUNCTION:
506 foreach(cell, objnames)
508 FuncWithArgs *func = (FuncWithArgs *) lfirst(cell);
509 Oid funcid;
511 funcid = LookupFuncNameTypeNames(func->funcname,
512 func->funcargs, false);
513 objects = lappend_oid(objects, funcid);
515 break;
516 case ACL_OBJECT_LANGUAGE:
517 foreach(cell, objnames)
519 char *langname = strVal(lfirst(cell));
520 HeapTuple tuple;
522 tuple = SearchSysCache(LANGNAME,
523 PointerGetDatum(langname),
524 0, 0, 0);
525 if (!HeapTupleIsValid(tuple))
526 ereport(ERROR,
527 (errcode(ERRCODE_UNDEFINED_OBJECT),
528 errmsg("language \"%s\" does not exist",
529 langname)));
531 objects = lappend_oid(objects, HeapTupleGetOid(tuple));
533 ReleaseSysCache(tuple);
535 break;
536 case ACL_OBJECT_NAMESPACE:
537 foreach(cell, objnames)
539 char *nspname = strVal(lfirst(cell));
540 HeapTuple tuple;
542 tuple = SearchSysCache(NAMESPACENAME,
543 CStringGetDatum(nspname),
544 0, 0, 0);
545 if (!HeapTupleIsValid(tuple))
546 ereport(ERROR,
547 (errcode(ERRCODE_UNDEFINED_SCHEMA),
548 errmsg("schema \"%s\" does not exist",
549 nspname)));
551 objects = lappend_oid(objects, HeapTupleGetOid(tuple));
553 ReleaseSysCache(tuple);
555 break;
556 case ACL_OBJECT_TABLESPACE:
557 foreach(cell, objnames)
559 char *spcname = strVal(lfirst(cell));
560 ScanKeyData entry[1];
561 HeapScanDesc scan;
562 HeapTuple tuple;
563 Relation relation;
565 relation = heap_open(TableSpaceRelationId, AccessShareLock);
567 ScanKeyInit(&entry[0],
568 Anum_pg_tablespace_spcname,
569 BTEqualStrategyNumber, F_NAMEEQ,
570 CStringGetDatum(spcname));
572 scan = heap_beginscan(relation, SnapshotNow, 1, entry);
573 tuple = heap_getnext(scan, ForwardScanDirection);
574 if (!HeapTupleIsValid(tuple))
575 ereport(ERROR,
576 (errcode(ERRCODE_UNDEFINED_OBJECT),
577 errmsg("tablespace \"%s\" does not exist", spcname)));
579 objects = lappend_oid(objects, HeapTupleGetOid(tuple));
581 heap_endscan(scan);
583 heap_close(relation, AccessShareLock);
585 break;
586 case ACL_OBJECT_FDW:
587 foreach(cell, objnames)
589 char *fdwname = strVal(lfirst(cell));
590 Oid fdwid = GetForeignDataWrapperOidByName(fdwname, false);
592 objects = lappend_oid(objects, fdwid);
594 break;
595 case ACL_OBJECT_FOREIGN_SERVER:
596 foreach(cell, objnames)
598 char *srvname = strVal(lfirst(cell));
599 Oid srvid = GetForeignServerOidByName(srvname, false);
601 objects = lappend_oid(objects, srvid);
603 break;
604 default:
605 elog(ERROR, "unrecognized GrantStmt.objtype: %d",
606 (int) objtype);
609 return objects;
613 * expand_col_privileges
615 * OR the specified privilege(s) into per-column array entries for each
616 * specified attribute. The per-column array is indexed starting at
617 * FirstLowInvalidHeapAttributeNumber, up to relation's last attribute.
619 static void
620 expand_col_privileges(List *colnames, Oid table_oid,
621 AclMode this_privileges,
622 AclMode *col_privileges,
623 int num_col_privileges)
625 ListCell *cell;
627 foreach(cell, colnames)
629 char *colname = strVal(lfirst(cell));
630 AttrNumber attnum;
632 attnum = get_attnum(table_oid, colname);
633 if (attnum == InvalidAttrNumber)
634 ereport(ERROR,
635 (errcode(ERRCODE_UNDEFINED_COLUMN),
636 errmsg("column \"%s\" of relation \"%s\" does not exist",
637 colname, get_rel_name(table_oid))));
638 attnum -= FirstLowInvalidHeapAttributeNumber;
639 if (attnum <= 0 || attnum >= num_col_privileges)
640 elog(ERROR, "column number out of range"); /* safety check */
641 col_privileges[attnum] |= this_privileges;
646 * expand_all_col_privileges
648 * OR the specified privilege(s) into per-column array entries for each valid
649 * attribute of a relation. The per-column array is indexed starting at
650 * FirstLowInvalidHeapAttributeNumber, up to relation's last attribute.
652 static void
653 expand_all_col_privileges(Oid table_oid, Form_pg_class classForm,
654 AclMode this_privileges,
655 AclMode *col_privileges,
656 int num_col_privileges)
658 AttrNumber curr_att;
660 Assert(classForm->relnatts - FirstLowInvalidHeapAttributeNumber < num_col_privileges);
661 for (curr_att = FirstLowInvalidHeapAttributeNumber + 1;
662 curr_att <= classForm->relnatts;
663 curr_att++)
665 HeapTuple attTuple;
666 bool isdropped;
668 if (curr_att == InvalidAttrNumber)
669 continue;
671 /* Skip OID column if it doesn't exist */
672 if (curr_att == ObjectIdAttributeNumber && !classForm->relhasoids)
673 continue;
675 /* Views don't have any system columns at all */
676 if (classForm->relkind == RELKIND_VIEW && curr_att < 0)
677 continue;
679 attTuple = SearchSysCache(ATTNUM,
680 ObjectIdGetDatum(table_oid),
681 Int16GetDatum(curr_att),
682 0, 0);
683 if (!HeapTupleIsValid(attTuple))
684 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
685 curr_att, table_oid);
687 isdropped = ((Form_pg_attribute) GETSTRUCT(attTuple))->attisdropped;
689 ReleaseSysCache(attTuple);
691 /* ignore dropped columns */
692 if (isdropped)
693 continue;
695 col_privileges[curr_att - FirstLowInvalidHeapAttributeNumber] |= this_privileges;
700 * This processes attributes, but expects to be called from
701 * ExecGrant_Relation, not directly from ExecGrantStmt.
703 static void
704 ExecGrant_Attribute(InternalGrant *istmt, Oid relOid, const char *relname,
705 AttrNumber attnum, Oid ownerId, AclMode col_privileges,
706 Relation attRelation, const Acl *old_rel_acl)
708 HeapTuple attr_tuple;
709 Form_pg_attribute pg_attribute_tuple;
710 Acl *old_acl;
711 Acl *new_acl;
712 Acl *merged_acl;
713 Datum aclDatum;
714 bool isNull;
715 Oid grantorId;
716 AclMode avail_goptions;
717 bool need_update;
718 HeapTuple newtuple;
719 Datum values[Natts_pg_attribute];
720 bool nulls[Natts_pg_attribute];
721 bool replaces[Natts_pg_attribute];
722 int noldmembers;
723 int nnewmembers;
724 Oid *oldmembers;
725 Oid *newmembers;
727 attr_tuple = SearchSysCache(ATTNUM,
728 ObjectIdGetDatum(relOid),
729 Int16GetDatum(attnum),
730 0, 0);
731 if (!HeapTupleIsValid(attr_tuple))
732 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
733 attnum, relOid);
734 pg_attribute_tuple = (Form_pg_attribute) GETSTRUCT(attr_tuple);
737 * Get working copy of existing ACL. If there's no ACL, substitute the
738 * proper default.
740 aclDatum = SysCacheGetAttr(ATTNUM, attr_tuple, Anum_pg_attribute_attacl,
741 &isNull);
742 if (isNull)
743 old_acl = acldefault(ACL_OBJECT_COLUMN, ownerId);
744 else
745 old_acl = DatumGetAclPCopy(aclDatum);
748 * In select_best_grantor we should consider existing table-level ACL bits
749 * as well as the per-column ACL. Build a new ACL that is their
750 * concatenation. (This is a bit cheap and dirty compared to merging them
751 * properly with no duplications, but it's all we need here.)
753 merged_acl = aclconcat(old_rel_acl, old_acl);
755 /* Determine ID to do the grant as, and available grant options */
756 select_best_grantor(GetUserId(), col_privileges,
757 merged_acl, ownerId,
758 &grantorId, &avail_goptions);
760 pfree(merged_acl);
763 * Restrict the privileges to what we can actually grant, and emit the
764 * standards-mandated warning and error messages. Note: we don't track
765 * whether the user actually used the ALL PRIVILEGES(columns) syntax for
766 * each column; we just approximate it by whether all the possible
767 * privileges are specified now. Since the all_privs flag only determines
768 * whether a warning is issued, this seems close enough.
770 col_privileges =
771 restrict_and_check_grant(istmt->is_grant, avail_goptions,
772 (col_privileges == ACL_ALL_RIGHTS_COLUMN),
773 col_privileges,
774 relOid, grantorId, ACL_KIND_COLUMN,
775 relname, attnum,
776 NameStr(pg_attribute_tuple->attname));
779 * Generate new ACL.
781 * We need the members of both old and new ACLs so we can correct the
782 * shared dependency information.
784 noldmembers = aclmembers(old_acl, &oldmembers);
786 new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
787 istmt->grant_option,
788 istmt->behavior, istmt->grantees,
789 col_privileges, grantorId,
790 ownerId);
792 nnewmembers = aclmembers(new_acl, &newmembers);
794 /* finished building new ACL value, now insert it */
795 MemSet(values, 0, sizeof(values));
796 MemSet(nulls, false, sizeof(nulls));
797 MemSet(replaces, false, sizeof(replaces));
800 * If the updated ACL is empty, we can set attacl to null, and maybe even
801 * avoid an update of the pg_attribute row. This is worth testing because
802 * we'll come through here multiple times for any relation-level REVOKE,
803 * even if there were never any column GRANTs. Note we are assuming that
804 * the "default" ACL state for columns is empty.
806 if (ACL_NUM(new_acl) > 0)
808 values[Anum_pg_attribute_attacl - 1] = PointerGetDatum(new_acl);
809 need_update = true;
811 else
813 nulls[Anum_pg_attribute_attacl - 1] = true;
814 need_update = !isNull;
816 replaces[Anum_pg_attribute_attacl - 1] = true;
818 if (need_update)
820 newtuple = heap_modify_tuple(attr_tuple, RelationGetDescr(attRelation),
821 values, nulls, replaces);
823 simple_heap_update(attRelation, &newtuple->t_self, newtuple);
825 /* keep the catalog indexes up to date */
826 CatalogUpdateIndexes(attRelation, newtuple);
828 /* Update the shared dependency ACL info */
829 updateAclDependencies(RelationRelationId, relOid, attnum,
830 ownerId, istmt->is_grant,
831 noldmembers, oldmembers,
832 nnewmembers, newmembers);
835 pfree(new_acl);
837 ReleaseSysCache(attr_tuple);
841 * This processes both sequences and non-sequences.
843 static void
844 ExecGrant_Relation(InternalGrant *istmt)
846 Relation relation;
847 Relation attRelation;
848 ListCell *cell;
850 relation = heap_open(RelationRelationId, RowExclusiveLock);
851 attRelation = heap_open(AttributeRelationId, RowExclusiveLock);
853 foreach(cell, istmt->objects)
855 Oid relOid = lfirst_oid(cell);
856 Datum aclDatum;
857 Form_pg_class pg_class_tuple;
858 bool isNull;
859 AclMode this_privileges;
860 AclMode *col_privileges;
861 int num_col_privileges;
862 bool have_col_privileges;
863 Acl *old_acl;
864 Acl *old_rel_acl;
865 Oid ownerId;
866 HeapTuple tuple;
867 ListCell *cell_colprivs;
869 tuple = SearchSysCache(RELOID,
870 ObjectIdGetDatum(relOid),
871 0, 0, 0);
872 if (!HeapTupleIsValid(tuple))
873 elog(ERROR, "cache lookup failed for relation %u", relOid);
874 pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
876 /* Not sensible to grant on an index */
877 if (pg_class_tuple->relkind == RELKIND_INDEX)
878 ereport(ERROR,
879 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
880 errmsg("\"%s\" is an index",
881 NameStr(pg_class_tuple->relname))));
883 /* Composite types aren't tables either */
884 if (pg_class_tuple->relkind == RELKIND_COMPOSITE_TYPE)
885 ereport(ERROR,
886 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
887 errmsg("\"%s\" is a composite type",
888 NameStr(pg_class_tuple->relname))));
890 /* Used GRANT SEQUENCE on a non-sequence? */
891 if (istmt->objtype == ACL_OBJECT_SEQUENCE &&
892 pg_class_tuple->relkind != RELKIND_SEQUENCE)
893 ereport(ERROR,
894 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
895 errmsg("\"%s\" is not a sequence",
896 NameStr(pg_class_tuple->relname))));
898 /* Adjust the default permissions based on whether it is a sequence */
899 if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
901 if (pg_class_tuple->relkind == RELKIND_SEQUENCE)
902 this_privileges = ACL_ALL_RIGHTS_SEQUENCE;
903 else
904 this_privileges = ACL_ALL_RIGHTS_RELATION;
906 else
907 this_privileges = istmt->privileges;
910 * The GRANT TABLE syntax can be used for sequences and non-sequences,
911 * so we have to look at the relkind to determine the supported
912 * permissions. The OR of table and sequence permissions were already
913 * checked.
915 if (istmt->objtype == ACL_OBJECT_RELATION)
917 if (pg_class_tuple->relkind == RELKIND_SEQUENCE)
920 * For backward compatibility, just throw a warning for
921 * invalid sequence permissions when using the non-sequence
922 * GRANT syntax.
924 if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_SEQUENCE))
927 * Mention the object name because the user needs to know
928 * which operations succeeded. This is required because
929 * WARNING allows the command to continue.
931 ereport(WARNING,
932 (errcode(ERRCODE_INVALID_GRANT_OPERATION),
933 errmsg("sequence \"%s\" only supports USAGE, SELECT, and UPDATE privileges",
934 NameStr(pg_class_tuple->relname))));
935 this_privileges &= (AclMode) ACL_ALL_RIGHTS_SEQUENCE;
938 else
940 if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_RELATION))
943 * USAGE is the only permission supported by sequences but
944 * not by non-sequences. Don't mention the object name
945 * because we didn't in the combined TABLE | SEQUENCE
946 * check.
948 ereport(ERROR,
949 (errcode(ERRCODE_INVALID_GRANT_OPERATION),
950 errmsg("invalid privilege type USAGE for table")));
956 * Set up array in which we'll accumulate any column privilege bits
957 * that need modification. The array is indexed such that entry [0]
958 * corresponds to FirstLowInvalidHeapAttributeNumber.
960 num_col_privileges = pg_class_tuple->relnatts - FirstLowInvalidHeapAttributeNumber + 1;
961 col_privileges = (AclMode *) palloc0(num_col_privileges * sizeof(AclMode));
962 have_col_privileges = false;
965 * If we are revoking relation privileges that are also column
966 * privileges, we must implicitly revoke them from each column too,
967 * per SQL spec. (We don't need to implicitly add column privileges
968 * during GRANT because the permissions-checking code always checks
969 * both relation and per-column privileges.)
971 if (!istmt->is_grant &&
972 (this_privileges & ACL_ALL_RIGHTS_COLUMN) != 0)
974 expand_all_col_privileges(relOid, pg_class_tuple,
975 this_privileges & ACL_ALL_RIGHTS_COLUMN,
976 col_privileges,
977 num_col_privileges);
978 have_col_privileges = true;
982 * Get owner ID and working copy of existing ACL. If there's no ACL,
983 * substitute the proper default.
985 ownerId = pg_class_tuple->relowner;
986 aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl,
987 &isNull);
988 if (isNull)
989 old_acl = acldefault(pg_class_tuple->relkind == RELKIND_SEQUENCE ?
990 ACL_OBJECT_SEQUENCE : ACL_OBJECT_RELATION,
991 ownerId);
992 else
993 old_acl = DatumGetAclPCopy(aclDatum);
995 /* Need an extra copy of original rel ACL for column handling */
996 old_rel_acl = aclcopy(old_acl);
999 * Handle relation-level privileges, if any were specified
1001 if (this_privileges != ACL_NO_RIGHTS)
1003 AclMode avail_goptions;
1004 Acl *new_acl;
1005 Oid grantorId;
1006 HeapTuple newtuple;
1007 Datum values[Natts_pg_class];
1008 bool nulls[Natts_pg_class];
1009 bool replaces[Natts_pg_class];
1010 int noldmembers;
1011 int nnewmembers;
1012 Oid *oldmembers;
1013 Oid *newmembers;
1015 /* Determine ID to do the grant as, and available grant options */
1016 select_best_grantor(GetUserId(), this_privileges,
1017 old_acl, ownerId,
1018 &grantorId, &avail_goptions);
1021 * Restrict the privileges to what we can actually grant, and emit
1022 * the standards-mandated warning and error messages.
1024 this_privileges =
1025 restrict_and_check_grant(istmt->is_grant, avail_goptions,
1026 istmt->all_privs, this_privileges,
1027 relOid, grantorId,
1028 pg_class_tuple->relkind == RELKIND_SEQUENCE
1029 ? ACL_KIND_SEQUENCE : ACL_KIND_CLASS,
1030 NameStr(pg_class_tuple->relname),
1031 0, NULL);
1034 * Generate new ACL.
1036 * We need the members of both old and new ACLs so we can correct
1037 * the shared dependency information.
1039 noldmembers = aclmembers(old_acl, &oldmembers);
1041 new_acl = merge_acl_with_grant(old_acl,
1042 istmt->is_grant,
1043 istmt->grant_option,
1044 istmt->behavior,
1045 istmt->grantees,
1046 this_privileges,
1047 grantorId,
1048 ownerId);
1050 nnewmembers = aclmembers(new_acl, &newmembers);
1052 /* finished building new ACL value, now insert it */
1053 MemSet(values, 0, sizeof(values));
1054 MemSet(nulls, false, sizeof(nulls));
1055 MemSet(replaces, false, sizeof(replaces));
1057 replaces[Anum_pg_class_relacl - 1] = true;
1058 values[Anum_pg_class_relacl - 1] = PointerGetDatum(new_acl);
1060 newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation),
1061 values, nulls, replaces);
1063 simple_heap_update(relation, &newtuple->t_self, newtuple);
1065 /* keep the catalog indexes up to date */
1066 CatalogUpdateIndexes(relation, newtuple);
1068 /* Update the shared dependency ACL info */
1069 updateAclDependencies(RelationRelationId, relOid, 0,
1070 ownerId, istmt->is_grant,
1071 noldmembers, oldmembers,
1072 nnewmembers, newmembers);
1074 pfree(new_acl);
1078 * Handle column-level privileges, if any were specified or implied.
1079 * We first expand the user-specified column privileges into the
1080 * array, and then iterate over all nonempty array entries.
1082 foreach(cell_colprivs, istmt->col_privs)
1084 AccessPriv *col_privs = (AccessPriv *) lfirst(cell_colprivs);
1086 if (col_privs->priv_name == NULL)
1087 this_privileges = ACL_ALL_RIGHTS_COLUMN;
1088 else
1089 this_privileges = string_to_privilege(col_privs->priv_name);
1091 if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_COLUMN))
1092 ereport(ERROR,
1093 (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1094 errmsg("invalid privilege type %s for column",
1095 privilege_to_string(this_privileges))));
1097 if (pg_class_tuple->relkind == RELKIND_SEQUENCE &&
1098 this_privileges & ~((AclMode) ACL_SELECT))
1101 * The only column privilege allowed on sequences is SELECT.
1102 * This is a warning not error because we do it that way for
1103 * relation-level privileges.
1105 ereport(WARNING,
1106 (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1107 errmsg("sequence \"%s\" only supports SELECT column privileges",
1108 NameStr(pg_class_tuple->relname))));
1110 this_privileges &= (AclMode) ACL_SELECT;
1113 expand_col_privileges(col_privs->cols, relOid,
1114 this_privileges,
1115 col_privileges,
1116 num_col_privileges);
1117 have_col_privileges = true;
1120 if (have_col_privileges)
1122 AttrNumber i;
1124 for (i = 0; i < num_col_privileges; i++)
1126 if (col_privileges[i] == ACL_NO_RIGHTS)
1127 continue;
1128 ExecGrant_Attribute(istmt,
1129 relOid,
1130 NameStr(pg_class_tuple->relname),
1131 i + FirstLowInvalidHeapAttributeNumber,
1132 ownerId,
1133 col_privileges[i],
1134 attRelation,
1135 old_rel_acl);
1139 pfree(old_rel_acl);
1140 pfree(col_privileges);
1142 ReleaseSysCache(tuple);
1144 /* prevent error when processing duplicate objects */
1145 CommandCounterIncrement();
1148 heap_close(attRelation, RowExclusiveLock);
1149 heap_close(relation, RowExclusiveLock);
1152 static void
1153 ExecGrant_Database(InternalGrant *istmt)
1155 Relation relation;
1156 ListCell *cell;
1158 if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
1159 istmt->privileges = ACL_ALL_RIGHTS_DATABASE;
1161 relation = heap_open(DatabaseRelationId, RowExclusiveLock);
1163 foreach(cell, istmt->objects)
1165 Oid datId = lfirst_oid(cell);
1166 Form_pg_database pg_database_tuple;
1167 Datum aclDatum;
1168 bool isNull;
1169 AclMode avail_goptions;
1170 AclMode this_privileges;
1171 Acl *old_acl;
1172 Acl *new_acl;
1173 Oid grantorId;
1174 Oid ownerId;
1175 HeapTuple newtuple;
1176 Datum values[Natts_pg_database];
1177 bool nulls[Natts_pg_database];
1178 bool replaces[Natts_pg_database];
1179 int noldmembers;
1180 int nnewmembers;
1181 Oid *oldmembers;
1182 Oid *newmembers;
1183 HeapTuple tuple;
1185 tuple = SearchSysCache(DATABASEOID,
1186 ObjectIdGetDatum(datId),
1187 0, 0, 0);
1188 if (!HeapTupleIsValid(tuple))
1189 elog(ERROR, "cache lookup failed for database %u", datId);
1191 pg_database_tuple = (Form_pg_database) GETSTRUCT(tuple);
1194 * Get owner ID and working copy of existing ACL. If there's no ACL,
1195 * substitute the proper default.
1197 ownerId = pg_database_tuple->datdba;
1198 aclDatum = heap_getattr(tuple, Anum_pg_database_datacl,
1199 RelationGetDescr(relation), &isNull);
1200 if (isNull)
1201 old_acl = acldefault(ACL_OBJECT_DATABASE, ownerId);
1202 else
1203 old_acl = DatumGetAclPCopy(aclDatum);
1205 /* Determine ID to do the grant as, and available grant options */
1206 select_best_grantor(GetUserId(), istmt->privileges,
1207 old_acl, ownerId,
1208 &grantorId, &avail_goptions);
1211 * Restrict the privileges to what we can actually grant, and emit the
1212 * standards-mandated warning and error messages.
1214 this_privileges =
1215 restrict_and_check_grant(istmt->is_grant, avail_goptions,
1216 istmt->all_privs, istmt->privileges,
1217 datId, grantorId, ACL_KIND_DATABASE,
1218 NameStr(pg_database_tuple->datname),
1219 0, NULL);
1222 * Generate new ACL.
1224 * We need the members of both old and new ACLs so we can correct the
1225 * shared dependency information.
1227 noldmembers = aclmembers(old_acl, &oldmembers);
1229 new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
1230 istmt->grant_option, istmt->behavior,
1231 istmt->grantees, this_privileges,
1232 grantorId, ownerId);
1234 nnewmembers = aclmembers(new_acl, &newmembers);
1236 /* finished building new ACL value, now insert it */
1237 MemSet(values, 0, sizeof(values));
1238 MemSet(nulls, false, sizeof(nulls));
1239 MemSet(replaces, false, sizeof(replaces));
1241 replaces[Anum_pg_database_datacl - 1] = true;
1242 values[Anum_pg_database_datacl - 1] = PointerGetDatum(new_acl);
1244 newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
1245 nulls, replaces);
1247 simple_heap_update(relation, &newtuple->t_self, newtuple);
1249 /* keep the catalog indexes up to date */
1250 CatalogUpdateIndexes(relation, newtuple);
1252 /* Update the shared dependency ACL info */
1253 updateAclDependencies(DatabaseRelationId, HeapTupleGetOid(tuple), 0,
1254 ownerId, istmt->is_grant,
1255 noldmembers, oldmembers,
1256 nnewmembers, newmembers);
1258 ReleaseSysCache(tuple);
1260 pfree(new_acl);
1262 /* prevent error when processing duplicate objects */
1263 CommandCounterIncrement();
1266 heap_close(relation, RowExclusiveLock);
1269 static void
1270 ExecGrant_Fdw(InternalGrant *istmt)
1272 Relation relation;
1273 ListCell *cell;
1275 if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
1276 istmt->privileges = ACL_ALL_RIGHTS_FDW;
1278 relation = heap_open(ForeignDataWrapperRelationId, RowExclusiveLock);
1280 foreach(cell, istmt->objects)
1282 Oid fdwid = lfirst_oid(cell);
1283 Form_pg_foreign_data_wrapper pg_fdw_tuple;
1284 Datum aclDatum;
1285 bool isNull;
1286 AclMode avail_goptions;
1287 AclMode this_privileges;
1288 Acl *old_acl;
1289 Acl *new_acl;
1290 Oid grantorId;
1291 Oid ownerId;
1292 HeapTuple tuple;
1293 HeapTuple newtuple;
1294 Datum values[Natts_pg_foreign_data_wrapper];
1295 bool nulls[Natts_pg_foreign_data_wrapper];
1296 bool replaces[Natts_pg_foreign_data_wrapper];
1297 int noldmembers;
1298 int nnewmembers;
1299 Oid *oldmembers;
1300 Oid *newmembers;
1302 tuple = SearchSysCache(FOREIGNDATAWRAPPEROID,
1303 ObjectIdGetDatum(fdwid),
1304 0, 0, 0);
1305 if (!HeapTupleIsValid(tuple))
1306 elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwid);
1308 pg_fdw_tuple = (Form_pg_foreign_data_wrapper) GETSTRUCT(tuple);
1311 * Get owner ID and working copy of existing ACL. If there's no ACL,
1312 * substitute the proper default.
1314 ownerId = pg_fdw_tuple->fdwowner;
1315 aclDatum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID, tuple,
1316 Anum_pg_foreign_data_wrapper_fdwacl,
1317 &isNull);
1318 if (isNull)
1319 old_acl = acldefault(ACL_OBJECT_FDW, ownerId);
1320 else
1321 old_acl = DatumGetAclPCopy(aclDatum);
1323 /* Determine ID to do the grant as, and available grant options */
1324 select_best_grantor(GetUserId(), istmt->privileges,
1325 old_acl, ownerId,
1326 &grantorId, &avail_goptions);
1329 * Restrict the privileges to what we can actually grant, and emit the
1330 * standards-mandated warning and error messages.
1332 this_privileges =
1333 restrict_and_check_grant(istmt->is_grant, avail_goptions,
1334 istmt->all_privs, istmt->privileges,
1335 fdwid, grantorId, ACL_KIND_FDW,
1336 NameStr(pg_fdw_tuple->fdwname),
1337 0, NULL);
1340 * Generate new ACL.
1342 * We need the members of both old and new ACLs so we can correct the
1343 * shared dependency information.
1345 noldmembers = aclmembers(old_acl, &oldmembers);
1347 new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
1348 istmt->grant_option, istmt->behavior,
1349 istmt->grantees, this_privileges,
1350 grantorId, ownerId);
1352 nnewmembers = aclmembers(new_acl, &newmembers);
1354 /* finished building new ACL value, now insert it */
1355 MemSet(values, 0, sizeof(values));
1356 MemSet(nulls, false, sizeof(nulls));
1357 MemSet(replaces, false, sizeof(replaces));
1359 replaces[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
1360 values[Anum_pg_foreign_data_wrapper_fdwacl - 1] = PointerGetDatum(new_acl);
1362 newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
1363 nulls, replaces);
1365 simple_heap_update(relation, &newtuple->t_self, newtuple);
1367 /* keep the catalog indexes up to date */
1368 CatalogUpdateIndexes(relation, newtuple);
1370 /* Update the shared dependency ACL info */
1371 updateAclDependencies(ForeignDataWrapperRelationId,
1372 HeapTupleGetOid(tuple), 0,
1373 ownerId, istmt->is_grant,
1374 noldmembers, oldmembers,
1375 nnewmembers, newmembers);
1377 ReleaseSysCache(tuple);
1379 pfree(new_acl);
1381 /* prevent error when processing duplicate objects */
1382 CommandCounterIncrement();
1385 heap_close(relation, RowExclusiveLock);
1388 static void
1389 ExecGrant_ForeignServer(InternalGrant *istmt)
1391 Relation relation;
1392 ListCell *cell;
1394 if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
1395 istmt->privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
1397 relation = heap_open(ForeignServerRelationId, RowExclusiveLock);
1399 foreach(cell, istmt->objects)
1401 Oid srvid = lfirst_oid(cell);
1402 Form_pg_foreign_server pg_server_tuple;
1403 Datum aclDatum;
1404 bool isNull;
1405 AclMode avail_goptions;
1406 AclMode this_privileges;
1407 Acl *old_acl;
1408 Acl *new_acl;
1409 Oid grantorId;
1410 Oid ownerId;
1411 HeapTuple tuple;
1412 HeapTuple newtuple;
1413 Datum values[Natts_pg_foreign_server];
1414 bool nulls[Natts_pg_foreign_server];
1415 bool replaces[Natts_pg_foreign_server];
1416 int noldmembers;
1417 int nnewmembers;
1418 Oid *oldmembers;
1419 Oid *newmembers;
1421 tuple = SearchSysCache(FOREIGNSERVEROID,
1422 ObjectIdGetDatum(srvid),
1423 0, 0, 0);
1424 if (!HeapTupleIsValid(tuple))
1425 elog(ERROR, "cache lookup failed for foreign server %u", srvid);
1427 pg_server_tuple = (Form_pg_foreign_server) GETSTRUCT(tuple);
1430 * Get owner ID and working copy of existing ACL. If there's no ACL,
1431 * substitute the proper default.
1433 ownerId = pg_server_tuple->srvowner;
1434 aclDatum = SysCacheGetAttr(FOREIGNSERVEROID, tuple,
1435 Anum_pg_foreign_server_srvacl,
1436 &isNull);
1437 if (isNull)
1438 old_acl = acldefault(ACL_OBJECT_FOREIGN_SERVER, ownerId);
1439 else
1440 old_acl = DatumGetAclPCopy(aclDatum);
1442 /* Determine ID to do the grant as, and available grant options */
1443 select_best_grantor(GetUserId(), istmt->privileges,
1444 old_acl, ownerId,
1445 &grantorId, &avail_goptions);
1448 * Restrict the privileges to what we can actually grant, and emit the
1449 * standards-mandated warning and error messages.
1451 this_privileges =
1452 restrict_and_check_grant(istmt->is_grant, avail_goptions,
1453 istmt->all_privs, istmt->privileges,
1454 srvid, grantorId, ACL_KIND_FOREIGN_SERVER,
1455 NameStr(pg_server_tuple->srvname),
1456 0, NULL);
1459 * Generate new ACL.
1461 * We need the members of both old and new ACLs so we can correct the
1462 * shared dependency information.
1464 noldmembers = aclmembers(old_acl, &oldmembers);
1466 new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
1467 istmt->grant_option, istmt->behavior,
1468 istmt->grantees, this_privileges,
1469 grantorId, ownerId);
1471 nnewmembers = aclmembers(new_acl, &newmembers);
1473 /* finished building new ACL value, now insert it */
1474 MemSet(values, 0, sizeof(values));
1475 MemSet(nulls, false, sizeof(nulls));
1476 MemSet(replaces, false, sizeof(replaces));
1478 replaces[Anum_pg_foreign_server_srvacl - 1] = true;
1479 values[Anum_pg_foreign_server_srvacl - 1] = PointerGetDatum(new_acl);
1481 newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
1482 nulls, replaces);
1484 simple_heap_update(relation, &newtuple->t_self, newtuple);
1486 /* keep the catalog indexes up to date */
1487 CatalogUpdateIndexes(relation, newtuple);
1489 /* Update the shared dependency ACL info */
1490 updateAclDependencies(ForeignServerRelationId,
1491 HeapTupleGetOid(tuple), 0,
1492 ownerId, istmt->is_grant,
1493 noldmembers, oldmembers,
1494 nnewmembers, newmembers);
1496 ReleaseSysCache(tuple);
1498 pfree(new_acl);
1500 /* prevent error when processing duplicate objects */
1501 CommandCounterIncrement();
1504 heap_close(relation, RowExclusiveLock);
1507 static void
1508 ExecGrant_Function(InternalGrant *istmt)
1510 Relation relation;
1511 ListCell *cell;
1513 if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
1514 istmt->privileges = ACL_ALL_RIGHTS_FUNCTION;
1516 relation = heap_open(ProcedureRelationId, RowExclusiveLock);
1518 foreach(cell, istmt->objects)
1520 Oid funcId = lfirst_oid(cell);
1521 Form_pg_proc pg_proc_tuple;
1522 Datum aclDatum;
1523 bool isNull;
1524 AclMode avail_goptions;
1525 AclMode this_privileges;
1526 Acl *old_acl;
1527 Acl *new_acl;
1528 Oid grantorId;
1529 Oid ownerId;
1530 HeapTuple tuple;
1531 HeapTuple newtuple;
1532 Datum values[Natts_pg_proc];
1533 bool nulls[Natts_pg_proc];
1534 bool replaces[Natts_pg_proc];
1535 int noldmembers;
1536 int nnewmembers;
1537 Oid *oldmembers;
1538 Oid *newmembers;
1540 tuple = SearchSysCache(PROCOID,
1541 ObjectIdGetDatum(funcId),
1542 0, 0, 0);
1543 if (!HeapTupleIsValid(tuple))
1544 elog(ERROR, "cache lookup failed for function %u", funcId);
1546 pg_proc_tuple = (Form_pg_proc) GETSTRUCT(tuple);
1549 * Get owner ID and working copy of existing ACL. If there's no ACL,
1550 * substitute the proper default.
1552 ownerId = pg_proc_tuple->proowner;
1553 aclDatum = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_proacl,
1554 &isNull);
1555 if (isNull)
1556 old_acl = acldefault(ACL_OBJECT_FUNCTION, ownerId);
1557 else
1558 old_acl = DatumGetAclPCopy(aclDatum);
1560 /* Determine ID to do the grant as, and available grant options */
1561 select_best_grantor(GetUserId(), istmt->privileges,
1562 old_acl, ownerId,
1563 &grantorId, &avail_goptions);
1566 * Restrict the privileges to what we can actually grant, and emit the
1567 * standards-mandated warning and error messages.
1569 this_privileges =
1570 restrict_and_check_grant(istmt->is_grant, avail_goptions,
1571 istmt->all_privs, istmt->privileges,
1572 funcId, grantorId, ACL_KIND_PROC,
1573 NameStr(pg_proc_tuple->proname),
1574 0, NULL);
1577 * Generate new ACL.
1579 * We need the members of both old and new ACLs so we can correct the
1580 * shared dependency information.
1582 noldmembers = aclmembers(old_acl, &oldmembers);
1584 new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
1585 istmt->grant_option, istmt->behavior,
1586 istmt->grantees, this_privileges,
1587 grantorId, ownerId);
1589 nnewmembers = aclmembers(new_acl, &newmembers);
1591 /* finished building new ACL value, now insert it */
1592 MemSet(values, 0, sizeof(values));
1593 MemSet(nulls, false, sizeof(nulls));
1594 MemSet(replaces, false, sizeof(replaces));
1596 replaces[Anum_pg_proc_proacl - 1] = true;
1597 values[Anum_pg_proc_proacl - 1] = PointerGetDatum(new_acl);
1599 newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
1600 nulls, replaces);
1602 simple_heap_update(relation, &newtuple->t_self, newtuple);
1604 /* keep the catalog indexes up to date */
1605 CatalogUpdateIndexes(relation, newtuple);
1607 /* Update the shared dependency ACL info */
1608 updateAclDependencies(ProcedureRelationId, funcId, 0,
1609 ownerId, istmt->is_grant,
1610 noldmembers, oldmembers,
1611 nnewmembers, newmembers);
1613 ReleaseSysCache(tuple);
1615 pfree(new_acl);
1617 /* prevent error when processing duplicate objects */
1618 CommandCounterIncrement();
1621 heap_close(relation, RowExclusiveLock);
1624 static void
1625 ExecGrant_Language(InternalGrant *istmt)
1627 Relation relation;
1628 ListCell *cell;
1630 if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
1631 istmt->privileges = ACL_ALL_RIGHTS_LANGUAGE;
1633 relation = heap_open(LanguageRelationId, RowExclusiveLock);
1635 foreach(cell, istmt->objects)
1637 Oid langId = lfirst_oid(cell);
1638 Form_pg_language pg_language_tuple;
1639 Datum aclDatum;
1640 bool isNull;
1641 AclMode avail_goptions;
1642 AclMode this_privileges;
1643 Acl *old_acl;
1644 Acl *new_acl;
1645 Oid grantorId;
1646 Oid ownerId;
1647 HeapTuple tuple;
1648 HeapTuple newtuple;
1649 Datum values[Natts_pg_language];
1650 bool nulls[Natts_pg_language];
1651 bool replaces[Natts_pg_language];
1652 int noldmembers;
1653 int nnewmembers;
1654 Oid *oldmembers;
1655 Oid *newmembers;
1657 tuple = SearchSysCache(LANGOID,
1658 ObjectIdGetDatum(langId),
1659 0, 0, 0);
1660 if (!HeapTupleIsValid(tuple))
1661 elog(ERROR, "cache lookup failed for language %u", langId);
1663 pg_language_tuple = (Form_pg_language) GETSTRUCT(tuple);
1665 if (!pg_language_tuple->lanpltrusted)
1666 ereport(ERROR,
1667 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1668 errmsg("language \"%s\" is not trusted",
1669 NameStr(pg_language_tuple->lanname)),
1670 errhint("Only superusers can use untrusted languages.")));
1673 * Get owner ID and working copy of existing ACL. If there's no ACL,
1674 * substitute the proper default.
1676 ownerId = pg_language_tuple->lanowner;
1677 aclDatum = SysCacheGetAttr(LANGNAME, tuple, Anum_pg_language_lanacl,
1678 &isNull);
1679 if (isNull)
1680 old_acl = acldefault(ACL_OBJECT_LANGUAGE, ownerId);
1681 else
1682 old_acl = DatumGetAclPCopy(aclDatum);
1684 /* Determine ID to do the grant as, and available grant options */
1685 select_best_grantor(GetUserId(), istmt->privileges,
1686 old_acl, ownerId,
1687 &grantorId, &avail_goptions);
1690 * Restrict the privileges to what we can actually grant, and emit the
1691 * standards-mandated warning and error messages.
1693 this_privileges =
1694 restrict_and_check_grant(istmt->is_grant, avail_goptions,
1695 istmt->all_privs, istmt->privileges,
1696 langId, grantorId, ACL_KIND_LANGUAGE,
1697 NameStr(pg_language_tuple->lanname),
1698 0, NULL);
1701 * Generate new ACL.
1703 * We need the members of both old and new ACLs so we can correct the
1704 * shared dependency information.
1706 noldmembers = aclmembers(old_acl, &oldmembers);
1708 new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
1709 istmt->grant_option, istmt->behavior,
1710 istmt->grantees, this_privileges,
1711 grantorId, ownerId);
1713 nnewmembers = aclmembers(new_acl, &newmembers);
1715 /* finished building new ACL value, now insert it */
1716 MemSet(values, 0, sizeof(values));
1717 MemSet(nulls, false, sizeof(nulls));
1718 MemSet(replaces, false, sizeof(replaces));
1720 replaces[Anum_pg_language_lanacl - 1] = true;
1721 values[Anum_pg_language_lanacl - 1] = PointerGetDatum(new_acl);
1723 newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
1724 nulls, replaces);
1726 simple_heap_update(relation, &newtuple->t_self, newtuple);
1728 /* keep the catalog indexes up to date */
1729 CatalogUpdateIndexes(relation, newtuple);
1731 /* Update the shared dependency ACL info */
1732 updateAclDependencies(LanguageRelationId, HeapTupleGetOid(tuple), 0,
1733 ownerId, istmt->is_grant,
1734 noldmembers, oldmembers,
1735 nnewmembers, newmembers);
1737 ReleaseSysCache(tuple);
1739 pfree(new_acl);
1741 /* prevent error when processing duplicate objects */
1742 CommandCounterIncrement();
1745 heap_close(relation, RowExclusiveLock);
1748 static void
1749 ExecGrant_Namespace(InternalGrant *istmt)
1751 Relation relation;
1752 ListCell *cell;
1754 if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
1755 istmt->privileges = ACL_ALL_RIGHTS_NAMESPACE;
1757 relation = heap_open(NamespaceRelationId, RowExclusiveLock);
1759 foreach(cell, istmt->objects)
1761 Oid nspid = lfirst_oid(cell);
1762 Form_pg_namespace pg_namespace_tuple;
1763 Datum aclDatum;
1764 bool isNull;
1765 AclMode avail_goptions;
1766 AclMode this_privileges;
1767 Acl *old_acl;
1768 Acl *new_acl;
1769 Oid grantorId;
1770 Oid ownerId;
1771 HeapTuple tuple;
1772 HeapTuple newtuple;
1773 Datum values[Natts_pg_namespace];
1774 bool nulls[Natts_pg_namespace];
1775 bool replaces[Natts_pg_namespace];
1776 int noldmembers;
1777 int nnewmembers;
1778 Oid *oldmembers;
1779 Oid *newmembers;
1781 tuple = SearchSysCache(NAMESPACEOID,
1782 ObjectIdGetDatum(nspid),
1783 0, 0, 0);
1784 if (!HeapTupleIsValid(tuple))
1785 elog(ERROR, "cache lookup failed for namespace %u", nspid);
1787 pg_namespace_tuple = (Form_pg_namespace) GETSTRUCT(tuple);
1790 * Get owner ID and working copy of existing ACL. If there's no ACL,
1791 * substitute the proper default.
1793 ownerId = pg_namespace_tuple->nspowner;
1794 aclDatum = SysCacheGetAttr(NAMESPACENAME, tuple,
1795 Anum_pg_namespace_nspacl,
1796 &isNull);
1797 if (isNull)
1798 old_acl = acldefault(ACL_OBJECT_NAMESPACE, ownerId);
1799 else
1800 old_acl = DatumGetAclPCopy(aclDatum);
1802 /* Determine ID to do the grant as, and available grant options */
1803 select_best_grantor(GetUserId(), istmt->privileges,
1804 old_acl, ownerId,
1805 &grantorId, &avail_goptions);
1808 * Restrict the privileges to what we can actually grant, and emit the
1809 * standards-mandated warning and error messages.
1811 this_privileges =
1812 restrict_and_check_grant(istmt->is_grant, avail_goptions,
1813 istmt->all_privs, istmt->privileges,
1814 nspid, grantorId, ACL_KIND_NAMESPACE,
1815 NameStr(pg_namespace_tuple->nspname),
1816 0, NULL);
1819 * Generate new ACL.
1821 * We need the members of both old and new ACLs so we can correct the
1822 * shared dependency information.
1824 noldmembers = aclmembers(old_acl, &oldmembers);
1826 new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
1827 istmt->grant_option, istmt->behavior,
1828 istmt->grantees, this_privileges,
1829 grantorId, ownerId);
1831 nnewmembers = aclmembers(new_acl, &newmembers);
1833 /* finished building new ACL value, now insert it */
1834 MemSet(values, 0, sizeof(values));
1835 MemSet(nulls, false, sizeof(nulls));
1836 MemSet(replaces, false, sizeof(replaces));
1838 replaces[Anum_pg_namespace_nspacl - 1] = true;
1839 values[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(new_acl);
1841 newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
1842 nulls, replaces);
1844 simple_heap_update(relation, &newtuple->t_self, newtuple);
1846 /* keep the catalog indexes up to date */
1847 CatalogUpdateIndexes(relation, newtuple);
1849 /* Update the shared dependency ACL info */
1850 updateAclDependencies(NamespaceRelationId, HeapTupleGetOid(tuple), 0,
1851 ownerId, istmt->is_grant,
1852 noldmembers, oldmembers,
1853 nnewmembers, newmembers);
1855 ReleaseSysCache(tuple);
1857 pfree(new_acl);
1859 /* prevent error when processing duplicate objects */
1860 CommandCounterIncrement();
1863 heap_close(relation, RowExclusiveLock);
1866 static void
1867 ExecGrant_Tablespace(InternalGrant *istmt)
1869 Relation relation;
1870 ListCell *cell;
1872 if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
1873 istmt->privileges = ACL_ALL_RIGHTS_TABLESPACE;
1875 relation = heap_open(TableSpaceRelationId, RowExclusiveLock);
1877 foreach(cell, istmt->objects)
1879 Oid tblId = lfirst_oid(cell);
1880 Form_pg_tablespace pg_tablespace_tuple;
1881 Datum aclDatum;
1882 bool isNull;
1883 AclMode avail_goptions;
1884 AclMode this_privileges;
1885 Acl *old_acl;
1886 Acl *new_acl;
1887 Oid grantorId;
1888 Oid ownerId;
1889 HeapTuple newtuple;
1890 Datum values[Natts_pg_tablespace];
1891 bool nulls[Natts_pg_tablespace];
1892 bool replaces[Natts_pg_tablespace];
1893 int noldmembers;
1894 int nnewmembers;
1895 Oid *oldmembers;
1896 Oid *newmembers;
1897 ScanKeyData entry[1];
1898 SysScanDesc scan;
1899 HeapTuple tuple;
1901 /* There's no syscache for pg_tablespace, so must look the hard way */
1902 ScanKeyInit(&entry[0],
1903 ObjectIdAttributeNumber,
1904 BTEqualStrategyNumber, F_OIDEQ,
1905 ObjectIdGetDatum(tblId));
1906 scan = systable_beginscan(relation, TablespaceOidIndexId, true,
1907 SnapshotNow, 1, entry);
1908 tuple = systable_getnext(scan);
1909 if (!HeapTupleIsValid(tuple))
1910 elog(ERROR, "cache lookup failed for tablespace %u", tblId);
1912 pg_tablespace_tuple = (Form_pg_tablespace) GETSTRUCT(tuple);
1915 * Get owner ID and working copy of existing ACL. If there's no ACL,
1916 * substitute the proper default.
1918 ownerId = pg_tablespace_tuple->spcowner;
1919 aclDatum = heap_getattr(tuple, Anum_pg_tablespace_spcacl,
1920 RelationGetDescr(relation), &isNull);
1921 if (isNull)
1922 old_acl = acldefault(ACL_OBJECT_TABLESPACE, ownerId);
1923 else
1924 old_acl = DatumGetAclPCopy(aclDatum);
1926 /* Determine ID to do the grant as, and available grant options */
1927 select_best_grantor(GetUserId(), istmt->privileges,
1928 old_acl, ownerId,
1929 &grantorId, &avail_goptions);
1932 * Restrict the privileges to what we can actually grant, and emit the
1933 * standards-mandated warning and error messages.
1935 this_privileges =
1936 restrict_and_check_grant(istmt->is_grant, avail_goptions,
1937 istmt->all_privs, istmt->privileges,
1938 tblId, grantorId, ACL_KIND_TABLESPACE,
1939 NameStr(pg_tablespace_tuple->spcname),
1940 0, NULL);
1943 * Generate new ACL.
1945 * We need the members of both old and new ACLs so we can correct the
1946 * shared dependency information.
1948 noldmembers = aclmembers(old_acl, &oldmembers);
1950 new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
1951 istmt->grant_option, istmt->behavior,
1952 istmt->grantees, this_privileges,
1953 grantorId, ownerId);
1955 nnewmembers = aclmembers(new_acl, &newmembers);
1957 /* finished building new ACL value, now insert it */
1958 MemSet(values, 0, sizeof(values));
1959 MemSet(nulls, false, sizeof(nulls));
1960 MemSet(replaces, false, sizeof(replaces));
1962 replaces[Anum_pg_tablespace_spcacl - 1] = true;
1963 values[Anum_pg_tablespace_spcacl - 1] = PointerGetDatum(new_acl);
1965 newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
1966 nulls, replaces);
1968 simple_heap_update(relation, &newtuple->t_self, newtuple);
1970 /* keep the catalog indexes up to date */
1971 CatalogUpdateIndexes(relation, newtuple);
1973 /* Update the shared dependency ACL info */
1974 updateAclDependencies(TableSpaceRelationId, tblId, 0,
1975 ownerId, istmt->is_grant,
1976 noldmembers, oldmembers,
1977 nnewmembers, newmembers);
1979 systable_endscan(scan);
1981 pfree(new_acl);
1983 /* prevent error when processing duplicate objects */
1984 CommandCounterIncrement();
1987 heap_close(relation, RowExclusiveLock);
1991 static AclMode
1992 string_to_privilege(const char *privname)
1994 if (strcmp(privname, "insert") == 0)
1995 return ACL_INSERT;
1996 if (strcmp(privname, "select") == 0)
1997 return ACL_SELECT;
1998 if (strcmp(privname, "update") == 0)
1999 return ACL_UPDATE;
2000 if (strcmp(privname, "delete") == 0)
2001 return ACL_DELETE;
2002 if (strcmp(privname, "truncate") == 0)
2003 return ACL_TRUNCATE;
2004 if (strcmp(privname, "references") == 0)
2005 return ACL_REFERENCES;
2006 if (strcmp(privname, "trigger") == 0)
2007 return ACL_TRIGGER;
2008 if (strcmp(privname, "execute") == 0)
2009 return ACL_EXECUTE;
2010 if (strcmp(privname, "usage") == 0)
2011 return ACL_USAGE;
2012 if (strcmp(privname, "create") == 0)
2013 return ACL_CREATE;
2014 if (strcmp(privname, "temporary") == 0)
2015 return ACL_CREATE_TEMP;
2016 if (strcmp(privname, "temp") == 0)
2017 return ACL_CREATE_TEMP;
2018 if (strcmp(privname, "connect") == 0)
2019 return ACL_CONNECT;
2020 if (strcmp(privname, "rule") == 0)
2021 return 0; /* ignore old RULE privileges */
2022 ereport(ERROR,
2023 (errcode(ERRCODE_SYNTAX_ERROR),
2024 errmsg("unrecognized privilege type \"%s\"", privname)));
2025 return 0; /* appease compiler */
2028 static const char *
2029 privilege_to_string(AclMode privilege)
2031 switch (privilege)
2033 case ACL_INSERT:
2034 return "INSERT";
2035 case ACL_SELECT:
2036 return "SELECT";
2037 case ACL_UPDATE:
2038 return "UPDATE";
2039 case ACL_DELETE:
2040 return "DELETE";
2041 case ACL_TRUNCATE:
2042 return "TRUNCATE";
2043 case ACL_REFERENCES:
2044 return "REFERENCES";
2045 case ACL_TRIGGER:
2046 return "TRIGGER";
2047 case ACL_EXECUTE:
2048 return "EXECUTE";
2049 case ACL_USAGE:
2050 return "USAGE";
2051 case ACL_CREATE:
2052 return "CREATE";
2053 case ACL_CREATE_TEMP:
2054 return "TEMP";
2055 case ACL_CONNECT:
2056 return "CONNECT";
2057 default:
2058 elog(ERROR, "unrecognized privilege: %d", (int) privilege);
2060 return NULL; /* appease compiler */
2064 * Standardized reporting of aclcheck permissions failures.
2066 * Note: we do not double-quote the %s's below, because many callers
2067 * supply strings that might be already quoted.
2070 static const char *const no_priv_msg[MAX_ACL_KIND] =
2072 /* ACL_KIND_COLUMN */
2073 gettext_noop("permission denied for column %s"),
2074 /* ACL_KIND_CLASS */
2075 gettext_noop("permission denied for relation %s"),
2076 /* ACL_KIND_SEQUENCE */
2077 gettext_noop("permission denied for sequence %s"),
2078 /* ACL_KIND_DATABASE */
2079 gettext_noop("permission denied for database %s"),
2080 /* ACL_KIND_PROC */
2081 gettext_noop("permission denied for function %s"),
2082 /* ACL_KIND_OPER */
2083 gettext_noop("permission denied for operator %s"),
2084 /* ACL_KIND_TYPE */
2085 gettext_noop("permission denied for type %s"),
2086 /* ACL_KIND_LANGUAGE */
2087 gettext_noop("permission denied for language %s"),
2088 /* ACL_KIND_NAMESPACE */
2089 gettext_noop("permission denied for schema %s"),
2090 /* ACL_KIND_OPCLASS */
2091 gettext_noop("permission denied for operator class %s"),
2092 /* ACL_KIND_OPFAMILY */
2093 gettext_noop("permission denied for operator family %s"),
2094 /* ACL_KIND_CONVERSION */
2095 gettext_noop("permission denied for conversion %s"),
2096 /* ACL_KIND_TABLESPACE */
2097 gettext_noop("permission denied for tablespace %s"),
2098 /* ACL_KIND_TSDICTIONARY */
2099 gettext_noop("permission denied for text search dictionary %s"),
2100 /* ACL_KIND_TSCONFIGURATION */
2101 gettext_noop("permission denied for text search configuration %s"),
2102 /* ACL_KIND_FDW */
2103 gettext_noop("permission denied for foreign-data wrapper %s"),
2104 /* ACL_KIND_FOREIGN_SERVER */
2105 gettext_noop("permission denied for foreign server %s")
2108 static const char *const not_owner_msg[MAX_ACL_KIND] =
2110 /* ACL_KIND_COLUMN */
2111 gettext_noop("must be owner of relation %s"),
2112 /* ACL_KIND_CLASS */
2113 gettext_noop("must be owner of relation %s"),
2114 /* ACL_KIND_SEQUENCE */
2115 gettext_noop("must be owner of sequence %s"),
2116 /* ACL_KIND_DATABASE */
2117 gettext_noop("must be owner of database %s"),
2118 /* ACL_KIND_PROC */
2119 gettext_noop("must be owner of function %s"),
2120 /* ACL_KIND_OPER */
2121 gettext_noop("must be owner of operator %s"),
2122 /* ACL_KIND_TYPE */
2123 gettext_noop("must be owner of type %s"),
2124 /* ACL_KIND_LANGUAGE */
2125 gettext_noop("must be owner of language %s"),
2126 /* ACL_KIND_NAMESPACE */
2127 gettext_noop("must be owner of schema %s"),
2128 /* ACL_KIND_OPCLASS */
2129 gettext_noop("must be owner of operator class %s"),
2130 /* ACL_KIND_OPFAMILY */
2131 gettext_noop("must be owner of operator family %s"),
2132 /* ACL_KIND_CONVERSION */
2133 gettext_noop("must be owner of conversion %s"),
2134 /* ACL_KIND_TABLESPACE */
2135 gettext_noop("must be owner of tablespace %s"),
2136 /* ACL_KIND_TSDICTIONARY */
2137 gettext_noop("must be owner of text search dictionary %s"),
2138 /* ACL_KIND_TSCONFIGURATION */
2139 gettext_noop("must be owner of text search configuration %s"),
2140 /* ACL_KIND_FDW */
2141 gettext_noop("must be owner of foreign-data wrapper %s"),
2142 /* ACL_KIND_FOREIGN_SERVER */
2143 gettext_noop("must be owner of foreign server %s")
2147 void
2148 aclcheck_error(AclResult aclerr, AclObjectKind objectkind,
2149 const char *objectname)
2151 switch (aclerr)
2153 case ACLCHECK_OK:
2154 /* no error, so return to caller */
2155 break;
2156 case ACLCHECK_NO_PRIV:
2157 ereport(ERROR,
2158 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2159 errmsg(no_priv_msg[objectkind], objectname)));
2160 break;
2161 case ACLCHECK_NOT_OWNER:
2162 ereport(ERROR,
2163 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2164 errmsg(not_owner_msg[objectkind], objectname)));
2165 break;
2166 default:
2167 elog(ERROR, "unrecognized AclResult: %d", (int) aclerr);
2168 break;
2173 void
2174 aclcheck_error_col(AclResult aclerr, AclObjectKind objectkind,
2175 const char *objectname, const char *colname)
2177 switch (aclerr)
2179 case ACLCHECK_OK:
2180 /* no error, so return to caller */
2181 break;
2182 case ACLCHECK_NO_PRIV:
2183 ereport(ERROR,
2184 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2185 errmsg("permission denied for column %s of relation %s",
2186 colname, objectname)));
2187 break;
2188 case ACLCHECK_NOT_OWNER:
2189 /* relation msg is OK since columns don't have separate owners */
2190 ereport(ERROR,
2191 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2192 errmsg(not_owner_msg[objectkind], objectname)));
2193 break;
2194 default:
2195 elog(ERROR, "unrecognized AclResult: %d", (int) aclerr);
2196 break;
2201 /* Check if given user has rolcatupdate privilege according to pg_authid */
2202 static bool
2203 has_rolcatupdate(Oid roleid)
2205 bool rolcatupdate;
2206 HeapTuple tuple;
2208 tuple = SearchSysCache(AUTHOID,
2209 ObjectIdGetDatum(roleid),
2210 0, 0, 0);
2211 if (!HeapTupleIsValid(tuple))
2212 ereport(ERROR,
2213 (errcode(ERRCODE_UNDEFINED_OBJECT),
2214 errmsg("role with OID %u does not exist", roleid)));
2216 rolcatupdate = ((Form_pg_authid) GETSTRUCT(tuple))->rolcatupdate;
2218 ReleaseSysCache(tuple);
2220 return rolcatupdate;
2224 * Relay for the various pg_*_mask routines depending on object kind
2226 static AclMode
2227 pg_aclmask(AclObjectKind objkind, Oid table_oid, AttrNumber attnum, Oid roleid,
2228 AclMode mask, AclMaskHow how)
2230 switch (objkind)
2232 case ACL_KIND_COLUMN:
2233 return
2234 pg_class_aclmask(table_oid, roleid, mask, how) |
2235 pg_attribute_aclmask(table_oid, attnum, roleid, mask, how);
2236 case ACL_KIND_CLASS:
2237 case ACL_KIND_SEQUENCE:
2238 return pg_class_aclmask(table_oid, roleid, mask, how);
2239 case ACL_KIND_DATABASE:
2240 return pg_database_aclmask(table_oid, roleid, mask, how);
2241 case ACL_KIND_PROC:
2242 return pg_proc_aclmask(table_oid, roleid, mask, how);
2243 case ACL_KIND_LANGUAGE:
2244 return pg_language_aclmask(table_oid, roleid, mask, how);
2245 case ACL_KIND_NAMESPACE:
2246 return pg_namespace_aclmask(table_oid, roleid, mask, how);
2247 case ACL_KIND_TABLESPACE:
2248 return pg_tablespace_aclmask(table_oid, roleid, mask, how);
2249 case ACL_KIND_FDW:
2250 return pg_foreign_data_wrapper_aclmask(table_oid, roleid, mask, how);
2251 case ACL_KIND_FOREIGN_SERVER:
2252 return pg_foreign_server_aclmask(table_oid, roleid, mask, how);
2253 default:
2254 elog(ERROR, "unrecognized objkind: %d",
2255 (int) objkind);
2256 /* not reached, but keep compiler quiet */
2257 return ACL_NO_RIGHTS;
2262 /* ****************************************************************
2263 * Exported routines for examining a user's privileges for various objects
2265 * See aclmask() for a description of the common API for these functions.
2267 * Note: we give lookup failure the full ereport treatment because the
2268 * has_xxx_privilege() family of functions allow users to pass any random
2269 * OID to these functions.
2270 * ****************************************************************
2274 * Exported routine for examining a user's privileges for a column
2276 * Note: this considers only privileges granted specifically on the column.
2277 * It is caller's responsibility to take relation-level privileges into account
2278 * as appropriate. (For the same reason, we have no special case for
2279 * superuser-ness here.)
2281 AclMode
2282 pg_attribute_aclmask(Oid table_oid, AttrNumber attnum, Oid roleid,
2283 AclMode mask, AclMaskHow how)
2285 AclMode result;
2286 HeapTuple classTuple;
2287 HeapTuple attTuple;
2288 Form_pg_class classForm;
2289 Form_pg_attribute attributeForm;
2290 Datum aclDatum;
2291 bool isNull;
2292 Acl *acl;
2293 Oid ownerId;
2296 * First, get the column's ACL from its pg_attribute entry
2298 attTuple = SearchSysCache(ATTNUM,
2299 ObjectIdGetDatum(table_oid),
2300 Int16GetDatum(attnum),
2301 0, 0);
2302 if (!HeapTupleIsValid(attTuple))
2303 ereport(ERROR,
2304 (errcode(ERRCODE_UNDEFINED_COLUMN),
2305 errmsg("attribute %d of relation with OID %u does not exist",
2306 attnum, table_oid)));
2307 attributeForm = (Form_pg_attribute) GETSTRUCT(attTuple);
2309 /* Throw error on dropped columns, too */
2310 if (attributeForm->attisdropped)
2311 ereport(ERROR,
2312 (errcode(ERRCODE_UNDEFINED_COLUMN),
2313 errmsg("attribute %d of relation with OID %u does not exist",
2314 attnum, table_oid)));
2316 aclDatum = SysCacheGetAttr(ATTNUM, attTuple, Anum_pg_attribute_attacl,
2317 &isNull);
2320 * Here we hard-wire knowledge that the default ACL for a column grants no
2321 * privileges, so that we can fall out quickly in the very common case
2322 * where attacl is null.
2324 if (isNull)
2326 ReleaseSysCache(attTuple);
2327 return 0;
2331 * Must get the relation's ownerId from pg_class. Since we already found
2332 * a pg_attribute entry, the only likely reason for this to fail is that a
2333 * concurrent DROP of the relation committed since then (which could only
2334 * happen if we don't have lock on the relation). We prefer to report "no
2335 * privileges" rather than failing in such a case, so as to avoid unwanted
2336 * failures in has_column_privilege() tests.
2338 classTuple = SearchSysCache(RELOID,
2339 ObjectIdGetDatum(table_oid),
2340 0, 0, 0);
2341 if (!HeapTupleIsValid(classTuple))
2343 ReleaseSysCache(attTuple);
2344 return 0;
2346 classForm = (Form_pg_class) GETSTRUCT(classTuple);
2348 ownerId = classForm->relowner;
2350 ReleaseSysCache(classTuple);
2352 /* detoast column's ACL if necessary */
2353 acl = DatumGetAclP(aclDatum);
2355 result = aclmask(acl, roleid, ownerId, mask, how);
2357 /* if we have a detoasted copy, free it */
2358 if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
2359 pfree(acl);
2361 ReleaseSysCache(attTuple);
2363 return result;
2367 * Exported routine for examining a user's privileges for a table
2369 AclMode
2370 pg_class_aclmask(Oid table_oid, Oid roleid,
2371 AclMode mask, AclMaskHow how)
2373 AclMode result;
2374 HeapTuple tuple;
2375 Form_pg_class classForm;
2376 Datum aclDatum;
2377 bool isNull;
2378 Acl *acl;
2379 Oid ownerId;
2382 * Must get the relation's tuple from pg_class
2384 tuple = SearchSysCache(RELOID,
2385 ObjectIdGetDatum(table_oid),
2386 0, 0, 0);
2387 if (!HeapTupleIsValid(tuple))
2388 ereport(ERROR,
2389 (errcode(ERRCODE_UNDEFINED_TABLE),
2390 errmsg("relation with OID %u does not exist",
2391 table_oid)));
2392 classForm = (Form_pg_class) GETSTRUCT(tuple);
2395 * Deny anyone permission to update a system catalog unless
2396 * pg_authid.rolcatupdate is set. (This is to let superusers protect
2397 * themselves from themselves.) Also allow it if allowSystemTableMods.
2399 * As of 7.4 we have some updatable system views; those shouldn't be
2400 * protected in this way. Assume the view rules can take care of
2401 * themselves. ACL_USAGE is if we ever have system sequences.
2403 if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE)) &&
2404 IsSystemClass(classForm) &&
2405 classForm->relkind != RELKIND_VIEW &&
2406 !has_rolcatupdate(roleid) &&
2407 !allowSystemTableMods)
2409 #ifdef ACLDEBUG
2410 elog(DEBUG2, "permission denied for system catalog update");
2411 #endif
2412 mask &= ~(ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE);
2416 * Otherwise, superusers bypass all permission-checking.
2418 if (superuser_arg(roleid))
2420 #ifdef ACLDEBUG
2421 elog(DEBUG2, "OID %u is superuser, home free", roleid);
2422 #endif
2423 ReleaseSysCache(tuple);
2424 return mask;
2428 * Normal case: get the relation's ACL from pg_class
2430 ownerId = classForm->relowner;
2432 aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl,
2433 &isNull);
2434 if (isNull)
2436 /* No ACL, so build default ACL */
2437 acl = acldefault(classForm->relkind == RELKIND_SEQUENCE ?
2438 ACL_OBJECT_SEQUENCE : ACL_OBJECT_RELATION,
2439 ownerId);
2440 aclDatum = (Datum) 0;
2442 else
2444 /* detoast rel's ACL if necessary */
2445 acl = DatumGetAclP(aclDatum);
2448 result = aclmask(acl, roleid, ownerId, mask, how);
2450 /* if we have a detoasted copy, free it */
2451 if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
2452 pfree(acl);
2454 ReleaseSysCache(tuple);
2456 return result;
2460 * Exported routine for examining a user's privileges for a database
2462 AclMode
2463 pg_database_aclmask(Oid db_oid, Oid roleid,
2464 AclMode mask, AclMaskHow how)
2466 AclMode result;
2467 HeapTuple tuple;
2468 Datum aclDatum;
2469 bool isNull;
2470 Acl *acl;
2471 Oid ownerId;
2473 /* Superusers bypass all permission checking. */
2474 if (superuser_arg(roleid))
2475 return mask;
2478 * Get the database's ACL from pg_database
2480 tuple = SearchSysCache(DATABASEOID,
2481 ObjectIdGetDatum(db_oid),
2482 0, 0, 0);
2483 if (!HeapTupleIsValid(tuple))
2484 ereport(ERROR,
2485 (errcode(ERRCODE_UNDEFINED_DATABASE),
2486 errmsg("database with OID %u does not exist", db_oid)));
2488 ownerId = ((Form_pg_database) GETSTRUCT(tuple))->datdba;
2490 aclDatum = SysCacheGetAttr(DATABASEOID, tuple, Anum_pg_database_datacl,
2491 &isNull);
2492 if (isNull)
2494 /* No ACL, so build default ACL */
2495 acl = acldefault(ACL_OBJECT_DATABASE, ownerId);
2496 aclDatum = (Datum) 0;
2498 else
2500 /* detoast ACL if necessary */
2501 acl = DatumGetAclP(aclDatum);
2504 result = aclmask(acl, roleid, ownerId, mask, how);
2506 /* if we have a detoasted copy, free it */
2507 if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
2508 pfree(acl);
2510 ReleaseSysCache(tuple);
2512 return result;
2516 * Exported routine for examining a user's privileges for a function
2518 AclMode
2519 pg_proc_aclmask(Oid proc_oid, Oid roleid,
2520 AclMode mask, AclMaskHow how)
2522 AclMode result;
2523 HeapTuple tuple;
2524 Datum aclDatum;
2525 bool isNull;
2526 Acl *acl;
2527 Oid ownerId;
2529 /* Superusers bypass all permission checking. */
2530 if (superuser_arg(roleid))
2531 return mask;
2534 * Get the function's ACL from pg_proc
2536 tuple = SearchSysCache(PROCOID,
2537 ObjectIdGetDatum(proc_oid),
2538 0, 0, 0);
2539 if (!HeapTupleIsValid(tuple))
2540 ereport(ERROR,
2541 (errcode(ERRCODE_UNDEFINED_FUNCTION),
2542 errmsg("function with OID %u does not exist", proc_oid)));
2544 ownerId = ((Form_pg_proc) GETSTRUCT(tuple))->proowner;
2546 aclDatum = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_proacl,
2547 &isNull);
2548 if (isNull)
2550 /* No ACL, so build default ACL */
2551 acl = acldefault(ACL_OBJECT_FUNCTION, ownerId);
2552 aclDatum = (Datum) 0;
2554 else
2556 /* detoast ACL if necessary */
2557 acl = DatumGetAclP(aclDatum);
2560 result = aclmask(acl, roleid, ownerId, mask, how);
2562 /* if we have a detoasted copy, free it */
2563 if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
2564 pfree(acl);
2566 ReleaseSysCache(tuple);
2568 return result;
2572 * Exported routine for examining a user's privileges for a language
2574 AclMode
2575 pg_language_aclmask(Oid lang_oid, Oid roleid,
2576 AclMode mask, AclMaskHow how)
2578 AclMode result;
2579 HeapTuple tuple;
2580 Datum aclDatum;
2581 bool isNull;
2582 Acl *acl;
2583 Oid ownerId;
2585 /* Superusers bypass all permission checking. */
2586 if (superuser_arg(roleid))
2587 return mask;
2590 * Get the language's ACL from pg_language
2592 tuple = SearchSysCache(LANGOID,
2593 ObjectIdGetDatum(lang_oid),
2594 0, 0, 0);
2595 if (!HeapTupleIsValid(tuple))
2596 ereport(ERROR,
2597 (errcode(ERRCODE_UNDEFINED_OBJECT),
2598 errmsg("language with OID %u does not exist", lang_oid)));
2600 ownerId = ((Form_pg_language) GETSTRUCT(tuple))->lanowner;
2602 aclDatum = SysCacheGetAttr(LANGOID, tuple, Anum_pg_language_lanacl,
2603 &isNull);
2604 if (isNull)
2606 /* No ACL, so build default ACL */
2607 acl = acldefault(ACL_OBJECT_LANGUAGE, ownerId);
2608 aclDatum = (Datum) 0;
2610 else
2612 /* detoast ACL if necessary */
2613 acl = DatumGetAclP(aclDatum);
2616 result = aclmask(acl, roleid, ownerId, mask, how);
2618 /* if we have a detoasted copy, free it */
2619 if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
2620 pfree(acl);
2622 ReleaseSysCache(tuple);
2624 return result;
2628 * Exported routine for examining a user's privileges for a namespace
2630 AclMode
2631 pg_namespace_aclmask(Oid nsp_oid, Oid roleid,
2632 AclMode mask, AclMaskHow how)
2634 AclMode result;
2635 HeapTuple tuple;
2636 Datum aclDatum;
2637 bool isNull;
2638 Acl *acl;
2639 Oid ownerId;
2641 /* Superusers bypass all permission checking. */
2642 if (superuser_arg(roleid))
2643 return mask;
2646 * If we have been assigned this namespace as a temp namespace, check to
2647 * make sure we have CREATE TEMP permission on the database, and if so act
2648 * as though we have all standard (but not GRANT OPTION) permissions on
2649 * the namespace. If we don't have CREATE TEMP, act as though we have
2650 * only USAGE (and not CREATE) rights.
2652 * This may seem redundant given the check in InitTempTableNamespace, but
2653 * it really isn't since current user ID may have changed since then. The
2654 * upshot of this behavior is that a SECURITY DEFINER function can create
2655 * temp tables that can then be accessed (if permission is granted) by
2656 * code in the same session that doesn't have permissions to create temp
2657 * tables.
2659 * XXX Would it be safe to ereport a special error message as
2660 * InitTempTableNamespace does? Returning zero here means we'll get a
2661 * generic "permission denied for schema pg_temp_N" message, which is not
2662 * remarkably user-friendly.
2664 if (isTempNamespace(nsp_oid))
2666 if (pg_database_aclcheck(MyDatabaseId, roleid,
2667 ACL_CREATE_TEMP) == ACLCHECK_OK)
2668 return mask & ACL_ALL_RIGHTS_NAMESPACE;
2669 else
2670 return mask & ACL_USAGE;
2674 * Get the schema's ACL from pg_namespace
2676 tuple = SearchSysCache(NAMESPACEOID,
2677 ObjectIdGetDatum(nsp_oid),
2678 0, 0, 0);
2679 if (!HeapTupleIsValid(tuple))
2680 ereport(ERROR,
2681 (errcode(ERRCODE_UNDEFINED_SCHEMA),
2682 errmsg("schema with OID %u does not exist", nsp_oid)));
2684 ownerId = ((Form_pg_namespace) GETSTRUCT(tuple))->nspowner;
2686 aclDatum = SysCacheGetAttr(NAMESPACEOID, tuple, Anum_pg_namespace_nspacl,
2687 &isNull);
2688 if (isNull)
2690 /* No ACL, so build default ACL */
2691 acl = acldefault(ACL_OBJECT_NAMESPACE, ownerId);
2692 aclDatum = (Datum) 0;
2694 else
2696 /* detoast ACL if necessary */
2697 acl = DatumGetAclP(aclDatum);
2700 result = aclmask(acl, roleid, ownerId, mask, how);
2702 /* if we have a detoasted copy, free it */
2703 if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
2704 pfree(acl);
2706 ReleaseSysCache(tuple);
2708 return result;
2712 * Exported routine for examining a user's privileges for a tablespace
2714 AclMode
2715 pg_tablespace_aclmask(Oid spc_oid, Oid roleid,
2716 AclMode mask, AclMaskHow how)
2718 AclMode result;
2719 Relation pg_tablespace;
2720 ScanKeyData entry[1];
2721 SysScanDesc scan;
2722 HeapTuple tuple;
2723 Datum aclDatum;
2724 bool isNull;
2725 Acl *acl;
2726 Oid ownerId;
2728 /* Superusers bypass all permission checking. */
2729 if (superuser_arg(roleid))
2730 return mask;
2733 * Get the tablespace's ACL from pg_tablespace
2735 * There's no syscache for pg_tablespace, so must look the hard way
2737 pg_tablespace = heap_open(TableSpaceRelationId, AccessShareLock);
2738 ScanKeyInit(&entry[0],
2739 ObjectIdAttributeNumber,
2740 BTEqualStrategyNumber, F_OIDEQ,
2741 ObjectIdGetDatum(spc_oid));
2742 scan = systable_beginscan(pg_tablespace, TablespaceOidIndexId, true,
2743 SnapshotNow, 1, entry);
2744 tuple = systable_getnext(scan);
2745 if (!HeapTupleIsValid(tuple))
2746 ereport(ERROR,
2747 (errcode(ERRCODE_UNDEFINED_OBJECT),
2748 errmsg("tablespace with OID %u does not exist", spc_oid)));
2750 ownerId = ((Form_pg_tablespace) GETSTRUCT(tuple))->spcowner;
2752 aclDatum = heap_getattr(tuple, Anum_pg_tablespace_spcacl,
2753 RelationGetDescr(pg_tablespace), &isNull);
2755 if (isNull)
2757 /* No ACL, so build default ACL */
2758 acl = acldefault(ACL_OBJECT_TABLESPACE, ownerId);
2759 aclDatum = (Datum) 0;
2761 else
2763 /* detoast ACL if necessary */
2764 acl = DatumGetAclP(aclDatum);
2767 result = aclmask(acl, roleid, ownerId, mask, how);
2769 /* if we have a detoasted copy, free it */
2770 if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
2771 pfree(acl);
2773 systable_endscan(scan);
2774 heap_close(pg_tablespace, AccessShareLock);
2776 return result;
2780 * Exported routine for examining a user's privileges for a foreign
2781 * data wrapper
2783 AclMode
2784 pg_foreign_data_wrapper_aclmask(Oid fdw_oid, Oid roleid,
2785 AclMode mask, AclMaskHow how)
2787 AclMode result;
2788 HeapTuple tuple;
2789 Datum aclDatum;
2790 bool isNull;
2791 Acl *acl;
2792 Oid ownerId;
2794 Form_pg_foreign_data_wrapper fdwForm;
2796 /* Bypass permission checks for superusers */
2797 if (superuser_arg(roleid))
2798 return mask;
2801 * Must get the FDW's tuple from pg_foreign_data_wrapper
2803 tuple = SearchSysCache(FOREIGNDATAWRAPPEROID,
2804 ObjectIdGetDatum(fdw_oid),
2805 0, 0, 0);
2806 if (!HeapTupleIsValid(tuple))
2807 ereport(ERROR,
2808 (errmsg("foreign-data wrapper with OID %u does not exist",
2809 fdw_oid)));
2810 fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tuple);
2813 * Normal case: get the FDW's ACL from pg_foreign_data_wrapper
2815 ownerId = fdwForm->fdwowner;
2817 aclDatum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID, tuple,
2818 Anum_pg_foreign_data_wrapper_fdwacl, &isNull);
2819 if (isNull)
2821 /* No ACL, so build default ACL */
2822 acl = acldefault(ACL_OBJECT_FDW, ownerId);
2823 aclDatum = (Datum) 0;
2825 else
2827 /* detoast rel's ACL if necessary */
2828 acl = DatumGetAclP(aclDatum);
2831 result = aclmask(acl, roleid, ownerId, mask, how);
2833 /* if we have a detoasted copy, free it */
2834 if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
2835 pfree(acl);
2837 ReleaseSysCache(tuple);
2839 return result;
2843 * Exported routine for examining a user's privileges for a foreign
2844 * server.
2846 AclMode
2847 pg_foreign_server_aclmask(Oid srv_oid, Oid roleid,
2848 AclMode mask, AclMaskHow how)
2850 AclMode result;
2851 HeapTuple tuple;
2852 Datum aclDatum;
2853 bool isNull;
2854 Acl *acl;
2855 Oid ownerId;
2857 Form_pg_foreign_server srvForm;
2859 /* Bypass permission checks for superusers */
2860 if (superuser_arg(roleid))
2861 return mask;
2864 * Must get the FDW's tuple from pg_foreign_data_wrapper
2866 tuple = SearchSysCache(FOREIGNSERVEROID,
2867 ObjectIdGetDatum(srv_oid),
2868 0, 0, 0);
2869 if (!HeapTupleIsValid(tuple))
2870 ereport(ERROR,
2871 (errmsg("foreign server with OID %u does not exist",
2872 srv_oid)));
2873 srvForm = (Form_pg_foreign_server) GETSTRUCT(tuple);
2876 * Normal case: get the foreign server's ACL from pg_foreign_server
2878 ownerId = srvForm->srvowner;
2880 aclDatum = SysCacheGetAttr(FOREIGNSERVEROID, tuple,
2881 Anum_pg_foreign_server_srvacl, &isNull);
2882 if (isNull)
2884 /* No ACL, so build default ACL */
2885 acl = acldefault(ACL_OBJECT_FOREIGN_SERVER, ownerId);
2886 aclDatum = (Datum) 0;
2888 else
2890 /* detoast rel's ACL if necessary */
2891 acl = DatumGetAclP(aclDatum);
2894 result = aclmask(acl, roleid, ownerId, mask, how);
2896 /* if we have a detoasted copy, free it */
2897 if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
2898 pfree(acl);
2900 ReleaseSysCache(tuple);
2902 return result;
2906 * Exported routine for checking a user's access privileges to a column
2908 * Returns ACLCHECK_OK if the user has any of the privileges identified by
2909 * 'mode'; otherwise returns a suitable error code (in practice, always
2910 * ACLCHECK_NO_PRIV).
2912 * As with pg_attribute_aclmask, only privileges granted directly on the
2913 * column are considered here.
2915 AclResult
2916 pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum,
2917 Oid roleid, AclMode mode)
2919 if (pg_attribute_aclmask(table_oid, attnum, roleid, mode, ACLMASK_ANY) != 0)
2920 return ACLCHECK_OK;
2921 else
2922 return ACLCHECK_NO_PRIV;
2926 * Exported routine for checking a user's access privileges to any/all columns
2928 * If 'how' is ACLMASK_ANY, then returns ACLCHECK_OK if user has any of the
2929 * privileges identified by 'mode' on any non-dropped column in the relation;
2930 * otherwise returns a suitable error code (in practice, always
2931 * ACLCHECK_NO_PRIV).
2933 * If 'how' is ACLMASK_ALL, then returns ACLCHECK_OK if user has any of the
2934 * privileges identified by 'mode' on each non-dropped column in the relation
2935 * (and there must be at least one such column); otherwise returns a suitable
2936 * error code (in practice, always ACLCHECK_NO_PRIV).
2938 * As with pg_attribute_aclmask, only privileges granted directly on the
2939 * column(s) are considered here.
2941 * Note: system columns are not considered here; there are cases where that
2942 * might be appropriate but there are also cases where it wouldn't.
2944 AclResult
2945 pg_attribute_aclcheck_all(Oid table_oid, Oid roleid, AclMode mode,
2946 AclMaskHow how)
2948 AclResult result;
2949 HeapTuple classTuple;
2950 Form_pg_class classForm;
2951 AttrNumber nattrs;
2952 AttrNumber curr_att;
2955 * Must fetch pg_class row to check number of attributes. As in
2956 * pg_attribute_aclmask, we prefer to return "no privileges" instead of
2957 * throwing an error if we get any unexpected lookup errors.
2959 classTuple = SearchSysCache(RELOID,
2960 ObjectIdGetDatum(table_oid),
2961 0, 0, 0);
2962 if (!HeapTupleIsValid(classTuple))
2963 return ACLCHECK_NO_PRIV;
2964 classForm = (Form_pg_class) GETSTRUCT(classTuple);
2966 nattrs = classForm->relnatts;
2968 ReleaseSysCache(classTuple);
2971 * Initialize result in case there are no non-dropped columns. We want to
2972 * report failure in such cases for either value of 'how'.
2974 result = ACLCHECK_NO_PRIV;
2976 for (curr_att = 1; curr_att <= nattrs; curr_att++)
2978 HeapTuple attTuple;
2979 AclMode attmask;
2981 attTuple = SearchSysCache(ATTNUM,
2982 ObjectIdGetDatum(table_oid),
2983 Int16GetDatum(curr_att),
2984 0, 0);
2985 if (!HeapTupleIsValid(attTuple))
2986 continue;
2988 /* ignore dropped columns */
2989 if (((Form_pg_attribute) GETSTRUCT(attTuple))->attisdropped)
2991 ReleaseSysCache(attTuple);
2992 continue;
2996 * Here we hard-wire knowledge that the default ACL for a column
2997 * grants no privileges, so that we can fall out quickly in the very
2998 * common case where attacl is null.
3000 if (heap_attisnull(attTuple, Anum_pg_attribute_attacl))
3001 attmask = 0;
3002 else
3003 attmask = pg_attribute_aclmask(table_oid, curr_att, roleid,
3004 mode, ACLMASK_ANY);
3006 ReleaseSysCache(attTuple);
3008 if (attmask != 0)
3010 result = ACLCHECK_OK;
3011 if (how == ACLMASK_ANY)
3012 break; /* succeed on any success */
3014 else
3016 result = ACLCHECK_NO_PRIV;
3017 if (how == ACLMASK_ALL)
3018 break; /* fail on any failure */
3022 return result;
3026 * Exported routine for checking a user's access privileges to a table
3028 * Returns ACLCHECK_OK if the user has any of the privileges identified by
3029 * 'mode'; otherwise returns a suitable error code (in practice, always
3030 * ACLCHECK_NO_PRIV).
3032 AclResult
3033 pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
3035 if (pg_class_aclmask(table_oid, roleid, mode, ACLMASK_ANY) != 0)
3036 return ACLCHECK_OK;
3037 else
3038 return ACLCHECK_NO_PRIV;
3042 * Exported routine for checking a user's access privileges to a database
3044 AclResult
3045 pg_database_aclcheck(Oid db_oid, Oid roleid, AclMode mode)
3047 if (pg_database_aclmask(db_oid, roleid, mode, ACLMASK_ANY) != 0)
3048 return ACLCHECK_OK;
3049 else
3050 return ACLCHECK_NO_PRIV;
3054 * Exported routine for checking a user's access privileges to a function
3056 AclResult
3057 pg_proc_aclcheck(Oid proc_oid, Oid roleid, AclMode mode)
3059 if (pg_proc_aclmask(proc_oid, roleid, mode, ACLMASK_ANY) != 0)
3060 return ACLCHECK_OK;
3061 else
3062 return ACLCHECK_NO_PRIV;
3066 * Exported routine for checking a user's access privileges to a language
3068 AclResult
3069 pg_language_aclcheck(Oid lang_oid, Oid roleid, AclMode mode)
3071 if (pg_language_aclmask(lang_oid, roleid, mode, ACLMASK_ANY) != 0)
3072 return ACLCHECK_OK;
3073 else
3074 return ACLCHECK_NO_PRIV;
3078 * Exported routine for checking a user's access privileges to a namespace
3080 AclResult
3081 pg_namespace_aclcheck(Oid nsp_oid, Oid roleid, AclMode mode)
3083 if (pg_namespace_aclmask(nsp_oid, roleid, mode, ACLMASK_ANY) != 0)
3084 return ACLCHECK_OK;
3085 else
3086 return ACLCHECK_NO_PRIV;
3090 * Exported routine for checking a user's access privileges to a tablespace
3092 AclResult
3093 pg_tablespace_aclcheck(Oid spc_oid, Oid roleid, AclMode mode)
3095 if (pg_tablespace_aclmask(spc_oid, roleid, mode, ACLMASK_ANY) != 0)
3096 return ACLCHECK_OK;
3097 else
3098 return ACLCHECK_NO_PRIV;
3102 * Exported routine for checking a user's access privileges to a foreign
3103 * data wrapper
3105 AclResult
3106 pg_foreign_data_wrapper_aclcheck(Oid fdw_oid, Oid roleid, AclMode mode)
3108 if (pg_foreign_data_wrapper_aclmask(fdw_oid, roleid, mode, ACLMASK_ANY) != 0)
3109 return ACLCHECK_OK;
3110 else
3111 return ACLCHECK_NO_PRIV;
3115 * Exported routine for checking a user's access privileges to a foreign
3116 * server
3118 AclResult
3119 pg_foreign_server_aclcheck(Oid srv_oid, Oid roleid, AclMode mode)
3121 if (pg_foreign_server_aclmask(srv_oid, roleid, mode, ACLMASK_ANY) != 0)
3122 return ACLCHECK_OK;
3123 else
3124 return ACLCHECK_NO_PRIV;
3128 * Ownership check for a relation (specified by OID).
3130 bool
3131 pg_class_ownercheck(Oid class_oid, Oid roleid)
3133 HeapTuple tuple;
3134 Oid ownerId;
3136 /* Superusers bypass all permission checking. */
3137 if (superuser_arg(roleid))
3138 return true;
3140 tuple = SearchSysCache(RELOID,
3141 ObjectIdGetDatum(class_oid),
3142 0, 0, 0);
3143 if (!HeapTupleIsValid(tuple))
3144 ereport(ERROR,
3145 (errcode(ERRCODE_UNDEFINED_TABLE),
3146 errmsg("relation with OID %u does not exist", class_oid)));
3148 ownerId = ((Form_pg_class) GETSTRUCT(tuple))->relowner;
3150 ReleaseSysCache(tuple);
3152 return has_privs_of_role(roleid, ownerId);
3156 * Ownership check for a type (specified by OID).
3158 bool
3159 pg_type_ownercheck(Oid type_oid, Oid roleid)
3161 HeapTuple tuple;
3162 Oid ownerId;
3164 /* Superusers bypass all permission checking. */
3165 if (superuser_arg(roleid))
3166 return true;
3168 tuple = SearchSysCache(TYPEOID,
3169 ObjectIdGetDatum(type_oid),
3170 0, 0, 0);
3171 if (!HeapTupleIsValid(tuple))
3172 ereport(ERROR,
3173 (errcode(ERRCODE_UNDEFINED_OBJECT),
3174 errmsg("type with OID %u does not exist", type_oid)));
3176 ownerId = ((Form_pg_type) GETSTRUCT(tuple))->typowner;
3178 ReleaseSysCache(tuple);
3180 return has_privs_of_role(roleid, ownerId);
3184 * Ownership check for an operator (specified by OID).
3186 bool
3187 pg_oper_ownercheck(Oid oper_oid, Oid roleid)
3189 HeapTuple tuple;
3190 Oid ownerId;
3192 /* Superusers bypass all permission checking. */
3193 if (superuser_arg(roleid))
3194 return true;
3196 tuple = SearchSysCache(OPEROID,
3197 ObjectIdGetDatum(oper_oid),
3198 0, 0, 0);
3199 if (!HeapTupleIsValid(tuple))
3200 ereport(ERROR,
3201 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3202 errmsg("operator with OID %u does not exist", oper_oid)));
3204 ownerId = ((Form_pg_operator) GETSTRUCT(tuple))->oprowner;
3206 ReleaseSysCache(tuple);
3208 return has_privs_of_role(roleid, ownerId);
3212 * Ownership check for a function (specified by OID).
3214 bool
3215 pg_proc_ownercheck(Oid proc_oid, Oid roleid)
3217 HeapTuple tuple;
3218 Oid ownerId;
3220 /* Superusers bypass all permission checking. */
3221 if (superuser_arg(roleid))
3222 return true;
3224 tuple = SearchSysCache(PROCOID,
3225 ObjectIdGetDatum(proc_oid),
3226 0, 0, 0);
3227 if (!HeapTupleIsValid(tuple))
3228 ereport(ERROR,
3229 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3230 errmsg("function with OID %u does not exist", proc_oid)));
3232 ownerId = ((Form_pg_proc) GETSTRUCT(tuple))->proowner;
3234 ReleaseSysCache(tuple);
3236 return has_privs_of_role(roleid, ownerId);
3240 * Ownership check for a procedural language (specified by OID)
3242 bool
3243 pg_language_ownercheck(Oid lan_oid, Oid roleid)
3245 HeapTuple tuple;
3246 Oid ownerId;
3248 /* Superusers bypass all permission checking. */
3249 if (superuser_arg(roleid))
3250 return true;
3252 tuple = SearchSysCache(LANGOID,
3253 ObjectIdGetDatum(lan_oid),
3254 0, 0, 0);
3255 if (!HeapTupleIsValid(tuple))
3256 ereport(ERROR,
3257 (errcode(ERRCODE_UNDEFINED_FUNCTION),
3258 errmsg("language with OID %u does not exist", lan_oid)));
3260 ownerId = ((Form_pg_language) GETSTRUCT(tuple))->lanowner;
3262 ReleaseSysCache(tuple);
3264 return has_privs_of_role(roleid, ownerId);
3268 * Ownership check for a namespace (specified by OID).
3270 bool
3271 pg_namespace_ownercheck(Oid nsp_oid, Oid roleid)
3273 HeapTuple tuple;
3274 Oid ownerId;
3276 /* Superusers bypass all permission checking. */
3277 if (superuser_arg(roleid))
3278 return true;
3280 tuple = SearchSysCache(NAMESPACEOID,
3281 ObjectIdGetDatum(nsp_oid),
3282 0, 0, 0);
3283 if (!HeapTupleIsValid(tuple))
3284 ereport(ERROR,
3285 (errcode(ERRCODE_UNDEFINED_SCHEMA),
3286 errmsg("schema with OID %u does not exist", nsp_oid)));
3288 ownerId = ((Form_pg_namespace) GETSTRUCT(tuple))->nspowner;
3290 ReleaseSysCache(tuple);
3292 return has_privs_of_role(roleid, ownerId);
3296 * Ownership check for a tablespace (specified by OID).
3298 bool
3299 pg_tablespace_ownercheck(Oid spc_oid, Oid roleid)
3301 Relation pg_tablespace;
3302 ScanKeyData entry[1];
3303 SysScanDesc scan;
3304 HeapTuple spctuple;
3305 Oid spcowner;
3307 /* Superusers bypass all permission checking. */
3308 if (superuser_arg(roleid))
3309 return true;
3311 /* There's no syscache for pg_tablespace, so must look the hard way */
3312 pg_tablespace = heap_open(TableSpaceRelationId, AccessShareLock);
3313 ScanKeyInit(&entry[0],
3314 ObjectIdAttributeNumber,
3315 BTEqualStrategyNumber, F_OIDEQ,
3316 ObjectIdGetDatum(spc_oid));
3317 scan = systable_beginscan(pg_tablespace, TablespaceOidIndexId, true,
3318 SnapshotNow, 1, entry);
3320 spctuple = systable_getnext(scan);
3322 if (!HeapTupleIsValid(spctuple))
3323 ereport(ERROR,
3324 (errcode(ERRCODE_UNDEFINED_OBJECT),
3325 errmsg("tablespace with OID %u does not exist", spc_oid)));
3327 spcowner = ((Form_pg_tablespace) GETSTRUCT(spctuple))->spcowner;
3329 systable_endscan(scan);
3330 heap_close(pg_tablespace, AccessShareLock);
3332 return has_privs_of_role(roleid, spcowner);
3336 * Ownership check for an operator class (specified by OID).
3338 bool
3339 pg_opclass_ownercheck(Oid opc_oid, Oid roleid)
3341 HeapTuple tuple;
3342 Oid ownerId;
3344 /* Superusers bypass all permission checking. */
3345 if (superuser_arg(roleid))
3346 return true;
3348 tuple = SearchSysCache(CLAOID,
3349 ObjectIdGetDatum(opc_oid),
3350 0, 0, 0);
3351 if (!HeapTupleIsValid(tuple))
3352 ereport(ERROR,
3353 (errcode(ERRCODE_UNDEFINED_OBJECT),
3354 errmsg("operator class with OID %u does not exist",
3355 opc_oid)));
3357 ownerId = ((Form_pg_opclass) GETSTRUCT(tuple))->opcowner;
3359 ReleaseSysCache(tuple);
3361 return has_privs_of_role(roleid, ownerId);
3365 * Ownership check for an operator family (specified by OID).
3367 bool
3368 pg_opfamily_ownercheck(Oid opf_oid, Oid roleid)
3370 HeapTuple tuple;
3371 Oid ownerId;
3373 /* Superusers bypass all permission checking. */
3374 if (superuser_arg(roleid))
3375 return true;
3377 tuple = SearchSysCache(OPFAMILYOID,
3378 ObjectIdGetDatum(opf_oid),
3379 0, 0, 0);
3380 if (!HeapTupleIsValid(tuple))
3381 ereport(ERROR,
3382 (errcode(ERRCODE_UNDEFINED_OBJECT),
3383 errmsg("operator family with OID %u does not exist",
3384 opf_oid)));
3386 ownerId = ((Form_pg_opfamily) GETSTRUCT(tuple))->opfowner;
3388 ReleaseSysCache(tuple);
3390 return has_privs_of_role(roleid, ownerId);
3394 * Ownership check for a text search dictionary (specified by OID).
3396 bool
3397 pg_ts_dict_ownercheck(Oid dict_oid, Oid roleid)
3399 HeapTuple tuple;
3400 Oid ownerId;
3402 /* Superusers bypass all permission checking. */
3403 if (superuser_arg(roleid))
3404 return true;
3406 tuple = SearchSysCache(TSDICTOID,
3407 ObjectIdGetDatum(dict_oid),
3408 0, 0, 0);
3409 if (!HeapTupleIsValid(tuple))
3410 ereport(ERROR,
3411 (errcode(ERRCODE_UNDEFINED_OBJECT),
3412 errmsg("text search dictionary with OID %u does not exist",
3413 dict_oid)));
3415 ownerId = ((Form_pg_ts_dict) GETSTRUCT(tuple))->dictowner;
3417 ReleaseSysCache(tuple);
3419 return has_privs_of_role(roleid, ownerId);
3423 * Ownership check for a text search configuration (specified by OID).
3425 bool
3426 pg_ts_config_ownercheck(Oid cfg_oid, Oid roleid)
3428 HeapTuple tuple;
3429 Oid ownerId;
3431 /* Superusers bypass all permission checking. */
3432 if (superuser_arg(roleid))
3433 return true;
3435 tuple = SearchSysCache(TSCONFIGOID,
3436 ObjectIdGetDatum(cfg_oid),
3437 0, 0, 0);
3438 if (!HeapTupleIsValid(tuple))
3439 ereport(ERROR,
3440 (errcode(ERRCODE_UNDEFINED_OBJECT),
3441 errmsg("text search configuration with OID %u does not exist",
3442 cfg_oid)));
3444 ownerId = ((Form_pg_ts_config) GETSTRUCT(tuple))->cfgowner;
3446 ReleaseSysCache(tuple);
3448 return has_privs_of_role(roleid, ownerId);
3452 * Ownership check for a foreign server (specified by OID).
3454 bool
3455 pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid)
3457 HeapTuple tuple;
3458 Oid ownerId;
3460 /* Superusers bypass all permission checking. */
3461 if (superuser_arg(roleid))
3462 return true;
3464 tuple = SearchSysCache(FOREIGNSERVEROID,
3465 ObjectIdGetDatum(srv_oid),
3466 0, 0, 0);
3467 if (!HeapTupleIsValid(tuple))
3468 ereport(ERROR,
3469 (errcode(ERRCODE_UNDEFINED_OBJECT),
3470 errmsg("foreign server with OID %u does not exist",
3471 srv_oid)));
3473 ownerId = ((Form_pg_foreign_server) GETSTRUCT(tuple))->srvowner;
3475 ReleaseSysCache(tuple);
3477 return has_privs_of_role(roleid, ownerId);
3481 * Ownership check for a database (specified by OID).
3483 bool
3484 pg_database_ownercheck(Oid db_oid, Oid roleid)
3486 HeapTuple tuple;
3487 Oid dba;
3489 /* Superusers bypass all permission checking. */
3490 if (superuser_arg(roleid))
3491 return true;
3493 tuple = SearchSysCache(DATABASEOID,
3494 ObjectIdGetDatum(db_oid),
3495 0, 0, 0);
3496 if (!HeapTupleIsValid(tuple))
3497 ereport(ERROR,
3498 (errcode(ERRCODE_UNDEFINED_DATABASE),
3499 errmsg("database with OID %u does not exist", db_oid)));
3501 dba = ((Form_pg_database) GETSTRUCT(tuple))->datdba;
3503 ReleaseSysCache(tuple);
3505 return has_privs_of_role(roleid, dba);
3509 * Ownership check for a conversion (specified by OID).
3511 bool
3512 pg_conversion_ownercheck(Oid conv_oid, Oid roleid)
3514 HeapTuple tuple;
3515 Oid ownerId;
3517 /* Superusers bypass all permission checking. */
3518 if (superuser_arg(roleid))
3519 return true;
3521 tuple = SearchSysCache(CONVOID,
3522 ObjectIdGetDatum(conv_oid),
3523 0, 0, 0);
3524 if (!HeapTupleIsValid(tuple))
3525 ereport(ERROR,
3526 (errcode(ERRCODE_UNDEFINED_OBJECT),
3527 errmsg("conversion with OID %u does not exist", conv_oid)));
3529 ownerId = ((Form_pg_conversion) GETSTRUCT(tuple))->conowner;
3531 ReleaseSysCache(tuple);
3533 return has_privs_of_role(roleid, ownerId);