7
7
*
8
8
*
9
9
* IDENTIFICATION
10
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.27 1998/04/15 15:29:41 momjian Exp $
10
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.28 1998/07/19 05:49:14 momjian Exp $
11
11
*
12
12
*-------------------------------------------------------------------------
13
13
*/
@@ -95,6 +95,11 @@ Plan *
95
95
union_planner (Query * parse )
96
96
{
97
97
List * tlist = parse -> targetList ;
98
+
99
+ /* copy the original tlist, we will need the original one
100
+ * for the AGG node later on */
101
+ List * new_tlist = new_unsorted_tlist (tlist );
102
+
98
103
List * rangetable = parse -> rtable ;
99
104
100
105
Plan * result_plan = (Plan * ) NULL ;
@@ -104,46 +109,77 @@ union_planner(Query *parse)
104
109
105
110
if (parse -> unionClause )
106
111
{
107
- result_plan = (Plan * ) plan_union_queries (parse );
108
- /* XXX do we need to do this? bjm 12/19/97 */
109
- tlist = preprocess_targetlist (tlist ,
110
- parse -> commandType ,
111
- parse -> resultRelation ,
112
- parse -> rtable );
112
+ result_plan = (Plan * ) plan_union_queries (parse );
113
+ /* XXX do we need to do this? bjm 12/19/97 */
114
+ tlist = preprocess_targetlist (tlist ,
115
+ parse -> commandType ,
116
+ parse -> resultRelation ,
117
+ parse -> rtable );
113
118
}
114
119
else if ((rt_index =
115
120
first_inherit_rt_entry (rangetable )) != -1 )
116
121
{
117
122
result_plan = (Plan * ) plan_inherit_queries (parse , rt_index );
118
123
/* XXX do we need to do this? bjm 12/19/97 */
119
124
tlist = preprocess_targetlist (tlist ,
120
- parse -> commandType ,
121
- parse -> resultRelation ,
122
- parse -> rtable );
125
+ parse -> commandType ,
126
+ parse -> resultRelation ,
127
+ parse -> rtable );
123
128
}
124
129
else
125
130
{
126
- List * * vpm = NULL ;
127
-
128
- tlist = preprocess_targetlist (tlist ,
129
- parse -> commandType ,
130
- parse -> resultRelation ,
131
- parse -> rtable );
132
- if (parse -> rtable != NULL )
131
+ List * * vpm = NULL ;
132
+
133
+ /* This is only necessary if aggregates are in use in queries like:
134
+ * SELECT sid
135
+ * FROM part
136
+ * GROUP BY sid
137
+ * HAVING MIN(pid) > 1; (pid is used but never selected for!!!)
138
+ * because the function 'query_planner' creates the plan for the lefttree
139
+ * of the 'GROUP' node and returns only those attributes contained in 'tlist'.
140
+ * The original 'tlist' contains only 'sid' here and that's why we have to
141
+ * to extend it to attributes which are not selected but are used in the
142
+ * havingQual. */
143
+
144
+ /* 'check_having_qual_for_vars' takes the havingQual and the actual 'tlist'
145
+ * as arguments and recursively scans the havingQual for attributes
146
+ * (VAR nodes) that are not contained in 'tlist' yet. If so, it creates
147
+ * a new entry and attaches it to the list 'new_tlist' (consisting of the
148
+ * VAR node and the RESDOM node as usual with tlists :-) ) */
149
+ if (parse -> hasAggs )
150
+ {
151
+ if (parse -> havingQual != NULL )
133
152
{
134
- vpm = (List * * ) palloc (length (parse -> rtable ) * sizeof (List * ));
135
- memset (vpm , 0 , length (parse -> rtable ) * sizeof (List * ));
153
+ new_tlist = check_having_qual_for_vars (parse -> havingQual ,new_tlist );
136
154
}
137
- PlannerVarParam = lcons (vpm , PlannerVarParam );
138
- result_plan = query_planner (parse ,
139
- parse -> commandType ,
140
- tlist ,
141
- (List * ) parse -> qual );
142
- PlannerVarParam = lnext (PlannerVarParam );
143
- if (vpm != NULL )
144
- pfree (vpm );
155
+ }
156
+
157
+ new_tlist = preprocess_targetlist (new_tlist ,
158
+ parse -> commandType ,
159
+ parse -> resultRelation ,
160
+ parse -> rtable );
161
+
162
+ /* Here starts the original (pre having) code */
163
+ tlist = preprocess_targetlist (tlist ,
164
+ parse -> commandType ,
165
+ parse -> resultRelation ,
166
+ parse -> rtable );
167
+
168
+ if (parse -> rtable != NULL )
169
+ {
170
+ vpm = (List * * ) palloc (length (parse -> rtable ) * sizeof (List * ));
171
+ memset (vpm , 0 , length (parse -> rtable ) * sizeof (List * ));
172
+ }
173
+ PlannerVarParam = lcons (vpm , PlannerVarParam );
174
+ result_plan = query_planner (parse ,
175
+ parse -> commandType ,
176
+ new_tlist ,
177
+ (List * ) parse -> qual );
178
+ PlannerVarParam = lnext (PlannerVarParam );
179
+ if (vpm != NULL )
180
+ pfree (vpm );
145
181
}
146
-
182
+
147
183
/*
148
184
* If we have a GROUP BY clause, insert a group node (with the
149
185
* appropriate sort node.)
@@ -160,43 +196,96 @@ union_planner(Query *parse)
160
196
*/
161
197
tuplePerGroup = parse -> hasAggs ;
162
198
199
+ /* Use 'new_tlist' instead of 'tlist' */
163
200
result_plan =
164
- make_groupPlan (& tlist ,
201
+ make_groupPlan (& new_tlist ,
165
202
tuplePerGroup ,
166
203
parse -> groupClause ,
167
204
result_plan );
168
-
169
205
}
170
206
171
207
/*
172
208
* If aggregate is present, insert the agg node
173
209
*/
174
210
if (parse -> hasAggs )
175
211
{
212
+ int old_length = 0 , new_length = 0 ;
213
+
214
+ /* Create the AGG node but use 'tlist' not 'new_tlist' as target list because we
215
+ * don't want the additional attributes (only used for the havingQual, see above)
216
+ * to show up in the result */
176
217
result_plan = (Plan * ) make_agg (tlist , result_plan );
177
218
178
219
/*
179
220
* set the varno/attno entries to the appropriate references to
180
221
* the result tuple of the subplans.
181
222
*/
182
223
((Agg * ) result_plan )-> aggs =
183
- set_agg_tlist_references ((Agg * ) result_plan );
224
+ set_agg_tlist_references ((Agg * ) result_plan );
184
225
185
- if (parse -> havingQual != NULL ) {
186
- List * clause ;
187
226
188
- /* set qpqual of having clause */
189
- ((Agg * ) result_plan )-> plan .qual = cnfify ((Expr * )parse -> havingQual ,true);
190
-
191
- foreach (clause , ((Agg * ) result_plan )-> plan .qual )
192
- {
193
- ((Agg * ) result_plan )-> aggs = nconc (((Agg * ) result_plan )-> aggs ,
194
- check_having_qual_for_aggs ((Node * ) lfirst (clause ),
195
- ((Agg * ) result_plan )-> plan .lefttree -> targetlist ));
196
- }
197
- }
198
- }
227
+ if (parse -> havingQual != NULL )
228
+ {
229
+ List * clause ;
230
+ List * * vpm = NULL ;
231
+
232
+
233
+ /* stuff copied from above to handle the use of attributes from outside
234
+ * in subselects */
199
235
236
+ if (parse -> rtable != NULL )
237
+ {
238
+ vpm = (List * * ) palloc (length (parse -> rtable ) * sizeof (List * ));
239
+ memset (vpm , 0 , length (parse -> rtable ) * sizeof (List * ));
240
+ }
241
+ PlannerVarParam = lcons (vpm , PlannerVarParam );
242
+
243
+ /* There is a subselect in the havingQual, so we have to process it
244
+ * using the same function as for a subselect in 'where' */
245
+ if (parse -> hasSubLinks )
246
+ {
247
+ (List * ) parse -> havingQual =
248
+ (List * ) SS_process_sublinks ((Node * ) parse -> havingQual );
249
+ }
250
+
251
+ /* convert the havingQual to conjunctive normal form (cnf) */
252
+ (List * ) parse -> havingQual = cnfify ((Expr * )(Node * ) parse -> havingQual ,true);
253
+
254
+ /* Calculate the opfids from the opnos (=select the correct functions for
255
+ * the used VAR datatypes) */
256
+ (List * ) parse -> havingQual = fix_opids ((List * ) parse -> havingQual );
257
+
258
+ ((Agg * ) result_plan )-> plan .qual = (List * ) parse -> havingQual ;
259
+
260
+ /* Check every clause of the havingQual for aggregates used and append
261
+ * them to result_plan->aggs */
262
+ foreach (clause , ((Agg * ) result_plan )-> plan .qual )
263
+ {
264
+ /* Make sure there are aggregates in the havingQual
265
+ * if so, the list must be longer after check_having_qual_for_aggs */
266
+ old_length = length (((Agg * ) result_plan )-> aggs );
267
+
268
+ ((Agg * ) result_plan )-> aggs = nconc (((Agg * ) result_plan )-> aggs ,
269
+ check_having_qual_for_aggs ((Node * ) lfirst (clause ),
270
+ ((Agg * ) result_plan )-> plan .lefttree -> targetlist ,
271
+ ((List * ) parse -> groupClause )));
272
+
273
+ /* Have a look at the length of the returned list. If there is no
274
+ * difference, no aggregates have been found and that means, that
275
+ * the Qual belongs to the where clause */
276
+ if (((new_length = length (((Agg * ) result_plan )-> aggs )) == old_length ) ||
277
+ (new_length == 0 ))
278
+ {
279
+ elog (ERROR ,"This could have been done in a where clause!!" );
280
+ return (Plan * )NIL ;
281
+ }
282
+ }
283
+ PlannerVarParam = lnext (PlannerVarParam );
284
+ if (vpm != NULL )
285
+ pfree (vpm );
286
+ }
287
+ }
288
+
200
289
/*
201
290
* For now, before we hand back the plan, check to see if there is a
202
291
* user-specified sort that needs to be done. Eventually, this will
0 commit comments