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("(");
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,21 +3007,7 @@ public class Parser {
return new ExpressionColumn(database, null, objectName, name);
}
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:
private Parameter readParameter() {
// there must be no space between ? and the number
boolean indexed = Character.isDigit(sqlCommandChars[parseIndex]);
......@@ -3059,7 +3051,25 @@ public class Parser {
p = new Parameter(parameters.size());
}
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;
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();
}
}
......@@ -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 {
case PgServer.PG_TYPE_BOOL:
writeInt(1);
dataOut.writeByte(b ? 't' : 'f');
}
dataOut.writeByte(v.getBoolean() ? 't' : 'f');
break;
}
default:
String s = rs.getString(column);
if (s == null) {
writeInt(-1);
} else {
byte[] data = s.getBytes(getEncoding());
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 {
case PgServer.PG_TYPE_INT2:
writeInt(2);
writeShort(s);
}
writeShort(v.getShort());
break;
}
case PgServer.PG_TYPE_INT4: {
int i = rs.getInt(column);
if (rs.wasNull()) {
writeInt(-1);
} else {
case PgServer.PG_TYPE_INT4:
writeInt(4);
writeInt(i);
}
writeInt(v.getInt());
break;
}
case PgServer.PG_TYPE_INT8: {
long l = rs.getLong(column);
if (rs.wasNull()) {
writeInt(-1);
} else {
case PgServer.PG_TYPE_INT8:
writeInt(8);
dataOut.writeLong(l);
}
dataOut.writeLong(v.getLong());
break;
}
case PgServer.PG_TYPE_FLOAT4: {
float f = rs.getFloat(column);
if (rs.wasNull()) {
writeInt(-1);
} else {
case PgServer.PG_TYPE_FLOAT4:
writeInt(4);
dataOut.writeFloat(f);
}
dataOut.writeFloat(v.getFloat());
break;
}
case PgServer.PG_TYPE_FLOAT8: {
double d = rs.getDouble(column);
if (rs.wasNull()) {
writeInt(-1);
} else {
case PgServer.PG_TYPE_FLOAT8:
writeInt(8);
dataOut.writeDouble(d);
}
dataOut.writeDouble(v.getDouble());
break;
}
case PgServer.PG_TYPE_BYTEA: {
byte[] data = rs.getBytes(column);
if (data == null) {
writeInt(-1);
} else {
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 {
ValueDate d = (ValueDate) v.convertTo(Value.DATE);
writeInt(4);
long millis = d.getTime();
millis += TimeZone.getDefault().getOffset(millis);
writeInt((int) (toPostgreSeconds(millis) / 86400));
}
writeInt((int) (toPostgreDays(d.getDateValue())));
break;
}
case PgServer.PG_TYPE_TIME: {
Time t = rs.getTime(column);
if (t == null) {
writeInt(-1);
} else {
ValueTime t = (ValueTime) v.convertTo(Value.TIME);
writeInt(8);
long m = t.getTime();
m += TimeZone.getDefault().getOffset(m);
long m = t.getNanos();
if (INTEGER_DATE_TYPES) {
// long format
m *= 1000;
m /= 1_000;
} else {
// double format
m /= 1000;
m = Double.doubleToLongBits(m);
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);
} else {
ValueTimestamp t = (ValueTimestamp) v.convertTo(Value.TIMESTAMP);
writeInt(8);
long m = t.getTime();
m += TimeZone.getDefault().getOffset(m);
m = toPostgreSeconds(m);
int nanos = t.getNanos();
if (m < 0 && nanos != 0) {
m--;
}
long m = toPostgreDays(t.getDateValue()) * 86_400;
long nanos = t.getTimeNanos();
if (INTEGER_DATE_TYPES) {
// long format
m = m * 1000000 + nanos / 1000;
m = m * 1_000_000 + nanos / 1_000;
} else {
// double format
m = Double.doubleToLongBits(m + nanos * 0.000000001);
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) {
......
......@@ -543,8 +543,15 @@ public class LocalDateTimeUtils {
int year = DateTimeUtils.yearFromDateValue(dateValue);
int month = DateTimeUtils.monthFromDateValue(dateValue);
int day = DateTimeUtils.dayFromDateValue(dateValue);
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];
......
......@@ -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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论