KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > quadcap > sql > SelectExpression


1 package com.quadcap.sql;
2
3 /* Copyright 1999 - 2003 Quadcap Software. All rights reserved.
4  *
5  * This software is distributed under the Quadcap Free Software License.
6  * This software may be used or modified for any purpose, personal or
7  * commercial. Open Source redistributions are permitted. Commercial
8  * redistribution of larger works derived from, or works which bundle
9  * this software requires a "Commercial Redistribution License"; see
10  * http://www.quadcap.com/purchase.
11  *
12  * Redistributions qualify as "Open Source" under one of the following terms:
13  *
14  * Redistributions are made at no charge beyond the reasonable cost of
15  * materials and delivery.
16  *
17  * Redistributions are accompanied by a copy of the Source Code or by an
18  * irrevocable offer to provide a copy of the Source Code for up to three
19  * years at the cost of materials and delivery. Such redistributions
20  * must allow further use, modification, and redistribution of the Source
21  * Code under substantially the same terms as this license.
22  *
23  * Redistributions of source code must retain the copyright notices as they
24  * appear in each source code file, these license terms, and the
25  * disclaimer/limitation of liability set forth as paragraph 6 below.
26  *
27  * Redistributions in binary form must reproduce this Copyright Notice,
28  * these license terms, and the disclaimer/limitation of liability set
29  * forth as paragraph 6 below, in the documentation and/or other materials
30  * provided with the distribution.
31  *
32  * The Software is provided on an "AS IS" basis. No warranty is
33  * provided that the Software is free of defects, or fit for a
34  * particular purpose.
35  *
36  * Limitation of Liability. Quadcap Software shall not be liable
37  * for any damages suffered by the Licensee or any third party resulting
38  * from use of the Software.
39  */

40
41 import java.io.Externalizable JavaDoc;
42 import java.io.IOException JavaDoc;
43 import java.io.ObjectInput JavaDoc;
44 import java.io.ObjectOutput JavaDoc;
45
46 import java.util.Vector JavaDoc;
47
48 import java.sql.SQLException JavaDoc;
49
50 import com.quadcap.sql.types.Op;
51 import com.quadcap.sql.types.Value;
52 import com.quadcap.sql.types.ValueLong;
53 import com.quadcap.sql.types.ValueNull;
54
55 import com.quadcap.util.Debug;
56
57 /**
58  * A table expression representing a <b>SELECT</b> clause.
59  *
60  * @author Stan Bailes
61  */

62
63 public class SelectExpression extends TableExpression
64     implements Externalizable JavaDoc
65 {
66     boolean distinct = false;
67     Vector JavaDoc items = null;
68     Vector JavaDoc from = null;
69     Vector JavaDoc groupBy = null;
70     Expression having = null;
71     Vector JavaDoc aggregates = null;
72
73     /**
74      * Default constructor
75      */

76     public SelectExpression() {}
77
78     /**
79      * The typical 'select from table where' stuff
80      */

81     public SelectExpression(String JavaDoc tableName, Expression where) {
82     Vector JavaDoc v = new Vector JavaDoc();
83     v.addElement(new SelectFromTable(tableName));
84     this.setFrom(v);
85     this.setWhere(where);
86     }
87
88     /**
89      * Get the FROM list
90      */

91     public Vector JavaDoc getFrom() {
92         return from;
93     }
94
95     /**
96      * Set the DISTINCT flag
97      */

98     public void setDistinct(boolean b) { this.distinct = b; }
99
100     /**
101      * Set the select items (i.e., the colums returned from this select)
102      */

103     public void setItems(Vector JavaDoc v) { this.items = v; }
104
105     /**
106      * Set the from list (a list of tables is implicitly joined)
107      */

108     public void setFrom(Vector JavaDoc v) { this.from = v; }
109
110     /**
111      * Set the GROUP BY clause as a list of names
112      */

113     public void setGroupBy(Vector JavaDoc v) { this.groupBy = v; }
114
115     /**
116      * Set the HAVING expression
117      */

118     public void setHaving(Expression e) { this.having = e; }
119
120     /**
121      * We're a table
122      */

123     public int rank() { return 2; }
124
125     /**
126      * Well, a bunch of things can prevent us from being updateable.
127      * Here are the ones we know about:
128      */

129     public boolean isUpdatable() {
130     if (groupBy != null) return false;
131     if (having != null) return false;
132     if (distinct) return false;
133     if (from == null || from.size() > 1) return false;
134     if (aggregates != null && aggregates.size() > 0) return false;
135
136     TableExpression t = (TableExpression)from.elementAt(0);
137     if (!t.isUpdatable()) return false;
138     
139     return true; // Good luck, sir.
140
}
141
142     /**
143      * Cross-join multiple tables in 'from' clause.
144      *
145      * TODO:
146      * First, tables which have selection constraintsfind pairs
147      * of tables that have candidate join columns and greedily join
148      * them. Next, pick tables which have matching non-key join
149      * columns and join them Finally, join all the rest.
150      */

151     TableExpression scheduleJoin() throws SQLException JavaDoc {
152     while (from.size() > 1) {
153         TableExpression a = (TableExpression)from.elementAt(0);
154         TableExpression b = (TableExpression)from.elementAt(1);
155         JoinedTable t = new JoinedTable(Op.CROSS, a, b);
156             //t.setOnExpression(where);
157
from.removeElementAt(0);
158         from.setElementAt(t, 0);
159     }
160     TableExpression item = (TableExpression)from.elementAt(0);
161     return item;
162     }
163
164     /**
165      * This is the main entry point to this madness. Our handling of
166      * selects is fairly literal, and pretty brute force. Things work
167      * much better if there are appropriate indexes to use based on the
168      * query.....
169      */

170     public Cursor getCursor(Session session, Cursor cur) throws SQLException JavaDoc {
171         if (from == null) {
172             Row row = new Row(items.size());
173             TupleImpl t = new TupleImpl();
174             for (int i = 0; i < items.size(); i++) {
175                 SelectItem item = (SelectItem)items.elementAt(i);
176                 Expression expr = item.getExpression();
177                 Value v = expr.getValue(session, cur);
178                 String JavaDoc name = item.getAsName();
179                 if (name == null) name = "Column" + i;
180                 t.addColumn(name, v.getType());
181                 row.set(i+1, expr.getValue(session, cur));
182             }
183             return new StaticCursor(session, t, row);
184         }
185     TableExpression tab = null;
186         
187     if (from.size() > 1) {
188         tab = scheduleJoin();
189     } else {
190         tab = (TableExpression)from.elementAt(0);
191     }
192
193     // give the join or query a chance to optimize
194
tab.setWhere(where);
195
196         Cursor c = tab.getCursor(session, cur);
197     if (where != null) {
198             // Actually, we could avoid creating a predicate cursor
199
// in cases where the upstream index cursor is already
200
// sufficient.
201
c = new PredicateCursor(session, c, where);
202     }
203     if (groupBy != null) {
204         GroupByCursor gc = new GroupByCursor(session, c, groupBy);
205         c = gc;
206         Vector JavaDoc tItems = items; // why alias items here? XX
207
if (having != null) {
208                 int cnt = (items == null) ? 0 : items.size();
209                 tItems = new Vector JavaDoc(cnt + 1);
210                 for (int i = 0; i < cnt; i++) {
211                     tItems.addElement(items.elementAt(i));
212                 }
213                 tItems.addElement(new SelectItem(having));
214             }
215             if (isAggregate(session, tItems)) {
216                 ItemsCursor ic = new ItemsCursor(session, gc, tItems);
217                 c = new AggregateCursor(session, ic, gc, aggregates);
218                 if (having != null) {
219                     c = new HavingCursor(session, c);
220                 }
221             } else if (tItems != null) {
222                 ItemsCursor ic = new ItemsCursor(session, gc, tItems);
223                 c = ic;
224                 c = new AggregateCursor(session, ic, gc, aggregates);
225                 if (having != null) {
226                     c = new HavingCursor(session, c);
227                 }
228             }
229     } else {
230         if (isAggregate(session, items)) {
231                 Cursor optimized = optimizeAggregate(session, c, items);
232                 if (optimized != null) {
233                     c = optimized;
234                 } else {
235                     ItemsCursor ic = new ItemsCursor(session, c, items);
236                     c = new AggregateCursor(session, ic, null, aggregates);
237                 }
238         } else {
239         if (items != null) {
240             c = new ItemsCursor(session, c, items);
241         }
242         }
243     }
244     if (distinct) {
245         c = new DistinctCursor(session, c);
246     }
247
248     if (cur != null) c.setOuterCursor(cur);
249         //#ifdef DEBUG
250
showCursor(c);
251         //#endif
252
return c;
253     }
254
255     /**
256      * Visitor class used to analyze the select expression for aggregate usage
257      */

258     class IsAggregate implements ExpressionVisitor {
259         Session session = null;
260     Vector JavaDoc aggregates = null;
261     boolean seenName = false;
262     boolean seenAggregate = false;
263
264         public IsAggregate(Session session) {
265             this.session = session;
266         }
267         
268     public void visit(Expression sub) {
269         if (sub instanceof AggregateExpression) {
270         seenAggregate = true;
271         //try {
272
// ((AggregateExpression)sub).reset(session);
273
//} catch (IOException e) {
274
// Debug.print(e);
275
//}
276
if (aggregates == null) {
277             aggregates = new Vector JavaDoc();
278         }
279         aggregates.addElement(sub);
280         } else {
281         seenName |= (sub instanceof NameExpression);
282         sub.visitSubExpressions(this);
283         }
284     }
285     
286     void reset() {
287         seenName = seenAggregate = false;
288     }
289     }
290
291     /**
292      * Check the list of select items to see if any of them are aggregate
293      * expressions. (Side effect: set 'aggregates' variable.)
294      */

295     boolean isAggregate(Session session, Vector JavaDoc items) throws SQLException JavaDoc {
296     if (items == null) return false;
297     int cnt = 0;
298     IsAggregate isVis = new IsAggregate(session);
299     for (int i = 0; i < items.size(); i++) {
300         SelectItem item = (SelectItem)items.elementAt(i);
301         Expression expression = item.getExpression();
302             isVis.reset();
303         isVis.visit(expression);
304         if (isVis.seenAggregate && isVis.seenName && groupBy == null) {
305         throw new SQLException JavaDoc("Expressions can't mix aggregate and " +
306                        "non-aggregate values", "42000");
307         }
308         if (isVis.seenAggregate) cnt++;
309     }
310     this.aggregates = isVis.aggregates;
311     if (cnt == 0) return false;
312     return true;
313     }
314
315     /**
316      * Helper to create a cursor containing a single constant value.
317      */

318     Cursor staticCursor(Session session, Cursor c, Vector JavaDoc items, Value v)
319         throws SQLException JavaDoc
320     {
321         Row r = new Row(1);
322         r.set(1, v);
323         ItemsCursor it = new ItemsCursor(session, c, items);
324         return new StaticCursor(session, it, r);
325     }
326
327     /**
328      * Identify cases of MIN(column) or MAX(column) on indexed columns.
329      * Compute the value directly and replace the index cursor with a
330      * static cursor containing the value.
331      */

332     Cursor optimizeAggregate(Session session, Cursor c, Vector JavaDoc items)
333         throws SQLException JavaDoc
334     {
335         Cursor ret = null;
336         if (c instanceof IndexCursor) {
337             IndexCursor ic = (IndexCursor)c;
338             if (items.size() == 1) {
339                 SelectItem item = (SelectItem)items.get(0);
340                 Expression te = item.getExpression();
341                 if (te instanceof AggregateExpression) {
342                     AggregateExpression ag = (AggregateExpression)te;
343                     if (ag.isMin() || ag.isMax()) {
344                         Expression inner = ag.getInnerExpression();
345                         if (inner instanceof NameExpression) {
346                             NameExpression nam = (NameExpression)inner;
347                             Column col = ic.getConstraint().getColumn(0);
348                             if (nam.getName().equals(col.getName())) {
349                                 // This is the lucky special case. MAX or MIN
350
// on an indexed column.
351
if (c.absolute(ag.isMax() ? -1 : 1)) {
352                                     Row r = c.getRow();
353                                     ret = staticCursor(session, c, items,
354                                                        r.item(1));
355                                     c.close();
356                                 }
357                             }
358                         }
359                     } else if (ag.isCount() && ag.all && c.size() > 0) {
360                         // another easy case is count(*)
361
ret = staticCursor(session, c, items,
362                                            new ValueLong(c.size()));
363                         c.close();
364                     }
365                 }
366             }
367         }
368         return ret;
369     }
370
371     /**
372      * Return the expression as a scalar value. We try. If there's only
373      * a single column in the result, we just return the column's value for
374      * the first row of the result. Otherwise, return ValueNull.
375      */

376     public Value getValue(Session session, Cursor cur) throws SQLException JavaDoc {
377     Value ret = ValueNull.valueNull;
378     Cursor cursor = getCursor(session, cur);
379         try {
380             if (cursor.next()) {
381                 Row row = cursor.getRow();
382                 if (row.size() == 1) {
383                     ret = row.item(1);
384                 }
385                 if (cursor.next()) {
386                     throw new SQLException JavaDoc("Cardinality violation", "21000");
387                 }
388             }
389             return ret;
390         } finally {
391             cursor.close();
392         }
393     }
394
395     /**
396      * Get the base tables that participate in this select expression
397      */

398     public void getBaseTables(Vector JavaDoc v) {
399     for (int i = 0; i < from.size(); i++) {
400         ((TableExpression)from.elementAt(i)).getBaseTables(v);
401     }
402     }
403
404     /**
405      * Expression implementation...
406      */

407     public void visitSubExpressions(ExpressionVisitor ev) {
408     if (where != null) ev.visit(where);
409     }
410
411     /**
412      * Visitor class to set the WHERE clause on all subtables in this SELECT
413      */

414     class AndWhere implements ExpressionVisitor {
415     Expression where;
416     AndWhere(Expression where) { this.where = where; }
417     public void visit(Expression sub) {
418         if (sub instanceof TableExpression) {
419                 TableExpression te = (TableExpression)sub;
420                 if (!te.anded) {
421                     Expression oldW = te.getWhere();
422                     if (oldW != null) {
423                         where = new BinaryExpression(Op.AND, where, oldW);
424                     }
425                     te.setWhere(where);
426                     te.anded = true;
427                 }
428         sub.visitSubExpressions(this);
429         }
430     }
431     }
432
433     /**
434      * Nope, I'm not a boolean
435      *
436      * @exception RuntimeException is thrown
437      */

438     public void invert() {
439     throw new RuntimeException JavaDoc(
440         "invert not implemented for SelectExpression");
441     }
442
443     /**
444      * Read me from a stream
445      */

446     public void readExternal(ObjectInput JavaDoc in)
447     throws IOException JavaDoc, ClassNotFoundException JavaDoc
448     {
449     this.distinct = (in.read() == 1);
450     this.items = (Vector JavaDoc)in.readObject();
451     this.from = (Vector JavaDoc)in.readObject();
452     this.where = (Expression)in.readObject();
453     this.groupBy = (Vector JavaDoc)in.readObject();
454     this.having = (Expression)in.readObject();
455     }
456
457     /**
458      * Write me to a stream
459      */

460     public void writeExternal(ObjectOutput JavaDoc out) throws IOException JavaDoc {
461     out.write(distinct ? 1 : 0);
462     out.writeObject(items);
463     out.writeObject(from);
464     out.writeObject(where);
465     out.writeObject(groupBy);
466     out.writeObject(having);
467     }
468
469     /**
470      * Return a string representation for debugging purposes
471      */

472     public String JavaDoc toString() {
473     StringBuffer JavaDoc sb = new StringBuffer JavaDoc("SELECT ");
474     if (items == null) {
475         sb.append("*");
476     } else {
477         for (int i = 0; i < items.size(); i++) {
478         if (i > 0) sb.append(',');
479         sb.append(items.elementAt(i).toString());
480         }
481     }
482         if (from != null) {
483             sb.append(" FROM ");
484             for (int i = 0; i < from.size(); i++) {
485                 if (i > 0) sb.append(',');
486                 sb.append(from.elementAt(i).toString());
487             }
488         }
489     if (where != null) {
490         sb.append(" WHERE ");
491         sb.append(where.toString());
492     }
493     if (groupBy != null) {
494         sb.append(" GROUP BY ");
495         for (int i = 0; i < groupBy.size(); i++) {
496         if (i > 0) sb.append(',');
497         sb.append(groupBy.elementAt(i).toString());
498         }
499     }
500     if (having != null) {
501         sb.append(" HAVING ");
502         sb.append(having.toString());
503     }
504     return sb.toString();
505     }
506
507     //#ifdef DEBUG
508
/**
509      * Display a cursor (under trace control)
510      */

511     public static final void showCursor(Cursor c) {
512     try {
513         if (Trace.bit(2)) {
514         Debug.println(c.toString());
515         }
516     } catch (Exception JavaDoc e) {
517         Debug.print(e);
518     }
519     }
520
521     /**
522      * Display c cursor (or not)
523      */

524     public static void showCursor(Cursor c, boolean really) {
525     try {
526         if (really) {
527         Debug.println("\n" + c);
528         }
529     } catch (Throwable JavaDoc e) {
530         Debug.print(e);
531     }
532     }
533
534     public String JavaDoc name() {
535         return toString();
536     }
537     //#endif
538

539
540 }
541
542
Popular Tags