Skip to content
项目
群组
代码片段
帮助
正在加载...
帮助
为 GitLab 提交贡献
登录/注册
切换导航
H
h2database
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
计划
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
Administrator
h2database
Commits
9ebce112
提交
9ebce112
authored
11月 08, 2015
作者:
S.Vladykin
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Batched join now getting initialized in Select.prepare() + query plan prints batched join info
上级
83f9141d
隐藏空白字符变更
内嵌
并排
正在显示
5 个修改的文件
包含
152 行增加
和
69 行删除
+152
-69
Select.java
h2/src/main/org/h2/command/dml/Select.java
+6
-1
IndexLookupBatch.java
h2/src/main/org/h2/index/IndexLookupBatch.java
+7
-1
JoinBatch.java
h2/src/main/org/h2/table/JoinBatch.java
+79
-45
TableFilter.java
h2/src/main/org/h2/table/TableFilter.java
+55
-22
TestTableEngines.java
h2/src/test/org/h2/test/db/TestTableEngines.java
+5
-0
没有找到文件。
h2/src/main/org/h2/command/dml/Select.java
浏览文件 @
9ebce112
...
@@ -9,7 +9,6 @@ import java.util.ArrayList;
...
@@ -9,7 +9,6 @@ import java.util.ArrayList;
import
java.util.Arrays
;
import
java.util.Arrays
;
import
java.util.HashMap
;
import
java.util.HashMap
;
import
java.util.HashSet
;
import
java.util.HashSet
;
import
org.h2.api.ErrorCode
;
import
org.h2.api.ErrorCode
;
import
org.h2.api.Trigger
;
import
org.h2.api.Trigger
;
import
org.h2.command.CommandInterface
;
import
org.h2.command.CommandInterface
;
...
@@ -36,6 +35,7 @@ import org.h2.result.SortOrder;
...
@@ -36,6 +35,7 @@ import org.h2.result.SortOrder;
import
org.h2.table.Column
;
import
org.h2.table.Column
;
import
org.h2.table.ColumnResolver
;
import
org.h2.table.ColumnResolver
;
import
org.h2.table.IndexColumn
;
import
org.h2.table.IndexColumn
;
import
org.h2.table.JoinBatch
;
import
org.h2.table.Table
;
import
org.h2.table.Table
;
import
org.h2.table.TableFilter
;
import
org.h2.table.TableFilter
;
import
org.h2.util.New
;
import
org.h2.util.New
;
...
@@ -942,9 +942,14 @@ public class Select extends Query {
...
@@ -942,9 +942,14 @@ public class Select extends Query {
}
}
expressionArray
=
new
Expression
[
expressions
.
size
()];
expressionArray
=
new
Expression
[
expressions
.
size
()];
expressions
.
toArray
(
expressionArray
);
expressions
.
toArray
(
expressionArray
);
topTableFilter
.
prepareBatch
(
0
);
isPrepared
=
true
;
isPrepared
=
true
;
}
}
public
JoinBatch
getJoinBatch
()
{
return
getTopTableFilter
().
getJoinBatch
();
}
@Override
@Override
public
double
getCost
()
{
public
double
getCost
()
{
return
cost
;
return
cost
;
...
...
h2/src/main/org/h2/index/IndexLookupBatch.java
浏览文件 @
9ebce112
...
@@ -15,7 +15,8 @@ import org.h2.result.SearchRow;
...
@@ -15,7 +15,8 @@ import org.h2.result.SearchRow;
* method {@link #isBatchFull()}} will return {@code true} or there are no more
* method {@link #isBatchFull()}} will return {@code true} or there are no more
* search rows to add. Then method {@link #find()} will be called to execute batched lookup.
* search rows to add. Then method {@link #find()} will be called to execute batched lookup.
* Note that a single instance of {@link IndexLookupBatch} can be reused for multiple
* Note that a single instance of {@link IndexLookupBatch} can be reused for multiple
* sequential batched lookups.
* sequential batched lookups, moreover it can be reused for multiple queries for
* the same prepared statement.
*
*
* @see Index#createLookupBatch(org.h2.table.TableFilter)
* @see Index#createLookupBatch(org.h2.table.TableFilter)
* @author Sergi Vladykin
* @author Sergi Vladykin
...
@@ -47,4 +48,9 @@ public interface IndexLookupBatch {
...
@@ -47,4 +48,9 @@ public interface IndexLookupBatch {
* @return List of future cursors for collected search rows.
* @return List of future cursors for collected search rows.
*/
*/
List
<
Future
<
Cursor
>>
find
();
List
<
Future
<
Cursor
>>
find
();
/**
* Reset this batch to clear state. This method will be called before each query execution.
*/
void
reset
();
}
}
h2/src/main/org/h2/table/JoinBatch.java
浏览文件 @
9ebce112
...
@@ -28,65 +28,93 @@ import org.h2.value.ValueLong;
...
@@ -28,65 +28,93 @@ import org.h2.value.ValueLong;
* @author Sergi Vladykin
* @author Sergi Vladykin
*/
*/
public
final
class
JoinBatch
{
public
final
class
JoinBatch
{
private
static
final
Cursor
EMPTY_CURSOR
=
new
Cursor
()
{
private
static
final
Cursor
EMPTY_CURSOR
=
new
Cursor
()
{
@Override
@Override
public
boolean
previous
()
{
public
boolean
previous
()
{
return
false
;
return
false
;
}
}
@Override
@Override
public
boolean
next
()
{
public
boolean
next
()
{
return
false
;
return
false
;
}
}
@Override
@Override
public
SearchRow
getSearchRow
()
{
public
SearchRow
getSearchRow
()
{
return
null
;
return
null
;
}
}
@Override
@Override
public
Row
get
()
{
public
Row
get
()
{
return
null
;
return
null
;
}
}
@Override
@Override
public
String
toString
()
{
public
String
toString
()
{
return
"EMPTY_CURSOR"
;
return
"EMPTY_CURSOR"
;
}
}
};
};
private
int
filtersCount
;
private
JoinFilter
[]
filters
;
private
JoinFilter
[]
filters
;
private
JoinFilter
top
;
private
JoinFilter
top
;
private
boolean
started
;
private
boolean
started
;
private
JoinRow
current
;
private
JoinRow
current
;
private
boolean
found
;
private
boolean
found
;
/**
/**
* This filter joined after this batched join and can be used normally.
* This filter joined after this batched join and can be used normally.
*/
*/
private
final
TableFilter
additionalFilter
;
private
final
TableFilter
additionalFilter
;
/**
/**
* @param filtersCount number of filters participating in this batched join
* @param additionalFilter table filter after this batched join.
* @param additionalFilter table filter after this batched join.
*/
*/
public
JoinBatch
(
TableFilter
additionalFilter
)
{
public
JoinBatch
(
int
filtersCount
,
TableFilter
additionalFilter
)
{
if
(
filtersCount
>
32
)
{
// This is because we store state in a 64 bit field, 2 bits per joined table.
throw
DbException
.
getUnsupportedException
(
"Too many tables in join (at most 32 supported)."
);
}
filters
=
new
JoinFilter
[
filtersCount
];
this
.
additionalFilter
=
additionalFilter
;
this
.
additionalFilter
=
additionalFilter
;
}
}
/**
* @param joinFilterId joined table filter id
* @return {@code true} if index really supports batching in this query
*/
public
boolean
isBatchedIndex
(
int
joinFilterId
)
{
return
filters
[
joinFilterId
].
isBatched
();
}
/**
* Reset state of this batch.
*/
public
void
reset
()
{
current
=
null
;
started
=
false
;
found
=
false
;
for
(
JoinFilter
jf
:
filters
)
{
jf
.
reset
();
}
if
(
additionalFilter
!=
null
)
{
additionalFilter
.
reset
();
}
}
/**
/**
* @param filter table filter
* @param filter table filter
* @param lookupBatch lookup batch
* @param lookupBatch lookup batch
*/
*/
public
void
register
(
TableFilter
filter
,
IndexLookupBatch
lookupBatch
)
{
public
void
register
(
TableFilter
filter
,
IndexLookupBatch
lookupBatch
)
{
assert
filter
!=
null
;
assert
filter
!=
null
;
filtersCount
++;
top
=
new
JoinFilter
(
lookupBatch
,
filter
,
top
);
top
=
new
JoinFilter
(
lookupBatch
,
filter
,
top
);
filters
[
top
.
id
]
=
top
;
}
}
/**
/**
* @param filterId table filter id
* @param filterId table filter id
* @param column column
* @param column column
...
@@ -108,21 +136,9 @@ public final class JoinBatch {
...
@@ -108,21 +136,9 @@ public final class JoinBatch {
}
}
private
void
start
()
{
private
void
start
()
{
if
(
filtersCount
>
32
)
{
// TODO if filters[0].isBatched() then use batching instead of top.filter.getIndexCursor()
// This is because we store state in a 64 bit field, 2 bits per joined table.
throw
DbException
.
getUnsupportedException
(
"Too many tables in join (at most 32 supported)."
);
}
// fill filters
filters
=
new
JoinFilter
[
filtersCount
];
JoinFilter
jf
=
top
;
for
(
int
i
=
0
;
i
<
filtersCount
;
i
++)
{
jf
.
id
=
jf
.
filter
.
joinFilterId
=
i
;
filters
[
i
]
=
jf
;
jf
=
jf
.
join
;
}
// initialize current row
// initialize current row
current
=
new
JoinRow
(
new
Object
[
filters
Count
]);
current
=
new
JoinRow
(
new
Object
[
filters
.
length
]);
current
.
updateRow
(
top
.
id
,
top
.
filter
.
getIndexCursor
(),
JoinRow
.
S_NULL
,
JoinRow
.
S_CURSOR
);
current
.
updateRow
(
top
.
id
,
top
.
filter
.
getIndexCursor
(),
JoinRow
.
S_NULL
,
JoinRow
.
S_CURSOR
);
// initialize top cursor
// initialize top cursor
...
@@ -188,7 +204,7 @@ public final class JoinBatch {
...
@@ -188,7 +204,7 @@ public final class JoinBatch {
}
}
current
.
prev
=
null
;
current
.
prev
=
null
;
final
int
lastJfId
=
filters
Count
-
1
;
final
int
lastJfId
=
filters
.
length
-
1
;
int
jfId
=
lastJfId
;
int
jfId
=
lastJfId
;
while
(
current
.
row
(
jfId
)
==
null
)
{
while
(
current
.
row
(
jfId
)
==
null
)
{
...
@@ -325,17 +341,28 @@ public final class JoinBatch {
...
@@ -325,17 +341,28 @@ public final class JoinBatch {
private
static
final
class
JoinFilter
{
private
static
final
class
JoinFilter
{
private
final
TableFilter
filter
;
private
final
TableFilter
filter
;
private
final
JoinFilter
join
;
private
final
JoinFilter
join
;
private
int
id
;
private
final
int
id
;
private
final
boolean
fakeBatch
;
private
final
IndexLookupBatch
lookupBatch
;
private
final
IndexLookupBatch
lookupBatch
;
private
JoinFilter
(
IndexLookupBatch
lookupBatch
,
TableFilter
filter
,
JoinFilter
join
)
{
private
JoinFilter
(
IndexLookupBatch
lookupBatch
,
TableFilter
filter
,
JoinFilter
join
)
{
this
.
filter
=
filter
;
this
.
filter
=
filter
;
this
.
id
=
filter
.
getJoinFilterId
();
this
.
join
=
join
;
this
.
join
=
join
;
this
.
lookupBatch
=
lookupBatch
!=
null
?
lookupBatch
:
new
FakeLookupBatch
(
filter
);
fakeBatch
=
lookupBatch
==
null
;
this
.
lookupBatch
=
fakeBatch
?
new
FakeLookupBatch
(
filter
)
:
lookupBatch
;
}
}
public
Row
getNullRow
()
{
private
boolean
isBatched
()
{
return
!
fakeBatch
;
}
private
void
reset
()
{
lookupBatch
.
reset
();
}
private
Row
getNullRow
()
{
return
filter
.
getTable
().
getNullRow
();
return
filter
.
getTable
().
getNullRow
();
}
}
...
@@ -353,7 +380,7 @@ public final class JoinBatch {
...
@@ -353,7 +380,7 @@ public final class JoinBatch {
return
filterOk
&&
(
ignoreJoinCondition
||
joinOk
);
return
filterOk
&&
(
ignoreJoinCondition
||
joinOk
);
}
}
private
boolean
collectSearchRows
()
{
private
boolean
collectSearchRows
()
{
assert
!
isBatchFull
();
assert
!
isBatchFull
();
IndexCursor
c
=
filter
.
getIndexCursor
();
IndexCursor
c
=
filter
.
getIndexCursor
();
...
@@ -364,7 +391,7 @@ public final class JoinBatch {
...
@@ -364,7 +391,7 @@ public final class JoinBatch {
lookupBatch
.
addSearchRows
(
c
.
getStart
(),
c
.
getEnd
());
lookupBatch
.
addSearchRows
(
c
.
getStart
(),
c
.
getEnd
());
return
true
;
return
true
;
}
}
private
JoinRow
find
(
JoinRow
current
)
{
private
JoinRow
find
(
JoinRow
current
)
{
assert
current
!=
null
;
assert
current
!=
null
;
...
@@ -403,13 +430,13 @@ public final class JoinBatch {
...
@@ -403,13 +430,13 @@ public final class JoinBatch {
// the last updated row
// the last updated row
return
current
;
return
current
;
}
}
@Override
@Override
public
String
toString
()
{
public
String
toString
()
{
return
"JoinFilter->"
+
filter
;
return
"JoinFilter->"
+
filter
;
}
}
}
}
/**
/**
* Linked row in batched join.
* Linked row in batched join.
*/
*/
...
@@ -504,7 +531,7 @@ public final class JoinBatch {
...
@@ -504,7 +531,7 @@ public final class JoinBatch {
}
}
row
=
null
;
row
=
null
;
}
}
/**
/**
* Copy this JoinRow behind itself in linked list of all in progress rows.
* Copy this JoinRow behind itself in linked list of all in progress rows.
*
*
...
@@ -514,24 +541,24 @@ public final class JoinBatch {
...
@@ -514,24 +541,24 @@ public final class JoinBatch {
private
JoinRow
copyBehind
(
int
jfId
)
{
private
JoinRow
copyBehind
(
int
jfId
)
{
assert
isCursor
(
jfId
);
assert
isCursor
(
jfId
);
assert
jfId
+
1
==
row
.
length
||
row
[
jfId
+
1
]
==
null
;
assert
jfId
+
1
==
row
.
length
||
row
[
jfId
+
1
]
==
null
;
Object
[]
r
=
new
Object
[
row
.
length
];
Object
[]
r
=
new
Object
[
row
.
length
];
if
(
jfId
!=
0
)
{
if
(
jfId
!=
0
)
{
System
.
arraycopy
(
row
,
0
,
r
,
0
,
jfId
);
System
.
arraycopy
(
row
,
0
,
r
,
0
,
jfId
);
}
}
JoinRow
copy
=
new
JoinRow
(
r
);
JoinRow
copy
=
new
JoinRow
(
r
);
copy
.
state
=
state
;
copy
.
state
=
state
;
if
(
prev
!=
null
)
{
if
(
prev
!=
null
)
{
copy
.
prev
=
prev
;
copy
.
prev
=
prev
;
prev
.
next
=
copy
;
prev
.
next
=
copy
;
}
}
prev
=
copy
;
prev
=
copy
;
copy
.
next
=
this
;
copy
.
next
=
this
;
return
copy
;
return
copy
;
}
}
@Override
@Override
public
String
toString
()
{
public
String
toString
()
{
return
"JoinRow->"
+
Arrays
.
toString
(
row
);
return
"JoinRow->"
+
Arrays
.
toString
(
row
);
...
@@ -559,6 +586,13 @@ public final class JoinBatch {
...
@@ -559,6 +586,13 @@ public final class JoinBatch {
this
.
filter
=
filter
;
this
.
filter
=
filter
;
}
}
@Override
public
void
reset
()
{
full
=
false
;
first
=
last
=
null
;
result
.
set
(
0
,
null
);
}
@Override
@Override
public
void
addSearchRows
(
SearchRow
first
,
SearchRow
last
)
{
public
void
addSearchRows
(
SearchRow
first
,
SearchRow
last
)
{
assert
!
full
;
assert
!
full
;
...
...
h2/src/main/org/h2/table/TableFilter.java
浏览文件 @
9ebce112
...
@@ -60,7 +60,7 @@ public class TableFilter implements ColumnResolver {
...
@@ -60,7 +60,7 @@ public class TableFilter implements ColumnResolver {
* Batched join support.
* Batched join support.
*/
*/
private
JoinBatch
joinBatch
;
private
JoinBatch
joinBatch
;
int
joinFilterId
=
-
1
;
private
int
joinFilterId
=
-
1
;
/**
/**
* Indicates that this filter is used in the plan.
* Indicates that this filter is used in the plan.
...
@@ -308,52 +308,77 @@ public class TableFilter implements ColumnResolver {
...
@@ -308,52 +308,77 @@ public class TableFilter implements ColumnResolver {
* Start the query. This will reset the scan counts.
* Start the query. This will reset the scan counts.
*
*
* @param s the session
* @param s the session
* @return join batch if query runs over index which supports batched lookups, null otherwise
*/
*/
public
JoinBatch
startQuery
(
Session
s
)
{
public
void
startQuery
(
Session
s
)
{
joinBatch
=
null
;
joinFilterId
=
-
1
;
this
.
session
=
s
;
this
.
session
=
s
;
scanCount
=
0
;
scanCount
=
0
;
if
(
nestedJoin
!=
null
)
{
if
(
nestedJoin
!=
null
)
{
nestedJoin
.
startQuery
(
s
);
nestedJoin
.
startQuery
(
s
);
}
}
if
(
join
!=
null
)
{
join
.
startQuery
(
s
);
}
}
/**
* Reset to the current position.
*/
public
void
reset
()
{
if
(
joinBatch
!=
null
&&
joinFilterId
==
0
)
{
// reset join batch only on top table filter
joinBatch
.
reset
();
return
;
}
if
(
nestedJoin
!=
null
)
{
nestedJoin
.
reset
();
}
if
(
join
!=
null
)
{
join
.
reset
();
}
state
=
BEFORE_FIRST
;
foundOne
=
false
;
}
/**
* Attempt to initialize batched join.
*
* @param id join filter id (index of this table filter in join list)
* @return join batch if query runs over index which supports batched lookups, {@code null} otherwise
*/
public
JoinBatch
prepareBatch
(
int
id
)
{
JoinBatch
batch
=
null
;
JoinBatch
batch
=
null
;
if
(
join
!=
null
)
{
if
(
join
!=
null
)
{
batch
=
join
.
startQuery
(
s
);
batch
=
join
.
prepareBatch
(
id
+
1
);
}
}
IndexLookupBatch
lookupBatch
=
null
;
IndexLookupBatch
lookupBatch
=
null
;
if
(
batch
==
null
&&
select
!=
null
&&
select
.
getTopTableFilter
()
!=
this
)
{
if
(
batch
==
null
&&
select
!=
null
&&
id
!=
0
)
{
// TODO session.getSubQueryInfo() instead of id != 0 + use upper level sub-query info
lookupBatch
=
index
.
createLookupBatch
(
this
);
lookupBatch
=
index
.
createLookupBatch
(
this
);
if
(
lookupBatch
!=
null
)
{
if
(
lookupBatch
!=
null
)
{
batch
=
new
JoinBatch
(
join
);
batch
=
new
JoinBatch
(
id
+
1
,
join
);
}
}
}
}
if
(
batch
!=
null
)
{
if
(
batch
!=
null
)
{
if
(
nestedJoin
!=
null
)
{
if
(
nestedJoin
!=
null
)
{
throw
DbException
.
getUnsupportedException
(
"nested join with batched index"
);
throw
DbException
.
getUnsupportedException
(
"nested join with batched index"
);
}
}
if
(
lookupBatch
==
null
)
{
if
(
lookupBatch
==
null
&&
id
!=
0
)
{
// TODO session.getSubQueryInfo() instead of id != 0 + use upper level sub-query info
lookupBatch
=
index
.
createLookupBatch
(
this
);
lookupBatch
=
index
.
createLookupBatch
(
this
);
}
}
joinBatch
=
batch
;
joinBatch
=
batch
;
joinFilterId
=
id
;
batch
.
register
(
this
,
lookupBatch
);
batch
.
register
(
this
,
lookupBatch
);
}
}
return
batch
;
return
batch
;
}
}
/**
public
int
getJoinFilterId
()
{
* Reset to the current position.
return
joinFilterId
;
*/
}
public
void
reset
()
{
if
(
nestedJoin
!=
null
)
{
public
JoinBatch
getJoinBatch
()
{
nestedJoin
.
reset
();
return
joinBatch
;
}
if
(
join
!=
null
)
{
join
.
reset
();
}
state
=
BEFORE_FIRST
;
foundOne
=
false
;
}
}
/**
/**
...
@@ -363,7 +388,7 @@ public class TableFilter implements ColumnResolver {
...
@@ -363,7 +388,7 @@ public class TableFilter implements ColumnResolver {
*/
*/
public
boolean
next
()
{
public
boolean
next
()
{
if
(
joinBatch
!=
null
)
{
if
(
joinBatch
!=
null
)
{
// will happen only on topTableFilter since j
batch.next
does not call join.next()
// will happen only on topTableFilter since j
oinBatch.next()
does not call join.next()
return
joinBatch
.
next
();
return
joinBatch
.
next
();
}
}
if
(
state
==
AFTER_LAST
)
{
if
(
state
==
AFTER_LAST
)
{
...
@@ -722,6 +747,14 @@ public class TableFilter implements ColumnResolver {
...
@@ -722,6 +747,14 @@ public class TableFilter implements ColumnResolver {
if
(
index
!=
null
)
{
if
(
index
!=
null
)
{
buff
.
append
(
'\n'
);
buff
.
append
(
'\n'
);
StatementBuilder
planBuff
=
new
StatementBuilder
();
StatementBuilder
planBuff
=
new
StatementBuilder
();
if
(
joinBatch
!=
null
)
{
if
(
joinBatch
.
isBatchedIndex
(
joinFilterId
))
{
planBuff
.
append
(
"batched:true "
);
}
else
if
(
joinFilterId
!=
0
)
{
// top table filter does not need to fake batching, it works as usual in this case
planBuff
.
append
(
"batched:fake "
);
}
}
planBuff
.
append
(
index
.
getPlanSQL
());
planBuff
.
append
(
index
.
getPlanSQL
());
if
(
indexConditions
.
size
()
>
0
)
{
if
(
indexConditions
.
size
()
>
0
)
{
planBuff
.
append
(
": "
);
planBuff
.
append
(
": "
);
...
...
h2/src/test/org/h2/test/db/TestTableEngines.java
浏览文件 @
9ebce112
...
@@ -1118,6 +1118,11 @@ public class TestTableEngines extends TestBase {
...
@@ -1118,6 +1118,11 @@ public class TestTableEngines extends TestBase {
searchRows
.
add
(
first
);
searchRows
.
add
(
first
);
searchRows
.
add
(
last
);
searchRows
.
add
(
last
);
}
}
@Override
public
void
reset
()
{
searchRows
.
clear
();
}
};
};
}
}
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论