提交 9e5d9644 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Merge branch 'master' into generated_keys

......@@ -2300,9 +2300,9 @@ Each table has a pseudo-column named ""_ROWID_"" that contains the unique row id
"
"Other Grammar","Time","
TIME 'hh:mm:ss'
TIME 'hh:mm:ss[.nnnnnnnnn]'
","
A time literal. A value is between plus and minus 2 million hours
A time literal. A value is between 0:00:00 and 23:59:59.999999999
and has nanosecond resolution.
","
TIME '23:59:59'
......@@ -2318,6 +2318,19 @@ minimum and maximum years are 0001 and 9999.
TIMESTAMP '2005-12-31 23:59:59'
"
"Other Grammar","Timestamp with time zone","
TIMESTAMP 'yyyy-MM-dd hh:mm:ss[.nnnnnnnnn]
[Z | { - | + } timeZoneOffsetString | timeZoneNameString ]'
","
A timestamp with time zone literal.
If name of time zone is specified it will be converted to time zone offset.
","
TIMESTAMP WITH TIME ZONE '2005-12-31 23:59:59Z'
TIMESTAMP WITH TIME ZONE '2005-12-31 23:59:59-10:00'
TIMESTAMP WITH TIME ZONE '2005-12-31 23:59:59.123+05'
TIMESTAMP WITH TIME ZONE '2005-12-31 23:59:59.123456789 Europe/London'
"
"Other Grammar","Value","
string | dollarQuotedString | numeric | date | time | timestamp | boolean | bytes | array | null
","
......@@ -3671,7 +3684,7 @@ CURRENT_TIMESTAMP()
Adds units to a date-time value. The string indicates the unit.
Use negative values to subtract units.
addIntLong may be a long value when manipulating milliseconds,
otherwise it's range is restricted to int.
microseconds, or nanoseconds otherwise its range is restricted to int.
The same units as in the EXTRACT function are supported.
This method returns a value with the same type as specified value if unit is compatible with this value.
If specified unit is a HOUR, MINUTE, SECOND, MILLISECOND, etc and value is a DATE value DATEADD returns combined TIMESTAMP.
......@@ -3727,11 +3740,13 @@ DAY_OF_YEAR(CREATED)
"Functions (Time and Date)","EXTRACT","
EXTRACT ( { YEAR | YY | MONTH | MM | QUARTER | WEEK | ISO_WEEK
| DAY | DD | DAY_OF_YEAR | DOY
| HOUR | HH | MINUTE | MI | SECOND | SS | MILLISECOND | MS }
| HOUR | HH | MINUTE | MI | SECOND | SS | EPOCH
| MILLISECOND | MS | MICROSECOND | MCS | NANOSECOND | NS }
FROM timestamp )
","
Returns a specific value from a timestamps.
This method returns an int.
This method returns a numeric value with EPOCH unit and
an int for all other time units.
","
EXTRACT(SECOND FROM CURRENT_TIMESTAMP)
"
......@@ -3820,6 +3835,17 @@ This method uses the current system locale.
WEEK(CREATED)
"
"Functions (Time and Date)","ISO_WEEK","
ISO_WEEK(timestamp)
","
Returns the week (1-53) from a timestamp.
This method uses the ISO definition when
first week of year should have at least four days
and week is started with Monday.
","
ISO_WEEK(CREATED)
"
"Functions (Time and Date)","YEAR","
YEAR(timestamp)
","
......@@ -3828,6 +3854,14 @@ Returns the year from a timestamp.
YEAR(CREATED)
"
"Functions (Time and Date)","ISO_YEAR","
ISO_YEAR(timestamp)
","
Returns the ISO week year from a timestamp.
","
ISO_YEAR(CREATED)
"
"Functions (System)","ARRAY_GET","
ARRAY_GET(arrayExpression, indexExpression)
","
......
......@@ -10,7 +10,6 @@ import org.h2.engine.DbObject;
import org.h2.engine.Session;
import org.h2.expression.ExpressionVisitor;
import org.h2.index.Index;
import org.h2.message.DbException;
import org.h2.message.Trace;
import org.h2.result.Row;
import org.h2.schema.Schema;
......
......@@ -224,7 +224,8 @@ class AggregateDataMedian extends AggregateData {
case Value.DOUBLE:
return ValueDouble.get((v0.getFloat() + v1.getDouble()) / 2);
case Value.TIME: {
return ValueTime.fromMillis((v0.getTime().getTime() + v1.getTime().getTime()) / 2);
ValueTime t0 = (ValueTime) v0.convertTo(Value.TIME), t1 = (ValueTime) v1.convertTo(Value.TIME);
return ValueTime.fromNanos((t0.getNanos() + t1.getNanos()) / 2);
}
case Value.DATE: {
ValueDate d0 = (ValueDate) v0.convertTo(Value.DATE), d1 = (ValueDate) v1.convertTo(Value.DATE);
......
......@@ -148,6 +148,9 @@ public class IndexCursor implements Cursor {
}
}
}
if (inColumn != null) {
start = table.getTemplateRow();
}
}
/**
......@@ -326,7 +329,6 @@ public class IndexCursor implements Cursor {
while (inResult.next()) {
Value v = inResult.currentRow()[0];
if (v != ValueNull.INSTANCE) {
v = inColumn.convert(v);
if (inResultTested == null) {
inResultTested = new HashSet<>();
}
......@@ -342,9 +344,6 @@ public class IndexCursor implements Cursor {
private void find(Value v) {
v = inColumn.convert(v);
int id = inColumn.getColumnId();
if (start == null) {
start = table.getTemplateRow();
}
start.setValue(id, v);
cursor = index.find(tableFilter, start, start);
}
......
......@@ -2450,27 +2450,27 @@ public final class MVStore {
* needed.
*/
void writeInBackground() {
if (closed) {
return;
}
try {
if (closed) {
return;
}
// could also commit when there are many unsaved pages,
// but according to a test it doesn't really help
// could also commit when there are many unsaved pages,
// but according to a test it doesn't really help
long time = getTimeSinceCreation();
if (time <= lastCommitTime + autoCommitDelay) {
return;
}
if (hasUnsavedChanges()) {
try {
commitAndSave();
} catch (Throwable e) {
handleException(e);
long time = getTimeSinceCreation();
if (time <= lastCommitTime + autoCommitDelay) {
return;
}
}
if (autoCompactFillRate > 0) {
try {
if (hasUnsavedChanges()) {
try {
commitAndSave();
} catch (Throwable e) {
handleException(e);
return;
}
}
if (autoCompactFillRate > 0) {
// whether there were file read or write operations since
// the last time
boolean fileOps;
......@@ -2486,9 +2486,9 @@ public final class MVStore {
// in the bookkeeping?
compact(fillRate, autoCommitMemory);
autoCompactLastFileOpCount = fileStore.getWriteCount() + fileStore.getReadCount();
} catch (Throwable e) {
handleException(e);
}
} catch (Throwable e) {
handleException(e);
}
}
......@@ -2497,7 +2497,9 @@ public final class MVStore {
try {
backgroundExceptionHandler.uncaughtException(null, ex);
} catch(Throwable ignore) {
ex.addSuppressed(ignore);
if (ex != ignore) { // OOME may be the same
ex.addSuppressed(ignore);
}
}
}
}
......
......@@ -171,7 +171,7 @@ public class DataReader extends Reader {
int i = 0;
try {
for (; i < len; i++) {
buff[i] = readChar();
buff[off + i] = readChar();
}
return len;
} catch (EOFException e) {
......
......@@ -1615,7 +1615,7 @@ public class MetaTable extends Table {
if (constraintType == Constraint.Type.CHECK) {
checkExpression = ((ConstraintCheck) constraint).getExpression().getSQL();
} else if (constraintType == Constraint.Type.UNIQUE ||
constraintType == Constraint.Type.PRIMARY_KEY) {
constraintType == Constraint.Type.PRIMARY_KEY) {
indexColumns = ((ConstraintUnique) constraint).getColumns();
} else if (constraintType == Constraint.Type.REFERENTIAL) {
indexColumns = ((ConstraintReferential) constraint).getColumns();
......
......@@ -224,7 +224,8 @@ public class ChangeFileEncryption extends Tool {
try (FileChannel fileIn = getFileChannel(fileName, "r", decryptKey)){
try(InputStream inStream = new FileChannelInputStream(fileIn, true)) {
FileUtils.delete(temp);
try (OutputStream outStream = new FileChannelOutputStream(getFileChannel(temp, "rw", encryptKey), true)) {
try (OutputStream outStream = new FileChannelOutputStream(getFileChannel(temp, "rw", encryptKey),
true)) {
byte[] buffer = new byte[4 * 1024];
long remaining = fileIn.size();
long total = remaining;
......
......@@ -36,7 +36,7 @@ public class DateTimeUtils {
* The number of milliseconds per day.
*/
public static final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000L;
/**
* The number of seconds per day.
*/
......@@ -85,6 +85,11 @@ public class DateTimeUtils {
private static final ThreadLocal<GregorianCalendar> CACHED_CALENDAR_NON_DEFAULT_TIMEZONE =
new ThreadLocal<>();
/**
* Cached local time zone.
*/
private static volatile TimeZone timeZone;
/**
* Observed JVM behaviour is that if the timezone of the host computer is
* changed while the JVM is running, the zone offset does not change but
......@@ -100,12 +105,26 @@ public class DateTimeUtils {
// utility class
}
/**
* Returns local time zone.
*
* @return local time zone
*/
private static TimeZone getTimeZone() {
TimeZone tz = timeZone;
if (tz == null) {
timeZone = tz = TimeZone.getDefault();
}
return tz;
}
/**
* Reset the cached calendar for default timezone, for example after
* changing the default timezone.
*/
public static void resetCalendar() {
CACHED_CALENDAR.remove();
timeZone = null;
zoneOffsetMillis = DateTimeUtils.createGregorianCalendar().get(Calendar.ZONE_OFFSET);
}
......@@ -628,20 +647,6 @@ public class DateTimeUtils {
return ValueTimestamp.fromDateValueAndNanos(dateValue, timeNanos);
}
/**
* Get the year (positive or negative) from a calendar.
*
* @param calendar the calendar
* @return the year
*/
private static int getYear(Calendar calendar) {
int year = calendar.get(Calendar.YEAR);
if (calendar.get(Calendar.ERA) == GregorianCalendar.BC) {
year = 1 - year;
}
return year;
}
/**
* Get the number of milliseconds since 1970-01-01 in the local timezone,
* but without daylight saving time into account.
......@@ -921,19 +926,6 @@ public class DateTimeUtils {
return new Date(millis);
}
/**
* Convert an encoded date value to millis, using the supplied timezone.
*
* @param tz the timezone
* @param dateValue the date value
* @return the date
*/
public static long convertDateValueToMillis(TimeZone tz, long dateValue) {
return getMillis(tz, yearFromDateValue(dateValue),
monthFromDateValue(dateValue), dayFromDateValue(dateValue), 0,
0, 0, 0);
}
/**
* Convert an encoded date-time value to millis, using the supplied timezone.
*
......@@ -1070,9 +1062,13 @@ public class DateTimeUtils {
* @return the date value
*/
public static long dateValueFromDate(long ms) {
Calendar cal = getCalendar();
cal.setTimeInMillis(ms);
return dateValueFromCalendar(cal);
ms += getTimeZone().getOffset(ms);
long absoluteDay = ms / MILLIS_PER_DAY;
// Round toward negative infinity
if (ms < 0 && (absoluteDay * MILLIS_PER_DAY != ms)) {
absoluteDay--;
}
return dateValueFromAbsoluteDay(absoluteDay);
}
/**
......@@ -1082,10 +1078,12 @@ public class DateTimeUtils {
* @return the date value
*/
private static long dateValueFromCalendar(Calendar cal) {
int year, month, day;
year = getYear(cal);
month = cal.get(Calendar.MONTH) + 1;
day = cal.get(Calendar.DAY_OF_MONTH);
int year = cal.get(Calendar.YEAR);
if (cal.get(Calendar.ERA) == GregorianCalendar.BC) {
year = 1 - year;
}
int month = cal.get(Calendar.MONTH) + 1;
int day = cal.get(Calendar.DAY_OF_MONTH);
return ((long) year << SHIFT_YEAR) | (month << SHIFT_MONTH) | day;
}
......@@ -1097,9 +1095,13 @@ public class DateTimeUtils {
* @return the nanoseconds
*/
public static long nanosFromDate(long ms) {
Calendar cal = getCalendar();
cal.setTimeInMillis(ms);
return nanosFromCalendar(cal);
ms += getTimeZone().getOffset(ms);
long absoluteDay = ms / MILLIS_PER_DAY;
// Round toward negative infinity
if (ms < 0 && (absoluteDay * MILLIS_PER_DAY != ms)) {
absoluteDay--;
}
return (ms - absoluteDay * MILLIS_PER_DAY) * 1_000_000;
}
/**
......@@ -1154,7 +1156,7 @@ public class DateTimeUtils {
m += 12;
}
long a = ((y * 2922L) >> 3) + DAYS_OFFSET[m - 3] + d - 719484;
if (y <= 1582 && ((y < 1582) || (m * 100 + d < 1005))) {
if (y <= 1582 && ((y < 1582) || (m * 100 + d < 1015))) {
// Julian calendar (cutover at 1582-10-04 / 1582-10-15)
a += 13;
} else if (y < 1901 || y > 2099) {
......
......@@ -7,9 +7,13 @@ package org.h2.util;
import static java.lang.String.format;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.TimeZone;
import org.h2.value.ValueTimestamp;
import org.h2.value.ValueTimestampTimeZone;
/**
* Emulates Oracle's TO_DATE function.<br>
......@@ -21,8 +25,29 @@ public class ToDateParser {
private final ConfigParam functionName;
private String inputStr;
private String formatStr;
private final Calendar resultCalendar = DateTimeUtils.createGregorianCalendar();
private Integer nanos;
private boolean doyValid = false, absoluteDayValid = false,
hour12Valid = false,
timeZoneHMValid = false;
private boolean bc;
private long absoluteDay;
private int year, month, day = 1;
private int dayOfYear;
private int hour, minute, second, nanos;
private int hour12;
boolean isAM = true;
private TimeZone timeZone;
private int timeZoneHour, timeZoneMinute;
private int currentYear, currentMonth;
/**
* @param input the input date with the date-time info
......@@ -31,20 +56,6 @@ public class ToDateParser {
* code)
*/
private ToDateParser(ConfigParam functionName, String input, String format) {
// reset calendar - default oracle behaviour
resultCalendar.set(Calendar.YEAR, 1970);
resultCalendar.set(Calendar.MONTH, DateTimeUtils.createGregorianCalendar().get(Calendar.MONTH));
resultCalendar.clear(Calendar.DAY_OF_YEAR);
resultCalendar.clear(Calendar.DAY_OF_WEEK);
resultCalendar.clear(Calendar.DAY_OF_WEEK_IN_MONTH);
resultCalendar.set(Calendar.DAY_OF_MONTH, 1);
resultCalendar.set(Calendar.HOUR, 0);
resultCalendar.set(Calendar.HOUR_OF_DAY, 0);
resultCalendar.set(Calendar.MINUTE, 0);
resultCalendar.set(Calendar.SECOND, 0);
resultCalendar.set(Calendar.MILLISECOND, 0);
resultCalendar.set(Calendar.AM_PM, Calendar.AM);
this.functionName = functionName;
inputStr = input.trim();
// Keep a copy
......@@ -59,30 +70,65 @@ public class ToDateParser {
unmodifiedFormatStr = formatStr;
}
private static ToDateParser getDateParser(String input, String format) {
ToDateParser result = new ToDateParser(ConfigParam.TO_DATE, input, format);
private static ToDateParser getTimestampParser(ConfigParam param, String input, String format) {
ToDateParser result = new ToDateParser(param, input, format);
parse(result);
return result;
}
private static ToDateParser getTimestampParser(String input, String format) {
ToDateParser result = new ToDateParser(ConfigParam.TO_TIMESTAMP, input, format);
parse(result);
return result;
}
private Timestamp getResultingTimestamp() {
Calendar cal = (Calendar) getResultCalendar().clone();
int nanosToSet = nanos == null ?
cal.get(Calendar.MILLISECOND) * 1000000 : nanos.intValue();
cal.set(Calendar.MILLISECOND, 0);
Timestamp ts = new Timestamp(cal.getTimeInMillis());
ts.setNanos(nanosToSet);
return ts;
private ValueTimestamp getResultingValue() {
long dateValue;
if (absoluteDayValid) {
dateValue = DateTimeUtils.dateValueFromAbsoluteDay(absoluteDay);
} else {
int year = this.year;
if (year == 0) {
year = getCurrentYear();
}
if (bc) {
year = 1 - year;
}
if (doyValid) {
dateValue = DateTimeUtils.dateValueFromAbsoluteDay(
DateTimeUtils.absoluteDayFromDateValue(DateTimeUtils.dateValue(year, 1, 1))
+ dayOfYear - 1);
} else {
int month = this.month;
if (month == 0) {
// Oracle uses current month as default
month = getCurrentMonth();
}
dateValue = DateTimeUtils.dateValue(year, month, day);
}
}
int hour;
if (hour12Valid) {
hour = hour12 % 12;
if (!isAM) {
hour += 12;
}
} else {
hour = this.hour;
}
long timeNanos = ((((hour * 60) + minute) * 60) + second) * 1_000_000_000L + nanos;
return ValueTimestamp.fromDateValueAndNanos(dateValue, timeNanos);
}
Calendar getResultCalendar() {
return resultCalendar;
private ValueTimestampTimeZone getResultingValueWithTimeZone() {
ValueTimestamp ts = getResultingValue();
long dateValue = ts.getDateValue();
short offset;
if (timeZoneHMValid) {
offset = (short) (timeZoneHour * 60 + ((timeZoneHour >= 0) ? timeZoneMinute : -timeZoneMinute));
} else {
TimeZone timeZone = this.timeZone;
if (timeZone == null) {
timeZone = TimeZone.getDefault();
}
long millis = DateTimeUtils.convertDateTimeValueToMillis(timeZone, dateValue, nanos / 1000000);
offset = (short) (timeZone.getOffset(millis) / 1000 / 60);
}
return ValueTimestampTimeZone.fromDateValueAndNanos(dateValue, ts.getTimeNanos(), offset);
}
String getInputStr() {
......@@ -97,10 +143,111 @@ public class ToDateParser {
return functionName.name();
}
private void queryCurrentYearAndMonth() {
GregorianCalendar gc = DateTimeUtils.getCalendar();
gc.setTimeInMillis(System.currentTimeMillis());
currentYear = gc.get(Calendar.YEAR);
currentMonth = gc.get(Calendar.MONTH) + 1;
}
int getCurrentYear() {
if (currentYear == 0) {
queryCurrentYearAndMonth();
}
return currentYear;
}
int getCurrentMonth() {
if (currentMonth == 0) {
queryCurrentYearAndMonth();
}
return currentMonth;
}
void setAbsoluteDay(int absoluteDay) {
doyValid = false;
absoluteDayValid = true;
this.absoluteDay = absoluteDay;
}
void setBC(boolean bc) {
doyValid = false;
absoluteDayValid = false;
this.bc = bc;
}
void setYear(int year) {
doyValid = false;
absoluteDayValid = false;
this.year = year;
}
void setMonth(int month) {
doyValid = false;
absoluteDayValid = false;
this.month = month;
if (year == 0) {
year = 1970;
}
}
void setDay(int day) {
doyValid = false;
absoluteDayValid = false;
this.day = day;
if (year == 0) {
year = 1970;
}
}
void setDayOfYear(int dayOfYear) {
doyValid = true;
absoluteDayValid = false;
this.dayOfYear = dayOfYear;
}
void setHour(int hour) {
hour12Valid = false;
this.hour = hour;
}
void setMinute(int minute) {
this.minute = minute;
}
void setSecord(int second) {
this.second = second;
}
void setNanos(int nanos) {
this.nanos = nanos;
}
void setAmPm(boolean isAM) {
hour12Valid = true;
this.isAM = isAM;
}
void setHour12(int hour12) {
hour12Valid = true;
this.hour12 = hour12;
}
void setTimeZone(TimeZone timeZone) {
timeZoneHMValid = false;
this.timeZone = timeZone;
}
void setTimeZoneHour(int timeZoneHour) {
timeZoneHMValid = true;
this.timeZoneHour = timeZoneHour;
}
void setTimeZoneMinute(int timeZoneMinute) {
timeZoneHMValid = true;
this.timeZoneMinute = timeZoneMinute;
}
private boolean hasToParseData() {
return formatStr.length() > 0;
}
......@@ -180,9 +327,21 @@ public class ToDateParser {
* @param format the format
* @return the timestamp
*/
public static Timestamp toTimestamp(String input, String format) {
ToDateParser parser = getTimestampParser(input, format);
return parser.getResultingTimestamp();
public static ValueTimestamp toTimestamp(String input, String format) {
ToDateParser parser = getTimestampParser(ConfigParam.TO_TIMESTAMP, input, format);
return parser.getResultingValue();
}
/**
* Parse a string as a timestamp with the given format.
*
* @param input the input
* @param format the format
* @return the timestamp
*/
public static ValueTimestampTimeZone toTimestampTz(String input, String format) {
ToDateParser parser = getTimestampParser(ConfigParam.TO_TIMESTAMP_TZ, input, format);
return parser.getResultingValueWithTimeZone();
}
/**
......@@ -192,9 +351,9 @@ public class ToDateParser {
* @param format the format
* @return the date as a timestamp
*/
public static Timestamp toDate(String input, String format) {
ToDateParser parser = getDateParser(input, format);
return parser.getResultingTimestamp();
public static ValueTimestamp toDate(String input, String format) {
ToDateParser parser = getTimestampParser(ConfigParam.TO_DATE, input, format);
return parser.getResultingValue();
}
/**
......@@ -202,7 +361,8 @@ public class ToDateParser {
*/
private enum ConfigParam {
TO_DATE("DD MON YYYY"),
TO_TIMESTAMP("DD MON YYYY HH:MI:SS");
TO_TIMESTAMP("DD MON YYYY HH:MI:SS"),
TO_TIMESTAMP_TZ("DD MON YYYY HH:MI:SS TZR");
private final String defaultFormatStr;
ConfigParam(String defaultFormatStr) {
......
......@@ -14,7 +14,6 @@ import java.io.Reader;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
......@@ -463,10 +462,6 @@ public class Transfer {
throw DbException.get(
ErrorCode.CONNECTION_BROKEN_1, "length=" + length);
}
if (length > Integer.MAX_VALUE) {
throw DbException.get(
ErrorCode.CONNECTION_BROKEN_1, "length="+ length);
}
writeLong(length);
Reader reader = v.getReader();
Data.copyString(reader, out);
......@@ -652,21 +647,10 @@ public class Transfer {
return ValueLobDb.create(
Value.CLOB, session.getDataHandler(), tableId, id, hmac, precision);
}
if (length < 0 || length > Integer.MAX_VALUE) {
if (length < 0) {
throw DbException.get(
ErrorCode.CONNECTION_BROKEN_1, "length="+ length);
}
DataReader reader = new DataReader(in);
int len = (int) length;
char[] buff = new char[len];
IOUtils.readFully(reader, buff, len);
int magic = readInt();
if (magic != LOB_MAGIC) {
throw DbException.get(
ErrorCode.CONNECTION_BROKEN_1, "magic=" + magic);
}
byte[] small = new String(buff).getBytes(StandardCharsets.UTF_8);
return ValueLobDb.createSmallLob(Value.CLOB, small, length);
}
Value v = session.getDataHandler().getLobStorage().
createClob(new DataReader(in), length);
......
......@@ -26,6 +26,7 @@ import org.h2.store.FileStoreInputStream;
import org.h2.store.FileStoreOutputStream;
import org.h2.store.LobStorageFrontend;
import org.h2.store.LobStorageInterface;
import org.h2.store.RangeReader;
import org.h2.store.fs.FileUtils;
import org.h2.util.IOUtils;
import org.h2.util.MathUtils;
......@@ -544,6 +545,15 @@ public class ValueLobDb extends Value implements Value.ValueClob,
*/
public static ValueLobDb createTempClob(Reader in, long length,
DataHandler handler) {
if (length >= 0) {
// Otherwise BufferedReader may try to read more data than needed and that
// blocks the network level
try {
in = new RangeReader(in, 0, length);
} catch (IOException e) {
throw DbException.convert(e);
}
}
BufferedReader reader;
if (in instanceof BufferedReader) {
reader = (BufferedReader) in;
......
......@@ -9,8 +9,6 @@ import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.SimpleTimeZone;
import java.util.TimeZone;
import org.h2.api.ErrorCode;
import org.h2.api.TimestampWithTimeZone;
import org.h2.message.DbException;
......@@ -148,19 +146,6 @@ public class ValueTimestampTimeZone extends Value {
return timeZoneOffsetMins;
}
/**
* Returns compatible offset-based time zone with no DST schedule.
*
* @return compatible offset-based time zone
*/
public TimeZone getTimeZone() {
int offset = timeZoneOffsetMins;
if (offset == 0) {
return DateTimeUtils.UTC;
}
return new SimpleTimeZone(offset * 60000, Integer.toString(offset));
}
@Override
public Timestamp getTimestamp() {
throw new UnsupportedOperationException("unimplemented");
......@@ -219,34 +204,31 @@ public class ValueTimestampTimeZone extends Value {
@Override
protected int compareSecure(Value o, CompareMode mode) {
ValueTimestampTimeZone t = (ValueTimestampTimeZone) o;
// We are pretending that the dateValue is in UTC because that gives us
// a stable sort even if the DST database changes.
// convert to minutes and add timezone offset
long a = DateTimeUtils.convertDateValueToMillis(
DateTimeUtils.UTC, dateValue) /
(1000L * 60L);
long ma = timeNanos / (1000L * 1000L * 1000L * 60L);
a += ma;
a -= timeZoneOffsetMins;
// convert to minutes and add timezone offset
long b = DateTimeUtils.convertDateValueToMillis(
DateTimeUtils.UTC, t.dateValue) /
(1000L * 60L);
long mb = t.timeNanos / (1000L * 1000L * 1000L * 60L);
b += mb;
b -= t.timeZoneOffsetMins;
// compare date
int c = Long.compare(a, b);
if (c != 0) {
return c;
// Maximum time zone offset is +/-18 hours so difference in days between local
// and UTC cannot be more than one day
long daysA = DateTimeUtils.absoluteDayFromDateValue(dateValue);
long timeA = timeNanos - timeZoneOffsetMins * 60_000_000_000L;
if (timeA < 0) {
timeA += DateTimeUtils.NANOS_PER_DAY;
daysA--;
} else if (timeA >= DateTimeUtils.NANOS_PER_DAY) {
timeA -= DateTimeUtils.NANOS_PER_DAY;
daysA++;
}
long daysB = DateTimeUtils.absoluteDayFromDateValue(t.dateValue);
long timeB = t.timeNanos - t.timeZoneOffsetMins * 60_000_000_000L;
if (timeB < 0) {
timeB += DateTimeUtils.NANOS_PER_DAY;
daysB--;
} else if (timeB >= DateTimeUtils.NANOS_PER_DAY) {
timeB -= DateTimeUtils.NANOS_PER_DAY;
daysB++;
}
int cmp = Long.compare(daysA, daysB);
if (cmp != 0) {
return cmp;
}
// compare time
long na = timeNanos - (ma * 1000L * 1000L * 1000L * 60L);
long nb = t.timeNanos - (mb * 1000L * 1000L * 1000L * 60L);
return Long.compare(na, nb);
return Long.compare(timeA, timeB);
}
@Override
......
......@@ -6,6 +6,7 @@
package org.h2.test;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
......@@ -30,7 +31,9 @@ import java.sql.Types;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.SimpleTimeZone;
import java.util.concurrent.TimeUnit;
......@@ -40,6 +43,7 @@ import org.h2.store.fs.FilePath;
import org.h2.store.fs.FileUtils;
import org.h2.test.utils.ProxyCodeGenerator;
import org.h2.test.utils.ResultVerifier;
import org.h2.test.utils.SelfDestructor;
import org.h2.tools.DeleteDbFiles;
/**
......@@ -466,16 +470,16 @@ public abstract class TestBase {
throw new AssertionError(string);
}
/**
* Log an error message.
*
* @param s the message
*/
public static void logErrorMessage(String s) {
System.out.flush();
System.err.println("ERROR: " + s + "------------------------------");
logThrowable(s, null);
}
/**
* Log an error message.
*
* @param s the message
*/
public static void logErrorMessage(String s) {
System.out.flush();
System.err.println("ERROR: " + s + "------------------------------");
logThrowable(s, null);
}
/**
* Log an error message.
......@@ -1444,6 +1448,16 @@ public abstract class TestBase {
return System.getProperty("java.class.path");
}
/**
* Get the path to a java executable of the current process
*
* @return the path to java
*/
private String getJVM() {
return System.getProperty("java.home") + File.separatorChar + "bin" +
File.separator + "java";
}
/**
* Use up almost all memory.
*
......@@ -1687,4 +1701,46 @@ public abstract class TestBase {
return getClass().getSimpleName();
}
public ProcessBuilder buildChild(String name, Class<? extends TestBase> childClass,
String... jvmArgs) {
List<String> args = new ArrayList<>(16);
args.add(getJVM());
Collections.addAll(args, jvmArgs);
Collections.addAll(args, "-cp", getClassPath(),
SelfDestructor.getPropertyString(1),
childClass.getName(),
"-url", getURL(name, true),
"-user", getUser(),
"-password", getPassword());
ProcessBuilder processBuilder = new ProcessBuilder()
// .redirectError(ProcessBuilder.Redirect.INHERIT)
.redirectErrorStream(true)
.redirectOutput(ProcessBuilder.Redirect.INHERIT)
.command(args);
return processBuilder;
}
public abstract static class Child extends TestBase
{
private String url;
private String user;
private String password;
public Child(String... args) {
for (int i = 0; i < args.length; i++) {
if ("-url".equals(args[i])) {
url = args[++i];
} else if ("-user".equals(args[i])) {
user = args[++i];
} else if ("-password".equals(args[i])) {
password = args[++i];
}
SelfDestructor.startCountdown(60);
}
}
public Connection getConnection() throws SQLException {
return getConnection(url, user, password);
}
}
}
......@@ -15,7 +15,6 @@ import java.util.Map;
import java.util.Random;
import java.util.concurrent.atomic.AtomicReference;
import org.h2.api.ErrorCode;
import org.h2.message.DbException;
import org.h2.mvstore.MVStore;
import org.h2.store.fs.FilePath;
import org.h2.store.fs.FilePathMem;
......@@ -28,6 +27,8 @@ import org.h2.test.TestBase;
*/
public class TestOutOfMemory extends TestBase {
private static final String DB_NAME = "outOfMemory";
/**
* Run just this test.
*
......@@ -38,16 +39,18 @@ public class TestOutOfMemory extends TestBase {
}
@Override
public void test() throws SQLException, InterruptedException {
public void test() throws Exception {
if (config.vmlens) {
// running out of memory will cause the vmlens agent to stop working
return;
}
try {
System.gc();
testMVStoreUsingInMemoryFileSystem();
System.gc();
testDatabaseUsingInMemoryFileSystem();
if (!config.travis) {
System.gc();
testMVStoreUsingInMemoryFileSystem();
System.gc();
testDatabaseUsingInMemoryFileSystem();
}
System.gc();
testUpdateWhenNearlyOutOfMemory();
} finally {
......@@ -147,67 +150,92 @@ public class TestOutOfMemory extends TestBase {
}
}
private void testUpdateWhenNearlyOutOfMemory() throws SQLException, InterruptedException {
private void testUpdateWhenNearlyOutOfMemory() throws Exception {
if (config.memory) {
return;
}
recoverAfterOOM();
deleteDb("outOfMemory");
Connection conn = getConnection("outOfMemory;MAX_OPERATION_MEMORY=1000000");
Statement stat = conn.createStatement();
stat.execute("drop all objects");
stat.execute("create table stuff (id int, text varchar as space(100) || id)");
stat.execute("insert into stuff(id) select x from system_range(1, 3000)");
PreparedStatement prep = conn.prepareStatement(
"update stuff set text = text || space(1000) || id");
prep.execute();
stat.execute("checkpoint");
eatMemory(80);
try {
try {
prep.execute();
fail();
} catch(DbException ex) {
freeMemory();
assertTrue(ErrorCode.OUT_OF_MEMORY == ex.getErrorCode() || ErrorCode.GENERAL_ERROR_1 == ex.getErrorCode());
} catch (SQLException ex) {
freeMemory();
assertTrue(ErrorCode.OUT_OF_MEMORY == ex.getErrorCode() || ErrorCode.GENERAL_ERROR_1 == ex.getErrorCode());
}
recoverAfterOOM();
try {
conn.close();
fail();
} catch(DbException ex) {
freeMemory();
assertEquals(ErrorCode.DATABASE_IS_CLOSED, ex.getErrorCode());
} catch (SQLException ex) {
freeMemory();
assertEquals(ErrorCode.DATABASE_IS_CLOSED, ex.getErrorCode());
deleteDb(DB_NAME);
ProcessBuilder processBuilder = buildChild(
DB_NAME + ";MAX_OPERATION_MEMORY=1000000",
MyChild.class,
"-XX:+UseParallelGC",
// "-XX:+UseG1GC",
"-Xmx128m");
//*
processBuilder.start().waitFor();
/*/
List<String> args = processBuilder.command();
for (Iterator<String> iter = args.iterator(); iter.hasNext(); ) {
String arg = iter.next();
if(arg.equals(MyChild.class.getName())) {
iter.remove();
break;
}
freeMemory();
conn = null;
conn = getConnection("outOfMemory");
stat = conn.createStatement();
ResultSet rs = stat.executeQuery("select count(*) from stuff");
rs.next();
iter.remove();
}
MyChild.main(args.toArray(new String[0]));
//*/
try (Connection conn = getConnection(DB_NAME)) {
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery("SELECT count(*) FROM stuff");
assertTrue(rs.next());
assertEquals(3000, rs.getInt(1));
} catch (OutOfMemoryError e) {
freeMemory();
// out of memory not detected
throw new AssertionError("Out of memory not detected", e);
} finally {
freeMemory();
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// out of memory will / may close the database
assertKnownException(e);
}
rs = stat.executeQuery("SELECT * FROM stuff WHERE id = 3000");
assertTrue(rs.next());
String text = rs.getString(2);
assertFalse(rs.wasNull());
assertEquals(1004, text.length());
// TODO: there are intermittent failures here
// where number is about 1000 short of expected value.
// This indicates a real problem - durability failure
// and need to be looked at.
rs = stat.executeQuery("SELECT sum(length(text)) FROM stuff");
assertTrue(rs.next());
int totalSize = rs.getInt(1);
if (3010893 > totalSize) {
TestBase.logErrorMessage("Durability failure - expected: 3010893, actual: " + totalSize);
}
} finally {
deleteDb(DB_NAME);
}
deleteDb("outOfMemory");
}
public static final class MyChild extends TestBase.Child
{
public static void main(String... args) throws Exception {
new MyChild(args).init().test();
}
private MyChild(String... args) {
super(args);
}
@Override
public void test() {
try (Connection conn = getConnection()) {
Statement stat = conn.createStatement();
stat.execute("DROP ALL OBJECTS");
stat.execute("CREATE TABLE stuff (id INT, text VARCHAR)");
stat.execute("INSERT INTO stuff(id) SELECT x FROM system_range(1, 3000)");
PreparedStatement prep = conn.prepareStatement(
"UPDATE stuff SET text = IFNULL(text,'') || space(1000) || id");
prep.execute();
stat.execute("CHECKPOINT");
ResultSet rs = stat.executeQuery("SELECT sum(length(text)) FROM stuff");
assertTrue(rs.next());
assertEquals(3010893, rs.getInt(1));
eatMemory(80);
prep.execute();
fail();
} catch (SQLException ignore) {
} finally {
freeMemory();
}
}
}
}
......@@ -119,7 +119,20 @@ public class TestLobApi extends TestBase {
prep.setString(1, "");
prep.setBytes(2, new byte[0]);
prep.execute();
Random r = new Random(1);
char[] charsSmall = new char[20];
for (int i = 0; i < charsSmall.length; i++) {
charsSmall[i] = (char) r.nextInt(10000);
}
String dSmall = new String(charsSmall);
prep.setCharacterStream(1, new StringReader(dSmall), -1);
byte[] bytesSmall = new byte[20];
r.nextBytes(bytesSmall);
prep.setBinaryStream(2, new ByteArrayInputStream(bytesSmall), -1);
prep.execute();
char[] chars = new char[100000];
for (int i = 0; i < chars.length; i++) {
chars[i] = (char) r.nextInt(10000);
......@@ -130,6 +143,7 @@ public class TestLobApi extends TestBase {
r.nextBytes(bytes);
prep.setBinaryStream(2, new ByteArrayInputStream(bytes), -1);
prep.execute();
conn.setAutoCommit(false);
ResultSet rs = stat.executeQuery("select * from test order by id");
rs.next();
......@@ -138,18 +152,27 @@ public class TestLobApi extends TestBase {
rs.next();
Clob c2 = rs.getClob(2);
Blob b2 = rs.getBlob(3);
rs.next();
Clob c3 = rs.getClob(2);
Blob b3 = rs.getBlob(3);
assertFalse(rs.next());
// now close
rs.close();
// but the LOBs must stay open
assertEquals(0, c1.length());
assertEquals(0, b1.length());
assertEquals(chars.length, c2.length());
assertEquals(bytes.length, b2.length());
assertEquals("", c1.getSubString(1, 0));
assertEquals(new byte[0], b1.getBytes(1, 0));
assertEquals(d, c2.getSubString(1, (int) c2.length()));
assertEquals(bytes, b2.getBytes(1, (int) b2.length()));
assertEquals(charsSmall.length, c2.length());
assertEquals(bytesSmall.length, b2.length());
assertEquals(dSmall, c2.getSubString(1, (int) c2.length()));
assertEquals(bytesSmall, b2.getBytes(1, (int) b2.length()));
assertEquals(chars.length, c3.length());
assertEquals(bytes.length, b3.length());
assertEquals(d, c3.getSubString(1, (int) c3.length()));
assertEquals(bytes, b3.getBytes(1, (int) b3.length()));
stat.execute("drop table test");
conn.close();
}
......
......@@ -92,7 +92,7 @@ public class TestScript extends TestBase {
} else {
decimal2 = "decimal_numeric";
}
for (String s : new String[] { "array", "bigint", "binary", "blob",
"boolean", "char", "clob", "date", "decimal", decimal2, "double", "enum",
"geometry", "identity", "int", "other", "real", "smallint",
......
......@@ -105,6 +105,18 @@ call dateadd('MS', 1, TIMESTAMP '2001-02-03 04:05:06.789001');
> 2001-02-03 04:05:06.790001
> rows: 1
SELECT DATEADD('MICROSECOND', 1, TIME '10:00:01'), DATEADD('MCS', 1, TIMESTAMP '2010-10-20 10:00:01.1');
> TIME '10:00:01.000001' TIMESTAMP '2010-10-20 10:00:01.100001'
> ---------------------- --------------------------------------
> 10:00:01.000001 2010-10-20 10:00:01.100001
> rows: 1
SELECT DATEADD('NANOSECOND', 1, TIME '10:00:01'), DATEADD('NS', 1, TIMESTAMP '2010-10-20 10:00:01.1');
> TIME '10:00:01.000000001' TIMESTAMP '2010-10-20 10:00:01.100000001'
> ------------------------- -----------------------------------------
> 10:00:01.000000001 2010-10-20 10:00:01.100000001
> rows: 1
SELECT DATEADD('HOUR', 1, DATE '2010-01-20');
> TIMESTAMP '2010-01-20 01:00:00.0'
> ---------------------------------
......
......@@ -159,6 +159,22 @@ call datediff('MS', TIMESTAMP '1900-01-01 00:00:01.000', TIMESTAMP '2008-01-01 0
> 3408134399000
> rows: 1
SELECT DATEDIFF('MICROSECOND', '2006-01-01 00:00:00.0000000', '2006-01-01 00:00:00.123456789'),
DATEDIFF('MCS', '2006-01-01 00:00:00.0000000', '2006-01-01 00:00:00.123456789'),
DATEDIFF('MCS', '2006-01-01 00:00:00.0000000', '2006-01-02 00:00:00.123456789');
> 123456 123456 86400123456
> ------ ------ -----------
> 123456 123456 86400123456
> rows: 1
SELECT DATEDIFF('NANOSECOND', '2006-01-01 00:00:00.0000000', '2006-01-01 00:00:00.123456789'),
DATEDIFF('NS', '2006-01-01 00:00:00.0000000', '2006-01-01 00:00:00.123456789'),
DATEDIFF('NS', '2006-01-01 00:00:00.0000000', '2006-01-02 00:00:00.123456789');
> 123456789 123456789 86400123456789
> --------- --------- --------------
> 123456789 123456789 86400123456789
> 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
> - -
......
......@@ -3,6 +3,20 @@
-- Initial Developer: H2 Group
--
SELECT EXTRACT (MICROSECOND FROM TIME '10:00:00.123456789'),
EXTRACT (MCS FROM TIMESTAMP '2015-01-01 11:22:33.987654321');
> 123456 987654
> ------ ------
> 123456 987654
> rows: 1
SELECT EXTRACT (NANOSECOND FROM TIME '10:00:00.123456789'),
EXTRACT (NS FROM TIMESTAMP '2015-01-01 11:22:33.987654321');
> 123456789 987654321
> --------- ---------
> 123456789 987654321
> rows: 1
select EXTRACT (EPOCH from time '00:00:00');
> 0
......
......@@ -124,14 +124,6 @@ public class TestMVStoreBenchmark extends TestBase {
}
static long getMemory() {
try {
LinkedList<byte[]> list = new LinkedList<>();
while (true) {
list.add(new byte[1024]);
}
} catch (OutOfMemoryError e) {
// ok
}
for (int i = 0; i < 16; i++) {
System.gc();
try {
......
......@@ -10,6 +10,7 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Random;
......@@ -107,33 +108,37 @@ public class TestFuzzOptimizations extends TestBase {
ArrayList<String> params = New.arrayList();
String condition = getRandomCondition(random, params, columns,
compares, values);
// System.out.println(condition + " " + params);
PreparedStatement prep0 = conn.prepareStatement(
"select * from test0 where " + condition
+ " order by 1, 2, 3");
PreparedStatement prep1 = conn.prepareStatement(
"select * from test1 where " + condition
+ " order by 1, 2, 3");
for (int j = 0; j < params.size(); j++) {
prep0.setString(j + 1, params.get(j));
prep1.setString(j + 1, params.get(j));
}
ResultSet rs0 = prep0.executeQuery();
ResultSet rs1 = prep1.executeQuery();
assertEquals("seed: " + seed + " " + condition, rs0, rs1);
String message = "seed: " + seed + " " + condition;
executeAndCompare(condition, params, message);
if (params.size() > 0) {
for (int j = 0; j < params.size(); j++) {
String value = values[random.nextInt(values.length - 2)];
params.set(j, value);
prep0.setString(j + 1, value);
prep1.setString(j + 1, value);
}
assertEquals("seed: " + seed + " " + condition, rs0, rs1);
executeAndCompare(condition, params, message);
}
}
executeAndCompare("a >=0 and b in(?, 2) and a in(1, ?, null)", Arrays.asList("10", "2"),
"seed=-6191135606105920350L");
db.execute("drop table test0, test1");
}
private void executeAndCompare(String condition, List<String> params, String message) throws SQLException {
PreparedStatement prep0 = conn.prepareStatement(
"select * from test0 where " + condition
+ " order by 1, 2, 3");
PreparedStatement prep1 = conn.prepareStatement(
"select * from test1 where " + condition
+ " order by 1, 2, 3");
for (int j = 0; j < params.size(); j++) {
prep0.setString(j + 1, params.get(j));
prep1.setString(j + 1, params.get(j));
}
ResultSet rs0 = prep0.executeQuery();
ResultSet rs1 = prep1.executeQuery();
assertEquals(message, rs0, rs1);
}
private String getRandomCondition(Random random, ArrayList<String> params,
String[] columns, String[] compares, String[] values) {
int comp = 1 + random.nextInt(4);
......
......@@ -26,6 +26,7 @@ public class TestClearReferences extends TestBase {
"org.h2.compress.CompressLZF.cachedHashTable",
"org.h2.engine.DbSettings.defaultSettings",
"org.h2.engine.SessionRemote.sessionFactory",
"org.h2.expression.Function.MONTHS_AND_WEEKS",
"org.h2.jdbcx.JdbcDataSourceFactory.cachedTraceSystem",
"org.h2.store.RecoverTester.instance",
"org.h2.store.fs.FilePath.defaultProvider",
......@@ -36,6 +37,7 @@ public class TestClearReferences extends TestBase {
"org.h2.tools.CompressTool.cachedBuffer",
"org.h2.util.CloseWatcher.queue",
"org.h2.util.CloseWatcher.refs",
"org.h2.util.DateTimeUtils.timeZone",
"org.h2.util.MathUtils.cachedSecureRandom",
"org.h2.util.NetUtils.cachedLocalAddress",
"org.h2.util.StringUtils.softCache",
......@@ -43,6 +45,7 @@ public class TestClearReferences extends TestBase {
"org.h2.util.JdbcUtils.allowedClassNamePrefixes",
"org.h2.util.JdbcUtils.userClassFactories",
"org.h2.util.Task.counter",
"org.h2.util.ToChar.NAMES",
"org.h2.value.CompareMode.lastUsed",
"org.h2.value.Value.softCache",
};
......
......@@ -5,13 +5,16 @@
*/
package org.h2.test.unit;
import static org.h2.util.DateTimeUtils.dateValue;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import org.h2.test.TestBase;
import org.h2.util.DateTimeUtils;
import static org.h2.util.DateTimeUtils.dateValue;
import org.h2.value.ValueTimestamp;
/**
* Unit tests for the DateTimeUtils class
......@@ -21,9 +24,18 @@ public class TestDateTimeUtils extends TestBase {
/**
* Run just this test.
*
* @param a ignored
* @param a
* if {@code "testUtc2Value"} only {@link #testUTC2Value(boolean)}
* will be executed with all time zones (slow). Otherwise all tests
* in this test unit will be executed with local time zone.
*/
public static void main(String... a) throws Exception {
if (a.length == 1) {
if ("testUtc2Value".equals(a[0])) {
new TestDateTimeUtils().testUTC2Value(true);
return;
}
}
TestBase.createCaller().init().test();
}
......@@ -33,6 +45,7 @@ public class TestDateTimeUtils extends TestBase {
testDayOfWeek();
testWeekOfYear();
testDateValueFromDenormalizedDate();
testUTC2Value(false);
}
private void testParseTimeNanosDB2Format() {
......@@ -44,7 +57,7 @@ public class TestDateTimeUtils extends TestBase {
}
/**
* Test for {@link DateTimeUtils#getSundayDayOfWeek()} and
* Test for {@link DateTimeUtils#getSundayDayOfWeek(long)} and
* {@link DateTimeUtils#getIsoDayOfWeek(long)}.
*/
private void testDayOfWeek() {
......@@ -106,4 +119,45 @@ public class TestDateTimeUtils extends TestBase {
assertEquals(dateValue(-100, 2, 29), DateTimeUtils.dateValueFromDenormalizedDate(-100, 2, 30));
}
private void testUTC2Value(boolean allTimeZones) {
TimeZone def = TimeZone.getDefault();
GregorianCalendar gc = new GregorianCalendar();
if (allTimeZones) {
try {
for (String id : TimeZone.getAvailableIDs()) {
System.out.println(id);
TimeZone tz = TimeZone.getTimeZone(id);
TimeZone.setDefault(tz);
DateTimeUtils.resetCalendar();
testUTC2ValueImpl(tz, gc);
}
} finally {
TimeZone.setDefault(def);
DateTimeUtils.resetCalendar();
}
} else {
testUTC2ValueImpl(def, gc);
}
}
private void testUTC2ValueImpl(TimeZone tz, GregorianCalendar gc) {
gc.setTimeZone(tz);
gc.set(Calendar.MILLISECOND, 0);
long absoluteStart = DateTimeUtils.absoluteDayFromDateValue(DateTimeUtils.dateValue(1950, 01, 01));
long absoluteEnd = DateTimeUtils.absoluteDayFromDateValue(DateTimeUtils.dateValue(2050, 01, 01));
for (long i = absoluteStart; i < absoluteEnd; i++) {
long dateValue = DateTimeUtils.dateValueFromAbsoluteDay(i);
int year = DateTimeUtils.yearFromDateValue(dateValue);
int month = DateTimeUtils.monthFromDateValue(dateValue);
int day = DateTimeUtils.dayFromDateValue(dateValue);
for (int j = 0; j < 48; j++) {
gc.set(year, month - 1, day, j / 2, (j & 1) * 30, 0);
long timeMillis = gc.getTimeInMillis();
ValueTimestamp ts = DateTimeUtils.convertTimestamp(new Timestamp(timeMillis), gc);
assertEquals(ts.getDateValue(), DateTimeUtils.dateValueFromDate(timeMillis));
assertEquals(ts.getTimeNanos(), DateTimeUtils.nanosFromDate(timeMillis));
}
}
}
}
......@@ -126,21 +126,27 @@ public class TestTimeStampWithTimeZone extends TestBase {
ValueTimestampTimeZone a = ValueTimestampTimeZone.parse("1970-01-01 12:00:00.00+00:15");
ValueTimestampTimeZone b = ValueTimestampTimeZone.parse("1970-01-01 12:00:01.00+01:15");
int c = a.compareTo(b, null);
assertEquals(c, 1);
assertEquals(1, c);
c = b.compareTo(a, null);
assertEquals(-1, c);
}
private void test3() {
ValueTimestampTimeZone a = ValueTimestampTimeZone.parse("1970-01-02 00:00:02.00+01:15");
ValueTimestampTimeZone b = ValueTimestampTimeZone.parse("1970-01-01 23:00:01.00+00:15");
int c = a.compareTo(b, null);
assertEquals(c, 1);
assertEquals(1, c);
c = b.compareTo(a, null);
assertEquals(-1, c);
}
private void test4() {
ValueTimestampTimeZone a = ValueTimestampTimeZone.parse("1970-01-02 00:00:01.00+01:15");
ValueTimestampTimeZone b = ValueTimestampTimeZone.parse("1970-01-01 23:00:01.00+00:15");
int c = a.compareTo(b, null);
assertEquals(c, 0);
assertEquals(0, c);
c = b.compareTo(a, null);
assertEquals(0, c);
}
private void test5() throws SQLException {
......
......@@ -764,5 +764,5 @@ mdy destfile hclf forbids spellchecking selfdestruct expects accident jacocoagen
jacoco xdata invokes sourcefiles classfiles duplication crypto stacktraces prt directions handled overly asm hardcoded
interpolated thead
die weekdiff osx subprocess dow proleptic
die weekdiff osx subprocess dow proleptic microsecond microseconds divisible cmp denormalized suppressed saturated mcs
london
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论