Unverified 提交 fc67a911 authored 作者: ScaY's avatar ScaY 提交者: GitHub

Merge branch 'master' into scay-issue#832

......@@ -498,7 +498,7 @@ qualified class name. The class must implement the interface
Admin rights are required to execute this command.
This command commits an open transaction in this connection.
","
CREATE AGGREGATE MEDIAN FOR ""com.acme.db.Median""
CREATE AGGREGATE SIMPLE_MEDIAN FOR ""com.acme.db.Median""
"
"Commands (DDL)","CREATE ALIAS","
......@@ -821,7 +821,7 @@ Drops an existing user-defined aggregate function.
Admin rights are required to execute this command.
This command commits an open transaction in this connection.
","
DROP AGGREGATE MEDIAN
DROP AGGREGATE SIMPLE_MEDIAN
"
"Commands (DDL)","DROP ALIAS","
......@@ -2801,6 +2801,19 @@ Aggregates are only allowed in select statements.
VAR_SAMP(X)
"
"Functions (Aggregate)","MEDIAN","
MEDIAN( [ DISTINCT ] value )
","
The value separating the higher half of a values from the lower half.
Returns the middle value or an interpolated value between two middle values if number of values is even.
Interpolation is only supported for numeric, date, and time data types.
NULL values are ignored in the calculation.
If no rows are selected, the result is NULL.
Aggregates are only allowed in select statements.
","
MEDIAN(X)
"
"Functions (Numeric)","ABS","
ABS ( { numeric } )
","
......
......@@ -77,7 +77,7 @@ public class Select extends Query {
boolean[] groupByExpression;
/**
* Thhe current group-by values.
* The current group-by values.
*/
HashMap<Expression, Object> currentGroup;
......
......@@ -123,10 +123,15 @@ public class Aggregate extends Expression {
/**
* The aggregate type for HISTOGRAM(expression).
*/
HISTOGRAM
HISTOGRAM,
/**
* The aggregate type for MEDIAN(expression).
*/
MEDIAN
}
private static final HashMap<String, AggregateType> AGGREGATES = new HashMap<>(24);
private static final HashMap<String, AggregateType> AGGREGATES = new HashMap<>(25);
private final AggregateType type;
private final Select select;
......@@ -187,6 +192,7 @@ public class Aggregate extends Expression {
addAggregate("HISTOGRAM", AggregateType.HISTOGRAM);
addAggregate("BIT_OR", AggregateType.BIT_OR);
addAggregate("BIT_AND", AggregateType.BIT_AND);
addAggregate("MEDIAN", AggregateType.MEDIAN);
}
private static void addAggregate(String name, AggregateType type) {
......@@ -287,7 +293,7 @@ public class Aggregate extends Expression {
Table table = select.getTopTableFilter().getTable();
return ValueLong.get(table.getRowCount(session));
case MIN:
case MAX:
case MAX: {
boolean first = type == AggregateType.MIN;
Index index = getMinMaxColumnIndex();
int sortType = index.getIndexColumns()[0].sortType;
......@@ -303,6 +309,10 @@ public class Aggregate extends Expression {
v = row.getValue(index.getColumns()[0].getColumnId());
}
return v;
}
case MEDIAN: {
return AggregateDataMedian.getFromIndex(session, on, dataType);
}
default:
DbException.throwInternalError("type=" + type);
}
......@@ -434,6 +444,7 @@ public class Aggregate extends Expression {
break;
case MIN:
case MAX:
case MEDIAN:
break;
case STDDEV_POP:
case STDDEV_SAMP:
......@@ -568,6 +579,9 @@ public class Aggregate extends Expression {
case BIT_OR:
text = "BIT_OR";
break;
case MEDIAN:
text = "MEDIAN";
break;
default:
throw DbException.throwInternalError("type=" + type);
}
......@@ -606,6 +620,11 @@ public class Aggregate extends Expression {
case MAX:
Index index = getMinMaxColumnIndex();
return index != null;
case MEDIAN:
if (distinct) {
return false;
}
return AggregateDataMedian.getMedianColumnIndex(on) != null;
default:
return false;
}
......
......@@ -31,6 +31,8 @@ abstract class AggregateData {
return new AggregateDataCount();
} else if (aggregateType == AggregateType.HISTOGRAM) {
return new AggregateDataHistogram();
} else if (aggregateType == AggregateType.MEDIAN) {
return new AggregateDataMedian();
} else {
return new AggregateDataDefault(aggregateType);
}
......
......@@ -27,38 +27,38 @@ public class Operation extends Expression {
* This operation represents a string concatenation as in
* 'Hello' || 'World'.
*/
CONCAT,
CONCAT,
/**
* This operation represents an addition as in 1 + 2.
*/
PLUS,
PLUS,
/**
* This operation represents a subtraction as in 2 - 1.
*/
MINUS,
MINUS,
/**
* This operation represents a multiplication as in 2 * 3.
*/
MULTIPLY,
MULTIPLY,
/**
* This operation represents a division as in 4 * 2.
*/
DIVIDE,
DIVIDE,
/**
* This operation represents a negation as in - ID.
*/
NEGATE,
NEGATE,
/**
* This operation represents a modulus as in 5 % 2.
*/
MODULUS
};
MODULUS
}
private OpType opType;
private Expression left, right;
......
......@@ -23,6 +23,7 @@ import javax.sql.XAConnection;
import javax.sql.XADataSource;
import org.h2.Driver;
import org.h2.jdbc.JdbcConnection;
import org.h2.message.DbException;
import org.h2.message.TraceObject;
import org.h2.util.StringUtils;
......@@ -401,23 +402,31 @@ public class JdbcDataSource extends TraceObject implements XADataSource,
}
/**
* [Not supported] Return an object of this class if possible.
* Return an object of this class if possible.
*
* @param iface the class
*/
@Override
@SuppressWarnings("unchecked")
public <T> T unwrap(Class<T> iface) throws SQLException {
throw unsupported("unwrap");
try {
if (isWrapperFor(iface)) {
return (T) this;
}
throw DbException.getInvalidValueException("iface", iface);
} catch (Exception e) {
throw logAndConvert(e);
}
}
/**
* [Not supported] Checks if unwrap can return an object of this class.
* Checks if unwrap can return an object of this class.
*
* @param iface the class
*/
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
throw unsupported("isWrapperFor");
return iface != null && iface.isAssignableFrom(getClass());
}
/**
......
......@@ -43,11 +43,19 @@ public class DateTimeUtils {
*/
public static final TimeZone UTC = TimeZone.getTimeZone("UTC");
private static final long NANOS_PER_DAY = MILLIS_PER_DAY * 1000000;
/**
* The number of nanoseconds per day.
*/
public static final long NANOS_PER_DAY = MILLIS_PER_DAY * 1000000;
private static final int SHIFT_YEAR = 9;
private static final int SHIFT_MONTH = 5;
/**
* Date value for 1970-01-01.
*/
private static final int EPOCH_DATE_VALUE = (1970 << SHIFT_YEAR) + (1 << SHIFT_MONTH) + 1;
private static final int[] NORMAL_DAYS_PER_MONTH = { 0, 31, 28, 31, 30, 31,
30, 31, 31, 30, 31, 30, 31 };
......@@ -65,12 +73,12 @@ public class DateTimeUtils {
* have that problem, and while it is still a small memory leak, it is not a
* class loader memory leak.
*/
private static final ThreadLocal<Calendar> CACHED_CALENDAR = new ThreadLocal<>();
private static final ThreadLocal<GregorianCalendar> CACHED_CALENDAR = new ThreadLocal<>();
/**
* A cached instance of Calendar used when a timezone is specified.
*/
private static final ThreadLocal<Calendar> CACHED_CALENDAR_NON_DEFAULT_TIMEZONE =
private static final ThreadLocal<GregorianCalendar> CACHED_CALENDAR_NON_DEFAULT_TIMEZONE =
new ThreadLocal<>();
/**
......@@ -102,8 +110,8 @@ public class DateTimeUtils {
*
* @return a calendar instance. A cached instance is returned where possible
*/
private static Calendar getCalendar() {
Calendar c = CACHED_CALENDAR.get();
private static GregorianCalendar getCalendar() {
GregorianCalendar c = CACHED_CALENDAR.get();
if (c == null) {
c = DateTimeUtils.createGregorianCalendar();
CACHED_CALENDAR.set(c);
......@@ -118,8 +126,8 @@ public class DateTimeUtils {
* @param tz timezone for the calendar, is never null
* @return a calendar instance. A cached instance is returned where possible
*/
private static Calendar getCalendar(TimeZone tz) {
Calendar c = CACHED_CALENDAR_NON_DEFAULT_TIMEZONE.get();
private static GregorianCalendar getCalendar(TimeZone tz) {
GregorianCalendar c = CACHED_CALENDAR_NON_DEFAULT_TIMEZONE.get();
if (c == null || !c.getTimeZone().equals(tz)) {
c = DateTimeUtils.createGregorianCalendar(tz);
CACHED_CALENDAR_NON_DEFAULT_TIMEZONE.set(c);
......@@ -137,7 +145,7 @@ public class DateTimeUtils {
*
* @return a new calendar instance.
*/
public static Calendar createGregorianCalendar() {
public static GregorianCalendar createGregorianCalendar() {
return new GregorianCalendar();
}
......@@ -151,7 +159,7 @@ public class DateTimeUtils {
* @param tz timezone for the calendar, is never null
* @return a new calendar instance.
*/
public static Calendar createGregorianCalendar(TimeZone tz) {
public static GregorianCalendar createGregorianCalendar(TimeZone tz) {
return new GregorianCalendar(tz);
}
......@@ -520,9 +528,15 @@ public class DateTimeUtils {
*/
public static long getMillis(TimeZone tz, int year, int month, int day,
int hour, int minute, int second, int millis) {
GregorianCalendar c;
if (tz == null) {
c = getCalendar();
} else {
c = getCalendar(tz);
}
c.setLenient(false);
try {
return getTimeTry(false, tz, year, month, day, hour, minute, second,
millis);
return convertToMillis(c, year, month, day, hour, minute, second, millis);
} catch (IllegalArgumentException e) {
// special case: if the time simply doesn't exist because of
// daylight saving time changes, use the lenient version
......@@ -531,12 +545,10 @@ public class DateTimeUtils {
if (hour < 0 || hour > 23) {
throw e;
}
return getTimeTry(true, tz, year, month, day, hour, minute,
second, millis);
} else if (message.indexOf("DAY_OF_MONTH") > 0) {
int maxDay;
if (month == 2) {
maxDay = new GregorianCalendar().isLeapYear(year) ? 29 : 28;
maxDay = c.isLeapYear(year) ? 29 : 28;
} else {
maxDay = 30 + ((month + (month > 7 ? 1 : 0)) & 1);
}
......@@ -547,27 +559,12 @@ public class DateTimeUtils {
// using the timezone Brasilia and others,
// for example for 2042-10-12 00:00:00.
hour += 6;
return getTimeTry(true, tz, year, month, day, hour, minute,
second, millis);
} else {
return getTimeTry(true, tz, year, month, day, hour, minute,
second, millis);
}
c.setLenient(true);
return convertToMillis(c, year, month, day, hour, minute, second, millis);
}
}
private static long getTimeTry(boolean lenient, TimeZone tz, int year,
int month, int day, int hour, int minute, int second, int millis) {
Calendar c;
if (tz == null) {
c = getCalendar();
} else {
c = getCalendar(tz);
}
c.setLenient(lenient);
return convertToMillis(c, year, month, day, hour, minute, second, millis);
}
private static long convertToMillis(Calendar cal, int year, int month, int day,
int hour, int minute, int second, int millis) {
if (year <= 0) {
......@@ -595,19 +592,45 @@ public class DateTimeUtils {
* @param field the field type
* @return the value
*/
public static long getDatePart(Value date, int field) {
Calendar c = valueToCalendar(date);
if(field == Function.EPOCH) {
return c.getTime().getTime() / 1000;
}
if (field == Calendar.YEAR) {
return getYear(c);
public static int getDatePart(Value date, int field) {
long dateValue = EPOCH_DATE_VALUE;
long timeNanos = 0;
if (date instanceof ValueTimestamp) {
ValueTimestamp v = (ValueTimestamp) date;
dateValue = v.getDateValue();
timeNanos = v.getTimeNanos();
} else if (date instanceof ValueDate) {
dateValue = ((ValueDate) date).getDateValue();
} else if (date instanceof ValueTime) {
timeNanos = ((ValueTime) date).getNanos();
} else if (date instanceof ValueTimestampTimeZone) {
ValueTimestampTimeZone v = (ValueTimestampTimeZone) date;
dateValue = v.getDateValue();
timeNanos = v.getTimeNanos();
} else {
ValueTimestamp v = (ValueTimestamp) date.convertTo(Value.TIMESTAMP);
date = v; // For valueToCalendar() to avoid second convertTo() call
dateValue = v.getDateValue();
timeNanos = v.getTimeNanos();
}
int value = c.get(field);
if (field == Calendar.MONTH) {
return value + 1;
switch (field) {
case Calendar.YEAR:
return yearFromDateValue(dateValue);
case Calendar.MONTH:
return monthFromDateValue(dateValue);
case Calendar.DAY_OF_MONTH:
return dayFromDateValue(dateValue);
case Calendar.HOUR_OF_DAY:
return (int) (timeNanos / 3_600_000_000_000L % 24);
case Calendar.MINUTE:
return (int) (timeNanos / 60_000_000_000L % 60);
case Calendar.SECOND:
return (int) (timeNanos / 1_000_000_000 % 60);
case Calendar.MILLISECOND:
return (int) (timeNanos / 1_000_000 % 1_000);
}
return value;
return valueToCalendar(date).get(field);
}
/**
......
......@@ -12,7 +12,6 @@ import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import org.h2.message.DbException;
import org.h2.value.Value;
import org.h2.value.ValueDate;
......@@ -61,58 +60,103 @@ public class LocalDateTimeUtils {
*/
public static final Class<?> OFFSET_DATE_TIME;
// Class<java.time.ZoneOffset>
/**
* {@code Class<java.time.ZoneOffset>} or {@code null}.
*/
private static final Class<?> ZONE_OFFSET;
// java.time.LocalTime#ofNanoOfDay()
/**
* {@code java.time.LocalTime#ofNanoOfDay()} or {@code null}.
*/
private static final Method LOCAL_TIME_OF_NANO;
// java.time.LocalTime#toNanoOfDay()
/**
* {@code java.time.LocalTime#toNanoOfDay()} or {@code null}.
*/
private static final Method LOCAL_TIME_TO_NANO;
// java.time.LocalDate#of(int, int, int)
/**
* {@code java.time.LocalDate#of(int, int, int)} or {@code null}.
*/
private static final Method LOCAL_DATE_OF_YEAR_MONTH_DAY;
// java.time.LocalDate#parse(CharSequence)
/**
* {@code java.time.LocalDate#parse(CharSequence)} or {@code null}.
*/
private static final Method LOCAL_DATE_PARSE;
// java.time.LocalDate#getYear()
/**
* {@code java.time.LocalDate#getYear()} or {@code null}.
*/
private static final Method LOCAL_DATE_GET_YEAR;
// java.time.LocalDate#getMonthValue()
/**
* {@code java.time.LocalDate#getMonthValue()} or {@code null}.
*/
private static final Method LOCAL_DATE_GET_MONTH_VALUE;
// java.time.LocalDate#getDayOfMonth()
/**
* {@code java.time.LocalDate#getDayOfMonth()} or {@code null}.
*/
private static final Method LOCAL_DATE_GET_DAY_OF_MONTH;
// java.time.LocalDate#atStartOfDay()
/**
* {@code java.time.LocalDate#atStartOfDay()} or {@code null}.
*/
private static final Method LOCAL_DATE_AT_START_OF_DAY;
// java.sql.Timestamp.from(java.time.Instant)
/**
* {@code java.sql.Timestamp.from(java.time.Instant)} or {@code null}.
*/
private static final Method TIMESTAMP_FROM;
// java.sql.Timestamp.toInstant()
/**
* {@code java.sql.Timestamp.toInstant()} or {@code null}.
*/
private static final Method TIMESTAMP_TO_INSTANT;
// java.time.LocalTime#parse(CharSequence)
/**
* {@code java.time.LocalTime#parse(CharSequence)} or {@code null}.
*/
private static final Method LOCAL_TIME_PARSE;
// java.time.LocalDateTime#plusNanos(long)
/**
* {@code java.time.LocalDateTime#plusNanos(long)} or {@code null}.
*/
private static final Method LOCAL_DATE_TIME_PLUS_NANOS;
// java.time.LocalDateTime#toLocalDate()
/**
* {@code java.time.LocalDateTime#toLocalDate()} or {@code null}.
*/
private static final Method LOCAL_DATE_TIME_TO_LOCAL_DATE;
// java.time.LocalDateTime#toLocalTime()
/**
* {@code java.time.LocalDateTime#toLocalTime()} or {@code null}.
*/
private static final Method LOCAL_DATE_TIME_TO_LOCAL_TIME;
// java.time.LocalDateTime#parse(CharSequence)
/**
* {@code java.time.LocalDateTime#parse(CharSequence)} or {@code null}.
*/
private static final Method LOCAL_DATE_TIME_PARSE;
// java.time.ZoneOffset#ofTotalSeconds(int)
/**
* {@code java.time.ZoneOffset#ofTotalSeconds(int)} or {@code null}.
*/
private static final Method ZONE_OFFSET_OF_TOTAL_SECONDS;
// java.time.OffsetDateTime#of(LocalDateTime, ZoneOffset)
/**
* {@code java.time.OffsetDateTime#of(LocalDateTime, ZoneOffset)} or
* {@code null}.
*/
private static final Method OFFSET_DATE_TIME_OF_LOCAL_DATE_TIME_ZONE_OFFSET;
// java.time.OffsetDateTime#parse(CharSequence)
/**
* {@code java.time.OffsetDateTime#parse(CharSequence)} or {@code null}.
*/
private static final Method OFFSET_DATE_TIME_PARSE;
// java.time.OffsetDateTime#toLocalDateTime()
/**
* {@code java.time.OffsetDateTime#toLocalDateTime()} or {@code null}.
*/
private static final Method OFFSET_DATE_TIME_TO_LOCAL_DATE_TIME;
// java.time.OffsetDateTime#getOffset()
/**
* {@code java.time.OffsetDateTime#getOffset()} or {@code null}.
*/
private static final Method OFFSET_DATE_TIME_GET_OFFSET;
// java.time.ZoneOffset#getTotalSeconds()
/**
* {@code java.time.ZoneOffset#getTotalSeconds()} or {@code null}.
*/
private static final Method ZONE_OFFSET_GET_TOTAL_SECONDS;
private static final boolean IS_JAVA8_DATE_API_PRESENT;
......
......@@ -740,12 +740,12 @@ public class TestFunctions extends TestBase implements AggregateFunction {
deleteDb("functions");
Connection conn = getConnection("functions");
Statement stat = conn.createStatement();
stat.execute("CREATE AGGREGATE MEDIAN FOR \"" +
stat.execute("CREATE AGGREGATE SIMPLE_MEDIAN FOR \"" +
MedianStringType.class.getName() + "\"");
stat.execute("CREATE AGGREGATE IF NOT EXISTS MEDIAN FOR \"" +
stat.execute("CREATE AGGREGATE IF NOT EXISTS SIMPLE_MEDIAN FOR \"" +
MedianStringType.class.getName() + "\"");
ResultSet rs = stat.executeQuery(
"SELECT MEDIAN(X) FROM SYSTEM_RANGE(1, 9)");
"SELECT SIMPLE_MEDIAN(X) FROM SYSTEM_RANGE(1, 9)");
rs.next();
assertEquals("5", rs.getString(1));
conn.close();
......@@ -756,22 +756,22 @@ public class TestFunctions extends TestBase implements AggregateFunction {
conn = getConnection("functions");
stat = conn.createStatement();
stat.executeQuery("SELECT MEDIAN(X) FROM SYSTEM_RANGE(1, 9)");
stat.executeQuery("SELECT SIMPLE_MEDIAN(X) FROM SYSTEM_RANGE(1, 9)");
DatabaseMetaData meta = conn.getMetaData();
rs = meta.getProcedures(null, null, "MEDIAN");
rs = meta.getProcedures(null, null, "SIMPLE_MEDIAN");
assertTrue(rs.next());
assertFalse(rs.next());
rs = stat.executeQuery("SCRIPT");
boolean found = false;
while (rs.next()) {
String sql = rs.getString(1);
if (sql.contains("MEDIAN")) {
if (sql.contains("SIMPLE_MEDIAN")) {
found = true;
}
}
assertTrue(found);
stat.execute("DROP AGGREGATE MEDIAN");
stat.execute("DROP AGGREGATE IF EXISTS MEDIAN");
stat.execute("DROP AGGREGATE SIMPLE_MEDIAN");
stat.execute("DROP AGGREGATE IF EXISTS SIMPLE_MEDIAN");
conn.close();
}
......@@ -779,12 +779,12 @@ public class TestFunctions extends TestBase implements AggregateFunction {
deleteDb("functions");
Connection conn = getConnection("functions");
Statement stat = conn.createStatement();
stat.execute("CREATE AGGREGATE MEDIAN FOR \"" +
stat.execute("CREATE AGGREGATE SIMPLE_MEDIAN FOR \"" +
MedianString.class.getName() + "\"");
stat.execute("CREATE AGGREGATE IF NOT EXISTS MEDIAN FOR \"" +
stat.execute("CREATE AGGREGATE IF NOT EXISTS SIMPLE_MEDIAN FOR \"" +
MedianString.class.getName() + "\"");
ResultSet rs = stat.executeQuery(
"SELECT MEDIAN(X) FROM SYSTEM_RANGE(1, 9)");
"SELECT SIMPLE_MEDIAN(X) FROM SYSTEM_RANGE(1, 9)");
rs.next();
assertEquals("5", rs.getString(1));
conn.close();
......@@ -795,22 +795,22 @@ public class TestFunctions extends TestBase implements AggregateFunction {
conn = getConnection("functions");
stat = conn.createStatement();
stat.executeQuery("SELECT MEDIAN(X) FROM SYSTEM_RANGE(1, 9)");
stat.executeQuery("SELECT SIMPLE_MEDIAN(X) FROM SYSTEM_RANGE(1, 9)");
DatabaseMetaData meta = conn.getMetaData();
rs = meta.getProcedures(null, null, "MEDIAN");
rs = meta.getProcedures(null, null, "SIMPLE_MEDIAN");
assertTrue(rs.next());
assertFalse(rs.next());
rs = stat.executeQuery("SCRIPT");
boolean found = false;
while (rs.next()) {
String sql = rs.getString(1);
if (sql.contains("MEDIAN")) {
if (sql.contains("SIMPLE_MEDIAN")) {
found = true;
}
}
assertTrue(found);
stat.execute("DROP AGGREGATE MEDIAN");
stat.execute("DROP AGGREGATE IF EXISTS MEDIAN");
stat.execute("DROP AGGREGATE SIMPLE_MEDIAN");
stat.execute("DROP AGGREGATE IF EXISTS SIMPLE_MEDIAN");
conn.close();
}
......
......@@ -262,7 +262,8 @@ public class TestIndex extends TestBase {
c.close();
}
private void testConcurrentUpdateRun(ConcurrentUpdateThread[] threads, PreparedStatement check) throws SQLException {
private void testConcurrentUpdateRun(ConcurrentUpdateThread[] threads, PreparedStatement check)
throws SQLException {
for (ConcurrentUpdateThread t : threads) {
t.start();
}
......
......@@ -15,9 +15,12 @@ import javax.naming.StringRefAddr;
import javax.naming.spi.ObjectFactory;
import javax.sql.ConnectionEvent;
import javax.sql.ConnectionEventListener;
import javax.sql.DataSource;
import javax.sql.XAConnection;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import org.h2.api.ErrorCode;
import org.h2.jdbcx.JdbcDataSource;
import org.h2.jdbcx.JdbcDataSourceFactory;
import org.h2.jdbcx.JdbcXAConnection;
......@@ -71,6 +74,7 @@ public class TestDataSource extends TestBase {
}
testDataSourceFactory();
testDataSource();
testUnwrap();
testXAConnection();
deleteDb("dataSource");
}
......@@ -190,4 +194,20 @@ public class TestDataSource extends TestBase {
conn.close();
}
private void testUnwrap() throws SQLException {
JdbcDataSource ds = new JdbcDataSource();
assertTrue(ds.isWrapperFor(Object.class));
assertTrue(ds.isWrapperFor(DataSource.class));
assertTrue(ds.isWrapperFor(JdbcDataSource.class));
assertFalse(ds.isWrapperFor(String.class));
assertTrue(ds == ds.unwrap(Object.class));
assertTrue(ds == ds.unwrap(DataSource.class));
try {
ds.unwrap(String.class);
fail();
} catch (SQLException ex) {
assertEquals(ErrorCode.INVALID_VALUE_2, ex.getErrorCode());
}
}
}
......@@ -99,7 +99,7 @@ public class TestScript extends TestBase {
testScript("datatypes/" + s + ".sql");
}
for (String s : new String[] { "avg", "bit-and", "bit-or", "count",
"group-concat", "max", "min", "selectivity", "stddev-pop",
"group-concat", "max", "median", "min", "selectivity", "stddev-pop",
"stddev-samp", "sum", "var-pop", "var-samp" }) {
testScript("functions/aggregate/" + s + ".sql");
}
......
......@@ -139,7 +139,7 @@ public class TestTools extends TestBase {
c.runTool("-web", "-webPort", "9002", "-tool", "-browser", "-tcp",
"-tcpPort", "9003", "-pg", "-pgPort", "9004");
assertContains(lastUrl, ":9002");
c.shutdown();
shutdownConsole(c);
// check if starting the browser works
c.runTool("-web", "-webPort", "9002", "-tool");
......@@ -169,7 +169,7 @@ public class TestTools extends TestBase {
// ignore
}
c.shutdown();
shutdownConsole(c);
// trying to use the same port for two services should fail,
// but also stop the first service
......@@ -184,7 +184,19 @@ public class TestTools extends TestBase {
} else {
System.clearProperty(SysProperties.H2_BROWSER);
}
c.shutdown();
shutdownConsole(c);
}
}
private static void shutdownConsole(Console c) {
c.shutdown();
if (Thread.currentThread().isInterrupted()) {
// Clear interrupted state so test can continue its work safely
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// Ignore
}
}
}
......
......@@ -307,10 +307,10 @@ public class TestValue extends TestBase {
}
private void testTimestamp() {
ValueTimestamp vts = ValueTimestamp.parse("2000-01-15 10:20:30.333222111");
ValueTimestamp valueTs = ValueTimestamp.parse("2000-01-15 10:20:30.333222111");
Timestamp ts = Timestamp.valueOf("2000-01-15 10:20:30.333222111");
assertEquals(ts.toString(), vts.getString());
assertEquals(ts, vts.getTimestamp());
assertEquals(ts.toString(), valueTs.getString());
assertEquals(ts, valueTs.getTimestamp());
Calendar c = Calendar.getInstance(TimeZone.getTimeZone("Europe/Berlin"));
c.set(2018, 02, 25, 1, 59, 00);
c.set(Calendar.MILLISECOND, 123);
......
......@@ -760,5 +760,6 @@ zzbbzz cldr booleans maria enquotes mtc cbuf checksummed nreturn despite bbzz re
unconditionally coco aren eecccc decimals charsets zzbb lsb msb usecount outdir endian misleading precompiled
assorted reimplemented hangups confirmation predefined
mdy destfile hclf forbids spellchecking selfdestruct expects accident jacocoagent cli historic mitigate
jacoco xdata invokes sourcefiles classfiles duplication crypto stacktraces prt directions handled overly asm hardcoded
mdy destfile hclf forbids spellchecking selfdestruct expects accident jacocoagent cli historic mitigate
jacoco xdata invokes sourcefiles classfiles duplication crypto stacktraces prt directions handled overly asm hardcoded
interpolated thead
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论