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

Merge branch 'master' into scay-issue#832

......@@ -228,6 +228,7 @@ WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
exec "$JAVACMD" \
$MAVEN_OPTS \
-Djava.net.useSystemProxies=true \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CMD_LINE_ARGS
......
......@@ -121,7 +121,7 @@ SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR=""%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar""
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS%
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -Djava.net.useSystemProxies=true -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS%
if ERRORLEVEL 1 goto error
goto end
......
......@@ -222,6 +222,7 @@
</executions>
</plugin>
<!-- Make sure neither we nor one of our dependencies uses anything outside JDK 1.7 -->
<!-- commented out for now because it fails to build under JDK1.8+ because we are manually adding the tools jar
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
......@@ -262,6 +263,7 @@
</dependency>
</dependencies>
</plugin>
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
......
......@@ -3685,6 +3685,7 @@ Returns the the number of crossed unit boundaries between two timestamps.
This method returns a long.
The string indicates the unit.
The same units as in the EXTRACT function are supported.
If timestamps have time zone offset component it is ignored.
","
DATEDIFF('YEAR', T1.CREATED, T2.CREATED)
"
......@@ -3722,7 +3723,7 @@ DAY_OF_YEAR(CREATED)
"
"Functions (Time and Date)","EXTRACT","
EXTRACT ( { YEAR | YY | MONTH | MM | WEEK | DAY | DD | DAY_OF_YEAR
EXTRACT ( { YEAR | YY | MONTH | MM | WEEK | ISO_WEEK | DAY | DD | DAY_OF_YEAR
| DOY | HOUR | HH | MINUTE | MI | SECOND | SS | MILLISECOND | MS }
FROM timestamp )
","
......
......@@ -490,7 +490,7 @@ Instead, use a prepared statement with arrays as in the following example:
</p>
<pre>
PreparedStatement prep = conn.prepareStatement(
"SELECT * FROM TABLE(X INT=?) T INNER JOIN TEST ON T.X=TEST.ID");
"SELECT * TEST.ID = ANY(?)");
prep.setObject(1, new Object[] { "1", "2" });
ResultSet rs = prep.executeQuery();
</pre>
......
......@@ -112,6 +112,7 @@ import org.h2.expression.Comparison;
import org.h2.expression.ConditionAndOr;
import org.h2.expression.ConditionExists;
import org.h2.expression.ConditionIn;
import org.h2.expression.ConditionInParameter;
import org.h2.expression.ConditionInSelect;
import org.h2.expression.ConditionNot;
import org.h2.expression.Expression;
......@@ -2504,9 +2505,14 @@ public class Parser {
read(")");
} else if (readIf("ANY") || readIf("SOME")) {
read("(");
Query query = parseSelect();
r = new ConditionInSelect(database, r, query, false,
compareType);
if (currentTokenType == PARAMETER && compareType == 0) {
Parameter p = readParameter();
r = new ConditionInParameter(database, r, p);
} else {
Query query = parseSelect();
r = new ConditionInSelect(database, r, query, false,
compareType);
}
read(")");
} else {
Expression right = readConcat();
......@@ -3001,6 +3007,53 @@ public class Parser {
return new ExpressionColumn(database, null, objectName, name);
}
private Parameter readParameter() {
// there must be no space between ? and the number
boolean indexed = Character.isDigit(sqlCommandChars[parseIndex]);
Parameter p;
if (indexed) {
readParameterIndex();
if (indexedParameterList == null) {
if (parameters == null) {
// this can occur when parsing expressions only (for
// example check constraints)
throw getSyntaxError();
} else if (parameters.size() > 0) {
throw DbException
.get(ErrorCode.CANNOT_MIX_INDEXED_AND_UNINDEXED_PARAMS);
}
indexedParameterList = New.arrayList();
}
int index = currentValue.getInt() - 1;
if (index < 0 || index >= Constants.MAX_PARAMETER_INDEX) {
throw DbException.getInvalidValueException(
"parameter index", index);
}
if (indexedParameterList.size() <= index) {
indexedParameterList.ensureCapacity(index + 1);
while (indexedParameterList.size() <= index) {
indexedParameterList.add(null);
}
}
p = indexedParameterList.get(index);
if (p == null) {
p = new Parameter(index);
indexedParameterList.set(index, p);
}
read();
} else {
read();
if (indexedParameterList != null) {
throw DbException
.get(ErrorCode.CANNOT_MIX_INDEXED_AND_UNINDEXED_PARAMS);
}
p = new Parameter(parameters.size());
}
parameters.add(p);
return p;
}
private Expression readTerm() {
Expression r;
switch (currentTokenType) {
......@@ -3016,50 +3069,7 @@ public class Parser {
}
break;
case PARAMETER:
// there must be no space between ? and the number
boolean indexed = Character.isDigit(sqlCommandChars[parseIndex]);
Parameter p;
if (indexed) {
readParameterIndex();
if (indexedParameterList == null) {
if (parameters == null) {
// this can occur when parsing expressions only (for
// example check constraints)
throw getSyntaxError();
} else if (parameters.size() > 0) {
throw DbException
.get(ErrorCode.CANNOT_MIX_INDEXED_AND_UNINDEXED_PARAMS);
}
indexedParameterList = New.arrayList();
}
int index = currentValue.getInt() - 1;
if (index < 0 || index >= Constants.MAX_PARAMETER_INDEX) {
throw DbException.getInvalidValueException(
"parameter index", index);
}
if (indexedParameterList.size() <= index) {
indexedParameterList.ensureCapacity(index + 1);
while (indexedParameterList.size() <= index) {
indexedParameterList.add(null);
}
}
p = indexedParameterList.get(index);
if (p == null) {
p = new Parameter(index);
indexedParameterList.set(index, p);
}
read();
} else {
read();
if (indexedParameterList != null) {
throw DbException
.get(ErrorCode.CANNOT_MIX_INDEXED_AND_UNINDEXED_PARAMS);
}
p = new Parameter(parameters.size());
}
parameters.add(p);
r = p;
r = readParameter();
break;
case KEYWORD:
if (isToken("SELECT") || isToken("FROM") || isToken("WITH")) {
......
......@@ -44,7 +44,11 @@ import java.util.HashSet;
*/
public class Select extends Query {
/**
* The main (top) table filter.
*/
TableFilter topTableFilter;
private final ArrayList<TableFilter> filters = New.arrayList();
private final ArrayList<TableFilter> topFilters = New.arrayList();
......
/*
* 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.util.AbstractList;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.index.IndexCondition;
import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
import org.h2.value.Value;
import org.h2.value.ValueArray;
import org.h2.value.ValueBoolean;
import org.h2.value.ValueNull;
/**
* A condition with parameter as {@code = ANY(?)}.
*/
public class ConditionInParameter extends Condition {
private static final class ParameterList extends AbstractList<Expression> {
private final Parameter parameter;
ParameterList(Parameter parameter) {
this.parameter = parameter;
}
@Override
public Expression get(int index) {
Value value = parameter.getParamValue();
if (value instanceof ValueArray) {
return ValueExpression.get(((ValueArray) value).getList()[index]);
}
if (index != 0) {
throw new IndexOutOfBoundsException();
}
return ValueExpression.get(value);
}
@Override
public int size() {
if (!parameter.isValueSet()) {
return 0;
}
Value value = parameter.getParamValue();
if (value instanceof ValueArray) {
return ((ValueArray) value).getList().length;
}
return 1;
}
}
private final Database database;
private Expression left;
final Parameter parameter;
/**
* Create a new {@code = ANY(?)} condition.
*
* @param database
* the database
* @param left
* the expression before {@code = ANY(?)}
* @param parameter
* parameter
*/
public ConditionInParameter(Database database, Expression left, Parameter parameter) {
this.database = database;
this.left = left;
this.parameter = parameter;
}
@Override
public Value getValue(Session session) {
Value l = left.getValue(session);
if (l == ValueNull.INSTANCE) {
return l;
}
boolean result = false;
boolean hasNull = false;
Value value = parameter.getValue(session);
if (value instanceof ValueArray) {
for (Value r : ((ValueArray) value).getList()) {
if (r == ValueNull.INSTANCE) {
hasNull = true;
} else {
r = r.convertTo(l.getType());
result = Comparison.compareNotNull(database, l, r, Comparison.EQUAL);
if (result) {
break;
}
}
}
} else {
if (value == ValueNull.INSTANCE) {
hasNull = true;
} else {
value = value.convertTo(l.getType());
result = Comparison.compareNotNull(database, l, value, Comparison.EQUAL);
}
}
if (!result && hasNull) {
return ValueNull.INSTANCE;
}
return ValueBoolean.get(result);
}
@Override
public void mapColumns(ColumnResolver resolver, int level) {
left.mapColumns(resolver, level);
}
@Override
public Expression optimize(Session session) {
left = left.optimize(session);
if (left.isConstant() && left == ValueExpression.getNull()) {
return left;
}
return this;
}
@Override
public void createIndexConditions(Session session, TableFilter filter) {
if (!(left instanceof ExpressionColumn)) {
return;
}
ExpressionColumn l = (ExpressionColumn) left;
if (filter != l.getTableFilter()) {
return;
}
filter.addIndexCondition(IndexCondition.getInList(l, new ParameterList(parameter)));
}
@Override
public void setEvaluatable(TableFilter tableFilter, boolean b) {
left.setEvaluatable(tableFilter, b);
}
@Override
public String getSQL() {
return '(' + left.getSQL() + " = ANY(" + parameter.getSQL() + "))";
}
@Override
public void updateAggregate(Session session) {
left.updateAggregate(session);
}
@Override
public boolean isEverything(ExpressionVisitor visitor) {
return left.isEverything(visitor) && parameter.isEverything(visitor);
}
@Override
public int getCost() {
return left.getCost();
}
}
......@@ -20,7 +20,6 @@ import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Locale;
import java.util.TimeZone;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
......@@ -108,6 +107,11 @@ public class Function extends Expression implements FunctionCall {
FORMATDATETIME = 121, PARSEDATETIME = 122, ISO_YEAR = 123,
ISO_WEEK = 124, ISO_DAY_OF_WEEK = 125, EPOCH = 126;
/**
* Pseudo function for {@code EXTRACT(MILLISECOND FROM ...)}.
*/
public static final int MILLISECOND = 126;
public static final int DATABASE = 150, USER = 151, CURRENT_USER = 152,
IDENTITY = 153, SCOPE_IDENTITY = 154, AUTOCOMMIT = 155,
READONLY = 156, DATABASE_PATH = 157, LOCK_TIMEOUT = 158,
......@@ -158,40 +162,40 @@ public class Function extends Expression implements FunctionCall {
static {
// DATE_PART
DATE_PART.put("SQL_TSI_YEAR", Calendar.YEAR);
DATE_PART.put("YEAR", Calendar.YEAR);
DATE_PART.put("YYYY", Calendar.YEAR);
DATE_PART.put("YY", Calendar.YEAR);
DATE_PART.put("SQL_TSI_MONTH", Calendar.MONTH);
DATE_PART.put("MONTH", Calendar.MONTH);
DATE_PART.put("MM", Calendar.MONTH);
DATE_PART.put("M", Calendar.MONTH);
DATE_PART.put("SQL_TSI_WEEK", Calendar.WEEK_OF_YEAR);
DATE_PART.put("WW", Calendar.WEEK_OF_YEAR);
DATE_PART.put("WK", Calendar.WEEK_OF_YEAR);
DATE_PART.put("WEEK", Calendar.WEEK_OF_YEAR);
DATE_PART.put("DAY", Calendar.DAY_OF_MONTH);
DATE_PART.put("DD", Calendar.DAY_OF_MONTH);
DATE_PART.put("D", Calendar.DAY_OF_MONTH);
DATE_PART.put("SQL_TSI_DAY", Calendar.DAY_OF_MONTH);
DATE_PART.put("DAYOFYEAR", Calendar.DAY_OF_YEAR);
DATE_PART.put("DAY_OF_YEAR", Calendar.DAY_OF_YEAR);
DATE_PART.put("DY", Calendar.DAY_OF_YEAR);
DATE_PART.put("DOY", Calendar.DAY_OF_YEAR);
DATE_PART.put("SQL_TSI_HOUR", Calendar.HOUR_OF_DAY);
DATE_PART.put("HOUR", Calendar.HOUR_OF_DAY);
DATE_PART.put("HH", Calendar.HOUR_OF_DAY);
DATE_PART.put("SQL_TSI_MINUTE", Calendar.MINUTE);
DATE_PART.put("MINUTE", Calendar.MINUTE);
DATE_PART.put("MI", Calendar.MINUTE);
DATE_PART.put("N", Calendar.MINUTE);
DATE_PART.put("SQL_TSI_SECOND", Calendar.SECOND);
DATE_PART.put("SECOND", Calendar.SECOND);
DATE_PART.put("SS", Calendar.SECOND);
DATE_PART.put("S", Calendar.SECOND);
DATE_PART.put("MILLISECOND", Calendar.MILLISECOND);
DATE_PART.put("MS", Calendar.MILLISECOND);
DATE_PART.put("EPOCH", EPOCH);
DATE_PART.put("SQL_TSI_YEAR", YEAR);
DATE_PART.put("YEAR", YEAR);
DATE_PART.put("YYYY", YEAR);
DATE_PART.put("YY", YEAR);
DATE_PART.put("SQL_TSI_MONTH", MONTH);
DATE_PART.put("MONTH", MONTH);
DATE_PART.put("MM", MONTH);
DATE_PART.put("M", MONTH);
DATE_PART.put("SQL_TSI_WEEK", WEEK);
DATE_PART.put("WW", WEEK);
DATE_PART.put("WK", WEEK);
DATE_PART.put("WEEK", WEEK);
DATE_PART.put("ISO_WEEK", ISO_WEEK);
DATE_PART.put("DAY", DAY_OF_MONTH);
DATE_PART.put("DD", DAY_OF_MONTH);
DATE_PART.put("D", DAY_OF_MONTH);
DATE_PART.put("SQL_TSI_DAY", DAY_OF_MONTH);
DATE_PART.put("DAYOFYEAR", DAY_OF_YEAR);
DATE_PART.put("DAY_OF_YEAR", DAY_OF_YEAR);
DATE_PART.put("DY", DAY_OF_YEAR);
DATE_PART.put("DOY", DAY_OF_YEAR);
DATE_PART.put("SQL_TSI_HOUR", HOUR);
DATE_PART.put("HOUR", HOUR);
DATE_PART.put("HH", HOUR);
DATE_PART.put("SQL_TSI_MINUTE", MINUTE);
DATE_PART.put("MINUTE", MINUTE);
DATE_PART.put("MI", MINUTE);
DATE_PART.put("N", MINUTE);
DATE_PART.put("SQL_TSI_SECOND", SECOND);
DATE_PART.put("SECOND", SECOND);
DATE_PART.put("SS", SECOND);
DATE_PART.put("S", SECOND);
DATE_PART.put("MILLISECOND", MILLISECOND);
DATE_PART.put("MS", MILLISECOND);
// SOUNDEX_INDEX
String index = "7AEIOUY8HW1BFPV2CGJKQSXZ3DT4L5MN6R";
......@@ -838,22 +842,19 @@ public class Function extends Expression implements FunctionCall {
break;
}
case DAY_OF_MONTH:
result = ValueLong.get(DateTimeUtils.getDatePart(v0, Calendar.DAY_OF_MONTH));
break;
case DAY_OF_WEEK:
result = ValueLong.get(DateTimeUtils.getDatePart(v0, Calendar.DAY_OF_WEEK));
break;
case DAY_OF_YEAR:
result = ValueLong.get(DateTimeUtils.getDatePart(v0, Calendar.DAY_OF_YEAR));
break;
case HOUR:
result = ValueLong.get(DateTimeUtils.getDatePart(v0, Calendar.HOUR_OF_DAY));
break;
case MINUTE:
result = ValueLong.get(DateTimeUtils.getDatePart(v0, Calendar.MINUTE));
break;
case MONTH:
result = ValueLong.get(DateTimeUtils.getDatePart(v0, Calendar.MONTH));
case QUARTER:
case ISO_YEAR:
case ISO_WEEK:
case ISO_DAY_OF_WEEK:
case SECOND:
case WEEK:
case YEAR:
result = ValueInt.get(DateTimeUtils.getDatePart(v0, info.type));
break;
case MONTH_NAME: {
SimpleDateFormat monthName = new SimpleDateFormat("MMMM",
......@@ -862,27 +863,6 @@ public class Function extends Expression implements FunctionCall {
database.getMode().treatEmptyStringsAsNull);
break;
}
case QUARTER:
result = ValueLong.get((DateTimeUtils.getDatePart(v0, Calendar.MONTH) - 1) / 3 + 1);
break;
case SECOND:
result = ValueLong.get(DateTimeUtils.getDatePart(v0, Calendar.SECOND));
break;
case WEEK:
result = ValueLong.get(DateTimeUtils.getDatePart(v0, Calendar.WEEK_OF_YEAR));
break;
case YEAR:
result = ValueLong.get(DateTimeUtils.getDatePart(v0, Calendar.YEAR));
break;
case ISO_YEAR:
result = ValueInt.get(DateTimeUtils.getIsoYear(v0));
break;
case ISO_WEEK:
result = ValueInt.get(DateTimeUtils.getIsoWeek(v0));
break;
case ISO_DAY_OF_WEEK:
result = ValueInt.get(DateTimeUtils.getIsoDayOfWeek(v0));
break;
case CURDATE:
case CURRENT_DATE: {
long now = session.getTransactionStart();
......@@ -1509,8 +1489,7 @@ public class Function extends Expression implements FunctionCall {
v0.getString(), v1.getLong(), v2.getTimestamp()));
break;
case DATE_DIFF:
result = ValueLong.get(datediff(
v0.getString(), v1.getTimestamp(), v2.getTimestamp()));
result = ValueLong.get(datediff(v0.getString(), v1, v2));
break;
case EXTRACT: {
int field = getDatePart(v0.getString());
......@@ -1828,11 +1807,39 @@ public class Function extends Expression implements FunctionCall {
private static Timestamp dateadd(String part, long count, Timestamp d) {
int field = getDatePart(part);
if (field == Calendar.MILLISECOND) {
switch (field) {
case YEAR:
field = Calendar.YEAR;
break;
case MONTH:
field = Calendar.MONTH;
break;
case DAY_OF_MONTH:
field = Calendar.DAY_OF_MONTH;
break;
case DAY_OF_YEAR:
field = Calendar.DAY_OF_YEAR;
break;
case WEEK:
field = Calendar.WEEK_OF_YEAR;
break;
case HOUR:
field = Calendar.HOUR_OF_DAY;
break;
case MINUTE:
field = Calendar.MINUTE;
break;
case SECOND:
field = Calendar.SECOND;
break;
case MILLISECOND: {
Timestamp ts = new Timestamp(d.getTime() + count);
ts.setNanos(ts.getNanos() + (d.getNanos() % 1000000));
return ts;
}
default:
throw DbException.getUnsupportedException("DATEADD " + part);
}
// We allow long for manipulating the millisecond component,
// for the rest we only allow int.
if (count > Integer.MAX_VALUE) {
......@@ -1855,79 +1862,72 @@ public class Function extends Expression implements FunctionCall {
* </pre>
*
* @param part the part
* @param d1 the first date
* @param d2 the second date
* @param v1 the first date-time value
* @param v2 the second date-time value
* @return the number of crossed boundaries
*/
private static long datediff(String part, Timestamp d1, Timestamp d2) {
private static long datediff(String part, Value v1, Value v2) {
int field = getDatePart(part);
Calendar calendar = DateTimeUtils.createGregorianCalendar();
long t1 = d1.getTime(), t2 = d2.getTime();
// need to convert to UTC, otherwise we get inconsistent results with
// certain time zones (those that are 30 minutes off)
TimeZone zone = calendar.getTimeZone();
calendar.setTime(d1);
t1 += zone.getOffset(calendar.get(Calendar.ERA),
calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH),
calendar.get(Calendar.DAY_OF_WEEK),
calendar.get(Calendar.MILLISECOND));
calendar.setTime(d2);
t2 += zone.getOffset(calendar.get(Calendar.ERA),
calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH),
calendar.get(Calendar.DAY_OF_WEEK),
calendar.get(Calendar.MILLISECOND));
long[] a1 = DateTimeUtils.dateAndTimeFromValue(v1);
long dateValue1 = a1[0];
long absolute1 = DateTimeUtils.absoluteDayFromDateValue(dateValue1);
long[] a2 = DateTimeUtils.dateAndTimeFromValue(v2);
long dateValue2 = a2[0];
long absolute2 = DateTimeUtils.absoluteDayFromDateValue(dateValue2);
switch (field) {
case Calendar.MILLISECOND:
return t2 - t1;
case Calendar.SECOND:
case Calendar.MINUTE:
case Calendar.HOUR_OF_DAY:
case Calendar.DAY_OF_YEAR:
case Calendar.WEEK_OF_YEAR: {
// first 'normalize' the numbers so both are not negative
long hour = 60 * 60 * 1000;
long add = Math.min(t1 / hour * hour, t2 / hour * hour);
t1 -= add;
t2 -= add;
case MILLISECOND:
case SECOND:
case MINUTE:
case HOUR:
long timeNanos1 = a1[1];
long timeNanos2 = a2[1];
switch (field) {
case Calendar.SECOND:
return t2 / 1000 - t1 / 1000;
case Calendar.MINUTE:
return t2 / (60 * 1000) - t1 / (60 * 1000);
case Calendar.HOUR_OF_DAY:
return t2 / hour - t1 / hour;
case Calendar.DAY_OF_YEAR:
return t2 / (hour * 24) - t1 / (hour * 24);
case Calendar.WEEK_OF_YEAR:
return t2 / (hour * 24 * 7) - t1 / (hour * 24 * 7);
default:
throw DbException.throwInternalError("field:" + field);
}
}
case Calendar.DATE:
return t2 / (24 * 60 * 60 * 1000) - t1 / (24 * 60 * 60 * 1000);
case MILLISECOND:
return (absolute2 - absolute1) * DateTimeUtils.MILLIS_PER_DAY
+ (timeNanos2 / 1_000_000 - timeNanos1 / 1_000_000);
case SECOND:
return (absolute2 - absolute1) * 86_400
+ (timeNanos2 / 1_000_000_000 - timeNanos1 / 1_000_000_000);
case MINUTE:
return (absolute2 - absolute1) * 1_440
+ (timeNanos2 / 60_000_000_000L - timeNanos1 / 60_000_000_000L);
case HOUR:
return (absolute2 - absolute1) * 24
+ (timeNanos2 / 3_600_000_000_000L - timeNanos1 / 3_600_000_000_000L);
}
// Fake fall-through
//$FALL-THROUGH$
case DAY_OF_MONTH:
case DAY_OF_YEAR:
return absolute2 - absolute1;
case WEEK:
return weekdiff(absolute1, absolute2, 0);
case ISO_WEEK:
return weekdiff(absolute1, absolute2, 1);
case MONTH:
return (DateTimeUtils.yearFromDateValue(dateValue2) - DateTimeUtils.yearFromDateValue(dateValue1)) * 12
+ DateTimeUtils.monthFromDateValue(dateValue2) - DateTimeUtils.monthFromDateValue(dateValue1);
case YEAR:
return DateTimeUtils.yearFromDateValue(dateValue2) - DateTimeUtils.yearFromDateValue(dateValue1);
default:
break;
}
calendar = DateTimeUtils.createGregorianCalendar(DateTimeUtils.UTC);
calendar.setTimeInMillis(t1);
int year1 = calendar.get(Calendar.YEAR);
int month1 = calendar.get(Calendar.MONTH);
calendar.setTimeInMillis(t2);
int year2 = calendar.get(Calendar.YEAR);
int month2 = calendar.get(Calendar.MONTH);
int result = year2 - year1;
if (field == Calendar.MONTH) {
return 12 * result + (month2 - month1);
} else if (field == Calendar.YEAR) {
return result;
} else {
throw DbException.getUnsupportedException("DATEDIFF " + part);
}
}
private static long weekdiff(long absolute1, long absolute2, int firstDayOfWeek) {
absolute1 += 4 - firstDayOfWeek;
long r1 = absolute1 / 7;
if (absolute1 < 0 && (r1 * 7 != absolute1)) {
r1--;
}
absolute2 += 4 - firstDayOfWeek;
long r2 = absolute2 / 7;
if (absolute2 < 0 && (r2 * 7 != absolute2)) {
r2--;
}
return r2 - r1;
}
private static String substring(String s, int start, int length) {
int len = s.length();
start--;
......
......@@ -955,6 +955,12 @@ public class FullText {
}
}
/**
* Check whether the database is in multi-threaded mode.
*
* @param conn the connection
* @return true if the multi-threaded mode is used
*/
static boolean isMultiThread(Connection conn)
throws SQLException {
try (Statement stat = conn.createStatement()) {
......
......@@ -703,11 +703,21 @@ public class FullTextLucene extends FullText {
searcher = new IndexSearcher(reader);
}
/**
* Start using the searcher.
*
* @return the searcher
*/
synchronized IndexSearcher getSearcher() {
++counter;
return searcher;
}
/**
* Stop using the searcher.
*
* @param searcher the searcher
*/
synchronized void returnSearcher(IndexSearcher searcher) {
if (this.searcher == searcher) {
--counter;
......@@ -738,6 +748,9 @@ public class FullTextLucene extends FullText {
searcher = new IndexSearcher(IndexReader.open(writer, true));
}
/**
* Close the index.
*/
public synchronized void close() throws IOException {
for (IndexSearcher searcher : counters.keySet()) {
closeSearcher(searcher);
......
......@@ -3222,7 +3222,14 @@ public class JdbcResultSet extends TraceObject implements ResultSet, JdbcResultS
}
}
private Value get(int columnIndex) {
/**
* INTERNAL
*
* @param columnIndex
* index of a column
* @return internal representation of the value in the specified column
*/
public Value get(int columnIndex) {
checkColumnIndex(columnIndex);
checkOnValidRow();
Value[] list;
......
......@@ -405,6 +405,7 @@ public class JdbcDataSource extends TraceObject implements XADataSource,
* Return an object of this class if possible.
*
* @param iface the class
* @return this
*/
@Override
@SuppressWarnings("unchecked")
......@@ -423,6 +424,7 @@ public class JdbcDataSource extends TraceObject implements XADataSource,
* Checks if unwrap can return an object of this class.
*
* @param iface the class
* @return whether or not the interface is assignable from this class
*/
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
......
......@@ -896,6 +896,9 @@ public class TransactionStore {
*/
final MVMap<K, VersionedValue> map;
/**
* The transaction which is used for this map.
*/
final Transaction transaction;
TransactionMap(Transaction transaction, MVMap<K, VersionedValue> map,
......
......@@ -20,35 +20,38 @@ import java.net.Socket;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.Date;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Properties;
import java.util.TimeZone;
import org.h2.command.CommandInterface;
import org.h2.engine.ConnectionInfo;
import org.h2.engine.SysProperties;
import org.h2.jdbc.JdbcConnection;
import org.h2.jdbc.JdbcPreparedStatement;
import org.h2.jdbc.JdbcResultSet;
import org.h2.jdbc.JdbcStatement;
import org.h2.message.DbException;
import org.h2.mvstore.DataUtils;
import org.h2.util.DateTimeUtils;
import org.h2.util.JdbcUtils;
import org.h2.util.MathUtils;
import org.h2.util.ScriptReader;
import org.h2.util.StringUtils;
import org.h2.util.Utils;
import org.h2.value.CaseInsensitiveMap;
import org.h2.value.Value;
import org.h2.value.ValueDate;
import org.h2.value.ValueNull;
import org.h2.value.ValueTime;
import org.h2.value.ValueTimestamp;
/**
* One server thread is opened for each client.
......@@ -527,153 +530,91 @@ public class PgServerThread implements Runnable {
sendMessage();
}
private static long toPostgreSeconds(long millis) {
// TODO handle Julian/Gregorian transitions
return millis / 1000 - 946684800L;
private static long toPostgreDays(long dateValue) {
return DateTimeUtils.prolepticGregorianAbsoluteDayFromDateValue(dateValue) - 10_957;
}
private void writeDataColumn(ResultSet rs, int column, int pgType, boolean text)
throws Exception {
Value v = ((JdbcResultSet) rs).get(column);
if (v == ValueNull.INSTANCE) {
writeInt(-1);
return;
}
if (text) {
// plain text
switch (pgType) {
case PgServer.PG_TYPE_BOOL: {
boolean b = rs.getBoolean(column);
if (rs.wasNull()) {
writeInt(-1);
} else {
writeInt(1);
dataOut.writeByte(b ? 't' : 'f');
}
case PgServer.PG_TYPE_BOOL:
writeInt(1);
dataOut.writeByte(v.getBoolean() ? 't' : 'f');
break;
}
default:
String s = rs.getString(column);
if (s == null) {
writeInt(-1);
} else {
byte[] data = s.getBytes(getEncoding());
writeInt(data.length);
write(data);
}
byte[] data = v.getString().getBytes(getEncoding());
writeInt(data.length);
write(data);
}
} else {
// binary
switch (pgType) {
case PgServer.PG_TYPE_INT2: {
short s = rs.getShort(column);
if (rs.wasNull()) {
writeInt(-1);
} else {
writeInt(2);
writeShort(s);
}
case PgServer.PG_TYPE_INT2:
writeInt(2);
writeShort(v.getShort());
break;
}
case PgServer.PG_TYPE_INT4: {
int i = rs.getInt(column);
if (rs.wasNull()) {
writeInt(-1);
} else {
writeInt(4);
writeInt(i);
}
case PgServer.PG_TYPE_INT4:
writeInt(4);
writeInt(v.getInt());
break;
}
case PgServer.PG_TYPE_INT8: {
long l = rs.getLong(column);
if (rs.wasNull()) {
writeInt(-1);
} else {
writeInt(8);
dataOut.writeLong(l);
}
case PgServer.PG_TYPE_INT8:
writeInt(8);
dataOut.writeLong(v.getLong());
break;
}
case PgServer.PG_TYPE_FLOAT4: {
float f = rs.getFloat(column);
if (rs.wasNull()) {
writeInt(-1);
} else {
writeInt(4);
dataOut.writeFloat(f);
}
case PgServer.PG_TYPE_FLOAT4:
writeInt(4);
dataOut.writeFloat(v.getFloat());
break;
}
case PgServer.PG_TYPE_FLOAT8: {
double d = rs.getDouble(column);
if (rs.wasNull()) {
writeInt(-1);
} else {
writeInt(8);
dataOut.writeDouble(d);
}
case PgServer.PG_TYPE_FLOAT8:
writeInt(8);
dataOut.writeDouble(v.getDouble());
break;
}
case PgServer.PG_TYPE_BYTEA: {
byte[] data = rs.getBytes(column);
if (data == null) {
writeInt(-1);
} else {
writeInt(data.length);
write(data);
}
byte[] data = v.getBytesNoCopy();
writeInt(data.length);
write(data);
break;
}
case PgServer.PG_TYPE_DATE: {
Date d = rs.getDate(column);
if (d == null) {
writeInt(-1);
} else {
writeInt(4);
long millis = d.getTime();
millis += TimeZone.getDefault().getOffset(millis);
writeInt((int) (toPostgreSeconds(millis) / 86400));
}
ValueDate d = (ValueDate) v.convertTo(Value.DATE);
writeInt(4);
writeInt((int) (toPostgreDays(d.getDateValue())));
break;
}
case PgServer.PG_TYPE_TIME: {
Time t = rs.getTime(column);
if (t == null) {
writeInt(-1);
ValueTime t = (ValueTime) v.convertTo(Value.TIME);
writeInt(8);
long m = t.getNanos();
if (INTEGER_DATE_TYPES) {
// long format
m /= 1_000;
} else {
writeInt(8);
long m = t.getTime();
m += TimeZone.getDefault().getOffset(m);
if (INTEGER_DATE_TYPES) {
// long format
m *= 1000;
} else {
// double format
m /= 1000;
m = Double.doubleToLongBits(m);
}
dataOut.writeLong(m);
// double format
m = Double.doubleToLongBits(m * 0.000_000_001);
}
dataOut.writeLong(m);
break;
}
case PgServer.PG_TYPE_TIMESTAMP_NO_TMZONE: {
Timestamp t = rs.getTimestamp(column);
if (t == null) {
writeInt(-1);
ValueTimestamp t = (ValueTimestamp) v.convertTo(Value.TIMESTAMP);
writeInt(8);
long m = toPostgreDays(t.getDateValue()) * 86_400;
long nanos = t.getTimeNanos();
if (INTEGER_DATE_TYPES) {
// long format
m = m * 1_000_000 + nanos / 1_000;
} else {
writeInt(8);
long m = t.getTime();
m += TimeZone.getDefault().getOffset(m);
m = toPostgreSeconds(m);
int nanos = t.getNanos();
if (m < 0 && nanos != 0) {
m--;
}
if (INTEGER_DATE_TYPES) {
// long format
m = m * 1000000 + nanos / 1000;
} else {
// double format
m = Double.doubleToLongBits(m + nanos * 0.000000001);
}
dataOut.writeLong(m);
// double format
m = Double.doubleToLongBits(m + nanos * 0.000_000_001);
}
dataOut.writeLong(m);
break;
}
default: throw new IllegalStateException("output binary format is undefined");
......
......@@ -37,6 +37,11 @@ public class TableSynonym extends SchemaObjectBase {
return synonymFor;
}
/**
* Set (update) the data.
*
* @param data the new data
*/
public void updateData(CreateSynonymData data) {
this.data = data;
}
......@@ -46,7 +51,6 @@ public class TableSynonym extends SchemaObjectBase {
return SYNONYM;
}
@Override
public String getCreateSQLForCopy(Table table, String quotedName) {
return synonymFor.getCreateSQLForCopy(table, quotedName);
......
......@@ -76,9 +76,11 @@ public class TableView extends Table {
* dependent views.
*
* @param querySQL the SQL statement
* @param newColumnTemplates the columns
* @param session the session
* @param recursive whether this is a recursive view
* @param force if errors should be ignored
* @param literalsChecked if literals have been checked
*/
public void replace(String querySQL, Column[] newColumnTemplates, Session session,
boolean recursive, boolean force, boolean literalsChecked) {
......@@ -715,6 +717,22 @@ public class TableView extends Table {
return isPersistent;
}
/**
* Create a view.
*
* @param schema the schema
* @param id the view id
* @param name the view name
* @param querySQL the query
* @param parameters the parameters
* @param columnTemplates the columns
* @param session the session
* @param literalsChecked whether literals in the query are checked
* @param isTableExpression if this is a table expression
* @param isPersistent whether the view is persisted
* @param db the database
* @return the view
*/
public static TableView createTableViewMaybeRecursive(Schema schema, int id, String name, String querySQL,
ArrayList<Parameter> parameters, Column[] columnTemplates, Session session,
boolean literalsChecked, boolean isTableExpression, boolean isPersistent, Database db) {
......@@ -805,6 +823,17 @@ public class TableView extends Table {
return columnTemplateList;
}
/**
* Create a table for a recursive query.
*
* @param isPersistent whether the table is persisted
* @param targetSession the session
* @param cteViewName the name
* @param schema the schema
* @param columns the columns
* @param db the database
* @return the table
*/
public static Table createShadowTableForRecursiveTableExpression(boolean isPersistent, Session targetSession,
String cteViewName, Schema schema, List<Column> columns, Database db) {
......@@ -834,6 +863,13 @@ public class TableView extends Table {
return recursiveTable;
}
/**
* Remove a table for a recursive query.
*
* @param isPersistent whether the table is persisted
* @param targetSession the session
* @param recursiveTable the table
*/
public static void destroyShadowTableForRecursiveExpression(boolean isPersistent, Session targetSession,
Table recursiveTable) {
if (recursiveTable != null) {
......
......@@ -25,6 +25,7 @@ import org.h2.value.ValueTime;
import org.h2.value.ValueTimestamp;
import org.h2.value.ValueTimestampTimeZone;
/**
* This utility class contains time conversion functions.
* <p>
......@@ -299,30 +300,6 @@ public class DateTimeUtils {
return ValueTimestamp.fromDateValueAndNanos(dateValue, nanos);
}
private static Calendar valueToCalendar(Value value) {
Calendar cal;
if (value instanceof ValueTimestamp) {
cal = createGregorianCalendar();
cal.setTime(value.getTimestamp());
} else if (value instanceof ValueDate) {
cal = createGregorianCalendar();
cal.setTime(value.getDate());
} else if (value instanceof ValueTime) {
cal = createGregorianCalendar();
cal.setTime(value.getTime());
} else if (value instanceof ValueTimestampTimeZone) {
ValueTimestampTimeZone v = (ValueTimestampTimeZone) value;
cal = createGregorianCalendar(v.getTimeZone());
cal.setTimeInMillis(DateTimeUtils.convertDateValueToMillis(DateTimeUtils.UTC, v.getDateValue())
+ v.getTimeNanos() / 1000000L
- v.getTimeZoneOffsetMins() * 60000);
} else {
cal = createGregorianCalendar();
cal.setTime(value.getTimestamp());
}
return cal;
}
/**
* Parse a date string. The format is: [+|-]year-month-day
*
......@@ -585,52 +562,79 @@ public class DateTimeUtils {
}
/**
* Get the specified field of a date, however with years normalized to
* positive or negative, and month starting with 1.
* Extracts date value and nanos of day from the specified value.
*
* @param date the date value
* @param field the field type
* @return the value
* @param value
* value to extract fields from
* @return array with date value and nanos of day
*/
public static int getDatePart(Value date, int field) {
public static long[] dateAndTimeFromValue(Value value) {
long dateValue = EPOCH_DATE_VALUE;
long timeNanos = 0;
if (date instanceof ValueTimestamp) {
ValueTimestamp v = (ValueTimestamp) date;
if (value instanceof ValueTimestamp) {
ValueTimestamp v = (ValueTimestamp) value;
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;
} else if (value instanceof ValueDate) {
dateValue = ((ValueDate) value).getDateValue();
} else if (value instanceof ValueTime) {
timeNanos = ((ValueTime) value).getNanos();
} else if (value instanceof ValueTimestampTimeZone) {
ValueTimestampTimeZone v = (ValueTimestampTimeZone) value;
dateValue = v.getDateValue();
timeNanos = v.getTimeNanos();
} else {
ValueTimestamp v = (ValueTimestamp) date.convertTo(Value.TIMESTAMP);
date = v; // For valueToCalendar() to avoid second convertTo() call
ValueTimestamp v = (ValueTimestamp) value.convertTo(Value.TIMESTAMP);
dateValue = v.getDateValue();
timeNanos = v.getTimeNanos();
}
return new long[] {dateValue, timeNanos};
}
/**
* Get the specified field of a date, however with years normalized to
* positive or negative, and month starting with 1.
*
* @param date the date value
* @param field the field type, see {@link Function} for constants
* @return the value
*/
public static int getDatePart(Value date, int field) {
long[] a = dateAndTimeFromValue(date);
long dateValue = a[0];
long timeNanos = a[1];
switch (field) {
case Calendar.YEAR:
case Function.YEAR:
return yearFromDateValue(dateValue);
case Calendar.MONTH:
case Function.MONTH:
return monthFromDateValue(dateValue);
case Calendar.DAY_OF_MONTH:
case Function.DAY_OF_MONTH:
return dayFromDateValue(dateValue);
case Calendar.HOUR_OF_DAY:
case Function.HOUR:
return (int) (timeNanos / 3_600_000_000_000L % 24);
case Calendar.MINUTE:
case Function.MINUTE:
return (int) (timeNanos / 60_000_000_000L % 60);
case Calendar.SECOND:
case Function.SECOND:
return (int) (timeNanos / 1_000_000_000 % 60);
case Calendar.MILLISECOND:
case Function.MILLISECOND:
return (int) (timeNanos / 1_000_000 % 1_000);
case Function.DAY_OF_YEAR:
return getDayOfYear(dateValue);
case Function.DAY_OF_WEEK:
return getSundayDayOfWeek(dateValue);
case Function.WEEK:
GregorianCalendar gc = getCalendar();
return getWeekOfYear(dateValue, gc.getFirstDayOfWeek() - 1, gc.getMinimalDaysInFirstWeek());
case Function.QUARTER:
return (monthFromDateValue(dateValue) - 1) / 3 + 1;
case Function.ISO_YEAR:
return getIsoWeekYear(dateValue);
case Function.ISO_WEEK:
return getIsoWeekOfYear(dateValue);
case Function.ISO_DAY_OF_WEEK:
return getIsoDayOfWeek(dateValue);
}
return valueToCalendar(date).get(field);
throw DbException.getUnsupportedException("getDatePart(" + date + ", " + field + ')');
}
/**
......@@ -670,57 +674,144 @@ public class DateTimeUtils {
}
/**
* Return the day of week according to the ISO 8601 specification. Week
* starts at Monday. See also http://en.wikipedia.org/wiki/ISO_8601
* Returns day of week.
*
* @author Robert Rathsack
* @param value the date object which day of week should be calculated
* @return the day of the week, Monday as 1 to Sunday as 7
* @param dateValue
* the date value
* @param firstDayOfWeek
* first day of week, Monday as 1, Sunday as 7 or 0
* @return day of week
* @see #getIsoDayOfWeek(long)
*/
public static int getIsoDayOfWeek(Value value) {
int val = valueToCalendar(value).get(Calendar.DAY_OF_WEEK) - 1;
return val == 0 ? 7 : val;
public static int getDayOfWeek(long dateValue, int firstDayOfWeek) {
return getDayOfWeekFromAbsolute(absoluteDayFromDateValue(dateValue), firstDayOfWeek);
}
private static int getDayOfWeekFromAbsolute(long absoluteValue, int firstDayOfWeek) {
return absoluteValue >= 0 ? (int) ((absoluteValue - firstDayOfWeek + 11) % 7) + 1
: (int) ((absoluteValue - firstDayOfWeek - 2) % 7) + 7;
}
/**
* Returns the week of the year according to the ISO 8601 specification. The
* spec defines the first week of the year as the week which contains at
* least 4 days of the new year. The week starts at Monday. Therefore
* December 29th - 31th could belong to the next year and January 1st - 3th
* could belong to the previous year. If January 1st is on Thursday (or
* earlier) it belongs to the first week, otherwise to the last week of the
* previous year. Hence January 4th always belongs to the first week while
* the December 28th always belongs to the last week.
* Returns number of day in year.
*
* @author Robert Rathsack
* @param value the date object which week of year should be calculated
* @return the week of the year
* @param dateValue
* the date value
* @return number of day in year
*/
public static int getIsoWeek(Value value) {
Calendar c = valueToCalendar(value);
c.setFirstDayOfWeek(Calendar.MONDAY);
c.setMinimalDaysInFirstWeek(4);
return c.get(Calendar.WEEK_OF_YEAR);
public static int getDayOfYear(long dateValue) {
int year = yearFromDateValue(dateValue);
return (int) (absoluteDayFromDateValue(dateValue) - absoluteDayFromDateValue(dateValue(year, 1, 1))) + 1;
}
/**
* Returns the year according to the ISO week definition.
* Returns ISO day of week.
*
* @author Robert Rathsack
* @param value the date object which year should be calculated
* @return the year
* @param dateValue
* the date value
* @return ISO day of week, Monday as 1 to Sunday as 7
* @see #getSundayDayOfWeek(long)
*/
public static int getIsoDayOfWeek(long dateValue) {
return getDayOfWeek(dateValue, 1);
}
/**
* Returns ISO number of week in year.
*
* @param dateValue
* the date value
* @return number of week in year
* @see #getIsoWeekYear(long)
* @see #getWeekOfYear(long, int, int)
*/
public static int getIsoWeekOfYear(long dateValue) {
return getWeekOfYear(dateValue, 1, 4);
}
/**
* Returns ISO week year.
*
* @param dateValue
* the date value
* @return ISO week year
* @see #getIsoWeekOfYear(long)
* @see #getWeekYear(long, int, int)
*/
public static int getIsoYear(Value value) {
Calendar cal = valueToCalendar(value);
cal.setFirstDayOfWeek(Calendar.MONDAY);
cal.setMinimalDaysInFirstWeek(4);
int year = getYear(cal);
int month = cal.get(Calendar.MONTH);
int week = cal.get(Calendar.WEEK_OF_YEAR);
if (month == 0 && week > 51) {
year--;
} else if (month == 11 && week == 1) {
year++;
public static int getIsoWeekYear(long dateValue) {
return getWeekYear(dateValue, 1, 4);
}
/**
* Returns day of week with Sunday as 1.
*
* @param dateValue
* the date value
* @return day of week, Sunday as 1 to Monday as 7
* @see #getIsoDayOfWeek(long)
*/
public static int getSundayDayOfWeek(long dateValue) {
return getDayOfWeek(dateValue, 0);
}
/**
* Returns number of week in year.
*
* @param dateValue
* the date value
* @param firstDayOfWeek
* first day of week, Monday as 1, Sunday as 7 or 0
* @param minimalDaysInFirstWeek
* minimal days in first week of year
* @return number of week in year
* @see #getIsoWeekOfYear(long)
*/
public static int getWeekOfYear(long dateValue, int firstDayOfWeek, int minimalDaysInFirstWeek) {
long abs = absoluteDayFromDateValue(dateValue);
int year = yearFromDateValue(dateValue);
long base = getWeekOfYearBase(year, firstDayOfWeek, minimalDaysInFirstWeek);
if (abs - base < 0) {
base = getWeekOfYearBase(year - 1, firstDayOfWeek, minimalDaysInFirstWeek);
} else if (monthFromDateValue(dateValue) == 12 && 24 + minimalDaysInFirstWeek < dayFromDateValue(dateValue)) {
if (abs >= getWeekOfYearBase(year + 1, firstDayOfWeek, minimalDaysInFirstWeek)) {
return 1;
}
}
return (int) ((abs - base) / 7) + 1;
}
private static long getWeekOfYearBase(int year, int firstDayOfWeek, int minimalDaysInFirstWeek) {
long first = absoluteDayFromDateValue(dateValue(year, 1, 1));
int daysInFirstWeek = 8 - getDayOfWeekFromAbsolute(first, firstDayOfWeek);
long base = first + daysInFirstWeek;
if (daysInFirstWeek >= minimalDaysInFirstWeek) {
base -= 7;
}
return base;
}
/**
* Returns week year.
*
* @param dateValue
* the date value
* @param firstDayOfWeek
* first day of week, Monday as 1, Sunday as 7 or 0
* @param minimalDaysInFirstWeek
* minimal days in first week of year
* @return week year
* @see #getIsoWeekYear(long)
*/
public static int getWeekYear(long dateValue, int firstDayOfWeek, int minimalDaysInFirstWeek) {
long abs = absoluteDayFromDateValue(dateValue);
int year = yearFromDateValue(dateValue);
long base = getWeekOfYearBase(year, firstDayOfWeek, minimalDaysInFirstWeek);
if (abs - base < 0) {
return year - 1;
} else if (monthFromDateValue(dateValue) == 12 && 24 + minimalDaysInFirstWeek < dayFromDateValue(dateValue)) {
if (abs >= getWeekOfYearBase(year + 1, firstDayOfWeek, minimalDaysInFirstWeek)) {
return year + 1;
}
}
return year;
}
......@@ -1043,6 +1134,29 @@ public class DateTimeUtils {
return a;
}
/**
* Calculate the absolute day from an encoded date value in proleptic Gregorian
* calendar.
*
* @param dateValue the date value
* @return the absolute day in proleptic Gregorian calendar
*/
public static long prolepticGregorianAbsoluteDayFromDateValue(long dateValue) {
long y = yearFromDateValue(dateValue);
int m = monthFromDateValue(dateValue);
int d = dayFromDateValue(dateValue);
if (m <= 2) {
y--;
m += 12;
}
long a = ((y * 2922L) >> 3) + DAYS_OFFSET[m - 3] + d - 719484;
if (y < 1901 || y > 2099) {
// Slow mode
a += (y / 400) - (y / 100) + 15;
}
return a;
}
/**
* Calculate the encoded date value from an absolute day.
*
......
......@@ -543,8 +543,15 @@ public class LocalDateTimeUtils {
int year = DateTimeUtils.yearFromDateValue(dateValue);
int month = DateTimeUtils.monthFromDateValue(dateValue);
int day = DateTimeUtils.dayFromDateValue(dateValue);
return LOCAL_DATE_OF_YEAR_MONTH_DAY.invoke(null, year, month, day);
try {
return LOCAL_DATE_OF_YEAR_MONTH_DAY.invoke(null, year, month, day);
} catch (InvocationTargetException e) {
if (year <= 1500 && (year & 3) == 0 && month == 2 && day == 29) {
// If proleptic Gregorian doesn't have such date use the next day
return LOCAL_DATE_OF_YEAR_MONTH_DAY.invoke(null, year, 3, 1);
}
throw e;
}
}
private static Object localDateTimeFromDateNanos(long dateValue, long timeNanos)
......
......@@ -15,6 +15,9 @@ import java.util.Comparator;
*/
public class CharsetCollator extends Collator {
/**
* The comparator used to compare byte arrays.
*/
static final Comparator<byte[]> COMPARATOR = new Comparator<byte[]>() {
@Override
public int compare(byte[] b1, byte[] b2) {
......
......@@ -14,6 +14,7 @@ import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URL;
import java.sql.Connection;
import java.sql.Date;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
......@@ -23,6 +24,8 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.UUID;
import org.h2.api.ErrorCode;
import org.h2.api.Trigger;
......@@ -99,6 +102,7 @@ public class TestPreparedStatement extends TestBase {
conn.close();
testPreparedStatementWithLiteralsNone();
testPreparedStatementWithIndexedParameterAndLiteralsNone();
testPreparedStatementWithAnyParameter();
deleteDb("preparedStatement");
}
......@@ -644,6 +648,60 @@ public class TestPreparedStatement extends TestBase {
localDate2 = rs.getObject(1, LocalDateTimeUtils.LOCAL_DATE);
assertEquals(localDate, localDate2);
rs.close();
/*
* Check that date that doesn't exist in proleptic Gregorian calendar can be
* read as a next date.
*/
prep.setString(1, "1500-02-29");
rs = prep.executeQuery();
rs.next();
localDate2 = rs.getObject(1, LocalDateTimeUtils.LOCAL_DATE);
assertEquals(LocalDateTimeUtils.parseLocalDate("1500-03-01"), localDate2);
rs.close();
prep.setString(1, "1400-02-29");
rs = prep.executeQuery();
rs.next();
localDate2 = rs.getObject(1, LocalDateTimeUtils.LOCAL_DATE);
assertEquals(LocalDateTimeUtils.parseLocalDate("1400-03-01"), localDate2);
rs.close();
prep.setString(1, "1300-02-29");
rs = prep.executeQuery();
rs.next();
localDate2 = rs.getObject(1, LocalDateTimeUtils.LOCAL_DATE);
assertEquals(LocalDateTimeUtils.parseLocalDate("1300-03-01"), localDate2);
rs.close();
prep.setString(1, "-0100-02-29");
rs = prep.executeQuery();
rs.next();
localDate2 = rs.getObject(1, LocalDateTimeUtils.LOCAL_DATE);
assertEquals(LocalDateTimeUtils.parseLocalDate("-0100-03-01"), localDate2);
rs.close();
/*
* Check that date that doesn't exist in traditional calendar can be set and
* read with LocalDate and can be read with getDate() as a next date.
*/
localDate = LocalDateTimeUtils.parseLocalDate("1582-10-05");
prep.setObject(1, localDate);
rs = prep.executeQuery();
rs.next();
localDate2 = rs.getObject(1, LocalDateTimeUtils.LOCAL_DATE);
assertEquals(localDate, localDate2);
assertEquals("1582-10-05", rs.getString(1));
assertEquals(Date.valueOf("1582-10-15"), rs.getDate(1));
/*
* Also check that date that doesn't exist in traditional calendar can be read
* with getDate() with custom Calendar properly.
*/
GregorianCalendar gc = new GregorianCalendar();
gc.setGregorianChange(new java.util.Date(Long.MIN_VALUE));
gc.clear();
gc.set(Calendar.YEAR, 1582);
gc.set(Calendar.MONTH, 9);
gc.set(Calendar.DAY_OF_MONTH, 5);
Date expected = new Date(gc.getTimeInMillis());
gc.clear();
assertEquals(expected, rs.getDate(1, gc));
rs.close();
}
private void testTime8(Connection conn) throws SQLException {
......@@ -1491,7 +1549,43 @@ public class TestPreparedStatement extends TestBase {
deleteDb("preparedStatement");
}
private void testPreparedStatementWithAnyParameter() throws SQLException {
deleteDb("preparedStatement");
Connection conn = getConnection("preparedStatement");
conn.prepareStatement("CREATE TABLE TEST(ID INT PRIMARY KEY, VALUE INT UNIQUE)").execute();
PreparedStatement ps = conn.prepareStatement("INSERT INTO TEST(ID, VALUE) VALUES (?, ?)");
for (int i = 0; i < 10_000; i++) {
ps.setInt(1, i);
ps.setInt(2, i * 10);
ps.executeUpdate();
}
Object[] values = {-100, 10, 200, 3_000, 40_000, 500_000};
int[] expected = {1, 20, 300, 4_000};
// Ensure that other methods return the same results
ps = conn.prepareStatement("SELECT ID FROM TEST WHERE VALUE IN (SELECT * FROM TABLE(X INT=?)) ORDER BY ID");
anyParameterCheck(ps, values, expected);
ps = conn.prepareStatement("SELECT ID FROM TEST INNER JOIN TABLE(X INT=?) T ON TEST.VALUE = T.X");
anyParameterCheck(ps, values, expected);
// Test expression = ANY(?)
ps = conn.prepareStatement("SELECT ID FROM TEST WHERE VALUE = ANY(?)");
assertThrows(ErrorCode.PARAMETER_NOT_SET_1, ps).executeQuery();
anyParameterCheck(ps, values, expected);
anyParameterCheck(ps, 300, new int[] {30});
anyParameterCheck(ps, -5, new int[0]);
conn.close();
deleteDb("preparedStatement");
}
private void anyParameterCheck(PreparedStatement ps, Object values, int[] expected) throws SQLException {
ps.setObject(1, values);
try (ResultSet rs = ps.executeQuery()) {
for (int exp : expected) {
assertTrue(rs.next());
assertEquals(exp, rs.getInt(1));
}
assertFalse(rs.next());
}
}
private void checkBigDecimal(ResultSet rs, String[] value) throws SQLException {
for (String v : value) {
......
......@@ -158,3 +158,39 @@ call datediff('MS', TIMESTAMP '1900-01-01 00:00:01.000', TIMESTAMP '2008-01-01 0
> -------------
> 3408134399000
> rows: 1
SELECT DATEDIFF('WEEK', DATE '2018-02-02', DATE '2018-02-03'), DATEDIFF('ISO_WEEK', DATE '2018-02-02', DATE '2018-02-03');
> 0 0
> - -
> 0 0
> rows: 1
SELECT DATEDIFF('WEEK', DATE '2018-02-03', DATE '2018-02-04'), DATEDIFF('ISO_WEEK', DATE '2018-02-03', DATE '2018-02-04');
> 1 0
> - -
> 1 0
> rows: 1
SELECT DATEDIFF('WEEK', DATE '2018-02-04', DATE '2018-02-05'), DATEDIFF('ISO_WEEK', DATE '2018-02-04', DATE '2018-02-05');
> 0 1
> - -
> 0 1
> rows: 1
SELECT DATEDIFF('WEEK', DATE '2018-02-05', DATE '2018-02-06'), DATEDIFF('ISO_WEEK', DATE '2018-02-05', DATE '2018-02-06');
> 0 0
> - -
> 0 0
> rows: 1
SELECT DATEDIFF('WEEK', DATE '1969-12-27', DATE '1969-12-28'), DATEDIFF('ISO_WEEK', DATE '1969-12-27', DATE '1969-12-28');
> 1 0
> - -
> 1 0
> rows: 1
SELECT DATEDIFF('WEEK', DATE '1969-12-28', DATE '1969-12-29'), DATEDIFF('ISO_WEEK', DATE '1969-12-28', DATE '1969-12-29');
> 0 1
> - -
> 0 1
> rows: 1
......@@ -5,13 +5,17 @@
*/
package org.h2.test.synth;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.h2.test.TestBase;
import org.h2.test.utils.SelfDestructor;
......@@ -58,7 +62,7 @@ public class TestKillRestart extends TestBase {
Thread.sleep(100);
printTime("killing: " + i);
p.destroy();
p.waitFor();
waitForTimeout(p);
break;
} else if (s.startsWith("#Fail")) {
fail("Failed: " + s);
......@@ -68,6 +72,58 @@ public class TestKillRestart extends TestBase {
deleteDb("killRestart");
}
/**
* Wait for a subprocess with timeout.
*/
private static void waitForTimeout(final Process p)
throws InterruptedException, IOException {
final long pid = getPidOfProcess(p);
if (pid == -1) {
p.waitFor();
}
// when we hit Java8 we can use the waitFor(1,TimeUnit.MINUTES) method
final CountDownLatch latch = new CountDownLatch(1);
new Thread("waitForTimeout") {
@Override
public void run() {
try {
p.waitFor();
latch.countDown();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}.start();
if (!latch.await(2, TimeUnit.MINUTES)) {
String[] procDef = { "jstack", "-F", "-m", "-l", "" + pid };
new ProcessBuilder().redirectErrorStream(true).command(procDef)
.start();
OutputCatcher catcher = new OutputCatcher(p.getInputStream());
catcher.start();
Thread.sleep(500);
throw new IOException("timed out waiting for subprocess to die");
}
}
/**
* Get the PID of a subprocess. Only works on Linux and OSX.
*/
private static long getPidOfProcess(Process p) {
// When we hit Java9 we can call getPid() on Process.
long pid = -1;
try {
if (p.getClass().getName().equals("java.lang.UNIXProcess")) {
Field f = p.getClass().getDeclaredField("pid");
f.setAccessible(true);
pid = f.getLong(p);
f.setAccessible(false);
}
} catch (Exception e) {
pid = -1;
}
return pid;
}
/**
* This method is called when executing this application from the command
* line.
......@@ -77,7 +133,7 @@ public class TestKillRestart extends TestBase {
public static void main(String... args) {
SelfDestructor.startCountdown(60);
String driver = "org.h2.Driver";
String url = "jdbc:h2:test", user = "sa", password = "sa";
String url = "jdbc:h2:mem:test", user = "sa", password = "sa";
for (int i = 0; i < args.length; i++) {
if ("-url".equals(args[i])) {
url = args[++i];
......
......@@ -6,11 +6,10 @@
package org.h2.test.unit;
import static org.h2.util.DateTimeUtils.getIsoDayOfWeek;
import static org.h2.util.DateTimeUtils.getIsoWeek;
import static org.h2.util.DateTimeUtils.getIsoYear;
import static org.h2.util.DateTimeUtils.getIsoWeekOfYear;
import static org.h2.util.DateTimeUtils.getIsoWeekYear;
import org.h2.test.TestBase;
import org.h2.value.Value;
import org.h2.value.ValueDate;
import org.h2.value.ValueTimestamp;
import org.h2.value.ValueTimestampTimeZone;
......@@ -35,21 +34,21 @@ public class TestDateIso8601 extends TestBase {
TestBase.createCaller().init().test();
}
private static Value parse(String s) {
private static long parse(String s) {
if (type == null) {
throw new IllegalStateException();
}
switch (type) {
case DATE:
return ValueDate.parse(s);
return ValueDate.parse(s).getDateValue();
case TIMESTAMP:
return ValueTimestamp.parse(s);
return ValueTimestamp.parse(s).getDateValue();
case TIMESTAMP_TIMEZONE_0:
return ValueTimestampTimeZone.parse(s + " 00:00:00.0Z");
return ValueTimestampTimeZone.parse(s + " 00:00:00.0Z").getDateValue();
case TIMESTAMP_TIMEZONE_PLUS_18:
return ValueTimestampTimeZone.parse(s + " 00:00:00+18:00");
return ValueTimestampTimeZone.parse(s + " 00:00:00+18:00").getDateValue();
case TIMESTAMP_TIMEZONE_MINUS_18:
return ValueTimestampTimeZone.parse(s + " 00:00:00-18:00");
return ValueTimestampTimeZone.parse(s + " 00:00:00-18:00").getDateValue();
default:
throw new IllegalStateException();
}
......@@ -104,57 +103,57 @@ public class TestDateIso8601 extends TestBase {
* January 1st is a Monday therefore the week belongs to the next year.
*/
private void testIsoWeekJanuary1thMonday() throws Exception {
assertEquals(52, getIsoWeek(parse("2006-12-31")));
assertEquals(1, getIsoWeek(parse("2007-01-01")));
assertEquals(1, getIsoWeek(parse("2007-01-07")));
assertEquals(2, getIsoWeek(parse("2007-01-08")));
assertEquals(52, getIsoWeekOfYear(parse("2006-12-31")));
assertEquals(1, getIsoWeekOfYear(parse("2007-01-01")));
assertEquals(1, getIsoWeekOfYear(parse("2007-01-07")));
assertEquals(2, getIsoWeekOfYear(parse("2007-01-08")));
}
/**
* January 1st is a Tuesday therefore the week belongs to the next year.
*/
private void testIsoWeekJanuary1thTuesday() throws Exception {
assertEquals(52, getIsoWeek(parse("2007-12-30")));
assertEquals(1, getIsoWeek(parse("2007-12-31")));
assertEquals(1, getIsoWeek(parse("2008-01-01")));
assertEquals(1, getIsoWeek(parse("2008-01-06")));
assertEquals(2, getIsoWeek(parse("2008-01-07")));
assertEquals(52, getIsoWeekOfYear(parse("2007-12-30")));
assertEquals(1, getIsoWeekOfYear(parse("2007-12-31")));
assertEquals(1, getIsoWeekOfYear(parse("2008-01-01")));
assertEquals(1, getIsoWeekOfYear(parse("2008-01-06")));
assertEquals(2, getIsoWeekOfYear(parse("2008-01-07")));
}
/**
* January1th is a Wednesday therefore the week belongs to the next year.
*/
private void testIsoWeekJanuary1thWednesday() throws Exception {
assertEquals(52, getIsoWeek(parse("2002-12-28")));
assertEquals(52, getIsoWeek(parse("2002-12-29")));
assertEquals(1, getIsoWeek(parse("2002-12-30")));
assertEquals(1, getIsoWeek(parse("2002-12-31")));
assertEquals(1, getIsoWeek(parse("2003-01-01")));
assertEquals(1, getIsoWeek(parse("2003-01-05")));
assertEquals(2, getIsoWeek(parse("2003-01-06")));
assertEquals(52, getIsoWeekOfYear(parse("2002-12-28")));
assertEquals(52, getIsoWeekOfYear(parse("2002-12-29")));
assertEquals(1, getIsoWeekOfYear(parse("2002-12-30")));
assertEquals(1, getIsoWeekOfYear(parse("2002-12-31")));
assertEquals(1, getIsoWeekOfYear(parse("2003-01-01")));
assertEquals(1, getIsoWeekOfYear(parse("2003-01-05")));
assertEquals(2, getIsoWeekOfYear(parse("2003-01-06")));
}
/**
* January 1st is a Thursday therefore the week belongs to the next year.
*/
private void testIsoWeekJanuary1thThursday() throws Exception {
assertEquals(52, getIsoWeek(parse("2008-12-28")));
assertEquals(1, getIsoWeek(parse("2008-12-29")));
assertEquals(1, getIsoWeek(parse("2008-12-30")));
assertEquals(1, getIsoWeek(parse("2008-12-31")));
assertEquals(1, getIsoWeek(parse("2009-01-01")));
assertEquals(1, getIsoWeek(parse("2009-01-04")));
assertEquals(2, getIsoWeek(parse("2009-01-09")));
assertEquals(52, getIsoWeekOfYear(parse("2008-12-28")));
assertEquals(1, getIsoWeekOfYear(parse("2008-12-29")));
assertEquals(1, getIsoWeekOfYear(parse("2008-12-30")));
assertEquals(1, getIsoWeekOfYear(parse("2008-12-31")));
assertEquals(1, getIsoWeekOfYear(parse("2009-01-01")));
assertEquals(1, getIsoWeekOfYear(parse("2009-01-04")));
assertEquals(2, getIsoWeekOfYear(parse("2009-01-09")));
}
/**
* January 1st is a Friday therefore the week belongs to the previous year.
*/
private void testIsoWeekJanuary1thFriday() throws Exception {
assertEquals(53, getIsoWeek(parse("2009-12-31")));
assertEquals(53, getIsoWeek(parse("2010-01-01")));
assertEquals(53, getIsoWeek(parse("2010-01-03")));
assertEquals(1, getIsoWeek(parse("2010-01-04")));
assertEquals(53, getIsoWeekOfYear(parse("2009-12-31")));
assertEquals(53, getIsoWeekOfYear(parse("2010-01-01")));
assertEquals(53, getIsoWeekOfYear(parse("2010-01-03")));
assertEquals(1, getIsoWeekOfYear(parse("2010-01-04")));
}
/**
......@@ -162,34 +161,34 @@ public class TestDateIso8601 extends TestBase {
* year.
*/
private void testIsoWeekJanuary1thSaturday() throws Exception {
assertEquals(52, getIsoWeek(parse("2010-12-31")));
assertEquals(52, getIsoWeek(parse("2011-01-01")));
assertEquals(52, getIsoWeek(parse("2011-01-02")));
assertEquals(1, getIsoWeek(parse("2011-01-03")));
assertEquals(52, getIsoWeekOfYear(parse("2010-12-31")));
assertEquals(52, getIsoWeekOfYear(parse("2011-01-01")));
assertEquals(52, getIsoWeekOfYear(parse("2011-01-02")));
assertEquals(1, getIsoWeekOfYear(parse("2011-01-03")));
}
/**
* January 1st is a Sunday therefore the week belongs to the previous year.
*/
private void testIsoWeekJanuary1thSunday() throws Exception {
assertEquals(52, getIsoWeek(parse("2011-12-31")));
assertEquals(52, getIsoWeek(parse("2012-01-01")));
assertEquals(1, getIsoWeek(parse("2012-01-02")));
assertEquals(1, getIsoWeek(parse("2012-01-08")));
assertEquals(2, getIsoWeek(parse("2012-01-09")));
assertEquals(52, getIsoWeekOfYear(parse("2011-12-31")));
assertEquals(52, getIsoWeekOfYear(parse("2012-01-01")));
assertEquals(1, getIsoWeekOfYear(parse("2012-01-02")));
assertEquals(1, getIsoWeekOfYear(parse("2012-01-08")));
assertEquals(2, getIsoWeekOfYear(parse("2012-01-09")));
}
/**
* January 1st is a Monday therefore year is equal to isoYear.
*/
private void testIsoYearJanuary1thMonday() throws Exception {
assertEquals(2006, getIsoYear(parse("2006-12-28")));
assertEquals(2006, getIsoYear(parse("2006-12-29")));
assertEquals(2006, getIsoYear(parse("2006-12-30")));
assertEquals(2006, getIsoYear(parse("2006-12-31")));
assertEquals(2007, getIsoYear(parse("2007-01-01")));
assertEquals(2007, getIsoYear(parse("2007-01-02")));
assertEquals(2007, getIsoYear(parse("2007-01-03")));
assertEquals(2006, getIsoWeekYear(parse("2006-12-28")));
assertEquals(2006, getIsoWeekYear(parse("2006-12-29")));
assertEquals(2006, getIsoWeekYear(parse("2006-12-30")));
assertEquals(2006, getIsoWeekYear(parse("2006-12-31")));
assertEquals(2007, getIsoWeekYear(parse("2007-01-01")));
assertEquals(2007, getIsoWeekYear(parse("2007-01-02")));
assertEquals(2007, getIsoWeekYear(parse("2007-01-03")));
}
/**
......@@ -197,14 +196,14 @@ public class TestDateIso8601 extends TestBase {
* year.
*/
private void testIsoYearJanuary1thTuesday() throws Exception {
assertEquals(2007, getIsoYear(parse("2007-12-28")));
assertEquals(2007, getIsoYear(parse("2007-12-29")));
assertEquals(2007, getIsoYear(parse("2007-12-30")));
assertEquals(2008, getIsoYear(parse("2007-12-31")));
assertEquals(2008, getIsoYear(parse("2008-01-01")));
assertEquals(2008, getIsoYear(parse("2008-01-02")));
assertEquals(2008, getIsoYear(parse("2008-01-03")));
assertEquals(2008, getIsoYear(parse("2008-01-04")));
assertEquals(2007, getIsoWeekYear(parse("2007-12-28")));
assertEquals(2007, getIsoWeekYear(parse("2007-12-29")));
assertEquals(2007, getIsoWeekYear(parse("2007-12-30")));
assertEquals(2008, getIsoWeekYear(parse("2007-12-31")));
assertEquals(2008, getIsoWeekYear(parse("2008-01-01")));
assertEquals(2008, getIsoWeekYear(parse("2008-01-02")));
assertEquals(2008, getIsoWeekYear(parse("2008-01-03")));
assertEquals(2008, getIsoWeekYear(parse("2008-01-04")));
}
/**
......@@ -212,13 +211,13 @@ public class TestDateIso8601 extends TestBase {
* the next year.
*/
private void testIsoYearJanuary1thWednesday() throws Exception {
assertEquals(2002, getIsoYear(parse("2002-12-28")));
assertEquals(2002, getIsoYear(parse("2002-12-29")));
assertEquals(2003, getIsoYear(parse("2002-12-30")));
assertEquals(2003, getIsoYear(parse("2002-12-31")));
assertEquals(2003, getIsoYear(parse("2003-01-01")));
assertEquals(2003, getIsoYear(parse("2003-01-02")));
assertEquals(2003, getIsoYear(parse("2003-12-02")));
assertEquals(2002, getIsoWeekYear(parse("2002-12-28")));
assertEquals(2002, getIsoWeekYear(parse("2002-12-29")));
assertEquals(2003, getIsoWeekYear(parse("2002-12-30")));
assertEquals(2003, getIsoWeekYear(parse("2002-12-31")));
assertEquals(2003, getIsoWeekYear(parse("2003-01-01")));
assertEquals(2003, getIsoWeekYear(parse("2003-01-02")));
assertEquals(2003, getIsoWeekYear(parse("2003-12-02")));
}
/**
......@@ -226,14 +225,14 @@ public class TestDateIso8601 extends TestBase {
* next year.
*/
private void testIsoYearJanuary1thThursday() throws Exception {
assertEquals(2008, getIsoYear(parse("2008-12-28")));
assertEquals(2009, getIsoYear(parse("2008-12-29")));
assertEquals(2009, getIsoYear(parse("2008-12-30")));
assertEquals(2009, getIsoYear(parse("2008-12-31")));
assertEquals(2009, getIsoYear(parse("2009-01-01")));
assertEquals(2009, getIsoYear(parse("2009-01-02")));
assertEquals(2009, getIsoYear(parse("2009-01-03")));
assertEquals(2009, getIsoYear(parse("2009-01-04")));
assertEquals(2008, getIsoWeekYear(parse("2008-12-28")));
assertEquals(2009, getIsoWeekYear(parse("2008-12-29")));
assertEquals(2009, getIsoWeekYear(parse("2008-12-30")));
assertEquals(2009, getIsoWeekYear(parse("2008-12-31")));
assertEquals(2009, getIsoWeekYear(parse("2009-01-01")));
assertEquals(2009, getIsoWeekYear(parse("2009-01-02")));
assertEquals(2009, getIsoWeekYear(parse("2009-01-03")));
assertEquals(2009, getIsoWeekYear(parse("2009-01-04")));
}
/**
......@@ -241,14 +240,14 @@ public class TestDateIso8601 extends TestBase {
* previous year.
*/
private void testIsoYearJanuary1thFriday() throws Exception {
assertEquals(2009, getIsoYear(parse("2009-12-28")));
assertEquals(2009, getIsoYear(parse("2009-12-29")));
assertEquals(2009, getIsoYear(parse("2009-12-30")));
assertEquals(2009, getIsoYear(parse("2009-12-31")));
assertEquals(2009, getIsoYear(parse("2010-01-01")));
assertEquals(2009, getIsoYear(parse("2010-01-02")));
assertEquals(2009, getIsoYear(parse("2010-01-03")));
assertEquals(2010, getIsoYear(parse("2010-01-04")));
assertEquals(2009, getIsoWeekYear(parse("2009-12-28")));
assertEquals(2009, getIsoWeekYear(parse("2009-12-29")));
assertEquals(2009, getIsoWeekYear(parse("2009-12-30")));
assertEquals(2009, getIsoWeekYear(parse("2009-12-31")));
assertEquals(2009, getIsoWeekYear(parse("2010-01-01")));
assertEquals(2009, getIsoWeekYear(parse("2010-01-02")));
assertEquals(2009, getIsoWeekYear(parse("2010-01-03")));
assertEquals(2010, getIsoWeekYear(parse("2010-01-04")));
}
/**
......@@ -256,28 +255,28 @@ public class TestDateIso8601 extends TestBase {
* previous year.
*/
private void testIsoYearJanuary1thSaturday() throws Exception {
assertEquals(2010, getIsoYear(parse("2010-12-28")));
assertEquals(2010, getIsoYear(parse("2010-12-29")));
assertEquals(2010, getIsoYear(parse("2010-12-30")));
assertEquals(2010, getIsoYear(parse("2010-12-31")));
assertEquals(2010, getIsoYear(parse("2011-01-01")));
assertEquals(2010, getIsoYear(parse("2011-01-02")));
assertEquals(2011, getIsoYear(parse("2011-01-03")));
assertEquals(2011, getIsoYear(parse("2011-01-04")));
assertEquals(2010, getIsoWeekYear(parse("2010-12-28")));
assertEquals(2010, getIsoWeekYear(parse("2010-12-29")));
assertEquals(2010, getIsoWeekYear(parse("2010-12-30")));
assertEquals(2010, getIsoWeekYear(parse("2010-12-31")));
assertEquals(2010, getIsoWeekYear(parse("2011-01-01")));
assertEquals(2010, getIsoWeekYear(parse("2011-01-02")));
assertEquals(2011, getIsoWeekYear(parse("2011-01-03")));
assertEquals(2011, getIsoWeekYear(parse("2011-01-04")));
}
/**
* January 1st is a Sunday therefore this day belong to the previous year.
*/
private void testIsoYearJanuary1thSunday() throws Exception {
assertEquals(2011, getIsoYear(parse("2011-12-28")));
assertEquals(2011, getIsoYear(parse("2011-12-29")));
assertEquals(2011, getIsoYear(parse("2011-12-30")));
assertEquals(2011, getIsoYear(parse("2011-12-31")));
assertEquals(2011, getIsoYear(parse("2012-01-01")));
assertEquals(2012, getIsoYear(parse("2012-01-02")));
assertEquals(2012, getIsoYear(parse("2012-01-03")));
assertEquals(2012, getIsoYear(parse("2012-01-04")));
assertEquals(2011, getIsoWeekYear(parse("2011-12-28")));
assertEquals(2011, getIsoWeekYear(parse("2011-12-29")));
assertEquals(2011, getIsoWeekYear(parse("2011-12-30")));
assertEquals(2011, getIsoWeekYear(parse("2011-12-31")));
assertEquals(2011, getIsoWeekYear(parse("2012-01-01")));
assertEquals(2012, getIsoWeekYear(parse("2012-01-02")));
assertEquals(2012, getIsoWeekYear(parse("2012-01-03")));
assertEquals(2012, getIsoWeekYear(parse("2012-01-04")));
}
}
......@@ -5,6 +5,9 @@
*/
package org.h2.test.unit;
import java.util.Calendar;
import java.util.GregorianCalendar;
import org.h2.test.TestBase;
import org.h2.util.DateTimeUtils;
......@@ -25,6 +28,8 @@ public class TestDateTimeUtils extends TestBase {
@Override
public void test() throws Exception {
testParseTimeNanosDB2Format();
testDayOfWeek();
testWeekOfYear();
}
private void testParseTimeNanosDB2Format() {
......@@ -34,4 +39,55 @@ public class TestDateTimeUtils extends TestBase {
assertEquals(3723000000000L, DateTimeUtils.parseTimeNanos("01:02:03", 0, 8, true));
assertEquals(3723000000000L, DateTimeUtils.parseTimeNanos("01.02.03", 0, 8, true));
}
/**
* Test for {@link DateTimeUtils#getSundayDayOfWeek()} and
* {@link DateTimeUtils#getIsoDayOfWeek(long)}.
*/
private void testDayOfWeek() {
GregorianCalendar gc = DateTimeUtils.createGregorianCalendar();
for (int i = -1_000_000; i <= 1_000_000; i++) {
gc.clear();
gc.setTimeInMillis(i * 86400000L);
int year = gc.get(Calendar.YEAR);
if (gc.get(Calendar.ERA) == GregorianCalendar.BC) {
year = 1 - year;
}
long expectedDateValue = DateTimeUtils.dateValue(year, gc.get(Calendar.MONTH) + 1,
gc.get(Calendar.DAY_OF_MONTH));
long dateValue = DateTimeUtils.dateValueFromAbsoluteDay(i);
assertEquals(expectedDateValue, dateValue);
assertEquals(i, DateTimeUtils.absoluteDayFromDateValue(dateValue));
int dow = gc.get(Calendar.DAY_OF_WEEK);
assertEquals(dow, DateTimeUtils.getSundayDayOfWeek(dateValue));
int isoDow = (dow + 5) % 7 + 1;
assertEquals(isoDow, DateTimeUtils.getIsoDayOfWeek(dateValue));
assertEquals(gc.get(Calendar.WEEK_OF_YEAR),
DateTimeUtils.getWeekOfYear(dateValue, gc.getFirstDayOfWeek() - 1, gc.getMinimalDaysInFirstWeek()));
}
}
/**
* Test for {@link DateTimeUtils#getDayOfYear(long)},
* {@link DateTimeUtils#getWeekOfYear(long, int, int)} and
* {@link DateTimeUtils#getWeekYear(long, int, int)}.
*/
private void testWeekOfYear() {
GregorianCalendar gc = new GregorianCalendar(DateTimeUtils.UTC);
for (int firstDay = 1; firstDay <= 7; firstDay++) {
gc.setFirstDayOfWeek(firstDay);
for (int minimalDays = 1; minimalDays <= 7; minimalDays++) {
gc.setMinimalDaysInFirstWeek(minimalDays);
for (int i = 0; i < 150_000; i++) {
long dateValue = DateTimeUtils.dateValueFromAbsoluteDay(i);
gc.clear();
gc.setTimeInMillis(i * 86400000L);
assertEquals(gc.get(Calendar.DAY_OF_YEAR), DateTimeUtils.getDayOfYear(dateValue));
assertEquals(gc.get(Calendar.WEEK_OF_YEAR), DateTimeUtils.getWeekOfYear(dateValue, firstDay - 1, minimalDays));
assertEquals(gc.getWeekYear(), DateTimeUtils.getWeekYear(dateValue, firstDay - 1, minimalDays));
}
}
}
}
}
......@@ -487,13 +487,16 @@ public class TestPgServer extends TestBase {
Date[] dates = { null, Date.valueOf("2017-02-20"),
Date.valueOf("1970-01-01"), Date.valueOf("1969-12-31"),
Date.valueOf("1940-01-10"), Date.valueOf("1950-11-10") };
Date.valueOf("1940-01-10"), Date.valueOf("1950-11-10"),
Date.valueOf("1500-01-01")};
Time[] times = { null, Time.valueOf("14:15:16"),
Time.valueOf("00:00:00"), Time.valueOf("23:59:59"),
Time.valueOf("00:10:59"), Time.valueOf("08:30:42") };
Time.valueOf("00:10:59"), Time.valueOf("08:30:42"),
Time.valueOf("10:00:00")};
Timestamp[] timestamps = { null, Timestamp.valueOf("2017-02-20 14:15:16.763"),
Timestamp.valueOf("1970-01-01 00:00:00"), Timestamp.valueOf("1969-12-31 23:59:59"),
Timestamp.valueOf("1940-01-10 00:10:59"), Timestamp.valueOf("1950-11-10 08:30:42.12") };
Timestamp.valueOf("1940-01-10 00:10:59"), Timestamp.valueOf("1950-11-10 08:30:42.12"),
Timestamp.valueOf("1500-01-01 10:00:10")};
int count = dates.length;
PreparedStatement ps = conn.prepareStatement(
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论