mirror of
https://github.com/postgres/postgres.git
synced 2025-04-22 23:02:54 +03:00
Allow LEAKPROOF functions for better performance of security views.
We don't normally allow quals to be pushed down into a view created with the security_barrier option, but functions without side effects are an exception: they're OK. This allows much better performance in common cases, such as when using an equality operator (that might even be indexable). There is an outstanding issue here with the CREATE FUNCTION / ALTER FUNCTION syntax: there's no way to use ALTER FUNCTION to unset the leakproof flag. But I'm committing this as-is so that it doesn't have to be rebased again; we can fix up the grammar in a future commit. KaiGai Kohei, with some wordsmithing by me.
This commit is contained in:
parent
2bbd88f8f8
commit
cd30728fb2
@ -4423,6 +4423,18 @@
|
|||||||
function)</entry>
|
function)</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><structfield>proleakproof</structfield></entry>
|
||||||
|
<entry><type>bool</type></entry>
|
||||||
|
<entry></entry>
|
||||||
|
<entry>
|
||||||
|
The function has no side effects. No information about the
|
||||||
|
arguments is conveyed except via the return value. Any function
|
||||||
|
that might throw an error depending on the values of its arguments
|
||||||
|
is not leakproof.
|
||||||
|
</entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
<entry><structfield>proisstrict</structfield></entry>
|
<entry><structfield>proisstrict</structfield></entry>
|
||||||
<entry><type>bool</type></entry>
|
<entry><type>bool</type></entry>
|
||||||
|
@ -33,7 +33,7 @@ ALTER FUNCTION <replaceable>name</replaceable> ( [ [ <replaceable class="paramet
|
|||||||
<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
|
<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
|
||||||
|
|
||||||
CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
|
CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
|
||||||
IMMUTABLE | STABLE | VOLATILE
|
IMMUTABLE | STABLE | VOLATILE | LEAKPROOF
|
||||||
[ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
|
[ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
|
||||||
COST <replaceable class="parameter">execution_cost</replaceable>
|
COST <replaceable class="parameter">execution_cost</replaceable>
|
||||||
ROWS <replaceable class="parameter">result_rows</replaceable>
|
ROWS <replaceable class="parameter">result_rows</replaceable>
|
||||||
@ -191,6 +191,17 @@ ALTER FUNCTION <replaceable>name</replaceable> ( [ [ <replaceable class="paramet
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><literal>LEAKPROOF</literal></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Change whether the function is considered leakproof or not.
|
||||||
|
See <xref linkend="sql-createfunction"> for more information about
|
||||||
|
this capability.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><literal>COST</literal> <replaceable class="parameter">execution_cost</replaceable></term>
|
<term><literal>COST</literal> <replaceable class="parameter">execution_cost</replaceable></term>
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ CREATE [ OR REPLACE ] FUNCTION
|
|||||||
| RETURNS TABLE ( <replaceable class="parameter">column_name</replaceable> <replaceable class="parameter">column_type</replaceable> [, ...] ) ]
|
| RETURNS TABLE ( <replaceable class="parameter">column_name</replaceable> <replaceable class="parameter">column_type</replaceable> [, ...] ) ]
|
||||||
{ LANGUAGE <replaceable class="parameter">lang_name</replaceable>
|
{ LANGUAGE <replaceable class="parameter">lang_name</replaceable>
|
||||||
| WINDOW
|
| WINDOW
|
||||||
| IMMUTABLE | STABLE | VOLATILE
|
| IMMUTABLE | STABLE | VOLATILE | LEAKPROOF
|
||||||
| CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
|
| CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
|
||||||
| [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
|
| [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
|
||||||
| COST <replaceable class="parameter">execution_cost</replaceable>
|
| COST <replaceable class="parameter">execution_cost</replaceable>
|
||||||
@ -324,6 +324,23 @@ CREATE [ OR REPLACE ] FUNCTION
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><literal>LEAKPROOF</literal></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<literal>LEAKPROOF</literal> indicates that the function has no side
|
||||||
|
effects. It reveals no information about its arguments other than by
|
||||||
|
its return value. For example, a function which throws an error message
|
||||||
|
for some argument values but not others, or which includes the argument
|
||||||
|
values in any error message, is not leakproof. The query planner may
|
||||||
|
push leakproof functions (but not others) into views created with the
|
||||||
|
<literal>security_barrier</literal> option. See
|
||||||
|
<xref linkend="sql-createview"> and <xref linkend="rules-privileges">.
|
||||||
|
This option can only be set by the superuser.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><literal>CALLED ON NULL INPUT</literal></term>
|
<term><literal>CALLED ON NULL INPUT</literal></term>
|
||||||
<term><literal>RETURNS NULL ON NULL INPUT</literal></term>
|
<term><literal>RETURNS NULL ON NULL INPUT</literal></term>
|
||||||
|
@ -1890,6 +1890,20 @@ CREATE VIEW phone_number WITH (security_barrier) AS
|
|||||||
enabled by default.
|
enabled by default.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The query planner has more flexibility when dealing with functions that
|
||||||
|
have no side effects. Such functions are referred to as LEAKPROOF, and
|
||||||
|
include many simple, commonly used operators, such as many equality
|
||||||
|
operators. The query planner can safely allow such functions to be evaluated
|
||||||
|
at any point in the query execution process, since invoking them on rows
|
||||||
|
invisible to the user will not leak any information about the unseen rows.
|
||||||
|
In contrast, a function that might throw an error depending on the values
|
||||||
|
received as arguments (such as one that throws an error in the event of
|
||||||
|
overflow or division by zero) are not leak-proof, and could provide
|
||||||
|
significant information about the unseen rows if applied before the security
|
||||||
|
view's row filters.
|
||||||
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
It is important to understand that even a view created with the
|
It is important to understand that even a view created with the
|
||||||
<literal>security_barrier</literal> option is intended to be secure only
|
<literal>security_barrier</literal> option is intended to be secure only
|
||||||
|
@ -241,6 +241,7 @@ AggregateCreate(const char *aggName,
|
|||||||
false, /* isWindowFunc */
|
false, /* isWindowFunc */
|
||||||
false, /* security invoker (currently not
|
false, /* security invoker (currently not
|
||||||
* definable for agg) */
|
* definable for agg) */
|
||||||
|
false, /* isLeakProof */
|
||||||
false, /* isStrict (not needed for agg) */
|
false, /* isStrict (not needed for agg) */
|
||||||
PROVOLATILE_IMMUTABLE, /* volatility (not
|
PROVOLATILE_IMMUTABLE, /* volatility (not
|
||||||
* needed for agg) */
|
* needed for agg) */
|
||||||
|
@ -76,6 +76,7 @@ ProcedureCreate(const char *procedureName,
|
|||||||
bool isAgg,
|
bool isAgg,
|
||||||
bool isWindowFunc,
|
bool isWindowFunc,
|
||||||
bool security_definer,
|
bool security_definer,
|
||||||
|
bool isLeakProof,
|
||||||
bool isStrict,
|
bool isStrict,
|
||||||
char volatility,
|
char volatility,
|
||||||
oidvector *parameterTypes,
|
oidvector *parameterTypes,
|
||||||
@ -334,6 +335,7 @@ ProcedureCreate(const char *procedureName,
|
|||||||
values[Anum_pg_proc_proisagg - 1] = BoolGetDatum(isAgg);
|
values[Anum_pg_proc_proisagg - 1] = BoolGetDatum(isAgg);
|
||||||
values[Anum_pg_proc_proiswindow - 1] = BoolGetDatum(isWindowFunc);
|
values[Anum_pg_proc_proiswindow - 1] = BoolGetDatum(isWindowFunc);
|
||||||
values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(security_definer);
|
values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(security_definer);
|
||||||
|
values[Anum_pg_proc_proleakproof - 1] = BoolGetDatum(isLeakProof);
|
||||||
values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(isStrict);
|
values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(isStrict);
|
||||||
values[Anum_pg_proc_proretset - 1] = BoolGetDatum(returnsSet);
|
values[Anum_pg_proc_proretset - 1] = BoolGetDatum(returnsSet);
|
||||||
values[Anum_pg_proc_provolatile - 1] = CharGetDatum(volatility);
|
values[Anum_pg_proc_provolatile - 1] = CharGetDatum(volatility);
|
||||||
|
@ -446,6 +446,7 @@ compute_common_attribute(DefElem *defel,
|
|||||||
DefElem **volatility_item,
|
DefElem **volatility_item,
|
||||||
DefElem **strict_item,
|
DefElem **strict_item,
|
||||||
DefElem **security_item,
|
DefElem **security_item,
|
||||||
|
DefElem **leakproof_item,
|
||||||
List **set_items,
|
List **set_items,
|
||||||
DefElem **cost_item,
|
DefElem **cost_item,
|
||||||
DefElem **rows_item)
|
DefElem **rows_item)
|
||||||
@ -471,6 +472,13 @@ compute_common_attribute(DefElem *defel,
|
|||||||
|
|
||||||
*security_item = defel;
|
*security_item = defel;
|
||||||
}
|
}
|
||||||
|
else if (strcmp(defel->defname, "leakproof") == 0)
|
||||||
|
{
|
||||||
|
if (*leakproof_item)
|
||||||
|
goto duplicate_error;
|
||||||
|
|
||||||
|
*leakproof_item = defel;
|
||||||
|
}
|
||||||
else if (strcmp(defel->defname, "set") == 0)
|
else if (strcmp(defel->defname, "set") == 0)
|
||||||
{
|
{
|
||||||
*set_items = lappend(*set_items, defel->arg);
|
*set_items = lappend(*set_items, defel->arg);
|
||||||
@ -564,6 +572,7 @@ compute_attributes_sql_style(List *options,
|
|||||||
char *volatility_p,
|
char *volatility_p,
|
||||||
bool *strict_p,
|
bool *strict_p,
|
||||||
bool *security_definer,
|
bool *security_definer,
|
||||||
|
bool *leakproof_p,
|
||||||
ArrayType **proconfig,
|
ArrayType **proconfig,
|
||||||
float4 *procost,
|
float4 *procost,
|
||||||
float4 *prorows)
|
float4 *prorows)
|
||||||
@ -575,6 +584,7 @@ compute_attributes_sql_style(List *options,
|
|||||||
DefElem *volatility_item = NULL;
|
DefElem *volatility_item = NULL;
|
||||||
DefElem *strict_item = NULL;
|
DefElem *strict_item = NULL;
|
||||||
DefElem *security_item = NULL;
|
DefElem *security_item = NULL;
|
||||||
|
DefElem *leakproof_item = NULL;
|
||||||
List *set_items = NIL;
|
List *set_items = NIL;
|
||||||
DefElem *cost_item = NULL;
|
DefElem *cost_item = NULL;
|
||||||
DefElem *rows_item = NULL;
|
DefElem *rows_item = NULL;
|
||||||
@ -611,6 +621,7 @@ compute_attributes_sql_style(List *options,
|
|||||||
&volatility_item,
|
&volatility_item,
|
||||||
&strict_item,
|
&strict_item,
|
||||||
&security_item,
|
&security_item,
|
||||||
|
&leakproof_item,
|
||||||
&set_items,
|
&set_items,
|
||||||
&cost_item,
|
&cost_item,
|
||||||
&rows_item))
|
&rows_item))
|
||||||
@ -653,6 +664,8 @@ compute_attributes_sql_style(List *options,
|
|||||||
*strict_p = intVal(strict_item->arg);
|
*strict_p = intVal(strict_item->arg);
|
||||||
if (security_item)
|
if (security_item)
|
||||||
*security_definer = intVal(security_item->arg);
|
*security_definer = intVal(security_item->arg);
|
||||||
|
if (leakproof_item)
|
||||||
|
*leakproof_p = intVal(leakproof_item->arg);
|
||||||
if (set_items)
|
if (set_items)
|
||||||
*proconfig = update_proconfig_value(NULL, set_items);
|
*proconfig = update_proconfig_value(NULL, set_items);
|
||||||
if (cost_item)
|
if (cost_item)
|
||||||
@ -805,7 +818,8 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
|
|||||||
Oid requiredResultType;
|
Oid requiredResultType;
|
||||||
bool isWindowFunc,
|
bool isWindowFunc,
|
||||||
isStrict,
|
isStrict,
|
||||||
security;
|
security,
|
||||||
|
isLeakProof;
|
||||||
char volatility;
|
char volatility;
|
||||||
ArrayType *proconfig;
|
ArrayType *proconfig;
|
||||||
float4 procost;
|
float4 procost;
|
||||||
@ -828,6 +842,7 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
|
|||||||
isWindowFunc = false;
|
isWindowFunc = false;
|
||||||
isStrict = false;
|
isStrict = false;
|
||||||
security = false;
|
security = false;
|
||||||
|
isLeakProof = false;
|
||||||
volatility = PROVOLATILE_VOLATILE;
|
volatility = PROVOLATILE_VOLATILE;
|
||||||
proconfig = NULL;
|
proconfig = NULL;
|
||||||
procost = -1; /* indicates not set */
|
procost = -1; /* indicates not set */
|
||||||
@ -837,7 +852,7 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
|
|||||||
compute_attributes_sql_style(stmt->options,
|
compute_attributes_sql_style(stmt->options,
|
||||||
&as_clause, &language,
|
&as_clause, &language,
|
||||||
&isWindowFunc, &volatility,
|
&isWindowFunc, &volatility,
|
||||||
&isStrict, &security,
|
&isStrict, &security, &isLeakProof,
|
||||||
&proconfig, &procost, &prorows);
|
&proconfig, &procost, &prorows);
|
||||||
|
|
||||||
/* Look up the language and validate permissions */
|
/* Look up the language and validate permissions */
|
||||||
@ -874,6 +889,16 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
|
|||||||
|
|
||||||
ReleaseSysCache(languageTuple);
|
ReleaseSysCache(languageTuple);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only superuser is allowed to create leakproof functions because
|
||||||
|
* it possibly allows unprivileged users to reference invisible tuples
|
||||||
|
* to be filtered out using views for row-level security.
|
||||||
|
*/
|
||||||
|
if (isLeakProof && !superuser())
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
|
errmsg("only superuser can define a leakproof function")));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert remaining parameters of CREATE to form wanted by
|
* Convert remaining parameters of CREATE to form wanted by
|
||||||
* ProcedureCreate.
|
* ProcedureCreate.
|
||||||
@ -960,6 +985,7 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
|
|||||||
false, /* not an aggregate */
|
false, /* not an aggregate */
|
||||||
isWindowFunc,
|
isWindowFunc,
|
||||||
security,
|
security,
|
||||||
|
isLeakProof,
|
||||||
isStrict,
|
isStrict,
|
||||||
volatility,
|
volatility,
|
||||||
parameterTypes,
|
parameterTypes,
|
||||||
@ -1238,6 +1264,7 @@ AlterFunction(AlterFunctionStmt *stmt)
|
|||||||
DefElem *volatility_item = NULL;
|
DefElem *volatility_item = NULL;
|
||||||
DefElem *strict_item = NULL;
|
DefElem *strict_item = NULL;
|
||||||
DefElem *security_def_item = NULL;
|
DefElem *security_def_item = NULL;
|
||||||
|
DefElem *leakproof_item = NULL;
|
||||||
List *set_items = NIL;
|
List *set_items = NIL;
|
||||||
DefElem *cost_item = NULL;
|
DefElem *cost_item = NULL;
|
||||||
DefElem *rows_item = NULL;
|
DefElem *rows_item = NULL;
|
||||||
@ -1274,6 +1301,7 @@ AlterFunction(AlterFunctionStmt *stmt)
|
|||||||
&volatility_item,
|
&volatility_item,
|
||||||
&strict_item,
|
&strict_item,
|
||||||
&security_def_item,
|
&security_def_item,
|
||||||
|
&leakproof_item,
|
||||||
&set_items,
|
&set_items,
|
||||||
&cost_item,
|
&cost_item,
|
||||||
&rows_item) == false)
|
&rows_item) == false)
|
||||||
@ -1286,6 +1314,14 @@ AlterFunction(AlterFunctionStmt *stmt)
|
|||||||
procForm->proisstrict = intVal(strict_item->arg);
|
procForm->proisstrict = intVal(strict_item->arg);
|
||||||
if (security_def_item)
|
if (security_def_item)
|
||||||
procForm->prosecdef = intVal(security_def_item->arg);
|
procForm->prosecdef = intVal(security_def_item->arg);
|
||||||
|
if (leakproof_item)
|
||||||
|
{
|
||||||
|
if (intVal(leakproof_item->arg) && !superuser())
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
|
errmsg("only superuser can define a leakproof function")));
|
||||||
|
procForm->proleakproof = intVal(leakproof_item->arg);
|
||||||
|
}
|
||||||
if (cost_item)
|
if (cost_item)
|
||||||
{
|
{
|
||||||
procForm->procost = defGetNumeric(cost_item);
|
procForm->procost = defGetNumeric(cost_item);
|
||||||
|
@ -131,6 +131,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
|||||||
false, /* isAgg */
|
false, /* isAgg */
|
||||||
false, /* isWindowFunc */
|
false, /* isWindowFunc */
|
||||||
false, /* security_definer */
|
false, /* security_definer */
|
||||||
|
false, /* isLeakProof */
|
||||||
false, /* isStrict */
|
false, /* isStrict */
|
||||||
PROVOLATILE_VOLATILE,
|
PROVOLATILE_VOLATILE,
|
||||||
buildoidvector(funcargtypes, 0),
|
buildoidvector(funcargtypes, 0),
|
||||||
@ -166,6 +167,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
|||||||
false, /* isAgg */
|
false, /* isAgg */
|
||||||
false, /* isWindowFunc */
|
false, /* isWindowFunc */
|
||||||
false, /* security_definer */
|
false, /* security_definer */
|
||||||
|
false, /* isLeakProof */
|
||||||
true, /* isStrict */
|
true, /* isStrict */
|
||||||
PROVOLATILE_VOLATILE,
|
PROVOLATILE_VOLATILE,
|
||||||
buildoidvector(funcargtypes, 1),
|
buildoidvector(funcargtypes, 1),
|
||||||
@ -204,6 +206,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
|||||||
false, /* isAgg */
|
false, /* isAgg */
|
||||||
false, /* isWindowFunc */
|
false, /* isWindowFunc */
|
||||||
false, /* security_definer */
|
false, /* security_definer */
|
||||||
|
false, /* isLeakProof */
|
||||||
true, /* isStrict */
|
true, /* isStrict */
|
||||||
PROVOLATILE_VOLATILE,
|
PROVOLATILE_VOLATILE,
|
||||||
buildoidvector(funcargtypes, 1),
|
buildoidvector(funcargtypes, 1),
|
||||||
|
@ -1521,6 +1521,7 @@ makeRangeConstructors(const char *name, Oid namespace,
|
|||||||
false, /* isAgg */
|
false, /* isAgg */
|
||||||
false, /* isWindowFunc */
|
false, /* isWindowFunc */
|
||||||
false, /* security_definer */
|
false, /* security_definer */
|
||||||
|
false, /* leakproof */
|
||||||
false, /* isStrict */
|
false, /* isStrict */
|
||||||
PROVOLATILE_IMMUTABLE, /* volatility */
|
PROVOLATILE_IMMUTABLE, /* volatility */
|
||||||
constructorArgTypesVector, /* parameterTypes */
|
constructorArgTypesVector, /* parameterTypes */
|
||||||
|
@ -1042,16 +1042,9 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
|||||||
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
|
||||||
Node *clause = (Node *) rinfo->clause;
|
Node *clause = (Node *) rinfo->clause;
|
||||||
|
|
||||||
/*
|
|
||||||
* XXX. You might wonder why we're testing rte->security_barrier
|
|
||||||
* qual-by-qual here rather than hoisting the test up into the
|
|
||||||
* surrounding if statement; after all, the answer will be the
|
|
||||||
* same for all quals. The answer is that we expect to shortly
|
|
||||||
* change this logic to allow pushing down some quals that use only
|
|
||||||
* "leakproof" operators even through a security barrier.
|
|
||||||
*/
|
|
||||||
if (!rinfo->pseudoconstant &&
|
if (!rinfo->pseudoconstant &&
|
||||||
!rte->security_barrier &&
|
(!rte->security_barrier ||
|
||||||
|
!contain_leaky_functions(clause)) &&
|
||||||
qual_is_pushdown_safe(subquery, rti, clause, differentTypes))
|
qual_is_pushdown_safe(subquery, rti, clause, differentTypes))
|
||||||
{
|
{
|
||||||
/* Push it down */
|
/* Push it down */
|
||||||
|
@ -93,6 +93,7 @@ static bool contain_subplans_walker(Node *node, void *context);
|
|||||||
static bool contain_mutable_functions_walker(Node *node, void *context);
|
static bool contain_mutable_functions_walker(Node *node, void *context);
|
||||||
static bool contain_volatile_functions_walker(Node *node, void *context);
|
static bool contain_volatile_functions_walker(Node *node, void *context);
|
||||||
static bool contain_nonstrict_functions_walker(Node *node, void *context);
|
static bool contain_nonstrict_functions_walker(Node *node, void *context);
|
||||||
|
static bool contain_leaky_functions_walker(Node *node, void *context);
|
||||||
static Relids find_nonnullable_rels_walker(Node *node, bool top_level);
|
static Relids find_nonnullable_rels_walker(Node *node, bool top_level);
|
||||||
static List *find_nonnullable_vars_walker(Node *node, bool top_level);
|
static List *find_nonnullable_vars_walker(Node *node, bool top_level);
|
||||||
static bool is_strict_saop(ScalarArrayOpExpr *expr, bool falseOK);
|
static bool is_strict_saop(ScalarArrayOpExpr *expr, bool falseOK);
|
||||||
@ -1129,6 +1130,145 @@ contain_nonstrict_functions_walker(Node *node, void *context)
|
|||||||
context);
|
context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* Check clauses for non-leakproof functions
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* contain_leaky_functions
|
||||||
|
* Recursively search for leaky functions within a clause.
|
||||||
|
*
|
||||||
|
* Returns true if any function call with side-effect may be present in the
|
||||||
|
* clause. Qualifiers from outside the a security_barrier view should not
|
||||||
|
* be pushed down into the view, lest the contents of tuples intended to be
|
||||||
|
* filtered out be revealed via side effects.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
contain_leaky_functions(Node *clause)
|
||||||
|
{
|
||||||
|
return contain_leaky_functions_walker(clause, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
contain_leaky_functions_walker(Node *node, void *context)
|
||||||
|
{
|
||||||
|
if (node == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (nodeTag(node))
|
||||||
|
{
|
||||||
|
case T_Var:
|
||||||
|
case T_Const:
|
||||||
|
case T_Param:
|
||||||
|
case T_ArrayExpr:
|
||||||
|
case T_NamedArgExpr:
|
||||||
|
case T_BoolExpr:
|
||||||
|
case T_RelabelType:
|
||||||
|
case T_CaseExpr:
|
||||||
|
case T_CaseTestExpr:
|
||||||
|
case T_RowExpr:
|
||||||
|
case T_MinMaxExpr:
|
||||||
|
case T_NullTest:
|
||||||
|
case T_BooleanTest:
|
||||||
|
case T_List:
|
||||||
|
/*
|
||||||
|
* We know these node types don't contain function calls; but
|
||||||
|
* something further down in the node tree might.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_FuncExpr:
|
||||||
|
{
|
||||||
|
FuncExpr *expr = (FuncExpr *) node;
|
||||||
|
|
||||||
|
if (!get_func_leakproof(expr->funcid))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_OpExpr:
|
||||||
|
case T_DistinctExpr: /* struct-equivalent to OpExpr */
|
||||||
|
case T_NullIfExpr: /* struct-equivalent to OpExpr */
|
||||||
|
{
|
||||||
|
OpExpr *expr = (OpExpr *) node;
|
||||||
|
|
||||||
|
set_opfuncid(expr);
|
||||||
|
if (!get_func_leakproof(expr->opfuncid))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_ScalarArrayOpExpr:
|
||||||
|
{
|
||||||
|
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
|
||||||
|
|
||||||
|
set_sa_opfuncid(expr);
|
||||||
|
if (!get_func_leakproof(expr->opfuncid))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_CoerceViaIO:
|
||||||
|
{
|
||||||
|
CoerceViaIO *expr = (CoerceViaIO *) node;
|
||||||
|
Oid funcid;
|
||||||
|
Oid ioparam;
|
||||||
|
bool varlena;
|
||||||
|
|
||||||
|
getTypeInputInfo(exprType((Node *)expr->arg),
|
||||||
|
&funcid, &ioparam);
|
||||||
|
if (!get_func_leakproof(funcid))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
getTypeOutputInfo(expr->resulttype, &funcid, &varlena);
|
||||||
|
if (!get_func_leakproof(funcid))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_ArrayCoerceExpr:
|
||||||
|
{
|
||||||
|
ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node;
|
||||||
|
Oid funcid;
|
||||||
|
Oid ioparam;
|
||||||
|
bool varlena;
|
||||||
|
|
||||||
|
getTypeInputInfo(exprType((Node *)expr->arg),
|
||||||
|
&funcid, &ioparam);
|
||||||
|
if (!get_func_leakproof(funcid))
|
||||||
|
return true;
|
||||||
|
getTypeOutputInfo(expr->resulttype, &funcid, &varlena);
|
||||||
|
if (!get_func_leakproof(funcid))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_RowCompareExpr:
|
||||||
|
{
|
||||||
|
RowCompareExpr *rcexpr = (RowCompareExpr *) node;
|
||||||
|
ListCell *opid;
|
||||||
|
|
||||||
|
foreach(opid, rcexpr->opnos)
|
||||||
|
{
|
||||||
|
Oid funcid = get_opcode(lfirst_oid(opid));
|
||||||
|
|
||||||
|
if (!get_func_leakproof(funcid))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/*
|
||||||
|
* If we don't recognize the node tag, assume it might be leaky.
|
||||||
|
* This prevents an unexpected security hole if someone adds a new
|
||||||
|
* node type that can call a function.
|
||||||
|
*/
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return expression_tree_walker(node, contain_leaky_functions_walker,
|
||||||
|
context);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* find_nonnullable_rels
|
* find_nonnullable_rels
|
||||||
|
@ -526,7 +526,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType,
|
|||||||
|
|
||||||
KEY
|
KEY
|
||||||
|
|
||||||
LABEL 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 LEAKPROOF
|
||||||
LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP
|
LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP
|
||||||
LOCATION LOCK_P
|
LOCATION LOCK_P
|
||||||
|
|
||||||
@ -6115,6 +6115,10 @@ common_func_opt_item:
|
|||||||
{
|
{
|
||||||
$$ = makeDefElem("security", (Node *)makeInteger(FALSE));
|
$$ = makeDefElem("security", (Node *)makeInteger(FALSE));
|
||||||
}
|
}
|
||||||
|
| LEAKPROOF
|
||||||
|
{
|
||||||
|
$$ = makeDefElem("leakproof", (Node *)makeInteger(TRUE));
|
||||||
|
}
|
||||||
| COST NumericOnly
|
| COST NumericOnly
|
||||||
{
|
{
|
||||||
$$ = makeDefElem("cost", (Node *)$2);
|
$$ = makeDefElem("cost", (Node *)$2);
|
||||||
@ -12219,6 +12223,7 @@ unreserved_keyword:
|
|||||||
| LAST_P
|
| LAST_P
|
||||||
| LC_COLLATE_P
|
| LC_COLLATE_P
|
||||||
| LC_CTYPE_P
|
| LC_CTYPE_P
|
||||||
|
| LEAKPROOF
|
||||||
| LEVEL
|
| LEVEL
|
||||||
| LISTEN
|
| LISTEN
|
||||||
| LOAD
|
| LOAD
|
||||||
|
19
src/backend/utils/cache/lsyscache.c
vendored
19
src/backend/utils/cache/lsyscache.c
vendored
@ -1547,6 +1547,25 @@ func_volatile(Oid funcid)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get_func_leakproof
|
||||||
|
* Given procedure id, return the function's leakproof field.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
get_func_leakproof(Oid funcid)
|
||||||
|
{
|
||||||
|
HeapTuple tp;
|
||||||
|
bool result;
|
||||||
|
|
||||||
|
tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
|
||||||
|
if (!HeapTupleIsValid(tp))
|
||||||
|
elog(ERROR, "cache lookup failed for function %u", funcid);
|
||||||
|
|
||||||
|
result = ((Form_pg_proc) GETSTRUCT(tp))->proleakproof;
|
||||||
|
ReleaseSysCache(tp);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get_func_cost
|
* get_func_cost
|
||||||
* Given procedure id, return the function's procost field.
|
* Given procedure id, return the function's procost field.
|
||||||
|
@ -9071,6 +9071,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
|||||||
char *provolatile;
|
char *provolatile;
|
||||||
char *proisstrict;
|
char *proisstrict;
|
||||||
char *prosecdef;
|
char *prosecdef;
|
||||||
|
char *proleakproof;
|
||||||
char *proconfig;
|
char *proconfig;
|
||||||
char *procost;
|
char *procost;
|
||||||
char *prorows;
|
char *prorows;
|
||||||
@ -9098,7 +9099,24 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
|||||||
selectSourceSchema(fout, finfo->dobj.namespace->dobj.name);
|
selectSourceSchema(fout, finfo->dobj.namespace->dobj.name);
|
||||||
|
|
||||||
/* Fetch function-specific details */
|
/* Fetch function-specific details */
|
||||||
if (fout->remoteVersion >= 80400)
|
if (fout->remoteVersion >= 90200)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* proleakproof was added at v9.2
|
||||||
|
*/
|
||||||
|
appendPQExpBuffer(query,
|
||||||
|
"SELECT proretset, prosrc, probin, "
|
||||||
|
"pg_catalog.pg_get_function_arguments(oid) AS funcargs, "
|
||||||
|
"pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, "
|
||||||
|
"pg_catalog.pg_get_function_result(oid) AS funcresult, "
|
||||||
|
"proiswindow, provolatile, proisstrict, prosecdef, "
|
||||||
|
"proleakproof, proconfig, procost, prorows, "
|
||||||
|
"(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
|
||||||
|
"FROM pg_catalog.pg_proc "
|
||||||
|
"WHERE oid = '%u'::pg_catalog.oid",
|
||||||
|
finfo->dobj.catId.oid);
|
||||||
|
}
|
||||||
|
else if (fout->remoteVersion >= 80400)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* In 8.4 and up we rely on pg_get_function_arguments and
|
* In 8.4 and up we rely on pg_get_function_arguments and
|
||||||
@ -9110,7 +9128,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
|||||||
"pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, "
|
"pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, "
|
||||||
"pg_catalog.pg_get_function_result(oid) AS funcresult, "
|
"pg_catalog.pg_get_function_result(oid) AS funcresult, "
|
||||||
"proiswindow, provolatile, proisstrict, prosecdef, "
|
"proiswindow, provolatile, proisstrict, prosecdef, "
|
||||||
"proconfig, procost, prorows, "
|
"false AS proleakproof, "
|
||||||
|
" proconfig, procost, prorows, "
|
||||||
"(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
|
"(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
|
||||||
"FROM pg_catalog.pg_proc "
|
"FROM pg_catalog.pg_proc "
|
||||||
"WHERE oid = '%u'::pg_catalog.oid",
|
"WHERE oid = '%u'::pg_catalog.oid",
|
||||||
@ -9123,6 +9142,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
|||||||
"proallargtypes, proargmodes, proargnames, "
|
"proallargtypes, proargmodes, proargnames, "
|
||||||
"false AS proiswindow, "
|
"false AS proiswindow, "
|
||||||
"provolatile, proisstrict, prosecdef, "
|
"provolatile, proisstrict, prosecdef, "
|
||||||
|
"false AS proleakproof, "
|
||||||
"proconfig, procost, prorows, "
|
"proconfig, procost, prorows, "
|
||||||
"(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
|
"(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
|
||||||
"FROM pg_catalog.pg_proc "
|
"FROM pg_catalog.pg_proc "
|
||||||
@ -9136,6 +9156,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
|||||||
"proallargtypes, proargmodes, proargnames, "
|
"proallargtypes, proargmodes, proargnames, "
|
||||||
"false AS proiswindow, "
|
"false AS proiswindow, "
|
||||||
"provolatile, proisstrict, prosecdef, "
|
"provolatile, proisstrict, prosecdef, "
|
||||||
|
"false AS proleakproof, "
|
||||||
"null AS proconfig, 0 AS procost, 0 AS prorows, "
|
"null AS proconfig, 0 AS procost, 0 AS prorows, "
|
||||||
"(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
|
"(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
|
||||||
"FROM pg_catalog.pg_proc "
|
"FROM pg_catalog.pg_proc "
|
||||||
@ -9151,6 +9172,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
|||||||
"proargnames, "
|
"proargnames, "
|
||||||
"false AS proiswindow, "
|
"false AS proiswindow, "
|
||||||
"provolatile, proisstrict, prosecdef, "
|
"provolatile, proisstrict, prosecdef, "
|
||||||
|
"false AS proleakproof, "
|
||||||
"null AS proconfig, 0 AS procost, 0 AS prorows, "
|
"null AS proconfig, 0 AS procost, 0 AS prorows, "
|
||||||
"(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
|
"(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
|
||||||
"FROM pg_catalog.pg_proc "
|
"FROM pg_catalog.pg_proc "
|
||||||
@ -9166,6 +9188,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
|||||||
"null AS proargnames, "
|
"null AS proargnames, "
|
||||||
"false AS proiswindow, "
|
"false AS proiswindow, "
|
||||||
"provolatile, proisstrict, prosecdef, "
|
"provolatile, proisstrict, prosecdef, "
|
||||||
|
"false AS proleakproof, "
|
||||||
"null AS proconfig, 0 AS procost, 0 AS prorows, "
|
"null AS proconfig, 0 AS procost, 0 AS prorows, "
|
||||||
"(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
|
"(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
|
||||||
"FROM pg_catalog.pg_proc "
|
"FROM pg_catalog.pg_proc "
|
||||||
@ -9183,6 +9206,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
|||||||
"case when proiscachable then 'i' else 'v' end AS provolatile, "
|
"case when proiscachable then 'i' else 'v' end AS provolatile, "
|
||||||
"proisstrict, "
|
"proisstrict, "
|
||||||
"false AS prosecdef, "
|
"false AS prosecdef, "
|
||||||
|
"false AS proleakproof, "
|
||||||
"null AS proconfig, 0 AS procost, 0 AS prorows, "
|
"null AS proconfig, 0 AS procost, 0 AS prorows, "
|
||||||
"(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname "
|
"(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname "
|
||||||
"FROM pg_proc "
|
"FROM pg_proc "
|
||||||
@ -9200,6 +9224,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
|||||||
"CASE WHEN proiscachable THEN 'i' ELSE 'v' END AS provolatile, "
|
"CASE WHEN proiscachable THEN 'i' ELSE 'v' END AS provolatile, "
|
||||||
"false AS proisstrict, "
|
"false AS proisstrict, "
|
||||||
"false AS prosecdef, "
|
"false AS prosecdef, "
|
||||||
|
"false AS proleakproof, "
|
||||||
"NULL AS proconfig, 0 AS procost, 0 AS prorows, "
|
"NULL AS proconfig, 0 AS procost, 0 AS prorows, "
|
||||||
"(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname "
|
"(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname "
|
||||||
"FROM pg_proc "
|
"FROM pg_proc "
|
||||||
@ -9241,6 +9266,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
|||||||
provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
|
provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
|
||||||
proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
|
proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
|
||||||
prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
|
prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
|
||||||
|
proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
|
||||||
proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
|
proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
|
||||||
procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
|
procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
|
||||||
prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
|
prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
|
||||||
@ -9404,6 +9430,9 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
|||||||
if (prosecdef[0] == 't')
|
if (prosecdef[0] == 't')
|
||||||
appendPQExpBuffer(q, " SECURITY DEFINER");
|
appendPQExpBuffer(q, " SECURITY DEFINER");
|
||||||
|
|
||||||
|
if (proleakproof[0] == 't')
|
||||||
|
appendPQExpBuffer(q, " LEAKPROOF");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* COST and ROWS are emitted only if present and not default, so as not to
|
* COST and ROWS are emitted only if present and not default, so as not to
|
||||||
* break backwards-compatibility of the dump without need. Keep this code
|
* break backwards-compatibility of the dump without need. Keep this code
|
||||||
|
@ -53,6 +53,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 201202083
|
#define CATALOG_VERSION_NO 201202131
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -134,7 +134,7 @@ DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 0 f f p r 30 0
|
|||||||
DESCR("");
|
DESCR("");
|
||||||
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 0 f f p r 21 0 f f f f f 3 _null_ _null_ ));
|
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 0 f f p r 21 0 f f f f f 3 _null_ _null_ ));
|
||||||
DESCR("");
|
DESCR("");
|
||||||
DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 0 f f p r 26 0 t f f f f 3 _null_ _null_ ));
|
DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 0 f f p r 27 0 t f f f f 3 _null_ _null_ ));
|
||||||
DESCR("");
|
DESCR("");
|
||||||
DATA(insert OID = 1259 ( pg_class PGNSP 83 0 PGUID 0 0 0 0 0 0 0 0 f f p r 27 0 t f f f f 3 _null_ _null_ ));
|
DATA(insert OID = 1259 ( pg_class PGNSP 83 0 PGUID 0 0 0 0 0 0 0 0 f f p r 27 0 t f f f f 3 _null_ _null_ ));
|
||||||
DESCR("");
|
DESCR("");
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -28,6 +28,7 @@ extern Oid ProcedureCreate(const char *procedureName,
|
|||||||
bool isAgg,
|
bool isAgg,
|
||||||
bool isWindowFunc,
|
bool isWindowFunc,
|
||||||
bool security_definer,
|
bool security_definer,
|
||||||
|
bool isLeakProof,
|
||||||
bool isStrict,
|
bool isStrict,
|
||||||
char volatility,
|
char volatility,
|
||||||
oidvector *parameterTypes,
|
oidvector *parameterTypes,
|
||||||
|
@ -61,6 +61,8 @@ extern bool contain_subplans(Node *clause);
|
|||||||
extern bool contain_mutable_functions(Node *clause);
|
extern bool contain_mutable_functions(Node *clause);
|
||||||
extern bool contain_volatile_functions(Node *clause);
|
extern bool contain_volatile_functions(Node *clause);
|
||||||
extern bool contain_nonstrict_functions(Node *clause);
|
extern bool contain_nonstrict_functions(Node *clause);
|
||||||
|
extern bool contain_leaky_functions(Node *clause);
|
||||||
|
|
||||||
extern Relids find_nonnullable_rels(Node *clause);
|
extern Relids find_nonnullable_rels(Node *clause);
|
||||||
extern List *find_nonnullable_vars(Node *clause);
|
extern List *find_nonnullable_vars(Node *clause);
|
||||||
extern List *find_forced_null_vars(Node *clause);
|
extern List *find_forced_null_vars(Node *clause);
|
||||||
|
@ -215,6 +215,7 @@ PG_KEYWORD("last", LAST_P, UNRESERVED_KEYWORD)
|
|||||||
PG_KEYWORD("lc_collate", LC_COLLATE_P, UNRESERVED_KEYWORD)
|
PG_KEYWORD("lc_collate", LC_COLLATE_P, UNRESERVED_KEYWORD)
|
||||||
PG_KEYWORD("lc_ctype", LC_CTYPE_P, UNRESERVED_KEYWORD)
|
PG_KEYWORD("lc_ctype", LC_CTYPE_P, UNRESERVED_KEYWORD)
|
||||||
PG_KEYWORD("leading", LEADING, RESERVED_KEYWORD)
|
PG_KEYWORD("leading", LEADING, RESERVED_KEYWORD)
|
||||||
|
PG_KEYWORD("leakproof", LEAKPROOF, UNRESERVED_KEYWORD)
|
||||||
PG_KEYWORD("least", LEAST, COL_NAME_KEYWORD)
|
PG_KEYWORD("least", LEAST, COL_NAME_KEYWORD)
|
||||||
PG_KEYWORD("left", LEFT, TYPE_FUNC_NAME_KEYWORD)
|
PG_KEYWORD("left", LEFT, TYPE_FUNC_NAME_KEYWORD)
|
||||||
PG_KEYWORD("level", LEVEL, UNRESERVED_KEYWORD)
|
PG_KEYWORD("level", LEVEL, UNRESERVED_KEYWORD)
|
||||||
|
@ -91,6 +91,7 @@ extern Oid get_func_signature(Oid funcid, Oid **argtypes, int *nargs);
|
|||||||
extern bool get_func_retset(Oid funcid);
|
extern bool get_func_retset(Oid funcid);
|
||||||
extern bool func_strict(Oid funcid);
|
extern bool func_strict(Oid funcid);
|
||||||
extern char func_volatile(Oid funcid);
|
extern char func_volatile(Oid funcid);
|
||||||
|
extern bool get_func_leakproof(Oid funcid);
|
||||||
extern float4 get_func_cost(Oid funcid);
|
extern float4 get_func_cost(Oid funcid);
|
||||||
extern float4 get_func_rows(Oid funcid);
|
extern float4 get_func_rows(Oid funcid);
|
||||||
extern Oid get_relname_relid(const char *relname, Oid relnamespace);
|
extern Oid get_relname_relid(const char *relname, Oid relnamespace);
|
||||||
|
463
src/test/regress/expected/create_function_3.out
Normal file
463
src/test/regress/expected/create_function_3.out
Normal file
@ -0,0 +1,463 @@
|
|||||||
|
--
|
||||||
|
-- CREATE FUNCTION
|
||||||
|
--
|
||||||
|
-- sanity check of pg_proc catalog to the given parameters
|
||||||
|
--
|
||||||
|
CREATE SCHEMA temp_func_test;
|
||||||
|
SET search_path TO temp_func_test, public;
|
||||||
|
--
|
||||||
|
-- ARGUMENT and RETURN TYPES
|
||||||
|
--
|
||||||
|
CREATE FUNCTION functest_A_1(text, date) RETURNS bool LANGUAGE 'sql'
|
||||||
|
AS 'SELECT $1 = ''abcd'' AND $2 > ''2001-01-01''';
|
||||||
|
CREATE FUNCTION functest_A_2(text[]) RETURNS int LANGUAGE 'sql'
|
||||||
|
AS 'SELECT $1[0]::int';
|
||||||
|
CREATE FUNCTION functest_A_3() RETURNS bool LANGUAGE 'sql'
|
||||||
|
AS 'SELECT false';
|
||||||
|
SELECT proname, prorettype::regtype, proargtypes::regtype[] FROM pg_proc
|
||||||
|
WHERE oid in ('functest_A_1'::regproc,
|
||||||
|
'functest_A_2'::regproc,
|
||||||
|
'functest_A_3'::regproc);
|
||||||
|
proname | prorettype | proargtypes
|
||||||
|
--------------+------------+-------------------
|
||||||
|
functest_a_1 | boolean | [0:1]={text,date}
|
||||||
|
functest_a_2 | integer | [0:0]={text[]}
|
||||||
|
functest_a_3 | boolean | {}
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
--
|
||||||
|
-- IMMUTABLE | STABLE | VOLATILE
|
||||||
|
--
|
||||||
|
CREATE FUNCTION functest_B_1(int) RETURNS bool LANGUAGE 'sql'
|
||||||
|
AS 'SELECT $1 > 0';
|
||||||
|
CREATE FUNCTION functest_B_2(int) RETURNS bool LANGUAGE 'sql'
|
||||||
|
IMMUTABLE AS 'SELECT $1 > 0';
|
||||||
|
CREATE FUNCTION functest_B_3(int) RETURNS bool LANGUAGE 'sql'
|
||||||
|
STABLE AS 'SELECT $1 = 0';
|
||||||
|
CREATE FUNCTION functest_B_4(int) RETURNS bool LANGUAGE 'sql'
|
||||||
|
VOLATILE AS 'SELECT $1 < 0';
|
||||||
|
SELECT proname, provolatile FROM pg_proc
|
||||||
|
WHERE oid in ('functest_B_1'::regproc,
|
||||||
|
'functest_B_2'::regproc,
|
||||||
|
'functest_B_3'::regproc,
|
||||||
|
'functest_B_4'::regproc);
|
||||||
|
proname | provolatile
|
||||||
|
--------------+-------------
|
||||||
|
functest_b_1 | v
|
||||||
|
functest_b_2 | i
|
||||||
|
functest_b_3 | s
|
||||||
|
functest_b_4 | v
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
ALTER FUNCTION functest_B_2(int) VOLATILE;
|
||||||
|
ALTER FUNCTION functest_B_3(int) COST 100; -- unrelated change, no effect
|
||||||
|
SELECT proname, provolatile FROM pg_proc
|
||||||
|
WHERE oid in ('functest_B_1'::regproc,
|
||||||
|
'functest_B_2'::regproc,
|
||||||
|
'functest_B_3'::regproc,
|
||||||
|
'functest_B_4'::regproc);
|
||||||
|
proname | provolatile
|
||||||
|
--------------+-------------
|
||||||
|
functest_b_1 | v
|
||||||
|
functest_b_2 | v
|
||||||
|
functest_b_3 | s
|
||||||
|
functest_b_4 | v
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
--
|
||||||
|
-- SECURITY DEFINER | INVOKER
|
||||||
|
--
|
||||||
|
CREATE FUNCTION functext_C_1(int) RETURNS bool LANGUAGE 'sql'
|
||||||
|
AS 'SELECT $1 > 0';
|
||||||
|
CREATE FUNCTION functext_C_2(int) RETURNS bool LANGUAGE 'sql'
|
||||||
|
SECURITY DEFINER AS 'SELECT $1 = 0';
|
||||||
|
CREATE FUNCTION functext_C_3(int) RETURNS bool LANGUAGE 'sql'
|
||||||
|
SECURITY INVOKER AS 'SELECT $1 < 0';
|
||||||
|
SELECT proname, prosecdef FROM pg_proc
|
||||||
|
WHERE oid in ('functext_C_1'::regproc,
|
||||||
|
'functext_C_2'::regproc,
|
||||||
|
'functext_C_3'::regproc);
|
||||||
|
proname | prosecdef
|
||||||
|
--------------+-----------
|
||||||
|
functext_c_1 | f
|
||||||
|
functext_c_2 | t
|
||||||
|
functext_c_3 | f
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
ALTER FUNCTION functext_C_1(int) IMMUTABLE; -- unrelated change, no effect
|
||||||
|
ALTER FUNCTION functext_C_2(int) SECURITY INVOKER;
|
||||||
|
ALTER FUNCTION functext_C_3(int) SECURITY DEFINER;
|
||||||
|
SELECT proname, prosecdef FROM pg_proc
|
||||||
|
WHERE oid in ('functext_C_1'::regproc,
|
||||||
|
'functext_C_2'::regproc,
|
||||||
|
'functext_C_3'::regproc);
|
||||||
|
proname | prosecdef
|
||||||
|
--------------+-----------
|
||||||
|
functext_c_1 | f
|
||||||
|
functext_c_2 | f
|
||||||
|
functext_c_3 | t
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
--
|
||||||
|
-- COST
|
||||||
|
--
|
||||||
|
CREATE FUNCTION functext_D_1(int,int) RETURNS int LANGUAGE 'sql'
|
||||||
|
AS 'SELECT $1 + $2';
|
||||||
|
CREATE FUNCTION functext_D_2(int,int) RETURNS int LANGUAGE 'internal'
|
||||||
|
AS 'int4pl';
|
||||||
|
CREATE FUNCTION functext_D_3(int,int) RETURNS int LANGUAGE 'sql'
|
||||||
|
COST 500 AS 'SELECT $1 * $2';
|
||||||
|
CREATE FUNCTION functext_D_4(int,int) RETURNS int LANGUAGE 'sql'
|
||||||
|
COST 0 AS 'SELECT $1 / $2'; -- Error
|
||||||
|
ERROR: COST must be positive
|
||||||
|
SELECT proname, procost FROM pg_proc
|
||||||
|
WHERE oid in ('functext_D_1'::regproc,
|
||||||
|
'functext_D_2'::regproc,
|
||||||
|
'functext_D_3'::regproc);
|
||||||
|
proname | procost
|
||||||
|
--------------+---------
|
||||||
|
functext_d_1 | 100
|
||||||
|
functext_d_2 | 1
|
||||||
|
functext_d_3 | 500
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
ALTER FUNCTION functext_D_1(int,int) STABLE; -- unrelated change, no effect
|
||||||
|
ALTER FUNCTION functext_D_2(int,int) COST 50;
|
||||||
|
ALTER FUNCTION functext_D_3(int,int) COST 0.0001;
|
||||||
|
SELECT proname, procost FROM pg_proc
|
||||||
|
WHERE oid in ('functext_D_1'::regproc,
|
||||||
|
'functext_D_2'::regproc,
|
||||||
|
'functext_D_3'::regproc);
|
||||||
|
proname | procost
|
||||||
|
--------------+---------
|
||||||
|
functext_d_1 | 100
|
||||||
|
functext_d_2 | 50
|
||||||
|
functext_d_3 | 0.0001
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
--
|
||||||
|
-- LEAKPROOF
|
||||||
|
--
|
||||||
|
CREATE FUNCTION functext_E_1(int) RETURNS bool LANGUAGE 'sql'
|
||||||
|
AS 'SELECT $1 > 100';
|
||||||
|
CREATE FUNCTION functext_E_2(int) RETURNS bool LANGUAGE 'sql'
|
||||||
|
LEAKPROOF AS 'SELECT $1 > 100';
|
||||||
|
SELECT proname, proleakproof FROM pg_proc
|
||||||
|
WHERE oid in ('functext_E_1'::regproc,
|
||||||
|
'functext_E_2'::regproc);
|
||||||
|
proname | proleakproof
|
||||||
|
--------------+--------------
|
||||||
|
functext_e_1 | f
|
||||||
|
functext_e_2 | t
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
ALTER FUNCTION functext_E_1(int) LEAKPROOF;
|
||||||
|
ALTER FUNCTION functext_E_2(int) STABLE; -- unrelated change, no effect
|
||||||
|
SELECT proname, proleakproof FROM pg_proc
|
||||||
|
WHERE oid in ('functext_E_1'::regproc,
|
||||||
|
'functext_E_2'::regproc);
|
||||||
|
proname | proleakproof
|
||||||
|
--------------+--------------
|
||||||
|
functext_e_1 | t
|
||||||
|
functext_e_2 | t
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- list of built-in leakproof functions
|
||||||
|
SELECT proname, prorettype::regtype, proargtypes::regtype[]
|
||||||
|
FROM pg_proc JOIN pg_namespace ON pronamespace = pg_namespace.oid
|
||||||
|
WHERE nspname = 'pg_catalog' AND proleakproof ORDER BY proname;
|
||||||
|
proname | prorettype | proargtypes
|
||||||
|
----------------+------------+---------------------------------------------------------------------
|
||||||
|
abstimeeq | boolean | [0:1]={abstime,abstime}
|
||||||
|
abstimege | boolean | [0:1]={abstime,abstime}
|
||||||
|
abstimegt | boolean | [0:1]={abstime,abstime}
|
||||||
|
abstimele | boolean | [0:1]={abstime,abstime}
|
||||||
|
abstimelt | boolean | [0:1]={abstime,abstime}
|
||||||
|
abstimene | boolean | [0:1]={abstime,abstime}
|
||||||
|
biteq | boolean | [0:1]={bit,bit}
|
||||||
|
bitge | boolean | [0:1]={bit,bit}
|
||||||
|
bitgt | boolean | [0:1]={bit,bit}
|
||||||
|
bitle | boolean | [0:1]={bit,bit}
|
||||||
|
bitlt | boolean | [0:1]={bit,bit}
|
||||||
|
bitne | boolean | [0:1]={bit,bit}
|
||||||
|
booleq | boolean | [0:1]={boolean,boolean}
|
||||||
|
boolge | boolean | [0:1]={boolean,boolean}
|
||||||
|
boolgt | boolean | [0:1]={boolean,boolean}
|
||||||
|
boolle | boolean | [0:1]={boolean,boolean}
|
||||||
|
boollt | boolean | [0:1]={boolean,boolean}
|
||||||
|
boolne | boolean | [0:1]={boolean,boolean}
|
||||||
|
bpchareq | boolean | [0:1]={character,character}
|
||||||
|
bpcharne | boolean | [0:1]={character,character}
|
||||||
|
byteaeq | boolean | [0:1]={bytea,bytea}
|
||||||
|
byteage | boolean | [0:1]={bytea,bytea}
|
||||||
|
byteagt | boolean | [0:1]={bytea,bytea}
|
||||||
|
byteale | boolean | [0:1]={bytea,bytea}
|
||||||
|
bytealt | boolean | [0:1]={bytea,bytea}
|
||||||
|
byteane | boolean | [0:1]={bytea,bytea}
|
||||||
|
cash_eq | boolean | [0:1]={money,money}
|
||||||
|
cash_ge | boolean | [0:1]={money,money}
|
||||||
|
cash_gt | boolean | [0:1]={money,money}
|
||||||
|
cash_le | boolean | [0:1]={money,money}
|
||||||
|
cash_lt | boolean | [0:1]={money,money}
|
||||||
|
cash_ne | boolean | [0:1]={money,money}
|
||||||
|
chareq | boolean | [0:1]={"\"char\"","\"char\""}
|
||||||
|
charge | boolean | [0:1]={"\"char\"","\"char\""}
|
||||||
|
chargt | boolean | [0:1]={"\"char\"","\"char\""}
|
||||||
|
charle | boolean | [0:1]={"\"char\"","\"char\""}
|
||||||
|
charlt | boolean | [0:1]={"\"char\"","\"char\""}
|
||||||
|
charne | boolean | [0:1]={"\"char\"","\"char\""}
|
||||||
|
cideq | boolean | [0:1]={cid,cid}
|
||||||
|
circle_eq | boolean | [0:1]={circle,circle}
|
||||||
|
circle_ge | boolean | [0:1]={circle,circle}
|
||||||
|
circle_gt | boolean | [0:1]={circle,circle}
|
||||||
|
circle_le | boolean | [0:1]={circle,circle}
|
||||||
|
circle_lt | boolean | [0:1]={circle,circle}
|
||||||
|
circle_ne | boolean | [0:1]={circle,circle}
|
||||||
|
date_eq | boolean | [0:1]={date,date}
|
||||||
|
date_ge | boolean | [0:1]={date,date}
|
||||||
|
date_gt | boolean | [0:1]={date,date}
|
||||||
|
date_le | boolean | [0:1]={date,date}
|
||||||
|
date_lt | boolean | [0:1]={date,date}
|
||||||
|
date_ne | boolean | [0:1]={date,date}
|
||||||
|
float48eq | boolean | [0:1]={real,"double precision"}
|
||||||
|
float48ge | boolean | [0:1]={real,"double precision"}
|
||||||
|
float48gt | boolean | [0:1]={real,"double precision"}
|
||||||
|
float48le | boolean | [0:1]={real,"double precision"}
|
||||||
|
float48lt | boolean | [0:1]={real,"double precision"}
|
||||||
|
float48ne | boolean | [0:1]={real,"double precision"}
|
||||||
|
float4eq | boolean | [0:1]={real,real}
|
||||||
|
float4ge | boolean | [0:1]={real,real}
|
||||||
|
float4gt | boolean | [0:1]={real,real}
|
||||||
|
float4le | boolean | [0:1]={real,real}
|
||||||
|
float4lt | boolean | [0:1]={real,real}
|
||||||
|
float4ne | boolean | [0:1]={real,real}
|
||||||
|
float84eq | boolean | [0:1]={"double precision",real}
|
||||||
|
float84ge | boolean | [0:1]={"double precision",real}
|
||||||
|
float84gt | boolean | [0:1]={"double precision",real}
|
||||||
|
float84le | boolean | [0:1]={"double precision",real}
|
||||||
|
float84lt | boolean | [0:1]={"double precision",real}
|
||||||
|
float84ne | boolean | [0:1]={"double precision",real}
|
||||||
|
float8eq | boolean | [0:1]={"double precision","double precision"}
|
||||||
|
float8ge | boolean | [0:1]={"double precision","double precision"}
|
||||||
|
float8gt | boolean | [0:1]={"double precision","double precision"}
|
||||||
|
float8le | boolean | [0:1]={"double precision","double precision"}
|
||||||
|
float8lt | boolean | [0:1]={"double precision","double precision"}
|
||||||
|
float8ne | boolean | [0:1]={"double precision","double precision"}
|
||||||
|
int24eq | boolean | [0:1]={smallint,integer}
|
||||||
|
int24ge | boolean | [0:1]={smallint,integer}
|
||||||
|
int24gt | boolean | [0:1]={smallint,integer}
|
||||||
|
int24le | boolean | [0:1]={smallint,integer}
|
||||||
|
int24lt | boolean | [0:1]={smallint,integer}
|
||||||
|
int24ne | boolean | [0:1]={smallint,integer}
|
||||||
|
int28eq | boolean | [0:1]={smallint,bigint}
|
||||||
|
int28ge | boolean | [0:1]={smallint,bigint}
|
||||||
|
int28gt | boolean | [0:1]={smallint,bigint}
|
||||||
|
int28le | boolean | [0:1]={smallint,bigint}
|
||||||
|
int28lt | boolean | [0:1]={smallint,bigint}
|
||||||
|
int28ne | boolean | [0:1]={smallint,bigint}
|
||||||
|
int2eq | boolean | [0:1]={smallint,smallint}
|
||||||
|
int2ge | boolean | [0:1]={smallint,smallint}
|
||||||
|
int2gt | boolean | [0:1]={smallint,smallint}
|
||||||
|
int2le | boolean | [0:1]={smallint,smallint}
|
||||||
|
int2lt | boolean | [0:1]={smallint,smallint}
|
||||||
|
int2ne | boolean | [0:1]={smallint,smallint}
|
||||||
|
int42eq | boolean | [0:1]={integer,smallint}
|
||||||
|
int42ge | boolean | [0:1]={integer,smallint}
|
||||||
|
int42gt | boolean | [0:1]={integer,smallint}
|
||||||
|
int42le | boolean | [0:1]={integer,smallint}
|
||||||
|
int42lt | boolean | [0:1]={integer,smallint}
|
||||||
|
int42ne | boolean | [0:1]={integer,smallint}
|
||||||
|
int48eq | boolean | [0:1]={integer,bigint}
|
||||||
|
int48ge | boolean | [0:1]={integer,bigint}
|
||||||
|
int48gt | boolean | [0:1]={integer,bigint}
|
||||||
|
int48le | boolean | [0:1]={integer,bigint}
|
||||||
|
int48lt | boolean | [0:1]={integer,bigint}
|
||||||
|
int48ne | boolean | [0:1]={integer,bigint}
|
||||||
|
int4eq | boolean | [0:1]={integer,integer}
|
||||||
|
int4ge | boolean | [0:1]={integer,integer}
|
||||||
|
int4gt | boolean | [0:1]={integer,integer}
|
||||||
|
int4le | boolean | [0:1]={integer,integer}
|
||||||
|
int4lt | boolean | [0:1]={integer,integer}
|
||||||
|
int4ne | boolean | [0:1]={integer,integer}
|
||||||
|
int82eq | boolean | [0:1]={bigint,smallint}
|
||||||
|
int82ge | boolean | [0:1]={bigint,smallint}
|
||||||
|
int82gt | boolean | [0:1]={bigint,smallint}
|
||||||
|
int82le | boolean | [0:1]={bigint,smallint}
|
||||||
|
int82lt | boolean | [0:1]={bigint,smallint}
|
||||||
|
int82ne | boolean | [0:1]={bigint,smallint}
|
||||||
|
int84eq | boolean | [0:1]={bigint,integer}
|
||||||
|
int84ge | boolean | [0:1]={bigint,integer}
|
||||||
|
int84gt | boolean | [0:1]={bigint,integer}
|
||||||
|
int84le | boolean | [0:1]={bigint,integer}
|
||||||
|
int84lt | boolean | [0:1]={bigint,integer}
|
||||||
|
int84ne | boolean | [0:1]={bigint,integer}
|
||||||
|
int8eq | boolean | [0:1]={bigint,bigint}
|
||||||
|
int8ge | boolean | [0:1]={bigint,bigint}
|
||||||
|
int8gt | boolean | [0:1]={bigint,bigint}
|
||||||
|
int8le | boolean | [0:1]={bigint,bigint}
|
||||||
|
int8lt | boolean | [0:1]={bigint,bigint}
|
||||||
|
int8ne | boolean | [0:1]={bigint,bigint}
|
||||||
|
interval_eq | boolean | [0:1]={interval,interval}
|
||||||
|
interval_ge | boolean | [0:1]={interval,interval}
|
||||||
|
interval_gt | boolean | [0:1]={interval,interval}
|
||||||
|
interval_le | boolean | [0:1]={interval,interval}
|
||||||
|
interval_lt | boolean | [0:1]={interval,interval}
|
||||||
|
interval_ne | boolean | [0:1]={interval,interval}
|
||||||
|
lseg_eq | boolean | [0:1]={lseg,lseg}
|
||||||
|
lseg_ge | boolean | [0:1]={lseg,lseg}
|
||||||
|
lseg_gt | boolean | [0:1]={lseg,lseg}
|
||||||
|
lseg_le | boolean | [0:1]={lseg,lseg}
|
||||||
|
lseg_lt | boolean | [0:1]={lseg,lseg}
|
||||||
|
lseg_ne | boolean | [0:1]={lseg,lseg}
|
||||||
|
macaddr_eq | boolean | [0:1]={macaddr,macaddr}
|
||||||
|
macaddr_ge | boolean | [0:1]={macaddr,macaddr}
|
||||||
|
macaddr_gt | boolean | [0:1]={macaddr,macaddr}
|
||||||
|
macaddr_le | boolean | [0:1]={macaddr,macaddr}
|
||||||
|
macaddr_lt | boolean | [0:1]={macaddr,macaddr}
|
||||||
|
macaddr_ne | boolean | [0:1]={macaddr,macaddr}
|
||||||
|
nameeq | boolean | [0:1]={name,name}
|
||||||
|
namege | boolean | [0:1]={name,name}
|
||||||
|
namegt | boolean | [0:1]={name,name}
|
||||||
|
namele | boolean | [0:1]={name,name}
|
||||||
|
namelt | boolean | [0:1]={name,name}
|
||||||
|
namene | boolean | [0:1]={name,name}
|
||||||
|
network_eq | boolean | [0:1]={inet,inet}
|
||||||
|
network_ge | boolean | [0:1]={inet,inet}
|
||||||
|
network_gt | boolean | [0:1]={inet,inet}
|
||||||
|
network_le | boolean | [0:1]={inet,inet}
|
||||||
|
network_lt | boolean | [0:1]={inet,inet}
|
||||||
|
network_ne | boolean | [0:1]={inet,inet}
|
||||||
|
oideq | boolean | [0:1]={oid,oid}
|
||||||
|
oidge | boolean | [0:1]={oid,oid}
|
||||||
|
oidgt | boolean | [0:1]={oid,oid}
|
||||||
|
oidle | boolean | [0:1]={oid,oid}
|
||||||
|
oidlt | boolean | [0:1]={oid,oid}
|
||||||
|
oidne | boolean | [0:1]={oid,oid}
|
||||||
|
reltimeeq | boolean | [0:1]={reltime,reltime}
|
||||||
|
reltimege | boolean | [0:1]={reltime,reltime}
|
||||||
|
reltimegt | boolean | [0:1]={reltime,reltime}
|
||||||
|
reltimele | boolean | [0:1]={reltime,reltime}
|
||||||
|
reltimelt | boolean | [0:1]={reltime,reltime}
|
||||||
|
reltimene | boolean | [0:1]={reltime,reltime}
|
||||||
|
texteq | boolean | [0:1]={text,text}
|
||||||
|
textne | boolean | [0:1]={text,text}
|
||||||
|
tideq | boolean | [0:1]={tid,tid}
|
||||||
|
tidge | boolean | [0:1]={tid,tid}
|
||||||
|
tidgt | boolean | [0:1]={tid,tid}
|
||||||
|
tidle | boolean | [0:1]={tid,tid}
|
||||||
|
tidlt | boolean | [0:1]={tid,tid}
|
||||||
|
tidne | boolean | [0:1]={tid,tid}
|
||||||
|
time_eq | boolean | [0:1]={"time without time zone","time without time zone"}
|
||||||
|
time_ge | boolean | [0:1]={"time without time zone","time without time zone"}
|
||||||
|
time_gt | boolean | [0:1]={"time without time zone","time without time zone"}
|
||||||
|
time_le | boolean | [0:1]={"time without time zone","time without time zone"}
|
||||||
|
time_lt | boolean | [0:1]={"time without time zone","time without time zone"}
|
||||||
|
time_ne | boolean | [0:1]={"time without time zone","time without time zone"}
|
||||||
|
timestamp_eq | boolean | [0:1]={"timestamp without time zone","timestamp without time zone"}
|
||||||
|
timestamp_ge | boolean | [0:1]={"timestamp without time zone","timestamp without time zone"}
|
||||||
|
timestamp_gt | boolean | [0:1]={"timestamp without time zone","timestamp without time zone"}
|
||||||
|
timestamp_le | boolean | [0:1]={"timestamp without time zone","timestamp without time zone"}
|
||||||
|
timestamp_lt | boolean | [0:1]={"timestamp without time zone","timestamp without time zone"}
|
||||||
|
timestamp_ne | boolean | [0:1]={"timestamp without time zone","timestamp without time zone"}
|
||||||
|
timestamptz_eq | boolean | [0:1]={"timestamp with time zone","timestamp with time zone"}
|
||||||
|
timestamptz_ge | boolean | [0:1]={"timestamp with time zone","timestamp with time zone"}
|
||||||
|
timestamptz_gt | boolean | [0:1]={"timestamp with time zone","timestamp with time zone"}
|
||||||
|
timestamptz_le | boolean | [0:1]={"timestamp with time zone","timestamp with time zone"}
|
||||||
|
timestamptz_lt | boolean | [0:1]={"timestamp with time zone","timestamp with time zone"}
|
||||||
|
timestamptz_ne | boolean | [0:1]={"timestamp with time zone","timestamp with time zone"}
|
||||||
|
timetz_eq | boolean | [0:1]={"time with time zone","time with time zone"}
|
||||||
|
timetz_ge | boolean | [0:1]={"time with time zone","time with time zone"}
|
||||||
|
timetz_gt | boolean | [0:1]={"time with time zone","time with time zone"}
|
||||||
|
timetz_le | boolean | [0:1]={"time with time zone","time with time zone"}
|
||||||
|
timetz_lt | boolean | [0:1]={"time with time zone","time with time zone"}
|
||||||
|
timetz_ne | boolean | [0:1]={"time with time zone","time with time zone"}
|
||||||
|
tintervaleq | boolean | [0:1]={tinterval,tinterval}
|
||||||
|
tintervalge | boolean | [0:1]={tinterval,tinterval}
|
||||||
|
tintervalgt | boolean | [0:1]={tinterval,tinterval}
|
||||||
|
tintervalle | boolean | [0:1]={tinterval,tinterval}
|
||||||
|
tintervalleneq | boolean | [0:1]={tinterval,reltime}
|
||||||
|
tintervallenge | boolean | [0:1]={tinterval,reltime}
|
||||||
|
tintervallengt | boolean | [0:1]={tinterval,reltime}
|
||||||
|
tintervallenle | boolean | [0:1]={tinterval,reltime}
|
||||||
|
tintervallenlt | boolean | [0:1]={tinterval,reltime}
|
||||||
|
tintervallenne | boolean | [0:1]={tinterval,reltime}
|
||||||
|
tintervallt | boolean | [0:1]={tinterval,tinterval}
|
||||||
|
tintervalne | boolean | [0:1]={tinterval,tinterval}
|
||||||
|
uuid_eq | boolean | [0:1]={uuid,uuid}
|
||||||
|
uuid_ge | boolean | [0:1]={uuid,uuid}
|
||||||
|
uuid_gt | boolean | [0:1]={uuid,uuid}
|
||||||
|
uuid_le | boolean | [0:1]={uuid,uuid}
|
||||||
|
uuid_lt | boolean | [0:1]={uuid,uuid}
|
||||||
|
uuid_ne | boolean | [0:1]={uuid,uuid}
|
||||||
|
varbiteq | boolean | [0:1]={"bit varying","bit varying"}
|
||||||
|
varbitge | boolean | [0:1]={"bit varying","bit varying"}
|
||||||
|
varbitgt | boolean | [0:1]={"bit varying","bit varying"}
|
||||||
|
varbitle | boolean | [0:1]={"bit varying","bit varying"}
|
||||||
|
varbitlt | boolean | [0:1]={"bit varying","bit varying"}
|
||||||
|
varbitne | boolean | [0:1]={"bit varying","bit varying"}
|
||||||
|
xideq | boolean | [0:1]={xid,xid}
|
||||||
|
(228 rows)
|
||||||
|
|
||||||
|
--
|
||||||
|
-- CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
|
||||||
|
--
|
||||||
|
CREATE FUNCTION functext_F_1(int) RETURNS bool LANGUAGE 'sql'
|
||||||
|
AS 'SELECT $1 > 50';
|
||||||
|
CREATE FUNCTION functext_F_2(int) RETURNS bool LANGUAGE 'sql'
|
||||||
|
CALLED ON NULL INPUT AS 'SELECT $1 = 50';
|
||||||
|
CREATE FUNCTION functext_F_3(int) RETURNS bool LANGUAGE 'sql'
|
||||||
|
RETURNS NULL ON NULL INPUT AS 'SELECT $1 < 50';
|
||||||
|
CREATE FUNCTION functext_F_4(int) RETURNS bool LANGUAGE 'sql'
|
||||||
|
STRICT AS 'SELECT $1 = 50';
|
||||||
|
SELECT proname, proisstrict FROM pg_proc
|
||||||
|
WHERE oid in ('functext_F_1'::regproc,
|
||||||
|
'functext_F_2'::regproc,
|
||||||
|
'functext_F_3'::regproc,
|
||||||
|
'functext_F_4'::regproc);
|
||||||
|
proname | proisstrict
|
||||||
|
--------------+-------------
|
||||||
|
functext_f_1 | f
|
||||||
|
functext_f_2 | f
|
||||||
|
functext_f_3 | t
|
||||||
|
functext_f_4 | t
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
ALTER FUNCTION functext_F_1(int) IMMUTABLE; -- unrelated change, no effect
|
||||||
|
ALTER FUNCTION functext_F_2(int) STRICT;
|
||||||
|
ALTER FUNCTION functext_F_3(int) CALLED ON NULL INPUT;
|
||||||
|
SELECT proname, proisstrict FROM pg_proc
|
||||||
|
WHERE oid in ('functext_F_1'::regproc,
|
||||||
|
'functext_F_2'::regproc,
|
||||||
|
'functext_F_3'::regproc,
|
||||||
|
'functext_F_4'::regproc);
|
||||||
|
proname | proisstrict
|
||||||
|
--------------+-------------
|
||||||
|
functext_f_1 | f
|
||||||
|
functext_f_2 | t
|
||||||
|
functext_f_3 | f
|
||||||
|
functext_f_4 | t
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
-- Cleanups
|
||||||
|
DROP SCHEMA temp_func_test CASCADE;
|
||||||
|
NOTICE: drop cascades to 19 other objects
|
||||||
|
DETAIL: drop cascades to function functest_a_1(text,date)
|
||||||
|
drop cascades to function functest_a_2(text[])
|
||||||
|
drop cascades to function functest_a_3()
|
||||||
|
drop cascades to function functest_b_1(integer)
|
||||||
|
drop cascades to function functest_b_2(integer)
|
||||||
|
drop cascades to function functest_b_3(integer)
|
||||||
|
drop cascades to function functest_b_4(integer)
|
||||||
|
drop cascades to function functext_c_1(integer)
|
||||||
|
drop cascades to function functext_c_2(integer)
|
||||||
|
drop cascades to function functext_c_3(integer)
|
||||||
|
drop cascades to function functext_d_1(integer,integer)
|
||||||
|
drop cascades to function functext_d_2(integer,integer)
|
||||||
|
drop cascades to function functext_d_3(integer,integer)
|
||||||
|
drop cascades to function functext_e_1(integer)
|
||||||
|
drop cascades to function functext_e_2(integer)
|
||||||
|
drop cascades to function functext_f_1(integer)
|
||||||
|
drop cascades to function functext_f_2(integer)
|
||||||
|
drop cascades to function functext_f_3(integer)
|
||||||
|
drop cascades to function functext_f_4(integer)
|
||||||
|
RESET search_path;
|
@ -1397,7 +1397,8 @@ EXPLAIN (COSTS OFF) SELECT * FROM my_credit_card_secure WHERE f_leak(cnum);
|
|||||||
|
|
||||||
--
|
--
|
||||||
-- scenario: an external qualifier can be pushed-down by in-front-of the
|
-- scenario: an external qualifier can be pushed-down by in-front-of the
|
||||||
-- views with "security_barrier" attribute
|
-- views with "security_barrier" attribute, except for operators
|
||||||
|
-- implemented with leakproof functions.
|
||||||
--
|
--
|
||||||
SELECT * FROM my_credit_card_usage_normal
|
SELECT * FROM my_credit_card_usage_normal
|
||||||
WHERE f_leak(cnum) AND ymd >= '2011-10-01' AND ymd < '2011-11-01';
|
WHERE f_leak(cnum) AND ymd >= '2011-10-01' AND ymd < '2011-11-01';
|
||||||
@ -1432,8 +1433,6 @@ SELECT * FROM my_credit_card_usage_secure
|
|||||||
WHERE f_leak(cnum) AND ymd >= '2011-10-01' AND ymd < '2011-11-01';
|
WHERE f_leak(cnum) AND ymd >= '2011-10-01' AND ymd < '2011-11-01';
|
||||||
NOTICE: f_leak => 1111-2222-3333-4444
|
NOTICE: f_leak => 1111-2222-3333-4444
|
||||||
NOTICE: f_leak => 1111-2222-3333-4444
|
NOTICE: f_leak => 1111-2222-3333-4444
|
||||||
NOTICE: f_leak => 1111-2222-3333-4444
|
|
||||||
NOTICE: f_leak => 1111-2222-3333-4444
|
|
||||||
NOTICE: f_leak => 1111-2222-3333-4444
|
NOTICE: f_leak => 1111-2222-3333-4444
|
||||||
cid | name | tel | passwd | cnum | climit | ymd | usage
|
cid | name | tel | passwd | cnum | climit | ymd | usage
|
||||||
-----+---------------+------------------+-----------+---------------------+--------+------------+-------
|
-----+---------------+------------------+-----------+---------------------+--------+------------+-------
|
||||||
@ -1444,21 +1443,22 @@ NOTICE: f_leak => 1111-2222-3333-4444
|
|||||||
|
|
||||||
EXPLAIN (COSTS OFF) SELECT * FROM my_credit_card_usage_secure
|
EXPLAIN (COSTS OFF) SELECT * FROM my_credit_card_usage_secure
|
||||||
WHERE f_leak(cnum) AND ymd >= '2011-10-01' AND ymd < '2011-11-01';
|
WHERE f_leak(cnum) AND ymd >= '2011-10-01' AND ymd < '2011-11-01';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
------------------------------------------------------------------------------------
|
||||||
Subquery Scan on my_credit_card_usage_secure
|
Subquery Scan on my_credit_card_usage_secure
|
||||||
Filter: (f_leak(my_credit_card_usage_secure.cnum) AND (my_credit_card_usage_secure.ymd >= '10-01-2011'::date) AND (my_credit_card_usage_secure.ymd < '11-01-2011'::date))
|
Filter: f_leak(my_credit_card_usage_secure.cnum)
|
||||||
-> Hash Join
|
-> Nested Loop
|
||||||
Hash Cond: (r.cid = l.cid)
|
Join Filter: (l.cid = r.cid)
|
||||||
-> Seq Scan on credit_usage r
|
-> Seq Scan on credit_usage r
|
||||||
-> Hash
|
Filter: ((ymd >= '10-01-2011'::date) AND (ymd < '11-01-2011'::date))
|
||||||
|
-> Materialize
|
||||||
-> Hash Join
|
-> Hash Join
|
||||||
Hash Cond: (r.cid = l.cid)
|
Hash Cond: (r.cid = l.cid)
|
||||||
-> Seq Scan on credit_card r
|
-> Seq Scan on credit_card r
|
||||||
-> Hash
|
-> Hash
|
||||||
-> Seq Scan on customer l
|
-> Seq Scan on customer l
|
||||||
Filter: (name = ("current_user"())::text)
|
Filter: (name = ("current_user"())::text)
|
||||||
(12 rows)
|
(13 rows)
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Test for the case when security_barrier gets changed between rewriter
|
-- Test for the case when security_barrier gets changed between rewriter
|
||||||
|
@ -1397,7 +1397,8 @@ EXPLAIN (COSTS OFF) SELECT * FROM my_credit_card_secure WHERE f_leak(cnum);
|
|||||||
|
|
||||||
--
|
--
|
||||||
-- scenario: an external qualifier can be pushed-down by in-front-of the
|
-- scenario: an external qualifier can be pushed-down by in-front-of the
|
||||||
-- views with "security_barrier" attribute
|
-- views with "security_barrier" attribute, except for operators
|
||||||
|
-- implemented with leakproof functions.
|
||||||
--
|
--
|
||||||
SELECT * FROM my_credit_card_usage_normal
|
SELECT * FROM my_credit_card_usage_normal
|
||||||
WHERE f_leak(cnum) AND ymd >= '2011-10-01' AND ymd < '2011-11-01';
|
WHERE f_leak(cnum) AND ymd >= '2011-10-01' AND ymd < '2011-11-01';
|
||||||
@ -1432,8 +1433,6 @@ SELECT * FROM my_credit_card_usage_secure
|
|||||||
WHERE f_leak(cnum) AND ymd >= '2011-10-01' AND ymd < '2011-11-01';
|
WHERE f_leak(cnum) AND ymd >= '2011-10-01' AND ymd < '2011-11-01';
|
||||||
NOTICE: f_leak => 1111-2222-3333-4444
|
NOTICE: f_leak => 1111-2222-3333-4444
|
||||||
NOTICE: f_leak => 1111-2222-3333-4444
|
NOTICE: f_leak => 1111-2222-3333-4444
|
||||||
NOTICE: f_leak => 1111-2222-3333-4444
|
|
||||||
NOTICE: f_leak => 1111-2222-3333-4444
|
|
||||||
NOTICE: f_leak => 1111-2222-3333-4444
|
NOTICE: f_leak => 1111-2222-3333-4444
|
||||||
cid | name | tel | passwd | cnum | climit | ymd | usage
|
cid | name | tel | passwd | cnum | climit | ymd | usage
|
||||||
-----+---------------+------------------+-----------+---------------------+--------+------------+-------
|
-----+---------------+------------------+-----------+---------------------+--------+------------+-------
|
||||||
@ -1444,21 +1443,22 @@ NOTICE: f_leak => 1111-2222-3333-4444
|
|||||||
|
|
||||||
EXPLAIN (COSTS OFF) SELECT * FROM my_credit_card_usage_secure
|
EXPLAIN (COSTS OFF) SELECT * FROM my_credit_card_usage_secure
|
||||||
WHERE f_leak(cnum) AND ymd >= '2011-10-01' AND ymd < '2011-11-01';
|
WHERE f_leak(cnum) AND ymd >= '2011-10-01' AND ymd < '2011-11-01';
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
------------------------------------------------------------------------------------
|
||||||
Subquery Scan on my_credit_card_usage_secure
|
Subquery Scan on my_credit_card_usage_secure
|
||||||
Filter: (f_leak(my_credit_card_usage_secure.cnum) AND (my_credit_card_usage_secure.ymd >= '10-01-2011'::date) AND (my_credit_card_usage_secure.ymd < '11-01-2011'::date))
|
Filter: f_leak(my_credit_card_usage_secure.cnum)
|
||||||
-> Hash Join
|
-> Nested Loop
|
||||||
Hash Cond: (r.cid = l.cid)
|
Join Filter: (l.cid = r.cid)
|
||||||
-> Seq Scan on credit_usage r
|
-> Seq Scan on credit_usage r
|
||||||
-> Hash
|
Filter: ((ymd >= '10-01-2011'::date) AND (ymd < '11-01-2011'::date))
|
||||||
|
-> Materialize
|
||||||
-> Hash Join
|
-> Hash Join
|
||||||
Hash Cond: (r.cid = l.cid)
|
Hash Cond: (r.cid = l.cid)
|
||||||
-> Seq Scan on credit_card r
|
-> Seq Scan on credit_card r
|
||||||
-> Hash
|
-> Hash
|
||||||
-> Seq Scan on customer l
|
-> Seq Scan on customer l
|
||||||
Filter: (name = ("current_user"())::text)
|
Filter: (name = ("current_user"())::text)
|
||||||
(12 rows)
|
(13 rows)
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Test for the case when security_barrier gets changed between rewriter
|
-- Test for the case when security_barrier gets changed between rewriter
|
||||||
|
@ -40,6 +40,7 @@ test: create_function_1
|
|||||||
test: create_type
|
test: create_type
|
||||||
test: create_table
|
test: create_table
|
||||||
test: create_function_2
|
test: create_function_2
|
||||||
|
test: create_function_3
|
||||||
|
|
||||||
# ----------
|
# ----------
|
||||||
# Load huge amounts of data
|
# Load huge amounts of data
|
||||||
|
@ -50,6 +50,7 @@ test: create_function_1
|
|||||||
test: create_type
|
test: create_type
|
||||||
test: create_table
|
test: create_table
|
||||||
test: create_function_2
|
test: create_function_2
|
||||||
|
test: create_function_3
|
||||||
test: copy
|
test: copy
|
||||||
test: copyselect
|
test: copyselect
|
||||||
test: create_misc
|
test: create_misc
|
||||||
|
145
src/test/regress/sql/create_function_3.sql
Normal file
145
src/test/regress/sql/create_function_3.sql
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
--
|
||||||
|
-- CREATE FUNCTION
|
||||||
|
--
|
||||||
|
-- sanity check of pg_proc catalog to the given parameters
|
||||||
|
--
|
||||||
|
CREATE SCHEMA temp_func_test;
|
||||||
|
|
||||||
|
SET search_path TO temp_func_test, public;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- ARGUMENT and RETURN TYPES
|
||||||
|
--
|
||||||
|
CREATE FUNCTION functest_A_1(text, date) RETURNS bool LANGUAGE 'sql'
|
||||||
|
AS 'SELECT $1 = ''abcd'' AND $2 > ''2001-01-01''';
|
||||||
|
CREATE FUNCTION functest_A_2(text[]) RETURNS int LANGUAGE 'sql'
|
||||||
|
AS 'SELECT $1[0]::int';
|
||||||
|
CREATE FUNCTION functest_A_3() RETURNS bool LANGUAGE 'sql'
|
||||||
|
AS 'SELECT false';
|
||||||
|
SELECT proname, prorettype::regtype, proargtypes::regtype[] FROM pg_proc
|
||||||
|
WHERE oid in ('functest_A_1'::regproc,
|
||||||
|
'functest_A_2'::regproc,
|
||||||
|
'functest_A_3'::regproc);
|
||||||
|
|
||||||
|
--
|
||||||
|
-- IMMUTABLE | STABLE | VOLATILE
|
||||||
|
--
|
||||||
|
CREATE FUNCTION functest_B_1(int) RETURNS bool LANGUAGE 'sql'
|
||||||
|
AS 'SELECT $1 > 0';
|
||||||
|
CREATE FUNCTION functest_B_2(int) RETURNS bool LANGUAGE 'sql'
|
||||||
|
IMMUTABLE AS 'SELECT $1 > 0';
|
||||||
|
CREATE FUNCTION functest_B_3(int) RETURNS bool LANGUAGE 'sql'
|
||||||
|
STABLE AS 'SELECT $1 = 0';
|
||||||
|
CREATE FUNCTION functest_B_4(int) RETURNS bool LANGUAGE 'sql'
|
||||||
|
VOLATILE AS 'SELECT $1 < 0';
|
||||||
|
SELECT proname, provolatile FROM pg_proc
|
||||||
|
WHERE oid in ('functest_B_1'::regproc,
|
||||||
|
'functest_B_2'::regproc,
|
||||||
|
'functest_B_3'::regproc,
|
||||||
|
'functest_B_4'::regproc);
|
||||||
|
|
||||||
|
ALTER FUNCTION functest_B_2(int) VOLATILE;
|
||||||
|
ALTER FUNCTION functest_B_3(int) COST 100; -- unrelated change, no effect
|
||||||
|
SELECT proname, provolatile FROM pg_proc
|
||||||
|
WHERE oid in ('functest_B_1'::regproc,
|
||||||
|
'functest_B_2'::regproc,
|
||||||
|
'functest_B_3'::regproc,
|
||||||
|
'functest_B_4'::regproc);
|
||||||
|
|
||||||
|
--
|
||||||
|
-- SECURITY DEFINER | INVOKER
|
||||||
|
--
|
||||||
|
CREATE FUNCTION functext_C_1(int) RETURNS bool LANGUAGE 'sql'
|
||||||
|
AS 'SELECT $1 > 0';
|
||||||
|
CREATE FUNCTION functext_C_2(int) RETURNS bool LANGUAGE 'sql'
|
||||||
|
SECURITY DEFINER AS 'SELECT $1 = 0';
|
||||||
|
CREATE FUNCTION functext_C_3(int) RETURNS bool LANGUAGE 'sql'
|
||||||
|
SECURITY INVOKER AS 'SELECT $1 < 0';
|
||||||
|
SELECT proname, prosecdef FROM pg_proc
|
||||||
|
WHERE oid in ('functext_C_1'::regproc,
|
||||||
|
'functext_C_2'::regproc,
|
||||||
|
'functext_C_3'::regproc);
|
||||||
|
|
||||||
|
ALTER FUNCTION functext_C_1(int) IMMUTABLE; -- unrelated change, no effect
|
||||||
|
ALTER FUNCTION functext_C_2(int) SECURITY INVOKER;
|
||||||
|
ALTER FUNCTION functext_C_3(int) SECURITY DEFINER;
|
||||||
|
SELECT proname, prosecdef FROM pg_proc
|
||||||
|
WHERE oid in ('functext_C_1'::regproc,
|
||||||
|
'functext_C_2'::regproc,
|
||||||
|
'functext_C_3'::regproc);
|
||||||
|
|
||||||
|
--
|
||||||
|
-- COST
|
||||||
|
--
|
||||||
|
CREATE FUNCTION functext_D_1(int,int) RETURNS int LANGUAGE 'sql'
|
||||||
|
AS 'SELECT $1 + $2';
|
||||||
|
CREATE FUNCTION functext_D_2(int,int) RETURNS int LANGUAGE 'internal'
|
||||||
|
AS 'int4pl';
|
||||||
|
CREATE FUNCTION functext_D_3(int,int) RETURNS int LANGUAGE 'sql'
|
||||||
|
COST 500 AS 'SELECT $1 * $2';
|
||||||
|
CREATE FUNCTION functext_D_4(int,int) RETURNS int LANGUAGE 'sql'
|
||||||
|
COST 0 AS 'SELECT $1 / $2'; -- Error
|
||||||
|
SELECT proname, procost FROM pg_proc
|
||||||
|
WHERE oid in ('functext_D_1'::regproc,
|
||||||
|
'functext_D_2'::regproc,
|
||||||
|
'functext_D_3'::regproc);
|
||||||
|
|
||||||
|
ALTER FUNCTION functext_D_1(int,int) STABLE; -- unrelated change, no effect
|
||||||
|
ALTER FUNCTION functext_D_2(int,int) COST 50;
|
||||||
|
ALTER FUNCTION functext_D_3(int,int) COST 0.0001;
|
||||||
|
SELECT proname, procost FROM pg_proc
|
||||||
|
WHERE oid in ('functext_D_1'::regproc,
|
||||||
|
'functext_D_2'::regproc,
|
||||||
|
'functext_D_3'::regproc);
|
||||||
|
|
||||||
|
--
|
||||||
|
-- LEAKPROOF
|
||||||
|
--
|
||||||
|
CREATE FUNCTION functext_E_1(int) RETURNS bool LANGUAGE 'sql'
|
||||||
|
AS 'SELECT $1 > 100';
|
||||||
|
CREATE FUNCTION functext_E_2(int) RETURNS bool LANGUAGE 'sql'
|
||||||
|
LEAKPROOF AS 'SELECT $1 > 100';
|
||||||
|
SELECT proname, proleakproof FROM pg_proc
|
||||||
|
WHERE oid in ('functext_E_1'::regproc,
|
||||||
|
'functext_E_2'::regproc);
|
||||||
|
|
||||||
|
ALTER FUNCTION functext_E_1(int) LEAKPROOF;
|
||||||
|
ALTER FUNCTION functext_E_2(int) STABLE; -- unrelated change, no effect
|
||||||
|
SELECT proname, proleakproof FROM pg_proc
|
||||||
|
WHERE oid in ('functext_E_1'::regproc,
|
||||||
|
'functext_E_2'::regproc);
|
||||||
|
|
||||||
|
-- list of built-in leakproof functions
|
||||||
|
SELECT proname, prorettype::regtype, proargtypes::regtype[]
|
||||||
|
FROM pg_proc JOIN pg_namespace ON pronamespace = pg_namespace.oid
|
||||||
|
WHERE nspname = 'pg_catalog' AND proleakproof ORDER BY proname;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
|
||||||
|
--
|
||||||
|
CREATE FUNCTION functext_F_1(int) RETURNS bool LANGUAGE 'sql'
|
||||||
|
AS 'SELECT $1 > 50';
|
||||||
|
CREATE FUNCTION functext_F_2(int) RETURNS bool LANGUAGE 'sql'
|
||||||
|
CALLED ON NULL INPUT AS 'SELECT $1 = 50';
|
||||||
|
CREATE FUNCTION functext_F_3(int) RETURNS bool LANGUAGE 'sql'
|
||||||
|
RETURNS NULL ON NULL INPUT AS 'SELECT $1 < 50';
|
||||||
|
CREATE FUNCTION functext_F_4(int) RETURNS bool LANGUAGE 'sql'
|
||||||
|
STRICT AS 'SELECT $1 = 50';
|
||||||
|
SELECT proname, proisstrict FROM pg_proc
|
||||||
|
WHERE oid in ('functext_F_1'::regproc,
|
||||||
|
'functext_F_2'::regproc,
|
||||||
|
'functext_F_3'::regproc,
|
||||||
|
'functext_F_4'::regproc);
|
||||||
|
|
||||||
|
ALTER FUNCTION functext_F_1(int) IMMUTABLE; -- unrelated change, no effect
|
||||||
|
ALTER FUNCTION functext_F_2(int) STRICT;
|
||||||
|
ALTER FUNCTION functext_F_3(int) CALLED ON NULL INPUT;
|
||||||
|
SELECT proname, proisstrict FROM pg_proc
|
||||||
|
WHERE oid in ('functext_F_1'::regproc,
|
||||||
|
'functext_F_2'::regproc,
|
||||||
|
'functext_F_3'::regproc,
|
||||||
|
'functext_F_4'::regproc);
|
||||||
|
|
||||||
|
-- Cleanups
|
||||||
|
DROP SCHEMA temp_func_test CASCADE;
|
||||||
|
RESET search_path;
|
@ -108,7 +108,8 @@ EXPLAIN (COSTS OFF) SELECT * FROM my_credit_card_secure WHERE f_leak(cnum);
|
|||||||
|
|
||||||
--
|
--
|
||||||
-- scenario: an external qualifier can be pushed-down by in-front-of the
|
-- scenario: an external qualifier can be pushed-down by in-front-of the
|
||||||
-- views with "security_barrier" attribute
|
-- views with "security_barrier" attribute, except for operators
|
||||||
|
-- implemented with leakproof functions.
|
||||||
--
|
--
|
||||||
SELECT * FROM my_credit_card_usage_normal
|
SELECT * FROM my_credit_card_usage_normal
|
||||||
WHERE f_leak(cnum) AND ymd >= '2011-10-01' AND ymd < '2011-11-01';
|
WHERE f_leak(cnum) AND ymd >= '2011-10-01' AND ymd < '2011-11-01';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user