Skip to content
项目
群组
代码片段
帮助
正在加载...
帮助
为 GitLab 提交贡献
登录/注册
切换导航
H
h2database
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
计划
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
Administrator
h2database
Commits
5a428e25
提交
5a428e25
authored
6月 05, 2017
作者:
Noel Grandin
提交者:
GitHub
6月 05, 2017
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #522 from stumc/Issue#479
Issue#479 Allow non-recursive CTEs (WITH statements)
上级
5592109a
c4fb1dbf
显示空白字符变更
内嵌
并排
正在显示
9 个修改的文件
包含
287 行增加
和
69 行删除
+287
-69
help.csv
h2/src/docsrc/help/help.csv
+16
-4
changelog.html
h2/src/docsrc/html/changelog.html
+2
-0
Parser.java
h2/src/main/org/h2/command/Parser.java
+89
-57
help.csv
h2/src/main/org/h2/res/help.csv
+2
-2
RangeTable.java
h2/src/main/org/h2/table/RangeTable.java
+1
-1
TableView.java
h2/src/main/org/h2/table/TableView.java
+32
-0
TestAll.java
h2/src/test/org/h2/test/TestAll.java
+3
-0
TestGeneralCommonTableQueries.java
...rc/test/org/h2/test/db/TestGeneralCommonTableQueries.java
+137
-0
dictionary.txt
h2/src/tools/org/h2/build/doc/dictionary.txt
+5
-5
没有找到文件。
h2/src/docsrc/help/help.csv
浏览文件 @
5a428e25
...
...
@@ -193,12 +193,16 @@ SHOW TABLES
"
"Commands (DML)","WITH","
WITH [ RECURSIVE ]
name ( columnName [,...] )
AS ( select )
WITH [ RECURSIVE ]
{ name [( columnName [,...] )]
AS ( select )
[,...] }
select
","
Can be used to create a recursive query. The first select has to be a UNION.
Currently only one result set can be referred to by name.
Can be used to create a recursive query.
For recursive queries the first select has to be a UNION.
Non-recursive queries are also supported.
One or more common table entries can be use referred to by name..
Column name declarations are now optional - the column names will be inferred from the named select queries.
Positional parameters are not currently supported.
","
WITH RECURSIVE t(n) AS (
SELECT 1
...
...
@@ -208,6 +212,14 @@ WITH RECURSIVE t(n) AS (
WHERE n < 100
)
SELECT sum(n) FROM t;
","
WITH t1 AS (
SELECT 1 AS FIRST_COLUMN
),
t2 AS (
SELECT FIRST_COLUMN+1 AS FIRST_COLUMN FROM t1
)
SELECT sum(FIRST_COLUMN) FROM t2;
"
"Commands (DDL)","ALTER INDEX RENAME","
...
...
h2/src/docsrc/html/changelog.html
浏览文件 @
5a428e25
...
...
@@ -31,6 +31,8 @@ Change Log
</li>
<li>
Issue #472: Support CREATE SEQUENCE ... ORDER as a NOOP for Oracle compatibility
</li>
<li>
Issue #479: Allow non-recursive Common Table Expressions (CTE)
</li>
</ul>
<h2>
Version 1.4.195 (2017-04-23)
</h2>
...
...
h2/src/main/org/h2/command/Parser.java
浏览文件 @
5a428e25
...
...
@@ -17,6 +17,8 @@ import java.util.Collections;
import
java.util.Comparator
;
import
java.util.HashSet
;
import
java.util.LinkedHashSet
;
import
java.util.List
;
import
org.h2.api.ErrorCode
;
import
org.h2.api.Trigger
;
import
org.h2.command.ddl.AlterIndexRename
;
...
...
@@ -4855,16 +4857,33 @@ public class Parser {
}
private
Query
parseWith
()
{
List
<
TableView
>
viewsCreated
=
new
ArrayList
<
TableView
>();
readIf
(
"RECURSIVE"
);
do
{
viewsCreated
.
add
(
parseSingleCommonTableExpression
());
}
while
(
readIf
(
","
));
Query
q
=
parseSelectUnion
();
q
.
setPrepareAlways
(
true
);
return
q
;
}
private
TableView
parseSingleCommonTableExpression
()
{
String
tempViewName
=
readIdentifierWithSchema
();
Schema
schema
=
getSchema
();
Table
recursiveTable
;
read
(
"("
);
ArrayList
<
Column
>
columns
=
New
.
arrayList
();
String
[]
cols
=
parseColumnList
();
String
[]
cols
=
null
;
// column names are now optional - they can be inferred from the named
// query if not supplied
if
(
readIf
(
"("
)){
cols
=
parseColumnList
();
for
(
String
c
:
cols
)
{
// we dont really know the type of the column, so string will have to do
columns
.
add
(
new
Column
(
c
,
Value
.
STRING
));
}
}
Table
old
=
session
.
findLocalTempTable
(
tempViewName
);
if
(
old
!=
null
)
{
if
(!(
old
instanceof
TableView
))
{
...
...
@@ -4878,6 +4897,9 @@ public class Parser {
}
session
.
removeLocalTempTable
(
old
);
}
// this table is created as a work around because recursive
// table expressions need to reference something that look like themselves
// to work (its removed after creation in this method)
CreateTableData
data
=
new
CreateTableData
();
data
.
id
=
database
.
allocateObjectId
();
data
.
columns
=
columns
;
...
...
@@ -4890,7 +4912,7 @@ public class Parser {
recursiveTable
=
schema
.
createTable
(
data
);
session
.
addLocalTempTable
(
recursiveTable
);
String
querySQL
;
Column
[]
columnTemplates
=
new
Column
[
cols
.
length
]
;
List
<
Column
>
columnTemplateList
=
new
ArrayList
<
Column
>()
;
try
{
read
(
"AS"
);
read
(
"("
);
...
...
@@ -4899,22 +4921,32 @@ public class Parser {
withQuery
.
prepare
();
querySQL
=
StringUtils
.
cache
(
withQuery
.
getPlanSQL
());
ArrayList
<
Expression
>
withExpressions
=
withQuery
.
getExpressions
();
for
(
int
i
=
0
;
i
<
cols
.
length
;
++
i
)
{
columnTemplates
[
i
]
=
new
Column
(
cols
[
i
],
withExpressions
.
get
(
i
).
getType
());
for
(
int
i
=
0
;
i
<
withExpressions
.
size
();
++
i
)
{
String
columnName
=
cols
!=
null
?
cols
[
i
]
:
withExpressions
.
get
(
i
).
getColumnName
();
columnTemplateList
.
add
(
new
Column
(
columnName
,
withExpressions
.
get
(
i
).
getType
()));
}
}
finally
{
session
.
removeLocalTempTable
(
recursiveTable
);
}
int
id
=
database
.
allocateObjectId
();
TableView
view
=
new
TableView
(
schema
,
id
,
tempViewName
,
querySQL
,
parameters
,
columnTemplates
,
session
,
true
);
boolean
isRecursive
=
true
;
TableView
view
=
null
;
do
{
view
=
new
TableView
(
schema
,
id
,
tempViewName
,
querySQL
,
parameters
,
columnTemplateList
.
toArray
(
new
Column
[
0
]),
session
,
isRecursive
);
if
(
view
.
isRecursiveQueryDetected
()!=
isRecursive
){
isRecursive
=
view
.
isRecursiveQueryDetected
();
view
=
null
;
continue
;
}
}
while
(
view
==
null
);
view
.
setTableExpression
(
true
);
view
.
setTemporary
(
true
);
session
.
addLocalTempTable
(
view
);
view
.
setOnCommitDrop
(
true
);
Query
q
=
parseSelectUnion
();
q
.
setPrepareAlways
(
true
);
return
q
;
return
view
;
}
private
CreateView
parseCreateView
(
boolean
force
,
boolean
orReplace
)
{
...
...
h2/src/main/org/h2/res/help.csv
浏览文件 @
5a428e25
...
...
@@ -67,8 +67,8 @@ SHOW { SCHEMAS | TABLES [ FROM schemaName ] |
","
Lists the schemas, tables, or the columns of a table."
"Commands (DML)","WITH","
WITH [ RECURSIVE ]
name ( columnName [,...] )
AS ( select )
WITH [ RECURSIVE ]
{ name [( columnName [,...] )]
AS ( select )
[,...] }
select
","
Can be used to create a recursive query."
...
...
h2/src/main/org/h2/table/RangeTable.java
浏览文件 @
5a428e25
...
...
@@ -144,7 +144,7 @@ public class RangeTable extends Table {
@Override
public
TableType
getTableType
()
{
throw
DbException
.
throwInternalError
(
toString
())
;
return
TableType
.
SYSTEM_TABLE
;
}
@Override
...
...
h2/src/main/org/h2/table/TableView.java
浏览文件 @
5a428e25
...
...
@@ -57,6 +57,7 @@ public class TableView extends Table {
private
Query
topQuery
;
private
ResultInterface
recursiveResult
;
private
boolean
tableExpression
;
private
boolean
isRecursiveQueryDetected
;
public
TableView
(
Schema
schema
,
int
id
,
String
name
,
String
querySQL
,
ArrayList
<
Parameter
>
params
,
Column
[]
columnTemplates
,
Session
session
,
...
...
@@ -94,6 +95,7 @@ public class TableView extends Table {
this
.
querySQL
=
querySQL
;
this
.
columnTemplates
=
columnTemplates
;
this
.
recursive
=
recursive
;
this
.
isRecursiveQueryDetected
=
false
;
index
=
new
ViewIndex
(
this
,
querySQL
,
params
,
recursive
);
initColumnsAndTables
(
session
);
}
...
...
@@ -206,6 +208,11 @@ public class TableView extends Table {
// if it can't be compiled, then it's a 'zero column table'
// this avoids problems when creating the view when opening the
// database
// if it can not be compiled - it could also be a recursive common table expression query
if
(
isRecursiveQueryExceptionDetected
(
createException
)){
this
.
isRecursiveQueryDetected
=
true
;
}
tables
=
New
.
arrayList
();
cols
=
new
Column
[
0
];
if
(
recursive
&&
columnTemplates
!=
null
)
{
...
...
@@ -666,4 +673,29 @@ public class TableView extends Table {
}
}
/**
* If query recursion is detected (for recursion detection)
* @return is Recursive Query Flag Set
*/
public
boolean
isRecursiveQueryDetected
(){
return
isRecursiveQueryDetected
;
}
/**
* If query an exception indicates query recursion
* @return is Recursive Query Exception Detected
*/
private
boolean
isRecursiveQueryExceptionDetected
(
DbException
exception
){
if
(
exception
==
null
){
return
false
;
}
if
(
exception
.
getErrorCode
()!=
ErrorCode
.
TABLE_OR_VIEW_NOT_FOUND_1
){
return
false
;
}
if
(!
exception
.
getMessage
().
contains
(
"\""
+
this
.
getName
()+
"\""
)){
return
false
;
}
return
true
;
}
}
h2/src/test/org/h2/test/TestAll.java
浏览文件 @
5a428e25
...
...
@@ -11,6 +11,7 @@ import java.util.ArrayList;
import
java.util.Properties
;
import
java.util.TimerTask
;
import
java.util.concurrent.TimeUnit
;
import
org.h2.Driver
;
import
org.h2.engine.Constants
;
import
org.h2.store.fs.FilePathRec
;
...
...
@@ -37,6 +38,7 @@ import org.h2.test.db.TestExclusive;
import
org.h2.test.db.TestFullText
;
import
org.h2.test.db.TestFunctionOverload
;
import
org.h2.test.db.TestFunctions
;
import
org.h2.test.db.TestGeneralCommonTableQueries
;
import
org.h2.test.db.TestIndex
;
import
org.h2.test.db.TestIndexHints
;
import
org.h2.test.db.TestLargeBlob
;
...
...
@@ -738,6 +740,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
addTest
(
new
TestOutOfMemory
());
addTest
(
new
TestReadOnly
());
addTest
(
new
TestRecursiveQueries
());
addTest
(
new
TestGeneralCommonTableQueries
());
addTest
(
new
TestRights
());
addTest
(
new
TestRunscript
());
addTest
(
new
TestSQLInjection
());
...
...
h2/src/test/org/h2/test/db/TestGeneralCommonTableQueries.java
0 → 100755
浏览文件 @
5a428e25
/*
* Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package
org
.
h2
.
test
.
db
;
import
java.sql.Connection
;
import
java.sql.PreparedStatement
;
import
java.sql.ResultSet
;
import
java.sql.Statement
;
import
org.h2.test.TestBase
;
/**
* Test non-recursive queries using WITH, but more than one common table defined.
*/
public
class
TestGeneralCommonTableQueries
extends
TestBase
{
/**
* Run just this test.
*
* @param a ignored
*/
public
static
void
main
(
String
...
a
)
throws
Exception
{
TestBase
.
createCaller
().
init
().
test
();
}
@Override
public
void
test
()
throws
Exception
{
testSimple
();
testImpliedColumnNames
();
testChainedQuery
();
}
private
void
testSimple
()
throws
Exception
{
deleteDb
(
"commonTableExpressionQueries"
);
Connection
conn
=
getConnection
(
"commonTableExpressionQueries"
);
Statement
stat
;
PreparedStatement
prep
;
ResultSet
rs
;
stat
=
conn
.
createStatement
();
final
String
simple_two_column_query
=
"with "
+
"t1(n) as (select 1 as first) "
+
",t2(n) as (select 2 as first) "
+
"select * from t1 union all select * from t2"
;
rs
=
stat
.
executeQuery
(
simple_two_column_query
);
assertTrue
(
rs
.
next
());
assertEquals
(
1
,
rs
.
getInt
(
1
));
assertTrue
(
rs
.
next
());
assertEquals
(
2
,
rs
.
getInt
(
1
));
assertFalse
(
rs
.
next
());
prep
=
conn
.
prepareStatement
(
simple_two_column_query
);
rs
=
prep
.
executeQuery
();
assertTrue
(
rs
.
next
());
assertEquals
(
1
,
rs
.
getInt
(
1
));
assertTrue
(
rs
.
next
());
assertEquals
(
2
,
rs
.
getInt
(
1
));
assertFalse
(
rs
.
next
());
prep
=
conn
.
prepareStatement
(
"with "
+
"t1(n) as (select 2 as first) "
+
",t2(n) as (select 3 as first) "
+
"select * from t1 union all select * from t2 where n<>?"
);
prep
.
setInt
(
1
,
0
);
// omit no lines since zero is not in list
rs
=
prep
.
executeQuery
();
assertTrue
(
rs
.
next
());
assertEquals
(
2
,
rs
.
getInt
(
1
));
assertTrue
(
rs
.
next
());
assertEquals
(
3
,
rs
.
getInt
(
1
));
assertFalse
(
rs
.
next
());
prep
=
conn
.
prepareStatement
(
"with "
+
"t1(n) as (select 2 as first) "
+
",t2(n) as (select 3 as first) "
+
",t3(n) as (select 4 as first) "
+
"select * from t1 union all select * from t2 union all select * from t3 where n<>?"
);
prep
.
setInt
(
1
,
4
);
// omit 4 line (last)
rs
=
prep
.
executeQuery
();
assertTrue
(
rs
.
next
());
assertEquals
(
2
,
rs
.
getInt
(
1
));
assertTrue
(
rs
.
next
());
assertEquals
(
3
,
rs
.
getInt
(
1
));
assertFalse
(
rs
.
next
());
conn
.
close
();
deleteDb
(
"commonTableExpressionQueries"
);
}
private
void
testImpliedColumnNames
()
throws
Exception
{
deleteDb
(
"commonTableExpressionQueries"
);
Connection
conn
=
getConnection
(
"commonTableExpressionQueries"
);
PreparedStatement
prep
;
ResultSet
rs
;
prep
=
conn
.
prepareStatement
(
"with "
+
"t1 as (select 2 as first_col) "
+
",t2 as (select first_col+1 from t1) "
+
",t3 as (select 4 as first_col) "
+
"select * from t1 union all select * from t2 union all select * from t3 where first_col<>?"
);
prep
.
setInt
(
1
,
4
);
// omit 4 line (last)
rs
=
prep
.
executeQuery
();
assertTrue
(
rs
.
next
());
assertEquals
(
2
,
rs
.
getInt
(
1
));
assertTrue
(
rs
.
next
());
assertEquals
(
3
,
rs
.
getInt
(
"FIRST_COL"
));
assertFalse
(
rs
.
next
());
assertEquals
(
rs
.
getMetaData
().
getColumnCount
(),
1
);
assertEquals
(
"FIRST_COL"
,
rs
.
getMetaData
().
getColumnLabel
(
1
));
conn
.
close
();
deleteDb
(
"commonTableExpressionQueries"
);
}
private
void
testChainedQuery
()
throws
Exception
{
deleteDb
(
"commonTableExpressionQueries"
);
Connection
conn
=
getConnection
(
"commonTableExpressionQueries"
);
PreparedStatement
prep
;
ResultSet
rs
;
prep
=
conn
.
prepareStatement
(
" WITH t1 AS ("
+
" SELECT 1 AS FIRST_COLUMN"
+
"),"
+
" t2 AS ("
+
" SELECT FIRST_COLUMN+1 AS FIRST_COLUMN FROM t1 "
+
") "
+
"SELECT sum(FIRST_COLUMN) FROM t2"
);
rs
=
prep
.
executeQuery
();
assertTrue
(
rs
.
next
());
assertEquals
(
2
,
rs
.
getInt
(
1
));
assertFalse
(
rs
.
next
());
conn
.
close
();
deleteDb
(
"commonTableExpressionQueries"
);
}
}
h2/src/tools/org/h2/build/doc/dictionary.txt
浏览文件 @
5a428e25
...
...
@@ -12,7 +12,7 @@ adapting adaptive add added addiction adding addition additional additionally
additions addon addr address addressed addresses adds adeptia adjacent adjust
adjusted adjusts admin administration administrator admins admission ado adopt
advanced advances advantage advantages advised aeiou aejaks aelig aes afaik
affect affected affects affero affine after afterwards again against agar age
affect affected affects affero affine af
finity af
ter afterwards again against agar age
agent agentlib agg aggregate aggregated aggregates aggregating aggressive agile
agrave agree agreeable agreed agreement agreements agrees ahead
ahilmnqbjkcdeopfrsg aid ajax alan alarm ale alefsym alert alessio alexander alfki
...
...
@@ -300,7 +300,7 @@ icmpgt icmple icmplt icmpne ico icon iconified icons iconst icu ide idea ideal
ideas identical identification identified identifier identifiers identify identifying
identities identity idiomatic idiv idle ids idx idxname iee ieee iexcl iface ifeq
ifexists ifge ifgt ifle iflt ifne ifnonnull ifnull iframe ifx ignore ignorecase ignored
ignoredriverprivileges ignorelist ignores ignoring igrave iinc ikura ikvm ikvmc
ignoredriverprivileges ignorelist ignores ignoring ig
nite ig
rave iinc ikura ikvm ikvmc
illegal iload image imageio images imaginary img iml immediately immutable imola imp
impact imperial impersonate impl imple implement implementation implementations
implemented implementing implements implication implicit implicitly implied
...
...
@@ -315,7 +315,7 @@ indemnified indemnify indemnity indent indentation indentations indented indents
independent independently index indexdef indexed indexer indexers indexes indexid
indexing indicate indicated indicates indicating indication indicator indices
indirect indirectly individual individually indkey indonesia industries
inefficient ineg inet inf infin infinite infinity infix inflate inflater info
inefficient ineg inet inf inf
erred inf
in infinite infinity infix inflate inflater info
inform information informational informed informix informs informtn infos
infrastructure infringe infringed infringement infringements infringes infringing
inherent inherit inheritance inherited inherits ini init initial initialization
...
...
@@ -434,7 +434,7 @@ obligation obligations observer obsolete obtain obtained obtains obviously
occasionally occupied occupies occupy occur occurred occurrence occurrences occurs
ocirc octal octet october octype odbc odbcad odd odg off offending offer offered
offering offers office official offline offset offsets often ogc ograve ohloh oid okay
okra olap olapsys old older oldest oline oliver olivier omega omicron omissions
okra olap olapsys old older oldest oline oliver olivier omega omicron omissions
omit
omitted omitting once onchange onclick one ones onfocus ongoing onkeydown onkeyup
online onload only onmousedown onmousemove onmouseout onmouseover onmouseup
onreadystatechange onresize onscroll onsubmit onto ontology ontoprise oome oops
...
...
@@ -477,7 +477,7 @@ pointing points poker poland polar pole poleposition policies policy polish poll
polling polski poly polygon pom pondered poodle pool poolable pooled pooling
pools poor poormans pop popular populate populated population popup port
portability portable portal portals ported porting portion portions portlet ports
portugal portugu pos position positioned positions positive pospichal possibility
portugal portugu pos position positioned positions positi
onal positi
ve pospichal possibility
possible possibly post postal postfix postgre postgres postgresql posting
postmaster potential potentially poultry pound pow power powerful poweroff
practicable practice prd pre prec precedence precision precisions predicate
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论