Header And Logo

PostgreSQL
| The world's most advanced open source database.

Data Structures | Defines | Typedefs | Enumerations | Functions | Variables

predtest.c File Reference

#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"
Include dependency graph for predtest.c:

Go to the source code of this file.

Data Structures

struct  PredIterInfoData
struct  ArrayConstIterState
struct  ArrayExprIterState
struct  OprProofCacheKey
struct  OprProofCacheEntry

Defines

#define MAX_SAOP_ARRAY_SIZE   100
#define iterate_begin(item, clause, info)
#define iterate_end(info)
#define BTLT   BTLessStrategyNumber
#define BTLE   BTLessEqualStrategyNumber
#define BTEQ   BTEqualStrategyNumber
#define BTGE   BTGreaterEqualStrategyNumber
#define BTGT   BTGreaterStrategyNumber
#define BTNE   ROWCOMPARE_NE

Typedefs

typedef struct PredIterInfoDataPredIterInfo
typedef struct PredIterInfoData PredIterInfoData
typedef struct OprProofCacheKey OprProofCacheKey
typedef struct OprProofCacheEntry OprProofCacheEntry

Enumerations

enum  PredClass { CLASS_ATOM, CLASS_AND, CLASS_OR }

Functions

static bool predicate_implied_by_recurse (Node *clause, Node *predicate)
static bool predicate_refuted_by_recurse (Node *clause, Node *predicate)
static PredClass predicate_classify (Node *clause, PredIterInfo info)
static void list_startup_fn (Node *clause, PredIterInfo info)
static Nodelist_next_fn (PredIterInfo info)
static void list_cleanup_fn (PredIterInfo info)
static void boolexpr_startup_fn (Node *clause, PredIterInfo info)
static void arrayconst_startup_fn (Node *clause, PredIterInfo info)
static Nodearrayconst_next_fn (PredIterInfo info)
static void arrayconst_cleanup_fn (PredIterInfo info)
static void arrayexpr_startup_fn (Node *clause, PredIterInfo info)
static Nodearrayexpr_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 Nodeextract_not_arg (Node *clause)
static Nodeextract_strong_not_arg (Node *clause)
static bool list_member_strip (List *list, Expr *datum)
static bool btree_predicate_proof (Expr *predicate, Node *clause, bool refute_it)
static Oid get_btree_test_op (Oid pred_op, Oid clause_op, bool refute_it)
static void InvalidateOprProofCacheCallBack (Datum arg, int cacheid, uint32 hashvalue)
bool predicate_implied_by (List *predicate_list, List *restrictinfo_list)
bool predicate_refuted_by (List *predicate_list, List *restrictinfo_list)

Variables

static const StrategyNumber BT_implic_table [6][6]
static const StrategyNumber BT_refute_table [6][6]
static HTABOprProofCacheHash = NULL

Define Documentation

#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 
)
Value:
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  ) 
Value:
(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 Documentation

typedef struct PredIterInfoData* PredIterInfo

Definition at line 55 of file predtest.c.


Enumeration Type Documentation

enum PredClass
Enumerator:
CLASS_ATOM 
CLASS_AND 
CLASS_OR 

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;


Function Documentation

static void arrayconst_cleanup_fn ( PredIterInfo  info  )  [static]
static Node * arrayconst_next_fn ( PredIterInfo  info  )  [static]
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]
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.

{
    ArrayExprIterState *state = (ArrayExprIterState *) info->state;

    if (state->next == NULL)
        return NULL;
    lsecond(state->opexpr.args) = lfirst(state->next);
    state->next = lnext(state->next);
    return (Node *) &(state->opexpr);
}

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.

{
    info->state = (void *) list_head(((BoolExpr *) clause)->args);
}

static bool btree_predicate_proof ( Expr predicate,
Node clause,
bool  refute_it 
) [static]

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);
}

static Node * extract_not_arg ( Node clause  )  [static]

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;
}

static Node * extract_strong_not_arg ( Node clause  )  [static]

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;
}

static Oid get_btree_test_op ( Oid  pred_op,
Oid  clause_op,
bool  refute_it 
) [static]

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;
}

static void InvalidateOprProofCacheCallBack ( Datum  arg,
int  cacheid,
uint32  hashvalue 
) [static]

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 */
}

static bool list_member_strip ( List list,
Expr datum 
) [static]

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]

Definition at line 833 of file predtest.c.

References lfirst, lnext, NULL, and PredIterInfoData::state.

{
    ListCell   *l = (ListCell *) info->state;
    Node       *n;

    if (l == NULL)
        return NULL;
    n = lfirst(l);
    info->state = (void *) lnext(l);
    return n;
}

static void list_startup_fn ( Node clause,
PredIterInfo  info 
) [static]

Definition at line 827 of file predtest.c.

References list_head(), and PredIterInfoData::state.

{
    info->state = (void *) list_head((List *) clause);
}

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;
}

bool predicate_implied_by ( List predicate_list,
List restrictinfo_list 
)

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);
}

static bool predicate_implied_by_recurse ( Node clause,
Node predicate 
) [static]

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;
}

static bool predicate_implied_by_simple_clause ( Expr predicate,
Node clause 
) [static]

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);
}

bool predicate_refuted_by ( List predicate_list,
List restrictinfo_list 
)

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);
}

static bool predicate_refuted_by_recurse ( Node clause,
Node predicate 
) [static]

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;
}

static bool predicate_refuted_by_simple_clause ( Expr predicate,
Node clause 
) [static]

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);
}


Variable Documentation

const StrategyNumber BT_implic_table[6][6] [static]
Initial value:
 {

    {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]
Initial value:
 {

    {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.