diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 67921a08262..3cddc0a4082 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -50,6 +50,8 @@ #include "port/pg_bitutils.h" #include "rewrite/rewriteManip.h" #include "utils/lsyscache.h" +#include "parser/parse_expr.h" +#include "utils/array.h" /* Bitmask flags for pushdown_safety_info.unsafeFlags */ @@ -759,6 +761,179 @@ set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel, rel->consider_parallel = true; } +static List* +research_or_list(Expr *qual, List *or_list, RestrictInfo *sub_rinfo) +{ + List *elem_exprs = NIL; + + /* Check: it is an expr of the form 'F(x) oper ConstExpr' */ + if (!IsA(qual, ScalarArrayOpExpr)) + { + /* Again, it's not the expr we can transform */ + or_list = lappend(or_list, qual); + } + else + { + ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) qual; + Expr *rightop = (Expr *) lsecond(saop->args); + ListCell *lc1; + + if (sub_rinfo && !op_mergejoinable(((OpExpr *) sub_rinfo->clause)->opno, exprType(get_leftop(sub_rinfo->clause)))) + { + /* And again, filter out non-equality operators */ + or_list = lappend(or_list, (void *) qual); + } + else if (rightop && IsA(rightop, Const)) + { + ArrayType *arrval; + int16 elemlen; + bool elembyval; + char elemalign; + Datum *elem_values; + bool *elem_nulls; + int num_elems, + i; + Const *arr = (Const *) rightop; + + arrval = DatumGetArrayTypeP(arr->constvalue); + get_typlenbyvalalign(ARR_ELEMTYPE(arrval), + &elemlen, &elembyval, &elemalign); + deconstruct_array(arrval, + ARR_ELEMTYPE(arrval), + elemlen, elembyval, elemalign, + &elem_values, &elem_nulls, + &num_elems); + + for (i = 0; i < num_elems; i++) + { + Const *elem_expr; + + /* + * A null array element must lead to a null comparison result, + * since saop_op is known strict. We can ignore it in the + * useOr case, but otherwise it implies self-contradiction. + */ + if (elem_nulls[i]) + { + or_list = lappend(or_list, (void *) qual); + elem_exprs = NIL; + break; + } + + elem_expr = makeConst(ARR_ELEMTYPE(arrval), -1, + arr->constcollid, elemlen, + elem_values[i], false, elembyval); + elem_exprs = lappend(elem_exprs, elem_expr); + } + } + else if (rightop && IsA(rightop, ArrayExpr) && !((ArrayExpr *) rightop)->multidims) + { + ArrayExpr *arrexpr = (ArrayExpr *)get_rightop(rightop); + elem_exprs = arrexpr->elements; + } + else + or_list = lappend(or_list, qual); + + if (elem_exprs) + foreach(lc1, elem_exprs) + { + Expr *elem_clause; + + elem_clause = make_opclause(((ScalarArrayOpExpr*) qual)->opno, BOOLOID, false, + (Expr *) linitial(((ScalarArrayOpExpr*)qual)->args), lfirst(lc1), + InvalidOid, ((ScalarArrayOpExpr*)qual)->inputcollid); + or_list = lappend(or_list, (void*) elem_clause); + } + } + return or_list; +} + +static List * +get_baserestrictinfo(PlannerInfo *root, List *baserestrictinfo) +{ + ListCell *lc; + List *modified_rinfo = NIL; + bool or_transformation = false; + + if (!enable_or_transformation) + return NULL; + + foreach(lc, baserestrictinfo) + { + RestrictInfo *rinfo_base = lfirst_node(RestrictInfo, lc); + RestrictInfo *rinfo; + List *or_list = NIL; + + ListCell *lc_eargs, + *lc_rargs; + + if (!IsA(rinfo_base->clause, BoolExpr) && !IsA(rinfo_base->clause, ScalarArrayOpExpr)) + { + /* Add a clause without changes */ + modified_rinfo = lappend(modified_rinfo, rinfo_base); + continue; + } + + if (IsA(rinfo_base->clause, BoolExpr) && is_orclause(rinfo_base->clause)) + forboth(lc_eargs, ((BoolExpr *) rinfo_base->clause)->args, + lc_rargs, ((BoolExpr *) rinfo_base->orclause)->args) + { + Expr *orqual = (Expr *) lfirst(lc_eargs); + RestrictInfo *sub_rinfo = lfirst_node(RestrictInfo, lc_rargs); + + if (!IsA(orqual, OpExpr) || + !(bms_is_empty(sub_rinfo->left_relids) ^ + bms_is_empty(sub_rinfo->right_relids)) || + contain_volatile_functions((Node *) orqual)) + { + /* Again, it's not the expr we can transform */ + or_list = lappend(or_list, (void *) orqual); + continue; + } + + or_list = research_or_list(orqual, or_list, sub_rinfo); + } + else if (IsA(rinfo_base->clause, ScalarArrayOpExpr)) + { + Expr *orqual = rinfo_base->clause; + + or_list = research_or_list(orqual, or_list, NULL); + } + else + { + or_list = lappend(or_list, (void*)rinfo_base->clause); + } + + + rinfo = make_restrictinfo(root, + list_length(or_list) > 1 ? makeBoolExpr(OR_EXPR, or_list, -1): + (Expr *) linitial(or_list), + rinfo_base->is_pushed_down, + rinfo_base->has_clone, + rinfo_base->is_clone, + rinfo_base->pseudoconstant, + rinfo_base->security_level, + rinfo_base->required_relids, + rinfo_base->incompatible_relids, + rinfo_base->outer_relids); + rinfo->eval_cost=rinfo_base->eval_cost; + rinfo->norm_selec=rinfo_base->norm_selec; + rinfo->outer_selec=rinfo_base->outer_selec; + rinfo->left_bucketsize=rinfo_base->left_bucketsize; + rinfo->right_bucketsize=rinfo_base->right_bucketsize; + rinfo->left_mcvfreq=rinfo_base->left_mcvfreq; + rinfo->right_mcvfreq=rinfo_base->right_mcvfreq; + modified_rinfo = lappend(modified_rinfo, rinfo); + or_transformation = true; + } + + if(or_transformation) + return modified_rinfo; + + return baserestrictinfo; +} + + /* * set_plain_rel_pathlist * Build access paths for a plain relation (no subquery, no inheritance) @@ -767,6 +942,7 @@ static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) { Relids required_outer; + List *baserestrict_base = copyObject(rel->baserestrictinfo); /* * We don't support pushing join clauses into the quals of a seqscan, but @@ -778,6 +954,8 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) /* Consider sequential scan */ add_path(rel, create_seqscan_path(root, rel, required_outer, 0)); + rel->baserestrictinfo = get_baserestrictinfo(root, rel->baserestrictinfo); + /* If appropriate, consider parallel sequential scan */ if (rel->consider_parallel && required_outer == NULL) create_plain_partial_paths(root, rel);