*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 209,214 ****
--- 209,219 ----
       </row>
  
       <row>
+       <entry><link linkend="catalog-pg-seclabel"><structname>pg_seclabel</structname></link></entry>
+       <entry>security labels on local database objects</entry>
+      </row>
+ 
+      <row>
        <entry><link linkend="catalog-pg-shdepend"><structname>pg_shdepend</structname></link></entry>
        <entry>dependencies on shared objects</entry>
       </row>
***************
*** 4220,4225 ****
--- 4225,4315 ----
    </table>
   </sect1>
  
+  <sect1 id="catalog-pg-seclabel">
+   <title><structname>pg_seclabel</structname></title>
+ 
+   <indexterm zone="catalog-pg-seclabel">
+    <primary>pg_seclabel</primary>
+   </indexterm>
+ 
+   <para>
+    The catalog <structname>pg_seclabel</structname> stores the security
+    label of database objects. This information allows external security
+    providers to apply label based mandatory access controls.
+   </para>
+ 
+   <para>
+    When external security providers with label based mandatory access
+    control are installed, its security label shall be assigned on
+    creation of database obejcts.
+    (Please note that only relations are supported right now.)
+   </para>
+   <para>
+    The security labels shall be automatically cleaned up when the database
+    object being labeled is dropped.
+   </para>
+   <para>
+    See also <link linkend="sql-security-label"><command>SECURITY LABEL</command></link>,
+    which provides a functionality to relabel a certain database object,
+    as long as user has enough privileges.
+   </para>
+ 
+   <table>
+    <title><structname>pg_seclabel</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>reloid</structfield></entry>
+       <entry><type>oid</type></entry>
+       <entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
+       <entry>The OID of the system catalog this object appears in</entry>
+      </row>
+ 
+      <row>
+       <entry><structfield>objoid</structfield></entry>
+       <entry><type>oid</type></entry>
+       <entry>any OID column</entry>
+       <entry>The OID of the object this security label assigned on</entry>
+      </row>
+ 
+      <row>
+       <entry><structfield>subid</structfield></entry>
+       <entry><type>int4</type></entry>
+       <entry></entry>
+       <entry>For a security label on a table column, this is the column number
+        (the <structfield>objoid</structfield> and <structfield>classoid</structfield>
+        refer to the table itself).  For all other object types, this column is
+        zero
+       </entry>
+      </row>
+ 
+      <row>
+       <entry><structfield>tag</structfield></entry>
+       <entry><type>text</type></entry>
+       <entry></entry>
+       <entry>identifier of external security provider</entry>
+      </row>
+ 
+      <row>
+       <entry><structfield>label</structfield></entry>
+       <entry><type>text</type></entry>
+       <entry></entry>
+       <entry>security label in text format</entry>
+      </row>
+     </tbody>
+    </tgroup>
+   </table>
+  </sect1>
  
   <sect1 id="catalog-pg-shdepend">
    <title><structname>pg_shdepend</structname></title>
*** /dev/null
--- b/doc/src/sgml/esp.sgml
***************
*** 0 ****
--- 1,249 ----
+ <chapter id="esp">
+  <title>External Security Provider</title>
+  <para>
+   External security provider (ESP) is a plugin which makes access control
+   decision based on its security model and policy.
+  </para>
+  <para>
+   <productname>PostgreSQL</productname> version 9.1 or later provides a set of
+   facilities to enhance access control features using ESP, in addition to the
+   default database privilege mechanism introduced
+   as the <xref linkend="user-manag">.
+  </para>
+  <para>
+   This chapter introduces brief overview of ESP concept and corresponding
+   features, but it does not introduce details of individual ESP plugins.
+   If you need to know something about a certain ESP plugin, please reference
+   documentation of the plugin.
+  </para>
+ 
+  <sect1 id="esp-overview">
+   <title>Overview</title>
+   <para>
+    The ESP concept was designed to integrate various kind of security
+    models into <productname>PostgreSQL</productname>.
+ 
+    It allows third-party modules to perform as security provider which
+    makes access control decision on accesses to database objects;
+    such as tables, functions and so on.
+ 
+    Please note that it never replaces anything in the default database
+    privilege mechanism. It just installs additional access control stuffs.
+   </para>
+   <para>
+    Access control is a mechanism that decides what actions are allowed
+    or denied on a pair of certain user and object to be referenced,
+    then applies the decision on any requests come from clients.
+   </para>
+   <para>
+    The default database privilege mechanism makes its access control
+    decision based on the authenticated user identifier and permissions
+    of database objects to be referenced.
+   </para>
+   <para>
+    Here are some cases that we hope to make and apply access control
+    decisions based on a different basis from the default one.
+ 
+    For example, one significant goal of ESP is integration of mandatory
+    access control that only allows security administrator to set up
+    access rights of database objects, unlike the default one which
+    allows owner of them to set up their access rights.
+   </para>
+   <para>
+    At the implementation level, <productname>PostgreSQL</productname> 9.1
+    or later provides a series of security hooks which are put on strategic
+    points of core routines; such as a routine that checks permissions of
+    tables and columns for the supplied DML statement.
+   </para>
+   <para>
+    ESP modules shall be invoked via the security hooks, then it can make
+    its access control decision based on the security model and supplied
+    information about user's request; such as OID of tables to be referenced.
+   </para>
+   <para>
+    Expected behaviors are depending on every security hooks.
+ 
+    One hook may assume ESP raises an error, if access control violation.
+    One other hook may assume ESP returns a bool value which informs the
+    caller routine whether its decision was 'allowed' or 'denied'.
+   </para>
+   <para>
+    These hooks shall be filled up when we load plugins.
+ 
+    Administrator can add a configuration of
+    <literal>shared_preload_libraries</literal> on which we specify a few
+    plugins to be loaded on starting up the server process, as follows:
+ 
+ <programlisting>
+ shared_preload_libraries = 'sepgsql'    # (change requires restart)
+ </programlisting>
+ 
+    You should never use <literal>local_preload_libraries</literal> instead,
+    even if the plugin is well verified without any vulnerabilities, because
+    it can be overridden by GUC variable settings passed in startup packet.
+   </para>
+   <para>
+    Please note that ESP framework has quite-limited coverage of access control
+    compared to the default database privilege mechanism right now.
+    In this release, it only hooks on execution of DML, a part of DDL and
+    post client authentication process.
+   </para>
+  </sect1>
+ 
+  <sect1 id="esp-mac">
+   <title>Mandatory Access Control</title>
+   <para>
+    It is a major of ESP to support mandatory access control (MAC) policy
+    in <productname>PostgreSQL</productname>.
+   </para>
+   <para>
+    Unlike discretionary access control (DAC), only a security administrator
+    is allowed to control centralized MAC policy and access rights on
+    individual objects; it means any other users don't have ability to change
+    the access rights on database objects, even if they have ownership of the
+    object.
+   </para>
+   <para>
+    This characteristic enables to confine classified information in certain
+    domains, even if owner of data tries to leak it somewhere, because the
+    owner cannot change its access rights as he like.
+   </para>
+   <para>
+    Here is a a traditional rule for instance. It prevents people who can
+    read both of classified and unclassified data object to write unclassified
+    data object. It means infomation moves only single direction; from
+    unclassified to classified. Since this rule is applied to everybody,
+    noboby can leak classified information into unclassified ones, even if
+    a malicious internal tries to do.
+   </para>
+   <para>
+    MAC is designed to tackle such a strict security requirement.
+   </para>
+ 
+   <sect2 id="esp-mac-label">
+    <title>Security Label</title>
+    <para>
+     In the default database privileges, <command>GRANT</command> and
+     <command>REVOKE</command> commands allow superusers and owner of the
+     database obejcts to set up access permissions, then it makes access
+     control decision based on these properties.
+    </para>
+    <para>
+     On the other hand, MAC model also has a property that characterizes
+     database objects from the viewpoint of MAC. A typical MAC feature uses
+     security label to identify the objects. It is a short text with a format
+     according to the security model.
+    </para>
+    <para>
+     The following examples are security label in SELinux.
+     It contains all the needed properties to make access control decision
+     from the viewpoint of SELinux, like a pair of access permissions and
+     owner identifier of the database object.
+ <synopsis>
+ dbadm_u:system_r:postgresql_t:s0
+ 
+ system_u:object_r:sepgsql_table_t:s0:c0
+ </synopsis>
+    </para>
+    <para>
+     It also means MAC model cannot make its access control decision on accesses
+     to database objects without security labels. So, we need to assign a certain
+     security label on database objects to be referenced.
+    </para>
+    <para>
+     In addition, MAC model also represents privilege set of user as a security
+     label, instead of user identifier. Mostly, security label of the users are
+     assigned according to the result of authentication.
+    </para>
+    <para>
+     In other words, MAC mechanism is something like a function which returns
+     a binary state ('allowed' or 'denied') for the supplied actions and
+     security labels of user and object to be referenced.
+    </para>
+    <para>
+     DAC mechanism (ie; the default database privilege mechanism) is also like
+     a function which returns a binary state for the supplied actions, user
+     identifier and permissions on the object to be referenced, so they are not
+     fundamentally difference.
+    </para>
+   </sect2>
+   <sect2 id="esp-mac-label-manage">
+    <title>Management of security label</title>
+    <para>
+     As we introduced above, MAC mechanism needs both of user and object to be
+     labeled, to make its access control decision.
+    </para>
+    <para>
+     For database objects, <productname>PostgreSQL</productname> provides
+     a facility that enables to assign a security label on them.
+ 
+     When an ESP plugin of MAC is installed, it shall assign a default security
+     label of a new database object on its creation time.
+ 
+     In addition, user can relabel security label of database objects using
+     <xref linkend="SQL-SECURITY-LABEL"> command later, as long as this
+     operation is allowed by security mechanisms.
+    </para>
+    <para>
+     The following example creates a new table <literal>tbl</literal> on the
+     system with <productname>SELinux</productname> support using ESP.
+ <screen>
+ postgres=> CREATE TABLE tbl (x int, y text);
+ CREATE TABLE
+ postgres=> SELECT * FROM pg_seclabel WHERE objoid = 'tbl'::regclass and subid=0;
+  reloid | objoid | subid |   tag   |                label
+ --------+--------+-------+---------+--------------------------------------
+    1259 |  24600 |     0 | selinux | system_u:object_r:sepgsql_table_t:s0
+ (1 row)
+ </screen>
+     It shows us that <literal>system_u:object_r:sepgsql_table_t:s0</literal>
+     was assigned on the creation time of table <literal>tbl</literal>.
+ <screen>
+ postgres=> SECURITY LABEL ON TABLE tbl IS 'system_u:object_r:sepgsql_ro_table_t:s0';
+ SECURITY LABEL
+ postgres=> SELECT * FROM pg_seclabel WHERE objoid = 'tbl'::regclass and subid=0;
+  reloid | objoid | subid |   tag   |                  label
+ --------+--------+-------+---------+-----------------------------------------
+    1259 |  24600 |     0 | selinux | system_u:object_r:sepgsql_ro_table_t:s0
+ (1 row)
+ </screen>
+     It also shows us <command>SECURITY LABEL</command> command allows to relabel
+     the table to <literal>system_u:object_r:sepgsql_ro_table_t:s0</literal>.
+    </para>
+    <para>
+     The
+     <link linkend="catalog-pg-seclabel">
+      <structname>pg_seclabel</structname>
+     </link>
+     is a system catalog to store
+     security labels, and ESP plugins shall reference the catalog to fetch
+     them on making its access control decisions.
+    </para>
+    <para>
+     On the other hand, the way to assign a security label of user is depending
+     on the type of ESP plugin.
+ 
+     Perhaps, one plugin may associates a security label with authenticated
+     database user, and other plugin assigns a security label which is retrieved
+     from the peer process using system API.
+ 
+     So, <productname>PostgreSQL</productname> does not constrain ESP plugins
+     a certain way to retrive security label of users.
+     It provides a hook just after the database authentication, ESP plugin can
+     assign a security label on the current session with its own way.
+    </para>
+    <para>
+     When we backup and restore databases, security labels are also properties
+     of database objects to be handled correctly.
+ 
+     The <command>pg_dump</command> and <command>pg_dumpall</command> support
+     <literal>--security-label</literal> option that enables to include
+     security label of the database objects into archives.
+ 
+     And the <command>pg_restore</command> also supports
+     <literal>--no-security-label</literal> option that enables to ignore
+     security labels, even if the archives contains labels.
+    </para>
+   </sect2>
+  </sect1>
+ </chapter>
*** a/doc/src/sgml/external-projects.sgml
--- b/doc/src/sgml/external-projects.sgml
***************
*** 250,254 ****
--- 250,266 ----
     <application><ulink url="http://www.pgadmin.org/">pgAdmin III</ulink></>, 
     and there are several commercially available ones as well.
    </para>
+ 
+   <para>
+    The <xref linkend="esp"> introduces how <productname>PostgreSQL</>
+    integrate additional security features using external security
+    providers.
+ 
+    <application><ulink url="http://code.google.com/p/sepgsql/">
+      SE-PostgreSQL
+    </ulink></application> is an external security provider which
+    applies mandatory access control based on the centralized
+    security policy of operation system.
+   </para>
   </sect1>
  </appendix>
*** a/doc/src/sgml/filelist.sgml
--- b/doc/src/sgml/filelist.sgml
***************
*** 46,51 ****
--- 46,52 ----
  <!entity runtime       SYSTEM "runtime.sgml">
  <!entity config        SYSTEM "config.sgml">
  <!entity user-manag    SYSTEM "user-manag.sgml">
+ <!entity esp           SYSTEM "esp.sgml">
  <!entity wal           SYSTEM "wal.sgml">
  
  <!-- programmer's guide -->
*** a/doc/src/sgml/postgres.sgml
--- b/doc/src/sgml/postgres.sgml
***************
*** 150,155 ****
--- 150,156 ----
    &config;
    &client-auth;
    &user-manag;
+   &esp;
    &manage-ag;
    &charset;
    &maintenance;
*** a/doc/src/sgml/ref/allfiles.sgml
--- b/doc/src/sgml/ref/allfiles.sgml
***************
*** 132,137 **** Complete list of usable sgml source files in this directory.
--- 132,138 ----
  <!entity rollbackPrepared   system "rollback_prepared.sgml">
  <!entity rollbackTo         system "rollback_to.sgml">
  <!entity savepoint          system "savepoint.sgml">
+ <!entity securityLabel      system "seclabel.sgml">
  <!entity select             system "select.sgml">
  <!entity selectInto         system "select_into.sgml">
  <!entity set                system "set.sgml">
*** a/doc/src/sgml/ref/pg_dump.sgml
--- b/doc/src/sgml/ref/pg_dump.sgml
***************
*** 778,783 **** PostgreSQL documentation
--- 778,793 ----
         </para>
        </listitem>
       </varlistentry>
+ 
+      <varlistentry>
+       <term><option>--security-label</option></term>
+       <listitem>
+        <para>
+         With this option, it also outputs security labels of database
+         objects to be dumped, if labeled.
+        </para>
+       </listitem>
+      </varlistentry>
      </variablelist>
     </para>
   </refsect1>
*** a/doc/src/sgml/ref/pg_dumpall.sgml
--- b/doc/src/sgml/ref/pg_dumpall.sgml
***************
*** 493,498 **** PostgreSQL documentation
--- 493,507 ----
         </para>
        </listitem>
       </varlistentry>
+      <varlistentry>
+       <term><option>--security-label</option></term>
+       <listitem>
+        <para>
+         With this option, it also outputs security labels of database
+         objects to be dumped, if labeled.
+        </para>
+       </listitem>
+      </varlistentry>
     </variablelist>
    </para>
   </refsect1>
*** a/doc/src/sgml/ref/pg_restore.sgml
--- b/doc/src/sgml/ref/pg_restore.sgml
***************
*** 329,334 ****
--- 329,347 ----
       </varlistentry>
  
       <varlistentry>
+       <term><option>--no-security-label</option></term>
+       <listitem>
+        <para>
+         Do not output commands to relabel database objects,
+         even if the archive contains them.
+         With this option, all objects will be created with a default
+         security label, or without any labels if no label based
+         security feature is not installed.
+        </para>
+       </listitem>
+      </varlistentry>
+ 
+      <varlistentry>
        <term><option>-P <replaceable class="parameter">function-name(argtype [, ...])</replaceable></option></term>
        <term><option>--function=<replaceable class="parameter">function-name(argtype [, ...])</replaceable></option></term>
        <listitem>
*** /dev/null
--- b/doc/src/sgml/ref/seclabel.sgml
***************
*** 0 ****
--- 1,123 ----
+ <refentry id="SQL-SECURITY-LABEL">
+  <refmeta>
+   <refentrytitle>SECURITY LABEL</refentrytitle>
+   <manvolnum>7</manvolnum>
+   <refmiscinfo>SQL - Language Statements</refmiscinfo>
+  </refmeta>
+ 
+  <refnamediv>
+   <refname>SECURITY LABEL</refname>
+   <refpurpose>relabel the security label of an object</refpurpose>
+  </refnamediv>
+ 
+  <indexterm zone="sql-security-label">
+   <primary>SECURITY LABEL</primary>
+  </indexterm>
+ 
+  <refsynopsisdiv>
+ <synopsis>
+ SECURITY LABEL [ FOR '<replaceable class="PARAMETER">esp_tag</replaceable>' ] ON
+ {
+   TABLE <replaceable class="PARAMETER">object_name</replaceable> |
+   COLUMN <replaceable class="PARAMETER">table_name</replaceable>.<replaceable class="PARAMETER">column_name</replaceable> |
+   SEQUENCE <replaceable class="PARAMETER">object_name</replaceable> |
+   VIEW <replaceable class="PARAMETER">object_name</replaceable>
+ } IS '<replaceable class="PARAMETER">security_label</replaceable>';
+ </synopsis>
+  </refsynopsisdiv>
+ 
+  <refsect1>
+   <title>Description</title>
+ 
+   <para>
+    <command>SECURITY LABEL</command> relabels a security label of a database object.
+   </para>
+ 
+   <para>
+    We can assign individual security labels on a certain database object for
+    each security providers, using <command>SECURITY LABEL</command> command.
+   </para>
+   <para>
+    Security labels are automatically assigned when the object is created,
+    and also automatically dropped when the object is dropped.
+   </para>
+ 
+  </refsect1>
+ 
+  <refsect1>
+   <title>Parameters</title>
+   <variablelist>
+    <varlistentry>
+     <term><replaceable class="parameter">object_name</replaceable></term>
+     <term><replaceable class="parameter">table_name</replaceable></term>
+     <term><replaceable class="parameter">table_name.column_name</replaceable></term>
+     <listitem>
+      <para>
+       The name of the object to be relabeled.  Names of tables, columns,
+       sequences and views can be schema-qualified.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><replaceable class="parameter">security_label</replaceable></term>
+     <listitem>
+      <para>
+       The new security label as a string literal.
+      </para>
+      <para>
+       It shall be validated by one of the label based security features,
+       in addition to its permission checks relabeling on the specified
+       database objects.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><replaceable class="parameter">esp_tag</replaceable></term>
+     <listitem>
+      <para>
+       The identifier string of external security provider.
+      </para>
+      <para>
+       When we install just one provider, we can omit this clause because
+       it is obvious which provider shall handle the given security label.
+       Elsewhere, when we install two or more providers concurrently,
+       we need to identify a certain external security provider.
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Notes</title>
+   <para>
+    This feature requires one label based mandatory access control feature
+    to be installed at least, because a security label is specific for
+    each providers, so it has to be validated when we relabel it.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Examples</title>
+ 
+   <para>
+    Relabel a security label on the table to '<literal>system_u:object_r:sepgsql_table_t:s0</literal>'.
+ 
+ <programlisting>
+ SECURITY LABEL FOR 'selinux' ON TABLE mytable IS 'system_u:object_r:sepgsql_table_t:s0';
+ </programlisting>
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Compatibility</title>
+   <para>
+    There is no <command>SECURITY LABEL</command> command in the SQL standard.
+   </para>
+  </refsect1>
+ </refentry>
+ 
+ 
+ 
*** a/doc/src/sgml/reference.sgml
--- b/doc/src/sgml/reference.sgml
***************
*** 160,165 ****
--- 160,166 ----
     &rollbackPrepared;
     &rollbackTo;
     &savepoint;
+    &securityLabel;
     &select;
     &selectInto;
     &set;
*** a/src/backend/catalog/Makefile
--- b/src/backend/catalog/Makefile
***************
*** 38,44 **** 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_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
! 	pg_default_acl.h \
  	toasting.h indexing.h \
      )
  
--- 38,44 ----
  	pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
  	pg_ts_parser.h pg_ts_template.h \
  	pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
! 	pg_default_acl.h pg_seclabel.h \
  	toasting.h indexing.h \
      )
  
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
***************
*** 57,62 ****
--- 57,63 ----
  #include "commands/defrem.h"
  #include "commands/proclang.h"
  #include "commands/schemacmds.h"
+ #include "commands/seclabel.h"
  #include "commands/tablespace.h"
  #include "commands/trigger.h"
  #include "commands/typecmds.h"
***************
*** 1010,1015 **** deleteOneObject(const ObjectAddress *object, Relation depRel)
--- 1011,1023 ----
  	DeleteComments(object->objectId, object->classId, object->objectSubId);
  
  	/*
+ 	 * Delete any security labels associated with this object. (This is also
+ 	 * a convenient place to do it instead of having every object type know
+ 	 * to do it.)
+ 	 */
+ 	DeleteSecurityLabel(object);
+ 
+ 	/*
  	 * CommandCounterIncrement here to ensure that preceding changes are all
  	 * visible to the next deletion step.
  	 */
*** a/src/backend/commands/Makefile
--- b/src/backend/commands/Makefile
***************
*** 17,23 **** OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o  \
  	dbcommands.o define.o discard.o explain.o foreigncmds.o functioncmds.o \
  	indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \
  	portalcmds.o prepare.o proclang.o \
! 	schemacmds.o sequence.o tablecmds.o tablespace.o trigger.o \
  	tsearchcmds.o typecmds.o user.o vacuum.o vacuumlazy.o \
  	variable.o view.o
  
--- 17,23 ----
  	dbcommands.o define.o discard.o explain.o foreigncmds.o functioncmds.o \
  	indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \
  	portalcmds.o prepare.o proclang.o \
! 	schemacmds.o seclabel.o sequence.o tablecmds.o tablespace.o trigger.o \
  	tsearchcmds.o typecmds.o user.o vacuum.o vacuumlazy.o \
  	variable.o view.o
  
*** /dev/null
--- b/src/backend/commands/seclabel.c
***************
*** 0 ****
--- 1,416 ----
+ /* -------------------------------------------------------------------------
+  *
+  * seclabel.c
+  *    routines to support security label feature.
+  *
+  * Portions Copyright (c) 1996-2010, 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 "catalog/catalog.h"
+ #include "catalog/indexing.h"
+ #include "catalog/pg_seclabel.h"
+ #include "commands/seclabel.h"
+ #include "miscadmin.h"
+ #include "utils/acl.h"
+ #include "utils/builtins.h"
+ #include "utils/fmgroids.h"
+ #include "utils/lsyscache.h"
+ #include "utils/memutils.h"
+ #include "utils/tqual.h"
+ 
+ /*
+  * GetSecurityLabel
+  *
+  * It tries to look up a security label entry for the given OID of
+  * the catalog, object itself and sub identifier (if needed) from
+  * the pg_selabel system catalog.
+  * It can return NULL, if no valid entry. Elsewhere, it returns
+  * a security label of the specified database object.
+  */
+ char *
+ GetSecurityLabel(const ObjectAddress *object, const char *tag)
+ {
+ 	Relation	pg_seclabel;
+ 	ScanKeyData	keys[4];
+ 	SysScanDesc	scan;
+ 	HeapTuple	tuple;
+ 	Datum		datum;
+ 	bool		isnull;
+ 	char	   *seclabel = NULL;
+ 
+ 	Assert(!IsSharedRelation(object->classId));
+ 
+ 	ScanKeyInit(&keys[0],
+ 				Anum_pg_seclabel_reloid,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(object->classId));
+ 	ScanKeyInit(&keys[1],
+ 				Anum_pg_seclabel_objoid,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(object->objectId));
+ 	ScanKeyInit(&keys[2],
+ 				Anum_pg_seclabel_subid,
+ 				BTEqualStrategyNumber, F_INT4EQ,
+ 				Int32GetDatum(object->objectSubId));
+ 	ScanKeyInit(&keys[3],
+ 				Anum_pg_seclabel_tag,
+ 				BTEqualStrategyNumber, F_TEXTEQ,
+ 				CStringGetTextDatum(tag));
+ 
+ 	pg_seclabel = heap_open(SecLabelRelationId, AccessShareLock);
+ 
+ 	scan = systable_beginscan(pg_seclabel, SecLabelObjectIndexId, true,
+ 							  SnapshotNow, 4, keys);
+ 
+ 	tuple = systable_getnext(scan);
+ 	if (HeapTupleIsValid(tuple))
+ 	{
+ 		datum = heap_getattr(tuple, Anum_pg_seclabel_label,
+ 							 RelationGetDescr(pg_seclabel), &isnull);
+ 		if (!isnull)
+ 			seclabel = TextDatumGetCString(datum);
+ 	}
+ 	systable_endscan(scan);
+ 
+ 	heap_close(pg_seclabel, AccessShareLock);
+ 
+ 	return seclabel;
+ }
+ 
+ /*
+  * SetSecurityLabel
+  *
+  * It tries to insert/update/delete a security label for the given OID
+  * of the catalog, object itself and sub identifier (if needed) on the
+  * pg_seclabel system catalog.
+  * If given 'seclabel' is NULL, it tries to delete the specified entry.
+  * Elsewhere, it tries to insert (if no specified entry now) or updata
+  * security label of the specified entry.
+  */
+ void
+ SetSecurityLabel(const ObjectAddress *object,
+ 				 const char *tag, const char *seclabel)
+ {
+ 	Relation	pg_seclabel;
+ 	ScanKeyData	keys[4];
+ 	SysScanDesc	scan;
+ 	HeapTuple	oldtup;
+ 	HeapTuple	newtup = NULL;
+ 	Datum		values[Natts_pg_seclabel];
+ 	bool		nulls[Natts_pg_seclabel];
+ 	bool		replaces[Natts_pg_seclabel];
+ 
+ 	Assert(!IsSharedRelation(object->classId));
+ 
+ 	ScanKeyInit(&keys[0],
+ 				Anum_pg_seclabel_reloid,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+                 ObjectIdGetDatum(object->classId));
+ 	ScanKeyInit(&keys[1],
+ 				Anum_pg_seclabel_objoid,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(object->objectId));
+ 	ScanKeyInit(&keys[2],
+ 				Anum_pg_seclabel_subid,
+ 				BTEqualStrategyNumber, F_INT4EQ,
+ 				Int32GetDatum(object->objectSubId));
+ 	ScanKeyInit(&keys[3],
+ 				Anum_pg_seclabel_tag,
+ 				BTEqualStrategyNumber, F_TEXTEQ,
+ 				CStringGetTextDatum(tag));
+ 
+ 	pg_seclabel = heap_open(SecLabelRelationId, RowExclusiveLock);
+ 
+ 	scan = systable_beginscan(pg_seclabel, SecLabelObjectIndexId, true,
+ 							  SnapshotNow, 4, keys);
+ 
+ 	oldtup = systable_getnext(scan);
+ 	if (HeapTupleIsValid(oldtup))
+ 	{
+ 		if (seclabel != NULL)
+ 		{
+ 			/*
+ 			 * update the specified security label entry
+ 			 */
+ 			memset(nulls, false, sizeof(nulls));
+ 			memset(replaces, false, sizeof(replaces));
+ 
+ 			replaces[Anum_pg_seclabel_label - 1] = true;
+ 			values[Anum_pg_seclabel_label - 1]
+ 				= CStringGetTextDatum(seclabel);
+ 
+ 			newtup = heap_modify_tuple(oldtup, RelationGetDescr(pg_seclabel),
+ 									   values, nulls, replaces);
+ 			simple_heap_update(pg_seclabel, &oldtup->t_self, newtup);
+ 
+ 			CatalogUpdateIndexes(pg_seclabel, newtup);
+ 
+ 			heap_freetuple(newtup);
+ 		}
+ 		else
+ 		{
+ 			/*
+ 			 * when seclabel = NULL, it means to remove the matched
+ 			 * entry from pg_seclabel.
+ 			 */
+ 			simple_heap_delete(pg_seclabel, &oldtup->t_self);
+ 		}
+ 	}
+ 	else if (seclabel != NULL)
+ 	{
+ 		/*
+ 		 * insert a new security label entry
+ 		 */
+ 		memset(nulls, false, sizeof(nulls));
+ 		values[Anum_pg_seclabel_reloid - 1] = ObjectIdGetDatum(object->classId);
+ 		values[Anum_pg_seclabel_objoid - 1] = ObjectIdGetDatum(object->objectId);
+ 		values[Anum_pg_seclabel_subid - 1] = Int32GetDatum(object->objectSubId);
+ 		values[Anum_pg_seclabel_tag - 1] = CStringGetTextDatum(tag);
+ 		values[Anum_pg_seclabel_label - 1] = CStringGetTextDatum(seclabel);
+ 
+ 		newtup = heap_form_tuple(RelationGetDescr(pg_seclabel),
+ 								 values, nulls);
+ 		simple_heap_insert(pg_seclabel, newtup);
+ 
+ 		CatalogUpdateIndexes(pg_seclabel, newtup);
+ 
+ 		heap_freetuple(newtup);
+ 	}
+ 	systable_endscan(scan);
+ 		
+ 	heap_close(pg_seclabel, RowExclusiveLock);
+ }
+ 
+ /*
+  * DeleteSecurityLabel
+  *
+  * It tries to delete entries of security labels for given OID of
+  * the catalog, object itself and sub identifier (if needed) on
+  * the pg_seclabel system catalog.
+  * If given 'objectSubId' is 0, all the security labels matching
+  * with classId and objectId will be removed.
+  */
+ void
+ DeleteSecurityLabel(const ObjectAddress *object)
+ {
+ 	Relation	pg_seclabel;
+ 	ScanKeyData	keys[3];
+ 	SysScanDesc	scan;
+ 	HeapTuple	oldtup;
+ 	int			nkeys = 2;
+ 
+ 	/*
+ 	 * right now, we have obviously nothing to do on shared database
+ 	 * object deletion
+ 	 */
+ 	if (IsSharedRelation(object->classId))
+ 		return;
+ 
+ 	ScanKeyInit(&keys[0],
+ 				Anum_pg_seclabel_reloid,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(object->classId));
+ 	ScanKeyInit(&keys[1],
+ 				Anum_pg_seclabel_objoid,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(object->objectId));
+ 	if (object->objectSubId != 0)
+ 	{
+ 		ScanKeyInit(&keys[2],
+ 					Anum_pg_seclabel_subid,
+ 					BTEqualStrategyNumber, F_INT4EQ,
+ 					Int32GetDatum(object->objectSubId));
+ 		nkeys = 3;
+ 	}
+ 
+ 	pg_seclabel = heap_open(SecLabelRelationId, RowExclusiveLock);
+ 
+ 	scan = systable_beginscan(pg_seclabel, SecLabelObjectIndexId, true,
+ 							  SnapshotNow, nkeys, keys);
+ 	while (HeapTupleIsValid(oldtup = systable_getnext(scan)))
+ 	{
+ 		simple_heap_delete(pg_seclabel, &oldtup->t_self);
+ 	}
+ 	systable_endscan(scan);
+ 
+ 	heap_close(pg_seclabel, RowExclusiveLock);
+ }
+ 
+ /*
+  * External security provider hook
+  */
+ static List *esp_relabel_hook_list = NIL;
+ 
+ typedef struct
+ {
+ 	const char				   *tag;
+ 	check_object_relabel_type	hook;
+ } esp_relabel_hook_entry;
+ 
+ void
+ register_object_relabel_hook(const char *tag,
+ 							 check_object_relabel_type hook)
+ {
+ 	esp_relabel_hook_entry *entry;
+ 	MemoryContext			oldcxt;
+ 
+ 	oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+ 
+ 	entry = palloc(sizeof(esp_relabel_hook_entry));
+ 	entry->tag = pstrdup(tag);
+ 	entry->hook = hook;
+ 
+ 	esp_relabel_hook_list = lappend(esp_relabel_hook_list, entry);
+ 
+ 	MemoryContextSwitchTo(oldcxt);
+ }
+ 
+ /*
+  * check_relation_relabel 
+  *
+  * It checks whether the user is allowed to relabel this relation
+  */
+ static void
+ check_relation_relabel(int objtype, Relation relation)
+ {
+ 	/*
+ 	 * check object ownership
+ 	 */
+ 	if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
+ 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+ 					   RelationGetRelationName(relation));
+ 
+ 	/*
+ 	 * next, verify that the relation type matches the intent
+ 	 */
+ 	switch (objtype)
+ 	{
+ 		case OBJECT_TABLE:
+ 		case OBJECT_COLUMN:
+ 			if (relation->rd_rel->relkind != RELKIND_RELATION)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 						 errmsg("\"%s\" is not a table",
+ 								RelationGetRelationName(relation))));
+ 			break;
+ 
+ 		case OBJECT_SEQUENCE:
+ 			if (relation->rd_rel->relkind != RELKIND_SEQUENCE)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 						 errmsg("\"%s\" is not a sequence",
+ 								RelationGetRelationName(relation))));
+ 			break;
+ 
+ 		case OBJECT_VIEW:
+ 			if (relation->rd_rel->relkind != RELKIND_VIEW)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 						 errmsg("\"%s\" is not a view",
+ 								RelationGetRelationName(relation))));
+ 			break;
+ 	}
+ }
+ 
+ /*
+  * ExecSecLabelStmt
+  *
+  * SECURITY LABEL [FOR <esp>] ON <class> <name> IS <new label>
+  */
+ void
+ ExecSecLabelStmt(SecLabelStmt *stmt)
+ {
+ 	esp_relabel_hook_entry *esp_entry = NULL;
+ 	ObjectAddress			address;
+ 	Relation				relation;
+ 	ListCell			   *l;
+ 
+ 	/*
+ 	 * SECURITY LABEL statement needs a label based security feature is
+ 	 * available at least.
+ 	 */
+ 	if (esp_relabel_hook_list == NIL)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				 errmsg("no label based security is available")));
+ 
+ 	/*
+ 	 * when just one label based security is available, we can omit
+ 	 * FOR <esp> clause because it is obvious which ESP module handles
+ 	 * it. But, we have to specify the tag when multiple label based
+ 	 * securities are available.
+ 	 */
+ 	if (!stmt->tag)
+ 	{
+ 		if (list_length(esp_relabel_hook_list) > 1)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 					 errmsg("multiple label based securities are available")));
+ 
+ 		esp_entry = linitial(esp_relabel_hook_list);
+ 	}
+ 	else
+ 	{
+ 		foreach (l, esp_relabel_hook_list)
+ 		{
+ 			esp_entry = lfirst(l);
+ 
+ 			if (strcmp(stmt->tag, esp_entry->tag) == 0)
+ 				goto found;
+ 		}
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				 errmsg("no label based security for the tag: '%s'",
+ 						stmt->tag)));
+ 	}
+ 
+ found:
+ 	/*
+ 	 * Translate the parser representation which identifies this object
+ 	 * into ObjectAddress. get_object_address() will throw an error if
+      * the object does not exist, and will also acquire a lock on the
+      * target to guard against concurrent DROP operations.
+ 	 */
+ 	address = get_object_address(stmt->objtype, stmt->objname, stmt->objargs,
+ 								 &relation, ShareUpdateExclusiveLock);
+ 
+ 	/*
+ 	 * Privilege and integrity checks
+ 	 */
+ 	switch (stmt->objtype)
+ 	{
+ 		case OBJECT_SEQUENCE:
+ 		case OBJECT_TABLE:
+ 		case OBJECT_VIEW:
+ 		case OBJECT_COLUMN:
+ 			check_relation_relabel(stmt->objtype, relation);
+ 			break;
+ 
+ 		default:
+ 			elog(ERROR, "unrecognized object type: %d",
+ 				 (int)stmt->objtype);
+ 	}
+ 
+ 	Assert(esp_entry->hook != NULL);
+ 	(*esp_entry->hook)(&address, stmt->seclabel);
+ 
+ 	/*
+ 	 * Do actual relabeling. 
+ 	 */
+ 	SetSecurityLabel(&address, esp_entry->tag, stmt->seclabel);
+ 
+ 	/*
+ 	 * If get_object_address() opened the relation for us, we close it to keep
+ 	 * the reference count correct - but we retain any locks acquired by
+ 	 * get_object_address() until commit time, to guard against concurrent
+ 	 * activity.
+ 	 */
+ 	if (relation != NULL)
+ 		relation_close(relation, NoLock);
+ }
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 2607,2612 **** _copyCommentStmt(CommentStmt *from)
--- 2607,2626 ----
  	return newnode;
  }
  
+ static SecLabelStmt *
+ _copySecLabelStmt(SecLabelStmt *from)
+ {
+ 	SecLabelStmt *newnode = makeNode(SecLabelStmt);
+ 
+ 	COPY_SCALAR_FIELD(objtype);
+ 	COPY_NODE_FIELD(objname);
+ 	COPY_NODE_FIELD(objargs);
+ 	COPY_STRING_FIELD(tag);
+ 	COPY_STRING_FIELD(seclabel);
+ 
+ 	return newnode;
+ }
+ 
  static FetchStmt *
  _copyFetchStmt(FetchStmt *from)
  {
***************
*** 3958,3963 **** copyObject(void *from)
--- 3972,3980 ----
  		case T_CommentStmt:
  			retval = _copyCommentStmt(from);
  			break;
+ 		case T_SecLabelStmt:
+ 			retval = _copySecLabelStmt(from);
+ 			break;
  		case T_FetchStmt:
  			retval = _copyFetchStmt(from);
  			break;
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 1164,1169 **** _equalCommentStmt(CommentStmt *a, CommentStmt *b)
--- 1164,1181 ----
  }
  
  static bool
+ _equalSecLabelStmt(SecLabelStmt *a, SecLabelStmt *b)
+ {
+ 	COMPARE_SCALAR_FIELD(objtype);
+ 	COMPARE_NODE_FIELD(objname);
+ 	COMPARE_NODE_FIELD(objargs);
+ 	COMPARE_STRING_FIELD(tag);
+ 	COMPARE_STRING_FIELD(seclabel);
+ 
+ 	return true;
+ }
+ 
+ static bool
  _equalFetchStmt(FetchStmt *a, FetchStmt *b)
  {
  	COMPARE_SCALAR_FIELD(direction);
***************
*** 2624,2629 **** equal(void *a, void *b)
--- 2636,2644 ----
  		case T_CommentStmt:
  			retval = _equalCommentStmt(a, b);
  			break;
+ 		case T_SecLabelStmt:
+ 			retval = _equalSecLabelStmt(a, b);
+ 			break;
  		case T_FetchStmt:
  			retval = _equalFetchStmt(a, b);
  			break;
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 204,210 **** static TypeName *TableFuncTypeName(List *columns);
  		CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt
  		RemoveFuncStmt RemoveOperStmt RenameStmt RevokeStmt RevokeRoleStmt
  		RuleActionStmt RuleActionStmtOrEmpty RuleStmt
! 		SelectStmt TransactionStmt TruncateStmt
  		UnlistenStmt UpdateStmt VacuumStmt
  		VariableResetStmt VariableSetStmt VariableShowStmt
  		ViewStmt CheckPointStmt CreateConversionStmt
--- 204,210 ----
  		CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt
  		RemoveFuncStmt RemoveOperStmt RenameStmt RevokeStmt RevokeRoleStmt
  		RuleActionStmt RuleActionStmtOrEmpty RuleStmt
! 		SecLabelStmt SelectStmt TransactionStmt TruncateStmt
  		UnlistenStmt UpdateStmt VacuumStmt
  		VariableResetStmt VariableSetStmt VariableShowStmt
  		ViewStmt CheckPointStmt CreateConversionStmt
***************
*** 422,427 **** static TypeName *TableFuncTypeName(List *columns);
--- 422,429 ----
  %type <str>		OptTableSpace OptConsTableSpace OptTableSpaceOwner
  %type <list>	opt_check_option
  
+ %type <str>		label_item opt_esp
+ 
  %type <target>	xml_attribute_el
  %type <list>	xml_attribute_list xml_attributes
  %type <node>	xml_root_version opt_xml_root_standalone
***************
*** 499,505 **** static TypeName *TableFuncTypeName(List *columns);
  
  	KEY
  
! 	LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING
  	LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP
  	LOCATION LOCK_P LOGIN_P
  
--- 501,507 ----
  
  	KEY
  
! 	LABEL LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING
  	LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP
  	LOCATION LOCK_P LOGIN_P
  
***************
*** 737,742 **** stmt :
--- 739,745 ----
  			| RevokeStmt
  			| RevokeRoleStmt
  			| RuleStmt
+ 			| SecLabelStmt
  			| SelectStmt
  			| TransactionStmt
  			| TruncateStmt
***************
*** 4327,4332 **** comment_text:
--- 4330,4390 ----
  			| NULL_P							{ $$ = NULL; }
  		;
  
+ 
+ /*****************************************************************************
+  *
+  * ALTER THING name SECURITY LABEL TO label
+  *
+  *****************************************************************************/
+ 
+ SecLabelStmt: SECURITY LABEL opt_esp ON TABLE any_name IS label_item
+ 				{
+ 					SecLabelStmt *n = makeNode(SecLabelStmt);
+ 
+ 					n->objtype = OBJECT_TABLE;
+ 					n->objname = $6;
+ 					n->tag = $3;
+ 					n->seclabel = $8;
+ 					$$ = (Node *)n;
+ 				}
+ 			| SECURITY LABEL opt_esp ON COLUMN any_name IS label_item
+ 				{
+ 					SecLabelStmt *n = makeNode(SecLabelStmt);
+ 
+ 					n->objtype = OBJECT_COLUMN;
+ 					n->objname = $6;
+ 					n->tag = $3;
+ 					n->seclabel = $8;
+ 					$$ = (Node *)n;
+ 				}
+ 			| SECURITY LABEL opt_esp ON SEQUENCE any_name IS label_item
+ 				{
+ 					SecLabelStmt *n = makeNode(SecLabelStmt);
+ 
+ 					n->objtype = OBJECT_SEQUENCE;
+ 					n->objname = $6;
+ 					n->tag = $3;
+ 					n->seclabel = $8;
+ 					$$ = (Node *)n;
+ 				}
+ 			| SECURITY LABEL opt_esp ON VIEW any_name IS label_item
+ 				{
+ 					SecLabelStmt *n = makeNode(SecLabelStmt);
+ 
+ 					n->objtype = OBJECT_VIEW;
+ 					n->objname = $6;
+ 					n->tag = $3;
+ 					n->seclabel = $8;
+ 					$$ = (Node *)n;
+ 				}
+ 		;
+ 
+ label_item:		Sconst			{ $$ = $1; }
+ 
+ opt_esp:	FOR Sconst		{ $$ = $2; }
+ 			| /* empty */		{ $$ = NULL; }
+ 		;
+ 
  /*****************************************************************************
   *
   *		QUERY:
***************
*** 10993,10998 **** unreserved_keyword:
--- 11051,11057 ----
  			| INVOKER
  			| ISOLATION
  			| KEY
+ 			| LABEL
  			| LANGUAGE
  			| LARGE_P
  			| LAST_P
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 218,223 **** check_xact_readonly(Node *parsetree)
--- 218,224 ----
  		case T_AlterUserMappingStmt:
  		case T_DropUserMappingStmt:
  		case T_AlterTableSpaceOptionsStmt:
+ 		case T_SecLabelStmt:
  			PreventCommandIfReadOnly(CreateCommandTag(parsetree));
  			break;
  		default:
***************
*** 663,668 **** standard_ProcessUtility(Node *parsetree,
--- 664,673 ----
  			CommentObject((CommentStmt *) parsetree);
  			break;
  
+ 		case T_SecLabelStmt:
+ 			ExecSecLabelStmt((SecLabelStmt *) parsetree);
+ 			break;
+ 
  		case T_CopyStmt:
  			{
  				uint64		processed;
***************
*** 1592,1597 **** CreateCommandTag(Node *parsetree)
--- 1597,1606 ----
  			tag = "COMMENT";
  			break;
  
+ 		case T_SecLabelStmt:
+ 			tag = "SECURITY LABEL";
+ 			break;
+ 
  		case T_CopyStmt:
  			tag = "COPY";
  			break;
***************
*** 2314,2319 **** GetCommandLogLevel(Node *parsetree)
--- 2323,2332 ----
  			lev = LOGSTMT_DDL;
  			break;
  
+ 		case T_SecLabelStmt:
+ 			lev = LOGSTMT_DDL;
+ 			break;
+ 
  		case T_CopyStmt:
  			if (((CopyStmt *) parsetree)->is_from)
  				lev = LOGSTMT_MOD;
*** a/src/bin/pg_dump/pg_backup.h
--- b/src/bin/pg_dump/pg_backup.h
***************
*** 103,108 **** typedef struct _restoreOptions
--- 103,109 ----
  										 * restore */
  	int			use_setsessauth;/* Use SET SESSION AUTHORIZATION commands
  								 * instead of OWNER TO */
+ 	int			skip_seclabel;	/* Skip security label entries */
  	char	   *superuser;		/* Username to use as superuser */
  	char	   *use_role;		/* Issue SET ROLE to this */
  	int			dataOnly;
*** a/src/bin/pg_dump/pg_backup_archiver.c
--- b/src/bin/pg_dump/pg_backup_archiver.c
***************
*** 2275,2280 **** _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls)
--- 2275,2284 ----
  	if ((!include_acls || ropt->aclsSkip) && _tocEntryIsACL(te))
  		return 0;
  
+ 	/* If it's security labels, maybe ignore it */
+ 	if (ropt->skip_seclabel && strcmp(te->desc, "LABEL") == 0)
+ 		return 0;
+ 
  	/* Ignore DATABASE entry unless we should create it */
  	if (!ropt->createDB && strcmp(te->desc, "DATABASE") == 0)
  		return 0;
***************
*** 2341,2346 **** _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls)
--- 2345,2352 ----
  			(strcmp(te->desc, "ACL") == 0 &&
  			 strncmp(te->tag, "LARGE OBJECT ", 13) == 0) ||
  			(strcmp(te->desc, "COMMENT") == 0 &&
+ 			 strncmp(te->tag, "LARGE OBJECT ", 13) == 0) ||
+ 			(strcmp(te->desc, "LABEL") == 0 &&
  			 strncmp(te->tag, "LARGE OBJECT ", 13) == 0))
  			res = res & REQ_DATA;
  		else
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 125,130 **** static int	binary_upgrade = 0;
--- 125,131 ----
  static int	disable_dollar_quoting = 0;
  static int	dump_inserts = 0;
  static int	column_inserts = 0;
+ static int	security_label = 0;
  
  
  static void help(const char *progname);
***************
*** 183,188 **** static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
--- 184,194 ----
  		const char *tag, const char *nspname, const char *owner,
  		const char *acls);
  
+ static void dumpSecLabel(Archive *fout,
+ 						 CatalogId objCatId, DumpId objDumpId,
+ 						 const char *type, const char *target,
+ 						 const char *nspname, const char *owner);
+ 
  static void getDependencies(void);
  static void getDomainConstraints(TypeInfo *tyinfo);
  static void getTableData(TableInfo *tblinfo, int numTables, bool oids);
***************
*** 300,305 **** main(int argc, char **argv)
--- 306,312 ----
  		{"quote-all-identifiers", no_argument, &quote_all_identifiers, 1},
  		{"role", required_argument, NULL, 3},
  		{"use-set-session-authorization", no_argument, &use_setsessauth, 1},
+ 		{"security-label", no_argument, &security_label, 1},
  
  		{NULL, 0, NULL, 0}
  	};
***************
*** 448,453 **** main(int argc, char **argv)
--- 455,462 ----
  					outputNoTablespaces = 1;
  				else if (strcmp(optarg, "use-set-session-authorization") == 0)
  					use_setsessauth = 1;
+ 				else if (strcmp(optarg, "security-label") == 0)
+ 					security_label = 1;
  				else
  				{
  					fprintf(stderr,
***************
*** 643,648 **** main(int argc, char **argv)
--- 652,666 ----
  		do_sql_command(g_conn, "SET quote_all_identifiers = true");
  
  	/*
+ 	 * Disables security label support if server version < v9.1.x
+ 	 */
+ 	if (security_label && g_fout->remoteVersion < 90100)
+ 	{
+ 		write_msg(NULL, "Server does not support security labels\n");
+ 		security_label = 0;
+ 	}
+ 
+ 	/*
  	 * Start serializable transaction to dump consistent data.
  	 */
  	do_sql_command(g_conn, "BEGIN");
***************
*** 839,844 **** help(const char *progname)
--- 857,863 ----
  	printf(_("  --no-tablespaces            do not dump tablespace assignments\n"));
  	printf(_("  --quote-all-identifiers     quote all identifiers, even if not keywords\n"));
  	printf(_("  --role=ROLENAME             do SET ROLE before dump\n"));
+ 	printf(_("  --security-label            also dump security labels\n"));
  	printf(_("  --use-set-session-authorization\n"
  			 "                              use SET SESSION AUTHORIZATION commands instead of\n"
  	"                              ALTER OWNER commands to set ownership\n"));
***************
*** 10435,10440 **** dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
--- 10454,10564 ----
  }
  
  /*
+  * dumpSecLabel 
+  *
+  * It dumps any security labels associated with the object handed
+  * to this routine. The routine takes a constant character string
+  * for the target part of the security-label command, plus the
+  * namespace and owner of the object (for labeling the ArchiveEntry),
+  * plus catalogId which is lookup key for pg_seclabel, and dumpId
+  * for the object.
+  * If a matching pg_seclabel entry is found, it is dumped.
+  */
+ static void
+ dumpSecLabel(Archive *fout, CatalogId objCatId, DumpId objDumpId,
+ 			 const char *type, const char *target,
+ 			 const char *nspname, const char *owner)
+ {
+ 	PGresult	   *res;
+ 	PQExpBuffer		lquery;
+ 	PQExpBuffer		cquery;
+ 	PQExpBuffer		tgbuf;
+ 	int				i_attname;
+ 	int				i_tag;
+ 	int				i_label;
+ 	int				i, ntups;
+ 
+ 	/* do nothing, if security label dump is not enabled */
+ 	if (!security_label)
+ 		return;
+ 
+ 	/* --data-only skips security labels *except* large object */
+ 	if (dataOnly && strcmp(type, "LARGE OBJECT") != 0)
+ 		return;
+ 
+ 	/*
+ 	 * Fetch security labels associated with the object
+ 	 */
+ 	lquery = createPQExpBuffer();
+ 	cquery = createPQExpBuffer();
+ 	tgbuf = createPQExpBuffer();
+ 
+ 	appendPQExpBuffer(lquery,
+ 					  "SELECT a.attname, s.tag, s.label"
+ 					  "  FROM pg_catalog.pg_seclabel s"
+ 					  "       LEFT OUTER JOIN"
+ 					  "       pg_catalog.pg_attribute a"
+ 					  "       ON s.objoid = a.attrelid AND"
+ 					  "          s.subid = a.attnum"
+ 					  "  WHERE reloid = %u AND objoid = %u",
+ 					  objCatId.tableoid, objCatId.oid);
+ 
+ 	res = PQexec(g_conn, lquery->data);
+ 	check_sql_result(res, g_conn, lquery->data, PGRES_TUPLES_OK);
+ 
+ 	i_attname	= PQfnumber(res, "attname");
+ 	i_tag		= PQfnumber(res, "tag");
+ 	i_label		= PQfnumber(res, "label");
+ 
+ 	ntups = PQntuples(res);
+ 
+ 	for (i = 0; i < ntups; i++)
+ 	{
+ 		if (strcmp(type, "TABLE") == 0 &&
+ 			!PQgetisnull(res, i, i_attname))
+ 		{
+ 			appendPQExpBuffer(cquery,
+ 							  "SECURITY LABEL FOR '%s'"
+ 							  " ON COLUMN %s.%s IS '%s';\n",
+ 							  PQgetvalue(res, i, i_tag),
+ 							  target,
+ 							  fmtId(PQgetvalue(res, i, i_attname)),
+ 							  PQgetvalue(res, i, i_label));
+ 			continue;
+ 		}
+ 
+ 		/*
+ 		 * a.attname can be available only when type = "TABLE",
+ 		 * so we expect it will be always NULL. Elsewhere, we assume
+ 		 * it as a data corruption, so simply ignored.
+ 		 */
+ 		if (!PQgetisnull(res, i, i_attname))
+ 			continue;
+ 
+ 		appendPQExpBuffer(cquery,
+ 						  "SECURITY LABEL FOR '%s' ON %s %s IS '%s';\n",
+ 						  PQgetvalue(res, i, i_tag),
+ 						  type, target,
+ 						  PQgetvalue(res, i, i_label));
+ 	}
+ 	PQclear(res);
+ 
+ 	if (cquery->len > 0)
+ 	{
+ 		appendPQExpBuffer(tgbuf, "%s %s", type, target);
+ 		ArchiveEntry(fout, nilCatalogId, createDumpId(),
+ 					 tgbuf->data, nspname, NULL, owner,
+ 					 false, "LABEL", SECTION_NONE,
+ 					 cquery->data, "", NULL,
+ 					 &(objDumpId), 1,
+ 					 NULL, NULL);
+ 	}
+ 	destroyPQExpBuffer(lquery);
+ 	destroyPQExpBuffer(cquery);
+ 	destroyPQExpBuffer(tgbuf);
+ }
+ 
+ /*
   * dumpTable
   *	  write out to fout the declarations (not data) of a user-defined table
   */
***************
*** 10458,10463 **** dumpTable(Archive *fout, TableInfo *tbinfo)
--- 10582,10594 ----
  				tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
  				tbinfo->relacl);
  
+ 		/* Handle the security label here */
+ 		dumpSecLabel(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
+ 					 (tbinfo->relkind == RELKIND_SEQUENCE ? "SEQUENCE" :
+ 					  (tbinfo->relkind == RELKIND_VIEW ? "VIEW" : "TABLE")),
+ 					 namecopy,
+ 					 tbinfo->dobj.namespace->dobj.name,
+ 					 tbinfo->rolname);
  		/*
  		 * Handle column ACLs, if any.	Note: we pull these with a separate
  		 * query rather than trying to fetch them during getTableAttrs, so
*** a/src/bin/pg_dump/pg_dumpall.c
--- b/src/bin/pg_dump/pg_dumpall.c
***************
*** 69,74 **** static int	disable_triggers = 0;
--- 69,75 ----
  static int	inserts = 0;
  static int	no_tablespaces = 0;
  static int	use_setsessauth = 0;
+ static int	security_label = 0;
  static int	server_version;
  
  static FILE *OPF;
***************
*** 133,138 **** main(int argc, char *argv[])
--- 134,140 ----
  		{"quote-all-identifiers", no_argument, &quote_all_identifiers, 1},
  		{"role", required_argument, NULL, 3},
  		{"use-set-session-authorization", no_argument, &use_setsessauth, 1},
+ 		{"security-label", no_argument, &security_label, 1},
  
  		{NULL, 0, NULL, 0}
  	};
***************
*** 286,291 **** main(int argc, char *argv[])
--- 288,295 ----
  					no_tablespaces = 1;
  				else if (strcmp(optarg, "use-set-session-authorization") == 0)
  					use_setsessauth = 1;
+ 				else if (strcmp(optarg, "security-label") == 0)
+ 					security_label = 1;
  				else
  				{
  					fprintf(stderr,
***************
*** 371,376 **** main(int argc, char *argv[])
--- 375,382 ----
  		appendPQExpBuffer(pgdumpopts, " --quote-all-identifiers");
  	if (use_setsessauth)
  		appendPQExpBuffer(pgdumpopts, " --use-set-session-authorization");
+ 	if (security_label)
+ 		appendPQExpBuffer(pgdumpopts, " --security-label");
  
  	/*
  	 * If there was a database specified on the command line, use that,
***************
*** 567,572 **** help(void)
--- 573,579 ----
  	printf(_("  --no-tablespaces            do not dump tablespace assignments\n"));
  	printf(_("  --quote-all-identifiers     quote all identifiers, even if not keywords\n"));
  	printf(_("  --role=ROLENAME             do SET ROLE before dump\n"));
+ 	printf(_("  --security-label            also dump security labels\n"));
  	printf(_("  --use-set-session-authorization\n"
  			 "                              use SET SESSION AUTHORIZATION commands instead of\n"
  	"                              ALTER OWNER commands to set ownership\n"));
*** a/src/bin/pg_dump/pg_restore.c
--- b/src/bin/pg_dump/pg_restore.c
***************
*** 76,81 **** main(int argc, char **argv)
--- 76,82 ----
  	static int	no_data_for_failed_tables = 0;
  	static int	outputNoTablespaces = 0;
  	static int	use_setsessauth = 0;
+ 	static int	skip_seclabel = 0;
  
  	struct option cmdopts[] = {
  		{"clean", 0, NULL, 'c'},
***************
*** 116,121 **** main(int argc, char **argv)
--- 117,123 ----
  		{"no-tablespaces", no_argument, &outputNoTablespaces, 1},
  		{"role", required_argument, NULL, 2},
  		{"use-set-session-authorization", no_argument, &use_setsessauth, 1},
+ 		{"no-security-label", no_argument, &skip_seclabel, 1},
  
  		{NULL, 0, NULL, 0}
  	};
***************
*** 262,267 **** main(int argc, char **argv)
--- 264,271 ----
  					outputNoTablespaces = 1;
  				else if (strcmp(optarg, "use-set-session-authorization") == 0)
  					use_setsessauth = 1;
+ 				else if (strcmp(optarg, "no-security-label") == 0)
+ 					skip_seclabel = 1;
  				else
  				{
  					fprintf(stderr,
***************
*** 337,342 **** main(int argc, char **argv)
--- 341,347 ----
  	opts->noDataForFailedTables = no_data_for_failed_tables;
  	opts->noTablespace = outputNoTablespaces;
  	opts->use_setsessauth = use_setsessauth;
+ 	opts->skip_seclabel = skip_seclabel;
  
  	if (opts->formatName)
  	{
***************
*** 442,447 **** usage(const char *progname)
--- 447,453 ----
  			 "                           do not restore data of tables that could not be\n"
  			 "                           created\n"));
  	printf(_("  --no-tablespaces         do not restore tablespace assignments\n"));
+ 	printf(_("  --no-security-label      do not restore security labels\n"));
  	printf(_("  --role=ROLENAME          do SET ROLE before restore\n"));
  	printf(_("  --use-set-session-authorization\n"
  			 "                           use SET SESSION AUTHORIZATION commands instead of\n"
*** a/src/include/catalog/indexing.h
--- b/src/include/catalog/indexing.h
***************
*** 281,286 **** DECLARE_UNIQUE_INDEX(pg_default_acl_oid_index, 828, on pg_default_acl using btre
--- 281,289 ----
  DECLARE_UNIQUE_INDEX(pg_db_role_setting_databaseid_rol_index, 2965, on pg_db_role_setting using btree(setdatabase oid_ops, setrole oid_ops));
  #define DbRoleSettingDatidRolidIndexId	2965
  
+ DECLARE_UNIQUE_INDEX(pg_seclabel_object_index, 3038, on pg_seclabel using btree(reloid oid_ops, objoid oid_ops, subid int4_ops, tag text_ops));
+ #define SecLabelObjectIndexId				3038
+ 
  /* last step of initialization script: build the indexes declared above */
  BUILD_INDICES
  
*** /dev/null
--- b/src/include/catalog/pg_seclabel.h
***************
*** 0 ****
--- 1,43 ----
+ /* -------------------------------------------------------------------------
+  *
+  * pg_seclabel.h
+  *    definition of the system "security label" relation (pg_seclabel)
+  *
+  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * -------------------------------------------------------------------------
+  */
+ #ifndef PG_SECLABEL_H
+ #define PG_SECLABEL_H
+ 
+ #include "catalog/genbki.h"
+ 
+ /* ----------------
+  *		pg_seclabel definition.  cpp turns this into
+  *		typedef struct FormData_pg_seclabel
+  * ----------------
+  */
+ #define SecLabelRelationId		3037
+ 
+ CATALOG(pg_seclabel,3037) BKI_WITHOUT_OIDS
+ {
+ 	Oid			reloid;		/* OID of table containing the object */
+ 	Oid			objoid;		/* OID of the object itself */
+ 	int4		subid;		/* column number, or 0 if not used */
+ 	text		tag;		/* identifier of external security provider */
+ 	text		label;		/* security label of the object */
+ } FormData_pg_seclabel;
+ 
+ /* ----------------
+  *		compiler constants for pg_seclabel
+  * ----------------
+  */
+ #define Natts_pg_seclabel			5
+ #define Anum_pg_seclabel_reloid		1
+ #define Anum_pg_seclabel_objoid		2
+ #define Anum_pg_seclabel_subid		3
+ #define Anum_pg_seclabel_tag		4
+ #define Anum_pg_seclabel_label		5
+ 
+ #endif	/* PG_SECLABEL_H */
*** a/src/include/catalog/toasting.h
--- b/src/include/catalog/toasting.h
***************
*** 45,50 **** DECLARE_TOAST(pg_constraint, 2832, 2833);
--- 45,51 ----
  DECLARE_TOAST(pg_description, 2834, 2835);
  DECLARE_TOAST(pg_proc, 2836, 2837);
  DECLARE_TOAST(pg_rewrite, 2838, 2839);
+ DECLARE_TOAST(pg_seclabel, 3039, 3040);
  DECLARE_TOAST(pg_statistic, 2840, 2841);
  DECLARE_TOAST(pg_trigger, 2336, 2337);
  
*** /dev/null
--- b/src/include/commands/seclabel.h
***************
*** 0 ****
--- 1,36 ----
+ /*
+  * seclabel.h
+  *
+  * Prototypes for functions in commands/seclabel.c
+  *
+  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  */
+ #ifndef SECLABEL_H
+ #define SECLABEL_H
+ 
+ #include "catalog/objectaddress.h"
+ #include "nodes/primnodes.h"
+ #include "nodes/parsenodes.h"
+ 
+ /*
+  * Internal APIs
+  */
+ extern char *GetSecurityLabel(const ObjectAddress *object,
+ 							  const char *tag);
+ extern void  SetSecurityLabel(const ObjectAddress *object,
+ 							  const char *tag,
+ 							  const char *seclabel);
+ extern void  DeleteSecurityLabel(const ObjectAddress *object);
+ 
+ /*
+  * Statement and ESP hook support
+  */
+ extern void ExecSecLabelStmt(SecLabelStmt *stmt);
+ 
+ typedef void (*check_object_relabel_type)(const ObjectAddress *object,
+ 										  const char *seclabel);
+ extern void register_object_relabel_hook(const char *tag,
+ 										 check_object_relabel_type hook);
+ 
+ #endif	/* SECLABEL_H */
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
***************
*** 347,352 **** typedef enum NodeTag
--- 347,353 ----
  	T_AlterUserMappingStmt,
  	T_DropUserMappingStmt,
  	T_AlterTableSpaceOptionsStmt,
+ 	T_SecLabelStmt,
  
  	/*
  	 * TAGS FOR PARSE TREE NODES (parsenodes.h)
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 1850,1855 **** typedef struct CommentStmt
--- 1850,1869 ----
  } CommentStmt;
  
  /* ----------------------
+  *				SECURITY LABEL Statemetn
+  * ----------------------
+  */
+ typedef struct SecLabelStmt
+ {
+ 	NodeTag		type;
+ 	ObjectType	objtype;		/* Object's type */
+ 	List	   *objname;		/* Qualified name of the object */
+ 	List	   *objargs;		/* Arguments if needed (eg, for functions) */
+ 	char	   *tag;			/* Identifier of ESP, if specified */
+ 	char	   *seclabel;		/* New security label to be assigned */
+ } SecLabelStmt;
+ 
+ /* ----------------------
   *		Declare Cursor Statement
   *
   * Note: the "query" field of DeclareCursorStmt is only used in the raw grammar
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
***************
*** 208,213 **** PG_KEYWORD("isnull", ISNULL, TYPE_FUNC_NAME_KEYWORD)
--- 208,214 ----
  PG_KEYWORD("isolation", ISOLATION, UNRESERVED_KEYWORD)
  PG_KEYWORD("join", JOIN, TYPE_FUNC_NAME_KEYWORD)
  PG_KEYWORD("key", KEY, UNRESERVED_KEYWORD)
+ PG_KEYWORD("label", LABEL, UNRESERVED_KEYWORD)
  PG_KEYWORD("language", LANGUAGE, UNRESERVED_KEYWORD)
  PG_KEYWORD("large", LARGE_P, UNRESERVED_KEYWORD)
  PG_KEYWORD("last", LAST_P, UNRESERVED_KEYWORD)
*** a/src/test/regress/expected/sanity_check.out
--- b/src/test/regress/expected/sanity_check.out
***************
*** 114,119 **** SELECT relname, relhasindex
--- 114,120 ----
   pg_pltemplate           | t
   pg_proc                 | t
   pg_rewrite              | t
+  pg_seclabel             | t
   pg_shdepend             | t
   pg_shdescription        | t
   pg_statistic            | t
***************
*** 153,159 **** SELECT relname, relhasindex
   timetz_tbl              | f
   tinterval_tbl           | f
   varchar_tbl             | f
! (142 rows)
  
  --
  -- another sanity check: every system catalog that has OIDs should have
--- 154,160 ----
   timetz_tbl              | f
   tinterval_tbl           | f
   varchar_tbl             | f
! (143 rows)
  
  --
  -- another sanity check: every system catalog that has OIDs should have
