Multi-tenancy with RLS

Started by Haribabu Kommiover 10 years ago63 messages
#1Haribabu Kommi
kommi.haribabu@gmail.com
1 attachment(s)

This is regarding supporting of multi-tenancy in a single PostgreSQL instance
using the row level security feature. The main idea is to have the
"row level security"
enabled on system catalog tables, thus the user can get only the rows that are
either system objects or the user objects, where the user is the owner.

Example:

postgres=# create role test login;
postgres=# create role test1 login;

postgres=# \c postgres test
postgres=> create table test(f1 int);
postgres=> \d
List of relations
Schema | Name | Type | Owner
--------+------+-------+-------
public | test | table | test
(1 row)

postgres=> \c postgres test1
postgres=> create table test1(f1 int);
postgres=> \d
List of relations
Schema | Name | Type | Owner
--------+-------+-------+-------
public | test1 | table | test1
(1 row)

postgres=# \c postgres test
postgres=> \d
List of relations
Schema | Name | Type | Owner
--------+------+-------+-------
public | test | table | test
(1 row)

To enable row level security on system catalog tables, currently I
added a new database option to create/alter database. The syntax can
be changed later. Adding an option to database makes it easier for
users to enable/disable the row level security on system catalog
tables.

CREATE DATABASE USERDB WITH ROW LEVEL SECURITY = TRUE;
ALTER DATBASE USERDB WITH ROW LEVEL SECURITY = FALSE;

A new boolean column "datrowlevelsecurity" is added to pg_database
system catalog table to display the status of row level security on
that database.

Currently I just implemented the row level security is enabled only
for pg_class system table as a proof of concept. whenever the row
level security on the database is enabled/disabled, it internally
fires the create policy/remove policy commands using SPI interfaces.

Here I attached the proof concept patch.

Pending items:
1. Supporting of RLS on all system catalog tables
2. Instead of SPI interfaces, any better way to create/remove policies.

Any comments/suggestions regarding the way to achieve multi-tenancy in
PostgreSQL?

Regards,
Hari Babu
Fujitsu Australia

Attachments:

multi-tenancy_with_rls_poc.patchapplication/octet-stream; name=multi-tenancy_with_rls_poc.patchDownload
*** 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"
***************
*** 128,133 **** createdb(const CreatedbStmt *stmt)
--- 129,135 ----
  	DefElem    *distemplate = NULL;
  	DefElem    *dallowconnections = NULL;
  	DefElem    *dconnlimit = NULL;
+ 	DefElem    *drowlevelsecurity = NULL;
  	char	   *dbname = stmt->dbname;
  	char	   *dbowner = NULL;
  	const char *dbtemplate = NULL;
***************
*** 137,142 **** createdb(const CreatedbStmt *stmt)
--- 139,145 ----
  	int			encoding = -1;
  	bool		dbistemplate = false;
  	bool		dballowconnections = true;
+ 	bool		dbrowlevelsecurity = false;
  	int			dbconnlimit = -1;
  	int			notherbackends;
  	int			npreparedxacts;
***************
*** 226,231 **** createdb(const CreatedbStmt *stmt)
--- 229,242 ----
  					 errmsg("LOCATION is not supported anymore"),
  					 errhint("Consider using tablespaces instead.")));
  		}
+ 		else if (strcmp(defel->defname, "row_level_security") == 0)
+ 		{
+ 			if (drowlevelsecurity)
+ 				ereport(ERROR,
+ 					(errcode(ERRCODE_SYNTAX_ERROR),
+ 					errmsg("conflicting or redundant options")));
+ 			drowlevelsecurity = defel;
+ 		}
  		else
  			ereport(ERROR,
  					(errcode(ERRCODE_SYNTAX_ERROR),
***************
*** 278,283 **** createdb(const CreatedbStmt *stmt)
--- 289,296 ----
  					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  					 errmsg("invalid connection limit: %d", dbconnlimit)));
  	}
+ 	if (drowlevelsecurity && drowlevelsecurity->arg)
+ 		dbrowlevelsecurity = defGetBoolean(drowlevelsecurity);
  
  	/* obtain OID of proposed owner */
  	if (dbowner)
***************
*** 519,524 **** createdb(const CreatedbStmt *stmt)
--- 532,538 ----
  	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_datrowlevelsecurity - 1] = BoolGetDatum(dbrowlevelsecurity);
  
  	/*
  	 * We deliberately set datacl to default (NULL), rather than copying it
***************
*** 563,568 **** createdb(const CreatedbStmt *stmt)
--- 577,585 ----
  	RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE | CHECKPOINT_WAIT
  					  | CHECKPOINT_FLUSH_ALL);
  
+ 	if (dbrowlevelsecurity)
+ 		CreateCatalogPolicy();
+ 
  	/*
  	 * Once we start copying subdirectories, we need to be able to clean 'em
  	 * up if we fail.  Use an ENSURE block to make sure this happens.  (This
***************
*** 1375,1385 **** AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
--- 1392,1405 ----
  	ListCell   *option;
  	bool		dbistemplate = false;
  	bool		dballowconnections = true;
+ 	bool		dbrowlevelsecurity = false;
  	int			dbconnlimit = -1;
  	DefElem    *distemplate = NULL;
  	DefElem    *dallowconnections = NULL;
  	DefElem    *dconnlimit = NULL;
  	DefElem    *dtablespace = NULL;
+ 	DefElem    *drowlevelsecurity = 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)
--- 1441,1455 ----
  						 errmsg("conflicting or redundant options")));
  			dtablespace = defel;
  		}
+ 		else if (strcmp(defel->defname, "row_level_security") == 0)
+ 		{
+ 			if (drowlevelsecurity)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						errmsg("conflicting or redundant options")));
+ 
+ 			drowlevelsecurity = defel;
+ 		}
  		else
  			ereport(ERROR,
  					(errcode(ERRCODE_SYNTAX_ERROR),
***************
*** 1457,1462 **** AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
--- 1486,1493 ----
  					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  					 errmsg("invalid connection limit: %d", dbconnlimit)));
  	}
+ 	if (drowlevelsecurity && drowlevelsecurity->arg)
+ 		dbrowlevelsecurity = defGetBoolean(drowlevelsecurity);
  
  	/*
  	 * Get the old tuple.  We don't need a lock on the database per se,
***************
*** 1476,1481 **** AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
--- 1507,1513 ----
  				(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()))
***************
*** 1493,1498 **** AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
--- 1525,1539 ----
  				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  				 errmsg("cannot disallow connections for current database")));
  
+ 	if (dbrowlevelsecurity)
+ 	{
+ 		CreateCatalogPolicy();
+ 	}
+ 	else if (pg_database_tuple->datrowlevelsecurity)
+ 	{
+ 		RemoveCatalogPolicy();
+ 	}
+ 
  	/*
  	 * 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 (drowlevelsecurity)
+ 	{
+ 		new_record[Anum_pg_database_datrowlevelsecurity - 1] = BoolGetDatum(dbrowlevelsecurity);
+ 		new_record_repl[Anum_pg_database_datrowlevelsecurity - 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
***************
*** 26,31 ****
--- 26,32 ----
  #include "catalog/pg_policy.h"
  #include "catalog/pg_type.h"
  #include "commands/policy.h"
+ #include "executor/spi.h"
  #include "miscadmin.h"
  #include "nodes/makefuncs.h"
  #include "nodes/pg_list.h"
***************
*** 44,49 ****
--- 45,51 ----
  #include "utils/lsyscache.h"
  #include "utils/memutils.h"
  #include "utils/rel.h"
+ #include "utils/snapmgr.h"
  #include "utils/syscache.h"
  
  static void RangeVarCallbackForPolicy(const RangeVar *rv,
***************
*** 449,454 **** RemovePolicyById(Oid policy_id)
--- 451,619 ----
  }
  
  /*
+  * CreateCatalogPolicy -
+  *	 handles the execution of the ALTER DATBASE ROW LEVEL SECUTIRY = TRUE command.
+  */
+ void
+ CreateCatalogPolicy()
+ {
+ 	Relation	rel;
+ 	ScanKeyData scankey;
+ 	SysScanDesc scan;
+ 	HeapTuple	tuple;
+ 	Form_pg_class pg_class_tuple;
+ 	bool allow_sytem_table_mods_old;
+ 
+ 	/*
+ 	 * 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);
+ 
+ 	allow_sytem_table_mods_old = allowSystemTableMods;
+ 	allowSystemTableMods = true;
+ 
+ 	PG_TRY();
+ 	{
+ 		while ((tuple = systable_getnext(scan)) != NULL)
+ 		{
+ 			int			ret;
+ 			char		buf[200];
+ 			HeapTuple	cache_tuple;
+ 
+ 			pg_class_tuple = (Form_pg_class)GETSTRUCT(tuple);
+ 			if ((pg_class_tuple->relkind != RELKIND_RELATION)
+ 				|| (HeapTupleGetOid(tuple) != RelationRelationId))
+ 				continue;
+ 				
+ 			cache_tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(HeapTupleGetOid(tuple)));
+ 
+ 			if (!HeapTupleIsValid(cache_tuple))
+ 				elog(ERROR, "cache lookup failed for relation %u", HeapTupleGetOid(tuple));
+ 
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR (pg_get_userbyid(relowner) = current_user))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 		
+ 			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;
+ 			simple_heap_update(rel, &cache_tuple->t_self, cache_tuple);
+ 
+ 			/* keep catalog indexes current */
+ 			CatalogUpdateIndexes(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;
+ 	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;
+ 
+ 	/*
+ 	* 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);
+ 
+ 	allow_sytem_table_mods_old = allowSystemTableMods;
+ 	allowSystemTableMods = true;
+ 
+ 	PG_TRY();
+ 	{
+ 		while ((tuple = systable_getnext(scan)) != NULL)
+ 		{
+ 			int			ret;
+ 			char		buf[200];
+ 			HeapTuple	cache_tuple;
+ 
+ 			pg_class_tuple = (Form_pg_class)GETSTRUCT(tuple);
+ 			if ((pg_class_tuple->relkind != RELKIND_RELATION)
+ 				|| (HeapTupleGetOid(tuple) != RelationRelationId))
+ 				continue;
+ 
+ 			cache_tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(HeapTupleGetOid(tuple)));
+ 
+ 			if (!HeapTupleIsValid(cache_tuple))
+ 				elog(ERROR, "cache lookup failed for relation %u", HeapTupleGetOid(tuple));
+ 
+ 			sprintf(buf, "Drop policy %s_read_own_data on %s",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 
+ 			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;
+ 			simple_heap_update(rel, &cache_tuple->t_self, cache_tuple);
+ 
+ 			/* keep catalog indexes current */
+ 			CatalogUpdateIndexes(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;
+ 	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
***************
*** 8859,8864 **** createdb_opt_name:
--- 8859,8865 ----
  			| OWNER							{ $$ = pstrdup($1); }
  			| TABLESPACE					{ $$ = pstrdup($1); }
  			| TEMPLATE						{ $$ = pstrdup($1); }
+ 			| ROW LEVEL SECURITY			{ $$ = pstrdup("row_level_security"); }
  		;
  
  /*
*** a/src/backend/utils/misc/rls.c
--- b/src/backend/utils/misc/rls.c
***************
*** 60,68 **** check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
  	Oid			user_id = checkAsUser ? checkAsUser : GetUserId();
  
  	/* Nothing to do for built-in relations */
  	if (relid < FirstNormalObjectId)
  		return RLS_NONE;
! 
  	/*
  	 * Check if we have been told to explicitly skip RLS (perhaps because this
  	 * is a foreign key check)
--- 60,69 ----
  	Oid			user_id = checkAsUser ? checkAsUser : GetUserId();
  
  	/* Nothing to do for built-in relations */
+ #if 0
  	if (relid < FirstNormalObjectId)
  		return RLS_NONE;
! #endif
  	/*
  	 * Check if we have been told to explicitly skip RLS (perhaps because this
  	 * is a foreign key check)
*** 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		datrowlevelsecurity; /* row level 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_datrowlevelsecurity	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/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 */
#2Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Haribabu Kommi (#1)
2 attachment(s)
Re: Multi-tenancy with RLS

On Fri, Aug 14, 2015 at 12:00 PM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

Here I attached the proof concept patch.

Here I attached an updated patch by adding policies to the most of the
system catalog tables, except the following.
AggregateRelationId

AccessMethodRelationId
AccessMethodOperatorRelationId
AccessMethodProcedureRelationId

AuthMemRelationId
CastRelationId
EnumRelationId
EventTriggerRelationId
ExtensionRelationId

LargeObjectRelationId
LargeObjectMetadataRelationId

PLTemplateRelationId
RangeRelationId
RewriteRelationId
TransformRelationId

TSConfigRelationId
TSConfigMapRelationId
TSDictionaryRelationId
TSParserRelationId
TSTemplateRelationId

Following catalog tables needs to create the policy based on the
class, so currently didn't added any policy for the same.

SecLabelRelationId
SharedDependRelationId
SharedDescriptionRelationId
SharedSecLabelRelationId

If any user is granted any permissions on that object then that user
can view it's meta data of that object from the catalog tables.
To check the permissions of the user on the object, instead of
checking each and every available option, I just added a new
privilege check option called "any". If user have any permissions on
the object, the corresponding permission check function returns
true. Patch attached for the same.

Any thoughts/comments?

Regards,
Hari Babu
Fujitsu Australia

Attachments:

multi-tenancy_with_rls_poc_2.patchapplication/octet-stream; name=multi-tenancy_with_rls_poc_2.patchDownload
*** 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"
***************
*** 128,133 **** createdb(const CreatedbStmt *stmt)
--- 129,135 ----
  	DefElem    *distemplate = NULL;
  	DefElem    *dallowconnections = NULL;
  	DefElem    *dconnlimit = NULL;
+ 	DefElem    *drowlevelsecurity = NULL;
  	char	   *dbname = stmt->dbname;
  	char	   *dbowner = NULL;
  	const char *dbtemplate = NULL;
***************
*** 137,142 **** createdb(const CreatedbStmt *stmt)
--- 139,145 ----
  	int			encoding = -1;
  	bool		dbistemplate = false;
  	bool		dballowconnections = true;
+ 	bool		dbrowlevelsecurity = false;
  	int			dbconnlimit = -1;
  	int			notherbackends;
  	int			npreparedxacts;
***************
*** 226,231 **** createdb(const CreatedbStmt *stmt)
--- 229,242 ----
  					 errmsg("LOCATION is not supported anymore"),
  					 errhint("Consider using tablespaces instead.")));
  		}
+ 		else if (strcmp(defel->defname, "row_level_security") == 0)
+ 		{
+ 			if (drowlevelsecurity)
+ 				ereport(ERROR,
+ 					(errcode(ERRCODE_SYNTAX_ERROR),
+ 					errmsg("conflicting or redundant options")));
+ 			drowlevelsecurity = defel;
+ 		}
  		else
  			ereport(ERROR,
  					(errcode(ERRCODE_SYNTAX_ERROR),
***************
*** 278,283 **** createdb(const CreatedbStmt *stmt)
--- 289,296 ----
  					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  					 errmsg("invalid connection limit: %d", dbconnlimit)));
  	}
+ 	if (drowlevelsecurity && drowlevelsecurity->arg)
+ 		dbrowlevelsecurity = defGetBoolean(drowlevelsecurity);
  
  	/* obtain OID of proposed owner */
  	if (dbowner)
***************
*** 519,524 **** createdb(const CreatedbStmt *stmt)
--- 532,538 ----
  	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_datrowlevelsecurity - 1] = BoolGetDatum(dbrowlevelsecurity);
  
  	/*
  	 * We deliberately set datacl to default (NULL), rather than copying it
***************
*** 563,568 **** createdb(const CreatedbStmt *stmt)
--- 577,585 ----
  	RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE | CHECKPOINT_WAIT
  					  | CHECKPOINT_FLUSH_ALL);
  
+ 	if (dbrowlevelsecurity)
+ 		CreateCatalogPolicy();
+ 
  	/*
  	 * Once we start copying subdirectories, we need to be able to clean 'em
  	 * up if we fail.  Use an ENSURE block to make sure this happens.  (This
***************
*** 1375,1385 **** AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
--- 1392,1405 ----
  	ListCell   *option;
  	bool		dbistemplate = false;
  	bool		dballowconnections = true;
+ 	bool		dbrowlevelsecurity = false;
  	int			dbconnlimit = -1;
  	DefElem    *distemplate = NULL;
  	DefElem    *dallowconnections = NULL;
  	DefElem    *dconnlimit = NULL;
  	DefElem    *dtablespace = NULL;
+ 	DefElem    *drowlevelsecurity = 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)
--- 1441,1455 ----
  						 errmsg("conflicting or redundant options")));
  			dtablespace = defel;
  		}
+ 		else if (strcmp(defel->defname, "row_level_security") == 0)
+ 		{
+ 			if (drowlevelsecurity)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						errmsg("conflicting or redundant options")));
+ 
+ 			drowlevelsecurity = defel;
+ 		}
  		else
  			ereport(ERROR,
  					(errcode(ERRCODE_SYNTAX_ERROR),
***************
*** 1457,1462 **** AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
--- 1486,1493 ----
  					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  					 errmsg("invalid connection limit: %d", dbconnlimit)));
  	}
+ 	if (drowlevelsecurity && drowlevelsecurity->arg)
+ 		dbrowlevelsecurity = defGetBoolean(drowlevelsecurity);
  
  	/*
  	 * Get the old tuple.  We don't need a lock on the database per se,
***************
*** 1476,1481 **** AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
--- 1507,1513 ----
  				(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()))
***************
*** 1493,1498 **** AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
--- 1525,1539 ----
  				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  				 errmsg("cannot disallow connections for current database")));
  
+ 	if (dbrowlevelsecurity)
+ 	{
+ 		CreateCatalogPolicy();
+ 	}
+ 	else if (pg_database_tuple->datrowlevelsecurity)
+ 	{
+ 		RemoveCatalogPolicy();
+ 	}
+ 
  	/*
  	 * 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 (drowlevelsecurity)
+ 	{
+ 		new_record[Anum_pg_database_datrowlevelsecurity - 1] = BoolGetDatum(dbrowlevelsecurity);
+ 		new_record_repl[Anum_pg_database_datrowlevelsecurity - 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,83 ----
  #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_auth_members.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_database.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_pltemplate.h"
  #include "catalog/pg_policy.h"
+ #include "catalog/pg_proc.h"
+ #include "catalog/pg_range.h"
+ #include "catalog/pg_replication_origin.h"
+ #include "catalog/pg_rewrite.h"
+ #include "catalog/pg_seclabel.h"
+ #include "catalog/pg_shdepend.h"
+ #include "catalog/pg_shdescription.h"
+ #include "catalog/pg_shseclabel.h"
+ #include "catalog/pg_statistic.h"
+ #include "catalog/pg_tablespace.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,49 ****
--- 96,102 ----
  #include "utils/lsyscache.h"
  #include "utils/memutils.h"
  #include "utils/rel.h"
+ #include "utils/snapmgr.h"
  #include "utils/syscache.h"
  
  static void RangeVarCallbackForPolicy(const RangeVar *rv,
***************
*** 197,202 **** RelationBuildRowSecurity(Relation relation)
--- 250,259 ----
  	MemoryContext oldcxt = CurrentMemoryContext;
  	RowSecurityDesc *volatile rsdesc = NULL;
  
+ 	/* */
+ 	if (!criticalRelcachesBuilt || !criticalSharedRelcachesBuilt || relation->rd_id == PolicyRelationId)
+ 		return;
+ 
  	/*
  	 * Create a memory context to hold everything associated with this
  	 * relation's row security policy.  This makes it easy to clean up during
***************
*** 448,453 **** RemovePolicyById(Oid policy_id)
--- 505,926 ----
  	heap_close(pg_policy_rel, RowExclusiveLock);
  }
  
+ static bool 
+ generate_catalog_policy_string(HeapTuple tuple, char *buf)
+ {
+ 	bool can_create_policy = false;
+ 	Form_pg_class pg_class_tuple;
+ 
+ 	pg_class_tuple = (Form_pg_class)GETSTRUCT(tuple);
+ 
+ 	switch (HeapTupleGetOid(tuple))
+ 	{
+ 		/*
+ 		 * 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 AuthMemRelationId:
+ 			break;
+ 		case AuthIdRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR pg_has_role(oid, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case CastRelationId:
+ 			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_table_privilege(conrelid,'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 DatabaseRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR has_database_privilege(oid,'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case DbRoleSettingRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" (pg_get_userbyid(setrole) = 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"
+ 				" (has_table_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"
+ 				" (has_table_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 PLTemplateRelationId:
+ 			break;
+ 		case PolicyRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR has_table_privilege(polrelid, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case ProcedureRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR has_function_privilege(oid, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case RangeRelationId:
+ 			break;
+ 		case ReplicationOriginRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" (pg_has_role(roident, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case RewriteRelationId:
+ 			break;
+ 		case SecLabelRelationId:
+ 			break;
+ 		case SharedDependRelationId:
+ 			break;
+ 		case SharedDescriptionRelationId:
+ 			break;
+ 		case SharedSecLabelRelationId:
+ 			break;
+ 		case StatisticRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((starelid < 16384) OR has_table_privilege(starelid, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case TableSpaceRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR has_tablespace_privilege(oid, '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(oid, '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;
+ }
+ 
+ /*
+  * 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(200);
+ 	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_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;
+ 			simple_heap_update(rel, &cache_tuple->t_self, cache_tuple);
+ 
+ 			/* keep catalog indexes current */
+ 			CatalogUpdateIndexes(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;
+ 
+ 	/*
+ 	* 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);
+ 
+ 	allow_sytem_table_mods_old = allowSystemTableMods;
+ 	allowSystemTableMods = true;
+ 
+ 	PG_TRY();
+ 	{
+ 		while ((tuple = systable_getnext(scan)) != NULL)
+ 		{
+ 			int			ret;
+ 			char		buf[200];
+ 			HeapTuple	cache_tuple;
+ 
+ 			pg_class_tuple = (Form_pg_class)GETSTRUCT(tuple);
+ 			if ((pg_class_tuple->relkind != RELKIND_RELATION)
+ 				|| (HeapTupleGetOid(tuple) != RelationRelationId))
+ 				continue;
+ 
+ 			cache_tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(HeapTupleGetOid(tuple)));
+ 
+ 			if (!HeapTupleIsValid(cache_tuple))
+ 				elog(ERROR, "cache lookup failed for relation %u", HeapTupleGetOid(tuple));
+ 
+ 			sprintf(buf, "Drop policy %s_read_own_data on %s",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 
+ 			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;
+ 			simple_heap_update(rel, &cache_tuple->t_self, cache_tuple);
+ 
+ 			/* keep catalog indexes current */
+ 			CatalogUpdateIndexes(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;
+ 	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
***************
*** 8859,8864 **** createdb_opt_name:
--- 8859,8865 ----
  			| OWNER							{ $$ = pstrdup($1); }
  			| TABLESPACE					{ $$ = pstrdup($1); }
  			| TEMPLATE						{ $$ = pstrdup($1); }
+ 			| ROW LEVEL SECURITY			{ $$ = pstrdup("row_level_security"); }
  		;
  
  /*
*** a/src/backend/utils/misc/rls.c
--- b/src/backend/utils/misc/rls.c
***************
*** 60,68 **** check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
  	Oid			user_id = checkAsUser ? checkAsUser : GetUserId();
  
  	/* Nothing to do for built-in relations */
  	if (relid < FirstNormalObjectId)
  		return RLS_NONE;
! 
  	/*
  	 * Check if we have been told to explicitly skip RLS (perhaps because this
  	 * is a foreign key check)
--- 60,69 ----
  	Oid			user_id = checkAsUser ? checkAsUser : GetUserId();
  
  	/* Nothing to do for built-in relations */
+ #if 0
  	if (relid < FirstNormalObjectId)
  		return RLS_NONE;
! #endif
  	/*
  	 * Check if we have been told to explicitly skip RLS (perhaps because this
  	 * is a foreign key check)
*** 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		datrowlevelsecurity; /* row level 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_datrowlevelsecurity	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/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 */
any_privilege_check_option.patchapplication/octet-stream; name=any_privilege_check_option.patchDownload
*** a/src/backend/utils/adt/acl.c
--- b/src/backend/utils/adt/acl.c
***************
*** 2031,2036 **** convert_table_priv_string(text *priv_type_text)
--- 2031,2041 ----
  		{"REFERENCES WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
  		{"TRIGGER", ACL_TRIGGER},
  		{"TRIGGER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
+ 		{"ANY", ACL_SELECT | ACL_GRANT_OPTION_FOR(ACL_SELECT) |
+ 				ACL_INSERT | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
+ 				ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_DELETE | ACL_GRANT_OPTION_FOR(ACL_DELETE) |
+ 				ACL_TRUNCATE | ACL_GRANT_OPTION_FOR(ACL_TRUNCATE) | ACL_REFERENCES |
+ 				ACL_GRANT_OPTION_FOR(ACL_REFERENCES) | ACL_TRIGGER | ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
  		{"RULE", 0},			/* ignore old RULE privileges */
  		{"RULE WITH GRANT OPTION", 0},
  		{NULL, 0}
***************
*** 2243,2248 **** convert_sequence_priv_string(text *priv_type_text)
--- 2248,2254 ----
  		{"USAGE", ACL_USAGE},
  		{"SELECT", ACL_SELECT},
  		{"UPDATE", ACL_UPDATE},
+ 		{"ANY", ACL_USAGE | ACL_SELECT | ACL_UPDATE},
  		{NULL, 0}
  	};
  
***************
*** 2858,2863 **** convert_column_priv_string(text *priv_type_text)
--- 2864,2872 ----
  		{"UPDATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_UPDATE)},
  		{"REFERENCES", ACL_REFERENCES},
  		{"REFERENCES WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
+ 		{"ANY", ACL_SELECT | ACL_GRANT_OPTION_FOR(ACL_SELECT) | ACL_INSERT |
+ 				ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
+ 				ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_REFERENCES | ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
  		{NULL, 0}
  	};
  
***************
*** 3055,3060 **** convert_database_priv_string(text *priv_type_text)
--- 3064,3072 ----
  		{"TEMP WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP)},
  		{"CONNECT", ACL_CONNECT},
  		{"CONNECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
+ 		{"ANY", ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE) |
+ 									ACL_CREATE_TEMP | ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CREATE_TEMP |
+ 									ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CONNECT | ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
  		{NULL, 0}
  	};
  
***************
*** 3238,3243 **** convert_foreign_data_wrapper_priv_string(text *priv_type_text)
--- 3250,3256 ----
  	static const priv_map foreign_data_wrapper_priv_map[] = {
  		{"USAGE", ACL_USAGE},
  		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+ 		{"ANY", ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
  		{NULL, 0}
  	};
  
***************
*** 3438,3443 **** convert_function_priv_string(text *priv_type_text)
--- 3451,3457 ----
  	static const priv_map function_priv_map[] = {
  		{"EXECUTE", ACL_EXECUTE},
  		{"EXECUTE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_EXECUTE)},
+ 		{"ANY", ACL_EXECUTE | ACL_GRANT_OPTION_FOR(ACL_EXECUTE)},
  		{NULL, 0}
  	};
  
***************
*** 3629,3634 **** convert_language_priv_string(text *priv_type_text)
--- 3643,3649 ----
  	static const priv_map language_priv_map[] = {
  		{"USAGE", ACL_USAGE},
  		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+ 		{"ANY", ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
  		{NULL, 0}
  	};
  
***************
*** 3822,3827 **** convert_schema_priv_string(text *priv_type_text)
--- 3837,3843 ----
  		{"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
  		{"USAGE", ACL_USAGE},
  		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+ 		{"ANY", ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE) | ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
  		{NULL, 0}
  	};
  
***************
*** 4004,4009 **** convert_server_priv_string(text *priv_type_text)
--- 4020,4026 ----
  	static const priv_map server_priv_map[] = {
  		{"USAGE", ACL_USAGE},
  		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+ 		{"ANY", ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
  		{NULL, 0}
  	};
  
***************
*** 4186,4191 **** convert_tablespace_priv_string(text *priv_type_text)
--- 4203,4209 ----
  	static const priv_map tablespace_priv_map[] = {
  		{"CREATE", ACL_CREATE},
  		{"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
+ 		{"ANY", ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE)},
  		{NULL, 0}
  	};
  
***************
*** 4385,4390 **** convert_type_priv_string(text *priv_type_text)
--- 4403,4409 ----
  	static const priv_map type_priv_map[] = {
  		{"USAGE", ACL_USAGE},
  		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+ 		{"ANY", ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
  		{NULL, 0}
  	};
  
***************
*** 4566,4571 **** convert_role_priv_string(text *priv_type_text)
--- 4585,4591 ----
  		{"USAGE WITH ADMIN OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
  		{"MEMBER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
  		{"MEMBER WITH ADMIN OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
+ 		{"ANY", ACL_USAGE | ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE)},
  		{NULL, 0}
  	};
  
#3Joe Conway
mail@joeconway.com
In reply to: Haribabu Kommi (#2)
Re: Multi-tenancy with RLS

On 09/01/2015 11:25 PM, Haribabu Kommi wrote:

If any user is granted any permissions on that object then that user
can view it's meta data of that object from the catalog tables.
To check the permissions of the user on the object, instead of
checking each and every available option, I just added a new
privilege check option called "any". If user have any permissions on
the object, the corresponding permission check function returns
true. Patch attached for the same.

Any thoughts/comments?

Thanks for working on this! Overall I like the concept and know of use
cases where it is critical and should be supported. Some comments:

1.) "... WITH ROW LEVEL SECURITY ...": name is not really accurate. This
feature does not, for example automatically add row security to all
tables in the database. It is really a specific set of RLS policies on
system tables to accomplish one very specific goal -- restrict metadata
access to those objects on which the user has some access. So maybe
something like one of these?
... WITH METADATA SECURITY ...
... WITH CATALOG SECURITY ...
... WITH METADATA RESTRICT ...
... WITH CATALOG RESTRICT ...

2.) Related to #1, I wonder if we should even provide a set of policies,
or should we just allow RLS on system tables and let people create their
own policies to suit their own needs. Maybe that could be dangerous, but
so are a lot of things we allow superusers do.

3.) In RelationBuildRowSecurity:

8<--------------
+ 	/* */
+ 	if (!criticalRelcachesBuilt || !criticalSharedRelcachesBuilt ||
relation->rd_id == PolicyRelationId)
+ 		return;
8<--------------

Bypassing RelationBuildRowSecurity() for relation->rd_id ==
PolicyRelationId causes a segfault for me and prevents me from even
logging into the database. Additionally I would want row security to
apply to pg_policy as well. Maybe not the best approach, but I solved
the infinite recursion issue like this:

8<--------------
diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c
index 45326a3..49db767 100644
*** a/src/backend/commands/policy.c
--- b/src/backend/commands/policy.c
*************** static char parse_policy_command(const c
*** 52,57 ****
--- 105,116 ----
  static Datum *policy_role_list_to_array(List *roles, int *num_roles);
  /*
+  * used to prevent infinite recursion when building
+  * row security for pg_policy itself
+  */
+ int rdepth = 0;
+
+ /*
   * Callback to RangeVarGetRelidExtended().
   *
   * Checks the following:
*************** RelationBuildRowSecurity(Relation relati
*** 197,202 ****
--- 257,273 ----
        MemoryContext oldcxt = CurrentMemoryContext;
        RowSecurityDesc *volatile rsdesc = NULL;
+       /* */
+       if (!criticalRelcachesBuilt || !criticalSharedRelcachesBuilt)
+               return;
+       if (relation->rd_id == PolicyRelationId && rdepth > 0)
+       {
+               rdepth = 0;
+               return;
+       }
+       if (relation->rd_id == PolicyRelationId)
+               rdepth = 1;
+
        /*
         * Create a memory context to hold everything associated with this
         * relation's row security policy.  This makes it easy to clean
up during
*************** RelationBuildRowSecurity(Relation relati
*** 366,377 ****
--- 437,450 ----
                /* Delete rscxt, first making sure it isn't active */
                MemoryContextSwitchTo(oldcxt);
                MemoryContextDelete(rscxt);
+               rdepth = 0;
                PG_RE_THROW();
        }
        PG_END_TRY();

/* Success --- attach the policy descriptor to the relcache entry */
relation->rd_rsdesc = rsdesc;
+ rdepth = 0;
}

/*
8<--------------

4.) Currently, policies are always installed in the current database
rather than the target database. Didn't try to figure out how to fix:
8<--------------
postgres=# create database test with ROW LEVEL SECURITY = true;
CREATE DATABASE
postgres=# select count(1) from pg_policy;
count
-------
30
(1 row)

postgres=# \c test
You are now connected to database "test" as user "postgres".
test=# select count(1) from pg_policy;
count
-------
0
(1 row)
8<--------------

5.) I'm not thrilled with all the additional included headers added to
policy.c for this, but I don't offhand see a better way either.

6.) I would like to see this work in conjunction with sepgsql (and/or
other security label providers) as well. That could be accomplished
either as mentioned in #2 above (let me create custom policies to suit
my needs), or by providing security label hooks somewhere that they
affect the output of the has_<obj>_privilege*() functions.

HTH,

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

#4Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Joe Conway (#3)
Re: Multi-tenancy with RLS

On Fri, Sep 11, 2015 at 7:50 AM, Joe Conway <mail@joeconway.com> wrote:

On 09/01/2015 11:25 PM, Haribabu Kommi wrote:

If any user is granted any permissions on that object then that user
can view it's meta data of that object from the catalog tables.
To check the permissions of the user on the object, instead of
checking each and every available option, I just added a new
privilege check option called "any". If user have any permissions on
the object, the corresponding permission check function returns
true. Patch attached for the same.

Any thoughts/comments?

Thanks for working on this! Overall I like the concept and know of use
cases where it is critical and should be supported. Some comments:

Thanks for the review, I will take care of the comments in the next patch.

I didn't find any better approach other than creating policies automatically
or providing permission to superuser on system catalog tables. If everyone
feels as this is the best approach, then i will create policies for all catalog
tables in the next version.

Regards,
Hari Babu
Fujitsu Australia

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#5Jim Nasby
Jim.Nasby@BlueTreble.com
In reply to: Haribabu Kommi (#4)
Re: Multi-tenancy with RLS

On 9/14/15 7:38 PM, Haribabu Kommi wrote:

On Fri, Sep 11, 2015 at 7:50 AM, Joe Conway <mail@joeconway.com> wrote:

On 09/01/2015 11:25 PM, Haribabu Kommi wrote:

If any user is granted any permissions on that object then that user
can view it's meta data of that object from the catalog tables.
To check the permissions of the user on the object, instead of
checking each and every available option, I just added a new
privilege check option called "any". If user have any permissions on
the object, the corresponding permission check function returns
true. Patch attached for the same.

Any thoughts/comments?

Thanks for working on this! Overall I like the concept and know of use
cases where it is critical and should be supported. Some comments:

Thanks for the review, I will take care of the comments in the next patch.

I didn't find any better approach other than creating policies automatically
or providing permission to superuser on system catalog tables. If everyone
feels as this is the best approach, then i will create policies for all catalog
tables in the next version.

Instead of adding or removing the rules, couldn't they just stay in
place and be governed by what the field in the database was set to? It
would also be nice if we could grant full access to roles instead of
requiring superuser to see everything. Perhaps instead of a boolean
store a role name in pg_database; anyone granted that role can see the
full catalogs.

Also, we've faced issues in the past with making catalog changes due to
fear of breaking user scripts. Instead of doubling down on that with RLS
on top of catalog tables, would it be better to move the tables to a
different schema, make them accessible only to superusers and put views
in pg_catalog?
--
Jim Nasby, Data Architect, Blue Treble Consulting, Austin TX
Experts in Analytics, Data Architecture and PostgreSQL
Data in Trouble? Get it in Treble! http://BlueTreble.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#6Robert Haas
robertmhaas@gmail.com
In reply to: Jim Nasby (#5)
Re: Multi-tenancy with RLS

On Tue, Sep 15, 2015 at 9:18 AM, Jim Nasby <Jim.Nasby@bluetreble.com> wrote:

Also, we've faced issues in the past with making catalog changes due to fear
of breaking user scripts. Instead of doubling down on that with RLS on top
of catalog tables, would it be better to move the tables to a different
schema, make them accessible only to superusers and put views in pg_catalog?

Uggh. -1 on that option from me.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#7Stephen Frost
sfrost@snowman.net
In reply to: Robert Haas (#6)
Re: Multi-tenancy with RLS

* Robert Haas (robertmhaas@gmail.com) wrote:

On Tue, Sep 15, 2015 at 9:18 AM, Jim Nasby <Jim.Nasby@bluetreble.com> wrote:

Also, we've faced issues in the past with making catalog changes due to fear
of breaking user scripts. Instead of doubling down on that with RLS on top
of catalog tables, would it be better to move the tables to a different
schema, make them accessible only to superusers and put views in pg_catalog?

Uggh. -1 on that option from me.

Yeah, -1 from here too... That way leads to madness (note that we still
haven't managed to get rid of the pg_user, et al,
backwards-compatibility views from, uh, 8.2?).

Thanks!

Stephen

#8Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Joe Conway (#3)
1 attachment(s)
Re: Multi-tenancy with RLS

On Fri, Sep 11, 2015 at 7:50 AM, Joe Conway <mail@joeconway.com> wrote:

On 09/01/2015 11:25 PM, Haribabu Kommi wrote:

If any user is granted any permissions on that object then that user
can view it's meta data of that object from the catalog tables.
To check the permissions of the user on the object, instead of
checking each and every available option, I just added a new
privilege check option called "any". If user have any permissions on
the object, the corresponding permission check function returns
true. Patch attached for the same.

Any thoughts/comments?

Thanks for working on this! Overall I like the concept and know of use
cases where it is critical and should be supported. Some comments:

Here I attached an updated version of the patch with the following changes.

Two options to the user to create catalog security on system catalog tables.

./initdb -C -D data

With the above option during initdb, the catalog security is enabled
on all shared system catalog
tables. With this way the user can achieve the catalog security at
database level. For some users
this may be enough. Currently enabling catalog security is supported
only at initdb.

ALTER DATABASE <database> WITH CATALOG SECURITY=true;
ALTER DATABASE <database> WITH CATALOG SECURITY=false;

With the above commands, user can enable/disable catalog security on a
database system catalog
tables if multi-tenancy requires at table level.

Currently setting catalog security at create database command is not
supported. And also with
alter database command to enable/disable to database where the backend
is not connected.
This is because of a restriction to execute the policy commands
without connecting to a database.

Pending things needs to be taken care:

1. select * from tenancy_user1_tbl1;
ERROR: permission denied for relation tenancy_user1_tbl1

As we are not able to see the above user table in any catalog relation
because of the multi-tenancy policies,
but if user tries to select the data of the table directly, The error
message comes as permission denied, I feel
instead of the permission denied error, in case of multi-tenancy is
enabled, the error message should be
"relation doesn't exist".

2. Correct all catalog relation policies
3. Add regression tests for all system catalog relations and views.
4. Documentation changes

Any comments?

Regards,
Hari Babu
Fujitsu Australia

Attachments:

multi-tenancy_with_rls_poc_3.patchapplication/octet-stream; name=multi-tenancy_with_rls_poc_3.patchDownload
*** 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,83 ----
  #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_auth_members.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_database.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_pltemplate.h"
  #include "catalog/pg_policy.h"
+ #include "catalog/pg_proc.h"
+ #include "catalog/pg_range.h"
+ #include "catalog/pg_replication_origin.h"
+ #include "catalog/pg_rewrite.h"
+ #include "catalog/pg_seclabel.h"
+ #include "catalog/pg_shdepend.h"
+ #include "catalog/pg_shdescription.h"
+ #include "catalog/pg_shseclabel.h"
+ #include "catalog/pg_statistic.h"
+ #include "catalog/pg_tablespace.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 ****
--- 96,117 ----
  #include "utils/lsyscache.h"
  #include "utils/memutils.h"
  #include "utils/rel.h"
+ #include "utils/snapmgr.h"
  #include "utils/syscache.h"
  
+ #define CATALOG_POLICY_STRING_SIZE 2000
+ 
  static void RangeVarCallbackForPolicy(const RangeVar *rv,
  						  Oid relid, Oid oldrelid, void *arg);
  static char parse_policy_command(const char *cmd_name);
  static Datum *policy_role_list_to_array(List *roles, int *num_roles);
  
+ static bool generate_catalog_create_policy_string(HeapTuple cache_tuple, char *buf);
+ static bool generate_catalog_drop_policy_string(HeapTuple cache_tuple, char *buf);
+ 
+ /* variable to identify whether pg_policy relcache is built or not? */
+ bool policyRelcacheBuilt = false;
+ 
  /*
   * Callback to RangeVarGetRelidExtended().
   *
***************
*** 194,199 **** RelationBuildRowSecurity(Relation relation)
--- 255,272 ----
  	MemoryContext oldcxt = CurrentMemoryContext;
  	RowSecurityDesc *volatile rsdesc = NULL;
  
+ 	/* */
+ 	if (!criticalRelcachesBuilt || !criticalSharedRelcachesBuilt)
+ 		return;
+ 
+  	if (relation->rd_id == PolicyRelationId)
+ 	{
+ 		if (policyRelcacheBuilt)
+ 			return;
+ 		else
+ 			policyRelcacheBuilt = true;
+ 	}
+ 
  	/*
  	 * Create a memory context to hold everything associated with this
  	 * relation's row security policy.  This makes it easy to clean up during
***************
*** 325,330 **** RelationBuildRowSecurity(Relation relation)
--- 398,410 ----
  		/* Delete rscxt, first making sure it isn't active */
  		MemoryContextSwitchTo(oldcxt);
  		MemoryContextDelete(rscxt);
+ 		
+ 		if (relation->rd_id == PolicyRelationId)
+ 		{
+ 			Assert(policyRelcacheBuilt == true);
+ 			policyRelcacheBuilt = false;
+ 		}
+ 
  		PG_RE_THROW();
  	}
  	PG_END_TRY();
***************
*** 407,412 **** RemovePolicyById(Oid policy_id)
--- 487,1040 ----
  	heap_close(pg_policy_rel, RowExclusiveLock);
  }
  
+ static bool 
+ generate_catalog_create_policy_string(HeapTuple tuple, char *buf)
+ {
+ 	bool can_create_policy = false;
+ 	Form_pg_class pg_class_tuple;
+ 	Oid	relationid;
+ 
+ 	pg_class_tuple = (Form_pg_class)GETSTRUCT(tuple);
+ 	relationid = HeapTupleGetOid(tuple);
+ 
+ 	if (IsSharedRelation(relationid))
+ 		return can_create_policy;
+ 
+ 	switch (relationid)
+ 	{
+ 		/*
+ 		 * Following catalog tables data is accessible to all roles. 
+ 		 * So they doesn't need any speicific RLS policies on them.
+ 		 */ 
+ 		case AggregateRelationId:
+ 		case AccessMethodRelationId:
+ 		case AccessMethodOperatorRelationId:
+ 		case AccessMethodProcedureRelationId:
+ 			break;
+ 		case AttrDefaultRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" (has_column_privilege(adrelid, adnum,'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case AttributeRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" (has_column_privilege(attrelid, attnum,'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case CastRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR has_cast_privilege(oid, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case RelationRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR has_table_privilege(oid,'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case CollationRelationId:
+ 			/*sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR (pg_get_userbyid(collowner) = current_user))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;*/
+ 			break;
+ 		case ConstraintRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR has_constraint_privilege(oid,'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case ConversionRelationId:
+ 			/*sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR (pg_get_userbyid(conowner) = current_user))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;*/
+ 			break;
+ 		case DefaultAclRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" (pg_get_userbyid(defaclrole) = current_user)",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case DependRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((classid = 2600)"
+ 				" OR (classid = 2605 AND has_cast_privilege(objid, 'any'))"
+ 				" OR (classid = 3456)"
+ 				" OR (classid = 1249 AND has_column_privilege(objid, objsubid::smallint, 'any'))"
+ 				" OR (classid = 2606 AND has_constraint_privilege(objid, 'any'))"
+ 				" OR (classid = 2607)"
+ 				" OR (classid = 1262 AND has_database_privilege(objid, 'any'))"
+ 				" OR (classid = 1247 AND has_type_privilege(objid, 'any'))"
+ 				" OR (classid = 3079)"
+ 				" OR (classid = 3466)"
+ 				" OR (classid = 2328 AND has_foreign_data_wrapper_privilege(objid, 'any'))"
+ 				" OR (classid = 1259 AND has_table_privilege(objid,'any'))"
+ 				" OR (classid = 1255 AND has_function_privilege(objid, 'any'))"
+ 				" OR (classid = 2601)"
+ 				" OR (classid = 2602)"
+ 				" OR (classid = 2603)"
+ 				" OR (classid = 2604 AND has_column_default_privilege(objid, 'any'))"
+ 				" OR (classid = 2612 AND has_language_privilege(objid, 'any'))"
+ 				" OR (classid = 2613)"
+ 				" OR (classid = 2616)"
+ 				" OR (classid = 2617)"
+ 				" OR (classid = 2753)"
+ 				" OR (classid = 3501)"
+ 				" OR (classid = 3466)"
+ 				" OR (classid = 3079)"
+ 				" OR (classid = 3256 AND has_policy_privilege(objid, 'any'))"
+ 				" OR (classid = 1260 AND pg_has_role(objid, 'any'))"
+ 				" OR (classid = 2618)"
+ 				" OR (classid = 2615 AND has_schema_privilege(objid, 'any'))"
+ 				" OR (classid = 1417 AND has_server_privilege(objid, 'any'))"
+ 				" OR (classid = 1213 AND has_tablespace_privilege(objid, 'any'))"
+ 				" OR (classid = 3600)"
+ 				" OR (classid = 3601)"
+ 				" OR (classid = 3602)"
+ 				" OR (classid = 3764)"
+ 				" OR (classid = 3576)"
+ 				" OR (classid = 2620 AND has_trigger_privilege(objid, 'any'))"
+ 				" OR (classid = 1418 AND has_user_mapping_privilege(objid, 'any')))", 
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case DescriptionRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((classoid = 2600)"
+ 				" OR (classoid = 2605 AND has_cast_privilege(objoid, 'any'))"
+ 				" OR (classoid = 3456)"
+ 				" OR (classoid = 1249 AND has_column_privilege(objoid, objsubid::smallint, 'any'))"
+ 				" OR (classoid = 2606 AND has_constraint_privilege(objoid, 'any'))"
+ 				" OR (classoid = 2607)"
+ 				" OR (classoid = 1262 AND has_database_privilege(objoid, 'any'))"
+ 				" OR (classoid = 1247 AND has_type_privilege(objoid, 'any'))"
+ 				" OR (classoid = 3079)"
+ 				" OR (classoid = 3466)"
+ 				" OR (classoid = 2328 AND has_foreign_data_wrapper_privilege(objoid, 'any'))"
+ 				" OR (classoid = 1259 AND has_table_privilege(objoid,'any'))"
+ 				" OR (classoid = 1255 AND has_function_privilege(objoid, 'any'))"
+ 				" OR (classoid = 2613)"
+ 				" OR (classoid = 2616)"
+ 				" OR (classoid = 2617)"
+ 				" OR (classoid = 2753)"
+ 				" OR (classoid = 2995)"
+ 				" OR (classoid = 3256 AND has_policy_privilege(objoid, 'any'))"
+ 				" OR (classoid = 1260 AND pg_has_role(objoid, 'any'))"
+ 				" OR (classoid = 2618)"
+ 				" OR (classoid = 2615 AND has_schema_privilege(objoid, 'any'))"
+ 				" OR (classoid = 1417 AND has_server_privilege(objoid, 'any'))"
+ 				" OR (classoid = 1213 AND has_tablespace_privilege(objoid, 'any'))"
+ 				" OR (classoid = 3541)"
+ 				" OR (classoid = 3600)"
+ 				" OR (classoid = 3601)"
+ 				" OR (classoid = 3602)"
+ 				" OR (classoid = 3764)"
+ 				" OR (classoid = 3576)"
+ 				" OR (classoid = 2620 AND has_trigger_privilege(objoid, 'any')))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case EnumRelationId:
+ 			break;
+ 		case EventTriggerRelationId:
+ 			break;
+ 		case ExtensionRelationId:
+ 			break;
+ 		case ForeignDataWrapperRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR has_foreign_data_wrapper_privilege(oid,'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case ForeignServerRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR has_server_privilege(oid,'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case ForeignTableRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" (has_table_privilege(ftrelid, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case IndexRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" (has_table_privilege(indrelid, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case InheritsRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" (has_table_privilege(inhrelid, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case LanguageRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR has_language_privilege(oid, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case LargeObjectRelationId:
+ 			break;
+ 		case LargeObjectMetadataRelationId:
+ 			break;
+ 		case NamespaceRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR has_schema_privilege(oid, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case OperatorClassRelationId:
+ 			/*sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR (pg_get_userbyid(opcowner) = current_user))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;*/
+ 			break;
+ 		case OperatorRelationId:
+ 			/*sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR (pg_get_userbyid(oprowner) = current_user))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;*/
+ 			break;
+ 		case OperatorFamilyRelationId:
+ 			/*sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR (pg_get_userbyid(opfowner) = current_user))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;*/
+ 			break;
+ 		case PolicyRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR has_table_privilege(polrelid, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case ProcedureRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR has_function_privilege(oid, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case RangeRelationId:
+ 			break;
+ 		case RewriteRelationId:
+ 			break;
+ 		case SecLabelRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((classoid = 2600)"
+ 				" OR (classoid = 1249 AND has_column_privilege(objoid, objsubid::smallint, 'any'))"
+ 				" OR (classoid = 1262 AND has_database_privilege(objoid, 'any'))"
+ 				" OR (classoid = 1247 AND has_type_privilege(objoid, 'any'))"
+ 				" OR (classoid = 1259 AND has_table_privilege(objoid,'any'))"
+ 				" OR (classoid = 1255 AND has_function_privilege(objoid, 'any'))"
+ 				" OR (classoid = 2612 AND has_language_privilege(objoid, 'any'))"
+ 				" OR (classoid = 1260 AND pg_has_role(objoid, 'any'))"
+ 				" OR (classoid = 2613)"
+ 				" OR (classoid = 3466)"
+ 				" OR (classoid = 1213 AND has_tablespace_privilege(objoid, 'any')))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case StatisticRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((starelid < 16384) OR has_column_privilege(starelid, staattnum, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case TransformRelationId:
+ 			break;
+ 		case TriggerRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR has_table_privilege(tgrelid, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case TSConfigRelationId:
+ 			break;
+ 		case TSConfigMapRelationId:
+ 			break;
+ 		case TSDictionaryRelationId:
+ 			break;
+ 		case TSParserRelationId:
+ 			break;
+ 		case TSTemplateRelationId:
+ 			break;
+ 		case TypeRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR has_type_privilege(oid, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case UserMappingRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR pg_has_role(umuser, 'any')"
+ 				" OR has_server_privilege(umserver, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		default:
+ 			can_create_policy = false;
+ 			break;
+ 	}
+ 
+ 	return can_create_policy;
+ }
+ 
+ static bool
+ generate_catalog_drop_policy_string(HeapTuple tuple, char *buf)
+ {
+ 	bool can_create_policy = false;
+ 	Form_pg_class pg_class_tuple;
+ 	Oid	relationid;
+ 
+ 	pg_class_tuple = (Form_pg_class)GETSTRUCT(tuple);
+ 	relationid = HeapTupleGetOid(tuple);
+ 
+ 	if (IsSharedRelation(relationid))
+ 		return can_create_policy;
+ 
+ 	switch (relationid)
+ 	{
+ 		/*
+ 		* Following catalog tables data is accessible to all roles.
+ 		* So they doesn't need any speicific RLS policies on them.
+ 		*/
+ 	case AggregateRelationId:
+ 	case AccessMethodRelationId:
+ 	case AccessMethodOperatorRelationId:
+ 	case AccessMethodProcedureRelationId:
+ 	case CollationRelationId:
+ 	case ConversionRelationId:
+ 	case EnumRelationId:
+ 	case EventTriggerRelationId:
+ 	case ExtensionRelationId:
+ 	case LargeObjectRelationId:
+ 	case LargeObjectMetadataRelationId:
+ 	case OperatorClassRelationId:
+ 	case OperatorRelationId:
+ 	case OperatorFamilyRelationId:
+ 	case RangeRelationId:
+ 	case RewriteRelationId:
+ 	case TransformRelationId:
+ 	case TSConfigRelationId:
+ 	case TSConfigMapRelationId:
+ 	case TSDictionaryRelationId:
+ 	case TSParserRelationId:
+ 	case TSTemplateRelationId:
+ 		break;
+ 
+ 	case AttrDefaultRelationId:
+ 	case AttributeRelationId:
+ 	case CastRelationId:
+ 	case RelationRelationId:
+ 	case ConstraintRelationId:
+ 	case DefaultAclRelationId:
+ 	case DependRelationId:
+ 	case DescriptionRelationId:
+ 	case ForeignDataWrapperRelationId:
+ 	case ForeignServerRelationId:
+ 	case ForeignTableRelationId:
+ 	case IndexRelationId:
+ 	case InheritsRelationId:
+ 	case LanguageRelationId:
+ 	case NamespaceRelationId:
+ 	case PolicyRelationId:
+ 	case ProcedureRelationId:
+ 	case SecLabelRelationId:
+ 	case StatisticRelationId:
+ 	case TriggerRelationId:
+ 	case TypeRelationId:
+ 	case UserMappingRelationId:
+ 		sprintf(buf, "drop policy %s_read_own_data on %s",
+ 			pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 		can_create_policy = true;
+ 		break;
+ 
+ 	default:
+ 		can_create_policy = false;
+ 		break;
+ 	}
+ 
+ 	return can_create_policy;
+ }
+ 
+ /*
+  * CreateCatalogPolicy -
+  *	 handles the execution of the ALTER DATBASE ROW LEVEL SECUTIRY = TRUE command.
+  */
+ void
+ CreateCatalogPolicy()
+ {
+ 	Relation	rel;
+ 	ScanKeyData scankey;
+ 	SysScanDesc scan;
+ 	HeapTuple	tuple;
+ 	bool allow_sytem_table_mods_old;
+ 	char *buf;
+ 
+ 	/*
+ 	 * Get all catalog relations from pg_class system table and
+ 	 * enable the row level security along with the catalog policy
+ 	 * command.
+ 	 */
+ 	SPI_connect();
+ 	PushActiveSnapshot(GetTransactionSnapshot());
+ 
+ 	rel = heap_open(RelationRelationId, RowExclusiveLock);
+ 	ScanKeyInit(&scankey,
+ 				ObjectIdAttributeNumber,
+ 				BTLessStrategyNumber, F_OIDLT,
+ 				ObjectIdGetDatum(FirstNormalObjectId));
+ 	scan = systable_beginscan(rel, ClassOidIndexId, true,
+ 								NULL, 1, &scankey);
+ 
+ 	buf = palloc(CATALOG_POLICY_STRING_SIZE);
+ 	allow_sytem_table_mods_old = allowSystemTableMods;
+ 	allowSystemTableMods = true;
+ 
+ 	PG_TRY();
+ 	{
+ 		while ((tuple = systable_getnext(scan)) != NULL)
+ 		{
+ 			int			ret;
+ 			HeapTuple	cache_tuple;
+ 			Form_pg_class pg_class_tuple;
+ 			bool can_create_policy;
+ 
+ 			pg_class_tuple = (Form_pg_class)GETSTRUCT(tuple);
+ 			if (pg_class_tuple->relkind != RELKIND_RELATION)
+ 				continue;
+ 				
+ 			cache_tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(HeapTupleGetOid(tuple)));
+ 
+ 			if (!HeapTupleIsValid(cache_tuple))
+ 				elog(ERROR, "cache lookup failed for relation %u", HeapTupleGetOid(tuple));
+ 
+ 			can_create_policy = generate_catalog_create_policy_string(cache_tuple, buf);
+ 			if (!can_create_policy)
+ 			{
+ 				heap_freetuple(cache_tuple);
+ 				continue;
+ 			}
+ 	
+ 			ret = SPI_execute(buf, false, 0);
+ 			if (ret != SPI_OK_UTILITY)
+ 				elog(ERROR, "Creating policy failed : error code %d", ret);
+ 
+ 			((Form_pg_class)GETSTRUCT(cache_tuple))->relrowsecurity = true;
+ 			heap_inplace_update(rel, cache_tuple);
+ 
+ 			heap_freetuple(cache_tuple);
+ 		}
+ 	}
+ 	PG_CATCH();
+ 	{
+ 		allowSystemTableMods = allow_sytem_table_mods_old;
+ 		PG_RE_THROW();
+ 	}
+ 	PG_END_TRY();
+ 
+ 	allowSystemTableMods = allow_sytem_table_mods_old;
+ 	pfree(buf);
+ 
+ 	systable_endscan(scan);
+ 	heap_close(rel, NoLock);
+ 
+ 	SPI_finish();
+ 	PopActiveSnapshot();
+ }
+ 
+ /*
+  * RemoveCatalogPolicy -
+  *	 handles the execution of the ALTER DATBASE ROW LEVEL SECUTIRY = FALSE command.
+  */
+ void
+ RemoveCatalogPolicy()
+ {
+ 	Relation	rel;
+ 	ScanKeyData scankey;
+ 	SysScanDesc scan;
+ 	HeapTuple	tuple;
+ 	Form_pg_class pg_class_tuple;
+ 	bool allow_sytem_table_mods_old;
+ 	char		 *buf;
+ 
+ 	/*
+ 	* Get all catalog relations from pg_class system table and
+ 	* enable the row level security along with the catalog policy
+ 	* command.
+ 	*/
+ 	SPI_connect();
+ 	PushActiveSnapshot(GetTransactionSnapshot());
+ 
+ 	rel = heap_open(RelationRelationId, RowExclusiveLock);
+ 	ScanKeyInit(&scankey,
+ 		ObjectIdAttributeNumber,
+ 		BTLessStrategyNumber, F_OIDLT,
+ 		ObjectIdGetDatum(FirstNormalObjectId));
+ 	scan = systable_beginscan(rel, ClassOidIndexId, true,
+ 		NULL, 1, &scankey);
+ 
+ 	buf = palloc(CATALOG_POLICY_STRING_SIZE);
+ 
+ 	allow_sytem_table_mods_old = allowSystemTableMods;
+ 	allowSystemTableMods = true;
+ 
+ 	PG_TRY();
+ 	{
+ 		while ((tuple = systable_getnext(scan)) != NULL)
+ 		{
+ 			int			ret;
+ 			HeapTuple	cache_tuple;
+ 			bool		can_drop_policy;
+ 
+ 			pg_class_tuple = (Form_pg_class)GETSTRUCT(tuple);
+ 			if (pg_class_tuple->relkind != RELKIND_RELATION)
+ 				continue;
+ 
+ 			cache_tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(HeapTupleGetOid(tuple)));
+ 			if (!HeapTupleIsValid(cache_tuple))
+ 				elog(ERROR, "cache lookup failed for relation %u", HeapTupleGetOid(tuple));
+ 
+ 			can_drop_policy = generate_catalog_drop_policy_string(cache_tuple, buf);
+ 			if (!can_drop_policy)
+ 			{
+ 				heap_freetuple(cache_tuple);
+ 				continue;
+ 			}			
+ 
+ 			ret = SPI_execute(buf, false, 0);
+ 			if (ret != SPI_OK_UTILITY)
+ 				elog(ERROR, "Creating policy failed : error code %d", ret);
+ 
+ 			((Form_pg_class)GETSTRUCT(cache_tuple))->relrowsecurity = false;
+ 			heap_inplace_update(rel, cache_tuple);
+ 
+ 			heap_freetuple(cache_tuple);
+ 		}
+ 	}
+ 	PG_CATCH();
+ 	{
+ 		allowSystemTableMods = allow_sytem_table_mods_old;
+ 		PG_RE_THROW();
+ 	}
+ 	PG_END_TRY();
+ 
+ 	allowSystemTableMods = allow_sytem_table_mods_old;
+ 
+ 	pfree(buf);
+ 	systable_endscan(scan);
+ 	heap_close(rel, NoLock);
+ 
+ 	SPI_finish();
+ 	PopActiveSnapshot();
+ }
+ 
  /*
   * CreatePolicy -
   *	 handles the execution of the CREATE POLICY command.
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 3436,3441 **** ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode)
--- 3436,3450 ----
  	{
  		AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
  
+ 		/*
+ 		 * ALTER table on sytem catalog tables is possible only when user specifies
+ 		 * CATALOG SECURITY on system catalog tables. To avoid the error in the 
+ 		 * AlterTableCreateToastTable function for system catalog tables, the system
+ 		 * catalog tables are ignored for the toast table creation.
+ 		 */
+ 		if (!IsUnderPostmaster && IsSharedRelation(tab->relid))
+ 			continue;
+ 
  		if (tab->relkind == RELKIND_RELATION ||
  			tab->relkind == RELKIND_MATVIEW)
  			AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 8881,8886 **** createdb_opt_name:
--- 8881,8887 ----
  			| OWNER							{ $$ = pstrdup($1); }
  			| TABLESPACE					{ $$ = pstrdup($1); }
  			| TEMPLATE						{ $$ = pstrdup($1); }
+ 			| CATALOG_P SECURITY				{ $$ = pstrdup("catalog_security"); }
  		;
  
  /*
*** a/src/backend/utils/adt/acl.c
--- b/src/backend/utils/adt/acl.c
***************
*** 17,27 ****
  #include <ctype.h>
  
  #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 <ctype.h>
  
  #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"
***************
*** 2031,2036 **** convert_table_priv_string(text *priv_type_text)
--- 2040,2050 ----
  		{"REFERENCES WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
  		{"TRIGGER", ACL_TRIGGER},
  		{"TRIGGER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
+ 		{"ANY", ACL_SELECT | ACL_GRANT_OPTION_FOR(ACL_SELECT) |
+ 				ACL_INSERT | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
+ 				ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_DELETE | ACL_GRANT_OPTION_FOR(ACL_DELETE) |
+ 				ACL_TRUNCATE | ACL_GRANT_OPTION_FOR(ACL_TRUNCATE) | ACL_REFERENCES |
+ 				ACL_GRANT_OPTION_FOR(ACL_REFERENCES) | ACL_TRIGGER | ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
  		{"RULE", 0},			/* ignore old RULE privileges */
  		{"RULE WITH GRANT OPTION", 0},
  		{NULL, 0}
***************
*** 2243,2248 **** convert_sequence_priv_string(text *priv_type_text)
--- 2257,2263 ----
  		{"USAGE", ACL_USAGE},
  		{"SELECT", ACL_SELECT},
  		{"UPDATE", ACL_UPDATE},
+ 		{"ANY", ACL_USAGE | ACL_SELECT | ACL_UPDATE},
  		{NULL, 0}
  	};
  
***************
*** 2858,2863 **** convert_column_priv_string(text *priv_type_text)
--- 2873,2881 ----
  		{"UPDATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_UPDATE)},
  		{"REFERENCES", ACL_REFERENCES},
  		{"REFERENCES WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
+ 		{"ANY", ACL_SELECT | ACL_GRANT_OPTION_FOR(ACL_SELECT) | ACL_INSERT |
+ 				ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
+ 				ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_REFERENCES | ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
  		{NULL, 0}
  	};
  
***************
*** 3055,3060 **** convert_database_priv_string(text *priv_type_text)
--- 3073,3081 ----
  		{"TEMP WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP)},
  		{"CONNECT", ACL_CONNECT},
  		{"CONNECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
+ 		{"ANY", ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE) |
+ 									ACL_CREATE_TEMP | ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CREATE_TEMP |
+ 									ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CONNECT | ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
  		{NULL, 0}
  	};
  
***************
*** 3238,3243 **** convert_foreign_data_wrapper_priv_string(text *priv_type_text)
--- 3259,3265 ----
  	static const priv_map foreign_data_wrapper_priv_map[] = {
  		{"USAGE", ACL_USAGE},
  		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+ 		{"ANY", ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
  		{NULL, 0}
  	};
  
***************
*** 3438,3443 **** convert_function_priv_string(text *priv_type_text)
--- 3460,3466 ----
  	static const priv_map function_priv_map[] = {
  		{"EXECUTE", ACL_EXECUTE},
  		{"EXECUTE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_EXECUTE)},
+ 		{"ANY", ACL_EXECUTE | ACL_GRANT_OPTION_FOR(ACL_EXECUTE)},
  		{NULL, 0}
  	};
  
***************
*** 3629,3634 **** convert_language_priv_string(text *priv_type_text)
--- 3652,3658 ----
  	static const priv_map language_priv_map[] = {
  		{"USAGE", ACL_USAGE},
  		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+ 		{"ANY", ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
  		{NULL, 0}
  	};
  
***************
*** 3822,3827 **** convert_schema_priv_string(text *priv_type_text)
--- 3846,3852 ----
  		{"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
  		{"USAGE", ACL_USAGE},
  		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+ 		{"ANY", ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE) | ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
  		{NULL, 0}
  	};
  
***************
*** 4004,4009 **** convert_server_priv_string(text *priv_type_text)
--- 4029,4035 ----
  	static const priv_map server_priv_map[] = {
  		{"USAGE", ACL_USAGE},
  		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+ 		{"ANY", ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
  		{NULL, 0}
  	};
  
***************
*** 4186,4191 **** convert_tablespace_priv_string(text *priv_type_text)
--- 4212,4218 ----
  	static const priv_map tablespace_priv_map[] = {
  		{"CREATE", ACL_CREATE},
  		{"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
+ 		{"ANY", ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE)},
  		{NULL, 0}
  	};
  
***************
*** 4385,4390 **** convert_type_priv_string(text *priv_type_text)
--- 4412,4418 ----
  	static const priv_map type_priv_map[] = {
  		{"USAGE", ACL_USAGE},
  		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+ 		{"ANY", ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
  		{NULL, 0}
  	};
  
***************
*** 4566,4571 **** convert_role_priv_string(text *priv_type_text)
--- 4594,4600 ----
  		{"USAGE WITH ADMIN OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
  		{"MEMBER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
  		{"MEMBER WITH ADMIN OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
+ 		{"ANY", ACL_USAGE | ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE)},
  		{NULL, 0}
  	};
  
***************
*** 5247,5249 **** 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/backend/utils/misc/rls.c
--- b/src/backend/utils/misc/rls.c
***************
*** 58,67 **** check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
  	bool		relforcerowsecurity;
  	Oid			user_id = checkAsUser ? checkAsUser : GetUserId();
  
- 	/* Nothing to do for built-in relations */
- 	if (relid < FirstNormalObjectId)
- 		return RLS_NONE;
- 
  	tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
  	if (!HeapTupleIsValid(tuple))
  		return RLS_NONE;
--- 58,63 ----
*** a/src/bin/initdb/initdb.c
--- b/src/bin/initdb/initdb.c
***************
*** 132,137 **** static bool do_sync = true;
--- 132,138 ----
  static bool sync_only = false;
  static bool show_setting = false;
  static bool data_checksums = false;
+ static bool shared_catalog_security = false;
  static char *xlog_dir = "";
  
  
***************
*** 186,191 **** static char *authwarning = NULL;
--- 187,193 ----
   */
  static const char *boot_options = "-F";
  static const char *backend_options = "--single -F -O -c search_path=pg_catalog -c exit_on_error=true";
+ static const char *catalog_security_options = "--single -F -O -c search_path=pg_catalog -c exit_on_error=true -c allow_system_table_mods=true";
  
  static const char *subdirs[] = {
  	"global",
***************
*** 255,260 **** static void setup_dictionary(void);
--- 257,263 ----
  static void setup_privileges(void);
  static void set_info_version(void);
  static void setup_schema(void);
+ static void setup_shared_catalog_security(void);
  static void load_plpgsql(void);
  static void vacuum_db(void);
  static void make_template0(void);
***************
*** 2252,2257 **** setup_schema(void)
--- 2255,2357 ----
  }
  
  /*
+  * setup shared catalog security by defining policies
+  */
+ static void
+ setup_shared_catalog_security(void)
+ {
+ 	PG_CMD_DECL;
+ 	char	  **line;
+ 	char	  **policy_lines;
+ 	static const char *pg_shared_catalog_security_setup[] = {
+ 		/* AuthMemRelationId */
+ 		/*
+ 		* Currently there is no policy needed for this table, so
+ 		* leave it as it is.
+ 		*/
+ 
+ 		/* AuthIdRelationId */
+ 		"create policy pg_authid_read_own_data on pg_authid for select using"
+ 		" ((oid < 16384) OR pg_has_role(oid, 'any'));\n",
+ 
+ 		"alter table pg_authid enable row level security;\n",
+ 
+ 		/* DatabaseRelationId */
+ 		"create policy pg_database_read_own_data on pg_database for select using"
+ 		" ((oid < 16384) OR has_database_privilege(oid,'any'));\n",
+ 
+ 		"alter table pg_database enable row level security;\n",
+ 
+ 		/* DbRoleSettingRelationId */
+ 		"create policy pg_db_role_setting_read_own_data on pg_db_role_setting for select using"
+ 		" (pg_get_userbyid(setrole) = current_user);\n",
+ 
+ 		"alter table pg_database enable row level security;\n",
+ 
+ 		/* PLTemplateRelationId */
+ 		/*
+ 		* Currently there is no policy needed for this table, so
+ 		* leave it as it is.
+ 		*/
+ 
+ 		/* ReplicationOriginRelationId */
+ 		"create policy pg_replication_origin_read_own_data on pg_replication_origin for select using"
+ 		" (pg_has_role(roident, 'any'));\n",
+ 
+ 		"alter table pg_replication_origin enable row level security;\n",
+ 
+ 		/* SharedDependRelationId */
+ 		"create policy pg_shdepend_read_own_data on pg_shdepend for select using"
+ 		" ((classid = 1262 AND has_database_privilege(objid, 'any'))"
+ 		" OR (classid = 1260 AND pg_has_role(objid, 'any'))"
+ 		" OR (classid = 1213 AND has_tablespace_privilege(objid, 'any')));\n",
+ 
+ 		"alter table pg_shdepend enable row level security;\n",
+ 
+ 		/* SharedDescriptionRelationId */
+ 		"create policy pg_shdescription_read_own_data on pg_shdescription for select using"
+ 		" ((classoid = 1262 AND has_database_privilege(objoid, 'any'))"
+ 		" OR (classoid = 1260 AND pg_has_role(objoid, 'any'))"
+ 		" OR (classoid = 1213 AND has_tablespace_privilege(objoid, 'any')));\n",
+ 
+ 		"alter table pg_shdescription enable row level security;\n",
+ 
+ 		/* SharedSecLabelRelationId */
+ 		"create policy pg_shseclabel_read_own_data on pg_shseclabel for select using"
+ 		" ((classoid = 1262 AND has_database_privilege(objoid, 'any'))"
+ 		" OR (classoid = 1260 AND pg_has_role(objoid, 'any'))"
+ 		" OR (classoid = 1213 AND has_tablespace_privilege(objoid, 'any')));\n",
+ 
+ 		"alter table pg_shseclabel enable row level security;\n",
+ 
+ 		/* TableSpaceRelationId */
+ 		"create policy pg_tablespace_read_own_data on pg_tablespace for select using"
+ 		" ((oid < 16384) OR has_tablespace_privilege(oid, 'any'));\n",
+ 
+ 		"alter table pg_tablespace enable row level security;\n",
+ 
+ 		NULL
+ 	};
+ 
+ 	fputs(_("creating shared catalog security policy ... "), stdout);
+ 	fflush(stdout);
+ 
+ 	snprintf(cmd, sizeof(cmd),
+ 		"\"%s\" %s template1 >%s",
+ 		backend_exec, catalog_security_options,
+ 		DEVNULL);
+ 
+ 	PG_CMD_OPEN;
+ 
+ 	for (line = pg_shared_catalog_security_setup; *line != NULL; line++)
+ 		PG_CMD_PUTS(*line);
+ 
+ 	PG_CMD_CLOSE;
+ 
+ 	check_ok();
+ }
+ 
+ /*
   * load PL/pgsql server-side language
   */
  static void
***************
*** 2768,2773 **** usage(const char *progname)
--- 2868,2875 ----
  	printf(_("\nLess commonly used options:\n"));
  	printf(_("  -d, --debug               generate lots of debugging output\n"));
  	printf(_("  -k, --data-checksums      use data page checksums\n"));
+ 	printf(_("  -C, --shared-catalog-security\n"
+ 			"						      use shared catalog security\n"));
  	printf(_("  -L DIRECTORY              where to find the input files\n"));
  	printf(_("  -n, --noclean             do not clean up after errors\n"));
  	printf(_("  -N, --nosync              do not wait for changes to be written safely to disk\n"));
***************
*** 3365,3377 **** initialize_data_directory(void)
  
  	setup_schema();
  
  	load_plpgsql();
  
  	vacuum_db();
  
  	make_template0();
  
! 	make_postgres();
  }
  
  
--- 3467,3482 ----
  
  	setup_schema();
  
+ 	if (shared_catalog_security)
+ 		setup_shared_catalog_security();
+ 	
  	load_plpgsql();
  
  	vacuum_db();
  
  	make_template0();
  
! 	make_postgres();		
  }
  
  
***************
*** 3405,3410 **** main(int argc, char *argv[])
--- 3510,3516 ----
  		{"sync-only", no_argument, NULL, 'S'},
  		{"xlogdir", required_argument, NULL, 'X'},
  		{"data-checksums", no_argument, NULL, 'k'},
+ 		{"shared-catalog-security", no_argument, NULL, 'C' },
  		{NULL, 0, NULL, 0}
  	};
  
***************
*** 3445,3451 **** main(int argc, char *argv[])
  
  	/* process command-line options */
  
! 	while ((c = getopt_long(argc, argv, "dD:E:kL:nNU:WA:sST:X:", long_options, &option_index)) != -1)
  	{
  		switch (c)
  		{
--- 3551,3557 ----
  
  	/* process command-line options */
  
! 	while ((c = getopt_long(argc, argv, "dD:E:kCL:nNU:WA:sST:X:", long_options, &option_index)) != -1)
  	{
  		switch (c)
  		{
***************
*** 3497,3502 **** main(int argc, char *argv[])
--- 3603,3611 ----
  			case 'k':
  				data_checksums = true;
  				break;
+ 			case 'C':
+ 				shared_catalog_security = true;
+ 				break;
  			case 'L':
  				share_path = pg_strdup(optarg);
  				break;
*** 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,322 ----
+ -- Create roles that are used by the following tests
+ create role tenancy_user1 login createdb;
+ create role tenancy_user2 login createdb;
+ create schema tenancy_user1;
+ grant all on schema tenancy_user1 to tenancy_user1;
+ create schema tenancy_user2;
+ grant all on schema tenancy_user2 to tenancy_user2;
+ alter database regression with catalog security = true;
+ -- create objects realted to tenacy_user1
+ SET SESSION ROLE tenancy_user1;
+ create table public.tenancy_user1_tbl1 (tenancy_user1_tbl1_column1 serial,
+ 								tenancy_user1_tbl1_column2 char(10) default 'FUJITSU',
+ 								tenancy_user1_tbl1_column3 int);
+ create index tenancy_user1_tbl1_idx on tenancy_user1_tbl1 (tenancy_user1_tbl1_column3);
+ insert into tenancy_user1_tbl1(tenancy_user1_tbl1_column3) values(1);
+ create table tenancy_user1.tenancy_user1_tbl2 (tenancy_user1_tbl2_column1 serial,
+ 								tenancy_user1_tbl2_column2 char(10) default 'FUJITSU',
+ 								tenancy_user1_tbl2_column3 int);
+ create index tenancy_user1_tbl2_idx on tenancy_user1.tenancy_user1_tbl2 (tenancy_user1_tbl2_column3);
+ create view public.tenancy_user1_view1 as select * from tenancy_user1_tbl1;
+ create materialized view public.tenancy_user1_matview1 as select * from tenancy_user1_tbl1;
+ select * from tenancy_user1_tbl1;
+  tenancy_user1_tbl1_column1 | tenancy_user1_tbl1_column2 | tenancy_user1_tbl1_column3 
+ ----------------------------+----------------------------+----------------------------
+                           1 | FUJITSU                    |                          1
+ (1 row)
+ 
+ select * from tenancy_user1_view1;
+  tenancy_user1_tbl1_column1 | tenancy_user1_tbl1_column2 | tenancy_user1_tbl1_column3 
+ ----------------------------+----------------------------+----------------------------
+                           1 | FUJITSU                    |                          1
+ (1 row)
+ 
+ select * from tenancy_user1_matview1;
+  tenancy_user1_tbl1_column1 | tenancy_user1_tbl1_column2 | tenancy_user1_tbl1_column3 
+ ----------------------------+----------------------------+----------------------------
+                           1 | FUJITSU                    |                          1
+ (1 row)
+ 
+ 								
+ -- verify all system catalogs related to the objects created by tenancy_user1
+ select relname from pg_class where relname = 'tenancy_user1_tbl1';
+       relname       
+ --------------------
+  tenancy_user1_tbl1
+ (1 row)
+ 
+ select relname from pg_class where relname = 'tenancy_user1_tbl1_idx';
+         relname         
+ ------------------------
+  tenancy_user1_tbl1_idx
+ (1 row)
+ 
+ select relname from pg_class where relname = 'tenancy_user1_view1';
+        relname       
+ ---------------------
+  tenancy_user1_view1
+ (1 row)
+ 
+ select relname from pg_class where relname = 'tenancy_user1_matview1';
+         relname         
+ ------------------------
+  tenancy_user1_matview1
+ (1 row)
+ 
+ select attname from pg_attribute where attname = 'tenancy_user1_tbl1_column1';
+           attname           
+ ----------------------------
+  tenancy_user1_tbl1_column1
+  tenancy_user1_tbl1_column1
+  tenancy_user1_tbl1_column1
+ (3 rows)
+ 
+ select adnum, adsrc from pg_attrdef where adrelid in (select oid from pg_class where relname = 'tenancy_user1_tbl1');
+  adnum |                                 adsrc                                  
+ -------+------------------------------------------------------------------------
+      1 | nextval('tenancy_user1_tbl1_tenancy_user1_tbl1_column1_seq'::regclass)
+      2 | 'FUJITSU'::bpchar
+ (2 rows)
+ 
+ select indnatts, indkey from pg_index where indrelid in (select oid from pg_class where relname = 'tenancy_user1_tbl1');
+  indnatts | indkey 
+ ----------+--------
+         1 | 3
+ (1 row)
+ 
+ select nspname, nspowner from pg_namespace where nspname = 'tenancy_user1';
+     nspname    | nspowner 
+ ---------------+----------
+  tenancy_user1 |       10
+ (1 row)
+ 
+ 		
+ -- verify all system views related to the objects created by tenancy_user1
+ --select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user1_tbl1';
+ --select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user1_tbl2' and schemaname = 'tenancy_user1';
+ 		
+ RESET ROLE;
+ -- create objects realted to tenacy_user2
+ SET SESSION ROLE tenancy_user2;
+ create table public.tenancy_user2_tbl1 (tenancy_user2_tbl1_column1 serial,
+ 								tenancy_user2_tbl1_column2 char(10) default 'FUJITSU',
+ 								tenancy_user2_tbl1_column3 int);
+ create index tenancy_user2_tbl1_idx on tenancy_user2_tbl1 (tenancy_user2_tbl1_column3);
+ insert into tenancy_user2_tbl1(tenancy_user2_tbl1_column3) values(1);
+ create table tenancy_user2.tenancy_user2_tbl2 (tenancy_user2_tbl2_column1 serial,
+ 								tenancy_user2_tbl2_column2 char(10) default 'FUJITSU',
+ 								tenancy_user2_tbl2_column3 int);
+ create index tenancy_user2_tbl2_idx on tenancy_user2.tenancy_user2_tbl2 (tenancy_user2_tbl2_column3);
+ create view public.tenancy_user2_view1 as select * from tenancy_user2_tbl1;
+ create materialized view public.tenancy_user2_matview1 as select * from tenancy_user2_tbl1;
+ select * from tenancy_user2_tbl1;
+  tenancy_user2_tbl1_column1 | tenancy_user2_tbl1_column2 | tenancy_user2_tbl1_column3 
+ ----------------------------+----------------------------+----------------------------
+                           1 | FUJITSU                    |                          1
+ (1 row)
+ 
+ select * from tenancy_user2_view1;
+  tenancy_user2_tbl1_column1 | tenancy_user2_tbl1_column2 | tenancy_user2_tbl1_column3 
+ ----------------------------+----------------------------+----------------------------
+                           1 | FUJITSU                    |                          1
+ (1 row)
+ 
+ select * from tenancy_user2_matview1;
+  tenancy_user2_tbl1_column1 | tenancy_user2_tbl1_column2 | tenancy_user2_tbl1_column3 
+ ----------------------------+----------------------------+----------------------------
+                           1 | FUJITSU                    |                          1
+ (1 row)
+ 
+ -- verify all system catalogs related to the objects created by tenancy_user2
+ select relname from pg_class where relname = 'tenancy_user2_tbl1';
+       relname       
+ --------------------
+  tenancy_user2_tbl1
+ (1 row)
+ 
+ select relname from pg_class where relname = 'tenancy_user2_tbl1_idx';
+         relname         
+ ------------------------
+  tenancy_user2_tbl1_idx
+ (1 row)
+ 
+ select relname from pg_class where relname = 'tenancy_user2_view1';
+        relname       
+ ---------------------
+  tenancy_user2_view1
+ (1 row)
+ 
+ select relname from pg_class where relname = 'tenancy_user2_matview1';
+         relname         
+ ------------------------
+  tenancy_user2_matview1
+ (1 row)
+ 
+ select attname from pg_attribute where attname = 'tenancy_user2_tbl1_column1';
+           attname           
+ ----------------------------
+  tenancy_user2_tbl1_column1
+  tenancy_user2_tbl1_column1
+  tenancy_user2_tbl1_column1
+ (3 rows)
+ 
+ select adnum, adsrc from pg_attrdef where adrelid in (select oid from pg_class where relname = 'tenancy_user2_tbl1');
+  adnum |                                 adsrc                                  
+ -------+------------------------------------------------------------------------
+      1 | nextval('tenancy_user2_tbl1_tenancy_user2_tbl1_column1_seq'::regclass)
+      2 | 'FUJITSU'::bpchar
+ (2 rows)
+ 
+ select indnatts, indkey from pg_index where indrelid in (select oid from pg_class where relname = 'tenancy_user2_tbl1');
+  indnatts | indkey 
+ ----------+--------
+         1 | 3
+ (1 row)
+ 
+ select nspname, nspowner from pg_namespace where nspname = 'tenancy_user2';
+     nspname    | nspowner 
+ ---------------+----------
+  tenancy_user2 |       10
+ (1 row)
+ 
+ 		
+ -- verify all system views related to the objects created by tenancy_user1
+ --select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user2_tbl1';
+ --select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user2_tbl2' and schemaname = 'tenancy_user2';
+ RESET ROLE;
+ -- Try to get the objects created by tenancy_user2 from tenancy_user1
+ SET SESSION ROLE tenancy_user1;
+ select * from tenancy_user2_tbl1;
+ ERROR:  permission denied for relation tenancy_user2_tbl1
+ select * from tenancy_user2_view1;
+ ERROR:  permission denied for relation tenancy_user2_view1
+ select * from tenancy_user2_matview1;
+ ERROR:  permission denied for relation tenancy_user2_matview1
+ select * from tenancy_user2.tenancy_user2.tbl2;
+ ERROR:  cross-database references are not implemented: "tenancy_user2.tenancy_user2.tbl2"
+ LINE 1: select * from tenancy_user2.tenancy_user2.tbl2;
+                       ^
+ -- verify all system catalogs related to the objects created by tenancy_user2
+ select relname from pg_class where relname = 'tenancy_user2_tbl1';
+  relname 
+ ---------
+ (0 rows)
+ 
+ select relname from pg_class where relname = 'tenancy_user2_tbl1_idx';
+  relname 
+ ---------
+ (0 rows)
+ 
+ select relname from pg_class where relname = 'tenancy_user2_view1';
+  relname 
+ ---------
+ (0 rows)
+ 
+ select relname from pg_class where relname = 'tenancy_user2_matview1';
+  relname 
+ ---------
+ (0 rows)
+ 
+ select attname from pg_attribute where attname = 'tenancy_user2_tbl1_column1';
+  attname 
+ ---------
+ (0 rows)
+ 
+ select adnum, adsrc from pg_attrdef where adrelid in (select oid from pg_class where relname = 'tenancy_user2_tbl1');
+  adnum | adsrc 
+ -------+-------
+ (0 rows)
+ 
+ select indnatts, indkey from pg_index where indrelid in (select oid from pg_class where relname = 'tenancy_user2_tbl1');
+  indnatts | indkey 
+ ----------+--------
+ (0 rows)
+ 
+ select nspname, nspowner from pg_namespace where nspname = 'tenancy_user2';
+  nspname | nspowner 
+ ---------+----------
+ (0 rows)
+ 
+ 		
+ -- verify all system views related to the objects created by tenancy_user1
+ --select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user2_tbl1';
+ --select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user2_tbl2' and schemaname = 'tenancy_user2';
+ RESET ROLE;
+ -- Try to get the objects created by tenancy_user1 from tenancy_user2
+ SET SESSION ROLE tenancy_user2;
+ select * from tenancy_user1_tbl1;
+ 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:  cross-database references are not implemented: "tenancy_user1.tenancy_user1.tbl2"
+ LINE 1: select * from tenancy_user1.tenancy_user1.tbl2;
+                       ^
+ -- verify all system catalogs related to the objects created by tenancy_user1
+ select relname from pg_class where relname = 'tenancy_user1_tbl1';
+  relname 
+ ---------
+ (0 rows)
+ 
+ select relname from pg_class where relname = 'tenancy_user1_tbl1_idx';
+  relname 
+ ---------
+ (0 rows)
+ 
+ select relname from pg_class where relname = 'tenancy_user1_view1';
+  relname 
+ ---------
+ (0 rows)
+ 
+ select relname from pg_class where relname = 'tenancy_user1_matview1';
+  relname 
+ ---------
+ (0 rows)
+ 
+ select attname from pg_attribute where attname = 'tenancy_user1_tbl1_column1';
+  attname 
+ ---------
+ (0 rows)
+ 
+ select adnum, adsrc from pg_attrdef where adrelid in (select oid from pg_class where relname = 'tenancy_user1_tbl1');
+  adnum | adsrc 
+ -------+-------
+ (0 rows)
+ 
+ select indnatts, indkey from pg_index where indrelid in (select oid from pg_class where relname = 'tenancy_user1_tbl1');
+  indnatts | indkey 
+ ----------+--------
+ (0 rows)
+ 
+ select nspname, nspowner from pg_namespace where nspname = 'tenancy_user1';
+  nspname | nspowner 
+ ---------+----------
+ (0 rows)
+ 
+ 		
+ -- verify all system views related to the objects created by tenancy_user1
+ --select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user1_tbl1';
+ --select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user1_tbl2' and schemaname = 'tenancy_user1';
+ RESET ROLE;
+ -- Delete the roles and it's associated objects.
+ SET SESSION ROLE tenancy_user1;
+ DROP TABLE tenancy_user1_tbl1 cascade;
+ NOTICE:  drop cascades to 2 other objects
+ DETAIL:  drop cascades to view tenancy_user1_view1
+ drop cascades to materialized view tenancy_user1_matview1
+ DROP TABLE tenancy_user1_tbl2 cascade;
+ RESET ROLE;
+ SET SESSION ROLE tenancy_user2;
+ DROP TABLE tenancy_user2_tbl1 cascade;
+ NOTICE:  drop cascades to 2 other objects
+ DETAIL:  drop cascades to view tenancy_user2_view1
+ drop cascades to materialized view tenancy_user2_matview1
+ DROP TABLE tenancy_user2_tbl2 cascade;
+ RESET ROLE;
+ alter database regression with catalog security = false;
+ drop schema tenancy_user1;
+ drop schema tenancy_user2;
+ drop role tenancy_user1;
+ drop role tenancy_user2;
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
***************
*** 60,66 **** test: create_index create_view
  # ----------
  # Another group of parallel tests
  # ----------
! test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes
  
  # ----------
  # sanity_check does a vacuum, affecting the sort order of SELECT *
--- 60,66 ----
  # ----------
  # Another group of parallel tests
  # ----------
! test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes multitenancy
  
  # ----------
  # sanity_check does a vacuum, affecting the sort order of SELECT *
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
***************
*** 157,160 **** test: largeobject
--- 157,161 ----
  test: with
  test: xml
  test: event_trigger
+ test: multitenancy
  test: stats
*** /dev/null
--- b/src/test/regress/sql/multitenancy.sql
***************
*** 0 ****
--- 1,169 ----
+ -- Create roles that are used by the following tests
+ create role tenancy_user1 login createdb;
+ create role tenancy_user2 login createdb;
+ 
+ create schema tenancy_user1;
+ grant all on schema tenancy_user1 to tenancy_user1;
+ 
+ create schema tenancy_user2;
+ grant all on schema tenancy_user2 to tenancy_user2;
+ 
+ alter database regression with catalog security = true;
+ 
+ -- create objects realted to tenacy_user1
+ SET SESSION ROLE tenancy_user1;
+ 
+ create table public.tenancy_user1_tbl1 (tenancy_user1_tbl1_column1 serial,
+ 								tenancy_user1_tbl1_column2 char(10) default 'FUJITSU',
+ 								tenancy_user1_tbl1_column3 int);
+ create index tenancy_user1_tbl1_idx on tenancy_user1_tbl1 (tenancy_user1_tbl1_column3);
+ 
+ insert into tenancy_user1_tbl1(tenancy_user1_tbl1_column3) values(1);
+ 
+ create table tenancy_user1.tenancy_user1_tbl2 (tenancy_user1_tbl2_column1 serial,
+ 								tenancy_user1_tbl2_column2 char(10) default 'FUJITSU',
+ 								tenancy_user1_tbl2_column3 int);
+ create index tenancy_user1_tbl2_idx on tenancy_user1.tenancy_user1_tbl2 (tenancy_user1_tbl2_column3);
+ 
+ 
+ create view public.tenancy_user1_view1 as select * from tenancy_user1_tbl1;
+ create materialized view public.tenancy_user1_matview1 as select * from tenancy_user1_tbl1;
+ 
+ select * from tenancy_user1_tbl1;
+ select * from tenancy_user1_view1;
+ select * from tenancy_user1_matview1;
+ 								
+ 
+ -- verify all system catalogs related to the objects created by tenancy_user1
+ select relname from pg_class where relname = 'tenancy_user1_tbl1';
+ select relname from pg_class where relname = 'tenancy_user1_tbl1_idx';
+ select relname from pg_class where relname = 'tenancy_user1_view1';
+ select relname from pg_class where relname = 'tenancy_user1_matview1';
+ 
+ select attname from pg_attribute where attname = 'tenancy_user1_tbl1_column1';
+ select adnum, adsrc from pg_attrdef where adrelid in (select oid from pg_class where relname = 'tenancy_user1_tbl1');
+ select indnatts, indkey from pg_index where indrelid in (select oid from pg_class where relname = 'tenancy_user1_tbl1');
+ select nspname, nspowner from pg_namespace where nspname = 'tenancy_user1';
+ 		
+ -- verify all system views related to the objects created by tenancy_user1
+ --select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user1_tbl1';
+ --select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user1_tbl2' and schemaname = 'tenancy_user1';
+ 
+ 		
+ RESET ROLE;
+ 
+ 
+ -- create objects realted to tenacy_user2
+ SET SESSION ROLE tenancy_user2;
+ 
+ create table public.tenancy_user2_tbl1 (tenancy_user2_tbl1_column1 serial,
+ 								tenancy_user2_tbl1_column2 char(10) default 'FUJITSU',
+ 								tenancy_user2_tbl1_column3 int);
+ create index tenancy_user2_tbl1_idx on tenancy_user2_tbl1 (tenancy_user2_tbl1_column3);
+ 
+ insert into tenancy_user2_tbl1(tenancy_user2_tbl1_column3) values(1);
+ 
+ create table tenancy_user2.tenancy_user2_tbl2 (tenancy_user2_tbl2_column1 serial,
+ 								tenancy_user2_tbl2_column2 char(10) default 'FUJITSU',
+ 								tenancy_user2_tbl2_column3 int);
+ create index tenancy_user2_tbl2_idx on tenancy_user2.tenancy_user2_tbl2 (tenancy_user2_tbl2_column3);
+ 
+ create view public.tenancy_user2_view1 as select * from tenancy_user2_tbl1;
+ create materialized view public.tenancy_user2_matview1 as select * from tenancy_user2_tbl1;
+ 
+ select * from tenancy_user2_tbl1;
+ select * from tenancy_user2_view1;
+ select * from tenancy_user2_matview1;
+ 
+ -- verify all system catalogs related to the objects created by tenancy_user2
+ select relname from pg_class where relname = 'tenancy_user2_tbl1';
+ select relname from pg_class where relname = 'tenancy_user2_tbl1_idx';
+ select relname from pg_class where relname = 'tenancy_user2_view1';
+ select relname from pg_class where relname = 'tenancy_user2_matview1';
+ 
+ select attname from pg_attribute where attname = 'tenancy_user2_tbl1_column1';
+ select adnum, adsrc from pg_attrdef where adrelid in (select oid from pg_class where relname = 'tenancy_user2_tbl1');
+ select indnatts, indkey from pg_index where indrelid in (select oid from pg_class where relname = 'tenancy_user2_tbl1');
+ select nspname, nspowner from pg_namespace where nspname = 'tenancy_user2';
+ 		
+ -- verify all system views related to the objects created by tenancy_user1
+ --select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user2_tbl1';
+ --select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user2_tbl2' and schemaname = 'tenancy_user2';
+ 
+ RESET ROLE;
+ 
+ -- Try to get the objects created by tenancy_user2 from tenancy_user1
+ SET SESSION ROLE tenancy_user1;
+ 
+ select * from tenancy_user2_tbl1;
+ select * from tenancy_user2_view1;
+ select * from tenancy_user2_matview1;
+ 
+ select * from tenancy_user2.tenancy_user2.tbl2;
+ 
+ -- verify all system catalogs related to the objects created by tenancy_user2
+ select relname from pg_class where relname = 'tenancy_user2_tbl1';
+ select relname from pg_class where relname = 'tenancy_user2_tbl1_idx';
+ select relname from pg_class where relname = 'tenancy_user2_view1';
+ select relname from pg_class where relname = 'tenancy_user2_matview1';
+ 
+ select attname from pg_attribute where attname = 'tenancy_user2_tbl1_column1';
+ select adnum, adsrc from pg_attrdef where adrelid in (select oid from pg_class where relname = 'tenancy_user2_tbl1');
+ select indnatts, indkey from pg_index where indrelid in (select oid from pg_class where relname = 'tenancy_user2_tbl1');
+ select nspname, nspowner from pg_namespace where nspname = 'tenancy_user2';
+ 		
+ -- verify all system views related to the objects created by tenancy_user1
+ --select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user2_tbl1';
+ --select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user2_tbl2' and schemaname = 'tenancy_user2';
+ 
+ RESET ROLE;
+ 
+ 
+ -- Try to get the objects created by tenancy_user1 from tenancy_user2
+ SET SESSION ROLE tenancy_user2;
+ 
+ select * from tenancy_user1_tbl1;
+ select * from tenancy_user1_view1;
+ select * from tenancy_user1_matview1;
+ 
+ select * from tenancy_user1.tenancy_user1.tbl2;
+ 
+ -- verify all system catalogs related to the objects created by tenancy_user1
+ select relname from pg_class where relname = 'tenancy_user1_tbl1';
+ select relname from pg_class where relname = 'tenancy_user1_tbl1_idx';
+ select relname from pg_class where relname = 'tenancy_user1_view1';
+ select relname from pg_class where relname = 'tenancy_user1_matview1';
+ 
+ select attname from pg_attribute where attname = 'tenancy_user1_tbl1_column1';
+ select adnum, adsrc from pg_attrdef where adrelid in (select oid from pg_class where relname = 'tenancy_user1_tbl1');
+ select indnatts, indkey from pg_index where indrelid in (select oid from pg_class where relname = 'tenancy_user1_tbl1');
+ select nspname, nspowner from pg_namespace where nspname = 'tenancy_user1';
+ 		
+ -- verify all system views related to the objects created by tenancy_user1
+ --select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user1_tbl1';
+ --select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user1_tbl2' and schemaname = 'tenancy_user1';
+ 
+ RESET ROLE;
+ 
+ -- Delete the roles and it's associated objects.
+ SET SESSION ROLE tenancy_user1;
+ 
+ DROP TABLE tenancy_user1_tbl1 cascade;
+ DROP TABLE tenancy_user1_tbl2 cascade;
+ 
+ RESET ROLE;
+ 
+ SET SESSION ROLE tenancy_user2;
+ 
+ DROP TABLE tenancy_user2_tbl1 cascade;
+ DROP TABLE tenancy_user2_tbl2 cascade;
+ 
+ RESET ROLE;
+ 
+ alter database regression with catalog security = false;
+ 
+ drop schema tenancy_user1;
+ drop schema tenancy_user2;
+ 
+ drop role tenancy_user1;
+ drop role tenancy_user2;
#9Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Haribabu Kommi (#8)
Re: Multi-tenancy with RLS

On Tue, Oct 6, 2015 at 10:56 AM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

Here I attached an updated version of the patch with the following changes.

I found some problems related to providing multi-tenancy on a system
catalog view.
This is because, system catalog view uses the owner that is created
the user instead
of the current user by storing the user information in "checkAsUser"
field in RangeTblEntry
structure.

The same "checkAsUser" is used in check_enable_rls function before
getting the policies for the table. All the system catalog views are
created by the super user, so no row level security policies
are applied to the views.

Ex-
SET SESSION ROLE tenancy_user1;

select relname from pg_class where relname = 'tenancy_user2_tbl1';
relname
---------
(0 rows)

select schemaname, relname from pg_stat_all_tables where relname =
'tenancy_user2_tbl1';
schemaname | relname
------------+--------------------
public | tenancy_user2_tbl1
(1 row)

The policy that is created on pg_class system catalog table is, get
all the objects that current
user have permissions. Permissions can be anything.

To fix the problem, I thought of using current session id instead of
"checkAsUser" while applying
row level security policies to system catalog objects. This doesn't
affect the normal objects. But this solution has given some problems
for foreign_data.sql while running the regress tests as the planner is
generating targetlist as NULL.

Is the above specified solution is the correct approach to handle this
problem? If it is i will check the foreign_data.sql problem, otherwise
is there any good approach to handle the same?

Regards,
Hari Babu
Fujitsu Australia

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#10Stephen Frost
sfrost@snowman.net
In reply to: Haribabu Kommi (#9)
Re: Multi-tenancy with RLS

* Haribabu Kommi (kommi.haribabu@gmail.com) wrote:

On Tue, Oct 6, 2015 at 10:56 AM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

Here I attached an updated version of the patch with the following changes.

I found some problems related to providing multi-tenancy on a system
catalog view.
This is because, system catalog view uses the owner that is created
the user instead
of the current user by storing the user information in "checkAsUser"
field in RangeTblEntry
structure.

Right, when querying through a view to tables underneath, we use the
permissions of the view owner. View creators should be generally aware
of this already.

I agree that it adds complications to the multi-tenancy idea since the
system views, today, allow viewing of all objects. There are two ways
to address that:

Modify the system catalog views to include the same constraints that the
policies on the tables do

or

Allow RLS policies against views and then create the necessary policies
on the views in the catalog.

My inclination is to work towards the latter as that's a capability we'd
like to have anyway.

Thanks!

Stephen

#11Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Stephen Frost (#10)
Re: Multi-tenancy with RLS

On Tue, Oct 6, 2015 at 10:29 PM, Stephen Frost <sfrost@snowman.net> wrote:

* Haribabu Kommi (kommi.haribabu@gmail.com) wrote:

On Tue, Oct 6, 2015 at 10:56 AM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

Here I attached an updated version of the patch with the following changes.

I found some problems related to providing multi-tenancy on a system
catalog view.
This is because, system catalog view uses the owner that is created
the user instead
of the current user by storing the user information in "checkAsUser"
field in RangeTblEntry
structure.

Right, when querying through a view to tables underneath, we use the
permissions of the view owner. View creators should be generally aware
of this already.

I agree that it adds complications to the multi-tenancy idea since the
system views, today, allow viewing of all objects. There are two ways
to address that:

Modify the system catalog views to include the same constraints that the
policies on the tables do

or

Allow RLS policies against views and then create the necessary policies
on the views in the catalog.

My inclination is to work towards the latter as that's a capability we'd
like to have anyway.

Thanks for the solutions to handle the problem.

Currently I thought of providing two multi-tenancy solutions to the user.
They are:

1. Tenancy at shared system catalog tables level
2. Tenancy at database system catalog tables.

User can create views on system catalog tables, even though I want to provide
tenancy on those views also. I will do further analysis and provide
details of which
solution gives the benefit of two tenancy levels and then I can proceed for
implementation after discussion.

Regards,
Hari Babu
Fujitsu Australia

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#12Robert Haas
robertmhaas@gmail.com
In reply to: Stephen Frost (#10)
Re: Multi-tenancy with RLS

On Tue, Oct 6, 2015 at 7:29 AM, Stephen Frost <sfrost@snowman.net> wrote:

* Haribabu Kommi (kommi.haribabu@gmail.com) wrote:

On Tue, Oct 6, 2015 at 10:56 AM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

Here I attached an updated version of the patch with the following changes.

I found some problems related to providing multi-tenancy on a system
catalog view.
This is because, system catalog view uses the owner that is created
the user instead
of the current user by storing the user information in "checkAsUser"
field in RangeTblEntry
structure.

Right, when querying through a view to tables underneath, we use the
permissions of the view owner. View creators should be generally aware
of this already.

I agree that it adds complications to the multi-tenancy idea since the
system views, today, allow viewing of all objects. There are two ways
to address that:

Modify the system catalog views to include the same constraints that the
policies on the tables do

or

Allow RLS policies against views and then create the necessary policies
on the views in the catalog.

My inclination is to work towards the latter as that's a capability we'd
like to have anyway.

We've got one reloption for views already - security_barrier. Maybe
we could have another one that effectively changes a particular view
from "security definer" as it is today to "security invoker".

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#13Stephen Frost
sfrost@snowman.net
In reply to: Robert Haas (#12)
Re: Multi-tenancy with RLS

* Robert Haas (robertmhaas@gmail.com) wrote:

We've got one reloption for views already - security_barrier. Maybe
we could have another one that effectively changes a particular view
from "security definer" as it is today to "security invoker".

As I recall, there was a previous suggestion (honestly, I thought it was
your idea) to have a reloption which made views "fully" security
definer, in that functions in the view definition would run as the view
owner instead of the view invoker.

I liked that idea, though we would need to have a function to say "who
is the 'outer' user?" (CURRENT_USER always being the owner with the
above described reloption).

I'm less sure about the idea of having a view which runs entirely as the
view invoker, but I'm not against it either.

I do think both of those are independent of supporting policies for
views and foreign tables though, which we'd want even if we had
reloptions for the above ideas.

Thanks!

Stephen

#14Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Stephen Frost (#13)
Re: Multi-tenancy with RLS

On Fri, Oct 9, 2015 at 2:04 PM, Stephen Frost <sfrost@snowman.net> wrote:

* Robert Haas (robertmhaas@gmail.com) wrote:

We've got one reloption for views already - security_barrier. Maybe
we could have another one that effectively changes a particular view
from "security definer" as it is today to "security invoker".

As I recall, there was a previous suggestion (honestly, I thought it was
your idea) to have a reloption which made views "fully" security
definer, in that functions in the view definition would run as the view
owner instead of the view invoker.

I liked that idea, though we would need to have a function to say "who
is the 'outer' user?" (CURRENT_USER always being the owner with the
above described reloption).

I'm less sure about the idea of having a view which runs entirely as the
view invoker, but I'm not against it either.

I changed in function check_enable_rls to use the invoker id instead of owner id
for all the system objects, the catalog table policies are getting
applied and it is
working fine till now in my multi-tenancy testing.

Currently I am writing tests to validate it against all user objects also.
If this change works for all user objects also, then we may not needed
the security invoker
reloption.

Regards,
Hari Babu
Fujitsu Australia

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#15Joe Conway
mail@joeconway.com
In reply to: Stephen Frost (#13)
Re: Multi-tenancy with RLS

On 10/08/2015 11:04 PM, Stephen Frost wrote:

* Robert Haas (robertmhaas@gmail.com) wrote:

We've got one reloption for views already - security_barrier. Maybe
we could have another one that effectively changes a particular view
from "security definer" as it is today to "security invoker".

As I recall, there was a previous suggestion (honestly, I thought it was
your idea) to have a reloption which made views "fully" security
definer, in that functions in the view definition would run as the view
owner instead of the view invoker.

I'd love to see a way for views to behave in an entirely view definer or
entirely view invoker way. The current mixed mode is bad/confusing IMHO,
although I guess we need some backward compatibility mode?

I liked that idea, though we would need to have a function to say "who
is the 'outer' user?" (CURRENT_USER always being the owner with the
above described reloption).

I'm less sure about the idea of having a view which runs entirely as the
view invoker, but I'm not against it either.

I do think both of those are independent of supporting policies for
views and foreign tables though, which we'd want even if we had
reloptions for the above ideas.

Agreed

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

#16Stephen Frost
sfrost@snowman.net
In reply to: Haribabu Kommi (#14)
Re: Multi-tenancy with RLS

* Haribabu Kommi (kommi.haribabu@gmail.com) wrote:

On Fri, Oct 9, 2015 at 2:04 PM, Stephen Frost <sfrost@snowman.net> wrote:

* Robert Haas (robertmhaas@gmail.com) wrote:

We've got one reloption for views already - security_barrier. Maybe
we could have another one that effectively changes a particular view
from "security definer" as it is today to "security invoker".

As I recall, there was a previous suggestion (honestly, I thought it was
your idea) to have a reloption which made views "fully" security
definer, in that functions in the view definition would run as the view
owner instead of the view invoker.

I liked that idea, though we would need to have a function to say "who
is the 'outer' user?" (CURRENT_USER always being the owner with the
above described reloption).

I'm less sure about the idea of having a view which runs entirely as the
view invoker, but I'm not against it either.

I changed in function check_enable_rls to use the invoker id instead of owner id
for all the system objects, the catalog table policies are getting
applied and it is
working fine till now in my multi-tenancy testing.

Currently I am writing tests to validate it against all user objects also.
If this change works for all user objects also, then we may not needed
the security invoker
reloption.

The reloption would be to allow the user to decide which behavior they
wanted, as there are use-cases for both.

Thanks!

Stephen

#17Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Stephen Frost (#16)
4 attachment(s)
Re: Multi-tenancy with RLS

On Sat, Oct 10, 2015 at 1:54 AM, Stephen Frost <sfrost@snowman.net> wrote:

* Haribabu Kommi (kommi.haribabu@gmail.com) wrote:

On Fri, Oct 9, 2015 at 2:04 PM, Stephen Frost <sfrost@snowman.net> wrote:

* Robert Haas (robertmhaas@gmail.com) wrote:

We've got one reloption for views already - security_barrier. Maybe
we could have another one that effectively changes a particular view
from "security definer" as it is today to "security invoker".

As I recall, there was a previous suggestion (honestly, I thought it was
your idea) to have a reloption which made views "fully" security
definer, in that functions in the view definition would run as the view
owner instead of the view invoker.

I liked that idea, though we would need to have a function to say "who
is the 'outer' user?" (CURRENT_USER always being the owner with the
above described reloption).

I'm less sure about the idea of having a view which runs entirely as the
view invoker, but I'm not against it either.

I changed in function check_enable_rls to use the invoker id instead of owner id
for all the system objects, the catalog table policies are getting
applied and it is
working fine till now in my multi-tenancy testing.

Currently I am writing tests to validate it against all user objects also.
If this change works for all user objects also, then we may not needed
the security invoker
reloption.

The reloption would be to allow the user to decide which behavior they
wanted, as there are use-cases for both.

Any_privilege_option:
Patch that adds 'any' type as a privilege option to verify whether the user
is having any privileges on the object, instead of specifying each and every
privilege type that object supports. Using of this option at grant and revoke
commands throw an error.

View_security_definer:
Patch that adds "security_definer" as a view option to specify whether the
view owner needs to be used for all operations on the view, otherwise the
current user is used.

Currently by default the view owner is used to check against all privileges,
so changing it as invoker instead of owner leads to backward compatibility
problems as permission denied on the base relation and etc. To minimize
the impact, currently the invoker id is used only when the view is rewritten
to base relation for 1) updatable views 2) while applying the row security
policies to the base relations.

Instead of the above change, if we treat all the views by default as security
definer, then to support multi-tenancy we need to change all the system views
as security_definer=false.

comments?

shared_catalog_tenancy:
Patch adds an initdb option -C or --shared-catalog-security to add row level
security policies on shared catalog tables that are eligible for tenancy.
With this option, user gets the tenancy at database level, means user can
get the database list that he has some privileges, but not all. It is
not possible
to disable the shared catalog security once it is set at initdb time.

database_catalog_tenancy:
Patch that adds an database option of "catalog security". This can be used
with alter database only not possible with create database command.
With this option, user gets the tenancy at table level. Once user enables
the catalog security at database level, row level security policies are created
on catalog tables that are eligible. User can disable catalog security if wants.

Known issues:
1. If user (U1) grants permissions on object (tbl1) to user (U2), the user U2
can get the information that there exists an user (U1) in the system, but
U2 cannot get the details of U1.

2. If user (U2) executes a query on an object (tbl2) which the user
(U2) don't have
permissions, as he cannot able to see that object from catalog views/tables,
but the query returns an error message as "permission denied", but in case
if multi-tenancy is enabled, the error message should be "relation
doesn't exist".

Pending items:
1. Need to add some more tests to verify all database catalog tables.
2. Documentation changes for database catalog tenancy.

Regards,
Hari Babu
Fujitsu Australia

Attachments:

1_any_privilege_option_v1.patchapplication/octet-stream; name=1_any_privilege_option_v1.patchDownload
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 15138,15144 **** SET search_path TO <replaceable>schema</> <optional>, <replaceable>schema</>, ..
     <xref linkend="functions-info-access-table"> lists functions that
     allow the user to query object access privileges programmatically.
     See <xref linkend="ddl-priv"> for more information about
!    privileges.
    </para>
  
     <table id="functions-info-access-table">
--- 15138,15148 ----
     <xref linkend="functions-info-access-table"> lists functions that
     allow the user to query object access privileges programmatically.
     See <xref linkend="ddl-priv"> for more information about
!    privileges. In case if user wants to find out any privilege check
!    on the corresponding object, instead of providing individual/all
!    privileges, user can specify "any" as an privilege option. The
!    Access Prvilege Inquiry Functions returns true if the user have 
!    any privileges on the object.
    </para>
  
     <table id="functions-info-access-table">
*** a/src/backend/utils/adt/acl.c
--- b/src/backend/utils/adt/acl.c
***************
*** 2031,2036 **** convert_table_priv_string(text *priv_type_text)
--- 2031,2041 ----
  		{"REFERENCES WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
  		{"TRIGGER", ACL_TRIGGER},
  		{"TRIGGER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
+ 		{"ANY", ACL_SELECT | ACL_GRANT_OPTION_FOR(ACL_SELECT) |
+ 				ACL_INSERT | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
+ 				ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_DELETE | ACL_GRANT_OPTION_FOR(ACL_DELETE) |
+ 				ACL_TRUNCATE | ACL_GRANT_OPTION_FOR(ACL_TRUNCATE) | ACL_REFERENCES |
+ 				ACL_GRANT_OPTION_FOR(ACL_REFERENCES) | ACL_TRIGGER | ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
  		{"RULE", 0},			/* ignore old RULE privileges */
  		{"RULE WITH GRANT OPTION", 0},
  		{NULL, 0}
***************
*** 2243,2248 **** convert_sequence_priv_string(text *priv_type_text)
--- 2248,2254 ----
  		{"USAGE", ACL_USAGE},
  		{"SELECT", ACL_SELECT},
  		{"UPDATE", ACL_UPDATE},
+ 		{"ANY", ACL_USAGE | ACL_SELECT | ACL_UPDATE},
  		{NULL, 0}
  	};
  
***************
*** 2858,2863 **** convert_column_priv_string(text *priv_type_text)
--- 2864,2872 ----
  		{"UPDATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_UPDATE)},
  		{"REFERENCES", ACL_REFERENCES},
  		{"REFERENCES WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
+ 		{"ANY", ACL_SELECT | ACL_GRANT_OPTION_FOR(ACL_SELECT) | ACL_INSERT |
+ 				ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
+ 				ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_REFERENCES | ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
  		{NULL, 0}
  	};
  
***************
*** 3055,3060 **** convert_database_priv_string(text *priv_type_text)
--- 3064,3072 ----
  		{"TEMP WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP)},
  		{"CONNECT", ACL_CONNECT},
  		{"CONNECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
+ 		{"ANY", ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE) |
+ 									ACL_CREATE_TEMP | ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CREATE_TEMP |
+ 									ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CONNECT | ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
  		{NULL, 0}
  	};
  
***************
*** 3238,3243 **** convert_foreign_data_wrapper_priv_string(text *priv_type_text)
--- 3250,3256 ----
  	static const priv_map foreign_data_wrapper_priv_map[] = {
  		{"USAGE", ACL_USAGE},
  		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+ 		{"ANY", ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
  		{NULL, 0}
  	};
  
***************
*** 3438,3443 **** convert_function_priv_string(text *priv_type_text)
--- 3451,3457 ----
  	static const priv_map function_priv_map[] = {
  		{"EXECUTE", ACL_EXECUTE},
  		{"EXECUTE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_EXECUTE)},
+ 		{"ANY", ACL_EXECUTE | ACL_GRANT_OPTION_FOR(ACL_EXECUTE)},
  		{NULL, 0}
  	};
  
***************
*** 3629,3634 **** convert_language_priv_string(text *priv_type_text)
--- 3643,3649 ----
  	static const priv_map language_priv_map[] = {
  		{"USAGE", ACL_USAGE},
  		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+ 		{"ANY", ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
  		{NULL, 0}
  	};
  
***************
*** 3822,3827 **** convert_schema_priv_string(text *priv_type_text)
--- 3837,3843 ----
  		{"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
  		{"USAGE", ACL_USAGE},
  		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+ 		{"ANY", ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE) | ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
  		{NULL, 0}
  	};
  
***************
*** 4004,4009 **** convert_server_priv_string(text *priv_type_text)
--- 4020,4026 ----
  	static const priv_map server_priv_map[] = {
  		{"USAGE", ACL_USAGE},
  		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+ 		{"ANY", ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
  		{NULL, 0}
  	};
  
***************
*** 4186,4191 **** convert_tablespace_priv_string(text *priv_type_text)
--- 4203,4209 ----
  	static const priv_map tablespace_priv_map[] = {
  		{"CREATE", ACL_CREATE},
  		{"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
+ 		{"ANY", ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE)},
  		{NULL, 0}
  	};
  
***************
*** 4385,4390 **** convert_type_priv_string(text *priv_type_text)
--- 4403,4409 ----
  	static const priv_map type_priv_map[] = {
  		{"USAGE", ACL_USAGE},
  		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+ 		{"ANY", ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
  		{NULL, 0}
  	};
  
***************
*** 4566,4571 **** convert_role_priv_string(text *priv_type_text)
--- 4585,4591 ----
  		{"USAGE WITH ADMIN OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
  		{"MEMBER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
  		{"MEMBER WITH ADMIN OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
+ 		{"ANY", ACL_USAGE | ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE)},
  		{NULL, 0}
  	};
  
2_view_security_definer_v1.patchapplication/octet-stream; name=2_view_security_definer_v1.patchDownload
*** a/doc/src/sgml/ref/alter_view.sgml
--- b/doc/src/sgml/ref/alter_view.sgml
***************
*** 142,147 **** ALTER VIEW [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RESET
--- 142,157 ----
           </para>
          </listitem>
         </varlistentry>
+        <varlistentry>
+         <term><literal>security_definer</literal> (<type>boolean</type>)</term>
+         <listitem>
+          <para>
+           Changes the security-definer property of the view.  The value must
+           be Boolean value, such as <literal>true</literal>
+           or <literal>false</literal>.
+          </para>
+         </listitem>
+        </varlistentry>
        </variablelist>
       </para>
      </listitem>
*** a/doc/src/sgml/ref/create_view.sgml
--- b/doc/src/sgml/ref/create_view.sgml
***************
*** 139,145 **** CREATE VIEW <replaceable>name</> AS WITH RECURSIVE <replaceable>name</> (<replac
         </varlistentry>
  
         <varlistentry>
!         <term><literal>security_barrier</literal> (<type>string</type>)</term>
          <listitem>
           <para>
            This should be used if the view is intended to provide row-level
--- 139,145 ----
         </varlistentry>
  
         <varlistentry>
!         <term><literal>security_barrier</literal> (<type>boolean</type>)</term>
          <listitem>
           <para>
            This should be used if the view is intended to provide row-level
***************
*** 147,152 **** CREATE VIEW <replaceable>name</> AS WITH RECURSIVE <replaceable>name</> (<replac
--- 147,162 ----
           </para>
          </listitem>
         </varlistentry>
+        
+        <varlistentry>
+         <term><literal>security_definer</literal> (<type>boolean</type>)</term>
+         <listitem>
+          <para>
+           This should be used if the view is intended to be executed with
+           owner privileges rather than the current user.
+          </para>
+         </listitem>
+        </varlistentry>
        </variablelist>
       </para>
      </listitem>
***************
*** 276,281 **** CREATE VIEW vista AS SELECT text 'Hello World' AS hello;
--- 286,322 ----
      to replace it (this includes being a member of the owning role).
     </para>
  
+   <refsect2 id="SQL-CREATEVIEW-security-definer-views">
+    <title id="SQL-CREATEVIEW-security-definer-views-title">Security definer Views</title>
+ 
+    <indexterm zone="sql-createview-security-definer-views">
+     <primary>security definer views</primary>
+    </indexterm>
+ 
+    <para>
+     Security definer views uses the view owner id instead of the current user
+     in the following conditions, otherwise the current user is used to verify
+     the privileges and etc.
+     <itemizedlist>
+      <listitem>
+       <para>
+        The view is used in <command>INSERT</>, <command>UPDATE</>
+        and <command>DELETE</> statements in the same way as on a
+        regular table.
+       </para>
+      </listitem>
+ 
+      <listitem>
+       <para>
+        To apply row-level security policies on the underlying base
+        relations of the view, based on the security definer, the 
+        corresponding policies related to the user are applied.
+       </para>
+      </listitem>
+     </itemizedlist>
+    </para>
+   </refsect2>
+  
    <refsect2 id="SQL-CREATEVIEW-updatable-views">
     <title id="SQL-CREATEVIEW-updatable-views-title">Updatable Views</title>
  
*** a/src/backend/access/common/reloptions.c
--- b/src/backend/access/common/reloptions.c
***************
*** 89,94 **** static relopt_bool boolRelOpts[] =
--- 89,103 ----
  		},
  		false
  	},
+ 	{
+ 		{
+ 			"security_definer",
+ 			"specifies that the view is to be executed with the privileges of the user that created it.",
+ 			RELOPT_KIND_VIEW,
+ 			AccessExclusiveLock
+ 		},
+ 		false
+ 	},
  	/* list terminator */
  	{{NULL}}
  };
***************
*** 1320,1325 **** view_reloptions(Datum reloptions, bool validate)
--- 1329,1336 ----
  	static const relopt_parse_elt tab[] = {
  		{"security_barrier", RELOPT_TYPE_BOOL,
  		offsetof(ViewOptions, security_barrier)},
+ 		{ "security_definer", RELOPT_TYPE_BOOL,
+ 		offsetof(ViewOptions, security_definer) },
  		{"check_option", RELOPT_TYPE_STRING,
  		offsetof(ViewOptions, check_option_offset)}
  	};
*** a/src/backend/rewrite/rewriteHandler.c
--- b/src/backend/rewrite/rewriteHandler.c
***************
*** 24,29 ****
--- 24,30 ----
  #include "catalog/pg_type.h"
  #include "commands/trigger.h"
  #include "foreign/fdwapi.h"
+ #include "miscadmin.h"
  #include "nodes/makefuncs.h"
  #include "nodes/nodeFuncs.h"
  #include "parser/analyze.h"
***************
*** 1616,1621 **** fireRIRrules(Query *parsetree, List *activeRIRs, bool forUpdatePushedDown)
--- 1617,1623 ----
  	int			origResultRelation = parsetree->resultRelation;
  	int			rt_index;
  	ListCell   *lc;
+ 	bool		security_definer_view = false;
  
  	/*
  	 * don't try to convert this into a foreach loop, because rtable list can
***************
*** 1777,1794 **** fireRIRrules(Query *parsetree, List *activeRIRs, bool forUpdatePushedDown)
  		++rt_index;
  
  		/* Only normal relations can have RLS policies */
! 		if (rte->rtekind != RTE_RELATION ||
! 			rte->relkind != RELKIND_RELATION)
  			continue;
! 
  		rel = heap_open(rte->relid, NoLock);
  
  		/*
  		 * Fetch any new security quals that must be applied to this RTE.
  		 */
  		get_row_security_policies(parsetree, rte, rt_index,
  								  &securityQuals, &withCheckOptions,
! 								  &hasRowSecurity, &hasSubLinks);
  
  		if (securityQuals != NIL || withCheckOptions != NIL)
  		{
--- 1779,1805 ----
  		++rt_index;
  
  		/* Only normal relations can have RLS policies */
! 		if (rte->rtekind != RTE_RELATION)
  			continue;
! 		
  		rel = heap_open(rte->relid, NoLock);
  
+ 		if (rte->relkind == RELKIND_VIEW && RelationIsSecurityDefinerView(rel))
+ 			security_definer_view = true;
+ 
+ 		if (rte->relkind != RELKIND_RELATION)
+ 		{
+ 			heap_close(rel, NoLock);
+ 			continue;
+ 		}
+ 
  		/*
  		 * Fetch any new security quals that must be applied to this RTE.
  		 */
  		get_row_security_policies(parsetree, rte, rt_index,
  								  &securityQuals, &withCheckOptions,
! 								  &hasRowSecurity, &hasSubLinks,
! 								  security_definer_view);
  
  		if (securityQuals != NIL || withCheckOptions != NIL)
  		{
***************
*** 2824,2830 **** rewriteTargetView(Query *parsetree, Relation view)
  	 * the executor still performs appropriate permissions checks for the
  	 * query caller's use of the view.
  	 */
! 	new_rte->checkAsUser = view->rd_rel->relowner;
  	new_rte->requiredPerms = view_rte->requiredPerms;
  
  	/*
--- 2835,2844 ----
  	 * the executor still performs appropriate permissions checks for the
  	 * query caller's use of the view.
  	 */
! 	if (RelationIsSecurityDefinerView(view))
! 		new_rte->checkAsUser = view->rd_rel->relowner;
! 	else
! 		new_rte->checkAsUser = GetUserId();
  	new_rte->requiredPerms = view_rte->requiredPerms;
  
  	/*
*** a/src/backend/rewrite/rowsecurity.c
--- b/src/backend/rewrite/rowsecurity.c
***************
*** 105,111 **** row_security_policy_hook_type row_security_policy_hook_restrictive = NULL;
  void
  get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
  						  List **securityQuals, List **withCheckOptions,
! 						  bool *hasRowSecurity, bool *hasSubLinks)
  {
  	Oid			user_id;
  	int			rls_status;
--- 105,112 ----
  void
  get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
  						  List **securityQuals, List **withCheckOptions,
! 						  bool *hasRowSecurity, bool *hasSubLinks,
! 						  bool security_definer_view)
  {
  	Oid			user_id;
  	int			rls_status;
***************
*** 125,134 **** get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
  		return;
  
  	/* Switch to checkAsUser if it's set */
! 	user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId();
  
  	/* Determine the state of RLS for this, pass checkAsUser explicitly */
! 	rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false);
  
  	/* If there is no RLS on this table at all, nothing to do */
  	if (rls_status == RLS_NONE)
--- 126,138 ----
  		return;
  
  	/* Switch to checkAsUser if it's set */
! 	if (security_definer_view)
! 		user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId();
! 	else
! 		user_id = GetUserId();
  
  	/* Determine the state of RLS for this, pass checkAsUser explicitly */
! 	rls_status = check_enable_rls(rte->relid, user_id, false);
  
  	/* If there is no RLS on this table at all, nothing to do */
  	if (rls_status == RLS_NONE)
*** a/src/include/rewrite/rowsecurity.h
--- b/src/include/rewrite/rowsecurity.h
***************
*** 43,48 **** extern PGDLLIMPORT row_security_policy_hook_type row_security_policy_hook_restri
  extern void get_row_security_policies(Query *root,
  						  RangeTblEntry *rte, int rt_index,
  						  List **securityQuals, List **withCheckOptions,
! 						  bool *hasRowSecurity, bool *hasSubLinks);
  
  #endif   /* ROWSECURITY_H */
--- 43,49 ----
  extern void get_row_security_policies(Query *root,
  						  RangeTblEntry *rte, int rt_index,
  						  List **securityQuals, List **withCheckOptions,
! 						  bool *hasRowSecurity, bool *hasSubLinks,
! 						  bool security_definer_view);
  
  #endif   /* ROWSECURITY_H */
*** a/src/include/utils/rel.h
--- b/src/include/utils/rel.h
***************
*** 265,270 **** typedef struct ViewOptions
--- 265,271 ----
  {
  	int32		vl_len_;		/* varlena header (do not touch directly!) */
  	bool		security_barrier;
+ 	bool		security_definer;
  	int			check_option_offset;
  } ViewOptions;
  
***************
*** 278,283 **** typedef struct ViewOptions
--- 279,292 ----
  	 ((ViewOptions *) (relation)->rd_options)->security_barrier : false)
  
  /*
+  * RelationIsSecurityDefiner
+  *		Returns whether the relation is security definer, or not.
+  */
+ #define RelationIsSecurityDefinerView(relation)	\
+ 	((relation)->rd_options ?				\
+ 	 ((ViewOptions *) (relation)->rd_options)->security_definer : false)
+ 
+ /*
   * RelationHasCheckOption
   *		Returns true if the relation is a view defined with either the local
   *		or the cascaded check option.  Note multiple eval of argument!
*** a/src/test/regress/expected/rowsecurity.out
--- b/src/test/regress/expected/rowsecurity.out
***************
*** 1425,1431 **** CREATE POLICY p1 ON b1 USING (a % 2 = 0);
  ALTER TABLE b1 ENABLE ROW LEVEL SECURITY;
  GRANT ALL ON b1 TO rls_regress_user1;
  SET SESSION AUTHORIZATION rls_regress_user1;
! CREATE VIEW bv1 WITH (security_barrier) AS SELECT * FROM b1 WHERE a > 0 WITH CHECK OPTION;
  GRANT ALL ON bv1 TO rls_regress_user2;
  SET SESSION AUTHORIZATION rls_regress_user2;
  EXPLAIN (COSTS OFF) SELECT * FROM bv1 WHERE f_leak(b);
--- 1425,1431 ----
  ALTER TABLE b1 ENABLE ROW LEVEL SECURITY;
  GRANT ALL ON b1 TO rls_regress_user1;
  SET SESSION AUTHORIZATION rls_regress_user1;
! CREATE VIEW bv1 WITH (security_barrier, security_definer) AS SELECT * FROM b1 WHERE a > 0 WITH CHECK OPTION;
  GRANT ALL ON bv1 TO rls_regress_user2;
  SET SESSION AUTHORIZATION rls_regress_user2;
  EXPLAIN (COSTS OFF) SELECT * FROM bv1 WHERE f_leak(b);
***************
*** 1740,1746 **** EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b);
  --
  -- View and Table owner are the same.
  SET SESSION AUTHORIZATION rls_regress_user0;
! CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b);
  GRANT SELECT ON rls_view TO rls_regress_user1;
  -- Query as role that is not owner of view or table.  Should return all records.
  SET SESSION AUTHORIZATION rls_regress_user1;
--- 1740,1746 ----
  --
  -- View and Table owner are the same.
  SET SESSION AUTHORIZATION rls_regress_user0;
! CREATE VIEW rls_view WITH (security_definer) AS SELECT * FROM z1 WHERE f_leak(b);
  GRANT SELECT ON rls_view TO rls_regress_user1;
  -- Query as role that is not owner of view or table.  Should return all records.
  SET SESSION AUTHORIZATION rls_regress_user1;
***************
*** 1789,1795 **** EXPLAIN (COSTS OFF) SELECT * FROM rls_view;
  DROP VIEW rls_view;
  -- View and Table owners are different.
  SET SESSION AUTHORIZATION rls_regress_user1;
! CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b);
  GRANT SELECT ON rls_view TO rls_regress_user0;
  -- Query as role that is not owner of view but is owner of table.
  -- Should return records based on view owner policies.
--- 1789,1795 ----
  DROP VIEW rls_view;
  -- View and Table owners are different.
  SET SESSION AUTHORIZATION rls_regress_user1;
! CREATE VIEW rls_view WITH (security_definer) AS SELECT * FROM z1 WHERE f_leak(b);
  GRANT SELECT ON rls_view TO rls_regress_user0;
  -- Query as role that is not owner of view but is owner of table.
  -- Should return records based on view owner policies.
*** a/src/test/regress/expected/updatable_views.out
--- b/src/test/regress/expected/updatable_views.out
***************
*** 966,972 **** CREATE USER view_user2;
  SET SESSION AUTHORIZATION view_user1;
  CREATE TABLE base_tbl(a int, b text, c float);
  INSERT INTO base_tbl VALUES (1, 'Row 1', 1.0);
! CREATE VIEW rw_view1 AS SELECT b AS bb, c AS cc, a AS aa FROM base_tbl;
  INSERT INTO rw_view1 VALUES ('Row 2', 2.0, 2);
  GRANT SELECT ON base_tbl TO view_user2;
  GRANT SELECT ON rw_view1 TO view_user2;
--- 966,972 ----
  SET SESSION AUTHORIZATION view_user1;
  CREATE TABLE base_tbl(a int, b text, c float);
  INSERT INTO base_tbl VALUES (1, 'Row 1', 1.0);
! CREATE VIEW rw_view1 WITH (security_definer) AS SELECT b AS bb, c AS cc, a AS aa FROM base_tbl;
  INSERT INTO rw_view1 VALUES ('Row 2', 2.0, 2);
  GRANT SELECT ON base_tbl TO view_user2;
  GRANT SELECT ON rw_view1 TO view_user2;
*** a/src/test/regress/sql/rowsecurity.sql
--- b/src/test/regress/sql/rowsecurity.sql
***************
*** 491,497 **** ALTER TABLE b1 ENABLE ROW LEVEL SECURITY;
  GRANT ALL ON b1 TO rls_regress_user1;
  
  SET SESSION AUTHORIZATION rls_regress_user1;
! CREATE VIEW bv1 WITH (security_barrier) AS SELECT * FROM b1 WHERE a > 0 WITH CHECK OPTION;
  GRANT ALL ON bv1 TO rls_regress_user2;
  
  SET SESSION AUTHORIZATION rls_regress_user2;
--- 491,497 ----
  GRANT ALL ON b1 TO rls_regress_user1;
  
  SET SESSION AUTHORIZATION rls_regress_user1;
! CREATE VIEW bv1 WITH (security_barrier, security_definer) AS SELECT * FROM b1 WHERE a > 0 WITH CHECK OPTION;
  GRANT ALL ON bv1 TO rls_regress_user2;
  
  SET SESSION AUTHORIZATION rls_regress_user2;
***************
*** 665,671 **** EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b);
  --
  -- View and Table owner are the same.
  SET SESSION AUTHORIZATION rls_regress_user0;
! CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b);
  GRANT SELECT ON rls_view TO rls_regress_user1;
  
  -- Query as role that is not owner of view or table.  Should return all records.
--- 665,671 ----
  --
  -- View and Table owner are the same.
  SET SESSION AUTHORIZATION rls_regress_user0;
! CREATE VIEW rls_view WITH (security_definer) AS SELECT * FROM z1 WHERE f_leak(b);
  GRANT SELECT ON rls_view TO rls_regress_user1;
  
  -- Query as role that is not owner of view or table.  Should return all records.
***************
*** 681,687 **** DROP VIEW rls_view;
  
  -- View and Table owners are different.
  SET SESSION AUTHORIZATION rls_regress_user1;
! CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b);
  GRANT SELECT ON rls_view TO rls_regress_user0;
  
  -- Query as role that is not owner of view but is owner of table.
--- 681,687 ----
  
  -- View and Table owners are different.
  SET SESSION AUTHORIZATION rls_regress_user1;
! CREATE VIEW rls_view WITH (security_definer) AS SELECT * FROM z1 WHERE f_leak(b);
  GRANT SELECT ON rls_view TO rls_regress_user0;
  
  -- Query as role that is not owner of view but is owner of table.
*** a/src/test/regress/sql/updatable_views.sql
--- b/src/test/regress/sql/updatable_views.sql
***************
*** 397,403 **** CREATE USER view_user2;
  SET SESSION AUTHORIZATION view_user1;
  CREATE TABLE base_tbl(a int, b text, c float);
  INSERT INTO base_tbl VALUES (1, 'Row 1', 1.0);
! CREATE VIEW rw_view1 AS SELECT b AS bb, c AS cc, a AS aa FROM base_tbl;
  INSERT INTO rw_view1 VALUES ('Row 2', 2.0, 2);
  
  GRANT SELECT ON base_tbl TO view_user2;
--- 397,403 ----
  SET SESSION AUTHORIZATION view_user1;
  CREATE TABLE base_tbl(a int, b text, c float);
  INSERT INTO base_tbl VALUES (1, 'Row 1', 1.0);
! CREATE VIEW rw_view1 WITH (security_definer) AS SELECT b AS bb, c AS cc, a AS aa FROM base_tbl;
  INSERT INTO rw_view1 VALUES ('Row 2', 2.0, 2);
  
  GRANT SELECT ON base_tbl TO view_user2;
3_shared_catalog_tenancy_v1.patchapplication/octet-stream; name=3_shared_catalog_tenancy_v1.patchDownload
*** a/doc/src/sgml/ref/initdb.sgml
--- b/doc/src/sgml/ref/initdb.sgml
***************
*** 152,157 **** PostgreSQL documentation
--- 152,170 ----
       </varlistentry>
  
       <varlistentry>
+       <term><option>-C</option></term>
+       <term><option>--shared-catalog-security</option></term>
+       <listitem>
+        <para>
+         This option enables the shared catalog tables security, by adding
+         row level security policies on all eligible shared catalog tables.
+         With this option, multi-tenancy in PostgreSQL is supported at 
+         database level.
+        </para>
+       </listitem>
+      </varlistentry>
+ 
+      <varlistentry>
        <term><option>-D <replaceable class="parameter">directory</replaceable></option></term>
        <term><option>--pgdata=<replaceable class="parameter">directory</replaceable></option></term>
        <listitem>
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 3436,3441 **** ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode)
--- 3436,3450 ----
  	{
  		AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
  
+ 		/*
+ 		 * ALTER table on sytem catalog tables is possible only when user specifies
+ 		 * CATALOG SECURITY on system catalog tables. To avoid an error in the 
+ 		 * AlterTableCreateToastTable function for system catalog tables, the system
+ 		 * catalog tables are ignored for the toast table creation.
+ 		 */
+ 		if (!IsUnderPostmaster && IsSharedRelation(tab->relid))
+ 			continue;
+ 
  		if (tab->relkind == RELKIND_RELATION ||
  			tab->relkind == RELKIND_MATVIEW)
  			AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
*** a/src/backend/utils/misc/rls.c
--- b/src/backend/utils/misc/rls.c
***************
*** 58,67 **** check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
  	bool		relforcerowsecurity;
  	Oid			user_id = checkAsUser ? checkAsUser : GetUserId();
  
- 	/* Nothing to do for built-in relations */
- 	if (relid < FirstNormalObjectId)
- 		return RLS_NONE;
- 
  	tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
  	if (!HeapTupleIsValid(tuple))
  		return RLS_NONE;
--- 58,63 ----
*** a/src/bin/initdb/initdb.c
--- b/src/bin/initdb/initdb.c
***************
*** 132,137 **** static bool do_sync = true;
--- 132,138 ----
  static bool sync_only = false;
  static bool show_setting = false;
  static bool data_checksums = false;
+ static bool shared_catalog_security = false;
  static char *xlog_dir = "";
  
  
***************
*** 186,191 **** static char *authwarning = NULL;
--- 187,193 ----
   */
  static const char *boot_options = "-F";
  static const char *backend_options = "--single -F -O -c search_path=pg_catalog -c exit_on_error=true";
+ static const char *catalog_security_options = "--single -F -O -c search_path=pg_catalog -c exit_on_error=true -c allow_system_table_mods=true";
  
  static const char *subdirs[] = {
  	"global",
***************
*** 255,260 **** static void setup_dictionary(void);
--- 257,263 ----
  static void setup_privileges(void);
  static void set_info_version(void);
  static void setup_schema(void);
+ static void setup_shared_catalog_security(void);
  static void load_plpgsql(void);
  static void vacuum_db(void);
  static void make_template0(void);
***************
*** 2252,2257 **** setup_schema(void)
--- 2255,2356 ----
  }
  
  /*
+  * setup shared catalog security by defining policies
+  */
+ static void
+ setup_shared_catalog_security(void)
+ {
+ 	PG_CMD_DECL;
+ 	const char	  **line;
+ 	static const char *pg_shared_catalog_security_setup[] = {
+ 		/* AuthMemRelationId */
+ 		"create policy pg_auth_members_read_own_data on pg_auth_members for select using"
+ 		" (pg_has_role(roleid, 'any') AND pg_has_role(member, 'any'));\n",
+ 
+ 		"alter table pg_auth_members enable row level security;\n",
+ 
+ 		/* AuthIdRelationId */
+ 		"create policy pg_authid_read_own_data on pg_authid for select using"
+ 		" (pg_has_role(oid, 'any'));\n",
+ 
+ 		"alter table pg_authid enable row level security;\n",
+ 
+ 		/* DatabaseRelationId */
+ 		"create policy pg_database_read_own_data on pg_database for select using"
+ 		" ((oid < 16384) OR has_database_privilege(oid,'any'));\n",
+ 
+ 		"alter table pg_database enable row level security;\n",
+ 
+ 		/* DbRoleSettingRelationId */
+ 		"create policy pg_db_role_setting_read_own_data on pg_db_role_setting for select using"
+ 		" (pg_has_role(setrole, 'any') AND has_database_privilege(setdatabase,'any'));\n",
+ 
+ 		"alter table pg_database enable row level security;\n",
+ 
+ 		/* PLTemplateRelationId */
+ 		/*
+ 		 * Currently there is no policy needed for this table, so
+ 		 * leave it as it is.
+ 		 */
+ 
+ 		/* ReplicationOriginRelationId */
+ 		/*
+ 		 * Currently there is no policy needed for this table, so
+ 		 * leave it as it is.
+ 		 */
+ 		 
+ 		/* SharedDependRelationId */
+ 		"create policy pg_shdepend_read_own_data on pg_shdepend for select using"
+ 		" ((classid = 1262 AND has_database_privilege(objid, 'any'))"
+ 		" OR (classid = 1260 AND pg_has_role(objid, 'any'))"
+ 		" OR (classid = 1213 AND has_tablespace_privilege(objid, 'any')));\n",
+ 
+ 		"alter table pg_shdepend enable row level security;\n",
+ 
+ 		/* SharedDescriptionRelationId */
+ 		"create policy pg_shdescription_read_own_data on pg_shdescription for select using"
+ 		" ((classoid = 1262 AND has_database_privilege(objoid, 'any'))"
+ 		" OR (classoid = 1260 AND pg_has_role(objoid, 'any'))"
+ 		" OR (classoid = 1213 AND has_tablespace_privilege(objoid, 'any')));\n",
+ 
+ 		"alter table pg_shdescription enable row level security;\n",
+ 
+ 		/* SharedSecLabelRelationId */
+ 		"create policy pg_shseclabel_read_own_data on pg_shseclabel for select using"
+ 		" ((classoid = 1262 AND has_database_privilege(objoid, 'any'))"
+ 		" OR (classoid = 1260 AND pg_has_role(objoid, 'any'))"
+ 		" OR (classoid = 1213 AND has_tablespace_privilege(objoid, 'any')));\n",
+ 
+ 		"alter table pg_shseclabel enable row level security;\n",
+ 
+ 		/* TableSpaceRelationId */
+ 		"create policy pg_tablespace_read_own_data on pg_tablespace for select using"
+ 		" ((oid < 16384) OR has_tablespace_privilege(oid, 'any'));\n",
+ 
+ 		"alter table pg_tablespace enable row level security;\n",
+ 
+ 		NULL
+ 	};
+ 
+ 	fputs(_("creating shared catalog security policy ... "), stdout);
+ 	fflush(stdout);
+ 
+ 	snprintf(cmd, sizeof(cmd),
+ 		"\"%s\" %s template1 >%s",
+ 		backend_exec, catalog_security_options,
+ 		DEVNULL);
+ 
+ 	PG_CMD_OPEN;
+ 
+ 	for (line = pg_shared_catalog_security_setup; *line != NULL; line++)
+ 		PG_CMD_PUTS(*line);
+ 
+ 	PG_CMD_CLOSE;
+ 
+ 	check_ok();
+ }
+ 
+ /*
   * load PL/pgsql server-side language
   */
  static void
***************
*** 2768,2773 **** usage(const char *progname)
--- 2867,2874 ----
  	printf(_("\nLess commonly used options:\n"));
  	printf(_("  -d, --debug               generate lots of debugging output\n"));
  	printf(_("  -k, --data-checksums      use data page checksums\n"));
+ 	printf(_("  -C, --shared-catalog-security\n"
+ 			"						      use shared catalog security\n"));
  	printf(_("  -L DIRECTORY              where to find the input files\n"));
  	printf(_("  -n, --noclean             do not clean up after errors\n"));
  	printf(_("  -N, --nosync              do not wait for changes to be written safely to disk\n"));
***************
*** 3365,3370 **** initialize_data_directory(void)
--- 3466,3474 ----
  
  	setup_schema();
  
+ 	if (shared_catalog_security)
+ 		setup_shared_catalog_security();
+ 	
  	load_plpgsql();
  
  	vacuum_db();
***************
*** 3405,3410 **** main(int argc, char *argv[])
--- 3509,3515 ----
  		{"sync-only", no_argument, NULL, 'S'},
  		{"xlogdir", required_argument, NULL, 'X'},
  		{"data-checksums", no_argument, NULL, 'k'},
+ 		{"shared-catalog-security", no_argument, NULL, 'C' },
  		{NULL, 0, NULL, 0}
  	};
  
***************
*** 3445,3451 **** main(int argc, char *argv[])
  
  	/* process command-line options */
  
! 	while ((c = getopt_long(argc, argv, "dD:E:kL:nNU:WA:sST:X:", long_options, &option_index)) != -1)
  	{
  		switch (c)
  		{
--- 3550,3556 ----
  
  	/* process command-line options */
  
! 	while ((c = getopt_long(argc, argv, "dD:E:kCL:nNU:WA:sST:X:", long_options, &option_index)) != -1)
  	{
  		switch (c)
  		{
***************
*** 3497,3502 **** main(int argc, char *argv[])
--- 3602,3610 ----
  			case 'k':
  				data_checksums = true;
  				break;
+ 			case 'C':
+ 				shared_catalog_security = true;
+ 				break;
  			case 'L':
  				share_path = pg_strdup(optarg);
  				break;
4_database_catalog_tenancy_v1.patchapplication/octet-stream; name=4_database_catalog_tenancy_v1.patchDownload
*** a/src/backend/commands/dbcommands.c
--- b/src/backend/commands/dbcommands.c
***************
*** 42,47 ****
--- 42,48 ----
  #include "commands/dbcommands.h"
  #include "commands/dbcommands_xlog.h"
  #include "commands/defrem.h"
+ #include "commands/policy.h"
  #include "commands/seclabel.h"
  #include "commands/tablespace.h"
  #include "mb/pg_wchar.h"
***************
*** 226,231 **** createdb(const CreatedbStmt *stmt)
--- 227,239 ----
  					 errmsg("LOCATION is not supported anymore"),
  					 errhint("Consider using tablespaces instead.")));
  		}
+ 		else if (strcmp(defel->defname, "catalog_security") == 0)
+ 		{
+ 			ereport(ERROR,
+                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                     errmsg("catalog security is not supported with create database command."),
+                     errdetail("Enable catalog security using Alter database command.")));
+ 		}
  		else
  			ereport(ERROR,
  					(errcode(ERRCODE_SYNTAX_ERROR),
***************
*** 519,524 **** createdb(const CreatedbStmt *stmt)
--- 527,533 ----
  	new_record[Anum_pg_database_datfrozenxid - 1] = TransactionIdGetDatum(src_frozenxid);
  	new_record[Anum_pg_database_datminmxid - 1] = TransactionIdGetDatum(src_minmxid);
  	new_record[Anum_pg_database_dattablespace - 1] = ObjectIdGetDatum(dst_deftablespace);
+ 	new_record[Anum_pg_database_datcatalogsecurity - 1] = BoolGetDatum(false);
  
  	/*
  	 * We deliberately set datacl to default (NULL), rather than copying it
***************
*** 1375,1385 **** AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
--- 1384,1397 ----
  	ListCell   *option;
  	bool		dbistemplate = false;
  	bool		dballowconnections = true;
+ 	bool		dbcatalogsecurity = false;
  	int			dbconnlimit = -1;
  	DefElem    *distemplate = NULL;
  	DefElem    *dallowconnections = NULL;
  	DefElem    *dconnlimit = NULL;
  	DefElem    *dtablespace = NULL;
+ 	DefElem    *dcatalogsecurity = NULL;
+ 	Form_pg_database pg_database_tuple;
  	Datum		new_record[Natts_pg_database];
  	bool		new_record_nulls[Natts_pg_database];
  	bool		new_record_repl[Natts_pg_database];
***************
*** 1421,1426 **** AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
--- 1433,1447 ----
  						 errmsg("conflicting or redundant options")));
  			dtablespace = defel;
  		}
+ 		else if (strcmp(defel->defname, "catalog_security") == 0)
+ 		{
+ 			if (dcatalogsecurity)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						errmsg("conflicting or redundant options")));
+ 
+ 			dcatalogsecurity = defel;
+ 		}
  		else
  			ereport(ERROR,
  					(errcode(ERRCODE_SYNTAX_ERROR),
***************
*** 1457,1462 **** AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
--- 1478,1485 ----
  					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  					 errmsg("invalid connection limit: %d", dbconnlimit)));
  	}
+ 	if (dcatalogsecurity && dcatalogsecurity->arg)
+ 		dbcatalogsecurity = defGetBoolean(dcatalogsecurity);
  
  	/*
  	 * Get the old tuple.  We don't need a lock on the database per se,
***************
*** 1476,1487 **** AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
--- 1499,1517 ----
  				(errcode(ERRCODE_UNDEFINED_DATABASE),
  				 errmsg("database \"%s\" does not exist", stmt->dbname)));
  
+ 	pg_database_tuple = (Form_pg_database)GETSTRUCT(tuple);
  	dboid = HeapTupleGetOid(tuple);
  
  	if (!pg_database_ownercheck(HeapTupleGetOid(tuple), GetUserId()))
  		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
  					   stmt->dbname);
  
+ 	if (dcatalogsecurity && (dboid != MyDatabaseId))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				errmsg("Enabling/disabling catalog security can be done"
+ 						" only to the connected database \"%s\"", stmt->dbname)));
+ 
  	/*
  	 * In order to avoid getting locked out and having to go through
  	 * standalone mode, we refuse to disallow connections to the database
***************
*** 1493,1498 **** AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
--- 1523,1539 ----
  				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  				 errmsg("cannot disallow connections for current database")));
  
+ 	if (dcatalogsecurity && BoolGetDatum(dbcatalogsecurity) && !pg_database_tuple->datcatalogsecurity)
+ 	{
+ 		CreateCatalogPolicy();
+ 		CommandCounterIncrement();
+ 	}
+ 	else if (pg_database_tuple->datcatalogsecurity)
+ 	{
+ 		RemoveCatalogPolicy();
+ 		CommandCounterIncrement();
+ 	}
+ 
  	/*
  	 * Build an updated tuple, perusing the information just obtained
  	 */
***************
*** 1515,1520 **** AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
--- 1556,1566 ----
  		new_record[Anum_pg_database_datconnlimit - 1] = Int32GetDatum(dbconnlimit);
  		new_record_repl[Anum_pg_database_datconnlimit - 1] = true;
  	}
+ 	if (dcatalogsecurity)
+ 	{
+ 		new_record[Anum_pg_database_datcatalogsecurity - 1] = BoolGetDatum(dbcatalogsecurity);
+ 		new_record_repl[Anum_pg_database_datcatalogsecurity - 1] = true;
+ 	}
  
  	newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel), new_record,
  								 new_record_nulls, new_record_repl);
*** a/src/backend/commands/policy.c
--- b/src/backend/commands/policy.c
***************
*** 22,31 ****
--- 22,75 ----
  #include "catalog/indexing.h"
  #include "catalog/namespace.h"
  #include "catalog/objectaccess.h"
+ #include "catalog/pg_aggregate.h"
+ #include "catalog/pg_am.h"
+ #include "catalog/pg_amop.h"
+ #include "catalog/pg_amproc.h"
+ #include "catalog/pg_attrdef.h"
+ #include "catalog/pg_attribute.h"
  #include "catalog/pg_authid.h"
+ #include "catalog/pg_cast.h"
+ #include "catalog/pg_class.h"
+ #include "catalog/pg_collation.h"
+ #include "catalog/pg_constraint.h"
+ #include "catalog/pg_conversion.h"
+ #include "catalog/pg_db_role_setting.h"
+ #include "catalog/pg_default_acl.h"
+ #include "catalog/pg_depend.h"
+ #include "catalog/pg_description.h"
+ #include "catalog/pg_enum.h"
+ #include "catalog/pg_event_trigger.h"
+ #include "catalog/pg_extension.h"
+ #include "catalog/pg_foreign_data_wrapper.h"
+ #include "catalog/pg_foreign_server.h"
+ #include "catalog/pg_foreign_table.h"
+ #include "catalog/pg_index.h"
+ #include "catalog/pg_inherits.h"
+ #include "catalog/pg_language.h"
+ #include "catalog/pg_largeobject.h"
+ #include "catalog/pg_largeobject_metadata.h"
+ #include "catalog/pg_namespace.h"
+ #include "catalog/pg_opclass.h"
+ #include "catalog/pg_operator.h"
+ #include "catalog/pg_opfamily.h"
  #include "catalog/pg_policy.h"
+ #include "catalog/pg_proc.h"
+ #include "catalog/pg_range.h"
+ #include "catalog/pg_rewrite.h"
+ #include "catalog/pg_seclabel.h"
+ #include "catalog/pg_statistic.h"
+ #include "catalog/pg_transform.h"
+ #include "catalog/pg_trigger.h"
+ #include "catalog/pg_ts_config.h"
+ #include "catalog/pg_ts_config_map.h"
+ #include "catalog/pg_ts_dict.h"
+ #include "catalog/pg_ts_parser.h"
+ #include "catalog/pg_ts_template.h"
  #include "catalog/pg_type.h"
+ #include "catalog/pg_user_mapping.h"
  #include "commands/policy.h"
+ #include "executor/spi.h"
  #include "miscadmin.h"
  #include "nodes/makefuncs.h"
  #include "nodes/pg_list.h"
***************
*** 44,56 ****
--- 88,109 ----
  #include "utils/lsyscache.h"
  #include "utils/memutils.h"
  #include "utils/rel.h"
+ #include "utils/snapmgr.h"
  #include "utils/syscache.h"
  
+ #define CATALOG_POLICY_STRING_SIZE 2000
+ 
  static void RangeVarCallbackForPolicy(const RangeVar *rv,
  						  Oid relid, Oid oldrelid, void *arg);
  static char parse_policy_command(const char *cmd_name);
  static Datum *policy_role_list_to_array(List *roles, int *num_roles);
  
+ static bool generate_catalog_create_policy_string(HeapTuple cache_tuple, char *buf);
+ static bool generate_catalog_drop_policy_string(HeapTuple cache_tuple, char *buf);
+ 
+ /* variable to identify whether pg_policy relcache is built or not? */
+ bool policyRelcacheBuilt = false;
+ 
  /*
   * Callback to RangeVarGetRelidExtended().
   *
***************
*** 194,199 **** RelationBuildRowSecurity(Relation relation)
--- 247,264 ----
  	MemoryContext oldcxt = CurrentMemoryContext;
  	RowSecurityDesc *volatile rsdesc = NULL;
  
+ 	/* */
+ 	if (!criticalRelcachesBuilt || !criticalSharedRelcachesBuilt)
+ 		return;
+ 
+  	if (relation->rd_id == PolicyRelationId)
+ 	{
+ 		if (policyRelcacheBuilt)
+ 			return;
+ 		else
+ 			policyRelcacheBuilt = true;
+ 	}
+ 
  	/*
  	 * Create a memory context to hold everything associated with this
  	 * relation's row security policy.  This makes it easy to clean up during
***************
*** 325,330 **** RelationBuildRowSecurity(Relation relation)
--- 390,402 ----
  		/* Delete rscxt, first making sure it isn't active */
  		MemoryContextSwitchTo(oldcxt);
  		MemoryContextDelete(rscxt);
+ 		
+ 		if (relation->rd_id == PolicyRelationId)
+ 		{
+ 			Assert(policyRelcacheBuilt == true);
+ 			policyRelcacheBuilt = false;
+ 		}
+ 
  		PG_RE_THROW();
  	}
  	PG_END_TRY();
***************
*** 407,412 **** RemovePolicyById(Oid policy_id)
--- 479,1032 ----
  	heap_close(pg_policy_rel, RowExclusiveLock);
  }
  
+ static bool 
+ generate_catalog_create_policy_string(HeapTuple tuple, char *buf)
+ {
+ 	bool can_create_policy = false;
+ 	Form_pg_class pg_class_tuple;
+ 	Oid	relationid;
+ 
+ 	pg_class_tuple = (Form_pg_class)GETSTRUCT(tuple);
+ 	relationid = HeapTupleGetOid(tuple);
+ 
+ 	if (IsSharedRelation(relationid))
+ 		return can_create_policy;
+ 
+ 	switch (relationid)
+ 	{
+ 		/*
+ 		 * Following catalog tables data is accessible to all roles. 
+ 		 * So they doesn't need any speicific RLS policies on them.
+ 		 */ 
+ 		case AggregateRelationId:
+ 		case AccessMethodRelationId:
+ 		case AccessMethodOperatorRelationId:
+ 		case AccessMethodProcedureRelationId:
+ 			break;
+ 		case AttrDefaultRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" (has_column_privilege(adrelid, adnum,'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case AttributeRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" (has_column_privilege(attrelid, attnum,'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case CastRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR has_cast_privilege(oid, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case RelationRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR has_table_privilege(oid,'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case CollationRelationId:
+ 			/*sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR (pg_get_userbyid(collowner) = current_user))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;*/
+ 			break;
+ 		case ConstraintRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR has_constraint_privilege(oid,'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case ConversionRelationId:
+ 			/*sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR (pg_get_userbyid(conowner) = current_user))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;*/
+ 			break;
+ 		case DefaultAclRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" (pg_get_userbyid(defaclrole) = current_user)",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case DependRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((classid = 2600)"
+ 				" OR (classid = 2605 AND has_cast_privilege(objid, 'any'))"
+ 				" OR (classid = 3456)"
+ 				" OR (classid = 1249 AND has_column_privilege(objid, objsubid::smallint, 'any'))"
+ 				" OR (classid = 2606 AND has_constraint_privilege(objid, 'any'))"
+ 				" OR (classid = 2607)"
+ 				" OR (classid = 1262 AND has_database_privilege(objid, 'any'))"
+ 				" OR (classid = 1247 AND has_type_privilege(objid, 'any'))"
+ 				" OR (classid = 3079)"
+ 				" OR (classid = 3466)"
+ 				" OR (classid = 2328 AND has_foreign_data_wrapper_privilege(objid, 'any'))"
+ 				" OR (classid = 1259 AND has_table_privilege(objid,'any'))"
+ 				" OR (classid = 1255 AND has_function_privilege(objid, 'any'))"
+ 				" OR (classid = 2601)"
+ 				" OR (classid = 2602)"
+ 				" OR (classid = 2603)"
+ 				" OR (classid = 2604 AND has_column_default_privilege(objid, 'any'))"
+ 				" OR (classid = 2612 AND has_language_privilege(objid, 'any'))"
+ 				" OR (classid = 2613)"
+ 				" OR (classid = 2616)"
+ 				" OR (classid = 2617)"
+ 				" OR (classid = 2753)"
+ 				" OR (classid = 3501)"
+ 				" OR (classid = 3466)"
+ 				" OR (classid = 3079)"
+ 				" OR (classid = 3256 AND has_policy_privilege(objid, 'any'))"
+ 				" OR (classid = 1260 AND pg_has_role(objid, 'any'))"
+ 				" OR (classid = 2618)"
+ 				" OR (classid = 2615 AND has_schema_privilege(objid, 'any'))"
+ 				" OR (classid = 1417 AND has_server_privilege(objid, 'any'))"
+ 				" OR (classid = 1213 AND has_tablespace_privilege(objid, 'any'))"
+ 				" OR (classid = 3600)"
+ 				" OR (classid = 3601)"
+ 				" OR (classid = 3602)"
+ 				" OR (classid = 3764)"
+ 				" OR (classid = 3576)"
+ 				" OR (classid = 2620 AND has_trigger_privilege(objid, 'any'))"
+ 				" OR (classid = 1418 AND has_user_mapping_privilege(objid, 'any')))", 
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case DescriptionRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((classoid = 2600)"
+ 				" OR (classoid = 2605 AND has_cast_privilege(objoid, 'any'))"
+ 				" OR (classoid = 3456)"
+ 				" OR (classoid = 1249 AND has_column_privilege(objoid, objsubid::smallint, 'any'))"
+ 				" OR (classoid = 2606 AND has_constraint_privilege(objoid, 'any'))"
+ 				" OR (classoid = 2607)"
+ 				" OR (classoid = 1262 AND has_database_privilege(objoid, 'any'))"
+ 				" OR (classoid = 1247 AND has_type_privilege(objoid, 'any'))"
+ 				" OR (classoid = 3079)"
+ 				" OR (classoid = 3466)"
+ 				" OR (classoid = 2328 AND has_foreign_data_wrapper_privilege(objoid, 'any'))"
+ 				" OR (classoid = 1259 AND has_table_privilege(objoid,'any'))"
+ 				" OR (classoid = 1255 AND has_function_privilege(objoid, 'any'))"
+ 				" OR (classoid = 2613)"
+ 				" OR (classoid = 2616)"
+ 				" OR (classoid = 2617)"
+ 				" OR (classoid = 2753)"
+ 				" OR (classoid = 2995)"
+ 				" OR (classoid = 3256 AND has_policy_privilege(objoid, 'any'))"
+ 				" OR (classoid = 1260 AND pg_has_role(objoid, 'any'))"
+ 				" OR (classoid = 2618)"
+ 				" OR (classoid = 2615 AND has_schema_privilege(objoid, 'any'))"
+ 				" OR (classoid = 1417 AND has_server_privilege(objoid, 'any'))"
+ 				" OR (classoid = 1213 AND has_tablespace_privilege(objoid, 'any'))"
+ 				" OR (classoid = 3541)"
+ 				" OR (classoid = 3600)"
+ 				" OR (classoid = 3601)"
+ 				" OR (classoid = 3602)"
+ 				" OR (classoid = 3764)"
+ 				" OR (classoid = 3576)"
+ 				" OR (classoid = 2620 AND has_trigger_privilege(objoid, 'any')))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case EnumRelationId:
+ 			break;
+ 		case EventTriggerRelationId:
+ 			break;
+ 		case ExtensionRelationId:
+ 			break;
+ 		case ForeignDataWrapperRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR has_foreign_data_wrapper_privilege(oid,'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case ForeignServerRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR has_server_privilege(oid,'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case ForeignTableRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" (has_table_privilege(ftrelid, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case IndexRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" (has_table_privilege(indrelid, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case InheritsRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" (has_table_privilege(inhrelid, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case LanguageRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR has_language_privilege(oid, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case LargeObjectRelationId:
+ 			break;
+ 		case LargeObjectMetadataRelationId:
+ 			break;
+ 		case NamespaceRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR has_schema_privilege(oid, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case OperatorClassRelationId:
+ 			/*sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR (pg_get_userbyid(opcowner) = current_user))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;*/
+ 			break;
+ 		case OperatorRelationId:
+ 			/*sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR (pg_get_userbyid(oprowner) = current_user))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;*/
+ 			break;
+ 		case OperatorFamilyRelationId:
+ 			/*sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR (pg_get_userbyid(opfowner) = current_user))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;*/
+ 			break;
+ 		case PolicyRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR has_table_privilege(polrelid, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case ProcedureRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR has_function_privilege(oid, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case RangeRelationId:
+ 			break;
+ 		case RewriteRelationId:
+ 			break;
+ 		case SecLabelRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((classoid = 2600)"
+ 				" OR (classoid = 1249 AND has_column_privilege(objoid, objsubid::smallint, 'any'))"
+ 				" OR (classoid = 1262 AND has_database_privilege(objoid, 'any'))"
+ 				" OR (classoid = 1247 AND has_type_privilege(objoid, 'any'))"
+ 				" OR (classoid = 1259 AND has_table_privilege(objoid,'any'))"
+ 				" OR (classoid = 1255 AND has_function_privilege(objoid, 'any'))"
+ 				" OR (classoid = 2612 AND has_language_privilege(objoid, 'any'))"
+ 				" OR (classoid = 1260 AND pg_has_role(objoid, 'any'))"
+ 				" OR (classoid = 2613)"
+ 				" OR (classoid = 3466)"
+ 				" OR (classoid = 1213 AND has_tablespace_privilege(objoid, 'any')))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case StatisticRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((starelid < 16384) OR has_column_privilege(starelid, staattnum, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case TransformRelationId:
+ 			break;
+ 		case TriggerRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR has_table_privilege(tgrelid, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case TSConfigRelationId:
+ 			break;
+ 		case TSConfigMapRelationId:
+ 			break;
+ 		case TSDictionaryRelationId:
+ 			break;
+ 		case TSParserRelationId:
+ 			break;
+ 		case TSTemplateRelationId:
+ 			break;
+ 		case TypeRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR has_type_privilege(oid, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		case UserMappingRelationId:
+ 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+ 				" ((oid < 16384) OR pg_has_role(umuser, 'any')"
+ 				" OR has_server_privilege(umserver, 'any'))",
+ 				pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 			can_create_policy = true;
+ 			break;
+ 		default:
+ 			can_create_policy = false;
+ 			break;
+ 	}
+ 
+ 	return can_create_policy;
+ }
+ 
+ static bool
+ generate_catalog_drop_policy_string(HeapTuple tuple, char *buf)
+ {
+ 	bool can_create_policy = false;
+ 	Form_pg_class pg_class_tuple;
+ 	Oid	relationid;
+ 
+ 	pg_class_tuple = (Form_pg_class)GETSTRUCT(tuple);
+ 	relationid = HeapTupleGetOid(tuple);
+ 
+ 	if (IsSharedRelation(relationid))
+ 		return can_create_policy;
+ 
+ 	switch (relationid)
+ 	{
+ 		/*
+ 		* Following catalog tables data is accessible to all roles.
+ 		* So they doesn't need any speicific RLS policies on them.
+ 		*/
+ 	case AggregateRelationId:
+ 	case AccessMethodRelationId:
+ 	case AccessMethodOperatorRelationId:
+ 	case AccessMethodProcedureRelationId:
+ 	case CollationRelationId:
+ 	case ConversionRelationId:
+ 	case EnumRelationId:
+ 	case EventTriggerRelationId:
+ 	case ExtensionRelationId:
+ 	case LargeObjectRelationId:
+ 	case LargeObjectMetadataRelationId:
+ 	case OperatorClassRelationId:
+ 	case OperatorRelationId:
+ 	case OperatorFamilyRelationId:
+ 	case RangeRelationId:
+ 	case RewriteRelationId:
+ 	case TransformRelationId:
+ 	case TSConfigRelationId:
+ 	case TSConfigMapRelationId:
+ 	case TSDictionaryRelationId:
+ 	case TSParserRelationId:
+ 	case TSTemplateRelationId:
+ 		break;
+ 
+ 	case AttrDefaultRelationId:
+ 	case AttributeRelationId:
+ 	case CastRelationId:
+ 	case RelationRelationId:
+ 	case ConstraintRelationId:
+ 	case DefaultAclRelationId:
+ 	case DependRelationId:
+ 	case DescriptionRelationId:
+ 	case ForeignDataWrapperRelationId:
+ 	case ForeignServerRelationId:
+ 	case ForeignTableRelationId:
+ 	case IndexRelationId:
+ 	case InheritsRelationId:
+ 	case LanguageRelationId:
+ 	case NamespaceRelationId:
+ 	case PolicyRelationId:
+ 	case ProcedureRelationId:
+ 	case SecLabelRelationId:
+ 	case StatisticRelationId:
+ 	case TriggerRelationId:
+ 	case TypeRelationId:
+ 	case UserMappingRelationId:
+ 		sprintf(buf, "drop policy %s_read_own_data on %s",
+ 			pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+ 		can_create_policy = true;
+ 		break;
+ 
+ 	default:
+ 		can_create_policy = false;
+ 		break;
+ 	}
+ 
+ 	return can_create_policy;
+ }
+ 
+ /*
+  * CreateCatalogPolicy -
+  *	 handles the execution of the ALTER DATBASE ROW LEVEL SECUTIRY = TRUE command.
+  */
+ void
+ CreateCatalogPolicy()
+ {
+ 	Relation	rel;
+ 	ScanKeyData scankey;
+ 	SysScanDesc scan;
+ 	HeapTuple	tuple;
+ 	bool allow_sytem_table_mods_old;
+ 	char *buf;
+ 
+ 	/*
+ 	 * Get all catalog relations from pg_class system table and
+ 	 * enable the row level security along with the catalog policy
+ 	 * command.
+ 	 */
+ 	SPI_connect();
+ 	PushActiveSnapshot(GetTransactionSnapshot());
+ 
+ 	rel = heap_open(RelationRelationId, RowExclusiveLock);
+ 	ScanKeyInit(&scankey,
+ 				ObjectIdAttributeNumber,
+ 				BTLessStrategyNumber, F_OIDLT,
+ 				ObjectIdGetDatum(FirstNormalObjectId));
+ 	scan = systable_beginscan(rel, ClassOidIndexId, true,
+ 								NULL, 1, &scankey);
+ 
+ 	buf = palloc(CATALOG_POLICY_STRING_SIZE);
+ 	allow_sytem_table_mods_old = allowSystemTableMods;
+ 	allowSystemTableMods = true;
+ 
+ 	PG_TRY();
+ 	{
+ 		while ((tuple = systable_getnext(scan)) != NULL)
+ 		{
+ 			int			ret;
+ 			HeapTuple	cache_tuple;
+ 			Form_pg_class pg_class_tuple;
+ 			bool can_create_policy;
+ 
+ 			pg_class_tuple = (Form_pg_class)GETSTRUCT(tuple);
+ 			if (pg_class_tuple->relkind != RELKIND_RELATION)
+ 				continue;
+ 				
+ 			cache_tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(HeapTupleGetOid(tuple)));
+ 
+ 			if (!HeapTupleIsValid(cache_tuple))
+ 				elog(ERROR, "cache lookup failed for relation %u", HeapTupleGetOid(tuple));
+ 
+ 			can_create_policy = generate_catalog_create_policy_string(cache_tuple, buf);
+ 			if (!can_create_policy)
+ 			{
+ 				heap_freetuple(cache_tuple);
+ 				continue;
+ 			}
+ 	
+ 			ret = SPI_execute(buf, false, 0);
+ 			if (ret != SPI_OK_UTILITY)
+ 				elog(ERROR, "Creating policy failed : error code %d", ret);
+ 
+ 			((Form_pg_class)GETSTRUCT(cache_tuple))->relrowsecurity = true;
+ 			heap_inplace_update(rel, cache_tuple);
+ 
+ 			heap_freetuple(cache_tuple);
+ 		}
+ 	}
+ 	PG_CATCH();
+ 	{
+ 		allowSystemTableMods = allow_sytem_table_mods_old;
+ 		PG_RE_THROW();
+ 	}
+ 	PG_END_TRY();
+ 
+ 	allowSystemTableMods = allow_sytem_table_mods_old;
+ 	pfree(buf);
+ 
+ 	systable_endscan(scan);
+ 	heap_close(rel, NoLock);
+ 
+ 	SPI_finish();
+ 	PopActiveSnapshot();
+ }
+ 
+ /*
+  * RemoveCatalogPolicy -
+  *	 handles the execution of the ALTER DATBASE ROW LEVEL SECUTIRY = FALSE command.
+  */
+ void
+ RemoveCatalogPolicy()
+ {
+ 	Relation	rel;
+ 	ScanKeyData scankey;
+ 	SysScanDesc scan;
+ 	HeapTuple	tuple;
+ 	Form_pg_class pg_class_tuple;
+ 	bool allow_sytem_table_mods_old;
+ 	char		 *buf;
+ 
+ 	/*
+ 	* Get all catalog relations from pg_class system table and
+ 	* enable the row level security along with the catalog policy
+ 	* command.
+ 	*/
+ 	SPI_connect();
+ 	PushActiveSnapshot(GetTransactionSnapshot());
+ 
+ 	rel = heap_open(RelationRelationId, RowExclusiveLock);
+ 	ScanKeyInit(&scankey,
+ 		ObjectIdAttributeNumber,
+ 		BTLessStrategyNumber, F_OIDLT,
+ 		ObjectIdGetDatum(FirstNormalObjectId));
+ 	scan = systable_beginscan(rel, ClassOidIndexId, true,
+ 		NULL, 1, &scankey);
+ 
+ 	buf = palloc(CATALOG_POLICY_STRING_SIZE);
+ 
+ 	allow_sytem_table_mods_old = allowSystemTableMods;
+ 	allowSystemTableMods = true;
+ 
+ 	PG_TRY();
+ 	{
+ 		while ((tuple = systable_getnext(scan)) != NULL)
+ 		{
+ 			int			ret;
+ 			HeapTuple	cache_tuple;
+ 			bool		can_drop_policy;
+ 
+ 			pg_class_tuple = (Form_pg_class)GETSTRUCT(tuple);
+ 			if (pg_class_tuple->relkind != RELKIND_RELATION)
+ 				continue;
+ 
+ 			cache_tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(HeapTupleGetOid(tuple)));
+ 			if (!HeapTupleIsValid(cache_tuple))
+ 				elog(ERROR, "cache lookup failed for relation %u", HeapTupleGetOid(tuple));
+ 
+ 			can_drop_policy = generate_catalog_drop_policy_string(cache_tuple, buf);
+ 			if (!can_drop_policy)
+ 			{
+ 				heap_freetuple(cache_tuple);
+ 				continue;
+ 			}			
+ 
+ 			ret = SPI_execute(buf, false, 0);
+ 			if (ret != SPI_OK_UTILITY)
+ 				elog(ERROR, "Creating policy failed : error code %d", ret);
+ 
+ 			((Form_pg_class)GETSTRUCT(cache_tuple))->relrowsecurity = false;
+ 			heap_inplace_update(rel, cache_tuple);
+ 
+ 			heap_freetuple(cache_tuple);
+ 		}
+ 	}
+ 	PG_CATCH();
+ 	{
+ 		allowSystemTableMods = allow_sytem_table_mods_old;
+ 		PG_RE_THROW();
+ 	}
+ 	PG_END_TRY();
+ 
+ 	allowSystemTableMods = allow_sytem_table_mods_old;
+ 
+ 	pfree(buf);
+ 	systable_endscan(scan);
+ 	heap_close(rel, NoLock);
+ 
+ 	SPI_finish();
+ 	PopActiveSnapshot();
+ }
+ 
  /*
   * CreatePolicy -
   *	 handles the execution of the CREATE POLICY command.
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 8881,8886 **** createdb_opt_name:
--- 8881,8887 ----
  			| OWNER							{ $$ = pstrdup($1); }
  			| TABLESPACE					{ $$ = pstrdup($1); }
  			| TEMPLATE						{ $$ = pstrdup($1); }
+ 			| CATALOG_P SECURITY				{ $$ = pstrdup("catalog_security"); }
  		;
  
  /*
*** a/src/backend/utils/adt/acl.c
--- b/src/backend/utils/adt/acl.c
***************
*** 17,27 ****
  #include <ctype.h>
  
  #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 <ctype.h>
  
  #include "access/htup_details.h"
+ #include "access/sysattr.h"
+ #include "catalog/indexing.h"
  #include "catalog/namespace.h"
  #include "catalog/pg_authid.h"
  #include "catalog/pg_auth_members.h"
! #include "catalog/pg_attrdef.h"
! #include "catalog/pg_cast.h"
  #include "catalog/pg_class.h"
+ #include "catalog/pg_constraint.h"
+ #include "catalog/pg_policy.h"
+ #include "catalog/pg_trigger.h"
+ #include "catalog/pg_type.h"
+ #include "catalog/pg_user_mapping.h"
  #include "commands/dbcommands.h"
  #include "commands/proclang.h"
  #include "commands/tablespace.h"
***************
*** 31,36 ****
--- 39,45 ----
  #include "utils/acl.h"
  #include "utils/builtins.h"
  #include "utils/catcache.h"
+ #include "utils/fmgroids.h"
  #include "utils/inval.h"
  #include "utils/lsyscache.h"
  #include "utils/memutils.h"
***************
*** 5267,5269 **** get_rolespec_name(const Node *node)
--- 5276,5568 ----
  
  	return rolename;
  }
+ 
+ /*
+  * has_cast_privilege_id
+  *		Check user privileges on a cast given
+  *		cast oid, and text priv name.
+  */
+ Datum
+ has_cast_privilege_id(PG_FUNCTION_ARGS)
+ {
+ 	Oid			castoid = PG_GETARG_OID(0);
+ 	text	   *priv_type_text = PG_GETARG_TEXT_P(1);
+ 	Oid			roleid;
+ 	AclMode		mode;
+ 	AclResult	aclresult1;
+ 	AclResult	aclresult2;
+ 	Relation	castDesc;
+ 	ScanKeyData skey[1];
+ 	SysScanDesc rcscan;
+ 	HeapTuple	tup;
+ 	Form_pg_cast castForm;
+ 
+ 	roleid = GetUserId();
+ 	mode = convert_server_priv_string(priv_type_text);
+ 
+ 	castDesc = heap_open(CastRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&skey[0],
+ 		ObjectIdAttributeNumber,
+ 		BTEqualStrategyNumber, F_OIDEQ,
+ 		ObjectIdGetDatum(castoid));
+ 
+ 	rcscan = systable_beginscan(castDesc, CastOidIndexId, true,
+ 		NULL, 1, skey);
+ 
+ 	tup = systable_getnext(rcscan);
+ 
+ 	if (!HeapTupleIsValid(tup))
+ 	{
+ 		systable_endscan(rcscan);
+ 		heap_close(castDesc, AccessShareLock);
+ 		PG_RETURN_NULL();
+ 	}
+ 
+ 	castForm = (Form_pg_cast)GETSTRUCT(tup);
+ 
+ 	aclresult1 = pg_type_aclcheck(castForm->castsource, roleid, mode);
+ 	aclresult2 = pg_type_aclcheck(castForm->casttarget, roleid, mode);
+ 
+ 	systable_endscan(rcscan);
+ 	heap_close(castDesc, AccessShareLock);
+ 	PG_RETURN_BOOL((aclresult1 == ACLCHECK_OK) || (aclresult2 == ACLCHECK_OK));
+ }
+ 
+ /*
+  * has_constraint_privilege_id
+  *		Check user privileges on a constraint given
+  *		constraint oid, and text priv name.
+  */
+ Datum
+ has_constraint_privilege_id(PG_FUNCTION_ARGS)
+ {
+ 	Oid			constraintoid = PG_GETARG_OID(0);
+ 	text	   *priv_type_text = PG_GETARG_TEXT_P(1);
+ 	Oid			roleid;
+ 	AclMode		mode;
+ 	AclResult	aclresult;
+ 	Relation	constraintDesc;
+ 	ScanKeyData skey[1];
+ 	SysScanDesc rcscan;
+ 	HeapTuple	tup;
+ 	Form_pg_constraint constraintForm;
+ 
+ 	roleid = GetUserId();
+ 	mode = convert_server_priv_string(priv_type_text);
+ 
+ 	constraintDesc = heap_open(ConstraintRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&skey[0],
+ 		ObjectIdAttributeNumber,
+ 		BTEqualStrategyNumber, F_OIDEQ,
+ 		ObjectIdGetDatum(constraintoid));
+ 
+ 	rcscan = systable_beginscan(constraintDesc, ConstraintOidIndexId, true,
+ 		NULL, 1, skey);
+ 
+ 	tup = systable_getnext(rcscan);
+ 
+ 	if (!HeapTupleIsValid(tup))
+ 	{
+ 		systable_endscan(rcscan);
+ 		heap_close(constraintDesc, AccessShareLock);
+ 		PG_RETURN_NULL();
+ 	}
+ 
+ 	constraintForm = (Form_pg_constraint)GETSTRUCT(tup);
+ 
+ 	if (constraintForm->contypid)
+ 		aclresult = pg_type_aclcheck(constraintForm->contypid, roleid, mode);
+ 	else
+ 		aclresult = pg_class_aclcheck(constraintForm->conrelid, roleid, mode);
+ 			
+ 	systable_endscan(rcscan);
+ 	heap_close(constraintDesc, AccessShareLock);
+ 	PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+ }
+ 
+ /*
+  * has_column_default_privilege_id
+  *		Check user privileges on a column default given
+  *		attrdefault oid, and text priv name.
+  */
+ Datum
+ has_column_default_privilege_id(PG_FUNCTION_ARGS)
+ {
+ 	Oid			attrdefaulttoid = PG_GETARG_OID(0);
+ 	text	   *priv_type_text = PG_GETARG_TEXT_P(1);
+ 	Oid			roleid;
+ 	AclMode		mode;
+ 	int			privresult;
+ 	Relation	attrDefaultDesc;
+ 	ScanKeyData skey[1];
+ 	SysScanDesc rcscan;
+ 	HeapTuple	tup;
+ 	Form_pg_attrdef attrDefForm;
+ 
+ 	roleid = GetUserId();
+ 	mode = convert_server_priv_string(priv_type_text);
+ 
+ 	attrDefaultDesc = heap_open(AttrDefaultRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&skey[0],
+ 		ObjectIdAttributeNumber,
+ 		BTEqualStrategyNumber, F_OIDEQ,
+ 		ObjectIdGetDatum(attrdefaulttoid));
+ 
+ 	rcscan = systable_beginscan(attrDefaultDesc, AttrDefaultOidIndexId, true,
+ 		NULL, 1, skey);
+ 
+ 	tup = systable_getnext(rcscan);
+ 
+ 	if (!HeapTupleIsValid(tup))
+ 	{
+ 		systable_endscan(rcscan);
+ 		heap_close(attrDefaultDesc, AccessShareLock);
+ 		PG_RETURN_NULL();
+ 	}
+ 
+ 	attrDefForm = (Form_pg_attrdef)GETSTRUCT(tup);
+ 
+ 	privresult = column_privilege_check(attrDefForm->adrelid, attrDefForm->adnum, roleid, mode);
+ 	
+ 	systable_endscan(rcscan);
+ 	heap_close(attrDefaultDesc, AccessShareLock);
+ 
+ 	if (privresult < 0)
+ 		PG_RETURN_NULL();
+ 	PG_RETURN_BOOL(privresult);
+ }
+ 
+ /*
+  * has_policy_privilege_id
+  *		Check user privileges on a policy given
+  *		policy oid, and text priv name.
+  */
+ Datum
+ has_policy_privilege_id(PG_FUNCTION_ARGS)
+ {
+ 	Oid			policyoid = PG_GETARG_OID(0);
+ 	text	   *priv_type_text = PG_GETARG_TEXT_P(1);
+ 	Oid			roleid;
+ 	AclMode		mode;
+ 	AclResult	aclresult;
+ 	Relation	policyDesc;
+ 	ScanKeyData skey[1];
+ 	SysScanDesc rcscan;
+ 	HeapTuple	tup;
+ 	Form_pg_policy policyForm;
+ 
+ 	roleid = GetUserId();
+ 	mode = convert_server_priv_string(priv_type_text);
+ 
+ 	policyDesc = heap_open(PolicyRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&skey[0],
+ 		ObjectIdAttributeNumber,
+ 		BTEqualStrategyNumber, F_OIDEQ,
+ 		ObjectIdGetDatum(policyoid));
+ 
+ 	rcscan = systable_beginscan(policyDesc, PolicyOidIndexId, true,
+ 		NULL, 1, skey);
+ 
+ 	tup = systable_getnext(rcscan);
+ 
+ 	if (!HeapTupleIsValid(tup))
+ 	{
+ 		systable_endscan(rcscan);
+ 		heap_close(policyDesc, AccessShareLock);
+ 		PG_RETURN_NULL();
+ 	}
+ 
+ 	policyForm = (Form_pg_policy)GETSTRUCT(tup);
+ 
+ 	aclresult = pg_class_aclcheck(policyForm->polrelid, roleid, mode);
+ 
+ 	systable_endscan(rcscan);
+ 	heap_close(policyDesc, AccessShareLock);
+ 	PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+ }
+ 
+ /*
+  * has_trigger_privilege_id
+  *		Check user privileges on a trigger given
+  *		trigger oid, and text priv name.
+  */
+ Datum
+ has_trigger_privilege_id(PG_FUNCTION_ARGS)
+ {
+ 	Oid			triggeroid = PG_GETARG_OID(0);
+ 	text	   *priv_type_text = PG_GETARG_TEXT_P(1);
+ 	Oid			roleid;
+ 	AclMode		mode;
+ 	AclResult	aclresult;
+ 	Relation	triggerDesc;
+ 	ScanKeyData skey[1];
+ 	SysScanDesc rcscan;
+ 	HeapTuple	tup;
+ 	Form_pg_trigger triggerForm;
+ 
+ 	roleid = GetUserId();
+ 	mode = convert_server_priv_string(priv_type_text);
+ 
+ 	triggerDesc = heap_open(PolicyRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&skey[0],
+ 		ObjectIdAttributeNumber,
+ 		BTEqualStrategyNumber, F_OIDEQ,
+ 		ObjectIdGetDatum(triggeroid));
+ 
+ 	rcscan = systable_beginscan(triggerDesc, PolicyOidIndexId, true,
+ 		NULL, 1, skey);
+ 
+ 	tup = systable_getnext(rcscan);
+ 
+ 	if (!HeapTupleIsValid(tup))
+ 	{
+ 		systable_endscan(rcscan);
+ 		heap_close(triggerDesc, AccessShareLock);
+ 		PG_RETURN_NULL();
+ 	}
+ 
+ 	triggerForm = (Form_pg_trigger)GETSTRUCT(tup);
+ 
+ 	aclresult = pg_class_aclcheck(triggerForm->tgrelid, roleid, mode);
+ 
+ 	systable_endscan(rcscan);
+ 	heap_close(triggerDesc, AccessShareLock);
+ 	PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+ }
+ 
+ /*
+ * has_user_mapping_privilege_id
+ *		Check user privileges on a user mapping given
+ *		user_mapping oid, and text priv name.
+ */
+ Datum
+ has_user_mapping_privilege_id(PG_FUNCTION_ARGS)
+ {
+ 	Oid			usermapoid = PG_GETARG_OID(0);
+ 	text	   *priv_type_text = PG_GETARG_TEXT_P(1);
+ 	Oid			roleid;
+ 	AclMode		mode;
+ 	AclResult	aclresult1;
+ 	AclResult	aclresult2;
+ 	HeapTuple	tup;
+ 	Form_pg_user_mapping usermapForm;
+ 
+ 	roleid = GetUserId();
+ 	mode = convert_server_priv_string(priv_type_text);
+ 
+ 	tup = SearchSysCache1(USERMAPPINGOID, usermapoid);
+ 	if (!HeapTupleIsValid(tup))
+ 		PG_RETURN_NULL();
+ 
+ 	usermapForm = (Form_pg_user_mapping)GETSTRUCT(tup);
+ 
+ 	aclresult1 = pg_role_aclcheck(usermapForm->umuser, roleid, mode);
+ 	aclresult2 = pg_foreign_server_aclcheck(usermapForm->umserver, roleid, mode);
+ 		
+ 	PG_RETURN_BOOL((aclresult1 == ACLCHECK_OK) || (aclresult2 == ACLCHECK_OK));
+ }
*** a/src/backend/utils/cache/relcache.c
--- b/src/backend/utils/cache/relcache.c
***************
*** 2076,2081 **** RelationClearRelation(Relation relation, bool rebuild)
--- 2076,2083 ----
  	 */
  	if (relation->rd_isnailed)
  	{
+ 		HeapTuple pg_class_tuple;
+ 
  		RelationInitPhysicalAddr(relation);
  
  		if (relation->rd_rel->relkind == RELKIND_INDEX)
***************
*** 2084,2089 **** RelationClearRelation(Relation relation, bool rebuild)
--- 2086,2112 ----
  			if (relation->rd_refcnt > 1 && IsTransactionState())
  				RelationReloadIndexInfo(relation);
  		}
+ 
+ 		/* 
+ 		 * A nailed-in system relation never ever blow away from rel cache, because
+ 		 * we'd be unable to recover. So for such relations, we will update the
+ 		 * row security descriptor if it is enabled. Usually this happens during
+ 		 * RelationBuildDesc function, but for nailed-in system relations, we will 
+ 		 * do it here.
+ 		 */
+ 		if (criticalRelcachesBuilt 
+ 			&& criticalSharedRelcachesBuilt
+ 			&& IsTransactionState())
+ 		{
+ 			/*
+ 			 * find the tuple in pg_class corresponding to the given relation id
+ 			 */
+ 			pg_class_tuple = ScanPgRelation(RelationGetRelid(relation), true, false);
+ 
+ 			if (((Form_pg_class)GETSTRUCT(pg_class_tuple))->relrowsecurity)
+ 				RelationBuildRowSecurity(relation);
+ 			heap_freetuple(pg_class_tuple);
+ 		}
  		return;
  	}
  
*** a/src/include/catalog/pg_database.h
--- b/src/include/catalog/pg_database.h
***************
*** 43,49 **** CATALOG(pg_database,1262) BKI_SHARED_RELATION BKI_ROWTYPE_OID(1248) BKI_SCHEMA_M
  	TransactionId datfrozenxid; /* all Xids < this are frozen in this DB */
  	TransactionId datminmxid;	/* all multixacts in the DB are >= this */
  	Oid			dattablespace;	/* default table space for this DB */
! 
  #ifdef CATALOG_VARLEN			/* variable-length fields start here */
  	aclitem		datacl[1];		/* access permissions */
  #endif
--- 43,49 ----
  	TransactionId datfrozenxid; /* all Xids < this are frozen in this DB */
  	TransactionId datminmxid;	/* all multixacts in the DB are >= this */
  	Oid			dattablespace;	/* default table space for this DB */
! 	bool		datcatalogsecurity; /* catalog security is enabled? */
  #ifdef CATALOG_VARLEN			/* variable-length fields start here */
  	aclitem		datacl[1];		/* access permissions */
  #endif
***************
*** 60,81 **** typedef FormData_pg_database *Form_pg_database;
   *		compiler constants for pg_database
   * ----------------
   */
! #define Natts_pg_database				13
! #define Anum_pg_database_datname		1
! #define Anum_pg_database_datdba			2
! #define Anum_pg_database_encoding		3
! #define Anum_pg_database_datcollate		4
! #define Anum_pg_database_datctype		5
! #define Anum_pg_database_datistemplate	6
! #define Anum_pg_database_datallowconn	7
! #define Anum_pg_database_datconnlimit	8
! #define Anum_pg_database_datlastsysoid	9
! #define Anum_pg_database_datfrozenxid	10
! #define Anum_pg_database_datminmxid		11
! #define Anum_pg_database_dattablespace	12
! #define Anum_pg_database_datacl			13
  
! DATA(insert OID = 1 (  template1 PGUID ENCODING "LC_COLLATE" "LC_CTYPE" t t -1 0 0 1 1663 _null_));
  SHDESCR("default template for new databases");
  #define TemplateDbOid			1
  
--- 60,82 ----
   *		compiler constants for pg_database
   * ----------------
   */
! #define Natts_pg_database						14
! #define Anum_pg_database_datname				1
! #define Anum_pg_database_datdba					2
! #define Anum_pg_database_encoding				3
! #define Anum_pg_database_datcollate				4
! #define Anum_pg_database_datctype				5
! #define Anum_pg_database_datistemplate			6
! #define Anum_pg_database_datallowconn			7
! #define Anum_pg_database_datconnlimit			8
! #define Anum_pg_database_datlastsysoid			9
! #define Anum_pg_database_datfrozenxid			10
! #define Anum_pg_database_datminmxid				11
! #define Anum_pg_database_dattablespace			12
! #define Anum_pg_database_datcatalogsecurity	13
! #define Anum_pg_database_datacl					14
  
! DATA(insert OID = 1 (  template1 PGUID ENCODING "LC_COLLATE" "LC_CTYPE" t t -1 0 0 1 1663 f _null_));
  SHDESCR("default template for new databases");
  #define TemplateDbOid			1
  
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 3642,3647 **** DESCR("current user privilege on role by role name");
--- 3642,3665 ----
  DATA(insert OID = 2710 (  pg_has_role		PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 16 "26 25" _null_ _null_ _null_ _null_ _null_ pg_has_role_id _null_ _null_ _null_ ));
  DESCR("current user privilege on role by role oid");
  
+ DATA(insert OID = 3315 (has_cast_privilege		PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 16 "26 25" _null_ _null_ _null_ _null_ _null_ has_cast_privilege_id _null_ _null_ _null_));
+ DESCR("current user privilege on cast by cast oid");
+ 
+ DATA(insert OID = 3316 (has_constraint_privilege		PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 16 "26 25" _null_ _null_ _null_ _null_ _null_ has_constraint_privilege_id _null_ _null_ _null_));
+ DESCR("current user privilege on contrainst by constraint oid");
+ 
+ DATA(insert OID = 3317 (has_column_default_privilege		PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 16 "26 25" _null_ _null_ _null_ _null_ _null_ has_column_default_privilege_id _null_ _null_ _null_));
+ DESCR("current user privilege on attrdefault by attrdefault oid");
+ 
+ DATA(insert OID = 3318 (has_policy_privilege		PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 16 "26 25" _null_ _null_ _null_ _null_ _null_ has_policy_privilege_id _null_ _null_ _null_));
+ DESCR("current user privilege on policy by policy oid");
+ 
+ DATA(insert OID = 3319 (has_trigger_privilege		PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 16 "26 25" _null_ _null_ _null_ _null_ _null_ has_trigger_privilege_id _null_ _null_ _null_));
+ DESCR("current user privilege on trigger by trigger oid");
+ 
+ DATA(insert OID = 3320 (has_user_mapping_privilege		PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 16 "26 25" _null_ _null_ _null_ _null_ _null_ has_user_mapping_privilege_id _null_ _null_ _null_));
+ DESCR("current user privilege on user mapping by user_mapping oid");
+ 
  DATA(insert OID = 1269 (  pg_column_size		PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 23 "2276" _null_ _null_ _null_ _null_ _null_	pg_column_size _null_ _null_ _null_ ));
  DESCR("bytes required to store the value, perhaps with compression");
  DATA(insert OID = 2322 ( pg_tablespace_size		PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 20 "26" _null_ _null_ _null_ _null_ _null_ pg_tablespace_size_oid _null_ _null_ _null_ ));
*** a/src/include/commands/policy.h
--- b/src/include/commands/policy.h
***************
*** 33,36 **** extern ObjectAddress rename_policy(RenameStmt *stmt);
--- 33,39 ----
  
  extern bool relation_has_policies(Relation rel);
  
+ extern void CreateCatalogPolicy(void);
+ extern void RemoveCatalogPolicy(void);
+ 
  #endif   /* POLICY_H */
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 106,111 **** extern Datum pg_has_role_id_name(PG_FUNCTION_ARGS);
--- 106,117 ----
  extern Datum pg_has_role_id_id(PG_FUNCTION_ARGS);
  extern Datum pg_has_role_name(PG_FUNCTION_ARGS);
  extern Datum pg_has_role_id(PG_FUNCTION_ARGS);
+ extern Datum has_cast_privilege_id(PG_FUNCTION_ARGS);
+ extern Datum has_constraint_privilege_id(PG_FUNCTION_ARGS);
+ extern Datum has_column_default_privilege_id(PG_FUNCTION_ARGS);
+ extern Datum has_policy_privilege_id(PG_FUNCTION_ARGS);
+ extern Datum has_trigger_privilege_id(PG_FUNCTION_ARGS);
+ extern Datum has_user_mapping_privilege_id(PG_FUNCTION_ARGS);
  
  /* bool.c */
  extern Datum boolin(PG_FUNCTION_ARGS);
*** /dev/null
--- b/src/test/regress/expected/multitenancy.out
***************
*** 0 ****
--- 1,358 ----
+ -- Create roles that are used by the following tests
+ create role tenancy_user1 login createdb;
+ create role tenancy_user2 login createdb;
+ create schema tenancy_user1;
+ grant all on schema tenancy_user1 to tenancy_user1;
+ create schema tenancy_user2;
+ grant all on schema tenancy_user2 to tenancy_user2;
+ alter database regression with catalog security = true;
+ -- create objects realted to tenacy_user1
+ SET SESSION ROLE tenancy_user1;
+ create table public.tenancy_user1_tbl1 (tenancy_user1_tbl1_column1 serial,
+ 								tenancy_user1_tbl1_column2 char(10) default 'FUJITSU',
+ 								tenancy_user1_tbl1_column3 int);
+ create index tenancy_user1_tbl1_idx on tenancy_user1_tbl1 (tenancy_user1_tbl1_column3);
+ insert into tenancy_user1_tbl1(tenancy_user1_tbl1_column3) values(1);
+ create table tenancy_user1.tenancy_user1_tbl2 (tenancy_user1_tbl2_column1 serial,
+ 								tenancy_user1_tbl2_column2 char(10) default 'FUJITSU',
+ 								tenancy_user1_tbl2_column3 int);
+ create index tenancy_user1_tbl2_idx on tenancy_user1.tenancy_user1_tbl2 (tenancy_user1_tbl2_column3);
+ create view public.tenancy_user1_view1 as select * from tenancy_user1_tbl1;
+ create materialized view public.tenancy_user1_matview1 as select * from tenancy_user1_tbl1;
+ select * from tenancy_user1_tbl1;
+  tenancy_user1_tbl1_column1 | tenancy_user1_tbl1_column2 | tenancy_user1_tbl1_column3 
+ ----------------------------+----------------------------+----------------------------
+                           1 | FUJITSU                    |                          1
+ (1 row)
+ 
+ select * from tenancy_user1_view1;
+  tenancy_user1_tbl1_column1 | tenancy_user1_tbl1_column2 | tenancy_user1_tbl1_column3 
+ ----------------------------+----------------------------+----------------------------
+                           1 | FUJITSU                    |                          1
+ (1 row)
+ 
+ select * from tenancy_user1_matview1;
+  tenancy_user1_tbl1_column1 | tenancy_user1_tbl1_column2 | tenancy_user1_tbl1_column3 
+ ----------------------------+----------------------------+----------------------------
+                           1 | FUJITSU                    |                          1
+ (1 row)
+ 
+ 								
+ -- verify all system catalogs related to the objects created by tenancy_user1
+ select relname from pg_class where relname = 'tenancy_user1_tbl1';
+       relname       
+ --------------------
+  tenancy_user1_tbl1
+ (1 row)
+ 
+ select relname from pg_class where relname = 'tenancy_user1_tbl1_idx';
+         relname         
+ ------------------------
+  tenancy_user1_tbl1_idx
+ (1 row)
+ 
+ select relname from pg_class where relname = 'tenancy_user1_view1';
+        relname       
+ ---------------------
+  tenancy_user1_view1
+ (1 row)
+ 
+ select relname from pg_class where relname = 'tenancy_user1_matview1';
+         relname         
+ ------------------------
+  tenancy_user1_matview1
+ (1 row)
+ 
+ select attname from pg_attribute where attname = 'tenancy_user1_tbl1_column1';
+           attname           
+ ----------------------------
+  tenancy_user1_tbl1_column1
+  tenancy_user1_tbl1_column1
+  tenancy_user1_tbl1_column1
+ (3 rows)
+ 
+ select adnum, adsrc from pg_attrdef where adrelid in (select oid from pg_class where relname = 'tenancy_user1_tbl1');
+  adnum |                                 adsrc                                  
+ -------+------------------------------------------------------------------------
+      1 | nextval('tenancy_user1_tbl1_tenancy_user1_tbl1_column1_seq'::regclass)
+      2 | 'FUJITSU'::bpchar
+ (2 rows)
+ 
+ select indnatts, indkey from pg_index where indrelid in (select oid from pg_class where relname = 'tenancy_user1_tbl1');
+  indnatts | indkey 
+ ----------+--------
+         1 | 3
+ (1 row)
+ 
+ select nspname, nspowner from pg_namespace where nspname = 'tenancy_user1';
+     nspname    | nspowner 
+ ---------------+----------
+  tenancy_user1 |       10
+ (1 row)
+ 
+ 		
+ -- verify all system views related to the objects created by tenancy_user1
+ select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user1_tbl1';
+  schemaname |      relname       
+ ------------+--------------------
+  public     | tenancy_user1_tbl1
+ (1 row)
+ 
+ select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user1_tbl2' and schemaname = 'tenancy_user1';
+   schemaname   |      relname       
+ ---------------+--------------------
+  tenancy_user1 | tenancy_user1_tbl2
+ (1 row)
+ 
+ 		
+ RESET ROLE;
+ -- create objects realted to tenacy_user2
+ SET SESSION ROLE tenancy_user2;
+ create table public.tenancy_user2_tbl1 (tenancy_user2_tbl1_column1 serial,
+ 								tenancy_user2_tbl1_column2 char(10) default 'FUJITSU',
+ 								tenancy_user2_tbl1_column3 int);
+ create index tenancy_user2_tbl1_idx on tenancy_user2_tbl1 (tenancy_user2_tbl1_column3);
+ insert into tenancy_user2_tbl1(tenancy_user2_tbl1_column3) values(1);
+ create table tenancy_user2.tenancy_user2_tbl2 (tenancy_user2_tbl2_column1 serial,
+ 								tenancy_user2_tbl2_column2 char(10) default 'FUJITSU',
+ 								tenancy_user2_tbl2_column3 int);
+ create index tenancy_user2_tbl2_idx on tenancy_user2.tenancy_user2_tbl2 (tenancy_user2_tbl2_column3);
+ create view public.tenancy_user2_view1 as select * from tenancy_user2_tbl1;
+ create materialized view public.tenancy_user2_matview1 as select * from tenancy_user2_tbl1;
+ select * from tenancy_user2_tbl1;
+  tenancy_user2_tbl1_column1 | tenancy_user2_tbl1_column2 | tenancy_user2_tbl1_column3 
+ ----------------------------+----------------------------+----------------------------
+                           1 | FUJITSU                    |                          1
+ (1 row)
+ 
+ select * from tenancy_user2_view1;
+  tenancy_user2_tbl1_column1 | tenancy_user2_tbl1_column2 | tenancy_user2_tbl1_column3 
+ ----------------------------+----------------------------+----------------------------
+                           1 | FUJITSU                    |                          1
+ (1 row)
+ 
+ select * from tenancy_user2_matview1;
+  tenancy_user2_tbl1_column1 | tenancy_user2_tbl1_column2 | tenancy_user2_tbl1_column3 
+ ----------------------------+----------------------------+----------------------------
+                           1 | FUJITSU                    |                          1
+ (1 row)
+ 
+ -- verify all system catalogs related to the objects created by tenancy_user2
+ select relname from pg_class where relname = 'tenancy_user2_tbl1';
+       relname       
+ --------------------
+  tenancy_user2_tbl1
+ (1 row)
+ 
+ select relname from pg_class where relname = 'tenancy_user2_tbl1_idx';
+         relname         
+ ------------------------
+  tenancy_user2_tbl1_idx
+ (1 row)
+ 
+ select relname from pg_class where relname = 'tenancy_user2_view1';
+        relname       
+ ---------------------
+  tenancy_user2_view1
+ (1 row)
+ 
+ select relname from pg_class where relname = 'tenancy_user2_matview1';
+         relname         
+ ------------------------
+  tenancy_user2_matview1
+ (1 row)
+ 
+ select attname from pg_attribute where attname = 'tenancy_user2_tbl1_column1';
+           attname           
+ ----------------------------
+  tenancy_user2_tbl1_column1
+  tenancy_user2_tbl1_column1
+  tenancy_user2_tbl1_column1
+ (3 rows)
+ 
+ select adnum, adsrc from pg_attrdef where adrelid in (select oid from pg_class where relname = 'tenancy_user2_tbl1');
+  adnum |                                 adsrc                                  
+ -------+------------------------------------------------------------------------
+      1 | nextval('tenancy_user2_tbl1_tenancy_user2_tbl1_column1_seq'::regclass)
+      2 | 'FUJITSU'::bpchar
+ (2 rows)
+ 
+ select indnatts, indkey from pg_index where indrelid in (select oid from pg_class where relname = 'tenancy_user2_tbl1');
+  indnatts | indkey 
+ ----------+--------
+         1 | 3
+ (1 row)
+ 
+ select nspname, nspowner from pg_namespace where nspname = 'tenancy_user2';
+     nspname    | nspowner 
+ ---------------+----------
+  tenancy_user2 |       10
+ (1 row)
+ 
+ 		
+ -- verify all system views related to the objects created by tenancy_user1
+ select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user2_tbl1';
+  schemaname |      relname       
+ ------------+--------------------
+  public     | tenancy_user2_tbl1
+ (1 row)
+ 
+ select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user2_tbl2' and schemaname = 'tenancy_user2';
+   schemaname   |      relname       
+ ---------------+--------------------
+  tenancy_user2 | tenancy_user2_tbl2
+ (1 row)
+ 
+ RESET ROLE;
+ -- Try to get the objects created by tenancy_user2 from tenancy_user1
+ SET SESSION ROLE tenancy_user1;
+ select * from tenancy_user2_tbl1;
+ ERROR:  permission denied for relation tenancy_user2_tbl1
+ select * from tenancy_user2_view1;
+ ERROR:  permission denied for relation tenancy_user2_view1
+ select * from tenancy_user2_matview1;
+ ERROR:  permission denied for relation tenancy_user2_matview1
+ select * from tenancy_user2.tenancy_user2_tbl2;
+ ERROR:  permission denied for schema tenancy_user2
+ LINE 1: select * from tenancy_user2.tenancy_user2_tbl2;
+                       ^
+ -- verify all system catalogs related to the objects created by tenancy_user2
+ select relname from pg_class where relname = 'tenancy_user2_tbl1';
+  relname 
+ ---------
+ (0 rows)
+ 
+ select relname from pg_class where relname = 'tenancy_user2_tbl1_idx';
+  relname 
+ ---------
+ (0 rows)
+ 
+ select relname from pg_class where relname = 'tenancy_user2_view1';
+  relname 
+ ---------
+ (0 rows)
+ 
+ select relname from pg_class where relname = 'tenancy_user2_matview1';
+  relname 
+ ---------
+ (0 rows)
+ 
+ select attname from pg_attribute where attname = 'tenancy_user2_tbl1_column1';
+  attname 
+ ---------
+ (0 rows)
+ 
+ select adnum, adsrc from pg_attrdef where adrelid in (select oid from pg_class where relname = 'tenancy_user2_tbl1');
+  adnum | adsrc 
+ -------+-------
+ (0 rows)
+ 
+ select indnatts, indkey from pg_index where indrelid in (select oid from pg_class where relname = 'tenancy_user2_tbl1');
+  indnatts | indkey 
+ ----------+--------
+ (0 rows)
+ 
+ select nspname, nspowner from pg_namespace where nspname = 'tenancy_user2';
+  nspname | nspowner 
+ ---------+----------
+ (0 rows)
+ 
+ 		
+ -- verify all system views related to the objects created by tenancy_user1
+ select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user2_tbl1';
+  schemaname | relname 
+ ------------+---------
+ (0 rows)
+ 
+ select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user2_tbl2' and schemaname = 'tenancy_user2';
+  schemaname | relname 
+ ------------+---------
+ (0 rows)
+ 
+ RESET ROLE;
+ -- Try to get the objects created by tenancy_user1 from tenancy_user2
+ SET SESSION ROLE tenancy_user2;
+ select * from tenancy_user1_tbl1;
+ ERROR:  permission denied for relation tenancy_user1_tbl1
+ select * from tenancy_user1_view1;
+ ERROR:  permission denied for relation tenancy_user1_view1
+ select * from tenancy_user1_matview1;
+ ERROR:  permission denied for relation tenancy_user1_matview1
+ select * from tenancy_user1.tenancy_user1_tbl2;
+ ERROR:  permission denied for schema tenancy_user1
+ LINE 1: select * from tenancy_user1.tenancy_user1_tbl2;
+                       ^
+ -- verify all system catalogs related to the objects created by tenancy_user1
+ select relname from pg_class where relname = 'tenancy_user1_tbl1';
+  relname 
+ ---------
+ (0 rows)
+ 
+ select relname from pg_class where relname = 'tenancy_user1_tbl1_idx';
+  relname 
+ ---------
+ (0 rows)
+ 
+ select relname from pg_class where relname = 'tenancy_user1_view1';
+  relname 
+ ---------
+ (0 rows)
+ 
+ select relname from pg_class where relname = 'tenancy_user1_matview1';
+  relname 
+ ---------
+ (0 rows)
+ 
+ select attname from pg_attribute where attname = 'tenancy_user1_tbl1_column1';
+  attname 
+ ---------
+ (0 rows)
+ 
+ select adnum, adsrc from pg_attrdef where adrelid in (select oid from pg_class where relname = 'tenancy_user1_tbl1');
+  adnum | adsrc 
+ -------+-------
+ (0 rows)
+ 
+ select indnatts, indkey from pg_index where indrelid in (select oid from pg_class where relname = 'tenancy_user1_tbl1');
+  indnatts | indkey 
+ ----------+--------
+ (0 rows)
+ 
+ select nspname, nspowner from pg_namespace where nspname = 'tenancy_user1';
+  nspname | nspowner 
+ ---------+----------
+ (0 rows)
+ 
+ 		
+ -- verify all system views related to the objects created by tenancy_user1
+ select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user1_tbl1';
+  schemaname | relname 
+ ------------+---------
+ (0 rows)
+ 
+ select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user1_tbl2' and schemaname = 'tenancy_user1';
+  schemaname | relname 
+ ------------+---------
+ (0 rows)
+ 
+ RESET ROLE;
+ -- Delete the roles and it's associated objects.
+ SET SESSION ROLE tenancy_user1;
+ DROP TABLE tenancy_user1_tbl1 cascade;
+ NOTICE:  drop cascades to 2 other objects
+ DETAIL:  drop cascades to view tenancy_user1_view1
+ drop cascades to materialized view tenancy_user1_matview1
+ DROP TABLE tenancy_user1_tbl2 cascade;
+ RESET ROLE;
+ SET SESSION ROLE tenancy_user2;
+ DROP TABLE tenancy_user2_tbl1 cascade;
+ NOTICE:  drop cascades to 2 other objects
+ DETAIL:  drop cascades to view tenancy_user2_view1
+ drop cascades to materialized view tenancy_user2_matview1
+ DROP TABLE tenancy_user2_tbl2 cascade;
+ RESET ROLE;
+ alter database regression with catalog security = false;
+ drop schema tenancy_user1;
+ drop schema tenancy_user2;
+ drop role tenancy_user1;
+ drop role tenancy_user2;
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
***************
*** 60,66 **** test: create_index create_view
  # ----------
  # Another group of parallel tests
  # ----------
! test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes
  
  # ----------
  # sanity_check does a vacuum, affecting the sort order of SELECT *
--- 60,66 ----
  # ----------
  # Another group of parallel tests
  # ----------
! test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes multitenancy
  
  # ----------
  # sanity_check does a vacuum, affecting the sort order of SELECT *
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
***************
*** 158,161 **** test: largeobject
--- 158,162 ----
  test: with
  test: xml
  test: event_trigger
+ test: multitenancy
  test: stats
*** /dev/null
--- b/src/test/regress/sql/multitenancy.sql
***************
*** 0 ****
--- 1,169 ----
+ -- Create roles that are used by the following tests
+ create role tenancy_user1 login createdb;
+ create role tenancy_user2 login createdb;
+ 
+ create schema tenancy_user1;
+ grant all on schema tenancy_user1 to tenancy_user1;
+ 
+ create schema tenancy_user2;
+ grant all on schema tenancy_user2 to tenancy_user2;
+ 
+ alter database regression with catalog security = true;
+ 
+ -- create objects realted to tenacy_user1
+ SET SESSION ROLE tenancy_user1;
+ 
+ create table public.tenancy_user1_tbl1 (tenancy_user1_tbl1_column1 serial,
+ 								tenancy_user1_tbl1_column2 char(10) default 'FUJITSU',
+ 								tenancy_user1_tbl1_column3 int);
+ create index tenancy_user1_tbl1_idx on tenancy_user1_tbl1 (tenancy_user1_tbl1_column3);
+ 
+ insert into tenancy_user1_tbl1(tenancy_user1_tbl1_column3) values(1);
+ 
+ create table tenancy_user1.tenancy_user1_tbl2 (tenancy_user1_tbl2_column1 serial,
+ 								tenancy_user1_tbl2_column2 char(10) default 'FUJITSU',
+ 								tenancy_user1_tbl2_column3 int);
+ create index tenancy_user1_tbl2_idx on tenancy_user1.tenancy_user1_tbl2 (tenancy_user1_tbl2_column3);
+ 
+ 
+ create view public.tenancy_user1_view1 as select * from tenancy_user1_tbl1;
+ create materialized view public.tenancy_user1_matview1 as select * from tenancy_user1_tbl1;
+ 
+ select * from tenancy_user1_tbl1;
+ select * from tenancy_user1_view1;
+ select * from tenancy_user1_matview1;
+ 								
+ 
+ -- verify all system catalogs related to the objects created by tenancy_user1
+ select relname from pg_class where relname = 'tenancy_user1_tbl1';
+ select relname from pg_class where relname = 'tenancy_user1_tbl1_idx';
+ select relname from pg_class where relname = 'tenancy_user1_view1';
+ select relname from pg_class where relname = 'tenancy_user1_matview1';
+ 
+ select attname from pg_attribute where attname = 'tenancy_user1_tbl1_column1';
+ select adnum, adsrc from pg_attrdef where adrelid in (select oid from pg_class where relname = 'tenancy_user1_tbl1');
+ select indnatts, indkey from pg_index where indrelid in (select oid from pg_class where relname = 'tenancy_user1_tbl1');
+ select nspname, nspowner from pg_namespace where nspname = 'tenancy_user1';
+ 		
+ -- verify all system views related to the objects created by tenancy_user1
+ select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user1_tbl1';
+ select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user1_tbl2' and schemaname = 'tenancy_user1';
+ 
+ 		
+ RESET ROLE;
+ 
+ 
+ -- create objects realted to tenacy_user2
+ SET SESSION ROLE tenancy_user2;
+ 
+ create table public.tenancy_user2_tbl1 (tenancy_user2_tbl1_column1 serial,
+ 								tenancy_user2_tbl1_column2 char(10) default 'FUJITSU',
+ 								tenancy_user2_tbl1_column3 int);
+ create index tenancy_user2_tbl1_idx on tenancy_user2_tbl1 (tenancy_user2_tbl1_column3);
+ 
+ insert into tenancy_user2_tbl1(tenancy_user2_tbl1_column3) values(1);
+ 
+ create table tenancy_user2.tenancy_user2_tbl2 (tenancy_user2_tbl2_column1 serial,
+ 								tenancy_user2_tbl2_column2 char(10) default 'FUJITSU',
+ 								tenancy_user2_tbl2_column3 int);
+ create index tenancy_user2_tbl2_idx on tenancy_user2.tenancy_user2_tbl2 (tenancy_user2_tbl2_column3);
+ 
+ create view public.tenancy_user2_view1 as select * from tenancy_user2_tbl1;
+ create materialized view public.tenancy_user2_matview1 as select * from tenancy_user2_tbl1;
+ 
+ select * from tenancy_user2_tbl1;
+ select * from tenancy_user2_view1;
+ select * from tenancy_user2_matview1;
+ 
+ -- verify all system catalogs related to the objects created by tenancy_user2
+ select relname from pg_class where relname = 'tenancy_user2_tbl1';
+ select relname from pg_class where relname = 'tenancy_user2_tbl1_idx';
+ select relname from pg_class where relname = 'tenancy_user2_view1';
+ select relname from pg_class where relname = 'tenancy_user2_matview1';
+ 
+ select attname from pg_attribute where attname = 'tenancy_user2_tbl1_column1';
+ select adnum, adsrc from pg_attrdef where adrelid in (select oid from pg_class where relname = 'tenancy_user2_tbl1');
+ select indnatts, indkey from pg_index where indrelid in (select oid from pg_class where relname = 'tenancy_user2_tbl1');
+ select nspname, nspowner from pg_namespace where nspname = 'tenancy_user2';
+ 		
+ -- verify all system views related to the objects created by tenancy_user1
+ select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user2_tbl1';
+ select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user2_tbl2' and schemaname = 'tenancy_user2';
+ 
+ RESET ROLE;
+ 
+ -- Try to get the objects created by tenancy_user2 from tenancy_user1
+ SET SESSION ROLE tenancy_user1;
+ 
+ select * from tenancy_user2_tbl1;
+ select * from tenancy_user2_view1;
+ select * from tenancy_user2_matview1;
+ 
+ select * from tenancy_user2.tenancy_user2_tbl2;
+ 
+ -- verify all system catalogs related to the objects created by tenancy_user2
+ select relname from pg_class where relname = 'tenancy_user2_tbl1';
+ select relname from pg_class where relname = 'tenancy_user2_tbl1_idx';
+ select relname from pg_class where relname = 'tenancy_user2_view1';
+ select relname from pg_class where relname = 'tenancy_user2_matview1';
+ 
+ select attname from pg_attribute where attname = 'tenancy_user2_tbl1_column1';
+ select adnum, adsrc from pg_attrdef where adrelid in (select oid from pg_class where relname = 'tenancy_user2_tbl1');
+ select indnatts, indkey from pg_index where indrelid in (select oid from pg_class where relname = 'tenancy_user2_tbl1');
+ select nspname, nspowner from pg_namespace where nspname = 'tenancy_user2';
+ 		
+ -- verify all system views related to the objects created by tenancy_user1
+ select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user2_tbl1';
+ select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user2_tbl2' and schemaname = 'tenancy_user2';
+ 
+ RESET ROLE;
+ 
+ 
+ -- Try to get the objects created by tenancy_user1 from tenancy_user2
+ SET SESSION ROLE tenancy_user2;
+ 
+ select * from tenancy_user1_tbl1;
+ select * from tenancy_user1_view1;
+ select * from tenancy_user1_matview1;
+ 
+ select * from tenancy_user1.tenancy_user1_tbl2;
+ 
+ -- verify all system catalogs related to the objects created by tenancy_user1
+ select relname from pg_class where relname = 'tenancy_user1_tbl1';
+ select relname from pg_class where relname = 'tenancy_user1_tbl1_idx';
+ select relname from pg_class where relname = 'tenancy_user1_view1';
+ select relname from pg_class where relname = 'tenancy_user1_matview1';
+ 
+ select attname from pg_attribute where attname = 'tenancy_user1_tbl1_column1';
+ select adnum, adsrc from pg_attrdef where adrelid in (select oid from pg_class where relname = 'tenancy_user1_tbl1');
+ select indnatts, indkey from pg_index where indrelid in (select oid from pg_class where relname = 'tenancy_user1_tbl1');
+ select nspname, nspowner from pg_namespace where nspname = 'tenancy_user1';
+ 		
+ -- verify all system views related to the objects created by tenancy_user1
+ select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user1_tbl1';
+ select schemaname, relname from pg_stat_all_tables where relname = 'tenancy_user1_tbl2' and schemaname = 'tenancy_user1';
+ 
+ RESET ROLE;
+ 
+ -- Delete the roles and it's associated objects.
+ SET SESSION ROLE tenancy_user1;
+ 
+ DROP TABLE tenancy_user1_tbl1 cascade;
+ DROP TABLE tenancy_user1_tbl2 cascade;
+ 
+ RESET ROLE;
+ 
+ SET SESSION ROLE tenancy_user2;
+ 
+ DROP TABLE tenancy_user2_tbl1 cascade;
+ DROP TABLE tenancy_user2_tbl2 cascade;
+ 
+ RESET ROLE;
+ 
+ alter database regression with catalog security = false;
+ 
+ drop schema tenancy_user1;
+ drop schema tenancy_user2;
+ 
+ drop role tenancy_user1;
+ drop role tenancy_user2;
#18Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Haribabu Kommi (#17)
1 attachment(s)
Re: Multi-tenancy with RLS

On Wed, Oct 21, 2015 at 2:42 PM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

Pending items:
1. Need to add some more tests to verify all database catalog tables.
2. Documentation changes for database catalog tenancy.

Here I attached the updated database-catalog-security with more tests
including system views,
information schema views and documentation.

Known issues:
2. If user (U2) executes a query on an object (tbl2) which the user
(U2) don't have
permissions, as he cannot able to see that object from catalog views/tables,
but the query returns an error message as "permission denied", but in case
if multi-tenancy is enabled, the error message should be "relation
doesn't exist".

To handle the above problem, we can add a check to verify whether the
corresponding
catalog relation has the row level security is enabled or not? in all
*_aclmask or similar
functions. Based on the ACL result, if the row security is enabled,
through an error as
"object does not exist", instead of permission denied by the
aclcheck_error function.
This will increase the extra processing time for queries irrespective
of whether the
multi-tenancy is enabled or not?

comments?

Regards,
Hari Babu
Fujitsu Australia

Attachments:

4_database_catalog_tenancy_v2.patchapplication/octet-stream; name=4_database_catalog_tenancy_v2.patchDownload
*** a/doc/src/sgml/ref/alter_database.sgml
--- b/doc/src/sgml/ref/alter_database.sgml
***************
*** 28,33 **** ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> [ [ WITH ] <rep
--- 28,34 ----
      ALLOW_CONNECTIONS <replaceable class="PARAMETER">allowconn</replaceable>
      CONNECTION LIMIT <replaceable class="PARAMETER">connlimit</replaceable>
      IS_TEMPLATE <replaceable class="PARAMETER">istemplate</replaceable>
+     CATALOG SECURITY <replaceable class="PARAMETER">catalog_security</replaceable>
  
  ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> RENAME TO <replaceable>new_name</replaceable>
  
***************
*** 139,144 **** ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> RESET ALL
--- 140,157 ----
          </para>
         </listitem>
        </varlistentry>
+       
+      <varlistentry>
+        <term><replaceable class="parameter">catalog_security</replaceable></term>
+        <listitem>
+         <para>
+          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.
+         </para>
+        </listitem>
+      </varlistentry>
  
     <varlistentry>
      <term><replaceable>new_name</replaceable></term>
***************
*** 203,208 **** ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> RESET ALL
--- 216,227 ----
     Role-specific settings override database-specific
     ones if there is a conflict.
    </para>
+   
+   <para>
+    The catalog_security option is not supported at <xref linkend="sql-createdatabase">
+    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.
+   </para>
   </refsect1>
  
   <refsect1>
*** 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 <ctype.h>
  
  #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 <ctype.h>
  
  #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;
#19Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Haribabu Kommi (#18)
1 attachment(s)
Re: Multi-tenancy with RLS

Rebased patch is attached as it is having an OID conflict with the
latest set of changes
in the master branch.

Regards,
Hari Babu
Fujitsu Australia

Attachments:

4_database_catalog_tenancy_v3.patchapplication/octet-stream; name=4_database_catalog_tenancy_v3.patchDownload
*** a/doc/src/sgml/ref/alter_database.sgml
--- b/doc/src/sgml/ref/alter_database.sgml
***************
*** 28,33 **** ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> [ [ WITH ] <rep
--- 28,34 ----
      ALLOW_CONNECTIONS <replaceable class="PARAMETER">allowconn</replaceable>
      CONNECTION LIMIT <replaceable class="PARAMETER">connlimit</replaceable>
      IS_TEMPLATE <replaceable class="PARAMETER">istemplate</replaceable>
+     CATALOG SECURITY <replaceable class="PARAMETER">catalog_security</replaceable>
  
  ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> RENAME TO <replaceable>new_name</replaceable>
  
***************
*** 139,144 **** ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> RESET ALL
--- 140,157 ----
          </para>
         </listitem>
        </varlistentry>
+       
+      <varlistentry>
+        <term><replaceable class="parameter">catalog_security</replaceable></term>
+        <listitem>
+         <para>
+          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.
+         </para>
+        </listitem>
+      </varlistentry>
  
     <varlistentry>
      <term><replaceable>new_name</replaceable></term>
***************
*** 203,208 **** ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> RESET ALL
--- 216,227 ----
     Role-specific settings override database-specific
     ones if there is a conflict.
    </para>
+   
+   <para>
+    The catalog_security option is not supported at <xref linkend="sql-createdatabase">
+    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.
+   </para>
   </refsect1>
  
   <refsect1>
*** 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();
+ }
+ 
  /*
   * RemoveRoleFromObjectPolicy -
   *	 remove a role from a policy by its OID.  If the role is not a member of
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 8876,8881 **** createdb_opt_name:
--- 8876,8882 ----
  			| 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 <ctype.h>
  
  #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 <ctype.h>
  
  #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
***************
*** 3644,3649 **** DESCR("current user privilege on role by role name");
--- 3644,3667 ----
  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 = 3321 (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 = 3322 (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 = 3323 (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 = 3324 (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 = 3325 (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 = 3326 (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
***************
*** 35,38 **** extern ObjectAddress rename_policy(RenameStmt *stmt);
--- 35,41 ----
  
  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
***************
*** 160,163 **** test: largeobject
--- 160,164 ----
  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;
#20Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Haribabu Kommi (#19)
3 attachment(s)
Re: Multi-tenancy with RLS

On Thu, Dec 17, 2015 at 12:46 PM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

Rebased patch is attached as it is having an OID conflict with the
latest set of changes
in the master branch.

Here I attached new series of patches with a slightly different approach.
Instead of creating the policies on the system catalog tables whenever
the catalog security command is executed, just enable row level security
on the system catalog tables. During the relation build, in
RelationBuildRowSecurity function, if it is a system relation, frame the
policy using the policy query which we earlier used to create by parsing it.

With the above approach, in case of any problems in the policy, to use
the corrected policy, user just needs to replace the binaries. whereas in
earlier approach, either pg_upgrade or disabling and enabling of catalog
security is required.

Currently it is changed only for shared system catalog tables and also the
way of enabling catalog security on shared system catalog tables is through
initdb only. This also can be changed later. I will do similar changes for
remaining catalog tables.

Any comments on the approach?

Regards,
Hari Babu
Fujitsu Australia

Attachments:

3_shared_catalog_tenancy_v2.patchapplication/octet-stream; name=3_shared_catalog_tenancy_v2.patchDownload
diff --git a/doc/src/sgml/ref/initdb.sgml b/doc/src/sgml/ref/initdb.sgml
index 4e339ec..ac460e9 100644
--- a/doc/src/sgml/ref/initdb.sgml
+++ b/doc/src/sgml/ref/initdb.sgml
@@ -152,6 +152,19 @@ PostgreSQL documentation
      </varlistentry>
 
      <varlistentry>
+      <term><option>-C</option></term>
+      <term><option>--shared-catalog-security</option></term>
+      <listitem>
+       <para>
+        This option enables the shared catalog tables security, by adding
+        row level security policies on all eligible shared catalog tables.
+        With this option, multi-tenancy in PostgreSQL is supported at 
+        database level.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><option>-D <replaceable class="parameter">directory</replaceable></option></term>
       <term><option>--pgdata=<replaceable class="parameter">directory</replaceable></option></term>
       <listitem>
diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c
index 6b9c306..69e66f1 100644
--- a/src/backend/commands/policy.c
+++ b/src/backend/commands/policy.c
@@ -23,7 +23,16 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_authid.h"
+#include "catalog/pg_auth_members.h"
+#include "catalog/pg_database.h"
+#include "catalog/pg_db_role_setting.h"
+#include "catalog/pg_pltemplate.h"
 #include "catalog/pg_policy.h"
+#include "catalog/pg_replication_origin.h"
+#include "catalog/pg_shdepend.h"
+#include "catalog/pg_shdescription.h"
+#include "catalog/pg_shseclabel.h"
+#include "catalog/pg_tablespace.h"
 #include "catalog/pg_type.h"
 #include "commands/policy.h"
 #include "miscadmin.h"
@@ -36,6 +45,7 @@
 #include "rewrite/rewriteManip.h"
 #include "rewrite/rowsecurity.h"
 #include "storage/lock.h"
+#include "tcop/tcopprot.h"
 #include "utils/acl.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
@@ -46,6 +56,72 @@
 #include "utils/rel.h"
 #include "utils/syscache.h"
 
+
+struct catalog_table_policy_qual
+{
+	Oid	  tableid;
+	char *policy_query;
+};
+
+static struct catalog_table_policy_qual catalog_policy_qual[] = {
+	{
+		AuthMemRelationId,
+		"create policy pg_auth_members_read_own_data on pg_auth_members for select using"
+		" (pg_has_role(roleid, 'any') AND pg_has_role(member, 'any'))"
+	},
+	{
+		AuthIdRelationId,
+		"create policy pg_authid_read_own_data on pg_authid for select using"
+		" (pg_has_role(oid, 'any'))"
+	},
+	{
+		DatabaseRelationId,
+		"create policy pg_database_read_own_data on pg_database for select using"
+		" ((oid < 16384) OR has_database_privilege(oid,'any'))"
+	},
+	{
+		DbRoleSettingRelationId,
+		"create policy pg_db_role_setting_read_own_data on pg_db_role_setting for select using"
+		" (pg_has_role(setrole, 'any') AND has_database_privilege(setdatabase,'any'))"
+	},
+	{
+		PLTemplateRelationId,
+		NULL
+	},
+	{
+		ReplicationOriginRelationId,
+		NULL
+	},
+	{
+		SharedDependRelationId,
+		"create policy pg_shdepend_read_own_data on pg_shdepend for select using"
+		" ((classid = (select oid from pg_class where relname = 'pg_database') AND has_database_privilege(objid, 'any'))"
+		" OR (classid = (select oid from pg_class where relname = 'pg_authid') AND pg_has_role(objid, 'any'))"
+		" OR (classid = (select oid from pg_class where relname = 'pg_tablespace') AND has_tablespace_privilege(objid, 'any')))"
+	},
+	{
+		SharedDescriptionRelationId,
+		"create policy pg_shdescription_read_own_data on pg_shdescription for select using"
+		" ((classoid = (select oid from pg_class where relname = 'pg_database') AND has_database_privilege(objoid, 'any'))"
+		" OR (classoid = (select oid from pg_class where relname = 'pg_authid') AND pg_has_role(objoid, 'any'))"
+		" OR (classoid = (select oid from pg_class where relname = 'pg_tablespace') AND has_tablespace_privilege(objoid, 'any')))"
+	},
+	{
+		SharedSecLabelRelationId,
+		"create policy pg_shseclabel_read_own_data on pg_shseclabel for select using"
+		" ((classoid = (select oid from pg_class where relname = 'pg_database') AND has_database_privilege(objoid, 'any'))"
+		" OR (classoid = (select oid from pg_class where relname = 'pg_authid') AND pg_has_role(objoid, 'any'))"
+		" OR (classoid = (select oid from pg_class where relname = 'pg_tablespace') AND has_tablespace_privilege(objoid, 'any')))"
+	},
+	{
+		TableSpaceRelationId,
+		"create policy pg_tablespace_read_own_data on pg_tablespace for select using"
+		" ((oid < 16384) OR has_tablespace_privilege(oid, 'any'))"
+	}
+};
+
+#define catalog_table_policy_size ((int) lengthof(catalog_policy_qual))
+
 static void RangeVarCallbackForPolicy(const RangeVar *rv,
 						  Oid relid, Oid oldrelid, void *arg);
 static char parse_policy_command(const char *cmd_name);
@@ -211,114 +287,181 @@ RelationBuildRowSecurity(Relation relation)
 	 */
 	PG_TRY();
 	{
-		Relation	catalog;
-		ScanKeyData skey;
-		SysScanDesc sscan;
-		HeapTuple	tuple;
-
 		rsdesc = MemoryContextAllocZero(rscxt, sizeof(RowSecurityDesc));
 		rsdesc->rscxt = rscxt;
 
-		catalog = heap_open(PolicyRelationId, AccessShareLock);
-
-		ScanKeyInit(&skey,
-					Anum_pg_policy_polrelid,
-					BTEqualStrategyNumber, F_OIDEQ,
-					ObjectIdGetDatum(RelationGetRelid(relation)));
-
-		sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
-								   NULL, 1, &skey);
-
-		/*
-		 * Loop through the row level security policies for this relation, if
-		 * any.
-		 */
-		while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
+		if (IsSystemRelation(relation))
 		{
-			Datum		value_datum;
-			char		cmd_value;
-			Datum		roles_datum;
-			char	   *qual_value;
-			Expr	   *qual_expr;
-			char	   *with_check_value;
-			Expr	   *with_check_qual;
-			char	   *policy_name_value;
-			bool		isnull;
 			RowSecurityPolicy *policy;
+			Datum	   *role_oids;
+			int			nitems = 0;
+			ArrayType  *role_ids;
+			Node	   *qual_expr;
+			List	   *raw_parsetree_list;
+			ListCell   *list_item;
+			ParseState *qual_pstate;
+			RangeTblEntry *rte;
+			CreatePolicyStmt *stmt;
+			int id;
 
-			/*
-			 * Note: all the pass-by-reference data we collect here is either
-			 * still stored in the tuple, or constructed in the caller's
-			 * short-lived memory context.  We must copy it into rscxt
-			 * explicitly below.
-			 */
-
-			/* Get policy command */
-			value_datum = heap_getattr(tuple, Anum_pg_policy_polcmd,
-									   RelationGetDescr(catalog), &isnull);
-			Assert(!isnull);
-			cmd_value = DatumGetChar(value_datum);
-
-			/* Get policy name */
-			value_datum = heap_getattr(tuple, Anum_pg_policy_polname,
-									   RelationGetDescr(catalog), &isnull);
-			Assert(!isnull);
-			policy_name_value = NameStr(*(DatumGetName(value_datum)));
-
-			/* Get policy roles */
-			roles_datum = heap_getattr(tuple, Anum_pg_policy_polroles,
-									   RelationGetDescr(catalog), &isnull);
-			/* shouldn't be null, but initdb doesn't mark it so, so check */
-			if (isnull)
-				elog(ERROR, "unexpected null value in pg_policy.polroles");
-
-			/* Get policy qual */
-			value_datum = heap_getattr(tuple, Anum_pg_policy_polqual,
-									   RelationGetDescr(catalog), &isnull);
-			if (!isnull)
+			for (id = 0; id < catalog_table_policy_size; id++)
 			{
-				qual_value = TextDatumGetCString(value_datum);
-				qual_expr = (Expr *) stringToNode(qual_value);
+				if (catalog_policy_qual[id].tableid == RelationGetRelid(relation))
+					break;
 			}
-			else
-				qual_expr = NULL;
 
-			/* Get WITH CHECK qual */
-			value_datum = heap_getattr(tuple, Anum_pg_policy_polwithcheck,
-									   RelationGetDescr(catalog), &isnull);
-			if (!isnull)
+			Assert(catalog_qual);
+
+			role_oids = policy_role_list_to_array(NULL, &nitems);
+			role_ids = construct_array(role_oids, nitems, OIDOID,
+									   sizeof(Oid), true, 'i');
+
+			raw_parsetree_list = pg_parse_query(catalog_policy_qual[id].policy_query);
+			Assert(list_length(raw_parsetree_list) == 1);
+
+			foreach(list_item, raw_parsetree_list)
 			{
-				with_check_value = TextDatumGetCString(value_datum);
-				with_check_qual = (Expr *) stringToNode(with_check_value);
+				Node	   *parsetree;
+				parsetree = (Node *) lfirst(list_item);
+				stmt = (CreatePolicyStmt *)parsetree;
 			}
-			else
-				with_check_qual = NULL;
+
+			qual_pstate = make_parsestate(NULL);
+			rte = addRangeTableEntryForRelation(qual_pstate, relation,
+												NULL, false, false);
+
+			addRTEtoQuery(qual_pstate, rte, false, true, true);
+			qual_expr = transformWhereClause(qual_pstate,
+										stmt->qual,
+										EXPR_KIND_POLICY,
+										"POLICY");
+
+			/* Fix up collation information */
+			assign_expr_collations(qual_pstate, qual_expr);
 
 			/* Now copy everything into the cache context */
 			MemoryContextSwitchTo(rscxt);
 
 			policy = palloc0(sizeof(RowSecurityPolicy));
-			policy->policy_name = pstrdup(policy_name_value);
-			policy->polcmd = cmd_value;
-			policy->roles = DatumGetArrayTypePCopy(roles_datum);
+			policy->policy_name = pstrdup(stmt->policy_name);
+			policy->polcmd = ACL_SELECT_CHR;
+			policy->roles = DatumGetArrayTypePCopy(role_ids);
 			policy->qual = copyObject(qual_expr);
-			policy->with_check_qual = copyObject(with_check_qual);
-			policy->hassublinks = checkExprHasSubLink((Node *) qual_expr) ||
-				checkExprHasSubLink((Node *) with_check_qual);
+			policy->with_check_qual = NULL;
+			policy->hassublinks = checkExprHasSubLink((Node *) qual_expr);
 
 			rsdesc->policies = lcons(policy, rsdesc->policies);
 
 			MemoryContextSwitchTo(oldcxt);
-
-			/* clean up some (not all) of the junk ... */
-			if (qual_expr != NULL)
-				pfree(qual_expr);
-			if (with_check_qual != NULL)
-				pfree(with_check_qual);
 		}
+		else
+		{
+			Relation	catalog;
+			ScanKeyData skey;
+			SysScanDesc sscan;
+			HeapTuple	tuple;
+
+			catalog = heap_open(PolicyRelationId, AccessShareLock);
 
-		systable_endscan(sscan);
-		heap_close(catalog, AccessShareLock);
+			ScanKeyInit(&skey,
+						Anum_pg_policy_polrelid,
+						BTEqualStrategyNumber, F_OIDEQ,
+						ObjectIdGetDatum(RelationGetRelid(relation)));
+
+			sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
+									   NULL, 1, &skey);
+
+			/*
+			 * Loop through the row level security policies for this relation, if
+			 * any.
+			 */
+			while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
+			{
+				Datum		value_datum;
+				char		cmd_value;
+				Datum		roles_datum;
+				char	   *qual_value;
+				Expr	   *qual_expr;
+				char	   *with_check_value;
+				Expr	   *with_check_qual;
+				char	   *policy_name_value;
+				bool		isnull;
+				RowSecurityPolicy *policy;
+
+				/*
+				 * Note: all the pass-by-reference data we collect here is either
+				 * still stored in the tuple, or constructed in the caller's
+				 * short-lived memory context.  We must copy it into rscxt
+				 * explicitly below.
+				 */
+
+				/* Get policy command */
+				value_datum = heap_getattr(tuple, Anum_pg_policy_polcmd,
+										   RelationGetDescr(catalog), &isnull);
+				Assert(!isnull);
+				cmd_value = DatumGetChar(value_datum);
+
+				/* Get policy name */
+				value_datum = heap_getattr(tuple, Anum_pg_policy_polname,
+										   RelationGetDescr(catalog), &isnull);
+				Assert(!isnull);
+				policy_name_value = NameStr(*(DatumGetName(value_datum)));
+
+				/* Get policy roles */
+				roles_datum = heap_getattr(tuple, Anum_pg_policy_polroles,
+										   RelationGetDescr(catalog), &isnull);
+				/* shouldn't be null, but initdb doesn't mark it so, so check */
+				if (isnull)
+					elog(ERROR, "unexpected null value in pg_policy.polroles");
+
+				/* Get policy qual */
+				value_datum = heap_getattr(tuple, Anum_pg_policy_polqual,
+										   RelationGetDescr(catalog), &isnull);
+				if (!isnull)
+				{
+					qual_value = TextDatumGetCString(value_datum);
+					qual_expr = (Expr *) stringToNode(qual_value);
+				}
+				else
+					qual_expr = NULL;
+
+				/* Get WITH CHECK qual */
+				value_datum = heap_getattr(tuple, Anum_pg_policy_polwithcheck,
+										   RelationGetDescr(catalog), &isnull);
+				if (!isnull)
+				{
+					with_check_value = TextDatumGetCString(value_datum);
+					with_check_qual = (Expr *) stringToNode(with_check_value);
+				}
+				else
+					with_check_qual = NULL;
+
+				/* Now copy everything into the cache context */
+				MemoryContextSwitchTo(rscxt);
+
+				policy = palloc0(sizeof(RowSecurityPolicy));
+				policy->policy_name = pstrdup(policy_name_value);
+				policy->polcmd = cmd_value;
+				policy->roles = DatumGetArrayTypePCopy(roles_datum);
+				policy->qual = copyObject(qual_expr);
+				policy->with_check_qual = copyObject(with_check_qual);
+				policy->hassublinks = checkExprHasSubLink((Node *) qual_expr) ||
+					checkExprHasSubLink((Node *) with_check_qual);
+
+				rsdesc->policies = lcons(policy, rsdesc->policies);
+
+				MemoryContextSwitchTo(oldcxt);
+
+				/* clean up some (not all) of the junk ... */
+				if (qual_expr != NULL)
+					pfree(qual_expr);
+				if (with_check_qual != NULL)
+					pfree(with_check_qual);
+			}
+
+			systable_endscan(sscan);
+			heap_close(catalog, AccessShareLock);
+		}
 	}
 	PG_CATCH();
 	{
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index a217dbc..50e27ec 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -3442,6 +3442,15 @@ ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode)
 	{
 		AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
 
+		/*
+		 * ALTER table on sytem catalog tables is possible only when user specifies
+		 * CATALOG SECURITY on system catalog tables. To avoid an error in the 
+		 * AlterTableCreateToastTable function for system catalog tables, the system
+		 * catalog tables are ignored for the toast table creation.
+		 */
+		if (!IsUnderPostmaster && IsSharedRelation(tab->relid))
+			continue;
+
 		if (tab->relkind == RELKIND_RELATION ||
 			tab->relkind == RELKIND_MATVIEW)
 			AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
diff --git a/src/backend/utils/misc/rls.c b/src/backend/utils/misc/rls.c
index 9f2ebfe..1b720a3 100644
--- a/src/backend/utils/misc/rls.c
+++ b/src/backend/utils/misc/rls.c
@@ -58,10 +58,6 @@ check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
 	bool		relforcerowsecurity;
 	Oid			user_id = checkAsUser ? checkAsUser : GetUserId();
 
-	/* Nothing to do for built-in relations */
-	if (relid < FirstNormalObjectId)
-		return RLS_NONE;
-
 	tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
 	if (!HeapTupleIsValid(tuple))
 		return RLS_NONE;
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 460342a..2186f16 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -136,6 +136,7 @@ static bool do_sync = true;
 static bool sync_only = false;
 static bool show_setting = false;
 static bool data_checksums = false;
+static bool shared_catalog_security = false;
 static char *xlog_dir = "";
 
 
@@ -190,6 +191,7 @@ static char *authwarning = NULL;
  */
 static const char *boot_options = "-F";
 static const char *backend_options = "--single -F -O -j -c search_path=pg_catalog -c exit_on_error=true";
+static const char *catalog_security_options = "--single -F -O -j -c search_path=pg_catalog -c exit_on_error=true -c allow_system_table_mods=true";
 
 static const char *const subdirs[] = {
 	"global",
@@ -259,6 +261,7 @@ static void setup_dictionary(FILE *cmdfd);
 static void setup_privileges(FILE *cmdfd);
 static void set_info_version(void);
 static void setup_schema(FILE *cmdfd);
+static void setup_shared_catalog_security(FILE *cmdfd);
 static void load_plpgsql(FILE *cmdfd);
 static void vacuum_db(FILE *cmdfd);
 static void make_template0(FILE *cmdfd);
@@ -2082,6 +2085,57 @@ setup_schema(FILE *cmdfd)
 }
 
 /*
+ * setup shared catalog security by defining policies
+ */
+static void
+setup_shared_catalog_security(FILE *cmdfd)
+{
+	const char	  **line;
+	static const char *pg_shared_catalog_security_setup[] = {
+		/* AuthMemRelationId */
+		"alter table pg_auth_members enable row level security;\n",
+
+		/* AuthIdRelationId */
+		"alter table pg_authid enable row level security;\n",
+
+		/* DatabaseRelationId */
+		"alter table pg_database enable row level security;\n",
+
+		/* DbRoleSettingRelationId */
+		"alter table pg_database enable row level security;\n",
+
+		/* PLTemplateRelationId */
+		/*
+		 * Currently there is no policy needed for this table, so
+		 * leave it as it is.
+		 */
+
+		/* ReplicationOriginRelationId */
+		/*
+		 * Currently there is no policy needed for this table, so
+		 * leave it as it is.
+		 */
+		 
+		/* SharedDependRelationId */
+		"alter table pg_shdepend enable row level security;\n",
+
+		/* SharedDescriptionRelationId */
+		"alter table pg_shdescription enable row level security;\n",
+
+		/* SharedSecLabelRelationId */
+		"alter table pg_shseclabel enable row level security;\n",
+
+		/* TableSpaceRelationId */
+		"alter table pg_tablespace enable row level security;\n",
+
+		NULL
+	};
+
+	for (line = pg_shared_catalog_security_setup; *line != NULL; line++)
+		PG_CMD_PUTS(*line);
+}
+
+/*
  * load PL/pgsql server-side language
  */
 static void
@@ -2536,6 +2590,8 @@ usage(const char *progname)
 	printf(_("\nLess commonly used options:\n"));
 	printf(_("  -d, --debug               generate lots of debugging output\n"));
 	printf(_("  -k, --data-checksums      use data page checksums\n"));
+	printf(_("  -C, --shared-catalog-security\n"
+			"						      use shared catalog security\n"));
 	printf(_("  -L DIRECTORY              where to find the input files\n"));
 	printf(_("  -n, --noclean             do not clean up after errors\n"));
 	printf(_("  -N, --nosync              do not wait for changes to be written safely to disk\n"));
@@ -3073,6 +3129,10 @@ initialize_data_directory(void)
 {
 	PG_CMD_DECL;
 	int			i;
+	const char *options = backend_options;
+
+	if (shared_catalog_security)
+		options = catalog_security_options;
 
 	setup_signals();
 
@@ -3121,7 +3181,7 @@ initialize_data_directory(void)
 
 	snprintf(cmd, sizeof(cmd),
 			 "\"%s\" %s template1 >%s",
-			 backend_exec, backend_options,
+			 backend_exec, options,
 			 DEVNULL);
 
 	PG_CMD_OPEN;
@@ -3146,6 +3206,9 @@ initialize_data_directory(void)
 
 	setup_schema(cmdfd);
 
+	if (shared_catalog_security)
+		setup_shared_catalog_security(cmdfd);
+	
 	load_plpgsql(cmdfd);
 
 	vacuum_db(cmdfd);
@@ -3190,6 +3253,7 @@ main(int argc, char *argv[])
 		{"sync-only", no_argument, NULL, 'S'},
 		{"xlogdir", required_argument, NULL, 'X'},
 		{"data-checksums", no_argument, NULL, 'k'},
+		{"shared-catalog-security", no_argument, NULL, 'C' },
 		{NULL, 0, NULL, 0}
 	};
 
@@ -3230,7 +3294,7 @@ main(int argc, char *argv[])
 
 	/* process command-line options */
 
-	while ((c = getopt_long(argc, argv, "dD:E:kL:nNU:WA:sST:X:", long_options, &option_index)) != -1)
+	while ((c = getopt_long(argc, argv, "dD:E:kCL:nNU:WA:sST:X:", long_options, &option_index)) != -1)
 	{
 		switch (c)
 		{
@@ -3282,6 +3346,9 @@ main(int argc, char *argv[])
 			case 'k':
 				data_checksums = true;
 				break;
+			case 'C':
+				shared_catalog_security = true;
+				break;
 			case 'L':
 				share_path = pg_strdup(optarg);
 				break;
1_any_privilege_option_v2.patchapplication/octet-stream; name=1_any_privilege_option_v2.patchDownload
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 8ef9fce..946bd4b 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -15142,7 +15142,11 @@ SET search_path TO <replaceable>schema</> <optional>, <replaceable>schema</>, ..
    <xref linkend="functions-info-access-table"> lists functions that
    allow the user to query object access privileges programmatically.
    See <xref linkend="ddl-priv"> for more information about
-   privileges.
+   privileges. In case if user wants to find out any privilege check
+   on the corresponding object, instead of providing individual/all
+   privileges, user can specify "any" as an privilege option. The
+   Access Prvilege Inquiry Functions returns true if the user have 
+   any privileges on the object.
   </para>
 
    <table id="functions-info-access-table">
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 3ca168b..4685a51 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -2031,6 +2031,11 @@ convert_table_priv_string(text *priv_type_text)
 		{"REFERENCES WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
 		{"TRIGGER", ACL_TRIGGER},
 		{"TRIGGER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
+		{"ANY", ACL_SELECT | ACL_GRANT_OPTION_FOR(ACL_SELECT) |
+				ACL_INSERT | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
+				ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_DELETE | ACL_GRANT_OPTION_FOR(ACL_DELETE) |
+				ACL_TRUNCATE | ACL_GRANT_OPTION_FOR(ACL_TRUNCATE) | ACL_REFERENCES |
+				ACL_GRANT_OPTION_FOR(ACL_REFERENCES) | ACL_TRIGGER | ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
 		{"RULE", 0},			/* ignore old RULE privileges */
 		{"RULE WITH GRANT OPTION", 0},
 		{NULL, 0}
@@ -2243,6 +2248,7 @@ convert_sequence_priv_string(text *priv_type_text)
 		{"USAGE", ACL_USAGE},
 		{"SELECT", ACL_SELECT},
 		{"UPDATE", ACL_UPDATE},
+		{"ANY", ACL_USAGE | ACL_SELECT | ACL_UPDATE},
 		{NULL, 0}
 	};
 
@@ -2858,6 +2864,9 @@ convert_column_priv_string(text *priv_type_text)
 		{"UPDATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_UPDATE)},
 		{"REFERENCES", ACL_REFERENCES},
 		{"REFERENCES WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
+		{"ANY", ACL_SELECT | ACL_GRANT_OPTION_FOR(ACL_SELECT) | ACL_INSERT |
+				ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
+				ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_REFERENCES | ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
 		{NULL, 0}
 	};
 
@@ -3055,6 +3064,9 @@ convert_database_priv_string(text *priv_type_text)
 		{"TEMP WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP)},
 		{"CONNECT", ACL_CONNECT},
 		{"CONNECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
+		{"ANY", ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE) |
+									ACL_CREATE_TEMP | ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CREATE_TEMP |
+									ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CONNECT | ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
 		{NULL, 0}
 	};
 
@@ -3238,6 +3250,7 @@ convert_foreign_data_wrapper_priv_string(text *priv_type_text)
 	static const priv_map foreign_data_wrapper_priv_map[] = {
 		{"USAGE", ACL_USAGE},
 		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+		{"ANY", ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
 		{NULL, 0}
 	};
 
@@ -3438,6 +3451,7 @@ convert_function_priv_string(text *priv_type_text)
 	static const priv_map function_priv_map[] = {
 		{"EXECUTE", ACL_EXECUTE},
 		{"EXECUTE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_EXECUTE)},
+		{"ANY", ACL_EXECUTE | ACL_GRANT_OPTION_FOR(ACL_EXECUTE)},
 		{NULL, 0}
 	};
 
@@ -3629,6 +3643,7 @@ convert_language_priv_string(text *priv_type_text)
 	static const priv_map language_priv_map[] = {
 		{"USAGE", ACL_USAGE},
 		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+		{"ANY", ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
 		{NULL, 0}
 	};
 
@@ -3822,6 +3837,7 @@ convert_schema_priv_string(text *priv_type_text)
 		{"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
 		{"USAGE", ACL_USAGE},
 		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+		{"ANY", ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE) | ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
 		{NULL, 0}
 	};
 
@@ -4004,6 +4020,7 @@ convert_server_priv_string(text *priv_type_text)
 	static const priv_map server_priv_map[] = {
 		{"USAGE", ACL_USAGE},
 		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+		{"ANY", ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
 		{NULL, 0}
 	};
 
@@ -4186,6 +4203,7 @@ convert_tablespace_priv_string(text *priv_type_text)
 	static const priv_map tablespace_priv_map[] = {
 		{"CREATE", ACL_CREATE},
 		{"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
+		{"ANY", ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE)},
 		{NULL, 0}
 	};
 
@@ -4385,6 +4403,7 @@ convert_type_priv_string(text *priv_type_text)
 	static const priv_map type_priv_map[] = {
 		{"USAGE", ACL_USAGE},
 		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+		{"ANY", ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
 		{NULL, 0}
 	};
 
@@ -4566,6 +4585,7 @@ convert_role_priv_string(text *priv_type_text)
 		{"USAGE WITH ADMIN OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
 		{"MEMBER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
 		{"MEMBER WITH ADMIN OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
+		{"ANY", ACL_USAGE | ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE)},
 		{NULL, 0}
 	};
 
2_view_security_definer_v2.patchapplication/octet-stream; name=2_view_security_definer_v2.patchDownload
diff --git a/doc/src/sgml/ref/alter_view.sgml b/doc/src/sgml/ref/alter_view.sgml
index 00f4ecb..d1f3a54 100644
--- a/doc/src/sgml/ref/alter_view.sgml
+++ b/doc/src/sgml/ref/alter_view.sgml
@@ -142,6 +142,16 @@ ALTER VIEW [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RESET
          </para>
         </listitem>
        </varlistentry>
+       <varlistentry>
+        <term><literal>security_definer</literal> (<type>boolean</type>)</term>
+        <listitem>
+         <para>
+          Changes the security-definer property of the view.  The value must
+          be Boolean value, such as <literal>true</literal>
+          or <literal>false</literal>.
+         </para>
+        </listitem>
+       </varlistentry>
       </variablelist>
      </para>
     </listitem>
diff --git a/doc/src/sgml/ref/create_view.sgml b/doc/src/sgml/ref/create_view.sgml
index e37bb00..71c6f79 100644
--- a/doc/src/sgml/ref/create_view.sgml
+++ b/doc/src/sgml/ref/create_view.sgml
@@ -147,6 +147,16 @@ CREATE VIEW <replaceable>name</> AS WITH RECURSIVE <replaceable>name</> (<replac
          </para>
         </listitem>
        </varlistentry>
+       
+       <varlistentry>
+        <term><literal>security_definer</literal> (<type>boolean</type>)</term>
+        <listitem>
+         <para>
+          This should be used if the view is intended to be executed with
+          owner privileges rather than the current user.
+         </para>
+        </listitem>
+       </varlistentry>
       </variablelist>
      </para>
     </listitem>
@@ -276,6 +286,37 @@ CREATE VIEW vista AS SELECT text 'Hello World' AS hello;
     to replace it (this includes being a member of the owning role).
    </para>
 
+  <refsect2 id="SQL-CREATEVIEW-security-definer-views">
+   <title id="SQL-CREATEVIEW-security-definer-views-title">Security definer Views</title>
+
+   <indexterm zone="sql-createview-security-definer-views">
+    <primary>security definer views</primary>
+   </indexterm>
+
+   <para>
+    Security definer views uses the view owner id instead of the current user
+    in the following conditions, otherwise the current user is used to verify
+    the privileges and etc.
+    <itemizedlist>
+     <listitem>
+      <para>
+       The view is used in <command>INSERT</>, <command>UPDATE</>
+       and <command>DELETE</> statements in the same way as on a
+       regular table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       To apply row-level security policies on the underlying base
+       relations of the view, based on the security definer, the 
+       corresponding policies related to the user are applied.
+      </para>
+     </listitem>
+    </itemizedlist>
+   </para>
+  </refsect2>
+ 
   <refsect2 id="SQL-CREATEVIEW-updatable-views">
    <title id="SQL-CREATEVIEW-updatable-views-title">Updatable Views</title>
 
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 931dceb..430ea71 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -89,6 +89,15 @@ static relopt_bool boolRelOpts[] =
 		},
 		false
 	},
+	{
+		{
+			"security_definer",
+			"specifies that the view is to be executed with the privileges of the user that created it.",
+			RELOPT_KIND_VIEW,
+			AccessExclusiveLock
+		},
+		false
+	},
 	/* list terminator */
 	{{NULL}}
 };
@@ -1320,6 +1329,8 @@ view_reloptions(Datum reloptions, bool validate)
 	static const relopt_parse_elt tab[] = {
 		{"security_barrier", RELOPT_TYPE_BOOL,
 		offsetof(ViewOptions, security_barrier)},
+		{ "security_definer", RELOPT_TYPE_BOOL,
+		offsetof(ViewOptions, security_definer) },
 		{"check_option", RELOPT_TYPE_STRING,
 		offsetof(ViewOptions, check_option_offset)}
 	};
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 315d00c..ea5edaf 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -24,6 +24,7 @@
 #include "catalog/pg_type.h"
 #include "commands/trigger.h"
 #include "foreign/fdwapi.h"
+#include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
 #include "parser/analyze.h"
@@ -1616,6 +1617,7 @@ fireRIRrules(Query *parsetree, List *activeRIRs, bool forUpdatePushedDown)
 	int			origResultRelation = parsetree->resultRelation;
 	int			rt_index;
 	ListCell   *lc;
+	bool		security_definer_view = false;
 
 	/*
 	 * don't try to convert this into a foreach loop, because rtable list can
@@ -1777,18 +1779,27 @@ fireRIRrules(Query *parsetree, List *activeRIRs, bool forUpdatePushedDown)
 		++rt_index;
 
 		/* Only normal relations can have RLS policies */
-		if (rte->rtekind != RTE_RELATION ||
-			rte->relkind != RELKIND_RELATION)
+		if (rte->rtekind != RTE_RELATION)
 			continue;
-
+		
 		rel = heap_open(rte->relid, NoLock);
 
+		if (rte->relkind == RELKIND_VIEW && RelationIsSecurityDefinerView(rel))
+			security_definer_view = true;
+
+		if (rte->relkind != RELKIND_RELATION)
+		{
+			heap_close(rel, NoLock);
+			continue;
+		}
+
 		/*
 		 * Fetch any new security quals that must be applied to this RTE.
 		 */
 		get_row_security_policies(parsetree, rte, rt_index,
 								  &securityQuals, &withCheckOptions,
-								  &hasRowSecurity, &hasSubLinks);
+								  &hasRowSecurity, &hasSubLinks,
+								  security_definer_view);
 
 		if (securityQuals != NIL || withCheckOptions != NIL)
 		{
@@ -2834,7 +2845,10 @@ rewriteTargetView(Query *parsetree, Relation view)
 	 * the executor still performs appropriate permissions checks for the
 	 * query caller's use of the view.
 	 */
-	new_rte->checkAsUser = view->rd_rel->relowner;
+	if (RelationIsSecurityDefinerView(view))
+		new_rte->checkAsUser = view->rd_rel->relowner;
+	else
+		new_rte->checkAsUser = GetUserId();
 	new_rte->requiredPerms = view_rte->requiredPerms;
 
 	/*
diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c
index eebc493..513a2dd 100644
--- a/src/backend/rewrite/rowsecurity.c
+++ b/src/backend/rewrite/rowsecurity.c
@@ -105,7 +105,8 @@ row_security_policy_hook_type row_security_policy_hook_restrictive = NULL;
 void
 get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
 						  List **securityQuals, List **withCheckOptions,
-						  bool *hasRowSecurity, bool *hasSubLinks)
+						  bool *hasRowSecurity, bool *hasSubLinks,
+						  bool security_definer_view)
 {
 	Oid			user_id;
 	int			rls_status;
@@ -125,10 +126,13 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
 		return;
 
 	/* Switch to checkAsUser if it's set */
-	user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId();
+	if (security_definer_view)
+		user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId();
+	else
+		user_id = GetUserId();
 
 	/* Determine the state of RLS for this, pass checkAsUser explicitly */
-	rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false);
+	rls_status = check_enable_rls(rte->relid, user_id, false);
 
 	/* If there is no RLS on this table at all, nothing to do */
 	if (rls_status == RLS_NONE)
diff --git a/src/include/rewrite/rowsecurity.h b/src/include/rewrite/rowsecurity.h
index 4af244d..0aabf3d 100644
--- a/src/include/rewrite/rowsecurity.h
+++ b/src/include/rewrite/rowsecurity.h
@@ -43,6 +43,7 @@ extern PGDLLIMPORT row_security_policy_hook_type row_security_policy_hook_restri
 extern void get_row_security_policies(Query *root,
 						  RangeTblEntry *rte, int rt_index,
 						  List **securityQuals, List **withCheckOptions,
-						  bool *hasRowSecurity, bool *hasSubLinks);
+						  bool *hasRowSecurity, bool *hasSubLinks,
+						  bool security_definer_view);
 
 #endif   /* ROWSECURITY_H */
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 8a55a09..da248e3 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -265,6 +265,7 @@ typedef struct ViewOptions
 {
 	int32		vl_len_;		/* varlena header (do not touch directly!) */
 	bool		security_barrier;
+	bool		security_definer;
 	int			check_option_offset;
 } ViewOptions;
 
@@ -278,6 +279,14 @@ typedef struct ViewOptions
 	 ((ViewOptions *) (relation)->rd_options)->security_barrier : false)
 
 /*
+ * RelationIsSecurityDefiner
+ *		Returns whether the relation is security definer, or not.
+ */
+#define RelationIsSecurityDefinerView(relation)	\
+	((relation)->rd_options ?				\
+	 ((ViewOptions *) (relation)->rd_options)->security_definer : false)
+
+/*
  * RelationHasCheckOption
  *		Returns true if the relation is a view defined with either the local
  *		or the cascaded check option.  Note multiple eval of argument!
diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out
index 8d925dc..d1f5742 100644
--- a/src/test/regress/expected/rowsecurity.out
+++ b/src/test/regress/expected/rowsecurity.out
@@ -1425,7 +1425,7 @@ CREATE POLICY p1 ON b1 USING (a % 2 = 0);
 ALTER TABLE b1 ENABLE ROW LEVEL SECURITY;
 GRANT ALL ON b1 TO rls_regress_user1;
 SET SESSION AUTHORIZATION rls_regress_user1;
-CREATE VIEW bv1 WITH (security_barrier) AS SELECT * FROM b1 WHERE a > 0 WITH CHECK OPTION;
+CREATE VIEW bv1 WITH (security_barrier, security_definer) AS SELECT * FROM b1 WHERE a > 0 WITH CHECK OPTION;
 GRANT ALL ON bv1 TO rls_regress_user2;
 SET SESSION AUTHORIZATION rls_regress_user2;
 EXPLAIN (COSTS OFF) SELECT * FROM bv1 WHERE f_leak(b);
@@ -1892,7 +1892,7 @@ EXPLAIN (COSTS OFF) EXECUTE plancache_test3;
 --
 -- View and Table owner are the same.
 SET SESSION AUTHORIZATION rls_regress_user0;
-CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b);
+CREATE VIEW rls_view WITH (security_definer) AS SELECT * FROM z1 WHERE f_leak(b);
 GRANT SELECT ON rls_view TO rls_regress_user1;
 -- Query as role that is not owner of view or table.  Should return all records.
 SET SESSION AUTHORIZATION rls_regress_user1;
@@ -1941,7 +1941,7 @@ EXPLAIN (COSTS OFF) SELECT * FROM rls_view;
 DROP VIEW rls_view;
 -- View and Table owners are different.
 SET SESSION AUTHORIZATION rls_regress_user1;
-CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b);
+CREATE VIEW rls_view WITH (security_definer) AS SELECT * FROM z1 WHERE f_leak(b);
 GRANT SELECT ON rls_view TO rls_regress_user0;
 -- Query as role that is not owner of view but is owner of table.
 -- Should return records based on view owner policies.
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index 6c71371..b62f1ad 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -966,7 +966,7 @@ CREATE USER view_user2;
 SET SESSION AUTHORIZATION view_user1;
 CREATE TABLE base_tbl(a int, b text, c float);
 INSERT INTO base_tbl VALUES (1, 'Row 1', 1.0);
-CREATE VIEW rw_view1 AS SELECT b AS bb, c AS cc, a AS aa FROM base_tbl;
+CREATE VIEW rw_view1 WITH (security_definer) AS SELECT b AS bb, c AS cc, a AS aa FROM base_tbl;
 INSERT INTO rw_view1 VALUES ('Row 2', 2.0, 2);
 GRANT SELECT ON base_tbl TO view_user2;
 GRANT SELECT ON rw_view1 TO view_user2;
diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql
index decde90..817ade7 100644
--- a/src/test/regress/sql/rowsecurity.sql
+++ b/src/test/regress/sql/rowsecurity.sql
@@ -491,7 +491,7 @@ ALTER TABLE b1 ENABLE ROW LEVEL SECURITY;
 GRANT ALL ON b1 TO rls_regress_user1;
 
 SET SESSION AUTHORIZATION rls_regress_user1;
-CREATE VIEW bv1 WITH (security_barrier) AS SELECT * FROM b1 WHERE a > 0 WITH CHECK OPTION;
+CREATE VIEW bv1 WITH (security_barrier, security_definer) AS SELECT * FROM b1 WHERE a > 0 WITH CHECK OPTION;
 GRANT ALL ON bv1 TO rls_regress_user2;
 
 SET SESSION AUTHORIZATION rls_regress_user2;
@@ -687,7 +687,7 @@ EXPLAIN (COSTS OFF) EXECUTE plancache_test3;
 --
 -- View and Table owner are the same.
 SET SESSION AUTHORIZATION rls_regress_user0;
-CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b);
+CREATE VIEW rls_view WITH (security_definer) AS SELECT * FROM z1 WHERE f_leak(b);
 GRANT SELECT ON rls_view TO rls_regress_user1;
 
 -- Query as role that is not owner of view or table.  Should return all records.
@@ -703,7 +703,7 @@ DROP VIEW rls_view;
 
 -- View and Table owners are different.
 SET SESSION AUTHORIZATION rls_regress_user1;
-CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b);
+CREATE VIEW rls_view WITH (security_definer) AS SELECT * FROM z1 WHERE f_leak(b);
 GRANT SELECT ON rls_view TO rls_regress_user0;
 
 -- Query as role that is not owner of view but is owner of table.
diff --git a/src/test/regress/sql/updatable_views.sql b/src/test/regress/sql/updatable_views.sql
index 5297a71..8452c18 100644
--- a/src/test/regress/sql/updatable_views.sql
+++ b/src/test/regress/sql/updatable_views.sql
@@ -397,7 +397,7 @@ CREATE USER view_user2;
 SET SESSION AUTHORIZATION view_user1;
 CREATE TABLE base_tbl(a int, b text, c float);
 INSERT INTO base_tbl VALUES (1, 'Row 1', 1.0);
-CREATE VIEW rw_view1 AS SELECT b AS bb, c AS cc, a AS aa FROM base_tbl;
+CREATE VIEW rw_view1 WITH (security_definer) AS SELECT b AS bb, c AS cc, a AS aa FROM base_tbl;
 INSERT INTO rw_view1 VALUES ('Row 2', 2.0, 2);
 
 GRANT SELECT ON base_tbl TO view_user2;
#21Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Haribabu Kommi (#20)
2 attachment(s)
Re: Multi-tenancy with RLS

On Wed, Dec 30, 2015 at 11:28 AM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

On Thu, Dec 17, 2015 at 12:46 PM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

Rebased patch is attached as it is having an OID conflict with the
latest set of changes
in the master branch.

Here I attached new series of patches with a slightly different approach.
Instead of creating the policies on the system catalog tables whenever
the catalog security command is executed, just enable row level security
on the system catalog tables. During the relation build, in
RelationBuildRowSecurity function, if it is a system relation, frame the
policy using the policy query which we earlier used to create by parsing it.

With the above approach, in case of any problems in the policy, to use
the corrected policy, user just needs to replace the binaries. whereas in
earlier approach, either pg_upgrade or disabling and enabling of catalog
security is required.

Currently it is changed only for shared system catalog tables and also the
way of enabling catalog security on shared system catalog tables is through
initdb only. This also can be changed later. I will do similar changes for
remaining catalog tables.

Any comments on the approach?

Instead of creating policies during the "alter database" command for database
catalog tables, generating at relation building is leading to an
infinite recursion
loop because of transformExpr call for the qual. Any ideas to handle the same?

Here I attached updated patches to HEAD.

Regards,
Hari Babu
Fujitsu Australia

Attachments:

4_database_catalog_tenancy_v4.patchapplication/octet-stream; name=4_database_catalog_tenancy_v4.patchDownload
diff --git a/doc/src/sgml/ref/alter_database.sgml b/doc/src/sgml/ref/alter_database.sgml
index cfc28cf..adc3326 100644
--- a/doc/src/sgml/ref/alter_database.sgml
+++ b/doc/src/sgml/ref/alter_database.sgml
@@ -28,6 +28,7 @@ ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> [ [ WITH ] <rep
     ALLOW_CONNECTIONS <replaceable class="PARAMETER">allowconn</replaceable>
     CONNECTION LIMIT <replaceable class="PARAMETER">connlimit</replaceable>
     IS_TEMPLATE <replaceable class="PARAMETER">istemplate</replaceable>
+    CATALOG SECURITY <replaceable class="PARAMETER">catalog_security</replaceable>
 
 ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> RENAME TO <replaceable>new_name</replaceable>
 
@@ -139,6 +140,18 @@ ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> RESET ALL
         </para>
        </listitem>
       </varlistentry>
+      
+     <varlistentry>
+       <term><replaceable class="parameter">catalog_security</replaceable></term>
+       <listitem>
+        <para>
+         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.
+        </para>
+       </listitem>
+     </varlistentry>
 
    <varlistentry>
     <term><replaceable>new_name</replaceable></term>
@@ -203,6 +216,12 @@ ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> RESET ALL
    Role-specific settings override database-specific
    ones if there is a conflict.
   </para>
+  
+  <para>
+   The catalog_security option is not supported at <xref linkend="sql-createdatabase">
+   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.
+  </para>
  </refsect1>
 
  <refsect1>
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index c1c0223..2002dbc 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -42,6 +42,7 @@
 #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,6 +227,13 @@ createdb(const CreatedbStmt *stmt)
 					 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,6 +527,7 @@ createdb(const CreatedbStmt *stmt)
 	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,11 +1384,14 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 	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,6 +1433,15 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 						 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,6 +1478,8 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 					(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,12 +1499,19 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 				(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,6 +1523,17 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 				(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,6 +1556,11 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 		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);
diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c
index fd9596f..dde82e3 100644
--- a/src/backend/commands/policy.c
+++ b/src/backend/commands/policy.c
@@ -22,19 +22,62 @@
 #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_auth_members.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_database.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_pltemplate.h"
 #include "catalog/pg_policy.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_range.h"
 #include "catalog/pg_replication_origin.h"
+#include "catalog/pg_rewrite.h"
+#include "catalog/pg_seclabel.h"
 #include "catalog/pg_shdepend.h"
 #include "catalog/pg_shdescription.h"
 #include "catalog/pg_shseclabel.h"
+#include "catalog/pg_statistic.h"
 #include "catalog/pg_tablespace.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"
@@ -54,6 +97,7 @@
 #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 65536
@@ -65,6 +109,9 @@ static Datum *policy_role_list_to_array(List *roles, int *num_roles);
 
 static bool get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *buf);
 
+/* variable to identify whether pg_policy relcache is built is in progress or not? */
+bool policyRelcacheBuiltInProgress = false;
+
 /*
  * Callback to RangeVarGetRelidExtended().
  *
@@ -209,6 +256,16 @@ RelationBuildRowSecurity(Relation relation)
 	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.
@@ -228,7 +285,7 @@ RelationBuildRowSecurity(Relation relation)
 		rsdesc = MemoryContextAllocZero(rscxt, sizeof(RowSecurityDesc));
 		rsdesc->rscxt = rscxt;
 
-		if (IsSystemRelation(relation))
+		if (IsSharedRelation(relation->rd_id))
 		{
 			RowSecurityPolicy *policy;
 			Datum	   *role_oids;
@@ -296,6 +353,12 @@ RelationBuildRowSecurity(Relation relation)
 			SysScanDesc sscan;
 			HeapTuple	tuple;
 
+			if (relation->rd_id == PolicyRelationId)
+			{
+				Assert(policyRelcacheBuiltInProgress == false);
+				policyRelcacheBuiltInProgress = true;
+			}
+
 			catalog = heap_open(PolicyRelationId, AccessShareLock);
 
 			ScanKeyInit(&skey,
@@ -403,10 +466,23 @@ RelationBuildRowSecurity(Relation relation)
 		/* 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;
 }
@@ -492,6 +568,26 @@ get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *bu
 
 	switch (relationid)
 	{
+		/*
+		 * Following catalog tables data is accessible to all roles. 
+		 * So they doesn't need any specific RLS policies on them.
+		 */
+		case AccessMethodRelationId:
+		case AccessMethodOperatorRelationId:
+		case AccessMethodProcedureRelationId:
+		case AggregateRelationId:
+			policy_exist = false;
+			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);
+			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);
+			break;
 		case AuthIdRelationId:
 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
 					" (pg_has_role(oid, 'any'))",
@@ -502,6 +598,22 @@ get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *bu
 					" (pg_has_role(roleid, 'any') AND pg_has_role(member, 'any'))",
 					pg_class_tuple->relname.data, pg_class_tuple->relname.data);
 			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);
+			break;
+		case CollationRelationId:
+			policy_exist = false;
+			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);
+			break;
+		case ConversionRelationId:
+			policy_exist = false;
+			break;
 		case DatabaseRelationId:
 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
 					" ((oid < 16384) OR has_database_privilege(oid,'any'))",
@@ -512,12 +624,175 @@ get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *bu
 					" (pg_has_role(setrole, 'any') AND has_database_privilege(setdatabase,'any'))",
 					pg_class_tuple->relname.data, pg_class_tuple->relname.data);
 			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);
+			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);
+			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);
+			break;
+		case EnumRelationId:
+		case EventTriggerRelationId:
+		case ExtensionRelationId:
+			policy_exist = false;
+			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);
+			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);
+			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);
+			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);
+			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);
+			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);
+			break;
+		case LargeObjectRelationId:
+		case LargeObjectMetadataRelationId:
+			policy_exist = false;
+			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);
+			policy_exist = true;
+			break;
+		case OperatorClassRelationId:
+		case OperatorRelationId:
+		case OperatorFamilyRelationId:
 		case PLTemplateRelationId:
 			policy_exist = false;
 			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);
+			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);
+			break;
+		case RangeRelationId:
+			policy_exist = false;
+			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);
+			break;
 		case ReplicationOriginRelationId:
+		case RewriteRelationId:
 			policy_exist = false;
 			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);
+			break;
 		case SharedDependRelationId:
 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
 					" ((classid = (select oid from pg_class where relname = 'pg_database') AND has_database_privilege(objid, 'any'))"
@@ -539,11 +814,43 @@ get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *bu
 					" 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);
 			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);
+			break;
 		case TableSpaceRelationId:
 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
 					" ((oid < 16384) OR has_tablespace_privilege(oid, 'any'))",
 					pg_class_tuple->relname.data, pg_class_tuple->relname.data);
 			break;
+		case TransformRelationId:
+			policy_exist = false;
+			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);
+			policy_exist = true;
+			break;
+		case TSConfigRelationId:
+		case TSConfigMapRelationId:
+		case TSDictionaryRelationId:
+		case TSParserRelationId:
+		case TSTemplateRelationId:
+			policy_exist = false;
+			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);
+			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);
+			break;
 		default:
 			policy_exist = false;
 			break;
@@ -551,6 +858,177 @@ get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *bu
 
 	return policy_exist;
 }
+
+/*
+ * 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 policy_exist;
+
+			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));
+
+			pg_class_tuple = (Form_pg_class)GETSTRUCT(cache_tuple);
+			policy_exist = get_catalog_policy_string(HeapTupleGetOid(tuple), pg_class_tuple, buf);
+			if (!policy_exist)
+			{
+				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;
+
+			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));
+
+			pg_class_tuple = (Form_pg_class)GETSTRUCT(tuple);
+			if (!pg_class_tuple->relrowsecurity)
+				continue;
+
+			sprintf(buf, "drop policy %s_read_own_data on %s",
+					pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+
+			ret = SPI_execute(buf, false, 0);
+			if (ret != SPI_OK_UTILITY)
+				elog(ERROR, "Drop 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();
 }
 
 /*
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b307b48..5ceb1e5 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -8876,6 +8876,7 @@ createdb_opt_name:
 			| OWNER							{ $$ = pstrdup($1); }
 			| TABLESPACE					{ $$ = pstrdup($1); }
 			| TEMPLATE						{ $$ = pstrdup($1); }
+			| CATALOG_P SECURITY				{ $$ = pstrdup("catalog_security"); }
 		;
 
 /*
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index a07350d..c6a697b 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -17,11 +17,19 @@
 #include <ctype.h>
 
 #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_type.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,6 +39,7 @@
 #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,3 +5276,293 @@ get_rolespec_name(const Node *node)
 
 	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));
+}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 114b7a7..3ef41ab 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -2076,6 +2076,8 @@ RelationClearRelation(Relation relation, bool rebuild)
 	 */
 	if (relation->rd_isnailed)
 	{
+		HeapTuple pg_class_tuple;
+
 		RelationInitPhysicalAddr(relation);
 
 		if (relation->rd_rel->relkind == RELKIND_INDEX)
@@ -2084,6 +2086,27 @@ RelationClearRelation(Relation relation, bool rebuild)
 			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;
 	}
 
diff --git a/src/include/catalog/pg_database.h b/src/include/catalog/pg_database.h
index 6ae1b40..1816d8b 100644
--- a/src/include/catalog/pg_database.h
+++ b/src/include/catalog/pg_database.h
@@ -43,7 +43,7 @@ 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 */
-
+	bool		datcatalogsecurity; /* catalog security is enabled? */
 #ifdef CATALOG_VARLEN			/* variable-length fields start here */
 	aclitem		datacl[1];		/* access permissions */
 #endif
@@ -60,22 +60,23 @@ 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
+#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 _null_));
+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
 
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index e5d6c77..1e8a604 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3644,6 +3644,24 @@ DESCR("current user privilege on role by role name");
 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 = 3321 (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 = 3322 (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 = 3323 (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 = 3324 (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 = 3325 (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 = 3326 (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_ ));
diff --git a/src/include/commands/policy.h b/src/include/commands/policy.h
index dbf7824..453d335 100644
--- a/src/include/commands/policy.h
+++ b/src/include/commands/policy.h
@@ -35,4 +35,7 @@ extern ObjectAddress rename_policy(RenameStmt *stmt);
 
 extern bool relation_has_policies(Relation rel);
 
+extern void CreateCatalogPolicy(void);
+extern void RemoveCatalogPolicy(void);
+
 #endif   /* POLICY_H */
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index bbaa2ce..0681af9 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -106,6 +106,12 @@ extern Datum pg_has_role_id_name(PG_FUNCTION_ARGS);
 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);
diff --git a/src/test/regress/expected/multitenancy.out b/src/test/regress/expected/multitenancy.out
new file mode 100644
index 0000000..cbdb1fa
--- /dev/null
+++ b/src/test/regress/expected/multitenancy.out
@@ -0,0 +1,644 @@
+-- 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;
+alter schema tenancy_user1 owner to tenancy_user1;
+create schema tenancy_user2;
+alter schema tenancy_user2 owner 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;
+ALTER SERVER tenancy_server OWNER 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 from pg_namespace where nspname = 'tenancy_user1';
+    nspname    
+---------------
+ tenancy_user1
+(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_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 from pg_namespace where nspname = 'tenancy_user1';
+ nspname 
+---------
+(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_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
+drop schema tenancy_user1;
+RESET ROLE;
+SET SESSION ROLE tenancy_user2;
+DROP FUNCTION tenancy_user2_tenancytestfunc(text);
+DROP FOREIGN TABLE tenancy_user2_ft1;
+DROP LANGUAGE tenancy_lang1;
+drop schema tenancy_user2;
+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 role tenancy_user1;
+drop role tenancy_user2;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index b1bc7c7..04939da 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -60,7 +60,7 @@ 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
+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 *
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index ade9ef1..8832ef1 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -160,4 +160,5 @@ test: largeobject
 test: with
 test: xml
 test: event_trigger
+test: multitenancy
 test: stats
diff --git a/src/test/regress/sql/multitenancy.sql b/src/test/regress/sql/multitenancy.sql
new file mode 100644
index 0000000..0b774a5
--- /dev/null
+++ b/src/test/regress/sql/multitenancy.sql
@@ -0,0 +1,297 @@
+-- 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;
+alter schema tenancy_user1 owner to tenancy_user1;
+
+create schema tenancy_user2;
+alter schema tenancy_user2 owner 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;
+ALTER SERVER tenancy_server OWNER 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 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_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 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_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;
+drop schema tenancy_user1;
+
+RESET ROLE;
+
+SET SESSION ROLE tenancy_user2;
+
+DROP FUNCTION tenancy_user2_tenancytestfunc(text);
+DROP FOREIGN TABLE tenancy_user2_ft1;
+DROP LANGUAGE tenancy_lang1;
+drop schema tenancy_user2;
+
+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 role tenancy_user1;
+drop role tenancy_user2;
3_shared_catalog_tenancy_v3.patchapplication/octet-stream; name=3_shared_catalog_tenancy_v3.patchDownload
diff --git a/doc/src/sgml/ref/initdb.sgml b/doc/src/sgml/ref/initdb.sgml
index 4e339ec..ac460e9 100644
--- a/doc/src/sgml/ref/initdb.sgml
+++ b/doc/src/sgml/ref/initdb.sgml
@@ -152,6 +152,19 @@ PostgreSQL documentation
      </varlistentry>
 
      <varlistentry>
+      <term><option>-C</option></term>
+      <term><option>--shared-catalog-security</option></term>
+      <listitem>
+       <para>
+        This option enables the shared catalog tables security, by adding
+        row level security policies on all eligible shared catalog tables.
+        With this option, multi-tenancy in PostgreSQL is supported at 
+        database level.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><option>-D <replaceable class="parameter">directory</replaceable></option></term>
       <term><option>--pgdata=<replaceable class="parameter">directory</replaceable></option></term>
       <listitem>
diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c
index 7e97b83..fd9596f 100644
--- a/src/backend/commands/policy.c
+++ b/src/backend/commands/policy.c
@@ -23,7 +23,16 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_authid.h"
+#include "catalog/pg_auth_members.h"
+#include "catalog/pg_database.h"
+#include "catalog/pg_db_role_setting.h"
+#include "catalog/pg_pltemplate.h"
 #include "catalog/pg_policy.h"
+#include "catalog/pg_replication_origin.h"
+#include "catalog/pg_shdepend.h"
+#include "catalog/pg_shdescription.h"
+#include "catalog/pg_shseclabel.h"
+#include "catalog/pg_tablespace.h"
 #include "catalog/pg_type.h"
 #include "commands/policy.h"
 #include "miscadmin.h"
@@ -36,6 +45,7 @@
 #include "rewrite/rewriteManip.h"
 #include "rewrite/rowsecurity.h"
 #include "storage/lock.h"
+#include "tcop/tcopprot.h"
 #include "utils/acl.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
@@ -46,11 +56,15 @@
 #include "utils/rel.h"
 #include "utils/syscache.h"
 
+#define CATALOG_POLICY_STRING_SIZE 65536
+
 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 get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *buf);
+
 /*
  * Callback to RangeVarGetRelidExtended().
  *
@@ -211,114 +225,178 @@ RelationBuildRowSecurity(Relation relation)
 	 */
 	PG_TRY();
 	{
-		Relation	catalog;
-		ScanKeyData skey;
-		SysScanDesc sscan;
-		HeapTuple	tuple;
-
 		rsdesc = MemoryContextAllocZero(rscxt, sizeof(RowSecurityDesc));
 		rsdesc->rscxt = rscxt;
 
-		catalog = heap_open(PolicyRelationId, AccessShareLock);
-
-		ScanKeyInit(&skey,
-					Anum_pg_policy_polrelid,
-					BTEqualStrategyNumber, F_OIDEQ,
-					ObjectIdGetDatum(RelationGetRelid(relation)));
-
-		sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
-								   NULL, 1, &skey);
-
-		/*
-		 * Loop through the row level security policies for this relation, if
-		 * any.
-		 */
-		while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
+		if (IsSystemRelation(relation))
 		{
-			Datum		value_datum;
-			char		cmd_value;
-			Datum		roles_datum;
-			char	   *qual_value;
-			Expr	   *qual_expr;
-			char	   *with_check_value;
-			Expr	   *with_check_qual;
-			char	   *policy_name_value;
-			bool		isnull;
 			RowSecurityPolicy *policy;
+			Datum	   *role_oids;
+			int			nitems = 0;
+			ArrayType  *role_ids;
+			Node	   *qual_expr;
+			List	   *raw_parsetree_list;
+			ListCell   *list_item;
+			ParseState *qual_pstate;
+			RangeTblEntry *rte;
+			CreatePolicyStmt *stmt;
+			bool	policy_exist;
+			char 	buf[CATALOG_POLICY_STRING_SIZE];
 
-			/*
-			 * Note: all the pass-by-reference data we collect here is either
-			 * still stored in the tuple, or constructed in the caller's
-			 * short-lived memory context.  We must copy it into rscxt
-			 * explicitly below.
-			 */
+			policy_exist = get_catalog_policy_string(relation->rd_id, relation->rd_rel, buf);
+			if (!policy_exist)
+				continue;
 
-			/* Get policy command */
-			value_datum = heap_getattr(tuple, Anum_pg_policy_polcmd,
-									   RelationGetDescr(catalog), &isnull);
-			Assert(!isnull);
-			cmd_value = DatumGetChar(value_datum);
-
-			/* Get policy name */
-			value_datum = heap_getattr(tuple, Anum_pg_policy_polname,
-									   RelationGetDescr(catalog), &isnull);
-			Assert(!isnull);
-			policy_name_value = NameStr(*(DatumGetName(value_datum)));
-
-			/* Get policy roles */
-			roles_datum = heap_getattr(tuple, Anum_pg_policy_polroles,
-									   RelationGetDescr(catalog), &isnull);
-			/* shouldn't be null, but initdb doesn't mark it so, so check */
-			if (isnull)
-				elog(ERROR, "unexpected null value in pg_policy.polroles");
-
-			/* Get policy qual */
-			value_datum = heap_getattr(tuple, Anum_pg_policy_polqual,
-									   RelationGetDescr(catalog), &isnull);
-			if (!isnull)
-			{
-				qual_value = TextDatumGetCString(value_datum);
-				qual_expr = (Expr *) stringToNode(qual_value);
-			}
-			else
-				qual_expr = NULL;
+			role_oids = policy_role_list_to_array(NULL, &nitems);
+			role_ids = construct_array(role_oids, nitems, OIDOID,
+									   sizeof(Oid), true, 'i');
 
-			/* Get WITH CHECK qual */
-			value_datum = heap_getattr(tuple, Anum_pg_policy_polwithcheck,
-									   RelationGetDescr(catalog), &isnull);
-			if (!isnull)
+			raw_parsetree_list = pg_parse_query(buf);
+			Assert(list_length(raw_parsetree_list) == 1);
+
+			foreach(list_item, raw_parsetree_list)
 			{
-				with_check_value = TextDatumGetCString(value_datum);
-				with_check_qual = (Expr *) stringToNode(with_check_value);
+				Node	   *parsetree;
+				parsetree = (Node *) lfirst(list_item);
+				stmt = (CreatePolicyStmt *)parsetree;
 			}
-			else
-				with_check_qual = NULL;
+
+			qual_pstate = make_parsestate(NULL);
+			rte = addRangeTableEntryForRelation(qual_pstate, relation,
+												NULL, false, false);
+
+			addRTEtoQuery(qual_pstate, rte, false, true, true);
+			qual_expr = transformWhereClause(qual_pstate,
+										stmt->qual,
+										EXPR_KIND_POLICY,
+										"POLICY");
+
+			/* Fix up collation information */
+			assign_expr_collations(qual_pstate, qual_expr);
 
 			/* Now copy everything into the cache context */
 			MemoryContextSwitchTo(rscxt);
 
 			policy = palloc0(sizeof(RowSecurityPolicy));
-			policy->policy_name = pstrdup(policy_name_value);
-			policy->polcmd = cmd_value;
-			policy->roles = DatumGetArrayTypePCopy(roles_datum);
+			policy->policy_name = pstrdup(stmt->policy_name);
+			policy->polcmd = ACL_SELECT_CHR;
+			policy->roles = DatumGetArrayTypePCopy(role_ids);
 			policy->qual = copyObject(qual_expr);
-			policy->with_check_qual = copyObject(with_check_qual);
-			policy->hassublinks = checkExprHasSubLink((Node *) qual_expr) ||
-				checkExprHasSubLink((Node *) with_check_qual);
+			policy->with_check_qual = NULL;
+			policy->hassublinks = checkExprHasSubLink((Node *) qual_expr);
 
 			rsdesc->policies = lcons(policy, rsdesc->policies);
 
 			MemoryContextSwitchTo(oldcxt);
-
-			/* clean up some (not all) of the junk ... */
-			if (qual_expr != NULL)
-				pfree(qual_expr);
-			if (with_check_qual != NULL)
-				pfree(with_check_qual);
 		}
+		else
+		{
+			Relation	catalog;
+			ScanKeyData skey;
+			SysScanDesc sscan;
+			HeapTuple	tuple;
+
+			catalog = heap_open(PolicyRelationId, AccessShareLock);
+
+			ScanKeyInit(&skey,
+						Anum_pg_policy_polrelid,
+						BTEqualStrategyNumber, F_OIDEQ,
+						ObjectIdGetDatum(RelationGetRelid(relation)));
+
+			sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
+									   NULL, 1, &skey);
+
+			/*
+			 * Loop through the row level security policies for this relation, if
+			 * any.
+			 */
+			while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
+			{
+				Datum		value_datum;
+				char		cmd_value;
+				Datum		roles_datum;
+				char	   *qual_value;
+				Expr	   *qual_expr;
+				char	   *with_check_value;
+				Expr	   *with_check_qual;
+				char	   *policy_name_value;
+				bool		isnull;
+				RowSecurityPolicy *policy;
+
+				/*
+				 * Note: all the pass-by-reference data we collect here is either
+				 * still stored in the tuple, or constructed in the caller's
+				 * short-lived memory context.  We must copy it into rscxt
+				 * explicitly below.
+				 */
+
+				/* Get policy command */
+				value_datum = heap_getattr(tuple, Anum_pg_policy_polcmd,
+										   RelationGetDescr(catalog), &isnull);
+				Assert(!isnull);
+				cmd_value = DatumGetChar(value_datum);
+
+				/* Get policy name */
+				value_datum = heap_getattr(tuple, Anum_pg_policy_polname,
+										   RelationGetDescr(catalog), &isnull);
+				Assert(!isnull);
+				policy_name_value = NameStr(*(DatumGetName(value_datum)));
+
+				/* Get policy roles */
+				roles_datum = heap_getattr(tuple, Anum_pg_policy_polroles,
+										   RelationGetDescr(catalog), &isnull);
+				/* shouldn't be null, but initdb doesn't mark it so, so check */
+				if (isnull)
+					elog(ERROR, "unexpected null value in pg_policy.polroles");
+
+				/* Get policy qual */
+				value_datum = heap_getattr(tuple, Anum_pg_policy_polqual,
+										   RelationGetDescr(catalog), &isnull);
+				if (!isnull)
+				{
+					qual_value = TextDatumGetCString(value_datum);
+					qual_expr = (Expr *) stringToNode(qual_value);
+				}
+				else
+					qual_expr = NULL;
+
+				/* Get WITH CHECK qual */
+				value_datum = heap_getattr(tuple, Anum_pg_policy_polwithcheck,
+										   RelationGetDescr(catalog), &isnull);
+				if (!isnull)
+				{
+					with_check_value = TextDatumGetCString(value_datum);
+					with_check_qual = (Expr *) stringToNode(with_check_value);
+				}
+				else
+					with_check_qual = NULL;
+
+				/* Now copy everything into the cache context */
+				MemoryContextSwitchTo(rscxt);
+
+				policy = palloc0(sizeof(RowSecurityPolicy));
+				policy->policy_name = pstrdup(policy_name_value);
+				policy->polcmd = cmd_value;
+				policy->roles = DatumGetArrayTypePCopy(roles_datum);
+				policy->qual = copyObject(qual_expr);
+				policy->with_check_qual = copyObject(with_check_qual);
+				policy->hassublinks = checkExprHasSubLink((Node *) qual_expr) ||
+					checkExprHasSubLink((Node *) with_check_qual);
+
+				rsdesc->policies = lcons(policy, rsdesc->policies);
+
+				MemoryContextSwitchTo(oldcxt);
+
+				/* clean up some (not all) of the junk ... */
+				if (qual_expr != NULL)
+					pfree(qual_expr);
+				if (with_check_qual != NULL)
+					pfree(with_check_qual);
+			}
 
-		systable_endscan(sscan);
-		heap_close(catalog, AccessShareLock);
+			systable_endscan(sscan);
+			heap_close(catalog, AccessShareLock);
+		}
 	}
 	PG_CATCH();
 	{
@@ -407,6 +485,74 @@ RemovePolicyById(Oid policy_id)
 	heap_close(pg_policy_rel, RowExclusiveLock);
 }
 
+static bool 
+get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *buf)
+{
+	bool policy_exist = true;
+
+	switch (relationid)
+	{
+		case AuthIdRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+					" (pg_has_role(oid, 'any'))",
+					pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+			break;
+		case AuthMemRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+					" (pg_has_role(roleid, 'any') AND pg_has_role(member, 'any'))",
+					pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+			break;
+		case DatabaseRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+					" ((oid < 16384) OR has_database_privilege(oid,'any'))",
+					pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+			break;
+		case DbRoleSettingRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+					" (pg_has_role(setrole, 'any') AND has_database_privilege(setdatabase,'any'))",
+					pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+			break;
+		case PLTemplateRelationId:
+			policy_exist = false;
+			break;
+		case ReplicationOriginRelationId:
+			policy_exist = false;
+			break;
+		case SharedDependRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+					" ((classid = (select oid from pg_class where relname = 'pg_database') AND has_database_privilege(objid, 'any'))"
+					" OR (classid = (select oid from pg_class where relname = 'pg_authid') AND pg_has_role(objid, 'any'))"
+					" OR (classid = (select oid from pg_class where relname = 'pg_tablespace') AND has_tablespace_privilege(objid, 'any')))",
+					pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+			break;
+		case SharedDescriptionRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+					" ((classoid = (select oid from pg_class where relname = 'pg_database') AND has_database_privilege(objoid, 'any'))"
+					" OR (classoid = (select oid from pg_class where relname = 'pg_authid') AND pg_has_role(objoid, 'any'))"
+					" 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);
+			break;
+		case SharedSecLabelRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+					" ((classoid = (select oid from pg_class where relname = 'pg_database') AND has_database_privilege(objoid, 'any'))"
+					" OR (classoid = (select oid from pg_class where relname = 'pg_authid') AND pg_has_role(objoid, 'any'))"
+					" 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);
+			break;
+		case TableSpaceRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+					" ((oid < 16384) OR has_tablespace_privilege(oid, 'any'))",
+					pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+			break;
+		default:
+			policy_exist = false;
+			break;
+	}
+
+	return policy_exist;
+}
+}
+
 /*
  * RemoveRoleFromObjectPolicy -
  *	 remove a role from a policy by its OID.  If the role is not a member of
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 0b4a334..8204b99 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -3442,6 +3442,15 @@ ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode)
 	{
 		AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
 
+		/*
+		 * ALTER table on sytem catalog tables is possible only when user specifies
+		 * CATALOG SECURITY on system catalog tables. To avoid an error in the 
+		 * AlterTableCreateToastTable function for system catalog tables, the system
+		 * catalog tables are ignored for the toast table creation.
+		 */
+		if (!IsUnderPostmaster && IsSharedRelation(tab->relid))
+			continue;
+
 		if (tab->relkind == RELKIND_RELATION ||
 			tab->relkind == RELKIND_MATVIEW)
 			AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
diff --git a/src/backend/utils/misc/rls.c b/src/backend/utils/misc/rls.c
index b6c1d75..ab370b1 100644
--- a/src/backend/utils/misc/rls.c
+++ b/src/backend/utils/misc/rls.c
@@ -58,10 +58,6 @@ check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
 	bool		relforcerowsecurity;
 	Oid			user_id = checkAsUser ? checkAsUser : GetUserId();
 
-	/* Nothing to do for built-in relations */
-	if (relid < FirstNormalObjectId)
-		return RLS_NONE;
-
 	tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
 	if (!HeapTupleIsValid(tuple))
 		return RLS_NONE;
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 35e39ce..0e996e0 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -136,6 +136,7 @@ static bool do_sync = true;
 static bool sync_only = false;
 static bool show_setting = false;
 static bool data_checksums = false;
+static bool shared_catalog_security = false;
 static char *xlog_dir = "";
 
 
@@ -190,6 +191,7 @@ static char *authwarning = NULL;
  */
 static const char *boot_options = "-F";
 static const char *backend_options = "--single -F -O -j -c search_path=pg_catalog -c exit_on_error=true";
+static const char *catalog_security_options = "--single -F -O -j -c search_path=pg_catalog -c exit_on_error=true -c allow_system_table_mods=true";
 
 static const char *const subdirs[] = {
 	"global",
@@ -259,6 +261,7 @@ static void setup_dictionary(FILE *cmdfd);
 static void setup_privileges(FILE *cmdfd);
 static void set_info_version(void);
 static void setup_schema(FILE *cmdfd);
+static void setup_shared_catalog_security(FILE *cmdfd);
 static void load_plpgsql(FILE *cmdfd);
 static void vacuum_db(FILE *cmdfd);
 static void make_template0(FILE *cmdfd);
@@ -2082,6 +2085,57 @@ setup_schema(FILE *cmdfd)
 }
 
 /*
+ * setup shared catalog security by defining policies
+ */
+static void
+setup_shared_catalog_security(FILE *cmdfd)
+{
+	const char	  **line;
+	static const char *pg_shared_catalog_security_setup[] = {
+		/* AuthMemRelationId */
+		"alter table pg_auth_members enable row level security;\n",
+
+		/* AuthIdRelationId */
+		"alter table pg_authid enable row level security;\n",
+
+		/* DatabaseRelationId */
+		"alter table pg_database enable row level security;\n",
+
+		/* DbRoleSettingRelationId */
+		"alter table pg_database enable row level security;\n",
+
+		/* PLTemplateRelationId */
+		/*
+		 * Currently there is no policy needed for this table, so
+		 * leave it as it is.
+		 */
+
+		/* ReplicationOriginRelationId */
+		/*
+		 * Currently there is no policy needed for this table, so
+		 * leave it as it is.
+		 */
+		 
+		/* SharedDependRelationId */
+		"alter table pg_shdepend enable row level security;\n",
+
+		/* SharedDescriptionRelationId */
+		"alter table pg_shdescription enable row level security;\n",
+
+		/* SharedSecLabelRelationId */
+		"alter table pg_shseclabel enable row level security;\n",
+
+		/* TableSpaceRelationId */
+		"alter table pg_tablespace enable row level security;\n",
+
+		NULL
+	};
+
+	for (line = pg_shared_catalog_security_setup; *line != NULL; line++)
+		PG_CMD_PUTS(*line);
+}
+
+/*
  * load PL/pgsql server-side language
  */
 static void
@@ -2536,6 +2590,8 @@ usage(const char *progname)
 	printf(_("\nLess commonly used options:\n"));
 	printf(_("  -d, --debug               generate lots of debugging output\n"));
 	printf(_("  -k, --data-checksums      use data page checksums\n"));
+	printf(_("  -C, --shared-catalog-security\n"
+			"						      use shared catalog security\n"));
 	printf(_("  -L DIRECTORY              where to find the input files\n"));
 	printf(_("  -n, --noclean             do not clean up after errors\n"));
 	printf(_("  -N, --nosync              do not wait for changes to be written safely to disk\n"));
@@ -3073,6 +3129,10 @@ initialize_data_directory(void)
 {
 	PG_CMD_DECL;
 	int			i;
+	const char *options = backend_options;
+
+	if (shared_catalog_security)
+		options = catalog_security_options;
 
 	setup_signals();
 
@@ -3121,7 +3181,7 @@ initialize_data_directory(void)
 
 	snprintf(cmd, sizeof(cmd),
 			 "\"%s\" %s template1 >%s",
-			 backend_exec, backend_options,
+			 backend_exec, options,
 			 DEVNULL);
 
 	PG_CMD_OPEN;
@@ -3146,6 +3206,9 @@ initialize_data_directory(void)
 
 	setup_schema(cmdfd);
 
+	if (shared_catalog_security)
+		setup_shared_catalog_security(cmdfd);
+	
 	load_plpgsql(cmdfd);
 
 	vacuum_db(cmdfd);
@@ -3190,6 +3253,7 @@ main(int argc, char *argv[])
 		{"sync-only", no_argument, NULL, 'S'},
 		{"xlogdir", required_argument, NULL, 'X'},
 		{"data-checksums", no_argument, NULL, 'k'},
+		{"shared-catalog-security", no_argument, NULL, 'C' },
 		{NULL, 0, NULL, 0}
 	};
 
@@ -3230,7 +3294,7 @@ main(int argc, char *argv[])
 
 	/* process command-line options */
 
-	while ((c = getopt_long(argc, argv, "dD:E:kL:nNU:WA:sST:X:", long_options, &option_index)) != -1)
+	while ((c = getopt_long(argc, argv, "dD:E:kCL:nNU:WA:sST:X:", long_options, &option_index)) != -1)
 	{
 		switch (c)
 		{
@@ -3282,6 +3346,9 @@ main(int argc, char *argv[])
 			case 'k':
 				data_checksums = true;
 				break;
+			case 'C':
+				shared_catalog_security = true;
+				break;
 			case 'L':
 				share_path = pg_strdup(optarg);
 				break;
#22Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Haribabu Kommi (#21)
Re: Multi-tenancy with RLS

On 2016/01/04 14:43, Haribabu Kommi wrote:

Here I attached new series of patches with a slightly different approach.
Instead of creating the policies on the system catalog tables whenever
the catalog security command is executed, just enable row level security
on the system catalog tables. During the relation build, in
RelationBuildRowSecurity function, if it is a system relation, frame the
policy using the policy query which we earlier used to create by parsing it.

With the above approach, in case of any problems in the policy, to use
the corrected policy, user just needs to replace the binaries. whereas in
earlier approach, either pg_upgrade or disabling and enabling of catalog
security is required.

Currently it is changed only for shared system catalog tables and also the
way of enabling catalog security on shared system catalog tables is through
initdb only. This also can be changed later. I will do similar changes for
remaining catalog tables.

Any comments on the approach?

Instead of creating policies during the "alter database" command for database
catalog tables, generating at relation building is leading to an
infinite recursion
loop because of transformExpr call for the qual. Any ideas to handle the same?

I tried your latest patch to see what may have caused the infinite
recursion. The recursion occurs during backend startup itself, right?

ISTM, doing transformWhereClause during RelationCacheInitializePhase3()
would not work. Things like operators, functions within the policy qual
require namespace lookup which down the line would call
RelationBuildRowSecurity for pg_namespace build and so on thus causing the
infinite recursion. Perhaps, it would have to be done in a separate phase
after the phase 3 but I'm not sure.

I wonder why do the policy quals need to be built from scratch every time
in RelationBuildRowSecurity for system tables (shared or otherwise)? Why
not just read from the pg_policy catalog like regular relations if ALTER
DATABASE CATALOG SECURITY TRUE already created those entries? Maybe I
missing something though.

Thanks,
Amit

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#23Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Amit Langote (#22)
Re: Multi-tenancy with RLS

On Mon, Jan 4, 2016 at 8:34 PM, Amit Langote
<Langote_Amit_f8@lab.ntt.co.jp> wrote:

On 2016/01/04 14:43, Haribabu Kommi wrote:

Here I attached new series of patches with a slightly different approach.
Instead of creating the policies on the system catalog tables whenever
the catalog security command is executed, just enable row level security
on the system catalog tables. During the relation build, in
RelationBuildRowSecurity function, if it is a system relation, frame the
policy using the policy query which we earlier used to create by parsing it.

With the above approach, in case of any problems in the policy, to use
the corrected policy, user just needs to replace the binaries. whereas in
earlier approach, either pg_upgrade or disabling and enabling of catalog
security is required.

Currently it is changed only for shared system catalog tables and also the
way of enabling catalog security on shared system catalog tables is through
initdb only. This also can be changed later. I will do similar changes for
remaining catalog tables.

Any comments on the approach?

Instead of creating policies during the "alter database" command for database
catalog tables, generating at relation building is leading to an
infinite recursion
loop because of transformExpr call for the qual. Any ideas to handle the same?

I tried your latest patch to see what may have caused the infinite
recursion. The recursion occurs during backend startup itself, right?

ISTM, doing transformWhereClause during RelationCacheInitializePhase3()
would not work. Things like operators, functions within the policy qual
require namespace lookup which down the line would call
RelationBuildRowSecurity for pg_namespace build and so on thus causing the
infinite recursion. Perhaps, it would have to be done in a separate phase
after the phase 3 but I'm not sure.

Thanks for the test. Yes, the issue happens at backend startup itself.
I will give a try by separating the initialization of security
policies after init phase 3.

I wonder why do the policy quals need to be built from scratch every time
in RelationBuildRowSecurity for system tables (shared or otherwise)? Why
not just read from the pg_policy catalog like regular relations if ALTER
DATABASE CATALOG SECURITY TRUE already created those entries? Maybe I
missing something though.

Yes, creating policies at start and using them every time when
relation is building works
until there is no problem is found in the policies. The row level
security policies on catalog
tables are created automatically when user enables catalog security,
so user don't have
any control on these policies.

In case if we found any problem in these policies, later if we want to
correct them, for
shared system catalog tables policies user needs to do a pg_upgrade to
correct them.
And for the other catalog tables, user needs to disable and enable the
catalog security
on all databases.

Instead of the above, if we built the policy at run time always for
catalog tables, user
can just replace binaries with latest works. Currently it is working
fine for shared system
catalog tables. I will give a try by separating
RelationBuildRowSecurity from init phase 3.

Regards,
Hari Babu
Fujitsu Australia

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#24Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Haribabu Kommi (#23)
2 attachment(s)
Re: Multi-tenancy with RLS

On Mon, Jan 4, 2016 at 10:43 PM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

On Mon, Jan 4, 2016 at 8:34 PM, Amit Langote
<Langote_Amit_f8@lab.ntt.co.jp> wrote:

I tried your latest patch to see what may have caused the infinite
recursion. The recursion occurs during backend startup itself, right?

ISTM, doing transformWhereClause during RelationCacheInitializePhase3()
would not work. Things like operators, functions within the policy qual
require namespace lookup which down the line would call
RelationBuildRowSecurity for pg_namespace build and so on thus causing the
infinite recursion. Perhaps, it would have to be done in a separate phase
after the phase 3 but I'm not sure.

Thanks for the test. Yes, the issue happens at backend startup itself.
I will give a try by separating the initialization of security
policies after init phase 3.

Here I attached updated patches with the fix of infinite recursion in
RelationBuildRowSecurity function by checking with a variable that
whether the build row security is already in progress for a system
relation or not. If it is already in progress for a relation, then it doesn't
build the row security description for this relation.

Regards,
Hari Babu
Fujitsu Australia

Attachments:

4_database_catalog_tenancy_v5.patchapplication/octet-stream; name=4_database_catalog_tenancy_v5.patchDownload
diff --git a/doc/src/sgml/ref/alter_database.sgml b/doc/src/sgml/ref/alter_database.sgml
index cfc28cf..adc3326 100644
--- a/doc/src/sgml/ref/alter_database.sgml
+++ b/doc/src/sgml/ref/alter_database.sgml
@@ -28,6 +28,7 @@ ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> [ [ WITH ] <rep
     ALLOW_CONNECTIONS <replaceable class="PARAMETER">allowconn</replaceable>
     CONNECTION LIMIT <replaceable class="PARAMETER">connlimit</replaceable>
     IS_TEMPLATE <replaceable class="PARAMETER">istemplate</replaceable>
+    CATALOG SECURITY <replaceable class="PARAMETER">catalog_security</replaceable>
 
 ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> RENAME TO <replaceable>new_name</replaceable>
 
@@ -139,6 +140,18 @@ ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> RESET ALL
         </para>
        </listitem>
       </varlistentry>
+      
+     <varlistentry>
+       <term><replaceable class="parameter">catalog_security</replaceable></term>
+       <listitem>
+        <para>
+         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.
+        </para>
+       </listitem>
+     </varlistentry>
 
    <varlistentry>
     <term><replaceable>new_name</replaceable></term>
@@ -203,6 +216,12 @@ ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> RESET ALL
    Role-specific settings override database-specific
    ones if there is a conflict.
   </para>
+  
+  <para>
+   The catalog_security option is not supported at <xref linkend="sql-createdatabase">
+   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.
+  </para>
  </refsect1>
 
  <refsect1>
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index c1c0223..f7b7141 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -42,6 +42,7 @@
 #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,6 +227,13 @@ createdb(const CreatedbStmt *stmt)
 					 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,6 +527,7 @@ createdb(const CreatedbStmt *stmt)
 	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,11 +1384,14 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 	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,6 +1433,15 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 						 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,6 +1478,8 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 					(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,12 +1499,19 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 				(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,6 +1523,17 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 				(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,6 +1556,11 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 		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);
diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c
index 820261e..6d902fd 100644
--- a/src/backend/commands/policy.c
+++ b/src/backend/commands/policy.c
@@ -22,19 +22,62 @@
 #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_auth_members.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_database.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_pltemplate.h"
 #include "catalog/pg_policy.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_range.h"
 #include "catalog/pg_replication_origin.h"
+#include "catalog/pg_rewrite.h"
+#include "catalog/pg_seclabel.h"
 #include "catalog/pg_shdepend.h"
 #include "catalog/pg_shdescription.h"
 #include "catalog/pg_shseclabel.h"
+#include "catalog/pg_statistic.h"
 #include "catalog/pg_tablespace.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"
@@ -54,6 +97,7 @@
 #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 65536
@@ -514,6 +558,26 @@ get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *bu
 
 	switch (relationid)
 	{
+			/*
+			 * Following catalog tables data is accessible to all roles. So
+			 * they doesn't need any specific RLS policies on them.
+			 */
+		case AccessMethodRelationId:
+		case AccessMethodOperatorRelationId:
+		case AccessMethodProcedureRelationId:
+		case AggregateRelationId:
+			policy_exist = false;
+			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);
+			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);
+			break;
 		case AuthIdRelationId:
 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
 					" (pg_has_role(oid, 'any'))",
@@ -524,6 +588,22 @@ get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *bu
 			  " (pg_has_role(roleid, 'any') AND pg_has_role(member, 'any'))",
 				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
 			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);
+			break;
+		case CollationRelationId:
+			policy_exist = false;
+			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);
+			break;
+		case ConversionRelationId:
+			policy_exist = false;
+			break;
 		case DatabaseRelationId:
 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
 					" ((oid < 16384) OR has_database_privilege(oid,'any'))",
@@ -534,12 +614,175 @@ get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *bu
 					" (pg_has_role(setrole, 'any') AND has_database_privilege(setdatabase,'any'))",
 				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
 			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);
+			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);
+			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);
+			break;
+		case EnumRelationId:
+		case EventTriggerRelationId:
+		case ExtensionRelationId:
+			policy_exist = false;
+			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);
+			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);
+			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);
+			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);
+			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);
+			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);
+			break;
+		case LargeObjectRelationId:
+		case LargeObjectMetadataRelationId:
+			policy_exist = false;
+			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);
+			break;
+		case OperatorClassRelationId:
+		case OperatorRelationId:
+		case OperatorFamilyRelationId:
 		case PLTemplateRelationId:
 			policy_exist = false;
 			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);
+			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);
+			break;
+		case RangeRelationId:
+			policy_exist = false;
+			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);
+			break;
 		case ReplicationOriginRelationId:
+		case RewriteRelationId:
 			policy_exist = false;
 			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);
+			break;
 		case SharedDependRelationId:
 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
 					" ((classid = (select oid from pg_class where relname = 'pg_database') AND has_database_privilege(objid, 'any'))"
@@ -561,11 +804,42 @@ get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *bu
 					" 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);
 			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);
+			break;
 		case TableSpaceRelationId:
 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
 				  " ((oid < 16384) OR has_tablespace_privilege(oid, 'any'))",
 				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
 			break;
+		case TransformRelationId:
+			policy_exist = false;
+			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);
+			break;
+		case TSConfigRelationId:
+		case TSConfigMapRelationId:
+		case TSDictionaryRelationId:
+		case TSParserRelationId:
+		case TSTemplateRelationId:
+			policy_exist = false;
+			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);
+			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);
+			break;
 		default:
 			policy_exist = false;
 			break;
@@ -575,6 +849,163 @@ get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *bu
 }
 
 /*
+ * 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)
+		{
+			HeapTuple	cache_tuple;
+			Form_pg_class pg_class_tuple;
+			bool		policy_exist;
+
+			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));
+
+			pg_class_tuple = (Form_pg_class) GETSTRUCT(cache_tuple);
+			policy_exist = get_catalog_policy_string(HeapTupleGetOid(tuple), pg_class_tuple, buf);
+			if (!policy_exist)
+			{
+				heap_freetuple(cache_tuple);
+				continue;
+			}
+
+			pg_class_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)
+		{
+			HeapTuple	cache_tuple;
+
+			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));
+
+			pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
+			if (!pg_class_tuple->relrowsecurity)
+				continue;
+
+			pg_class_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();
+}
+
+/*
  * RemoveRoleFromObjectPolicy -
  *	 remove a role from a policy by its OID.  If the role is not a member of
  *	 the policy then an error is raised.  False is returned to indicate that
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b307b48..5ceb1e5 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -8876,6 +8876,7 @@ createdb_opt_name:
 			| OWNER							{ $$ = pstrdup($1); }
 			| TABLESPACE					{ $$ = pstrdup($1); }
 			| TEMPLATE						{ $$ = pstrdup($1); }
+			| CATALOG_P SECURITY				{ $$ = pstrdup("catalog_security"); }
 		;
 
 /*
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index a07350d..7ecddc8 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -17,11 +17,19 @@
 #include <ctype.h>
 
 #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_type.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,6 +39,7 @@
 #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"
@@ -2032,10 +2041,10 @@ convert_table_priv_string(text *priv_type_text)
 		{"TRIGGER", ACL_TRIGGER},
 		{"TRIGGER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
 		{"ANY", ACL_SELECT | ACL_GRANT_OPTION_FOR(ACL_SELECT) |
-				ACL_INSERT | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
-				ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_DELETE | ACL_GRANT_OPTION_FOR(ACL_DELETE) |
-				ACL_TRUNCATE | ACL_GRANT_OPTION_FOR(ACL_TRUNCATE) | ACL_REFERENCES |
-				ACL_GRANT_OPTION_FOR(ACL_REFERENCES) | ACL_TRIGGER | ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
+			ACL_INSERT | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
+			ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_DELETE | ACL_GRANT_OPTION_FOR(ACL_DELETE) |
+			ACL_TRUNCATE | ACL_GRANT_OPTION_FOR(ACL_TRUNCATE) | ACL_REFERENCES |
+		ACL_GRANT_OPTION_FOR(ACL_REFERENCES) | ACL_TRIGGER | ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
 		{"RULE", 0},			/* ignore old RULE privileges */
 		{"RULE WITH GRANT OPTION", 0},
 		{NULL, 0}
@@ -2865,8 +2874,8 @@ convert_column_priv_string(text *priv_type_text)
 		{"REFERENCES", ACL_REFERENCES},
 		{"REFERENCES WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
 		{"ANY", ACL_SELECT | ACL_GRANT_OPTION_FOR(ACL_SELECT) | ACL_INSERT |
-				ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
-				ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_REFERENCES | ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
+			ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
+		ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_REFERENCES | ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
 		{NULL, 0}
 	};
 
@@ -3065,8 +3074,8 @@ convert_database_priv_string(text *priv_type_text)
 		{"CONNECT", ACL_CONNECT},
 		{"CONNECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
 		{"ANY", ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE) |
-									ACL_CREATE_TEMP | ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CREATE_TEMP |
-									ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CONNECT | ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
+			ACL_CREATE_TEMP | ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CREATE_TEMP |
+		ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CONNECT | ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
 		{NULL, 0}
 	};
 
@@ -5267,3 +5276,293 @@ get_rolespec_name(const Node *node)
 
 	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));
+}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index fc5b9d9..44756d6 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -2078,6 +2078,8 @@ RelationClearRelation(Relation relation, bool rebuild)
 	 */
 	if (relation->rd_isnailed)
 	{
+		HeapTuple	pg_class_tuple;
+
 		RelationInitPhysicalAddr(relation);
 
 		if (relation->rd_rel->relkind == RELKIND_INDEX)
@@ -2086,6 +2088,28 @@ RelationClearRelation(Relation relation, bool rebuild)
 			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;
 	}
 
diff --git a/src/include/catalog/pg_database.h b/src/include/catalog/pg_database.h
index 6ae1b40..1816d8b 100644
--- a/src/include/catalog/pg_database.h
+++ b/src/include/catalog/pg_database.h
@@ -43,7 +43,7 @@ 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 */
-
+	bool		datcatalogsecurity; /* catalog security is enabled? */
 #ifdef CATALOG_VARLEN			/* variable-length fields start here */
 	aclitem		datacl[1];		/* access permissions */
 #endif
@@ -60,22 +60,23 @@ 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
+#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 _null_));
+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
 
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 9250545..60dd7b3 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3646,6 +3646,24 @@ DESCR("current user privilege on role by role name");
 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 = 3321 (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 = 3322 (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 = 3323 (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 = 3324 (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 = 3325 (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 = 3326 (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_ ));
diff --git a/src/include/commands/policy.h b/src/include/commands/policy.h
index dbf7824..453d335 100644
--- a/src/include/commands/policy.h
+++ b/src/include/commands/policy.h
@@ -35,4 +35,7 @@ extern ObjectAddress rename_policy(RenameStmt *stmt);
 
 extern bool relation_has_policies(Relation rel);
 
+extern void CreateCatalogPolicy(void);
+extern void RemoveCatalogPolicy(void);
+
 #endif   /* POLICY_H */
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index b35d206..4cf1d8b 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -106,6 +106,12 @@ extern Datum pg_has_role_id_name(PG_FUNCTION_ARGS);
 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);
diff --git a/src/test/regress/expected/multitenancy.out b/src/test/regress/expected/multitenancy.out
new file mode 100644
index 0000000..cbdb1fa
--- /dev/null
+++ b/src/test/regress/expected/multitenancy.out
@@ -0,0 +1,644 @@
+-- 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;
+alter schema tenancy_user1 owner to tenancy_user1;
+create schema tenancy_user2;
+alter schema tenancy_user2 owner 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;
+ALTER SERVER tenancy_server OWNER 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 from pg_namespace where nspname = 'tenancy_user1';
+    nspname    
+---------------
+ tenancy_user1
+(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_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 from pg_namespace where nspname = 'tenancy_user1';
+ nspname 
+---------
+(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_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
+drop schema tenancy_user1;
+RESET ROLE;
+SET SESSION ROLE tenancy_user2;
+DROP FUNCTION tenancy_user2_tenancytestfunc(text);
+DROP FOREIGN TABLE tenancy_user2_ft1;
+DROP LANGUAGE tenancy_lang1;
+drop schema tenancy_user2;
+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 role tenancy_user1;
+drop role tenancy_user2;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index b1bc7c7..04939da 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -60,7 +60,7 @@ 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
+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 *
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index ade9ef1..8832ef1 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -160,4 +160,5 @@ test: largeobject
 test: with
 test: xml
 test: event_trigger
+test: multitenancy
 test: stats
diff --git a/src/test/regress/sql/multitenancy.sql b/src/test/regress/sql/multitenancy.sql
new file mode 100644
index 0000000..0b774a5
--- /dev/null
+++ b/src/test/regress/sql/multitenancy.sql
@@ -0,0 +1,297 @@
+-- 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;
+alter schema tenancy_user1 owner to tenancy_user1;
+
+create schema tenancy_user2;
+alter schema tenancy_user2 owner 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;
+ALTER SERVER tenancy_server OWNER 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 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_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 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_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;
+drop schema tenancy_user1;
+
+RESET ROLE;
+
+SET SESSION ROLE tenancy_user2;
+
+DROP FUNCTION tenancy_user2_tenancytestfunc(text);
+DROP FOREIGN TABLE tenancy_user2_ft1;
+DROP LANGUAGE tenancy_lang1;
+drop schema tenancy_user2;
+
+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 role tenancy_user1;
+drop role tenancy_user2;
3_shared_catalog_tenancy_v4.patchapplication/octet-stream; name=3_shared_catalog_tenancy_v4.patchDownload
diff --git a/doc/src/sgml/ref/initdb.sgml b/doc/src/sgml/ref/initdb.sgml
index 4e339ec..ac460e9 100644
--- a/doc/src/sgml/ref/initdb.sgml
+++ b/doc/src/sgml/ref/initdb.sgml
@@ -152,6 +152,19 @@ PostgreSQL documentation
      </varlistentry>
 
      <varlistentry>
+      <term><option>-C</option></term>
+      <term><option>--shared-catalog-security</option></term>
+      <listitem>
+       <para>
+        This option enables the shared catalog tables security, by adding
+        row level security policies on all eligible shared catalog tables.
+        With this option, multi-tenancy in PostgreSQL is supported at 
+        database level.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><option>-D <replaceable class="parameter">directory</replaceable></option></term>
       <term><option>--pgdata=<replaceable class="parameter">directory</replaceable></option></term>
       <listitem>
diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c
index bb735ac..820261e 100644
--- a/src/backend/commands/policy.c
+++ b/src/backend/commands/policy.c
@@ -23,7 +23,16 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_authid.h"
+#include "catalog/pg_auth_members.h"
+#include "catalog/pg_database.h"
+#include "catalog/pg_db_role_setting.h"
+#include "catalog/pg_pltemplate.h"
 #include "catalog/pg_policy.h"
+#include "catalog/pg_replication_origin.h"
+#include "catalog/pg_shdepend.h"
+#include "catalog/pg_shdescription.h"
+#include "catalog/pg_shseclabel.h"
+#include "catalog/pg_tablespace.h"
 #include "catalog/pg_type.h"
 #include "commands/policy.h"
 #include "miscadmin.h"
@@ -36,6 +45,7 @@
 #include "rewrite/rewriteManip.h"
 #include "rewrite/rowsecurity.h"
 #include "storage/lock.h"
+#include "tcop/tcopprot.h"
 #include "utils/acl.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
@@ -46,11 +56,18 @@
 #include "utils/rel.h"
 #include "utils/syscache.h"
 
+#define CATALOG_POLICY_STRING_SIZE 65536
+
 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 get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *buf);
+
+/* variable to identify whether catalog row security is built is in progress or not? */
+bool		CatalogRowSecurityBuiltInProgress = false;
+
 /*
  * Callback to RangeVarGetRelidExtended().
  *
@@ -195,6 +212,16 @@ RelationBuildRowSecurity(Relation relation)
 	RowSecurityDesc *volatile rsdesc = NULL;
 
 	/*
+	 * Build the row security descriptor of a relation, once all the critical
+	 * relations are built.
+	 */
+	if (!criticalRelcachesBuilt || !criticalSharedRelcachesBuilt)
+		return;
+
+	if (IsSystemRelation(relation) && CatalogRowSecurityBuiltInProgress)
+		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.
@@ -211,124 +238,194 @@ RelationBuildRowSecurity(Relation relation)
 	 */
 	PG_TRY();
 	{
-		Relation	catalog;
-		ScanKeyData skey;
-		SysScanDesc sscan;
-		HeapTuple	tuple;
-
 		rsdesc = MemoryContextAllocZero(rscxt, sizeof(RowSecurityDesc));
 		rsdesc->rscxt = rscxt;
 
-		catalog = heap_open(PolicyRelationId, AccessShareLock);
-
-		ScanKeyInit(&skey,
-					Anum_pg_policy_polrelid,
-					BTEqualStrategyNumber, F_OIDEQ,
-					ObjectIdGetDatum(RelationGetRelid(relation)));
-
-		sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
-								   NULL, 1, &skey);
-
-		/*
-		 * Loop through the row level security policies for this relation, if
-		 * any.
-		 */
-		while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
+		if (IsSystemRelation(relation))
 		{
-			Datum		value_datum;
-			char		cmd_value;
-			Datum		roles_datum;
-			char	   *qual_value;
-			Expr	   *qual_expr;
-			char	   *with_check_value;
-			Expr	   *with_check_qual;
-			char	   *policy_name_value;
-			bool		isnull;
 			RowSecurityPolicy *policy;
+			Datum	   *role_oids;
+			int			nitems = 0;
+			ArrayType  *role_ids;
+			Node	   *qual_expr;
+			List	   *raw_parsetree_list;
+			ListCell   *list_item;
+			ParseState *qual_pstate;
+			RangeTblEntry *rte;
+			CreatePolicyStmt *stmt;
+			char		buf[CATALOG_POLICY_STRING_SIZE];
 
-			/*
-			 * Note: all the pass-by-reference data we collect here is either
-			 * still stored in the tuple, or constructed in the caller's
-			 * short-lived memory context.  We must copy it into rscxt
-			 * explicitly below.
-			 */
+			CatalogRowSecurityBuiltInProgress = true;
+			get_catalog_policy_string(relation->rd_id, relation->rd_rel, buf);
 
-			/* Get policy command */
-			value_datum = heap_getattr(tuple, Anum_pg_policy_polcmd,
-									   RelationGetDescr(catalog), &isnull);
-			Assert(!isnull);
-			cmd_value = DatumGetChar(value_datum);
-
-			/* Get policy name */
-			value_datum = heap_getattr(tuple, Anum_pg_policy_polname,
-									   RelationGetDescr(catalog), &isnull);
-			Assert(!isnull);
-			policy_name_value = NameStr(*(DatumGetName(value_datum)));
-
-			/* Get policy roles */
-			roles_datum = heap_getattr(tuple, Anum_pg_policy_polroles,
-									   RelationGetDescr(catalog), &isnull);
-			/* shouldn't be null, but initdb doesn't mark it so, so check */
-			if (isnull)
-				elog(ERROR, "unexpected null value in pg_policy.polroles");
-
-			/* Get policy qual */
-			value_datum = heap_getattr(tuple, Anum_pg_policy_polqual,
-									   RelationGetDescr(catalog), &isnull);
-			if (!isnull)
-			{
-				qual_value = TextDatumGetCString(value_datum);
-				qual_expr = (Expr *) stringToNode(qual_value);
-			}
-			else
-				qual_expr = NULL;
+			role_oids = policy_role_list_to_array(NULL, &nitems);
+			role_ids = construct_array(role_oids, nitems, OIDOID,
+									   sizeof(Oid), true, 'i');
 
-			/* Get WITH CHECK qual */
-			value_datum = heap_getattr(tuple, Anum_pg_policy_polwithcheck,
-									   RelationGetDescr(catalog), &isnull);
-			if (!isnull)
+			raw_parsetree_list = pg_parse_query(buf);
+			Assert(list_length(raw_parsetree_list) == 1);
+
+			foreach(list_item, raw_parsetree_list)
 			{
-				with_check_value = TextDatumGetCString(value_datum);
-				with_check_qual = (Expr *) stringToNode(with_check_value);
+				Node	   *parsetree;
+
+				parsetree = (Node *) lfirst(list_item);
+				stmt = (CreatePolicyStmt *) parsetree;
 			}
-			else
-				with_check_qual = NULL;
+
+			qual_pstate = make_parsestate(NULL);
+			rte = addRangeTableEntryForRelation(qual_pstate, relation,
+												NULL, false, false);
+
+			addRTEtoQuery(qual_pstate, rte, false, true, true);
+			qual_expr = transformWhereClause(qual_pstate,
+											 stmt->qual,
+											 EXPR_KIND_POLICY,
+											 "POLICY");
+
+			/* Fix up collation information */
+			assign_expr_collations(qual_pstate, qual_expr);
 
 			/* Now copy everything into the cache context */
 			MemoryContextSwitchTo(rscxt);
 
 			policy = palloc0(sizeof(RowSecurityPolicy));
-			policy->policy_name = pstrdup(policy_name_value);
-			policy->polcmd = cmd_value;
-			policy->roles = DatumGetArrayTypePCopy(roles_datum);
+			policy->policy_name = pstrdup(stmt->policy_name);
+			policy->polcmd = ACL_SELECT_CHR;
+			policy->roles = DatumGetArrayTypePCopy(role_ids);
 			policy->qual = copyObject(qual_expr);
-			policy->with_check_qual = copyObject(with_check_qual);
-			policy->hassublinks = checkExprHasSubLink((Node *) qual_expr) ||
-				checkExprHasSubLink((Node *) with_check_qual);
+			policy->with_check_qual = NULL;
+			policy->hassublinks = checkExprHasSubLink((Node *) qual_expr);
 
 			rsdesc->policies = lcons(policy, rsdesc->policies);
 
 			MemoryContextSwitchTo(oldcxt);
-
-			/* clean up some (not all) of the junk ... */
-			if (qual_expr != NULL)
-				pfree(qual_expr);
-			if (with_check_qual != NULL)
-				pfree(with_check_qual);
 		}
+		else
+		{
+			Relation	catalog;
+			ScanKeyData skey;
+			SysScanDesc sscan;
+			HeapTuple	tuple;
+
+			catalog = heap_open(PolicyRelationId, AccessShareLock);
 
-		systable_endscan(sscan);
-		heap_close(catalog, AccessShareLock);
+			ScanKeyInit(&skey,
+						Anum_pg_policy_polrelid,
+						BTEqualStrategyNumber, F_OIDEQ,
+						ObjectIdGetDatum(RelationGetRelid(relation)));
+
+			sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
+									   NULL, 1, &skey);
+
+			/*
+			 * Loop through the row level security policies for this relation,
+			 * if any.
+			 */
+			while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
+			{
+				Datum		value_datum;
+				char		cmd_value;
+				Datum		roles_datum;
+				char	   *qual_value;
+				Expr	   *qual_expr;
+				char	   *with_check_value;
+				Expr	   *with_check_qual;
+				char	   *policy_name_value;
+				bool		isnull;
+				RowSecurityPolicy *policy;
+
+				/*
+				 * Note: all the pass-by-reference data we collect here is
+				 * either still stored in the tuple, or constructed in the
+				 * caller's short-lived memory context.  We must copy it into
+				 * rscxt explicitly below.
+				 */
+
+				/* Get policy command */
+				value_datum = heap_getattr(tuple, Anum_pg_policy_polcmd,
+										 RelationGetDescr(catalog), &isnull);
+				Assert(!isnull);
+				cmd_value = DatumGetChar(value_datum);
+
+				/* Get policy name */
+				value_datum = heap_getattr(tuple, Anum_pg_policy_polname,
+										 RelationGetDescr(catalog), &isnull);
+				Assert(!isnull);
+				policy_name_value = NameStr(*(DatumGetName(value_datum)));
+
+				/* Get policy roles */
+				roles_datum = heap_getattr(tuple, Anum_pg_policy_polroles,
+										 RelationGetDescr(catalog), &isnull);
+				/* shouldn't be null, but initdb doesn't mark it so, so check */
+				if (isnull)
+					elog(ERROR, "unexpected null value in pg_policy.polroles");
+
+				/* Get policy qual */
+				value_datum = heap_getattr(tuple, Anum_pg_policy_polqual,
+										 RelationGetDescr(catalog), &isnull);
+				if (!isnull)
+				{
+					qual_value = TextDatumGetCString(value_datum);
+					qual_expr = (Expr *) stringToNode(qual_value);
+				}
+				else
+					qual_expr = NULL;
+
+				/* Get WITH CHECK qual */
+				value_datum = heap_getattr(tuple, Anum_pg_policy_polwithcheck,
+										 RelationGetDescr(catalog), &isnull);
+				if (!isnull)
+				{
+					with_check_value = TextDatumGetCString(value_datum);
+					with_check_qual = (Expr *) stringToNode(with_check_value);
+				}
+				else
+					with_check_qual = NULL;
+
+				/* Now copy everything into the cache context */
+				MemoryContextSwitchTo(rscxt);
+
+				policy = palloc0(sizeof(RowSecurityPolicy));
+				policy->policy_name = pstrdup(policy_name_value);
+				policy->polcmd = cmd_value;
+				policy->roles = DatumGetArrayTypePCopy(roles_datum);
+				policy->qual = copyObject(qual_expr);
+				policy->with_check_qual = copyObject(with_check_qual);
+				policy->hassublinks = checkExprHasSubLink((Node *) qual_expr) ||
+					checkExprHasSubLink((Node *) with_check_qual);
+
+				rsdesc->policies = lcons(policy, rsdesc->policies);
+
+				MemoryContextSwitchTo(oldcxt);
+
+				/* clean up some (not all) of the junk ... */
+				if (qual_expr != NULL)
+					pfree(qual_expr);
+				if (with_check_qual != NULL)
+					pfree(with_check_qual);
+			}
+
+			systable_endscan(sscan);
+			heap_close(catalog, AccessShareLock);
+		}
 	}
 	PG_CATCH();
 	{
 		/* Delete rscxt, first making sure it isn't active */
 		MemoryContextSwitchTo(oldcxt);
 		MemoryContextDelete(rscxt);
+
+		if (IsSystemRelation(relation))
+			CatalogRowSecurityBuiltInProgress = false;
+
 		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
+	if (IsSystemRelation(relation))
+		CatalogRowSecurityBuiltInProgress = false;
+
 	/* Success --- attach the policy descriptor to the relcache entry */
 	relation->rd_rsdesc = rsdesc;
 }
@@ -410,6 +507,73 @@ RemovePolicyById(Oid policy_id)
 	heap_close(pg_policy_rel, RowExclusiveLock);
 }
 
+static bool
+get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *buf)
+{
+	bool		policy_exist = true;
+
+	switch (relationid)
+	{
+		case AuthIdRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+					" (pg_has_role(oid, 'any'))",
+				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+			break;
+		case AuthMemRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+			  " (pg_has_role(roleid, 'any') AND pg_has_role(member, 'any'))",
+				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+			break;
+		case DatabaseRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+					" ((oid < 16384) OR has_database_privilege(oid,'any'))",
+				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+			break;
+		case DbRoleSettingRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+					" (pg_has_role(setrole, 'any') AND has_database_privilege(setdatabase,'any'))",
+				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+			break;
+		case PLTemplateRelationId:
+			policy_exist = false;
+			break;
+		case ReplicationOriginRelationId:
+			policy_exist = false;
+			break;
+		case SharedDependRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+					" ((classid = (select oid from pg_class where relname = 'pg_database') AND has_database_privilege(objid, 'any'))"
+					" OR (classid = (select oid from pg_class where relname = 'pg_authid') AND pg_has_role(objid, 'any'))"
+					" OR (classid = (select oid from pg_class where relname = 'pg_tablespace') AND has_tablespace_privilege(objid, 'any')))",
+				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+			break;
+		case SharedDescriptionRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+					" ((classoid = (select oid from pg_class where relname = 'pg_database') AND has_database_privilege(objoid, 'any'))"
+					" OR (classoid = (select oid from pg_class where relname = 'pg_authid') AND pg_has_role(objoid, 'any'))"
+					" 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);
+			break;
+		case SharedSecLabelRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+					" ((classoid = (select oid from pg_class where relname = 'pg_database') AND has_database_privilege(objoid, 'any'))"
+					" OR (classoid = (select oid from pg_class where relname = 'pg_authid') AND pg_has_role(objoid, 'any'))"
+					" 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);
+			break;
+		case TableSpaceRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+				  " ((oid < 16384) OR has_tablespace_privilege(oid, 'any'))",
+				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+			break;
+		default:
+			policy_exist = false;
+			break;
+	}
+
+	return policy_exist;
+}
+
 /*
  * RemoveRoleFromObjectPolicy -
  *	 remove a role from a policy by its OID.  If the role is not a member of
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 0b4a334..5b00a8d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -3442,6 +3442,16 @@ ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode)
 	{
 		AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
 
+		/*
+		 * ALTER table on sytem catalog tables is possible only when user
+		 * specifies CATALOG SECURITY on system catalog tables. To avoid an
+		 * error in the AlterTableCreateToastTable function for system catalog
+		 * tables, the system catalog tables are ignored for the toast table
+		 * creation.
+		 */
+		if (!IsUnderPostmaster && IsSharedRelation(tab->relid))
+			continue;
+
 		if (tab->relkind == RELKIND_RELATION ||
 			tab->relkind == RELKIND_MATVIEW)
 			AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
diff --git a/src/backend/utils/misc/rls.c b/src/backend/utils/misc/rls.c
index c33f29e..4d6c88d 100644
--- a/src/backend/utils/misc/rls.c
+++ b/src/backend/utils/misc/rls.c
@@ -58,10 +58,6 @@ check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
 	bool		relforcerowsecurity;
 	bool		amowner;
 
-	/* Nothing to do for built-in relations */
-	if (relid < (Oid) FirstNormalObjectId)
-		return RLS_NONE;
-
 	/* Fetch relation's relrowsecurity and relforcerowsecurity flags */
 	tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
 	if (!HeapTupleIsValid(tuple))
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 35e39ce..13a22ee 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -136,6 +136,7 @@ static bool do_sync = true;
 static bool sync_only = false;
 static bool show_setting = false;
 static bool data_checksums = false;
+static bool shared_catalog_security = false;
 static char *xlog_dir = "";
 
 
@@ -190,6 +191,7 @@ static char *authwarning = NULL;
  */
 static const char *boot_options = "-F";
 static const char *backend_options = "--single -F -O -j -c search_path=pg_catalog -c exit_on_error=true";
+static const char *catalog_security_options = "--single -F -O -j -c search_path=pg_catalog -c exit_on_error=true -c allow_system_table_mods=true";
 
 static const char *const subdirs[] = {
 	"global",
@@ -259,6 +261,7 @@ static void setup_dictionary(FILE *cmdfd);
 static void setup_privileges(FILE *cmdfd);
 static void set_info_version(void);
 static void setup_schema(FILE *cmdfd);
+static void setup_shared_catalog_security(FILE *cmdfd);
 static void load_plpgsql(FILE *cmdfd);
 static void vacuum_db(FILE *cmdfd);
 static void make_template0(FILE *cmdfd);
@@ -2082,6 +2085,59 @@ setup_schema(FILE *cmdfd)
 }
 
 /*
+ * setup shared catalog security by defining policies
+ */
+static void
+setup_shared_catalog_security(FILE *cmdfd)
+{
+	const char **line;
+	static const char *pg_shared_catalog_security_setup[] = {
+		/* AuthMemRelationId */
+		"alter table pg_auth_members enable row level security;\n",
+
+		/* AuthIdRelationId */
+		"alter table pg_authid enable row level security;\n",
+
+		/* DatabaseRelationId */
+		"alter table pg_database enable row level security;\n",
+
+		/* DbRoleSettingRelationId */
+		"alter table pg_database enable row level security;\n",
+
+		/* PLTemplateRelationId */
+
+		/*
+		 * Currently there is no policy needed for this table, so leave it as
+		 * it is.
+		 */
+
+		/* ReplicationOriginRelationId */
+
+		/*
+		 * Currently there is no policy needed for this table, so leave it as
+		 * it is.
+		 */
+
+		/* SharedDependRelationId */
+		"alter table pg_shdepend enable row level security;\n",
+
+		/* SharedDescriptionRelationId */
+		"alter table pg_shdescription enable row level security;\n",
+
+		/* SharedSecLabelRelationId */
+		"alter table pg_shseclabel enable row level security;\n",
+
+		/* TableSpaceRelationId */
+		"alter table pg_tablespace enable row level security;\n",
+
+		NULL
+	};
+
+	for (line = pg_shared_catalog_security_setup; *line != NULL; line++)
+		PG_CMD_PUTS(*line);
+}
+
+/*
  * load PL/pgsql server-side language
  */
 static void
@@ -2536,6 +2592,8 @@ usage(const char *progname)
 	printf(_("\nLess commonly used options:\n"));
 	printf(_("  -d, --debug               generate lots of debugging output\n"));
 	printf(_("  -k, --data-checksums      use data page checksums\n"));
+	printf(_("  -C, --shared-catalog-security\n"
+			 "						      use shared catalog security\n"));
 	printf(_("  -L DIRECTORY              where to find the input files\n"));
 	printf(_("  -n, --noclean             do not clean up after errors\n"));
 	printf(_("  -N, --nosync              do not wait for changes to be written safely to disk\n"));
@@ -3073,6 +3131,10 @@ initialize_data_directory(void)
 {
 	PG_CMD_DECL;
 	int			i;
+	const char *options = backend_options;
+
+	if (shared_catalog_security)
+		options = catalog_security_options;
 
 	setup_signals();
 
@@ -3121,7 +3183,7 @@ initialize_data_directory(void)
 
 	snprintf(cmd, sizeof(cmd),
 			 "\"%s\" %s template1 >%s",
-			 backend_exec, backend_options,
+			 backend_exec, options,
 			 DEVNULL);
 
 	PG_CMD_OPEN;
@@ -3146,6 +3208,9 @@ initialize_data_directory(void)
 
 	setup_schema(cmdfd);
 
+	if (shared_catalog_security)
+		setup_shared_catalog_security(cmdfd);
+
 	load_plpgsql(cmdfd);
 
 	vacuum_db(cmdfd);
@@ -3190,6 +3255,7 @@ main(int argc, char *argv[])
 		{"sync-only", no_argument, NULL, 'S'},
 		{"xlogdir", required_argument, NULL, 'X'},
 		{"data-checksums", no_argument, NULL, 'k'},
+		{"shared-catalog-security", no_argument, NULL, 'C'},
 		{NULL, 0, NULL, 0}
 	};
 
@@ -3230,7 +3296,7 @@ main(int argc, char *argv[])
 
 	/* process command-line options */
 
-	while ((c = getopt_long(argc, argv, "dD:E:kL:nNU:WA:sST:X:", long_options, &option_index)) != -1)
+	while ((c = getopt_long(argc, argv, "dD:E:kCL:nNU:WA:sST:X:", long_options, &option_index)) != -1)
 	{
 		switch (c)
 		{
@@ -3282,6 +3348,9 @@ main(int argc, char *argv[])
 			case 'k':
 				data_checksums = true;
 				break;
+			case 'C':
+				shared_catalog_security = true;
+				break;
 			case 'L':
 				share_path = pg_strdup(optarg);
 				break;
#25Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Haribabu Kommi (#24)
Re: Multi-tenancy with RLS

On 2016/01/06 10:17, Haribabu Kommi wrote:

On Mon, Jan 4, 2016 at 10:43 PM, Haribabu Kommi

Thanks for the test. Yes, the issue happens at backend startup itself.
I will give a try by separating the initialization of security
policies after init phase 3.

Here I attached updated patches with the fix of infinite recursion in
RelationBuildRowSecurity function by checking with a variable that
whether the build row security is already in progress for a system
relation or not. If it is already in progress for a relation, then it doesn't
build the row security description for this relation.

Thanks for updating the patch.

Patch 4_database_catalog_tenancy_v5 fails to apply:

patching file src/backend/commands/policy.c
Hunk #3 succeeded at 112 with fuzz 2 (offset 3 lines).
Hunk #4 succeeded at 269 with fuzz 1 (offset 13 lines).
Hunk #5 succeeded at 298 (offset 13 lines).
Hunk #6 succeeded at 365 (offset 12 lines).
Hunk #7 FAILED at 466.
Hunk #8 succeeded at 577 (offset 22 lines).
Hunk #9 succeeded at 607 with fuzz 2 (offset 22 lines).
Hunk #10 succeeded at 633 with fuzz 2 (offset 22 lines).
Hunk #11 FAILED at 801.
Hunk #12 FAILED at 813.
3 out of 12 hunks FAILED -- saving rejects to file
src/backend/commands/policy.c.rej

Thanks,
Amit

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#26Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Amit Langote (#25)
4 attachment(s)
Re: Multi-tenancy with RLS

On Wed, Jan 6, 2016 at 1:43 PM, Amit Langote
<Langote_Amit_f8@lab.ntt.co.jp> wrote:

On 2016/01/06 10:17, Haribabu Kommi wrote:

On Mon, Jan 4, 2016 at 10:43 PM, Haribabu Kommi

Thanks for the test. Yes, the issue happens at backend startup itself.
I will give a try by separating the initialization of security
policies after init phase 3.

Here I attached updated patches with the fix of infinite recursion in
RelationBuildRowSecurity function by checking with a variable that
whether the build row security is already in progress for a system
relation or not. If it is already in progress for a relation, then it doesn't
build the row security description for this relation.

Thanks for updating the patch.

Patch 4_database_catalog_tenancy_v5 fails to apply:

patching file src/backend/commands/policy.c
Hunk #3 succeeded at 112 with fuzz 2 (offset 3 lines).
Hunk #4 succeeded at 269 with fuzz 1 (offset 13 lines).
Hunk #5 succeeded at 298 (offset 13 lines).
Hunk #6 succeeded at 365 (offset 12 lines).
Hunk #7 FAILED at 466.
Hunk #8 succeeded at 577 (offset 22 lines).
Hunk #9 succeeded at 607 with fuzz 2 (offset 22 lines).
Hunk #10 succeeded at 633 with fuzz 2 (offset 22 lines).
Hunk #11 FAILED at 801.
Hunk #12 FAILED at 813.
3 out of 12 hunks FAILED -- saving rejects to file
src/backend/commands/policy.c.rej

May be you missed to apply the 3_shared_catalog_tenancy_v4 path,
because 4_database_catalog_tenancy_v5 patch depends on it.

Here I attached all the patches for your convenience, I am able to
apply all patches in the order without any problem.

Regards,
Hari Babu
Fujitsu Australia

Attachments:

4_database_catalog_tenancy_v5.patchapplication/octet-stream; name=4_database_catalog_tenancy_v5.patchDownload
diff --git a/doc/src/sgml/ref/alter_database.sgml b/doc/src/sgml/ref/alter_database.sgml
index cfc28cf..adc3326 100644
--- a/doc/src/sgml/ref/alter_database.sgml
+++ b/doc/src/sgml/ref/alter_database.sgml
@@ -28,6 +28,7 @@ ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> [ [ WITH ] <rep
     ALLOW_CONNECTIONS <replaceable class="PARAMETER">allowconn</replaceable>
     CONNECTION LIMIT <replaceable class="PARAMETER">connlimit</replaceable>
     IS_TEMPLATE <replaceable class="PARAMETER">istemplate</replaceable>
+    CATALOG SECURITY <replaceable class="PARAMETER">catalog_security</replaceable>
 
 ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> RENAME TO <replaceable>new_name</replaceable>
 
@@ -139,6 +140,18 @@ ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> RESET ALL
         </para>
        </listitem>
       </varlistentry>
+      
+     <varlistentry>
+       <term><replaceable class="parameter">catalog_security</replaceable></term>
+       <listitem>
+        <para>
+         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.
+        </para>
+       </listitem>
+     </varlistentry>
 
    <varlistentry>
     <term><replaceable>new_name</replaceable></term>
@@ -203,6 +216,12 @@ ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> RESET ALL
    Role-specific settings override database-specific
    ones if there is a conflict.
   </para>
+  
+  <para>
+   The catalog_security option is not supported at <xref linkend="sql-createdatabase">
+   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.
+  </para>
  </refsect1>
 
  <refsect1>
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index c1c0223..f7b7141 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -42,6 +42,7 @@
 #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,6 +227,13 @@ createdb(const CreatedbStmt *stmt)
 					 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,6 +527,7 @@ createdb(const CreatedbStmt *stmt)
 	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,11 +1384,14 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 	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,6 +1433,15 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 						 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,6 +1478,8 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 					(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,12 +1499,19 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 				(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,6 +1523,17 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 				(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,6 +1556,11 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 		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);
diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c
index 820261e..6d902fd 100644
--- a/src/backend/commands/policy.c
+++ b/src/backend/commands/policy.c
@@ -22,19 +22,62 @@
 #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_auth_members.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_database.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_pltemplate.h"
 #include "catalog/pg_policy.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_range.h"
 #include "catalog/pg_replication_origin.h"
+#include "catalog/pg_rewrite.h"
+#include "catalog/pg_seclabel.h"
 #include "catalog/pg_shdepend.h"
 #include "catalog/pg_shdescription.h"
 #include "catalog/pg_shseclabel.h"
+#include "catalog/pg_statistic.h"
 #include "catalog/pg_tablespace.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"
@@ -54,6 +97,7 @@
 #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 65536
@@ -514,6 +558,26 @@ get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *bu
 
 	switch (relationid)
 	{
+			/*
+			 * Following catalog tables data is accessible to all roles. So
+			 * they doesn't need any specific RLS policies on them.
+			 */
+		case AccessMethodRelationId:
+		case AccessMethodOperatorRelationId:
+		case AccessMethodProcedureRelationId:
+		case AggregateRelationId:
+			policy_exist = false;
+			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);
+			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);
+			break;
 		case AuthIdRelationId:
 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
 					" (pg_has_role(oid, 'any'))",
@@ -524,6 +588,22 @@ get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *bu
 			  " (pg_has_role(roleid, 'any') AND pg_has_role(member, 'any'))",
 				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
 			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);
+			break;
+		case CollationRelationId:
+			policy_exist = false;
+			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);
+			break;
+		case ConversionRelationId:
+			policy_exist = false;
+			break;
 		case DatabaseRelationId:
 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
 					" ((oid < 16384) OR has_database_privilege(oid,'any'))",
@@ -534,12 +614,175 @@ get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *bu
 					" (pg_has_role(setrole, 'any') AND has_database_privilege(setdatabase,'any'))",
 				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
 			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);
+			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);
+			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);
+			break;
+		case EnumRelationId:
+		case EventTriggerRelationId:
+		case ExtensionRelationId:
+			policy_exist = false;
+			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);
+			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);
+			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);
+			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);
+			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);
+			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);
+			break;
+		case LargeObjectRelationId:
+		case LargeObjectMetadataRelationId:
+			policy_exist = false;
+			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);
+			break;
+		case OperatorClassRelationId:
+		case OperatorRelationId:
+		case OperatorFamilyRelationId:
 		case PLTemplateRelationId:
 			policy_exist = false;
 			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);
+			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);
+			break;
+		case RangeRelationId:
+			policy_exist = false;
+			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);
+			break;
 		case ReplicationOriginRelationId:
+		case RewriteRelationId:
 			policy_exist = false;
 			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);
+			break;
 		case SharedDependRelationId:
 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
 					" ((classid = (select oid from pg_class where relname = 'pg_database') AND has_database_privilege(objid, 'any'))"
@@ -561,11 +804,42 @@ get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *bu
 					" 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);
 			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);
+			break;
 		case TableSpaceRelationId:
 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
 				  " ((oid < 16384) OR has_tablespace_privilege(oid, 'any'))",
 				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
 			break;
+		case TransformRelationId:
+			policy_exist = false;
+			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);
+			break;
+		case TSConfigRelationId:
+		case TSConfigMapRelationId:
+		case TSDictionaryRelationId:
+		case TSParserRelationId:
+		case TSTemplateRelationId:
+			policy_exist = false;
+			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);
+			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);
+			break;
 		default:
 			policy_exist = false;
 			break;
@@ -575,6 +849,163 @@ get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *bu
 }
 
 /*
+ * 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)
+		{
+			HeapTuple	cache_tuple;
+			Form_pg_class pg_class_tuple;
+			bool		policy_exist;
+
+			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));
+
+			pg_class_tuple = (Form_pg_class) GETSTRUCT(cache_tuple);
+			policy_exist = get_catalog_policy_string(HeapTupleGetOid(tuple), pg_class_tuple, buf);
+			if (!policy_exist)
+			{
+				heap_freetuple(cache_tuple);
+				continue;
+			}
+
+			pg_class_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)
+		{
+			HeapTuple	cache_tuple;
+
+			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));
+
+			pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
+			if (!pg_class_tuple->relrowsecurity)
+				continue;
+
+			pg_class_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();
+}
+
+/*
  * RemoveRoleFromObjectPolicy -
  *	 remove a role from a policy by its OID.  If the role is not a member of
  *	 the policy then an error is raised.  False is returned to indicate that
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b307b48..5ceb1e5 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -8876,6 +8876,7 @@ createdb_opt_name:
 			| OWNER							{ $$ = pstrdup($1); }
 			| TABLESPACE					{ $$ = pstrdup($1); }
 			| TEMPLATE						{ $$ = pstrdup($1); }
+			| CATALOG_P SECURITY				{ $$ = pstrdup("catalog_security"); }
 		;
 
 /*
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index a07350d..7ecddc8 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -17,11 +17,19 @@
 #include <ctype.h>
 
 #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_type.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,6 +39,7 @@
 #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"
@@ -2032,10 +2041,10 @@ convert_table_priv_string(text *priv_type_text)
 		{"TRIGGER", ACL_TRIGGER},
 		{"TRIGGER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
 		{"ANY", ACL_SELECT | ACL_GRANT_OPTION_FOR(ACL_SELECT) |
-				ACL_INSERT | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
-				ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_DELETE | ACL_GRANT_OPTION_FOR(ACL_DELETE) |
-				ACL_TRUNCATE | ACL_GRANT_OPTION_FOR(ACL_TRUNCATE) | ACL_REFERENCES |
-				ACL_GRANT_OPTION_FOR(ACL_REFERENCES) | ACL_TRIGGER | ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
+			ACL_INSERT | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
+			ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_DELETE | ACL_GRANT_OPTION_FOR(ACL_DELETE) |
+			ACL_TRUNCATE | ACL_GRANT_OPTION_FOR(ACL_TRUNCATE) | ACL_REFERENCES |
+		ACL_GRANT_OPTION_FOR(ACL_REFERENCES) | ACL_TRIGGER | ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
 		{"RULE", 0},			/* ignore old RULE privileges */
 		{"RULE WITH GRANT OPTION", 0},
 		{NULL, 0}
@@ -2865,8 +2874,8 @@ convert_column_priv_string(text *priv_type_text)
 		{"REFERENCES", ACL_REFERENCES},
 		{"REFERENCES WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
 		{"ANY", ACL_SELECT | ACL_GRANT_OPTION_FOR(ACL_SELECT) | ACL_INSERT |
-				ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
-				ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_REFERENCES | ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
+			ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
+		ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_REFERENCES | ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
 		{NULL, 0}
 	};
 
@@ -3065,8 +3074,8 @@ convert_database_priv_string(text *priv_type_text)
 		{"CONNECT", ACL_CONNECT},
 		{"CONNECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
 		{"ANY", ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE) |
-									ACL_CREATE_TEMP | ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CREATE_TEMP |
-									ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CONNECT | ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
+			ACL_CREATE_TEMP | ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CREATE_TEMP |
+		ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CONNECT | ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
 		{NULL, 0}
 	};
 
@@ -5267,3 +5276,293 @@ get_rolespec_name(const Node *node)
 
 	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));
+}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index fc5b9d9..44756d6 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -2078,6 +2078,8 @@ RelationClearRelation(Relation relation, bool rebuild)
 	 */
 	if (relation->rd_isnailed)
 	{
+		HeapTuple	pg_class_tuple;
+
 		RelationInitPhysicalAddr(relation);
 
 		if (relation->rd_rel->relkind == RELKIND_INDEX)
@@ -2086,6 +2088,28 @@ RelationClearRelation(Relation relation, bool rebuild)
 			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;
 	}
 
diff --git a/src/include/catalog/pg_database.h b/src/include/catalog/pg_database.h
index 6ae1b40..1816d8b 100644
--- a/src/include/catalog/pg_database.h
+++ b/src/include/catalog/pg_database.h
@@ -43,7 +43,7 @@ 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 */
-
+	bool		datcatalogsecurity; /* catalog security is enabled? */
 #ifdef CATALOG_VARLEN			/* variable-length fields start here */
 	aclitem		datacl[1];		/* access permissions */
 #endif
@@ -60,22 +60,23 @@ 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
+#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 _null_));
+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
 
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 9250545..60dd7b3 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3646,6 +3646,24 @@ DESCR("current user privilege on role by role name");
 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 = 3321 (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 = 3322 (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 = 3323 (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 = 3324 (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 = 3325 (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 = 3326 (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_ ));
diff --git a/src/include/commands/policy.h b/src/include/commands/policy.h
index dbf7824..453d335 100644
--- a/src/include/commands/policy.h
+++ b/src/include/commands/policy.h
@@ -35,4 +35,7 @@ extern ObjectAddress rename_policy(RenameStmt *stmt);
 
 extern bool relation_has_policies(Relation rel);
 
+extern void CreateCatalogPolicy(void);
+extern void RemoveCatalogPolicy(void);
+
 #endif   /* POLICY_H */
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index b35d206..4cf1d8b 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -106,6 +106,12 @@ extern Datum pg_has_role_id_name(PG_FUNCTION_ARGS);
 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);
diff --git a/src/test/regress/expected/multitenancy.out b/src/test/regress/expected/multitenancy.out
new file mode 100644
index 0000000..cbdb1fa
--- /dev/null
+++ b/src/test/regress/expected/multitenancy.out
@@ -0,0 +1,644 @@
+-- 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;
+alter schema tenancy_user1 owner to tenancy_user1;
+create schema tenancy_user2;
+alter schema tenancy_user2 owner 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;
+ALTER SERVER tenancy_server OWNER 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 from pg_namespace where nspname = 'tenancy_user1';
+    nspname    
+---------------
+ tenancy_user1
+(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_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 from pg_namespace where nspname = 'tenancy_user1';
+ nspname 
+---------
+(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_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
+drop schema tenancy_user1;
+RESET ROLE;
+SET SESSION ROLE tenancy_user2;
+DROP FUNCTION tenancy_user2_tenancytestfunc(text);
+DROP FOREIGN TABLE tenancy_user2_ft1;
+DROP LANGUAGE tenancy_lang1;
+drop schema tenancy_user2;
+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 role tenancy_user1;
+drop role tenancy_user2;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index b1bc7c7..04939da 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -60,7 +60,7 @@ 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
+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 *
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index ade9ef1..8832ef1 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -160,4 +160,5 @@ test: largeobject
 test: with
 test: xml
 test: event_trigger
+test: multitenancy
 test: stats
diff --git a/src/test/regress/sql/multitenancy.sql b/src/test/regress/sql/multitenancy.sql
new file mode 100644
index 0000000..0b774a5
--- /dev/null
+++ b/src/test/regress/sql/multitenancy.sql
@@ -0,0 +1,297 @@
+-- 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;
+alter schema tenancy_user1 owner to tenancy_user1;
+
+create schema tenancy_user2;
+alter schema tenancy_user2 owner 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;
+ALTER SERVER tenancy_server OWNER 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 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_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 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_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;
+drop schema tenancy_user1;
+
+RESET ROLE;
+
+SET SESSION ROLE tenancy_user2;
+
+DROP FUNCTION tenancy_user2_tenancytestfunc(text);
+DROP FOREIGN TABLE tenancy_user2_ft1;
+DROP LANGUAGE tenancy_lang1;
+drop schema tenancy_user2;
+
+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 role tenancy_user1;
+drop role tenancy_user2;
1_any_privilege_option_v3.patchapplication/octet-stream; name=1_any_privilege_option_v3.patchDownload
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 8591aa3..b9549b3 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -15155,7 +15155,11 @@ SET search_path TO <replaceable>schema</> <optional>, <replaceable>schema</>, ..
    <xref linkend="functions-info-access-table"> lists functions that
    allow the user to query object access privileges programmatically.
    See <xref linkend="ddl-priv"> for more information about
-   privileges.
+   privileges. In case if user wants to find out any privilege check
+   on the corresponding object, instead of providing individual/all
+   privileges, user can specify "any" as an privilege option. The
+   Access Prvilege Inquiry Functions returns true if the user have 
+   any privileges on the object.
   </para>
 
    <table id="functions-info-access-table">
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 63fbce0..a07350d 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -2031,6 +2031,11 @@ convert_table_priv_string(text *priv_type_text)
 		{"REFERENCES WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
 		{"TRIGGER", ACL_TRIGGER},
 		{"TRIGGER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
+		{"ANY", ACL_SELECT | ACL_GRANT_OPTION_FOR(ACL_SELECT) |
+				ACL_INSERT | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
+				ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_DELETE | ACL_GRANT_OPTION_FOR(ACL_DELETE) |
+				ACL_TRUNCATE | ACL_GRANT_OPTION_FOR(ACL_TRUNCATE) | ACL_REFERENCES |
+				ACL_GRANT_OPTION_FOR(ACL_REFERENCES) | ACL_TRIGGER | ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
 		{"RULE", 0},			/* ignore old RULE privileges */
 		{"RULE WITH GRANT OPTION", 0},
 		{NULL, 0}
@@ -2243,6 +2248,7 @@ convert_sequence_priv_string(text *priv_type_text)
 		{"USAGE", ACL_USAGE},
 		{"SELECT", ACL_SELECT},
 		{"UPDATE", ACL_UPDATE},
+		{"ANY", ACL_USAGE | ACL_SELECT | ACL_UPDATE},
 		{NULL, 0}
 	};
 
@@ -2858,6 +2864,9 @@ convert_column_priv_string(text *priv_type_text)
 		{"UPDATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_UPDATE)},
 		{"REFERENCES", ACL_REFERENCES},
 		{"REFERENCES WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
+		{"ANY", ACL_SELECT | ACL_GRANT_OPTION_FOR(ACL_SELECT) | ACL_INSERT |
+				ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
+				ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_REFERENCES | ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
 		{NULL, 0}
 	};
 
@@ -3055,6 +3064,9 @@ convert_database_priv_string(text *priv_type_text)
 		{"TEMP WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP)},
 		{"CONNECT", ACL_CONNECT},
 		{"CONNECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
+		{"ANY", ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE) |
+									ACL_CREATE_TEMP | ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CREATE_TEMP |
+									ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CONNECT | ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
 		{NULL, 0}
 	};
 
@@ -3238,6 +3250,7 @@ convert_foreign_data_wrapper_priv_string(text *priv_type_text)
 	static const priv_map foreign_data_wrapper_priv_map[] = {
 		{"USAGE", ACL_USAGE},
 		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+		{"ANY", ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
 		{NULL, 0}
 	};
 
@@ -3438,6 +3451,7 @@ convert_function_priv_string(text *priv_type_text)
 	static const priv_map function_priv_map[] = {
 		{"EXECUTE", ACL_EXECUTE},
 		{"EXECUTE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_EXECUTE)},
+		{"ANY", ACL_EXECUTE | ACL_GRANT_OPTION_FOR(ACL_EXECUTE)},
 		{NULL, 0}
 	};
 
@@ -3629,6 +3643,7 @@ convert_language_priv_string(text *priv_type_text)
 	static const priv_map language_priv_map[] = {
 		{"USAGE", ACL_USAGE},
 		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+		{"ANY", ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
 		{NULL, 0}
 	};
 
@@ -3822,6 +3837,7 @@ convert_schema_priv_string(text *priv_type_text)
 		{"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
 		{"USAGE", ACL_USAGE},
 		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+		{"ANY", ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE) | ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
 		{NULL, 0}
 	};
 
@@ -4004,6 +4020,7 @@ convert_server_priv_string(text *priv_type_text)
 	static const priv_map server_priv_map[] = {
 		{"USAGE", ACL_USAGE},
 		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+		{"ANY", ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
 		{NULL, 0}
 	};
 
@@ -4186,6 +4203,7 @@ convert_tablespace_priv_string(text *priv_type_text)
 	static const priv_map tablespace_priv_map[] = {
 		{"CREATE", ACL_CREATE},
 		{"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
+		{"ANY", ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE)},
 		{NULL, 0}
 	};
 
@@ -4385,6 +4403,7 @@ convert_type_priv_string(text *priv_type_text)
 	static const priv_map type_priv_map[] = {
 		{"USAGE", ACL_USAGE},
 		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+		{"ANY", ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
 		{NULL, 0}
 	};
 
@@ -4566,6 +4585,7 @@ convert_role_priv_string(text *priv_type_text)
 		{"USAGE WITH ADMIN OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
 		{"MEMBER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
 		{"MEMBER WITH ADMIN OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
+		{"ANY", ACL_USAGE | ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE)},
 		{NULL, 0}
 	};
 
2_view_security_definer_v3.patchapplication/octet-stream; name=2_view_security_definer_v3.patchDownload
diff --git a/doc/src/sgml/ref/alter_view.sgml b/doc/src/sgml/ref/alter_view.sgml
index 00f4ecb..d1f3a54 100644
--- a/doc/src/sgml/ref/alter_view.sgml
+++ b/doc/src/sgml/ref/alter_view.sgml
@@ -142,6 +142,16 @@ ALTER VIEW [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RESET
          </para>
         </listitem>
        </varlistentry>
+       <varlistentry>
+        <term><literal>security_definer</literal> (<type>boolean</type>)</term>
+        <listitem>
+         <para>
+          Changes the security-definer property of the view.  The value must
+          be Boolean value, such as <literal>true</literal>
+          or <literal>false</literal>.
+         </para>
+        </listitem>
+       </varlistentry>
       </variablelist>
      </para>
     </listitem>
diff --git a/doc/src/sgml/ref/create_view.sgml b/doc/src/sgml/ref/create_view.sgml
index e37bb00..71c6f79 100644
--- a/doc/src/sgml/ref/create_view.sgml
+++ b/doc/src/sgml/ref/create_view.sgml
@@ -147,6 +147,16 @@ CREATE VIEW <replaceable>name</> AS WITH RECURSIVE <replaceable>name</> (<replac
          </para>
         </listitem>
        </varlistentry>
+       
+       <varlistentry>
+        <term><literal>security_definer</literal> (<type>boolean</type>)</term>
+        <listitem>
+         <para>
+          This should be used if the view is intended to be executed with
+          owner privileges rather than the current user.
+         </para>
+        </listitem>
+       </varlistentry>
       </variablelist>
      </para>
     </listitem>
@@ -276,6 +286,37 @@ CREATE VIEW vista AS SELECT text 'Hello World' AS hello;
     to replace it (this includes being a member of the owning role).
    </para>
 
+  <refsect2 id="SQL-CREATEVIEW-security-definer-views">
+   <title id="SQL-CREATEVIEW-security-definer-views-title">Security definer Views</title>
+
+   <indexterm zone="sql-createview-security-definer-views">
+    <primary>security definer views</primary>
+   </indexterm>
+
+   <para>
+    Security definer views uses the view owner id instead of the current user
+    in the following conditions, otherwise the current user is used to verify
+    the privileges and etc.
+    <itemizedlist>
+     <listitem>
+      <para>
+       The view is used in <command>INSERT</>, <command>UPDATE</>
+       and <command>DELETE</> statements in the same way as on a
+       regular table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       To apply row-level security policies on the underlying base
+       relations of the view, based on the security definer, the 
+       corresponding policies related to the user are applied.
+      </para>
+     </listitem>
+    </itemizedlist>
+   </para>
+  </refsect2>
+ 
   <refsect2 id="SQL-CREATEVIEW-updatable-views">
    <title id="SQL-CREATEVIEW-updatable-views-title">Updatable Views</title>
 
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 45fba90..78a81d3 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -89,6 +89,15 @@ static relopt_bool boolRelOpts[] =
 		},
 		false
 	},
+	{
+		{
+			"security_definer",
+			"specifies that the view is to be executed with the privileges of the user that created it.",
+			RELOPT_KIND_VIEW,
+			AccessExclusiveLock
+		},
+		false
+	},
 	/* list terminator */
 	{{NULL}}
 };
@@ -1320,6 +1329,8 @@ view_reloptions(Datum reloptions, bool validate)
 	static const relopt_parse_elt tab[] = {
 		{"security_barrier", RELOPT_TYPE_BOOL,
 		offsetof(ViewOptions, security_barrier)},
+		{ "security_definer", RELOPT_TYPE_BOOL,
+		offsetof(ViewOptions, security_definer) },
 		{"check_option", RELOPT_TYPE_STRING,
 		offsetof(ViewOptions, check_option_offset)}
 	};
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index a22a11e..b80c2a3 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -24,6 +24,7 @@
 #include "catalog/pg_type.h"
 #include "commands/trigger.h"
 #include "foreign/fdwapi.h"
+#include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
 #include "parser/analyze.h"
@@ -1616,6 +1617,7 @@ fireRIRrules(Query *parsetree, List *activeRIRs, bool forUpdatePushedDown)
 	int			origResultRelation = parsetree->resultRelation;
 	int			rt_index;
 	ListCell   *lc;
+	bool		security_definer_view = false;
 
 	/*
 	 * don't try to convert this into a foreach loop, because rtable list can
@@ -1777,18 +1779,27 @@ fireRIRrules(Query *parsetree, List *activeRIRs, bool forUpdatePushedDown)
 		++rt_index;
 
 		/* Only normal relations can have RLS policies */
-		if (rte->rtekind != RTE_RELATION ||
-			rte->relkind != RELKIND_RELATION)
+		if (rte->rtekind != RTE_RELATION)
 			continue;
-
+		
 		rel = heap_open(rte->relid, NoLock);
 
+		if (rte->relkind == RELKIND_VIEW && RelationIsSecurityDefinerView(rel))
+			security_definer_view = true;
+
+		if (rte->relkind != RELKIND_RELATION)
+		{
+			heap_close(rel, NoLock);
+			continue;
+		}
+
 		/*
 		 * Fetch any new security quals that must be applied to this RTE.
 		 */
 		get_row_security_policies(parsetree, rte, rt_index,
 								  &securityQuals, &withCheckOptions,
-								  &hasRowSecurity, &hasSubLinks);
+								  &hasRowSecurity, &hasSubLinks,
+								  security_definer_view);
 
 		if (securityQuals != NIL || withCheckOptions != NIL)
 		{
@@ -2834,7 +2845,10 @@ rewriteTargetView(Query *parsetree, Relation view)
 	 * the executor still performs appropriate permissions checks for the
 	 * query caller's use of the view.
 	 */
-	new_rte->checkAsUser = view->rd_rel->relowner;
+	if (RelationIsSecurityDefinerView(view))
+		new_rte->checkAsUser = view->rd_rel->relowner;
+	else
+		new_rte->checkAsUser = GetUserId();
 	new_rte->requiredPerms = view_rte->requiredPerms;
 
 	/*
diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c
index 970fa33..2d06feb 100644
--- a/src/backend/rewrite/rowsecurity.c
+++ b/src/backend/rewrite/rowsecurity.c
@@ -105,7 +105,8 @@ row_security_policy_hook_type row_security_policy_hook_restrictive = NULL;
 void
 get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
 						  List **securityQuals, List **withCheckOptions,
-						  bool *hasRowSecurity, bool *hasSubLinks)
+						  bool *hasRowSecurity, bool *hasSubLinks,
+						  bool security_definer_view)
 {
 	Oid			user_id;
 	int			rls_status;
@@ -125,10 +126,13 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
 		return;
 
 	/* Switch to checkAsUser if it's set */
-	user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId();
+	if (security_definer_view)
+		user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId();
+	else
+		user_id = GetUserId();
 
 	/* Determine the state of RLS for this, pass checkAsUser explicitly */
-	rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false);
+	rls_status = check_enable_rls(rte->relid, user_id, false);
 
 	/* If there is no RLS on this table at all, nothing to do */
 	if (rls_status == RLS_NONE)
diff --git a/src/include/rewrite/rowsecurity.h b/src/include/rewrite/rowsecurity.h
index fd0cbaff..661c9dd 100644
--- a/src/include/rewrite/rowsecurity.h
+++ b/src/include/rewrite/rowsecurity.h
@@ -43,6 +43,7 @@ extern PGDLLIMPORT row_security_policy_hook_type row_security_policy_hook_restri
 extern void get_row_security_policies(Query *root,
 						  RangeTblEntry *rte, int rt_index,
 						  List **securityQuals, List **withCheckOptions,
-						  bool *hasRowSecurity, bool *hasSubLinks);
+						  bool *hasRowSecurity, bool *hasSubLinks,
+						  bool security_definer_view);
 
 #endif   /* ROWSECURITY_H */
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index ff5672d..53f01cd 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -265,6 +265,7 @@ typedef struct ViewOptions
 {
 	int32		vl_len_;		/* varlena header (do not touch directly!) */
 	bool		security_barrier;
+	bool		security_definer;
 	int			check_option_offset;
 } ViewOptions;
 
@@ -278,6 +279,14 @@ typedef struct ViewOptions
 	 ((ViewOptions *) (relation)->rd_options)->security_barrier : false)
 
 /*
+ * RelationIsSecurityDefiner
+ *		Returns whether the relation is security definer, or not.
+ */
+#define RelationIsSecurityDefinerView(relation)	\
+	((relation)->rd_options ?				\
+	 ((ViewOptions *) (relation)->rd_options)->security_definer : false)
+
+/*
  * RelationHasCheckOption
  *		Returns true if the relation is a view defined with either the local
  *		or the cascaded check option.  Note multiple eval of argument!
diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out
index 4aaa88f..0ab349c 100644
--- a/src/test/regress/expected/rowsecurity.out
+++ b/src/test/regress/expected/rowsecurity.out
@@ -1425,7 +1425,7 @@ CREATE POLICY p1 ON b1 USING (a % 2 = 0);
 ALTER TABLE b1 ENABLE ROW LEVEL SECURITY;
 GRANT ALL ON b1 TO rls_regress_user1;
 SET SESSION AUTHORIZATION rls_regress_user1;
-CREATE VIEW bv1 WITH (security_barrier) AS SELECT * FROM b1 WHERE a > 0 WITH CHECK OPTION;
+CREATE VIEW bv1 WITH (security_barrier, security_definer) AS SELECT * FROM b1 WHERE a > 0 WITH CHECK OPTION;
 GRANT ALL ON bv1 TO rls_regress_user2;
 SET SESSION AUTHORIZATION rls_regress_user2;
 EXPLAIN (COSTS OFF) SELECT * FROM bv1 WHERE f_leak(b);
@@ -1892,7 +1892,7 @@ EXPLAIN (COSTS OFF) EXECUTE plancache_test3;
 --
 -- View and Table owner are the same.
 SET SESSION AUTHORIZATION rls_regress_user0;
-CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b);
+CREATE VIEW rls_view WITH (security_definer) AS SELECT * FROM z1 WHERE f_leak(b);
 GRANT SELECT ON rls_view TO rls_regress_user1;
 -- Query as role that is not owner of view or table.  Should return all records.
 SET SESSION AUTHORIZATION rls_regress_user1;
@@ -1941,7 +1941,7 @@ EXPLAIN (COSTS OFF) SELECT * FROM rls_view;
 DROP VIEW rls_view;
 -- View and Table owners are different.
 SET SESSION AUTHORIZATION rls_regress_user1;
-CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b);
+CREATE VIEW rls_view WITH (security_definer) AS SELECT * FROM z1 WHERE f_leak(b);
 GRANT SELECT ON rls_view TO rls_regress_user0;
 -- Query as role that is not owner of view but is owner of table.
 -- Should return records based on view owner policies.
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index 6c71371..b62f1ad 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -966,7 +966,7 @@ CREATE USER view_user2;
 SET SESSION AUTHORIZATION view_user1;
 CREATE TABLE base_tbl(a int, b text, c float);
 INSERT INTO base_tbl VALUES (1, 'Row 1', 1.0);
-CREATE VIEW rw_view1 AS SELECT b AS bb, c AS cc, a AS aa FROM base_tbl;
+CREATE VIEW rw_view1 WITH (security_definer) AS SELECT b AS bb, c AS cc, a AS aa FROM base_tbl;
 INSERT INTO rw_view1 VALUES ('Row 2', 2.0, 2);
 GRANT SELECT ON base_tbl TO view_user2;
 GRANT SELECT ON rw_view1 TO view_user2;
diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql
index b5f5bcf..cc7e139 100644
--- a/src/test/regress/sql/rowsecurity.sql
+++ b/src/test/regress/sql/rowsecurity.sql
@@ -491,7 +491,7 @@ ALTER TABLE b1 ENABLE ROW LEVEL SECURITY;
 GRANT ALL ON b1 TO rls_regress_user1;
 
 SET SESSION AUTHORIZATION rls_regress_user1;
-CREATE VIEW bv1 WITH (security_barrier) AS SELECT * FROM b1 WHERE a > 0 WITH CHECK OPTION;
+CREATE VIEW bv1 WITH (security_barrier, security_definer) AS SELECT * FROM b1 WHERE a > 0 WITH CHECK OPTION;
 GRANT ALL ON bv1 TO rls_regress_user2;
 
 SET SESSION AUTHORIZATION rls_regress_user2;
@@ -687,7 +687,7 @@ EXPLAIN (COSTS OFF) EXECUTE plancache_test3;
 --
 -- View and Table owner are the same.
 SET SESSION AUTHORIZATION rls_regress_user0;
-CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b);
+CREATE VIEW rls_view WITH (security_definer) AS SELECT * FROM z1 WHERE f_leak(b);
 GRANT SELECT ON rls_view TO rls_regress_user1;
 
 -- Query as role that is not owner of view or table.  Should return all records.
@@ -703,7 +703,7 @@ DROP VIEW rls_view;
 
 -- View and Table owners are different.
 SET SESSION AUTHORIZATION rls_regress_user1;
-CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b);
+CREATE VIEW rls_view WITH (security_definer) AS SELECT * FROM z1 WHERE f_leak(b);
 GRANT SELECT ON rls_view TO rls_regress_user0;
 
 -- Query as role that is not owner of view but is owner of table.
diff --git a/src/test/regress/sql/updatable_views.sql b/src/test/regress/sql/updatable_views.sql
index 5297a71..8452c18 100644
--- a/src/test/regress/sql/updatable_views.sql
+++ b/src/test/regress/sql/updatable_views.sql
@@ -397,7 +397,7 @@ CREATE USER view_user2;
 SET SESSION AUTHORIZATION view_user1;
 CREATE TABLE base_tbl(a int, b text, c float);
 INSERT INTO base_tbl VALUES (1, 'Row 1', 1.0);
-CREATE VIEW rw_view1 AS SELECT b AS bb, c AS cc, a AS aa FROM base_tbl;
+CREATE VIEW rw_view1 WITH (security_definer) AS SELECT b AS bb, c AS cc, a AS aa FROM base_tbl;
 INSERT INTO rw_view1 VALUES ('Row 2', 2.0, 2);
 
 GRANT SELECT ON base_tbl TO view_user2;
3_shared_catalog_tenancy_v4.patchapplication/octet-stream; name=3_shared_catalog_tenancy_v4.patchDownload
diff --git a/doc/src/sgml/ref/initdb.sgml b/doc/src/sgml/ref/initdb.sgml
index 4e339ec..ac460e9 100644
--- a/doc/src/sgml/ref/initdb.sgml
+++ b/doc/src/sgml/ref/initdb.sgml
@@ -152,6 +152,19 @@ PostgreSQL documentation
      </varlistentry>
 
      <varlistentry>
+      <term><option>-C</option></term>
+      <term><option>--shared-catalog-security</option></term>
+      <listitem>
+       <para>
+        This option enables the shared catalog tables security, by adding
+        row level security policies on all eligible shared catalog tables.
+        With this option, multi-tenancy in PostgreSQL is supported at 
+        database level.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><option>-D <replaceable class="parameter">directory</replaceable></option></term>
       <term><option>--pgdata=<replaceable class="parameter">directory</replaceable></option></term>
       <listitem>
diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c
index bb735ac..820261e 100644
--- a/src/backend/commands/policy.c
+++ b/src/backend/commands/policy.c
@@ -23,7 +23,16 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_authid.h"
+#include "catalog/pg_auth_members.h"
+#include "catalog/pg_database.h"
+#include "catalog/pg_db_role_setting.h"
+#include "catalog/pg_pltemplate.h"
 #include "catalog/pg_policy.h"
+#include "catalog/pg_replication_origin.h"
+#include "catalog/pg_shdepend.h"
+#include "catalog/pg_shdescription.h"
+#include "catalog/pg_shseclabel.h"
+#include "catalog/pg_tablespace.h"
 #include "catalog/pg_type.h"
 #include "commands/policy.h"
 #include "miscadmin.h"
@@ -36,6 +45,7 @@
 #include "rewrite/rewriteManip.h"
 #include "rewrite/rowsecurity.h"
 #include "storage/lock.h"
+#include "tcop/tcopprot.h"
 #include "utils/acl.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
@@ -46,11 +56,18 @@
 #include "utils/rel.h"
 #include "utils/syscache.h"
 
+#define CATALOG_POLICY_STRING_SIZE 65536
+
 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 get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *buf);
+
+/* variable to identify whether catalog row security is built is in progress or not? */
+bool		CatalogRowSecurityBuiltInProgress = false;
+
 /*
  * Callback to RangeVarGetRelidExtended().
  *
@@ -195,6 +212,16 @@ RelationBuildRowSecurity(Relation relation)
 	RowSecurityDesc *volatile rsdesc = NULL;
 
 	/*
+	 * Build the row security descriptor of a relation, once all the critical
+	 * relations are built.
+	 */
+	if (!criticalRelcachesBuilt || !criticalSharedRelcachesBuilt)
+		return;
+
+	if (IsSystemRelation(relation) && CatalogRowSecurityBuiltInProgress)
+		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.
@@ -211,124 +238,194 @@ RelationBuildRowSecurity(Relation relation)
 	 */
 	PG_TRY();
 	{
-		Relation	catalog;
-		ScanKeyData skey;
-		SysScanDesc sscan;
-		HeapTuple	tuple;
-
 		rsdesc = MemoryContextAllocZero(rscxt, sizeof(RowSecurityDesc));
 		rsdesc->rscxt = rscxt;
 
-		catalog = heap_open(PolicyRelationId, AccessShareLock);
-
-		ScanKeyInit(&skey,
-					Anum_pg_policy_polrelid,
-					BTEqualStrategyNumber, F_OIDEQ,
-					ObjectIdGetDatum(RelationGetRelid(relation)));
-
-		sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
-								   NULL, 1, &skey);
-
-		/*
-		 * Loop through the row level security policies for this relation, if
-		 * any.
-		 */
-		while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
+		if (IsSystemRelation(relation))
 		{
-			Datum		value_datum;
-			char		cmd_value;
-			Datum		roles_datum;
-			char	   *qual_value;
-			Expr	   *qual_expr;
-			char	   *with_check_value;
-			Expr	   *with_check_qual;
-			char	   *policy_name_value;
-			bool		isnull;
 			RowSecurityPolicy *policy;
+			Datum	   *role_oids;
+			int			nitems = 0;
+			ArrayType  *role_ids;
+			Node	   *qual_expr;
+			List	   *raw_parsetree_list;
+			ListCell   *list_item;
+			ParseState *qual_pstate;
+			RangeTblEntry *rte;
+			CreatePolicyStmt *stmt;
+			char		buf[CATALOG_POLICY_STRING_SIZE];
 
-			/*
-			 * Note: all the pass-by-reference data we collect here is either
-			 * still stored in the tuple, or constructed in the caller's
-			 * short-lived memory context.  We must copy it into rscxt
-			 * explicitly below.
-			 */
+			CatalogRowSecurityBuiltInProgress = true;
+			get_catalog_policy_string(relation->rd_id, relation->rd_rel, buf);
 
-			/* Get policy command */
-			value_datum = heap_getattr(tuple, Anum_pg_policy_polcmd,
-									   RelationGetDescr(catalog), &isnull);
-			Assert(!isnull);
-			cmd_value = DatumGetChar(value_datum);
-
-			/* Get policy name */
-			value_datum = heap_getattr(tuple, Anum_pg_policy_polname,
-									   RelationGetDescr(catalog), &isnull);
-			Assert(!isnull);
-			policy_name_value = NameStr(*(DatumGetName(value_datum)));
-
-			/* Get policy roles */
-			roles_datum = heap_getattr(tuple, Anum_pg_policy_polroles,
-									   RelationGetDescr(catalog), &isnull);
-			/* shouldn't be null, but initdb doesn't mark it so, so check */
-			if (isnull)
-				elog(ERROR, "unexpected null value in pg_policy.polroles");
-
-			/* Get policy qual */
-			value_datum = heap_getattr(tuple, Anum_pg_policy_polqual,
-									   RelationGetDescr(catalog), &isnull);
-			if (!isnull)
-			{
-				qual_value = TextDatumGetCString(value_datum);
-				qual_expr = (Expr *) stringToNode(qual_value);
-			}
-			else
-				qual_expr = NULL;
+			role_oids = policy_role_list_to_array(NULL, &nitems);
+			role_ids = construct_array(role_oids, nitems, OIDOID,
+									   sizeof(Oid), true, 'i');
 
-			/* Get WITH CHECK qual */
-			value_datum = heap_getattr(tuple, Anum_pg_policy_polwithcheck,
-									   RelationGetDescr(catalog), &isnull);
-			if (!isnull)
+			raw_parsetree_list = pg_parse_query(buf);
+			Assert(list_length(raw_parsetree_list) == 1);
+
+			foreach(list_item, raw_parsetree_list)
 			{
-				with_check_value = TextDatumGetCString(value_datum);
-				with_check_qual = (Expr *) stringToNode(with_check_value);
+				Node	   *parsetree;
+
+				parsetree = (Node *) lfirst(list_item);
+				stmt = (CreatePolicyStmt *) parsetree;
 			}
-			else
-				with_check_qual = NULL;
+
+			qual_pstate = make_parsestate(NULL);
+			rte = addRangeTableEntryForRelation(qual_pstate, relation,
+												NULL, false, false);
+
+			addRTEtoQuery(qual_pstate, rte, false, true, true);
+			qual_expr = transformWhereClause(qual_pstate,
+											 stmt->qual,
+											 EXPR_KIND_POLICY,
+											 "POLICY");
+
+			/* Fix up collation information */
+			assign_expr_collations(qual_pstate, qual_expr);
 
 			/* Now copy everything into the cache context */
 			MemoryContextSwitchTo(rscxt);
 
 			policy = palloc0(sizeof(RowSecurityPolicy));
-			policy->policy_name = pstrdup(policy_name_value);
-			policy->polcmd = cmd_value;
-			policy->roles = DatumGetArrayTypePCopy(roles_datum);
+			policy->policy_name = pstrdup(stmt->policy_name);
+			policy->polcmd = ACL_SELECT_CHR;
+			policy->roles = DatumGetArrayTypePCopy(role_ids);
 			policy->qual = copyObject(qual_expr);
-			policy->with_check_qual = copyObject(with_check_qual);
-			policy->hassublinks = checkExprHasSubLink((Node *) qual_expr) ||
-				checkExprHasSubLink((Node *) with_check_qual);
+			policy->with_check_qual = NULL;
+			policy->hassublinks = checkExprHasSubLink((Node *) qual_expr);
 
 			rsdesc->policies = lcons(policy, rsdesc->policies);
 
 			MemoryContextSwitchTo(oldcxt);
-
-			/* clean up some (not all) of the junk ... */
-			if (qual_expr != NULL)
-				pfree(qual_expr);
-			if (with_check_qual != NULL)
-				pfree(with_check_qual);
 		}
+		else
+		{
+			Relation	catalog;
+			ScanKeyData skey;
+			SysScanDesc sscan;
+			HeapTuple	tuple;
+
+			catalog = heap_open(PolicyRelationId, AccessShareLock);
 
-		systable_endscan(sscan);
-		heap_close(catalog, AccessShareLock);
+			ScanKeyInit(&skey,
+						Anum_pg_policy_polrelid,
+						BTEqualStrategyNumber, F_OIDEQ,
+						ObjectIdGetDatum(RelationGetRelid(relation)));
+
+			sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
+									   NULL, 1, &skey);
+
+			/*
+			 * Loop through the row level security policies for this relation,
+			 * if any.
+			 */
+			while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
+			{
+				Datum		value_datum;
+				char		cmd_value;
+				Datum		roles_datum;
+				char	   *qual_value;
+				Expr	   *qual_expr;
+				char	   *with_check_value;
+				Expr	   *with_check_qual;
+				char	   *policy_name_value;
+				bool		isnull;
+				RowSecurityPolicy *policy;
+
+				/*
+				 * Note: all the pass-by-reference data we collect here is
+				 * either still stored in the tuple, or constructed in the
+				 * caller's short-lived memory context.  We must copy it into
+				 * rscxt explicitly below.
+				 */
+
+				/* Get policy command */
+				value_datum = heap_getattr(tuple, Anum_pg_policy_polcmd,
+										 RelationGetDescr(catalog), &isnull);
+				Assert(!isnull);
+				cmd_value = DatumGetChar(value_datum);
+
+				/* Get policy name */
+				value_datum = heap_getattr(tuple, Anum_pg_policy_polname,
+										 RelationGetDescr(catalog), &isnull);
+				Assert(!isnull);
+				policy_name_value = NameStr(*(DatumGetName(value_datum)));
+
+				/* Get policy roles */
+				roles_datum = heap_getattr(tuple, Anum_pg_policy_polroles,
+										 RelationGetDescr(catalog), &isnull);
+				/* shouldn't be null, but initdb doesn't mark it so, so check */
+				if (isnull)
+					elog(ERROR, "unexpected null value in pg_policy.polroles");
+
+				/* Get policy qual */
+				value_datum = heap_getattr(tuple, Anum_pg_policy_polqual,
+										 RelationGetDescr(catalog), &isnull);
+				if (!isnull)
+				{
+					qual_value = TextDatumGetCString(value_datum);
+					qual_expr = (Expr *) stringToNode(qual_value);
+				}
+				else
+					qual_expr = NULL;
+
+				/* Get WITH CHECK qual */
+				value_datum = heap_getattr(tuple, Anum_pg_policy_polwithcheck,
+										 RelationGetDescr(catalog), &isnull);
+				if (!isnull)
+				{
+					with_check_value = TextDatumGetCString(value_datum);
+					with_check_qual = (Expr *) stringToNode(with_check_value);
+				}
+				else
+					with_check_qual = NULL;
+
+				/* Now copy everything into the cache context */
+				MemoryContextSwitchTo(rscxt);
+
+				policy = palloc0(sizeof(RowSecurityPolicy));
+				policy->policy_name = pstrdup(policy_name_value);
+				policy->polcmd = cmd_value;
+				policy->roles = DatumGetArrayTypePCopy(roles_datum);
+				policy->qual = copyObject(qual_expr);
+				policy->with_check_qual = copyObject(with_check_qual);
+				policy->hassublinks = checkExprHasSubLink((Node *) qual_expr) ||
+					checkExprHasSubLink((Node *) with_check_qual);
+
+				rsdesc->policies = lcons(policy, rsdesc->policies);
+
+				MemoryContextSwitchTo(oldcxt);
+
+				/* clean up some (not all) of the junk ... */
+				if (qual_expr != NULL)
+					pfree(qual_expr);
+				if (with_check_qual != NULL)
+					pfree(with_check_qual);
+			}
+
+			systable_endscan(sscan);
+			heap_close(catalog, AccessShareLock);
+		}
 	}
 	PG_CATCH();
 	{
 		/* Delete rscxt, first making sure it isn't active */
 		MemoryContextSwitchTo(oldcxt);
 		MemoryContextDelete(rscxt);
+
+		if (IsSystemRelation(relation))
+			CatalogRowSecurityBuiltInProgress = false;
+
 		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
+	if (IsSystemRelation(relation))
+		CatalogRowSecurityBuiltInProgress = false;
+
 	/* Success --- attach the policy descriptor to the relcache entry */
 	relation->rd_rsdesc = rsdesc;
 }
@@ -410,6 +507,73 @@ RemovePolicyById(Oid policy_id)
 	heap_close(pg_policy_rel, RowExclusiveLock);
 }
 
+static bool
+get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *buf)
+{
+	bool		policy_exist = true;
+
+	switch (relationid)
+	{
+		case AuthIdRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+					" (pg_has_role(oid, 'any'))",
+				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+			break;
+		case AuthMemRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+			  " (pg_has_role(roleid, 'any') AND pg_has_role(member, 'any'))",
+				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+			break;
+		case DatabaseRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+					" ((oid < 16384) OR has_database_privilege(oid,'any'))",
+				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+			break;
+		case DbRoleSettingRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+					" (pg_has_role(setrole, 'any') AND has_database_privilege(setdatabase,'any'))",
+				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+			break;
+		case PLTemplateRelationId:
+			policy_exist = false;
+			break;
+		case ReplicationOriginRelationId:
+			policy_exist = false;
+			break;
+		case SharedDependRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+					" ((classid = (select oid from pg_class where relname = 'pg_database') AND has_database_privilege(objid, 'any'))"
+					" OR (classid = (select oid from pg_class where relname = 'pg_authid') AND pg_has_role(objid, 'any'))"
+					" OR (classid = (select oid from pg_class where relname = 'pg_tablespace') AND has_tablespace_privilege(objid, 'any')))",
+				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+			break;
+		case SharedDescriptionRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+					" ((classoid = (select oid from pg_class where relname = 'pg_database') AND has_database_privilege(objoid, 'any'))"
+					" OR (classoid = (select oid from pg_class where relname = 'pg_authid') AND pg_has_role(objoid, 'any'))"
+					" 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);
+			break;
+		case SharedSecLabelRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+					" ((classoid = (select oid from pg_class where relname = 'pg_database') AND has_database_privilege(objoid, 'any'))"
+					" OR (classoid = (select oid from pg_class where relname = 'pg_authid') AND pg_has_role(objoid, 'any'))"
+					" 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);
+			break;
+		case TableSpaceRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+				  " ((oid < 16384) OR has_tablespace_privilege(oid, 'any'))",
+				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+			break;
+		default:
+			policy_exist = false;
+			break;
+	}
+
+	return policy_exist;
+}
+
 /*
  * RemoveRoleFromObjectPolicy -
  *	 remove a role from a policy by its OID.  If the role is not a member of
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 0b4a334..5b00a8d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -3442,6 +3442,16 @@ ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode)
 	{
 		AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
 
+		/*
+		 * ALTER table on sytem catalog tables is possible only when user
+		 * specifies CATALOG SECURITY on system catalog tables. To avoid an
+		 * error in the AlterTableCreateToastTable function for system catalog
+		 * tables, the system catalog tables are ignored for the toast table
+		 * creation.
+		 */
+		if (!IsUnderPostmaster && IsSharedRelation(tab->relid))
+			continue;
+
 		if (tab->relkind == RELKIND_RELATION ||
 			tab->relkind == RELKIND_MATVIEW)
 			AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
diff --git a/src/backend/utils/misc/rls.c b/src/backend/utils/misc/rls.c
index c33f29e..4d6c88d 100644
--- a/src/backend/utils/misc/rls.c
+++ b/src/backend/utils/misc/rls.c
@@ -58,10 +58,6 @@ check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
 	bool		relforcerowsecurity;
 	bool		amowner;
 
-	/* Nothing to do for built-in relations */
-	if (relid < (Oid) FirstNormalObjectId)
-		return RLS_NONE;
-
 	/* Fetch relation's relrowsecurity and relforcerowsecurity flags */
 	tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
 	if (!HeapTupleIsValid(tuple))
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 35e39ce..13a22ee 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -136,6 +136,7 @@ static bool do_sync = true;
 static bool sync_only = false;
 static bool show_setting = false;
 static bool data_checksums = false;
+static bool shared_catalog_security = false;
 static char *xlog_dir = "";
 
 
@@ -190,6 +191,7 @@ static char *authwarning = NULL;
  */
 static const char *boot_options = "-F";
 static const char *backend_options = "--single -F -O -j -c search_path=pg_catalog -c exit_on_error=true";
+static const char *catalog_security_options = "--single -F -O -j -c search_path=pg_catalog -c exit_on_error=true -c allow_system_table_mods=true";
 
 static const char *const subdirs[] = {
 	"global",
@@ -259,6 +261,7 @@ static void setup_dictionary(FILE *cmdfd);
 static void setup_privileges(FILE *cmdfd);
 static void set_info_version(void);
 static void setup_schema(FILE *cmdfd);
+static void setup_shared_catalog_security(FILE *cmdfd);
 static void load_plpgsql(FILE *cmdfd);
 static void vacuum_db(FILE *cmdfd);
 static void make_template0(FILE *cmdfd);
@@ -2082,6 +2085,59 @@ setup_schema(FILE *cmdfd)
 }
 
 /*
+ * setup shared catalog security by defining policies
+ */
+static void
+setup_shared_catalog_security(FILE *cmdfd)
+{
+	const char **line;
+	static const char *pg_shared_catalog_security_setup[] = {
+		/* AuthMemRelationId */
+		"alter table pg_auth_members enable row level security;\n",
+
+		/* AuthIdRelationId */
+		"alter table pg_authid enable row level security;\n",
+
+		/* DatabaseRelationId */
+		"alter table pg_database enable row level security;\n",
+
+		/* DbRoleSettingRelationId */
+		"alter table pg_database enable row level security;\n",
+
+		/* PLTemplateRelationId */
+
+		/*
+		 * Currently there is no policy needed for this table, so leave it as
+		 * it is.
+		 */
+
+		/* ReplicationOriginRelationId */
+
+		/*
+		 * Currently there is no policy needed for this table, so leave it as
+		 * it is.
+		 */
+
+		/* SharedDependRelationId */
+		"alter table pg_shdepend enable row level security;\n",
+
+		/* SharedDescriptionRelationId */
+		"alter table pg_shdescription enable row level security;\n",
+
+		/* SharedSecLabelRelationId */
+		"alter table pg_shseclabel enable row level security;\n",
+
+		/* TableSpaceRelationId */
+		"alter table pg_tablespace enable row level security;\n",
+
+		NULL
+	};
+
+	for (line = pg_shared_catalog_security_setup; *line != NULL; line++)
+		PG_CMD_PUTS(*line);
+}
+
+/*
  * load PL/pgsql server-side language
  */
 static void
@@ -2536,6 +2592,8 @@ usage(const char *progname)
 	printf(_("\nLess commonly used options:\n"));
 	printf(_("  -d, --debug               generate lots of debugging output\n"));
 	printf(_("  -k, --data-checksums      use data page checksums\n"));
+	printf(_("  -C, --shared-catalog-security\n"
+			 "						      use shared catalog security\n"));
 	printf(_("  -L DIRECTORY              where to find the input files\n"));
 	printf(_("  -n, --noclean             do not clean up after errors\n"));
 	printf(_("  -N, --nosync              do not wait for changes to be written safely to disk\n"));
@@ -3073,6 +3131,10 @@ initialize_data_directory(void)
 {
 	PG_CMD_DECL;
 	int			i;
+	const char *options = backend_options;
+
+	if (shared_catalog_security)
+		options = catalog_security_options;
 
 	setup_signals();
 
@@ -3121,7 +3183,7 @@ initialize_data_directory(void)
 
 	snprintf(cmd, sizeof(cmd),
 			 "\"%s\" %s template1 >%s",
-			 backend_exec, backend_options,
+			 backend_exec, options,
 			 DEVNULL);
 
 	PG_CMD_OPEN;
@@ -3146,6 +3208,9 @@ initialize_data_directory(void)
 
 	setup_schema(cmdfd);
 
+	if (shared_catalog_security)
+		setup_shared_catalog_security(cmdfd);
+
 	load_plpgsql(cmdfd);
 
 	vacuum_db(cmdfd);
@@ -3190,6 +3255,7 @@ main(int argc, char *argv[])
 		{"sync-only", no_argument, NULL, 'S'},
 		{"xlogdir", required_argument, NULL, 'X'},
 		{"data-checksums", no_argument, NULL, 'k'},
+		{"shared-catalog-security", no_argument, NULL, 'C'},
 		{NULL, 0, NULL, 0}
 	};
 
@@ -3230,7 +3296,7 @@ main(int argc, char *argv[])
 
 	/* process command-line options */
 
-	while ((c = getopt_long(argc, argv, "dD:E:kL:nNU:WA:sST:X:", long_options, &option_index)) != -1)
+	while ((c = getopt_long(argc, argv, "dD:E:kCL:nNU:WA:sST:X:", long_options, &option_index)) != -1)
 	{
 		switch (c)
 		{
@@ -3282,6 +3348,9 @@ main(int argc, char *argv[])
 			case 'k':
 				data_checksums = true;
 				break;
+			case 'C':
+				shared_catalog_security = true;
+				break;
 			case 'L':
 				share_path = pg_strdup(optarg);
 				break;
#27Robert Haas
robertmhaas@gmail.com
In reply to: Haribabu Kommi (#26)
Re: Multi-tenancy with RLS

On Tue, Jan 5, 2016 at 11:07 PM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

May be you missed to apply the 3_shared_catalog_tenancy_v4 path,
because 4_database_catalog_tenancy_v5 patch depends on it.

Here I attached all the patches for your convenience, I am able to
apply all patches in the order without any problem.

Is any committer thinking about taking a serious look at this patch series?

I ask because (1) it seems like it could be nice to have but (2) it
frightens me terribly. We are generally very sparing about assuming
that "stuff" (triggers, partial indexes, etc.) that works for user
tables can also be made to work for system tables. I haven't thought
deeply about what might go wrong in this particular case, but it
strikes me that if Haribabu Kommi is building something that is doomed
for some reason, it would be good to figure that out before he spends
any more time on it than he already has.

Apart from the issue of whether this is doomed for some architectural
reason, it is not entirely clear to me that there's any consensus that
we want this. I don't think that I understand the issues here well
enough to proffer an opinion of my own just yet... but I'd like to
hear what other people think.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#28Stephen Frost
sfrost@snowman.net
In reply to: Robert Haas (#27)
Re: Multi-tenancy with RLS

Robert,

* Robert Haas (robertmhaas@gmail.com) wrote:

On Tue, Jan 5, 2016 at 11:07 PM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

May be you missed to apply the 3_shared_catalog_tenancy_v4 path,
because 4_database_catalog_tenancy_v5 patch depends on it.

Here I attached all the patches for your convenience, I am able to
apply all patches in the order without any problem.

Is any committer thinking about taking a serious look at this patch series?

Joe took a look at it earlier and I'm definitely interested in it.
Furhter, he and I have chatted about it a few times.

Reference here to comments from Joe:
/messages/by-id/55F1FB2E.8020101@joeconway.com

I ask because (1) it seems like it could be nice to have but (2) it
frightens me terribly. We are generally very sparing about assuming
that "stuff" (triggers, partial indexes, etc.) that works for user
tables can also be made to work for system tables. I haven't thought
deeply about what might go wrong in this particular case, but it
strikes me that if Haribabu Kommi is building something that is doomed
for some reason, it would be good to figure that out before he spends
any more time on it than he already has.

I'm not aware of any particular reason for it to be doomed out of the
gate. That isn't to say that we won't find an issue with it or that
I've given it an in depth look (I've not), but the concept seems
reasonable enough and I can't think of any off-hand reasons why it
won't work.

I will note that there's a couple different patches involved here and
they really deserve their own review and consideration.

Apart from the issue of whether this is doomed for some architectural
reason, it is not entirely clear to me that there's any consensus that
we want this. I don't think that I understand the issues here well
enough to proffer an opinion of my own just yet... but I'd like to
hear what other people think.

I'm certainly of the opinion that we want this or something similar.

The big caveat kicking around in my head is if we want to have our own
set of defined policies or if we want to give flexibility to the
administrator to define their own policies. In particular, I'm
wondering about things like:

CREATE POLICY namespace_limit ON pg_namespace TO company1 USING
(substring(nspname,1,8) = 'company1_');

Which is a bit different, as I understand it, from what Haribadu has
been proposing and quite a bit more complicated, as we'd then have to
make the internal lookups respect the policy (so things like CREATE
SCHEMA would have to check if you're allowed to actually create that
schema, which would be based on the policy...).

I don't know if we want to try and support that level of flexibility
or if we'd like to simply go based on the concept of 'you only get to
see what you have access to', which I'm thinking will allow us to avoid
changing the existing functions as they are already doing permissions
checks.

My general thinking here is that we could support one set of provided
policies via these patches and then, if there is sufficient interest,
generalize how policies on system catalogs are handled and eventually
allow users to redefine the system catalog policies.

Thanks!

Stephen

#29Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Haribabu Kommi (#26)
Re: Multi-tenancy with RLS

On 2016/01/06 13:07, Haribabu Kommi wrote:

On Wed, Jan 6, 2016 at 1:43 PM, Amit Langote

Patch 4_database_catalog_tenancy_v5 fails to apply:

May be you missed to apply the 3_shared_catalog_tenancy_v4 path,
because 4_database_catalog_tenancy_v5 patch depends on it.

Oops, I even missed patches 1 and 2 at all.

Here I attached all the patches for your convenience, I am able to
apply all patches in the order without any problem.

Okay, thanks!

I applied all the patches. I have a basic question. Sorry though if I've
entirely missed the point (and/or scope) of your proposal. I wonder if
something like the following should not have failed with the patch:

postgres=# CREATE POLICY class_policy ON pg_class TO PUBLIC USING
(relowner = current_user);
ERROR: permission denied: "pg_class" is a system catalog

Is there no support yet for user-defined catalog policies?

Regards,
Amit

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#30Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Amit Langote (#29)
1 attachment(s)
Re: Multi-tenancy with RLS

On Thu, Jan 7, 2016 at 4:31 PM, Amit Langote
<Langote_Amit_f8@lab.ntt.co.jp> wrote:

I applied all the patches. I have a basic question. Sorry though if I've
entirely missed the point (and/or scope) of your proposal. I wonder if
something like the following should not have failed with the patch:

postgres=# CREATE POLICY class_policy ON pg_class TO PUBLIC USING
(relowner = current_user);
ERROR: permission denied: "pg_class" is a system catalog

Is there no support yet for user-defined catalog policies?

Currently the patches don't have the support of allowing user to
create policies on catalog tables. The policies similar like you
specified are prepared for all eligible catalog tables and those
will be used when the user enables the catalog security.

Presently, default policies are used to provide proper multi-tenancy
behavior. May be we can add the support of user to update the
existing policies and add new policies on the catalog tables
without dropping the creation of default polices, as these are
required for supporting multi-tenancy by default without any
user policies.

Example usage:

postgres=# create role test_user1;
CREATE ROLE
postgres=# create role test_user2;
CREATE ROLE
postgres=# alter database postgres with catalog security true;
ALTER DATABASE
postgres=# set session authorization test_user1;
SET
postgres=> create table tbl1(f1 int);
CREATE TABLE
postgres=> set session authorization test_user2;
SET
postgres=> create table tbl2(f2 int);
CREATE TABLE
postgres=> \d
List of relations
Schema | Name | Type | Owner
--------+------+-------+------------
public | tbl2 | table | test_user2
(1 row)

postgres=> select attrelid, attname from pg_attribute where attname
like 'f%' and attrelid > 16345;
attrelid | attname
----------+---------
16389 | f2
(1 row)

postgres=> set session authorization test_user1;
SET
postgres=> \d
List of relations
Schema | Name | Type | Owner
--------+------+-------+------------
public | tbl1 | table | test_user1
(1 row)

postgres=> select attrelid, attname from pg_attribute where attname
like 'f%' and attrelid > 16345;
attrelid | attname
----------+---------
16386 | f1
(1 row)

Without multi-tenancy patches, both users can see two tables
and columns that are created.

Turning off catalog security is not working in earlier patches,
because of a using a wrong tuple. updated patch is attached.

Regards,
Hari Babu
Fujitsu Australia

Attachments:

4_database_catalog_tenancy_v6.patchapplication/octet-stream; name=4_database_catalog_tenancy_v6.patchDownload
diff --git a/doc/src/sgml/ref/alter_database.sgml b/doc/src/sgml/ref/alter_database.sgml
index cfc28cf..adc3326 100644
--- a/doc/src/sgml/ref/alter_database.sgml
+++ b/doc/src/sgml/ref/alter_database.sgml
@@ -28,6 +28,7 @@ ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> [ [ WITH ] <rep
     ALLOW_CONNECTIONS <replaceable class="PARAMETER">allowconn</replaceable>
     CONNECTION LIMIT <replaceable class="PARAMETER">connlimit</replaceable>
     IS_TEMPLATE <replaceable class="PARAMETER">istemplate</replaceable>
+    CATALOG SECURITY <replaceable class="PARAMETER">catalog_security</replaceable>
 
 ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> RENAME TO <replaceable>new_name</replaceable>
 
@@ -139,6 +140,18 @@ ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> RESET ALL
         </para>
        </listitem>
       </varlistentry>
+      
+     <varlistentry>
+       <term><replaceable class="parameter">catalog_security</replaceable></term>
+       <listitem>
+        <para>
+         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.
+        </para>
+       </listitem>
+     </varlistentry>
 
    <varlistentry>
     <term><replaceable>new_name</replaceable></term>
@@ -203,6 +216,12 @@ ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> RESET ALL
    Role-specific settings override database-specific
    ones if there is a conflict.
   </para>
+  
+  <para>
+   The catalog_security option is not supported at <xref linkend="sql-createdatabase">
+   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.
+  </para>
  </refsect1>
 
  <refsect1>
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index c1c0223..f7b7141 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -42,6 +42,7 @@
 #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,6 +227,13 @@ createdb(const CreatedbStmt *stmt)
 					 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,6 +527,7 @@ createdb(const CreatedbStmt *stmt)
 	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,11 +1384,14 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 	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,6 +1433,15 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 						 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,6 +1478,8 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 					(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,12 +1499,19 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 				(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,6 +1523,17 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 				(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,6 +1556,11 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 		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);
diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c
index 820261e..62a3ae2 100644
--- a/src/backend/commands/policy.c
+++ b/src/backend/commands/policy.c
@@ -22,19 +22,62 @@
 #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_auth_members.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_database.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_pltemplate.h"
 #include "catalog/pg_policy.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_range.h"
 #include "catalog/pg_replication_origin.h"
+#include "catalog/pg_rewrite.h"
+#include "catalog/pg_seclabel.h"
 #include "catalog/pg_shdepend.h"
 #include "catalog/pg_shdescription.h"
 #include "catalog/pg_shseclabel.h"
+#include "catalog/pg_statistic.h"
 #include "catalog/pg_tablespace.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"
@@ -54,6 +97,7 @@
 #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 65536
@@ -514,6 +558,26 @@ get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *bu
 
 	switch (relationid)
 	{
+			/*
+			 * Following catalog tables data is accessible to all roles. So
+			 * they doesn't need any specific RLS policies on them.
+			 */
+		case AccessMethodRelationId:
+		case AccessMethodOperatorRelationId:
+		case AccessMethodProcedureRelationId:
+		case AggregateRelationId:
+			policy_exist = false;
+			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);
+			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);
+			break;
 		case AuthIdRelationId:
 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
 					" (pg_has_role(oid, 'any'))",
@@ -524,6 +588,22 @@ get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *bu
 			  " (pg_has_role(roleid, 'any') AND pg_has_role(member, 'any'))",
 				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
 			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);
+			break;
+		case CollationRelationId:
+			policy_exist = false;
+			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);
+			break;
+		case ConversionRelationId:
+			policy_exist = false;
+			break;
 		case DatabaseRelationId:
 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
 					" ((oid < 16384) OR has_database_privilege(oid,'any'))",
@@ -534,12 +614,175 @@ get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *bu
 					" (pg_has_role(setrole, 'any') AND has_database_privilege(setdatabase,'any'))",
 				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
 			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);
+			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);
+			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);
+			break;
+		case EnumRelationId:
+		case EventTriggerRelationId:
+		case ExtensionRelationId:
+			policy_exist = false;
+			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);
+			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);
+			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);
+			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);
+			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);
+			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);
+			break;
+		case LargeObjectRelationId:
+		case LargeObjectMetadataRelationId:
+			policy_exist = false;
+			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);
+			break;
+		case OperatorClassRelationId:
+		case OperatorRelationId:
+		case OperatorFamilyRelationId:
 		case PLTemplateRelationId:
 			policy_exist = false;
 			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);
+			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);
+			break;
+		case RangeRelationId:
+			policy_exist = false;
+			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);
+			break;
 		case ReplicationOriginRelationId:
+		case RewriteRelationId:
 			policy_exist = false;
 			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);
+			break;
 		case SharedDependRelationId:
 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
 					" ((classid = (select oid from pg_class where relname = 'pg_database') AND has_database_privilege(objid, 'any'))"
@@ -561,11 +804,42 @@ get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *bu
 					" 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);
 			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);
+			break;
 		case TableSpaceRelationId:
 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
 				  " ((oid < 16384) OR has_tablespace_privilege(oid, 'any'))",
 				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
 			break;
+		case TransformRelationId:
+			policy_exist = false;
+			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);
+			break;
+		case TSConfigRelationId:
+		case TSConfigMapRelationId:
+		case TSDictionaryRelationId:
+		case TSParserRelationId:
+		case TSTemplateRelationId:
+			policy_exist = false;
+			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);
+			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);
+			break;
 		default:
 			policy_exist = false;
 			break;
@@ -575,6 +849,163 @@ get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *bu
 }
 
 /*
+ * 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)
+		{
+			HeapTuple	cache_tuple;
+			Form_pg_class pg_class_tuple;
+			bool		policy_exist;
+
+			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));
+
+			pg_class_tuple = (Form_pg_class) GETSTRUCT(cache_tuple);
+			policy_exist = get_catalog_policy_string(HeapTupleGetOid(tuple), pg_class_tuple, buf);
+			if (!policy_exist)
+			{
+				heap_freetuple(cache_tuple);
+				continue;
+			}
+
+			pg_class_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)
+		{
+			HeapTuple	cache_tuple;
+
+			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));
+
+			pg_class_tuple = (Form_pg_class) GETSTRUCT(cache_tuple);
+			if (!pg_class_tuple->relrowsecurity)
+				continue;
+
+			pg_class_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();
+}
+
+/*
  * RemoveRoleFromObjectPolicy -
  *	 remove a role from a policy by its OID.  If the role is not a member of
  *	 the policy then an error is raised.  False is returned to indicate that
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b307b48..5ceb1e5 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -8876,6 +8876,7 @@ createdb_opt_name:
 			| OWNER							{ $$ = pstrdup($1); }
 			| TABLESPACE					{ $$ = pstrdup($1); }
 			| TEMPLATE						{ $$ = pstrdup($1); }
+			| CATALOG_P SECURITY				{ $$ = pstrdup("catalog_security"); }
 		;
 
 /*
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index a07350d..7ecddc8 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -17,11 +17,19 @@
 #include <ctype.h>
 
 #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_type.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,6 +39,7 @@
 #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"
@@ -2032,10 +2041,10 @@ convert_table_priv_string(text *priv_type_text)
 		{"TRIGGER", ACL_TRIGGER},
 		{"TRIGGER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
 		{"ANY", ACL_SELECT | ACL_GRANT_OPTION_FOR(ACL_SELECT) |
-				ACL_INSERT | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
-				ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_DELETE | ACL_GRANT_OPTION_FOR(ACL_DELETE) |
-				ACL_TRUNCATE | ACL_GRANT_OPTION_FOR(ACL_TRUNCATE) | ACL_REFERENCES |
-				ACL_GRANT_OPTION_FOR(ACL_REFERENCES) | ACL_TRIGGER | ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
+			ACL_INSERT | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
+			ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_DELETE | ACL_GRANT_OPTION_FOR(ACL_DELETE) |
+			ACL_TRUNCATE | ACL_GRANT_OPTION_FOR(ACL_TRUNCATE) | ACL_REFERENCES |
+		ACL_GRANT_OPTION_FOR(ACL_REFERENCES) | ACL_TRIGGER | ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
 		{"RULE", 0},			/* ignore old RULE privileges */
 		{"RULE WITH GRANT OPTION", 0},
 		{NULL, 0}
@@ -2865,8 +2874,8 @@ convert_column_priv_string(text *priv_type_text)
 		{"REFERENCES", ACL_REFERENCES},
 		{"REFERENCES WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
 		{"ANY", ACL_SELECT | ACL_GRANT_OPTION_FOR(ACL_SELECT) | ACL_INSERT |
-				ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
-				ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_REFERENCES | ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
+			ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
+		ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_REFERENCES | ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
 		{NULL, 0}
 	};
 
@@ -3065,8 +3074,8 @@ convert_database_priv_string(text *priv_type_text)
 		{"CONNECT", ACL_CONNECT},
 		{"CONNECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
 		{"ANY", ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE) |
-									ACL_CREATE_TEMP | ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CREATE_TEMP |
-									ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CONNECT | ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
+			ACL_CREATE_TEMP | ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CREATE_TEMP |
+		ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CONNECT | ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
 		{NULL, 0}
 	};
 
@@ -5267,3 +5276,293 @@ get_rolespec_name(const Node *node)
 
 	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));
+}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index fc5b9d9..44756d6 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -2078,6 +2078,8 @@ RelationClearRelation(Relation relation, bool rebuild)
 	 */
 	if (relation->rd_isnailed)
 	{
+		HeapTuple	pg_class_tuple;
+
 		RelationInitPhysicalAddr(relation);
 
 		if (relation->rd_rel->relkind == RELKIND_INDEX)
@@ -2086,6 +2088,28 @@ RelationClearRelation(Relation relation, bool rebuild)
 			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;
 	}
 
diff --git a/src/include/catalog/pg_database.h b/src/include/catalog/pg_database.h
index 6ae1b40..1816d8b 100644
--- a/src/include/catalog/pg_database.h
+++ b/src/include/catalog/pg_database.h
@@ -43,7 +43,7 @@ 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 */
-
+	bool		datcatalogsecurity; /* catalog security is enabled? */
 #ifdef CATALOG_VARLEN			/* variable-length fields start here */
 	aclitem		datacl[1];		/* access permissions */
 #endif
@@ -60,22 +60,23 @@ 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
+#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 _null_));
+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
 
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 9250545..60dd7b3 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3646,6 +3646,24 @@ DESCR("current user privilege on role by role name");
 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 = 3321 (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 = 3322 (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 = 3323 (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 = 3324 (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 = 3325 (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 = 3326 (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_ ));
diff --git a/src/include/commands/policy.h b/src/include/commands/policy.h
index dbf7824..453d335 100644
--- a/src/include/commands/policy.h
+++ b/src/include/commands/policy.h
@@ -35,4 +35,7 @@ extern ObjectAddress rename_policy(RenameStmt *stmt);
 
 extern bool relation_has_policies(Relation rel);
 
+extern void CreateCatalogPolicy(void);
+extern void RemoveCatalogPolicy(void);
+
 #endif   /* POLICY_H */
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index b35d206..4cf1d8b 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -106,6 +106,12 @@ extern Datum pg_has_role_id_name(PG_FUNCTION_ARGS);
 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);
diff --git a/src/test/regress/expected/multitenancy.out b/src/test/regress/expected/multitenancy.out
new file mode 100644
index 0000000..cbdb1fa
--- /dev/null
+++ b/src/test/regress/expected/multitenancy.out
@@ -0,0 +1,644 @@
+-- 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;
+alter schema tenancy_user1 owner to tenancy_user1;
+create schema tenancy_user2;
+alter schema tenancy_user2 owner 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;
+ALTER SERVER tenancy_server OWNER 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 from pg_namespace where nspname = 'tenancy_user1';
+    nspname    
+---------------
+ tenancy_user1
+(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_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 from pg_namespace where nspname = 'tenancy_user1';
+ nspname 
+---------
+(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_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
+drop schema tenancy_user1;
+RESET ROLE;
+SET SESSION ROLE tenancy_user2;
+DROP FUNCTION tenancy_user2_tenancytestfunc(text);
+DROP FOREIGN TABLE tenancy_user2_ft1;
+DROP LANGUAGE tenancy_lang1;
+drop schema tenancy_user2;
+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 role tenancy_user1;
+drop role tenancy_user2;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index b1bc7c7..04939da 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -60,7 +60,7 @@ 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
+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 *
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index ade9ef1..8832ef1 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -160,4 +160,5 @@ test: largeobject
 test: with
 test: xml
 test: event_trigger
+test: multitenancy
 test: stats
diff --git a/src/test/regress/sql/multitenancy.sql b/src/test/regress/sql/multitenancy.sql
new file mode 100644
index 0000000..0b774a5
--- /dev/null
+++ b/src/test/regress/sql/multitenancy.sql
@@ -0,0 +1,297 @@
+-- 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;
+alter schema tenancy_user1 owner to tenancy_user1;
+
+create schema tenancy_user2;
+alter schema tenancy_user2 owner 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;
+ALTER SERVER tenancy_server OWNER 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 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_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 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_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;
+drop schema tenancy_user1;
+
+RESET ROLE;
+
+SET SESSION ROLE tenancy_user2;
+
+DROP FUNCTION tenancy_user2_tenancytestfunc(text);
+DROP FOREIGN TABLE tenancy_user2_ft1;
+DROP LANGUAGE tenancy_lang1;
+drop schema tenancy_user2;
+
+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 role tenancy_user1;
+drop role tenancy_user2;
#31Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Stephen Frost (#28)
Re: Multi-tenancy with RLS

On Thu, Jan 7, 2016 at 2:29 PM, Stephen Frost <sfrost@snowman.net> wrote:

Robert,

* Robert Haas (robertmhaas@gmail.com) wrote:

Apart from the issue of whether this is doomed for some architectural
reason, it is not entirely clear to me that there's any consensus that
we want this. I don't think that I understand the issues here well
enough to proffer an opinion of my own just yet... but I'd like to
hear what other people think.

I'm certainly of the opinion that we want this or something similar.

The big caveat kicking around in my head is if we want to have our own
set of defined policies or if we want to give flexibility to the
administrator to define their own policies. In particular, I'm
wondering about things like:

CREATE POLICY namespace_limit ON pg_namespace TO company1 USING
(substring(nspname,1,8) = 'company1_');

Which is a bit different, as I understand it, from what Haribadu has
been proposing and quite a bit more complicated, as we'd then have to
make the internal lookups respect the policy (so things like CREATE
SCHEMA would have to check if you're allowed to actually create that
schema, which would be based on the policy...).

I feel we may needed both our own set of policies and also providing
the user to create/alter/drop the catalog policies. This way we can
support both simple and complex scenarios. With default policies
an user can setup multi-tenancy easily. With the help of edit option,
user can tune the policies according to their scenarios.

The one problem with either approach as i am thinking, currently with
our own set of policies, the objects entries that are present on the
catalog tables are visible to the users, those are having any kind of
privileges on those objects. In case if a user tries to create an object
that is already present in the catalog relation will produce an error, but
user cannot view that object because of permissions problem.

To avoid such problem, administrator has to add policies such as
"namespace_prefix" needs to be added to all catalog tables.

Regards,
Hari Babu
Fujitsu Australia

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#32Amit Langote
Langote_Amit_f8@lab.ntt.co.jp
In reply to: Haribabu Kommi (#30)
Re: Multi-tenancy with RLS

On 2016/01/07 15:25, Haribabu Kommi wrote:

On Thu, Jan 7, 2016 at 4:31 PM, Amit Langote
<Langote_Amit_f8@lab.ntt.co.jp> wrote:

I applied all the patches. I have a basic question. Sorry though if I've
entirely missed the point (and/or scope) of your proposal. I wonder if
something like the following should not have failed with the patch:

postgres=# CREATE POLICY class_policy ON pg_class TO PUBLIC USING
(relowner = current_user);
ERROR: permission denied: "pg_class" is a system catalog

Is there no support yet for user-defined catalog policies?

Currently the patches don't have the support of allowing user to
create policies on catalog tables. The policies similar like you
specified are prepared for all eligible catalog tables and those
will be used when the user enables the catalog security.

Presently, default policies are used to provide proper multi-tenancy
behavior. May be we can add the support of user to update the
existing policies and add new policies on the catalog tables
without dropping the creation of default polices, as these are
required for supporting multi-tenancy by default without any
user policies.

Okay. Thanks for explaining.

Example usage:

postgres=# create role test_user1;
CREATE ROLE
postgres=# create role test_user2;
CREATE ROLE
postgres=# alter database postgres with catalog security true;
ALTER DATABASE
postgres=# set session authorization test_user1;
SET
postgres=> create table tbl1(f1 int);
CREATE TABLE
postgres=> set session authorization test_user2;
SET
postgres=> create table tbl2(f2 int);
CREATE TABLE
postgres=> \d
List of relations
Schema | Name | Type | Owner
--------+------+-------+------------
public | tbl2 | table | test_user2
(1 row)

Okay, so the patch's system-defined policy seems enough to achieve this
much isolation.

By the way, if test_user2 had created a table with the same name, it would
produce the following error:

ERROR: relation "tbl1" already exists

So, certain information (relname in this case) is bound to be leaked here
because syscache look-ups don't abide by catalog RLS. But I guess hoping
that it would may be being overly paranoid and if pursued at all, an
entirely separate project.

Thanks,
Amit

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#33Joe Conway
mail@joeconway.com
In reply to: Robert Haas (#27)
Re: Multi-tenancy with RLS

On 01/06/2016 12:15 PM, Robert Haas wrote:

On Tue, Jan 5, 2016 at 11:07 PM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

May be you missed to apply the 3_shared_catalog_tenancy_v4 path,
because 4_database_catalog_tenancy_v5 patch depends on it.

Here I attached all the patches for your convenience, I am able to
apply all patches in the order without any problem.

Is any committer thinking about taking a serious look at this patch series?

I ask because (1) it seems like it could be nice to have but (2) it
frightens me terribly. We are generally very sparing about assuming
that "stuff" (triggers, partial indexes, etc.) that works for user
tables can also be made to work for system tables. I haven't thought
deeply about what might go wrong in this particular case, but it
strikes me that if Haribabu Kommi is building something that is doomed
for some reason, it would be good to figure that out before he spends
any more time on it than he already has.

As Stephen mentioned, yes, I am very interested in at least some aspects
of this patch. The ability to apply RLS to system tables could be useful
to solve a number of problems we don't have a good story for today,
multi-tenancy only being one of them.

Apart from the issue of whether this is doomed for some architectural
reason, it is not entirely clear to me that there's any consensus that
we want this. I don't think that I understand the issues here well
enough to proffer an opinion of my own just yet... but I'd like to
hear what other people think.

As said above, I definitely see a need for something like this if not
this specifically.

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

#34Tom Lane
tgl@sss.pgh.pa.us
In reply to: Joe Conway (#33)
Re: Multi-tenancy with RLS

Joe Conway <mail@joeconway.com> writes:

On 01/06/2016 12:15 PM, Robert Haas wrote:

Is any committer thinking about taking a serious look at this patch series?

I ask because (1) it seems like it could be nice to have but (2) it
frightens me terribly. We are generally very sparing about assuming
that "stuff" (triggers, partial indexes, etc.) that works for user
tables can also be made to work for system tables. I haven't thought
deeply about what might go wrong in this particular case, but it
strikes me that if Haribabu Kommi is building something that is doomed
for some reason, it would be good to figure that out before he spends
any more time on it than he already has.

As Stephen mentioned, yes, I am very interested in at least some aspects
of this patch. The ability to apply RLS to system tables could be useful
to solve a number of problems we don't have a good story for today,
multi-tenancy only being one of them.

FWIW, it seems offhand like we might not have that much trouble with
applying RLS to system catalogs as long as it's understood that RLS
only has anything to do with SQL queries issued against the catalogs.

If we imagine that RLS should somehow filter a backend's own operations on
the catalogs, then I agree with Robert that the entire thing is deeply
scary and probably incapable of being made to work robustly.

However, by "not that much trouble" I only mean getting an implementation
that works and doesn't create more security problems than it fixes.
Usability is still likely to be a huge problem. In particular it seems
likely that any attempt to actually put RLS policies on the catalogs would
completely destroy the ability to run pg_dump except as a BYPASSRLS role.
That would be an unpleasant consequence.

I've not read the patch set, so maybe it contains some ideas about how
to mitigate the usability issues, in which case never mind ...

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#35Stephen Frost
sfrost@snowman.net
In reply to: Tom Lane (#34)
Re: Multi-tenancy with RLS

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Joe Conway <mail@joeconway.com> writes:

As Stephen mentioned, yes, I am very interested in at least some aspects
of this patch. The ability to apply RLS to system tables could be useful
to solve a number of problems we don't have a good story for today,
multi-tenancy only being one of them.

FWIW, it seems offhand like we might not have that much trouble with
applying RLS to system catalogs as long as it's understood that RLS
only has anything to do with SQL queries issued against the catalogs.

Right, that's what this patch set is about.

If we imagine that RLS should somehow filter a backend's own operations on
the catalogs, then I agree with Robert that the entire thing is deeply
scary and probably incapable of being made to work robustly.

Personally, I like the idea of the capability, but I also agree that
it'd be a great deal more challenging to do and would require a lot of
pretty invasive and scary changes. Hence, my thinking was that we'd
define our own set of policies which mimic what we already do through
the permissions system (thus, only impacting SQL queries against the
catalog and not anything about how the backend accesses the catalogs).

I'm on the fence about if we'd allow those policies to be modified by
users or not.

However, by "not that much trouble" I only mean getting an implementation
that works and doesn't create more security problems than it fixes.
Usability is still likely to be a huge problem. In particular it seems
likely that any attempt to actually put RLS policies on the catalogs would
completely destroy the ability to run pg_dump except as a BYPASSRLS role.
That would be an unpleasant consequence.

I don't follow how this would destroy the ability to run pg_dump.
Ideally, we'd have a result where a user could run pg_dump without
having to apply any filters of their own and they'd get a dump of all
objects they're allowed to see.

Thanks!

Stephen

#36Tom Lane
tgl@sss.pgh.pa.us
In reply to: Stephen Frost (#35)
Re: Multi-tenancy with RLS

Stephen Frost <sfrost@snowman.net> writes:

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

However, by "not that much trouble" I only mean getting an implementation
that works and doesn't create more security problems than it fixes.
Usability is still likely to be a huge problem. In particular it seems
likely that any attempt to actually put RLS policies on the catalogs would
completely destroy the ability to run pg_dump except as a BYPASSRLS role.
That would be an unpleasant consequence.

I don't follow how this would destroy the ability to run pg_dump.
Ideally, we'd have a result where a user could run pg_dump without
having to apply any filters of their own and they'd get a dump of all
objects they're allowed to see.

You mean, other than the fact that pg_dump sets row_security = off
to ensure that what it's seeing *isn't* filtered.

The bigger picture here is that I do not think that you can just
arbitrarily exclude non-owned objects from its view and still expect to
get a valid dump; that will break dependency chains for example, possibly
leading to stuff getting output in an order that doesn't restore.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#37Stephen Frost
sfrost@snowman.net
In reply to: Tom Lane (#36)
Re: Multi-tenancy with RLS

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

However, by "not that much trouble" I only mean getting an implementation
that works and doesn't create more security problems than it fixes.
Usability is still likely to be a huge problem. In particular it seems
likely that any attempt to actually put RLS policies on the catalogs would
completely destroy the ability to run pg_dump except as a BYPASSRLS role.
That would be an unpleasant consequence.

I don't follow how this would destroy the ability to run pg_dump.
Ideally, we'd have a result where a user could run pg_dump without
having to apply any filters of their own and they'd get a dump of all
objects they're allowed to see.

You mean, other than the fact that pg_dump sets row_security = off
to ensure that what it's seeing *isn't* filtered.

There's a specific option to turn it back on already though. This
wouldn't change that.

The bigger picture here is that I do not think that you can just
arbitrarily exclude non-owned objects from its view and still expect to
get a valid dump; that will break dependency chains for example, possibly
leading to stuff getting output in an order that doesn't restore.

We already have that issue when users select to dump out specific
schemas, I don't see this as being any different. The idea behind
multi-tenancy is, generally speaking, you don't see or have any
references or dependencies with what other people have. In those cases,
there won't be any dependencies to objects that you can't see.

Thanks!

Stephen

#38Tom Lane
tgl@sss.pgh.pa.us
In reply to: Stephen Frost (#37)
Re: Multi-tenancy with RLS

Stephen Frost <sfrost@snowman.net> writes:

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

I don't follow how this would destroy the ability to run pg_dump.
Ideally, we'd have a result where a user could run pg_dump without
having to apply any filters of their own and they'd get a dump of all
objects they're allowed to see.

You mean, other than the fact that pg_dump sets row_security = off
to ensure that what it's seeing *isn't* filtered.

There's a specific option to turn it back on already though.

Whereupon you'd have no certainty that what you got represented a
complete dump of your own data.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#39Stephen Frost
sfrost@snowman.net
In reply to: Tom Lane (#38)
Re: Multi-tenancy with RLS

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Stephen Frost <sfrost@snowman.net> writes:

I don't follow how this would destroy the ability to run pg_dump.
Ideally, we'd have a result where a user could run pg_dump without
having to apply any filters of their own and they'd get a dump of all
objects they're allowed to see.

You mean, other than the fact that pg_dump sets row_security = off
to ensure that what it's seeing *isn't* filtered.

There's a specific option to turn it back on already though.

Whereupon you'd have no certainty that what you got represented a
complete dump of your own data.

It would be a dump of what you're allowed to see, rather than an error
saying you couldn't dump something you couldn't see, which is the
alternative we're talking about here. Even if you've got a dependency
to something-or-other, if you don't have access to it, then you're
going to get an error.

In practice, you have to make sure to remember to include all of your
schemas when you pg_dump, and don't get it wrong or you'll get an error
(you don't have access to some schema referenced) or a subset of what
you intended (you forgot to include one you meant to). That is not a
better user experience than being able to say "dump out everything I've
got access to."

In many, many use-cases that's exactly what you want. pg_dump is more
than just a whole-database backup tool, and when it's used as a
whole-database backup tool, you'll need to make sure it has BYPASSRLS or
is a superuser or you could end up getting errors. I don't see any
issue with that.

If the policies are incorrect then that'd be a problem, but I'm
certainly hopeful that we'd be able to get that right.

Thanks!

Stephen

#40Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Stephen Frost (#39)
Re: Multi-tenancy with RLS

I've closed this as returned-with-feedback.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#41Robert Haas
robertmhaas@gmail.com
In reply to: Stephen Frost (#39)
Re: Multi-tenancy with RLS

On Fri, Jan 15, 2016 at 11:53 AM, Stephen Frost <sfrost@snowman.net> wrote:

Whereupon you'd have no certainty that what you got represented a
complete dump of your own data.

It would be a dump of what you're allowed to see, rather than an error
saying you couldn't dump something you couldn't see, which is the
alternative we're talking about here. Even if you've got a dependency
to something-or-other, if you don't have access to it, then you're
going to get an error.

I think you're dismissing Tom's concerns far too lightly. The
row_security=off mode, which is the default, becomes unusable for
non-superusers under this proposal. That's bad. And if you switch to
the other mode, then you might accidentally fail to get all of the
data in some table you're trying to back up. That's bad too: that's
why it isn't the default. There's a big difference between saying
"I'm OK with not dumping the tables I can't see" and "I'm OK with not
dumping all of the data in some table I *can* see".

It seems to me that there's a big difference between policies we ship
out of the box and policies that are created be users: specifically,
the former can be assumed benign, while the latter can't. I think
that difference matters here, although I'm not sure exactly where to
go with it.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#42Joe Conway
mail@joeconway.com
In reply to: Robert Haas (#41)
Re: Multi-tenancy with RLS

On 02/09/2016 11:47 AM, Robert Haas wrote:

On Fri, Jan 15, 2016 at 11:53 AM, Stephen Frost <sfrost@snowman.net> wrote:

Whereupon you'd have no certainty that what you got represented a
complete dump of your own data.

It would be a dump of what you're allowed to see, rather than an error
saying you couldn't dump something you couldn't see, which is the
alternative we're talking about here. Even if you've got a dependency
to something-or-other, if you don't have access to it, then you're
going to get an error.

I think you're dismissing Tom's concerns far too lightly. The
row_security=off mode, which is the default, becomes unusable for
non-superusers under this proposal. That's bad. And if you switch to
the other mode, then you might accidentally fail to get all of the
data in some table you're trying to back up. That's bad too: that's
why it isn't the default. There's a big difference between saying
"I'm OK with not dumping the tables I can't see" and "I'm OK with not
dumping all of the data in some table I *can* see".

I don't grok what you're saying here. If I, as a non-superuser could
somehow see all the rows in a table just by running pg_dump, including
rows that I could not normally see due to RLS policies, *that* would be
bad. I should have no expectation of being able to dump rows I can't
normally see.

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

#43Robert Haas
robertmhaas@gmail.com
In reply to: Joe Conway (#42)
Re: Multi-tenancy with RLS

On Tue, Feb 9, 2016 at 3:01 PM, Joe Conway <mail@joeconway.com> wrote:

On 02/09/2016 11:47 AM, Robert Haas wrote:

On Fri, Jan 15, 2016 at 11:53 AM, Stephen Frost <sfrost@snowman.net> wrote:

Whereupon you'd have no certainty that what you got represented a
complete dump of your own data.

It would be a dump of what you're allowed to see, rather than an error
saying you couldn't dump something you couldn't see, which is the
alternative we're talking about here. Even if you've got a dependency
to something-or-other, if you don't have access to it, then you're
going to get an error.

I think you're dismissing Tom's concerns far too lightly. The
row_security=off mode, which is the default, becomes unusable for
non-superusers under this proposal. That's bad. And if you switch to
the other mode, then you might accidentally fail to get all of the
data in some table you're trying to back up. That's bad too: that's
why it isn't the default. There's a big difference between saying
"I'm OK with not dumping the tables I can't see" and "I'm OK with not
dumping all of the data in some table I *can* see".

I don't grok what you're saying here. If I, as a non-superuser could
somehow see all the rows in a table just by running pg_dump, including
rows that I could not normally see due to RLS policies, *that* would be
bad. I should have no expectation of being able to dump rows I can't
normally see.

That's true. But I should also have an expectation that running
pg_dump won't trigger arbitrary code execution, which is why by
default, pg_dump sets row_security to OFF. That way, if a row
security policy applies, I get an error rather than an incomplete,
possibly-invalid dump (and arbitrary code execution on the server
side). If I'm OK with doing the dump subject to row security, I can
rerun with --enable-row-security. But this proposal would force
non-superusers to always use that option, and that's not a good idea.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#44Joshua D. Drake
jd@commandprompt.com
In reply to: Robert Haas (#43)
Re: Multi-tenancy with RLS

On 02/09/2016 12:05 PM, Robert Haas wrote:

That's true. But I should also have an expectation that running
pg_dump won't trigger arbitrary code execution, which is why by
default, pg_dump sets row_security to OFF. That way, if a row
security policy applies, I get an error rather than an incomplete,
possibly-invalid dump (and arbitrary code execution on the server
side). If I'm OK with doing the dump subject to row security, I can
rerun with --enable-row-security. But this proposal would force
non-superusers to always use that option, and that's not a good idea.

If I understand correctly what we are talking about here is:

1. If RLS is enabled and a non-super user issues a pg_dump, it will
error unless I issue --enable-row-security

2. If RLS is not enabled and a non-super user issues a pg_dump the
behavior is basically what it is now.

3. If RLS is enabled or not and I am a super user, it doesn't matter
either way.

From my perspective, I should not have to enable row security as a
non-super user to take a pg_dump. It should just work for what I am
allowed to see. In other words:

pg_dump -U $non-super_user

Should just work, period.

Sincerely,

Joshua D. Drake

--
Command Prompt, Inc. http://the.postgres.company/
+1-503-667-4564
PostgreSQL Centered full stack support, consulting and development.
Everyone appreciates your honesty, until you are honest with them.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#45Stephen Frost
sfrost@snowman.net
In reply to: Robert Haas (#43)
Re: Multi-tenancy with RLS

Robert,

* Robert Haas (robertmhaas@gmail.com) wrote:

On Tue, Feb 9, 2016 at 3:01 PM, Joe Conway <mail@joeconway.com> wrote:

On 02/09/2016 11:47 AM, Robert Haas wrote:

On Fri, Jan 15, 2016 at 11:53 AM, Stephen Frost <sfrost@snowman.net> wrote:

Whereupon you'd have no certainty that what you got represented a
complete dump of your own data.

It would be a dump of what you're allowed to see, rather than an error
saying you couldn't dump something you couldn't see, which is the
alternative we're talking about here. Even if you've got a dependency
to something-or-other, if you don't have access to it, then you're
going to get an error.

I think you're dismissing Tom's concerns far too lightly. The
row_security=off mode, which is the default, becomes unusable for
non-superusers under this proposal. That's bad. And if you switch to
the other mode, then you might accidentally fail to get all of the
data in some table you're trying to back up. That's bad too: that's
why it isn't the default. There's a big difference between saying
"I'm OK with not dumping the tables I can't see" and "I'm OK with not
dumping all of the data in some table I *can* see".

I don't grok what you're saying here. If I, as a non-superuser could
somehow see all the rows in a table just by running pg_dump, including
rows that I could not normally see due to RLS policies, *that* would be
bad. I should have no expectation of being able to dump rows I can't
normally see.

That's true. But I should also have an expectation that running
pg_dump won't trigger arbitrary code execution, which is why by
default, pg_dump sets row_security to OFF. That way, if a row
security policy applies, I get an error rather than an incomplete,
possibly-invalid dump (and arbitrary code execution on the server
side). If I'm OK with doing the dump subject to row security, I can
rerun with --enable-row-security. But this proposal would force
non-superusers to always use that option, and that's not a good idea.

Arbitrary code execution is quite a different concern from the prior
concern regarding incomplete dumps.

To the extent that untrusted code execution is an issue (and my
experience with environments which would deploy RLS tells me that it
isn't a practical concern), an option could be created which would cause
an error to be thrown on non-catalog RLS being run.

When it comes to multi-tenancy environments, as this thread is about,
chances are the only tables you can see are ones which you own or are
owned by a trusted user, which is why I don't view this as a pratical
concern, but I'm not against having a solution to address the issue
raised regarding arbitrary code execution, provided it doesn't create
more problems than it purports to solve.

Thanks!

Stephen

#46Stephen Frost
sfrost@snowman.net
In reply to: Joshua D. Drake (#44)
Re: Multi-tenancy with RLS

JD,

* Joshua D. Drake (jd@commandprompt.com) wrote:

pg_dump -U $non-super_user

Should just work, period.

That ship has sailed already, where you're running a pg_dump against
objects you don't own and which have RLS enabled on them.

Thanks!

Stephen

#47Robert Haas
robertmhaas@gmail.com
In reply to: Stephen Frost (#45)
Re: Multi-tenancy with RLS

On Tue, Feb 9, 2016 at 3:26 PM, Stephen Frost <sfrost@snowman.net> wrote:

Arbitrary code execution is quite a different concern from the prior
concern regarding incomplete dumps.

I've had both concerns all along, and I think I've mentioned them before.

To the extent that untrusted code execution is an issue (and my
experience with environments which would deploy RLS tells me that it
isn't a practical concern), an option could be created which would cause
an error to be thrown on non-catalog RLS being run.

There's a major release already in the wild that doesn't behave that
way. And anyway I think that's missing the point: it's true that
features that are turned off don't cause problems, but features that
are turned on shouldn't break things either.

When it comes to multi-tenancy environments, as this thread is about,
chances are the only tables you can see are ones which you own or are
owned by a trusted user, which is why I don't view this as a pratical
concern, but I'm not against having a solution to address the issue
raised regarding arbitrary code execution, provided it doesn't create
more problems than it purports to solve.

Well, I'm against accepting this patch without such a solution.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#48Robert Haas
robertmhaas@gmail.com
In reply to: Stephen Frost (#46)
Re: Multi-tenancy with RLS

On Tue, Feb 9, 2016 at 3:28 PM, Stephen Frost <sfrost@snowman.net> wrote:

JD,

* Joshua D. Drake (jd@commandprompt.com) wrote:

pg_dump -U $non-super_user

Should just work, period.

That ship has sailed already, where you're running a pg_dump against
objects you don't own and which have RLS enabled on them.

But you'll get an error rather than an incomplete dump, and you won't
run some code that you didn't want to run. Those distinctions matter.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#49Stephen Frost
sfrost@snowman.net
In reply to: Robert Haas (#47)
Re: Multi-tenancy with RLS

* Robert Haas (robertmhaas@gmail.com) wrote:

On Tue, Feb 9, 2016 at 3:26 PM, Stephen Frost <sfrost@snowman.net> wrote:

To the extent that untrusted code execution is an issue (and my
experience with environments which would deploy RLS tells me that it
isn't a practical concern), an option could be created which would cause
an error to be thrown on non-catalog RLS being run.

There's a major release already in the wild that doesn't behave that
way.

I'm at a loss as to what you're getting at there. We don't have any
catalog RLS, and when it comes to non-catalog RLS, we do have an option
to throw an error when it's going to be run (and it's the default, as
you pointed out), in the one major version which supports RLS.

And anyway I think that's missing the point: it's true that
features that are turned off don't cause problems, but features that
are turned on shouldn't break things either.

I don't, generally, disagree with that statement, but we have to agree
on what's on vs. off and what is broken vs. working correctly. See
nearby comments from JD about how non-superuser pg_dump could be seen as
broken when running against an environment where RLS is in use.

When it comes to multi-tenancy environments, as this thread is about,
chances are the only tables you can see are ones which you own or are
owned by a trusted user, which is why I don't view this as a pratical
concern, but I'm not against having a solution to address the issue
raised regarding arbitrary code execution, provided it doesn't create
more problems than it purports to solve.

Well, I'm against accepting this patch without such a solution.

That's at least something which can be built upon then to help this
progress.

Thanks!

Stephen

#50Joshua D. Drake
jd@commandprompt.com
In reply to: Stephen Frost (#46)
Re: Multi-tenancy with RLS

On 02/09/2016 12:28 PM, Stephen Frost wrote:

JD,

* Joshua D. Drake (jd@commandprompt.com) wrote:

pg_dump -U $non-super_user

Should just work, period.

That ship has sailed already, where you're running a pg_dump against
objects you don't own and which have RLS enabled on them.

Just to be clear, what I was suggesting is that pg_dump would just work
(and RLS would properly execute) or in other words, I shouldn't have to
tell pg_dump to enable row security else throw an error. If RLS is
enabled, then the backup just runs with appropriate permissions.

Or am I missing something?

Sincerely,

JD

Thanks!

Stephen

--
Command Prompt, Inc. http://the.postgres.company/
+1-503-667-4564
PostgreSQL Centered full stack support, consulting and development.
Everyone appreciates your honesty, until you are honest with them.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#51Stephen Frost
sfrost@snowman.net
In reply to: Joshua D. Drake (#50)
Re: Multi-tenancy with RLS

JD,

* Joshua D. Drake (jd@commandprompt.com) wrote:

On 02/09/2016 12:28 PM, Stephen Frost wrote:

* Joshua D. Drake (jd@commandprompt.com) wrote:

pg_dump -U $non-super_user

Should just work, period.

That ship has sailed already, where you're running a pg_dump against
objects you don't own and which have RLS enabled on them.

Just to be clear, what I was suggesting is that pg_dump would just
work (and RLS would properly execute) or in other words, I shouldn't
have to tell pg_dump to enable row security else throw an error. If
RLS is enabled, then the backup just runs with appropriate
permissions.

Or am I missing something?

You do have to tell pg_dump to enable RLS if you want it to be enabled
when performing a pg_dump. There's multiple reasons for this, the first
being that, otherwise, you might get an incomplete dump, and secondly,
you might execute a function that some untrusted user wrote and included
in their RLS policy. We want to avoid both of those, unless you've
specifically asked for it to be done. That's why row_security is set to
'off' by pg_dump by default.

Thanks!

Stephen

#52Dean Rasheed
dean.a.rasheed@gmail.com
In reply to: Robert Haas (#41)
Re: Multi-tenancy with RLS

On 9 February 2016 at 19:47, Robert Haas <robertmhaas@gmail.com> wrote:

I think you're dismissing Tom's concerns far too lightly. The
row_security=off mode, which is the default, becomes unusable for
non-superusers under this proposal. That's bad. And if you switch to
the other mode, then you might accidentally fail to get all of the
data in some table you're trying to back up. That's bad too: that's
why it isn't the default. There's a big difference between saying
"I'm OK with not dumping the tables I can't see" and "I'm OK with not
dumping all of the data in some table I *can* see".

It seems to me that there's a big difference between policies we ship
out of the box and policies that are created be users: specifically,
the former can be assumed benign, while the latter can't. I think
that difference matters here, although I'm not sure exactly where to
go with it.

It sounds like there needs to be a separate system_row_security
setting that controls RLS on the system catalogs, and that it should
be on by default in pg_dump.

Regards,
Dean

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#53Joe Conway
mail@joeconway.com
In reply to: Robert Haas (#48)
Re: Multi-tenancy with RLS

On 02/09/2016 12:47 PM, Robert Haas wrote:

On Tue, Feb 9, 2016 at 3:28 PM, Stephen Frost <sfrost@snowman.net> wrote:

JD,

* Joshua D. Drake (jd@commandprompt.com) wrote:

pg_dump -U $non-super_user

Should just work, period.

That ship has sailed already, where you're running a pg_dump against
objects you don't own and which have RLS enabled on them.

But you'll get an error rather than an incomplete dump, and you won't
run some code that you didn't want to run. Those distinctions matter.

From the perspective of that unprivileged user, the dump is not
incomplete -- it is exactly as complete as it is supposed to be.

Personally I don't buy that the current situation is a good thing. I
know that the "ship has sailed" and regret not having participated in
the earlier discussions, but I agree with JD here -- the unprivileged
user should not have to even think about whether RLS exists, they should
only see what they have been allowed to see by the privileged users (and
in the context of their own objects, owners are privileged). I don't
think an unprivileged user should get to decide what code runs in order
to make that happen.

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

#54Stephen Frost
sfrost@snowman.net
In reply to: Dean Rasheed (#52)
Re: Multi-tenancy with RLS

* Dean Rasheed (dean.a.rasheed@gmail.com) wrote:

On 9 February 2016 at 19:47, Robert Haas <robertmhaas@gmail.com> wrote:

I think you're dismissing Tom's concerns far too lightly. The
row_security=off mode, which is the default, becomes unusable for
non-superusers under this proposal. That's bad. And if you switch to
the other mode, then you might accidentally fail to get all of the
data in some table you're trying to back up. That's bad too: that's
why it isn't the default. There's a big difference between saying
"I'm OK with not dumping the tables I can't see" and "I'm OK with not
dumping all of the data in some table I *can* see".

It seems to me that there's a big difference between policies we ship
out of the box and policies that are created be users: specifically,
the former can be assumed benign, while the latter can't. I think
that difference matters here, although I'm not sure exactly where to
go with it.

It sounds like there needs to be a separate system_row_security
setting that controls RLS on the system catalogs, and that it should
be on by default in pg_dump.

Right, that's what I had been thinking also.

Thanks (and congrats, btw!),

Stephen

#55Tom Lane
tgl@sss.pgh.pa.us
In reply to: Joe Conway (#53)
Re: Multi-tenancy with RLS

Joe Conway <mail@joeconway.com> writes:

Personally I don't buy that the current situation is a good thing. I
know that the "ship has sailed" and regret not having participated in
the earlier discussions, but I agree with JD here -- the unprivileged
user should not have to even think about whether RLS exists, they should
only see what they have been allowed to see by the privileged users (and
in the context of their own objects, owners are privileged). I don't
think an unprivileged user should get to decide what code runs in order
to make that happen.

Part of the problem here is that we have *not* created any hard and fast
distinction between "privileged" and "unprivileged" users; I think that
even speaking in those terms about RLS risks errors in your thinking.

In particular, the code-execution issue arises from the fact that a table
owner can now cause code to execute *with the permissions of someone else*
if the someone else is foolish enough to select from his table. No
special privileges required, just the ability to create a table. If we
make pg_dump run with RLS enabled, then the "foolish" part doesn't need to
be any more foolish than forgetting a -t switch when using pg_dump.

Maybe we need to restrict that somehow, or maybe some better solution
exists that we've not thought of yet. But in its current state, RLS
is at least as much a security hazard as it is a security aid.
I do not want to see it extended in ways that make pg_dump unsafe to
use.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#56Stephen Frost
sfrost@snowman.net
In reply to: Tom Lane (#55)
Re: Multi-tenancy with RLS

Tom,

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

Joe Conway <mail@joeconway.com> writes:

Personally I don't buy that the current situation is a good thing. I
know that the "ship has sailed" and regret not having participated in
the earlier discussions, but I agree with JD here -- the unprivileged
user should not have to even think about whether RLS exists, they should
only see what they have been allowed to see by the privileged users (and
in the context of their own objects, owners are privileged). I don't
think an unprivileged user should get to decide what code runs in order
to make that happen.

Part of the problem here is that we have *not* created any hard and fast
distinction between "privileged" and "unprivileged" users; I think that
even speaking in those terms about RLS risks errors in your thinking.

I agree that where, exactly, that line is drawn is part of the issue,
and where's it's drawn is really system-dependent. In many
environments, I view Joe's comments as entirely accurate- only
privileged users are allowed to create objects *at all*. Of course,
there are a lot of environments where everyone is allowed to create
objects and, in those environments, all those users would be viewed as
"unprivileged", generally speaking.

In particular, the code-execution issue arises from the fact that a table
owner can now cause code to execute *with the permissions of someone else*
if the someone else is foolish enough to select from his table. No
special privileges required, just the ability to create a table. If we
make pg_dump run with RLS enabled, then the "foolish" part doesn't need to
be any more foolish than forgetting a -t switch when using pg_dump.

That distinction is really only relevant when it comes to pg_dump, as
those same users could use views to cause their code to be executed by
other users who are selecting from their view, and they could change if
it's a table or a view quite easily. From a practical standpoint, we're
making a huge distinction between our client tools- pg_dump must be
protected from X, but we don't have any such qualms or concerns
regarding queries sent from psql.

Perhaps that's the right distinction to make, or perhaps we should come
up with a better answer for psql than what we have now, but I don't
agree that RLS is seriously moving the goalposts, overall, here,
particularly since you're not going to have any RLS policies being
executed by pg_dump when run as a superuser anyway, given Noah's change
to how BYPASSRLS works.

Maybe we need to restrict that somehow, or maybe some better solution
exists that we've not thought of yet. But in its current state, RLS
is at least as much a security hazard as it is a security aid.
I do not want to see it extended in ways that make pg_dump unsafe to
use.

I'm not against coming up with an approach which restricts cases where
user A can write code that will be run under another user's rights,
provided it doesn't make the system overly painful to use. I don't see
RLS as changing the security risks all that much when you're talking
about regular user queries through psql, and the concern regarding
pg_dump has been addressed through the default of row_security being
off.

Thanks!

Stephen

#57Joe Conway
mail@joeconway.com
In reply to: Tom Lane (#55)
Re: Multi-tenancy with RLS

On 02/09/2016 01:22 PM, Tom Lane wrote:

Maybe we need to restrict that somehow, or maybe some better solution
exists that we've not thought of yet. But in its current state, RLS
is at least as much a security hazard as it is a security aid.
I do not want to see it extended in ways that make pg_dump unsafe to
use.

Ok, I can see that. Maybe we should have a specific GRANT for CREATE
POLICY which is distinct from the privilege to CREATE TABLE?

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

#58Stephen Frost
sfrost@snowman.net
In reply to: Joe Conway (#57)
Re: Multi-tenancy with RLS

* Joe Conway (mail@joeconway.com) wrote:

On 02/09/2016 01:22 PM, Tom Lane wrote:

Maybe we need to restrict that somehow, or maybe some better solution
exists that we've not thought of yet. But in its current state, RLS
is at least as much a security hazard as it is a security aid.
I do not want to see it extended in ways that make pg_dump unsafe to
use.

Ok, I can see that. Maybe we should have a specific GRANT for CREATE
POLICY which is distinct from the privilege to CREATE TABLE?

Well, the only privilege we have now is "CREATE", which allows creation
of any kind of object inside a schema. I'm generally in favor of
providing more granluar 'create table', 'create view', etc privileges
that can be granted out at the schema level, and 'create policy' would
be appropriate to include in such a set of object-creation permissions.

I don't have any particularly genius ideas about where we'd get the bits
to implement such a grant system though. We could modify the existing
grant system to use larger bits, but given that this would only be
applicable for schemas, perhaps it'd make sense to have another field
in pg_namespace instead? Not sure, just brainstorming here.

Thanks!

Stephen

#59Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#55)
Re: Multi-tenancy with RLS

On Tue, Feb 9, 2016 at 4:22 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Part of the problem here is that we have *not* created any hard and fast
distinction between "privileged" and "unprivileged" users; I think that
even speaking in those terms about RLS risks errors in your thinking.

+1.

In particular, the code-execution issue arises from the fact that a table
owner can now cause code to execute *with the permissions of someone else*
if the someone else is foolish enough to select from his table. No
special privileges required, just the ability to create a table. If we
make pg_dump run with RLS enabled, then the "foolish" part doesn't need to
be any more foolish than forgetting a -t switch when using pg_dump.

Yes. That is exactly why I argued for the current situation to be the
way it is, and I think it would have been a huge mistake if we now
decided otherwise. I don't have a ton of confidence that the database
is free of problems that would allow one user to assume the privileges
of another - but I certainly don't want to design more such problems
into the server.

Maybe we need to restrict that somehow, or maybe some better solution
exists that we've not thought of yet. But in its current state, RLS
is at least as much a security hazard as it is a security aid.
I do not want to see it extended in ways that make pg_dump unsafe to
use.

I could not agree more.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#60Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Robert Haas (#59)
5 attachment(s)
Re: Multi-tenancy with RLS

Hi All,

Here I attached new set of patches related to supporting of multi tenancy.
All the existing old patches are just rebased to latest master.

A new patch is added to address the pg_dump problem of dumping the data
based on the visibility after applying catalog row level security policies.

A new GUC catalog_row_level_security option is added, this option is
ON by default
and it will be set to OFF by pg_dump whenever --disable-catalog-row-security is
passed as an option to pg_dump. I didn't add this option to pg_restore similar
like --enable-row-security as I don't find a need for the same. By
default pg_dump
dumps the data that is visible to the user, whenever the catalog_row_security is
disabled and there exists some row level security policies, an error
is thrown similar
like row_security.

The above changes are based on my understanding to the discussion occurred in
this mail. In case if I miss anything, please let me know, i will
correct the same.

Regards,
Hari Babu
Fujitsu Australia

Attachments:

5_pgdump_new_row_security_option_v1.patchapplication/octet-stream; name=5_pgdump_new_row_security_option_v1.patchDownload
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 4e8c982..5da3747 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -6012,6 +6012,31 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-catalog-row-security" xreflabel="catalog_row_security">
+      <term><varname>catalog_row_security</varname> (<type>boolean</type>)
+      <indexterm>
+       <primary><varname>catalog_row_security</> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        This variable controls whether to raise an error in lieu of applying a
+        row security policy for a catalog table.  When set to <literal>on</>,
+        policies apply normally.  When set to <literal>off</>, queries fail which
+        would otherwise apply at least one policy.  The default is <literal>on</>.
+        Change to <literal>off</> where limited row visibility could cause
+        incorrect results. This variable has no effect on roles which bypass
+        every row security policy, to wit, superusers and roles with
+        the <literal>BYPASSRLS</> attribute.
+       </para>
+
+       <para>
+        For more information on row security policies,
+        see <xref linkend="SQL-CREATEPOLICY">.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-default-tablespace" xreflabel="default_tablespace">
       <term><varname>default_tablespace</varname> (<type>string</type>)
       <indexterm>
diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml
index be1b684..70267e9 100644
--- a/doc/src/sgml/ref/pg_dump.sgml
+++ b/doc/src/sgml/ref/pg_dump.sgml
@@ -703,6 +703,21 @@ PostgreSQL documentation
      </varlistentry>
 
      <varlistentry>
+      <term><option>--disable-catalog-row-security</></term>
+      <listitem>
+       <para>
+        This option is relevant only to throw an error instead of dumping
+        limited row visibility because of a row security on a catalog table.
+        If the user does not have sufficient privileges to bypass row security,
+        then an error is thrown. This parameter instructs <application>pg_dump</application>
+        to set <xref linkend="guc-catalog-row-security"> to off instead, allowing the user
+        to dump the parts of the contents of the table that they have access to.
+       </para>
+
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><option>--exclude-table-data=<replaceable class="parameter">table</replaceable></option></term>
       <listitem>
        <para>
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 6ac5184..719f638 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -419,6 +419,7 @@ bool		log_btree_build_stats = false;
 char	   *event_source;
 
 bool		row_security;
+bool		catalog_row_security;
 bool		check_function_bodies = true;
 bool		default_with_oids = false;
 bool		SQL_inheritance = true;
@@ -1390,6 +1391,15 @@ static struct config_bool ConfigureNamesBool[] =
 		NULL, NULL, NULL
 	},
 	{
+		{"catalog_row_security", PGC_USERSET, CONN_AUTH_SECURITY,
+			gettext_noop("Enable catalog row security."),
+			gettext_noop("When enabled, row security will be applied to all users on catalog tables.")
+		},
+		&catalog_row_security,
+		true,
+		NULL, NULL, NULL
+	},
+	{
 		{"check_function_bodies", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Check function bodies during CREATE FUNCTION."),
 			NULL
diff --git a/src/backend/utils/misc/rls.c b/src/backend/utils/misc/rls.c
index 4d6c88d..3d1bb10 100644
--- a/src/backend/utils/misc/rls.c
+++ b/src/backend/utils/misc/rls.c
@@ -113,16 +113,31 @@ check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
 			return RLS_NONE_ENV;
 	}
 
-	/*
-	 * We should apply RLS.  However, the user may turn off the row_security
-	 * GUC to get a forced error instead.
-	 */
-	if (!row_security && !noError)
-		ereport(ERROR,
-				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 errmsg("query would be affected by row-level security policy for table \"%s\"",
-						get_rel_name(relid)),
-				 amowner ? errhint("To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY.") : 0));
+	if (relid < FirstNormalObjectId)
+	{
+		/*
+		 * We should apply catalog RLS.  However, the user may turn off the
+		 * catalog_row_security GUC to get a forced error instead.
+		 */
+		if (!catalog_row_security && !noError)
+			ereport(ERROR,
+					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+					 errmsg("query would be affected by catalog row-level security policy for table \"%s\"",
+							get_rel_name(relid))));
+	}
+	else
+	{
+		/*
+		 * We should apply RLS.  However, the user may turn off the row_security
+		 * GUC to get a forced error instead.
+		 */
+		if (!row_security && !noError)
+			ereport(ERROR,
+					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+					 errmsg("query would be affected by row-level security policy for table \"%s\"",
+							get_rel_name(relid)),
+					 amowner ? errhint("To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY.") : 0));
+	}
 
 	/* RLS should be fully enabled for this relation. */
 	return RLS_ENABLED;
diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index f94caa3..a2c6b7a 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -150,6 +150,7 @@ typedef struct _dumpOptions
 	int			outputNoTablespaces;
 	int			use_setsessauth;
 	int			enable_row_security;
+	int			disable_catalog_row_security;
 
 	/* default, if no "inclusion" switches appear, is to dump everything */
 	bool		include_everything;
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 08c2b0c..0680de5 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -334,6 +334,7 @@ main(int argc, char **argv)
 		{"disable-dollar-quoting", no_argument, &dopt.disable_dollar_quoting, 1},
 		{"disable-triggers", no_argument, &dopt.disable_triggers, 1},
 		{"enable-row-security", no_argument, &dopt.enable_row_security, 1},
+		{"disable-catalog-row-security", no_argument, &dopt.disable_catalog_row_security, 1},
 		{"exclude-table-data", required_argument, NULL, 4},
 		{"if-exists", no_argument, &dopt.if_exists, 1},
 		{"inserts", no_argument, &dopt.dump_inserts, 1},
@@ -912,6 +914,9 @@ help(const char *progname)
 	printf(_("  --disable-triggers           disable triggers during data-only restore\n"));
 	printf(_("  --enable-row-security        enable row security (dump only content user has\n"
 			 "                               access to)\n"));
+	printf(_("  --disable-catalog-row-security\n"
+			 "        						 disable catalog row security (throw error instead of\n"
+			 "								 dumping only content user has access to)\n"));
 	printf(_("  --exclude-table-data=TABLE   do NOT dump data for the named table(s)\n"));
 	printf(_("  --if-exists                  use IF EXISTS when dropping objects\n"));
 	printf(_("  --inserts                    dump data as INSERT commands, rather than COPY\n"));
@@ -1040,6 +1045,9 @@ setup_connection(Archive *AH, const char *dumpencoding,
 			ExecuteSqlStatement(AH, "SET row_security = on");
 		else
 			ExecuteSqlStatement(AH, "SET row_security = off");
+
+		if (dopt->disable_catalog_row_security)
+			ExecuteSqlStatement(AH, "SET catalog_row_security = off");
 	}
 
 	/*
diff --git a/src/include/utils/rls.h b/src/include/utils/rls.h
index 29d17a4..a76abe0 100644
--- a/src/include/utils/rls.h
+++ b/src/include/utils/rls.h
@@ -15,6 +15,7 @@
 
 /* GUC variable */
 extern bool row_security;
+extern bool catalog_row_security;
 
 /*
  * Used by callers of check_enable_rls.
1_any_privilege_option_v1.patchapplication/octet-stream; name=1_any_privilege_option_v1.patchDownload
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index baef80d..9379f59 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -15559,7 +15559,11 @@ SET search_path TO <replaceable>schema</> <optional>, <replaceable>schema</>, ..
    <xref linkend="functions-info-access-table"> lists functions that
    allow the user to query object access privileges programmatically.
    See <xref linkend="ddl-priv"> for more information about
-   privileges.
+   privileges. In case if user wants to find out any privilege check
+   on the corresponding object, instead of providing individual/all
+   privileges, user can specify "any" as an privilege option. The
+   Access Prvilege Inquiry Functions returns true if the user have 
+   any privileges on the object.
   </para>
 
    <table id="functions-info-access-table">
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index fecf605..059a220 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -2032,6 +2032,11 @@ convert_table_priv_string(text *priv_type_text)
 		{"REFERENCES WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
 		{"TRIGGER", ACL_TRIGGER},
 		{"TRIGGER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
+		{"ANY", ACL_SELECT | ACL_GRANT_OPTION_FOR(ACL_SELECT) |
+				ACL_INSERT | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
+				ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_DELETE | ACL_GRANT_OPTION_FOR(ACL_DELETE) |
+				ACL_TRUNCATE | ACL_GRANT_OPTION_FOR(ACL_TRUNCATE) | ACL_REFERENCES |
+				ACL_GRANT_OPTION_FOR(ACL_REFERENCES) | ACL_TRIGGER | ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
 		{"RULE", 0},			/* ignore old RULE privileges */
 		{"RULE WITH GRANT OPTION", 0},
 		{NULL, 0}
@@ -2244,6 +2249,7 @@ convert_sequence_priv_string(text *priv_type_text)
 		{"USAGE", ACL_USAGE},
 		{"SELECT", ACL_SELECT},
 		{"UPDATE", ACL_UPDATE},
+		{"ANY", ACL_USAGE | ACL_SELECT | ACL_UPDATE},
 		{NULL, 0}
 	};
 
@@ -2859,6 +2865,9 @@ convert_column_priv_string(text *priv_type_text)
 		{"UPDATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_UPDATE)},
 		{"REFERENCES", ACL_REFERENCES},
 		{"REFERENCES WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
+		{"ANY", ACL_SELECT | ACL_GRANT_OPTION_FOR(ACL_SELECT) | ACL_INSERT |
+				ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
+				ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_REFERENCES | ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
 		{NULL, 0}
 	};
 
@@ -3056,6 +3065,9 @@ convert_database_priv_string(text *priv_type_text)
 		{"TEMP WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP)},
 		{"CONNECT", ACL_CONNECT},
 		{"CONNECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
+		{"ANY", ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE) |
+									ACL_CREATE_TEMP | ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CREATE_TEMP |
+									ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CONNECT | ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
 		{NULL, 0}
 	};
 
@@ -3239,6 +3251,7 @@ convert_foreign_data_wrapper_priv_string(text *priv_type_text)
 	static const priv_map foreign_data_wrapper_priv_map[] = {
 		{"USAGE", ACL_USAGE},
 		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+		{"ANY", ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
 		{NULL, 0}
 	};
 
@@ -3439,6 +3452,7 @@ convert_function_priv_string(text *priv_type_text)
 	static const priv_map function_priv_map[] = {
 		{"EXECUTE", ACL_EXECUTE},
 		{"EXECUTE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_EXECUTE)},
+		{"ANY", ACL_EXECUTE | ACL_GRANT_OPTION_FOR(ACL_EXECUTE)},
 		{NULL, 0}
 	};
 
@@ -3630,6 +3644,7 @@ convert_language_priv_string(text *priv_type_text)
 	static const priv_map language_priv_map[] = {
 		{"USAGE", ACL_USAGE},
 		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+		{"ANY", ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
 		{NULL, 0}
 	};
 
@@ -3823,6 +3838,7 @@ convert_schema_priv_string(text *priv_type_text)
 		{"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
 		{"USAGE", ACL_USAGE},
 		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+		{"ANY", ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE) | ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
 		{NULL, 0}
 	};
 
@@ -4005,6 +4021,7 @@ convert_server_priv_string(text *priv_type_text)
 	static const priv_map server_priv_map[] = {
 		{"USAGE", ACL_USAGE},
 		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+		{"ANY", ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
 		{NULL, 0}
 	};
 
@@ -4187,6 +4204,7 @@ convert_tablespace_priv_string(text *priv_type_text)
 	static const priv_map tablespace_priv_map[] = {
 		{"CREATE", ACL_CREATE},
 		{"CREATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
+		{"ANY", ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE)},
 		{NULL, 0}
 	};
 
@@ -4386,6 +4404,7 @@ convert_type_priv_string(text *priv_type_text)
 	static const priv_map type_priv_map[] = {
 		{"USAGE", ACL_USAGE},
 		{"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_USAGE)},
+		{"ANY", ACL_USAGE | ACL_GRANT_OPTION_FOR(ACL_USAGE)},
 		{NULL, 0}
 	};
 
@@ -4567,6 +4586,7 @@ convert_role_priv_string(text *priv_type_text)
 		{"USAGE WITH ADMIN OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
 		{"MEMBER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
 		{"MEMBER WITH ADMIN OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)},
+		{"ANY", ACL_USAGE | ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE)},
 		{NULL, 0}
 	};
 
2_view_security_definer_v1.patchapplication/octet-stream; name=2_view_security_definer_v1.patchDownload
diff --git a/doc/src/sgml/ref/alter_view.sgml b/doc/src/sgml/ref/alter_view.sgml
index 00f4ecb..d1f3a54 100644
--- a/doc/src/sgml/ref/alter_view.sgml
+++ b/doc/src/sgml/ref/alter_view.sgml
@@ -142,6 +142,16 @@ ALTER VIEW [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RESET
          </para>
         </listitem>
        </varlistentry>
+       <varlistentry>
+        <term><literal>security_definer</literal> (<type>boolean</type>)</term>
+        <listitem>
+         <para>
+          Changes the security-definer property of the view.  The value must
+          be Boolean value, such as <literal>true</literal>
+          or <literal>false</literal>.
+         </para>
+        </listitem>
+       </varlistentry>
       </variablelist>
      </para>
     </listitem>
diff --git a/doc/src/sgml/ref/create_view.sgml b/doc/src/sgml/ref/create_view.sgml
index e37bb00..71c6f79 100644
--- a/doc/src/sgml/ref/create_view.sgml
+++ b/doc/src/sgml/ref/create_view.sgml
@@ -147,6 +147,16 @@ CREATE VIEW <replaceable>name</> AS WITH RECURSIVE <replaceable>name</> (<replac
          </para>
         </listitem>
        </varlistentry>
+       
+       <varlistentry>
+        <term><literal>security_definer</literal> (<type>boolean</type>)</term>
+        <listitem>
+         <para>
+          This should be used if the view is intended to be executed with
+          owner privileges rather than the current user.
+         </para>
+        </listitem>
+       </varlistentry>
       </variablelist>
      </para>
     </listitem>
@@ -276,6 +286,37 @@ CREATE VIEW vista AS SELECT text 'Hello World' AS hello;
     to replace it (this includes being a member of the owning role).
    </para>
 
+  <refsect2 id="SQL-CREATEVIEW-security-definer-views">
+   <title id="SQL-CREATEVIEW-security-definer-views-title">Security definer Views</title>
+
+   <indexterm zone="sql-createview-security-definer-views">
+    <primary>security definer views</primary>
+   </indexterm>
+
+   <para>
+    Security definer views uses the view owner id instead of the current user
+    in the following conditions, otherwise the current user is used to verify
+    the privileges and etc.
+    <itemizedlist>
+     <listitem>
+      <para>
+       The view is used in <command>INSERT</>, <command>UPDATE</>
+       and <command>DELETE</> statements in the same way as on a
+       regular table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       To apply row-level security policies on the underlying base
+       relations of the view, based on the security definer, the 
+       corresponding policies related to the user are applied.
+      </para>
+     </listitem>
+    </itemizedlist>
+   </para>
+  </refsect2>
+ 
   <refsect2 id="SQL-CREATEVIEW-updatable-views">
    <title id="SQL-CREATEVIEW-updatable-views-title">Updatable Views</title>
 
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index ba1f3aa..7b0bbf6 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -90,6 +90,15 @@ static relopt_bool boolRelOpts[] =
 		},
 		false
 	},
+	{
+		{
+			"security_definer",
+			"specifies that the view is to be executed with the privileges of the user that created it.",
+			RELOPT_KIND_VIEW,
+			AccessExclusiveLock
+		},
+		false
+	},
 	/* list terminator */
 	{{NULL}}
 };
@@ -1338,6 +1347,8 @@ view_reloptions(Datum reloptions, bool validate)
 	static const relopt_parse_elt tab[] = {
 		{"security_barrier", RELOPT_TYPE_BOOL,
 		offsetof(ViewOptions, security_barrier)},
+		{ "security_definer", RELOPT_TYPE_BOOL,
+		offsetof(ViewOptions, security_definer) },
 		{"check_option", RELOPT_TYPE_STRING,
 		offsetof(ViewOptions, check_option_offset)}
 	};
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index a22a11e..b80c2a3 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -24,6 +24,7 @@
 #include "catalog/pg_type.h"
 #include "commands/trigger.h"
 #include "foreign/fdwapi.h"
+#include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
 #include "parser/analyze.h"
@@ -1616,6 +1617,7 @@ fireRIRrules(Query *parsetree, List *activeRIRs, bool forUpdatePushedDown)
 	int			origResultRelation = parsetree->resultRelation;
 	int			rt_index;
 	ListCell   *lc;
+	bool		security_definer_view = false;
 
 	/*
 	 * don't try to convert this into a foreach loop, because rtable list can
@@ -1777,18 +1779,27 @@ fireRIRrules(Query *parsetree, List *activeRIRs, bool forUpdatePushedDown)
 		++rt_index;
 
 		/* Only normal relations can have RLS policies */
-		if (rte->rtekind != RTE_RELATION ||
-			rte->relkind != RELKIND_RELATION)
+		if (rte->rtekind != RTE_RELATION)
 			continue;
-
+		
 		rel = heap_open(rte->relid, NoLock);
 
+		if (rte->relkind == RELKIND_VIEW && RelationIsSecurityDefinerView(rel))
+			security_definer_view = true;
+
+		if (rte->relkind != RELKIND_RELATION)
+		{
+			heap_close(rel, NoLock);
+			continue;
+		}
+
 		/*
 		 * Fetch any new security quals that must be applied to this RTE.
 		 */
 		get_row_security_policies(parsetree, rte, rt_index,
 								  &securityQuals, &withCheckOptions,
-								  &hasRowSecurity, &hasSubLinks);
+								  &hasRowSecurity, &hasSubLinks,
+								  security_definer_view);
 
 		if (securityQuals != NIL || withCheckOptions != NIL)
 		{
@@ -2834,7 +2845,10 @@ rewriteTargetView(Query *parsetree, Relation view)
 	 * the executor still performs appropriate permissions checks for the
 	 * query caller's use of the view.
 	 */
-	new_rte->checkAsUser = view->rd_rel->relowner;
+	if (RelationIsSecurityDefinerView(view))
+		new_rte->checkAsUser = view->rd_rel->relowner;
+	else
+		new_rte->checkAsUser = GetUserId();
 	new_rte->requiredPerms = view_rte->requiredPerms;
 
 	/*
diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c
index e029116..2c33d12 100644
--- a/src/backend/rewrite/rowsecurity.c
+++ b/src/backend/rewrite/rowsecurity.c
@@ -105,7 +105,8 @@ row_security_policy_hook_type row_security_policy_hook_restrictive = NULL;
 void
 get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
 						  List **securityQuals, List **withCheckOptions,
-						  bool *hasRowSecurity, bool *hasSubLinks)
+						  bool *hasRowSecurity, bool *hasSubLinks,
+						  bool security_definer_view)
 {
 	Oid			user_id;
 	int			rls_status;
@@ -125,10 +126,13 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
 		return;
 
 	/* Switch to checkAsUser if it's set */
-	user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId();
+	if (security_definer_view)
+		user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId();
+	else
+		user_id = GetUserId();
 
 	/* Determine the state of RLS for this, pass checkAsUser explicitly */
-	rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false);
+	rls_status = check_enable_rls(rte->relid, user_id, false);
 
 	/* If there is no RLS on this table at all, nothing to do */
 	if (rls_status == RLS_NONE)
diff --git a/src/include/rewrite/rowsecurity.h b/src/include/rewrite/rowsecurity.h
index fd0cbaff..661c9dd 100644
--- a/src/include/rewrite/rowsecurity.h
+++ b/src/include/rewrite/rowsecurity.h
@@ -43,6 +43,7 @@ extern PGDLLIMPORT row_security_policy_hook_type row_security_policy_hook_restri
 extern void get_row_security_policies(Query *root,
 						  RangeTblEntry *rte, int rt_index,
 						  List **securityQuals, List **withCheckOptions,
-						  bool *hasRowSecurity, bool *hasSubLinks);
+						  bool *hasRowSecurity, bool *hasSubLinks,
+						  bool security_definer_view);
 
 #endif   /* ROWSECURITY_H */
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index ed14442..9b87983 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -291,6 +291,7 @@ typedef struct ViewOptions
 {
 	int32		vl_len_;		/* varlena header (do not touch directly!) */
 	bool		security_barrier;
+	bool		security_definer;
 	int			check_option_offset;
 } ViewOptions;
 
@@ -304,6 +305,14 @@ typedef struct ViewOptions
 	 ((ViewOptions *) (relation)->rd_options)->security_barrier : false)
 
 /*
+ * RelationIsSecurityDefiner
+ *		Returns whether the relation is security definer, or not.
+ */
+#define RelationIsSecurityDefinerView(relation)	\
+	((relation)->rd_options ?				\
+	 ((ViewOptions *) (relation)->rd_options)->security_definer : false)
+
+/*
  * RelationHasCheckOption
  *		Returns true if the relation is a view defined with either the local
  *		or the cascaded check option.  Note multiple eval of argument!
diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out
index 35541ce..85d0542 100644
--- a/src/test/regress/expected/rowsecurity.out
+++ b/src/test/regress/expected/rowsecurity.out
@@ -1425,7 +1425,7 @@ CREATE POLICY p1 ON b1 USING (a % 2 = 0);
 ALTER TABLE b1 ENABLE ROW LEVEL SECURITY;
 GRANT ALL ON b1 TO regress_rls_bob;
 SET SESSION AUTHORIZATION regress_rls_bob;
-CREATE VIEW bv1 WITH (security_barrier) AS SELECT * FROM b1 WHERE a > 0 WITH CHECK OPTION;
+CREATE VIEW bv1 WITH (security_barrier, security_definer) AS SELECT * FROM b1 WHERE a > 0 WITH CHECK OPTION;
 GRANT ALL ON bv1 TO regress_rls_carol;
 SET SESSION AUTHORIZATION regress_rls_carol;
 EXPLAIN (COSTS OFF) SELECT * FROM bv1 WHERE f_leak(b);
@@ -1892,7 +1892,7 @@ EXPLAIN (COSTS OFF) EXECUTE plancache_test3;
 --
 -- View and Table owner are the same.
 SET SESSION AUTHORIZATION regress_rls_alice;
-CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b);
+CREATE VIEW rls_view WITH (security_definer) AS SELECT * FROM z1 WHERE f_leak(b);
 GRANT SELECT ON rls_view TO regress_rls_bob;
 -- Query as role that is not owner of view or table.  Should return all records.
 SET SESSION AUTHORIZATION regress_rls_bob;
@@ -1941,7 +1941,7 @@ EXPLAIN (COSTS OFF) SELECT * FROM rls_view;
 DROP VIEW rls_view;
 -- View and Table owners are different.
 SET SESSION AUTHORIZATION regress_rls_bob;
-CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b);
+CREATE VIEW rls_view WITH (security_definer) AS SELECT * FROM z1 WHERE f_leak(b);
 GRANT SELECT ON rls_view TO regress_rls_alice;
 -- Query as role that is not owner of view but is owner of table.
 -- Should return records based on view owner policies.
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index c5dfbb5..3d0fbdf 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -966,7 +966,7 @@ CREATE USER view_user2;
 SET SESSION AUTHORIZATION view_user1;
 CREATE TABLE base_tbl(a int, b text, c float);
 INSERT INTO base_tbl VALUES (1, 'Row 1', 1.0);
-CREATE VIEW rw_view1 AS SELECT b AS bb, c AS cc, a AS aa FROM base_tbl;
+CREATE VIEW rw_view1 WITH (security_definer) AS SELECT b AS bb, c AS cc, a AS aa FROM base_tbl;
 INSERT INTO rw_view1 VALUES ('Row 2', 2.0, 2);
 GRANT SELECT ON base_tbl TO view_user2;
 GRANT SELECT ON rw_view1 TO view_user2;
diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql
index a142923..66a44bf 100644
--- a/src/test/regress/sql/rowsecurity.sql
+++ b/src/test/regress/sql/rowsecurity.sql
@@ -491,7 +491,7 @@ ALTER TABLE b1 ENABLE ROW LEVEL SECURITY;
 GRANT ALL ON b1 TO regress_rls_bob;
 
 SET SESSION AUTHORIZATION regress_rls_bob;
-CREATE VIEW bv1 WITH (security_barrier) AS SELECT * FROM b1 WHERE a > 0 WITH CHECK OPTION;
+CREATE VIEW bv1 WITH (security_barrier, security_definer) AS SELECT * FROM b1 WHERE a > 0 WITH CHECK OPTION;
 GRANT ALL ON bv1 TO regress_rls_carol;
 
 SET SESSION AUTHORIZATION regress_rls_carol;
@@ -687,7 +687,7 @@ EXPLAIN (COSTS OFF) EXECUTE plancache_test3;
 --
 -- View and Table owner are the same.
 SET SESSION AUTHORIZATION regress_rls_alice;
-CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b);
+CREATE VIEW rls_view WITH (security_definer) AS SELECT * FROM z1 WHERE f_leak(b);
 GRANT SELECT ON rls_view TO regress_rls_bob;
 
 -- Query as role that is not owner of view or table.  Should return all records.
@@ -703,7 +703,7 @@ DROP VIEW rls_view;
 
 -- View and Table owners are different.
 SET SESSION AUTHORIZATION regress_rls_bob;
-CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b);
+CREATE VIEW rls_view WITH (security_definer) AS SELECT * FROM z1 WHERE f_leak(b);
 GRANT SELECT ON rls_view TO regress_rls_alice;
 
 -- Query as role that is not owner of view but is owner of table.
diff --git a/src/test/regress/sql/updatable_views.sql b/src/test/regress/sql/updatable_views.sql
index e6674cd..9f1aa07 100644
--- a/src/test/regress/sql/updatable_views.sql
+++ b/src/test/regress/sql/updatable_views.sql
@@ -397,7 +397,7 @@ CREATE USER view_user2;
 SET SESSION AUTHORIZATION view_user1;
 CREATE TABLE base_tbl(a int, b text, c float);
 INSERT INTO base_tbl VALUES (1, 'Row 1', 1.0);
-CREATE VIEW rw_view1 AS SELECT b AS bb, c AS cc, a AS aa FROM base_tbl;
+CREATE VIEW rw_view1 WITH (security_definer) AS SELECT b AS bb, c AS cc, a AS aa FROM base_tbl;
 INSERT INTO rw_view1 VALUES ('Row 2', 2.0, 2);
 
 GRANT SELECT ON base_tbl TO view_user2;
3_shared_catalog_tenancy_v1.patchapplication/octet-stream; name=3_shared_catalog_tenancy_v1.patchDownload
diff --git a/doc/src/sgml/ref/initdb.sgml b/doc/src/sgml/ref/initdb.sgml
index 4e339ec..ac460e9 100644
--- a/doc/src/sgml/ref/initdb.sgml
+++ b/doc/src/sgml/ref/initdb.sgml
@@ -152,6 +152,19 @@ PostgreSQL documentation
      </varlistentry>
 
      <varlistentry>
+      <term><option>-C</option></term>
+      <term><option>--shared-catalog-security</option></term>
+      <listitem>
+       <para>
+        This option enables the shared catalog tables security, by adding
+        row level security policies on all eligible shared catalog tables.
+        With this option, multi-tenancy in PostgreSQL is supported at 
+        database level.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><option>-D <replaceable class="parameter">directory</replaceable></option></term>
       <term><option>--pgdata=<replaceable class="parameter">directory</replaceable></option></term>
       <listitem>
diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c
index bc2e4af..6bd2a3b 100644
--- a/src/backend/commands/policy.c
+++ b/src/backend/commands/policy.c
@@ -23,7 +23,16 @@
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_authid.h"
+#include "catalog/pg_auth_members.h"
+#include "catalog/pg_database.h"
+#include "catalog/pg_db_role_setting.h"
+#include "catalog/pg_pltemplate.h"
 #include "catalog/pg_policy.h"
+#include "catalog/pg_replication_origin.h"
+#include "catalog/pg_shdepend.h"
+#include "catalog/pg_shdescription.h"
+#include "catalog/pg_shseclabel.h"
+#include "catalog/pg_tablespace.h"
 #include "catalog/pg_type.h"
 #include "commands/policy.h"
 #include "miscadmin.h"
@@ -36,6 +45,7 @@
 #include "rewrite/rewriteManip.h"
 #include "rewrite/rowsecurity.h"
 #include "storage/lock.h"
+#include "tcop/tcopprot.h"
 #include "utils/acl.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
@@ -46,11 +56,18 @@
 #include "utils/rel.h"
 #include "utils/syscache.h"
 
+#define CATALOG_POLICY_STRING_SIZE 65536
+
 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 get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *buf);
+
+/* variable to identify whether catalog row security is built is in progress or not? */
+bool		CatalogRowSecurityBuiltInProgress = false;
+
 /*
  * Callback to RangeVarGetRelidExtended().
  *
@@ -195,6 +212,16 @@ RelationBuildRowSecurity(Relation relation)
 	RowSecurityDesc *volatile rsdesc = NULL;
 
 	/*
+	 * Build the row security descriptor of a relation, once all the critical
+	 * relations are built.
+	 */
+	if (!criticalRelcachesBuilt || !criticalSharedRelcachesBuilt)
+		return;
+
+	if (IsSystemRelation(relation) && CatalogRowSecurityBuiltInProgress)
+		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.
@@ -211,124 +238,194 @@ RelationBuildRowSecurity(Relation relation)
 	 */
 	PG_TRY();
 	{
-		Relation	catalog;
-		ScanKeyData skey;
-		SysScanDesc sscan;
-		HeapTuple	tuple;
-
 		rsdesc = MemoryContextAllocZero(rscxt, sizeof(RowSecurityDesc));
 		rsdesc->rscxt = rscxt;
 
-		catalog = heap_open(PolicyRelationId, AccessShareLock);
-
-		ScanKeyInit(&skey,
-					Anum_pg_policy_polrelid,
-					BTEqualStrategyNumber, F_OIDEQ,
-					ObjectIdGetDatum(RelationGetRelid(relation)));
-
-		sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
-								   NULL, 1, &skey);
-
-		/*
-		 * Loop through the row level security policies for this relation, if
-		 * any.
-		 */
-		while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
+		if (IsSystemRelation(relation))
 		{
-			Datum		value_datum;
-			char		cmd_value;
-			Datum		roles_datum;
-			char	   *qual_value;
-			Expr	   *qual_expr;
-			char	   *with_check_value;
-			Expr	   *with_check_qual;
-			char	   *policy_name_value;
-			bool		isnull;
 			RowSecurityPolicy *policy;
+			Datum	   *role_oids;
+			int			nitems = 0;
+			ArrayType  *role_ids;
+			Node	   *qual_expr;
+			List	   *raw_parsetree_list;
+			ListCell   *list_item;
+			ParseState *qual_pstate;
+			RangeTblEntry *rte;
+			CreatePolicyStmt *stmt;
+			char		buf[CATALOG_POLICY_STRING_SIZE];
 
-			/*
-			 * Note: all the pass-by-reference data we collect here is either
-			 * still stored in the tuple, or constructed in the caller's
-			 * short-lived memory context.  We must copy it into rscxt
-			 * explicitly below.
-			 */
+			CatalogRowSecurityBuiltInProgress = true;
+			get_catalog_policy_string(relation->rd_id, relation->rd_rel, buf);
 
-			/* Get policy command */
-			value_datum = heap_getattr(tuple, Anum_pg_policy_polcmd,
-									   RelationGetDescr(catalog), &isnull);
-			Assert(!isnull);
-			cmd_value = DatumGetChar(value_datum);
-
-			/* Get policy name */
-			value_datum = heap_getattr(tuple, Anum_pg_policy_polname,
-									   RelationGetDescr(catalog), &isnull);
-			Assert(!isnull);
-			policy_name_value = NameStr(*(DatumGetName(value_datum)));
-
-			/* Get policy roles */
-			roles_datum = heap_getattr(tuple, Anum_pg_policy_polroles,
-									   RelationGetDescr(catalog), &isnull);
-			/* shouldn't be null, but initdb doesn't mark it so, so check */
-			if (isnull)
-				elog(ERROR, "unexpected null value in pg_policy.polroles");
-
-			/* Get policy qual */
-			value_datum = heap_getattr(tuple, Anum_pg_policy_polqual,
-									   RelationGetDescr(catalog), &isnull);
-			if (!isnull)
-			{
-				qual_value = TextDatumGetCString(value_datum);
-				qual_expr = (Expr *) stringToNode(qual_value);
-			}
-			else
-				qual_expr = NULL;
+			role_oids = policy_role_list_to_array(NULL, &nitems);
+			role_ids = construct_array(role_oids, nitems, OIDOID,
+									   sizeof(Oid), true, 'i');
 
-			/* Get WITH CHECK qual */
-			value_datum = heap_getattr(tuple, Anum_pg_policy_polwithcheck,
-									   RelationGetDescr(catalog), &isnull);
-			if (!isnull)
+			raw_parsetree_list = pg_parse_query(buf);
+			Assert(list_length(raw_parsetree_list) == 1);
+
+			foreach(list_item, raw_parsetree_list)
 			{
-				with_check_value = TextDatumGetCString(value_datum);
-				with_check_qual = (Expr *) stringToNode(with_check_value);
+				Node	   *parsetree;
+
+				parsetree = (Node *) lfirst(list_item);
+				stmt = (CreatePolicyStmt *) parsetree;
 			}
-			else
-				with_check_qual = NULL;
+
+			qual_pstate = make_parsestate(NULL);
+			rte = addRangeTableEntryForRelation(qual_pstate, relation,
+												NULL, false, false);
+
+			addRTEtoQuery(qual_pstate, rte, false, true, true);
+			qual_expr = transformWhereClause(qual_pstate,
+											 stmt->qual,
+											 EXPR_KIND_POLICY,
+											 "POLICY");
+
+			/* Fix up collation information */
+			assign_expr_collations(qual_pstate, qual_expr);
 
 			/* Now copy everything into the cache context */
 			MemoryContextSwitchTo(rscxt);
 
 			policy = palloc0(sizeof(RowSecurityPolicy));
-			policy->policy_name = pstrdup(policy_name_value);
-			policy->polcmd = cmd_value;
-			policy->roles = DatumGetArrayTypePCopy(roles_datum);
+			policy->policy_name = pstrdup(stmt->policy_name);
+			policy->polcmd = ACL_SELECT_CHR;
+			policy->roles = DatumGetArrayTypePCopy(role_ids);
 			policy->qual = copyObject(qual_expr);
-			policy->with_check_qual = copyObject(with_check_qual);
-			policy->hassublinks = checkExprHasSubLink((Node *) qual_expr) ||
-				checkExprHasSubLink((Node *) with_check_qual);
+			policy->with_check_qual = NULL;
+			policy->hassublinks = checkExprHasSubLink((Node *) qual_expr);
 
 			rsdesc->policies = lcons(policy, rsdesc->policies);
 
 			MemoryContextSwitchTo(oldcxt);
-
-			/* clean up some (not all) of the junk ... */
-			if (qual_expr != NULL)
-				pfree(qual_expr);
-			if (with_check_qual != NULL)
-				pfree(with_check_qual);
 		}
+		else
+		{
+			Relation	catalog;
+			ScanKeyData skey;
+			SysScanDesc sscan;
+			HeapTuple	tuple;
+
+			catalog = heap_open(PolicyRelationId, AccessShareLock);
 
-		systable_endscan(sscan);
-		heap_close(catalog, AccessShareLock);
+			ScanKeyInit(&skey,
+						Anum_pg_policy_polrelid,
+						BTEqualStrategyNumber, F_OIDEQ,
+						ObjectIdGetDatum(RelationGetRelid(relation)));
+
+			sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
+									   NULL, 1, &skey);
+
+			/*
+			 * Loop through the row level security policies for this relation,
+			 * if any.
+			 */
+			while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
+			{
+				Datum		value_datum;
+				char		cmd_value;
+				Datum		roles_datum;
+				char	   *qual_value;
+				Expr	   *qual_expr;
+				char	   *with_check_value;
+				Expr	   *with_check_qual;
+				char	   *policy_name_value;
+				bool		isnull;
+				RowSecurityPolicy *policy;
+
+				/*
+				 * Note: all the pass-by-reference data we collect here is
+				 * either still stored in the tuple, or constructed in the
+				 * caller's short-lived memory context.  We must copy it into
+				 * rscxt explicitly below.
+				 */
+
+				/* Get policy command */
+				value_datum = heap_getattr(tuple, Anum_pg_policy_polcmd,
+										 RelationGetDescr(catalog), &isnull);
+				Assert(!isnull);
+				cmd_value = DatumGetChar(value_datum);
+
+				/* Get policy name */
+				value_datum = heap_getattr(tuple, Anum_pg_policy_polname,
+										 RelationGetDescr(catalog), &isnull);
+				Assert(!isnull);
+				policy_name_value = NameStr(*(DatumGetName(value_datum)));
+
+				/* Get policy roles */
+				roles_datum = heap_getattr(tuple, Anum_pg_policy_polroles,
+										 RelationGetDescr(catalog), &isnull);
+				/* shouldn't be null, but initdb doesn't mark it so, so check */
+				if (isnull)
+					elog(ERROR, "unexpected null value in pg_policy.polroles");
+
+				/* Get policy qual */
+				value_datum = heap_getattr(tuple, Anum_pg_policy_polqual,
+										 RelationGetDescr(catalog), &isnull);
+				if (!isnull)
+				{
+					qual_value = TextDatumGetCString(value_datum);
+					qual_expr = (Expr *) stringToNode(qual_value);
+				}
+				else
+					qual_expr = NULL;
+
+				/* Get WITH CHECK qual */
+				value_datum = heap_getattr(tuple, Anum_pg_policy_polwithcheck,
+										 RelationGetDescr(catalog), &isnull);
+				if (!isnull)
+				{
+					with_check_value = TextDatumGetCString(value_datum);
+					with_check_qual = (Expr *) stringToNode(with_check_value);
+				}
+				else
+					with_check_qual = NULL;
+
+				/* Now copy everything into the cache context */
+				MemoryContextSwitchTo(rscxt);
+
+				policy = palloc0(sizeof(RowSecurityPolicy));
+				policy->policy_name = pstrdup(policy_name_value);
+				policy->polcmd = cmd_value;
+				policy->roles = DatumGetArrayTypePCopy(roles_datum);
+				policy->qual = copyObject(qual_expr);
+				policy->with_check_qual = copyObject(with_check_qual);
+				policy->hassublinks = checkExprHasSubLink((Node *) qual_expr) ||
+					checkExprHasSubLink((Node *) with_check_qual);
+
+				rsdesc->policies = lcons(policy, rsdesc->policies);
+
+				MemoryContextSwitchTo(oldcxt);
+
+				/* clean up some (not all) of the junk ... */
+				if (qual_expr != NULL)
+					pfree(qual_expr);
+				if (with_check_qual != NULL)
+					pfree(with_check_qual);
+			}
+
+			systable_endscan(sscan);
+			heap_close(catalog, AccessShareLock);
+		}
 	}
 	PG_CATCH();
 	{
 		/* Delete rscxt, first making sure it isn't active */
 		MemoryContextSwitchTo(oldcxt);
 		MemoryContextDelete(rscxt);
+
+		if (IsSystemRelation(relation))
+			CatalogRowSecurityBuiltInProgress = false;
+
 		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
+	if (IsSystemRelation(relation))
+		CatalogRowSecurityBuiltInProgress = false;
+
 	/* Success --- attach the policy descriptor to the relcache entry */
 	relation->rd_rsdesc = rsdesc;
 }
@@ -410,6 +507,73 @@ RemovePolicyById(Oid policy_id)
 	heap_close(pg_policy_rel, RowExclusiveLock);
 }
 
+static bool
+get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *buf)
+{
+	bool		policy_exist = true;
+
+	switch (relationid)
+	{
+		case AuthIdRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+					" (pg_has_role(oid, 'any'))",
+				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+			break;
+		case AuthMemRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+			  " (pg_has_role(roleid, 'any') AND pg_has_role(member, 'any'))",
+				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+			break;
+		case DatabaseRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+					" ((oid < 16384) OR has_database_privilege(oid,'any'))",
+				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+			break;
+		case DbRoleSettingRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+					" (pg_has_role(setrole, 'any') AND has_database_privilege(setdatabase,'any'))",
+				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+			break;
+		case PLTemplateRelationId:
+			policy_exist = false;
+			break;
+		case ReplicationOriginRelationId:
+			policy_exist = false;
+			break;
+		case SharedDependRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+					" ((classid = (select oid from pg_class where relname = 'pg_database') AND has_database_privilege(objid, 'any'))"
+					" OR (classid = (select oid from pg_class where relname = 'pg_authid') AND pg_has_role(objid, 'any'))"
+					" OR (classid = (select oid from pg_class where relname = 'pg_tablespace') AND has_tablespace_privilege(objid, 'any')))",
+				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+			break;
+		case SharedDescriptionRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+					" ((classoid = (select oid from pg_class where relname = 'pg_database') AND has_database_privilege(objoid, 'any'))"
+					" OR (classoid = (select oid from pg_class where relname = 'pg_authid') AND pg_has_role(objoid, 'any'))"
+					" 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);
+			break;
+		case SharedSecLabelRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+					" ((classoid = (select oid from pg_class where relname = 'pg_database') AND has_database_privilege(objoid, 'any'))"
+					" OR (classoid = (select oid from pg_class where relname = 'pg_authid') AND pg_has_role(objoid, 'any'))"
+					" 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);
+			break;
+		case TableSpaceRelationId:
+			sprintf(buf, "create policy %s_read_own_data on %s for select using"
+				  " ((oid < 16384) OR has_tablespace_privilege(oid, 'any'))",
+				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
+			break;
+		default:
+			policy_exist = false;
+			break;
+	}
+
+	return policy_exist;
+}
+
 /*
  * RemoveRoleFromObjectPolicy -
  *	 remove a role from a policy by its OID.  If the role is not a member of
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 86e9814..f80ee42 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -3444,6 +3444,16 @@ ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode)
 	{
 		AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
 
+		/*
+		 * ALTER table on sytem catalog tables is possible only when user
+		 * specifies CATALOG SECURITY on system catalog tables. To avoid an
+		 * error in the AlterTableCreateToastTable function for system catalog
+		 * tables, the system catalog tables are ignored for the toast table
+		 * creation.
+		 */
+		if (!IsUnderPostmaster && IsSharedRelation(tab->relid))
+			continue;
+
 		if (tab->relkind == RELKIND_RELATION ||
 			tab->relkind == RELKIND_MATVIEW)
 			AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
diff --git a/src/backend/utils/misc/rls.c b/src/backend/utils/misc/rls.c
index c33f29e..4d6c88d 100644
--- a/src/backend/utils/misc/rls.c
+++ b/src/backend/utils/misc/rls.c
@@ -58,10 +58,6 @@ check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
 	bool		relforcerowsecurity;
 	bool		amowner;
 
-	/* Nothing to do for built-in relations */
-	if (relid < (Oid) FirstNormalObjectId)
-		return RLS_NONE;
-
 	/* Fetch relation's relrowsecurity and relforcerowsecurity flags */
 	tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
 	if (!HeapTupleIsValid(tuple))
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index d4a5e7c..c8fbca6 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -142,6 +142,7 @@ static bool do_sync = true;
 static bool sync_only = false;
 static bool show_setting = false;
 static bool data_checksums = false;
+static bool shared_catalog_security = false;
 static char *xlog_dir = "";
 
 
@@ -196,6 +197,7 @@ static char *authwarning = NULL;
  */
 static const char *boot_options = "-F";
 static const char *backend_options = "--single -F -O -j -c search_path=pg_catalog -c exit_on_error=true";
+static const char *catalog_security_options = "--single -F -O -j -c search_path=pg_catalog -c exit_on_error=true -c allow_system_table_mods=true";
 
 static const char *const subdirs[] = {
 	"global",
@@ -264,6 +266,7 @@ static void setup_dictionary(FILE *cmdfd);
 static void setup_privileges(FILE *cmdfd);
 static void set_info_version(void);
 static void setup_schema(FILE *cmdfd);
+static void setup_shared_catalog_security(FILE *cmdfd);
 static void load_plpgsql(FILE *cmdfd);
 static void vacuum_db(FILE *cmdfd);
 static void make_template0(FILE *cmdfd);
@@ -2226,6 +2229,59 @@ setup_schema(FILE *cmdfd)
 }
 
 /*
+ * setup shared catalog security by defining policies
+ */
+static void
+setup_shared_catalog_security(FILE *cmdfd)
+{
+	const char **line;
+	static const char *pg_shared_catalog_security_setup[] = {
+		/* AuthMemRelationId */
+		"alter table pg_auth_members enable row level security;\n",
+
+		/* AuthIdRelationId */
+		"alter table pg_authid enable row level security;\n",
+
+		/* DatabaseRelationId */
+		"alter table pg_database enable row level security;\n",
+
+		/* DbRoleSettingRelationId */
+		"alter table pg_database enable row level security;\n",
+
+		/* PLTemplateRelationId */
+
+		/*
+		 * Currently there is no policy needed for this table, so leave it as
+		 * it is.
+		 */
+
+		/* ReplicationOriginRelationId */
+
+		/*
+		 * Currently there is no policy needed for this table, so leave it as
+		 * it is.
+		 */
+
+		/* SharedDependRelationId */
+		"alter table pg_shdepend enable row level security;\n",
+
+		/* SharedDescriptionRelationId */
+		"alter table pg_shdescription enable row level security;\n",
+
+		/* SharedSecLabelRelationId */
+		"alter table pg_shseclabel enable row level security;\n",
+
+		/* TableSpaceRelationId */
+		"alter table pg_tablespace enable row level security;\n",
+
+		NULL
+	};
+
+	for (line = pg_shared_catalog_security_setup; *line != NULL; line++)
+		PG_CMD_PUTS(*line);
+}
+
+/*
  * load PL/pgsql server-side language
  */
 static void
@@ -2680,6 +2736,8 @@ usage(const char *progname)
 	printf(_("\nLess commonly used options:\n"));
 	printf(_("  -d, --debug               generate lots of debugging output\n"));
 	printf(_("  -k, --data-checksums      use data page checksums\n"));
+	printf(_("  -C, --shared-catalog-security\n"
+			 "						      use shared catalog security\n"));
 	printf(_("  -L DIRECTORY              where to find the input files\n"));
 	printf(_("  -n, --noclean             do not clean up after errors\n"));
 	printf(_("  -N, --nosync              do not wait for changes to be written safely to disk\n"));
@@ -3233,6 +3291,10 @@ initialize_data_directory(void)
 {
 	PG_CMD_DECL;
 	int			i;
+	const char *options = backend_options;
+
+	if (shared_catalog_security)
+		options = catalog_security_options;
 
 	setup_signals();
 
@@ -3295,7 +3357,7 @@ initialize_data_directory(void)
 
 	snprintf(cmd, sizeof(cmd),
 			 "\"%s\" %s template1 >%s",
-			 backend_exec, backend_options,
+			 backend_exec, options,
 			 DEVNULL);
 
 	PG_CMD_OPEN;
@@ -3320,6 +3382,9 @@ initialize_data_directory(void)
 
 	setup_schema(cmdfd);
 
+	if (shared_catalog_security)
+		setup_shared_catalog_security(cmdfd);
+
 	load_plpgsql(cmdfd);
 
 	vacuum_db(cmdfd);
@@ -3364,6 +3429,7 @@ main(int argc, char *argv[])
 		{"sync-only", no_argument, NULL, 'S'},
 		{"xlogdir", required_argument, NULL, 'X'},
 		{"data-checksums", no_argument, NULL, 'k'},
+		{"shared-catalog-security", no_argument, NULL, 'C'},
 		{NULL, 0, NULL, 0}
 	};
 
@@ -3404,7 +3470,7 @@ main(int argc, char *argv[])
 
 	/* process command-line options */
 
-	while ((c = getopt_long(argc, argv, "dD:E:kL:nNU:WA:sST:X:", long_options, &option_index)) != -1)
+	while ((c = getopt_long(argc, argv, "dD:E:kCL:nNU:WA:sST:X:", long_options, &option_index)) != -1)
 	{
 		switch (c)
 		{
@@ -3456,6 +3522,9 @@ main(int argc, char *argv[])
 			case 'k':
 				data_checksums = true;
 				break;
+			case 'C':
+				shared_catalog_security = true;
+				break;
 			case 'L':
 				share_path = pg_strdup(optarg);
 				break;
4_database_catalog_tenancy_v1.patchapplication/octet-stream; name=4_database_catalog_tenancy_v1.patchDownload
diff --git a/doc/src/sgml/ref/alter_database.sgml b/doc/src/sgml/ref/alter_database.sgml
index cfc28cf..adc3326 100644
--- a/doc/src/sgml/ref/alter_database.sgml
+++ b/doc/src/sgml/ref/alter_database.sgml
@@ -28,6 +28,7 @@ ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> [ [ WITH ] <rep
     ALLOW_CONNECTIONS <replaceable class="PARAMETER">allowconn</replaceable>
     CONNECTION LIMIT <replaceable class="PARAMETER">connlimit</replaceable>
     IS_TEMPLATE <replaceable class="PARAMETER">istemplate</replaceable>
+    CATALOG SECURITY <replaceable class="PARAMETER">catalog_security</replaceable>
 
 ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> RENAME TO <replaceable>new_name</replaceable>
 
@@ -139,6 +140,18 @@ ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> RESET ALL
         </para>
        </listitem>
       </varlistentry>
+      
+     <varlistentry>
+       <term><replaceable class="parameter">catalog_security</replaceable></term>
+       <listitem>
+        <para>
+         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.
+        </para>
+       </listitem>
+     </varlistentry>
 
    <varlistentry>
     <term><replaceable>new_name</replaceable></term>
@@ -203,6 +216,12 @@ ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> RESET ALL
    Role-specific settings override database-specific
    ones if there is a conflict.
   </para>
+  
+  <para>
+   The catalog_security option is not supported at <xref linkend="sql-createdatabase">
+   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.
+  </para>
  </refsect1>
 
  <refsect1>
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index c1c0223..f7b7141 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -42,6 +42,7 @@
 #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,6 +227,13 @@ createdb(const CreatedbStmt *stmt)
 					 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,6 +527,7 @@ createdb(const CreatedbStmt *stmt)
 	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,11 +1384,14 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 	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,6 +1433,15 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 						 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,6 +1478,8 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 					(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,12 +1499,19 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 				(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,6 +1523,17 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 				(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,6 +1556,11 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
 		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);
diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c
index 6bd2a3b..8884b51 100644
--- a/src/backend/commands/policy.c
+++ b/src/backend/commands/policy.c
@@ -22,19 +22,62 @@
 #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_auth_members.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_database.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_pltemplate.h"
 #include "catalog/pg_policy.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_range.h"
 #include "catalog/pg_replication_origin.h"
+#include "catalog/pg_rewrite.h"
+#include "catalog/pg_seclabel.h"
 #include "catalog/pg_shdepend.h"
 #include "catalog/pg_shdescription.h"
 #include "catalog/pg_shseclabel.h"
+#include "catalog/pg_statistic.h"
 #include "catalog/pg_tablespace.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"
@@ -54,6 +97,7 @@
 #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 65536
@@ -514,6 +558,26 @@ get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *bu
 
 	switch (relationid)
 	{
+			/*
+			 * Following catalog tables data is accessible to all roles. So
+			 * they doesn't need any specific RLS policies on them.
+			 */
+		case AccessMethodRelationId:
+		case AccessMethodOperatorRelationId:
+		case AccessMethodProcedureRelationId:
+		case AggregateRelationId:
+			policy_exist = false;
+			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);
+			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);
+			break;
 		case AuthIdRelationId:
 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
 					" (pg_has_role(oid, 'any'))",
@@ -524,6 +588,22 @@ get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *bu
 			  " (pg_has_role(roleid, 'any') AND pg_has_role(member, 'any'))",
 				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
 			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);
+			break;
+		case CollationRelationId:
+			policy_exist = false;
+			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);
+			break;
+		case ConversionRelationId:
+			policy_exist = false;
+			break;
 		case DatabaseRelationId:
 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
 					" ((oid < 16384) OR has_database_privilege(oid,'any'))",
@@ -534,12 +614,175 @@ get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *bu
 					" (pg_has_role(setrole, 'any') AND has_database_privilege(setdatabase,'any'))",
 				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
 			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);
+			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);
+			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);
+			break;
+		case EnumRelationId:
+		case EventTriggerRelationId:
+		case ExtensionRelationId:
+			policy_exist = false;
+			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);
+			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);
+			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);
+			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);
+			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);
+			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);
+			break;
+		case LargeObjectRelationId:
+		case LargeObjectMetadataRelationId:
+			policy_exist = false;
+			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);
+			break;
+		case OperatorClassRelationId:
+		case OperatorRelationId:
+		case OperatorFamilyRelationId:
 		case PLTemplateRelationId:
 			policy_exist = false;
 			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);
+			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);
+			break;
+		case RangeRelationId:
+			policy_exist = false;
+			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);
+			break;
 		case ReplicationOriginRelationId:
+		case RewriteRelationId:
 			policy_exist = false;
 			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);
+			break;
 		case SharedDependRelationId:
 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
 					" ((classid = (select oid from pg_class where relname = 'pg_database') AND has_database_privilege(objid, 'any'))"
@@ -561,11 +804,42 @@ get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *bu
 					" 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);
 			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);
+			break;
 		case TableSpaceRelationId:
 			sprintf(buf, "create policy %s_read_own_data on %s for select using"
 				  " ((oid < 16384) OR has_tablespace_privilege(oid, 'any'))",
 				 pg_class_tuple->relname.data, pg_class_tuple->relname.data);
 			break;
+		case TransformRelationId:
+			policy_exist = false;
+			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);
+			break;
+		case TSConfigRelationId:
+		case TSConfigMapRelationId:
+		case TSDictionaryRelationId:
+		case TSParserRelationId:
+		case TSTemplateRelationId:
+			policy_exist = false;
+			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);
+			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);
+			break;
 		default:
 			policy_exist = false;
 			break;
@@ -575,6 +849,163 @@ get_catalog_policy_string(Oid relationid, Form_pg_class pg_class_tuple, char *bu
 }
 
 /*
+ * 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)
+		{
+			HeapTuple	cache_tuple;
+			Form_pg_class pg_class_tuple;
+			bool		policy_exist;
+
+			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));
+
+			pg_class_tuple = (Form_pg_class) GETSTRUCT(cache_tuple);
+			policy_exist = get_catalog_policy_string(HeapTupleGetOid(tuple), pg_class_tuple, buf);
+			if (!policy_exist)
+			{
+				heap_freetuple(cache_tuple);
+				continue;
+			}
+
+			pg_class_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)
+		{
+			HeapTuple	cache_tuple;
+
+			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));
+
+			pg_class_tuple = (Form_pg_class) GETSTRUCT(cache_tuple);
+			if (!pg_class_tuple->relrowsecurity)
+				continue;
+
+			pg_class_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();
+}
+
+/*
  * RemoveRoleFromObjectPolicy -
  *	 remove a role from a policy by its OID.  If the role is not a member of
  *	 the policy then an error is raised.  False is returned to indicate that
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index edf4516..2469453 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -8948,6 +8948,7 @@ createdb_opt_name:
 			| OWNER							{ $$ = pstrdup($1); }
 			| TABLESPACE					{ $$ = pstrdup($1); }
 			| TEMPLATE						{ $$ = pstrdup($1); }
+			| CATALOG_P SECURITY				{ $$ = pstrdup("catalog_security"); }
 		;
 
 /*
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 059a220..e22baf6 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -17,12 +17,20 @@
 #include <ctype.h>
 
 #include "access/htup_details.h"
+#include "access/sysattr.h"
 #include "catalog/catalog.h"
+#include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_authid.h"
 #include "catalog/pg_auth_members.h"
-#include "catalog/pg_type.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"
@@ -32,6 +40,7 @@
 #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"
@@ -2033,10 +2042,10 @@ convert_table_priv_string(text *priv_type_text)
 		{"TRIGGER", ACL_TRIGGER},
 		{"TRIGGER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
 		{"ANY", ACL_SELECT | ACL_GRANT_OPTION_FOR(ACL_SELECT) |
-				ACL_INSERT | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
-				ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_DELETE | ACL_GRANT_OPTION_FOR(ACL_DELETE) |
-				ACL_TRUNCATE | ACL_GRANT_OPTION_FOR(ACL_TRUNCATE) | ACL_REFERENCES |
-				ACL_GRANT_OPTION_FOR(ACL_REFERENCES) | ACL_TRIGGER | ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
+			ACL_INSERT | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
+			ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_DELETE | ACL_GRANT_OPTION_FOR(ACL_DELETE) |
+			ACL_TRUNCATE | ACL_GRANT_OPTION_FOR(ACL_TRUNCATE) | ACL_REFERENCES |
+		ACL_GRANT_OPTION_FOR(ACL_REFERENCES) | ACL_TRIGGER | ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
 		{"RULE", 0},			/* ignore old RULE privileges */
 		{"RULE WITH GRANT OPTION", 0},
 		{NULL, 0}
@@ -2866,8 +2875,8 @@ convert_column_priv_string(text *priv_type_text)
 		{"REFERENCES", ACL_REFERENCES},
 		{"REFERENCES WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
 		{"ANY", ACL_SELECT | ACL_GRANT_OPTION_FOR(ACL_SELECT) | ACL_INSERT |
-				ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
-				ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_REFERENCES | ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
+			ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_GRANT_OPTION_FOR(ACL_INSERT) | ACL_UPDATE |
+		ACL_GRANT_OPTION_FOR(ACL_UPDATE) | ACL_REFERENCES | ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
 		{NULL, 0}
 	};
 
@@ -3066,8 +3075,8 @@ convert_database_priv_string(text *priv_type_text)
 		{"CONNECT", ACL_CONNECT},
 		{"CONNECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
 		{"ANY", ACL_CREATE | ACL_GRANT_OPTION_FOR(ACL_CREATE) |
-									ACL_CREATE_TEMP | ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CREATE_TEMP |
-									ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CONNECT | ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
+			ACL_CREATE_TEMP | ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CREATE_TEMP |
+		ACL_GRANT_OPTION_FOR(ACL_CREATE_TEMP) | ACL_CONNECT | ACL_GRANT_OPTION_FOR(ACL_CONNECT)},
 		{NULL, 0}
 	};
 
@@ -5306,3 +5315,293 @@ check_rolespec_name(const Node *node, const char *detail_msg)
 							role->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));
+}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 8d2ad01..6d7deb2 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -2109,6 +2109,8 @@ RelationClearRelation(Relation relation, bool rebuild)
 	 */
 	if (relation->rd_isnailed)
 	{
+		HeapTuple	pg_class_tuple;
+
 		RelationInitPhysicalAddr(relation);
 
 		if (relation->rd_rel->relkind == RELKIND_INDEX)
@@ -2117,6 +2119,28 @@ RelationClearRelation(Relation relation, bool rebuild)
 			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;
 	}
 
diff --git a/src/include/catalog/pg_database.h b/src/include/catalog/pg_database.h
index 6ae1b40..1816d8b 100644
--- a/src/include/catalog/pg_database.h
+++ b/src/include/catalog/pg_database.h
@@ -43,7 +43,7 @@ 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 */
-
+	bool		datcatalogsecurity; /* catalog security is enabled? */
 #ifdef CATALOG_VARLEN			/* variable-length fields start here */
 	aclitem		datacl[1];		/* access permissions */
 #endif
@@ -60,22 +60,23 @@ 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
+#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 _null_));
+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
 
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index d92c05e..9bc4c96 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3624,6 +3624,19 @@ DESCR("current user privilege on role by role name");
 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 = 3343 (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 = 3344 (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 = 3345 (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 = 3346 (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 = 3347 (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 = 3348 (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_ ));
diff --git a/src/include/commands/policy.h b/src/include/commands/policy.h
index dbf7824..453d335 100644
--- a/src/include/commands/policy.h
+++ b/src/include/commands/policy.h
@@ -35,4 +35,7 @@ extern ObjectAddress rename_policy(RenameStmt *stmt);
 
 extern bool relation_has_policies(Relation rel);
 
+extern void CreateCatalogPolicy(void);
+extern void RemoveCatalogPolicy(void);
+
 #endif   /* POLICY_H */
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 01976a1..dc255a7 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -107,6 +107,12 @@ extern Datum pg_has_role_id_name(PG_FUNCTION_ARGS);
 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);
diff --git a/src/test/regress/expected/multitenancy.out b/src/test/regress/expected/multitenancy.out
new file mode 100644
index 0000000..cbdb1fa
--- /dev/null
+++ b/src/test/regress/expected/multitenancy.out
@@ -0,0 +1,644 @@
+-- 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;
+alter schema tenancy_user1 owner to tenancy_user1;
+create schema tenancy_user2;
+alter schema tenancy_user2 owner 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;
+ALTER SERVER tenancy_server OWNER 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 from pg_namespace where nspname = 'tenancy_user1';
+    nspname    
+---------------
+ tenancy_user1
+(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_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 from pg_namespace where nspname = 'tenancy_user1';
+ nspname 
+---------
+(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_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
+drop schema tenancy_user1;
+RESET ROLE;
+SET SESSION ROLE tenancy_user2;
+DROP FUNCTION tenancy_user2_tenancytestfunc(text);
+DROP FOREIGN TABLE tenancy_user2_ft1;
+DROP LANGUAGE tenancy_lang1;
+drop schema tenancy_user2;
+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 role tenancy_user1;
+drop role tenancy_user2;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 4ebad04..557c215 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -60,7 +60,7 @@ 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 create_am
+test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes create_am multitenancy
 
 # ----------
 # sanity_check does a vacuum, affecting the sort order of SELECT *
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 5c7038d..f4e1839 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -166,4 +166,5 @@ test: largeobject
 test: with
 test: xml
 test: event_trigger
+test: multitenancy
 test: stats
diff --git a/src/test/regress/sql/multitenancy.sql b/src/test/regress/sql/multitenancy.sql
new file mode 100644
index 0000000..0b774a5
--- /dev/null
+++ b/src/test/regress/sql/multitenancy.sql
@@ -0,0 +1,297 @@
+-- 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;
+alter schema tenancy_user1 owner to tenancy_user1;
+
+create schema tenancy_user2;
+alter schema tenancy_user2 owner 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;
+ALTER SERVER tenancy_server OWNER 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 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_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 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_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;
+drop schema tenancy_user1;
+
+RESET ROLE;
+
+SET SESSION ROLE tenancy_user2;
+
+DROP FUNCTION tenancy_user2_tenancytestfunc(text);
+DROP FOREIGN TABLE tenancy_user2_ft1;
+DROP LANGUAGE tenancy_lang1;
+drop schema tenancy_user2;
+
+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 role tenancy_user1;
+drop role tenancy_user2;
#61Michael Paquier
michael.paquier@gmail.com
In reply to: Haribabu Kommi (#60)
Re: Multi-tenancy with RLS

On Tue, Jul 19, 2016 at 3:42 PM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

The above changes are based on my understanding to the discussion occurred in
this mail. In case if I miss anything, please let me know, i will
correct the same.

The patch series still apply.

+                   " ((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'))"
[... long list ...]
That's quite hard to digest...
+static bool
+get_catalog_policy_string(Oid relationid, Form_pg_class
pg_class_tuple, char *buf)
This is an exceptionally weak interface at quick glance. This is using
SQL strings, and nothing is actually done regarding potentially
conflicting name types...

The number of new files included in policy.c is impressive as well..

This does not count as a full review though, so I am moving it to next
CF. Perhaps it will find its audience.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#62Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Michael Paquier (#61)
Re: Multi-tenancy with RLS

On Mon, Oct 3, 2016 at 3:11 PM, Michael Paquier <michael.paquier@gmail.com>
wrote:

On Tue, Jul 19, 2016 at 3:42 PM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

The above changes are based on my understanding to the discussion

occurred in

this mail. In case if I miss anything, please let me know, i will
correct the same.

The patch series still apply.

+                   " ((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'))"
[... long list ...]
That's quite hard to digest...
+static bool
+get_catalog_policy_string(Oid relationid, Form_pg_class
pg_class_tuple, char *buf)
This is an exceptionally weak interface at quick glance. This is using
SQL strings, and nothing is actually done regarding potentially
conflicting name types...

The number of new files included in policy.c is impressive as well..

This does not count as a full review though, so I am moving it to next
CF. Perhaps it will find its audience.

As the patch doesn't receive full review. Just kept in the commitfest to
see any interest from others for this patch.

Moved to next CF with "needs review" status.

Regards,
Hari Babu
Fujitsu Australia

#63Haribabu Kommi
kommi.haribabu@gmail.com
In reply to: Haribabu Kommi (#62)
Re: Multi-tenancy with RLS

On Mon, Dec 5, 2016 at 4:31 PM, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:

On Mon, Oct 3, 2016 at 3:11 PM, Michael Paquier <michael.paquier@gmail.com

wrote:

On Tue, Jul 19, 2016 at 3:42 PM, Haribabu Kommi
<kommi.haribabu@gmail.com> wrote:

The above changes are based on my understanding to the discussion

occurred in

this mail. In case if I miss anything, please let me know, i will
correct the same.

The patch series still apply.

+                   " ((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'))"
[... long list ...]
That's quite hard to digest...
+static bool
+get_catalog_policy_string(Oid relationid, Form_pg_class
pg_class_tuple, char *buf)
This is an exceptionally weak interface at quick glance. This is using
SQL strings, and nothing is actually done regarding potentially
conflicting name types...

The number of new files included in policy.c is impressive as well..

This does not count as a full review though, so I am moving it to next
CF. Perhaps it will find its audience.

As the patch doesn't receive full review. Just kept in the commitfest to
see any interest from others for this patch.

Moved to next CF with "needs review" status.

This patch is not generating much interest from the community, may be
because of the design that is chosen to implement multi-tenancy.

Currently this patch is marked as rejected.

Regards,
Hari Babu
Fujitsu Australia