提交 f91fa5c2 authored 作者: Noel Grandin's avatar Noel Grandin

Merge remote-tracking branch 'upstream/master' into 1091_new

...@@ -208,7 +208,10 @@ public class Parser { ...@@ -208,7 +208,10 @@ public class Parser {
new Comparator<TableFilter>() { new Comparator<TableFilter>() {
@Override @Override
public int compare(TableFilter o1, TableFilter o2) { public int compare(TableFilter o1, TableFilter o2) {
return o1 == o2 ? 0 : compareTableFilters(o1, o2); if (o1 == o2)
return 0;
assert o1.getOrderInFrom() != o2.getOrderInFrom();
return o1.getOrderInFrom() > o2.getOrderInFrom() ? 1 : -1;
} }
}; };
...@@ -2162,41 +2165,8 @@ public class Parser { ...@@ -2162,41 +2165,8 @@ public class Parser {
// Parser can reorder joined table filters, need to explicitly sort them // Parser can reorder joined table filters, need to explicitly sort them
// to get the order as it was in the original query. // to get the order as it was in the original query.
if (session.isForceJoinOrder()) { if (session.isForceJoinOrder()) {
sortTableFilters(command.getTopFilters()); Collections.sort(command.getTopFilters(), TABLE_FILTER_COMPARATOR);
}
}
private static void sortTableFilters(ArrayList<TableFilter> filters) {
if (filters.size() < 2) {
return;
}
// Most probably we are already sorted correctly.
boolean sorted = true;
TableFilter prev = filters.get(0);
for (int i = 1; i < filters.size(); i++) {
TableFilter next = filters.get(i);
if (compareTableFilters(prev, next) > 0) {
sorted = false;
break;
}
prev = next;
} }
// If not, then sort manually.
if (!sorted) {
Collections.sort(filters, TABLE_FILTER_COMPARATOR);
}
}
/**
* Find out which of the table filters appears first in the "from" clause.
*
* @param o1 the first table filter
* @param o2 the second table filter
* @return -1 if o1 appears first, and 1 if o2 appears first
*/
static int compareTableFilters(TableFilter o1, TableFilter o2) {
assert o1.getOrderInFrom() != o2.getOrderInFrom();
return o1.getOrderInFrom() > o2.getOrderInFrom() ? 1 : -1;
} }
private void parseJoinTableFilter(TableFilter top, final Select command) { private void parseJoinTableFilter(TableFilter top, final Select command) {
......
...@@ -23,7 +23,6 @@ public class AlterIndexRename extends DefineCommand { ...@@ -23,7 +23,6 @@ public class AlterIndexRename extends DefineCommand {
private boolean ifExists; private boolean ifExists;
private Schema oldSchema; private Schema oldSchema;
private String oldIndexName; private String oldIndexName;
private Index oldIndex;
private String newIndexName; private String newIndexName;
public AlterIndexRename(Session session) { public AlterIndexRename(Session session) {
...@@ -50,7 +49,7 @@ public class AlterIndexRename extends DefineCommand { ...@@ -50,7 +49,7 @@ public class AlterIndexRename extends DefineCommand {
public int update() { public int update() {
session.commit(true); session.commit(true);
Database db = session.getDatabase(); Database db = session.getDatabase();
oldIndex = oldSchema.findIndex(session, oldIndexName); Index oldIndex = oldSchema.findIndex(session, oldIndexName);
if (oldIndex == null) { if (oldIndex == null) {
if (!ifExists) { if (!ifExists) {
throw DbException.get(ErrorCode.INDEX_NOT_FOUND_1, throw DbException.get(ErrorCode.INDEX_NOT_FOUND_1,
......
...@@ -137,7 +137,6 @@ public class CreateTable extends CommandWithColumns { ...@@ -137,7 +137,6 @@ public class CreateTable extends CommandWithColumns {
} }
} }
HashSet<DbObject> set = new HashSet<>(); HashSet<DbObject> set = new HashSet<>();
set.clear();
table.addDependencies(set); table.addDependencies(set);
for (DbObject obj : set) { for (DbObject obj : set) {
if (obj == table) { if (obj == table) {
......
...@@ -73,7 +73,6 @@ import org.h2.util.SourceCompiler; ...@@ -73,7 +73,6 @@ import org.h2.util.SourceCompiler;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import org.h2.util.TempFileDeleter; import org.h2.util.TempFileDeleter;
import org.h2.util.Utils; import org.h2.util.Utils;
import org.h2.value.CaseInsensitiveConcurrentMap;
import org.h2.value.CaseInsensitiveMap; import org.h2.value.CaseInsensitiveMap;
import org.h2.value.CompareMode; import org.h2.value.CompareMode;
import org.h2.value.NullableKeyConcurrentMap; import org.h2.value.NullableKeyConcurrentMap;
...@@ -2860,9 +2859,7 @@ public class Database implements DataHandler { ...@@ -2860,9 +2859,7 @@ public class Database implements DataHandler {
* @return the hash map * @return the hash map
*/ */
public <V> ConcurrentHashMap<String, V> newConcurrentStringMap() { public <V> ConcurrentHashMap<String, V> newConcurrentStringMap() {
return dbSettings.databaseToUpper ? return new NullableKeyConcurrentMap<>(!dbSettings.databaseToUpper);
new NullableKeyConcurrentMap<V>() :
new CaseInsensitiveConcurrentMap<V>();
} }
/** /**
......
...@@ -924,7 +924,7 @@ public class Session extends SessionWithState implements TransactionStore.Rollba ...@@ -924,7 +924,7 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
if (!locks.contains(log.getTable()) if (!locks.contains(log.getTable())
&& TableType.TABLE_LINK != tableType && TableType.TABLE_LINK != tableType
&& TableType.EXTERNAL_TABLE_ENGINE != tableType) { && TableType.EXTERNAL_TABLE_ENGINE != tableType) {
DbException.throwInternalError("" + tableType); DbException.throwInternalError(String.valueOf(tableType));
} }
} }
} }
......
...@@ -51,7 +51,7 @@ public class PageBtreeIndex extends PageIndex { ...@@ -51,7 +51,7 @@ public class PageBtreeIndex extends PageIndex {
// trace.setLevel(TraceSystem.DEBUG); // trace.setLevel(TraceSystem.DEBUG);
tableData = table; tableData = table;
if (!database.isPersistent() || id < 0) { if (!database.isPersistent() || id < 0) {
throw DbException.throwInternalError("" + indexName); throw DbException.throwInternalError(indexName);
} }
this.store = database.getPageStore(); this.store = database.getPageStore();
store.addIndex(this); store.addIndex(this);
...@@ -154,7 +154,7 @@ public class PageBtreeIndex extends PageIndex { ...@@ -154,7 +154,7 @@ public class PageBtreeIndex extends PageIndex {
store.update(empty); store.update(empty);
return empty; return empty;
} else if (!(p instanceof PageBtree)) { } else if (!(p instanceof PageBtree)) {
throw DbException.get(ErrorCode.FILE_CORRUPTED_1, "" + p); throw DbException.get(ErrorCode.FILE_CORRUPTED_1, String.valueOf(p));
} }
return (PageBtree) p; return (PageBtree) p;
} }
......
...@@ -237,7 +237,7 @@ public class PageDataIndex extends PageIndex { ...@@ -237,7 +237,7 @@ public class PageDataIndex extends PageIndex {
store.update(empty); store.update(empty);
return empty; return empty;
} else if (!(pd instanceof PageData)) { } else if (!(pd instanceof PageData)) {
throw DbException.get(ErrorCode.FILE_CORRUPTED_1, "" + pd); throw DbException.get(ErrorCode.FILE_CORRUPTED_1, String.valueOf(pd));
} }
PageData p = (PageData) pd; PageData p = (PageData) pd;
if (parent != -1) { if (parent != -1) {
......
...@@ -153,7 +153,7 @@ public class PageDataLeaf extends PageData { ...@@ -153,7 +153,7 @@ public class PageDataLeaf extends PageData {
private int findInsertionPoint(long key) { private int findInsertionPoint(long key) {
int x = find(key); int x = find(key);
if (x < entryCount && keys[x] == key) { if (x < entryCount && keys[x] == key) {
throw index.getDuplicateKeyException(""+key); throw index.getDuplicateKeyException(String.valueOf(key));
} }
return x; return x;
} }
......
...@@ -32,7 +32,7 @@ public class PageDelegateIndex extends PageIndex { ...@@ -32,7 +32,7 @@ public class PageDelegateIndex extends PageIndex {
this.initBaseIndex(table, id, name, cols, indexType); this.initBaseIndex(table, id, name, cols, indexType);
this.mainIndex = mainIndex; this.mainIndex = mainIndex;
if (!database.isPersistent() || id < 0) { if (!database.isPersistent() || id < 0) {
throw DbException.throwInternalError("" + name); throw DbException.throwInternalError(name);
} }
PageStore store = database.getPageStore(); PageStore store = database.getPageStore();
store.addIndex(this); store.addIndex(this);
......
...@@ -1098,7 +1098,7 @@ public class JdbcConnection extends TraceObject ...@@ -1098,7 +1098,7 @@ public class JdbcConnection extends TraceObject
private static JdbcSavepoint convertSavepoint(Savepoint savepoint) { private static JdbcSavepoint convertSavepoint(Savepoint savepoint) {
if (!(savepoint instanceof JdbcSavepoint)) { if (!(savepoint instanceof JdbcSavepoint)) {
throw DbException.get(ErrorCode.SAVEPOINT_IS_INVALID_1, throw DbException.get(ErrorCode.SAVEPOINT_IS_INVALID_1,
"" + savepoint); String.valueOf(savepoint));
} }
return (JdbcSavepoint) savepoint; return (JdbcSavepoint) savepoint;
} }
......
...@@ -35,7 +35,7 @@ public class MVDelegateIndex extends BaseIndex implements MVIndex { ...@@ -35,7 +35,7 @@ public class MVDelegateIndex extends BaseIndex implements MVIndex {
this.initBaseIndex(table, id, name, cols, indexType); this.initBaseIndex(table, id, name, cols, indexType);
this.mainIndex = mainIndex; this.mainIndex = mainIndex;
if (id < 0) { if (id < 0) {
throw DbException.throwInternalError("" + name); throw DbException.throwInternalError(name);
} }
} }
......
...@@ -368,7 +368,7 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -368,7 +368,7 @@ public class MVPrimaryIndex extends BaseIndex {
/** /**
* A cursor. * A cursor.
*/ */
class MVStoreCursor implements Cursor { static class MVStoreCursor implements Cursor {
private final Session session; private final Session session;
private final Iterator<Entry<Value, Value>> it; private final Iterator<Entry<Value, Value>> it;
......
...@@ -120,7 +120,6 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex { ...@@ -120,7 +120,6 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex {
@Override @Override
public void addBufferedRows(List<String> bufferNames) { public void addBufferedRows(List<String> bufferNames) {
ArrayList<String> mapNames = new ArrayList<>(bufferNames);
CompareMode compareMode = database.getCompareMode(); CompareMode compareMode = database.getCompareMode();
int buffersCount = bufferNames.size(); int buffersCount = bufferNames.size();
Queue<Source> queue = new PriorityQueue<>(buffersCount, new Source.Comparator(compareMode)); Queue<Source> queue = new PriorityQueue<>(buffersCount, new Source.Comparator(compareMode));
...@@ -155,7 +154,7 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex { ...@@ -155,7 +154,7 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex {
} }
} }
} finally { } finally {
for (String tempMapName : mapNames) { for (String tempMapName : bufferNames) {
MVMap<ValueArray, Value> map = openMap(tempMapName); MVMap<ValueArray, Value> map = openMap(tempMapName);
map.getStore().removeMap(map); map.getStore().removeMap(map);
} }
......
...@@ -49,7 +49,6 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex { ...@@ -49,7 +49,6 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex {
*/ */
final MVTable mvTable; final MVTable mvTable;
private final String mapName;
private final TransactionMap<SpatialKey, Value> dataMap; private final TransactionMap<SpatialKey, Value> dataMap;
private final MVRTreeMap<VersionedValue> spatialMap; private final MVRTreeMap<VersionedValue> spatialMap;
...@@ -93,7 +92,7 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex { ...@@ -93,7 +92,7 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex {
if (!database.isStarting()) { if (!database.isStarting()) {
checkIndexColumnTypes(columns); checkIndexColumnTypes(columns);
} }
mapName = "index." + getId(); String mapName = "index." + getId();
ValueDataType vt = new ValueDataType(null, null, null); ValueDataType vt = new ValueDataType(null, null, null);
VersionedValue.Type valueType = new VersionedValue.Type(vt); VersionedValue.Type valueType = new VersionedValue.Type(vt);
MVRTreeMap.Builder<VersionedValue> mapBuilder = MVRTreeMap.Builder<VersionedValue> mapBuilder =
......
...@@ -672,10 +672,9 @@ public class Schema extends DbObjectBase { ...@@ -672,10 +672,9 @@ public class Schema extends DbObjectBase {
public void remove(SchemaObject obj) { public void remove(SchemaObject obj) {
String objName = obj.getName(); String objName = obj.getName();
Map<String, SchemaObject> map = getMap(obj.getType()); Map<String, SchemaObject> map = getMap(obj.getType());
if (SysProperties.CHECK && !map.containsKey(objName)) { if (map.remove(objName) == null) {
DbException.throwInternalError("not found: " + objName); DbException.throwInternalError("not found: " + objName);
} }
map.remove(objName);
freeUniqueName(objName); freeUniqueName(objName);
} }
......
...@@ -301,7 +301,7 @@ public class WebApp { ...@@ -301,7 +301,7 @@ public class WebApp {
for (Map.Entry<String, String> entry : map.entrySet()) { for (Map.Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey(); String key = entry.getKey();
String value = entry.getValue(); String value = entry.getValue();
String type = "" + key.charAt(0); String type = String.valueOf(key.charAt(0));
if (Integer.parseInt(type) > 2) { if (Integer.parseInt(type) > 2) {
continue; continue;
} }
...@@ -1182,14 +1182,14 @@ public class WebApp { ...@@ -1182,14 +1182,14 @@ public class WebApp {
rs.addRow("conn.getCatalog", conn.getCatalog()); rs.addRow("conn.getCatalog", conn.getCatalog());
rs.addRow("conn.getAutoCommit", Boolean.toString(conn.getAutoCommit())); rs.addRow("conn.getAutoCommit", Boolean.toString(conn.getAutoCommit()));
rs.addRow("conn.getTransactionIsolation", Integer.toString(conn.getTransactionIsolation())); rs.addRow("conn.getTransactionIsolation", Integer.toString(conn.getTransactionIsolation()));
rs.addRow("conn.getWarnings", "" + conn.getWarnings()); rs.addRow("conn.getWarnings", String.valueOf(conn.getWarnings()));
String map; String map;
try { try {
map = "" + conn.getTypeMap(); map = String.valueOf(conn.getTypeMap());
} catch (SQLException e) { } catch (SQLException e) {
map = e.toString(); map = e.toString();
} }
rs.addRow("conn.getTypeMap", "" + map); rs.addRow("conn.getTypeMap", map);
rs.addRow("conn.isReadOnly", Boolean.toString(conn.isReadOnly())); rs.addRow("conn.isReadOnly", Boolean.toString(conn.isReadOnly()));
rs.addRow("conn.getHoldability", Integer.toString(conn.getHoldability())); rs.addRow("conn.getHoldability", Integer.toString(conn.getHoldability()));
addDatabaseMetaData(rs, meta); addDatabaseMetaData(rs, meta);
...@@ -1229,7 +1229,7 @@ public class WebApp { ...@@ -1229,7 +1229,7 @@ public class WebApp {
if (m.getParameterTypes().length == 0) { if (m.getParameterTypes().length == 0) {
try { try {
Object o = m.invoke(meta); Object o = m.invoke(meta);
rs.addRow("meta." + m.getName(), "" + o); rs.addRow("meta." + m.getName(), String.valueOf(o));
} catch (InvocationTargetException e) { } catch (InvocationTargetException e) {
rs.addRow("meta." + m.getName(), e.getTargetException().toString()); rs.addRow("meta." + m.getName(), e.getTargetException().toString());
} catch (Exception e) { } catch (Exception e) {
......
...@@ -73,7 +73,7 @@ public class FileLock implements Runnable { ...@@ -73,7 +73,7 @@ public class FileLock implements Runnable {
*/ */
private long lastWrite; private long lastWrite;
private String method, ipAddress; private String method;
private Properties properties; private Properties properties;
private String uniqueId; private String uniqueId;
private Thread watchdog; private Thread watchdog;
...@@ -350,7 +350,7 @@ public class FileLock implements Runnable { ...@@ -350,7 +350,7 @@ public class FileLock implements Runnable {
setUniqueId(); setUniqueId();
// if this returns 127.0.0.1, // if this returns 127.0.0.1,
// the computer is probably not networked // the computer is probably not networked
ipAddress = NetUtils.getLocalAddress(); String ipAddress = NetUtils.getLocalAddress();
FileUtils.createDirectories(FileUtils.getParent(fileName)); FileUtils.createDirectories(FileUtils.getParent(fileName));
if (!FileUtils.createFile(fileName)) { if (!FileUtils.createFile(fileName)) {
waitUntilOld(); waitUntilOld();
......
...@@ -60,14 +60,6 @@ public class LobStorageMap implements LobStorageInterface { ...@@ -60,14 +60,6 @@ public class LobStorageMap implements LobStorageInterface {
*/ */
private MVMap<Object[], Boolean> refMap; private MVMap<Object[], Boolean> refMap;
/**
* The stream store data map.
*
* Key: stream store block id (long).
* Value: data (byte[]).
*/
private MVMap<Long, byte[]> dataMap;
private StreamStore streamStore; private StreamStore streamStore;
public LobStorageMap(Database database) { public LobStorageMap(Database database) {
...@@ -90,7 +82,13 @@ public class LobStorageMap implements LobStorageInterface { ...@@ -90,7 +82,13 @@ public class LobStorageMap implements LobStorageInterface {
} }
lobMap = mvStore.openMap("lobMap"); lobMap = mvStore.openMap("lobMap");
refMap = mvStore.openMap("lobRef"); refMap = mvStore.openMap("lobRef");
dataMap = mvStore.openMap("lobData");
/* The stream store data map.
*
* Key: stream store block id (long).
* Value: data (byte[]).
*/
MVMap<Long, byte[]> dataMap = mvStore.openMap("lobData");
streamStore = new StreamStore(dataMap); streamStore = new StreamStore(dataMap);
// garbage collection of the last blocks // garbage collection of the last blocks
if (database.isReadOnly()) { if (database.isReadOnly()) {
...@@ -279,8 +277,7 @@ public class LobStorageMap implements LobStorageInterface { ...@@ -279,8 +277,7 @@ public class LobStorageMap implements LobStorageInterface {
if (lob.getTableId() == LobStorageFrontend.TABLE_RESULT || if (lob.getTableId() == LobStorageFrontend.TABLE_RESULT ||
lob.getTableId() == LobStorageFrontend.TABLE_ID_SESSION_VARIABLE) { lob.getTableId() == LobStorageFrontend.TABLE_ID_SESSION_VARIABLE) {
throw DbException.get( throw DbException.get(
ErrorCode.LOB_CLOSED_ON_TIMEOUT_1, "" + ErrorCode.LOB_CLOSED_ON_TIMEOUT_1, lob.getLobId() + "/" + lob.getTableId());
lob.getLobId() + "/" + lob.getTableId());
} }
throw DbException.throwInternalError("Lob not found: " + throw DbException.throwInternalError("Lob not found: " +
lob.getLobId() + "/" + lob.getTableId()); lob.getLobId() + "/" + lob.getTableId());
......
...@@ -1764,11 +1764,11 @@ public class MetaTable extends Table { ...@@ -1764,11 +1764,11 @@ public class MetaTable extends Table {
// SELECTIVITY INT // SELECTIVITY INT
Integer.toString(col.getSelectivity()), Integer.toString(col.getSelectivity()),
// CHECK_CONSTRAINT // CHECK_CONSTRAINT
"" + col.getCheckConstraintSQL(session, "VALUE"), col.getCheckConstraintSQL(session, "VALUE"),
// REMARKS // REMARKS
replaceNullWithEmpty(dt.getComment()), replaceNullWithEmpty(dt.getComment()),
// SQL // SQL
"" + dt.getCreateSQL(), dt.getCreateSQL(),
// ID // ID
Integer.toString(dt.getId()) Integer.toString(dt.getId())
); );
......
...@@ -606,8 +606,7 @@ public class SimpleResultSet implements ResultSet, ResultSetMetaData, ...@@ -606,8 +606,7 @@ public class SimpleResultSet implements ResultSet, ResultSetMetaData,
*/ */
@Override @Override
public Clob getClob(int columnIndex) throws SQLException { public Clob getClob(int columnIndex) throws SQLException {
Clob c = (Clob) get(columnIndex); return (Clob) get(columnIndex);
return c == null ? null : c;
} }
/** /**
......
...@@ -18,14 +18,12 @@ public class ColumnNamer { ...@@ -18,14 +18,12 @@ public class ColumnNamer {
private static final String DEFAULT_COLUMN_NAME = "DEFAULT"; private static final String DEFAULT_COLUMN_NAME = "DEFAULT";
private final ColumnNamerConfiguration configuration; private final ColumnNamerConfiguration configuration;
private final Session session;
private final Set<String> existingColumnNames = new HashSet<>(); private final Set<String> existingColumnNames = new HashSet<>();
public ColumnNamer(Session session) { public ColumnNamer(Session session) {
this.session = session; if (session != null && session.getColumnNamerConfiguration() != null) {
if (this.session != null && this.session.getColumnNamerConfiguration() != null) {
// use original from session // use original from session
this.configuration = this.session.getColumnNamerConfiguration(); this.configuration = session.getColumnNamerConfiguration();
} else { } else {
// detached namer, create new // detached namer, create new
this.configuration = ColumnNamerConfiguration.getDefault(); this.configuration = ColumnNamerConfiguration.getDefault();
......
...@@ -32,13 +32,13 @@ public class ThreadDeadlockDetector { ...@@ -32,13 +32,13 @@ public class ThreadDeadlockDetector {
private final ThreadMXBean threadBean; private final ThreadMXBean threadBean;
// a daemon thread
private final Timer threadCheck = new Timer("ThreadDeadlockDetector", true);
private ThreadDeadlockDetector() { private ThreadDeadlockDetector() {
this.threadBean = ManagementFactory.getThreadMXBean(); this.threadBean = ManagementFactory.getThreadMXBean();
// a daemon thread
// delay: 10 ms // delay: 10 ms
// period: 10000 ms (100 seconds) // period: 10000 ms (100 seconds)
Timer threadCheck = new Timer("ThreadDeadlockDetector", true);
threadCheck.schedule(new TimerTask() { threadCheck.schedule(new TimerTask() {
@Override @Override
public void run() { public void run() {
......
/*
* 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.value;
import java.util.concurrent.ConcurrentHashMap;
import org.h2.util.StringUtils;
/**
* A concurrent hash map with a case-insensitive string key, that also allows
* NULL as a key.
*
* @param <V> the value type
*/
public class CaseInsensitiveConcurrentMap<V> extends ConcurrentHashMap<String, V> {
private static final long serialVersionUID = 1L;
private static final String NULL = new String(new byte[0]);
@Override
public V get(Object key) {
return super.get(toUpper(key));
}
@Override
public V put(String key, V value) {
return super.put(toUpper(key), value);
}
@Override
public boolean containsKey(Object key) {
return super.containsKey(toUpper(key));
}
@Override
public V remove(Object key) {
return super.remove(toUpper(key));
}
private static String toUpper(Object key) {
return key == null ? NULL : StringUtils.toUpperEnglish(key.toString());
}
}
...@@ -7,15 +7,29 @@ package org.h2.value; ...@@ -7,15 +7,29 @@ package org.h2.value;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import org.h2.util.StringUtils;
/** /**
* A concurrent hash map that allows null keys * A concurrent hash map with string keys that allows null keys.
* *
* @param <V> the value type * @param <V> the value type
*/ */
public class NullableKeyConcurrentMap<V> extends ConcurrentHashMap<String, V> { public class NullableKeyConcurrentMap<V> extends ConcurrentHashMap<String, V> {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final String NULL = new String(new byte[0]); private static final String NULL = new String();
private final boolean toUpper;
/**
* Create new instance of map.
*
* @param toUpper
* whether keys should be converted to upper case
*/
public NullableKeyConcurrentMap(boolean toUpper) {
this.toUpper = toUpper;
}
@Override @Override
public V get(Object key) { public V get(Object key) {
...@@ -37,8 +51,12 @@ public class NullableKeyConcurrentMap<V> extends ConcurrentHashMap<String, V> { ...@@ -37,8 +51,12 @@ public class NullableKeyConcurrentMap<V> extends ConcurrentHashMap<String, V> {
return super.remove(toUpper(key)); return super.remove(toUpper(key));
} }
private static String toUpper(Object key) { private String toUpper(Object key) {
return key == null ? NULL : key.toString(); if (key == null) {
return NULL;
}
String s = key.toString();
return toUpper ? StringUtils.toUpperEnglish(s) : s;
} }
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论