diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index a2d0a8346f1d1e776372d5373310dec6236edf69..8dac4b2e44049a74eeddd1d2184fc87503b5164b 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.129 2003/05/02 20:54:33 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.130 2003/05/28 22:32:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2320,9 +2320,10 @@ ExecInitExpr(Expr *node, PlanState *parent)
 			{
 				CaseExpr   *caseexpr = (CaseExpr *) node;
 				CaseExprState *cstate = makeNode(CaseExprState);
-				List	   *outlist = NIL;
+				FastList	outlist;
 				List	   *inlist;
 
+				FastListInit(&outlist);
 				foreach(inlist, caseexpr->args)
 				{
 					CaseWhen   *when = (CaseWhen *) lfirst(inlist);
@@ -2332,9 +2333,9 @@ ExecInitExpr(Expr *node, PlanState *parent)
 					wstate->xprstate.expr = (Expr *) when;
 					wstate->expr = ExecInitExpr(when->expr, parent);
 					wstate->result = ExecInitExpr(when->result, parent);
-					outlist = lappend(outlist, wstate);
+					FastAppend(&outlist, wstate);
 				}
-				cstate->args = outlist;
+				cstate->args = FastListValue(&outlist);
 				/* caseexpr->arg should be null by now */
 				Assert(caseexpr->arg == NULL);
 				cstate->defresult = ExecInitExpr(caseexpr->defresult, parent);
@@ -2345,18 +2346,19 @@ ExecInitExpr(Expr *node, PlanState *parent)
 			{
 				ArrayExpr	   *arrayexpr = (ArrayExpr *) node;
 				ArrayExprState *astate = makeNode(ArrayExprState);
-				List		   *outlist = NIL;
+				FastList		outlist;
 				List		   *inlist;
 
+				FastListInit(&outlist);
 				foreach(inlist, arrayexpr->elements)
 				{
 					Expr	   *e = (Expr *) lfirst(inlist);
 					ExprState  *estate;
 
 					estate = ExecInitExpr(e, parent);
-					outlist = lappend(outlist, estate);
+					FastAppend(&outlist, estate);
 				}
-				astate->elements = outlist;
+				astate->elements = FastListValue(&outlist);
 				/* do one-time catalog lookup for type info */
 				get_typlenbyvalalign(arrayexpr->element_typeid,
 									 &astate->elemlength,
@@ -2369,18 +2371,19 @@ ExecInitExpr(Expr *node, PlanState *parent)
 			{
 				CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
 				CoalesceExprState *cstate = makeNode(CoalesceExprState);
-				List	   *outlist = NIL;
+				FastList	outlist;
 				List	   *inlist;
 
+				FastListInit(&outlist);
 				foreach(inlist, coalesceexpr->args)
 				{
 					Expr *e = (Expr *) lfirst(inlist);
 					ExprState *estate;
 
 					estate = ExecInitExpr(e, parent);
-					outlist = lappend(outlist, estate);
+					FastAppend(&outlist, estate);
 				}
-				cstate->args = outlist;
+				cstate->args = FastListValue(&outlist);
 				state = (ExprState *) cstate;
 			}
 			break;
@@ -2434,17 +2437,18 @@ ExecInitExpr(Expr *node, PlanState *parent)
 			break;
 		case T_List:
 			{
-				List	   *outlist = NIL;
+				FastList	outlist;
 				List	   *inlist;
 
+				FastListInit(&outlist);
 				foreach(inlist, (List *) node)
 				{
-					outlist = lappend(outlist,
-									  ExecInitExpr((Expr *) lfirst(inlist),
-												   parent));
+					FastAppend(&outlist,
+							   ExecInitExpr((Expr *) lfirst(inlist),
+											parent));
 				}
 				/* Don't fall through to the "common" code below */
-				return (ExprState *) outlist;
+				return (ExprState *) FastListValue(&outlist);
 			}
 		default:
 			elog(ERROR, "ExecInitExpr: unknown expression type %d",
diff --git a/src/backend/nodes/list.c b/src/backend/nodes/list.c
index 0a379a753b1a2d3058138f9df42be09e039a8127..207acea9472b05d57b9aa33f37d564d9e9045e94 100644
--- a/src/backend/nodes/list.c
+++ b/src/backend/nodes/list.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.48 2003/02/09 06:56:27 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.49 2003/05/28 22:32:49 tgl Exp $
  *
  * NOTES
  *	  XXX a few of the following functions are duplicated to handle
@@ -141,9 +141,9 @@ lconso(Oid datum, List *list)
  * MORE EXPENSIVE THAN lcons
  */
 List *
-lappend(List *list, void *obj)
+lappend(List *list, void *datum)
 {
-	return nconc(list, makeList1(obj));
+	return nconc(list, makeList1(datum));
 }
 
 /*
@@ -195,6 +195,120 @@ nconc(List *l1, List *l2)
 	return l1;					/* list1 is now list1+list2  */
 }
 
+/*
+ * FastAppend - append to a FastList.
+ *
+ * For long lists this is significantly faster than repeated lappend's,
+ * since we avoid having to chase down the list again each time.
+ */
+void
+FastAppend(FastList *fl, void *datum)
+{
+	List	   *cell = makeList1(datum);
+
+	if (fl->tail)
+	{
+		lnext(fl->tail) = cell;
+		fl->tail = cell;
+	}
+	else
+	{
+		/* First cell of list */
+		Assert(fl->head == NIL);
+		fl->head = fl->tail = cell;
+	}
+}
+
+/*
+ * FastAppendi - same for integers
+ */
+void
+FastAppendi(FastList *fl, int datum)
+{
+	List	   *cell = makeListi1(datum);
+
+	if (fl->tail)
+	{
+		lnext(fl->tail) = cell;
+		fl->tail = cell;
+	}
+	else
+	{
+		/* First cell of list */
+		Assert(fl->head == NIL);
+		fl->head = fl->tail = cell;
+	}
+}
+
+/*
+ * FastAppendo - same for Oids
+ */
+void
+FastAppendo(FastList *fl, Oid datum)
+{
+	List	   *cell = makeListo1(datum);
+
+	if (fl->tail)
+	{
+		lnext(fl->tail) = cell;
+		fl->tail = cell;
+	}
+	else
+	{
+		/* First cell of list */
+		Assert(fl->head == NIL);
+		fl->head = fl->tail = cell;
+	}
+}
+
+/*
+ * FastConc - nconc() for FastList building
+ *
+ * Note that the cells of the second argument are absorbed into the FastList.
+ */
+void
+FastConc(FastList *fl, List *cells)
+{
+	if (cells == NIL)
+		return;					/* nothing to do */
+	if (fl->tail)
+	{
+		lnext(fl->tail) = cells;
+	}
+	else
+	{
+		/* First cell of list */
+		Assert(fl->head == NIL);
+		fl->head = cells;
+	}
+	while (lnext(cells) != NIL)
+		cells = lnext(cells);
+	fl->tail = cells;
+}
+
+/*
+ * FastConcFast - nconc() for FastList building
+ *
+ * Note that the cells of the second argument are absorbed into the first.
+ */
+void
+FastConcFast(FastList *fl, FastList *fl2)
+{
+	if (fl2->head == NIL)
+		return;					/* nothing to do */
+	if (fl->tail)
+	{
+		lnext(fl->tail) = fl2->head;
+	}
+	else
+	{
+		/* First cell of list */
+		Assert(fl->head == NIL);
+		fl->head = fl2->head;
+	}
+	fl->tail = fl2->tail;
+}
+
 /*
  *	nth
  *
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 9319f8c147414b88376c22b6df2593cd28894e33..ace494ba20bad254c566dc177c09671599a5675d 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.142 2003/05/28 16:03:56 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.143 2003/05/28 22:32:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -417,10 +417,11 @@ extract_or_indexqual_conditions(RelOptInfo *rel,
 								IndexOptInfo *index,
 								Expr *orsubclause)
 {
-	List	   *quals = NIL;
+	FastList	quals;
 	int			indexcol = 0;
 	Oid		   *classes = index->classlist;
 
+	FastListInit(&quals);
 	/*
 	 * Extract relevant indexclauses in indexkey order.  This is
 	 * essentially just like group_clauses_by_indexkey() except that the
@@ -430,9 +431,10 @@ extract_or_indexqual_conditions(RelOptInfo *rel,
 	do
 	{
 		Oid			curClass = classes[0];
-		List	   *clausegroup = NIL;
+		FastList	clausegroup;
 		List	   *item;
 
+		FastListInit(&clausegroup);
 		if (and_clause((Node *) orsubclause))
 		{
 			foreach(item, ((BoolExpr *) orsubclause)->args)
@@ -442,21 +444,23 @@ extract_or_indexqual_conditions(RelOptInfo *rel,
 				if (match_clause_to_indexcol(rel, index,
 											 indexcol, curClass,
 											 subsubclause))
-					clausegroup = nconc(clausegroup,
-										expand_indexqual_condition(subsubclause,
-																   curClass));
+					FastConc(&clausegroup,
+							 expand_indexqual_condition(subsubclause,
+														curClass));
 			}
 		}
 		else if (match_clause_to_indexcol(rel, index,
 										  indexcol, curClass,
 										  orsubclause))
-			clausegroup = expand_indexqual_condition(orsubclause, curClass);
+			FastConc(&clausegroup,
+					 expand_indexqual_condition(orsubclause,
+												curClass));
 
 		/*
 		 * If we found no clauses for this indexkey in the OR subclause
 		 * itself, try looking in the rel's top-level restriction list.
 		 */
-		if (clausegroup == NIL)
+		if (FastListValue(&clausegroup) == NIL)
 		{
 			foreach(item, rel->baserestrictinfo)
 			{
@@ -465,9 +469,9 @@ extract_or_indexqual_conditions(RelOptInfo *rel,
 				if (match_clause_to_indexcol(rel, index,
 											 indexcol, curClass,
 											 rinfo->clause))
-					clausegroup = nconc(clausegroup,
-										expand_indexqual_condition(rinfo->clause,
-																   curClass));
+					FastConc(&clausegroup,
+							 expand_indexqual_condition(rinfo->clause,
+														curClass));
 			}
 		}
 
@@ -475,20 +479,20 @@ extract_or_indexqual_conditions(RelOptInfo *rel,
 		 * If still no clauses match this key, we're done; we don't want
 		 * to look at keys to its right.
 		 */
-		if (clausegroup == NIL)
+		if (FastListValue(&clausegroup) == NIL)
 			break;
 
-		quals = nconc(quals, clausegroup);
+		FastConcFast(&quals, &clausegroup);
 
 		indexcol++;
 		classes++;
 
 	} while (!DoneMatchingIndexKeys(classes));
 
-	if (quals == NIL)
+	if (FastListValue(&quals) == NIL)
 		elog(ERROR, "extract_or_indexqual_conditions: no matching clause");
 
-	return quals;
+	return FastListValue(&quals);
 }
 
 
@@ -520,7 +524,7 @@ extract_or_indexqual_conditions(RelOptInfo *rel,
 static List *
 group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index)
 {
-	List	   *clausegroup_list = NIL;
+	FastList	clausegroup_list;
 	List	   *restrictinfo_list = rel->baserestrictinfo;
 	int			indexcol = 0;
 	Oid		   *classes = index->classlist;
@@ -528,12 +532,14 @@ group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index)
 	if (restrictinfo_list == NIL)
 		return NIL;
 
+	FastListInit(&clausegroup_list);
 	do
 	{
 		Oid			curClass = classes[0];
-		List	   *clausegroup = NIL;
+		FastList	clausegroup;
 		List	   *i;
 
+		FastListInit(&clausegroup);
 		foreach(i, restrictinfo_list)
 		{
 			RestrictInfo *rinfo = (RestrictInfo *) lfirst(i);
@@ -543,24 +549,24 @@ group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index)
 										 indexcol,
 										 curClass,
 										 rinfo->clause))
-				clausegroup = lappend(clausegroup, rinfo);
+				FastAppend(&clausegroup, rinfo);
 		}
 
 		/*
 		 * If no clauses match this key, we're done; we don't want to look
 		 * at keys to its right.
 		 */
-		if (clausegroup == NIL)
+		if (FastListValue(&clausegroup) == NIL)
 			break;
 
-		clausegroup_list = lappend(clausegroup_list, clausegroup);
+		FastAppend(&clausegroup_list, FastListValue(&clausegroup));
 
 		indexcol++;
 		classes++;
 
 	} while (!DoneMatchingIndexKeys(classes));
 
-	return clausegroup_list;
+	return FastListValue(&clausegroup_list);
 }
 
 /*
@@ -580,17 +586,20 @@ static List *
 group_clauses_by_indexkey_for_join(RelOptInfo *rel, IndexOptInfo *index,
 								   Relids outer_relids, bool isouterjoin)
 {
-	List	   *clausegroup_list = NIL;
+	FastList	clausegroup_list;
 	bool		jfound = false;
 	int			indexcol = 0;
 	Oid		   *classes = index->classlist;
 
+	FastListInit(&clausegroup_list);
 	do
 	{
 		Oid			curClass = classes[0];
-		List	   *clausegroup = NIL;
+		FastList	clausegroup;
 		List	   *i;
 
+		FastListInit(&clausegroup);
+
 		/* Look for joinclauses that are usable with given outer_relids */
 		foreach(i, rel->joininfo)
 		{
@@ -614,7 +623,7 @@ group_clauses_by_indexkey_for_join(RelOptInfo *rel, IndexOptInfo *index,
 												  curClass,
 												  rinfo->clause))
 				{
-					clausegroup = lappend(clausegroup, rinfo);
+					FastAppend(&clausegroup, rinfo);
 					jfound = true;
 				}
 			}
@@ -634,17 +643,17 @@ group_clauses_by_indexkey_for_join(RelOptInfo *rel, IndexOptInfo *index,
 										 indexcol,
 										 curClass,
 										 rinfo->clause))
-				clausegroup = lappend(clausegroup, rinfo);
+				FastAppend(&clausegroup, rinfo);
 		}
 
 		/*
 		 * If no clauses match this key, we're done; we don't want to look
 		 * at keys to its right.
 		 */
-		if (clausegroup == NIL)
+		if (FastListValue(&clausegroup) == NIL)
 			break;
 
-		clausegroup_list = lappend(clausegroup_list, clausegroup);
+		FastAppend(&clausegroup_list, FastListValue(&clausegroup));
 
 		indexcol++;
 		classes++;
@@ -653,12 +662,9 @@ group_clauses_by_indexkey_for_join(RelOptInfo *rel, IndexOptInfo *index,
 
 	/* if no join clause was matched then forget it, per comments above */
 	if (!jfound)
-	{
-		freeList(clausegroup_list);
 		return NIL;
-	}
 
-	return clausegroup_list;
+	return FastListValue(&clausegroup_list);
 }
 
 
@@ -1870,12 +1876,13 @@ match_special_index_operator(Expr *clause, Oid opclass,
 List *
 expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
 {
-	List	   *resultquals = NIL;
+	FastList	resultquals;
 	Oid		   *classes = index->classlist;
 
 	if (clausegroups == NIL)
 		return NIL;
 
+	FastListInit(&resultquals);
 	do
 	{
 		Oid			curClass = classes[0];
@@ -1885,9 +1892,9 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
 		{
 			RestrictInfo *rinfo = (RestrictInfo *) lfirst(i);
 
-			resultquals = nconc(resultquals,
-								expand_indexqual_condition(rinfo->clause,
-														   curClass));
+			FastConc(&resultquals,
+					 expand_indexqual_condition(rinfo->clause,
+												curClass));
 		}
 
 		clausegroups = lnext(clausegroups);
@@ -1898,7 +1905,7 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
 
 	Assert(clausegroups == NIL); /* else more groups than indexkeys... */
 
-	return resultquals;
+	return FastListValue(&resultquals);
 }
 
 /*
diff --git a/src/backend/optimizer/path/orindxpath.c b/src/backend/optimizer/path/orindxpath.c
index 101866867b9627a6f8c82fc572ce2adba277f435..10eb050f3a3193dba8da301b5a9b54c76c3d770a 100644
--- a/src/backend/optimizer/path/orindxpath.c
+++ b/src/backend/optimizer/path/orindxpath.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.49 2002/12/12 15:49:31 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.50 2003/05/28 22:32:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -149,10 +149,12 @@ best_or_subclause_indices(Query *root,
 						  List *indices,
 						  IndexPath *pathnode)
 {
+	FastList	infos;
+	FastList	quals;
 	List	   *slist;
 
-	pathnode->indexinfo = NIL;
-	pathnode->indexqual = NIL;
+	FastListInit(&infos);
+	FastListInit(&quals);
 	pathnode->path.startup_cost = 0;
 	pathnode->path.total_cost = 0;
 
@@ -170,14 +172,17 @@ best_or_subclause_indices(Query *root,
 
 		Assert(best_indexinfo != NULL);
 
-		pathnode->indexinfo = lappend(pathnode->indexinfo, best_indexinfo);
-		pathnode->indexqual = lappend(pathnode->indexqual, best_indexqual);
+		FastAppend(&infos, best_indexinfo);
+		FastAppend(&quals, best_indexqual);
 		if (slist == subclauses)	/* first scan? */
 			pathnode->path.startup_cost = best_startup_cost;
 		pathnode->path.total_cost += best_total_cost;
 
 		indices = lnext(indices);
 	}
+
+	pathnode->indexinfo = FastListValue(&infos);
+	pathnode->indexqual = FastListValue(&quals);
 }
 
 /*
diff --git a/src/backend/optimizer/prep/prepqual.c b/src/backend/optimizer/prep/prepqual.c
index 4016ba476deafc55e542eb2c3024317861db9ee1..24ea1316e117c7cec527f8de3ad36629f85e4aa3 100644
--- a/src/backend/optimizer/prep/prepqual.c
+++ b/src/backend/optimizer/prep/prepqual.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepqual.c,v 1.34 2002/12/12 15:49:32 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepqual.c,v 1.35 2003/05/28 22:32:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,8 +21,12 @@
 #include "utils/lsyscache.h"
 
 static Expr *flatten_andors(Expr *qual);
-static List *pull_ors(List *orlist);
+static void flatten_andors_and_walker(FastList *out_list, List *andlist);
+static void flatten_andors_or_walker(FastList *out_list, List *orlist);
 static List *pull_ands(List *andlist);
+static void pull_ands_walker(FastList *out_list, List *andlist);
+static List *pull_ors(List *orlist);
+static void pull_ors_walker(FastList *out_list, List *orlist);
 static Expr *find_nots(Expr *qual);
 static Expr *push_nots(Expr *qual);
 static Expr *find_ors(Expr *qual);
@@ -291,47 +295,19 @@ flatten_andors(Expr *qual)
 
 	if (and_clause((Node *) qual))
 	{
-		List	   *out_list = NIL;
-		List	   *arg;
+		FastList	out_list;
 
-		foreach(arg, ((BoolExpr *) qual)->args)
-		{
-			Expr	   *subexpr = flatten_andors((Expr *) lfirst(arg));
-
-			/*
-			 * Note: we can destructively nconc the subexpression's
-			 * arglist because we know the recursive invocation of
-			 * flatten_andors will have built a new arglist not shared
-			 * with any other expr. Otherwise we'd need a listCopy here.
-			 */
-			if (and_clause((Node *) subexpr))
-				out_list = nconc(out_list, ((BoolExpr *) subexpr)->args);
-			else
-				out_list = lappend(out_list, subexpr);
-		}
-		return make_andclause(out_list);
+		FastListInit(&out_list);
+		flatten_andors_and_walker(&out_list, ((BoolExpr *) qual)->args);
+		return make_andclause(FastListValue(&out_list));
 	}
 	else if (or_clause((Node *) qual))
 	{
-		List	   *out_list = NIL;
-		List	   *arg;
+		FastList	out_list;
 
-		foreach(arg, ((BoolExpr *) qual)->args)
-		{
-			Expr	   *subexpr = flatten_andors((Expr *) lfirst(arg));
-
-			/*
-			 * Note: we can destructively nconc the subexpression's
-			 * arglist because we know the recursive invocation of
-			 * flatten_andors will have built a new arglist not shared
-			 * with any other expr. Otherwise we'd need a listCopy here.
-			 */
-			if (or_clause((Node *) subexpr))
-				out_list = nconc(out_list, ((BoolExpr *) subexpr)->args);
-			else
-				out_list = lappend(out_list, subexpr);
-		}
-		return make_orclause(out_list);
+		FastListInit(&out_list);
+		flatten_andors_or_walker(&out_list, ((BoolExpr *) qual)->args);
+		return make_orclause(FastListValue(&out_list));
 	}
 	else if (not_clause((Node *) qual))
 		return make_notclause(flatten_andors(get_notclausearg(qual)));
@@ -351,69 +327,102 @@ flatten_andors(Expr *qual)
 		return qual;
 }
 
-/*
- * pull_ors
- *	  Pull the arguments of an 'or' clause nested within another 'or'
- *	  clause up into the argument list of the parent.
- *
- * Input is the arglist of an OR clause.
- * Returns the rebuilt arglist (note original list structure is not touched).
- */
-static List *
-pull_ors(List *orlist)
+static void
+flatten_andors_and_walker(FastList *out_list, List *andlist)
+{
+	List	   *arg;
+
+	foreach(arg, andlist)
+	{
+		Expr	   *subexpr = (Expr *) lfirst(arg);
+
+		if (and_clause((Node *) subexpr))
+			flatten_andors_and_walker(out_list, ((BoolExpr *) subexpr)->args);
+		else
+			FastAppend(out_list, flatten_andors(subexpr));
+	}
+}
+
+static void
+flatten_andors_or_walker(FastList *out_list, List *orlist)
 {
-	List	   *out_list = NIL;
 	List	   *arg;
 
 	foreach(arg, orlist)
 	{
 		Expr	   *subexpr = (Expr *) lfirst(arg);
 
-		/*
-		 * Note: we can destructively nconc the subexpression's arglist
-		 * because we know the recursive invocation of pull_ors will have
-		 * built a new arglist not shared with any other expr. Otherwise
-		 * we'd need a listCopy here.
-		 */
 		if (or_clause((Node *) subexpr))
-			out_list = nconc(out_list,
-							 pull_ors(((BoolExpr *) subexpr)->args));
+			flatten_andors_or_walker(out_list, ((BoolExpr *) subexpr)->args);
 		else
-			out_list = lappend(out_list, subexpr);
+			FastAppend(out_list, flatten_andors(subexpr));
 	}
-	return out_list;
 }
 
 /*
  * pull_ands
- *	  Pull the arguments of an 'and' clause nested within another 'and'
- *	  clause up into the argument list of the parent.
+ *	  Recursively flatten nested AND clauses into a single and-clause list.
  *
- * Returns the modified list.
+ * Input is the arglist of an AND clause.
+ * Returns the rebuilt arglist (note original list structure is not touched).
  */
 static List *
 pull_ands(List *andlist)
 {
-	List	   *out_list = NIL;
+	FastList	out_list;
+
+	FastListInit(&out_list);
+	pull_ands_walker(&out_list, andlist);
+	return FastListValue(&out_list);
+}
+
+static void
+pull_ands_walker(FastList *out_list, List *andlist)
+{
 	List	   *arg;
 
 	foreach(arg, andlist)
 	{
 		Expr	   *subexpr = (Expr *) lfirst(arg);
 
-		/*
-		 * Note: we can destructively nconc the subexpression's arglist
-		 * because we know the recursive invocation of pull_ands will have
-		 * built a new arglist not shared with any other expr. Otherwise
-		 * we'd need a listCopy here.
-		 */
 		if (and_clause((Node *) subexpr))
-			out_list = nconc(out_list,
-							 pull_ands(((BoolExpr *) subexpr)->args));
+			pull_ands_walker(out_list, ((BoolExpr *) subexpr)->args);
+		else
+			FastAppend(out_list, subexpr);
+	}
+}
+
+/*
+ * pull_ors
+ *	  Recursively flatten nested OR clauses into a single or-clause list.
+ *
+ * Input is the arglist of an OR clause.
+ * Returns the rebuilt arglist (note original list structure is not touched).
+ */
+static List *
+pull_ors(List *orlist)
+{
+	FastList	out_list;
+
+	FastListInit(&out_list);
+	pull_ors_walker(&out_list, orlist);
+	return FastListValue(&out_list);
+}
+
+static void
+pull_ors_walker(FastList *out_list, List *orlist)
+{
+	List	   *arg;
+
+	foreach(arg, orlist)
+	{
+		Expr	   *subexpr = (Expr *) lfirst(arg);
+
+		if (or_clause((Node *) subexpr))
+			pull_ors_walker(out_list, ((BoolExpr *) subexpr)->args);
 		else
-			out_list = lappend(out_list, subexpr);
+			FastAppend(out_list, subexpr);
 	}
-	return out_list;
 }
 
 /*
@@ -447,21 +456,23 @@ find_nots(Expr *qual)
 #endif
 	if (and_clause((Node *) qual))
 	{
-		List	   *t_list = NIL;
+		FastList	t_list;
 		List	   *temp;
 
+		FastListInit(&t_list);
 		foreach(temp, ((BoolExpr *) qual)->args)
-			t_list = lappend(t_list, find_nots(lfirst(temp)));
-		return make_andclause(pull_ands(t_list));
+			FastAppend(&t_list, find_nots(lfirst(temp)));
+		return make_andclause(pull_ands(FastListValue(&t_list)));
 	}
 	else if (or_clause((Node *) qual))
 	{
-		List	   *t_list = NIL;
+		FastList	t_list;
 		List	   *temp;
 
+		FastListInit(&t_list);
 		foreach(temp, ((BoolExpr *) qual)->args)
-			t_list = lappend(t_list, find_nots(lfirst(temp)));
-		return make_orclause(pull_ors(t_list));
+			FastAppend(&t_list, find_nots(lfirst(temp)));
+		return make_orclause(pull_ors(FastListValue(&t_list)));
 	}
 	else if (not_clause((Node *) qual))
 		return push_nots(get_notclausearg(qual));
@@ -511,21 +522,23 @@ push_nots(Expr *qual)
 		 * i.e., swap AND for OR and negate all the subclauses.
 		 *--------------------
 		 */
-		List	   *t_list = NIL;
+		FastList	t_list;
 		List	   *temp;
 
+		FastListInit(&t_list);
 		foreach(temp, ((BoolExpr *) qual)->args)
-			t_list = lappend(t_list, push_nots(lfirst(temp)));
-		return make_orclause(pull_ors(t_list));
+			FastAppend(&t_list, push_nots(lfirst(temp)));
+		return make_orclause(pull_ors(FastListValue(&t_list)));
 	}
 	else if (or_clause((Node *) qual))
 	{
-		List	   *t_list = NIL;
+		FastList	t_list;
 		List	   *temp;
 
+		FastListInit(&t_list);
 		foreach(temp, ((BoolExpr *) qual)->args)
-			t_list = lappend(t_list, push_nots(lfirst(temp)));
-		return make_andclause(pull_ands(t_list));
+			FastAppend(&t_list, push_nots(lfirst(temp)));
+		return make_andclause(pull_ands(FastListValue(&t_list)));
 	}
 	else if (not_clause((Node *) qual))
 	{
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 4d51e7ffbc05b77603aa4036b864b9ab1f54a1c4..0c2e652c7950dbffc171ef47cab1e329c7e1cc99 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.137 2003/05/28 16:03:56 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.138 2003/05/28 22:32:49 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -771,21 +771,24 @@ is_pseudo_constant_clause(Node *clause)
 List *
 pull_constant_clauses(List *quals, List **constantQual)
 {
-	List	   *constqual = NIL;
-	List	   *restqual = NIL;
+	FastList	constqual,
+				restqual;
 	List	   *q;
 
+	FastListInit(&constqual);
+	FastListInit(&restqual);
+
 	foreach(q, quals)
 	{
 		Node	   *qual = (Node *) lfirst(q);
 
 		if (is_pseudo_constant_clause(qual))
-			constqual = lappend(constqual, qual);
+			FastAppend(&constqual, qual);
 		else
-			restqual = lappend(restqual, qual);
+			FastAppend(&restqual, qual);
 	}
-	*constantQual = constqual;
-	return restqual;
+	*constantQual = FastListValue(&constqual);
+	return FastListValue(&restqual);
 }
 
 
@@ -1187,16 +1190,17 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
 					 * when no input is TRUE and at least one is NULL.
 					 *----------
 					 */
-					List	   *newargs = NIL;
+					FastList	newargs;
 					List	   *arg;
 					bool		haveNull = false;
 					bool		forceTrue = false;
 
+					FastListInit(&newargs);
 					foreach(arg, args)
 					{
 						if (!IsA(lfirst(arg), Const))
 						{
-							newargs = lappend(newargs, lfirst(arg));
+							FastAppend(&newargs, lfirst(arg));
 							continue;
 						}
 						const_input = (Const *) lfirst(arg);
@@ -1217,15 +1221,15 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
 					if (forceTrue)
 						return MAKEBOOLCONST(true, false);
 					if (haveNull)
-						newargs = lappend(newargs, MAKEBOOLCONST(false, true));
+						FastAppend(&newargs, MAKEBOOLCONST(false, true));
 					/* If all the inputs are FALSE, result is FALSE */
-					if (newargs == NIL)
+					if (FastListValue(&newargs) == NIL)
 						return MAKEBOOLCONST(false, false);
 					/* If only one nonconst-or-NULL input, it's the result */
-					if (lnext(newargs) == NIL)
-						return (Node *) lfirst(newargs);
+					if (lnext(FastListValue(&newargs)) == NIL)
+						return (Node *) lfirst(FastListValue(&newargs));
 					/* Else we still need an OR node */
-					return (Node *) make_orclause(newargs);
+					return (Node *) make_orclause(FastListValue(&newargs));
 				}
 			case AND_EXPR:
 				{
@@ -1239,16 +1243,17 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
 					 * when no input is FALSE and at least one is NULL.
 					 *----------
 					 */
-					List	   *newargs = NIL;
+					FastList	newargs;
 					List	   *arg;
 					bool		haveNull = false;
 					bool		forceFalse = false;
 
+					FastListInit(&newargs);
 					foreach(arg, args)
 					{
 						if (!IsA(lfirst(arg), Const))
 						{
-							newargs = lappend(newargs, lfirst(arg));
+							FastAppend(&newargs, lfirst(arg));
 							continue;
 						}
 						const_input = (Const *) lfirst(arg);
@@ -1269,15 +1274,15 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
 					if (forceFalse)
 						return MAKEBOOLCONST(false, false);
 					if (haveNull)
-						newargs = lappend(newargs, MAKEBOOLCONST(false, true));
+						FastAppend(&newargs, MAKEBOOLCONST(false, true));
 					/* If all the inputs are TRUE, result is TRUE */
-					if (newargs == NIL)
+					if (FastListValue(&newargs) == NIL)
 						return MAKEBOOLCONST(true, false);
 					/* If only one nonconst-or-NULL input, it's the result */
-					if (lnext(newargs) == NIL)
-						return (Node *) lfirst(newargs);
+					if (lnext(FastListValue(&newargs)) == NIL)
+						return (Node *) lfirst(FastListValue(&newargs));
 					/* Else we still need an AND node */
-					return (Node *) make_andclause(newargs);
+					return (Node *) make_andclause(FastListValue(&newargs));
 				}
 			case NOT_EXPR:
 				Assert(length(args) == 1);
@@ -1370,11 +1375,12 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
 		 */
 		CaseExpr   *caseexpr = (CaseExpr *) node;
 		CaseExpr   *newcase;
-		List	   *newargs = NIL;
+		FastList	newargs;
 		Node	   *defresult;
 		Const	   *const_input;
 		List	   *arg;
 
+		FastListInit(&newargs);
 		foreach(arg, caseexpr->args)
 		{
 			/* Simplify this alternative's condition and result */
@@ -1387,7 +1393,7 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
 			if (casewhen->expr == NULL ||
 				!IsA(casewhen->expr, Const))
 			{
-				newargs = lappend(newargs, casewhen);
+				FastAppend(&newargs, casewhen);
 				continue;
 			}
 			const_input = (Const *) casewhen->expr;
@@ -1399,13 +1405,13 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
 			 * Found a TRUE condition.	If it's the first (un-dropped)
 			 * alternative, the CASE reduces to just this alternative.
 			 */
-			if (newargs == NIL)
+			if (FastListValue(&newargs) == NIL)
 				return (Node *) casewhen->result;
 
 			/*
 			 * Otherwise, add it to the list, and drop all the rest.
 			 */
-			newargs = lappend(newargs, casewhen);
+			FastAppend(&newargs, casewhen);
 			break;
 		}
 
@@ -1417,13 +1423,13 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
 		 * If no non-FALSE alternatives, CASE reduces to the default
 		 * result
 		 */
-		if (newargs == NIL)
+		if (FastListValue(&newargs) == NIL)
 			return defresult;
 		/* Otherwise we need a new CASE node */
 		newcase = makeNode(CaseExpr);
 		newcase->casetype = caseexpr->casetype;
 		newcase->arg = NULL;
-		newcase->args = newargs;
+		newcase->args = FastListValue(&newargs);
 		newcase->defresult = (Expr *) defresult;
 		return (Node *) newcase;
 	}
@@ -1432,9 +1438,10 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
 		ArrayExpr *arrayexpr = (ArrayExpr *) node;
 		ArrayExpr *newarray;
 		bool all_const = true;
-		List *newelems = NIL;
+		FastList	newelems;
 		List *element;
 
+		FastListInit(&newelems);
 		foreach(element, arrayexpr->elements)
 		{
 			Node *e;
@@ -1443,13 +1450,13 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
 											   active_fns);
 			if (!IsA(e, Const))
 				all_const = false;
-			newelems = lappend(newelems, e);
+			FastAppend(&newelems, e);
 		}
 
 		newarray = makeNode(ArrayExpr);
 		newarray->array_typeid = arrayexpr->array_typeid;
 		newarray->element_typeid = arrayexpr->element_typeid;
-		newarray->elements = newelems;
+		newarray->elements = FastListValue(&newelems);
 		newarray->ndims = arrayexpr->ndims;
 
 		if (all_const)
@@ -1462,9 +1469,10 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
 	{
 		CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
 		CoalesceExpr *newcoalesce;
-		List *newargs = NIL;
+		FastList	newargs;
 		List *arg;
 
+		FastListInit(&newargs);
 		foreach(arg, coalesceexpr->args)
 		{
 			Node *e;
@@ -1480,15 +1488,15 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
 			{
 				if (((Const *) e)->constisnull)
 					continue;	/* drop null constant */
-				if (newargs == NIL)
+				if (FastListValue(&newargs) == NIL)
 					return e;	/* first expr */
 			}
-			newargs = lappend(newargs, e);
+			FastAppend(&newargs, e);
 		}
 
 		newcoalesce = makeNode(CoalesceExpr);
 		newcoalesce->coalescetype = coalesceexpr->coalescetype;
-		newcoalesce->args = newargs;
+		newcoalesce->args = FastListValue(&newargs);
 		return (Node *) newcoalesce;
 	}
 	if (IsA(node, FieldSelect))
@@ -2647,16 +2655,16 @@ expression_tree_mutator(Node *node,
 				 * NOTE: this would fail badly on a list with integer
 				 * elements!
 				 */
-				List	   *resultlist = NIL;
+				FastList	resultlist;
 				List	   *temp;
 
+				FastListInit(&resultlist);
 				foreach(temp, (List *) node)
 				{
-					resultlist = lappend(resultlist,
-										 mutator((Node *) lfirst(temp),
-												 context));
+					FastAppend(&resultlist,
+							   mutator((Node *) lfirst(temp), context));
 				}
-				return (Node *) resultlist;
+				return (Node *) FastListValue(&resultlist);
 			}
 			break;
 		case T_FromExpr:
@@ -2739,7 +2747,7 @@ query_tree_mutator(Query *query,
 				   void *context,
 				   int flags)
 {
-	List	   *newrt = NIL;
+	FastList	newrt;
 	List	   *rt;
 
 	Assert(query != NULL && IsA(query, Query));
@@ -2757,6 +2765,7 @@ query_tree_mutator(Query *query,
 	MUTATE(query->setOperations, query->setOperations, Node *);
 	MUTATE(query->havingQual, query->havingQual, Node *);
 	MUTATE(query->in_info_list, query->in_info_list, List *);
+	FastListInit(&newrt);
 	foreach(rt, query->rtable)
 	{
 		RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
@@ -2791,9 +2800,9 @@ query_tree_mutator(Query *query,
 				rte = newrte;
 				break;
 		}
-		newrt = lappend(newrt, rte);
+		FastAppend(&newrt, rte);
 	}
-	query->rtable = newrt;
+	query->rtable = FastListValue(&newrt);
 	return query;
 }
 
diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c
index 929f5561dbc284bc18a95c141b49fb5929910f7c..a365b7b159ec4c680fd3dfc3e653253152790f27 100644
--- a/src/backend/optimizer/util/var.c
+++ b/src/backend/optimizer/util/var.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.49 2003/02/08 20:20:55 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.50 2003/05/28 22:32:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -37,7 +37,7 @@ typedef struct
 
 typedef struct
 {
-	List	   *varlist;
+	FastList	varlist;
 	bool		includeUpperVars;
 } pull_var_clause_context;
 
@@ -344,11 +344,11 @@ pull_var_clause(Node *node, bool includeUpperVars)
 {
 	pull_var_clause_context context;
 
-	context.varlist = NIL;
+	FastListInit(&context.varlist);
 	context.includeUpperVars = includeUpperVars;
 
 	pull_var_clause_walker(node, &context);
-	return context.varlist;
+	return FastListValue(&context.varlist);
 }
 
 static bool
@@ -359,7 +359,7 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context)
 	if (IsA(node, Var))
 	{
 		if (((Var *) node)->varlevelsup == 0 || context->includeUpperVars)
-			context->varlist = lappend(context->varlist, node);
+			FastAppend(&context->varlist, node);
 		return false;
 	}
 	return expression_tree_walker(node, pull_var_clause_walker,
diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h
index b48fc02e8554ded06717f18c5d01121088df87f7..bc42917a355eea361a63b616c18f4b6771afd37b 100644
--- a/src/include/nodes/pg_list.h
+++ b/src/include/nodes/pg_list.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_list.h,v 1.35 2003/02/09 06:56:28 tgl Exp $
+ * $Id: pg_list.h,v 1.36 2003/05/28 22:32:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -128,6 +128,21 @@ typedef struct List
 #define makeListo1(x1)				lconso(x1, NIL)
 #define makeListo2(x1,x2)			lconso(x1, makeListo1(x2))
 
+/*
+ * FastList is an optimization for building large lists.  The conventional
+ * way to build a list is repeated lappend() operations, but that is O(N^2)
+ * in the number of list items, which gets tedious for large lists.
+ */
+typedef struct FastList
+{
+	List	   *head;
+	List	   *tail;
+} FastList;
+
+#define FastListInit(fl)	( (fl)->head = (fl)->tail = NIL )
+#define FastListValue(fl)	( (fl)->head )
+
+
 /*
  * function prototypes in nodes/list.c
  */
@@ -143,6 +158,11 @@ extern List *lappend(List *list, void *datum);
 extern List *lappendi(List *list, int datum);
 extern List *lappendo(List *list, Oid datum);
 extern List *nconc(List *list1, List *list2);
+extern void FastAppend(FastList *fl, void *datum);
+extern void FastAppendi(FastList *fl, int datum);
+extern void FastAppendo(FastList *fl, Oid datum);
+extern void FastConc(FastList *fl, List *cells);
+extern void FastConcFast(FastList *fl, FastList *fl2);
 extern void *nth(int n, List *l);
 extern int	length(List *list);
 extern void *llast(List *list);