提交 e7d335f2 authored 作者: Owner's avatar Owner

Merge branch 'master' of https://github.com/h2database/h2database into Issue#645

......@@ -206,11 +206,13 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.10</version>
<version>3.0.0</version>
<executions>
<execution>
<phase>generate-test-sources</phase>
<goals><goal>add-test-source</goal></goals>
<goals>
<goal>add-test-source</goal>
</goals>
<configuration>
<sources>
<source>src/tools</source>
......@@ -219,53 +221,51 @@
</execution>
</executions>
</plugin>
<!-- Maven requires at least JRE 1.7 but we want to build with JDK 1.6 -->
<!-- Make sure neither we nor one of our dependencies uses anything outside JDK 1.7 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-toolchains-plugin</artifactId>
<version>1.1</version>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.0.0-M1</version>
<executions>
<execution>
<id>check-java-version</id>
<goals>
<goal>toolchain</goal>
</goals>
</execution>
</executions>
<configuration>
<toolchains>
<jdk>
<version>1.6</version>
</jdk>
</toolchains>
</configuration>
</plugin>
<!-- Make sure we are not using anything outside JDK 1.6 -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-maven-plugin</artifactId>
<version>1.15</version>
<executions>
<execution>
<id>check-java-api</id>
<phase>test</phase>
<goals>
<goal>check</goal>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<enforceBytecodeVersion>
<maxJdkVersion>1.7</maxJdkVersion>
</enforceBytecodeVersion>
<checkSignatureRule implementation="org.codehaus.mojo.animal_sniffer.enforcer.CheckSignatureRule">
<signature>
<groupId>org.codehaus.mojo.signature</groupId>
<artifactId>java17</artifactId>
<version>1.0</version>
</signature>
</checkSignatureRule>
</rules>
<fail>true</fail>
</configuration>
</execution>
</executions>
<configuration>
<signature>
<groupId>org.codehaus.mojo.signature</groupId>
<artifactId>java16</artifactId>
<version>1.1</version>
</signature>
</configuration>
<dependencies>
<dependency>
<groupId>org.codehaus.mojo</groupId>
<artifactId>extra-enforcer-rules</artifactId>
<version>1.0-beta-6</version>
</dependency>
<dependency>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-enforcer-rule</artifactId>
<version>1.16</version>
</dependency>
</dependencies>
</plugin>
<!-- Disable surefire since we don't use Junit -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version>
<version>2.20.1</version>
<configuration>
<includes>
<include>TestAllJunit.java</include>
......@@ -277,4 +277,4 @@
</build>
</project>
\ No newline at end of file
</project>
......@@ -216,7 +216,7 @@ The web site is currently translated using Google.
<p>
If you'd like to contribute bug fixes or new features, please consider the following guidelines to simplify merging them:
</p>
<ul><li>Only use Java 6 features (do not use Java 7) (see <a href="#environment">Environment</a>).
<ul><li>Only use Java 7 features (do not use Java 8/9/etc) (see <a href="#environment">Environment</a>).
</li><li>Follow the coding style used in the project, and use Checkstyle (see above) to verify.
For example, do not use tabs (use spaces instead).
The checkstyle configuration is in <code>src/installer/checkstyle.xml</code>.
......
......@@ -21,6 +21,16 @@ Change Log
<h2>Next Version (unreleased)</h2>
<ul>
<li>Issue #654: List ENUM type values in INFORMATION_SCHEMA.COLUMNS
</li>
<li>Issue #668: Fail of an update command on large table with ENUM column
</li>
<li>Issue #662: column called CONSTRAINT is not properly escaped when storing to metadata
</li>
<li>Issue #660: Outdated java version mentioned on http://h2database.com/html/build.html#providing_patches
</li>
<li>Issue #643: H2 doesn't use index when I use IN and EQUAL in one query
</li>
<li>Reset transaction start timestamp on ROLLBACK
</li>
<li>Issue #632: CREATE OR REPLACE VIEW creates incorrect columns names
......@@ -123,7 +133,7 @@ Change Log
</li>
<li>Added support for invisible columns.
</li>
<li>Added an ENUM data type, with syntax similar to that of MySQL and Oracle.
<li>Added an ENUM data type, with syntax similar to that of MySQL.
</li>
<li>MVStore: for object data types, the cache size memory estimation
was sometimes far off in a read-only scenario.
......
h2/src/docsrc/images/favicon.ico

5.2 KB | W: | H:

h2/src/docsrc/images/favicon.ico

4.2 KB | W: | H:

h2/src/docsrc/images/favicon.ico
h2/src/docsrc/images/favicon.ico
h2/src/docsrc/images/favicon.ico
h2/src/docsrc/images/favicon.ico
  • 2-up
  • Swipe
  • Onion skin
h2/src/docsrc/images/h2-16.png

597 Bytes | W: | H:

h2/src/docsrc/images/h2-16.png

675 Bytes | W: | H:

h2/src/docsrc/images/h2-16.png
h2/src/docsrc/images/h2-16.png
h2/src/docsrc/images/h2-16.png
h2/src/docsrc/images/h2-16.png
  • 2-up
  • Swipe
  • Onion skin
h2/src/docsrc/images/h2-24.png

934 Bytes | W: | H:

h2/src/docsrc/images/h2-24.png

995 Bytes | W: | H:

h2/src/docsrc/images/h2-24.png
h2/src/docsrc/images/h2-24.png
h2/src/docsrc/images/h2-24.png
h2/src/docsrc/images/h2-24.png
  • 2-up
  • Swipe
  • Onion skin
h2/src/docsrc/images/h2-32.png

1.1 KB | W: | H:

h2/src/docsrc/images/h2-32.png

1.3 KB | W: | H:

h2/src/docsrc/images/h2-32.png
h2/src/docsrc/images/h2-32.png
h2/src/docsrc/images/h2-32.png
h2/src/docsrc/images/h2-32.png
  • 2-up
  • Swipe
  • Onion skin
h2/src/docsrc/images/h2-64.png

1.9 KB | W: | H:

h2/src/docsrc/images/h2-64.png

2.7 KB | W: | H:

h2/src/docsrc/images/h2-64.png
h2/src/docsrc/images/h2-64.png
h2/src/docsrc/images/h2-64.png
h2/src/docsrc/images/h2-64.png
  • 2-up
  • Swipe
  • Onion skin
h2/src/docsrc/images/h2-logo.png

10.4 KB | W: | H:

h2/src/docsrc/images/h2-logo.png

12.5 KB | W: | H:

h2/src/docsrc/images/h2-logo.png
h2/src/docsrc/images/h2-logo.png
h2/src/docsrc/images/h2-logo.png
h2/src/docsrc/images/h2-logo.png
  • 2-up
  • Swipe
  • Onion skin
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg8552"
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
sodipodi:docname="h2_v2_3_7.svg">
<defs
id="defs8546" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.98994949"
inkscape:cx="126.57488"
inkscape:cy="654.06648"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="3840"
inkscape:window-height="2115"
inkscape:window-x="-13"
inkscape:window-y="-13"
inkscape:window-maximized="1" />
<metadata
id="metadata8549">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
d="m 118.88848,142.31555 c 0.36476,-0.0389 0.72463,-0.0584 1.07965,-0.0584 1.78761,0 3.15733,0.40328 4.10924,1.20993 0.95192,0.80656 1.42793,1.95109 1.42793,3.43314 0,1.23601 -0.37064,2.48554 -1.1118,3.74954 -0.74123,1.26488 -1.99106,2.74719 -3.74961,4.44784 -1.17719,1.14758 -2.74314,2.51353 -4.69785,4.09776 -1.9547,1.58426 -3.90579,3.08101 -5.85327,4.49115 v 5.36266 h 25.48412 v -6.21254 h -14.58422 c 0.56686,-0.40687 1.51876,-1.12322 2.85581,-2.14722 1.33708,-1.02489 2.66691,-2.14813 3.98944,-3.36881 2.10727,-1.96138 3.6732,-3.86592 4.69783,-5.71091 1.02463,-1.84589 1.53689,-3.79375 1.53689,-5.84273 0,-3.12466 -1.0682,-5.54084 -3.2046,-7.24852 -2.13631,-1.70759 -5.23924,-2.56143 -9.30851,-2.56143 -0.89111,0 -1.7814,0.0438 -2.67105,0.13154 v -12.40108 h -8.53425 v 12.11228 H 97.752873 v -12.11228 h -8.533889 v 33.09188 h 8.533889 v -14.57898 h 12.601357 v 14.57896 h 0.0967 c 0.79988,-0.6198 1.60032,-1.25405 2.40138,-1.90362 1.95471,-1.58336 3.52073,-2.95019 4.69792,-4.09779 0.48476,-0.46914 0.9309,-0.92114 1.33832,-1.35691 z m -13.28694,25.83146 c -16.060472,0 -29.133317,-13.07285 -29.133317,-29.13352 0,-16.04854 13.060215,-29.07304 29.133317,-29.07304 16.06065,0 29.07314,13.01241 29.07314,29.07304 v 0.90222 h 1.8044 v -0.90222 c 0,-17.0572 -13.82032,-30.87744 -30.87754,-30.87744 -17.068227,0 -30.937713,13.83125 -30.937713,30.87744 0,17.0576 13.880313,30.93792 30.937713,30.93792 h 0.90219 v -1.8044 z"
id="path6394"
inkscape:connector-curvature="0"
style="fill:#09476b;fill-opacity:1;stroke-width:0.90219772" />
</g>
</svg>
h2/src/installer/favicon.ico

5.2 KB | W: | H:

h2/src/installer/favicon.ico

4.2 KB | W: | H:

h2/src/installer/favicon.ico
h2/src/installer/favicon.ico
h2/src/installer/favicon.ico
h2/src/installer/favicon.ico
  • 2-up
  • Swipe
  • Onion skin
......@@ -212,7 +212,7 @@ public class AlterTableAddConstraint extends SchemaCommand {
isOwner = true;
index.getIndexType().setBelongsToConstraint(true);
} else {
index = getIndex(table, indexColumns, true);
index = getIndex(table, indexColumns, false);
if (index == null) {
index = createIndex(table, indexColumns, false);
isOwner = true;
......@@ -244,20 +244,20 @@ public class AlterTableAddConstraint extends SchemaCommand {
}
int id = getObjectId();
String name = generateConstraintName(table);
ConstraintReferential ref = new ConstraintReferential(getSchema(),
ConstraintReferential refConstraint = new ConstraintReferential(getSchema(),
id, name, table);
ref.setColumns(indexColumns);
ref.setIndex(index, isOwner);
ref.setRefTable(refTable);
ref.setRefColumns(refIndexColumns);
ref.setRefIndex(refIndex, isRefOwner);
refConstraint.setColumns(indexColumns);
refConstraint.setIndex(index, isOwner);
refConstraint.setRefTable(refTable);
refConstraint.setRefColumns(refIndexColumns);
refConstraint.setRefIndex(refIndex, isRefOwner);
if (checkExisting) {
ref.checkExistingData(session);
refConstraint.checkExistingData(session);
}
constraint = ref;
refTable.addConstraint(constraint);
ref.setDeleteAction(deleteAction);
ref.setUpdateAction(updateAction);
refTable.addConstraint(refConstraint);
refConstraint.setDeleteAction(deleteAction);
refConstraint.setUpdateAction(updateAction);
constraint = refConstraint;
break;
}
default:
......@@ -329,29 +329,30 @@ public class AlterTableAddConstraint extends SchemaCommand {
}
return null;
}
// all cols must be in the index key, the order doesn't matter and there must be no other fields in the index key
private static boolean canUseUniqueIndex(Index idx, Table table,
IndexColumn[] cols) {
IndexColumn[] cols) {
if (idx.getTable() != table || !idx.getIndexType().isUnique()) {
return false;
}
Column[] indexCols = idx.getColumns();
if (indexCols.length > cols.length) {
return false;
HashSet<Column> indexColsSet = New.hashSet();
for (Column c : indexCols) {
indexColsSet.add(c);
}
HashSet<Column> set = New.hashSet();
HashSet<Column> colsSet = New.hashSet();
for (IndexColumn c : cols) {
set.add(c.column);
colsSet.add(c.column);
}
for (Column c : indexCols) {
// all columns of the index must be part of the list,
// but not all columns of the list need to be part of the index
if (!set.contains(c)) {
return false;
}
}
return true;
}
return colsSet.equals(indexColsSet);
}
private static boolean canUseIndex(Index existingIndex, Table table,
IndexColumn[] cols, boolean moreColumnsOk) {
......
......@@ -165,7 +165,7 @@ public class Database implements DataHandler {
private boolean referentialIntegrity = true;
private boolean multiVersion;
private DatabaseCloser closeOnExit;
private Mode mode = Mode.getInstance(Mode.REGULAR);
private Mode mode = Mode.getRegular();
private boolean multiThreaded;
private int maxOperationMemory =
Constants.DEFAULT_MAX_OPERATION_MEMORY;
......@@ -902,6 +902,9 @@ public class Database implements DataHandler {
}
}
private static final ThreadLocal<Session> metaLockDebugging = new ThreadLocal<Session>();
private static final ThreadLocal<Throwable> metaLockDebuggingStack = new ThreadLocal<Throwable>();
/**
* Lock the metadata table for updates.
*
......@@ -916,6 +919,19 @@ public class Database implements DataHandler {
if (meta == null) {
return true;
}
if (SysProperties.CHECK2) {
final Session prev = metaLockDebugging.get();
if (prev == null) {
metaLockDebugging.set(session);
metaLockDebuggingStack.set(new Throwable());
} else if (prev != session) {
metaLockDebuggingStack.get().printStackTrace();
throw new IllegalStateException("meta currently locked by "
+ prev
+ " and trying to be locked by different session, "
+ session + " on same thread");
}
}
boolean wasLocked = meta.lock(session, true, true);
return wasLocked;
}
......@@ -947,10 +963,20 @@ public class Database implements DataHandler {
* @param session the session
*/
public void unlockMeta(Session session) {
unlockMetaDebug(session);
meta.unlock(session);
session.unlock(meta);
}
public void unlockMetaDebug(Session session) {
if (SysProperties.CHECK2) {
if (metaLockDebugging.get() == session) {
metaLockDebugging.set(null);
metaLockDebuggingStack.set(null);
}
}
}
/**
* Remove the given object from the meta data.
*
......@@ -980,6 +1006,7 @@ public class Database implements DataHandler {
checkMetaFree(session, id);
}
} else if (!wasLocked) {
unlockMetaDebug(session);
// must not keep the lock if it was not locked
// otherwise updating sequences may cause a deadlock
meta.unlock(session);
......@@ -1392,6 +1419,7 @@ public class Database implements DataHandler {
if (!readOnly) {
lockMeta(pageStore.getPageStoreSession());
pageStore.compact(compactMode);
unlockMeta(pageStore.getPageStoreSession());
}
} catch (DbException e) {
if (SysProperties.CHECK2) {
......
......@@ -17,7 +17,7 @@ import org.h2.util.StringUtils;
* PostgreSQL, MySQL). Each mode has different settings.
*/
public class Mode {
public enum ModeEnum {
REGULAR, DB2, Derby, MSSQLServer, HSQLDB, MySQL, Oracle, PostgreSQL, Ignite,
}
......@@ -325,6 +325,10 @@ public class Mode {
return getInstance(ModeEnum.Oracle.name());
}
public static Mode getRegular() {
return getInstance(ModeEnum.REGULAR.name());
}
public String getName() {
return name;
}
......
......@@ -857,6 +857,10 @@ public class Session extends SessionWithState {
removeTemporaryLobs(false);
cleanTempTables(true);
undoLog.clear();
// Table#removeChildrenAndResources can take the meta lock,
// and we need to unlock before we call removeSession(), which might
// want to take the meta lock using the system session.
database.unlockMeta(this);
database.removeSession(this);
} finally {
closed = true;
......@@ -965,6 +969,7 @@ public class Session extends SessionWithState {
}
locks.clear();
}
database.unlockMetaDebug(this);
savepoints = null;
sessionStateChanged = true;
}
......@@ -991,11 +996,6 @@ public class Session extends SessionWithState {
table.truncate(this);
}
}
// sometimes Table#removeChildrenAndResources
// will take the meta lock
if (closeSession) {
database.unlockMeta(this);
}
}
}
}
......
......@@ -338,8 +338,15 @@ public class SysProperties {
* The maximum number of objects in the cache.
* This value must be a power of 2.
*/
public static final int OBJECT_CACHE_SIZE =
MathUtils.nextPowerOf2(Utils.getProperty("h2.objectCacheSize", 1024));
public static final int OBJECT_CACHE_SIZE;
static {
try {
OBJECT_CACHE_SIZE = MathUtils.nextPowerOf2(
Utils.getProperty("h2.objectCacheSize", 1024));
} catch (IllegalArgumentException e) {
throw new IllegalStateException("Invalid h2.objectCacheSize", e);
}
}
/**
* System property <code>h2.oldStyleOuterJoin</code>
......
......@@ -21,7 +21,6 @@ import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.util.MathUtils;
import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils;
import org.h2.value.Value;
......@@ -348,7 +347,7 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
if (isMultiVersion) {
int v1 = rowData.getVersion();
int v2 = compare.getVersion();
return MathUtils.compareInt(v2, v1);
return Integer.compare(v2, v1);
}
return 0;
}
......
......@@ -131,9 +131,9 @@ public class IndexCursor implements Cursor {
if (isIntersects) {
intersects = getSpatialSearchRow(intersects, columnId, v);
}
if (isStart || isEnd) {
// an X=? condition will produce less rows than
// an X IN(..) condition
// An X=? condition will produce less rows than
// an X IN(..) condition, unless the X IN condition can use the index.
if ((isStart || isEnd) && !canUseIndexFor(inColumn)) {
inColumn = null;
inList = null;
inResult = null;
......@@ -176,6 +176,10 @@ public class IndexCursor implements Cursor {
// only one IN(..) condition can be used at the same time
return false;
}
return canUseIndexFor(column);
}
private boolean canUseIndexFor(Column column) {
// The first column of the index must match this column,
// or it must be a VIEW index (where the column is null).
// Multiple IN conditions with views are not supported, see
......
......@@ -10,7 +10,6 @@ import org.h2.engine.SysProperties;
import org.h2.message.DbException;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.util.MathUtils;
/**
* The cursor implementation for the multi-version index.
......@@ -143,7 +142,7 @@ public class MultiVersionCursor implements Cursor {
// version would be compared as well
long k1 = deltaRow.getKey();
long k2 = baseRow.getKey();
compare = MathUtils.compareLong(k1, k2);
compare = Long.compare(k1, k2);
}
if (compare == 0) {
if (isDeleted) {
......
......@@ -30,9 +30,11 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.Executor;
import java.util.regex.Pattern;
import org.h2.api.ErrorCode;
import org.h2.command.CommandInterface;
import org.h2.engine.ConnectionInfo;
......@@ -1737,6 +1739,12 @@ public class JdbcConnection extends TraceObject
+ ");");
}
checkClosed();
// no change to property: Ignore call. This early exit fixes a problem with websphere liberty
// resetting the client info of a pooled connection to its initial values.
if (Objects.equals(value, getClientInfo(name))) {
return;
}
if (isInternalProperty(name)) {
throw new SQLClientInfoException(
......
......@@ -197,7 +197,7 @@ public class MVPrimaryIndex extends BaseIndex {
}
}
TransactionMap<Value, Value> map = getMap(session);
return new MVStoreCursor(session, map.entryIterator(min), max);
return new MVStoreCursor(session, map.entryIterator(min, max));
}
@Override
......@@ -270,13 +270,13 @@ public class MVPrimaryIndex extends BaseIndex {
TransactionMap<Value, Value> map = getMap(session);
ValueLong v = (ValueLong) (first ? map.firstKey() : map.lastKey());
if (v == null) {
return new MVStoreCursor(session, Collections
.<Entry<Value, Value>> emptyList().iterator(), null);
return new MVStoreCursor(session,
Collections.<Entry<Value, Value>> emptyList().iterator());
}
Value value = map.get(v);
Entry<Value, Value> e = new DataUtils.MapEntry<Value, Value>(v, value);
List<Entry<Value, Value>> list = Arrays.asList(e);
MVStoreCursor c = new MVStoreCursor(session, list.iterator(), v);
List<Entry<Value, Value>> list = Collections.singletonList(e);
MVStoreCursor c = new MVStoreCursor(session, list.iterator());
c.next();
return c;
}
......@@ -356,7 +356,7 @@ public class MVPrimaryIndex extends BaseIndex {
*/
Cursor find(Session session, ValueLong first, ValueLong last) {
TransactionMap<Value, Value> map = getMap(session);
return new MVStoreCursor(session, map.entryIterator(first), last);
return new MVStoreCursor(session, map.entryIterator(first, last));
}
@Override
......@@ -385,14 +385,12 @@ public class MVPrimaryIndex extends BaseIndex {
private final Session session;
private final Iterator<Entry<Value, Value>> it;
private final ValueLong last;
private Entry<Value, Value> current;
private Row row;
public MVStoreCursor(Session session, Iterator<Entry<Value, Value>> it, ValueLong last) {
public MVStoreCursor(Session session, Iterator<Entry<Value, Value>> it) {
this.session = session;
this.it = it;
this.last = last;
}
@Override
......@@ -415,9 +413,6 @@ public class MVPrimaryIndex extends BaseIndex {
@Override
public boolean next() {
current = it.hasNext() ? it.next() : null;
if (current != null && current.getKey().getLong() > last.getLong()) {
current = null;
}
row = null;
return current != null;
}
......
......@@ -1470,7 +1470,7 @@ public class TransactionStore {
* @param from the first key to return
* @return the iterator
*/
public Iterator<Entry<K, V>> entryIterator(final K from) {
public Iterator<Entry<K, V>> entryIterator(final K from, final K to) {
return new Iterator<Entry<K, V>>() {
private Entry<K, V> current;
private K currentKey = from;
......@@ -1507,6 +1507,9 @@ public class TransactionStore {
}
}
final K key = k;
if (to != null && map.getKeyType().compare(k, to) > 0) {
break;
}
VersionedValue data = cursor.getValue();
data = getValue(key, readLogId, data);
if (data != null && data.value != null) {
......
......@@ -5,7 +5,6 @@
*/
package org.h2.server.web;
import org.h2.util.MathUtils;
import org.h2.util.StringUtils;
/**
......@@ -61,7 +60,7 @@ public class ConnectionInfo implements Comparable<ConnectionInfo> {
@Override
public int compareTo(ConnectionInfo o) {
return -MathUtils.compareInt(lastAccess, o.lastAccess);
return -Integer.compare(lastAccess, o.lastAccess);
}
}
......@@ -438,6 +438,7 @@ public class Data {
writeByte((byte) type);
writeShortInt(v.getShort());
break;
case Value.ENUM:
case Value.INT: {
int x = v.getInt();
if (x < 0) {
......@@ -728,6 +729,7 @@ public class Data {
return ValueBoolean.get(false);
case INT_NEG:
return ValueInt.get(-readVarInt());
case Value.ENUM:
case Value.INT:
return ValueInt.get(readVarInt());
case LONG_NEG:
......@@ -933,6 +935,7 @@ public class Data {
return 2;
case Value.SHORT:
return 3;
case Value.ENUM:
case Value.INT: {
int x = v.getInt();
if (x < 0) {
......
......@@ -471,9 +471,17 @@ public class Column {
}
}
public String getCreateSQLWithoutName() {
return getCreateSQL(false);
}
public String getCreateSQL() {
return getCreateSQL(true);
}
private String getCreateSQL(boolean includeName) {
StringBuilder buff = new StringBuilder();
if (name != null) {
if (includeName && name != null) {
buff.append(Parser.quoteIdentifier(name)).append(' ');
}
if (originalSQL != null) {
......
......@@ -174,7 +174,8 @@ public class MetaTable extends Table {
"CHECK_CONSTRAINT",
"SEQUENCE_NAME",
"REMARKS",
"SOURCE_DATA_TYPE SMALLINT"
"SOURCE_DATA_TYPE SMALLINT",
"COLUMN_TYPE"
);
indexColumnName = "TABLE_NAME";
break;
......@@ -828,7 +829,9 @@ public class MetaTable extends Table {
// REMARKS
replaceNullWithEmpty(c.getComment()),
// SOURCE_DATA_TYPE
null
null,
// COLUMN_TYPE
c.getCreateSQLWithoutName()
);
}
}
......
......@@ -16,7 +16,6 @@ import java.util.StringTokenizer;
import org.h2.message.DbException;
import org.h2.store.fs.FileUtils;
import org.h2.util.IOUtils;
import org.h2.util.MathUtils;
import org.h2.util.New;
import org.h2.util.StringUtils;
import org.h2.util.Tool;
......@@ -46,9 +45,9 @@ public class ConvertTraceFile extends Tool {
if (other == this) {
return 0;
}
int c = MathUtils.compareLong(other.time, time);
int c = Long.compare(other.time, time);
if (c == 0) {
c = MathUtils.compareInt(other.executeCount, executeCount);
c = Integer.compare(other.executeCount, executeCount);
if (c == 0) {
c = sql.compareTo(other.sql);
}
......
......@@ -13,14 +13,15 @@ import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.h2.api.ErrorCode;
import org.h2.engine.Constants;
import org.h2.util.IOUtils;
import org.h2.util.JdbcUtils;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.h2.util.Tool;
/**
* Creates a cluster from a standalone database.
* Creates a cluster from a stand-alone database.
* <br />
* Copies a database to another location if required.
* @h2.resource
......@@ -100,116 +101,98 @@ public class CreateCluster extends Tool {
private static void process(String urlSource, String urlTarget,
String user, String password, String serverList) throws SQLException {
Connection connSource = null, connTarget = null;
Statement statSource = null, statTarget = null;
PipedReader pipeReader = null;
try {
org.h2.Driver.load();
// verify that the database doesn't exist,
// or if it exists (an old cluster instance), it is deleted
boolean exists = true;
try {
connTarget = DriverManager.getConnection(urlTarget +
";IFEXISTS=TRUE;CLUSTER=" + Constants.CLUSTERING_ENABLED,
user, password);
Statement stat = connTarget.createStatement();
stat.execute("DROP ALL OBJECTS DELETE FILES");
stat.close();
exists = false;
connTarget.close();
} catch (SQLException e) {
if (e.getErrorCode() == ErrorCode.DATABASE_NOT_FOUND_1) {
// database does not exists yet - ok
exists = false;
} else {
throw e;
}
}
if (exists) {
throw new SQLException(
"Target database must not yet exist. Please delete it first: " +
urlTarget);
}
// use cluster='' so connecting is possible
// even if the cluster is enabled
connSource = DriverManager.getConnection(urlSource +
";CLUSTER=''", user, password);
statSource = connSource.createStatement();
org.h2.Driver.load();
try (Connection connSource = DriverManager.getConnection(
// use cluster='' so connecting is possible
// even if the cluster is enabled
urlSource + ";CLUSTER=''", user, password);
Statement statSource = connSource.createStatement())
{
// enable the exclusive mode and close other connections,
// so that data can't change while restoring the second database
statSource.execute("SET EXCLUSIVE 2");
pipeReader = new PipedReader();
try {
/*
* Pipe writer is used + closed in the inner class, in a
* separate thread (needs to be final). It should be initialized
* within try{} so an exception could be caught if creation
* fails. In that scenario, the the writer should be null and
* needs no closing, and the main goal is that finally{} should
* bring the source DB out of exclusive mode, and close the
* reader.
*/
final PipedWriter pipeWriter = new PipedWriter(pipeReader);
// Backup data from source database in script form.
// Start writing to pipe writer in separate thread.
final ResultSet rs = statSource.executeQuery("SCRIPT");
// Delete the target database first.
connTarget = DriverManager.getConnection(
urlTarget + ";CLUSTER=''", user, password);
statTarget = connTarget.createStatement();
statTarget.execute("DROP ALL OBJECTS DELETE FILES");
connTarget.close();
new Thread(
new Runnable(){
@Override
public void run() {
try {
while (rs.next()) {
pipeWriter.write(rs.getString(1) + "\n");
}
} catch (SQLException ex) {
throw new IllegalStateException("Producing script from the source DB is failing.",ex);
} catch (IOException ex) {
throw new IllegalStateException("Producing script from the source DB is failing.",ex);
} finally {
IOUtils.closeSilently(pipeWriter);
}
}
}
).start();
performTransfer(statSource, urlTarget, user, password, serverList);
} finally {
// switch back to the regular mode
statSource.execute("SET EXCLUSIVE FALSE");
}
}
}
// Read data from pipe reader, restore on target.
connTarget = DriverManager.getConnection(urlTarget, user, password);
RunScript.execute(connTarget,pipeReader);
statTarget = connTarget.createStatement();
private static void performTransfer(Statement statSource, String urlTarget,
String user, String password, String serverList) throws SQLException {
// Delete the target database first.
try (Connection connTarget = DriverManager.getConnection(
urlTarget + ";CLUSTER=''", user, password);
Statement statTarget = connTarget.createStatement())
{
statTarget.execute("DROP ALL OBJECTS DELETE FILES");
}
try (PipedReader pipeReader = new PipedReader()) {
Future<?> threadFuture = startWriter(pipeReader, statSource);
// Read data from pipe reader, restore on target.
try (Connection connTarget = DriverManager.getConnection(
urlTarget, user, password);
Statement statTarget = connTarget.createStatement())
{
RunScript.execute(connTarget, pipeReader);
// Check if the writer encountered any exception
try {
threadFuture.get();
} catch (ExecutionException ex) {
throw new SQLException(ex.getCause());
} catch (InterruptedException ex) {
throw new SQLException(ex);
}
// set the cluster to the serverList on both databases
statSource.executeUpdate("SET CLUSTER '" + serverList + "'");
statTarget.executeUpdate("SET CLUSTER '" + serverList + "'");
} catch (IOException ex) {
throw new SQLException(ex);
} finally {
// switch back to the regular mode
statSource.execute("SET EXCLUSIVE FALSE");
}
} finally {
IOUtils.closeSilently(pipeReader);
JdbcUtils.closeSilently(statSource);
JdbcUtils.closeSilently(statTarget);
JdbcUtils.closeSilently(connSource);
JdbcUtils.closeSilently(connTarget);
} catch (IOException ex) {
throw new SQLException(ex);
}
}
private static Future<?> startWriter(final PipedReader pipeReader,
final Statement statSource) throws SQLException, IOException {
final ExecutorService thread = Executors.newFixedThreadPool(1);
// Since exceptions cannot be thrown across thread boundaries, return
// the task's future so we can check manually
Future<?> threadFuture = thread.submit(new Runnable() {
@Override
public void run() {
/*
* If the creation of the piped writer fails, the reader will
* throw an IOException as soon as read() is called:
* IOException - if the pipe is broken, unconnected, closed,
* or an I/O error occurs.
* The reader's IOException will then trigger the finally{} that
* releases exclusive mode on the source DB.
*/
try (final PipedWriter pipeWriter = new PipedWriter(pipeReader);
final ResultSet rs = statSource.executeQuery("SCRIPT"))
{
while (rs.next()) {
pipeWriter.write(rs.getString(1) + "\n");
}
} catch (SQLException | IOException ex) {
throw new IllegalStateException("Producing script from the source DB is failing.", ex);
}
}
});
thread.shutdown();
return threadFuture;
}
}
......@@ -51,11 +51,17 @@ public class CacheLRU implements Cache {
this.writer = writer;
this.fifo = fifo;
this.setMaxMemory(maxMemoryKb);
long tmpLen = MathUtils.nextPowerOf2(maxMemory / 64);
if (tmpLen > Integer.MAX_VALUE) {
throw new IllegalStateException("do not support this much cache memory: " + maxMemoryKb + "kb");
try {
// Since setMaxMemory() ensures that maxMemory is >=0,
// we don't have to worry about an underflow.
long tmpLen = maxMemory / 64;
if (tmpLen > Integer.MAX_VALUE) {
throw new IllegalArgumentException();
}
this.len = MathUtils.nextPowerOf2((int) tmpLen);
} catch (IllegalArgumentException e) {
throw new IllegalStateException("This much cache memory is not supported: " + maxMemoryKb + "kb", e);
}
this.len = (int) tmpLen;
this.mask = len - 1;
clear();
}
......
......@@ -77,7 +77,7 @@ public abstract class CacheObject implements Comparable<CacheObject> {
@Override
public int compareTo(CacheObject other) {
return MathUtils.compareInt(getPos(), other.getPos());
return Integer.compare(getPos(), other.getPos());
}
public boolean isStream() {
......
......@@ -8,12 +8,12 @@ import org.h2.expression.Expression;
public class ColumnNamer {
private static final String DEFAULT_COLUMN_NAME = "DEFAULT";
private ColumnNamerConfiguration configuration;
private Session session;
private Set<String> existingColumnNames = new HashSet<String>();
private final Set<String> existingColumnNames = new HashSet<>();
public ColumnNamer(Session session) {
this.session = session;
......@@ -31,9 +31,8 @@ public class ColumnNamer {
}
/**
* Create a standardized column name that isn't null and doesn't have a CR/LF in it.
* @param columnExp the column expression
* @param expr the column expression
* @param indexOfColumn index of column in below array
* @return
*/
public String getColumnName(Expression expr, int indexOfColumn) {
return getColumnName(expr,indexOfColumn,(String) null);
......@@ -49,10 +48,10 @@ public class ColumnNamer {
String columnNameOverride = null;
if (columnNameOverides != null && columnNameOverides.length > indexOfColumn){
columnNameOverride = columnNameOverides[indexOfColumn];
}
}
return getColumnName(columnExp, indexOfColumn, columnNameOverride);
}
}
/**
* Create a standardized column name that isn't null and doesn't have a CR/LF in it.
* @param columnExp the column expression
......@@ -67,11 +66,11 @@ public class ColumnNamer {
columnName = columnNameOverride;
if(!isAllowableColumnName(columnName)){
columnName = fixColumnName(columnName);
}
}
if(!isAllowableColumnName(columnName)){
columnName = null;
}
}
}
// try a name from the column alias
if (columnName==null && columnExp.getAlias()!=null && !DEFAULT_COLUMN_NAME.equals(columnExp.getAlias())){
columnName = columnExp.getAlias();
......@@ -112,22 +111,22 @@ public class ColumnNamer {
existingColumnNames.add(columnName);
return columnName;
}
private String generateUniqueName(String columnName) {
String newColumnName = columnName;
int loopCount = 2;
while(existingColumnNames.contains(newColumnName)){
String loopCountString = "_"+loopCount;
newColumnName = columnName.substring(0,Math.min(columnName.length(), configuration.getMaxIdentiferLength()-loopCountString.length()))+loopCountString;
loopCount++;
}
return newColumnName;
}
public boolean isAllowableColumnName(String proposedName){
// check null
if (proposedName == null){
return false;
......@@ -142,21 +141,21 @@ public class ColumnNamer {
}
return true;
}
private String fixColumnName(String proposedName) {
Matcher match = configuration.getCompiledRegularExpressionMatchDisallowed().matcher(proposedName);
proposedName = match.replaceAll("");
// check size limits - then truncate
if (proposedName.length() > configuration.getMaxIdentiferLength()){
proposedName=proposedName.substring(0, configuration.getMaxIdentiferLength());
}
return proposedName;
}
public ColumnNamerConfiguration getConfiguration() {
return configuration;
}
}
......@@ -204,7 +204,7 @@ public class MathUtils {
* @param s the message to print
* @param t the stack trace
*/
static void warn(String s, Throwable t) {
private static void warn(String s, Throwable t) {
// not a fatal problem, but maybe reduced security
System.out.println("Warning: " + s);
if (t != null) {
......@@ -213,33 +213,27 @@ public class MathUtils {
}
/**
* Get the value that is equal or higher than this value, and that is a
* Get the value that is equal to or higher than this value, and that is a
* power of two.
*
* @param x the original value
* @return the next power of two value
* @throws IllegalArgumentException if x < 0 or x > 0x40000000
*/
public static int nextPowerOf2(int x) {
long i = 1;
while (i < x && i < (Integer.MAX_VALUE / 2)) {
i += i;
public static int nextPowerOf2(int x) throws IllegalArgumentException {
if (x == 0) {
return 1;
} else if (x < 0 || x > 0x40000000 ) {
throw new IllegalArgumentException("Argument out of range"
+ " [0x0-0x40000000]. Argument was: " + x);
}
return (int) i;
}
/**
* Get the value that is equal or higher than this value, and that is a
* power of two.
*
* @param x the original value
* @return the next power of two value
*/
public static long nextPowerOf2(long x) {
long i = 1;
while (i < x && i < (Long.MAX_VALUE / 2)) {
i += i;
}
return i;
x--;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
return ++x;
}
/**
......@@ -260,30 +254,6 @@ public class MathUtils {
}
}
/**
* Compare two values. Returns -1 if the first value is smaller, 1 if
* bigger, and 0 if equal.
*
* @param a the first value
* @param b the second value
* @return the result
*/
public static int compareInt(int a, int b) {
return a == b ? 0 : a < b ? -1 : 1;
}
/**
* Compare two values. Returns -1 if the first value is smaller, 1 if
* bigger, and 0 if equal.
*
* @param a the first value
* @param b the second value
* @return the result
*/
public static int compareLong(long a, long b) {
return a == b ? 0 : a < b ? -1 : 1;
}
/**
* Get a cryptographically secure pseudo random long value.
*
......
......@@ -10,7 +10,6 @@ import java.sql.SQLException;
import org.h2.api.ErrorCode;
import org.h2.message.DbException;
import org.h2.util.MathUtils;
/**
* Implementation of the BYTE data type.
......@@ -106,7 +105,7 @@ public class ValueByte extends Value {
@Override
protected int compareSecure(Value o, CompareMode mode) {
ValueByte v = (ValueByte) o;
return MathUtils.compareInt(value, v.value);
return Integer.compare(value, v.value);
}
@Override
......
......@@ -12,7 +12,6 @@ import java.sql.SQLException;
import org.h2.api.ErrorCode;
import org.h2.message.DbException;
import org.h2.util.DateTimeUtils;
import org.h2.util.MathUtils;
import org.h2.util.StringUtils;
/**
......@@ -121,7 +120,7 @@ public class ValueDate extends Value {
@Override
protected int compareSecure(Value o, CompareMode mode) {
return MathUtils.compareLong(dateValue, ((ValueDate) o).dateValue);
return Long.compare(dateValue, ((ValueDate) o).dateValue);
}
@Override
......
......@@ -8,7 +8,6 @@ package org.h2.value;
import java.util.Locale;
import org.h2.api.ErrorCode;
import org.h2.message.DbException;
import org.h2.util.MathUtils;
public class ValueEnum extends ValueEnumBase {
private static enum Validation {
......@@ -57,7 +56,7 @@ public class ValueEnum extends ValueEnumBase {
@Override
protected int compareSecure(final Value v, final CompareMode mode) {
return MathUtils.compareInt(getInt(), v.getInt());
return Integer.compare(getInt(), v.getInt());
}
/**
......
......@@ -7,7 +7,6 @@ package org.h2.value;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.h2.util.MathUtils;
/**
* Base implementation of the ENUM data type.
......@@ -35,7 +34,7 @@ public class ValueEnumBase extends Value {
@Override
protected int compareSecure(final Value v, final CompareMode mode) {
return MathUtils.compareInt(getInt(), v.getInt());
return Integer.compare(getInt(), v.getInt());
}
@Override
......
......@@ -10,7 +10,6 @@ import java.sql.SQLException;
import org.h2.api.ErrorCode;
import org.h2.message.DbException;
import org.h2.util.MathUtils;
/**
* Implementation of the INT data type.
......@@ -140,7 +139,7 @@ public class ValueInt extends Value {
@Override
protected int compareSecure(Value o, CompareMode mode) {
ValueInt v = (ValueInt) o;
return MathUtils.compareInt(value, v.value);
return Integer.compare(value, v.value);
}
@Override
......
......@@ -12,7 +12,6 @@ import java.sql.SQLException;
import org.h2.api.ErrorCode;
import org.h2.message.DbException;
import org.h2.util.MathUtils;
/**
* Implementation of the BIGINT data type.
......@@ -171,7 +170,7 @@ public class ValueLong extends Value {
@Override
protected int compareSecure(Value o, CompareMode mode) {
ValueLong v = (ValueLong) o;
return MathUtils.compareLong(value, v.value);
return Long.compare(value, v.value);
}
@Override
......
......@@ -10,7 +10,6 @@ import java.sql.SQLException;
import org.h2.api.ErrorCode;
import org.h2.message.DbException;
import org.h2.util.MathUtils;
/**
* Implementation of the SMALLINT data type.
......@@ -106,7 +105,7 @@ public class ValueShort extends Value {
@Override
protected int compareSecure(Value o, CompareMode mode) {
ValueShort v = (ValueShort) o;
return MathUtils.compareInt(value, v.value);
return Integer.compare(value, v.value);
}
@Override
......
......@@ -11,7 +11,6 @@ import java.sql.Time;
import org.h2.api.ErrorCode;
import org.h2.message.DbException;
import org.h2.util.DateTimeUtils;
import org.h2.util.MathUtils;
import org.h2.util.StringUtils;
/**
......@@ -129,7 +128,7 @@ public class ValueTime extends Value {
@Override
protected int compareSecure(Value o, CompareMode mode) {
return MathUtils.compareLong(nanos, ((ValueTime) o).nanos);
return Long.compare(nanos, ((ValueTime) o).nanos);
}
@Override
......
......@@ -16,7 +16,6 @@ import org.h2.api.ErrorCode;
import org.h2.engine.Mode;
import org.h2.message.DbException;
import org.h2.util.DateTimeUtils;
import org.h2.util.MathUtils;
/**
* Implementation of the TIMESTAMP data type.
......@@ -310,11 +309,11 @@ public class ValueTimestamp extends Value {
@Override
protected int compareSecure(Value o, CompareMode mode) {
ValueTimestamp t = (ValueTimestamp) o;
int c = MathUtils.compareLong(dateValue, t.dateValue);
int c = Long.compare(dateValue, t.dateValue);
if (c != 0) {
return c;
}
return MathUtils.compareLong(timeNanos, t.timeNanos);
return Long.compare(timeNanos, t.timeNanos);
}
@Override
......
......@@ -14,7 +14,6 @@ import org.h2.api.ErrorCode;
import org.h2.api.TimestampWithTimeZone;
import org.h2.message.DbException;
import org.h2.util.DateTimeUtils;
import org.h2.util.MathUtils;
import org.h2.util.StringUtils;
/**
......@@ -310,14 +309,14 @@ public class ValueTimestampTimeZone extends Value {
b -= t.timeZoneOffsetMins;
// compare date
int c = MathUtils.compareLong(a, b);
int c = Long.compare(a, b);
if (c != 0) {
return c;
}
// compare time
long na = timeNanos - (ma * 1000L * 1000L * 1000L * 60L);
long nb = t.timeNanos - (mb * 1000L * 1000L * 1000L * 60L);
return MathUtils.compareLong(na, nb);
return Long.compare(na, nb);
}
@Override
......
......@@ -159,7 +159,7 @@ public class ValueUuid extends Value {
}
ValueUuid v = (ValueUuid) o;
if (high == v.high) {
return MathUtils.compareLong(low, v.low);
return Long.compare(low, v.low);
}
return high > v.high ? 1 : -1;
}
......
......@@ -113,10 +113,18 @@ public class TestAlter extends TestBase {
}
private void testAlterTableDropMultipleColumns() throws SQLException {
stat.execute("create table test(id int, name varchar, name2 varchar)");
stat.execute("alter table test drop column name, name2");
stat.execute("create table test(id int, b varchar, c int, d int)");
stat.execute("alter table test drop column b, c");
stat.execute("alter table test drop d");
stat.execute("drop table test");
// Test-Case: Same as above but using brackets (Oracle style)
stat.execute("create table test(id int, b varchar, c int, d int)");
stat.execute("alter table test drop column (b, c)");
assertThrows(ErrorCode.COLUMN_NOT_FOUND_1, stat).
execute("alter table test drop column b");
stat.execute("alter table test drop (d)");
stat.execute("drop table test");
// Test-Case: Error if dropping all columns
stat.execute("create table test(id int, name varchar, name2 varchar)");
assertThrows(ErrorCode.CANNOT_DROP_LAST_COLUMN, stat).
execute("alter table test drop column id, name, name2");
......@@ -209,6 +217,8 @@ public class TestAlter extends TestBase {
stat.execute("drop table t");
}
// column and field names must be upper-case due to getMetaData sensitivity
private void testAlterTableAddMultipleColumnsBefore() throws SQLException {
stat.execute("create table T(X varchar)");
......@@ -304,7 +314,7 @@ public class TestAlter extends TestBase {
// This failed in v1.4.196
stat.execute("create table T (C int not null)");
stat.execute("alter table T modify C null"); // Silently corrupted column C
stat.execute("insert into T values(null)"); // <- ERROR: NULL not allowed
stat.execute("insert into T values(null)"); // <- Fixed in v1.4.196 - NULL is allowed
stat.execute("drop table T");
}
}
......@@ -150,18 +150,6 @@ public class TestCases extends TestBase {
"foreign key(a_id) references a(id)");
stat.execute("update a set x=200");
stat.execute("drop table if exists a, b");
stat.execute("drop all objects");
stat.execute("create table parent(id int primary key)");
stat.execute("create table child(id int, parent_id int, x int)");
stat.execute("create index y on child(parent_id, x)");
stat.execute("alter table child add constraint z " +
"foreign key(parent_id) references parent(id)");
ResultSet rs = stat.executeQuery(
"select * from information_schema.indexes where table_name = 'CHILD'");
while (rs.next()) {
assertEquals("Y", rs.getString("index_name"));
}
conn.close();
}
......
......@@ -34,18 +34,81 @@ public class TestCompatibilityOracle extends TestBase {
@Override
public void test() throws Exception {
testNotNullSyntax();
testTreatEmptyStringsAsNull();
testDecimalScale();
testPoundSymbolInColumnName();
testToDate();
testForbidEmptyInClause();
testSpecialTypes();
}
private void testTreatEmptyStringsAsNull() throws SQLException {
private void testNotNullSyntax() throws SQLException {
deleteDb("oracle");
Connection conn = getConnection("oracle;MODE=Oracle");
Statement stat = conn.createStatement();
// Some other variation (oracle syntax)
stat.execute("create table T (C int not null enable)");
stat.execute("insert into T values(1)");
stat.execute("drop table T");
stat.execute("create table T (C int not null enable validate)");
stat.execute("insert into T values(1)");
stat.execute("drop table T");
// can set NULL
stat.execute("create table T (C int not null disable)"); // can set NULL even with 'not null syntax' (oracle)
stat.execute("insert into T values(null)");
stat.execute("drop table T");
stat.execute("create table T (C int not null enable novalidate)"); // can set NULL even with 'not null syntax' (oracle)
stat.execute("insert into T values(null)");
stat.execute("drop table T");
// Some other variation with oracle syntax
stat.execute("create table T (C int not null)");
stat.execute("insert into T values(1)");
stat.execute("alter table T modify C not null");
stat.execute("insert into T values(1)");
stat.execute("alter table T modify C not null enable");
stat.execute("insert into T values(1)");
stat.execute("alter table T modify C not null enable validate");
stat.execute("insert into T values(1)");
stat.execute("drop table T");
// can set NULL
stat.execute("create table T (C int null)");
stat.execute("insert into T values(null)");
stat.execute("alter table T modify C null enable");
stat.execute("alter table T modify C null enable validate");
stat.execute("insert into T values(null)");
stat.execute("alter table T modify C not null disable"); // can set NULL even with 'not null syntax' (oracle)
stat.execute("insert into T values(null)");
stat.execute("alter table T modify C not null enable novalidate"); // can set NULL even with 'not null syntax' (oracle)
stat.execute("insert into T values(null)");
stat.execute("drop table T");
conn.close();
}
private void testSpecialTypes() throws SQLException {
// Test VARCHAR, VARCHAR2 with CHAR and BYTE
deleteDb("oracle");
Connection conn = getConnection("oracle;MODE=Oracle");
Statement stat = conn.createStatement();
stat.execute("create table T (ID NUMBER)");
stat.execute("alter table T add A_1 VARCHAR(1)");
stat.execute("alter table T add A_2 VARCHAR2(1)");
stat.execute("alter table T add B_1 VARCHAR(1 byte)"); // with BYTE
stat.execute("alter table T add B_2 VARCHAR2(1 byte)");
stat.execute("alter table T add C_1 VARCHAR(1 char)"); // with CHAR
stat.execute("alter table T add C_2 VARCHAR2(1 char)");
stat.execute("alter table T add B_255 VARCHAR(255 byte)");
stat.execute("alter table T add C_255 VARCHAR(255 char)");
stat.execute("drop table T");
conn.close();
}
private void testTreatEmptyStringsAsNull() throws SQLException {
deleteDb("oracle");
Connection conn = getConnection("oracle;MODE=Oracle");
Statement stat = conn.createStatement();
stat.execute("CREATE TABLE A (ID NUMBER, X VARCHAR2(1))");
stat.execute("INSERT INTO A VALUES (1, 'a')");
stat.execute("INSERT INTO A VALUES (2, '')");
......
......@@ -13,6 +13,8 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
* Tests the client info
*/
......@@ -35,6 +37,7 @@ public class TestConnection extends TestBase {
testSetSupportedClientInfoProperties();
testSetUnsupportedClientInfoProperties();
testSetInternalProperty();
testSetInternalPropertyToInitialValue();
testSetGetSchema();
}
......@@ -47,6 +50,29 @@ public class TestConnection extends TestBase {
assertThrows(SQLClientInfoException.class, conn).setClientInfo("server23", "SomeValue");
conn.close();
}
/**
* Test that no exception is thrown if the client info of a connection managed in a connection pool is reset
* to its initial values.
*
* This is needed when using h2 in websphere liberty.
*/
private void testSetInternalPropertyToInitialValue() throws SQLException {
// Use MySQL-mode since this allows all property names
// (apart from h2 internal names).
Connection conn = getConnection("clientInfoMySQL;MODE=MySQL");
String numServersPropertyName = "numServers";
String numServers = conn.getClientInfo(numServersPropertyName);
conn.setClientInfo(numServersPropertyName, numServers);
assertEquals(conn.getClientInfo(numServersPropertyName), numServers);
conn.close();
}
private void testSetUnsupportedClientInfoProperties() throws SQLException {
Connection conn = getConnection("clientInfo");
......
......@@ -11,8 +11,6 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.PrintStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
......@@ -74,12 +72,15 @@ public class TestScript extends TestBase {
}
@Override
public void test() throws Exception {
public void test() throws Exception {
if (config.networked && config.big) {
return;
}
reconnectOften = !config.memory && config.big;
testScript("testScript.sql");
testScript("altertable-index-reuse.sql");
testScript("query-optimisations.sql");
testScript("commands-dml-script.sql");
testScript("commands-dml-create-view.sql");
for (String s : new String[] { "array", "bigint", "binary", "blob",
......
-- Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
-- and the EPL 1.0 (http://h2database.com/html/license.html).
-- Initial Developer: H2 Group
--
CREATE TABLE "domains" ("id" bigint NOT NULL auto_increment PRIMARY KEY);
> ok
CREATE TABLE "users" ("id" bigint NOT NULL auto_increment PRIMARY KEY,"username" varchar_ignorecase(255),"domain" bigint,"desc" varchar_ignorecase(255));
> ok
-- adds constraint on (domain,username) and generates unique index domainusername_key_INDEX_xxx
ALTER TABLE "users" ADD CONSTRAINT "domainusername_key" UNIQUE ("domain","username");
> ok
-- adds foreign key on domain - if domainusername_key didn't exist it would create unique index on domain, but it reuses the existing index
ALTER TABLE "users" ADD CONSTRAINT "udomain_fkey" FOREIGN KEY ("domain") REFERENCES "domains"("id") ON DELETE RESTRICT;
> ok
-- now we drop the domainusername_key, but domainusername_key_INDEX_xxx is used by udomain_fkey and was not being dropped
-- this was an issue, because it's a unique index and still enforcing constraint on (domain,username)
ALTER TABLE "users" DROP CONSTRAINT "domainusername_key";
> ok
insert into "domains" ("id") VALUES (1);
> update count: 1
insert into "users" ("username","domain","desc") VALUES ('test',1,'first user');
> update count: 1
-- should work,because we dropped domainusername_key, but failed: Unique index or primary key violation
INSERT INTO "users" ("username","domain","desc") VALUES ('test',1,'second user');
> update count: 1
......@@ -48,6 +48,12 @@ select rank from card where suit = 'diamonds';
> ----
> 8
select column_type from information_schema.columns where COLUMN_NAME = 'SUIT';
> COLUMN_TYPE
> ------------------------------------------
> ENUM('hearts','clubs','spades','diamonds')
> rows: 1
--- ENUM integer-based operations
select rank from card where suit = 1;
......
-- Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
-- and the EPL 1.0 (http://h2database.com/html/license.html).
-- Initial Developer: H2 Group
--
create table person(firstname varchar, lastname varchar);
> ok
create index person_1 on person(firstname, lastname);
> ok
insert into person select convert(x,varchar) as firstname, (convert(x,varchar) || ' last') as lastname from system_range(1,100);
> update count: 100
-- Issue #643: verify that when using an index, we use the IN part of the query, if that part of the query
-- can directly use the index.
--
explain analyze SELECT * FROM person WHERE firstname IN ('FirstName1', 'FirstName2') AND lastname='LastName1';
> PLAN
> -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> SELECT PERSON.FIRSTNAME, PERSON.LASTNAME FROM PUBLIC.PERSON /* PUBLIC.PERSON_1: FIRSTNAME IN('FirstName1', 'FirstName2') AND LASTNAME = 'LastName1' */ /* scanCount: 1 */ WHERE (FIRSTNAME IN('FirstName1', 'FirstName2')) AND (LASTNAME = 'LastName1')
> rows: 1
......@@ -2705,13 +2705,13 @@ create memory table orders ( orderid varchar(10), name varchar(20), customer_id
> ok
select * from information_schema.columns where table_name = 'ORDERS';
> TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION COLUMN_DEFAULT IS_NULLABLE DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_PRECISION_RADIX NUMERIC_SCALE CHARACTER_SET_NAME COLLATION_NAME TYPE_NAME NULLABLE IS_COMPUTED SELECTIVITY CHECK_CONSTRAINT SEQUENCE_NAME REMARKS SOURCE_DATA_TYPE
> ------------- ------------ ---------- ----------- ---------------- -------------- ----------- --------- ------------------------ ---------------------- ----------------- ----------------------- ------------- ------------------ -------------- --------- -------- ----------- ----------- ---------------- ------------- ------- ----------------
> SCRIPT PUBLIC ORDERS COMPLETED 4 null NO 3 1 1 1 10 0 Unicode OFF DECIMAL 0 FALSE 50 null null
> SCRIPT PUBLIC ORDERS CUSTOMER_ID 3 null YES 12 10 10 10 10 0 Unicode OFF VARCHAR 1 FALSE 50 null null
> SCRIPT PUBLIC ORDERS NAME 2 null YES 12 20 20 20 10 0 Unicode OFF VARCHAR 1 FALSE 50 null null
> SCRIPT PUBLIC ORDERS ORDERID 1 null YES 12 10 10 10 10 0 Unicode OFF VARCHAR 1 FALSE 50 null null
> SCRIPT PUBLIC ORDERS VERIFIED 5 null YES 3 1 1 1 10 0 Unicode OFF DECIMAL 1 FALSE 50 null null
> TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION COLUMN_DEFAULT IS_NULLABLE DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_PRECISION_RADIX NUMERIC_SCALE CHARACTER_SET_NAME COLLATION_NAME TYPE_NAME NULLABLE IS_COMPUTED SELECTIVITY CHECK_CONSTRAINT SEQUENCE_NAME REMARKS SOURCE_DATA_TYPE COLUMN_TYPE
> ------------- ------------ ---------- ----------- ---------------- -------------- ----------- --------- ------------------------ ---------------------- ----------------- ----------------------- ------------- ------------------ -------------- --------- -------- ----------- ----------- ---------------- ------------- ------- ---------------- -------------------
> SCRIPT PUBLIC ORDERS COMPLETED 4 null NO 3 1 1 1 10 0 Unicode OFF DECIMAL 0 FALSE 50 null null NUMERIC(1) NOT NULL
> SCRIPT PUBLIC ORDERS CUSTOMER_ID 3 null YES 12 10 10 10 10 0 Unicode OFF VARCHAR 1 FALSE 50 null null VARCHAR(10)
> SCRIPT PUBLIC ORDERS NAME 2 null YES 12 20 20 20 10 0 Unicode OFF VARCHAR 1 FALSE 50 null null VARCHAR(20)
> SCRIPT PUBLIC ORDERS ORDERID 1 null YES 12 10 10 10 10 0 Unicode OFF VARCHAR 1 FALSE 50 null null VARCHAR(10)
> SCRIPT PUBLIC ORDERS VERIFIED 5 null YES 3 1 1 1 10 0 Unicode OFF DECIMAL 1 FALSE 50 null null NUMERIC(1)
> rows: 5
drop table orders;
......
......@@ -18,6 +18,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Savepoint;
import java.sql.Statement;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.h2.api.ErrorCode;
import org.h2.engine.Constants;
......@@ -85,6 +86,7 @@ public class TestMVTableEngine extends TestBase {
testDataTypes();
testLocking();
testSimple();
testReverseDeletePerformance();
}
private void testLobCopy() throws Exception {
......@@ -1478,4 +1480,32 @@ public class TestMVTableEngine extends TestBase {
conn.close();
}
private void testReverseDeletePerformance() throws Exception {
long direct = 0;
long reverse = 0;
for (int i = 0; i < 5; i++) {
reverse += testReverseDeletePerformance(true);
direct += testReverseDeletePerformance(false);
}
assertTrue("direct: " + direct + ", reverse: " + reverse, 2 * Math.abs(reverse - direct) < reverse + direct);
}
private long testReverseDeletePerformance(boolean reverse) throws Exception {
deleteDb(getTestName());
String dbName = getTestName() + ";MV_STORE=TRUE";
try (Connection conn = getConnection(dbName)) {
Statement stat = conn.createStatement();
stat.execute("CREATE TABLE test(id INT PRIMARY KEY, name VARCHAR) AS SELECT x, x || space(1024) || x FROM system_range(1, 1000)");
conn.setAutoCommit(false);
PreparedStatement prep = conn.prepareStatement("DELETE FROM test WHERE id = ?");
long start = System.nanoTime();
for (int i = 0; i < 1000; i++) {
prep.setInt(1, reverse ? 1000 - i : i);
prep.execute();
}
long end = System.nanoTime();
conn.commit();
return TimeUnit.NANOSECONDS.toMillis(end - start);
}
}
}
......@@ -35,7 +35,7 @@ public class TestAutoReconnect extends TestBase {
TestBase.createCaller().init().test();
}
private void restart() throws SQLException {
private void restart() throws SQLException, InterruptedException {
if (autoServer) {
if (connServer != null) {
connServer.createStatement().execute("SHUTDOWN");
......@@ -45,6 +45,7 @@ public class TestAutoReconnect extends TestBase {
connServer = getConnection(url);
} else {
server.stop();
Thread.sleep(100); // try to prevent "port may be in use" error
server.start();
}
}
......
......@@ -5,10 +5,7 @@
*/
package org.h2.test.unit;
import java.math.BigInteger;
import java.util.Random;
import org.h2.test.TestBase;
import org.h2.test.utils.AssertThrows;
import org.h2.util.MathUtils;
/**
......@@ -28,8 +25,7 @@ public class TestMathUtils extends TestBase {
@Override
public void test() {
testRandom();
testReverse();
testFactorial();
testNextPowerOf2Int();
}
private void testRandom() {
......@@ -53,78 +49,17 @@ public class TestMathUtils extends TestBase {
assertTrue(data.length > 10);
}
private void testReverse() {
assertEquals(Integer.reverse(0), Integer.reverse(0));
assertEquals(Integer.reverse(Integer.MAX_VALUE),
Integer.reverse(Integer.MAX_VALUE));
assertEquals(Integer.reverse(Integer.MIN_VALUE),
Integer.reverse(Integer.MIN_VALUE));
assertEquals(Long.reverse(0), Long.reverse(0L));
assertEquals(Long.reverse(Long.MAX_VALUE), Long.reverse(Long.MAX_VALUE));
assertEquals(Long.reverse(Long.MIN_VALUE), Long.reverse(Long.MIN_VALUE));
for (int i = Integer.MIN_VALUE; i < 0; i += 1019) {
int x = Integer.reverse(i);
assertEquals(Integer.reverse(i), x);
}
for (int i = 0; i > 0; i += 1019) {
int x = Integer.reverse(i);
assertEquals(Integer.reverse(i), x);
}
for (long i = Long.MIN_VALUE; i < 0; i += 1018764321251L) {
long x = Long.reverse(i);
assertEquals(Long.reverse(i), x);
}
for (long i = 0; i > 0; i += 1018764321251L) {
long x = Long.reverse(i);
assertEquals(Long.reverse(i), x);
}
Random random = new Random(10);
for (int i = 0; i < 1000000; i++) {
long x = random.nextLong();
long r = Long.reverse(x);
assertEquals(Long.reverse(x), r);
int y = random.nextInt();
int s = Integer.reverse(y);
assertEquals(Integer.reverse(y), s);
}
}
private void testNextPowerOf2Int() {
// the largest power of two that fits into an integer
final int LARGEST_POW2 = 0x40000000;
int[] testValues = { 0, 1, 2, 3, 4, 12, 17, 500, 1023,
LARGEST_POW2-500, LARGEST_POW2 };
int[] resultValues = { 1, 1, 2, 4, 4, 16, 32, 512, 1024,
LARGEST_POW2, LARGEST_POW2 };
private void testFactorial() {
new AssertThrows(IllegalArgumentException.class) { @Override
public void test() {
factorial(-1);
}};
assertEquals("1", factorial(0).toString());
assertEquals("1", factorial(1).toString());
assertEquals("2", factorial(2).toString());
assertEquals("6", factorial(3).toString());
assertEquals("3628800", factorial(10).toString());
assertEquals("2432902008176640000", factorial(20).toString());
}
/**
* Calculate the factorial (n!) of a number.
* This implementation uses a naive multiplication loop, and
* is very slow for large n.
* For n = 1000, it takes about 10 ms.
* For n = 8000, it takes about 800 ms.
*
* @param n the number
* @return the factorial of n
*/
public static BigInteger factorial(int n) {
if (n < 0) {
throw new IllegalArgumentException(n + "<0");
} else if (n < 2) {
return BigInteger.ONE;
}
BigInteger x = new BigInteger("" + n);
BigInteger result = x;
for (int i = n - 1; i >= 2; i--) {
x = x.subtract(BigInteger.ONE);
result = result.multiply(x);
for (int i = 0; i < testValues.length; i++) {
assertEquals(resultValues[i], MathUtils.nextPowerOf2(testValues[i]));
}
return result;
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论