提交 19d93261 authored 作者: Owner's avatar Owner

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

...@@ -907,8 +907,8 @@ DROP SEQUENCE SEQ_ID ...@@ -907,8 +907,8 @@ DROP SEQUENCE SEQ_ID
DROP TABLE [ IF EXISTS ] tableName [,...] [ RESTRICT | CASCADE ] DROP TABLE [ IF EXISTS ] tableName [,...] [ RESTRICT | CASCADE ]
"," ","
Drops an existing table, or a list of tables. Drops an existing table, or a list of tables.
The command will fail if dependent views exist and the RESTRICT clause is used (the default). The command will fail if dependent objects exist and the RESTRICT clause is used (the default).
All dependent views are dropped as well if the CASCADE clause is used. All dependent views and constraints are dropped as well if the CASCADE clause is used.
This command commits an open transaction in this connection. This command commits an open transaction in this connection.
"," ","
DROP TABLE TEST DROP TABLE TEST
......
File mode changed from 100755 to 100644
...@@ -1200,7 +1200,7 @@ public class Parser { ...@@ -1200,7 +1200,7 @@ public class Parser {
// build and prepare the targetMatchQuery ready to test each rows // build and prepare the targetMatchQuery ready to test each rows
// existence in the target table (using source row to match) // existence in the target table (using source row to match)
StringBuffer targetMatchQuerySQL = new StringBuffer( StringBuilder targetMatchQuerySQL = new StringBuilder(
"SELECT _ROWID_ FROM " + command.getTargetTable().getName()); "SELECT _ROWID_ FROM " + command.getTargetTable().getName());
if (command.getTargetTableFilter().getTableAlias() != null) { if (command.getTargetTableFilter().getTableAlias() != null) {
targetMatchQuerySQL.append( targetMatchQuerySQL.append(
......
File mode changed from 100755 to 100644
...@@ -5,9 +5,12 @@ ...@@ -5,9 +5,12 @@
*/ */
package org.h2.command.ddl; package org.h2.command.ddl;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.command.CommandInterface; import org.h2.command.CommandInterface;
import org.h2.constraint.Constraint;
import org.h2.constraint.ConstraintReferential; import org.h2.constraint.ConstraintReferential;
import org.h2.engine.Database; import org.h2.engine.Database;
import org.h2.engine.Right; import org.h2.engine.Right;
...@@ -73,15 +76,29 @@ public class DropTable extends SchemaCommand { ...@@ -73,15 +76,29 @@ public class DropTable extends SchemaCommand {
throw DbException.get(ErrorCode.CANNOT_DROP_TABLE_1, tableName); throw DbException.get(ErrorCode.CANNOT_DROP_TABLE_1, tableName);
} }
if (dropAction == ConstraintReferential.RESTRICT) { if (dropAction == ConstraintReferential.RESTRICT) {
StatementBuilder buff = new StatementBuilder();
CopyOnWriteArrayList<TableView> dependentViews = table.getDependentViews(); CopyOnWriteArrayList<TableView> dependentViews = table.getDependentViews();
if (dependentViews != null && dependentViews.size() > 0) { if (dependentViews != null && dependentViews.size() > 0) {
StatementBuilder buff = new StatementBuilder();
for (TableView v : dependentViews) { for (TableView v : dependentViews) {
buff.appendExceptFirst(", "); buff.appendExceptFirst(", ");
buff.append(v.getName()); buff.append(v.getName());
} }
throw DbException.get(ErrorCode.CANNOT_DROP_2, tableName, buff.toString());
} }
if (session.getDatabase()
.getSettings().standardDropTableRestrict) {
final List<Constraint> constraints = table.getConstraints();
if (constraints != null && constraints.size() > 0) {
for (Constraint c : constraints) {
if (c.getTable() != table) {
buff.appendExceptFirst(", ");
buff.append(c.getName());
}
}
}
}
if (buff.length() > 0)
throw DbException.get(ErrorCode.CANNOT_DROP_2, tableName, buff.toString());
} }
table.lock(session, true, true); table.lock(session, true, true);
} }
......
File mode changed from 100755 to 100644
...@@ -350,6 +350,17 @@ public class DbSettings extends SettingsBase { ...@@ -350,6 +350,17 @@ public class DbSettings extends SettingsBase {
*/ */
public final boolean multiThreaded = get("MULTI_THREADED", false); public final boolean multiThreaded = get("MULTI_THREADED", false);
/**
* Database setting <code>STANDARD_DROP_TABLE_RESTRICT</code> (default:
* false).<br />
* <code>true</code> if DROP TABLE RESTRICT should fail if there's any
* foreign key referencing the table to be dropped. <code>false</code> if
* foreign keys referencing the table to be dropped should be silently
* dropped as well.
*/
public final boolean standardDropTableRestrict = get(
"STANDARD_DROP_TABLE_RESTRICT", false);
private DbSettings(HashMap<String, String> s) { private DbSettings(HashMap<String, String> s) {
super(s); super(s);
} }
......
File mode changed from 100755 to 100644
...@@ -3810,10 +3810,10 @@ public class JdbcResultSet extends TraceObject implements ResultSet, JdbcResultS ...@@ -3810,10 +3810,10 @@ public class JdbcResultSet extends TraceObject implements ResultSet, JdbcResultS
int id = getNextId(TraceObject.ARRAY); int id = getNextId(TraceObject.ARRAY);
return type.cast(value == ValueNull.INSTANCE ? null : new JdbcArray(conn, value, id)); return type.cast(value == ValueNull.INSTANCE ? null : new JdbcArray(conn, value, id));
} else if (type == Blob.class) { } else if (type == Blob.class) {
int id = getNextId(TraceObject.ARRAY); int id = getNextId(TraceObject.BLOB);
return type.cast(value == ValueNull.INSTANCE ? null : new JdbcBlob(conn, value, id)); return type.cast(value == ValueNull.INSTANCE ? null : new JdbcBlob(conn, value, id));
} else if (type == Clob.class) { } else if (type == Clob.class) {
int id = getNextId(TraceObject.ARRAY); int id = getNextId(TraceObject.CLOB);
return type.cast(value == ValueNull.INSTANCE ? null : new JdbcClob(conn, value, id)); return type.cast(value == ValueNull.INSTANCE ? null : new JdbcClob(conn, value, id));
} else if (type == TimestampWithTimeZone.class) { } else if (type == TimestampWithTimeZone.class) {
return type.cast(value.getObject()); return type.cast(value.getObject());
......
/*
* 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
*/
package org.h2.store.fs;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
/**
* Fake file channel to use by in-memory and ZIP file systems.
*/
public class FakeFileChannel extends FileChannel {
@Override
protected void implCloseChannel() throws IOException {
throw new IOException();
}
@Override
public FileLock lock(long position, long size, boolean shared) throws IOException {
throw new IOException();
}
@Override
public MappedByteBuffer map(MapMode mode, long position, long size) throws IOException {
throw new IOException();
}
@Override
public long position() throws IOException {
throw new IOException();
}
@Override
public FileChannel position(long newPosition) throws IOException {
throw new IOException();
}
@Override
public int read(ByteBuffer dst) throws IOException {
throw new IOException();
}
@Override
public int read(ByteBuffer dst, long position) throws IOException {
throw new IOException();
}
@Override
public long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
throw new IOException();
}
@Override
public long size() throws IOException {
throw new IOException();
}
@Override
public long transferFrom(ReadableByteChannel src, long position, long count) throws IOException {
throw new IOException();
}
@Override
public long transferTo(long position, long count, WritableByteChannel target) throws IOException {
throw new IOException();
}
@Override
public FileChannel truncate(long size) throws IOException {
throw new IOException();
}
@Override
public FileLock tryLock(long position, long size, boolean shared) throws IOException {
throw new IOException();
}
@Override
public int write(ByteBuffer src) throws IOException {
throw new IOException();
}
@Override
public int write(ByteBuffer src, long position) throws IOException {
throw new IOException();
}
@Override
public long write(ByteBuffer[] srcs, int offset, int len) throws IOException {
throw new IOException();
}
@Override
public void force(boolean metaData) throws IOException {
throw new IOException();
}
}
\ No newline at end of file
...@@ -392,8 +392,7 @@ class FileMem extends FileBase { ...@@ -392,8 +392,7 @@ class FileMem extends FileBase {
} }
} }
// cast to FileChannel to avoid JDK 1.7 ambiguity FileLock lock = new FileLock(new FakeFileChannel(), position, size, shared) {
FileLock lock = new FileLock((FileChannel) null, position, size, shared) {
@Override @Override
public boolean isValid() { public boolean isValid() {
......
...@@ -386,8 +386,7 @@ class FileNioMem extends FileBase { ...@@ -386,8 +386,7 @@ class FileNioMem extends FileBase {
} }
} }
// cast to FileChannel to avoid JDK 1.7 ambiguity FileLock lock = new FileLock(new FakeFileChannel(), position, size, shared) {
FileLock lock = new FileLock((FileChannel) null, position, size, shared) {
@Override @Override
public boolean isValid() { public boolean isValid() {
......
...@@ -354,8 +354,7 @@ class FileZip extends FileBase { ...@@ -354,8 +354,7 @@ class FileZip extends FileBase {
public synchronized FileLock tryLock(long position, long size, public synchronized FileLock tryLock(long position, long size,
boolean shared) throws IOException { boolean shared) throws IOException {
if (shared) { if (shared) {
// cast to FileChannel to avoid JDK 1.7 ambiguity return new FileLock(new FakeFileChannel(), position, size, shared) {
return new FileLock((FileChannel) null, position, size, shared) {
@Override @Override
public boolean isValid() { public boolean isValid() {
......
...@@ -788,7 +788,7 @@ public class TableFilter implements ColumnResolver { ...@@ -788,7 +788,7 @@ public class TableFilter implements ColumnResolver {
} }
} }
if (nestedJoin != null) { if (nestedJoin != null) {
StringBuffer buffNested = new StringBuffer(); StringBuilder buffNested = new StringBuilder();
TableFilter n = nestedJoin; TableFilter n = nestedJoin;
do { do {
buffNested.append(n.getPlanSQL(n != nestedJoin)); buffNested.append(n.getPlanSQL(n != nestedJoin));
......
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
...@@ -271,7 +271,6 @@ public class IOUtils { ...@@ -271,7 +271,6 @@ public class IOUtils {
public static void closeSilently(Writer writer) { public static void closeSilently(Writer writer) {
if (writer != null) { if (writer != null) {
try { try {
writer.flush();
writer.close(); writer.close();
} catch (Exception e) { } catch (Exception e) {
// ignore // ignore
......
...@@ -27,22 +27,10 @@ import org.h2.value.ValueTimestampTimeZone; ...@@ -27,22 +27,10 @@ import org.h2.value.ValueTimestampTimeZone;
* <p>This class is implemented using reflection so that it compiles on * <p>This class is implemented using reflection so that it compiles on
* Java 7 as well.</p> * Java 7 as well.</p>
* *
* <p>For LocalDate and LocalTime the conversion methods provided by * <p>Custom conversion methods between H2 internal values and JSR-310 classes
* the JDK are used. For OffsetDateTime a custom conversion method is * are used without intermediate conversions to java.sql classes. Direct
* used because it has no equivalent in JDBC. For LocalDateTime a * conversion is simpler, faster, and it does not inherit limitations and
* custom conversion method is used instead of the one provided by the * issues from java.sql classes and conversion methods provided by JDK.</p>
* JDK as well.</p>
*
* <p>Using the JDK provided conversion method for LocalDateTime would
* introduces some errors in edge cases. Consider the following case:
* at 2016-03-27 02:00 in Europe/Berlin the clocks were set to
* 2016-03-27 03:00. This means that 2016-03-27 02:15 does not exist in
* Europe/Berlin. Unfortunately java.sql.Timestamp is in the the time
* zone of the JVM. That means if you run a JVM with the time zone
* Europe/Berlin then the SQL value 'TIMESTAMP 2016-03-27 02:15:00' can
* not be represented. java.time.LocalDateTime does not have these
* limitations but if we convert through java.sql.Timestamp we inherit
* its limitations. Therefore that conversion must be avoided.</p>
* *
* <p>Once the driver requires Java 8 all the reflection can be removed.</p> * <p>Once the driver requires Java 8 all the reflection can be removed.</p>
*/ */
...@@ -85,8 +73,8 @@ public class LocalDateTimeUtils { ...@@ -85,8 +73,8 @@ public class LocalDateTimeUtils {
private static final Method LOCAL_DATE_TIME_PLUS_NANOS; private static final Method LOCAL_DATE_TIME_PLUS_NANOS;
// java.time.LocalDateTime#toLocalDate() // java.time.LocalDateTime#toLocalDate()
private static final Method LOCAL_DATE_TIME_TO_LOCAL_DATE; private static final Method LOCAL_DATE_TIME_TO_LOCAL_DATE;
// java.time.LocalDateTime#truncatedTo(TemporalUnit) // java.time.LocalDateTime#toLocalTime()
private static final Method LOCAL_DATE_TIME_TRUNCATED_TO; private static final Method LOCAL_DATE_TIME_TO_LOCAL_TIME;
// java.time.LocalDateTime#parse(CharSequence) // java.time.LocalDateTime#parse(CharSequence)
private static final Method LOCAL_DATE_TIME_PARSE; private static final Method LOCAL_DATE_TIME_PARSE;
...@@ -105,17 +93,8 @@ public class LocalDateTimeUtils { ...@@ -105,17 +93,8 @@ public class LocalDateTimeUtils {
// java.time.ZoneOffset#getTotalSeconds() // java.time.ZoneOffset#getTotalSeconds()
private static final Method ZONE_OFFSET_GET_TOTAL_SECONDS; private static final Method ZONE_OFFSET_GET_TOTAL_SECONDS;
// java.time.Duration#between(Temporal, Temporal)
private static final Method DURATION_BETWEEN;
// java.time.Duration#toNanos()
private static final Method DURATION_TO_NANOS;
// java.time.temporal.ChronoUnit#DAYS
private static final Object CHRONO_UNIT_DAYS;
private static final boolean IS_JAVA8_DATE_API_PRESENT; private static final boolean IS_JAVA8_DATE_API_PRESENT;
static { static {
LOCAL_DATE = tryGetClass("java.time.LocalDate"); LOCAL_DATE = tryGetClass("java.time.LocalDate");
LOCAL_TIME = tryGetClass("java.time.LocalTime"); LOCAL_TIME = tryGetClass("java.time.LocalTime");
...@@ -127,12 +106,6 @@ public class LocalDateTimeUtils { ...@@ -127,12 +106,6 @@ public class LocalDateTimeUtils {
ZONE_OFFSET != null; ZONE_OFFSET != null;
if (IS_JAVA8_DATE_API_PRESENT) { if (IS_JAVA8_DATE_API_PRESENT) {
Class<?> temporalUnit = getClass("java.time.temporal.TemporalUnit");
Class<?> chronoUnit = getClass("java.time.temporal.ChronoUnit");
Class<?> duration = getClass("java.time.Duration");
Class<?> temporal = getClass("java.time.temporal.Temporal");
LOCAL_TIME_OF_NANO = getMethod(LOCAL_TIME, "ofNanoOfDay", long.class); LOCAL_TIME_OF_NANO = getMethod(LOCAL_TIME, "ofNanoOfDay", long.class);
LOCAL_TIME_TO_NANO = getMethod(LOCAL_TIME, "toNanoOfDay"); LOCAL_TIME_TO_NANO = getMethod(LOCAL_TIME, "toNanoOfDay");
...@@ -150,7 +123,7 @@ public class LocalDateTimeUtils { ...@@ -150,7 +123,7 @@ public class LocalDateTimeUtils {
LOCAL_DATE_TIME_PLUS_NANOS = getMethod(LOCAL_DATE_TIME, "plusNanos", long.class); LOCAL_DATE_TIME_PLUS_NANOS = getMethod(LOCAL_DATE_TIME, "plusNanos", long.class);
LOCAL_DATE_TIME_TO_LOCAL_DATE = getMethod(LOCAL_DATE_TIME, "toLocalDate"); LOCAL_DATE_TIME_TO_LOCAL_DATE = getMethod(LOCAL_DATE_TIME, "toLocalDate");
LOCAL_DATE_TIME_TRUNCATED_TO = getMethod(LOCAL_DATE_TIME, "truncatedTo", temporalUnit); LOCAL_DATE_TIME_TO_LOCAL_TIME = getMethod(LOCAL_DATE_TIME, "toLocalTime");
LOCAL_DATE_TIME_PARSE = getMethod(LOCAL_DATE_TIME, "parse", CharSequence.class); LOCAL_DATE_TIME_PARSE = getMethod(LOCAL_DATE_TIME, "parse", CharSequence.class);
ZONE_OFFSET_OF_TOTAL_SECONDS = getMethod(ZONE_OFFSET, "ofTotalSeconds", int.class); ZONE_OFFSET_OF_TOTAL_SECONDS = getMethod(ZONE_OFFSET, "ofTotalSeconds", int.class);
...@@ -162,11 +135,6 @@ public class LocalDateTimeUtils { ...@@ -162,11 +135,6 @@ public class LocalDateTimeUtils {
OFFSET_DATE_TIME_PARSE = getMethod(OFFSET_DATE_TIME, "parse", CharSequence.class); OFFSET_DATE_TIME_PARSE = getMethod(OFFSET_DATE_TIME, "parse", CharSequence.class);
ZONE_OFFSET_GET_TOTAL_SECONDS = getMethod(ZONE_OFFSET, "getTotalSeconds"); ZONE_OFFSET_GET_TOTAL_SECONDS = getMethod(ZONE_OFFSET, "getTotalSeconds");
DURATION_BETWEEN = getMethod(duration, "between", temporal, temporal);
DURATION_TO_NANOS = getMethod(duration, "toNanos");
CHRONO_UNIT_DAYS = getFieldValue(chronoUnit, "DAYS");
} else { } else {
LOCAL_TIME_OF_NANO = null; LOCAL_TIME_OF_NANO = null;
LOCAL_TIME_TO_NANO = null; LOCAL_TIME_TO_NANO = null;
...@@ -179,7 +147,7 @@ public class LocalDateTimeUtils { ...@@ -179,7 +147,7 @@ public class LocalDateTimeUtils {
LOCAL_TIME_PARSE = null; LOCAL_TIME_PARSE = null;
LOCAL_DATE_TIME_PLUS_NANOS = null; LOCAL_DATE_TIME_PLUS_NANOS = null;
LOCAL_DATE_TIME_TO_LOCAL_DATE = null; LOCAL_DATE_TIME_TO_LOCAL_DATE = null;
LOCAL_DATE_TIME_TRUNCATED_TO = null; LOCAL_DATE_TIME_TO_LOCAL_TIME = null;
LOCAL_DATE_TIME_PARSE = null; LOCAL_DATE_TIME_PARSE = null;
ZONE_OFFSET_OF_TOTAL_SECONDS = null; ZONE_OFFSET_OF_TOTAL_SECONDS = null;
OFFSET_DATE_TIME_TO_LOCAL_DATE_TIME = null; OFFSET_DATE_TIME_TO_LOCAL_DATE_TIME = null;
...@@ -187,9 +155,6 @@ public class LocalDateTimeUtils { ...@@ -187,9 +155,6 @@ public class LocalDateTimeUtils {
OFFSET_DATE_TIME_OF_LOCAL_DATE_TIME_ZONE_OFFSET = null; OFFSET_DATE_TIME_OF_LOCAL_DATE_TIME_ZONE_OFFSET = null;
OFFSET_DATE_TIME_PARSE = null; OFFSET_DATE_TIME_PARSE = null;
ZONE_OFFSET_GET_TOTAL_SECONDS = null; ZONE_OFFSET_GET_TOTAL_SECONDS = null;
DURATION_BETWEEN = null;
DURATION_TO_NANOS = null;
CHRONO_UNIT_DAYS = null;
} }
} }
...@@ -310,15 +275,6 @@ public class LocalDateTimeUtils { ...@@ -310,15 +275,6 @@ public class LocalDateTimeUtils {
} }
} }
private static Class<?> getClass(String className) {
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
throw new IllegalStateException("Java 8 or later but class " +
className + " is missing", e);
}
}
private static Method getMethod(Class<?> clazz, String methodName, private static Method getMethod(Class<?> clazz, String methodName,
Class<?>... parameterTypes) { Class<?>... parameterTypes) {
try { try {
...@@ -330,15 +286,6 @@ public class LocalDateTimeUtils { ...@@ -330,15 +286,6 @@ public class LocalDateTimeUtils {
} }
} }
private static Object getFieldValue(Class<?> clazz, String fieldName) {
try {
return clazz.getField(fieldName).get(null);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new IllegalStateException("Java 8 or later but field " +
clazz.getName() + "#" + fieldName + " is missing", e);
}
}
/** /**
* Checks if the given class is LocalDate. * Checks if the given class is LocalDate.
* *
...@@ -523,7 +470,7 @@ public class LocalDateTimeUtils { ...@@ -523,7 +470,7 @@ public class LocalDateTimeUtils {
try { try {
Object localDate = LOCAL_DATE_TIME_TO_LOCAL_DATE.invoke(localDateTime); Object localDate = LOCAL_DATE_TIME_TO_LOCAL_DATE.invoke(localDateTime);
long dateValue = dateValueFromLocalDate(localDate); long dateValue = dateValueFromLocalDate(localDate);
long timeNanos = timeNanosFromLocalDate(localDateTime); long timeNanos = timeNanosFromLocalDateTime(localDateTime);
return ValueTimestamp.fromDateValueAndNanos(dateValue, timeNanos); return ValueTimestamp.fromDateValueAndNanos(dateValue, timeNanos);
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
throw DbException.convert(e); throw DbException.convert(e);
...@@ -545,7 +492,7 @@ public class LocalDateTimeUtils { ...@@ -545,7 +492,7 @@ public class LocalDateTimeUtils {
Object zoneOffset = OFFSET_DATE_TIME_GET_OFFSET.invoke(offsetDateTime); Object zoneOffset = OFFSET_DATE_TIME_GET_OFFSET.invoke(offsetDateTime);
long dateValue = dateValueFromLocalDate(localDate); long dateValue = dateValueFromLocalDate(localDate);
long timeNanos = timeNanosFromLocalDate(localDateTime); long timeNanos = timeNanosFromLocalDateTime(localDateTime);
short timeZoneOffsetMins = zoneOffsetToOffsetMinute(zoneOffset); short timeZoneOffsetMins = zoneOffsetToOffsetMinute(zoneOffset);
return ValueTimestampTimeZone.fromDateValueAndNanos(dateValue, return ValueTimestampTimeZone.fromDateValueAndNanos(dateValue,
timeNanos, timeZoneOffsetMins); timeNanos, timeZoneOffsetMins);
...@@ -564,11 +511,10 @@ public class LocalDateTimeUtils { ...@@ -564,11 +511,10 @@ public class LocalDateTimeUtils {
return DateTimeUtils.dateValue(year, month, day); return DateTimeUtils.dateValue(year, month, day);
} }
private static long timeNanosFromLocalDate(Object localDateTime) private static long timeNanosFromLocalDateTime(Object localDateTime)
throws IllegalAccessException, InvocationTargetException { throws IllegalAccessException, InvocationTargetException {
Object midnight = LOCAL_DATE_TIME_TRUNCATED_TO.invoke(localDateTime, CHRONO_UNIT_DAYS); Object localTime = LOCAL_DATE_TIME_TO_LOCAL_TIME.invoke(localDateTime);
Object duration = DURATION_BETWEEN.invoke(null, midnight, localDateTime); return (Long) LOCAL_TIME_TO_NANO.invoke(localTime);
return (Long) DURATION_TO_NANOS.invoke(duration);
} }
private static short zoneOffsetToOffsetMinute(Object zoneOffset) private static short zoneOffsetToOffsetMinute(Object zoneOffset)
......
...@@ -197,7 +197,7 @@ public class OsgiDataSourceFactory implements DataSourceFactory { ...@@ -197,7 +197,7 @@ public class OsgiDataSourceFactory implements DataSourceFactory {
} }
// Setting URL // Setting URL
StringBuffer connectionUrl = new StringBuffer(); StringBuilder connectionUrl = new StringBuilder();
if (p.containsKey(DataSourceFactory.JDBC_URL)) { if (p.containsKey(DataSourceFactory.JDBC_URL)) {
// Use URL if specified // Use URL if specified
connectionUrl.append(p.remove(DataSourceFactory.JDBC_URL)); connectionUrl.append(p.remove(DataSourceFactory.JDBC_URL));
......
...@@ -19,7 +19,6 @@ import java.sql.Timestamp; ...@@ -19,7 +19,6 @@ import java.sql.Timestamp;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.store.fs.FileUtils; import org.h2.store.fs.FileUtils;
...@@ -54,6 +53,7 @@ public class TestCases extends TestBase { ...@@ -54,6 +53,7 @@ public class TestCases extends TestBase {
testGroupSubquery(); testGroupSubquery();
testCountDistinctNotNull(); testCountDistinctNotNull();
testDependencies(); testDependencies();
testDropTable();
testConvertType(); testConvertType();
testSortedSelect(); testSortedSelect();
testMaxMemoryRows(); testMaxMemoryRows();
...@@ -150,7 +150,7 @@ public class TestCases extends TestBase { ...@@ -150,7 +150,7 @@ public class TestCases extends TestBase {
stat.execute("alter table b add constraint x " + stat.execute("alter table b add constraint x " +
"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 cascade");
conn.close(); conn.close();
} }
...@@ -278,6 +278,70 @@ public class TestCases extends TestBase { ...@@ -278,6 +278,70 @@ public class TestCases extends TestBase {
conn.close(); conn.close();
} }
private void testDropTable() throws SQLException {
trace("testDropTable");
final boolean[] booleans = new boolean[] { true, false };
for (final boolean stdDropTableRestrict : booleans) {
for (final boolean restrict : booleans) {
testDropTableNoReference(stdDropTableRestrict, restrict);
testDropTableViewReference(stdDropTableRestrict, restrict);
testDropTableForeignKeyReference(stdDropTableRestrict, restrict);
}
}
}
private Statement createTable(final boolean stdDropTableRestrict) throws SQLException {
deleteDb("cases");
Connection conn = getConnection("cases;STANDARD_DROP_TABLE_RESTRICT=" + stdDropTableRestrict);
Statement stat = conn.createStatement();
stat.execute("create table test(id int)");
return stat;
}
private void dropTable(final boolean restrict, Statement stat, final boolean expectedDropSuccess)
throws SQLException {
assertThrows(expectedDropSuccess ? 0 : ErrorCode.CANNOT_DROP_2, stat)
.execute("drop table test " + (restrict ? "restrict" : "cascade"));
assertThrows(expectedDropSuccess ? ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1 : 0, stat).execute("select * from test");
}
private void testDropTableNoReference(final boolean stdDropTableRestrict, final boolean restrict)
throws SQLException {
Statement stat = createTable(stdDropTableRestrict);
// always succeed as there's no reference to the table
dropTable(restrict, stat, true);
stat.getConnection().close();
}
private void testDropTableViewReference(final boolean stdDropTableRestrict, final boolean restrict)
throws SQLException {
Statement stat = createTable(stdDropTableRestrict);
stat.execute("create view abc as select * from test");
// drop allowed only if cascade
final boolean expectedDropSuccess = !restrict;
dropTable(restrict, stat, expectedDropSuccess);
// missing view if the drop succeeded
assertThrows(expectedDropSuccess ? ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1 : 0, stat).execute("select * from abc");
stat.getConnection().close();
}
private void testDropTableForeignKeyReference(final boolean stdDropTableRestrict, final boolean restrict)
throws SQLException {
Statement stat = createTable(stdDropTableRestrict);
stat.execute("create table ref(id int, id_test int, foreign key (id_test) references test (id)) ");
// test table is empty, so the foreign key forces ref table to be also
// empty
assertThrows(ErrorCode.REFERENTIAL_INTEGRITY_VIOLATED_PARENT_MISSING_1, stat)
.execute("insert into ref values(1,2)");
// drop allowed if cascade or old style
final boolean expectedDropSuccess = !stdDropTableRestrict || !restrict;
dropTable(restrict, stat, expectedDropSuccess);
// insertion succeeds if the foreign key was dropped
assertThrows(expectedDropSuccess ? 0 : ErrorCode.REFERENTIAL_INTEGRITY_VIOLATED_PARENT_MISSING_1, stat)
.execute("insert into ref values(1,2)");
stat.getConnection().close();
}
private void testConvertType() throws SQLException { private void testConvertType() throws SQLException {
deleteDb("cases"); deleteDb("cases");
Connection conn = getConnection("cases"); Connection conn = getConnection("cases");
......
...@@ -1498,6 +1498,7 @@ public class TestFunctions extends TestBase implements AggregateFunction { ...@@ -1498,6 +1498,7 @@ public class TestFunctions extends TestBase implements AggregateFunction {
assertResult("1979-11-12 08:12:34.56", stat, "SELECT X FROM T"); assertResult("1979-11-12 08:12:34.56", stat, "SELECT X FROM T");
assertResult("-100-01-15 14:04:02.12", stat, "SELECT X FROM U"); assertResult("-100-01-15 14:04:02.12", stat, "SELECT X FROM U");
String expected = String.format("%tb", timestamp1979).toUpperCase(); String expected = String.format("%tb", timestamp1979).toUpperCase();
expected = stripTrailingPeriod(expected);
assertResult("12-" + expected + "-79 08.12.34.560000 AM", stat, assertResult("12-" + expected + "-79 08.12.34.560000 AM", stat,
"SELECT TO_CHAR(X) FROM T"); "SELECT TO_CHAR(X) FROM T");
assertResult("- / , . ; : text - /", stat, assertResult("- / , . ; : text - /", stat,
...@@ -1618,6 +1619,7 @@ public class TestFunctions extends TestBase implements AggregateFunction { ...@@ -1618,6 +1619,7 @@ public class TestFunctions extends TestBase implements AggregateFunction {
assertResult("11", stat, "SELECT TO_CHAR(X, 'mM') FROM T"); assertResult("11", stat, "SELECT TO_CHAR(X, 'mM') FROM T");
assertResult("11", stat, "SELECT TO_CHAR(X, 'mm') FROM T"); assertResult("11", stat, "SELECT TO_CHAR(X, 'mm') FROM T");
expected = String.format("%1$tb", timestamp1979); expected = String.format("%1$tb", timestamp1979);
expected = stripTrailingPeriod(expected);
expected = expected.substring(0, 1).toUpperCase() + expected.substring(1); expected = expected.substring(0, 1).toUpperCase() + expected.substring(1);
assertResult(expected.toUpperCase(), stat, assertResult(expected.toUpperCase(), stat,
"SELECT TO_CHAR(X, 'MON') FROM T"); "SELECT TO_CHAR(X, 'MON') FROM T");
...@@ -1672,6 +1674,14 @@ public class TestFunctions extends TestBase implements AggregateFunction { ...@@ -1672,6 +1674,14 @@ public class TestFunctions extends TestBase implements AggregateFunction {
conn.close(); conn.close();
} }
String stripTrailingPeriod(String expected) {
// CLDR provider appends period on some locales
int l = expected.length() - 1;
if (expected.charAt(l) == '.')
expected = expected.substring(0, l);
return expected;
}
private void testIfNull() throws SQLException { private void testIfNull() throws SQLException {
deleteDb("functions"); deleteDb("functions");
Connection conn = getConnection("functions"); Connection conn = getConnection("functions");
......
File mode changed from 100755 to 100644
...@@ -305,7 +305,7 @@ public class TestMergeUsing extends TestBase implements Trigger { ...@@ -305,7 +305,7 @@ public class TestMergeUsing extends TestBase implements Trigger {
+ expectedResultsSQL + " )"); + expectedResultsSQL + " )");
int rowCount = 0; int rowCount = 0;
StringBuffer diffBuffer = new StringBuffer(""); StringBuilder diffBuffer = new StringBuilder("");
while (rs.next()) { while (rs.next()) {
rowCount++; rowCount++;
diffBuffer.append("|"); diffBuffer.append("|");
...@@ -401,7 +401,7 @@ public class TestMergeUsing extends TestBase implements Trigger { ...@@ -401,7 +401,7 @@ public class TestMergeUsing extends TestBase implements Trigger {
} }
private String getCreateTriggerSQL() { private String getCreateTriggerSQL() {
StringBuffer buf = new StringBuffer(); StringBuilder buf = new StringBuilder();
buf.append("CREATE TRIGGER INS_BEFORE " + "BEFORE INSERT ON PARENT " buf.append("CREATE TRIGGER INS_BEFORE " + "BEFORE INSERT ON PARENT "
+ "FOR EACH ROW NOWAIT CALL \"" + getClass().getName() + "\";"); + "FOR EACH ROW NOWAIT CALL \"" + getClass().getName() + "\";");
buf.append("CREATE TRIGGER UPD_BEFORE " + "BEFORE UPDATE ON PARENT " buf.append("CREATE TRIGGER UPD_BEFORE " + "BEFORE UPDATE ON PARENT "
......
...@@ -9,11 +9,12 @@ create memory table test(id int primary key, name varchar(255)); ...@@ -9,11 +9,12 @@ create memory table test(id int primary key, name varchar(255));
insert into test values(1, 'Hello'); insert into test values(1, 'Hello');
> update count: 1 > update count: 1
select degrees(null) vn, degrees(1) v1, degrees(1.1) v2, degrees(-1.1) v3, degrees(1.9) v4, degrees(-1.9) v5 from test; -- Truncate least significant digits because implementations returns slightly
> VN V1 V2 V3 V4 V5 -- different results depending on Java version
> ---- ----------------- ----------------- ------------------ ------------------ ------------------- select degrees(null) vn, truncate(degrees(1), 10) v1, truncate(degrees(1.1), 10) v2,
> null 57.29577951308232 63.02535746439057 -63.02535746439057 108.86198107485642 -108.86198107485642 truncate(degrees(-1.1), 10) v3, truncate(degrees(1.9), 10) v4,
truncate(degrees(-1.9), 10) v5 from test;
> VN V1 V2 V3 V4 V5
> ---- ------------ ------------- -------------- -------------- ---------------
> null 57.295779513 63.0253574643 -63.0253574643 108.8619810748 -108.8619810748
> rows: 1 > rows: 1
...@@ -9,11 +9,12 @@ create memory table test(id int primary key, name varchar(255)); ...@@ -9,11 +9,12 @@ create memory table test(id int primary key, name varchar(255));
insert into test values(1, 'Hello'); insert into test values(1, 'Hello');
> update count: 1 > update count: 1
select radians(null) vn, radians(1) v1, radians(1.1) v2, radians(-1.1) v3, radians(1.9) v4, radians(-1.9) v5 from test; -- Truncate least significant digits because implementations returns slightly
> VN V1 V2 V3 V4 V5 -- different results depending on Java version
> ---- -------------------- -------------------- --------------------- ------------------- -------------------- select radians(null) vn, truncate(radians(1), 10) v1, truncate(radians(1.1), 10) v2,
> null 0.017453292519943295 0.019198621771937624 -0.019198621771937624 0.03316125578789226 -0.03316125578789226 truncate(radians(-1.1), 10) v3, truncate(radians(1.9), 10) v4,
truncate(radians(-1.9), 10) v5 from test;
> VN V1 V2 V3 V4 V5
> ---- ------------ ------------ ------------- ------------ -------------
> null 0.0174532925 0.0191986217 -0.0191986217 0.0331612557 -0.0331612557
> rows: 1 > rows: 1
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
...@@ -17,6 +17,7 @@ import java.util.zip.ZipEntry; ...@@ -17,6 +17,7 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream; import java.util.zip.ZipInputStream;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.store.fs.FakeFileChannel;
import org.h2.store.fs.FileBase; import org.h2.store.fs.FileBase;
import org.h2.store.fs.FileChannelInputStream; import org.h2.store.fs.FileChannelInputStream;
import org.h2.store.fs.FilePath; import org.h2.store.fs.FilePath;
...@@ -426,9 +427,7 @@ class FileZip2 extends FileBase { ...@@ -426,9 +427,7 @@ class FileZip2 extends FileBase {
public synchronized FileLock tryLock(long position, long size, public synchronized FileLock tryLock(long position, long size,
boolean shared) throws IOException { boolean shared) throws IOException {
if (shared) { if (shared) {
return new FileLock(new FakeFileChannel(), position, size, shared) {
// cast to FileChannel to avoid JDK 1.7 ambiguity
return new FileLock((FileChannel) null, position, size, shared) {
@Override @Override
public boolean isValid() { public boolean isValid() {
......
...@@ -344,7 +344,7 @@ class ForStatement extends StatementBase { ...@@ -344,7 +344,7 @@ class ForStatement extends StatementBase {
@Override @Override
public String asString() { public String asString() {
StringBuffer buff = new StringBuffer(); StringBuilder buff = new StringBuilder();
buff.append("for ("); buff.append("for (");
if (iterableType != null) { if (iterableType != null) {
Type it = iterable.getType(); Type it = iterable.getType();
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论