File 00010-joinsel.patch of Package postgresql10

diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 132916aa70..75e1986ecd 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -306,6 +306,27 @@ performDeletion(const ObjectAddress *object,
 	Relation	depRel;
 	ObjectAddresses *targetObjects;
 
+	if (flags & PERFORM_DELETION_CONCURRENTLY)
+	{
+		/*
+		 * We must commit our transaction in order to make the first pg_index
+		 * state update visible to other sessions.  If the DROP machinery has
+		 * already performed any other actions (removal of other objects,
+		 * pg_depend entries, etc), the commit would make those actions
+		 * permanent, which would leave us with inconsistent catalog state if
+		 * we fail partway through the following sequence.  Since DROP INDEX
+		 * CONCURRENTLY is restricted to dropping just one index that has no
+		 * dependencies, we should get here before anything's been done ---
+		 * but let's check that to be sure.  We can verify that the current
+		 * transaction has not executed any transactional updates by checking
+		 * that no XID has been assigned.
+		 */
+		if (GetTopTransactionIdIfAny() != InvalidTransactionId)
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("DROP INDEX CONCURRENTLY must be first action in transaction")));
+	}
+
 	/*
 	 * We save some cycles by opening pg_depend just once and passing the
 	 * Relation pointer down to all the recursive deletion steps.
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 0c2e457697..764d9de75f 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -46,9 +46,11 @@
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
+#include "catalog/pg_type_fn.h"
 #include "catalog/storage.h"
 #include "commands/tablecmds.h"
 #include "commands/trigger.h"
+#include "commands/typecmds.h"
 #include "executor/executor.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
@@ -94,7 +96,8 @@ static TupleDesc ConstructTupleDescriptor(Relation heapRelation,
 						 List *indexColNames,
 						 Oid accessMethodObjectId,
 						 Oid *collationObjectId,
-						 Oid *classObjectId);
+						 Oid *classObjectId,
+						 bool *preserveAttrType);
 static void InitializeAttributeOids(Relation indexRelation,
 						int numatts, Oid indexoid);
 static void AppendAttributeTuples(Relation indexRelation, int numatts);
@@ -282,7 +285,8 @@ ConstructTupleDescriptor(Relation heapRelation,
 						 List *indexColNames,
 						 Oid accessMethodObjectId,
 						 Oid *collationObjectId,
-						 Oid *classObjectId)
+						 Oid *classObjectId,
+						 bool *preserveAttrType)
 {
 	int			numatts = indexInfo->ii_NumIndexAttrs;
 	ListCell   *colnames_item = list_head(indexColNames);
@@ -306,6 +310,11 @@ ConstructTupleDescriptor(Relation heapRelation,
 	indexTupDesc = CreateTemplateTupleDesc(numatts, false);
 
 	/*
+	 * By default assume that index will reuse attribute types of the heap relation
+	 */
+	*preserveAttrType = true;
+
+    /*
 	 * For simple index columns, we copy the pg_attribute row from the parent
 	 * relation and modify it as necessary.  For expressions we have to cons
 	 * up a pg_attribute row the hard way.
@@ -476,6 +485,7 @@ ConstructTupleDescriptor(Relation heapRelation,
 			to->attbyval = typeTup->typbyval;
 			to->attalign = typeTup->typalign;
 			to->attstorage = typeTup->typstorage;
+			*preserveAttrType = false;
 
 			ReleaseSysCache(tuple);
 		}
@@ -732,10 +742,14 @@ index_create(Relation heapRelation,
 	Oid			namespaceId;
 	int			i;
 	char		relpersistence;
+	bool		preserve_attr_type;
+	Oid         ownerId;
+	ObjectAddress new_type_addr;
 
 	is_exclusion = (indexInfo->ii_ExclusionOps != NULL);
 
 	pg_class = heap_open(RelationRelationId, RowExclusiveLock);
+	ownerId = GetUserId();
 
 	/*
 	 * The index will be in the same namespace as its parent table, and is
@@ -821,7 +835,8 @@ index_create(Relation heapRelation,
 											indexColNames,
 											accessMethodObjectId,
 											collationObjectId,
-											classObjectId);
+											classObjectId,
+											&preserve_attr_type);
 
 	/*
 	 * Allocate an OID for the index, unless we were told what to use.
@@ -868,6 +883,95 @@ index_create(Relation heapRelation,
 
 	Assert(indexRelationId == RelationGetRelid(indexRelation));
 
+	if (indexInfo->ii_NumIndexAttrs > 1
+		&& preserve_attr_type
+		&& !is_internal
+		&& (!IsBinaryUpgrade || binary_upgrade_next_pg_type_oid != InvalidOid))
+	{
+		Oid new_array_oid = AssignTypeArrayOid();
+		char* relarrayname;
+
+		/*
+		 * Build compound type for compound index to be able to use it in statistic.
+		 * We need to collect statistic for compound indexes to be able to better predict selectivity of multicolumn joins.
+		 */
+		new_type_addr = TypeCreate(InvalidOid,
+			   indexRelationName,
+			   namespaceId,
+			   indexRelationId,
+			   RELKIND_INDEX,
+			   ownerId, /* owner's ID */
+			   -1,			/* internal size (varlena) */
+			   TYPTYPE_COMPOSITE,	/* type-type (composite) */
+			   TYPCATEGORY_COMPOSITE,	/* type-category (ditto) */
+			   false,		/* composite types are never preferred */
+			   DEFAULT_TYPDELIM,	/* default array delimiter */
+			   F_RECORD_IN, /* input procedure */
+			   F_RECORD_OUT,	/* output procedure */
+			   F_RECORD_RECV,	/* receive procedure */
+			   F_RECORD_SEND,	/* send procedure */
+			   InvalidOid,	/* typmodin procedure - none */
+			   InvalidOid,	/* typmodout procedure - none */
+			   InvalidOid,	/* analyze procedure - default */
+			   InvalidOid,	/* array element type - irrelevant */
+			   false,		/* this is not an array type */
+			   new_array_oid,	/* array type if any */
+			   InvalidOid,	/* domain base type - irrelevant */
+			   NULL,		/* default value - none */
+			   NULL,		/* default binary representation */
+			   false,		/* passed by reference */
+			   'd',			/* alignment - must be the largest! */
+			   'x',			/* fully TOASTable */
+			   -1,			/* typmod */
+			   0,			/* array dimensions for typBaseType */
+			   false,		/* Type NOT NULL */
+			   InvalidOid); /* rowtypes never have a collation */
+
+		indexRelation->rd_rel->reltype = new_type_addr.objectId;
+
+		relarrayname = makeArrayTypeName(indexRelationName, namespaceId);
+
+		TypeCreate(new_array_oid,	/* force the type's OID to this */
+				   relarrayname,	/* Array type name */
+				   namespaceId,	/* Same namespace as parent */
+				   InvalidOid,	/* Not composite, no relationOid */
+				   0,			/* relkind, also N/A here */
+				   ownerId,	    /* owner's ID */
+				   -1,			/* Internal size (varlena) */
+				   TYPTYPE_BASE,	/* Not composite - typelem is */
+				   TYPCATEGORY_ARRAY,	/* type-category (array) */
+				   false,		/* array types are never preferred */
+				   DEFAULT_TYPDELIM,	/* default array delimiter */
+				   F_ARRAY_IN,	/* array input proc */
+				   F_ARRAY_OUT, /* array output proc */
+				   F_ARRAY_RECV,	/* array recv (bin) proc */
+				   F_ARRAY_SEND,	/* array send (bin) proc */
+				   InvalidOid,	/* typmodin procedure - none */
+				   InvalidOid,	/* typmodout procedure - none */
+				   F_ARRAY_TYPANALYZE,	/* array analyze procedure */
+				   indexRelation->rd_rel->reltype,	/* array element type - the rowtype */
+				   true,		/* yes, this is an array type */
+				   InvalidOid,	/* this has no array type */
+				   InvalidOid,	/* domain base type - irrelevant */
+				   NULL,		/* default value - none */
+				   NULL,		/* default binary representation */
+				   false,		/* passed by reference */
+				   'd',			/* alignment - must be the largest! */
+				   'x',			/* fully TOASTable */
+				   -1,			/* typmod */
+				   0,			/* array dimensions for typBaseType */
+				   false,		/* Type NOT NULL */
+				   InvalidOid); /* rowtypes never have a collation */
+
+		pfree(relarrayname);
+	}
+
+	/*
+	 * Register relcache invalidation on the indexes' heap relation, to
+	 * maintain consistency of its index list
+	 */
+	CacheInvalidateRelcache(heapRelation);
+
 	/*
 	 * Obtain exclusive lock on it.  Although no other backends can see it
 	 * until we commit, this prevents deadlock-risk complaints from lock
@@ -923,12 +1027,6 @@ index_create(Relation heapRelation,
 						!deferrable,
 						!concurrent);
 
-	/*
-	 * Register relcache invalidation on the indexes' heap relation, to
-	 * maintain consistency of its index list
-	 */
-	CacheInvalidateRelcache(heapRelation);
-
 	/*
 	 * Register constraint and dependencies for the index.
 	 *
@@ -1428,24 +1526,6 @@ index_drop(Oid indexId, bool concurrent)
 	 */
 	if (concurrent)
 	{
-		/*
-		 * We must commit our transaction in order to make the first pg_index
-		 * state update visible to other sessions.  If the DROP machinery has
-		 * already performed any other actions (removal of other objects,
-		 * pg_depend entries, etc), the commit would make those actions
-		 * permanent, which would leave us with inconsistent catalog state if
-		 * we fail partway through the following sequence.  Since DROP INDEX
-		 * CONCURRENTLY is restricted to dropping just one index that has no
-		 * dependencies, we should get here before anything's been done ---
-		 * but let's check that to be sure.  We can verify that the current
-		 * transaction has not executed any transactional updates by checking
-		 * that no XID has been assigned.
-		 */
-		if (GetTopTransactionIdIfAny() != InvalidTransactionId)
-			ereport(ERROR,
-					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-					 errmsg("DROP INDEX CONCURRENTLY must be first action in transaction")));
-
 		/*
 		 * Mark index invalid by updating its pg_index entry
 		 */
@@ -3219,9 +3299,6 @@ index_set_state_flags(Oid indexId, IndexStateFlagsAction action)
 	HeapTuple	indexTuple;
 	Form_pg_index indexForm;
 
-	/* Assert that current xact hasn't done any transactional updates */
-	Assert(GetTopTransactionIdIfAny() == InvalidTransactionId);
-
 	/* Open pg_index and fetch a writable copy of the index's tuple */
 	pg_index = heap_open(IndexRelationId, RowExclusiveLock);
 
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 925d06ed8c..d5a024db14 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -35,8 +35,11 @@
 #include "commands/vacuum.h"
 #include "executor/executor.h"
 #include "foreign/fdwapi.h"
+#include "funcapi.h"
 #include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
+#include "nodes/makefuncs.h"
+#include "nodes/pg_list.h"
 #include "parser/parse_oper.h"
 #include "parser/parse_relation.h"
 #include "pgstat.h"
@@ -61,6 +64,7 @@
 #include "utils/syscache.h"
 #include "utils/timestamp.h"
 #include "utils/tqual.h"
+#include "utils/typcache.h"
 
 
 /* Per-index data for ANALYZE */
@@ -70,6 +74,7 @@ typedef struct AnlIndexData
 	double		tupleFract;		/* fraction of rows for partial index */
 	VacAttrStats **vacattrstats;	/* index attrs to analyze */
 	int			attr_cnt;
+	bool        multicolumn;    /* Collect compound row statistic for multicolumn index */
 } AnlIndexData;
 
 
@@ -480,6 +485,21 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
 				}
 				thisdata->attr_cnt = tcnt;
 			}
+			else if (indexInfo->ii_NumIndexAttrs > 1 && va_cols == NIL &&
+					 Irel[ind]->rd_rel->reltype != InvalidOid)
+			{
+				/* Collect statistic for multicolumn index for better predicting selectivity of multicolumn joins */
+				RowExpr* row = makeNode(RowExpr);
+				row->row_typeid = Irel[ind]->rd_rel->reltype;
+				row->row_format = COERCE_EXPLICIT_CAST;
+				row->location = -1;
+				row->colnames = NULL;
+				thisdata->vacattrstats = (VacAttrStats **)palloc(sizeof(VacAttrStats *));
+				thisdata->vacattrstats[0] = examine_attribute(Irel[ind], 1, (Node*)row);
+				thisdata->vacattrstats[0]->tupDesc = lookup_type_cache(row->row_typeid, TYPECACHE_TUPDESC)->tupDesc;
+				thisdata->attr_cnt = 1;
+				thisdata->multicolumn = true;
+			}
 		}
 	}
 
@@ -797,28 +817,41 @@ compute_index_stats(Relation onerel, double totalrows,
 							   values,
 							   isnull);
 
-				/*
-				 * Save just the columns we care about.  We copy the values
-				 * into ind_context from the estate's per-tuple context.
-				 */
-				for (i = 0; i < attr_cnt; i++)
+				if (thisdata->multicolumn)
 				{
-					VacAttrStats *stats = thisdata->vacattrstats[i];
-					int			attnum = stats->attr->attnum;
-
-					if (isnull[attnum - 1])
-					{
-						exprvals[tcnt] = (Datum) 0;
-						exprnulls[tcnt] = true;
-					}
-					else
+					/* For multicolumn index construct compound value */
+					VacAttrStats *stats = thisdata->vacattrstats[0];
+					exprvals[tcnt] = HeapTupleGetDatum(heap_form_tuple(stats->tupDesc,
+																	   values,
+																	   isnull));
+					exprnulls[tcnt] = false;
+					tcnt++;
+				}
+				else
+				{
+					/*
+					 * Save just the columns we care about.  We copy the values
+					 * into ind_context from the estate's per-tuple context.
+					 */
+					for (i = 0; i < attr_cnt; i++)
 					{
-						exprvals[tcnt] = datumCopy(values[attnum - 1],
-												   stats->attrtype->typbyval,
-												   stats->attrtype->typlen);
-						exprnulls[tcnt] = false;
+						VacAttrStats *stats = thisdata->vacattrstats[i];
+						int			attnum = stats->attr->attnum;
+
+						if (isnull[attnum - 1])
+						{
+							exprvals[tcnt] = (Datum) 0;
+							exprnulls[tcnt] = true;
+						}
+						else
+						{
+							exprvals[tcnt] = datumCopy(values[attnum - 1],
+													   stats->attrtype->typbyval,
+													   stats->attrtype->typlen);
+							exprnulls[tcnt] = false;
+						}
+						tcnt++;
 					}
-					tcnt++;
 				}
 			}
 		}
diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c
index 9d340255c3..ea9660bb62 100644
--- a/src/backend/optimizer/path/clausesel.c
+++ b/src/backend/optimizer/path/clausesel.c
@@ -14,6 +14,9 @@
  */
 #include "postgres.h"
 
+#include "access/genam.h"
+#include "access/htup_details.h"
+#include "funcapi.h"
 #include "nodes/makefuncs.h"
 #include "optimizer/clauses.h"
 #include "optimizer/cost.h"
@@ -21,7 +24,10 @@
 #include "optimizer/plancat.h"
 #include "utils/fmgroids.h"
 #include "utils/lsyscache.h"
+#include "utils/rel.h"
 #include "utils/selfuncs.h"
+#include "utils/syscache.h"
+#include "utils/typcache.h"
 #include "statistics/statistics.h"
 
 
@@ -43,6 +49,327 @@ static void addRangeClause(RangeQueryClause **rqlist, Node *clause,
 			   bool varonleft, bool isLTsel, Selectivity s2);
 static RelOptInfo *find_single_rel_for_clauses(PlannerInfo *root,
 							List *clauses);
+/*
+ *  Get variabe node. Returns null if node is not a Var node.
+ */
+static inline Var*
+get_var(Node* node)
+{
+	if (IsA(node, RelabelType))
+		node = (Node *) ((RelabelType *) node)->arg;
+
+	return IsA(node, Var) ? (Var*)node : NULL;
+}
+
+/*
+ * Locate compound index which can be used for multicolumn join. Join clauses can refer to index keys in any order, but this order should be the same
+ * for left and right indexes. This is why we first call this function with strict_vars_order == false and fill permutation array. And second time wee use this
+ * permutation for previously chosen order.
+ */
+static IndexOptInfo*
+locate_multicolumn_index(PlannerInfo *root, Index varno, List* vars, int* permutation, bool strict_vars_order)
+{
+	ListCell   *ilist;
+	RelOptInfo* rel = find_base_rel(root, varno);
+	int n_vars = list_length(vars);
+
+	Assert(n_vars > 1);
+
+	foreach(ilist, rel->indexlist)
+	{
+		IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
+		ListCell   *vlist = list_head(vars);
+		int			i = 0;
+		bool        used[INDEX_MAX_KEYS] = {false};
+
+		if (index->ncolumns != n_vars)
+			continue;
+
+		foreach (vlist, vars)
+		{
+			Var* var = lfirst(vlist);
+			if (strict_vars_order)
+			{
+				if (index->indexkeys[permutation[i]] != var->varattno)
+					break;
+			}
+			else
+			{
+				int pos;
+				for (pos = 0; pos < n_vars; pos++)
+				{
+					if (index->indexkeys[pos] == var->varattno  && !used[pos])
+					{
+						used[pos] = true;
+						permutation[i] = pos;
+						break;
+					}
+				}
+				if (pos == n_vars)
+					break;
+			}
+			i += 1;
+		}
+		if (vlist == NULL)
+			return index;
+	}
+	return NULL;
+}
+
+/*
+ * treat_as_join_clause -
+ *	  Decide whether an operator clause is to be handled by the
+ *	  restriction or join estimator.  Subroutine for clause_selectivity().
+ */
+static inline bool
+treat_as_join_clause(Node *clause, RestrictInfo *rinfo,
+					 int varRelid, SpecialJoinInfo *sjinfo)
+{
+	if (varRelid != 0)
+	{
+		/*
+		 * Caller is forcing restriction mode (eg, because we are examining an
+		 * inner indexscan qual).
+		 */
+		return false;
+	}
+	else if (sjinfo == NULL)
+	{
+		/*
+		 * It must be a restriction clause, since it's being evaluated at a
+		 * scan node.
+		 */
+		return false;
+	}
+	else
+	{
+		/*
+		 * Otherwise, it's a join if there's more than one relation used. We
+		 * can optimize this calculation if an rinfo was passed.
+		 *
+		 * XXX	Since we know the clause is being evaluated at a join, the
+		 * only way it could be single-relation is if it was delayed by outer
+		 * joins.  Although we can make use of the restriction qual estimators
+		 * anyway, it seems likely that we ought to account for the
+		 * probability of injected nulls somehow.
+		 */
+		if (rinfo)
+			return (bms_membership(rinfo->clause_relids) == BMS_MULTIPLE);
+		else
+			return (NumRelids(clause) > 1);
+	}
+}
+
+
+/*
+ * Check if clauses represent multicolumnn join with compound indexes available for both side or
+ * comparison of indexed columns of one relation with constant values.
+ * If so, calculates selectivity of compound type comparison and returns true
+ */
+static bool
+use_multicolumn_statistic(PlannerInfo *root, List *clauses, int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo, Selectivity* selectivity)
+{
+	ListCell   *l;
+	List* vars1 = NULL;
+	List* vars2 = NULL;
+	Index varno1 = 0;
+	Index varno2 = 0;
+	VariableStatData vardata[2];
+	IndexOptInfo* index[2];
+	TypeCacheEntry *typentry;
+	int i;
+	int n_indexes;
+	int permutation[INDEX_MAX_KEYS];
+	bool varonleft = true;
+	Datum constant = 0;
+
+	foreach(l, clauses)
+	{
+		Node* clause = (Node *) lfirst(l);
+		RestrictInfo* rinfo = NULL;
+		OpExpr	   *opclause = NULL;
+
+		if (IsA(clause, RestrictInfo))
+		{
+			rinfo = (RestrictInfo *) clause;
+			if (!rinfo->orclause)
+				clause = (Node*)rinfo->clause;
+		}
+		if (IsA(clause, OpExpr))
+			opclause = (OpExpr*)clause;
+
+		if (opclause
+			&& list_length(opclause->args) == 2
+			&& get_oprrest(opclause->opno) == F_EQSEL)
+		{
+			Node* arg1 = (Node*) linitial(opclause->args);
+			Node* arg2 = (Node*) lsecond(opclause->args);
+			Var* var1 = get_var(arg1);
+			Var* var2 = get_var(arg2);
+
+			if (treat_as_join_clause((Node*)opclause, NULL, varRelid, sjinfo))
+			{
+				if (var1 == NULL || var2 == NULL || var1->vartype != var2->vartype)
+					return false;
+
+				if (varno1 == 0)
+				{
+					varno1 = var1->varno;
+					varno2 = var2->varno;
+					vars1 = list_make1(var1);
+					vars2 = list_make1(var2);
+				}
+				else if (var1->varno == varno1 && var2->varno == varno2)
+				{
+					vars1 = lappend(vars1, var1);
+					vars2 = lappend(vars2, var2);
+				}
+				else if (var1->varno == varno2 && var2->varno == varno1)
+				{
+					vars1 = lappend(vars1, var2);
+					vars2 = lappend(vars2, var1);
+				}
+				else
+				{
+					return false;
+				}
+			}
+			else /* Estimate selectivity for a restriction clause. */
+			{
+				/*
+				 * Give up if it is not equality comparison of variable with
+				 * constant or some other clause is treated as join condition
+				 */
+				if (((var1 == NULL) == (var2 == NULL)) || varno2 != 0)
+					return false;
+
+				if (var1 != NULL) /* variable at left */
+				{
+					if (varno1 == 0)
+						varno1 = var1->varno;
+					else if (var1->varno != varno1)
+						return false;
+					vars1 = lappend(vars1, var1);
+
+					if ((rinfo && is_pseudo_constant_clause_relids(arg2, rinfo->right_relids))
+						|| (!rinfo && NumRelids(clause) == 1 && is_pseudo_constant_clause(arg2)))
+					{
+						/* Restriction clause with a pseudoconstant on right side. */
+						Node* const_val = estimate_expression_value(root, arg2);
+						if (IsA(const_val, Const))
+							vars2 = lappend(vars2, const_val);
+						else
+							return false;
+					}
+					else
+						return false;
+				}
+				else
+				{
+					varonleft = false; /* Case (x=1 and 2=x) is not considered */
+
+					if (varno1 == 0)
+						varno1 = var2->varno;
+					else if (var2->varno != varno1)
+						return false;
+					vars1 = lappend(vars1, var2);
+
+					if ((rinfo && is_pseudo_constant_clause_relids(arg1, rinfo->left_relids))
+						|| (!rinfo && NumRelids(clause) == 1 && is_pseudo_constant_clause(arg1)))
+					{
+						/* Restriction clause with a pseudoconstant on left side. */
+						Node* const_val = estimate_expression_value(root, arg1);
+						if (IsA(const_val, Const))
+							vars2 = lappend(vars2, const_val);
+						else
+							return false;
+					}
+					else
+						return false;
+				}
+			}
+		}
+		else
+			return false;
+	}
+	if (varno1 == 0)
+		return false;
+
+	index[0] = locate_multicolumn_index(root, varno1, vars1, permutation, false);
+	if (!index[0])
+		return false;
+
+	if (varno2 != 0)
+	{
+		index[1] = locate_multicolumn_index(root, varno2, vars2, permutation, true);
+		if (!index[1])
+			return false;
+		n_indexes = 2;
+	}
+	else
+		n_indexes = 1;
+
+	for (i = 0; i < n_indexes; i++)
+	{
+		Relation indexRel = index_open(index[i]->indexoid, AccessShareLock);
+
+		if (!indexRel->rd_rel->reltype)
+		{
+			index_close(indexRel, AccessShareLock);
+
+			while (--i >= 0)
+				ReleaseVariableStats(vardata[i]);
+
+			return false;
+		}
+		memset(&vardata[i], 0, sizeof(vardata[i]));
+		vardata[i].isunique = index[i]->unique;
+		vardata[i].atttype = indexRel->rd_rel->reltype;
+		vardata[i].rel = index[i]->rel;
+		vardata[i].acl_ok = true;
+		vardata[i].statsTuple = SearchSysCache3(STATRELATTINH,
+												ObjectIdGetDatum(index[i]->indexoid),
+												Int16GetDatum(1),
+												BoolGetDatum(false));
+		vardata[i].freefunc = ReleaseSysCache;
+
+		index_close(indexRel, AccessShareLock);
+	}
+
+	/*
+	 * Assume that two compound types are coheerent, so we can use equality function from one type
+	 * to compare it with other type.
+	 */
+	typentry = lookup_type_cache(vardata[0].atttype, TYPECACHE_EQ_OPR|TYPECACHE_TUPDESC);
+
+	if (n_indexes == 1) /* restriction clause selectivity */
+	{
+		/* Create compound constant value */
+		Datum	values[INDEX_MAX_KEYS];
+		bool	isnull[INDEX_MAX_KEYS];
+		i = 0;
+		foreach (l, vars2)
+		{
+			Const* c = (Const*) lfirst(l);
+			int j = permutation[i++];
+			values[j] = c->constvalue;
+			isnull[j] = c->constisnull;
+		}
+		constant = HeapTupleGetDatum(heap_form_tuple(typentry->tupDesc, values, isnull));
+		*selectivity = eqconst_selectivity(typentry->eq_opr, &vardata[0],
+			constant, false, varonleft, false);
+	}
+	else /* join clause selectivity */
+	{
+		*selectivity = eqjoin_selectivity(root, typentry->eq_opr,
+								  &vardata[0], &vardata[1], sjinfo);
+	}
+
+	for (i = 0; i < n_indexes; i++)
+		ReleaseVariableStats(vardata[i]);
+
+	return true;
+}
 
 /****************************************************************************
  *		ROUTINES TO COMPUTE SELECTIVITIES
@@ -103,6 +430,7 @@ clauselist_selectivity(PlannerInfo *root,
 					   SpecialJoinInfo *sjinfo)
 {
 	Selectivity s1 = 1.0;
+	Selectivity s2;
 	RelOptInfo *rel;
 	Bitmapset  *estimatedclauses = NULL;
 	RangeQueryClause *rqlist = NULL;
@@ -141,108 +469,120 @@ clauselist_selectivity(PlannerInfo *root,
 	}
 
 	/*
-	 * Apply normal selectivity estimates for remaining clauses. We'll be
-	 * careful to skip any clauses which were already estimated above.
-	 *
-	 * Anything that doesn't look like a potential rangequery clause gets
-	 * multiplied into s1 and forgotten. Anything that does gets inserted into
-	 * an rqlist entry.
+	 * Check if join conjuncts  corresponds to some compound indexes on left and
+	 * right joined relations. In this case selectivity of join can be
+	 * calculated based on statistic of this compound indexes.
 	 */
-	listidx = -1;
-	foreach(l, clauses)
+	if (use_multicolumn_statistic(root,  clauses, varRelid,  jointype,  sjinfo, &s2))
+	{
+		s1 *=  s2;
+	}
+	else
 	{
-		Node	   *clause = (Node *) lfirst(l);
-		RestrictInfo *rinfo;
-		Selectivity s2;
-
-		listidx++;
-
-		/*
-		 * Skip this clause if it's already been estimated by some other
-		 * statistics above.
-		 */
-		if (bms_is_member(listidx, estimatedclauses))
-			continue;
-
-		/* Always compute the selectivity using clause_selectivity */
-		s2 = clause_selectivity(root, clause, varRelid, jointype, sjinfo);
-
 		/*
-		 * Check for being passed a RestrictInfo.
+		 * Apply normal selectivity estimates for remaining clauses. We'll be
+		 * careful to skip any clauses which were already estimated above.
 		 *
-		 * If it's a pseudoconstant RestrictInfo, then s2 is either 1.0 or
-		 * 0.0; just use that rather than looking for range pairs.
+		 * Anything that doesn't look like a potential rangequery clause gets
+		 * multiplied into s1 and forgotten. Anything that does gets inserted
+		 * into an rqlist entry.
 		 */
-		if (IsA(clause, RestrictInfo))
+
+		listidx = -1;
+		foreach(l, clauses)
 		{
-			rinfo = (RestrictInfo *) clause;
-			if (rinfo->pseudoconstant)
-			{
-				s1 = s1 * s2;
+			Node	*clause = (Node *) lfirst(l);
+			RestrictInfo	*rinfo;
+
+			listidx++;
+
+			/*
+			 * Skip this clause if it's already been estimated by some other
+			 * statistics above.
+			 */
+			if (bms_is_member(listidx, estimatedclauses))
 				continue;
-			}
-			clause = (Node *) rinfo->clause;
-		}
-		else
-			rinfo = NULL;
 
-		/*
-		 * See if it looks like a restriction clause with a pseudoconstant on
-		 * one side.  (Anything more complicated than that might not behave in
-		 * the simple way we are expecting.)  Most of the tests here can be
-		 * done more efficiently with rinfo than without.
-		 */
-		if (is_opclause(clause) && list_length(((OpExpr *) clause)->args) == 2)
-		{
-			OpExpr	   *expr = (OpExpr *) clause;
-			bool		varonleft = true;
-			bool		ok;
+			/* Always compute the selectivity using clause_selectivity */
+			s2 = clause_selectivity(root, clause, varRelid, jointype, sjinfo);
 
-			if (rinfo)
+			/*
+			 * Check for being passed a RestrictInfo.
+			 *
+			 * If it's a pseudoconstant RestrictInfo, then s2 is either 1.0 or
+			 * 0.0; just use that rather than looking for range pairs.
+			 */
+			if (IsA(clause, RestrictInfo))
 			{
-				ok = (bms_membership(rinfo->clause_relids) == BMS_SINGLETON) &&
-					(is_pseudo_constant_clause_relids(lsecond(expr->args),
-													  rinfo->right_relids) ||
-					 (varonleft = false,
-					  is_pseudo_constant_clause_relids(linitial(expr->args),
-													   rinfo->left_relids)));
+				rinfo = (RestrictInfo *) clause;
+				if (rinfo->pseudoconstant)
+				{
+					s1 = s1 * s2;
+					continue;
+				}
+				clause = (Node *) rinfo->clause;
 			}
 			else
-			{
-				ok = (NumRelids(clause) == 1) &&
-					(is_pseudo_constant_clause(lsecond(expr->args)) ||
-					 (varonleft = false,
-					  is_pseudo_constant_clause(linitial(expr->args))));
-			}
+				rinfo = NULL;
 
-			if (ok)
+			/*
+			 * See if it looks like a restriction clause with a pseudoconstant
+			 * on one side.  (Anything more complicated than that might not
+			 * behave in the simple way we are expecting.)  Most of the tests
+			 * here can be done more efficiently with rinfo than without.
+			 */
+			if (is_opclause(clause) && list_length(((OpExpr *) clause)->args) == 2)
 			{
-				/*
-				 * If it's not a "<" or ">" operator, just merge the
-				 * selectivity in generically.  But if it's the right oprrest,
-				 * add the clause to rqlist for later processing.
-				 */
-				switch (get_oprrest(expr->opno))
+				OpExpr		*expr = (OpExpr *) clause;
+				bool		varonleft = true;
+				bool		ok;
+
+				if (rinfo)
 				{
-					case F_SCALARLTSEL:
-						addRangeClause(&rqlist, clause,
-									   varonleft, true, s2);
-						break;
-					case F_SCALARGTSEL:
-						addRangeClause(&rqlist, clause,
-									   varonleft, false, s2);
+					ok = (bms_membership(rinfo->clause_relids) == BMS_SINGLETON) &&
+						(is_pseudo_constant_clause_relids(lsecond(expr->args),
+														  rinfo->right_relids) ||
+						 (varonleft = false,
+						  is_pseudo_constant_clause_relids(linitial(expr->args),
+														   rinfo->left_relids)));
+				}
+				else
+				{
+					ok = (NumRelids(clause) == 1) &&
+						(is_pseudo_constant_clause(lsecond(expr->args)) ||
+						 (varonleft = false,
+						  is_pseudo_constant_clause(linitial(expr->args))));
+				}
+
+				if (ok)
+				{
+					/*
+					 * If it's not a "<"/"<="/">"/">=" operator, just merge the
+					 * selectivity in generically.  But if it's the right
+					 * oprrest, add the clause to rqlist for later processing.
+					 */
+					switch (get_oprrest(expr->opno))
+					{
+						case F_SCALARLTSEL:
+							addRangeClause(&rqlist, clause,
+										   varonleft, true, s2);
 						break;
-					default:
-						/* Just merge the selectivity in generically */
-						s1 = s1 * s2;
+						case F_SCALARGTSEL:
+							addRangeClause(&rqlist, clause,
+										   varonleft, false, s2);
 						break;
+						default:
+							/* Just merge the selectivity in generically */
+							s1 = s1 * s2;
+							break;
+					}
+					continue;		/* drop to loop bottom */
 				}
-				continue;		/* drop to loop bottom */
 			}
-		}
 
-		/* Not the right form, so treat it generically. */
-		s1 = s1 * s2;
+			/* Not the right form, so treat it generically. */
+			s1 = s1 * s2;
+		}
 	}
 
 	/*
@@ -485,51 +825,6 @@ bms_is_subset_singleton(const Bitmapset *s, int x)
 	return false;
 }
 
-/*
- * treat_as_join_clause -
- *	  Decide whether an operator clause is to be handled by the
- *	  restriction or join estimator.  Subroutine for clause_selectivity().
- */
-static inline bool
-treat_as_join_clause(Node *clause, RestrictInfo *rinfo,
-					 int varRelid, SpecialJoinInfo *sjinfo)
-{
-	if (varRelid != 0)
-	{
-		/*
-		 * Caller is forcing restriction mode (eg, because we are examining an
-		 * inner indexscan qual).
-		 */
-		return false;
-	}
-	else if (sjinfo == NULL)
-	{
-		/*
-		 * It must be a restriction clause, since it's being evaluated at a
-		 * scan node.
-		 */
-		return false;
-	}
-	else
-	{
-		/*
-		 * Otherwise, it's a join if there's more than one relation used. We
-		 * can optimize this calculation if an rinfo was passed.
-		 *
-		 * XXX	Since we know the clause is being evaluated at a join, the
-		 * only way it could be single-relation is if it was delayed by outer
-		 * joins.  Although we can make use of the restriction qual estimators
-		 * anyway, it seems likely that we ought to account for the
-		 * probability of injected nulls somehow.
-		 */
-		if (rinfo)
-			return (bms_membership(rinfo->clause_relids) == BMS_MULTIPLE);
-		else
-			return (NumRelids(clause) > 1);
-	}
-}
-
-
 /*
  * clause_selectivity -
  *	  Compute the selectivity of a general boolean expression clause.
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 3d633de8ff..2e570f9d8b 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -297,12 +297,21 @@ static double
 var_eq_const(VariableStatData *vardata, Oid operator,
 			 Datum constval, bool constisnull,
 			 bool varonleft, bool negate)
+{
+	return eqconst_selectivity(operator, vardata, constval, constisnull, varonleft, negate);
+}
+
+
+Selectivity
+eqconst_selectivity(Oid operator,
+					VariableStatData *vardata,
+					Datum constval, bool constisnull,
+					bool varonleft, bool negate)
 {
 	double		selec;
 	double		nullfrac = 0.0;
 	bool		isdefault;
 	Oid			opfuncoid;
-
 	/*
 	 * If the constant is NULL, assume operator is strict and return zero, ie,
 	 * operator will never return TRUE.  (It's zero even for a negator op.)
@@ -2208,21 +2217,36 @@ eqjoinsel(PG_FUNCTION_ARGS)
 	JoinType	jointype = (JoinType) PG_GETARG_INT16(3);
 #endif
 	SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) PG_GETARG_POINTER(4);
-	double		selec;
 	VariableStatData vardata1;
 	VariableStatData vardata2;
 	bool		join_is_reversed;
-	RelOptInfo *inner_rel;
+	double		selec;
 
 	get_join_variables(root, args, sjinfo,
 					   &vardata1, &vardata2, &join_is_reversed);
 
+	selec =	join_is_reversed
+		? eqjoin_selectivity(root, operator, &vardata2, &vardata1, sjinfo)
+		: eqjoin_selectivity(root, operator, &vardata1, &vardata2, sjinfo);
+
+	ReleaseVariableStats(vardata1);
+	ReleaseVariableStats(vardata2);
+
+	PG_RETURN_FLOAT8((float8)selec);
+}
+
+Selectivity
+eqjoin_selectivity(PlannerInfo *root, Oid operator, VariableStatData* vardata1, VariableStatData* vardata2, SpecialJoinInfo *sjinfo)
+{
+	Selectivity	selec;
+	RelOptInfo *inner_rel;
+
 	switch (sjinfo->jointype)
 	{
 		case JOIN_INNER:
 		case JOIN_LEFT:
 		case JOIN_FULL:
-			selec = eqjoinsel_inner(operator, &vardata1, &vardata2);
+			selec = eqjoinsel_inner(operator, vardata1, vardata2);
 			break;
 		case JOIN_SEMI:
 		case JOIN_ANTI:
@@ -2235,13 +2259,7 @@ eqjoinsel(PG_FUNCTION_ARGS)
 			 */
 			inner_rel = find_join_input_rel(root, sjinfo->min_righthand);
 
-			if (!join_is_reversed)
-				selec = eqjoinsel_semi(operator, &vardata1, &vardata2,
-									   inner_rel);
-			else
-				selec = eqjoinsel_semi(get_commutator(operator),
-									   &vardata2, &vardata1,
-									   inner_rel);
+			selec = eqjoinsel_semi(operator, vardata1, vardata2, inner_rel);
 			break;
 		default:
 			/* other values not expected here */
@@ -2251,12 +2269,9 @@ eqjoinsel(PG_FUNCTION_ARGS)
 			break;
 	}
 
-	ReleaseVariableStats(vardata1);
-	ReleaseVariableStats(vardata2);
-
 	CLAMP_PROBABILITY(selec);
 
-	PG_RETURN_FLOAT8((float8) selec);
+	return selec;
 }
 
 /*
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index a7b07827e0..7bf031ce60 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -180,6 +180,7 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt,
 
 	/* Otherwise we need the pg_proc entry */
 	procedureTuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionId));
+	Assert(procedureTuple);
 	if (!HeapTupleIsValid(procedureTuple))
 		elog(ERROR, "cache lookup failed for function %u", functionId);
 	procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index 57acc1932f..b511423bca 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -1146,6 +1146,25 @@ repairDependencyLoop(DumpableObject **loop,
 		return;
 	}
 
+	/* Indirect loop between index and its generated type.
+	 * Originally types were generated only for tables,
+	 * but for better estimation of multicolumn join selectivity
+	 * types are also generated for compound indexes.
+	 * But indexes and tables are assigned difference priorities
+	 * DO_INDEX=28, DO_TABLE=18 which are on different sides of
+     * DO_PRE_DATA_BOUNDARY=26. The trick with introducing DO_DUMMY_TYPE=19
+	 * done in selectDumpableType doesn't work for types generated for indexes
+	 * and we got cyclic dependency here. So we have to explicitly break such loop
+	 * here to avoid circular constrains warning
+	 */
+	if (nLoop >= 2 &&
+		loop[0]->objType == DO_DUMMY_TYPE &&
+		loop[1]->objType == DO_INDEX)
+	{
+		removeObjectDependency(loop[0], loop[1]->dumpId);
+		return;
+	}
+
 	/* Indirect loop involving domain and CHECK constraint */
 	if (nLoop > 2)
 	{
diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h
index c7fdd540e8..0d20ef3561 100644
--- a/src/include/utils/selfuncs.h
+++ b/src/include/utils/selfuncs.h
@@ -221,5 +221,14 @@ extern Selectivity scalararraysel_containment(PlannerInfo *root,
 						   Node *leftop, Node *rightop,
 						   Oid elemtype, bool isEquality, bool useOr,
 						   int varRelid);
+extern Selectivity
+eqjoin_selectivity(PlannerInfo *root, Oid operator, VariableStatData* vardata1, VariableStatData* vardata2, SpecialJoinInfo *sjinfo);
+
+
+extern Selectivity
+eqconst_selectivity(Oid operator,
+					VariableStatData *vardata,
+					Datum constval, bool constisnull,
+					bool varonleft, bool negate);
 
 #endif							/* SELFUNCS_H */
diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out
index f60f76756a..e5e2a69264 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -2935,11 +2935,13 @@ RESET enable_indexonlyscan;
 --
 explain (costs off)
   select * from tenk1 where (thousand, tenthous) in ((1,1001), (null,null));
-                      QUERY PLAN                      
-------------------------------------------------------
- Index Scan using tenk1_thous_tenthous on tenk1
-   Index Cond: ((thousand = 1) AND (tenthous = 1001))
-(2 rows)
+                         QUERY PLAN                         
+------------------------------------------------------------
+ Bitmap Heap Scan on tenk1
+   Recheck Cond: ((thousand = 1) AND (tenthous = 1001))
+   ->  Bitmap Index Scan on tenk1_thous_tenthous
+         Index Cond: ((thousand = 1) AND (tenthous = 1001))
+(4 rows)
 
 --
 -- Check matching of boolean index columns to WHERE conditions and sort keys
diff --git a/src/test/regress/expected/stats_ext.out b/src/test/regress/expected/stats_ext.out
index 054a381dad..a4b0ce6dd8 100644
--- a/src/test/regress/expected/stats_ext.out
+++ b/src/test/regress/expected/stats_ext.out
@@ -445,19 +445,24 @@ INSERT INTO functional_dependencies (a, b, c, filler1)
 ANALYZE functional_dependencies;
 EXPLAIN (COSTS OFF)
  SELECT * FROM functional_dependencies WHERE a = 1 AND b = '1';
-                        QUERY PLAN                         
------------------------------------------------------------
- Index Scan using fdeps_abc_idx on functional_dependencies
-   Index Cond: ((a = 1) AND (b = '1'::text))
-(2 rows)
+                    QUERY PLAN                     
+---------------------------------------------------
+ Bitmap Heap Scan on functional_dependencies
+   Recheck Cond: ((a = 1) AND (b = '1'::text))
+   ->  Bitmap Index Scan on fdeps_abc_idx
+         Index Cond: ((a = 1) AND (b = '1'::text))
+(4 rows)
 
 EXPLAIN (COSTS OFF)
  SELECT * FROM functional_dependencies WHERE a = 1 AND b = '1' AND c = 1;
-                        QUERY PLAN                         
------------------------------------------------------------
- Index Scan using fdeps_abc_idx on functional_dependencies
-   Index Cond: ((a = 1) AND (b = '1'::text) AND (c = 1))
-(2 rows)
+                    QUERY PLAN                     
+---------------------------------------------------
+ Bitmap Heap Scan on functional_dependencies
+   Recheck Cond: ((a = 1) AND (b = '1'::text))
+   Filter: (c = 1)
+   ->  Bitmap Index Scan on fdeps_ab_idx
+         Index Cond: ((a = 1) AND (b = '1'::text))
+(5 rows)
 
 -- create statistics
 CREATE STATISTICS func_deps_stat (dependencies) ON a, b, c FROM functional_dependencies;
openSUSE Build Service is sponsored by