pgsql-v9.2-fix-leaky-view-part-2.v4.patch
application/octet-stream
Filename: pgsql-v9.2-fix-leaky-view-part-2.v4.patch
Type: application/octet-stream
Part: 1
Message:
Re: [v9.2] Fix Leaky View Problem
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v9
| File | + | − |
|---|---|---|
| src/backend/nodes/copyfuncs.c | 8 | 0 |
| src/backend/nodes/equalfuncs.c | 8 | 0 |
| src/backend/nodes/outfuncs.c | 8 | 0 |
| src/backend/nodes/readfuncs.c | 8 | 0 |
| src/backend/optimizer/path/costsize.c | 17 | 0 |
| src/backend/optimizer/plan/createplan.c | 12 | 1 |
| src/backend/optimizer/plan/planner.c | 1 | 1 |
| src/backend/optimizer/prep/prepjointree.c | 180 | 24 |
| src/backend/optimizer/util/clauses.c | 16 | 8 |
| src/include/nodes/primnodes.h | 6 | 0 |
| src/include/nodes/relation.h | 1 | 0 |
| src/include/optimizer/prep.h | 2 | 1 |
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 | 2 +-
src/backend/optimizer/prep/prepjointree.c | 204 +++++++++++++++++++++++++----
src/backend/optimizer/util/clauses.c | 24 +++-
src/include/nodes/primnodes.h | 6 +
src/include/nodes/relation.h | 1 +
src/include/optimizer/prep.h | 3 +-
12 files changed, 267 insertions(+), 35 deletions(-)
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 5100642..346e381 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1184,6 +1184,7 @@ _copyFuncExpr(FuncExpr *from)
COPY_SCALAR_FIELD(inputcollid);
COPY_NODE_FIELD(args);
COPY_LOCATION_FIELD(location);
+ COPY_SCALAR_FIELD(depth);
return newnode;
}
@@ -1220,6 +1221,7 @@ _copyOpExpr(OpExpr *from)
COPY_SCALAR_FIELD(inputcollid);
COPY_NODE_FIELD(args);
COPY_LOCATION_FIELD(location);
+ COPY_SCALAR_FIELD(depth);
return newnode;
}
@@ -1240,6 +1242,7 @@ _copyDistinctExpr(DistinctExpr *from)
COPY_SCALAR_FIELD(inputcollid);
COPY_NODE_FIELD(args);
COPY_LOCATION_FIELD(location);
+ COPY_SCALAR_FIELD(depth);
return newnode;
}
@@ -1260,6 +1263,7 @@ _copyNullIfExpr(NullIfExpr *from)
COPY_SCALAR_FIELD(inputcollid);
COPY_NODE_FIELD(args);
COPY_LOCATION_FIELD(location);
+ COPY_SCALAR_FIELD(depth);
return newnode;
}
@@ -1278,6 +1282,7 @@ _copyScalarArrayOpExpr(ScalarArrayOpExpr *from)
COPY_SCALAR_FIELD(inputcollid);
COPY_NODE_FIELD(args);
COPY_LOCATION_FIELD(location);
+ COPY_SCALAR_FIELD(depth);
return newnode;
}
@@ -1418,6 +1423,7 @@ _copyCoerceViaIO(CoerceViaIO *from)
COPY_SCALAR_FIELD(resultcollid);
COPY_SCALAR_FIELD(coerceformat);
COPY_LOCATION_FIELD(location);
+ COPY_SCALAR_FIELD(depth);
return newnode;
}
@@ -1438,6 +1444,7 @@ _copyArrayCoerceExpr(ArrayCoerceExpr *from)
COPY_SCALAR_FIELD(isExplicit);
COPY_SCALAR_FIELD(coerceformat);
COPY_LOCATION_FIELD(location);
+ COPY_SCALAR_FIELD(depth);
return newnode;
}
@@ -1570,6 +1577,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 4052a9a..abef8a7 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 9f56427..28617fd 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -998,6 +998,7 @@ _outFuncExpr(StringInfo str, FuncExpr *node)
WRITE_OID_FIELD(inputcollid);
WRITE_NODE_FIELD(args);
WRITE_LOCATION_FIELD(location);
+ WRITE_INT_FIELD(depth);
}
static void
@@ -1024,6 +1025,7 @@ _outOpExpr(StringInfo str, OpExpr *node)
WRITE_OID_FIELD(inputcollid);
WRITE_NODE_FIELD(args);
WRITE_LOCATION_FIELD(location);
+ WRITE_INT_FIELD(depth);
}
static void
@@ -1039,6 +1041,7 @@ _outDistinctExpr(StringInfo str, DistinctExpr *node)
WRITE_OID_FIELD(inputcollid);
WRITE_NODE_FIELD(args);
WRITE_LOCATION_FIELD(location);
+ WRITE_INT_FIELD(depth);
}
static void
@@ -1054,6 +1057,7 @@ _outNullIfExpr(StringInfo str, NullIfExpr *node)
WRITE_OID_FIELD(inputcollid);
WRITE_NODE_FIELD(args);
WRITE_LOCATION_FIELD(location);
+ WRITE_INT_FIELD(depth);
}
static void
@@ -1067,6 +1071,7 @@ _outScalarArrayOpExpr(StringInfo str, ScalarArrayOpExpr *node)
WRITE_OID_FIELD(inputcollid);
WRITE_NODE_FIELD(args);
WRITE_LOCATION_FIELD(location);
+ WRITE_INT_FIELD(depth);
}
static void
@@ -1184,6 +1189,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
@@ -1199,6 +1205,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
@@ -1291,6 +1298,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 45c5524..e9b8200 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -2619,6 +2619,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 ... */
@@ -2644,6 +2645,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);
@@ -2673,6 +2675,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
@@ -2697,6 +2700,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;
}
@@ -2720,6 +2725,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) ||
@@ -2729,6 +2736,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))
{
@@ -2742,6 +2751,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))
@@ -2772,6 +2783,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))
{
@@ -2781,6 +2794,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))
{
@@ -2795,6 +2810,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 36ee7c5..674ef1c 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -2703,6 +2703,7 @@ order_qual_clauses(PlannerInfo *root, List *clauses)
{
Node *clause;
Cost cost;
+ int depth;
} QualItem;
int nitems = list_length(clauses);
QualItem *items;
@@ -2728,6 +2729,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++;
}
@@ -2744,7 +2746,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 5c18b72..103b4f3 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -334,7 +334,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
* query.
*/
parse->jointree = (FromExpr *)
- pull_up_subqueries(root, (Node *) parse->jointree, NULL, NULL);
+ pull_up_subqueries(root, (Node *) parse->jointree, NULL, NULL, 0);
/*
* If this is a simple UNION ALL query, flatten it into an appendrel. We
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index aeaae8c..7d07883 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -23,6 +23,8 @@
*/
#include "postgres.h"
+#include "access/reloptions.h"
+#include "catalog/pg_class.h"
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
@@ -34,6 +36,8 @@
#include "parser/parse_relation.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
typedef struct pullup_replace_vars_context
@@ -62,12 +66,13 @@ static Node *pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
static Node *pull_up_simple_subquery(PlannerInfo *root, Node *jtnode,
RangeTblEntry *rte,
JoinExpr *lowest_outer_join,
- AppendRelInfo *containing_appendrel);
+ AppendRelInfo *containing_appendrel,
+ int qubquery_depth);
static Node *pull_up_simple_union_all(PlannerInfo *root, Node *jtnode,
- RangeTblEntry *rte);
+ RangeTblEntry *rte, int subquery_depth);
static void pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root,
int parentRTindex, Query *setOpQuery,
- int childRToffset);
+ int childRToffset, int subquery_depth);
static void make_setop_translation_list(Query *query, Index newvarno,
List **translated_vars);
static bool is_simple_subquery(Query *subquery);
@@ -91,10 +96,12 @@ static void reduce_outer_joins_pass2(Node *jtnode,
List *forced_null_vars);
static void substitute_multiple_relids(Node *node,
int varno, Relids subrelids);
+static void mark_qualifiers_depth(Node *node, int depth);
static void fix_append_rel_relids(List *append_rel_list, int varno,
Relids subrelids);
static Node *find_jointree_node_for_rel(Node *jtnode, int relid);
+static bool subquery_was_security_barrier(RangeTblEntry *rte);
/*
* pull_up_sublinks
@@ -525,7 +532,8 @@ inline_set_returning_functions(PlannerInfo *root)
Node *
pull_up_subqueries(PlannerInfo *root, Node *jtnode,
JoinExpr *lowest_outer_join,
- AppendRelInfo *containing_appendrel)
+ AppendRelInfo *containing_appendrel,
+ int subquery_depth)
{
if (jtnode == NULL)
return NULL;
@@ -547,7 +555,8 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode,
is_safe_append_member(rte->subquery)))
return pull_up_simple_subquery(root, jtnode, rte,
lowest_outer_join,
- containing_appendrel);
+ containing_appendrel,
+ subquery_depth);
/*
* Alternatively, is it a simple UNION ALL subquery? If so, flatten
@@ -560,7 +569,8 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode,
*/
if (rte->rtekind == RTE_SUBQUERY &&
is_simple_union_all(rte->subquery))
- return pull_up_simple_union_all(root, jtnode, rte);
+ return pull_up_simple_union_all(root, jtnode, rte,
+ subquery_depth);
/* Otherwise, do nothing at this node. */
}
@@ -572,7 +582,16 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode,
Assert(containing_appendrel == NULL);
foreach(l, f->fromlist)
lfirst(l) = pull_up_subqueries(root, lfirst(l),
- lowest_outer_join, NULL);
+ lowest_outer_join, NULL,
+ subquery_depth);
+ /*
+ * Mark the sub-query depth of qualifiers to determine the original
+ * level of them, if necessary. Expr->depth is initialized to zero,
+ * so we don't need to walk on the expression tree, if security view
+ * was not used.
+ */
+ if (subquery_depth > 0)
+ mark_qualifiers_depth(f->quals, subquery_depth);
}
else if (IsA(jtnode, JoinExpr))
{
@@ -584,35 +603,45 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode,
{
case JOIN_INNER:
j->larg = pull_up_subqueries(root, j->larg,
- lowest_outer_join, NULL);
+ lowest_outer_join, NULL,
+ subquery_depth);
j->rarg = pull_up_subqueries(root, j->rarg,
- lowest_outer_join, NULL);
+ lowest_outer_join, NULL,
+ subquery_depth);
break;
case JOIN_LEFT:
case JOIN_SEMI:
case JOIN_ANTI:
j->larg = pull_up_subqueries(root, j->larg,
- lowest_outer_join, NULL);
+ lowest_outer_join, NULL,
+ subquery_depth);
j->rarg = pull_up_subqueries(root, j->rarg,
- j, NULL);
+ j, NULL,
+ subquery_depth);
break;
case JOIN_FULL:
j->larg = pull_up_subqueries(root, j->larg,
- j, NULL);
+ j, NULL,
+ subquery_depth);
j->rarg = pull_up_subqueries(root, j->rarg,
- j, NULL);
+ j, NULL,
+ subquery_depth);
break;
case JOIN_RIGHT:
j->larg = pull_up_subqueries(root, j->larg,
- j, NULL);
+ j, NULL,
+ subquery_depth);
j->rarg = pull_up_subqueries(root, j->rarg,
- lowest_outer_join, NULL);
+ lowest_outer_join, NULL,
+ subquery_depth);
break;
default:
elog(ERROR, "unrecognized join type: %d",
(int) j->jointype);
break;
}
+ if (subquery_depth > 0)
+ mark_qualifiers_depth(j->quals, subquery_depth);
}
else
elog(ERROR, "unrecognized node type: %d",
@@ -635,7 +664,8 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode,
static Node *
pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
JoinExpr *lowest_outer_join,
- AppendRelInfo *containing_appendrel)
+ AppendRelInfo *containing_appendrel,
+ int subquery_depth)
{
Query *parse = root->parse;
int varno = ((RangeTblRef *) jtnode)->rtindex;
@@ -679,6 +709,17 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
Assert(subquery->cteList == NIL);
/*
+ * Increment subquery_depth, if the supplied sub-query come from
+ * a view with security_barrier=true option. This depth value shall
+ * be checked to determine the order to launch qualifiers during
+ * relation scans. It enables to prevent unexpected data leaks
+ * due to the interaction between query optimization and functions
+ * with side-effects.
+ */
+ if (subquery_was_security_barrier(rte))
+ subquery_depth += 1;
+
+ /*
* Pull up any SubLinks within the subquery's quals, so that we don't
* leave unoptimized SubLinks behind.
*/
@@ -701,7 +742,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
* handling an appendrel member.
*/
subquery->jointree = (FromExpr *)
- pull_up_subqueries(subroot, (Node *) subquery->jointree, NULL, NULL);
+ pull_up_subqueries(subroot, (Node *) subquery->jointree,
+ NULL, NULL, subquery_depth);
/*
* Now we must recheck whether the subquery is still simple enough to pull
@@ -903,7 +945,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
* jtnode, since we don't actually need to change the query jointree.
*/
static Node *
-pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
+pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
+ int subquery_depth)
{
int varno = ((RangeTblRef *) jtnode)->rtindex;
Query *subquery = rte->subquery;
@@ -930,7 +973,7 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
*/
Assert(subquery->setOperations);
pull_up_union_leaf_queries(subquery->setOperations, root, varno, subquery,
- rtoffset);
+ rtoffset, subquery_depth);
/*
* Mark the parent as an append relation.
@@ -960,7 +1003,8 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
*/
static void
pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex,
- Query *setOpQuery, int childRToffset)
+ Query *setOpQuery, int childRToffset,
+ int subquery_depth)
{
if (IsA(setOp, RangeTblRef))
{
@@ -995,7 +1039,8 @@ pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex,
*/
rtr = makeNode(RangeTblRef);
rtr->rtindex = childRTindex;
- (void) pull_up_subqueries(root, (Node *) rtr, NULL, appinfo);
+ (void) pull_up_subqueries(root, (Node *) rtr, NULL,
+ appinfo, subquery_depth);
}
else if (IsA(setOp, SetOperationStmt))
{
@@ -1003,9 +1048,9 @@ pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex,
/* Recurse to reach leaf queries */
pull_up_union_leaf_queries(op->larg, root, parentRTindex, setOpQuery,
- childRToffset);
+ childRToffset, subquery_depth);
pull_up_union_leaf_queries(op->rarg, root, parentRTindex, setOpQuery,
- childRToffset);
+ childRToffset, subquery_depth);
}
else
{
@@ -1566,7 +1611,7 @@ flatten_simple_union_all(PlannerInfo *root)
* weren't previously referenced by the jointree, and so were missed by
* the main invocation of pull_up_subqueries.)
*/
- pull_up_union_leaf_queries((Node *) topop, root, leftmostRTI, parse, 0);
+ pull_up_union_leaf_queries((Node *) topop, root, leftmostRTI, parse, 0, 0);
}
@@ -2051,6 +2096,73 @@ substitute_multiple_relids(Node *node, int varno, Relids subrelids)
}
/*
+ * 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 *)node)->depth = depth;
+ }
+ else if (IsA(node, OpExpr))
+ {
+ ((OpExpr *)node)->depth = depth;
+ }
+ else if (IsA(node, DistinctExpr))
+ {
+ ((DistinctExpr *)node)->depth = depth;
+ }
+ else if (IsA(node, ScalarArrayOpExpr))
+ {
+ ((ScalarArrayOpExpr *)node)->depth = depth;
+ }
+ else if (IsA(node, CoerceViaIO))
+ {
+ ((CoerceViaIO *)node)->depth = depth;
+ }
+ else if (IsA(node, ArrayCoerceExpr))
+ {
+ ((ArrayCoerceExpr *)node)->depth = depth;
+ }
+ else if (IsA(node, NullIfExpr))
+ {
+ ((NullIfExpr *)node)->depth = depth;
+ }
+ else if (IsA(node, RowCompareExpr))
+ {
+ ((RowCompareExpr *)node)->depth = depth;
+ }
+ return expression_tree_walker(node, mark_qualifiers_depth_walker, context);
+}
+
+static void
+mark_qualifiers_depth(Node *node, int depth)
+{
+ mark_qualifiers_depth_walker(node, &depth);
+}
+
+/*
* fix_append_rel_relids: update RT-index fields of AppendRelInfo nodes
*
* When we pull up a subquery, any AppendRelInfo references to the subquery's
@@ -2201,3 +2313,47 @@ find_jointree_node_for_rel(Node *jtnode, int relid)
(int) nodeTag(jtnode));
return NULL;
}
+
+/*
+ * subquery_was_security_barrier
+ *
+ * If the supplied sub-query was a view that got flatten at the rewritter
+ * stage, this routine checks whether it should perform as a security
+ * barrier, or not.
+ */
+static bool
+subquery_was_security_barrier(RangeTblEntry *rte)
+{
+ HeapTuple tuple;
+ Datum datum;
+ bool isnull;
+ bool result = false;
+
+ Assert(rte->rtekind == RTE_SUBQUERY);
+
+ /* If it was a view, rte->relid tracks its OID. */
+ if (!OidIsValid(rte->relid))
+ return false;
+
+ tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(rte->relid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for relation %u", rte->relid);
+
+ datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions, &isnull);
+ if (!isnull)
+ {
+ char relkind = ((Form_pg_class) GETSTRUCT(tuple))->relkind;
+ StdRdOptions *rdopts
+ = (StdRdOptions *)heap_reloptions(relkind, datum, false);
+
+ if (rdopts)
+ {
+ if (rdopts->security_barrier)
+ result = true;
+ pfree(rdopts);
+ }
+ }
+ ReleaseSysCache(tuple);
+
+ return result;
+}
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index baa90fa..6df99f6 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -110,6 +110,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,
@@ -122,7 +123,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,
@@ -2188,7 +2189,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;
@@ -2207,6 +2208,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))
@@ -2241,7 +2243,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;
@@ -2272,6 +2274,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))
@@ -2339,7 +2342,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 */
{
/*
@@ -2369,6 +2372,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))
@@ -2529,7 +2533,7 @@ eval_const_expressions_mutator(Node *node,
InvalidOid,
InvalidOid,
&args,
- false, true, context);
+ false, true, expr->depth, context);
if (simple) /* successfully simplified output fn */
{
/*
@@ -2550,7 +2554,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;
}
@@ -2566,6 +2570,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))
@@ -2590,6 +2595,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
@@ -3406,6 +3412,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;
@@ -3436,7 +3443,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
@@ -3723,7 +3730,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);
@@ -3809,6 +3816,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 cf48ba4..44c1d83 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -43,6 +43,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/include/optimizer/prep.h b/src/include/optimizer/prep.h
index 5e94620..292ec06 100644
--- a/src/include/optimizer/prep.h
+++ b/src/include/optimizer/prep.h
@@ -25,7 +25,8 @@ extern void pull_up_sublinks(PlannerInfo *root);
extern void inline_set_returning_functions(PlannerInfo *root);
extern Node *pull_up_subqueries(PlannerInfo *root, Node *jtnode,
JoinExpr *lowest_outer_join,
- AppendRelInfo *containing_appendrel);
+ AppendRelInfo *containing_appendrel,
+ int subquery_depth);
extern void flatten_simple_union_all(PlannerInfo *root);
extern void reduce_outer_joins(PlannerInfo *root);
extern Relids get_relids_in_jointree(Node *jtnode, bool include_joins);