提交 68032a31 authored 作者: S.Vladykin's avatar S.Vladykin

added more tests

上级 4232718e
...@@ -374,14 +374,13 @@ public final class JoinBatch { ...@@ -374,14 +374,13 @@ public final class JoinBatch {
*/ */
public static IndexLookupBatch createViewIndexLookupBatch(ViewIndex viewIndex) { public static IndexLookupBatch createViewIndexLookupBatch(ViewIndex viewIndex) {
Query query = viewIndex.getQuery(); Query query = viewIndex.getQuery();
query.prepareJoinBatch();
if (query.isUnion()) { if (query.isUnion()) {
ViewIndexLookupBatchUnion unionBatch = new ViewIndexLookupBatchUnion(viewIndex); ViewIndexLookupBatchUnion unionBatch = new ViewIndexLookupBatchUnion(viewIndex);
return unionBatch.hasBatchedQueries() ? unionBatch : null; return unionBatch.initialize() ? unionBatch : null;
} }
JoinBatch jb = ((Select) query).getJoinBatch(); JoinBatch jb = ((Select) query).getJoinBatch();
if (jb == null) { if (jb == null || jb.getLookupBatch(0) == null) {
// our sub-query is not batched, will run usual way // our sub-query is not batched or is top batched sub-query
return null; return null;
} }
assert !jb.batchedSubQuery; assert !jb.batchedSubQuery;
...@@ -389,6 +388,16 @@ public final class JoinBatch { ...@@ -389,6 +388,16 @@ public final class JoinBatch {
return jb.viewIndexLookupBatch(viewIndex); return jb.viewIndexLookupBatch(viewIndex);
} }
/**
* Create fake index lookup batch for non-batched table filter.
*
* @param filter the table filter
* @return fake index lookup batch
*/
public static IndexLookupBatch createFakeIndexLookupBatch(TableFilter filter) {
return new FakeLookupBatch(filter);
}
@Override @Override
public String toString() { public String toString() {
return "JoinBatch->\nprev->" + (current == null ? null : current.prev) + return "JoinBatch->\nprev->" + (current == null ? null : current.prev) +
...@@ -410,10 +419,8 @@ public final class JoinBatch { ...@@ -410,10 +419,8 @@ public final class JoinBatch {
this.filter = filter; this.filter = filter;
this.id = filter.getJoinFilterId(); this.id = filter.getJoinFilterId();
this.join = join; this.join = join;
if (lookupBatch == null && id != 0) {
lookupBatch = new FakeLookupBatch(filter);
}
this.lookupBatch = lookupBatch; this.lookupBatch = lookupBatch;
assert lookupBatch != null || id == 0;
} }
private void reset() { private void reset() {
...@@ -913,34 +920,37 @@ public final class JoinBatch { ...@@ -913,34 +920,37 @@ public final class JoinBatch {
protected ViewIndexLookupBatchUnion(ViewIndex viewIndex) { protected ViewIndexLookupBatchUnion(ViewIndex viewIndex) {
super(viewIndex); super(viewIndex);
collectJoinBatches(viewIndex.getQuery());
} }
private boolean hasBatchedQueries() { private boolean initialize() {
return joinBatches != null; return collectJoinBatches(viewIndex.getQuery()) && joinBatches != null;
} }
private void collectJoinBatches(Query query) { private boolean collectJoinBatches(Query query) {
if (query.isUnion()) { if (query.isUnion()) {
SelectUnion union = (SelectUnion) query; SelectUnion union = (SelectUnion) query;
collectJoinBatches(union.getLeft()); return collectJoinBatches(union.getLeft()) &&
collectJoinBatches(union.getRight()); collectJoinBatches(union.getRight());
}
Select select = (Select) query;
JoinBatch jb = select.getJoinBatch();
if (jb == null) {
onlyBatchedQueries = false;
} else { } else {
Select select = (Select) query; if (jb.getLookupBatch(0) == null) {
JoinBatch jb = select.getJoinBatch(); // we are top sub-query
if (jb == null) { return false;
onlyBatchedQueries = false; }
} else { assert !jb.batchedSubQuery;
assert !jb.batchedSubQuery; jb.batchedSubQuery = true;
jb.batchedSubQuery = true; if (joinBatches == null) {
if (joinBatches == null) { joinBatches = New.arrayList();
joinBatches = New.arrayList(); filters = New.arrayList();
filters = New.arrayList();
}
filters.add(jb.filters[0]);
joinBatches.add(jb);
} }
filters.add(jb.filters[0]);
joinBatches.add(jb);
} }
return true;
} }
@Override @Override
......
...@@ -20,6 +20,7 @@ import org.h2.index.Index; ...@@ -20,6 +20,7 @@ import org.h2.index.Index;
import org.h2.index.IndexLookupBatch; import org.h2.index.IndexLookupBatch;
import org.h2.index.IndexCondition; import org.h2.index.IndexCondition;
import org.h2.index.IndexCursor; import org.h2.index.IndexCursor;
import org.h2.index.ViewIndex;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.result.Row; import org.h2.result.Row;
import org.h2.result.SearchRow; import org.h2.result.SearchRow;
...@@ -368,6 +369,14 @@ public class TableFilter implements ColumnResolver { ...@@ -368,6 +369,14 @@ public class TableFilter implements ColumnResolver {
joinBatch = null; joinBatch = null;
joinFilterId = -1; joinFilterId = -1;
IndexLookupBatch lookupBatch = null; IndexLookupBatch lookupBatch = null;
if (getTable().isView()) {
session.pushSubQueryInfo(masks, filters, filter, select.getSortOrder());
try {
((ViewIndex) index).getQuery().prepareJoinBatch();
} finally {
session.popSubQueryInfo();
}
}
// For globally top table filter we don't need to create lookup batch, because // For globally top table filter we don't need to create lookup batch, because
// currently it will not be used (this will be shown in ViewIndex.getPlanSQL()). Probably // currently it will not be used (this will be shown in ViewIndex.getPlanSQL()). Probably
// later on it will make sense to create it to better support X IN (...) conditions, // later on it will make sense to create it to better support X IN (...) conditions,
...@@ -376,7 +385,7 @@ public class TableFilter implements ColumnResolver { ...@@ -376,7 +385,7 @@ public class TableFilter implements ColumnResolver {
// in turn is not top in outer query, thus we need to enable batching here to allow // in turn is not top in outer query, thus we need to enable batching here to allow
// outer query run batched join against this sub-query. // outer query run batched join against this sub-query.
if (jb == null && select != null && !isAlwaysTopTableFilter(filter)) { if (jb == null && select != null && !isAlwaysTopTableFilter(filter)) {
lookupBatch = createLookupBatch(filters, filter); lookupBatch = index.createLookupBatch(this);
if (lookupBatch != null) { if (lookupBatch != null) {
jb = new JoinBatch(filter + 1, join); jb = new JoinBatch(filter + 1, join);
} }
...@@ -387,28 +396,23 @@ public class TableFilter implements ColumnResolver { ...@@ -387,28 +396,23 @@ public class TableFilter implements ColumnResolver {
} }
joinBatch = jb; joinBatch = jb;
joinFilterId = filter; joinFilterId = filter;
if (lookupBatch == null && !isAlwaysTopTableFilter(filter)) { if (lookupBatch == null) {
// createLookupBatch will be called at most once because jb can be boolean notTop = !isAlwaysTopTableFilter(filter);
// created only if lookupBatch is already not null from the call above. if (notTop || getTable().isView()) {
lookupBatch = createLookupBatch(filters, filter); // createLookupBatch will be called at most once because jb can be
// created only if lookupBatch is already not null from the call above.
lookupBatch = index.createLookupBatch(this);
if (lookupBatch == null && notTop) {
// the index does not support lookup batching, need to fake it because we are not top
lookupBatch = JoinBatch.createFakeIndexLookupBatch(this);
}
}
} }
jb.register(this, lookupBatch); jb.register(this, lookupBatch);
} }
return jb; return jb;
} }
private IndexLookupBatch createLookupBatch(TableFilter[] filters, int filter) {
if (getTable().isView()) {
session.pushSubQueryInfo(masks, filters, filter, select.getSortOrder());
try {
return index.createLookupBatch(this);
} finally {
session.popSubQueryInfo();
}
}
return index.createLookupBatch(this);
}
public int getJoinFilterId() { public int getJoinFilterId() {
return joinFilterId; return joinFilterId;
} }
......
...@@ -489,15 +489,15 @@ public class TestTableEngines extends TestBase { ...@@ -489,15 +489,15 @@ public class TestTableEngines extends TestBase {
stat.execute("CREATE INDEX T_IDX_B ON t(b)"); stat.execute("CREATE INDEX T_IDX_B ON t(b)");
setBatchSize(t, 3); setBatchSize(t, 3);
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
stat.execute("insert into t values (" + i + "," + i + ")"); stat.execute("insert into t values (" + i + "," + (i + 10) + ")");
} }
stat.execute("CREATE TABLE u (a int, b int) ENGINE " + engine); stat.execute("CREATE TABLE u (a int, b int) ENGINE " + engine);
TreeSetTable u = TreeSetIndexTableEngine.created; TreeSetTable u = TreeSetIndexTableEngine.created;
stat.execute("CREATE INDEX U_IDX_A ON u(a)"); stat.execute("CREATE INDEX U_IDX_A ON u(a)");
stat.execute("CREATE INDEX U_IDX_B ON u(b)"); stat.execute("CREATE INDEX U_IDX_B ON u(b)");
setBatchSize(u, 0); setBatchSize(u, 0);
for (int i = 0; i < 20; i++) { for (int i = 10; i < 25; i++) {
stat.execute("insert into u values (" + i + "," + i + ")"); stat.execute("insert into u values (" + i + "," + (i - 15)+ ")");
} }
checkPlan(stat, "SELECT 1 FROM PUBLIC.T T1 /* PUBLIC.\"scan\" */ " checkPlan(stat, "SELECT 1 FROM PUBLIC.T T1 /* PUBLIC.\"scan\" */ "
...@@ -521,11 +521,87 @@ public class TestTableEngines extends TestBase { ...@@ -521,11 +521,87 @@ public class TestTableEngines extends TestBase {
+ "INNER JOIN ( SELECT A FROM PUBLIC.T ) Z " + "INNER JOIN ( SELECT A FROM PUBLIC.T ) Z "
+ "/* batched:view SELECT A FROM PUBLIC.T /++ batched:test PUBLIC.T_IDX_A: A IS ?1 ++/ " + "/* batched:view SELECT A FROM PUBLIC.T /++ batched:test PUBLIC.T_IDX_A: A IS ?1 ++/ "
+ "WHERE A IS ?1: A = T.B */ ON 1=1 WHERE Z.A = T.B"); + "WHERE A IS ?1: A = T.B */ ON 1=1 WHERE Z.A = T.B");
checkPlan(stat, "SELECT 1 FROM PUBLIC.T /* PUBLIC.\"scan\" */ "
+ "INNER JOIN ( ((SELECT A FROM PUBLIC.T) UNION ALL (SELECT B FROM PUBLIC.U)) "
+ "UNION ALL (SELECT B FROM PUBLIC.T) ) Z /* batched:view "
+ "((SELECT A FROM PUBLIC.T /++ batched:test PUBLIC.T_IDX_A: A IS ?1 ++/ WHERE A IS ?1) "
+ "UNION ALL "
+ "(SELECT B FROM PUBLIC.U /++ PUBLIC.U_IDX_B: B IS ?1 ++/ WHERE B IS ?1)) "
+ "UNION ALL "
+ "(SELECT B FROM PUBLIC.T /++ batched:test PUBLIC.T_IDX_B: B IS ?1 ++/ WHERE B IS ?1)"
+ ": A = T.A */ ON 1=1 WHERE Z.A = T.A");
checkPlan(stat, "SELECT 1 FROM PUBLIC.T /* PUBLIC.\"scan\" */ "
+ "INNER JOIN ( SELECT U.A FROM PUBLIC.U INNER JOIN PUBLIC.T ON 1=1 WHERE U.B = T.B ) Z "
+ "/* batched:view SELECT U.A FROM PUBLIC.U /++ batched:fake PUBLIC.U_IDX_A: A IS ?1 ++/ "
+ "/++ WHERE U.A IS ?1 ++/ INNER JOIN PUBLIC.T /++ batched:test PUBLIC.T_IDX_B: B = U.B ++/ "
+ "ON 1=1 WHERE (U.A IS ?1) AND (U.B = T.B): A = T.A */ ON 1=1 WHERE Z.A = T.A");
checkPlan(stat, "SELECT 1 FROM PUBLIC.T /* PUBLIC.\"scan\" */ "
+ "INNER JOIN ( SELECT A FROM PUBLIC.U ) Z /* SELECT A FROM PUBLIC.U "
+ "/++ PUBLIC.U_IDX_A: A IS ?1 ++/ WHERE A IS ?1: A = T.A */ ON 1=1 WHERE T.A = Z.A");
checkPlan(stat, "SELECT 1 FROM "
+ "( SELECT U.A FROM PUBLIC.U INNER JOIN PUBLIC.T ON 1=1 WHERE U.B = T.B ) Z "
+ "/* SELECT U.A FROM PUBLIC.U /++ PUBLIC.\"scan\" ++/ "
+ "INNER JOIN PUBLIC.T /++ batched:test PUBLIC.T_IDX_B: B = U.B ++/ "
+ "ON 1=1 WHERE U.B = T.B */ "
+ "INNER JOIN PUBLIC.T /* batched:test PUBLIC.T_IDX_A: A = Z.A */ ON 1=1 WHERE T.A = Z.A");
checkPlan(stat, "SELECT 1 FROM "
+ "( SELECT U.A FROM PUBLIC.T INNER JOIN PUBLIC.U ON 1=1 WHERE T.B = U.B ) Z "
+ "/* SELECT U.A FROM PUBLIC.T /++ PUBLIC.\"scan\" ++/ "
+ "INNER JOIN PUBLIC.U /++ PUBLIC.U_IDX_B: B = T.B ++/ "
+ "ON 1=1 WHERE T.B = U.B */ INNER JOIN PUBLIC.T /* batched:test PUBLIC.T_IDX_A: A = Z.A */ "
+ "ON 1=1 WHERE Z.A = T.A");
checkPlan(stat, "SELECT 1 FROM ( (SELECT A FROM PUBLIC.T) UNION (SELECT A FROM PUBLIC.U) ) Z "
+ "/* (SELECT A FROM PUBLIC.T /++ PUBLIC.\"scan\" ++/) "
+ "UNION "
+ "(SELECT A FROM PUBLIC.U /++ PUBLIC.\"scan\" ++/) */ "
+ "INNER JOIN PUBLIC.T /* batched:test PUBLIC.T_IDX_A: A = Z.A */ ON 1=1 WHERE Z.A = T.A");
checkPlan(stat, "SELECT 1 FROM PUBLIC.U /* PUBLIC.\"scan\" */ "
+ "INNER JOIN ( (SELECT A, B FROM PUBLIC.T) UNION (SELECT B, A FROM PUBLIC.U) ) Z "
+ "/* batched:view (SELECT A, B FROM PUBLIC.T /++ batched:test PUBLIC.T_IDX_B: B IS ?1 ++/ "
+ "WHERE B IS ?1) UNION (SELECT B, A FROM PUBLIC.U /++ PUBLIC.U_IDX_A: A IS ?1 ++/ "
+ "WHERE A IS ?1): B = U.B */ ON 1=1 /* WHERE U.B = Z.B */ "
+ "INNER JOIN PUBLIC.T /* batched:test PUBLIC.T_IDX_A: A = Z.A */ ON 1=1 "
+ "WHERE (U.B = Z.B) AND (Z.A = T.A)");
checkPlan(stat, "SELECT 1 FROM PUBLIC.U /* PUBLIC.\"scan\" */ "
+ "INNER JOIN ( SELECT A, B FROM PUBLIC.U ) Z "
+ "/* batched:fake SELECT A, B FROM PUBLIC.U /++ PUBLIC.U_IDX_A: A IS ?1 ++/ "
+ "WHERE A IS ?1: A = U.A */ ON 1=1 /* WHERE U.A = Z.A */ "
+ "INNER JOIN PUBLIC.T /* batched:test PUBLIC.T_IDX_B: B = Z.B */ "
+ "ON 1=1 WHERE (U.A = Z.A) AND (Z.B = T.B)");
// t: a = [ 0..20), b = [10..30)
// u: a = [10..25), b = [-5..10)
checkBatchedQueryResult(stat, 10,
"select t.a from t, (select t.b from u, t where u.a = t.a) z where t.b = z.b");
checkBatchedQueryResult(stat, 5,
"select t.a from (select t1.b from t t1, t t2 where t1.a = t2.b) z, t where t.b = z.b + 5");
checkBatchedQueryResult(stat, 1,
"select t.a from (select u.b from u, t t2 where u.a = t2.b) z, t where t.b = z.b + 1");
checkBatchedQueryResult(stat, 15,
"select t.a from (select u.b from u, t t2 where u.a = t2.b) z left join t on t.b = z.b");
checkBatchedQueryResult(stat, 15,
"select t.a from (select t1.b from t t1 left join t t2 on t1.a = t2.b) z, t "
+ "where t.b = z.b + 5");
checkBatchedQueryResult(stat, 1, "select t.a from t,(select 5 as b from t union select 10 from u) z "
+ "where t.b = z.b");
checkBatchedQueryResult(stat, 15, "select t.a from u,(select 5 as b, a from t "
+ "union select 10, a from u) z, t where t.b = z.b and z.a = u.a");
stat.execute("DROP TABLE T"); stat.execute("DROP TABLE T");
stat.execute("DROP TABLE U"); stat.execute("DROP TABLE U");
} }
private void checkBatchedQueryResult(Statement stat, int size, String sql) throws SQLException {
setBatchingEnabled(stat, false);
List<List<Object>> expected = query(stat, sql);
assertEquals(size, expected.size());
setBatchingEnabled(stat, true);
List<List<Object>> actual = query(stat, sql);
if (!expected.equals(actual)) {
fail("\nexpected: " + expected + "\nactual: " + actual);
}
}
private void doTestBatchedJoin(Statement stat, int... batchSizes) throws SQLException { private void doTestBatchedJoin(Statement stat, int... batchSizes) throws SQLException {
ArrayList<TreeSetTable> tables = New.arrayList(batchSizes.length); ArrayList<TreeSetTable> tables = New.arrayList(batchSizes.length);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论