pgsql-v9.2-fix-leaky-view-part-1.v2.patch
application/octet-stream
Filename: pgsql-v9.2-fix-leaky-view-part-1.v2.patch
Type: application/octet-stream
Part: 0
src/backend/nodes/copyfuncs.c | 8 ++
src/backend/nodes/equalfuncs.c | 8 ++
src/backend/nodes/outfuncs.c | 8 ++
src/backend/nodes/readfuncs.c | 8 ++
src/backend/optimizer/path/costsize.c | 17 ++++
src/backend/optimizer/plan/createplan.c | 13 +++-
src/backend/optimizer/plan/planner.c | 114 ++++++++++++++++++++++++++++
src/backend/optimizer/util/clauses.c | 24 ++++--
src/include/nodes/primnodes.h | 6 ++
src/include/nodes/relation.h | 1 +
src/test/regress/expected/select_views.out | 70 ++++++++++++++----
src/test/regress/sql/select_views.sql | 35 +++++++++
12 files changed, 289 insertions(+), 23 deletions(-)
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index c9133dd..1b1cf93 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1188,6 +1188,7 @@ _copyFuncExpr(FuncExpr *from)
COPY_SCALAR_FIELD(inputcollid);
COPY_NODE_FIELD(args);
COPY_LOCATION_FIELD(location);
+ COPY_SCALAR_FIELD(depth);
return newnode;
}
@@ -1224,6 +1225,7 @@ _copyOpExpr(OpExpr *from)
COPY_SCALAR_FIELD(inputcollid);
COPY_NODE_FIELD(args);
COPY_LOCATION_FIELD(location);
+ COPY_SCALAR_FIELD(depth);
return newnode;
}
@@ -1244,6 +1246,7 @@ _copyDistinctExpr(DistinctExpr *from)
COPY_SCALAR_FIELD(inputcollid);
COPY_NODE_FIELD(args);
COPY_LOCATION_FIELD(location);
+ COPY_SCALAR_FIELD(depth);
return newnode;
}
@@ -1264,6 +1267,7 @@ _copyNullIfExpr(NullIfExpr *from)
COPY_SCALAR_FIELD(inputcollid);
COPY_NODE_FIELD(args);
COPY_LOCATION_FIELD(location);
+ COPY_SCALAR_FIELD(depth);
return newnode;
}
@@ -1282,6 +1286,7 @@ _copyScalarArrayOpExpr(ScalarArrayOpExpr *from)
COPY_SCALAR_FIELD(inputcollid);
COPY_NODE_FIELD(args);
COPY_LOCATION_FIELD(location);
+ COPY_SCALAR_FIELD(depth);
return newnode;
}
@@ -1422,6 +1427,7 @@ _copyCoerceViaIO(CoerceViaIO *from)
COPY_SCALAR_FIELD(resultcollid);
COPY_SCALAR_FIELD(coerceformat);
COPY_LOCATION_FIELD(location);
+ COPY_SCALAR_FIELD(depth);
return newnode;
}
@@ -1442,6 +1448,7 @@ _copyArrayCoerceExpr(ArrayCoerceExpr *from)
COPY_SCALAR_FIELD(isExplicit);
COPY_SCALAR_FIELD(coerceformat);
COPY_LOCATION_FIELD(location);
+ COPY_SCALAR_FIELD(depth);
return newnode;
}
@@ -1574,6 +1581,7 @@ _copyRowCompareExpr(RowCompareExpr *from)
COPY_NODE_FIELD(inputcollids);
COPY_NODE_FIELD(largs);
COPY_NODE_FIELD(rargs);
+ COPY_SCALAR_FIELD(depth);
return newnode;
}
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 3a0267c..71c4694 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -248,6 +248,7 @@ _equalFuncExpr(FuncExpr *a, FuncExpr *b)
COMPARE_SCALAR_FIELD(inputcollid);
COMPARE_NODE_FIELD(args);
COMPARE_LOCATION_FIELD(location);
+ COMPARE_SCALAR_FIELD(depth);
return true;
}
@@ -285,6 +286,7 @@ _equalOpExpr(OpExpr *a, OpExpr *b)
COMPARE_SCALAR_FIELD(inputcollid);
COMPARE_NODE_FIELD(args);
COMPARE_LOCATION_FIELD(location);
+ COMPARE_SCALAR_FIELD(depth);
return true;
}
@@ -311,6 +313,7 @@ _equalDistinctExpr(DistinctExpr *a, DistinctExpr *b)
COMPARE_SCALAR_FIELD(inputcollid);
COMPARE_NODE_FIELD(args);
COMPARE_LOCATION_FIELD(location);
+ COMPARE_SCALAR_FIELD(depth);
return true;
}
@@ -337,6 +340,7 @@ _equalNullIfExpr(NullIfExpr *a, NullIfExpr *b)
COMPARE_SCALAR_FIELD(inputcollid);
COMPARE_NODE_FIELD(args);
COMPARE_LOCATION_FIELD(location);
+ COMPARE_SCALAR_FIELD(depth);
return true;
}
@@ -361,6 +365,7 @@ _equalScalarArrayOpExpr(ScalarArrayOpExpr *a, ScalarArrayOpExpr *b)
COMPARE_SCALAR_FIELD(inputcollid);
COMPARE_NODE_FIELD(args);
COMPARE_LOCATION_FIELD(location);
+ COMPARE_SCALAR_FIELD(depth);
return true;
}
@@ -479,6 +484,7 @@ _equalCoerceViaIO(CoerceViaIO *a, CoerceViaIO *b)
return false;
COMPARE_LOCATION_FIELD(location);
+ COMPARE_SCALAR_FIELD(depth);
return true;
}
@@ -503,6 +509,7 @@ _equalArrayCoerceExpr(ArrayCoerceExpr *a, ArrayCoerceExpr *b)
return false;
COMPARE_LOCATION_FIELD(location);
+ COMPARE_SCALAR_FIELD(depth);
return true;
}
@@ -613,6 +620,7 @@ _equalRowCompareExpr(RowCompareExpr *a, RowCompareExpr *b)
COMPARE_NODE_FIELD(inputcollids);
COMPARE_NODE_FIELD(largs);
COMPARE_NODE_FIELD(rargs);
+ COMPARE_SCALAR_FIELD(depth);
return true;
}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 681f5f8..f9a8aef 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1004,6 +1004,7 @@ _outFuncExpr(StringInfo str, FuncExpr *node)
WRITE_OID_FIELD(inputcollid);
WRITE_NODE_FIELD(args);
WRITE_LOCATION_FIELD(location);
+ WRITE_INT_FIELD(depth);
}
static void
@@ -1030,6 +1031,7 @@ _outOpExpr(StringInfo str, OpExpr *node)
WRITE_OID_FIELD(inputcollid);
WRITE_NODE_FIELD(args);
WRITE_LOCATION_FIELD(location);
+ WRITE_INT_FIELD(depth);
}
static void
@@ -1045,6 +1047,7 @@ _outDistinctExpr(StringInfo str, DistinctExpr *node)
WRITE_OID_FIELD(inputcollid);
WRITE_NODE_FIELD(args);
WRITE_LOCATION_FIELD(location);
+ WRITE_INT_FIELD(depth);
}
static void
@@ -1060,6 +1063,7 @@ _outNullIfExpr(StringInfo str, NullIfExpr *node)
WRITE_OID_FIELD(inputcollid);
WRITE_NODE_FIELD(args);
WRITE_LOCATION_FIELD(location);
+ WRITE_INT_FIELD(depth);
}
static void
@@ -1073,6 +1077,7 @@ _outScalarArrayOpExpr(StringInfo str, ScalarArrayOpExpr *node)
WRITE_OID_FIELD(inputcollid);
WRITE_NODE_FIELD(args);
WRITE_LOCATION_FIELD(location);
+ WRITE_INT_FIELD(depth);
}
static void
@@ -1190,6 +1195,7 @@ _outCoerceViaIO(StringInfo str, CoerceViaIO *node)
WRITE_OID_FIELD(resultcollid);
WRITE_ENUM_FIELD(coerceformat, CoercionForm);
WRITE_LOCATION_FIELD(location);
+ WRITE_INT_FIELD(depth);
}
static void
@@ -1205,6 +1211,7 @@ _outArrayCoerceExpr(StringInfo str, ArrayCoerceExpr *node)
WRITE_BOOL_FIELD(isExplicit);
WRITE_ENUM_FIELD(coerceformat, CoercionForm);
WRITE_LOCATION_FIELD(location);
+ WRITE_INT_FIELD(depth);
}
static void
@@ -1297,6 +1304,7 @@ _outRowCompareExpr(StringInfo str, RowCompareExpr *node)
WRITE_NODE_FIELD(inputcollids);
WRITE_NODE_FIELD(largs);
WRITE_NODE_FIELD(rargs);
+ WRITE_INT_FIELD(depth);
}
static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 29a0e8f..c4f934d 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -541,6 +541,7 @@ _readFuncExpr(void)
READ_OID_FIELD(inputcollid);
READ_NODE_FIELD(args);
READ_LOCATION_FIELD(location);
+ READ_INT_FIELD(depth);
READ_DONE();
}
@@ -588,6 +589,7 @@ _readOpExpr(void)
READ_OID_FIELD(inputcollid);
READ_NODE_FIELD(args);
READ_LOCATION_FIELD(location);
+ READ_INT_FIELD(depth);
READ_DONE();
}
@@ -619,6 +621,7 @@ _readDistinctExpr(void)
READ_OID_FIELD(inputcollid);
READ_NODE_FIELD(args);
READ_LOCATION_FIELD(location);
+ READ_INT_FIELD(depth);
READ_DONE();
}
@@ -650,6 +653,7 @@ _readNullIfExpr(void)
READ_OID_FIELD(inputcollid);
READ_NODE_FIELD(args);
READ_LOCATION_FIELD(location);
+ READ_INT_FIELD(depth);
READ_DONE();
}
@@ -679,6 +683,7 @@ _readScalarArrayOpExpr(void)
READ_OID_FIELD(inputcollid);
READ_NODE_FIELD(args);
READ_LOCATION_FIELD(location);
+ READ_INT_FIELD(depth);
READ_DONE();
}
@@ -794,6 +799,7 @@ _readCoerceViaIO(void)
READ_OID_FIELD(resultcollid);
READ_ENUM_FIELD(coerceformat, CoercionForm);
READ_LOCATION_FIELD(location);
+ READ_INT_FIELD(depth);
READ_DONE();
}
@@ -814,6 +820,7 @@ _readArrayCoerceExpr(void)
READ_BOOL_FIELD(isExplicit);
READ_ENUM_FIELD(coerceformat, CoercionForm);
READ_LOCATION_FIELD(location);
+ READ_INT_FIELD(depth);
READ_DONE();
}
@@ -946,6 +953,7 @@ _readRowCompareExpr(void)
READ_NODE_FIELD(inputcollids);
READ_NODE_FIELD(largs);
READ_NODE_FIELD(rargs);
+ READ_INT_FIELD(depth);
READ_DONE();
}
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index bb38768..0302ad2 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -2593,6 +2593,7 @@ cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root)
context.root = root;
context.total.startup = 0;
context.total.per_tuple = 0;
+ context.total.depth = 0;
/* We don't charge any cost for the implicit ANDing at top level ... */
@@ -2618,6 +2619,7 @@ cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root)
context.root = root;
context.total.startup = 0;
context.total.per_tuple = 0;
+ context.total.depth = 0;
cost_qual_eval_walker(qual, &context);
@@ -2647,6 +2649,7 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
locContext.root = context->root;
locContext.total.startup = 0;
locContext.total.per_tuple = 0;
+ locContext.total.depth = 0;
/*
* For an OR clause, recurse into the marked-up tree so that we
@@ -2671,6 +2674,8 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
}
context->total.startup += rinfo->eval_cost.startup;
context->total.per_tuple += rinfo->eval_cost.per_tuple;
+ if (rinfo->eval_cost.depth > context->total.depth)
+ context->total.depth = rinfo->eval_cost.depth;
/* do NOT recurse into children */
return false;
}
@@ -2694,6 +2699,8 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
{
context->total.per_tuple +=
get_func_cost(((FuncExpr *) node)->funcid) * cpu_operator_cost;
+ if (((FuncExpr *)node)->depth > context->total.depth)
+ context->total.depth = ((FuncExpr *)node)->depth;
}
else if (IsA(node, OpExpr) ||
IsA(node, DistinctExpr) ||
@@ -2703,6 +2710,8 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
set_opfuncid((OpExpr *) node);
context->total.per_tuple +=
get_func_cost(((OpExpr *) node)->opfuncid) * cpu_operator_cost;
+ if (((OpExpr *)node)->depth > context->total.depth)
+ context->total.depth = ((OpExpr *)node)->depth;
}
else if (IsA(node, ScalarArrayOpExpr))
{
@@ -2716,6 +2725,8 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
set_sa_opfuncid(saop);
context->total.per_tuple += get_func_cost(saop->opfuncid) *
cpu_operator_cost * estimate_array_length(arraynode) * 0.5;
+ if (saop->depth > context->total.depth)
+ context->total.depth = saop->depth;
}
else if (IsA(node, Aggref) ||
IsA(node, WindowFunc))
@@ -2746,6 +2757,8 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
getTypeOutputInfo(exprType((Node *) iocoerce->arg),
&iofunc, &typisvarlena);
context->total.per_tuple += get_func_cost(iofunc) * cpu_operator_cost;
+ if (iocoerce->depth > context->total.depth)
+ context->total.depth = iocoerce->depth;
}
else if (IsA(node, ArrayCoerceExpr))
{
@@ -2755,6 +2768,8 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
if (OidIsValid(acoerce->elemfuncid))
context->total.per_tuple += get_func_cost(acoerce->elemfuncid) *
cpu_operator_cost * estimate_array_length(arraynode);
+ if (acoerce->depth > context->total.depth)
+ context->total.depth = acoerce->depth;
}
else if (IsA(node, RowCompareExpr))
{
@@ -2769,6 +2784,8 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
context->total.per_tuple += get_func_cost(get_opcode(opid)) *
cpu_operator_cost;
}
+ if (rcexpr->depth > context->total.depth)
+ context->total.depth = rcexpr->depth;
}
else if (IsA(node, CurrentOfExpr))
{
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index e4ccf5c..3414b4c 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -2704,6 +2704,7 @@ order_qual_clauses(PlannerInfo *root, List *clauses)
{
Node *clause;
Cost cost;
+ int depth;
} QualItem;
int nitems = list_length(clauses);
QualItem *items;
@@ -2729,6 +2730,7 @@ order_qual_clauses(PlannerInfo *root, List *clauses)
cost_qual_eval_node(&qcost, clause, root);
items[i].clause = clause;
items[i].cost = qcost.per_tuple;
+ items[i].depth = qcost.depth;
i++;
}
@@ -2745,7 +2747,16 @@ order_qual_clauses(PlannerInfo *root, List *clauses)
/* insert newitem into the already-sorted subarray */
for (j = i; j > 0; j--)
{
- if (newitem.cost >= items[j - 1].cost)
+ /*
+ * Higher priority shall be given to the items originated from
+ * deeper nest level. If same level, it shall be given to the
+ * items with smaller estimated cost.
+ * Such kind of consideration is needed to prevent leaky-view
+ * problem.
+ */
+ if (newitem.depth < items[j - 1].depth ||
+ (newitem.depth == items[j - 1].depth &&
+ newitem.cost >= items[j - 1].cost))
break;
items[j] = items[j - 1];
}
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 9aafc8a..d58d789 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -33,6 +33,7 @@
#include "optimizer/subselect.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"
+#include "nodes/nodeFuncs.h"
#ifdef OPTIMIZER_DEBUG
#include "nodes/print.h"
#endif
@@ -103,6 +104,7 @@ static void get_column_info_for_window(PlannerInfo *root, WindowClause *wc,
int *ordNumCols,
AttrNumber **ordColIdx,
Oid **ordOperators);
+static void mark_qualifiers_depth(Query *query);
/*****************************************************************************
@@ -148,6 +150,12 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
cursorOptions |= ((DeclareCursorStmt *) parse->utilityStmt)->options;
/*
+ * Mark qualifiers its original depth to prevent reversal of orders
+ * on evaluation of WHERE clause during relation scanns.
+ */
+ mark_qualifiers_depth(parse);
+
+ /*
* Set up global state for this planner invocation. This data is needed
* across all levels of sub-Query that might exist in the given command,
* so we keep it in a separate struct that's linked to by each per-Query
@@ -2993,6 +3001,112 @@ get_column_info_for_window(PlannerInfo *root, WindowClause *wc, List *tlist,
}
}
+/*
+ * mark_qualifiers_depth
+ *
+ * It marks depth field of the each expression nodes that eventually
+ * invokes functions, to track the original nest-level. On the evaluation
+ * of qualifiers within WHERE or JOIN ... ON clauses during relation scans,
+ * these items shall be reordered according to the nest-level and estimated
+ * cost.
+ * The optimizer may pull-up simple sub-queries or join clause, and
+ * qualifiers to filter out tuples shall be mixed with ones in upper-
+ * level. Thus, we need to track the original nest-level of qualifiers
+ * to prevent reverse of order in evaluation, because some of qualifiers
+ * can have side-effects that allows to leak supplied argument to outside.
+ * It can be abused to break row-level security using a user defined function
+ * with very small estimated cost, so nest level of qualifiers originated
+ * from is used as a criteria, rather than estimated cost, to decide order
+ * to evaluate qualifiers.
+ */
+static bool
+mark_qualifiers_depth_walker(Node *node, void *context)
+{
+ int depth = *((int *)(context));
+
+ if (node == NULL)
+ return false;
+ if (IsA(node, FuncExpr))
+ {
+ FuncExpr *exp = (FuncExpr *)node;
+
+ exp->depth = depth;
+
+ return false;
+ }
+ else if (IsA(node, OpExpr))
+ {
+ OpExpr *exp = (OpExpr *)node;
+
+ exp->depth = depth;
+
+ return false;
+ }
+ else if (IsA(node, DistinctExpr))
+ {
+ DistinctExpr *exp = (DistinctExpr *)node;
+
+ exp->depth = depth;
+
+ return false;
+ }
+ else if (IsA(node, ScalarArrayOpExpr))
+ {
+ ScalarArrayOpExpr *exp = (ScalarArrayOpExpr *)node;
+
+ exp->depth = depth;
+
+ return false;
+ }
+ else if (IsA(node, CoerceViaIO))
+ {
+ CoerceViaIO *exp = (CoerceViaIO *)node;
+
+ exp->depth = depth;
+
+ return false;
+ }
+ else if (IsA(node, ArrayCoerceExpr))
+ {
+ ArrayCoerceExpr *exp = (ArrayCoerceExpr *)node;
+
+ exp->depth = depth;
+
+ return false;
+ }
+ else if (IsA(node, NullIfExpr))
+ {
+ NullIfExpr *exp = (NullIfExpr *)node;
+
+ exp->depth = depth;
+
+ return false;
+ }
+ else if (IsA(node, RowCompareExpr))
+ {
+ RowCompareExpr *exp = (RowCompareExpr *)node;
+
+ exp->depth = depth;
+
+ return false;
+ }
+ else if (IsA(node, Query))
+ {
+ depth += 2;
+
+ query_tree_walker((Query *)node, mark_qualifiers_depth_walker, &depth, 0);
+ return false;
+ }
+ return expression_tree_walker(node, mark_qualifiers_depth_walker, context);
+}
+
+static void
+mark_qualifiers_depth(Query *query)
+{
+ int depth = 0;
+
+ query_tree_walker(query, mark_qualifiers_depth_walker, &depth, 0);
+}
/*
* expression_planner
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index be0935d..95c07aa 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -111,6 +111,7 @@ static Expr *simplify_function(Expr *oldexpr, Oid funcid,
Oid input_collid, List **args,
bool has_named_args,
bool allow_inline,
+ int depth,
eval_const_expressions_context *context);
static List *reorder_function_arguments(List *args, Oid result_type,
HeapTuple func_tuple,
@@ -123,7 +124,7 @@ static void recheck_cast_function_args(List *args, Oid result_type,
HeapTuple func_tuple);
static Expr *evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
Oid result_collid, Oid input_collid, List *args,
- HeapTuple func_tuple,
+ HeapTuple func_tuple, int depth,
eval_const_expressions_context *context);
static Expr *inline_function(Oid funcid, Oid result_type, Oid result_collid,
Oid input_collid, List *args,
@@ -2229,7 +2230,7 @@ eval_const_expressions_mutator(Node *node,
expr->funccollid,
expr->inputcollid,
&args,
- has_named_args, true, context);
+ has_named_args, true, expr->depth, context);
if (simple) /* successfully simplified it */
return (Node *) simple;
@@ -2248,6 +2249,7 @@ eval_const_expressions_mutator(Node *node,
newexpr->inputcollid = expr->inputcollid;
newexpr->args = args;
newexpr->location = expr->location;
+ newexpr->depth = expr->depth;
return (Node *) newexpr;
}
if (IsA(node, OpExpr))
@@ -2282,7 +2284,7 @@ eval_const_expressions_mutator(Node *node,
expr->opcollid,
expr->inputcollid,
&args,
- false, true, context);
+ false, true, expr->depth, context);
if (simple) /* successfully simplified it */
return (Node *) simple;
@@ -2313,6 +2315,7 @@ eval_const_expressions_mutator(Node *node,
newexpr->inputcollid = expr->inputcollid;
newexpr->args = args;
newexpr->location = expr->location;
+ newexpr->depth = expr->depth;
return (Node *) newexpr;
}
if (IsA(node, DistinctExpr))
@@ -2380,7 +2383,7 @@ eval_const_expressions_mutator(Node *node,
expr->opcollid,
expr->inputcollid,
&args,
- false, false, context);
+ false, false, expr->depth, context);
if (simple) /* successfully simplified it */
{
/*
@@ -2410,6 +2413,7 @@ eval_const_expressions_mutator(Node *node,
newexpr->inputcollid = expr->inputcollid;
newexpr->args = args;
newexpr->location = expr->location;
+ newexpr->depth = expr->depth;
return (Node *) newexpr;
}
if (IsA(node, BoolExpr))
@@ -2570,7 +2574,7 @@ eval_const_expressions_mutator(Node *node,
InvalidOid,
InvalidOid,
&args,
- false, true, context);
+ false, true, expr->depth, context);
if (simple) /* successfully simplified output fn */
{
/*
@@ -2591,7 +2595,7 @@ eval_const_expressions_mutator(Node *node,
expr->resultcollid,
InvalidOid,
&args,
- false, true, context);
+ false, true, expr->depth, context);
if (simple) /* successfully simplified input fn */
return (Node *) simple;
}
@@ -2607,6 +2611,7 @@ eval_const_expressions_mutator(Node *node,
newexpr->resultcollid = expr->resultcollid;
newexpr->coerceformat = expr->coerceformat;
newexpr->location = expr->location;
+ newexpr->depth = expr->depth;
return (Node *) newexpr;
}
if (IsA(node, ArrayCoerceExpr))
@@ -2631,6 +2636,7 @@ eval_const_expressions_mutator(Node *node,
newexpr->isExplicit = expr->isExplicit;
newexpr->coerceformat = expr->coerceformat;
newexpr->location = expr->location;
+ newexpr->depth = expr->depth;
/*
* If constant argument and it's a binary-coercible or immutable
@@ -3447,6 +3453,7 @@ simplify_function(Expr *oldexpr, Oid funcid,
Oid input_collid, List **args,
bool has_named_args,
bool allow_inline,
+ int depth,
eval_const_expressions_context *context)
{
HeapTuple func_tuple;
@@ -3477,7 +3484,7 @@ simplify_function(Expr *oldexpr, Oid funcid,
newexpr = evaluate_function(funcid, result_type, result_typmod,
result_collid, input_collid, *args,
- func_tuple, context);
+ func_tuple, depth, context);
/*
* Some functions calls can be simplified at plan time based on properties
@@ -3764,7 +3771,7 @@ recheck_cast_function_args(List *args, Oid result_type, HeapTuple func_tuple)
static Expr *
evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
Oid result_collid, Oid input_collid, List *args,
- HeapTuple func_tuple,
+ HeapTuple func_tuple, int depth,
eval_const_expressions_context *context)
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
@@ -3850,6 +3857,7 @@ evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
newexpr->inputcollid = input_collid;
newexpr->args = args;
newexpr->location = -1;
+ newexpr->depth = depth;
return evaluate_expr((Expr *) newexpr, result_type, result_typmod,
result_collid);
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index f1e20ef..b626386 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -335,6 +335,7 @@ typedef struct FuncExpr
Oid inputcollid; /* OID of collation that function should use */
List *args; /* arguments to the function */
int location; /* token location, or -1 if unknown */
+ int depth; /* depth of clause in the original query */
} FuncExpr;
/*
@@ -380,6 +381,7 @@ typedef struct OpExpr
Oid inputcollid; /* OID of collation that operator should use */
List *args; /* arguments to the operator (1 or 2) */
int location; /* token location, or -1 if unknown */
+ int depth; /* depth of clause in the original query */
} OpExpr;
/*
@@ -421,6 +423,7 @@ typedef struct ScalarArrayOpExpr
Oid inputcollid; /* OID of collation that operator should use */
List *args; /* the scalar and array operands */
int location; /* token location, or -1 if unknown */
+ int depth; /* depth of clause in the original query */
} ScalarArrayOpExpr;
/*
@@ -685,6 +688,7 @@ typedef struct CoerceViaIO
Oid resultcollid; /* OID of collation, or InvalidOid if none */
CoercionForm coerceformat; /* how to display this node */
int location; /* token location, or -1 if unknown */
+ int depth; /* depth of clause in the original query */
} CoerceViaIO;
/* ----------------
@@ -710,6 +714,7 @@ typedef struct ArrayCoerceExpr
bool isExplicit; /* conversion semantics flag to pass to func */
CoercionForm coerceformat; /* how to display this node */
int location; /* token location, or -1 if unknown */
+ int depth; /* depth of clause in the original query */
} ArrayCoerceExpr;
/* ----------------
@@ -901,6 +906,7 @@ typedef struct RowCompareExpr
List *inputcollids; /* OID list of collations for comparisons */
List *largs; /* the left-hand input arguments */
List *rargs; /* the right-hand input arguments */
+ int depth; /* depth of clause in the original query */
} RowCompareExpr;
/*
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index f659269..250191b 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -44,6 +44,7 @@ typedef struct QualCost
{
Cost startup; /* one-time cost */
Cost per_tuple; /* per-evaluation cost */
+ int depth; /* depth of qual in the original query */
} QualCost;
/*
diff --git a/src/test/regress/expected/select_views.out b/src/test/regress/expected/select_views.out
index 6cd317c..466689f 100644
--- a/src/test/regress/expected/select_views.out
+++ b/src/test/regress/expected/select_views.out
@@ -467,6 +467,20 @@ SELECT name, #thepath FROM iexit ORDER BY 1, 2;
I- 580 | 21
I- 580 | 22
I- 580 | 22
+ I- 580/I-680 Ramp | 2
+ I- 580/I-680 Ramp | 2
+ I- 580/I-680 Ramp | 2
+ I- 580/I-680 Ramp | 2
+ I- 580/I-680 Ramp | 2
+ I- 580/I-680 Ramp | 2
+ I- 580/I-680 Ramp | 4
+ I- 580/I-680 Ramp | 4
+ I- 580/I-680 Ramp | 4
+ I- 580/I-680 Ramp | 4
+ I- 580/I-680 Ramp | 5
+ I- 580/I-680 Ramp | 6
+ I- 580/I-680 Ramp | 6
+ I- 580/I-680 Ramp | 6
I- 580 Ramp | 2
I- 580 Ramp | 2
I- 580 Ramp | 2
@@ -717,20 +731,6 @@ SELECT name, #thepath FROM iexit ORDER BY 1, 2;
I- 580 Ramp | 8
I- 580 Ramp | 8
I- 580 Ramp | 8
- I- 580/I-680 Ramp | 2
- I- 580/I-680 Ramp | 2
- I- 580/I-680 Ramp | 2
- I- 580/I-680 Ramp | 2
- I- 580/I-680 Ramp | 2
- I- 580/I-680 Ramp | 2
- I- 580/I-680 Ramp | 4
- I- 580/I-680 Ramp | 4
- I- 580/I-680 Ramp | 4
- I- 580/I-680 Ramp | 4
- I- 580/I-680 Ramp | 5
- I- 580/I-680 Ramp | 6
- I- 580/I-680 Ramp | 6
- I- 580/I-680 Ramp | 6
I- 680 | 2
I- 680 | 2
I- 680 | 2
@@ -1247,3 +1247,45 @@ SELECT * FROM toyemp WHERE name = 'sharon';
sharon | 25 | (15,12) | 12000
(1 row)
+--
+-- Test for leaky-view problem
+--
+CREATE USER alice;
+CREATE FUNCTION f_leak(text, text)
+ RETURNS bool LANGUAGE 'plpgsql'
+ COST 0.00000001
+ AS 'begin raise notice ''% => %'', $1, $2; return true; end';
+CREATE TABLE credit_cards (
+ name text,
+ number text,
+ expired text
+);
+INSERT INTO credit_cards VALUES ('alice', '1111-2222-3333-4444', 'Aug-2012'),
+ ('bob', '5555-6666-7777-8888', 'Nov-2016'),
+ ('eve', '9801-2345-6789-0123', 'Jan-2018');
+CREATE VIEW your_credit AS
+ SELECT * FROM credit_cards WHERE name = getpgusername();
+GRANT SELECT ON your_credit TO public;
+-- run leaky view
+SET SESSION AUTHORIZATION alice;
+SELECT * FROM your_credit WHERE f_leak(number,expired);
+NOTICE: 1111-2222-3333-4444 => Aug-2012
+ name | number | expired
+-------+---------------------+----------
+ alice | 1111-2222-3333-4444 | Aug-2012
+(1 row)
+
+EXPLAIN (COSTS OFF) SELECT * FROM your_credit WHERE f_leak(number,expired);
+ QUERY PLAN
+--------------------------------------------------------------------------
+ Seq Scan on credit_cards
+ Filter: ((name = (getpgusername())::text) AND f_leak(number, expired))
+(2 rows)
+
+\c -
+-- cleanups
+DROP ROLE IF EXISTS alice;
+DROP FUNCTION IF EXISTS f_leak(text);
+NOTICE: function f_leak(text) does not exist, skipping
+DROP TABLE IF EXISTS credit_cards CASCADE;
+NOTICE: drop cascades to view your_credit
diff --git a/src/test/regress/sql/select_views.sql b/src/test/regress/sql/select_views.sql
index 14f1be8..8ed1512 100644
--- a/src/test/regress/sql/select_views.sql
+++ b/src/test/regress/sql/select_views.sql
@@ -8,3 +8,38 @@ SELECT * FROM street;
SELECT name, #thepath FROM iexit ORDER BY 1, 2;
SELECT * FROM toyemp WHERE name = 'sharon';
+
+--
+-- Test for leaky-view problem
+--
+
+CREATE USER alice;
+CREATE FUNCTION f_leak(text, text)
+ RETURNS bool LANGUAGE 'plpgsql'
+ COST 0.00000001
+ AS 'begin raise notice ''% => %'', $1, $2; return true; end';
+CREATE TABLE credit_cards (
+ name text,
+ number text,
+ expired text
+);
+
+INSERT INTO credit_cards VALUES ('alice', '1111-2222-3333-4444', 'Aug-2012'),
+ ('bob', '5555-6666-7777-8888', 'Nov-2016'),
+ ('eve', '9801-2345-6789-0123', 'Jan-2018');
+CREATE VIEW your_credit AS
+ SELECT * FROM credit_cards WHERE name = getpgusername();
+
+GRANT SELECT ON your_credit TO public;
+-- run leaky view
+SET SESSION AUTHORIZATION alice;
+
+SELECT * FROM your_credit WHERE f_leak(number,expired);
+EXPLAIN (COSTS OFF) SELECT * FROM your_credit WHERE f_leak(number,expired);
+
+\c -
+-- cleanups
+DROP ROLE IF EXISTS alice;
+
+DROP FUNCTION IF EXISTS f_leak(text);
+DROP TABLE IF EXISTS credit_cards CASCADE;