Hello,

I've implemented basic support for Row-level security policies (introduced in PostgreSQL 9.5) in PgAdmin3. Please, have a look at patches attached to mail.

First patch contains pgPolicy tree object, dlgPolicy dialog, some small changes in the pgTable and dlgTable code to represent new security options (ENABLE/FORCE RLS). I've not included changes in xrcDialogs.cpp into the patch.

Second patch implements tab-completion for new statements (CREATE/ALTER/DROP POLICY). I've decided to put these into separate patch, because I don't know whether I've done it correctly: seems like tab-complete.inc used to be generated by tabcomplete.pl once, but it doesn't look like a viable option anymore (script failed at first match with latest tab-complete.c from psql repository). So I've just edited tab-complete.inc manually.

P.S. Sorry for my previous mail, looks like it got broken by my mail server.

Best Regards,
Alexander
From 795125bf37e398a9299f88fa5679cc7ec7b09cae Mon Sep 17 00:00:00 2001
From: Alexander Polyakov <alexp...@yandex.ru>
Date: Thu, 14 Apr 2016 12:19:59 +0400
Subject: [PATCH] Row security policy support: object and dialog for policy,
 policy parameters in table object (Enable/Force RLS)

---
 pgadmin/dlg/dlgPolicy.cpp            | 288 ++++++++++++++++++++++++++
 pgadmin/dlg/dlgProperty.cpp          |   1 +
 pgadmin/dlg/dlgTable.cpp             |  56 +++++
 pgadmin/dlg/module.mk                |   1 +
 pgadmin/frm/events.cpp               |   1 +
 pgadmin/include/dlg/dlgPolicy.h      |  66 ++++++
 pgadmin/include/dlg/dlgTable.h       |   2 +
 pgadmin/include/dlg/module.mk        |   1 +
 pgadmin/include/images/module.mk     |   3 +
 pgadmin/include/images/policies.png  | Bin 0 -> 439 bytes
 pgadmin/include/images/policy-sm.png | Bin 0 -> 394 bytes
 pgadmin/include/images/policy.png    | Bin 0 -> 472 bytes
 pgadmin/include/schema/module.mk     |   1 +
 pgadmin/include/schema/pgPolicy.h    | 123 +++++++++++
 pgadmin/include/schema/pgTable.h     |  20 +-
 pgadmin/include/utils/misc.h         |   1 +
 pgadmin/pgAdmin3.vcxproj             |  10 +-
 pgadmin/pgAdmin3.vcxproj.filters     |  24 +++
 pgadmin/schema/module.mk             |   1 +
 pgadmin/schema/pgPolicy.cpp          | 391 +++++++++++++++++++++++++++++++++++
 pgadmin/schema/pgTable.cpp           |  46 +++++
 pgadmin/ui/dlgPolicy.xrc             | 278 +++++++++++++++++++++++++
 pgadmin/ui/dlgTable.xrc              |  30 +++
 pgadmin/ui/module.mk                 |   1 +
 24 files changed, 1343 insertions(+), 2 deletions(-)
 create mode 100644 pgadmin/dlg/dlgPolicy.cpp
 create mode 100644 pgadmin/include/dlg/dlgPolicy.h
 create mode 100644 pgadmin/include/images/policies.png
 create mode 100644 pgadmin/include/images/policy-sm.png
 create mode 100644 pgadmin/include/images/policy.png
 create mode 100644 pgadmin/include/schema/pgPolicy.h
 create mode 100644 pgadmin/schema/pgPolicy.cpp
 create mode 100644 pgadmin/ui/dlgPolicy.xrc

diff --git a/pgadmin/dlg/dlgPolicy.cpp b/pgadmin/dlg/dlgPolicy.cpp
new file mode 100644
index 0000000..49d83e1
--- /dev/null
+++ b/pgadmin/dlg/dlgPolicy.cpp
@@ -0,0 +1,288 @@
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin III - PostgreSQL Tools
+//
+// Copyright (C) 2002 - 2016, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+// dlgPolicy.cpp - Policy property dialog
+//
+//////////////////////////////////////////////////////////////////////////
+
+// wxWindows headers
+#include <wx/wx.h>
+
+// App headers
+#include "pgAdmin3.h"
+#include "utils/misc.h"
+#include "utils/pgDefs.h"
+
+#include "ctl/ctlSQLBox.h"
+#include "dlg/dlgPolicy.h"
+#include "schema/pgPolicy.h"
+#include "schema/pgTable.h"
+#include "schema/pgCollection.h"
+
+#define rbxCommand                     CTRL_RADIOBOX("rbxCommand")
+#define btnAddRole                     CTRL_BUTTON("btnAddRole")
+#define btnDelRole                     CTRL_BUTTON("btnDelRole")
+#define lbRolesSelected                CTRL_LISTBOX("lbRolesSelected")
+#define lbRolesAll                     CTRL_LISTBOX("lbRolesAll")
+#define sqlBoxUsing                    CTRL_SQLBOX("sqlBoxUsing")
+#define sqlBoxWithCheck                CTRL_SQLBOX("sqlBoxWithCheck")
+
+BEGIN_EVENT_TABLE(dlgPolicy, dlgProperty)
+       EVT_RADIOBOX(XRCID("rbxCommand"),               dlgProperty::OnChange)
+       EVT_BUTTON(XRCID("btnAddRole"),                 dlgPolicy::OnAddRole)
+       EVT_BUTTON(XRCID("btnDelRole"),                 dlgPolicy::OnDelRole)
+       EVT_STC_MODIFIED(XRCID("sqlBoxUsing"),          
dlgProperty::OnChangeStc)
+       EVT_STC_MODIFIED(XRCID("sqlBoxWithCheck"),      
dlgProperty::OnChangeStc)
+END_EVENT_TABLE();
+
+dlgPolicy::dlgPolicy(pgaFactory *f, frmMain *frame, pgPolicy *p, pgTable *tab)
+       : dlgProperty(f, frame, wxT("dlgPolicy"))
+{
+       table = tab;
+       policy = p;
+}
+
+pgObject *dlgPolicy::GetObject()
+{
+       return policy;
+}
+
+pgObject *dlgPolicy::CreateObject(pgCollection *collection)
+{
+       pgObject *obj = policyFactory.CreateObjects(collection, 0,
+                       wxT("\n   AND p.polname=") + qtDbString(GetName()) +
+                       wxT("\n   AND p.polrelid=") + table->GetOidStr());
+       return obj;
+}
+
+void dlgPolicy::SetPolicyCommand(char command)
+{
+       int index = 0;
+       switch (command)
+       {
+       case POLICY_ALL: index = 0; break;
+       case POLICY_SELECT: index = 1; break;
+       case POLICY_INSERT: index = 2; break;
+       case POLICY_UPDATE: index = 3; break;
+       case POLICY_DELETE: index = 4; break;
+       }
+
+       rbxCommand->SetSelection(index);
+}
+
+char dlgPolicy::GetPolicyCommand() const
+{
+       switch (rbxCommand->GetSelection())
+       {
+       default:
+       case 0: return POLICY_ALL; 
+       case 1: return POLICY_SELECT;
+       case 2: return POLICY_INSERT;
+       case 3: return POLICY_UPDATE;
+       case 4: return POLICY_DELETE;
+       }
+}
+
+void dlgPolicy::SetPolicyRoles(const wxArrayString &roles)
+{
+       for (int i = 0; i < roles.size(); i++)
+       {
+               SelectRole(roles[i]);
+       }
+}
+
+void dlgPolicy::SetPolicyUsingExpr(const wxString &expr)
+{
+       sqlBoxUsing->SetText(expr);
+}
+
+wxString dlgPolicy::GetPolicyUsingExpr() const
+{
+       return sqlBoxUsing->GetText();
+}
+
+void dlgPolicy::SetPolicyWithCheckExpr(const wxString &expr)
+{
+       sqlBoxWithCheck->SetText(expr);
+}
+
+wxString dlgPolicy::GetPolicyWithCheckExpr() const
+{
+       return sqlBoxWithCheck->GetText();
+}
+
+int dlgPolicy::Go(bool modal)
+{
+       PopulateAllRoles();
+
+       if (policy)
+       {
+               SetPolicyCommand(policy->GetPolicyCommand());
+               rbxCommand->Disable();
+               SetPolicyRoles(policy->GetPolicyRolesSorted());
+               SetPolicyUsingExpr(policy->GetPolicyUsingExpression());
+               SetPolicyWithCheckExpr(policy->GetPolicyWithCheckExpression());
+       }
+
+       return dlgProperty::Go(modal);
+}
+
+// get sql command from pgPolicy object
+wxString dlgPolicy::GetSql()
+{
+       wxString sql;
+       if(policy)
+       {
+               sql = policy->GetAlterPolicySql(qtIdent(GetName()), 
GetPolicyRolesSorted(), GetPolicyUsingExpr(), GetPolicyWithCheckExpr());
+       }
+       else
+       {
+               sql = pgPolicy::GetCreatePolicySql(qtIdent(GetName()), 
table->GetQuotedFullIdentifier(), GetPolicyCommand(), GetPolicyRolesSorted(),
+                       GetPolicyUsingExpr(), GetPolicyWithCheckExpr());
+       }
+
+       AppendComment(sql, wxT("POLICY ") + qtIdent(GetName())
+                       + wxT(" ON ") + table->GetQuotedFullIdentifier(), 
policy);
+
+       return sql;
+}
+
+void dlgPolicy::CheckChange()
+{
+       if (policy)
+       {
+               EnableOK(didChange());
+       }
+       else
+       {
+               wxString name = GetName();
+               bool enable = true;
+               CheckValid(enable, !name.IsEmpty(), _("Please specify name."));
+               EnableOK(enable);
+       }
+}
+
+// populate list box with roles from catalog
+void dlgPolicy::PopulateAllRoles()
+{
+       lbRolesAll->Clear();
+       // not sure if we really need these in pgAdmin
+       //lbRolesAll->Append(wxT("CURRENT_USER"));
+       //lbRolesAll->Append(wxT("SESSION_USER"));
+
+       if (connection)
+       {
+               wxString sql =
+                       wxT("SELECT rolname\n")
+                       wxT("  FROM pg_roles r\n")
+                       wxT("  ORDER BY rolname");
+
+               pgSet *roles = connection->ExecuteSet(sql);
+               if (roles)
+               {
+                       while (!roles->Eof())
+                       {
+                               lbRolesAll->Append(roles->GetVal(0));
+                               roles->MoveNext();
+                       }
+
+                       delete roles;
+               }
+       }
+}
+
+// add role to list and reflect list box changes
+// listIndex - index of entry in 'all roles' list box
+void dlgPolicy::SelectRole(int listIndex)
+{
+       if (listIndex >= 0)
+       {
+               wxString role = lbRolesAll->GetString(listIndex);
+               lbRolesSelected->Append(role);
+               lbRolesAll->Delete(listIndex);
+               roleNames.Add(role);
+       }
+}
+
+// add role to list of roles and and reflect list box changes
+void dlgPolicy::SelectRole(const wxString &role)
+{
+       lbRolesSelected->Append(role);
+       lbRolesAll->Delete(lbRolesAll->FindString(role, true));
+       roleNames.Add(role);
+}
+
+// remove role from list and reflect list box changes
+// listIndex - index of entry in 'selected roles' list box 
+void dlgPolicy::DeselectRole(int listIndex)
+{
+       if (listIndex >= 0)
+       {
+               wxString role = lbRolesSelected->GetString(listIndex);
+               lbRolesAll->Append(role);
+               lbRolesSelected->Delete(listIndex);
+               roleNames.Remove(role);
+       }
+}
+
+// test for changes
+bool dlgPolicy::didChange()
+{
+       if (GetName() != policy->GetName())
+               return true;
+       if (GetPolicyRolesSorted() != policy->GetPolicyRolesSorted())
+               return true;
+       if (GetPolicyUsingExpr() != policy->GetPolicyUsingExpression())
+               return true;
+       if (GetPolicyWithCheckExpr() != policy->GetPolicyWithCheckExpression())
+               return true;
+       if (txtComment->GetValue() != policy->GetComment())
+               return true;
+
+       return false;
+}
+
+int IntArrayCmp(int *a, int *b)
+{
+       if (*a == *b)
+               return 0;
+
+       if (*a > *b)
+               return 1;
+       else
+               return -1;
+}
+
+// add role button
+void dlgPolicy::OnAddRole(wxCommandEvent &ev)
+{
+       wxArrayInt selection;
+       lbRolesAll->GetSelections(selection);
+       selection.Sort(IntArrayCmp);
+
+       for (int i = selection.size() - 1; i >= 0; i--)
+       {
+               SelectRole(selection[i]);
+       }
+
+       OnChange(ev);
+}
+
+// remove role button
+void dlgPolicy::OnDelRole(wxCommandEvent &ev)
+{
+       wxArrayInt selection;
+       lbRolesSelected->GetSelections(selection);
+       selection.Sort(IntArrayCmp);
+
+       for (int i = selection.size() - 1; i >= 0; i--)
+       {
+               DeselectRole(selection[i]);
+       }
+
+       OnChange(ev);
+}
\ No newline at end of file
diff --git a/pgadmin/dlg/dlgProperty.cpp b/pgadmin/dlg/dlgProperty.cpp
index 7381729..578fa1b 100644
--- a/pgadmin/dlg/dlgProperty.cpp
+++ b/pgadmin/dlg/dlgProperty.cpp
@@ -1152,6 +1152,7 @@ void dlgProperty::InitDialog(frmMain *frame, pgObject 
*node)
                case PGM_PRIMARYKEY:
                case PGM_UNIQUE:
                case PGM_TRIGGER:
+               case PGM_POLICY:
                case PGM_RULE: // Rules are technically table objects! Yeuch
                case EDB_PACKAGEFUNCTION:
                case EDB_PACKAGEVARIABLE:
diff --git a/pgadmin/dlg/dlgTable.cpp b/pgadmin/dlg/dlgTable.cpp
index 8939a7c..32ad9c0 100644
--- a/pgadmin/dlg/dlgTable.cpp
+++ b/pgadmin/dlg/dlgTable.cpp
@@ -32,6 +32,7 @@
 #include "schema/pgForeignKey.h"
 #include "schema/pgIndexConstraint.h"
 #include "schema/pgDatatype.h"
+#include "schema/pgPolicy.h"
 #include "ctl/ctlSeclabelPanel.h"
 
 
@@ -104,6 +105,10 @@
 #define txtToastFreezeTableAge    CTRL_TEXT("txtToastFreezeTableAge")
 #define stToastFreezeTableAgeCurr CTRL_STATIC("stToastFreezeTableAgeCurr")
 
+/* Row level security options */
+#define chkPolicyEnable                CTRL_CHECKBOX("chkPolicyEnable")
+#define chkPolicyForce         CTRL_CHECKBOX("chkPolicyForce")
+
 
 BEGIN_EVENT_TABLE(dlgTable, dlgSecurityProperty)
        EVT_CHECKBOX(XRCID("chkUnlogged"),              dlgProperty::OnChange)
@@ -150,6 +155,9 @@ BEGIN_EVENT_TABLE(dlgTable, dlgSecurityProperty)
        EVT_TEXT(XRCID("txtToastFreezeMaxAge"),             
dlgTable::OnChangeVacuum)
        EVT_TEXT(XRCID("txtToastFreezeTableAge"),           
dlgTable::OnChangeVacuum)
 
+       EVT_CHECKBOX(XRCID("chkPolicyEnable"),          dlgProperty::OnChange)
+       EVT_CHECKBOX(XRCID("chkPolicyForce"),                   
dlgProperty::OnChange)
+
        EVT_BUTTON(wxID_OK,                             dlgTable::OnOK)
 
 #ifdef __WXMAC__
@@ -236,6 +244,23 @@ int dlgTable::Go(bool modal)
        PrepareTablespace(cbTablespace);
        PopulateDatatypeCache();
 
+       // Row level security options 
+       if (connection->BackendMinimumVersion(9, 5))
+       {
+               chkPolicyEnable->Enable();
+               chkPolicyForce->Enable();
+               if (table)
+               {
+                       
chkPolicyEnable->SetValue(table->GetRowLevelSecurityEnabled());
+                       
chkPolicyForce->SetValue(table->GetForceRowLevelSecurity());
+               }
+       }
+       else
+       {
+               chkPolicyEnable->Disable();
+               chkPolicyForce->Disable();
+       }
+
        if (connection->BackendMinimumVersion(9, 1))
        {
                seclabelPage->SetConnection(connection);
@@ -1472,6 +1497,10 @@ wxString dlgTable::GetSql()
                }
        }
 
+       // Row level security options 
+       if (connection->BackendMinimumVersion(9, 5))
+               AppendPolicySql(sql, tabname);
+
        // Comments
        for (pos = 0 ; pos < lstColumns->GetItemCount() ; pos++)
        {
@@ -2112,3 +2141,30 @@ void dlgTable::OnChange(wxCommandEvent &event)
 {
        CheckChange();
 }
+
+// append ALTER TABLE statement with row level security options
+wxString& dlgTable::AppendPolicySql(wxString &sql, const wxString &tabname)
+{
+       int enableRls;
+       int forceRls;
+
+       if (table)
+       {
+               if (table->GetRowLevelSecurityEnabled() != 
chkPolicyEnable->IsChecked())
+                       enableRls = chkPolicyEnable->IsChecked() ? 
ROW_SECURUTY_ENABLE : ROW_SECURUTY_DISABLE;
+               if (table->GetForceRowLevelSecurity() != 
chkPolicyForce->IsChecked())
+                       forceRls = chkPolicyForce->IsChecked() ? 
ROW_SECURUTY_FORCE : ROW_SECURUTY_NOFORCE;
+       }
+       else
+       {
+               // default values will be omitted
+               enableRls = chkPolicyEnable->IsChecked() ? ROW_SECURUTY_ENABLE 
: ROW_SECURUTY_SKIP;
+               forceRls = chkPolicyForce->IsChecked() ? ROW_SECURUTY_FORCE : 
ROW_SECURUTY_SKIP;
+       }
+
+       wxString rlsSql = pgPolicy::GetAlterTablePolicyParamsSql(tabname, 
enableRls, forceRls);
+       if (!rlsSql.IsEmpty())
+               sql += rlsSql + wxT("\n");
+
+       return sql;
+}
\ No newline at end of file
diff --git a/pgadmin/dlg/module.mk b/pgadmin/dlg/module.mk
index c1a4e33..3d53185 100644
--- a/pgadmin/dlg/module.mk
+++ b/pgadmin/dlg/module.mk
@@ -41,6 +41,7 @@ pgadmin3_SOURCES += \
        dlg/dlgOperator.cpp \
        dlg/dlgPackage.cpp \
        dlg/dlgPgpassConfig.cpp \
+       dlg/dlgPolicy.cpp \
        dlg/dlgProperty.cpp \
        dlg/dlgReassignDropOwned.cpp \
        dlg/dlgRole.cpp \
diff --git a/pgadmin/frm/events.cpp b/pgadmin/frm/events.cpp
index 4e05f84..0b6a959 100644
--- a/pgadmin/frm/events.cpp
+++ b/pgadmin/frm/events.cpp
@@ -789,6 +789,7 @@ void frmMain::ExecDrop(bool cascaded)
                case PGM_PRIMARYKEY:
                case PGM_UNIQUE:
                case PGM_TRIGGER:
+               case PGM_POLICY:
                case PGM_RULE: // Rules are technically table objects! Yeuch
                case EDB_PACKAGEFUNCTION:
                case EDB_PACKAGEVARIABLE:
diff --git a/pgadmin/include/dlg/dlgPolicy.h b/pgadmin/include/dlg/dlgPolicy.h
new file mode 100644
index 0000000..b340309
--- /dev/null
+++ b/pgadmin/include/dlg/dlgPolicy.h
@@ -0,0 +1,66 @@
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin III - PostgreSQL Tools
+//
+// Copyright (C) 2002 - 2016, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+// dlgPolicy.h - Policy property dialog
+//
+//////////////////////////////////////////////////////////////////////////
+
+
+#ifndef DLG_POLICY_H
+#define DLG_POLICY_H
+
+#include "dlg/dlgProperty.h"
+
+class pgTable;
+class pgPolicy;
+
+class dlgPolicy : public dlgProperty
+{
+public:
+       dlgPolicy(pgaFactory *factory, frmMain *frame, pgPolicy *p, pgTable 
*tab);
+       int Go(bool modal);
+
+       void CheckChange();
+       wxString GetSql();
+       pgObject *CreateObject(pgCollection *collection);
+       pgObject *GetObject();
+
+       // ui control value getters and settes
+       void SetPolicyCommand(char command);
+       char GetPolicyCommand() const;
+       void SetPolicyRoles(const wxArrayString &roles);
+       const wxSortedArrayString& GetPolicyRolesSorted() const
+       {
+               return roleNames;
+       }
+       void SetPolicyUsingExpr(const wxString &expr);
+       wxString GetPolicyUsingExpr() const;
+       void SetPolicyWithCheckExpr(const wxString &expr);
+       wxString GetPolicyWithCheckExpr() const;
+
+private:
+       void PopulateAllRoles();
+       void SelectRole(int listIndex);
+       void SelectRole(const wxString &role);
+       void DeselectRole(int listIndex);
+
+       bool didChange();
+
+       void OnAddRole(wxCommandEvent &ev);
+       void OnDelRole(wxCommandEvent &ev);
+
+private:
+       pgTable *table;
+       pgPolicy *policy;
+
+       // sorted, so we can easily test for changes
+       wxSortedArrayString roleNames;
+
+       DECLARE_EVENT_TABLE()
+};
+
+#endif
\ No newline at end of file
diff --git a/pgadmin/include/dlg/dlgTable.h b/pgadmin/include/dlg/dlgTable.h
index 1b82fe0..f28f05c 100644
--- a/pgadmin/include/dlg/dlgTable.h
+++ b/pgadmin/include/dlg/dlgTable.h
@@ -125,6 +125,8 @@ private:
                 toastTableFreezeTableAge;
        wxString toastTableVacFactor;
 
+       wxString& AppendPolicySql(wxString &sql, const wxString &tabname);
+
        DECLARE_EVENT_TABLE()
 };
 
diff --git a/pgadmin/include/dlg/module.mk b/pgadmin/include/dlg/module.mk
index 8fed68d..f5e908f 100644
--- a/pgadmin/include/dlg/module.mk
+++ b/pgadmin/include/dlg/module.mk
@@ -41,6 +41,7 @@ pgadmin3_SOURCES += \
        include/dlg/dlgOperator.h \
        include/dlg/dlgPackage.h \
        include/dlg/dlgPgpassConfig.h \
+       include/dlg/dlgPolicy.h \
        include/dlg/dlgProperty.h \
        include/dlg/dlgReassignDropOwned.h \
        include/dlg/dlgRole.h \
diff --git a/pgadmin/include/images/module.mk b/pgadmin/include/images/module.mk
index 6d60abf..08ac833 100644
--- a/pgadmin/include/images/module.mk
+++ b/pgadmin/include/images/module.mk
@@ -218,6 +218,9 @@ pgadmin3_SOURCES += \
        include/images/pgAdmin3.png \
        include/images/pgAdmin3-16.png \
        include/images/pgAdmin3-32.png \
+       include/images/policies.png \
+       include/images/policy.png \
+       include/images/policy-sm.png \
        include/images/primarykey.png \
        include/images/procedures.png \
        include/images/procedure.png \
diff --git a/pgadmin/include/images/policies.png 
b/pgadmin/include/images/policies.png
new file mode 100644
index 
0000000000000000000000000000000000000000..08a9c2d6df12bca22bd9fc5a363925749bc6cae7
GIT binary patch
literal 439
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl
z_H+M9WCij$3p^r=85sBugD~Uq{1qucL5ULAh?3y^w370~qEv>0#LT=By}Z;C1rt33
zJ=4@yqg0@w8lEnWAr}5~C!F>=93aqke)SI@w&*=uxW5PGXkF5BGQZos*CF09Cd1`I
zE5BmN^a(mboSr3$DyK95Hx^rEDtWQl85vtXKka|z=ijbx%jW-gx+)rHv0A?<b~@+b
z4NfAiCYAqPo1Wf0+#scXkcr7gTJ_+AN;!RLRmabFUw>Q_D5N>T^IJau%KsKix}U%1
zS{vD(lfFfi#qmJ14^v}lxeNPdVHe$tv&u6b#N@L6U7qOKptA4%W{bXLHu2Q;_NSO+
z-PL|>(D}77ms#&*TzwC#=cEq`lN}Tes@{}l<l>#+S$0sh_nsspSF=x(eCf|3=f&3z
zH(MtCdi+$=-kV88jKTBPXT7cBv37eZQxBYJ+AhV&<*nj*dCDf8#Wv@)MO+VF;R>)i
e8LzSQiqui7d;|Wl=-0q-W$<+Mb6Mw<&;$VcjHGD*

literal 0
HcmV?d00001

diff --git a/pgadmin/include/images/policy-sm.png 
b/pgadmin/include/images/policy-sm.png
new file mode 100644
index 
0000000000000000000000000000000000000000..cf335fa53c3d8f9470721893d475bdaa8349ea74
GIT binary patch
literal 394
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl
z_H+M9WCij$3p^r=85sBugD~Uq{1qucL5ULAh?3y^w370~qEv>0#LT=By}Z;C1rt33
zJ=4@yqg0@p-<~dxAr}705;hAm&TjqR6+B<KbzxKhqes%|t>ypU-`mT5%zwV!(F5n_
z+5W!2PrhAI;ozRqrT?4NUH|Ie3;DV~CtH_|Z@$>~{VCq2iOf#RT>kUV7UK?TX*#_8
z_+S0ILCv=ts=vScTmRv~!T;_2^8f08eo~cSUK(&i!Ez4U9(|1;^_D76{y(22WWnaO
zaD!tOhg3sPV~|G99=3)#?R||w1w6usGaWuY(<*QX%~}6He(!2dpnO5unKvm0y_XpR
zL|lFP|H~UH%dL^n;5n{fccbBM1y9A_Utj;5=imEN|NGn9|M~a#B|dn6e}A)K7_$R|
jiX%^(mb^$qBO`;Uam9K=ClPO8Ffe$!`njxgN@xNAUTB?z

literal 0
HcmV?d00001

diff --git a/pgadmin/include/images/policy.png 
b/pgadmin/include/images/policy.png
new file mode 100644
index 
0000000000000000000000000000000000000000..2e04101d20605bb8e5d744ad3d3ed0ab3b119674
GIT binary patch
literal 472
zcmV;}0Vn>6P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00001b5ch_0Itp)
z=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D02p*dSaefwW^{L9
za%BK;VQFr3E^cLXAT%y8E;VI^GGzb&0ZB<jK~y+TrIO1^!$1^<`zk(@v8~#R;9dev
zy|jwr#;vGn3%-MhjVQWs?^bZvCM~of<YFPDS^mzMv;)bcLUG_@hB^QFm`tXy92>fg
zb-4Zvv1m=2jqd=1!GJRy4q=+6#AGtTbUMY=M<Q9YCxU~ACN^(&(0FR0(QiTPw<Qjr
z&rs^rpc(De0_AQO$}NND48=|rrEU#P<5U*Vu`SEOXf)!C$72b{aYS7b{HQ9v6a}HC
z{Rw3#yHb#al~d4Ou3&kzJXl0pO9J)rh+h=la26Ixttq=wu%%Z})T=32gwIr`_3_KO
zET9Ks+cv*x*LC4}o`6LJVHhHcB3VF>hyF3yiIdk00gLdt{dS4U{XU;10exS&TSvK9
z$98W|z{&z@=7RSe!SdL??;{8T%w{u*`FxHzjyWj-4cb_TMcaQzq3{d$z%^$fS{q0J
O0000<MNUMnLSTXe7saap

literal 0
HcmV?d00001

diff --git a/pgadmin/include/schema/module.mk b/pgadmin/include/schema/module.mk
index b36a8c5..3ba1f99 100644
--- a/pgadmin/include/schema/module.mk
+++ b/pgadmin/include/schema/module.mk
@@ -42,6 +42,7 @@ pgadmin3_SOURCES += \
        include/schema/pgOperator.h \
        include/schema/pgOperatorClass.h \
        include/schema/pgOperatorFamily.h \
+       include/schema/pgPolicy.h \
        include/schema/pgRole.h \
        include/schema/pgRule.h \
        include/schema/pgSchema.h \
diff --git a/pgadmin/include/schema/pgPolicy.h 
b/pgadmin/include/schema/pgPolicy.h
new file mode 100644
index 0000000..1202e90
--- /dev/null
+++ b/pgadmin/include/schema/pgPolicy.h
@@ -0,0 +1,123 @@
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin III - PostgreSQL Tools
+//
+// Copyright (C) 2002 - 2016, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+// pgPolicy.h PostgreSQL Row security policy
+//
+//////////////////////////////////////////////////////////////////////////
+
+#ifndef PGPOLICY_H
+#define PGPOLICY_H
+
+#include <wx/arrstr.h>
+#include "pgTable.h"
+
+class pgCollection;
+
+class pgPolicyFactory : public pgTableObjFactory
+{
+public:
+       pgPolicyFactory();
+       virtual dlgProperty *CreateDialog(frmMain *frame, pgObject *node, 
pgObject *parent);
+       virtual pgObject *CreateObjects(pgCollection *coll, ctlTree *browser, 
const wxString &restriction = wxEmptyString);
+       virtual pgCollection *CreateCollection(pgObject *obj);
+};
+extern pgPolicyFactory policyFactory;
+
+// policy command values
+enum
+{
+       POLICY_ALL = '*',
+       POLICY_SELECT = 'r',
+       POLICY_INSERT = 'a',
+       POLICY_UPDATE = 'w',
+       POLICY_DELETE = 'd'
+};
+
+// use with GetAlterTablePolicyParamsSql
+enum 
+{
+       ROW_SECURUTY_SKIP = 0,  // do not generate statement
+       ROW_SECURUTY_ENABLE,    // generate ENABLE ROW LEVEL SECURITY statement
+       ROW_SECURUTY_DISABLE,   // generate DISABLE ROW LEVEL SECURITY statement
+       ROW_SECURUTY_FORCE,             // generate FORCE ROW LEVEL SECURITY 
statement
+       ROW_SECURUTY_NOFORCE    // generate NO FORCE ROW LEVEL SECURITY 
statement
+};
+
+// Row-Level Security policy object 
+class pgPolicy : public pgTableObject
+{
+public:
+       pgPolicy(pgTable *newTable, const wxString &newName = wxT(""));
+       ~pgPolicy();
+
+       char GetPolicyCommand() const
+       {
+               return command;
+       }
+       void iSetPolicyCommand(char cmd)
+       {
+               command = cmd;
+       }
+       const wxSortedArrayString& GetPolicyRolesSorted() const
+       {
+               return roleNames;
+       }
+       void iSetPolicyRoles(const wxArrayString &names);
+       wxString GetPolicyUsingExpression() const
+       {
+               return exprUsing;
+       }
+       void iSetPolicyUsingExpression(const wxString &expr)
+       {
+               exprUsing = expr;
+       }
+       wxString GetPolicyWithCheckExpression() const
+       {
+               return exprWithCheck;
+       }
+       void iSetPolicyWithCheckExpression(const wxString &expr)
+       {
+               exprWithCheck = expr;
+       }
+
+       bool DropObject(wxFrame *frame, ctlTree *browser, bool cascaded);
+
+       
+       wxString GetSql(ctlTree *browser);
+       wxString GetTranslatedMessage(int kindOfMessage) const;
+       void ShowTreeDetail(ctlTree *browser, frmMain *form = 0, ctlListView 
*properties = 0, ctlSQLBox *sqlPane = 0);
+
+       // generate ALTER statement for table RLS parameters 
(ENABLE/DISABLE/FORCE/NOFORCE)
+       static wxString GetAlterTablePolicyParamsSql(const wxString &table, int 
enableRls, int forceRls);
+       // generate ALTER POLICY statement 
+       // arguments will be checked against current state of pgPolicy object 
and corresponding sql will be created
+       wxString GetAlterPolicySql(const wxString &name, const wxArrayString 
&sortedRoles, const wxString &exprUsing, const wxString &exprCheck) const;
+       // generate CREATE POLICY statement 
+       // default statements will be omitted
+       static wxString GetCreatePolicySql(const wxString &name, const wxString 
&table, char command, const wxArrayString &roles, 
+                                                                          
const wxString &exprUsing, const wxString &checkExpr);
+
+private:
+       static wxString GetPolicyCommandString(char command);
+       static wxString& WrapParentheses(wxString &expr);
+       static wxString GetPolicyRolesString(const wxArrayString &roles);       
+
+private:
+       char command;
+       wxSortedArrayString roleNames;  // sorted for easy change checks. empty 
list means TO PUBLIC policy
+       wxString exprUsing, exprWithCheck;
+};
+
+class pgPolicyCollection : public pgTableObjCollection
+{
+public:
+       pgPolicyCollection(pgaFactory *factory, pgTable *tbl);
+       wxString GetTranslatedMessage(int kindOfMessage) const;
+};
+
+
+#endif
\ No newline at end of file
diff --git a/pgadmin/include/schema/pgTable.h b/pgadmin/include/schema/pgTable.h
index 31eb25d..1add04c 100644
--- a/pgadmin/include/schema/pgTable.h
+++ b/pgadmin/include/schema/pgTable.h
@@ -517,6 +517,24 @@ public:
        }
        bool HasPgstattuple();
 
+       /* row level security options */
+       bool GetRowLevelSecurityEnabled() const
+       {
+               return rowSecurityEnabled;
+       }
+       void iSetRowLevelSecurityEnabled(bool enabled)
+       {
+               rowSecurityEnabled = enabled;
+       }
+       bool GetForceRowLevelSecurity() const
+       {
+               return forceRowSecurity;
+       }
+       void iSetForceRowLevelSecurity(bool force)
+       {
+               forceRowSecurity = force;
+       }
+
        virtual wxMenu *GetNewMenu();
        virtual wxString GetSql(ctlTree *browser);
        wxString GetSelectSql(ctlTree *browser);
@@ -562,7 +580,7 @@ private:
        wxString checksum;
        wxString partitionDef;
        bool isPartitioned;
-       bool hasOids, unlogged, hasSubclass, rowsCounted, isReplicated, 
showExtendedStatistics, distributionIsRandom;
+       bool hasOids, unlogged, hasSubclass, rowsCounted, isReplicated, 
showExtendedStatistics, distributionIsRandom, rowSecurityEnabled, 
forceRowSecurity;
 
        wxString toast_fillFactor, toast_autovacuum_vacuum_threshold,
                 toast_autovacuum_vacuum_scale_factor, 
toast_autovacuum_vacuum_cost_delay,
diff --git a/pgadmin/include/utils/misc.h b/pgadmin/include/utils/misc.h
index b4a30a0..3aabf8d 100644
--- a/pgadmin/include/utils/misc.h
+++ b/pgadmin/include/utils/misc.h
@@ -220,6 +220,7 @@ enum
        PGM_INDEX,
        PGM_OPCLASS,
        PGM_OPFAMILY,
+       PGM_POLICY,
        PGM_PRIMARYKEY,
        PGM_ROLE,
        PGM_RULE,
diff --git a/pgadmin/pgAdmin3.vcxproj b/pgadmin/pgAdmin3.vcxproj
index bc10166..5fffc24 100644
--- a/pgadmin/pgAdmin3.vcxproj
+++ b/pgadmin/pgAdmin3.vcxproj
@@ -857,6 +857,7 @@
     <ClCompile Include="dlg\dlgOperator.cpp" />
     <ClCompile Include="dlg\dlgPackage.cpp" />
     <ClCompile Include="dlg\dlgPgpassConfig.cpp" />
+    <ClCompile Include="dlg\dlgPolicy.cpp" />
     <ClCompile Include="dlg\dlgProperty.cpp" />
     <ClCompile Include="dlg\dlgReassignDropOwned.cpp" />
     <ClCompile Include="dlg\dlgResourceGroup.cpp" />
@@ -2217,6 +2218,7 @@
     <ClCompile Include="schema\pgOperator.cpp" />
     <ClCompile Include="schema\pgOperatorClass.cpp" />
     <ClCompile Include="schema\pgOperatorFamily.cpp" />
+    <ClCompile Include="schema\pgPolicy.cpp" />
     <ClCompile Include="schema\pgRole.cpp" />
     <ClCompile Include="schema\pgRule.cpp" />
     <ClCompile Include="schema\pgSchema.cpp" />
@@ -2696,6 +2698,7 @@
     <None Include="ui\dlgOperator.xrc" />
     <None Include="ui\dlgPackage.xrc" />
     <None Include="ui\dlgPgpassConfig.xrc" />
+    <None Include="ui\dlgPolicy.xrc" />
     <None Include="ui\dlgReassignDropOwned.xrc" />
     <None Include="ui\dlgRepCluster.xrc" />
     <None Include="ui\dlgRepClusterUpgrade.xrc" />
@@ -2787,6 +2790,7 @@
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="include\copyright.h" />
+    <ClInclude Include="include\dlg\dlgPolicy.h" />
     <ClInclude Include="include\dlg\dlgResourceGroup.h" />
     <ClInclude Include="include\libssh2\channel.h" />
     <ClInclude Include="include\libssh2\comp.h" />
@@ -2809,6 +2813,7 @@
     <ClInclude Include="include\postgres.h" />
     <ClInclude Include="include\precomp.h" />
     <ClInclude Include="include\schema\edbResourceGroup.h" />
+    <ClInclude Include="include\schema\pgPolicy.h" />
     <ClInclude Include="include\svnversion.h" />
     <ClInclude Include="include\utils\sshTunnel.h" />
     <ClInclude Include="include\version.h" />
@@ -3458,6 +3463,9 @@
     <png2c Include="include\images\pgAdmin3-32.png" />
     <png2c Include="include\images\pgAdmin3.png" />
     <png2c Include="include\images\plugins.png" />
+    <png2c Include="include\images\policies.png" />
+    <png2c Include="include\images\policy-sm.png" />
+    <png2c Include="include\images\policy.png" />
     <png2c Include="include\images\primarykey.png" />
     <png2c Include="include\images\procedure.png" />
     <png2c Include="include\images\procedures.png" />
@@ -3570,4 +3578,4 @@
   <ImportGroup Label="ExtensionTargets">
     <Import Project="..\pgAdmin3.targets" />
   </ImportGroup>
-</Project>
+</Project>
\ No newline at end of file
diff --git a/pgadmin/pgAdmin3.vcxproj.filters b/pgadmin/pgAdmin3.vcxproj.filters
index f65b268..2758b3a 100644
--- a/pgadmin/pgAdmin3.vcxproj.filters
+++ b/pgadmin/pgAdmin3.vcxproj.filters
@@ -1686,6 +1686,12 @@
     <ClCompile Include="dlg\dlgMoveTablespace.cpp">
       <Filter>dlg</Filter>
     </ClCompile>
+    <ClCompile Include="schema\pgPolicy.cpp">
+      <Filter>schema</Filter>
+    </ClCompile>
+    <ClCompile Include="dlg\dlgPolicy.cpp">
+      <Filter>dlg</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <None Include="agent\module.mk">
@@ -2211,6 +2217,9 @@
     <None Include="ui\dlgMoveTablespace.xrc">
       <Filter>ui</Filter>
     </None>
+    <None Include="ui\dlgPolicy.xrc">
+      <Filter>ui</Filter>
+    </None>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="include\copyright.h">
@@ -3581,6 +3590,12 @@
     <ClInclude Include="include\db\pgQueryResultEvent.h">
       <Filter>include\db</Filter>
     </ClInclude>
+    <ClInclude Include="include\schema\pgPolicy.h">
+      <Filter>include\schema</Filter>
+    </ClInclude>
+    <ClInclude Include="include\dlg\dlgPolicy.h">
+      <Filter>include\dlg</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <png2c Include="include\images\aggregate-sm.png">
@@ -4519,6 +4534,15 @@
     <png2c Include="include\images\query_rollback.png">
       <Filter>include\images</Filter>
     </png2c>
+    <png2c Include="include\images\policies.png">
+      <Filter>include\images</Filter>
+    </png2c>
+    <png2c Include="include\images\policy.png">
+      <Filter>include\images</Filter>
+    </png2c>
+    <png2c Include="include\images\policy-sm.png">
+      <Filter>include\images</Filter>
+    </png2c>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="pgAdmin3.rc" />
diff --git a/pgadmin/schema/module.mk b/pgadmin/schema/module.mk
index 9ce59c5..1f832d4 100644
--- a/pgadmin/schema/module.mk
+++ b/pgadmin/schema/module.mk
@@ -42,6 +42,7 @@ pgadmin3_SOURCES += \
         schema/pgOperator.cpp \
         schema/pgOperatorClass.cpp \
         schema/pgOperatorFamily.cpp \
+               schema/pgPolicy.cpp \
         schema/pgRole.cpp \
         schema/pgRule.cpp \
         schema/pgSchema.cpp \
diff --git a/pgadmin/schema/pgPolicy.cpp b/pgadmin/schema/pgPolicy.cpp
new file mode 100644
index 0000000..6428295
--- /dev/null
+++ b/pgadmin/schema/pgPolicy.cpp
@@ -0,0 +1,391 @@
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin III - PostgreSQL Tools
+//
+// Copyright (C) 2002 - 2016, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+// pgPolicy.cpp PostgreSQL Row security policy
+//
+//////////////////////////////////////////////////////////////////////////
+
+// wxWindows headers
+#include <wx/wx.h>
+
+// App headers
+#include "pgAdmin3.h"
+#include "utils/misc.h"
+#include "utils/pgDefs.h"
+#include "schema/pgPolicy.h"
+#include "dlg/dlgPolicy.h"
+
+#include <list>
+
+pgPolicy::pgPolicy(pgTable *newTable, const wxString &newName)
+       : pgTableObject(newTable, policyFactory, newName)
+{
+       command = POLICY_ALL;
+}
+
+pgPolicy::~pgPolicy()
+{
+}
+
+void pgPolicy::iSetPolicyRoles(const wxArrayString &names)
+{
+       // can't use assignment operator because it rewrites m_autoSort flag of 
wxSortedArrayString
+       roleNames.Clear();
+       for (int i = 0; i < names.size(); i++)
+               roleNames.Add(names[i]);
+}
+
+bool pgPolicy::DropObject(wxFrame *frame, ctlTree *browser, bool cascaded)
+{
+       wxString sql = wxT("DROP POLICY ") + GetQuotedIdentifier() + wxT(" ON 
") + GetTable()->GetQuotedFullIdentifier();
+
+       return GetDatabase()->ExecuteVoid(sql);
+}
+
+wxString pgPolicy::GetAlterPolicySql(const wxString &name, const wxArrayString 
&sortedRoles, 
+                                                                          
const wxString &exprUsing, const wxString &exprCheck) const 
+{
+       wxString sql;
+       wxString sqlRename;
+       wxString sqlChange;
+
+       if (GetQuotedIdentifier() != name)
+               sqlRename = wxT("ALTER POLICY ") + GetQuotedIdentifier() + 
wxT(" ON ") + table->GetQuotedFullIdentifier() + wxT("\n")
+                       + wxT(" RENAME TO ") + name + wxT(";\n\n");
+
+       if (sortedRoles != GetPolicyRolesSorted())
+       {
+               wxString roles = GetPolicyRolesString(sortedRoles);
+               if (roles.IsEmpty())
+                       roles = wxT("PUBLIC");
+
+               sqlChange += wxT("\nTO ") + roles;
+       }
+
+       if (exprUsing != GetPolicyUsingExpression())
+       {
+               wxString expr = exprUsing;
+               if (expr.IsEmpty())
+                       expr = wxT("true");
+
+               sqlChange += wxT("\nUSING ") + WrapParentheses(expr);
+       }
+
+       if (exprCheck != GetPolicyWithCheckExpression())
+       {
+               wxString expr = exprCheck;
+               if (expr.IsEmpty())
+                       expr = wxT("true");
+
+               sqlChange += wxT("\nWITH CHECK ") + WrapParentheses(expr);
+       }
+
+       if (!sqlRename.IsEmpty())
+               sql += sqlRename;
+
+       if (!sqlChange.IsEmpty())
+               sql += wxT("ALTER POLICY ") + name + wxT(" ON ") + 
table->GetQuotedFullIdentifier() + wxT(" ") + sqlChange;
+
+       return sql;
+}
+
+wxString pgPolicy::GetCreatePolicySql(const wxString &name, const wxString 
&table, char command, const wxArrayString &roles, 
+                                                                          
const wxString &exprUsing, const wxString &checkExpr)
+{
+       wxString sql;
+
+       sql += wxT("CREATE POLICY ") + name + wxT(" ON ") + table;
+       if (command != POLICY_ALL)
+       {
+               wxString cmd = pgPolicy::GetPolicyCommandString(command);
+               if (!cmd.IsEmpty())
+                       sql += wxT("\nFOR ") + cmd;
+       }
+       
+       if (!roles.IsEmpty())
+               sql += wxT("\nTO ") + GetPolicyRolesString(roles);
+
+       wxString expr = exprUsing;
+       if (!expr.IsEmpty())
+               sql += wxT("\nUSING ") + WrapParentheses(expr);
+
+       expr = checkExpr;
+       if (!expr.IsEmpty())
+               sql += wxT("\nWITH CHECK ") + WrapParentheses(expr);
+
+       sql += wxT(";\n");
+
+       return sql;
+}
+
+wxString pgPolicy::GetSql(ctlTree *browser)
+{
+       wxString sql;
+
+       sql = wxT("-- Policy: ") + GetQuotedIdentifier() + wxT("\n\n");
+       sql += wxT("-- DROP POLICY ") + GetQuotedIdentifier() + wxT(" ON ") + 
GetTable()->GetQuotedFullIdentifier() + wxT("\n\n");
+       sql += GetCreatePolicySql(GetQuotedIdentifier(), 
GetTable()->GetQuotedFullIdentifier(), GetPolicyCommand(), 
GetPolicyRolesSorted(),
+               GetPolicyUsingExpression(), GetPolicyWithCheckExpression());
+
+       if (!GetComment().IsEmpty())
+               sql += wxT("\nCOMMENT ON POLICY ") + GetQuotedIdentifier() + 
wxT(" ON ") + GetTable()->GetQuotedFullIdentifier()
+                     + wxT("\n  IS ") + qtDbString(GetComment()) + wxT(";\n");
+
+       return sql;
+}
+
+wxString pgPolicy::GetTranslatedMessage(int kindOfMessage) const
+{
+       wxString message = wxEmptyString;
+
+       switch (kindOfMessage)
+       {
+               case RETRIEVINGDETAILS:
+                       message = _("Retrieving details on policy");
+                       message += wxT(" ") + GetName();
+                       break;
+               case REFRESHINGDETAILS:
+                       message = _("Refreshing policy");
+                       message += wxT(" ") + GetName();
+                       break;
+               case DROPEXCLUDINGDEPS:
+                       message = wxString::Format(_("Are you sure you wish to 
drop policy \"%s\"?"),
+                                                  GetFullIdentifier().c_str());
+                       break;
+               case DROPTITLE:
+                       message = _("Drop policy?");
+                       break;
+               case PROPERTIESREPORT:
+                       message = _("Policy properties report");
+                       message += wxT(" - ") + GetName();
+                       break;
+               case PROPERTIES:
+                       message = _("Policy properties");
+                       break;
+               case DDLREPORT:
+                       message = _("Policy DDL report");
+                       message += wxT(" - ") + GetName();
+                       break;
+               case DDL:
+                       message = _("Policy DDL");
+                       break;
+       }
+
+       return message;
+}
+
+void pgPolicy::ShowTreeDetail(ctlTree *browser, frmMain *form, ctlListView 
*properties, ctlSQLBox *sqlPane)
+{
+       if (properties)
+       {
+               CreateListColumns(properties);
+
+               properties->AppendItem(_("Name"), GetName());
+               properties->AppendItem(_("Command"), 
pgPolicy::GetPolicyCommandString(GetPolicyCommand()));
+               properties->AppendItem(_("Roles"), 
GetPolicyRolesString(GetPolicyRolesSorted()));
+               properties->AppendItem(_("Using expression"), 
firstLineOnly(GetPolicyUsingExpression()));
+               properties->AppendItem(_("With Check expression"), 
firstLineOnly(GetPolicyWithCheckExpression()));
+               properties->AppendItem(_("Comment"), 
firstLineOnly(GetComment()));
+       }
+}
+
+// char command value to sql text, e.g. '*' -> 'ALL', 'r' -> 'SELECT'
+wxString pgPolicy::GetPolicyCommandString(char command)
+{
+       switch (command)
+       {
+       case POLICY_ALL: return wxT("ALL");
+       case POLICY_SELECT: return wxT("SELECT");
+       case POLICY_INSERT: return wxT("INSERT");
+       case POLICY_UPDATE: return wxT("UPDATE");
+       case POLICY_DELETE: return wxT("DELETE");
+       default: return wxString();
+       }
+}
+
+wxString pgPolicy::GetAlterTablePolicyParamsSql(const wxString &table, int 
enableRls, int forceRls)
+{
+       wxString sql;
+
+       wxString enableExpr;
+       if (enableRls == ROW_SECURUTY_ENABLE)
+               enableExpr = wxT("ENABLE ROW LEVEL SECURITY");
+       else if (enableRls == ROW_SECURUTY_DISABLE)
+               enableExpr = wxT("DISABLE ROW LEVEL SECURITY");
+
+       wxString forceExpr;
+       if (forceRls == ROW_SECURUTY_FORCE)
+               forceExpr = wxT("FORCE ROW LEVEL SECURITY");
+       else if (forceRls == ROW_SECURUTY_NOFORCE)
+               forceExpr = wxT("NO FORCE ROW LEVEL SECURITY");
+
+       if (!enableExpr.IsNull() || !forceExpr.IsNull())
+       {
+               sql += wxT("ALTER TABLE ") + table + wxT(" ");
+               if (!enableExpr.IsNull())
+                       sql += enableExpr;
+               if (!forceExpr.IsNull())
+               {
+                       if (!enableExpr.IsNull())
+                               sql += wxT(", ");
+                       sql += forceExpr;
+               }
+               sql += wxT(";");
+       }
+
+       return sql;
+}
+
+// wrap expression in parantheses, if it is not already wrapped
+wxString& pgPolicy::WrapParentheses(wxString &expr)
+{
+       if (expr.IsEmpty())
+               return expr;
+
+       if (expr.GetChar(0) != '(' || expr.GetChar(expr.size() - 1) != ')')
+               expr = wxT("(") + expr + wxT(")");
+
+       return expr;
+}
+
+// array of roles to comma-separated string
+wxString pgPolicy::GetPolicyRolesString(const wxArrayString &roles)
+{
+       wxString result;
+       int numRoles = roles.size();
+       for (int i = 0; i < numRoles; i++)
+       {
+               result += roles[i] + wxT(", ");
+       }
+       result.RemoveLast(2);
+
+       return result;
+}
+
+/////////////////////////////
+
+pgPolicyCollection::pgPolicyCollection(pgaFactory *factory, pgTable *tbl)
+       : pgTableObjCollection(factory, tbl)
+{
+}
+
+
+wxString pgPolicyCollection::GetTranslatedMessage(int kindOfMessage) const
+{
+       wxString message = wxEmptyString;
+
+       switch (kindOfMessage)
+       {
+               case RETRIEVINGDETAILS:
+                       message = _("Retrieving details on policies");
+                       break;
+               case REFRESHINGDETAILS:
+                       message = _("Refreshing policies");
+                       break;
+               case OBJECTSLISTREPORT:
+                       message = _("Policies list report");
+                       break;
+       }
+
+       return message;
+}
+
+/////////////////////////////
+
+dlgProperty* pgPolicyFactory::CreateDialog(frmMain *frame, pgObject *node, 
pgObject *parent)
+{
+       return new dlgPolicy(this, frame, (pgPolicy*)node, (pgTable*)parent);
+}
+
+pgObject* pgPolicyFactory::CreateObjects(pgCollection *collection, ctlTree 
*browser, const wxString &restriction)
+{
+       pgPolicy *policy = NULL;
+       pgSet *policies = NULL;
+       pgDatabase *db = collection->GetDatabase();
+       wxString name, cmd, roleOids, roleOid, roleName;
+       wxStringTokenizer roleTokens;
+
+       wxString query = 
+                          wxT("SELECT\n")
+                          wxT("        p.oid, p.polname, p.polrelid, p.polcmd, 
p.polroles,\n")
+                          wxT("        pg_get_expr(p.polqual, p.polrelid") + 
db->GetPrettyOption() + wxT(") AS exprusing,\n")
+                          wxT("        pg_get_expr(p.polwithcheck, 
p.polrelid") + db->GetPrettyOption() + wxT(") AS exprwithcheck,\n")
+                          wxT("        des.description\n")
+                          wxT("FROM\n")
+                          wxT("        pg_policy p\n")
+                  wxT("        LEFT OUTER JOIN pg_description des ON 
(des.objoid = p.oid AND des.classoid = 'pg_policy'::regclass)\n")
+                          wxT("WHERE\n")
+                          wxT("        p.polrelid = ") + 
collection->GetOidStr() + wxT(" ") + restriction + wxT("\n")
+                          wxT("ORDER BY p.polname\n");
+
+       policies = db->ExecuteSet(query);
+       if (policies)
+       {
+               while (!policies->Eof())
+               {
+                       name = policies->GetVal(wxT("polname"));
+                       policy = new pgPolicy(collection->GetTable(), name);
+
+                       policy->iSetOid(policies->GetOid(wxT("oid")));
+                       
policy->iSetComment(policies->GetVal(wxT("description")));
+                       cmd = policies->GetVal(wxT("polcmd"));
+                       if (!cmd.IsEmpty())
+                       {
+                               policy->iSetPolicyCommand(cmd[0]);
+                       }
+                       
+                       
policy->iSetPolicyUsingExpression(policies->GetVal(wxT("exprusing")));
+                       
policy->iSetPolicyWithCheckExpression(policies->GetVal(wxT("exprwithcheck")));
+
+                       wxArrayString roleNames;
+                       roleOids = policies->GetVal(wxT("polroles"));
+                       roleOids = roleOids.Mid(1, roleOids.size() - 2);
+                       roleTokens.SetString(roleOids, wxT(","));
+                       while (roleTokens.HasMoreTokens())
+                       {
+                               // get role names from catalog
+                               roleOid = roleTokens.GetNextToken();
+                               if (roleOid != wxT("0")) // PUBLIC
+                               {
+                                       roleName = 
db->ExecuteScalar(wxT("SELECT rolname FROM pg_roles WHERE oid =") + roleOid);
+                                       roleNames.Add(roleName);
+                               }
+                       }
+                       policy->iSetPolicyRoles(roleNames);
+
+                       if (browser)
+                       {
+                               browser->AppendObject(collection, policy);
+                               policies->MoveNext();
+                       }
+                       else
+                               break;
+               }
+
+               delete policies;
+       }
+
+       return policy;
+}
+
+#include "images/policy.pngc"
+#include "images/policy-sm.pngc"
+#include "images/policies.pngc"
+
+pgPolicyFactory::pgPolicyFactory()
+       : pgTableObjFactory(__("Policy"), __("New Policy..."), __("Create a new 
Policy."), policy_png_img, policy_sm_png_img)
+{
+       metaType = PGM_POLICY;
+}
+
+pgCollection* pgPolicyFactory::CreateCollection(pgObject *obj)
+{
+       return new pgPolicyCollection(GetCollectionFactory(), (pgTable *)obj);
+}
+
+pgPolicyFactory policyFactory;
+static pgaCollectionFactory cf(&policyFactory, __("Policies"), 
policies_png_img);
\ No newline at end of file
diff --git a/pgadmin/schema/pgTable.cpp b/pgadmin/schema/pgTable.cpp
index fdfea40..792873d 100644
--- a/pgadmin/schema/pgTable.cpp
+++ b/pgadmin/schema/pgTable.cpp
@@ -27,6 +27,7 @@
 #include "schema/pgTrigger.h"
 #include "schema/pgConstraints.h"
 #include "schema/gpPartition.h"
+#include "schema/pgPolicy.h"
 
 
 // App headers
@@ -65,6 +66,8 @@ void pgTable::Init()
        isReplicated = false;
        showExtendedStatistics = false;
        distributionIsRandom = false;
+       rowSecurityEnabled = false;
+       forceRowSecurity = false;
 
        inheritedTableCount = 0;
        triggerCount = 0;
@@ -179,6 +182,8 @@ wxMenu *pgTable::GetNewMenu()
                indexFactory.AppendMenu(menu);
                ruleFactory.AppendMenu(menu);
                triggerFactory.AppendMenu(menu);
+               if (GetConnection() != 0 && 
GetConnection()->BackendMinimumVersion(9, 5))
+                       policyFactory.AppendMenu(menu);
 
                /*
                 * TEMPORARY:  Disable adding new partitions until that code is 
working right.
@@ -682,6 +687,16 @@ wxString pgTable::GetSql(ctlTree *browser)
 
                sql += GetCommentSql();
 
+               // row level security options
+               if (GetConnection()->BackendMinimumVersion(9, 5))
+               {
+                       int enableRls = GetRowLevelSecurityEnabled() ? 
ROW_SECURUTY_ENABLE : ROW_SECURUTY_SKIP;
+                       int forceRls = GetForceRowLevelSecurity() ? 
ROW_SECURUTY_FORCE : ROW_SECURUTY_SKIP;
+                       wxString rlsSql = 
pgPolicy::GetAlterTablePolicyParamsSql(GetQuotedFullIdentifier(), enableRls, 
forceRls);
+                       if (!rlsSql.IsEmpty())
+                               sql += wxT("\n") + rlsSql + wxT("\n\n");
+               }
+
                if (GetConnection()->BackendMinimumVersion(9, 1))
                        sql += GetSeqLabelsSql();
 
@@ -701,6 +716,10 @@ wxString pgTable::GetSql(ctlTree *browser)
                AppendStuff(sql, browser, ruleFactory);
                AppendStuff(sql, browser, triggerFactory);
 
+               // row security policies for table
+               if (GetConnection()->BackendMinimumVersion(9, 5))
+                       AppendStuff(sql, browser, policyFactory);
+
                /*
                 * Disable adding partitions until that code works.
                 *
@@ -929,6 +948,9 @@ void pgTable::ShowTreeDetail(ctlTree *browser, frmMain 
*form, ctlListView *prope
                browser->AppendCollection(this, indexFactory);
                browser->AppendCollection(this, ruleFactory);
                browser->AppendCollection(this, triggerFactory);
+               // row level security policies
+               if (GetConnection() != 0 && 
GetConnection()->BackendMinimumVersion(9, 5))
+                       browser->AppendCollection(this, policyFactory);
 
                if (GetConnection() != 0 && GetConnection()->GetIsGreenplum() 
&& GetIsPartitioned())
                        browser->AppendCollection(this, partitionFactory);
@@ -1083,6 +1105,13 @@ void pgTable::ShowTreeDetail(ctlTree *browser, frmMain 
*form, ctlListView *prope
                        if (!GetToastAutoVacuumFreezeTableAge().IsEmpty())
                                properties->AppendItem(_("Toast auto-vacuum 
FREEZE table age"), GetToastAutoVacuumFreezeTableAge());
                }
+
+               // row level security options
+               if (GetConnection()->BackendMinimumVersion(9, 5))
+               {
+                       properties->AppendItem(_("RLS enabled?"), 
GetRowLevelSecurityEnabled() ? _("Yes") : _("No"));
+                       properties->AppendItem(_("RLS forced?"), 
GetForceRowLevelSecurity() ? _("Yes") : _("No"));
+               }
                properties->AppendItem(_("Comment"), 
firstLineOnly(GetComment()));
 
                if (!GetLabels().IsEmpty())
@@ -1450,6 +1479,12 @@ pgObject *pgTableFactory::CreateObjects(pgCollection 
*collection, ctlTree *brows
                                 wxT("                     WHERE 
tgrelid=rel.oid AND tgisconstraint = FALSE) AS triggercount\n");
                }
 
+               // row level security options
+               if (collection->GetDatabase()->BackendMinimumVersion(9, 5))
+               {
+                       query += wxT(", rel.relrowsecurity \n");
+                       query += wxT(", rel.relforcerowsecurity \n");
+               }
                if (collection->GetConnection()->BackendMinimumVersion(9, 1))
                        query += wxT(", rel.relpersistence \n");
                if (collection->GetConnection()->BackendMinimumVersion(8, 2))
@@ -1574,6 +1609,17 @@ pgObject *pgTableFactory::CreateObjects(pgCollection 
*collection, ctlTree *brows
                                table->iSetOfType(wxT(""));
                        }
                        table->iSetComment(tables->GetVal(wxT("description")));
+                       // row level security options
+                       if (collection->GetDatabase()->BackendMinimumVersion(9, 
5))
+                       {
+                               
table->iSetRowLevelSecurityEnabled(StrToBool(tables->GetVal(wxT("relrowsecurity"))));
+                               
table->iSetForceRowLevelSecurity(StrToBool(tables->GetVal(wxT("relforcerowsecurity"))));
+                       }
+                       else
+                       {
+                               table->iSetRowLevelSecurityEnabled(false);
+                               table->iSetForceRowLevelSecurity(false);
+                       }
                        if 
(collection->GetConnection()->BackendMinimumVersion(9, 1))
                                
table->iSetUnlogged(tables->GetVal(wxT("relpersistence")) == wxT("u"));
                        else
diff --git a/pgadmin/ui/dlgPolicy.xrc b/pgadmin/ui/dlgPolicy.xrc
new file mode 100644
index 0000000..64a613b
--- /dev/null
+++ b/pgadmin/ui/dlgPolicy.xrc
@@ -0,0 +1,278 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<resource>
+  <object class="wxDialog" name="dlgPolicy">
+    <title></title>
+    <size>300,265d</size>
+    
<style>wxDEFAULT_DIALOG_STYLE|wxCAPTION|wxSYSTEM_MENU|wxRESIZE_BORDER</style>
+    <object class="wxFlexGridSizer">
+      <cols>1</cols>
+      <growablerows>0</growablerows>
+      <growablecols>0</growablecols>
+      <object class="sizeritem">
+        <object class="wxNotebook" name="nbNotebook">
+          <size>296,240d</size>
+          <selected>0</selected>
+          <object class="notebookpage">
+            <label>Properties</label>
+            <object class="wxPanel" name="pnlProperties">
+              <object class="wxFlexGridSizer">
+                <cols>2</cols>
+                <vgap>5</vgap>
+                <hgap>5</hgap>
+                <growablerows>2</growablerows>
+                <growablecols>1</growablecols>
+                <object class="sizeritem">
+                  <object class="wxStaticText" name="stName">
+                    <label>Name</label>
+                  </object>
+                  <flag>wxALIGN_CENTRE_VERTICAL|wxTOP|wxLEFT|wxRIGHT</flag>
+                  <border>4</border>
+                </object>
+                <object class="sizeritem">
+                  <object class="wxTextCtrl" name="txtName">
+                  </object>
+                  
<flag>wxEXPAND|wxALIGN_CENTER_VERTICAL|wxTOP|wxLEFT|wxRIGHT</flag>
+                  <border>4</border>
+                </object>
+                <object class="sizeritem">
+                  <object class="wxStaticText" name="stOID">
+                    <label>OID</label>
+                  </object>
+                  <flag>wxALIGN_CENTRE_VERTICAL|wxTOP|wxLEFT|wxRIGHT</flag>
+                  <border>4</border>
+                </object>
+                <object class="sizeritem">
+                  <object class="wxTextCtrl" name="txtOID">
+                  </object>
+                  
<flag>wxEXPAND|wxALIGN_CENTER_VERTICAL|wxTOP|wxLEFT|wxRIGHT</flag>
+                  <border>4</border>
+                </object>
+                <object class="sizeritem">
+                  <object class="wxStaticText" name="stComment">
+                    <label>Comment</label>
+                  </object>
+                  <flag>wxALIGN_CENTRE_VERTICAL|wxTOP|wxLEFT|wxRIGHT</flag>
+                  <border>4</border>
+                </object>
+                <object class="sizeritem">
+                  <object class="wxTextCtrl" name="txtComment">
+                    <style>wxTE_MULTILINE</style>
+                  </object>
+                  <flag>wxEXPAND|wxALIGN_CENTER_VERTICAL|wxALL</flag>
+                  <border>4</border>
+                </object>
+                <object class="sizeritem">
+                  <object class="wxStaticText" name="stClusterSet">
+                    <label>Use Slony</label>
+                  </object>
+                  <flag>wxALIGN_CENTRE_VERTICAL|wxALL</flag>
+                  <border>4</border>
+                </object>
+                <object class="sizeritem">
+                  <object class="wxComboBox" name="cbClusterSet">
+                    <content/>
+                    <style>wxCB_READONLY|wxCB_DROPDOWN</style>
+                  </object>
+                  <flag>wxEXPAND|wxALIGN_CENTRE_VERTICAL|wxALL</flag>
+                  <border>4</border>
+                </object>
+              </object>
+            </object>
+          </object>
+          <object class="notebookpage">
+            <label>Definition</label>
+            <object class="wxPanel" name="pnlDefinition">
+              <object class="wxFlexGridSizer">
+                <cols>2</cols>
+                <vgap>5</vgap>
+                <hgap>5</hgap>
+                <growablerows>1</growablerows>
+                <growablecols>1</growablecols>
+                <object class="sizeritem">
+                  <object class="wxStaticText" name="stCommand">
+                    <label>Command</label>
+                  </object>
+                  <flag>wxALIGN_CENTRE_VERTICAL|wxTOP|wxLEFT|wxRIGHT</flag>
+                  <border>4</border>
+                </object>
+                <object class="sizeritem">
+                  <object class="wxRadioBox" name="rbxCommand">
+                    <label></label>
+                    <content>
+                      <item>ALL</item>
+                      <item>SELECT</item>
+                      <item>INSERT</item>
+                      <item>UPDATE</item>
+                      <item>DELETE</item>
+                    </content>
+                    <dimension>1</dimension>
+                    <style>wxRA_SPECIFY_COLS</style>
+                  </object>
+                  
<flag>wxEXPAND|wxALIGN_CENTER_VERTICAL|wxTOP|wxLEFT|wxRIGHT</flag>
+                  <border>4</border>
+                </object>
+                <object class="sizeritem">
+                  <object class="wxStaticText" name="stRoles">
+                    <label>Roles</label>
+                  </object>
+                  <flag>wxALIGN_TOP|wxTOP|wxLEFT|wxRIGHT</flag>
+                  <border>4</border>
+                </object>
+                <object class="sizeritem">
+                  <object class="wxFlexGridSizer">
+                    <cols>3</cols>
+                    <vgap>5</vgap>
+                    <hgap>5</hgap>
+                    <growablerows>2</growablerows>
+                    <growablecols>0,2</growablecols>
+                    <object class="sizeritem">
+                      <object class="wxStaticText" name="stRolesSelected">
+                        <label>Apply policy to</label>
+                      </object>
+                      <flag>wxALIGN_CENTRE_VERTICAL|wxTOP|wxLEFT|wxRIGHT</flag>
+                      <border>0</border>
+                    </object>
+                    <object class="spacer">
+                      <size>0,0d</size>
+                    </object>
+                    <object class="spacer">
+                      <size>0,0d</size>
+                    </object>
+                    <object class="sizeritem">
+                      <object class="wxListBox" name="lbRolesSelected">
+                        <content/>
+                        <size>86,123d</size>
+                        <style>wxLB_EXTENDED|wxLB_NEEDED_SB|wxLB_SORT</style>
+                      </object>
+                      <flag>wxEXPAND|wxBOTTOM</flag>
+                      <border>0</border>
+                    </object>
+                    <object class="sizeritem">
+                      <object class="wxFlexGridSizer">
+                        <cols>1</cols>
+                        <vgap>5</vgap>
+                        <hgap>5</hgap>
+                        <object class="sizeritem">
+                          <object class="wxButton" name="btnAddRole">
+                            <label>&lt;&lt;</label>
+                            <tooltip>Add role</tooltip>
+                            <size>18,-1d</size>
+                          </object>
+                          <flag>wxEXPAND|wxALIGN_BOTTOM|wxALL</flag>
+                          <border>4</border>
+                        </object>
+                        <object class="sizeritem">
+                          <object class="wxButton" name="btnDelRole">
+                            <label>&gt;&gt;</label>
+                            <tooltip>Remove role</tooltip>
+                            <size>18,-1d</size>
+                          </object>
+                          <flag>wxEXPAND|wxALIGN_TOP|wxALL</flag>
+                          <border>4</border>
+                        </object>
+                        <flag>wxALIGN_CENTRE_VERTICAL|wxALL</flag>
+                        <border>0</border>
+                      </object>
+                    </object>
+                    <object class="sizeritem">
+                      <object class="wxListBox" name="lbRolesAll">
+                        <content/>
+                        <size>86,123d</size>
+                        <style>wxLB_EXTENDED|wxLB_NEEDED_SB|wxLB_SORT</style>
+                      </object>
+                      <flag>wxEXPAND|wxBOTTOM|wxRIGHT</flag>
+                      <border>0</border>
+                    </object>
+                  </object>
+                  <flag>wxEXPAND|wxALL</flag>
+                  <border>4</border>
+                </object>
+              </object>
+            </object>
+          </object>
+          <object class="notebookpage">
+            <label>Statements</label>
+            <object class="wxPanel" name="pnlStatements">
+              <object class="wxFlexGridSizer">
+                <cols>2</cols>
+                <vgap>5</vgap>
+                <hgap>5</hgap>
+                <growablerows>0,1</growablerows>
+                <growablecols>1</growablecols>
+                <object class="sizeritem">
+                  <object class="wxStaticText" name="stUsing">
+                    <label>Using</label>
+                  </object>
+                  <flag>wxALIGN_TOP|wxTOP|wxLEFT|wxRIGHT</flag>
+                  <border>4</border>
+                </object>
+                <object class="sizeritem">
+                  <object class="ctlSQLBox" name="sqlBoxUsing">
+                    <style>wxTE_MULTILINE</style>
+                  </object>
+                  <flag>wxEXPAND|wxTOP|wxLEFT|wxRIGHT</flag>
+                  <border>4</border>
+                </object>
+                <object class="sizeritem">
+                  <object class="wxStaticText" name="stWithCheck">
+                    <label>With check</label>
+                  </object>
+                  <flag>wxALIGN_TOP|wxTOP|wxLEFT|wxRIGHT</flag>
+                  <border>4</border>
+                </object>
+                <object class="sizeritem">
+                  <object class="ctlSQLBox" name="sqlBoxWithCheck">
+                    <style>wxTE_MULTILINE</style>
+                  </object>
+                  <flag>wxEXPAND|wxTOP|wxLEFT|wxRIGHT</flag>
+                  <border>4</border>
+                </object>
+              </object>
+            </object>
+          </object>
+        </object>
+        <flag>wxALL|wxGROW|wxALIGN_CENTRE</flag>
+        <border>3</border>
+      </object>
+      <object class="sizeritem">
+        <object class="wxFlexGridSizer">
+          <cols>4</cols>
+          <growablecols>1</growablecols>
+          <object class="sizeritem">
+            <object class="wxButton" name="wxID_HELP">
+              <label>Help</label>
+            </object>
+            <flag>wxEXPAND|wxALL</flag>
+            <border>3</border>
+          </object>
+          <object class="spacer">
+            <size>0,0d</size>
+          </object>
+          <object class="sizeritem">
+            <object class="wxButton" name="wxID_OK">
+              <label>&amp;OK</label>
+              <default>1</default>
+            </object>
+            <flag>wxEXPAND|wxALL</flag>
+            <border>3</border>
+          </object>
+          <object class="sizeritem">
+            <object class="wxButton" name="wxID_CANCEL">
+              <label>&amp;Cancel</label>
+            </object>
+            <flag>wxEXPAND|wxALL</flag>
+            <border>3</border>
+          </object>
+        </object>
+        <flag>wxEXPAND|wxTOP|wxLEFT|wxRIGHT</flag>
+      </object>
+      <object class="sizeritem">
+        <object class="wxStatusBar" name="unkStatusBar">
+          <style>wxST_SIZEGRIP</style>
+        </object>
+        <flag>wxEXPAND|wxALIGN_CENTRE</flag>
+        <border>3</border>
+      </object>
+    </object>
+  </object>
+</resource>
diff --git a/pgadmin/ui/dlgTable.xrc b/pgadmin/ui/dlgTable.xrc
index 6ebaa32..bdb8512 100644
--- a/pgadmin/ui/dlgTable.xrc
+++ b/pgadmin/ui/dlgTable.xrc
@@ -187,6 +187,36 @@
                   
<flag>wxEXPAND|wxALIGN_CENTER_VERTICAL|wxTOP|wxLEFT|wxRIGHT</flag>
                   <border>4</border>
                 </object>
+                <object class="sizeritem">
+                  <object class="wxStaticText" name="stPolicyEnable">
+                    <label>Enable row level security</label>
+                  </object>
+                  <flag>wxALIGN_CENTRE_VERTICAL|wxTOP|wxLEFT|wxRIGHT</flag>
+                  <border>4</border>
+                </object>
+                <object class="sizeritem">
+                  <object class="wxCheckBox" name="chkPolicyEnable">
+                    <label></label>
+                    <checked>0</checked>
+                  </object>
+                  
<flag>wxEXPAND|wxALIGN_CENTER_VERTICAL|wxTOP|wxLEFT|wxRIGHT</flag>
+                  <border>4</border>
+                </object>
+                <object class="sizeritem">
+                  <object class="wxStaticText" name="stPolicyForce">
+                    <label>Force row level security</label>
+                  </object>
+                  <flag>wxALIGN_CENTRE_VERTICAL|wxTOP|wxLEFT|wxRIGHT</flag>
+                  <border>4</border>
+                </object>
+                <object class="sizeritem">
+                  <object class="wxCheckBox" name="chkPolicyForce">
+                    <label></label>
+                    <checked>0</checked>
+                  </object>
+                  
<flag>wxEXPAND|wxALIGN_CENTER_VERTICAL|wxTOP|wxLEFT|wxRIGHT</flag>
+                  <border>4</border>
+                </object>
               </object>
             </object>
           </object>
diff --git a/pgadmin/ui/module.mk b/pgadmin/ui/module.mk
index b546e41..1b998b9 100644
--- a/pgadmin/ui/module.mk
+++ b/pgadmin/ui/module.mk
@@ -49,6 +49,7 @@ TMP_ui += \
        ui/dlgOperator.xrc \
        ui/dlgPackage.xrc \
        ui/dlgPgpassConfig.xrc \
+       ui/dlgPolicy.xrc \
        ui/dlgReassignDropOwned.xrc \
        ui/dlgRepCluster.xrc \
        ui/dlgRepClusterUpgrade.xrc \
-- 
2.7.2.windows.1

From 145c9d04f807ebd4f85601420b21535657076db6 Mon Sep 17 00:00:00 2001
From: Alexander Polyakov <alexp...@yandex.ru>
Date: Tue, 5 Apr 2016 17:09:57 +0400
Subject: [PATCH] Basic tab-completion for policies

---
 pgadmin/ctl/ctlSQLBox.cpp      |   6 +-
 pgadmin/utils/tab-complete.inc | 164 ++++++++++++++++++++++++++++++++++++++++-
 pgadmin/utils/tabcomplete.c    |   4 +-
 3 files changed, 165 insertions(+), 9 deletions(-)

diff --git a/pgadmin/ctl/ctlSQLBox.cpp b/pgadmin/ctl/ctlSQLBox.cpp
index a45bb5e..37967d6 100644
--- a/pgadmin/ctl/ctlSQLBox.cpp
+++ b/pgadmin/ctl/ctlSQLBox.cpp
@@ -828,7 +828,7 @@ void ctlSQLBox::OnMarginClick(wxStyledTextEvent &event)
 }
 
 
-extern "C" char *tab_complete(const char *allstr, const int startptr, const 
int endptr, void *dbptr);
+extern "C" char *tab_complete(const char *allstr, const int startptr, const 
int endptr, void *dbptr, int dbvmajor, int dbvminor);
 void ctlSQLBox::OnAutoComplete(wxCommandEvent &rev)
 {
        if (GetReadOnly())
@@ -843,9 +843,9 @@ void ctlSQLBox::OnAutoComplete(wxCommandEvent &rev)
 
        char *tab_ret;
        if (spaceidx == -1)
-               tab_ret = tab_complete(what.mb_str(wxConvUTF8), 0, what.Len() + 
1, m_database);
+               tab_ret = tab_complete(what.mb_str(wxConvUTF8), 0, what.Len() + 
1, m_database, m_database->GetMajorVersion(), m_database->GetMinorVersion());
        else
-               tab_ret = tab_complete(what.mb_str(wxConvUTF8), spaceidx + 1, 
what.Len() + 1, m_database);
+               tab_ret = tab_complete(what.mb_str(wxConvUTF8), spaceidx + 1, 
what.Len() + 1, m_database, m_database->GetMajorVersion(), 
m_database->GetMinorVersion());
 
        if (tab_ret == NULL || tab_ret[0] == '\0')
                return; /* No autocomplete available for this string */
diff --git a/pgadmin/utils/tab-complete.inc b/pgadmin/utils/tab-complete.inc
index 98b7a35..2a42f85 100644
--- a/pgadmin/utils/tab-complete.inc
+++ b/pgadmin/utils/tab-complete.inc
@@ -315,6 +315,19 @@ static const SchemaQuery Query_for_list_of_views = {
 "       (SELECT tgrelid FROM pg_catalog.pg_trigger "\
 "         WHERE pg_catalog.quote_ident(tgname)='%s')"
 
+#define Query_for_list_of_policies \
+" SELECT pg_catalog.quote_ident(polname) "\
+"   FROM pg_catalog.pg_policy "\
+"  WHERE substring(pg_catalog.quote_ident(polname),1,%d)='%s'"
+
+#define Query_for_list_of_tables_for_policy \
+"SELECT pg_catalog.quote_ident(relname) "\
+"  FROM pg_catalog.pg_class"\
+" WHERE (%d = pg_catalog.length('%s'))"\
+"   AND oid IN "\
+"       (SELECT polrelid FROM pg_catalog.pg_policy "\
+"         WHERE pg_catalog.quote_ident(polname)='%s')"
+
 /*
  * This is a list of all "things" in Pgsql, which can show up after CREATE or
  * DROP; and there is also a query to get a list of them.
@@ -345,6 +358,7 @@ static const pgsql_thing_t words_after_create[] = {
        {"INDEX", NULL, &Query_for_list_of_indexes},
        {"OPERATOR", NULL, NULL},       /* Querying for this is probably not 
such a
                                                                 * good idea. */
+       {"POLICY", NULL, NULL},
        {"ROLE", Query_for_list_of_roles},
        {"RULE", "SELECT pg_catalog.quote_ident(rulename) FROM 
pg_catalog.pg_rules WHERE 
substring(pg_catalog.quote_ident(rulename),1,%d)='%s'"},
        {"SCHEMA", Query_for_list_of_schemas},
@@ -360,13 +374,14 @@ static const pgsql_thing_t words_after_create[] = {
        {NULL, NULL, NULL}                      /* end of list */
 };
 
+#define BACKEND_MINIMUM(major, minor)          (dbvmajor > major || (dbvmajor 
== major && dbvminor >= minor))
 
 /* The completion function. Acc. to readline spec this gets passed the text
    entered to far and its start and end in the readline buffer. The return 
value
    is some partially obscure list format that can be generated by the readline
    libraries completion_matches() function, so we don't have to worry about it.
 */
-static char * psql_completion(char *text, int start, int end, void *dbptr)
+static char * psql_completion(char *text, int start, int end, void *dbptr, int 
dbvmajor, int dbvminor)
 {
        /* This is the variable we'll return. */
        char *matches = NULL;
@@ -376,7 +391,9 @@ static char * psql_completion(char *text, int start, int 
end, void *dbptr)
                           *prev2_wd,
                           *prev3_wd,
                           *prev4_wd,
-                          *prev5_wd;
+                          *prev5_wd,
+                          *prev6_wd,
+                          *prev7_wd;
 
        static const char *const sql_commands[] = {
                "ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", 
"CLUSTER", "COMMENT",
@@ -419,6 +436,8 @@ static char * psql_completion(char *text, int start, int 
end, void *dbptr)
        prev3_wd = previous_word(start, 2);
        prev4_wd = previous_word(start, 3);
        prev5_wd = previous_word(start, 4);
+       prev6_wd = previous_word(start, 5);
+       prev7_wd = previous_word(start, 6);
 
        /* If a backslash command was started, continue */
        if (text[0] == '\\')
@@ -448,7 +467,7 @@ static char * psql_completion(char *text, int start, int 
end, void *dbptr)
        {
                static const char *const list_ALTER[] =
                {"AGGREGATE", "CONVERSION", "DATABASE", "DOMAIN", "FUNCTION",
-                       "GROUP", "INDEX", "LANGUAGE", "OPERATOR", "ROLE", 
"SCHEMA", "SEQUENCE", "TABLE",
+                       "GROUP", "INDEX", "LANGUAGE", "OPERATOR", "POLICY", 
"ROLE", "SCHEMA", "SEQUENCE", "TABLE",
                "TABLESPACE", "TRIGGER", "TYPE", "USER", NULL};
 
                COMPLETE_WITH_LIST(list_ALTER);
@@ -781,7 +800,7 @@ static char * psql_completion(char *text, int start, int 
end, void *dbptr)
                         pg_strcasecmp(prev_wd, "ON") == 0)
        {
                static const char *const list_COMMENT[] =
-               {"CAST", "CONVERSION", "DATABASE", "INDEX", "LANGUAGE", "RULE", 
"SCHEMA",
+               {"CAST", "CONVERSION", "DATABASE", "INDEX", "LANGUAGE", 
"POLICY", "RULE", "SCHEMA",
                        "SEQUENCE", "TABLE", "TYPE", "VIEW", "COLUMN", 
"AGGREGATE", "FUNCTION",
                "OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT", 
NULL};
 
@@ -1265,6 +1284,141 @@ static char * psql_completion(char *text, int start, 
int end, void *dbptr)
                         pg_strcasecmp(prev_wd, "GROUP") == 0)
                COMPLETE_WITH_CONST("BY");
 
+/* POLICY */
+       /* Complete "CREATE POLICY <name> ON" */
+       else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
+                        pg_strcasecmp(prev2_wd, "POLICY") == 0)
+               COMPLETE_WITH_CONST("ON");
+       /* Complete "CREATE POLICY <name> ON <table>" */
+       else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
+                        pg_strcasecmp(prev3_wd, "POLICY") == 0 &&
+                        pg_strcasecmp(prev_wd, "ON") == 0)
+               COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
+       /* Complete "CREATE POLICY <name> ON <table> FOR|TO|USING|WITH CHECK" */
+       else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 &&
+                        pg_strcasecmp(prev4_wd, "POLICY") == 0 &&
+                        pg_strcasecmp(prev2_wd, "ON") == 0)
+       {
+               static const char *const list_CREATEPOLICY[] =
+                       {"FOR", "TO", "USING (", "WITH CHECK (", NULL};
+               COMPLETE_WITH_LIST(list_CREATEPOLICY);
+       }
+       /* CREATE POLICY <name> ON <table> FOR ALL|SELECT|INSERT|UPDATE|DELETE 
*/
+       else if (pg_strcasecmp(prev6_wd, "CREATE") == 0 &&
+                        pg_strcasecmp(prev5_wd, "POLICY") == 0 &&
+                        pg_strcasecmp(prev3_wd, "ON") == 0 &&
+                        pg_strcasecmp(prev_wd, "FOR") == 0)
+       {
+               static const char *const list_CREATEPOLICYFOR[] =
+                       {"ALL", "SELECT", "INSERT", "UPDATE", "DELETE", NULL};
+               COMPLETE_WITH_LIST(list_CREATEPOLICYFOR);
+       }
+       /* Complete "CREATE POLICY <name> ON <table> FOR INSERT TO|WITH CHECK" 
*/
+       /* Note this should happen before INSERT INTO/UPDATE checks*/
+       else if (pg_strcasecmp(prev7_wd, "CREATE") == 0 &&
+                        pg_strcasecmp(prev6_wd, "POLICY") == 0 &&
+                        pg_strcasecmp(prev4_wd, "ON") == 0 &&
+                        pg_strcasecmp(prev2_wd, "FOR") == 0 &&
+                        pg_strcasecmp(prev_wd, "INSERT") == 0)
+       {
+               static const char *const list_CREATEPOLICYFORINSERT[] =
+                       {"TO", "WITH CHECK (", NULL};
+               COMPLETE_WITH_LIST(list_CREATEPOLICYFORINSERT);
+       }
+       /* Complete "CREATE POLICY <name> ON <table> FOR SELECT|DELETE 
TO|USING" */
+       else if (pg_strcasecmp(prev7_wd, "CREATE") == 0 &&
+                        pg_strcasecmp(prev6_wd, "POLICY") == 0 &&
+                        pg_strcasecmp(prev4_wd, "ON") == 0 &&
+                        pg_strcasecmp(prev2_wd, "FOR") == 0 &&
+                        (pg_strcasecmp(prev_wd, "SELECT") == 0 || 
pg_strcasecmp(prev_wd, "DELETE") == 0))
+       {
+               static const char *const list_CREATEPOLICYFORSELECT[] =
+                       {"TO", "USING (", NULL};
+               COMPLETE_WITH_LIST(list_CREATEPOLICYFORSELECT);
+       }
+       /* CREATE POLICY <name> ON <table> FOR ALL|UPDATE TO|USING|WITH CHECK */
+       else if (pg_strcasecmp(prev7_wd, "CREATE") == 0 &&
+                        pg_strcasecmp(prev6_wd, "POLICY") == 0 &&
+                        pg_strcasecmp(prev4_wd, "ON") == 0 &&
+                        pg_strcasecmp(prev2_wd, "FOR") == 0 &&
+                        (pg_strcasecmp(prev_wd, "ALL") == 0 || 
pg_strcasecmp(prev_wd, "UPDATE") == 0))
+       {
+               static const char *const list_CREATEPOLICYFORSELECT[] =
+                       {"TO", "USING (", "WITH CHECK (", NULL};
+               COMPLETE_WITH_LIST(list_CREATEPOLICYFORSELECT);
+       }
+       /* Complete "CREATE POLICY <name> ON <table> TO <role>" */
+       else if (pg_strcasecmp(prev6_wd, "CREATE") == 0 &&
+                        pg_strcasecmp(prev5_wd, "POLICY") == 0 &&
+                        pg_strcasecmp(prev3_wd, "ON") == 0 &&
+                        pg_strcasecmp(prev_wd, "TO") == 0)
+               COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);
+       /* CREATE POLICY <name> ON <table> USING ( */
+       else if (pg_strcasecmp(prev6_wd, "CREATE") == 0 &&
+                        pg_strcasecmp(prev5_wd, "POLICY") == 0 &&
+                        pg_strcasecmp(prev3_wd, "ON") == 0 &&
+                        pg_strcasecmp(prev_wd, "USING") == 0)
+               COMPLETE_WITH_CONST("(");
+
+       /* ALTER POLICY <name> */
+       /* Should check db version to avoid errors with nonexistent catalogs */
+       else if (pg_strcasecmp(prev2_wd, "ALTER") == 0 &&
+                        pg_strcasecmp(prev_wd, "POLICY") == 0 &&
+                        BACKEND_MINIMUM(9, 5))
+               COMPLETE_WITH_QUERY(Query_for_list_of_policies);
+       /* ALTER POLICY <name> ON */
+       else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
+                        pg_strcasecmp(prev2_wd, "POLICY") == 0)
+               COMPLETE_WITH_CONST("ON");
+       /* ALTER POLICY <name> ON <table> */
+       else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
+                        pg_strcasecmp(prev3_wd, "POLICY") == 0 &&
+                        pg_strcasecmp(prev_wd, "ON") == 0)
+               COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
+       /* ALTER POLICY <name> ON <table> - show options */
+       else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
+                        pg_strcasecmp(prev4_wd, "POLICY") == 0 &&
+                        pg_strcasecmp(prev2_wd, "ON") == 0)
+       {
+               static const char *const list_ALTERPOLICY[] =
+                       {"RENAME TO", "TO", "USING (", "WITH CHECK (", NULL};
+               COMPLETE_WITH_LIST(list_ALTERPOLICY);
+       }
+       /* ALTER POLICY <name> ON <table> TO <role> */
+       else if (pg_strcasecmp(prev6_wd, "ALTER") == 0 &&
+                        pg_strcasecmp(prev5_wd, "POLICY") == 0 &&
+                        pg_strcasecmp(prev3_wd, "ON") == 0 &&
+                        pg_strcasecmp(prev_wd, "TO") == 0)
+               COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);
+       /* ALTER POLICY <name> ON <table> USING ( */
+       else if (pg_strcasecmp(prev6_wd, "ALTER") == 0 &&
+                        pg_strcasecmp(prev5_wd, "POLICY") == 0 &&
+                        pg_strcasecmp(prev3_wd, "ON") == 0 &&
+                        pg_strcasecmp(prev_wd, "USING") == 0)
+               COMPLETE_WITH_CONST("(");
+       /* ALTER POLICY <name> ON <table> WITH CHECK ( */
+       else if (pg_strcasecmp(prev7_wd, "CREATE") == 0 &&
+                        pg_strcasecmp(prev6_wd, "POLICY") == 0 &&
+                        pg_strcasecmp(prev4_wd, "ON") == 0 &&
+                        pg_strcasecmp(prev2_wd, "WITH") == 0 &&
+                        pg_strcasecmp(prev_wd, "CHECK") == 0)
+               COMPLETE_WITH_CONST("(");
+
+       /* DROP POLICY <name> */
+       else if (pg_strcasecmp(prev2_wd, "DROP") == 0 &&
+                        pg_strcasecmp(prev_wd, "POLICY") == 0 &&
+                        BACKEND_MINIMUM(9, 5))
+               COMPLETE_WITH_QUERY(Query_for_list_of_policies);
+       /* DROP POLICY <name> ON */
+       else if (pg_strcasecmp(prev3_wd, "DROP") == 0 &&
+                        pg_strcasecmp(prev2_wd, "POLICY") == 0)
+               COMPLETE_WITH_CONST("ON");
+       /* DROP POLICY <name> ON <table> */
+       else if (pg_strcasecmp(prev4_wd, "DROP") == 0 &&
+                        pg_strcasecmp(prev3_wd, "POLICY") == 0 &&
+                        pg_strcasecmp(prev_wd, "ON") == 0)
+               COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
+
 /* INSERT */
        /* Complete INSERT with "INTO" */
        else if (pg_strcasecmp(prev_wd, "INSERT") == 0)
@@ -1730,6 +1884,8 @@ static char * psql_completion(char *text, int start, int 
end, void *dbptr)
        free(prev3_wd);
        free(prev4_wd);
        free(prev5_wd);
+       free(prev6_wd);
+       free(prev7_wd);
 
        /* Return our Grand List O' Matches */
        return matches;
diff --git a/pgadmin/utils/tabcomplete.c b/pgadmin/utils/tabcomplete.c
index f8cba05..853915f 100644
--- a/pgadmin/utils/tabcomplete.c
+++ b/pgadmin/utils/tabcomplete.c
@@ -408,8 +408,8 @@ static char *complete_filename()
 /*
  * Entrypoint from the C++ world
  */
-char *tab_complete(const char *allstr, const int startptr, const int endptr, 
void *dbptr)
+char *tab_complete(const char *allstr, const int startptr, const int endptr, 
void *dbptr, int dbvmajor, int dbvminor)
 {
        rl_line_buffer = (char *)allstr;
-       return psql_completion((char *)(allstr + startptr), 
startptr,endptr,dbptr);
+       return psql_completion((char *)(allstr + startptr), 
startptr,endptr,dbptr,dbvmajor,dbvminor);
 }
-- 
2.7.2.windows.1

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

Reply via email to