mirror of
https://github.com/postgres/postgres.git
synced 2025-07-08 11:42:09 +03:00
Teach predtest.c about CHECK clauses to fix partitioning bugs.
In a CHECK clause, a null result means true, whereas in a WHERE clause it means false. predtest.c provided different functions depending on which set of semantics applied to the predicate being proved, but had no option to control what a null meant in the clauses provided as axioms. Add one. Use that in the partitioning code when figuring out whether the validation scan on a new partition can be skipped. Rip out the old logic that attempted (not very successfully) to compensate for the absence of the necessary support in predtest.c. Ashutosh Bapat and Robert Haas, reviewed by Amit Langote and incorporating feedback from Tom Lane. Discussion: http://postgr.es/m/CAFjFpReT_kq_uwU_B8aWDxR7jNGE=P0iELycdq5oupi=xSQTOw@mail.gmail.com
This commit is contained in:
@ -1210,10 +1210,10 @@ build_paths_for_OR(PlannerInfo *root, RelOptInfo *rel,
|
||||
all_clauses = list_concat(list_copy(clauses),
|
||||
other_clauses);
|
||||
|
||||
if (!predicate_implied_by(index->indpred, all_clauses))
|
||||
if (!predicate_implied_by(index->indpred, all_clauses, false))
|
||||
continue; /* can't use it at all */
|
||||
|
||||
if (!predicate_implied_by(index->indpred, other_clauses))
|
||||
if (!predicate_implied_by(index->indpred, other_clauses, false))
|
||||
useful_predicate = true;
|
||||
}
|
||||
}
|
||||
@ -1519,7 +1519,7 @@ choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, List *paths)
|
||||
{
|
||||
Node *np = (Node *) lfirst(l);
|
||||
|
||||
if (predicate_implied_by(list_make1(np), qualsofar))
|
||||
if (predicate_implied_by(list_make1(np), qualsofar, false))
|
||||
{
|
||||
redundant = true;
|
||||
break; /* out of inner foreach loop */
|
||||
@ -2871,7 +2871,8 @@ check_index_predicates(PlannerInfo *root, RelOptInfo *rel)
|
||||
continue; /* ignore non-partial indexes here */
|
||||
|
||||
if (!index->predOK) /* don't repeat work if already proven OK */
|
||||
index->predOK = predicate_implied_by(index->indpred, clauselist);
|
||||
index->predOK = predicate_implied_by(index->indpred, clauselist,
|
||||
false);
|
||||
|
||||
/* If rel is an update target, leave indrestrictinfo as set above */
|
||||
if (is_target_rel)
|
||||
@ -2886,7 +2887,7 @@ check_index_predicates(PlannerInfo *root, RelOptInfo *rel)
|
||||
/* predicate_implied_by() assumes first arg is immutable */
|
||||
if (contain_mutable_functions((Node *) rinfo->clause) ||
|
||||
!predicate_implied_by(list_make1(rinfo->clause),
|
||||
index->indpred))
|
||||
index->indpred, false))
|
||||
index->indrestrictinfo = lappend(index->indrestrictinfo, rinfo);
|
||||
}
|
||||
}
|
||||
|
@ -2576,7 +2576,7 @@ create_indexscan_plan(PlannerInfo *root,
|
||||
if (is_redundant_derived_clause(rinfo, indexquals))
|
||||
continue; /* derived from same EquivalenceClass */
|
||||
if (!contain_mutable_functions((Node *) rinfo->clause) &&
|
||||
predicate_implied_by(list_make1(rinfo->clause), indexquals))
|
||||
predicate_implied_by(list_make1(rinfo->clause), indexquals, false))
|
||||
continue; /* provably implied by indexquals */
|
||||
qpqual = lappend(qpqual, rinfo);
|
||||
}
|
||||
@ -2737,7 +2737,7 @@ create_bitmap_scan_plan(PlannerInfo *root,
|
||||
if (rinfo->parent_ec && list_member_ptr(indexECs, rinfo->parent_ec))
|
||||
continue; /* derived from same EquivalenceClass */
|
||||
if (!contain_mutable_functions(clause) &&
|
||||
predicate_implied_by(list_make1(clause), indexquals))
|
||||
predicate_implied_by(list_make1(clause), indexquals, false))
|
||||
continue; /* provably implied by indexquals */
|
||||
qpqual = lappend(qpqual, rinfo);
|
||||
}
|
||||
@ -2968,7 +2968,8 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
|
||||
* the conditions that got pushed into the bitmapqual. Avoid
|
||||
* generating redundant conditions.
|
||||
*/
|
||||
if (!predicate_implied_by(list_make1(pred), ipath->indexclauses))
|
||||
if (!predicate_implied_by(list_make1(pred), ipath->indexclauses,
|
||||
false))
|
||||
{
|
||||
*qual = lappend(*qual, pred);
|
||||
*indexqual = lappend(*indexqual, pred);
|
||||
|
@ -776,7 +776,7 @@ infer_arbiter_indexes(PlannerInfo *root)
|
||||
*/
|
||||
predExprs = RelationGetIndexPredicate(idxRel);
|
||||
|
||||
if (!predicate_implied_by(predExprs, (List *) onconflict->arbiterWhere))
|
||||
if (!predicate_implied_by(predExprs, (List *) onconflict->arbiterWhere, false))
|
||||
goto next;
|
||||
|
||||
results = lappend_oid(results, idxForm->indexrelid);
|
||||
@ -1399,7 +1399,7 @@ relation_excluded_by_constraints(PlannerInfo *root,
|
||||
safe_restrictions = lappend(safe_restrictions, rinfo->clause);
|
||||
}
|
||||
|
||||
if (predicate_refuted_by(safe_restrictions, safe_restrictions))
|
||||
if (predicate_refuted_by(safe_restrictions, safe_restrictions, false))
|
||||
return true;
|
||||
|
||||
/* Only plain relations have constraints */
|
||||
@ -1438,7 +1438,7 @@ relation_excluded_by_constraints(PlannerInfo *root,
|
||||
* have volatile and nonvolatile subclauses, and it's OK to make
|
||||
* deductions with the nonvolatile parts.
|
||||
*/
|
||||
if (predicate_refuted_by(safe_constraints, rel->baserestrictinfo))
|
||||
if (predicate_refuted_by(safe_constraints, rel->baserestrictinfo, false))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
@ -77,8 +77,10 @@ typedef struct PredIterInfoData
|
||||
} 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);
|
||||
@ -90,8 +92,10 @@ static void arrayconst_cleanup_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);
|
||||
@ -107,8 +111,11 @@ static void InvalidateOprProofCacheCallBack(Datum arg, int cacheid, uint32 hashv
|
||||
|
||||
/*
|
||||
* 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
|
||||
@ -125,14 +132,15 @@ static void InvalidateOprProofCacheCallBack(Datum arg, int cacheid, uint32 hashv
|
||||
* 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 */
|
||||
|
||||
/*
|
||||
@ -145,19 +153,22 @@ predicate_implied_by(List *predicate_list, List *restrictinfo_list)
|
||||
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.
|
||||
@ -183,14 +194,15 @@ predicate_implied_by(List *predicate_list, List *restrictinfo_list)
|
||||
* 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 */
|
||||
|
||||
/*
|
||||
@ -203,13 +215,13 @@ predicate_refuted_by(List *predicate_list, List *restrictinfo_list)
|
||||
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);
|
||||
}
|
||||
|
||||
/*----------
|
||||
@ -248,7 +260,8 @@ predicate_refuted_by(List *predicate_list, List *restrictinfo_list)
|
||||
*----------
|
||||
*/
|
||||
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;
|
||||
@ -275,7 +288,8 @@ predicate_implied_by_recurse(Node *clause, Node *predicate)
|
||||
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;
|
||||
@ -294,7 +308,8 @@ predicate_implied_by_recurse(Node *clause, Node *predicate)
|
||||
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;
|
||||
@ -311,7 +326,8 @@ predicate_implied_by_recurse(Node *clause, Node *predicate)
|
||||
*/
|
||||
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;
|
||||
@ -328,7 +344,8 @@ predicate_implied_by_recurse(Node *clause, Node *predicate)
|
||||
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;
|
||||
@ -355,7 +372,8 @@ predicate_implied_by_recurse(Node *clause, Node *predicate)
|
||||
|
||||
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;
|
||||
@ -382,7 +400,8 @@ predicate_implied_by_recurse(Node *clause, Node *predicate)
|
||||
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;
|
||||
@ -404,7 +423,8 @@ predicate_implied_by_recurse(Node *clause, Node *predicate)
|
||||
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;
|
||||
@ -421,7 +441,8 @@ predicate_implied_by_recurse(Node *clause, Node *predicate)
|
||||
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;
|
||||
@ -437,7 +458,8 @@ predicate_implied_by_recurse(Node *clause, Node *predicate)
|
||||
*/
|
||||
return
|
||||
predicate_implied_by_simple_clause((Expr *) predicate,
|
||||
clause);
|
||||
clause,
|
||||
clause_is_check);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -478,7 +500,8 @@ predicate_implied_by_recurse(Node *clause, Node *predicate)
|
||||
*----------
|
||||
*/
|
||||
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;
|
||||
@ -508,7 +531,8 @@ predicate_refuted_by_recurse(Node *clause, Node *predicate)
|
||||
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;
|
||||
@ -525,7 +549,8 @@ predicate_refuted_by_recurse(Node *clause, Node *predicate)
|
||||
*/
|
||||
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;
|
||||
@ -542,7 +567,8 @@ predicate_refuted_by_recurse(Node *clause, Node *predicate)
|
||||
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;
|
||||
@ -558,7 +584,8 @@ predicate_refuted_by_recurse(Node *clause, Node *predicate)
|
||||
*/
|
||||
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;
|
||||
|
||||
/*
|
||||
@ -567,7 +594,8 @@ predicate_refuted_by_recurse(Node *clause, Node *predicate)
|
||||
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;
|
||||
@ -589,7 +617,8 @@ predicate_refuted_by_recurse(Node *clause, Node *predicate)
|
||||
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;
|
||||
@ -611,7 +640,8 @@ predicate_refuted_by_recurse(Node *clause, Node *predicate)
|
||||
|
||||
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;
|
||||
@ -634,7 +664,8 @@ predicate_refuted_by_recurse(Node *clause, Node *predicate)
|
||||
*/
|
||||
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;
|
||||
|
||||
/*
|
||||
@ -643,7 +674,8 @@ predicate_refuted_by_recurse(Node *clause, Node *predicate)
|
||||
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;
|
||||
@ -679,7 +711,8 @@ predicate_refuted_by_recurse(Node *clause, Node *predicate)
|
||||
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;
|
||||
@ -696,7 +729,8 @@ predicate_refuted_by_recurse(Node *clause, Node *predicate)
|
||||
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;
|
||||
@ -712,7 +746,8 @@ predicate_refuted_by_recurse(Node *clause, Node *predicate)
|
||||
*/
|
||||
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;
|
||||
|
||||
/*
|
||||
@ -720,7 +755,8 @@ predicate_refuted_by_recurse(Node *clause, Node *predicate)
|
||||
*/
|
||||
return
|
||||
predicate_refuted_by_simple_clause((Expr *) predicate,
|
||||
clause);
|
||||
clause,
|
||||
clause_is_check);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1022,14 +1058,15 @@ arrayexpr_cleanup_fn(PredIterInfo info)
|
||||
* 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
|
||||
@ -1037,7 +1074,8 @@ arrayexpr_cleanup_fn(PredIterInfo info)
|
||||
*----------
|
||||
*/
|
||||
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();
|
||||
@ -1053,7 +1091,7 @@ predicate_implied_by_simple_clause(Expr *predicate, Node *clause)
|
||||
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) &&
|
||||
@ -1098,7 +1136,8 @@ predicate_implied_by_simple_clause(Expr *predicate, Node *clause)
|
||||
*----------
|
||||
*/
|
||||
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();
|
||||
@ -1114,6 +1153,9 @@ predicate_refuted_by_simple_clause(Expr *predicate, Node *clause)
|
||||
{
|
||||
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;
|
||||
|
Reference in New Issue
Block a user