diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index 2fe9867353..2525667418 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -23,6 +23,7 @@
 #include "miscadmin.h"
 #include "storage/lmgr.h"
 #include "storage/predicate.h"
+#include "storage/smgr.h"
 #include "utils/tqual.h"
 
 
@@ -85,7 +86,6 @@ static bool _bt_isequal(TupleDesc itupdesc, Page page, OffsetNumber offnum,
 			int keysz, ScanKey scankey);
 static void _bt_vacuum_one_page(Relation rel, Buffer buffer, Relation heapRel);
 
-
 /*
  *	_bt_doinsert() -- Handle insertion of a single index tuple in the tree.
  *
@@ -111,32 +111,95 @@ _bt_doinsert(Relation rel, IndexTuple itup,
 	bool		is_unique = false;
 	int			natts = rel->rd_rel->relnatts;
 	ScanKey		itup_scankey;
-	BTStack		stack;
+	BTStack		stack = NULL;
 	Buffer		buf;
 	OffsetNumber offset;
+	bool		fastpath;
 
 	/* we need an insertion scan key to do our search, so build one */
 	itup_scankey = _bt_mkscankey(rel, itup);
 
+	/*
+	 * It's very common to have an index on an auto-incremented or
+	 * monotonically increasing value. In such cases, every insertion happens
+	 * to the end of the index. We try to optimise that case by caching the
+	 * right-most block of the index. If our cached block is still the
+	 * rightmost block, has enough free space to accommodate a new entry and
+	 * the insertion key is greater or equal to the first key in this page,
+	 * then we can simply insert the new insertion key in the cached block. We
+	 * call it a fastpath.
+	 */
 top:
-	/* find the first page containing this key */
-	stack = _bt_search(rel, natts, itup_scankey, false, &buf, BT_WRITE, NULL);
+	fastpath = false;
+	if (RelationGetTargetBlock(rel) != InvalidBlockNumber)
+	{
+		Size 			itemsz;
+		Page			page;
+		BTPageOpaque	lpageop;
 
-	offset = InvalidOffsetNumber;
+		/*
+		 * Acquire exclusive lock on the buffer before doing any checks. This
+		 * ensures that the index state cannot change, as far as the rightmost
+		 * part of the index is concerned.
+		 */
+		buf = _bt_getbuf(rel, RelationGetTargetBlock(rel), BT_WRITE);
+		page = BufferGetPage(buf);
 
-	/* trade in our read lock for a write lock */
-	LockBuffer(buf, BUFFER_LOCK_UNLOCK);
-	LockBuffer(buf, BT_WRITE);
+		lpageop = (BTPageOpaque) PageGetSpecialPointer(page);
+		itemsz = IndexTupleSize(itup);
+		itemsz = MAXALIGN(itemsz);	/* be safe, PageAddItem will do this but we
+									 * need to be consistent */
 
-	/*
-	 * If the page was split between the time that we surrendered our read
-	 * lock and acquired our write lock, then this page may no longer be the
-	 * right place for the key we want to insert.  In this case, we need to
-	 * move right in the tree.  See Lehman and Yao for an excruciatingly
-	 * precise description.
-	 */
-	buf = _bt_moveright(rel, buf, natts, itup_scankey, false,
-						true, stack, BT_WRITE, NULL);
+		/*
+		 * Check if the page is still the rightmost leaf page, has enough free
+		 * space to accommodate the new tuple, no split is in progress and the
+		 * scankey is greater than or equal to the first key on the page.
+		 */
+		if (P_ISLEAF(lpageop) && P_RIGHTMOST(lpageop) &&
+			!P_INCOMPLETE_SPLIT(lpageop) &&
+			!P_IGNORE(lpageop) &&
+			(PageGetFreeSpace(page) > itemsz) &&
+			_bt_compare(rel, natts, itup_scankey, page,
+						RelationGetLastOffset(rel)) >= 0)
+		{
+			offset = InvalidOffsetNumber;
+			fastpath = true;
+		}
+		else
+		{
+			_bt_relbuf(rel, buf);
+
+			/*
+			 * Something did not workout. Just forget about the cached block
+			 * and follow the normal path. It might be set again if the
+			 * conditions are favourble.
+			 */
+			RelationSetTargetBlock(rel, InvalidBlockNumber);
+		}
+	}
+
+	if (!fastpath)
+	{
+		/* find the first page containing this key */
+		stack = _bt_search(rel, natts, itup_scankey, false, &buf, BT_WRITE,
+						   NULL);
+
+		offset = InvalidOffsetNumber;
+
+		/* trade in our read lock for a write lock */
+		LockBuffer(buf, BUFFER_LOCK_UNLOCK);
+		LockBuffer(buf, BT_WRITE);
+
+		/*
+		 * If the page was split between the time that we surrendered our read
+		 * lock and acquired our write lock, then this page may no longer be
+		 * the right place for the key we want to insert.  In this case, we
+		 * need to move right in the tree.  See Lehman and Yao for an
+		 * excruciatingly precise description.
+		 */
+		buf = _bt_moveright(rel, buf, natts, itup_scankey, false,
+							true, stack, BT_WRITE, NULL);
+	}
 
 	/*
 	 * If we're not allowing duplicates, make sure the key isn't already in
@@ -184,7 +247,8 @@ top:
 				XactLockTableWait(xwait, rel, &itup->t_tid, XLTW_InsertIndex);
 
 			/* start over... */
-			_bt_freestack(stack);
+			if (stack)
+				_bt_freestack(stack);
 			goto top;
 		}
 	}
@@ -211,7 +275,8 @@ top:
 	}
 
 	/* be tidy */
-	_bt_freestack(stack);
+	if (stack)
+		_bt_freestack(stack);
 	_bt_freeskey(itup_scankey);
 
 	return is_unique;
@@ -879,7 +944,19 @@ _bt_insertonpg(Relation rel,
 			XLogRegisterData((char *) &xlrec, SizeOfBtreeInsert);
 
 			if (P_ISLEAF(lpageop))
+			{
 				xlinfo = XLOG_BTREE_INSERT_LEAF;
+
+				/*
+				 * Cache the block information if we just inserted into the
+				 * rightmost leaf page of the index.
+				 */
+				if (P_RIGHTMOST(lpageop))
+				{
+					RelationSetTargetBlock(rel, BufferGetBlockNumber(buf));
+					RelationSetLastOffset(rel, itup_off);
+				}
+			}
 			else
 			{
 				/*
diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h
index 558e4d8518..78452920d1 100644
--- a/src/include/storage/smgr.h
+++ b/src/include/storage/smgr.h
@@ -16,6 +16,7 @@
 
 #include "fmgr.h"
 #include "storage/block.h"
+#include "storage/off.h"
 #include "storage/relfilenode.h"
 
 
@@ -53,6 +54,7 @@ typedef struct SMgrRelationData
 	 * happens.  In all three cases, InvalidBlockNumber means "unknown".
 	 */
 	BlockNumber smgr_targblock; /* current insertion target block */
+	OffsetNumber	smgr_lastoffnum;	/* last insertion offset */
 	BlockNumber smgr_fsm_nblocks;	/* last known size of fsm fork */
 	BlockNumber smgr_vm_nblocks;	/* last known size of vm fork */
 
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index aa8add544a..6814b4b118 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -507,6 +507,16 @@ typedef struct ViewOptions
 		(relation)->rd_smgr->smgr_targblock = (targblock); \
 	} while (0)
 
+#define RelationGetLastOffset(relation) \
+	( (relation)->rd_smgr != NULL ? (relation)->rd_smgr->smgr_lastoffnum : InvalidOffsetNumber)
+
+#define RelationSetLastOffset(relation, offnum) \
+	do { \
+		RelationOpenSmgr(relation); \
+		(relation)->rd_smgr->smgr_lastoffnum = (offnum); \
+	} while (0)
+
+
 /*
  * RelationNeedsWAL
  *		True if relation needs WAL.
diff --git a/src/test/regress/expected/indexing.out b/src/test/regress/expected/indexing.out
index fb32ffffdc..f21699bc9a 100644
--- a/src/test/regress/expected/indexing.out
+++ b/src/test/regress/expected/indexing.out
@@ -1067,6 +1067,241 @@ select tableoid::regclass, * from idxpart order by a;
 (8 rows)
 
 drop table idxpart;
+-- test fastpath mechanism for index insertion
+create table fastpath (a int, b text, c numeric);
+create unique index fpindex1 on fastpath(a);
+insert into fastpath values (1, 'b1', 100.00);
+insert into fastpath values (1, 'b1', 100.00); -- unique key check
+ERROR:  duplicate key value violates unique constraint "fpindex1"
+DETAIL:  Key (a)=(1) already exists.
+truncate fastpath;
+insert into fastpath select generate_series(1,10000), 'b', 100;
+vacuum fastpath;
+set enable_seqscan to false;
+set enable_bitmapscan to false;
+explain select sum(a) from fastpath where a = 6456;
+                                     QUERY PLAN                                     
+------------------------------------------------------------------------------------
+ Aggregate  (cost=4.31..4.32 rows=1 width=8)
+   ->  Index Only Scan using fpindex1 on fastpath  (cost=0.29..4.30 rows=1 width=4)
+         Index Cond: (a = 6456)
+(3 rows)
+
+explain select sum(a) from fastpath where a >= 5000 and a < 5700;
+                                     QUERY PLAN                                      
+-------------------------------------------------------------------------------------
+ Aggregate  (cost=5.41..5.42 rows=1 width=8)
+   ->  Index Only Scan using fpindex1 on fastpath  (cost=0.29..5.29 rows=50 width=4)
+         Index Cond: ((a >= 5000) AND (a < 5700))
+(3 rows)
+
+select sum(a) from fastpath where a = 6456;
+ sum  
+------
+ 6456
+(1 row)
+
+select sum(a) from fastpath where a >= 5000 and a < 5700;
+   sum   
+---------
+ 3744650
+(1 row)
+
+drop index fpindex1;
+create index fpindex2 on fastpath(a, b);
+truncate fastpath;
+insert into fastpath select y.x, 'b' || (y.x/10)::text, 100 from (select generate_series(1,10000) as x) y;;
+vacuum fastpath;
+explain select md5(string_agg(a::text, b order by a, b asc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+                                         QUERY PLAN                                         
+--------------------------------------------------------------------------------------------
+ Aggregate  (cost=5.06..5.07 rows=1 width=32)
+   ->  Index Only Scan using fpindex2 on fastpath  (cost=0.29..5.04 rows=1 width=36)
+         Index Cond: ((a >= 1000) AND (a < 2000) AND (b > 'b1'::text) AND (b < 'b3'::text))
+(3 rows)
+
+select md5(string_agg(a::text, b order by a, b asc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+               md5                
+----------------------------------
+ 2ca216010a558a52d7df12f76dfc77ab
+(1 row)
+
+explain select md5(string_agg(a::text, b order by a, b desc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+                                         QUERY PLAN                                         
+--------------------------------------------------------------------------------------------
+ Aggregate  (cost=5.06..5.07 rows=1 width=32)
+   ->  Index Only Scan using fpindex2 on fastpath  (cost=0.29..5.04 rows=1 width=36)
+         Index Cond: ((a >= 1000) AND (a < 2000) AND (b > 'b1'::text) AND (b < 'b3'::text))
+(3 rows)
+
+select md5(string_agg(a::text, b order by a, b desc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+               md5                
+----------------------------------
+ 2ca216010a558a52d7df12f76dfc77ab
+(1 row)
+
+explain select md5(string_agg(a::text, b order by a desc, b asc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+                                         QUERY PLAN                                         
+--------------------------------------------------------------------------------------------
+ Aggregate  (cost=5.06..5.07 rows=1 width=32)
+   ->  Index Only Scan using fpindex2 on fastpath  (cost=0.29..5.04 rows=1 width=36)
+         Index Cond: ((a >= 1000) AND (a < 2000) AND (b > 'b1'::text) AND (b < 'b3'::text))
+(3 rows)
+
+select md5(string_agg(a::text, b order by a desc, b asc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+               md5                
+----------------------------------
+ 6167a852b3e0679886b84a5405b5b53d
+(1 row)
+
+explain select md5(string_agg(a::text, b order by a desc, b desc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+                                         QUERY PLAN                                         
+--------------------------------------------------------------------------------------------
+ Aggregate  (cost=5.06..5.07 rows=1 width=32)
+   ->  Index Only Scan using fpindex2 on fastpath  (cost=0.29..5.04 rows=1 width=36)
+         Index Cond: ((a >= 1000) AND (a < 2000) AND (b > 'b1'::text) AND (b < 'b3'::text))
+(3 rows)
+
+select md5(string_agg(a::text, b order by a desc, b desc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+               md5                
+----------------------------------
+ 6167a852b3e0679886b84a5405b5b53d
+(1 row)
+
+drop index fpindex2;
+create index fpindex3 on fastpath(a desc, b asc);
+explain select md5(string_agg(a::text, b order by a, b asc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+                                         QUERY PLAN                                         
+--------------------------------------------------------------------------------------------
+ Aggregate  (cost=5.06..5.07 rows=1 width=32)
+   ->  Index Only Scan using fpindex3 on fastpath  (cost=0.29..5.04 rows=1 width=36)
+         Index Cond: ((a >= 1000) AND (a < 2000) AND (b > 'b1'::text) AND (b < 'b3'::text))
+(3 rows)
+
+select md5(string_agg(a::text, b order by a, b asc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+               md5                
+----------------------------------
+ 2ca216010a558a52d7df12f76dfc77ab
+(1 row)
+
+explain select md5(string_agg(a::text, b order by a, b desc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+                                         QUERY PLAN                                         
+--------------------------------------------------------------------------------------------
+ Aggregate  (cost=5.06..5.07 rows=1 width=32)
+   ->  Index Only Scan using fpindex3 on fastpath  (cost=0.29..5.04 rows=1 width=36)
+         Index Cond: ((a >= 1000) AND (a < 2000) AND (b > 'b1'::text) AND (b < 'b3'::text))
+(3 rows)
+
+select md5(string_agg(a::text, b order by a, b desc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+               md5                
+----------------------------------
+ 2ca216010a558a52d7df12f76dfc77ab
+(1 row)
+
+explain select md5(string_agg(a::text, b order by a desc, b asc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+                                         QUERY PLAN                                         
+--------------------------------------------------------------------------------------------
+ Aggregate  (cost=5.06..5.07 rows=1 width=32)
+   ->  Index Only Scan using fpindex3 on fastpath  (cost=0.29..5.04 rows=1 width=36)
+         Index Cond: ((a >= 1000) AND (a < 2000) AND (b > 'b1'::text) AND (b < 'b3'::text))
+(3 rows)
+
+select md5(string_agg(a::text, b order by a desc, b asc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+               md5                
+----------------------------------
+ 6167a852b3e0679886b84a5405b5b53d
+(1 row)
+
+explain select md5(string_agg(a::text, b order by a desc, b desc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+                                         QUERY PLAN                                         
+--------------------------------------------------------------------------------------------
+ Aggregate  (cost=5.06..5.07 rows=1 width=32)
+   ->  Index Only Scan using fpindex3 on fastpath  (cost=0.29..5.04 rows=1 width=36)
+         Index Cond: ((a >= 1000) AND (a < 2000) AND (b > 'b1'::text) AND (b < 'b3'::text))
+(3 rows)
+
+drop index fpindex3;
+create index fpindex4 on fastpath(a asc, b desc);
+explain select md5(string_agg(a::text, b order by a, b asc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+                                         QUERY PLAN                                         
+--------------------------------------------------------------------------------------------
+ Aggregate  (cost=5.06..5.07 rows=1 width=32)
+   ->  Index Only Scan using fpindex4 on fastpath  (cost=0.29..5.04 rows=1 width=36)
+         Index Cond: ((a >= 1000) AND (a < 2000) AND (b > 'b1'::text) AND (b < 'b3'::text))
+(3 rows)
+
+select md5(string_agg(a::text, b order by a, b asc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+               md5                
+----------------------------------
+ 2ca216010a558a52d7df12f76dfc77ab
+(1 row)
+
+explain select md5(string_agg(a::text, b order by a, b desc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+                                         QUERY PLAN                                         
+--------------------------------------------------------------------------------------------
+ Aggregate  (cost=5.06..5.07 rows=1 width=32)
+   ->  Index Only Scan using fpindex4 on fastpath  (cost=0.29..5.04 rows=1 width=36)
+         Index Cond: ((a >= 1000) AND (a < 2000) AND (b > 'b1'::text) AND (b < 'b3'::text))
+(3 rows)
+
+select md5(string_agg(a::text, b order by a, b desc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+               md5                
+----------------------------------
+ 2ca216010a558a52d7df12f76dfc77ab
+(1 row)
+
+explain select md5(string_agg(a::text, b order by a desc, b asc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+                                         QUERY PLAN                                         
+--------------------------------------------------------------------------------------------
+ Aggregate  (cost=5.06..5.07 rows=1 width=32)
+   ->  Index Only Scan using fpindex4 on fastpath  (cost=0.29..5.04 rows=1 width=36)
+         Index Cond: ((a >= 1000) AND (a < 2000) AND (b > 'b1'::text) AND (b < 'b3'::text))
+(3 rows)
+
+select md5(string_agg(a::text, b order by a desc, b asc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+               md5                
+----------------------------------
+ 6167a852b3e0679886b84a5405b5b53d
+(1 row)
+
+explain select md5(string_agg(a::text, b order by a desc, b desc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+                                         QUERY PLAN                                         
+--------------------------------------------------------------------------------------------
+ Aggregate  (cost=5.06..5.07 rows=1 width=32)
+   ->  Index Only Scan using fpindex4 on fastpath  (cost=0.29..5.04 rows=1 width=36)
+         Index Cond: ((a >= 1000) AND (a < 2000) AND (b > 'b1'::text) AND (b < 'b3'::text))
+(3 rows)
+
+select md5(string_agg(a::text, b order by a desc, b desc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+               md5                
+----------------------------------
+ 6167a852b3e0679886b84a5405b5b53d
+(1 row)
+
+drop table fastpath;
 -- intentionally leave some objects around
 create table idxpart (a int) partition by range (a);
 create table idxpart1 partition of idxpart for values from (0) to (100);
diff --git a/src/test/regress/sql/indexing.sql b/src/test/regress/sql/indexing.sql
index c4ab89fc48..f8cf72c4e7 100644
--- a/src/test/regress/sql/indexing.sql
+++ b/src/test/regress/sql/indexing.sql
@@ -574,6 +574,87 @@ insert into idxpart values (857142, 'six');
 select tableoid::regclass, * from idxpart order by a;
 drop table idxpart;
 
+-- test fastpath mechanism for index insertion
+create table fastpath (a int, b text, c numeric);
+create unique index fpindex1 on fastpath(a);
+
+insert into fastpath values (1, 'b1', 100.00);
+insert into fastpath values (1, 'b1', 100.00); -- unique key check
+
+truncate fastpath;
+insert into fastpath select generate_series(1,10000), 'b', 100;
+
+vacuum fastpath;
+
+set enable_seqscan to false;
+set enable_bitmapscan to false;
+
+explain select sum(a) from fastpath where a = 6456;
+explain select sum(a) from fastpath where a >= 5000 and a < 5700;
+select sum(a) from fastpath where a = 6456;
+select sum(a) from fastpath where a >= 5000 and a < 5700;
+
+drop index fpindex1;
+create index fpindex2 on fastpath(a, b);
+truncate fastpath;
+insert into fastpath select y.x, 'b' || (y.x/10)::text, 100 from (select generate_series(1,10000) as x) y;;
+vacuum fastpath;
+
+explain select md5(string_agg(a::text, b order by a, b asc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+select md5(string_agg(a::text, b order by a, b asc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+explain select md5(string_agg(a::text, b order by a, b desc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+select md5(string_agg(a::text, b order by a, b desc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+explain select md5(string_agg(a::text, b order by a desc, b asc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+select md5(string_agg(a::text, b order by a desc, b asc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+explain select md5(string_agg(a::text, b order by a desc, b desc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+select md5(string_agg(a::text, b order by a desc, b desc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+
+drop index fpindex2;
+create index fpindex3 on fastpath(a desc, b asc);
+explain select md5(string_agg(a::text, b order by a, b asc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+select md5(string_agg(a::text, b order by a, b asc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+explain select md5(string_agg(a::text, b order by a, b desc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+select md5(string_agg(a::text, b order by a, b desc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+explain select md5(string_agg(a::text, b order by a desc, b asc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+select md5(string_agg(a::text, b order by a desc, b asc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+explain select md5(string_agg(a::text, b order by a desc, b desc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+
+drop index fpindex3;
+create index fpindex4 on fastpath(a asc, b desc);
+explain select md5(string_agg(a::text, b order by a, b asc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+select md5(string_agg(a::text, b order by a, b asc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+explain select md5(string_agg(a::text, b order by a, b desc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+select md5(string_agg(a::text, b order by a, b desc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+explain select md5(string_agg(a::text, b order by a desc, b asc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+select md5(string_agg(a::text, b order by a desc, b asc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+explain select md5(string_agg(a::text, b order by a desc, b desc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+select md5(string_agg(a::text, b order by a desc, b desc)) from fastpath
+	where a >= 1000 and a < 2000 and b > 'b1' and b < 'b3';
+
+drop table fastpath;
+
 -- intentionally leave some objects around
 create table idxpart (a int) partition by range (a);
 create table idxpart1 partition of idxpart for values from (0) to (100);
