From ad60419074deef9f3d057e395ff08375afd9eeaa Mon Sep 17 00:00:00 2001
From: Craig Ringer <craig@2ndquadrant.com>
Date: Mon, 20 Jan 2014 13:07:50 +0800
Subject: [PATCH 02/10] RLS: add pg_rowsecurity catalog

---
 doc/src/sgml/catalogs.sgml                 |  65 +++++++
 src/backend/catalog/Makefile               |   4 +-
 src/backend/catalog/dependency.c           |   8 +
 src/backend/catalog/heap.c                 |   1 +
 src/backend/catalog/objectaddress.c        |  50 +++++
 src/backend/catalog/pg_rowsecurity.c       | 287 +++++++++++++++++++++++++++++
 src/backend/utils/cache/relcache.c         |  17 +-
 src/include/catalog/dependency.h           |   1 +
 src/include/catalog/indexing.h             |   5 +
 src/include/catalog/pg_class.h             |  25 +--
 src/include/catalog/pg_rowsecurity.h       |  74 ++++++++
 src/include/parser/parse_node.h            |   3 +-
 src/include/utils/rel.h                    |   2 +
 src/test/regress/expected/sanity_check.out |   1 +
 14 files changed, 528 insertions(+), 15 deletions(-)
 create mode 100644 src/backend/catalog/pg_rowsecurity.c
 create mode 100644 src/include/catalog/pg_rowsecurity.h

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 2230c93..1ca088a 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -234,6 +234,11 @@
      </row>
 
      <row>
+      <entry><link linkend="catalog-pg-rowsecurity"><structname>pg_rowsecuritylevelsec</structname></link></entry>
+      <entry>row-level security policy of relation</entry>
+     </row>
+
+     <row>
       <entry><link linkend="catalog-pg-seclabel"><structname>pg_seclabel</structname></link></entry>
       <entry>security labels on database objects</entry>
      </row>
@@ -1876,6 +1881,16 @@
      </row>
 
      <row>
+      <entry><structfield>relhasrowsecurity</structfield></entry>
+      <entry><type>bool</type></entry>
+      <entry></entry>
+      <entry>
+       True if table has row-security policy; see
+       <link linkend="catalog-pg-rowsecurity"><structname>pg_rowsecurity</structname></link> catalog
+      </entry>
+     </row>
+
+     <row>
       <entry><structfield>relhassubclass</structfield></entry>
       <entry><type>bool</type></entry>
       <entry></entry>
@@ -5157,6 +5172,56 @@
 
  </sect1>
 
+ <sect1 id="catalog-pg-rowsecurity">
+  <title><structname>pg_rowsecurity</structname></title>
+
+  <indexterm zone="catalog-pg-rowsecurity">
+   <primary>pg_rowsecurity</primary>
+  </indexterm>
+  <para>
+   The catalog <structname>pg_rowsecurity</structname> stores expression
+   tree of row-security policy to be performed on a particular relation.
+  </para>
+  <table>
+   <title><structname>pg_rowsecurity</structname> Columns</title>
+   <tgroup cols="4">
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Type</entry>
+      <entry>References</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+    <tbody>
+     <row>
+      <entry><structfield>rsecrelid</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
+      <entry>The table this row-security is for</entry>
+     </row>
+     <row>
+      <entry><structfield>rseccmd</structfield></entry>
+      <entry><type>char</type></entry>
+      <entry></entry>
+      <entry>The command this row-security is for. 'a' meaning all is only possible value right now.</entry>
+     </row>
+     <row>
+      <entry><structfield>rsecqual</structfield></entry>
+      <entry><type>pg_node_tree</type></entry>
+      <entry></entry>
+      <entry>An expression tree to be performed as row-security policy</entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+  <note>
+   <para>
+    <literal>pg_class.relhasrowsecurity</literal>
+    must be true if a table has row-security policy in this catalog.
+   </para>
+  </note>
+ </sect1>
 
  <sect1 id="catalog-pg-seclabel">
   <title><structname>pg_seclabel</structname></title>
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index a974bd5..beb73df 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -15,7 +15,7 @@ OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \
        pg_constraint.o pg_conversion.o \
        pg_depend.o pg_enum.o pg_inherits.o pg_largeobject.o pg_namespace.o \
        pg_operator.o pg_proc.o pg_range.o pg_db_role_setting.o pg_shdepend.o \
-       pg_type.o storage.o toasting.o
+       pg_rowsecurity.o pg_type.o storage.o toasting.o
 
 BKIFILES = postgres.bki postgres.description postgres.shdescription
 
@@ -39,7 +39,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
 	pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
 	pg_ts_parser.h pg_ts_template.h pg_extension.h \
 	pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
-	pg_foreign_table.h \
+	pg_foreign_table.h pg_rowsecurity.h \
 	pg_default_acl.h pg_seclabel.h pg_shseclabel.h pg_collation.h pg_range.h \
 	toasting.h indexing.h \
     )
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index e511669..8f95b14 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -45,6 +45,7 @@
 #include "catalog/pg_opfamily.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_rewrite.h"
+#include "catalog/pg_rowsecurity.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_ts_config.h"
@@ -1249,6 +1250,10 @@ doDeletion(const ObjectAddress *object, int flags)
 			RemoveEventTriggerById(object->objectId);
 			break;
 
+		case OCLASS_ROWSECURITY:
+			RemoveRowSecurityById(object->objectId);
+			break;
+
 		default:
 			elog(ERROR, "unrecognized object class: %u",
 				 object->classId);
@@ -2316,6 +2321,9 @@ getObjectClass(const ObjectAddress *object)
 
 		case EventTriggerRelationId:
 			return OCLASS_EVENT_TRIGGER;
+
+		case RowSecurityRelationId:
+			return OCLASS_ROWSECURITY;
 	}
 
 	/* shouldn't get here */
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 2cf4bc0..5be195b 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -799,6 +799,7 @@ InsertPgClassTuple(Relation pg_class_desc,
 	values[Anum_pg_class_relhaspkey - 1] = BoolGetDatum(rd_rel->relhaspkey);
 	values[Anum_pg_class_relhasrules - 1] = BoolGetDatum(rd_rel->relhasrules);
 	values[Anum_pg_class_relhastriggers - 1] = BoolGetDatum(rd_rel->relhastriggers);
+	values[Anum_pg_class_relhasrowsecurity - 1] = BoolGetDatum(rd_rel->relhasrowsecurity);
 	values[Anum_pg_class_relhassubclass - 1] = BoolGetDatum(rd_rel->relhassubclass);
 	values[Anum_pg_class_relispopulated - 1] = BoolGetDatum(rd_rel->relispopulated);
 	values[Anum_pg_class_relreplident - 1] = CharGetDatum(rd_rel->relreplident);
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 991c02a..870e477 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -2143,6 +2143,56 @@ getObjectDescription(const ObjectAddress *object)
 				break;
 			}
 
+		case OCLASS_ROWSECURITY:
+			{
+				Relation	rsec_rel;
+				ScanKeyData	skey;
+				SysScanDesc	sscan;
+				HeapTuple	tuple;
+				Form_pg_rowsecurity	form_rsec;
+
+				rsec_rel = heap_open(RowSecurityRelationId, AccessShareLock);
+
+				ScanKeyInit(&skey,
+							ObjectIdAttributeNumber,
+							BTEqualStrategyNumber, F_OIDEQ,
+							ObjectIdGetDatum(object->objectId));
+				sscan = systable_beginscan(rsec_rel, RowSecurityOidIndexId,
+										   true, NULL, 1, &skey);
+				tuple = systable_getnext(sscan);
+				if (!HeapTupleIsValid(tuple))
+					elog(ERROR, "could not find tuple for row-security %u",
+						 object->objectId);
+				form_rsec = (Form_pg_rowsecurity) GETSTRUCT(tuple);
+
+				appendStringInfo(&buffer, _("row-security of "));
+				getRelationDescription(&buffer, form_rsec->rsecrelid);
+				switch (form_rsec->rseccmd)
+				{
+					case ROWSECURITY_CMD_ALL:
+						appendStringInfo(&buffer, _(" FOR ALL"));
+						break;
+					case ROWSECURITY_CMD_SELECT:
+						appendStringInfo(&buffer, _(" FOR SELECT"));
+						break;
+					case ROWSECURITY_CMD_INSERT:
+						appendStringInfo(&buffer, _(" FOR INSERT"));
+						break;
+					case ROWSECURITY_CMD_UPDATE:
+						appendStringInfo(&buffer, _(" FOR UPDATE"));
+						break;
+					case ROWSECURITY_CMD_DELETE:
+						appendStringInfo(&buffer, _(" FOR DELETE"));
+						break;
+					default:
+						elog(ERROR, "unexpected row-security command type: %c",
+							 form_rsec->rseccmd);
+				}
+				systable_endscan(sscan);
+				heap_close(rsec_rel, AccessShareLock);
+				break;
+			}
+
 		default:
 			appendStringInfo(&buffer, "unrecognized object %u %u %d",
 							 object->classId,
diff --git a/src/backend/catalog/pg_rowsecurity.c b/src/backend/catalog/pg_rowsecurity.c
new file mode 100644
index 0000000..7ee4e88
--- /dev/null
+++ b/src/backend/catalog/pg_rowsecurity.c
@@ -0,0 +1,287 @@
+/* -------------------------------------------------------------------------
+ *
+ * pg_rowsecurity.c
+ *    routines to support manipulation of the pg_rowsecurity catalog
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * -------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/sysattr.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_rowsecurity.h"
+#include "catalog/pg_type.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/clauses.h"
+#include "parser/parse_clause.h"
+#include "parser/parse_node.h"
+#include "parser/parse_relation.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/inval.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+#include "utils/tqual.h"
+
+/*
+ * Load row-security policy from the catalog, and keep it on
+ * the relation cache.
+ */
+void
+RelationBuildRowSecurity(Relation relation)
+{
+	Relation		catalog;
+	ScanKeyData		skey;
+	SysScanDesc		sscan;
+	HeapTuple		tuple;
+	MemoryContext	oldcxt;
+	MemoryContext	rscxt = NULL;
+	RowSecurityDesc	*rsdesc = NULL;
+
+	catalog = heap_open(RowSecurityRelationId, AccessShareLock);
+
+	ScanKeyInit(&skey,
+				Anum_pg_rowsecurity_rsecrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(RelationGetRelid(relation)));
+	sscan = systable_beginscan(catalog, RowSecurityRelidIndexId, true,
+							   NULL, 1, &skey);
+	PG_TRY();
+	{
+		while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
+		{
+			Datum	value;
+			bool	isnull;
+			char   *temp;
+
+			if (!rsdesc)
+			{
+				rscxt = AllocSetContextCreate(CacheMemoryContext,
+											  "Row-security descriptor",
+											  ALLOCSET_SMALL_MINSIZE,
+											  ALLOCSET_SMALL_INITSIZE,
+											  ALLOCSET_SMALL_MAXSIZE);
+				oldcxt = MemoryContextSwitchTo(rscxt);
+				rsdesc = palloc0(sizeof(RowSecurityDesc));
+				rsdesc->rscxt = rscxt;
+				MemoryContextSwitchTo(oldcxt);
+			}
+			value = heap_getattr(tuple, Anum_pg_rowsecurity_rseccmd,
+								 RelationGetDescr(catalog), &isnull);
+			Assert(!isnull);
+
+			if (DatumGetChar(value) != ROWSECURITY_CMD_ALL)
+			{
+				ereport(WARNING,
+						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+						 errmsg("Per-command row-security not implemented")));
+				continue;
+			}
+
+			value = heap_getattr(tuple, Anum_pg_rowsecurity_rsecqual,
+								 RelationGetDescr(catalog), &isnull);
+			Assert(!isnull);
+			temp = TextDatumGetCString(value);
+
+			oldcxt = MemoryContextSwitchTo(rscxt);
+			rsdesc->rsall.rsecid = HeapTupleGetOid(tuple);
+			rsdesc->rsall.qual = (Expr *) stringToNode(temp);
+			Assert(exprType((Node *)rsdesc->rsall.qual) == BOOLOID);
+			rsdesc->rsall.hassublinks
+				= contain_subplans((Node *)rsdesc->rsall.qual);
+			MemoryContextSwitchTo(oldcxt);
+
+			pfree(temp);
+		}
+	}
+	PG_CATCH();
+	{
+		if (rscxt != NULL)
+			MemoryContextDelete(rscxt);
+		PG_RE_THROW();
+	}
+	PG_END_TRY();
+
+	systable_endscan(sscan);
+	heap_close(catalog, AccessShareLock);
+
+	relation->rsdesc = rsdesc;
+}
+
+/*
+ * Parse the supplied row-security policy, and insert/update a row
+ * of pg_rowsecurity catalog.
+ */
+static void
+InsertOrUpdatePolicyRow(Relation relation, char rseccmd, Node *clause)
+{
+	Oid				relationId = RelationGetRelid(relation);
+	Oid				rowsecId;
+	ParseState	   *pstate;
+	RangeTblEntry  *rte;
+	Node		   *qual;
+	Relation		catalog;
+	ScanKeyData		skeys[2];
+	SysScanDesc		sscan;
+	HeapTuple		oldtup;
+	HeapTuple		newtup;
+	Datum			values[Natts_pg_rowsecurity];
+	bool			isnull[Natts_pg_rowsecurity];
+	bool			replaces[Natts_pg_rowsecurity];
+	ObjectAddress	target;
+	ObjectAddress	myself;
+
+	/* Parse the supplied clause */
+	pstate = make_parsestate(NULL);
+
+	rte = addRangeTableEntryForRelation(pstate, relation,
+										NULL, false, false);
+	addRTEtoQuery(pstate, rte, false, true, true);
+
+	qual = transformWhereClause(pstate, copyObject(clause),
+								EXPR_KIND_ROW_SECURITY,
+								"ROW SECURITY");
+	/* zero-clear */
+	memset(values,   0, sizeof(values));
+	memset(replaces, 0, sizeof(replaces));
+	memset(isnull,   0, sizeof(isnull));
+
+	/* Update or Insert an entry to pg_rowsecurity catalog  */
+	catalog = heap_open(RowSecurityRelationId, RowExclusiveLock);
+
+	ScanKeyInit(&skeys[0],
+				Anum_pg_rowsecurity_rsecrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(RelationGetRelid(relation)));
+	ScanKeyInit(&skeys[1],
+				Anum_pg_rowsecurity_rseccmd,
+				BTEqualStrategyNumber, F_CHAREQ,
+				CharGetDatum(rseccmd));
+	sscan = systable_beginscan(catalog, RowSecurityRelidIndexId, true,
+							   NULL, 2, skeys);
+	oldtup = systable_getnext(sscan);
+	if (HeapTupleIsValid(oldtup))
+	{
+		rowsecId = HeapTupleGetOid(oldtup);
+
+		replaces[Anum_pg_rowsecurity_rsecqual - 1] = true;
+		values[Anum_pg_rowsecurity_rsecqual - 1]
+			= CStringGetTextDatum(nodeToString(qual));
+
+		newtup = heap_modify_tuple(oldtup,
+								   RelationGetDescr(catalog),
+								   values, isnull, replaces);
+		simple_heap_update(catalog, &newtup->t_self, newtup);
+
+		deleteDependencyRecordsFor(RowSecurityRelationId, rowsecId, false);
+	}
+	else
+	{
+		values[Anum_pg_rowsecurity_rsecrelid - 1]
+			= ObjectIdGetDatum(relationId);
+		values[Anum_pg_rowsecurity_rseccmd - 1]
+			= CharGetDatum(rseccmd);
+		values[Anum_pg_rowsecurity_rsecqual - 1]
+			= CStringGetTextDatum(nodeToString(qual));
+		newtup = heap_form_tuple(RelationGetDescr(catalog),
+								 values, isnull);
+		rowsecId = simple_heap_insert(catalog, newtup);
+	}
+	CatalogUpdateIndexes(catalog, newtup);
+
+	heap_freetuple(newtup);
+
+	/* records dependencies of row-security policy and relation/columns */
+	target.classId = RelationRelationId;
+	target.objectId = relationId;
+	target.objectSubId = 0;
+
+	myself.classId = RowSecurityRelationId;
+	myself.objectId = rowsecId;
+	myself.objectSubId = 0;
+
+	recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
+
+	recordDependencyOnExpr(&myself, qual, pstate->p_rtable,
+						   DEPENDENCY_NORMAL);
+	free_parsestate(pstate);
+
+	systable_endscan(sscan);
+	heap_close(catalog, RowExclusiveLock);
+}
+
+/*
+ * Remove row-security policy row of pg_rowsecurity
+ */
+static void
+DeletePolicyRow(Relation relation, char rseccmd)
+{
+	Assert(rseccmd == ROWSECURITY_CMD_ALL);
+
+	if (relation->rsdesc)
+	{
+		ObjectAddress	address;
+
+		address.classId = RowSecurityRelationId;
+		address.objectId = relation->rsdesc->rsall.rsecid;
+		address.objectSubId = 0;
+
+		performDeletion(&address, DROP_RESTRICT, 0);
+	}
+	else
+	{
+		/* Nothing to do here */
+		elog(INFO, "relation %s has no row-security policy, skipped",
+			 RelationGetRelationName(relation));
+	}
+}
+
+/*
+ * Guts of row-security policy deletion.
+ */
+void
+RemoveRowSecurityById(Oid rowsecId)
+{
+	Relation	catalog;
+	ScanKeyData	skey;
+	SysScanDesc	sscan;
+	HeapTuple	tuple;
+	Relation	rel;
+	Oid			relid;
+
+	catalog = heap_open(RowSecurityRelationId, RowExclusiveLock);
+
+	ScanKeyInit(&skey,
+				ObjectIdAttributeNumber,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(rowsecId));
+	sscan = systable_beginscan(catalog, RowSecurityOidIndexId, true,
+							   NULL, 1, &skey);
+	tuple = systable_getnext(sscan);
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "could not find tuple for row-security %u", rowsecId);
+
+	/*
+	 * Open and exclusive-lock the relation the row-security belongs to.
+	 */
+	relid = ((Form_pg_rowsecurity) GETSTRUCT(tuple))->rsecrelid;
+
+	rel = heap_open(relid, AccessExclusiveLock);
+
+	simple_heap_delete(catalog, &tuple->t_self);
+
+	/* Ensure relcache entries of other session being rebuilt */
+	CacheInvalidateRelcache(rel);
+
+	heap_close(rel, NoLock);
+
+	systable_endscan(sscan);
+	heap_close(catalog, RowExclusiveLock);
+}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 2a46cfc..8ee5353 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -50,6 +50,7 @@
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_rewrite.h"
+#include "catalog/pg_rowsecurity.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
@@ -932,6 +933,11 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	else
 		relation->trigdesc = NULL;
 
+	if (relation->rd_rel->relhasrowsecurity)
+		RelationBuildRowSecurity(relation);
+	else
+		relation->rsdesc = NULL;
+
 	/*
 	 * if it's an index, initialize index-related information
 	 */
@@ -1840,6 +1846,8 @@ RelationDestroyRelation(Relation relation)
 		MemoryContextDelete(relation->rd_indexcxt);
 	if (relation->rd_rulescxt)
 		MemoryContextDelete(relation->rd_rulescxt);
+	if (relation->rsdesc)
+		MemoryContextDelete(relation->rsdesc->rscxt);
 	if (relation->rd_fdwroutine)
 		pfree(relation->rd_fdwroutine);
 	pfree(relation);
@@ -3166,7 +3174,13 @@ RelationCacheInitializePhase3(void)
 				relation->rd_rel->relhastriggers = false;
 			restart = true;
 		}
-
+		if (relation->rd_rel->relhasrowsecurity && relation->rsdesc == NULL)
+		{
+			RelationBuildRowSecurity(relation);
+			if (relation->rsdesc == NULL)
+				relation->rd_rel->relhasrowsecurity = false;
+			restart = true;
+		}
 		/* Release hold on the relation */
 		RelationDecrementReferenceCount(relation);
 
@@ -4478,6 +4492,7 @@ load_relcache_init_file(bool shared)
 		rel->rd_rules = NULL;
 		rel->rd_rulescxt = NULL;
 		rel->trigdesc = NULL;
+		rel->rsdesc = NULL;
 		rel->rd_indexprs = NIL;
 		rel->rd_indpred = NIL;
 		rel->rd_exclops = NULL;
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 8948589..de9323e 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -147,6 +147,7 @@ typedef enum ObjectClass
 	OCLASS_DEFACL,				/* pg_default_acl */
 	OCLASS_EXTENSION,			/* pg_extension */
 	OCLASS_EVENT_TRIGGER,		/* pg_event_trigger */
+	OCLASS_ROWSECURITY,			/* pg_rowsecurity */
 	MAX_OCLASS					/* MUST BE LAST */
 } ObjectClass;
 
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 0515b75..4f46c5d 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -313,6 +313,11 @@ DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(
 DECLARE_UNIQUE_INDEX(pg_range_rngtypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
 #define RangeTypidIndexId					3542
 
+DECLARE_UNIQUE_INDEX(pg_rowsecurity_oid_index, 5001, on pg_rowsecurity using btree(oid oid_ops));
+#define RowSecurityOidIndexId				5001
+DECLARE_UNIQUE_INDEX(pg_rowsecurity_relid_index, 5002, on pg_rowsecurity using btree(rsecrelid oid_ops, rseccmd char_ops));
+#define RowSecurityRelidIndexId				5002
+
 /* last step of initialization script: build the indexes declared above */
 BUILD_INDICES
 
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index f2fb317..bbbf62d 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -65,6 +65,7 @@ CATALOG(pg_class,1259) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83) BKI_SCHEMA_MACRO
 	bool		relhasrules;	/* has (or has had) any rules */
 	bool		relhastriggers; /* has (or has had) any TRIGGERs */
 	bool		relhassubclass; /* has (or has had) derived classes */
+	bool		relhasrowsecurity;	/* has (or has had) row-security policy */
 	bool		relispopulated; /* matview currently holds query results */
 	char		relreplident;	/* see REPLICA_IDENTITY_xxx constants  */
 	TransactionId relfrozenxid; /* all Xids < this are frozen in this rel */
@@ -94,7 +95,7 @@ typedef FormData_pg_class *Form_pg_class;
  * ----------------
  */
 
-#define Natts_pg_class					29
+#define Natts_pg_class					30
 #define Anum_pg_class_relname			1
 #define Anum_pg_class_relnamespace		2
 #define Anum_pg_class_reltype			3
@@ -118,12 +119,13 @@ typedef FormData_pg_class *Form_pg_class;
 #define Anum_pg_class_relhasrules		21
 #define Anum_pg_class_relhastriggers	22
 #define Anum_pg_class_relhassubclass	23
-#define Anum_pg_class_relispopulated	24
-#define Anum_pg_class_relreplident		25
-#define Anum_pg_class_relfrozenxid		26
-#define Anum_pg_class_relminmxid		27
-#define Anum_pg_class_relacl			28
-#define Anum_pg_class_reloptions		29
+#define Anum_pg_class_relhasrowsecurity	24
+#define Anum_pg_class_relispopulated	25
+#define Anum_pg_class_relreplident		26
+#define Anum_pg_class_relfrozenxid		27
+#define Anum_pg_class_relminmxid		28
+#define Anum_pg_class_relacl			29
+#define Anum_pg_class_reloptions		30	
 
 /* ----------------
  *		initial contents of pg_class
@@ -138,13 +140,14 @@ typedef FormData_pg_class *Form_pg_class;
  * Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId;
  * similarly, "1" in relminmxid stands for FirstMultiXactId
  */
-DATA(insert OID = 1247 (  pg_type		PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 30 0 t f f f f t n 3 1 _null_ _null_ ));
+
+DATA(insert OID = 1247 (  pg_type		PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 30 0 t f f f f f t n 3 1 _null_ _null_ ));
 DESCR("");
-DATA(insert OID = 1249 (  pg_attribute	PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 21 0 f f f f f t n 3 1 _null_ _null_ ));
+DATA(insert OID = 1249 (  pg_attribute	PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 21 0 f f f f f f t n 3 1 _null_ _null_ ));
 DESCR("");
-DATA(insert OID = 1255 (  pg_proc		PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f p r 27 0 t f f f f t n 3 1 _null_ _null_ ));
+DATA(insert OID = 1255 (  pg_proc		PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f p r 27 0 t f f f f f t n 3 1 _null_ _null_ ));
 DESCR("");
-DATA(insert OID = 1259 (  pg_class		PGNSP 83 0 PGUID 0 0 0 0 0 0 0 f f p r 29 0 t f f f f t n 3 1 _null_ _null_ ));
+DATA(insert OID = 1259 (  pg_class		PGNSP 83 0 PGUID 0 0 0 0 0 0 0 f f p r 30 0 t f f f f f t n 3 1 _null_ _null_ ));
 DESCR("");
 
 
diff --git a/src/include/catalog/pg_rowsecurity.h b/src/include/catalog/pg_rowsecurity.h
new file mode 100644
index 0000000..10aed9a
--- /dev/null
+++ b/src/include/catalog/pg_rowsecurity.h
@@ -0,0 +1,74 @@
+/*
+ * pg_rowsecurity.h
+ *   definition of the system catalog for row-security policy (pg_rowsecurity)
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ */
+#ifndef PG_ROWSECURITY_H
+#define PG_ROWSECURITY_H
+
+#include "catalog/genbki.h"
+#include "nodes/primnodes.h"
+#include "utils/memutils.h"
+#include "utils/relcache.h"
+
+/* ----------------
+ *		pg_rowlevelsec definition. cpp turns this into
+ *		typedef struct FormData_pg_rowlevelsec
+ * ----------------
+ */
+#define RowSecurityRelationId	5000
+
+CATALOG(pg_rowsecurity,5000)
+{
+	/* Oid of the relation that has row-security policy */
+	Oid				rsecrelid;
+
+	/* One of ROWSECURITY_CMD_* below */
+	char			rseccmd;
+#ifdef CATALOG_VARLEN
+	pg_node_tree	rsecqual;
+#endif
+} FormData_pg_rowsecurity;
+
+/* ----------------
+ *		Form_pg_rowlevelsec corresponds to a pointer to a row with
+ *		the format of pg_rowlevelsec relation.
+ * ----------------
+ */
+typedef FormData_pg_rowsecurity *Form_pg_rowsecurity;
+
+/* ----------------
+ * 		compiler constants for pg_rowlevelsec
+ * ----------------
+ */
+#define Natts_pg_rowsecurity				3
+#define Anum_pg_rowsecurity_rsecrelid		1
+#define Anum_pg_rowsecurity_rseccmd			2
+#define Anum_pg_rowsecurity_rsecqual		3
+
+#define ROWSECURITY_CMD_ALL			'a'
+#define ROWSECURITY_CMD_SELECT		's'
+#define ROWSECURITY_CMD_INSERT		'i'
+#define ROWSECURITY_CMD_UPDATE		'u'
+#define ROWSECURITY_CMD_DELETE		'd'
+
+typedef struct
+{
+	Oid			rsecid;
+	Expr	   *qual;
+	bool		hassublinks;
+} RowSecurityEntry;
+
+typedef struct
+{
+	MemoryContext		rscxt;
+	RowSecurityEntry	rsall;	/* row-security policy for ALL */
+} RowSecurityDesc;
+
+extern void	RelationBuildRowSecurity(Relation relation);
+extern void	RemoveRowSecurityById(Oid relationId);
+
+#endif  /* PG_ROWSECURITY_H */
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 85598e8..9c7c382 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -63,7 +63,8 @@ typedef enum ParseExprKind
 	EXPR_KIND_INDEX_PREDICATE,	/* index predicate */
 	EXPR_KIND_ALTER_COL_TRANSFORM,		/* transform expr in ALTER COLUMN TYPE */
 	EXPR_KIND_EXECUTE_PARAMETER,	/* parameter value in EXECUTE */
-	EXPR_KIND_TRIGGER_WHEN		/* WHEN condition in CREATE TRIGGER */
+	EXPR_KIND_TRIGGER_WHEN,		/* WHEN condition in CREATE TRIGGER */
+	EXPR_KIND_ROW_SECURITY 		/* ROW SECURITY policy for a table */
 } ParseExprKind;
 
 
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 9b8a4c9..83a8d03 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -18,6 +18,7 @@
 #include "catalog/pg_am.h"
 #include "catalog/pg_class.h"
 #include "catalog/pg_index.h"
+#include "catalog/pg_rowsecurity.h"
 #include "fmgr.h"
 #include "nodes/bitmapset.h"
 #include "rewrite/prs2lock.h"
@@ -110,6 +111,7 @@ typedef struct RelationData
 	RuleLock   *rd_rules;		/* rewrite rules */
 	MemoryContext rd_rulescxt;	/* private memory cxt for rd_rules, if any */
 	TriggerDesc *trigdesc;		/* Trigger info, or NULL if rel has none */
+	RowSecurityDesc	*rsdesc;	/* Row-security policy, or NULL */
 
 	/*
 	 * The index chosen as the relation's replication identity or
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index a62a3e3..a41344b 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -121,6 +121,7 @@ pg_pltemplate|t
 pg_proc|t
 pg_range|t
 pg_rewrite|t
+pg_rowsecurity|t
 pg_seclabel|t
 pg_shdepend|t
 pg_shdescription|t
-- 
1.8.3.1