diff options
Diffstat (limited to 'src/backend/optimizer/path')
-rw-r--r-- | src/backend/optimizer/path/allpaths.c | 166 | ||||
-rw-r--r-- | src/backend/optimizer/path/equivclass.c | 61 | ||||
-rw-r--r-- | src/backend/optimizer/path/indxpath.c | 26 | ||||
-rw-r--r-- | src/backend/optimizer/path/tidpath.c | 25 |
4 files changed, 190 insertions, 88 deletions
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 46d7d064d41..da68d0d61fc 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -896,9 +896,11 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *childRTE; RelOptInfo *childrel; List *childquals; - Node *childqual; + Index cq_min_security; + bool have_const_false_cq; ListCell *parentvars; ListCell *childvars; + ListCell *lc; /* append_rel_list contains all append rels; ignore others */ if (appinfo->parent_relid != parentRTindex) @@ -921,34 +923,113 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, * constraint exclusion; so do that first and then check to see if we * can disregard this child. * - * As of 8.4, the child rel's targetlist might contain non-Var - * expressions, which means that substitution into the quals could - * produce opportunities for const-simplification, and perhaps even - * pseudoconstant quals. To deal with this, we strip the RestrictInfo - * nodes, do the substitution, do const-simplification, and then - * reconstitute the RestrictInfo layer. + * The child rel's targetlist might contain non-Var expressions, which + * means that substitution into the quals could produce opportunities + * for const-simplification, and perhaps even pseudoconstant quals. + * Therefore, transform each RestrictInfo separately to see if it + * reduces to a constant or pseudoconstant. (We must process them + * separately to keep track of the security level of each qual.) + */ + childquals = NIL; + cq_min_security = UINT_MAX; + have_const_false_cq = false; + foreach(lc, rel->baserestrictinfo) + { + RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); + Node *childqual; + bool pseudoconstant; + + Assert(IsA(rinfo, RestrictInfo)); + childqual = adjust_appendrel_attrs(root, + (Node *) rinfo->clause, + appinfo); + childqual = eval_const_expressions(root, childqual); + /* check for flat-out constant */ + if (childqual && IsA(childqual, Const)) + { + if (((Const *) childqual)->constisnull || + !DatumGetBool(((Const *) childqual)->constvalue)) + { + /* Restriction reduces to constant FALSE or NULL */ + have_const_false_cq = true; + break; + } + /* Restriction reduces to constant TRUE, so drop it */ + continue; + } + /* check for pseudoconstant (no Vars or volatile functions) */ + pseudoconstant = + !contain_vars_of_level(childqual, 0) && + !contain_volatile_functions(childqual); + if (pseudoconstant) + { + /* tell createplan.c to check for gating quals */ + root->hasPseudoConstantQuals = true; + } + /* reconstitute RestrictInfo with appropriate properties */ + childquals = lappend(childquals, + make_restrictinfo((Expr *) childqual, + rinfo->is_pushed_down, + rinfo->outerjoin_delayed, + pseudoconstant, + rinfo->security_level, + NULL, NULL, NULL)); + /* track minimum security level among child quals */ + cq_min_security = Min(cq_min_security, rinfo->security_level); + } + + /* + * In addition to the quals inherited from the parent, we might have + * securityQuals associated with this particular child node. + * (Currently this can only happen in appendrels originating from + * UNION ALL; inheritance child tables don't have their own + * securityQuals, see expand_inherited_rtentry().) Pull any such + * securityQuals up into the baserestrictinfo for the child. This is + * similar to process_security_barrier_quals() for the parent rel, + * except that we can't make any general deductions from such quals, + * since they don't hold for the whole appendrel. + */ + if (childRTE->securityQuals) + { + Index security_level = 0; + + foreach(lc, childRTE->securityQuals) + { + List *qualset = (List *) lfirst(lc); + ListCell *lc2; + + foreach(lc2, qualset) + { + Expr *qual = (Expr *) lfirst(lc2); + + /* not likely that we'd see constants here, so no check */ + childquals = lappend(childquals, + make_restrictinfo(qual, + true, false, false, + security_level, + NULL, NULL, NULL)); + cq_min_security = Min(cq_min_security, security_level); + } + security_level++; + } + Assert(security_level <= root->qual_security_level); + } + + /* + * OK, we've got all the baserestrictinfo quals for this child. */ - childquals = get_all_actual_clauses(rel->baserestrictinfo); - childquals = (List *) adjust_appendrel_attrs(root, - (Node *) childquals, - appinfo); - childqual = eval_const_expressions(root, (Node *) - make_ands_explicit(childquals)); - if (childqual && IsA(childqual, Const) && - (((Const *) childqual)->constisnull || - !DatumGetBool(((Const *) childqual)->constvalue))) + childrel->baserestrictinfo = childquals; + childrel->baserestrict_min_security = cq_min_security; + + if (have_const_false_cq) { /* - * Restriction reduces to constant FALSE or constant NULL after + * Some restriction clause reduced to constant FALSE or NULL after * substitution, so this child need not be scanned. */ set_dummy_rel_pathlist(childrel); continue; } - childquals = make_ands_implicit((Expr *) childqual); - childquals = make_restrictinfos_from_actual_clauses(root, - childquals); - childrel->baserestrictinfo = childquals; if (relation_excluded_by_constraints(root, childrel, childRTE)) { @@ -1712,6 +1793,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, } } rel->baserestrictinfo = upperrestrictlist; + /* We don't bother recomputing baserestrict_min_security */ } pfree(safetyInfo.unsafeColumns); @@ -2640,46 +2722,6 @@ subquery_push_qual(Query *subquery, RangeTblEntry *rte, Index rti, Node *qual) recurse_push_qual(subquery->setOperations, subquery, rte, rti, qual); } - else if (IsA(qual, CurrentOfExpr)) - { - /* - * This is possible when a WHERE CURRENT OF expression is applied to a - * table with row-level security. In that case, the subquery should - * contain precisely one rtable entry for the table, and we can safely - * push the expression down into the subquery. This will cause a TID - * scan subquery plan to be generated allowing the target relation to - * be updated. - * - * Someday we might also be able to use a WHERE CURRENT OF expression - * on a view, but currently the rewriter prevents that, so we should - * never see any other case here, but generate sane error messages in - * case it does somehow happen. - */ - if (subquery->rtable == NIL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("WHERE CURRENT OF is not supported on a view with no underlying relation"))); - - if (list_length(subquery->rtable) > 1) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("WHERE CURRENT OF is not supported on a view with more than one underlying relation"))); - - if (subquery->hasAggs || subquery->groupClause || subquery->groupingSets || subquery->havingQual) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("WHERE CURRENT OF is not supported on a view with grouping or aggregation"))); - - /* - * Adjust the CURRENT OF expression to refer to the underlying table - * in the subquery, and attach it to the subquery's WHERE clause. - */ - qual = copyObject(qual); - ((CurrentOfExpr *) qual)->cvarno = 1; - - subquery->jointree->quals = - make_and_qual(subquery->jointree->quals, qual); - } else { /* @@ -2708,7 +2750,7 @@ subquery_push_qual(Query *subquery, RangeTblEntry *rte, Index rti, Node *qual) make_and_qual(subquery->jointree->quals, qual); /* - * We need not change the subquery's hasAggs or hasSublinks flags, + * We need not change the subquery's hasAggs or hasSubLinks flags, * since we can't be pushing down any aggregates that weren't there * before, and we don't push down subselects at all. */ diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c index 6703dc384d6..a329dd1e10d 100644 --- a/src/backend/optimizer/path/equivclass.c +++ b/src/backend/optimizer/path/equivclass.c @@ -16,6 +16,8 @@ */ #include "postgres.h" +#include <limits.h> + #include "access/stratnum.h" #include "catalog/pg_type.h" #include "nodes/makefuncs.h" @@ -78,9 +80,16 @@ static bool reconsider_full_join_clause(PlannerInfo *root, * care to mark an EquivalenceClass if it came from any such clauses. Also, * we have to check that both sides are either pseudo-constants or strict * functions of Vars, else they might not both go to NULL above the outer - * join. (This is the reason why we need a failure return. It's more + * join. (This is the main reason why we need a failure return. It's more * convenient to check this case here than at the call sites...) * + * We also reject proposed equivalence clauses if they contain leaky functions + * and have security_level above zero. The EC evaluation rules require us to + * apply certain tests at certain joining levels, and we can't tolerate + * delaying any test on security_level grounds. By rejecting candidate clauses + * that might require security delays, we ensure it's safe to apply an EC + * clause as soon as it's supposed to be applied. + * * On success return, we have also initialized the clause's left_ec/right_ec * fields to point to the EquivalenceClass representing it. This saves lookup * effort later. @@ -120,6 +129,10 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo, Assert(restrictinfo->left_ec == NULL); Assert(restrictinfo->right_ec == NULL); + /* Reject if it is potentially postponable by security considerations */ + if (restrictinfo->security_level > 0 && !restrictinfo->leakproof) + return false; + /* Extract info from given clause */ Assert(is_opclause(clause)); opno = ((OpExpr *) clause)->opno; @@ -275,6 +288,10 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo, { ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo); ec1->ec_below_outer_join |= below_outer_join; + ec1->ec_min_security = Min(ec1->ec_min_security, + restrictinfo->security_level); + ec1->ec_max_security = Max(ec1->ec_max_security, + restrictinfo->security_level); /* mark the RI as associated with this eclass */ restrictinfo->left_ec = ec1; restrictinfo->right_ec = ec1; @@ -306,6 +323,10 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo, ec1->ec_has_const |= ec2->ec_has_const; /* can't need to set has_volatile */ ec1->ec_below_outer_join |= ec2->ec_below_outer_join; + ec1->ec_min_security = Min(ec1->ec_min_security, + ec2->ec_min_security); + ec1->ec_max_security = Max(ec1->ec_max_security, + ec2->ec_max_security); ec2->ec_merged = ec1; root->eq_classes = list_delete_ptr(root->eq_classes, ec2); /* just to avoid debugging confusion w/ dangling pointers: */ @@ -315,6 +336,10 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo, ec2->ec_relids = NULL; ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo); ec1->ec_below_outer_join |= below_outer_join; + ec1->ec_min_security = Min(ec1->ec_min_security, + restrictinfo->security_level); + ec1->ec_max_security = Max(ec1->ec_max_security, + restrictinfo->security_level); /* mark the RI as associated with this eclass */ restrictinfo->left_ec = ec1; restrictinfo->right_ec = ec1; @@ -329,6 +354,10 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo, false, item2_type); ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo); ec1->ec_below_outer_join |= below_outer_join; + ec1->ec_min_security = Min(ec1->ec_min_security, + restrictinfo->security_level); + ec1->ec_max_security = Max(ec1->ec_max_security, + restrictinfo->security_level); /* mark the RI as associated with this eclass */ restrictinfo->left_ec = ec1; restrictinfo->right_ec = ec1; @@ -343,6 +372,10 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo, false, item1_type); ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo); ec2->ec_below_outer_join |= below_outer_join; + ec2->ec_min_security = Min(ec2->ec_min_security, + restrictinfo->security_level); + ec2->ec_max_security = Max(ec2->ec_max_security, + restrictinfo->security_level); /* mark the RI as associated with this eclass */ restrictinfo->left_ec = ec2; restrictinfo->right_ec = ec2; @@ -366,6 +399,8 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo, ec->ec_below_outer_join = below_outer_join; ec->ec_broken = false; ec->ec_sortref = 0; + ec->ec_min_security = restrictinfo->security_level; + ec->ec_max_security = restrictinfo->security_level; ec->ec_merged = NULL; em1 = add_eq_member(ec, item1, item1_relids, item1_nullable_relids, false, item1_type); @@ -639,6 +674,8 @@ get_eclass_for_sort_expr(PlannerInfo *root, newec->ec_below_outer_join = false; newec->ec_broken = false; newec->ec_sortref = sortref; + newec->ec_min_security = UINT_MAX; + newec->ec_max_security = 0; newec->ec_merged = NULL; if (newec->ec_has_volatile && sortref == 0) /* should not happen */ @@ -834,6 +871,7 @@ generate_base_implied_equalities_const(PlannerInfo *root, bms_copy(ec->ec_relids), bms_union(cur_em->em_nullable_relids, const_em->em_nullable_relids), + ec->ec_min_security, ec->ec_below_outer_join, cur_em->em_is_const); } @@ -890,6 +928,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root, bms_copy(ec->ec_relids), bms_union(prev_em->em_nullable_relids, cur_em->em_nullable_relids), + ec->ec_min_security, ec->ec_below_outer_join, false); } @@ -1313,7 +1352,13 @@ select_equality_operator(EquivalenceClass *ec, Oid lefttype, Oid righttype) opno = get_opfamily_member(opfamily, lefttype, righttype, BTEqualStrategyNumber); - if (OidIsValid(opno)) + if (!OidIsValid(opno)) + continue; + /* If no barrier quals in query, don't worry about leaky operators */ + if (ec->ec_max_security == 0) + return opno; + /* Otherwise, insist that selected operators be leakproof */ + if (get_func_leakproof(get_opcode(opno))) return opno; } return InvalidOid; @@ -1380,7 +1425,8 @@ create_join_clause(PlannerInfo *root, bms_union(leftem->em_relids, rightem->em_relids), bms_union(leftem->em_nullable_relids, - rightem->em_nullable_relids)); + rightem->em_nullable_relids), + ec->ec_min_security); /* Mark the clause as redundant, or not */ rinfo->parent_ec = parent_ec; @@ -1691,7 +1737,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo, innervar, cur_em->em_expr, bms_copy(inner_relids), - bms_copy(inner_nullable_relids)); + bms_copy(inner_nullable_relids), + cur_ec->ec_min_security); if (process_equivalence(root, newrinfo, true)) match = true; } @@ -1833,7 +1880,8 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo) leftvar, cur_em->em_expr, bms_copy(left_relids), - bms_copy(left_nullable_relids)); + bms_copy(left_nullable_relids), + cur_ec->ec_min_security); if (process_equivalence(root, newrinfo, true)) matchleft = true; } @@ -1847,7 +1895,8 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo) rightvar, cur_em->em_expr, bms_copy(right_relids), - bms_copy(right_nullable_relids)); + bms_copy(right_nullable_relids), + cur_ec->ec_min_security); if (process_equivalence(root, newrinfo, true)) matchright = true; } diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 0a5c05033a0..52834689881 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -2143,6 +2143,23 @@ match_clause_to_index(IndexOptInfo *index, { int indexcol; + /* + * Never match pseudoconstants to indexes. (Normally a match could not + * happen anyway, since a pseudoconstant clause couldn't contain a Var, + * but what if someone builds an expression index on a constant? It's not + * totally unreasonable to do so with a partial index, either.) + */ + if (rinfo->pseudoconstant) + return; + + /* + * If clause can't be used as an indexqual because it must wait till after + * some lower-security-level restriction clause, reject it. + */ + if (!restriction_is_securely_promotable(rinfo, index->rel)) + return; + + /* OK, check each index column for a match */ for (indexcol = 0; indexcol < index->ncolumns; indexcol++) { if (match_clause_to_indexcol(index, @@ -2237,15 +2254,6 @@ match_clause_to_indexcol(IndexOptInfo *index, Oid expr_coll; bool plain_op; - /* - * Never match pseudoconstants to indexes. (Normally this could not - * happen anyway, since a pseudoconstant clause couldn't contain a Var, - * but what if someone builds an expression index on a constant? It's not - * totally unreasonable to do so with a partial index, either.) - */ - if (rinfo->pseudoconstant) - return false; - /* First check for boolean-index cases. */ if (IsBooleanOpfamily(opfamily)) { diff --git a/src/backend/optimizer/path/tidpath.c b/src/backend/optimizer/path/tidpath.c index 240ade6708b..a2fe661075f 100644 --- a/src/backend/optimizer/path/tidpath.c +++ b/src/backend/optimizer/path/tidpath.c @@ -43,12 +43,13 @@ #include "optimizer/clauses.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" +#include "optimizer/restrictinfo.h" static bool IsTidEqualClause(OpExpr *node, int varno); static bool IsTidEqualAnyClause(ScalarArrayOpExpr *node, int varno); static List *TidQualFromExpr(Node *expr, int varno); -static List *TidQualFromRestrictinfo(List *restrictinfo, int varno); +static List *TidQualFromBaseRestrictinfo(RelOptInfo *rel); /* @@ -216,24 +217,26 @@ TidQualFromExpr(Node *expr, int varno) } /* - * Extract a set of CTID conditions from the given restrictinfo list - * - * This is essentially identical to the AND case of TidQualFromExpr, - * except for the format of the input. + * Extract a set of CTID conditions from the rel's baserestrictinfo list */ static List * -TidQualFromRestrictinfo(List *restrictinfo, int varno) +TidQualFromBaseRestrictinfo(RelOptInfo *rel) { List *rlst = NIL; ListCell *l; - foreach(l, restrictinfo) + foreach(l, rel->baserestrictinfo) { RestrictInfo *rinfo = (RestrictInfo *) lfirst(l); - if (!IsA(rinfo, RestrictInfo)) - continue; /* probably should never happen */ - rlst = TidQualFromExpr((Node *) rinfo->clause, varno); + /* + * If clause must wait till after some lower-security-level + * restriction clause, reject it. + */ + if (!restriction_is_securely_promotable(rinfo, rel)) + continue; + + rlst = TidQualFromExpr((Node *) rinfo->clause, rel->relid); if (rlst) break; } @@ -259,7 +262,7 @@ create_tidscan_paths(PlannerInfo *root, RelOptInfo *rel) */ required_outer = rel->lateral_relids; - tidquals = TidQualFromRestrictinfo(rel->baserestrictinfo, rel->relid); + tidquals = TidQualFromBaseRestrictinfo(rel); if (tidquals) add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals, |