*** a/doc/src/sgml/ref/alter_database.sgml
--- b/doc/src/sgml/ref/alter_database.sgml
***************
*** 28,33 **** ALTER DATABASE name [ [ WITH ] allowconn
CONNECTION LIMIT connlimit
IS_TEMPLATE istemplate
+ CATALOG SECURITY catalog_security
ALTER DATABASE name RENAME TO new_name
***************
*** 139,144 **** ALTER DATABASE name RESET ALL
--- 140,157 ----
+
+
+ catalog_security
+
+
+ If true, the row level security policies on system catalog tables are
+ applied to this database. These policies are used to provide multi-tenancy
+ under that database. If false, all the policies that are present on the
+ database are removed.
+
+
+
new_name
***************
*** 203,208 **** ALTER DATABASE name RESET ALL
--- 216,227 ----
Role-specific settings override database-specific
ones if there is a conflict.
+
+
+ The catalog_security option is not supported at
+ command. This is because of the limitation of creating row level security policies.
+ The policies can only be created on that database that is connected by the session.
+
*** 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 65535
+
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 is in progress or not? */
+ bool policyRelcacheBuiltInProgress = false;
+
/*
* Callback to RangeVarGetRelidExtended().
*
***************
*** 195,200 **** RelationBuildRowSecurity(Relation relation)
--- 248,263 ----
RowSecurityDesc *volatile rsdesc = NULL;
/*
+ * Build the row security descriptor of a relation, once all
+ * the critical relations are built.
+ */
+ if (!criticalRelcachesBuilt || !criticalSharedRelcachesBuilt)
+ return;
+
+ if (relation->rd_id == PolicyRelationId && policyRelcacheBuiltInProgress)
+ return;
+
+ /*
* Create a memory context to hold everything associated with this
* relation's row security policy. This makes it easy to clean up during
* a relcache flush.
***************
*** 216,221 **** RelationBuildRowSecurity(Relation relation)
--- 279,290 ----
SysScanDesc sscan;
HeapTuple tuple;
+ if (relation->rd_id == PolicyRelationId)
+ {
+ Assert(policyRelcacheBuiltInProgress == false);
+ policyRelcacheBuiltInProgress = true;
+ }
+
rsdesc = MemoryContextAllocZero(rscxt, sizeof(RowSecurityDesc));
rsdesc->rscxt = rscxt;
***************
*** 325,334 **** RelationBuildRowSecurity(Relation relation)
--- 394,416 ----
/* Delete rscxt, first making sure it isn't active */
MemoryContextSwitchTo(oldcxt);
MemoryContextDelete(rscxt);
+
+ if (relation->rd_id == PolicyRelationId)
+ {
+ Assert(policyRelcacheBuiltInProgress == true);
+ policyRelcacheBuiltInProgress = false;
+ }
+
PG_RE_THROW();
}
PG_END_TRY();
+ if (relation->rd_id == PolicyRelationId)
+ {
+ Assert(policyRelcacheBuiltInProgress == true);
+ policyRelcacheBuiltInProgress = false;
+ }
+
/* Success --- attach the policy descriptor to the relcache entry */
relation->rd_rsdesc = rsdesc;
}
***************
*** 407,412 **** RemovePolicyById(Oid policy_id)
--- 489,1041 ----
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 = (select oid from pg_class where relname = 'pg_aggregate'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_cast') AND has_cast_privilege(objid, 'any'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_collation'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_attribute') AND has_column_privilege(objid, objsubid::smallint, 'any'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_constraint') AND has_constraint_privilege(objid, 'any'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_conversion'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_type') AND has_type_privilege(objid, 'any'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_extension'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_event_trigger'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_foreign_data_wrapper') AND has_foreign_data_wrapper_privilege(objid, 'any'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_class') AND has_table_privilege(objid,'any'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_proc') AND has_function_privilege(objid, 'any'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_am'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_amop'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_amproc'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_attdef') AND has_column_default_privilege(objid, 'any'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_language') AND has_language_privilege(objid, 'any'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_largeobject'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_opclass'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_operator'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_opfamily'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_enum'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_event_trigger'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_extension'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_policy') AND has_policy_privilege(objid, 'any'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_rewrite'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_namespace') AND has_schema_privilege(objid, 'any'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_foreign_server') AND has_server_privilege(objid, 'any'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_tablespace') AND has_tablespace_privilege(objid, 'any'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_ts_dict'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_ts_parser'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_ts_config'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_ts_template'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_transform'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_trigger') AND has_trigger_privilege(objid, 'any'))"
+ " OR (classid = (select oid from pg_class where relname = 'pg_user_mapping') 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 = (select oid from pg_class where relname = 'pg_aggregate'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_cast') AND has_cast_privilege(objoid, 'any'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_collation'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_attribute') AND has_column_privilege(objoid, objsubid::smallint, 'any'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_constraint') AND has_constraint_privilege(objoid, 'any'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_conversion'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_type') AND has_type_privilege(objoid, 'any'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_extension'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_event_trigger'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_foreign_data_wrapper') AND has_foreign_data_wrapper_privilege(objoid, 'any'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_class') AND has_table_privilege(objoid,'any'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_proc') AND has_function_privilege(objoid, 'any'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_largeobject'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_opclass'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_operator'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_opfamily'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_largeobject_metadata'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_policy') AND has_policy_privilege(objoid, 'any'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_rewrite'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_namespace') AND has_schema_privilege(objoid, 'any'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_foreign_server') AND has_server_privilege(objoid, 'any'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_tablespace') AND has_tablespace_privilege(objoid, 'any'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_range'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_ts_dict'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_ts_parser'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_ts_config'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_ts_template'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_transform'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_trigger') 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:
+ /*
+ * Only user with bypass rls or owner of the table can view the
+ * policies on the table, unless the forcesecurity is specified
+ * for the owners also.
+ */
+ sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ " (false)",
+ 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 = (select oid from pg_class where relname = 'pg_aggregate'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_attribute') AND has_column_privilege(objoid, objsubid::smallint, 'any'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_type') AND has_type_privilege(objoid, 'any'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_class') AND has_table_privilege(objoid,'any'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_proc') AND has_function_privilege(objoid, 'any'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_language') AND has_language_privilege(objoid, 'any'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_largeobject'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_event_trigger'))"
+ " OR (classoid = (select oid from pg_class where relname = 'pg_tablespace') 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
***************
*** 8871,8876 **** createdb_opt_name:
--- 8871,8877 ----
| 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,657 ----
+ -- 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;
+ -- Create a type to test
+ CREATE TYPE tenancytesttype;
+ CREATE FUNCTION tenancytesttype_in(cstring)
+ RETURNS tenancytesttype
+ AS 'textin'
+ LANGUAGE internal STRICT IMMUTABLE;
+ NOTICE: return type tenancytesttype is only a shell
+ CREATE FUNCTION tenancytesttype_out(tenancytesttype)
+ RETURNS cstring
+ AS 'textout'
+ LANGUAGE internal STRICT IMMUTABLE;
+ NOTICE: argument type tenancytesttype is only a shell
+ CREATE TYPE tenancytesttype (
+ internallength = variable,
+ input = tenancytesttype_in,
+ output = tenancytesttype_out,
+ alignment = int4
+ );
+ COMMENT ON TYPE tenancytesttype IS 'tenancytesttype';
+ REVOKE ALL ON FUNCTION tenancytesttype_in(cstring) FROM public;
+ REVOKE ALL ON FUNCTION tenancytesttype_out(tenancytesttype) FROM public;
+ GRANT ALL ON FUNCTION tenancytesttype_in(cstring) TO tenancy_user2;
+ GRANT ALL ON FUNCTION tenancytesttype_out(tenancytesttype) TO tenancy_user2;
+ REVOKE ALL ON TYPE tenancytesttype FROM public;
+ GRANT ALL ON TYPE tenancytesttype TO tenancy_user2;
+ -- Temp Language create with owner as tenancy_user2
+ CREATE TRUSTED LANGUAGE tenancy_lang1 HANDLER plpgsql_call_handler;
+ REVOKE ALL ON LANGUAGE tenancy_lang1 FROM public;
+ ALTER LANGUAGE tenancy_lang1 OWNER TO tenancy_user2;
+ --Foriegn data wrapper, server and user mapping creation for user tenancy_user2
+ CREATE FOREIGN DATA WRAPPER tenancy_wrapper;
+ CREATE SERVER tenancy_server FOREIGN DATA WRAPPER tenancy_wrapper;
+ CREATE USER MAPPING FOR tenancy_user2 SERVER tenancy_server;
+ REVOKE ALL ON FOREIGN DATA WRAPPER tenancy_wrapper FROM public;
+ REVOKE ALL ON FOREIGN SERVER tenancy_server FROM public;
+ GRANT ALL ON FOREIGN DATA WRAPPER tenancy_wrapper To tenancy_user2;
+ GRANT ALL ON FOREIGN SERVER tenancy_server 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);
+ ALTER TABLE tenancy_user1.tenancy_user1_tbl2 ADD CONSTRAINT tenancy_user1_tbl2_constraint UNIQUE (tenancy_user1_tbl2_column3);
+ CREATE FUNCTION tenancy_user1_trigger_func() RETURNS trigger LANGUAGE plpgsql AS '
+ BEGIN
+ RAISE NOTICE ''tenancy_user1_trigger_func(%) called: action = %, when = %, level = %'', TG_ARGV[0], TG_OP, TG_WHEN, TG_LEVEL;
+ RETURN NULL;
+ END;';
+ CREATE TRIGGER tenancy_user1_tbl1_before_ins_stmt_trig BEFORE INSERT ON tenancy_user1_tbl1
+ FOR EACH STATEMENT EXECUTE PROCEDURE tenancy_user1_trigger_func('before_ins_stmt');
+ CREATE TABLE tenancy_user1_main_table (aa TEXT);
+ CREATE TABLE tenancy_user1_child_table (bb TEXT) INHERITS (tenancy_user1_main_table);
+ 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 relname from pg_class where relname = 'tenancy_user1_tbl1_tenancy_user1_tbl1_column1_seq';
+ relname
+ ---------------------------------------------------
+ tenancy_user1_tbl1_tenancy_user1_tbl1_column1_seq
+ (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)
+
+ select conname from pg_constraint where conname = 'tenancy_user1_tbl2_constraint';
+ conname
+ -------------------------------
+ tenancy_user1_tbl2_constraint
+ (1 row)
+
+ select classid::regclass, objid::regclass, deptype from pg_depend
+ where classid = (select oid from pg_class where relname = 'pg_class')
+ and objid = (select oid from pg_class where relname = 'tenancy_user1_tbl2');
+ classid | objid | deptype
+ ----------+--------------------+---------
+ pg_class | tenancy_user1_tbl2 | n
+ (1 row)
+
+ select inhrelid::regclass, inhparent::regclass from pg_inherits
+ where inhrelid = (select oid from pg_class where relname = 'tenancy_user1_child_table');
+ inhrelid | inhparent
+ ---------------------------+--------------------------
+ tenancy_user1_child_table | tenancy_user1_main_table
+ (1 row)
+
+
+ --pg_class policy view, is shouldn't be visible to normal users
+ select polname from pg_policy where polrelid = (select oid from pg_class where relname = 'pg_class');
+ polname
+ ---------
+ (0 rows)
+
+ select tgname, tgtype from pg_trigger where tgname = 'tenancy_user1_tbl1_before_ins_stmt_trig';
+ tgname | tgtype
+ -----------------------------------------+--------
+ tenancy_user1_tbl1_before_ins_stmt_trig | 6
+ (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)
+
+ --works only when the shared catalog security is enabled
+ --select rolname, rolsuper from pg_roles where rolname like 'tenancy_user%';
+ select schemaname, tablename from pg_policies where tablename = 'pg_class';
+ schemaname | tablename
+ ------------+-----------
+ (0 rows)
+
+ select schemaname, viewname from pg_views where viewname = 'tenancy_user1_view1';
+ schemaname | viewname
+ ------------+---------------------
+ public | tenancy_user1_view1
+ (1 row)
+
+ select schemaname, tablename from pg_tables where tablename = 'tenancy_user1_tbl2';
+ schemaname | tablename
+ ---------------+--------------------
+ tenancy_user1 | tenancy_user1_tbl2
+ (1 row)
+
+ select schemaname, matviewname from pg_matviews where matviewname = 'tenancy_user1_matview1';
+ schemaname | matviewname
+ ------------+------------------------
+ public | tenancy_user1_matview1
+ (1 row)
+
+ select schemaname, tablename, indexname from pg_indexes where indexname = 'tenancy_user1_tbl1_idx';
+ schemaname | tablename | indexname
+ ------------+--------------------+------------------------
+ public | tenancy_user1_tbl1 | tenancy_user1_tbl1_idx
+ (1 row)
+
+ select schemaname, relname from pg_statio_all_tables where relname = 'tenancy_user1_tbl1';
+ schemaname | relname
+ ------------+--------------------
+ public | tenancy_user1_tbl1
+ (1 row)
+
+ select schemaname, relname, indexrelname from pg_stat_all_indexes where indexrelname = 'tenancy_user1_tbl1_idx';
+ schemaname | relname | indexrelname
+ ------------+--------------------+------------------------
+ public | tenancy_user1_tbl1 | tenancy_user1_tbl1_idx
+ (1 row)
+
+ --information_schema views
+ select * from information_schema.table_constraints where constraint_name = 'tenancy_user1_tbl2_constraint';
+ constraint_catalog | constraint_schema | constraint_name | table_catalog | table_schema | table_name | constraint_type | is_deferrable | initially_deferred
+ --------------------+-------------------+-------------------------------+---------------+---------------+--------------------+-----------------+---------------+--------------------
+ regression | tenancy_user1 | tenancy_user1_tbl2_constraint | regression | tenancy_user1 | tenancy_user1_tbl2 | UNIQUE | NO | NO
+ (1 row)
+
+ select catalog_name, schema_name from information_schema.schemata where schema_name = 'tenancy_user1';
+ catalog_name | schema_name
+ --------------+---------------
+ regression | tenancy_user1
+ (1 row)
+
+ select sequence_catalog, sequence_schema, sequence_name from information_schema.sequences
+ where sequence_name = 'tenancy_user1_tbl1_tenancy_user1_tbl1_column1_seq';
+ sequence_catalog | sequence_schema | sequence_name
+ ------------------+-----------------+---------------------------------------------------
+ regression | public | tenancy_user1_tbl1_tenancy_user1_tbl1_column1_seq
+ (1 row)
+
+ select * from information_schema.tables where table_name = 'tenancy_user1_tbl1';
+ table_catalog | table_schema | table_name | table_type | self_referencing_column_name | reference_generation | user_defined_type_catalog | user_defined_type_schema | user_defined_type_name | is_insertable_into | is_typed | commit_action
+ ---------------+--------------+--------------------+------------+------------------------------+----------------------+---------------------------+--------------------------+------------------------+--------------------+----------+---------------
+ regression | public | tenancy_user1_tbl1 | BASE TABLE | | | | | | YES | NO |
+ (1 row)
+
+ select * from information_schema.triggers where trigger_name = 'tenancy_user1_tbl1_before_ins_stmt_trig';
+ trigger_catalog | trigger_schema | trigger_name | event_manipulation | event_object_catalog | event_object_schema | event_object_table | action_order | action_condition | action_statement | action_orientation | action_timing | action_reference_old_table | action_reference_new_table | action_reference_old_row | action_reference_new_row | created
+ -----------------+----------------+-----------------------------------------+--------------------+----------------------+---------------------+--------------------+--------------+------------------+-----------------------------------------------------------------+--------------------+---------------+----------------------------+----------------------------+--------------------------+--------------------------+---------
+ regression | public | tenancy_user1_tbl1_before_ins_stmt_trig | INSERT | regression | public | tenancy_user1_tbl1 | | | EXECUTE PROCEDURE tenancy_user1_trigger_func('before_ins_stmt') | STATEMENT | BEFORE | | | | |
+ (1 row)
+
+ select * from information_schema.views where table_name = 'tenancy_user1_view1';
+ table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into
+ ---------------+--------------+---------------------+--------------------------------------------------------+--------------+--------------+--------------------+----------------------+----------------------+----------------------------
+ regression | public | tenancy_user1_view1 | SELECT tenancy_user1_tbl1.tenancy_user1_tbl1_column1,+| NONE | YES | YES | NO | NO | NO
+ | | | tenancy_user1_tbl1.tenancy_user1_tbl1_column2, +| | | | | |
+ | | | tenancy_user1_tbl1.tenancy_user1_tbl1_column3 +| | | | | |
+ | | | FROM tenancy_user1_tbl1; | | | | | |
+ (1 row)
+
+ RESET ROLE;
+ -- create objects realted to tenacy_user2
+ SET SESSION ROLE tenancy_user2;
+ CREATE FOREIGN TABLE tenancy_user2_ft1 (
+ tenancy_user2_c1 integer OPTIONS ("param 1" 'val1') NOT NULL,
+ tenancy_user2_c2 text OPTIONS (param2 'val2', param3 'val3') CHECK (tenancy_user2_c2 <> ''),
+ tenancy_user2_c3 date,
+ CHECK (tenancy_user2_c3 BETWEEN '1994-01-01'::date AND '1994-01-31'::date)
+ ) SERVER tenancy_server OPTIONS (delimiter ',', quote '"', "be quoted" 'value');
+ -- a dummy function to test the new type
+ CREATE FUNCTION tenancy_user2_tenancytestfunc(text) RETURNS int4 LANGUAGE SQL AS
+ $$ SELECT 1; $$;
+ SELECT tenancy_user2_tenancytestfunc('foo'::text);
+ tenancy_user2_tenancytestfunc
+ -------------------------------
+ 1
+ (1 row)
+
+ --verify system catalogs to view the details of type, function and cast.
+ select proname from pg_proc where proname = 'tenancy_user2_tenancytestfunc';
+ proname
+ -------------------------------
+ tenancy_user2_tenancytestfunc
+ (1 row)
+
+ select proname, pronamespace from pg_proc where proname = 'tenancytesttype_in';
+ proname | pronamespace
+ --------------------+--------------
+ tenancytesttype_in | 2200
+ (1 row)
+
+ select proname, pronamespace from pg_proc where proname = 'tenancytesttype_out';
+ proname | pronamespace
+ ---------------------+--------------
+ tenancytesttype_out | 2200
+ (1 row)
+
+ --select the description of casttesttype
+ select description from pg_description where classoid = (select oid from pg_class where relname = 'pg_type')
+ and objoid = (select oid from pg_type where typname = 'tenancytesttype');
+ description
+ -----------------
+ tenancytesttype
+ (1 row)
+
+
+ --select the language that is owned by the tenancy_user2
+ select lanname, lanispl from pg_language where lanname = 'tenancy_lang1';
+ lanname | lanispl
+ ---------------+---------
+ tenancy_lang1 | t
+ (1 row)
+
+ --Foriegn data wrapper, server and foreign table details
+ SELECT fdwname, fdwhandler::regproc, fdwvalidator::regproc, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3;
+ fdwname | fdwhandler | fdwvalidator | fdwoptions
+ -----------------+------------+--------------+------------
+ tenancy_wrapper | - | - |
+ (1 row)
+
+ SELECT srvname, srvoptions FROM pg_foreign_server;
+ srvname | srvoptions
+ ----------------+------------
+ tenancy_server |
+ (1 row)
+
+ Select srvname FROM pg_user_mappings where srvname = 'tenancy_server';
+ srvname
+ ----------------
+ tenancy_server
+ (1 row)
+
+ SELECT relname, relkind from pg_class where relname = 'tenancy_user2_ft1';
+ relname | relkind
+ -------------------+---------
+ tenancy_user2_ft1 | f
+ (1 row)
+
+
+ --information_schema views
+ select foreign_data_wrapper_catalog, foreign_data_wrapper_name from information_schema.foreign_data_wrappers
+ where foreign_data_wrapper_name = 'tenancy_wrapper';
+ foreign_data_wrapper_catalog | foreign_data_wrapper_name
+ ------------------------------+---------------------------
+ regression | tenancy_wrapper
+ (1 row)
+
+ select foreign_server_catalog, foreign_server_name, foreign_data_wrapper_catalog, foreign_data_wrapper_name
+ from information_schema.foreign_servers where foreign_server_name = 'tenancy_server';
+ foreign_server_catalog | foreign_server_name | foreign_data_wrapper_catalog | foreign_data_wrapper_name
+ ------------------------+---------------------+------------------------------+---------------------------
+ regression | tenancy_server | regression | tenancy_wrapper
+ (1 row)
+
+ select * from information_schema.foreign_tables where foreign_table_name = 'tenancy_user2_ft1';
+ foreign_table_catalog | foreign_table_schema | foreign_table_name | foreign_server_catalog | foreign_server_name
+ -----------------------+----------------------+--------------------+------------------------+---------------------
+ regression | tenancy_user2 | tenancy_user2_ft1 | regression | tenancy_server
+ (1 row)
+
+ select * from information_schema.user_mappings where foreign_server_name = 'tenancy_server';
+ authorization_identifier | foreign_server_catalog | foreign_server_name
+ --------------------------+------------------------+---------------------
+ tenancy_user2 | regression | tenancy_server
+ (1 row)
+
+ 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 relname from pg_class where relname = 'tenancy_user1_tbl1_tenancy_user1_tbl1_column1_seq';
+ 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)
+
+ select conname from pg_constraint where conname = 'tenancy_user1_tbl2_constraint';
+ conname
+ ---------
+ (0 rows)
+
+ select classid::regclass, objid::regclass, deptype from pg_depend
+ where classid = (select oid from pg_class where relname = 'pg_class')
+ and objid = (select oid from pg_class where relname = 'tenancy_user1_tbl2');
+ classid | objid | deptype
+ ---------+-------+---------
+ (0 rows)
+
+ select inhrelid::regclass, inhparent::regclass from pg_inherits
+ where inhrelid = (select oid from pg_class where relname = 'tenancy_user1_child_table');
+ inhrelid | inhparent
+ ----------+-----------
+ (0 rows)
+
+
+ --pg_class policy view, is shouldn't be visible to normal users
+ select polname from pg_policy where polrelid = (select oid from pg_class where relname = 'pg_class');
+ polname
+ ---------
+ (0 rows)
+
+ select tgname, tgtype from pg_trigger where tgname = 'tenancy_user1_tbl1_before_ins_stmt_trig';
+ tgname | tgtype
+ --------+--------
+ (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)
+
+ --works only when the shared catalog security is enabled
+ --select rolname, rolsuper from pg_roles where rolname like 'tenancy_user%';
+ select schemaname, tablename from pg_policies where tablename = 'pg_class';
+ schemaname | tablename
+ ------------+-----------
+ (0 rows)
+
+ select schemaname, viewname from pg_views where viewname = 'tenancy_user1_view1';
+ schemaname | viewname
+ ------------+----------
+ (0 rows)
+
+ select schemaname, tablename from pg_tables where tablename = 'tenancy_user1_tbl2';
+ schemaname | tablename
+ ------------+-----------
+ (0 rows)
+
+ select schemaname, matviewname from pg_matviews where matviewname = 'tenancy_user1_matview1';
+ schemaname | matviewname
+ ------------+-------------
+ (0 rows)
+
+ select schemaname, tablename, indexname from pg_indexes where indexname = 'tenancy_user1_tbl1_idx';
+ schemaname | tablename | indexname
+ ------------+-----------+-----------
+ (0 rows)
+
+ select schemaname, relname from pg_statio_all_tables where relname = 'tenancy_user1_tbl1';
+ schemaname | relname
+ ------------+---------
+ (0 rows)
+
+ select schemaname, relname, indexrelname from pg_stat_all_indexes where indexrelname = 'tenancy_user1_tbl1_idx';
+ schemaname | relname | indexrelname
+ ------------+---------+--------------
+ (0 rows)
+
+ --information_schema views
+ select * from information_schema.table_constraints where constraint_name = 'tenancy_user1_tbl2_constraint';
+ constraint_catalog | constraint_schema | constraint_name | table_catalog | table_schema | table_name | constraint_type | is_deferrable | initially_deferred
+ --------------------+-------------------+-----------------+---------------+--------------+------------+-----------------+---------------+--------------------
+ (0 rows)
+
+ select catalog_name, schema_name from information_schema.schemata where schema_name = 'tenancy_user1';
+ catalog_name | schema_name
+ --------------+-------------
+ (0 rows)
+
+ select sequence_catalog, sequence_schema, sequence_name from information_schema.sequences
+ where sequence_name = 'tenancy_user1_tbl1_tenancy_user1_tbl1_column1_seq';
+ sequence_catalog | sequence_schema | sequence_name
+ ------------------+-----------------+---------------
+ (0 rows)
+
+ select * from information_schema.tables where table_name = 'tenancy_user1_tbl1';
+ table_catalog | table_schema | table_name | table_type | self_referencing_column_name | reference_generation | user_defined_type_catalog | user_defined_type_schema | user_defined_type_name | is_insertable_into | is_typed | commit_action
+ ---------------+--------------+------------+------------+------------------------------+----------------------+---------------------------+--------------------------+------------------------+--------------------+----------+---------------
+ (0 rows)
+
+ select * from information_schema.triggers where trigger_name = 'tenancy_user1_tbl1_before_ins_stmt_trig';
+ trigger_catalog | trigger_schema | trigger_name | event_manipulation | event_object_catalog | event_object_schema | event_object_table | action_order | action_condition | action_statement | action_orientation | action_timing | action_reference_old_table | action_reference_new_table | action_reference_old_row | action_reference_new_row | created
+ -----------------+----------------+--------------+--------------------+----------------------+---------------------+--------------------+--------------+------------------+------------------+--------------------+---------------+----------------------------+----------------------------+--------------------------+--------------------------+---------
+ (0 rows)
+
+ select * from information_schema.views where table_name = 'tenancy_user1_view1';
+ table_catalog | table_schema | table_name | view_definition | check_option | is_updatable | is_insertable_into | is_trigger_updatable | is_trigger_deletable | is_trigger_insertable_into
+ ---------------+--------------+------------+-----------------+--------------+--------------+--------------------+----------------------+----------------------+----------------------------
+ (0 rows)
+
+ RESET ROLE;
+ -- Try to get the objects created by tenancy_user2 from tenancy_user1
+ SET SESSION ROLE tenancy_user1;
+ --verify system catalogs to view the details of type, function and cast.
+ select proname from pg_proc where proname = 'tenancy_user2_tenancytestfunc';
+ proname
+ -------------------------------
+ tenancy_user2_tenancytestfunc
+ (1 row)
+
+ select proname, pronamespace from pg_proc where proname = 'tenancytesttype_in';
+ proname | pronamespace
+ ---------+--------------
+ (0 rows)
+
+ select proname, pronamespace from pg_proc where proname = 'tenancytesttype_out';
+ proname | pronamespace
+ ---------+--------------
+ (0 rows)
+
+ --select the description of casttesttype
+ select description from pg_description where classoid = (select oid from pg_class where relname = 'pg_type')
+ and objoid = (select oid from pg_type where typname = 'tenancytesttype');
+ description
+ -------------
+ (0 rows)
+
+ --select the language that is owned by the tenancy_user2
+ select lanname, lanispl from pg_language where lanname = 'tenancy_lang1';
+ lanname | lanispl
+ ---------+---------
+ (0 rows)
+
+ --Foriegn data wrapper, server and foreign table details
+ SELECT fdwname, fdwhandler::regproc, fdwvalidator::regproc, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3;
+ fdwname | fdwhandler | fdwvalidator | fdwoptions
+ ---------+------------+--------------+------------
+ (0 rows)
+
+ SELECT srvname, srvoptions FROM pg_foreign_server;
+ srvname | srvoptions
+ ---------+------------
+ (0 rows)
+
+ Select srvname FROM pg_user_mappings where srvname = 'tenancy_server';
+ srvname
+ ---------
+ (0 rows)
+
+ SELECT relname, relkind from pg_class where relname = 'tenancy_user2_ft1';
+ relname | relkind
+ ---------+---------
+ (0 rows)
+
+ --information_schema views
+ select foreign_data_wrapper_catalog, foreign_data_wrapper_name from information_schema.foreign_data_wrappers
+ where foreign_data_wrapper_name = 'tenancy_wrapper';
+ foreign_data_wrapper_catalog | foreign_data_wrapper_name
+ ------------------------------+---------------------------
+ (0 rows)
+
+ select foreign_server_catalog, foreign_server_name, foreign_data_wrapper_catalog, foreign_data_wrapper_name
+ from information_schema.foreign_servers where foreign_server_name = 'tenancy_server';
+ foreign_server_catalog | foreign_server_name | foreign_data_wrapper_catalog | foreign_data_wrapper_name
+ ------------------------+---------------------+------------------------------+---------------------------
+ (0 rows)
+
+ select * from information_schema.foreign_tables where foreign_table_name = 'tenancy_user2_ft1';
+ foreign_table_catalog | foreign_table_schema | foreign_table_name | foreign_server_catalog | foreign_server_name
+ -----------------------+----------------------+--------------------+------------------------+---------------------
+ (0 rows)
+
+ select * from information_schema.user_mappings where foreign_server_name = 'tenancy_server';
+ authorization_identifier | foreign_server_catalog | foreign_server_name
+ --------------------------+------------------------+---------------------
+ (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;
+ DROP FUNCTION tenancy_user1_trigger_func() cascade;
+ DROP TABLE tenancy_user1_main_table cascade;
+ NOTICE: drop cascades to table tenancy_user1_child_table
+ RESET ROLE;
+ SET SESSION ROLE tenancy_user2;
+ DROP FUNCTION tenancy_user2_tenancytestfunc(text);
+ DROP FOREIGN TABLE tenancy_user2_ft1;
+ DROP LANGUAGE tenancy_lang1;
+ RESET ROLE;
+ DROP TYPE tenancytesttype cascade;
+ NOTICE: drop cascades to 2 other objects
+ DETAIL: drop cascades to function tenancytesttype_in(cstring)
+ drop cascades to function tenancytesttype_out(tenancytesttype)
+ DROP USER MAPPING FOR tenancy_user2 SERVER tenancy_server;
+ DROP SERVER tenancy_server;
+ DROP FOREIGN DATA WRAPPER tenancy_wrapper;
+ 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,304 ----
+ -- 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;
+
+ -- Create a type to test
+ CREATE TYPE tenancytesttype;
+
+ CREATE FUNCTION tenancytesttype_in(cstring)
+ RETURNS tenancytesttype
+ AS 'textin'
+ LANGUAGE internal STRICT IMMUTABLE;
+ CREATE FUNCTION tenancytesttype_out(tenancytesttype)
+ RETURNS cstring
+ AS 'textout'
+ LANGUAGE internal STRICT IMMUTABLE;
+
+ CREATE TYPE tenancytesttype (
+ internallength = variable,
+ input = tenancytesttype_in,
+ output = tenancytesttype_out,
+ alignment = int4
+ );
+
+ COMMENT ON TYPE tenancytesttype IS 'tenancytesttype';
+
+ REVOKE ALL ON FUNCTION tenancytesttype_in(cstring) FROM public;
+ REVOKE ALL ON FUNCTION tenancytesttype_out(tenancytesttype) FROM public;
+ GRANT ALL ON FUNCTION tenancytesttype_in(cstring) TO tenancy_user2;
+ GRANT ALL ON FUNCTION tenancytesttype_out(tenancytesttype) TO tenancy_user2;
+
+ REVOKE ALL ON TYPE tenancytesttype FROM public;
+ GRANT ALL ON TYPE tenancytesttype TO tenancy_user2;
+
+ -- Temp Language create with owner as tenancy_user2
+ CREATE TRUSTED LANGUAGE tenancy_lang1 HANDLER plpgsql_call_handler;
+ REVOKE ALL ON LANGUAGE tenancy_lang1 FROM public;
+ ALTER LANGUAGE tenancy_lang1 OWNER TO tenancy_user2;
+
+ --Foriegn data wrapper, server and user mapping creation for user tenancy_user2
+ CREATE FOREIGN DATA WRAPPER tenancy_wrapper;
+ CREATE SERVER tenancy_server FOREIGN DATA WRAPPER tenancy_wrapper;
+ CREATE USER MAPPING FOR tenancy_user2 SERVER tenancy_server;
+
+ REVOKE ALL ON FOREIGN DATA WRAPPER tenancy_wrapper FROM public;
+ REVOKE ALL ON FOREIGN SERVER tenancy_server FROM public;
+ GRANT ALL ON FOREIGN DATA WRAPPER tenancy_wrapper To tenancy_user2;
+ GRANT ALL ON FOREIGN SERVER tenancy_server 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);
+ ALTER TABLE tenancy_user1.tenancy_user1_tbl2 ADD CONSTRAINT tenancy_user1_tbl2_constraint UNIQUE (tenancy_user1_tbl2_column3);
+
+ CREATE FUNCTION tenancy_user1_trigger_func() RETURNS trigger LANGUAGE plpgsql AS '
+ BEGIN
+ RAISE NOTICE ''tenancy_user1_trigger_func(%) called: action = %, when = %, level = %'', TG_ARGV[0], TG_OP, TG_WHEN, TG_LEVEL;
+ RETURN NULL;
+ END;';
+
+ CREATE TRIGGER tenancy_user1_tbl1_before_ins_stmt_trig BEFORE INSERT ON tenancy_user1_tbl1
+ FOR EACH STATEMENT EXECUTE PROCEDURE tenancy_user1_trigger_func('before_ins_stmt');
+
+ CREATE TABLE tenancy_user1_main_table (aa TEXT);
+ CREATE TABLE tenancy_user1_child_table (bb TEXT) INHERITS (tenancy_user1_main_table);
+
+ 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 relname from pg_class where relname = 'tenancy_user1_tbl1_tenancy_user1_tbl1_column1_seq';
+
+ 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';
+ select conname from pg_constraint where conname = 'tenancy_user1_tbl2_constraint';
+ select classid::regclass, objid::regclass, deptype from pg_depend
+ where classid = (select oid from pg_class where relname = 'pg_class')
+ and objid = (select oid from pg_class where relname = 'tenancy_user1_tbl2');
+ select inhrelid::regclass, inhparent::regclass from pg_inherits
+ where inhrelid = (select oid from pg_class where relname = 'tenancy_user1_child_table');
+
+ --pg_class policy view, is shouldn't be visible to normal users
+ select polname from pg_policy where polrelid = (select oid from pg_class where relname = 'pg_class');
+
+ select tgname, tgtype from pg_trigger where tgname = 'tenancy_user1_tbl1_before_ins_stmt_trig';
+
+ -- 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';
+ --works only when the shared catalog security is enabled
+ --select rolname, rolsuper from pg_roles where rolname like 'tenancy_user%';
+ select schemaname, tablename from pg_policies where tablename = 'pg_class';
+ select schemaname, viewname from pg_views where viewname = 'tenancy_user1_view1';
+ select schemaname, tablename from pg_tables where tablename = 'tenancy_user1_tbl2';
+ select schemaname, matviewname from pg_matviews where matviewname = 'tenancy_user1_matview1';
+ select schemaname, tablename, indexname from pg_indexes where indexname = 'tenancy_user1_tbl1_idx';
+ select schemaname, relname from pg_statio_all_tables where relname = 'tenancy_user1_tbl1';
+ select schemaname, relname, indexrelname from pg_stat_all_indexes where indexrelname = 'tenancy_user1_tbl1_idx';
+
+ --information_schema views
+ select * from information_schema.table_constraints where constraint_name = 'tenancy_user1_tbl2_constraint';
+ select catalog_name, schema_name from information_schema.schemata where schema_name = 'tenancy_user1';
+ select sequence_catalog, sequence_schema, sequence_name from information_schema.sequences
+ where sequence_name = 'tenancy_user1_tbl1_tenancy_user1_tbl1_column1_seq';
+ select * from information_schema.tables where table_name = 'tenancy_user1_tbl1';
+ select * from information_schema.triggers where trigger_name = 'tenancy_user1_tbl1_before_ins_stmt_trig';
+ select * from information_schema.views where table_name = 'tenancy_user1_view1';
+
+
+ RESET ROLE;
+
+
+ -- create objects realted to tenacy_user2
+ SET SESSION ROLE tenancy_user2;
+
+ CREATE FOREIGN TABLE tenancy_user2_ft1 (
+ tenancy_user2_c1 integer OPTIONS ("param 1" 'val1') NOT NULL,
+ tenancy_user2_c2 text OPTIONS (param2 'val2', param3 'val3') CHECK (tenancy_user2_c2 <> ''),
+ tenancy_user2_c3 date,
+ CHECK (tenancy_user2_c3 BETWEEN '1994-01-01'::date AND '1994-01-31'::date)
+ ) SERVER tenancy_server OPTIONS (delimiter ',', quote '"', "be quoted" 'value');
+
+ -- a dummy function to test the new type
+ CREATE FUNCTION tenancy_user2_tenancytestfunc(text) RETURNS int4 LANGUAGE SQL AS
+ $$ SELECT 1; $$;
+
+ SELECT tenancy_user2_tenancytestfunc('foo'::text);
+
+ --verify system catalogs to view the details of type, function and cast.
+ select proname from pg_proc where proname = 'tenancy_user2_tenancytestfunc';
+ select proname, pronamespace from pg_proc where proname = 'tenancytesttype_in';
+ select proname, pronamespace from pg_proc where proname = 'tenancytesttype_out';
+
+ --select the description of casttesttype
+ select description from pg_description where classoid = (select oid from pg_class where relname = 'pg_type')
+ and objoid = (select oid from pg_type where typname = 'tenancytesttype');
+
+ --select the language that is owned by the tenancy_user2
+ select lanname, lanispl from pg_language where lanname = 'tenancy_lang1';
+
+ --Foriegn data wrapper, server and foreign table details
+ SELECT fdwname, fdwhandler::regproc, fdwvalidator::regproc, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3;
+ SELECT srvname, srvoptions FROM pg_foreign_server;
+ Select srvname FROM pg_user_mappings where srvname = 'tenancy_server';
+
+ SELECT relname, relkind from pg_class where relname = 'tenancy_user2_ft1';
+
+ --information_schema views
+ select foreign_data_wrapper_catalog, foreign_data_wrapper_name from information_schema.foreign_data_wrappers
+ where foreign_data_wrapper_name = 'tenancy_wrapper';
+ select foreign_server_catalog, foreign_server_name, foreign_data_wrapper_catalog, foreign_data_wrapper_name
+ from information_schema.foreign_servers where foreign_server_name = 'tenancy_server';
+ select * from information_schema.foreign_tables where foreign_table_name = 'tenancy_user2_ft1';
+ select * from information_schema.user_mappings where foreign_server_name = 'tenancy_server';
+
+ 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 relname from pg_class where relname = 'tenancy_user1_tbl1_tenancy_user1_tbl1_column1_seq';
+
+ 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';
+ select conname from pg_constraint where conname = 'tenancy_user1_tbl2_constraint';
+ select classid::regclass, objid::regclass, deptype from pg_depend
+ where classid = (select oid from pg_class where relname = 'pg_class')
+ and objid = (select oid from pg_class where relname = 'tenancy_user1_tbl2');
+ select inhrelid::regclass, inhparent::regclass from pg_inherits
+ where inhrelid = (select oid from pg_class where relname = 'tenancy_user1_child_table');
+
+ --pg_class policy view, is shouldn't be visible to normal users
+ select polname from pg_policy where polrelid = (select oid from pg_class where relname = 'pg_class');
+
+ select tgname, tgtype from pg_trigger where tgname = 'tenancy_user1_tbl1_before_ins_stmt_trig';
+
+ -- 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';
+ --works only when the shared catalog security is enabled
+ --select rolname, rolsuper from pg_roles where rolname like 'tenancy_user%';
+ select schemaname, tablename from pg_policies where tablename = 'pg_class';
+ select schemaname, viewname from pg_views where viewname = 'tenancy_user1_view1';
+ select schemaname, tablename from pg_tables where tablename = 'tenancy_user1_tbl2';
+ select schemaname, matviewname from pg_matviews where matviewname = 'tenancy_user1_matview1';
+ select schemaname, tablename, indexname from pg_indexes where indexname = 'tenancy_user1_tbl1_idx';
+ select schemaname, relname from pg_statio_all_tables where relname = 'tenancy_user1_tbl1';
+ select schemaname, relname, indexrelname from pg_stat_all_indexes where indexrelname = 'tenancy_user1_tbl1_idx';
+
+ --information_schema views
+ select * from information_schema.table_constraints where constraint_name = 'tenancy_user1_tbl2_constraint';
+ select catalog_name, schema_name from information_schema.schemata where schema_name = 'tenancy_user1';
+ select sequence_catalog, sequence_schema, sequence_name from information_schema.sequences
+ where sequence_name = 'tenancy_user1_tbl1_tenancy_user1_tbl1_column1_seq';
+ select * from information_schema.tables where table_name = 'tenancy_user1_tbl1';
+ select * from information_schema.triggers where trigger_name = 'tenancy_user1_tbl1_before_ins_stmt_trig';
+ select * from information_schema.views where table_name = 'tenancy_user1_view1';
+
+ RESET ROLE;
+
+ -- Try to get the objects created by tenancy_user2 from tenancy_user1
+ SET SESSION ROLE tenancy_user1;
+
+ --verify system catalogs to view the details of type, function and cast.
+ select proname from pg_proc where proname = 'tenancy_user2_tenancytestfunc';
+ select proname, pronamespace from pg_proc where proname = 'tenancytesttype_in';
+ select proname, pronamespace from pg_proc where proname = 'tenancytesttype_out';
+
+ --select the description of casttesttype
+ select description from pg_description where classoid = (select oid from pg_class where relname = 'pg_type')
+ and objoid = (select oid from pg_type where typname = 'tenancytesttype');
+
+ --select the language that is owned by the tenancy_user2
+ select lanname, lanispl from pg_language where lanname = 'tenancy_lang1';
+
+ --Foriegn data wrapper, server and foreign table details
+ SELECT fdwname, fdwhandler::regproc, fdwvalidator::regproc, fdwoptions FROM pg_foreign_data_wrapper ORDER BY 1, 2, 3;
+ SELECT srvname, srvoptions FROM pg_foreign_server;
+ Select srvname FROM pg_user_mappings where srvname = 'tenancy_server';
+
+ SELECT relname, relkind from pg_class where relname = 'tenancy_user2_ft1';
+
+ --information_schema views
+ select foreign_data_wrapper_catalog, foreign_data_wrapper_name from information_schema.foreign_data_wrappers
+ where foreign_data_wrapper_name = 'tenancy_wrapper';
+ select foreign_server_catalog, foreign_server_name, foreign_data_wrapper_catalog, foreign_data_wrapper_name
+ from information_schema.foreign_servers where foreign_server_name = 'tenancy_server';
+ select * from information_schema.foreign_tables where foreign_table_name = 'tenancy_user2_ft1';
+ select * from information_schema.user_mappings where foreign_server_name = 'tenancy_server';
+
+ 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;
+ DROP FUNCTION tenancy_user1_trigger_func() cascade;
+ DROP TABLE tenancy_user1_main_table cascade;
+
+ RESET ROLE;
+
+ SET SESSION ROLE tenancy_user2;
+
+ DROP FUNCTION tenancy_user2_tenancytestfunc(text);
+ DROP FOREIGN TABLE tenancy_user2_ft1;
+ DROP LANGUAGE tenancy_lang1;
+
+ RESET ROLE;
+
+ DROP TYPE tenancytesttype cascade;
+ DROP USER MAPPING FOR tenancy_user2 SERVER tenancy_server;
+ DROP SERVER tenancy_server;
+ DROP FOREIGN DATA WRAPPER tenancy_wrapper;
+
+ alter database regression with catalog security = false;
+
+ drop schema tenancy_user1;
+ drop schema tenancy_user2;
+
+ drop role tenancy_user1;
+ drop role tenancy_user2;