From e94d26d35ea8e6ff9c69576bb31e10248eb29805 Mon Sep 17 00:00:00 2001 From: wq Date: Mon, 25 Nov 2024 16:27:34 +0800 Subject: [PATCH] Self contradictory examining on rel's baserestrictinfo --- src/backend/optimizer/plan/Makefile | 3 +- src/backend/optimizer/plan/contradictory.c | 2640 ++++++++++++++++++++ src/backend/optimizer/plan/initsplan.c | 16 +- src/backend/optimizer/plan/meson.build | 1 + src/backend/optimizer/plan/planmain.c | 4 + src/backend/utils/cache/lsyscache.c | 39 + src/include/nodes/pathnodes.h | 7 + src/include/optimizer/planmain.h | 2 + src/include/utils/lsyscache.h | 3 + 9 files changed, 2711 insertions(+), 4 deletions(-) create mode 100644 src/backend/optimizer/plan/contradictory.c diff --git a/src/backend/optimizer/plan/Makefile b/src/backend/optimizer/plan/Makefile index 80ef162e48..36ba23664a 100644 --- a/src/backend/optimizer/plan/Makefile +++ b/src/backend/optimizer/plan/Makefile @@ -20,6 +20,7 @@ OBJS = \ planmain.o \ planner.o \ setrefs.o \ - subselect.o + subselect.o \ + contradictory.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/optimizer/plan/contradictory.c b/src/backend/optimizer/plan/contradictory.c new file mode 100644 index 0000000000..a58a825b32 --- /dev/null +++ b/src/backend/optimizer/plan/contradictory.c @@ -0,0 +1,2640 @@ +/*------------------------------------------------------------------------- + * + * initsplan.c + * Target list, qualification, joininfo initialization routines + * + * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/optimizer/plan/contradictory.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "nodes/nodeFuncs.h" +#include "nodes/makefuncs.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/typcache.h" +#include "utils/array.h" +#include "access/nbtree.h" +#include "lib/qunique.h" +#include "common/int.h" +#include "optimizer/restrictinfo.h" +#include "optimizer/paths.h" + +/* + * Self-contradictory definations + * + */ +#define DEFAULT_ELEMS_NUM 4 +#define STRATEGY_NOT_EQUAL (BTMaxStrategyNumber + 1) /* operator '<>' */ + +typedef enum ClauseCxtType +{ + CCT_DEL, + CCT_CONSTT, /* Evaluated expr is const and true. */ + CCT_CONSTF, /* Evaluated expr is const and false. */ + CCT_REPLACE +}ClauseCxtType; + +#define NT_MARK (1<<7) +#define NT_IS_NULL (1<ccfree) + { + ccxt = sec->ccfree; + sec->ccfree = (ClauseContext*) ccxt->cxt; + ccxt->cxt = NULL; + ccxt->origin = NULL; + } + else + ccxt = palloc0(sizeof(ClauseContext)); + + return ccxt; +} + +static inline void free_clause_cxt_internal(SelfExamContext *sec, ClauseContext *cc) +{ + if (sec->ccfree) + { + cc->cxt = sec->ccfree; + sec->ccfree = cc; + } + else + { + cc->cxt = NULL; + sec->ccfree = cc; + } +} + +static inline VarattInfo* get_attinfo_cxt_internal(SelfExamContext *sec) +{ + VarattInfo *varatt; + if (sec->vifree) + { + varatt = sec->vifree; + sec->vifree = varatt->next; + varatt->next = NULL; + } + else + varatt = palloc0(sizeof(VarattInfo)); + + return varatt; +} + +static inline void free_attinfo_cxt_internal(SelfExamContext *sec, VarattInfo *atinfo) +{ + if (sec->vifree) + { + atinfo->next = sec->vifree; + sec->vifree = atinfo; + } + else + { + atinfo->next = NULL; + sec->vifree = atinfo; + } +} + + +static inline AttrOpFamily* get_atop_element_internal(AttrsDetailsCache *attrsdc) +{ + AttrOpFamilySet *atops; + if (attrsdc->len_set == DEFAULT_ELEMS_NUM) + { + atops = palloc0(sizeof(AttrOpFamilySet)); + atops->next = attrsdc->atop_set; + attrsdc->atop_set = atops; + attrsdc->len_set = 0; + } + else + attrsdc->atop_set->elems[attrsdc->len_set].negator = InvalidOid; + return attrsdc->atop_set->elems + attrsdc->len_set++; +} + +static int compare_scalararray_elements(const void *a, const void *b, void *arg) +{ + Datum da = *((const Datum *) a); + Datum db = *((const Datum *) b); + SortArrayContext *cxt = (SortArrayContext *) arg; + int32 compare; + + compare = DatumGetInt32(FunctionCall2Coll(&cxt->sortproc, + cxt->collation, + da, db)); + if (cxt->reverse) + INVERT_COMPARE_RESULT(compare); + return compare; +} + +static int sort_scalararray_elements(SortArrayContext *cxt, + Oid intype, + Oid opfamily, + Oid collid, + bool reverse, + Datum *elems, + int nelems) +{ + RegProcedure cmp_proc; + + if (nelems <= 1) + return nelems; /* No work to do */ + /* + * Look up the appropriate comparison function in the opfamily. + * + * Note: it's possible that this would fail, if the opfamily is + * incomplete, but it seems quite unlikely that an opfamily would omit + * non-cross-type support functions for any datatype that it supports at + * all. + */ + cmp_proc = get_opfamily_proc(opfamily, + intype, + intype, + BTORDER_PROC); + if (!RegProcedureIsValid(cmp_proc)) + return 0; /* No support function, give up, do nothing. */ + + /* Sort the array elements */ + fmgr_info(cmp_proc, &cxt->sortproc); + cxt->collation = collid; + cxt->reverse = reverse; + qsort_arg((void *) elems, nelems, sizeof(Datum), + compare_scalararray_elements, (void *) cxt); + + /* Now scan the sorted elements and remove duplicates */ + return qunique_arg(elems, nelems, sizeof(Datum), + compare_scalararray_elements, cxt); +} + +static void extract_opc_info(int nums, Oid opcid, Oid opcfamily, Oid opcintype, void *infocxt) +{ + int i; + OpcInfoElement *opcele; + SortedOpclassInfo *sopcinfo = (SortedOpclassInfo*) infocxt; + if (sopcinfo->n_members == 0) + { + sopcinfo->opcies = palloc0(nums * (sizeof(OpcInfoElement*) + sizeof(OpcInfoElement))); + opcele = (OpcInfoElement*) (sopcinfo->opcies + nums); + for (i = 0; i < nums; i++) + sopcinfo->opcies[i] = opcele + i; + } + + sopcinfo->opcies[sopcinfo->n_members]->opcfamily = opcfamily; + sopcinfo->opcies[sopcinfo->n_members]->opcid = opcid; + sopcinfo->opcies[sopcinfo->n_members]->opcintype = opcintype; + sopcinfo->n_members++; + Assert(sopcinfo->n_members <= nums); +} + +static int opc_element_cmp(const void *p1, const void *p2) +{ + Oid v1 = (*((const OpcInfoElement **) p1))->opcintype; + Oid v2 = (*((const OpcInfoElement **) p2))->opcintype; + + return pg_cmp_u32(v1, v2); +} + +static Oid binary_search_opc_info(SortedOpclassInfo *sopi, Oid inputtype) +{ + Assert(sopi != NULL); + if (sopi->n_members > 0) + { + int i, l, r, res; + l = 0; + r = sopi->n_members - 1; + while (l <= r) + { + i = (l + r) / 2; + res = pg_cmp_u32(sopi->opcies[i]->opcintype, inputtype); + if (res == 0) + { + return sopi->opcies[i]->opcfamily; + } + else if (res < 0) + l = i + 1; + else + r = i - 1; + } + } + return InvalidOid; +} + +static int ne_const_arg_cmp(const void *a, const void *b, void *arg) +{ + Datum da = *(((const NeConstDat *) a)->val); + Datum db = *(((const NeConstDat *) b)->val); + SortNeConstCxt *cncc = (SortNeConstCxt *) arg; + int32 compare; + + compare = DatumGetInt32(FunctionCall2Coll(&cncc->cmp_proc, + cncc->collation, + da, db)); + return compare; +} + +static inline void init_ne_const(NeConstVal *nv) +{ + if (nv->capacity == 0) + { + nv->ncds = palloc0(DEFAULT_ELEMS_NUM * sizeof(NeConstDat)); + nv->size = 0; + nv->capacity = DEFAULT_ELEMS_NUM; + } +} + +static inline void insert_ne_const(NeConstVal *nv, uint32 idx, Datum *dat) +{ + if (nv->size == nv->capacity) { + nv->capacity *= 2; + nv->ncds = repalloc(nv->ncds, nv->capacity * sizeof(NeConstDat)); + } + nv->ncds[nv->size].idx = idx; + nv->ncds[nv->size].val = dat; + nv->size++; + nv->sorted = false; +} + +/* binary_search_ne_const + * Note: caller need to check number of elements in NeConstVal. + * + */ +static bool binary_search_ne_const(NeConstVal *nv, Datum *dat) +{ + int i, l, r, res; + NeConstDat ncd; + ncd.val = dat; + l = 0; + r = nv->size - 1; + while (l <= r) + { + i = (l + r) / 2; + res = ne_const_arg_cmp(&nv->ncds[i], &ncd, (void *) &nv->sncc); + if (res == 0) + { + return true; + } + else if (res < 0) + l = i + 1; + else + r = i - 1; + } + return false; +} + + +static bool search_ne_const(NeConstVal *nv, Oid collid, Datum *val) +{ + int i; + bool result; + + if (nv->upper_ncv) + { + if (search_ne_const(nv->upper_ncv, collid, val)) + return true; + } + + if (nv->size == 0) + return false; + + /* If there is no support function or size is small less than 4, use + * linear searching. + */ + if (!nv->can_sort || nv->size <= DEFAULT_ELEMS_NUM) + { + for (i = 0; i < nv->size;i++) + { + result = DatumGetBool(FunctionCall2Coll(&nv->op_func, + collid, + *val, + *nv->ncds[i].val)); + if (!result) + return true; + } + return false; + } + + if (nv->can_sort && !nv->sorted) + { + qsort_arg((void *) nv->ncds, nv->size, sizeof(NeConstDat), + ne_const_arg_cmp, (void *) &nv->sncc); + nv->sorted = true; + } + + if (binary_search_ne_const(nv, val)) + return true; + return false; +} + +static VarattInfo* get_varatt_info_internal(SelfExamContext *sec, ExtractedAttris *ea, Var *var) +{ + VarattInfo *attinfo, *attupper; + ConstVal *constv; + ExtractedAttris *up_ea; + NeConstVal **up_ncv; + int idx, i; + idx = var->varattno - ea->min_attr; + if (ea->array_vi[idx] == NULL) + { + attinfo = get_attinfo_cxt_internal(sec); + if (ea->att_lst) + { + attinfo->next = ea->att_lst; + ea->att_lst = attinfo; + } + else + ea->att_lst = ea->last = attinfo; + ea->array_vi[idx] = attinfo; + + attinfo->stratcount = 0; + attinfo->null_test = 0; + attinfo->bool_test = 0; + attinfo->atidx = idx; + attinfo->collid = var->varcollid; + attinfo->type = var->vartype; + attinfo->ne_const.size = 0; + attinfo->ne_const.upper_ncv = NULL; + attinfo->saop.elemnents = NIL; + attinfo->saop.upper_elems = NIL; + for(i = 0; i < BTMaxStrategyNumber; i++) + attinfo->st_table[i].strategy = InvalidStrategy; + + /* Copy push-down attribute particulars. */ + up_ea = ea->upper_ea; + up_ncv = &attinfo->ne_const.upper_ncv; + while(up_ea) + { + if (up_ea->array_vi[idx]) + { + attupper = up_ea->array_vi[idx]; + + if (attinfo->null_test == 0 && attupper->null_test != 0) + attinfo->null_test = attupper->null_test; + if (attinfo->bool_test == 0 && attupper->bool_test != 0) + attinfo->bool_test = attupper->bool_test; + if (!*up_ncv && attupper->ne_const.size != 0) + { + *up_ncv = &attupper->ne_const; + up_ncv = &(*up_ncv)->upper_ncv; + } + if (attinfo->stratcount == 0 && attupper->stratcount != 0) + { + attinfo->stratcount = attupper->stratcount; + for(i = 0; i < BTMaxStrategyNumber; i++) + { + constv = &attupper->st_table[i]; + if (constv->strategy != InvalidStrategy) + { + memcpy(&attinfo->st_table[i], constv, sizeof(ConstVal)); + attinfo->st_table[i].push_down = true; + } + } + } + if (attinfo->saop.upper_elems == NIL && attupper->saop.elemnents != NIL) + attinfo->saop.upper_elems = attupper->saop.elemnents; + } + up_ea = up_ea->upper_ea; + } + } + else + attinfo = ea->array_vi[idx]; + return attinfo; +} + +static bool deconstruct_nulltest_attr(Node *ntnode, + NullTestType ntt, + SelfExamContext *sec, + ExtractedAttris *ea) +{ + if (IsA(ntnode, Var)) + { + VarattInfo *att_elem; + att_elem = get_varatt_info_internal(sec, ea, (Var *) ntnode); + + if (ntt == IS_NULL) + att_elem->null_test = att_elem->null_test | NT_MARK | NT_IS_NULL; + else + att_elem->null_test = att_elem->null_test | NT_MARK | NT_IS_NOT_NULL; + if (att_elem->null_test == (NT_MARK | NT_IS_NULL | NT_IS_NOT_NULL)) + { + return false; + } + } + else if (IsA(ntnode, ArrayExpr)) + { + ListCell *l; + foreach(l, ((ArrayExpr *) ntnode)->elements) + { + if(!deconstruct_nulltest_attr((Node *) lfirst(l), ntt, sec, ea)) + return false; + } + } + + return true; +} + +static bool add_binary_op_clause_info(VarattInfo *att_elem, + Datum *vals, + int nums, + uint32 idx, + uint16 strat, + RegProcedure opfuncid, + Expr *expr, + SelfExamContext *sec, + ClauseCxtWapper *ccw) +{ + ConstVal *constval; + ClauseContext *ccxt; + AttrOpFamily *atop; + Oid cmp_proc; + bool result; + + if (strat == STRATEGY_NOT_EQUAL) + { + if (att_elem->st_table[BTEqualStrategyNumber-1].strategy != InvalidStrategy) + { + constval = &(att_elem->st_table[BTEqualStrategyNumber-1]); + result = DatumGetBool(FunctionCall2Coll(&constval->op_func, + att_elem->collid, + *vals, + *(constval->value))); + if (result) + return false; + } + + if (att_elem->ne_const.size == 0) + { + init_ne_const(&att_elem->ne_const); + att_elem->ne_const.can_sort = false; + att_elem->ne_const.sorted = false; + fmgr_info(opfuncid, &(att_elem->ne_const.op_func)); + atop = sec->attrsdc.atop_array[att_elem->atidx]; + cmp_proc = get_opfamily_proc(atop->opfamily, + att_elem->type, + att_elem->type, + BTORDER_PROC); + if (!RegProcedureIsValid(cmp_proc)) + { + /* No support function, use linear searching. */ + att_elem->ne_const.can_sort = false; + } + else + { + att_elem->ne_const.sncc.reverse = false; + fmgr_info(cmp_proc, &att_elem->ne_const.sncc.cmp_proc); + att_elem->ne_const.sncc.collation = att_elem->collid; + att_elem->ne_const.can_sort = true; + } + } + insert_ne_const(&att_elem->ne_const, idx, vals); + } + else if (strat <= BTMaxStrategyNumber) + { + Assert(strat >= BTLessStrategyNumber); + constval = &(att_elem->st_table[strat-1]); + if (constval->strategy == InvalidStrategy) + { + att_elem->stratcount++; + constval->strategy = strat; + fmgr_info(opfuncid, &(constval->op_func)); + constval->value = vals; + constval->expr = expr; + constval->push_down = false; + constval->idx = idx; + } + else + { + result = DatumGetBool(FunctionCall2Coll(&constval->op_func, + att_elem->collid, + *vals, + *(constval->value))); + /* Repeat operator = but the consts ain't equal ( eg. x=2 and x=3 ). */ + if (strat == BTEqualStrategyNumber) + { + if (!result) + return false; + if (constval->push_down) + { + constval->value = vals; + constval->expr = expr; + constval->push_down = false; + constval->idx = idx; + } + else if (IsA(expr, OpExpr)) + { + ccxt = get_clause_cxt_internal(sec); + ccxt->cct = CCT_DEL; + ccxt->idx = idx; + ccw->clausecxt = lappend(ccw->clausecxt, ccxt); + } + } + else if (result) + { + /* Current Expr is more restrict, so keep it. */ + if (!constval->push_down && IsA(constval->expr, OpExpr)) + { + ccxt = get_clause_cxt_internal(sec); + ccxt->cct = CCT_DEL; + ccxt->idx = constval->idx; + ccw->clausecxt = lappend(ccw->clausecxt, ccxt); + } + constval->value = vals; + constval->expr = expr; + constval->push_down = false; + constval->idx = idx; + } + else + { + /* Current Expr is less restrict, so abandon it. */ + if (constval->push_down) + { + ccxt = get_clause_cxt_internal(sec); + ccxt->cct = CCT_REPLACE; + ccxt->idx = idx; + ccxt->cxt = copyObjectImpl(constval->expr); + ccw->clausecxt = lappend(ccw->clausecxt, ccxt); + + constval->push_down = false; + constval->expr = ccxt->cxt; + constval->idx = idx; + } + else if (IsA(expr, OpExpr)) + { + ccxt = get_clause_cxt_internal(sec); + ccxt->cct = CCT_DEL; + ccxt->idx = idx; + ccw->clausecxt = lappend(ccw->clausecxt, ccxt); + } + } + } + } + return true; +} + +static bool self_contradictory_element(SelfExamContext *sec, + ClauseCxtWapper *ccw, + VarattInfo *att_elem) +{ + ConstVal *leftcv, *rightcv; + ClauseContext *ccxt; + int i; + uint8 btest; + bool result; + bool macthed; + + if (att_elem->bool_test & BT_MARK) + { + btest = att_elem->bool_test & ~BT_MARK; + if (((btest & BT_IS_TRUE) && (btest & (BT_IS_NOT_TRUE | BT_IS_FALSE | BT_IS_UNKNOWN))) + || ((btest & BT_IS_FALSE) && (btest &(BT_IS_TRUE | BT_IS_NOT_FALSE | BT_IS_UNKNOWN))) + || ((btest & BT_IS_UNKNOWN) && (btest &(BT_IS_NOT_UNKNOWN | BT_IS_TRUE | BT_IS_FALSE)))) + { + return true; + } + } + + if (att_elem->null_test == (NT_MARK | NT_IS_NULL) + && (att_elem->stratcount > 0 + || att_elem->ne_const.size != 0 + || att_elem->ne_const.upper_ncv)) + return true; + + if ((att_elem->bool_test & BT_MARK) && (att_elem->null_test & NT_MARK)) + { + if (att_elem->null_test == (NT_MARK | NT_IS_NULL) + && (btest & (BT_IS_TRUE | BT_IS_FALSE | BT_IS_NOT_UNKNOWN))) + return true; + else if (att_elem->null_test == (NT_MARK | NT_IS_NOT_NULL) + && (btest & BT_IS_UNKNOWN)) + return true; + } + + /* If operator '=' and '<>' are both appeared. */ + if (att_elem->st_table[BTEqualStrategyNumber-1].strategy != 0 + && (att_elem->ne_const.size != 0 || att_elem->ne_const.upper_ncv)) + { + leftcv = &att_elem->st_table[BTEqualStrategyNumber-1]; + + if (search_ne_const(&att_elem->ne_const, att_elem->collid, leftcv->value)) + return true; + /* If they are not contradictory, eliminate the not equalities. */ + for (i = 0; i < att_elem->ne_const.size; i++) + { + ccxt = get_clause_cxt_internal(sec); + ccxt->cct = CCT_DEL; + ccxt->idx = att_elem->ne_const.ncds[i].idx; + ccw->clausecxt = lappend(ccw->clausecxt, ccxt); + } + att_elem->ne_const.size = 0; + } + + /* Try to keep only one of <, <= */ + if (att_elem->st_table[BTLessStrategyNumber - 1].strategy != 0 + && att_elem->st_table[BTLessEqualStrategyNumber - 1].strategy != 0) + { + leftcv = &att_elem->st_table[BTLessStrategyNumber - 1]; + rightcv = &att_elem->st_table[BTLessEqualStrategyNumber - 1]; + result = DatumGetBool(FunctionCall2Coll(&(rightcv->op_func), + att_elem->collid, + *(leftcv->value), + *(rightcv->value))); + if (result) + { + rightcv->strategy = InvalidStrategy; + if (leftcv->push_down) + { + ccxt = get_clause_cxt_internal(sec); + ccxt->cct = CCT_REPLACE; + ccxt->idx = rightcv->idx; + ccxt->cxt = copyObjectImpl(leftcv->expr); + ccw->clausecxt = lappend(ccw->clausecxt, ccxt); + + leftcv->push_down = false; + leftcv->expr = ccxt->cxt; + leftcv->idx = rightcv->idx; + } + else if (!rightcv->push_down) + { + ccxt = get_clause_cxt_internal(sec); + ccxt->cct = CCT_DEL; + ccxt->idx = rightcv->idx; + ccw->clausecxt = lappend(ccw->clausecxt, ccxt); + } + } + else + { + leftcv->strategy = InvalidStrategy; + if (rightcv->push_down) + { + ccxt = get_clause_cxt_internal(sec); + ccxt->cct = CCT_REPLACE; + ccxt->idx = leftcv->idx; + ccxt->cxt = copyObjectImpl(rightcv->expr); + ccw->clausecxt = lappend(ccw->clausecxt, ccxt); + + rightcv->push_down = false; + rightcv->expr = ccxt->cxt; + rightcv->idx = leftcv->idx; + } + else if (!leftcv->push_down) + { + ccxt = get_clause_cxt_internal(sec); + ccxt->cct = CCT_DEL; + ccxt->idx = leftcv->idx; + ccw->clausecxt = lappend(ccw->clausecxt, ccxt); + } + } + att_elem->stratcount--; + } + + /* try to keep only one of >, >= */ + if (att_elem->st_table[BTGreaterStrategyNumber - 1].strategy != 0 + && att_elem->st_table[BTGreaterEqualStrategyNumber - 1].strategy != 0) + { + leftcv = &att_elem->st_table[BTGreaterStrategyNumber - 1]; + rightcv = &att_elem->st_table[BTGreaterEqualStrategyNumber - 1]; + result = DatumGetBool(FunctionCall2Coll(&(rightcv->op_func), + att_elem->collid, + *(leftcv->value), + *(rightcv->value))); + if (result) + { + rightcv->strategy = InvalidStrategy; + if (leftcv->push_down) + { + ccxt = get_clause_cxt_internal(sec); + ccxt->cct = CCT_REPLACE; + ccxt->idx = rightcv->idx; + ccxt->cxt = copyObjectImpl(leftcv->expr); + ccw->clausecxt = lappend(ccw->clausecxt, ccxt); + + leftcv->push_down = false; + leftcv->expr = ccxt->cxt; + leftcv->idx = rightcv->idx; + } + else if (!rightcv->push_down) + { + ccxt = get_clause_cxt_internal(sec); + ccxt->cct = CCT_DEL; + ccxt->idx = rightcv->idx; + ccw->clausecxt = lappend(ccw->clausecxt, ccxt); + } + } + else + { + leftcv->strategy = InvalidStrategy; + if (rightcv->push_down) + { + ccxt = get_clause_cxt_internal(sec); + ccxt->cct = CCT_REPLACE; + ccxt->idx = leftcv->idx; + ccxt->cxt = copyObjectImpl(rightcv->expr); + ccw->clausecxt = lappend(ccw->clausecxt, ccxt); + + rightcv->push_down = false; + rightcv->expr = ccxt->cxt; + rightcv->idx = leftcv->idx; + } + else if (!leftcv->push_down) + { + ccxt = get_clause_cxt_internal(sec); + ccxt->cct = CCT_DEL; + ccxt->idx = leftcv->idx; + ccw->clausecxt = lappend(ccw->clausecxt, ccxt); + } + } + att_elem->stratcount--; + } + + if (att_elem->type == BOOLOID && att_elem->stratcount > 0) + { + /* + * Beyond the scope of the defined values of boolean type, + * obviously it does not make sense. + */ + if (att_elem->st_table[BTGreaterStrategyNumber - 1].strategy != 0) + { + leftcv = &att_elem->st_table[BTGreaterStrategyNumber - 1]; + result = DatumGetBool(FunctionCall2Coll(&leftcv->op_func, + att_elem->collid, + BoolGetDatum(true), + *leftcv->value)); + if (!result) + return true; + } + if (att_elem->st_table[BTLessStrategyNumber - 1].strategy != 0) + { + leftcv = &att_elem->st_table[BTLessStrategyNumber - 1]; + result = DatumGetBool(FunctionCall2Coll(&leftcv->op_func, + att_elem->collid, + BoolGetDatum(false), + *leftcv->value)); + if (!result) + return true; + } + + if (att_elem->bool_test & BT_MARK) + { + btest = att_elem->bool_test & ~BT_MARK; + if (btest & BT_IS_UNKNOWN) + return true; + else + { + bool isvalue; + macthed = true; + /* If IS_NOT_FALSE is set, that mean the equivalent expression is + * (x IS NULL OR x IS TRUE). The NULL test will always fail with + * the strategy operator, so we only need to test whether the + * sub-expression (x IS TRUE) is contradictory to the strategy + * operator. If IS_NOT_TRUE is set, it's similar to IS_NOT_FALSE. + */ + if (btest & (BT_IS_TRUE| BT_IS_NOT_FALSE)) + isvalue = true; + else if (btest & (BT_IS_FALSE| BT_IS_NOT_TRUE)) + isvalue = false; + else + macthed = false; + if (macthed) + { + for (i = BTMaxStrategyNumber; --i >= 0;) + { + leftcv = &att_elem->st_table[i]; + if (leftcv->strategy == InvalidStrategy) + continue; + result = DatumGetBool(FunctionCall2Coll(&leftcv->op_func, + att_elem->collid, + BoolGetDatum(isvalue), + *leftcv->value)); + if (!result) + return true; + } + } + } + } + } + + if (att_elem->stratcount <=1) + return false; + + if (att_elem->st_table[BTEqualStrategyNumber-1].strategy != 0) + { + leftcv = &att_elem->st_table[BTEqualStrategyNumber - 1]; + for (i = BTMaxStrategyNumber; --i >= 0;) + { + rightcv = &att_elem->st_table[i]; + if (rightcv->strategy == InvalidStrategy || i == (BTEqualStrategyNumber - 1)) + continue; + result = DatumGetBool(FunctionCall2Coll(&(rightcv->op_func), + att_elem->collid, + *(leftcv->value), + *(rightcv->value))); + if (!result) + return true; + /* The element had be dominated by operator =, so abandon it. */ + if (leftcv->push_down) + { + ccxt = get_clause_cxt_internal(sec); + ccxt->cct = CCT_REPLACE; + ccxt->idx = rightcv->idx; + ccxt->cxt = copyObjectImpl(leftcv->expr); + ccw->clausecxt = lappend(ccw->clausecxt, ccxt); + + leftcv->push_down = false; + leftcv->expr = ccxt->cxt; + leftcv->idx = rightcv->idx; + } + else if (!rightcv->push_down) + { + ccxt = get_clause_cxt_internal(sec); + ccxt->cct = CCT_DEL; + ccxt->idx = rightcv->idx; + ccw->clausecxt = lappend(ccw->clausecxt, ccxt); + } + rightcv->strategy = InvalidStrategy; + } + /* The operator = has dominated others, so no need to check other case. */ + return false; + } + + /* Detect the contradictory case that like x <4 and x >10. */ + macthed = true; + if (att_elem->st_table[BTGreaterStrategyNumber - 1].strategy != 0) + { + rightcv = &att_elem->st_table[BTGreaterStrategyNumber - 1]; + if (att_elem->st_table[BTLessStrategyNumber - 1].strategy != 0) + leftcv = &att_elem->st_table[BTLessStrategyNumber - 1]; + else if (att_elem->st_table[BTLessEqualStrategyNumber - 1].strategy != 0) + leftcv = &att_elem->st_table[BTLessEqualStrategyNumber - 1]; + else + macthed = false; + } + else if (att_elem->st_table[BTLessStrategyNumber - 1].strategy != 0) + { + rightcv = &att_elem->st_table[BTLessStrategyNumber - 1]; + if (att_elem->st_table[BTGreaterStrategyNumber - 1].strategy != 0) + leftcv = &att_elem->st_table[BTGreaterStrategyNumber - 1]; + else if (att_elem->st_table[BTGreaterEqualStrategyNumber - 1].strategy != 0) + leftcv = &att_elem->st_table[BTGreaterEqualStrategyNumber - 1]; + else + macthed = false; + } + else if (att_elem->st_table[BTLessEqualStrategyNumber - 1].strategy != 0 + && att_elem->st_table[BTGreaterEqualStrategyNumber - 1].strategy != 0) + { + rightcv = &att_elem->st_table[BTGreaterEqualStrategyNumber - 1]; + leftcv = &att_elem->st_table[BTLessEqualStrategyNumber - 1]; + } + else + macthed = false; + if (macthed) + { + result = DatumGetBool(FunctionCall2Coll(&(rightcv->op_func), + att_elem->collid, + *(leftcv->value), + *(rightcv->value))); + if (!result) + return true; + } + + return false; +} + +static bool check_eq_sa_op_elemnet(Datum *value, VarattInfo *att_elem) +{ + ConstVal *constval; + int i; + bool result; + uint8 btest; + bool isvalue, macthed; + + if (att_elem->null_test == (NT_MARK | NT_IS_NULL)) + return true; + + if (att_elem->bool_test & BT_MARK) + { + btest = att_elem->bool_test & ~BT_MARK; + + if (btest & BT_IS_UNKNOWN) + return true; + + macthed = true; + if (btest & (BT_IS_TRUE| BT_IS_NOT_FALSE)) + isvalue = true; + else if (btest & (BT_IS_FALSE| BT_IS_NOT_TRUE)) + isvalue = false; + else + macthed = false; + + if (macthed && DatumGetBool(*value) != isvalue) + return true; + } + + if (search_ne_const(&att_elem->ne_const, att_elem->collid, value)) + return true; + if (att_elem->stratcount > 0) + { + for (i = BTMaxStrategyNumber; --i >= 0;) + { + constval = &att_elem->st_table[i]; + if (constval->strategy == InvalidStrategy) + continue; + result = DatumGetBool(FunctionCall2Coll(&(constval->op_func), + att_elem->collid, + *(value), + *(constval->value))); + if (!result) + return true; + } + } + return false; +} + +/* + * self_contradictory_saop + * The Scalar array expr of any-equality expression are postponed here + * to examine them (eg. x in (1,3) and x in (3,5) and x = 1 ). + * Extract intersection elements and report to the caller if there is + * no intersection element or check intersection elements one by one + * and eliminate it if it's self-contradictory. All of them are + * self-contradictory report it. + * Rebuilt the saop finally. + */ +static bool self_contradictory_saop(SelfExamContext *sec, + ClauseCxtWapper *ccw, + VarattInfo *att_elem) +{ + /* Scalar array expr of single attribute. */ + ScalarArrayOpExpr *saopexpr; + SAEqualDatElem *saout; + ClauseContext *ccxt; + ListCell *lc; + Expr *newclause; + Const *con; + int i, j, initial_nums; + + if (att_elem->saop.elemnents == NIL) + return false; + + if (att_elem->saop.upper_elems != NIL) + att_elem->saop.elemnents = list_concat(att_elem->saop.elemnents, att_elem->saop.upper_elems); + + saout = linitial(att_elem->saop.elemnents); + initial_nums = saout->elem_nums; + if (list_length(att_elem->saop.elemnents) > 1) + { + /* Initialize saout with the first one, merge the rest of elemnents + * one by one and extract the intersection element at the same time. + * Update the saout with merged result. + */ + for_each_from(lc, att_elem->saop.elemnents, 1) + { + SAEqualDatElem *sade; + int nelems_dups; + + sade =lfirst(lc); + nelems_dups = 0; + for (i = 0, j = 0; i < saout->elem_nums && j < sade->elem_nums;) + { + Datum *oelem = saout->elem_values + i, + *nelem = sade->elem_values + j; + int res = compare_scalararray_elements(oelem, nelem, &att_elem->saop.sortcxt); + if (res == 0) + { + saout->elem_values[nelems_dups++] = *oelem; + i++; + j++; + } + else if (res < 0) + i++; + else /* res > 0 */ + j++; + } + if (nelems_dups == 0) + { + att_elem->saop.elemnents = NIL; + return true; /* No intersection element, so report it. */ + } + else + saout->elem_nums = nelems_dups; + } + } + + /* Examine the rest of intersection elements one by one. */ + for(i = 0; i < saout->elem_nums;) + { + if (check_eq_sa_op_elemnet(&saout->elem_values[i], att_elem)) + { + if (i < saout->elem_nums - 1) + memmove(saout->elem_values + i, saout->elem_values + i + 1, + sizeof(Datum) * (saout->elem_nums - i - 1)); + saout->elem_nums--; + } + else + i++; + } + if (0 == saout->elem_nums) + { + att_elem->saop.elemnents = NIL; + return true; + } + + /* + * Convert the intersection elements to a new clause and delete orignal + * elements. + */ + saopexpr = (ScalarArrayOpExpr*)saout->expr; + if (saout->elem_nums == 1) + { + con = makeConst(att_elem->saop.sortcxt.elmtype, + -1, /* typmod -1 is OK for all cases */ + InvalidOid, + att_elem->saop.sortcxt.elmlen, + saout->elem_values[0], + false, + att_elem->saop.sortcxt.elmbyval); + con->location = -1; + newclause = make_opclause(saopexpr->opno, + BOOLOID, + false, + linitial(saopexpr->args), + (Expr*)con, + InvalidOid, + saopexpr->inputcollid); + ccxt = get_clause_cxt_internal(sec); + ccxt->cct = CCT_REPLACE; + ccxt->idx = saout->idx; + ccxt->cxt = newclause; + ccw->clausecxt = lappend(ccw->clausecxt, ccxt); + add_binary_op_clause_info(att_elem, + &saout->elem_values[0], + 1, + saout->idx, + BTEqualStrategyNumber, + att_elem->saop.opfuncid, + newclause, + sec, + ccw); + } + else if (saout->elem_nums != initial_nums) + { + /* It's need to rebuilt the first saop if there are multi-saop, + * and delete others saop later. + */ + ArrayType *arrval = construct_array(saout->elem_values, + saout->elem_nums, + att_elem->saop.sortcxt.elmtype, + att_elem->saop.sortcxt.elmlen, + att_elem->saop.sortcxt.elmbyval, + att_elem->saop.sortcxt.elmalign); + con = lsecond(saopexpr->args); + con->constvalue = PointerGetDatum(arrval); + + ccxt = get_clause_cxt_internal(sec); + ccxt->cct = CCT_REPLACE; + ccxt->idx = saout->idx; + ccxt->cxt = saopexpr; + ccw->clausecxt = lappend(ccw->clausecxt, ccxt); + + } + + /* The push-down elements have finished their jobs, so truncate them. */ + if (att_elem->saop.upper_elems != NIL) + { att_elem->saop.elemnents = list_truncate(att_elem->saop.elemnents, \ + list_length(att_elem->saop.elemnents) - list_length(att_elem->saop.upper_elems)); + } + + /* Since the whole elements except first one have be merged into first one, + * it's time to delete them from the orignal clause list. + */ + for_each_from(lc, att_elem->saop.elemnents, 1) + { + saout =lfirst(lc); + ccxt = get_clause_cxt_internal(sec); + ccxt->cct = CCT_DEL; + ccxt->idx = saout->idx; + ccw->clausecxt = lappend(ccw->clausecxt, ccxt); + } + /* It's need to truncate the list, it maybe push down to + * compare with lower level ScalarArrayOpExpr, since the + * whole elements except first one have already merged. + */ + att_elem->saop.elemnents = list_truncate(att_elem->saop.elemnents, 1); + return false; +} + +static bool self_contradictory_check(SelfExamContext *sec, ExtractedAttris *ea, ClauseCxtWapper *ccw) +{ + VarattInfo *attinfo; + if (!ea->addOp) + return false; + attinfo = ea->att_lst; + while(attinfo) + { + if (self_contradictory_element(sec, ccw, attinfo)) + return true; + + if (self_contradictory_saop(sec, ccw, attinfo)) + return true; + attinfo = attinfo->next; + } + return false; +} + +/* + * extract_binary_op_clause_info + * Extract binary operation clause information + * + * In practice, B-tree index is used widely; Binary operation + * expr that Var op Const is often written; Also without + * data type coercion and collation. So this routine support + * above features. + * There are several consideration 1) self-contradictory + * rel seldom appeared. 2) Invoker will get upset if spend + * too much effort on it and find that rel is not + * self-contradictory (eg. strict function sqrt, expr like + * sqrt(x) > 2 and sqrt(x) < 4 ). 3) Finally the expression + * evaluation mechanism will do the right things. So this is + * a compromise. + */ +static bool extract_binary_op_clause_info(void *expr, + Node *lop, + Node *rop, + uint32 idx, + Oid opno, + Oid collid, + SelfExamContext *sec, + ExtractedAttris *ea, + ClauseCxtWapper *ccw) +{ + VarattInfo *attinfo; + Node *leftop, *rightop; + AttrOpFamily *atop; + Expr *newclause; + Oid left_type, right_type; + Oid varcollid; + Oid opid, opfamily; + RegProcedure opfuncid; + StrategyNumber strat; + uint16 attidx; + + if (!(IsA(lop, Const) || IsA(rop, Const))) + return true; + + if (IsA(lop, Const)) + { + opid = get_commutator(opno); + if (!OidIsValid(opid)) + return true; + leftop = rop; + rightop = lop; + } + else + { + opid = opno; + leftop = lop; + rightop = rop; + } + opfuncid = get_opcode(opid); + Assert(OidIsValid(opfuncid)); + + if (((Const *) rightop)->constisnull) + return true; + + left_type = InvalidOid; + right_type = ((Const *) rightop)->consttype; + + if (IsA(expr, ScalarArrayOpExpr)) + { + Oid typInput, typelem; + getTypeInputInfo(right_type, &typInput, &typelem); + right_type = typelem; + } + + if (leftop && IsA(leftop, RelabelType)) + { + left_type = ((RelabelType *) leftop)->resulttype; + varcollid = ((RelabelType *) leftop)->resultcollid; + leftop = (Node*)((RelabelType *) leftop)->arg; + } + + if (IsA(leftop, Var)) + { + if (((Var *) leftop)->varattno > sec->attrsdc.max_attr) + elog(ERROR, "bogus relation attribute"); + attidx = ((Var *) leftop)->varattno - sec->attrsdc.min_attr; + } + else /* Don't spend much effort for other types, so skip it */ + return true; + + if (!OidIsValid(left_type)) + { + left_type = ((Var *) leftop)->vartype; + varcollid = ((Var *) leftop)->varcollid; + } + + if (left_type != right_type) + return true; + if (collid != varcollid) + return true; + + /* Search attribute opfamily cache, be happy to use it if find. */ + if (sec->attrsdc.atop_array[attidx]) + { + atop = sec->attrsdc.atop_array[attidx]; + if (!OidIsValid(atop->opfamily)) + return true; + opfamily = atop->opfamily; + } + else + { + atop = get_atop_element_internal(&sec->attrsdc); + sec->attrsdc.atop_array[attidx] = atop; + + opfamily = binary_search_opc_info(&sec->sopcinfo, left_type); + if (!OidIsValid(opfamily)) + { + atop->opfamily = InvalidOid; + return true; + } + atop->opfamily = opfamily; + } + + if (!OidIsValid(atop->negator) && atop->negator == opid) + strat = STRATEGY_NOT_EQUAL; + else + { + strat = get_op_opfamily_strategy(opid, opfamily); + if (strat == 0) + { + /* Perhaps it is a <> operator. See if it has a negator that is in an opfamily. */ + Oid op_negator = get_negator(opid); + if (!OidIsValid(op_negator)) + return true; + if (BTEqualStrategyNumber == get_op_opfamily_strategy(op_negator, opfamily)) + { + atop->negator = opid; + strat = STRATEGY_NOT_EQUAL; + } + else + return true; + } + } + + /* If it's scalar array expr, deconstruct the const values. */ + if (unlikely(IsA(expr, ScalarArrayOpExpr))) + { + ArrayType *arrayval; + int num_elems; + Datum *elem_values, *one_elem; + bool *elem_nulls; + int num_nonnulls; + int j; + SortArrayContext sacontext; + ClauseContext *ccxt; + Const *con; + ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *)expr; + arrayval = DatumGetArrayTypeP(((Const *) rightop)->constvalue); + /* We could cache this data, but not clear it's worth it */ + get_typlenbyvalalign(ARR_ELEMTYPE(arrayval), + &sacontext.elmlen, &sacontext.elmbyval, &sacontext.elmalign); + sacontext.elmtype = ARR_ELEMTYPE(arrayval); + deconstruct_array(arrayval, + ARR_ELEMTYPE(arrayval), + sacontext.elmlen, sacontext.elmbyval, sacontext.elmalign, + &elem_values, &elem_nulls, &num_elems); + + /* + * Compress out any null elements. We can ignore them since we assume + * all B-tree operators are strict. + */ + num_nonnulls = 0; + for (j = 0; j < num_elems; j++) + { + if (!elem_nulls[j]) + elem_values[num_nonnulls++] = elem_values[j]; + } + if (num_nonnulls == 0) + return true; + + /* If NULL is existed and with ALL flag, report it. */ + if (!saop->useOr && num_nonnulls != num_elems) + return false; + + /* Sort the non-null elements and eliminate any duplicates. + * If there is no support function will return 0 and then + * give up. + */ + if ( 0 == (num_elems = sort_scalararray_elements(&sacontext, right_type, opfamily, collid, + false, elem_values, num_nonnulls))) + return true; + + attinfo = get_varatt_info_internal(sec, ea, (Var *) leftop); + if (unlikely(strat == STRATEGY_NOT_EQUAL)) + { + /* operator <> */ + if (!saop->useOr) + { + for (j = 0; j < num_elems; j++) + { + if (!add_binary_op_clause_info(attinfo, + &elem_values[j], + 1, + idx, + strat, + opfuncid, + expr, + sec, + ccw)) + return false; + ea->addOp = true; + } + } + else + { + /* Treat it as and-expr implicitly if there is only one element. */ + if (num_elems != 1) + return true; /* Don't flatten, forget it. */ + con = makeConst(left_type, + -1, /* typmod -1 is OK for all cases */ + InvalidOid, + sacontext.elmlen, + elem_values[0], + false, + sacontext.elmbyval); + con->location = -1; + newclause = make_opclause(opid, + BOOLOID, + false, + (Expr*)leftop, + (Expr*)con, + InvalidOid, + collid); + if (!add_binary_op_clause_info(attinfo, + &elem_values[0], + 1, + idx, + strat, + opfuncid, + newclause, + sec, + ccw)) + return false; + ea->addOp = true; + ccxt = get_clause_cxt_internal(sec); + ccxt->cct = CCT_REPLACE; + ccxt->idx = idx; + ccxt->cxt = newclause; + ccw->clausecxt = lappend(ccw->clausecxt, ccxt); + } + } + else + { + switch (strat) + { + case BTLessStrategyNumber: + case BTLessEqualStrategyNumber: + if (!saop->useOr) + one_elem = &elem_values[0]; + else + one_elem = &elem_values[num_elems-1]; + break; + case BTEqualStrategyNumber: + /* If the number of elements is not one and postpone them, + * examine them later, because maybe there are multiple + * any-op ScalarArrayOpExpr (eg. x in(1,3) and x in(1,5)), + * see self_contradictory_saop comment. + */ + if (num_elems != 1) + { + if (!saop->useOr) + return false; /* It's contradictory obviously. */ + else + { + SAEqualDatElem *saopele; + if (attinfo->saop.elemnents == NIL) + { + attinfo->saop.opfuncid = opfuncid; + memcpy(&(attinfo->saop.sortcxt), &sacontext, sizeof(SortArrayContext)); + } + saopele = palloc0(sizeof(SAEqualDatElem)); + saopele->idx = idx; + saopele->expr = expr; + saopele->elem_nums = num_elems; + saopele->elem_values = elem_values; + attinfo->saop.elemnents = lappend(attinfo->saop.elemnents, saopele); + } + } + else + { + Assert(num_elems == 1); + one_elem = &elem_values[0]; + } + break; + case BTGreaterEqualStrategyNumber: + case BTGreaterStrategyNumber: + if (!saop->useOr) + one_elem = &elem_values[num_elems-1]; + else + one_elem = &elem_values[0]; + break; + default: + elog(ERROR, "unrecognized StrategyNumber: %d", + strat); + break; + } + if (!(strat == BTEqualStrategyNumber && num_elems != 1)) + { + con = makeConst(left_type, + -1, /* typmod -1 is OK for all cases */ + InvalidOid, + sacontext.elmlen, + *one_elem, + false, + sacontext.elmbyval); + con->location = -1; + newclause = make_opclause(opid, + BOOLOID, + false, + (Expr*)leftop, + (Expr*)con, + InvalidOid, + collid); + if (!add_binary_op_clause_info(attinfo, + one_elem, + 1, + idx, + strat, + opfuncid, + newclause, + sec, + ccw)) + return false; + + ccxt = get_clause_cxt_internal(sec); + ccxt->cct = CCT_REPLACE; + ccxt->idx = idx; + ccxt->cxt = newclause; + ccw->clausecxt = lappend(ccw->clausecxt, ccxt); + } + } + } + else + { + attinfo = get_varatt_info_internal(sec, ea, (Var *) leftop); + if (!add_binary_op_clause_info(attinfo, + &((Const *) rightop)->constvalue, + 1, + idx, + strat, + opfuncid, + expr, + sec, + ccw)) + return false; + } + ea->addOp = true; + + return true; +} + +/* + * row_comparsion_to_nonconstructor + * Convert a row comparison to nonconstructor + * + * We have checked the expression in analysis phase, so + * there is no need to check whether it can satisfy the + * required of B-tree or not. + */ +static int row_comparsion_to_nonconstructor(Expr *rowexpr, Expr **transformed) +{ + Oid left_type, right_type; + Oid varcollid; + ListCell *l_left_expr, + *l_right_expr, + *l_opno, + *l_opfamily, + *l_inputcollid; + ListCell *lc; + List *rcelst, *arglst; + BoolExpr *orclause, *andclause; + RowCompElem *rce; + Expr *newclause; + RowCompareExpr *rcexpr; + FmgrInfo op_func; + bool result, lasteva; + + *transformed = NULL; + rcexpr = (RowCompareExpr *) rowexpr; + Assert(list_length(rcexpr->opnos) != 1); + Assert(rcexpr->rctype != ROWCOMPARE_EQ && rcexpr->rctype != ROWCOMPARE_NE); + + rcelst = NIL; + orclause = (BoolExpr*)make_orclause(NIL); + forfive(l_left_expr, rcexpr->largs, + l_right_expr, rcexpr->rargs, + l_opno, rcexpr->opnos, + l_opfamily, rcexpr->opfamilies, + l_inputcollid, rcexpr->inputcollids) + { + RegProcedure opfuncid; + int strat; + Oid eqid; + Expr *tmp_expr; + Expr *left_expr = (Expr *) lfirst(l_left_expr); + Expr *right_expr = (Expr *) lfirst(l_right_expr); + Oid opno = lfirst_oid(l_opno); + Oid opfamily = lfirst_oid(l_opfamily); + Oid inputcollid = lfirst_oid(l_inputcollid); + + if ((IsA(left_expr, Const) + && ((Const*)left_expr)->constisnull) + || (IsA(right_expr, Const) + && ((Const*)right_expr)->constisnull)) + { + if (list_cell_number(rcexpr->opnos, l_opno) == 0) + return CCT_CONSTF; + lasteva = false; + break; /* The rest of pairs are dominated by the NULL, ignore them. */ + } + if (equal(left_expr, right_expr)) + { + if (!contain_volatile_functions((Node*)left_expr)) + { + if (!IsA(left_expr, Const)) + { + NullTest *nulltest = makeNode(NullTest); + nulltest->arg = left_expr; + nulltest->nulltesttype = IS_NOT_NULL; + nulltest->argisrow = false; + nulltest->location = -1; + rce = palloc0(sizeof(RowCompElem)); + rce->contact = (Expr*)nulltest; + rcelst = lappend(rcelst, rce); + } + /* IF the last operator is > or <, the last sub-expr must be failed. */ + if (l_opno == list_last_cell(rcexpr->opnos)) + { + strat = get_op_opfamily_strategy(opno, opfamily); + if (strat == BTLessStrategyNumber || strat == BTGreaterStrategyNumber) + lasteva = false; + else + lasteva = true; + if (lasteva) + { + arglst = NIL; + foreach(lc, rcelst) + { + arglst = lappend(arglst, ((RowCompElem*)lfirst(lc))->contact); + } + goto lab_built; + } + else + break; + } + continue; + } + else + { + /* Forget this rcexpr, cann't do anything more. */ + *transformed = NULL; + return 0; + } + } + + /* If Const Op Const and are not equal. It's need to evaluate + * the expt is positive or not. + */ + if (IsA(left_expr, Const) && IsA(right_expr, Const)) + { + opfuncid = get_opcode(opno); + fmgr_info(opfuncid, &op_func); + result = DatumGetBool(FunctionCall2Coll(&op_func, + inputcollid, + ((Const *) left_expr)->constvalue, + ((Const *) right_expr)->constvalue)); + if (result) + { + if (list_cell_number(rcexpr->opnos, l_opno) == 0) + return CCT_CONSTT; + else + { + arglst = NIL; + foreach(lc, rcelst) + { + arglst = lappend(arglst, ((RowCompElem*)lfirst(lc))->contact); + } + if (list_length(arglst) == 1) + { + orclause->args = lappend(orclause->args, linitial(arglst)); + } + else if (list_length(arglst) > 1) + { + andclause = (BoolExpr*) make_andclause(arglst); + orclause->args = lappend(orclause->args, andclause); + } + lasteva = true; + break; + } + } + else + { + if (list_cell_number(rcexpr->opnos, l_opno) == 0) + return CCT_CONSTF; + else + { + lasteva = false; + break; + } + } + } + + /* Only accept Var op Const, others cause the rcexpr will be ignored. */ + if (!(IsA(left_expr, Const) || IsA(right_expr, Const))) + { + *transformed = NULL; + return 0; + } + + if (IsA(left_expr, Const)) + { + opno = get_commutator(opno); + tmp_expr = left_expr; + left_expr = right_expr; + right_expr = tmp_expr; + } + + tmp_expr = left_expr; + if (IsA(left_expr, RelabelType)) + { + left_type = ((RelabelType *) left_expr)->resulttype; + varcollid = ((RelabelType *) left_expr)->resultcollid; + left_expr = ((RelabelType *) left_expr)->arg; + } + + if (IsA(left_expr, Var)) + { + left_type = ((Var *) left_expr)->vartype; + varcollid = ((Var *) left_expr)->varcollid; + } + else + { + *transformed = NULL; + return 0; + } + right_type = ((Const *) right_expr)->consttype; + if ( left_type != right_type + || inputcollid != varcollid) /* Don't spend much effort for other types, so skip it */ + + { + *transformed = NULL; + return 0; + } + /* Built sub expr. */ + rce = palloc0(sizeof(RowCompElem)); + rce->opid = opno; + eqid = get_opfamily_member(opfamily, left_type, left_type, BTEqualStrategyNumber); + newclause = make_opclause(eqid, + BOOLOID, + false, + tmp_expr, + right_expr, + InvalidOid, + inputcollid); + rce->contact = newclause; + strat = get_op_opfamily_strategy(opno, opfamily); + if (strat == BTLessEqualStrategyNumber) + rce->stricter_opid = get_opfamily_member(opfamily, left_type, left_type, BTLessStrategyNumber); + else if (strat == BTGreaterEqualStrategyNumber) + rce->stricter_opid = get_opfamily_member(opfamily, left_type, left_type, BTGreaterStrategyNumber); + else + rce->stricter_opid = opno; + + if (l_opno == list_last_cell(rcexpr->opnos)) + opno = rce->opid; + else + opno = rce->stricter_opid; + newclause = make_opclause(opno, + BOOLOID, + false, + tmp_expr, + right_expr, + InvalidOid, + inputcollid); + + arglst = NIL; + foreach(lc, rcelst) + { + arglst = lappend(arglst, ((RowCompElem*)lfirst(lc))->contact); + } + arglst = lappend(arglst, newclause); + rcelst = lappend(rcelst, rce); + +lab_built: + if (list_length(arglst) == 1) + { + orclause->args = lappend(orclause->args, linitial(arglst)); + } + else if (list_length(arglst) > 1) + { + andclause = (BoolExpr*)make_andclause(NIL); + andclause->args = arglst; + orclause->args = lappend(orclause->args, andclause); + } + } + + /* If or-clause args is empty, that mean all of pairs of elements + * are const equal, so check the result of last sub-expr. + */ + if (orclause->args == NIL) + { + if (lasteva) + return CCT_CONSTT; + else + return CCT_CONSTF; + } + + if (list_length(orclause->args) == 1) + { + *transformed = linitial(orclause->args); + } + else + *transformed = (Expr*)orclause; +#ifdef DEBUG_ROW_DECODE + elog(NOTICE, "ROW COMPARSION TO NONCONSTRUCTOR: %s", nodeToString(*transformed)); +#endif + return 0; +} + + +/* + * self_contradictory_recurse + * Extract particulares of implict-AND clauses list + * passed in and check whether it + * is self contradictory or not. During the time of invoking routine + * also do the following jobs pruning and replacing and deleting clause. + * + * Returned value: + * 0: it's Ok. + * 1: is self-contradictory + * Function expect a couple of caluse that mean these clauses are + * implict-AND caluses. Once a pair of clauses or a clause are + * self-contradictory report to the caller immediately. + * A or-clause is a bit tricky, it's need to check every arg, if + * sub-arg is self-contradictory abandon it. After recursion + * if all of them are self contradictory that mean the or-clause + * is self contradictory and report to the caller, else rebuilt the + * or-clause (eg. x > 2 and x < 1 or y >3 in the case x > 2 and x < 1 + * will be eliminated, the output expression is y > 3 ). + * We can push other expressions these have the same semantic level + * with OR-expression down into the OR-expression + * (eg. (x > 2 and x < 1 or y > 3) and y < 2, y < 2 will be + * push down to sub-arg y > 3, this clause is self-contradictory). + */ +static int self_contradictory_recurse(List *node, + ExtractedAttris *ea, + ClauseCxtWapper *ccw, + SelfExamContext *sec) +{ + int ret = 0; + int i; + Expr *inter_node; + VarattInfo *att_elem; + ListCell *lc; + ClauseContext *clausecxt; + NullTest *nt_node; + Var *varnode; + ExtractedAttris ea_info; + ClauseCxtWapper orccw; + + ea_info.addOp = false; + ea_info.min_attr = sec->attrsdc.min_attr; + ea_info.att_lst = NULL; + ea_info.upper_ea = NULL; + ea_info.array_vi = palloc0((sec->attrsdc.max_attr - sec->attrsdc.min_attr + 1) * sizeof(VarattInfo*)); + orccw.clausecxt = NIL; + if (ea) + ea_info.upper_ea = ea; + + if (IsA(node, List)) + { + i = -1; + foreach(lc, (List*) node) + { + inter_node = lfirst(lc); + i++; + if (IsA(inter_node, RestrictInfo)) + { + RestrictInfo *rinfo = (RestrictInfo *) inter_node; + if (rinfo->pseudoconstant) + continue; + inter_node = rinfo->clause; + if (rinfo->fromec) + { + clausecxt = get_clause_cxt_internal(sec); + clausecxt->cct = CCT_DEL; + clausecxt->idx = i; + ccw->clausecxt = lappend(ccw->clausecxt, clausecxt); + } + } + if (IsA(inter_node, Var)) + { + varnode = (Var*) inter_node; + if (varnode->vartype == BOOLOID) + { + att_elem = get_varatt_info_internal(sec, &ea_info, varnode); + att_elem->bool_test |= BT_MARK | BT_IS_TRUE; + ea_info.addOp = true; + } + } + else if (IsA(inter_node, BoolExpr)) + { + /* + * It's need to postpone the or-expr, then the same + * level simple expr can be push down. + */ + BoolExpr *boolnode; + boolnode = (BoolExpr*) inter_node; + if (boolnode->boolop == NOT_EXPR + && list_length(boolnode->args) == 1 + && IsA(linitial(boolnode->args), Var)) + { + varnode = (Var*) linitial(boolnode->args); + if (varnode->vartype == BOOLOID) + { + att_elem = get_varatt_info_internal(sec, &ea_info, varnode); + att_elem->bool_test |= BT_MARK | BT_IS_FALSE; + ea_info.addOp = true; + } + } + else + { + clausecxt = get_clause_cxt_internal(sec); + clausecxt->idx = i; + clausecxt->cxt = inter_node; + clausecxt->origin = inter_node; + orccw.clausecxt = lappend(orccw.clausecxt, clausecxt); + } + } + else if (IsA(inter_node, NullTest)) + { + nt_node = (NullTest *)inter_node; + if (!nt_node->argisrow && nt_node->arg) + { + if(!deconstruct_nulltest_attr((Node *)nt_node->arg, + nt_node->nulltesttype, sec, &ea_info)) + EXAMINE_ABORT(1); + ea_info.addOp = true; + } + } + else if (IsA(inter_node, BooleanTest)) + { + Node *basenode; + + BooleanTest *btest = (BooleanTest *) inter_node; + if (IsA(btest->arg, RelabelType)) + basenode = (Node *) ((RelabelType *) btest->arg)->arg; + else + basenode = (Node *)btest->arg; + if (basenode && IsA(basenode, Var)) + { + att_elem = get_varatt_info_internal(sec, &ea_info, (Var *) btest->arg); + switch (btest->booltesttype) + { + case IS_TRUE: + att_elem->bool_test |= BT_MARK | BT_IS_TRUE; + break; + case IS_NOT_TRUE: + att_elem->bool_test |= BT_MARK | BT_IS_NOT_TRUE; + break; + case IS_FALSE: + att_elem->bool_test |= BT_MARK | BT_IS_FALSE; + break; + case IS_NOT_FALSE: + att_elem->bool_test |= BT_MARK | BT_IS_NOT_FALSE; + break; + case IS_UNKNOWN: + att_elem->bool_test |= BT_MARK | BT_IS_UNKNOWN; + break; + case IS_NOT_UNKNOWN: + att_elem->bool_test |= BT_MARK | BT_IS_NOT_UNKNOWN; + break; + default: + elog(ERROR, "unrecognized booltesttype: %d", + (int) btest->booltesttype); + break; + } + ea_info.addOp = true; + } + } + else if (IsA(inter_node, OpExpr)) + { + OpExpr *opexpr; + opexpr = (OpExpr *) inter_node; + if (list_length(opexpr->args) != 2) + continue; + if (!extract_binary_op_clause_info(opexpr, + linitial(opexpr->args), + lsecond(opexpr->args), + i, + opexpr->opno, + opexpr->inputcollid, + sec, + &ea_info, + ccw)) + EXAMINE_ABORT(1); + } + else if (IsA(inter_node, ScalarArrayOpExpr)) + { + ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) inter_node; + if (list_length(saop->args) != 2) + continue; + if (!extract_binary_op_clause_info(saop, + linitial(saop->args), + lsecond(saop->args), + i, + saop->opno, + saop->inputcollid, + sec, + &ea_info, + ccw)) + EXAMINE_ABORT(1); + } + else if (IsA(inter_node, RowCompareExpr)) + { + Expr *trans; + int result; + result = row_comparsion_to_nonconstructor(inter_node, &trans); + if (result == 0 && trans != NULL) + { + if (is_orclause(trans)) + { + clausecxt = get_clause_cxt_internal(sec); + clausecxt->idx = i; + clausecxt->cxt = trans; + clausecxt->origin = inter_node; + orccw.clausecxt = lappend(orccw.clausecxt, clausecxt); + } + else if (is_andclause(trans)) + { + /* Must be NullTest expr eg. row(a,b) >= row(a,b) */ + ListCell *lnt; + foreach(lnt, ((BoolExpr*)trans)->args) + { + Assert(IsA(lfirst(lnt), NullTest)); + nt_node = lfirst(lnt); + if(!deconstruct_nulltest_attr((Node *)nt_node->arg, + nt_node->nulltesttype, + sec, + &ea_info)) + EXAMINE_ABORT(1); + } + ea_info.addOp = true; + } + else if (IsA(trans, OpExpr)) + { + if (!extract_binary_op_clause_info(trans, + linitial(((OpExpr*)trans)->args), + lsecond(((OpExpr*)trans)->args), + i, + ((OpExpr*)trans)->opno, + ((OpExpr*)trans)->inputcollid, + sec, + &ea_info, + ccw)) + EXAMINE_ABORT(1); + clausecxt = get_clause_cxt_internal(sec); + clausecxt->cct = CCT_REPLACE; + clausecxt->idx = i; + clausecxt->cxt = trans; + ccw->clausecxt = lappend(ccw->clausecxt, clausecxt); + } + else if (IsA(trans, NullTest)) + { + nt_node = (NullTest *)trans; + if(!deconstruct_nulltest_attr((Node *)nt_node->arg, + nt_node->nulltesttype, + sec, + &ea_info)) + EXAMINE_ABORT(1); + clausecxt = get_clause_cxt_internal(sec); + clausecxt->cct = CCT_REPLACE; + clausecxt->idx = i; + clausecxt->cxt = trans; + ccw->clausecxt = lappend(ccw->clausecxt, clausecxt); + } + } + else if (result == CCT_CONSTT) + { + /* (eg. row(1,2,3) >= row(1,2,3)) */ + clausecxt = get_clause_cxt_internal(sec); + clausecxt->cct = CCT_CONSTT; + clausecxt->idx = i; + ccw->clausecxt = lappend(ccw->clausecxt, clausecxt); + } + else if (result == CCT_CONSTF) + EXAMINE_ABORT(1); + } + } + } + else + { + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(node)); + } + + /* All base particulars had been collected, it's time to examine. */ + if(self_contradictory_check(sec, &ea_info, ccw)) + EXAMINE_ABORT(1); + + /* Examine the or-clause that was postponed here because every + * OpExpr at same semantic level can be push down ( eg. x > 10 and y > 12 and + * ( x < 9 or y < 11) ), this case can be transform to (x > 10 and y > 12 and + * x <9) or (x > 10 and y > 12 and y < 11). + */ + foreach(lc, orccw.clausecxt) + { + ListCell *arg, *lc1; + List *arglst, *newraglst; + ClauseCxtWapper boolccw; + ClauseContext *ccxt; + BoolExpr *boolnode; + uint8 *delidx; + uint8 orchanged; + clausecxt = lfirst(lc); + boolnode = (BoolExpr *) clausecxt->cxt; + + switch (boolnode->boolop) + { + case AND_EXPR: + delidx = palloc0((list_length(boolnode->args)+1) * sizeof(uint8)); + delidx++; + boolccw.clausecxt = NIL; + ret = self_contradictory_recurse(boolnode->args, ea, &boolccw, sec); + if (ret > 0) + EXAMINE_ABORT(1); + + foreach(lc1, boolccw.clausecxt) + { + ccxt = lfirst(lc1); + delidx[-1] = 0x01; + switch (ccxt->cct) + { + case CCT_DEL: + case CCT_CONSTT: + delidx[ccxt->idx] = 0x01; + break; + case CCT_REPLACE: + arg = list_nth_cell(boolnode->args, ccxt->idx); + /* It's need to be flatten if a and-expr, so flag it. */ + if (is_andclause(ccxt->cxt)) + delidx[ccxt->idx] = 0x02; + lfirst(arg) = ccxt->cxt; + break; + default: + break; + } + free_clause_cxt_internal(sec, ccxt); + } + /* OK, fix the args. */ + if (delidx[-1]) + { + i = 0; + arglst = NIL; + newraglst = NIL; + foreach(lc1, boolnode->args) + { + if (delidx[i] == 0x00) + arglst = lappend(arglst, lfirst(lc1)); + else if (delidx[i] == 0x02) + newraglst = list_concat(newraglst, ((BoolExpr*)lfirst(lc1))->args); + i++; + } + boolnode->args = list_concat(arglst,newraglst); + ccxt = get_clause_cxt_internal(sec); + ccxt->idx = clausecxt->idx; + /* If the reset of args is empty, that mean the and-expr + * is const true, because only weaker restrictive expr will be + * delete and the args must be not empty. + */ + if (boolnode->args == NIL) + ccxt->cct = CCT_CONSTT; + else + { + ccxt->cct = CCT_REPLACE; + if (list_length(boolnode->args) == 1) + ccxt->cxt = linitial(boolnode->args); + else + ccxt->cxt = boolnode; + } + ccw->clausecxt = lappend(ccw->clausecxt, ccxt); + } + break; + case OR_EXPR: + newraglst = NIL; + orchanged = 0x0; + foreach(arg, boolnode->args) + { + boolccw.clausecxt = NIL; + arglst = list_make1(lfirst(arg)); + if (self_contradictory_recurse(arglst, &ea_info, &boolccw, sec)) + { + orchanged = 0x01; + ret++; + } + else + { + Assert(list_length(boolccw.clausecxt) <= 1); + if (list_length(boolccw.clausecxt) == 1) + { + orchanged = 0x01; + ccxt = linitial(boolccw.clausecxt); + Assert(ccxt->idx == 0); + if (ccxt->cct == CCT_REPLACE) + lfirst(arg) = ccxt->cxt; + else if (ccxt->cct == CCT_CONSTT) + { + /* + * Once found a const true sub-arg, skip checking any others, + * the or-expr must be const true. + */ + orchanged = 0x02; + free_clause_cxt_internal(sec, ccxt); + list_free(arglst); + break; + } + free_clause_cxt_internal(sec, ccxt); + } + if (is_orclause(lfirst(arg))) + newraglst = list_concat(newraglst, ((const BoolExpr *) lfirst(arg))->args); + else + newraglst = lappend(newraglst, lfirst(arg)); + } + list_free(arglst); + } + if (ret == list_length(boolnode->args)) + EXAMINE_ABORT(1); +#ifndef DEBUG_ROW_DECODE + else if (orchanged == 0x01) + { + /* + * RowCompareExpr has been coverted to nonconstructor (OR-expression), + * if the number of the or-expr's args is more than + * one, it do not has benifit to subsequent planning, so + * do not do anything and keep itself. + */ + + if ((IsA(clausecxt->origin, RowCompareExpr) + && list_length(newraglst) != 1)) + ; + else +#else + else if (orchanged == 0x01 || (IsA(clausecxt->origin, RowCompareExpr) && orchanged != 0x02)) + { +#endif + { + ccxt = get_clause_cxt_internal(sec); + ccxt->cct = CCT_REPLACE; + ccxt->idx = clausecxt->idx; + if (list_length(newraglst) == 1) + ccxt->cxt = linitial(newraglst); + else + { + boolnode->args = newraglst; + ccxt->cxt = boolnode; + } + ccw->clausecxt = lappend(ccw->clausecxt, ccxt); + } + } + else if (orchanged == 0x02) + { + ccxt = get_clause_cxt_internal(sec); + ccxt->cct = CCT_CONSTT; + ccxt->idx = clausecxt->idx; + ccw->clausecxt = lappend(ccw->clausecxt, ccxt); + } + /* If or-expr is not changed, do nothing more. */ + ret = 0; + break; + case NOT_EXPR: + break; + default: + elog(ERROR, "unrecognized boolop: %d", (int) boolnode->boolop); + break; + } + free_clause_cxt_internal(sec, clausecxt); + } + +finished: + if (ea_info.att_lst != NULL && ea_info.att_lst == ea_info.last) + { + free_attinfo_cxt_internal(sec, ea_info.att_lst); + } + else if (ea_info.att_lst != NULL) + { + ea_info.last->next = sec->vifree; + sec->vifree = ea_info.att_lst; + } + pfree(ea_info.array_vi); + return ret; +} + +/* + * examine_self_contradictory_rels + * + * Examine the base rel's baserestrictinfo to confirm that it's a + * self-contradictory rel or not. + * Mark the rel as a dummy rel if it's a self-contradictory rel, + * this is a huge win for subsequent planning. + * Note: deduct will be set true if caller need to process EC. + */ +static void examine_self_contradictory_rels(PlannerInfo *root, RelOptInfo *rel, bool deduct) +{ + int n; + RestrictInfo *restrictinfo; + ClauseCxtWapper ccwapper; + SelfExamContext sec; + + MemSet(&sec, 0, sizeof(SelfExamContext)); + /* + * Initialize sopcinfo with opclass info. Extract all bt-am opclass + * particulars and sort by opcintype. + */ + if (sec.sopcinfo.n_members == 0) + { + if (!default_opclassinfo_for_am(BTREE_AM_OID, extract_opc_info, &sec.sopcinfo)) + elog(ERROR, "non default opclass records for AM %d", (int) BTREE_AM_OID); + qsort(sec.sopcinfo.opcies, sec.sopcinfo.n_members, sizeof(OpcInfoElement*), opc_element_cmp); + } + + /* For every base rel reuse the AttrsDetailsCache memory. */ + if (!sec.attrsdc.atop_array) + { + sec.attrsdc.atop_array = palloc0((rel->max_attr - rel->min_attr + 1) * sizeof(AttrOpFamily*)); + sec.attrsdc.atop_set = palloc0(sizeof(AttrOpFamilySet)); + } + else + { + AttrOpFamilySet *atfs, *atfs_tmp; + atfs = sec.attrsdc.atop_set->next; + n = rel->max_attr - rel->min_attr + 1; + /* If the number of current rel attri greater than previous rel and reset it. */ + if (n > sec.attrsdc.max_attr - sec.attrsdc.min_attr + 1) + { + pfree(sec.attrsdc.atop_array); + sec.attrsdc.atop_array = palloc0(n * sizeof(AttrOpFamily*)); + } + else + MemSet(sec.attrsdc.atop_array, 0, n * sizeof(AttrOpFamily*)); + while(atfs) + { + atfs_tmp = atfs->next; + pfree(atfs); + atfs = atfs_tmp; + } + sec.attrsdc.len_set = 0; + } + sec.attrsdc.min_attr = rel->min_attr; + sec.attrsdc.max_attr = rel->max_attr; + + ccwapper.clausecxt = NIL; + if (!self_contradictory_recurse(rel->baserestrictinfo, NULL, &ccwapper, &sec)) + { + /* Check any pruned-clause and rebuilt it. */ + List *arglst; + ListCell *br, *lc; + ListCell *arg; + BoolExpr *expression; + RestrictInfo *or_rinfo; + ClauseContext *ccxt; + bool *delidx = NULL; + if (list_length(ccwapper.clausecxt) > 0) + { + delidx = palloc0((list_length(rel->baserestrictinfo) + 1) * sizeof(bool)); + delidx++; + } + foreach(lc, ccwapper.clausecxt) + { + ccxt = lfirst(lc); + br = list_nth_cell(rel->baserestrictinfo, ccxt->idx); + restrictinfo = lfirst(br); + switch (ccxt->cct) + { + case CCT_DEL: + /* Be carefull if it's keeped from EC, + * count baseri_nums because it will be delete below + * and can't count it in phase1. + */ + if (deduct && restrictinfo->fromec) + rel->baseri_nums++; + delidx[-1] = delidx[ccxt->idx] = true; + break; + case CCT_CONSTT: + delidx[-1] = delidx[ccxt->idx] = true; + break; + case CCT_REPLACE: + Assert(!IsA(ccxt->cxt, List)); + arglst = NIL; + if (is_andclause(ccxt->cxt)) + { + expression = ccxt->cxt; + arglst = list_concat(arglst, expression->args); + } + else + arglst = lappend(arglst, ccxt->cxt); + delidx[-1] = delidx[ccxt->idx] = true; + foreach(arg, arglst) + { + or_rinfo = make_restrictinfo(root, + (Expr*) lfirst(arg), + restrictinfo->is_pushed_down, + restrictinfo->has_clone, + restrictinfo->is_clone, + restrictinfo->pseudoconstant, + restrictinfo->security_level, + NULL, + restrictinfo->incompatible_relids, + restrictinfo->outer_relids); + if (deduct) + { + check_mergejoinable(or_rinfo); + if (or_rinfo->mergeopfamilies) + { + if (restrictinfo->allow_equivalence) + { + if (process_equivalence(root, &or_rinfo, restrictinfo->jdomain)) + { + rel->baseri_nums++; + continue; + } + if (or_rinfo->mergeopfamilies) + initialize_mergeclause_eclasses(root, or_rinfo); + } + else + initialize_mergeclause_eclasses(root, or_rinfo); + } + } + rel->baserestrictinfo = lappend(rel->baserestrictinfo, or_rinfo); + } + break; + default: + break; + } + free_clause_cxt_internal(&sec, ccxt); + } + if (delidx && delidx[-1]) + { + List *rlist = NIL; + int i = 0; + foreach(lc, rel->baserestrictinfo) + { + if (!delidx[i++]) + rlist = lappend(rlist, lfirst(lc)); + } + rel->baserestrictinfo = rlist; + } + } + else + mark_dummy_rel(rel); +} + +/* + * examine_self_contradictory_rels_phase1 + * + * Some clause maybe be changed and the reult may match the + * EC mechanism, so it's need to add it into the EC. + */ +void examine_self_contradictory_rels_phase1(PlannerInfo *root) +{ + int rti; + RestrictInfo *rinfo; + + for (rti = 1; rti < root->simple_rel_array_size; rti++) + { + RelOptInfo *rel = root->simple_rel_array[rti]; + /* There may be empty slots corresponding to non-baserel RTEs */ + if (rel == NULL) + continue; + /* Ignore any "otherrels". */ + if (rel->reloptkind != RELOPT_BASEREL) + continue; + if (rel->baserestrictinfo == NIL) + continue; + if (list_length(rel->baserestrictinfo) == 1) + { + rinfo = linitial(rel->baserestrictinfo); + if (rinfo->fromec) + { + rel->baseri_nums = 1; + rel->baserestrictinfo = NIL; + continue; + } + } + examine_self_contradictory_rels(root, rel, true); + if (!is_dummy_rel(rel)) + rel->baseri_nums += list_length(rel->baserestrictinfo); + } +} + +/* + * examine_self_contradictory_rels_phase1 + * + * Only care about rel's baserestrictinfo that had added a EC clause. + */ +void examine_self_contradictory_rels_phase2(PlannerInfo *root) +{ + int rti; + for (rti = 1; rti < root->simple_rel_array_size; rti++) + { + RelOptInfo *rel = root->simple_rel_array[rti]; + /* There may be empty slots corresponding to non-baserel RTEs */ + if (rel == NULL) + continue; + /* Ignore any "otherrels". */ + if (rel->reloptkind != RELOPT_BASEREL) + continue; + if (rel->baserestrictinfo == NIL) + continue; + if (list_length(rel->baserestrictinfo) == 1) + continue; + if (is_dummy_rel(rel)) + continue; + if (rel->baseri_nums != list_length(rel->baserestrictinfo)) + examine_self_contradictory_rels(root, rel, false); + } +} diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 903c397d40..72445ba859 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -127,7 +127,7 @@ static void distribute_qual_to_rels(PlannerInfo *root, Node *clause, List **postponed_oj_qual_list); static bool check_redundant_nullability_qual(PlannerInfo *root, Node *clause); static Relids get_join_domain_min_rels(PlannerInfo *root, Relids domain_relids); -static void check_mergejoinable(RestrictInfo *restrictinfo); +void check_mergejoinable(RestrictInfo *restrictinfo); static void check_hashjoinable(RestrictInfo *restrictinfo); static void check_memoizable(RestrictInfo *restrictinfo); @@ -2534,7 +2534,8 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause, relids, incompatible_relids, outerjoin_nonnullable); - + restrictinfo->jdomain = jtitem->jdomain; + restrictinfo->allow_equivalence = allow_equivalence; /* * If it's a join clause, add vars used in the clause to targetlists of * their relations, so that they will be emitted by the plan nodes that @@ -2616,7 +2617,16 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause, if (maybe_equivalence) { if (process_equivalence(root, &restrictinfo, jtitem->jdomain)) + { + /* Distribute to base rel. */ + int relid; + if (bms_get_singleton_member(restrictinfo->required_relids, &relid)) + { + restrictinfo->fromec = true; + distribute_restrictinfo_to_rels(root, restrictinfo); + } return; + } /* EC rejected it, so set left_ec/right_ec the hard way ... */ if (restrictinfo->mergeopfamilies) /* EC might have changed this */ initialize_mergeclause_eclasses(root, restrictinfo); @@ -3519,7 +3529,7 @@ match_foreign_keys_to_quals(PlannerInfo *root) * the operator is a mergejoinable operator. The arguments can be * anything --- as long as there are no volatile functions in them. */ -static void +void check_mergejoinable(RestrictInfo *restrictinfo) { Expr *clause = restrictinfo->clause; diff --git a/src/backend/optimizer/plan/meson.build b/src/backend/optimizer/plan/meson.build index c3b191cdf8..2f8c32fe5d 100644 --- a/src/backend/optimizer/plan/meson.build +++ b/src/backend/optimizer/plan/meson.build @@ -9,4 +9,5 @@ backend_sources += files( 'planner.c', 'setrefs.c', 'subselect.c', + 'contradictory.c', ) diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index e17d31a5c3..34cc104f1b 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -187,6 +187,8 @@ query_planner(PlannerInfo *root, joinlist = deconstruct_jointree(root); + examine_self_contradictory_rels_phase1(root); + /* * Reconsider any postponed outer-join quals now that we have built up * equivalence classes. (This could result in further additions or @@ -201,6 +203,8 @@ query_planner(PlannerInfo *root, */ generate_base_implied_equalities(root); + examine_self_contradictory_rels_phase2(root); + /* * We have completed merging equivalence sets, so it's now possible to * generate pathkeys in canonical form; so compute query_pathkeys and diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index a85dc0d891..bdebae5dbb 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -1181,6 +1181,45 @@ get_language_name(Oid langoid, bool missing_ok) /* ---------- OPCLASS CACHE ---------- */ +/* + * default_opclassinfo_for_am + * + * Extract all records these opcdefault is true with specified AM oid. + */ +bool +default_opclassinfo_for_am(Oid amoid, SortOpclassCallback callback, void *infocxt) +{ + bool result = false; + CatCList *opclist; + int i; + + /* + * Search through all the specified AM's opclass details to + * grab records which opcintype is true. + */ + opclist = SearchSysCacheList1(CLAAMNAMENSP, ObjectIdGetDatum(amoid)); + + for (i = 0; i < opclist->n_members; i++) + { + HeapTuple classtup = &opclist->members[i]->tuple; + Form_pg_opclass classform = (Form_pg_opclass) GETSTRUCT(classtup); + + if (classform->opcdefault) + { + callback(opclist->n_members, + classform->oid, + classform->opcfamily, + classform->opcintype, + infocxt); + } + } + if (opclist->n_members > 0) + result = true; + ReleaseCatCacheList(opclist); + + return result; +} + /* * get_opclass_family * diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index add0f9e45f..36d7e2bc21 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -982,6 +982,7 @@ typedef struct RelOptInfo * used by various scans and joins: */ /* RestrictInfo structures (if base rel) */ + uint32 baseri_nums; List *baserestrictinfo; /* cost of evaluating the above */ QualCost baserestrictcost; @@ -2573,6 +2574,12 @@ typedef struct RestrictInfo /* the represented clause of WHERE or JOIN */ Expr *clause; + JoinDomain *jdomain pg_node_attr(copy_as_scalar, equal_ignore, read_write_ignore); + + bool fromec; + + bool allow_equivalence; + /* true if clause was pushed down in level */ bool is_pushed_down; diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index 93137261e4..943e279bd8 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -66,6 +66,8 @@ extern Limit *make_limit(Plan *lefttree, Node *limitOffset, Node *limitCount, */ extern PGDLLIMPORT int from_collapse_limit; extern PGDLLIMPORT int join_collapse_limit; +extern void examine_self_contradictory_rels_phase1(PlannerInfo *root); +extern void examine_self_contradictory_rels_phase2(PlannerInfo *root); extern void add_base_rels_to_query(PlannerInfo *root, Node *jtnode); extern void add_other_rels_to_query(PlannerInfo *root); diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index 20446f6f83..17faf447ef 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -61,6 +61,8 @@ typedef struct AttStatsSlot void *numbers_arr; /* palloc'd numbers array, if any */ } AttStatsSlot; +typedef void (*SortOpclassCallback) (int nums, Oid opcid, Oid opcfamily, Oid opcintype, void *infocxt); + /* Hook for plugins to get control in get_attavgwidth() */ typedef int32 (*get_attavgwidth_hook_type) (Oid relid, AttrNumber attnum); extern PGDLLIMPORT get_attavgwidth_hook_type get_attavgwidth_hook; @@ -103,6 +105,7 @@ extern Oid get_constraint_index(Oid conoid); extern char get_constraint_type(Oid conoid); extern char *get_language_name(Oid langoid, bool missing_ok); +extern bool default_opclassinfo_for_am(Oid amoid, SortOpclassCallback callback, void *infocxt); extern Oid get_opclass_family(Oid opclass); extern Oid get_opclass_input_type(Oid opclass); extern bool get_opclass_opfamily_and_input_type(Oid opclass, -- 2.43.0.windows.1