001: package com.quadcap.sql;
002:
003: /* Copyright 1999 - 2003 Quadcap Software. All rights reserved.
004: *
005: * This software is distributed under the Quadcap Free Software License.
006: * This software may be used or modified for any purpose, personal or
007: * commercial. Open Source redistributions are permitted. Commercial
008: * redistribution of larger works derived from, or works which bundle
009: * this software requires a "Commercial Redistribution License"; see
010: * http://www.quadcap.com/purchase.
011: *
012: * Redistributions qualify as "Open Source" under one of the following terms:
013: *
014: * Redistributions are made at no charge beyond the reasonable cost of
015: * materials and delivery.
016: *
017: * Redistributions are accompanied by a copy of the Source Code or by an
018: * irrevocable offer to provide a copy of the Source Code for up to three
019: * years at the cost of materials and delivery. Such redistributions
020: * must allow further use, modification, and redistribution of the Source
021: * Code under substantially the same terms as this license.
022: *
023: * Redistributions of source code must retain the copyright notices as they
024: * appear in each source code file, these license terms, and the
025: * disclaimer/limitation of liability set forth as paragraph 6 below.
026: *
027: * Redistributions in binary form must reproduce this Copyright Notice,
028: * these license terms, and the disclaimer/limitation of liability set
029: * forth as paragraph 6 below, in the documentation and/or other materials
030: * provided with the distribution.
031: *
032: * The Software is provided on an "AS IS" basis. No warranty is
033: * provided that the Software is free of defects, or fit for a
034: * particular purpose.
035: *
036: * Limitation of Liability. Quadcap Software shall not be liable
037: * for any damages suffered by the Licensee or any third party resulting
038: * from use of the Software.
039: */
040:
041: import java.io.ByteArrayOutputStream;
042: import java.io.IOException;
043: import java.io.ObjectInput;
044: import java.io.ObjectOutput;
045:
046: import java.util.Enumeration;
047: import java.util.Hashtable;
048: import java.util.Vector;
049:
050: import java.sql.SQLException;
051:
052: import com.quadcap.sql.io.ObjectOutputStream;
053:
054: import com.quadcap.sql.file.ByteUtil;
055:
056: import com.quadcap.sql.index.Btree;
057: import com.quadcap.sql.index.Comparator;
058:
059: import com.quadcap.sql.types.Op;
060: import com.quadcap.sql.types.Value;
061: import com.quadcap.sql.types.ValueException;
062: import com.quadcap.sql.types.ValueInteger;
063: import com.quadcap.sql.types.ValueNull;
064:
065: import com.quadcap.util.Debug;
066: import com.quadcap.util.Util;
067:
068: /**
069: * Cursor for iterating an index.
070: *
071: * @author Stan Bailes
072: */
073: public class IndexCursor extends BC_Cursor {
074: Table table;
075: IndexConstraint constraint;
076: Btree index;
077: Comparator compare;
078: Vector indexNames = null;
079: StaticCursor startVals = null;
080: StaticCursor stopVals = null;
081:
082: byte[] startVal;
083: byte[] stopVal;
084:
085: /**
086: * IndexCursor construtor for the specified table (with optional
087: * where clause)
088: */
089: public IndexCursor(Table table, Session session,
090: IndexConstraint constraint, Expression where,
091: String qualifier, Cursor outerCursor) throws SQLException,
092: IOException {
093: super (session, qualifier, outerCursor);
094: this .table = table;
095: this .constraint = constraint;
096: this .index = constraint.getIndex(session.getDatabase());
097: this .compare = index.getComparator();
098:
099: addColumns(session, table);
100:
101: this .indexNames = constraint.getColumnNames();
102: reset(where, outerCursor);
103: }
104:
105: public void checkCursor() throws SQLException {
106: if (bc == null) {
107: throw new SQLException("Cursor closed");
108: }
109: }
110:
111: public long getCurrentRowId() throws SQLException {
112: checkCursor();
113: return ByteUtil.getLong(bc.getValBuf(), 0);
114: }
115:
116: public void fetchCurrentRow() throws SQLException, IOException {
117: session.getDatabase().getRow(rowId, row, false);
118: rowValid = true;
119: }
120:
121: public IndexConstraint getConstraint() {
122: return constraint;
123: }
124:
125: public void reset(Expression where, Cursor outer)
126: throws SQLException {
127: try {
128: this .setOuterCursor(outer);
129: startVal = null;
130: stopVal = null;
131: if (indexNames != null && indexNames.size() > 0) {
132: startVals = makeValues(session, table,
133: ValueNull.valueNull);
134: stopVals = makeValues(session, table,
135: ValueNull.valueLastNull);
136: getCursorRange(session, where, startVals, stopVals);
137: startVal = constraint.makeKey(session, startVals
138: .getRow(), Long.MIN_VALUE);
139: stopVal = constraint.makeKey(session,
140: stopVals.getRow(), Long.MAX_VALUE);
141: //Debug.println("startVal = " + Util.hexBytes(startVal));
142: //Debug.println("stopVal = " + Util.hexBytes(stopVal));
143: }
144:
145: this .row = new LazyRow(table.getColumnCount());
146: bc = index.getCursor(false);
147: if (startVal != null) {
148: bc.seek(startVal);
149: } else {
150: bc.beforeFirst();
151: }
152: } catch (IOException e) {
153: throw DbException.wrapThrowable(e);
154: }
155: }
156:
157: /**
158: * Make a row for the specified table and fill it with the specified
159: * value
160: */
161: StaticCursor makeValues(Session session, Table table, Value v)
162: throws SQLException {
163: Row r = new Row(table.getColumnCount());
164: for (int i = 1; i <= r.size(); i++) {
165: r.set(i, v);
166: }
167: StaticCursor s = new StaticCursor(session, table, r);
168: s.absolute(1);
169: return s;
170: }
171:
172: //#ifdef DEBUG
173: static String isa(Object x) {
174: return x == null ? "null" : x.getClass().getName() + ":" + x;
175: }
176:
177: //#endif
178:
179: /**
180: * Determine if an expression in the where clause can supply a lower
181: * or upper bound on the value of the index key
182: */
183: void getCursorRange(Session session, Expression e,
184: StaticCursor startVals, StaticCursor stopVals)
185: throws SQLException {
186: if (e instanceof BinaryExpression) {
187: BinaryExpression b = (BinaryExpression) e;
188: switch (b.op) {
189: case Op.EQ:
190: //Debug.println("b.e = " + b.e);
191: //Debug.println("b.f = " + b.f);
192: //Debug.println("startVals = " + startVals);
193: if (b.e instanceof NameExpression) {
194: NameExpression ne = (NameExpression) b.e;
195: String name = session.getConnection()
196: .resolveColname(ne.getName(), table);
197: String tname = table.getName();
198: if (name.startsWith(tname))
199: name = name.substring(tname.length() + 1);
200: //Debug.println(" name = " + name + ", tablename = " + table.getName());
201: if (startVals.get(name) != null) {
202: try {
203: Value v = b.f.getValue(session, outer);
204: //Debug.println(" v = " + v);
205: startVals.put(name, v);
206: stopVals.put(name, v);
207: return;
208: } catch (SQLException se) {
209: }
210: }
211: }
212: if (b.f instanceof NameExpression) {
213: NameExpression nf = (NameExpression) b.f;
214: String name = session.getConnection()
215: .resolveColname(nf.getName(), table);
216: if (startVals.get(name) != null) {
217: try {
218: Value v = b.e.getValue(session, outer);
219: startVals.put(name, v);
220: stopVals.put(name, v);
221: } catch (SQLException se) {
222: }
223: }
224: }
225: break;
226: case Op.AND:
227: getCursorRange(session, b.e, startVals, stopVals);
228: getCursorRange(session, b.f, startVals, stopVals);
229: break;
230: case Op.LT:
231: case Op.LE:
232: doOneBound(session, b.e, b.f, startVals, stopVals);
233: break;
234: case Op.GT:
235: case Op.GE:
236: doOneBound(session, b.f, b.e, startVals, stopVals);
237: break;
238: }
239: } else if (e instanceof TernaryExpression) {
240: TernaryExpression t = (TernaryExpression) e;
241: if (t.op == Op.BETWEEN) {
242: if (!t.not) {
243: doOneBound(session, t.f, t.e, startVals, stopVals);
244: doOneBound(session, t.e, t.g, startVals, stopVals);
245: }
246: }
247: }
248: }
249:
250: /**
251: * Compare a single element to a bound to determine a possible
252: * limit
253: */
254: void doOneBound(Session session, Expression a, Expression b,
255: StaticCursor startVals, StaticCursor stopVals)
256: throws SQLException {
257: if (a instanceof NameExpression) {
258: NameExpression na = (NameExpression) a;
259: String name = na.getName();
260: Value s = (Value) stopVals.get(name);
261: if (s != null) {
262: Value v = b.getValue(session, null); // XXX outer?
263: Value cmp = Value.binop(Op.COMPARE, v, s);
264: ValueInteger vi = (ValueInteger) cmp;
265: int ret = vi.intValue();
266: if (ret < 0) {
267: stopVals.put(name, v);
268: }
269: }
270: } else if (b instanceof NameExpression) {
271: NameExpression nb = (NameExpression) b;
272: String name = nb.getName();
273: Value s = (Value) startVals.get(name);
274: if (s != null) {
275: Value v = a.getValue(session, null); // XXX outer?
276: Value cmp = Value.binop(Op.COMPARE, v, s);
277: ValueInteger vi = (ValueInteger) cmp;
278: int ret = vi.intValue();
279: if (ret > 0) {
280: startVals.put(name, v);
281: }
282: }
283: }
284: }
285:
286: /**
287: * Cursor.next():
288: */
289: public boolean next() throws SQLException {
290: boolean ret = super .next();
291: if (ret) {
292: if (stopVal != null) {
293: if (compare.compare(bc.getKeyBuf(), 0, bc.getKeyLen(),
294: stopVal, 0, stopVal.length) > 0) {
295: ret = false;
296: }
297: }
298: }
299: return ret;
300: }
301:
302: /**
303: * Cursor.prev()
304: */
305: public boolean prev() throws SQLException {
306: boolean ret = super .prev();
307: if (ret) {
308: if (startVal != null) {
309: if (compare.compare(bc.getKeyBuf(), 0, bc.getKeyLen(),
310: startVal, 0, startVal.length) < 0) {
311: ret = false;
312: }
313: }
314: }
315: return ret;
316: }
317:
318: /**
319: * Update the current row with the specified new values
320: */
321: public void updateRow(Row row) throws SQLException {
322: try {
323: TableOps.updateRow(session, table, bc.getKey(), rowId, row,
324: constraint);
325: } catch (IOException e) {
326: throw DbException.wrapThrowable(e);
327: }
328: }
329:
330: /**
331: * Insert the specified row into the table referenced by this cursor
332: */
333: public void insertRow(Row row) throws SQLException {
334: try {
335: TableOps.insertRow(session, table, row);
336: } catch (IOException e) {
337: throw DbException.wrapThrowable(e);
338: }
339: }
340:
341: /**
342: * Delete the current cursor row
343: */
344: public void deleteRow() throws SQLException {
345: try {
346: TableOps.deleteRow(session, table, rowId, getRow(),
347: constraint);
348: } catch (IOException e) {
349: throw DbException.wrapThrowable(e);
350: }
351: }
352:
353: /**
354: * Move the cursor to the beginning; before the first row
355: */
356: public void beforeFirst() throws SQLException {
357: try {
358: rowValid = false;
359: rowId = 0;
360: checkCursor();
361: if (startVal != null)
362: bc.seek(startVal);
363: else
364: bc.beforeFirst();
365: } catch (IOException e) {
366: throw DbException.wrapThrowable(e);
367: }
368: }
369:
370: /**
371: * Move the cursor to the beginning; before the first row
372: */
373: public void afterLast() throws SQLException {
374: try {
375: rowValid = false;
376: rowId = 0;
377: checkCursor();
378: if (stopVal != null)
379: bc.seek(stopVal);
380: else
381: bc.afterLast();
382: } catch (IOException e) {
383: throw DbException.wrapThrowable(e);
384: }
385: }
386:
387: // /**
388: // * Absolute cursor positioning
389: // */
390: // public boolean absolute(int row) throws SQLException {
391: // return ((CursorImpl)this).absolute(row);
392: // }
393:
394: /**
395: * All columns in an index cursor are writable.
396: */
397: public boolean isWritable(int col) {
398: return true;
399: }
400:
401: /**
402: * Return the underlying table for this cursor. Apparently, some poor
403: * cursors don't have tables! Can you imagine!
404: */
405: public Table getTable() {
406: return table;
407: }
408:
409: //#ifdef DEBUG
410: public String toString() {
411: try {
412: StringBuffer sb = new StringBuffer(Table.strip(getClass()
413: .getName()));
414: sb.append(": ");
415: sb.append(constraint.getName());
416: if (outer != null) {
417: sb.append(" (outer ");
418: sb.append(outer.toString()); // Table.strip(outer.getClass().getName()));
419: sb.append(")");
420: }
421: sb.append(" {");
422: for (int i = 1; i <= getColumnCount(); i++) {
423: Column c = getColumn(i);
424: if (i > 1)
425: sb.append(',');
426: sb.append(c.getName());
427: }
428: sb.append('}');
429: return sb.toString();
430: } catch (Exception e) {
431: Debug.print(e);
432: return this .getClass().getName();
433: }
434: }
435: //#endif
436: }
|