Instead of Lists of integers, we now store variable-length bitmap sets.
This should be faster as well as less error-prone.
<!--
-$Header: /cvsroot/pgsql/doc/src/sgml/Attic/indexcost.sgml,v 2.15 2003/01/28 22:13:24 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/Attic/indexcost.sgml,v 2.16 2003/02/08 20:20:53 tgl Exp $
-->
<chapter id="indexcost">
<programlisting>
*indexSelectivity = clauselist_selectivity(root, indexQuals,
- lfirsti(rel->relids),
- JOIN_INNER);
+ rel->relid, JOIN_INNER);
</programlisting>
</para>
</step>
* Portions Copyright (c) 1994-5, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.100 2003/02/02 23:46:38 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.101 2003/02/08 20:20:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
if (outer_plan)
{
- if (intMember(OUTER, pull_varnos(node)))
+ Relids varnos = pull_varnos(node);
+
+ if (bms_is_member(OUTER, varnos))
outercontext = deparse_context_for_subplan("outer",
outer_plan->targetlist,
es->rtable);
else
outercontext = NULL;
+ bms_free(varnos);
}
else
outercontext = NULL;
int keyno;
List *tl;
char *exprstr;
+ Relids varnos;
int i;
if (nkeys <= 0)
* there are Vars with zero varno, use the tlist itself to determine
* their names.
*/
- if (intMember(0, pull_varnos((Node *) tlist)))
+ varnos = pull_varnos((Node *) tlist);
+ if (bms_is_member(0, varnos))
{
Node *outercontext;
es->rtable);
useprefix = length(es->rtable) > 1;
}
+ bms_free(varnos);
for (keyno = 1; keyno <= nkeys; keyno++)
{
# Makefile for backend/nodes
#
# IDENTIFICATION
-# $Header: /cvsroot/pgsql/src/backend/nodes/Makefile,v 1.13 2000/08/31 16:10:06 petere Exp $
+# $Header: /cvsroot/pgsql/src/backend/nodes/Makefile,v 1.14 2003/02/08 20:20:53 tgl Exp $
#
#-------------------------------------------------------------------------
top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
-OBJS = nodeFuncs.o nodes.o list.o \
+OBJS = nodeFuncs.o nodes.o list.o bitmapset.o \
copyfuncs.o equalfuncs.o makefuncs.o \
outfuncs.o readfuncs.o print.o read.o
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * bitmapset.c
+ * PostgreSQL generic bitmap set package
+ *
+ * A bitmap set can represent any set of nonnegative integers, although
+ * it is mainly intended for sets where the maximum value is not large,
+ * say at most a few hundred. By convention, a NULL pointer is always
+ * accepted by all operations to represent the empty set. (But beware
+ * that this is not the only representation of the empty set. Use
+ * bms_is_empty() in preference to testing for NULL.)
+ *
+ *
+ * Copyright (c) 2003, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/nodes/bitmapset.c,v 1.1 2003/02/08 20:20:53 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "nodes/bitmapset.h"
+
+
+#define WORDNUM(x) ((x) / BITS_PER_BITMAPWORD)
+#define BITNUM(x) ((x) % BITS_PER_BITMAPWORD)
+
+#define BITMAPSET_SIZE(nwords) \
+ (offsetof(Bitmapset, words) + (nwords) * sizeof(bitmapword))
+
+/*----------
+ * This is a well-known cute trick for isolating the rightmost one-bit
+ * in a word. It assumes two's complement arithmetic. Consider any
+ * nonzero value, and focus attention on the rightmost one. The value is
+ * then something like
+ * xxxxxx10000
+ * where x's are unspecified bits. The two's complement negative is formed
+ * by inverting all the bits and adding one. Inversion gives
+ * yyyyyy01111
+ * where each y is the inverse of the corresponding x. Incrementing gives
+ * yyyyyy10000
+ * and then ANDing with the original value gives
+ * 00000010000
+ * This works for all cases except original value = zero, where of course
+ * we get zero.
+ *----------
+ */
+#define RIGHTMOST_ONE(x) ((signedbitmapword) (x) & -((signedbitmapword) (x)))
+
+#define HAS_MULTIPLE_ONES(x) ((bitmapword) RIGHTMOST_ONE(x) != (x))
+
+
+/*
+ * Lookup tables to avoid need for bit-by-bit groveling
+ *
+ * rightmost_one_pos[x] gives the bit number (0-7) of the rightmost one bit
+ * in a nonzero byte value x. The entry for x=0 is never used.
+ *
+ * number_of_ones[x] gives the number of one-bits (0-8) in a byte value x.
+ *
+ * We could make these tables larger and reduce the number of iterations
+ * in the functions that use them, but bytewise shifts and masks are
+ * especially fast on many machines, so working a byte at a time seems best.
+ */
+
+static const uint8 rightmost_one_pos[256] = {
+ 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
+};
+
+static const uint8 number_of_ones[256] = {
+ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
+};
+
+
+/*
+ * bms_copy - make a palloc'd copy of a bitmapset
+ */
+Bitmapset *
+bms_copy(const Bitmapset *a)
+{
+ Bitmapset *result;
+ size_t size;
+
+ if (a == NULL)
+ return NULL;
+ size = BITMAPSET_SIZE(a->nwords);
+ result = (Bitmapset *) palloc(size);
+ memcpy(result, a, size);
+ return result;
+}
+
+/*
+ * bms_equal - are two bitmapsets equal?
+ *
+ * This is logical not physical equality; in particular, a NULL pointer will
+ * be reported as equal to a palloc'd value containing no members.
+ */
+bool
+bms_equal(const Bitmapset *a, const Bitmapset *b)
+{
+ const Bitmapset *shorter;
+ const Bitmapset *longer;
+ int shortlen;
+ int longlen;
+ int i;
+
+ /* Handle cases where either input is NULL */
+ if (a == NULL)
+ {
+ if (b == NULL)
+ return true;
+ return bms_is_empty(b);
+ }
+ else if (b == NULL)
+ {
+ return bms_is_empty(a);
+ }
+ /* Identify shorter and longer input */
+ if (a->nwords <= b->nwords)
+ {
+ shorter = a;
+ longer = b;
+ }
+ else
+ {
+ shorter = b;
+ longer = a;
+ }
+ /* And process */
+ shortlen = shorter->nwords;
+ for (i = 0; i < shortlen; i++)
+ {
+ if (shorter->words[i] != longer->words[i])
+ return false;
+ }
+ longlen = longer->nwords;
+ for (; i < longlen; i++)
+ {
+ if (longer->words[i] != 0)
+ return false;
+ }
+ return true;
+}
+
+/*
+ * bms_make_singleton - build a bitmapset containing a single member
+ */
+Bitmapset *
+bms_make_singleton(int x)
+{
+ Bitmapset *result;
+ int wordnum,
+ bitnum;
+
+ if (x < 0)
+ elog(ERROR, "bms_make_singleton: negative set member not allowed");
+ wordnum = WORDNUM(x);
+ bitnum = BITNUM(x);
+ result = (Bitmapset *) palloc0(BITMAPSET_SIZE(wordnum + 1));
+ result->nwords = wordnum + 1;
+ result->words[wordnum] = ((bitmapword) 1 << bitnum);
+ return result;
+}
+
+/*
+ * bms_free - free a bitmapset
+ *
+ * Same as pfree except for allowing NULL input
+ */
+void
+bms_free(Bitmapset *a)
+{
+ if (a)
+ pfree(a);
+}
+
+
+/*
+ * These operations all make a freshly palloc'd result,
+ * leaving their inputs untouched
+ */
+
+
+/*
+ * bms_union - set union
+ */
+Bitmapset *
+bms_union(const Bitmapset *a, const Bitmapset *b)
+{
+ Bitmapset *result;
+ const Bitmapset *other;
+ int otherlen;
+ int i;
+
+ /* Handle cases where either input is NULL */
+ if (a == NULL)
+ return bms_copy(b);
+ if (b == NULL)
+ return bms_copy(a);
+ /* Identify shorter and longer input; copy the longer one */
+ if (a->nwords <= b->nwords)
+ {
+ result = bms_copy(b);
+ other = a;
+ }
+ else
+ {
+ result = bms_copy(a);
+ other = b;
+ }
+ /* And union the shorter input into the result */
+ otherlen = other->nwords;
+ for (i = 0; i < otherlen; i++)
+ {
+ result->words[i] |= other->words[i];
+ }
+ return result;
+}
+
+/*
+ * bms_intersect - set intersection
+ */
+Bitmapset *
+bms_intersect(const Bitmapset *a, const Bitmapset *b)
+{
+ Bitmapset *result;
+ const Bitmapset *other;
+ int resultlen;
+ int i;
+
+ /* Handle cases where either input is NULL */
+ if (a == NULL || b == NULL)
+ return NULL;
+ /* Identify shorter and longer input; copy the shorter one */
+ if (a->nwords <= b->nwords)
+ {
+ result = bms_copy(a);
+ other = b;
+ }
+ else
+ {
+ result = bms_copy(b);
+ other = a;
+ }
+ /* And intersect the longer input with the result */
+ resultlen = result->nwords;
+ for (i = 0; i < resultlen; i++)
+ {
+ result->words[i] &= other->words[i];
+ }
+ return result;
+}
+
+/*
+ * bms_difference - set difference (ie, A without members of B)
+ */
+Bitmapset *
+bms_difference(const Bitmapset *a, const Bitmapset *b)
+{
+ Bitmapset *result;
+ int shortlen;
+ int i;
+
+ /* Handle cases where either input is NULL */
+ if (a == NULL)
+ return NULL;
+ if (b == NULL)
+ return bms_copy(a);
+ /* Copy the left input */
+ result = bms_copy(a);
+ /* And remove b's bits from result */
+ shortlen = Min(a->nwords, b->nwords);
+ for (i = 0; i < shortlen; i++)
+ {
+ result->words[i] &= ~ b->words[i];
+ }
+ return result;
+}
+
+/*
+ * bms_is_subset - is A a subset of B?
+ */
+bool
+bms_is_subset(const Bitmapset *a, const Bitmapset *b)
+{
+ int shortlen;
+ int longlen;
+ int i;
+
+ /* Handle cases where either input is NULL */
+ if (a == NULL)
+ return true; /* empty set is a subset of anything */
+ if (b == NULL)
+ return bms_is_empty(a);
+ /* Check common words */
+ shortlen = Min(a->nwords, b->nwords);
+ for (i = 0; i < shortlen; i++)
+ {
+ if ((a->words[i] & ~ b->words[i]) != 0)
+ return false;
+ }
+ /* Check extra words */
+ if (a->nwords > b->nwords)
+ {
+ longlen = a->nwords;
+ for (; i < longlen; i++)
+ {
+ if (a->words[i] != 0)
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
+ * bms_is_member - is X a member of A?
+ */
+bool
+bms_is_member(int x, const Bitmapset *a)
+{
+ int wordnum,
+ bitnum;
+
+ /* XXX better to just return false for x<0 ? */
+ if (x < 0)
+ elog(ERROR, "bms_is_member: negative set member not allowed");
+ if (a == NULL)
+ return false;
+ wordnum = WORDNUM(x);
+ bitnum = BITNUM(x);
+ if (wordnum >= a->nwords)
+ return false;
+ if ((a->words[wordnum] & ((bitmapword) 1 << bitnum)) != 0)
+ return true;
+ return false;
+}
+
+/*
+ * bms_overlap - do sets overlap (ie, have a nonempty intersection)?
+ */
+bool
+bms_overlap(const Bitmapset *a, const Bitmapset *b)
+{
+ int shortlen;
+ int i;
+
+ /* Handle cases where either input is NULL */
+ if (a == NULL || b == NULL)
+ return false;
+ /* Check words in common */
+ shortlen = Min(a->nwords, b->nwords);
+ for (i = 0; i < shortlen; i++)
+ {
+ if ((a->words[i] & b->words[i]) != 0)
+ return true;
+ }
+ return false;
+}
+
+/*
+ * bms_singleton_member - return the sole integer member of set
+ *
+ * Raises error if |a| is not 1.
+ */
+int
+bms_singleton_member(const Bitmapset *a)
+{
+ int result = -1;
+ int nwords;
+ int wordnum;
+
+ if (a == NULL)
+ elog(ERROR, "bms_singleton_member: set is empty");
+ nwords = a->nwords;
+ for (wordnum = 0; wordnum < nwords; wordnum++)
+ {
+ bitmapword w = a->words[wordnum];
+
+ if (w != 0)
+ {
+ if (result >= 0 || HAS_MULTIPLE_ONES(w))
+ elog(ERROR, "bms_singleton_member: set has multiple members");
+ result = wordnum * BITS_PER_BITMAPWORD;
+ while ((w & 255) == 0)
+ {
+ w >>= 8;
+ result += 8;
+ }
+ result += rightmost_one_pos[w & 255];
+ }
+ }
+ if (result < 0)
+ elog(ERROR, "bms_singleton_member: set is empty");
+ return result;
+}
+
+/*
+ * bms_num_members - count members of set
+ */
+int
+bms_num_members(const Bitmapset *a)
+{
+ int result = 0;
+ int nwords;
+ int wordnum;
+
+ if (a == NULL)
+ return 0;
+ nwords = a->nwords;
+ for (wordnum = 0; wordnum < nwords; wordnum++)
+ {
+ bitmapword w = a->words[wordnum];
+
+ /* we assume here that bitmapword is an unsigned type */
+ while (w != 0)
+ {
+ result += number_of_ones[w & 255];
+ w >>= 8;
+ }
+ }
+ return result;
+}
+
+/*
+ * bms_membership - does a set have zero, one, or multiple members?
+ *
+ * This is faster than making an exact count with bms_num_members().
+ */
+BMS_Membership
+bms_membership(const Bitmapset *a)
+{
+ BMS_Membership result = BMS_EMPTY_SET;
+ int nwords;
+ int wordnum;
+
+ if (a == NULL)
+ return BMS_EMPTY_SET;
+ nwords = a->nwords;
+ for (wordnum = 0; wordnum < nwords; wordnum++)
+ {
+ bitmapword w = a->words[wordnum];
+
+ if (w != 0)
+ {
+ if (result != BMS_EMPTY_SET || HAS_MULTIPLE_ONES(w))
+ return BMS_MULTIPLE;
+ result = BMS_SINGLETON;
+ }
+ }
+ return result;
+}
+
+/*
+ * bms_is_empty - is a set empty?
+ *
+ * This is even faster than bms_membership().
+ */
+bool
+bms_is_empty(const Bitmapset *a)
+{
+ int nwords;
+ int wordnum;
+
+ if (a == NULL)
+ return true;
+ nwords = a->nwords;
+ for (wordnum = 0; wordnum < nwords; wordnum++)
+ {
+ bitmapword w = a->words[wordnum];
+
+ if (w != 0)
+ return false;
+ }
+ return true;
+}
+
+
+/*
+ * These operations all "recycle" their non-const inputs, ie, either
+ * return the modified input or pfree it if it can't hold the result.
+ *
+ * These should generally be used in the style
+ *
+ * foo = bms_add_member(foo, x);
+ */
+
+
+/*
+ * bms_add_member - add a specified member to set
+ *
+ * Input set is modified or recycled!
+ */
+Bitmapset *
+bms_add_member(Bitmapset *a, int x)
+{
+ int wordnum,
+ bitnum;
+
+ if (x < 0)
+ elog(ERROR, "bms_add_member: negative set member not allowed");
+ if (a == NULL)
+ return bms_make_singleton(x);
+ wordnum = WORDNUM(x);
+ bitnum = BITNUM(x);
+ if (wordnum >= a->nwords)
+ {
+ /* Slow path: make a larger set and union the input set into it */
+ Bitmapset *result;
+ int nwords;
+ int i;
+
+ result = bms_make_singleton(x);
+ nwords = a->nwords;
+ for (i = 0; i < nwords; i++)
+ {
+ result->words[i] |= a->words[i];
+ }
+ pfree(a);
+ return result;
+ }
+ /* Fast path: x fits in existing set */
+ a->words[wordnum] |= ((bitmapword) 1 << bitnum);
+ return a;
+}
+
+/*
+ * bms_del_member - remove a specified member from set
+ *
+ * No error if x is not currently a member of set
+ *
+ * Input set is modified in-place!
+ */
+Bitmapset *
+bms_del_member(Bitmapset *a, int x)
+{
+ int wordnum,
+ bitnum;
+
+ if (x < 0)
+ elog(ERROR, "bms_del_member: negative set member not allowed");
+ if (a == NULL)
+ return NULL;
+ wordnum = WORDNUM(x);
+ bitnum = BITNUM(x);
+ if (wordnum < a->nwords)
+ {
+ a->words[wordnum] &= ~ ((bitmapword) 1 << bitnum);
+ }
+ return a;
+}
+
+/*
+ * bms_add_members - like bms_union, but left input is recycled
+ */
+Bitmapset *
+bms_add_members(Bitmapset *a, const Bitmapset *b)
+{
+ Bitmapset *result;
+ const Bitmapset *other;
+ int otherlen;
+ int i;
+
+ /* Handle cases where either input is NULL */
+ if (a == NULL)
+ return bms_copy(b);
+ if (b == NULL)
+ return a;
+ /* Identify shorter and longer input; copy the longer one if needed */
+ if (a->nwords < b->nwords)
+ {
+ result = bms_copy(b);
+ other = a;
+ }
+ else
+ {
+ result = a;
+ other = b;
+ }
+ /* And union the shorter input into the result */
+ otherlen = other->nwords;
+ for (i = 0; i < otherlen; i++)
+ {
+ result->words[i] |= other->words[i];
+ }
+ if (result != a)
+ pfree(a);
+ return result;
+}
+
+/*
+ * bms_int_members - like bms_intersect, but left input is recycled
+ */
+Bitmapset *
+bms_int_members(Bitmapset *a, const Bitmapset *b)
+{
+ int shortlen;
+ int i;
+
+ /* Handle cases where either input is NULL */
+ if (a == NULL)
+ return NULL;
+ if (b == NULL)
+ {
+ pfree(a);
+ return NULL;
+ }
+ /* Intersect b into a; we need never copy */
+ shortlen = Min(a->nwords, b->nwords);
+ for (i = 0; i < shortlen; i++)
+ {
+ a->words[i] &= b->words[i];
+ }
+ for (; i < a->nwords; i++)
+ {
+ a->words[i] = 0;
+ }
+ return a;
+}
+
+/*
+ * bms_del_members - like bms_difference, but left input is recycled
+ */
+Bitmapset *
+bms_del_members(Bitmapset *a, const Bitmapset *b)
+{
+ int shortlen;
+ int i;
+
+ /* Handle cases where either input is NULL */
+ if (a == NULL)
+ return NULL;
+ if (b == NULL)
+ return a;
+ /* Remove b's bits from a; we need never copy */
+ shortlen = Min(a->nwords, b->nwords);
+ for (i = 0; i < shortlen; i++)
+ {
+ a->words[i] &= ~ b->words[i];
+ }
+ return a;
+}
+
+/*
+ * bms_join - like bms_union, but *both* inputs are recycled
+ */
+Bitmapset *
+bms_join(Bitmapset *a, Bitmapset *b)
+{
+ Bitmapset *result;
+ Bitmapset *other;
+ int otherlen;
+ int i;
+
+ /* Handle cases where either input is NULL */
+ if (a == NULL)
+ return b;
+ if (b == NULL)
+ return a;
+ /* Identify shorter and longer input; use longer one as result */
+ if (a->nwords < b->nwords)
+ {
+ result = b;
+ other = a;
+ }
+ else
+ {
+ result = a;
+ other = b;
+ }
+ /* And union the shorter input into the result */
+ otherlen = other->nwords;
+ for (i = 0; i < otherlen; i++)
+ {
+ result->words[i] |= other->words[i];
+ }
+ if (other != result) /* pure paranoia */
+ pfree(other);
+ return result;
+}
+
+/*----------
+ * bms_first_member - find and remove first member of a set
+ *
+ * Returns -1 if set is empty. NB: set is destructively modified!
+ *
+ * This is intended as support for iterating through the members of a set.
+ * The typical pattern is
+ *
+ * tmpset = bms_copy(inputset);
+ * while ((x = bms_first_member(tmpset)) >= 0)
+ * {
+ * process member x;
+ * }
+ * bms_free(tmpset);
+ *----------
+ */
+int
+bms_first_member(Bitmapset *a)
+{
+ int nwords;
+ int wordnum;
+
+ if (a == NULL)
+ return -1;
+ nwords = a->nwords;
+ for (wordnum = 0; wordnum < nwords; wordnum++)
+ {
+ bitmapword w = a->words[wordnum];
+
+ if (w != 0)
+ {
+ int result;
+
+ w = RIGHTMOST_ONE(w);
+ a->words[wordnum] &= ~ w;
+
+ result = wordnum * BITS_PER_BITMAPWORD;
+ while ((w & 255) == 0)
+ {
+ w >>= 8;
+ result += 8;
+ }
+ result += rightmost_one_pos[w & 255];
+ return result;
+ }
+ }
+ return -1;
+}
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.239 2003/02/03 21:15:43 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.240 2003/02/08 20:20:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#define COPY_INTLIST_FIELD(fldname) \
(newnode->fldname = listCopy(from->fldname))
+/* Copy a field that is a pointer to a Bitmapset */
+#define COPY_BITMAPSET_FIELD(fldname) \
+ (newnode->fldname = bms_copy(from->fldname))
+
/* Copy a field that is a pointer to a C string, or perhaps NULL */
#define COPY_STRING_FIELD(fldname) \
(newnode->fldname = from->fldname ? pstrdup(from->fldname) : (char *) NULL)
COPY_NODE_FIELD(subclauseindices); /* XXX probably bad */
COPY_SCALAR_FIELD(eval_cost);
COPY_SCALAR_FIELD(this_selec);
- COPY_INTLIST_FIELD(left_relids);
- COPY_INTLIST_FIELD(right_relids);
+ COPY_BITMAPSET_FIELD(left_relids);
+ COPY_BITMAPSET_FIELD(right_relids);
COPY_SCALAR_FIELD(mergejoinoperator);
COPY_SCALAR_FIELD(left_sortop);
COPY_SCALAR_FIELD(right_sortop);
{
JoinInfo *newnode = makeNode(JoinInfo);
- COPY_INTLIST_FIELD(unjoined_relids);
+ COPY_BITMAPSET_FIELD(unjoined_relids);
COPY_NODE_FIELD(jinfo_restrictinfo);
return newnode;
{
InClauseInfo *newnode = makeNode(InClauseInfo);
- COPY_INTLIST_FIELD(lefthand);
- COPY_INTLIST_FIELD(righthand);
+ COPY_BITMAPSET_FIELD(lefthand);
+ COPY_BITMAPSET_FIELD(righthand);
COPY_NODE_FIELD(sub_targetlist);
return newnode;
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.183 2003/02/03 21:15:44 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.184 2003/02/08 20:20:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
return false; \
} while (0)
+/* Compare a field that is a pointer to a Bitmapset */
+#define COMPARE_BITMAPSET_FIELD(fldname) \
+ do { \
+ if (!bms_equal(a->fldname, b->fldname)) \
+ return false; \
+ } while (0)
+
/* Compare a field that is a pointer to a C string, or perhaps NULL */
#define COMPARE_STRING_FIELD(fldname) \
do { \
static bool
_equalJoinInfo(JoinInfo *a, JoinInfo *b)
{
- COMPARE_INTLIST_FIELD(unjoined_relids);
+ COMPARE_BITMAPSET_FIELD(unjoined_relids);
COMPARE_NODE_FIELD(jinfo_restrictinfo);
return true;
static bool
_equalInClauseInfo(InClauseInfo *a, InClauseInfo *b)
{
- COMPARE_INTLIST_FIELD(lefthand);
- COMPARE_INTLIST_FIELD(righthand);
+ COMPARE_BITMAPSET_FIELD(lefthand);
+ COMPARE_BITMAPSET_FIELD(righthand);
COMPARE_NODE_FIELD(sub_targetlist);
return true;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.46 2003/01/27 20:51:49 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.47 2003/02/08 20:20:54 tgl Exp $
*
* NOTES
* XXX a few of the following functions are duplicated to handle
}
/*
- * llasti
- *
- * As above, but for integer lists
- */
-int
-llasti(List *l)
-{
- if (l == NIL)
- elog(ERROR, "llasti: empty list");
- while (lnext(l) != NIL)
- l = lnext(l);
- return lfirsti(l);
-}
-
-/*
* freeList
*
* Free the List nodes of a list
}
/*
- * sameseti
- *
- * Returns t if two integer lists contain the same elements
- * (but unlike equali(), they need not be in the same order)
- *
- * Caution: this routine could be fooled if list1 contains
- * duplicate elements. It is intended to be used on lists
- * containing only nonduplicate elements, eg Relids lists.
- */
-bool
-sameseti(List *list1, List *list2)
-{
- List *temp;
-
- if (list1 == NIL)
- return list2 == NIL;
- if (list2 == NIL)
- return false;
- if (length(list1) != length(list2))
- return false;
- foreach(temp, list1)
- {
- if (!intMember(lfirsti(temp), list2))
- return false;
- }
- return true;
-}
-
-/*
* Generate the union of two lists,
* ie, l1 plus all members of l2 that are not already in l1.
*
* 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)
{
}
return retval;
}
-#endif
List *
set_intersecti(List *l1, List *l2)
/*
* Reverse a list, non-destructively
*/
+#ifdef NOT_USED
List *
lreverse(List *l)
{
result = lcons(lfirst(i), result);
return result;
}
-
-/*
- * Return t if two integer lists have any members in common.
- */
-bool
-overlap_setsi(List *list1, List *list2)
-{
- List *x;
-
- foreach(x, list1)
- {
- int e = lfirsti(x);
-
- if (intMember(e, list2))
- return true;
- }
- return false;
-}
-
-/*
- * Return t if all members of integer list list1 appear in list2.
- */
-bool
-is_subseti(List *list1, List *list2)
-{
- List *x;
-
- foreach(x, list1)
- {
- int e = lfirsti(x);
-
- if (!intMember(e, list2))
- return false;
- }
- return true;
-}
+#endif
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.195 2003/02/03 21:15:44 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.196 2003/02/08 20:20:54 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
(appendStringInfo(str, " :" CppAsString(fldname) " "), \
_outOidList(str, node->fldname))
+/* Write a bitmapset field */
+#define WRITE_BITMAPSET_FIELD(fldname) \
+ (appendStringInfo(str, " :" CppAsString(fldname) " "), \
+ _outBitmapset(str, node->fldname))
+
#define booltostr(x) ((x) ? "true" : "false")
}
/*
+ * _outBitmapset -
+ * converts a bitmap set of integers
+ *
+ * Note: for historical reasons, the output is formatted exactly like
+ * an integer List would be.
+ */
+static void
+_outBitmapset(StringInfo str, Bitmapset *bms)
+{
+ Bitmapset *tmpset;
+ int x;
+
+ appendStringInfoChar(str, '(');
+ tmpset = bms_copy(bms);
+ while ((x = bms_first_member(tmpset)) >= 0)
+ {
+ appendStringInfo(str, " %d", x);
+ }
+ bms_free(tmpset);
+ appendStringInfoChar(str, ')');
+}
+
+/*
* Print the value of a Datum given its type.
*/
static void
WRITE_NODE_FIELD(clause);
WRITE_BOOL_FIELD(ispusheddown);
WRITE_NODE_FIELD(subclauseindices);
- WRITE_INTLIST_FIELD(left_relids);
- WRITE_INTLIST_FIELD(right_relids);
+ WRITE_BITMAPSET_FIELD(left_relids);
+ WRITE_BITMAPSET_FIELD(right_relids);
WRITE_OID_FIELD(mergejoinoperator);
WRITE_OID_FIELD(left_sortop);
WRITE_OID_FIELD(right_sortop);
{
WRITE_NODE_TYPE("JOININFO");
- WRITE_INTLIST_FIELD(unjoined_relids);
+ WRITE_BITMAPSET_FIELD(unjoined_relids);
WRITE_NODE_FIELD(jinfo_restrictinfo);
}
{
WRITE_NODE_TYPE("INCLAUSEINFO");
- WRITE_INTLIST_FIELD(lefthand);
- WRITE_INTLIST_FIELD(righthand);
+ WRITE_BITMAPSET_FIELD(lefthand);
+ WRITE_BITMAPSET_FIELD(righthand);
WRITE_NODE_FIELD(sub_targetlist);
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.95 2003/01/25 23:10:27 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.96 2003/02/08 20:20:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* The result should join all the query's base rels.
*/
- Assert(length(rel->relids) == length(root->base_rel_list));
+ Assert(bms_num_members(rel->relids) == length(root->base_rel_list));
return rel;
}
foreach(rellist, root->base_rel_list)
{
RelOptInfo *rel = (RelOptInfo *) lfirst(rellist);
- Index rti;
+ Index rti = rel->relid;
RangeTblEntry *rte;
List *inheritlist;
- Assert(length(rel->relids) == 1); /* better be base rel */
- rti = lfirsti(rel->relids);
+ Assert(rti > 0); /* better be base rel */
rte = rt_fetch(rti, root->rtable);
if (rel->rtekind == RTE_SUBQUERY)
static void
print_relids(Relids relids)
{
- List *l;
+ Relids tmprelids;
+ int x;
+ bool first = true;
- foreach(l, relids)
+ tmprelids = bms_copy(relids);
+ while ((x = bms_first_member(tmprelids)) >= 0)
{
- printf("%d", lfirsti(l));
- if (lnext(l))
+ if (!first)
printf(" ");
+ printf("%d", x);
+ first = false;
}
+ bms_free(tmprelids);
}
static void
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.104 2003/01/28 22:13:33 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.105 2003/02/08 20:20:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Cost cpu_per_tuple;
/* Should only be applied to base relations */
- Assert(length(baserel->relids) == 1);
+ Assert(baserel->relid > 0);
Assert(baserel->rtekind == RTE_RELATION);
if (!enable_seqscan)
/* Should only be applied to base relations */
Assert(IsA(baserel, RelOptInfo) &&
IsA(index, IndexOptInfo));
- Assert(length(baserel->relids) == 1);
+ Assert(baserel->relid > 0);
Assert(baserel->rtekind == RTE_RELATION);
if (!enable_indexscan)
int ntuples = length(tideval);
/* Should only be applied to base relations */
- Assert(length(baserel->relids) == 1);
+ Assert(baserel->relid > 0);
Assert(baserel->rtekind == RTE_RELATION);
if (!enable_tidscan)
Cost cpu_per_tuple;
/* Should only be applied to base relations that are functions */
- Assert(length(baserel->relids) == 1);
+ Assert(baserel->relid > 0);
Assert(baserel->rtekind == RTE_FUNCTION);
/*
&firstclause->left_mergescansel,
&firstclause->right_mergescansel);
- if (is_subseti(firstclause->left_relids, outer_path->parent->relids))
+ if (bms_is_subset(firstclause->left_relids, outer_path->parent->relids))
{
/* left side of clause is outer */
outerscansel = firstclause->left_mergescansel;
* planning a large query, we cache the bucketsize estimate in the
* RestrictInfo node to avoid repeated lookups of statistics.
*/
- if (is_subseti(restrictinfo->right_relids,
- inner_path->parent->relids))
+ if (bms_is_subset(restrictinfo->right_relids,
+ inner_path->parent->relids))
{
/* righthand side is inner */
thisbucketsize = restrictinfo->right_bucketsize;
}
else
{
- Assert(is_subseti(restrictinfo->left_relids,
- inner_path->parent->relids));
+ Assert(bms_is_subset(restrictinfo->left_relids,
+ inner_path->parent->relids));
/* lefthand side is inner */
thisbucketsize = restrictinfo->left_bucketsize;
if (thisbucketsize < 0)
double temp;
/* Should only be applied to base relations */
- Assert(length(rel->relids) == 1);
+ Assert(rel->relid > 0);
temp = rel->tuples *
restrictlist_selectivity(root,
rel->baserestrictinfo,
- lfirsti(rel->relids),
+ rel->relid,
JOIN_INNER);
/*
double temp;
/* Should only be applied to base relations that are functions */
- Assert(length(rel->relids) == 1);
+ Assert(rel->relid > 0);
Assert(rel->rtekind == RTE_FUNCTION);
/*
temp = rel->tuples *
restrictlist_selectivity(root,
rel->baserestrictinfo,
- lfirsti(rel->relids),
+ rel->relid,
JOIN_INNER);
/*
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.134 2003/01/28 22:13:33 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.135 2003/02/08 20:20:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
List *restrictinfo_list = rel->baserestrictinfo;
List *joininfo_list = rel->joininfo;
- Relids all_join_outerrelids = NIL;
+ Relids all_join_outerrelids = NULL;
List *ilist;
foreach(ilist, rel->indexlist)
*/
if (index->indpred != NIL)
if (!pred_test(index->indpred, restrictinfo_list, joininfo_list,
- lfirsti(rel->relids)))
+ rel->relid))
continue;
/*
/*
* 6. Examine join clauses to see which ones are potentially
- * usable with this index, and generate a list of all other relids
- * that participate in such join clauses. We'll use this list later
+ * usable with this index, and generate the set of all other relids
+ * that participate in such join clauses. We'll use this set later
* to recognize outer rels that are equivalent for joining purposes.
- * We compute both per-index and overall-for-relation lists.
+ * We compute both per-index and overall-for-relation sets.
*/
join_outerrelids = indexable_outerrelids(rel, index);
index->outer_relids = join_outerrelids;
- all_join_outerrelids = set_unioni(all_join_outerrelids,
- join_outerrelids);
+ all_join_outerrelids = bms_add_members(all_join_outerrelids,
+ join_outerrelids);
}
rel->index_outer_relids = all_join_outerrelids;
JoinInfo *joininfo = (JoinInfo *) lfirst(i);
List *j;
- if (!is_subseti(joininfo->unjoined_relids, outer_relids))
+ if (!bms_is_subset(joininfo->unjoined_relids, outer_relids))
continue;
foreach(j, joininfo->jinfo_restrictinfo)
*/
if (match_index_to_operand(indexkey, leftop, rel, index))
{
- List *othervarnos = pull_varnos(rightop);
+ Relids othervarnos = pull_varnos(rightop);
bool isIndexable;
isIndexable =
- !intMember(lfirsti(rel->relids), othervarnos) &&
+ !bms_overlap(rel->relids, othervarnos) &&
!contain_volatile_functions(rightop) &&
is_indexable_operator(clause, opclass, true);
- freeList(othervarnos);
+ bms_free(othervarnos);
return isIndexable;
}
if (match_index_to_operand(indexkey, rightop, rel, index))
{
- List *othervarnos = pull_varnos(leftop);
+ Relids othervarnos = pull_varnos(leftop);
bool isIndexable;
isIndexable =
- !intMember(lfirsti(rel->relids), othervarnos) &&
+ !bms_overlap(rel->relids, othervarnos) &&
!contain_volatile_functions(leftop) &&
is_indexable_operator(clause, opclass, false);
- freeList(othervarnos);
+ bms_free(othervarnos);
return isIndexable;
}
/*
* indexable_outerrelids
* Finds all other relids that participate in any indexable join clause
- * for the specified index. Returns a list of relids.
+ * for the specified index. Returns a set of relids.
*
* 'rel' is the relation for which 'index' is defined
*/
static Relids
indexable_outerrelids(RelOptInfo *rel, IndexOptInfo *index)
{
- Relids outer_relids = NIL;
+ Relids outer_relids = NULL;
List *i;
foreach(i, rel->joininfo)
if (match_found)
{
- outer_relids = set_unioni(outer_relids,
- joininfo->unjoined_relids);
+ outer_relids = bms_add_members(outer_relids,
+ joininfo->unjoined_relids);
}
}
/*
* If there are no indexable joinclauses for this rel, exit quickly.
*/
- if (!rel->index_outer_relids)
+ if (bms_is_empty(rel->index_outer_relids))
return NULL;
/*
* Otherwise, we have to do path selection in the memory context of
* to find the set of outer relids actually relevant for this index.
* If there are none, again we can fail immediately.
*/
- outer_relids = set_intersecti(rel->index_outer_relids, outer_relids);
- if (!outer_relids)
+ outer_relids = bms_intersect(rel->index_outer_relids, outer_relids);
+ if (bms_is_empty(outer_relids))
{
+ bms_free(outer_relids);
MemoryContextSwitchTo(oldcontext);
return NULL;
}
foreach(jlist, rel->index_inner_paths)
{
info = (InnerIndexscanInfo *) lfirst(jlist);
- if (sameseti(info->other_relids, outer_relids) &&
+ if (bms_equal(info->other_relids, outer_relids) &&
info->isouterjoin == isouterjoin)
{
- freeList(outer_relids);
+ bms_free(outer_relids);
MemoryContextSwitchTo(oldcontext);
return info->best_innerpath;
}
Relids index_outer_relids;
Path *path = NULL;
- /* skip quickly if index has no useful join clauses */
- if (!index->outer_relids)
- continue;
/* identify set of relevant outer relids for this index */
- index_outer_relids = set_intersecti(index->outer_relids, outer_relids);
- if (!index_outer_relids)
+ index_outer_relids = bms_intersect(index->outer_relids, outer_relids);
+ /* skip if none */
+ if (bms_is_empty(index_outer_relids))
+ {
+ bms_free(index_outer_relids);
continue;
+ }
/*
* Look to see if we already computed the result for this index.
*/
foreach(jlist, index->inner_paths)
{
info = (InnerIndexscanInfo *) lfirst(jlist);
- if (sameseti(info->other_relids, index_outer_relids) &&
+ if (bms_equal(info->other_relids, index_outer_relids) &&
info->isouterjoin == isouterjoin)
{
path = info->best_innerpath;
- freeList(index_outer_relids); /* not needed anymore */
+ bms_free(index_outer_relids); /* not needed anymore */
break;
}
}
restrictlist_selectivity(root,
set_ptrUnion(rel->baserestrictinfo,
clausegroup),
- lfirsti(rel->relids),
+ rel->relid,
JOIN_INNER);
/* Like costsize.c, force estimate to be at least one row */
if (pathnode->rows < 1.0)
* Simple index.
*/
if (operand && IsA(operand, Var) &&
- lfirsti(rel->relids) == ((Var *) operand)->varno &&
+ rel->relid == ((Var *) operand)->varno &&
indexkey == ((Var *) operand)->varattno)
return true;
else
static bool
function_index_operand(Expr *funcOpnd, RelOptInfo *rel, IndexOptInfo *index)
{
- int relvarno = lfirsti(rel->relids);
FuncExpr *function;
List *funcargs;
int *indexKeys = index->indexkeys;
return false;
if (indexKeys[i] == 0)
return false;
- if (var->varno != relvarno || var->varattno != indexKeys[i])
+ if (var->varno != rel->relid || var->varattno != indexKeys[i])
return false;
i++;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.77 2003/01/27 20:51:51 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.78 2003/02/08 20:20:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i);
- if (restrictinfo->left_relids == NIL ||
+ if (restrictinfo->left_relids == NULL ||
restrictinfo->hashjoinoperator == InvalidOid)
continue; /* not hashjoinable */
/*
* Check if clause is usable with these input rels.
*/
- if (is_subseti(restrictinfo->left_relids, outerrel->relids) &&
- is_subseti(restrictinfo->right_relids, innerrel->relids))
+ if (bms_is_subset(restrictinfo->left_relids, outerrel->relids) &&
+ bms_is_subset(restrictinfo->right_relids, innerrel->relids))
{
/* righthand side is inner */
}
- else if (is_subseti(restrictinfo->left_relids, innerrel->relids) &&
- is_subseti(restrictinfo->right_relids, outerrel->relids))
+ else if (bms_is_subset(restrictinfo->left_relids, innerrel->relids) &&
+ bms_is_subset(restrictinfo->right_relids, outerrel->relids))
{
/* lefthand side is inner */
}
switch (jointype)
{
case JOIN_RIGHT:
- if (restrictinfo->left_relids == NIL ||
+ if (restrictinfo->left_relids == NULL ||
restrictinfo->mergejoinoperator == InvalidOid)
return NIL; /* not mergejoinable */
break;
case JOIN_FULL:
- if (restrictinfo->left_relids == NIL ||
+ if (restrictinfo->left_relids == NULL ||
restrictinfo->mergejoinoperator == InvalidOid)
elog(ERROR, "FULL JOIN is only supported with mergejoinable join conditions");
break;
}
}
- if (restrictinfo->left_relids == NIL ||
+ if (restrictinfo->left_relids == NULL ||
restrictinfo->mergejoinoperator == InvalidOid)
continue; /* not mergejoinable */
* needed on each side of the clause must be available from one or
* the other of the input rels.
*/
- if (is_subseti(restrictinfo->left_relids, outerrel->relids) &&
- is_subseti(restrictinfo->right_relids, innerrel->relids))
+ if (bms_is_subset(restrictinfo->left_relids, outerrel->relids) &&
+ bms_is_subset(restrictinfo->right_relids, innerrel->relids))
{
/* righthand side is inner */
}
- else if (is_subseti(restrictinfo->left_relids, innerrel->relids) &&
- is_subseti(restrictinfo->right_relids, outerrel->relids))
+ else if (bms_is_subset(restrictinfo->left_relids, innerrel->relids) &&
+ bms_is_subset(restrictinfo->right_relids, outerrel->relids))
{
/* lefthand side is inner */
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.59 2003/01/20 18:54:51 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.60 2003/02/08 20:20:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
RelOptInfo *new_rel = (RelOptInfo *) lfirst(r2);
- if (nonoverlap_setsi(old_rel->relids, new_rel->relids))
+ if (!bms_overlap(old_rel->relids, new_rel->relids))
{
List *i;
{
JoinInfo *joininfo = (JoinInfo *) lfirst(i);
- if (is_subseti(joininfo->unjoined_relids,
- new_rel->relids))
+ if (bms_is_subset(joininfo->unjoined_relids,
+ new_rel->relids))
{
RelOptInfo *jrel;
{
RelOptInfo *other_rel = (RelOptInfo *) lfirst(j);
- if (is_subseti(unjoined_relids, other_rel->relids))
+ if (bms_is_subset(unjoined_relids, other_rel->relids))
{
RelOptInfo *jrel;
{
RelOptInfo *other_rel = (RelOptInfo *) lfirst(i);
- if (nonoverlap_setsi(other_rel->relids, old_rel->relids))
+ if (!bms_overlap(other_rel->relids, old_rel->relids))
{
RelOptInfo *jrel;
make_join_rel(Query *root, RelOptInfo *rel1, RelOptInfo *rel2,
JoinType jointype)
{
- List *joinrelids;
+ Relids joinrelids;
RelOptInfo *joinrel;
List *restrictlist;
/* We should never try to join two overlapping sets of rels. */
- Assert(nonoverlap_setsi(rel1->relids, rel2->relids));
+ Assert(!bms_overlap(rel1->relids, rel2->relids));
/* Construct Relids set that identifies the joinrel. */
- joinrelids = nconc(listCopy(rel1->relids), listCopy(rel2->relids));
+ joinrelids = bms_union(rel1->relids, rel2->relids);
/*
* If we are implementing IN clauses as joins, there are some joins
/*
* Cannot join if proposed join contains part, but only
* part, of the RHS, *and* it contains rels not in the RHS.
- *
- * Singleton RHS cannot be a problem, so skip expensive tests.
*/
- if (length(ininfo->righthand) > 1 &&
- overlap_setsi(ininfo->righthand, joinrelids) &&
- !is_subseti(ininfo->righthand, joinrelids) &&
- !is_subseti(joinrelids, ininfo->righthand))
+ if (bms_overlap(ininfo->righthand, joinrelids) &&
+ !bms_is_subset(ininfo->righthand, joinrelids) &&
+ !bms_is_subset(joinrelids, ininfo->righthand))
{
- freeList(joinrelids);
+ bms_free(joinrelids);
return NULL;
}
* No issue unless we are looking at a join of the IN's RHS
* to other stuff.
*/
- if (! (length(ininfo->righthand) < length(joinrelids) &&
- is_subseti(ininfo->righthand, joinrelids)))
+ if (! (bms_is_subset(ininfo->righthand, joinrelids) &&
+ !bms_equal(ininfo->righthand, joinrelids)))
continue;
/*
* If we already joined IN's RHS to any part of its LHS in either
* input path, then this join is not constrained (the necessary
* work was done at a lower level).
*/
- if (overlap_setsi(ininfo->lefthand, rel1->relids) &&
- is_subseti(ininfo->righthand, rel1->relids))
+ if (bms_overlap(ininfo->lefthand, rel1->relids) &&
+ bms_is_subset(ininfo->righthand, rel1->relids))
continue;
- if (overlap_setsi(ininfo->lefthand, rel2->relids) &&
- is_subseti(ininfo->righthand, rel2->relids))
+ if (bms_overlap(ininfo->lefthand, rel2->relids) &&
+ bms_is_subset(ininfo->righthand, rel2->relids))
continue;
/*
* JOIN_IN technique will work if outerrel includes LHS and
*/
if (jointype != JOIN_INNER)
{
- freeList(joinrelids);
+ bms_free(joinrelids);
return NULL;
}
- if (is_subseti(ininfo->lefthand, rel1->relids) &&
- sameseti(ininfo->righthand, rel2->relids))
+ if (bms_is_subset(ininfo->lefthand, rel1->relids) &&
+ bms_equal(ininfo->righthand, rel2->relids))
{
jointype = JOIN_IN;
}
- else if (is_subseti(ininfo->lefthand, rel2->relids) &&
- sameseti(ininfo->righthand, rel1->relids))
+ else if (bms_is_subset(ininfo->lefthand, rel2->relids) &&
+ bms_equal(ininfo->righthand, rel1->relids))
{
jointype = JOIN_REVERSE_IN;
}
- else if (sameseti(ininfo->righthand, rel1->relids))
+ else if (bms_equal(ininfo->righthand, rel1->relids))
{
jointype = JOIN_UNIQUE_OUTER;
}
- else if (sameseti(ininfo->righthand, rel2->relids))
+ else if (bms_equal(ininfo->righthand, rel2->relids))
{
jointype = JOIN_UNIQUE_INNER;
}
else
{
/* invalid join path */
- freeList(joinrelids);
+ bms_free(joinrelids);
return NULL;
}
}
break;
}
- freeList(joinrelids);
+ bms_free(joinrelids);
return joinrel;
}
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.45 2003/01/24 03:58:35 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.46 2003/02/08 20:20:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* Collect info about relids mentioned in each item. For this
* routine we only really care whether there are any at all in
* each item, but process_implied_equality() needs the exact
- * lists, so we may as well pull them here.
+ * sets, so we may as well pull them here.
*/
relids = (Relids *) palloc(nitems * sizeof(Relids));
have_consts = false;
PathKeyItem *item1 = (PathKeyItem *) lfirst(ptr1);
relids[i1] = pull_varnos(item1->key);
- if (relids[i1] == NIL)
+ if (bms_is_empty(relids[i1]))
have_consts = true;
i1++;
}
foreach(ptr1, curset)
{
PathKeyItem *item1 = (PathKeyItem *) lfirst(ptr1);
+ bool i1_is_variable = !bms_is_empty(relids[i1]);
List *ptr2;
int i2 = i1 + 1;
foreach(ptr2, lnext(ptr1))
{
PathKeyItem *item2 = (PathKeyItem *) lfirst(ptr2);
+ bool i2_is_variable = !bms_is_empty(relids[i2]);
/*
* If it's "const = const" then just ignore it altogether.
* propagating the comparison to Vars will cause us to
* produce zero rows out, as expected.)
*/
- if (relids[i1] != NIL || relids[i2] != NIL)
+ if (i1_is_variable || i2_is_variable)
{
/*
* Tell process_implied_equality to delete the clause,
* present in the list.
*/
bool delete_it = (have_consts &&
- relids[i1] != NIL &&
- relids[i2] != NIL);
+ i1_is_variable &&
+ i2_is_variable);
+
process_implied_equality(root,
item1->key, item2->key,
item1->sortop, item2->sortop,
find_indexkey_var(Query *root, RelOptInfo *rel, AttrNumber varattno)
{
List *temp;
- int relid;
+ Index relid;
Oid reloid,
vartypeid;
int32 type_mod;
return tle_var;
}
- relid = lfirsti(rel->relids);
+ relid = rel->relid;
+ Assert(relid > 0);
reloid = getrelid(relid, root->rtable);
vartypeid = get_atttype(reloid, varattno);
type_mod = get_atttypmod(reloid, varattno);
cache_mergeclause_pathkeys(root, restrictinfo);
- if (is_subseti(restrictinfo->left_relids, rel->relids))
+ if (bms_is_subset(restrictinfo->left_relids, rel->relids))
{
/* Rel is left side of mergeclause */
pathkey = restrictinfo->left_pathkey;
}
- else if (is_subseti(restrictinfo->right_relids, rel->relids))
+ else if (bms_is_subset(restrictinfo->right_relids, rel->relids))
{
/* Rel is right side of mergeclause */
pathkey = restrictinfo->right_pathkey;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/tidpath.c,v 1.13 2002/12/12 15:49:32 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/tidpath.c,v 1.14 2003/02/08 20:20:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "parser/parse_coerce.h"
#include "utils/lsyscache.h"
-static List *TidqualFromRestrictinfo(List *relids, List *restrictinfo);
+static List *TidqualFromRestrictinfo(Relids relids, List *restrictinfo);
static bool isEvaluable(int varno, Node *node);
static Node *TidequalClause(int varno, OpExpr *node);
static List *TidqualFromExpr(int varno, Expr *expr);
}
static List *
-TidqualFromRestrictinfo(List *relids, List *restrictinfo)
+TidqualFromRestrictinfo(Relids relids, List *restrictinfo)
{
List *lst,
*rlst = NIL;
Node *node;
Expr *expr;
- if (length(relids) != 1)
+ if (bms_membership(relids) != BMS_SINGLETON)
return NIL;
- varno = lfirsti(relids);
+ varno = bms_singleton_member(relids);
foreach(lst, restrictinfo)
{
node = lfirst(lst);
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.134 2003/02/03 15:07:07 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.135 2003/02/08 20:20:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static void fix_indxqual_references(List *indexquals, IndexPath *index_path,
List **fixed_indexquals,
List **recheck_indexquals);
-static void fix_indxqual_sublist(List *indexqual, int baserelid,
- IndexOptInfo *index,
- List **fixed_quals, List **recheck_quals);
+static void fix_indxqual_sublist(List *indexqual,
+ Relids baserelids, int baserelid,
+ IndexOptInfo *index,
+ List **fixed_quals, List **recheck_quals);
static Node *fix_indxqual_operand(Node *node, int baserelid,
IndexOptInfo *index,
Oid *opclass);
-static List *get_switched_clauses(List *clauses, List *outerrelids);
+static List *get_switched_clauses(List *clauses, Relids outerrelids);
static List *order_qual_clauses(Query *root, List *clauses);
static void copy_path_costsize(Plan *dest, Path *src);
static void copy_plan_costsize(Plan *dest, Plan *src);
Plan *lefttree, Plan *righttree,
JoinType jointype);
static Sort *make_sort_from_pathkeys(Query *root, Plan *lefttree,
- List *relids, List *pathkeys);
+ Relids relids, List *pathkeys);
/*
{
InClauseInfo *ininfo = (InClauseInfo *) lfirst(l);
- if (sameseti(ininfo->righthand, best_path->path.parent->relids))
+ if (bms_equal(ininfo->righthand, best_path->path.parent->relids))
{
sub_targetlist = ininfo->sub_targetlist;
break;
create_seqscan_plan(Path *best_path, List *tlist, List *scan_clauses)
{
SeqScan *scan_plan;
- Index scan_relid;
+ Index scan_relid = best_path->parent->relid;
- /* there should be exactly one base rel involved... */
- Assert(length(best_path->parent->relids) == 1);
+ /* it should be a base rel... */
+ Assert(scan_relid > 0);
Assert(best_path->parent->rtekind == RTE_RELATION);
- scan_relid = (Index) lfirsti(best_path->parent->relids);
-
scan_plan = make_seqscan(tlist,
scan_clauses,
scan_relid);
List *scan_clauses)
{
List *indxqual = best_path->indexqual;
- Index baserelid;
+ Index baserelid = best_path->path.parent->relid;
List *qpqual;
Expr *indxqual_or_expr = NULL;
List *fixed_indxqual;
List *ixinfo;
IndexScan *scan_plan;
- /* there should be exactly one base rel involved... */
- Assert(length(best_path->path.parent->relids) == 1);
+ /* it should be a base rel... */
+ Assert(baserelid > 0);
Assert(best_path->path.parent->rtekind == RTE_RELATION);
- baserelid = lfirsti(best_path->path.parent->relids);
-
/*
* Build list of index OIDs.
*/
create_tidscan_plan(TidPath *best_path, List *tlist, List *scan_clauses)
{
TidScan *scan_plan;
- Index scan_relid;
+ Index scan_relid = best_path->path.parent->relid;
- /* there should be exactly one base rel involved... */
- Assert(length(best_path->path.parent->relids) == 1);
+ /* it should be a base rel... */
+ Assert(scan_relid > 0);
Assert(best_path->path.parent->rtekind == RTE_RELATION);
- scan_relid = (Index) lfirsti(best_path->path.parent->relids);
-
scan_plan = make_tidscan(tlist,
scan_clauses,
scan_relid,
create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses)
{
SubqueryScan *scan_plan;
- Index scan_relid;
+ Index scan_relid = best_path->parent->relid;
- /* there should be exactly one base rel involved... */
- Assert(length(best_path->parent->relids) == 1);
- /* and it must be a subquery */
+ /* it should be a subquery base rel... */
+ Assert(scan_relid > 0);
Assert(best_path->parent->rtekind == RTE_SUBQUERY);
- scan_relid = (Index) lfirsti(best_path->parent->relids);
-
scan_plan = make_subqueryscan(tlist,
scan_clauses,
scan_relid,
create_functionscan_plan(Path *best_path, List *tlist, List *scan_clauses)
{
FunctionScan *scan_plan;
- Index scan_relid;
+ Index scan_relid = best_path->parent->relid;
- /* there should be exactly one base rel involved... */
- Assert(length(best_path->parent->relids) == 1);
- /* and it must be a function */
+ /* it should be a function base rel... */
+ Assert(scan_relid > 0);
Assert(best_path->parent->rtekind == RTE_FUNCTION);
- scan_relid = (Index) lfirsti(best_path->parent->relids);
-
scan_plan = make_functionscan(tlist, scan_clauses, scan_relid);
copy_path_costsize(&scan_plan->scan.plan, best_path);
{
List *fixed_quals = NIL;
List *recheck_quals = NIL;
- int baserelid = lfirsti(index_path->path.parent->relids);
+ Relids baserelids = index_path->path.parent->relids;
+ int baserelid = index_path->path.parent->relid;
List *ixinfo = index_path->indexinfo;
List *i;
List *fixed_qual;
List *recheck_qual;
- fix_indxqual_sublist(indexqual, baserelid, index,
+ fix_indxqual_sublist(indexqual, baserelids, baserelid, index,
&fixed_qual, &recheck_qual);
fixed_quals = lappend(fixed_quals, fixed_qual);
if (recheck_qual != NIL)
* the index is lossy for this operator type.
*/
static void
-fix_indxqual_sublist(List *indexqual, int baserelid, IndexOptInfo *index,
+fix_indxqual_sublist(List *indexqual,
+ Relids baserelids, int baserelid,
+ IndexOptInfo *index,
List **fixed_quals, List **recheck_quals)
{
List *fixed_qual = NIL;
{
OpExpr *clause = (OpExpr *) lfirst(i);
OpExpr *newclause;
- List *leftvarnos;
+ Relids leftvarnos;
Oid opclass;
if (!IsA(clause, OpExpr) || length(clause->args) != 2)
* (only) the base relation.
*/
leftvarnos = pull_varnos((Node *) lfirst(newclause->args));
- if (length(leftvarnos) != 1 || lfirsti(leftvarnos) != baserelid)
+ if (!bms_equal(leftvarnos, baserelids))
CommuteClause(newclause);
- freeList(leftvarnos);
+ bms_free(leftvarnos);
/*
* Now, determine which index attribute this is, change the
* a modified list is returned.
*/
static List *
-get_switched_clauses(List *clauses, List *outerrelids)
+get_switched_clauses(List *clauses, Relids outerrelids)
{
List *t_list = NIL;
List *i;
OpExpr *clause = (OpExpr *) restrictinfo->clause;
Assert(is_opclause(clause));
- if (is_subseti(restrictinfo->right_relids, outerrelids))
+ if (bms_is_subset(restrictinfo->right_relids, outerrelids))
{
/*
* Duplicate just enough of the structure to allow commuting
*/
static Sort *
make_sort_from_pathkeys(Query *root, Plan *lefttree,
- List *relids, List *pathkeys)
+ Relids relids, List *pathkeys)
{
List *tlist = lefttree->targetlist;
List *sort_tlist;
foreach(j, keysublist)
{
pathkey = lfirst(j);
- if (is_subseti(pull_varnos(pathkey->key), relids))
+ if (bms_is_subset(pull_varnos(pathkey->key), relids))
break;
}
if (!j)
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.83 2003/01/24 03:58:43 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.84 2003/02/08 20:20:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* of an outer join since the qual might eliminate matching rows and cause a
* NULL row to be incorrectly emitted by the join. Therefore, rels appearing
* within the nullable side(s) of an outer join are marked with
- * outerjoinset = list of Relids used at the outer join node.
- * This list will be added to the list of rels referenced by quals using such
+ * outerjoinset = set of Relids used at the outer join node.
+ * This set will be added to the set of rels referenced by quals using such
* a rel, thereby forcing them up the join tree to the right level.
*
* To ease the calculation of these values, distribute_quals_to_rels() returns
- * the list of base Relids involved in its own level of join. This is just an
+ * the set of base Relids involved in its own level of join. This is just an
* internal convenience; no outside callers pay attention to the result.
*/
Relids
distribute_quals_to_rels(Query *root, Node *jtnode)
{
- Relids result = NIL;
+ Relids result = NULL;
if (jtnode == NULL)
return result;
int varno = ((RangeTblRef *) jtnode)->rtindex;
/* No quals to deal with, just return correct result */
- result = makeListi1(varno);
+ result = bms_make_singleton(varno);
}
else if (IsA(jtnode, FromExpr))
{
/*
* First, recurse to handle child joins.
- *
- * Note: we assume it's impossible to see same RT index from more
- * than one subtree, so nconc() is OK rather than set_unioni().
*/
foreach(l, f->fromlist)
{
- result = nconc(result,
- distribute_quals_to_rels(root, lfirst(l)));
+ result = bms_add_members(result,
+ distribute_quals_to_rels(root,
+ lfirst(l)));
}
/*
* recurse to handle sub-JOINs. Their join quals will be placed
* without regard for whether this level is an outer join, which
* is correct. Then, if we are an outer join, we mark baserels
- * contained within the nullable side(s) with our own rel list;
+ * contained within the nullable side(s) with our own rel set;
* this will restrict placement of subsequent quals using those
* rels, including our own quals and quals above us in the join
* tree. Finally we place our own join quals.
leftids = distribute_quals_to_rels(root, j->larg);
rightids = distribute_quals_to_rels(root, j->rarg);
- result = nconc(listCopy(leftids), rightids);
+ result = bms_union(leftids, rightids);
isouterjoin = false;
switch (j->jointype)
static void
mark_baserels_for_outer_join(Query *root, Relids rels, Relids outerrels)
{
- List *relid;
+ Relids tmprelids;
+ int relno;
- foreach(relid, rels)
+ tmprelids = bms_copy(rels);
+ while ((relno = bms_first_member(tmprelids)) >= 0)
{
- int relno = lfirsti(relid);
RelOptInfo *rel = find_base_rel(root, relno);
/*
* Since we do this bottom-up, any outer-rels previously marked
* should be within the new outer join set.
*/
- Assert(is_subseti(rel->outerjoinset, outerrels));
+ Assert(bms_is_subset(rel->outerjoinset, outerrels));
/*
* Presently the executor cannot support FOR UPDATE marking of
* It's sufficient to make this check once per rel, so do it only
* if rel wasn't already known nullable.
*/
- if (rel->outerjoinset == NIL)
+ if (rel->outerjoinset == NULL)
{
if (intMember(relno, root->rowMarks))
elog(ERROR, "SELECT FOR UPDATE cannot be applied to the nullable side of an OUTER JOIN");
rel->outerjoinset = outerrels;
}
+ bms_free(tmprelids);
}
/*
* (this indicates the clause came from a FromExpr, not a JoinExpr)
* 'isouterjoin': TRUE if the qual came from an OUTER JOIN's ON-clause
* 'isdeduced': TRUE if the qual came from implied-equality deduction
- * 'qualscope': list of baserels the qual's syntactic scope covers
+ * 'qualscope': set of baserels the qual's syntactic scope covers
*
* 'qualscope' identifies what level of JOIN the qual came from. For a top
* level qual (WHERE qual), qualscope lists all baserel ids and in addition
Relids qualscope)
{
RestrictInfo *restrictinfo = makeNode(RestrictInfo);
+ RelOptInfo *rel;
Relids relids;
List *vars;
bool can_be_equijoin;
restrictinfo->subclauseindices = NIL;
restrictinfo->eval_cost.startup = -1; /* not computed until needed */
restrictinfo->this_selec = -1; /* not computed until needed */
- restrictinfo->left_relids = NIL; /* set below, if join clause */
- restrictinfo->right_relids = NIL;
+ restrictinfo->left_relids = NULL; /* set below, if join clause */
+ restrictinfo->right_relids = NULL;
restrictinfo->mergejoinoperator = InvalidOid;
restrictinfo->left_sortop = InvalidOid;
restrictinfo->right_sortop = InvalidOid;
* Cross-check: clause should contain no relids not within its scope.
* Otherwise the parser messed up.
*/
- if (!is_subseti(relids, qualscope))
+ if (!bms_is_subset(relids, qualscope))
elog(ERROR, "JOIN qualification may not refer to other relations");
/*
* it will happen for variable-free JOIN/ON clauses. We don't have to
* be real smart about such a case, we just have to be correct.
*/
- if (relids == NIL)
+ if (bms_is_empty(relids))
relids = qualscope;
/*
* have all the rels it mentions, and (2) we are at or above any outer
* joins that can null any of these rels and are below the syntactic
* location of the given qual. To enforce the latter, scan the base
- * rels listed in relids, and merge their outer-join lists into the
+ * rels listed in relids, and merge their outer-join sets into the
* clause's own reference list. At the time we are called, the
- * outerjoinset list of each baserel will show exactly those outer
+ * outerjoinset of each baserel will show exactly those outer
* joins that are below the qual in the join tree.
*
* If the qual came from implied-equality deduction, we can evaluate the
*/
if (isdeduced)
{
- Assert(sameseti(relids, qualscope));
+ Assert(bms_equal(relids, qualscope));
can_be_equijoin = true;
}
else if (isouterjoin)
}
else
{
- Relids newrelids = relids;
- List *relid;
+ /* copy to ensure we don't change caller's qualscope set */
+ Relids newrelids = bms_copy(relids);
+ Relids tmprelids;
+ int relno;
- /*
- * We rely on set_unioni to be nondestructive of its input
- * lists...
- */
can_be_equijoin = true;
- foreach(relid, relids)
+ tmprelids = bms_copy(relids);
+ while ((relno = bms_first_member(tmprelids)) >= 0)
{
- RelOptInfo *rel = find_base_rel(root, lfirsti(relid));
+ RelOptInfo *rel = find_base_rel(root, relno);
- if (rel->outerjoinset &&
- !is_subseti(rel->outerjoinset, relids))
+ if (!bms_is_subset(rel->outerjoinset, relids))
{
- newrelids = set_unioni(newrelids, rel->outerjoinset);
+ newrelids = bms_add_members(newrelids, rel->outerjoinset);
/*
* Because application of the qual will be delayed by
can_be_equijoin = false;
}
}
+ bms_free(tmprelids);
relids = newrelids;
/* Should still be a subset of current scope ... */
- Assert(is_subseti(relids, qualscope));
+ Assert(bms_is_subset(relids, qualscope));
}
/*
* same joinrel. A qual originating from WHERE is always considered
* "pushed down".
*/
- restrictinfo->ispusheddown = ispusheddown || !sameseti(relids,
- qualscope);
+ restrictinfo->ispusheddown = ispusheddown || !bms_equal(relids,
+ qualscope);
- if (length(relids) == 1)
+ switch (bms_membership(relids))
{
- /*
- * There is only one relation participating in 'clause', so
- * 'clause' is a restriction clause for that relation.
- */
- RelOptInfo *rel = find_base_rel(root, lfirsti(relids));
-
- /*
- * Check for a "mergejoinable" clause even though it's not a join
- * clause. This is so that we can recognize that "a.x = a.y"
- * makes x and y eligible to be considered equal, even when they
- * belong to the same rel. Without this, we would not recognize
- * that "a.x = a.y AND a.x = b.z AND a.y = c.q" allows us to
- * consider z and q equal after their rels are joined.
- */
- if (can_be_equijoin)
- check_mergejoinable(restrictinfo);
+ case BMS_SINGLETON:
+ /*
+ * There is only one relation participating in 'clause', so
+ * 'clause' is a restriction clause for that relation.
+ */
+ rel = find_base_rel(root, bms_singleton_member(relids));
- /*
- * If the clause was deduced from implied equality, check to see
- * whether it is redundant with restriction clauses we already
- * have for this rel. Note we cannot apply this check to
- * user-written clauses, since we haven't found the canonical
- * pathkey sets yet while processing user clauses. (NB: no
- * comparable check is done in the join-clause case; redundancy
- * will be detected when the join clause is moved into a join
- * rel's restriction list.)
- */
- if (!isdeduced ||
- !qual_is_redundant(root, restrictinfo, rel->baserestrictinfo))
- {
- /* Add clause to rel's restriction list */
- rel->baserestrictinfo = lappend(rel->baserestrictinfo,
- restrictinfo);
- }
- }
- else if (relids != NIL)
- {
- /*
- * 'clause' is a join clause, since there is more than one rel in
- * the relid list. Set additional RestrictInfo fields for
- * joining. First, does it look like a normal join clause, i.e.,
- * a binary operator relating expressions that come from distinct
- * relations? If so we might be able to use it in a join algorithm.
- */
- if (is_opclause(clause) && length(((OpExpr *) clause)->args) == 2)
- {
- List *left_relids;
- List *right_relids;
+ /*
+ * Check for a "mergejoinable" clause even though it's not a join
+ * clause. This is so that we can recognize that "a.x = a.y"
+ * makes x and y eligible to be considered equal, even when they
+ * belong to the same rel. Without this, we would not recognize
+ * that "a.x = a.y AND a.x = b.z AND a.y = c.q" allows us to
+ * consider z and q equal after their rels are joined.
+ */
+ if (can_be_equijoin)
+ check_mergejoinable(restrictinfo);
- left_relids = pull_varnos(get_leftop((Expr *) clause));
- right_relids = pull_varnos(get_rightop((Expr *) clause));
- if (left_relids && right_relids &&
- nonoverlap_setsi(left_relids, right_relids))
+ /*
+ * If the clause was deduced from implied equality, check to see
+ * whether it is redundant with restriction clauses we already
+ * have for this rel. Note we cannot apply this check to
+ * user-written clauses, since we haven't found the canonical
+ * pathkey sets yet while processing user clauses. (NB: no
+ * comparable check is done in the join-clause case; redundancy
+ * will be detected when the join clause is moved into a join
+ * rel's restriction list.)
+ */
+ if (!isdeduced ||
+ !qual_is_redundant(root, restrictinfo, rel->baserestrictinfo))
{
- restrictinfo->left_relids = left_relids;
- restrictinfo->right_relids = right_relids;
+ /* Add clause to rel's restriction list */
+ rel->baserestrictinfo = lappend(rel->baserestrictinfo,
+ restrictinfo);
+ }
+ break;
+ case BMS_MULTIPLE:
+ /*
+ * 'clause' is a join clause, since there is more than one rel in
+ * the relid set. Set additional RestrictInfo fields for
+ * joining. First, does it look like a normal join clause, i.e.,
+ * a binary operator relating expressions that come from distinct
+ * relations? If so we might be able to use it in a join
+ * algorithm.
+ */
+ if (is_opclause(clause) && length(((OpExpr *) clause)->args) == 2)
+ {
+ Relids left_relids;
+ Relids right_relids;
+
+ left_relids = pull_varnos(get_leftop((Expr *) clause));
+ right_relids = pull_varnos(get_rightop((Expr *) clause));
+ if (!bms_is_empty(left_relids) &&
+ !bms_is_empty(right_relids) &&
+ !bms_overlap(left_relids, right_relids))
+ {
+ restrictinfo->left_relids = left_relids;
+ restrictinfo->right_relids = right_relids;
+ }
}
- }
- /*
- * Now check for hash or mergejoinable operators.
- *
- * We don't bother setting the hashjoin info if we're not going
- * to need it. We do want to know about mergejoinable ops in all
- * cases, however, because we use mergejoinable ops for other
- * purposes such as detecting redundant clauses.
- */
- check_mergejoinable(restrictinfo);
- if (enable_hashjoin)
- check_hashjoinable(restrictinfo);
+ /*
+ * Now check for hash or mergejoinable operators.
+ *
+ * We don't bother setting the hashjoin info if we're not going
+ * to need it. We do want to know about mergejoinable ops in all
+ * cases, however, because we use mergejoinable ops for other
+ * purposes such as detecting redundant clauses.
+ */
+ check_mergejoinable(restrictinfo);
+ if (enable_hashjoin)
+ check_hashjoinable(restrictinfo);
- /*
- * Add clause to the join lists of all the relevant relations.
- */
- add_join_clause_to_rels(root, restrictinfo, relids);
+ /*
+ * Add clause to the join lists of all the relevant relations.
+ */
+ add_join_clause_to_rels(root, restrictinfo, relids);
- /*
- * Add vars used in the join clause to targetlists of their
- * relations, so that they will be emitted by the plan nodes that
- * scan those relations (else they won't be available at the join
- * node!).
- */
- add_vars_to_targetlist(root, vars);
- }
- else
- {
- /*
- * 'clause' references no rels, and therefore we have no place to
- * attach it. Shouldn't get here if callers are working properly.
- */
- elog(ERROR, "distribute_qual_to_rels: can't cope with variable-free clause");
+ /*
+ * Add vars used in the join clause to targetlists of their
+ * relations, so that they will be emitted by the plan nodes that
+ * scan those relations (else they won't be available at the join
+ * node!).
+ */
+ add_vars_to_targetlist(root, vars);
+ break;
+ default:
+ /*
+ * 'clause' references no rels, and therefore we have no place to
+ * attach it. Shouldn't get here if callers are working properly.
+ */
+ elog(ERROR, "distribute_qual_to_rels: can't cope with variable-free clause");
+ break;
}
/*
bool delete_it)
{
Relids relids;
+ BMS_Membership membership;
RelOptInfo *rel1;
List *restrictlist;
List *itm;
Form_pg_operator pgopform;
Expr *clause;
- /* Get list of relids referenced in the two expressions */
- relids = set_unioni(item1_relids, item2_relids);
+ /* Get set of relids referenced in the two expressions */
+ relids = bms_union(item1_relids, item2_relids);
+ membership = bms_membership(relids);
/*
* generate_implied_equalities() shouldn't call me on two constants.
*/
- Assert(relids != NIL);
+ Assert(membership != BMS_EMPTY_SET);
/*
* If the exprs involve a single rel, we need to look at that rel's
* baserestrictinfo list. If multiple rels, any one will have a
* joininfo node for the rest, and we can scan any of 'em.
*/
- rel1 = find_base_rel(root, lfirsti(relids));
- if (lnext(relids) == NIL)
+ if (membership == BMS_SINGLETON)
+ {
+ rel1 = find_base_rel(root, bms_singleton_member(relids));
restrictlist = rel1->baserestrictinfo;
+ }
else
{
- JoinInfo *joininfo = find_joininfo_node(rel1, lnext(relids));
+ Relids other_rels;
+ int first_rel;
+ JoinInfo *joininfo;
+
+ /* Copy relids, find and remove one member */
+ other_rels = bms_copy(relids);
+ first_rel = bms_first_member(other_rels);
+
+ rel1 = find_base_rel(root, first_rel);
+
+ /* use remaining members to find join node */
+ joininfo = find_joininfo_node(rel1, other_rels);
restrictlist = joininfo ? joininfo->jinfo_restrictinfo : NIL;
+
+ bms_free(other_rels);
}
/*
/* found a matching clause */
if (delete_it)
{
- if (lnext(relids) == NIL)
+ if (membership == BMS_SINGLETON)
{
/* delete it from local restrictinfo list */
rel1->baserestrictinfo = lremove(restrictinfo,
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.69 2003/01/28 22:13:35 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.70 2003/02/08 20:20:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
convert_IN_to_join(Query *parse, SubLink *sublink)
{
Query *subselect = (Query *) sublink->subselect;
- List *left_varnos;
+ Relids left_varnos;
int rtindex;
RangeTblEntry *rte;
RangeTblRef *rtr;
* query, else it's not gonna be a join.
*/
left_varnos = pull_varnos((Node *) sublink->lefthand);
- if (left_varnos == NIL)
+ if (bms_is_empty(left_varnos))
return NULL;
/*
* The left-hand expressions mustn't be volatile. (Perhaps we should
*/
ininfo = makeNode(InClauseInfo);
ininfo->lefthand = left_varnos;
- ininfo->righthand = makeListi1(rtindex);
+ ininfo->righthand = bms_make_singleton(rtindex);
parse->in_info_list = lcons(ininfo, parse->in_info_list);
/*
* Build the result qual expressions. As a side effect,
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.2 2003/01/25 23:10:27 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.3 2003/02/08 20:20:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
parse->rowMarks = nconc(parse->rowMarks, subquery->rowMarks);
/*
- * We also have to fix the relid lists of any parent InClauseInfo
+ * We also have to fix the relid sets of any parent InClauseInfo
* nodes. (This could perhaps be done by ResolveNew, but it
* would clutter that routine's API unreasonably.)
*/
}
/*
- * fix_in_clause_relids: update RT-index lists of InClauseInfo nodes
+ * fix_in_clause_relids: update RT-index sets of InClauseInfo nodes
*
* When we pull up a subquery, any InClauseInfo references to the subquery's
- * RT index have to be replaced by the list of substituted relids.
+ * RT index have to be replaced by the set of substituted relids.
*
* We assume we may modify the InClauseInfo nodes in-place.
*/
{
InClauseInfo *ininfo = (InClauseInfo *) lfirst(l);
- if (intMember(varno, ininfo->lefthand))
+ if (bms_is_member(varno, ininfo->lefthand))
{
- ininfo->lefthand = lremovei(varno, ininfo->lefthand);
- ininfo->lefthand = nconc(ininfo->lefthand, listCopy(subrelids));
+ ininfo->lefthand = bms_del_member(ininfo->lefthand, varno);
+ ininfo->lefthand = bms_add_members(ininfo->lefthand, subrelids);
}
- if (intMember(varno, ininfo->righthand))
+ if (bms_is_member(varno, ininfo->righthand))
{
- ininfo->righthand = lremovei(varno, ininfo->righthand);
- ininfo->righthand = nconc(ininfo->righthand, listCopy(subrelids));
+ ininfo->righthand = bms_del_member(ininfo->righthand, varno);
+ ininfo->righthand = bms_add_members(ininfo->righthand, subrelids);
}
}
}
/*
- * get_relids_in_jointree: get list of base RT indexes present in a jointree
+ * get_relids_in_jointree: get set of base RT indexes present in a jointree
*/
-List *
+Relids
get_relids_in_jointree(Node *jtnode)
{
- Relids result = NIL;
+ Relids result = NULL;
if (jtnode == NULL)
return result;
{
int varno = ((RangeTblRef *) jtnode)->rtindex;
- result = makeListi1(varno);
+ result = bms_make_singleton(varno);
}
else if (IsA(jtnode, FromExpr))
{
FromExpr *f = (FromExpr *) jtnode;
List *l;
- /*
- * Note: we assume it's impossible to see same RT index from more
- * than one subtree, so nconc() is OK rather than set_unioni().
- */
foreach(l, f->fromlist)
{
- result = nconc(result,
- get_relids_in_jointree(lfirst(l)));
+ result = bms_join(result,
+ get_relids_in_jointree(lfirst(l)));
}
}
else if (IsA(jtnode, JoinExpr))
/* join's own RT index is not wanted in result */
result = get_relids_in_jointree(j->larg);
- result = nconc(result, get_relids_in_jointree(j->rarg));
+ result = bms_join(result, get_relids_in_jointree(j->rarg));
}
else
elog(ERROR, "get_relids_in_jointree: unexpected node type %d",
}
/*
- * get_relids_for_join: get list of base RT indexes making up a join
+ * get_relids_for_join: get set of base RT indexes making up a join
*
* NB: this will not work reliably after preprocess_jointree() is run,
* since that may eliminate join nodes from the jointree.
*/
-List *
+Relids
get_relids_for_join(Query *parse, int joinrelid)
{
Node *jtnode;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.88 2003/01/20 18:54:54 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.89 2003/02/08 20:20:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
List *refnames_tlist);
static Node *adjust_inherited_attrs_mutator(Node *node,
adjust_inherited_attrs_context *context);
-static List *adjust_rtindex_list(List *relids, Index oldrelid, Index newrelid);
+static Relids adjust_relid_set(Relids relids, Index oldrelid, Index newrelid);
static List *adjust_inherited_tlist(List *tlist, Oid new_relid);
/*
* find_all_inheritors -
- * Returns an integer list of relids including the given rel plus
+ * Returns an integer list of relation OIDs including the given rel plus
* all relations that inherit from it, directly or indirectly.
*/
List *
* its inh flag cleared, whether or not there were any children. This
* ensures we won't expand the same RTE twice, which would otherwise occur
* for the case of an inherited UPDATE/DELETE target relation.
+ *
+ * XXX probably should convert the result type to Relids?
*/
List *
expand_inherted_rtentry(Query *parse, Index rti, bool dup_parent)
ininfo = (InClauseInfo *) expression_tree_mutator(node,
adjust_inherited_attrs_mutator,
(void *) context);
- /* now fix InClauseInfo's rtindex lists */
- ininfo->lefthand = adjust_rtindex_list(ininfo->lefthand,
- context->old_rt_index,
- context->new_rt_index);
- ininfo->righthand = adjust_rtindex_list(ininfo->righthand,
- context->old_rt_index,
- context->new_rt_index);
+ /* now fix InClauseInfo's relid sets */
+ ininfo->lefthand = adjust_relid_set(ininfo->lefthand,
+ context->old_rt_index,
+ context->new_rt_index);
+ ininfo->righthand = adjust_relid_set(ininfo->righthand,
+ context->old_rt_index,
+ context->new_rt_index);
return (Node *) ininfo;
}
newinfo->subclauseindices = NIL;
/*
- * Adjust left/right relids lists too.
+ * Adjust left/right relid sets too.
*/
- newinfo->left_relids = adjust_rtindex_list(oldinfo->left_relids,
- context->old_rt_index,
- context->new_rt_index);
- newinfo->right_relids = adjust_rtindex_list(oldinfo->right_relids,
- context->old_rt_index,
- context->new_rt_index);
+ newinfo->left_relids = adjust_relid_set(oldinfo->left_relids,
+ context->old_rt_index,
+ context->new_rt_index);
+ newinfo->right_relids = adjust_relid_set(oldinfo->right_relids,
+ context->old_rt_index,
+ context->new_rt_index);
newinfo->eval_cost.startup = -1; /* reset these too */
newinfo->this_selec = -1;
}
/*
- * Substitute newrelid for oldrelid in a list of RT indexes
+ * Substitute newrelid for oldrelid in a Relid set
*/
-static List *
-adjust_rtindex_list(List *relids, Index oldrelid, Index newrelid)
+static Relids
+adjust_relid_set(Relids relids, Index oldrelid, Index newrelid)
{
- if (intMember(oldrelid, relids))
+ if (bms_is_member(oldrelid, relids))
{
/* Ensure we have a modifiable copy */
- relids = listCopy(relids);
+ relids = bms_copy(relids);
/* Remove old, add new */
- relids = lremovei(oldrelid, relids);
- relids = lconsi(newrelid, relids);
+ relids = bms_del_member(relids, oldrelid);
+ relids = bms_add_member(relids, newrelid);
}
return relids;
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.127 2003/02/04 00:50:00 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.128 2003/02/08 20:20:55 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
* clause_get_relids_vars
* Retrieves distinct relids and vars appearing within a clause.
*
- * '*relids' is set to an integer list of all distinct "varno"s appearing
+ * '*relids' is set to the set of all distinct "varno"s appearing
* in Vars within the clause.
* '*vars' is set to a list of all distinct Vars appearing within the clause.
* Var nodes are considered distinct if they have different varno
clause_get_relids_vars(Node *clause, Relids *relids, List **vars)
{
List *clvars = pull_var_clause(clause, false);
- List *varno_list = NIL;
+ Relids varnos = NULL;
List *var_list = NIL;
List *i;
Var *var = (Var *) lfirst(i);
List *vi;
- if (!intMember(var->varno, varno_list))
- varno_list = lconsi(var->varno, varno_list);
+ varnos = bms_add_member(varnos, var->varno);
foreach(vi, var_list)
{
Var *in_list = (Var *) lfirst(vi);
}
freeList(clvars);
- *relids = varno_list;
+ *relids = varnos;
*vars = var_list;
}
int
NumRelids(Node *clause)
{
- List *varno_list = pull_varnos(clause);
- int result = length(varno_list);
+ Relids varnos = pull_varnos(clause);
+ int result = bms_num_members(varnos);
- freeList(varno_list);
+ bms_free(varnos);
return result;
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/joininfo.c,v 1.33 2003/01/24 03:58:43 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/joininfo.c,v 1.34 2003/02/08 20:20:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
JoinInfo *joininfo = (JoinInfo *) lfirst(i);
- if (sameseti(join_relids, joininfo->unjoined_relids))
+ if (bms_equal(join_relids, joininfo->unjoined_relids))
return joininfo;
}
return NULL;
RestrictInfo *restrictinfo,
Relids join_relids)
{
- List *join_relid;
+ Relids tmprelids;
+ int cur_relid;
/* For every relid, find the joininfo, and add the proper join entries */
- foreach(join_relid, join_relids)
+ tmprelids = bms_copy(join_relids);
+ while ((cur_relid = bms_first_member(tmprelids)) >= 0)
{
- int cur_relid = lfirsti(join_relid);
- Relids unjoined_relids = NIL;
+ Relids unjoined_relids;
JoinInfo *joininfo;
- List *otherrel;
/* Get the relids not equal to the current relid */
- foreach(otherrel, join_relids)
- {
- if (lfirsti(otherrel) != cur_relid)
- unjoined_relids = lappendi(unjoined_relids, lfirsti(otherrel));
- }
- Assert(unjoined_relids != NIL);
+ unjoined_relids = bms_copy(join_relids);
+ unjoined_relids = bms_del_member(unjoined_relids, cur_relid);
+ Assert(!bms_is_empty(unjoined_relids));
/*
* Find or make the joininfo node for this combination of rels,
joininfo->jinfo_restrictinfo = lappend(joininfo->jinfo_restrictinfo,
restrictinfo);
/*
- * Can't freeList(unjoined_relids) because new joininfo node may
- * link to it. We could avoid leaking memory by doing listCopy()
+ * Can't bms_free(unjoined_relids) because new joininfo node may
+ * link to it. We could avoid leaking memory by doing bms_copy()
* in make_joininfo_node, but for now speed seems better.
*/
}
+ bms_free(tmprelids);
}
/*
RestrictInfo *restrictinfo,
Relids join_relids)
{
- List *join_relid;
+ Relids tmprelids;
+ int cur_relid;
/* For every relid, find the joininfo */
- foreach(join_relid, join_relids)
+ tmprelids = bms_copy(join_relids);
+ while ((cur_relid = bms_first_member(tmprelids)) >= 0)
{
- int cur_relid = lfirsti(join_relid);
- Relids unjoined_relids = NIL;
+ Relids unjoined_relids;
JoinInfo *joininfo;
- List *otherrel;
/* Get the relids not equal to the current relid */
- foreach(otherrel, join_relids)
- {
- if (lfirsti(otherrel) != cur_relid)
- unjoined_relids = lappendi(unjoined_relids, lfirsti(otherrel));
- }
- Assert(unjoined_relids != NIL);
+ unjoined_relids = bms_copy(join_relids);
+ unjoined_relids = bms_del_member(unjoined_relids, cur_relid);
+ Assert(!bms_is_empty(unjoined_relids));
/*
* Find the joininfo node for this combination of rels; it should
Assert(ptrMember(restrictinfo, joininfo->jinfo_restrictinfo));
joininfo->jinfo_restrictinfo = lremove(restrictinfo,
joininfo->jinfo_restrictinfo);
- freeList(unjoined_relids);
+ bms_free(unjoined_relids);
}
+ bms_free(tmprelids);
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.86 2003/01/27 20:51:54 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.87 2003/02/08 20:20:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
InClauseInfo *ininfo = (InClauseInfo *) lfirst(l);
- if (sameseti(ininfo->righthand, rel->relids))
+ if (bms_equal(ininfo->righthand, rel->relids))
{
sub_targetlist = ininfo->sub_targetlist;
break;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.77 2003/02/03 15:07:07 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.78 2003/02/08 20:20:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
get_relation_info(Oid relationObjectId, RelOptInfo *rel)
{
Relation relation;
- Index varno = lfirsti(rel->relids);
+ Index varno = rel->relid;
bool hasindex;
List *varlist = NIL;
List *indexinfos = NIL;
}
/* initialize cached join info to empty */
- info->outer_relids = NIL;
+ info->outer_relids = NULL;
info->inner_paths = NIL;
index_close(indexRelation);
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.46 2003/02/03 15:07:07 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.47 2003/02/08 20:20:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
foreach(rels, root->base_rel_list)
{
rel = (RelOptInfo *) lfirst(rels);
-
- /* length(rel->relids) == 1 for all members of base_rel_list */
- if (lfirsti(rel->relids) == relid)
+ if (relid == rel->relid)
elog(ERROR, "build_base_rel: rel already exists");
}
foreach(rels, root->other_rel_list)
{
rel = (RelOptInfo *) lfirst(rels);
-
- if (lfirsti(rel->relids) == relid)
+ if (relid == rel->relid)
elog(ERROR, "build_base_rel: rel already exists as 'other' rel");
}
foreach(rels, root->other_rel_list)
{
rel = (RelOptInfo *) lfirst(rels);
-
- /* length(rel->relids) == 1 for all members of other_rel_list */
- if (lfirsti(rel->relids) == relid)
+ if (relid == rel->relid)
return rel;
}
foreach(rels, root->base_rel_list)
{
rel = (RelOptInfo *) lfirst(rels);
-
- if (lfirsti(rel->relids) == relid)
+ if (relid == rel->relid)
elog(ERROR, "build_other_rel: rel already exists as base rel");
}
RangeTblEntry *rte = rt_fetch(relid, root->rtable);
rel->reloptkind = RELOPT_BASEREL;
- rel->relids = makeListi1(relid);
+ rel->relids = bms_make_singleton(relid);
rel->rows = 0;
rel->width = 0;
rel->targetlist = NIL;
rel->cheapest_total_path = NULL;
rel->cheapest_unique_path = NULL;
rel->pruneable = true;
+ rel->relid = relid;
rel->rtekind = rte->rtekind;
rel->varlist = NIL;
rel->indexlist = NIL;
rel->baserestrictinfo = NIL;
rel->baserestrictcost.startup = 0;
rel->baserestrictcost.per_tuple = 0;
- rel->outerjoinset = NIL;
+ rel->outerjoinset = NULL;
rel->joininfo = NIL;
- rel->index_outer_relids = NIL;
+ rel->index_outer_relids = NULL;
rel->index_inner_paths = NIL;
/* Check type of rtable entry */
foreach(rels, root->base_rel_list)
{
rel = (RelOptInfo *) lfirst(rels);
-
- /* length(rel->relids) == 1 for all members of base_rel_list */
- if (lfirsti(rel->relids) == relid)
+ if (relid == rel->relid)
return rel;
}
foreach(rels, root->other_rel_list)
{
rel = (RelOptInfo *) lfirst(rels);
-
- if (lfirsti(rel->relids) == relid)
+ if (relid == rel->relid)
return rel;
}
/*
* find_join_rel
- * Returns relation entry corresponding to 'relids' (a list of RT indexes),
+ * Returns relation entry corresponding to 'relids' (a set of RT indexes),
* or NULL if none exists. This is for join relations.
*
* Note: there is probably no good reason for this to be called from
{
RelOptInfo *rel = (RelOptInfo *) lfirst(joinrels);
- if (sameseti(rel->relids, relids))
+ if (bms_equal(rel->relids, relids))
return rel;
}
* Returns relation entry corresponding to the union of two given rels,
* creating a new relation entry if none already exists.
*
- * 'joinrelids' is the Relids list that uniquely identifies the join
+ * 'joinrelids' is the Relids set that uniquely identifies the join
* 'outer_rel' and 'inner_rel' are relation nodes for the relations to be
* joined
* 'jointype': type of join (inner/outer)
*/
RelOptInfo *
build_join_rel(Query *root,
- List *joinrelids,
+ Relids joinrelids,
RelOptInfo *outer_rel,
RelOptInfo *inner_rel,
JoinType jointype,
*/
joinrel = makeNode(RelOptInfo);
joinrel->reloptkind = RELOPT_JOINREL;
- joinrel->relids = listCopy(joinrelids);
+ joinrel->relids = bms_copy(joinrelids);
joinrel->rows = 0;
joinrel->width = 0;
joinrel->targetlist = NIL;
joinrel->cheapest_total_path = NULL;
joinrel->cheapest_unique_path = NULL;
joinrel->pruneable = true;
+ joinrel->relid = 0; /* indicates not a baserel */
joinrel->rtekind = RTE_JOIN;
joinrel->varlist = NIL;
joinrel->indexlist = NIL;
joinrel->baserestrictinfo = NIL;
joinrel->baserestrictcost.startup = 0;
joinrel->baserestrictcost.per_tuple = 0;
- joinrel->outerjoinset = NIL;
+ joinrel->outerjoinset = NULL;
joinrel->joininfo = NIL;
- joinrel->index_outer_relids = NIL;
+ joinrel->index_outer_relids = NULL;
joinrel->index_inner_paths = NIL;
/*
{
JoinInfo *joininfo = (JoinInfo *) lfirst(xjoininfo);
- if (is_subseti(joininfo->unjoined_relids, joinrel->relids))
+ if (bms_is_subset(joininfo->unjoined_relids, joinrel->relids))
{
/*
* Clauses in this JoinInfo list become restriction clauses
JoinInfo *joininfo = (JoinInfo *) lfirst(xjoininfo);
Relids new_unjoined_relids;
- new_unjoined_relids = set_differencei(joininfo->unjoined_relids,
- joinrel->relids);
- if (new_unjoined_relids == NIL)
+ new_unjoined_relids = bms_difference(joininfo->unjoined_relids,
+ joinrel->relids);
+ if (bms_is_empty(new_unjoined_relids))
{
/*
* Clauses in this JoinInfo list become restriction clauses
* for the joinrel, since they refer to no outside rels. So we
* can ignore them in this routine.
*/
+ bms_free(new_unjoined_relids);
}
else
{
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.48 2003/02/06 22:21:11 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.49 2003/02/08 20:20:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
typedef struct
{
- List *varlist;
+ Relids varnos;
int sublevels_up;
} pull_varnos_context;
pull_var_clause_context *context);
static Node *flatten_join_alias_vars_mutator(Node *node,
flatten_join_alias_vars_context *context);
-static List *alias_rtindex_list(Query *root, List *rtlist);
+static Relids alias_relid_set(Query *root, Relids relids);
/*
- * pull_varnos
- *
- * Create a list of all the distinct varnos present in a parsetree.
+ * pull_varnos
+ * Create a set of all the distinct varnos present in a parsetree.
* Only varnos that reference level-zero rtable entries are considered.
*
* NOTE: this is used on not-yet-planned expressions. It may therefore find
* references to the desired rtable level! But when we find a completed
* SubPlan, we only need to look at the parameters passed to the subplan.
*/
-List *
+Relids
pull_varnos(Node *node)
{
pull_varnos_context context;
- context.varlist = NIL;
+ context.varnos = NULL;
context.sublevels_up = 0;
/*
(void *) &context,
0);
- return context.varlist;
+ return context.varnos;
}
static bool
{
Var *var = (Var *) node;
- if (var->varlevelsup == context->sublevels_up &&
- !intMember(var->varno, context->varlist))
- context->varlist = lconsi(var->varno, context->varlist);
+ if (var->varlevelsup == context->sublevels_up)
+ context->varnos = bms_add_member(context->varnos, var->varno);
return false;
}
if (IsA(node, Query))
ininfo = (InClauseInfo *) expression_tree_mutator(node,
flatten_join_alias_vars_mutator,
(void *) context);
- /* now fix InClauseInfo's rtindex lists */
+ /* now fix InClauseInfo's relid sets */
if (context->sublevels_up == 0)
{
- ininfo->lefthand = alias_rtindex_list(context->root,
- ininfo->lefthand);
- ininfo->righthand = alias_rtindex_list(context->root,
- ininfo->righthand);
+ ininfo->lefthand = alias_relid_set(context->root,
+ ininfo->lefthand);
+ ininfo->righthand = alias_relid_set(context->root,
+ ininfo->righthand);
}
return (Node *) ininfo;
}
}
/*
- * alias_rtindex_list: in a list of RT indexes, replace joins by their
+ * alias_relid_set: in a set of RT indexes, replace joins by their
* underlying base relids
*/
-static List *
-alias_rtindex_list(Query *root, List *rtlist)
+static Relids
+alias_relid_set(Query *root, Relids relids)
{
- List *result = NIL;
- List *l;
+ Relids result = NULL;
+ Relids tmprelids;
+ int rtindex;
- foreach(l, rtlist)
+ tmprelids = bms_copy(relids);
+ while ((rtindex = bms_first_member(tmprelids)) >= 0)
{
- int rtindex = lfirsti(l);
- RangeTblEntry *rte;
+ RangeTblEntry *rte = rt_fetch(rtindex, root->rtable);
- rte = rt_fetch(rtindex, root->rtable);
if (rte->rtekind == RTE_JOIN)
- result = nconc(result, get_relids_for_join(root, rtindex));
+ result = bms_join(result, get_relids_for_join(root, rtindex));
else
- result = lappendi(result, rtindex);
+ result = bms_add_member(result, rtindex);
}
+ bms_free(tmprelids);
return result;
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.103 2002/12/16 18:39:22 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.104 2003/02/08 20:20:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
Node *result;
List *save_namespace;
- List *clause_varnos,
- *l;
+ Relids clause_varnos;
+ int varno;
/*
* This is a tad tricky, for two reasons. First, the namespace that
* here.)
*/
clause_varnos = pull_varnos(result);
- foreach(l, clause_varnos)
+ while ((varno = bms_first_member(clause_varnos)) >= 0)
{
- int varno = lfirsti(l);
-
if (!intMember(varno, containedRels))
{
elog(ERROR, "JOIN/ON clause refers to \"%s\", which is not part of JOIN",
rt_fetch(varno, pstate->p_rtable)->eref->aliasname);
}
}
- freeList(clause_varnos);
+ bms_free(clause_varnos);
return result;
}
* no local Var references in the transformed expression. (Outer
* references are OK, and are ignored here.)
*/
- if (pull_varnos(funcexpr) != NIL)
+ if (!bms_is_empty(pull_varnos(funcexpr)))
elog(ERROR, "FROM function expression may not refer to other relations of same query level");
/*
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.70 2003/01/20 18:54:59 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.71 2003/02/08 20:20:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static bool checkExprHasAggs_walker(Node *node, void *context);
static bool checkExprHasSubLink_walker(Node *node, void *context);
+static Relids offset_relid_set(Relids relids, int offset);
+static Relids adjust_relid_set(Relids relids, int oldrelid, int newrelid);
/*
if (context->sublevels_up == 0)
{
- List *rt;
-
- foreach(rt, ininfo->lefthand)
- {
- lfirsti(rt) += context->offset;
- }
- foreach(rt, ininfo->righthand)
- {
- lfirsti(rt) += context->offset;
- }
+ ininfo->lefthand = offset_relid_set(ininfo->lefthand,
+ context->offset);
+ ininfo->righthand = offset_relid_set(ininfo->righthand,
+ context->offset);
}
/* fall through to examine children */
}
OffsetVarNodes_walker(node, &context);
}
+static Relids
+offset_relid_set(Relids relids, int offset)
+{
+ Relids result = NULL;
+ Relids tmprelids;
+ int rtindex;
+
+ tmprelids = bms_copy(relids);
+ while ((rtindex = bms_first_member(tmprelids)) >= 0)
+ {
+ result = bms_add_member(result, rtindex + offset);
+ }
+ bms_free(tmprelids);
+ return result;
+}
+
/*
* ChangeVarNodes - adjust Var nodes for a specific change of RT index
*
if (context->sublevels_up == 0)
{
- List *rt;
-
- foreach(rt, ininfo->lefthand)
- {
- if (lfirsti(rt) == context->rt_index)
- lfirsti(rt) = context->new_index;
- }
- foreach(rt, ininfo->righthand)
- {
- if (lfirsti(rt) == context->rt_index)
- lfirsti(rt) = context->new_index;
- }
+ ininfo->lefthand = adjust_relid_set(ininfo->lefthand,
+ context->rt_index,
+ context->new_index);
+ ininfo->righthand = adjust_relid_set(ininfo->righthand,
+ context->rt_index,
+ context->new_index);
}
/* fall through to examine children */
}
}
/*
+ * Substitute newrelid for oldrelid in a Relid set
+ */
+static Relids
+adjust_relid_set(Relids relids, int oldrelid, int newrelid)
+{
+ if (bms_is_member(oldrelid, relids))
+ {
+ /* Ensure we have a modifiable copy */
+ relids = bms_copy(relids);
+ /* Remove old, add new */
+ relids = bms_del_member(relids, oldrelid);
+ relids = bms_add_member(relids, newrelid);
+ }
+ return relids;
+}
+
+/*
* IncrementVarSublevelsUp - adjust Var nodes when pushing them down in tree
*
* Find all Var nodes in the given tree having varlevelsup >= min_sublevels_up,
InClauseInfo *ininfo = (InClauseInfo *) node;
if (context->sublevels_up == 0 &&
- (intMember(context->rt_index, ininfo->lefthand) ||
- intMember(context->rt_index, ininfo->righthand)))
+ (bms_is_member(context->rt_index, ininfo->lefthand) ||
+ bms_is_member(context->rt_index, ininfo->righthand)))
return true;
/* fall through to examine children */
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.131 2003/01/28 22:13:35 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.132 2003/02/08 20:20:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/* Estimate the fraction of main-table tuples that will be visited */
*indexSelectivity = clauselist_selectivity(root, selectivityQuals,
- lfirsti(rel->relids),
+ rel->relid,
JOIN_INNER);
/*
Oid relid;
HeapTuple tuple;
- relid = getrelid(lfirsti(rel->relids), root->rtable);
+ relid = getrelid(rel->relid, root->rtable);
Assert(relid != InvalidOid);
tuple = SearchSysCache(STATRELATT,
ObjectIdGetDatum(relid),
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * bitmapset.h
+ * PostgreSQL generic bitmap set package
+ *
+ * A bitmap set can represent any set of nonnegative integers, although
+ * it is mainly intended for sets where the maximum value is not large,
+ * say at most a few hundred. By convention, a NULL pointer is always
+ * accepted by all operations to represent the empty set. (But beware
+ * that this is not the only representation of the empty set. Use
+ * bms_is_empty() in preference to testing for NULL.)
+ *
+ *
+ * Copyright (c) 2003, PostgreSQL Global Development Group
+ *
+ * $Id: bitmapset.h,v 1.1 2003/02/08 20:20:55 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef BITMAPSET_H
+#define BITMAPSET_H
+
+/*
+ * Data representation
+ */
+
+/* The unit size can be adjusted by changing these three declarations: */
+#define BITS_PER_BITMAPWORD 32
+typedef uint32 bitmapword; /* must be an unsigned type */
+typedef int32 signedbitmapword; /* must be the matching signed type */
+
+typedef struct Bitmapset {
+ int nwords; /* number of words in array */
+ bitmapword words[1]; /* really [nwords] */
+} Bitmapset; /* VARIABLE LENGTH STRUCT */
+
+
+/* result of bms_membership */
+typedef enum
+{
+ BMS_EMPTY_SET, /* 0 members */
+ BMS_SINGLETON, /* 1 member */
+ BMS_MULTIPLE /* >1 member */
+} BMS_Membership;
+
+
+/*
+ * function prototypes in nodes/bitmapset.c
+ */
+
+extern Bitmapset *bms_copy(const Bitmapset *a);
+extern bool bms_equal(const Bitmapset *a, const Bitmapset *b);
+extern Bitmapset *bms_make_singleton(int x);
+extern void bms_free(Bitmapset *a);
+
+extern Bitmapset *bms_union(const Bitmapset *a, const Bitmapset *b);
+extern Bitmapset *bms_intersect(const Bitmapset *a, const Bitmapset *b);
+extern Bitmapset *bms_difference(const Bitmapset *a, const Bitmapset *b);
+extern bool bms_is_subset(const Bitmapset *a, const Bitmapset *b);
+extern bool bms_is_member(int x, const Bitmapset *a);
+extern bool bms_overlap(const Bitmapset *a, const Bitmapset *b);
+extern int bms_singleton_member(const Bitmapset *a);
+extern int bms_num_members(const Bitmapset *a);
+/* optimized tests when we don't need to know exact membership count: */
+extern BMS_Membership bms_membership(const Bitmapset *a);
+extern bool bms_is_empty(const Bitmapset *a);
+
+/* these routines recycle (modify or free) their non-const inputs: */
+
+extern Bitmapset *bms_add_member(Bitmapset *a, int x);
+extern Bitmapset *bms_del_member(Bitmapset *a, int x);
+extern Bitmapset *bms_add_members(Bitmapset *a, const Bitmapset *b);
+extern Bitmapset *bms_int_members(Bitmapset *a, const Bitmapset *b);
+extern Bitmapset *bms_del_members(Bitmapset *a, const Bitmapset *b);
+extern Bitmapset *bms_join(Bitmapset *a, Bitmapset *b);
+
+/* support for iterating through the integer elements of a set: */
+extern int bms_first_member(Bitmapset *a);
+
+#endif /* BITMAPSET_H */
* 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.33 2003/01/27 20:51:54 tgl Exp $
+ * $Id: pg_list.h,v 1.34 2003/02/08 20:20:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
extern int length(List *list);
extern void *llast(List *list);
-extern int llasti(List *list);
extern List *nconc(List *list1, List *list2);
extern List *lcons(void *datum, List *list);
extern List *lconsi(int datum, List *list);
extern int nthi(int n, List *l);
extern void set_nth(List *l, int n, void *elem);
-extern List *set_difference(List *list1, List *list2);
-extern List *set_differencei(List *list1, List *list2);
-extern List *set_ptrDifference(List *list1, List *list2);
-extern List *lreverse(List *l);
extern List *set_union(List *list1, List *list2);
extern List *set_unioni(List *list1, List *list2);
extern List *set_ptrUnion(List *list1, List *list2);
+extern List *set_intersect(List *l1, List *l2);
extern List *set_intersecti(List *list1, List *list2);
+extern List *set_difference(List *list1, List *list2);
+extern List *set_differencei(List *list1, List *list2);
+extern List *set_ptrDifference(List *list1, List *list2);
extern bool equali(List *list1, List *list2);
-extern bool sameseti(List *list1, List *list2);
-extern bool overlap_setsi(List *list1, List *list2);
-#define nonoverlap_setsi(list1, list2) (!overlap_setsi(list1, list2))
-extern bool is_subseti(List *list1, List *list2);
extern void freeList(List *list);
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: relation.h,v 1.78 2003/02/03 15:07:08 tgl Exp $
+ * $Id: relation.h,v 1.79 2003/02/08 20:20:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#define RELATION_H
#include "access/sdir.h"
+#include "nodes/bitmapset.h"
#include "nodes/parsenodes.h"
+
/*
* Relids
- * List of relation identifiers (indexes into the rangetable).
- *
- * Note: these are lists of integers, not Nodes.
+ * Set of relation identifiers (indexes into the rangetable).
*/
-typedef List *Relids;
+typedef Bitmapset *Relids;
/*
* When looking for a "cheapest path", this enum specifies whether we want
* Parts of this data structure are specific to various scan and join
* mechanisms. It didn't seem worth creating new node types for them.
*
- * relids - List of base-relation identifiers; it is a base relation
+ * relids - Set of base-relation identifiers; it is a base relation
* if there is just one, a join relation if more than one
* rows - estimated number of tuples in the relation after restriction
* clauses have been applied (ie, output rows of a plan for it)
*
* If the relation is a base relation it will have these fields set:
*
+ * relid - RTE index (this is redundant with the relids field, but
+ * is provided for convenience of access)
* rtekind - distinguishes plain relation, subquery, or function RTE
* varlist - list of Vars for physical columns (only if table)
* indexlist - list of IndexOptInfo nodes for relation's indexes
* baserestrictcost - Estimated cost of evaluating the baserestrictinfo
* clauses at a single tuple (only used for base rels)
* outerjoinset - For a base rel: if the rel appears within the nullable
- * side of an outer join, the list of all relids
- * participating in the highest such outer join; else NIL.
+ * side of an outer join, the set of all relids
+ * participating in the highest such outer join; else NULL.
* Otherwise, unused.
* joininfo - List of JoinInfo nodes, containing info about each join
* clause in which this relation participates
- * index_outer_relids - only used for base rels; list of outer relids
+ * index_outer_relids - only used for base rels; set of outer relids
* that participate in indexable joinclauses for this rel
* index_inner_paths - only used for base rels; list of InnerIndexscanInfo
* nodes showing best indexpaths for various subsets of
RelOptKind reloptkind;
/* all relations included in this RelOptInfo */
- Relids relids; /* integer list of base relids (rangetable
- * indexes) */
+ Relids relids; /* set of base relids (rangetable indexes) */
/* size estimates generated by planner */
double rows; /* estimated number of result tuples */
bool pruneable;
/* information about a base rel (not set for join rels!) */
+ Index relid;
RTEKind rtekind; /* RELATION, SUBQUERY, or FUNCTION */
List *varlist;
List *indexlist;
List *baserestrictinfo; /* RestrictInfo structures (if
* base rel) */
QualCost baserestrictcost; /* cost of evaluating the above */
- Relids outerjoinset; /* integer list of base relids */
+ Relids outerjoinset; /* set of base relids */
List *joininfo; /* JoinInfo structures */
/* cached info about inner indexscan paths for relation: */
/*
* If the clause looks useful for joining --- that is, it is a binary
* opclause with nonoverlapping sets of relids referenced in the left
- * and right sides --- then these two fields are set to lists of the
- * referenced relids. Otherwise they are both NIL.
+ * and right sides --- then these two fields are set to sets of the
+ * referenced relids. Otherwise they are both NULL.
*/
- List *left_relids; /* relids in left side of join clause */
- List *right_relids; /* relids in right side of join clause */
+ Relids left_relids; /* relids in left side of join clause */
+ Relids right_relids; /* relids in right side of join clause */
/* valid if clause is mergejoinable, else InvalidOid: */
Oid mergejoinoperator; /* copy of clause operator */
typedef struct InClauseInfo
{
NodeTag type;
- List *lefthand; /* base relids in lefthand expressions */
- List *righthand; /* base relids coming from the subselect */
+ Relids lefthand; /* base relids in lefthand expressions */
+ Relids righthand; /* base relids coming from the subselect */
List *sub_targetlist; /* targetlist of original RHS subquery */
/*
* Note: sub_targetlist is just a list of Vars or expressions;
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: pathnode.h,v 1.48 2003/01/20 18:55:05 tgl Exp $
+ * $Id: pathnode.h,v 1.49 2003/02/08 20:20:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "nodes/relation.h"
+
/*
* prototypes for pathnode.c
*/
extern RelOptInfo *build_other_rel(Query *root, int relid);
extern RelOptInfo *find_base_rel(Query *root, int relid);
extern RelOptInfo *build_join_rel(Query *root,
- List *joinrelids,
- RelOptInfo *outer_rel,
- RelOptInfo *inner_rel,
- JoinType jointype,
- List **restrictlist_ptr);
+ Relids joinrelids,
+ RelOptInfo *outer_rel,
+ RelOptInfo *inner_rel,
+ JoinType jointype,
+ List **restrictlist_ptr);
#endif /* PATHNODE_H */
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: prep.h,v 1.35 2003/01/25 23:10:30 tgl Exp $
+ * $Id: prep.h,v 1.36 2003/02/08 20:20:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "nodes/parsenodes.h"
#include "nodes/plannodes.h"
+#include "nodes/relation.h"
+
/*
* prototypes for prepjointree.c
extern Node *pull_up_subqueries(Query *parse, Node *jtnode,
bool below_outer_join);
extern Node *preprocess_jointree(Query *parse, Node *jtnode);
-extern List *get_relids_in_jointree(Node *jtnode);
-extern List *get_relids_for_join(Query *parse, int joinrelid);
+extern Relids get_relids_in_jointree(Node *jtnode);
+extern Relids get_relids_for_join(Query *parse, int joinrelid);
/*
* prototypes for prepqual.c
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: var.h,v 1.25 2003/01/20 18:55:06 tgl Exp $
+ * $Id: var.h,v 1.26 2003/02/08 20:20:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef VAR_H
#define VAR_H
-#include "nodes/parsenodes.h"
+#include "nodes/relation.h"
-extern List *pull_varnos(Node *node);
+extern Relids pull_varnos(Node *node);
extern bool contain_var_reference(Node *node, int varno, int varattno,
int levelsup);
extern bool contain_whole_tuple_var(Node *node, int varno, int levelsup);