static ObjectAddress
ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
{
- PartitionKey key = RelationGetPartitionKey(rel);
Relation attachRel,
catalog;
List *childrels;
{
int num_check = attachRel_constr->num_check;
int i;
- Bitmapset *not_null_attrs = NULL;
- List *part_constr;
- ListCell *lc;
- bool partition_accepts_null = true;
- int partnatts;
if (attachRel_constr->has_not_null)
{
ntest->argisrow = false;
ntest->location = -1;
existConstraint = lappend(existConstraint, ntest);
- not_null_attrs = bms_add_member(not_null_attrs, i);
}
}
}
existConstraint = list_make1(make_ands_explicit(existConstraint));
/* And away we go ... */
- if (predicate_implied_by(partConstraint, existConstraint))
+ if (predicate_implied_by(partConstraint, existConstraint, true))
skip_validate = true;
-
- /*
- * We choose to err on the safer side, i.e., give up on skipping the
- * validation scan, if the partition key column doesn't have the NOT
- * NULL constraint and the table is to become a list partition that
- * does not accept nulls. In this case, the partition predicate
- * (partConstraint) does include an 'key IS NOT NULL' expression,
- * however, because of the way predicate_implied_by_simple_clause() is
- * designed to handle IS NOT NULL predicates in the absence of a IS
- * NOT NULL clause, we cannot rely on just the above proof.
- *
- * That is not an issue in case of a range partition, because if there
- * were no NOT NULL constraint defined on the key columns, an error
- * would be thrown before we get here anyway. That is not true,
- * however, if any of the partition keys is an expression, which is
- * handled below.
- */
- part_constr = linitial(partConstraint);
- part_constr = make_ands_implicit((Expr *) part_constr);
-
- /*
- * part_constr contains an IS NOT NULL expression, if this is a list
- * partition that does not accept nulls (in fact, also if this is a
- * range partition and some partition key is an expression, but we
- * never skip validation in that case anyway; see below)
- */
- foreach(lc, part_constr)
- {
- Node *expr = lfirst(lc);
-
- if (IsA(expr, NullTest) &&
- ((NullTest *) expr)->nulltesttype == IS_NOT_NULL)
- {
- partition_accepts_null = false;
- break;
- }
- }
-
- partnatts = get_partition_natts(key);
- for (i = 0; i < partnatts; i++)
- {
- AttrNumber partattno;
-
- partattno = get_partition_col_attnum(key, i);
-
- /* If partition key is an expression, must not skip validation */
- if (!partition_accepts_null &&
- (partattno == 0 ||
- !bms_is_member(partattno, not_null_attrs)))
- skip_validate = false;
- }
}
/* It's safe to skip the validation scan after all */
} while (0)
-static bool predicate_implied_by_recurse(Node *clause, Node *predicate);
-static bool predicate_refuted_by_recurse(Node *clause, Node *predicate);
+static bool predicate_implied_by_recurse(Node *clause, Node *predicate,
+ bool clause_is_check);
+static bool predicate_refuted_by_recurse(Node *clause, Node *predicate,
+ bool clause_is_check);
static PredClass predicate_classify(Node *clause, PredIterInfo info);
static void list_startup_fn(Node *clause, PredIterInfo info);
static Node *list_next_fn(PredIterInfo info);
static void arrayexpr_startup_fn(Node *clause, PredIterInfo info);
static Node *arrayexpr_next_fn(PredIterInfo info);
static void arrayexpr_cleanup_fn(PredIterInfo info);
-static bool predicate_implied_by_simple_clause(Expr *predicate, Node *clause);
-static bool predicate_refuted_by_simple_clause(Expr *predicate, Node *clause);
+static bool predicate_implied_by_simple_clause(Expr *predicate, Node *clause,
+ bool clause_is_check);
+static bool predicate_refuted_by_simple_clause(Expr *predicate, Node *clause,
+ bool clause_is_check);
static Node *extract_not_arg(Node *clause);
static Node *extract_strong_not_arg(Node *clause);
static bool list_member_strip(List *list, Expr *datum);
/*
* predicate_implied_by
- * Recursively checks whether the clauses in restrictinfo_list imply
- * that the given predicate is true.
+ * Recursively checks whether the clauses in clause_list imply that the
+ * given predicate is true. If clause_is_check is true, assume that the
+ * clauses in clause_list are CHECK constraints (where null is
+ * effectively true) rather than WHERE clauses (where null is effectively
+ * false).
*
* The top-level List structure of each list corresponds to an AND list.
* We assume that eval_const_expressions() has been applied and so there
* the plan and the time we execute the plan.
*/
bool
-predicate_implied_by(List *predicate_list, List *restrictinfo_list)
+predicate_implied_by(List *predicate_list, List *clause_list,
+ bool clause_is_check)
{
Node *p,
*r;
if (predicate_list == NIL)
return true; /* no predicate: implication is vacuous */
- if (restrictinfo_list == NIL)
+ if (clause_list == NIL)
return false; /* no restriction: implication must fail */
/*
p = (Node *) linitial(predicate_list);
else
p = (Node *) predicate_list;
- if (list_length(restrictinfo_list) == 1)
- r = (Node *) linitial(restrictinfo_list);
+ if (list_length(clause_list) == 1)
+ r = (Node *) linitial(clause_list);
else
- r = (Node *) restrictinfo_list;
+ r = (Node *) clause_list;
/* And away we go ... */
- return predicate_implied_by_recurse(r, p);
+ return predicate_implied_by_recurse(r, p, clause_is_check);
}
/*
* predicate_refuted_by
- * Recursively checks whether the clauses in restrictinfo_list refute
- * the given predicate (that is, prove it false).
+ * Recursively checks whether the clauses in clause_list refute the given
+ * predicate (that is, prove it false). If clause_is_check is true, assume
+ * that the clauses in clause_list are CHECK constraints (where null is
+ * effectively true) rather than WHERE clauses (where null is effectively
+ * false).
*
* This is NOT the same as !(predicate_implied_by), though it is similar
* in the technique and structure of the code.
* time we make the plan and the time we execute the plan.
*/
bool
-predicate_refuted_by(List *predicate_list, List *restrictinfo_list)
+predicate_refuted_by(List *predicate_list, List *clause_list,
+ bool clause_is_check)
{
Node *p,
*r;
if (predicate_list == NIL)
return false; /* no predicate: no refutation is possible */
- if (restrictinfo_list == NIL)
+ if (clause_list == NIL)
return false; /* no restriction: refutation must fail */
/*
p = (Node *) linitial(predicate_list);
else
p = (Node *) predicate_list;
- if (list_length(restrictinfo_list) == 1)
- r = (Node *) linitial(restrictinfo_list);
+ if (list_length(clause_list) == 1)
+ r = (Node *) linitial(clause_list);
else
- r = (Node *) restrictinfo_list;
+ r = (Node *) clause_list;
/* And away we go ... */
- return predicate_refuted_by_recurse(r, p);
+ return predicate_refuted_by_recurse(r, p, clause_is_check);
}
/*----------
*----------
*/
static bool
-predicate_implied_by_recurse(Node *clause, Node *predicate)
+predicate_implied_by_recurse(Node *clause, Node *predicate,
+ bool clause_is_check)
{
PredIterInfoData clause_info;
PredIterInfoData pred_info;
result = true;
iterate_begin(pitem, predicate, pred_info)
{
- if (!predicate_implied_by_recurse(clause, pitem))
+ if (!predicate_implied_by_recurse(clause, pitem,
+ clause_is_check))
{
result = false;
break;
result = false;
iterate_begin(pitem, predicate, pred_info)
{
- if (predicate_implied_by_recurse(clause, pitem))
+ if (predicate_implied_by_recurse(clause, pitem,
+ clause_is_check))
{
result = true;
break;
*/
iterate_begin(citem, clause, clause_info)
{
- if (predicate_implied_by_recurse(citem, predicate))
+ if (predicate_implied_by_recurse(citem, predicate,
+ clause_is_check))
{
result = true;
break;
result = false;
iterate_begin(citem, clause, clause_info)
{
- if (predicate_implied_by_recurse(citem, predicate))
+ if (predicate_implied_by_recurse(citem, predicate,
+ clause_is_check))
{
result = true;
break;
iterate_begin(pitem, predicate, pred_info)
{
- if (predicate_implied_by_recurse(citem, pitem))
+ if (predicate_implied_by_recurse(citem, pitem,
+ clause_is_check))
{
presult = true;
break;
result = true;
iterate_begin(citem, clause, clause_info)
{
- if (!predicate_implied_by_recurse(citem, predicate))
+ if (!predicate_implied_by_recurse(citem, predicate,
+ clause_is_check))
{
result = false;
break;
result = true;
iterate_begin(pitem, predicate, pred_info)
{
- if (!predicate_implied_by_recurse(clause, pitem))
+ if (!predicate_implied_by_recurse(clause, pitem,
+ clause_is_check))
{
result = false;
break;
result = false;
iterate_begin(pitem, predicate, pred_info)
{
- if (predicate_implied_by_recurse(clause, pitem))
+ if (predicate_implied_by_recurse(clause, pitem,
+ clause_is_check))
{
result = true;
break;
*/
return
predicate_implied_by_simple_clause((Expr *) predicate,
- clause);
+ clause,
+ clause_is_check);
}
break;
}
*----------
*/
static bool
-predicate_refuted_by_recurse(Node *clause, Node *predicate)
+predicate_refuted_by_recurse(Node *clause, Node *predicate,
+ bool clause_is_check)
{
PredIterInfoData clause_info;
PredIterInfoData pred_info;
result = false;
iterate_begin(pitem, predicate, pred_info)
{
- if (predicate_refuted_by_recurse(clause, pitem))
+ if (predicate_refuted_by_recurse(clause, pitem,
+ clause_is_check))
{
result = true;
break;
*/
iterate_begin(citem, clause, clause_info)
{
- if (predicate_refuted_by_recurse(citem, predicate))
+ if (predicate_refuted_by_recurse(citem, predicate,
+ clause_is_check))
{
result = true;
break;
result = true;
iterate_begin(pitem, predicate, pred_info)
{
- if (!predicate_refuted_by_recurse(clause, pitem))
+ if (!predicate_refuted_by_recurse(clause, pitem,
+ clause_is_check))
{
result = false;
break;
*/
not_arg = extract_not_arg(predicate);
if (not_arg &&
- predicate_implied_by_recurse(clause, not_arg))
+ predicate_implied_by_recurse(clause, not_arg,
+ clause_is_check))
return true;
/*
result = false;
iterate_begin(citem, clause, clause_info)
{
- if (predicate_refuted_by_recurse(citem, predicate))
+ if (predicate_refuted_by_recurse(citem, predicate,
+ clause_is_check))
{
result = true;
break;
result = true;
iterate_begin(pitem, predicate, pred_info)
{
- if (!predicate_refuted_by_recurse(clause, pitem))
+ if (!predicate_refuted_by_recurse(clause, pitem,
+ clause_is_check))
{
result = false;
break;
iterate_begin(pitem, predicate, pred_info)
{
- if (predicate_refuted_by_recurse(citem, pitem))
+ if (predicate_refuted_by_recurse(citem, pitem,
+ clause_is_check))
{
presult = true;
break;
*/
not_arg = extract_not_arg(predicate);
if (not_arg &&
- predicate_implied_by_recurse(clause, not_arg))
+ predicate_implied_by_recurse(clause, not_arg,
+ clause_is_check))
return true;
/*
result = true;
iterate_begin(citem, clause, clause_info)
{
- if (!predicate_refuted_by_recurse(citem, predicate))
+ if (!predicate_refuted_by_recurse(citem, predicate,
+ clause_is_check))
{
result = false;
break;
result = false;
iterate_begin(pitem, predicate, pred_info)
{
- if (predicate_refuted_by_recurse(clause, pitem))
+ if (predicate_refuted_by_recurse(clause, pitem,
+ clause_is_check))
{
result = true;
break;
result = true;
iterate_begin(pitem, predicate, pred_info)
{
- if (!predicate_refuted_by_recurse(clause, pitem))
+ if (!predicate_refuted_by_recurse(clause, pitem,
+ clause_is_check))
{
result = false;
break;
*/
not_arg = extract_not_arg(predicate);
if (not_arg &&
- predicate_implied_by_recurse(clause, not_arg))
+ predicate_implied_by_recurse(clause, not_arg,
+ clause_is_check))
return true;
/*
*/
return
predicate_refuted_by_simple_clause((Expr *) predicate,
- clause);
+ clause,
+ clause_is_check);
}
break;
}
* functions in the expression are immutable, ie dependent only on their input
* arguments --- but this was checked for the predicate by the caller.)
*
- * When the predicate is of the form "foo IS NOT NULL", we can conclude that
- * the predicate is implied if the clause is a strict operator or function
- * that has "foo" as an input. In this case the clause must yield NULL when
- * "foo" is NULL, which we can take as equivalent to FALSE because we know
- * we are within an AND/OR subtree of a WHERE clause. (Again, "foo" is
- * already known immutable, so the clause will certainly always fail.)
- * Also, if the clause is just "foo" (meaning it's a boolean variable),
- * the predicate is implied since the clause can't be true if "foo" is NULL.
+ * When clause_is_check is false, we know we are within an AND/OR
+ * subtree of a WHERE clause. So, if the predicate is of the form "foo IS
+ * NOT NULL", we can conclude that the predicate is implied if the clause is
+ * a strict operator or function that has "foo" as an input. In this case
+ * the clause must yield NULL when "foo" is NULL, which we can take as
+ * equivalent to FALSE given the context. (Again, "foo" is already known
+ * immutable, so the clause will certainly always fail.) Also, if the clause
+ * is just "foo" (meaning it's a boolean variable), the predicate is implied
+ * since the clause can't be true if "foo" is NULL.
*
* Finally, if both clauses are binary operator expressions, we may be able
* to prove something using the system's knowledge about operators; those
*----------
*/
static bool
-predicate_implied_by_simple_clause(Expr *predicate, Node *clause)
+predicate_implied_by_simple_clause(Expr *predicate, Node *clause,
+ bool clause_is_check)
{
/* Allow interrupting long proof attempts */
CHECK_FOR_INTERRUPTS();
Expr *nonnullarg = ((NullTest *) predicate)->arg;
/* row IS NOT NULL does not act in the simple way we have in mind */
- if (!((NullTest *) predicate)->argisrow)
+ if (!((NullTest *) predicate)->argisrow && !clause_is_check)
{
if (is_opclause(clause) &&
list_member_strip(((OpExpr *) clause)->args, nonnullarg) &&
*----------
*/
static bool
-predicate_refuted_by_simple_clause(Expr *predicate, Node *clause)
+predicate_refuted_by_simple_clause(Expr *predicate, Node *clause,
+ bool clause_is_check)
{
/* Allow interrupting long proof attempts */
CHECK_FOR_INTERRUPTS();
{
Expr *isnullarg = ((NullTest *) predicate)->arg;
+ if (clause_is_check)
+ return false;
+
/* row IS NULL does not act in the simple way we have in mind */
if (((NullTest *) predicate)->argisrow)
return false;