pgsql-v9.1-security-label-2.v2.patch
application/octect-stream
Filename: pgsql-v9.1-security-label-2.v2.patch
Type: application/octect-stream
Part: 0
Message:
Re: security label support, part.2
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v9
| File | + | − |
|---|---|---|
| doc/src/sgml/ref/allfiles.sgml | 1 | 0 |
| doc/src/sgml/reference.sgml | 1 | 0 |
| doc/src/sgml/ref/seclabel.sgml | 123 | 0 |
| src/backend/commands/seclabel.c | 254 | 0 |
| src/backend/nodes/copyfuncs.c | 17 | 0 |
| src/backend/nodes/equalfuncs.c | 15 | 0 |
| src/backend/parser/gram.y | 61 | 2 |
| src/backend/tcop/utility.c | 13 | 0 |
| src/include/commands/seclabel.h | 13 | 1 |
| src/include/nodes/nodes.h | 1 | 0 |
| src/include/nodes/parsenodes.h | 14 | 0 |
| src/include/parser/kwlist.h | 1 | 0 |
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index 7518c84..6f46356 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -132,6 +132,7 @@ Complete list of usable sgml source files in this directory.
<!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">
diff --git a/doc/src/sgml/ref/seclabel.sgml b/doc/src/sgml/ref/seclabel.sgml
new file mode 100644
index 0000000..9215255
--- /dev/null
+++ b/doc/src/sgml/ref/seclabel.sgml
@@ -0,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>
+
+
+
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index c33d883..1448f47 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -160,6 +160,7 @@
&rollbackPrepared;
&rollbackTo;
&savepoint;
+ &securityLabel;
&select;
&selectInto;
&set;
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index 5f914fa..0947376 100644
--- a/src/backend/commands/seclabel.c
+++ b/src/backend/commands/seclabel.c
@@ -15,10 +15,18 @@
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_seclabel.h"
#include "commands/seclabel.h"
+#include "miscadmin.h"
+#include "parser/parse_clause.h"
+#include "storage/lmgr.h"
+#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/tqual.h"
@@ -269,3 +277,249 @@ DeleteSecurityLabel(const ObjectAddress *object)
DeleteLocalSecLabel(object);
}
+
+/*
+ * 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;
+
+ if (!process_shared_preload_libraries_in_progress)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("unavailable to register security hooks now")));
+
+ 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);
+}
+
+/*
+ * ExecRelationSecLabel
+ *
+ * It relabels relabel/attribute
+ *
+ */
+static void
+ExecRelationSecLabel(ObjectType objtype, List *nameList,
+ const char *seclabel,
+ esp_relabel_hook_entry *esp_entry)
+{
+ List *relname = nameList;
+ char *attname = NULL;
+ RangeVar *rel;
+ Oid relOid;
+ List *child_oids;
+ List *child_numparents;
+ ListCell *lo, *li;
+
+ if (objtype == OBJECT_COLUMN)
+ {
+ int nnames = list_length(nameList);
+
+ if (nnames < 2)
+ elog(ERROR, "must specify relation and attribute");
+
+ attname = strVal(lfirst(list_tail(nameList)));
+ relname = list_truncate(list_copy(nameList), nnames - 1);
+ }
+
+ /*
+ * Resolve the specified relation
+ */
+ rel = makeRangeVarFromNameList(relname);
+ relOid = RangeVarGetRelid(rel, false);
+
+ /*
+ * Build a list of inherited relations, if exists.
+ * Note that 'child_oids' contains relOid.
+ */
+ child_oids = find_all_inheritors(relOid,
+ AccessExclusiveLock,
+ &child_numparents);
+
+ /*
+ * Relabel each tables being listed
+ */
+ forboth (lo, child_oids, li, child_numparents)
+ {
+ ObjectAddress object;
+ Oid tableOid = lfirst_oid(lo);
+ Oid namespaceOid = get_rel_namespace(tableOid);
+ int expected_parents = lfirst_int(li);
+ AttrNumber attnum = InvalidAttrNumber;
+
+ switch (objtype)
+ {
+ case OBJECT_TABLE:
+ Assert(attname == NULL);
+ if (get_rel_relkind(tableOid) != RELKIND_RELATION)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a table",
+ get_rel_name(tableOid))));
+ break;
+
+ case OBJECT_SEQUENCE:
+ Assert(attname == NULL);
+ if (get_rel_relkind(tableOid) != RELKIND_SEQUENCE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a sequence",
+ get_rel_name(tableOid))));
+ break;
+
+ case OBJECT_VIEW:
+ Assert(attname == NULL);
+ if (get_rel_relkind(tableOid) != RELKIND_VIEW)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a view",
+ get_rel_name(tableOid))));
+ break;
+
+ case OBJECT_COLUMN:
+ Assert(attname != NULL);
+ if (get_rel_relkind(tableOid) != RELKIND_RELATION)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a table",
+ get_rel_name(tableOid))));
+ break;
+
+ default:
+ elog(ERROR, "unexpected object type %d", (int)objtype);
+ break;
+ }
+
+ /*
+ * Resolve the column name, if necessary
+ */
+ if (attname)
+ {
+ attnum = get_attnum(tableOid, attname);
+ if (attnum == InvalidAttrNumber)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("column \"%s\" of relation \"%s\" does not exist",
+ attname, get_rel_name(tableOid))));
+ }
+
+ /*
+ * Prevent relabeling system catalogs
+ */
+ if (!allowSystemTableMods &&
+ (IsSystemNamespace(namespaceOid) || IsToastNamespace(namespaceOid)))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied: \"%s\" is a system catalog",
+ get_rel_name(tableOid))));
+ /*
+ * Permission checks
+ */
+ if (!pg_class_ownercheck(tableOid, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+ get_rel_name(tableOid));
+
+ object.classId = RelationRelationId;
+ object.objectId = tableOid;
+ object.objectSubId = attnum;
+
+ (*esp_entry->hook)(&object, seclabel, expected_parents);
+
+ /*
+ * Do actual relabeling
+ */
+ SetSecurityLabel(&object, esp_entry->tag, seclabel);
+ }
+}
+
+/*
+ * ExecSecLabelStmt
+ *
+ * SECURITY LABEL [FOR <esp>] ON <class> <name> IS <new label>
+ *
+ */
+void
+ExecSecLabelStmt(SecLabelStmt *stmt)
+{
+ esp_relabel_hook_entry *esp_entry = NULL;
+ ListCell *l;
+
+ /*
+ * SECURITY LABEL statement needs one or more label based security
+ * features are 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 handle
+ * 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:
+
+ switch (stmt->objtype)
+ {
+ case OBJECT_TABLE:
+ case OBJECT_COLUMN:
+ case OBJECT_SEQUENCE:
+ case OBJECT_VIEW:
+ ExecRelationSecLabel(stmt->objtype,
+ stmt->objname,
+ stmt->seclabel,
+ esp_entry);
+ break;
+
+ default:
+ elog(ERROR, "unrecognized object type: %d", (int)stmt->objtype);
+ break;
+ }
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 9af1217..92d786a 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2605,6 +2605,20 @@ _copyCommentStmt(CommentStmt *from)
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)
{
@@ -3955,6 +3969,9 @@ copyObject(void *from)
case T_CommentStmt:
retval = _copyCommentStmt(from);
break;
+ case T_SecLabelStmt:
+ retval = _copySecLabelStmt(from);
+ break;
case T_FetchStmt:
retval = _copyFetchStmt(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 70b3c62..1b84a55 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1163,6 +1163,18 @@ _equalCommentStmt(CommentStmt *a, CommentStmt *b)
}
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);
@@ -2622,6 +2634,9 @@ equal(void *a, void *b)
case T_CommentStmt:
retval = _equalCommentStmt(a, b);
break;
+ case T_SecLabelStmt:
+ retval = _equalSecLabelStmt(a, b);
+ break;
case T_FetchStmt:
retval = _equalFetchStmt(a, b);
break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 1722036..0a92027 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -204,7 +204,7 @@ static TypeName *TableFuncTypeName(List *columns);
CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt
RemoveFuncStmt RemoveOperStmt RenameStmt RevokeStmt RevokeRoleStmt
RuleActionStmt RuleActionStmtOrEmpty RuleStmt
- SelectStmt TransactionStmt TruncateStmt
+ SecLabelStmt SelectStmt TransactionStmt TruncateStmt
UnlistenStmt UpdateStmt VacuumStmt
VariableResetStmt VariableSetStmt VariableShowStmt
ViewStmt CheckPointStmt CreateConversionStmt
@@ -422,6 +422,8 @@ static TypeName *TableFuncTypeName(List *columns);
%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
@@ -498,7 +500,7 @@ static TypeName *TableFuncTypeName(List *columns);
KEY
- LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING
+ 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
@@ -736,6 +738,7 @@ stmt :
| RevokeStmt
| RevokeRoleStmt
| RuleStmt
+ | SecLabelStmt
| SelectStmt
| TransactionStmt
| TruncateStmt
@@ -4324,6 +4327,61 @@ comment_text:
| 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:
@@ -10954,6 +11012,7 @@ unreserved_keyword:
| INVOKER
| ISOLATION
| KEY
+ | LABEL
| LANGUAGE
| LARGE_P
| LAST_P
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 1815539..2d3324d 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -218,6 +218,7 @@ check_xact_readonly(Node *parsetree)
case T_AlterUserMappingStmt:
case T_DropUserMappingStmt:
case T_AlterTableSpaceOptionsStmt:
+ case T_SecLabelStmt:
PreventCommandIfReadOnly(CreateCommandTag(parsetree));
break;
default:
@@ -662,6 +663,10 @@ standard_ProcessUtility(Node *parsetree,
CommentObject((CommentStmt *) parsetree);
break;
+ case T_SecLabelStmt:
+ ExecSecLabelStmt((SecLabelStmt *) parsetree);
+ break;
+
case T_CopyStmt:
{
uint64 processed;
@@ -1591,6 +1596,10 @@ CreateCommandTag(Node *parsetree)
tag = "COMMENT";
break;
+ case T_SecLabelStmt:
+ tag = "SECURITY LABEL";
+ break;
+
case T_CopyStmt:
tag = "COPY";
break;
@@ -2313,6 +2322,10 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_DDL;
break;
+ case T_SecLabelStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_CopyStmt:
if (((CopyStmt *) parsetree)->is_from)
lev = LOGSTMT_MOD;
diff --git a/src/include/commands/seclabel.h b/src/include/commands/seclabel.h
index b69fc07..088180f 100644
--- a/src/include/commands/seclabel.h
+++ b/src/include/commands/seclabel.h
@@ -10,6 +10,8 @@
#define SECLABEL_H
#include "catalog/dependency.h"
+#include "nodes/primnodes.h"
+#include "nodes/parsenodes.h"
/*
* Internal APIs
@@ -21,5 +23,15 @@ extern void SetSecurityLabel(const ObjectAddress *object,
const char *seclabel);
extern void DeleteSecurityLabel(const ObjectAddress *object);
-#endif /* SECLABEL_H */
+/*
+ * Statement and ESP hook support
+ */
+extern void ExecSecLabelStmt(SecLabelStmt *stmt);
+typedef void (*check_object_relabel_type)(const ObjectAddress *object,
+ const char *seclabel,
+ int expected_parents);
+extern void register_object_relabel_hook(const char *tag,
+ check_object_relabel_type hook);
+
+#endif /* SECLABEL_H */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index a5f5df5..bad1520 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -347,6 +347,7 @@ typedef enum NodeTag
T_AlterUserMappingStmt,
T_DropUserMappingStmt,
T_AlterTableSpaceOptionsStmt,
+ T_SecLabelStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index fec8d3c..2cf6798 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1846,6 +1846,20 @@ typedef struct CommentStmt
} 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
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 49d4b6c..13c88a1 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -208,6 +208,7 @@ PG_KEYWORD("isnull", ISNULL, TYPE_FUNC_NAME_KEYWORD)
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)