KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > quadcap > sql > InExpression


1 package com.quadcap.sql;
2
3 /* Copyright 1999 - 2003 Quadcap Software. All rights reserved.
4  *
5  * This software is distributed under the Quadcap Free Software License.
6  * This software may be used or modified for any purpose, personal or
7  * commercial. Open Source redistributions are permitted. Commercial
8  * redistribution of larger works derived from, or works which bundle
9  * this software requires a "Commercial Redistribution License"; see
10  * http://www.quadcap.com/purchase.
11  *
12  * Redistributions qualify as "Open Source" under one of the following terms:
13  *
14  * Redistributions are made at no charge beyond the reasonable cost of
15  * materials and delivery.
16  *
17  * Redistributions are accompanied by a copy of the Source Code or by an
18  * irrevocable offer to provide a copy of the Source Code for up to three
19  * years at the cost of materials and delivery. Such redistributions
20  * must allow further use, modification, and redistribution of the Source
21  * Code under substantially the same terms as this license.
22  *
23  * Redistributions of source code must retain the copyright notices as they
24  * appear in each source code file, these license terms, and the
25  * disclaimer/limitation of liability set forth as paragraph 6 below.
26  *
27  * Redistributions in binary form must reproduce this Copyright Notice,
28  * these license terms, and the disclaimer/limitation of liability set
29  * forth as paragraph 6 below, in the documentation and/or other materials
30  * provided with the distribution.
31  *
32  * The Software is provided on an "AS IS" basis. No warranty is
33  * provided that the Software is free of defects, or fit for a
34  * particular purpose.
35  *
36  * Limitation of Liability. Quadcap Software shall not be liable
37  * for any damages suffered by the Licensee or any third party resulting
38  * from use of the Software.
39  */

40
41 import java.io.Externalizable JavaDoc;
42 import java.io.IOException JavaDoc;
43 import java.io.ObjectInput JavaDoc;
44 import java.io.ObjectOutput JavaDoc;
45
46 import java.util.Enumeration JavaDoc;
47 import java.util.Vector JavaDoc;
48
49 import java.sql.SQLException JavaDoc;
50
51 import com.quadcap.sql.index.Btree;
52
53 import com.quadcap.sql.types.Op;
54 import com.quadcap.sql.types.Type;
55 import com.quadcap.sql.types.TypeBoolean;
56 import com.quadcap.sql.types.Value;
57 import com.quadcap.sql.types.ValueBoolean;
58
59 import com.quadcap.util.Debug;
60 import com.quadcap.util.Util;
61
62 /**
63  * Expression implementing <b>IN </b><i>(list)</i>.
64  *
65  * @author Stan Bailes
66  */

67 public class InExpression extends Expression implements Externalizable JavaDoc {
68     Expression e = null;
69     Expression f = null;
70     boolean not = false;
71
72     /**
73      * Private class to maintain per-session state for this expression
74      */

75     class InSessionState implements StatementContext {
76         boolean correlatedSubquery = true;
77         Session session;
78         Btree index = null;
79         byte[] aByte = new byte[1];
80
81         /**
82          * Construct "IN" session state
83          */

84         public InSessionState(Session session) {
85             this.session = session;
86         }
87
88         public boolean initialized() {
89             return index != null;
90         }
91
92         /**
93          * Initialize "IN" session state from RHS cursor.
94          */

95         void init(Expression f, Cursor cursor) throws SQLException JavaDoc {
96             // Ok, there's probably a better way to do this, but the
97
// idea is that if this is a correlated subquery, then
98
// attempting to evaluate the query without reference to
99
// the outer cursor will fail (by throwing an exception)
100
Cursor d = f.getCursor(session, null);
101             try {
102                 d.beforeFirst();
103                 d.next();
104                 Type eType = e.getType(session, cursor);
105                 Type fType = d.getColumn(1).getType();
106                 if (eType.toString().equals(fType.toString())) {
107                     correlatedSubquery = false;
108                 } else {
109                     // not really a correlated query, per se, but if the types
110
// don't match, our index-based optimization doesn't work.
111
// XXX (Though we could fix this by performing the appropriate
112
// XXX conversion when building the index...)
113
correlatedSubquery = true;
114                 }
115             } catch (Throwable JavaDoc t) {
116                 correlatedSubquery = true;
117             } finally {
118                 d.close();
119             }
120             if (!correlatedSubquery) {
121                 Cursor c = f.getCursor(session, cursor);
122                 try {
123                     this.index = session.makeTempTree();
124                     c.beforeFirst();
125                     if (c.getColumnCount() != 1) {
126                         throw new SQLException JavaDoc("'IN' comparator: Rank mismatch",
127                                                "42000");
128                     }
129                     while (c.next()) {
130                         Row fcrow = c.getRow();
131                         Value fval = fcrow.item(1);
132                         if (!Value.isNull(fval)) {
133                             byte[] key = Value.bytes(fval);
134                             index.set(key, key.length, aByte, 0, 1);
135                         }
136                     }
137                 } catch (IOException JavaDoc ex) {
138                     throw DbException.wrapThrowable(ex);
139                 } finally {
140                     c.close();
141                 }
142             }
143         }
144
145         /**
146          * Initialize "IN" session state from RHS row.
147          */

148         void init(Row r) throws SQLException JavaDoc {
149             try {
150                 this.index = session.makeTempTree();
151                 for (int i = 1; i <= r.size(); i++) {
152                     Value fval = r.item(i);
153                     byte[] key = Value.bytes(fval);
154                     index.set(key, key.length, aByte, 0, 1);
155                 }
156             } catch (IOException JavaDoc ex) {
157                 throw DbException.wrapThrowable(ex);
158             }
159         }
160
161         /** Whenever you get around to finishing me is fine. */
162         public int priority() { return 4; }
163
164         /** Clean up any resources held by this context */
165         public void finish(boolean abort) throws IOException JavaDoc {
166             try {
167                 if (index != null) {
168                     index.free();
169                 }
170             } finally {
171                 if (index != null) session.getDatabase().releaseTempFile();
172                 index = null;
173             }
174         }
175
176         /** Get ready for another day */
177         public void reset() throws IOException JavaDoc {
178             finish(false);
179         }
180
181         /** Does RHS set contain this value? */
182         public boolean contains(Value eval, Expression f, Cursor cursor) throws SQLException JavaDoc {
183             if (!correlatedSubquery) {
184                 byte[] ekey = Value.bytes(eval);
185                 try {
186                     return index.get(ekey, ekey.length, aByte) != -1;
187                 } catch (IOException JavaDoc ex) {
188                     throw DbException.wrapThrowable(ex);
189                 }
190             } else {
191                 return matchCorrelated(f, cursor, eval);
192             }
193         }
194
195         /** Correlated sub-queries can't be short-circuited, alas. */
196         boolean matchCorrelated(Expression f, Cursor cursor, Value eval)
197             throws SQLException JavaDoc
198         {
199             boolean match = false;
200             Cursor c = f.getCursor(session, cursor);
201             try {
202                 c.beforeFirst();
203                 if (c.getColumnCount() != 1) {
204                     throw new SQLException JavaDoc("'IN' comparator: Rank mismatch",
205                                            "42000");
206                 }
207                 while (!match && c.next()) {
208                     Row fcrow = c.getRow();
209                     Value fval = fcrow.item(1);
210                     match = Value.boolOp(Op.EQ, eval, fval);
211                 }
212             } finally {
213                 c.close();
214             }
215             return match;
216         }
217         
218     }
219
220     private InSessionState getSessionState(Session session) {
221         InSessionState s = (InSessionState)session.getContext(this, false);
222         if (s == null) {
223             s = new InSessionState(session);
224             session.putContext(this, false, s);
225         }
226         return s;
227     }
228
229     /**
230      * Default constructor
231      */

232     public InExpression() {}
233
234     /**
235      * Explicit constructor, called by SQL parser.
236      */

237     public InExpression(Expression e, Expression f) {
238     this.e = e;
239     this.f = f;
240     }
241
242     /**
243      * "IN" always returns a scalar value.
244      */

245     public int rank() { return 0; }
246
247     /**
248      * "IN" always returns a scalar value.
249      */

250     public Type getType(Session session, Cursor cursor) {
251         return TypeBoolean.typeBoolean;
252     }
253
254     /**
255      * Implementation of IN varies based on the rank of the LHS
256      */

257     public Value getValue(Session session, Cursor cursor) throws SQLException JavaDoc {
258     switch (e.rank()) {
259     case 0:
260         return getValue1(session, cursor);
261     case 1:
262         return getValue2(session, cursor);
263     default:
264         throw new SQLException JavaDoc("bad rank (" + e.rank() +
265                    ") for left argument to IN", "42000");
266     }
267     }
268
269     private Value getValue1(Session session, Cursor cursor)
270         throws SQLException JavaDoc
271     {
272     Value eval = e.getValue(session, cursor);
273     boolean match = false;
274     switch (f.rank()) {
275     case 0:
276         throw new SQLException JavaDoc("'IN' comparator: Rank mismatch", "42000");
277     case 1:
278         Row frow = f.getValues(session, cursor);
279         for (int i = 1; !match && i <= frow.size(); i++) {
280         Value fval = frow.item(i);
281         match = Value.boolOp(Op.EQ, eval, fval);
282         }
283         break;
284     case 2:
285             InSessionState s = getSessionState(session);
286             if (!s.initialized()) {
287                 s.init(f, cursor);
288             }
289             match = s.contains(eval, f, cursor);
290         break;
291     default:
292         throw new SQLException JavaDoc("internal error, bad rank: " + f.rank(),
293                    "Q0004");
294     }
295     match ^= not;
296     return new ValueBoolean(match);
297     }
298
299     
300     private Value getValue2(Session session, Cursor cursor)
301         throws SQLException JavaDoc
302     {
303     Row erow = e.getValues(session, cursor);
304     boolean match = false;
305     switch (f.rank()) {
306     case 0:
307     case 1:
308         throw new SQLException JavaDoc("'IN' comparator: Rank mismatch", "42000");
309     case 2:
310         Cursor c = f.getCursor(session, cursor);
311         try {
312         if (c.getColumnCount() != cursor.getColumnCount()) {
313             throw new SQLException JavaDoc("'IN' comparator: Rank mismatch",
314                        "42000");
315         }
316         while (!match && c.next()) {
317             Row frow = c.getRow();
318             match = matchRow(erow, frow);
319         }
320         } finally {
321         c.close();
322         }
323         break;
324     default:
325         throw new SQLException JavaDoc("internal error, bad rank: " + f.rank(),
326                    "Q0005");
327     }
328     match ^= not;
329     return new ValueBoolean(match);
330     }
331
332     static boolean matchRow(Row erow, Row frow) throws SQLException JavaDoc {
333     boolean match = true;
334     for (int i = 1; match && i <= erow.size(); i++) {
335         match = Value.boolOp(Op.EQ, erow.item(i), frow.item(i));
336     }
337     return match;
338     }
339     
340     public void invert() {
341     not = !not;
342     }
343
344     public String JavaDoc toString() {
345     String JavaDoc n = not ? "not " : "";
346     return "(" + n + e + " IN " + f + ")";
347     }
348
349     public Expression getLhs() { return e; }
350     
351     public void visitSubExpressions(ExpressionVisitor ev) {
352     ev.visit(e);
353     ev.visit(f);
354     }
355
356     public void readExternal(ObjectInput JavaDoc in)
357     throws IOException JavaDoc, ClassNotFoundException JavaDoc
358     {
359     e = (Expression)in.readObject();
360     f = (Expression)in.readObject();
361     not = (in.read() == 1);
362     }
363     
364     public void writeExternal(ObjectOutput JavaDoc out) throws IOException JavaDoc {
365     out.writeObject(e);
366     out.writeObject(f);
367     out.write(not ? 1 : 0);
368     }
369 }
370
Popular Tags