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);
} }
......
/*
* Copyright 2004-2018 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.expression;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.index.Cursor;
import org.h2.index.Index;
import org.h2.result.SearchRow;
import org.h2.result.SortOrder;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.util.DateTimeUtils;
import org.h2.value.CompareMode;
import org.h2.value.Value;
import org.h2.value.ValueDate;
import org.h2.value.ValueDecimal;
import org.h2.value.ValueDouble;
import org.h2.value.ValueFloat;
import org.h2.value.ValueInt;
import org.h2.value.ValueLong;
import org.h2.value.ValueNull;
import org.h2.value.ValueTime;
import org.h2.value.ValueTimestamp;
import org.h2.value.ValueTimestampTimeZone;
/**
* Data stored while calculating a MEDIAN aggregate.
*/
class AggregateDataMedian extends AggregateData {
private Collection<Value> values;
private static boolean isNullsLast(Index index) {
IndexColumn ic = index.getIndexColumns()[0];
int sortType = ic.sortType;
return (sortType & SortOrder.NULLS_LAST) != 0
|| (sortType & SortOrder.DESCENDING) != 0 && (sortType & SortOrder.NULLS_FIRST) == 0;
}
static Index getMedianColumnIndex(Expression on) {
if (on instanceof ExpressionColumn) {
ExpressionColumn col = (ExpressionColumn) on;
Column column = col.getColumn();
TableFilter filter = col.getTableFilter();
if (filter != null) {
Table table = filter.getTable();
ArrayList<Index> indexes = table.getIndexes();
Index result = null;
if (indexes != null) {
boolean nullable = column.isNullable();
for (int i = 1, size = indexes.size(); i < size; i++) {
Index index = indexes.get(i);
if (!index.canFindNext()) {
continue;
}
if (!index.isFirstColumn(column)) {
continue;
}
if (result == null || result.getColumns().length > index.getColumns().length
// Prefer index without nulls last for nullable columns
|| nullable && isNullsLast(result) && !isNullsLast(index)) {
result = index;
}
}
}
return result;
}
}
return null;
}
static Value getFromIndex(Session session, Expression on, int dataType) {
Index index = getMedianColumnIndex(on);
long count = index.getRowCount(session);
if (count == 0) {
return ValueNull.INSTANCE;
}
Cursor cursor = index.find(session, null, null);
cursor.next();
int columnId = index.getColumns()[0].getColumnId();
ExpressionColumn expr = (ExpressionColumn) on;
if (expr.getColumn().isNullable()) {
boolean hasNulls = false;
SearchRow row;
/*
* Try to skip nulls from the start first with the same cursor that will be used
* to read values.
*/
while (count > 0) {
row = cursor.getSearchRow();
if (row == null) {
return ValueNull.INSTANCE;
}
if (row.getValue(columnId) == ValueNull.INSTANCE) {
count--;
cursor.next();
hasNulls = true;
} else
break;
}
if (count == 0) {
return ValueNull.INSTANCE;
}
/*
* If no nulls found and if index orders nulls last create a second cursor to
* count nulls at the end.
*/
if (!hasNulls && isNullsLast(index)) {
TableFilter tableFilter = expr.getTableFilter();
SearchRow check = tableFilter.getTable().getTemplateSimpleRow(true);
check.setValue(columnId, ValueNull.INSTANCE);
Cursor nullsCursor = index.find(session, check, check);
while (nullsCursor.next()) {
count--;
}
if (count <= 0) {
return ValueNull.INSTANCE;
}
}
}
long skip = (count - 1) / 2;
for (int i = 0; i < skip; i++) {
cursor.next();
}
SearchRow row = cursor.getSearchRow();
if (row == null) {
return ValueNull.INSTANCE;
}
Value v = row.getValue(columnId);
if (v == ValueNull.INSTANCE) {
return v;
}
if ((count & 1) == 0) {
cursor.next();
row = cursor.getSearchRow();
if (row == null) {
return v;
}
Value v2 = row.getValue(columnId);
if (v2 == ValueNull.INSTANCE) {
return v;
}
return getMedian(v, v2, dataType, session.getDatabase().getCompareMode());
}
return v;
}
@Override
void add(Database database, int dataType, boolean distinct, Value v) {
if (v == ValueNull.INSTANCE) {
return;
}
Collection<Value> c = values;
if (c == null) {
values = c = distinct ? new HashSet<Value>() : new ArrayList<Value>();
}
c.add(v);
}
@Override
Value getValue(Database database, int dataType, boolean distinct) {
Collection<Value> c = values;
// Non-null collection cannot be empty here
if (c == null) {
return ValueNull.INSTANCE;
}
if (distinct && c instanceof ArrayList) {
c = new HashSet<>(c);
}
Value[] a = c.toArray(new Value[0]);
final CompareMode mode = database.getCompareMode();
Arrays.sort(a, new Comparator<Value>() {
@Override
public int compare(Value o1, Value o2) {
return o1.compareTo(o2, mode);
}
});
int len = a.length;
int idx = len / 2;
Value v1 = a[idx];
if ((len & 1) == 1) {
return v1.convertTo(dataType);
}
return getMedian(a[idx - 1], v1, dataType, mode);
}
static Value getMedian(Value v0, Value v1, int dataType, CompareMode mode) {
if (v0.compareTo(v1, mode) == 0) {
return v0.convertTo(dataType);
}
switch (dataType) {
case Value.BYTE:
case Value.SHORT:
case Value.INT:
return ValueInt.get((v0.getInt() + v1.getInt()) / 2).convertTo(dataType);
case Value.LONG:
return ValueLong.get((v0.getLong() + v1.getLong()) / 2);
case Value.DECIMAL:
return ValueDecimal.get(v0.getBigDecimal().add(v1.getBigDecimal()).divide(BigDecimal.valueOf(2)));
case Value.FLOAT:
return ValueFloat.get((v0.getFloat() + v1.getFloat()) / 2);
case Value.DOUBLE:
return ValueDouble.get((v0.getFloat() + v1.getDouble()) / 2);
case Value.TIME: {
return ValueTime.fromMillis((v0.getTime().getTime() + v1.getTime().getTime()) / 2);
}
case Value.DATE: {
ValueDate d0 = (ValueDate) v0.convertTo(Value.DATE), d1 = (ValueDate) v1.convertTo(Value.DATE);
return ValueDate.fromDateValue(
DateTimeUtils.dateValueFromAbsoluteDay((DateTimeUtils.absoluteDayFromDateValue(d0.getDateValue())
+ DateTimeUtils.absoluteDayFromDateValue(d1.getDateValue())) / 2));
}
case Value.TIMESTAMP: {
ValueTimestamp ts0 = (ValueTimestamp) v0.convertTo(Value.TIMESTAMP),
ts1 = (ValueTimestamp) v1.convertTo(Value.TIMESTAMP);
long dateSum = DateTimeUtils.absoluteDayFromDateValue(ts0.getDateValue())
+ DateTimeUtils.absoluteDayFromDateValue(ts1.getDateValue());
long nanos = (ts0.getTimeNanos() + ts1.getTimeNanos()) / 2;
if ((dateSum & 1) != 0) {
nanos += DateTimeUtils.NANOS_PER_DAY / 2;
if (nanos >= DateTimeUtils.NANOS_PER_DAY) {
nanos -= DateTimeUtils.NANOS_PER_DAY;
dateSum++;
}
}
return ValueTimestamp.fromDateValueAndNanos(DateTimeUtils.dateValueFromAbsoluteDay(dateSum / 2), nanos);
}
case Value.TIMESTAMP_TZ: {
ValueTimestampTimeZone ts0 = (ValueTimestampTimeZone) v0.convertTo(Value.TIMESTAMP_TZ),
ts1 = (ValueTimestampTimeZone) v1.convertTo(Value.TIMESTAMP_TZ);
long dateSum = DateTimeUtils.absoluteDayFromDateValue(ts0.getDateValue())
+ DateTimeUtils.absoluteDayFromDateValue(ts1.getDateValue());
long nanos = (ts0.getTimeNanos() + ts1.getTimeNanos()) / 2;
int offset = ts0.getTimeZoneOffsetMins() + ts1.getTimeZoneOffsetMins();
if ((dateSum & 1) != 0) {
nanos += DateTimeUtils.NANOS_PER_DAY / 2;
}
if ((offset & 1) != 0) {
nanos += 30L * 1000000000;
}
if (nanos >= DateTimeUtils.NANOS_PER_DAY) {
nanos -= DateTimeUtils.NANOS_PER_DAY;
dateSum++;
}
return ValueTimestampTimeZone.fromDateValueAndNanos(DateTimeUtils.dateValueFromAbsoluteDay(dateSum / 2),
nanos, (short) (offset / 2));
}
default:
// Just return first
return v0.convertTo(dataType);
}
}
}
...@@ -27,38 +27,38 @@ public class Operation extends Expression { ...@@ -27,38 +27,38 @@ public class Operation extends Expression {
* This operation represents a string concatenation as in * This operation represents a string concatenation as in
* 'Hello' || 'World'. * 'Hello' || 'World'.
*/ */
CONCAT, CONCAT,
/** /**
* This operation represents an addition as in 1 + 2. * This operation represents an addition as in 1 + 2.
*/ */
PLUS, PLUS,
/** /**
* This operation represents a subtraction as in 2 - 1. * This operation represents a subtraction as in 2 - 1.
*/ */
MINUS, MINUS,
/** /**
* This operation represents a multiplication as in 2 * 3. * This operation represents a multiplication as in 2 * 3.
*/ */
MULTIPLY, MULTIPLY,
/** /**
* This operation represents a division as in 4 * 2. * This operation represents a division as in 4 * 2.
*/ */
DIVIDE, DIVIDE,
/** /**
* This operation represents a negation as in - ID. * This operation represents a negation as in - ID.
*/ */
NEGATE, NEGATE,
/** /**
* 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,27 +559,12 @@ public class DateTimeUtils { ...@@ -547,27 +559,12 @@ 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);
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, 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) {
if (year <= 0) { if (year <= 0) {
...@@ -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();
} 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); switch (field) {
if (field == Calendar.MONTH) { case Calendar.YEAR:
return value + 1; 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; ...@@ -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");
} }
......
-- Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
-- and the EPL 1.0 (http://h2database.com/html/license.html).
-- Initial Developer: H2 Group
--
-- ASC
create table test(v tinyint);
> ok
create index test_idx on test(v asc);
> ok
insert into test values (20), (20), (10);
> update count: 3
select median(v) from test;
> MEDIAN(V)
> ---------
> 20
insert into test values (null);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 20
select median(distinct v) from test;
> MEDIAN(DISTINCT V)
> ------------------
> 15
insert into test values (10);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 15
drop table test;
> ok
-- ASC NULLS FIRST
create table test(v tinyint);
> ok
create index test_idx on test(v asc nulls first);
> ok
insert into test values (20), (20), (10);
> update count: 3
select median(v) from test;
> MEDIAN(V)
> ---------
> 20
insert into test values (null);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 20
select median(distinct v) from test;
> MEDIAN(DISTINCT V)
> ------------------
> 15
insert into test values (10);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 15
drop table test;
> ok
-- ASC NULLS LAST
create table test(v tinyint);
> ok
create index test_idx on test(v asc nulls last);
> ok
insert into test values (20), (20), (10);
> update count: 3
select median(v) from test;
> MEDIAN(V)
> ---------
> 20
insert into test values (null);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 20
select median(distinct v) from test;
> MEDIAN(DISTINCT V)
> ------------------
> 15
insert into test values (10);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 15
drop table test;
> ok
-- DESC
create table test(v tinyint);
> ok
create index test_idx on test(v desc);
> ok
insert into test values (20), (20), (10);
> update count: 3
select median(v) from test;
> MEDIAN(V)
> ---------
> 20
insert into test values (null);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 20
select median(distinct v) from test;
> MEDIAN(DISTINCT V)
> ------------------
> 15
insert into test values (10);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 15
drop table test;
> ok
-- DESC NULLS FIRST
create table test(v tinyint);
> ok
create index test_idx on test(v desc nulls first);
> ok
insert into test values (20), (20), (10);
> update count: 3
select median(v) from test;
> MEDIAN(V)
> ---------
> 20
insert into test values (null);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 20
select median(distinct v) from test;
> MEDIAN(DISTINCT V)
> ------------------
> 15
insert into test values (10);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 15
drop table test;
> ok
-- DESC NULLS LAST
create table test(v tinyint);
> ok
create index test_idx on test(v desc nulls last);
> ok
insert into test values (20), (20), (10);
> update count: 3
select median(v) from test;
> MEDIAN(V)
> ---------
> 20
insert into test values (null);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 20
select median(distinct v) from test;
> MEDIAN(DISTINCT V)
> ------------------
> 15
insert into test values (10);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 15
drop table test;
> ok
create table test(v tinyint);
> ok
insert into test values (20), (20), (10);
> update count: 3
select median(v) from test;
> MEDIAN(V)
> ---------
> 20
insert into test values (null);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 20
select median(distinct v) from test;
> MEDIAN(DISTINCT V)
> ------------------
> 15
insert into test values (10);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 15
drop table test;
> ok
create table test(v smallint);
> ok
insert into test values (20), (20), (10);
> update count: 3
select median(v) from test;
> MEDIAN(V)
> ---------
> 20
insert into test values (null);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 20
select median(distinct v) from test;
> MEDIAN(DISTINCT V)
> ------------------
> 15
insert into test values (10);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 15
drop table test;
> ok
create table test(v int);
> ok
insert into test values (20), (20), (10);
> update count: 3
select median(v) from test;
> MEDIAN(V)
> ---------
> 20
insert into test values (null);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 20
select median(distinct v) from test;
> MEDIAN(DISTINCT V)
> ------------------
> 15
insert into test values (10);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 15
drop table test;
> ok
create table test(v bigint);
> ok
insert into test values (20), (20), (10);
> update count: 3
select median(v) from test;
> MEDIAN(V)
> ---------
> 20
insert into test values (null);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 20
select median(distinct v) from test;
> MEDIAN(DISTINCT V)
> ------------------
> 15
insert into test values (10);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 15
drop table test;
> ok
create table test(v real);
> ok
insert into test values (2), (2), (1);
> update count: 3
select median(v) from test;
> MEDIAN(V)
> ---------
> 2.0
insert into test values (null);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 2.0
select median(distinct v) from test;
> MEDIAN(DISTINCT V)
> ------------------
> 1.5
insert into test values (1);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 1.5
drop table test;
> ok
create table test(v double);
> ok
insert into test values (2), (2), (1);
> update count: 3
select median(v) from test;
> MEDIAN(V)
> ---------
> 2.0
insert into test values (null);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 2.0
select median(distinct v) from test;
> MEDIAN(DISTINCT V)
> ------------------
> 1.5
insert into test values (1);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 1.5
drop table test;
> ok
create table test(v numeric(1));
> ok
insert into test values (2), (2), (1);
> update count: 3
select median(v) from test;
> MEDIAN(V)
> ---------
> 2
insert into test values (null);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 2
select median(distinct v) from test;
> MEDIAN(DISTINCT V)
> ------------------
> 1.5
insert into test values (1);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 1.5
drop table test;
> ok
create table test(v time);
> ok
insert into test values ('20:00:00'), ('20:00:00'), ('10:00:00');
> update count: 3
select median(v) from test;
> MEDIAN(V)
> ---------
> 20:00:00
insert into test values (null);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 20:00:00
select median(distinct v) from test;
> MEDIAN(DISTINCT V)
> ------------------
> 15:00:00
insert into test values ('10:00:00');
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------
> 15:00:00
drop table test;
> ok
create table test(v date);
> ok
insert into test values ('2000-01-20'), ('2000-01-20'), ('2000-01-10');
> update count: 3
select median(v) from test;
> MEDIAN(V)
> ----------
> 2000-01-20
insert into test values (null);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ----------
> 2000-01-20
select median(distinct v) from test;
> MEDIAN(DISTINCT V)
> ------------------
> 2000-01-15
insert into test values ('2000-01-10');
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ----------
> 2000-01-15
drop table test;
> ok
create table test(v timestamp);
> ok
insert into test values ('2000-01-20 20:00:00'), ('2000-01-20 20:00:00'), ('2000-01-10 10:00:00');
> update count: 3
select median(v) from test;
> MEDIAN(V)
> ---------------------
> 2000-01-20 20:00:00.0
insert into test values (null);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------------------
> 2000-01-20 20:00:00.0
select median(distinct v) from test;
> MEDIAN(DISTINCT V)
> ---------------------
> 2000-01-15 15:00:00.0
insert into test values ('2000-01-10 10:00:00');
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ---------------------
> 2000-01-15 15:00:00.0
delete from test;
> update count: 5
insert into test values ('2000-01-20 20:00:00'), ('2000-01-21 20:00:00');
> update count: 2
select median(v) from test;
> MEDIAN(V)
> ---------------------
> 2000-01-21 08:00:00.0
drop table test;
> ok
create table test(v timestamp with time zone);
> ok
insert into test values ('2000-01-20 20:00:00+04'), ('2000-01-20 20:00:00+04'), ('2000-01-10 10:00:00+02');
> update count: 3
select median(v) from test;
> MEDIAN(V)
> ------------------------
> 2000-01-20 20:00:00.0+04
insert into test values (null);
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ------------------------
> 2000-01-20 20:00:00.0+04
select median(distinct v) from test;
> MEDIAN(DISTINCT V)
> ------------------------
> 2000-01-15 15:00:00.0+03
insert into test values ('2000-01-10 10:00:00+02');
> update count: 1
select median(v) from test;
> MEDIAN(V)
> ------------------------
> 2000-01-15 15:00:00.0+03
delete from test;
> update count: 5
insert into test values ('2000-01-20 20:00:00+10:15'), ('2000-01-21 20:00:00-09');
> update count: 2
select median(v) from test;
> MEDIAN(V)
> ---------------------------
> 2000-01-21 08:00:30.0+00:37
drop table test;
> ok
-- with group by
create table test(name varchar, value int);
> ok
insert into test values ('Group 2A', 10), ('Group 2A', 10), ('Group 2A', 20),
('Group 1X', 40), ('Group 1X', 50), ('Group 3B', null);
> update count: 6
select name, median(value) from test group by name order by name;
> NAME MEDIAN(VALUE)
> -------- -------------
> Group 1X 45
> Group 2A 10
> Group 3B null
> rows (ordered): 3
drop table test;
> ok
-- with filter
create table test(v int);
> ok
insert into test values (20), (20), (10);
> update count: 3
select median(v) from test where v <> 20;
> MEDIAN(V)
> ---------
> 10
create index test_idx on test(v asc);
> ok
select median(v) from test where v <> 20;
> MEDIAN(V)
> ---------
> 10
drop table test;
> ok
-- two-column index
create table test(v int, v2 int);
> ok
create index test_idx on test(v, v2);
> ok
insert into test values (20, 1), (10, 2), (20, 3);
> update count: 3
select median(v) from test;
> MEDIAN(V)
> ---------
> 20
drop table test;
> ok
-- not null column
create table test (v int not null);
> ok
create index test_idx on test(v desc);
> ok
select median(v) from test;
> MEDIAN(V)
> ---------
> null
insert into test values (10), (20);
> update count: 2
select median(v) from test;
> MEDIAN(V)
> ---------
> 15
insert into test values (20), (10), (20);
> update count: 3
select median(v) from test;
> MEDIAN(V)
> ---------
> 20
drop table test;
> ok
...@@ -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);
} }
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 { ...@@ -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);
......
...@@ -760,5 +760,6 @@ zzbbzz cldr booleans maria enquotes mtc cbuf checksummed nreturn despite bbzz re ...@@ -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 unconditionally coco aren eecccc decimals charsets zzbb lsb msb usecount outdir endian misleading precompiled
assorted reimplemented hangups confirmation predefined 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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论