* statext_is_compatible_clause_internal
* Determines if the clause is compatible with MCV lists.
*
- * Does the heavy lifting of actually inspecting the clauses for
- * statext_is_compatible_clause. It needs to be split like this because
- * of recursion. The attnums bitmap is an input/output parameter collecting
- * attribute numbers from all compatible clauses (recursively).
+ * To be compatible, the given clause must be a combination of supported
+ * clauses built from Vars or sub-expressions (where a sub-expression is
+ * something that exactly matches an expression found in statistics objects).
+ * This function recursively examines the clause and extracts any
+ * sub-expressions that will need to be matched against statistics.
+ *
+ * Currently, we only support the following types of clauses:
+ *
+ * (a) OpExprs of the form (Var/Expr op Const), or (Const op Var/Expr), where
+ * the op is one of ("=", "<", ">", ">=", "<=")
+ *
+ * (b) (Var/Expr IS [NOT] NULL)
+ *
+ * (c) combinations using AND/OR/NOT
+ *
+ * (d) ScalarArrayOpExprs of the form (Var/Expr op ANY (array)) or (Var/Expr
+ * op ALL (array))
+ *
+ * In the future, the range of supported clauses may be expanded to more
+ * complex cases, for example (Var op Var).
+ *
+ * Arguments:
+ * clause: (sub)clause to be inspected (bare clause, not a RestrictInfo)
+ * relid: rel that all Vars in clause must belong to
+ * *attnums: input/output parameter collecting attribute numbers of all
+ * mentioned Vars. Note that we do not offset the attribute numbers,
+ * so we can't cope with system columns.
+ * *exprs: input/output parameter collecting primitive subclauses within
+ * the clause tree
+ *
+ * Returns false if there is something we definitively can't handle.
+ * On true return, we can proceed to match the *exprs against statistics.
*/
static bool
statext_is_compatible_clause_internal(PlannerInfo *root, Node *clause,
if (var->varlevelsup > 0)
return false;
- /* Also skip system attributes (we don't allow stats on those). */
+ /*
+ * Also reject system attributes and whole-row Vars (we don't allow
+ * stats on those).
+ */
if (!AttrNumberIsForUserDefinedAttr(var->varattno))
return false;
+ /* OK, record the attnum for later permissions checks. */
*attnums = bms_add_member(*attnums, var->varattno);
return true;
foreach(lc, expr->args)
{
/*
- * Had we found incompatible clause in the arguments, treat the
+ * If we find an incompatible clause in the arguments, treat the
* whole clause as incompatible.
*/
if (!statext_is_compatible_clause_internal(root,
* statext_is_compatible_clause
* Determines if the clause is compatible with MCV lists.
*
- * Currently, we only support the following types of clauses:
+ * See statext_is_compatible_clause_internal, above, for the basic rules.
+ * This layer deals with RestrictInfo superstructure and applies permissions
+ * checks to verify that it's okay to examine all mentioned Vars.
*
- * (a) OpExprs of the form (Var/Expr op Const), or (Const op Var/Expr), where
- * the op is one of ("=", "<", ">", ">=", "<=")
+ * Arguments:
+ * clause: clause to be inspected (in RestrictInfo form)
+ * relid: rel that all Vars in clause must belong to
+ * *attnums: input/output parameter collecting attribute numbers of all
+ * mentioned Vars. Note that we do not offset the attribute numbers,
+ * so we can't cope with system columns.
+ * *exprs: input/output parameter collecting primitive subclauses within
+ * the clause tree
*
- * (b) (Var/Expr IS [NOT] NULL)
- *
- * (c) combinations using AND/OR/NOT
- *
- * (d) ScalarArrayOpExprs of the form (Var/Expr op ANY (array)) or (Var/Expr
- * op ALL (array))
- *
- * In the future, the range of supported clauses may be expanded to more
- * complex cases, for example (Var op Var).
+ * Returns false if there is something we definitively can't handle.
+ * On true return, we can proceed to match the *exprs against statistics.
*/
static bool
statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid,
Bitmapset **attnums, List **exprs)
{
RangeTblEntry *rte = root->simple_rte_array[relid];
- RestrictInfo *rinfo = (RestrictInfo *) clause;
+ RestrictInfo *rinfo;
int clause_relid;
Oid userid;
}
/* Otherwise it must be a RestrictInfo. */
- if (!IsA(rinfo, RestrictInfo))
+ if (!IsA(clause, RestrictInfo))
return false;
+ rinfo = (RestrictInfo *) clause;
/* Pseudoconstants are not really interesting here. */
if (rinfo->pseudoconstant)
*/
userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
+ /* Table-level SELECT privilege is sufficient for all columns */
if (pg_class_aclcheck(rte->relid, userid, ACL_SELECT) != ACLCHECK_OK)
{
Bitmapset *clause_attnums = NULL;
+ int attnum = -1;
- /* Don't have table privilege, must check individual columns */
- if (*exprs != NIL)
+ /*
+ * We have to check per-column privileges. *attnums has the attnums
+ * for individual Vars we saw, but there may also be Vars within
+ * subexpressions in *exprs. We can use pull_varattnos() to extract
+ * those, but there's an impedance mismatch: attnums returned by
+ * pull_varattnos() are offset by FirstLowInvalidHeapAttributeNumber,
+ * while attnums within *attnums aren't. Convert *attnums to the
+ * offset style so we can combine the results.
+ */
+ while ((attnum = bms_next_member(*attnums, attnum)) >= 0)
{
- pull_varattnos((Node *) exprs, relid, &clause_attnums);
- clause_attnums = bms_add_members(clause_attnums, *attnums);
+ clause_attnums =
+ bms_add_member(clause_attnums,
+ attnum - FirstLowInvalidHeapAttributeNumber);
}
- else
- clause_attnums = *attnums;
- if (bms_is_member(InvalidAttrNumber, clause_attnums))
- {
- /* Have a whole-row reference, must have access to all columns */
- if (pg_attribute_aclcheck_all(rte->relid, userid, ACL_SELECT,
- ACLMASK_ALL) != ACLCHECK_OK)
- return false;
- }
- else
+ /* Now merge attnums from *exprs into clause_attnums */
+ if (*exprs != NIL)
+ pull_varattnos((Node *) *exprs, relid, &clause_attnums);
+
+ attnum = -1;
+ while ((attnum = bms_next_member(clause_attnums, attnum)) >= 0)
{
- /* Check the columns referenced by the clause */
- int attnum = -1;
+ /* Undo the offset */
+ AttrNumber attno = attnum + FirstLowInvalidHeapAttributeNumber;
- while ((attnum = bms_next_member(clause_attnums, attnum)) >= 0)
+ if (attno == InvalidAttrNumber)
+ {
+ /* Whole-row reference, so must have access to all columns */
+ if (pg_attribute_aclcheck_all(rte->relid, userid, ACL_SELECT,
+ ACLMASK_ALL) != ACLCHECK_OK)
+ return false;
+ }
+ else
{
- if (pg_attribute_aclcheck(rte->relid, attnum, userid,
+ if (pg_attribute_aclcheck(rte->relid, attno, userid,
ACL_SELECT) != ACLCHECK_OK)
return false;
}