*** a/src/backend/commands/dbcommands.c --- b/src/backend/commands/dbcommands.c *************** *** 42,47 **** --- 42,48 ---- #include "commands/dbcommands.h" #include "commands/dbcommands_xlog.h" #include "commands/defrem.h" + #include "commands/policy.h" #include "commands/seclabel.h" #include "commands/tablespace.h" #include "mb/pg_wchar.h" *************** *** 226,231 **** createdb(const CreatedbStmt *stmt) --- 227,239 ---- errmsg("LOCATION is not supported anymore"), errhint("Consider using tablespaces instead."))); } + else if (strcmp(defel->defname, "catalog_security") == 0) + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("catalog security is not supported with create database command."), + errdetail("Enable catalog security using Alter database command."))); + } else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), *************** *** 519,524 **** createdb(const CreatedbStmt *stmt) --- 527,533 ---- new_record[Anum_pg_database_datfrozenxid - 1] = TransactionIdGetDatum(src_frozenxid); new_record[Anum_pg_database_datminmxid - 1] = TransactionIdGetDatum(src_minmxid); new_record[Anum_pg_database_dattablespace - 1] = ObjectIdGetDatum(dst_deftablespace); + new_record[Anum_pg_database_datcatalogsecurity - 1] = BoolGetDatum(false); /* * We deliberately set datacl to default (NULL), rather than copying it *************** *** 1375,1385 **** AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel) --- 1384,1397 ---- ListCell *option; bool dbistemplate = false; bool dballowconnections = true; + bool dbcatalogsecurity = false; int dbconnlimit = -1; DefElem *distemplate = NULL; DefElem *dallowconnections = NULL; DefElem *dconnlimit = NULL; DefElem *dtablespace = NULL; + DefElem *dcatalogsecurity = NULL; + Form_pg_database pg_database_tuple; Datum new_record[Natts_pg_database]; bool new_record_nulls[Natts_pg_database]; bool new_record_repl[Natts_pg_database]; *************** *** 1421,1426 **** AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel) --- 1433,1447 ---- errmsg("conflicting or redundant options"))); dtablespace = defel; } + else if (strcmp(defel->defname, "catalog_security") == 0) + { + if (dcatalogsecurity) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + + dcatalogsecurity = defel; + } else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), *************** *** 1457,1462 **** AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel) --- 1478,1485 ---- (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid connection limit: %d", dbconnlimit))); } + if (dcatalogsecurity && dcatalogsecurity->arg) + dbcatalogsecurity = defGetBoolean(dcatalogsecurity); /* * Get the old tuple. We don't need a lock on the database per se, *************** *** 1476,1487 **** AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel) --- 1499,1517 ---- (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\" does not exist", stmt->dbname))); + pg_database_tuple = (Form_pg_database)GETSTRUCT(tuple); dboid = HeapTupleGetOid(tuple); if (!pg_database_ownercheck(HeapTupleGetOid(tuple), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, stmt->dbname); + if (dcatalogsecurity && (dboid != MyDatabaseId)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Enabling/disabling catalog security can be done" + " only to the connected database \"%s\"", stmt->dbname))); + /* * In order to avoid getting locked out and having to go through * standalone mode, we refuse to disallow connections to the database *************** *** 1493,1498 **** AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel) --- 1523,1539 ---- (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("cannot disallow connections for current database"))); + if (dcatalogsecurity && BoolGetDatum(dbcatalogsecurity) && !pg_database_tuple->datcatalogsecurity) + { + CreateCatalogPolicy(); + CommandCounterIncrement(); + } + else if (pg_database_tuple->datcatalogsecurity) + { + RemoveCatalogPolicy(); + CommandCounterIncrement(); + } + /* * Build an updated tuple, perusing the information just obtained */ *************** *** 1515,1520 **** AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel) --- 1556,1566 ---- new_record[Anum_pg_database_datconnlimit - 1] = Int32GetDatum(dbconnlimit); new_record_repl[Anum_pg_database_datconnlimit - 1] = true; } + if (dcatalogsecurity) + { + new_record[Anum_pg_database_datcatalogsecurity - 1] = BoolGetDatum(dbcatalogsecurity); + new_record_repl[Anum_pg_database_datcatalogsecurity - 1] = true; + } newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel), new_record, new_record_nulls, new_record_repl); *** a/src/backend/commands/policy.c --- b/src/backend/commands/policy.c *************** *** 22,31 **** --- 22,75 ---- #include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/objectaccess.h" + #include "catalog/pg_aggregate.h" + #include "catalog/pg_am.h" + #include "catalog/pg_amop.h" + #include "catalog/pg_amproc.h" + #include "catalog/pg_attrdef.h" + #include "catalog/pg_attribute.h" #include "catalog/pg_authid.h" + #include "catalog/pg_cast.h" + #include "catalog/pg_class.h" + #include "catalog/pg_collation.h" + #include "catalog/pg_constraint.h" + #include "catalog/pg_conversion.h" + #include "catalog/pg_db_role_setting.h" + #include "catalog/pg_default_acl.h" + #include "catalog/pg_depend.h" + #include "catalog/pg_description.h" + #include "catalog/pg_enum.h" + #include "catalog/pg_event_trigger.h" + #include "catalog/pg_extension.h" + #include "catalog/pg_foreign_data_wrapper.h" + #include "catalog/pg_foreign_server.h" + #include "catalog/pg_foreign_table.h" + #include "catalog/pg_index.h" + #include "catalog/pg_inherits.h" + #include "catalog/pg_language.h" + #include "catalog/pg_largeobject.h" + #include "catalog/pg_largeobject_metadata.h" + #include "catalog/pg_namespace.h" + #include "catalog/pg_opclass.h" + #include "catalog/pg_operator.h" + #include "catalog/pg_opfamily.h" #include "catalog/pg_policy.h" + #include "catalog/pg_proc.h" + #include "catalog/pg_range.h" + #include "catalog/pg_rewrite.h" + #include "catalog/pg_seclabel.h" + #include "catalog/pg_statistic.h" + #include "catalog/pg_transform.h" + #include "catalog/pg_trigger.h" + #include "catalog/pg_ts_config.h" + #include "catalog/pg_ts_config_map.h" + #include "catalog/pg_ts_dict.h" + #include "catalog/pg_ts_parser.h" + #include "catalog/pg_ts_template.h" #include "catalog/pg_type.h" + #include "catalog/pg_user_mapping.h" #include "commands/policy.h" + #include "executor/spi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/pg_list.h" *************** *** 44,56 **** --- 88,109 ---- #include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/rel.h" + #include "utils/snapmgr.h" #include "utils/syscache.h" + #define CATALOG_POLICY_STRING_SIZE 2000 + static void RangeVarCallbackForPolicy(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg); static char parse_policy_command(const char *cmd_name); static Datum *policy_role_list_to_array(List *roles, int *num_roles); + static bool generate_catalog_create_policy_string(HeapTuple cache_tuple, char *buf); + static bool generate_catalog_drop_policy_string(HeapTuple cache_tuple, char *buf); + + /* variable to identify whether pg_policy relcache is built or not? */ + bool policyRelcacheBuilt = false; + /* * Callback to RangeVarGetRelidExtended(). * *************** *** 194,199 **** RelationBuildRowSecurity(Relation relation) --- 247,264 ---- MemoryContext oldcxt = CurrentMemoryContext; RowSecurityDesc *volatile rsdesc = NULL; + /* */ + if (!criticalRelcachesBuilt || !criticalSharedRelcachesBuilt) + return; + + if (relation->rd_id == PolicyRelationId) + { + if (policyRelcacheBuilt) + return; + else + policyRelcacheBuilt = true; + } + /* * Create a memory context to hold everything associated with this * relation's row security policy. This makes it easy to clean up during *************** *** 325,330 **** RelationBuildRowSecurity(Relation relation) --- 390,402 ---- /* Delete rscxt, first making sure it isn't active */ MemoryContextSwitchTo(oldcxt); MemoryContextDelete(rscxt); + + if (relation->rd_id == PolicyRelationId) + { + Assert(policyRelcacheBuilt == true); + policyRelcacheBuilt = false; + } + PG_RE_THROW(); } PG_END_TRY(); *************** *** 407,412 **** RemovePolicyById(Oid policy_id) --- 479,1032 ---- heap_close(pg_policy_rel, RowExclusiveLock); } + static bool + generate_catalog_create_policy_string(HeapTuple tuple, char *buf) + { + bool can_create_policy = false; + Form_pg_class pg_class_tuple; + Oid relationid; + + pg_class_tuple = (Form_pg_class)GETSTRUCT(tuple); + relationid = HeapTupleGetOid(tuple); + + if (IsSharedRelation(relationid)) + return can_create_policy; + + switch (relationid) + { + /* + * Following catalog tables data is accessible to all roles. + * So they doesn't need any speicific RLS policies on them. + */ + case AggregateRelationId: + case AccessMethodRelationId: + case AccessMethodOperatorRelationId: + case AccessMethodProcedureRelationId: + break; + case AttrDefaultRelationId: + sprintf(buf, "create policy %s_read_own_data on %s for select using" + " (has_column_privilege(adrelid, adnum,'any'))", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true; + break; + case AttributeRelationId: + sprintf(buf, "create policy %s_read_own_data on %s for select using" + " (has_column_privilege(attrelid, attnum,'any'))", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true; + break; + case CastRelationId: + sprintf(buf, "create policy %s_read_own_data on %s for select using" + " ((oid < 16384) OR has_cast_privilege(oid, 'any'))", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true; + break; + case RelationRelationId: + sprintf(buf, "create policy %s_read_own_data on %s for select using" + " ((oid < 16384) OR has_table_privilege(oid,'any'))", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true; + break; + case CollationRelationId: + /*sprintf(buf, "create policy %s_read_own_data on %s for select using" + " ((oid < 16384) OR (pg_get_userbyid(collowner) = current_user))", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true;*/ + break; + case ConstraintRelationId: + sprintf(buf, "create policy %s_read_own_data on %s for select using" + " ((oid < 16384) OR has_constraint_privilege(oid,'any'))", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true; + break; + case ConversionRelationId: + /*sprintf(buf, "create policy %s_read_own_data on %s for select using" + " ((oid < 16384) OR (pg_get_userbyid(conowner) = current_user))", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true;*/ + break; + case DefaultAclRelationId: + sprintf(buf, "create policy %s_read_own_data on %s for select using" + " (pg_get_userbyid(defaclrole) = current_user)", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true; + break; + case DependRelationId: + sprintf(buf, "create policy %s_read_own_data on %s for select using" + " ((classid = 2600)" + " OR (classid = 2605 AND has_cast_privilege(objid, 'any'))" + " OR (classid = 3456)" + " OR (classid = 1249 AND has_column_privilege(objid, objsubid::smallint, 'any'))" + " OR (classid = 2606 AND has_constraint_privilege(objid, 'any'))" + " OR (classid = 2607)" + " OR (classid = 1262 AND has_database_privilege(objid, 'any'))" + " OR (classid = 1247 AND has_type_privilege(objid, 'any'))" + " OR (classid = 3079)" + " OR (classid = 3466)" + " OR (classid = 2328 AND has_foreign_data_wrapper_privilege(objid, 'any'))" + " OR (classid = 1259 AND has_table_privilege(objid,'any'))" + " OR (classid = 1255 AND has_function_privilege(objid, 'any'))" + " OR (classid = 2601)" + " OR (classid = 2602)" + " OR (classid = 2603)" + " OR (classid = 2604 AND has_column_default_privilege(objid, 'any'))" + " OR (classid = 2612 AND has_language_privilege(objid, 'any'))" + " OR (classid = 2613)" + " OR (classid = 2616)" + " OR (classid = 2617)" + " OR (classid = 2753)" + " OR (classid = 3501)" + " OR (classid = 3466)" + " OR (classid = 3079)" + " OR (classid = 3256 AND has_policy_privilege(objid, 'any'))" + " OR (classid = 1260 AND pg_has_role(objid, 'any'))" + " OR (classid = 2618)" + " OR (classid = 2615 AND has_schema_privilege(objid, 'any'))" + " OR (classid = 1417 AND has_server_privilege(objid, 'any'))" + " OR (classid = 1213 AND has_tablespace_privilege(objid, 'any'))" + " OR (classid = 3600)" + " OR (classid = 3601)" + " OR (classid = 3602)" + " OR (classid = 3764)" + " OR (classid = 3576)" + " OR (classid = 2620 AND has_trigger_privilege(objid, 'any'))" + " OR (classid = 1418 AND has_user_mapping_privilege(objid, 'any')))", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true; + break; + case DescriptionRelationId: + sprintf(buf, "create policy %s_read_own_data on %s for select using" + " ((classoid = 2600)" + " OR (classoid = 2605 AND has_cast_privilege(objoid, 'any'))" + " OR (classoid = 3456)" + " OR (classoid = 1249 AND has_column_privilege(objoid, objsubid::smallint, 'any'))" + " OR (classoid = 2606 AND has_constraint_privilege(objoid, 'any'))" + " OR (classoid = 2607)" + " OR (classoid = 1262 AND has_database_privilege(objoid, 'any'))" + " OR (classoid = 1247 AND has_type_privilege(objoid, 'any'))" + " OR (classoid = 3079)" + " OR (classoid = 3466)" + " OR (classoid = 2328 AND has_foreign_data_wrapper_privilege(objoid, 'any'))" + " OR (classoid = 1259 AND has_table_privilege(objoid,'any'))" + " OR (classoid = 1255 AND has_function_privilege(objoid, 'any'))" + " OR (classoid = 2613)" + " OR (classoid = 2616)" + " OR (classoid = 2617)" + " OR (classoid = 2753)" + " OR (classoid = 2995)" + " OR (classoid = 3256 AND has_policy_privilege(objoid, 'any'))" + " OR (classoid = 1260 AND pg_has_role(objoid, 'any'))" + " OR (classoid = 2618)" + " OR (classoid = 2615 AND has_schema_privilege(objoid, 'any'))" + " OR (classoid = 1417 AND has_server_privilege(objoid, 'any'))" + " OR (classoid = 1213 AND has_tablespace_privilege(objoid, 'any'))" + " OR (classoid = 3541)" + " OR (classoid = 3600)" + " OR (classoid = 3601)" + " OR (classoid = 3602)" + " OR (classoid = 3764)" + " OR (classoid = 3576)" + " OR (classoid = 2620 AND has_trigger_privilege(objoid, 'any')))", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true; + break; + case EnumRelationId: + break; + case EventTriggerRelationId: + break; + case ExtensionRelationId: + break; + case ForeignDataWrapperRelationId: + sprintf(buf, "create policy %s_read_own_data on %s for select using" + " ((oid < 16384) OR has_foreign_data_wrapper_privilege(oid,'any'))", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true; + break; + case ForeignServerRelationId: + sprintf(buf, "create policy %s_read_own_data on %s for select using" + " ((oid < 16384) OR has_server_privilege(oid,'any'))", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true; + break; + case ForeignTableRelationId: + sprintf(buf, "create policy %s_read_own_data on %s for select using" + " (has_table_privilege(ftrelid, 'any'))", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true; + break; + case IndexRelationId: + sprintf(buf, "create policy %s_read_own_data on %s for select using" + " (has_table_privilege(indrelid, 'any'))", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true; + break; + case InheritsRelationId: + sprintf(buf, "create policy %s_read_own_data on %s for select using" + " (has_table_privilege(inhrelid, 'any'))", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true; + break; + case LanguageRelationId: + sprintf(buf, "create policy %s_read_own_data on %s for select using" + " ((oid < 16384) OR has_language_privilege(oid, 'any'))", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true; + break; + case LargeObjectRelationId: + break; + case LargeObjectMetadataRelationId: + break; + case NamespaceRelationId: + sprintf(buf, "create policy %s_read_own_data on %s for select using" + " ((oid < 16384) OR has_schema_privilege(oid, 'any'))", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true; + break; + case OperatorClassRelationId: + /*sprintf(buf, "create policy %s_read_own_data on %s for select using" + " ((oid < 16384) OR (pg_get_userbyid(opcowner) = current_user))", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true;*/ + break; + case OperatorRelationId: + /*sprintf(buf, "create policy %s_read_own_data on %s for select using" + " ((oid < 16384) OR (pg_get_userbyid(oprowner) = current_user))", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true;*/ + break; + case OperatorFamilyRelationId: + /*sprintf(buf, "create policy %s_read_own_data on %s for select using" + " ((oid < 16384) OR (pg_get_userbyid(opfowner) = current_user))", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true;*/ + break; + case PolicyRelationId: + sprintf(buf, "create policy %s_read_own_data on %s for select using" + " ((oid < 16384) OR has_table_privilege(polrelid, 'any'))", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true; + break; + case ProcedureRelationId: + sprintf(buf, "create policy %s_read_own_data on %s for select using" + " ((oid < 16384) OR has_function_privilege(oid, 'any'))", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true; + break; + case RangeRelationId: + break; + case RewriteRelationId: + break; + case SecLabelRelationId: + sprintf(buf, "create policy %s_read_own_data on %s for select using" + " ((classoid = 2600)" + " OR (classoid = 1249 AND has_column_privilege(objoid, objsubid::smallint, 'any'))" + " OR (classoid = 1262 AND has_database_privilege(objoid, 'any'))" + " OR (classoid = 1247 AND has_type_privilege(objoid, 'any'))" + " OR (classoid = 1259 AND has_table_privilege(objoid,'any'))" + " OR (classoid = 1255 AND has_function_privilege(objoid, 'any'))" + " OR (classoid = 2612 AND has_language_privilege(objoid, 'any'))" + " OR (classoid = 1260 AND pg_has_role(objoid, 'any'))" + " OR (classoid = 2613)" + " OR (classoid = 3466)" + " OR (classoid = 1213 AND has_tablespace_privilege(objoid, 'any')))", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true; + break; + case StatisticRelationId: + sprintf(buf, "create policy %s_read_own_data on %s for select using" + " ((starelid < 16384) OR has_column_privilege(starelid, staattnum, 'any'))", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true; + break; + case TransformRelationId: + break; + case TriggerRelationId: + sprintf(buf, "create policy %s_read_own_data on %s for select using" + " ((oid < 16384) OR has_table_privilege(tgrelid, 'any'))", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true; + break; + case TSConfigRelationId: + break; + case TSConfigMapRelationId: + break; + case TSDictionaryRelationId: + break; + case TSParserRelationId: + break; + case TSTemplateRelationId: + break; + case TypeRelationId: + sprintf(buf, "create policy %s_read_own_data on %s for select using" + " ((oid < 16384) OR has_type_privilege(oid, 'any'))", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true; + break; + case UserMappingRelationId: + sprintf(buf, "create policy %s_read_own_data on %s for select using" + " ((oid < 16384) OR pg_has_role(umuser, 'any')" + " OR has_server_privilege(umserver, 'any'))", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true; + break; + default: + can_create_policy = false; + break; + } + + return can_create_policy; + } + + static bool + generate_catalog_drop_policy_string(HeapTuple tuple, char *buf) + { + bool can_create_policy = false; + Form_pg_class pg_class_tuple; + Oid relationid; + + pg_class_tuple = (Form_pg_class)GETSTRUCT(tuple); + relationid = HeapTupleGetOid(tuple); + + if (IsSharedRelation(relationid)) + return can_create_policy; + + switch (relationid) + { + /* + * Following catalog tables data is accessible to all roles. + * So they doesn't need any speicific RLS policies on them. + */ + case AggregateRelationId: + case AccessMethodRelationId: + case AccessMethodOperatorRelationId: + case AccessMethodProcedureRelationId: + case CollationRelationId: + case ConversionRelationId: + case EnumRelationId: + case EventTriggerRelationId: + case ExtensionRelationId: + case LargeObjectRelationId: + case LargeObjectMetadataRelationId: + case OperatorClassRelationId: + case OperatorRelationId: + case OperatorFamilyRelationId: + case RangeRelationId: + case RewriteRelationId: + case TransformRelationId: + case TSConfigRelationId: + case TSConfigMapRelationId: + case TSDictionaryRelationId: + case TSParserRelationId: + case TSTemplateRelationId: + break; + + case AttrDefaultRelationId: + case AttributeRelationId: + case CastRelationId: + case RelationRelationId: + case ConstraintRelationId: + case DefaultAclRelationId: + case DependRelationId: + case DescriptionRelationId: + case ForeignDataWrapperRelationId: + case ForeignServerRelationId: + case ForeignTableRelationId: + case IndexRelationId: + case InheritsRelationId: + case LanguageRelationId: + case NamespaceRelationId: + case PolicyRelationId: + case ProcedureRelationId: + case SecLabelRelationId: + case StatisticRelationId: + case TriggerRelationId: + case TypeRelationId: + case UserMappingRelationId: + sprintf(buf, "drop policy %s_read_own_data on %s", + pg_class_tuple->relname.data, pg_class_tuple->relname.data); + can_create_policy = true; + break; + + default: + can_create_policy = false; + break; + } + + return can_create_policy; + } + + /* + * CreateCatalogPolicy - + * handles the execution of the ALTER DATBASE ROW LEVEL SECUTIRY = TRUE command. + */ + void + CreateCatalogPolicy() + { + Relation rel; + ScanKeyData scankey; + SysScanDesc scan; + HeapTuple tuple; + bool allow_sytem_table_mods_old; + char *buf; + + /* + * Get all catalog relations from pg_class system table and + * enable the row level security along with the catalog policy + * command. + */ + SPI_connect(); + PushActiveSnapshot(GetTransactionSnapshot()); + + rel = heap_open(RelationRelationId, RowExclusiveLock); + ScanKeyInit(&scankey, + ObjectIdAttributeNumber, + BTLessStrategyNumber, F_OIDLT, + ObjectIdGetDatum(FirstNormalObjectId)); + scan = systable_beginscan(rel, ClassOidIndexId, true, + NULL, 1, &scankey); + + buf = palloc(CATALOG_POLICY_STRING_SIZE); + allow_sytem_table_mods_old = allowSystemTableMods; + allowSystemTableMods = true; + + PG_TRY(); + { + while ((tuple = systable_getnext(scan)) != NULL) + { + int ret; + HeapTuple cache_tuple; + Form_pg_class pg_class_tuple; + bool can_create_policy; + + pg_class_tuple = (Form_pg_class)GETSTRUCT(tuple); + if (pg_class_tuple->relkind != RELKIND_RELATION) + continue; + + cache_tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(HeapTupleGetOid(tuple))); + + if (!HeapTupleIsValid(cache_tuple)) + elog(ERROR, "cache lookup failed for relation %u", HeapTupleGetOid(tuple)); + + can_create_policy = generate_catalog_create_policy_string(cache_tuple, buf); + if (!can_create_policy) + { + heap_freetuple(cache_tuple); + continue; + } + + ret = SPI_execute(buf, false, 0); + if (ret != SPI_OK_UTILITY) + elog(ERROR, "Creating policy failed : error code %d", ret); + + ((Form_pg_class)GETSTRUCT(cache_tuple))->relrowsecurity = true; + heap_inplace_update(rel, cache_tuple); + + heap_freetuple(cache_tuple); + } + } + PG_CATCH(); + { + allowSystemTableMods = allow_sytem_table_mods_old; + PG_RE_THROW(); + } + PG_END_TRY(); + + allowSystemTableMods = allow_sytem_table_mods_old; + pfree(buf); + + systable_endscan(scan); + heap_close(rel, NoLock); + + SPI_finish(); + PopActiveSnapshot(); + } + + /* + * RemoveCatalogPolicy - + * handles the execution of the ALTER DATBASE ROW LEVEL SECUTIRY = FALSE command. + */ + void + RemoveCatalogPolicy() + { + Relation rel; + ScanKeyData scankey; + SysScanDesc scan; + HeapTuple tuple; + Form_pg_class pg_class_tuple; + bool allow_sytem_table_mods_old; + char *buf; + + /* + * Get all catalog relations from pg_class system table and + * enable the row level security along with the catalog policy + * command. + */ + SPI_connect(); + PushActiveSnapshot(GetTransactionSnapshot()); + + rel = heap_open(RelationRelationId, RowExclusiveLock); + ScanKeyInit(&scankey, + ObjectIdAttributeNumber, + BTLessStrategyNumber, F_OIDLT, + ObjectIdGetDatum(FirstNormalObjectId)); + scan = systable_beginscan(rel, ClassOidIndexId, true, + NULL, 1, &scankey); + + buf = palloc(CATALOG_POLICY_STRING_SIZE); + + allow_sytem_table_mods_old = allowSystemTableMods; + allowSystemTableMods = true; + + PG_TRY(); + { + while ((tuple = systable_getnext(scan)) != NULL) + { + int ret; + HeapTuple cache_tuple; + bool can_drop_policy; + + pg_class_tuple = (Form_pg_class)GETSTRUCT(tuple); + if (pg_class_tuple->relkind != RELKIND_RELATION) + continue; + + cache_tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(HeapTupleGetOid(tuple))); + if (!HeapTupleIsValid(cache_tuple)) + elog(ERROR, "cache lookup failed for relation %u", HeapTupleGetOid(tuple)); + + can_drop_policy = generate_catalog_drop_policy_string(cache_tuple, buf); + if (!can_drop_policy) + { + heap_freetuple(cache_tuple); + continue; + } + + ret = SPI_execute(buf, false, 0); + if (ret != SPI_OK_UTILITY) + elog(ERROR, "Creating policy failed : error code %d", ret); + + ((Form_pg_class)GETSTRUCT(cache_tuple))->relrowsecurity = false; + heap_inplace_update(rel, cache_tuple); + + heap_freetuple(cache_tuple); + } + } + PG_CATCH(); + { + allowSystemTableMods = allow_sytem_table_mods_old; + PG_RE_THROW(); + } + PG_END_TRY(); + + allowSystemTableMods = allow_sytem_table_mods_old; + + pfree(buf); + systable_endscan(scan); + heap_close(rel, NoLock); + + SPI_finish(); + PopActiveSnapshot(); + } + /* * CreatePolicy - * handles the execution of the CREATE POLICY command. *** a/src/backend/parser/gram.y --- b/src/backend/parser/gram.y *************** *** 8881,8886 **** createdb_opt_name: --- 8881,8887 ---- | OWNER { $$ = pstrdup($1); } | TABLESPACE { $$ = pstrdup($1); } | TEMPLATE { $$ = pstrdup($1); } + | CATALOG_P SECURITY { $$ = pstrdup("catalog_security"); } ; /* *** a/src/backend/utils/adt/acl.c --- b/src/backend/utils/adt/acl.c *************** *** 17,27 **** #include #include "access/htup_details.h" #include "catalog/namespace.h" #include "catalog/pg_authid.h" #include "catalog/pg_auth_members.h" ! #include "catalog/pg_type.h" #include "catalog/pg_class.h" #include "commands/dbcommands.h" #include "commands/proclang.h" #include "commands/tablespace.h" --- 17,35 ---- #include #include "access/htup_details.h" + #include "access/sysattr.h" + #include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/pg_authid.h" #include "catalog/pg_auth_members.h" ! #include "catalog/pg_attrdef.h" ! #include "catalog/pg_cast.h" #include "catalog/pg_class.h" + #include "catalog/pg_constraint.h" + #include "catalog/pg_policy.h" + #include "catalog/pg_trigger.h" + #include "catalog/pg_type.h" + #include "catalog/pg_user_mapping.h" #include "commands/dbcommands.h" #include "commands/proclang.h" #include "commands/tablespace.h" *************** *** 31,36 **** --- 39,45 ---- #include "utils/acl.h" #include "utils/builtins.h" #include "utils/catcache.h" + #include "utils/fmgroids.h" #include "utils/inval.h" #include "utils/lsyscache.h" #include "utils/memutils.h" *************** *** 5267,5269 **** get_rolespec_name(const Node *node) --- 5276,5568 ---- return rolename; } + + /* + * has_cast_privilege_id + * Check user privileges on a cast given + * cast oid, and text priv name. + */ + Datum + has_cast_privilege_id(PG_FUNCTION_ARGS) + { + Oid castoid = PG_GETARG_OID(0); + text *priv_type_text = PG_GETARG_TEXT_P(1); + Oid roleid; + AclMode mode; + AclResult aclresult1; + AclResult aclresult2; + Relation castDesc; + ScanKeyData skey[1]; + SysScanDesc rcscan; + HeapTuple tup; + Form_pg_cast castForm; + + roleid = GetUserId(); + mode = convert_server_priv_string(priv_type_text); + + castDesc = heap_open(CastRelationId, AccessShareLock); + + ScanKeyInit(&skey[0], + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(castoid)); + + rcscan = systable_beginscan(castDesc, CastOidIndexId, true, + NULL, 1, skey); + + tup = systable_getnext(rcscan); + + if (!HeapTupleIsValid(tup)) + { + systable_endscan(rcscan); + heap_close(castDesc, AccessShareLock); + PG_RETURN_NULL(); + } + + castForm = (Form_pg_cast)GETSTRUCT(tup); + + aclresult1 = pg_type_aclcheck(castForm->castsource, roleid, mode); + aclresult2 = pg_type_aclcheck(castForm->casttarget, roleid, mode); + + systable_endscan(rcscan); + heap_close(castDesc, AccessShareLock); + PG_RETURN_BOOL((aclresult1 == ACLCHECK_OK) || (aclresult2 == ACLCHECK_OK)); + } + + /* + * has_constraint_privilege_id + * Check user privileges on a constraint given + * constraint oid, and text priv name. + */ + Datum + has_constraint_privilege_id(PG_FUNCTION_ARGS) + { + Oid constraintoid = PG_GETARG_OID(0); + text *priv_type_text = PG_GETARG_TEXT_P(1); + Oid roleid; + AclMode mode; + AclResult aclresult; + Relation constraintDesc; + ScanKeyData skey[1]; + SysScanDesc rcscan; + HeapTuple tup; + Form_pg_constraint constraintForm; + + roleid = GetUserId(); + mode = convert_server_priv_string(priv_type_text); + + constraintDesc = heap_open(ConstraintRelationId, AccessShareLock); + + ScanKeyInit(&skey[0], + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(constraintoid)); + + rcscan = systable_beginscan(constraintDesc, ConstraintOidIndexId, true, + NULL, 1, skey); + + tup = systable_getnext(rcscan); + + if (!HeapTupleIsValid(tup)) + { + systable_endscan(rcscan); + heap_close(constraintDesc, AccessShareLock); + PG_RETURN_NULL(); + } + + constraintForm = (Form_pg_constraint)GETSTRUCT(tup); + + if (constraintForm->contypid) + aclresult = pg_type_aclcheck(constraintForm->contypid, roleid, mode); + else + aclresult = pg_class_aclcheck(constraintForm->conrelid, roleid, mode); + + systable_endscan(rcscan); + heap_close(constraintDesc, AccessShareLock); + PG_RETURN_BOOL(aclresult == ACLCHECK_OK); + } + + /* + * has_column_default_privilege_id + * Check user privileges on a column default given + * attrdefault oid, and text priv name. + */ + Datum + has_column_default_privilege_id(PG_FUNCTION_ARGS) + { + Oid attrdefaulttoid = PG_GETARG_OID(0); + text *priv_type_text = PG_GETARG_TEXT_P(1); + Oid roleid; + AclMode mode; + int privresult; + Relation attrDefaultDesc; + ScanKeyData skey[1]; + SysScanDesc rcscan; + HeapTuple tup; + Form_pg_attrdef attrDefForm; + + roleid = GetUserId(); + mode = convert_server_priv_string(priv_type_text); + + attrDefaultDesc = heap_open(AttrDefaultRelationId, AccessShareLock); + + ScanKeyInit(&skey[0], + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(attrdefaulttoid)); + + rcscan = systable_beginscan(attrDefaultDesc, AttrDefaultOidIndexId, true, + NULL, 1, skey); + + tup = systable_getnext(rcscan); + + if (!HeapTupleIsValid(tup)) + { + systable_endscan(rcscan); + heap_close(attrDefaultDesc, AccessShareLock); + PG_RETURN_NULL(); + } + + attrDefForm = (Form_pg_attrdef)GETSTRUCT(tup); + + privresult = column_privilege_check(attrDefForm->adrelid, attrDefForm->adnum, roleid, mode); + + systable_endscan(rcscan); + heap_close(attrDefaultDesc, AccessShareLock); + + if (privresult < 0) + PG_RETURN_NULL(); + PG_RETURN_BOOL(privresult); + } + + /* + * has_policy_privilege_id + * Check user privileges on a policy given + * policy oid, and text priv name. + */ + Datum + has_policy_privilege_id(PG_FUNCTION_ARGS) + { + Oid policyoid = PG_GETARG_OID(0); + text *priv_type_text = PG_GETARG_TEXT_P(1); + Oid roleid; + AclMode mode; + AclResult aclresult; + Relation policyDesc; + ScanKeyData skey[1]; + SysScanDesc rcscan; + HeapTuple tup; + Form_pg_policy policyForm; + + roleid = GetUserId(); + mode = convert_server_priv_string(priv_type_text); + + policyDesc = heap_open(PolicyRelationId, AccessShareLock); + + ScanKeyInit(&skey[0], + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(policyoid)); + + rcscan = systable_beginscan(policyDesc, PolicyOidIndexId, true, + NULL, 1, skey); + + tup = systable_getnext(rcscan); + + if (!HeapTupleIsValid(tup)) + { + systable_endscan(rcscan); + heap_close(policyDesc, AccessShareLock); + PG_RETURN_NULL(); + } + + policyForm = (Form_pg_policy)GETSTRUCT(tup); + + aclresult = pg_class_aclcheck(policyForm->polrelid, roleid, mode); + + systable_endscan(rcscan); + heap_close(policyDesc, AccessShareLock); + PG_RETURN_BOOL(aclresult == ACLCHECK_OK); + } + + /* + * has_trigger_privilege_id + * Check user privileges on a trigger given + * trigger oid, and text priv name. + */ + Datum + has_trigger_privilege_id(PG_FUNCTION_ARGS) + { + Oid triggeroid = PG_GETARG_OID(0); + text *priv_type_text = PG_GETARG_TEXT_P(1); + Oid roleid; + AclMode mode; + AclResult aclresult; + Relation triggerDesc; + ScanKeyData skey[1]; + SysScanDesc rcscan; + HeapTuple tup; + Form_pg_trigger triggerForm; + + roleid = GetUserId(); + mode = convert_server_priv_string(priv_type_text); + + triggerDesc = heap_open(PolicyRelationId, AccessShareLock); + + ScanKeyInit(&skey[0], + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(triggeroid)); + + rcscan = systable_beginscan(triggerDesc, PolicyOidIndexId, true, + NULL, 1, skey); + + tup = systable_getnext(rcscan); + + if (!HeapTupleIsValid(tup)) + { + systable_endscan(rcscan); + heap_close(triggerDesc, AccessShareLock); + PG_RETURN_NULL(); + } + + triggerForm = (Form_pg_trigger)GETSTRUCT(tup); + + aclresult = pg_class_aclcheck(triggerForm->tgrelid, roleid, mode); + + systable_endscan(rcscan); + heap_close(triggerDesc, AccessShareLock); + PG_RETURN_BOOL(aclresult == ACLCHECK_OK); + } + + /* + * has_user_mapping_privilege_id + * Check user privileges on a user mapping given + * user_mapping oid, and text priv name. + */ + Datum + has_user_mapping_privilege_id(PG_FUNCTION_ARGS) + { + Oid usermapoid = PG_GETARG_OID(0); + text *priv_type_text = PG_GETARG_TEXT_P(1); + Oid roleid; + AclMode mode; + AclResult aclresult1; + AclResult aclresult2; + HeapTuple tup; + Form_pg_user_mapping usermapForm; + + roleid = GetUserId(); + mode = convert_server_priv_string(priv_type_text); + + tup = SearchSysCache1(USERMAPPINGOID, usermapoid); + if (!HeapTupleIsValid(tup)) + PG_RETURN_NULL(); + + usermapForm = (Form_pg_user_mapping)GETSTRUCT(tup); + + aclresult1 = pg_role_aclcheck(usermapForm->umuser, roleid, mode); + aclresult2 = pg_foreign_server_aclcheck(usermapForm->umserver, roleid, mode); + + PG_RETURN_BOOL((aclresult1 == ACLCHECK_OK) || (aclresult2 == ACLCHECK_OK)); + } *** a/src/backend/utils/cache/relcache.c --- b/src/backend/utils/cache/relcache.c *************** *** 2076,2081 **** RelationClearRelation(Relation relation, bool rebuild) --- 2076,2083 ---- */ if (relation->rd_isnailed) { + HeapTuple pg_class_tuple; + RelationInitPhysicalAddr(relation); if (relation->rd_rel->relkind == RELKIND_INDEX) *************** *** 2084,2089 **** RelationClearRelation(Relation relation, bool rebuild) --- 2086,2112 ---- if (relation->rd_refcnt > 1 && IsTransactionState()) RelationReloadIndexInfo(relation); } + + /* + * A nailed-in system relation never ever blow away from rel cache, because + * we'd be unable to recover. So for such relations, we will update the + * row security descriptor if it is enabled. Usually this happens during + * RelationBuildDesc function, but for nailed-in system relations, we will + * do it here. + */ + if (criticalRelcachesBuilt + && criticalSharedRelcachesBuilt + && IsTransactionState()) + { + /* + * find the tuple in pg_class corresponding to the given relation id + */ + pg_class_tuple = ScanPgRelation(RelationGetRelid(relation), true, false); + + if (((Form_pg_class)GETSTRUCT(pg_class_tuple))->relrowsecurity) + RelationBuildRowSecurity(relation); + heap_freetuple(pg_class_tuple); + } return; } *** a/src/include/catalog/pg_database.h --- b/src/include/catalog/pg_database.h *************** *** 43,49 **** CATALOG(pg_database,1262) BKI_SHARED_RELATION BKI_ROWTYPE_OID(1248) BKI_SCHEMA_M TransactionId datfrozenxid; /* all Xids < this are frozen in this DB */ TransactionId datminmxid; /* all multixacts in the DB are >= this */ Oid dattablespace; /* default table space for this DB */ ! #ifdef CATALOG_VARLEN /* variable-length fields start here */ aclitem datacl[1]; /* access permissions */ #endif --- 43,49 ---- TransactionId datfrozenxid; /* all Xids < this are frozen in this DB */ TransactionId datminmxid; /* all multixacts in the DB are >= this */ Oid dattablespace; /* default table space for this DB */ ! bool datcatalogsecurity; /* catalog security is enabled? */ #ifdef CATALOG_VARLEN /* variable-length fields start here */ aclitem datacl[1]; /* access permissions */ #endif *************** *** 60,81 **** typedef FormData_pg_database *Form_pg_database; * compiler constants for pg_database * ---------------- */ ! #define Natts_pg_database 13 ! #define Anum_pg_database_datname 1 ! #define Anum_pg_database_datdba 2 ! #define Anum_pg_database_encoding 3 ! #define Anum_pg_database_datcollate 4 ! #define Anum_pg_database_datctype 5 ! #define Anum_pg_database_datistemplate 6 ! #define Anum_pg_database_datallowconn 7 ! #define Anum_pg_database_datconnlimit 8 ! #define Anum_pg_database_datlastsysoid 9 ! #define Anum_pg_database_datfrozenxid 10 ! #define Anum_pg_database_datminmxid 11 ! #define Anum_pg_database_dattablespace 12 ! #define Anum_pg_database_datacl 13 ! DATA(insert OID = 1 ( template1 PGUID ENCODING "LC_COLLATE" "LC_CTYPE" t t -1 0 0 1 1663 _null_)); SHDESCR("default template for new databases"); #define TemplateDbOid 1 --- 60,82 ---- * compiler constants for pg_database * ---------------- */ ! #define Natts_pg_database 14 ! #define Anum_pg_database_datname 1 ! #define Anum_pg_database_datdba 2 ! #define Anum_pg_database_encoding 3 ! #define Anum_pg_database_datcollate 4 ! #define Anum_pg_database_datctype 5 ! #define Anum_pg_database_datistemplate 6 ! #define Anum_pg_database_datallowconn 7 ! #define Anum_pg_database_datconnlimit 8 ! #define Anum_pg_database_datlastsysoid 9 ! #define Anum_pg_database_datfrozenxid 10 ! #define Anum_pg_database_datminmxid 11 ! #define Anum_pg_database_dattablespace 12 ! #define Anum_pg_database_datcatalogsecurity 13 ! #define Anum_pg_database_datacl 14 ! DATA(insert OID = 1 ( template1 PGUID ENCODING "LC_COLLATE" "LC_CTYPE" t t -1 0 0 1 1663 f _null_)); SHDESCR("default template for new databases"); #define TemplateDbOid 1 *** a/src/include/catalog/pg_proc.h --- b/src/include/catalog/pg_proc.h *************** *** 3642,3647 **** DESCR("current user privilege on role by role name"); --- 3642,3665 ---- DATA(insert OID = 2710 ( pg_has_role PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 16 "26 25" _null_ _null_ _null_ _null_ _null_ pg_has_role_id _null_ _null_ _null_ )); DESCR("current user privilege on role by role oid"); + DATA(insert OID = 3315 (has_cast_privilege PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 16 "26 25" _null_ _null_ _null_ _null_ _null_ has_cast_privilege_id _null_ _null_ _null_)); + DESCR("current user privilege on cast by cast oid"); + + DATA(insert OID = 3316 (has_constraint_privilege PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 16 "26 25" _null_ _null_ _null_ _null_ _null_ has_constraint_privilege_id _null_ _null_ _null_)); + DESCR("current user privilege on contrainst by constraint oid"); + + DATA(insert OID = 3317 (has_column_default_privilege PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 16 "26 25" _null_ _null_ _null_ _null_ _null_ has_column_default_privilege_id _null_ _null_ _null_)); + DESCR("current user privilege on attrdefault by attrdefault oid"); + + DATA(insert OID = 3318 (has_policy_privilege PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 16 "26 25" _null_ _null_ _null_ _null_ _null_ has_policy_privilege_id _null_ _null_ _null_)); + DESCR("current user privilege on policy by policy oid"); + + DATA(insert OID = 3319 (has_trigger_privilege PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 16 "26 25" _null_ _null_ _null_ _null_ _null_ has_trigger_privilege_id _null_ _null_ _null_)); + DESCR("current user privilege on trigger by trigger oid"); + + DATA(insert OID = 3320 (has_user_mapping_privilege PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 16 "26 25" _null_ _null_ _null_ _null_ _null_ has_user_mapping_privilege_id _null_ _null_ _null_)); + DESCR("current user privilege on user mapping by user_mapping oid"); + DATA(insert OID = 1269 ( pg_column_size PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 23 "2276" _null_ _null_ _null_ _null_ _null_ pg_column_size _null_ _null_ _null_ )); DESCR("bytes required to store the value, perhaps with compression"); DATA(insert OID = 2322 ( pg_tablespace_size PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 20 "26" _null_ _null_ _null_ _null_ _null_ pg_tablespace_size_oid _null_ _null_ _null_ )); *** a/src/include/commands/policy.h --- b/src/include/commands/policy.h *************** *** 33,36 **** extern ObjectAddress rename_policy(RenameStmt *stmt); --- 33,39 ---- extern bool relation_has_policies(Relation rel); + extern void CreateCatalogPolicy(void); + extern void RemoveCatalogPolicy(void); + #endif /* POLICY_H */ *** a/src/include/utils/builtins.h --- b/src/include/utils/builtins.h *************** *** 106,111 **** extern Datum pg_has_role_id_name(PG_FUNCTION_ARGS); --- 106,117 ---- extern Datum pg_has_role_id_id(PG_FUNCTION_ARGS); extern Datum pg_has_role_name(PG_FUNCTION_ARGS); extern Datum pg_has_role_id(PG_FUNCTION_ARGS); + extern Datum has_cast_privilege_id(PG_FUNCTION_ARGS); + extern Datum has_constraint_privilege_id(PG_FUNCTION_ARGS); + extern Datum has_column_default_privilege_id(PG_FUNCTION_ARGS); + extern Datum has_policy_privilege_id(PG_FUNCTION_ARGS); + extern Datum has_trigger_privilege_id(PG_FUNCTION_ARGS); + extern Datum has_user_mapping_privilege_id(PG_FUNCTION_ARGS); /* bool.c */ extern Datum boolin(PG_FUNCTION_ARGS); *** /dev/null --- b/src/test/regress/expected/multitenancy.out *************** *** 0 **** --- 1,358 ---- + -- Create roles that are used by the following tests + create role tenancy_user1 login createdb; + create role tenancy_user2 login createdb; + create schema tenancy_user1; + grant all on schema tenancy_user1 to tenancy_user1; + create schema tenancy_user2; + grant all on schema tenancy_user2 to tenancy_user2; + alter database regression with catalog security = true; + -- create objects realted to tenacy_user1 + SET SESSION ROLE tenancy_user1; + create table public.tenancy_user1_tbl1 (tenancy_user1_tbl1_column1 serial, + tenancy_user1_tbl1_column2 char(10) default 'FUJITSU', + tenancy_user1_tbl1_column3 int); + create index tenancy_user1_tbl1_idx on tenancy_user1_tbl1 (tenancy_user1_tbl1_column3); + insert into tenancy_user1_tbl1(tenancy_user1_tbl1_column3) values(1); + create table tenancy_user1.tenancy_user1_tbl2 (tenancy_user1_tbl2_column1 serial, + tenancy_user1_tbl2_column2 char(10) default 'FUJITSU', + tenancy_user1_tbl2_column3 int); + create index tenancy_user1_tbl2_idx on tenancy_user1.tenancy_user1_tbl2 (tenancy_user1_tbl2_column3); + create view public.tenancy_user1_view1 as select * from tenancy_user1_tbl1; + create materialized view public.tenancy_user1_matview1 as select * from tenancy_user1_tbl1; + select * from tenancy_user1_tbl1; + tenancy_user1_tbl1_column1 | tenancy_user1_tbl1_column2 | tenancy_user1_tbl1_column3 + ----------------------------+----------------------------+---------------------------- + 1 | FUJITSU | 1 + (1 row) + + select * from tenancy_user1_view1; + tenancy_user1_tbl1_column1 | tenancy_user1_tbl1_column2 | tenancy_user1_tbl1_column3 + ----------------------------+----------------------------+---------------------------- + 1 | FUJITSU | 1 + (1 row) + + select * from tenancy_user1_matview1; + tenancy_user1_tbl1_column1 | tenancy_user1_tbl1_column2 | tenancy_user1_tbl1_column3 + ----------------------------+----------------------------+---------------------------- + 1 | FUJITSU | 1 + (1 row) + + + -- verify all system catalogs related to the objects created by tenancy_user1 + select relname from pg_class where relname = 'tenancy_user1_tbl1'; + relname + -------------------- + tenancy_user1_tbl1 + (1 row) + + select relname from pg_class where relname = 'tenancy_user1_tbl1_idx'; + relname + ------------------------ + tenancy_user1_tbl1_idx + (1 row) + + select relname from pg_class where relname = 'tenancy_user1_view1'; + relname + --------------------- + tenancy_user1_view1 + (1 row) + + select relname from pg_class where relname = 'tenancy_user1_matview1'; + relname + ------------------------ + tenancy_user1_matview1 + (1 row) + + select attname from pg_attribute where attname = 'tenancy_user1_tbl1_column1'; + attname + ---------------------------- + tenancy_user1_tbl1_column1 + tenancy_user1_tbl1_column1 + tenancy_user1_tbl1_column1 + (3 rows) + + select adnum, adsrc from pg_attrdef where adrelid in (select oid from pg_class where relname = 'tenancy_user1_tbl1'); + adnum | adsrc + -------+------------------------------------------------------------------------ + 1 | nextval('tenancy_user1_tbl1_tenancy_user1_tbl1_column1_seq'::regclass) + 2 | 'FUJITSU'::bpchar + (2 rows) + + select indnatts, indkey from pg_index where indrelid in (select oid from pg_class where relname = 'tenancy_user1_tbl1'); + indnatts | indkey + ----------+-------- + 1 | 3 + (1 row) + + select nspname, nspowner from pg_namespace where nspname = 'tenancy_user1'; + nspname | nspowner + ---------------+---------- + tenancy_user1 | 10 + (1 row) + + + -- verify all system views related to the objects created by tenancy_user1 + select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user1_tbl1'; + schemaname | relname + ------------+-------------------- + public | tenancy_user1_tbl1 + (1 row) + + select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user1_tbl2' and schemaname = 'tenancy_user1'; + schemaname | relname + ---------------+-------------------- + tenancy_user1 | tenancy_user1_tbl2 + (1 row) + + + RESET ROLE; + -- create objects realted to tenacy_user2 + SET SESSION ROLE tenancy_user2; + create table public.tenancy_user2_tbl1 (tenancy_user2_tbl1_column1 serial, + tenancy_user2_tbl1_column2 char(10) default 'FUJITSU', + tenancy_user2_tbl1_column3 int); + create index tenancy_user2_tbl1_idx on tenancy_user2_tbl1 (tenancy_user2_tbl1_column3); + insert into tenancy_user2_tbl1(tenancy_user2_tbl1_column3) values(1); + create table tenancy_user2.tenancy_user2_tbl2 (tenancy_user2_tbl2_column1 serial, + tenancy_user2_tbl2_column2 char(10) default 'FUJITSU', + tenancy_user2_tbl2_column3 int); + create index tenancy_user2_tbl2_idx on tenancy_user2.tenancy_user2_tbl2 (tenancy_user2_tbl2_column3); + create view public.tenancy_user2_view1 as select * from tenancy_user2_tbl1; + create materialized view public.tenancy_user2_matview1 as select * from tenancy_user2_tbl1; + select * from tenancy_user2_tbl1; + tenancy_user2_tbl1_column1 | tenancy_user2_tbl1_column2 | tenancy_user2_tbl1_column3 + ----------------------------+----------------------------+---------------------------- + 1 | FUJITSU | 1 + (1 row) + + select * from tenancy_user2_view1; + tenancy_user2_tbl1_column1 | tenancy_user2_tbl1_column2 | tenancy_user2_tbl1_column3 + ----------------------------+----------------------------+---------------------------- + 1 | FUJITSU | 1 + (1 row) + + select * from tenancy_user2_matview1; + tenancy_user2_tbl1_column1 | tenancy_user2_tbl1_column2 | tenancy_user2_tbl1_column3 + ----------------------------+----------------------------+---------------------------- + 1 | FUJITSU | 1 + (1 row) + + -- verify all system catalogs related to the objects created by tenancy_user2 + select relname from pg_class where relname = 'tenancy_user2_tbl1'; + relname + -------------------- + tenancy_user2_tbl1 + (1 row) + + select relname from pg_class where relname = 'tenancy_user2_tbl1_idx'; + relname + ------------------------ + tenancy_user2_tbl1_idx + (1 row) + + select relname from pg_class where relname = 'tenancy_user2_view1'; + relname + --------------------- + tenancy_user2_view1 + (1 row) + + select relname from pg_class where relname = 'tenancy_user2_matview1'; + relname + ------------------------ + tenancy_user2_matview1 + (1 row) + + select attname from pg_attribute where attname = 'tenancy_user2_tbl1_column1'; + attname + ---------------------------- + tenancy_user2_tbl1_column1 + tenancy_user2_tbl1_column1 + tenancy_user2_tbl1_column1 + (3 rows) + + select adnum, adsrc from pg_attrdef where adrelid in (select oid from pg_class where relname = 'tenancy_user2_tbl1'); + adnum | adsrc + -------+------------------------------------------------------------------------ + 1 | nextval('tenancy_user2_tbl1_tenancy_user2_tbl1_column1_seq'::regclass) + 2 | 'FUJITSU'::bpchar + (2 rows) + + select indnatts, indkey from pg_index where indrelid in (select oid from pg_class where relname = 'tenancy_user2_tbl1'); + indnatts | indkey + ----------+-------- + 1 | 3 + (1 row) + + select nspname, nspowner from pg_namespace where nspname = 'tenancy_user2'; + nspname | nspowner + ---------------+---------- + tenancy_user2 | 10 + (1 row) + + + -- verify all system views related to the objects created by tenancy_user1 + select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user2_tbl1'; + schemaname | relname + ------------+-------------------- + public | tenancy_user2_tbl1 + (1 row) + + select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user2_tbl2' and schemaname = 'tenancy_user2'; + schemaname | relname + ---------------+-------------------- + tenancy_user2 | tenancy_user2_tbl2 + (1 row) + + RESET ROLE; + -- Try to get the objects created by tenancy_user2 from tenancy_user1 + SET SESSION ROLE tenancy_user1; + select * from tenancy_user2_tbl1; + ERROR: permission denied for relation tenancy_user2_tbl1 + select * from tenancy_user2_view1; + ERROR: permission denied for relation tenancy_user2_view1 + select * from tenancy_user2_matview1; + ERROR: permission denied for relation tenancy_user2_matview1 + select * from tenancy_user2.tenancy_user2_tbl2; + ERROR: permission denied for schema tenancy_user2 + LINE 1: select * from tenancy_user2.tenancy_user2_tbl2; + ^ + -- verify all system catalogs related to the objects created by tenancy_user2 + select relname from pg_class where relname = 'tenancy_user2_tbl1'; + relname + --------- + (0 rows) + + select relname from pg_class where relname = 'tenancy_user2_tbl1_idx'; + relname + --------- + (0 rows) + + select relname from pg_class where relname = 'tenancy_user2_view1'; + relname + --------- + (0 rows) + + select relname from pg_class where relname = 'tenancy_user2_matview1'; + relname + --------- + (0 rows) + + select attname from pg_attribute where attname = 'tenancy_user2_tbl1_column1'; + attname + --------- + (0 rows) + + select adnum, adsrc from pg_attrdef where adrelid in (select oid from pg_class where relname = 'tenancy_user2_tbl1'); + adnum | adsrc + -------+------- + (0 rows) + + select indnatts, indkey from pg_index where indrelid in (select oid from pg_class where relname = 'tenancy_user2_tbl1'); + indnatts | indkey + ----------+-------- + (0 rows) + + select nspname, nspowner from pg_namespace where nspname = 'tenancy_user2'; + nspname | nspowner + ---------+---------- + (0 rows) + + + -- verify all system views related to the objects created by tenancy_user1 + select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user2_tbl1'; + schemaname | relname + ------------+--------- + (0 rows) + + select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user2_tbl2' and schemaname = 'tenancy_user2'; + schemaname | relname + ------------+--------- + (0 rows) + + RESET ROLE; + -- Try to get the objects created by tenancy_user1 from tenancy_user2 + SET SESSION ROLE tenancy_user2; + select * from tenancy_user1_tbl1; + ERROR: permission denied for relation tenancy_user1_tbl1 + select * from tenancy_user1_view1; + ERROR: permission denied for relation tenancy_user1_view1 + select * from tenancy_user1_matview1; + ERROR: permission denied for relation tenancy_user1_matview1 + select * from tenancy_user1.tenancy_user1_tbl2; + ERROR: permission denied for schema tenancy_user1 + LINE 1: select * from tenancy_user1.tenancy_user1_tbl2; + ^ + -- verify all system catalogs related to the objects created by tenancy_user1 + select relname from pg_class where relname = 'tenancy_user1_tbl1'; + relname + --------- + (0 rows) + + select relname from pg_class where relname = 'tenancy_user1_tbl1_idx'; + relname + --------- + (0 rows) + + select relname from pg_class where relname = 'tenancy_user1_view1'; + relname + --------- + (0 rows) + + select relname from pg_class where relname = 'tenancy_user1_matview1'; + relname + --------- + (0 rows) + + select attname from pg_attribute where attname = 'tenancy_user1_tbl1_column1'; + attname + --------- + (0 rows) + + select adnum, adsrc from pg_attrdef where adrelid in (select oid from pg_class where relname = 'tenancy_user1_tbl1'); + adnum | adsrc + -------+------- + (0 rows) + + select indnatts, indkey from pg_index where indrelid in (select oid from pg_class where relname = 'tenancy_user1_tbl1'); + indnatts | indkey + ----------+-------- + (0 rows) + + select nspname, nspowner from pg_namespace where nspname = 'tenancy_user1'; + nspname | nspowner + ---------+---------- + (0 rows) + + + -- verify all system views related to the objects created by tenancy_user1 + select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user1_tbl1'; + schemaname | relname + ------------+--------- + (0 rows) + + select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user1_tbl2' and schemaname = 'tenancy_user1'; + schemaname | relname + ------------+--------- + (0 rows) + + RESET ROLE; + -- Delete the roles and it's associated objects. + SET SESSION ROLE tenancy_user1; + DROP TABLE tenancy_user1_tbl1 cascade; + NOTICE: drop cascades to 2 other objects + DETAIL: drop cascades to view tenancy_user1_view1 + drop cascades to materialized view tenancy_user1_matview1 + DROP TABLE tenancy_user1_tbl2 cascade; + RESET ROLE; + SET SESSION ROLE tenancy_user2; + DROP TABLE tenancy_user2_tbl1 cascade; + NOTICE: drop cascades to 2 other objects + DETAIL: drop cascades to view tenancy_user2_view1 + drop cascades to materialized view tenancy_user2_matview1 + DROP TABLE tenancy_user2_tbl2 cascade; + RESET ROLE; + alter database regression with catalog security = false; + drop schema tenancy_user1; + drop schema tenancy_user2; + drop role tenancy_user1; + drop role tenancy_user2; *** a/src/test/regress/parallel_schedule --- b/src/test/regress/parallel_schedule *************** *** 60,66 **** test: create_index create_view # ---------- # Another group of parallel tests # ---------- ! test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes # ---------- # sanity_check does a vacuum, affecting the sort order of SELECT * --- 60,66 ---- # ---------- # Another group of parallel tests # ---------- ! test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes multitenancy # ---------- # sanity_check does a vacuum, affecting the sort order of SELECT * *** a/src/test/regress/serial_schedule --- b/src/test/regress/serial_schedule *************** *** 158,161 **** test: largeobject --- 158,162 ---- test: with test: xml test: event_trigger + test: multitenancy test: stats *** /dev/null --- b/src/test/regress/sql/multitenancy.sql *************** *** 0 **** --- 1,169 ---- + -- Create roles that are used by the following tests + create role tenancy_user1 login createdb; + create role tenancy_user2 login createdb; + + create schema tenancy_user1; + grant all on schema tenancy_user1 to tenancy_user1; + + create schema tenancy_user2; + grant all on schema tenancy_user2 to tenancy_user2; + + alter database regression with catalog security = true; + + -- create objects realted to tenacy_user1 + SET SESSION ROLE tenancy_user1; + + create table public.tenancy_user1_tbl1 (tenancy_user1_tbl1_column1 serial, + tenancy_user1_tbl1_column2 char(10) default 'FUJITSU', + tenancy_user1_tbl1_column3 int); + create index tenancy_user1_tbl1_idx on tenancy_user1_tbl1 (tenancy_user1_tbl1_column3); + + insert into tenancy_user1_tbl1(tenancy_user1_tbl1_column3) values(1); + + create table tenancy_user1.tenancy_user1_tbl2 (tenancy_user1_tbl2_column1 serial, + tenancy_user1_tbl2_column2 char(10) default 'FUJITSU', + tenancy_user1_tbl2_column3 int); + create index tenancy_user1_tbl2_idx on tenancy_user1.tenancy_user1_tbl2 (tenancy_user1_tbl2_column3); + + + create view public.tenancy_user1_view1 as select * from tenancy_user1_tbl1; + create materialized view public.tenancy_user1_matview1 as select * from tenancy_user1_tbl1; + + select * from tenancy_user1_tbl1; + select * from tenancy_user1_view1; + select * from tenancy_user1_matview1; + + + -- verify all system catalogs related to the objects created by tenancy_user1 + select relname from pg_class where relname = 'tenancy_user1_tbl1'; + select relname from pg_class where relname = 'tenancy_user1_tbl1_idx'; + select relname from pg_class where relname = 'tenancy_user1_view1'; + select relname from pg_class where relname = 'tenancy_user1_matview1'; + + select attname from pg_attribute where attname = 'tenancy_user1_tbl1_column1'; + select adnum, adsrc from pg_attrdef where adrelid in (select oid from pg_class where relname = 'tenancy_user1_tbl1'); + select indnatts, indkey from pg_index where indrelid in (select oid from pg_class where relname = 'tenancy_user1_tbl1'); + select nspname, nspowner from pg_namespace where nspname = 'tenancy_user1'; + + -- verify all system views related to the objects created by tenancy_user1 + select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user1_tbl1'; + select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user1_tbl2' and schemaname = 'tenancy_user1'; + + + RESET ROLE; + + + -- create objects realted to tenacy_user2 + SET SESSION ROLE tenancy_user2; + + create table public.tenancy_user2_tbl1 (tenancy_user2_tbl1_column1 serial, + tenancy_user2_tbl1_column2 char(10) default 'FUJITSU', + tenancy_user2_tbl1_column3 int); + create index tenancy_user2_tbl1_idx on tenancy_user2_tbl1 (tenancy_user2_tbl1_column3); + + insert into tenancy_user2_tbl1(tenancy_user2_tbl1_column3) values(1); + + create table tenancy_user2.tenancy_user2_tbl2 (tenancy_user2_tbl2_column1 serial, + tenancy_user2_tbl2_column2 char(10) default 'FUJITSU', + tenancy_user2_tbl2_column3 int); + create index tenancy_user2_tbl2_idx on tenancy_user2.tenancy_user2_tbl2 (tenancy_user2_tbl2_column3); + + create view public.tenancy_user2_view1 as select * from tenancy_user2_tbl1; + create materialized view public.tenancy_user2_matview1 as select * from tenancy_user2_tbl1; + + select * from tenancy_user2_tbl1; + select * from tenancy_user2_view1; + select * from tenancy_user2_matview1; + + -- verify all system catalogs related to the objects created by tenancy_user2 + select relname from pg_class where relname = 'tenancy_user2_tbl1'; + select relname from pg_class where relname = 'tenancy_user2_tbl1_idx'; + select relname from pg_class where relname = 'tenancy_user2_view1'; + select relname from pg_class where relname = 'tenancy_user2_matview1'; + + select attname from pg_attribute where attname = 'tenancy_user2_tbl1_column1'; + select adnum, adsrc from pg_attrdef where adrelid in (select oid from pg_class where relname = 'tenancy_user2_tbl1'); + select indnatts, indkey from pg_index where indrelid in (select oid from pg_class where relname = 'tenancy_user2_tbl1'); + select nspname, nspowner from pg_namespace where nspname = 'tenancy_user2'; + + -- verify all system views related to the objects created by tenancy_user1 + select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user2_tbl1'; + select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user2_tbl2' and schemaname = 'tenancy_user2'; + + RESET ROLE; + + -- Try to get the objects created by tenancy_user2 from tenancy_user1 + SET SESSION ROLE tenancy_user1; + + select * from tenancy_user2_tbl1; + select * from tenancy_user2_view1; + select * from tenancy_user2_matview1; + + select * from tenancy_user2.tenancy_user2_tbl2; + + -- verify all system catalogs related to the objects created by tenancy_user2 + select relname from pg_class where relname = 'tenancy_user2_tbl1'; + select relname from pg_class where relname = 'tenancy_user2_tbl1_idx'; + select relname from pg_class where relname = 'tenancy_user2_view1'; + select relname from pg_class where relname = 'tenancy_user2_matview1'; + + select attname from pg_attribute where attname = 'tenancy_user2_tbl1_column1'; + select adnum, adsrc from pg_attrdef where adrelid in (select oid from pg_class where relname = 'tenancy_user2_tbl1'); + select indnatts, indkey from pg_index where indrelid in (select oid from pg_class where relname = 'tenancy_user2_tbl1'); + select nspname, nspowner from pg_namespace where nspname = 'tenancy_user2'; + + -- verify all system views related to the objects created by tenancy_user1 + select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user2_tbl1'; + select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user2_tbl2' and schemaname = 'tenancy_user2'; + + RESET ROLE; + + + -- Try to get the objects created by tenancy_user1 from tenancy_user2 + SET SESSION ROLE tenancy_user2; + + select * from tenancy_user1_tbl1; + select * from tenancy_user1_view1; + select * from tenancy_user1_matview1; + + select * from tenancy_user1.tenancy_user1_tbl2; + + -- verify all system catalogs related to the objects created by tenancy_user1 + select relname from pg_class where relname = 'tenancy_user1_tbl1'; + select relname from pg_class where relname = 'tenancy_user1_tbl1_idx'; + select relname from pg_class where relname = 'tenancy_user1_view1'; + select relname from pg_class where relname = 'tenancy_user1_matview1'; + + select attname from pg_attribute where attname = 'tenancy_user1_tbl1_column1'; + select adnum, adsrc from pg_attrdef where adrelid in (select oid from pg_class where relname = 'tenancy_user1_tbl1'); + select indnatts, indkey from pg_index where indrelid in (select oid from pg_class where relname = 'tenancy_user1_tbl1'); + select nspname, nspowner from pg_namespace where nspname = 'tenancy_user1'; + + -- verify all system views related to the objects created by tenancy_user1 + select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user1_tbl1'; + select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user1_tbl2' and schemaname = 'tenancy_user1'; + + RESET ROLE; + + -- Delete the roles and it's associated objects. + SET SESSION ROLE tenancy_user1; + + DROP TABLE tenancy_user1_tbl1 cascade; + DROP TABLE tenancy_user1_tbl2 cascade; + + RESET ROLE; + + SET SESSION ROLE tenancy_user2; + + DROP TABLE tenancy_user2_tbl1 cascade; + DROP TABLE tenancy_user2_tbl2 cascade; + + RESET ROLE; + + alter database regression with catalog security = false; + + drop schema tenancy_user1; + drop schema tenancy_user2; + + drop role tenancy_user1; + drop role tenancy_user2;