Tom Lane 8a6ac83dab 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.
2003-05-28 22:32:50 +00:00

770 lines
12 KiB
C

/*-------------------------------------------------------------------------
*
* list.c
* POSTGRES generic list package
*
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $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
* List of pointers and List of integers separately. Some day,
* someone should unify them. - ay 11/2/94
* This file needs cleanup.
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
* Andrew Yu Oct, 1994 file creation
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "nodes/parsenodes.h"
/*
* makeInteger
*/
Value *
makeInteger(long i)
{
Value *v = makeNode(Value);
v->type = T_Integer;
v->val.ival = i;
return v;
}
/*
* makeFloat
*
* Caller is responsible for passing a palloc'd string.
*/
Value *
makeFloat(char *numericStr)
{
Value *v = makeNode(Value);
v->type = T_Float;
v->val.str = numericStr;
return v;
}
/*
* makeString
*
* Caller is responsible for passing a palloc'd string.
*/
Value *
makeString(char *str)
{
Value *v = makeNode(Value);
v->type = T_String;
v->val.str = str;
return v;
}
/*
* makeBitString
*
* Caller is responsible for passing a palloc'd string.
*/
Value *
makeBitString(char *str)
{
Value *v = makeNode(Value);
v->type = T_BitString;
v->val.str = str;
return v;
}
/*
* lcons
*
* Add obj to the front of list, or make a new list if 'list' is NIL
*/
List *
lcons(void *obj, List *list)
{
List *l = makeNode(List);
lfirst(l) = obj;
lnext(l) = list;
return l;
}
/*
* lconsi
*
* Same as lcons, but for integer data
*/
List *
lconsi(int datum, List *list)
{
List *l = makeNode(List);
lfirsti(l) = datum;
lnext(l) = list;
return l;
}
/*
* lconso
*
* Same as lcons, but for Oid data
*/
List *
lconso(Oid datum, List *list)
{
List *l = makeNode(List);
lfirsto(l) = datum;
lnext(l) = list;
return l;
}
/*
* lappend
*
* Add obj to the end of list, or make a new list if 'list' is NIL
*
* MORE EXPENSIVE THAN lcons
*/
List *
lappend(List *list, void *datum)
{
return nconc(list, makeList1(datum));
}
/*
* lappendi
*
* Same as lappend, but for integers
*/
List *
lappendi(List *list, int datum)
{
return nconc(list, makeListi1(datum));
}
/*
* lappendo
*
* Same as lappend, but for Oids
*/
List *
lappendo(List *list, Oid datum)
{
return nconc(list, makeListo1(datum));
}
/*
* nconc
*
* Concat l2 on to the end of l1
*
* NB: l1 is destructively changed! Use nconc(listCopy(l1), l2)
* if you need to make a merged list without touching the original lists.
*/
List *
nconc(List *l1, List *l2)
{
List *temp;
if (l1 == NIL)
return l2;
if (l2 == NIL)
return l1;
if (l1 == l2)
elog(ERROR, "can't nconc a list to itself");
for (temp = l1; lnext(temp) != NIL; temp = lnext(temp))
;
lnext(temp) = 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
*
* Get the n'th element of the list. First element is 0th.
*/
void *
nth(int n, List *l)
{
/* XXX assume list is long enough */
while (n-- > 0)
{
l = lnext(l);
}
return lfirst(l);
}
/*
* length
*
* Get the length of l
*/
int
length(List *l)
{
int i = 0;
while (l != NIL)
{
l = lnext(l);
i++;
}
return i;
}
/*
* llast
*
* Get the last element of l ... error if empty list
*/
void *
llast(List *l)
{
if (l == NIL)
elog(ERROR, "llast: empty list");
while (lnext(l) != NIL)
l = lnext(l);
return lfirst(l);
}
/*
* freeList
*
* Free the List nodes of a list
* The pointed-to nodes, if any, are NOT freed.
* This works for integer and Oid lists too.
*/
void
freeList(List *list)
{
while (list != NIL)
{
List *l = list;
list = lnext(list);
pfree(l);
}
}
/*
* equali
* compares two lists of integers
*/
bool
equali(List *list1, List *list2)
{
List *l;
foreach(l, list1)
{
if (list2 == NIL)
return false;
if (lfirsti(l) != lfirsti(list2))
return false;
list2 = lnext(list2);
}
if (list2 != NIL)
return false;
return true;
}
/*
* equalo
* compares two lists of Oids
*/
bool
equalo(List *list1, List *list2)
{
List *l;
foreach(l, list1)
{
if (list2 == NIL)
return false;
if (lfirsto(l) != lfirsto(list2))
return false;
list2 = lnext(list2);
}
if (list2 != NIL)
return false;
return true;
}
/*
* Generate the union of two lists,
* ie, l1 plus all members of l2 that are not already in l1.
*
* NOTE: if there are duplicates in l1 they will still be duplicate in the
* result; but duplicates in l2 are discarded.
*
* The result is a fresh List, but it points to the same member nodes
* as were in the inputs.
*/
List *
set_union(List *l1, List *l2)
{
List *retval = listCopy(l1);
List *i;
foreach(i, l2)
{
if (!member(lfirst(i), retval))
retval = lappend(retval, lfirst(i));
}
return retval;
}
/* set_union for Oid lists */
List *
set_uniono(List *l1, List *l2)
{
List *retval = listCopy(l1);
List *i;
foreach(i, l2)
{
if (!oidMember(lfirsto(i), retval))
retval = lappendo(retval, lfirsto(i));
}
return retval;
}
/* set_union when pointer-equality comparison is sufficient */
List *
set_ptrUnion(List *l1, List *l2)
{
List *retval = listCopy(l1);
List *i;
foreach(i, l2)
{
if (!ptrMember(lfirst(i), retval))
retval = lappend(retval, lfirst(i));
}
return retval;
}
/*
* Generate the intersection of two lists,
* ie, all members of both l1 and l2.
*
* NOTE: if there are duplicates in l1 they will still be duplicate in the
* result; but duplicates in l2 are discarded.
*
* The result is a fresh List, but it points to the same member nodes
* as were in the inputs.
*/
#ifdef NOT_USED
List *
set_intersect(List *l1, List *l2)
{
List *retval = NIL;
List *i;
foreach(i, l1)
{
if (member(lfirst(i), l2))
retval = lappend(retval, lfirst(i));
}
return retval;
}
#endif
/*
* member()
* nondestructive, returns t iff l1 is a member of the list l2
*/
bool
member(void *l1, List *l2)
{
List *i;
foreach(i, l2)
{
if (equal((Node *) l1, (Node *) lfirst(i)))
return true;
}
return false;
}
/*
* like member(), but use when pointer-equality comparison is sufficient
*/
bool
ptrMember(void *l1, List *l2)
{
List *i;
foreach(i, l2)
{
if (l1 == lfirst(i))
return true;
}
return false;
}
/*
* membership test for integer lists
*/
bool
intMember(int l1, List *l2)
{
List *i;
foreach(i, l2)
{
if (l1 == lfirsti(i))
return true;
}
return false;
}
/*
* membership test for Oid lists
*/
bool
oidMember(Oid l1, List *l2)
{
List *i;
foreach(i, l2)
{
if (l1 == lfirsto(i))
return true;
}
return false;
}
/*
* lremove
* Removes 'elem' from the linked list (destructively changing the list!).
* (If there is more than one equal list member, the first is removed.)
*
* This version matches 'elem' using simple pointer comparison.
* See also LispRemove.
*/
List *
lremove(void *elem, List *list)
{
List *l;
List *prev = NIL;
List *result = list;
foreach(l, list)
{
if (elem == lfirst(l))
break;
prev = l;
}
if (l != NIL)
{
if (prev == NIL)
result = lnext(l);
else
lnext(prev) = lnext(l);
pfree(l);
}
return result;
}
/*
* LispRemove
* Removes 'elem' from the linked list (destructively changing the list!).
* (If there is more than one equal list member, the first is removed.)
*
* This version matches 'elem' using equal().
* See also lremove.
*/
List *
LispRemove(void *elem, List *list)
{
List *l;
List *prev = NIL;
List *result = list;
foreach(l, list)
{
if (equal(elem, lfirst(l)))
break;
prev = l;
}
if (l != NIL)
{
if (prev == NIL)
result = lnext(l);
else
lnext(prev) = lnext(l);
pfree(l);
}
return result;
}
/*
* lremovei
* lremove() for integer lists.
*/
List *
lremovei(int elem, List *list)
{
List *l;
List *prev = NIL;
List *result = list;
foreach(l, list)
{
if (elem == lfirsti(l))
break;
prev = l;
}
if (l != NIL)
{
if (prev == NIL)
result = lnext(l);
else
lnext(prev) = lnext(l);
pfree(l);
}
return result;
}
/*
* ltruncate
* Truncate a list to n elements.
* Does nothing if n >= length(list).
* NB: the list is modified in-place!
*/
List *
ltruncate(int n, List *list)
{
List *ptr;
if (n <= 0)
return NIL; /* truncate to zero length */
foreach(ptr, list)
{
if (--n == 0)
{
lnext(ptr) = NIL;
break;
}
}
return list;
}
/*
* set_difference
*
* Return l1 without the elements in l2.
*
* The result is a fresh List, but it points to the same member nodes
* as were in l1.
*/
List *
set_difference(List *l1, List *l2)
{
List *result = NIL;
List *i;
if (l2 == NIL)
return listCopy(l1); /* slightly faster path for empty l2 */
foreach(i, l1)
{
if (!member(lfirst(i), l2))
result = lappend(result, lfirst(i));
}
return result;
}
/*
* set_differenceo
*
* Same as set_difference, but for Oid lists
*/
List *
set_differenceo(List *l1, List *l2)
{
List *result = NIL;
List *i;
if (l2 == NIL)
return listCopy(l1); /* slightly faster path for empty l2 */
foreach(i, l1)
{
if (!oidMember(lfirsto(i), l2))
result = lappendo(result, lfirsto(i));
}
return result;
}
/*
* set_ptrDifference
*
* Same as set_difference, when pointer-equality comparison is sufficient
*/
List *
set_ptrDifference(List *l1, List *l2)
{
List *result = NIL;
List *i;
if (l2 == NIL)
return listCopy(l1); /* slightly faster path for empty l2 */
foreach(i, l1)
{
if (!ptrMember(lfirst(i), l2))
result = lappend(result, lfirst(i));
}
return result;
}
/*
* Reverse a list, non-destructively
*/
#ifdef NOT_USED
List *
lreverse(List *l)
{
List *result = NIL;
List *i;
foreach(i, l)
result = lcons(lfirst(i), result);
return result;
}
#endif