KickJava   Java API By Example, From Geeks To Geeks.

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


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.ArrayList JavaDoc;
47 import java.util.HashMap JavaDoc;
48 import java.util.Iterator JavaDoc;
49 import java.util.Vector JavaDoc;
50
51 import java.sql.SQLException JavaDoc;
52
53 import antlr.RecognitionException;
54
55 import com.quadcap.sql.types.Op;
56
57 import com.quadcap.util.Debug;
58
59 /**
60  * A table expression representing a single join operation.
61  *
62  * @author Stan Bailes
63  */

64 public class JoinedTable extends TableExpression implements Externalizable JavaDoc {
65     int op = -1;
66     TableExpression a = null;
67     TableExpression b = null;
68     Tuple tuple = null;
69     Expression onExpression = null;
70     /** Vector<String> **/
71     Vector JavaDoc usingList = null;
72
73     /**
74      * Default public constructor
75      */

76     public JoinedTable() {}
77
78     /**
79      * Either a cross join or a natural join over any common columns.
80      */

81     public JoinedTable(int op, TableExpression a, TableExpression b) {
82     this.op = op;
83     this.a = a;
84     this.b = b;
85     }
86
87     /**
88      * Join 'ON' on
89      */

90     public void setOnExpression(Expression on) {
91     this.onExpression = on;
92     }
93
94     /**
95      * Join 'USING' column list
96      */

97     public void setUsingList(Vector JavaDoc usingList) {
98     this.usingList = usingList;
99     }
100
101     
102     /**
103      * Verify the correctness of the join expression -- the grammar can't
104      * catch all of the possible errors.
105      */

106     /*{joinedTable.xml-100}
107      * <section name="Semantic Constraints">
108      *
109      * <p>The syntax as specified here will accept certain join expressions
110      * that are illegal; as a result there are additional semantic checks
111      * performed on all join expressions:</p>
112      *
113      * <ul>
114      * <li>At most one of the {<code>NATURAL, UNION, ON, USING</code>} may be
115      * specified in a join expression.</li>
116      * <li>If neither <code>NATURAL</code> or <code>UNION</code> is specified,
117      * then
118      * either <code>ON</code> or <code>USING</code> must be specified.</li>
119      * </ul>
120      * </section>
121      */

122     public void checkSyntax() throws RecognitionException {
123     if ((op & Op.NATURAL) != 0) {
124         if (usingList != null) {
125         throw new RecognitionException("Can't specify both NATURAL and " +
126                       "USING");
127         }
128         if (onExpression != null) {
129         throw new RecognitionException("Can't specify both NATURAL and ON");
130         }
131     } else if (op == Op.LEFT || op == Op.RIGHT ||
132            op == Op.FULL || op == Op.INNER) {
133         if (usingList == null && onExpression == null) {
134         throw new RecognitionException(Op.toString(op) +
135                       " join requires ON or USING " +
136                       "clause");
137         }
138     } else if (op == Op.UNION) {
139         if (usingList != null) {
140         throw new RecognitionException("Can't specify both UNION and USING");
141         }
142         if (onExpression != null) {
143         throw new RecognitionException("Can't specify both UNION and ON");
144         }
145     }
146     }
147
148     /**
149      * Override base method to propagate to base tables.
150      */

151     public void setWhere(Expression where) {
152         this.where = where;
153         a.setWhere(where);
154         b.setWhere(where);
155     }
156
157     public int rank() { return 2; }
158
159     public boolean isUpdatable() { return false; }
160
161     public void getBaseTables(Vector JavaDoc v) {
162     a.getBaseTables(v);
163     b.getBaseTables(v);
164     }
165
166     public void visitSubExpressions(ExpressionVisitor ev) {
167     if (a != null) ev.visit(a);
168     if (b != null) ev.visit(b);
169     }
170
171     String JavaDoc toString(Iterator JavaDoc iter) {
172         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
173         while (iter.hasNext()) {
174             if (sb.length() > 0) sb.append(", ");
175             sb.append(iter.next().toString());
176         }
177         return sb.toString();
178     }
179
180     static String JavaDoc isa(Object JavaDoc x) {
181         return x == null ? "null" :
182             x.getClass().getName() + ":" + x;
183     }
184
185     public Cursor getCursor(Session session, Cursor outer)
186     throws SQLException JavaDoc
187     {
188     int nop = op & ~Op.NATURAL;
189         int[] map = null;
190
191         Cursor ra = a.getCursor(session, outer);
192         Cursor rb = b.getCursor(session, outer);
193
194         int size = ra.getColumnCount() + rb.getColumnCount();
195
196         if ((op & Op.NATURAL) != 0) {
197             getNaturalJoinColumns(ra, rb);
198         }
199              
200         int usize = usingList == null ? 0 : usingList.size();
201         map = new int[size - usize];
202         Tuple jtl = mapColumns(ra, rb, map, true);
203         JoinMapRow jrl = new JoinMapRow(map);
204
205         map = new int[size - usize];
206         Tuple jtr = mapColumns(ra, rb, map, false);
207         JoinMapRow jrr = new JoinMapRow(map);
208
209
210         Cursor ret = null;
211         Cursor left = null;
212         Cursor right = null;
213         if (onExpression != null) {
214             where = new BinaryExpression(Op.AND, where, onExpression);
215         }
216
217         switch (nop) {
218         case Op.CROSS:
219         case Op.INNER:
220             ret = getCrossCursor(session, outer,
221                                  ra, rb, where, jtl, jrl,
222                                  false /* left */,
223                                  true /* inner */);
224             break;
225         case Op.LEFT:
226             ret = getCrossCursor(session, outer,
227                                  ra, rb, where, jtl, jrl,
228                                  true /* left */,
229                                  true /* inner */);
230             break;
231         case Op.RIGHT:
232             ret = getCrossCursor(session, outer,
233                                  rb, ra, where, jtr, jrr,
234                                  true /* left */,
235                                  true /* inner */);
236             break;
237         case Op.FULL:
238             left = getCrossCursor(session, outer,
239                                   ra, rb, where, jtl, jrl,
240                                   true /* left */,
241                                   true /* inner */);
242             right = getCrossCursor(session, outer,
243                                    rb, ra, where, jtr, jrr,
244                                   true /* left */,
245                                   false /* inner */);
246             break;
247         case Op.UNION:
248             ret = new JoinUnionCursor(session, outer, ra, rb, where, jtl, jrl);
249             break;
250         default:
251             throw new SQLException JavaDoc("Bad join type: " + nop);
252         }
253         if (left != null && right != null) {
254             MultiCursor mc = new MultiCursor(session, left);
255             mc.appendCursor(session, right);
256             ret = mc;
257         }
258         if (ret == null) {
259             throw new SQLException JavaDoc("Bad join");
260         }
261         return ret;
262     }
263
264     Cursor getCrossCursor(Session session, Cursor outer, Cursor ca, Cursor cb,
265               Expression where, Tuple jt, JoinMapRow jrl,
266                           boolean left, boolean inner)
267         throws SQLException JavaDoc
268     {
269         int[][] cols =
270             usingList != null ? getUsingColumns(ca, cb) :
271             new Analyze(session, where).getJoinColumns(ca, cb);
272
273         Cursor ret = null;
274     if (cols == null) {
275             //Debug.println("CROSS");
276
ret = new JoinCrossCursor(session, outer, ca, cb, where, jt, jrl,
277                                       left, inner);
278     } else {
279             //Debug.println("INNER");
280
int[] aCols = cols[0];
281         int[] bCols = cols[1];
282         ret = new JoinInnerCursor(session, outer, ca, aCols, cb, bCols,
283                                       where, jt, jrl, left, inner);
284     }
285         return ret;
286     }
287
288     int[][] getUsingColumns(Tuple a, Tuple b) throws SQLException JavaDoc {
289         int[][] ret = null;
290         if (usingList != null) {
291             int len = usingList.size();
292             ret = new int[2][len];
293             for (int i = 0; i < len; i++) {
294                 String JavaDoc u = usingList.get(i).toString();
295                 Column ca = a.getColumn(u);
296                 Column cb = b.getColumn(u);
297                 ret[0][i] = ca.getColumn();
298                 ret[1][i] = cb.getColumn();
299             }
300         }
301         return ret;
302     }
303
304     void getNaturalJoinColumns(Tuple ta, Tuple tb) throws SQLException JavaDoc {
305         usingList = new Vector JavaDoc();
306         HashMap JavaDoc t = new HashMap JavaDoc();
307         for (int i = 1; i <= tb.getColumnCount(); i++) {
308             Column cb = tb.getColumn(i);
309             t.put(cb.getShortName(), cb);
310         }
311         for (int i = 1; i <= ta.getColumnCount(); i++) {
312             Column ca = ta.getColumn(i);
313             if (t.containsKey(ca.getShortName())) {
314                 usingList.addElement(ca.getShortName());
315             }
316         }
317     }
318
319     /**
320      * Build the tuple for this join operation
321      */

322     private final Tuple mapColumns(Tuple ra, Tuple rb, int[] map, boolean left)
323         throws SQLException JavaDoc
324     {
325         HashMap JavaDoc uMap = null;
326         TupleImpl ti = new TupleImpl();
327         int usize = usingList == null ? 0 : usingList.size();
328
329         // First, add the common columns, where we drop the table names.
330
if (usize > 0) {
331             Tuple outer = left ? ra : rb;
332             uMap = new HashMap JavaDoc();
333             int[] umap = outer.mapColumns(usingList);
334             for (int i = 0; i < umap.length; i++) {
335                 int u = umap[i];
336                 Column ci = outer.getColumn(u);
337                 uMap.put(ci.getShortName(), "");
338                 Column col = new Column(ci.getShortName(), ci);
339                 col.setJoinColumn(true);
340                 ti.addColumn(col);
341                 map[i] = 0-u;
342             }
343         }
344
345         // Then the remaining columns from 'A'
346
int base = usize;
347         for (int i = 1; i <= ra.getColumnCount(); i++) {
348             Column ci = ra.getColumn(i);
349             if (uMap == null || !uMap.containsKey(ci.getShortName())) {
350                 int c = ci.getColumn();
351                 if (map[base] == 0) {
352                     map[base++] = left ? 0 - c : c;
353                     ti.addColumn(ci.getName(), ci.getType());
354                 }
355             }
356         }
357
358         // And the remaining columns from 'B'
359
for (int i = 1; i <= rb.getColumnCount(); i++) {
360             Column ci = rb.getColumn(i);
361             if (uMap == null || !uMap.containsKey(ci.getShortName())) {
362                 int c = ci.getColumn();
363                 if (map[base] == 0) {
364                     map[base++] = left ? c : 0 - c;
365                     ti.addColumn(ci.getName(), ci.getType());
366                 }
367             }
368         }
369         return ti;
370     }
371
372     public void readExternal(ObjectInput JavaDoc in)
373     throws IOException JavaDoc, ClassNotFoundException JavaDoc
374     {
375     this.op = in.readInt();
376     this.a = (TableExpression)in.readObject();
377     this.b = (TableExpression)in.readObject();
378     this.onExpression = (Expression)in.readObject();
379     this.usingList = (Vector JavaDoc)in.readObject();
380     }
381     
382     public void writeExternal(ObjectOutput JavaDoc out) throws IOException JavaDoc {
383     out.writeInt(op);
384     out.writeObject(a);
385     out.writeObject(b);
386     out.writeObject(onExpression);
387     out.writeObject(usingList);
388     }
389
390     public String JavaDoc toString() {
391         StringBuffer JavaDoc sb = new StringBuffer JavaDoc(a.toString());
392         
393         if ((op & Op.NATURAL) != 0) sb.append(" NATURAL");
394         op &= ~Op.NATURAL;
395         sb.append(' ');
396         sb.append(Op.toString(op));
397         sb.append(" JOIN ");
398         sb.append(b.toString());
399         return sb.toString();
400     }
401
402     //#ifdef DEBUG
403
public String JavaDoc name() {
404         StringBuffer JavaDoc sb = new StringBuffer JavaDoc('(');
405         sb.append(a.name());
406         sb.append("\n ");
407         sb.append(Op.toString(op));
408         sb.append("\n");
409         sb.append(b.name());
410         sb.append(')');
411         return sb.toString();
412     }
413     //#endif
414
}
415
Popular Tags