Quadcap Embeddable Database

com/quadcap/sql/JoinedTable.java

Go to the documentation of this file.
00001 package com.quadcap.sql; 00002 00003 /* Copyright 1999 - 2003 Quadcap Software. All rights reserved. 00004 * 00005 * This software is distributed under the Quadcap Free Software License. 00006 * This software may be used or modified for any purpose, personal or 00007 * commercial. Open Source redistributions are permitted. Commercial 00008 * redistribution of larger works derived from, or works which bundle 00009 * this software requires a "Commercial Redistribution License"; see 00010 * http://www.quadcap.com/purchase. 00011 * 00012 * Redistributions qualify as "Open Source" under one of the following terms: 00013 * 00014 * Redistributions are made at no charge beyond the reasonable cost of 00015 * materials and delivery. 00016 * 00017 * Redistributions are accompanied by a copy of the Source Code or by an 00018 * irrevocable offer to provide a copy of the Source Code for up to three 00019 * years at the cost of materials and delivery. Such redistributions 00020 * must allow further use, modification, and redistribution of the Source 00021 * Code under substantially the same terms as this license. 00022 * 00023 * Redistributions of source code must retain the copyright notices as they 00024 * appear in each source code file, these license terms, and the 00025 * disclaimer/limitation of liability set forth as paragraph 6 below. 00026 * 00027 * Redistributions in binary form must reproduce this Copyright Notice, 00028 * these license terms, and the disclaimer/limitation of liability set 00029 * forth as paragraph 6 below, in the documentation and/or other materials 00030 * provided with the distribution. 00031 * 00032 * The Software is provided on an "AS IS" basis. No warranty is 00033 * provided that the Software is free of defects, or fit for a 00034 * particular purpose. 00035 * 00036 * Limitation of Liability. Quadcap Software shall not be liable 00037 * for any damages suffered by the Licensee or any third party resulting 00038 * from use of the Software. 00039 */ 00040 00041 import java.io.Externalizable; 00042 import java.io.IOException; 00043 import java.io.ObjectInput; 00044 import java.io.ObjectOutput; 00045 00046 import java.util.ArrayList; 00047 import java.util.HashMap; 00048 import java.util.Iterator; 00049 import java.util.Vector; 00050 00051 import java.sql.SQLException; 00052 00053 import antlr.RecognitionException; 00054 00055 import com.quadcap.sql.types.Op; 00056 00057 import com.quadcap.util.Debug; 00058 00059 /** 00060 * A table expression representing a single join operation. 00061 * 00062 * @author Stan Bailes 00063 */ 00064 public class JoinedTable extends TableExpression implements Externalizable { 00065 int op = -1; 00066 TableExpression a = null; 00067 TableExpression b = null; 00068 Tuple tuple = null; 00069 Expression onExpression = null; 00070 /** Vector<String> **/ 00071 Vector usingList = null; 00072 00073 /** 00074 * Default public constructor 00075 */ 00076 public JoinedTable() {} 00077 00078 /** 00079 * Either a cross join or a natural join over any common columns. 00080 */ 00081 public JoinedTable(int op, TableExpression a, TableExpression b) { 00082 this.op = op; 00083 this.a = a; 00084 this.b = b; 00085 } 00086 00087 /** 00088 * Join 'ON' on 00089 */ 00090 public void setOnExpression(Expression on) { 00091 this.onExpression = on; 00092 } 00093 00094 /** 00095 * Join 'USING' column list 00096 */ 00097 public void setUsingList(Vector usingList) { 00098 this.usingList = usingList; 00099 } 00100 00101 00102 /** 00103 * Verify the correctness of the join expression -- the grammar can't 00104 * catch all of the possible errors. 00105 */ 00106 /*{joinedTable.xml-100} 00107 * <section name="Semantic Constraints"> 00108 * 00109 * <p>The syntax as specified here will accept certain join expressions 00110 * that are illegal; as a result there are additional semantic checks 00111 * performed on all join expressions:</p> 00112 * 00113 * <ul> 00114 * <li>At most one of the {<code>NATURAL, UNION, ON, USING</code>} may be 00115 * specified in a join expression.</li> 00116 * <li>If neither <code>NATURAL</code> or <code>UNION</code> is specified, 00117 * then 00118 * either <code>ON</code> or <code>USING</code> must be specified.</li> 00119 * </ul> 00120 * </section> 00121 */ 00122 public void checkSyntax() throws RecognitionException { 00123 if ((op & Op.NATURAL) != 0) { 00124 if (usingList != null) { 00125 throw new RecognitionException("Can't specify both NATURAL and " + 00126 "USING"); 00127 } 00128 if (onExpression != null) { 00129 throw new RecognitionException("Can't specify both NATURAL and ON"); 00130 } 00131 } else if (op == Op.LEFT || op == Op.RIGHT || 00132 op == Op.FULL || op == Op.INNER) { 00133 if (usingList == null && onExpression == null) { 00134 throw new RecognitionException(Op.toString(op) + 00135 " join requires ON or USING " + 00136 "clause"); 00137 } 00138 } else if (op == Op.UNION) { 00139 if (usingList != null) { 00140 throw new RecognitionException("Can't specify both UNION and USING"); 00141 } 00142 if (onExpression != null) { 00143 throw new RecognitionException("Can't specify both UNION and ON"); 00144 } 00145 } 00146 } 00147 00148 /** 00149 * Override base method to propagate to base tables. 00150 */ 00151 public void setWhere(Expression where) { 00152 this.where = where; 00153 a.setWhere(where); 00154 b.setWhere(where); 00155 } 00156 00157 public int rank() { return 2; } 00158 00159 public boolean isUpdatable() { return false; } 00160 00161 public void getBaseTables(Vector v) { 00162 a.getBaseTables(v); 00163 b.getBaseTables(v); 00164 } 00165 00166 public void visitSubExpressions(ExpressionVisitor ev) { 00167 if (a != null) ev.visit(a); 00168 if (b != null) ev.visit(b); 00169 } 00170 00171 String toString(Iterator iter) { 00172 StringBuffer sb = new StringBuffer(); 00173 while (iter.hasNext()) { 00174 if (sb.length() > 0) sb.append(", "); 00175 sb.append(iter.next().toString()); 00176 } 00177 return sb.toString(); 00178 } 00179 00180 static String isa(Object x) { 00181 return x == null ? "null" : 00182 x.getClass().getName() + ":" + x; 00183 } 00184 00185 public Cursor getCursor(Session session, Cursor outer) 00186 throws SQLException 00187 { 00188 int nop = op & ~Op.NATURAL; 00189 int[] map = null; 00190 00191 Cursor ra = a.getCursor(session, outer); 00192 Cursor rb = b.getCursor(session, outer); 00193 00194 int size = ra.getColumnCount() + rb.getColumnCount(); 00195 00196 if ((op & Op.NATURAL) != 0) { 00197 getNaturalJoinColumns(ra, rb); 00198 } 00199 00200 int usize = usingList == null ? 0 : usingList.size(); 00201 map = new int[size - usize]; 00202 Tuple jtl = mapColumns(ra, rb, map, true); 00203 JoinMapRow jrl = new JoinMapRow(map); 00204 00205 map = new int[size - usize]; 00206 Tuple jtr = mapColumns(ra, rb, map, false); 00207 JoinMapRow jrr = new JoinMapRow(map); 00208 00209 00210 Cursor ret = null; 00211 Cursor left = null; 00212 Cursor right = null; 00213 if (onExpression != null) { 00214 where = new BinaryExpression(Op.AND, where, onExpression); 00215 } 00216 00217 switch (nop) { 00218 case Op.CROSS: 00219 case Op.INNER: 00220 ret = getCrossCursor(session, outer, 00221 ra, rb, where, jtl, jrl, 00222 false /* left */, 00223 true /* inner */); 00224 break; 00225 case Op.LEFT: 00226 ret = getCrossCursor(session, outer, 00227 ra, rb, where, jtl, jrl, 00228 true /* left */, 00229 true /* inner */); 00230 break; 00231 case Op.RIGHT: 00232 ret = getCrossCursor(session, outer, 00233 rb, ra, where, jtr, jrr, 00234 true /* left */, 00235 true /* inner */); 00236 break; 00237 case Op.FULL: 00238 left = getCrossCursor(session, outer, 00239 ra, rb, where, jtl, jrl, 00240 true /* left */, 00241 true /* inner */); 00242 right = getCrossCursor(session, outer, 00243 rb, ra, where, jtr, jrr, 00244 true /* left */, 00245 false /* inner */); 00246 break; 00247 case Op.UNION: 00248 ret = new JoinUnionCursor(session, outer, ra, rb, where, jtl, jrl); 00249 break; 00250 default: 00251 throw new SQLException("Bad join type: " + nop); 00252 } 00253 if (left != null && right != null) { 00254 MultiCursor mc = new MultiCursor(session, left); 00255 mc.appendCursor(session, right); 00256 ret = mc; 00257 } 00258 if (ret == null) { 00259 throw new SQLException("Bad join"); 00260 } 00261 return ret; 00262 } 00263 00264 Cursor getCrossCursor(Session session, Cursor outer, Cursor ca, Cursor cb, 00265 Expression where, Tuple jt, JoinMapRow jrl, 00266 boolean left, boolean inner) 00267 throws SQLException 00268 { 00269 int[][] cols = 00270 usingList != null ? getUsingColumns(ca, cb) : 00271 new Analyze(session, where).getJoinColumns(ca, cb); 00272 00273 Cursor ret = null; 00274 if (cols == null) { 00275 //Debug.println("CROSS"); 00276 ret = new JoinCrossCursor(session, outer, ca, cb, where, jt, jrl, 00277 left, inner); 00278 } else { 00279 //Debug.println("INNER"); 00280 int[] aCols = cols[0]; 00281 int[] bCols = cols[1]; 00282 ret = new JoinInnerCursor(session, outer, ca, aCols, cb, bCols, 00283 where, jt, jrl, left, inner); 00284 } 00285 return ret; 00286 } 00287 00288 int[][] getUsingColumns(Tuple a, Tuple b) throws SQLException { 00289 int[][] ret = null; 00290 if (usingList != null) { 00291 int len = usingList.size(); 00292 ret = new int[2][len]; 00293 for (int i = 0; i < len; i++) { 00294 String u = usingList.get(i).toString(); 00295 Column ca = a.getColumn(u); 00296 Column cb = b.getColumn(u); 00297 ret[0][i] = ca.getColumn(); 00298 ret[1][i] = cb.getColumn(); 00299 } 00300 } 00301 return ret; 00302 } 00303 00304 void getNaturalJoinColumns(Tuple ta, Tuple tb) throws SQLException { 00305 usingList = new Vector(); 00306 HashMap t = new HashMap(); 00307 for (int i = 1; i <= tb.getColumnCount(); i++) { 00308 Column cb = tb.getColumn(i); 00309 t.put(cb.getShortName(), cb); 00310 } 00311 for (int i = 1; i <= ta.getColumnCount(); i++) { 00312 Column ca = ta.getColumn(i); 00313 if (t.containsKey(ca.getShortName())) { 00314 usingList.addElement(ca.getShortName()); 00315 } 00316 } 00317 } 00318 00319 /** 00320 * Build the tuple for this join operation 00321 */ 00322 private final Tuple mapColumns(Tuple ra, Tuple rb, int[] map, boolean left) 00323 throws SQLException 00324 { 00325 HashMap uMap = null; 00326 TupleImpl ti = new TupleImpl(); 00327 int usize = usingList == null ? 0 : usingList.size(); 00328 00329 // First, add the common columns, where we drop the table names. 00330 if (usize > 0) { 00331 Tuple outer = left ? ra : rb; 00332 uMap = new HashMap(); 00333 int[] umap = outer.mapColumns(usingList); 00334 for (int i = 0; i < umap.length; i++) { 00335 int u = umap[i]; 00336 Column ci = outer.getColumn(u); 00337 uMap.put(ci.getShortName(), ""); 00338 Column col = new Column(ci.getShortName(), ci); 00339 col.setJoinColumn(true); 00340 ti.addColumn(col); 00341 map[i] = 0-u; 00342 } 00343 } 00344 00345 // Then the remaining columns from 'A' 00346 int base = usize; 00347 for (int i = 1; i <= ra.getColumnCount(); i++) { 00348 Column ci = ra.getColumn(i); 00349 if (uMap == null || !uMap.containsKey(ci.getShortName())) { 00350 int c = ci.getColumn(); 00351 if (map[base] == 0) { 00352 map[base++] = left ? 0 - c : c; 00353 ti.addColumn(ci.getName(), ci.getType()); 00354 } 00355 } 00356 } 00357 00358 // And the remaining columns from 'B' 00359 for (int i = 1; i <= rb.getColumnCount(); i++) { 00360 Column ci = rb.getColumn(i); 00361 if (uMap == null || !uMap.containsKey(ci.getShortName())) { 00362 int c = ci.getColumn(); 00363 if (map[base] == 0) { 00364 map[base++] = left ? c : 0 - c; 00365 ti.addColumn(ci.getName(), ci.getType()); 00366 } 00367 } 00368 } 00369 return ti; 00370 } 00371 00372 public void readExternal(ObjectInput in) 00373 throws IOException, ClassNotFoundException 00374 { 00375 this.op = in.readInt(); 00376 this.a = (TableExpression)in.readObject(); 00377 this.b = (TableExpression)in.readObject(); 00378 this.onExpression = (Expression)in.readObject(); 00379 this.usingList = (Vector)in.readObject(); 00380 } 00381 00382 public void writeExternal(ObjectOutput out) throws IOException { 00383 out.writeInt(op); 00384 out.writeObject(a); 00385 out.writeObject(b); 00386 out.writeObject(onExpression); 00387 out.writeObject(usingList); 00388 } 00389 00390 public String toString() { 00391 StringBuffer sb = new StringBuffer(a.toString()); 00392 00393 if ((op & Op.NATURAL) != 0) sb.append(" NATURAL"); 00394 op &= ~Op.NATURAL; 00395 sb.append(' '); 00396 sb.append(Op.toString(op)); 00397 sb.append(" JOIN "); 00398 sb.append(b.toString()); 00399 return sb.toString(); 00400 } 00401 00402 //#ifdef DEBUG 00403 public String name() { 00404 StringBuffer sb = new StringBuffer('('); 00405 sb.append(a.name()); 00406 sb.append("\n "); 00407 sb.append(Op.toString(op)); 00408 sb.append("\n"); 00409 sb.append(b.name()); 00410 sb.append(')'); 00411 return sb.toString(); 00412 } 00413 //#endif 00414 }