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>
|
||||
</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>
|
||||
<entry><structfield>proisstrict</structfield></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>
|
||||
|
||||
CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
|
||||
IMMUTABLE | STABLE | VOLATILE
|
||||
IMMUTABLE | STABLE | VOLATILE | LEAKPROOF
|
||||
[ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
|
||||
COST <replaceable class="parameter">execution_cost</replaceable>
|
||||
ROWS <replaceable class="parameter">result_rows</replaceable>
|
||||
@ -191,6 +191,17 @@ ALTER FUNCTION <replaceable>name</replaceable> ( [ [ <replaceable class="paramet
|
||||
</listitem>
|
||||
</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>
|
||||
<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> [, ...] ) ]
|
||||
{ LANGUAGE <replaceable class="parameter">lang_name</replaceable>
|
||||
| WINDOW
|
||||
| IMMUTABLE | STABLE | VOLATILE
|
||||
| IMMUTABLE | STABLE | VOLATILE | LEAKPROOF
|
||||
| CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
|
||||
| [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
|
||||
| COST <replaceable class="parameter">execution_cost</replaceable>
|
||||
@ -324,6 +324,23 @@ CREATE [ OR REPLACE ] FUNCTION
|
||||
</listitem>
|
||||
</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>
|
||||
<term><literal>CALLED 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.
|
||||
</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>
|
||||
It is important to understand that even a view created with the
|
||||
<literal>security_barrier</literal> option is intended to be secure only
|
||||
|
@ -241,6 +241,7 @@ AggregateCreate(const char *aggName,
|
||||
false, /* isWindowFunc */
|
||||
false, /* security invoker (currently not
|
||||
* definable for agg) */
|
||||
false, /* isLeakProof */
|
||||
false, /* isStrict (not needed for agg) */
|
||||
PROVOLATILE_IMMUTABLE, /* volatility (not
|
||||
* needed for agg) */
|
||||
|
@ -76,6 +76,7 @@ ProcedureCreate(const char *procedureName,
|
||||
bool isAgg,
|
||||
bool isWindowFunc,
|
||||
bool security_definer,
|
||||
bool isLeakProof,
|
||||
bool isStrict,
|
||||
char volatility,
|
||||
oidvector *parameterTypes,
|
||||
@ -334,6 +335,7 @@ ProcedureCreate(const char *procedureName,
|
||||
values[Anum_pg_proc_proisagg - 1] = BoolGetDatum(isAgg);
|
||||
values[Anum_pg_proc_proiswindow - 1] = BoolGetDatum(isWindowFunc);
|
||||
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_proretset - 1] = BoolGetDatum(returnsSet);
|
||||
values[Anum_pg_proc_provolatile - 1] = CharGetDatum(volatility);
|
||||
|
@ -446,6 +446,7 @@ compute_common_attribute(DefElem *defel,
|
||||
DefElem **volatility_item,
|
||||
DefElem **strict_item,
|
||||
DefElem **security_item,
|
||||
DefElem **leakproof_item,
|
||||
List **set_items,
|
||||
DefElem **cost_item,
|
||||
DefElem **rows_item)
|
||||
@ -471,6 +472,13 @@ compute_common_attribute(DefElem *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)
|
||||
{
|
||||
*set_items = lappend(*set_items, defel->arg);
|
||||
@ -564,6 +572,7 @@ compute_attributes_sql_style(List *options,
|
||||
char *volatility_p,
|
||||
bool *strict_p,
|
||||
bool *security_definer,
|
||||
bool *leakproof_p,
|
||||
ArrayType **proconfig,
|
||||
float4 *procost,
|
||||
float4 *prorows)
|
||||
@ -575,6 +584,7 @@ compute_attributes_sql_style(List *options,
|
||||
DefElem *volatility_item = NULL;
|
||||
DefElem *strict_item = NULL;
|
||||
DefElem *security_item = NULL;
|
||||
DefElem *leakproof_item = NULL;
|
||||
List *set_items = NIL;
|
||||
DefElem *cost_item = NULL;
|
||||
DefElem *rows_item = NULL;
|
||||
@ -611,6 +621,7 @@ compute_attributes_sql_style(List *options,
|
||||
&volatility_item,
|
||||
&strict_item,
|
||||
&security_item,
|
||||
&leakproof_item,
|
||||
&set_items,
|
||||
&cost_item,
|
||||
&rows_item))
|
||||
@ -653,6 +664,8 @@ compute_attributes_sql_style(List *options,
|
||||
*strict_p = intVal(strict_item->arg);
|
||||
if (security_item)
|
||||
*security_definer = intVal(security_item->arg);
|
||||
if (leakproof_item)
|
||||
*leakproof_p = intVal(leakproof_item->arg);
|
||||
if (set_items)
|
||||
*proconfig = update_proconfig_value(NULL, set_items);
|
||||
if (cost_item)
|
||||
@ -805,7 +818,8 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
|
||||
Oid requiredResultType;
|
||||
bool isWindowFunc,
|
||||
isStrict,
|
||||
security;
|
||||
security,
|
||||
isLeakProof;
|
||||
char volatility;
|
||||
ArrayType *proconfig;
|
||||
float4 procost;
|
||||
@ -828,6 +842,7 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
|
||||
isWindowFunc = false;
|
||||
isStrict = false;
|
||||
security = false;
|
||||
isLeakProof = false;
|
||||
volatility = PROVOLATILE_VOLATILE;
|
||||
proconfig = NULL;
|
||||
procost = -1; /* indicates not set */
|
||||
@ -837,7 +852,7 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
|
||||
compute_attributes_sql_style(stmt->options,
|
||||
&as_clause, &language,
|
||||
&isWindowFunc, &volatility,
|
||||
&isStrict, &security,
|
||||
&isStrict, &security, &isLeakProof,
|
||||
&proconfig, &procost, &prorows);
|
||||
|
||||
/* Look up the language and validate permissions */
|
||||
@ -874,6 +889,16 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
|
||||
|
||||
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
|
||||
* ProcedureCreate.
|
||||
@ -960,6 +985,7 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
|
||||
false, /* not an aggregate */
|
||||
isWindowFunc,
|
||||
security,
|
||||
isLeakProof,
|
||||
isStrict,
|
||||
volatility,
|
||||
parameterTypes,
|
||||
@ -1238,6 +1264,7 @@ AlterFunction(AlterFunctionStmt *stmt)
|
||||
DefElem *volatility_item = NULL;
|
||||
DefElem *strict_item = NULL;
|
||||
DefElem *security_def_item = NULL;
|
||||
DefElem *leakproof_item = NULL;
|
||||
List *set_items = NIL;
|
||||
DefElem *cost_item = NULL;
|
||||
DefElem *rows_item = NULL;
|
||||
@ -1274,6 +1301,7 @@ AlterFunction(AlterFunctionStmt *stmt)
|
||||
&volatility_item,
|
||||
&strict_item,
|
||||
&security_def_item,
|
||||
&leakproof_item,
|
||||
&set_items,
|
||||
&cost_item,
|
||||
&rows_item) == false)
|
||||
@ -1286,6 +1314,14 @@ AlterFunction(AlterFunctionStmt *stmt)
|
||||
procForm->proisstrict = intVal(strict_item->arg);
|
||||
if (security_def_item)
|
||||
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)
|
||||
{
|
||||
procForm->procost = defGetNumeric(cost_item);
|
||||
|
@ -131,6 +131,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
||||
false, /* isAgg */
|
||||
false, /* isWindowFunc */
|
||||
false, /* security_definer */
|
||||
false, /* isLeakProof */
|
||||
false, /* isStrict */
|
||||
PROVOLATILE_VOLATILE,
|
||||
buildoidvector(funcargtypes, 0),
|
||||
@ -166,6 +167,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
||||
false, /* isAgg */
|
||||
false, /* isWindowFunc */
|
||||
false, /* security_definer */
|
||||
false, /* isLeakProof */
|
||||
true, /* isStrict */
|
||||
PROVOLATILE_VOLATILE,
|
||||
buildoidvector(funcargtypes, 1),
|
||||
@ -204,6 +206,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
||||
false, /* isAgg */
|
||||
false, /* isWindowFunc */
|
||||
false, /* security_definer */
|
||||
false, /* isLeakProof */
|
||||
true, /* isStrict */
|
||||
PROVOLATILE_VOLATILE,
|
||||
buildoidvector(funcargtypes, 1),
|
||||
|
@ -1521,6 +1521,7 @@ makeRangeConstructors(const char *name, Oid namespace,
|
||||
false, /* isAgg */
|
||||
false, /* isWindowFunc */
|
||||
false, /* security_definer */
|
||||
false, /* leakproof */
|
||||
false, /* isStrict */
|
||||
PROVOLATILE_IMMUTABLE, /* volatility */
|
||||
constructorArgTypesVector, /* parameterTypes */
|
||||
|
@ -1042,16 +1042,9 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
||||
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
|
||||
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 &&
|
||||
!rte->security_barrier &&
|
||||
(!rte->security_barrier ||
|
||||
!contain_leaky_functions(clause)) &&
|
||||
qual_is_pushdown_safe(subquery, rti, clause, differentTypes))
|
||||
{
|
||||
/* 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_volatile_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 List *find_nonnullable_vars_walker(Node *node, bool top_level);
|
||||
static bool is_strict_saop(ScalarArrayOpExpr *expr, bool falseOK);
|
||||
@ -1129,6 +1130,145 @@ contain_nonstrict_functions_walker(Node *node, void *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
|
||||
|
@ -526,7 +526,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType,
|
||||
|
||||
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
|
||||
LOCATION LOCK_P
|
||||
|
||||
@ -6115,6 +6115,10 @@ common_func_opt_item:
|
||||
{
|
||||
$$ = makeDefElem("security", (Node *)makeInteger(FALSE));
|
||||
}
|
||||
| LEAKPROOF
|
||||
{
|
||||
$$ = makeDefElem("leakproof", (Node *)makeInteger(TRUE));
|
||||
}
|
||||
| COST NumericOnly
|
||||
{
|
||||
$$ = makeDefElem("cost", (Node *)$2);
|
||||
@ -12219,6 +12223,7 @@ unreserved_keyword:
|
||||
| LAST_P
|
||||
| LC_COLLATE_P
|
||||
| LC_CTYPE_P
|
||||
| LEAKPROOF
|
||||
| LEVEL
|
||||
| LISTEN
|
||||
| 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
* Given procedure id, return the function's procost field.
|
||||
|
@ -9071,6 +9071,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
||||
char *provolatile;
|
||||
char *proisstrict;
|
||||
char *prosecdef;
|
||||
char *proleakproof;
|
||||
char *proconfig;
|
||||
char *procost;
|
||||
char *prorows;
|
||||
@ -9098,7 +9099,24 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
||||
selectSourceSchema(fout, finfo->dobj.namespace->dobj.name);
|
||||
|
||||
/* 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
|
||||
@ -9110,7 +9128,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
||||
"pg_catalog.pg_get_function_identity_arguments(oid) AS funciargs, "
|
||||
"pg_catalog.pg_get_function_result(oid) AS funcresult, "
|
||||
"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 "
|
||||
"FROM pg_catalog.pg_proc "
|
||||
"WHERE oid = '%u'::pg_catalog.oid",
|
||||
@ -9123,6 +9142,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
||||
"proallargtypes, proargmodes, proargnames, "
|
||||
"false AS proiswindow, "
|
||||
"provolatile, proisstrict, prosecdef, "
|
||||
"false AS proleakproof, "
|
||||
"proconfig, procost, prorows, "
|
||||
"(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
|
||||
"FROM pg_catalog.pg_proc "
|
||||
@ -9136,6 +9156,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
||||
"proallargtypes, proargmodes, proargnames, "
|
||||
"false AS proiswindow, "
|
||||
"provolatile, proisstrict, prosecdef, "
|
||||
"false AS proleakproof, "
|
||||
"null AS proconfig, 0 AS procost, 0 AS prorows, "
|
||||
"(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
|
||||
"FROM pg_catalog.pg_proc "
|
||||
@ -9151,6 +9172,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
||||
"proargnames, "
|
||||
"false AS proiswindow, "
|
||||
"provolatile, proisstrict, prosecdef, "
|
||||
"false AS proleakproof, "
|
||||
"null AS proconfig, 0 AS procost, 0 AS prorows, "
|
||||
"(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
|
||||
"FROM pg_catalog.pg_proc "
|
||||
@ -9166,6 +9188,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
||||
"null AS proargnames, "
|
||||
"false AS proiswindow, "
|
||||
"provolatile, proisstrict, prosecdef, "
|
||||
"false AS proleakproof, "
|
||||
"null AS proconfig, 0 AS procost, 0 AS prorows, "
|
||||
"(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
|
||||
"FROM pg_catalog.pg_proc "
|
||||
@ -9183,6 +9206,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
||||
"case when proiscachable then 'i' else 'v' end AS provolatile, "
|
||||
"proisstrict, "
|
||||
"false AS prosecdef, "
|
||||
"false AS proleakproof, "
|
||||
"null AS proconfig, 0 AS procost, 0 AS prorows, "
|
||||
"(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname "
|
||||
"FROM pg_proc "
|
||||
@ -9200,6 +9224,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
||||
"CASE WHEN proiscachable THEN 'i' ELSE 'v' END AS provolatile, "
|
||||
"false AS proisstrict, "
|
||||
"false AS prosecdef, "
|
||||
"false AS proleakproof, "
|
||||
"NULL AS proconfig, 0 AS procost, 0 AS prorows, "
|
||||
"(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname "
|
||||
"FROM pg_proc "
|
||||
@ -9241,6 +9266,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
||||
provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
|
||||
proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
|
||||
prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
|
||||
proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
|
||||
proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
|
||||
procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
|
||||
prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
|
||||
@ -9404,6 +9430,9 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
||||
if (prosecdef[0] == 't')
|
||||
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
|
||||
* break backwards-compatibility of the dump without need. Keep this code
|
||||
|
@ -53,6 +53,6 @@
|
||||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 201202083
|
||||
#define CATALOG_VERSION_NO 201202131
|
||||
|
||||
#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("");
|
||||
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("");
|
||||
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("");
|
||||
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("");
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -28,6 +28,7 @@ extern Oid ProcedureCreate(const char *procedureName,
|
||||
bool isAgg,
|
||||
bool isWindowFunc,
|
||||
bool security_definer,
|
||||
bool isLeakProof,
|
||||
bool isStrict,
|
||||
char volatility,
|
||||
oidvector *parameterTypes,
|
||||
|
@ -61,6 +61,8 @@ extern bool contain_subplans(Node *clause);
|
||||
extern bool contain_mutable_functions(Node *clause);
|
||||
extern bool contain_volatile_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 List *find_nonnullable_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_ctype", LC_CTYPE_P, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("leading", LEADING, RESERVED_KEYWORD)
|
||||
PG_KEYWORD("leakproof", LEAKPROOF, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("least", LEAST, COL_NAME_KEYWORD)
|
||||
PG_KEYWORD("left", LEFT, TYPE_FUNC_NAME_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 func_strict(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_rows(Oid funcid);
|
||||
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
|
||||
-- views with "security_barrier" attribute
|
||||
-- views with "security_barrier" attribute, except for operators
|
||||
-- implemented with leakproof functions.
|
||||
--
|
||||
SELECT * FROM my_credit_card_usage_normal
|
||||
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';
|
||||
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
|
||||
-----+---------------+------------------+-----------+---------------------+--------+------------+-------
|
||||
@ -1444,21 +1443,22 @@ NOTICE: f_leak => 1111-2222-3333-4444
|
||||
|
||||
EXPLAIN (COSTS OFF) SELECT * FROM my_credit_card_usage_secure
|
||||
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
|
||||
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))
|
||||
-> Hash Join
|
||||
Hash Cond: (r.cid = l.cid)
|
||||
Filter: f_leak(my_credit_card_usage_secure.cnum)
|
||||
-> Nested Loop
|
||||
Join Filter: (l.cid = r.cid)
|
||||
-> Seq Scan on credit_usage r
|
||||
-> Hash
|
||||
Filter: ((ymd >= '10-01-2011'::date) AND (ymd < '11-01-2011'::date))
|
||||
-> Materialize
|
||||
-> Hash Join
|
||||
Hash Cond: (r.cid = l.cid)
|
||||
-> Seq Scan on credit_card r
|
||||
-> Hash
|
||||
-> Seq Scan on customer l
|
||||
Filter: (name = ("current_user"())::text)
|
||||
(12 rows)
|
||||
(13 rows)
|
||||
|
||||
--
|
||||
-- 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
|
||||
-- views with "security_barrier" attribute
|
||||
-- views with "security_barrier" attribute, except for operators
|
||||
-- implemented with leakproof functions.
|
||||
--
|
||||
SELECT * FROM my_credit_card_usage_normal
|
||||
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';
|
||||
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
|
||||
-----+---------------+------------------+-----------+---------------------+--------+------------+-------
|
||||
@ -1444,21 +1443,22 @@ NOTICE: f_leak => 1111-2222-3333-4444
|
||||
|
||||
EXPLAIN (COSTS OFF) SELECT * FROM my_credit_card_usage_secure
|
||||
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
|
||||
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))
|
||||
-> Hash Join
|
||||
Hash Cond: (r.cid = l.cid)
|
||||
Filter: f_leak(my_credit_card_usage_secure.cnum)
|
||||
-> Nested Loop
|
||||
Join Filter: (l.cid = r.cid)
|
||||
-> Seq Scan on credit_usage r
|
||||
-> Hash
|
||||
Filter: ((ymd >= '10-01-2011'::date) AND (ymd < '11-01-2011'::date))
|
||||
-> Materialize
|
||||
-> Hash Join
|
||||
Hash Cond: (r.cid = l.cid)
|
||||
-> Seq Scan on credit_card r
|
||||
-> Hash
|
||||
-> Seq Scan on customer l
|
||||
Filter: (name = ("current_user"())::text)
|
||||
(12 rows)
|
||||
(13 rows)
|
||||
|
||||
--
|
||||
-- Test for the case when security_barrier gets changed between rewriter
|
||||
|
@ -40,6 +40,7 @@ test: create_function_1
|
||||
test: create_type
|
||||
test: create_table
|
||||
test: create_function_2
|
||||
test: create_function_3
|
||||
|
||||
# ----------
|
||||
# Load huge amounts of data
|
||||
|
@ -50,6 +50,7 @@ test: create_function_1
|
||||
test: create_type
|
||||
test: create_table
|
||||
test: create_function_2
|
||||
test: create_function_3
|
||||
test: copy
|
||||
test: copyselect
|
||||
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
|
||||
-- views with "security_barrier" attribute
|
||||
-- views with "security_barrier" attribute, except for operators
|
||||
-- implemented with leakproof functions.
|
||||
--
|
||||
SELECT * FROM my_credit_card_usage_normal
|
||||
WHERE f_leak(cnum) AND ymd >= '2011-10-01' AND ymd < '2011-11-01';
|
||||
|
Loading…
x
Reference in New Issue
Block a user