Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * aclchk.c
4 : : * Routines to check access control permissions.
5 : : *
6 : : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/catalog/aclchk.c
12 : : *
13 : : * NOTES
14 : : * See acl.h.
15 : : *
16 : : * The xxx_aclmask() functions in this file are wrappers around
17 : : * acl.c's aclmask() function; see that for basic usage information.
18 : : * The wrapper functions add object-type-specific lookup capability.
19 : : * Generally, they will throw error if the object doesn't exist.
20 : : *
21 : : * The xxx_aclmask_ext() functions add the ability to not throw
22 : : * error if the object doesn't exist. If their "is_missing" argument
23 : : * isn't NULL, then when the object isn't found they will set
24 : : * *is_missing = true and return zero (no privileges) instead of
25 : : * throwing an error. Caller must initialize *is_missing = false.
26 : : *
27 : : * The xxx_aclcheck() functions are simplified wrappers around the
28 : : * corresponding xxx_aclmask() functions, simply returning ACLCHECK_OK
29 : : * if any of the privileges specified in "mode" are held, and otherwise
30 : : * a suitable error code (in practice, always ACLCHECK_NO_PRIV).
31 : : * Again, they will throw error if the object doesn't exist.
32 : : *
33 : : * The xxx_aclcheck_ext() functions add the ability to not throw
34 : : * error if the object doesn't exist. Their "is_missing" argument
35 : : * works similarly to the xxx_aclmask_ext() functions.
36 : : *
37 : : *-------------------------------------------------------------------------
38 : : */
39 : : #include "postgres.h"
40 : :
41 : : #include "access/genam.h"
42 : : #include "access/heapam.h"
43 : : #include "access/htup_details.h"
44 : : #include "access/sysattr.h"
45 : : #include "access/tableam.h"
46 : : #include "access/xact.h"
47 : : #include "catalog/binary_upgrade.h"
48 : : #include "catalog/catalog.h"
49 : : #include "catalog/dependency.h"
50 : : #include "catalog/indexing.h"
51 : : #include "catalog/objectaccess.h"
52 : : #include "catalog/pg_authid.h"
53 : : #include "catalog/pg_class.h"
54 : : #include "catalog/pg_database.h"
55 : : #include "catalog/pg_default_acl.h"
56 : : #include "catalog/pg_foreign_data_wrapper.h"
57 : : #include "catalog/pg_foreign_server.h"
58 : : #include "catalog/pg_init_privs.h"
59 : : #include "catalog/pg_language.h"
60 : : #include "catalog/pg_largeobject.h"
61 : : #include "catalog/pg_largeobject_metadata.h"
62 : : #include "catalog/pg_namespace.h"
63 : : #include "catalog/pg_parameter_acl.h"
64 : : #include "catalog/pg_proc.h"
65 : : #include "catalog/pg_tablespace.h"
66 : : #include "catalog/pg_type.h"
67 : : #include "commands/dbcommands.h"
68 : : #include "commands/defrem.h"
69 : : #include "commands/event_trigger.h"
70 : : #include "commands/extension.h"
71 : : #include "commands/proclang.h"
72 : : #include "commands/tablespace.h"
73 : : #include "foreign/foreign.h"
74 : : #include "miscadmin.h"
75 : : #include "nodes/makefuncs.h"
76 : : #include "parser/parse_func.h"
77 : : #include "parser/parse_type.h"
78 : : #include "utils/acl.h"
79 : : #include "utils/aclchk_internal.h"
80 : : #include "utils/builtins.h"
81 : : #include "utils/fmgroids.h"
82 : : #include "utils/guc.h"
83 : : #include "utils/lsyscache.h"
84 : : #include "utils/rel.h"
85 : : #include "utils/syscache.h"
86 : :
87 : : /*
88 : : * Internal format used by ALTER DEFAULT PRIVILEGES.
89 : : */
90 : : typedef struct
91 : : {
92 : : Oid roleid; /* owning role */
93 : : Oid nspid; /* namespace, or InvalidOid if none */
94 : : /* remaining fields are same as in InternalGrant: */
95 : : bool is_grant;
96 : : ObjectType objtype;
97 : : bool all_privs;
98 : : AclMode privileges;
99 : : List *grantees;
100 : : bool grant_option;
101 : : DropBehavior behavior;
102 : : } InternalDefaultACL;
103 : :
104 : : /*
105 : : * When performing a binary-upgrade, pg_dump will call a function to set
106 : : * this variable to let us know that we need to populate the pg_init_privs
107 : : * table for the GRANT/REVOKE commands while this variable is set to true.
108 : : */
109 : : bool binary_upgrade_record_init_privs = false;
110 : :
111 : : static void ExecGrantStmt_oids(InternalGrant *istmt);
112 : : static void ExecGrant_Relation(InternalGrant *istmt);
113 : : static void ExecGrant_common(InternalGrant *istmt, Oid classid, AclMode default_privs,
114 : : void (*object_check) (InternalGrant *istmt, HeapTuple tuple));
115 : : static void ExecGrant_Language_check(InternalGrant *istmt, HeapTuple tuple);
116 : : static void ExecGrant_Largeobject(InternalGrant *istmt);
117 : : static void ExecGrant_Type_check(InternalGrant *istmt, HeapTuple tuple);
118 : : static void ExecGrant_Parameter(InternalGrant *istmt);
119 : :
120 : : static void SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames);
121 : : static void SetDefaultACL(InternalDefaultACL *iacls);
122 : :
123 : : static List *objectNamesToOids(ObjectType objtype, List *objnames,
124 : : bool is_grant);
125 : : static List *objectsInSchemaToOids(ObjectType objtype, List *nspnames);
126 : : static List *getRelationsInNamespace(Oid namespaceId, char relkind);
127 : : static void expand_col_privileges(List *colnames, Oid table_oid,
128 : : AclMode this_privileges,
129 : : AclMode *col_privileges,
130 : : int num_col_privileges);
131 : : static void expand_all_col_privileges(Oid table_oid, Form_pg_class classForm,
132 : : AclMode this_privileges,
133 : : AclMode *col_privileges,
134 : : int num_col_privileges);
135 : : static AclMode string_to_privilege(const char *privname);
136 : : static const char *privilege_to_string(AclMode privilege);
137 : : static AclMode restrict_and_check_grant(bool is_grant, AclMode avail_goptions,
138 : : bool all_privs, AclMode privileges,
139 : : Oid objectId, Oid grantorId,
140 : : ObjectType objtype, const char *objname,
141 : : AttrNumber att_number, const char *colname);
142 : : static AclMode pg_aclmask(ObjectType objtype, Oid object_oid, AttrNumber attnum,
143 : : Oid roleid, AclMode mask, AclMaskHow how);
144 : : static AclMode object_aclmask(Oid classid, Oid objectid, Oid roleid,
145 : : AclMode mask, AclMaskHow how);
146 : : static AclMode object_aclmask_ext(Oid classid, Oid objectid, Oid roleid,
147 : : AclMode mask, AclMaskHow how,
148 : : bool *is_missing);
149 : : static AclMode pg_attribute_aclmask(Oid table_oid, AttrNumber attnum,
150 : : Oid roleid, AclMode mask, AclMaskHow how);
151 : : static AclMode pg_attribute_aclmask_ext(Oid table_oid, AttrNumber attnum,
152 : : Oid roleid, AclMode mask,
153 : : AclMaskHow how, bool *is_missing);
154 : : static AclMode pg_class_aclmask_ext(Oid table_oid, Oid roleid,
155 : : AclMode mask, AclMaskHow how,
156 : : bool *is_missing);
157 : : static AclMode pg_parameter_acl_aclmask(Oid acl_oid, Oid roleid,
158 : : AclMode mask, AclMaskHow how);
159 : : static AclMode pg_largeobject_aclmask_snapshot(Oid lobj_oid, Oid roleid,
160 : : AclMode mask, AclMaskHow how, Snapshot snapshot);
161 : : static AclMode pg_namespace_aclmask_ext(Oid nsp_oid, Oid roleid,
162 : : AclMode mask, AclMaskHow how,
163 : : bool *is_missing);
164 : : static AclMode pg_type_aclmask_ext(Oid type_oid, Oid roleid,
165 : : AclMode mask, AclMaskHow how,
166 : : bool *is_missing);
167 : : static void recordExtensionInitPriv(Oid objoid, Oid classoid, int objsubid,
168 : : Acl *new_acl);
169 : : static void recordExtensionInitPrivWorker(Oid objoid, Oid classoid, int objsubid,
170 : : Acl *new_acl);
171 : :
172 : :
173 : : /*
174 : : * If is_grant is true, adds the given privileges for the list of
175 : : * grantees to the existing old_acl. If is_grant is false, the
176 : : * privileges for the given grantees are removed from old_acl.
177 : : *
178 : : * NB: the original old_acl is pfree'd.
179 : : */
180 : : static Acl *
8029 tgl@sss.pgh.pa.us 181 :CBC 16314 : merge_acl_with_grant(Acl *old_acl, bool is_grant,
182 : : bool grant_option, DropBehavior behavior,
183 : : List *grantees, AclMode privileges,
184 : : Oid grantorId, Oid ownerId)
185 : : {
186 : : unsigned modechg;
187 : : ListCell *j;
188 : : Acl *new_acl;
189 : :
190 [ + + ]: 16314 : modechg = is_grant ? ACL_MODECHG_ADD : ACL_MODECHG_DEL;
191 : :
8091 peter_e@gmx.net 192 : 16314 : new_acl = old_acl;
193 : :
194 [ + - + + : 32685 : foreach(j, grantees)
+ + ]
195 : : {
196 : : AclItem aclitem;
197 : : Acl *newer_acl;
198 : :
4326 bruce@momjian.us 199 : 16377 : aclitem.ai_grantee = lfirst_oid(j);
200 : :
201 : : /*
202 : : * Grant options can only be granted to individual roles, not PUBLIC.
203 : : * The reason is that if a user would re-grant a privilege that he
204 : : * held through PUBLIC, and later the user is removed, the situation
205 : : * is impossible to clean up.
206 : : */
6865 tgl@sss.pgh.pa.us 207 [ + + + + : 16377 : if (is_grant && grant_option && aclitem.ai_grantee == ACL_ID_PUBLIC)
- + ]
7573 tgl@sss.pgh.pa.us 208 [ # # ]:UBC 0 : ereport(ERROR,
209 : : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
210 : : errmsg("grant options can only be granted to roles")));
211 : :
4326 bruce@momjian.us 212 :CBC 16377 : aclitem.ai_grantor = grantorId;
213 : :
214 : : /*
215 : : * The asymmetry in the conditions here comes from the spec. In
216 : : * GRANT, the grant_option flag signals WITH GRANT OPTION, which means
217 : : * to grant both the basic privilege and its grant option. But in
218 : : * REVOKE, plain revoke revokes both the basic privilege and its grant
219 : : * option, while REVOKE GRANT OPTION revokes only the option.
220 : : */
6865 tgl@sss.pgh.pa.us 221 [ + + + + : 16377 : ACLITEM_SET_PRIVS_GOPTIONS(aclitem,
+ + + + ]
222 : : (is_grant || !grant_option) ? privileges : ACL_NO_RIGHTS,
223 : : (!is_grant || grant_option) ? privileges : ACL_NO_RIGHTS);
224 : :
225 : 16377 : newer_acl = aclupdate(new_acl, &aclitem, modechg, ownerId, behavior);
226 : :
227 : : /* avoid memory leak when there are many grantees */
7528 228 : 16371 : pfree(new_acl);
229 : 16371 : new_acl = newer_acl;
230 : : }
231 : :
8091 peter_e@gmx.net 232 : 16308 : return new_acl;
233 : : }
234 : :
235 : : /*
236 : : * Restrict the privileges to what we can actually grant, and emit
237 : : * the standards-mandated warning and error messages.
238 : : */
239 : : static AclMode
6709 alvherre@alvh.no-ip. 240 : 16234 : restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
241 : : AclMode privileges, Oid objectId, Oid grantorId,
242 : : ObjectType objtype, const char *objname,
243 : : AttrNumber att_number, const char *colname)
244 : : {
245 : : AclMode this_privileges;
246 : : AclMode whole_mask;
247 : :
2325 peter_e@gmx.net 248 [ + + + + : 16234 : switch (objtype)
+ + + + -
+ + - + +
- ]
249 : : {
250 : 8717 : case OBJECT_COLUMN:
5561 tgl@sss.pgh.pa.us 251 : 8717 : whole_mask = ACL_ALL_RIGHTS_COLUMN;
252 : 8717 : break;
2325 peter_e@gmx.net 253 : 3628 : case OBJECT_TABLE:
6709 alvherre@alvh.no-ip. 254 : 3628 : whole_mask = ACL_ALL_RIGHTS_RELATION;
255 : 3628 : break;
2325 peter_e@gmx.net 256 : 80 : case OBJECT_SEQUENCE:
6658 bruce@momjian.us 257 : 80 : whole_mask = ACL_ALL_RIGHTS_SEQUENCE;
258 : 80 : break;
2325 peter_e@gmx.net 259 : 136 : case OBJECT_DATABASE:
6709 alvherre@alvh.no-ip. 260 : 136 : whole_mask = ACL_ALL_RIGHTS_DATABASE;
261 : 136 : break;
2325 peter_e@gmx.net 262 : 3206 : case OBJECT_FUNCTION:
6709 alvherre@alvh.no-ip. 263 : 3206 : whole_mask = ACL_ALL_RIGHTS_FUNCTION;
264 : 3206 : break;
2325 peter_e@gmx.net 265 : 18 : case OBJECT_LANGUAGE:
6709 alvherre@alvh.no-ip. 266 : 18 : whole_mask = ACL_ALL_RIGHTS_LANGUAGE;
267 : 18 : break;
2325 peter_e@gmx.net 268 : 40 : case OBJECT_LARGEOBJECT:
5238 itagaki.takahiro@gma 269 : 40 : whole_mask = ACL_ALL_RIGHTS_LARGEOBJECT;
270 : 40 : break;
2325 peter_e@gmx.net 271 : 188 : case OBJECT_SCHEMA:
2377 272 : 188 : whole_mask = ACL_ALL_RIGHTS_SCHEMA;
6709 alvherre@alvh.no-ip. 273 : 188 : break;
2325 peter_e@gmx.net 274 :UBC 0 : case OBJECT_TABLESPACE:
6709 alvherre@alvh.no-ip. 275 : 0 : whole_mask = ACL_ALL_RIGHTS_TABLESPACE;
276 : 0 : break;
2325 peter_e@gmx.net 277 :CBC 47 : case OBJECT_FDW:
5595 278 : 47 : whole_mask = ACL_ALL_RIGHTS_FDW;
279 : 47 : break;
2325 280 : 46 : case OBJECT_FOREIGN_SERVER:
5595 281 : 46 : whole_mask = ACL_ALL_RIGHTS_FOREIGN_SERVER;
282 : 46 : break;
2325 peter_e@gmx.net 283 :UBC 0 : case OBJECT_EVENT_TRIGGER:
4288 rhaas@postgresql.org 284 [ # # ]: 0 : elog(ERROR, "grantable rights not supported for event triggers");
285 : : /* not reached, but keep compiler quiet */
286 : : return ACL_NO_RIGHTS;
2325 peter_e@gmx.net 287 :CBC 61 : case OBJECT_TYPE:
4499 288 : 61 : whole_mask = ACL_ALL_RIGHTS_TYPE;
289 : 61 : break;
739 tgl@sss.pgh.pa.us 290 : 67 : case OBJECT_PARAMETER_ACL:
291 : 67 : whole_mask = ACL_ALL_RIGHTS_PARAMETER_ACL;
292 : 67 : break;
6709 alvherre@alvh.no-ip. 293 :UBC 0 : default:
2325 peter_e@gmx.net 294 [ # # ]: 0 : elog(ERROR, "unrecognized object type: %d", objtype);
295 : : /* not reached, but keep compiler quiet */
296 : : return ACL_NO_RIGHTS;
297 : : }
298 : :
299 : : /*
300 : : * If we found no grant options, consider whether to issue a hard error.
301 : : * Per spec, having any privilege at all on the object will get you by
302 : : * here.
303 : : */
6709 alvherre@alvh.no-ip. 304 [ + + ]:CBC 16234 : if (avail_goptions == ACL_NO_RIGHTS)
305 : : {
2325 peter_e@gmx.net 306 [ + + ]: 33 : if (pg_aclmask(objtype, objectId, att_number, grantorId,
6709 alvherre@alvh.no-ip. 307 : 33 : whole_mask | ACL_GRANT_OPTION_FOR(whole_mask),
308 : : ACLMASK_ANY) == ACL_NO_RIGHTS)
309 : : {
2325 peter_e@gmx.net 310 [ - + - - ]: 15 : if (objtype == OBJECT_COLUMN && colname)
2325 peter_e@gmx.net 311 :UBC 0 : aclcheck_error_col(ACLCHECK_NO_PRIV, objtype, objname, colname);
312 : : else
2325 peter_e@gmx.net 313 :CBC 15 : aclcheck_error(ACLCHECK_NO_PRIV, objtype, objname);
314 : : }
315 : : }
316 : :
317 : : /*
318 : : * Restrict the operation to what we can actually grant or revoke, and
319 : : * issue a warning if appropriate. (For REVOKE this isn't quite what the
320 : : * spec says to do: the spec seems to want a warning only if no privilege
321 : : * bits actually change in the ACL. In practice that behavior seems much
322 : : * too noisy, as well as inconsistent with the GRANT case.)
323 : : */
6709 alvherre@alvh.no-ip. 324 : 16219 : this_privileges = privileges & ACL_OPTION_TO_PRIVS(avail_goptions);
325 [ + + ]: 16219 : if (is_grant)
326 : : {
327 [ + + ]: 4845 : if (this_privileges == 0)
328 : : {
2325 peter_e@gmx.net 329 [ - + - - ]: 15 : if (objtype == OBJECT_COLUMN && colname)
5153 tgl@sss.pgh.pa.us 330 [ # # ]:UBC 0 : ereport(WARNING,
331 : : (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
332 : : errmsg("no privileges were granted for column \"%s\" of relation \"%s\"",
333 : : colname, objname)));
334 : : else
5153 tgl@sss.pgh.pa.us 335 [ + - ]:CBC 15 : ereport(WARNING,
336 : : (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
337 : : errmsg("no privileges were granted for \"%s\"",
338 : : objname)));
339 : : }
6709 alvherre@alvh.no-ip. 340 [ + + - + ]: 4830 : else if (!all_privs && this_privileges != privileges)
341 : : {
2325 peter_e@gmx.net 342 [ # # # # ]:UBC 0 : if (objtype == OBJECT_COLUMN && colname)
5153 tgl@sss.pgh.pa.us 343 [ # # ]: 0 : ereport(WARNING,
344 : : (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
345 : : errmsg("not all privileges were granted for column \"%s\" of relation \"%s\"",
346 : : colname, objname)));
347 : : else
348 [ # # ]: 0 : ereport(WARNING,
349 : : (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
350 : : errmsg("not all privileges were granted for \"%s\"",
351 : : objname)));
352 : : }
353 : : }
354 : : else
355 : : {
6709 alvherre@alvh.no-ip. 356 [ + + ]:CBC 11374 : if (this_privileges == 0)
357 : : {
2325 peter_e@gmx.net 358 [ - + - - ]: 3 : if (objtype == OBJECT_COLUMN && colname)
5153 tgl@sss.pgh.pa.us 359 [ # # ]:UBC 0 : ereport(WARNING,
360 : : (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
361 : : errmsg("no privileges could be revoked for column \"%s\" of relation \"%s\"",
362 : : colname, objname)));
363 : : else
5153 tgl@sss.pgh.pa.us 364 [ + - ]:CBC 3 : ereport(WARNING,
365 : : (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
366 : : errmsg("no privileges could be revoked for \"%s\"",
367 : : objname)));
368 : : }
6709 alvherre@alvh.no-ip. 369 [ + + - + ]: 11371 : else if (!all_privs && this_privileges != privileges)
370 : : {
2325 peter_e@gmx.net 371 [ # # # # ]:UBC 0 : if (objtype == OBJECT_COLUMN && colname)
5153 tgl@sss.pgh.pa.us 372 [ # # ]: 0 : ereport(WARNING,
373 : : (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
374 : : errmsg("not all privileges could be revoked for column \"%s\" of relation \"%s\"",
375 : : colname, objname)));
376 : : else
377 [ # # ]: 0 : ereport(WARNING,
378 : : (errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
379 : : errmsg("not all privileges could be revoked for \"%s\"",
380 : : objname)));
381 : : }
382 : : }
383 : :
6709 alvherre@alvh.no-ip. 384 :CBC 16219 : return this_privileges;
385 : : }
386 : :
387 : : /*
388 : : * Called to execute the utility commands GRANT and REVOKE
389 : : */
390 : : void
8345 peter_e@gmx.net 391 : 7536 : ExecuteGrantStmt(GrantStmt *stmt)
392 : : {
393 : : InternalGrant istmt;
394 : : ListCell *cell;
395 : : const char *errormsg;
396 : : AclMode all_privileges;
397 : :
1170 peter@eisentraut.org 398 [ + + ]: 7536 : if (stmt->grantor)
399 : : {
400 : : Oid grantor;
401 : :
402 : 9 : grantor = get_rolespec_oid(stmt->grantor, false);
403 : :
404 : : /*
405 : : * Currently, this clause is only for SQL compatibility, not very
406 : : * interesting otherwise.
407 : : */
408 [ + + ]: 9 : if (grantor != GetUserId())
409 [ + - ]: 3 : ereport(ERROR,
410 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
411 : : errmsg("grantor must be current user")));
412 : : }
413 : :
414 : : /*
415 : : * Turn the regular GrantStmt into the InternalGrant form.
416 : : */
6709 alvherre@alvh.no-ip. 417 : 7533 : istmt.is_grant = stmt->is_grant;
418 : 7533 : istmt.objtype = stmt->objtype;
419 : :
420 : : /* Collect the OIDs of the target objects */
5298 tgl@sss.pgh.pa.us 421 [ + + - ]: 7533 : switch (stmt->targtype)
422 : : {
423 : 7518 : case ACL_TARGET_OBJECT:
739 424 : 15023 : istmt.objects = objectNamesToOids(stmt->objtype, stmt->objects,
425 : 7518 : stmt->is_grant);
5298 426 : 7505 : break;
427 : 15 : case ACL_TARGET_ALL_IN_SCHEMA:
428 : 15 : istmt.objects = objectsInSchemaToOids(stmt->objtype, stmt->objects);
429 : 15 : break;
430 : : /* ACL_TARGET_DEFAULTS should not be seen here */
5298 tgl@sss.pgh.pa.us 431 :UBC 0 : default:
432 [ # # ]: 0 : elog(ERROR, "unrecognized GrantStmt.targtype: %d",
433 : : (int) stmt->targtype);
434 : : }
435 : :
436 : : /* all_privs to be filled below */
437 : : /* privileges to be filled below */
5561 tgl@sss.pgh.pa.us 438 :CBC 7520 : istmt.col_privs = NIL; /* may get filled below */
439 : 7520 : istmt.grantees = NIL; /* filled below */
6709 alvherre@alvh.no-ip. 440 : 7520 : istmt.grant_option = stmt->grant_option;
441 : 7520 : istmt.behavior = stmt->behavior;
442 : :
443 : : /*
444 : : * Convert the RoleSpec list into an Oid list. Note that at this point we
445 : : * insert an ACL_ID_PUBLIC into the list if appropriate, so downstream
446 : : * there shouldn't be any additional work needed to support this case.
447 : : */
6719 448 [ + - + + : 15085 : foreach(cell, stmt->grantees)
+ + ]
449 : : {
3249 bruce@momjian.us 450 : 7568 : RoleSpec *grantee = (RoleSpec *) lfirst(cell);
451 : : Oid grantee_uid;
452 : :
3324 alvherre@alvh.no-ip. 453 [ + + ]: 7568 : switch (grantee->roletype)
454 : : {
455 : 5807 : case ROLESPEC_PUBLIC:
456 : 5807 : grantee_uid = ACL_ID_PUBLIC;
457 : 5807 : break;
458 : 1761 : default:
2664 peter_e@gmx.net 459 : 1761 : grantee_uid = get_rolespec_oid(grantee, false);
3324 alvherre@alvh.no-ip. 460 : 1758 : break;
461 : : }
462 : 7565 : istmt.grantees = lappend_oid(istmt.grantees, grantee_uid);
463 : : }
464 : :
465 : : /*
466 : : * Convert stmt->privileges, a list of AccessPriv nodes, into an AclMode
467 : : * bitmask. Note: objtype can't be OBJECT_COLUMN.
468 : : */
8029 tgl@sss.pgh.pa.us 469 [ + + + + : 7517 : switch (stmt->objtype)
+ + + + +
+ - + + +
+ - ]
470 : : {
2377 peter_e@gmx.net 471 : 3819 : case OBJECT_TABLE:
472 : :
473 : : /*
474 : : * Because this might be a sequence, we test both relation and
475 : : * sequence bits, and later do a more limited test when we know
476 : : * the object type.
477 : : */
6658 bruce@momjian.us 478 : 3819 : all_privileges = ACL_ALL_RIGHTS_RELATION | ACL_ALL_RIGHTS_SEQUENCE;
5865 tgl@sss.pgh.pa.us 479 : 3819 : errormsg = gettext_noop("invalid privilege type %s for relation");
6658 bruce@momjian.us 480 : 3819 : break;
2377 peter_e@gmx.net 481 : 8 : case OBJECT_SEQUENCE:
6658 bruce@momjian.us 482 : 8 : all_privileges = ACL_ALL_RIGHTS_SEQUENCE;
5865 tgl@sss.pgh.pa.us 483 : 8 : errormsg = gettext_noop("invalid privilege type %s for sequence");
8029 484 : 8 : break;
2377 peter_e@gmx.net 485 : 131 : case OBJECT_DATABASE:
6719 alvherre@alvh.no-ip. 486 : 131 : all_privileges = ACL_ALL_RIGHTS_DATABASE;
5865 tgl@sss.pgh.pa.us 487 : 131 : errormsg = gettext_noop("invalid privilege type %s for database");
8091 peter_e@gmx.net 488 : 131 : break;
2377 489 : 13 : case OBJECT_DOMAIN:
4499 490 : 13 : all_privileges = ACL_ALL_RIGHTS_TYPE;
491 : 13 : errormsg = gettext_noop("invalid privilege type %s for domain");
492 : 13 : break;
2377 493 : 3150 : case OBJECT_FUNCTION:
6719 alvherre@alvh.no-ip. 494 : 3150 : all_privileges = ACL_ALL_RIGHTS_FUNCTION;
5865 tgl@sss.pgh.pa.us 495 : 3150 : errormsg = gettext_noop("invalid privilege type %s for function");
8091 peter_e@gmx.net 496 : 3150 : break;
2377 497 : 21 : case OBJECT_LANGUAGE:
6719 alvherre@alvh.no-ip. 498 : 21 : all_privileges = ACL_ALL_RIGHTS_LANGUAGE;
5865 tgl@sss.pgh.pa.us 499 : 21 : errormsg = gettext_noop("invalid privilege type %s for language");
8029 500 : 21 : break;
2377 peter_e@gmx.net 501 : 31 : case OBJECT_LARGEOBJECT:
5238 itagaki.takahiro@gma 502 : 31 : all_privileges = ACL_ALL_RIGHTS_LARGEOBJECT;
503 : 31 : errormsg = gettext_noop("invalid privilege type %s for large object");
504 : 31 : break;
2377 peter_e@gmx.net 505 : 141 : case OBJECT_SCHEMA:
506 : 141 : all_privileges = ACL_ALL_RIGHTS_SCHEMA;
5865 tgl@sss.pgh.pa.us 507 : 141 : errormsg = gettext_noop("invalid privilege type %s for schema");
8091 peter_e@gmx.net 508 : 141 : break;
2377 509 : 24 : case OBJECT_PROCEDURE:
2327 510 : 24 : all_privileges = ACL_ALL_RIGHTS_FUNCTION;
511 : 24 : errormsg = gettext_noop("invalid privilege type %s for procedure");
512 : 24 : break;
2377 513 : 3 : case OBJECT_ROUTINE:
2327 514 : 3 : all_privileges = ACL_ALL_RIGHTS_FUNCTION;
515 : 3 : errormsg = gettext_noop("invalid privilege type %s for routine");
516 : 3 : break;
2377 peter_e@gmx.net 517 :UBC 0 : case OBJECT_TABLESPACE:
6719 alvherre@alvh.no-ip. 518 : 0 : all_privileges = ACL_ALL_RIGHTS_TABLESPACE;
5865 tgl@sss.pgh.pa.us 519 : 0 : errormsg = gettext_noop("invalid privilege type %s for tablespace");
7240 520 : 0 : break;
2377 peter_e@gmx.net 521 :CBC 55 : case OBJECT_TYPE:
4499 522 : 55 : all_privileges = ACL_ALL_RIGHTS_TYPE;
523 : 55 : errormsg = gettext_noop("invalid privilege type %s for type");
524 : 55 : break;
2377 525 : 46 : case OBJECT_FDW:
5595 526 : 46 : all_privileges = ACL_ALL_RIGHTS_FDW;
527 : 46 : errormsg = gettext_noop("invalid privilege type %s for foreign-data wrapper");
528 : 46 : break;
2377 529 : 39 : case OBJECT_FOREIGN_SERVER:
5595 530 : 39 : all_privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
531 : 39 : errormsg = gettext_noop("invalid privilege type %s for foreign server");
532 : 39 : break;
739 tgl@sss.pgh.pa.us 533 : 36 : case OBJECT_PARAMETER_ACL:
534 : 36 : all_privileges = ACL_ALL_RIGHTS_PARAMETER_ACL;
535 : 36 : errormsg = gettext_noop("invalid privilege type %s for parameter");
536 : 36 : break;
8091 peter_e@gmx.net 537 :UBC 0 : default:
5305 tgl@sss.pgh.pa.us 538 [ # # ]: 0 : elog(ERROR, "unrecognized GrantStmt.objtype: %d",
539 : : (int) stmt->objtype);
540 : : /* keep compiler quiet */
541 : : all_privileges = ACL_NO_RIGHTS;
542 : : errormsg = NULL;
543 : : }
544 : :
6865 tgl@sss.pgh.pa.us 545 [ + + ]:CBC 7517 : if (stmt->privileges == NIL)
546 : : {
6709 alvherre@alvh.no-ip. 547 : 1000 : istmt.all_privs = true;
548 : :
549 : : /*
550 : : * will be turned into ACL_ALL_RIGHTS_* by the internal routines
551 : : * depending on the object type
552 : : */
553 : 1000 : istmt.privileges = ACL_NO_RIGHTS;
554 : : }
555 : : else
556 : : {
557 : 6517 : istmt.all_privs = false;
558 : 6517 : istmt.privileges = ACL_NO_RIGHTS;
559 : :
6719 560 [ + - + + : 13234 : foreach(cell, stmt->privileges)
+ + ]
561 : : {
5561 tgl@sss.pgh.pa.us 562 : 6729 : AccessPriv *privnode = (AccessPriv *) lfirst(cell);
563 : : AclMode priv;
564 : :
565 : : /*
566 : : * If it's a column-level specification, we just set it aside in
567 : : * col_privs for the moment; but insist it's for a relation.
568 : : */
569 [ + + ]: 6729 : if (privnode->cols)
570 : : {
2377 peter_e@gmx.net 571 [ - + ]: 204 : if (stmt->objtype != OBJECT_TABLE)
5561 tgl@sss.pgh.pa.us 572 [ # # ]:UBC 0 : ereport(ERROR,
573 : : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
574 : : errmsg("column privileges are only valid for relations")));
5561 tgl@sss.pgh.pa.us 575 :CBC 204 : istmt.col_privs = lappend(istmt.col_privs, privnode);
576 : 204 : continue;
577 : : }
578 : :
5421 bruce@momjian.us 579 [ - + ]: 6525 : if (privnode->priv_name == NULL) /* parser mistake? */
5561 tgl@sss.pgh.pa.us 580 [ # # ]:UBC 0 : elog(ERROR, "AccessPriv node must specify privilege or columns");
5561 tgl@sss.pgh.pa.us 581 :CBC 6525 : priv = string_to_privilege(privnode->priv_name);
582 : :
6719 alvherre@alvh.no-ip. 583 [ + + ]: 6525 : if (priv & ~((AclMode) all_privileges))
7573 tgl@sss.pgh.pa.us 584 [ + - ]: 12 : ereport(ERROR,
585 : : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
586 : : errmsg(errormsg, privilege_to_string(priv))));
587 : :
6709 alvherre@alvh.no-ip. 588 : 6513 : istmt.privileges |= priv;
589 : : }
590 : : }
591 : :
592 : 7505 : ExecGrantStmt_oids(&istmt);
6719 593 : 7472 : }
594 : :
595 : : /*
596 : : * ExecGrantStmt_oids
597 : : *
598 : : * Internal entry point for granting and revoking privileges.
599 : : */
600 : : static void
6709 601 : 7611 : ExecGrantStmt_oids(InternalGrant *istmt)
602 : : {
603 [ + + + + : 7611 : switch (istmt->objtype)
+ + + + +
- + - ]
604 : : {
2377 peter_e@gmx.net 605 : 3873 : case OBJECT_TABLE:
606 : : case OBJECT_SEQUENCE:
6709 alvherre@alvh.no-ip. 607 : 3873 : ExecGrant_Relation(istmt);
6719 608 : 3870 : break;
2377 peter_e@gmx.net 609 : 136 : case OBJECT_DATABASE:
488 peter@eisentraut.org 610 : 136 : ExecGrant_common(istmt, DatabaseRelationId, ACL_ALL_RIGHTS_DATABASE, NULL);
6719 alvherre@alvh.no-ip. 611 : 136 : break;
2377 peter_e@gmx.net 612 : 70 : case OBJECT_DOMAIN:
613 : : case OBJECT_TYPE:
488 peter@eisentraut.org 614 : 70 : ExecGrant_common(istmt, TypeRelationId, ACL_ALL_RIGHTS_TYPE, ExecGrant_Type_check);
4499 peter_e@gmx.net 615 : 58 : break;
2377 616 : 47 : case OBJECT_FDW:
488 peter@eisentraut.org 617 : 47 : ExecGrant_common(istmt, ForeignDataWrapperRelationId, ACL_ALL_RIGHTS_FDW, NULL);
5595 peter_e@gmx.net 618 : 38 : break;
2377 619 : 46 : case OBJECT_FOREIGN_SERVER:
488 peter@eisentraut.org 620 : 46 : ExecGrant_common(istmt, ForeignServerRelationId, ACL_ALL_RIGHTS_FOREIGN_SERVER, NULL);
5595 peter_e@gmx.net 621 : 40 : break;
2377 622 : 3185 : case OBJECT_FUNCTION:
623 : : case OBJECT_PROCEDURE:
624 : : case OBJECT_ROUTINE:
488 peter@eisentraut.org 625 : 3185 : ExecGrant_common(istmt, ProcedureRelationId, ACL_ALL_RIGHTS_FUNCTION, NULL);
6719 alvherre@alvh.no-ip. 626 : 3185 : break;
2377 peter_e@gmx.net 627 : 21 : case OBJECT_LANGUAGE:
488 peter@eisentraut.org 628 : 21 : ExecGrant_common(istmt, LanguageRelationId, ACL_ALL_RIGHTS_LANGUAGE, ExecGrant_Language_check);
6719 alvherre@alvh.no-ip. 629 : 18 : break;
2377 peter_e@gmx.net 630 : 37 : case OBJECT_LARGEOBJECT:
5238 itagaki.takahiro@gma 631 : 37 : ExecGrant_Largeobject(istmt);
632 : 37 : break;
2377 peter_e@gmx.net 633 : 148 : case OBJECT_SCHEMA:
488 peter@eisentraut.org 634 : 148 : ExecGrant_common(istmt, NamespaceRelationId, ACL_ALL_RIGHTS_SCHEMA, NULL);
6719 alvherre@alvh.no-ip. 635 : 148 : break;
2377 peter_e@gmx.net 636 :UBC 0 : case OBJECT_TABLESPACE:
488 peter@eisentraut.org 637 : 0 : ExecGrant_common(istmt, TableSpaceRelationId, ACL_ALL_RIGHTS_TABLESPACE, NULL);
6719 alvherre@alvh.no-ip. 638 : 0 : break;
739 tgl@sss.pgh.pa.us 639 :CBC 48 : case OBJECT_PARAMETER_ACL:
640 : 48 : ExecGrant_Parameter(istmt);
641 : 48 : break;
6719 alvherre@alvh.no-ip. 642 :UBC 0 : default:
643 [ # # ]: 0 : elog(ERROR, "unrecognized GrantStmt.objtype: %d",
644 : : (int) istmt->objtype);
645 : : }
646 : :
647 : : /*
648 : : * Pass the info to event triggers about the just-executed GRANT. Note
649 : : * that we prefer to do it after actually executing it, because that gives
650 : : * the functions a chance to adjust the istmt with privileges actually
651 : : * granted.
652 : : */
2377 peter_e@gmx.net 653 [ + + ]:CBC 7578 : if (EventTriggerSupportsObjectType(istmt->objtype))
3261 alvherre@alvh.no-ip. 654 : 7394 : EventTriggerCollectGrant(istmt);
6719 655 : 7578 : }
656 : :
657 : : /*
658 : : * objectNamesToOids
659 : : *
660 : : * Turn a list of object names of a given type into an Oid list.
661 : : *
662 : : * XXX: This function doesn't take any sort of locks on the objects whose
663 : : * names it looks up. In the face of concurrent DDL, we might easily latch
664 : : * onto an old version of an object, causing the GRANT or REVOKE statement
665 : : * to fail.
666 : : */
667 : : static List *
739 tgl@sss.pgh.pa.us 668 : 7518 : objectNamesToOids(ObjectType objtype, List *objnames, bool is_grant)
669 : : {
6718 bruce@momjian.us 670 : 7518 : List *objects = NIL;
671 : : ListCell *cell;
672 : :
6719 alvherre@alvh.no-ip. 673 [ - + ]: 7518 : Assert(objnames != NIL);
674 : :
675 [ + + + + : 7518 : switch (objtype)
+ + + + -
- + + +
- ]
676 : : {
2377 peter_e@gmx.net 677 : 3821 : case OBJECT_TABLE:
678 : : case OBJECT_SEQUENCE:
6719 alvherre@alvh.no-ip. 679 [ + - + + : 7669 : foreach(cell, objnames)
+ + ]
680 : : {
681 : 3848 : RangeVar *relvar = (RangeVar *) lfirst(cell);
682 : : Oid relOid;
683 : :
4519 rhaas@postgresql.org 684 : 3848 : relOid = RangeVarGetRelid(relvar, NoLock, false);
6719 alvherre@alvh.no-ip. 685 : 3848 : objects = lappend_oid(objects, relOid);
686 : : }
687 : 3821 : break;
2377 peter_e@gmx.net 688 : 131 : case OBJECT_DATABASE:
6719 alvherre@alvh.no-ip. 689 [ + - + + : 262 : foreach(cell, objnames)
+ + ]
690 : : {
691 : 131 : char *dbname = strVal(lfirst(cell));
692 : : Oid dbid;
693 : :
5001 rhaas@postgresql.org 694 : 131 : dbid = get_database_oid(dbname, false);
6556 tgl@sss.pgh.pa.us 695 : 131 : objects = lappend_oid(objects, dbid);
696 : : }
6719 alvherre@alvh.no-ip. 697 : 131 : break;
2377 peter_e@gmx.net 698 : 68 : case OBJECT_DOMAIN:
699 : : case OBJECT_TYPE:
4499 700 [ + - + + : 136 : foreach(cell, objnames)
+ + ]
701 : : {
702 : 68 : List *typname = (List *) lfirst(cell);
703 : : Oid oid;
704 : :
705 : 68 : oid = typenameTypeId(NULL, makeTypeNameFromNameList(typname));
706 : 68 : objects = lappend_oid(objects, oid);
707 : : }
708 : 68 : break;
2377 709 : 3153 : case OBJECT_FUNCTION:
6719 alvherre@alvh.no-ip. 710 [ + - + + : 6312 : foreach(cell, objnames)
+ + ]
711 : : {
2664 peter_e@gmx.net 712 : 3165 : ObjectWithArgs *func = (ObjectWithArgs *) lfirst(cell);
713 : : Oid funcid;
714 : :
2327 715 : 3165 : funcid = LookupFuncWithArgs(OBJECT_FUNCTION, func, false);
6719 alvherre@alvh.no-ip. 716 : 3159 : objects = lappend_oid(objects, funcid);
717 : : }
718 : 3147 : break;
2377 peter_e@gmx.net 719 : 21 : case OBJECT_LANGUAGE:
6719 alvherre@alvh.no-ip. 720 [ + - + + : 42 : foreach(cell, objnames)
+ + ]
721 : : {
6718 bruce@momjian.us 722 : 21 : char *langname = strVal(lfirst(cell));
723 : : Oid oid;
724 : :
5001 rhaas@postgresql.org 725 : 21 : oid = get_language_oid(langname, false);
726 : 21 : objects = lappend_oid(objects, oid);
727 : : }
6719 alvherre@alvh.no-ip. 728 : 21 : break;
2377 peter_e@gmx.net 729 : 40 : case OBJECT_LARGEOBJECT:
5238 itagaki.takahiro@gma 730 [ + - + + : 77 : foreach(cell, objnames)
+ + ]
731 : : {
5054 rhaas@postgresql.org 732 : 43 : Oid lobjOid = oidparse(lfirst(cell));
733 : :
5238 itagaki.takahiro@gma 734 [ + + ]: 43 : if (!LargeObjectExists(lobjOid))
735 [ + - ]: 6 : ereport(ERROR,
736 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
737 : : errmsg("large object %u does not exist",
738 : : lobjOid)));
739 : :
740 : 37 : objects = lappend_oid(objects, lobjOid);
741 : : }
742 : 34 : break;
2377 peter_e@gmx.net 743 : 141 : case OBJECT_SCHEMA:
6718 bruce@momjian.us 744 [ + - + + : 322 : foreach(cell, objnames)
+ + ]
745 : : {
6719 alvherre@alvh.no-ip. 746 : 181 : char *nspname = strVal(lfirst(cell));
747 : : Oid oid;
748 : :
5001 rhaas@postgresql.org 749 : 181 : oid = get_namespace_oid(nspname, false);
750 : 181 : objects = lappend_oid(objects, oid);
751 : : }
6719 alvherre@alvh.no-ip. 752 : 141 : break;
2377 peter_e@gmx.net 753 : 21 : case OBJECT_PROCEDURE:
2327 754 [ + - + + : 42 : foreach(cell, objnames)
+ + ]
755 : : {
756 : 21 : ObjectWithArgs *func = (ObjectWithArgs *) lfirst(cell);
757 : : Oid procid;
758 : :
759 : 21 : procid = LookupFuncWithArgs(OBJECT_PROCEDURE, func, false);
760 : 21 : objects = lappend_oid(objects, procid);
761 : : }
762 : 21 : break;
2377 peter_e@gmx.net 763 :UBC 0 : case OBJECT_ROUTINE:
2327 764 [ # # # # : 0 : foreach(cell, objnames)
# # ]
765 : : {
766 : 0 : ObjectWithArgs *func = (ObjectWithArgs *) lfirst(cell);
767 : : Oid routid;
768 : :
769 : 0 : routid = LookupFuncWithArgs(OBJECT_ROUTINE, func, false);
770 : 0 : objects = lappend_oid(objects, routid);
771 : : }
772 : 0 : break;
2377 773 : 0 : case OBJECT_TABLESPACE:
6718 bruce@momjian.us 774 [ # # # # : 0 : foreach(cell, objnames)
# # ]
775 : : {
776 : 0 : char *spcname = strVal(lfirst(cell));
777 : : Oid spcoid;
778 : :
5001 rhaas@postgresql.org 779 : 0 : spcoid = get_tablespace_oid(spcname, false);
780 : 0 : objects = lappend_oid(objects, spcoid);
781 : : }
6719 alvherre@alvh.no-ip. 782 : 0 : break;
2377 peter_e@gmx.net 783 :CBC 46 : case OBJECT_FDW:
5595 784 [ + - + + : 92 : foreach(cell, objnames)
+ + ]
785 : : {
5421 bruce@momjian.us 786 : 46 : char *fdwname = strVal(lfirst(cell));
4762 rhaas@postgresql.org 787 : 46 : Oid fdwid = get_foreign_data_wrapper_oid(fdwname, false);
788 : :
5595 peter_e@gmx.net 789 : 46 : objects = lappend_oid(objects, fdwid);
790 : : }
791 : 46 : break;
2377 792 : 39 : case OBJECT_FOREIGN_SERVER:
5595 793 [ + - + + : 78 : foreach(cell, objnames)
+ + ]
794 : : {
5421 bruce@momjian.us 795 : 39 : char *srvname = strVal(lfirst(cell));
4762 rhaas@postgresql.org 796 : 39 : Oid srvid = get_foreign_server_oid(srvname, false);
797 : :
5595 peter_e@gmx.net 798 : 39 : objects = lappend_oid(objects, srvid);
799 : : }
800 : 39 : break;
739 tgl@sss.pgh.pa.us 801 : 37 : case OBJECT_PARAMETER_ACL:
802 [ + - + + : 98 : foreach(cell, objnames)
+ + ]
803 : : {
804 : : /*
805 : : * In this code we represent a GUC by the OID of its entry in
806 : : * pg_parameter_acl, which we have to manufacture here if it
807 : : * doesn't exist yet. (That's a hack for sure, but it avoids
808 : : * messing with all the GRANT/REVOKE infrastructure that
809 : : * expects to use OIDs for object identities.) However, if
810 : : * this is a REVOKE, we can instead just ignore any GUCs that
811 : : * don't have such an entry, as they must not have any
812 : : * privileges needing removal.
813 : : */
814 : 62 : char *parameter = strVal(lfirst(cell));
815 : 62 : Oid parameterId = ParameterAclLookup(parameter, true);
816 : :
817 [ + + + + ]: 62 : if (!OidIsValid(parameterId) && is_grant)
818 : : {
819 : 34 : parameterId = ParameterAclCreate(parameter);
820 : :
821 : : /*
822 : : * Prevent error when processing duplicate objects, and
823 : : * make this new entry visible so that ExecGrant_Parameter
824 : : * can update it.
825 : : */
826 : 33 : CommandCounterIncrement();
827 : : }
828 [ + + ]: 61 : if (OidIsValid(parameterId))
829 : 55 : objects = lappend_oid(objects, parameterId);
830 : : }
831 : 36 : break;
6719 alvherre@alvh.no-ip. 832 :UBC 0 : default:
833 [ # # ]: 0 : elog(ERROR, "unrecognized GrantStmt.objtype: %d",
834 : : (int) objtype);
835 : : }
836 : :
6719 alvherre@alvh.no-ip. 837 :CBC 7505 : return objects;
838 : : }
839 : :
840 : : /*
841 : : * objectsInSchemaToOids
842 : : *
843 : : * Find all objects of a given type in specified schemas, and make a list
844 : : * of their Oids. We check USAGE privilege on the schemas, but there is
845 : : * no privilege checking on the individual objects here.
846 : : */
847 : : static List *
2377 peter_e@gmx.net 848 : 15 : objectsInSchemaToOids(ObjectType objtype, List *nspnames)
849 : : {
5298 tgl@sss.pgh.pa.us 850 : 15 : List *objects = NIL;
851 : : ListCell *cell;
852 : :
853 [ + - + + : 30 : foreach(cell, nspnames)
+ + ]
854 : : {
855 : 15 : char *nspname = strVal(lfirst(cell));
856 : : Oid namespaceId;
857 : : List *objs;
858 : :
4096 bruce@momjian.us 859 : 15 : namespaceId = LookupExplicitNamespace(nspname, false);
860 : :
5298 tgl@sss.pgh.pa.us 861 [ + - + - ]: 15 : switch (objtype)
862 : : {
2377 peter_e@gmx.net 863 : 6 : case OBJECT_TABLE:
5298 tgl@sss.pgh.pa.us 864 : 6 : objs = getRelationsInNamespace(namespaceId, RELKIND_RELATION);
865 : 6 : objects = list_concat(objects, objs);
866 : 6 : objs = getRelationsInNamespace(namespaceId, RELKIND_VIEW);
867 : 6 : objects = list_concat(objects, objs);
4060 kgrittn@postgresql.o 868 : 6 : objs = getRelationsInNamespace(namespaceId, RELKIND_MATVIEW);
869 : 6 : objects = list_concat(objects, objs);
4852 rhaas@postgresql.org 870 : 6 : objs = getRelationsInNamespace(namespaceId, RELKIND_FOREIGN_TABLE);
871 : 6 : objects = list_concat(objects, objs);
2685 872 : 6 : objs = getRelationsInNamespace(namespaceId, RELKIND_PARTITIONED_TABLE);
873 : 6 : objects = list_concat(objects, objs);
5298 tgl@sss.pgh.pa.us 874 : 6 : break;
2377 peter_e@gmx.net 875 :UBC 0 : case OBJECT_SEQUENCE:
5298 tgl@sss.pgh.pa.us 876 : 0 : objs = getRelationsInNamespace(namespaceId, RELKIND_SEQUENCE);
877 : 0 : objects = list_concat(objects, objs);
878 : 0 : break;
2377 peter_e@gmx.net 879 :CBC 9 : case OBJECT_FUNCTION:
880 : : case OBJECT_PROCEDURE:
881 : : case OBJECT_ROUTINE:
882 : : {
883 : : ScanKeyData key[2];
884 : : int keycount;
885 : : Relation rel;
886 : : TableScanDesc scan;
887 : : HeapTuple tuple;
888 : :
2327 889 : 9 : keycount = 0;
890 : 9 : ScanKeyInit(&key[keycount++],
891 : : Anum_pg_proc_pronamespace,
892 : : BTEqualStrategyNumber, F_OIDEQ,
893 : : ObjectIdGetDatum(namespaceId));
894 : :
2377 895 [ + + ]: 9 : if (objtype == OBJECT_FUNCTION)
896 : : /* includes aggregates and window functions */
2327 897 : 3 : ScanKeyInit(&key[keycount++],
898 : : Anum_pg_proc_prokind,
899 : : BTEqualStrategyNumber, F_CHARNE,
900 : : CharGetDatum(PROKIND_PROCEDURE));
2377 901 [ + + ]: 6 : else if (objtype == OBJECT_PROCEDURE)
2327 902 : 3 : ScanKeyInit(&key[keycount++],
903 : : Anum_pg_proc_prokind,
904 : : BTEqualStrategyNumber, F_CHAREQ,
905 : : CharGetDatum(PROKIND_PROCEDURE));
906 : :
1910 andres@anarazel.de 907 : 9 : rel = table_open(ProcedureRelationId, AccessShareLock);
1861 908 : 9 : scan = table_beginscan_catalog(rel, keycount, key);
909 : :
5298 tgl@sss.pgh.pa.us 910 [ + + ]: 27 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
911 : : {
1789 912 : 18 : Oid oid = ((Form_pg_proc) GETSTRUCT(tuple))->oid;
913 : :
1972 andres@anarazel.de 914 : 18 : objects = lappend_oid(objects, oid);
915 : : }
916 : :
1861 917 : 9 : table_endscan(scan);
1910 918 : 9 : table_close(rel, AccessShareLock);
919 : : }
5298 tgl@sss.pgh.pa.us 920 : 9 : break;
5298 tgl@sss.pgh.pa.us 921 :UBC 0 : default:
922 : : /* should not happen */
923 [ # # ]: 0 : elog(ERROR, "unrecognized GrantStmt.objtype: %d",
924 : : (int) objtype);
925 : : }
926 : : }
927 : :
5298 tgl@sss.pgh.pa.us 928 :CBC 15 : return objects;
929 : : }
930 : :
931 : : /*
932 : : * getRelationsInNamespace
933 : : *
934 : : * Return Oid list of relations in given namespace filtered by relation kind
935 : : */
936 : : static List *
937 : 30 : getRelationsInNamespace(Oid namespaceId, char relkind)
938 : : {
939 : 30 : List *relations = NIL;
940 : : ScanKeyData key[2];
941 : : Relation rel;
942 : : TableScanDesc scan;
943 : : HeapTuple tuple;
944 : :
945 : 30 : ScanKeyInit(&key[0],
946 : : Anum_pg_class_relnamespace,
947 : : BTEqualStrategyNumber, F_OIDEQ,
948 : : ObjectIdGetDatum(namespaceId));
949 : 30 : ScanKeyInit(&key[1],
950 : : Anum_pg_class_relkind,
951 : : BTEqualStrategyNumber, F_CHAREQ,
952 : : CharGetDatum(relkind));
953 : :
1910 andres@anarazel.de 954 : 30 : rel = table_open(RelationRelationId, AccessShareLock);
1861 955 : 30 : scan = table_beginscan_catalog(rel, 2, key);
956 : :
5298 tgl@sss.pgh.pa.us 957 [ + + ]: 42 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
958 : : {
1789 959 : 12 : Oid oid = ((Form_pg_class) GETSTRUCT(tuple))->oid;
960 : :
1972 andres@anarazel.de 961 : 12 : relations = lappend_oid(relations, oid);
962 : : }
963 : :
1861 964 : 30 : table_endscan(scan);
1910 965 : 30 : table_close(rel, AccessShareLock);
966 : :
5298 tgl@sss.pgh.pa.us 967 : 30 : return relations;
968 : : }
969 : :
970 : :
971 : : /*
972 : : * ALTER DEFAULT PRIVILEGES statement
973 : : */
974 : : void
2777 peter_e@gmx.net 975 : 80 : ExecAlterDefaultPrivilegesStmt(ParseState *pstate, AlterDefaultPrivilegesStmt *stmt)
976 : : {
5305 tgl@sss.pgh.pa.us 977 : 80 : GrantStmt *action = stmt->action;
978 : : InternalDefaultACL iacls;
979 : : ListCell *cell;
3324 alvherre@alvh.no-ip. 980 : 80 : List *rolespecs = NIL;
5305 tgl@sss.pgh.pa.us 981 : 80 : List *nspnames = NIL;
3324 alvherre@alvh.no-ip. 982 : 80 : DefElem *drolespecs = NULL;
5305 tgl@sss.pgh.pa.us 983 : 80 : DefElem *dnspnames = NULL;
984 : : AclMode all_privileges;
985 : : const char *errormsg;
986 : :
987 : : /* Deconstruct the "options" part of the statement */
988 [ + + + + : 141 : foreach(cell, stmt->options)
+ + ]
989 : : {
990 : 61 : DefElem *defel = (DefElem *) lfirst(cell);
991 : :
992 [ + + ]: 61 : if (strcmp(defel->defname, "schemas") == 0)
993 : : {
994 [ - + ]: 27 : if (dnspnames)
1004 dean.a.rasheed@gmail 995 :UBC 0 : errorConflictingDefElem(defel, pstate);
5305 tgl@sss.pgh.pa.us 996 :CBC 27 : dnspnames = defel;
997 : : }
998 [ + - ]: 34 : else if (strcmp(defel->defname, "roles") == 0)
999 : : {
3324 alvherre@alvh.no-ip. 1000 [ - + ]: 34 : if (drolespecs)
1004 dean.a.rasheed@gmail 1001 :UBC 0 : errorConflictingDefElem(defel, pstate);
3324 alvherre@alvh.no-ip. 1002 :CBC 34 : drolespecs = defel;
1003 : : }
1004 : : else
5305 tgl@sss.pgh.pa.us 1005 [ # # ]:UBC 0 : elog(ERROR, "option \"%s\" not recognized", defel->defname);
1006 : : }
1007 : :
5305 tgl@sss.pgh.pa.us 1008 [ + + ]:CBC 80 : if (dnspnames)
1009 : 27 : nspnames = (List *) dnspnames->arg;
3324 alvherre@alvh.no-ip. 1010 [ + + ]: 80 : if (drolespecs)
1011 : 34 : rolespecs = (List *) drolespecs->arg;
1012 : :
1013 : : /* Prepare the InternalDefaultACL representation of the statement */
1014 : : /* roleid to be filled below */
1015 : : /* nspid to be filled in SetDefaultACLsInSchemas */
5305 tgl@sss.pgh.pa.us 1016 : 80 : iacls.is_grant = action->is_grant;
1017 : 80 : iacls.objtype = action->objtype;
1018 : : /* all_privs to be filled below */
1019 : : /* privileges to be filled below */
1020 : 80 : iacls.grantees = NIL; /* filled below */
1021 : 80 : iacls.grant_option = action->grant_option;
1022 : 80 : iacls.behavior = action->behavior;
1023 : :
1024 : : /*
1025 : : * Convert the RoleSpec list into an Oid list. Note that at this point we
1026 : : * insert an ACL_ID_PUBLIC into the list if appropriate, so downstream
1027 : : * there shouldn't be any additional work needed to support this case.
1028 : : */
1029 [ + - + + : 163 : foreach(cell, action->grantees)
+ + ]
1030 : : {
3249 bruce@momjian.us 1031 : 83 : RoleSpec *grantee = (RoleSpec *) lfirst(cell);
1032 : : Oid grantee_uid;
1033 : :
3324 alvherre@alvh.no-ip. 1034 [ + + ]: 83 : switch (grantee->roletype)
1035 : : {
1036 : 20 : case ROLESPEC_PUBLIC:
1037 : 20 : grantee_uid = ACL_ID_PUBLIC;
1038 : 20 : break;
1039 : 63 : default:
2664 peter_e@gmx.net 1040 : 63 : grantee_uid = get_rolespec_oid(grantee, false);
3324 alvherre@alvh.no-ip. 1041 : 63 : break;
1042 : : }
1043 : 83 : iacls.grantees = lappend_oid(iacls.grantees, grantee_uid);
1044 : : }
1045 : :
1046 : : /*
1047 : : * Convert action->privileges, a list of privilege strings, into an
1048 : : * AclMode bitmask.
1049 : : */
5305 tgl@sss.pgh.pa.us 1050 [ + + + - : 80 : switch (action->objtype)
- + + - ]
1051 : : {
2377 peter_e@gmx.net 1052 : 39 : case OBJECT_TABLE:
5305 tgl@sss.pgh.pa.us 1053 : 39 : all_privileges = ACL_ALL_RIGHTS_RELATION;
1054 : 39 : errormsg = gettext_noop("invalid privilege type %s for relation");
1055 : 39 : break;
2377 peter_e@gmx.net 1056 : 3 : case OBJECT_SEQUENCE:
5305 tgl@sss.pgh.pa.us 1057 : 3 : all_privileges = ACL_ALL_RIGHTS_SEQUENCE;
1058 : 3 : errormsg = gettext_noop("invalid privilege type %s for sequence");
1059 : 3 : break;
2377 peter_e@gmx.net 1060 : 11 : case OBJECT_FUNCTION:
5305 tgl@sss.pgh.pa.us 1061 : 11 : all_privileges = ACL_ALL_RIGHTS_FUNCTION;
1062 : 11 : errormsg = gettext_noop("invalid privilege type %s for function");
1063 : 11 : break;
2377 peter_e@gmx.net 1064 :UBC 0 : case OBJECT_PROCEDURE:
2327 1065 : 0 : all_privileges = ACL_ALL_RIGHTS_FUNCTION;
1066 : 0 : errormsg = gettext_noop("invalid privilege type %s for procedure");
1067 : 0 : break;
2377 1068 : 0 : case OBJECT_ROUTINE:
2327 1069 : 0 : all_privileges = ACL_ALL_RIGHTS_FUNCTION;
1070 : 0 : errormsg = gettext_noop("invalid privilege type %s for routine");
1071 : 0 : break;
2377 peter_e@gmx.net 1072 :CBC 9 : case OBJECT_TYPE:
4499 1073 : 9 : all_privileges = ACL_ALL_RIGHTS_TYPE;
1074 : 9 : errormsg = gettext_noop("invalid privilege type %s for type");
1075 : 9 : break;
2377 1076 : 18 : case OBJECT_SCHEMA:
1077 : 18 : all_privileges = ACL_ALL_RIGHTS_SCHEMA;
2574 teodor@sigaev.ru 1078 : 18 : errormsg = gettext_noop("invalid privilege type %s for schema");
1079 : 18 : break;
5305 tgl@sss.pgh.pa.us 1080 :UBC 0 : default:
1081 [ # # ]: 0 : elog(ERROR, "unrecognized GrantStmt.objtype: %d",
1082 : : (int) action->objtype);
1083 : : /* keep compiler quiet */
1084 : : all_privileges = ACL_NO_RIGHTS;
1085 : : errormsg = NULL;
1086 : : }
1087 : :
5305 tgl@sss.pgh.pa.us 1088 [ + + ]:CBC 80 : if (action->privileges == NIL)
1089 : : {
1090 : 28 : iacls.all_privs = true;
1091 : :
1092 : : /*
1093 : : * will be turned into ACL_ALL_RIGHTS_* by the internal routines
1094 : : * depending on the object type
1095 : : */
1096 : 28 : iacls.privileges = ACL_NO_RIGHTS;
1097 : : }
1098 : : else
1099 : : {
1100 : 52 : iacls.all_privs = false;
1101 : 52 : iacls.privileges = ACL_NO_RIGHTS;
1102 : :
1103 [ + - + + : 104 : foreach(cell, action->privileges)
+ + ]
1104 : : {
1105 : 52 : AccessPriv *privnode = (AccessPriv *) lfirst(cell);
1106 : : AclMode priv;
1107 : :
1108 [ - + ]: 52 : if (privnode->cols)
5305 tgl@sss.pgh.pa.us 1109 [ # # ]:UBC 0 : ereport(ERROR,
1110 : : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1111 : : errmsg("default privileges cannot be set for columns")));
1112 : :
5305 tgl@sss.pgh.pa.us 1113 [ - + ]:CBC 52 : if (privnode->priv_name == NULL) /* parser mistake? */
5305 tgl@sss.pgh.pa.us 1114 [ # # ]:UBC 0 : elog(ERROR, "AccessPriv node must specify privilege");
5305 tgl@sss.pgh.pa.us 1115 :CBC 52 : priv = string_to_privilege(privnode->priv_name);
1116 : :
1117 [ - + ]: 52 : if (priv & ~((AclMode) all_privileges))
5305 tgl@sss.pgh.pa.us 1118 [ # # ]:UBC 0 : ereport(ERROR,
1119 : : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1120 : : errmsg(errormsg, privilege_to_string(priv))));
1121 : :
5305 tgl@sss.pgh.pa.us 1122 :CBC 52 : iacls.privileges |= priv;
1123 : : }
1124 : : }
1125 : :
3324 alvherre@alvh.no-ip. 1126 [ + + ]: 80 : if (rolespecs == NIL)
1127 : : {
1128 : : /* Set permissions for myself */
5305 tgl@sss.pgh.pa.us 1129 : 46 : iacls.roleid = GetUserId();
1130 : :
1131 : 46 : SetDefaultACLsInSchemas(&iacls, nspnames);
1132 : : }
1133 : : else
1134 : : {
1135 : : /* Look up the role OIDs and do permissions checks */
1136 : : ListCell *rolecell;
1137 : :
3324 alvherre@alvh.no-ip. 1138 [ + - + + : 68 : foreach(rolecell, rolespecs)
+ + ]
1139 : : {
1140 : 34 : RoleSpec *rolespec = lfirst(rolecell);
1141 : :
2664 peter_e@gmx.net 1142 : 34 : iacls.roleid = get_rolespec_oid(rolespec, false);
1143 : :
573 rhaas@postgresql.org 1144 [ - + ]: 34 : if (!has_privs_of_role(GetUserId(), iacls.roleid))
573 rhaas@postgresql.org 1145 [ # # ]:UBC 0 : ereport(ERROR,
1146 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1147 : : errmsg("permission denied to change default privileges")));
1148 : :
5305 tgl@sss.pgh.pa.us 1149 :CBC 34 : SetDefaultACLsInSchemas(&iacls, nspnames);
1150 : : }
1151 : : }
1152 : 77 : }
1153 : :
1154 : : /*
1155 : : * Process ALTER DEFAULT PRIVILEGES for a list of target schemas
1156 : : *
1157 : : * All fields of *iacls except nspid were filled already
1158 : : */
1159 : : static void
1160 : 80 : SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames)
1161 : : {
1162 [ + + ]: 80 : if (nspnames == NIL)
1163 : : {
1164 : : /* Set database-wide permissions if no schema was specified */
1165 : 53 : iacls->nspid = InvalidOid;
1166 : :
1167 : 53 : SetDefaultACL(iacls);
1168 : : }
1169 : : else
1170 : : {
1171 : : /* Look up the schema OIDs and set permissions for each one */
1172 : : ListCell *nspcell;
1173 : :
1174 [ + - + + : 54 : foreach(nspcell, nspnames)
+ + ]
1175 : : {
1176 : 30 : char *nspname = strVal(lfirst(nspcell));
1177 : :
5001 rhaas@postgresql.org 1178 : 30 : iacls->nspid = get_namespace_oid(nspname, false);
1179 : :
1180 : : /*
1181 : : * We used to insist that the target role have CREATE privileges
1182 : : * on the schema, since without that it wouldn't be able to create
1183 : : * an object for which these default privileges would apply.
1184 : : * However, this check proved to be more confusing than helpful,
1185 : : * and it also caused certain database states to not be
1186 : : * dumpable/restorable, since revoking CREATE doesn't cause
1187 : : * default privileges for the schema to go away. So now, we just
1188 : : * allow the ALTER; if the user lacks CREATE he'll find out when
1189 : : * he tries to create an object.
1190 : : */
1191 : :
5305 tgl@sss.pgh.pa.us 1192 : 30 : SetDefaultACL(iacls);
1193 : : }
1194 : : }
1195 : 77 : }
1196 : :
1197 : :
1198 : : /*
1199 : : * Create or update a pg_default_acl entry
1200 : : */
1201 : : static void
1202 : 98 : SetDefaultACL(InternalDefaultACL *iacls)
1203 : : {
1204 : 98 : AclMode this_privileges = iacls->privileges;
1205 : : char objtype;
1206 : : Relation rel;
1207 : : HeapTuple tuple;
1208 : : bool isNew;
1209 : : Acl *def_acl;
1210 : : Acl *old_acl;
1211 : : Acl *new_acl;
1212 : : HeapTuple newtuple;
1213 : : int noldmembers;
1214 : : int nnewmembers;
1215 : : Oid *oldmembers;
1216 : : Oid *newmembers;
1217 : :
1910 andres@anarazel.de 1218 : 98 : rel = table_open(DefaultAclRelationId, RowExclusiveLock);
1219 : :
1220 : : /*
1221 : : * The default for a global entry is the hard-wired default ACL for the
1222 : : * particular object type. The default for non-global entries is an empty
1223 : : * ACL. This must be so because global entries replace the hard-wired
1224 : : * defaults, while others are added on.
1225 : : */
5123 tgl@sss.pgh.pa.us 1226 [ + + ]: 98 : if (!OidIsValid(iacls->nspid))
1227 : 68 : def_acl = acldefault(iacls->objtype, iacls->roleid);
1228 : : else
1229 : 30 : def_acl = make_empty_acl();
1230 : :
1231 : : /*
1232 : : * Convert ACL object type to pg_default_acl object type and handle
1233 : : * all_privs option
1234 : : */
5305 1235 [ + + + + : 98 : switch (iacls->objtype)
+ - ]
1236 : : {
2377 peter_e@gmx.net 1237 : 45 : case OBJECT_TABLE:
5305 tgl@sss.pgh.pa.us 1238 : 45 : objtype = DEFACLOBJ_RELATION;
1239 [ + + + - ]: 45 : if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
1240 : 13 : this_privileges = ACL_ALL_RIGHTS_RELATION;
1241 : 45 : break;
1242 : :
2377 peter_e@gmx.net 1243 : 6 : case OBJECT_SEQUENCE:
5305 tgl@sss.pgh.pa.us 1244 : 6 : objtype = DEFACLOBJ_SEQUENCE;
1245 [ + - + - ]: 6 : if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
1246 : 6 : this_privileges = ACL_ALL_RIGHTS_SEQUENCE;
1247 : 6 : break;
1248 : :
2377 peter_e@gmx.net 1249 : 14 : case OBJECT_FUNCTION:
5305 tgl@sss.pgh.pa.us 1250 : 14 : objtype = DEFACLOBJ_FUNCTION;
1251 [ + + + - ]: 14 : if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
1252 : 6 : this_privileges = ACL_ALL_RIGHTS_FUNCTION;
1253 : 14 : break;
1254 : :
2377 peter_e@gmx.net 1255 : 12 : case OBJECT_TYPE:
4499 1256 : 12 : objtype = DEFACLOBJ_TYPE;
1257 [ + + + - ]: 12 : if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
1258 : 6 : this_privileges = ACL_ALL_RIGHTS_TYPE;
1259 : 12 : break;
1260 : :
2377 1261 : 21 : case OBJECT_SCHEMA:
2574 teodor@sigaev.ru 1262 [ + + ]: 21 : if (OidIsValid(iacls->nspid))
1263 [ + - ]: 3 : ereport(ERROR,
1264 : : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1265 : : errmsg("cannot use IN SCHEMA clause when using GRANT/REVOKE ON SCHEMAS")));
1266 : 18 : objtype = DEFACLOBJ_NAMESPACE;
1267 [ + + + - ]: 18 : if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
2377 peter_e@gmx.net 1268 : 12 : this_privileges = ACL_ALL_RIGHTS_SCHEMA;
2574 teodor@sigaev.ru 1269 : 18 : break;
1270 : :
5305 tgl@sss.pgh.pa.us 1271 :UBC 0 : default:
523 peter@eisentraut.org 1272 [ # # ]: 0 : elog(ERROR, "unrecognized object type: %d",
1273 : : (int) iacls->objtype);
1274 : : objtype = 0; /* keep compiler quiet */
1275 : : break;
1276 : : }
1277 : :
1278 : : /* Search for existing row for this object type in catalog */
5173 rhaas@postgresql.org 1279 :CBC 95 : tuple = SearchSysCache3(DEFACLROLENSPOBJ,
1280 : : ObjectIdGetDatum(iacls->roleid),
1281 : : ObjectIdGetDatum(iacls->nspid),
1282 : : CharGetDatum(objtype));
1283 : :
5305 tgl@sss.pgh.pa.us 1284 [ + + ]: 95 : if (HeapTupleIsValid(tuple))
1285 : : {
1286 : : Datum aclDatum;
1287 : : bool isNull;
1288 : :
1289 : 36 : aclDatum = SysCacheGetAttr(DEFACLROLENSPOBJ, tuple,
1290 : : Anum_pg_default_acl_defaclacl,
1291 : : &isNull);
1292 [ + - ]: 36 : if (!isNull)
1293 : 36 : old_acl = DatumGetAclPCopy(aclDatum);
1294 : : else
5123 tgl@sss.pgh.pa.us 1295 :UBC 0 : old_acl = NULL; /* this case shouldn't happen, probably */
5305 tgl@sss.pgh.pa.us 1296 :CBC 36 : isNew = false;
1297 : : }
1298 : : else
1299 : : {
1300 : 59 : old_acl = NULL;
1301 : 59 : isNew = true;
1302 : : }
1303 : :
5123 1304 [ + + ]: 95 : if (old_acl != NULL)
1305 : : {
1306 : : /*
1307 : : * We need the members of both old and new ACLs so we can correct the
1308 : : * shared dependency information. Collect data before
1309 : : * merge_acl_with_grant throws away old_acl.
1310 : : */
1311 : 36 : noldmembers = aclmembers(old_acl, &oldmembers);
1312 : : }
1313 : : else
1314 : : {
1315 : : /* If no or null entry, start with the default ACL value */
1316 : 59 : old_acl = aclcopy(def_acl);
1317 : : /* There are no old member roles according to the catalogs */
1318 : 59 : noldmembers = 0;
1319 : 59 : oldmembers = NULL;
1320 : : }
1321 : :
1322 : : /*
1323 : : * Generate new ACL. Grantor of rights is always the same as the target
1324 : : * role.
1325 : : */
5305 1326 : 95 : new_acl = merge_acl_with_grant(old_acl,
1327 : 95 : iacls->is_grant,
1328 : 95 : iacls->grant_option,
1329 : : iacls->behavior,
1330 : : iacls->grantees,
1331 : : this_privileges,
1332 : : iacls->roleid,
1333 : : iacls->roleid);
1334 : :
1335 : : /*
1336 : : * If the result is the same as the default value, we do not need an
1337 : : * explicit pg_default_acl entry, and should in fact remove the entry if
1338 : : * it exists. Must sort both arrays to compare properly.
1339 : : */
5123 1340 : 95 : aclitemsort(new_acl);
1341 : 95 : aclitemsort(def_acl);
1342 [ + + ]: 95 : if (aclequal(new_acl, def_acl))
1343 : : {
1344 : : /* delete old entry, if indeed there is one */
1345 [ + + ]: 28 : if (!isNew)
1346 : : {
1347 : : ObjectAddress myself;
1348 : :
1349 : : /*
1350 : : * The dependency machinery will take care of removing all
1351 : : * associated dependency entries. We use DROP_RESTRICT since
1352 : : * there shouldn't be anything depending on this entry.
1353 : : */
1354 : 27 : myself.classId = DefaultAclRelationId;
1972 andres@anarazel.de 1355 : 27 : myself.objectId = ((Form_pg_default_acl) GETSTRUCT(tuple))->oid;
5123 tgl@sss.pgh.pa.us 1356 : 27 : myself.objectSubId = 0;
1357 : :
4462 rhaas@postgresql.org 1358 : 27 : performDeletion(&myself, DROP_RESTRICT, 0);
1359 : : }
1360 : : }
1361 : : else
1362 : : {
638 peter@eisentraut.org 1363 : 67 : Datum values[Natts_pg_default_acl] = {0};
1364 : 67 : bool nulls[Natts_pg_default_acl] = {0};
1365 : 67 : bool replaces[Natts_pg_default_acl] = {0};
1366 : : Oid defAclOid;
1367 : :
5123 tgl@sss.pgh.pa.us 1368 [ + + ]: 67 : if (isNew)
1369 : : {
1370 : : /* insert new entry */
1972 andres@anarazel.de 1371 : 58 : defAclOid = GetNewOidWithIndex(rel, DefaultAclOidIndexId,
1372 : : Anum_pg_default_acl_oid);
1373 : 58 : values[Anum_pg_default_acl_oid - 1] = ObjectIdGetDatum(defAclOid);
5123 tgl@sss.pgh.pa.us 1374 : 58 : values[Anum_pg_default_acl_defaclrole - 1] = ObjectIdGetDatum(iacls->roleid);
1375 : 58 : values[Anum_pg_default_acl_defaclnamespace - 1] = ObjectIdGetDatum(iacls->nspid);
1376 : 58 : values[Anum_pg_default_acl_defaclobjtype - 1] = CharGetDatum(objtype);
1377 : 58 : values[Anum_pg_default_acl_defaclacl - 1] = PointerGetDatum(new_acl);
1378 : :
1379 : 58 : newtuple = heap_form_tuple(RelationGetDescr(rel), values, nulls);
2630 alvherre@alvh.no-ip. 1380 : 58 : CatalogTupleInsert(rel, newtuple);
1381 : : }
1382 : : else
1383 : : {
1972 andres@anarazel.de 1384 : 9 : defAclOid = ((Form_pg_default_acl) GETSTRUCT(tuple))->oid;
1385 : :
1386 : : /* update existing entry */
5123 tgl@sss.pgh.pa.us 1387 : 9 : values[Anum_pg_default_acl_defaclacl - 1] = PointerGetDatum(new_acl);
1388 : 9 : replaces[Anum_pg_default_acl_defaclacl - 1] = true;
1389 : :
1390 : 9 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel),
1391 : : values, nulls, replaces);
2630 alvherre@alvh.no-ip. 1392 : 9 : CatalogTupleUpdate(rel, &newtuple->t_self, newtuple);
1393 : : }
1394 : :
1395 : : /* these dependencies don't change in an update */
5123 tgl@sss.pgh.pa.us 1396 [ + + ]: 67 : if (isNew)
1397 : : {
1398 : : /* dependency on role */
1972 andres@anarazel.de 1399 : 58 : recordDependencyOnOwner(DefaultAclRelationId, defAclOid,
1400 : : iacls->roleid);
1401 : :
1402 : : /* dependency on namespace */
5123 tgl@sss.pgh.pa.us 1403 [ + + ]: 58 : if (OidIsValid(iacls->nspid))
1404 : : {
1405 : : ObjectAddress myself,
1406 : : referenced;
1407 : :
1408 : 17 : myself.classId = DefaultAclRelationId;
1972 andres@anarazel.de 1409 : 17 : myself.objectId = defAclOid;
5123 tgl@sss.pgh.pa.us 1410 : 17 : myself.objectSubId = 0;
1411 : :
1412 : 17 : referenced.classId = NamespaceRelationId;
1413 : 17 : referenced.objectId = iacls->nspid;
1414 : 17 : referenced.objectSubId = 0;
1415 : :
1416 : 17 : recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
1417 : : }
1418 : : }
1419 : :
1420 : : /*
1421 : : * Update the shared dependency ACL info
1422 : : */
1423 : 67 : nnewmembers = aclmembers(new_acl, &newmembers);
1424 : :
1425 : 67 : updateAclDependencies(DefaultAclRelationId,
1426 : : defAclOid, 0,
1427 : : iacls->roleid,
1428 : : noldmembers, oldmembers,
1429 : : nnewmembers, newmembers);
1430 : :
4046 rhaas@postgresql.org 1431 [ + + ]: 67 : if (isNew)
1972 andres@anarazel.de 1432 [ - + ]: 58 : InvokeObjectPostCreateHook(DefaultAclRelationId, defAclOid, 0);
1433 : : else
1789 tgl@sss.pgh.pa.us 1434 [ - + ]: 9 : InvokeObjectPostAlterHook(DefaultAclRelationId, defAclOid, 0);
1435 : : }
1436 : :
5305 1437 [ + + ]: 95 : if (HeapTupleIsValid(tuple))
1438 : 36 : ReleaseSysCache(tuple);
1439 : :
1910 andres@anarazel.de 1440 : 95 : table_close(rel, RowExclusiveLock);
1441 : :
1442 : : /* prevent error when processing duplicate objects */
1180 michael@paquier.xyz 1443 : 95 : CommandCounterIncrement();
5305 tgl@sss.pgh.pa.us 1444 : 95 : }
1445 : :
1446 : :
1447 : : /*
1448 : : * RemoveRoleFromObjectACL
1449 : : *
1450 : : * Used by shdepDropOwned to remove mentions of a role in ACLs
1451 : : */
1452 : : void
1453 : 121 : RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
1454 : : {
1455 [ + + ]: 121 : if (classid == DefaultAclRelationId)
1456 : : {
1457 : : InternalDefaultACL iacls;
1458 : : Form_pg_default_acl pg_default_acl_tuple;
1459 : : Relation rel;
1460 : : ScanKeyData skey[1];
1461 : : SysScanDesc scan;
1462 : : HeapTuple tuple;
1463 : :
1464 : : /* first fetch info needed by SetDefaultACL */
1910 andres@anarazel.de 1465 : 15 : rel = table_open(DefaultAclRelationId, AccessShareLock);
1466 : :
5305 tgl@sss.pgh.pa.us 1467 : 15 : ScanKeyInit(&skey[0],
1468 : : Anum_pg_default_acl_oid,
1469 : : BTEqualStrategyNumber, F_OIDEQ,
1470 : : ObjectIdGetDatum(objid));
1471 : :
1472 : 15 : scan = systable_beginscan(rel, DefaultAclOidIndexId, true,
1473 : : NULL, 1, skey);
1474 : :
1475 : 15 : tuple = systable_getnext(scan);
1476 : :
1477 [ - + ]: 15 : if (!HeapTupleIsValid(tuple))
5305 tgl@sss.pgh.pa.us 1478 [ # # ]:UBC 0 : elog(ERROR, "could not find tuple for default ACL %u", objid);
1479 : :
5305 tgl@sss.pgh.pa.us 1480 :CBC 15 : pg_default_acl_tuple = (Form_pg_default_acl) GETSTRUCT(tuple);
1481 : :
1482 : 15 : iacls.roleid = pg_default_acl_tuple->defaclrole;
1483 : 15 : iacls.nspid = pg_default_acl_tuple->defaclnamespace;
1484 : :
1485 [ + + + + : 15 : switch (pg_default_acl_tuple->defaclobjtype)
+ - ]
1486 : : {
1487 : 3 : case DEFACLOBJ_RELATION:
2377 peter_e@gmx.net 1488 : 3 : iacls.objtype = OBJECT_TABLE;
5305 tgl@sss.pgh.pa.us 1489 : 3 : break;
4743 rhaas@postgresql.org 1490 : 3 : case DEFACLOBJ_SEQUENCE:
2377 peter_e@gmx.net 1491 : 3 : iacls.objtype = OBJECT_SEQUENCE;
5305 tgl@sss.pgh.pa.us 1492 : 3 : break;
1493 : 3 : case DEFACLOBJ_FUNCTION:
2377 peter_e@gmx.net 1494 : 3 : iacls.objtype = OBJECT_FUNCTION;
5305 tgl@sss.pgh.pa.us 1495 : 3 : break;
4144 1496 : 3 : case DEFACLOBJ_TYPE:
2377 peter_e@gmx.net 1497 : 3 : iacls.objtype = OBJECT_TYPE;
4144 tgl@sss.pgh.pa.us 1498 : 3 : break;
2574 teodor@sigaev.ru 1499 : 3 : case DEFACLOBJ_NAMESPACE:
2377 peter_e@gmx.net 1500 : 3 : iacls.objtype = OBJECT_SCHEMA;
2574 teodor@sigaev.ru 1501 : 3 : break;
5305 tgl@sss.pgh.pa.us 1502 :UBC 0 : default:
1503 : : /* Shouldn't get here */
4144 1504 [ # # ]: 0 : elog(ERROR, "unexpected default ACL type: %d",
1505 : : (int) pg_default_acl_tuple->defaclobjtype);
1506 : : break;
1507 : : }
1508 : :
5305 tgl@sss.pgh.pa.us 1509 :CBC 15 : systable_endscan(scan);
1910 andres@anarazel.de 1510 : 15 : table_close(rel, AccessShareLock);
1511 : :
5305 tgl@sss.pgh.pa.us 1512 : 15 : iacls.is_grant = false;
1513 : 15 : iacls.all_privs = true;
1514 : 15 : iacls.privileges = ACL_NO_RIGHTS;
1515 : 15 : iacls.grantees = list_make1_oid(roleid);
1516 : 15 : iacls.grant_option = false;
1517 : 15 : iacls.behavior = DROP_CASCADE;
1518 : :
1519 : : /* Do it */
1520 : 15 : SetDefaultACL(&iacls);
1521 : : }
1522 : : else
1523 : : {
1524 : : InternalGrant istmt;
1525 : :
1526 [ + + + + : 106 : switch (classid)
- + + - +
+ + - ]
1527 : : {
1528 : 46 : case RelationRelationId:
1529 : : /* it's OK to use TABLE for a sequence */
2377 peter_e@gmx.net 1530 : 46 : istmt.objtype = OBJECT_TABLE;
5305 tgl@sss.pgh.pa.us 1531 : 46 : break;
1532 : 5 : case DatabaseRelationId:
2377 peter_e@gmx.net 1533 : 5 : istmt.objtype = OBJECT_DATABASE;
5305 tgl@sss.pgh.pa.us 1534 : 5 : break;
4499 peter_e@gmx.net 1535 :GBC 2 : case TypeRelationId:
2377 1536 : 2 : istmt.objtype = OBJECT_TYPE;
4499 1537 : 2 : break;
5305 tgl@sss.pgh.pa.us 1538 :CBC 17 : case ProcedureRelationId:
2377 peter_e@gmx.net 1539 : 17 : istmt.objtype = OBJECT_ROUTINE;
5305 tgl@sss.pgh.pa.us 1540 : 17 : break;
5305 tgl@sss.pgh.pa.us 1541 :UBC 0 : case LanguageRelationId:
2377 peter_e@gmx.net 1542 : 0 : istmt.objtype = OBJECT_LANGUAGE;
5305 tgl@sss.pgh.pa.us 1543 : 0 : break;
5238 itagaki.takahiro@gma 1544 :CBC 9 : case LargeObjectRelationId:
2377 peter_e@gmx.net 1545 : 9 : istmt.objtype = OBJECT_LARGEOBJECT;
5238 itagaki.takahiro@gma 1546 : 9 : break;
5305 tgl@sss.pgh.pa.us 1547 : 7 : case NamespaceRelationId:
2377 peter_e@gmx.net 1548 : 7 : istmt.objtype = OBJECT_SCHEMA;
5305 tgl@sss.pgh.pa.us 1549 : 7 : break;
5305 tgl@sss.pgh.pa.us 1550 :UBC 0 : case TableSpaceRelationId:
2377 peter_e@gmx.net 1551 : 0 : istmt.objtype = OBJECT_TABLESPACE;
5305 tgl@sss.pgh.pa.us 1552 : 0 : break;
4902 heikki.linnakangas@i 1553 :CBC 7 : case ForeignServerRelationId:
2377 peter_e@gmx.net 1554 : 7 : istmt.objtype = OBJECT_FOREIGN_SERVER;
4902 heikki.linnakangas@i 1555 : 7 : break;
1556 : 1 : case ForeignDataWrapperRelationId:
2377 peter_e@gmx.net 1557 : 1 : istmt.objtype = OBJECT_FDW;
4902 heikki.linnakangas@i 1558 : 1 : break;
739 tgl@sss.pgh.pa.us 1559 : 12 : case ParameterAclRelationId:
1560 : 12 : istmt.objtype = OBJECT_PARAMETER_ACL;
1561 : 12 : break;
5305 tgl@sss.pgh.pa.us 1562 :UBC 0 : default:
1563 [ # # ]: 0 : elog(ERROR, "unexpected object class %u", classid);
1564 : : break;
1565 : : }
5305 tgl@sss.pgh.pa.us 1566 :CBC 106 : istmt.is_grant = false;
1567 : 106 : istmt.objects = list_make1_oid(objid);
1568 : 106 : istmt.all_privs = true;
1569 : 106 : istmt.privileges = ACL_NO_RIGHTS;
1570 : 106 : istmt.col_privs = NIL;
1571 : 106 : istmt.grantees = list_make1_oid(roleid);
1572 : 106 : istmt.grant_option = false;
1573 : 106 : istmt.behavior = DROP_CASCADE;
1574 : :
1575 : 106 : ExecGrantStmt_oids(&istmt);
1576 : : }
1577 : 121 : }
1578 : :
1579 : :
1580 : : /*
1581 : : * expand_col_privileges
1582 : : *
1583 : : * OR the specified privilege(s) into per-column array entries for each
1584 : : * specified attribute. The per-column array is indexed starting at
1585 : : * FirstLowInvalidHeapAttributeNumber, up to relation's last attribute.
1586 : : */
1587 : : static void
5561 1588 : 204 : expand_col_privileges(List *colnames, Oid table_oid,
1589 : : AclMode this_privileges,
1590 : : AclMode *col_privileges,
1591 : : int num_col_privileges)
1592 : : {
1593 : : ListCell *cell;
1594 : :
1595 [ + - + + : 1137 : foreach(cell, colnames)
+ + ]
1596 : : {
1597 : 933 : char *colname = strVal(lfirst(cell));
1598 : : AttrNumber attnum;
1599 : :
1600 : 933 : attnum = get_attnum(table_oid, colname);
1601 [ - + ]: 933 : if (attnum == InvalidAttrNumber)
5561 tgl@sss.pgh.pa.us 1602 [ # # ]:UBC 0 : ereport(ERROR,
1603 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
1604 : : errmsg("column \"%s\" of relation \"%s\" does not exist",
1605 : : colname, get_rel_name(table_oid))));
5561 tgl@sss.pgh.pa.us 1606 :CBC 933 : attnum -= FirstLowInvalidHeapAttributeNumber;
1607 [ + - - + ]: 933 : if (attnum <= 0 || attnum >= num_col_privileges)
5421 bruce@momjian.us 1608 [ # # ]:UBC 0 : elog(ERROR, "column number out of range"); /* safety check */
5561 tgl@sss.pgh.pa.us 1609 :CBC 933 : col_privileges[attnum] |= this_privileges;
1610 : : }
1611 : 204 : }
1612 : :
1613 : : /*
1614 : : * expand_all_col_privileges
1615 : : *
1616 : : * OR the specified privilege(s) into per-column array entries for each valid
1617 : : * attribute of a relation. The per-column array is indexed starting at
1618 : : * FirstLowInvalidHeapAttributeNumber, up to relation's last attribute.
1619 : : */
1620 : : static void
1621 : 744 : expand_all_col_privileges(Oid table_oid, Form_pg_class classForm,
1622 : : AclMode this_privileges,
1623 : : AclMode *col_privileges,
1624 : : int num_col_privileges)
1625 : : {
1626 : : AttrNumber curr_att;
1627 : :
1628 [ - + ]: 744 : Assert(classForm->relnatts - FirstLowInvalidHeapAttributeNumber < num_col_privileges);
1629 : 744 : for (curr_att = FirstLowInvalidHeapAttributeNumber + 1;
1630 [ + + ]: 11156 : curr_att <= classForm->relnatts;
1631 : 10412 : curr_att++)
1632 : : {
1633 : : HeapTuple attTuple;
1634 : : bool isdropped;
1635 : :
1636 [ + + ]: 10412 : if (curr_att == InvalidAttrNumber)
1637 : 744 : continue;
1638 : :
1639 : : /* Views don't have any system columns at all */
1640 [ + + + + ]: 9668 : if (classForm->relkind == RELKIND_VIEW && curr_att < 0)
1641 : 1860 : continue;
1642 : :
5173 rhaas@postgresql.org 1643 : 7808 : attTuple = SearchSysCache2(ATTNUM,
1644 : : ObjectIdGetDatum(table_oid),
1645 : : Int16GetDatum(curr_att));
5561 tgl@sss.pgh.pa.us 1646 [ - + ]: 7808 : if (!HeapTupleIsValid(attTuple))
5561 tgl@sss.pgh.pa.us 1647 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
1648 : : curr_att, table_oid);
1649 : :
5561 tgl@sss.pgh.pa.us 1650 :CBC 7808 : isdropped = ((Form_pg_attribute) GETSTRUCT(attTuple))->attisdropped;
1651 : :
1652 : 7808 : ReleaseSysCache(attTuple);
1653 : :
1654 : : /* ignore dropped columns */
1655 [ + + ]: 7808 : if (isdropped)
1656 : 3 : continue;
1657 : :
1658 : 7805 : col_privileges[curr_att - FirstLowInvalidHeapAttributeNumber] |= this_privileges;
1659 : : }
1660 : 744 : }
1661 : :
1662 : : /*
1663 : : * This processes attributes, but expects to be called from
1664 : : * ExecGrant_Relation, not directly from ExecuteGrantStmt.
1665 : : */
1666 : : static void
1667 : 8717 : ExecGrant_Attribute(InternalGrant *istmt, Oid relOid, const char *relname,
1668 : : AttrNumber attnum, Oid ownerId, AclMode col_privileges,
1669 : : Relation attRelation, const Acl *old_rel_acl)
1670 : : {
1671 : : HeapTuple attr_tuple;
1672 : : Form_pg_attribute pg_attribute_tuple;
1673 : : Acl *old_acl;
1674 : : Acl *new_acl;
1675 : : Acl *merged_acl;
1676 : : Datum aclDatum;
1677 : : bool isNull;
1678 : : Oid grantorId;
1679 : : AclMode avail_goptions;
1680 : : bool need_update;
1681 : : HeapTuple newtuple;
638 peter@eisentraut.org 1682 : 8717 : Datum values[Natts_pg_attribute] = {0};
1683 : 8717 : bool nulls[Natts_pg_attribute] = {0};
1684 : 8717 : bool replaces[Natts_pg_attribute] = {0};
1685 : : int noldmembers;
1686 : : int nnewmembers;
1687 : : Oid *oldmembers;
1688 : : Oid *newmembers;
1689 : :
5173 rhaas@postgresql.org 1690 : 8717 : attr_tuple = SearchSysCache2(ATTNUM,
1691 : : ObjectIdGetDatum(relOid),
1692 : : Int16GetDatum(attnum));
5561 tgl@sss.pgh.pa.us 1693 [ - + ]: 8717 : if (!HeapTupleIsValid(attr_tuple))
5561 tgl@sss.pgh.pa.us 1694 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
1695 : : attnum, relOid);
5561 tgl@sss.pgh.pa.us 1696 :CBC 8717 : pg_attribute_tuple = (Form_pg_attribute) GETSTRUCT(attr_tuple);
1697 : :
1698 : : /*
1699 : : * Get working copy of existing ACL. If there's no ACL, substitute the
1700 : : * proper default.
1701 : : */
1702 : 8717 : aclDatum = SysCacheGetAttr(ATTNUM, attr_tuple, Anum_pg_attribute_attacl,
1703 : : &isNull);
1704 [ + + ]: 8717 : if (isNull)
1705 : : {
2377 peter_e@gmx.net 1706 : 8564 : old_acl = acldefault(OBJECT_COLUMN, ownerId);
1707 : : /* There are no old member roles according to the catalogs */
5123 tgl@sss.pgh.pa.us 1708 : 8564 : noldmembers = 0;
1709 : 8564 : oldmembers = NULL;
1710 : : }
1711 : : else
1712 : : {
5561 1713 : 153 : old_acl = DatumGetAclPCopy(aclDatum);
1714 : : /* Get the roles mentioned in the existing ACL */
5123 1715 : 153 : noldmembers = aclmembers(old_acl, &oldmembers);
1716 : : }
1717 : :
1718 : : /*
1719 : : * In select_best_grantor we should consider existing table-level ACL bits
1720 : : * as well as the per-column ACL. Build a new ACL that is their
1721 : : * concatenation. (This is a bit cheap and dirty compared to merging them
1722 : : * properly with no duplications, but it's all we need here.)
1723 : : */
5561 1724 : 8717 : merged_acl = aclconcat(old_rel_acl, old_acl);
1725 : :
1726 : : /* Determine ID to do the grant as, and available grant options */
1727 : 8717 : select_best_grantor(GetUserId(), col_privileges,
1728 : : merged_acl, ownerId,
1729 : : &grantorId, &avail_goptions);
1730 : :
1731 : 8717 : pfree(merged_acl);
1732 : :
1733 : : /*
1734 : : * Restrict the privileges to what we can actually grant, and emit the
1735 : : * standards-mandated warning and error messages. Note: we don't track
1736 : : * whether the user actually used the ALL PRIVILEGES(columns) syntax for
1737 : : * each column; we just approximate it by whether all the possible
1738 : : * privileges are specified now. Since the all_privs flag only determines
1739 : : * whether a warning is issued, this seems close enough.
1740 : : */
1741 : : col_privileges =
1742 : 8717 : restrict_and_check_grant(istmt->is_grant, avail_goptions,
1743 : : (col_privileges == ACL_ALL_RIGHTS_COLUMN),
1744 : : col_privileges,
1745 : : relOid, grantorId, OBJECT_COLUMN,
1746 : : relname, attnum,
1747 : 8717 : NameStr(pg_attribute_tuple->attname));
1748 : :
1749 : : /*
1750 : : * Generate new ACL.
1751 : : */
1752 : 8717 : new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
1753 : 8717 : istmt->grant_option,
1754 : : istmt->behavior, istmt->grantees,
1755 : : col_privileges, grantorId,
1756 : : ownerId);
1757 : :
1758 : : /*
1759 : : * We need the members of both old and new ACLs so we can correct the
1760 : : * shared dependency information.
1761 : : */
1762 : 8717 : nnewmembers = aclmembers(new_acl, &newmembers);
1763 : :
1764 : : /* finished building new ACL value, now insert it */
1765 : :
1766 : : /*
1767 : : * If the updated ACL is empty, we can set attacl to null, and maybe even
1768 : : * avoid an update of the pg_attribute row. This is worth testing because
1769 : : * we'll come through here multiple times for any relation-level REVOKE,
1770 : : * even if there were never any column GRANTs. Note we are assuming that
1771 : : * the "default" ACL state for columns is empty.
1772 : : */
1773 [ + + ]: 8717 : if (ACL_NUM(new_acl) > 0)
1774 : : {
1775 : 945 : values[Anum_pg_attribute_attacl - 1] = PointerGetDatum(new_acl);
1776 : 945 : need_update = true;
1777 : : }
1778 : : else
1779 : : {
1780 : 7772 : nulls[Anum_pg_attribute_attacl - 1] = true;
1781 : 7772 : need_update = !isNull;
1782 : : }
1783 : 8717 : replaces[Anum_pg_attribute_attacl - 1] = true;
1784 : :
1785 [ + + ]: 8717 : if (need_update)
1786 : : {
1787 : 992 : newtuple = heap_modify_tuple(attr_tuple, RelationGetDescr(attRelation),
1788 : : values, nulls, replaces);
1789 : :
2630 alvherre@alvh.no-ip. 1790 : 992 : CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
1791 : :
1792 : : /* Update initial privileges for extensions */
2930 sfrost@snowman.net 1793 : 992 : recordExtensionInitPriv(relOid, RelationRelationId, attnum,
1794 [ + + ]: 992 : ACL_NUM(new_acl) > 0 ? new_acl : NULL);
1795 : :
1796 : : /* Update the shared dependency ACL info */
5561 tgl@sss.pgh.pa.us 1797 : 992 : updateAclDependencies(RelationRelationId, relOid, attnum,
1798 : : ownerId,
1799 : : noldmembers, oldmembers,
1800 : : nnewmembers, newmembers);
1801 : : }
1802 : :
1803 : 8717 : pfree(new_acl);
1804 : :
1805 : 8717 : ReleaseSysCache(attr_tuple);
1806 : 8717 : }
1807 : :
1808 : : /*
1809 : : * This processes both sequences and non-sequences.
1810 : : */
1811 : : static void
6709 alvherre@alvh.no-ip. 1812 : 3873 : ExecGrant_Relation(InternalGrant *istmt)
1813 : : {
1814 : : Relation relation;
1815 : : Relation attRelation;
1816 : : ListCell *cell;
1817 : :
1910 andres@anarazel.de 1818 : 3873 : relation = table_open(RelationRelationId, RowExclusiveLock);
1819 : 3873 : attRelation = table_open(AttributeRelationId, RowExclusiveLock);
1820 : :
6709 alvherre@alvh.no-ip. 1821 [ + - + + : 7776 : foreach(cell, istmt->objects)
+ + ]
1822 : : {
6719 1823 : 3906 : Oid relOid = lfirst_oid(cell);
1824 : : Datum aclDatum;
1825 : : Form_pg_class pg_class_tuple;
1826 : : bool isNull;
1827 : : AclMode this_privileges;
1828 : : AclMode *col_privileges;
1829 : : int num_col_privileges;
1830 : : bool have_col_privileges;
1831 : : Acl *old_acl;
1832 : : Acl *old_rel_acl;
1833 : : int noldmembers;
1834 : : Oid *oldmembers;
1835 : : Oid ownerId;
1836 : : HeapTuple tuple;
1837 : : ListCell *cell_colprivs;
1838 : :
5173 rhaas@postgresql.org 1839 : 3906 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
8345 peter_e@gmx.net 1840 [ - + ]: 3906 : if (!HeapTupleIsValid(tuple))
7573 tgl@sss.pgh.pa.us 1841 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for relation %u", relOid);
8345 peter_e@gmx.net 1842 :CBC 3906 : pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
1843 : :
1844 : : /* Not sensible to grant on an index */
2277 alvherre@alvh.no-ip. 1845 [ + - ]: 3906 : if (pg_class_tuple->relkind == RELKIND_INDEX ||
1846 [ - + ]: 3906 : pg_class_tuple->relkind == RELKIND_PARTITIONED_INDEX)
7573 tgl@sss.pgh.pa.us 1847 [ # # ]:UBC 0 : ereport(ERROR,
1848 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1849 : : errmsg("\"%s\" is an index",
1850 : : NameStr(pg_class_tuple->relname))));
1851 : :
1852 : : /* Composite types aren't tables either */
7257 tgl@sss.pgh.pa.us 1853 [ - + ]:CBC 3906 : if (pg_class_tuple->relkind == RELKIND_COMPOSITE_TYPE)
7257 tgl@sss.pgh.pa.us 1854 [ # # ]:UBC 0 : ereport(ERROR,
1855 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1856 : : errmsg("\"%s\" is a composite type",
1857 : : NameStr(pg_class_tuple->relname))));
1858 : :
1859 : : /* Used GRANT SEQUENCE on a non-sequence? */
2377 peter_e@gmx.net 1860 [ + + ]:CBC 3906 : if (istmt->objtype == OBJECT_SEQUENCE &&
6658 bruce@momjian.us 1861 [ - + ]: 8 : pg_class_tuple->relkind != RELKIND_SEQUENCE)
6658 bruce@momjian.us 1862 [ # # ]:UBC 0 : ereport(ERROR,
1863 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1864 : : errmsg("\"%s\" is not a sequence",
1865 : : NameStr(pg_class_tuple->relname))));
1866 : :
1867 : : /* Adjust the default permissions based on object type */
6658 bruce@momjian.us 1868 [ + + + - ]:CBC 3906 : if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
1869 : : {
1870 [ + + ]: 771 : if (pg_class_tuple->relkind == RELKIND_SEQUENCE)
1871 : 37 : this_privileges = ACL_ALL_RIGHTS_SEQUENCE;
1872 : : else
1873 : 734 : this_privileges = ACL_ALL_RIGHTS_RELATION;
1874 : : }
1875 : : else
1876 : 3135 : this_privileges = istmt->privileges;
1877 : :
1878 : : /*
1879 : : * The GRANT TABLE syntax can be used for sequences and non-sequences,
1880 : : * so we have to look at the relkind to determine the supported
1881 : : * permissions. The OR of table and sequence permissions were already
1882 : : * checked.
1883 : : */
2377 peter_e@gmx.net 1884 [ + + ]: 3906 : if (istmt->objtype == OBJECT_TABLE)
1885 : : {
6658 bruce@momjian.us 1886 [ + + ]: 3898 : if (pg_class_tuple->relkind == RELKIND_SEQUENCE)
1887 : : {
1888 : : /*
1889 : : * For backward compatibility, just throw a warning for
1890 : : * invalid sequence permissions when using the non-sequence
1891 : : * GRANT syntax.
1892 : : */
1893 [ - + ]: 72 : if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_SEQUENCE))
1894 : : {
1895 : : /*
1896 : : * Mention the object name because the user needs to know
1897 : : * which operations succeeded. This is required because
1898 : : * WARNING allows the command to continue.
1899 : : */
6658 bruce@momjian.us 1900 [ # # ]:UBC 0 : ereport(WARNING,
1901 : : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1902 : : errmsg("sequence \"%s\" only supports USAGE, SELECT, and UPDATE privileges",
1903 : : NameStr(pg_class_tuple->relname))));
1904 : 0 : this_privileges &= (AclMode) ACL_ALL_RIGHTS_SEQUENCE;
1905 : : }
1906 : : }
1907 : : else
1908 : : {
6658 bruce@momjian.us 1909 [ - + ]:CBC 3826 : if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_RELATION))
1910 : : {
1911 : : /*
1912 : : * USAGE is the only permission supported by sequences but
1913 : : * not by non-sequences. Don't mention the object name
1914 : : * because we didn't in the combined TABLE | SEQUENCE
1915 : : * check.
1916 : : */
6658 bruce@momjian.us 1917 [ # # ]:UBC 0 : ereport(ERROR,
1918 : : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
1919 : : errmsg("invalid privilege type %s for table",
1920 : : "USAGE")));
1921 : : }
1922 : : }
1923 : : }
1924 : :
1925 : : /*
1926 : : * Set up array in which we'll accumulate any column privilege bits
1927 : : * that need modification. The array is indexed such that entry [0]
1928 : : * corresponds to FirstLowInvalidHeapAttributeNumber.
1929 : : */
5561 tgl@sss.pgh.pa.us 1930 :CBC 3906 : num_col_privileges = pg_class_tuple->relnatts - FirstLowInvalidHeapAttributeNumber + 1;
1931 : 3906 : col_privileges = (AclMode *) palloc0(num_col_privileges * sizeof(AclMode));
1932 : 3906 : have_col_privileges = false;
1933 : :
1934 : : /*
1935 : : * If we are revoking relation privileges that are also column
1936 : : * privileges, we must implicitly revoke them from each column too,
1937 : : * per SQL spec. (We don't need to implicitly add column privileges
1938 : : * during GRANT because the permissions-checking code always checks
1939 : : * both relation and per-column privileges.)
1940 : : */
1941 [ + + ]: 3906 : if (!istmt->is_grant &&
1942 [ + + ]: 772 : (this_privileges & ACL_ALL_RIGHTS_COLUMN) != 0)
1943 : : {
1944 : 744 : expand_all_col_privileges(relOid, pg_class_tuple,
1945 : : this_privileges & ACL_ALL_RIGHTS_COLUMN,
1946 : : col_privileges,
1947 : : num_col_privileges);
1948 : 744 : have_col_privileges = true;
1949 : : }
1950 : :
1951 : : /*
1952 : : * Get owner ID and working copy of existing ACL. If there's no ACL,
1953 : : * substitute the proper default.
1954 : : */
7257 1955 : 3906 : ownerId = pg_class_tuple->relowner;
6761 1956 : 3906 : aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl,
1957 : : &isNull);
1958 [ + + ]: 3906 : if (isNull)
1959 : : {
4852 rhaas@postgresql.org 1960 [ + + ]: 3365 : switch (pg_class_tuple->relkind)
1961 : : {
1962 : 42 : case RELKIND_SEQUENCE:
2377 peter_e@gmx.net 1963 : 42 : old_acl = acldefault(OBJECT_SEQUENCE, ownerId);
4852 rhaas@postgresql.org 1964 : 42 : break;
1965 : 3323 : default:
2377 peter_e@gmx.net 1966 : 3323 : old_acl = acldefault(OBJECT_TABLE, ownerId);
4852 rhaas@postgresql.org 1967 : 3323 : break;
1968 : : }
1969 : : /* There are no old member roles according to the catalogs */
5123 tgl@sss.pgh.pa.us 1970 : 3365 : noldmembers = 0;
1971 : 3365 : oldmembers = NULL;
1972 : : }
1973 : : else
1974 : : {
6761 1975 : 541 : old_acl = DatumGetAclPCopy(aclDatum);
1976 : : /* Get the roles mentioned in the existing ACL */
5123 1977 : 541 : noldmembers = aclmembers(old_acl, &oldmembers);
1978 : : }
1979 : :
1980 : : /* Need an extra copy of original rel ACL for column handling */
5561 1981 : 3906 : old_rel_acl = aclcopy(old_acl);
1982 : :
1983 : : /*
1984 : : * Handle relation-level privileges, if any were specified
1985 : : */
1986 [ + + ]: 3906 : if (this_privileges != ACL_NO_RIGHTS)
1987 : : {
1988 : : AclMode avail_goptions;
1989 : : Acl *new_acl;
1990 : : Oid grantorId;
1991 : : HeapTuple newtuple;
638 peter@eisentraut.org 1992 : 3708 : Datum values[Natts_pg_class] = {0};
1993 : 3708 : bool nulls[Natts_pg_class] = {0};
1994 : 3708 : bool replaces[Natts_pg_class] = {0};
1995 : : int nnewmembers;
1996 : : Oid *newmembers;
1997 : : ObjectType objtype;
1998 : :
1999 : : /* Determine ID to do the grant as, and available grant options */
5561 tgl@sss.pgh.pa.us 2000 : 3708 : select_best_grantor(GetUserId(), this_privileges,
2001 : : old_acl, ownerId,
2002 : : &grantorId, &avail_goptions);
2003 : :
4852 rhaas@postgresql.org 2004 [ + + ]: 3708 : switch (pg_class_tuple->relkind)
2005 : : {
2006 : 80 : case RELKIND_SEQUENCE:
2325 peter_e@gmx.net 2007 : 80 : objtype = OBJECT_SEQUENCE;
4852 rhaas@postgresql.org 2008 : 80 : break;
2009 : 3628 : default:
2325 peter_e@gmx.net 2010 : 3628 : objtype = OBJECT_TABLE;
4852 rhaas@postgresql.org 2011 : 3628 : break;
2012 : : }
2013 : :
2014 : : /*
2015 : : * Restrict the privileges to what we can actually grant, and emit
2016 : : * the standards-mandated warning and error messages.
2017 : : */
2018 : : this_privileges =
5561 tgl@sss.pgh.pa.us 2019 : 3708 : restrict_and_check_grant(istmt->is_grant, avail_goptions,
2020 : 3708 : istmt->all_privs, this_privileges,
2021 : : relOid, grantorId, objtype,
2022 : 3708 : NameStr(pg_class_tuple->relname),
2023 : : 0, NULL);
2024 : :
2025 : : /*
2026 : : * Generate new ACL.
2027 : : */
2028 : 3708 : new_acl = merge_acl_with_grant(old_acl,
2029 : 3708 : istmt->is_grant,
2030 : 3708 : istmt->grant_option,
2031 : : istmt->behavior,
2032 : : istmt->grantees,
2033 : : this_privileges,
2034 : : grantorId,
2035 : : ownerId);
2036 : :
2037 : : /*
2038 : : * We need the members of both old and new ACLs so we can correct
2039 : : * the shared dependency information.
2040 : : */
2041 : 3705 : nnewmembers = aclmembers(new_acl, &newmembers);
2042 : :
2043 : : /* finished building new ACL value, now insert it */
2044 : 3705 : replaces[Anum_pg_class_relacl - 1] = true;
2045 : 3705 : values[Anum_pg_class_relacl - 1] = PointerGetDatum(new_acl);
2046 : :
2047 : 3705 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation),
2048 : : values, nulls, replaces);
2049 : :
2630 alvherre@alvh.no-ip. 2050 : 3705 : CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
2051 : :
2052 : : /* Update initial privileges for extensions */
2930 sfrost@snowman.net 2053 : 3705 : recordExtensionInitPriv(relOid, RelationRelationId, 0, new_acl);
2054 : :
2055 : : /* Update the shared dependency ACL info */
5561 tgl@sss.pgh.pa.us 2056 : 3705 : updateAclDependencies(RelationRelationId, relOid, 0,
2057 : : ownerId,
2058 : : noldmembers, oldmembers,
2059 : : nnewmembers, newmembers);
2060 : :
2061 : 3705 : pfree(new_acl);
2062 : : }
2063 : :
2064 : : /*
2065 : : * Handle column-level privileges, if any were specified or implied.
2066 : : * We first expand the user-specified column privileges into the
2067 : : * array, and then iterate over all nonempty array entries.
2068 : : */
2069 [ + + + + : 4107 : foreach(cell_colprivs, istmt->col_privs)
+ + ]
2070 : : {
5421 bruce@momjian.us 2071 : 204 : AccessPriv *col_privs = (AccessPriv *) lfirst(cell_colprivs);
2072 : :
5561 tgl@sss.pgh.pa.us 2073 [ + + ]: 204 : if (col_privs->priv_name == NULL)
2074 : 9 : this_privileges = ACL_ALL_RIGHTS_COLUMN;
2075 : : else
2076 : 195 : this_privileges = string_to_privilege(col_privs->priv_name);
2077 : :
2078 [ - + ]: 204 : if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_COLUMN))
5561 tgl@sss.pgh.pa.us 2079 [ # # ]:UBC 0 : ereport(ERROR,
2080 : : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
2081 : : errmsg("invalid privilege type %s for column",
2082 : : privilege_to_string(this_privileges))));
2083 : :
5561 tgl@sss.pgh.pa.us 2084 [ - + ]:CBC 204 : if (pg_class_tuple->relkind == RELKIND_SEQUENCE &&
5561 tgl@sss.pgh.pa.us 2085 [ # # ]:UBC 0 : this_privileges & ~((AclMode) ACL_SELECT))
2086 : : {
2087 : : /*
2088 : : * The only column privilege allowed on sequences is SELECT.
2089 : : * This is a warning not error because we do it that way for
2090 : : * relation-level privileges.
2091 : : */
2092 [ # # ]: 0 : ereport(WARNING,
2093 : : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
2094 : : errmsg("sequence \"%s\" only supports SELECT column privileges",
2095 : : NameStr(pg_class_tuple->relname))));
2096 : :
2097 : 0 : this_privileges &= (AclMode) ACL_SELECT;
2098 : : }
2099 : :
5561 tgl@sss.pgh.pa.us 2100 :CBC 204 : expand_col_privileges(col_privs->cols, relOid,
2101 : : this_privileges,
2102 : : col_privileges,
2103 : : num_col_privileges);
2104 : 204 : have_col_privileges = true;
2105 : : }
2106 : :
2107 [ + + ]: 3903 : if (have_col_privileges)
2108 : : {
2109 : : AttrNumber i;
2110 : :
2111 [ + + ]: 15004 : for (i = 0; i < num_col_privileges; i++)
2112 : : {
2113 [ + + ]: 14065 : if (col_privileges[i] == ACL_NO_RIGHTS)
2114 : 5348 : continue;
2115 : 8717 : ExecGrant_Attribute(istmt,
2116 : : relOid,
2117 : 8717 : NameStr(pg_class_tuple->relname),
2118 : 8717 : i + FirstLowInvalidHeapAttributeNumber,
2119 : : ownerId,
2120 : 8717 : col_privileges[i],
2121 : : attRelation,
2122 : : old_rel_acl);
2123 : : }
2124 : : }
2125 : :
2126 : 3903 : pfree(old_rel_acl);
2127 : 3903 : pfree(col_privileges);
2128 : :
6856 2129 : 3903 : ReleaseSysCache(tuple);
2130 : :
2131 : : /* prevent error when processing duplicate objects */
6820 bruce@momjian.us 2132 : 3903 : CommandCounterIncrement();
2133 : : }
2134 : :
1910 andres@anarazel.de 2135 : 3870 : table_close(attRelation, RowExclusiveLock);
2136 : 3870 : table_close(relation, RowExclusiveLock);
10141 scrappy@hub.org 2137 : 3870 : }
2138 : :
2139 : : static void
488 peter@eisentraut.org 2140 : 3653 : ExecGrant_common(InternalGrant *istmt, Oid classid, AclMode default_privs,
2141 : : void (*object_check) (InternalGrant *istmt, HeapTuple tuple))
2142 : : {
2143 : : int cacheid;
2144 : : Relation relation;
2145 : : ListCell *cell;
2146 : :
6709 alvherre@alvh.no-ip. 2147 [ + + + - ]: 3653 : if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
488 peter@eisentraut.org 2148 : 313 : istmt->privileges = default_privs;
2149 : :
2150 : 3653 : cacheid = get_object_catcache_oid(classid);
2151 : :
2152 : 3653 : relation = table_open(classid, RowExclusiveLock);
2153 : :
5595 peter_e@gmx.net 2154 [ + - + + : 7337 : foreach(cell, istmt->objects)
+ + ]
2155 : : {
488 peter@eisentraut.org 2156 : 3714 : Oid objectid = lfirst_oid(cell);
2157 : : Datum aclDatum;
2158 : : Datum nameDatum;
2159 : : bool isNull;
2160 : : AclMode avail_goptions;
2161 : : AclMode this_privileges;
2162 : : Acl *old_acl;
2163 : : Acl *new_acl;
2164 : : Oid grantorId;
2165 : : Oid ownerId;
2166 : : HeapTuple tuple;
2167 : : HeapTuple newtuple;
2168 : 3714 : Datum *values = palloc0_array(Datum, RelationGetDescr(relation)->natts);
2169 : 3714 : bool *nulls = palloc0_array(bool, RelationGetDescr(relation)->natts);
2170 : 3714 : bool *replaces = palloc0_array(bool, RelationGetDescr(relation)->natts);
2171 : : int noldmembers;
2172 : : int nnewmembers;
2173 : : Oid *oldmembers;
2174 : : Oid *newmembers;
2175 : :
2176 : 3714 : tuple = SearchSysCache1(cacheid, ObjectIdGetDatum(objectid));
5595 peter_e@gmx.net 2177 [ - + ]: 3714 : if (!HeapTupleIsValid(tuple))
488 peter@eisentraut.org 2178 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for %s %u", get_object_class_descr(classid), objectid);
2179 : :
2180 : : /*
2181 : : * Additional object-type-specific checks
2182 : : */
488 peter@eisentraut.org 2183 [ + + ]:CBC 3714 : if (object_check)
2184 : 91 : object_check(istmt, tuple);
2185 : :
2186 : : /*
2187 : : * Get owner ID and working copy of existing ACL. If there's no ACL,
2188 : : * substitute the proper default.
2189 : : */
386 dgustafsson@postgres 2190 : 3702 : ownerId = DatumGetObjectId(SysCacheGetAttrNotNull(cacheid,
2191 : : tuple,
2192 : 3702 : get_object_attnum_owner(classid)));
488 peter@eisentraut.org 2193 : 3702 : aclDatum = SysCacheGetAttr(cacheid,
2194 : : tuple,
2195 : 3702 : get_object_attnum_acl(classid),
2196 : : &isNull);
5595 peter_e@gmx.net 2197 [ + + ]: 3702 : if (isNull)
2198 : : {
488 peter@eisentraut.org 2199 : 2870 : old_acl = acldefault(get_object_type(classid, objectid), ownerId);
2200 : : /* There are no old member roles according to the catalogs */
5123 tgl@sss.pgh.pa.us 2201 : 2870 : noldmembers = 0;
2202 : 2870 : oldmembers = NULL;
2203 : : }
2204 : : else
2205 : : {
5595 peter_e@gmx.net 2206 : 832 : old_acl = DatumGetAclPCopy(aclDatum);
2207 : : /* Get the roles mentioned in the existing ACL */
5123 tgl@sss.pgh.pa.us 2208 : 832 : noldmembers = aclmembers(old_acl, &oldmembers);
2209 : : }
2210 : :
2211 : : /* Determine ID to do the grant as, and available grant options */
5595 peter_e@gmx.net 2212 : 3702 : select_best_grantor(GetUserId(), istmt->privileges,
2213 : : old_acl, ownerId,
2214 : : &grantorId, &avail_goptions);
2215 : :
386 dgustafsson@postgres 2216 : 3702 : nameDatum = SysCacheGetAttrNotNull(cacheid, tuple,
2217 : 3702 : get_object_attnum_name(classid));
2218 : :
2219 : : /*
2220 : : * Restrict the privileges to what we can actually grant, and emit the
2221 : : * standards-mandated warning and error messages.
2222 : : */
2223 : : this_privileges =
5595 peter_e@gmx.net 2224 : 7404 : restrict_and_check_grant(istmt->is_grant, avail_goptions,
2225 : 3702 : istmt->all_privs, istmt->privileges,
2226 : : objectid, grantorId, get_object_type(classid, objectid),
488 peter@eisentraut.org 2227 : 3702 : NameStr(*DatumGetName(nameDatum)),
2228 : : 0, NULL);
2229 : :
2230 : : /*
2231 : : * Generate new ACL.
2232 : : */
5595 peter_e@gmx.net 2233 : 3687 : new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
2234 : 3687 : istmt->grant_option, istmt->behavior,
2235 : : istmt->grantees, this_privileges,
2236 : : grantorId, ownerId);
2237 : :
2238 : : /*
2239 : : * We need the members of both old and new ACLs so we can correct the
2240 : : * shared dependency information.
2241 : : */
2242 : 3684 : nnewmembers = aclmembers(new_acl, &newmembers);
2243 : :
2244 : : /* finished building new ACL value, now insert it */
488 peter@eisentraut.org 2245 : 3684 : replaces[get_object_attnum_acl(classid) - 1] = true;
2246 : 3684 : values[get_object_attnum_acl(classid) - 1] = PointerGetDatum(new_acl);
2247 : :
5595 peter_e@gmx.net 2248 : 3684 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
2249 : : nulls, replaces);
2250 : :
2630 alvherre@alvh.no-ip. 2251 : 3684 : CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
2252 : :
2253 : : /* Update initial privileges for extensions */
488 peter@eisentraut.org 2254 : 3684 : recordExtensionInitPriv(objectid, classid, 0, new_acl);
2255 : :
2256 : : /* Update the shared dependency ACL info */
2257 : 3684 : updateAclDependencies(classid,
2258 : : objectid, 0,
2259 : : ownerId,
2260 : : noldmembers, oldmembers,
2261 : : nnewmembers, newmembers);
2262 : :
5595 peter_e@gmx.net 2263 : 3684 : ReleaseSysCache(tuple);
2264 : :
2265 : 3684 : pfree(new_acl);
2266 : :
2267 : : /* prevent error when processing duplicate objects */
2268 : 3684 : CommandCounterIncrement();
2269 : : }
2270 : :
1910 andres@anarazel.de 2271 : 3623 : table_close(relation, RowExclusiveLock);
5595 peter_e@gmx.net 2272 : 3623 : }
2273 : :
2274 : : static void
488 peter@eisentraut.org 2275 : 21 : ExecGrant_Language_check(InternalGrant *istmt, HeapTuple tuple)
2276 : : {
2277 : : Form_pg_language pg_language_tuple;
2278 : :
2279 : 21 : pg_language_tuple = (Form_pg_language) GETSTRUCT(tuple);
2280 : :
2281 [ + + ]: 21 : if (!pg_language_tuple->lanpltrusted)
2282 [ + - ]: 3 : ereport(ERROR,
2283 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2284 : : errmsg("language \"%s\" is not trusted",
2285 : : NameStr(pg_language_tuple->lanname)),
2286 : : errdetail("GRANT and REVOKE are not allowed on untrusted languages, "
2287 : : "because only superusers can use untrusted languages.")));
5595 peter_e@gmx.net 2288 : 18 : }
2289 : :
2290 : : static void
488 peter@eisentraut.org 2291 : 37 : ExecGrant_Largeobject(InternalGrant *istmt)
2292 : : {
2293 : : Relation relation;
2294 : : ListCell *cell;
2295 : :
6709 alvherre@alvh.no-ip. 2296 [ + + + - ]: 37 : if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
488 peter@eisentraut.org 2297 : 22 : istmt->privileges = ACL_ALL_RIGHTS_LARGEOBJECT;
2298 : :
2299 : 37 : relation = table_open(LargeObjectMetadataRelationId,
2300 : : RowExclusiveLock);
2301 : :
6709 alvherre@alvh.no-ip. 2302 [ + - + + : 77 : foreach(cell, istmt->objects)
+ + ]
2303 : : {
488 peter@eisentraut.org 2304 : 40 : Oid loid = lfirst_oid(cell);
2305 : : Form_pg_largeobject_metadata form_lo_meta;
2306 : : char loname[NAMEDATALEN];
2307 : : Datum aclDatum;
2308 : : bool isNull;
2309 : : AclMode avail_goptions;
2310 : : AclMode this_privileges;
2311 : : Acl *old_acl;
2312 : : Acl *new_acl;
2313 : : Oid grantorId;
2314 : : Oid ownerId;
2315 : : HeapTuple newtuple;
2316 : 40 : Datum values[Natts_pg_largeobject_metadata] = {0};
2317 : 40 : bool nulls[Natts_pg_largeobject_metadata] = {0};
2318 : 40 : bool replaces[Natts_pg_largeobject_metadata] = {0};
2319 : : int noldmembers;
2320 : : int nnewmembers;
2321 : : Oid *oldmembers;
2322 : : Oid *newmembers;
2323 : : ScanKeyData entry[1];
2324 : : SysScanDesc scan;
2325 : : HeapTuple tuple;
2326 : :
2327 : : /* There's no syscache for pg_largeobject_metadata */
2328 : 40 : ScanKeyInit(&entry[0],
2329 : : Anum_pg_largeobject_metadata_oid,
2330 : : BTEqualStrategyNumber, F_OIDEQ,
2331 : : ObjectIdGetDatum(loid));
2332 : :
2333 : 40 : scan = systable_beginscan(relation,
2334 : : LargeObjectMetadataOidIndexId, true,
2335 : : NULL, 1, entry);
2336 : :
2337 : 40 : tuple = systable_getnext(scan);
8091 peter_e@gmx.net 2338 [ - + ]: 40 : if (!HeapTupleIsValid(tuple))
488 peter@eisentraut.org 2339 [ # # ]:UBC 0 : elog(ERROR, "could not find tuple for large object %u", loid);
2340 : :
488 peter@eisentraut.org 2341 :CBC 40 : form_lo_meta = (Form_pg_largeobject_metadata) GETSTRUCT(tuple);
2342 : :
2343 : : /*
2344 : : * Get owner ID and working copy of existing ACL. If there's no ACL,
2345 : : * substitute the proper default.
2346 : : */
2347 : 40 : ownerId = form_lo_meta->lomowner;
2348 : 40 : aclDatum = heap_getattr(tuple,
2349 : : Anum_pg_largeobject_metadata_lomacl,
2350 : : RelationGetDescr(relation), &isNull);
6761 tgl@sss.pgh.pa.us 2351 [ + + ]: 40 : if (isNull)
2352 : : {
488 peter@eisentraut.org 2353 : 22 : old_acl = acldefault(OBJECT_LARGEOBJECT, ownerId);
2354 : : /* There are no old member roles according to the catalogs */
5123 tgl@sss.pgh.pa.us 2355 : 22 : noldmembers = 0;
2356 : 22 : oldmembers = NULL;
2357 : : }
2358 : : else
2359 : : {
6761 2360 : 18 : old_acl = DatumGetAclPCopy(aclDatum);
2361 : : /* Get the roles mentioned in the existing ACL */
5123 2362 : 18 : noldmembers = aclmembers(old_acl, &oldmembers);
2363 : : }
2364 : :
2365 : : /* Determine ID to do the grant as, and available grant options */
6709 alvherre@alvh.no-ip. 2366 : 40 : select_best_grantor(GetUserId(), istmt->privileges,
2367 : : old_acl, ownerId,
2368 : : &grantorId, &avail_goptions);
2369 : :
2370 : : /*
2371 : : * Restrict the privileges to what we can actually grant, and emit the
2372 : : * standards-mandated warning and error messages.
2373 : : */
488 peter@eisentraut.org 2374 : 40 : snprintf(loname, sizeof(loname), "large object %u", loid);
2375 : : this_privileges =
6709 alvherre@alvh.no-ip. 2376 : 40 : restrict_and_check_grant(istmt->is_grant, avail_goptions,
2377 : 40 : istmt->all_privs, istmt->privileges,
2378 : : loid, grantorId, OBJECT_LARGEOBJECT,
2379 : : loname, 0, NULL);
2380 : :
2381 : : /*
2382 : : * Generate new ACL.
2383 : : */
2384 : 40 : new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
2385 : 40 : istmt->grant_option, istmt->behavior,
2386 : : istmt->grantees, this_privileges,
2387 : : grantorId, ownerId);
2388 : :
2389 : : /*
2390 : : * We need the members of both old and new ACLs so we can correct the
2391 : : * shared dependency information.
2392 : : */
6856 tgl@sss.pgh.pa.us 2393 : 40 : nnewmembers = aclmembers(new_acl, &newmembers);
2394 : :
2395 : : /* finished building new ACL value, now insert it */
488 peter@eisentraut.org 2396 : 40 : replaces[Anum_pg_largeobject_metadata_lomacl - 1] = true;
2397 : : values[Anum_pg_largeobject_metadata_lomacl - 1]
2398 : 40 : = PointerGetDatum(new_acl);
2399 : :
2400 : 40 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation),
2401 : : values, nulls, replaces);
2402 : :
2630 alvherre@alvh.no-ip. 2403 : 40 : CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
2404 : :
2405 : : /* Update initial privileges for extensions */
488 peter@eisentraut.org 2406 : 40 : recordExtensionInitPriv(loid, LargeObjectRelationId, 0, new_acl);
2407 : :
2408 : : /* Update the shared dependency ACL info */
2409 : 40 : updateAclDependencies(LargeObjectRelationId,
2410 : : form_lo_meta->oid, 0,
2411 : : ownerId,
2412 : : noldmembers, oldmembers,
2413 : : nnewmembers, newmembers);
2414 : :
2415 : 40 : systable_endscan(scan);
2416 : :
8091 peter_e@gmx.net 2417 : 40 : pfree(new_acl);
2418 : :
2419 : : /* prevent error when processing duplicate objects */
6820 bruce@momjian.us 2420 : 40 : CommandCounterIncrement();
2421 : : }
2422 : :
1910 andres@anarazel.de 2423 : 37 : table_close(relation, RowExclusiveLock);
8091 peter_e@gmx.net 2424 : 37 : }
2425 : :
2426 : : static void
488 peter@eisentraut.org 2427 : 70 : ExecGrant_Type_check(InternalGrant *istmt, HeapTuple tuple)
2428 : : {
2429 : : Form_pg_type pg_type_tuple;
2430 : :
2431 : 70 : pg_type_tuple = (Form_pg_type) GETSTRUCT(tuple);
2432 : :
2433 : : /* Disallow GRANT on dependent types */
2434 [ + + + - ]: 70 : if (IsTrueArrayType(pg_type_tuple))
2435 [ + - ]: 3 : ereport(ERROR,
2436 : : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
2437 : : errmsg("cannot set privileges of array types"),
2438 : : errhint("Set the privileges of the element type instead.")));
60 tgl@sss.pgh.pa.us 2439 [ + + ]:GNC 67 : if (pg_type_tuple->typtype == TYPTYPE_MULTIRANGE)
2440 [ + - ]: 3 : ereport(ERROR,
2441 : : (errcode(ERRCODE_INVALID_GRANT_OPERATION),
2442 : : errmsg("cannot set privileges of multirange types"),
2443 : : errhint("Set the privileges of the range type instead.")));
2444 : :
2445 : : /* Used GRANT DOMAIN on a non-domain? */
488 peter@eisentraut.org 2446 [ + + ]:CBC 64 : if (istmt->objtype == OBJECT_DOMAIN &&
2447 [ + + ]: 13 : pg_type_tuple->typtype != TYPTYPE_DOMAIN)
2448 [ + - ]: 3 : ereport(ERROR,
2449 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2450 : : errmsg("\"%s\" is not a domain",
2451 : : NameStr(pg_type_tuple->typname))));
4499 peter_e@gmx.net 2452 : 61 : }
2453 : :
2454 : : static void
739 tgl@sss.pgh.pa.us 2455 : 48 : ExecGrant_Parameter(InternalGrant *istmt)
2456 : : {
2457 : : Relation relation;
2458 : : ListCell *cell;
2459 : :
2460 [ + + + - ]: 48 : if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
2461 : 21 : istmt->privileges = ACL_ALL_RIGHTS_PARAMETER_ACL;
2462 : :
2463 : 48 : relation = table_open(ParameterAclRelationId, RowExclusiveLock);
2464 : :
2465 [ + + + + : 115 : foreach(cell, istmt->objects)
+ + ]
2466 : : {
2467 : 67 : Oid parameterId = lfirst_oid(cell);
2468 : : Datum nameDatum;
2469 : : const char *parname;
2470 : : Datum aclDatum;
2471 : : bool isNull;
2472 : : AclMode avail_goptions;
2473 : : AclMode this_privileges;
2474 : : Acl *old_acl;
2475 : : Acl *new_acl;
2476 : : Oid grantorId;
2477 : : Oid ownerId;
2478 : : HeapTuple tuple;
2479 : : int noldmembers;
2480 : : int nnewmembers;
2481 : : Oid *oldmembers;
2482 : : Oid *newmembers;
2483 : :
2484 : 67 : tuple = SearchSysCache1(PARAMETERACLOID, ObjectIdGetDatum(parameterId));
2485 [ - + ]: 67 : if (!HeapTupleIsValid(tuple))
739 tgl@sss.pgh.pa.us 2486 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for parameter ACL %u",
2487 : : parameterId);
2488 : :
2489 : : /* We'll need the GUC's name */
386 dgustafsson@postgres 2490 :CBC 67 : nameDatum = SysCacheGetAttrNotNull(PARAMETERACLOID, tuple,
2491 : : Anum_pg_parameter_acl_parname);
739 tgl@sss.pgh.pa.us 2492 : 67 : parname = TextDatumGetCString(nameDatum);
2493 : :
2494 : : /* Treat all parameters as belonging to the bootstrap superuser. */
2495 : 67 : ownerId = BOOTSTRAP_SUPERUSERID;
2496 : :
2497 : : /*
2498 : : * Get working copy of existing ACL. If there's no ACL, substitute the
2499 : : * proper default.
2500 : : */
2501 : 67 : aclDatum = SysCacheGetAttr(PARAMETERACLOID, tuple,
2502 : : Anum_pg_parameter_acl_paracl,
2503 : : &isNull);
2504 : :
2505 [ + + ]: 67 : if (isNull)
2506 : : {
2507 : 33 : old_acl = acldefault(istmt->objtype, ownerId);
2508 : : /* There are no old member roles according to the catalogs */
2509 : 33 : noldmembers = 0;
2510 : 33 : oldmembers = NULL;
2511 : : }
2512 : : else
2513 : : {
2514 : 34 : old_acl = DatumGetAclPCopy(aclDatum);
2515 : : /* Get the roles mentioned in the existing ACL */
2516 : 34 : noldmembers = aclmembers(old_acl, &oldmembers);
2517 : : }
2518 : :
2519 : : /* Determine ID to do the grant as, and available grant options */
2520 : 67 : select_best_grantor(GetUserId(), istmt->privileges,
2521 : : old_acl, ownerId,
2522 : : &grantorId, &avail_goptions);
2523 : :
2524 : : /*
2525 : : * Restrict the privileges to what we can actually grant, and emit the
2526 : : * standards-mandated warning and error messages.
2527 : : */
2528 : : this_privileges =
2529 : 67 : restrict_and_check_grant(istmt->is_grant, avail_goptions,
2530 : 67 : istmt->all_privs, istmt->privileges,
2531 : : parameterId, grantorId,
2532 : : OBJECT_PARAMETER_ACL,
2533 : : parname,
2534 : : 0, NULL);
2535 : :
2536 : : /*
2537 : : * Generate new ACL.
2538 : : */
2539 : 67 : new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
2540 : 67 : istmt->grant_option, istmt->behavior,
2541 : : istmt->grantees, this_privileges,
2542 : : grantorId, ownerId);
2543 : :
2544 : : /*
2545 : : * We need the members of both old and new ACLs so we can correct the
2546 : : * shared dependency information.
2547 : : */
2548 : 67 : nnewmembers = aclmembers(new_acl, &newmembers);
2549 : :
2550 : : /*
2551 : : * If the new ACL is equal to the default, we don't need the catalog
2552 : : * entry any longer. Delete it rather than updating it, to avoid
2553 : : * leaving a degenerate entry.
2554 : : */
2555 [ + + ]: 67 : if (aclequal(new_acl, acldefault(istmt->objtype, ownerId)))
2556 : : {
2557 : 29 : CatalogTupleDelete(relation, &tuple->t_self);
2558 : : }
2559 : : else
2560 : : {
2561 : : /* finished building new ACL value, now insert it */
2562 : : HeapTuple newtuple;
638 peter@eisentraut.org 2563 : 38 : Datum values[Natts_pg_parameter_acl] = {0};
2564 : 38 : bool nulls[Natts_pg_parameter_acl] = {0};
2565 : 38 : bool replaces[Natts_pg_parameter_acl] = {0};
2566 : :
739 tgl@sss.pgh.pa.us 2567 : 38 : replaces[Anum_pg_parameter_acl_paracl - 1] = true;
2568 : 38 : values[Anum_pg_parameter_acl_paracl - 1] = PointerGetDatum(new_acl);
2569 : :
2570 : 38 : newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation),
2571 : : values, nulls, replaces);
2572 : :
2573 : 38 : CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
2574 : : }
2575 : :
2576 : : /* Update initial privileges for extensions */
2577 : 67 : recordExtensionInitPriv(parameterId, ParameterAclRelationId, 0,
2578 : : new_acl);
2579 : :
2580 : : /* Update the shared dependency ACL info */
2581 : 67 : updateAclDependencies(ParameterAclRelationId, parameterId, 0,
2582 : : ownerId,
2583 : : noldmembers, oldmembers,
2584 : : nnewmembers, newmembers);
2585 : :
2586 : 67 : ReleaseSysCache(tuple);
2587 : 67 : pfree(new_acl);
2588 : :
2589 : : /* prevent error when processing duplicate objects */
2590 : 67 : CommandCounterIncrement();
2591 : : }
2592 : :
2593 : 48 : table_close(relation, RowExclusiveLock);
2594 : 48 : }
2595 : :
2596 : :
2597 : : static AclMode
6865 2598 : 6772 : string_to_privilege(const char *privname)
2599 : : {
2600 [ + + ]: 6772 : if (strcmp(privname, "insert") == 0)
2601 : 111 : return ACL_INSERT;
2602 [ + + ]: 6661 : if (strcmp(privname, "select") == 0)
2603 : 2895 : return ACL_SELECT;
2604 [ + + ]: 3766 : if (strcmp(privname, "update") == 0)
2605 : 160 : return ACL_UPDATE;
2606 [ + + ]: 3606 : if (strcmp(privname, "delete") == 0)
2607 : 57 : return ACL_DELETE;
5697 2608 [ + + ]: 3549 : if (strcmp(privname, "truncate") == 0)
2609 : 17 : return ACL_TRUNCATE;
6865 2610 [ + + ]: 3532 : if (strcmp(privname, "references") == 0)
2611 : 7 : return ACL_REFERENCES;
2612 [ + + ]: 3525 : if (strcmp(privname, "trigger") == 0)
2613 : 4 : return ACL_TRIGGER;
2614 [ + + ]: 3521 : if (strcmp(privname, "execute") == 0)
2615 : 2962 : return ACL_EXECUTE;
2616 [ + + ]: 559 : if (strcmp(privname, "usage") == 0)
2617 : 300 : return ACL_USAGE;
2618 [ + + ]: 259 : if (strcmp(privname, "create") == 0)
2619 : 123 : return ACL_CREATE;
2620 [ + + ]: 136 : if (strcmp(privname, "temporary") == 0)
2621 : 77 : return ACL_CREATE_TEMP;
2622 [ - + ]: 59 : if (strcmp(privname, "temp") == 0)
6865 tgl@sss.pgh.pa.us 2623 :UBC 0 : return ACL_CREATE_TEMP;
6559 tgl@sss.pgh.pa.us 2624 [ + + ]:CBC 59 : if (strcmp(privname, "connect") == 0)
bruce@momjian.us 2625 : 7 : return ACL_CONNECT;
739 tgl@sss.pgh.pa.us 2626 [ + + ]: 52 : if (strcmp(privname, "set") == 0)
2627 : 24 : return ACL_SET;
2628 [ + + ]: 28 : if (strcmp(privname, "alter system") == 0)
2629 : 12 : return ACL_ALTER_SYSTEM;
32 nathan@postgresql.or 2630 [ + - ]:GNC 16 : if (strcmp(privname, "maintain") == 0)
2631 : 16 : return ACL_MAINTAIN;
6431 tgl@sss.pgh.pa.us 2632 [ # # ]:UBC 0 : if (strcmp(privname, "rule") == 0)
2633 : 0 : return 0; /* ignore old RULE privileges */
6865 2634 [ # # ]: 0 : ereport(ERROR,
2635 : : (errcode(ERRCODE_SYNTAX_ERROR),
2636 : : errmsg("unrecognized privilege type \"%s\"", privname)));
2637 : : return 0; /* appease compiler */
2638 : : }
2639 : :
2640 : : static const char *
8029 tgl@sss.pgh.pa.us 2641 :CBC 12 : privilege_to_string(AclMode privilege)
2642 : : {
2643 [ + - - - : 12 : switch (privilege)
- - - - +
- - - - -
- - ]
2644 : : {
2645 : 3 : case ACL_INSERT:
2646 : 3 : return "INSERT";
8029 tgl@sss.pgh.pa.us 2647 :UBC 0 : case ACL_SELECT:
2648 : 0 : return "SELECT";
2649 : 0 : case ACL_UPDATE:
2650 : 0 : return "UPDATE";
2651 : 0 : case ACL_DELETE:
2652 : 0 : return "DELETE";
5697 2653 : 0 : case ACL_TRUNCATE:
2654 : 0 : return "TRUNCATE";
8029 2655 : 0 : case ACL_REFERENCES:
2656 : 0 : return "REFERENCES";
2657 : 0 : case ACL_TRIGGER:
2658 : 0 : return "TRIGGER";
2659 : 0 : case ACL_EXECUTE:
2660 : 0 : return "EXECUTE";
8029 tgl@sss.pgh.pa.us 2661 :CBC 9 : case ACL_USAGE:
2662 : 9 : return "USAGE";
8029 tgl@sss.pgh.pa.us 2663 :UBC 0 : case ACL_CREATE:
2664 : 0 : return "CREATE";
2665 : 0 : case ACL_CREATE_TEMP:
2666 : 0 : return "TEMP";
6559 bruce@momjian.us 2667 : 0 : case ACL_CONNECT:
tgl@sss.pgh.pa.us 2668 : 0 : return "CONNECT";
739 2669 : 0 : case ACL_SET:
2670 : 0 : return "SET";
2671 : 0 : case ACL_ALTER_SYSTEM:
2672 : 0 : return "ALTER SYSTEM";
32 nathan@postgresql.or 2673 :UNC 0 : case ACL_MAINTAIN:
2674 : 0 : return "MAINTAIN";
8029 tgl@sss.pgh.pa.us 2675 :UBC 0 : default:
7573 2676 [ # # ]: 0 : elog(ERROR, "unrecognized privilege: %d", (int) privilege);
2677 : : }
2678 : : return NULL; /* appease compiler */
2679 : : }
2680 : :
2681 : : /*
2682 : : * Standardized reporting of aclcheck permissions failures.
2683 : : *
2684 : : * Note: we do not double-quote the %s's below, because many callers
2685 : : * supply strings that might be already quoted.
2686 : : */
2687 : : void
2325 peter_e@gmx.net 2688 :CBC 1306 : aclcheck_error(AclResult aclerr, ObjectType objtype,
2689 : : const char *objectname)
2690 : : {
7573 tgl@sss.pgh.pa.us 2691 [ - + + - ]: 1306 : switch (aclerr)
2692 : : {
8023 tgl@sss.pgh.pa.us 2693 :UBC 0 : case ACLCHECK_OK:
2694 : : /* no error, so return to caller */
2695 : 0 : break;
8023 tgl@sss.pgh.pa.us 2696 :CBC 1049 : case ACLCHECK_NO_PRIV:
2697 : : {
2277 2698 [ + - - - : 1049 : const char *msg = "???";
+ - - - +
+ + + + +
- + - - -
- - + - -
+ - - - +
+ - - + +
- - ]
2699 : :
2700 : : switch (objtype)
2701 : : {
2325 peter_e@gmx.net 2702 : 3 : case OBJECT_AGGREGATE:
2703 : 3 : msg = gettext_noop("permission denied for aggregate %s");
2704 : 3 : break;
2325 peter_e@gmx.net 2705 :UBC 0 : case OBJECT_COLLATION:
2706 : 0 : msg = gettext_noop("permission denied for collation %s");
2707 : 0 : break;
2708 : 0 : case OBJECT_COLUMN:
2709 : 0 : msg = gettext_noop("permission denied for column %s");
2710 : 0 : break;
2711 : 0 : case OBJECT_CONVERSION:
2712 : 0 : msg = gettext_noop("permission denied for conversion %s");
2713 : 0 : break;
2325 peter_e@gmx.net 2714 :CBC 9 : case OBJECT_DATABASE:
2715 : 9 : msg = gettext_noop("permission denied for database %s");
2716 : 9 : break;
2325 peter_e@gmx.net 2717 :UBC 0 : case OBJECT_DOMAIN:
2718 : 0 : msg = gettext_noop("permission denied for domain %s");
2719 : 0 : break;
2720 : 0 : case OBJECT_EVENT_TRIGGER:
2721 : 0 : msg = gettext_noop("permission denied for event trigger %s");
2722 : 0 : break;
2723 : 0 : case OBJECT_EXTENSION:
2724 : 0 : msg = gettext_noop("permission denied for extension %s");
2725 : 0 : break;
2325 peter_e@gmx.net 2726 :CBC 22 : case OBJECT_FDW:
2727 : 22 : msg = gettext_noop("permission denied for foreign-data wrapper %s");
2728 : 22 : break;
2729 : 10 : case OBJECT_FOREIGN_SERVER:
2730 : 10 : msg = gettext_noop("permission denied for foreign server %s");
2731 : 10 : break;
2732 : 1 : case OBJECT_FOREIGN_TABLE:
2733 : 1 : msg = gettext_noop("permission denied for foreign table %s");
2734 : 1 : break;
2735 : 45 : case OBJECT_FUNCTION:
2736 : 45 : msg = gettext_noop("permission denied for function %s");
2737 : 45 : break;
2325 peter_e@gmx.net 2738 :GBC 6 : case OBJECT_INDEX:
2739 : 6 : msg = gettext_noop("permission denied for index %s");
2740 : 6 : break;
2325 peter_e@gmx.net 2741 :CBC 4 : case OBJECT_LANGUAGE:
2742 : 4 : msg = gettext_noop("permission denied for language %s");
2743 : 4 : break;
2325 peter_e@gmx.net 2744 :UBC 0 : case OBJECT_LARGEOBJECT:
2745 : 0 : msg = gettext_noop("permission denied for large object %s");
2746 : 0 : break;
2325 peter_e@gmx.net 2747 :GBC 3 : case OBJECT_MATVIEW:
2748 : 3 : msg = gettext_noop("permission denied for materialized view %s");
2749 : 3 : break;
2325 peter_e@gmx.net 2750 :UBC 0 : case OBJECT_OPCLASS:
2751 : 0 : msg = gettext_noop("permission denied for operator class %s");
2752 : 0 : break;
2753 : 0 : case OBJECT_OPERATOR:
2754 : 0 : msg = gettext_noop("permission denied for operator %s");
2755 : 0 : break;
2756 : 0 : case OBJECT_OPFAMILY:
2757 : 0 : msg = gettext_noop("permission denied for operator family %s");
2758 : 0 : break;
739 tgl@sss.pgh.pa.us 2759 : 0 : case OBJECT_PARAMETER_ACL:
2760 : 0 : msg = gettext_noop("permission denied for parameter %s");
2761 : 0 : break;
2325 peter_e@gmx.net 2762 : 0 : case OBJECT_POLICY:
2763 : 0 : msg = gettext_noop("permission denied for policy %s");
2764 : 0 : break;
2325 peter_e@gmx.net 2765 :CBC 6 : case OBJECT_PROCEDURE:
2766 : 6 : msg = gettext_noop("permission denied for procedure %s");
2767 : 6 : break;
2325 peter_e@gmx.net 2768 :UBC 0 : case OBJECT_PUBLICATION:
2769 : 0 : msg = gettext_noop("permission denied for publication %s");
2770 : 0 : break;
2771 : 0 : case OBJECT_ROUTINE:
2772 : 0 : msg = gettext_noop("permission denied for routine %s");
2773 : 0 : break;
2325 peter_e@gmx.net 2774 :CBC 7 : case OBJECT_SCHEMA:
2775 : 7 : msg = gettext_noop("permission denied for schema %s");
2776 : 7 : break;
2325 peter_e@gmx.net 2777 :UBC 0 : case OBJECT_SEQUENCE:
2778 : 0 : msg = gettext_noop("permission denied for sequence %s");
2779 : 0 : break;
2780 : 0 : case OBJECT_STATISTIC_EXT:
2781 : 0 : msg = gettext_noop("permission denied for statistics object %s");
2782 : 0 : break;
2783 : 0 : case OBJECT_SUBSCRIPTION:
2784 : 0 : msg = gettext_noop("permission denied for subscription %s");
2785 : 0 : break;
2325 peter_e@gmx.net 2786 :CBC 671 : case OBJECT_TABLE:
2787 : 671 : msg = gettext_noop("permission denied for table %s");
2788 : 671 : break;
2789 : 9 : case OBJECT_TABLESPACE:
2790 : 9 : msg = gettext_noop("permission denied for tablespace %s");
2791 : 9 : break;
2325 peter_e@gmx.net 2792 :UBC 0 : case OBJECT_TSCONFIGURATION:
2793 : 0 : msg = gettext_noop("permission denied for text search configuration %s");
2794 : 0 : break;
2795 : 0 : case OBJECT_TSDICTIONARY:
2796 : 0 : msg = gettext_noop("permission denied for text search dictionary %s");
2797 : 0 : break;
2325 peter_e@gmx.net 2798 :CBC 60 : case OBJECT_TYPE:
2799 : 60 : msg = gettext_noop("permission denied for type %s");
2800 : 60 : break;
2801 : 193 : case OBJECT_VIEW:
2802 : 193 : msg = gettext_noop("permission denied for view %s");
2803 : 193 : break;
2804 : : /* these currently aren't used */
2325 peter_e@gmx.net 2805 :UBC 0 : case OBJECT_ACCESS_METHOD:
2806 : : case OBJECT_AMOP:
2807 : : case OBJECT_AMPROC:
2808 : : case OBJECT_ATTRIBUTE:
2809 : : case OBJECT_CAST:
2810 : : case OBJECT_DEFAULT:
2811 : : case OBJECT_DEFACL:
2812 : : case OBJECT_DOMCONSTRAINT:
2813 : : case OBJECT_PUBLICATION_NAMESPACE:
2814 : : case OBJECT_PUBLICATION_REL:
2815 : : case OBJECT_ROLE:
2816 : : case OBJECT_RULE:
2817 : : case OBJECT_TABCONSTRAINT:
2818 : : case OBJECT_TRANSFORM:
2819 : : case OBJECT_TRIGGER:
2820 : : case OBJECT_TSPARSER:
2821 : : case OBJECT_TSTEMPLATE:
2822 : : case OBJECT_USER_MAPPING:
523 peter@eisentraut.org 2823 [ # # ]: 0 : elog(ERROR, "unsupported object type: %d", objtype);
2824 : : }
2825 : :
2325 peter_e@gmx.net 2826 [ + - ]:CBC 1049 : ereport(ERROR,
2827 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2828 : : errmsg(msg, objectname)));
2829 : : break;
2830 : : }
8023 tgl@sss.pgh.pa.us 2831 : 257 : case ACLCHECK_NOT_OWNER:
2832 : : {
2277 2833 [ + - + - : 257 : const char *msg = "???";
- - - + +
- + + + -
- + + + +
+ - + + +
+ + + + -
+ + + -
- ]
2834 : :
2835 : : switch (objtype)
2836 : : {
2325 peter_e@gmx.net 2837 : 3 : case OBJECT_AGGREGATE:
2838 : 3 : msg = gettext_noop("must be owner of aggregate %s");
2839 : 3 : break;
2325 peter_e@gmx.net 2840 :UBC 0 : case OBJECT_COLLATION:
2841 : 0 : msg = gettext_noop("must be owner of collation %s");
2842 : 0 : break;
2325 peter_e@gmx.net 2843 :CBC 9 : case OBJECT_CONVERSION:
2844 : 9 : msg = gettext_noop("must be owner of conversion %s");
2845 : 9 : break;
2325 peter_e@gmx.net 2846 :UBC 0 : case OBJECT_DATABASE:
2847 : 0 : msg = gettext_noop("must be owner of database %s");
2848 : 0 : break;
2849 : 0 : case OBJECT_DOMAIN:
2850 : 0 : msg = gettext_noop("must be owner of domain %s");
2851 : 0 : break;
2852 : 0 : case OBJECT_EVENT_TRIGGER:
2853 : 0 : msg = gettext_noop("must be owner of event trigger %s");
2854 : 0 : break;
2855 : 0 : case OBJECT_EXTENSION:
2856 : 0 : msg = gettext_noop("must be owner of extension %s");
2857 : 0 : break;
2325 peter_e@gmx.net 2858 :CBC 9 : case OBJECT_FDW:
2859 : 9 : msg = gettext_noop("must be owner of foreign-data wrapper %s");
2860 : 9 : break;
2861 : 57 : case OBJECT_FOREIGN_SERVER:
2862 : 57 : msg = gettext_noop("must be owner of foreign server %s");
2863 : 57 : break;
2325 peter_e@gmx.net 2864 :UBC 0 : case OBJECT_FOREIGN_TABLE:
2865 : 0 : msg = gettext_noop("must be owner of foreign table %s");
2866 : 0 : break;
2325 peter_e@gmx.net 2867 :CBC 21 : case OBJECT_FUNCTION:
2868 : 21 : msg = gettext_noop("must be owner of function %s");
2869 : 21 : break;
2870 : 12 : case OBJECT_INDEX:
2871 : 12 : msg = gettext_noop("must be owner of index %s");
2872 : 12 : break;
2873 : 6 : case OBJECT_LANGUAGE:
2874 : 6 : msg = gettext_noop("must be owner of language %s");
2875 : 6 : break;
2325 peter_e@gmx.net 2876 :UBC 0 : case OBJECT_LARGEOBJECT:
2877 : 0 : msg = gettext_noop("must be owner of large object %s");
2878 : 0 : break;
2879 : 0 : case OBJECT_MATVIEW:
2880 : 0 : msg = gettext_noop("must be owner of materialized view %s");
2881 : 0 : break;
2325 peter_e@gmx.net 2882 :CBC 9 : case OBJECT_OPCLASS:
2883 : 9 : msg = gettext_noop("must be owner of operator class %s");
2884 : 9 : break;
2885 : 9 : case OBJECT_OPERATOR:
2886 : 9 : msg = gettext_noop("must be owner of operator %s");
2887 : 9 : break;
2888 : 9 : case OBJECT_OPFAMILY:
2889 : 9 : msg = gettext_noop("must be owner of operator family %s");
2890 : 9 : break;
2891 : 3 : case OBJECT_PROCEDURE:
2892 : 3 : msg = gettext_noop("must be owner of procedure %s");
2893 : 3 : break;
2894 : 3 : case OBJECT_PUBLICATION:
2895 : 3 : msg = gettext_noop("must be owner of publication %s");
2896 : 3 : break;
2325 peter_e@gmx.net 2897 :UBC 0 : case OBJECT_ROUTINE:
2898 : 0 : msg = gettext_noop("must be owner of routine %s");
2899 : 0 : break;
2325 peter_e@gmx.net 2900 :CBC 3 : case OBJECT_SEQUENCE:
2901 : 3 : msg = gettext_noop("must be owner of sequence %s");
2902 : 3 : break;
2903 : 3 : case OBJECT_SUBSCRIPTION:
2904 : 3 : msg = gettext_noop("must be owner of subscription %s");
2905 : 3 : break;
2906 : 35 : case OBJECT_TABLE:
2907 : 35 : msg = gettext_noop("must be owner of table %s");
2908 : 35 : break;
2909 : 3 : case OBJECT_TYPE:
2910 : 3 : msg = gettext_noop("must be owner of type %s");
2911 : 3 : break;
2912 : 9 : case OBJECT_VIEW:
2913 : 9 : msg = gettext_noop("must be owner of view %s");
2914 : 9 : break;
2915 : 9 : case OBJECT_SCHEMA:
2916 : 9 : msg = gettext_noop("must be owner of schema %s");
2917 : 9 : break;
2918 : 18 : case OBJECT_STATISTIC_EXT:
2919 : 18 : msg = gettext_noop("must be owner of statistics object %s");
2920 : 18 : break;
2325 peter_e@gmx.net 2921 :UBC 0 : case OBJECT_TABLESPACE:
2922 : 0 : msg = gettext_noop("must be owner of tablespace %s");
2923 : 0 : break;
2325 peter_e@gmx.net 2924 :CBC 9 : case OBJECT_TSCONFIGURATION:
2925 : 9 : msg = gettext_noop("must be owner of text search configuration %s");
2926 : 9 : break;
2927 : 9 : case OBJECT_TSDICTIONARY:
2928 : 9 : msg = gettext_noop("must be owner of text search dictionary %s");
2929 : 9 : break;
2930 : :
2931 : : /*
2932 : : * Special cases: For these, the error message talks
2933 : : * about "relation", because that's where the
2934 : : * ownership is attached. See also
2935 : : * check_object_ownership().
2936 : : */
2937 : 9 : case OBJECT_COLUMN:
2938 : : case OBJECT_POLICY:
2939 : : case OBJECT_RULE:
2940 : : case OBJECT_TABCONSTRAINT:
2941 : : case OBJECT_TRIGGER:
2942 : 9 : msg = gettext_noop("must be owner of relation %s");
2943 : 9 : break;
2944 : : /* these currently aren't used */
2325 peter_e@gmx.net 2945 :UBC 0 : case OBJECT_ACCESS_METHOD:
2946 : : case OBJECT_AMOP:
2947 : : case OBJECT_AMPROC:
2948 : : case OBJECT_ATTRIBUTE:
2949 : : case OBJECT_CAST:
2950 : : case OBJECT_DEFAULT:
2951 : : case OBJECT_DEFACL:
2952 : : case OBJECT_DOMCONSTRAINT:
2953 : : case OBJECT_PARAMETER_ACL:
2954 : : case OBJECT_PUBLICATION_NAMESPACE:
2955 : : case OBJECT_PUBLICATION_REL:
2956 : : case OBJECT_ROLE:
2957 : : case OBJECT_TRANSFORM:
2958 : : case OBJECT_TSPARSER:
2959 : : case OBJECT_TSTEMPLATE:
2960 : : case OBJECT_USER_MAPPING:
523 peter@eisentraut.org 2961 [ # # ]: 0 : elog(ERROR, "unsupported object type: %d", objtype);
2962 : : }
2963 : :
2325 peter_e@gmx.net 2964 [ + - ]:CBC 257 : ereport(ERROR,
2965 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2966 : : errmsg(msg, objectname)));
2967 : : break;
2968 : : }
8023 tgl@sss.pgh.pa.us 2969 :UBC 0 : default:
7573 2970 [ # # ]: 0 : elog(ERROR, "unrecognized AclResult: %d", (int) aclerr);
2971 : : break;
2972 : : }
8023 2973 : 0 : }
2974 : :
2975 : :
2976 : : void
2325 peter_e@gmx.net 2977 : 0 : aclcheck_error_col(AclResult aclerr, ObjectType objtype,
2978 : : const char *objectname, const char *colname)
2979 : : {
5561 tgl@sss.pgh.pa.us 2980 [ # # # # ]: 0 : switch (aclerr)
2981 : : {
2982 : 0 : case ACLCHECK_OK:
2983 : : /* no error, so return to caller */
2984 : 0 : break;
2985 : 0 : case ACLCHECK_NO_PRIV:
2986 [ # # ]: 0 : ereport(ERROR,
2987 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2988 : : errmsg("permission denied for column \"%s\" of relation \"%s\"",
2989 : : colname, objectname)));
2990 : : break;
2991 : 0 : case ACLCHECK_NOT_OWNER:
2992 : : /* relation msg is OK since columns don't have separate owners */
2325 peter_e@gmx.net 2993 : 0 : aclcheck_error(aclerr, objtype, objectname);
5561 tgl@sss.pgh.pa.us 2994 : 0 : break;
2995 : 0 : default:
2996 [ # # ]: 0 : elog(ERROR, "unrecognized AclResult: %d", (int) aclerr);
2997 : : break;
2998 : : }
2999 : 0 : }
3000 : :
3001 : :
3002 : : /*
3003 : : * Special common handling for types: use element type instead of array type,
3004 : : * and format nicely
3005 : : */
3006 : : void
4321 peter_e@gmx.net 3007 :CBC 60 : aclcheck_error_type(AclResult aclerr, Oid typeOid)
3008 : : {
3973 bruce@momjian.us 3009 : 60 : Oid element_type = get_element_type(typeOid);
3010 : :
2325 peter_e@gmx.net 3011 [ + + ]: 60 : aclcheck_error(aclerr, OBJECT_TYPE, format_type_be(element_type ? element_type : typeOid));
4321 peter_e@gmx.net 3012 :UBC 0 : }
3013 : :
3014 : :
3015 : : /*
3016 : : * Relay for the various pg_*_mask routines depending on object kind
3017 : : */
3018 : : static AclMode
518 peter@eisentraut.org 3019 :CBC 33 : pg_aclmask(ObjectType objtype, Oid object_oid, AttrNumber attnum, Oid roleid,
3020 : : AclMode mask, AclMaskHow how)
3021 : : {
2325 peter_e@gmx.net 3022 [ - + - - : 33 : switch (objtype)
+ - - - -
- + + - +
- ]
3023 : : {
2325 peter_e@gmx.net 3024 :UBC 0 : case OBJECT_COLUMN:
3025 : : return
518 peter@eisentraut.org 3026 : 0 : pg_class_aclmask(object_oid, roleid, mask, how) |
3027 : 0 : pg_attribute_aclmask(object_oid, attnum, roleid, mask, how);
2325 peter_e@gmx.net 3028 :CBC 6 : case OBJECT_TABLE:
3029 : : case OBJECT_SEQUENCE:
518 peter@eisentraut.org 3030 : 6 : return pg_class_aclmask(object_oid, roleid, mask, how);
2325 peter_e@gmx.net 3031 :UBC 0 : case OBJECT_DATABASE:
518 peter@eisentraut.org 3032 : 0 : return object_aclmask(DatabaseRelationId, object_oid, roleid, mask, how);
2325 peter_e@gmx.net 3033 : 0 : case OBJECT_FUNCTION:
518 peter@eisentraut.org 3034 : 0 : return object_aclmask(ProcedureRelationId, object_oid, roleid, mask, how);
2325 peter_e@gmx.net 3035 :CBC 3 : case OBJECT_LANGUAGE:
518 peter@eisentraut.org 3036 : 3 : return object_aclmask(LanguageRelationId, object_oid, roleid, mask, how);
2325 peter_e@gmx.net 3037 :UBC 0 : case OBJECT_LARGEOBJECT:
518 peter@eisentraut.org 3038 : 0 : return pg_largeobject_aclmask_snapshot(object_oid, roleid,
3039 : : mask, how, NULL);
739 tgl@sss.pgh.pa.us 3040 : 0 : case OBJECT_PARAMETER_ACL:
518 peter@eisentraut.org 3041 : 0 : return pg_parameter_acl_aclmask(object_oid, roleid, mask, how);
2325 peter_e@gmx.net 3042 : 0 : case OBJECT_SCHEMA:
518 peter@eisentraut.org 3043 : 0 : return object_aclmask(NamespaceRelationId, object_oid, roleid, mask, how);
2325 peter_e@gmx.net 3044 : 0 : case OBJECT_STATISTIC_EXT:
2527 tgl@sss.pgh.pa.us 3045 [ # # ]: 0 : elog(ERROR, "grantable rights not supported for statistics objects");
3046 : : /* not reached, but keep compiler quiet */
3047 : : return ACL_NO_RIGHTS;
2325 peter_e@gmx.net 3048 : 0 : case OBJECT_TABLESPACE:
518 peter@eisentraut.org 3049 : 0 : return object_aclmask(TableSpaceRelationId, object_oid, roleid, mask, how);
2325 peter_e@gmx.net 3050 :CBC 9 : case OBJECT_FDW:
518 peter@eisentraut.org 3051 : 9 : return object_aclmask(ForeignDataWrapperRelationId, object_oid, roleid, mask, how);
2325 peter_e@gmx.net 3052 : 9 : case OBJECT_FOREIGN_SERVER:
518 peter@eisentraut.org 3053 : 9 : return object_aclmask(ForeignServerRelationId, object_oid, roleid, mask, how);
2325 peter_e@gmx.net 3054 :UBC 0 : case OBJECT_EVENT_TRIGGER:
4288 rhaas@postgresql.org 3055 [ # # ]: 0 : elog(ERROR, "grantable rights not supported for event triggers");
3056 : : /* not reached, but keep compiler quiet */
3057 : : return ACL_NO_RIGHTS;
2325 peter_e@gmx.net 3058 :CBC 6 : case OBJECT_TYPE:
518 peter@eisentraut.org 3059 : 6 : return object_aclmask(TypeRelationId, object_oid, roleid, mask, how);
6709 alvherre@alvh.no-ip. 3060 :UBC 0 : default:
523 peter@eisentraut.org 3061 [ # # ]: 0 : elog(ERROR, "unrecognized object type: %d",
3062 : : (int) objtype);
3063 : : /* not reached, but keep compiler quiet */
3064 : : return ACL_NO_RIGHTS;
3065 : : }
3066 : : }
3067 : :
3068 : :
3069 : : /* ****************************************************************
3070 : : * Exported routines for examining a user's privileges for various objects
3071 : : *
3072 : : * See aclmask() for a description of the common API for these functions.
3073 : : *
3074 : : * Note: we give lookup failure the full ereport treatment because the
3075 : : * has_xxx_privilege() family of functions allow users to pass any random
3076 : : * OID to these functions.
3077 : : * ****************************************************************
3078 : : */
3079 : :
3080 : : /*
3081 : : * Generic routine for examining a user's privileges for an object
3082 : : */
3083 : : static AclMode
518 peter@eisentraut.org 3084 :CBC 27 : object_aclmask(Oid classid, Oid objectid, Oid roleid,
3085 : : AclMode mask, AclMaskHow how)
3086 : : {
183 tgl@sss.pgh.pa.us 3087 :GNC 27 : return object_aclmask_ext(classid, objectid, roleid, mask, how, NULL);
3088 : : }
3089 : :
3090 : : /*
3091 : : * Generic routine for examining a user's privileges for an object,
3092 : : * with is_missing
3093 : : */
3094 : : static AclMode
3095 : 1438031 : object_aclmask_ext(Oid classid, Oid objectid, Oid roleid,
3096 : : AclMode mask, AclMaskHow how,
3097 : : bool *is_missing)
3098 : : {
3099 : : int cacheid;
3100 : : AclMode result;
3101 : : HeapTuple tuple;
3102 : : Datum aclDatum;
3103 : : bool isNull;
3104 : : Acl *acl;
3105 : : Oid ownerId;
3106 : :
3107 : : /* Special cases */
518 peter@eisentraut.org 3108 [ + + + ]:CBC 1438031 : switch (classid)
3109 : : {
3110 : 402569 : case NamespaceRelationId:
183 tgl@sss.pgh.pa.us 3111 :GNC 402569 : return pg_namespace_aclmask_ext(objectid, roleid, mask, how,
3112 : : is_missing);
518 peter@eisentraut.org 3113 :CBC 143562 : case TypeRelationId:
183 tgl@sss.pgh.pa.us 3114 :GNC 143562 : return pg_type_aclmask_ext(objectid, roleid, mask, how,
3115 : : is_missing);
3116 : : }
3117 : :
3118 : : /* Even more special cases */
518 peter@eisentraut.org 3119 [ - + ]:CBC 891900 : Assert(classid != RelationRelationId); /* should use pg_class_acl* */
3120 [ - + ]: 891900 : Assert(classid != LargeObjectMetadataRelationId); /* should use
3121 : : * pg_largeobject_acl* */
3122 : :
3123 : : /* Superusers bypass all permission checking. */
3124 [ + + ]: 891900 : if (superuser_arg(roleid))
3125 : 869894 : return mask;
3126 : :
3127 : : /*
3128 : : * Get the object's ACL from its catalog
3129 : : */
3130 : :
3131 : 22006 : cacheid = get_object_catcache_oid(classid);
3132 : :
3133 : 22006 : tuple = SearchSysCache1(cacheid, ObjectIdGetDatum(objectid));
3134 [ - + ]: 22006 : if (!HeapTupleIsValid(tuple))
3135 : : {
183 tgl@sss.pgh.pa.us 3136 [ # # ]:UNC 0 : if (is_missing != NULL)
3137 : : {
3138 : : /* return "no privileges" instead of throwing an error */
3139 : 0 : *is_missing = true;
3140 : 0 : return 0;
3141 : : }
3142 : : else
3143 [ # # ]: 0 : ereport(ERROR,
3144 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
3145 : : errmsg("%s with OID %u does not exist",
3146 : : get_object_class_descr(classid), objectid)));
3147 : : }
3148 : :
386 dgustafsson@postgres 3149 :CBC 22006 : ownerId = DatumGetObjectId(SysCacheGetAttrNotNull(cacheid,
3150 : : tuple,
3151 : 22006 : get_object_attnum_owner(classid)));
3152 : :
518 peter@eisentraut.org 3153 : 22006 : aclDatum = SysCacheGetAttr(cacheid, tuple, get_object_attnum_acl(classid),
3154 : : &isNull);
3155 [ + + ]: 22006 : if (isNull)
3156 : : {
3157 : : /* No ACL, so build default ACL */
3158 : 20751 : acl = acldefault(get_object_type(classid, objectid), ownerId);
3159 : 20751 : aclDatum = (Datum) 0;
3160 : : }
3161 : : else
3162 : : {
3163 : : /* detoast ACL if necessary */
3164 : 1255 : acl = DatumGetAclP(aclDatum);
3165 : : }
3166 : :
3167 : 22006 : result = aclmask(acl, roleid, ownerId, mask, how);
3168 : :
3169 : : /* if we have a detoasted copy, free it */
3170 [ + - + - ]: 22006 : if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
3171 : 22006 : pfree(acl);
3172 : :
3173 : 22006 : ReleaseSysCache(tuple);
3174 : :
3175 : 22006 : return result;
3176 : : }
3177 : :
3178 : : /*
3179 : : * Routine for examining a user's privileges for a column
3180 : : *
3181 : : * Note: this considers only privileges granted specifically on the column.
3182 : : * It is caller's responsibility to take relation-level privileges into account
3183 : : * as appropriate. (For the same reason, we have no special case for
3184 : : * superuser-ness here.)
3185 : : */
3186 : : static AclMode
5561 tgl@sss.pgh.pa.us 3187 :LBC (78) : pg_attribute_aclmask(Oid table_oid, AttrNumber attnum, Oid roleid,
3188 : : AclMode mask, AclMaskHow how)
3189 : : {
1110 mail@joeconway.com 3190 : (78) : return pg_attribute_aclmask_ext(table_oid, attnum, roleid,
3191 : : mask, how, NULL);
3192 : : }
3193 : :
3194 : : /*
3195 : : * Routine for examining a user's privileges for a column, with is_missing
3196 : : */
3197 : : static AclMode
1110 mail@joeconway.com 3198 :CBC 2994 : pg_attribute_aclmask_ext(Oid table_oid, AttrNumber attnum, Oid roleid,
3199 : : AclMode mask, AclMaskHow how, bool *is_missing)
3200 : : {
3201 : : AclMode result;
3202 : : HeapTuple classTuple;
3203 : : HeapTuple attTuple;
3204 : : Form_pg_class classForm;
3205 : : Form_pg_attribute attributeForm;
3206 : : Datum aclDatum;
3207 : : bool isNull;
3208 : : Acl *acl;
3209 : : Oid ownerId;
3210 : :
3211 : : /*
3212 : : * First, get the column's ACL from its pg_attribute entry
3213 : : */
5173 rhaas@postgresql.org 3214 : 2994 : attTuple = SearchSysCache2(ATTNUM,
3215 : : ObjectIdGetDatum(table_oid),
3216 : : Int16GetDatum(attnum));
5561 tgl@sss.pgh.pa.us 3217 [ + + ]: 2994 : if (!HeapTupleIsValid(attTuple))
3218 : : {
1110 mail@joeconway.com 3219 [ + - ]: 15 : if (is_missing != NULL)
3220 : : {
3221 : : /* return "no privileges" instead of throwing an error */
3222 : 15 : *is_missing = true;
3223 : 15 : return 0;
3224 : : }
3225 : : else
1110 mail@joeconway.com 3226 [ # # ]:UBC 0 : ereport(ERROR,
3227 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
3228 : : errmsg("attribute %d of relation with OID %u does not exist",
3229 : : attnum, table_oid)));
3230 : : }
3231 : :
5561 tgl@sss.pgh.pa.us 3232 :CBC 2979 : attributeForm = (Form_pg_attribute) GETSTRUCT(attTuple);
3233 : :
3234 : : /* Check dropped columns, too */
3235 [ + + ]: 2979 : if (attributeForm->attisdropped)
3236 : : {
1110 mail@joeconway.com 3237 [ + - ]: 6 : if (is_missing != NULL)
3238 : : {
3239 : : /* return "no privileges" instead of throwing an error */
3240 : 6 : *is_missing = true;
3241 : 6 : ReleaseSysCache(attTuple);
3242 : 6 : return 0;
3243 : : }
3244 : : else
1110 mail@joeconway.com 3245 [ # # ]:UBC 0 : ereport(ERROR,
3246 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
3247 : : errmsg("attribute %d of relation with OID %u does not exist",
3248 : : attnum, table_oid)));
3249 : : }
3250 : :
5561 tgl@sss.pgh.pa.us 3251 :CBC 2973 : aclDatum = SysCacheGetAttr(ATTNUM, attTuple, Anum_pg_attribute_attacl,
3252 : : &isNull);
3253 : :
3254 : : /*
3255 : : * Here we hard-wire knowledge that the default ACL for a column grants no
3256 : : * privileges, so that we can fall out quickly in the very common case
3257 : : * where attacl is null.
3258 : : */
3259 [ + + ]: 2973 : if (isNull)
3260 : : {
5546 3261 : 1726 : ReleaseSysCache(attTuple);
3262 : 1726 : return 0;
3263 : : }
3264 : :
3265 : : /*
3266 : : * Must get the relation's ownerId from pg_class. Since we already found
3267 : : * a pg_attribute entry, the only likely reason for this to fail is that a
3268 : : * concurrent DROP of the relation committed since then (which could only
3269 : : * happen if we don't have lock on the relation). Treat that similarly to
3270 : : * not finding the attribute entry.
3271 : : */
5173 rhaas@postgresql.org 3272 : 1247 : classTuple = SearchSysCache1(RELOID, ObjectIdGetDatum(table_oid));
5546 tgl@sss.pgh.pa.us 3273 [ - + ]: 1247 : if (!HeapTupleIsValid(classTuple))
3274 : : {
5546 tgl@sss.pgh.pa.us 3275 :UBC 0 : ReleaseSysCache(attTuple);
183 tgl@sss.pgh.pa.us 3276 [ # # ]:UNC 0 : if (is_missing != NULL)
3277 : : {
3278 : : /* return "no privileges" instead of throwing an error */
3279 : 0 : *is_missing = true;
3280 : 0 : return 0;
3281 : : }
3282 : : else
3283 [ # # ]: 0 : ereport(ERROR,
3284 : : (errcode(ERRCODE_UNDEFINED_TABLE),
3285 : : errmsg("relation with OID %u does not exist",
3286 : : table_oid)));
3287 : : }
5546 tgl@sss.pgh.pa.us 3288 :CBC 1247 : classForm = (Form_pg_class) GETSTRUCT(classTuple);
3289 : :
3290 : 1247 : ownerId = classForm->relowner;
3291 : :
3292 : 1247 : ReleaseSysCache(classTuple);
3293 : :
3294 : : /* detoast column's ACL if necessary */
3295 : 1247 : acl = DatumGetAclP(aclDatum);
3296 : :
5561 3297 : 1247 : result = aclmask(acl, roleid, ownerId, mask, how);
3298 : :
3299 : : /* if we have a detoasted copy, free it */
3300 [ + - + - ]: 1247 : if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
3301 : 1247 : pfree(acl);
3302 : :
3303 : 1247 : ReleaseSysCache(attTuple);
3304 : :
3305 : 1247 : return result;
3306 : : }
3307 : :
3308 : : /*
3309 : : * Exported routine for examining a user's privileges for a table
3310 : : */
3311 : : AclMode
6865 3312 : 247468 : pg_class_aclmask(Oid table_oid, Oid roleid,
3313 : : AclMode mask, AclMaskHow how)
3314 : : {
1110 mail@joeconway.com 3315 : 247468 : return pg_class_aclmask_ext(table_oid, roleid, mask, how, NULL);
3316 : : }
3317 : :
3318 : : /*
3319 : : * Routine for examining a user's privileges for a table, with is_missing
3320 : : */
3321 : : static AclMode
3322 : 1318020 : pg_class_aclmask_ext(Oid table_oid, Oid roleid, AclMode mask,
3323 : : AclMaskHow how, bool *is_missing)
3324 : : {
3325 : : AclMode result;
3326 : : HeapTuple tuple;
3327 : : Form_pg_class classForm;
3328 : : Datum aclDatum;
3329 : : bool isNull;
3330 : : Acl *acl;
3331 : : Oid ownerId;
3332 : :
3333 : : /*
3334 : : * Must get the relation's tuple from pg_class
3335 : : */
5173 rhaas@postgresql.org 3336 : 1318020 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(table_oid));
8060 tgl@sss.pgh.pa.us 3337 [ + + ]: 1318020 : if (!HeapTupleIsValid(tuple))
3338 : : {
1110 mail@joeconway.com 3339 [ + - ]:GBC 4 : if (is_missing != NULL)
3340 : : {
3341 : : /* return "no privileges" instead of throwing an error */
3342 : 4 : *is_missing = true;
3343 : 4 : return 0;
3344 : : }
3345 : : else
1110 mail@joeconway.com 3346 [ # # ]:UBC 0 : ereport(ERROR,
3347 : : (errcode(ERRCODE_UNDEFINED_TABLE),
3348 : : errmsg("relation with OID %u does not exist",
3349 : : table_oid)));
3350 : : }
3351 : :
7396 tgl@sss.pgh.pa.us 3352 :CBC 1318016 : classForm = (Form_pg_class) GETSTRUCT(tuple);
3353 : :
3354 : : /*
3355 : : * Deny anyone permission to update a system catalog unless
3356 : : * pg_authid.rolsuper is set.
3357 : : *
3358 : : * As of 7.4 we have some updatable system views; those shouldn't be
3359 : : * protected in this way. Assume the view rules can take care of
3360 : : * themselves. ACL_USAGE is if we ever have system sequences.
3361 : : */
5697 3362 [ + + + + ]: 1727151 : if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE)) &&
3790 rhaas@postgresql.org 3363 : 409135 : IsSystemClass(table_oid, classForm) &&
7396 tgl@sss.pgh.pa.us 3364 [ + - ]: 2257 : classForm->relkind != RELKIND_VIEW &&
1598 peter@eisentraut.org 3365 [ + + ]: 2257 : !superuser_arg(roleid))
5697 tgl@sss.pgh.pa.us 3366 : 35 : mask &= ~(ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE);
3367 : :
3368 : : /*
3369 : : * Otherwise, superusers bypass all permission-checking.
3370 : : */
6865 3371 [ + + ]: 1318016 : if (superuser_arg(roleid))
3372 : : {
8550 3373 : 1301272 : ReleaseSysCache(tuple);
7278 3374 : 1301272 : return mask;
3375 : : }
3376 : :
3377 : : /*
3378 : : * Normal case: get the relation's ACL from pg_class
3379 : : */
7257 3380 : 16744 : ownerId = classForm->relowner;
3381 : :
8060 3382 : 16744 : aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl,
3383 : : &isNull);
8595 3384 [ + + ]: 16744 : if (isNull)
3385 : : {
3386 : : /* No ACL, so build default ACL */
4852 rhaas@postgresql.org 3387 [ + + ]: 2930 : switch (classForm->relkind)
3388 : : {
3389 : 18 : case RELKIND_SEQUENCE:
2377 peter_e@gmx.net 3390 : 18 : acl = acldefault(OBJECT_SEQUENCE, ownerId);
4852 rhaas@postgresql.org 3391 : 18 : break;
3392 : 2912 : default:
2377 peter_e@gmx.net 3393 : 2912 : acl = acldefault(OBJECT_TABLE, ownerId);
4852 rhaas@postgresql.org 3394 : 2912 : break;
3395 : : }
8349 tgl@sss.pgh.pa.us 3396 : 2930 : aclDatum = (Datum) 0;
3397 : : }
3398 : : else
3399 : : {
3400 : : /* detoast rel's ACL if necessary */
3401 : 13814 : acl = DatumGetAclP(aclDatum);
3402 : : }
3403 : :
6865 3404 : 16744 : result = aclmask(acl, roleid, ownerId, mask, how);
3405 : :
3406 : : /* if we have a detoasted copy, free it */
8349 3407 [ + - + - ]: 16744 : if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
9716 bruce@momjian.us 3408 : 16744 : pfree(acl);
3409 : :
8550 tgl@sss.pgh.pa.us 3410 : 16744 : ReleaseSysCache(tuple);
3411 : :
3412 : : /*
3413 : : * Check if ACL_SELECT is being checked and, if so, and not set already as
3414 : : * part of the result, then check if the user is a member of the
3415 : : * pg_read_all_data role, which allows read access to all relations.
3416 : : */
1105 sfrost@snowman.net 3417 [ + + + + : 17718 : if (mask & ACL_SELECT && !(result & ACL_SELECT) &&
+ + ]
1100 noah@leadboat.com 3418 : 974 : has_privs_of_role(roleid, ROLE_PG_READ_ALL_DATA))
1105 sfrost@snowman.net 3419 : 6 : result |= ACL_SELECT;
3420 : :
3421 : : /*
3422 : : * Check if ACL_INSERT, ACL_UPDATE, or ACL_DELETE is being checked and, if
3423 : : * so, and not set already as part of the result, then check if the user
3424 : : * is a member of the pg_write_all_data role, which allows
3425 : : * INSERT/UPDATE/DELETE access to all relations (except system catalogs,
3426 : : * which requires superuser, see above).
3427 : : */
3428 [ + + ]: 16744 : if (mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE) &&
1068 tgl@sss.pgh.pa.us 3429 [ + + + + ]: 3643 : !(result & (ACL_INSERT | ACL_UPDATE | ACL_DELETE)) &&
1100 noah@leadboat.com 3430 : 838 : has_privs_of_role(roleid, ROLE_PG_WRITE_ALL_DATA))
1105 sfrost@snowman.net 3431 : 9 : result |= (mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE));
3432 : :
3433 : : /*
3434 : : * Check if ACL_MAINTAIN is being checked and, if so, and not already set
3435 : : * as part of the result, then check if the user is a member of the
3436 : : * pg_maintain role, which allows VACUUM, ANALYZE, CLUSTER, REFRESH
3437 : : * MATERIALIZED VIEW, and REINDEX on all relations.
3438 : : */
32 nathan@postgresql.or 3439 [ + + ]:GNC 16744 : if (mask & ACL_MAINTAIN &&
3440 [ + + + + ]: 944 : !(result & ACL_MAINTAIN) &&
3441 : 336 : has_privs_of_role(roleid, ROLE_PG_MAINTAIN))
3442 : 33 : result |= ACL_MAINTAIN;
3443 : :
9357 bruce@momjian.us 3444 :CBC 16744 : return result;
3445 : : }
3446 : :
3447 : : /*
3448 : : * Routine for examining a user's privileges for a configuration
3449 : : * parameter (GUC), identified by GUC name.
3450 : : */
3451 : : static AclMode
739 tgl@sss.pgh.pa.us 3452 : 80 : pg_parameter_aclmask(const char *name, Oid roleid, AclMode mask, AclMaskHow how)
3453 : : {
3454 : : AclMode result;
3455 : : char *parname;
3456 : : text *partext;
3457 : : HeapTuple tuple;
3458 : :
3459 : : /* Superusers bypass all permission checking. */
3460 [ + + ]: 80 : if (superuser_arg(roleid))
3461 : 1 : return mask;
3462 : :
3463 : : /* Convert name to the form it should have in pg_parameter_acl... */
3464 : 79 : parname = convert_GUC_name_for_parameter_acl(name);
3465 : 79 : partext = cstring_to_text(parname);
3466 : :
3467 : : /* ... and look it up */
3468 : 79 : tuple = SearchSysCache1(PARAMETERACLNAME, PointerGetDatum(partext));
3469 : :
3470 [ + + ]: 79 : if (!HeapTupleIsValid(tuple))
3471 : : {
3472 : : /* If no entry, GUC has no permissions for non-superusers */
3473 : 35 : result = ACL_NO_RIGHTS;
3474 : : }
3475 : : else
3476 : : {
3477 : : Datum aclDatum;
3478 : : bool isNull;
3479 : : Acl *acl;
3480 : :
3481 : 44 : aclDatum = SysCacheGetAttr(PARAMETERACLNAME, tuple,
3482 : : Anum_pg_parameter_acl_paracl,
3483 : : &isNull);
3484 [ - + ]: 44 : if (isNull)
3485 : : {
3486 : : /* No ACL, so build default ACL */
739 tgl@sss.pgh.pa.us 3487 :UBC 0 : acl = acldefault(OBJECT_PARAMETER_ACL, BOOTSTRAP_SUPERUSERID);
3488 : 0 : aclDatum = (Datum) 0;
3489 : : }
3490 : : else
3491 : : {
3492 : : /* detoast ACL if necessary */
739 tgl@sss.pgh.pa.us 3493 :CBC 44 : acl = DatumGetAclP(aclDatum);
3494 : : }
3495 : :
3496 : 44 : result = aclmask(acl, roleid, BOOTSTRAP_SUPERUSERID, mask, how);
3497 : :
3498 : : /* if we have a detoasted copy, free it */
3499 [ + - + - ]: 44 : if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
3500 : 44 : pfree(acl);
3501 : :
3502 : 44 : ReleaseSysCache(tuple);
3503 : : }
3504 : :
3505 : 79 : pfree(parname);
3506 : 79 : pfree(partext);
3507 : :
3508 : 79 : return result;
3509 : : }
3510 : :
3511 : : /*
3512 : : * Routine for examining a user's privileges for a configuration
3513 : : * parameter (GUC), identified by the OID of its pg_parameter_acl entry.
3514 : : */
3515 : : static AclMode
739 tgl@sss.pgh.pa.us 3516 :UBC 0 : pg_parameter_acl_aclmask(Oid acl_oid, Oid roleid, AclMode mask, AclMaskHow how)
3517 : : {
3518 : : AclMode result;
3519 : : HeapTuple tuple;
3520 : : Datum aclDatum;
3521 : : bool isNull;
3522 : : Acl *acl;
3523 : :
3524 : : /* Superusers bypass all permission checking. */
3525 [ # # ]: 0 : if (superuser_arg(roleid))
3526 : 0 : return mask;
3527 : :
3528 : : /* Get the ACL from pg_parameter_acl */
3529 : 0 : tuple = SearchSysCache1(PARAMETERACLOID, ObjectIdGetDatum(acl_oid));
3530 [ # # ]: 0 : if (!HeapTupleIsValid(tuple))
3531 [ # # ]: 0 : ereport(ERROR,
3532 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
3533 : : errmsg("parameter ACL with OID %u does not exist",
3534 : : acl_oid)));
3535 : :
3536 : 0 : aclDatum = SysCacheGetAttr(PARAMETERACLOID, tuple,
3537 : : Anum_pg_parameter_acl_paracl,
3538 : : &isNull);
3539 [ # # ]: 0 : if (isNull)
3540 : : {
3541 : : /* No ACL, so build default ACL */
3542 : 0 : acl = acldefault(OBJECT_PARAMETER_ACL, BOOTSTRAP_SUPERUSERID);
3543 : 0 : aclDatum = (Datum) 0;
3544 : : }
3545 : : else
3546 : : {
3547 : : /* detoast ACL if necessary */
3548 : 0 : acl = DatumGetAclP(aclDatum);
3549 : : }
3550 : :
3551 : 0 : result = aclmask(acl, roleid, BOOTSTRAP_SUPERUSERID, mask, how);
3552 : :
3553 : : /* if we have a detoasted copy, free it */
3554 [ # # # # ]: 0 : if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
3555 : 0 : pfree(acl);
3556 : :
3557 : 0 : ReleaseSysCache(tuple);
3558 : :
3559 : 0 : return result;
3560 : : }
3561 : :
3562 : : /*
3563 : : * Routine for examining a user's privileges for a largeobject
3564 : : *
3565 : : * When a large object is opened for reading, it is opened relative to the
3566 : : * caller's snapshot, but when it is opened for writing, a current
3567 : : * MVCC snapshot will be used. See doc/src/sgml/lobj.sgml. This function
3568 : : * takes a snapshot argument so that the permissions check can be made
3569 : : * relative to the same snapshot that will be used to read the underlying
3570 : : * data. The caller will actually pass NULL for an instantaneous MVCC
3571 : : * snapshot, since all we do with the snapshot argument is pass it through
3572 : : * to systable_beginscan().
3573 : : */
3574 : : static AclMode
5238 itagaki.takahiro@gma 3575 :CBC 292 : pg_largeobject_aclmask_snapshot(Oid lobj_oid, Oid roleid,
3576 : : AclMode mask, AclMaskHow how,
3577 : : Snapshot snapshot)
3578 : : {
3579 : : AclMode result;
3580 : : Relation pg_lo_meta;
3581 : : ScanKeyData entry[1];
3582 : : SysScanDesc scan;
3583 : : HeapTuple tuple;
3584 : : Datum aclDatum;
3585 : : bool isNull;
3586 : : Acl *acl;
3587 : : Oid ownerId;
3588 : :
3589 : : /* Superusers bypass all permission checking. */
3590 [ + + ]: 292 : if (superuser_arg(roleid))
3591 : 217 : return mask;
3592 : :
3593 : : /*
3594 : : * Get the largeobject's ACL from pg_largeobject_metadata
3595 : : */
1910 andres@anarazel.de 3596 : 75 : pg_lo_meta = table_open(LargeObjectMetadataRelationId,
3597 : : AccessShareLock);
3598 : :
5238 itagaki.takahiro@gma 3599 : 75 : ScanKeyInit(&entry[0],
3600 : : Anum_pg_largeobject_metadata_oid,
3601 : : BTEqualStrategyNumber, F_OIDEQ,
3602 : : ObjectIdGetDatum(lobj_oid));
3603 : :
3604 : 75 : scan = systable_beginscan(pg_lo_meta,
3605 : : LargeObjectMetadataOidIndexId, true,
3606 : : snapshot, 1, entry);
3607 : :
3608 : 75 : tuple = systable_getnext(scan);
3609 [ - + ]: 75 : if (!HeapTupleIsValid(tuple))
5238 itagaki.takahiro@gma 3610 [ # # ]:UBC 0 : ereport(ERROR,
3611 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
3612 : : errmsg("large object %u does not exist", lobj_oid)));
3613 : :
5238 itagaki.takahiro@gma 3614 :CBC 75 : ownerId = ((Form_pg_largeobject_metadata) GETSTRUCT(tuple))->lomowner;
3615 : :
3616 : 75 : aclDatum = heap_getattr(tuple, Anum_pg_largeobject_metadata_lomacl,
3617 : : RelationGetDescr(pg_lo_meta), &isNull);
3618 : :
3619 [ + + ]: 75 : if (isNull)
3620 : : {
3621 : : /* No ACL, so build default ACL */
2377 peter_e@gmx.net 3622 : 18 : acl = acldefault(OBJECT_LARGEOBJECT, ownerId);
5238 itagaki.takahiro@gma 3623 : 18 : aclDatum = (Datum) 0;
3624 : : }
3625 : : else
3626 : : {
3627 : : /* detoast ACL if necessary */
3628 : 57 : acl = DatumGetAclP(aclDatum);
3629 : : }
3630 : :
3631 : 75 : result = aclmask(acl, roleid, ownerId, mask, how);
3632 : :
3633 : : /* if we have a detoasted copy, free it */
3634 [ + - + - ]: 75 : if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
3635 : 75 : pfree(acl);
3636 : :
3637 : 75 : systable_endscan(scan);
3638 : :
1910 andres@anarazel.de 3639 : 75 : table_close(pg_lo_meta, AccessShareLock);
3640 : :
5238 itagaki.takahiro@gma 3641 : 75 : return result;
3642 : : }
3643 : :
3644 : : /*
3645 : : * Routine for examining a user's privileges for a namespace, with is_missing
3646 : : */
3647 : : static AclMode
183 tgl@sss.pgh.pa.us 3648 :GNC 402569 : pg_namespace_aclmask_ext(Oid nsp_oid, Oid roleid,
3649 : : AclMode mask, AclMaskHow how,
3650 : : bool *is_missing)
3651 : : {
3652 : : AclMode result;
3653 : : HeapTuple tuple;
3654 : : Datum aclDatum;
3655 : : bool isNull;
3656 : : Acl *acl;
3657 : : Oid ownerId;
3658 : :
3659 : : /* Superusers bypass all permission checking. */
6865 tgl@sss.pgh.pa.us 3660 [ + + ]:CBC 402569 : if (superuser_arg(roleid))
7278 3661 : 394128 : return mask;
3662 : :
3663 : : /*
3664 : : * If we have been assigned this namespace as a temp namespace, check to
3665 : : * make sure we have CREATE TEMP permission on the database, and if so act
3666 : : * as though we have all standard (but not GRANT OPTION) permissions on
3667 : : * the namespace. If we don't have CREATE TEMP, act as though we have
3668 : : * only USAGE (and not CREATE) rights.
3669 : : *
3670 : : * This may seem redundant given the check in InitTempTableNamespace, but
3671 : : * it really isn't since current user ID may have changed since then. The
3672 : : * upshot of this behavior is that a SECURITY DEFINER function can create
3673 : : * temp tables that can then be accessed (if permission is granted) by
3674 : : * code in the same session that doesn't have permissions to create temp
3675 : : * tables.
3676 : : *
3677 : : * XXX Would it be safe to ereport a special error message as
3678 : : * InitTempTableNamespace does? Returning zero here means we'll get a
3679 : : * generic "permission denied for schema pg_temp_N" message, which is not
3680 : : * remarkably user-friendly.
3681 : : */
7261 3682 [ + + ]: 8441 : if (isTempNamespace(nsp_oid))
3683 : : {
183 tgl@sss.pgh.pa.us 3684 [ + - ]:GNC 141 : if (object_aclcheck_ext(DatabaseRelationId, MyDatabaseId, roleid,
3685 : : ACL_CREATE_TEMP, is_missing) == ACLCHECK_OK)
2377 peter_e@gmx.net 3686 :CBC 141 : return mask & ACL_ALL_RIGHTS_SCHEMA;
3687 : : else
7261 tgl@sss.pgh.pa.us 3688 :UBC 0 : return mask & ACL_USAGE;
3689 : : }
3690 : :
3691 : : /*
3692 : : * Get the schema's ACL from pg_namespace
3693 : : */
5173 rhaas@postgresql.org 3694 :CBC 8300 : tuple = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(nsp_oid));
8029 tgl@sss.pgh.pa.us 3695 [ - + ]: 8300 : if (!HeapTupleIsValid(tuple))
3696 : : {
183 tgl@sss.pgh.pa.us 3697 [ # # ]:UNC 0 : if (is_missing != NULL)
3698 : : {
3699 : : /* return "no privileges" instead of throwing an error */
3700 : 0 : *is_missing = true;
3701 : 0 : return 0;
3702 : : }
3703 : : else
3704 [ # # ]: 0 : ereport(ERROR,
3705 : : (errcode(ERRCODE_UNDEFINED_SCHEMA),
3706 : : errmsg("schema with OID %u does not exist", nsp_oid)));
3707 : : }
3708 : :
7257 tgl@sss.pgh.pa.us 3709 :CBC 8300 : ownerId = ((Form_pg_namespace) GETSTRUCT(tuple))->nspowner;
3710 : :
8029 3711 : 8300 : aclDatum = SysCacheGetAttr(NAMESPACEOID, tuple, Anum_pg_namespace_nspacl,
3712 : : &isNull);
3713 [ + + ]: 8300 : if (isNull)
3714 : : {
3715 : : /* No ACL, so build default ACL */
2377 peter_e@gmx.net 3716 : 142 : acl = acldefault(OBJECT_SCHEMA, ownerId);
8029 tgl@sss.pgh.pa.us 3717 : 142 : aclDatum = (Datum) 0;
3718 : : }
3719 : : else
3720 : : {
3721 : : /* detoast ACL if necessary */
3722 : 8158 : acl = DatumGetAclP(aclDatum);
3723 : : }
3724 : :
6865 3725 : 8300 : result = aclmask(acl, roleid, ownerId, mask, how);
3726 : :
3727 : : /* if we have a detoasted copy, free it */
8091 peter_e@gmx.net 3728 [ + - + - ]: 8300 : if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
3729 : 8300 : pfree(acl);
3730 : :
3731 : 8300 : ReleaseSysCache(tuple);
3732 : :
3733 : : /*
3734 : : * Check if ACL_USAGE is being checked and, if so, and not set already as
3735 : : * part of the result, then check if the user is a member of the
3736 : : * pg_read_all_data or pg_write_all_data roles, which allow usage access
3737 : : * to all schemas.
3738 : : */
1105 sfrost@snowman.net 3739 [ + + + + : 8320 : if (mask & ACL_USAGE && !(result & ACL_USAGE) &&
+ + ]
1100 noah@leadboat.com 3740 [ + + ]: 37 : (has_privs_of_role(roleid, ROLE_PG_READ_ALL_DATA) ||
3741 : 17 : has_privs_of_role(roleid, ROLE_PG_WRITE_ALL_DATA)))
1105 sfrost@snowman.net 3742 : 7 : result |= ACL_USAGE;
8091 peter_e@gmx.net 3743 : 8300 : return result;
3744 : : }
3745 : :
3746 : : /*
3747 : : * Routine for examining a user's privileges for a type, with is_missing
3748 : : */
3749 : : static AclMode
183 tgl@sss.pgh.pa.us 3750 :GNC 143562 : pg_type_aclmask_ext(Oid type_oid, Oid roleid, AclMode mask, AclMaskHow how,
3751 : : bool *is_missing)
3752 : : {
3753 : : AclMode result;
3754 : : HeapTuple tuple;
3755 : : Form_pg_type typeForm;
3756 : : Datum aclDatum;
3757 : : bool isNull;
3758 : : Acl *acl;
3759 : : Oid ownerId;
3760 : :
3761 : : /* Bypass permission checks for superusers */
4499 peter_e@gmx.net 3762 [ + + ]:CBC 143562 : if (superuser_arg(roleid))
3763 : 141441 : return mask;
3764 : :
3765 : : /*
3766 : : * Must get the type's tuple from pg_type
3767 : : */
3768 : 2121 : tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_oid));
3769 [ - + ]: 2121 : if (!HeapTupleIsValid(tuple))
3770 : : {
183 tgl@sss.pgh.pa.us 3771 [ # # ]:UNC 0 : if (is_missing != NULL)
3772 : : {
3773 : : /* return "no privileges" instead of throwing an error */
3774 : 0 : *is_missing = true;
3775 : 0 : return 0;
3776 : : }
3777 : : else
3778 [ # # ]: 0 : ereport(ERROR,
3779 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
3780 : : errmsg("type with OID %u does not exist",
3781 : : type_oid)));
3782 : : }
4499 peter_e@gmx.net 3783 :CBC 2121 : typeForm = (Form_pg_type) GETSTRUCT(tuple);
3784 : :
3785 : : /*
3786 : : * "True" array types don't manage permissions of their own; consult the
3787 : : * element type instead.
3788 : : */
1222 tgl@sss.pgh.pa.us 3789 [ + + + + ]: 2121 : if (IsTrueArrayType(typeForm))
3790 : : {
4326 bruce@momjian.us 3791 : 24 : Oid elttype_oid = typeForm->typelem;
3792 : :
4499 peter_e@gmx.net 3793 : 24 : ReleaseSysCache(tuple);
3794 : :
3795 : 24 : tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(elttype_oid));
3796 [ - + ]: 24 : if (!HeapTupleIsValid(tuple))
3797 : : {
183 tgl@sss.pgh.pa.us 3798 [ # # ]:UNC 0 : if (is_missing != NULL)
3799 : : {
3800 : : /* return "no privileges" instead of throwing an error */
3801 : 0 : *is_missing = true;
3802 : 0 : return 0;
3803 : : }
3804 : : else
3805 [ # # ]: 0 : ereport(ERROR,
3806 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
3807 : : errmsg("type with OID %u does not exist",
3808 : : elttype_oid)));
3809 : : }
4499 peter_e@gmx.net 3810 :GNC 24 : typeForm = (Form_pg_type) GETSTRUCT(tuple);
3811 : : }
3812 : :
3813 : : /*
3814 : : * Likewise, multirange types don't manage their own permissions; consult
3815 : : * the associated range type. (Note we must do this after the array step
3816 : : * to get the right answer for arrays of multiranges.)
3817 : : */
60 tgl@sss.pgh.pa.us 3818 [ + + ]: 2121 : if (typeForm->typtype == TYPTYPE_MULTIRANGE)
3819 : : {
3820 : 6 : Oid rangetype = get_multirange_range(typeForm->oid);
3821 : :
3822 : 6 : ReleaseSysCache(tuple);
3823 : :
3824 : 6 : tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(rangetype));
3825 [ - + ]: 6 : if (!HeapTupleIsValid(tuple))
3826 : : {
60 tgl@sss.pgh.pa.us 3827 [ # # ]:UNC 0 : if (is_missing != NULL)
3828 : : {
3829 : : /* return "no privileges" instead of throwing an error */
3830 : 0 : *is_missing = true;
3831 : 0 : return 0;
3832 : : }
3833 : : else
3834 [ # # ]: 0 : ereport(ERROR,
3835 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
3836 : : errmsg("type with OID %u does not exist",
3837 : : rangetype)));
3838 : : }
60 tgl@sss.pgh.pa.us 3839 :CBC 6 : typeForm = (Form_pg_type) GETSTRUCT(tuple);
3840 : : }
3841 : :
3842 : : /*
3843 : : * Now get the type's owner and ACL from the tuple
3844 : : */
4499 peter_e@gmx.net 3845 : 2121 : ownerId = typeForm->typowner;
3846 : :
3847 : 2121 : aclDatum = SysCacheGetAttr(TYPEOID, tuple,
3848 : : Anum_pg_type_typacl, &isNull);
3849 [ + + ]: 2121 : if (isNull)
3850 : : {
3851 : : /* No ACL, so build default ACL */
2377 3852 : 2004 : acl = acldefault(OBJECT_TYPE, ownerId);
4499 3853 : 2004 : aclDatum = (Datum) 0;
3854 : : }
3855 : : else
3856 : : {
3857 : : /* detoast rel's ACL if necessary */
3858 : 117 : acl = DatumGetAclP(aclDatum);
3859 : : }
3860 : :
3861 : 2121 : result = aclmask(acl, roleid, ownerId, mask, how);
3862 : :
3863 : : /* if we have a detoasted copy, free it */
3864 [ + - + - ]: 2121 : if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
3865 : 2121 : pfree(acl);
3866 : :
3867 : 2121 : ReleaseSysCache(tuple);
3868 : :
3869 : 2121 : return result;
3870 : : }
3871 : :
3872 : : /*
3873 : : * Exported generic routine for checking a user's access privileges to an object
3874 : : */
3875 : : AclResult
518 peter@eisentraut.org 3876 : 1437809 : object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
3877 : : {
183 tgl@sss.pgh.pa.us 3878 :GNC 1437809 : return object_aclcheck_ext(classid, objectid, roleid, mode, NULL);
3879 : : }
3880 : :
3881 : : /*
3882 : : * Exported generic routine for checking a user's access privileges to an
3883 : : * object, with is_missing
3884 : : */
3885 : : AclResult
3886 : 1438004 : object_aclcheck_ext(Oid classid, Oid objectid,
3887 : : Oid roleid, AclMode mode,
3888 : : bool *is_missing)
3889 : : {
3890 [ + + ]: 1438004 : if (object_aclmask_ext(classid, objectid, roleid, mode, ACLMASK_ANY,
3891 : : is_missing) != 0)
518 peter@eisentraut.org 3892 :CBC 1437732 : return ACLCHECK_OK;
3893 : : else
3894 : 272 : return ACLCHECK_NO_PRIV;
3895 : : }
3896 : :
3897 : : /*
3898 : : * Exported routine for checking a user's access privileges to a column
3899 : : *
3900 : : * Returns ACLCHECK_OK if the user has any of the privileges identified by
3901 : : * 'mode'; otherwise returns a suitable error code (in practice, always
3902 : : * ACLCHECK_NO_PRIV).
3903 : : *
3904 : : * As with pg_attribute_aclmask, only privileges granted directly on the
3905 : : * column are considered here.
3906 : : */
3907 : : AclResult
5561 tgl@sss.pgh.pa.us 3908 : 1769 : pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum,
3909 : : Oid roleid, AclMode mode)
3910 : : {
1110 mail@joeconway.com 3911 : 1769 : return pg_attribute_aclcheck_ext(table_oid, attnum, roleid, mode, NULL);
3912 : : }
3913 : :
3914 : :
3915 : : /*
3916 : : * Exported routine for checking a user's access privileges to a column,
3917 : : * with is_missing
3918 : : */
3919 : : AclResult
3920 : 2994 : pg_attribute_aclcheck_ext(Oid table_oid, AttrNumber attnum,
3921 : : Oid roleid, AclMode mode, bool *is_missing)
3922 : : {
3923 [ + + ]: 2994 : if (pg_attribute_aclmask_ext(table_oid, attnum, roleid, mode,
3924 : : ACLMASK_ANY, is_missing) != 0)
5561 tgl@sss.pgh.pa.us 3925 : 980 : return ACLCHECK_OK;
3926 : : else
3927 : 2014 : return ACLCHECK_NO_PRIV;
3928 : : }
3929 : :
3930 : : /*
3931 : : * Exported routine for checking a user's access privileges to any/all columns
3932 : : *
3933 : : * If 'how' is ACLMASK_ANY, then returns ACLCHECK_OK if user has any of the
3934 : : * privileges identified by 'mode' on any non-dropped column in the relation;
3935 : : * otherwise returns a suitable error code (in practice, always
3936 : : * ACLCHECK_NO_PRIV).
3937 : : *
3938 : : * If 'how' is ACLMASK_ALL, then returns ACLCHECK_OK if user has any of the
3939 : : * privileges identified by 'mode' on each non-dropped column in the relation
3940 : : * (and there must be at least one such column); otherwise returns a suitable
3941 : : * error code (in practice, always ACLCHECK_NO_PRIV).
3942 : : *
3943 : : * As with pg_attribute_aclmask, only privileges granted directly on the
3944 : : * column(s) are considered here.
3945 : : *
3946 : : * Note: system columns are not considered here; there are cases where that
3947 : : * might be appropriate but there are also cases where it wouldn't.
3948 : : */
3949 : : AclResult
3950 : 81 : pg_attribute_aclcheck_all(Oid table_oid, Oid roleid, AclMode mode,
3951 : : AclMaskHow how)
3952 : : {
183 tgl@sss.pgh.pa.us 3953 :GNC 81 : return pg_attribute_aclcheck_all_ext(table_oid, roleid, mode, how, NULL);
3954 : : }
3955 : :
3956 : : /*
3957 : : * Exported routine for checking a user's access privileges to any/all columns,
3958 : : * with is_missing
3959 : : */
3960 : : AclResult
3961 : 81 : pg_attribute_aclcheck_all_ext(Oid table_oid, Oid roleid,
3962 : : AclMode mode, AclMaskHow how,
3963 : : bool *is_missing)
3964 : : {
3965 : : AclResult result;
3966 : : HeapTuple classTuple;
3967 : : Form_pg_class classForm;
3968 : : Oid ownerId;
3969 : : AttrNumber nattrs;
3970 : : AttrNumber curr_att;
3971 : :
3972 : : /*
3973 : : * Must fetch pg_class row to get owner ID and number of attributes.
3974 : : */
5173 rhaas@postgresql.org 3975 :CBC 81 : classTuple = SearchSysCache1(RELOID, ObjectIdGetDatum(table_oid));
5561 tgl@sss.pgh.pa.us 3976 [ - + ]: 81 : if (!HeapTupleIsValid(classTuple))
3977 : : {
183 tgl@sss.pgh.pa.us 3978 [ # # ]:UNC 0 : if (is_missing != NULL)
3979 : : {
3980 : : /* return "no privileges" instead of throwing an error */
3981 : 0 : *is_missing = true;
3982 : 0 : return ACLCHECK_NO_PRIV;
3983 : : }
3984 : : else
3985 [ # # ]: 0 : ereport(ERROR,
3986 : : (errcode(ERRCODE_UNDEFINED_TABLE),
3987 : : errmsg("relation with OID %u does not exist",
3988 : : table_oid)));
3989 : : }
5561 tgl@sss.pgh.pa.us 3990 :CBC 81 : classForm = (Form_pg_class) GETSTRUCT(classTuple);
3991 : :
183 tgl@sss.pgh.pa.us 3992 :GNC 81 : ownerId = classForm->relowner;
5561 tgl@sss.pgh.pa.us 3993 :CBC 81 : nattrs = classForm->relnatts;
3994 : :
3995 : 81 : ReleaseSysCache(classTuple);
3996 : :
3997 : : /*
3998 : : * Initialize result in case there are no non-dropped columns. We want to
3999 : : * report failure in such cases for either value of 'how'.
4000 : : */
4001 : 81 : result = ACLCHECK_NO_PRIV;
4002 : :
4003 [ + + ]: 207 : for (curr_att = 1; curr_att <= nattrs; curr_att++)
4004 : : {
4005 : : HeapTuple attTuple;
4006 : : Datum aclDatum;
4007 : : bool isNull;
4008 : : Acl *acl;
4009 : : AclMode attmask;
4010 : :
5173 rhaas@postgresql.org 4011 : 165 : attTuple = SearchSysCache2(ATTNUM,
4012 : : ObjectIdGetDatum(table_oid),
4013 : : Int16GetDatum(curr_att));
4014 : :
4015 : : /*
4016 : : * Lookup failure probably indicates that the table was just dropped,
4017 : : * but we'll treat it the same as a dropped column rather than
4018 : : * throwing error.
4019 : : */
5561 tgl@sss.pgh.pa.us 4020 [ - + ]: 165 : if (!HeapTupleIsValid(attTuple))
5546 tgl@sss.pgh.pa.us 4021 :UBC 0 : continue;
4022 : :
4023 : : /* ignore dropped columns */
5546 tgl@sss.pgh.pa.us 4024 [ + + ]:CBC 165 : if (((Form_pg_attribute) GETSTRUCT(attTuple))->attisdropped)
4025 : : {
4026 : 9 : ReleaseSysCache(attTuple);
5561 4027 : 9 : continue;
4028 : : }
4029 : :
183 tgl@sss.pgh.pa.us 4030 :GNC 156 : aclDatum = SysCacheGetAttr(ATTNUM, attTuple, Anum_pg_attribute_attacl,
4031 : : &isNull);
4032 : :
4033 : : /*
4034 : : * Here we hard-wire knowledge that the default ACL for a column
4035 : : * grants no privileges, so that we can fall out quickly in the very
4036 : : * common case where attacl is null.
4037 : : */
4038 [ + + ]: 156 : if (isNull)
5546 tgl@sss.pgh.pa.us 4039 :CBC 78 : attmask = 0;
4040 : : else
4041 : : {
4042 : : /* detoast column's ACL if necessary */
183 tgl@sss.pgh.pa.us 4043 :GNC 78 : acl = DatumGetAclP(aclDatum);
4044 : :
4045 : 78 : attmask = aclmask(acl, roleid, ownerId, mode, ACLMASK_ANY);
4046 : :
4047 : : /* if we have a detoasted copy, free it */
4048 [ + - ]: 78 : if ((Pointer) acl != DatumGetPointer(aclDatum))
4049 : 78 : pfree(acl);
4050 : : }
4051 : :
5546 tgl@sss.pgh.pa.us 4052 :CBC 156 : ReleaseSysCache(attTuple);
4053 : :
4054 [ + + ]: 156 : if (attmask != 0)
4055 : : {
5561 4056 : 69 : result = ACLCHECK_OK;
4057 [ + + ]: 69 : if (how == ACLMASK_ANY)
4058 : 39 : break; /* succeed on any success */
4059 : : }
4060 : : else
4061 : : {
4062 : 87 : result = ACLCHECK_NO_PRIV;
4063 [ + + ]: 87 : if (how == ACLMASK_ALL)
4064 : 18 : break; /* fail on any failure */
4065 : : }
4066 : : }
4067 : :
4068 : 81 : return result;
4069 : : }
4070 : :
4071 : : /*
4072 : : * Exported routine for checking a user's access privileges to a table
4073 : : *
4074 : : * Returns ACLCHECK_OK if the user has any of the privileges identified by
4075 : : * 'mode'; otherwise returns a suitable error code (in practice, always
4076 : : * ACLCHECK_NO_PRIV).
4077 : : */
4078 : : AclResult
6865 4079 : 1069209 : pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
4080 : : {
1110 mail@joeconway.com 4081 : 1069209 : return pg_class_aclcheck_ext(table_oid, roleid, mode, NULL);
4082 : : }
4083 : :
4084 : : /*
4085 : : * Exported routine for checking a user's access privileges to a table,
4086 : : * with is_missing
4087 : : */
4088 : : AclResult
4089 : 1070552 : pg_class_aclcheck_ext(Oid table_oid, Oid roleid,
4090 : : AclMode mode, bool *is_missing)
4091 : : {
4092 [ + + ]: 1070552 : if (pg_class_aclmask_ext(table_oid, roleid, mode,
4093 : : ACLMASK_ANY, is_missing) != 0)
7278 tgl@sss.pgh.pa.us 4094 : 1069975 : return ACLCHECK_OK;
4095 : : else
4096 : 577 : return ACLCHECK_NO_PRIV;
4097 : : }
4098 : :
4099 : : /*
4100 : : * Exported routine for checking a user's access privileges to a configuration
4101 : : * parameter (GUC), identified by GUC name.
4102 : : */
4103 : : AclResult
739 4104 : 80 : pg_parameter_aclcheck(const char *name, Oid roleid, AclMode mode)
4105 : : {
4106 [ + + ]: 80 : if (pg_parameter_aclmask(name, roleid, mode, ACLMASK_ANY) != 0)
4107 : 34 : return ACLCHECK_OK;
4108 : : else
4109 : 46 : return ACLCHECK_NO_PRIV;
4110 : : }
4111 : :
4112 : : /*
4113 : : * Exported routine for checking a user's access privileges to a largeobject
4114 : : */
4115 : : AclResult
5238 itagaki.takahiro@gma 4116 : 292 : pg_largeobject_aclcheck_snapshot(Oid lobj_oid, Oid roleid, AclMode mode,
4117 : : Snapshot snapshot)
4118 : : {
4119 [ + + ]: 292 : if (pg_largeobject_aclmask_snapshot(lobj_oid, roleid, mode,
4120 : : ACLMASK_ANY, snapshot) != 0)
4121 : 265 : return ACLCHECK_OK;
4122 : : else
4123 : 27 : return ACLCHECK_NO_PRIV;
4124 : : }
4125 : :
4126 : : /*
4127 : : * Generic ownership check for an object
4128 : : */
4129 : : bool
518 peter@eisentraut.org 4130 : 189595 : object_ownercheck(Oid classid, Oid objectid, Oid roleid)
4131 : : {
4132 : : int cacheid;
4133 : : Oid ownerId;
4134 : :
4135 : : /* Superusers bypass all permission checking. */
4810 peter_e@gmx.net 4136 [ + + ]: 189595 : if (superuser_arg(roleid))
4137 : 185672 : return true;
4138 : :
4139 : : /* For large objects, the catalog to consult is pg_largeobject_metadata */
121 tgl@sss.pgh.pa.us 4140 [ + + ]: 3923 : if (classid == LargeObjectRelationId)
4141 : 12 : classid = LargeObjectMetadataRelationId;
4142 : :
518 peter@eisentraut.org 4143 : 3923 : cacheid = get_object_catcache_oid(classid);
4144 [ + + ]: 3923 : if (cacheid != -1)
4145 : : {
4146 : : /* we can get the object's tuple from the syscache */
4147 : : HeapTuple tuple;
4148 : :
4149 : 3909 : tuple = SearchSysCache1(cacheid, ObjectIdGetDatum(objectid));
4150 [ - + ]: 3909 : if (!HeapTupleIsValid(tuple))
518 peter@eisentraut.org 4151 [ # # ]:UBC 0 : ereport(ERROR,
4152 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
4153 : : errmsg("%s with OID %u does not exist", get_object_class_descr(classid), objectid)));
4154 : :
386 dgustafsson@postgres 4155 :CBC 3909 : ownerId = DatumGetObjectId(SysCacheGetAttrNotNull(cacheid,
4156 : : tuple,
4157 : 3909 : get_object_attnum_owner(classid)));
518 peter@eisentraut.org 4158 : 3909 : ReleaseSysCache(tuple);
4159 : : }
4160 : : else
4161 : : {
4162 : : /* for catalogs without an appropriate syscache */
4163 : : Relation rel;
4164 : : ScanKeyData entry[1];
4165 : : SysScanDesc scan;
4166 : : HeapTuple tuple;
4167 : : bool isnull;
4168 : :
4169 : 14 : rel = table_open(classid, AccessShareLock);
4170 : :
4171 : 28 : ScanKeyInit(&entry[0],
4172 : 14 : get_object_attnum_oid(classid),
4173 : : BTEqualStrategyNumber, F_OIDEQ,
4174 : : ObjectIdGetDatum(objectid));
4175 : :
4176 : 14 : scan = systable_beginscan(rel,
4177 : : get_object_oid_index(classid), true,
4178 : : NULL, 1, entry);
4179 : :
4180 : 14 : tuple = systable_getnext(scan);
4181 [ - + ]: 14 : if (!HeapTupleIsValid(tuple))
518 peter@eisentraut.org 4182 [ # # ]:UBC 0 : ereport(ERROR,
4183 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
4184 : : errmsg("%s with OID %u does not exist", get_object_class_descr(classid), objectid)));
4185 : :
518 peter@eisentraut.org 4186 :CBC 14 : ownerId = DatumGetObjectId(heap_getattr(tuple,
4187 : 14 : get_object_attnum_owner(classid),
4188 : : RelationGetDescr(rel),
4189 : : &isnull));
4190 [ - + ]: 14 : Assert(!isnull);
4191 : :
4192 : 14 : systable_endscan(scan);
4193 : 14 : table_close(rel, AccessShareLock);
4194 : : }
4195 : :
2578 alvherre@alvh.no-ip. 4196 : 3923 : return has_privs_of_role(roleid, ownerId);
4197 : : }
4198 : :
4199 : : /*
4200 : : * Check whether specified role has CREATEROLE privilege (or is a superuser)
4201 : : *
4202 : : * Note: roles do not have owners per se; instead we use this test in
4203 : : * places where an ownership-like permissions test is needed for a role.
4204 : : * Be sure to apply it to the role trying to do the operation, not the
4205 : : * role being operated on! Also note that this generally should not be
4206 : : * considered enough privilege if the target role is a superuser.
4207 : : * (We don't handle that consideration here because we want to give a
4208 : : * separate error message for such cases, so the caller has to deal with it.)
4209 : : */
4210 : : bool
3400 4211 : 1165 : has_createrole_privilege(Oid roleid)
4212 : : {
4213 : 1165 : bool result = false;
4214 : : HeapTuple utup;
4215 : :
4216 : : /* Superusers bypass all permission checking. */
4217 [ + + ]: 1165 : if (superuser_arg(roleid))
4218 : 906 : return true;
4219 : :
4220 : 259 : utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
4221 [ + - ]: 259 : if (HeapTupleIsValid(utup))
4222 : : {
4223 : 259 : result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreaterole;
4224 : 259 : ReleaseSysCache(utup);
4225 : : }
4226 : 259 : return result;
4227 : : }
4228 : :
4229 : : bool
4230 : 2242 : has_bypassrls_privilege(Oid roleid)
4231 : : {
4232 : 2242 : bool result = false;
4233 : : HeapTuple utup;
4234 : :
4235 : : /* Superusers bypass all permission checking. */
4236 [ + + ]: 2242 : if (superuser_arg(roleid))
4237 : 692 : return true;
4238 : :
4239 : 1550 : utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
4240 [ + - ]: 1550 : if (HeapTupleIsValid(utup))
4241 : : {
4242 : 1550 : result = ((Form_pg_authid) GETSTRUCT(utup))->rolbypassrls;
4243 : 1550 : ReleaseSysCache(utup);
4244 : : }
4245 : 1550 : return result;
4246 : : }
4247 : :
4248 : : /*
4249 : : * Fetch pg_default_acl entry for given role, namespace and object type
4250 : : * (object type must be given in pg_default_acl's encoding).
4251 : : * Returns NULL if no such entry.
4252 : : */
4253 : : static Acl *
5305 tgl@sss.pgh.pa.us 4254 : 69850 : get_default_acl_internal(Oid roleId, Oid nsp_oid, char objtype)
4255 : : {
4256 : 69850 : Acl *result = NULL;
4257 : : HeapTuple tuple;
4258 : :
5173 rhaas@postgresql.org 4259 : 69850 : tuple = SearchSysCache3(DEFACLROLENSPOBJ,
4260 : : ObjectIdGetDatum(roleId),
4261 : : ObjectIdGetDatum(nsp_oid),
4262 : : CharGetDatum(objtype));
4263 : :
5305 tgl@sss.pgh.pa.us 4264 [ + + ]: 69850 : if (HeapTupleIsValid(tuple))
4265 : : {
4266 : : Datum aclDatum;
4267 : : bool isNull;
4268 : :
4269 : 114 : aclDatum = SysCacheGetAttr(DEFACLROLENSPOBJ, tuple,
4270 : : Anum_pg_default_acl_defaclacl,
4271 : : &isNull);
4272 [ + - ]: 114 : if (!isNull)
4273 : 114 : result = DatumGetAclPCopy(aclDatum);
4274 : 114 : ReleaseSysCache(tuple);
4275 : : }
4276 : :
4277 : 69850 : return result;
4278 : : }
4279 : :
4280 : : /*
4281 : : * Get default permissions for newly created object within given schema
4282 : : *
4283 : : * Returns NULL if built-in system defaults should be used.
4284 : : *
4285 : : * If the result is not NULL, caller must call recordDependencyOnNewAcl
4286 : : * once the OID of the new object is known.
4287 : : */
4288 : : Acl *
2377 peter_e@gmx.net 4289 : 34925 : get_user_default_acl(ObjectType objtype, Oid ownerId, Oid nsp_oid)
4290 : : {
4291 : : Acl *result;
4292 : : Acl *glob_acl;
4293 : : Acl *schema_acl;
4294 : : Acl *def_acl;
4295 : : char defaclobjtype;
4296 : :
4297 : : /*
4298 : : * Use NULL during bootstrap, since pg_default_acl probably isn't there
4299 : : * yet.
4300 : : */
5305 tgl@sss.pgh.pa.us 4301 [ - + ]: 34925 : if (IsBootstrapProcessingMode())
5305 tgl@sss.pgh.pa.us 4302 :UBC 0 : return NULL;
4303 : :
4304 : : /* Check if object type is supported in pg_default_acl */
5305 tgl@sss.pgh.pa.us 4305 [ + + + + :CBC 34925 : switch (objtype)
+ - ]
4306 : : {
2377 peter_e@gmx.net 4307 : 24965 : case OBJECT_TABLE:
5305 tgl@sss.pgh.pa.us 4308 : 24965 : defaclobjtype = DEFACLOBJ_RELATION;
4309 : 24965 : break;
4310 : :
2377 peter_e@gmx.net 4311 : 835 : case OBJECT_SEQUENCE:
5305 tgl@sss.pgh.pa.us 4312 : 835 : defaclobjtype = DEFACLOBJ_SEQUENCE;
4313 : 835 : break;
4314 : :
2377 peter_e@gmx.net 4315 : 7375 : case OBJECT_FUNCTION:
5305 tgl@sss.pgh.pa.us 4316 : 7375 : defaclobjtype = DEFACLOBJ_FUNCTION;
4317 : 7375 : break;
4318 : :
2377 peter_e@gmx.net 4319 : 1282 : case OBJECT_TYPE:
4499 4320 : 1282 : defaclobjtype = DEFACLOBJ_TYPE;
4321 : 1282 : break;
4322 : :
2377 4323 : 468 : case OBJECT_SCHEMA:
2574 teodor@sigaev.ru 4324 : 468 : defaclobjtype = DEFACLOBJ_NAMESPACE;
4325 : 468 : break;
4326 : :
5305 tgl@sss.pgh.pa.us 4327 :UBC 0 : default:
4328 : 0 : return NULL;
4329 : : }
4330 : :
4331 : : /* Look up the relevant pg_default_acl entries */
5305 tgl@sss.pgh.pa.us 4332 :CBC 34925 : glob_acl = get_default_acl_internal(ownerId, InvalidOid, defaclobjtype);
4333 : 34925 : schema_acl = get_default_acl_internal(ownerId, nsp_oid, defaclobjtype);
4334 : :
4335 : : /* Quick out if neither entry exists */
4336 [ + + + + ]: 34925 : if (glob_acl == NULL && schema_acl == NULL)
4337 : 34829 : return NULL;
4338 : :
4339 : : /* We need to know the hard-wired default value, too */
4340 : 96 : def_acl = acldefault(objtype, ownerId);
4341 : :
4342 : : /* If there's no global entry, substitute the hard-wired default */
4343 [ + + ]: 96 : if (glob_acl == NULL)
4344 : 9 : glob_acl = def_acl;
4345 : :
4346 : : /* Merge in any per-schema privileges */
4347 : 96 : result = aclmerge(glob_acl, schema_acl, ownerId);
4348 : :
4349 : : /*
4350 : : * For efficiency, we want to return NULL if the result equals default.
4351 : : * This requires sorting both arrays to get an accurate comparison.
4352 : : */
4353 : 96 : aclitemsort(result);
4354 : 96 : aclitemsort(def_acl);
4355 [ + + ]: 96 : if (aclequal(result, def_acl))
4356 : 12 : result = NULL;
4357 : :
4358 : 96 : return result;
4359 : : }
4360 : :
4361 : : /*
4362 : : * Record dependencies on roles mentioned in a new object's ACL.
4363 : : */
4364 : : void
1983 4365 : 36274 : recordDependencyOnNewAcl(Oid classId, Oid objectId, int32 objsubId,
4366 : : Oid ownerId, Acl *acl)
4367 : : {
4368 : : int nmembers;
4369 : : Oid *members;
4370 : :
4371 : : /* Nothing to do if ACL is defaulted */
4372 [ + + ]: 36274 : if (acl == NULL)
4373 : 36190 : return;
4374 : :
4375 : : /* Extract roles mentioned in ACL */
4376 : 84 : nmembers = aclmembers(acl, &members);
4377 : :
4378 : : /* Update the shared dependency ACL info */
4379 : 84 : updateAclDependencies(classId, objectId, objsubId,
4380 : : ownerId,
4381 : : 0, NULL,
4382 : : nmembers, members);
4383 : : }
4384 : :
4385 : : /*
4386 : : * Record initial privileges for the top-level object passed in.
4387 : : *
4388 : : * For the object passed in, this will record its ACL (if any) and the ACLs of
4389 : : * any sub-objects (eg: columns) into pg_init_privs.
4390 : : */
4391 : : void
2632 sfrost@snowman.net 4392 : 48 : recordExtObjInitPriv(Oid objoid, Oid classoid)
4393 : : {
4394 : : /*
4395 : : * pg_class / pg_attribute
4396 : : *
4397 : : * If this is a relation then we need to see if there are any sub-objects
4398 : : * (eg: columns) for it and, if so, be sure to call
4399 : : * recordExtensionInitPrivWorker() for each one.
4400 : : */
4401 [ + + ]: 48 : if (classoid == RelationRelationId)
4402 : : {
4403 : : Form_pg_class pg_class_tuple;
4404 : : Datum aclDatum;
4405 : : bool isNull;
4406 : : HeapTuple tuple;
4407 : :
4408 : 8 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(objoid));
4409 [ - + ]: 8 : if (!HeapTupleIsValid(tuple))
2632 sfrost@snowman.net 4410 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for relation %u", objoid);
2632 sfrost@snowman.net 4411 :CBC 8 : pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
4412 : :
4413 : : /*
4414 : : * Indexes don't have permissions, neither do the pg_class rows for
4415 : : * composite types. (These cases are unreachable given the
4416 : : * restrictions in ALTER EXTENSION ADD, but let's check anyway.)
4417 : : */
2277 alvherre@alvh.no-ip. 4418 [ + - ]: 8 : if (pg_class_tuple->relkind == RELKIND_INDEX ||
1458 tgl@sss.pgh.pa.us 4419 [ + - ]: 8 : pg_class_tuple->relkind == RELKIND_PARTITIONED_INDEX ||
4420 [ - + ]: 8 : pg_class_tuple->relkind == RELKIND_COMPOSITE_TYPE)
4421 : : {
1458 tgl@sss.pgh.pa.us 4422 :UBC 0 : ReleaseSysCache(tuple);
2632 sfrost@snowman.net 4423 : 0 : return;
4424 : : }
4425 : :
4426 : : /*
4427 : : * If this isn't a sequence then it's possibly going to have
4428 : : * column-level ACLs associated with it.
4429 : : */
2632 sfrost@snowman.net 4430 [ + + ]:CBC 8 : if (pg_class_tuple->relkind != RELKIND_SEQUENCE)
4431 : : {
4432 : : AttrNumber curr_att;
4433 : 7 : AttrNumber nattrs = pg_class_tuple->relnatts;
4434 : :
4435 [ + + ]: 19 : for (curr_att = 1; curr_att <= nattrs; curr_att++)
4436 : : {
4437 : : HeapTuple attTuple;
4438 : : Datum attaclDatum;
4439 : :
4440 : 12 : attTuple = SearchSysCache2(ATTNUM,
4441 : : ObjectIdGetDatum(objoid),
4442 : : Int16GetDatum(curr_att));
4443 : :
4444 [ - + ]: 12 : if (!HeapTupleIsValid(attTuple))
2632 sfrost@snowman.net 4445 :UBC 0 : continue;
4446 : :
4447 : : /* ignore dropped columns */
2632 sfrost@snowman.net 4448 [ + + ]:CBC 12 : if (((Form_pg_attribute) GETSTRUCT(attTuple))->attisdropped)
4449 : : {
4450 : 1 : ReleaseSysCache(attTuple);
4451 : 1 : continue;
4452 : : }
4453 : :
4454 : 11 : attaclDatum = SysCacheGetAttr(ATTNUM, attTuple,
4455 : : Anum_pg_attribute_attacl,
4456 : : &isNull);
4457 : :
4458 : : /* no need to do anything for a NULL ACL */
4459 [ + + ]: 11 : if (isNull)
4460 : : {
4461 : 9 : ReleaseSysCache(attTuple);
4462 : 9 : continue;
4463 : : }
4464 : :
4465 : 2 : recordExtensionInitPrivWorker(objoid, classoid, curr_att,
4466 : 2 : DatumGetAclP(attaclDatum));
4467 : :
4468 : 2 : ReleaseSysCache(attTuple);
4469 : : }
4470 : : }
4471 : :
4472 : 8 : aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl,
4473 : : &isNull);
4474 : :
4475 : : /* Add the record, if any, for the top-level object */
4476 [ + + ]: 8 : if (!isNull)
4477 : 4 : recordExtensionInitPrivWorker(objoid, classoid, 0,
4478 : 4 : DatumGetAclP(aclDatum));
4479 : :
4480 : 8 : ReleaseSysCache(tuple);
4481 : : }
121 tgl@sss.pgh.pa.us 4482 [ - + ]: 40 : else if (classoid == LargeObjectRelationId)
4483 : : {
4484 : : /* For large objects, we must consult pg_largeobject_metadata */
4485 : : Datum aclDatum;
4486 : : bool isNull;
4487 : : HeapTuple tuple;
4488 : : ScanKeyData entry[1];
4489 : : SysScanDesc scan;
4490 : : Relation relation;
4491 : :
4492 : : /*
4493 : : * Note: this is dead code, given that we don't allow large objects to
4494 : : * be made extension members. But it seems worth carrying in case
4495 : : * some future caller of this function has need for it.
4496 : : */
1910 andres@anarazel.de 4497 :UBC 0 : relation = table_open(LargeObjectMetadataRelationId, RowExclusiveLock);
4498 : :
4499 : : /* There's no syscache for pg_largeobject_metadata */
2632 sfrost@snowman.net 4500 : 0 : ScanKeyInit(&entry[0],
4501 : : Anum_pg_largeobject_metadata_oid,
4502 : : BTEqualStrategyNumber, F_OIDEQ,
4503 : : ObjectIdGetDatum(objoid));
4504 : :
4505 : 0 : scan = systable_beginscan(relation,
4506 : : LargeObjectMetadataOidIndexId, true,
4507 : : NULL, 1, entry);
4508 : :
4509 : 0 : tuple = systable_getnext(scan);
4510 [ # # ]: 0 : if (!HeapTupleIsValid(tuple))
2506 tgl@sss.pgh.pa.us 4511 [ # # ]: 0 : elog(ERROR, "could not find tuple for large object %u", objoid);
4512 : :
2632 sfrost@snowman.net 4513 : 0 : aclDatum = heap_getattr(tuple,
4514 : : Anum_pg_largeobject_metadata_lomacl,
4515 : : RelationGetDescr(relation), &isNull);
4516 : :
4517 : : /* Add the record, if any, for the top-level object */
4518 [ # # ]: 0 : if (!isNull)
4519 : 0 : recordExtensionInitPrivWorker(objoid, classoid, 0,
4520 : 0 : DatumGetAclP(aclDatum));
4521 : :
4522 : 0 : systable_endscan(scan);
4523 : : }
4524 : : /* This will error on unsupported classoid. */
453 peter@eisentraut.org 4525 [ + + ]:CBC 40 : else if (get_object_attnum_acl(classoid) != InvalidAttrNumber)
4526 : : {
4527 : : Datum aclDatum;
4528 : : bool isNull;
4529 : : HeapTuple tuple;
4530 : :
4531 : 29 : tuple = SearchSysCache1(get_object_catcache_oid(classoid),
4532 : : ObjectIdGetDatum(objoid));
2632 sfrost@snowman.net 4533 [ - + ]: 29 : if (!HeapTupleIsValid(tuple))
453 peter@eisentraut.org 4534 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for %s %u",
4535 : : get_object_class_descr(classoid), objoid);
4536 : :
453 peter@eisentraut.org 4537 :CBC 29 : aclDatum = SysCacheGetAttr(get_object_catcache_oid(classoid), tuple,
4538 : 29 : get_object_attnum_acl(classoid),
4539 : : &isNull);
4540 : :
4541 : : /* Add the record, if any, for the top-level object */
2632 sfrost@snowman.net 4542 [ + + ]: 29 : if (!isNull)
4543 : 5 : recordExtensionInitPrivWorker(objoid, classoid, 0,
4544 : 5 : DatumGetAclP(aclDatum));
4545 : :
4546 : 29 : ReleaseSysCache(tuple);
4547 : : }
4548 : : }
4549 : :
4550 : : /*
4551 : : * For the object passed in, remove its ACL and the ACLs of any object subIds
4552 : : * from pg_init_privs (via recordExtensionInitPrivWorker()).
4553 : : */
4554 : : void
4555 : 113 : removeExtObjInitPriv(Oid objoid, Oid classoid)
4556 : : {
4557 : : /*
4558 : : * If this is a relation then we need to see if there are any sub-objects
4559 : : * (eg: columns) for it and, if so, be sure to call
4560 : : * recordExtensionInitPrivWorker() for each one.
4561 : : */
4562 [ + + ]: 113 : if (classoid == RelationRelationId)
4563 : : {
4564 : : Form_pg_class pg_class_tuple;
4565 : : HeapTuple tuple;
4566 : :
4567 : 20 : tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(objoid));
4568 [ - + ]: 20 : if (!HeapTupleIsValid(tuple))
2632 sfrost@snowman.net 4569 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for relation %u", objoid);
2632 sfrost@snowman.net 4570 :CBC 20 : pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
4571 : :
4572 : : /*
4573 : : * Indexes don't have permissions, neither do the pg_class rows for
4574 : : * composite types. (These cases are unreachable given the
4575 : : * restrictions in ALTER EXTENSION DROP, but let's check anyway.)
4576 : : */
2277 alvherre@alvh.no-ip. 4577 [ + - ]: 20 : if (pg_class_tuple->relkind == RELKIND_INDEX ||
1458 tgl@sss.pgh.pa.us 4578 [ + - ]: 20 : pg_class_tuple->relkind == RELKIND_PARTITIONED_INDEX ||
4579 [ - + ]: 20 : pg_class_tuple->relkind == RELKIND_COMPOSITE_TYPE)
4580 : : {
1458 tgl@sss.pgh.pa.us 4581 :UBC 0 : ReleaseSysCache(tuple);
2632 sfrost@snowman.net 4582 : 0 : return;
4583 : : }
4584 : :
4585 : : /*
4586 : : * If this isn't a sequence then it's possibly going to have
4587 : : * column-level ACLs associated with it.
4588 : : */
2632 sfrost@snowman.net 4589 [ + - ]:CBC 20 : if (pg_class_tuple->relkind != RELKIND_SEQUENCE)
4590 : : {
4591 : : AttrNumber curr_att;
4592 : 20 : AttrNumber nattrs = pg_class_tuple->relnatts;
4593 : :
4594 [ + + ]: 469 : for (curr_att = 1; curr_att <= nattrs; curr_att++)
4595 : : {
4596 : : HeapTuple attTuple;
4597 : :
4598 : 449 : attTuple = SearchSysCache2(ATTNUM,
4599 : : ObjectIdGetDatum(objoid),
4600 : : Int16GetDatum(curr_att));
4601 : :
4602 [ - + ]: 449 : if (!HeapTupleIsValid(attTuple))
2632 sfrost@snowman.net 4603 :UBC 0 : continue;
4604 : :
4605 : : /* when removing, remove all entries, even dropped columns */
4606 : :
2632 sfrost@snowman.net 4607 :CBC 449 : recordExtensionInitPrivWorker(objoid, classoid, curr_att, NULL);
4608 : :
4609 : 449 : ReleaseSysCache(attTuple);
4610 : : }
4611 : : }
4612 : :
4613 : 20 : ReleaseSysCache(tuple);
4614 : : }
4615 : :
4616 : : /* Remove the record, if any, for the top-level object */
4617 : 113 : recordExtensionInitPrivWorker(objoid, classoid, 0, NULL);
4618 : : }
4619 : :
4620 : : /*
4621 : : * Record initial ACL for an extension object
4622 : : *
4623 : : * Can be called at any time, we check if 'creating_extension' is set and, if
4624 : : * not, exit immediately.
4625 : : *
4626 : : * Pass in the object OID, the OID of the class (the OID of the table which
4627 : : * the object is defined in) and the 'sub' id of the object (objsubid), if
4628 : : * any. If there is no 'sub' id (they are currently only used for columns of
4629 : : * tables) then pass in '0'. Finally, pass in the complete ACL to store.
4630 : : *
4631 : : * If an ACL already exists for this object/sub-object then we will replace
4632 : : * it with what is passed in.
4633 : : *
4634 : : * Passing in NULL for 'new_acl' will result in the entry for the object being
4635 : : * removed, if one is found.
4636 : : */
4637 : : static void
2930 4638 : 8488 : recordExtensionInitPriv(Oid objoid, Oid classoid, int objsubid, Acl *new_acl)
4639 : : {
4640 : : /*
4641 : : * Generally, we only record the initial privileges when an extension is
4642 : : * being created, but because we don't actually use CREATE EXTENSION
4643 : : * during binary upgrades with pg_upgrade, there is a variable to let us
4644 : : * know that the GRANT and REVOKE statements being issued, while this
4645 : : * variable is true, are for the initial privileges of the extension
4646 : : * object and therefore we need to record them.
4647 : : */
4648 [ + + + - ]: 8488 : if (!creating_extension && !binary_upgrade_record_init_privs)
4649 : 8173 : return;
4650 : :
2632 4651 : 315 : recordExtensionInitPrivWorker(objoid, classoid, objsubid, new_acl);
4652 : : }
4653 : :
4654 : : /*
4655 : : * Record initial ACL for an extension object, worker.
4656 : : *
4657 : : * This will perform a wholesale replacement of the entire ACL for the object
4658 : : * passed in, therefore be sure to pass in the complete new ACL to use.
4659 : : *
4660 : : * Generally speaking, do *not* use this function directly but instead use
4661 : : * recordExtensionInitPriv(), which checks if 'creating_extension' is set.
4662 : : * This function does *not* check if 'creating_extension' is set as it is also
4663 : : * used when an object is added to or removed from an extension via ALTER
4664 : : * EXTENSION ... ADD/DROP.
4665 : : */
4666 : : static void
4667 : 888 : recordExtensionInitPrivWorker(Oid objoid, Oid classoid, int objsubid, Acl *new_acl)
4668 : : {
4669 : : Relation relation;
4670 : : ScanKeyData key[3];
4671 : : SysScanDesc scan;
4672 : : HeapTuple tuple;
4673 : : HeapTuple oldtuple;
4674 : :
1910 andres@anarazel.de 4675 : 888 : relation = table_open(InitPrivsRelationId, RowExclusiveLock);
4676 : :
2930 sfrost@snowman.net 4677 : 888 : ScanKeyInit(&key[0],
4678 : : Anum_pg_init_privs_objoid,
4679 : : BTEqualStrategyNumber, F_OIDEQ,
4680 : : ObjectIdGetDatum(objoid));
4681 : 888 : ScanKeyInit(&key[1],
4682 : : Anum_pg_init_privs_classoid,
4683 : : BTEqualStrategyNumber, F_OIDEQ,
4684 : : ObjectIdGetDatum(classoid));
4685 : 888 : ScanKeyInit(&key[2],
4686 : : Anum_pg_init_privs_objsubid,
4687 : : BTEqualStrategyNumber, F_INT4EQ,
4688 : : Int32GetDatum(objsubid));
4689 : :
4690 : 888 : scan = systable_beginscan(relation, InitPrivsObjIndexId, true,
4691 : : NULL, 3, key);
4692 : :
4693 : : /* There should exist only one entry or none. */
4694 : 888 : oldtuple = systable_getnext(scan);
4695 : :
4696 : : /* If we find an entry, update it with the latest ACL. */
4697 [ + + ]: 888 : if (HeapTupleIsValid(oldtuple))
4698 : : {
638 peter@eisentraut.org 4699 : 104 : Datum values[Natts_pg_init_privs] = {0};
4700 : 104 : bool nulls[Natts_pg_init_privs] = {0};
4701 : 104 : bool replace[Natts_pg_init_privs] = {0};
4702 : :
4703 : : /* If we have a new ACL to set, then update the row with it. */
2930 sfrost@snowman.net 4704 [ + + ]: 104 : if (new_acl)
4705 : : {
2234 tgl@sss.pgh.pa.us 4706 : 72 : values[Anum_pg_init_privs_initprivs - 1] = PointerGetDatum(new_acl);
4707 : 72 : replace[Anum_pg_init_privs_initprivs - 1] = true;
4708 : :
2930 sfrost@snowman.net 4709 : 72 : oldtuple = heap_modify_tuple(oldtuple, RelationGetDescr(relation),
4710 : : values, nulls, replace);
4711 : :
2630 alvherre@alvh.no-ip. 4712 : 72 : CatalogTupleUpdate(relation, &oldtuple->t_self, oldtuple);
4713 : : }
4714 : : else
4715 : : {
4716 : : /* new_acl is NULL, so delete the entry we found. */
2629 tgl@sss.pgh.pa.us 4717 : 32 : CatalogTupleDelete(relation, &oldtuple->t_self);
4718 : : }
4719 : : }
4720 : : else
4721 : : {
638 peter@eisentraut.org 4722 : 784 : Datum values[Natts_pg_init_privs] = {0};
4723 : 784 : bool nulls[Natts_pg_init_privs] = {0};
4724 : :
4725 : : /*
4726 : : * Only add a new entry if the new ACL is non-NULL.
4727 : : *
4728 : : * If we are passed in a NULL ACL and no entry exists, we can just
4729 : : * fall through and do nothing.
4730 : : */
2632 sfrost@snowman.net 4731 [ + + ]: 784 : if (new_acl)
4732 : : {
4733 : : /* No entry found, so add it. */
4734 : 252 : values[Anum_pg_init_privs_objoid - 1] = ObjectIdGetDatum(objoid);
4735 : 252 : values[Anum_pg_init_privs_classoid - 1] = ObjectIdGetDatum(classoid);
4736 : 252 : values[Anum_pg_init_privs_objsubid - 1] = Int32GetDatum(objsubid);
4737 : :
4738 : : /* This function only handles initial privileges of extensions */
4739 : 252 : values[Anum_pg_init_privs_privtype - 1] =
4740 : 252 : CharGetDatum(INITPRIVS_EXTENSION);
4741 : :
2234 tgl@sss.pgh.pa.us 4742 : 252 : values[Anum_pg_init_privs_initprivs - 1] = PointerGetDatum(new_acl);
4743 : :
2632 sfrost@snowman.net 4744 : 252 : tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls);
4745 : :
2630 alvherre@alvh.no-ip. 4746 : 252 : CatalogTupleInsert(relation, tuple);
4747 : : }
4748 : : }
4749 : :
2921 sfrost@snowman.net 4750 : 888 : systable_endscan(scan);
4751 : :
4752 : : /* prevent error when processing objects multiple times */
2930 4753 : 888 : CommandCounterIncrement();
4754 : :
1910 andres@anarazel.de 4755 : 888 : table_close(relation, RowExclusiveLock);
2930 sfrost@snowman.net 4756 : 888 : }
|