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 ...@@ -498,7 +498,7 @@ qualified class name. The class must implement the interface
Admin rights are required to execute this command. Admin rights are required to execute this command.
This command commits an open transaction in this connection. 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"," "Commands (DDL)","CREATE ALIAS","
...@@ -821,7 +821,7 @@ Drops an existing user-defined aggregate function. ...@@ -821,7 +821,7 @@ Drops an existing user-defined aggregate function.
Admin rights are required to execute this command. Admin rights are required to execute this command.
This command commits an open transaction in this connection. This command commits an open transaction in this connection.
"," ","
DROP AGGREGATE MEDIAN DROP AGGREGATE SIMPLE_MEDIAN
" "
"Commands (DDL)","DROP ALIAS"," "Commands (DDL)","DROP ALIAS","
...@@ -2801,6 +2801,19 @@ Aggregates are only allowed in select statements. ...@@ -2801,6 +2801,19 @@ Aggregates are only allowed in select statements.
VAR_SAMP(X) 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"," "Functions (Numeric)","ABS","
ABS ( { numeric } ) ABS ( { numeric } )
"," ","
......
...@@ -77,7 +77,7 @@ public class Select extends Query { ...@@ -77,7 +77,7 @@ public class Select extends Query {
boolean[] groupByExpression; boolean[] groupByExpression;
/** /**
* Thhe current group-by values. * The current group-by values.
*/ */
HashMap<Expression, Object> currentGroup; HashMap<Expression, Object> currentGroup;
......
...@@ -123,10 +123,15 @@ public class Aggregate extends Expression { ...@@ -123,10 +123,15 @@ public class Aggregate extends Expression {
/** /**
* The aggregate type for HISTOGRAM(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 AggregateType type;
private final Select select; private final Select select;
...@@ -187,6 +192,7 @@ public class Aggregate extends Expression { ...@@ -187,6 +192,7 @@ public class Aggregate extends Expression {
addAggregate("HISTOGRAM", AggregateType.HISTOGRAM); addAggregate("HISTOGRAM", AggregateType.HISTOGRAM);
addAggregate("BIT_OR", AggregateType.BIT_OR); addAggregate("BIT_OR", AggregateType.BIT_OR);
addAggregate("BIT_AND", AggregateType.BIT_AND); addAggregate("BIT_AND", AggregateType.BIT_AND);
addAggregate("MEDIAN", AggregateType.MEDIAN);
} }
private static void addAggregate(String name, AggregateType type) { private static void addAggregate(String name, AggregateType type) {
...@@ -287,7 +293,7 @@ public class Aggregate extends Expression { ...@@ -287,7 +293,7 @@ public class Aggregate extends Expression {
Table table = select.getTopTableFilter().getTable(); Table table = select.getTopTableFilter().getTable();
return ValueLong.get(table.getRowCount(session)); return ValueLong.get(table.getRowCount(session));
case MIN: case MIN:
case MAX: case MAX: {
boolean first = type == AggregateType.MIN; boolean first = type == AggregateType.MIN;
Index index = getMinMaxColumnIndex(); Index index = getMinMaxColumnIndex();
int sortType = index.getIndexColumns()[0].sortType; int sortType = index.getIndexColumns()[0].sortType;
...@@ -303,6 +309,10 @@ public class Aggregate extends Expression { ...@@ -303,6 +309,10 @@ public class Aggregate extends Expression {
v = row.getValue(index.getColumns()[0].getColumnId()); v = row.getValue(index.getColumns()[0].getColumnId());
} }
return v; return v;
}
case MEDIAN: {
return AggregateDataMedian.getFromIndex(session, on, dataType);
}
default: default:
DbException.throwInternalError("type=" + type); DbException.throwInternalError("type=" + type);
} }
...@@ -434,6 +444,7 @@ public class Aggregate extends Expression { ...@@ -434,6 +444,7 @@ public class Aggregate extends Expression {
break; break;
case MIN: case MIN:
case MAX: case MAX:
case MEDIAN:
break; break;
case STDDEV_POP: case STDDEV_POP:
case STDDEV_SAMP: case STDDEV_SAMP:
...@@ -568,6 +579,9 @@ public class Aggregate extends Expression { ...@@ -568,6 +579,9 @@ public class Aggregate extends Expression {
case BIT_OR: case BIT_OR:
text = "BIT_OR"; text = "BIT_OR";
break; break;
case MEDIAN:
text = "MEDIAN";
break;
default: default:
throw DbException.throwInternalError("type=" + type); throw DbException.throwInternalError("type=" + type);
} }
...@@ -606,6 +620,11 @@ public class Aggregate extends Expression { ...@@ -606,6 +620,11 @@ public class Aggregate extends Expression {
case MAX: case MAX:
Index index = getMinMaxColumnIndex(); Index index = getMinMaxColumnIndex();
return index != null; return index != null;
case MEDIAN:
if (distinct) {
return false;
}
return AggregateDataMedian.getMedianColumnIndex(on) != null;
default: default:
return false; return false;
} }
......
...@@ -31,6 +31,8 @@ abstract class AggregateData { ...@@ -31,6 +31,8 @@ abstract class AggregateData {
return new AggregateDataCount(); return new AggregateDataCount();
} else if (aggregateType == AggregateType.HISTOGRAM) { } else if (aggregateType == AggregateType.HISTOGRAM) {
return new AggregateDataHistogram(); return new AggregateDataHistogram();
} else if (aggregateType == AggregateType.MEDIAN) {
return new AggregateDataMedian();
} else { } else {
return new AggregateDataDefault(aggregateType); return new AggregateDataDefault(aggregateType);
} }
......
...@@ -58,7 +58,7 @@ public class Operation extends Expression { ...@@ -58,7 +58,7 @@ public class Operation extends Expression {
* This operation represents a modulus as in 5 % 2. * This operation represents a modulus as in 5 % 2.
*/ */
MODULUS MODULUS
}; }
private OpType opType; private OpType opType;
private Expression left, right; private Expression left, right;
......
...@@ -23,6 +23,7 @@ import javax.sql.XAConnection; ...@@ -23,6 +23,7 @@ import javax.sql.XAConnection;
import javax.sql.XADataSource; import javax.sql.XADataSource;
import org.h2.Driver; import org.h2.Driver;
import org.h2.jdbc.JdbcConnection; import org.h2.jdbc.JdbcConnection;
import org.h2.message.DbException;
import org.h2.message.TraceObject; import org.h2.message.TraceObject;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
...@@ -401,23 +402,31 @@ public class JdbcDataSource extends TraceObject implements XADataSource, ...@@ -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 * @param iface the class
*/ */
@Override @Override
@SuppressWarnings("unchecked")
public <T> T unwrap(Class<T> iface) throws SQLException { 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 * @param iface the class
*/ */
@Override @Override
public boolean isWrapperFor(Class<?> iface) throws SQLException { public boolean isWrapperFor(Class<?> iface) throws SQLException {
throw unsupported("isWrapperFor"); return iface != null && iface.isAssignableFrom(getClass());
} }
/** /**
......
...@@ -43,11 +43,19 @@ public class DateTimeUtils { ...@@ -43,11 +43,19 @@ public class DateTimeUtils {
*/ */
public static final TimeZone UTC = TimeZone.getTimeZone("UTC"); 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_YEAR = 9;
private static final int SHIFT_MONTH = 5; 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, private static final int[] NORMAL_DAYS_PER_MONTH = { 0, 31, 28, 31, 30, 31,
30, 31, 31, 30, 31, 30, 31 }; 30, 31, 31, 30, 31, 30, 31 };
...@@ -65,12 +73,12 @@ public class DateTimeUtils { ...@@ -65,12 +73,12 @@ public class DateTimeUtils {
* have that problem, and while it is still a small memory leak, it is not a * have that problem, and while it is still a small memory leak, it is not a
* class loader memory leak. * 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. * 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<>(); new ThreadLocal<>();
/** /**
...@@ -102,8 +110,8 @@ public class DateTimeUtils { ...@@ -102,8 +110,8 @@ public class DateTimeUtils {
* *
* @return a calendar instance. A cached instance is returned where possible * @return a calendar instance. A cached instance is returned where possible
*/ */
private static Calendar getCalendar() { private static GregorianCalendar getCalendar() {
Calendar c = CACHED_CALENDAR.get(); GregorianCalendar c = CACHED_CALENDAR.get();
if (c == null) { if (c == null) {
c = DateTimeUtils.createGregorianCalendar(); c = DateTimeUtils.createGregorianCalendar();
CACHED_CALENDAR.set(c); CACHED_CALENDAR.set(c);
...@@ -118,8 +126,8 @@ public class DateTimeUtils { ...@@ -118,8 +126,8 @@ public class DateTimeUtils {
* @param tz timezone for the calendar, is never null * @param tz timezone for the calendar, is never null
* @return a calendar instance. A cached instance is returned where possible * @return a calendar instance. A cached instance is returned where possible
*/ */
private static Calendar getCalendar(TimeZone tz) { private static GregorianCalendar getCalendar(TimeZone tz) {
Calendar c = CACHED_CALENDAR_NON_DEFAULT_TIMEZONE.get(); GregorianCalendar c = CACHED_CALENDAR_NON_DEFAULT_TIMEZONE.get();
if (c == null || !c.getTimeZone().equals(tz)) { if (c == null || !c.getTimeZone().equals(tz)) {
c = DateTimeUtils.createGregorianCalendar(tz); c = DateTimeUtils.createGregorianCalendar(tz);
CACHED_CALENDAR_NON_DEFAULT_TIMEZONE.set(c); CACHED_CALENDAR_NON_DEFAULT_TIMEZONE.set(c);
...@@ -137,7 +145,7 @@ public class DateTimeUtils { ...@@ -137,7 +145,7 @@ public class DateTimeUtils {
* *
* @return a new calendar instance. * @return a new calendar instance.
*/ */
public static Calendar createGregorianCalendar() { public static GregorianCalendar createGregorianCalendar() {
return new GregorianCalendar(); return new GregorianCalendar();
} }
...@@ -151,7 +159,7 @@ public class DateTimeUtils { ...@@ -151,7 +159,7 @@ public class DateTimeUtils {
* @param tz timezone for the calendar, is never null * @param tz timezone for the calendar, is never null
* @return a new calendar instance. * @return a new calendar instance.
*/ */
public static Calendar createGregorianCalendar(TimeZone tz) { public static GregorianCalendar createGregorianCalendar(TimeZone tz) {
return new GregorianCalendar(tz); return new GregorianCalendar(tz);
} }
...@@ -520,9 +528,15 @@ public class DateTimeUtils { ...@@ -520,9 +528,15 @@ public class DateTimeUtils {
*/ */
public static long getMillis(TimeZone tz, int year, int month, int day, public static long getMillis(TimeZone tz, int year, int month, int day,
int hour, int minute, int second, int millis) { int hour, int minute, int second, int millis) {
GregorianCalendar c;
if (tz == null) {
c = getCalendar();
} else {
c = getCalendar(tz);
}
c.setLenient(false);
try { try {
return getTimeTry(false, tz, year, month, day, hour, minute, second, return convertToMillis(c, year, month, day, hour, minute, second, millis);
millis);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
// special case: if the time simply doesn't exist because of // special case: if the time simply doesn't exist because of
// daylight saving time changes, use the lenient version // daylight saving time changes, use the lenient version
...@@ -531,12 +545,10 @@ public class DateTimeUtils { ...@@ -531,12 +545,10 @@ public class DateTimeUtils {
if (hour < 0 || hour > 23) { if (hour < 0 || hour > 23) {
throw e; throw e;
} }
return getTimeTry(true, tz, year, month, day, hour, minute,
second, millis);
} else if (message.indexOf("DAY_OF_MONTH") > 0) { } else if (message.indexOf("DAY_OF_MONTH") > 0) {
int maxDay; int maxDay;
if (month == 2) { if (month == 2) {
maxDay = new GregorianCalendar().isLeapYear(year) ? 29 : 28; maxDay = c.isLeapYear(year) ? 29 : 28;
} else { } else {
maxDay = 30 + ((month + (month > 7 ? 1 : 0)) & 1); maxDay = 30 + ((month + (month > 7 ? 1 : 0)) & 1);
} }
...@@ -547,26 +559,11 @@ public class DateTimeUtils { ...@@ -547,26 +559,11 @@ public class DateTimeUtils {
// using the timezone Brasilia and others, // using the timezone Brasilia and others,
// for example for 2042-10-12 00:00:00. // for example for 2042-10-12 00:00:00.
hour += 6; 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);
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); return convertToMillis(c, year, month, day, hour, minute, second, millis);
} }
}
private static long convertToMillis(Calendar cal, int year, int month, int day, private static long convertToMillis(Calendar cal, int year, int month, int day,
int hour, int minute, int second, int millis) { int hour, int minute, int second, int millis) {
...@@ -595,19 +592,45 @@ public class DateTimeUtils { ...@@ -595,19 +592,45 @@ public class DateTimeUtils {
* @param field the field type * @param field the field type
* @return the value * @return the value
*/ */
public static long getDatePart(Value date, int field) {
Calendar c = valueToCalendar(date); public static int getDatePart(Value date, int field) {
if(field == Function.EPOCH) { long dateValue = EPOCH_DATE_VALUE;
return c.getTime().getTime() / 1000; long timeNanos = 0;
} if (date instanceof ValueTimestamp) {
if (field == Calendar.YEAR) { ValueTimestamp v = (ValueTimestamp) date;
return getYear(c); dateValue = v.getDateValue();
} timeNanos = v.getTimeNanos();
int value = c.get(field); } else if (date instanceof ValueDate) {
if (field == Calendar.MONTH) { dateValue = ((ValueDate) date).getDateValue();
return value + 1; } else if (date instanceof ValueTime) {
} timeNanos = ((ValueTime) date).getNanos();
return value; } 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();
}
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 valueToCalendar(date).get(field);
} }
/** /**
......
...@@ -12,7 +12,6 @@ import java.lang.reflect.Method; ...@@ -12,7 +12,6 @@ import java.lang.reflect.Method;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueDate; import org.h2.value.ValueDate;
...@@ -61,58 +60,103 @@ public class LocalDateTimeUtils { ...@@ -61,58 +60,103 @@ public class LocalDateTimeUtils {
*/ */
public static final Class<?> OFFSET_DATE_TIME; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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 Method ZONE_OFFSET_GET_TOTAL_SECONDS;
private static final boolean IS_JAVA8_DATE_API_PRESENT; private static final boolean IS_JAVA8_DATE_API_PRESENT;
......
...@@ -740,12 +740,12 @@ public class TestFunctions extends TestBase implements AggregateFunction { ...@@ -740,12 +740,12 @@ public class TestFunctions extends TestBase implements AggregateFunction {
deleteDb("functions"); deleteDb("functions");
Connection conn = getConnection("functions"); Connection conn = getConnection("functions");
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
stat.execute("CREATE AGGREGATE MEDIAN FOR \"" + stat.execute("CREATE AGGREGATE SIMPLE_MEDIAN FOR \"" +
MedianStringType.class.getName() + "\""); 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() + "\""); MedianStringType.class.getName() + "\"");
ResultSet rs = stat.executeQuery( ResultSet rs = stat.executeQuery(
"SELECT MEDIAN(X) FROM SYSTEM_RANGE(1, 9)"); "SELECT SIMPLE_MEDIAN(X) FROM SYSTEM_RANGE(1, 9)");
rs.next(); rs.next();
assertEquals("5", rs.getString(1)); assertEquals("5", rs.getString(1));
conn.close(); conn.close();
...@@ -756,22 +756,22 @@ public class TestFunctions extends TestBase implements AggregateFunction { ...@@ -756,22 +756,22 @@ public class TestFunctions extends TestBase implements AggregateFunction {
conn = getConnection("functions"); conn = getConnection("functions");
stat = conn.createStatement(); 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(); DatabaseMetaData meta = conn.getMetaData();
rs = meta.getProcedures(null, null, "MEDIAN"); rs = meta.getProcedures(null, null, "SIMPLE_MEDIAN");
assertTrue(rs.next()); assertTrue(rs.next());
assertFalse(rs.next()); assertFalse(rs.next());
rs = stat.executeQuery("SCRIPT"); rs = stat.executeQuery("SCRIPT");
boolean found = false; boolean found = false;
while (rs.next()) { while (rs.next()) {
String sql = rs.getString(1); String sql = rs.getString(1);
if (sql.contains("MEDIAN")) { if (sql.contains("SIMPLE_MEDIAN")) {
found = true; found = true;
} }
} }
assertTrue(found); assertTrue(found);
stat.execute("DROP AGGREGATE MEDIAN"); stat.execute("DROP AGGREGATE SIMPLE_MEDIAN");
stat.execute("DROP AGGREGATE IF EXISTS MEDIAN"); stat.execute("DROP AGGREGATE IF EXISTS SIMPLE_MEDIAN");
conn.close(); conn.close();
} }
...@@ -779,12 +779,12 @@ public class TestFunctions extends TestBase implements AggregateFunction { ...@@ -779,12 +779,12 @@ public class TestFunctions extends TestBase implements AggregateFunction {
deleteDb("functions"); deleteDb("functions");
Connection conn = getConnection("functions"); Connection conn = getConnection("functions");
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
stat.execute("CREATE AGGREGATE MEDIAN FOR \"" + stat.execute("CREATE AGGREGATE SIMPLE_MEDIAN FOR \"" +
MedianString.class.getName() + "\""); 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() + "\""); MedianString.class.getName() + "\"");
ResultSet rs = stat.executeQuery( ResultSet rs = stat.executeQuery(
"SELECT MEDIAN(X) FROM SYSTEM_RANGE(1, 9)"); "SELECT SIMPLE_MEDIAN(X) FROM SYSTEM_RANGE(1, 9)");
rs.next(); rs.next();
assertEquals("5", rs.getString(1)); assertEquals("5", rs.getString(1));
conn.close(); conn.close();
...@@ -795,22 +795,22 @@ public class TestFunctions extends TestBase implements AggregateFunction { ...@@ -795,22 +795,22 @@ public class TestFunctions extends TestBase implements AggregateFunction {
conn = getConnection("functions"); conn = getConnection("functions");
stat = conn.createStatement(); 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(); DatabaseMetaData meta = conn.getMetaData();
rs = meta.getProcedures(null, null, "MEDIAN"); rs = meta.getProcedures(null, null, "SIMPLE_MEDIAN");
assertTrue(rs.next()); assertTrue(rs.next());
assertFalse(rs.next()); assertFalse(rs.next());
rs = stat.executeQuery("SCRIPT"); rs = stat.executeQuery("SCRIPT");
boolean found = false; boolean found = false;
while (rs.next()) { while (rs.next()) {
String sql = rs.getString(1); String sql = rs.getString(1);
if (sql.contains("MEDIAN")) { if (sql.contains("SIMPLE_MEDIAN")) {
found = true; found = true;
} }
} }
assertTrue(found); assertTrue(found);
stat.execute("DROP AGGREGATE MEDIAN"); stat.execute("DROP AGGREGATE SIMPLE_MEDIAN");
stat.execute("DROP AGGREGATE IF EXISTS MEDIAN"); stat.execute("DROP AGGREGATE IF EXISTS SIMPLE_MEDIAN");
conn.close(); conn.close();
} }
......
...@@ -262,7 +262,8 @@ public class TestIndex extends TestBase { ...@@ -262,7 +262,8 @@ public class TestIndex extends TestBase {
c.close(); c.close();
} }
private void testConcurrentUpdateRun(ConcurrentUpdateThread[] threads, PreparedStatement check) throws SQLException { private void testConcurrentUpdateRun(ConcurrentUpdateThread[] threads, PreparedStatement check)
throws SQLException {
for (ConcurrentUpdateThread t : threads) { for (ConcurrentUpdateThread t : threads) {
t.start(); t.start();
} }
......
...@@ -15,9 +15,12 @@ import javax.naming.StringRefAddr; ...@@ -15,9 +15,12 @@ import javax.naming.StringRefAddr;
import javax.naming.spi.ObjectFactory; import javax.naming.spi.ObjectFactory;
import javax.sql.ConnectionEvent; import javax.sql.ConnectionEvent;
import javax.sql.ConnectionEventListener; import javax.sql.ConnectionEventListener;
import javax.sql.DataSource;
import javax.sql.XAConnection; import javax.sql.XAConnection;
import javax.transaction.xa.XAResource; import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid; import javax.transaction.xa.Xid;
import org.h2.api.ErrorCode;
import org.h2.jdbcx.JdbcDataSource; import org.h2.jdbcx.JdbcDataSource;
import org.h2.jdbcx.JdbcDataSourceFactory; import org.h2.jdbcx.JdbcDataSourceFactory;
import org.h2.jdbcx.JdbcXAConnection; import org.h2.jdbcx.JdbcXAConnection;
...@@ -71,6 +74,7 @@ public class TestDataSource extends TestBase { ...@@ -71,6 +74,7 @@ public class TestDataSource extends TestBase {
} }
testDataSourceFactory(); testDataSourceFactory();
testDataSource(); testDataSource();
testUnwrap();
testXAConnection(); testXAConnection();
deleteDb("dataSource"); deleteDb("dataSource");
} }
...@@ -190,4 +194,20 @@ public class TestDataSource extends TestBase { ...@@ -190,4 +194,20 @@ public class TestDataSource extends TestBase {
conn.close(); 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 { ...@@ -99,7 +99,7 @@ public class TestScript extends TestBase {
testScript("datatypes/" + s + ".sql"); testScript("datatypes/" + s + ".sql");
} }
for (String s : new String[] { "avg", "bit-and", "bit-or", "count", 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" }) { "stddev-samp", "sum", "var-pop", "var-samp" }) {
testScript("functions/aggregate/" + s + ".sql"); testScript("functions/aggregate/" + s + ".sql");
} }
......
...@@ -139,7 +139,7 @@ public class TestTools extends TestBase { ...@@ -139,7 +139,7 @@ public class TestTools extends TestBase {
c.runTool("-web", "-webPort", "9002", "-tool", "-browser", "-tcp", c.runTool("-web", "-webPort", "9002", "-tool", "-browser", "-tcp",
"-tcpPort", "9003", "-pg", "-pgPort", "9004"); "-tcpPort", "9003", "-pg", "-pgPort", "9004");
assertContains(lastUrl, ":9002"); assertContains(lastUrl, ":9002");
c.shutdown(); shutdownConsole(c);
// check if starting the browser works // check if starting the browser works
c.runTool("-web", "-webPort", "9002", "-tool"); c.runTool("-web", "-webPort", "9002", "-tool");
...@@ -169,7 +169,7 @@ public class TestTools extends TestBase { ...@@ -169,7 +169,7 @@ public class TestTools extends TestBase {
// ignore // ignore
} }
c.shutdown(); shutdownConsole(c);
// trying to use the same port for two services should fail, // trying to use the same port for two services should fail,
// but also stop the first service // but also stop the first service
...@@ -184,7 +184,19 @@ public class TestTools extends TestBase { ...@@ -184,7 +184,19 @@ public class TestTools extends TestBase {
} else { } else {
System.clearProperty(SysProperties.H2_BROWSER); System.clearProperty(SysProperties.H2_BROWSER);
} }
shutdownConsole(c);
}
}
private static void shutdownConsole(Console c) {
c.shutdown(); 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 { ...@@ -307,10 +307,10 @@ public class TestValue extends TestBase {
} }
private void testTimestamp() { 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"); Timestamp ts = Timestamp.valueOf("2000-01-15 10:20:30.333222111");
assertEquals(ts.toString(), vts.getString()); assertEquals(ts.toString(), valueTs.getString());
assertEquals(ts, vts.getTimestamp()); assertEquals(ts, valueTs.getTimestamp());
Calendar c = Calendar.getInstance(TimeZone.getTimeZone("Europe/Berlin")); Calendar c = Calendar.getInstance(TimeZone.getTimeZone("Europe/Berlin"));
c.set(2018, 02, 25, 1, 59, 00); c.set(2018, 02, 25, 1, 59, 00);
c.set(Calendar.MILLISECOND, 123); c.set(Calendar.MILLISECOND, 123);
......
...@@ -762,3 +762,4 @@ assorted reimplemented hangups confirmation predefined ...@@ -762,3 +762,4 @@ assorted reimplemented hangups confirmation predefined
mdy destfile hclf forbids spellchecking selfdestruct expects accident jacocoagent cli historic mitigate 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 jacoco xdata invokes sourcefiles classfiles duplication crypto stacktraces prt directions handled overly asm hardcoded
interpolated thead
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论