提交 3c0bc6c0 authored 作者: Noel Grandin's avatar Noel Grandin 提交者: GitHub

Merge pull request #534 from httpdigest/Issue-89

Add DB2 timestamp format compatibility
...@@ -21,7 +21,9 @@ Change Log ...@@ -21,7 +21,9 @@ Change Log
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul> <ul>
<li>Add padding for CHAR(N) values in PostgreSQL mode <li>Add padding for CHAR(N) values in PostgreSQL mode
</li>
<li>Issue #89: Add DB2 timestamp format compatibility
</li> </li>
<li> <li>
</li> </li>
......
...@@ -2907,7 +2907,7 @@ public class Parser { ...@@ -2907,7 +2907,7 @@ public class Parser {
String timestamp = currentValue.getString(); String timestamp = currentValue.getString();
read(); read();
r = ValueExpression r = ValueExpression
.get(ValueTimestamp.parse(timestamp)); .get(ValueTimestamp.parse(timestamp, session.getDatabase().getMode()));
} else if (equalsToken("X", name)) { } else if (equalsToken("X", name)) {
read(); read();
byte[] buffer = StringUtils byte[] buffer = StringUtils
......
...@@ -439,7 +439,7 @@ public class FunctionAlias extends SchemaObjectBase { ...@@ -439,7 +439,7 @@ public class FunctionAlias extends SchemaObjectBase {
} }
o = objArray; o = objArray;
} else { } else {
v = v.convertTo(type); v = v.convertTo(type, -1, session.getDatabase().getMode());
o = v.getObject(); o = v.getObject();
} }
if (o == null) { if (o == null) {
......
...@@ -165,6 +165,11 @@ public class Mode { ...@@ -165,6 +165,11 @@ public class Mode {
*/ */
public boolean padFixedLengthStrings; public boolean padFixedLengthStrings;
/**
* Whether DB2 TIMESTAMP formats are allowed.
*/
public boolean allowDB2TimestampFormat;
private final String name; private final String name;
static { static {
...@@ -184,6 +189,7 @@ public class Mode { ...@@ -184,6 +189,7 @@ public class Mode {
Pattern.compile("ApplicationName|ClientAccountingInformation|" + Pattern.compile("ApplicationName|ClientAccountingInformation|" +
"ClientUser|ClientCorrelationToken"); "ClientUser|ClientCorrelationToken");
mode.prohibitEmptyInPredicate = true; mode.prohibitEmptyInPredicate = true;
mode.allowDB2TimestampFormat = true;
add(mode); add(mode);
mode = new Mode("Derby"); mode = new Mode("Derby");
......
...@@ -1263,7 +1263,7 @@ public class Function extends Expression implements FunctionCall { ...@@ -1263,7 +1263,7 @@ public class Function extends Expression implements FunctionCall {
} else if (v0.getType() == Value.STRING) { } else if (v0.getType() == Value.STRING) {
ValueString vd = (ValueString) v0; ValueString vd = (ValueString) v0;
Calendar c = Calendar.getInstance(); Calendar c = Calendar.getInstance();
c.setTime(ValueTimestamp.parse(vd.getString()).getDate()); c.setTime(ValueTimestamp.parse(vd.getString(), session.getDatabase().getMode()).getDate());
c.set(Calendar.HOUR_OF_DAY, 0); c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0); c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0); c.set(Calendar.SECOND, 0);
......
...@@ -963,7 +963,7 @@ public abstract class Value { ...@@ -963,7 +963,7 @@ public abstract class Value {
case DATE: case DATE:
return ValueDate.parse(s.trim()); return ValueDate.parse(s.trim());
case TIMESTAMP: case TIMESTAMP:
return ValueTimestamp.parse(s.trim()); return ValueTimestamp.parse(s.trim(), mode);
case TIMESTAMP_TZ: case TIMESTAMP_TZ:
return ValueTimestampTimeZone.parse(s.trim()); return ValueTimestampTimeZone.parse(s.trim());
case BYTES: case BYTES:
......
...@@ -13,6 +13,7 @@ import java.sql.Timestamp; ...@@ -13,6 +13,7 @@ import java.sql.Timestamp;
import java.util.Calendar; import java.util.Calendar;
import java.util.TimeZone; import java.util.TimeZone;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.engine.Mode;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.util.DateTimeUtils; import org.h2.util.DateTimeUtils;
import org.h2.util.MathUtils; import org.h2.util.MathUtils;
...@@ -109,26 +110,53 @@ public class ValueTimestamp extends Value { ...@@ -109,26 +110,53 @@ public class ValueTimestamp extends Value {
/** /**
* Parse a string to a ValueTimestamp. This method supports the format * Parse a string to a ValueTimestamp. This method supports the format
* +/-year-month-day hour:minute:seconds.fractional and an optional timezone * +/-year-month-day hour[:.]minute[:.]seconds.fractional and an optional timezone
* part. * part.
* *
* @param s the string to parse * @param s the string to parse
* @return the date * @return the date
*/ */
public static ValueTimestamp parse(String s) { public static ValueTimestamp parse(String s) {
return parse(s, null);
}
/**
* Parse a string to a ValueTimestamp, using the given {@link Mode}.
* This method supports the format +/-year-month-day[ -]hour[:.]minute[:.]seconds.fractional
* and an optional timezone part.
*
* @param s the string to parse
* @param mode the database {@link Mode}
* @return the date
*/
public static ValueTimestamp parse(String s, Mode mode) {
try { try {
return parseTry(s); return parseTry(s, mode);
} catch (Exception e) { } catch (Exception e) {
throw DbException.get(ErrorCode.INVALID_DATETIME_CONSTANT_2, throw DbException.get(ErrorCode.INVALID_DATETIME_CONSTANT_2,
e, "TIMESTAMP", s); e, "TIMESTAMP", s);
} }
} }
private static ValueTimestamp parseTry(String s) { /**
* See: https://stackoverflow.com/questions/3976616/how-to-find-nth-occurrence-of-character-in-a-string#answer-3976656
*/
private static int findNthIndexOf(String str, char chr, int n) {
int pos = str.indexOf(chr);
while (--n > 0 && pos != -1)
pos = str.indexOf(chr, pos + 1);
return pos;
}
private static ValueTimestamp parseTry(String s, Mode mode) {
int dateEnd = s.indexOf(' '); int dateEnd = s.indexOf(' ');
if (dateEnd < 0) { if (dateEnd < 0) {
// ISO 8601 compatibility // ISO 8601 compatibility
dateEnd = s.indexOf('T'); dateEnd = s.indexOf('T');
if (dateEnd < 0 && mode != null && mode.allowDB2TimestampFormat) {
// DB2 also allows dash between date and time
dateEnd = findNthIndexOf(s, '-', 3);
}
} }
int timeStart; int timeStart;
if (dateEnd < 0) { if (dateEnd < 0) {
...@@ -148,9 +176,9 @@ public class ValueTimestamp extends Value { ...@@ -148,9 +176,9 @@ public class ValueTimestamp extends Value {
tz = TimeZone.getTimeZone("UTC"); tz = TimeZone.getTimeZone("UTC");
timeEnd--; timeEnd--;
} else { } else {
int timeZoneStart = s.indexOf('+', dateEnd); int timeZoneStart = s.indexOf('+', dateEnd + 1);
if (timeZoneStart < 0) { if (timeZoneStart < 0) {
timeZoneStart = s.indexOf('-', dateEnd); timeZoneStart = s.indexOf('-', dateEnd + 1);
} }
if (timeZoneStart >= 0) { if (timeZoneStart >= 0) {
String tzName = "GMT" + s.substring(timeZoneStart); String tzName = "GMT" + s.substring(timeZoneStart);
......
...@@ -497,6 +497,14 @@ public class TestCompatibility extends TestBase { ...@@ -497,6 +497,14 @@ public class TestCompatibility extends TestBase {
"fetch next 2 rows only with rs use and keep update locks"); "fetch next 2 rows only with rs use and keep update locks");
res = stat.executeQuery("select * from test order by id " + res = stat.executeQuery("select * from test order by id " +
"fetch next 2 rows only with rr use and keep exclusive locks"); "fetch next 2 rows only with rr use and keep exclusive locks");
// Test DB2 TIMESTAMP format with dash separating date and time
stat.execute("drop table test if exists");
stat.execute("create table test(date TIMESTAMP)");
stat.executeUpdate("insert into test (date) values ('2014-04-05-09.48.28.020005')");
assertResult("2014-04-05 09:48:28.020005", stat, "select date from test"); // <- result is always H2 format timestamp!
assertResult("2014-04-05 09:48:28.020005", stat, "select date from test where date = '2014-04-05-09.48.28.020005'");
assertResult("2014-04-05 09:48:28.020005", stat, "select date from test where date = '2014-04-05 09:48:28.020005'");
} }
private void testDerby() throws SQLException { private void testDerby() throws SQLException {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论