aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2003-05-28 22:32:50 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2003-05-28 22:32:50 +0000
commit8a6ac83dab3b3a5701a0508daa1d9356e2d59cdb (patch)
tree0f96d0304864f7439c90dc6ae6a1e30945051d1a /src
parent0f3c68aa436dca175d29a43e104146259b4b0a00 (diff)
downloadpostgresql-8a6ac83dab3b3a5701a0508daa1d9356e2d59cdb.tar.gz
postgresql-8a6ac83dab3b3a5701a0508daa1d9356e2d59cdb.zip
Fix some planner performance problems with large WHERE clauses, by
introducing new 'FastList' list-construction subroutines to use in hot spots. This avoids the O(N^2) behavior of repeated lappend's by keeping a tail pointer, while not changing behavior by reversing list order as the lcons() method would do.
Diffstat (limited to 'src')
-rw-r--r--src/backend/executor/execQual.c34
-rw-r--r--src/backend/nodes/list.c120
-rw-r--r--src/backend/optimizer/path/indxpath.c79
-rw-r--r--src/backend/optimizer/path/orindxpath.c15
-rw-r--r--src/backend/optimizer/prep/prepqual.c183
-rw-r--r--src/backend/optimizer/util/clauses.c93
-rw-r--r--src/backend/optimizer/util/var.c10
-rw-r--r--src/include/nodes/pg_list.h22
8 files changed, 364 insertions, 192 deletions
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index a2d0a8346f1..8dac4b2e440 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 0a379a753b1..207acea9472 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));
}
/*
@@ -196,6 +196,120 @@ nconc(List *l1, List *l2)
}
/*
+ * 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
*
* Get the n'th element of the list. First element is 0th.
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 9319f8c1474..ace494ba20b 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 101866867b9..10eb050f3a3 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 4016ba476de..24ea1316e11 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 4d51e7ffbc0..0c2e652c795 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 929f5561dbc..a365b7b159e 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 b48fc02e855..bc42917a355 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 $
*
*-------------------------------------------------------------------------
*/
@@ -129,6 +129,21 @@ typedef struct List
#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
*/
extern Value *makeInteger(long i);
@@ -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);