any_to_or.diff.txt

text/plain

Filename: any_to_or.diff.txt
Type: text/plain
Part: 2
Message: Re: POC, WIP: OR-clause support for indexes
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);