/* * Auto-stats employs this sub-routine to issue an analyze on a specific relation. */ static void autostats_issue_analyze(Oid relationOid) { VacuumStmt *analyzeStmt = NULL; RangeVar *relation = NULL; /* * If this user does not own the table, then auto-stats will not issue the * analyze. */ if (!(pg_class_ownercheck(relationOid, GetUserId()) || (pg_database_ownercheck(MyDatabaseId, GetUserId()) && !IsSharedRelation(relationOid)))) { elog(DEBUG3, "Auto-stats did not issue ANALYZE on tableoid %d since the user does not have table-owner level permissions.", relationOid); return; } relation = makeRangeVar(get_namespace_name(get_rel_namespace(relationOid)), get_rel_name(relationOid), -1); analyzeStmt = makeNode(VacuumStmt); /* Set up command parameters */ analyzeStmt->vacuum = false; analyzeStmt->full = false; analyzeStmt->analyze = true; analyzeStmt->freeze_min_age = -1; analyzeStmt->verbose = false; analyzeStmt->rootonly = false; analyzeStmt->relation = relation; /* not used since we pass relids list */ analyzeStmt->va_cols = NIL; vacuum(analyzeStmt, NIL, NULL, false, false); pfree(analyzeStmt); }
/* * Callback to RangeVarGetRelidExtended(). * * Checks the following: * - the relation specified is a table. * - current user owns the table. * - the table is not a system table. * * If any of these checks fails then an error is raised. */ static void RangeVarCallbackForPolicy(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg) { HeapTuple tuple; Form_pg_class classform; char relkind; tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); if (!HeapTupleIsValid(tuple)) return; classform = (Form_pg_class) GETSTRUCT(tuple); relkind = classform->relkind; /* Must own relation. */ if (!pg_class_ownercheck(relid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, rv->relname); /* No system table modifications unless explicitly allowed. */ if (!allowSystemTableMods && IsSystemClass(relid, classform)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied: \"%s\" is a system catalog", rv->relname))); /* Relation type MUST be a table. */ if (relkind != RELKIND_RELATION) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table", rv->relname))); ReleaseSysCache(tuple); }
/* * check_enable_rls * * Determine, based on the relation, row_security setting, and current role, * if RLS is applicable to this query. RLS_NONE_ENV indicates that, while * RLS is not to be added for this query, a change in the environment may change * that. RLS_NONE means that RLS is not on the relation at all and therefore * we don't need to worry about it. RLS_ENABLED means RLS should be implemented * for the table and the plan cache needs to be invalidated if the environment * changes. * * Handle checking as another role via checkAsUser (for views, etc). Note that * if *not* checking as another role, the caller should pass InvalidOid rather * than GetUserId(). Otherwise the check for row_security = OFF is skipped, and * so we may falsely report that RLS is active when the user has bypassed it. * * If noError is set to 'true' then we just return RLS_ENABLED instead of doing * an ereport() if the user has attempted to bypass RLS and they are not * allowed to. This allows users to check if RLS is enabled without having to * deal with the actual error case (eg: error cases which are trying to decide * if the user should get data from the relation back as part of the error). */ int check_enable_rls(Oid relid, Oid checkAsUser, bool noError) { HeapTuple tuple; Form_pg_class classform; bool relrowsecurity; Oid user_id = checkAsUser ? checkAsUser : GetUserId(); /* Nothing to do for built-in relations */ if (relid < FirstNormalObjectId) return RLS_NONE; tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); if (!HeapTupleIsValid(tuple)) return RLS_NONE; classform = (Form_pg_class) GETSTRUCT(tuple); relrowsecurity = classform->relrowsecurity; ReleaseSysCache(tuple); /* Nothing to do if the relation does not have RLS */ if (!relrowsecurity) return RLS_NONE; /* * Check permissions * * Table owners always bypass RLS. Note that superuser is always * considered an owner. Return RLS_NONE_ENV to indicate that this * decision depends on the environment (in this case, the user_id). */ if (pg_class_ownercheck(relid, user_id)) return RLS_NONE_ENV; /* * If the row_security GUC is 'off', check if the user has permission to * bypass RLS. row_security is always considered 'on' when querying * through a view or other cases where checkAsUser is valid. */ if (!row_security && !checkAsUser) { if (has_bypassrls_privilege(user_id)) /* OK to bypass */ return RLS_NONE_ENV; else if (noError) return RLS_ENABLED; else ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("insufficient privilege to bypass row security."))); } /* RLS should be fully enabled for this relation. */ return RLS_ENABLED; }
/*--------------------------------------------------------------------------- * This cluster code allows for clustering multiple tables at once. Because * of this, we cannot just run everything on a single transaction, or we * would be forced to acquire exclusive locks on all the tables being * clustered, simultaneously --- very likely leading to deadlock. * * To solve this we follow a similar strategy to VACUUM code, * clustering each relation in a separate transaction. For this to work, * we need to: * - provide a separate memory context so that we can pass information in * a way that survives across transactions * - start a new transaction every time a new relation is clustered * - check for validity of the information on to-be-clustered relations, * as someone might have deleted a relation behind our back, or * clustered one on a different index * - end the transaction * * The single-relation case does not have any such overhead. * * We also allow a relation to be specified without index. In that case, * the indisclustered bit will be looked up, and an ERROR will be thrown * if there is no index with the bit set. *--------------------------------------------------------------------------- */ void cluster(ClusterStmt *stmt, bool isTopLevel) { if (stmt->relation != NULL) { /* This is the single-relation case. */ Oid tableOid, indexOid = InvalidOid; Relation rel; RelToCluster rvtc; /* Find and lock the table */ rel = heap_openrv(stmt->relation, AccessExclusiveLock); tableOid = RelationGetRelid(rel); /* Check permissions */ if (!pg_class_ownercheck(tableOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(rel)); /* * Reject clustering a remote temp table ... their local buffer * manager is not going to cope. */ if (isOtherTempNamespace(RelationGetNamespace(rel))) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot cluster temporary tables of other sessions"))); if (stmt->indexname == NULL) { ListCell *index; /* We need to find the index that has indisclustered set. */ foreach(index, RelationGetIndexList(rel)) { HeapTuple idxtuple; Form_pg_index indexForm; indexOid = lfirst_oid(index); idxtuple = SearchSysCache(INDEXRELID, ObjectIdGetDatum(indexOid), 0, 0, 0); if (!HeapTupleIsValid(idxtuple)) elog(ERROR, "cache lookup failed for index %u", indexOid); indexForm = (Form_pg_index) GETSTRUCT(idxtuple); if (indexForm->indisclustered) { ReleaseSysCache(idxtuple); break; } ReleaseSysCache(idxtuple); indexOid = InvalidOid; }
/* * SQL-callable function to scan through an index and summarize all ranges * that are not currently summarized. */ Datum brin_summarize_new_values(PG_FUNCTION_ARGS) { Oid indexoid = PG_GETARG_OID(0); Oid heapoid; Relation indexRel; Relation heapRel; double numSummarized = 0; /* * We must lock table before index to avoid deadlocks. However, if the * passed indexoid isn't an index then IndexGetRelation() will fail. * Rather than emitting a not-very-helpful error message, postpone * complaining, expecting that the is-it-an-index test below will fail. */ heapoid = IndexGetRelation(indexoid, true); if (OidIsValid(heapoid)) heapRel = heap_open(heapoid, ShareUpdateExclusiveLock); else heapRel = NULL; indexRel = index_open(indexoid, ShareUpdateExclusiveLock); /* Must be a BRIN index */ if (indexRel->rd_rel->relkind != RELKIND_INDEX || indexRel->rd_rel->relam != BRIN_AM_OID) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a BRIN index", RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ if (!pg_class_ownercheck(indexoid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(indexRel)); /* * Since we did the IndexGetRelation call above without any lock, it's * barely possible that a race against an index drop/recreation could have * netted us the wrong table. Recheck. */ if (heapRel == NULL || heapoid != IndexGetRelation(indexoid, false)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE), errmsg("could not open parent table of index %s", RelationGetRelationName(indexRel)))); /* OK, do it */ brinsummarize(indexRel, heapRel, &numSummarized, NULL); relation_close(indexRel, ShareUpdateExclusiveLock); relation_close(heapRel, ShareUpdateExclusiveLock); PG_RETURN_INT32((int32) numSummarized); }
void DefineQueryRewrite(RuleStmt *stmt) { RangeVar *event_obj = stmt->relation; Node *event_qual = stmt->whereClause; CmdType event_type = stmt->event; bool is_instead = stmt->instead; bool replace = stmt->replace; List *action = stmt->actions; Relation event_relation; Oid ev_relid; int event_attno; ListCell *l; Query *query; bool RelisBecomingView = false; /* * If we are installing an ON SELECT rule, we had better grab * AccessExclusiveLock to ensure no SELECTs are currently running on the * event relation. For other types of rules, it might be sufficient to * grab ShareLock to lock out insert/update/delete actions. But for now, * let's just grab AccessExclusiveLock all the time. */ event_relation = heap_openrv(event_obj, AccessExclusiveLock); ev_relid = RelationGetRelid(event_relation); /* * Check user has permission to apply rules to this relation. */ if (!pg_class_ownercheck(ev_relid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(event_relation)); /* * No rule actions that modify OLD or NEW */ foreach(l, action) { query = (Query *) lfirst(l); if (query->resultRelation == 0) continue; /* Don't be fooled by INSERT/SELECT */ if (query != getInsertSelectQuery(query, NULL)) continue; if (query->resultRelation == PRS2_OLD_VARNO) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("rule actions on OLD are not implemented"), errhint("Use views or triggers instead."))); if (query->resultRelation == PRS2_NEW_VARNO) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("rule actions on NEW are not implemented"), errhint("Use triggers instead."))); }
/* * RemoveRewriteRule * * Delete a rule given its name. */ void RemoveRewriteRule(Oid owningRel, const char *ruleName, DropBehavior behavior, bool missing_ok) { HeapTuple tuple; Oid eventRelationOid; ObjectAddress object; /* * Find the tuple for the target rule. */ tuple = SearchSysCache(RULERELNAME, ObjectIdGetDatum(owningRel), PointerGetDatum(ruleName), 0, 0); /* * complain if no rule with such name exists */ if (!HeapTupleIsValid(tuple)) { if (!missing_ok) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("rule \"%s\" for relation \"%s\" does not exist", ruleName, get_rel_name(owningRel)))); else ereport(NOTICE, (errmsg("rule \"%s\" for relation \"%s\" does not exist, skipping", ruleName, get_rel_name(owningRel)))); return; } /* * Verify user has appropriate permissions. */ eventRelationOid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class; Assert(eventRelationOid == owningRel); if (!pg_class_ownercheck(eventRelationOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, get_rel_name(eventRelationOid)); /* * Do the deletion */ object.classId = RewriteRelationId; object.objectId = HeapTupleGetOid(tuple); object.objectSubId = 0; ReleaseSysCache(tuple); performDeletion(&object, behavior); }
/*--------------------------------------------------------------------------- * This cluster code allows for clustering multiple tables at once. Because * of this, we cannot just run everything on a single transaction, or we * would be forced to acquire exclusive locks on all the tables being * clustered, simultaneously --- very likely leading to deadlock. * * To solve this we follow a similar strategy to VACUUM code, * clustering each relation in a separate transaction. For this to work, * we need to: * - provide a separate memory context so that we can pass information in * a way that survives across transactions * - start a new transaction every time a new relation is clustered * - check for validity of the information on to-be-clustered relations, * as someone might have deleted a relation behind our back, or * clustered one on a different index * - end the transaction * * The single-relation case does not have any such overhead. * * We also allow a relation being specified without index. In that case, * the indisclustered bit will be looked up, and an ERROR will be thrown * if there is no index with the bit set. *--------------------------------------------------------------------------- */ void cluster(ClusterStmt *stmt) { if (stmt->relation != NULL) { /* This is the single-relation case. */ Oid tableOid, indexOid = InvalidOid; Relation rel; RelToCluster rvtc; /* Find and lock the table */ rel = heap_openrv(stmt->relation, AccessExclusiveLock); tableOid = RelationGetRelid(rel); /* Check permissions */ if (!pg_class_ownercheck(tableOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(rel)); if (stmt->indexname == NULL) { List *index; /* We need to find the index that has indisclustered set. */ foreach(index, RelationGetIndexList(rel)) { HeapTuple idxtuple; Form_pg_index indexForm; indexOid = lfirsto(index); idxtuple = SearchSysCache(INDEXRELID, ObjectIdGetDatum(indexOid), 0, 0, 0); if (!HeapTupleIsValid(idxtuple)) elog(ERROR, "cache lookup failed for index %u", indexOid); indexForm = (Form_pg_index) GETSTRUCT(idxtuple); if (indexForm->indisclustered) { ReleaseSysCache(idxtuple); break; } ReleaseSysCache(idxtuple); indexOid = InvalidOid; }
/* * SQL-callable function to clean the insert pending list */ Datum gin_clean_pending_list(PG_FUNCTION_ARGS) { Oid indexoid = PG_GETARG_OID(0); Relation indexRel = index_open(indexoid, AccessShareLock); IndexBulkDeleteResult stats; GinState ginstate; if (RecoveryInProgress()) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("recovery is in progress"), errhint("GIN pending list cannot be cleaned up during recovery."))); /* Must be a GIN index */ if (indexRel->rd_rel->relkind != RELKIND_INDEX || indexRel->rd_rel->relam != GIN_AM_OID) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a GIN index", RelationGetRelationName(indexRel)))); /* * Reject attempts to read non-local temporary relations; we would be * likely to get wrong data since we have no visibility into the owning * session's local buffers. */ if (RELATION_IS_OTHER_TEMP(indexRel)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot access temporary indexes of other sessions"))); /* User must own the index (comparable to privileges needed for VACUUM) */ if (!pg_class_ownercheck(indexoid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(indexRel)); memset(&stats, 0, sizeof(stats)); initGinState(&ginstate, indexRel); ginInsertCleanup(&ginstate, true, true, &stats); index_close(indexRel, AccessShareLock); PG_RETURN_INT64((int64) stats.pages_deleted); }
/* * Check whether the user is allowed to comment on an attribute of the * specified relation. */ static void CheckAttributeComment(Relation relation) { if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(relation)); /* * Allow comments only on columns of tables, views, and composite types * (which are the only relkinds for which pg_dump will dump per-column * comments). In particular we wish to disallow comments on index * columns, because the naming of an index's columns may change across PG * versions, so dumping per-column comments could create reload failures. */ if (relation->rd_rel->relkind != RELKIND_RELATION && relation->rd_rel->relkind != RELKIND_VIEW && relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table, view, or composite type", RelationGetRelationName(relation)))); }
/* * RemoveRewriteRule * * Delete a rule given its name. */ void RemoveRewriteRule(Oid owningRel, const char *ruleName, DropBehavior behavior, bool missing_ok) { HeapTuple tuple; Oid eventRelationOid; ObjectAddress object; cqContext *pcqCtx; /* * Find the tuple for the target rule. */ pcqCtx = caql_beginscan( NULL, cql("SELECT * FROM pg_rewrite " " WHERE ev_class = :1 " " AND rulename = :2 " " FOR UPDATE ", ObjectIdGetDatum(owningRel), CStringGetDatum((char *) ruleName))); tuple = caql_getnext(pcqCtx); /* * complain if no rule with such name exists */ if (!HeapTupleIsValid(tuple)) { if (!missing_ok) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("rule \"%s\" for relation \"%s\" does not exist", ruleName, get_rel_name(owningRel)))); else ereport(NOTICE, (errmsg("rule \"%s\" for relation \"%s\" does not exist, skipping", ruleName, get_rel_name(owningRel)))); caql_endscan(pcqCtx); return; } /* * Verify user has appropriate permissions. */ eventRelationOid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class; Assert(eventRelationOid == owningRel); if (!pg_class_ownercheck(eventRelationOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, get_rel_name(eventRelationOid)); /* * Do the deletion */ object.classId = RewriteRelationId; object.objectId = HeapTupleGetOid(tuple); object.objectSubId = 0; caql_endscan(pcqCtx); performDeletion(&object, behavior); }
/* * Check ownership of an object previously identified by get_object_address. */ void check_object_ownership( oid_t roleid, objtype_e objtype, struct objaddr address, struct list* objname, struct list* objargs, struct relation* relation) { switch (objtype) { case OBJECT_INDEX: case OBJECT_SEQUENCE: case OBJECT_TABLE: case OBJECT_VIEW: case OBJECT_FOREIGN_TABLE: case OBJECT_COLUMN: case OBJECT_RULE: case OBJECT_TRIGGER: case OBJECT_CONSTRAINT: if (!pg_class_ownercheck(REL_ID(relation), roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, REL_NAME(relation)); break; case OBJECT_DATABASE: if (!pg_db_owner_check(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, nl_to_string(objname)); break; case OBJECT_TYPE: case OBJECT_DOMAIN: case OBJECT_ATTRIBUTE: if (!pg_type_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, format_type_be(address.objectId)); break; case OBJECT_AGGREGATE: case OBJECT_FUNCTION: if (!pg_proc_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, nl_to_string(objname)); break; case OBJECT_OPERATOR: if (!pg_opr_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, nl_to_string(objname)); break; case OBJECT_SCHEMA: if (!pg_ns_owner_check(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, nl_to_string(objname)); break; case OBJECT_COLLATION: if (!pg_collation_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_COLLATION, nl_to_string(objname)); break; case OBJECT_CONVERSION: if (!pg_conversion_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION, nl_to_string(objname)); break; case OBJECT_EXTENSION: if (!pg_extension_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EXTENSION, nl_to_string(objname)); break; case OBJECT_FDW: if (!pg_foreign_data_wrapper_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FDW, nl_to_string(objname)); break; case OBJECT_FOREIGN_SERVER: if (!pg_foreign_server_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, nl_to_string(objname)); break; case OBJECT_LANGUAGE: if (!pg_language_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE, nl_to_string(objname)); break; case OBJECT_OPCLASS: if (!pg_opclass_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS, nl_to_string(objname)); break; case OBJECT_OPFAMILY: if (!pg_opfamily_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY, nl_to_string(objname)); break; case OBJECT_LARGEOBJECT: if (!lo_compat_privileges && !pg_largeobject_ownercheck(address.objectId, roleid)) { ereport(ERROR, ( errcode(E_INSUFFICIENT_PRIVILEGE), errmsg("must be owner of large object %u", address.objectId))); } break; case OBJECT_CAST: { /* We can only check permissions on the source/target types */ type_name_n* sourcetype; type_name_n* targettype; oid_t sourcetypeid; oid_t targettypeid; sourcetype = (type_name_n*) linitial(objname); targettype = (type_name_n*) linitial(objargs); sourcetypeid = typename_to_oid(NULL, sourcetype); targettypeid = typename_to_oid(NULL, targettype); if (!pg_type_ownercheck(sourcetypeid, roleid) && !pg_type_ownercheck(targettypeid, roleid)) { ereport(ERROR, ( errcode(E_INSUFFICIENT_PRIVILEGE), errmsg("must be owner of type %s or type %s", format_type_be(sourcetypeid), format_type_be(targettypeid)))); } } break; case OBJECT_TABLESPACE: if (!tbs_ownercheck(address.objectId, roleid)) aclcheck_error( ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE, nl_to_string(objname)); break; case OBJECT_TSDICTIONARY: if (!pg_ts_dict_ownercheck(address.objectId, roleid)) aclcheck_error( ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY, nl_to_string(objname)); break; case OBJECT_TSCONFIGURATION: if (!pg_ts_config_ownercheck(address.objectId, roleid)) aclcheck_error( ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION, nl_to_string(objname)); break; case OBJECT_ROLE: /* * We treat roles as being "owned" by those with CREATEROLE priv, * except that superusers are only owned by superusers. */ if (superuser_arg(address.objectId)) { if (!superuser_arg(roleid)) { ereport(ERROR, ( errcode(E_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser"))); } } else { if (!has_createrole_privilege(roleid)) { ereport(ERROR, ( errcode(E_INSUFFICIENT_PRIVILEGE), errmsg("must have CREATEROLE privilege"))); } } break; case OBJECT_TSPARSER: case OBJECT_TSTEMPLATE: /* We treat these object types as being owned by superusers */ if (!superuser_arg(roleid)) { ereport(ERROR, ( errcode(E_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser"))); } break; default: elog(ERROR, "unrecognized object type: %d", (int) objtype); } }
/* * SQL-callable interface to mark a range as no longer summarized */ Datum brin_desummarize_range(PG_FUNCTION_ARGS) { Oid indexoid = PG_GETARG_OID(0); int64 heapBlk64 = PG_GETARG_INT64(1); BlockNumber heapBlk; Oid heapoid; Relation heapRel; Relation indexRel; bool done; if (RecoveryInProgress()) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("recovery is in progress"), errhint("BRIN control functions cannot be executed during recovery."))); if (heapBlk64 > MaxBlockNumber || heapBlk64 < 0) { char *blk = psprintf(INT64_FORMAT, heapBlk64); ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("block number out of range: %s", blk))); } heapBlk = (BlockNumber) heapBlk64; /* * We must lock table before index to avoid deadlocks. However, if the * passed indexoid isn't an index then IndexGetRelation() will fail. * Rather than emitting a not-very-helpful error message, postpone * complaining, expecting that the is-it-an-index test below will fail. */ heapoid = IndexGetRelation(indexoid, true); if (OidIsValid(heapoid)) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); else heapRel = NULL; indexRel = index_open(indexoid, ShareUpdateExclusiveLock); /* Must be a BRIN index */ if (indexRel->rd_rel->relkind != RELKIND_INDEX || indexRel->rd_rel->relam != BRIN_AM_OID) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a BRIN index", RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ if (!pg_class_ownercheck(indexoid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); /* * Since we did the IndexGetRelation call above without any lock, it's * barely possible that a race against an index drop/recreation could have * netted us the wrong table. Recheck. */ if (heapRel == NULL || heapoid != IndexGetRelation(indexoid, false)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE), errmsg("could not open parent table of index %s", RelationGetRelationName(indexRel)))); /* the revmap does the hard work */ do { done = brinRevmapDesummarizeRange(indexRel, heapBlk); } while (!done); relation_close(indexRel, ShareUpdateExclusiveLock); relation_close(heapRel, ShareUpdateExclusiveLock); PG_RETURN_VOID(); }
/* * Alter table space move * * Allows a user to move all of their objects in a given tablespace in the * current database to another tablespace. Only objects which the user is * considered to be an owner of are moved and the user must have CREATE rights * on the new tablespace. These checks should mean that ALTER TABLE will never * fail due to permissions, but note that permissions will also be checked at * that level. Objects can be ALL, TABLES, INDEXES, or MATERIALIZED VIEWS. * * All to-be-moved objects are locked first. If NOWAIT is specified and the * lock can't be acquired then we ereport(ERROR). */ Oid AlterTableSpaceMove(AlterTableSpaceMoveStmt *stmt) { List *relations = NIL; ListCell *l; ScanKeyData key[1]; Relation rel; HeapScanDesc scan; HeapTuple tuple; Oid orig_tablespaceoid; Oid new_tablespaceoid; List *role_oids = roleNamesToIds(stmt->roles); /* Ensure we were not asked to move something we can't */ if (!stmt->move_all && stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX && stmt->objtype != OBJECT_MATVIEW) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("only tables, indexes, and materialized views exist in tablespaces"))); /* Get the orig and new tablespace OIDs */ orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false); new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false); /* Can't move shared relations in to or out of pg_global */ /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */ if (orig_tablespaceoid == GLOBALTABLESPACE_OID || new_tablespaceoid == GLOBALTABLESPACE_OID) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("cannot move relations in to or out of pg_global tablespace"))); /* * Must have CREATE rights on the new tablespace, unless it is the * database default tablespace (which all users implicitly have CREATE * rights on). */ if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace) { AclResult aclresult; aclresult = pg_tablespace_aclcheck(new_tablespaceoid, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_TABLESPACE, get_tablespace_name(new_tablespaceoid)); } /* * Now that the checks are done, check if we should set either to * InvalidOid because it is our database's default tablespace. */ if (orig_tablespaceoid == MyDatabaseTableSpace) orig_tablespaceoid = InvalidOid; if (new_tablespaceoid == MyDatabaseTableSpace) new_tablespaceoid = InvalidOid; /* no-op */ if (orig_tablespaceoid == new_tablespaceoid) return new_tablespaceoid; /* * Walk the list of objects in the tablespace and move them. This will * only find objects in our database, of course. */ ScanKeyInit(&key[0], Anum_pg_class_reltablespace, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(orig_tablespaceoid)); rel = heap_open(RelationRelationId, AccessShareLock); scan = heap_beginscan_catalog(rel, 1, key); while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { Oid relOid = HeapTupleGetOid(tuple); Form_pg_class relForm; relForm = (Form_pg_class) GETSTRUCT(tuple); /* * Do not move objects in pg_catalog as part of this, if an admin * really wishes to do so, they can issue the individual ALTER * commands directly. * * Also, explicitly avoid any shared tables, temp tables, or TOAST * (TOAST will be moved with the main table). */ if (IsSystemNamespace(relForm->relnamespace) || relForm->relisshared || isAnyTempNamespace(relForm->relnamespace) || relForm->relnamespace == PG_TOAST_NAMESPACE) continue; /* Only consider objects which live in tablespaces */ if (relForm->relkind != RELKIND_RELATION && relForm->relkind != RELKIND_INDEX && relForm->relkind != RELKIND_MATVIEW) continue; /* Check if we were asked to only move a certain type of object */ if (!stmt->move_all && ((stmt->objtype == OBJECT_TABLE && relForm->relkind != RELKIND_RELATION) || (stmt->objtype == OBJECT_INDEX && relForm->relkind != RELKIND_INDEX) || (stmt->objtype == OBJECT_MATVIEW && relForm->relkind != RELKIND_MATVIEW))) continue; /* Check if we are only moving objects owned by certain roles */ if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner)) continue; /* * Handle permissions-checking here since we are locking the tables * and also to avoid doing a bunch of work only to fail part-way. * Note that permissions will also be checked by AlterTableInternal(). * * Caller must be considered an owner on the table to move it. */ if (!pg_class_ownercheck(relOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, NameStr(relForm->relname)); if (stmt->nowait && !ConditionalLockRelationOid(relOid, AccessExclusiveLock)) ereport(ERROR, (errcode(ERRCODE_OBJECT_IN_USE), errmsg("aborting due to \"%s\".\"%s\" --- lock not available", get_namespace_name(relForm->relnamespace), NameStr(relForm->relname)))); else LockRelationOid(relOid, AccessExclusiveLock); /* Add to our list of objects to move */ relations = lappend_oid(relations, relOid); } heap_endscan(scan); heap_close(rel, AccessShareLock); if (relations == NIL) ereport(NOTICE, (errcode(ERRCODE_NO_DATA_FOUND), errmsg("no matching relations in tablespace \"%s\" found", orig_tablespaceoid == InvalidOid ? "(database default)" : get_tablespace_name(orig_tablespaceoid)))); /* Everything is locked, loop through and move all of the relations. */ foreach(l, relations) { List *cmds = NIL; AlterTableCmd *cmd = makeNode(AlterTableCmd); cmd->subtype = AT_SetTableSpace; cmd->name = stmt->new_tablespacename; cmds = lappend(cmds, cmd); AlterTableInternal(lfirst_oid(l), cmds, false); }
/* * CommentObject -- * * This routine is used to add the associated comment into * pg_description for the object specified by the given SQL command. */ void CommentObject(CommentStmt *stmt) { ObjectAddress address; Relation relation; /* * When loading a dump, we may see a COMMENT ON DATABASE for the old name * of the database. Erroring out would prevent pg_restore from completing * (which is really pg_restore's fault, but for now we will work around * the problem here). Consensus is that the best fix is to treat wrong * database name as a WARNING not an ERROR; hence, the following special * case. (If the length of stmt->objname is not 1, get_object_address will * throw an error below; that's OK.) */ if (stmt->objtype == OBJECT_DATABASE && list_length(stmt->objname) == 1) { char *database = strVal(linitial(stmt->objname)); if (!OidIsValid(get_database_oid(database, true))) { ereport(WARNING, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\" does not exist", database))); return; } } /* * Translate the parser representation which identifies this object into * an ObjectAddress. get_object_address() will throw an error if the * object does not exist, and will also acquire a lock on the target * to guard against concurrent DROP operations. */ address = get_object_address(stmt->objtype, stmt->objname, stmt->objargs, &relation, ShareUpdateExclusiveLock); /* Privilege and integrity checks. */ switch (stmt->objtype) { case OBJECT_INDEX: case OBJECT_SEQUENCE: case OBJECT_TABLE: case OBJECT_VIEW: if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(relation)); break; case OBJECT_COLUMN: CheckAttributeComment(relation); break; case OBJECT_DATABASE: if (!pg_database_ownercheck(address.objectId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, strVal(linitial(stmt->objname))); break; case OBJECT_TYPE: if (!pg_type_ownercheck(address.objectId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, format_type_be(address.objectId)); break; case OBJECT_AGGREGATE: case OBJECT_FUNCTION: if (!pg_proc_ownercheck(address.objectId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, NameListToString(stmt->objname)); break; case OBJECT_OPERATOR: if (!pg_oper_ownercheck(address.objectId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, NameListToString(stmt->objname)); break; case OBJECT_RULE: case OBJECT_TRIGGER: case OBJECT_CONSTRAINT: if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(relation)); break; case OBJECT_SCHEMA: if (!pg_namespace_ownercheck(address.objectId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, strVal(linitial(stmt->objname))); break; case OBJECT_CONVERSION: if (!pg_conversion_ownercheck(address.objectId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION, NameListToString(stmt->objname)); break; case OBJECT_LANGUAGE: if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to comment on procedural language"))); break; case OBJECT_OPCLASS: if (!pg_opclass_ownercheck(address.objectId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS, NameListToString(stmt->objname)); break; case OBJECT_OPFAMILY: if (!pg_opfamily_ownercheck(address.objectId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY, NameListToString(stmt->objname)); break; case OBJECT_LARGEOBJECT: if (!lo_compat_privileges && !pg_largeobject_ownercheck(address.objectId, GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be owner of large object %u", address.objectId))); break; case OBJECT_CAST: CheckCastComment(stmt->objname, stmt->objargs); break; case OBJECT_TABLESPACE: if (!pg_tablespace_ownercheck(address.objectId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE, strVal(linitial(stmt->objname))); break; case OBJECT_ROLE: if (!has_privs_of_role(GetUserId(), address.objectId)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be member of role \"%s\" to comment upon it", strVal(linitial(stmt->objname))))); break; case OBJECT_TSPARSER: if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to comment on text search parser"))); break; case OBJECT_TSDICTIONARY: if (!pg_ts_dict_ownercheck(address.objectId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY, NameListToString(stmt->objname)); break; case OBJECT_TSTEMPLATE: if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to comment on text search template"))); break; case OBJECT_TSCONFIGURATION: if (!pg_ts_config_ownercheck(address.objectId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION, NameListToString(stmt->objname)); break; default: elog(ERROR, "unrecognized object type: %d", (int) stmt->objtype); } /* * Databases, tablespaces, and roles are cluster-wide objects, so any * comments on those objects are recorded in the shared pg_shdescription * catalog. Comments on all other objects are recorded in pg_description. */ if (stmt->objtype == OBJECT_DATABASE || stmt->objtype == OBJECT_TABLESPACE || stmt->objtype == OBJECT_ROLE) CreateSharedComments(address.objectId, address.classId, stmt->comment); else CreateComments(address.objectId, address.classId, address.objectSubId, stmt->comment); /* * If get_object_address() opened the relation for us, we close it to keep * the reference count correct - but we retain any locks acquired by * get_object_address() until commit time, to guard against concurrent * activity. */ if (relation != NULL) relation_close(relation, NoLock); }
/* * AlterSequence * * Modify the definition of a sequence relation */ ObjectAddress AlterSequence(AlterSeqStmt *stmt) { Oid relid; SeqTable elm; Relation seqrel; Buffer buf; HeapTupleData seqtuple; Form_pg_sequence seq; FormData_pg_sequence new___; List *owned_by; ObjectAddress address; /* Open and lock sequence. */ relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok); if (relid == InvalidOid) { ereport(NOTICE, (errmsg("relation \"%s\" does not exist, skipping", stmt->sequence->relname))); return InvalidObjectAddress; } init_sequence(relid, &elm, &seqrel); /* allow ALTER to sequence owner only */ if (!pg_class_ownercheck(relid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, stmt->sequence->relname); /* lock page' buffer and read tuple into new___ sequence structure */ seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple); /* Copy old values of options into workspace */ memcpy(&new___, seq, sizeof(FormData_pg_sequence)); /* Check and set new___ values */ init_params(stmt->options, false, &new___, &owned_by); /* Clear local cache so that we don't think we have cached numbers */ /* Note that we do not change the currval() state */ elm->cached = elm->last; /* check the comment above nextval_internal()'s equivalent call. */ if (RelationNeedsWAL(seqrel)) GetTopTransactionId(); /* Now okay to update the on-disk tuple */ START_CRIT_SECTION(); memcpy(seq, &new___, sizeof(FormData_pg_sequence)); MarkBufferDirty(buf); /* XLOG stuff */ if (RelationNeedsWAL(seqrel)) { xl_seq_rec xlrec; XLogRecPtr recptr; Page page = BufferGetPage(buf); XLogBeginInsert(); XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT); xlrec.node = seqrel->rd_node; XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec)); XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len); recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG); PageSetLSN(page, recptr); } END_CRIT_SECTION(); UnlockReleaseBuffer(buf); /* process OWNED BY if given */ if (owned_by) process_owned_by(seqrel, owned_by); InvokeObjectPostAlterHook(RelationRelationId, relid, 0); ObjectAddressSet(address, RelationRelationId, relid); relation_close(seqrel, NoLock); return address; }
/* * check_enable_rls * * Determine, based on the relation, row_security setting, and current role, * if RLS is applicable to this query. RLS_NONE_ENV indicates that, while * RLS is not to be added for this query, a change in the environment may change * that. RLS_NONE means that RLS is not on the relation at all and therefore * we don't need to worry about it. RLS_ENABLED means RLS should be implemented * for the table and the plan cache needs to be invalidated if the environment * changes. * * Handle checking as another role via checkAsUser (for views, etc). Note that * if *not* checking as another role, the caller should pass InvalidOid rather * than GetUserId(). Otherwise the check for row_security = OFF is skipped, and * so we may falsely report that RLS is active when the user has bypassed it. * * If noError is set to 'true' then we just return RLS_ENABLED instead of doing * an ereport() if the user has attempted to bypass RLS and they are not * allowed to. This allows users to check if RLS is enabled without having to * deal with the actual error case (eg: error cases which are trying to decide * if the user should get data from the relation back as part of the error). */ int check_enable_rls(Oid relid, Oid checkAsUser, bool noError) { HeapTuple tuple; Form_pg_class classform; bool relrowsecurity; Oid user_id = checkAsUser ? checkAsUser : GetUserId(); /* Nothing to do for built-in relations */ if (relid < FirstNormalObjectId) return RLS_NONE; /* * Check if we have been told to explicitly skip RLS (perhaps because this * is a foreign key check) */ if (InRowLevelSecurityDisabled()) return RLS_NONE; tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); if (!HeapTupleIsValid(tuple)) return RLS_NONE; classform = (Form_pg_class) GETSTRUCT(tuple); relrowsecurity = classform->relrowsecurity; ReleaseSysCache(tuple); /* Nothing to do if the relation does not have RLS */ if (!relrowsecurity) return RLS_NONE; /* * Check permissions * * If the relation has row level security enabled and the row_security GUC * is off, then check if the user has rights to bypass RLS for this * relation. Table owners can always bypass, as can any role with the * BYPASSRLS capability. * * If the role is the table owner, then we bypass RLS unless row_security * is set to 'force'. Note that superuser is always considered an owner. * * Return RLS_NONE_ENV to indicate that this decision depends on the * environment (in this case, what the current values of user_id and * row_security are). */ if (row_security != ROW_SECURITY_FORCE && (pg_class_ownercheck(relid, user_id))) return RLS_NONE_ENV; /* * If the row_security GUC is 'off' then check if the user has permission * to bypass it. Note that we have already handled the case where the * user is the table owner above. * * Note that row_security is always considered 'on' when querying through * a view or other cases where checkAsUser is true, so skip this if * checkAsUser is in use. */ if (!checkAsUser && row_security == ROW_SECURITY_OFF) { if (has_bypassrls_privilege(user_id)) /* OK to bypass */ return RLS_NONE_ENV; else if (noError) return RLS_ENABLED; else ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("insufficient privilege to bypass row security."))); } /* RLS should be fully enabled for this relation. */ return RLS_ENABLED; }
/* * DefineQueryRewrite * Create a rule * * This is essentially the same as DefineRule() except that the rule's * action and qual have already been passed through parse analysis. */ void DefineQueryRewrite(char *rulename, Oid event_relid, Node *event_qual, CmdType event_type, bool is_instead, bool replace, List *action) { Relation event_relation; int event_attno; ListCell *l; Query *query; bool RelisBecomingView = false; /* * If we are installing an ON SELECT rule, we had better grab * AccessExclusiveLock to ensure no SELECTs are currently running on the * event relation. For other types of rules, it would be sufficient to * grab ShareRowExclusiveLock to lock out insert/update/delete actions and * to ensure that we lock out current CREATE RULE statements; but because * of race conditions in access to catalog entries, we can't do that yet. */ event_relation = heap_open(event_relid, AccessExclusiveLock); /* * Verify relation is of a type that rules can sensibly be applied to. */ if (event_relation->rd_rel->relkind != RELKIND_RELATION && event_relation->rd_rel->relkind != RELKIND_VIEW) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table or view", RelationGetRelationName(event_relation)))); if (!allowSystemTableMods && IsSystemRelation(event_relation)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied: \"%s\" is a system catalog", RelationGetRelationName(event_relation)))); /* * Check user has permission to apply rules to this relation. */ if (!pg_class_ownercheck(event_relid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(event_relation)); /* * No rule actions that modify OLD or NEW */ foreach(l, action) { query = (Query *) lfirst(l); if (query->resultRelation == 0) continue; /* Don't be fooled by INSERT/SELECT */ if (query != getInsertSelectQuery(query, NULL)) continue; if (query->resultRelation == PRS2_OLD_VARNO) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("rule actions on OLD are not implemented"), errhint("Use views or triggers instead."))); if (query->resultRelation == PRS2_NEW_VARNO) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("rule actions on NEW are not implemented"), errhint("Use triggers instead."))); }
/* * Check ownership of an object previously identified by get_object_address. */ void check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address, List *objname, List *objargs, Relation relation) { switch (objtype) { case OBJECT_INDEX: case OBJECT_SEQUENCE: case OBJECT_TABLE: case OBJECT_VIEW: case OBJECT_COLUMN: case OBJECT_RULE: case OBJECT_TRIGGER: case OBJECT_CONSTRAINT: if (!pg_class_ownercheck(RelationGetRelid(relation), roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(relation)); break; case OBJECT_DATABASE: if (!pg_database_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, NameListToString(objname)); break; case OBJECT_TYPE: case OBJECT_DOMAIN: if (!pg_type_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, format_type_be(address.objectId)); break; case OBJECT_AGGREGATE: case OBJECT_FUNCTION: if (!pg_proc_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, NameListToString(objname)); break; case OBJECT_OPERATOR: if (!pg_oper_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, NameListToString(objname)); break; case OBJECT_SCHEMA: if (!pg_namespace_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, NameListToString(objname)); break; case OBJECT_CONVERSION: if (!pg_conversion_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION, NameListToString(objname)); break; case OBJECT_EXTENSION: if (!pg_extension_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EXTENSION, NameListToString(objname)); break; case OBJECT_LANGUAGE: if (!pg_language_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE, NameListToString(objname)); break; case OBJECT_OPCLASS: if (!pg_opclass_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS, NameListToString(objname)); break; case OBJECT_OPFAMILY: if (!pg_opfamily_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY, NameListToString(objname)); break; case OBJECT_CAST: { /* We can only check permissions on the source/target types */ TypeName *sourcetype = (TypeName *) linitial(objname); TypeName *targettype = (TypeName *) linitial(objargs); Oid sourcetypeid = typenameTypeId(NULL, sourcetype, NULL); Oid targettypeid = typenameTypeId(NULL, targettype, NULL); if (!pg_type_ownercheck(sourcetypeid, roleid) && !pg_type_ownercheck(targettypeid, roleid)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be owner of type %s or type %s", format_type_be(sourcetypeid), format_type_be(targettypeid)))); } break; case OBJECT_TABLESPACE: if (!pg_tablespace_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE, NameListToString(objname)); break; case OBJECT_TSDICTIONARY: if (!pg_ts_dict_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY, NameListToString(objname)); break; case OBJECT_TSCONFIGURATION: if (!pg_ts_config_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION, NameListToString(objname)); break; case OBJECT_ROLE: /* * We treat roles as being "owned" by those with CREATEROLE priv, * except that superusers are only owned by superusers. */ if (superuser_arg(address.objectId)) { if (!superuser_arg(roleid)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser"))); } else { if (!has_createrole_privilege(roleid)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must have CREATEROLE privilege"))); } break; case OBJECT_TSPARSER: case OBJECT_TSTEMPLATE: /* We treat these object types as being owned by superusers */ if (!superuser_arg(roleid)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser"))); break; default: elog(ERROR, "unrecognized object type: %d", (int) objtype); } }