Quadcap Embeddable Database

com/quadcap/sql/JoinInnerCursor.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.ByteArrayOutputStream; 00042 import java.io.Externalizable; 00043 import java.io.IOException; 00044 import java.io.ObjectInput; 00045 import java.io.ObjectOutput; 00046 00047 import java.util.BitSet; 00048 import java.util.Enumeration; 00049 import java.util.Hashtable; 00050 import java.util.Vector; 00051 00052 import java.sql.SQLException; 00053 00054 import com.quadcap.sql.file.BlockFile; 00055 import com.quadcap.sql.file.ByteUtil; 00056 import com.quadcap.sql.file.PageManager; 00057 import com.quadcap.sql.file.RandomAccess; 00058 00059 import com.quadcap.sql.io.ObjectOutputStream; 00060 00061 import com.quadcap.sql.index.BCursor; 00062 import com.quadcap.sql.index.Btree; 00063 import com.quadcap.sql.index.Comparator; 00064 00065 import com.quadcap.sql.types.Value; 00066 import com.quadcap.sql.types.ValueBoolean; 00067 import com.quadcap.sql.types.ValueInteger; 00068 import com.quadcap.sql.types.Type; 00069 00070 import com.quadcap.util.Debug; 00071 import com.quadcap.util.Util; 00072 00073 /** 00074 * Cursor implementing <b>INNER JOIN</b> using nested loops (optimized using 00075 * the inner index) 00076 * 00077 * @author Stan Bailes 00078 */ 00079 public class JoinInnerCursor extends JoinCursor { 00080 Database db; 00081 BlockFile file; 00082 BlockFile tempFile; 00083 00084 /** The join columns in the outer table. */ 00085 int[] caCols; 00086 00087 /** The current join key. */ 00088 byte[] caKey; 00089 00090 /** Cache number of columns in inner table. */ 00091 int cbCnt; 00092 00093 /** The join columns in the inner table. */ 00094 int[] cbCols; 00095 00096 /** Map inner, non-join-key columns from join cursor to inner cursor pos */ 00097 int[] cbMap; 00098 00099 /** Map outer columns from join cursor to outer cursor pos */ 00100 int[] caMap; 00101 00102 /** If the inner is really a table, or null if it's a view. */ 00103 Table cbTable; 00104 00105 /** If there's an index on the join columns of the inner table */ 00106 IndexConstraint cbIndex; 00107 00108 /** Either the index from above, or a temporary one we create. */ 00109 Btree index; 00110 00111 /** Did we make 'index', or were the columns already indexed? */ 00112 boolean tempIndex; 00113 00114 /** Did we make create new (temporary) rows for the inner index? */ 00115 boolean mustFreeRows = false; 00116 00117 BCursor cbKeys; 00118 00119 /** Used to compare the inner and outer join keys. */ 00120 Comparator compare; 00121 00122 /** Temporary used to build keys for inner table index */ 00123 MapRow mapRow; 00124 00125 /** 00126 * Constructor takes a whole shitload of parameters. 00127 * 00128 * @param session my execution session context (transaction, etc) 00129 * @param ca the outer join cursor 00130 * @param caCols the join columns in the outer cursor 00131 * @param cb the inner join cursor 00132 * @param cbCols the join columns in the inner cursor 00133 * @param where the ON/WHERE predicate expression 00134 * @param tuple the format we're supposed to return 00135 * @param row A mapping between the inner/outer rows and the join row 00136 * @param left Include non-matching outer rows with nulls for inner 00137 * @param inner Include matching rows 00138 * @param flip Left is Right, Right is Left 00139 */ 00140 public JoinInnerCursor(Session session, Cursor outer, Cursor ca, int[] caCols, 00141 Cursor cb, int[] cbCols, 00142 Expression where, Tuple tuple, JoinMapRow row, 00143 boolean left, boolean inner) 00144 throws SQLException 00145 { 00146 super(session, outer, ca, cb, where, tuple, row, left, inner); 00147 this.db = session.getDatabase(); 00148 this.file = db.getFile(); 00149 try { 00150 this.tempFile = db.getTempFile(); 00151 } catch (IOException e) { 00152 throw DbException.wrapThrowable(e); 00153 } 00154 this.caCols = caCols; 00155 this.cbCnt = cb.getColumnCount(); 00156 this.cbCols = cbCols; 00157 this.cbIndex = findIndex(cb, cbCols); 00158 this.compare = new Key(cbCols.length); 00159 00160 int joinCnt = cbCols.length; 00161 00162 } 00163 00164 final IndexConstraint findIndex(Cursor r, int[] cols) 00165 throws SQLException 00166 { 00167 IndexConstraint ret = null; 00168 BitSet b = null; 00169 this.cbTable = r.getTable(); 00170 if (this.cbTable == null) { 00171 return null; 00172 } 00173 final int num = cbTable.getNumConstraints(); 00174 for (int ci = 0; ci < num; ci++) { 00175 Constraint c = cbTable.getConstraint(ci); 00176 if (!(c instanceof IndexConstraint)) continue; 00177 int[] ccols = c.getColumns(); 00178 if (cols.length != ccols.length) continue; 00179 if (b == null) b = intArrayToBitSet(cols); 00180 boolean match = true; 00181 for (int i = 0; i < ccols.length; i++) { 00182 if (!b.get(ccols[i])) { 00183 match = false; 00184 break; 00185 } 00186 } 00187 if (match) ret = (IndexConstraint)c; 00188 } 00189 return ret; 00190 } 00191 00192 final static BitSet intArrayToBitSet(int[] a) { 00193 BitSet b = new BitSet(); 00194 for (int i = 0; i < a.length; i++) { 00195 b.set(a[i]); 00196 } 00197 return b; 00198 } 00199 00200 final byte[] makeInnerKey(Cursor cursor, Row row, long rowId) 00201 throws IOException, SQLException 00202 { 00203 return Key.makeKey(cursor, row, cbCols, rowId, true); 00204 } 00205 00206 final byte[] makeOuterKey(Cursor cursor, Row row) 00207 throws IOException, SQLException 00208 { 00209 if (cbIndex != null) { 00210 mapRow.setRow(row); 00211 return cbIndex.makeKey(session, mapRow, Long.MIN_VALUE); 00212 } 00213 return Key.makeKey(cursor, row, caCols, Long.MIN_VALUE, 00214 true); 00215 } 00216 00217 00218 protected void bfirst() throws SQLException { 00219 try { 00220 row.setB(null); 00221 if (index == null) { 00222 this.index = makeInnerIndex(); 00223 this.rb = new LazyRow(cbCnt); 00224 } 00225 caKey = makeOuterKey(ca, ra); 00226 if (cbKeys == null) cbKeys = index.getCursor(false); 00227 cbKeys.seek(caKey); 00228 } catch (IOException e) { 00229 throw DbException.wrapThrowable(e); 00230 } 00231 } 00232 00233 final protected boolean bnext() throws SQLException { 00234 try { 00235 boolean ret = false; 00236 00237 while (!ret && cbKeys.next()) { 00238 byte[] cbKey = cbKeys.getKeyBuf(); 00239 int len = cbKeys.getKeyLen(); 00240 if (compare.compare(caKey, 0, caKey.length, 00241 cbKey, 0, len) != 0) { 00242 break; 00243 } 00244 long rowId = cbKeys.getValAsLong(); 00245 boolean isTemp = cbTable == null; 00246 db.getRow(rowId, (LazyRow)rb, isTemp); 00247 row.setB(rb); 00248 //Debug.println("bnext: rb = " + rb + ", row = " + row); 00249 ret = true; 00250 } 00251 if (!ret) row.setB(null); 00252 return ret; 00253 } catch (IOException ex) { 00254 throw DbException.wrapThrowable(ex); 00255 } 00256 } 00257 00258 final void makeTemporaryIndexForTable() throws IOException, SQLException { 00259 //#ifdef DEBUG 00260 if (Trace.bit(17)) { 00261 StringBuffer sb = new StringBuffer(); 00262 for (int i = 0; i < cbCols.length; i++) { 00263 if (i > 0) sb.append(", "); 00264 sb.append(cb.getColumn(cbCols[i]).getName()); 00265 } 00266 Debug.println("Make Temporary Index for " + cb.getName() + 00267 " (" + sb + ")"); 00268 } 00269 //#endif 00270 Comparator tempCompare = new Key(cbCols.length + 1); 00271 index = session.makeTempTree(tempCompare); 00272 while (cb.next()) { 00273 long rowId = cb.getRowId(); 00274 Row r = cb.getRow(); 00275 byte[] key = makeInnerKey(cb, r, rowId); 00276 index.set(key, session.getBuf8(rowId)); 00277 } 00278 } 00279 00280 final void makeTemporaryIndexForView() throws IOException, SQLException { 00281 Comparator tempCompare = new Key(cbCols.length + 1); 00282 index = session.makeTempTree(tempCompare); 00283 int cnt = 0; 00284 mustFreeRows = true; 00285 while (cb.next()) { 00286 Row r = cb.getRow(); 00287 byte[] key = makeInnerKey(cb, r, cnt++); 00288 long rowId = session.getDatabase().putRow(session, tempFile, cb, r); 00289 index.set(key, session.getBuf8(rowId)); 00290 } 00291 } 00292 00293 final Btree makeInnerIndex() throws IOException, SQLException { 00294 if (cbTable != null) { 00295 if (cbIndex != null) { 00296 int[] map = new int[cb.getColumnCount() + 1]; 00297 for (int i = 0; i < cbCols.length; i++) { 00298 int ic = cbCols[i]; 00299 int oc = caCols[i]; 00300 map[ic] = oc; 00301 } 00302 mapRow = new MapRow(map, 1); 00303 index = cbIndex.getIndex(db); 00304 } else { 00305 makeTemporaryIndexForTable(); 00306 tempIndex = true; 00307 } 00308 } else { 00309 makeTemporaryIndexForView(); 00310 tempIndex = true; 00311 } 00312 return index; 00313 } 00314 00315 public void freeRows(Btree index, BlockFile file) 00316 throws IOException 00317 { 00318 BCursor c = index.getCursor(); 00319 try { 00320 while (c.next()) { 00321 long rowId = c.getValAsLong(); 00322 db.removeRow(file, rowId); 00323 } 00324 } finally { 00325 c.release(); 00326 } 00327 } 00328 00329 public void close() throws SQLException { 00330 try { 00331 super.close(); 00332 } finally { 00333 try { 00334 if (cbKeys != null) cbKeys.release(); 00335 } finally { 00336 try { 00337 cbKeys = null; 00338 if (tempIndex && mustFreeRows) { 00339 freeRows(index, tempFile); 00340 } 00341 } catch (IOException e2) { 00342 throw DbException.wrapThrowable(e2); 00343 } finally { 00344 try { 00345 mustFreeRows = false; 00346 if (tempIndex) { 00347 index.free(); 00348 session.getDatabase().releaseTempFile(); 00349 } 00350 } catch (IOException e3) { 00351 throw DbException.wrapThrowable(e3); 00352 } finally { 00353 index = null; 00354 if (tempFile != null) { 00355 session.getDatabase().releaseTempFile(); 00356 tempFile = null; 00357 } 00358 tempIndex = false; 00359 } 00360 } 00361 } 00362 } 00363 } 00364 }