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

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

...@@ -206,11 +206,13 @@ ...@@ -206,11 +206,13 @@
<plugin> <plugin>
<groupId>org.codehaus.mojo</groupId> <groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId> <artifactId>build-helper-maven-plugin</artifactId>
<version>1.10</version> <version>3.0.0</version>
<executions> <executions>
<execution> <execution>
<phase>generate-test-sources</phase> <phase>generate-test-sources</phase>
<goals><goal>add-test-source</goal></goals> <goals>
<goal>add-test-source</goal>
</goals>
<configuration> <configuration>
<sources> <sources>
<source>src/tools</source> <source>src/tools</source>
...@@ -219,53 +221,51 @@ ...@@ -219,53 +221,51 @@
</execution> </execution>
</executions> </executions>
</plugin> </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> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-toolchains-plugin</artifactId> <artifactId>maven-enforcer-plugin</artifactId>
<version>1.1</version> <version>3.0.0-M1</version>
<executions> <executions>
<execution> <execution>
<id>check-java-version</id>
<goals> <goals>
<goal>toolchain</goal> <goal>enforce</goal>
</goals> </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>
</goals>
</execution>
</executions>
<configuration> <configuration>
<rules>
<enforceBytecodeVersion>
<maxJdkVersion>1.7</maxJdkVersion>
</enforceBytecodeVersion>
<checkSignatureRule implementation="org.codehaus.mojo.animal_sniffer.enforcer.CheckSignatureRule">
<signature> <signature>
<groupId>org.codehaus.mojo.signature</groupId> <groupId>org.codehaus.mojo.signature</groupId>
<artifactId>java16</artifactId> <artifactId>java17</artifactId>
<version>1.1</version> <version>1.0</version>
</signature> </signature>
</checkSignatureRule>
</rules>
<fail>true</fail>
</configuration> </configuration>
</execution>
</executions>
<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> </plugin>
<!-- Disable surefire since we don't use Junit -->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version> <version>2.20.1</version>
<configuration> <configuration>
<includes> <includes>
<include>TestAllJunit.java</include> <include>TestAllJunit.java</include>
......
...@@ -216,7 +216,7 @@ The web site is currently translated using Google. ...@@ -216,7 +216,7 @@ The web site is currently translated using Google.
<p> <p>
If you'd like to contribute bug fixes or new features, please consider the following guidelines to simplify merging them: If you'd like to contribute bug fixes or new features, please consider the following guidelines to simplify merging them:
</p> </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. </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). For example, do not use tabs (use spaces instead).
The checkstyle configuration is in <code>src/installer/checkstyle.xml</code>. The checkstyle configuration is in <code>src/installer/checkstyle.xml</code>.
......
...@@ -21,6 +21,16 @@ Change Log ...@@ -21,6 +21,16 @@ Change Log
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul> <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>Reset transaction start timestamp on ROLLBACK
</li> </li>
<li>Issue #632: CREATE OR REPLACE VIEW creates incorrect columns names <li>Issue #632: CREATE OR REPLACE VIEW creates incorrect columns names
...@@ -123,7 +133,7 @@ Change Log ...@@ -123,7 +133,7 @@ Change Log
</li> </li>
<li>Added support for invisible columns. <li>Added support for invisible columns.
</li> </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>
<li>MVStore: for object data types, the cache size memory estimation <li>MVStore: for object data types, the cache size memory estimation
was sometimes far off in a read-only scenario. 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 { ...@@ -212,7 +212,7 @@ public class AlterTableAddConstraint extends SchemaCommand {
isOwner = true; isOwner = true;
index.getIndexType().setBelongsToConstraint(true); index.getIndexType().setBelongsToConstraint(true);
} else { } else {
index = getIndex(table, indexColumns, true); index = getIndex(table, indexColumns, false);
if (index == null) { if (index == null) {
index = createIndex(table, indexColumns, false); index = createIndex(table, indexColumns, false);
isOwner = true; isOwner = true;
...@@ -244,20 +244,20 @@ public class AlterTableAddConstraint extends SchemaCommand { ...@@ -244,20 +244,20 @@ public class AlterTableAddConstraint extends SchemaCommand {
} }
int id = getObjectId(); int id = getObjectId();
String name = generateConstraintName(table); String name = generateConstraintName(table);
ConstraintReferential ref = new ConstraintReferential(getSchema(), ConstraintReferential refConstraint = new ConstraintReferential(getSchema(),
id, name, table); id, name, table);
ref.setColumns(indexColumns); refConstraint.setColumns(indexColumns);
ref.setIndex(index, isOwner); refConstraint.setIndex(index, isOwner);
ref.setRefTable(refTable); refConstraint.setRefTable(refTable);
ref.setRefColumns(refIndexColumns); refConstraint.setRefColumns(refIndexColumns);
ref.setRefIndex(refIndex, isRefOwner); refConstraint.setRefIndex(refIndex, isRefOwner);
if (checkExisting) { if (checkExisting) {
ref.checkExistingData(session); refConstraint.checkExistingData(session);
} }
constraint = ref; refTable.addConstraint(refConstraint);
refTable.addConstraint(constraint); refConstraint.setDeleteAction(deleteAction);
ref.setDeleteAction(deleteAction); refConstraint.setUpdateAction(updateAction);
ref.setUpdateAction(updateAction); constraint = refConstraint;
break; break;
} }
default: default:
...@@ -330,27 +330,28 @@ public class AlterTableAddConstraint extends SchemaCommand { ...@@ -330,27 +330,28 @@ public class AlterTableAddConstraint extends SchemaCommand {
return null; 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, private static boolean canUseUniqueIndex(Index idx, Table table,
IndexColumn[] cols) { IndexColumn[] cols) {
if (idx.getTable() != table || !idx.getIndexType().isUnique()) { if (idx.getTable() != table || !idx.getIndexType().isUnique()) {
return false; return false;
} }
Column[] indexCols = idx.getColumns(); Column[] indexCols = idx.getColumns();
if (indexCols.length > cols.length) {
return false; HashSet<Column> indexColsSet = New.hashSet();
}
HashSet<Column> set = New.hashSet();
for (IndexColumn c : cols) {
set.add(c.column);
}
for (Column c : indexCols) { for (Column c : indexCols) {
// all columns of the index must be part of the list, indexColsSet.add(c);
// but not all columns of the list need to be part of the index
if (!set.contains(c)) {
return false;
} }
HashSet<Column> colsSet = New.hashSet();
for (IndexColumn c : cols) {
colsSet.add(c.column);
} }
return true;
return colsSet.equals(indexColsSet);
} }
private static boolean canUseIndex(Index existingIndex, Table table, private static boolean canUseIndex(Index existingIndex, Table table,
......
...@@ -165,7 +165,7 @@ public class Database implements DataHandler { ...@@ -165,7 +165,7 @@ public class Database implements DataHandler {
private boolean referentialIntegrity = true; private boolean referentialIntegrity = true;
private boolean multiVersion; private boolean multiVersion;
private DatabaseCloser closeOnExit; private DatabaseCloser closeOnExit;
private Mode mode = Mode.getInstance(Mode.REGULAR); private Mode mode = Mode.getRegular();
private boolean multiThreaded; private boolean multiThreaded;
private int maxOperationMemory = private int maxOperationMemory =
Constants.DEFAULT_MAX_OPERATION_MEMORY; Constants.DEFAULT_MAX_OPERATION_MEMORY;
...@@ -902,6 +902,9 @@ public class Database implements DataHandler { ...@@ -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. * Lock the metadata table for updates.
* *
...@@ -916,6 +919,19 @@ public class Database implements DataHandler { ...@@ -916,6 +919,19 @@ public class Database implements DataHandler {
if (meta == null) { if (meta == null) {
return true; 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); boolean wasLocked = meta.lock(session, true, true);
return wasLocked; return wasLocked;
} }
...@@ -947,10 +963,20 @@ public class Database implements DataHandler { ...@@ -947,10 +963,20 @@ public class Database implements DataHandler {
* @param session the session * @param session the session
*/ */
public void unlockMeta(Session session) { public void unlockMeta(Session session) {
unlockMetaDebug(session);
meta.unlock(session); meta.unlock(session);
session.unlock(meta); 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. * Remove the given object from the meta data.
* *
...@@ -980,6 +1006,7 @@ public class Database implements DataHandler { ...@@ -980,6 +1006,7 @@ public class Database implements DataHandler {
checkMetaFree(session, id); checkMetaFree(session, id);
} }
} else if (!wasLocked) { } else if (!wasLocked) {
unlockMetaDebug(session);
// must not keep the lock if it was not locked // must not keep the lock if it was not locked
// otherwise updating sequences may cause a deadlock // otherwise updating sequences may cause a deadlock
meta.unlock(session); meta.unlock(session);
...@@ -1392,6 +1419,7 @@ public class Database implements DataHandler { ...@@ -1392,6 +1419,7 @@ public class Database implements DataHandler {
if (!readOnly) { if (!readOnly) {
lockMeta(pageStore.getPageStoreSession()); lockMeta(pageStore.getPageStoreSession());
pageStore.compact(compactMode); pageStore.compact(compactMode);
unlockMeta(pageStore.getPageStoreSession());
} }
} catch (DbException e) { } catch (DbException e) {
if (SysProperties.CHECK2) { if (SysProperties.CHECK2) {
......
...@@ -325,6 +325,10 @@ public class Mode { ...@@ -325,6 +325,10 @@ public class Mode {
return getInstance(ModeEnum.Oracle.name()); return getInstance(ModeEnum.Oracle.name());
} }
public static Mode getRegular() {
return getInstance(ModeEnum.REGULAR.name());
}
public String getName() { public String getName() {
return name; return name;
} }
......
...@@ -857,6 +857,10 @@ public class Session extends SessionWithState { ...@@ -857,6 +857,10 @@ public class Session extends SessionWithState {
removeTemporaryLobs(false); removeTemporaryLobs(false);
cleanTempTables(true); cleanTempTables(true);
undoLog.clear(); 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); database.removeSession(this);
} finally { } finally {
closed = true; closed = true;
...@@ -965,6 +969,7 @@ public class Session extends SessionWithState { ...@@ -965,6 +969,7 @@ public class Session extends SessionWithState {
} }
locks.clear(); locks.clear();
} }
database.unlockMetaDebug(this);
savepoints = null; savepoints = null;
sessionStateChanged = true; sessionStateChanged = true;
} }
...@@ -991,11 +996,6 @@ public class Session extends SessionWithState { ...@@ -991,11 +996,6 @@ public class Session extends SessionWithState {
table.truncate(this); table.truncate(this);
} }
} }
// sometimes Table#removeChildrenAndResources
// will take the meta lock
if (closeSession) {
database.unlockMeta(this);
}
} }
} }
} }
......
...@@ -338,8 +338,15 @@ public class SysProperties { ...@@ -338,8 +338,15 @@ public class SysProperties {
* The maximum number of objects in the cache. * The maximum number of objects in the cache.
* This value must be a power of 2. * This value must be a power of 2.
*/ */
public static final int OBJECT_CACHE_SIZE = public static final int OBJECT_CACHE_SIZE;
MathUtils.nextPowerOf2(Utils.getProperty("h2.objectCacheSize", 1024)); 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> * System property <code>h2.oldStyleOuterJoin</code>
......
...@@ -21,7 +21,6 @@ import org.h2.table.Column; ...@@ -21,7 +21,6 @@ import org.h2.table.Column;
import org.h2.table.IndexColumn; import org.h2.table.IndexColumn;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
import org.h2.util.MathUtils;
import org.h2.util.StatementBuilder; import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import org.h2.value.Value; import org.h2.value.Value;
...@@ -348,7 +347,7 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index { ...@@ -348,7 +347,7 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
if (isMultiVersion) { if (isMultiVersion) {
int v1 = rowData.getVersion(); int v1 = rowData.getVersion();
int v2 = compare.getVersion(); int v2 = compare.getVersion();
return MathUtils.compareInt(v2, v1); return Integer.compare(v2, v1);
} }
return 0; return 0;
} }
......
...@@ -131,9 +131,9 @@ public class IndexCursor implements Cursor { ...@@ -131,9 +131,9 @@ public class IndexCursor implements Cursor {
if (isIntersects) { if (isIntersects) {
intersects = getSpatialSearchRow(intersects, columnId, v); intersects = getSpatialSearchRow(intersects, columnId, v);
} }
if (isStart || isEnd) { // An X=? condition will produce less rows than
// an X=? condition will produce less rows than // an X IN(..) condition, unless the X IN condition can use the index.
// an X IN(..) condition if ((isStart || isEnd) && !canUseIndexFor(inColumn)) {
inColumn = null; inColumn = null;
inList = null; inList = null;
inResult = null; inResult = null;
...@@ -176,6 +176,10 @@ public class IndexCursor implements Cursor { ...@@ -176,6 +176,10 @@ public class IndexCursor implements Cursor {
// only one IN(..) condition can be used at the same time // only one IN(..) condition can be used at the same time
return false; return false;
} }
return canUseIndexFor(column);
}
private boolean canUseIndexFor(Column column) {
// The first column of the index must match this column, // The first column of the index must match this column,
// or it must be a VIEW index (where the column is null). // or it must be a VIEW index (where the column is null).
// Multiple IN conditions with views are not supported, see // Multiple IN conditions with views are not supported, see
......
...@@ -10,7 +10,6 @@ import org.h2.engine.SysProperties; ...@@ -10,7 +10,6 @@ import org.h2.engine.SysProperties;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.result.Row; import org.h2.result.Row;
import org.h2.result.SearchRow; import org.h2.result.SearchRow;
import org.h2.util.MathUtils;
/** /**
* The cursor implementation for the multi-version index. * The cursor implementation for the multi-version index.
...@@ -143,7 +142,7 @@ public class MultiVersionCursor implements Cursor { ...@@ -143,7 +142,7 @@ public class MultiVersionCursor implements Cursor {
// version would be compared as well // version would be compared as well
long k1 = deltaRow.getKey(); long k1 = deltaRow.getKey();
long k2 = baseRow.getKey(); long k2 = baseRow.getKey();
compare = MathUtils.compareLong(k1, k2); compare = Long.compare(k1, k2);
} }
if (compare == 0) { if (compare == 0) {
if (isDeleted) { if (isDeleted) {
......
...@@ -30,9 +30,11 @@ import java.util.ArrayList; ...@@ -30,9 +30,11 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.command.CommandInterface; import org.h2.command.CommandInterface;
import org.h2.engine.ConnectionInfo; import org.h2.engine.ConnectionInfo;
...@@ -1738,6 +1740,12 @@ public class JdbcConnection extends TraceObject ...@@ -1738,6 +1740,12 @@ public class JdbcConnection extends TraceObject
} }
checkClosed(); 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)) { if (isInternalProperty(name)) {
throw new SQLClientInfoException( throw new SQLClientInfoException(
"Property name '" + name + " is used internally by H2.", "Property name '" + name + " is used internally by H2.",
......
...@@ -197,7 +197,7 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -197,7 +197,7 @@ public class MVPrimaryIndex extends BaseIndex {
} }
} }
TransactionMap<Value, Value> map = getMap(session); TransactionMap<Value, Value> map = getMap(session);
return new MVStoreCursor(session, map.entryIterator(min), max); return new MVStoreCursor(session, map.entryIterator(min, max));
} }
@Override @Override
...@@ -270,13 +270,13 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -270,13 +270,13 @@ public class MVPrimaryIndex extends BaseIndex {
TransactionMap<Value, Value> map = getMap(session); TransactionMap<Value, Value> map = getMap(session);
ValueLong v = (ValueLong) (first ? map.firstKey() : map.lastKey()); ValueLong v = (ValueLong) (first ? map.firstKey() : map.lastKey());
if (v == null) { if (v == null) {
return new MVStoreCursor(session, Collections return new MVStoreCursor(session,
.<Entry<Value, Value>> emptyList().iterator(), null); Collections.<Entry<Value, Value>> emptyList().iterator());
} }
Value value = map.get(v); Value value = map.get(v);
Entry<Value, Value> e = new DataUtils.MapEntry<Value, Value>(v, value); Entry<Value, Value> e = new DataUtils.MapEntry<Value, Value>(v, value);
List<Entry<Value, Value>> list = Arrays.asList(e); List<Entry<Value, Value>> list = Collections.singletonList(e);
MVStoreCursor c = new MVStoreCursor(session, list.iterator(), v); MVStoreCursor c = new MVStoreCursor(session, list.iterator());
c.next(); c.next();
return c; return c;
} }
...@@ -356,7 +356,7 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -356,7 +356,7 @@ public class MVPrimaryIndex extends BaseIndex {
*/ */
Cursor find(Session session, ValueLong first, ValueLong last) { Cursor find(Session session, ValueLong first, ValueLong last) {
TransactionMap<Value, Value> map = getMap(session); TransactionMap<Value, Value> map = getMap(session);
return new MVStoreCursor(session, map.entryIterator(first), last); return new MVStoreCursor(session, map.entryIterator(first, last));
} }
@Override @Override
...@@ -385,14 +385,12 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -385,14 +385,12 @@ public class MVPrimaryIndex extends BaseIndex {
private final Session session; private final Session session;
private final Iterator<Entry<Value, Value>> it; private final Iterator<Entry<Value, Value>> it;
private final ValueLong last;
private Entry<Value, Value> current; private Entry<Value, Value> current;
private Row row; 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.session = session;
this.it = it; this.it = it;
this.last = last;
} }
@Override @Override
...@@ -415,9 +413,6 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -415,9 +413,6 @@ public class MVPrimaryIndex extends BaseIndex {
@Override @Override
public boolean next() { public boolean next() {
current = it.hasNext() ? it.next() : null; current = it.hasNext() ? it.next() : null;
if (current != null && current.getKey().getLong() > last.getLong()) {
current = null;
}
row = null; row = null;
return current != null; return current != null;
} }
......
...@@ -1470,7 +1470,7 @@ public class TransactionStore { ...@@ -1470,7 +1470,7 @@ public class TransactionStore {
* @param from the first key to return * @param from the first key to return
* @return the iterator * @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>>() { return new Iterator<Entry<K, V>>() {
private Entry<K, V> current; private Entry<K, V> current;
private K currentKey = from; private K currentKey = from;
...@@ -1507,6 +1507,9 @@ public class TransactionStore { ...@@ -1507,6 +1507,9 @@ public class TransactionStore {
} }
} }
final K key = k; final K key = k;
if (to != null && map.getKeyType().compare(k, to) > 0) {
break;
}
VersionedValue data = cursor.getValue(); VersionedValue data = cursor.getValue();
data = getValue(key, readLogId, data); data = getValue(key, readLogId, data);
if (data != null && data.value != null) { if (data != null && data.value != null) {
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
*/ */
package org.h2.server.web; package org.h2.server.web;
import org.h2.util.MathUtils;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
/** /**
...@@ -61,7 +60,7 @@ public class ConnectionInfo implements Comparable<ConnectionInfo> { ...@@ -61,7 +60,7 @@ public class ConnectionInfo implements Comparable<ConnectionInfo> {
@Override @Override
public int compareTo(ConnectionInfo o) { public int compareTo(ConnectionInfo o) {
return -MathUtils.compareInt(lastAccess, o.lastAccess); return -Integer.compare(lastAccess, o.lastAccess);
} }
} }
...@@ -438,6 +438,7 @@ public class Data { ...@@ -438,6 +438,7 @@ public class Data {
writeByte((byte) type); writeByte((byte) type);
writeShortInt(v.getShort()); writeShortInt(v.getShort());
break; break;
case Value.ENUM:
case Value.INT: { case Value.INT: {
int x = v.getInt(); int x = v.getInt();
if (x < 0) { if (x < 0) {
...@@ -728,6 +729,7 @@ public class Data { ...@@ -728,6 +729,7 @@ public class Data {
return ValueBoolean.get(false); return ValueBoolean.get(false);
case INT_NEG: case INT_NEG:
return ValueInt.get(-readVarInt()); return ValueInt.get(-readVarInt());
case Value.ENUM:
case Value.INT: case Value.INT:
return ValueInt.get(readVarInt()); return ValueInt.get(readVarInt());
case LONG_NEG: case LONG_NEG:
...@@ -933,6 +935,7 @@ public class Data { ...@@ -933,6 +935,7 @@ public class Data {
return 2; return 2;
case Value.SHORT: case Value.SHORT:
return 3; return 3;
case Value.ENUM:
case Value.INT: { case Value.INT: {
int x = v.getInt(); int x = v.getInt();
if (x < 0) { if (x < 0) {
......
...@@ -471,9 +471,17 @@ public class Column { ...@@ -471,9 +471,17 @@ public class Column {
} }
} }
public String getCreateSQLWithoutName() {
return getCreateSQL(false);
}
public String getCreateSQL() { public String getCreateSQL() {
return getCreateSQL(true);
}
private String getCreateSQL(boolean includeName) {
StringBuilder buff = new StringBuilder(); StringBuilder buff = new StringBuilder();
if (name != null) { if (includeName && name != null) {
buff.append(Parser.quoteIdentifier(name)).append(' '); buff.append(Parser.quoteIdentifier(name)).append(' ');
} }
if (originalSQL != null) { if (originalSQL != null) {
......
...@@ -174,7 +174,8 @@ public class MetaTable extends Table { ...@@ -174,7 +174,8 @@ public class MetaTable extends Table {
"CHECK_CONSTRAINT", "CHECK_CONSTRAINT",
"SEQUENCE_NAME", "SEQUENCE_NAME",
"REMARKS", "REMARKS",
"SOURCE_DATA_TYPE SMALLINT" "SOURCE_DATA_TYPE SMALLINT",
"COLUMN_TYPE"
); );
indexColumnName = "TABLE_NAME"; indexColumnName = "TABLE_NAME";
break; break;
...@@ -828,7 +829,9 @@ public class MetaTable extends Table { ...@@ -828,7 +829,9 @@ public class MetaTable extends Table {
// REMARKS // REMARKS
replaceNullWithEmpty(c.getComment()), replaceNullWithEmpty(c.getComment()),
// SOURCE_DATA_TYPE // SOURCE_DATA_TYPE
null null,
// COLUMN_TYPE
c.getCreateSQLWithoutName()
); );
} }
} }
......
...@@ -16,7 +16,6 @@ import java.util.StringTokenizer; ...@@ -16,7 +16,6 @@ import java.util.StringTokenizer;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.store.fs.FileUtils; import org.h2.store.fs.FileUtils;
import org.h2.util.IOUtils; import org.h2.util.IOUtils;
import org.h2.util.MathUtils;
import org.h2.util.New; import org.h2.util.New;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import org.h2.util.Tool; import org.h2.util.Tool;
...@@ -46,9 +45,9 @@ public class ConvertTraceFile extends Tool { ...@@ -46,9 +45,9 @@ public class ConvertTraceFile extends Tool {
if (other == this) { if (other == this) {
return 0; return 0;
} }
int c = MathUtils.compareLong(other.time, time); int c = Long.compare(other.time, time);
if (c == 0) { if (c == 0) {
c = MathUtils.compareInt(other.executeCount, executeCount); c = Integer.compare(other.executeCount, executeCount);
if (c == 0) { if (c == 0) {
c = sql.compareTo(other.sql); c = sql.compareTo(other.sql);
} }
......
...@@ -13,14 +13,15 @@ import java.sql.DriverManager; ...@@ -13,14 +13,15 @@ import java.sql.DriverManager;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import org.h2.api.ErrorCode; import java.util.concurrent.ExecutionException;
import org.h2.engine.Constants; import java.util.concurrent.ExecutorService;
import org.h2.util.IOUtils; import java.util.concurrent.Executors;
import org.h2.util.JdbcUtils; import java.util.concurrent.Future;
import org.h2.util.Tool; import org.h2.util.Tool;
/** /**
* Creates a cluster from a standalone database. * Creates a cluster from a stand-alone database.
* <br /> * <br />
* Copies a database to another location if required. * Copies a database to another location if required.
* @h2.resource * @h2.resource
...@@ -100,116 +101,98 @@ public class CreateCluster extends Tool { ...@@ -100,116 +101,98 @@ public class CreateCluster extends Tool {
private static void process(String urlSource, String urlTarget, private static void process(String urlSource, String urlTarget,
String user, String password, String serverList) throws SQLException { 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(); org.h2.Driver.load();
// verify that the database doesn't exist, try (Connection connSource = DriverManager.getConnection(
// 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 // use cluster='' so connecting is possible
// even if the cluster is enabled // even if the cluster is enabled
connSource = DriverManager.getConnection(urlSource + urlSource + ";CLUSTER=''", user, password);
";CLUSTER=''", user, password); Statement statSource = connSource.createStatement())
statSource = connSource.createStatement(); {
// enable the exclusive mode and close other connections, // enable the exclusive mode and close other connections,
// so that data can't change while restoring the second database // so that data can't change while restoring the second database
statSource.execute("SET EXCLUSIVE 2"); statSource.execute("SET EXCLUSIVE 2");
pipeReader = new PipedReader();
try { try {
/* performTransfer(statSource, urlTarget, user, password, serverList);
* Pipe writer is used + closed in the inner class, in a } finally {
* separate thread (needs to be final). It should be initialized // switch back to the regular mode
* within try{} so an exception could be caught if creation statSource.execute("SET EXCLUSIVE FALSE");
* 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. private static void performTransfer(Statement statSource, String urlTarget,
// Start writing to pipe writer in separate thread. String user, String password, String serverList) throws SQLException {
final ResultSet rs = statSource.executeQuery("SCRIPT");
// Delete the target database first. // Delete the target database first.
connTarget = DriverManager.getConnection( try (Connection connTarget = DriverManager.getConnection(
urlTarget + ";CLUSTER=''", user, password); urlTarget + ";CLUSTER=''", user, password);
statTarget = connTarget.createStatement(); Statement statTarget = connTarget.createStatement())
{
statTarget.execute("DROP ALL OBJECTS DELETE FILES"); statTarget.execute("DROP ALL OBJECTS DELETE FILES");
connTarget.close(); }
try (PipedReader pipeReader = new PipedReader()) {
Future<?> threadFuture = startWriter(pipeReader, statSource);
new Thread( // Read data from pipe reader, restore on target.
new Runnable(){ try (Connection connTarget = DriverManager.getConnection(
@Override urlTarget, user, password);
public void run() { Statement statTarget = connTarget.createStatement())
{
RunScript.execute(connTarget, pipeReader);
// Check if the writer encountered any exception
try { try {
while (rs.next()) { threadFuture.get();
pipeWriter.write(rs.getString(1) + "\n"); } catch (ExecutionException ex) {
} throw new SQLException(ex.getCause());
} catch (SQLException ex) { } catch (InterruptedException ex) {
throw new IllegalStateException("Producing script from the source DB is failing.",ex); throw new SQLException(ex);
} catch (IOException ex) {
throw new IllegalStateException("Producing script from the source DB is failing.",ex);
} finally {
IOUtils.closeSilently(pipeWriter);
}
}
} }
).start();
// Read data from pipe reader, restore on target.
connTarget = DriverManager.getConnection(urlTarget, user, password);
RunScript.execute(connTarget,pipeReader);
statTarget = connTarget.createStatement();
// set the cluster to the serverList on both databases // set the cluster to the serverList on both databases
statSource.executeUpdate("SET CLUSTER '" + serverList + "'"); statSource.executeUpdate("SET CLUSTER '" + serverList + "'");
statTarget.executeUpdate("SET CLUSTER '" + serverList + "'"); statTarget.executeUpdate("SET CLUSTER '" + serverList + "'");
}
} catch (IOException ex) { } catch (IOException ex) {
throw new SQLException(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);
} }
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 { ...@@ -51,11 +51,17 @@ public class CacheLRU implements Cache {
this.writer = writer; this.writer = writer;
this.fifo = fifo; this.fifo = fifo;
this.setMaxMemory(maxMemoryKb); this.setMaxMemory(maxMemoryKb);
long tmpLen = MathUtils.nextPowerOf2(maxMemory / 64); 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) { if (tmpLen > Integer.MAX_VALUE) {
throw new IllegalStateException("do not support this much cache memory: " + maxMemoryKb + "kb"); 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; this.mask = len - 1;
clear(); clear();
} }
......
...@@ -77,7 +77,7 @@ public abstract class CacheObject implements Comparable<CacheObject> { ...@@ -77,7 +77,7 @@ public abstract class CacheObject implements Comparable<CacheObject> {
@Override @Override
public int compareTo(CacheObject other) { public int compareTo(CacheObject other) {
return MathUtils.compareInt(getPos(), other.getPos()); return Integer.compare(getPos(), other.getPos());
} }
public boolean isStream() { public boolean isStream() {
......
...@@ -13,7 +13,7 @@ public class ColumnNamer { ...@@ -13,7 +13,7 @@ public class ColumnNamer {
private ColumnNamerConfiguration configuration; private ColumnNamerConfiguration configuration;
private Session session; private Session session;
private Set<String> existingColumnNames = new HashSet<String>(); private final Set<String> existingColumnNames = new HashSet<>();
public ColumnNamer(Session session) { public ColumnNamer(Session session) {
this.session = session; this.session = session;
...@@ -31,9 +31,8 @@ public class ColumnNamer { ...@@ -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. * 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 * @param indexOfColumn index of column in below array
* @return
*/ */
public String getColumnName(Expression expr, int indexOfColumn) { public String getColumnName(Expression expr, int indexOfColumn) {
return getColumnName(expr,indexOfColumn,(String) null); return getColumnName(expr,indexOfColumn,(String) null);
......
...@@ -204,7 +204,7 @@ public class MathUtils { ...@@ -204,7 +204,7 @@ public class MathUtils {
* @param s the message to print * @param s the message to print
* @param t the stack trace * @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 // not a fatal problem, but maybe reduced security
System.out.println("Warning: " + s); System.out.println("Warning: " + s);
if (t != null) { if (t != null) {
...@@ -213,33 +213,27 @@ public class MathUtils { ...@@ -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. * power of two.
* *
* @param x the original value * @param x the original value
* @return the next power of two value * @return the next power of two value
* @throws IllegalArgumentException if x < 0 or x > 0x40000000
*/ */
public static int nextPowerOf2(int x) { public static int nextPowerOf2(int x) throws IllegalArgumentException {
long i = 1; if (x == 0) {
while (i < x && i < (Integer.MAX_VALUE / 2)) { return 1;
i += i; } else if (x < 0 || x > 0x40000000 ) {
} throw new IllegalArgumentException("Argument out of range"
return (int) i; + " [0x0-0x40000000]. Argument was: " + x);
} }
x--;
/** x |= x >> 1;
* Get the value that is equal or higher than this value, and that is a x |= x >> 2;
* power of two. x |= x >> 4;
* x |= x >> 8;
* @param x the original value x |= x >> 16;
* @return the next power of two value return ++x;
*/
public static long nextPowerOf2(long x) {
long i = 1;
while (i < x && i < (Long.MAX_VALUE / 2)) {
i += i;
}
return i;
} }
/** /**
...@@ -260,30 +254,6 @@ public class MathUtils { ...@@ -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. * Get a cryptographically secure pseudo random long value.
* *
......
...@@ -10,7 +10,6 @@ import java.sql.SQLException; ...@@ -10,7 +10,6 @@ import java.sql.SQLException;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.util.MathUtils;
/** /**
* Implementation of the BYTE data type. * Implementation of the BYTE data type.
...@@ -106,7 +105,7 @@ public class ValueByte extends Value { ...@@ -106,7 +105,7 @@ public class ValueByte extends Value {
@Override @Override
protected int compareSecure(Value o, CompareMode mode) { protected int compareSecure(Value o, CompareMode mode) {
ValueByte v = (ValueByte) o; ValueByte v = (ValueByte) o;
return MathUtils.compareInt(value, v.value); return Integer.compare(value, v.value);
} }
@Override @Override
......
...@@ -12,7 +12,6 @@ import java.sql.SQLException; ...@@ -12,7 +12,6 @@ import java.sql.SQLException;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.util.DateTimeUtils; import org.h2.util.DateTimeUtils;
import org.h2.util.MathUtils;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
/** /**
...@@ -121,7 +120,7 @@ public class ValueDate extends Value { ...@@ -121,7 +120,7 @@ public class ValueDate extends Value {
@Override @Override
protected int compareSecure(Value o, CompareMode mode) { protected int compareSecure(Value o, CompareMode mode) {
return MathUtils.compareLong(dateValue, ((ValueDate) o).dateValue); return Long.compare(dateValue, ((ValueDate) o).dateValue);
} }
@Override @Override
......
...@@ -8,7 +8,6 @@ package org.h2.value; ...@@ -8,7 +8,6 @@ package org.h2.value;
import java.util.Locale; import java.util.Locale;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.util.MathUtils;
public class ValueEnum extends ValueEnumBase { public class ValueEnum extends ValueEnumBase {
private static enum Validation { private static enum Validation {
...@@ -57,7 +56,7 @@ public class ValueEnum extends ValueEnumBase { ...@@ -57,7 +56,7 @@ public class ValueEnum extends ValueEnumBase {
@Override @Override
protected int compareSecure(final Value v, final CompareMode mode) { 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; ...@@ -7,7 +7,6 @@ package org.h2.value;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
import org.h2.util.MathUtils;
/** /**
* Base implementation of the ENUM data type. * Base implementation of the ENUM data type.
...@@ -35,7 +34,7 @@ public class ValueEnumBase extends Value { ...@@ -35,7 +34,7 @@ public class ValueEnumBase extends Value {
@Override @Override
protected int compareSecure(final Value v, final CompareMode mode) { protected int compareSecure(final Value v, final CompareMode mode) {
return MathUtils.compareInt(getInt(), v.getInt()); return Integer.compare(getInt(), v.getInt());
} }
@Override @Override
......
...@@ -10,7 +10,6 @@ import java.sql.SQLException; ...@@ -10,7 +10,6 @@ import java.sql.SQLException;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.util.MathUtils;
/** /**
* Implementation of the INT data type. * Implementation of the INT data type.
...@@ -140,7 +139,7 @@ public class ValueInt extends Value { ...@@ -140,7 +139,7 @@ public class ValueInt extends Value {
@Override @Override
protected int compareSecure(Value o, CompareMode mode) { protected int compareSecure(Value o, CompareMode mode) {
ValueInt v = (ValueInt) o; ValueInt v = (ValueInt) o;
return MathUtils.compareInt(value, v.value); return Integer.compare(value, v.value);
} }
@Override @Override
......
...@@ -12,7 +12,6 @@ import java.sql.SQLException; ...@@ -12,7 +12,6 @@ import java.sql.SQLException;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.util.MathUtils;
/** /**
* Implementation of the BIGINT data type. * Implementation of the BIGINT data type.
...@@ -171,7 +170,7 @@ public class ValueLong extends Value { ...@@ -171,7 +170,7 @@ public class ValueLong extends Value {
@Override @Override
protected int compareSecure(Value o, CompareMode mode) { protected int compareSecure(Value o, CompareMode mode) {
ValueLong v = (ValueLong) o; ValueLong v = (ValueLong) o;
return MathUtils.compareLong(value, v.value); return Long.compare(value, v.value);
} }
@Override @Override
......
...@@ -10,7 +10,6 @@ import java.sql.SQLException; ...@@ -10,7 +10,6 @@ import java.sql.SQLException;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.util.MathUtils;
/** /**
* Implementation of the SMALLINT data type. * Implementation of the SMALLINT data type.
...@@ -106,7 +105,7 @@ public class ValueShort extends Value { ...@@ -106,7 +105,7 @@ public class ValueShort extends Value {
@Override @Override
protected int compareSecure(Value o, CompareMode mode) { protected int compareSecure(Value o, CompareMode mode) {
ValueShort v = (ValueShort) o; ValueShort v = (ValueShort) o;
return MathUtils.compareInt(value, v.value); return Integer.compare(value, v.value);
} }
@Override @Override
......
...@@ -11,7 +11,6 @@ import java.sql.Time; ...@@ -11,7 +11,6 @@ import java.sql.Time;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.util.DateTimeUtils; import org.h2.util.DateTimeUtils;
import org.h2.util.MathUtils;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
/** /**
...@@ -129,7 +128,7 @@ public class ValueTime extends Value { ...@@ -129,7 +128,7 @@ public class ValueTime extends Value {
@Override @Override
protected int compareSecure(Value o, CompareMode mode) { protected int compareSecure(Value o, CompareMode mode) {
return MathUtils.compareLong(nanos, ((ValueTime) o).nanos); return Long.compare(nanos, ((ValueTime) o).nanos);
} }
@Override @Override
......
...@@ -16,7 +16,6 @@ import org.h2.api.ErrorCode; ...@@ -16,7 +16,6 @@ import org.h2.api.ErrorCode;
import org.h2.engine.Mode; import org.h2.engine.Mode;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.util.DateTimeUtils; import org.h2.util.DateTimeUtils;
import org.h2.util.MathUtils;
/** /**
* Implementation of the TIMESTAMP data type. * Implementation of the TIMESTAMP data type.
...@@ -310,11 +309,11 @@ public class ValueTimestamp extends Value { ...@@ -310,11 +309,11 @@ public class ValueTimestamp extends Value {
@Override @Override
protected int compareSecure(Value o, CompareMode mode) { protected int compareSecure(Value o, CompareMode mode) {
ValueTimestamp t = (ValueTimestamp) o; ValueTimestamp t = (ValueTimestamp) o;
int c = MathUtils.compareLong(dateValue, t.dateValue); int c = Long.compare(dateValue, t.dateValue);
if (c != 0) { if (c != 0) {
return c; return c;
} }
return MathUtils.compareLong(timeNanos, t.timeNanos); return Long.compare(timeNanos, t.timeNanos);
} }
@Override @Override
......
...@@ -14,7 +14,6 @@ import org.h2.api.ErrorCode; ...@@ -14,7 +14,6 @@ import org.h2.api.ErrorCode;
import org.h2.api.TimestampWithTimeZone; import org.h2.api.TimestampWithTimeZone;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.util.DateTimeUtils; import org.h2.util.DateTimeUtils;
import org.h2.util.MathUtils;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
/** /**
...@@ -310,14 +309,14 @@ public class ValueTimestampTimeZone extends Value { ...@@ -310,14 +309,14 @@ public class ValueTimestampTimeZone extends Value {
b -= t.timeZoneOffsetMins; b -= t.timeZoneOffsetMins;
// compare date // compare date
int c = MathUtils.compareLong(a, b); int c = Long.compare(a, b);
if (c != 0) { if (c != 0) {
return c; return c;
} }
// compare time // compare time
long na = timeNanos - (ma * 1000L * 1000L * 1000L * 60L); long na = timeNanos - (ma * 1000L * 1000L * 1000L * 60L);
long nb = t.timeNanos - (mb * 1000L * 1000L * 1000L * 60L); long nb = t.timeNanos - (mb * 1000L * 1000L * 1000L * 60L);
return MathUtils.compareLong(na, nb); return Long.compare(na, nb);
} }
@Override @Override
......
...@@ -159,7 +159,7 @@ public class ValueUuid extends Value { ...@@ -159,7 +159,7 @@ public class ValueUuid extends Value {
} }
ValueUuid v = (ValueUuid) o; ValueUuid v = (ValueUuid) o;
if (high == v.high) { if (high == v.high) {
return MathUtils.compareLong(low, v.low); return Long.compare(low, v.low);
} }
return high > v.high ? 1 : -1; return high > v.high ? 1 : -1;
} }
......
...@@ -113,10 +113,18 @@ public class TestAlter extends TestBase { ...@@ -113,10 +113,18 @@ public class TestAlter extends TestBase {
} }
private void testAlterTableDropMultipleColumns() throws SQLException { private void testAlterTableDropMultipleColumns() throws SQLException {
stat.execute("create table test(id int, name varchar, name2 varchar)"); stat.execute("create table test(id int, b varchar, c int, d int)");
stat.execute("alter table test drop column name, name2"); stat.execute("alter table test drop column b, c");
stat.execute("alter table test drop d");
stat.execute("drop table test"); 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)"); stat.execute("create table test(id int, name varchar, name2 varchar)");
assertThrows(ErrorCode.CANNOT_DROP_LAST_COLUMN, stat). assertThrows(ErrorCode.CANNOT_DROP_LAST_COLUMN, stat).
execute("alter table test drop column id, name, name2"); execute("alter table test drop column id, name, name2");
...@@ -209,6 +217,8 @@ public class TestAlter extends TestBase { ...@@ -209,6 +217,8 @@ public class TestAlter extends TestBase {
stat.execute("drop table t"); stat.execute("drop table t");
} }
// column and field names must be upper-case due to getMetaData sensitivity // column and field names must be upper-case due to getMetaData sensitivity
private void testAlterTableAddMultipleColumnsBefore() throws SQLException { private void testAlterTableAddMultipleColumnsBefore() throws SQLException {
stat.execute("create table T(X varchar)"); stat.execute("create table T(X varchar)");
...@@ -304,7 +314,7 @@ public class TestAlter extends TestBase { ...@@ -304,7 +314,7 @@ public class TestAlter extends TestBase {
// This failed in v1.4.196 // This failed in v1.4.196
stat.execute("create table T (C int not null)"); stat.execute("create table T (C int not null)");
stat.execute("alter table T modify C null"); // Silently corrupted column C 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"); stat.execute("drop table T");
} }
} }
...@@ -150,18 +150,6 @@ public class TestCases extends TestBase { ...@@ -150,18 +150,6 @@ public class TestCases extends TestBase {
"foreign key(a_id) references a(id)"); "foreign key(a_id) references a(id)");
stat.execute("update a set x=200"); stat.execute("update a set x=200");
stat.execute("drop table if exists a, b"); 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(); conn.close();
} }
......
...@@ -34,18 +34,81 @@ public class TestCompatibilityOracle extends TestBase { ...@@ -34,18 +34,81 @@ public class TestCompatibilityOracle extends TestBase {
@Override @Override
public void test() throws Exception { public void test() throws Exception {
testNotNullSyntax();
testTreatEmptyStringsAsNull(); testTreatEmptyStringsAsNull();
testDecimalScale(); testDecimalScale();
testPoundSymbolInColumnName(); testPoundSymbolInColumnName();
testToDate(); testToDate();
testForbidEmptyInClause(); testForbidEmptyInClause();
testSpecialTypes();
} }
private void testTreatEmptyStringsAsNull() throws SQLException { private void testNotNullSyntax() throws SQLException {
deleteDb("oracle"); deleteDb("oracle");
Connection conn = getConnection("oracle;MODE=Oracle"); Connection conn = getConnection("oracle;MODE=Oracle");
Statement stat = conn.createStatement(); 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("CREATE TABLE A (ID NUMBER, X VARCHAR2(1))");
stat.execute("INSERT INTO A VALUES (1, 'a')"); stat.execute("INSERT INTO A VALUES (1, 'a')");
stat.execute("INSERT INTO A VALUES (2, '')"); stat.execute("INSERT INTO A VALUES (2, '')");
......
...@@ -13,6 +13,8 @@ import java.sql.SQLException; ...@@ -13,6 +13,8 @@ import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.Properties; import java.util.Properties;
/** /**
* Tests the client info * Tests the client info
*/ */
...@@ -35,6 +37,7 @@ public class TestConnection extends TestBase { ...@@ -35,6 +37,7 @@ public class TestConnection extends TestBase {
testSetSupportedClientInfoProperties(); testSetSupportedClientInfoProperties();
testSetUnsupportedClientInfoProperties(); testSetUnsupportedClientInfoProperties();
testSetInternalProperty(); testSetInternalProperty();
testSetInternalPropertyToInitialValue();
testSetGetSchema(); testSetGetSchema();
} }
...@@ -48,6 +51,29 @@ public class TestConnection extends TestBase { ...@@ -48,6 +51,29 @@ public class TestConnection extends TestBase {
conn.close(); 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 { private void testSetUnsupportedClientInfoProperties() throws SQLException {
Connection conn = getConnection("clientInfo"); Connection conn = getConnection("clientInfo");
......
...@@ -11,8 +11,6 @@ import java.io.InputStream; ...@@ -11,8 +11,6 @@ import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.LineNumberReader; import java.io.LineNumberReader;
import java.io.PrintStream; import java.io.PrintStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
...@@ -79,7 +77,10 @@ public class TestScript extends TestBase { ...@@ -79,7 +77,10 @@ public class TestScript extends TestBase {
return; return;
} }
reconnectOften = !config.memory && config.big; reconnectOften = !config.memory && config.big;
testScript("testScript.sql"); testScript("testScript.sql");
testScript("altertable-index-reuse.sql");
testScript("query-optimisations.sql");
testScript("commands-dml-script.sql"); testScript("commands-dml-script.sql");
testScript("commands-dml-create-view.sql"); testScript("commands-dml-create-view.sql");
for (String s : new String[] { "array", "bigint", "binary", "blob", 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'; ...@@ -48,6 +48,12 @@ select rank from card where suit = 'diamonds';
> ---- > ----
> 8 > 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 --- ENUM integer-based operations
select rank from card where suit = 1; 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 ...@@ -2705,13 +2705,13 @@ create memory table orders ( orderid varchar(10), name varchar(20), customer_id
> ok > ok
select * from information_schema.columns where table_name = 'ORDERS'; 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 > 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 > 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 > 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 > 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 > 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 > 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 > rows: 5
drop table orders; drop table orders;
......
...@@ -18,6 +18,7 @@ import java.sql.ResultSet; ...@@ -18,6 +18,7 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Savepoint; import java.sql.Savepoint;
import java.sql.Statement; import java.sql.Statement;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.engine.Constants; import org.h2.engine.Constants;
...@@ -85,6 +86,7 @@ public class TestMVTableEngine extends TestBase { ...@@ -85,6 +86,7 @@ public class TestMVTableEngine extends TestBase {
testDataTypes(); testDataTypes();
testLocking(); testLocking();
testSimple(); testSimple();
testReverseDeletePerformance();
} }
private void testLobCopy() throws Exception { private void testLobCopy() throws Exception {
...@@ -1478,4 +1480,32 @@ public class TestMVTableEngine extends TestBase { ...@@ -1478,4 +1480,32 @@ public class TestMVTableEngine extends TestBase {
conn.close(); 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 { ...@@ -35,7 +35,7 @@ public class TestAutoReconnect extends TestBase {
TestBase.createCaller().init().test(); TestBase.createCaller().init().test();
} }
private void restart() throws SQLException { private void restart() throws SQLException, InterruptedException {
if (autoServer) { if (autoServer) {
if (connServer != null) { if (connServer != null) {
connServer.createStatement().execute("SHUTDOWN"); connServer.createStatement().execute("SHUTDOWN");
...@@ -45,6 +45,7 @@ public class TestAutoReconnect extends TestBase { ...@@ -45,6 +45,7 @@ public class TestAutoReconnect extends TestBase {
connServer = getConnection(url); connServer = getConnection(url);
} else { } else {
server.stop(); server.stop();
Thread.sleep(100); // try to prevent "port may be in use" error
server.start(); server.start();
} }
} }
......
...@@ -5,10 +5,7 @@ ...@@ -5,10 +5,7 @@
*/ */
package org.h2.test.unit; package org.h2.test.unit;
import java.math.BigInteger;
import java.util.Random;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.test.utils.AssertThrows;
import org.h2.util.MathUtils; import org.h2.util.MathUtils;
/** /**
...@@ -28,8 +25,7 @@ public class TestMathUtils extends TestBase { ...@@ -28,8 +25,7 @@ public class TestMathUtils extends TestBase {
@Override @Override
public void test() { public void test() {
testRandom(); testRandom();
testReverse(); testNextPowerOf2Int();
testFactorial();
} }
private void testRandom() { private void testRandom() {
...@@ -53,78 +49,17 @@ public class TestMathUtils extends TestBase { ...@@ -53,78 +49,17 @@ public class TestMathUtils extends TestBase {
assertTrue(data.length > 10); assertTrue(data.length > 10);
} }
private void testReverse() { private void testNextPowerOf2Int() {
assertEquals(Integer.reverse(0), Integer.reverse(0)); // the largest power of two that fits into an integer
assertEquals(Integer.reverse(Integer.MAX_VALUE), final int LARGEST_POW2 = 0x40000000;
Integer.reverse(Integer.MAX_VALUE)); int[] testValues = { 0, 1, 2, 3, 4, 12, 17, 500, 1023,
assertEquals(Integer.reverse(Integer.MIN_VALUE), LARGEST_POW2-500, LARGEST_POW2 };
Integer.reverse(Integer.MIN_VALUE)); int[] resultValues = { 1, 1, 2, 4, 4, 16, 32, 512, 1024,
assertEquals(Long.reverse(0), Long.reverse(0L)); LARGEST_POW2, LARGEST_POW2 };
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 testFactorial() { for (int i = 0; i < testValues.length; i++) {
new AssertThrows(IllegalArgumentException.class) { @Override assertEquals(resultValues[i], MathUtils.nextPowerOf2(testValues[i]));
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);
} }
return result;
} }
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论