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 ...@@ -228,6 +228,7 @@ WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
exec "$JAVACMD" \ exec "$JAVACMD" \
$MAVEN_OPTS \ $MAVEN_OPTS \
-Djava.net.useSystemProxies=true \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CMD_LINE_ARGS ${WRAPPER_LAUNCHER} $MAVEN_CMD_LINE_ARGS
......
...@@ -121,7 +121,7 @@ SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" ...@@ -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_JAR=""%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar""
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 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 if ERRORLEVEL 1 goto error
goto end goto end
......
...@@ -222,6 +222,7 @@ ...@@ -222,6 +222,7 @@
</executions> </executions>
</plugin> </plugin>
<!-- Make sure neither we nor one of our dependencies uses anything outside JDK 1.7 --> <!-- 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> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId> <artifactId>maven-enforcer-plugin</artifactId>
...@@ -262,6 +263,7 @@ ...@@ -262,6 +263,7 @@
</dependency> </dependency>
</dependencies> </dependencies>
</plugin> </plugin>
-->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
......
...@@ -3685,6 +3685,7 @@ Returns the the number of crossed unit boundaries between two timestamps. ...@@ -3685,6 +3685,7 @@ Returns the the number of crossed unit boundaries between two timestamps.
This method returns a long. This method returns a long.
The string indicates the unit. The string indicates the unit.
The same units as in the EXTRACT function are supported. 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) DATEDIFF('YEAR', T1.CREATED, T2.CREATED)
" "
...@@ -3722,7 +3723,7 @@ DAY_OF_YEAR(CREATED) ...@@ -3722,7 +3723,7 @@ DAY_OF_YEAR(CREATED)
" "
"Functions (Time and Date)","EXTRACT"," "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 } | DOY | HOUR | HH | MINUTE | MI | SECOND | SS | MILLISECOND | MS }
FROM timestamp ) FROM timestamp )
"," ","
......
...@@ -490,7 +490,7 @@ Instead, use a prepared statement with arrays as in the following example: ...@@ -490,7 +490,7 @@ Instead, use a prepared statement with arrays as in the following example:
</p> </p>
<pre> <pre>
PreparedStatement prep = conn.prepareStatement( 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" }); prep.setObject(1, new Object[] { "1", "2" });
ResultSet rs = prep.executeQuery(); ResultSet rs = prep.executeQuery();
</pre> </pre>
......
...@@ -112,6 +112,7 @@ import org.h2.expression.Comparison; ...@@ -112,6 +112,7 @@ import org.h2.expression.Comparison;
import org.h2.expression.ConditionAndOr; import org.h2.expression.ConditionAndOr;
import org.h2.expression.ConditionExists; import org.h2.expression.ConditionExists;
import org.h2.expression.ConditionIn; import org.h2.expression.ConditionIn;
import org.h2.expression.ConditionInParameter;
import org.h2.expression.ConditionInSelect; import org.h2.expression.ConditionInSelect;
import org.h2.expression.ConditionNot; import org.h2.expression.ConditionNot;
import org.h2.expression.Expression; import org.h2.expression.Expression;
...@@ -2504,9 +2505,14 @@ public class Parser { ...@@ -2504,9 +2505,14 @@ public class Parser {
read(")"); read(")");
} else if (readIf("ANY") || readIf("SOME")) { } else if (readIf("ANY") || readIf("SOME")) {
read("("); read("(");
if (currentTokenType == PARAMETER && compareType == 0) {
Parameter p = readParameter();
r = new ConditionInParameter(database, r, p);
} else {
Query query = parseSelect(); Query query = parseSelect();
r = new ConditionInSelect(database, r, query, false, r = new ConditionInSelect(database, r, query, false,
compareType); compareType);
}
read(")"); read(")");
} else { } else {
Expression right = readConcat(); Expression right = readConcat();
...@@ -3001,21 +3007,7 @@ public class Parser { ...@@ -3001,21 +3007,7 @@ public class Parser {
return new ExpressionColumn(database, null, objectName, name); return new ExpressionColumn(database, null, objectName, name);
} }
private Expression readTerm() { private Parameter readParameter() {
Expression r;
switch (currentTokenType) {
case AT:
read();
r = new Variable(session, readAliasIdentifier());
if (readIf(":=")) {
Expression value = readExpression();
Function function = Function.getFunction(database, "SET");
function.setParameter(0, r);
function.setParameter(1, value);
r = function;
}
break;
case PARAMETER:
// there must be no space between ? and the number // there must be no space between ? and the number
boolean indexed = Character.isDigit(sqlCommandChars[parseIndex]); boolean indexed = Character.isDigit(sqlCommandChars[parseIndex]);
...@@ -3059,7 +3051,25 @@ public class Parser { ...@@ -3059,7 +3051,25 @@ public class Parser {
p = new Parameter(parameters.size()); p = new Parameter(parameters.size());
} }
parameters.add(p); parameters.add(p);
r = p; return p;
}
private Expression readTerm() {
Expression r;
switch (currentTokenType) {
case AT:
read();
r = new Variable(session, readAliasIdentifier());
if (readIf(":=")) {
Expression value = readExpression();
Function function = Function.getFunction(database, "SET");
function.setParameter(0, r);
function.setParameter(1, value);
r = function;
}
break;
case PARAMETER:
r = readParameter();
break; break;
case KEYWORD: case KEYWORD:
if (isToken("SELECT") || isToken("FROM") || isToken("WITH")) { if (isToken("SELECT") || isToken("FROM") || isToken("WITH")) {
......
...@@ -44,7 +44,11 @@ import java.util.HashSet; ...@@ -44,7 +44,11 @@ import java.util.HashSet;
*/ */
public class Select extends Query { public class Select extends Query {
/**
* The main (top) table filter.
*/
TableFilter topTableFilter; TableFilter topTableFilter;
private final ArrayList<TableFilter> filters = New.arrayList(); private final ArrayList<TableFilter> filters = New.arrayList();
private final ArrayList<TableFilter> topFilters = 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; ...@@ -20,7 +20,6 @@ import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.TimeZone;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException; import java.util.regex.PatternSyntaxException;
...@@ -108,6 +107,11 @@ public class Function extends Expression implements FunctionCall { ...@@ -108,6 +107,11 @@ public class Function extends Expression implements FunctionCall {
FORMATDATETIME = 121, PARSEDATETIME = 122, ISO_YEAR = 123, FORMATDATETIME = 121, PARSEDATETIME = 122, ISO_YEAR = 123,
ISO_WEEK = 124, ISO_DAY_OF_WEEK = 125, EPOCH = 126; 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, public static final int DATABASE = 150, USER = 151, CURRENT_USER = 152,
IDENTITY = 153, SCOPE_IDENTITY = 154, AUTOCOMMIT = 155, IDENTITY = 153, SCOPE_IDENTITY = 154, AUTOCOMMIT = 155,
READONLY = 156, DATABASE_PATH = 157, LOCK_TIMEOUT = 158, READONLY = 156, DATABASE_PATH = 157, LOCK_TIMEOUT = 158,
...@@ -158,40 +162,40 @@ public class Function extends Expression implements FunctionCall { ...@@ -158,40 +162,40 @@ public class Function extends Expression implements FunctionCall {
static { static {
// DATE_PART // DATE_PART
DATE_PART.put("SQL_TSI_YEAR", Calendar.YEAR); DATE_PART.put("SQL_TSI_YEAR", YEAR);
DATE_PART.put("YEAR", Calendar.YEAR); DATE_PART.put("YEAR", YEAR);
DATE_PART.put("YYYY", Calendar.YEAR); DATE_PART.put("YYYY", YEAR);
DATE_PART.put("YY", Calendar.YEAR); DATE_PART.put("YY", YEAR);
DATE_PART.put("SQL_TSI_MONTH", Calendar.MONTH); DATE_PART.put("SQL_TSI_MONTH", MONTH);
DATE_PART.put("MONTH", Calendar.MONTH); DATE_PART.put("MONTH", MONTH);
DATE_PART.put("MM", Calendar.MONTH); DATE_PART.put("MM", MONTH);
DATE_PART.put("M", Calendar.MONTH); DATE_PART.put("M", MONTH);
DATE_PART.put("SQL_TSI_WEEK", Calendar.WEEK_OF_YEAR); DATE_PART.put("SQL_TSI_WEEK", WEEK);
DATE_PART.put("WW", Calendar.WEEK_OF_YEAR); DATE_PART.put("WW", WEEK);
DATE_PART.put("WK", Calendar.WEEK_OF_YEAR); DATE_PART.put("WK", WEEK);
DATE_PART.put("WEEK", Calendar.WEEK_OF_YEAR); DATE_PART.put("WEEK", WEEK);
DATE_PART.put("DAY", Calendar.DAY_OF_MONTH); DATE_PART.put("ISO_WEEK", ISO_WEEK);
DATE_PART.put("DD", Calendar.DAY_OF_MONTH); DATE_PART.put("DAY", DAY_OF_MONTH);
DATE_PART.put("D", Calendar.DAY_OF_MONTH); DATE_PART.put("DD", DAY_OF_MONTH);
DATE_PART.put("SQL_TSI_DAY", Calendar.DAY_OF_MONTH); DATE_PART.put("D", DAY_OF_MONTH);
DATE_PART.put("DAYOFYEAR", Calendar.DAY_OF_YEAR); DATE_PART.put("SQL_TSI_DAY", DAY_OF_MONTH);
DATE_PART.put("DAY_OF_YEAR", Calendar.DAY_OF_YEAR); DATE_PART.put("DAYOFYEAR", DAY_OF_YEAR);
DATE_PART.put("DY", Calendar.DAY_OF_YEAR); DATE_PART.put("DAY_OF_YEAR", DAY_OF_YEAR);
DATE_PART.put("DOY", Calendar.DAY_OF_YEAR); DATE_PART.put("DY", DAY_OF_YEAR);
DATE_PART.put("SQL_TSI_HOUR", Calendar.HOUR_OF_DAY); DATE_PART.put("DOY", DAY_OF_YEAR);
DATE_PART.put("HOUR", Calendar.HOUR_OF_DAY); DATE_PART.put("SQL_TSI_HOUR", HOUR);
DATE_PART.put("HH", Calendar.HOUR_OF_DAY); DATE_PART.put("HOUR", HOUR);
DATE_PART.put("SQL_TSI_MINUTE", Calendar.MINUTE); DATE_PART.put("HH", HOUR);
DATE_PART.put("MINUTE", Calendar.MINUTE); DATE_PART.put("SQL_TSI_MINUTE", MINUTE);
DATE_PART.put("MI", Calendar.MINUTE); DATE_PART.put("MINUTE", MINUTE);
DATE_PART.put("N", Calendar.MINUTE); DATE_PART.put("MI", MINUTE);
DATE_PART.put("SQL_TSI_SECOND", Calendar.SECOND); DATE_PART.put("N", MINUTE);
DATE_PART.put("SECOND", Calendar.SECOND); DATE_PART.put("SQL_TSI_SECOND", SECOND);
DATE_PART.put("SS", Calendar.SECOND); DATE_PART.put("SECOND", SECOND);
DATE_PART.put("S", Calendar.SECOND); DATE_PART.put("SS", SECOND);
DATE_PART.put("MILLISECOND", Calendar.MILLISECOND); DATE_PART.put("S", SECOND);
DATE_PART.put("MS", Calendar.MILLISECOND); DATE_PART.put("MILLISECOND", MILLISECOND);
DATE_PART.put("EPOCH", EPOCH); DATE_PART.put("MS", MILLISECOND);
// SOUNDEX_INDEX // SOUNDEX_INDEX
String index = "7AEIOUY8HW1BFPV2CGJKQSXZ3DT4L5MN6R"; String index = "7AEIOUY8HW1BFPV2CGJKQSXZ3DT4L5MN6R";
...@@ -838,22 +842,19 @@ public class Function extends Expression implements FunctionCall { ...@@ -838,22 +842,19 @@ public class Function extends Expression implements FunctionCall {
break; break;
} }
case DAY_OF_MONTH: case DAY_OF_MONTH:
result = ValueLong.get(DateTimeUtils.getDatePart(v0, Calendar.DAY_OF_MONTH));
break;
case DAY_OF_WEEK: case DAY_OF_WEEK:
result = ValueLong.get(DateTimeUtils.getDatePart(v0, Calendar.DAY_OF_WEEK));
break;
case DAY_OF_YEAR: case DAY_OF_YEAR:
result = ValueLong.get(DateTimeUtils.getDatePart(v0, Calendar.DAY_OF_YEAR));
break;
case HOUR: case HOUR:
result = ValueLong.get(DateTimeUtils.getDatePart(v0, Calendar.HOUR_OF_DAY));
break;
case MINUTE: case MINUTE:
result = ValueLong.get(DateTimeUtils.getDatePart(v0, Calendar.MINUTE));
break;
case MONTH: 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; break;
case MONTH_NAME: { case MONTH_NAME: {
SimpleDateFormat monthName = new SimpleDateFormat("MMMM", SimpleDateFormat monthName = new SimpleDateFormat("MMMM",
...@@ -862,27 +863,6 @@ public class Function extends Expression implements FunctionCall { ...@@ -862,27 +863,6 @@ public class Function extends Expression implements FunctionCall {
database.getMode().treatEmptyStringsAsNull); database.getMode().treatEmptyStringsAsNull);
break; 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 CURDATE:
case CURRENT_DATE: { case CURRENT_DATE: {
long now = session.getTransactionStart(); long now = session.getTransactionStart();
...@@ -1509,8 +1489,7 @@ public class Function extends Expression implements FunctionCall { ...@@ -1509,8 +1489,7 @@ public class Function extends Expression implements FunctionCall {
v0.getString(), v1.getLong(), v2.getTimestamp())); v0.getString(), v1.getLong(), v2.getTimestamp()));
break; break;
case DATE_DIFF: case DATE_DIFF:
result = ValueLong.get(datediff( result = ValueLong.get(datediff(v0.getString(), v1, v2));
v0.getString(), v1.getTimestamp(), v2.getTimestamp()));
break; break;
case EXTRACT: { case EXTRACT: {
int field = getDatePart(v0.getString()); int field = getDatePart(v0.getString());
...@@ -1828,11 +1807,39 @@ public class Function extends Expression implements FunctionCall { ...@@ -1828,11 +1807,39 @@ public class Function extends Expression implements FunctionCall {
private static Timestamp dateadd(String part, long count, Timestamp d) { private static Timestamp dateadd(String part, long count, Timestamp d) {
int field = getDatePart(part); 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); Timestamp ts = new Timestamp(d.getTime() + count);
ts.setNanos(ts.getNanos() + (d.getNanos() % 1000000)); ts.setNanos(ts.getNanos() + (d.getNanos() % 1000000));
return ts; return ts;
} }
default:
throw DbException.getUnsupportedException("DATEADD " + part);
}
// We allow long for manipulating the millisecond component, // We allow long for manipulating the millisecond component,
// for the rest we only allow int. // for the rest we only allow int.
if (count > Integer.MAX_VALUE) { if (count > Integer.MAX_VALUE) {
...@@ -1855,77 +1862,70 @@ public class Function extends Expression implements FunctionCall { ...@@ -1855,77 +1862,70 @@ public class Function extends Expression implements FunctionCall {
* </pre> * </pre>
* *
* @param part the part * @param part the part
* @param d1 the first date * @param v1 the first date-time value
* @param d2 the second date * @param v2 the second date-time value
* @return the number of crossed boundaries * @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); int field = getDatePart(part);
Calendar calendar = DateTimeUtils.createGregorianCalendar(); long[] a1 = DateTimeUtils.dateAndTimeFromValue(v1);
long t1 = d1.getTime(), t2 = d2.getTime(); long dateValue1 = a1[0];
// need to convert to UTC, otherwise we get inconsistent results with long absolute1 = DateTimeUtils.absoluteDayFromDateValue(dateValue1);
// certain time zones (those that are 30 minutes off) long[] a2 = DateTimeUtils.dateAndTimeFromValue(v2);
TimeZone zone = calendar.getTimeZone(); long dateValue2 = a2[0];
calendar.setTime(d1); long absolute2 = DateTimeUtils.absoluteDayFromDateValue(dateValue2);
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));
switch (field) { switch (field) {
case Calendar.MILLISECOND: case MILLISECOND:
return t2 - t1; case SECOND:
case Calendar.SECOND: case MINUTE:
case Calendar.MINUTE: case HOUR:
case Calendar.HOUR_OF_DAY: long timeNanos1 = a1[1];
case Calendar.DAY_OF_YEAR: long timeNanos2 = a2[1];
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;
switch (field) { switch (field) {
case Calendar.SECOND: case MILLISECOND:
return t2 / 1000 - t1 / 1000; return (absolute2 - absolute1) * DateTimeUtils.MILLIS_PER_DAY
case Calendar.MINUTE: + (timeNanos2 / 1_000_000 - timeNanos1 / 1_000_000);
return t2 / (60 * 1000) - t1 / (60 * 1000); case SECOND:
case Calendar.HOUR_OF_DAY: return (absolute2 - absolute1) * 86_400
return t2 / hour - t1 / hour; + (timeNanos2 / 1_000_000_000 - timeNanos1 / 1_000_000_000);
case Calendar.DAY_OF_YEAR: case MINUTE:
return t2 / (hour * 24) - t1 / (hour * 24); return (absolute2 - absolute1) * 1_440
case Calendar.WEEK_OF_YEAR: + (timeNanos2 / 60_000_000_000L - timeNanos1 / 60_000_000_000L);
return t2 / (hour * 24 * 7) - t1 / (hour * 24 * 7); 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: default:
throw DbException.throwInternalError("field:" + field); throw DbException.getUnsupportedException("DATEDIFF " + part);
} }
} }
case Calendar.DATE:
return t2 / (24 * 60 * 60 * 1000) - t1 / (24 * 60 * 60 * 1000); private static long weekdiff(long absolute1, long absolute2, int firstDayOfWeek) {
default: absolute1 += 4 - firstDayOfWeek;
break; long r1 = absolute1 / 7;
if (absolute1 < 0 && (r1 * 7 != absolute1)) {
r1--;
} }
calendar = DateTimeUtils.createGregorianCalendar(DateTimeUtils.UTC); absolute2 += 4 - firstDayOfWeek;
calendar.setTimeInMillis(t1); long r2 = absolute2 / 7;
int year1 = calendar.get(Calendar.YEAR); if (absolute2 < 0 && (r2 * 7 != absolute2)) {
int month1 = calendar.get(Calendar.MONTH); r2--;
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);
} }
return r2 - r1;
} }
private static String substring(String s, int start, int length) { private static String substring(String s, int start, int length) {
......
...@@ -955,6 +955,12 @@ public class FullText { ...@@ -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) static boolean isMultiThread(Connection conn)
throws SQLException { throws SQLException {
try (Statement stat = conn.createStatement()) { try (Statement stat = conn.createStatement()) {
......
...@@ -703,11 +703,21 @@ public class FullTextLucene extends FullText { ...@@ -703,11 +703,21 @@ public class FullTextLucene extends FullText {
searcher = new IndexSearcher(reader); searcher = new IndexSearcher(reader);
} }
/**
* Start using the searcher.
*
* @return the searcher
*/
synchronized IndexSearcher getSearcher() { synchronized IndexSearcher getSearcher() {
++counter; ++counter;
return searcher; return searcher;
} }
/**
* Stop using the searcher.
*
* @param searcher the searcher
*/
synchronized void returnSearcher(IndexSearcher searcher) { synchronized void returnSearcher(IndexSearcher searcher) {
if (this.searcher == searcher) { if (this.searcher == searcher) {
--counter; --counter;
...@@ -738,6 +748,9 @@ public class FullTextLucene extends FullText { ...@@ -738,6 +748,9 @@ public class FullTextLucene extends FullText {
searcher = new IndexSearcher(IndexReader.open(writer, true)); searcher = new IndexSearcher(IndexReader.open(writer, true));
} }
/**
* Close the index.
*/
public synchronized void close() throws IOException { public synchronized void close() throws IOException {
for (IndexSearcher searcher : counters.keySet()) { for (IndexSearcher searcher : counters.keySet()) {
closeSearcher(searcher); closeSearcher(searcher);
......
...@@ -3222,7 +3222,14 @@ public class JdbcResultSet extends TraceObject implements ResultSet, JdbcResultS ...@@ -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); checkColumnIndex(columnIndex);
checkOnValidRow(); checkOnValidRow();
Value[] list; Value[] list;
......
...@@ -405,6 +405,7 @@ public class JdbcDataSource extends TraceObject implements XADataSource, ...@@ -405,6 +405,7 @@ public class JdbcDataSource extends TraceObject implements XADataSource,
* Return an object of this class if possible. * Return an object of this class if possible.
* *
* @param iface the class * @param iface the class
* @return this
*/ */
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
...@@ -423,6 +424,7 @@ public class JdbcDataSource extends TraceObject implements XADataSource, ...@@ -423,6 +424,7 @@ public class JdbcDataSource extends TraceObject implements XADataSource,
* 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
* @return whether or not the interface is assignable from this class
*/ */
@Override @Override
public boolean isWrapperFor(Class<?> iface) throws SQLException { public boolean isWrapperFor(Class<?> iface) throws SQLException {
......
...@@ -896,6 +896,9 @@ public class TransactionStore { ...@@ -896,6 +896,9 @@ public class TransactionStore {
*/ */
final MVMap<K, VersionedValue> map; final MVMap<K, VersionedValue> map;
/**
* The transaction which is used for this map.
*/
final Transaction transaction; final Transaction transaction;
TransactionMap(Transaction transaction, MVMap<K, VersionedValue> map, TransactionMap(Transaction transaction, MVMap<K, VersionedValue> map,
......
...@@ -20,35 +20,38 @@ import java.net.Socket; ...@@ -20,35 +20,38 @@ import java.net.Socket;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.sql.Connection; import java.sql.Connection;
import java.sql.Date;
import java.sql.ParameterMetaData; import java.sql.ParameterMetaData;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.ResultSetMetaData; import java.sql.ResultSetMetaData;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types; import java.sql.Types;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Properties; import java.util.Properties;
import java.util.TimeZone;
import org.h2.command.CommandInterface; import org.h2.command.CommandInterface;
import org.h2.engine.ConnectionInfo; import org.h2.engine.ConnectionInfo;
import org.h2.engine.SysProperties; import org.h2.engine.SysProperties;
import org.h2.jdbc.JdbcConnection; import org.h2.jdbc.JdbcConnection;
import org.h2.jdbc.JdbcPreparedStatement; import org.h2.jdbc.JdbcPreparedStatement;
import org.h2.jdbc.JdbcResultSet;
import org.h2.jdbc.JdbcStatement; import org.h2.jdbc.JdbcStatement;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.mvstore.DataUtils; import org.h2.mvstore.DataUtils;
import org.h2.util.DateTimeUtils;
import org.h2.util.JdbcUtils; import org.h2.util.JdbcUtils;
import org.h2.util.MathUtils; import org.h2.util.MathUtils;
import org.h2.util.ScriptReader; import org.h2.util.ScriptReader;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import org.h2.util.Utils; import org.h2.util.Utils;
import org.h2.value.CaseInsensitiveMap; 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. * One server thread is opened for each client.
...@@ -527,153 +530,91 @@ public class PgServerThread implements Runnable { ...@@ -527,153 +530,91 @@ public class PgServerThread implements Runnable {
sendMessage(); sendMessage();
} }
private static long toPostgreSeconds(long millis) { private static long toPostgreDays(long dateValue) {
// TODO handle Julian/Gregorian transitions return DateTimeUtils.prolepticGregorianAbsoluteDayFromDateValue(dateValue) - 10_957;
return millis / 1000 - 946684800L;
} }
private void writeDataColumn(ResultSet rs, int column, int pgType, boolean text) private void writeDataColumn(ResultSet rs, int column, int pgType, boolean text)
throws Exception { throws Exception {
Value v = ((JdbcResultSet) rs).get(column);
if (v == ValueNull.INSTANCE) {
writeInt(-1);
return;
}
if (text) { if (text) {
// plain text // plain text
switch (pgType) { switch (pgType) {
case PgServer.PG_TYPE_BOOL: { case PgServer.PG_TYPE_BOOL:
boolean b = rs.getBoolean(column);
if (rs.wasNull()) {
writeInt(-1);
} else {
writeInt(1); writeInt(1);
dataOut.writeByte(b ? 't' : 'f'); dataOut.writeByte(v.getBoolean() ? 't' : 'f');
}
break; break;
}
default: default:
String s = rs.getString(column); byte[] data = v.getString().getBytes(getEncoding());
if (s == null) {
writeInt(-1);
} else {
byte[] data = s.getBytes(getEncoding());
writeInt(data.length); writeInt(data.length);
write(data); write(data);
} }
}
} else { } else {
// binary // binary
switch (pgType) { switch (pgType) {
case PgServer.PG_TYPE_INT2: { case PgServer.PG_TYPE_INT2:
short s = rs.getShort(column);
if (rs.wasNull()) {
writeInt(-1);
} else {
writeInt(2); writeInt(2);
writeShort(s); writeShort(v.getShort());
}
break; break;
} case PgServer.PG_TYPE_INT4:
case PgServer.PG_TYPE_INT4: {
int i = rs.getInt(column);
if (rs.wasNull()) {
writeInt(-1);
} else {
writeInt(4); writeInt(4);
writeInt(i); writeInt(v.getInt());
}
break; break;
} case PgServer.PG_TYPE_INT8:
case PgServer.PG_TYPE_INT8: {
long l = rs.getLong(column);
if (rs.wasNull()) {
writeInt(-1);
} else {
writeInt(8); writeInt(8);
dataOut.writeLong(l); dataOut.writeLong(v.getLong());
}
break; break;
} case PgServer.PG_TYPE_FLOAT4:
case PgServer.PG_TYPE_FLOAT4: {
float f = rs.getFloat(column);
if (rs.wasNull()) {
writeInt(-1);
} else {
writeInt(4); writeInt(4);
dataOut.writeFloat(f); dataOut.writeFloat(v.getFloat());
}
break; break;
} case PgServer.PG_TYPE_FLOAT8:
case PgServer.PG_TYPE_FLOAT8: {
double d = rs.getDouble(column);
if (rs.wasNull()) {
writeInt(-1);
} else {
writeInt(8); writeInt(8);
dataOut.writeDouble(d); dataOut.writeDouble(v.getDouble());
}
break; break;
}
case PgServer.PG_TYPE_BYTEA: { case PgServer.PG_TYPE_BYTEA: {
byte[] data = rs.getBytes(column); byte[] data = v.getBytesNoCopy();
if (data == null) {
writeInt(-1);
} else {
writeInt(data.length); writeInt(data.length);
write(data); write(data);
}
break; break;
} }
case PgServer.PG_TYPE_DATE: { case PgServer.PG_TYPE_DATE: {
Date d = rs.getDate(column); ValueDate d = (ValueDate) v.convertTo(Value.DATE);
if (d == null) {
writeInt(-1);
} else {
writeInt(4); writeInt(4);
long millis = d.getTime(); writeInt((int) (toPostgreDays(d.getDateValue())));
millis += TimeZone.getDefault().getOffset(millis);
writeInt((int) (toPostgreSeconds(millis) / 86400));
}
break; break;
} }
case PgServer.PG_TYPE_TIME: { case PgServer.PG_TYPE_TIME: {
Time t = rs.getTime(column); ValueTime t = (ValueTime) v.convertTo(Value.TIME);
if (t == null) {
writeInt(-1);
} else {
writeInt(8); writeInt(8);
long m = t.getTime(); long m = t.getNanos();
m += TimeZone.getDefault().getOffset(m);
if (INTEGER_DATE_TYPES) { if (INTEGER_DATE_TYPES) {
// long format // long format
m *= 1000; m /= 1_000;
} else { } else {
// double format // double format
m /= 1000; m = Double.doubleToLongBits(m * 0.000_000_001);
m = Double.doubleToLongBits(m);
} }
dataOut.writeLong(m); dataOut.writeLong(m);
}
break; break;
} }
case PgServer.PG_TYPE_TIMESTAMP_NO_TMZONE: { case PgServer.PG_TYPE_TIMESTAMP_NO_TMZONE: {
Timestamp t = rs.getTimestamp(column); ValueTimestamp t = (ValueTimestamp) v.convertTo(Value.TIMESTAMP);
if (t == null) {
writeInt(-1);
} else {
writeInt(8); writeInt(8);
long m = t.getTime(); long m = toPostgreDays(t.getDateValue()) * 86_400;
m += TimeZone.getDefault().getOffset(m); long nanos = t.getTimeNanos();
m = toPostgreSeconds(m);
int nanos = t.getNanos();
if (m < 0 && nanos != 0) {
m--;
}
if (INTEGER_DATE_TYPES) { if (INTEGER_DATE_TYPES) {
// long format // long format
m = m * 1000000 + nanos / 1000; m = m * 1_000_000 + nanos / 1_000;
} else { } else {
// double format // double format
m = Double.doubleToLongBits(m + nanos * 0.000000001); m = Double.doubleToLongBits(m + nanos * 0.000_000_001);
} }
dataOut.writeLong(m); dataOut.writeLong(m);
}
break; break;
} }
default: throw new IllegalStateException("output binary format is undefined"); default: throw new IllegalStateException("output binary format is undefined");
......
...@@ -37,6 +37,11 @@ public class TableSynonym extends SchemaObjectBase { ...@@ -37,6 +37,11 @@ public class TableSynonym extends SchemaObjectBase {
return synonymFor; return synonymFor;
} }
/**
* Set (update) the data.
*
* @param data the new data
*/
public void updateData(CreateSynonymData data) { public void updateData(CreateSynonymData data) {
this.data = data; this.data = data;
} }
...@@ -46,7 +51,6 @@ public class TableSynonym extends SchemaObjectBase { ...@@ -46,7 +51,6 @@ public class TableSynonym extends SchemaObjectBase {
return SYNONYM; return SYNONYM;
} }
@Override @Override
public String getCreateSQLForCopy(Table table, String quotedName) { public String getCreateSQLForCopy(Table table, String quotedName) {
return synonymFor.getCreateSQLForCopy(table, quotedName); return synonymFor.getCreateSQLForCopy(table, quotedName);
......
...@@ -76,9 +76,11 @@ public class TableView extends Table { ...@@ -76,9 +76,11 @@ public class TableView extends Table {
* dependent views. * dependent views.
* *
* @param querySQL the SQL statement * @param querySQL the SQL statement
* @param newColumnTemplates the columns
* @param session the session * @param session the session
* @param recursive whether this is a recursive view * @param recursive whether this is a recursive view
* @param force if errors should be ignored * @param force if errors should be ignored
* @param literalsChecked if literals have been checked
*/ */
public void replace(String querySQL, Column[] newColumnTemplates, Session session, public void replace(String querySQL, Column[] newColumnTemplates, Session session,
boolean recursive, boolean force, boolean literalsChecked) { boolean recursive, boolean force, boolean literalsChecked) {
...@@ -715,6 +717,22 @@ public class TableView extends Table { ...@@ -715,6 +717,22 @@ public class TableView extends Table {
return isPersistent; 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, public static TableView createTableViewMaybeRecursive(Schema schema, int id, String name, String querySQL,
ArrayList<Parameter> parameters, Column[] columnTemplates, Session session, ArrayList<Parameter> parameters, Column[] columnTemplates, Session session,
boolean literalsChecked, boolean isTableExpression, boolean isPersistent, Database db) { boolean literalsChecked, boolean isTableExpression, boolean isPersistent, Database db) {
...@@ -805,6 +823,17 @@ public class TableView extends Table { ...@@ -805,6 +823,17 @@ public class TableView extends Table {
return columnTemplateList; 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, public static Table createShadowTableForRecursiveTableExpression(boolean isPersistent, Session targetSession,
String cteViewName, Schema schema, List<Column> columns, Database db) { String cteViewName, Schema schema, List<Column> columns, Database db) {
...@@ -834,6 +863,13 @@ public class TableView extends Table { ...@@ -834,6 +863,13 @@ public class TableView extends Table {
return recursiveTable; 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, public static void destroyShadowTableForRecursiveExpression(boolean isPersistent, Session targetSession,
Table recursiveTable) { Table recursiveTable) {
if (recursiveTable != null) { if (recursiveTable != null) {
......
...@@ -25,6 +25,7 @@ import org.h2.value.ValueTime; ...@@ -25,6 +25,7 @@ import org.h2.value.ValueTime;
import org.h2.value.ValueTimestamp; import org.h2.value.ValueTimestamp;
import org.h2.value.ValueTimestampTimeZone; import org.h2.value.ValueTimestampTimeZone;
/** /**
* This utility class contains time conversion functions. * This utility class contains time conversion functions.
* <p> * <p>
...@@ -299,30 +300,6 @@ public class DateTimeUtils { ...@@ -299,30 +300,6 @@ public class DateTimeUtils {
return ValueTimestamp.fromDateValueAndNanos(dateValue, nanos); 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 * Parse a date string. The format is: [+|-]year-month-day
* *
...@@ -585,52 +562,79 @@ public class DateTimeUtils { ...@@ -585,52 +562,79 @@ public class DateTimeUtils {
} }
/** /**
* Get the specified field of a date, however with years normalized to * Extracts date value and nanos of day from the specified value.
* positive or negative, and month starting with 1.
* *
* @param date the date value * @param value
* @param field the field type * value to extract fields from
* @return the value * @return array with date value and nanos of day
*/ */
public static long[] dateAndTimeFromValue(Value value) {
public static int getDatePart(Value date, int field) {
long dateValue = EPOCH_DATE_VALUE; long dateValue = EPOCH_DATE_VALUE;
long timeNanos = 0; long timeNanos = 0;
if (date instanceof ValueTimestamp) { if (value instanceof ValueTimestamp) {
ValueTimestamp v = (ValueTimestamp) date; ValueTimestamp v = (ValueTimestamp) value;
dateValue = v.getDateValue(); dateValue = v.getDateValue();
timeNanos = v.getTimeNanos(); timeNanos = v.getTimeNanos();
} else if (date instanceof ValueDate) { } else if (value instanceof ValueDate) {
dateValue = ((ValueDate) date).getDateValue(); dateValue = ((ValueDate) value).getDateValue();
} else if (date instanceof ValueTime) { } else if (value instanceof ValueTime) {
timeNanos = ((ValueTime) date).getNanos(); timeNanos = ((ValueTime) value).getNanos();
} else if (date instanceof ValueTimestampTimeZone) { } else if (value instanceof ValueTimestampTimeZone) {
ValueTimestampTimeZone v = (ValueTimestampTimeZone) date; ValueTimestampTimeZone v = (ValueTimestampTimeZone) value;
dateValue = v.getDateValue(); dateValue = v.getDateValue();
timeNanos = v.getTimeNanos(); timeNanos = v.getTimeNanos();
} else { } else {
ValueTimestamp v = (ValueTimestamp) date.convertTo(Value.TIMESTAMP); ValueTimestamp v = (ValueTimestamp) value.convertTo(Value.TIMESTAMP);
date = v; // For valueToCalendar() to avoid second convertTo() call
dateValue = v.getDateValue(); dateValue = v.getDateValue();
timeNanos = v.getTimeNanos(); 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) { switch (field) {
case Calendar.YEAR: case Function.YEAR:
return yearFromDateValue(dateValue); return yearFromDateValue(dateValue);
case Calendar.MONTH: case Function.MONTH:
return monthFromDateValue(dateValue); return monthFromDateValue(dateValue);
case Calendar.DAY_OF_MONTH: case Function.DAY_OF_MONTH:
return dayFromDateValue(dateValue); return dayFromDateValue(dateValue);
case Calendar.HOUR_OF_DAY: case Function.HOUR:
return (int) (timeNanos / 3_600_000_000_000L % 24); return (int) (timeNanos / 3_600_000_000_000L % 24);
case Calendar.MINUTE: case Function.MINUTE:
return (int) (timeNanos / 60_000_000_000L % 60); return (int) (timeNanos / 60_000_000_000L % 60);
case Calendar.SECOND: case Function.SECOND:
return (int) (timeNanos / 1_000_000_000 % 60); return (int) (timeNanos / 1_000_000_000 % 60);
case Calendar.MILLISECOND: case Function.MILLISECOND:
return (int) (timeNanos / 1_000_000 % 1_000); return (int) (timeNanos / 1_000_000 % 1_000);
} case Function.DAY_OF_YEAR:
return valueToCalendar(date).get(field); 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);
}
throw DbException.getUnsupportedException("getDatePart(" + date + ", " + field + ')');
} }
/** /**
...@@ -670,57 +674,144 @@ public class DateTimeUtils { ...@@ -670,57 +674,144 @@ public class DateTimeUtils {
} }
/** /**
* Return the day of week according to the ISO 8601 specification. Week * Returns day of week.
* starts at Monday. See also http://en.wikipedia.org/wiki/ISO_8601
* *
* @author Robert Rathsack * @param dateValue
* @param value the date object which day of week should be calculated * the date value
* @return the day of the week, Monday as 1 to Sunday as 7 * @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) { public static int getDayOfWeek(long dateValue, int firstDayOfWeek) {
int val = valueToCalendar(value).get(Calendar.DAY_OF_WEEK) - 1; return getDayOfWeekFromAbsolute(absoluteDayFromDateValue(dateValue), firstDayOfWeek);
return val == 0 ? 7 : val; }
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 * Returns number of day in year.
* 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.
* *
* @author Robert Rathsack * @param dateValue
* @param value the date object which week of year should be calculated * the date value
* @return the week of the year * @return number of day in year
*/ */
public static int getIsoWeek(Value value) { public static int getDayOfYear(long dateValue) {
Calendar c = valueToCalendar(value); int year = yearFromDateValue(dateValue);
c.setFirstDayOfWeek(Calendar.MONDAY); return (int) (absoluteDayFromDateValue(dateValue) - absoluteDayFromDateValue(dateValue(year, 1, 1))) + 1;
c.setMinimalDaysInFirstWeek(4);
return c.get(Calendar.WEEK_OF_YEAR);
} }
/** /**
* Returns the year according to the ISO week definition. * Returns ISO day of week.
* *
* @author Robert Rathsack * @param dateValue
* @param value the date object which year should be calculated * the date value
* @return the year * @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 getIsoYear(Value value) { public static int getIsoWeekOfYear(long dateValue) {
Calendar cal = valueToCalendar(value); return getWeekOfYear(dateValue, 1, 4);
cal.setFirstDayOfWeek(Calendar.MONDAY); }
cal.setMinimalDaysInFirstWeek(4);
int year = getYear(cal); /**
int month = cal.get(Calendar.MONTH); * Returns ISO week year.
int week = cal.get(Calendar.WEEK_OF_YEAR); *
if (month == 0 && week > 51) { * @param dateValue
year--; * the date value
} else if (month == 11 && week == 1) { * @return ISO week year
year++; * @see #getIsoWeekOfYear(long)
* @see #getWeekYear(long, int, int)
*/
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; return year;
} }
...@@ -1043,6 +1134,29 @@ public class DateTimeUtils { ...@@ -1043,6 +1134,29 @@ public class DateTimeUtils {
return a; 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. * Calculate the encoded date value from an absolute day.
* *
......
...@@ -543,8 +543,15 @@ public class LocalDateTimeUtils { ...@@ -543,8 +543,15 @@ public class LocalDateTimeUtils {
int year = DateTimeUtils.yearFromDateValue(dateValue); int year = DateTimeUtils.yearFromDateValue(dateValue);
int month = DateTimeUtils.monthFromDateValue(dateValue); int month = DateTimeUtils.monthFromDateValue(dateValue);
int day = DateTimeUtils.dayFromDateValue(dateValue); int day = DateTimeUtils.dayFromDateValue(dateValue);
try {
return LOCAL_DATE_OF_YEAR_MONTH_DAY.invoke(null, year, month, day); 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) private static Object localDateTimeFromDateNanos(long dateValue, long timeNanos)
......
...@@ -15,6 +15,9 @@ import java.util.Comparator; ...@@ -15,6 +15,9 @@ import java.util.Comparator;
*/ */
public class CharsetCollator extends Collator { public class CharsetCollator extends Collator {
/**
* The comparator used to compare byte arrays.
*/
static final Comparator<byte[]> COMPARATOR = new Comparator<byte[]>() { static final Comparator<byte[]> COMPARATOR = new Comparator<byte[]>() {
@Override @Override
public int compare(byte[] b1, byte[] b2) { public int compare(byte[] b1, byte[] b2) {
......
...@@ -14,6 +14,7 @@ import java.math.BigDecimal; ...@@ -14,6 +14,7 @@ import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
import java.net.URL; import java.net.URL;
import java.sql.Connection; import java.sql.Connection;
import java.sql.Date;
import java.sql.ParameterMetaData; import java.sql.ParameterMetaData;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
...@@ -23,6 +24,8 @@ import java.sql.SQLException; ...@@ -23,6 +24,8 @@ import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.sql.Types; import java.sql.Types;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.UUID; import java.util.UUID;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.api.Trigger; import org.h2.api.Trigger;
...@@ -99,6 +102,7 @@ public class TestPreparedStatement extends TestBase { ...@@ -99,6 +102,7 @@ public class TestPreparedStatement extends TestBase {
conn.close(); conn.close();
testPreparedStatementWithLiteralsNone(); testPreparedStatementWithLiteralsNone();
testPreparedStatementWithIndexedParameterAndLiteralsNone(); testPreparedStatementWithIndexedParameterAndLiteralsNone();
testPreparedStatementWithAnyParameter();
deleteDb("preparedStatement"); deleteDb("preparedStatement");
} }
...@@ -644,6 +648,60 @@ public class TestPreparedStatement extends TestBase { ...@@ -644,6 +648,60 @@ public class TestPreparedStatement extends TestBase {
localDate2 = rs.getObject(1, LocalDateTimeUtils.LOCAL_DATE); localDate2 = rs.getObject(1, LocalDateTimeUtils.LOCAL_DATE);
assertEquals(localDate, localDate2); assertEquals(localDate, localDate2);
rs.close(); 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 { private void testTime8(Connection conn) throws SQLException {
...@@ -1491,7 +1549,43 @@ public class TestPreparedStatement extends TestBase { ...@@ -1491,7 +1549,43 @@ public class TestPreparedStatement extends TestBase {
deleteDb("preparedStatement"); 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 { private void checkBigDecimal(ResultSet rs, String[] value) throws SQLException {
for (String v : value) { for (String v : value) {
......
...@@ -158,3 +158,39 @@ call datediff('MS', TIMESTAMP '1900-01-01 00:00:01.000', TIMESTAMP '2008-01-01 0 ...@@ -158,3 +158,39 @@ call datediff('MS', TIMESTAMP '1900-01-01 00:00:01.000', TIMESTAMP '2008-01-01 0
> ------------- > -------------
> 3408134399000 > 3408134399000
> rows: 1 > 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 @@ ...@@ -5,13 +5,17 @@
*/ */
package org.h2.test.synth; package org.h2.test.synth;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.Field;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DriverManager; import java.sql.DriverManager;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.Statement; import java.sql.Statement;
import java.util.Random; import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.test.utils.SelfDestructor; import org.h2.test.utils.SelfDestructor;
...@@ -58,7 +62,7 @@ public class TestKillRestart extends TestBase { ...@@ -58,7 +62,7 @@ public class TestKillRestart extends TestBase {
Thread.sleep(100); Thread.sleep(100);
printTime("killing: " + i); printTime("killing: " + i);
p.destroy(); p.destroy();
p.waitFor(); waitForTimeout(p);
break; break;
} else if (s.startsWith("#Fail")) { } else if (s.startsWith("#Fail")) {
fail("Failed: " + s); fail("Failed: " + s);
...@@ -68,6 +72,58 @@ public class TestKillRestart extends TestBase { ...@@ -68,6 +72,58 @@ public class TestKillRestart extends TestBase {
deleteDb("killRestart"); 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 * This method is called when executing this application from the command
* line. * line.
...@@ -77,7 +133,7 @@ public class TestKillRestart extends TestBase { ...@@ -77,7 +133,7 @@ public class TestKillRestart extends TestBase {
public static void main(String... args) { public static void main(String... args) {
SelfDestructor.startCountdown(60); SelfDestructor.startCountdown(60);
String driver = "org.h2.Driver"; 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++) { for (int i = 0; i < args.length; i++) {
if ("-url".equals(args[i])) { if ("-url".equals(args[i])) {
url = args[++i]; url = args[++i];
......
...@@ -6,11 +6,10 @@ ...@@ -6,11 +6,10 @@
package org.h2.test.unit; package org.h2.test.unit;
import static org.h2.util.DateTimeUtils.getIsoDayOfWeek; import static org.h2.util.DateTimeUtils.getIsoDayOfWeek;
import static org.h2.util.DateTimeUtils.getIsoWeek; import static org.h2.util.DateTimeUtils.getIsoWeekOfYear;
import static org.h2.util.DateTimeUtils.getIsoYear; import static org.h2.util.DateTimeUtils.getIsoWeekYear;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.value.Value;
import org.h2.value.ValueDate; import org.h2.value.ValueDate;
import org.h2.value.ValueTimestamp; import org.h2.value.ValueTimestamp;
import org.h2.value.ValueTimestampTimeZone; import org.h2.value.ValueTimestampTimeZone;
...@@ -35,21 +34,21 @@ public class TestDateIso8601 extends TestBase { ...@@ -35,21 +34,21 @@ public class TestDateIso8601 extends TestBase {
TestBase.createCaller().init().test(); TestBase.createCaller().init().test();
} }
private static Value parse(String s) { private static long parse(String s) {
if (type == null) { if (type == null) {
throw new IllegalStateException(); throw new IllegalStateException();
} }
switch (type) { switch (type) {
case DATE: case DATE:
return ValueDate.parse(s); return ValueDate.parse(s).getDateValue();
case TIMESTAMP: case TIMESTAMP:
return ValueTimestamp.parse(s); return ValueTimestamp.parse(s).getDateValue();
case TIMESTAMP_TIMEZONE_0: 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: 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: 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: default:
throw new IllegalStateException(); throw new IllegalStateException();
} }
...@@ -104,57 +103,57 @@ public class TestDateIso8601 extends TestBase { ...@@ -104,57 +103,57 @@ public class TestDateIso8601 extends TestBase {
* January 1st is a Monday therefore the week belongs to the next year. * January 1st is a Monday therefore the week belongs to the next year.
*/ */
private void testIsoWeekJanuary1thMonday() throws Exception { private void testIsoWeekJanuary1thMonday() throws Exception {
assertEquals(52, getIsoWeek(parse("2006-12-31"))); assertEquals(52, getIsoWeekOfYear(parse("2006-12-31")));
assertEquals(1, getIsoWeek(parse("2007-01-01"))); assertEquals(1, getIsoWeekOfYear(parse("2007-01-01")));
assertEquals(1, getIsoWeek(parse("2007-01-07"))); assertEquals(1, getIsoWeekOfYear(parse("2007-01-07")));
assertEquals(2, getIsoWeek(parse("2007-01-08"))); assertEquals(2, getIsoWeekOfYear(parse("2007-01-08")));
} }
/** /**
* January 1st is a Tuesday therefore the week belongs to the next year. * January 1st is a Tuesday therefore the week belongs to the next year.
*/ */
private void testIsoWeekJanuary1thTuesday() throws Exception { private void testIsoWeekJanuary1thTuesday() throws Exception {
assertEquals(52, getIsoWeek(parse("2007-12-30"))); assertEquals(52, getIsoWeekOfYear(parse("2007-12-30")));
assertEquals(1, getIsoWeek(parse("2007-12-31"))); assertEquals(1, getIsoWeekOfYear(parse("2007-12-31")));
assertEquals(1, getIsoWeek(parse("2008-01-01"))); assertEquals(1, getIsoWeekOfYear(parse("2008-01-01")));
assertEquals(1, getIsoWeek(parse("2008-01-06"))); assertEquals(1, getIsoWeekOfYear(parse("2008-01-06")));
assertEquals(2, getIsoWeek(parse("2008-01-07"))); assertEquals(2, getIsoWeekOfYear(parse("2008-01-07")));
} }
/** /**
* January1th is a Wednesday therefore the week belongs to the next year. * January1th is a Wednesday therefore the week belongs to the next year.
*/ */
private void testIsoWeekJanuary1thWednesday() throws Exception { private void testIsoWeekJanuary1thWednesday() throws Exception {
assertEquals(52, getIsoWeek(parse("2002-12-28"))); assertEquals(52, getIsoWeekOfYear(parse("2002-12-28")));
assertEquals(52, getIsoWeek(parse("2002-12-29"))); assertEquals(52, getIsoWeekOfYear(parse("2002-12-29")));
assertEquals(1, getIsoWeek(parse("2002-12-30"))); assertEquals(1, getIsoWeekOfYear(parse("2002-12-30")));
assertEquals(1, getIsoWeek(parse("2002-12-31"))); assertEquals(1, getIsoWeekOfYear(parse("2002-12-31")));
assertEquals(1, getIsoWeek(parse("2003-01-01"))); assertEquals(1, getIsoWeekOfYear(parse("2003-01-01")));
assertEquals(1, getIsoWeek(parse("2003-01-05"))); assertEquals(1, getIsoWeekOfYear(parse("2003-01-05")));
assertEquals(2, getIsoWeek(parse("2003-01-06"))); assertEquals(2, getIsoWeekOfYear(parse("2003-01-06")));
} }
/** /**
* January 1st is a Thursday therefore the week belongs to the next year. * January 1st is a Thursday therefore the week belongs to the next year.
*/ */
private void testIsoWeekJanuary1thThursday() throws Exception { private void testIsoWeekJanuary1thThursday() throws Exception {
assertEquals(52, getIsoWeek(parse("2008-12-28"))); assertEquals(52, getIsoWeekOfYear(parse("2008-12-28")));
assertEquals(1, getIsoWeek(parse("2008-12-29"))); assertEquals(1, getIsoWeekOfYear(parse("2008-12-29")));
assertEquals(1, getIsoWeek(parse("2008-12-30"))); assertEquals(1, getIsoWeekOfYear(parse("2008-12-30")));
assertEquals(1, getIsoWeek(parse("2008-12-31"))); assertEquals(1, getIsoWeekOfYear(parse("2008-12-31")));
assertEquals(1, getIsoWeek(parse("2009-01-01"))); assertEquals(1, getIsoWeekOfYear(parse("2009-01-01")));
assertEquals(1, getIsoWeek(parse("2009-01-04"))); assertEquals(1, getIsoWeekOfYear(parse("2009-01-04")));
assertEquals(2, getIsoWeek(parse("2009-01-09"))); assertEquals(2, getIsoWeekOfYear(parse("2009-01-09")));
} }
/** /**
* January 1st is a Friday therefore the week belongs to the previous year. * January 1st is a Friday therefore the week belongs to the previous year.
*/ */
private void testIsoWeekJanuary1thFriday() throws Exception { private void testIsoWeekJanuary1thFriday() throws Exception {
assertEquals(53, getIsoWeek(parse("2009-12-31"))); assertEquals(53, getIsoWeekOfYear(parse("2009-12-31")));
assertEquals(53, getIsoWeek(parse("2010-01-01"))); assertEquals(53, getIsoWeekOfYear(parse("2010-01-01")));
assertEquals(53, getIsoWeek(parse("2010-01-03"))); assertEquals(53, getIsoWeekOfYear(parse("2010-01-03")));
assertEquals(1, getIsoWeek(parse("2010-01-04"))); assertEquals(1, getIsoWeekOfYear(parse("2010-01-04")));
} }
/** /**
...@@ -162,34 +161,34 @@ public class TestDateIso8601 extends TestBase { ...@@ -162,34 +161,34 @@ public class TestDateIso8601 extends TestBase {
* year. * year.
*/ */
private void testIsoWeekJanuary1thSaturday() throws Exception { private void testIsoWeekJanuary1thSaturday() throws Exception {
assertEquals(52, getIsoWeek(parse("2010-12-31"))); assertEquals(52, getIsoWeekOfYear(parse("2010-12-31")));
assertEquals(52, getIsoWeek(parse("2011-01-01"))); assertEquals(52, getIsoWeekOfYear(parse("2011-01-01")));
assertEquals(52, getIsoWeek(parse("2011-01-02"))); assertEquals(52, getIsoWeekOfYear(parse("2011-01-02")));
assertEquals(1, getIsoWeek(parse("2011-01-03"))); assertEquals(1, getIsoWeekOfYear(parse("2011-01-03")));
} }
/** /**
* January 1st is a Sunday therefore the week belongs to the previous year. * January 1st is a Sunday therefore the week belongs to the previous year.
*/ */
private void testIsoWeekJanuary1thSunday() throws Exception { private void testIsoWeekJanuary1thSunday() throws Exception {
assertEquals(52, getIsoWeek(parse("2011-12-31"))); assertEquals(52, getIsoWeekOfYear(parse("2011-12-31")));
assertEquals(52, getIsoWeek(parse("2012-01-01"))); assertEquals(52, getIsoWeekOfYear(parse("2012-01-01")));
assertEquals(1, getIsoWeek(parse("2012-01-02"))); assertEquals(1, getIsoWeekOfYear(parse("2012-01-02")));
assertEquals(1, getIsoWeek(parse("2012-01-08"))); assertEquals(1, getIsoWeekOfYear(parse("2012-01-08")));
assertEquals(2, getIsoWeek(parse("2012-01-09"))); assertEquals(2, getIsoWeekOfYear(parse("2012-01-09")));
} }
/** /**
* January 1st is a Monday therefore year is equal to isoYear. * January 1st is a Monday therefore year is equal to isoYear.
*/ */
private void testIsoYearJanuary1thMonday() throws Exception { private void testIsoYearJanuary1thMonday() throws Exception {
assertEquals(2006, getIsoYear(parse("2006-12-28"))); assertEquals(2006, getIsoWeekYear(parse("2006-12-28")));
assertEquals(2006, getIsoYear(parse("2006-12-29"))); assertEquals(2006, getIsoWeekYear(parse("2006-12-29")));
assertEquals(2006, getIsoYear(parse("2006-12-30"))); assertEquals(2006, getIsoWeekYear(parse("2006-12-30")));
assertEquals(2006, getIsoYear(parse("2006-12-31"))); assertEquals(2006, getIsoWeekYear(parse("2006-12-31")));
assertEquals(2007, getIsoYear(parse("2007-01-01"))); assertEquals(2007, getIsoWeekYear(parse("2007-01-01")));
assertEquals(2007, getIsoYear(parse("2007-01-02"))); assertEquals(2007, getIsoWeekYear(parse("2007-01-02")));
assertEquals(2007, getIsoYear(parse("2007-01-03"))); assertEquals(2007, getIsoWeekYear(parse("2007-01-03")));
} }
/** /**
...@@ -197,14 +196,14 @@ public class TestDateIso8601 extends TestBase { ...@@ -197,14 +196,14 @@ public class TestDateIso8601 extends TestBase {
* year. * year.
*/ */
private void testIsoYearJanuary1thTuesday() throws Exception { private void testIsoYearJanuary1thTuesday() throws Exception {
assertEquals(2007, getIsoYear(parse("2007-12-28"))); assertEquals(2007, getIsoWeekYear(parse("2007-12-28")));
assertEquals(2007, getIsoYear(parse("2007-12-29"))); assertEquals(2007, getIsoWeekYear(parse("2007-12-29")));
assertEquals(2007, getIsoYear(parse("2007-12-30"))); assertEquals(2007, getIsoWeekYear(parse("2007-12-30")));
assertEquals(2008, getIsoYear(parse("2007-12-31"))); assertEquals(2008, getIsoWeekYear(parse("2007-12-31")));
assertEquals(2008, getIsoYear(parse("2008-01-01"))); assertEquals(2008, getIsoWeekYear(parse("2008-01-01")));
assertEquals(2008, getIsoYear(parse("2008-01-02"))); assertEquals(2008, getIsoWeekYear(parse("2008-01-02")));
assertEquals(2008, getIsoYear(parse("2008-01-03"))); assertEquals(2008, getIsoWeekYear(parse("2008-01-03")));
assertEquals(2008, getIsoYear(parse("2008-01-04"))); assertEquals(2008, getIsoWeekYear(parse("2008-01-04")));
} }
/** /**
...@@ -212,13 +211,13 @@ public class TestDateIso8601 extends TestBase { ...@@ -212,13 +211,13 @@ public class TestDateIso8601 extends TestBase {
* the next year. * the next year.
*/ */
private void testIsoYearJanuary1thWednesday() throws Exception { private void testIsoYearJanuary1thWednesday() throws Exception {
assertEquals(2002, getIsoYear(parse("2002-12-28"))); assertEquals(2002, getIsoWeekYear(parse("2002-12-28")));
assertEquals(2002, getIsoYear(parse("2002-12-29"))); assertEquals(2002, getIsoWeekYear(parse("2002-12-29")));
assertEquals(2003, getIsoYear(parse("2002-12-30"))); assertEquals(2003, getIsoWeekYear(parse("2002-12-30")));
assertEquals(2003, getIsoYear(parse("2002-12-31"))); assertEquals(2003, getIsoWeekYear(parse("2002-12-31")));
assertEquals(2003, getIsoYear(parse("2003-01-01"))); assertEquals(2003, getIsoWeekYear(parse("2003-01-01")));
assertEquals(2003, getIsoYear(parse("2003-01-02"))); assertEquals(2003, getIsoWeekYear(parse("2003-01-02")));
assertEquals(2003, getIsoYear(parse("2003-12-02"))); assertEquals(2003, getIsoWeekYear(parse("2003-12-02")));
} }
/** /**
...@@ -226,14 +225,14 @@ public class TestDateIso8601 extends TestBase { ...@@ -226,14 +225,14 @@ public class TestDateIso8601 extends TestBase {
* next year. * next year.
*/ */
private void testIsoYearJanuary1thThursday() throws Exception { private void testIsoYearJanuary1thThursday() throws Exception {
assertEquals(2008, getIsoYear(parse("2008-12-28"))); assertEquals(2008, getIsoWeekYear(parse("2008-12-28")));
assertEquals(2009, getIsoYear(parse("2008-12-29"))); assertEquals(2009, getIsoWeekYear(parse("2008-12-29")));
assertEquals(2009, getIsoYear(parse("2008-12-30"))); assertEquals(2009, getIsoWeekYear(parse("2008-12-30")));
assertEquals(2009, getIsoYear(parse("2008-12-31"))); assertEquals(2009, getIsoWeekYear(parse("2008-12-31")));
assertEquals(2009, getIsoYear(parse("2009-01-01"))); assertEquals(2009, getIsoWeekYear(parse("2009-01-01")));
assertEquals(2009, getIsoYear(parse("2009-01-02"))); assertEquals(2009, getIsoWeekYear(parse("2009-01-02")));
assertEquals(2009, getIsoYear(parse("2009-01-03"))); assertEquals(2009, getIsoWeekYear(parse("2009-01-03")));
assertEquals(2009, getIsoYear(parse("2009-01-04"))); assertEquals(2009, getIsoWeekYear(parse("2009-01-04")));
} }
/** /**
...@@ -241,14 +240,14 @@ public class TestDateIso8601 extends TestBase { ...@@ -241,14 +240,14 @@ public class TestDateIso8601 extends TestBase {
* previous year. * previous year.
*/ */
private void testIsoYearJanuary1thFriday() throws Exception { private void testIsoYearJanuary1thFriday() throws Exception {
assertEquals(2009, getIsoYear(parse("2009-12-28"))); assertEquals(2009, getIsoWeekYear(parse("2009-12-28")));
assertEquals(2009, getIsoYear(parse("2009-12-29"))); assertEquals(2009, getIsoWeekYear(parse("2009-12-29")));
assertEquals(2009, getIsoYear(parse("2009-12-30"))); assertEquals(2009, getIsoWeekYear(parse("2009-12-30")));
assertEquals(2009, getIsoYear(parse("2009-12-31"))); assertEquals(2009, getIsoWeekYear(parse("2009-12-31")));
assertEquals(2009, getIsoYear(parse("2010-01-01"))); assertEquals(2009, getIsoWeekYear(parse("2010-01-01")));
assertEquals(2009, getIsoYear(parse("2010-01-02"))); assertEquals(2009, getIsoWeekYear(parse("2010-01-02")));
assertEquals(2009, getIsoYear(parse("2010-01-03"))); assertEquals(2009, getIsoWeekYear(parse("2010-01-03")));
assertEquals(2010, getIsoYear(parse("2010-01-04"))); assertEquals(2010, getIsoWeekYear(parse("2010-01-04")));
} }
/** /**
...@@ -256,28 +255,28 @@ public class TestDateIso8601 extends TestBase { ...@@ -256,28 +255,28 @@ public class TestDateIso8601 extends TestBase {
* previous year. * previous year.
*/ */
private void testIsoYearJanuary1thSaturday() throws Exception { private void testIsoYearJanuary1thSaturday() throws Exception {
assertEquals(2010, getIsoYear(parse("2010-12-28"))); assertEquals(2010, getIsoWeekYear(parse("2010-12-28")));
assertEquals(2010, getIsoYear(parse("2010-12-29"))); assertEquals(2010, getIsoWeekYear(parse("2010-12-29")));
assertEquals(2010, getIsoYear(parse("2010-12-30"))); assertEquals(2010, getIsoWeekYear(parse("2010-12-30")));
assertEquals(2010, getIsoYear(parse("2010-12-31"))); assertEquals(2010, getIsoWeekYear(parse("2010-12-31")));
assertEquals(2010, getIsoYear(parse("2011-01-01"))); assertEquals(2010, getIsoWeekYear(parse("2011-01-01")));
assertEquals(2010, getIsoYear(parse("2011-01-02"))); assertEquals(2010, getIsoWeekYear(parse("2011-01-02")));
assertEquals(2011, getIsoYear(parse("2011-01-03"))); assertEquals(2011, getIsoWeekYear(parse("2011-01-03")));
assertEquals(2011, getIsoYear(parse("2011-01-04"))); assertEquals(2011, getIsoWeekYear(parse("2011-01-04")));
} }
/** /**
* January 1st is a Sunday therefore this day belong to the previous year. * January 1st is a Sunday therefore this day belong to the previous year.
*/ */
private void testIsoYearJanuary1thSunday() throws Exception { private void testIsoYearJanuary1thSunday() throws Exception {
assertEquals(2011, getIsoYear(parse("2011-12-28"))); assertEquals(2011, getIsoWeekYear(parse("2011-12-28")));
assertEquals(2011, getIsoYear(parse("2011-12-29"))); assertEquals(2011, getIsoWeekYear(parse("2011-12-29")));
assertEquals(2011, getIsoYear(parse("2011-12-30"))); assertEquals(2011, getIsoWeekYear(parse("2011-12-30")));
assertEquals(2011, getIsoYear(parse("2011-12-31"))); assertEquals(2011, getIsoWeekYear(parse("2011-12-31")));
assertEquals(2011, getIsoYear(parse("2012-01-01"))); assertEquals(2011, getIsoWeekYear(parse("2012-01-01")));
assertEquals(2012, getIsoYear(parse("2012-01-02"))); assertEquals(2012, getIsoWeekYear(parse("2012-01-02")));
assertEquals(2012, getIsoYear(parse("2012-01-03"))); assertEquals(2012, getIsoWeekYear(parse("2012-01-03")));
assertEquals(2012, getIsoYear(parse("2012-01-04"))); assertEquals(2012, getIsoWeekYear(parse("2012-01-04")));
} }
} }
...@@ -5,6 +5,9 @@ ...@@ -5,6 +5,9 @@
*/ */
package org.h2.test.unit; package org.h2.test.unit;
import java.util.Calendar;
import java.util.GregorianCalendar;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.util.DateTimeUtils; import org.h2.util.DateTimeUtils;
...@@ -25,6 +28,8 @@ public class TestDateTimeUtils extends TestBase { ...@@ -25,6 +28,8 @@ public class TestDateTimeUtils extends TestBase {
@Override @Override
public void test() throws Exception { public void test() throws Exception {
testParseTimeNanosDB2Format(); testParseTimeNanosDB2Format();
testDayOfWeek();
testWeekOfYear();
} }
private void testParseTimeNanosDB2Format() { private void testParseTimeNanosDB2Format() {
...@@ -34,4 +39,55 @@ public class TestDateTimeUtils extends TestBase { ...@@ -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));
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 { ...@@ -487,13 +487,16 @@ public class TestPgServer extends TestBase {
Date[] dates = { null, Date.valueOf("2017-02-20"), Date[] dates = { null, Date.valueOf("2017-02-20"),
Date.valueOf("1970-01-01"), Date.valueOf("1969-12-31"), 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[] times = { null, Time.valueOf("14:15:16"),
Time.valueOf("00:00:00"), Time.valueOf("23:59:59"), 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[] 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("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; int count = dates.length;
PreparedStatement ps = conn.prepareStatement( PreparedStatement ps = conn.prepareStatement(
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论