提交 0a1d5822 authored 作者: Thomas Mueller's avatar Thomas Mueller

Recursive queries with many rows could throw an IndexOutOfBoundsException.

上级 38d97e2f
...@@ -61,11 +61,12 @@ public abstract class Query extends Prepared { ...@@ -61,11 +61,12 @@ public abstract class Query extends Prepared {
*/ */
protected boolean randomAccessResult; protected boolean randomAccessResult;
private boolean noCache;
private int lastLimit; private int lastLimit;
private long lastEvaluated; private long lastEvaluated;
private LocalResult lastResult; private LocalResult lastResult;
private Value[] lastParameters; private Value[] lastParameters;
private boolean cacheableChecked, cacheable; private boolean cacheableChecked;
Query(Session session) { Query(Session session) {
super(session); super(session);
...@@ -218,13 +219,17 @@ public abstract class Query extends Prepared { ...@@ -218,13 +219,17 @@ public abstract class Query extends Prepared {
return true; return true;
} }
public void disableCache() {
this.noCache = true;
}
private boolean sameResultAsLast(Session s, Value[] params, Value[] lastParams, long lastEval) { private boolean sameResultAsLast(Session s, Value[] params, Value[] lastParams, long lastEval) {
if (!cacheableChecked) { if (!cacheableChecked) {
long max = getMaxDataModificationId(); long max = getMaxDataModificationId();
cacheable = max != Long.MAX_VALUE; noCache = max == Long.MAX_VALUE;
cacheableChecked = true; cacheableChecked = true;
} }
if (!cacheable) { if (noCache) {
return false; return false;
} }
Database db = s.getDatabase(); Database db = s.getDatabase();
...@@ -269,7 +274,7 @@ public abstract class Query extends Prepared { ...@@ -269,7 +274,7 @@ public abstract class Query extends Prepared {
*/ */
LocalResult query(int limit, ResultTarget target) { LocalResult query(int limit, ResultTarget target) {
fireBeforeSelectTriggers(); fireBeforeSelectTriggers();
if (!session.getDatabase().getOptimizeReuseResults()) { if (noCache || !session.getDatabase().getOptimizeReuseResults()) {
return queryWithoutCache(limit, target); return queryWithoutCache(limit, target);
} }
Value[] params = getParameterValues(); Value[] params = getParameterValues();
...@@ -287,10 +292,11 @@ public abstract class Query extends Prepared { ...@@ -287,10 +292,11 @@ public abstract class Query extends Prepared {
} }
lastParameters = params; lastParameters = params;
closeLastResult(); closeLastResult();
lastResult = queryWithoutCache(limit, target); LocalResult r = queryWithoutCache(limit, target);
lastResult = r;
this.lastEvaluated = now; this.lastEvaluated = now;
lastLimit = limit; lastLimit = limit;
return lastResult; return r;
} }
private void closeLastResult() { private void closeLastResult() {
......
...@@ -157,7 +157,7 @@ public class ViewIndex extends BaseIndex { ...@@ -157,7 +157,7 @@ public class ViewIndex extends BaseIndex {
public Cursor find(Session session, SearchRow first, SearchRow last) { public Cursor find(Session session, SearchRow first, SearchRow last) {
if (recursive) { if (recursive) {
ResultInterface recResult = view.getRecursiveResult(session); ResultInterface recResult = view.getRecursiveResult();
if (recResult != null) { if (recResult != null) {
recResult.reset(); recResult.reset();
return new ViewCursor(table, recResult); return new ViewCursor(table, recResult);
...@@ -174,6 +174,8 @@ public class ViewIndex extends BaseIndex { ...@@ -174,6 +174,8 @@ public class ViewIndex extends BaseIndex {
throw DbException.get(ErrorCode.SYNTAX_ERROR_2, "recursive queries without UNION ALL"); throw DbException.get(ErrorCode.SYNTAX_ERROR_2, "recursive queries without UNION ALL");
} }
Query left = union.getLeft(); Query left = union.getLeft();
// to ensure the last result isn't closed
left.disableCache();
LocalResult r = left.query(0); LocalResult r = left.query(0);
LocalResult result = union.getEmptyResult(); LocalResult result = union.getEmptyResult();
while (r.next()) { while (r.next()) {
...@@ -181,7 +183,9 @@ public class ViewIndex extends BaseIndex { ...@@ -181,7 +183,9 @@ public class ViewIndex extends BaseIndex {
} }
Query right = union.getRight(); Query right = union.getRight();
r.reset(); r.reset();
view.setRecursiveResult(r, session); view.setRecursiveResult(r);
// to ensure the last result isn't closed
right.disableCache();
while (true) { while (true) {
r = right.query(0); r = right.query(0);
if (r.getRowCount() == 0) { if (r.getRowCount() == 0) {
...@@ -191,8 +195,10 @@ public class ViewIndex extends BaseIndex { ...@@ -191,8 +195,10 @@ public class ViewIndex extends BaseIndex {
result.addRow(r.currentRow()); result.addRow(r.currentRow());
} }
r.reset(); r.reset();
view.setRecursiveResult(r, session); view.setRecursiveResult(r);
} }
view.setRecursiveResult(null);
result.done();
return new ViewCursor(table, result); return new ViewCursor(table, result);
} }
ArrayList<Parameter> paramList = query.getParameters(); ArrayList<Parameter> paramList = query.getParameters();
......
...@@ -444,12 +444,15 @@ public class TableView extends Table { ...@@ -444,12 +444,15 @@ public class TableView extends Table {
return viewQuery.isEverything(ExpressionVisitor.DETERMINISTIC_VISITOR); return viewQuery.isEverything(ExpressionVisitor.DETERMINISTIC_VISITOR);
} }
public void setRecursiveResult(LocalResult value, Session session) { public void setRecursiveResult(LocalResult value) {
this.recursiveResult = value.createShallowCopy(session); if (recursiveResult != null) {
recursiveResult.close();
}
this.recursiveResult = value;
} }
public ResultInterface getRecursiveResult(Session session) { public ResultInterface getRecursiveResult() {
return recursiveResult == null ? null : recursiveResult.createShallowCopy(session); return recursiveResult;
} }
public void setTableExpression(boolean tableExpression) { public void setTableExpression(boolean tableExpression) {
......
...@@ -38,12 +38,15 @@ public class TestRecursiveQueries extends TestBase { ...@@ -38,12 +38,15 @@ public class TestRecursiveQueries extends TestBase {
stat = conn.createStatement(); stat = conn.createStatement();
stat.execute("create table test(parent varchar(255), child varchar(255))"); stat.execute("create table test(parent varchar(255), child varchar(255))");
stat.execute("insert into test values('/', 'a'), ('a', 'b1'), ('a', 'b2'), ('a', 'c'), ('c', 'd1'), ('c', 'd2')"); stat.execute("insert into test values('/', 'a'), ('a', 'b1'), ('a', 'b2'), ('a', 'c'), ('c', 'd1'), ('c', 'd2')");
// stat.execute("with recursive rec_test(depth, parent, child) as (" +
// "select 0, parent, child from test where parent = '/' " + ResultSet rs = stat.executeQuery("with recursive rec_test(depth, parent, child) as (" +
// "union all " + "select 0, parent, child from test where parent = '/' " +
// "select depth+1, r.parent, r.child from test i join rec_test r " + "union all " +
// "on (i.parent = r.child) where depth<9 " + "select depth+1, r.parent, r.child from test i join rec_test r " +
// ") select * from rec_test"); "on (i.parent = r.child) where depth<9 " +
") select count(*) from rec_test");
rs.next();
assertEquals(29524, rs.getInt(1));
stat.execute("with recursive rec_test(depth, parent, child) as ( "+ stat.execute("with recursive rec_test(depth, parent, child) as ( "+
"select 0, parent, child from test where parent = '/' "+ "select 0, parent, child from test where parent = '/' "+
"union all "+ "union all "+
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论