summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/util/joininfo.c
blob: 350d2c5198de51d30861e1f1ae6c5c2f48f767ca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/*-------------------------------------------------------------------------
 *
 * joininfo.c
 *	  JoinInfo node manipulation routines
 *
 * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/joininfo.c,v 1.36 2003/08/04 02:40:01 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "optimizer/joininfo.h"
#include "optimizer/pathnode.h"


/*
 * find_joininfo_node
 *	  Find the joininfo node within a relation entry corresponding
 *	  to a join between 'this_rel' and the relations in 'join_relids'.
 *	  If there is no such node, return NULL.
 *
 * Returns a joininfo node, or NULL.
 */
JoinInfo *
find_joininfo_node(RelOptInfo *this_rel, Relids join_relids)
{
	List	   *i;

	foreach(i, this_rel->joininfo)
	{
		JoinInfo   *joininfo = (JoinInfo *) lfirst(i);

		if (bms_equal(join_relids, joininfo->unjoined_relids))
			return joininfo;
	}
	return NULL;
}

/*
 * make_joininfo_node
 *	  Find the joininfo node within a relation entry corresponding
 *	  to a join between 'this_rel' and the relations in 'join_relids'.
 *	  A new node is created and added to the relation entry's joininfo
 *	  field if the desired one can't be found.
 *
 * Returns a joininfo node.
 */
JoinInfo *
make_joininfo_node(RelOptInfo *this_rel, Relids join_relids)
{
	JoinInfo   *joininfo = find_joininfo_node(this_rel, join_relids);

	if (joininfo == NULL)
	{
		joininfo = makeNode(JoinInfo);
		joininfo->unjoined_relids = join_relids;
		joininfo->jinfo_restrictinfo = NIL;
		this_rel->joininfo = lcons(joininfo, this_rel->joininfo);
	}
	return joininfo;
}


/*
 * add_join_clause_to_rels
 *	  For every relation participating in a join clause, add 'restrictinfo' to
 *	  the appropriate joininfo list (creating a new list and adding it to the
 *	  appropriate rel node if necessary).
 *
 * Note that the same copy of the restrictinfo node is linked to by all the
 * lists it is in.	This allows us to exploit caching of information about
 * the restriction clause (but we must be careful that the information does
 * not depend on context).
 *
 * 'restrictinfo' describes the join clause
 * 'join_relids' is the list of relations participating in the join clause
 *				 (there must be more than one)
 */
void
add_join_clause_to_rels(Query *root,
						RestrictInfo *restrictinfo,
						Relids join_relids)
{
	Relids		tmprelids;
	int			cur_relid;

	/* For every relid, find the joininfo, and add the proper join entries */
	tmprelids = bms_copy(join_relids);
	while ((cur_relid = bms_first_member(tmprelids)) >= 0)
	{
		Relids		unjoined_relids;
		JoinInfo   *joininfo;

		/* Get the relids not equal to the current relid */
		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,
		 * and add the restrictinfo node to it.
		 */
		joininfo = make_joininfo_node(find_base_rel(root, cur_relid),
									  unjoined_relids);
		joininfo->jinfo_restrictinfo = lappend(joininfo->jinfo_restrictinfo,
											   restrictinfo);

		/*
		 * 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);
}

/*
 * remove_join_clause_from_rels
 *	  Delete 'restrictinfo' from all the joininfo lists it is in
 *
 * This reverses the effect of add_join_clause_to_rels.  It's used when we
 * discover that a join clause is redundant.
 *
 * 'restrictinfo' describes the join clause
 * 'join_relids' is the list of relations participating in the join clause
 *				 (there must be more than one)
 */
void
remove_join_clause_from_rels(Query *root,
							 RestrictInfo *restrictinfo,
							 Relids join_relids)
{
	Relids		tmprelids;
	int			cur_relid;

	/* For every relid, find the joininfo */
	tmprelids = bms_copy(join_relids);
	while ((cur_relid = bms_first_member(tmprelids)) >= 0)
	{
		Relids		unjoined_relids;
		JoinInfo   *joininfo;

		/* Get the relids not equal to the current relid */
		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
		 * exist already, if add_join_clause_to_rels was called.
		 */
		joininfo = find_joininfo_node(find_base_rel(root, cur_relid),
									  unjoined_relids);
		Assert(joininfo);

		/*
		 * Remove the restrictinfo from the list.  Pointer comparison is
		 * sufficient.
		 */
		Assert(ptrMember(restrictinfo, joininfo->jinfo_restrictinfo));
		joininfo->jinfo_restrictinfo = lremove(restrictinfo,
										   joininfo->jinfo_restrictinfo);
		bms_free(unjoined_relids);
	}
	bms_free(tmprelids);
}