Skip to content
项目
群组
代码片段
帮助
正在加载...
帮助
为 GitLab 提交贡献
登录/注册
切换导航
H
h2database
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
计划
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
Administrator
h2database
Commits
a751ce4b
提交
a751ce4b
authored
6 年前
作者:
Noel Grandin
浏览文件
操作
浏览文件
下载
差异文件
Merge remote-tracking branch 'upstream/master' into testing_pagestore
上级
74c35a5c
5365e664
全部展开
隐藏空白字符变更
内嵌
并排
正在显示
10 个修改的文件
包含
1108 行增加
和
80 行删除
+1108
-80
Insert.java
h2/src/main/org/h2/command/dml/Insert.java
+30
-11
MVPlainTempResult.java
h2/src/main/org/h2/mvstore/db/MVPlainTempResult.java
+144
-0
MVSortedTempResult.java
h2/src/main/org/h2/mvstore/db/MVSortedTempResult.java
+289
-0
MVTableEngine.java
h2/src/main/org/h2/mvstore/db/MVTableEngine.java
+10
-6
MVTempResult.java
h2/src/main/org/h2/mvstore/db/MVTempResult.java
+194
-0
LocalResult.java
h2/src/main/org/h2/result/LocalResult.java
+11
-3
ResultTempTable.java
h2/src/main/org/h2/result/ResultTempTable.java
+37
-28
TempFileDeleter.java
h2/src/main/org/h2/util/TempFileDeleter.java
+49
-32
TestBigResult.java
h2/src/test/org/h2/test/db/TestBigResult.java
+287
-0
insertIgnore.sql
h2/src/test/org/h2/test/scripts/dml/insertIgnore.sql
+57
-0
没有找到文件。
h2/src/main/org/h2/command/dml/Insert.java
浏览文件 @
a751ce4b
...
...
@@ -23,6 +23,7 @@ import org.h2.expression.Expression;
import
org.h2.expression.ExpressionColumn
;
import
org.h2.expression.Parameter
;
import
org.h2.expression.SequenceValue
;
import
org.h2.expression.ValueExpression
;
import
org.h2.index.Index
;
import
org.h2.index.PageDataIndex
;
import
org.h2.message.DbException
;
...
...
@@ -183,7 +184,7 @@ public class Insert extends Prepared implements ResultTarget {
try
{
table
.
addRow
(
session
,
newRow
);
}
catch
(
DbException
de
)
{
if
(
handleOnDuplicate
(
de
))
{
if
(
handleOnDuplicate
(
de
,
null
))
{
// MySQL returns 2 for updated row
// TODO: detect no-op change
rowNumber
++;
...
...
@@ -207,9 +208,20 @@ public class Insert extends Prepared implements ResultTarget {
while
(
rows
.
next
())
{
generatedKeys
.
nextRow
();
Value
[]
r
=
rows
.
currentRow
();
Row
newRow
=
addRowImpl
(
r
);
if
(
newRow
!=
null
)
{
generatedKeys
.
confirmRow
(
newRow
);
try
{
Row
newRow
=
addRowImpl
(
r
);
if
(
newRow
!=
null
)
{
generatedKeys
.
confirmRow
(
newRow
);
}
}
catch
(
DbException
de
)
{
if
(
handleOnDuplicate
(
de
,
r
))
{
// MySQL returns 2 for updated row
// TODO: detect no-op change
rowNumber
++;
}
else
{
// INSERT IGNORE case
rowNumber
--;
}
}
}
rows
.
close
();
...
...
@@ -365,9 +377,10 @@ public class Insert extends Prepared implements ResultTarget {
/**
* @param de duplicate key exception
* @param currentRow current row values (optional)
* @return {@code true} if row was updated, {@code false} if row was ignored
*/
private
boolean
handleOnDuplicate
(
DbException
de
)
{
private
boolean
handleOnDuplicate
(
DbException
de
,
Value
[]
currentRow
)
{
if
(
de
.
getErrorCode
()
!=
ErrorCode
.
DUPLICATE_KEY_1
)
{
throw
de
;
}
...
...
@@ -381,13 +394,20 @@ public class Insert extends Prepared implements ResultTarget {
ArrayList
<
String
>
variableNames
=
new
ArrayList
<>(
duplicateKeyAssignmentMap
.
size
());
Expression
[]
row
=
list
.
get
(
getCurrentRowNumber
()
-
1
);
Expression
[]
row
=
(
currentRow
==
null
)
?
list
.
get
(
getCurrentRowNumber
()
-
1
)
:
new
Expression
[
columns
.
length
];
for
(
int
i
=
0
;
i
<
columns
.
length
;
i
++)
{
String
key
=
table
.
getSchema
().
getName
()
+
"."
+
table
.
getName
()
+
"."
+
columns
[
i
].
getName
();
variableNames
.
add
(
key
);
session
.
setVariable
(
key
,
row
[
i
].
getValue
(
session
));
Value
value
;
if
(
currentRow
!=
null
)
{
value
=
currentRow
[
i
];
row
[
i
]
=
ValueExpression
.
get
(
value
);
}
else
{
value
=
row
[
i
].
getValue
(
session
);
}
session
.
setVariable
(
key
,
value
);
}
StatementBuilder
buff
=
new
StatementBuilder
(
"UPDATE "
);
...
...
@@ -403,7 +423,7 @@ public class Insert extends Prepared implements ResultTarget {
throw
DbException
.
getUnsupportedException
(
"Unable to apply ON DUPLICATE KEY UPDATE, no index found!"
);
}
buff
.
append
(
prepareUpdateCondition
(
foundIndex
).
getSQL
());
buff
.
append
(
prepareUpdateCondition
(
foundIndex
,
row
).
getSQL
());
String
sql
=
buff
.
toString
();
Update
command
=
(
Update
)
session
.
prepare
(
sql
);
command
.
setUpdateToCurrentValuesReturnsZero
(
true
);
...
...
@@ -418,7 +438,7 @@ public class Insert extends Prepared implements ResultTarget {
return
result
;
}
private
Expression
prepareUpdateCondition
(
Index
foundIndex
)
{
private
Expression
prepareUpdateCondition
(
Index
foundIndex
,
Expression
[]
row
)
{
// MVPrimaryIndex is playing fast and loose with it's implementation of
// the Index interface.
// It returns all of the columns in the table when we call
...
...
@@ -440,7 +460,6 @@ public class Insert extends Prepared implements ResultTarget {
indexedColumns
=
foundIndex
.
getColumns
();
}
Expression
[]
row
=
list
.
get
(
getCurrentRowNumber
()
-
1
);
Expression
condition
=
null
;
for
(
Column
column
:
indexedColumns
)
{
ExpressionColumn
expr
=
new
ExpressionColumn
(
session
.
getDatabase
(),
...
...
This diff is collapsed.
Click to expand it.
h2/src/main/org/h2/mvstore/db/MVPlainTempResult.java
0 → 100644
浏览文件 @
a751ce4b
/*
* Copyright 2004-2018 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
.
mvstore
.
db
;
import
org.h2.engine.Database
;
import
org.h2.expression.Expression
;
import
org.h2.message.DbException
;
import
org.h2.mvstore.Cursor
;
import
org.h2.mvstore.MVMap
;
import
org.h2.mvstore.MVMap.Builder
;
import
org.h2.result.ResultExternal
;
import
org.h2.value.Value
;
import
org.h2.value.ValueArray
;
import
org.h2.value.ValueLong
;
/**
* Plain temporary result.
*/
class
MVPlainTempResult
extends
MVTempResult
{
/**
* The type of the values in the main map and keys in the index.
*/
private
final
ValueDataType
valueType
;
/**
* Map with identities of rows as keys rows as values.
*/
private
final
MVMap
<
ValueLong
,
ValueArray
>
map
;
/**
* Counter for the identities of rows. A separate counter is used instead of
* {@link #rowCount} because rows due to presence of {@link #removeRow(Value[])}
* method to ensure that each row will have an own identity.
*/
private
long
counter
;
/**
* Optional index. This index is created only if {@link #contains(Value[])}
* method is invoked. Only the root result should have an index if required.
*/
private
MVMap
<
ValueArray
,
Boolean
>
index
;
/**
* Cursor for the {@link #next()} method.
*/
private
Cursor
<
ValueLong
,
ValueArray
>
cursor
;
/**
* Creates a shallow copy of the result.
*
* @param parent
* parent result
*/
private
MVPlainTempResult
(
MVPlainTempResult
parent
)
{
super
(
parent
);
this
.
valueType
=
null
;
this
.
map
=
parent
.
map
;
}
/**
* Creates a new plain temporary result.
*
* @param database
* database
* @param expressions
* column expressions
*/
MVPlainTempResult
(
Database
database
,
Expression
[]
expressions
)
{
super
(
database
);
ValueDataType
keyType
=
new
ValueDataType
(
null
,
null
,
null
);
valueType
=
new
ValueDataType
(
database
.
getCompareMode
(),
database
,
new
int
[
expressions
.
length
]);
Builder
<
ValueLong
,
ValueArray
>
builder
=
new
MVMap
.
Builder
<
ValueLong
,
ValueArray
>().
keyType
(
keyType
)
.
valueType
(
valueType
);
map
=
store
.
openMap
(
"tmp"
,
builder
);
}
@Override
public
int
addRow
(
Value
[]
values
)
{
assert
parent
==
null
&&
index
==
null
;
map
.
put
(
ValueLong
.
get
(
counter
++),
ValueArray
.
get
(
values
));
return
++
rowCount
;
}
@Override
public
boolean
contains
(
Value
[]
values
)
{
// Only parent result maintains the index
if
(
parent
!=
null
)
{
return
parent
.
contains
(
values
);
}
if
(
index
==
null
)
{
createIndex
();
}
return
index
.
containsKey
(
ValueArray
.
get
(
values
));
}
private
void
createIndex
()
{
Builder
<
ValueArray
,
Boolean
>
builder
=
new
MVMap
.
Builder
<
ValueArray
,
Boolean
>().
keyType
(
valueType
);
index
=
store
.
openMap
(
"idx"
,
builder
);
Cursor
<
ValueLong
,
ValueArray
>
c
=
map
.
cursor
(
null
);
while
(
c
.
hasNext
())
{
c
.
next
();
index
.
putIfAbsent
(
c
.
getValue
(),
true
);
}
}
@Override
public
synchronized
ResultExternal
createShallowCopy
()
{
if
(
parent
!=
null
)
{
return
parent
.
createShallowCopy
();
}
if
(
closed
)
{
return
null
;
}
childCount
++;
return
new
MVPlainTempResult
(
this
);
}
@Override
public
Value
[]
next
()
{
if
(
cursor
==
null
)
{
cursor
=
map
.
cursor
(
null
);
}
if
(!
cursor
.
hasNext
())
{
return
null
;
}
cursor
.
next
();
return
cursor
.
getValue
().
getList
();
}
@Override
public
int
removeRow
(
Value
[]
values
)
{
throw
DbException
.
getUnsupportedException
(
"removeRow()"
);
}
@Override
public
void
reset
()
{
cursor
=
null
;
}
}
This diff is collapsed.
Click to expand it.
h2/src/main/org/h2/mvstore/db/MVSortedTempResult.java
0 → 100644
浏览文件 @
a751ce4b
/*
* Copyright 2004-2018 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
.
mvstore
.
db
;
import
java.util.BitSet
;
import
org.h2.engine.Database
;
import
org.h2.expression.Expression
;
import
org.h2.mvstore.Cursor
;
import
org.h2.mvstore.MVMap
;
import
org.h2.mvstore.MVMap.Builder
;
import
org.h2.result.ResultExternal
;
import
org.h2.result.SortOrder
;
import
org.h2.value.Value
;
import
org.h2.value.ValueArray
;
/**
* Sorted temporary result.
*
* <p>
* This result is used for distinct and/or sorted results.
* </p>
*/
class
MVSortedTempResult
extends
MVTempResult
{
/**
* Whether this result is distinct.
*/
private
final
boolean
distinct
;
/**
* Mapping of indexes of columns to its positions in the store, or {@code null}
* if columns are not reordered.
*/
private
final
int
[]
indexes
;
/**
* Map with rows as keys and counts of duplicate rows as values. If this map is
* distinct all values are 1.
*/
private
final
MVMap
<
ValueArray
,
Long
>
map
;
/**
* Cursor for the {@link #next()} method.
*/
private
Cursor
<
ValueArray
,
Long
>
cursor
;
/**
* Current value for the {@link #next()} method. Used in non-distinct results
* with duplicate rows.
*/
private
Value
[]
current
;
/**
* Count of remaining duplicate rows for the {@link #next()} method. Used in
* non-distinct results.
*/
private
long
valueCount
;
/**
* Creates a shallow copy of the result.
*
* @param parent
* parent result
*/
private
MVSortedTempResult
(
MVSortedTempResult
parent
)
{
super
(
parent
);
this
.
distinct
=
parent
.
distinct
;
this
.
indexes
=
parent
.
indexes
;
this
.
map
=
parent
.
map
;
this
.
rowCount
=
parent
.
rowCount
;
}
/**
* Creates a new sorted temporary result.
*
* @param database
* database
* @param expressions
* column expressions
* @param distinct
* whether this result should be distinct
* @param sort
* sort order, or {@code null} if this result does not
* need any sorting
*/
MVSortedTempResult
(
Database
database
,
Expression
[]
expressions
,
boolean
distinct
,
SortOrder
sort
)
{
super
(
database
);
this
.
distinct
=
distinct
;
int
length
=
expressions
.
length
;
int
[]
sortTypes
=
new
int
[
length
];
int
[]
indexes
;
if
(
sort
!=
null
)
{
/*
* If sorting is specified we need to reorder columns in requested order and set
* sort types (ASC, DESC etc) for them properly.
*/
indexes
=
new
int
[
length
];
int
[]
colIndex
=
sort
.
getQueryColumnIndexes
();
int
len
=
colIndex
.
length
;
// This set is used to remember columns that are already included
BitSet
used
=
new
BitSet
();
for
(
int
i
=
0
;
i
<
len
;
i
++)
{
int
idx
=
colIndex
[
i
];
assert
!
used
.
get
(
idx
);
used
.
set
(
idx
);
indexes
[
i
]
=
idx
;
sortTypes
[
i
]
=
sort
.
getSortTypes
()[
i
];
}
/*
* Because this result may have more columns than specified in sorting we need
* to add all remaining columns to the mapping of columns. A default sorting
* order (ASC / 0) will be used for them.
*/
int
idx
=
0
;
for
(
int
i
=
len
;
i
<
length
;
i
++)
{
idx
=
used
.
nextClearBit
(
idx
);
indexes
[
i
]
=
idx
;
idx
++;
}
/*
* Sometimes columns may be not reordered. Because reordering of columns
* slightly slows down other methods we check whether columns are really
* reordered or have the same order.
*/
sameOrder:
{
for
(
int
i
=
0
;
i
<
length
;
i
++)
{
if
(
indexes
[
i
]
!=
i
)
{
// Columns are reordered
break
sameOrder
;
}
}
/*
* Columns are not reordered, set this field to null to disable reordering in
* other methods.
*/
indexes
=
null
;
}
}
else
{
// Columns are not reordered if sort order is not specified
indexes
=
null
;
}
this
.
indexes
=
indexes
;
ValueDataType
keyType
=
new
ValueDataType
(
database
.
getCompareMode
(),
database
,
sortTypes
);
Builder
<
ValueArray
,
Long
>
builder
=
new
MVMap
.
Builder
<
ValueArray
,
Long
>().
keyType
(
keyType
);
map
=
store
.
openMap
(
"tmp"
,
builder
);
}
@Override
public
int
addRow
(
Value
[]
values
)
{
assert
parent
==
null
;
ValueArray
key
=
getKey
(
values
);
if
(
distinct
)
{
// Add a row and increment the counter only if row does not exist
if
(
map
.
putIfAbsent
(
key
,
1L
)
==
null
)
{
rowCount
++;
}
}
else
{
// Try to set counter to 1 first if such row does not exist yet
Long
old
=
map
.
putIfAbsent
(
key
,
1L
);
if
(
old
!=
null
)
{
// This rows is already in the map, increment its own counter
map
.
put
(
key
,
old
+
1
);
}
rowCount
++;
}
return
rowCount
;
}
@Override
public
boolean
contains
(
Value
[]
values
)
{
return
map
.
containsKey
(
getKey
(
values
));
}
@Override
public
synchronized
ResultExternal
createShallowCopy
()
{
if
(
parent
!=
null
)
{
return
parent
.
createShallowCopy
();
}
if
(
closed
)
{
return
null
;
}
childCount
++;
return
new
MVSortedTempResult
(
this
);
}
/**
* Reorder values if required and convert them into {@link ValueArray}.
*
* @param values
* values
* @return ValueArray for maps
*/
private
ValueArray
getKey
(
Value
[]
values
)
{
if
(
indexes
!=
null
)
{
Value
[]
r
=
new
Value
[
indexes
.
length
];
for
(
int
i
=
0
;
i
<
indexes
.
length
;
i
++)
{
r
[
indexes
[
i
]]
=
values
[
i
];
}
values
=
r
;
}
return
ValueArray
.
get
(
values
);
}
/**
* Reorder values back if required.
*
* @param key
* reordered values
* @return original values
*/
private
Value
[]
getValue
(
Value
[]
key
)
{
if
(
indexes
!=
null
)
{
Value
[]
r
=
new
Value
[
indexes
.
length
];
for
(
int
i
=
0
;
i
<
indexes
.
length
;
i
++)
{
r
[
i
]
=
key
[
indexes
[
i
]];
}
key
=
r
;
}
return
key
;
}
@Override
public
Value
[]
next
()
{
if
(
cursor
==
null
)
{
cursor
=
map
.
cursor
(
null
);
current
=
null
;
valueCount
=
0L
;
}
// If we have multiple rows with the same values return them all
if
(--
valueCount
>
0
)
{
/*
* Underflow in valueCount is hypothetically possible after a lot of invocations
* (not really possible in practice), but current will be null anyway.
*/
return
current
;
}
if
(!
cursor
.
hasNext
())
{
// Set current to null to be sure
current
=
null
;
return
null
;
}
// Read the next row
current
=
getValue
(
cursor
.
next
().
getList
());
/*
* If valueCount is greater than 1 that is possible for non-distinct results the
* following invocations of next() will use this.current and this.valueCount.
*/
valueCount
=
cursor
.
getValue
();
return
current
;
}
@Override
public
int
removeRow
(
Value
[]
values
)
{
assert
parent
==
null
;
ValueArray
key
=
getKey
(
values
);
if
(
distinct
)
{
// If an entry was removed decrement the counter
if
(
map
.
remove
(
key
)
!=
null
)
{
rowCount
--;
}
}
else
{
Long
old
=
map
.
remove
(
key
);
if
(
old
!=
null
)
{
long
l
=
old
;
if
(
l
>
1
)
{
/*
* We have more than one such row. Decrement its counter by 1 and put this row
* back into map.
*/
map
.
put
(
key
,
l
-
1
);
}
rowCount
--;
}
}
return
rowCount
;
}
@Override
public
void
reset
()
{
cursor
=
null
;
current
=
null
;
valueCount
=
0L
;
}
}
This diff is collapsed.
Click to expand it.
h2/src/main/org/h2/mvstore/db/MVTableEngine.java
浏览文件 @
a751ce4b
...
...
@@ -75,12 +75,7 @@ public class MVTableEngine implements TableEngine {
}
if
(
key
!=
null
)
{
encrypted
=
true
;
char
[]
password
=
new
char
[
key
.
length
/
2
];
for
(
int
i
=
0
;
i
<
password
.
length
;
i
++)
{
password
[
i
]
=
(
char
)
(((
key
[
i
+
i
]
&
255
)
<<
16
)
|
((
key
[
i
+
i
+
1
])
&
255
));
}
builder
.
encryptionKey
(
password
);
builder
.
encryptionKey
(
decodePassword
(
key
));
}
if
(
db
.
getSettings
().
compressData
)
{
builder
.
compress
();
...
...
@@ -101,6 +96,15 @@ public class MVTableEngine implements TableEngine {
return
store
;
}
static
char
[]
decodePassword
(
byte
[]
key
)
{
char
[]
password
=
new
char
[
key
.
length
/
2
];
for
(
int
i
=
0
;
i
<
password
.
length
;
i
++)
{
password
[
i
]
=
(
char
)
(((
key
[
i
+
i
]
&
255
)
<<
16
)
|
((
key
[
i
+
i
+
1
])
&
255
));
}
return
password
;
}
@Override
public
TableBase
createTable
(
CreateTableData
data
)
{
Database
db
=
data
.
session
.
getDatabase
();
...
...
This diff is collapsed.
Click to expand it.
h2/src/main/org/h2/mvstore/db/MVTempResult.java
0 → 100644
浏览文件 @
a751ce4b
/*
* Copyright 2004-2018 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
.
mvstore
.
db
;
import
java.io.IOException
;
import
java.lang.ref.Reference
;
import
java.util.ArrayList
;
import
org.h2.engine.Constants
;
import
org.h2.engine.Database
;
import
org.h2.expression.Expression
;
import
org.h2.message.DbException
;
import
org.h2.mvstore.MVStore
;
import
org.h2.mvstore.MVStore.Builder
;
import
org.h2.result.ResultExternal
;
import
org.h2.result.SortOrder
;
import
org.h2.store.fs.FileUtils
;
import
org.h2.util.TempFileDeleter
;
import
org.h2.value.Value
;
/**
* Temporary result.
*
* <p>
* A separate MVStore in a temporary file is used for each result. The file is
* removed when this result and all its copies are closed.
* {@link TempFileDeleter} is also used to delete this file if results are not
* closed properly.
* </p>
*/
public
abstract
class
MVTempResult
implements
ResultExternal
{
private
static
final
class
CloseImpl
implements
AutoCloseable
{
/**
* MVStore.
*/
private
final
MVStore
store
;
/**
* File name.
*/
private
final
String
fileName
;
CloseImpl
(
MVStore
store
,
String
fileName
)
{
this
.
store
=
store
;
this
.
fileName
=
fileName
;
}
@Override
public
void
close
()
throws
Exception
{
store
.
closeImmediately
();
FileUtils
.
tryDelete
(
fileName
);
}
}
/**
* Creates MVStore-based temporary result.
*
* @param database
* database
* @param expressions
* expressions
* @param distinct
* is output distinct
* @param sort
* sort order, or {@code null}
* @return temporary result
*/
public
static
ResultExternal
of
(
Database
database
,
Expression
[]
expressions
,
boolean
distinct
,
SortOrder
sort
)
{
return
distinct
||
sort
!=
null
?
new
MVSortedTempResult
(
database
,
expressions
,
distinct
,
sort
)
:
new
MVPlainTempResult
(
database
,
expressions
);
}
/**
* MVStore.
*/
final
MVStore
store
;
/**
* Count of rows. Used only in a root results, copies always have 0 value.
*/
int
rowCount
;
/**
* Parent store for copies. If {@code null} this result is a root result.
*/
final
MVTempResult
parent
;
/**
* Count of child results.
*/
int
childCount
;
/**
* Whether this result is closed.
*/
boolean
closed
;
/**
* Temporary file deleter.
*/
private
final
TempFileDeleter
tempFileDeleter
;
/**
* Closeable to close the storage.
*/
private
final
CloseImpl
closeable
;
/**
* Reference to the record in the temporary file deleter.
*/
private
final
Reference
<?>
fileRef
;
/**
* Creates a shallow copy of the result.
*
* @param parent
* parent result
*/
MVTempResult
(
MVTempResult
parent
)
{
this
.
parent
=
parent
;
this
.
store
=
parent
.
store
;
this
.
tempFileDeleter
=
null
;
this
.
closeable
=
null
;
this
.
fileRef
=
null
;
}
/**
* Creates a new temporary result.
*
* @param database
* database
*/
MVTempResult
(
Database
database
)
{
try
{
String
fileName
=
FileUtils
.
createTempFile
(
"h2tmp"
,
Constants
.
SUFFIX_TEMP_FILE
,
false
,
true
);
Builder
builder
=
new
MVStore
.
Builder
().
fileName
(
fileName
);
byte
[]
key
=
database
.
getFileEncryptionKey
();
if
(
key
!=
null
)
{
builder
.
encryptionKey
(
MVTableEngine
.
decodePassword
(
key
));
}
store
=
builder
.
open
();
tempFileDeleter
=
database
.
getTempFileDeleter
();
closeable
=
new
CloseImpl
(
store
,
fileName
);
fileRef
=
tempFileDeleter
.
addFile
(
closeable
,
this
);
}
catch
(
IOException
e
)
{
throw
DbException
.
convert
(
e
);
}
parent
=
null
;
}
@Override
public
int
addRows
(
ArrayList
<
Value
[]>
rows
)
{
for
(
Value
[]
row
:
rows
)
{
addRow
(
row
);
}
return
rowCount
;
}
@Override
public
synchronized
void
close
()
{
if
(
closed
)
{
return
;
}
closed
=
true
;
if
(
parent
!=
null
)
{
parent
.
closeChild
();
}
else
{
if
(
childCount
==
0
)
{
delete
();
}
}
}
private
synchronized
void
closeChild
()
{
if
(--
childCount
==
0
&&
closed
)
{
delete
();
}
}
private
void
delete
()
{
tempFileDeleter
.
deleteFile
(
fileRef
,
closeable
);
}
@Override
public
void
done
()
{
// Do nothing
}
}
This diff is collapsed.
Click to expand it.
h2/src/main/org/h2/result/LocalResult.java
浏览文件 @
a751ce4b
...
...
@@ -15,6 +15,7 @@ import org.h2.engine.Session;
import
org.h2.engine.SessionInterface
;
import
org.h2.expression.Expression
;
import
org.h2.message.DbException
;
import
org.h2.mvstore.db.MVTempResult
;
import
org.h2.util.Utils
;
import
org.h2.util.ValueHashMap
;
import
org.h2.value.DataType
;
...
...
@@ -289,6 +290,13 @@ public class LocalResult implements ResultInterface, ResultTarget {
return
ValueArray
.
get
(
values
);
}
private
void
createExternalResult
()
{
Database
database
=
session
.
getDatabase
();
external
=
database
.
getMvStore
()
!=
null
?
MVTempResult
.
of
(
database
,
expressions
,
distinct
,
sort
)
:
new
ResultTempTable
(
session
,
expressions
,
distinct
,
sort
);
}
/**
* Add a row to this object.
*
...
...
@@ -303,7 +311,7 @@ public class LocalResult implements ResultInterface, ResultTarget {
distinctRows
.
put
(
array
,
values
);
rowCount
=
distinctRows
.
size
();
if
(
rowCount
>
maxMemoryRows
)
{
external
=
new
ResultTempTable
(
session
,
expressions
,
true
,
sort
);
createExternalResult
(
);
rowCount
=
external
.
addRows
(
distinctRows
.
values
());
distinctRows
=
null
;
}
...
...
@@ -316,7 +324,7 @@ public class LocalResult implements ResultInterface, ResultTarget {
rowCount
++;
if
(
rows
.
size
()
>
maxMemoryRows
)
{
if
(
external
==
null
)
{
external
=
new
ResultTempTable
(
session
,
expressions
,
false
,
sort
);
createExternalResult
(
);
}
addRowsToDisk
();
}
...
...
@@ -353,7 +361,7 @@ public class LocalResult implements ResultInterface, ResultTarget {
break
;
}
if
(
external
==
null
)
{
external
=
new
ResultTempTable
(
session
,
expressions
,
true
,
sort
);
createExternalResult
(
);
}
rows
.
add
(
list
);
if
(
rows
.
size
()
>
maxMemoryRows
)
{
...
...
This diff is collapsed.
Click to expand it.
h2/src/main/org/h2/result/ResultTempTable.java
浏览文件 @
a751ce4b
...
...
@@ -7,6 +7,8 @@ package org.h2.result;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.BitSet
;
import
org.h2.command.ddl.CreateTableData
;
import
org.h2.engine.Constants
;
import
org.h2.engine.Database
;
...
...
@@ -88,26 +90,38 @@ public class ResultTempTable implements ResultExternal {
}
private
void
createIndex
()
{
IndexColumn
[]
indexCols
=
null
;
// If we need to do distinct, the distinct columns may not match the
// sort columns. So we need to disregard the sort. Not ideal.
if
(
sort
!=
null
&&
!
distinct
)
{
IndexColumn
[]
indexCols
;
if
(
sort
!=
null
)
{
int
[]
colIndex
=
sort
.
getQueryColumnIndexes
();
indexCols
=
new
IndexColumn
[
colIndex
.
length
];
for
(
int
i
=
0
;
i
<
colIndex
.
length
;
i
++)
{
IndexColumn
indexColumn
=
new
IndexColumn
();
indexColumn
.
column
=
table
.
getColumn
(
colIndex
[
i
]);
indexColumn
.
sortType
=
sort
.
getSortTypes
()[
i
];
indexColumn
.
columnName
=
COLUMN_NAME
+
i
;
indexCols
[
i
]
=
indexColumn
;
int
len
=
colIndex
.
length
;
if
(
distinct
)
{
BitSet
used
=
new
BitSet
();
indexCols
=
new
IndexColumn
[
columnCount
];
for
(
int
i
=
0
;
i
<
len
;
i
++)
{
int
idx
=
colIndex
[
i
];
used
.
set
(
idx
);
IndexColumn
indexColumn
=
createIndexColumn
(
idx
);
indexColumn
.
sortType
=
sort
.
getSortTypes
()[
i
];
indexCols
[
i
]
=
indexColumn
;
}
int
idx
=
0
;
for
(
int
i
=
len
;
i
<
columnCount
;
i
++)
{
idx
=
used
.
nextClearBit
(
idx
);
indexCols
[
i
]
=
createIndexColumn
(
idx
);
idx
++;
}
}
else
{
indexCols
=
new
IndexColumn
[
len
];
for
(
int
i
=
0
;
i
<
len
;
i
++)
{
IndexColumn
indexColumn
=
createIndexColumn
(
colIndex
[
i
]);
indexColumn
.
sortType
=
sort
.
getSortTypes
()[
i
];
indexCols
[
i
]
=
indexColumn
;
}
}
}
else
{
indexCols
=
new
IndexColumn
[
columnCount
];
for
(
int
i
=
0
;
i
<
columnCount
;
i
++)
{
IndexColumn
indexColumn
=
new
IndexColumn
();
indexColumn
.
column
=
table
.
getColumn
(
i
);
indexColumn
.
columnName
=
COLUMN_NAME
+
i
;
indexCols
[
i
]
=
indexColumn
;
indexCols
[
i
]
=
createIndexColumn
(
i
);
}
}
String
indexName
=
table
.
getSchema
().
getUniqueIndexName
(
session
,
...
...
@@ -118,6 +132,13 @@ public class ResultTempTable implements ResultExternal {
indexType
,
true
,
null
);
}
private
IndexColumn
createIndexColumn
(
int
index
)
{
IndexColumn
indexColumn
=
new
IndexColumn
();
indexColumn
.
column
=
table
.
getColumn
(
index
);
indexColumn
.
columnName
=
COLUMN_NAME
+
index
;
return
indexColumn
;
}
@Override
public
synchronized
ResultExternal
createShallowCopy
()
{
if
(
parent
!=
null
)
{
...
...
@@ -257,19 +278,7 @@ public class ResultTempTable implements ResultExternal {
}
else
{
idx
=
table
.
getScanIndex
(
session
);
}
if
(
session
.
getDatabase
().
getMvStore
()
!=
null
)
{
// sometimes the transaction is already committed,
// in which case we can't use the session
if
(
idx
.
getRowCount
(
session
)
==
0
&&
rowCount
>
0
)
{
// this means querying is not transactional
resultCursor
=
idx
.
find
((
Session
)
null
,
null
,
null
);
}
else
{
// the transaction is still open
resultCursor
=
idx
.
find
(
session
,
null
,
null
);
}
}
else
{
resultCursor
=
idx
.
find
(
session
,
null
,
null
);
}
resultCursor
=
idx
.
find
(
session
,
null
,
null
);
}
if
(!
resultCursor
.
next
())
{
return
null
;
...
...
This diff is collapsed.
Click to expand it.
h2/src/main/org/h2/util/TempFileDeleter.java
浏览文件 @
a751ce4b
...
...
@@ -21,7 +21,7 @@ import org.h2.store.fs.FileUtils;
public
class
TempFileDeleter
{
private
final
ReferenceQueue
<
Object
>
queue
=
new
ReferenceQueue
<>();
private
final
HashMap
<
PhantomReference
<?>,
String
>
refMap
=
new
HashMap
<>();
private
final
HashMap
<
PhantomReference
<?>,
Object
>
refMap
=
new
HashMap
<>();
private
TempFileDeleter
()
{
// utility class
...
...
@@ -32,43 +32,59 @@ public class TempFileDeleter {
}
/**
* Add a file
to the list of temp files to delete. The file is deleted onc
e
* the file object is garbage collected.
* Add a file
or a closeable to the list of temporary objects to delete. Th
e
*
file is deleted once
the file object is garbage collected.
*
* @param
fileName the file nam
e
* @param
file
the object to monitor
* @return the reference that can be used to stop deleting the file
* @param
resource the file name or the closeabl
e
* @param
monitor
the object to monitor
* @return the reference that can be used to stop deleting the file
or closing the closeable
*/
public
synchronized
Reference
<?>
addFile
(
String
fileName
,
Object
file
)
{
IOUtils
.
trace
(
"TempFileDeleter.addFile"
,
fileName
,
file
);
PhantomReference
<?>
ref
=
new
PhantomReference
<>(
file
,
queue
);
refMap
.
put
(
ref
,
fileName
);
public
synchronized
Reference
<?>
addFile
(
Object
resource
,
Object
monitor
)
{
if
(!(
resource
instanceof
String
)
&&
!(
resource
instanceof
AutoCloseable
))
{
throw
DbException
.
getUnsupportedException
(
"Unsupported resource "
+
resource
);
}
IOUtils
.
trace
(
"TempFileDeleter.addFile"
,
resource
instanceof
String
?
(
String
)
resource
:
"-"
,
monitor
);
PhantomReference
<?>
ref
=
new
PhantomReference
<>(
monitor
,
queue
);
refMap
.
put
(
ref
,
resource
);
deleteUnused
();
return
ref
;
}
/**
* Delete the given file now. This will remove the reference from the list.
* Delete the given file or close the closeable now. This will remove the
* reference from the list.
*
* @param ref the reference as returned by addFile
* @param
fileName the file nam
e
* @param
resource the file name or closeabl
e
*/
public
synchronized
void
deleteFile
(
Reference
<?>
ref
,
String
fileNam
e
)
{
public
synchronized
void
deleteFile
(
Reference
<?>
ref
,
Object
resourc
e
)
{
if
(
ref
!=
null
)
{
String
f2
=
refMap
.
remove
(
ref
);
Object
f2
=
refMap
.
remove
(
ref
);
if
(
f2
!=
null
)
{
if
(
SysProperties
.
CHECK
)
{
if
(
fileName
!=
null
&&
!
f2
.
equals
(
fileNam
e
))
{
DbException
.
throwInternalError
(
"f2:"
+
f2
+
" f:"
+
fileNam
e
);
if
(
resource
!=
null
&&
!
f2
.
equals
(
resourc
e
))
{
DbException
.
throwInternalError
(
"f2:"
+
f2
+
" f:"
+
resourc
e
);
}
}
fileNam
e
=
f2
;
resourc
e
=
f2
;
}
}
if
(
fileName
!=
null
&&
FileUtils
.
exists
(
fileName
))
{
if
(
resource
instanceof
String
)
{
String
fileName
=
(
String
)
resource
;
if
(
FileUtils
.
exists
(
fileName
))
{
try
{
IOUtils
.
trace
(
"TempFileDeleter.deleteFile"
,
fileName
,
null
);
FileUtils
.
tryDelete
(
fileName
);
}
catch
(
Exception
e
)
{
// TODO log such errors?
}
}
}
else
if
(
resource
instanceof
AutoCloseable
)
{
AutoCloseable
closeable
=
(
AutoCloseable
)
resource
;
try
{
IOUtils
.
trace
(
"TempFileDeleter.delete
File"
,
fileName
,
null
);
FileUtils
.
tryDelete
(
fileName
);
IOUtils
.
trace
(
"TempFileDeleter.delete
Closeable"
,
"-"
,
null
);
closeable
.
close
(
);
}
catch
(
Exception
e
)
{
// TODO log such errors?
}
...
...
@@ -76,17 +92,17 @@ public class TempFileDeleter {
}
/**
* Delete all registered temp
fil
es.
* Delete all registered temp
resourc
es.
*/
public
void
deleteAll
()
{
for
(
String
tempFil
e
:
new
ArrayList
<>(
refMap
.
values
()))
{
deleteFile
(
null
,
tempFil
e
);
for
(
Object
resourc
e
:
new
ArrayList
<>(
refMap
.
values
()))
{
deleteFile
(
null
,
resourc
e
);
}
deleteUnused
();
}
/**
* Delete all unused
fil
es now.
* Delete all unused
resourc
es now.
*/
public
void
deleteUnused
()
{
while
(
queue
!=
null
)
{
...
...
@@ -99,20 +115,21 @@ public class TempFileDeleter {
}
/**
* This method is called if a file should no longer be deleted
if the object
* is garbage collected.
* This method is called if a file should no longer be deleted
or a resource
*
should no longer be closed if the object
is garbage collected.
*
* @param ref the reference as returned by addFile
* @param
fileName the file nam
e
* @param
resource file name or closeabl
e
*/
public
void
stopAutoDelete
(
Reference
<?>
ref
,
String
fileName
)
{
IOUtils
.
trace
(
"TempFileDeleter.stopAutoDelete"
,
fileName
,
ref
);
public
void
stopAutoDelete
(
Reference
<?>
ref
,
Object
resource
)
{
IOUtils
.
trace
(
"TempFileDeleter.stopAutoDelete"
,
resource
instanceof
String
?
(
String
)
resource
:
"-"
,
ref
);
if
(
ref
!=
null
)
{
String
f2
=
refMap
.
remove
(
ref
);
Object
f2
=
refMap
.
remove
(
ref
);
if
(
SysProperties
.
CHECK
)
{
if
(
f2
==
null
||
!
f2
.
equals
(
fileNam
e
))
{
if
(
f2
==
null
||
!
f2
.
equals
(
resourc
e
))
{
DbException
.
throwInternalError
(
"f2:"
+
f2
+
" "
+
(
f2
==
null
?
""
:
f2
)
+
" f:"
+
fileNam
e
);
" "
+
(
f2
==
null
?
""
:
f2
)
+
" f:"
+
resourc
e
);
}
}
}
...
...
This diff is collapsed.
Click to expand it.
h2/src/test/org/h2/test/db/TestBigResult.java
浏览文件 @
a751ce4b
差异被折叠。
点击展开。
h2/src/test/org/h2/test/scripts/dml/insertIgnore.sql
浏览文件 @
a751ce4b
...
...
@@ -39,3 +39,60 @@ SELECT * FROM TEST ORDER BY ID;
>
4
40
>
5
52
>
rows
(
ordered
):
5
CREATE
TABLE
TESTREF
(
ID
BIGINT
PRIMARY
KEY
,
VALUE
INT
NOT
NULL
);
>
ok
INSERT
INTO
TESTREF
VALUES
(
1
,
11
),
(
2
,
21
),
(
6
,
61
),
(
7
,
71
);
>
update
count
:
4
INSERT
INTO
TEST
(
ID
,
VALUE
)
SELECT
ID
,
VALUE
FROM
TESTREF
;
>
exception
DUPLICATE_KEY_1
SELECT
*
FROM
TEST
ORDER
BY
ID
;
>
ID
VALUE
>
-- -----
>
1
10
>
2
20
>
3
30
>
4
40
>
5
52
>
rows
(
ordered
):
5
INSERT
IGNORE
INTO
TEST
(
ID
,
VALUE
)
SELECT
ID
,
VALUE
FROM
TESTREF
;
>
update
count
:
2
INSERT
IGNORE
INTO
TEST
(
ID
,
VALUE
)
SELECT
ID
,
VALUE
FROM
TESTREF
;
>
ok
SELECT
*
FROM
TEST
ORDER
BY
ID
;
>
ID
VALUE
>
-- -----
>
1
10
>
2
20
>
3
30
>
4
40
>
5
52
>
6
61
>
7
71
>
rows
(
ordered
):
7
INSERT
INTO
TESTREF
VALUES
(
8
,
81
),
(
9
,
91
);
>
update
count
:
2
INSERT
INTO
TEST
(
ID
,
VALUE
)
SELECT
ID
,
VALUE
FROM
TESTREF
ON
DUPLICATE
KEY
UPDATE
VALUE
=
83
;
>
update
count
:
10
SELECT
*
FROM
TEST
ORDER
BY
ID
;
>
ID
VALUE
>
-- -----
>
1
83
>
2
83
>
3
30
>
4
40
>
5
52
>
6
83
>
7
83
>
8
81
>
9
91
>
rows
(
ordered
):
9
This diff is collapsed.
Click to expand it.
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论