#include "postgres.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "optimizer/clauses.h"
#include "optimizer/planmain.h"
#include "optimizer/predtest.h"
#include "utils/array.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
Go to the source code of this file.
#define BTEQ BTEqualStrategyNumber |
Definition at line 1284 of file predtest.c.
#define BTGE BTGreaterEqualStrategyNumber |
Definition at line 1285 of file predtest.c.
#define BTGT BTGreaterStrategyNumber |
Definition at line 1286 of file predtest.c.
#define BTLE BTLessEqualStrategyNumber |
Definition at line 1283 of file predtest.c.
#define BTLT BTLessStrategyNumber |
Definition at line 1282 of file predtest.c.
#define BTNE ROWCOMPARE_NE |
Definition at line 1287 of file predtest.c.
Referenced by get_btree_test_op().
#define iterate_begin | ( | item, | ||
clause, | ||||
info | ||||
) |
do { \ Node *item; \ (info).startup_fn((clause), &(info)); \ while ((item = (info).next_fn(&(info))) != NULL)
Definition at line 69 of file predtest.c.
Referenced by predicate_implied_by_recurse(), and predicate_refuted_by_recurse().
#define iterate_end | ( | info | ) |
(info).cleanup_fn(&(info)); \
} while (0)
Definition at line 75 of file predtest.c.
Referenced by predicate_implied_by_recurse(), and predicate_refuted_by_recurse().
#define MAX_SAOP_ARRAY_SIZE 100 |
Definition at line 38 of file predtest.c.
Referenced by predicate_classify().
typedef struct OprProofCacheEntry OprProofCacheEntry |
typedef struct OprProofCacheKey OprProofCacheKey |
typedef struct PredIterInfoData* PredIterInfo |
Definition at line 55 of file predtest.c.
typedef struct PredIterInfoData PredIterInfoData |
enum PredClass |
Definition at line 48 of file predtest.c.
{ CLASS_ATOM, /* expression that's not AND or OR */ CLASS_AND, /* expression with AND semantics */ CLASS_OR /* expression with OR semantics */ } PredClass;
static void arrayconst_cleanup_fn | ( | PredIterInfo | info | ) | [static] |
Definition at line 938 of file predtest.c.
References OpExpr::args, ArrayConstIterState::elem_nulls, ArrayConstIterState::elem_values, list_free(), ArrayConstIterState::opexpr, pfree(), and PredIterInfoData::state.
{ ArrayConstIterState *state = (ArrayConstIterState *) info->state; pfree(state->elem_values); pfree(state->elem_nulls); list_free(state->opexpr.args); pfree(state); }
static Node * arrayconst_next_fn | ( | PredIterInfo | info | ) | [static] |
Definition at line 925 of file predtest.c.
References ArrayConstIterState::constexpr, Const::constisnull, Const::constvalue, ArrayConstIterState::elem_nulls, ArrayConstIterState::elem_values, ArrayConstIterState::next_elem, ArrayConstIterState::num_elems, ArrayConstIterState::opexpr, and PredIterInfoData::state.
{ ArrayConstIterState *state = (ArrayConstIterState *) info->state; if (state->next_elem >= state->num_elems) return NULL; state->constexpr.constvalue = state->elem_values[state->next_elem]; state->constexpr.constisnull = state->elem_nulls[state->next_elem]; state->next_elem++; return (Node *) &(state->opexpr); }
static void arrayconst_startup_fn | ( | Node * | clause, | |
PredIterInfo | info | |||
) | [static] |
Definition at line 876 of file predtest.c.
References OpExpr::args, ScalarArrayOpExpr::args, ARR_ELEMTYPE, Const::constbyval, Const::constcollid, ArrayConstIterState::constexpr, Const::constlen, Const::consttype, Const::consttypmod, Const::constvalue, DatumGetArrayTypeP, deconstruct_array(), ArrayConstIterState::elem_nulls, ArrayConstIterState::elem_values, get_typlenbyvalalign(), ScalarArrayOpExpr::inputcollid, OpExpr::inputcollid, list_copy(), lsecond, ArrayConstIterState::next_elem, ArrayConstIterState::num_elems, OpExpr::opcollid, ArrayConstIterState::opexpr, ScalarArrayOpExpr::opfuncid, OpExpr::opfuncid, ScalarArrayOpExpr::opno, OpExpr::opno, OpExpr::opresulttype, OpExpr::opretset, palloc(), PredIterInfoData::state, Expr::type, Const::xpr, and OpExpr::xpr.
{ ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause; ArrayConstIterState *state; Const *arrayconst; ArrayType *arrayval; int16 elmlen; bool elmbyval; char elmalign; /* Create working state struct */ state = (ArrayConstIterState *) palloc(sizeof(ArrayConstIterState)); info->state = (void *) state; /* Deconstruct the array literal */ arrayconst = (Const *) lsecond(saop->args); arrayval = DatumGetArrayTypeP(arrayconst->constvalue); get_typlenbyvalalign(ARR_ELEMTYPE(arrayval), &elmlen, &elmbyval, &elmalign); deconstruct_array(arrayval, ARR_ELEMTYPE(arrayval), elmlen, elmbyval, elmalign, &state->elem_values, &state->elem_nulls, &state->num_elems); /* Set up a dummy OpExpr to return as the per-item node */ state->opexpr.xpr.type = T_OpExpr; state->opexpr.opno = saop->opno; state->opexpr.opfuncid = saop->opfuncid; state->opexpr.opresulttype = BOOLOID; state->opexpr.opretset = false; state->opexpr.opcollid = InvalidOid; state->opexpr.inputcollid = saop->inputcollid; state->opexpr.args = list_copy(saop->args); /* Set up a dummy Const node to hold the per-element values */ state->constexpr.xpr.type = T_Const; state->constexpr.consttype = ARR_ELEMTYPE(arrayval); state->constexpr.consttypmod = -1; state->constexpr.constcollid = arrayconst->constcollid; state->constexpr.constlen = elmlen; state->constexpr.constbyval = elmbyval; lsecond(state->opexpr.args) = &state->constexpr; /* Initialize iteration state */ state->next_elem = 0; }
static void arrayexpr_cleanup_fn | ( | PredIterInfo | info | ) | [static] |
Definition at line 997 of file predtest.c.
References OpExpr::args, list_free(), ArrayExprIterState::opexpr, pfree(), and PredIterInfoData::state.
{ ArrayExprIterState *state = (ArrayExprIterState *) info->state; list_free(state->opexpr.args); pfree(state); }
static Node * arrayexpr_next_fn | ( | PredIterInfo | info | ) | [static] |
Definition at line 985 of file predtest.c.
References OpExpr::args, lfirst, lnext, lsecond, ArrayExprIterState::next, NULL, ArrayExprIterState::opexpr, and PredIterInfoData::state.
static void arrayexpr_startup_fn | ( | Node * | clause, | |
PredIterInfo | info | |||
) | [static] |
Definition at line 959 of file predtest.c.
References ScalarArrayOpExpr::args, OpExpr::args, ArrayExpr::elements, ScalarArrayOpExpr::inputcollid, OpExpr::inputcollid, list_copy(), list_head(), lsecond, ArrayExprIterState::next, OpExpr::opcollid, ArrayExprIterState::opexpr, ScalarArrayOpExpr::opfuncid, OpExpr::opfuncid, ScalarArrayOpExpr::opno, OpExpr::opno, OpExpr::opresulttype, OpExpr::opretset, palloc(), PredIterInfoData::state, Expr::type, and OpExpr::xpr.
{ ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause; ArrayExprIterState *state; ArrayExpr *arrayexpr; /* Create working state struct */ state = (ArrayExprIterState *) palloc(sizeof(ArrayExprIterState)); info->state = (void *) state; /* Set up a dummy OpExpr to return as the per-item node */ state->opexpr.xpr.type = T_OpExpr; state->opexpr.opno = saop->opno; state->opexpr.opfuncid = saop->opfuncid; state->opexpr.opresulttype = BOOLOID; state->opexpr.opretset = false; state->opexpr.opcollid = InvalidOid; state->opexpr.inputcollid = saop->inputcollid; state->opexpr.args = list_copy(saop->args); /* Initialize iteration variable to first member of ArrayExpr */ arrayexpr = (ArrayExpr *) lsecond(saop->args); state->next = list_head(arrayexpr->elements); }
static void boolexpr_startup_fn | ( | Node * | clause, | |
PredIterInfo | info | |||
) | [static] |
Definition at line 856 of file predtest.c.
References list_head(), and PredIterInfoData::state.
Definition at line 1341 of file predtest.c.
References BOOLOID, Const::constisnull, CreateExecutorState(), DatumGetBool, DEBUG2, elog, equal(), EState::es_query_cxt, ExecEvalExprSwitchContext(), ExecInitExpr(), fix_opfuncids(), FreeExecutorState(), get_btree_test_op(), get_commutator(), get_leftop(), get_rightop(), GetPerTupleExprContext, InvalidOid, is_opclause, IsA, make_opclause(), MemoryContextSwitchTo(), NULL, and OidIsValid.
Referenced by predicate_implied_by_simple_clause(), and predicate_refuted_by_simple_clause().
{ Node *leftop, *rightop; Node *pred_var, *clause_var; Const *pred_const, *clause_const; bool pred_var_on_left, clause_var_on_left; Oid pred_collation, clause_collation; Oid pred_op, clause_op, test_op; Expr *test_expr; ExprState *test_exprstate; Datum test_result; bool isNull; EState *estate; MemoryContext oldcontext; /* * Both expressions must be binary opclauses with a Const on one side, and * identical subexpressions on the other sides. Note we don't have to * think about binary relabeling of the Const node, since that would have * been folded right into the Const. * * If either Const is null, we also fail right away; this assumes that the * test operator will always be strict. */ if (!is_opclause(predicate)) return false; leftop = get_leftop(predicate); rightop = get_rightop(predicate); if (rightop == NULL) return false; /* not a binary opclause */ if (IsA(rightop, Const)) { pred_var = leftop; pred_const = (Const *) rightop; pred_var_on_left = true; } else if (IsA(leftop, Const)) { pred_var = rightop; pred_const = (Const *) leftop; pred_var_on_left = false; } else return false; /* no Const to be found */ if (pred_const->constisnull) return false; if (!is_opclause(clause)) return false; leftop = get_leftop((Expr *) clause); rightop = get_rightop((Expr *) clause); if (rightop == NULL) return false; /* not a binary opclause */ if (IsA(rightop, Const)) { clause_var = leftop; clause_const = (Const *) rightop; clause_var_on_left = true; } else if (IsA(leftop, Const)) { clause_var = rightop; clause_const = (Const *) leftop; clause_var_on_left = false; } else return false; /* no Const to be found */ if (clause_const->constisnull) return false; /* * Check for matching subexpressions on the non-Const sides. We used to * only allow a simple Var, but it's about as easy to allow any * expression. Remember we already know that the pred expression does not * contain any non-immutable functions, so identical expressions should * yield identical results. */ if (!equal(pred_var, clause_var)) return false; /* * They'd better have the same collation, too. */ pred_collation = ((OpExpr *) predicate)->inputcollid; clause_collation = ((OpExpr *) clause)->inputcollid; if (pred_collation != clause_collation) return false; /* * Okay, get the operators in the two clauses we're comparing. Commute * them if needed so that we can assume the variables are on the left. */ pred_op = ((OpExpr *) predicate)->opno; if (!pred_var_on_left) { pred_op = get_commutator(pred_op); if (!OidIsValid(pred_op)) return false; } clause_op = ((OpExpr *) clause)->opno; if (!clause_var_on_left) { clause_op = get_commutator(clause_op); if (!OidIsValid(clause_op)) return false; } /* * Lookup the comparison operator using the system catalogs and the * operator implication tables. */ test_op = get_btree_test_op(pred_op, clause_op, refute_it); if (!OidIsValid(test_op)) { /* couldn't find a suitable comparison operator */ return false; } /* * Evaluate the test. For this we need an EState. */ estate = CreateExecutorState(); /* We can use the estate's working context to avoid memory leaks. */ oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); /* Build expression tree */ test_expr = make_opclause(test_op, BOOLOID, false, (Expr *) pred_const, (Expr *) clause_const, InvalidOid, pred_collation); /* Fill in opfuncids */ fix_opfuncids((Node *) test_expr); /* Prepare it for execution */ test_exprstate = ExecInitExpr(test_expr, NULL); /* And execute it. */ test_result = ExecEvalExprSwitchContext(test_exprstate, GetPerTupleExprContext(estate), &isNull, NULL); /* Get back to outer memory context */ MemoryContextSwitchTo(oldcontext); /* Release all the junk we just created */ FreeExecutorState(estate); if (isNull) { /* Treat a null result as non-proof ... but it's a tad fishy ... */ elog(DEBUG2, "null predicate test result"); return false; } return DatumGetBool(test_result); }
Definition at line 1161 of file predtest.c.
References BooleanTest::arg, BoolExpr::args, BoolExpr::boolop, BooleanTest::booltesttype, IS_FALSE, IS_NOT_TRUE, IS_UNKNOWN, IsA, linitial, NOT_EXPR, and NULL.
Referenced by predicate_refuted_by_recurse().
{ if (clause == NULL) return NULL; if (IsA(clause, BoolExpr)) { BoolExpr *bexpr = (BoolExpr *) clause; if (bexpr->boolop == NOT_EXPR) return (Node *) linitial(bexpr->args); } else if (IsA(clause, BooleanTest)) { BooleanTest *btest = (BooleanTest *) clause; if (btest->booltesttype == IS_NOT_TRUE || btest->booltesttype == IS_FALSE || btest->booltesttype == IS_UNKNOWN) return (Node *) btest->arg; } return NULL; }
Definition at line 1189 of file predtest.c.
References BooleanTest::arg, BoolExpr::args, BoolExpr::boolop, BooleanTest::booltesttype, IS_FALSE, IsA, linitial, NOT_EXPR, and NULL.
Referenced by predicate_refuted_by_recurse().
{ if (clause == NULL) return NULL; if (IsA(clause, BoolExpr)) { BoolExpr *bexpr = (BoolExpr *) clause; if (bexpr->boolop == NOT_EXPR) return (Node *) linitial(bexpr->args); } else if (IsA(clause, BooleanTest)) { BooleanTest *btest = (BooleanTest *) clause; if (btest->booltesttype == IS_FALSE) return (Node *) btest->arg; } return NULL; }
Definition at line 1553 of file predtest.c.
References AMOPOPID, Assert, BT_implic_table, BT_refute_table, BTEqualStrategyNumber, BTNE, CacheRegisterSyscacheCallback(), OprProofCacheKey::clause_op, HASHCTL::entrysize, get_negator(), get_op_btree_interpretation(), get_opfamily_member(), HASHCTL::hash, hash_create(), HASH_ELEM, HASH_FUNCTION, hash_search(), OprProofCacheEntry::have_implic, OprProofCacheEntry::have_refute, OprProofCacheEntry::implic_test_op, InvalidateOprProofCacheCallBack(), HASHCTL::keysize, lfirst, list_free_deep(), MemSet, NULL, OidIsValid, op_volatile(), OpBtreeInterpretation::opfamily_id, OpBtreeInterpretation::oplefttype, OpBtreeInterpretation::oprighttype, OprProofCacheKey::pred_op, PROVOLATILE_IMMUTABLE, OprProofCacheEntry::refute_test_op, and OpBtreeInterpretation::strategy.
Referenced by btree_predicate_proof().
{ OprProofCacheKey key; OprProofCacheEntry *cache_entry; bool cfound; Oid test_op = InvalidOid; bool found = false; List *pred_op_infos, *clause_op_infos; ListCell *lcp, *lcc; /* * Find or make a cache entry for this pair of operators. */ if (OprProofCacheHash == NULL) { /* First time through: initialize the hash table */ HASHCTL ctl; MemSet(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(OprProofCacheKey); ctl.entrysize = sizeof(OprProofCacheEntry); ctl.hash = tag_hash; OprProofCacheHash = hash_create("Btree proof lookup cache", 256, &ctl, HASH_ELEM | HASH_FUNCTION); /* Arrange to flush cache on pg_amop changes */ CacheRegisterSyscacheCallback(AMOPOPID, InvalidateOprProofCacheCallBack, (Datum) 0); } key.pred_op = pred_op; key.clause_op = clause_op; cache_entry = (OprProofCacheEntry *) hash_search(OprProofCacheHash, (void *) &key, HASH_ENTER, &cfound); if (!cfound) { /* new cache entry, set it invalid */ cache_entry->have_implic = false; cache_entry->have_refute = false; } else { /* pre-existing cache entry, see if we know the answer */ if (refute_it) { if (cache_entry->have_refute) return cache_entry->refute_test_op; } else { if (cache_entry->have_implic) return cache_entry->implic_test_op; } } /* * Try to find a btree opfamily containing the given operators. * * We must find a btree opfamily that contains both operators, else the * implication can't be determined. Also, the opfamily must contain a * suitable test operator taking the operators' righthand datatypes. * * If there are multiple matching opfamilies, assume we can use any one to * determine the logical relationship of the two operators and the correct * corresponding test operator. This should work for any logically * consistent opfamilies. */ clause_op_infos = get_op_btree_interpretation(clause_op); if (clause_op_infos) pred_op_infos = get_op_btree_interpretation(pred_op); else /* no point in looking */ pred_op_infos = NIL; foreach(lcp, pred_op_infos) { OpBtreeInterpretation *pred_op_info = lfirst(lcp); Oid opfamily_id = pred_op_info->opfamily_id; foreach(lcc, clause_op_infos) { OpBtreeInterpretation *clause_op_info = lfirst(lcc); StrategyNumber pred_strategy, clause_strategy, test_strategy; /* Must find them in same opfamily */ if (opfamily_id != clause_op_info->opfamily_id) continue; /* Lefttypes should match */ Assert(clause_op_info->oplefttype == pred_op_info->oplefttype); pred_strategy = pred_op_info->strategy; clause_strategy = clause_op_info->strategy; /* * Look up the "test" strategy number in the implication table */ if (refute_it) test_strategy = BT_refute_table[clause_strategy - 1][pred_strategy - 1]; else test_strategy = BT_implic_table[clause_strategy - 1][pred_strategy - 1]; if (test_strategy == 0) { /* Can't determine implication using this interpretation */ continue; } /* * See if opfamily has an operator for the test strategy and the * datatypes. */ if (test_strategy == BTNE) { test_op = get_opfamily_member(opfamily_id, pred_op_info->oprighttype, clause_op_info->oprighttype, BTEqualStrategyNumber); if (OidIsValid(test_op)) test_op = get_negator(test_op); } else { test_op = get_opfamily_member(opfamily_id, pred_op_info->oprighttype, clause_op_info->oprighttype, test_strategy); } if (!OidIsValid(test_op)) continue; /* * Last check: test_op must be immutable. * * Note that we require only the test_op to be immutable, not the * original clause_op. (pred_op is assumed to have been checked * immutable by the caller.) Essentially we are assuming that the * opfamily is consistent even if it contains operators that are * merely stable. */ if (op_volatile(test_op) == PROVOLATILE_IMMUTABLE) { found = true; break; } } if (found) break; } list_free_deep(pred_op_infos); list_free_deep(clause_op_infos); if (!found) { /* couldn't find a suitable comparison operator */ test_op = InvalidOid; } /* Cache the result, whether positive or negative */ if (refute_it) { cache_entry->refute_test_op = test_op; cache_entry->have_refute = true; } else { cache_entry->implic_test_op = test_op; cache_entry->have_implic = true; } return test_op; }
Definition at line 1738 of file predtest.c.
References Assert, hash_seq_init(), hash_seq_search(), OprProofCacheEntry::have_implic, OprProofCacheEntry::have_refute, and NULL.
Referenced by get_btree_test_op().
{ HASH_SEQ_STATUS status; OprProofCacheEntry *hentry; Assert(OprProofCacheHash != NULL); /* Currently we just reset all entries; hard to be smarter ... */ hash_seq_init(&status, OprProofCacheHash); while ((hentry = (OprProofCacheEntry *) hash_seq_search(&status)) != NULL) { hentry->have_implic = false; hentry->have_refute = false; } }
static void list_cleanup_fn | ( | PredIterInfo | info | ) | [static] |
Definition at line 846 of file predtest.c.
{
/* Nothing to clean up */
}
Definition at line 1219 of file predtest.c.
References arg, equal(), IsA, and lfirst.
Referenced by predicate_implied_by_simple_clause(), and predicate_refuted_by_simple_clause().
{ ListCell *cell; if (datum && IsA(datum, RelabelType)) datum = ((RelabelType *) datum)->arg; foreach(cell, list) { Expr *elem = (Expr *) lfirst(cell); if (elem && IsA(elem, RelabelType)) elem = ((RelabelType *) elem)->arg; if (equal(elem, datum)) return true; } return false; }
static Node * list_next_fn | ( | PredIterInfo | info | ) | [static] |
static void list_startup_fn | ( | Node * | clause, | |
PredIterInfo | info | |||
) | [static] |
Definition at line 827 of file predtest.c.
References list_head(), and PredIterInfoData::state.
static PredClass predicate_classify | ( | Node * | clause, | |
PredIterInfo | info | |||
) | [static] |
Definition at line 745 of file predtest.c.
References and_clause(), ScalarArrayOpExpr::args, ARR_DIMS, ARR_NDIM, ArrayGetNItems(), Assert, CLASS_OR, PredIterInfoData::cleanup_fn, DatumGetArrayTypeP, IsA, list_length(), lsecond, MAX_SAOP_ARRAY_SIZE, PredIterInfoData::next_fn, NULL, or_clause(), PredIterInfoData::startup_fn, and ScalarArrayOpExpr::useOr.
Referenced by predicate_implied_by_recurse(), and predicate_refuted_by_recurse().
{ /* Caller should not pass us NULL, nor a RestrictInfo clause */ Assert(clause != NULL); Assert(!IsA(clause, RestrictInfo)); /* * If we see a List, assume it's an implicit-AND list; this is the correct * semantics for lists of RestrictInfo nodes. */ if (IsA(clause, List)) { info->startup_fn = list_startup_fn; info->next_fn = list_next_fn; info->cleanup_fn = list_cleanup_fn; return CLASS_AND; } /* Handle normal AND and OR boolean clauses */ if (and_clause(clause)) { info->startup_fn = boolexpr_startup_fn; info->next_fn = list_next_fn; info->cleanup_fn = list_cleanup_fn; return CLASS_AND; } if (or_clause(clause)) { info->startup_fn = boolexpr_startup_fn; info->next_fn = list_next_fn; info->cleanup_fn = list_cleanup_fn; return CLASS_OR; } /* Handle ScalarArrayOpExpr */ if (IsA(clause, ScalarArrayOpExpr)) { ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause; Node *arraynode = (Node *) lsecond(saop->args); /* * We can break this down into an AND or OR structure, but only if we * know how to iterate through expressions for the array's elements. * We can do that if the array operand is a non-null constant or a * simple ArrayExpr. */ if (arraynode && IsA(arraynode, Const) && !((Const *) arraynode)->constisnull) { ArrayType *arrayval; int nelems; arrayval = DatumGetArrayTypeP(((Const *) arraynode)->constvalue); nelems = ArrayGetNItems(ARR_NDIM(arrayval), ARR_DIMS(arrayval)); if (nelems <= MAX_SAOP_ARRAY_SIZE) { info->startup_fn = arrayconst_startup_fn; info->next_fn = arrayconst_next_fn; info->cleanup_fn = arrayconst_cleanup_fn; return saop->useOr ? CLASS_OR : CLASS_AND; } } else if (arraynode && IsA(arraynode, ArrayExpr) && !((ArrayExpr *) arraynode)->multidims && list_length(((ArrayExpr *) arraynode)->elements) <= MAX_SAOP_ARRAY_SIZE) { info->startup_fn = arrayexpr_startup_fn; info->next_fn = arrayexpr_next_fn; info->cleanup_fn = arrayexpr_cleanup_fn; return saop->useOr ? CLASS_OR : CLASS_AND; } } /* None of the above, so it's an atom */ return CLASS_ATOM; }
Definition at line 124 of file predtest.c.
References linitial, list_length(), NIL, and predicate_implied_by_recurse().
Referenced by add_predicate_to_quals(), build_paths_for_OR(), check_partial_indexes(), choose_bitmap_and(), create_bitmap_scan_plan(), create_bitmap_subplan(), create_indexscan_plan(), gincostestimate(), and make_restrictinfo_from_bitmapqual().
{ Node *p, *r; if (predicate_list == NIL) return true; /* no predicate: implication is vacuous */ if (restrictinfo_list == NIL) return false; /* no restriction: implication must fail */ /* * If either input is a single-element list, replace it with its lone * member; this avoids one useless level of AND-recursion. We only need * to worry about this at top level, since eval_const_expressions should * have gotten rid of any trivial ANDs or ORs below that. */ if (list_length(predicate_list) == 1) p = (Node *) linitial(predicate_list); else p = (Node *) predicate_list; if (list_length(restrictinfo_list) == 1) r = (Node *) linitial(restrictinfo_list); else r = (Node *) restrictinfo_list; /* And away we go ... */ return predicate_implied_by_recurse(r, p); }
Definition at line 247 of file predtest.c.
References Assert, CLASS_AND, CLASS_ATOM, CLASS_OR, elog, ERROR, IsA, iterate_begin, iterate_end, NULL, predicate_classify(), and predicate_implied_by_simple_clause().
Referenced by predicate_implied_by(), and predicate_refuted_by_recurse().
{ PredIterInfoData clause_info; PredIterInfoData pred_info; PredClass pclass; bool result; /* skip through RestrictInfo */ Assert(clause != NULL); if (IsA(clause, RestrictInfo)) clause = (Node *) ((RestrictInfo *) clause)->clause; pclass = predicate_classify(predicate, &pred_info); switch (predicate_classify(clause, &clause_info)) { case CLASS_AND: switch (pclass) { case CLASS_AND: /* * AND-clause => AND-clause if A implies each of B's items */ result = true; iterate_begin(pitem, predicate, pred_info) { if (!predicate_implied_by_recurse(clause, pitem)) { result = false; break; } } iterate_end(pred_info); return result; case CLASS_OR: /* * AND-clause => OR-clause if A implies any of B's items * * Needed to handle (x AND y) => ((x AND y) OR z) */ result = false; iterate_begin(pitem, predicate, pred_info) { if (predicate_implied_by_recurse(clause, pitem)) { result = true; break; } } iterate_end(pred_info); if (result) return result; /* * Also check if any of A's items implies B * * Needed to handle ((x OR y) AND z) => (x OR y) */ iterate_begin(citem, clause, clause_info) { if (predicate_implied_by_recurse(citem, predicate)) { result = true; break; } } iterate_end(clause_info); return result; case CLASS_ATOM: /* * AND-clause => atom if any of A's items implies B */ result = false; iterate_begin(citem, clause, clause_info) { if (predicate_implied_by_recurse(citem, predicate)) { result = true; break; } } iterate_end(clause_info); return result; } break; case CLASS_OR: switch (pclass) { case CLASS_OR: /* * OR-clause => OR-clause if each of A's items implies any * of B's items. Messy but can't do it any more simply. */ result = true; iterate_begin(citem, clause, clause_info) { bool presult = false; iterate_begin(pitem, predicate, pred_info) { if (predicate_implied_by_recurse(citem, pitem)) { presult = true; break; } } iterate_end(pred_info); if (!presult) { result = false; /* doesn't imply any of B's */ break; } } iterate_end(clause_info); return result; case CLASS_AND: case CLASS_ATOM: /* * OR-clause => AND-clause if each of A's items implies B * * OR-clause => atom if each of A's items implies B */ result = true; iterate_begin(citem, clause, clause_info) { if (!predicate_implied_by_recurse(citem, predicate)) { result = false; break; } } iterate_end(clause_info); return result; } break; case CLASS_ATOM: switch (pclass) { case CLASS_AND: /* * atom => AND-clause if A implies each of B's items */ result = true; iterate_begin(pitem, predicate, pred_info) { if (!predicate_implied_by_recurse(clause, pitem)) { result = false; break; } } iterate_end(pred_info); return result; case CLASS_OR: /* * atom => OR-clause if A implies any of B's items */ result = false; iterate_begin(pitem, predicate, pred_info) { if (predicate_implied_by_recurse(clause, pitem)) { result = true; break; } } iterate_end(pred_info); return result; case CLASS_ATOM: /* * atom => atom is the base case */ return predicate_implied_by_simple_clause((Expr *) predicate, clause); } break; } /* can't get here */ elog(ERROR, "predicate_classify returned a bogus value"); return false; }
Definition at line 1033 of file predtest.c.
References btree_predicate_proof(), CHECK_FOR_INTERRUPTS, equal(), func_strict(), is_funcclause, IS_NOT_NULL, is_opclause, IsA, list_member_strip(), and op_strict().
Referenced by predicate_implied_by_recurse().
{ /* Allow interrupting long proof attempts */ CHECK_FOR_INTERRUPTS(); /* First try the equal() test */ if (equal((Node *) predicate, clause)) return true; /* Next try the IS NOT NULL case */ if (predicate && IsA(predicate, NullTest) && ((NullTest *) predicate)->nulltesttype == IS_NOT_NULL) { 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 (is_opclause(clause) && list_member_strip(((OpExpr *) clause)->args, nonnullarg) && op_strict(((OpExpr *) clause)->opno)) return true; if (is_funcclause(clause) && list_member_strip(((FuncExpr *) clause)->args, nonnullarg) && func_strict(((FuncExpr *) clause)->funcid)) return true; } return false; /* we can't succeed below... */ } /* Else try btree operator knowledge */ return btree_predicate_proof(predicate, clause, false); }
Definition at line 182 of file predtest.c.
References linitial, list_length(), NIL, and predicate_refuted_by_recurse().
Referenced by relation_excluded_by_constraints().
{ Node *p, *r; if (predicate_list == NIL) return false; /* no predicate: no refutation is possible */ if (restrictinfo_list == NIL) return false; /* no restriction: refutation must fail */ /* * If either input is a single-element list, replace it with its lone * member; this avoids one useless level of AND-recursion. We only need * to worry about this at top level, since eval_const_expressions should * have gotten rid of any trivial ANDs or ORs below that. */ if (list_length(predicate_list) == 1) p = (Node *) linitial(predicate_list); else p = (Node *) predicate_list; if (list_length(restrictinfo_list) == 1) r = (Node *) linitial(restrictinfo_list); else r = (Node *) restrictinfo_list; /* And away we go ... */ return predicate_refuted_by_recurse(r, p); }
Definition at line 477 of file predtest.c.
References Assert, CLASS_AND, CLASS_ATOM, CLASS_OR, elog, equal(), ERROR, extract_not_arg(), extract_strong_not_arg(), IsA, iterate_begin, iterate_end, NULL, predicate_classify(), predicate_implied_by_recurse(), and predicate_refuted_by_simple_clause().
Referenced by predicate_refuted_by().
{ PredIterInfoData clause_info; PredIterInfoData pred_info; PredClass pclass; Node *not_arg; bool result; /* skip through RestrictInfo */ Assert(clause != NULL); if (IsA(clause, RestrictInfo)) clause = (Node *) ((RestrictInfo *) clause)->clause; pclass = predicate_classify(predicate, &pred_info); switch (predicate_classify(clause, &clause_info)) { case CLASS_AND: switch (pclass) { case CLASS_AND: /* * AND-clause R=> AND-clause if A refutes any of B's items * * Needed to handle (x AND y) R=> ((!x OR !y) AND z) */ result = false; iterate_begin(pitem, predicate, pred_info) { if (predicate_refuted_by_recurse(clause, pitem)) { result = true; break; } } iterate_end(pred_info); if (result) return result; /* * Also check if any of A's items refutes B * * Needed to handle ((x OR y) AND z) R=> (!x AND !y) */ iterate_begin(citem, clause, clause_info) { if (predicate_refuted_by_recurse(citem, predicate)) { result = true; break; } } iterate_end(clause_info); return result; case CLASS_OR: /* * AND-clause R=> OR-clause if A refutes each of B's items */ result = true; iterate_begin(pitem, predicate, pred_info) { if (!predicate_refuted_by_recurse(clause, pitem)) { result = false; break; } } iterate_end(pred_info); return result; case CLASS_ATOM: /* * If B is a NOT-clause, A R=> B if A => B's arg */ not_arg = extract_not_arg(predicate); if (not_arg && predicate_implied_by_recurse(clause, not_arg)) return true; /* * AND-clause R=> atom if any of A's items refutes B */ result = false; iterate_begin(citem, clause, clause_info) { if (predicate_refuted_by_recurse(citem, predicate)) { result = true; break; } } iterate_end(clause_info); return result; } break; case CLASS_OR: switch (pclass) { case CLASS_OR: /* * OR-clause R=> OR-clause if A refutes each of B's items */ result = true; iterate_begin(pitem, predicate, pred_info) { if (!predicate_refuted_by_recurse(clause, pitem)) { result = false; break; } } iterate_end(pred_info); return result; case CLASS_AND: /* * OR-clause R=> AND-clause if each of A's items refutes * any of B's items. */ result = true; iterate_begin(citem, clause, clause_info) { bool presult = false; iterate_begin(pitem, predicate, pred_info) { if (predicate_refuted_by_recurse(citem, pitem)) { presult = true; break; } } iterate_end(pred_info); if (!presult) { result = false; /* citem refutes nothing */ break; } } iterate_end(clause_info); return result; case CLASS_ATOM: /* * If B is a NOT-clause, A R=> B if A => B's arg */ not_arg = extract_not_arg(predicate); if (not_arg && predicate_implied_by_recurse(clause, not_arg)) return true; /* * OR-clause R=> atom if each of A's items refutes B */ result = true; iterate_begin(citem, clause, clause_info) { if (!predicate_refuted_by_recurse(citem, predicate)) { result = false; break; } } iterate_end(clause_info); return result; } break; case CLASS_ATOM: /* * If A is a strong NOT-clause, A R=> B if B equals A's arg * * We cannot make the stronger conclusion that B is refuted if B * implies A's arg; that would only prove that B is not-TRUE, not * that it's not NULL either. Hence use equal() rather than * predicate_implied_by_recurse(). We could do the latter if we * ever had a need for the weak form of refutation. */ not_arg = extract_strong_not_arg(clause); if (not_arg && equal(predicate, not_arg)) return true; switch (pclass) { case CLASS_AND: /* * atom R=> AND-clause if A refutes any of B's items */ result = false; iterate_begin(pitem, predicate, pred_info) { if (predicate_refuted_by_recurse(clause, pitem)) { result = true; break; } } iterate_end(pred_info); return result; case CLASS_OR: /* * atom R=> OR-clause if A refutes each of B's items */ result = true; iterate_begin(pitem, predicate, pred_info) { if (!predicate_refuted_by_recurse(clause, pitem)) { result = false; break; } } iterate_end(pred_info); return result; case CLASS_ATOM: /* * If B is a NOT-clause, A R=> B if A => B's arg */ not_arg = extract_not_arg(predicate); if (not_arg && predicate_implied_by_recurse(clause, not_arg)) return true; /* * atom R=> atom is the base case */ return predicate_refuted_by_simple_clause((Expr *) predicate, clause); } break; } /* can't get here */ elog(ERROR, "predicate_classify returned a bogus value"); return false; }
Definition at line 1091 of file predtest.c.
References arg, btree_predicate_proof(), CHECK_FOR_INTERRUPTS, equal(), func_strict(), is_funcclause, IS_NOT_NULL, IS_NULL, is_opclause, IsA, list_member_strip(), and op_strict().
Referenced by predicate_refuted_by_recurse().
{ /* Allow interrupting long proof attempts */ CHECK_FOR_INTERRUPTS(); /* A simple clause can't refute itself */ /* Worth checking because of relation_excluded_by_constraints() */ if ((Node *) predicate == clause) return false; /* Try the predicate-IS-NULL case */ if (predicate && IsA(predicate, NullTest) && ((NullTest *) predicate)->nulltesttype == IS_NULL) { Expr *isnullarg = ((NullTest *) predicate)->arg; /* row IS NULL does not act in the simple way we have in mind */ if (((NullTest *) predicate)->argisrow) return false; /* Any strict op/func on foo refutes foo IS NULL */ if (is_opclause(clause) && list_member_strip(((OpExpr *) clause)->args, isnullarg) && op_strict(((OpExpr *) clause)->opno)) return true; if (is_funcclause(clause) && list_member_strip(((FuncExpr *) clause)->args, isnullarg) && func_strict(((FuncExpr *) clause)->funcid)) return true; /* foo IS NOT NULL refutes foo IS NULL */ if (clause && IsA(clause, NullTest) && ((NullTest *) clause)->nulltesttype == IS_NOT_NULL && !((NullTest *) clause)->argisrow && equal(((NullTest *) clause)->arg, isnullarg)) return true; return false; /* we can't succeed below... */ } /* Try the clause-IS-NULL case */ if (clause && IsA(clause, NullTest) && ((NullTest *) clause)->nulltesttype == IS_NULL) { Expr *isnullarg = ((NullTest *) clause)->arg; /* row IS NULL does not act in the simple way we have in mind */ if (((NullTest *) clause)->argisrow) return false; /* foo IS NULL refutes foo IS NOT NULL */ if (predicate && IsA(predicate, NullTest) && ((NullTest *) predicate)->nulltesttype == IS_NOT_NULL && !((NullTest *) predicate)->argisrow && equal(((NullTest *) predicate)->arg, isnullarg)) return true; return false; /* we can't succeed below... */ } /* Else try btree operator knowledge */ return btree_predicate_proof(predicate, clause, true); }
const StrategyNumber BT_implic_table[6][6] [static] |
{ {BTGE, BTGE, 0, 0, 0, BTGE}, {BTGT, BTGE, 0, 0, 0, BTGT}, {BTGT, BTGE, BTEQ, BTLE, BTLT, BTNE}, {0, 0, 0, BTLE, BTLT, BTLT}, {0, 0, 0, BTLE, BTLE, BTLE}, {0, 0, 0, 0, 0, BTEQ} }
Definition at line 1289 of file predtest.c.
Referenced by get_btree_test_op().
const StrategyNumber BT_refute_table[6][6] [static] |
{ {0, 0, BTGE, BTGE, BTGE, 0}, {0, 0, BTGT, BTGT, BTGE, 0}, {BTLE, BTLT, BTNE, BTGT, BTGE, BTEQ}, {BTLE, BTLT, BTLT, 0, 0, 0}, {BTLE, BTLE, BTLE, 0, 0, 0}, {0, 0, BTEQ, 0, 0, 0} }
Definition at line 1303 of file predtest.c.
Referenced by get_btree_test_op().
HTAB* OprProofCacheHash = NULL [static] |
Definition at line 1536 of file predtest.c.