00001
package com.quadcap.sql;
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
import java.io.Externalizable;
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.Vector;
00048
00049
import java.sql.SQLException;
00050
00051
import com.quadcap.sql.index.Btree;
00052
00053
import com.quadcap.sql.types.Op;
00054
import com.quadcap.sql.types.Type;
00055
import com.quadcap.sql.types.TypeBoolean;
00056
import com.quadcap.sql.types.Value;
00057
import com.quadcap.sql.types.ValueBoolean;
00058
00059
import com.quadcap.util.Debug;
00060
import com.quadcap.util.Util;
00061
00062
00063
00064
00065
00066
00067 public class InExpression extends Expression implements
Externalizable {
00068 Expression e = null;
00069 Expression f = null;
00070 boolean not =
false;
00071
00072
00073
00074
00075 class InSessionState implements StatementContext {
00076 boolean correlatedSubquery =
true;
00077 Session session;
00078 Btree
index = null;
00079 byte[]
aByte =
new byte[1];
00080
00081
00082
00083
00084 public InSessionState(
Session session) {
00085
this.session = session;
00086 }
00087
00088 public boolean initialized() {
00089
return index != null;
00090 }
00091
00092
00093
00094
00095 void init(
Expression f,
Cursor cursor)
throws SQLException {
00096
00097
00098
00099
00100
Cursor d = f.getCursor(
session, null);
00101
try {
00102 d.
beforeFirst();
00103 d.
next();
00104
Type eType = e.getType(
session, cursor);
00105
Type fType = d.
getColumn(1).
getType();
00106
if (eType.toString().equals(fType.toString())) {
00107
correlatedSubquery =
false;
00108 }
else {
00109
00110
00111
00112
00113
correlatedSubquery =
true;
00114 }
00115 }
catch (Throwable t) {
00116
correlatedSubquery =
true;
00117 } finally {
00118 d.
close();
00119 }
00120
if (!
correlatedSubquery) {
00121
Cursor c = f.getCursor(
session, cursor);
00122
try {
00123
this.index =
session.
makeTempTree();
00124 c.
beforeFirst();
00125
if (c.
getColumnCount() != 1) {
00126
throw new SQLException(
"'IN' comparator: Rank mismatch",
00127
"42000");
00128 }
00129
while (c.
next()) {
00130
Row fcrow = c.
getRow();
00131
Value fval = fcrow.
item(1);
00132
if (!
Value.isNull(fval)) {
00133 byte[] key =
Value.bytes(fval);
00134
index.set(key, key.length,
aByte, 0, 1);
00135 }
00136 }
00137 }
catch (IOException ex) {
00138
throw DbException.wrapThrowable(ex);
00139 } finally {
00140 c.
close();
00141 }
00142 }
00143 }
00144
00145
00146
00147
00148 void init(
Row r)
throws SQLException {
00149
try {
00150
this.index =
session.
makeTempTree();
00151
for (
int i = 1; i <= r.size(); i++) {
00152
Value fval = r.item(i);
00153 byte[] key =
Value.bytes(fval);
00154
index.set(key, key.length,
aByte, 0, 1);
00155 }
00156 }
catch (IOException ex) {
00157
throw DbException.wrapThrowable(ex);
00158 }
00159 }
00160
00161
00162 public int priority() {
return 4; }
00163
00164
00165 public void finish(
boolean abort)
throws IOException {
00166
try {
00167
if (
index != null) {
00168
index.free();
00169 }
00170 } finally {
00171
if (
index != null)
session.
getDatabase().releaseTempFile();
00172
index = null;
00173 }
00174 }
00175
00176
00177 public void reset() throws IOException {
00178 finish(
false);
00179 }
00180
00181
00182 public boolean contains(
Value eval,
Expression f,
Cursor cursor)
throws SQLException {
00183
if (!
correlatedSubquery) {
00184 byte[] ekey =
Value.bytes(eval);
00185
try {
00186
return index.get(ekey, ekey.length,
aByte) != -1;
00187 }
catch (IOException ex) {
00188
throw DbException.wrapThrowable(ex);
00189 }
00190 }
else {
00191
return matchCorrelated(f, cursor, eval);
00192 }
00193 }
00194
00195
00196 boolean matchCorrelated(
Expression f,
Cursor cursor,
Value eval)
00197
throws SQLException
00198 {
00199
boolean match =
false;
00200
Cursor c = f.getCursor(
session, cursor);
00201
try {
00202 c.
beforeFirst();
00203
if (c.
getColumnCount() != 1) {
00204
throw new SQLException(
"'IN' comparator: Rank mismatch",
00205
"42000");
00206 }
00207
while (!match && c.
next()) {
00208
Row fcrow = c.
getRow();
00209
Value fval = fcrow.
item(1);
00210 match =
Value.boolOp(
Op.EQ, eval, fval);
00211 }
00212 } finally {
00213 c.
close();
00214 }
00215
return match;
00216 }
00217
00218 }
00219
00220 private InSessionState getSessionState(
Session session) {
00221
InSessionState s = (
InSessionState)session.
getContext(
this,
false);
00222
if (s == null) {
00223 s =
new InSessionState(session);
00224 session.
putContext(
this,
false, s);
00225 }
00226
return s;
00227 }
00228
00229
00230
00231
00232 public InExpression() {}
00233
00234
00235
00236
00237 public InExpression(
Expression e,
Expression f) {
00238
this.e = e;
00239
this.f = f;
00240 }
00241
00242
00243
00244
00245 public int rank() {
return 0; }
00246
00247
00248
00249
00250 public Type getType(
Session session,
Cursor cursor) {
00251
return TypeBoolean.typeBoolean;
00252 }
00253
00254
00255
00256
00257 public Value getValue(
Session session,
Cursor cursor)
throws SQLException {
00258
switch (
e.
rank()) {
00259
case 0:
00260
return getValue1(session, cursor);
00261
case 1:
00262
return getValue2(session, cursor);
00263
default:
00264
throw new SQLException(
"bad rank (" +
e.
rank() +
00265
") for left argument to IN",
"42000");
00266 }
00267 }
00268
00269 private Value getValue1(
Session session,
Cursor cursor)
00270
throws SQLException
00271 {
00272
Value eval =
e.
getValue(session, cursor);
00273
boolean match =
false;
00274
switch (
f.
rank()) {
00275
case 0:
00276
throw new SQLException(
"'IN' comparator: Rank mismatch",
"42000");
00277
case 1:
00278
Row frow =
f.
getValues(session, cursor);
00279
for (
int i = 1; !match && i <= frow.
size(); i++) {
00280
Value fval = frow.
item(i);
00281 match =
Value.boolOp(
Op.EQ, eval, fval);
00282 }
00283
break;
00284
case 2:
00285
InSessionState s = getSessionState(session);
00286
if (!s.
initialized()) {
00287 s.
init(
f, cursor);
00288 }
00289 match = s.
contains(eval,
f, cursor);
00290
break;
00291
default:
00292
throw new SQLException(
"internal error, bad rank: " +
f.
rank(),
00293
"Q0004");
00294 }
00295 match ^=
not;
00296
return new ValueBoolean(match);
00297 }
00298
00299
00300 private Value getValue2(
Session session,
Cursor cursor)
00301
throws SQLException
00302 {
00303
Row erow =
e.
getValues(session, cursor);
00304
boolean match =
false;
00305
switch (
f.
rank()) {
00306
case 0:
00307
case 1:
00308
throw new SQLException(
"'IN' comparator: Rank mismatch",
"42000");
00309
case 2:
00310
Cursor c =
f.
getCursor(session, cursor);
00311
try {
00312
if (c.
getColumnCount() != cursor.getColumnCount()) {
00313
throw new SQLException(
"'IN' comparator: Rank mismatch",
00314
"42000");
00315 }
00316
while (!match && c.
next()) {
00317
Row frow = c.
getRow();
00318 match =
matchRow(erow, frow);
00319 }
00320 } finally {
00321 c.
close();
00322 }
00323
break;
00324
default:
00325
throw new SQLException(
"internal error, bad rank: " +
f.
rank(),
00326
"Q0005");
00327 }
00328 match ^=
not;
00329
return new ValueBoolean(match);
00330 }
00331
00332 static boolean matchRow(
Row erow,
Row frow)
throws SQLException {
00333
boolean match =
true;
00334
for (
int i = 1; match && i <= erow.size(); i++) {
00335 match =
Value.boolOp(
Op.EQ, erow.item(i), frow.item(i));
00336 }
00337
return match;
00338 }
00339
00340 public void invert() {
00341
not = !
not;
00342 }
00343
00344 public String
toString() {
00345 String n =
not ?
"not " :
"";
00346
return "(" + n +
e +
" IN " +
f +
")";
00347 }
00348
00349 public Expression getLhs() {
return e; }
00350
00351 public void visitSubExpressions(
ExpressionVisitor ev) {
00352 ev.
visit(
e);
00353 ev.
visit(
f);
00354 }
00355
00356 public void readExternal(ObjectInput in)
00357
throws IOException, ClassNotFoundException
00358 {
00359
e = (
Expression)in.readObject();
00360
f = (
Expression)in.readObject();
00361
not = (in.read() == 1);
00362 }
00363
00364 public void writeExternal(ObjectOutput out)
throws IOException {
00365 out.writeObject(
e);
00366 out.writeObject(
f);
00367 out.write(
not ? 1 : 0);
00368 }
00369 }