I revised my patch as I attached.
The hook function is modified and consolidated as follows:
typedef enum FunctionCallEventType
{
FCET_BE_HOOKED,
FCET_PREPARE,
FCET_START,
FCET_END,
FCET_ABORT,
} FunctionCallEventType;
typedef Datum (*function_call_event_type)(Oid functionId,
FunctionCallEventType event,
Datum event_arg);
extern PGDLLIMPORT function_call_event_type function_call_event_hook;
Unlike the subject of this e-mail, now it does not focus on only switching
security labels during execution of a certain functions.
For example, we may use this hook to track certain functions for security
auditing, performance tuning, and others.
In the case of SE-PgSQL, it shall return BoolGetDatum(true), if the target
function is configured as a trusted procedure, then, this invocation will
be hooked by fmgr_security_definer. In the first call, it shall compute
the security context to be assigned during execution on FCET_PREPARE event.
Then, it switches to the computed label on the FCET_START event, and
restore it on the FCET_END or ECET_ABORT event.
I also fixed up regression test, dummy_seclabel module and its
documentation as Robert pointed out in another topic.
Thanks,
(2010/11/14 13:16), KaiGai Kohei wrote:
> (2010/11/14 11:19), Robert Haas wrote:
>> 2010/11/12 KaiGai Kohei<[email protected]>:
>>> The attached patch allows the security label provider to switch
>>> security label of the client during execution of certain functions.
>>> I named it as "label switcher function"; also called as "trusted-
>>> procedure" in SELinux community.
>>>
>>> This feature is quite similar idea toward security definer function,
>>> or set-uid program on operating system. It allows label providers
>>> to switch its internal state that holds security label of the
>>> client, then restore it.
>>> If and when a label provider said the function being invoked is
>>> a label-switcher, fmgr_security_definer() traps this invocation
>>> and set some states just before actual invocations.
>>>
>>> We added three new hooks for security label provider.
>>> The get_client_label and set_client_label allows the PG core to
>>> save and restore security label of the client; which is mostly
>>> just an internal state of plugin module.
>>> And, the get_switched_label shall return NULL or a valid label
>>> if the supplied function is a label switcher. It also informs
>>> the PG core whether the function is switcher or not.
>>
>> I don't see why the plugin needs to expose the label stack to core PG.
>> If the plugin needs a label stack, it can do that all on its own. I
>> see that we need the hooks to allow the plugin to selectively disable
>> inlining and to gain control when function execution starts and ends
>> (or aborts) but I don't think the exact manipulations that the plugin
>> chooses to do at that point need to be visible to core PG.
>>
> Hmm. I designed this patch according to the implementation of existing
> security definer function, but it is not a only design.
>
> Does the "label stack" means that this patch touches xact.c, doesn't it?
> Yes, if we have above three hooks around function calls, the core PG
> does not need to manage a label stack.
>
> However, I want fmgr_security_definer_cache to have a field to save
> private opaque data, because it is not a very-light step to ask SE-Linux
> whether the function is trusted-procedure and to allocate a string to
> be applied during execution, although switching is a very-light step.
> So, I want to compute it at first time of the function calls, like as
> security definer function checks syscache at once.
>
> Of course, it is a private opaque data, it will be open for other usage.
>
>> For SE-Linux, how do you intend to determine whether or not the
>> function is a trusted procedure? Will that be a function of the
>> security label applied to it?
>>
> When the function being invoked has a special security label with
> a "type_transition" rule on the current client's label in the
> security policy, SE-Linux decides the function is trusted procedure.
>
> In other words, we can know whether or not the function is a trusted
> procedure by asking to the security policy. It is a task of the plugin.
>
> Thanks,
--
KaiGai Kohei <[email protected]>
diff --git a/contrib/dummy_seclabel/dummy_seclabel.c b/contrib/dummy_seclabel/dummy_seclabel.c
index 8bd50a3..557cc0c 100644
--- a/contrib/dummy_seclabel/dummy_seclabel.c
+++ b/contrib/dummy_seclabel/dummy_seclabel.c
@@ -12,14 +12,156 @@
*/
#include "postgres.h"
+#include "catalog/pg_proc.h"
#include "commands/seclabel.h"
#include "miscadmin.h"
PG_MODULE_MAGIC;
+PG_FUNCTION_INFO_V1(dummy_client_label);
+
+Datum dummay_client_label(PG_FUNCTION_ARGS);
+
/* Entrypoint of the module */
void _PG_init(void);
+static const char *client_label = "unclassified";
+
+static function_call_event_type function_call_event_next = NULL;
+
+typedef struct {
+ Datum self;
+ Datum next;
+} private_stack;
+
+static Datum
+dummy_function_call(Oid functionId,
+ FunctionCallEventType event,
+ Datum event_arg)
+{
+ Datum result = 0;
+ char *label;
+ ObjectAddress object = { .classId = ProcedureRelationId,
+ .objectId = functionId,
+ .objectSubId = 0 };
+ switch (event)
+ {
+ case FCET_BE_HOOKED:
+ /*
+ * If the target function is labeled as "trusted",
+ * the dummy tries to hook invocation of the function.
+ */
+ result = BoolGetDatum(false);
+
+ if (function_call_event_next)
+ {
+ result = (*function_call_event_next)(functionId,
+ event,
+ event_arg);
+ if (DatumGetBool(result))
+ break; /* no need to check anymore */
+ }
+ label = GetSecurityLabel(&object, "dummy");
+ if (label && strcmp(label, "trusted") == 0)
+ result = BoolGetDatum(true);
+ break;
+
+ case FCET_PREPARE:
+ /*
+ * It computes an alternative label during execution
+ * of the trusted procedure. This computation is not
+ * necessary to repeat twice or more, so we save it
+ * on the private opaque data.
+ */
+ if (function_call_event_next)
+ {
+ FmgrInfo *flinfo = (FmgrInfo *)(event_arg);
+ private_stack *out
+ = MemoryContextAlloc(flinfo->fn_mcxt, sizeof(*out));
+
+ out->next = (*function_call_event_next)(functionId,
+ event,
+ event_arg);
+ /*
+ * XXX - we already checked the function being labeled
+ * as "trusted"
+ */
+ if (!superuser())
+ out->self = PointerGetDatum("secret");
+ else
+ out->self = PointerGetDatum("top secret");
+
+ result = PointerGetDatum(out);
+ }
+ else
+ {
+ if (!superuser())
+ result = PointerGetDatum("secret");
+ else
+ result = PointerGetDatum("top secret");
+ }
+ break;
+
+ case FCET_START:
+ /*
+ * Switch security label of the client
+ */
+ if (function_call_event_next)
+ {
+ private_stack *in = (private_stack *)(event_arg);
+ private_stack *out = palloc(sizeof(*out));
+
+ out->next = (*function_call_event_next)(functionId,
+ event,
+ in->next);
+ out->self = PointerGetDatum(client_label);
+ client_label = DatumGetPointer(in->self);
+
+ result = PointerGetDatum(out);
+ }
+ else
+ {
+ result = PointerGetDatum(client_label);
+ client_label = DatumGetPointer(event_arg);
+ }
+ break;
+
+ case FCET_END:
+ case FCET_ABORT:
+ /*
+ * Restore security label of the client
+ */
+ if (function_call_event_next)
+ {
+ private_stack *in = (private_stack *)(event_arg);
+
+ (void)(*function_call_event_next)(functionId,
+ event,
+ in->next);
+ client_label = DatumGetPointer(in->self);
+ }
+ else
+ {
+ client_label = DatumGetPointer(event_arg);
+ }
+ break;
+
+ default:
+ elog(ERROR, "unexpected event type: %d", (int)event);
+ break;
+ }
+ return result;
+}
+
+Datum
+dummy_client_label(PG_FUNCTION_ARGS)
+{
+ if (!client_label)
+ PG_RETURN_NULL();
+
+ PG_RETURN_TEXT_P(cstring_to_text(client_label));
+}
+
static void
dummy_object_relabel(const ObjectAddress *object, const char *seclabel)
{
@@ -28,7 +170,8 @@ dummy_object_relabel(const ObjectAddress *object, const char *seclabel)
strcmp(seclabel, "classified") == 0)
return;
- if (strcmp(seclabel, "secret") == 0 ||
+ if (strcmp(seclabel, "trusted") == 0 ||
+ strcmp(seclabel, "secret") == 0 ||
strcmp(seclabel, "top secret") == 0)
{
if (!superuser())
@@ -46,4 +189,8 @@ void
_PG_init(void)
{
register_label_provider("dummy", dummy_object_relabel);
+
+ /* trusted procedure test */
+ function_call_event_next = function_call_event_hook;
+ function_call_event_hook = dummy_function_call;
}
diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml
index c310416..1d424a9 100644
--- a/doc/src/sgml/contrib.sgml
+++ b/doc/src/sgml/contrib.sgml
@@ -90,6 +90,7 @@ psql -d dbname -f <replaceable>SHAREDIR</>/contrib/<replaceable>module</>.sql
&dblink;
&dict-int;
&dict-xsyn;
+ &dummy_seclabel;
&earthdistance;
&fuzzystrmatch;
&hstore;
diff --git a/doc/src/sgml/dummy_seclabel.sgml b/doc/src/sgml/dummy_seclabel.sgml
new file mode 100644
index 0000000..1d65ff1
--- /dev/null
+++ b/doc/src/sgml/dummy_seclabel.sgml
@@ -0,0 +1,94 @@
+<!-- doc/src/sgml/dummy_seclabel.sgml -->
+
+<sect1 id="dummy_seclabel">
+ <title>dummy_seclabel</title>
+
+ <indexterm zone="dummy_seclabel">
+ <primary>dummy_seclabel</primary>
+ </indexterm>
+
+ <para>
+ The <filename>dummy_seclabel</> module provides a pseudo security label
+ support for regression testing.
+ </para>
+
+ <sect2>
+ <title>Rationale</title>
+
+ <para>
+ <productname>PostgreSQL</> got support <command>SECURITY LABEL</>
+ statement at the version 9.1 or later. It allows us to assign security
+ labels on database objects using plugin modules that are also called
+ external label provider.
+ </para>
+
+ <para>
+ This feature expects plugin modules validate given security labels,
+ because format of the labels completely depends on the security model
+ that plugin tries to provide, so we must install a plugin module to
+ provide security label feature at least.
+ </para>
+
+ <para>
+ However, we need to run regression test for the core features to
+ detect obvious regressions in the future. So, we also needed to ship
+ a dummy security label module independent from the platform.
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>How to Use It</title>
+
+ <para>
+ Here's a simple example of usage:
+ </para>
+
+<programlisting>
+# postgresql.conf
+shared_preload_libraries = 'dummy_label'
+</programlisting>
+
+<programlisting>
+postgres=# CREATE TABLE t (a int, b text);
+CREATE TABLE
+postgres=# SECURITY LABEL ON TABLE t IS 'classified';
+SECURITY LABEL
+</programlisting>
+
+ <para>
+ The <filename>dummy_seclabel</> provide only a few kind of security
+ labels: <literal>unclassified</>, <literal>classified</>,
+ <literal>secret</>, <literal>top secret</> and <literal>trusted</>.
+
+ It does not allow any other strings as security labels.
+ </para>
+ <para>
+ These labels are not used to any valid access controls.
+ So, all we can do is to check whether <command>SECURITY LABEL</>
+ statement works as expected, or not.
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>Limitations</title>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ This module is not intended to provide something useful features,
+ except for regression tests, so we don't recommend to install your
+ systems.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </sect2>
+
+ <sect2>
+ <title>Author</title>
+
+ <para>
+ KaiGai Kohei <email>[email protected]</email>
+ </para>
+ </sect2>
+
+</sect1>
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 9b1de85..ca24638 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -102,6 +102,7 @@
<!entity dblink SYSTEM "dblink.sgml">
<!entity dict-int SYSTEM "dict-int.sgml">
<!entity dict-xsyn SYSTEM "dict-xsyn.sgml">
+<!entity dummy_seclabel SYSTEM "dummy_seclabel.sgml">
<!entity earthdistance SYSTEM "earthdistance.sgml">
<!entity fuzzystrmatch SYSTEM "fuzzystrmatch.sgml">
<!entity hstore SYSTEM "hstore.sgml">
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index de2e66b..3b64d37 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -3721,6 +3721,10 @@ inline_function(Oid funcid, Oid result_type, List *args,
if (pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK)
return NULL;
+ /* Check whether plugin want to hook this function, or not */
+ if (IsFunctionCallEventHooked(funcid))
+ return NULL;
+
/*
* Make a temporary memory context, so that we don't leak all the stuff
* that parsing might create.
@@ -4153,6 +4157,10 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
if (pg_proc_aclcheck(func_oid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK)
return NULL;
+ /* Check whether plugin want to hook this function, or not */
+ if (IsFunctionCallEventHooked(func_oid))
+ return NULL;
+
/*
* OK, let's take a look at the function's pg_proc entry.
*/
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 1c9d2c2..e7651cb 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -30,6 +30,10 @@
#include "utils/lsyscache.h"
#include "utils/syscache.h"
+/*
+ * Hooks for function calls
+ */
+PGDLLIMPORT function_call_event_type function_call_event_hook = NULL;
/*
* Declaration for old-style function pointer type. This is now used only
@@ -230,7 +234,8 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt,
*/
if (!ignore_security &&
(procedureStruct->prosecdef ||
- !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig)))
+ !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig) ||
+ IsFunctionCallEventHooked(functionId)))
{
finfo->fn_addr = fmgr_security_definer;
finfo->fn_stats = TRACK_FUNC_ALL; /* ie, never track */
@@ -857,6 +862,7 @@ struct fmgr_security_definer_cache
FmgrInfo flinfo; /* lookup info for target function */
Oid userid; /* userid to set, or InvalidOid */
ArrayType *proconfig; /* GUC values to set, or NULL */
+ Datum private; /* private usage for hook plugins */
};
/*
@@ -878,6 +884,7 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
Oid save_userid;
int save_sec_context;
volatile int save_nestlevel;
+ Datum save_datum;
PgStat_FunctionCallUsage fcusage;
if (!fcinfo->flinfo->fn_extra)
@@ -916,6 +923,12 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
ReleaseSysCache(tuple);
+ /* Function call event hook */
+ if (function_call_event_hook)
+ fcache->private =
+ (*function_call_event_hook)(fcinfo->flinfo->fn_oid,
+ FCET_PREPARE,
+ PointerGetDatum(&fcache->flinfo));
fcinfo->flinfo->fn_extra = fcache;
}
else
@@ -930,7 +943,7 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
if (OidIsValid(fcache->userid))
SetUserIdAndSecContext(fcache->userid,
- save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
+ save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
if (fcache->proconfig)
{
@@ -940,6 +953,13 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
GUC_ACTION_SAVE);
}
+ if (function_call_event_hook)
+ save_datum = (*function_call_event_hook)(fcinfo->flinfo->fn_oid,
+ FCET_START,
+ fcache->private);
+ else
+ save_datum = PointerGetDatum(NULL); /* keep compiler quiet */
+
/*
* We don't need to restore GUC or userid settings on error, because the
* ensuing xact or subxact abort will do that. The PG_TRY block is only
@@ -968,6 +988,9 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
PG_CATCH();
{
fcinfo->flinfo = save_flinfo;
+ if (function_call_event_hook)
+ (void)(*function_call_event_hook)(fcinfo->flinfo->fn_oid,
+ FCET_ABORT, save_datum);
PG_RE_THROW();
}
PG_END_TRY();
@@ -978,7 +1001,9 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
AtEOXact_GUC(true, save_nestlevel);
if (OidIsValid(fcache->userid))
SetUserIdAndSecContext(save_userid, save_sec_context);
-
+ if (function_call_event_hook)
+ (void)(*function_call_event_hook)(fcinfo->flinfo->fn_oid,
+ FCET_END, save_datum);
return result;
}
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index ca5a5ea..a9a8d31 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -544,6 +544,55 @@ extern void **find_rendezvous_variable(const char *varName);
extern int AggCheckCallContext(FunctionCallInfo fcinfo,
MemoryContext *aggcontext);
+/*
+ * function_call_event_hook
+ * ------------------------
+ * This hook allows plugin modules to hook events of function calls.
+ * It enables to switch some of its internal state (such as privilege
+ * of the client) during execution of the hooked function.
+ *
+ * This hook takes three arguments: OID of the function, event typee
+ * and its argument depending on the type.
+ *
+ * FCET_BE_HOOKED is used to ask plugins whether it wants to hook this
+ * function call. This event type has no argument. It shall return
+ * BoolGetDatum(true), if a plugin wants to hook this function
+ *
+ * FCET_PREPARE allows plugins to acquire control on the first invocation
+ * time of the function. This event type delivers a pointer to the FmgrInfo.
+ * It shall return an opaque private data that will be delivered to
+ * FCET_START event.
+ *
+ * FCET_START allows plugins to acquire control just before invocation
+ * of the hooked function for each time. This event type delivers the
+ * opaque private data come from FCET_PREPARE. Then, it can also return
+ * an opaque private to inform something for FCET_END and FCET_ABORT.
+ *
+ * FCET_END and FCET_ABORT allow plugins to acquire control just after
+ * invocation of the hooked function for each time. This event type delivers
+ * an opaque private come from FCET_START.
+ */
+typedef enum FunctionCallEventType
+{
+ FCET_BE_HOOKED,
+ FCET_PREPARE,
+ FCET_START,
+ FCET_END,
+ FCET_ABORT,
+} FunctionCallEventType;
+
+typedef Datum (*function_call_event_type)(Oid functionId,
+ FunctionCallEventType event,
+ Datum event_arg);
+extern PGDLLIMPORT function_call_event_type function_call_event_hook;
+
+#define IsFunctionCallEventHooked(funcOid) \
+ (!function_call_event_hook ? \
+ false \
+ : \
+ DatumGetBool((*function_call_event_hook)(ObjectIdGetDatum(funcOid), \
+ FCET_BE_HOOKED, 0)) \
+ )
/*
* !!! OLD INTERFACE !!!
diff --git a/src/test/regress/input/security_label.source b/src/test/regress/input/security_label.source
index 810a721..8a9646c 100644
--- a/src/test/regress/input/security_label.source
+++ b/src/test/regress/input/security_label.source
@@ -37,6 +37,15 @@ SECURITY LABEL ON TABLE seclabel_tbl3 IS 'unclassified'; -- fail
-- Load dummy external security provider
LOAD '@libdir@/dummy_secla...@dlsuffix@';
+CREATE FUNCTION dummy_client_label() RETURNS text LANGUAGE 'c'
+ AS '@libdir@/dummy_secla...@dlsuffix@', 'dummy_client_label';
+
+CREATE FUNCTION seclabel_regular() RETURNS text LANGUAGE 'sql'
+ AS 'SELECT dummy_client_label()';
+
+CREATE FUNCTION seclabel_trusted() RETURNS text LANGUAGE 'sql'
+ AS 'SELECT dummy_client_label()';
+
--
-- Test of SECURITY LABEL statement with a plugin
--
@@ -70,7 +79,28 @@ SELECT objtype, objname, provider, label FROM pg_seclabels
SECURITY LABEL ON LANGUAGE plpgsql IS NULL; -- OK
SECURITY LABEL ON SCHEMA public IS NULL; -- OK
+-- test for trusted procedures
+SECURITY LABEL ON FUNCTION seclabel_regular() IS 'unclassified'; -- OK
+SECURITY LABEL ON FUNCTION seclabel_trusted() IS 'trusted'; -- OK
+
+-- should be 'unclassified' and 'top secret'
+SELECT seclabel_regular(), seclabel_trusted();
+
+SET SESSION AUTHORIZATION seclabel_user1;
+
+-- should be 'unclassified' and 'secret'
+SELECT seclabel_regular(), seclabel_trusted();
+
+-- be inlined
+EXPLAIN SELECT * FROM seclabel_regular();
+
+-- be not inlined
+EXPLAIN SELECT * FROM seclabel_trusted();
+
-- clean up objects
+RESET SESSION AUTHORIZATION;
+DROP FUNCTION seclabel_regular();
+DROP FUNCTION seclabel_trusted();
DROP FUNCTION seclabel_four();
DROP DOMAIN seclabel_domain;
DROP VIEW seclabel_view1;
diff --git a/src/test/regress/output/security_label.source b/src/test/regress/output/security_label.source
index 4bc803d..ec2baff 100644
--- a/src/test/regress/output/security_label.source
+++ b/src/test/regress/output/security_label.source
@@ -30,7 +30,13 @@ ERROR: no security label providers have been loaded
SECURITY LABEL ON TABLE seclabel_tbl3 IS 'unclassified'; -- fail
ERROR: no security label providers have been loaded
-- Load dummy external security provider
-LOAD '@abs_builddir@/dummy_secla...@dlsuffix@';
+LOAD '@libdir@/dummy_secla...@dlsuffix@';
+CREATE FUNCTION dummy_client_label() RETURNS text LANGUAGE 'c'
+ AS '@libdir@/dummy_secla...@dlsuffix@', 'dummy_client_label';
+CREATE FUNCTION seclabel_regular() RETURNS text LANGUAGE 'sql'
+ AS 'SELECT dummy_client_label()';
+CREATE FUNCTION seclabel_trusted() RETURNS text LANGUAGE 'sql'
+ AS 'SELECT dummy_client_label()';
--
-- Test of SECURITY LABEL statement with a plugin
--
@@ -75,7 +81,42 @@ SELECT objtype, objname, provider, label FROM pg_seclabels
SECURITY LABEL ON LANGUAGE plpgsql IS NULL; -- OK
SECURITY LABEL ON SCHEMA public IS NULL; -- OK
+-- test for trusted procedures
+SECURITY LABEL ON FUNCTION seclabel_regular() IS 'unclassified'; -- OK
+SECURITY LABEL ON FUNCTION seclabel_trusted() IS 'trusted'; -- OK
+-- should be 'unclassified' and 'top secret'
+SELECT seclabel_regular(), seclabel_trusted();
+ seclabel_regular | seclabel_trusted
+------------------+------------------
+ unclassified | top secret
+(1 row)
+
+SET SESSION AUTHORIZATION seclabel_user1;
+-- should be 'unclassified' and 'secret'
+SELECT seclabel_regular(), seclabel_trusted();
+ seclabel_regular | seclabel_trusted
+------------------+------------------
+ unclassified | secret
+(1 row)
+
+-- be inlined
+EXPLAIN SELECT * FROM seclabel_regular();
+ QUERY PLAN
+-----------------------------------------------------------------------------------------
+ Function Scan on dummy_client_label seclabel_regular (cost=0.00..0.01 rows=1 width=32)
+(1 row)
+
+-- be not inlined
+EXPLAIN SELECT * FROM seclabel_trusted();
+ QUERY PLAN
+----------------------------------------------------------------------
+ Function Scan on seclabel_trusted (cost=0.25..0.26 rows=1 width=32)
+(1 row)
+
-- clean up objects
+RESET SESSION AUTHORIZATION;
+DROP FUNCTION seclabel_regular();
+DROP FUNCTION seclabel_trusted();
DROP FUNCTION seclabel_four();
DROP DOMAIN seclabel_domain;
DROP VIEW seclabel_view1;
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers