diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c
index 9126c18d0e..6bfce99ca2 100644
--- a/contrib/amcheck/verify_nbtree.c
+++ b/contrib/amcheck/verify_nbtree.c
@@ -514,7 +514,7 @@ bt_check_every_level(Relation rel, Relation heaprel, bool heapkeyspace,
 	 */
 	if (state->heapallindexed)
 	{
-		IndexInfo  *indexinfo = BuildIndexInfo(state->rel);
+		IndexInfo  *indexinfo = BuildIndexInfo(state->rel, true);
 		TableScanDesc scan;
 
 		/* Report on extra downlink checks performed in readonly case */
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index ae7b729edd..ab68840ebc 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -1370,7 +1370,7 @@ brinsummarize(Relation index, Relation heapRel, BlockNumber pageRange,
 				Assert(!indexInfo);
 				state = initialize_brin_buildstate(index, revmap,
 												   pagesPerRange);
-				indexInfo = BuildIndexInfo(index);
+				indexInfo = BuildIndexInfo(index, true);
 			}
 			summarize_range(indexInfo, state, heapRel, startBlk, heapNumBlocks);
 
diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/nbtree/nbtsort.c
index d0b9013caf..b73c40ee8b 100644
--- a/src/backend/access/nbtree/nbtsort.c
+++ b/src/backend/access/nbtree/nbtsort.c
@@ -1766,7 +1766,7 @@ _bt_parallel_scan_and_sort(BTSpool *btspool, BTSpool *btspool2,
 	buildstate.btleader = NULL;
 
 	/* Join parallel scan */
-	indexInfo = BuildIndexInfo(btspool->index);
+	indexInfo = BuildIndexInfo(btspool->index, true);
 	indexInfo->ii_Concurrent = btshared->isconcurrent;
 	scan = table_beginscan_parallel(btspool->heap,
 									ParallelTableScanFromBTShared(btshared));
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 032fab9ac4..4db8f0615e 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -3149,7 +3149,7 @@ RelationTruncateIndexes(Relation heapRelation)
 		currentIndex = index_open(indexId, AccessExclusiveLock);
 
 		/* Fetch info needed for index_build */
-		indexInfo = BuildIndexInfo(currentIndex);
+		indexInfo = BuildIndexInfo(currentIndex, true);
 
 		/*
 		 * Now truncate the actual file (and discard buffers).
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 9234e93261..80eaddb59a 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1212,7 +1212,7 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, const char
 	indexRelation = index_open(oldIndexId, RowExclusiveLock);
 
 	/* New index uses the same index information as old index */
-	indexInfo = BuildIndexInfo(indexRelation);
+	indexInfo = BuildIndexInfo(indexRelation, false);
 
 	/* Get the array of class and column options IDs from index info */
 	indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(oldIndexId));
@@ -1312,7 +1312,7 @@ index_concurrently_build(Oid heapRelationId,
 	 * commit of the transaction where this concurrent index was created at
 	 * the catalog level.
 	 */
-	indexInfo = BuildIndexInfo(indexRelation);
+	indexInfo = BuildIndexInfo(indexRelation, true);
 	Assert(!indexInfo->ii_ReadyForInserts);
 	indexInfo->ii_Concurrent = true;
 	indexInfo->ii_BrokenHotChain = false;
@@ -2162,10 +2162,14 @@ index_drop(Oid indexId, bool concurrent, bool concurrent_lock_mode)
  * FormIndexDatum, which is used for both index_build() and later insertion
  * of individual index tuples.  Normally we build an IndexInfo for an index
  * just once per command, and then use it for (potentially) many tuples.
+ *
+ * If "optimize" is true, index expressions and predicates are not made
+ * planner-ready.  This is used by REINDEX CONCURRENTLY when creating a
+ * duplicate index entry rebuilt concurrently.
  * ----------------
  */
 IndexInfo *
-BuildIndexInfo(Relation index)
+BuildIndexInfo(Relation index, bool optimize)
 {
 	IndexInfo  *ii = makeNode(IndexInfo);
 	Form_pg_index indexStruct = index->rd_index;
@@ -2186,11 +2190,11 @@ BuildIndexInfo(Relation index)
 		ii->ii_IndexAttrNumbers[i] = indexStruct->indkey.values[i];
 
 	/* fetch any expressions needed for expressional indexes */
-	ii->ii_Expressions = RelationGetIndexExpressions(index);
+	ii->ii_Expressions = RelationGetIndexExpressions(index, optimize);
 	ii->ii_ExpressionsState = NIL;
 
 	/* fetch index predicate if any */
-	ii->ii_Predicate = RelationGetIndexPredicate(index);
+	ii->ii_Predicate = RelationGetIndexPredicate(index, optimize);
 	ii->ii_PredicateState = NULL;
 
 	/* fetch exclusion constraint info if any */
@@ -3034,7 +3038,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot)
 	 * passed in from DefineIndex, but its copy is long gone due to having
 	 * been built in a previous transaction.)
 	 */
-	indexInfo = BuildIndexInfo(indexRelation);
+	indexInfo = BuildIndexInfo(indexRelation, true);
 
 	/* mark build is concurrent just for consistency */
 	indexInfo->ii_Concurrent = true;
@@ -3322,7 +3326,7 @@ reindex_index(Oid indexId, bool skip_constraint_checks, char persistence,
 	TransferPredicateLocksToHeapRelation(iRel);
 
 	/* Fetch info needed for index_build */
-	indexInfo = BuildIndexInfo(iRel);
+	indexInfo = BuildIndexInfo(iRel, true);
 
 	/* If requested, skip checking uniqueness/exclusion constraints */
 	if (skip_constraint_checks)
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 8d633f2585..3f5b05b325 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -434,7 +434,7 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
 			AnlIndexData *thisdata = &indexdata[ind];
 			IndexInfo  *indexInfo;
 
-			thisdata->indexInfo = indexInfo = BuildIndexInfo(Irel[ind]);
+			thisdata->indexInfo = indexInfo = BuildIndexInfo(Irel[ind], true);
 			thisdata->tupleFract = 1.0; /* fix later if partial */
 			if (indexInfo->ii_Expressions != NIL && va_cols == NIL)
 			{
diff --git a/src/backend/commands/constraint.c b/src/backend/commands/constraint.c
index 806962a686..05fef01e79 100644
--- a/src/backend/commands/constraint.c
+++ b/src/backend/commands/constraint.c
@@ -132,7 +132,7 @@ unique_key_recheck(PG_FUNCTION_ARGS)
 	 */
 	indexRel = index_open(trigdata->tg_trigger->tgconstrindid,
 						  RowExclusiveLock);
-	indexInfo = BuildIndexInfo(indexRel);
+	indexInfo = BuildIndexInfo(indexRel, true);
 
 	/*
 	 * Typically the index won't have expressions, but if it does we need an
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index fd299273c5..42675360ef 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -1121,7 +1121,7 @@ DefineIndex(Oid relationId,
 						continue;
 
 					cldidx = index_open(cldidxid, lockmode);
-					cldIdxInfo = BuildIndexInfo(cldidx);
+					cldIdxInfo = BuildIndexInfo(cldidx, true);
 					if (CompareIndexInfo(cldIdxInfo, indexInfo,
 										 cldidx->rd_indcollation,
 										 collationObjectId,
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index 537d0e8cef..5dfd06447e 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -871,7 +871,7 @@ is_usable_unique_index(Relation indexRel)
 		indexStruct->indimmediate &&
 		indexRel->rd_rel->relam == BTREE_AM_OID &&
 		indexStruct->indisvalid &&
-		RelationGetIndexPredicate(indexRel) == NIL &&
+		RelationGetIndexPredicate(indexRel, true) == NIL &&
 		indexStruct->indnatts > 0)
 	{
 		/*
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index fc1c4dfa4c..7ba1bfdfeb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -7242,7 +7242,7 @@ ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
 
 	indexName = pstrdup(RelationGetRelationName(indexRel));
 
-	indexInfo = BuildIndexInfo(indexRel);
+	indexInfo = BuildIndexInfo(indexRel, true);
 
 	/* this should have been checked at parse time */
 	if (!indexInfo->ii_Unique)
@@ -13817,13 +13817,13 @@ ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode
 				 errmsg("cannot use non-immediate index \"%s\" as replica identity",
 						RelationGetRelationName(indexRel))));
 	/* Expression indexes aren't supported. */
-	if (RelationGetIndexExpressions(indexRel) != NIL)
+	if (RelationGetIndexExpressions(indexRel, true) != NIL)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("cannot use expression index \"%s\" as replica identity",
 						RelationGetRelationName(indexRel))));
 	/* Predicate indexes aren't supported. */
-	if (RelationGetIndexPredicate(indexRel) != NIL)
+	if (RelationGetIndexPredicate(indexRel, true) != NIL)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("cannot use partial index \"%s\" as replica identity",
@@ -15725,7 +15725,7 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel)
 		Oid			cldIdxId = lfirst_oid(cell);
 
 		attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
-		attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
+		attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i], true);
 		i++;
 	}
 
@@ -15781,7 +15781,7 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel)
 		}
 
 		/* construct an indexinfo to compare existing indexes against */
-		info = BuildIndexInfo(idxRel);
+		info = BuildIndexInfo(idxRel, true);
 		attmap = convert_tuples_by_name_map(RelationGetDescr(attachrel),
 											RelationGetDescr(rel),
 											gettext_noop("could not convert row type"));
@@ -16357,8 +16357,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
 							   RelationGetRelationName(parentTbl))));
 
 		/* Ensure the indexes are compatible */
-		childInfo = BuildIndexInfo(partIdx);
-		parentInfo = BuildIndexInfo(parentIdx);
+		childInfo = BuildIndexInfo(partIdx, true);
+		parentInfo = BuildIndexInfo(parentIdx, true);
 		attmap = convert_tuples_by_name_map(RelationGetDescr(partTbl),
 											RelationGetDescr(parentTbl),
 											gettext_noop("could not convert row type"));
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 40bd8049f0..4158b02f04 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -199,7 +199,7 @@ ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative)
 		indexDesc = index_open(indexOid, RowExclusiveLock);
 
 		/* extract index key information from the index's pg_index info */
-		ii = BuildIndexInfo(indexDesc);
+		ii = BuildIndexInfo(indexDesc, true);
 
 		/*
 		 * If the indexes are to be used for speculative insertion, add extra
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index ca3b7f29e1..076530172e 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -6307,8 +6307,8 @@ plan_create_index_workers(Oid tableOid, Oid indexOid)
 	 * safe.
 	 */
 	if (heap->rd_rel->relpersistence == RELPERSISTENCE_TEMP ||
-		!is_parallel_safe(root, (Node *) RelationGetIndexExpressions(index)) ||
-		!is_parallel_safe(root, (Node *) RelationGetIndexPredicate(index)))
+		!is_parallel_safe(root, (Node *) RelationGetIndexExpressions(index, true)) ||
+		!is_parallel_safe(root, (Node *) RelationGetIndexPredicate(index, true)))
 	{
 		parallel_workers = 0;
 		goto done;
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 98e99481c6..93a300880c 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -368,8 +368,8 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 			 * correct varno for the parent relation, so that they match up
 			 * correctly against qual clauses.
 			 */
-			info->indexprs = RelationGetIndexExpressions(indexRelation);
-			info->indpred = RelationGetIndexPredicate(indexRelation);
+			info->indexprs = RelationGetIndexExpressions(indexRelation, true);
+			info->indpred = RelationGetIndexPredicate(indexRelation, true);
 			if (info->indexprs && varno != 1)
 				ChangeVarNodes((Node *) info->indexprs, 1, varno, 0);
 			if (info->indpred && varno != 1)
@@ -759,7 +759,7 @@ infer_arbiter_indexes(PlannerInfo *root)
 			goto next;
 
 		/* Expression attributes (if any) must match */
-		idxExprs = RelationGetIndexExpressions(idxRel);
+		idxExprs = RelationGetIndexExpressions(idxRel, true);
 		foreach(el, onconflict->arbiterElems)
 		{
 			InferenceElem *elem = (InferenceElem *) lfirst(el);
@@ -810,7 +810,7 @@ infer_arbiter_indexes(PlannerInfo *root)
 		 * If it's a partial index, its predicate must be implied by the ON
 		 * CONFLICT's WHERE clause.
 		 */
-		predExprs = RelationGetIndexPredicate(idxRel);
+		predExprs = RelationGetIndexPredicate(idxRel, true);
 
 		if (!predicate_implied_by(predExprs, (List *) onconflict->arbiterWhere, false))
 			goto next;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index f0f6ee7db6..4c03f38581 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -2078,14 +2078,14 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
 					 errdetail("Cannot create a primary key or unique constraint using such an index."),
 					 parser_errposition(cxt->pstate, constraint->location)));
 
-		if (RelationGetIndexExpressions(index_rel) != NIL)
+		if (RelationGetIndexExpressions(index_rel, true) != NIL)
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 					 errmsg("index \"%s\" contains expressions", index_name),
 					 errdetail("Cannot create a primary key or unique constraint using such an index."),
 					 parser_errposition(cxt->pstate, constraint->location)));
 
-		if (RelationGetIndexPredicate(index_rel) != NIL)
+		if (RelationGetIndexPredicate(index_rel, true) != NIL)
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 					 errmsg("\"%s\" is a partial index", index_name),
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 7aa5d7c7fa..5e478b785c 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -4570,9 +4570,13 @@ RelationGetReplicaIndex(Relation relation)
  * Otherwise, the returned tree is copied into the caller's memory context.
  * (We don't want to return a pointer to the relcache copy, since it could
  * disappear due to relcache invalidation.)
+ *
+ * If "optimize" is true, apply all optimizations to make this result
+ * usable by the planner.  Non-optimized expressions are currently used
+ * when creating a concurrent index for REINDEX CONCURRENTLY.
  */
 List *
-RelationGetIndexExpressions(Relation relation)
+RelationGetIndexExpressions(Relation relation, bool optimize)
 {
 	List	   *result;
 	Datum		exprsDatum;
@@ -4603,14 +4607,17 @@ RelationGetIndexExpressions(Relation relation)
 	result = (List *) stringToNode(exprsString);
 	pfree(exprsString);
 
-	/*
-	 * Run the expressions through eval_const_expressions. This is not just an
-	 * optimization, but is necessary, because the planner will be comparing
-	 * them to similarly-processed qual clauses, and may fail to detect valid
-	 * matches without this.  We must not use canonicalize_qual, however,
-	 * since these aren't qual expressions.
-	 */
-	result = (List *) eval_const_expressions(NULL, (Node *) result);
+	if (optimize)
+	{
+		/*
+		 * Run the expressions through eval_const_expressions. This is not just an
+		 * optimization, but is necessary, because the planner will be comparing
+		 * them to similarly-processed qual clauses, and may fail to detect valid
+		 * matches without this.  We must not use canonicalize_qual, however,
+		 * since these aren't qual expressions.
+		 */
+		result = (List *) eval_const_expressions(NULL, (Node *) result);
+	}
 
 	/* May as well fix opfuncids too */
 	fix_opfuncids((Node *) result);
@@ -4627,14 +4634,18 @@ RelationGetIndexExpressions(Relation relation)
  * RelationGetIndexPredicate -- get the index predicate for an index
  *
  * We cache the result of transforming pg_index.indpred into an implicit-AND
- * node tree (suitable for use in planning).
+ * node tree suitable for use in planning if "optimize" is true.
  * If the rel is not an index or has no predicate, we return NIL.
  * Otherwise, the returned tree is copied into the caller's memory context.
  * (We don't want to return a pointer to the relcache copy, since it could
  * disappear due to relcache invalidation.)
+ *
+ * If "optimize" is true, apply all optimizations to make this result
+ * usable by the planner.  Non-optimized expressions are currently used
+ * when creating a concurrent index for REINDEX CONCURRENTLY.
  */
 List *
-RelationGetIndexPredicate(Relation relation)
+RelationGetIndexPredicate(Relation relation, bool optimize)
 {
 	List	   *result;
 	Datum		predDatum;
@@ -4665,24 +4676,28 @@ RelationGetIndexPredicate(Relation relation)
 	result = (List *) stringToNode(predString);
 	pfree(predString);
 
-	/*
-	 * Run the expression through const-simplification and canonicalization.
-	 * This is not just an optimization, but is necessary, because the planner
-	 * will be comparing it to similarly-processed qual clauses, and may fail
-	 * to detect valid matches without this.  This must match the processing
-	 * done to qual clauses in preprocess_expression()!  (We can skip the
-	 * stuff involving subqueries, however, since we don't allow any in index
-	 * predicates.)
-	 */
-	result = (List *) eval_const_expressions(NULL, (Node *) result);
+	if (optimize)
+	{
+		/*
+		 * Run the expression through const-simplification and canonicalization.
+		 * This is not just an optimization, but is necessary, because the planner
+		 * will be comparing it to similarly-processed qual clauses, and may fail
+		 * to detect valid matches without this.  This must match the processing
+		 * done to qual clauses in preprocess_expression()!  (We can skip the
+		 * stuff involving subqueries, however, since we don't allow any in index
+		 * predicates.)
+		 */
+		result = (List *) eval_const_expressions(NULL, (Node *) result);
 
-	result = (List *) canonicalize_qual((Expr *) result, false);
+		result = (List *) canonicalize_qual((Expr *) result, false);
+	}
 
 	/* Also convert to implicit-AND format */
 	result = make_ands_implicit((Expr *) result);
 
 	/* May as well fix opfuncids too */
-	fix_opfuncids((Node *) result);
+	if (optimize)
+		fix_opfuncids((Node *) result);
 
 	/* Now save a copy of the completed tree in the relcache entry. */
 	oldcxt = MemoryContextSwitchTo(relation->rd_indexcxt);
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index 7b8e67899e..cd963fda3f 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -915,7 +915,7 @@ tuplesort_begin_cluster(TupleDesc tupDesc,
 	state->readtup = readtup_cluster;
 	state->abbrevNext = 10;
 
-	state->indexInfo = BuildIndexInfo(indexRel);
+	state->indexInfo = BuildIndexInfo(indexRel, true);
 
 	state->tupDesc = tupDesc;	/* assume we need not copy tupDesc */
 
diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h
index 1113d25b2d..5b9d429e9f 100644
--- a/src/include/catalog/index.h
+++ b/src/include/catalog/index.h
@@ -104,7 +104,7 @@ extern ObjectAddress index_constraint_create(Relation heapRelation,
 
 extern void index_drop(Oid indexId, bool concurrent, bool concurrent_lock_mode);
 
-extern IndexInfo *BuildIndexInfo(Relation index);
+extern IndexInfo *BuildIndexInfo(Relation index, bool optimize);
 
 extern bool CompareIndexInfo(IndexInfo *info1, IndexInfo *info2,
 							 Oid *collations1, Oid *collations2,
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index d9c10ffcba..ee96bf0d36 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -47,8 +47,8 @@ extern List *RelationGetIndexList(Relation relation);
 extern List *RelationGetStatExtList(Relation relation);
 extern Oid	RelationGetPrimaryKeyIndex(Relation relation);
 extern Oid	RelationGetReplicaIndex(Relation relation);
-extern List *RelationGetIndexExpressions(Relation relation);
-extern List *RelationGetIndexPredicate(Relation relation);
+extern List *RelationGetIndexExpressions(Relation relation, bool optimize);
+extern List *RelationGetIndexPredicate(Relation relation, bool optimize);
 
 typedef enum IndexAttrBitmapKind
 {
diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out
index 9305649c11..ab419972c8 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -2170,6 +2170,40 @@ Indexes:
     "concur_reindex_ind5" UNIQUE, btree (c1)
 
 DROP TABLE concur_reindex_tab4;
+-- Check handling of indexes with expressions and predicates.
+-- Run a couple of commands, then the state of the expression and
+-- predicate tree stored in the catalogs before and after the rebuilds.
+-- Note that the index name is used for the tree comparison as the OID
+-- of an index rebuilt changes.
+CREATE TEMP TABLE concur_reindex_trees (
+  relname text,
+  indexprs_before pg_node_tree,
+  indexprs_after pg_node_tree,
+  indpred_before pg_node_tree,
+  indpred_after pg_node_tree);
+CREATE TABLE concur_exprs_tab (c1 int , c2 boolean);
+INSERT INTO concur_exprs_tab (c1, c2) VALUES (1369652450, FALSE),
+  (414515746, TRUE),
+  (897778963, FALSE);
+CREATE UNIQUE INDEX concur_exprs_index ON concur_exprs_tab ((1 / c1))
+  WHERE ('-H') >= (c2::TEXT) COLLATE "C";
+INSERT INTO concur_reindex_trees (relname, indexprs_before, indpred_before)
+  SELECT indexrelid::regclass, indexprs, indpred FROM pg_index
+    WHERE indexrelid = 'concur_exprs_index'::regclass;
+REINDEX TABLE CONCURRENTLY concur_exprs_tab;
+UPDATE concur_reindex_trees SET (indexprs_after, indpred_after) =
+  (SELECT indexprs, indpred FROM pg_index WHERE indexrelid = 'concur_exprs_index'::regclass)
+  WHERE relname = 'concur_exprs_index';
+SELECT relname,
+    indexprs_before = indexprs_after AS same_exprs,
+    indpred_before = indpred_after AS same_pred
+  FROM concur_reindex_trees;
+      relname       | same_exprs | same_pred 
+--------------------+------------+-----------
+ concur_exprs_index | f          | f
+(1 row)
+
+DROP TABLE concur_exprs_tab;
 --
 -- REINDEX SCHEMA
 --
diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql
index cd46f071bd..c92e107b35 100644
--- a/src/test/regress/sql/create_index.sql
+++ b/src/test/regress/sql/create_index.sql
@@ -872,6 +872,36 @@ REINDEX INDEX CONCURRENTLY concur_reindex_ind5;
 \d concur_reindex_tab4
 DROP TABLE concur_reindex_tab4;
 
+-- Check handling of indexes with expressions and predicates.
+-- Run a couple of commands, then the state of the expression and
+-- predicate tree stored in the catalogs before and after the rebuilds.
+-- Note that the index name is used for the tree comparison as the OID
+-- of an index rebuilt changes.
+CREATE TEMP TABLE concur_reindex_trees (
+  relname text,
+  indexprs_before pg_node_tree,
+  indexprs_after pg_node_tree,
+  indpred_before pg_node_tree,
+  indpred_after pg_node_tree);
+CREATE TABLE concur_exprs_tab (c1 int , c2 boolean);
+INSERT INTO concur_exprs_tab (c1, c2) VALUES (1369652450, FALSE),
+  (414515746, TRUE),
+  (897778963, FALSE);
+CREATE UNIQUE INDEX concur_exprs_index ON concur_exprs_tab ((1 / c1))
+  WHERE ('-H') >= (c2::TEXT) COLLATE "C";
+INSERT INTO concur_reindex_trees (relname, indexprs_before, indpred_before)
+  SELECT indexrelid::regclass, indexprs, indpred FROM pg_index
+    WHERE indexrelid = 'concur_exprs_index'::regclass;
+REINDEX TABLE CONCURRENTLY concur_exprs_tab;
+UPDATE concur_reindex_trees SET (indexprs_after, indpred_after) =
+  (SELECT indexprs, indpred FROM pg_index WHERE indexrelid = 'concur_exprs_index'::regclass)
+  WHERE relname = 'concur_exprs_index';
+SELECT relname,
+    indexprs_before = indexprs_after AS same_exprs,
+    indpred_before = indpred_after AS same_pred
+  FROM concur_reindex_trees;
+DROP TABLE concur_exprs_tab;
+
 --
 -- REINDEX SCHEMA
 --
