Skip to content
项目
群组
代码片段
帮助
正在加载...
帮助
为 GitLab 提交贡献
登录/注册
切换导航
H
h2database
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
计划
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
Administrator
h2database
Commits
230a1fcb
提交
230a1fcb
authored
5月 08, 2018
作者:
Noel Grandin
浏览文件
操作
浏览文件
下载
差异文件
Merge remote-tracking branch 'upstream/master' into testing_pagestore
上级
77fd8d37
1484337a
显示空白字符变更
内嵌
并排
正在显示
20 个修改的文件
包含
363 行增加
和
187 行删除
+363
-187
Parser.java
h2/src/main/org/h2/command/Parser.java
+4
-4
Select.java
h2/src/main/org/h2/command/dml/Select.java
+155
-76
Aggregate.java
h2/src/main/org/h2/expression/Aggregate.java
+7
-8
AggregateDataHistogram.java
h2/src/main/org/h2/expression/AggregateDataHistogram.java
+4
-3
ExpressionColumn.java
h2/src/main/org/h2/expression/ExpressionColumn.java
+5
-8
JavaAggregate.java
h2/src/main/org/h2/expression/JavaAggregate.java
+8
-11
DataUtils.java
h2/src/main/org/h2/mvstore/DataUtils.java
+0
-33
MVMap.java
h2/src/main/org/h2/mvstore/MVMap.java
+1
-1
MVPrimaryIndex.java
h2/src/main/org/h2/mvstore/db/MVPrimaryIndex.java
+2
-2
TransactionMap.java
h2/src/main/org/h2/mvstore/tx/TransactionMap.java
+3
-1
ValueHashMap.java
h2/src/main/org/h2/util/ValueHashMap.java
+71
-3
ValueArray.java
h2/src/main/org/h2/value/ValueArray.java
+17
-12
ValueDouble.java
h2/src/main/org/h2/value/ValueDouble.java
+6
-2
ValueFloat.java
h2/src/main/org/h2/value/ValueFloat.java
+9
-3
TestBase.java
h2/src/test/org/h2/test/TestBase.java
+13
-0
AbstractBaseForCommonTableExpressions.java
...org/h2/test/db/AbstractBaseForCommonTableExpressions.java
+22
-8
TestGeneralCommonTableQueries.java
...rc/test/org/h2/test/db/TestGeneralCommonTableQueries.java
+4
-4
TestPersistentCommonTableExpressions.java
.../org/h2/test/db/TestPersistentCommonTableExpressions.java
+5
-5
testScript.sql
h2/src/test/org/h2/test/scripts/testScript.sql
+5
-3
TestValue.java
h2/src/test/org/h2/test/unit/TestValue.java
+22
-0
没有找到文件。
h2/src/main/org/h2/command/Parser.java
浏览文件 @
230a1fcb
...
...
@@ -5320,7 +5320,7 @@ public class Parser {
if
(
isPersistent
)
{
db
.
addSchemaObject
(
targetSession
,
view
);
view
.
lock
(
targetSession
,
true
,
true
);
targetSession
.
getDatabase
()
.
removeSchemaObject
(
targetSession
,
view
);
db
.
removeSchemaObject
(
targetSession
,
view
);
}
else
{
session
.
removeLocalTempTable
(
view
);
}
...
...
@@ -5330,7 +5330,7 @@ public class Parser {
isPersistent
);
}
// both removeSchemaObject and removeLocalTempTable hold meta locks
targetSession
.
getDatabase
()
.
unlockMeta
(
targetSession
);
db
.
unlockMeta
(
targetSession
);
}
view
.
setTableExpression
(
true
);
view
.
setTemporary
(!
isPersistent
);
...
...
@@ -5929,8 +5929,8 @@ public class Parser {
boolean
isDualTable
(
String
tableName
)
{
return
((
schemaName
==
null
||
equalsToken
(
schemaName
,
"SYS"
))
&&
equalsToken
(
"DUAL"
,
tableName
))
||
(
database
.
getMode
().
sysDummy1
&&
(
schemaName
==
null
||
equalsToken
(
schemaName
,
"SYSIBM"
))
)
&&
equalsToken
(
"SYSDUMMY1"
,
tableName
);
||
(
database
.
getMode
().
sysDummy1
&&
(
schemaName
==
null
||
equalsToken
(
schemaName
,
"SYSIBM"
))
&&
equalsToken
(
"SYSDUMMY1"
,
tableName
)
)
;
}
private
Table
readTableOrView
()
{
...
...
h2/src/main/org/h2/command/dml/Select.java
浏览文件 @
230a1fcb
...
...
@@ -5,6 +5,11 @@
*/
package
org
.
h2
.
command
.
dml
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.HashMap
;
import
java.util.HashSet
;
import
java.util.Map
;
import
org.h2.api.ErrorCode
;
import
org.h2.api.Trigger
;
import
org.h2.command.CommandInterface
;
...
...
@@ -12,23 +17,39 @@ import org.h2.engine.Constants;
import
org.h2.engine.Database
;
import
org.h2.engine.Session
;
import
org.h2.engine.SysProperties
;
import
org.h2.expression.*
;
import
org.h2.expression.Alias
;
import
org.h2.expression.Comparison
;
import
org.h2.expression.ConditionAndOr
;
import
org.h2.expression.Expression
;
import
org.h2.expression.ExpressionColumn
;
import
org.h2.expression.ExpressionVisitor
;
import
org.h2.expression.Parameter
;
import
org.h2.index.Cursor
;
import
org.h2.index.Index
;
import
org.h2.index.IndexType
;
import
org.h2.message.DbException
;
import
org.h2.result.*
;
import
org.h2.table.*
;
import
org.h2.util.*
;
import
org.h2.result.LazyResult
;
import
org.h2.result.LocalResult
;
import
org.h2.result.ResultInterface
;
import
org.h2.result.ResultTarget
;
import
org.h2.result.Row
;
import
org.h2.result.SearchRow
;
import
org.h2.result.SortOrder
;
import
org.h2.table.Column
;
import
org.h2.table.ColumnResolver
;
import
org.h2.table.IndexColumn
;
import
org.h2.table.JoinBatch
;
import
org.h2.table.Table
;
import
org.h2.table.TableFilter
;
import
org.h2.table.TableView
;
import
org.h2.util.ColumnNamer
;
import
org.h2.util.StatementBuilder
;
import
org.h2.util.StringUtils
;
import
org.h2.util.Utils
;
import
org.h2.value.Value
;
import
org.h2.value.ValueArray
;
import
org.h2.value.ValueNull
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.HashMap
;
import
java.util.HashSet
;
/**
* This class represents a simple SELECT statement.
*
...
...
@@ -81,9 +102,21 @@ public class Select extends Query {
boolean
[]
groupByExpression
;
/**
* The
current group-by values
.
* The
array of current group-by expression data e.g. AggregateData
.
*/
HashMap
<
Expression
,
Object
>
currentGroup
;
Object
[]
currentGroupByExprData
;
/**
* Maps an expression object to an index, to use in accessing the Object[] pointed to by groupByData.
*/
final
HashMap
<
Expression
,
Integer
>
exprToIndexInGroupByData
=
new
HashMap
<>();
/**
* Map of group-by key to group-by expression data e.g. AggregateData
*/
private
HashMap
<
Value
,
Object
[]>
groupByData
;
/**
* Key into groupByData that produces currentGroupByExprData. Not used in lazy mode.
*/
ValueArray
currentGroupsKey
;
private
int
havingIndex
;
private
boolean
isGroupQuery
,
isGroupSortedQuery
;
...
...
@@ -151,8 +184,45 @@ public class Select extends Query {
return
group
;
}
public
HashMap
<
Expression
,
Object
>
getCurrentGroup
()
{
return
currentGroup
;
/**
* Is there currently a group-by active
*/
public
boolean
isCurrentGroup
()
{
return
currentGroupByExprData
!=
null
;
}
/**
* Get the group-by data for the current group and the passed in expression.
*/
public
Object
getCurrentGroupExprData
(
Expression
expr
)
{
Integer
index
=
exprToIndexInGroupByData
.
get
(
expr
);
if
(
index
==
null
)
{
return
null
;
}
return
currentGroupByExprData
[
index
];
}
/**
* Set the group-by data for the current group and the passed in expression.
*/
public
void
setCurrentGroupExprData
(
Expression
expr
,
Object
obj
)
{
Integer
index
=
exprToIndexInGroupByData
.
get
(
expr
);
if
(
index
!=
null
)
{
assert
currentGroupByExprData
[
index
]
==
null
;
currentGroupByExprData
[
index
]
=
obj
;
return
;
}
index
=
exprToIndexInGroupByData
.
size
();
exprToIndexInGroupByData
.
put
(
expr
,
index
);
if
(
index
>=
currentGroupByExprData
.
length
)
{
currentGroupByExprData
=
Arrays
.
copyOf
(
currentGroupByExprData
,
currentGroupByExprData
.
length
*
2
);
// this can be null in lazy mode
if
(
currentGroupsKey
!=
null
)
{
// since we changed the size of the array, update the object in the groups map
groupByData
.
put
(
currentGroupsKey
,
currentGroupByExprData
);
}
}
currentGroupByExprData
[
index
]
=
obj
;
}
public
int
getCurrentGroupRowId
()
{
...
...
@@ -313,20 +383,21 @@ public class Select extends Query {
}
private
void
queryGroup
(
int
columnCount
,
LocalResult
result
)
{
ValueHashMap
<
HashMap
<
Expression
,
Object
>>
groups
=
ValueHashMap
.
newInstance
();
groupByData
=
new
HashMap
<>();
currentGroupByExprData
=
null
;
currentGroupsKey
=
null
;
exprToIndexInGroupByData
.
clear
();
try
{
int
rowNumber
=
0
;
setCurrentRowNumber
(
0
);
currentGroup
=
null
;
ValueArray
defaultGroup
=
ValueArray
.
get
(
new
Value
[
0
]);
int
sampleSize
=
getSampleSizeValue
(
session
);
while
(
topTableFilter
.
next
())
{
setCurrentRowNumber
(
rowNumber
+
1
);
if
(
isConditionMet
())
{
Value
key
;
rowNumber
++;
if
(
groupIndex
==
null
)
{
k
ey
=
defaultGroup
;
currentGroupsK
ey
=
defaultGroup
;
}
else
{
Value
[]
keyValues
=
new
Value
[
groupIndex
.
length
];
// update group
...
...
@@ -335,14 +406,14 @@ public class Select extends Query {
Expression
expr
=
expressions
.
get
(
idx
);
keyValues
[
i
]
=
expr
.
getValue
(
session
);
}
k
ey
=
ValueArray
.
get
(
keyValues
);
currentGroupsK
ey
=
ValueArray
.
get
(
keyValues
);
}
HashMap
<
Expression
,
Object
>
values
=
groups
.
get
(
k
ey
);
Object
[]
values
=
groupByData
.
get
(
currentGroupsK
ey
);
if
(
values
==
null
)
{
values
=
new
HashMap
<>()
;
groups
.
put
(
k
ey
,
values
);
values
=
new
Object
[
Math
.
max
(
exprToIndexInGroupByData
.
size
(),
expressions
.
size
())]
;
groupByData
.
put
(
currentGroupsK
ey
,
values
);
}
currentGroup
=
values
;
currentGroupByExprData
=
values
;
currentGroupRowId
++;
for
(
int
i
=
0
;
i
<
columnCount
;
i
++)
{
if
(
groupByExpression
==
null
||
!
groupByExpression
[
i
])
{
...
...
@@ -355,14 +426,14 @@ public class Select extends Query {
}
}
}
if
(
groupIndex
==
null
&&
groups
.
size
()
==
0
)
{
groups
.
put
(
defaultGroup
,
new
HashMap
<
Expression
,
Object
>());
if
(
groupIndex
==
null
&&
groupByData
.
size
()
==
0
)
{
groupByData
.
put
(
defaultGroup
,
new
Object
[
Math
.
max
(
exprToIndexInGroupByData
.
size
(),
expressions
.
size
())]);
}
ArrayList
<
Value
>
keys
=
groups
.
keys
();
for
(
Value
v
:
keys
)
{
ValueArray
key
=
(
ValueArray
)
v
;
currentGroup
=
groups
.
get
(
key
);
Value
[]
keyValues
=
key
.
getList
();
for
(
Map
.
Entry
<
Value
,
Object
[]>
entry
:
groupByData
.
entrySet
())
{
currentGroupsKey
=
(
ValueArray
)
entry
.
getKey
();
currentGroupByExprData
=
entry
.
getValue
();
Value
[]
keyValues
=
currentGroupsKey
.
getList
();
Value
[]
row
=
new
Value
[
columnCount
];
for
(
int
j
=
0
;
groupIndex
!=
null
&&
j
<
groupIndex
.
length
;
j
++)
{
row
[
groupIndex
[
j
]]
=
keyValues
[
j
];
...
...
@@ -380,6 +451,12 @@ public class Select extends Query {
row
=
keepOnlyDistinct
(
row
,
columnCount
);
result
.
addRow
(
row
);
}
}
finally
{
groupByData
=
null
;
currentGroupsKey
=
null
;
currentGroupByExprData
=
null
;
exprToIndexInGroupByData
.
clear
();
}
}
/**
...
...
@@ -1475,13 +1552,15 @@ public class Select extends Query {
LazyResultGroupSorted
(
Expression
[]
expressions
,
int
columnCount
)
{
super
(
expressions
,
columnCount
);
currentGroup
=
null
;
currentGroupByExprData
=
null
;
currentGroupsKey
=
null
;
}
@Override
public
void
reset
()
{
super
.
reset
();
currentGroup
=
null
;
currentGroupByExprData
=
null
;
currentGroupsKey
=
null
;
}
@Override
...
...
@@ -1501,11 +1580,11 @@ public class Select extends Query {
Value
[]
row
=
null
;
if
(
previousKeyValues
==
null
)
{
previousKeyValues
=
keyValues
;
currentGroup
=
new
HashMap
<>()
;
currentGroup
ByExprData
=
new
Object
[
Math
.
max
(
exprToIndexInGroupByData
.
size
(),
expressions
.
size
())]
;
}
else
if
(!
Arrays
.
equals
(
previousKeyValues
,
keyValues
))
{
row
=
createGroupSortedRow
(
previousKeyValues
,
columnCount
);
previousKeyValues
=
keyValues
;
currentGroup
=
new
HashMap
<>()
;
currentGroup
ByExprData
=
new
Object
[
Math
.
max
(
exprToIndexInGroupByData
.
size
(),
expressions
.
size
())]
;
}
currentGroupRowId
++;
...
...
h2/src/main/org/h2/expression/Aggregate.java
浏览文件 @
230a1fcb
...
...
@@ -283,8 +283,7 @@ public class Aggregate extends Expression {
// if (on != null) {
// on.updateAggregate();
// }
HashMap
<
Expression
,
Object
>
group
=
select
.
getCurrentGroup
();
if
(
group
==
null
)
{
if
(!
select
.
isCurrentGroup
())
{
// this is a different level (the enclosing query)
return
;
}
...
...
@@ -296,10 +295,10 @@ public class Aggregate extends Expression {
}
lastGroupRowId
=
groupRowId
;
AggregateData
data
=
(
AggregateData
)
group
.
get
(
this
);
AggregateData
data
=
(
AggregateData
)
select
.
getCurrentGroupExprData
(
this
);
if
(
data
==
null
)
{
data
=
AggregateData
.
create
(
type
);
group
.
put
(
this
,
data
);
select
.
setCurrentGroupExprData
(
this
,
data
);
}
Value
v
=
on
==
null
?
null
:
on
.
getValue
(
session
);
if
(
type
==
AggregateType
.
GROUP_CONCAT
)
{
...
...
@@ -372,13 +371,13 @@ public class Aggregate extends Expression {
DbException
.
throwInternalError
(
"type="
+
type
);
}
}
HashMap
<
Expression
,
Object
>
group
=
select
.
getCurrentGroup
();
if
(
group
==
null
)
{
if
(!
select
.
isCurrentGroup
())
{
throw
DbException
.
get
(
ErrorCode
.
INVALID_USE_OF_AGGREGATE_FUNCTION_1
,
getSQL
());
}
AggregateData
data
=
(
AggregateData
)
group
.
get
(
this
);
AggregateData
data
=
(
AggregateData
)
select
.
getCurrentGroupExprData
(
this
);
if
(
data
==
null
)
{
data
=
AggregateData
.
create
(
type
);
select
.
setCurrentGroupExprData
(
this
,
data
);
}
if
(
type
==
AggregateType
.
GROUP_CONCAT
)
{
Value
[]
array
=
((
AggregateDataCollecting
)
data
).
getArray
();
...
...
h2/src/main/org/h2/expression/AggregateDataHistogram.java
浏览文件 @
230a1fcb
...
...
@@ -7,6 +7,7 @@ package org.h2.expression;
import
java.util.Arrays
;
import
java.util.Comparator
;
import
java.util.Map
;
import
org.h2.engine.Constants
;
import
org.h2.engine.Database
;
import
org.h2.util.ValueHashMap
;
...
...
@@ -47,9 +48,9 @@ class AggregateDataHistogram extends AggregateData {
}
ValueArray
[]
values
=
new
ValueArray
[
distinctValues
.
size
()];
int
i
=
0
;
for
(
Value
dv
:
distinctValues
.
key
s
())
{
AggregateDataHistogram
d
=
distinctValues
.
get
(
dv
);
values
[
i
]
=
ValueArray
.
get
(
new
Value
[]
{
dv
,
ValueLong
.
get
(
d
.
count
)
});
for
(
Map
.
Entry
<
Value
,
AggregateDataHistogram
>
entry
:
distinctValues
.
entrie
s
())
{
AggregateDataHistogram
d
=
entry
.
getValue
(
);
values
[
i
]
=
ValueArray
.
get
(
new
Value
[]
{
entry
.
getKey
()
,
ValueLong
.
get
(
d
.
count
)
});
i
++;
}
final
CompareMode
compareMode
=
database
.
getCompareMode
();
...
...
h2/src/main/org/h2/expression/ExpressionColumn.java
浏览文件 @
230a1fcb
...
...
@@ -5,7 +5,6 @@
*/
package
org
.
h2
.
expression
;
import
java.util.HashMap
;
import
org.h2.api.ErrorCode
;
import
org.h2.command.Parser
;
import
org.h2.command.dml.Select
;
...
...
@@ -159,14 +158,13 @@ public class ExpressionColumn extends Expression {
if
(
select
==
null
)
{
throw
DbException
.
get
(
ErrorCode
.
MUST_GROUP_BY_COLUMN_1
,
getSQL
());
}
HashMap
<
Expression
,
Object
>
values
=
select
.
getCurrentGroup
();
if
(
values
==
null
)
{
if
(!
select
.
isCurrentGroup
())
{
// this is a different level (the enclosing query)
return
;
}
Value
v
=
(
Value
)
values
.
get
(
this
);
Value
v
=
(
Value
)
select
.
getCurrentGroupExprData
(
this
);
if
(
v
==
null
)
{
values
.
put
(
this
,
now
);
select
.
setCurrentGroupExprData
(
this
,
now
);
}
else
{
if
(!
database
.
areEqual
(
now
,
v
))
{
throw
DbException
.
get
(
ErrorCode
.
MUST_GROUP_BY_COLUMN_1
,
getSQL
());
...
...
@@ -178,9 +176,8 @@ public class ExpressionColumn extends Expression {
public
Value
getValue
(
Session
session
)
{
Select
select
=
columnResolver
.
getSelect
();
if
(
select
!=
null
)
{
HashMap
<
Expression
,
Object
>
values
=
select
.
getCurrentGroup
();
if
(
values
!=
null
)
{
Value
v
=
(
Value
)
values
.
get
(
this
);
if
(
select
.
isCurrentGroup
())
{
Value
v
=
(
Value
)
select
.
getCurrentGroupExprData
(
this
);
if
(
v
!=
null
)
{
return
v
;
}
...
...
h2/src/main/org/h2/expression/JavaAggregate.java
浏览文件 @
230a1fcb
...
...
@@ -7,7 +7,6 @@ package org.h2.expression;
import
java.sql.Connection
;
import
java.sql.SQLException
;
import
java.util.HashMap
;
import
org.h2.api.Aggregate
;
import
org.h2.api.ErrorCode
;
import
org.h2.command.Parser
;
...
...
@@ -167,15 +166,14 @@ public class JavaAggregate extends Expression {
@Override
public
Value
getValue
(
Session
session
)
{
HashMap
<
Expression
,
Object
>
group
=
select
.
getCurrentGroup
();
if
(
group
==
null
)
{
if
(!
select
.
isCurrentGroup
())
{
throw
DbException
.
get
(
ErrorCode
.
INVALID_USE_OF_AGGREGATE_FUNCTION_1
,
getSQL
());
}
try
{
Aggregate
agg
;
if
(
distinct
)
{
agg
=
getInstance
();
AggregateDataCollecting
data
=
(
AggregateDataCollecting
)
group
.
get
(
this
);
AggregateDataCollecting
data
=
(
AggregateDataCollecting
)
select
.
getCurrentGroupExprData
(
this
);
if
(
data
!=
null
)
{
for
(
Value
value
:
data
.
values
)
{
if
(
args
.
length
==
1
)
{
...
...
@@ -191,7 +189,7 @@ public class JavaAggregate extends Expression {
}
}
}
else
{
agg
=
(
Aggregate
)
group
.
get
(
this
);
agg
=
(
Aggregate
)
select
.
getCurrentGroupExprData
(
this
);
if
(
agg
==
null
)
{
agg
=
getInstance
();
}
...
...
@@ -208,8 +206,7 @@ public class JavaAggregate extends Expression {
@Override
public
void
updateAggregate
(
Session
session
)
{
HashMap
<
Expression
,
Object
>
group
=
select
.
getCurrentGroup
();
if
(
group
==
null
)
{
if
(!
select
.
isCurrentGroup
())
{
// this is a different level (the enclosing query)
return
;
}
...
...
@@ -229,10 +226,10 @@ public class JavaAggregate extends Expression {
try
{
if
(
distinct
)
{
AggregateDataCollecting
data
=
(
AggregateDataCollecting
)
group
.
get
(
this
);
AggregateDataCollecting
data
=
(
AggregateDataCollecting
)
select
.
getCurrentGroupExprData
(
this
);
if
(
data
==
null
)
{
data
=
new
AggregateDataCollecting
();
group
.
put
(
this
,
data
);
select
.
setCurrentGroupExprData
(
this
,
data
);
}
Value
[]
argValues
=
new
Value
[
args
.
length
];
Value
arg
=
null
;
...
...
@@ -243,10 +240,10 @@ public class JavaAggregate extends Expression {
}
data
.
add
(
session
.
getDatabase
(),
dataType
,
true
,
args
.
length
==
1
?
arg
:
ValueArray
.
get
(
argValues
));
}
else
{
Aggregate
agg
=
(
Aggregate
)
group
.
get
(
this
);
Aggregate
agg
=
(
Aggregate
)
select
.
getCurrentGroupExprData
(
this
);
if
(
agg
==
null
)
{
agg
=
getInstance
();
group
.
put
(
this
,
agg
);
select
.
setCurrentGroupExprData
(
this
,
agg
);
}
Object
[]
argValues
=
new
Object
[
args
.
length
];
Object
arg
=
null
;
...
...
h2/src/main/org/h2/mvstore/DataUtils.java
浏览文件 @
230a1fcb
...
...
@@ -1026,39 +1026,6 @@ public final class DataUtils {
}
}
/**
* An entry of a map.
*
* @param <K> the key type
* @param <V> the value type
*/
public
static
final
class
MapEntry
<
K
,
V
>
implements
Map
.
Entry
<
K
,
V
>
{
private
final
K
key
;
private
final
V
value
;
public
MapEntry
(
K
key
,
V
value
)
{
this
.
key
=
key
;
this
.
value
=
value
;
}
@Override
public
K
getKey
()
{
return
key
;
}
@Override
public
V
getValue
()
{
return
value
;
}
@Override
public
V
setValue
(
V
value
)
{
throw
newUnsupportedOperationException
(
"Updating the value is not supported"
);
}
}
/**
* Get the configuration parameter value, or default.
*
...
...
h2/src/main/org/h2/mvstore/MVMap.java
浏览文件 @
230a1fcb
...
...
@@ -673,7 +673,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
@Override
public
Entry
<
K
,
V
>
next
()
{
K
k
=
cursor
.
next
();
return
new
DataUtils
.
Map
Entry
<>(
k
,
cursor
.
getValue
());
return
new
SimpleImmutable
Entry
<>(
k
,
cursor
.
getValue
());
}
@Override
...
...
h2/src/main/org/h2/mvstore/db/MVPrimaryIndex.java
浏览文件 @
230a1fcb
...
...
@@ -5,6 +5,7 @@
*/
package
org
.
h2
.
mvstore
.
db
;
import
java.util.AbstractMap
;
import
java.util.Collections
;
import
java.util.Iterator
;
import
java.util.List
;
...
...
@@ -18,7 +19,6 @@ import org.h2.index.BaseIndex;
import
org.h2.index.Cursor
;
import
org.h2.index.IndexType
;
import
org.h2.message.DbException
;
import
org.h2.mvstore.DataUtils
;
import
org.h2.mvstore.tx.Transaction
;
import
org.h2.mvstore.tx.TransactionMap
;
import
org.h2.result.Row
;
...
...
@@ -261,7 +261,7 @@ public class MVPrimaryIndex extends BaseIndex {
Collections
.<
Entry
<
Value
,
Value
>>
emptyList
().
iterator
());
}
Value
value
=
map
.
get
(
v
);
Entry
<
Value
,
Value
>
e
=
new
DataUtils
.
Map
Entry
<
Value
,
Value
>(
v
,
value
);
Entry
<
Value
,
Value
>
e
=
new
AbstractMap
.
SimpleImmutable
Entry
<
Value
,
Value
>(
v
,
value
);
List
<
Entry
<
Value
,
Value
>>
list
=
Collections
.
singletonList
(
e
);
MVStoreCursor
c
=
new
MVStoreCursor
(
session
,
list
.
iterator
());
c
.
next
();
...
...
h2/src/main/org/h2/mvstore/tx/TransactionMap.java
浏览文件 @
230a1fcb
...
...
@@ -9,6 +9,8 @@ import org.h2.mvstore.Cursor;
import
org.h2.mvstore.DataUtils
;
import
org.h2.mvstore.MVMap
;
import
org.h2.mvstore.type.DataType
;
import
java.util.AbstractMap
;
import
java.util.Iterator
;
import
java.util.Map
;
...
...
@@ -671,7 +673,7 @@ public class TransactionMap<K, V> {
if
(
data
!=
null
&&
data
.
value
!=
null
)
{
@SuppressWarnings
(
"unchecked"
)
final
V
value
=
(
V
)
data
.
value
;
current
=
new
DataUtils
.
Map
Entry
<>(
key
,
value
);
current
=
new
AbstractMap
.
SimpleImmutable
Entry
<>(
key
,
value
);
currentKey
=
key
;
return
;
}
...
...
h2/src/main/org/h2/util/ValueHashMap.java
浏览文件 @
230a1fcb
...
...
@@ -5,20 +5,38 @@
*/
package
org
.
h2
.
util
;
import
java.util.AbstractMap
;
import
java.util.ArrayList
;
import
java.util.Iterator
;
import
java.util.Map
;
import
java.util.NoSuchElementException
;
import
org.h2.message.DbException
;
import
org.h2.value.Value
;
import
org.h2.value.ValueNull
;
/**
* This hash map supports keys of type Value.
* <p>
* ValueHashMap is a very simple implementation without allocation of additional
* objects for entries. It's very fast with good distribution of hashes, but if
* hashes have a lot of collisions this implementation tends to be very slow.
* <p>
* HashMap in archaic versions of Java have some overhead for allocation of
* entries, but slightly better behaviour with limited number of collisions,
* because collisions have no impact on non-colliding entries. HashMap in modern
* versions of Java also have the same overhead, but it builds a trees of keys
* with colliding hashes, that's why even if the all keys have exactly the same
* hash code it still offers a good performance similar to TreeMap. So
* ValueHashMap is faster in typical cases, but may behave really bad in some
* cases. HashMap is slower in typical cases, but its performance does not
* degrade too much even in the worst possible case (if keys are comparable).
*
* @param <V> the value type
*/
public
class
ValueHashMap
<
V
>
extends
HashBase
{
private
Value
[]
keys
;
private
V
[]
values
;
Value
[]
keys
;
V
[]
values
;
/**
* Create a new value hash map.
...
...
@@ -54,7 +72,12 @@ public class ValueHashMap<V> extends HashBase {
}
private
int
getIndex
(
Value
key
)
{
return
key
.
hashCode
()
&
mask
;
int
h
=
key
.
hashCode
();
/*
* Add some protection against hashes with the same less significant bits
* (ValueDouble with integer values, for example).
*/
return
(
h
^
h
>>>
16
)
&
mask
;
}
/**
...
...
@@ -170,6 +193,51 @@ public class ValueHashMap<V> extends HashBase {
return
list
;
}
public
Iterable
<
Map
.
Entry
<
Value
,
V
>>
entries
()
{
return
new
EntryIterable
();
}
private
final
class
EntryIterable
implements
Iterable
<
Map
.
Entry
<
Value
,
V
>>
{
EntryIterable
()
{
}
@Override
public
Iterator
<
Map
.
Entry
<
Value
,
V
>>
iterator
()
{
return
new
EntryIterator
();
}
}
private
final
class
EntryIterator
implements
Iterator
<
Map
.
Entry
<
Value
,
V
>>
{
private
int
keysIndex
=
-
1
;
private
int
left
=
size
;
EntryIterator
()
{
}
@Override
public
boolean
hasNext
()
{
return
left
>
0
;
}
@Override
public
Map
.
Entry
<
Value
,
V
>
next
()
{
if
(
left
<=
0
)
throw
new
NoSuchElementException
();
left
--;
for
(;;)
{
keysIndex
++;
Value
key
=
keys
[
keysIndex
];
if
(
key
!=
null
&&
key
!=
ValueNull
.
DELETED
)
return
new
AbstractMap
.
SimpleImmutableEntry
<
Value
,
V
>(
key
,
values
[
keysIndex
]);
}
}
@Override
public
void
remove
()
{
throw
new
UnsupportedOperationException
();
}
}
/**
* Get the list of values.
*
...
...
h2/src/main/org/h2/value/ValueArray.java
浏览文件 @
230a1fcb
...
...
@@ -7,13 +7,12 @@ package org.h2.value;
import
java.lang.reflect.Array
;
import
java.sql.PreparedStatement
;
import
java.util.Array
List
;
import
java.util.Array
s
;
import
org.h2.engine.Constants
;
import
org.h2.engine.SysProperties
;
import
org.h2.util.MathUtils
;
import
org.h2.util.StatementBuilder
;
import
org.h2.util.Utils
;
/**
* Implementation of the ARRAY data type.
...
...
@@ -29,10 +28,6 @@ public class ValueArray extends Value {
this
.
values
=
list
;
}
private
ValueArray
(
Value
[]
list
)
{
this
(
Object
.
class
,
list
);
}
/**
* Get or create a array value for the given value array.
* Do not clone the data.
...
...
@@ -41,7 +36,7 @@ public class ValueArray extends Value {
* @return the value
*/
public
static
ValueArray
get
(
Value
[]
list
)
{
return
new
ValueArray
(
list
);
return
new
ValueArray
(
Object
.
class
,
list
);
}
/**
...
...
@@ -211,18 +206,28 @@ public class ValueArray extends Value {
if
(!
force
)
{
return
this
;
}
ArrayList
<
Value
>
list
=
Utils
.
newSmallArrayList
();
for
(
Value
v
:
values
)
{
v
=
v
.
convertPrecision
(
precision
,
true
);
int
length
=
values
.
length
;
Value
[]
newValues
=
new
Value
[
length
];
int
i
=
0
;
boolean
modified
=
false
;
for
(;
i
<
length
;
i
++)
{
Value
old
=
values
[
i
];
Value
v
=
old
.
convertPrecision
(
precision
,
true
);
if
(
v
!=
old
)
{
modified
=
true
;
}
// empty byte arrays or strings have precision 0
// they count as precision 1 here
precision
-=
Math
.
max
(
1
,
v
.
getPrecision
());
if
(
precision
<
0
)
{
break
;
}
list
.
add
(
v
);
newValues
[
i
]
=
v
;
}
if
(
i
<
length
)
{
return
get
(
componentType
,
Arrays
.
copyOf
(
newValues
,
i
));
}
return
get
(
list
.
toArray
(
new
Value
[
0
]))
;
return
modified
?
get
(
componentType
,
newValues
)
:
this
;
}
}
h2/src/main/org/h2/value/ValueDouble.java
浏览文件 @
230a1fcb
...
...
@@ -133,8 +133,12 @@ public class ValueDouble extends Value {
@Override
public
int
hashCode
()
{
long
hash
=
Double
.
doubleToLongBits
(
value
);
return
(
int
)
(
hash
^
(
hash
>>
32
));
/*
* NaNs are normalized in get() method, so it's safe to use
* doubleToRawLongBits() instead of doubleToLongBits() here.
*/
long
hash
=
Double
.
doubleToRawLongBits
(
value
);
return
(
int
)
(
hash
^
(
hash
>>>
32
));
}
@Override
...
...
h2/src/main/org/h2/value/ValueFloat.java
浏览文件 @
230a1fcb
...
...
@@ -34,6 +34,7 @@ public class ValueFloat extends Value {
private
static
final
ValueFloat
ZERO
=
new
ValueFloat
(
0.0
F
);
private
static
final
ValueFloat
ONE
=
new
ValueFloat
(
1.0
F
);
private
static
final
ValueFloat
NAN
=
new
ValueFloat
(
Float
.
NaN
);
private
final
float
value
;
...
...
@@ -88,7 +89,7 @@ public class ValueFloat extends Value {
return
"POWER(0, -1)"
;
}
else
if
(
value
==
Float
.
NEGATIVE_INFINITY
)
{
return
"(-POWER(0, -1))"
;
}
else
if
(
Double
.
isNaN
(
value
))
{
}
else
if
(
Float
.
isNaN
(
value
))
{
// NaN
return
"SQRT(-1)"
;
}
...
...
@@ -133,8 +134,11 @@ public class ValueFloat extends Value {
@Override
public
int
hashCode
()
{
long
hash
=
Float
.
floatToIntBits
(
value
);
return
(
int
)
(
hash
^
(
hash
>>
32
));
/*
* NaNs are normalized in get() method, so it's safe to use
* floatToRawIntBits() instead of floatToIntBits() here.
*/
return
Float
.
floatToRawIntBits
(
value
);
}
@Override
...
...
@@ -160,6 +164,8 @@ public class ValueFloat extends Value {
}
else
if
(
d
==
0.0
F
)
{
// -0.0 == 0.0, and we want to return 0.0 for both
return
ZERO
;
}
else
if
(
Float
.
isNaN
(
d
))
{
return
NAN
;
}
return
(
ValueFloat
)
Value
.
cache
(
new
ValueFloat
(
d
));
}
...
...
h2/src/test/org/h2/test/TestBase.java
浏览文件 @
230a1fcb
...
...
@@ -851,6 +851,19 @@ public abstract class TestBase {
assertFalse
(
message
,
rs1
.
next
());
}
/**
* Check if two objects are the same, and if not throw an exception.
*
* @param expected the expected value
* @param actual the actual value
* @throws AssertionError if the objects are not the same
*/
public
void
assertSame
(
Object
expected
,
Object
actual
)
{
if
(
expected
!=
actual
)
{
fail
(
" expected: "
+
expected
+
" != actual: "
+
actual
);
}
}
/**
* Check if the first value is larger or equal than the second value, and if
* not throw an exception.
...
...
h2/src/test/org/h2/test/db/AbstractBaseForCommonTableExpressions.java
浏览文件 @
230a1fcb
...
...
@@ -10,6 +10,10 @@ import java.sql.PreparedStatement;
import
java.sql.ResultSet
;
import
java.sql.SQLException
;
import
java.sql.Statement
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.Collections
;
import
org.h2.test.TestBase
;
/**
...
...
@@ -29,16 +33,25 @@ public abstract class AbstractBaseForCommonTableExpressions extends TestBase {
* @param closeAndReopenDatabaseConnectionOnIteration whether the connection
* should be re-opened each time
* @param expectedColumnTypes the expected datatypes of the result
* @param anyOrder whether any order of rows should be allowed.
* If {@code true}, this method may sort expectedRowData.
*/
void
testRepeatedQueryWithSetup
(
int
maxRetries
,
String
[]
expectedRowData
,
String
[]
expectedColumnNames
,
int
expectedNumberOfRows
,
String
setupSQL
,
String
withQuery
,
int
closeAndReopenDatabaseConnectionOnIteration
,
String
[]
expectedColumnTypes
)
throws
SQLException
{
int
closeAndReopenDatabaseConnectionOnIteration
,
String
[]
expectedColumnTypes
,
boolean
anyOrder
)
throws
SQLException
{
deleteDb
(
"commonTableExpressionQueries"
);
Connection
conn
=
getConnection
(
"commonTableExpressionQueries"
);
PreparedStatement
prep
;
ResultSet
rs
;
if
(
anyOrder
)
{
Arrays
.
sort
(
expectedRowData
);
}
ArrayList
<
String
>
rowData
=
new
ArrayList
<>();
StringBuilder
buf
=
new
StringBuilder
();
for
(
int
queryRunTries
=
1
;
queryRunTries
<=
maxRetries
;
queryRunTries
++)
{
Statement
stat
=
conn
.
createStatement
();
...
...
@@ -65,17 +78,18 @@ public abstract class AbstractBaseForCommonTableExpressions extends TestBase {
expectedColumnTypes
[
columnIndex
-
1
],
rs
.
getMetaData
().
getColumnTypeName
(
columnIndex
));
}
int
rowNdx
=
0
;
rowData
.
clear
()
;
while
(
rs
.
next
())
{
StringBuffer
buf
=
new
StringBuffer
(
);
buf
.
setLength
(
0
);
for
(
int
columnIndex
=
1
;
columnIndex
<=
rs
.
getMetaData
().
getColumnCount
();
columnIndex
++)
{
buf
.
append
(
"|"
+
rs
.
getString
(
columnIndex
));
buf
.
append
(
'|'
).
append
(
rs
.
getString
(
columnIndex
));
}
assertEquals
(
expectedRowData
[
rowNdx
],
buf
.
toString
());
rowNdx
++;
rowData
.
add
(
buf
.
toString
());
}
assertEquals
(
expectedNumberOfRows
,
rowNdx
);
if
(
anyOrder
)
{
Collections
.
sort
(
rowData
);
}
assertEquals
(
expectedRowData
,
rowData
.
toArray
(
new
String
[
0
]));
rs
.
close
();
prep
.
close
();
...
...
h2/src/test/org/h2/test/db/TestGeneralCommonTableQueries.java
浏览文件 @
230a1fcb
...
...
@@ -492,7 +492,7 @@ public class TestGeneralCommonTableQueries extends AbstractBaseForCommonTableExp
int
expectedNumberOfRows
=
expectedRowData
.
length
;
testRepeatedQueryWithSetup
(
maxRetries
,
expectedRowData
,
expectedColumnNames
,
expectedNumberOfRows
,
setupSQL
,
withQuery
,
maxRetries
-
1
,
expectedColumnTypes
);
withQuery
,
maxRetries
-
1
,
expectedColumnTypes
,
false
);
}
...
...
@@ -512,7 +512,7 @@ public class TestGeneralCommonTableQueries extends AbstractBaseForCommonTableExp
int
expectedNumberOfRows
=
expectedRowData
.
length
;
testRepeatedQueryWithSetup
(
maxRetries
,
expectedRowData
,
expectedColumnNames
,
expectedNumberOfRows
,
setupSQL
,
withQuery
,
maxRetries
-
1
,
expectedColumnTypes
);
withQuery
,
maxRetries
-
1
,
expectedColumnTypes
,
false
);
}
...
...
@@ -549,7 +549,7 @@ public class TestGeneralCommonTableQueries extends AbstractBaseForCommonTableExp
int
expectedNumberOfRows
=
expectedRowData
.
length
;
testRepeatedQueryWithSetup
(
maxRetries
,
expectedRowData
,
expectedColumnNames
,
expectedNumberOfRows
,
setupSQL
,
withQuery
,
maxRetries
-
1
,
expectedColumnTypes
);
setupSQL
,
withQuery
,
maxRetries
-
1
,
expectedColumnTypes
,
false
);
}
finally
{
config
=
backupConfig
;
}
...
...
@@ -576,6 +576,6 @@ public class TestGeneralCommonTableQueries extends AbstractBaseForCommonTableExp
int
expectedNumberOfRows
=
expectedRowData
.
length
;
testRepeatedQueryWithSetup
(
maxRetries
,
expectedRowData
,
expectedColumnNames
,
expectedNumberOfRows
,
setupSQL
,
withQuery
,
maxRetries
-
1
,
expectedColumnTypes
);
withQuery
,
maxRetries
-
1
,
expectedColumnTypes
,
false
);
}
}
h2/src/test/org/h2/test/db/TestPersistentCommonTableExpressions.java
浏览文件 @
230a1fcb
...
...
@@ -98,7 +98,7 @@ public class TestPersistentCommonTableExpressions extends AbstractBaseForCommonT
int
expectedNumberOfRows
=
expectedRowData
.
length
;
testRepeatedQueryWithSetup
(
maxRetries
,
expectedRowData
,
expectedColumnNames
,
expectedNumberOfRows
,
setupSQL
,
withQuery
,
maxRetries
-
1
,
expectedColumnTypes
);
withQuery
,
maxRetries
-
1
,
expectedColumnTypes
,
true
);
}
...
...
@@ -147,7 +147,7 @@ public class TestPersistentCommonTableExpressions extends AbstractBaseForCommonT
String
[]
expectedColumnTypes
=
new
String
[]{
"INTEGER"
,
"INTEGER"
,
"INTEGER"
,
"INTEGER"
};
int
expectedNumberOfRows
=
11
;
testRepeatedQueryWithSetup
(
maxRetries
,
expectedRowData
,
expectedColumnNames
,
expectedNumberOfRows
,
setupSQL
,
withQuery
,
maxRetries
-
1
,
expectedColumnTypes
);
withQuery
,
maxRetries
-
1
,
expectedColumnTypes
,
false
);
}
private
void
testPersistentNonRecursiveTableInCreateView
()
throws
Exception
{
...
...
@@ -186,7 +186,7 @@ public class TestPersistentCommonTableExpressions extends AbstractBaseForCommonT
String
[]
expectedColumnTypes
=
new
String
[]{
"INTEGER"
,
"INTEGER"
,
"INTEGER"
,
"INTEGER"
};
int
expectedNumberOfRows
=
5
;
testRepeatedQueryWithSetup
(
maxRetries
,
expectedRowData
,
expectedColumnNames
,
expectedNumberOfRows
,
setupSQL
,
withQuery
,
maxRetries
-
1
,
expectedColumnTypes
);
withQuery
,
maxRetries
-
1
,
expectedColumnTypes
,
false
);
}
private
void
testPersistentNonRecursiveTableInCreateViewDropAllObjects
()
throws
Exception
{
...
...
@@ -224,7 +224,7 @@ public class TestPersistentCommonTableExpressions extends AbstractBaseForCommonT
String
[]
expectedColumnTypes
=
new
String
[]{
"INTEGER"
,
"INTEGER"
,
"INTEGER"
,
"INTEGER"
};
int
expectedNumberOfRows
=
5
;
testRepeatedQueryWithSetup
(
maxRetries
,
expectedRowData
,
expectedColumnNames
,
expectedNumberOfRows
,
setupSQL
,
withQuery
,
maxRetries
-
1
,
expectedColumnTypes
);
withQuery
,
maxRetries
-
1
,
expectedColumnTypes
,
false
);
}
private
void
testPersistentRecursiveTableInCreateViewDropAllObjects
()
throws
Exception
{
...
...
@@ -271,6 +271,6 @@ public class TestPersistentCommonTableExpressions extends AbstractBaseForCommonT
String
[]
expectedColumnTypes
=
new
String
[]{
"INTEGER"
,
"INTEGER"
,
"INTEGER"
,
"INTEGER"
};
int
expectedNumberOfRows
=
11
;
testRepeatedQueryWithSetup
(
maxRetries
,
expectedRowData
,
expectedColumnNames
,
expectedNumberOfRows
,
setupSQL
,
withQuery
,
maxRetries
-
1
,
expectedColumnTypes
);
withQuery
,
maxRetries
-
1
,
expectedColumnTypes
,
false
);
}
}
h2/src/test/org/h2/test/scripts/testScript.sql
浏览文件 @
230a1fcb
...
...
@@ -6326,6 +6326,8 @@ SELECT 'abc', 'Papa Joe''s', CAST(-1 AS SMALLINT), CAST(2 AS BIGINT), CAST(0 AS
> abc Papa Joe'
s
-
1
2
0
.
0
0
a0f
125
TRUE
FALSE
>
rows
:
1
-- ' This apostrophe is here to fix syntax highlighting in the text editors.
SELECT
CAST
(
'abcd'
AS
VARCHAR
(
255
)),
CAST
(
'ef_gh'
AS
VARCHAR
(
3
));
>
'abcd'
'ef_'
>
------ -----
...
...
@@ -6899,14 +6901,14 @@ INSERT INTO TEST VALUES(?, ?, ?);
}
;
>
update
count
:
9
SELECT
IFNULL
(
NAME
,
''
)
||
': '
||
GROUP_CONCAT
(
VALUE
ORDER
BY
NAME
,
VALUE
DESC
SEPARATOR
', '
)
FROM
TEST
GROUP
BY
NAME
;
SELECT
IFNULL
(
NAME
,
''
)
||
': '
||
GROUP_CONCAT
(
VALUE
ORDER
BY
NAME
,
VALUE
DESC
SEPARATOR
', '
)
FROM
TEST
GROUP
BY
NAME
ORDER
BY
1
;
>
(
IFNULL
(
NAME
,
''
)
||
': '
)
||
GROUP_CONCAT
(
VALUE
ORDER
BY
NAME
,
VALUE
DESC
SEPARATOR
', '
)
>
------------------------------------------------------------------------------------------
>
:
3
.
10
,
-
10
.
00
>
Apples
:
1
.
50
,
1
.
20
,
1
.
10
>
Oranges
:
2
.
05
,
1
.
80
>
Bananas
:
2
.
50
>
Cherries
:
5
.
10
>
:
3
.
10
,
-
10
.
0
0
>
Oranges
:
2
.
05
,
1
.
8
0
>
rows
(
ordered
):
5
SELECT
GROUP_CONCAT
(
ID
ORDER
BY
ID
)
FROM
TEST
;
...
...
h2/src/test/org/h2/test/unit/TestValue.java
浏览文件 @
230a1fcb
...
...
@@ -61,6 +61,7 @@ public class TestValue extends TestBase {
testCastTrim
();
testValueResultSet
();
testDataType
();
testArray
();
testUUID
();
testDouble
(
false
);
testDouble
(
true
);
...
...
@@ -330,6 +331,27 @@ public class TestValue extends TestBase {
assertEquals
(
123123123
,
ts
.
getNanos
());
}
private
void
testArray
()
{
ValueArray
src
=
ValueArray
.
get
(
String
.
class
,
new
Value
[]
{
ValueString
.
get
(
"1"
),
ValueString
.
get
(
"22"
),
ValueString
.
get
(
"333"
)});
assertEquals
(
6
,
src
.
getPrecision
());
assertSame
(
src
,
src
.
convertPrecision
(
5
,
false
));
assertSame
(
src
,
src
.
convertPrecision
(
6
,
true
));
ValueArray
exp
=
ValueArray
.
get
(
String
.
class
,
new
Value
[]
{
ValueString
.
get
(
"1"
),
ValueString
.
get
(
"22"
),
ValueString
.
get
(
"33"
)});
Value
got
=
src
.
convertPrecision
(
5
,
true
);
assertEquals
(
exp
,
got
);
assertEquals
(
String
.
class
,
((
ValueArray
)
got
).
getComponentType
());
exp
=
ValueArray
.
get
(
String
.
class
,
new
Value
[]
{
ValueString
.
get
(
"1"
),
ValueString
.
get
(
"22"
)});
got
=
src
.
convertPrecision
(
3
,
true
);
assertEquals
(
exp
,
got
);
assertEquals
(
String
.
class
,
((
ValueArray
)
got
).
getComponentType
());
exp
=
ValueArray
.
get
(
String
.
class
,
new
Value
[
0
]);
got
=
src
.
convertPrecision
(
0
,
true
);
assertEquals
(
exp
,
got
);
assertEquals
(
String
.
class
,
((
ValueArray
)
got
).
getComponentType
());
}
private
void
testUUID
()
{
long
maxHigh
=
0
,
maxLow
=
0
,
minHigh
=
-
1L
,
minLow
=
-
1L
;
for
(
int
i
=
0
;
i
<
100
;
i
++)
{
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论