0002-OR-to-ANY-in-index.diff

text/x-patch

Filename: 0002-OR-to-ANY-in-index.diff
Type: text/x-patch
Part: 1
Message: Re: POC, WIP: OR-clause support for indexes

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
Series: patch 0002
File+
src/backend/optimizer/path/allpaths.c 429 0
src/backend/optimizer/path/indxpath.c 0 394
src/backend/optimizer/util/relnode.c 91 0
src/include/optimizer/pathnode.h 2 0
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 67921a08262..e0d83672756 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -50,6 +50,12 @@
 #include "port/pg_bitutils.h"
 #include "rewrite/rewriteManip.h"
 #include "utils/lsyscache.h"
+#include "parser/parse_coerce.h"
+#include "common/hashfn.h"
+#include "catalog/pg_operator.h"
+#include "nodes/queryjumble.h"
+#include "utils/syscache.h"
+#include "catalog/namespace.h"
 
 
 /* Bitmask flags for pushdown_safety_info.unsafeFlags */
@@ -165,6 +171,390 @@ static void remove_unused_subquery_outputs(Query *subquery, RelOptInfo *rel,
 										   Bitmapset *extra_used_attrs);
 
 
+bool		enable_or_transformation = true;
+typedef struct OrClauseGroupKey
+{
+	Expr   *expr; /* Pointer to the expression tree which has been a source for
+					the hashkey value */
+	Oid		opno;
+	Oid		exprtype;
+} OrClauseGroupKey;
+
+typedef struct OrClauseGroupEntry
+{
+	OrClauseGroupKey key;
+	Node		   *node;
+	List		   *consts;
+	Oid				collation;
+	RestrictInfo   *rinfo;
+	List 		   *exprs;
+} OrClauseGroupEntry;
+
+/*
+ * Hash function that's compatible with guc_name_compare
+ */
+static uint32
+orclause_hash(const void *data, Size keysize)
+{
+	OrClauseGroupKey   *key = (OrClauseGroupKey *) data;
+	uint64				hash;
+
+	(void) JumbleExpr(key->expr, &hash);
+	hash = ((uint64) key->opno + (uint64) key->exprtype) % UINT64_MAX;
+	return hash;
+}
+
+static void *
+orclause_keycopy(void *dest, const void *src, Size keysize)
+{
+	OrClauseGroupKey *src_key = (OrClauseGroupKey *) src;
+	OrClauseGroupKey *dst_key = (OrClauseGroupKey *) dest;
+
+	Assert(sizeof(OrClauseGroupKey) == keysize);
+
+	dst_key->expr = src_key->expr;
+	dst_key->opno = src_key->opno;
+	dst_key->exprtype = src_key->exprtype;
+	return dst_key;
+}
+
+/*
+ * Dynahash match function to use in guc_hashtab
+ */
+static int
+orclause_match(const void *data1, const void *data2, Size keysize)
+{
+	OrClauseGroupKey   *key1 = (OrClauseGroupKey *) data1;
+	OrClauseGroupKey   *key2 = (OrClauseGroupKey *) data2;
+
+	Assert(sizeof(OrClauseGroupKey) == keysize);
+
+	if (key1->opno == key2->opno && key1->exprtype == key2->exprtype &&
+		equal(key1->expr, key2->expr))
+		return 0;
+
+	return 1;
+}
+
+/*
+ * Pass through baserestrictinfo clauses and try to convert OR clauses into IN
+ * Return a modified clause list or just the same baserestrictinfo, if no
+ * changes have made.
+ * XXX: do not change source list of clauses at all.
+ */
+static List *
+transform_ors(PlannerInfo *root, List *baserestrictinfo, bool *something_changed)
+{
+	ListCell   *lc;
+	List	   *modified_rinfo = NIL;
+
+	/*
+	 * Complexity of a clause could be arbitrarily sophisticated. Here, we will
+	 * look up only on the top level of clause list.
+	 * XXX: It is substantiated? Could we change something here?
+	 */
+	foreach (lc, baserestrictinfo)
+	{
+		RestrictInfo   *rinfo = lfirst_node(RestrictInfo, lc);
+		RestrictInfo   *rinfo_base = copyObject(rinfo);
+		List		   *or_list = NIL;
+		ListCell	   *lc_eargs,
+					   *lc_rargs;
+		bool			change_apply = false;
+		HASHCTL			info;
+		HTAB 		   *or_group_htab = NULL;
+		int 			len_ors;
+		HASH_SEQ_STATUS		hash_seq;
+		OrClauseGroupEntry *gentry;
+
+		if (!enable_or_transformation || !restriction_is_or_clause(rinfo))
+		{
+			/* Add a clause without changes */
+			modified_rinfo = lappend(modified_rinfo, rinfo);
+			continue;
+		}
+
+		len_ors = ((BoolExpr *) rinfo->clause)->args? list_length(((BoolExpr *) rinfo->clause)->args) : 0;
+
+		if (len_ors < 2)
+		{
+			/* Add a clause without changes */
+			modified_rinfo = lappend(modified_rinfo, rinfo);
+			continue;
+		}
+
+		MemSet(&info, 0, sizeof(info));
+		info.keysize = sizeof(OrClauseGroupKey);
+		info.entrysize = sizeof(OrClauseGroupEntry);
+		info.hash = orclause_hash;
+		info.keycopy = orclause_keycopy;
+		info.match = orclause_match;
+		or_group_htab = hash_create("OR Groups",
+									len_ors,
+									&info,
+									HASH_ELEM | HASH_FUNCTION | HASH_COMPARE | HASH_KEYCOPY);
+
+		/*
+		 * NOTE:
+		 * It is an OR-clause. So, rinfo->orclause is a BoolExpr node, contains
+		 * a list of sub-restrictinfo args, and rinfo->clause - which is the
+		 * same expression, made from bare clauses. To not break selectivity
+		 * caches and other optimizations, use both:
+		 * - use rinfos from orclause if no transformation needed
+		 * - use  bare quals from rinfo->clause in the case of transformation,
+		 * to create new RestrictInfo: in this case we have no options to avoid
+		 * selectivity estimation procedure.
+		 */
+		forboth(lc_eargs, ((BoolExpr *) rinfo->clause)->args,
+				lc_rargs, ((BoolExpr *) rinfo->orclause)->args)
+		{
+			Expr			   *or_qual = (Expr *) lfirst(lc_eargs);
+			RestrictInfo	   *sub_rinfo;
+			Node			   *const_expr;
+			Node			   *nconst_expr;
+			OrClauseGroupKey		hashkey;
+			bool found = false;
+
+			/* It may be one more boolean expression, skip it for now */
+			if (!IsA(lfirst(lc_rargs), RestrictInfo))
+			{
+				or_list = lappend(or_list, (void *) or_qual);
+				continue;
+			}
+
+			sub_rinfo = lfirst_node(RestrictInfo, lc_rargs);
+
+			/* Check: it is an expr of the form 'F(x) oper ConstExpr' */
+			if (!IsA(or_qual, OpExpr) ||
+				!(bms_is_empty(sub_rinfo->left_relids) ^
+				bms_is_empty(sub_rinfo->right_relids)) ||
+				contain_volatile_functions((Node *) or_qual))
+			{
+				/* Again, it's not the expr we can transform */
+				or_list = lappend(or_list, (void *) or_qual);
+				continue;
+			}
+
+			/* Get pointers to constant and expression sides of the clause */
+			const_expr =bms_is_empty(sub_rinfo->left_relids) ?
+												get_leftop(sub_rinfo->clause) :
+												get_rightop(sub_rinfo->clause);
+			nconst_expr = bms_is_empty(sub_rinfo->left_relids) ?
+												get_rightop(sub_rinfo->clause) :
+												get_leftop(sub_rinfo->clause);
+
+			if (!op_mergejoinable(((OpExpr *) sub_rinfo->clause)->opno, exprType(nconst_expr)))
+			{
+				/* And again, filter out non-equality operators */
+				or_list = lappend(or_list, (void *) or_qual);
+				continue;
+			}
+
+			/*
+			 * At this point we definitely have a transformable clause.
+			 * Classify it and add into specific group of clauses, or create new
+			 * group.
+			 */
+			hashkey.expr = (Expr *) nconst_expr;
+			hashkey.opno = ((OpExpr *) or_qual)->opno;
+			hashkey.exprtype = exprType(nconst_expr);
+			gentry = hash_search(or_group_htab, &hashkey, HASH_ENTER, &found);
+
+			if (unlikely(found))
+			{
+				gentry->consts = lappend(gentry->consts, const_expr);
+				gentry->exprs = lappend(gentry->exprs, or_qual);
+			}
+			else
+			{
+				gentry->node = nconst_expr;
+				gentry->consts = list_make1(const_expr);
+				gentry->exprs = list_make1(or_qual);
+				gentry->rinfo = sub_rinfo;
+				gentry->collation = exprInputCollation((Node *)sub_rinfo->clause);
+			}
+		}
+
+		if (list_length(or_list) == len_ors)
+		{
+			/*
+			* No any transformations possible with this list of arguments. Here we
+			* already made all underlying transformations. Thus, just return the
+			* transformed bool expression.
+			*/
+			modified_rinfo = lappend(modified_rinfo, rinfo);
+			continue;
+		}
+
+		if (!or_group_htab && hash_get_num_entries(or_group_htab) < 1)
+		{
+			/*
+			 * No any transformations possible with this rinfo, just add itself
+			 * to the list and go further.
+			 */
+			modified_rinfo = lappend(modified_rinfo, rinfo);
+			continue;
+		}
+
+		hash_seq_init(&hash_seq, or_group_htab);
+
+		/* Let's convert each group of clauses to an IN operation. */
+
+		/*
+		* Go through the list of groups and convert each, where number of
+		* consts more than 1. trivial groups move to OR-list again
+		*/
+
+		while ((gentry = (OrClauseGroupEntry *) hash_seq_search(&hash_seq)) != NULL)
+		{
+			Oid				    scalar_type;
+			Oid					array_type;
+
+			Assert(list_length(gentry->consts) > 0);
+			Assert(list_length(gentry->exprs) == list_length(gentry->consts));
+
+			if (list_length(gentry->consts) == 1)
+			{
+				/*
+				* Only one element in the class. Return rinfo into the BoolExpr
+				* args list unchanged.
+				*/
+				list_free(gentry->consts);
+				or_list = list_concat(or_list, gentry->exprs);
+				continue;
+			}
+
+			/*
+			* Do the transformation.
+			*
+			* First of all, try to select a common type for the array elements.
+			* Note that since the LHS' type is first in the list, it will be
+			* preferred when there is doubt (eg, when all the RHS items are
+			* unknown literals).
+			*
+			* Note: use list_concat here not lcons, to avoid damaging rnonvars.
+			*
+			* As a source of insides, use make_scalar_array_op()
+			*/
+			scalar_type = gentry->key.exprtype;
+
+			if (scalar_type != RECORDOID && OidIsValid(scalar_type))
+				array_type = get_array_type(scalar_type);
+			else
+				array_type = InvalidOid;
+
+			if (array_type != InvalidOid && scalar_type != InvalidOid)
+			{
+				/*
+				* OK: coerce all the right-hand non-Var inputs to the common
+				* type and build an ArrayExpr for them.
+				*/
+				List	   *aexprs = NULL;
+				ArrayExpr  *newa = NULL;
+				ScalarArrayOpExpr *saopexpr = NULL;
+				HeapTuple			opertup;
+				Form_pg_operator	operform;
+				List			   *namelist = NIL;
+
+				aexprs = gentry->consts;
+
+				newa = makeNode(ArrayExpr);
+				/* array_collid will be set by parse_collate.c */
+				newa->element_typeid = scalar_type;
+				newa->array_typeid = array_type;
+				newa->multidims = false;
+				newa->elements = aexprs;
+				newa->location = -1;
+
+				opertup = SearchSysCache1(OPEROID,
+									  ObjectIdGetDatum(gentry->key.opno));
+
+				if (!HeapTupleIsValid(opertup))
+					elog(ERROR, "cache lookup failed for operator %u",
+						gentry->key.opno);
+
+				operform = (Form_pg_operator) GETSTRUCT(opertup);
+				if (!OperatorIsVisible(gentry->key.opno))
+					namelist = lappend(namelist, makeString(get_namespace_name(operform->oprnamespace)));
+
+				namelist = lappend(namelist, makeString(pstrdup(NameStr(operform->oprname))));
+				ReleaseSysCache(opertup);
+
+				saopexpr = makeNode(ScalarArrayOpExpr);
+				saopexpr->opno = ((OpExpr *) gentry->rinfo->clause)->opno;
+				saopexpr->useOr = true;
+				saopexpr->inputcollid = gentry->collation;
+				saopexpr->args = list_make2(gentry->node, newa);
+				saopexpr->location = -1;
+
+
+				or_list = lappend(or_list, (void *) saopexpr);
+
+				*something_changed = true;
+				change_apply = true;
+
+			}
+			else
+			{
+				list_free(gentry->consts);
+				or_list = lappend(or_list, (void *) gentry->rinfo->clause);
+			}
+			hash_search(or_group_htab, &gentry->key, HASH_REMOVE, NULL);
+		}
+
+		hash_destroy(or_group_htab);
+
+		if (!change_apply)
+		{
+			/*
+			 * Each group contains only one element - use rinfo as is.
+			 */
+			modified_rinfo = lappend(modified_rinfo, rinfo);
+			list_free(or_list);
+			continue;
+		}
+
+		/*
+		 * Make a new version of the restriction. Remember source restriction
+		 * can be used in another path (SeqScan, for example).
+		 */
+
+		/* One more trick: assemble correct clause */
+		rinfo = make_restrictinfo(root,
+				  list_length(or_list) > 1 ? makeBoolExpr(OR_EXPR, or_list, ((BoolExpr *) rinfo->clause)->location) :
+											 linitial(or_list),
+				  rinfo->is_pushed_down,
+				  rinfo->has_clone,
+				  rinfo->is_clone,
+				  rinfo->pseudoconstant,
+				  rinfo->security_level,
+				  rinfo->required_relids,
+				  rinfo->incompatible_relids,
+				  rinfo->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);
+		*something_changed = true;
+	}
+
+	/*
+	 * Check if transformation has made. If nothing changed - return
+	 * baserestrictinfo as is.
+	 */
+	if (*something_changed)
+	{
+		return modified_rinfo;
+	}
+
+	list_free(modified_rinfo);
+	return baserestrictinfo;
+}
+
 /*
  * make_one_rel
  *	  Finds all possible access paths for executing a query, returning a
@@ -767,6 +1157,12 @@ static void
 set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 {
 	Relids		required_outer;
+	RelOptInfo *reL_alternative = makeNode(RelOptInfo);
+	//reL_alternative = copyObject(rel);
+	reL_alternative = copy_simple_rel(root, rel->relid, rel, reL_alternative);
+	reL_alternative->indexlist = NIL;
+	reL_alternative->baserestrictinfo = rel->baserestrictinfo;
+	reL_alternative->eclass_indexes = NULL;
 
 	/*
 	 * We don't support pushing join clauses into the quals of a seqscan, but
@@ -787,6 +1183,39 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 
 	/* Consider TID scans */
 	create_tidscan_paths(root, rel);
+
+	set_cheapest(rel);
+
+	if (rel->reloptkind == RELOPT_BASEREL && enable_or_transformation)
+	{
+		bool applied_transfomation;
+		applied_transfomation =  (bool *) palloc(sizeof(bool));
+		reL_alternative->baserestrictinfo = transform_ors(root, reL_alternative->baserestrictinfo, &applied_transfomation);
+if (applied_transfomation)
+{
+	add_path(reL_alternative, create_seqscan_path(root, reL_alternative, required_outer, 0));
+
+	if (reL_alternative->consider_parallel && required_outer == NULL)
+		create_plain_partial_paths(root, reL_alternative);
+
+	create_index_paths(root, reL_alternative);
+
+	create_tidscan_paths(root, reL_alternative);
+
+	set_cheapest(reL_alternative);
+
+	elog(WARNING, "ors: - %f", rel->cheapest_total_path->total_cost);
+	elog(WARNING, "applied_transfomation: - %f", reL_alternative->cheapest_total_path->total_cost);
+
+	if (reL_alternative->cheapest_total_path->total_cost <= rel->cheapest_total_path->total_cost)
+	{
+		//copy_simple_rel(root, rel->relid, reL_alternative, rel);
+		rel->indexlist = reL_alternative->indexlist;
+		rel->baserestrictinfo = reL_alternative->baserestrictinfo;
+		rel->eclass_indexes = reL_alternative->eclass_indexes;
+	}
+	}
+	}
 }
 
 /*
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index acf4937db01..a9cc9a585df 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -34,12 +34,6 @@
 #include "optimizer/restrictinfo.h"
 #include "utils/lsyscache.h"
 #include "utils/selfuncs.h"
-#include "parser/parse_coerce.h"
-#include "common/hashfn.h"
-#include "catalog/pg_operator.h"
-#include "nodes/queryjumble.h"
-#include "utils/syscache.h"
-#include "catalog/namespace.h"
 
 /* XXX see PartCollMatchesExprColl */
 #define IndexCollMatchesExprColl(idxcollation, exprcollation) \
@@ -198,391 +192,6 @@ static bool ec_member_matches_indexcol(PlannerInfo *root, RelOptInfo *rel,
 									   EquivalenceClass *ec, EquivalenceMember *em,
 									   void *arg);
 
-bool		enable_or_transformation = true;
-typedef struct OrClauseGroupKey
-{
-	Expr   *expr; /* Pointer to the expression tree which has been a source for
-					the hashkey value */
-	Oid		opno;
-	Oid		exprtype;
-} OrClauseGroupKey;
-
-typedef struct OrClauseGroupEntry
-{
-	OrClauseGroupKey key;
-	Node		   *node;
-	List		   *consts;
-	Oid				collation;
-	RestrictInfo   *rinfo;
-	List 		   *exprs;
-} OrClauseGroupEntry;
-
-/*
- * Hash function that's compatible with guc_name_compare
- */
-static uint32
-orclause_hash(const void *data, Size keysize)
-{
-	OrClauseGroupKey   *key = (OrClauseGroupKey *) data;
-	uint64				hash;
-
-	(void) JumbleExpr(key->expr, &hash);
-	hash = ((uint64) key->opno + (uint64) key->exprtype) % UINT64_MAX;
-	return hash;
-}
-
-static void *
-orclause_keycopy(void *dest, const void *src, Size keysize)
-{
-	OrClauseGroupKey *src_key = (OrClauseGroupKey *) src;
-	OrClauseGroupKey *dst_key = (OrClauseGroupKey *) dest;
-
-	Assert(sizeof(OrClauseGroupKey) == keysize);
-
-	dst_key->expr = src_key->expr;
-	dst_key->opno = src_key->opno;
-	dst_key->exprtype = src_key->exprtype;
-	return dst_key;
-}
-
-/*
- * Dynahash match function to use in guc_hashtab
- */
-static int
-orclause_match(const void *data1, const void *data2, Size keysize)
-{
-	OrClauseGroupKey   *key1 = (OrClauseGroupKey *) data1;
-	OrClauseGroupKey   *key2 = (OrClauseGroupKey *) data2;
-
-	Assert(sizeof(OrClauseGroupKey) == keysize);
-
-	if (key1->opno == key2->opno && key1->exprtype == key2->exprtype &&
-		equal(key1->expr, key2->expr))
-		return 0;
-
-	return 1;
-}
-
-/*
- * Pass through baserestrictinfo clauses and try to convert OR clauses into IN
- * Return a modified clause list or just the same baserestrictinfo, if no
- * changes have made.
- * XXX: do not change source list of clauses at all.
- */
-static List *
-transform_ors(PlannerInfo *root, List *baserestrictinfo)
-{
-	ListCell   *lc;
-	List	   *modified_rinfo = NIL;
-	bool		something_changed = false;
-
-	/*
-	 * Complexity of a clause could be arbitrarily sophisticated. Here, we will
-	 * look up only on the top level of clause list.
-	 * XXX: It is substantiated? Could we change something here?
-	 */
-	foreach (lc, baserestrictinfo)
-	{
-		RestrictInfo   *rinfo = lfirst_node(RestrictInfo, lc);
-		RestrictInfo   *rinfo_base = copyObject(rinfo);
-		List		   *or_list = NIL;
-		ListCell	   *lc_eargs,
-					   *lc_rargs;
-		bool			change_apply = false;
-		HASHCTL			info;
-		HTAB 		   *or_group_htab = NULL;
-		int 			len_ors;
-		HASH_SEQ_STATUS		hash_seq;
-		OrClauseGroupEntry *gentry;
-
-		if (!enable_or_transformation || !restriction_is_or_clause(rinfo))
-		{
-			/* Add a clause without changes */
-			modified_rinfo = lappend(modified_rinfo, rinfo);
-			continue;
-		}
-
-		len_ors = ((BoolExpr *) rinfo->clause)->args? list_length(((BoolExpr *) rinfo->clause)->args) : 0;
-
-		if (len_ors < 2)
-		{
-			/* Add a clause without changes */
-			modified_rinfo = lappend(modified_rinfo, rinfo);
-			continue;
-		}
-
-		MemSet(&info, 0, sizeof(info));
-		info.keysize = sizeof(OrClauseGroupKey);
-		info.entrysize = sizeof(OrClauseGroupEntry);
-		info.hash = orclause_hash;
-		info.keycopy = orclause_keycopy;
-		info.match = orclause_match;
-		or_group_htab = hash_create("OR Groups",
-									len_ors,
-									&info,
-									HASH_ELEM | HASH_FUNCTION | HASH_COMPARE | HASH_KEYCOPY);
-
-		/*
-		 * NOTE:
-		 * It is an OR-clause. So, rinfo->orclause is a BoolExpr node, contains
-		 * a list of sub-restrictinfo args, and rinfo->clause - which is the
-		 * same expression, made from bare clauses. To not break selectivity
-		 * caches and other optimizations, use both:
-		 * - use rinfos from orclause if no transformation needed
-		 * - use  bare quals from rinfo->clause in the case of transformation,
-		 * to create new RestrictInfo: in this case we have no options to avoid
-		 * selectivity estimation procedure.
-		 */
-		forboth(lc_eargs, ((BoolExpr *) rinfo->clause)->args,
-				lc_rargs, ((BoolExpr *) rinfo->orclause)->args)
-		{
-			Expr			   *or_qual = (Expr *) lfirst(lc_eargs);
-			RestrictInfo	   *sub_rinfo;
-			Node			   *const_expr;
-			Node			   *nconst_expr;
-			OrClauseGroupKey		hashkey;
-			bool found = false;
-
-			/* It may be one more boolean expression, skip it for now */
-			if (!IsA(lfirst(lc_rargs), RestrictInfo))
-			{
-				or_list = lappend(or_list, (void *) or_qual);
-				continue;
-			}
-
-			sub_rinfo = lfirst_node(RestrictInfo, lc_rargs);
-
-			/* Check: it is an expr of the form 'F(x) oper ConstExpr' */
-			if (!IsA(or_qual, OpExpr) ||
-				!(bms_is_empty(sub_rinfo->left_relids) ^
-				bms_is_empty(sub_rinfo->right_relids)) ||
-				contain_volatile_functions((Node *) or_qual))
-			{
-				/* Again, it's not the expr we can transform */
-				or_list = lappend(or_list, (void *) or_qual);
-				continue;
-			}
-
-			/* Get pointers to constant and expression sides of the clause */
-			const_expr =bms_is_empty(sub_rinfo->left_relids) ?
-												get_leftop(sub_rinfo->clause) :
-												get_rightop(sub_rinfo->clause);
-			nconst_expr = bms_is_empty(sub_rinfo->left_relids) ?
-												get_rightop(sub_rinfo->clause) :
-												get_leftop(sub_rinfo->clause);
-
-			if (!op_mergejoinable(((OpExpr *) sub_rinfo->clause)->opno, exprType(nconst_expr)))
-			{
-				/* And again, filter out non-equality operators */
-				or_list = lappend(or_list, (void *) or_qual);
-				continue;
-			}
-
-			/*
-			 * At this point we definitely have a transformable clause.
-			 * Classify it and add into specific group of clauses, or create new
-			 * group.
-			 */
-			hashkey.expr = (Expr *) nconst_expr;
-			hashkey.opno = ((OpExpr *) or_qual)->opno;
-			hashkey.exprtype = exprType(nconst_expr);
-			gentry = hash_search(or_group_htab, &hashkey, HASH_ENTER, &found);
-
-			if (unlikely(found))
-			{
-				gentry->consts = lappend(gentry->consts, const_expr);
-				gentry->exprs = lappend(gentry->exprs, or_qual);
-			}
-			else
-			{
-				gentry->node = nconst_expr;
-				gentry->consts = list_make1(const_expr);
-				gentry->exprs = list_make1(or_qual);
-				gentry->rinfo = sub_rinfo;
-				gentry->collation = exprInputCollation((Node *)sub_rinfo->clause);
-			}
-		}
-
-		if (list_length(or_list) == len_ors)
-		{
-			/*
-			* No any transformations possible with this list of arguments. Here we
-			* already made all underlying transformations. Thus, just return the
-			* transformed bool expression.
-			*/
-			modified_rinfo = lappend(modified_rinfo, rinfo);
-			continue;
-		}
-
-		if (!or_group_htab && hash_get_num_entries(or_group_htab) < 1)
-		{
-			/*
-			 * No any transformations possible with this rinfo, just add itself
-			 * to the list and go further.
-			 */
-			modified_rinfo = lappend(modified_rinfo, rinfo);
-			continue;
-		}
-
-		hash_seq_init(&hash_seq, or_group_htab);
-
-		/* Let's convert each group of clauses to an IN operation. */
-
-		/*
-		* Go through the list of groups and convert each, where number of
-		* consts more than 1. trivial groups move to OR-list again
-		*/
-
-		while ((gentry = (OrClauseGroupEntry *) hash_seq_search(&hash_seq)) != NULL)
-		{
-			Oid				    scalar_type;
-			Oid					array_type;
-
-			Assert(list_length(gentry->consts) > 0);
-			Assert(list_length(gentry->exprs) == list_length(gentry->consts));
-
-			if (list_length(gentry->consts) == 1)
-			{
-				/*
-				* Only one element in the class. Return rinfo into the BoolExpr
-				* args list unchanged.
-				*/
-				list_free(gentry->consts);
-				or_list = list_concat(or_list, gentry->exprs);
-				continue;
-			}
-
-			/*
-			* Do the transformation.
-			*
-			* First of all, try to select a common type for the array elements.
-			* Note that since the LHS' type is first in the list, it will be
-			* preferred when there is doubt (eg, when all the RHS items are
-			* unknown literals).
-			*
-			* Note: use list_concat here not lcons, to avoid damaging rnonvars.
-			*
-			* As a source of insides, use make_scalar_array_op()
-			*/
-			scalar_type = gentry->key.exprtype;
-
-			if (scalar_type != RECORDOID && OidIsValid(scalar_type))
-				array_type = get_array_type(scalar_type);
-			else
-				array_type = InvalidOid;
-
-			if (array_type != InvalidOid && scalar_type != InvalidOid)
-			{
-				/*
-				* OK: coerce all the right-hand non-Var inputs to the common
-				* type and build an ArrayExpr for them.
-				*/
-				List	   *aexprs = NULL;
-				ArrayExpr  *newa = NULL;
-				ScalarArrayOpExpr *saopexpr = NULL;
-				HeapTuple			opertup;
-				Form_pg_operator	operform;
-				List			   *namelist = NIL;
-
-				aexprs = gentry->consts;
-
-				newa = makeNode(ArrayExpr);
-				/* array_collid will be set by parse_collate.c */
-				newa->element_typeid = scalar_type;
-				newa->array_typeid = array_type;
-				newa->multidims = false;
-				newa->elements = aexprs;
-				newa->location = -1;
-
-				opertup = SearchSysCache1(OPEROID,
-									  ObjectIdGetDatum(gentry->key.opno));
-
-				if (!HeapTupleIsValid(opertup))
-					elog(ERROR, "cache lookup failed for operator %u",
-						gentry->key.opno);
-
-				operform = (Form_pg_operator) GETSTRUCT(opertup);
-				if (!OperatorIsVisible(gentry->key.opno))
-					namelist = lappend(namelist, makeString(get_namespace_name(operform->oprnamespace)));
-
-				namelist = lappend(namelist, makeString(pstrdup(NameStr(operform->oprname))));
-				ReleaseSysCache(opertup);
-
-				saopexpr = makeNode(ScalarArrayOpExpr);
-				saopexpr->opno = ((OpExpr *) gentry->rinfo->clause)->opno;
-				saopexpr->useOr = true;
-				saopexpr->inputcollid = gentry->collation;
-				saopexpr->args = list_make2(gentry->node, newa);
-				saopexpr->location = -1;
-
-
-				or_list = lappend(or_list, (void *) saopexpr);
-
-				something_changed = true;
-				change_apply = true;
-
-			}
-			else
-			{
-				list_free(gentry->consts);
-				or_list = lappend(or_list, (void *) gentry->rinfo->clause);
-			}
-			hash_search(or_group_htab, &gentry->key, HASH_REMOVE, NULL);
-		}
-
-		hash_destroy(or_group_htab);
-
-		if (!change_apply)
-		{
-			/*
-			 * Each group contains only one element - use rinfo as is.
-			 */
-			modified_rinfo = lappend(modified_rinfo, rinfo);
-			list_free(or_list);
-			continue;
-		}
-
-		/*
-		 * Make a new version of the restriction. Remember source restriction
-		 * can be used in another path (SeqScan, for example).
-		 */
-
-		/* One more trick: assemble correct clause */
-		rinfo = make_restrictinfo(root,
-				  list_length(or_list) > 1 ? makeBoolExpr(OR_EXPR, or_list, ((BoolExpr *) rinfo->clause)->location) :
-											 linitial(or_list),
-				  rinfo->is_pushed_down,
-				  rinfo->has_clone,
-				  rinfo->is_clone,
-				  rinfo->pseudoconstant,
-				  rinfo->security_level,
-				  rinfo->required_relids,
-				  rinfo->incompatible_relids,
-				  rinfo->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);
-		something_changed = true;
-	}
-
-	/*
-	 * Check if transformation has made. If nothing changed - return
-	 * baserestrictinfo as is.
-	 */
-	if (something_changed)
-	{
-		return modified_rinfo;
-	}
-
-	list_free(modified_rinfo);
-	return baserestrictinfo;
-}
-
 /*
  * create_index_paths()
  *	  Generate all interesting index paths for the given relation.
@@ -1108,9 +717,6 @@ get_index_paths(PlannerInfo *root, RelOptInfo *rel,
 	bool		skip_lower_saop = false;
 	ListCell   *lc;
 
-	if (rel->reloptkind == RELOPT_BASEREL)
-		rel->baserestrictinfo = transform_ors(root, rel->baserestrictinfo);
-
 	/*
 	 * Build simple index paths using the clauses.  Allow ScalarArrayOpExpr
 	 * clauses only if the index AM supports them natively, and skip any such
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 5d83f60eb9a..189cee449bf 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -183,6 +183,97 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
 	root->simple_rel_array_size = new_size;
 }
 
+RelOptInfo *
+copy_simple_rel(PlannerInfo *root, int relid, RelOptInfo *base_rel, RelOptInfo *rel)
+{
+	rel->reloptkind = base_rel->parent ? RELOPT_OTHER_MEMBER_REL : RELOPT_BASEREL;
+	rel->relids = base_rel->relids;
+	rel->rows = base_rel->rows;
+	/* cheap startup cost is interesting iff not all tuples to be retrieved */
+	rel->consider_startup = (root->tuple_fraction > 0);
+	rel->consider_param_startup = base_rel->consider_param_startup;	/* might get changed later */
+	rel->consider_parallel = base_rel->consider_parallel; /* might get changed later */
+	rel->reltarget = base_rel->reltarget;
+	rel->pathlist = base_rel->pathlist;
+	rel->ppilist = base_rel->ppilist;
+	rel->partial_pathlist = base_rel->partial_pathlist;
+	rel->cheapest_startup_path = base_rel->cheapest_startup_path;
+	rel->cheapest_total_path = base_rel->cheapest_total_path;
+	rel->cheapest_unique_path = base_rel->cheapest_unique_path;
+	rel->cheapest_parameterized_paths = base_rel->cheapest_parameterized_paths;
+	rel->relid = base_rel->relid;
+	/* min_attr, max_attr, attr_needed, attr_widths are set below */
+	rel->lateral_vars = base_rel->lateral_vars;
+	rel->statlist = base_rel->statlist;
+	rel->pages = base_rel->pages;
+	rel->tuples = base_rel->tuples;
+	rel->allvisfrac = base_rel->allvisfrac;
+	rel->subroot = base_rel->subroot;
+	rel->subplan_params = base_rel->subplan_params;
+	rel->rel_parallel_workers = base_rel->rel_parallel_workers; /* set up in get_relation_info */
+	rel->amflags = base_rel->amflags;
+	rel->parent = base_rel->parent;
+	rel->serverid = base_rel->serverid;
+	rel->userid = base_rel->userid;
+	rel->useridiscurrent = base_rel->useridiscurrent;
+	rel->fdwroutine = base_rel->fdwroutine;
+	rel->fdw_private = base_rel->fdw_private;
+	rel->unique_for_rels = base_rel->unique_for_rels;
+	rel->non_unique_for_rels = base_rel->non_unique_for_rels;
+	rel->baserestrictcost.startup = 0;
+	rel->baserestrictcost.per_tuple = 0;
+	rel->baserestrict_min_security = UINT_MAX;
+	rel->joininfo = base_rel->joininfo;
+	rel->has_eclass_joins = base_rel->has_eclass_joins;
+
+	/*
+	 * Pass assorted information down the inheritance hierarchy.
+	 */
+	if (base_rel->parent)
+	{
+		/* We keep back-links to immediate parent and topmost parent. */
+		rel->parent = base_rel->parent;
+		rel->top_parent = base_rel->parent->top_parent ? base_rel->parent->top_parent : base_rel->parent;
+		rel->top_parent_relids = base_rel->top_parent->relids;
+
+		/*
+		 * A child rel is below the same outer joins as its parent.  (We
+		 * presume this info was already calculated for the parent.)
+		 */
+		rel->nulling_relids = base_rel->parent->nulling_relids;
+
+		/*
+		 * Also propagate lateral-reference information from appendrel parent
+		 * rels to their child rels.  We intentionally give each child rel the
+		 * same minimum parameterization, even though it's quite possible that
+		 * some don't reference all the lateral rels.  This is because any
+		 * append path for the parent will have to have the same
+		 * parameterization for every child anyway, and there's no value in
+		 * forcing extra reparameterize_path() calls.  Similarly, a lateral
+		 * reference to the parent prevents use of otherwise-movable join rels
+		 * for each child.
+		 *
+		 * It's possible for child rels to have their own children, in which
+		 * case the topmost parent's lateral info propagates all the way down.
+		 */
+		rel->direct_lateral_relids = base_rel->parent->direct_lateral_relids;
+		rel->lateral_relids = base_rel->parent->lateral_relids;
+		rel->lateral_referencers = base_rel->parent->lateral_referencers;
+	}
+	else
+	{
+		rel->parent = base_rel->parent;
+		rel->top_parent = base_rel->top_parent;
+		rel->top_parent_relids = base_rel->top_parent_relids;
+		rel->nulling_relids = base_rel->nulling_relids;
+		rel->direct_lateral_relids = base_rel->direct_lateral_relids;
+		rel->lateral_relids = base_rel->lateral_relids;
+		rel->lateral_referencers = base_rel->lateral_referencers;
+	}
+
+	return rel;
+}
+
 /*
  * build_simple_rel
  *	  Construct a new RelOptInfo for a base relation or 'other' relation.
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 6e557bebc44..2b3825b6e52 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -304,6 +304,8 @@ extern Path *reparameterize_path_by_child(PlannerInfo *root, Path *path,
  */
 extern void setup_simple_rel_arrays(PlannerInfo *root);
 extern void expand_planner_arrays(PlannerInfo *root, int add_size);
+extern RelOptInfo *
+copy_simple_rel(PlannerInfo *root, int relid, RelOptInfo *base_rel, RelOptInfo *rel);
 extern RelOptInfo *build_simple_rel(PlannerInfo *root, int relid,
 									RelOptInfo *parent);
 extern RelOptInfo *find_base_rel(PlannerInfo *root, int relid);
-- 
2.34.1