pgsql-fix-leaky-join-view.6.patch
text/x-patch
Filename: pgsql-fix-leaky-join-view.6.patch
Type: text/x-patch
Part: 0
Message:
Re: leaky views, yet again
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: context
| File | + | − |
|---|---|---|
| src/backend/access/common/reloptions.c | 14 | 0 |
| src/backend/commands/tablecmds.c | 11 | 0 |
| src/backend/commands/view.c | 24 | 0 |
| src/backend/nodes/copyfuncs.c | 10 | 0 |
| src/backend/nodes/equalfuncs.c | 10 | 0 |
| src/backend/nodes/outfuncs.c | 10 | 0 |
| src/backend/nodes/readfuncs.c | 10 | 0 |
| src/backend/optimizer/path/costsize.c | 17 | 0 |
| src/backend/optimizer/plan/createplan.c | 5 | 0 |
| src/backend/optimizer/plan/initsplan.c | 76 | 0 |
| src/backend/optimizer/prep/prepjointree.c | 6 | 0 |
| src/backend/optimizer/util/clauses.c | 131 | 0 |
| src/backend/parser/gram.y | 18 | 0 |
| src/backend/rewrite/rewriteHandler.c | 123 | 0 |
| src/backend/utils/cache/lsyscache.c | 19 | 0 |
| src/backend/utils/cache/relcache.c | 1 | 0 |
| src/include/access/reloptions.h | 2 | 0 |
| src/include/nodes/parsenodes.h | 3 | 0 |
| src/include/nodes/primnodes.h | 7 | 0 |
| src/include/nodes/relation.h | 1 | 0 |
| src/include/optimizer/clauses.h | 1 | 0 |
| src/include/utils/lsyscache.h | 1 | 0 |
| src/include/utils/rel.h | 1 | 0 |
*** a/src/backend/access/common/reloptions.c
--- b/src/backend/access/common/reloptions.c
***************
*** 66,71 **** static relopt_bool boolRelOpts[] =
--- 66,79 ----
},
true
},
+ {
+ {
+ "security_view",
+ "Prevent maximum optimization to prevent data leaks",
+ RELOPT_KIND_VIEW
+ },
+ false
+ },
/* list terminator */
{{NULL}}
};
***************
*** 777,782 **** extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
--- 785,791 ----
case RELKIND_RELATION:
case RELKIND_TOASTVALUE:
case RELKIND_UNCATALOGED:
+ case RELKIND_VIEW:
options = heap_reloptions(classForm->relkind, datum, false);
break;
case RELKIND_INDEX:
***************
*** 1131,1137 **** default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
{"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,
offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_scale_factor)},
{"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,
! offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, analyze_scale_factor)}
};
options = parseRelOptions(reloptions, validate, kind, &numoptions);
--- 1140,1148 ----
{"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,
offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_scale_factor)},
{"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,
! offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, analyze_scale_factor)},
! {"security_view", RELOPT_TYPE_BOOL,
! offsetof(StdRdOptions, security_view)},
};
options = parseRelOptions(reloptions, validate, kind, &numoptions);
***************
*** 1173,1178 **** heap_reloptions(char relkind, Datum reloptions, bool validate)
--- 1184,1191 ----
return (bytea *) rdopts;
case RELKIND_RELATION:
return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
+ case RELKIND_VIEW:
+ return default_reloptions(reloptions, validate, RELOPT_KIND_VIEW);
default:
/* sequences, composite types and views are not supported */
return NULL;
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 264,270 **** static void ATRewriteTables(List **wqueue, LOCKMODE lockmode);
static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
static void ATSimplePermissions(Relation rel, bool allowView);
! static void ATSimplePermissionsRelationOrIndex(Relation rel);
static void ATSimpleRecursion(List **wqueue, Relation rel,
AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode);
static void ATOneLevelRecursion(List **wqueue, Relation rel,
--- 264,270 ----
static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
static void ATSimplePermissions(Relation rel, bool allowView);
! static void ATSimplePermissionsRelationOrIndex(Relation rel, bool allowView);
static void ATSimpleRecursion(List **wqueue, Relation rel,
AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode);
static void ATOneLevelRecursion(List **wqueue, Relation rel,
***************
*** 2684,2690 **** ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
break;
case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
! ATSimplePermissionsRelationOrIndex(rel);
/* This command never recurses */
pass = AT_PASS_MISC;
break;
--- 2684,2690 ----
break;
case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
! ATSimplePermissionsRelationOrIndex(rel, false);
/* This command never recurses */
pass = AT_PASS_MISC;
break;
***************
*** 2762,2775 **** ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
pass = AT_PASS_DROP;
break;
case AT_SetTableSpace: /* SET TABLESPACE */
! ATSimplePermissionsRelationOrIndex(rel);
/* This command never recurses */
ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
pass = AT_PASS_MISC; /* doesn't actually matter */
break;
case AT_SetRelOptions: /* SET (...) */
case AT_ResetRelOptions: /* RESET (...) */
! ATSimplePermissionsRelationOrIndex(rel);
/* This command never recurses */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
--- 2762,2775 ----
pass = AT_PASS_DROP;
break;
case AT_SetTableSpace: /* SET TABLESPACE */
! ATSimplePermissionsRelationOrIndex(rel, false);
/* This command never recurses */
ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
pass = AT_PASS_MISC; /* doesn't actually matter */
break;
case AT_SetRelOptions: /* SET (...) */
case AT_ResetRelOptions: /* RESET (...) */
! ATSimplePermissionsRelationOrIndex(rel, true);
/* This command never recurses */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
***************
*** 3558,3571 **** ATSimplePermissions(Relation rel, bool allowView)
* - Ensure that it is not a system table
*/
static void
! ATSimplePermissionsRelationOrIndex(Relation rel)
{
if (rel->rd_rel->relkind != RELKIND_RELATION &&
! rel->rd_rel->relkind != RELKIND_INDEX)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
! errmsg("\"%s\" is not a table or index",
! RelationGetRelationName(rel))));
/* Permissions checks */
if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
--- 3558,3573 ----
* - Ensure that it is not a system table
*/
static void
! ATSimplePermissionsRelationOrIndex(Relation rel, bool allowView)
{
if (rel->rd_rel->relkind != RELKIND_RELATION &&
! rel->rd_rel->relkind != RELKIND_INDEX &&
! (!allowView || rel->rd_rel->relkind != RELKIND_VIEW))
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
! errmsg("\"%s\" is not a table%s or index",
! RelationGetRelationName(rel),
! allowView ? ", view" : "")));
/* Permissions checks */
if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
***************
*** 7009,7014 **** ATExecSetRelOptions(Relation rel, List *defList, bool isReset, LOCKMODE lockmode
--- 7011,7017 ----
{
case RELKIND_RELATION:
case RELKIND_TOASTVALUE:
+ case RELKIND_VIEW:
(void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
break;
case RELKIND_INDEX:
*** a/src/backend/commands/view.c
--- b/src/backend/commands/view.c
***************
*** 97,103 **** isViewOnTempTable_walker(Node *node, void *context)
*---------------------------------------------------------------------
*/
static Oid
! DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
{
Oid viewOid,
namespaceId;
--- 97,104 ----
*---------------------------------------------------------------------
*/
static Oid
! DefineVirtualRelation(const RangeVar *relation, List *tlist,
! bool replace, bool security)
{
Oid viewOid,
namespaceId;
***************
*** 148,153 **** DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
--- 149,156 ----
{
Relation rel;
TupleDesc descriptor;
+ List *atcmds = NIL;
+ AlterTableCmd *atcmd;
/*
* Yes. Get exclusive lock on the existing view ...
***************
*** 185,190 **** DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
--- 188,205 ----
descriptor = BuildDescForRelation(attrList);
checkViewTupleDesc(descriptor, rel->rd_att);
+ if (rel->rd_options || security)
+ {
+ Node *node;
+
+ node = (Node *)makeInteger(security);
+ node = (Node *)makeDefElem("security_view", node);
+ atcmd = makeNode(AlterTableCmd);
+ atcmd->subtype = AT_SetRelOptions;
+ atcmd->def = (Node *)list_make1(node);
+ atcmds = lappend(atcmds, atcmd);
+ }
+
/*
* If new attributes have been added, we must add pg_attribute entries
* for them. It is convenient (although overkill) to use the ALTER
***************
*** 192,205 **** DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
*/
if (list_length(attrList) > rel->rd_att->natts)
{
- List *atcmds = NIL;
ListCell *c;
int skip = rel->rd_att->natts;
foreach(c, attrList)
{
- AlterTableCmd *atcmd;
-
if (skip > 0)
{
skip--;
--- 207,217 ----
***************
*** 210,217 **** DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
atcmd->def = (Node *) lfirst(c);
atcmds = lappend(atcmds, atcmd);
}
- AlterTableInternal(viewOid, atcmds, true);
}
/*
* Seems okay, so return the OID of the pre-existing view.
--- 222,230 ----
atcmd->def = (Node *) lfirst(c);
atcmds = lappend(atcmds, atcmd);
}
}
+ if (atcmds != NIL)
+ AlterTableInternal(viewOid, atcmds, true);
/*
* Seems okay, so return the OID of the pre-existing view.
***************
*** 237,242 **** DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
--- 250,260 ----
createStmt->tablespacename = NULL;
createStmt->if_not_exists = false;
+ if (security)
+ createStmt->options = lappend(createStmt->options,
+ makeDefElem("security_view",
+ (Node *) makeInteger(true)));
+
/*
* finally create the relation (this will error out if there's an
* existing view, so we don't need more code to complain if "replace"
***************
*** 470,476 **** DefineView(ViewStmt *stmt, const char *queryString)
* aborted.
*/
viewOid = DefineVirtualRelation(view, viewParse->targetList,
! stmt->replace);
/*
* The relation we have just created is not visible to any other commands
--- 488,494 ----
* aborted.
*/
viewOid = DefineVirtualRelation(view, viewParse->targetList,
! stmt->replace, stmt->security);
/*
* The relation we have just created is not visible to any other commands
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 1107,1112 **** _copyFuncExpr(FuncExpr *from)
--- 1107,1113 ----
COPY_SCALAR_FIELD(funcformat);
COPY_NODE_FIELD(args);
COPY_LOCATION_FIELD(location);
+ COPY_SCALAR_FIELD(depth);
return newnode;
}
***************
*** 1141,1146 **** _copyOpExpr(OpExpr *from)
--- 1142,1148 ----
COPY_SCALAR_FIELD(opretset);
COPY_NODE_FIELD(args);
COPY_LOCATION_FIELD(location);
+ COPY_SCALAR_FIELD(depth);
return newnode;
}
***************
*** 1159,1164 **** _copyDistinctExpr(DistinctExpr *from)
--- 1161,1167 ----
COPY_SCALAR_FIELD(opretset);
COPY_NODE_FIELD(args);
COPY_LOCATION_FIELD(location);
+ COPY_SCALAR_FIELD(depth);
return newnode;
}
***************
*** 1176,1181 **** _copyScalarArrayOpExpr(ScalarArrayOpExpr *from)
--- 1179,1185 ----
COPY_SCALAR_FIELD(useOr);
COPY_NODE_FIELD(args);
COPY_LOCATION_FIELD(location);
+ COPY_SCALAR_FIELD(depth);
return newnode;
}
***************
*** 1312,1317 **** _copyCoerceViaIO(CoerceViaIO *from)
--- 1316,1322 ----
COPY_SCALAR_FIELD(resulttype);
COPY_SCALAR_FIELD(coerceformat);
COPY_LOCATION_FIELD(location);
+ COPY_SCALAR_FIELD(depth);
return newnode;
}
***************
*** 1331,1336 **** _copyArrayCoerceExpr(ArrayCoerceExpr *from)
--- 1336,1342 ----
COPY_SCALAR_FIELD(isExplicit);
COPY_SCALAR_FIELD(coerceformat);
COPY_LOCATION_FIELD(location);
+ COPY_SCALAR_FIELD(depth);
return newnode;
}
***************
*** 1444,1449 **** _copyRowCompareExpr(RowCompareExpr *from)
--- 1450,1456 ----
COPY_NODE_FIELD(opfamilies);
COPY_NODE_FIELD(largs);
COPY_NODE_FIELD(rargs);
+ COPY_SCALAR_FIELD(depth);
return newnode;
}
***************
*** 1514,1519 **** _copyNullIfExpr(NullIfExpr *from)
--- 1521,1527 ----
COPY_SCALAR_FIELD(opretset);
COPY_NODE_FIELD(args);
COPY_LOCATION_FIELD(location);
+ COPY_SCALAR_FIELD(depth);
return newnode;
}
***************
*** 1671,1676 **** _copyFromExpr(FromExpr *from)
--- 1679,1685 ----
COPY_NODE_FIELD(fromlist);
COPY_NODE_FIELD(quals);
+ COPY_SCALAR_FIELD(security_view);
return newnode;
}
***************
*** 1824,1829 **** _copyRangeTblEntry(RangeTblEntry *from)
--- 1833,1839 ----
COPY_SCALAR_FIELD(rtekind);
COPY_SCALAR_FIELD(relid);
COPY_NODE_FIELD(subquery);
+ COPY_SCALAR_FIELD(security_view);
COPY_SCALAR_FIELD(jointype);
COPY_NODE_FIELD(joinaliasvars);
COPY_NODE_FIELD(funcexpr);
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 238,243 **** _equalFuncExpr(FuncExpr *a, FuncExpr *b)
--- 238,244 ----
COMPARE_NODE_FIELD(args);
COMPARE_LOCATION_FIELD(location);
+ COMPARE_SCALAR_FIELD(depth);
return true;
}
***************
*** 273,278 **** _equalOpExpr(OpExpr *a, OpExpr *b)
--- 274,280 ----
COMPARE_SCALAR_FIELD(opretset);
COMPARE_NODE_FIELD(args);
COMPARE_LOCATION_FIELD(location);
+ COMPARE_SCALAR_FIELD(depth);
return true;
}
***************
*** 297,302 **** _equalDistinctExpr(DistinctExpr *a, DistinctExpr *b)
--- 299,305 ----
COMPARE_SCALAR_FIELD(opretset);
COMPARE_NODE_FIELD(args);
COMPARE_LOCATION_FIELD(location);
+ COMPARE_SCALAR_FIELD(depth);
return true;
}
***************
*** 320,325 **** _equalScalarArrayOpExpr(ScalarArrayOpExpr *a, ScalarArrayOpExpr *b)
--- 323,329 ----
COMPARE_SCALAR_FIELD(useOr);
COMPARE_NODE_FIELD(args);
COMPARE_LOCATION_FIELD(location);
+ COMPARE_SCALAR_FIELD(depth);
return true;
}
***************
*** 434,439 **** _equalCoerceViaIO(CoerceViaIO *a, CoerceViaIO *b)
--- 438,444 ----
return false;
COMPARE_LOCATION_FIELD(location);
+ COMPARE_SCALAR_FIELD(depth);
return true;
}
***************
*** 457,462 **** _equalArrayCoerceExpr(ArrayCoerceExpr *a, ArrayCoerceExpr *b)
--- 462,468 ----
return false;
COMPARE_LOCATION_FIELD(location);
+ COMPARE_SCALAR_FIELD(depth);
return true;
}
***************
*** 553,558 **** _equalRowCompareExpr(RowCompareExpr *a, RowCompareExpr *b)
--- 559,565 ----
COMPARE_NODE_FIELD(opfamilies);
COMPARE_NODE_FIELD(largs);
COMPARE_NODE_FIELD(rargs);
+ COMPARE_SCALAR_FIELD(depth);
return true;
}
***************
*** 614,619 **** _equalNullIfExpr(NullIfExpr *a, NullIfExpr *b)
--- 621,627 ----
COMPARE_SCALAR_FIELD(opretset);
COMPARE_NODE_FIELD(args);
COMPARE_LOCATION_FIELD(location);
+ COMPARE_SCALAR_FIELD(depth);
return true;
}
***************
*** 730,735 **** _equalFromExpr(FromExpr *a, FromExpr *b)
--- 738,744 ----
{
COMPARE_NODE_FIELD(fromlist);
COMPARE_NODE_FIELD(quals);
+ COMPARE_SCALAR_FIELD(security_view);
return true;
}
***************
*** 2170,2175 **** _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
--- 2179,2185 ----
COMPARE_SCALAR_FIELD(rtekind);
COMPARE_SCALAR_FIELD(relid);
COMPARE_NODE_FIELD(subquery);
+ COMPARE_SCALAR_FIELD(security_view);
COMPARE_SCALAR_FIELD(jointype);
COMPARE_NODE_FIELD(joinaliasvars);
COMPARE_NODE_FIELD(funcexpr);
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
***************
*** 930,935 **** _outFuncExpr(StringInfo str, FuncExpr *node)
--- 930,936 ----
WRITE_ENUM_FIELD(funcformat, CoercionForm);
WRITE_NODE_FIELD(args);
WRITE_LOCATION_FIELD(location);
+ WRITE_INT_FIELD(depth);
}
static void
***************
*** 954,959 **** _outOpExpr(StringInfo str, OpExpr *node)
--- 955,961 ----
WRITE_BOOL_FIELD(opretset);
WRITE_NODE_FIELD(args);
WRITE_LOCATION_FIELD(location);
+ WRITE_INT_FIELD(depth);
}
static void
***************
*** 967,972 **** _outDistinctExpr(StringInfo str, DistinctExpr *node)
--- 969,975 ----
WRITE_BOOL_FIELD(opretset);
WRITE_NODE_FIELD(args);
WRITE_LOCATION_FIELD(location);
+ WRITE_INT_FIELD(depth);
}
static void
***************
*** 979,984 **** _outScalarArrayOpExpr(StringInfo str, ScalarArrayOpExpr *node)
--- 982,988 ----
WRITE_BOOL_FIELD(useOr);
WRITE_NODE_FIELD(args);
WRITE_LOCATION_FIELD(location);
+ WRITE_INT_FIELD(depth);
}
static void
***************
*** 1092,1097 **** _outCoerceViaIO(StringInfo str, CoerceViaIO *node)
--- 1096,1102 ----
WRITE_OID_FIELD(resulttype);
WRITE_ENUM_FIELD(coerceformat, CoercionForm);
WRITE_LOCATION_FIELD(location);
+ WRITE_INT_FIELD(depth);
}
static void
***************
*** 1106,1111 **** _outArrayCoerceExpr(StringInfo str, ArrayCoerceExpr *node)
--- 1111,1117 ----
WRITE_BOOL_FIELD(isExplicit);
WRITE_ENUM_FIELD(coerceformat, CoercionForm);
WRITE_LOCATION_FIELD(location);
+ WRITE_INT_FIELD(depth);
}
static void
***************
*** 1184,1189 **** _outRowCompareExpr(StringInfo str, RowCompareExpr *node)
--- 1190,1196 ----
WRITE_NODE_FIELD(opfamilies);
WRITE_NODE_FIELD(largs);
WRITE_NODE_FIELD(rargs);
+ WRITE_INT_FIELD(depth);
}
static void
***************
*** 1234,1239 **** _outNullIfExpr(StringInfo str, NullIfExpr *node)
--- 1241,1247 ----
WRITE_BOOL_FIELD(opretset);
WRITE_NODE_FIELD(args);
WRITE_LOCATION_FIELD(location);
+ WRITE_INT_FIELD(depth);
}
static void
***************
*** 1341,1346 **** _outFromExpr(StringInfo str, FromExpr *node)
--- 1349,1355 ----
WRITE_NODE_FIELD(fromlist);
WRITE_NODE_FIELD(quals);
+ WRITE_BOOL_FIELD(security_view);
}
/*****************************************************************************
***************
*** 2120,2125 **** _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
--- 2129,2135 ----
break;
case RTE_SUBQUERY:
WRITE_NODE_FIELD(subquery);
+ WRITE_BOOL_FIELD(security_view);
break;
case RTE_JOIN:
WRITE_ENUM_FIELD(jointype, JoinType);
*** a/src/backend/nodes/readfuncs.c
--- b/src/backend/nodes/readfuncs.c
***************
*** 526,531 **** _readFuncExpr(void)
--- 526,532 ----
READ_ENUM_FIELD(funcformat, CoercionForm);
READ_NODE_FIELD(args);
READ_LOCATION_FIELD(location);
+ READ_INT_FIELD(depth);
READ_DONE();
}
***************
*** 571,576 **** _readOpExpr(void)
--- 572,578 ----
READ_BOOL_FIELD(opretset);
READ_NODE_FIELD(args);
READ_LOCATION_FIELD(location);
+ READ_INT_FIELD(depth);
READ_DONE();
}
***************
*** 600,605 **** _readDistinctExpr(void)
--- 602,608 ----
READ_BOOL_FIELD(opretset);
READ_NODE_FIELD(args);
READ_LOCATION_FIELD(location);
+ READ_INT_FIELD(depth);
READ_DONE();
}
***************
*** 628,633 **** _readScalarArrayOpExpr(void)
--- 631,637 ----
READ_BOOL_FIELD(useOr);
READ_NODE_FIELD(args);
READ_LOCATION_FIELD(location);
+ READ_INT_FIELD(depth);
READ_DONE();
}
***************
*** 740,745 **** _readCoerceViaIO(void)
--- 744,750 ----
READ_OID_FIELD(resulttype);
READ_ENUM_FIELD(coerceformat, CoercionForm);
READ_LOCATION_FIELD(location);
+ READ_INT_FIELD(depth);
READ_DONE();
}
***************
*** 759,764 **** _readArrayCoerceExpr(void)
--- 764,770 ----
READ_BOOL_FIELD(isExplicit);
READ_ENUM_FIELD(coerceformat, CoercionForm);
READ_LOCATION_FIELD(location);
+ READ_INT_FIELD(depth);
READ_DONE();
}
***************
*** 872,877 **** _readRowCompareExpr(void)
--- 878,884 ----
READ_NODE_FIELD(opfamilies);
READ_NODE_FIELD(largs);
READ_NODE_FIELD(rargs);
+ READ_INT_FIELD(depth);
READ_DONE();
}
***************
*** 953,958 **** _readNullIfExpr(void)
--- 960,966 ----
READ_BOOL_FIELD(opretset);
READ_NODE_FIELD(args);
READ_LOCATION_FIELD(location);
+ READ_INT_FIELD(depth);
READ_DONE();
}
***************
*** 1110,1115 **** _readFromExpr(void)
--- 1118,1124 ----
READ_NODE_FIELD(fromlist);
READ_NODE_FIELD(quals);
+ READ_BOOL_FIELD(security_view);
READ_DONE();
}
***************
*** 1140,1145 **** _readRangeTblEntry(void)
--- 1149,1155 ----
break;
case RTE_SUBQUERY:
READ_NODE_FIELD(subquery);
+ READ_BOOL_FIELD(security_view);
break;
case RTE_JOIN:
READ_ENUM_FIELD(jointype, JoinType);
*** a/src/backend/optimizer/path/costsize.c
--- b/src/backend/optimizer/path/costsize.c
***************
*** 2456,2461 **** cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root)
--- 2456,2462 ----
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 ... */
***************
*** 2481,2486 **** cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root)
--- 2482,2488 ----
context.root = root;
context.total.startup = 0;
context.total.per_tuple = 0;
+ context.total.depth = 0;
cost_qual_eval_walker(qual, &context);
***************
*** 2510,2515 **** cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
--- 2512,2518 ----
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
***************
*** 2534,2539 **** cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
--- 2537,2544 ----
}
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;
}
***************
*** 2562,2567 **** cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
--- 2567,2574 ----
{
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) ||
***************
*** 2571,2576 **** cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
--- 2578,2585 ----
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))
{
***************
*** 2584,2589 **** cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
--- 2593,2600 ----
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, CoerceViaIO))
{
***************
*** 2600,2605 **** cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
--- 2611,2618 ----
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))
{
***************
*** 2609,2614 **** cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
--- 2622,2629 ----
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))
{
***************
*** 2623,2628 **** cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
--- 2638,2645 ----
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))
{
*** a/src/backend/optimizer/plan/createplan.c
--- b/src/backend/optimizer/plan/createplan.c
***************
*** 2443,2448 **** order_qual_clauses(PlannerInfo *root, List *clauses)
--- 2443,2449 ----
{
Node *clause;
Cost cost;
+ int depth;
} QualItem;
int nitems = list_length(clauses);
QualItem *items;
***************
*** 2468,2473 **** order_qual_clauses(PlannerInfo *root, List *clauses)
--- 2469,2475 ----
cost_qual_eval_node(&qcost, clause, root);
items[i].clause = clause;
items[i].cost = qcost.per_tuple;
+ items[i].depth = qcost.depth;
i++;
}
***************
*** 2484,2490 **** 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)
break;
items[j] = items[j - 1];
}
--- 2486,2494 ----
/* insert newitem into the already-sorted subarray */
for (j = i; j > 0; j--)
{
! 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];
}
*** a/src/backend/optimizer/plan/initsplan.c
--- b/src/backend/optimizer/plan/initsplan.c
***************
*** 40,46 **** int join_collapse_limit;
static List *deconstruct_recurse(PlannerInfo *root, Node *jtnode,
bool below_outer_join,
! Relids *qualscope, Relids *inner_join_rels);
static SpecialJoinInfo *make_outerjoininfo(PlannerInfo *root,
Relids left_rels, Relids right_rels,
Relids inner_join_rels,
--- 40,47 ----
static List *deconstruct_recurse(PlannerInfo *root, Node *jtnode,
bool below_outer_join,
! Relids *qualscope, Relids *inner_join_rels,
! bool below_sec_barriers, Relids *sec_barriers);
static SpecialJoinInfo *make_outerjoininfo(PlannerInfo *root,
Relids left_rels, Relids right_rels,
Relids inner_join_rels,
***************
*** 51,57 **** static void distribute_qual_to_rels(PlannerInfo *root, Node *clause,
JoinType jointype,
Relids qualscope,
Relids ojscope,
! Relids outerjoin_nonnullable);
static bool check_outerjoin_delay(PlannerInfo *root, Relids *relids_p,
Relids *nullable_relids_p, bool is_pushed_down);
static bool check_redundant_nullability_qual(PlannerInfo *root, Node *clause);
--- 52,59 ----
JoinType jointype,
Relids qualscope,
Relids ojscope,
! Relids outerjoin_nonnullable,
! Relids sec_barriers);
static bool check_outerjoin_delay(PlannerInfo *root, Relids *relids_p,
Relids *nullable_relids_p, bool is_pushed_down);
static bool check_redundant_nullability_qual(PlannerInfo *root, Node *clause);
***************
*** 231,243 **** deconstruct_jointree(PlannerInfo *root)
{
Relids qualscope;
Relids inner_join_rels;
/* Start recursion at top of jointree */
! Assert(root->parse->jointree != NULL &&
! IsA(root->parse->jointree, FromExpr));
! return deconstruct_recurse(root, (Node *) root->parse->jointree, false,
! &qualscope, &inner_join_rels);
}
/*
--- 233,247 ----
{
Relids qualscope;
Relids inner_join_rels;
+ Relids sec_barriers;
+ FromExpr *f = (FromExpr *)root->parse->jointree;
/* Start recursion at top of jointree */
! Assert(root->parse->jointree != NULL && IsA(f, FromExpr));
! return deconstruct_recurse(root, (Node *) f, false,
! &qualscope, &inner_join_rels,
! f->security_view, &sec_barriers);
}
/*
***************
*** 261,267 **** deconstruct_jointree(PlannerInfo *root)
*/
static List *
deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
! Relids *qualscope, Relids *inner_join_rels)
{
List *joinlist;
--- 265,272 ----
*/
static List *
deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
! Relids *qualscope, Relids *inner_join_rels,
! bool below_sec_barriers, Relids *sec_barriers)
{
List *joinlist;
***************
*** 280,285 **** deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
--- 285,293 ----
/* A single baserel does not create an inner join */
*inner_join_rels = NULL;
joinlist = list_make1(jtnode);
+ /* Is it in security barrier? */
+ *sec_barriers = (below_sec_barriers ?
+ bms_make_singleton(varno) : NULL);
}
else if (IsA(jtnode, FromExpr))
{
***************
*** 295,300 **** deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
--- 303,309 ----
*/
*qualscope = NULL;
*inner_join_rels = NULL;
+ *sec_barriers = NULL;
joinlist = NIL;
remaining = list_length(f->fromlist);
foreach(l, f->fromlist)
***************
*** 302,313 **** deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
Relids sub_qualscope;
List *sub_joinlist;
int sub_members;
sub_joinlist = deconstruct_recurse(root, lfirst(l),
below_outer_join,
&sub_qualscope,
! inner_join_rels);
*qualscope = bms_add_members(*qualscope, sub_qualscope);
sub_members = list_length(sub_joinlist);
remaining--;
if (sub_members <= 1 ||
--- 311,327 ----
Relids sub_qualscope;
List *sub_joinlist;
int sub_members;
+ Relids sub_barriers;
sub_joinlist = deconstruct_recurse(root, lfirst(l),
below_outer_join,
&sub_qualscope,
! inner_join_rels,
! below_sec_barriers ?
! true : f->security_view,
! &sub_barriers);
*qualscope = bms_add_members(*qualscope, sub_qualscope);
+ *sec_barriers = bms_add_members(*sec_barriers, sub_barriers);
sub_members = list_length(sub_joinlist);
remaining--;
if (sub_members <= 1 ||
***************
*** 336,342 **** deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
distribute_qual_to_rels(root, qual,
false, below_outer_join, JOIN_INNER,
! *qualscope, NULL, NULL);
}
}
else if (IsA(jtnode, JoinExpr))
--- 350,356 ----
distribute_qual_to_rels(root, qual,
false, below_outer_join, JOIN_INNER,
! *qualscope, NULL, NULL, *sec_barriers);
}
}
else if (IsA(jtnode, JoinExpr))
***************
*** 346,351 **** deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
--- 360,367 ----
rightids,
left_inners,
right_inners,
+ left_barriers,
+ right_barriers,
nonnullable_rels,
ojscope;
List *leftjoinlist,
***************
*** 370,381 **** deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
case JOIN_INNER:
leftjoinlist = deconstruct_recurse(root, j->larg,
below_outer_join,
! &leftids, &left_inners);
rightjoinlist = deconstruct_recurse(root, j->rarg,
below_outer_join,
! &rightids, &right_inners);
*qualscope = bms_union(leftids, rightids);
*inner_join_rels = *qualscope;
/* Inner join adds no restrictions for quals */
nonnullable_rels = NULL;
break;
--- 386,402 ----
case JOIN_INNER:
leftjoinlist = deconstruct_recurse(root, j->larg,
below_outer_join,
! &leftids, &left_inners,
! below_sec_barriers,
! &left_barriers);
rightjoinlist = deconstruct_recurse(root, j->rarg,
below_outer_join,
! &rightids, &right_inners,
! below_sec_barriers,
! &right_barriers);
*qualscope = bms_union(leftids, rightids);
*inner_join_rels = *qualscope;
+ *sec_barriers = bms_union(left_barriers, right_barriers);
/* Inner join adds no restrictions for quals */
nonnullable_rels = NULL;
break;
***************
*** 383,417 **** deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
case JOIN_ANTI:
leftjoinlist = deconstruct_recurse(root, j->larg,
below_outer_join,
! &leftids, &left_inners);
rightjoinlist = deconstruct_recurse(root, j->rarg,
true,
! &rightids, &right_inners);
*qualscope = bms_union(leftids, rightids);
*inner_join_rels = bms_union(left_inners, right_inners);
nonnullable_rels = leftids;
break;
case JOIN_SEMI:
leftjoinlist = deconstruct_recurse(root, j->larg,
below_outer_join,
! &leftids, &left_inners);
rightjoinlist = deconstruct_recurse(root, j->rarg,
below_outer_join,
! &rightids, &right_inners);
*qualscope = bms_union(leftids, rightids);
*inner_join_rels = bms_union(left_inners, right_inners);
/* Semi join adds no restrictions for quals */
nonnullable_rels = NULL;
break;
case JOIN_FULL:
leftjoinlist = deconstruct_recurse(root, j->larg,
true,
! &leftids, &left_inners);
rightjoinlist = deconstruct_recurse(root, j->rarg,
true,
! &rightids, &right_inners);
*qualscope = bms_union(leftids, rightids);
*inner_join_rels = bms_union(left_inners, right_inners);
/* each side is both outer and inner */
nonnullable_rels = *qualscope;
break;
--- 404,453 ----
case JOIN_ANTI:
leftjoinlist = deconstruct_recurse(root, j->larg,
below_outer_join,
! &leftids, &left_inners,
! below_sec_barriers,
! &left_barriers);
rightjoinlist = deconstruct_recurse(root, j->rarg,
true,
! &rightids, &right_inners,
! below_sec_barriers,
! &right_barriers);
*qualscope = bms_union(leftids, rightids);
*inner_join_rels = bms_union(left_inners, right_inners);
+ *sec_barriers = bms_union(left_barriers, right_barriers);
nonnullable_rels = leftids;
break;
case JOIN_SEMI:
leftjoinlist = deconstruct_recurse(root, j->larg,
below_outer_join,
! &leftids, &left_inners,
! below_sec_barriers,
! &left_barriers);
rightjoinlist = deconstruct_recurse(root, j->rarg,
below_outer_join,
! &rightids, &right_inners,
! below_sec_barriers,
! &right_barriers);
*qualscope = bms_union(leftids, rightids);
*inner_join_rels = bms_union(left_inners, right_inners);
+ *sec_barriers = bms_union(left_barriers, right_barriers);
/* Semi join adds no restrictions for quals */
nonnullable_rels = NULL;
break;
case JOIN_FULL:
leftjoinlist = deconstruct_recurse(root, j->larg,
true,
! &leftids, &left_inners,
! below_sec_barriers,
! &left_barriers);
rightjoinlist = deconstruct_recurse(root, j->rarg,
true,
! &rightids, &right_inners,
! below_sec_barriers,
! &right_barriers);
*qualscope = bms_union(leftids, rightids);
*inner_join_rels = bms_union(left_inners, right_inners);
+ *sec_barriers = bms_union(left_barriers, right_barriers);
/* each side is both outer and inner */
nonnullable_rels = *qualscope;
break;
***************
*** 460,466 **** deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
distribute_qual_to_rels(root, qual,
false, below_outer_join, j->jointype,
*qualscope,
! ojscope, nonnullable_rels);
}
/* Now we can add the SpecialJoinInfo to join_info_list */
--- 496,503 ----
distribute_qual_to_rels(root, qual,
false, below_outer_join, j->jointype,
*qualscope,
! ojscope, nonnullable_rels,
! *sec_barriers);
}
/* Now we can add the SpecialJoinInfo to join_info_list */
***************
*** 754,760 **** distribute_qual_to_rels(PlannerInfo *root, Node *clause,
JoinType jointype,
Relids qualscope,
Relids ojscope,
! Relids outerjoin_nonnullable)
{
Relids relids;
bool is_pushed_down;
--- 791,798 ----
JoinType jointype,
Relids qualscope,
Relids ojscope,
! Relids outerjoin_nonnullable,
! Relids sec_barriers)
{
Relids relids;
bool is_pushed_down;
***************
*** 762,767 **** distribute_qual_to_rels(PlannerInfo *root, Node *clause,
--- 800,806 ----
bool pseudoconstant = false;
bool maybe_equivalence;
bool maybe_outer_join;
+ bool maybe_leakable_clause = false;
Relids nullable_relids;
RestrictInfo *restrictinfo;
***************
*** 778,783 **** distribute_qual_to_rels(PlannerInfo *root, Node *clause,
--- 817,824 ----
elog(ERROR, "JOIN qualification cannot refer to other relations");
if (ojscope && !bms_is_subset(relids, ojscope))
elog(ERROR, "JOIN qualification cannot refer to other relations");
+ if (sec_barriers && !bms_is_subset(sec_barriers, qualscope))
+ elog(ERROR, "Bug? incorrect security barriers");
/*
* If the clause is variable-free, our normal heuristic for pushing it
***************
*** 834,839 **** distribute_qual_to_rels(PlannerInfo *root, Node *clause,
--- 875,894 ----
}
}
+ /*
+ * If the clause contains a leakable function, it may be used to
+ * bypass row-level security using views. In this case, we don't
+ * push down the clause not to evaluate the leakable clause prior
+ * to the row-level policy functions.
+ */
+ if (!bms_is_empty(sec_barriers) &&
+ contain_leakable_functions(clause) &&
+ bms_overlap(relids, sec_barriers))
+ {
+ maybe_leakable_clause = true;
+ relids = bms_add_members(relids, sec_barriers);
+ }
+
/*----------
* Check to see if clause application must be delayed by outer-join
* considerations.
***************
*** 1030,1036 **** distribute_qual_to_rels(PlannerInfo *root, Node *clause,
* If none of the above hold, pass it off to
* distribute_restrictinfo_to_rels().
*/
! if (restrictinfo->mergeopfamilies)
{
if (maybe_equivalence)
{
--- 1085,1091 ----
* If none of the above hold, pass it off to
* distribute_restrictinfo_to_rels().
*/
! if (!maybe_leakable_clause && restrictinfo->mergeopfamilies)
{
if (maybe_equivalence)
{
***************
*** 1359,1365 **** process_implied_equality(PlannerInfo *root,
*/
distribute_qual_to_rels(root, (Node *) clause,
true, below_outer_join, JOIN_INNER,
! qualscope, NULL, NULL);
}
/*
--- 1414,1420 ----
*/
distribute_qual_to_rels(root, (Node *) clause,
true, below_outer_join, JOIN_INNER,
! qualscope, NULL, NULL, NULL);
}
/*
*** a/src/backend/optimizer/prep/prepjointree.c
--- b/src/backend/optimizer/prep/prepjointree.c
***************
*** 845,850 **** pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
--- 845,856 ----
*/
/*
+ * If the subquery is originated from security view, we mark it here
+ * to prevent over optimization later.
+ */
+ ((FromExpr *) subquery->jointree)->security_view = rte->security_view;
+
+ /*
* Return the adjusted subquery jointree to replace the RangeTblRef entry
* in parent's jointree.
*/
*** a/src/backend/optimizer/util/clauses.c
--- b/src/backend/optimizer/util/clauses.c
***************
*** 86,91 **** static bool contain_subplans_walker(Node *node, void *context);
--- 86,92 ----
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_leakable_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);
***************
*** 103,108 **** static Expr *simplify_function(Oid funcid,
--- 104,110 ----
Oid result_type, int32 result_typmod, 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,
***************
*** 115,121 **** 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, List *args,
! HeapTuple func_tuple,
eval_const_expressions_context *context);
static Expr *inline_function(Oid funcid, Oid result_type, List *args,
HeapTuple func_tuple,
--- 117,123 ----
HeapTuple func_tuple);
static Expr *evaluate_function(Oid funcid,
Oid result_type, int32 result_typmod, List *args,
! HeapTuple func_tuple, int depth,
eval_const_expressions_context *context);
static Expr *inline_function(Oid funcid, Oid result_type, List *args,
HeapTuple func_tuple,
***************
*** 1129,1134 **** contain_nonstrict_functions_walker(Node *node, void *context)
--- 1131,1250 ----
}
+
+ /*****************************************************************************
+ * Check clauses for leakable functions
+ *****************************************************************************/
+
+ /*
+ * contain_leakable_functions
+ * Recursively search for leakable functions within a clause.
+ *
+ * Returns true if any function call with side-effect is found.
+ * ie, some type-input/output handler will raise an error when given
+ * argument does not have a valid format.
+ *
+ * When people uses views for row-level security purpose, given qualifiers
+ * come from outside of the view should not be pushed down into the views,
+ * if they have side-effect, because contents of tuples to be filtered out
+ * may be leaked via side-effectable functions within the qualifiers.
+ *
+ * The idea here is that the planner restrain a part of optimization when
+ * the qualifiers contains leakable functions.
+ * This routine checks whether the given clause contains leakable functions,
+ * or not. If we return false, then the clause is clean.
+ */
+ bool
+ contain_leakable_functions(Node *clause)
+ {
+ return contain_leakable_functions_walker(clause, NULL);
+ }
+
+ static bool
+ contain_leakable_functions_walker(Node *node, void *context)
+ {
+ if (node == NULL)
+ return false;
+
+ if (IsA(node, FuncExpr))
+ {
+ /*
+ * currently, we have no way to distinguish a safe function and
+ * a leakable one, so all the function call shall be considered
+ * as leakable one.
+ */
+ return true;
+ }
+ else if (IsA(node, OpExpr))
+ {
+ OpExpr *expr = (OpExpr *) node;
+
+ /*
+ * we assume built-in functions to implement operators are not
+ * leakable, so don't need to prevent optimization.
+ */
+ set_opfuncid(expr);
+ if (get_func_lang(expr->opfuncid) != INTERNALlanguageId)
+ return true;
+ /* else fall through to check args */
+ }
+ else if (IsA(node, DistinctExpr))
+ {
+ DistinctExpr *expr = (DistinctExpr *) node;
+
+ set_opfuncid((OpExpr *) expr);
+ if (get_func_lang(expr->opfuncid) != INTERNALlanguageId)
+ return true;
+ /* else fall through to check args */
+ }
+ else if (IsA(node, ScalarArrayOpExpr))
+ {
+ ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
+
+ set_sa_opfuncid(expr);
+ if (get_func_lang(expr->opfuncid) != INTERNALlanguageId)
+ return true;
+ /* else fall through to check args */
+ }
+ else if (IsA(node, CoerceViaIO) ||
+ IsA(node, ArrayCoerceExpr))
+ {
+ /*
+ * we assume type-in/out handlers are leakable, even if built-in
+ * functions.
+ * ie, int4in() raises an error message with given argument,
+ * if it does not have valid format for numeric value.
+ */
+ return true;
+ }
+ else if (IsA(node, NullIfExpr))
+ {
+ NullIfExpr *expr = (NullIfExpr *) node;
+
+ set_opfuncid((OpExpr *) expr); /* rely on struct equivalence */
+ if (get_func_lang(expr->opfuncid) != INTERNALlanguageId)
+ return true;
+ /* else fall through to check args */
+ }
+ else if (IsA(node, RowCompareExpr))
+ {
+ /* RowCompare probably can't have volatile ops, but check anyway */
+ RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+ ListCell *opid;
+
+ foreach(opid, rcexpr->opnos)
+ {
+ Oid funcId = get_opcode(lfirst_oid(opid));
+
+ if (get_func_lang(funcId) != INTERNALlanguageId)
+ return true;
+ }
+ /* else fall through to check args */
+ }
+ return expression_tree_walker(node, contain_leakable_functions_walker,
+ context);
+ }
+
/*
* find_nonnullable_rels
* Determine which base rels are forced nonnullable by given clause.
***************
*** 2169,2175 **** eval_const_expressions_mutator(Node *node,
simple = simplify_function(expr->funcid,
expr->funcresulttype, exprTypmod(node),
&args,
! has_named_args, true, context);
if (simple) /* successfully simplified it */
return (Node *) simple;
--- 2285,2291 ----
simple = simplify_function(expr->funcid,
expr->funcresulttype, exprTypmod(node),
&args,
! has_named_args, true, expr->depth, context);
if (simple) /* successfully simplified it */
return (Node *) simple;
***************
*** 2186,2191 **** eval_const_expressions_mutator(Node *node,
--- 2302,2308 ----
newexpr->funcformat = expr->funcformat;
newexpr->args = args;
newexpr->location = expr->location;
+ newexpr->depth = expr->depth;
return (Node *) newexpr;
}
if (IsA(node, OpExpr))
***************
*** 2217,2223 **** eval_const_expressions_mutator(Node *node,
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
&args,
! false, true, context);
if (simple) /* successfully simplified it */
return (Node *) simple;
--- 2334,2340 ----
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
&args,
! false, true, expr->depth, context);
if (simple) /* successfully simplified it */
return (Node *) simple;
***************
*** 2246,2251 **** eval_const_expressions_mutator(Node *node,
--- 2363,2369 ----
newexpr->opretset = expr->opretset;
newexpr->args = args;
newexpr->location = expr->location;
+ newexpr->depth = expr->depth;
return (Node *) newexpr;
}
if (IsA(node, DistinctExpr))
***************
*** 2310,2316 **** eval_const_expressions_mutator(Node *node,
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
&args,
! false, false, context);
if (simple) /* successfully simplified it */
{
/*
--- 2428,2434 ----
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
&args,
! false, false, expr->depth, context);
if (simple) /* successfully simplified it */
{
/*
***************
*** 2338,2343 **** eval_const_expressions_mutator(Node *node,
--- 2456,2462 ----
newexpr->opretset = expr->opretset;
newexpr->args = args;
newexpr->location = expr->location;
+ newexpr->depth = expr->depth;
return (Node *) newexpr;
}
if (IsA(node, BoolExpr))
***************
*** 2502,2508 **** eval_const_expressions_mutator(Node *node,
simple = simplify_function(outfunc,
CSTRINGOID, -1,
&args,
! false, true, context);
if (simple) /* successfully simplified output fn */
{
/*
--- 2621,2627 ----
simple = simplify_function(outfunc,
CSTRINGOID, -1,
&args,
! false, true, expr->depth, context);
if (simple) /* successfully simplified output fn */
{
/*
***************
*** 2520,2526 **** eval_const_expressions_mutator(Node *node,
simple = simplify_function(infunc,
expr->resulttype, -1,
&args,
! false, true, context);
if (simple) /* successfully simplified input fn */
return (Node *) simple;
}
--- 2639,2645 ----
simple = simplify_function(infunc,
expr->resulttype, -1,
&args,
! false, true, expr->depth, context);
if (simple) /* successfully simplified input fn */
return (Node *) simple;
}
***************
*** 2535,2540 **** eval_const_expressions_mutator(Node *node,
--- 2654,2660 ----
newexpr->resulttype = expr->resulttype;
newexpr->coerceformat = expr->coerceformat;
newexpr->location = expr->location;
+ newexpr->depth = expr->depth;
return (Node *) newexpr;
}
if (IsA(node, ArrayCoerceExpr))
***************
*** 2558,2563 **** eval_const_expressions_mutator(Node *node,
--- 2678,2684 ----
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
***************
*** 3292,3297 **** simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
--- 3413,3419 ----
List **args,
bool has_named_args,
bool allow_inline,
+ int depth,
eval_const_expressions_context *context)
{
HeapTuple func_tuple;
***************
*** 3320,3326 **** simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
*args = add_function_defaults(*args, result_type, func_tuple, context);
newexpr = evaluate_function(funcid, result_type, result_typmod, *args,
! func_tuple, context);
if (!newexpr && allow_inline)
newexpr = inline_function(funcid, result_type, *args,
--- 3442,3448 ----
*args = add_function_defaults(*args, result_type, func_tuple, context);
newexpr = evaluate_function(funcid, result_type, result_typmod, *args,
! func_tuple, depth, context);
if (!newexpr && allow_inline)
newexpr = inline_function(funcid, result_type, *args,
***************
*** 3571,3577 **** recheck_cast_function_args(List *args, Oid result_type, HeapTuple func_tuple)
*/
static Expr *
evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, List *args,
! HeapTuple func_tuple,
eval_const_expressions_context *context)
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
--- 3693,3699 ----
*/
static Expr *
evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, List *args,
! HeapTuple func_tuple, int depth,
eval_const_expressions_context *context)
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
***************
*** 3654,3659 **** evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, List *args,
--- 3776,3782 ----
newexpr->funcformat = COERCE_DONTCARE; /* doesn't matter */
newexpr->args = args;
newexpr->location = -1;
+ newexpr->depth = depth;
return evaluate_expr((Expr *) newexpr, result_type, result_typmod);
}
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 310,316 **** static TypeName *TableFuncTypeName(List *columns);
%type <fun_param_mode> arg_class
%type <typnam> func_return func_type
! %type <boolean> TriggerForType OptTemp
%type <oncommit> OnCommitOption
%type <node> for_locking_item
--- 310,316 ----
%type <fun_param_mode> arg_class
%type <typnam> func_return func_type
! %type <boolean> TriggerForType OptTemp OptSecurity
%type <oncommit> OnCommitOption
%type <node> for_locking_item
***************
*** 6355,6389 **** transaction_mode_list_or_empty:
/*****************************************************************************
*
* QUERY:
! * CREATE [ OR REPLACE ] [ TEMP ] VIEW <viewname> '('target-list ')'
* AS <query> [ WITH [ CASCADED | LOCAL ] CHECK OPTION ]
*
*****************************************************************************/
! ViewStmt: CREATE OptTemp VIEW qualified_name opt_column_list
AS SelectStmt opt_check_option
{
ViewStmt *n = makeNode(ViewStmt);
! n->view = $4;
n->view->istemp = $2;
! n->aliases = $5;
! n->query = $7;
n->replace = false;
$$ = (Node *) n;
}
! | CREATE OR REPLACE OptTemp VIEW qualified_name opt_column_list
AS SelectStmt opt_check_option
{
ViewStmt *n = makeNode(ViewStmt);
! n->view = $6;
n->view->istemp = $4;
! n->aliases = $7;
! n->query = $9;
n->replace = true;
$$ = (Node *) n;
}
;
opt_check_option:
WITH CHECK OPTION
{
--- 6355,6397 ----
/*****************************************************************************
*
* QUERY:
! * CREATE [ OR REPLACE ] [ TEMP ] [ SECURITY ] VIEW
! * <viewname> '('target-list ')'
* AS <query> [ WITH [ CASCADED | LOCAL ] CHECK OPTION ]
*
*****************************************************************************/
! ViewStmt: CREATE OptTemp OptSecurity VIEW qualified_name opt_column_list
AS SelectStmt opt_check_option
{
ViewStmt *n = makeNode(ViewStmt);
! n->view = $5;
n->view->istemp = $2;
! n->aliases = $6;
! n->query = $8;
n->replace = false;
+ n->security = $3;
$$ = (Node *) n;
}
! | CREATE OR REPLACE OptTemp OptSecurity
! VIEW qualified_name opt_column_list
AS SelectStmt opt_check_option
{
ViewStmt *n = makeNode(ViewStmt);
! n->view = $7;
n->view->istemp = $4;
! n->aliases = $8;
! n->query = $10;
n->replace = true;
+ n->security = $5;
$$ = (Node *) n;
}
;
+ OptSecurity: SECURITY { $$ = TRUE; }
+ | /* EMPTY */ { $$ = FALSE; }
+ ;
+
opt_check_option:
WITH CHECK OPTION
{
*** a/src/backend/rewrite/rewriteHandler.c
--- b/src/backend/rewrite/rewriteHandler.c
***************
*** 1132,1137 **** matchLocks(CmdType event,
--- 1132,1251 ----
return matching_locks;
}
+ /*
+ * MarkFuncExprDepth
+ *
+ * It marks the depth field of each expression node that may eventually
+ * call functions. On the checks of WHERE clause, every qualifiers are
+ * sorted by cost estimation, then a qualifier with lower-cost shall
+ * be evaluated ealier than expensive one.
+ * However, this logic can cause a problem when simple subqueries are
+ * pull-up to join clauses. If a view is used to row-level security
+ * purpose, its security policy function should be evaluated earlier
+ * than any functions supplied by users, because they may have side-
+ * effects that leaks given arguments somewhere, alhough it is contents
+ * of tuples to be filtered out.
+ * So, we need to ensure a function originated from inner subquery
+ * being evaluated earlier than functions originated from outer
+ * subqueries.
+ */
+ static bool
+ MarkFuncExprDepthWalker(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))
+ {
+ query_tree_walker((Query *)node,
+ MarkFuncExprDepthWalker,
+ (void *)(depth + 1), 0);
+ return false;
+ }
+ return expression_tree_walker(node, MarkFuncExprDepthWalker, context);
+ }
+
+ static void
+ MarkFuncExprDepth(List *query_list)
+ {
+ ListCell *l;
+
+ foreach (l, query_list)
+ {
+ Node *node = lfirst(l);
+
+ Assert(IsA(node, Query));
+
+ query_tree_walker((Query *)node,
+ MarkFuncExprDepthWalker,
+ (void *) 1, 0);
+ }
+ }
/*
* ApplyRetrieveRule - expand an ON SELECT rule
***************
*** 1188,1193 **** ApplyRetrieveRule(Query *parsetree,
--- 1302,1311 ----
rte->subquery = rule_action;
rte->inh = false; /* must not be set for a subquery */
+ if (relation->rd_options &&
+ ((StdRdOptions *)relation->rd_options)->security_view)
+ rte->security_view = true;
+
/*
* We move the view's permission check data down to its rangetable. The
* checks will actually be done against the OLD entry therein.
***************
*** 1961,1965 **** QueryRewrite(Query *parsetree)
--- 2079,2088 ----
if (!foundOriginalQuery && lastInstead != NULL)
lastInstead->canSetTag = true;
+ /*
+ * Mark the ->depth fields of expression nodes
+ */
+ MarkFuncExprDepth(results);
+
return results;
}
*** a/src/backend/utils/cache/lsyscache.c
--- b/src/backend/utils/cache/lsyscache.c
***************
*** 1275,1280 **** get_func_namespace(Oid funcid)
--- 1275,1299 ----
}
/*
+ * get_func_lang
+ * Given procedure id, return the function's language
+ */
+ Oid
+ get_func_lang(Oid funcid)
+ {
+ HeapTuple tp;
+ Oid result;
+
+ tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "cache lookup failed for function %u", funcid);
+
+ result = ((Form_pg_proc) GETSTRUCT(tp))->prolang;
+ ReleaseSysCache(tp);
+ return result;
+ }
+
+ /*
* get_func_rettype
* Given procedure id, return the function's result type.
*/
*** a/src/backend/utils/cache/relcache.c
--- b/src/backend/utils/cache/relcache.c
***************
*** 385,390 **** RelationParseRelOptions(Relation relation, HeapTuple tuple)
--- 385,391 ----
case RELKIND_RELATION:
case RELKIND_TOASTVALUE:
case RELKIND_INDEX:
+ case RELKIND_VIEW:
break;
default:
return;
*** a/src/include/access/reloptions.h
--- b/src/include/access/reloptions.h
***************
*** 42,49 **** typedef enum relopt_kind
RELOPT_KIND_GIST = (1 << 5),
RELOPT_KIND_ATTRIBUTE = (1 << 6),
RELOPT_KIND_TABLESPACE = (1 << 7),
/* if you add a new kind, make sure you update "last_default" too */
! RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_TABLESPACE,
/* some compilers treat enums as signed ints, so we can't use 1 << 31 */
RELOPT_KIND_MAX = (1 << 30)
} relopt_kind;
--- 42,50 ----
RELOPT_KIND_GIST = (1 << 5),
RELOPT_KIND_ATTRIBUTE = (1 << 6),
RELOPT_KIND_TABLESPACE = (1 << 7),
+ RELOPT_KIND_VIEW = (1 << 8),
/* if you add a new kind, make sure you update "last_default" too */
! RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_VIEW,
/* some compilers treat enums as signed ints, so we can't use 1 << 31 */
RELOPT_KIND_MAX = (1 << 30)
} relopt_kind;
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 684,689 **** typedef struct RangeTblEntry
--- 684,691 ----
*/
Query *subquery; /* the sub-query */
+ bool security_view; /* Is the sub-query come from security view? */
+
/*
* Fields valid for a join RTE (else NULL/zero):
*
***************
*** 2186,2191 **** typedef struct ViewStmt
--- 2188,2194 ----
List *aliases; /* target column names */
Node *query; /* the SELECT query */
bool replace; /* replace an existing view? */
+ bool security; /* the view for row-level security? */
} ViewStmt;
/* ----------------------
*** a/src/include/nodes/primnodes.h
--- b/src/include/nodes/primnodes.h
***************
*** 325,330 **** typedef struct FuncExpr
--- 325,331 ----
CoercionForm funcformat; /* how to display this function call */
List *args; /* arguments to the function */
int location; /* token location, or -1 if unknown */
+ int depth; /* depth of clause in the original query */
} FuncExpr;
/*
***************
*** 368,373 **** typedef struct OpExpr
--- 369,375 ----
bool opretset; /* true if operator returns set */
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;
/*
***************
*** 400,405 **** typedef struct ScalarArrayOpExpr
--- 402,408 ----
bool useOr; /* true for ANY, false for ALL */
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;
/*
***************
*** 659,664 **** typedef struct CoerceViaIO
--- 662,668 ----
/* output typmod is not stored, but is presumed -1 */
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;
/* ----------------
***************
*** 683,688 **** typedef struct ArrayCoerceExpr
--- 687,693 ----
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;
/* ----------------
***************
*** 852,857 **** typedef struct RowCompareExpr
--- 857,863 ----
List *opfamilies; /* OID list of containing operator families */
List *largs; /* the left-hand input arguments */
List *rargs; /* the right-hand input arguments */
+ int depth; /* depth of clause in the original query */
} RowCompareExpr;
/*
***************
*** 1208,1213 **** typedef struct FromExpr
--- 1214,1220 ----
NodeTag type;
List *fromlist; /* List of join subtrees */
Node *quals; /* qualifiers on join, if any */
+ bool security_view; /* It came from security views */
} FromExpr;
#endif /* PRIMNODES_H */
*** a/src/include/nodes/relation.h
--- b/src/include/nodes/relation.h
***************
*** 44,49 **** typedef struct QualCost
--- 44,50 ----
{
Cost startup; /* one-time cost */
Cost per_tuple; /* per-evaluation cost */
+ int depth; /* depth of qual in the original query */
} QualCost;
*** a/src/include/optimizer/clauses.h
--- b/src/include/optimizer/clauses.h
***************
*** 67,72 **** extern bool contain_subplans(Node *clause);
--- 67,73 ----
extern bool contain_mutable_functions(Node *clause);
extern bool contain_volatile_functions(Node *clause);
extern bool contain_nonstrict_functions(Node *clause);
+ extern bool contain_leakable_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);
*** a/src/include/utils/lsyscache.h
--- b/src/include/utils/lsyscache.h
***************
*** 77,82 **** extern RegProcedure get_oprrest(Oid opno);
--- 77,83 ----
extern RegProcedure get_oprjoin(Oid opno);
extern char *get_func_name(Oid funcid);
extern Oid get_func_namespace(Oid funcid);
+ extern Oid get_func_lang(Oid funcid);
extern Oid get_func_rettype(Oid funcid);
extern int get_func_nargs(Oid funcid);
extern Oid get_func_signature(Oid funcid, Oid **argtypes, int *nargs);
*** a/src/include/utils/rel.h
--- b/src/include/utils/rel.h
***************
*** 242,247 **** typedef struct StdRdOptions
--- 242,248 ----
int32 vl_len_; /* varlena header (do not touch directly!) */
int fillfactor; /* page fill factor in percent (0..100) */
AutoVacOpts autovacuum; /* autovacuum-related options */
+ bool security_view; /* prevent full optimization due to RLS */
} StdRdOptions;
#define HEAP_MIN_FILLFACTOR 10