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.ByteArrayInputStream;
042: import java.io.Externalizable;
043: import java.io.IOException;
044: import java.io.ObjectInput;
045: import java.io.ObjectOutput;
046:
047: import java.util.Vector;
048:
049: import java.sql.SQLException;
050:
051: import com.quadcap.sql.io.ObjectInputStream;
052: import com.quadcap.sql.io.ObjectOutputStream;
053:
054: import com.quadcap.sql.file.ByteUtil;
055:
056: import com.quadcap.sql.index.BCursor;
057: import com.quadcap.sql.index.Btree;
058:
059: import com.quadcap.sql.types.Op;
060: import com.quadcap.sql.types.Value;
061:
062: import com.quadcap.util.Debug;
063: import com.quadcap.util.Util;
064:
065: /**
066: * Cursor implementing the SQL <b>UNION</b> or <b>INTERSECTION</b> operations.
067: * This cursor shares some functionality with the GroupByCursor class,
068: * and factorization should be investigated.
069: *
070: * @author Stan Bailes
071: */
072: public class MergeCursor extends CursorImpl {
073: int op;
074: boolean all;
075: TempTableMerge tempTable;
076: LazyRow row = null;
077: BCursor bc = null;
078: int duplicateCount = 0;
079:
080: public MergeCursor(Session session, int op, boolean all,
081: TempTableMerge tempTable) throws SQLException {
082: super (session, null, null);
083: this .op = op;
084: this .all = all;
085: this .tempTable = tempTable;
086: }
087:
088: public Row getRow() throws SQLException {
089: return row;
090: }
091:
092: public void checkCursor() throws IOException {
093: if (bc == null) {
094: bc = tempTable.getCursor();
095: }
096: }
097:
098: public long getRowId() {
099: byte[] val = bc.getValBuf();
100: return val[0] == 1 ? 0 : ByteUtil.getLong(val, 1);
101: }
102:
103: public void resolveColumns() throws SQLException {
104: super .resolveColumns();
105: row = new LazyRow(getColumnCount());
106: beforeFirst();
107: }
108:
109: public String getName() {
110: return "";
111: }
112:
113: public long size() throws SQLException {
114: try {
115: checkCursor();
116: return bc.size();
117: } catch (IOException e) {
118: throw DbException.wrapThrowable(e);
119: }
120: }
121:
122: public void afterLast() throws SQLException {
123: throw new SQLException(
124: "afterLast() not supported for this cursor type");
125: }
126:
127: public boolean isWritable(int col) {
128: return false;
129: }
130:
131: public boolean next() throws SQLException {
132: try {
133: checkCursor();
134: switch (op) {
135: case Op.UNION:
136: return all ? nextUnionAll() : nextUnion();
137: case Op.INTERSECT:
138: return nextIntersect();
139: case Op.EXCEPT:
140: return nextExcept();
141: default:
142: throw new SQLException("Bad merge op: " + op, "42000");
143: }
144: } catch (IOException e) {
145: throw DbException.wrapThrowable(e);
146: }
147: }
148:
149: public void updateRow(Row row) throws SQLException {
150: throw new SQLException("GroupBy expressions aren't updateable",
151: "42000");
152: }
153:
154: public void deleteRow() throws SQLException {
155: throw new SQLException("GroupBy expressions aren't updateable",
156: "42000");
157: }
158:
159: public void beforeFirst() throws SQLException {
160: try {
161: checkCursor();
162: bc.beforeFirst();
163: } catch (IOException e) {
164: throw DbException.wrapThrowable(e);
165: } finally {
166: duplicateCount = 0;
167: }
168: }
169:
170: /**
171: * Separate from 'nextUnionAll' because we can avoid fetching the
172: * data for this case.
173: */
174: boolean nextUnion() throws SQLException, IOException {
175: if (!bc.next())
176: return false;
177: tempTable.getRow(bc.getValBuf(), row);
178: return true;
179: }
180:
181: /**
182: * Fetch the data once when we fetch the counts, then return each
183: * row the required number of times
184: */
185: boolean nextUnionAll() throws SQLException, IOException {
186: if (duplicateCount-- <= 0) {
187: if (!bc.next())
188: return false;
189: tempTable.getRow(bc.getValBuf(), row);
190: byte[] data = bc.getValBuf();
191: int acnt = TempTableMerge.getCount(data, 0);
192: int bcnt = TempTableMerge.getCount(data, 1);
193: duplicateCount = acnt + bcnt - 1;
194: return true;
195: }
196: return true;
197: }
198:
199: boolean nextIntersect() throws SQLException, IOException {
200: if (!all || duplicateCount-- <= 0) {
201: boolean found = false;
202: do {
203: if (!bc.next())
204: return false;
205: byte[] data = bc.getValBuf();
206: int acnt = TempTableMerge.getCount(data, 0);
207: int bcnt = TempTableMerge.getCount(data, 1);
208: duplicateCount = all ? Math.min(acnt, bcnt)
209: : (acnt > 0 && bcnt > 0) ? 1 : 0;
210: if (duplicateCount-- > 0) {
211: tempTable.getRow(data, row);
212: found = true;
213: }
214: } while (!found);
215: return found;
216: }
217: return true;
218: }
219:
220: boolean nextExcept() throws SQLException, IOException {
221: if (!all || duplicateCount-- <= 0) {
222: boolean found = false;
223: do {
224: if (!bc.next())
225: return false;
226: byte[] data = bc.getValBuf();
227: int acnt = TempTableMerge.getCount(data, 0);
228: int bcnt = TempTableMerge.getCount(data, 1);
229: duplicateCount = all ? (acnt - bcnt)
230: : (acnt > 0 && bcnt == 0) ? 1 : 0;
231: if (duplicateCount-- > 0) {
232: tempTable.getRow(data, row);
233: found = true;
234: }
235: } while (!found);
236: return found;
237: }
238: return true;
239: }
240:
241: /**
242: * On close, we release the resources.
243: */
244: public void close() throws SQLException {
245: try {
246: if (bc != null)
247: bc.release();
248: } finally {
249: bc = null;
250: try {
251: if (tempTable != null)
252: tempTable.release();
253: } catch (IOException e2) {
254: throw DbException.wrapThrowable(e2);
255: } finally {
256: tempTable = null;
257: }
258: }
259: }
260: }
|