Quadcap Embeddable Database

com/quadcap/sql/IndexCursor.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.IOException; 00043 import java.io.ObjectInput; 00044 import java.io.ObjectOutput; 00045 00046 import java.util.Enumeration; 00047 import java.util.Hashtable; 00048 import java.util.Vector; 00049 00050 import java.sql.SQLException; 00051 00052 import com.quadcap.sql.io.ObjectOutputStream; 00053 00054 import com.quadcap.sql.file.ByteUtil; 00055 00056 import com.quadcap.sql.index.Btree; 00057 import com.quadcap.sql.index.Comparator; 00058 00059 import com.quadcap.sql.types.Op; 00060 import com.quadcap.sql.types.Value; 00061 import com.quadcap.sql.types.ValueException; 00062 import com.quadcap.sql.types.ValueInteger; 00063 import com.quadcap.sql.types.ValueNull; 00064 00065 import com.quadcap.util.Debug; 00066 import com.quadcap.util.Util; 00067 00068 /** 00069 * Cursor for iterating an index. 00070 * 00071 * @author Stan Bailes 00072 */ 00073 public class IndexCursor extends BC_Cursor { 00074 Table table; 00075 IndexConstraint constraint; 00076 Btree index; 00077 Comparator compare; 00078 Vector indexNames = null; 00079 StaticCursor startVals = null; 00080 StaticCursor stopVals = null; 00081 00082 byte[] startVal; 00083 byte[] stopVal; 00084 00085 00086 /** 00087 * IndexCursor construtor for the specified table (with optional 00088 * where clause) 00089 */ 00090 public IndexCursor(Table table, Session session, 00091 IndexConstraint constraint, Expression where, 00092 String qualifier, Cursor outerCursor) 00093 throws SQLException, IOException 00094 { 00095 super(session, qualifier, outerCursor); 00096 this.table = table; 00097 this.constraint = constraint; 00098 this.index = constraint.getIndex(session.getDatabase()); 00099 this.compare = index.getComparator(); 00100 00101 addColumns(session, table); 00102 00103 this.indexNames = constraint.getColumnNames(); 00104 reset(where, outerCursor); 00105 } 00106 public void checkCursor() throws SQLException { 00107 if (bc == null) { 00108 throw new SQLException("Cursor closed"); 00109 } 00110 } 00111 00112 public long getCurrentRowId() throws SQLException { 00113 checkCursor(); 00114 return ByteUtil.getLong(bc.getValBuf(), 0); 00115 } 00116 00117 public void fetchCurrentRow() throws SQLException, IOException { 00118 session.getDatabase().getRow(rowId, row, false); 00119 rowValid = true; 00120 } 00121 00122 public IndexConstraint getConstraint() { 00123 return constraint; 00124 } 00125 00126 public void reset(Expression where, Cursor outer) 00127 throws SQLException 00128 { 00129 try { 00130 this.setOuterCursor(outer); 00131 startVal = null; 00132 stopVal = null; 00133 if (indexNames != null && indexNames.size() > 0) { 00134 startVals = makeValues(session, table, ValueNull.valueNull); 00135 stopVals = makeValues(session, table, ValueNull.valueLastNull); 00136 getCursorRange(session, where, startVals, stopVals); 00137 startVal = constraint.makeKey(session, startVals.getRow(), 00138 Long.MIN_VALUE); 00139 stopVal = constraint.makeKey(session, stopVals.getRow(), 00140 Long.MAX_VALUE); 00141 //Debug.println("startVal = " + Util.hexBytes(startVal)); 00142 //Debug.println("stopVal = " + Util.hexBytes(stopVal)); 00143 } 00144 00145 this.row = new LazyRow(table.getColumnCount()); 00146 bc = index.getCursor(false); 00147 if (startVal != null) { 00148 bc.seek(startVal); 00149 } else { 00150 bc.beforeFirst(); 00151 } 00152 } catch (IOException e) { 00153 throw DbException.wrapThrowable(e); 00154 } 00155 } 00156 00157 /** 00158 * Make a row for the specified table and fill it with the specified 00159 * value 00160 */ 00161 StaticCursor makeValues(Session session, Table table, Value v) 00162 throws SQLException 00163 { 00164 Row r = new Row(table.getColumnCount()); 00165 for (int i = 1; i <= r.size(); i++) { 00166 r.set(i, v); 00167 } 00168 StaticCursor s = new StaticCursor(session, table, r); 00169 s.absolute(1); 00170 return s; 00171 } 00172 00173 //#ifdef DEBUG 00174 static String isa(Object x) { 00175 return x == null ? "null" : 00176 x.getClass().getName() + ":" + x; 00177 } 00178 //#endif 00179 00180 /** 00181 * Determine if an expression in the where clause can supply a lower 00182 * or upper bound on the value of the index key 00183 */ 00184 void getCursorRange(Session session, Expression e, 00185 StaticCursor startVals, StaticCursor stopVals) 00186 throws SQLException 00187 { 00188 if (e instanceof BinaryExpression) { 00189 BinaryExpression b = (BinaryExpression)e; 00190 switch (b.op) { 00191 case Op.EQ: 00192 //Debug.println("b.e = " + b.e); 00193 //Debug.println("b.f = " + b.f); 00194 //Debug.println("startVals = " + startVals); 00195 if (b.e instanceof NameExpression) { 00196 NameExpression ne = (NameExpression)b.e; 00197 String name = session.getConnection().resolveColname(ne.getName(), table); 00198 String tname = table.getName(); 00199 if (name.startsWith(tname)) name = name.substring(tname.length() + 1); 00200 //Debug.println(" name = " + name + ", tablename = " + table.getName()); 00201 if (startVals.get(name) != null) { 00202 try { 00203 Value v = b.f.getValue(session, outer); 00204 //Debug.println(" v = " + v); 00205 startVals.put(name, v); 00206 stopVals.put(name, v); 00207 return; 00208 } catch (SQLException se) {} 00209 } 00210 } 00211 if (b.f instanceof NameExpression) { 00212 NameExpression nf = (NameExpression)b.f; 00213 String name = session.getConnection().resolveColname(nf.getName(), table); 00214 if (startVals.get(name) != null) { 00215 try { 00216 Value v = b.e.getValue(session, outer); 00217 startVals.put(name, v); 00218 stopVals.put(name, v); 00219 } catch (SQLException se) {} 00220 } 00221 } 00222 break; 00223 case Op.AND: 00224 getCursorRange(session, b.e, startVals, stopVals); 00225 getCursorRange(session, b.f, startVals, stopVals); 00226 break; 00227 case Op.LT: 00228 case Op.LE: 00229 doOneBound(session, b.e, b.f, startVals, stopVals); 00230 break; 00231 case Op.GT: 00232 case Op.GE: 00233 doOneBound(session, b.f, b.e, startVals, stopVals); 00234 break; 00235 } 00236 } else if (e instanceof TernaryExpression) { 00237 TernaryExpression t = (TernaryExpression)e; 00238 if (t.op == Op.BETWEEN) { 00239 if (!t.not) { 00240 doOneBound(session, t.f, t.e, startVals, stopVals); 00241 doOneBound(session, t.e, t.g, startVals, stopVals); 00242 } 00243 } 00244 } 00245 } 00246 00247 /** 00248 * Compare a single element to a bound to determine a possible 00249 * limit 00250 */ 00251 void doOneBound(Session session, Expression a, Expression b, 00252 StaticCursor startVals, StaticCursor stopVals) 00253 throws SQLException 00254 { 00255 if (a instanceof NameExpression) { 00256 NameExpression na = (NameExpression)a; 00257 String name = na.getName(); 00258 Value s = (Value)stopVals.get(name); 00259 if (s != null) { 00260 Value v = b.getValue(session, null); // XXX outer? 00261 Value cmp = Value.binop(Op.COMPARE, v, s); 00262 ValueInteger vi = (ValueInteger)cmp; 00263 int ret = vi.intValue(); 00264 if (ret < 0) { 00265 stopVals.put(name, v); 00266 } 00267 } 00268 } else if (b instanceof NameExpression) { 00269 NameExpression nb = (NameExpression)b; 00270 String name = nb.getName(); 00271 Value s = (Value)startVals.get(name); 00272 if (s != null) { 00273 Value v = a.getValue(session, null); // XXX outer? 00274 Value cmp = Value.binop(Op.COMPARE, v, s); 00275 ValueInteger vi = (ValueInteger)cmp; 00276 int ret = vi.intValue(); 00277 if (ret > 0) { 00278 startVals.put(name, v); 00279 } 00280 } 00281 } 00282 } 00283 00284 /** 00285 * Cursor.next(): 00286 */ 00287 public boolean next() throws SQLException { 00288 boolean ret = super.next(); 00289 if (ret) { 00290 if (stopVal != null) { 00291 if (compare.compare(bc.getKeyBuf(), 0, bc.getKeyLen(), 00292 stopVal, 0, stopVal.length) > 0) { 00293 ret = false; 00294 } 00295 } 00296 } 00297 return ret; 00298 } 00299 00300 /** 00301 * Cursor.prev() 00302 */ 00303 public boolean prev() throws SQLException { 00304 boolean ret = super.prev(); 00305 if (ret) { 00306 if (startVal != null) { 00307 if (compare.compare(bc.getKeyBuf(), 0, bc.getKeyLen(), 00308 startVal, 0, startVal.length) < 0) { 00309 ret = false; 00310 } 00311 } 00312 } 00313 return ret; 00314 } 00315 00316 /** 00317 * Update the current row with the specified new values 00318 */ 00319 public void updateRow(Row row) throws SQLException { 00320 try { 00321 TableOps.updateRow(session, table, 00322 bc.getKey(), rowId, 00323 row, constraint); 00324 } catch (IOException e) { 00325 throw DbException.wrapThrowable(e); 00326 } 00327 } 00328 00329 /** 00330 * Insert the specified row into the table referenced by this cursor 00331 */ 00332 public void insertRow(Row row) throws SQLException { 00333 try { 00334 TableOps.insertRow(session, table, row); 00335 } catch (IOException e) { 00336 throw DbException.wrapThrowable(e); 00337 } 00338 } 00339 00340 /** 00341 * Delete the current cursor row 00342 */ 00343 public void deleteRow() throws SQLException { 00344 try { 00345 TableOps.deleteRow(session, table, 00346 rowId, getRow(), constraint); 00347 } catch (IOException e) { 00348 throw DbException.wrapThrowable(e); 00349 } 00350 } 00351 00352 /** 00353 * Move the cursor to the beginning; before the first row 00354 */ 00355 public void beforeFirst() throws SQLException { 00356 try { 00357 rowValid = false; 00358 rowId = 0; 00359 checkCursor(); 00360 if (startVal != null) bc.seek(startVal); 00361 else bc.beforeFirst(); 00362 } catch (IOException e) { 00363 throw DbException.wrapThrowable(e); 00364 } 00365 } 00366 00367 /** 00368 * Move the cursor to the beginning; before the first row 00369 */ 00370 public void afterLast() throws SQLException { 00371 try { 00372 rowValid = false; 00373 rowId = 0; 00374 checkCursor(); 00375 if (stopVal != null) bc.seek(stopVal); 00376 else bc.afterLast(); 00377 } catch (IOException e) { 00378 throw DbException.wrapThrowable(e); 00379 } 00380 } 00381 00382 // /** 00383 // * Absolute cursor positioning 00384 // */ 00385 // public boolean absolute(int row) throws SQLException { 00386 // return ((CursorImpl)this).absolute(row); 00387 // } 00388 00389 /** 00390 * All columns in an index cursor are writable. 00391 */ 00392 public boolean isWritable(int col) { 00393 return true; 00394 } 00395 00396 /** 00397 * Return the underlying table for this cursor. Apparently, some poor 00398 * cursors don't have tables! Can you imagine! 00399 */ 00400 public Table getTable() { return table; } 00401 00402 //#ifdef DEBUG 00403 public String toString() { 00404 try { 00405 StringBuffer sb = 00406 new StringBuffer(Table.strip(getClass().getName())); 00407 sb.append(": "); 00408 sb.append(constraint.getName()); 00409 if (outer != null) { 00410 sb.append(" (outer "); 00411 sb.append(outer.toString()); // Table.strip(outer.getClass().getName())); 00412 sb.append(")"); 00413 } 00414 sb.append(" {"); 00415 for (int i = 1; i <= getColumnCount(); i++) { 00416 Column c = getColumn(i); 00417 if (i > 1) sb.append(','); 00418 sb.append(c.getName()); 00419 } 00420 sb.append('}'); 00421 return sb.toString(); 00422 } catch (Exception e) { 00423 Debug.print(e); 00424 return this.getClass().getName(); 00425 } 00426 } 00427 //#endif 00428 }