diff --git a/src/backend/nodes/list.c b/src/backend/nodes/list.c
index 63cc815315..b8ee10017e 100644
--- a/src/backend/nodes/list.c
+++ b/src/backend/nodes/list.c
@@ -67,9 +67,14 @@ check_list_invariants(const List *list)
  * Since empty non-NIL lists are invalid, new_list() sets the initial length
  * to min_size, effectively marking that number of cells as valid; the caller
  * is responsible for filling in their data.
+ *
+ * forprepend marks we expect this list to be mostly built with lcons and
+ * friends.  In this case we'll mark the first property so that the first
+ * item to be added to the list is added at the end, leaving space in front
+ * for new items.
  */
 static List *
-new_list(NodeTag type, int min_size)
+new_list(NodeTag type, int min_size, bool forprepend)
 {
 	List	   *newlist;
 	int			max_size;
@@ -101,6 +106,7 @@ new_list(NodeTag type, int min_size)
 	newlist = (List *) palloc(sizeof(*newlist));
 	newlist->type = type;
 	newlist->length = min_size;
+	newlist->first = forprepend ? max_size - 1 : 0;
 	newlist->max_length = max_size;
 	newlist->elements = (ListCell *) palloc(max_size * sizeof(ListCell));
 
@@ -152,7 +158,7 @@ enlarge_list(List *list, int min_size)
 		ListCell   *newelements;
 
 		newelements = (ListCell *) palloc(new_max_len * sizeof(ListCell));
-		memcpy(newelements, list->elements,
+		memcpy(&newelements[list->first], &list->elements[list->first],
 			   list->length * sizeof(ListCell));
 		pfree(list->elements);
 		list->elements = newelements;
@@ -169,7 +175,7 @@ enlarge_list(List *list, int min_size)
 List *
 list_make1(void *datum1)
 {
-	List	   *list = new_list(T_List, 1);
+	List	   *list = new_list(T_List, 1, false);
 
 	lfirst(&list->elements[0]) = datum1;
 	check_list_invariants(list);
@@ -179,7 +185,7 @@ list_make1(void *datum1)
 List *
 list_make2(void *datum1, void *datum2)
 {
-	List	   *list = new_list(T_List, 2);
+	List	   *list = new_list(T_List, 2, false);
 
 	lfirst(&list->elements[0]) = datum1;
 	lfirst(&list->elements[1]) = datum2;
@@ -190,7 +196,7 @@ list_make2(void *datum1, void *datum2)
 List *
 list_make3(void *datum1, void *datum2, void *datum3)
 {
-	List	   *list = new_list(T_List, 3);
+	List	   *list = new_list(T_List, 3, false);
 
 	lfirst(&list->elements[0]) = datum1;
 	lfirst(&list->elements[1]) = datum2;
@@ -202,7 +208,7 @@ list_make3(void *datum1, void *datum2, void *datum3)
 List *
 list_make4(void *datum1, void *datum2, void *datum3, void *datum4)
 {
-	List	   *list = new_list(T_List, 4);
+	List	   *list = new_list(T_List, 4, false);
 
 	lfirst(&list->elements[0]) = datum1;
 	lfirst(&list->elements[1]) = datum2;
@@ -215,7 +221,7 @@ list_make4(void *datum1, void *datum2, void *datum3, void *datum4)
 List *
 list_make1_int(int datum1)
 {
-	List	   *list = new_list(T_IntList, 1);
+	List	   *list = new_list(T_IntList, 1, false);
 
 	lfirst_int(&list->elements[0]) = datum1;
 	check_list_invariants(list);
@@ -225,7 +231,7 @@ list_make1_int(int datum1)
 List *
 list_make2_int(int datum1, int datum2)
 {
-	List	   *list = new_list(T_IntList, 2);
+	List	   *list = new_list(T_IntList, 2, false);
 
 	lfirst_int(&list->elements[0]) = datum1;
 	lfirst_int(&list->elements[1]) = datum2;
@@ -236,7 +242,7 @@ list_make2_int(int datum1, int datum2)
 List *
 list_make3_int(int datum1, int datum2, int datum3)
 {
-	List	   *list = new_list(T_IntList, 3);
+	List	   *list = new_list(T_IntList, 3, false);
 
 	lfirst_int(&list->elements[0]) = datum1;
 	lfirst_int(&list->elements[1]) = datum2;
@@ -248,7 +254,7 @@ list_make3_int(int datum1, int datum2, int datum3)
 List *
 list_make4_int(int datum1, int datum2, int datum3, int datum4)
 {
-	List	   *list = new_list(T_IntList, 4);
+	List	   *list = new_list(T_IntList, 4, false);
 
 	lfirst_int(&list->elements[0]) = datum1;
 	lfirst_int(&list->elements[1]) = datum2;
@@ -261,7 +267,7 @@ list_make4_int(int datum1, int datum2, int datum3, int datum4)
 List *
 list_make1_oid(Oid datum1)
 {
-	List	   *list = new_list(T_OidList, 1);
+	List	   *list = new_list(T_OidList, 1, false);
 
 	lfirst_oid(&list->elements[0]) = datum1;
 	check_list_invariants(list);
@@ -271,7 +277,7 @@ list_make1_oid(Oid datum1)
 List *
 list_make2_oid(Oid datum1, Oid datum2)
 {
-	List	   *list = new_list(T_OidList, 2);
+	List	   *list = new_list(T_OidList, 2, false);
 
 	lfirst_oid(&list->elements[0]) = datum1;
 	lfirst_oid(&list->elements[1]) = datum2;
@@ -282,7 +288,7 @@ list_make2_oid(Oid datum1, Oid datum2)
 List *
 list_make3_oid(Oid datum1, Oid datum2, Oid datum3)
 {
-	List	   *list = new_list(T_OidList, 3);
+	List	   *list = new_list(T_OidList, 3, false);
 
 	lfirst_oid(&list->elements[0]) = datum1;
 	lfirst_oid(&list->elements[1]) = datum2;
@@ -294,7 +300,7 @@ list_make3_oid(Oid datum1, Oid datum2, Oid datum3)
 List *
 list_make4_oid(Oid datum1, Oid datum2, Oid datum3, Oid datum4)
 {
-	List	   *list = new_list(T_OidList, 4);
+	List	   *list = new_list(T_OidList, 4, false);
 
 	lfirst_oid(&list->elements[0]) = datum1;
 	lfirst_oid(&list->elements[1]) = datum2;
@@ -316,9 +322,15 @@ new_head_cell(List *list)
 	/* Enlarge array if necessary */
 	if (list->length >= list->max_length)
 		list = enlarge_list(list, list->length + 1);
-	/* Now shove the existing data over */
-	memmove(&list->elements[1], &list->elements[0],
-			list->length * sizeof(ListCell));
+	if (list->first > 0)
+		list->first -= 1;
+	else
+	{
+		/* Now shove the existing data to the end */
+		list->first = list->max_length - list->length - 1;
+		memmove(&list->elements[list->first + 1], &list->elements[0],
+				list->length * sizeof(ListCell));
+	}
 	list->length++;
 }
 
@@ -332,8 +344,8 @@ static void
 new_tail_cell(List *list)
 {
 	/* Enlarge array if necessary */
-	if (list->length >= list->max_length)
-		list = enlarge_list(list, list->length + 1);
+	if (list->first + list->length >= list->max_length)
+		list = enlarge_list(list, list->first + list->length + 1);
 	list->length++;
 }
 
@@ -350,7 +362,7 @@ lappend(List *list, void *datum)
 	Assert(IsPointerList(list));
 
 	if (list == NIL)
-		list = new_list(T_List, 1);
+		list = new_list(T_List, 1, false);
 	else
 		new_tail_cell(list);
 
@@ -368,7 +380,7 @@ lappend_int(List *list, int datum)
 	Assert(IsIntegerList(list));
 
 	if (list == NIL)
-		list = new_list(T_IntList, 1);
+		list = new_list(T_IntList, 1, false);
 	else
 		new_tail_cell(list);
 
@@ -386,7 +398,7 @@ lappend_oid(List *list, Oid datum)
 	Assert(IsOidList(list));
 
 	if (list == NIL)
-		list = new_list(T_OidList, 1);
+		list = new_list(T_OidList, 1, false);
 	else
 		new_tail_cell(list);
 
@@ -409,15 +421,17 @@ insert_new_cell(List *list, int pos)
 	Assert(pos >= 0 && pos <= list->length);
 
 	/* Enlarge array if necessary */
-	if (list->length >= list->max_length)
-		list = enlarge_list(list, list->length + 1);
+	if (list->first + list->length >= list->max_length)
+		list = enlarge_list(list, list->first + list->length + 1);
 	/* Now shove the existing data over */
-	if (pos < list->length)
-		memmove(&list->elements[pos + 1], &list->elements[pos],
+	if (pos == 0 && list->first > 0)
+		list->first -= 1;
+	else if (pos < list->length)
+		memmove(&list->elements[list->first + pos + 1], &list->elements[list->first + pos],
 				(list->length - pos) * sizeof(ListCell));
 	list->length++;
 
-	return &list->elements[pos];
+	return &list->elements[list->first + pos];
 }
 
 /*
@@ -476,7 +490,7 @@ list_insert_nth_oid(List *list, int pos, Oid datum)
 static ListCell *
 add_new_cell(List *list, ListCell *prev_cell)
 {
-	int			pos = prev_cell - list->elements;
+	int			pos = prev_cell - &list->elements[list->first];
 
 	return insert_new_cell(list, pos + 1);
 }
@@ -546,7 +560,7 @@ lcons(void *datum, List *list)
 	Assert(IsPointerList(list));
 
 	if (list == NIL)
-		list = new_list(T_List, 1);
+		list = new_list(T_List, 1, true);
 	else
 		new_head_cell(list);
 
@@ -564,7 +578,7 @@ lcons_int(int datum, List *list)
 	Assert(IsIntegerList(list));
 
 	if (list == NIL)
-		list = new_list(T_IntList, 1);
+		list = new_list(T_IntList, 1, true);
 	else
 		new_head_cell(list);
 
@@ -582,7 +596,7 @@ lcons_oid(Oid datum, List *list)
 	Assert(IsOidList(list));
 
 	if (list == NIL)
-		list = new_list(T_OidList, 1);
+		list = new_list(T_OidList, 1, true);
 	else
 		new_head_cell(list);
 
@@ -609,15 +623,19 @@ list_concat(List *list1, const List *list2)
 
 	Assert(list1->type == list2->type);
 
-	new_len = list1->length + list2->length;
+	new_len = list1->first + list1->length + list2->length;
 	/* Enlarge array if necessary */
 	if (new_len > list1->max_length)
 		list1 = enlarge_list(list1, new_len);
 
-	/* We use memmove in case list1 and list2 are the same list */
-	memmove(&list1->elements[list1->length], &list2->elements[0],
-			list2->length * sizeof(ListCell));
-	list1->length = new_len;
+	/*
+	 * memcpy here is fine even if the lists are the same. We're never going
+	 * to overwrite used elements.
+	 */
+	memcpy(&list1->elements[list1->first + list1->length],
+		   &list2->elements[list2->first],
+		   list2->length * sizeof(ListCell));
+	list1->length += list2->length;
 
 	check_list_invariants(list1);
 	return list1;
@@ -751,11 +769,19 @@ list_delete_nth_cell(List *list, int n)
 		return NIL;
 	}
 
-	/*
-	 * Otherwise, collapse out the removed element.
-	 */
-	memmove(&list->elements[n], &list->elements[n + 1],
-			(list->length - 1 - n) * sizeof(ListCell));
+	if (n == 0)
+	{
+		list->first += 1;
+	}
+	else
+	{
+		/*
+		 * Otherwise, collapse out the removed element.
+		 */
+		memmove(&list->elements[list->first + n],
+				&list->elements[list->first + n + 1],
+				(list->length - 1 - n) * sizeof(ListCell));
+	}
 	list->length--;
 
 	return list;
@@ -774,7 +800,7 @@ list_delete_cell(List *list, ListCell *cell)
 
 	check_list_invariants(list);
 
-	pos = cell - list->elements;
+	pos = cell - &list->elements[list->first];
 	Assert(pos >= 0 && pos < list->length);
 
 	/*
@@ -788,11 +814,18 @@ list_delete_cell(List *list, ListCell *cell)
 		return NIL;
 	}
 
-	/*
-	 * Otherwise, collapse out the removed element.
-	 */
-	memmove(&list->elements[pos], &list->elements[pos + 1],
-			(list->length - 1 - pos) * sizeof(ListCell));
+	if (pos == 0)
+	{
+		list->first += 1;
+	}
+	else
+	{
+		/*
+		 * Otherwise, collapse out the removed element.
+		 */
+		memmove(&list->elements[list->first + pos], &list->elements[list->first + pos + 1],
+				(list->length - 1 - pos) * sizeof(ListCell));
+	}
 	list->length--;
 
 	return list;
@@ -1333,7 +1366,7 @@ list_free_private(List *list, bool deep)
 
 	if (deep)
 	{
-		for (int i = 0; i < list->length; i++)
+		for (int i = list->first; i < list->length; i++)
 			pfree(lfirst(&list->elements[i]));
 	}
 	pfree(list->elements);
@@ -1383,8 +1416,8 @@ list_copy(const List *oldlist)
 	if (oldlist == NIL)
 		return NIL;
 
-	newlist = new_list(oldlist->type, oldlist->length);
-	memcpy(newlist->elements, oldlist->elements,
+	newlist = new_list(oldlist->type, oldlist->length, false);
+	memcpy(newlist->elements, &oldlist->elements[oldlist->first],
 		   newlist->length * sizeof(ListCell));
 
 	check_list_invariants(newlist);
@@ -1405,8 +1438,8 @@ list_copy_tail(const List *oldlist, int nskip)
 	if (oldlist == NIL || nskip >= oldlist->length)
 		return NIL;
 
-	newlist = new_list(oldlist->type, oldlist->length - nskip);
-	memcpy(newlist->elements, &oldlist->elements[nskip],
+	newlist = new_list(oldlist->type, oldlist->length - nskip, false);
+	memcpy(newlist->elements, &oldlist->elements[oldlist->first + nskip],
 		   newlist->length * sizeof(ListCell));
 
 	check_list_invariants(newlist);
@@ -1431,10 +1464,10 @@ list_copy_deep(const List *oldlist)
 	/* This is only sensible for pointer Lists */
 	Assert(IsA(oldlist, List));
 
-	newlist = new_list(oldlist->type, oldlist->length);
+	newlist = new_list(oldlist->type, oldlist->length, false);
 	for (int i = 0; i < newlist->length; i++)
 		lfirst(&newlist->elements[i]) =
-			copyObjectImpl(lfirst(&oldlist->elements[i]));
+			copyObjectImpl(lfirst(&oldlist->elements[oldlist->first + i]));
 
 	check_list_invariants(newlist);
 	return newlist;
@@ -1472,7 +1505,7 @@ list_qsort(const List *list, list_qsort_comparator cmp)
 	qsort(list_arr, len, sizeof(ListCell *), cmp);
 
 	/* Construct new list (this code is much like list_copy) */
-	newlist = new_list(list->type, len);
+	newlist = new_list(list->type, len, false);
 
 	for (i = 0; i < len; i++)
 		newlist->elements[i] = *list_arr[i];
diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h
index 8c79edd349..c164417e0b 100644
--- a/src/include/nodes/pg_list.h
+++ b/src/include/nodes/pg_list.h
@@ -50,6 +50,7 @@ typedef struct List
 {
 	NodeTag		type;			/* T_List, T_IntList, or T_OidList */
 	int			length;			/* number of elements currently present */
+	int			first;			/* First used cell index */
 	int			max_length;		/* allocated length of elements[] */
 	ListCell   *elements;		/* re-allocatable array of cells */
 } List;
@@ -69,14 +70,14 @@ typedef struct List
 static inline ListCell *
 list_head(const List *l)
 {
-	return l ? &l->elements[0] : NULL;
+	return l ? &l->elements[l->first] : NULL;
 }
 
 /* Fetch address of list's last cell; NULL if empty list */
 static inline ListCell *
 list_tail(const List *l)
 {
-	return l ? &l->elements[l->length - 1] : NULL;
+	return l ? &l->elements[l->first + l->length - 1] : NULL;
 }
 
 /* Fetch address of list's second cell, if it has one, else NULL */
@@ -84,7 +85,7 @@ static inline ListCell *
 list_second_cell(const List *l)
 {
 	if (l && l->length >= 2)
-		return &l->elements[1];
+		return &l->elements[l->first + 1];
 	else
 		return NULL;
 }
@@ -94,7 +95,7 @@ static inline ListCell *
 list_third_cell(const List *l)
 {
 	if (l && l->length >= 3)
-		return &l->elements[2];
+		return &l->elements[l->first + 2];
 	else
 		return NULL;
 }
@@ -104,7 +105,7 @@ static inline ListCell *
 list_fourth_cell(const List *l)
 {
 	if (l && l->length >= 4)
-		return &l->elements[3];
+		return &l->elements[l->first + 3];
 	else
 		return NULL;
 }
@@ -171,7 +172,7 @@ list_nth_cell(const List *list, int n)
 {
 	Assert(list != NIL);
 	Assert(n >= 0 && n < list->length);
-	return &list->elements[n];
+	return &list->elements[list->first + n];
 }
 
 /*
@@ -215,8 +216,8 @@ list_nth_oid(const List *list, int n)
 static inline int
 list_cell_number(const List *l, const ListCell *c)
 {
-	Assert(c >= &l->elements[0] && c < &l->elements[l->length]);
-	return c - l->elements;
+	Assert(c >= &l->elements[l->first] && c < &l->elements[l->first + l->length]);
+	return c - &l->elements[l->first];
 }
 
 /*
@@ -225,13 +226,10 @@ list_cell_number(const List *l, const ListCell *c)
 static inline ListCell *
 lnext(const List *l, const ListCell *c)
 {
-	if (c != NULL)
-	{
-		Assert(c >= &l->elements[0] && c < &l->elements[l->length]);
-		c = c + 1;
-		if (c < &l->elements[l->length])
-			return (ListCell *) c;
-	}
+	Assert(c >= &l->elements[l->first] && c < &l->elements[l->first + l->length]);
+	c = c + 1;
+	if (c < &l->elements[l->first + l->length])
+		return (ListCell *) c;
 	return NULL;
 }
 
@@ -248,7 +246,7 @@ lnext(const List *l, const ListCell *c)
  * and it's very unsafe to change the List object while the loop is iterating.
  */
 #define foreach(cell, l)	\
-	for ((cell) = list_head(l); (cell) != NULL; (cell) = lnext(l, cell))
+	for ((cell) = list_head(l); ((cell) && (cell) < &((List *) l)->elements[((List *) l)->first + ((List *) l)->length]) || (cell = NULL) != NULL; cell++)
 
 /*
  * for_each_cell -
