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.ArrayList;
00047
import java.util.HashMap;
00048
import java.util.Iterator;
00049
import java.util.Vector;
00050
00051
import java.sql.SQLException;
00052
00053
import antlr.RecognitionException;
00054
00055
import com.quadcap.sql.types.Op;
00056
00057
import com.quadcap.util.Debug;
00058
00059
00060
00061
00062
00063
00064 public class JoinedTable extends TableExpression implements
Externalizable {
00065 int op = -1;
00066 TableExpression a = null;
00067 TableExpression b = null;
00068 Tuple tuple = null;
00069 Expression onExpression = null;
00070
00071 Vector
usingList = null;
00072
00073
00074
00075
00076 public JoinedTable() {}
00077
00078
00079
00080
00081 public JoinedTable(
int op,
TableExpression a,
TableExpression b) {
00082
this.op = op;
00083
this.a = a;
00084
this.b = b;
00085 }
00086
00087
00088
00089
00090 public void setOnExpression(
Expression on) {
00091
this.onExpression = on;
00092 }
00093
00094
00095
00096
00097 public void setUsingList(Vector usingList) {
00098
this.usingList = usingList;
00099 }
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122 public void checkSyntax() throws RecognitionException {
00123
if ((
op &
Op.NATURAL) != 0) {
00124
if (
usingList != null) {
00125
throw new RecognitionException(
"Can't specify both NATURAL and " +
00126
"USING");
00127 }
00128
if (
onExpression != null) {
00129
throw new RecognitionException(
"Can't specify both NATURAL and ON");
00130 }
00131 }
else if (
op ==
Op.LEFT ||
op ==
Op.RIGHT ||
00132
op ==
Op.FULL ||
op ==
Op.INNER) {
00133
if (
usingList == null &&
onExpression == null) {
00134
throw new RecognitionException(
Op.toString(
op) +
00135
" join requires ON or USING " +
00136
"clause");
00137 }
00138 }
else if (
op ==
Op.UNION) {
00139
if (
usingList != null) {
00140
throw new RecognitionException(
"Can't specify both UNION and USING");
00141 }
00142
if (
onExpression != null) {
00143
throw new RecognitionException(
"Can't specify both UNION and ON");
00144 }
00145 }
00146 }
00147
00148
00149
00150
00151 public void setWhere(
Expression where) {
00152
this.where = where;
00153
a.
setWhere(where);
00154
b.
setWhere(where);
00155 }
00156
00157 public int rank() {
return 2; }
00158
00159 public boolean isUpdatable() {
return false; }
00160
00161 public void getBaseTables(Vector v) {
00162
a.
getBaseTables(v);
00163
b.
getBaseTables(v);
00164 }
00165
00166 public void visitSubExpressions(
ExpressionVisitor ev) {
00167
if (
a != null) ev.
visit(
a);
00168
if (
b != null) ev.
visit(
b);
00169 }
00170
00171 String
toString(Iterator iter) {
00172 StringBuffer sb =
new StringBuffer();
00173
while (iter.hasNext()) {
00174
if (sb.length() > 0) sb.append(
", ");
00175 sb.append(iter.next().toString());
00176 }
00177
return sb.toString();
00178 }
00179
00180 static String
isa(Object x) {
00181
return x == null ?
"null" :
00182 x.getClass().getName() +
":" + x;
00183 }
00184
00185 public Cursor getCursor(
Session session,
Cursor outer)
00186
throws SQLException
00187 {
00188
int nop =
op & ~
Op.NATURAL;
00189
int[] map = null;
00190
00191
Cursor ra =
a.
getCursor(session, outer);
00192
Cursor rb =
b.
getCursor(session, outer);
00193
00194
int size = ra.
getColumnCount() + rb.
getColumnCount();
00195
00196
if ((
op &
Op.NATURAL) != 0) {
00197
getNaturalJoinColumns(ra, rb);
00198 }
00199
00200
int usize =
usingList == null ? 0 : usingList.size();
00201 map =
new int[size - usize];
00202
Tuple jtl =
mapColumns(ra, rb, map,
true);
00203
JoinMapRow jrl =
new JoinMapRow(map);
00204
00205 map =
new int[size - usize];
00206
Tuple jtr = mapColumns(ra, rb, map,
false);
00207 JoinMapRow jrr =
new JoinMapRow(map);
00208
00209
00210
Cursor ret = null;
00211
Cursor left = null;
00212
Cursor right = null;
00213
if (
onExpression != null) {
00214 where =
new BinaryExpression(
Op.AND, where,
onExpression);
00215 }
00216
00217
switch (nop) {
00218
case Op.CROSS:
00219
case Op.INNER:
00220 ret =
getCrossCursor(session, outer,
00221 ra, rb, where, jtl, jrl,
00222
false ,
00223
true );
00224
break;
00225
case Op.LEFT:
00226 ret = getCrossCursor(session, outer,
00227 ra, rb, where, jtl, jrl,
00228
true ,
00229
true );
00230
break;
00231
case Op.RIGHT:
00232 ret = getCrossCursor(session, outer,
00233 rb, ra, where, jtr, jrr,
00234
true ,
00235
true );
00236
break;
00237
case Op.FULL:
00238 left = getCrossCursor(session, outer,
00239 ra, rb, where, jtl, jrl,
00240
true ,
00241
true );
00242 right = getCrossCursor(session, outer,
00243 rb, ra, where, jtr, jrr,
00244
true ,
00245
false );
00246
break;
00247
case Op.UNION:
00248 ret =
new JoinUnionCursor(session, outer, ra, rb, where, jtl, jrl);
00249
break;
00250
default:
00251
throw new SQLException(
"Bad join type: " + nop);
00252 }
00253
if (left != null && right != null) {
00254
MultiCursor mc =
new MultiCursor(session, left);
00255 mc.
appendCursor(session, right);
00256 ret = mc;
00257 }
00258
if (ret == null) {
00259
throw new SQLException(
"Bad join");
00260 }
00261
return ret;
00262 }
00263
00264 Cursor getCrossCursor(
Session session,
Cursor outer,
Cursor ca,
Cursor cb,
00265
Expression where,
Tuple jt,
JoinMapRow jrl,
00266
boolean left,
boolean inner)
00267
throws SQLException
00268 {
00269
int[][] cols =
00270
usingList != null ?
getUsingColumns(ca, cb) :
00271
new Analyze(session, where).getJoinColumns(ca, cb);
00272
00273
Cursor ret = null;
00274
if (cols == null) {
00275
00276 ret =
new JoinCrossCursor(session, outer, ca, cb, where, jt, jrl,
00277 left, inner);
00278 }
else {
00279
00280
int[] aCols = cols[0];
00281
int[] bCols = cols[1];
00282 ret =
new JoinInnerCursor(session, outer, ca, aCols, cb, bCols,
00283 where, jt, jrl, left, inner);
00284 }
00285
return ret;
00286 }
00287
00288 int[][]
getUsingColumns(
Tuple a,
Tuple b)
throws SQLException {
00289
int[][] ret = null;
00290
if (
usingList != null) {
00291
int len =
usingList.size();
00292 ret =
new int[2][len];
00293
for (
int i = 0; i < len; i++) {
00294 String u =
usingList.get(i).toString();
00295
Column ca =
a.getColumn(u);
00296
Column cb =
b.getColumn(u);
00297 ret[0][i] = ca.
getColumn();
00298 ret[1][i] = cb.
getColumn();
00299 }
00300 }
00301
return ret;
00302 }
00303
00304 void getNaturalJoinColumns(
Tuple ta,
Tuple tb)
throws SQLException {
00305
usingList =
new Vector();
00306 HashMap t =
new HashMap();
00307
for (
int i = 1; i <= tb.getColumnCount(); i++) {
00308
Column cb = tb.getColumn(i);
00309 t.put(cb.
getShortName(), cb);
00310 }
00311
for (
int i = 1; i <= ta.getColumnCount(); i++) {
00312
Column ca = ta.getColumn(i);
00313
if (t.containsKey(ca.
getShortName())) {
00314
usingList.addElement(ca.
getShortName());
00315 }
00316 }
00317 }
00318
00319
00320
00321
00322 private final Tuple mapColumns(
Tuple ra,
Tuple rb,
int[] map,
boolean left)
00323
throws SQLException
00324 {
00325 HashMap uMap = null;
00326
TupleImpl ti =
new TupleImpl();
00327
int usize =
usingList == null ? 0 : usingList.size();
00328
00329
00330
if (usize > 0) {
00331
Tuple outer = left ? ra : rb;
00332 uMap =
new HashMap();
00333
int[] umap = outer.
mapColumns(usingList);
00334
for (
int i = 0; i < umap.length; i++) {
00335
int u = umap[i];
00336
Column ci = outer.
getColumn(u);
00337 uMap.put(ci.
getShortName(),
"");
00338
Column col =
new Column(ci.
getShortName(), ci);
00339 col.
setJoinColumn(
true);
00340 ti.
addColumn(col);
00341 map[i] = 0-u;
00342 }
00343 }
00344
00345
00346
int base = usize;
00347
for (
int i = 1; i <= ra.getColumnCount(); i++) {
00348
Column ci = ra.getColumn(i);
00349
if (uMap == null || !uMap.containsKey(ci.
getShortName())) {
00350
int c = ci.
getColumn();
00351
if (map[base] == 0) {
00352 map[base++] = left ? 0 - c : c;
00353 ti.
addColumn(ci.
getName(), ci.
getType());
00354 }
00355 }
00356 }
00357
00358
00359
for (
int i = 1; i <= rb.getColumnCount(); i++) {
00360
Column ci = rb.getColumn(i);
00361
if (uMap == null || !uMap.containsKey(ci.
getShortName())) {
00362
int c = ci.
getColumn();
00363
if (map[base] == 0) {
00364 map[base++] = left ? c : 0 - c;
00365 ti.
addColumn(ci.
getName(), ci.
getType());
00366 }
00367 }
00368 }
00369
return ti;
00370 }
00371
00372 public void readExternal(ObjectInput in)
00373
throws IOException, ClassNotFoundException
00374 {
00375
this.op = in.readInt();
00376
this.a = (
TableExpression)in.readObject();
00377
this.b = (
TableExpression)in.readObject();
00378
this.onExpression = (
Expression)in.readObject();
00379
this.usingList = (Vector)in.readObject();
00380 }
00381
00382 public void writeExternal(ObjectOutput out)
throws IOException {
00383 out.writeInt(
op);
00384 out.writeObject(
a);
00385 out.writeObject(
b);
00386 out.writeObject(
onExpression);
00387 out.writeObject(
usingList);
00388 }
00389
00390 public String
toString() {
00391 StringBuffer sb =
new StringBuffer(
a.
toString());
00392
00393
if ((
op &
Op.NATURAL) != 0) sb.append(
" NATURAL");
00394
op &= ~
Op.NATURAL;
00395 sb.append(
' ');
00396 sb.append(
Op.toString(
op));
00397 sb.append(
" JOIN ");
00398 sb.append(
b.
toString());
00399
return sb.toString();
00400 }
00401
00402
00403 public String
name() {
00404 StringBuffer sb =
new StringBuffer(
'(');
00405 sb.append(
a.
name());
00406 sb.append(
"\n ");
00407 sb.append(
Op.toString(
op));
00408 sb.append(
"\n");
00409 sb.append(
b.
name());
00410 sb.append(
')');
00411
return sb.toString();
00412 }
00413
00414 }