提交 8273fd69 authored 作者: Thomas Mueller Graf's avatar Thomas Mueller Graf

An ArrayIndexOutOfBoundsException was thrown in some cases when opening an old version 1.3 database

上级 03eba50f
...@@ -20,7 +20,14 @@ Change Log ...@@ -20,7 +20,14 @@ Change Log
<h1>Change Log</h1> <h1>Change Log</h1>
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul><li>-</li> <ul>
<li>An ArrayIndexOutOfBoundsException was thrown in some cases
when opening an old version 1.3 database, or an 1.4 database with
both "mv_store=false" and the system property "h2.storeLocalTime" set to false.
It mainly showed up with an index on a time, date, or timestamp column.
The system property "h2.storeLocalTime" is no longer supported
(MVStore databases always store local time, and PageStore now databases never do).
</li>
</ul> </ul>
<h2>Version 1.4.188 Beta (2015-08-01)</h2> <h2>Version 1.4.188 Beta (2015-08-01)</h2>
......
...@@ -423,16 +423,6 @@ public class SysProperties { ...@@ -423,16 +423,6 @@ public class SysProperties {
public static final long SPLIT_FILE_SIZE_SHIFT = public static final long SPLIT_FILE_SIZE_SHIFT =
Utils.getProperty("h2.splitFileSizeShift", 30); Utils.getProperty("h2.splitFileSizeShift", 30);
/**
* System property <code>h2.storeLocalTime</code>
* (default: false for version 1.3, true for version 1.4).<br />
* Store the local time. If disabled, the daylight saving offset is not
* taken into account.
*/
public static final boolean STORE_LOCAL_TIME =
Utils.getProperty("h2.storeLocalTime",
Constants.VERSION_MINOR >= 4 ? true : false);
/** /**
* System property <code>h2.syncMethod</code> (default: sync).<br /> * System property <code>h2.syncMethod</code> (default: sync).<br />
* What method to call when closing the database, on checkpoint, and on * What method to call when closing the database, on checkpoint, and on
......
...@@ -90,6 +90,15 @@ public class Data { ...@@ -90,6 +90,15 @@ public class Data {
private static final long MILLIS_PER_MINUTE = 1000 * 60; private static final long MILLIS_PER_MINUTE = 1000 * 60;
/**
* Can not store the local time, because doing so with old database files
* that didn't do it could result in an ArrayIndexOutOfBoundsException. The
* reason is that adding a row to a page only allocated space for the new
* row, but didn't take into account that existing rows now can use more
* space, due to the changed format.
*/
private static final boolean STORE_LOCAL_TIME = false;
/** /**
* The data itself. * The data itself.
*/ */
...@@ -485,7 +494,7 @@ public class Data { ...@@ -485,7 +494,7 @@ public class Data {
break; break;
} }
case Value.TIME: case Value.TIME:
if (SysProperties.STORE_LOCAL_TIME) { if (STORE_LOCAL_TIME) {
writeByte((byte) LOCAL_TIME); writeByte((byte) LOCAL_TIME);
ValueTime t = (ValueTime) v; ValueTime t = (ValueTime) v;
long nanos = t.getNanos(); long nanos = t.getNanos();
...@@ -499,7 +508,7 @@ public class Data { ...@@ -499,7 +508,7 @@ public class Data {
} }
break; break;
case Value.DATE: { case Value.DATE: {
if (SysProperties.STORE_LOCAL_TIME) { if (STORE_LOCAL_TIME) {
writeByte((byte) LOCAL_DATE); writeByte((byte) LOCAL_DATE);
long x = ((ValueDate) v).getDateValue(); long x = ((ValueDate) v).getDateValue();
writeVarLong(x); writeVarLong(x);
...@@ -511,7 +520,7 @@ public class Data { ...@@ -511,7 +520,7 @@ public class Data {
break; break;
} }
case Value.TIMESTAMP: { case Value.TIMESTAMP: {
if (SysProperties.STORE_LOCAL_TIME) { if (STORE_LOCAL_TIME) {
writeByte((byte) LOCAL_TIMESTAMP); writeByte((byte) LOCAL_TIMESTAMP);
ValueTimestamp ts = (ValueTimestamp) v; ValueTimestamp ts = (ValueTimestamp) v;
long dateValue = ts.getDateValue(); long dateValue = ts.getDateValue();
...@@ -982,7 +991,7 @@ public class Data { ...@@ -982,7 +991,7 @@ public class Data {
return 1 + getVarIntLen(scale) + getVarIntLen(bytes.length) + bytes.length; return 1 + getVarIntLen(scale) + getVarIntLen(bytes.length) + bytes.length;
} }
case Value.TIME: case Value.TIME:
if (SysProperties.STORE_LOCAL_TIME) { if (STORE_LOCAL_TIME) {
long nanos = ((ValueTime) v).getNanos(); long nanos = ((ValueTime) v).getNanos();
long millis = nanos / 1000000; long millis = nanos / 1000000;
nanos -= millis * 1000000; nanos -= millis * 1000000;
...@@ -990,7 +999,7 @@ public class Data { ...@@ -990,7 +999,7 @@ public class Data {
} }
return 1 + getVarLongLen(DateTimeUtils.getTimeLocalWithoutDst(v.getTime())); return 1 + getVarLongLen(DateTimeUtils.getTimeLocalWithoutDst(v.getTime()));
case Value.DATE: { case Value.DATE: {
if (SysProperties.STORE_LOCAL_TIME) { if (STORE_LOCAL_TIME) {
long dateValue = ((ValueDate) v).getDateValue(); long dateValue = ((ValueDate) v).getDateValue();
return 1 + getVarLongLen(dateValue); return 1 + getVarLongLen(dateValue);
} }
...@@ -998,7 +1007,7 @@ public class Data { ...@@ -998,7 +1007,7 @@ public class Data {
return 1 + getVarLongLen(x / MILLIS_PER_MINUTE); return 1 + getVarLongLen(x / MILLIS_PER_MINUTE);
} }
case Value.TIMESTAMP: { case Value.TIMESTAMP: {
if (SysProperties.STORE_LOCAL_TIME) { if (STORE_LOCAL_TIME) {
ValueTimestamp ts = (ValueTimestamp) v; ValueTimestamp ts = (ValueTimestamp) v;
long dateValue = ts.getDateValue(); long dateValue = ts.getDateValue();
long nanos = ts.getTimeNanos(); long nanos = ts.getTimeNanos();
......
...@@ -19,7 +19,6 @@ import java.util.GregorianCalendar; ...@@ -19,7 +19,6 @@ import java.util.GregorianCalendar;
import java.util.SimpleTimeZone; import java.util.SimpleTimeZone;
import java.util.TimeZone; import java.util.TimeZone;
import org.h2.engine.SysProperties;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.test.unit.TestDate; import org.h2.test.unit.TestDate;
import org.h2.util.DateTimeUtils; import org.h2.util.DateTimeUtils;
...@@ -156,7 +155,7 @@ public class TestDateStorage extends TestBase { ...@@ -156,7 +155,7 @@ public class TestDateStorage extends TestBase {
if (config.memory) { if (config.memory) {
return; return;
} }
if (!SysProperties.STORE_LOCAL_TIME) { if (config.mvStore) {
return; return;
} }
String db = getTestName() + ";LOG=0;FILE_LOCK=NO"; String db = getTestName() + ";LOG=0;FILE_LOCK=NO";
......
...@@ -15,8 +15,6 @@ import java.util.GregorianCalendar; ...@@ -15,8 +15,6 @@ import java.util.GregorianCalendar;
import java.util.TimeZone; import java.util.TimeZone;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.engine.SysProperties;
import org.h2.store.Data;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.test.utils.AssertThrows; import org.h2.test.utils.AssertThrows;
import org.h2.util.DateTimeUtils; import org.h2.util.DateTimeUtils;
...@@ -25,8 +23,6 @@ import org.h2.value.Value; ...@@ -25,8 +23,6 @@ import org.h2.value.Value;
import org.h2.value.ValueDate; import org.h2.value.ValueDate;
import org.h2.value.ValueDouble; import org.h2.value.ValueDouble;
import org.h2.value.ValueInt; import org.h2.value.ValueInt;
import org.h2.value.ValueNull;
import org.h2.value.ValueString;
import org.h2.value.ValueTime; import org.h2.value.ValueTime;
import org.h2.value.ValueTimestamp; import org.h2.value.ValueTimestamp;
...@@ -57,7 +53,6 @@ public class TestDate extends TestBase { ...@@ -57,7 +53,6 @@ public class TestDate extends TestBase {
testValidDate(); testValidDate();
testAbsoluteDay(); testAbsoluteDay();
testCalculateLocalMillis(); testCalculateLocalMillis();
testTimeOperationsAcrossTimeZones();
testDateTimeUtils(); testDateTimeUtils();
} }
...@@ -437,62 +432,6 @@ public class TestDate extends TestBase { ...@@ -437,62 +432,6 @@ public class TestDate extends TestBase {
} }
} }
private void testTimeOperationsAcrossTimeZones() {
if (!SysProperties.STORE_LOCAL_TIME) {
return;
}
TimeZone defaultTimeZone = TimeZone.getDefault();
ArrayList<TimeZone> distinct = TestDate.getDistinctTimeZones();
Data d = Data.create(null, 10240);
try {
for (TimeZone tz : distinct) {
TimeZone.setDefault(tz);
DateTimeUtils.resetCalendar();
d.reset();
for (int m = 1; m <= 12; m++) {
for (int h = 0; h <= 23; h++) {
if (h == 0 || h == 2 || h == 3) {
// those hours may not exist for all days in all
// timezones because of daylight saving
continue;
}
String s = "2000-" + (m < 10 ? "0" + m : m) +
"-01 " + (h < 10 ? "0" + h : h) + ":00:00.0";
d.writeValue(ValueString.get(s));
d.writeValue(ValueTimestamp.get(Timestamp.valueOf(s)));
}
}
d.writeValue(ValueNull.INSTANCE);
d.reset();
for (TimeZone target : distinct) {
if ("Pacific/Kiritimati".equals(target.getID())) {
// there is a problem with this time zone, but it seems
// unrelated to this database (possibly wrong timezone
// information?)
continue;
}
TimeZone.setDefault(target);
DateTimeUtils.resetCalendar();
while (true) {
Value v = d.readValue();
if (v == ValueNull.INSTANCE) {
break;
}
String a = v.getString();
String b = d.readValue().getString();
if (!a.equals(b)) {
assertEquals("source: " + tz.getID() + " target: " +
target.getID(), a, b);
}
}
}
}
} finally {
TimeZone.setDefault(defaultTimeZone);
DateTimeUtils.resetCalendar();
}
}
/** /**
* Get the list of timezones with distinct rules. * Get the list of timezones with distinct rules.
* *
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论