Skip to content
项目
群组
代码片段
帮助
正在加载...
帮助
为 GitLab 提交贡献
登录/注册
切换导航
H
h2database
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
计划
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
Administrator
h2database
Commits
1e25c068
提交
1e25c068
authored
14 年前
作者:
Thomas Mueller
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Experimental feature to support nested joins.
上级
81de9e71
隐藏空白字符变更
内嵌
并排
正在显示
9 个修改的文件
包含
366 行增加
和
55 行删除
+366
-55
Parser.java
h2/src/main/org/h2/command/Parser.java
+30
-8
Optimizer.java
h2/src/main/org/h2/command/dml/Optimizer.java
+1
-1
Select.java
h2/src/main/org/h2/command/dml/Select.java
+12
-5
SysProperties.java
h2/src/main/org/h2/constant/SysProperties.java
+6
-0
PlanItem.java
h2/src/main/org/h2/table/PlanItem.java
+9
-0
TableFilter.java
h2/src/main/org/h2/table/TableFilter.java
+135
-24
TestAll.java
h2/src/test/org/h2/test/TestAll.java
+3
-0
TestNestedJoins.java
h2/src/test/org/h2/test/db/TestNestedJoins.java
+170
-0
todo.txt
h2/src/test/org/h2/test/todo/todo.txt
+0
-17
没有找到文件。
h2/src/main/org/h2/command/Parser.java
浏览文件 @
1e25c068
...
...
@@ -1229,7 +1229,7 @@ public class Parser {
if
(
readIf
(
"ON"
))
{
on
=
readExpression
();
}
newTop
.
addJoin
(
top
,
true
,
on
);
newTop
.
addJoin
(
top
,
true
,
fromOuter
,
on
);
top
=
newTop
;
last
=
newTop
;
}
else
if
(
readIf
(
"LEFT"
))
{
...
...
@@ -1241,10 +1241,10 @@ public class Parser {
if
(
readIf
(
"ON"
))
{
on
=
readExpression
();
}
top
.
addJoin
(
join
,
true
,
on
);
top
.
addJoin
(
join
,
true
,
fromOuter
,
on
);
last
=
join
;
}
else
if
(
readIf
(
"FULL"
))
{
throw
this
.
getSyntaxError
();
throw
getSyntaxError
();
}
else
if
(
readIf
(
"INNER"
))
{
read
(
"JOIN"
);
TableFilter
join
=
readTableFilter
(
fromOuter
);
...
...
@@ -1253,7 +1253,11 @@ public class Parser {
if
(
readIf
(
"ON"
))
{
on
=
readExpression
();
}
top
.
addJoin
(
join
,
fromOuter
,
on
);
if
(
SysProperties
.
NESTED_JOINS
)
{
top
.
addJoin
(
join
,
false
,
fromOuter
,
on
);
}
else
{
top
.
addJoin
(
join
,
fromOuter
,
fromOuter
,
on
);
}
last
=
join
;
}
else
if
(
readIf
(
"JOIN"
))
{
TableFilter
join
=
readTableFilter
(
fromOuter
);
...
...
@@ -1262,12 +1266,20 @@ public class Parser {
if
(
readIf
(
"ON"
))
{
on
=
readExpression
();
}
top
.
addJoin
(
join
,
fromOuter
,
on
);
if
(
SysProperties
.
NESTED_JOINS
)
{
top
.
addJoin
(
join
,
false
,
fromOuter
,
on
);
}
else
{
top
.
addJoin
(
join
,
fromOuter
,
fromOuter
,
on
);
}
last
=
join
;
}
else
if
(
readIf
(
"CROSS"
))
{
read
(
"JOIN"
);
TableFilter
join
=
readTableFilter
(
fromOuter
);
top
.
addJoin
(
join
,
fromOuter
,
null
);
if
(
SysProperties
.
NESTED_JOINS
)
{
top
.
addJoin
(
join
,
false
,
fromOuter
,
null
);
}
else
{
top
.
addJoin
(
join
,
fromOuter
,
fromOuter
,
null
);
}
last
=
join
;
}
else
if
(
readIf
(
"NATURAL"
))
{
read
(
"JOIN"
);
...
...
@@ -1296,7 +1308,11 @@ public class Parser {
}
}
}
top
.
addJoin
(
join
,
fromOuter
,
on
);
if
(
SysProperties
.
NESTED_JOINS
)
{
top
.
addJoin
(
join
,
false
,
fromOuter
,
on
);
}
else
{
top
.
addJoin
(
join
,
fromOuter
,
fromOuter
,
on
);
}
last
=
join
;
}
else
{
break
;
...
...
@@ -1543,6 +1559,12 @@ public class Parser {
command
.
addTableFilter
(
top
,
true
);
boolean
isOuter
=
false
;
while
(
true
)
{
for
(
TableFilter
n
=
top
.
getNestedJoin
();
n
!=
null
;
n
=
n
.
getNestedJoin
())
{
command
.
addTableFilter
(
n
,
false
);
for
(
TableFilter
j
=
n
.
getJoin
();
j
!=
null
;
j
=
j
.
getJoin
())
{
command
.
addTableFilter
(
j
,
false
);
}
}
TableFilter
join
=
top
.
getJoin
();
if
(
join
==
null
)
{
break
;
...
...
@@ -1810,7 +1832,7 @@ public class Parser {
int
idx
=
filters
.
indexOf
(
rightFilter
);
if
(
idx
>=
0
)
{
filters
.
remove
(
idx
);
leftFilter
.
addJoin
(
rightFilter
,
true
,
r
);
leftFilter
.
addJoin
(
rightFilter
,
true
,
false
,
r
);
}
else
{
rightFilter
.
mapAndAddFilter
(
r
);
}
...
...
This diff is collapsed.
Click to expand it.
h2/src/main/org/h2/command/dml/Optimizer.java
浏览文件 @
1e25c068
...
...
@@ -232,7 +232,7 @@ public class Optimizer {
TableFilter
[]
f2
=
bestPlan
.
getFilters
();
topFilter
=
f2
[
0
];
for
(
int
i
=
0
;
i
<
f2
.
length
-
1
;
i
++)
{
f2
[
i
].
addJoin
(
f2
[
i
+
1
],
false
,
null
);
f2
[
i
].
addJoin
(
f2
[
i
+
1
],
false
,
false
,
null
);
}
for
(
TableFilter
f
:
f2
)
{
PlanItem
item
=
bestPlan
.
getItem
(
f
);
...
...
This diff is collapsed.
Click to expand it.
h2/src/main/org/h2/command/dml/Select.java
浏览文件 @
1e25c068
...
...
@@ -845,9 +845,15 @@ public class Select extends Query {
optimizer
.
optimize
();
topTableFilter
=
optimizer
.
getTopFilter
();
double
planCost
=
optimizer
.
getCost
();
setEvaluatableRecursive
(
topTableFilter
);
TableFilter
f
=
topTableFilter
;
while
(
f
!=
null
)
{
topTableFilter
.
prepare
();
return
planCost
;
}
private
void
setEvaluatableRecursive
(
TableFilter
f
)
{
for
(;
f
!=
null
;
f
=
f
.
getJoin
())
{
f
.
setEvaluatable
(
f
,
true
);
if
(
condition
!=
null
)
{
condition
.
setEvaluatable
(
f
,
true
);
...
...
@@ -879,10 +885,11 @@ public class Select extends Query {
for
(
Expression
e
:
expressions
)
{
e
.
setEvaluatable
(
f
,
true
);
}
f
=
f
.
getJoin
();
TableFilter
n
=
f
.
getNestedJoin
();
if
(
n
!=
null
)
{
setEvaluatableRecursive
(
n
);
}
}
topTableFilter
.
prepare
();
return
planCost
;
}
public
String
getPlanSQL
()
{
...
...
This diff is collapsed.
Click to expand it.
h2/src/main/org/h2/constant/SysProperties.java
浏览文件 @
1e25c068
...
...
@@ -385,6 +385,12 @@ public class SysProperties {
* The minimum write delay that causes commits to be delayed.
*/
public
static
final
int
MIN_WRITE_DELAY
=
getIntSetting
(
"h2.minWriteDelay"
,
5
);
/**
* System property <code>h2.nestedJoins</code> (default: false).<br />
* Whether nested joins should be supported.
*/
public
static
final
boolean
NESTED_JOINS
=
getBooleanSetting
(
"h2.nestedJoins"
,
false
);
/**
* System property <code>h2.nioLoadMapped</code> (default: false).<br />
...
...
This diff is collapsed.
Click to expand it.
h2/src/main/org/h2/table/PlanItem.java
浏览文件 @
1e25c068
...
...
@@ -21,6 +21,7 @@ public class PlanItem {
private
Index
index
;
private
PlanItem
joinPlan
;
private
PlanItem
nestedJoinPlan
;
void
setIndex
(
Index
index
)
{
this
.
index
=
index
;
...
...
@@ -33,9 +34,17 @@ public class PlanItem {
PlanItem
getJoinPlan
()
{
return
joinPlan
;
}
PlanItem
getNestedJoinPlan
()
{
return
nestedJoinPlan
;
}
void
setJoinPlan
(
PlanItem
joinPlan
)
{
this
.
joinPlan
=
joinPlan
;
}
void
setNestedJoinPlan
(
PlanItem
nestedJoinPlan
)
{
this
.
nestedJoinPlan
=
nestedJoinPlan
;
}
}
This diff is collapsed.
Click to expand it.
h2/src/main/org/h2/table/TableFilter.java
浏览文件 @
1e25c068
...
...
@@ -70,8 +70,22 @@ public class TableFilter implements ColumnResolver {
private
SearchRow
currentSearchRow
;
private
Row
current
;
private
int
state
;
/**
* The joined table (if there is one).
*/
private
TableFilter
join
;
private
boolean
outerJoin
;
/**
* Whether this table is an outer join.
*/
private
boolean
joinOuter
;
/**
* The nested joined table (if there is one).
*/
private
TableFilter
nestedJoin
;
private
ArrayList
<
Column
>
naturalJoinColumns
;
private
boolean
foundOne
;
private
Expression
fullCondition
;
...
...
@@ -153,6 +167,14 @@ public class TableFilter implements ColumnResolver {
// x (x.a=10); y (x.b=y.b) - see issue 113
item
.
cost
-=
item
.
cost
*
indexConditions
.
size
()
/
100
/
level
;
}
if
(
nestedJoin
!=
null
)
{
setEvaluatable
(
nestedJoin
);
item
.
setNestedJoinPlan
(
nestedJoin
.
getBestPlanItem
(
s
,
level
));
int
todoNestJoinCostWrong
;
// TODO optimizer: calculate cost of a join: should use separate
// expected row number and lookup cost
item
.
cost
+=
item
.
cost
*
item
.
getNestedJoinPlan
().
cost
;
}
if
(
join
!=
null
)
{
setEvaluatable
(
join
);
item
.
setJoinPlan
(
join
.
getBestPlanItem
(
s
,
level
));
...
...
@@ -170,6 +192,10 @@ public class TableFilter implements ColumnResolver {
if
(
e
!=
null
)
{
e
.
setEvaluatable
(
this
,
true
);
}
TableFilter
n
=
join
.
getNestedJoin
();
if
(
n
!=
null
)
{
setEvaluatable
(
n
);
}
join
=
join
.
getJoin
();
}
while
(
join
!=
null
);
}
...
...
@@ -181,6 +207,11 @@ public class TableFilter implements ColumnResolver {
*/
public
void
setPlanItem
(
PlanItem
item
)
{
setIndex
(
item
.
getIndex
());
if
(
nestedJoin
!=
null
)
{
if
(
item
.
getNestedJoinPlan
()
!=
null
)
{
nestedJoin
.
setPlanItem
(
item
.
getNestedJoinPlan
());
}
}
if
(
join
!=
null
)
{
if
(
item
.
getJoinPlan
()
!=
null
)
{
join
.
setPlanItem
(
item
.
getJoinPlan
());
...
...
@@ -204,6 +235,12 @@ public class TableFilter implements ColumnResolver {
}
}
}
if
(
nestedJoin
!=
null
)
{
if
(
SysProperties
.
CHECK
&&
nestedJoin
==
this
)
{
DbException
.
throwInternalError
(
"self join"
);
}
nestedJoin
.
prepare
();
}
if
(
join
!=
null
)
{
if
(
SysProperties
.
CHECK
&&
join
==
this
)
{
DbException
.
throwInternalError
(
"self join"
);
...
...
@@ -226,6 +263,9 @@ public class TableFilter implements ColumnResolver {
public
void
startQuery
(
Session
s
)
{
this
.
session
=
s
;
scanCount
=
0
;
if
(
nestedJoin
!=
null
)
{
nestedJoin
.
startQuery
(
s
);
}
if
(
join
!=
null
)
{
join
.
startQuery
(
s
);
}
...
...
@@ -235,6 +275,9 @@ public class TableFilter implements ColumnResolver {
* Reset to the current position.
*/
public
void
reset
()
{
if
(
nestedJoin
!=
null
)
{
nestedJoin
.
reset
();
}
if
(
join
!=
null
)
{
join
.
reset
();
}
...
...
@@ -253,15 +296,35 @@ public class TableFilter implements ColumnResolver {
}
else
if
(
state
==
BEFORE_FIRST
)
{
cursor
.
find
(
session
,
indexConditions
);
if
(!
cursor
.
isAlwaysFalse
())
{
if
(
nestedJoin
!=
null
)
{
nestedJoin
.
reset
();
}
if
(
join
!=
null
)
{
join
.
reset
();
}
}
}
else
{
// state == FOUND ||
LAST
_ROW
// state == FOUND ||
NULL
_ROW
// the last row was ok - try next row of the join
if
(
join
!=
null
&&
join
.
next
())
{
return
true
;
if
(
nestedJoin
!=
null
)
{
if
(
join
==
null
)
{
if
(
nestedJoin
.
next
())
{
return
true
;
}
}
else
{
while
(
true
)
{
if
(
nestedJoin
.
next
())
{
if
(
join
.
next
())
{
return
true
;
}
join
.
reset
();
}
}
}
}
else
{
if
(
join
!=
null
&&
join
.
next
())
{
return
true
;
}
}
}
while
(
true
)
{
...
...
@@ -285,14 +348,18 @@ public class TableFilter implements ColumnResolver {
}
// if no more rows found, try the null row (for outer joins only)
if
(
state
==
AFTER_LAST
)
{
if
(
outerJoin
&&
!
foundOne
)
{
state
=
NULL_ROW
;
current
=
table
.
getNullRow
();
currentSearchRow
=
current
;
if
(
joinOuter
&&
!
foundOne
)
{
setNullRow
();
}
else
{
break
;
}
}
if
(
state
==
FOUND
&&
nestedJoin
!=
null
)
{
nestedJoin
.
reset
();
if
(!
nestedJoin
.
next
())
{
continue
;
}
}
if
(!
isOk
(
filterCondition
))
{
continue
;
}
...
...
@@ -318,6 +385,16 @@ public class TableFilter implements ColumnResolver {
state
=
AFTER_LAST
;
return
false
;
}
private
void
setNullRow
()
{
state
=
NULL_ROW
;
current
=
table
.
getNullRow
();
currentSearchRow
=
current
;
if
(
nestedJoin
!=
null
)
{
int
todoRecurse
;
nestedJoin
.
setNullRow
();
}
}
private
void
checkTimeout
()
{
session
.
checkCanceled
();
...
...
@@ -407,26 +484,39 @@ public class TableFilter implements ColumnResolver {
* @param outer if this is an outer join
* @param on the join condition
*/
public
void
addJoin
(
TableFilter
filter
,
boolean
outer
,
Expression
on
)
{
public
void
addJoin
(
TableFilter
filter
,
boolean
outer
,
boolean
nested
,
Expression
on
)
{
if
(
on
!=
null
)
{
on
.
mapColumns
(
this
,
0
);
}
if
(
join
==
null
)
{
this
.
join
=
filter
;
filter
.
outerJoin
=
outer
;
if
(
outer
)
{
// convert all inner joins on the right hand side to outer joins
TableFilter
f
=
filter
.
join
;
while
(
f
!=
null
)
{
f
.
outerJoin
=
true
;
f
=
f
.
join
;
}
if
(
nested
&&
SysProperties
.
NESTED_JOINS
)
{
if
(
nestedJoin
!=
null
)
{
throw
DbException
.
throwInternalError
();
}
nestedJoin
=
filter
;
filter
.
joinOuter
=
outer
;
if
(
on
!=
null
)
{
filter
.
mapAndAddFilter
(
on
);
}
}
else
{
join
.
addJoin
(
filter
,
outer
,
on
);
if
(
join
==
null
)
{
join
=
filter
;
filter
.
joinOuter
=
outer
;
if
(!
SysProperties
.
NESTED_JOINS
)
{
if
(
outer
)
{
// convert all inner joins on the right hand side to outer joins
TableFilter
f
=
filter
.
join
;
while
(
f
!=
null
)
{
f
.
joinOuter
=
true
;
f
=
f
.
join
;
}
}
}
if
(
on
!=
null
)
{
filter
.
mapAndAddFilter
(
on
);
}
}
else
{
join
.
addJoin
(
filter
,
outer
,
nested
,
on
);
}
}
}
...
...
@@ -439,6 +529,9 @@ public class TableFilter implements ColumnResolver {
on
.
mapColumns
(
this
,
0
);
addFilterCondition
(
on
,
true
);
on
.
createIndexConditions
(
session
,
this
);
if
(
nestedJoin
!=
null
)
{
nestedJoin
.
mapAndAddFilter
(
on
);
}
if
(
join
!=
null
)
{
join
.
mapAndAddFilter
(
on
);
}
...
...
@@ -454,7 +547,7 @@ public class TableFilter implements ColumnResolver {
* @return true if it is
*/
public
boolean
isJoinOuter
()
{
return
outerJoin
;
return
joinOuter
;
}
/**
...
...
@@ -466,12 +559,15 @@ public class TableFilter implements ColumnResolver {
public
String
getPlanSQL
(
boolean
isJoin
)
{
StringBuilder
buff
=
new
StringBuilder
();
if
(
isJoin
)
{
if
(
outerJoin
)
{
if
(
joinOuter
)
{
buff
.
append
(
"LEFT OUTER JOIN "
);
}
else
{
buff
.
append
(
"INNER JOIN "
);
}
}
if
(
nestedJoin
!=
null
)
{
buff
.
append
(
"("
);
}
buff
.
append
(
table
.
getSQL
());
if
(
alias
!=
null
)
{
buff
.
append
(
' '
).
append
(
Parser
.
quoteIdentifier
(
alias
));
...
...
@@ -490,6 +586,11 @@ public class TableFilter implements ColumnResolver {
String
plan
=
StringUtils
.
quoteRemarkSQL
(
planBuff
.
toString
());
buff
.
append
(
plan
).
append
(
" */"
);
}
if
(
nestedJoin
!=
null
)
{
buff
.
append
(
'\n'
);
buff
.
append
(
nestedJoin
.
getPlanSQL
(
true
));
buff
.
append
(
")"
);
}
if
(
isJoin
)
{
buff
.
append
(
" ON "
);
if
(
joinCondition
==
null
)
{
...
...
@@ -594,9 +695,12 @@ public class TableFilter implements ColumnResolver {
*/
void
optimizeFullCondition
(
boolean
fromOuterJoin
)
{
if
(
fullCondition
!=
null
)
{
fullCondition
.
addFilterConditions
(
this
,
fromOuterJoin
||
outerJoin
);
fullCondition
.
addFilterConditions
(
this
,
fromOuterJoin
||
joinOuter
);
if
(
nestedJoin
!=
null
)
{
nestedJoin
.
optimizeFullCondition
(
fromOuterJoin
||
joinOuter
);
}
if
(
join
!=
null
)
{
join
.
optimizeFullCondition
(
fromOuterJoin
||
outerJoin
);
join
.
optimizeFullCondition
(
fromOuterJoin
||
joinOuter
);
}
}
}
...
...
@@ -615,6 +719,9 @@ public class TableFilter implements ColumnResolver {
if
(
joinCondition
!=
null
)
{
joinCondition
.
setEvaluatable
(
filter
,
b
);
}
if
(
nestedJoin
!=
null
)
{
nestedJoin
.
setEvaluatable
(
filter
,
b
);
}
if
(
join
!=
null
)
{
join
.
setEvaluatable
(
filter
,
b
);
}
...
...
@@ -744,4 +851,8 @@ public class TableFilter implements ColumnResolver {
}
}
public
TableFilter
getNestedJoin
()
{
return
nestedJoin
;
}
}
This diff is collapsed.
Click to expand it.
h2/src/test/org/h2/test/TestAll.java
浏览文件 @
1e25c068
...
...
@@ -38,6 +38,7 @@ import org.h2.test.db.TestMultiConn;
import
org.h2.test.db.TestMultiDimension
;
import
org.h2.test.db.TestMultiThread
;
import
org.h2.test.db.TestMultiThreadedKernel
;
import
org.h2.test.db.TestNestedJoins
;
import
org.h2.test.db.TestOpenClose
;
import
org.h2.test.db.TestOptimizations
;
import
org.h2.test.db.TestOutOfMemory
;
...
...
@@ -302,6 +303,7 @@ java org.h2.test.TestAll timer
// System.setProperty("h2.largeTransactions", "true");
// System.setProperty("h2.lobInDatabase", "true");
// System.setProperty("h2.analyzeAuto", "100");
// System.setProperty("h2.nestedJoins", "true");
int
speedup
;
// System.setProperty("h2.syncMethod", "");
...
...
@@ -516,6 +518,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
new
TestMultiDimension
().
runTest
(
this
);
new
TestMultiThread
().
runTest
(
this
);
new
TestMultiThreadedKernel
().
runTest
(
this
);
new
TestNestedJoins
().
runTest
(
this
);
new
TestOpenClose
().
runTest
(
this
);
new
TestOptimizations
().
runTest
(
this
);
new
TestOutOfMemory
().
runTest
(
this
);
...
...
This diff is collapsed.
Click to expand it.
h2/src/test/org/h2/test/db/TestNestedJoins.java
0 → 100644
浏览文件 @
1e25c068
/*
* Copyright 2004-2010 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package
org
.
h2
.
test
.
db
;
import
java.io.StringReader
;
import
java.sql.Connection
;
import
java.sql.ResultSet
;
import
java.sql.SQLException
;
import
java.sql.Statement
;
import
org.h2.constant.SysProperties
;
import
org.h2.test.TestBase
;
import
org.h2.util.ScriptReader
;
/**
* Tests nested joins and right outer joins.
*/
public
class
TestNestedJoins
extends
TestBase
{
/**
* Run just this test.
*
* @param a ignored
*/
public
static
void
main
(
String
...
a
)
throws
Exception
{
System
.
setProperty
(
"h2.nestedJoins"
,
"true"
);
TestBase
.
createCaller
().
init
().
test
();
}
public
void
test
()
throws
Exception
{
if
(!
SysProperties
.
NESTED_JOINS
)
{
return
;
}
deleteDb
(
"nestedJoins"
);
Connection
conn
=
getConnection
(
"nestedJoins"
);
Statement
stat
=
conn
.
createStatement
();
ResultSet
rs
;
String
sql
;
// not yet supported:
/*
stat.execute("drop table a, b, c");
stat.execute("create table a(x int)");
stat.execute("create table b(x int)");
stat.execute("create table c(x int, y int)");
stat.execute("insert into a values(1), (2)");
stat.execute("insert into b values(3)");
stat.execute("insert into c values(1, 3)");
stat.execute("insert into c values(4, 5)");
rs = stat.executeQuery("explain select * from a left outer join (b left outer join c on b.x = c.y) on a.x = c.x");
assertTrue(rs.next());
sql = cleanRemarks(conn, rs.getString(1));
assertEquals("", sql);
rs = stat.executeQuery("select * from a left outer join (b left outer join c on b.x = c.y) on a.x = c.x");
// expected result: 1 3 1 3; 2 null null null
assertTrue(rs.next());
assertEquals("1", rs.getString(1));
assertEquals("3", rs.getString(2));
assertEquals("1", rs.getString(3));
assertEquals("3", rs.getString(4));
assertTrue(rs.next());
assertEquals("2", rs.getString(1));
assertEquals(null, rs.getString(2));
assertEquals(null, rs.getString(3));
assertEquals(null, rs.getString(4));
assertFalse(rs.next());
*/
stat
.
execute
(
"create table a(x int primary key)"
);
stat
.
execute
(
"insert into a values(0), (1)"
);
stat
.
execute
(
"create table b(x int primary key)"
);
stat
.
execute
(
"insert into b values(0)"
);
stat
.
execute
(
"create table c(x int primary key)"
);
rs
=
stat
.
executeQuery
(
"select a.*, b.*, c.* from a left outer join (b inner join c on b.x = c.x) on a.x = b.x"
);
// expected result: 0, null, null; 1, null, null
assertTrue
(
rs
.
next
());
assertEquals
(
"0"
,
rs
.
getString
(
1
));
assertEquals
(
null
,
rs
.
getString
(
2
));
assertEquals
(
null
,
rs
.
getString
(
3
));
assertTrue
(
rs
.
next
());
assertEquals
(
"1"
,
rs
.
getString
(
1
));
assertEquals
(
null
,
rs
.
getString
(
2
));
assertEquals
(
null
,
rs
.
getString
(
3
));
assertFalse
(
rs
.
next
());
rs
=
stat
.
executeQuery
(
"select * from a left outer join b on a.x = b.x inner join c on b.x = c.x"
);
// expected result: -
assertFalse
(
rs
.
next
());
rs
=
stat
.
executeQuery
(
"select * from a left outer join b on a.x = b.x left outer join c on b.x = c.x"
);
// expected result: 0 0 null; 1 null null
assertTrue
(
rs
.
next
());
assertEquals
(
"0"
,
rs
.
getString
(
1
));
assertEquals
(
"0"
,
rs
.
getString
(
2
));
assertEquals
(
null
,
rs
.
getString
(
3
));
assertTrue
(
rs
.
next
());
assertEquals
(
"1"
,
rs
.
getString
(
1
));
assertEquals
(
null
,
rs
.
getString
(
2
));
assertEquals
(
null
,
rs
.
getString
(
3
));
assertFalse
(
rs
.
next
());
rs
=
stat
.
executeQuery
(
"select * from a left outer join (b inner join c on b.x = c.x) on a.x = b.x"
);
// expected result: 0 null null; 1 null null
assertTrue
(
rs
.
next
());
assertEquals
(
"0"
,
rs
.
getString
(
1
));
assertEquals
(
null
,
rs
.
getString
(
2
));
assertEquals
(
null
,
rs
.
getString
(
3
));
assertTrue
(
rs
.
next
());
assertEquals
(
"1"
,
rs
.
getString
(
1
));
assertEquals
(
null
,
rs
.
getString
(
2
));
assertEquals
(
null
,
rs
.
getString
(
3
));
assertFalse
(
rs
.
next
());
rs
=
stat
.
executeQuery
(
"explain select * from a left outer join (b inner join c on c.x = 1) on a.x = b.x"
);
assertTrue
(
rs
.
next
());
sql
=
cleanRemarks
(
conn
,
rs
.
getString
(
1
));
assertEquals
(
"SELECT A.X, B.X, C.X FROM PUBLIC.A LEFT OUTER JOIN (PUBLIC.B INNER JOIN PUBLIC.C ON (C.X = 1) AND (A.X = B.X)) ON A.X = B.X"
,
sql
);
stat
.
execute
(
"create table test(id int primary key)"
);
stat
.
execute
(
"insert into test values(0), (1), (2)"
);
rs
=
stat
.
executeQuery
(
"select * from test a left outer join (test b inner join test c on b.id = c.id - 2) on a.id = b.id + 1"
);
// drop table test;
// create table test(id int primary key);
// insert into test values(0), (1), (2);
// select * from test a left outer join (test b inner join test c on b.id = c.id - 2) on a.id = b.id + 1;
// expected result: 0 null null; 1 0 2; 2 null null
assertTrue
(
rs
.
next
());
assertEquals
(
"0"
,
rs
.
getString
(
1
));
assertEquals
(
null
,
rs
.
getString
(
2
));
assertEquals
(
null
,
rs
.
getString
(
3
));
assertTrue
(
rs
.
next
());
assertEquals
(
"1"
,
rs
.
getString
(
1
));
assertEquals
(
"0"
,
rs
.
getString
(
2
));
assertEquals
(
"2"
,
rs
.
getString
(
3
));
assertTrue
(
rs
.
next
());
assertEquals
(
"2"
,
rs
.
getString
(
1
));
assertEquals
(
null
,
rs
.
getString
(
2
));
assertEquals
(
null
,
rs
.
getString
(
3
));
assertFalse
(
rs
.
next
());
// while (rs.next()) {
// for (int i = 0; i < rs.getMetaData().getColumnCount(); i++) {
// System.out.print(rs.getString(i + 1) + " ");
// }
// System.out.println();
// }
conn
.
close
();
deleteDb
(
"nestedJoins"
);
}
private
String
cleanRemarks
(
Connection
conn
,
String
sql
)
throws
SQLException
{
ScriptReader
r
=
new
ScriptReader
(
new
StringReader
(
sql
));
r
.
setSkipRemarks
(
true
);
sql
=
r
.
readStatement
();
sql
=
sql
.
replaceAll
(
"\\n"
,
" "
);
// sql = sql.replaceAll("\\/\\*.*\\*\\/", "");
while
(
sql
.
indexOf
(
" "
)
>=
0
)
{
sql
=
sql
.
replaceAll
(
" "
,
" "
);
}
return
sql
;
}
}
This diff is collapsed.
Click to expand it.
h2/src/test/org/h2/test/todo/todo.txt
浏览文件 @
1e25c068
...
...
@@ -2,23 +2,6 @@ Nested Outer Joins
-----------------
Example:
drop table a, b, c;
create table a(x int primary key);
insert into a values(0), (1);
create table b(x int primary key);
insert into b values(0);
create table c(x int primary key);
select * from a left outer join b on a.x = b.x inner join c on b.x = c.x;
select * from a left outer join b on a.x = b.x left outer join c on b.x = c.x;
select * from a left outer join (b inner join c on b.x = c.x) on a.x = b.x;
select a.x, b_x, c_x from a left outer join (select b.x b_x, c.x c_x from b inner join c on b.x = c.x) on a.x = b_x;
drop table test;
create table test(id int primary key);
insert into test values(0), (1), (2);
select * from test a left outer join (test b inner join test c on b.id = c.id - 2) on a.id = b.id + 1;
select a.id, b_id, c_id from test a left outer join (select b.id b_id, c.id c_id from test b inner join test c on b.id = c.id - 2) on a.id = b_id + 1;
create table a(x int);
create table b(x int);
create table c(x int, y int);
...
...
This diff is collapsed.
Click to expand it.
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论